86598 lines
2.9 MiB
Plaintext
86598 lines
2.9 MiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| TestClasses.mqh |
|
|
//| Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) |
|
|
//| Copyright 2012-2026, MetaQuotes Ltd. |
|
|
//| www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
//| Implementation of ALGLIB library in MetaQuotes Language 5 |
|
|
//| |
|
|
//| The features of the library include: |
|
|
//| - Linear algebra (direct algorithms, EVD, SVD) |
|
|
//| - Solving systems of linear and non-linear equations |
|
|
//| - Interpolation |
|
|
//| - Optimization |
|
|
//| - FFT (Fast Fourier Transform) |
|
|
//| - Numerical integration |
|
|
//| - Linear and nonlinear least-squares fitting |
|
|
//| - Ordinary differential equations |
|
|
//| - Computation of special functions |
|
|
//| - Descriptive statistics and hypothesis testing |
|
|
//| - Data analysis - classification, regression |
|
|
//| - Implementing linear algebra algorithms, interpolation, etc. |
|
|
//| in high-precision arithmetic (using MPFR) |
|
|
//| |
|
|
//| This file is free software; you can redistribute it and/or |
|
|
//| modify it under the terms of the GNU General Public License as |
|
|
//| published by the Free Software Foundation (www.fsf.org); either |
|
|
//| version 2 of the License, or (at your option) any later version. |
|
|
//| |
|
|
//| This program is distributed in the hope that it will be useful, |
|
|
//| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
//| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
//| GNU General Public License for more details. |
|
|
//+------------------------------------------------------------------+
|
|
#include <Math\Alglib\alglib.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#define CheckErrorDiff(val,refval,tol,s) (MathAbs(val-refval)>tol*MathMax(MathAbs(refval),s))
|
|
#define PrintResult(function,check) PrintFormat("%-40s: %10s",function,(check ? "PASSED " : "- FAILED -"));
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHighQualityRand |
|
|
//+------------------------------------------------------------------+
|
|
class CTestHQRndUnit
|
|
{
|
|
public:
|
|
static bool TestHQRnd(const bool silent);
|
|
|
|
private:
|
|
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
|
|
static void UnsetState(CHighQualityRandState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHighQualityRand |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestHQRndUnit::TestHQRnd(const bool silent)
|
|
{
|
|
bool waserrors;
|
|
int samplesize=0;
|
|
double sigmathreshold=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int i=0;
|
|
int pass=0;
|
|
int s1=0;
|
|
int s2=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double r1=0;
|
|
double r2=0;
|
|
double mean=0;
|
|
double means=0;
|
|
double stddev=0;
|
|
double stddevs=0;
|
|
double lambdav=0;
|
|
bool seederrors;
|
|
bool urerrors;
|
|
double ursigmaerr=0;
|
|
bool uierrors;
|
|
double uisigmaerr=0;
|
|
bool normerrors;
|
|
double normsigmaerr=0;
|
|
bool experrors;
|
|
double expsigmaerr=0;
|
|
//--- create array
|
|
double x[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- initialization
|
|
waserrors=false;
|
|
sigmathreshold=7;
|
|
samplesize=100000;
|
|
passcount=50;
|
|
seederrors=false;
|
|
urerrors=false;
|
|
uierrors=false;
|
|
normerrors=false;
|
|
experrors=false;
|
|
//--- allocation
|
|
ArrayResize(x,samplesize);
|
|
//--- Test seed errors
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- change values
|
|
s1=1+CMath::RandomInteger(32000);
|
|
s2=1+CMath::RandomInteger(32000);
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change value
|
|
i1=CHighQualityRand::HQRndUniformI(state,100);
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change values
|
|
i2=CHighQualityRand::HQRndUniformI(state,100);
|
|
seederrors=seederrors||i1!=i2;
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change value
|
|
r1=CHighQualityRand::HQRndUniformR(state);
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change values
|
|
r2=CHighQualityRand::HQRndUniformR(state);
|
|
seederrors=seederrors||r1!=r2;
|
|
}
|
|
//--- Test HQRNDRandomize() and real uniform generator
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
ursigmaerr=0;
|
|
for(i=0; i<=samplesize-1; i++)
|
|
x[i]=CHighQualityRand::HQRndUniformR(state);
|
|
for(i=0; i<=samplesize-1; i++)
|
|
urerrors=(urerrors||x[i]<=0.0)||x[i]>=1.0;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
ursigmaerr=MathMax(ursigmaerr,MathAbs((mean-0.5)/means));
|
|
else
|
|
urerrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
ursigmaerr=MathMax(ursigmaerr,MathAbs((stddev-MathSqrt(1.0/12.0))/stddevs));
|
|
else
|
|
urerrors=true;
|
|
//--- change value
|
|
urerrors=urerrors||ursigmaerr>sigmathreshold;
|
|
//--- Test HQRNDRandomize() and integer uniform
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- calculation
|
|
uisigmaerr=0;
|
|
for(n=2; n<=10; n++)
|
|
{
|
|
for(i=0; i<=samplesize-1; i++)
|
|
x[i]=CHighQualityRand::HQRndUniformI(state,n);
|
|
for(i=0; i<=samplesize-1; i++)
|
|
uierrors=(uierrors||x[i]<0.0)||x[i]>=n;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means));
|
|
else
|
|
uierrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs));
|
|
else
|
|
uierrors=true;
|
|
}
|
|
//--- change values
|
|
uierrors=uierrors||uisigmaerr>sigmathreshold;
|
|
//--- Special 'close-to-limit' test on uniformity of integers
|
|
//--- (straightforward implementation like 'RND mod N' will return
|
|
//--- non-uniform numbers for N=2/3*LIMIT)
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
uisigmaerr=0;
|
|
n=1431655708;
|
|
//--- calculation
|
|
for(i=0; i<=samplesize-1; i++)
|
|
x[i]=CHighQualityRand::HQRndUniformI(state,n);
|
|
for(i=0; i<=samplesize-1; i++)
|
|
uierrors=(uierrors||x[i]<0.0)||x[i]>=n;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means));
|
|
else
|
|
uierrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs));
|
|
else
|
|
uierrors=true;
|
|
uierrors=uierrors||uisigmaerr>sigmathreshold;
|
|
//--- Test normal
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
normsigmaerr=0;
|
|
i=0;
|
|
//--- cycle
|
|
while(i<samplesize)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,r1,r2);
|
|
x[i]=r1;
|
|
//--- check
|
|
if(i+1<samplesize)
|
|
{
|
|
x[i+1]=r2;
|
|
}
|
|
i=i+2;
|
|
}
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
normsigmaerr=MathMax(normsigmaerr,MathAbs((mean-0)/means));
|
|
else
|
|
normerrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
normsigmaerr=MathMax(normsigmaerr,MathAbs((stddev-1)/stddevs));
|
|
else
|
|
normerrors=true;
|
|
normerrors=normerrors||(double)(normsigmaerr)>sigmathreshold;
|
|
//--- Test exponential
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
expsigmaerr=0;
|
|
lambdav=2+5*CMath::RandomReal();
|
|
//--- calculation
|
|
for(i=0; i<=samplesize-1; i++)
|
|
x[i]=CHighQualityRand::HQRndExponential(state,lambdav);
|
|
for(i=0; i<=samplesize-1; i++)
|
|
uierrors=uierrors||x[i]<0.0;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
expsigmaerr=MathMax(expsigmaerr,MathAbs((mean-1.0/lambdav)/means));
|
|
else
|
|
experrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
expsigmaerr=MathMax(expsigmaerr,MathAbs((stddev-1.0/lambdav)/stddevs));
|
|
else
|
|
experrors=true;
|
|
experrors=experrors||expsigmaerr>sigmathreshold;
|
|
//--- Final report
|
|
waserrors=(((seederrors||urerrors)||uierrors)||normerrors)||experrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("RNG TEST");
|
|
//--- check
|
|
PrintResult("SEED TEST",!seederrors);
|
|
//--- check
|
|
PrintResult("UNIFORM CONTINUOUS",!urerrors);
|
|
//--- check
|
|
PrintResult("UNIFORM INTEGER",!uierrors);
|
|
//--- check
|
|
PrintResult("NORMAL",!normerrors);
|
|
//--- check
|
|
PrintResult("EXPONENTIAL",!experrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestHQRndUnit::CalculateMV(double &x[],const int n,double &mean,
|
|
double &means,double &stddev,
|
|
double &stddevs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double variance=0;
|
|
//--- initialization
|
|
mean=0;
|
|
means=1;
|
|
stddev=0;
|
|
stddevs=1;
|
|
variance=0;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- Mean
|
|
for(i=0; i<n; i++)
|
|
mean=mean+x[i];
|
|
mean=mean/n;
|
|
//--- Variance (using corrected two-pass algorithm)
|
|
if(n!=1)
|
|
{
|
|
//--- change value
|
|
v1=0;
|
|
for(i=0; i<n; i++)
|
|
v1+=CMath::Sqr(x[i]-mean);
|
|
//--- change value
|
|
v2=0;
|
|
for(i=0; i<n; i++)
|
|
v2=v2+(x[i]-mean);
|
|
//--- calculation
|
|
v2=CMath::Sqr(v2)/n;
|
|
variance=(v1-v2)/(n-1);
|
|
//--- check
|
|
if(variance<0.0)
|
|
variance=0;
|
|
stddev=MathSqrt(variance);
|
|
}
|
|
//--- Errors
|
|
means=stddev/MathSqrt(n);
|
|
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets HQRNDState structure |
|
|
//+------------------------------------------------------------------+
|
|
void CTestHQRndUnit::UnsetState(CHighQualityRandState &state)
|
|
{
|
|
state.m_s1=0;
|
|
state.m_s2=0;
|
|
state.m_v=0;
|
|
state.m_magicv=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTSort |
|
|
//+------------------------------------------------------------------+
|
|
class CTestTSortUnit
|
|
{
|
|
public:
|
|
static bool TestTSort(const bool silent);
|
|
|
|
private:
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void Unset1DI(int &a[]);
|
|
static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTSort |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestTSortUnit::TestTSort(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
int n=0;
|
|
int i=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
//--- create arrays
|
|
double a[];
|
|
double a0[];
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double ar[];
|
|
int ai[];
|
|
int p1[];
|
|
int p2[];
|
|
double bufr1[];
|
|
double bufr2[];
|
|
int bufi1[];
|
|
//--- initialization
|
|
waserrors=false;
|
|
maxn=100;
|
|
passcount=10;
|
|
//--- Test tagsort
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- (probably) distinct sort:
|
|
//--- * first sort A0 using TagSort and test sort results
|
|
//--- * now we can use A0 as reference point and test other functions
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=2*CMath::RandomReal()-1;
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a1[i]!=a0[i])||ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a2[i]!=a0[i])||ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||a3[i]!=a0[i];
|
|
//--- non-distinct sort
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=i/2;
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a1[i]!=a0[i])||ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a2[i]!=a0[i])||ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||a3[i]!=a0[i];
|
|
//--- 'All same' sort
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=0;
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a1[i]!=a0[i])||ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a2[i]!=a0[i])||ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||a3[i]!=a0[i];
|
|
//--- 0-1 sort
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=CMath::RandomInteger(2);
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a1[i]!=a0[i])||ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=(waserrors||a2[i]!=a0[i])||ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||a3[i]!=a0[i];
|
|
}
|
|
}
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
PrintResult("TESTING TAGSORT",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTSortUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTSortUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTSortUnit::Unset1DI(int &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=CMath::RandomInteger(3)-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTSortUnit::TestSortResults(double &asorted[],int &p1[],
|
|
int &p2[],double &aoriginal[],
|
|
const int n,bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double a2[];
|
|
int f[];
|
|
//--- allocation
|
|
ArrayResize(a2,n);
|
|
ArrayResize(f,n);
|
|
//--- is set ordered?
|
|
for(i=0; i<=n-2; i++)
|
|
waserrors=waserrors||asorted[i]>asorted[i+1];
|
|
//--- P1 correctness
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||asorted[i]!=aoriginal[p1[i]];
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
f[i]=0;
|
|
for(i=0; i<n; i++)
|
|
f[p1[i]]=f[p1[i]]+1;
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||f[i]!=1;
|
|
//--- P2 correctness
|
|
for(i=0; i<n; i++)
|
|
a2[i]=aoriginal[i];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(p2[i]!=i)
|
|
{
|
|
t=a2[i];
|
|
a2[i]=a2[p2[i]];
|
|
a2[p2[i]]=t;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||asorted[i]!=a2[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNearestNeighbor |
|
|
//+------------------------------------------------------------------+
|
|
class CTestNearestNeighborUnit
|
|
{
|
|
public:
|
|
static bool TestNearestNeighbor(const bool silent);
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static bool KDTResultsDifferent(CMatrixDouble &refxy,const int ntotal,CMatrixDouble &qx,CMatrixDouble &qxy,int &qt[],const int n,const int nx,const int ny);
|
|
static double VNorm(double &x[],const int n,const int normtype);
|
|
static void TestKDTUniform(CMatrixDouble &xy,const int n,const int nx,const int ny,const int normtype,bool &kdterrors);
|
|
|
|
private:
|
|
static void TestKDTreeSerialization(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNearestNeighbor |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestNearestNeighborUnit::TestNearestNeighbor(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int normtype=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int n=0;
|
|
int smalln=0;
|
|
int largen=0;
|
|
int passcount=0;
|
|
int pass=0;
|
|
bool waserrors;
|
|
bool kdterrors;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- initialization
|
|
kdterrors=false;
|
|
passcount=2;
|
|
smalln=256;
|
|
largen=2048;
|
|
ny=3;
|
|
//--- function call
|
|
TestKDTreeSerialization(kdterrors);
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(normtype=0; normtype<=2; normtype++)
|
|
{
|
|
for(nx=1; nx<=3; nx++)
|
|
{
|
|
//--- Test in hypercube
|
|
xy.Resize(largen,nx+ny);
|
|
for(i=0; i<=largen-1; i++)
|
|
{
|
|
for(j=0; j<nx+ny; j++)
|
|
xy.Set(i,j,10*CMath::RandomReal()-5);
|
|
}
|
|
//--- function calls
|
|
for(n=1; n<=10; n++)
|
|
TestKDTUniform(xy,n,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
TestKDTUniform(xy,largen,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
//--- Test clustered (2*N points,pairs of equal points)
|
|
xy.Resize(2*smalln,nx+ny);
|
|
for(i=0; i<=smalln-1; i++)
|
|
{
|
|
for(j=0; j<nx+ny; j++)
|
|
{
|
|
xy.Set(2*i,j,10*CMath::RandomReal()-5);
|
|
xy.Set(2*i+1,j,xy[2*i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
TestKDTUniform(xy,2*smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
//--- Test degenerate case: all points are same except for one
|
|
xy.Resize(smalln,nx+ny);
|
|
v=CMath::RandomReal();
|
|
//--- change values
|
|
for(i=0; i<=smalln-2; i++)
|
|
{
|
|
for(j=0; j<nx+ny; j++)
|
|
xy.Set(i,j,v);
|
|
}
|
|
for(j=0; j<nx+ny; j++)
|
|
xy.Set(smalln-1,j,10*CMath::RandomReal()-5);
|
|
//--- function call
|
|
TestKDTUniform(xy,smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=kdterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING NEAREST NEIGHBOR SEARCH");
|
|
//--- check
|
|
PrintResult("KD TREES",!kdterrors);
|
|
//--- check
|
|
PrintResult("TEST",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNearestNeighborUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNearestNeighborUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Compare results from different queries: |
|
|
//| * X just X-values |
|
|
//| * XY X-values and Y-values |
|
|
//| * XT X-values and tag values |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestNearestNeighborUnit::KDTResultsDifferent(CMatrixDouble &refxy,
|
|
const int ntotal,
|
|
CMatrixDouble &qx,
|
|
CMatrixDouble &qxy,
|
|
int &qt[],const int n,
|
|
const int nx,const int ny)
|
|
{
|
|
bool result=false;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(qt[i]<0 || qt[i]>=ntotal)
|
|
{
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- search errors
|
|
for(int j=0; j<nx ; j++)
|
|
{
|
|
result=result || qx.Get(i,j)!=refxy[qt[i]][j];
|
|
result=result || qxy.Get(i,j)!=refxy[qt[i]][j];
|
|
}
|
|
for(int j=0; j<ny; j++)
|
|
result=result || qxy[i][nx+j]!=refxy[qt[i]][nx+j];
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns norm |
|
|
//+------------------------------------------------------------------+
|
|
double CTestNearestNeighborUnit::VNorm(double &x[],const int n,
|
|
const int normtype)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
//--- initialization
|
|
result=CMath::RandomReal();
|
|
//--- check
|
|
if(normtype==0)
|
|
{
|
|
//--- calculation
|
|
result=0;
|
|
for(i=0; i<n; i++)
|
|
result=MathMax(result,MathAbs(x[i]));
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(normtype==1)
|
|
{
|
|
//--- calculation
|
|
result=0;
|
|
for(i=0; i<n; i++)
|
|
result+=MathAbs(x[i]);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(normtype==2)
|
|
{
|
|
//--- calculation
|
|
result=0;
|
|
for(i=0; i<n; i++)
|
|
result+=CMath::Sqr(x[i]);
|
|
result=MathSqrt(result);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing Nearest Neighbor Search on uniformly distributed |
|
|
//| hypercube |
|
|
//| NormType: 0,1,2 |
|
|
//| D: space dimension |
|
|
//| N: points count |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNearestNeighborUnit::TestKDTUniform(CMatrixDouble &xy,
|
|
const int n,const int nx,
|
|
const int ny,const int normtype,
|
|
bool &kdterrors)
|
|
{
|
|
//--- create variables
|
|
double errtol=0;
|
|
int kx=0;
|
|
int kxy=0;
|
|
int kt=0;
|
|
double eps=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int task=0;
|
|
bool isequal;
|
|
double r=0;
|
|
int q=0;
|
|
int qcount=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int tags[];
|
|
double ptx[];
|
|
double tmpx[];
|
|
bool tmpb[];
|
|
int qtags[];
|
|
double qr[];
|
|
//--- objects of classes
|
|
CKDTree treex;
|
|
CKDTree treexy;
|
|
CKDTree treext;
|
|
//--- create matrix
|
|
CMatrixDouble qx;
|
|
CMatrixDouble qxy;
|
|
//--- initialization
|
|
qcount=10;
|
|
//--- Tol - roundoff error tolerance (for '>=' comparisons)
|
|
errtol=100000*CMath::m_machineepsilon;
|
|
//--- fill tags
|
|
ArrayResize(tags,n);
|
|
for(i=0; i<n; i++)
|
|
tags[i]=i;
|
|
//--- build trees
|
|
CNearestNeighbor::KDTreeBuild(xy,n,nx,0,normtype,treex);
|
|
CNearestNeighbor::KDTreeBuild(xy,n,nx,ny,normtype,treexy);
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,0,normtype,treext);
|
|
//--- allocate arrays
|
|
ArrayResize(tmpx,nx);
|
|
ArrayResize(tmpb,n);
|
|
qx.Resize(n,nx);
|
|
qxy.Resize(n,nx+ny);
|
|
ArrayResize(qtags,n);
|
|
ArrayResize(qr,n);
|
|
ArrayResize(ptx,nx);
|
|
//--- test general K-NN queries (with self-matches):
|
|
//--- * compare results from different trees (must be equal) and
|
|
//--- check that correct (value,tag) pairs are returned
|
|
//--- * test results from XT tree - let R be radius of query result.
|
|
//--- then all points not in result must be not closer than R.
|
|
for(q=1; q<=qcount; q++)
|
|
{
|
|
//--- Select K: 1..N
|
|
if(CMath::RandomReal()>0.5)
|
|
k=1+CMath::RandomInteger(n);
|
|
else
|
|
k=1;
|
|
//--- Select point (either one of the points,or random)
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
for(i_=0; i_<nx ; i_++)
|
|
ptx[i_]=xy.Get(i,i_);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nx ; i++)
|
|
ptx[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- Test:
|
|
//--- * consistency of results from different queries
|
|
//--- * points in query are IN the R-sphere (or at the boundary),
|
|
//--- and points not in query are outside of the R-sphere (or at the boundary)
|
|
//--- * distances are correct and are ordered
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
tmpb[i]=true;
|
|
r=0;
|
|
//--- calculation
|
|
for(i=0; i<k; i++)
|
|
{
|
|
tmpb[qtags[i]]=false;
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-qx.Get(i,i_);
|
|
r=MathMax(r,VNorm(tmpx,nx,normtype));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(tmpb[i])
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-xy.Get(i,i_);
|
|
//--- search errors
|
|
kdterrors=kdterrors||VNorm(tmpx,nx,normtype)<r*(1-errtol);
|
|
}
|
|
}
|
|
for(i=0; i<=k-2; i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||qr[i]>qr[i+1];
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_];
|
|
//--- search errors
|
|
kdterrors=kdterrors||MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol;
|
|
}
|
|
//--- Test reallocation properties: buffered functions must automatically
|
|
//--- resize array which is too small,but leave unchanged array which is
|
|
//--- too large.
|
|
if(n>=2)
|
|
{
|
|
//--- First step: array is too small,two elements are required
|
|
k=2;
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
qx.Resize(1,1);
|
|
qxy.Resize(1,1);
|
|
ArrayResize(qtags,1);
|
|
ArrayResize(qr,1);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- Second step: array is one row larger than needed,so only first
|
|
//--- row is overwritten. Test it.
|
|
k=1;
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<nx ; i++)
|
|
qx.Set(1,i,CInfOrNaN::NaN());
|
|
for(i=0; i<nx+ny; i++)
|
|
qxy.Set(1,i,CInfOrNaN::NaN());
|
|
qtags[1]=999;
|
|
qr[1]=CInfOrNaN::NaN();
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
for(i=0; i<nx ; i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||!CInfOrNaN::IsNaN(qx[1][i]);
|
|
}
|
|
for(i=0; i<nx+ny; i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||!CInfOrNaN::IsNaN(qxy[1][i]);
|
|
}
|
|
//--- search errors
|
|
kdterrors=kdterrors||!(qtags[1]==999);
|
|
kdterrors=kdterrors||!CInfOrNaN::IsNaN(qr[1]);
|
|
}
|
|
//--- Test reallocation properties: 'interactive' functions must allocate
|
|
//--- new array on each call.
|
|
if(n>=2)
|
|
{
|
|
//--- On input array is either too small or too large
|
|
for(k=1; k<=2; k++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(k==1 || k==2,"KNN: internal error (unexpected K)!"))
|
|
return;
|
|
//--- change values
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
qx.Resize(3-k,3-k);
|
|
qxy.Resize(3-k,3-k);
|
|
ArrayResize(qtags,3-k);
|
|
ArrayResize(qr,3-k);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
kdterrors=(kdterrors||CAp::Rows(qx)!=k)||CAp::Cols(qx)!=nx;
|
|
kdterrors=(kdterrors||CAp::Rows(qxy)!=k)||CAp::Cols(qxy)!=nx+ny;
|
|
kdterrors=kdterrors||CAp::Len(qtags)!=k;
|
|
kdterrors=kdterrors||CAp::Len(qr)!=k;
|
|
}
|
|
}
|
|
}
|
|
//--- test general approximate K-NN queries (with self-matches):
|
|
//--- * compare results from different trees (must be equal) and
|
|
//--- check that correct (value,tag) pairs are returned
|
|
//--- * test results from XT tree - let R be radius of query result.
|
|
//--- then all points not in result must be not closer than R/(1+Eps).
|
|
for(q=1; q<=qcount; q++)
|
|
{
|
|
//--- Select K: 1..N
|
|
if(CMath::RandomReal()>0.5)
|
|
k=1+CMath::RandomInteger(n);
|
|
else
|
|
k=1;
|
|
//--- Select Eps
|
|
eps=0.5+CMath::RandomReal();
|
|
//--- Select point (either one of the points,or random)
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
for(i_=0; i_<nx ; i_++)
|
|
ptx[i_]=xy.Get(i,i_);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nx ; i++)
|
|
ptx[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- Test:
|
|
//--- * consistency of results from different queries
|
|
//--- * points in query are IN the R-sphere (or at the boundary),
|
|
//--- and points not in query are outside of the R-sphere (or at the boundary)
|
|
//--- * distances are correct and are ordered
|
|
kx=CNearestNeighbor::KDTreeQueryAKNN(treex,ptx,k,true,eps);
|
|
kxy=CNearestNeighbor::KDTreeQueryAKNN(treexy,ptx,k,true,eps);
|
|
kt=CNearestNeighbor::KDTreeQueryAKNN(treext,ptx,k,true,eps);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
tmpb[i]=true;
|
|
r=0;
|
|
//--- calculation
|
|
for(i=0; i<k; i++)
|
|
{
|
|
tmpb[qtags[i]]=false;
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-qx.Get(i,i_);
|
|
r=MathMax(r,VNorm(tmpx,nx,normtype));
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(tmpb[i])
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-xy.Get(i,i_);
|
|
//--- search errors
|
|
kdterrors=kdterrors||VNorm(tmpx,nx,normtype)<r*(1-errtol)/(1+eps);
|
|
}
|
|
}
|
|
for(i=0; i<=k-2; i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||qr[i]>qr[i+1];
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_];
|
|
//--- search errors
|
|
kdterrors=kdterrors||MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol;
|
|
}
|
|
}
|
|
//--- test general R-NN queries (with self-matches):
|
|
//--- * compare results from different trees (must be equal) and
|
|
//--- check that correct (value,tag) pairs are returned
|
|
//--- * test results from XT tree - let R be radius of query result.
|
|
//--- then all points not in result must be not closer than R.
|
|
for(q=1; q<=qcount; q++)
|
|
{
|
|
//--- Select R
|
|
if(CMath::RandomReal()>0.3)
|
|
r=MathMax(CMath::RandomReal(),CMath::m_machineepsilon);
|
|
else
|
|
r=CMath::m_machineepsilon;
|
|
//--- Select point (either one of the points,or random)
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
for(i_=0; i_<nx ; i_++)
|
|
ptx[i_]=xy.Get(i,i_);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nx ; i++)
|
|
ptx[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- Test:
|
|
//--- * consistency of results from different queries
|
|
//--- * points in query are IN the R-sphere (or at the boundary),
|
|
//--- and points not in query are outside of the R-sphere (or at the boundary)
|
|
//--- * distances are correct and are ordered
|
|
kx=CNearestNeighbor::KDTreeQueryRNN(treex,ptx,r,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryRNN(treexy,ptx,r,true);
|
|
kt=CNearestNeighbor::KDTreeQueryRNN(treext,ptx,r,true);
|
|
//--- check
|
|
if(kxy!=kx || kt!=kx)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors||KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
tmpb[i]=true;
|
|
for(i=0; i<=kx-1; i++)
|
|
tmpb[qtags[i]]=false;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0; i_<nx ; i_++)
|
|
tmpx[i_]=tmpx[i_]-xy.Get(i,i_);
|
|
//--- check
|
|
if(tmpb[i])
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||VNorm(tmpx,nx,normtype)<r*(1-errtol);
|
|
}
|
|
else
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||VNorm(tmpx,nx,normtype)>r*(1+errtol);
|
|
}
|
|
}
|
|
for(i=0; i<=kx-2; i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors||qr[i]>qr[i+1];
|
|
}
|
|
}
|
|
//--- Test self-matching:
|
|
//--- * self-match - nearest neighbor of each point in XY is the point itself
|
|
//--- * no self-match - nearest neighbor is NOT the point itself
|
|
if(n>1)
|
|
{
|
|
//--- test for N=1 have non-general form,but it is not really needed
|
|
for(task=0; task<=1; task++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
ptx[i_]=xy.Get(i,i_);
|
|
//--- function calls
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,1,task==0);
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
//--- check
|
|
if(kx!=1)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
isequal=true;
|
|
for(j=0; j<nx ; j++)
|
|
isequal=isequal && qx[0][j]==ptx[j];
|
|
//--- check
|
|
if(task==0)
|
|
kdterrors=kdterrors||!isequal;
|
|
else
|
|
kdterrors=kdterrors||isequal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing serialization of KD trees |
|
|
//| This function sets Err to True on errors, but leaves it unchanged|
|
|
//| on success |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNearestNeighborUnit::TestKDTreeSerialization(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int normtype=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int q=0;
|
|
double threshold=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
//--- objects of classes
|
|
CKDTree tree0;
|
|
CKDTree tree1;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy0;
|
|
CMatrixDouble xy1;
|
|
//--- create arrays
|
|
double x[];
|
|
int tags[];
|
|
int qsizes[];
|
|
int tags0[];
|
|
int tags1[];
|
|
int i_=0;
|
|
//--- initialization
|
|
threshold=100*CMath::m_machineepsilon;
|
|
//--- different N,NX,NY,NormType
|
|
n=1;
|
|
while(n<=51)
|
|
{
|
|
//--- prepare array with query sizes
|
|
ArrayResize(qsizes,4);
|
|
qsizes[0]=1;
|
|
qsizes[1]=(int)(MathMin(2,n));
|
|
qsizes[2]=(int)(MathMin(4,n));
|
|
qsizes[3]=n;
|
|
//--- different NX/NY/NormType
|
|
for(nx=1; nx<=2; nx++)
|
|
{
|
|
for(ny=0; ny<=2; ny++)
|
|
{
|
|
for(normtype=0; normtype<=2; normtype++)
|
|
{
|
|
//--- Prepare data
|
|
xy.Resize(n,nx+ny);
|
|
ArrayResize(tags,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx+ny; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
tags[i]=CMath::RandomInteger(100);
|
|
}
|
|
//--- Build tree,pass it through CSerializer
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,ny,normtype,tree0);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CNearestNeighbor::KDTreeAlloc(_local_serializer,tree0);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CNearestNeighbor::KDTreeSerialize(_local_serializer,tree0);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CNearestNeighbor::KDTreeUnserialize(_local_serializer,tree1);
|
|
_local_serializer.Stop();
|
|
}
|
|
//--- For each point of XY we make queries with different sizes
|
|
ArrayResize(x,nx);
|
|
for(k=0; k<n; k++)
|
|
{
|
|
for(q=0; q<=CAp::Len(qsizes)-1; q++)
|
|
{
|
|
for(i_=0; i_<nx ; i_++)
|
|
x[i_]=xy[k][i_];
|
|
//--- change values
|
|
k0=CNearestNeighbor::KDTreeQueryKNN(tree0,x,qsizes[q],true);
|
|
k1=CNearestNeighbor::KDTreeQueryKNN(tree1,x,qsizes[q],true);
|
|
//--- check
|
|
if(k0!=k1)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsXY(tree0,xy0);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsXY(tree1,xy1);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<nx+ny; j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(xy0.Get(i,j)-xy1.Get(i,j))>threshold)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(tree0,tags0);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(tree1,tags1);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
//--- check
|
|
if(tags0[i]!=tags1[i])
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Next N
|
|
n+=25;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CAblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAblasUnit
|
|
{
|
|
public:
|
|
static bool TestAblas(const bool silent);
|
|
|
|
private:
|
|
static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta);
|
|
static bool TestTrsM(const int minn,const int maxn);
|
|
static bool TestSyrk(const int minn,const int maxn);
|
|
static bool TestGemm(const int minn,const int maxn);
|
|
static bool TestTrans(const int minn,const int maxn);
|
|
static bool TestRank1(const int minn,const int maxn);
|
|
static bool TestMV(const int minn,const int maxn);
|
|
static bool TestCopy(const int minn,const int maxn);
|
|
static void RefCMatrixRightTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2);
|
|
static void RefCMatrixLeftTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2);
|
|
static void RefRMatrixRightTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
static void RefRMatrixLeftTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
static bool InternalCMatrixTrInverse(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool InternalRMatrixTrInverse(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static void RefCMatrixSyrk(const int n,const int k,const double alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,const double beta,CMatrixComplex &c,const int ic,const int jc,const bool isupper);
|
|
static void RefRMatrixSyrk(const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,const double beta,CMatrixDouble &c,const int ic,const int jc,const bool isupper);
|
|
static void RefCMatrixGemm(const int m,const int n,const int k,complex &alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,CMatrixComplex &b,const int ib,const int jb,const int optypeb,complex &beta,CMatrixComplex &c,const int ic,const int jc);
|
|
static void RefRMatrixGemm(const int m,const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,CMatrixDouble &b,const int ib,const int jb,const int optypeb,const double beta,CMatrixDouble &c,const int ic,const int jc);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CAblas |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestAblas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
bool trsmerrors;
|
|
bool syrkerrors;
|
|
bool gemmerrors;
|
|
bool transerrors;
|
|
bool rank1errors;
|
|
bool mverrors;
|
|
bool copyerrors;
|
|
bool waserrors;
|
|
//--- create array
|
|
CMatrixDouble ra;
|
|
//--- initialization
|
|
trsmerrors=false;
|
|
syrkerrors=false;
|
|
gemmerrors=false;
|
|
transerrors=false;
|
|
rank1errors=false;
|
|
mverrors=false;
|
|
copyerrors=false;
|
|
waserrors=false;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
//--- search errors
|
|
trsmerrors=trsmerrors||TestTrsM(1,3*CAblas::AblasBlockSize()+1);
|
|
syrkerrors=syrkerrors||TestSyrk(1,3*CAblas::AblasBlockSize()+1);
|
|
gemmerrors=gemmerrors||TestGemm(1,3*CAblas::AblasBlockSize()+1);
|
|
transerrors=transerrors||TestTrans(1,3*CAblas::AblasBlockSize()+1);
|
|
rank1errors=rank1errors||TestRank1(1,3*CAblas::AblasBlockSize()+1);
|
|
mverrors=mverrors||TestMV(1,3*CAblas::AblasBlockSize()+1);
|
|
copyerrors=copyerrors||TestCopy(1,3*CAblas::AblasBlockSize()+1);
|
|
//--- report
|
|
waserrors=(((((trsmerrors||syrkerrors)||gemmerrors)||transerrors)||rank1errors)||mverrors)||copyerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ABLAS");
|
|
PrintResult("* TRSM",!trsmerrors);
|
|
PrintResult("* SYRK",!syrkerrors);
|
|
PrintResult("* GEMM",!gemmerrors);
|
|
PrintResult("* TRANS",!transerrors);
|
|
PrintResult("* RANK1",!rank1errors);
|
|
PrintResult("* MV",!mverrors);
|
|
PrintResult("* COPY",!copyerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a,
|
|
const int ai1,
|
|
const int ai2,
|
|
const int aj1,
|
|
const int aj2,
|
|
const bool transa,
|
|
CMatrixDouble &b,
|
|
const int bi1,
|
|
const int bi2,
|
|
const int bj1,
|
|
const int bj2,
|
|
const bool transb,
|
|
const double alpha,
|
|
CMatrixDouble &c,
|
|
const int ci1,
|
|
const int ci2,
|
|
const int cj1,
|
|
const int cj2,
|
|
const double beta)
|
|
{
|
|
//--- create variables
|
|
int arows=0;
|
|
int acols=0;
|
|
int brows=0;
|
|
int bcols=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int r=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
//--- Setup
|
|
if(!transa)
|
|
{
|
|
arows=ai2-ai1+1;
|
|
acols=aj2-aj1+1;
|
|
}
|
|
else
|
|
{
|
|
arows=aj2-aj1+1;
|
|
acols=ai2-ai1+1;
|
|
}
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
brows=bi2-bi1+1;
|
|
bcols=bj2-bj1+1;
|
|
}
|
|
else
|
|
{
|
|
brows=bj2-bj1+1;
|
|
bcols=bi2-bi1+1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!"))
|
|
return;
|
|
//--- check
|
|
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
|
|
return;
|
|
//--- change values
|
|
l=arows;
|
|
r=bcols;
|
|
k=acols;
|
|
//--- allocation
|
|
ArrayResize(x1,k+1);
|
|
ArrayResize(x2,k+1);
|
|
//--- calculation
|
|
for(i=1; i<=l; i++)
|
|
{
|
|
for(j=1; j<=r; j++)
|
|
{
|
|
//--- check
|
|
if(!transa)
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1; i_<=bi2; i_++)
|
|
v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1; i_<=bj2; i_++)
|
|
v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1; i_<=bi2; i_++)
|
|
v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1; i_<=bj2; i_++)
|
|
v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
}
|
|
//--- check
|
|
if(beta==0.0)
|
|
c.Set(ci1+i-1,cj1+j-1,alpha*v);
|
|
else
|
|
c.Set(ci1+i-1,cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| ?Matrix????TRSM tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestTrsM(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int m=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int optype=0;
|
|
int uppertype=0;
|
|
int unittype=0;
|
|
int xoffsi=0;
|
|
int xoffsj=0;
|
|
int aoffsitype=0;
|
|
int aoffsjtype=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrxl;
|
|
CMatrixDouble refrxr;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcxl;
|
|
CMatrixComplex refcxr;
|
|
CMatrixDouble ra;
|
|
CMatrixComplex ca;
|
|
CMatrixDouble rxr1;
|
|
CMatrixDouble rxl1;
|
|
CMatrixComplex cxr1;
|
|
CMatrixComplex cxl1;
|
|
CMatrixDouble rxr2;
|
|
CMatrixDouble rxl2;
|
|
CMatrixComplex cxr2;
|
|
CMatrixComplex cxl2;
|
|
//--- initialization
|
|
threshold=CMath::Sqr(maxn)*100*CMath::m_machineepsilon;
|
|
result=false;
|
|
//--- calculation
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize RefRA/RefCA by random matrices whose upper
|
|
//--- and lower triangle submatrices are non-degenerate
|
|
//--- well-conditioned matrices.
|
|
//--- Matrix size is 2Mx2M (four copies of same MxM matrix
|
|
//--- to test different offsets)
|
|
refra.Resize(2*m,2*m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
refra.Set(i,j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
refra.Set(i,i,(2*CMath::RandomInteger(1)-1)*(2*m+CMath::RandomReal()));
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
refra.Set(i+m,j,refra.Get(i,j));
|
|
refra.Set(i,j+m,refra.Get(i,j));
|
|
refra.Set(i+m,j+m,refra.Get(i,j));
|
|
}
|
|
}
|
|
//--- allocation
|
|
refca.Resize(2*m,2*m);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
refca.SetRe(i,j,0.2*CMath::RandomReal()-0.1);
|
|
refca.SetIm(i,j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
refca.SetRe(i,i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
|
|
refca.SetIm(i,i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
refca.Set(i+m,j,refca.Get(i,j));
|
|
refca.Set(i,j+m,refca.Get(i,j));
|
|
refca.Set(i+m,j+m,refca.Get(i,j));
|
|
}
|
|
}
|
|
//--- Generate random XL/XR.
|
|
//--- XR is NxM matrix (matrix for 'Right' subroutines)
|
|
//--- XL is MxN matrix (matrix for 'Left' subroutines)
|
|
refrxr.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
refrxr.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
refrxl.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
refrxl.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
refcxr.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
refcxr.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refcxr.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
refcxl.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
refcxl.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refcxl.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- test different types of operations,offsets,and so on...
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
ra.Resize(2*m,2*m);
|
|
rxr1.Resize(n,m);
|
|
rxr2.Resize(n,m);
|
|
rxl1.Resize(m,n);
|
|
rxl2.Resize(m,n);
|
|
ca.Resize(2*m,2*m);
|
|
cxr1.Resize(n,m);
|
|
cxr2.Resize(n,m);
|
|
cxl1.Resize(m,n);
|
|
cxl2.Resize(m,n);
|
|
//--- initialization
|
|
optype=CMath::RandomInteger(3);
|
|
uppertype=CMath::RandomInteger(2);
|
|
unittype=CMath::RandomInteger(2);
|
|
xoffsi=CMath::RandomInteger(2);
|
|
xoffsj=CMath::RandomInteger(2);
|
|
aoffsitype=CMath::RandomInteger(2);
|
|
aoffsjtype=CMath::RandomInteger(2);
|
|
aoffsi=m*aoffsitype;
|
|
aoffsj=m*aoffsjtype;
|
|
//--- copy A,XR,XL (fill unused parts with random garbage)
|
|
for(i=0; i<=2*m-1; i++)
|
|
{
|
|
for(j=0; j<=2*m-1; j++)
|
|
{
|
|
//--- check
|
|
if(((i>=aoffsi && i<aoffsi+m) && j>=aoffsj) && j<aoffsj+m)
|
|
{
|
|
ca.Set(i,j,refca.Get(i,j));
|
|
ra.Set(i,j,refra.Get(i,j));
|
|
}
|
|
else
|
|
{
|
|
ca.Set(i,j,CMath::RandomReal());
|
|
ra.Set(i,j,CMath::RandomReal());
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- check
|
|
if(i>=xoffsi && j>=xoffsj)
|
|
{
|
|
cxr1.Set(i,j,refcxr.Get(i,j));
|
|
cxr2.Set(i,j,refcxr.Get(i,j));
|
|
rxr1.Set(i,j,refrxr.Get(i,j));
|
|
rxr2.Set(i,j,refrxr.Get(i,j));
|
|
}
|
|
else
|
|
{
|
|
cxr1.Set(i,j,CMath::RandomReal());
|
|
cxr2.Set(i,j,cxr1.Get(i,j));
|
|
rxr1.Set(i,j,CMath::RandomReal());
|
|
rxr2.Set(i,j,rxr1.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i>=xoffsi && j>=xoffsj)
|
|
{
|
|
cxl1.Set(i,j,refcxl.Get(i,j));
|
|
cxl2.Set(i,j,refcxl.Get(i,j));
|
|
rxl1.Set(i,j,refrxl.Get(i,j));
|
|
rxl2.Set(i,j,refrxl.Get(i,j));
|
|
}
|
|
else
|
|
{
|
|
cxl1.Set(i,j,CMath::RandomReal());
|
|
cxl2.Set(i,j,cxl1.Get(i,j));
|
|
rxl1.Set(i,j,CMath::RandomReal());
|
|
rxl2.Set(i,j,rxl1.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
//--- Test CXR
|
|
CAblas::CMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr1,xoffsi,xoffsj);
|
|
RefCMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
result=result||CMath::AbsComplex(cxr1.Get(i,j)-cxr2.Get(i,j))>threshold;
|
|
}
|
|
//--- Test CXL
|
|
CAblas::CMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl1,xoffsi,xoffsj);
|
|
RefCMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
result=result||CMath::AbsComplex(cxl1.Get(i,j)-cxl2.Get(i,j))>threshold;
|
|
}
|
|
//--- check
|
|
if(optype<2)
|
|
{
|
|
//--- Test RXR
|
|
CAblas::RMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr1,xoffsi,xoffsj);
|
|
RefRMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
result=result||MathAbs(rxr1.Get(i,j)-rxr2.Get(i,j))>threshold;
|
|
}
|
|
//--- Test RXL
|
|
CAblas::RMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl1,xoffsi,xoffsj);
|
|
RefRMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
result=result||MathAbs(rxl1.Get(i,j)-rxl2.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SYRK tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestSyrk(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int k=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int uppertype=0;
|
|
int xoffsi=0;
|
|
int xoffsj=0;
|
|
int aoffsitype=0;
|
|
int aoffsjtype=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int alphatype=0;
|
|
int betatype=0;
|
|
double alpha=0;
|
|
double beta=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrc;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcc;
|
|
CMatrixDouble ra1;
|
|
CMatrixDouble ra2;
|
|
CMatrixComplex ca1;
|
|
CMatrixComplex ca2;
|
|
CMatrixDouble rc;
|
|
CMatrixDouble rct;
|
|
CMatrixComplex cc;
|
|
CMatrixComplex cct;
|
|
//--- initialization
|
|
threshold=maxn*100*CMath::m_machineepsilon;
|
|
result=false;
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
k=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
k=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize RefRA/RefCA by random Hermitian matrices,
|
|
//--- RefRC/RefCC by random matrices
|
|
//--- RA/CA size is 2Nx2N (four copies of same NxN matrix
|
|
//--- to test different offsets)
|
|
refra.Resize(2*n,2*n);
|
|
refca.Resize(2*n,2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
refra.Set(i,i,2*CMath::RandomReal()-1);
|
|
refca.Set(i,i,2*CMath::RandomReal()-1);
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
refra.Set(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
refra.Set(j,i,refra.Get(i,j));
|
|
refca.Set(j,i,CMath::Conj(refca.Get(i,j)));
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
refra.Set(i+n,j,refra.Get(i,j));
|
|
refra.Set(i,j+n,refra.Get(i,j));
|
|
refra.Set(i+n,j+n,refra.Get(i,j));
|
|
refca.Set(i+n,j,refca.Get(i,j));
|
|
refca.Set(i,j+n,refca.Get(i,j));
|
|
refca.Set(i+n,j+n,refca.Get(i,j));
|
|
}
|
|
}
|
|
//--- allocation
|
|
refrc.Resize(n,k);
|
|
refcc.Resize(n,k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
{
|
|
refrc.Set(i,j,2*CMath::RandomReal()-1);
|
|
refcc.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refcc.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- test different types of operations,offsets,and so on...
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
ra1.Resize(2*n,2*n);
|
|
ra2.Resize(2*n,2*n);
|
|
ca1.Resize(2*n,2*n);
|
|
ca2.Resize(2*n,2*n);
|
|
rc.Resize(n,k);
|
|
rct.Resize(k,n);
|
|
cc.Resize(n,k);
|
|
cct.Resize(k,n);
|
|
//--- initialization
|
|
uppertype=CMath::RandomInteger(2);
|
|
xoffsi=CMath::RandomInteger(2);
|
|
xoffsj=CMath::RandomInteger(2);
|
|
aoffsitype=CMath::RandomInteger(2);
|
|
aoffsjtype=CMath::RandomInteger(2);
|
|
alphatype=CMath::RandomInteger(2);
|
|
betatype=CMath::RandomInteger(2);
|
|
aoffsi=n*aoffsitype;
|
|
aoffsj=n*aoffsjtype;
|
|
alpha=alphatype*(2*CMath::RandomReal()-1);
|
|
beta=betatype*(2*CMath::RandomReal()-1);
|
|
//--- copy A,C (fill unused parts with random garbage)
|
|
for(i=0; i<=2*n-1; i++)
|
|
{
|
|
for(j=0; j<=2*n-1; j++)
|
|
{
|
|
//--- check
|
|
if(((i>=aoffsi && i<aoffsi+n) && j>=aoffsj) && j<aoffsj+n)
|
|
{
|
|
ca1.Set(i,j,refca.Get(i,j));
|
|
ca2.Set(i,j,refca.Get(i,j));
|
|
ra1.Set(i,j,refra.Get(i,j));
|
|
ra2.Set(i,j,refra.Get(i,j));
|
|
}
|
|
else
|
|
{
|
|
ca1.Set(i,j,CMath::RandomReal());
|
|
ca2.Set(i,j,ca1.Get(i,j));
|
|
ra1.Set(i,j,CMath::RandomReal());
|
|
ra2.Set(i,j,ra1.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
{
|
|
//--- check
|
|
if(i>=xoffsi && j>=xoffsj)
|
|
{
|
|
rc.Set(i,j,refrc.Get(i,j));
|
|
rct.Set(j,i,refrc.Get(i,j));
|
|
cc.Set(i,j,refcc.Get(i,j));
|
|
cct.Set(j,i,refcc.Get(i,j));
|
|
}
|
|
else
|
|
{
|
|
rc.Set(i,j,CMath::RandomReal());
|
|
rct.Set(j,i,rc.Get(i,j));
|
|
cc.Set(i,j,CMath::RandomReal());
|
|
cct.Set(j,i,cct[j][i]);
|
|
}
|
|
}
|
|
}
|
|
//--- Test complex
|
|
//--- Only one of transform types is selected and tested
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca1,aoffsi,aoffsj,uppertype==0);
|
|
RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
else
|
|
{
|
|
CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca1,aoffsi,aoffsj,uppertype==0);
|
|
RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
result=result||CMath::AbsComplex(ca1.Get(i,j)-ca2.Get(i,j))>threshold;
|
|
}
|
|
//--- Test real
|
|
//--- Only one of transform types is selected and tested
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra1,aoffsi,aoffsj,uppertype==0);
|
|
RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
else
|
|
{
|
|
CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra1,aoffsi,aoffsj,uppertype==0);
|
|
RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
result=result||MathAbs(ra1.Get(i,j)-ra2.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| GEMM tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestGemm(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int k=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int aoptype=0;
|
|
int aoptyper=0;
|
|
int boffsi=0;
|
|
int boffsj=0;
|
|
int boptype=0;
|
|
int boptyper=0;
|
|
int coffsi=0;
|
|
int coffsj=0;
|
|
double alphar=0;
|
|
double betar=0;
|
|
complex alphac=0;
|
|
complex betac=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrb;
|
|
CMatrixDouble refrc;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcb;
|
|
CMatrixComplex refcc;
|
|
CMatrixDouble rc1;
|
|
CMatrixDouble rc2;
|
|
CMatrixComplex cc1;
|
|
CMatrixComplex cc2;
|
|
//--- initialization
|
|
threshold=maxn*100*CMath::m_machineepsilon;
|
|
result=false;
|
|
//--- calculation
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N/K in [1,MX] such that max(M,N,K)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
k=1+CMath::RandomInteger(mx);
|
|
i=CMath::RandomInteger(3);
|
|
//--- check
|
|
if(i==0)
|
|
m=mx;
|
|
//--- check
|
|
if(i==1)
|
|
n=mx;
|
|
//--- check
|
|
if(i==2)
|
|
k=mx;
|
|
//--- Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1)
|
|
refra.Resize(maxn+1,maxn+1);
|
|
refrb.Resize(maxn+1,maxn+1);
|
|
refrc.Resize(maxn+1,maxn+1);
|
|
refca.Resize(maxn+1,maxn+1);
|
|
refcb.Resize(maxn+1,maxn+1);
|
|
refcc.Resize(maxn+1,maxn+1);
|
|
//--- change values
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
{
|
|
refra.Set(i,j,2*CMath::RandomReal()-1);
|
|
refrb.Set(i,j,2*CMath::RandomReal()-1);
|
|
refrc.Set(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
refcb.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refcb.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
refcc.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refcc.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- test different types of operations,offsets,and so on...
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
rc1.Resize(maxn+1,maxn+1);
|
|
rc2.Resize(maxn+1,maxn+1);
|
|
cc1.Resize(maxn+1,maxn+1);
|
|
cc2.Resize(maxn+1,maxn+1);
|
|
//--- initialization
|
|
aoffsi=CMath::RandomInteger(2);
|
|
aoffsj=CMath::RandomInteger(2);
|
|
aoptype=CMath::RandomInteger(3);
|
|
aoptyper=CMath::RandomInteger(2);
|
|
boffsi=CMath::RandomInteger(2);
|
|
boffsj=CMath::RandomInteger(2);
|
|
boptype=CMath::RandomInteger(3);
|
|
boptyper=CMath::RandomInteger(2);
|
|
coffsi=CMath::RandomInteger(2);
|
|
coffsj=CMath::RandomInteger(2);
|
|
alphar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1);
|
|
betar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
alphac.real=2*CMath::RandomReal()-1;
|
|
alphac.imag=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
alphac=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
betac.real=2*CMath::RandomReal()-1;
|
|
betac.imag=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
betac=0;
|
|
//--- copy C
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
{
|
|
rc1.Set(i,j,refrc.Get(i,j));
|
|
rc2.Set(i,j,refrc.Get(i,j));
|
|
cc1.Set(i,j,refcc.Get(i,j));
|
|
cc2.Set(i,j,refcc.Get(i,j));
|
|
}
|
|
}
|
|
//--- Test complex
|
|
CAblas::CMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc1,coffsi,coffsj);
|
|
RefCMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc2,coffsi,coffsj);
|
|
//--- search errors
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
result=result||CMath::AbsComplex(cc1.Get(i,j)-cc2.Get(i,j))>threshold;
|
|
}
|
|
//--- Test real
|
|
CAblas::RMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc1,coffsi,coffsj);
|
|
RefRMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc2,coffsi,coffsj);
|
|
//--- search errors
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
result=result||MathAbs(rc1.Get(i,j)-rc2.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| transpose tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestTrans(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int boffsi=0;
|
|
int boffsj=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrb;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcb;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
//--- Generate random V1 and V2 which are used to fill
|
|
//--- RefRB/RefCB with control values.
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- change values
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
//--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
|
|
//--- Fill B with control values
|
|
refra.Resize(maxn+1,maxn+1);
|
|
refrb.Resize(maxn+1,maxn+1);
|
|
refca.Resize(maxn+1,maxn+1);
|
|
refcb.Resize(maxn+1,maxn+1);
|
|
//--- change values
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
{
|
|
refra.Set(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
refrb.Set(i,j,i*v1+j*v2);
|
|
refcb.Set(i,j,i*v1+j*v2);
|
|
}
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(2);
|
|
aoffsj=CMath::RandomInteger(2);
|
|
boffsi=CMath::RandomInteger(2);
|
|
boffsj=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CAblas::RMatrixTranspose(m,n,refra,aoffsi,aoffsj,refrb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+n) || j<boffsj) || j>=boffsj+m)
|
|
result=result||MathAbs(refrb.Get(i,j)-(v1*i+v2*j))>threshold;
|
|
else
|
|
result=result||MathAbs(refrb.Get(i,j)-refra[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixTranspose(m,n,refca,aoffsi,aoffsj,refcb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+n) || j<boffsj) || j>=boffsj+m)
|
|
result=result||CMath::AbsComplex(refcb.Get(i,j)-(v1*i+v2*j))>threshold;
|
|
else
|
|
result=result||CMath::AbsComplex(refcb.Get(i,j)-refca[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| rank-1tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestRank1(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int uoffs=0;
|
|
int voffs=0;
|
|
double threshold=0;
|
|
//--- create arrays
|
|
double ru[];
|
|
double rv[];
|
|
complex cu[];
|
|
complex cv[];
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrb;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcb;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
|
|
//--- Fill B with control values
|
|
refra.Resize(maxn+maxn,maxn+maxn);
|
|
refrb.Resize(maxn+maxn,maxn+maxn);
|
|
refca.Resize(maxn+maxn,maxn+maxn);
|
|
refcb.Resize(maxn+maxn,maxn+maxn);
|
|
//--- change values
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
refra.Set(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
refrb.Set(i,j,refra.Get(i,j));
|
|
refcb.Set(i,j,refca.Get(i,j));
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ru,2*m);
|
|
ArrayResize(cu,2*m);
|
|
//--- change values
|
|
for(i=0; i<=2*m-1; i++)
|
|
{
|
|
ru[i]=2*CMath::RandomReal()-1;
|
|
cu[i].real=2*CMath::RandomReal()-1;
|
|
cu[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(rv,2*n);
|
|
ArrayResize(cv,2*n);
|
|
//--- change values
|
|
for(i=0; i<=2*n-1; i++)
|
|
{
|
|
rv[i]=2*CMath::RandomReal()-1;
|
|
cv[i].real=2*CMath::RandomReal()-1;
|
|
cv[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(maxn);
|
|
aoffsj=CMath::RandomInteger(maxn);
|
|
uoffs=CMath::RandomInteger(m);
|
|
voffs=CMath::RandomInteger(n);
|
|
//--- function call
|
|
CAblas::CMatrixRank1(m,n,refca,aoffsi,aoffsj,cu,uoffs,cv,voffs);
|
|
//--- search errors
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
//--- check
|
|
if(((i<aoffsi || i>=aoffsi+m) || j<aoffsj) || j>=aoffsj+n)
|
|
result=result||CMath::AbsComplex(refca.Get(i,j)-refcb.Get(i,j))>threshold;
|
|
else
|
|
result=result||CMath::AbsComplex(refca.Get(i,j)-(refcb.Get(i,j)+cu[i-aoffsi+uoffs]*cv[j-aoffsj+voffs]))>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixRank1(m,n,refra,aoffsi,aoffsj,ru,uoffs,rv,voffs);
|
|
//--- search errors
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
//--- check
|
|
if(((i<aoffsi || i>=aoffsi+m) || j<aoffsj) || j>=aoffsj+n)
|
|
result=result||MathAbs(refra.Get(i,j)-refrb.Get(i,j))>threshold;
|
|
else
|
|
result=result||MathAbs(refra.Get(i,j)-(refrb.Get(i,j)+ru[i-aoffsi+uoffs]*rv[j-aoffsj+voffs]))>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| MV tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestMV(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int xoffs=0;
|
|
int yoffs=0;
|
|
int opca=0;
|
|
int opra=0;
|
|
double threshold=0;
|
|
double rv1=0;
|
|
double rv2=0;
|
|
complex cv1=0;
|
|
complex cv2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double rx[];
|
|
double ry[];
|
|
complex cx[];
|
|
complex cy[];
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixComplex refca;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
|
|
//--- Initialize X by random vector with size (MaxN+MaxN)
|
|
//--- Fill Y by control values
|
|
refra.Resize(maxn+maxn,maxn+maxn);
|
|
refca.Resize(maxn+maxn,maxn+maxn);
|
|
//--- change values
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
refra.Set(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
refca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(rx,2*maxn);
|
|
ArrayResize(cx,2*maxn);
|
|
ArrayResize(ry,2*maxn);
|
|
ArrayResize(cy,2*maxn);
|
|
//--- change values
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
rx[i]=2*CMath::RandomReal()-1;
|
|
cx[i].real=2*CMath::RandomReal()-1;
|
|
cx[i].imag=2*CMath::RandomReal()-1;
|
|
ry[i]=i;
|
|
cy[i]=i;
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(maxn);
|
|
aoffsj=CMath::RandomInteger(maxn);
|
|
xoffs=CMath::RandomInteger(maxn);
|
|
yoffs=CMath::RandomInteger(maxn);
|
|
opca=CMath::RandomInteger(3);
|
|
opra=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CAblas::CMatrixMVect(m,n,refca,aoffsi,aoffsj,opca,cx,xoffs,cy,yoffs);
|
|
//--- search errors
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
//--- check
|
|
if(i<yoffs || i>=yoffs+m)
|
|
result=result||cy[i]!=i;
|
|
else
|
|
{
|
|
cv1=cy[i];
|
|
cv2=0.0;
|
|
//--- check
|
|
if(opca==0)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsj;
|
|
cv2=0.0;
|
|
for(i_=aoffsj; i_<=aoffsj+n-1; i_++)
|
|
cv2+=refca[aoffsi+i-yoffs][i_]*cx[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(opca==1)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsi;
|
|
cv2=0.0;
|
|
for(i_=aoffsi; i_<=aoffsi+n-1; i_++)
|
|
cv2+=refca[i_][aoffsj+i-yoffs]*cx[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(opca==2)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsi;
|
|
cv2=0.0;
|
|
for(i_=aoffsi; i_<=aoffsi+n-1; i_++)
|
|
cv2+=CMath::Conj(refca[i_][aoffsj+i-yoffs])*cx[i_+i1_];
|
|
}
|
|
result=result||CMath::AbsComplex(cv1-cv2)>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixMVect(m,n,refra,aoffsi,aoffsj,opra,rx,xoffs,ry,yoffs);
|
|
//--- function call
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
//--- check
|
|
if(i<yoffs || i>=yoffs+m)
|
|
result=result||ry[i]!=i;
|
|
else
|
|
{
|
|
rv1=ry[i];
|
|
rv2=0;
|
|
//--- check
|
|
if(opra==0)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsj;
|
|
rv2=0.0;
|
|
for(i_=aoffsj; i_<=aoffsj+n-1; i_++)
|
|
rv2+=refra[aoffsi+i-yoffs][i_]*rx[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(opra==1)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsi;
|
|
rv2=0.0;
|
|
for(i_=aoffsi; i_<=aoffsi+n-1; i_++)
|
|
rv2+=refra[i_][aoffsj+i-yoffs]*rx[i_+i1_];
|
|
}
|
|
result=result||MathAbs(rv1-rv2)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| COPY tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::TestCopy(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int boffsi=0;
|
|
int boffsj=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixDouble rb;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cb;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn; mx<=maxn; mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
|
|
//--- Initialize X by random vector with size (MaxN+MaxN)
|
|
//--- Fill Y by control values
|
|
ra.Resize(maxn+maxn,maxn+maxn);
|
|
ca.Resize(maxn+maxn,maxn+maxn);
|
|
rb.Resize(maxn+maxn,maxn+maxn);
|
|
cb.Resize(maxn+maxn,maxn+maxn);
|
|
//--- change values
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
ra.Set(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
rb.Set(i,j,1+2*i+3*j);
|
|
cb.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(maxn);
|
|
aoffsj=CMath::RandomInteger(maxn);
|
|
boffsi=CMath::RandomInteger(maxn);
|
|
boffsj=CMath::RandomInteger(maxn);
|
|
//--- function call
|
|
CAblas::CMatrixCopy(m,n,ca,aoffsi,aoffsj,cb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+m) || j<boffsj) || j>=boffsj+n)
|
|
result=result||cb.Get(i,j)!=1+2*i+3*j;
|
|
else
|
|
result=result||CMath::AbsComplex(ca[aoffsi+i-boffsi][aoffsj+j-boffsj]-cb.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixCopy(m,n,ra,aoffsi,aoffsj,rb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0; i<=2*maxn-1; i++)
|
|
{
|
|
for(j=0; j<=2*maxn-1; j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+m) || j<boffsj) || j>=boffsj+n)
|
|
result=result||rb.Get(i,j)!=1+2*i+3*j;
|
|
else
|
|
result=result||MathAbs(ra[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefCMatrixRightTrsM(const int m,const int n,
|
|
CMatrixComplex &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixComplex &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a1.Set(i,j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=i; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a1.Set(i,i,1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
a2.Resize(n,n);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a2.Set(i,j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- check
|
|
if(optype==2)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a2.Set(i,j,CMath::Conj(a1[j][i]));
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalCMatrixTrInverse(a2,n,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,n);
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
i1_=j2;
|
|
for(i_=0; i_<n; i_++)
|
|
tx[i_]=x[i2+i][i_+i1_];
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
{
|
|
vc=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vc+=tx[i_]*a2.Get(i_,j);
|
|
x.Set(i2+i,j2+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefCMatrixLeftTrsM(const int m,const int n,
|
|
CMatrixComplex &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixComplex &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(m,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a1.Set(i,j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=i; j<m; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=i; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
a1.Set(i,i,1);
|
|
}
|
|
//--- allocation
|
|
a2.Resize(m,m);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a2.Set(i,j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- check
|
|
if(optype==2)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a2.Set(i,j,CMath::Conj(a1[j][i]));
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalCMatrixTrInverse(a2,m,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,m);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
i1_=i2;
|
|
for(i_=0; i_<m; i_++)
|
|
tx[i_]=x[i_+i1_][j2+j];
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
vc=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
vc+=a2.Get(i,i_)*tx[i_];
|
|
x.Set(i2+i,j2+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefRMatrixRightTrsM(const int m,const int n,
|
|
CMatrixDouble &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixDouble &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a1.Set(i,j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=i; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
a1.Set(i,i,1);
|
|
}
|
|
//--- allocation
|
|
a2.Resize(n,n);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a2.Set(i,j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalRMatrixTrInverse(a2,n,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
i1_=j2;
|
|
for(i_=0; i_<n; i_++)
|
|
tx[i_]=x[i2+i][i_+i1_];
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
{
|
|
vr=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vr+=tx[i_]*a2.Get(i_,j);
|
|
x.Set(i2+i,j2+j,vr);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefRMatrixLeftTrsM(const int m,const int n,
|
|
CMatrixDouble &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixDouble &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(m,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a1.Set(i,j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=i; j<m; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=i; j++)
|
|
a1.Set(i,j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
a1.Set(i,i,1);
|
|
}
|
|
//--- allocation
|
|
a2.Resize(m,m);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
a2.Set(i,j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalRMatrixTrInverse(a2,m,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,m);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
i1_=i2;
|
|
for(i_=0; i_<m; i_++)
|
|
tx[i_]=x[i_+i1_][j2+j];
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
vr=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
vr+=a2.Get(i,i_)*tx[i_];
|
|
x.Set(i2+i,j2+j,vr);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Triangular matrix inversion |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| February 29,1992 |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::InternalCMatrixTrInverse(CMatrixComplex &a,
|
|
const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
complex ajj=0;
|
|
complex one=1;
|
|
int i_=0;
|
|
//--- create array
|
|
complex t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a.Set(j,j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0; i_<j; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=0; i<j; i++)
|
|
{
|
|
//--- check
|
|
if(i+1<j)
|
|
{
|
|
v=0.0;
|
|
for(i_=i+1; i_<j; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=0; i_<j; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a.Set(j,j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j+1<n)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1; i_<n; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
v=0.0;
|
|
for(i_=j+1; i_<i; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Triangular matrix inversion |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| February 29,1992 |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasUnit::InternalRMatrixTrInverse(CMatrixDouble &a,
|
|
const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a.Set(j,j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0; i_<j; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=0; i<j; i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
v=0.0;
|
|
for(i_=i+1; i_<j; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=0; i_<j; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a.Set(j,j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1; i_<n; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
v=0.0;
|
|
for(i_=j+1; i_<i; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference SYRK subroutine. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefCMatrixSyrk(const int n,const int k,
|
|
const double alpha,CMatrixComplex &a,
|
|
const int ia,const int ja,
|
|
const int optypea,const double beta,
|
|
CMatrixComplex &c,const int ic,
|
|
const int jc,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex ae;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((isupper && j>=i) || (!isupper && j<=i))
|
|
{
|
|
//--- check
|
|
if(beta==0.0)
|
|
c.Set(i+ic,j+jc,0);
|
|
else
|
|
c.Set(i+ic,j+jc,c[i+ic][j+jc]*beta);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(alpha==0.0)
|
|
return;
|
|
//--- check
|
|
if(n*k>0)
|
|
ae.Resize(n,k);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae.Set(i,j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==2)
|
|
ae.Set(i,j,CMath::Conj(a[ia+j][ja+i]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
vc=0;
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
vc=0.0;
|
|
for(i_=0; i_<k; i_++)
|
|
vc+=ae.Get(i,i_)*CMath::Conj(ae.Get(j,i_));
|
|
}
|
|
vc=vc*alpha;
|
|
//--- check
|
|
if(isupper && j>=i)
|
|
c.Set(i+ic,jc+j,vc+c[ic+i][jc+j]);
|
|
//--- check
|
|
if(!isupper && j<=i)
|
|
c.Set(i+ic,jc+j,vc+c[ic+i][jc+j]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference SYRK subroutine. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefRMatrixSyrk(const int n,const int k,
|
|
const double alpha,CMatrixDouble &a,
|
|
const int ia,const int ja,
|
|
const int optypea,const double beta,
|
|
CMatrixDouble &c,const int ic,
|
|
const int jc,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble ae;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((isupper && j>=i) || (!isupper && j<=i))
|
|
{
|
|
//--- check
|
|
if(beta==0.0)
|
|
c.Set(i+ic,j+jc,0);
|
|
else
|
|
c.Set(i+ic,j+jc,c[i+ic][j+jc]*beta);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(alpha==0.0)
|
|
return;
|
|
//--- check
|
|
if(n*k>0)
|
|
ae.Resize(n,k);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae.Set(i,j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==1)
|
|
ae.Set(i,j,a[ia+j][ja+i]);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
vr=0;
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
vr=0.0;
|
|
for(i_=0; i_<k; i_++)
|
|
vr+=ae.Get(i,i_)*ae.Get(j,i_);
|
|
}
|
|
vr=alpha*vr;
|
|
//--- check
|
|
if(isupper && j>=i)
|
|
c.Set(i+ic,jc+j,vr+c[ic+i][jc+j]);
|
|
//--- check
|
|
if(!isupper && j<=i)
|
|
c.Set(i+ic,jc+j,vr+c[ic+i][jc+j]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference GEMM, |
|
|
//| ALGLIB subroutine |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefCMatrixGemm(const int m,const int n,
|
|
const int k,complex &alpha,
|
|
CMatrixComplex &a,const int ia,
|
|
const int ja,const int optypea,
|
|
CMatrixComplex &b,const int ib,
|
|
const int jb,const int optypeb,
|
|
complex &beta,CMatrixComplex &c,
|
|
const int ic,const int jc)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex ae;
|
|
CMatrixComplex be;
|
|
//--- allocation
|
|
ae.Resize(m,k);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae.Set(i,j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==1)
|
|
ae.Set(i,j,a[ia+j][ja+i]);
|
|
//--- check
|
|
if(optypea==2)
|
|
ae.Set(i,j,CMath::Conj(a[ia+j][ja+i]));
|
|
}
|
|
}
|
|
//--- allocation
|
|
be.Resize(k,n);
|
|
//--- change values
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(optypeb==0)
|
|
be.Set(i,j,b[ib+i][jb+j]);
|
|
//--- check
|
|
if(optypeb==1)
|
|
be.Set(i,j,b[ib+j][jb+i]);
|
|
//--- check
|
|
if(optypeb==2)
|
|
be.Set(i,j,CMath::Conj(b[ib+j][jb+i]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
vc=0.0;
|
|
for(i_=0; i_<k; i_++)
|
|
vc+=ae.Get(i,i_)*be.Get(i_,j);
|
|
vc=alpha*vc;
|
|
//--- check
|
|
if(beta!=0)
|
|
vc=vc+beta*c[ic+i][jc+j];
|
|
c.Set(i+ic,jc+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference GEMM, |
|
|
//| ALGLIB subroutine |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasUnit::RefRMatrixGemm(const int m,const int n,
|
|
const int k,const double alpha,
|
|
CMatrixDouble &a,const int ia,
|
|
const int ja,const int optypea,
|
|
CMatrixDouble &b,const int ib,
|
|
const int jb,const int optypeb,
|
|
const double beta,CMatrixDouble &c,
|
|
const int ic,const int jc)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vc=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble ae;
|
|
CMatrixDouble be;
|
|
//--- allocation
|
|
ae.Resize(m,k);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae.Set(i,j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==1)
|
|
ae.Set(i,j,a[ia+j][ja+i]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
be.Resize(k,n);
|
|
//--- change values
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(optypeb==0)
|
|
be.Set(i,j,b[ib+i][jb+j]);
|
|
//--- check
|
|
if(optypeb==1)
|
|
be.Set(i,j,b[ib+j][jb+i]);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
vc=0.0;
|
|
for(i_=0; i_<k; i_++)
|
|
vc+=ae.Get(i,i_)*be.Get(i_,j);
|
|
vc=alpha*vc;
|
|
//--- check
|
|
if(beta!=0.0)
|
|
vc=vc+beta*c[ic+i][jc+j];
|
|
c.Set(i+ic,jc+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBaseStat |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBaseStatUnit
|
|
{
|
|
public:
|
|
static bool TestBaseStat(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBaseStat |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestBaseStatUnit::TestBaseStat(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool s1errors;
|
|
bool covcorrerrors;
|
|
double threshold=0;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int kx=0;
|
|
int ky=0;
|
|
int ctype=0;
|
|
int cidxx=0;
|
|
int cidxy=0;
|
|
double mean=0;
|
|
double variance=0;
|
|
double skewness=0;
|
|
double kurtosis=0;
|
|
double adev=0;
|
|
double median=0;
|
|
double pv=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble mx;
|
|
CMatrixDouble my;
|
|
CMatrixDouble cc;
|
|
CMatrixDouble cp;
|
|
CMatrixDouble cs;
|
|
//--- Primary settings
|
|
waserrors=false;
|
|
s1errors=false;
|
|
covcorrerrors=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- * prepare X and Y - two test samples
|
|
//--- * test 1-sample coefficients
|
|
n=10;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=CMath::Sqr(i);
|
|
//--- function call
|
|
CBaseStat::SampleMoments(x,n,mean,variance,skewness,kurtosis);
|
|
//--- search errors
|
|
s1errors=s1errors||MathAbs(mean-28.5)>0.001;
|
|
s1errors=s1errors||MathAbs(variance-801.1667)>0.001;
|
|
s1errors=s1errors||MathAbs(skewness-0.5751)>0.001;
|
|
s1errors=s1errors||MathAbs(kurtosis+1.2666)>0.001;
|
|
//--- function call
|
|
CBaseStat::SampleAdev(x,n,adev);
|
|
//--- search errors
|
|
s1errors=s1errors||MathAbs(adev-23.2000)>0.001;
|
|
//--- function call
|
|
CBaseStat::SampleMedian(x,n,median);
|
|
//--- search errors
|
|
s1errors=s1errors||MathAbs(median-0.5*(16+25))>0.001;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- function call
|
|
CBaseStat::SamplePercentile(x,n,(double)i/(double)(n-1),pv);
|
|
//--- search errors
|
|
s1errors=s1errors||MathAbs(pv-x[i])>0.001;
|
|
}
|
|
//--- function call
|
|
CBaseStat::SamplePercentile(x,n,0.5,pv);
|
|
//--- search errors
|
|
s1errors=s1errors||MathAbs(pv-0.5*(16+25))>0.001;
|
|
//--- test covariance/correlation:
|
|
//--- * 2-sample coefficients
|
|
//--- We generate random matrices MX and MY
|
|
n=10;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=CMath::Sqr(i);
|
|
y[i]=i;
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-0.9627)>0.0001;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-1.0000)>0.0001;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::Cov2(x,y,n)-82.5000)>0.0001;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=CMath::Sqr(i-0.5*n);
|
|
y[i]=i;
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)+0.3676)>0.0001;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)+0.2761)>0.0001;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::Cov2(x,y,n)+9.1667)>0.0001;
|
|
//--- test covariance/correlation:
|
|
//--- * matrix covariance/correlation
|
|
//--- * matrix cross-covariance/cross-correlation
|
|
//--- We generate random matrices MX and MY which contain KX (KY)
|
|
//--- columns,all except one are random,one of them is constant.
|
|
//--- We test that function (a) do not crash on constant column,
|
|
//--- and (b) return variances/correlations that are exactly zero
|
|
//--- for this column.
|
|
//--- CType control variable controls type of constant: 0 - no constant
|
|
//--- column,1 - zero column,2 - nonzero column with value whose
|
|
//--- binary representation contains many non-zero bits. Using such
|
|
//--- type of constant column we are able to ensure than even in the
|
|
//--- presense of roundoff error functions correctly detect constant
|
|
//--- columns.
|
|
for(n=0; n<=10; n++)
|
|
{
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
}
|
|
//--- calculation
|
|
for(ctype=0; ctype<=2; ctype++)
|
|
{
|
|
for(kx=1; kx<=10; kx++)
|
|
{
|
|
for(ky=1; ky<=10; ky++)
|
|
{
|
|
//--- Fill matrices,add constant column (when CType=1 or=2)
|
|
cidxx=-1;
|
|
cidxy=-1;
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
//--- allocation
|
|
mx.Resize(n,kx);
|
|
my.Resize(n,ky);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=kx-1; j++)
|
|
mx.Set(i,j,2*CMath::RandomReal()-1);
|
|
for(j=0; j<=ky-1; j++)
|
|
my.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- check
|
|
if(ctype==1)
|
|
{
|
|
cidxx=CMath::RandomInteger(kx);
|
|
cidxy=CMath::RandomInteger(ky);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
mx.Set(i,cidxx,0.0);
|
|
my.Set(i,cidxy,0.0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(ctype==2)
|
|
{
|
|
cidxx=CMath::RandomInteger(kx);
|
|
cidxy=CMath::RandomInteger(ky);
|
|
//--- change values
|
|
v=MathSqrt((CMath::RandomInteger(kx)+1)/(double)kx);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
mx.Set(i,cidxx,v);
|
|
my.Set(i,cidxy,v);
|
|
}
|
|
}
|
|
}
|
|
//--- test covariance/correlation matrix using
|
|
//--- 2-sample functions as reference point.
|
|
//--- We also test that coefficients for constant variables
|
|
//--- are exactly zero.
|
|
CBaseStat::CovM(mx,n,kx,cc);
|
|
CBaseStat::PearsonCorrM(mx,n,kx,cp);
|
|
CBaseStat::SpearmanCorrM(mx,n,kx,cs);
|
|
for(i=0; i<=kx-1; i++)
|
|
{
|
|
for(j=0; j<=kx-1; j++)
|
|
{
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
x[i_]=mx.Get(i_,i);
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=mx.Get(i_,j);
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::Cov2(x,y,n)-cc.Get(i,j))>threshold;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp.Get(i,j))>threshold;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- check
|
|
if(ctype!=0 && n>0)
|
|
{
|
|
for(i=0; i<=kx-1; i++)
|
|
{
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||cc.Get(i,cidxx)!=0.0;
|
|
covcorrerrors=covcorrerrors||cc.Get(cidxx,i)!=0.0;
|
|
covcorrerrors=covcorrerrors||cp.Get(i,cidxx)!=0.0;
|
|
covcorrerrors=covcorrerrors||cp.Get(cidxx,i)!=0.0;
|
|
covcorrerrors=covcorrerrors||cs.Get(i,cidxx)!=0.0;
|
|
covcorrerrors=covcorrerrors||cs.Get(cidxx,i)!=0.0;
|
|
}
|
|
}
|
|
//--- test cross-covariance/cross-correlation matrix using
|
|
//--- 2-sample functions as reference point.
|
|
//--- We also test that coefficients for constant variables
|
|
//--- are exactly zero.
|
|
CBaseStat::CovM2(mx,my,n,kx,ky,cc);
|
|
CBaseStat::PearsonCorrM2(mx,my,n,kx,ky,cp);
|
|
CBaseStat::SpearmanCorrM2(mx,my,n,kx,ky,cs);
|
|
for(i=0; i<=kx-1; i++)
|
|
{
|
|
for(j=0; j<=ky-1; j++)
|
|
{
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
x[i_]=mx.Get(i_,i);
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=my.Get(i_,j);
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::Cov2(x,y,n)-cc.Get(i,j))>threshold;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp.Get(i,j))>threshold;
|
|
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- check
|
|
if(ctype!=0 && n>0)
|
|
{
|
|
for(i=0; i<=kx-1; i++)
|
|
{
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||cc.Get(i,cidxy)!=0.0;
|
|
covcorrerrors=covcorrerrors||cp.Get(i,cidxy)!=0.0;
|
|
covcorrerrors=covcorrerrors||cs.Get(i,cidxy)!=0.0;
|
|
}
|
|
for(j=0; j<=ky-1; j++)
|
|
{
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors||cc.Get(cidxx,j)!=0.0;
|
|
covcorrerrors=covcorrerrors||cp.Get(cidxx,j)!=0.0;
|
|
covcorrerrors=covcorrerrors||cs.Get(cidxx,j)!=0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Final report
|
|
waserrors=s1errors||covcorrerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("DESC.STAT TEST");
|
|
PrintResult("TOTAL RESULTS",!waserrors);
|
|
PrintResult("* 1-SAMPLE FUNCTIONALITY",!s1errors);
|
|
PrintResult("* CORRELATION/COVARIATION",!covcorrerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBdSS |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBdSSUnit
|
|
{
|
|
public:
|
|
static bool TestBdSS(const bool silent);
|
|
|
|
private:
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void Unset1DI(int &a[]);
|
|
static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBdSS |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestBdSSUnit::TestBdSS(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxnq=0;
|
|
int tiecount=0;
|
|
int c1=0;
|
|
int c0=0;
|
|
int ni=0;
|
|
int nc=0;
|
|
double pal=0;
|
|
double pbl=0;
|
|
double par=0;
|
|
double pbr=0;
|
|
double cve=0;
|
|
double cvr=0;
|
|
int info=0;
|
|
double threshold=0;
|
|
double rms=0;
|
|
double cvrms=0;
|
|
bool waserrors;
|
|
bool tieserrors;
|
|
bool split2errors;
|
|
bool optimalsplitkerrors;
|
|
bool splitkerrors;
|
|
//--- create arrays
|
|
double a[];
|
|
double a0[];
|
|
double at[];
|
|
double thresholds[];
|
|
int c[];
|
|
int p1[];
|
|
int p2[];
|
|
int ties[];
|
|
int pt1[];
|
|
int pt2[];
|
|
double tmp[];
|
|
double sortrbuf[];
|
|
double sortrbuf2[];
|
|
int sortibuf[];
|
|
int tiebuf[];
|
|
int cntbuf[];
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
//--- initialization
|
|
waserrors=false;
|
|
tieserrors=false;
|
|
split2errors=false;
|
|
splitkerrors=false;
|
|
optimalsplitkerrors=false;
|
|
maxn=100;
|
|
maxnq=49;
|
|
passcount=10;
|
|
//--- Test ties
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- untied data,test DSTie
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
Unset1DI(pt1);
|
|
Unset1DI(pt2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(at,n);
|
|
ArrayResize(tmp,n);
|
|
//--- change values
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
tmp[0]=CMath::RandomReal();
|
|
for(i=1; i<n; i++)
|
|
{
|
|
//--- A is randomly permuted
|
|
a[i]=a[i-1]+0.1*CMath::RandomReal()+0.1;
|
|
tmp[i]=CMath::RandomReal();
|
|
}
|
|
//--- function call
|
|
CTSort::TagSortFastR(tmp,a,sortrbuf,sortrbuf2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a0[i]=a[i];
|
|
at[i]=a[i];
|
|
}
|
|
//--- function call
|
|
CBdSS::DSTie(a0,n,ties,tiecount,p1,p2);
|
|
//--- function call
|
|
CTSort::TagSort(at,n,pt1,pt2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tieserrors=tieserrors||p1[i]!=pt1[i];
|
|
tieserrors=tieserrors||p2[i]!=pt2[i];
|
|
}
|
|
tieserrors=tieserrors||tiecount!=n;
|
|
//--- check
|
|
if(tiecount==n)
|
|
{
|
|
for(i=0; i<=n; i++)
|
|
tieserrors=tieserrors||ties[i]!=i;
|
|
}
|
|
//--- tied data,test DSTie
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
Unset1DI(pt1);
|
|
Unset1DI(pt2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(at,n);
|
|
//--- change values
|
|
c1=0;
|
|
c0=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=CMath::RandomInteger(2);
|
|
//--- check
|
|
if(a[i]==0.0)
|
|
c0=c0+1;
|
|
else
|
|
c1=c1+1;
|
|
a0[i]=a[i];
|
|
at[i]=a[i];
|
|
}
|
|
//--- function call
|
|
CBdSS::DSTie(a0,n,ties,tiecount,p1,p2);
|
|
//--- function call
|
|
CTSort::TagSort(at,n,pt1,pt2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tieserrors=tieserrors||p1[i]!=pt1[i];
|
|
tieserrors=tieserrors||p2[i]!=pt2[i];
|
|
}
|
|
//--- check
|
|
if(c0==0 || c1==0)
|
|
{
|
|
//--- search errors
|
|
tieserrors=tieserrors||tiecount!=1;
|
|
//--- check
|
|
if(tiecount==1)
|
|
{
|
|
tieserrors=tieserrors||ties[0]!=0;
|
|
tieserrors=tieserrors||ties[1]!=n;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- search errors
|
|
tieserrors=tieserrors||tiecount!=2;
|
|
//--- check
|
|
if(tiecount==2)
|
|
{
|
|
tieserrors=tieserrors||ties[0]!=0;
|
|
tieserrors=tieserrors||ties[1]!=c0;
|
|
tieserrors=tieserrors||ties[2]!=n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- split-2
|
|
//--- General tests for different N's
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=n;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
split2errors=split2errors||MathAbs(threshold-0.5)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(pal-1)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(pbl-0)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(par-0)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(pbr-1)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//--- Special "CREDIT"-test (transparency coefficient)
|
|
n=110;
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- initialization
|
|
a[0]=0.000;
|
|
c[0]=0;
|
|
a[1]=0.000;
|
|
c[1]=0;
|
|
a[2]=0.000;
|
|
c[2]=0;
|
|
a[3]=0.000;
|
|
c[3]=0;
|
|
a[4]=0.000;
|
|
c[4]=0;
|
|
a[5]=0.000;
|
|
c[5]=0;
|
|
a[6]=0.000;
|
|
c[6]=0;
|
|
a[7]=0.000;
|
|
c[7]=1;
|
|
a[8]=0.000;
|
|
c[8]=0;
|
|
a[9]=0.000;
|
|
c[9]=1;
|
|
a[10]=0.000;
|
|
c[10]=0;
|
|
a[11]=0.000;
|
|
c[11]=0;
|
|
a[12]=0.000;
|
|
c[12]=0;
|
|
a[13]=0.000;
|
|
c[13]=0;
|
|
a[14]=0.000;
|
|
c[14]=0;
|
|
a[15]=0.000;
|
|
c[15]=0;
|
|
a[16]=0.000;
|
|
c[16]=0;
|
|
a[17]=0.000;
|
|
c[17]=0;
|
|
a[18]=0.000;
|
|
c[18]=0;
|
|
a[19]=0.000;
|
|
c[19]=0;
|
|
a[20]=0.000;
|
|
c[20]=0;
|
|
a[21]=0.000;
|
|
c[21]=0;
|
|
a[22]=0.000;
|
|
c[22]=1;
|
|
a[23]=0.000;
|
|
c[23]=0;
|
|
a[24]=0.000;
|
|
c[24]=0;
|
|
a[25]=0.000;
|
|
c[25]=0;
|
|
a[26]=0.000;
|
|
c[26]=0;
|
|
a[27]=0.000;
|
|
c[27]=1;
|
|
a[28]=0.000;
|
|
c[28]=0;
|
|
a[29]=0.000;
|
|
c[29]=1;
|
|
a[30]=0.000;
|
|
c[30]=0;
|
|
a[31]=0.000;
|
|
c[31]=1;
|
|
a[32]=0.000;
|
|
c[32]=0;
|
|
a[33]=0.000;
|
|
c[33]=1;
|
|
a[34]=0.000;
|
|
c[34]=0;
|
|
a[35]=0.030;
|
|
c[35]=0;
|
|
a[36]=0.030;
|
|
c[36]=0;
|
|
a[37]=0.050;
|
|
c[37]=0;
|
|
a[38]=0.070;
|
|
c[38]=1;
|
|
a[39]=0.110;
|
|
c[39]=0;
|
|
a[40]=0.110;
|
|
c[40]=1;
|
|
a[41]=0.120;
|
|
c[41]=0;
|
|
a[42]=0.130;
|
|
c[42]=0;
|
|
a[43]=0.140;
|
|
c[43]=0;
|
|
a[44]=0.140;
|
|
c[44]=0;
|
|
a[45]=0.140;
|
|
c[45]=0;
|
|
a[46]=0.150;
|
|
c[46]=0;
|
|
a[47]=0.150;
|
|
c[47]=0;
|
|
a[48]=0.170;
|
|
c[48]=0;
|
|
a[49]=0.190;
|
|
c[49]=1;
|
|
a[50]=0.200;
|
|
c[50]=0;
|
|
a[51]=0.200;
|
|
c[51]=0;
|
|
a[52]=0.250;
|
|
c[52]=0;
|
|
a[53]=0.250;
|
|
c[53]=0;
|
|
a[54]=0.260;
|
|
c[54]=0;
|
|
a[55]=0.270;
|
|
c[55]=0;
|
|
a[56]=0.280;
|
|
c[56]=0;
|
|
a[57]=0.310;
|
|
c[57]=0;
|
|
a[58]=0.310;
|
|
c[58]=0;
|
|
a[59]=0.330;
|
|
c[59]=0;
|
|
a[60]=0.330;
|
|
c[60]=0;
|
|
a[61]=0.340;
|
|
c[61]=0;
|
|
a[62]=0.340;
|
|
c[62]=0;
|
|
a[63]=0.370;
|
|
c[63]=0;
|
|
a[64]=0.380;
|
|
c[64]=1;
|
|
a[65]=0.380;
|
|
c[65]=0;
|
|
a[66]=0.410;
|
|
c[66]=0;
|
|
a[67]=0.460;
|
|
c[67]=0;
|
|
a[68]=0.520;
|
|
c[68]=0;
|
|
a[69]=0.530;
|
|
c[69]=0;
|
|
a[70]=0.540;
|
|
c[70]=0;
|
|
a[71]=0.560;
|
|
c[71]=0;
|
|
a[72]=0.560;
|
|
c[72]=0;
|
|
a[73]=0.570;
|
|
c[73]=0;
|
|
a[74]=0.600;
|
|
c[74]=0;
|
|
a[75]=0.600;
|
|
c[75]=0;
|
|
a[76]=0.620;
|
|
c[76]=0;
|
|
a[77]=0.650;
|
|
c[77]=0;
|
|
a[78]=0.660;
|
|
c[78]=0;
|
|
a[79]=0.680;
|
|
c[79]=0;
|
|
a[80]=0.700;
|
|
c[80]=0;
|
|
a[81]=0.750;
|
|
c[81]=0;
|
|
a[82]=0.770;
|
|
c[82]=0;
|
|
a[83]=0.770;
|
|
c[83]=0;
|
|
a[84]=0.770;
|
|
c[84]=0;
|
|
a[85]=0.790;
|
|
c[85]=0;
|
|
a[86]=0.810;
|
|
c[86]=0;
|
|
a[87]=0.840;
|
|
c[87]=0;
|
|
a[88]=0.860;
|
|
c[88]=0;
|
|
a[89]=0.870;
|
|
c[89]=0;
|
|
a[90]=0.890;
|
|
c[90]=0;
|
|
a[91]=0.900;
|
|
c[91]=1;
|
|
a[92]=0.900;
|
|
c[92]=0;
|
|
a[93]=0.910;
|
|
c[93]=0;
|
|
a[94]=0.940;
|
|
c[94]=0;
|
|
a[95]=0.950;
|
|
c[95]=0;
|
|
a[96]=0.952;
|
|
c[96]=0;
|
|
a[97]=0.970;
|
|
c[97]=0;
|
|
a[98]=0.970;
|
|
c[98]=0;
|
|
a[99]=0.980;
|
|
c[99]=0;
|
|
a[100]=1.000;
|
|
c[100]=0;
|
|
a[101]=1.000;
|
|
c[101]=0;
|
|
a[102]=1.000;
|
|
c[102]=0;
|
|
a[103]=1.000;
|
|
c[103]=0;
|
|
a[104]=1.000;
|
|
c[104]=0;
|
|
a[105]=1.020;
|
|
c[105]=0;
|
|
a[106]=1.090;
|
|
c[106]=0;
|
|
a[107]=1.130;
|
|
c[107]=0;
|
|
a[108]=1.840;
|
|
c[108]=0;
|
|
a[109]=2.470;
|
|
c[109]=0;
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
split2errors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
split2errors=split2errors||MathAbs(threshold-0.195)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(pal-0.80)>0.02;
|
|
split2errors=split2errors||MathAbs(pbl-0.20)>0.02;
|
|
split2errors=split2errors||MathAbs(par-0.97)>0.02;
|
|
split2errors=split2errors||MathAbs(pbr-0.03)>0.02;
|
|
}
|
|
//--- split-2 fast
|
|
//--- General tests for different N's
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
ArrayResize(tiebuf,n+1);
|
|
ArrayResize(cntbuf,4);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=n;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
split2errors=split2errors||MathAbs(threshold-0.5)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(rms-0)>100*CMath::m_machineepsilon;
|
|
//--- check
|
|
if(n==2)
|
|
split2errors=split2errors||MathAbs(cvrms-0.5)>100*CMath::m_machineepsilon;
|
|
else
|
|
{
|
|
//--- check
|
|
if(n==3)
|
|
split2errors=split2errors||MathAbs(cvrms-MathSqrt((2*0+2*0+2*0.25)/6))>100*CMath::m_machineepsilon;
|
|
else
|
|
split2errors=split2errors||MathAbs(cvrms)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//--- special tests
|
|
n=10;
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
ArrayResize(tiebuf,n+1);
|
|
ArrayResize(cntbuf,2*3);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=i;
|
|
//--- check
|
|
if(i<=n-3)
|
|
c[i]=0;
|
|
else
|
|
c[i]=i-(n-3);
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,3,0.00,info,threshold,rms,cvrms);
|
|
//--- check
|
|
if(info!=1)
|
|
split2errors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
split2errors=split2errors||MathAbs(threshold-(n-2.5))>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(rms-MathSqrt((0.25+0.25+0.25+0.25)/(3*n)))>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors||MathAbs(cvrms-MathSqrt((double)(1+1+1+1)/(double)(3*n)))>100*CMath::m_machineepsilon;
|
|
}
|
|
//--- Optimal split-K
|
|
//--- General tests for different N's
|
|
for(n=1; n<=maxnq; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=n;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
c0=0;
|
|
c1=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
//--- check
|
|
if(c[i]==0)
|
|
c0=c0+1;
|
|
//--- check
|
|
if(c[i]==1)
|
|
c1=c1+1;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
optimalsplitkerrors=optimalsplitkerrors||ni!=2;
|
|
optimalsplitkerrors=optimalsplitkerrors||MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
optimalsplitkerrors=optimalsplitkerrors||MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
//--- test #2
|
|
if(n>2)
|
|
{
|
|
c0=1+CMath::RandomInteger(n-1);
|
|
c1=n-c0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i<c0)
|
|
{
|
|
a[i]=0;
|
|
c[i]=0;
|
|
}
|
|
else
|
|
{
|
|
a[i]=1;
|
|
c[i]=1;
|
|
}
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
optimalsplitkerrors=optimalsplitkerrors||ni!=2;
|
|
optimalsplitkerrors=optimalsplitkerrors||MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
optimalsplitkerrors=optimalsplitkerrors||MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
//--- multi-tie test
|
|
if(n>=16)
|
|
{
|
|
//--- Multi-tie test.
|
|
//--- First NC-1 ties have C0 entries,remaining NC-th tie
|
|
//--- have C1 entries.
|
|
nc=(int)MathRound(MathSqrt(n));
|
|
c0=n/nc;
|
|
c1=n-c0*(nc-1);
|
|
for(i=0; i<=nc-2; i++)
|
|
{
|
|
for(j=c0*i; j<=c0*(i+1)-1; j++)
|
|
{
|
|
a[j]=j;
|
|
c[j]=i;
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=c0*(nc-1); j<n; j++)
|
|
{
|
|
a[j]=j;
|
|
c[j]=nc-1;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
optimalsplitkerrors=optimalsplitkerrors||ni!=nc;
|
|
//--- check
|
|
if(ni==nc)
|
|
{
|
|
for(i=0; i<=nc-2; i++)
|
|
optimalsplitkerrors=optimalsplitkerrors||MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon;
|
|
cvr=-((nc-1)*c0*MathLog((double)c0/(double)(c0+nc-1))+c1*MathLog((double)c1/(double)(c1+nc-1)));
|
|
optimalsplitkerrors=optimalsplitkerrors||MathAbs(cve-cvr)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//--- Non-optimal split-K
|
|
//--- General tests for different N's
|
|
for(n=1; n<=maxnq; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=pass;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
c0=0;
|
|
c1=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
//--- check
|
|
if(c[i]==0)
|
|
c0=c0+1;
|
|
//--- check
|
|
if(c[i]==1)
|
|
c1=c1+1;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
splitkerrors=splitkerrors||ni!=2;
|
|
//--- check
|
|
if(ni==2)
|
|
{
|
|
splitkerrors=splitkerrors||MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
splitkerrors=splitkerrors||MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//--- test #2
|
|
if(n>2)
|
|
{
|
|
c0=1+CMath::RandomInteger(n-1);
|
|
c1=n-c0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i<c0)
|
|
{
|
|
a[i]=0;
|
|
c[i]=0;
|
|
}
|
|
else
|
|
{
|
|
a[i]=1;
|
|
c[i]=1;
|
|
}
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
splitkerrors=splitkerrors||ni!=2;
|
|
//--- check
|
|
if(ni==2)
|
|
{
|
|
splitkerrors=splitkerrors||MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
splitkerrors=splitkerrors||MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//--- multi-tie test
|
|
for(c0=4; c0<=n; c0++)
|
|
{
|
|
//--- check
|
|
if((n%c0==0 && n/c0<=c0) && n/c0>1)
|
|
{
|
|
nc=n/c0;
|
|
for(i=0; i<nc; i++)
|
|
{
|
|
for(j=c0*i; j<=c0*(i+1)-1; j++)
|
|
{
|
|
a[j]=j;
|
|
c[j]=i;
|
|
}
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
splitkerrors=splitkerrors||ni!=nc;
|
|
//--- check
|
|
if(ni==nc)
|
|
{
|
|
for(i=0; i<=nc-2; i++)
|
|
{
|
|
splitkerrors=splitkerrors||MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon;
|
|
}
|
|
cvr=-(nc*c0*MathLog((double)c0/(double)(c0+nc-1)));
|
|
splitkerrors=splitkerrors||MathAbs(cve-cvr)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((tieserrors||split2errors)||optimalsplitkerrors)||splitkerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BASIC DATASET SUBROUTINES");
|
|
PrintResult("TIES",!tieserrors);
|
|
PrintResult("SPLIT-2",!split2errors);
|
|
PrintResult("OPTIMAL SPLIT-K",!optimalsplitkerrors);
|
|
PrintResult("SPLIT-K",!splitkerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSSUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSSUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSSUnit::Unset1DI(int &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=CMath::RandomInteger(3)-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSSUnit::TestSortResults(double &asorted[],int &p1[],
|
|
int &p2[],double &aoriginal[],
|
|
const int n,bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double a2[];
|
|
int f[];
|
|
//--- allocation
|
|
ArrayResize(a2,n);
|
|
ArrayResize(f,n);
|
|
//--- is set ordered?
|
|
for(i=0; i<=n-2; i++)
|
|
waserrors=waserrors||asorted[i]>asorted[i+1];
|
|
//--- P1 correctness
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||asorted[i]!=aoriginal[p1[i]];
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
f[i]=0;
|
|
for(i=0; i<n; i++)
|
|
f[p1[i]]=f[p1[i]]+1;
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||f[i]!=1;
|
|
//--- P2 correctness
|
|
for(i=0; i<n; i++)
|
|
a2[i]=aoriginal[i];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(p2[i]!=i)
|
|
{
|
|
t=a2[i];
|
|
a2[i]=a2[p2[i]];
|
|
a2[p2[i]]=t;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
waserrors=waserrors||asorted[i]!=a2[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CDForest |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDForestUnit
|
|
{
|
|
public:
|
|
static bool TestDForest(const bool silent);
|
|
|
|
private:
|
|
static void TestProcessing(bool &err);
|
|
static void BasicTest1(const int nvars,const int nclasses,const int passcount,bool &err);
|
|
static void BasicTest2(bool &err);
|
|
static void BasicTest3(bool &err);
|
|
static void BasicTest4(bool &err);
|
|
static void BasicTest5(bool &err);
|
|
static double RNormal(void);
|
|
static void RSphere(CMatrixDouble &xy,const int n,const int i);
|
|
static void UnsetDF(CDecisionForest &df);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CDForest |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDForestUnit::TestDForest(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int ncmax=0;
|
|
int nvmax=0;
|
|
int passcount=0;
|
|
int nvars=0;
|
|
int nclasses=0;
|
|
bool waserrors;
|
|
bool basicerrors;
|
|
bool procerrors;
|
|
//--- Primary settings
|
|
nvmax=4;
|
|
ncmax=3;
|
|
passcount=10;
|
|
basicerrors=false;
|
|
procerrors=false;
|
|
waserrors=false;
|
|
//--- Tests
|
|
TestProcessing(procerrors);
|
|
for(nvars=1; nvars<=nvmax; nvars++)
|
|
{
|
|
for(nclasses=1; nclasses<=ncmax; nclasses++)
|
|
BasicTest1(nvars,nclasses,passcount,basicerrors);
|
|
}
|
|
//--- function calls
|
|
BasicTest2(basicerrors);
|
|
BasicTest3(basicerrors);
|
|
BasicTest4(basicerrors);
|
|
BasicTest5(basicerrors);
|
|
//--- Final report
|
|
waserrors=basicerrors||procerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("RANDOM FOREST TEST");
|
|
PrintResult("TOTAL RESULTS",!waserrors);
|
|
PrintResult("* PROCESSING FUNCTIONS",!procerrors);
|
|
PrintResult("* BASIC TESTS",!basicerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::TestProcessing(bool &err)
|
|
{
|
|
//--- create variables
|
|
int nvars=0;
|
|
int nclasses=0;
|
|
int nsample=0;
|
|
int ntrees=0;
|
|
int nfeatures=0;
|
|
int flags=0;
|
|
int npoints=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool allsame;
|
|
int info=0;
|
|
double v=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- objects of classes
|
|
CDecisionForest df1;
|
|
CDecisionForest df2;
|
|
CDFReport rep;
|
|
//--- initialization
|
|
passcount=100;
|
|
//--- Main cycle
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- initialize parameters
|
|
nvars=1+CMath::RandomInteger(5);
|
|
nclasses=1+CMath::RandomInteger(3);
|
|
ntrees=1+CMath::RandomInteger(4);
|
|
nfeatures=1+CMath::RandomInteger(nvars);
|
|
flags=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
flags=flags+2;
|
|
//--- Initialize arrays and data
|
|
npoints=10+CMath::RandomInteger(50);
|
|
nsample=(int)(MathMax(10,CMath::RandomInteger(npoints)));
|
|
//--- allocation
|
|
ArrayResize(x1,nvars);
|
|
ArrayResize(x2,nvars);
|
|
ArrayResize(y1,nclasses);
|
|
ArrayResize(y2,nclasses);
|
|
xy.Resize(npoints,nvars+1);
|
|
//--- change values
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
{
|
|
//--- check
|
|
if(j%2==0)
|
|
xy.Set(i,j,2*CMath::RandomReal()-1);
|
|
else
|
|
xy.Set(i,j,CMath::RandomInteger(2));
|
|
}
|
|
//--- check
|
|
if(nclasses==1)
|
|
xy.Set(i,nvars,2*CMath::RandomReal()-1);
|
|
else
|
|
xy.Set(i,nvars,CMath::RandomInteger(nclasses));
|
|
}
|
|
//--- create forest
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,ntrees,nsample,nfeatures,flags,info,df1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- Same inputs leads to same outputs
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0; i<nclasses; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x2,y2);
|
|
allsame=true;
|
|
//--- search errors
|
|
for(i=0; i<nclasses; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Same inputs on original forest leads to same outputs
|
|
//--- on copy created using DFCopy
|
|
UnsetDF(df2);
|
|
//--- function call
|
|
CDForest::DFCopy(df1,df2);
|
|
for(i=0; i<nvars; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0; i<nclasses; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
//--- function call
|
|
CDForest::DFProcess(df2,x2,y2);
|
|
allsame=true;
|
|
//--- search errors
|
|
for(i=0; i<nclasses; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Same inputs on original forest leads to same outputs
|
|
//--- on copy created using DFSerialize
|
|
UnsetDF(df2);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CDForest::DFAlloc(_local_serializer,df1);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CDForest::DFSerialize(_local_serializer,df1);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CDForest::DFUnserialize(_local_serializer,df2);
|
|
_local_serializer.Stop();
|
|
}
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0; i<nclasses; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
//--- function call
|
|
CDForest::DFProcess(df2,x2,y2);
|
|
allsame=true;
|
|
//--- search errors
|
|
for(i=0; i<nclasses; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Normalization properties
|
|
if(nclasses>1)
|
|
{
|
|
for(i=0; i<nvars ; i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
v=0;
|
|
//--- search errors
|
|
for(i=0; i<nclasses; i++)
|
|
{
|
|
v+=y1[i];
|
|
err=err||y1[i]<0.0;
|
|
}
|
|
err=err||MathAbs(v-1)>1000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: one-tree forest built using full sample must |
|
|
//| remember all the training cases |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::BasicTest1(const int nvars,const int nclasses,
|
|
const int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int npoints=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double s=0;
|
|
int info=0;
|
|
bool hassame;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- check
|
|
if(nclasses==1)
|
|
{
|
|
//--- only classification tasks
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- select number of points
|
|
if(pass<=3 && passcount>3)
|
|
npoints=pass;
|
|
else
|
|
npoints=100+CMath::RandomInteger(100);
|
|
//--- Prepare task
|
|
xy.Resize(npoints,nvars+1);
|
|
ArrayResize(x,nvars);
|
|
ArrayResize(y,nclasses);
|
|
//--- change values
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
xy.Set(i,j,2*CMath::RandomReal()-1);
|
|
xy.Set(i,nvars,CMath::RandomInteger(nclasses));
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,1,npoints,1,1,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
x[i_]=xy.Get(i,i_);
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
s=0;
|
|
for(j=0; j<nclasses; j++)
|
|
{
|
|
//--- check
|
|
if(y[j]<0.0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
s=s+y[j];
|
|
}
|
|
//--- check
|
|
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(MathAbs(y[(int)MathRound(xy[i][nvars])]-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
//--- not an error if there exists such K,J that XY.Set(k,j,XY[I,J]
|
|
//--- (may be we just can't distinguish two tied values).
|
|
//--- definitely error otherwise.
|
|
hassame=false;
|
|
for(k=0; k<npoints ; k++)
|
|
{
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
{
|
|
//--- check
|
|
if(xy[k][j]==xy.Get(i,j))
|
|
hassame=true;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(!hassame)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: tests generalization ability on a simple noisy |
|
|
//| classification task: |
|
|
//| * 0<x<1 - P(class=0)=1 |
|
|
//| * 1<x<2 - P(class=0)=2-x |
|
|
//| * 2<x<3 - P(class=0)=0 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::BasicTest2(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int npoints=0;
|
|
int ntrees=0;
|
|
int i=0;
|
|
int j=0;
|
|
double s=0;
|
|
int info=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- initialization
|
|
passcount=1;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- select npoints and ntrees
|
|
npoints=3000;
|
|
ntrees=50;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,2);
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,2);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
xy.Set(i,0,3*CMath::RandomReal());
|
|
//--- check
|
|
if(xy[i][0]<=1.0)
|
|
xy.Set(i,1,0);
|
|
else
|
|
{
|
|
//--- check
|
|
if(xy[i][0]<=2.0)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()<xy[i][0]-1)
|
|
xy.Set(i,1,1);
|
|
else
|
|
xy.Set(i,1,0);
|
|
}
|
|
else
|
|
xy.Set(i,1,1);
|
|
}
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,1,2,ntrees,(int)MathRound(0.05*npoints),1,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
x[0]=0.0;
|
|
//--- cycle
|
|
while(x[0]<=3.0)
|
|
{
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- Test for basic properties
|
|
s=0;
|
|
for(j=0; j<=1; j++)
|
|
{
|
|
//--- check
|
|
if(y[j]<0.0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
s=s+y[j];
|
|
}
|
|
//--- check
|
|
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- test for good correlation with results
|
|
if(x[0]<1.0)
|
|
err=err||y[0]<0.8;
|
|
//--- check
|
|
if(x[0]>=1.0 && x[0]<=2.0)
|
|
err=err||MathAbs(y[1]-(x[0]-1))>0.5;
|
|
//--- check
|
|
if(x[0]>2.0)
|
|
err=err||y[1]<0.8;
|
|
x[0]=x[0]+0.01;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: tests generalization ability on a simple |
|
|
//| classification task (no noise): |
|
|
//| * ||x||<1,||y||<1 |
|
|
//| * x^2+y^2<=0.25 - P(class=0)=1 |
|
|
//| * x^2+y^2>0.25 - P(class=0)=0 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::BasicTest3(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int npoints=0;
|
|
int ntrees=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double s=0;
|
|
int info=0;
|
|
int testgridsize=0;
|
|
double r=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- initialization
|
|
passcount=1;
|
|
testgridsize=50;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- select npoints and ntrees
|
|
npoints=2000;
|
|
ntrees=100;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,3);
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
//--- change values
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
xy.Set(i,0,2*CMath::RandomReal()-1);
|
|
xy.Set(i,1,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(CMath::Sqr(xy[i][0])+CMath::Sqr(xy[i][1])<=0.25)
|
|
xy.Set(i,2,0);
|
|
else
|
|
xy.Set(i,2,1);
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,2,2,ntrees,(int)MathRound(0.1*npoints),1,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(i=-(testgridsize/2); i<=testgridsize/2; i++)
|
|
{
|
|
for(j=-(testgridsize/2); j<=testgridsize/2; j++)
|
|
{
|
|
x[0]=(double)i/(double)(testgridsize/2);
|
|
x[1]=(double)j/(double)(testgridsize/2);
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- Test for basic properties
|
|
s=0;
|
|
for(k=0; k<=1; k++)
|
|
{
|
|
//--- check
|
|
if(y[k]<0.0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
s=s+y[k];
|
|
}
|
|
//--- check
|
|
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- test for good correlation with results
|
|
r=MathSqrt(CMath::Sqr(x[0])+CMath::Sqr(x[1]));
|
|
//--- check
|
|
if(r<0.5*0.5)
|
|
err=err||y[0]<0.6;
|
|
//--- check
|
|
if(r>0.5*1.5)
|
|
err=err||y[1]<0.6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: simple regression task without noise: |
|
|
//| * ||x||<1,||y||<1 |
|
|
//| * F(x,y)=x^2+y |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::BasicTest4(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int npoints=0;
|
|
int ntrees=0;
|
|
int ns=0;
|
|
int strongc=0;
|
|
int i=0;
|
|
int j=0;
|
|
int info=0;
|
|
int testgridsize=0;
|
|
double maxerr=0;
|
|
double maxerr2=0;
|
|
double avgerr=0;
|
|
double avgerr2=0;
|
|
int cnt=0;
|
|
double ey=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDecisionForest df2;
|
|
CDFReport rep;
|
|
CDFReport rep2;
|
|
//--- initialization
|
|
passcount=1;
|
|
testgridsize=50;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- select npoints and ntrees
|
|
npoints=5000;
|
|
ntrees=100;
|
|
ns=(int)MathRound(0.1*npoints);
|
|
strongc=1;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,3);
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,1);
|
|
//--- change values
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
xy.Set(i,0,2*CMath::RandomReal()-1);
|
|
xy.Set(i,1,2*CMath::RandomReal()-1);
|
|
xy.Set(i,2,CMath::Sqr(xy[i][0])+xy[i][1]);
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,strongc,info,df2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change values
|
|
maxerr=0;
|
|
maxerr2=0;
|
|
avgerr=0;
|
|
avgerr2=0;
|
|
cnt=0;
|
|
//--- calculation
|
|
for(i=(int)MathRound(-(0.7*testgridsize/2)); i<=(int)MathRound(0.7*testgridsize/2); i++)
|
|
{
|
|
for(j=(int)MathRound(-(0.7*testgridsize/2)); j<=(int)MathRound(0.7*testgridsize/2); j++)
|
|
{
|
|
x[0]=(double)i/(double)(testgridsize/2);
|
|
x[1]=(double)j/(double)(testgridsize/2);
|
|
ey=CMath::Sqr(x[0])+x[1];
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
maxerr=MathMax(maxerr,MathAbs(y[0]-ey));
|
|
avgerr=avgerr+MathAbs(y[0]-ey);
|
|
//--- function call
|
|
CDForest::DFProcess(df2,x,y);
|
|
maxerr2=MathMax(maxerr2,MathAbs(y[0]-ey));
|
|
avgerr2=avgerr2+MathAbs(y[0]-ey);
|
|
cnt=cnt+1;
|
|
}
|
|
}
|
|
//--- search errors
|
|
avgerr=avgerr/cnt;
|
|
avgerr2=avgerr2/cnt;
|
|
err=err||maxerr>0.2;
|
|
err=err||maxerr2>0.2;
|
|
err=err||avgerr>0.1;
|
|
err=err||avgerr2>0.1;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: extended variable selection leads to better results. |
|
|
//| Next task CAN be solved without EVS but it is very unlikely. |
|
|
//| With EVS it can be easily and exactly solved. |
|
|
//| Task matrix: |
|
|
//| 1 0 0 0 ... 0 0 |
|
|
//| 0 1 0 0 ... 0 1 |
|
|
//| 0 0 1 0 ... 0 2 |
|
|
//| 0 0 0 1 ... 0 3 |
|
|
//| 0 0 0 0 ... 1 N-1 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::BasicTest5(bool &err)
|
|
{
|
|
//--- create variables
|
|
int nvars=0;
|
|
int npoints=0;
|
|
int nfeatures=0;
|
|
int nsample=0;
|
|
int ntrees=0;
|
|
int evs=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool eflag;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- select npoints and ntrees
|
|
npoints=50;
|
|
nvars=npoints;
|
|
ntrees=1;
|
|
nsample=npoints;
|
|
evs=2;
|
|
nfeatures=1;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,nvars+1);
|
|
ArrayResize(x,nvars);
|
|
ArrayResize(y,1);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
{
|
|
xy.Set(i,j,0);
|
|
}
|
|
xy.Set(i,i,1);
|
|
xy.Set(i,nvars,i);
|
|
}
|
|
//--- Without EVS
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
eflag=false;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
x[i_]=xy.Get(i,i_);
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- check
|
|
if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon)
|
|
eflag=true;
|
|
}
|
|
//--- check
|
|
if(eflag)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- With EVS
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,evs,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
eflag=false;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
{
|
|
x[i_]=xy.Get(i,i_);
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- check
|
|
if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon)
|
|
{
|
|
eflag=true;
|
|
}
|
|
}
|
|
//--- check
|
|
if(eflag)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal number |
|
|
//+------------------------------------------------------------------+
|
|
double CTestDForestUnit::RNormal(void)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double s=0;
|
|
double x1=0;
|
|
double x2=0;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
u=2*CMath::RandomReal()-1;
|
|
v=2*CMath::RandomReal()-1;
|
|
s=CMath::Sqr(u)+CMath::Sqr(v);
|
|
//--- check
|
|
if(s>0.0 && s<1.0)
|
|
{
|
|
s=MathSqrt(-(2*MathLog(s)/s));
|
|
x1=u*s;
|
|
x2=v*s;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(x1!=0)
|
|
result=x1;
|
|
else
|
|
result=x2;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random point from sphere |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::RSphere(CMatrixDouble &xy,const int n,
|
|
const int i)
|
|
{
|
|
//--- create variables
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
xy.Set(i,j,RNormal());
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=xy.Get(i,i_)*xy.Get(i,i_);
|
|
//--- calculation
|
|
v=CMath::RandomReal()/MathSqrt(v);
|
|
for(i_=0; i_<n; i_++)
|
|
xy.Set(i,i_,v*xy.Get(i,i_));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets DF |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDForestUnit::UnsetDF(CDecisionForest &df)
|
|
{
|
|
//--- create a variable
|
|
int info=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CDFReport rep;
|
|
//--- allocation
|
|
xy.Resize(1,2);
|
|
//--- change values
|
|
xy.Set(0,0,0);
|
|
xy.Set(0,1,0);
|
|
//--- function call
|
|
CDForest::DFBuildInternal(xy,1,1,1,1,1,1,0,info,df,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBlas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBlasUnit
|
|
{
|
|
public:
|
|
static bool TestBlas(const bool silent);
|
|
|
|
private:
|
|
static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBlas |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestBlasUnit::TestBlas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int i=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int l=0;
|
|
int k=0;
|
|
int r=0;
|
|
int i3=0;
|
|
int j3=0;
|
|
int col1=0;
|
|
int col2=0;
|
|
int row1=0;
|
|
int row2=0;
|
|
double err=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double e3=0;
|
|
double v=0;
|
|
double scl1=0;
|
|
double scl2=0;
|
|
double scl3=0;
|
|
bool was1;
|
|
bool was2;
|
|
bool trans1;
|
|
bool trans2;
|
|
double threshold=0;
|
|
bool n2errors;
|
|
bool hsnerrors;
|
|
bool amaxerrors;
|
|
bool mverrors;
|
|
bool iterrors;
|
|
bool cterrors;
|
|
bool mmerrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble c1;
|
|
CMatrixDouble c2;
|
|
//--- initialization
|
|
n2errors=false;
|
|
amaxerrors=false;
|
|
hsnerrors=false;
|
|
mverrors=false;
|
|
iterrors=false;
|
|
cterrors=false;
|
|
mmerrors=false;
|
|
waserrors=false;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
//--- Test Norm2
|
|
passcount=1000;
|
|
e1=0;
|
|
e2=0;
|
|
e3=0;
|
|
scl2=0.5*CMath::m_maxrealnumber;
|
|
scl3=2*CMath::m_minrealnumber;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
n=1+CMath::RandomInteger(1000);
|
|
i1=CMath::RandomInteger(10);
|
|
i2=n+i1-1;
|
|
//--- allocation
|
|
ArrayResize(x1,i2+1);
|
|
ArrayResize(x2,i2+1);
|
|
//--- change values
|
|
for(i=i1; i<=i2; i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
for(i=i1; i<=i2; i++)
|
|
v+=CMath::Sqr(x1[i]);
|
|
v=MathSqrt(v);
|
|
//--- calculation
|
|
e1=MathMax(e1,MathAbs(v-CBlas::VectorNorm2(x1,i1,i2)));
|
|
for(i=i1; i<=i2; i++)
|
|
x2[i]=scl2*x1[i];
|
|
//--- calculation
|
|
e2=MathMax(e2,MathAbs(v*scl2-CBlas::VectorNorm2(x2,i1,i2)));
|
|
for(i=i1; i<=i2; i++)
|
|
x2[i]=scl3*x1[i];
|
|
//--- calculation
|
|
e3=MathMax(e3,MathAbs(v*scl3-CBlas::VectorNorm2(x2,i1,i2)));
|
|
}
|
|
e2=e2/scl2;
|
|
e3=e3/scl3;
|
|
//--- search errors
|
|
n2errors=(e1>=threshold || e2>=threshold) || e3>=threshold;
|
|
//--- Testing VectorAbsMax,Column/Row AbsMax
|
|
ArrayResize(x1,6);
|
|
x1[1]=2.0;
|
|
x1[2]=0.2;
|
|
x1[3]=-1.3;
|
|
x1[4]=0.7;
|
|
x1[5]=-3.0;
|
|
//--- search errors
|
|
amaxerrors=(CBlas::VectorIdxAbsMax(x1,1,5)!=5 || CBlas::VectorIdxAbsMax(x1,1,4)!=1) || CBlas::VectorIdxAbsMax(x1,2,4)!=3;
|
|
n=30;
|
|
//--- allocation
|
|
ArrayResize(x1,n+1);
|
|
a.Resize(n+1,n+1);
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- calculation
|
|
was1=false;
|
|
was2=false;
|
|
for(pass=1; pass<=1000; pass++)
|
|
{
|
|
//--- change values
|
|
j=1+CMath::RandomInteger(n);
|
|
i1=1+CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n+1-i1);
|
|
for(i_=i1; i_<=i2; i_++)
|
|
x1[i_]=a.Get(i_,j);
|
|
//--- check
|
|
if(CBlas::VectorIdxAbsMax(x1,i1,i2)!=CBlas::ColumnIdxAbsMax(a,i1,i2,j))
|
|
was1=true;
|
|
//--- change values
|
|
i=1+CMath::RandomInteger(n);
|
|
j1=1+CMath::RandomInteger(n);
|
|
j2=j1+CMath::RandomInteger(n+1-j1);
|
|
for(i_=j1; i_<=j2; i_++)
|
|
x1[i_]=a.Get(i,i_);
|
|
//--- check
|
|
if(CBlas::VectorIdxAbsMax(x1,j1,j2)!=CBlas::RowIdxAbsMax(a,j1,j2,i))
|
|
was2=true;
|
|
}
|
|
//--- search errors
|
|
amaxerrors=(amaxerrors||was1)||was2;
|
|
//--- Testing upper Hessenberg 1-norm
|
|
a.Resize(4,4);
|
|
ArrayResize(x1,4);
|
|
a.Set(1,1,2);
|
|
a.Set(1,2,3);
|
|
a.Set(1,3,1);
|
|
a.Set(2,1,4);
|
|
a.Set(2,2,-5);
|
|
a.Set(2,3,8);
|
|
a.Set(3,1,99);
|
|
a.Set(3,2,3);
|
|
a.Set(3,3,1);
|
|
//--- search errors
|
|
hsnerrors=MathAbs(CBlas::UpperHessenberg1Norm(a,1,3,1,3,x1)-11)>threshold;
|
|
//--- Testing MatrixVectorMultiply
|
|
a.Resize(4,6);
|
|
ArrayResize(x1,4);
|
|
ArrayResize(x2,3);
|
|
a.Set(2,3,2);
|
|
a.Set(2,4,-1);
|
|
a.Set(2,5,-1);
|
|
a.Set(3,3,1);
|
|
a.Set(3,4,-2);
|
|
a.Set(3,5,2);
|
|
x1[1]=1;
|
|
x1[2]=2;
|
|
x1[3]=1;
|
|
x2[1]=-1;
|
|
x2[2]=-1;
|
|
//--- function calls
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,1.0);
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,1.0);
|
|
//--- calculation
|
|
e1=MathAbs(x1[1]+5)+MathAbs(x1[2]-8)+MathAbs(x1[3]+1)+MathAbs(x2[1]+2)+MathAbs(x2[2]+2);
|
|
x1[1]=1;
|
|
x1[2]=2;
|
|
x1[3]=1;
|
|
x2[1]=-1;
|
|
x2[2]=-1;
|
|
//--- function calls
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,0.0);
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,0.0);
|
|
//--- calculation
|
|
e2=MathAbs(x1[1]+3)+MathAbs(x1[2]-3)+MathAbs(x1[3]+1)+MathAbs(x2[1]+1)+MathAbs(x2[2]+1);
|
|
mverrors=e1+e2>=threshold;
|
|
//--- testing inplace transpose
|
|
n=10;
|
|
a.Resize(n+1,n+1);
|
|
b.Resize(n+1,n+1);
|
|
ArrayResize(x1,n);
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
a.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
passcount=10000;
|
|
was1=false;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- change values
|
|
i1=1+CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1+1);
|
|
j1=1+CMath::RandomInteger(n-(i2-i1));
|
|
j2=j1+(i2-i1);
|
|
//--- function calls
|
|
CBlas::CopyMatrix(a,i1,i2,j1,j2,b,i1,i2,j1,j2);
|
|
CBlas::InplaceTranspose(b,i1,i2,j1,j2,x1);
|
|
for(i=i1; i<=i2; i++)
|
|
{
|
|
for(j=j1; j<=j2; j++)
|
|
{
|
|
//--- check
|
|
if(a.Get(i,j)!=b[i1+(j-j1)][j1+(i-i1)])
|
|
was1=true;
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
iterrors=was1;
|
|
//--- testing copy and transpose
|
|
n=10;
|
|
a.Resize(n+1,n+1);
|
|
b.Resize(n+1,n+1);
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
a.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
passcount=10000;
|
|
was1=false;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- change values
|
|
i1=1+CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1+1);
|
|
j1=1+CMath::RandomInteger(n);
|
|
j2=j1+CMath::RandomInteger(n-j1+1);
|
|
//--- function call
|
|
CBlas::CopyAndTranspose(a,i1,i2,j1,j2,b,j1,j2,i1,i2);
|
|
for(i=i1; i<=i2; i++)
|
|
{
|
|
for(j=j1; j<=j2; j++)
|
|
{
|
|
//--- check
|
|
if(a.Get(i,j)!=b[j][i])
|
|
was1=true;
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
cterrors=was1;
|
|
//--- Testing MatrixMatrixMultiply
|
|
n=10;
|
|
a.Resize(2*n+1,2*n+1);
|
|
b.Resize(2*n+1,2*n+1);
|
|
c1.Resize(2*n+1,2*n+1);
|
|
c2.Resize(2*n+1,2*n+1);
|
|
ArrayResize(x1,n+1);
|
|
ArrayResize(x2,n+1);
|
|
for(i=1; i<=2*n; i++)
|
|
{
|
|
for(j=1; j<=2*n; j++)
|
|
{
|
|
a.Set(i,j,CMath::RandomReal());
|
|
b.Set(i,j,CMath::RandomReal());
|
|
}
|
|
}
|
|
//--- calculation
|
|
passcount=1000;
|
|
was1=false;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(i=1; i<=2*n; i++)
|
|
{
|
|
for(j=1; j<=2*n; j++)
|
|
{
|
|
c1.Set(i,j,2.1*i+3.1*j);
|
|
c2.Set(i,j,c1.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
l=1+CMath::RandomInteger(n);
|
|
k=1+CMath::RandomInteger(n);
|
|
r=1+CMath::RandomInteger(n);
|
|
i1=1+CMath::RandomInteger(n);
|
|
j1=1+CMath::RandomInteger(n);
|
|
i2=1+CMath::RandomInteger(n);
|
|
j2=1+CMath::RandomInteger(n);
|
|
i3=1+CMath::RandomInteger(n);
|
|
j3=1+CMath::RandomInteger(n);
|
|
trans1=CMath::RandomReal()>0.5;
|
|
trans2=CMath::RandomReal()>0.5;
|
|
//--- check
|
|
if(trans1)
|
|
{
|
|
col1=l;
|
|
row1=k;
|
|
}
|
|
else
|
|
{
|
|
col1=k;
|
|
row1=l;
|
|
}
|
|
//--- check
|
|
if(trans2)
|
|
{
|
|
col2=k;
|
|
row2=r;
|
|
}
|
|
else
|
|
{
|
|
col2=r;
|
|
row2=k;
|
|
}
|
|
//--- change values
|
|
scl1=CMath::RandomReal();
|
|
scl2=CMath::RandomReal();
|
|
//--- function calls
|
|
CBlas::MatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c1,i3,i3+l-1,j3,j3+r-1,scl2,x1);
|
|
NaiveMatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c2,i3,i3+l-1,j3,j3+r-1,scl2);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=1; i<=l; i++)
|
|
{
|
|
for(j=1; j<=r; j++)
|
|
err=MathMax(err,MathAbs(c1[i3+i-1][j3+j-1]-c2[i3+i-1][j3+j-1]));
|
|
}
|
|
//--- check
|
|
if(err>threshold)
|
|
{
|
|
was1=true;
|
|
break;
|
|
}
|
|
}
|
|
//--- change value
|
|
mmerrors=was1;
|
|
//--- report
|
|
waserrors=(((((n2errors||amaxerrors)||hsnerrors)||mverrors)||iterrors)||cterrors)||mmerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BLAS");
|
|
PrintResult("VectorNorm2",!n2errors);
|
|
PrintResult("AbsMax (vector/row/column)",!amaxerrors);
|
|
PrintResult("UpperHessenberg1Norm",!hsnerrors);
|
|
PrintResult("MatrixVectorMultiply",!mverrors);
|
|
PrintResult("InplaceTranspose",!iterrors);
|
|
PrintResult("CopyAndTranspose",!cterrors);
|
|
PrintResult("MatrixMatrixMultiply",!mmerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBlasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a,
|
|
const int ai1,
|
|
const int ai2,
|
|
const int aj1,
|
|
const int aj2,
|
|
const bool transa,
|
|
CMatrixDouble &b,
|
|
const int bi1,
|
|
const int bi2,
|
|
const int bj1,
|
|
const int bj2,
|
|
const bool transb,
|
|
const double alpha,
|
|
CMatrixDouble &c,
|
|
const int ci1,
|
|
const int ci2,
|
|
const int cj1,
|
|
const int cj2,
|
|
const double beta)
|
|
{
|
|
//--- create variables
|
|
int arows=0;
|
|
int acols=0;
|
|
int brows=0;
|
|
int bcols=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int r=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
//--- Setup
|
|
if(!transa)
|
|
{
|
|
arows=ai2-ai1+1;
|
|
acols=aj2-aj1+1;
|
|
}
|
|
else
|
|
{
|
|
arows=aj2-aj1+1;
|
|
acols=ai2-ai1+1;
|
|
}
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
brows=bi2-bi1+1;
|
|
bcols=bj2-bj1+1;
|
|
}
|
|
else
|
|
{
|
|
brows=bj2-bj1+1;
|
|
bcols=bi2-bi1+1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!"))
|
|
return;
|
|
//--- check
|
|
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
|
|
return;
|
|
//--- change values
|
|
l=arows;
|
|
r=bcols;
|
|
k=acols;
|
|
//--- allocation
|
|
ArrayResize(x1,k+1);
|
|
ArrayResize(x2,k+1);
|
|
//--- calculation
|
|
for(i=1; i<=l; i++)
|
|
{
|
|
for(j=1; j<=r; j++)
|
|
{
|
|
//--- check
|
|
if(!transa)
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1; i_<=bi2; i_++)
|
|
v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1; i_<=bj2; i_++)
|
|
v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1; i_<=bi2; i_++)
|
|
v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1; i_<=bj2; i_++)
|
|
v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
}
|
|
//--- check
|
|
if(beta==0.0)
|
|
c.Set(ci1+i-1,cj1+j-1,alpha*v);
|
|
else
|
|
c.Set(ci1+i-1,cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CKMeans |
|
|
//+------------------------------------------------------------------+
|
|
class CTestKMeansUnit
|
|
{
|
|
public:
|
|
static bool TestKMeans(const bool silent);
|
|
|
|
private:
|
|
static void SimpleTest1(const int nvars,const int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors);
|
|
static void RestartsTest(bool &converrors,bool &restartserrors);
|
|
static double RNormal(void);
|
|
static void RSphere(CMatrixDouble &xy,const int n,const int i);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CKMeans |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestKMeansUnit::TestKMeans(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int nf=0;
|
|
int maxnf=0;
|
|
int nc=0;
|
|
int maxnc=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
bool converrors;
|
|
bool simpleerrors;
|
|
bool complexerrors;
|
|
bool othererrors;
|
|
bool restartserrors;
|
|
//--- Primary settings
|
|
maxnf=5;
|
|
maxnc=5;
|
|
passcount=10;
|
|
waserrors=false;
|
|
converrors=false;
|
|
othererrors=false;
|
|
simpleerrors=false;
|
|
complexerrors=false;
|
|
restartserrors=false;
|
|
//--- calculation
|
|
for(nf=1; nf<=maxnf; nf++)
|
|
{
|
|
for(nc=1; nc<=maxnc; nc++)
|
|
SimpleTest1(nf,nc,passcount,converrors,othererrors,simpleerrors);
|
|
}
|
|
RestartsTest(converrors,restartserrors);
|
|
//--- Final report
|
|
waserrors=(((converrors||othererrors)||simpleerrors)||complexerrors)||restartserrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("K-MEANS TEST");
|
|
PrintResult("* CONVERGENCE",!converrors);
|
|
PrintResult("* SIMPLE TASKS",!simpleerrors);
|
|
PrintResult("* COMPLEX TASKS",!complexerrors);
|
|
PrintResult("* OTHER PROPERTIES",!othererrors);
|
|
PrintResult("* RESTARTS PROPERTIES",!restartserrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Simple test 1: ellipsoid in NF-dimensional space. |
|
|
//| compare k-means centers with random centers |
|
|
//+------------------------------------------------------------------+
|
|
void CTestKMeansUnit::SimpleTest1(const int nvars,const int nc,
|
|
int passcount,bool &converrors,
|
|
bool &othererrors,bool &simpleerrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=0;
|
|
int majoraxis=0;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int info=0;
|
|
int pass=0;
|
|
int restarts=0;
|
|
double ekmeans=0;
|
|
double erandom=0;
|
|
double dclosest=0;
|
|
int cclosest=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tmp[];
|
|
int xyc[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble c;
|
|
//--- initialization
|
|
npoints=nc*100;
|
|
restarts=5;
|
|
passcount=10;
|
|
//--- allocation
|
|
ArrayResize(tmp,nvars);
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Fill
|
|
xy.Resize(npoints,nvars);
|
|
majoraxis=CMath::RandomInteger(nvars);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
RSphere(xy,nvars,i);
|
|
xy.Set(i,majoraxis,nc*xy[i][majoraxis]);
|
|
}
|
|
//--- Test
|
|
CKMeans::KMeansGenerate(xy,npoints,nvars,nc,restarts,info,c,xyc);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- Test that XYC is correct mapping to cluster centers
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
cclosest=-1;
|
|
dclosest=CMath::m_maxrealnumber;
|
|
//--- calculation
|
|
for(j=0; j<nc; j++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=xy.Get(i,i_);
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=tmp[i_]-c.Get(i_,j);
|
|
v=0.0;
|
|
for(i_=0; i_<nvars ; i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
//--- check
|
|
if(v<dclosest)
|
|
{
|
|
cclosest=j;
|
|
dclosest=v;
|
|
}
|
|
}
|
|
//--- check
|
|
if(cclosest!=xyc[i])
|
|
{
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Use first NC rows of XY as random centers
|
|
//--- (XY is totally random,so it is as good as any other choice).
|
|
//--- Compare potential functions.
|
|
ekmeans=0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=xy.Get(i,i_);
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=tmp[i_]-c[i_][xyc[i]];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nvars ; i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
ekmeans=ekmeans+v;
|
|
}
|
|
//--- calculation
|
|
erandom=0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
dclosest=CMath::m_maxrealnumber;
|
|
v=0;
|
|
for(j=0; j<nc; j++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=xy.Get(i,i_);
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=tmp[i_]-xy.Get(j,i_);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nvars ; i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
//--- check
|
|
if(v<dclosest)
|
|
dclosest=v;
|
|
}
|
|
erandom=erandom+v;
|
|
}
|
|
//--- check
|
|
if(erandom<ekmeans)
|
|
{
|
|
simpleerrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This non-deterministic test checks that Restarts>1 significantly |
|
|
//| improves quality of results. |
|
|
//| Subroutine generates random task 3 unit balls in 2D,each with 20 |
|
|
//| points, separated by 5 units wide gaps,and solves it with |
|
|
//| Restarts=1 and with Restarts=5. Potential functions are compared,|
|
|
//| outcome of the trial is either 0 or 1 (depending on what is |
|
|
//| better). |
|
|
//| Sequence of 1000 such tasks is olved. If Restarts>1 actually |
|
|
//| improve quality of solution,sum of outcome will be non-binomial. |
|
|
//| If it doesn't matter,it will be binomially distributed. |
|
|
//| P.S. This test was added after report from Gianluca Borello who |
|
|
//| noticed error in the handling of multiple restarts. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestKMeansUnit::RestartsTest(bool &converrors,bool &restartserrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=0;
|
|
int nvars=0;
|
|
int nclusters=0;
|
|
int clustersize=0;
|
|
int restarts=0;
|
|
int passcount=0;
|
|
double sigmathreshold=0;
|
|
double p=0;
|
|
double s=0;
|
|
int i=0;
|
|
int j=0;
|
|
int info=0;
|
|
int pass=0;
|
|
double ea=0;
|
|
double eb=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble ca;
|
|
CMatrixDouble cb;
|
|
//--- create arrays
|
|
int xyca[];
|
|
int xycb[];
|
|
double tmp[];
|
|
//--- initialization
|
|
restarts=5;
|
|
passcount=1000;
|
|
clustersize=20;
|
|
nclusters=3;
|
|
nvars=2;
|
|
npoints=nclusters*clustersize;
|
|
sigmathreshold=5;
|
|
//--- allocation
|
|
xy.Resize(npoints,nvars);
|
|
ArrayResize(tmp,nvars);
|
|
p=0;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Fill
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
RSphere(xy,nvars,i);
|
|
for(j=0; j<nvars ; j++)
|
|
xy.Set(i,j,xy.Get(i,j)+(double)i/(double)clustersize*5);
|
|
}
|
|
//--- Test: Restarts=1
|
|
CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,1,info,ca,xyca);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
ea=0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=xy.Get(i,i_);
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=tmp[i_]-ca[i_][xyca[i]];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nvars ; i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
ea=ea+v;
|
|
}
|
|
//--- Test: Restarts>1
|
|
CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,restarts,info,cb,xycb);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
eb=0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=xy.Get(i,i_);
|
|
for(i_=0; i_<nvars ; i_++)
|
|
tmp[i_]=tmp[i_]-cb[i_][xycb[i]];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nvars ; i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
eb=eb+v;
|
|
}
|
|
//--- Calculate statistic.
|
|
if(ea<eb)
|
|
p=p+1;
|
|
//--- check
|
|
if(ea==eb)
|
|
p=p+0.5;
|
|
}
|
|
//--- If Restarts doesn't influence quality of centers found,P must be
|
|
//--- binomially distributed random value with mean 0.5*PassCount and
|
|
//--- standard deviation Sqrt(PassCount/4).
|
|
//--- If Restarts do influence quality of solution,P must be significantly
|
|
//--- lower than 0.5*PassCount.
|
|
s=(p-0.5*passcount)/MathSqrt((double)passcount/4.0);
|
|
restartserrors=restartserrors||s>(double)(-sigmathreshold);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal number |
|
|
//+------------------------------------------------------------------+
|
|
double CTestKMeansUnit::RNormal(void)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double s=0;
|
|
double x1=0;
|
|
double x2=0;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- change values
|
|
u=2*CMath::RandomReal()-1;
|
|
v=2*CMath::RandomReal()-1;
|
|
s=CMath::Sqr(u)+CMath::Sqr(v);
|
|
//--- check
|
|
if(s>0.0 && s<1.0)
|
|
{
|
|
s=MathSqrt(-(2*MathLog(s)/s));
|
|
x1=u*s;
|
|
x2=v*s;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(x1==0)
|
|
result=x2;
|
|
else
|
|
result=x1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random point from sphere |
|
|
//+------------------------------------------------------------------+
|
|
void CTestKMeansUnit::RSphere(CMatrixDouble &xy,const int n,const int i)
|
|
{
|
|
//--- create variables
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
xy.Set(i,j,RNormal());
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=xy.Get(i,i_)*xy.Get(i,i_);
|
|
//--- calculation
|
|
v=CMath::RandomReal()/MathSqrt(v);
|
|
for(i_=0; i_<n; i_++)
|
|
xy.Set(i,i_,v*xy.Get(i,i_));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestHblasUnit
|
|
{
|
|
public:
|
|
static bool TestHblas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHblas |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestHblasUnit::TestHblas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
bool waserrors;
|
|
double mverr=0;
|
|
double threshold=0;
|
|
complex alpha=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
complex x[];
|
|
complex y1[];
|
|
complex y2[];
|
|
complex y3[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex ua;
|
|
CMatrixComplex la;
|
|
//--- initialization
|
|
mverr=0;
|
|
waserrors=false;
|
|
maxn=10;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Test MV
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n+1,n+1);
|
|
ua.Resize(n+1,n+1);
|
|
la.Resize(n+1,n+1);
|
|
ArrayResize(x,n+1);
|
|
ArrayResize(y1,n+1);
|
|
ArrayResize(y2,n+1);
|
|
ArrayResize(y3,n+1);
|
|
//--- fill A,UA,LA
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
a.SetRe(i,i,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,i,0);
|
|
//--- change values
|
|
for(j=i+1; j<=n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(j,i,CMath::Conj(a.Get(i,j)));
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
ua.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=i; j<=n; j++)
|
|
ua.Set(i,j,a.Get(i,j));
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
la.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=i; j++)
|
|
la.Set(i,j,a.Get(i,j));
|
|
}
|
|
//--- test on different I1,I2
|
|
for(i1=1; i1<=n; i1++)
|
|
{
|
|
for(i2=i1; i2<=n; i2++)
|
|
{
|
|
//--- Fill X,choose Alpha
|
|
for(i=1; i<=i2-i1+1; i++)
|
|
{
|
|
x[i].real=2*CMath::RandomReal()-1;
|
|
x[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
alpha.real=2*CMath::RandomReal()-1;
|
|
alpha.imag=2*CMath::RandomReal()-1;
|
|
//--- calculate A*x,UA*x,LA*x
|
|
for(i=i1; i<=i2; i++)
|
|
{
|
|
i1_=1-i1;
|
|
v=0.0;
|
|
for(i_=i1; i_<=i2; i_++)
|
|
v+=a.Get(i,i_)*x[i_+i1_];
|
|
y1[i-i1+1]=alpha*v;
|
|
}
|
|
//--- function call
|
|
CHblas::HermitianMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2);
|
|
//--- function call
|
|
CHblas::HermitianMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3);
|
|
//--- Calculate error
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
y2[i_]=y2[i_]-y1[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
v+=y2[i_]*CMath::Conj(y2[i_]);
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v)));
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
y3[i_]=y3[i_]-y1[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
v+=y3[i_]*CMath::Conj(y3[i_]);
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v)));
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=mverr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING HERMITIAN BLAS");
|
|
PrintFormat("MV error: %5.3E",mverr);
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CReflections |
|
|
//+------------------------------------------------------------------+
|
|
class CTestReflectionsUnit
|
|
{
|
|
public:
|
|
static bool TestReflections(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CReflections |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestReflectionsUnit::TestReflections(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int maxmn=0;
|
|
double tmp=0;
|
|
double beta=0;
|
|
double tau=0;
|
|
double err=0;
|
|
double mer=0;
|
|
double mel=0;
|
|
double meg=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
int tasktype=0;
|
|
double xscale=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double v[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble h;
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble c;
|
|
//--- initialization
|
|
passcount=10;
|
|
threshold=100*CMath::m_machineepsilon;
|
|
mer=0;
|
|
mel=0;
|
|
meg=0;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(m=1; m<=10; m++)
|
|
{
|
|
//--- Task
|
|
n=1+CMath::RandomInteger(10);
|
|
m=1+CMath::RandomInteger(10);
|
|
maxmn=MathMax(m,n);
|
|
//--- Initialize
|
|
ArrayResize(x,maxmn+1);
|
|
ArrayResize(v,maxmn+1);
|
|
ArrayResize(work,maxmn+1);
|
|
h.Resize(maxmn+1,maxmn+1);
|
|
a.Resize(maxmn+1,maxmn+1);
|
|
b.Resize(maxmn+1,maxmn+1);
|
|
c.Resize(maxmn+1,maxmn+1);
|
|
//--- GenerateReflection,three tasks are possible:
|
|
//--- * random X
|
|
//--- * zero X
|
|
//--- * non-zero X[1],all other are zeros
|
|
//--- * random X,near underflow scale
|
|
//--- * random X,near overflow scale
|
|
for(tasktype=0; tasktype<=4; tasktype++)
|
|
{
|
|
xscale=1;
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
for(i=1; i<=n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
for(i=1; i<=n; i++)
|
|
x[i]=0;
|
|
}
|
|
//--- check
|
|
if(tasktype==2)
|
|
{
|
|
x[1]=2*CMath::RandomReal()-1;
|
|
for(i=2; i<=n; i++)
|
|
x[i]=0;
|
|
}
|
|
//--- check
|
|
if(tasktype==3)
|
|
{
|
|
for(i=1; i<=n; i++)
|
|
x[i]=(CMath::RandomInteger(21)-10)*CMath::m_minrealnumber;
|
|
xscale=10*CMath::m_minrealnumber;
|
|
}
|
|
//--- check
|
|
if(tasktype==4)
|
|
{
|
|
for(i=1; i<=n; i++)
|
|
x[i]=(2*CMath::RandomReal()-1)*CMath::m_maxrealnumber;
|
|
xscale=CMath::m_maxrealnumber;
|
|
}
|
|
for(i_=1; i_<=n; i_++)
|
|
{
|
|
v[i_]=x[i_];
|
|
}
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,n,tau);
|
|
//--- change values
|
|
beta=v[1];
|
|
v[1]=1;
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h.Set(i,j,1-tau*v[i]*v[j]);
|
|
else
|
|
h.Set(i,j,-(tau*v[i]*v[j]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1; i_<=n; i_++)
|
|
tmp+=h.Get(i,i_)*x[i_];
|
|
//--- check
|
|
if(i==1)
|
|
err=MathMax(err,MathAbs(tmp-beta));
|
|
else
|
|
err=MathMax(err,MathAbs(tmp));
|
|
}
|
|
meg=MathMax(meg,err/xscale);
|
|
}
|
|
//--- ApplyReflectionFromTheLeft
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,m,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work);
|
|
//--- change values
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=m; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h.Set(i,j,1-tau*v[i]*v[j]);
|
|
else
|
|
h.Set(i,j,-(tau*v[i]*v[j]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1; i_<=m; i_++)
|
|
tmp+=h.Get(i,i_)*a.Get(i_,j);
|
|
c.Set(i,j,tmp);
|
|
}
|
|
}
|
|
//--- change value
|
|
err=0;
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
err=MathMax(err,MathAbs(b.Get(i,j)-c.Get(i,j)));
|
|
}
|
|
mel=MathMax(mel,err);
|
|
//--- ApplyReflectionFromTheRight
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,n,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work);
|
|
//--- change value
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h.Set(i,j,1-tau*v[i]*v[j]);
|
|
else
|
|
h.Set(i,j,-(tau*v[i]*v[j]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1; i_<=n; i_++)
|
|
tmp+=a.Get(i,i_)*h.Get(i_,j);
|
|
c.Set(i,j,tmp);
|
|
}
|
|
}
|
|
err=0;
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
err=MathMax(err,MathAbs(b.Get(i,j)-c.Get(i,j)));
|
|
}
|
|
mer=MathMax(mer,err);
|
|
}
|
|
}
|
|
}
|
|
//--- Overflow crash test
|
|
ArrayResize(x,11);
|
|
ArrayResize(v,11);
|
|
for(i=1; i<=10; i++)
|
|
v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1);
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,10,tau);
|
|
result=(meg<=threshold && mel<=threshold) && mer<=threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING REFLECTIONS");
|
|
PrintFormat("Pass count is %d",passcount);
|
|
PrintFormat("Generate absolute error is %5.3E",meg);
|
|
PrintFormat("Apply(Left) absolute error is %5.3E",mel);
|
|
PrintFormat("Apply(Right) absolute error is %5.3E",mer);
|
|
Print("Overflow crash test passed");
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CComplexReflections |
|
|
//+------------------------------------------------------------------+
|
|
class CTestCReflectionsUnit
|
|
{
|
|
public:
|
|
static bool TestCReflections(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CComplexReflections |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestCReflectionsUnit::TestCReflections(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int maxmn=0;
|
|
complex tmp=0;
|
|
complex beta=0;
|
|
complex tau=0;
|
|
double err=0;
|
|
double mer=0;
|
|
double mel=0;
|
|
double meg=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
double threshold=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex x[];
|
|
complex v[];
|
|
complex work[];
|
|
//--- create matrix
|
|
CMatrixComplex h;
|
|
CMatrixComplex a;
|
|
CMatrixComplex b;
|
|
CMatrixComplex c;
|
|
//--- initialization
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
passcount=1000;
|
|
mer=0;
|
|
mel=0;
|
|
meg=0;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Task
|
|
n=1+CMath::RandomInteger(10);
|
|
m=1+CMath::RandomInteger(10);
|
|
maxmn=MathMax(m,n);
|
|
//--- Initialize
|
|
ArrayResize(x,maxmn+1);
|
|
ArrayResize(v,maxmn+1);
|
|
ArrayResize(work,maxmn+1);
|
|
h.Resize(maxmn+1,maxmn+1);
|
|
a.Resize(maxmn+1,maxmn+1);
|
|
b.Resize(maxmn+1,maxmn+1);
|
|
c.Resize(maxmn+1,maxmn+1);
|
|
//--- GenerateReflection
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
x[i].real=2*CMath::RandomReal()-1;
|
|
x[i].imag=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,n,tau);
|
|
//--- change values
|
|
beta=v[1];
|
|
v[1]=1;
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1);
|
|
else
|
|
h.Set(i,j,-(tau*v[i]*CMath::Conj(v[j])));
|
|
}
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1; i_<=n; i_++)
|
|
tmp+=CMath::Conj(h.Get(i_,i))*x[i_];
|
|
//--- check
|
|
if(i==1)
|
|
err=MathMax(err,CMath::AbsComplex(tmp-beta));
|
|
else
|
|
err=MathMax(err,CMath::AbsComplex(tmp));
|
|
}
|
|
err=MathMax(err,MathAbs(beta.imag));
|
|
meg=MathMax(meg,err);
|
|
//--- ApplyReflectionFromTheLeft
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
x[i].real=2*CMath::RandomReal()-1;
|
|
x[i].imag=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,m,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work);
|
|
//--- change values
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=m; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1);
|
|
else
|
|
h.Set(i,j,-(tau*v[i]*CMath::Conj(v[j])));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1; i_<=m; i_++)
|
|
tmp+=h.Get(i,i_)*a.Get(i_,j);
|
|
c.Set(i,j,tmp);
|
|
}
|
|
}
|
|
err=0;
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
err=MathMax(err,CMath::AbsComplex(b.Get(i,j)-c.Get(i,j)));
|
|
}
|
|
mel=MathMax(mel,err);
|
|
//--- ApplyReflectionFromTheRight
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,n,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work);
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1);
|
|
else
|
|
h.Set(i,j,-(tau*v[i]*CMath::Conj(v[j])));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1; i_<=n; i_++)
|
|
tmp+=a.Get(i,i_)*h.Get(i_,j);
|
|
c.Set(i,j,tmp);
|
|
}
|
|
}
|
|
err=0;
|
|
for(i=1; i<=m; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
err=MathMax(err,CMath::AbsComplex(b.Get(i,j)-c.Get(i,j)));
|
|
}
|
|
mer=MathMax(mer,err);
|
|
}
|
|
//--- Overflow crash test
|
|
ArrayResize(x,11);
|
|
ArrayResize(v,11);
|
|
for(i=1; i<=10; i++)
|
|
v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1);
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,10,tau);
|
|
//--- report
|
|
waserrors=(meg>threshold || mel>threshold) || mer>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING COMPLEX REFLECTIONS");
|
|
PrintFormat("Generate error: %5.3E",meg);
|
|
PrintFormat("Apply(L) error: %5.3E",mel);
|
|
PrintFormat("Apply(R) error: %5.3E",mer);
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
PrintResult("Overflow crash test",true);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSblasUnit
|
|
{
|
|
public:
|
|
static bool TestSblas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSblas |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSblasUnit::TestSblas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
bool waserrors;
|
|
double mverr=0;
|
|
double threshold=0;
|
|
double alpha=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y1[];
|
|
double y2[];
|
|
double y3[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble ua;
|
|
CMatrixDouble la;
|
|
//--- initialization
|
|
mverr=0;
|
|
waserrors=false;
|
|
maxn=10;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Test MV
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n+1,n+1);
|
|
ua.Resize(n+1,n+1);
|
|
la.Resize(n+1,n+1);
|
|
ArrayResize(x,n+1);
|
|
ArrayResize(y1,n+1);
|
|
ArrayResize(y2,n+1);
|
|
ArrayResize(y3,n+1);
|
|
//--- fill A,UA,LA
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
a.Set(i,i,2*CMath::RandomReal()-1);
|
|
for(j=i+1; j<=n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
ua.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=i; j<=n; j++)
|
|
ua.Set(i,j,a.Get(i,j));
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
la.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=i; j++)
|
|
la.Set(i,j,a.Get(i,j));
|
|
}
|
|
//--- test on different I1,I2
|
|
for(i1=1; i1<=n; i1++)
|
|
{
|
|
for(i2=i1; i2<=n; i2++)
|
|
{
|
|
//--- Fill X,choose Alpha
|
|
for(i=1; i<=i2-i1+1; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
alpha=2*CMath::RandomReal()-1;
|
|
//--- calculate A*x,UA*x,LA*x
|
|
for(i=i1; i<=i2; i++)
|
|
{
|
|
i1_=1-i1;
|
|
v=0.0;
|
|
for(i_=i1; i_<=i2; i_++)
|
|
v+=a.Get(i,i_)*x[i_+i1_];
|
|
y1[i-i1+1]=alpha*v;
|
|
}
|
|
//--- function call
|
|
CSblas::SymmetricMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2);
|
|
//--- function call
|
|
CSblas::SymmetricMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3);
|
|
//--- Calculate error
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
y2[i_]=y2[i_]-y1[i_];
|
|
v=0.0;
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
v+=y2[i_]*y2[i_];
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(v));
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
y3[i_]=y3[i_]-y1[i_];
|
|
v=0.0;
|
|
for(i_=1; i_<=i2-i1+1; i_++)
|
|
v+=y3[i_]*y3[i_];
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(v));
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=mverr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SYMMETRIC BLAS");
|
|
PrintFormat("MV error: %5.3E",mverr);
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class COrtFac |
|
|
//+------------------------------------------------------------------+
|
|
class CTestOrtFacUnit
|
|
{
|
|
public:
|
|
static bool TestOrtFac(const bool silent);
|
|
|
|
private:
|
|
static double RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n);
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
|
|
static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity);
|
|
static void InternalMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2);
|
|
static void TestRQRProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &qrerrors);
|
|
static void TestCQRProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &qrerrors);
|
|
static void TestRLQProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &lqerrors);
|
|
static void TestCLQProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &lqerrors);
|
|
static void TestRBdProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &bderrors);
|
|
static void TestRHessProblem(CMatrixDouble &a,const int n,const double threshold,bool &hesserrors);
|
|
static void TestRTdProblem(CMatrixDouble &a,const int n,const double threshold,bool &tderrors);
|
|
static void TestCTdProblem(CMatrixComplex &a,const int n,const double threshold,bool &tderrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Main unittest subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestOrtFacUnit::TestOrtFac(const bool silent)
|
|
{
|
|
int maxmn=0;
|
|
double threshold=0;
|
|
int passcount=0;
|
|
int mx=0;
|
|
int m=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool rqrerrors;
|
|
bool rlqerrors;
|
|
bool cqrerrors;
|
|
bool clqerrors;
|
|
bool rbderrors;
|
|
bool rhesserrors;
|
|
bool rtderrors;
|
|
bool ctderrors;
|
|
bool waserrors;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixComplex ca;
|
|
//--- initialization
|
|
waserrors=false;
|
|
rqrerrors=false;
|
|
rlqerrors=false;
|
|
cqrerrors=false;
|
|
clqerrors=false;
|
|
rbderrors=false;
|
|
rhesserrors=false;
|
|
rtderrors=false;
|
|
ctderrors=false;
|
|
maxmn=3*CAblas::AblasBlockSize()+1;
|
|
passcount=1;
|
|
threshold=5*1000*CMath::m_machineepsilon;
|
|
//--- Different problems
|
|
for(mx=1; mx<=maxmn; mx++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Rectangular factorizations: QR,LQ,bidiagonal
|
|
//--- Matrix types: zero,dense,sparse
|
|
n=1+CMath::RandomInteger(mx);
|
|
m=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
n=mx;
|
|
else
|
|
m=mx;
|
|
//--- allocation
|
|
ra.Resize(m,n);
|
|
ca.Resize(m,n);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,0);
|
|
ca.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRQRProblem(ra,m,n,threshold,rqrerrors);
|
|
TestRLQProblem(ra,m,n,threshold,rlqerrors);
|
|
TestCQRProblem(ca,m,n,threshold,cqrerrors);
|
|
TestCLQProblem(ca,m,n,threshold,clqerrors);
|
|
TestRBdProblem(ra,m,n,threshold,rbderrors);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRQRProblem(ra,m,n,threshold,rqrerrors);
|
|
TestRLQProblem(ra,m,n,threshold,rlqerrors);
|
|
TestCQRProblem(ca,m,n,threshold,cqrerrors);
|
|
TestCLQProblem(ca,m,n,threshold,clqerrors);
|
|
TestRBdProblem(ra,m,n,threshold,rbderrors);
|
|
//--- function calls
|
|
RMatrixFillSparseA(ra,m,n,0.95);
|
|
CMatrixFillSparseA(ca,m,n,0.95);
|
|
//--- function calls
|
|
TestRQRProblem(ra,m,n,threshold,rqrerrors);
|
|
TestRLQProblem(ra,m,n,threshold,rlqerrors);
|
|
TestCQRProblem(ca,m,n,threshold,cqrerrors);
|
|
TestCLQProblem(ca,m,n,threshold,clqerrors);
|
|
TestRBdProblem(ra,m,n,threshold,rbderrors);
|
|
//--- Square factorizations: Hessenberg,tridiagonal
|
|
//--- Matrix types: zero,dense,sparse
|
|
ra.Resize(mx,mx);
|
|
ca.Resize(mx,mx);
|
|
//--- change values
|
|
for(i=0; i<=mx-1; i++)
|
|
{
|
|
for(j=0; j<=mx-1; j++)
|
|
{
|
|
ra.Set(i,j,0);
|
|
ca.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
TestRHessProblem(ra,mx,threshold,rhesserrors);
|
|
//--- change values
|
|
for(i=0; i<=mx-1; i++)
|
|
{
|
|
for(j=0; j<=mx-1; j++)
|
|
{
|
|
ra.Set(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRHessProblem(ra,mx,threshold,rhesserrors);
|
|
RMatrixFillSparseA(ra,mx,mx,0.95);
|
|
CMatrixFillSparseA(ca,mx,mx,0.95);
|
|
TestRHessProblem(ra,mx,threshold,rhesserrors);
|
|
//--- Symetric factorizations: tridiagonal
|
|
//--- Matrix types: zero,dense,sparse
|
|
ra=matrix<double>::Zeros(mx,mx);
|
|
ca=matrix<complex>::Zeros(mx,mx);
|
|
//--- function calls
|
|
TestRTdProblem(ra,mx,threshold,rtderrors);
|
|
TestCTdProblem(ca,mx,threshold,ctderrors);
|
|
//--- change values
|
|
for(i=0; i<mx; i++)
|
|
{
|
|
for(j=i; j<mx; j++)
|
|
{
|
|
ra.Set(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
ra.Set(j,i,ra.Get(i,j));
|
|
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
|
|
}
|
|
}
|
|
for(i=0; i<mx; i++)
|
|
ca.Set(i,i,((i+1.0)*(i+1.0))/(mx*mx)-0.5); //2 * CMath::RandomReal() - 1);
|
|
//--- function calls
|
|
TestRTdProblem(ra,mx,threshold,rtderrors);
|
|
TestCTdProblem(ca,mx,threshold,ctderrors);
|
|
RMatrixFillSparseA(ra,mx,mx,0.95);
|
|
CMatrixFillSparseA(ca,mx,mx,0.95);
|
|
//--- change values
|
|
for(i=0; i<=mx-1; i++)
|
|
{
|
|
for(j=i; j<=mx-1; j++)
|
|
{
|
|
ra.Set(j,i,ra.Get(i,j));
|
|
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
|
|
}
|
|
}
|
|
for(i=0; i<=mx-1; i++)
|
|
ca.Set(i,i,2*CMath::RandomReal()-1);
|
|
//--- function calls
|
|
TestRTdProblem(ra,mx,threshold,rtderrors);
|
|
TestCTdProblem(ca,mx,threshold,ctderrors);
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((((((rqrerrors||rlqerrors)||cqrerrors)||clqerrors)||rbderrors)||rhesserrors)||rtderrors)||ctderrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ORTFAC UNIT");
|
|
PrintResult("RQR ERRORS",!rqrerrors);
|
|
PrintResult("RLQ ERRORS",!rlqerrors);
|
|
PrintResult("CQR ERRORS",!cqrerrors);
|
|
PrintResult("CLQ ERRORS",!clqerrors);
|
|
PrintResult("RBD ERRORS",!rbderrors);
|
|
PrintResult("RHESS ERRORS",!rhesserrors);
|
|
PrintResult("RTD ERRORS",!rtderrors);
|
|
PrintResult("CTD ERRORS",!ctderrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Diff |
|
|
//+------------------------------------------------------------------+
|
|
double CTestOrtFacUnit::RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,
|
|
const int m,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
result=MathMax(result,MathAbs(b.Get(i,j)-a.Get(i,j)));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m,
|
|
const int n,CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix multiplication |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::InternalMatrixMatrixMultiply(CMatrixDouble &a,
|
|
const int ai1,
|
|
const int ai2,
|
|
const int aj1,
|
|
const int aj2,
|
|
const bool transa,
|
|
CMatrixDouble &b,
|
|
const int bi1,
|
|
const int bi2,
|
|
const int bj1,
|
|
const int bj2,
|
|
const bool transb,
|
|
CMatrixDouble &c,
|
|
const int ci1,
|
|
const int ci2,
|
|
const int cj1,
|
|
const int cj2)
|
|
{
|
|
//--- create variables
|
|
int arows=0;
|
|
int acols=0;
|
|
int brows=0;
|
|
int bcols=0;
|
|
int crows=0;
|
|
int ccols=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int r=0;
|
|
double v=0;
|
|
double beta=0;
|
|
double alpha=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- Pre-setup
|
|
k=MathMax(ai2-ai1+1,aj2-aj1+1);
|
|
k=MathMax(k,bi2-bi1+1);
|
|
k=MathMax(k,bj2-bj1+1);
|
|
//--- allocation
|
|
ArrayResize(work,k+1);
|
|
//--- initialization
|
|
beta=0;
|
|
alpha=1;
|
|
//--- Setup
|
|
if(!transa)
|
|
{
|
|
arows=ai2-ai1+1;
|
|
acols=aj2-aj1+1;
|
|
}
|
|
else
|
|
{
|
|
arows=aj2-aj1+1;
|
|
acols=ai2-ai1+1;
|
|
}
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
brows=bi2-bi1+1;
|
|
bcols=bj2-bj1+1;
|
|
}
|
|
else
|
|
{
|
|
brows=bj2-bj1+1;
|
|
bcols=bi2-bi1+1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(acols==brows,"MatrixMatrixMultiply: incorrect matrix sizes!"))
|
|
return;
|
|
//--- check
|
|
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
|
|
return;
|
|
//--- change values
|
|
crows=arows;
|
|
ccols=bcols;
|
|
//--- Test WORK
|
|
i=MathMax(arows,acols);
|
|
i=MathMax(brows,i);
|
|
i=MathMax(i,bcols);
|
|
work[1]=0;
|
|
work[i]=i;
|
|
//--- Prepare C
|
|
if(beta==0.0)
|
|
{
|
|
for(i=ci1; i<=ci2; i++)
|
|
{
|
|
for(j=cj1; j<=cj2; j++)
|
|
c.Set(i,j,0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=ci1; i<=ci2; i++)
|
|
{
|
|
for(i_=cj1; i_<=cj2; i_++)
|
|
c.Set(i,i_,beta*c.Get(i,i_));
|
|
}
|
|
}
|
|
//--- A*B
|
|
if(!transa && !transb)
|
|
{
|
|
for(l=ai1; l<=ai2; l++)
|
|
{
|
|
for(r=bi1; r<=bi2; r++)
|
|
{
|
|
//--- change values
|
|
v=alpha*a[l][aj1+r-bi1];
|
|
k=ci1+l-ai1;
|
|
i1_=bj1-cj1;
|
|
//--- calculation
|
|
for(i_=cj1; i_<=cj2; i_++)
|
|
c.Set(k,i_,c[k][i_]+v*b[r][i_+i1_]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- A*B'
|
|
if(!transa && transb)
|
|
{
|
|
//--- check
|
|
if(arows*acols<brows*bcols)
|
|
{
|
|
for(r=bi1; r<=bi2; r++)
|
|
{
|
|
for(l=ai1; l<=ai2; l++)
|
|
{
|
|
//--- change values
|
|
i1_=bj1-aj1;
|
|
v=0.0;
|
|
for(i_=aj1; i_<=aj2; i_++)
|
|
v+=a[l][i_]*b[r][i_+i1_];
|
|
//--- calculation
|
|
c.Set(ci1+l-ai1,cj1+r-bi1,c[ci1+l-ai1][cj1+r-bi1]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for(l=ai1; l<=ai2; l++)
|
|
{
|
|
for(r=bi1; r<=bi2; r++)
|
|
{
|
|
//--- change values
|
|
i1_=bj1-aj1;
|
|
v=0.0;
|
|
for(i_=aj1; i_<=aj2; i_++)
|
|
v+=a[l][i_]*b[r][i_+i1_];
|
|
//--- calculation
|
|
c.Set(ci1+l-ai1,cj1+r-bi1,c[ci1+l-ai1][cj1+r-bi1]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
//--- A'*B
|
|
if(transa && !transb)
|
|
{
|
|
for(l=aj1; l<=aj2; l++)
|
|
{
|
|
for(r=bi1; r<=bi2; r++)
|
|
{
|
|
//--- change values
|
|
v=alpha*a[ai1+r-bi1][l];
|
|
k=ci1+l-aj1;
|
|
i1_=bj1-cj1;
|
|
//--- calculation
|
|
for(i_=cj1; i_<=cj2; i_++)
|
|
c.Set(k,i_,c[k][i_]+v*b[r][i_+i1_]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- A'*B'
|
|
if(transa && transb)
|
|
{
|
|
//--- check
|
|
if(arows*acols<brows*bcols)
|
|
{
|
|
for(r=bi1; r<=bi2; r++)
|
|
{
|
|
for(i=1; i<=crows; i++)
|
|
work[i]=0;
|
|
for(l=ai1; l<=ai2; l++)
|
|
{
|
|
//--- change values
|
|
v=alpha*b[r][bj1+l-ai1];
|
|
k=cj1+r-bi1;
|
|
i1_=aj1-1;
|
|
//--- calculation
|
|
for(i_=1; i_<=crows; i_++)
|
|
work[i_]=work[i_]+v*a[l][i_+i1_];
|
|
}
|
|
//--- calculation
|
|
i1_=1-ci1;
|
|
for(i_=ci1; i_<=ci2; i_++)
|
|
c.Set(i_,k,c[i_][k]+work[i_+i1_]);
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for(l=aj1; l<=aj2; l++)
|
|
{
|
|
//--- change values
|
|
k=ai2-ai1+1;
|
|
i1_=ai1-1;
|
|
for(i_=1; i_<=k; i_++)
|
|
work[i_]=a[i_+i1_][l];
|
|
//--- calculation
|
|
for(r=bi1; r<=bi2; r++)
|
|
{
|
|
//--- change values
|
|
i1_=bj1-1;
|
|
v=0.0;
|
|
for(i_=1; i_<=k; i_++)
|
|
v+=work[i_]*b[r][i_+i1_];
|
|
c.Set(ci1+l-aj1,cj1+r-bi1,c[ci1+l-aj1][cj1+r-bi1]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestRQRProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &qrerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double taub[];
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
CMatrixDouble q;
|
|
CMatrixDouble r;
|
|
CMatrixDouble q2;
|
|
//--- Test decompose-and-unpack error
|
|
RMatrixMakeACopy(a,m,n,b);
|
|
//--- function call
|
|
COrtFac::RMatrixQR(b,m,n,taub);
|
|
COrtFac::RMatrixQRUnpackQ(b,m,n,taub,m,q);
|
|
COrtFac::RMatrixQRUnpackR(b,m,n,r);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=q.Get(i,i_)*r.Get(i_,j);
|
|
qrerrors=qrerrors||MathAbs(v-a.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=MathMin(i,n-1)-1; j++)
|
|
qrerrors=qrerrors||r.Get(i,j)!=0.0;
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=q.Get(i,i_)*q.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
qrerrors=qrerrors||MathAbs(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(m);
|
|
//--- function call
|
|
COrtFac::RMatrixQRUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
qrerrors=qrerrors||MathAbs(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestCQRProblem(CMatrixComplex &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &qrerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex taub[];
|
|
//--- create matrix
|
|
CMatrixComplex b;
|
|
CMatrixComplex q;
|
|
CMatrixComplex r;
|
|
CMatrixComplex q2;
|
|
//--- Test decompose-and-unpack error
|
|
CMatrixMakeACopy(a,m,n,b);
|
|
//--- function calls
|
|
COrtFac::CMatrixQR(b,m,n,taub);
|
|
COrtFac::CMatrixQRUnpackQ(b,m,n,taub,m,q);
|
|
COrtFac::CMatrixQRUnpackR(b,m,n,r);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=q.Get(i,i_)*r.Get(i_,j);
|
|
qrerrors=qrerrors||CMath::AbsComplex(v-a.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=MathMin(i,n-1)-1; j++)
|
|
qrerrors=qrerrors||r.Get(i,j)!=0;
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=q.Get(i,i_)*CMath::Conj(q.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
qrerrors=qrerrors||CMath::AbsComplex(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(m);
|
|
//--- function call
|
|
COrtFac::CMatrixQRUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
qrerrors=qrerrors||CMath::AbsComplex(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestRLQProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &lqerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double taub[];
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
CMatrixDouble q;
|
|
CMatrixDouble l;
|
|
CMatrixDouble q2;
|
|
//--- Test decompose-and-unpack error
|
|
RMatrixMakeACopy(a,m,n,b);
|
|
//--- function calls
|
|
COrtFac::RMatrixLQ(b,m,n,taub);
|
|
COrtFac::RMatrixLQUnpackQ(b,m,n,taub,n,q);
|
|
COrtFac::RMatrixLQUnpackL(b,m,n,l);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=l.Get(i,i_)*q.Get(i_,j);
|
|
lqerrors=lqerrors||MathAbs(v-a.Get(i,j))>=threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=MathMin(i,n-1)+1; j<n; j++)
|
|
lqerrors=lqerrors||l.Get(i,j)!=0.0;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i,i_)*q.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
lqerrors=lqerrors||MathAbs(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(n);
|
|
//--- function call
|
|
COrtFac::RMatrixLQUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
lqerrors=lqerrors||MathAbs(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestCLQProblem(CMatrixComplex &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &lqerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex taub[];
|
|
//--- create matrix
|
|
CMatrixComplex b;
|
|
CMatrixComplex q;
|
|
CMatrixComplex l;
|
|
CMatrixComplex q2;
|
|
//--- Test decompose-and-unpack error
|
|
CMatrixMakeACopy(a,m,n,b);
|
|
//--- function calls
|
|
COrtFac::CMatrixLQ(b,m,n,taub);
|
|
COrtFac::CMatrixLQUnpackQ(b,m,n,taub,n,q);
|
|
COrtFac::CMatrixLQUnpackL(b,m,n,l);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=l.Get(i,i_)*q.Get(i_,j);
|
|
lqerrors=lqerrors||CMath::AbsComplex(v-a.Get(i,j))>=threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=MathMin(i,n-1)+1; j<n; j++)
|
|
lqerrors=lqerrors||l.Get(i,j)!=0;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i,i_)*CMath::Conj(q.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
lqerrors=lqerrors||CMath::AbsComplex(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(n);
|
|
//--- function call
|
|
COrtFac::CMatrixLQUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
lqerrors=lqerrors||CMath::AbsComplex(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestRBdProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &bderrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
bool up;
|
|
double v=0;
|
|
int mtsize=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double taup[];
|
|
double tauq[];
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble t;
|
|
CMatrixDouble pt;
|
|
CMatrixDouble q;
|
|
CMatrixDouble r;
|
|
CMatrixDouble bd;
|
|
CMatrixDouble x;
|
|
CMatrixDouble r1;
|
|
CMatrixDouble r2;
|
|
//--- Bidiagonal decomposition error
|
|
RMatrixMakeACopy(a,m,n,t);
|
|
//--- function calls
|
|
COrtFac::RMatrixBD(t,m,n,tauq,taup);
|
|
COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,m,q);
|
|
COrtFac::RMatrixBDUnpackPT(t,m,n,taup,n,pt);
|
|
COrtFac::RMatrixBDUnpackDiagonals(t,m,n,up,d,e);
|
|
//--- allocation
|
|
bd.Resize(m,n);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
bd.Set(i,j,0);
|
|
}
|
|
for(i=0; i<=MathMin(m,n)-1; i++)
|
|
bd.Set(i,i,d[i]);
|
|
//--- check
|
|
if(up)
|
|
{
|
|
for(i=0; i<=MathMin(m,n)-2; i++)
|
|
bd.Set(i,i+1,e[i]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<=MathMin(m,n)-2; i++)
|
|
bd.Set(i+1,i,e[i]);
|
|
}
|
|
//--- allocation
|
|
r.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=q.Get(i,i_)*bd.Get(i_,j);
|
|
r.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=r.Get(i,i_)*pt.Get(i_,j);
|
|
bderrors=bderrors||MathAbs(v-a.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- Orthogonality test for Q/PT
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=q.Get(i_,i)*q.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
bderrors=bderrors||MathAbs(v-1)>threshold;
|
|
else
|
|
bderrors=bderrors||MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=CAblasF::RDotRR(n,pt,i,pt,j);
|
|
//--- check
|
|
if(i==j)
|
|
bderrors=bderrors||MathAbs(v-1)>threshold;
|
|
else
|
|
bderrors=bderrors||MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- Partial unpacking test
|
|
k=1+CMath::RandomInteger(m);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,k,r);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<k; j++)
|
|
bderrors=bderrors||MathAbs(r.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
|
|
}
|
|
k=1+CMath::RandomInteger(n);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackPT(t,m,n,taup,k,r);
|
|
//--- search errors
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
bderrors=bderrors||r.Get(i,j)-pt.Get(i,j)!=0.0;
|
|
}
|
|
//--- Multiplication test
|
|
x.Resize(MathMax(m,n),MathMax(m,n));
|
|
r.Resize(MathMax(m,n),MathMax(m,n));
|
|
r1.Resize(MathMax(m,n),MathMax(m,n));
|
|
r2.Resize(MathMax(m,n),MathMax(m,n));
|
|
//--- change values
|
|
for(i=0; i<=MathMax(m,n)-1; i++)
|
|
{
|
|
for(j=0; j<=MathMax(m,n)-1; j++)
|
|
x.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
mtsize=1+CMath::RandomInteger(MathMax(m,n));
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,m,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,false,r1,0,mtsize-1,0,m-1);
|
|
RMatrixMakeACopy(x,mtsize,m,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,false);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,m)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,m,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,true,r1,0,mtsize-1,0,m-1);
|
|
RMatrixMakeACopy(x,mtsize,m,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,true);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,m)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,m,mtsize,r);
|
|
InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,false,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,m,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,false);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,m,mtsize)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,m,mtsize,r);
|
|
InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,true,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,m,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,true);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,m,mtsize)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,n,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,true,r1,0,mtsize-1,0,n-1);
|
|
RMatrixMakeACopy(x,mtsize,n,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,false);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,n)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,n,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,false,r1,0,mtsize-1,0,n-1);
|
|
RMatrixMakeACopy(x,mtsize,n,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,true);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,n)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,n,mtsize,r);
|
|
InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,true,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,n,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,false);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,n,mtsize)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,n,mtsize,r);
|
|
InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,false,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,n,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,true);
|
|
//--- search errors
|
|
bderrors=bderrors||RMatrixDiff(r1,r2,n,mtsize)>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestRHessProblem(CMatrixDouble &a,const int n,
|
|
const double threshold,bool &hesserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double tau[];
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
CMatrixDouble h;
|
|
CMatrixDouble q;
|
|
CMatrixDouble t1;
|
|
CMatrixDouble t2;
|
|
//--- function call
|
|
RMatrixMakeACopy(a,n,n,b);
|
|
//--- Decomposition
|
|
COrtFac::RMatrixHessenberg(b,n,tau);
|
|
COrtFac::RMatrixHessenbergUnpackQ(b,n,tau,q);
|
|
COrtFac::RMatrixHessenbergUnpackH(b,n,h);
|
|
//--- Matrix properties
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i_,i)*q.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
hesserrors=hesserrors||MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=i-2; j++)
|
|
hesserrors=hesserrors||h.Get(i,j)!=0.0;
|
|
}
|
|
//--- Decomposition error
|
|
t1.Resize(n,n);
|
|
t2.Resize(n,n);
|
|
//--- function calls
|
|
InternalMatrixMatrixMultiply(q,0,n-1,0,n-1,false,h,0,n-1,0,n-1,false,t1,0,n-1,0,n-1);
|
|
InternalMatrixMatrixMultiply(t1,0,n-1,0,n-1,false,q,0,n-1,0,n-1,true,t2,0,n-1,0,n-1);
|
|
//--- search errors
|
|
hesserrors=hesserrors||RMatrixDiff(t2,a,n,n)>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tridiagonal tester |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestRTdProblem(CMatrixDouble &a,const int n,
|
|
const double threshold,bool &tderrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
CRowDouble tau;
|
|
CRowDouble d;
|
|
CRowDouble e;
|
|
//--- create matrix
|
|
CMatrixDouble ua;
|
|
CMatrixDouble la;
|
|
CMatrixDouble t;
|
|
CMatrixDouble q;
|
|
CMatrixDouble t2;
|
|
CMatrixDouble t3;
|
|
//--- allocation
|
|
ua=a.TriU()+0;
|
|
la=a.TriL()+0;
|
|
t=matrix<double>::Zeros(n,n);
|
|
q=matrix<double>::Zeros(n,n);
|
|
t2=matrix<double>::Zeros(n,n);
|
|
t3=matrix<double>::Zeros(n,n);
|
|
//--- Test 2tridiagonal: upper
|
|
COrtFac::SMatrixTD(ua,n,true,tau,d,e);
|
|
COrtFac::SMatrixTDUnpackQ(ua,n,true,tau,q);
|
|
//--- change values
|
|
t.Diag(d);
|
|
t.Diag(e,1);
|
|
t.Diag(e,-1);
|
|
//--- calculation
|
|
t2=q.Transpose()+0;
|
|
t2=t2.MatMul(a)+0;
|
|
t3=t2.MatMul(q)+0;
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
tderrors=tderrors||MathAbs(t3.Get(i,j)-t.Get(i,j))>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i,i_)*q.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors||MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- Test 2tridiagonal: lower
|
|
COrtFac::SMatrixTD(la,n,false,tau,d,e);
|
|
COrtFac::SMatrixTDUnpackQ(la,n,false,tau,q);
|
|
//--- change values
|
|
t.Diag(d);
|
|
t.Diag(e,1);
|
|
t.Diag(e,-1);
|
|
//--- calculation
|
|
t2=q.Transpose()+0;
|
|
t2=t2.MatMul(a)+0;
|
|
t3=t2.MatMul(q)+0;
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
tderrors=tderrors||MathAbs(t3.Get(i,j)-t.Get(i,j))>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i,i_)*q.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors||MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Hermitian problem tester |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOrtFacUnit::TestCTdProblem(CMatrixComplex &a,const int n,
|
|
const double threshold,bool &tderrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
CRowComplex tau;
|
|
CRowDouble d;
|
|
CRowDouble e;
|
|
//--- create matrix
|
|
CMatrixComplex ua;
|
|
CMatrixComplex la;
|
|
CMatrixComplex t;
|
|
CMatrixComplex q;
|
|
CMatrixComplex t2;
|
|
CMatrixComplex t3;
|
|
//--- allocation
|
|
ua=a.TriU()+0;
|
|
la=a.TriL()+0;
|
|
t=matrix<complex>::Full(n,n,0);
|
|
q=matrix<complex>::Full(n,n,0);
|
|
t2=matrix<complex>::Full(n,n,0);
|
|
t3=matrix<complex>::Full(n,n,0);
|
|
//--- Test 2tridiagonal: upper
|
|
COrtFac::HMatrixTD(ua,n,true,tau,d,e);
|
|
COrtFac::HMatrixTDUnpackQ(ua,n,true,tau,q);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
t.Set(i,i,d[i]);
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
t.Set(i,i+1,e[i]);
|
|
t.Set(i+1,i,e[i]);
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=CMath::Conj(q.Get(i_,i))*a.Get(i_,j);
|
|
t2.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=t2.Get(i,i_)*q.Get(i_,j);
|
|
t3.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
tderrors=tderrors||CMath::AbsComplex(t3.Get(i,j)-t.Get(i,j))>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i,i_)*CMath::Conj(q.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors||CMath::AbsComplex(v)>threshold;
|
|
}
|
|
}
|
|
//--- Test 2tridiagonal: lower
|
|
COrtFac::HMatrixTD(la,n,false,tau,d,e);
|
|
COrtFac::HMatrixTDUnpackQ(la,n,false,tau,q);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
t.Set(i,j,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
t.Set(i,i,d[i]);
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
t.Set(i,i+1,e[i]);
|
|
t.Set(i+1,i,e[i]);
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=CMath::Conj(q.Get(i_,i))*a.Get(i_,j);
|
|
t2.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=t2.Get(i,i_)*q.Get(i_,j);
|
|
t3.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
tderrors=tderrors||CMath::AbsComplex(t3.Get(i,j)-t.Get(i,j))>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=q.Get(i,i_)*CMath::Conj(q.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors||CMath::AbsComplex(v)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CEigenVDetect |
|
|
//+------------------------------------------------------------------+
|
|
class CTestEVDUnit
|
|
{
|
|
public:
|
|
static bool TestEVD(const bool silent);
|
|
|
|
private:
|
|
static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
|
|
static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity);
|
|
static void RMatrixSymmetricSplit(CMatrixDouble &a,const int n,CMatrixDouble &al,CMatrixDouble &au);
|
|
static void CMatrixHermitianSplit(CMatrixComplex &a,const int n,CMatrixComplex &al,CMatrixComplex &au);
|
|
static void Unset2D(CMatrixDouble &a);
|
|
static void CUnset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void CUnset1D(complex &a[]);
|
|
static double TdTestProduct(double &d[],double &e[],const int n,CMatrixDouble &z,double &lambdav[]);
|
|
static double TestProduct(CMatrixDouble &a,const int n,CMatrixDouble &z,double &lambdav[]);
|
|
static double TestOrt(CMatrixDouble &z,const int n);
|
|
static double TestCProduct(CMatrixComplex &a,const int n,CMatrixComplex &z,double &lambdav[]);
|
|
static double TestCOrt(CMatrixComplex &z,const int n);
|
|
static void TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,CMatrixDouble &au,const int n,const double threshold,bool &serrors,int &failc,int &runs);
|
|
static void TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,CMatrixComplex &au,const int n,const double threshold,bool &herrors,int &failc,int &runs);
|
|
static void TestSEVDBiProblem(CMatrixDouble &afull,CMatrixDouble &al,CMatrixDouble &au,const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs);
|
|
static void TestHEVDBiProblem(CMatrixComplex &afull,CMatrixComplex &al,CMatrixComplex &au,const int n,const bool distvals,const double threshold,bool &herrors,int &failc,int &runs);
|
|
static void TestTdEVDProblem(double &d[],double &e[],const int n,const double threshold,bool &tderrors,int &failc,int &runs);
|
|
static void TestTdEVDBiProblem(double &d[],double &e[],const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs);
|
|
static void TestNSEVDProblem(CMatrixDouble &a,const int n,const double threshold,bool &nserrors,int &failc,int &runs);
|
|
static void TestEVDSet(const int n,const double threshold,double bithreshold,int &failc,int &runs,bool &nserrors,bool &serrors,bool &herrors,bool &tderrors,bool &sbierrors,bool &hbierrors,bool &tdbierrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing symmetric EVD subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestEVDUnit::TestEVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int j=0;
|
|
int failc=0;
|
|
int runs=0;
|
|
double failthreshold=0;
|
|
double threshold=0;
|
|
double bithreshold=0;
|
|
bool waserrors;
|
|
bool nserrors;
|
|
bool serrors;
|
|
bool herrors;
|
|
bool tderrors;
|
|
bool sbierrors;
|
|
bool hbierrors;
|
|
bool tdbierrors;
|
|
bool wfailed;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
//--- initialization
|
|
failthreshold=0.005;
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
bithreshold=1.0E-6;
|
|
nserrors=false;
|
|
serrors=false;
|
|
herrors=false;
|
|
tderrors=false;
|
|
sbierrors=false;
|
|
hbierrors=false;
|
|
tdbierrors=false;
|
|
failc=0;
|
|
runs=0;
|
|
//--- Test problems
|
|
for(n=1; n<=CAblas::AblasBlockSize(); n++)
|
|
TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors);
|
|
for(j=2; j<=3; j++)
|
|
{
|
|
for(n=j*CAblas::AblasBlockSize()-1; n<=j*CAblas::AblasBlockSize()+1; n++)
|
|
TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors);
|
|
}
|
|
//--- report
|
|
wfailed=(double)failc/(double)runs>failthreshold;
|
|
waserrors=((((((nserrors||serrors)||herrors)||tderrors)||sbierrors)||hbierrors)||tdbierrors)||wfailed;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING EVD UNIT");
|
|
PrintResult("NS ERRORS",!nserrors);
|
|
PrintResult("S ERRORS",!serrors);
|
|
PrintResult("H ERRORS",!herrors);
|
|
PrintResult("TD ERRORS",!tderrors);
|
|
PrintResult("SBI ERRORS",!sbierrors);
|
|
PrintResult("HBI ERRORS",!hbierrors);
|
|
PrintResult("TDBI ERRORS",!tdbierrors);
|
|
PrintResult("FAILURE THRESHOLD",!wfailed);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copies A to AL (lower half) and AU (upper half),filling unused |
|
|
//| parts by random garbage. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::RMatrixSymmetricSplit(CMatrixDouble &a,const int n,
|
|
CMatrixDouble &al,CMatrixDouble &au)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
al.Set(i,j,2*CMath::RandomReal()-1);
|
|
al.Set(j,i,a.Get(i,j));
|
|
au.Set(i,j,a.Get(i,j));
|
|
au.Set(j,i,2*CMath::RandomReal()-1);
|
|
}
|
|
al.Set(i,i,a[i][i]);
|
|
au.Set(i,i,a[i][i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copies A to AL (lower half) and AU (upper half),filling unused |
|
|
//| parts by random garbage. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::CMatrixHermitianSplit(CMatrixComplex &a,const int n,
|
|
CMatrixComplex &al,CMatrixComplex &au)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
al.Set(i,j,2*CMath::RandomReal()-1);
|
|
al.Set(j,i,CMath::Conj(a.Get(i,j)));
|
|
au.Set(i,j,a.Get(i,j));
|
|
au.Set(j,i,2*CMath::RandomReal()-1);
|
|
}
|
|
al.Set(i,i,a[i][i]);
|
|
au.Set(i,i,a[i][i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::Unset2D(CMatrixDouble &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::CUnset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::CUnset1D(complex &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Lambda*Z' against tridiag(D,E). |
|
|
//| Returns relative error. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestEVDUnit::TdTestProduct(double &d[],double &e[],const int n,
|
|
CMatrixDouble &z,double &lambdav[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double mx=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Calculate V=A.Get(i,j),A=Z*Lambda*Z'
|
|
v=0;
|
|
for(k=0; k<n; k++)
|
|
v+=z[i][k]*lambdav[k]*z[j][k];
|
|
//--- Compare
|
|
if(MathAbs(i-j)==0)
|
|
result=MathMax(result,MathAbs(v-d[i]));
|
|
//--- check
|
|
if(MathAbs(i-j)==1)
|
|
result=MathMax(result,MathAbs(v-e[MathMin(i,j)]));
|
|
//--- check
|
|
if(MathAbs(i-j)>1)
|
|
result=MathMax(result,MathAbs(v));
|
|
}
|
|
}
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0; i<n; i++)
|
|
mx=MathMax(mx,MathAbs(d[i]));
|
|
for(i=0; i<=n-2; i++)
|
|
mx=MathMax(mx,MathAbs(e[i]));
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- return result
|
|
return(result/mx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Lambda*Z' against A |
|
|
//| Returns relative error. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestEVDUnit::TestProduct(CMatrixDouble &a,const int n,
|
|
CMatrixDouble &z,double &lambdav[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double mx=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Calculate V=A.Get(i,j),A=Z*Lambda*Z'
|
|
v=0;
|
|
for(k=0; k<n; k++)
|
|
v+=z[i][k]*lambdav[k]*z[j][k];
|
|
//--- Compare
|
|
result=MathMax(result,MathAbs(v-a.Get(i,j)));
|
|
}
|
|
}
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
mx=MathMax(mx,MathAbs(a.Get(i,j)));
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- return result
|
|
return(result/mx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Z' against diag(1...1) |
|
|
//| Returns absolute error. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestEVDUnit::TestOrt(CMatrixDouble &z,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,i)*z.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=MathMax(result,MathAbs(v));
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Lambda*Z' against A |
|
|
//| Returns relative error. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestEVDUnit::TestCProduct(CMatrixComplex &a,const int n,
|
|
CMatrixComplex &z,double &lambdav[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
double mx=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Calculate V=A.Get(i,j),A=Z*Lambda*Z'
|
|
v=0;
|
|
for(k=0; k<n; k++)
|
|
v+=z[i][k]*lambdav[k]*CMath::Conj(z[j][k]);
|
|
//--- Compare
|
|
result=MathMax(result,CMath::AbsComplex(v-a.Get(i,j)));
|
|
}
|
|
}
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
mx=MathMax(mx,CMath::AbsComplex(a.Get(i,j)));
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- return result
|
|
return(result/mx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Z' against diag(1...1) |
|
|
//| Returns absolute error. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestEVDUnit::TestCOrt(CMatrixComplex &z,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,i)*CMath::Conj(z.Get(i_,j));
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=MathMax(result,CMath::AbsComplex(v));
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests SEVD problem |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,
|
|
CMatrixDouble &au,const int n,
|
|
const double threshold,
|
|
bool &serrors,int &failc,int &runs)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
//--- Test simple EVD: values and full vectors,lower A
|
|
Unset1D(lambdaref);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(al,n,1,false,lambdaref,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
serrors=serrors||TestProduct(a,n,z,lambdaref)>threshold;
|
|
serrors=serrors||TestOrt(z,n)>threshold;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(lambdaref[i+1]<lambdaref[i])
|
|
{
|
|
serrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values and full vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(au,n,1,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
serrors=serrors||TestProduct(a,n,z,lambdav)>threshold;
|
|
serrors=serrors||TestOrt(z,n)>threshold;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(lambdav[i+1]<lambdav[i])
|
|
{
|
|
serrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values only,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(al,n,0,false,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
serrors=serrors||MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
//--- Test simple EVD: values only,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(au,n,0,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
serrors=serrors||MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests SEVD problem |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,
|
|
CMatrixComplex &au,const int n,
|
|
const double threshold,bool &herrors,
|
|
int &failc,int &runs)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixComplex z;
|
|
//--- Test simple EVD: values and full vectors,lower A
|
|
Unset1D(lambdaref);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(al,n,1,false,lambdaref,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
herrors=herrors||TestCProduct(a,n,z,lambdaref)>threshold;
|
|
herrors=herrors||TestCOrt(z,n)>threshold;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(lambdaref[i+1]<lambdaref[i])
|
|
{
|
|
herrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values and full vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(au,n,1,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
herrors=herrors||TestCProduct(a,n,z,lambdav)>threshold;
|
|
herrors=herrors||TestCOrt(z,n)>threshold;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(lambdav[i+1]<lambdav[i])
|
|
{
|
|
herrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values only,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(al,n,0,false,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
herrors=herrors||MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
//--- Test simple EVD: values only,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(au,n,0,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
herrors=herrors||MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//| DistVals - is True,when eigenvalues are distinct. Is False, |
|
|
//| when we are solving sparse task with lots of zero|
|
|
//| eigenvalues. In such cases some tests related to |
|
|
//| the eigenvectors are not performed. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestSEVDBiProblem(CMatrixDouble &afull,
|
|
CMatrixDouble &al,CMatrixDouble &au,
|
|
const int n,const bool distvals,
|
|
const double threshold,bool &serrors,
|
|
int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
double a=0;
|
|
double b=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
CMatrixDouble zref;
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble ar;
|
|
//--- allocation
|
|
ArrayResize(lambdaref,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- Reference EVD
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(afull,n,1,true,lambdaref,zref))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Select random interval boundaries.
|
|
//--- If there are non-distinct eigenvalues at the boundaries,
|
|
//--- we move indexes further until values splits. It is done to
|
|
//--- avoid situations where we can't get definite answer.
|
|
i1=CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1);
|
|
//--- calculation
|
|
while(i1>0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
|
|
break;
|
|
i1=i1-1;
|
|
}
|
|
while(i2<n-1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
|
|
break;
|
|
i2=i2+1;
|
|
}
|
|
//--- Select A,B
|
|
if(i1>0)
|
|
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
|
|
else
|
|
a=lambdaref[0]-1;
|
|
//--- check
|
|
if(i2<n-1)
|
|
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
|
|
else
|
|
b=lambdaref[n-1]+1;
|
|
//--- Test interval,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(al,n,0,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(au,n,0,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(al,n,0,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(au,n,0,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(al,n,1,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,-1*z.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test interval,vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(au,n,1,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,-1*z.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(al,n,1,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,-1*z.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(au,n,1,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,-1*z.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//| DistVals - is True,when eigenvalues are distinct. Is False, |
|
|
//| when we are solving sparse task with lots of zero|
|
|
//| eigenvalues. In such cases some tests related to |
|
|
//| the eigenvectors are not performed. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestHEVDBiProblem(CMatrixComplex &afull,
|
|
CMatrixComplex &al,
|
|
CMatrixComplex &au,const int n,
|
|
const bool distvals,const double threshold,
|
|
bool &herrors,int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
complex v=0;
|
|
double a=0;
|
|
double b=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixComplex z;
|
|
CMatrixComplex zref;
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a2;
|
|
CMatrixComplex ar;
|
|
//--- allocation
|
|
ArrayResize(lambdaref,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- Reference EVD
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(afull,n,1,true,lambdaref,zref))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Select random interval boundaries.
|
|
//--- If there are non-distinct eigenvalues at the boundaries,
|
|
//--- we move indexes further until values splits. It is done to
|
|
//--- avoid situations where we can't get definite answer.
|
|
i1=CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1);
|
|
//--- calculation
|
|
while(i1>0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
|
|
break;
|
|
i1=i1-1;
|
|
}
|
|
while(i2<n-1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
|
|
break;
|
|
i2=i2+1;
|
|
}
|
|
//--- Select A,B
|
|
if(i1>0)
|
|
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
|
|
else
|
|
a=lambdaref[0]-1;
|
|
//--- check
|
|
if(i2<n-1)
|
|
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
|
|
else
|
|
b=lambdaref[n-1]+1;
|
|
//--- Test interval,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(al,n,0,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(au,n,0,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(al,n,0,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(au,n,0,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(al,n,1,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,v*z.Get(i_,j));
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
herrors=herrors||CMath::AbsComplex(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test interval,vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(au,n,1,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,v*z.Get(i_,j));
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
herrors=herrors||CMath::AbsComplex(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(al,n,1,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,v*z.Get(i_,j));
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
herrors=herrors||CMath::AbsComplex(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(au,n,1,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,v*z.Get(i_,j));
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
herrors=herrors||CMath::AbsComplex(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestTdEVDProblem(double &d[],double &e[],
|
|
const int n,const double threshold,
|
|
bool &tderrors,int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
bool wsucc;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double ee[];
|
|
double lambda2[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
CMatrixDouble zref;
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
//--- allocation
|
|
ArrayResize(lambdav,n);
|
|
ArrayResize(lambda2,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- check
|
|
if(n>1)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(ee,n-1);
|
|
}
|
|
//--- Test simple EVD: values and full vectors
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
for(i=0; i<=n-2; i++)
|
|
ee[i]=e[i];
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambdav,ee,n,2,z);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
tderrors=tderrors||TdTestProduct(d,e,n,z,lambdav)>threshold;
|
|
tderrors=tderrors||TestOrt(z,n)>threshold;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(lambdav[i+1]<lambdav[i])
|
|
{
|
|
tderrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
zref.Set(i,j,z.Get(i,j));
|
|
}
|
|
//--- Test values only variant
|
|
for(i=0; i<n; i++)
|
|
lambda2[i]=d[i];
|
|
for(i=0; i<=n-2; i++)
|
|
ee[i]=e[i];
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,0,z);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
tderrors=tderrors||MathAbs(lambda2[i]-lambdav[i])>threshold;
|
|
//--- Test multiplication variant
|
|
for(i=0; i<n; i++)
|
|
lambda2[i]=d[i];
|
|
for(i=0; i<=n-2; i++)
|
|
ee[i]=e[i];
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a1.Set(i,j,2*CMath::RandomReal()-1);
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,1,a1);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
tderrors=tderrors||MathAbs(lambda2[i]-lambdav[i])>threshold;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a2.Get(i,i_)*zref.Get(i_,j);
|
|
//--- next line is a bit complicated because
|
|
//--- depending on algorithm used we can get either
|
|
//--- z or -z as eigenvector. so we compare result
|
|
//--- with both A*ZRef and -A*ZRef
|
|
tderrors=tderrors||(MathAbs(v-a1.Get(i,j))>threshold && MathAbs(v+a1.Get(i,j))>threshold);
|
|
}
|
|
}
|
|
//--- Test first row variant
|
|
for(i=0; i<n; i++)
|
|
lambda2[i]=d[i];
|
|
for(i=0; i<=n-2; i++)
|
|
ee[i]=e[i];
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,3,z);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tderrors=tderrors||MathAbs(lambda2[i]-lambdav[i])>threshold;
|
|
//--- next line is a bit complicated because
|
|
//--- depending on algorithm used we can get either
|
|
//--- z or -z as eigenvector. so we compare result
|
|
//--- with both z and -z
|
|
tderrors=tderrors||(MathAbs(z[0][i]-zref[0][i])>threshold && MathAbs(z[0][i]+zref[0][i])>threshold);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//| DistVals - is True,when eigenvalues are distinct. Is False, |
|
|
//| when we are solving sparse task with lots of zero|
|
|
//| eigenvalues. In such cases some tests related to |
|
|
//| the eigenvectors are not performed. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestTdEVDBiProblem(double &d[],double &e[],
|
|
const int n,const bool distvals,
|
|
const double threshold,bool &serrors,
|
|
int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
double a=0;
|
|
double b=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
CMatrixDouble zref;
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble ar;
|
|
//--- allocation
|
|
ArrayResize(lambdaref,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- Reference EVD
|
|
ArrayResize(lambdaref,n);
|
|
for(i_=0; i_<n; i_++)
|
|
lambdaref[i_]=d[i_];
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVD(lambdaref,e,n,2,zref))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Select random interval boundaries.
|
|
//--- If there are non-distinct eigenvalues at the boundaries,
|
|
//--- we move indexes further until values splits. It is done to
|
|
//--- avoid situations where we can't get definite answer.
|
|
i1=CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1);
|
|
//--- calculation
|
|
while(i1>0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
|
|
break;
|
|
i1=i1-1;
|
|
}
|
|
while(i2<n-1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
|
|
break;
|
|
i2=i2+1;
|
|
}
|
|
//--- Test different combinations
|
|
//--- Select A,B
|
|
if(i1>0)
|
|
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
|
|
else
|
|
a=lambdaref[0]-1;
|
|
//--- check
|
|
if(i2<n-1)
|
|
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
|
|
else
|
|
b=lambdaref[n-1]+1;
|
|
//--- Test interval,no vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,0,a,b,m,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,0,i1,i2,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a1.Set(i,j,2*CMath::RandomReal()-1);
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,1,a,b,m,a1))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- allocation
|
|
ar.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a2.Get(i,i_)*zref[i_][i1+j];
|
|
ar.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a1.Get(i_,j)*ar.Get(i_,j);
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
ar.Set(i_,j,-1*ar.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(a1.Get(i,j)-ar.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a1.Set(i,j,2*CMath::RandomReal()-1);
|
|
a2.Set(i,j,a1.Get(i,j));
|
|
}
|
|
}
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,1,i1,i2,a1))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- allocation
|
|
ar.Resize(n,m);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a2.Get(i,i_)*zref[i_][i1+j];
|
|
ar.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a1.Get(i_,j)*ar.Get(i_,j);
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
ar.Set(i_,j,-1*ar.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(a1.Get(i,j)-ar.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//--- Test interval,do not transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
z.Resize(1,1);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,2,a,b,m,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,-1*z.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,do not transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0; i<n; i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
z.Resize(1,1);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,2,i1,i2,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0; k<m; k++)
|
|
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=z.Get(i_,j)*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
z.Set(i_,j,-1*z.Get(i_,j));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Non-symmetric problem |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestNSEVDProblem(CMatrixDouble &a,const int n,
|
|
const double threshold,
|
|
bool &nserrors,int &failc,
|
|
int &runs)
|
|
{
|
|
//--- create variables
|
|
double mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int vjob=0;
|
|
bool needl;
|
|
bool needr;
|
|
double curwr=0;
|
|
double curwi=0;
|
|
double vt=0;
|
|
double tmp=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double wr0[];
|
|
double wi0[];
|
|
double wr1[];
|
|
double wi1[];
|
|
double wr0s[];
|
|
double wi0s[];
|
|
double wr1s[];
|
|
double wi1s[];
|
|
double vec1r[];
|
|
double vec1i[];
|
|
double vec2r[];
|
|
double vec2i[];
|
|
double vec3r[];
|
|
double vec3i[];
|
|
//--- create matrix
|
|
CMatrixDouble vl;
|
|
CMatrixDouble vr;
|
|
//--- allocation
|
|
ArrayResize(vec1r,n);
|
|
ArrayResize(vec2r,n);
|
|
ArrayResize(vec3r,n);
|
|
ArrayResize(vec1i,n);
|
|
ArrayResize(vec2i,n);
|
|
ArrayResize(vec3i,n);
|
|
ArrayResize(wr0s,n);
|
|
ArrayResize(wr1s,n);
|
|
ArrayResize(wi0s,n);
|
|
ArrayResize(wi1s,n);
|
|
//--- initialization
|
|
mx=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(a.Get(i,j))>mx)
|
|
mx=MathAbs(a.Get(i,j));
|
|
}
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- Load values-only
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::RMatrixEVD(a,n,0,wr0,wi0,vl,vr))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Test different jobs
|
|
for(vjob=1; vjob<=3; vjob++)
|
|
{
|
|
needr=vjob==1||vjob==3;
|
|
needl=vjob==2||vjob==3;
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::RMatrixEVD(a,n,vjob,wr1,wi1,vl,vr))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Test values:
|
|
//--- 1. sort by real part
|
|
//--- 2. test
|
|
for(i_=0; i_<n; i_++)
|
|
wr0s[i_]=wr0[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
wi0s[i_]=wi0[i_];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=n-2-i; j++)
|
|
{
|
|
//--- check
|
|
if(wr0s[j]>wr0s[j+1])
|
|
{
|
|
tmp=wr0s[j];
|
|
wr0s[j]=wr0s[j+1];
|
|
wr0s[j+1]=tmp;
|
|
tmp=wi0s[j];
|
|
wi0s[j]=wi0s[j+1];
|
|
wi0s[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
//--- copy
|
|
for(i_=0; i_<n; i_++)
|
|
wr1s[i_]=wr1[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
wi1s[i_]=wi1[i_];
|
|
//--- swap
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=n-2-i; j++)
|
|
{
|
|
//--- check
|
|
if(wr1s[j]>wr1s[j+1])
|
|
{
|
|
tmp=wr1s[j];
|
|
wr1s[j]=wr1s[j+1];
|
|
wr1s[j+1]=tmp;
|
|
tmp=wi1s[j];
|
|
wi1s[j]=wi1s[j+1];
|
|
wi1s[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(nserrors,MathAbs(wr0s[i]-wr1s[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));;
|
|
CAp::SetErrorFlag(nserrors,MathAbs(wi0s[i]-wi1s[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));;
|
|
}
|
|
//--- Test right vectors
|
|
if(needr)
|
|
{
|
|
k=0;
|
|
//--- calculation
|
|
while(k<=n-1)
|
|
{
|
|
//--- check
|
|
if(wi1[k]==0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
vec1r[i_]=vr[i_][k];
|
|
for(i=0; i<n; i++)
|
|
vec1i[i]=0;
|
|
curwr=wr1[k];
|
|
curwi=0;
|
|
}
|
|
//--- check
|
|
if(wi1[k]>0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
vec1r[i_]=vr[i_][k];
|
|
for(i_=0; i_<n; i_++)
|
|
vec1i[i_]=vr[i_][k+1];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- check
|
|
if(wi1[k]<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
vec1r[i_]=vr[i_][k-1];
|
|
for(i_=0; i_<n; i_++)
|
|
vec1i[i_]=-vr[i_][k];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=a.Get(i,i_)*vec1r[i_];
|
|
vec2r[i]=vt;
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=a.Get(i,i_)*vec1i[i_];
|
|
vec2i[i]=vt;
|
|
}
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
vec3r[i_]=curwr*vec1r[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
vec3r[i_]=vec3r[i_]-curwi*vec1i[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
vec3i[i_]=curwi*vec1r[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
vec3i[i_]=vec3i[i_]+curwr*vec1i[i_];
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(nserrors,MathAbs(vec2r[i]-vec3r[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
|
|
CAp::SetErrorFlag(nserrors,MathAbs(vec2i[i]-vec3i[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
|
|
}
|
|
k++;
|
|
}
|
|
}
|
|
//--- Test left vectors
|
|
if(needl)
|
|
{
|
|
k=0;
|
|
//--- calculation
|
|
while(k<=n-1)
|
|
{
|
|
//--- check
|
|
if(wi1[k]==0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
vec1r[i_]=vl[i_][k];
|
|
for(i=0; i<n; i++)
|
|
vec1i[i]=0;
|
|
curwr=wr1[k];
|
|
curwi=0;
|
|
}
|
|
//--- check
|
|
if(wi1[k]>0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
vec1r[i_]=vl[i_][k];
|
|
for(i_=0; i_<n; i_++)
|
|
vec1i[i_]=vl[i_][k+1];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- check
|
|
if(wi1[k]<0.0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
vec1r[i_]=vl[i_][k-1];
|
|
for(i_=0; i_<n; i_++)
|
|
vec1i[i_]=-vl[i_][k];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=vec1r[i_]*a.Get(i_,j);
|
|
vec2r[j]=vt;
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=vec1i[i_]*a.Get(i_,j);
|
|
vec2i[j]=-vt;
|
|
}
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
vec3r[i_]=curwr*vec1r[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
vec3r[i_]=vec3r[i_]+curwi*vec1i[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
vec3i[i_]=curwi*vec1r[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
vec3i[i_]=vec3i[i_]-curwr*vec1i[i_];
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(nserrors,MathAbs(vec2r[i]-vec3r[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
|
|
CAp::SetErrorFlag(nserrors,MathAbs(vec2i[i]-vec3i[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
|
|
}
|
|
k++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing EVD subroutines for one N |
|
|
//| NOTES: |
|
|
//| * BIThreshold is a threshold for bisection-and-inverse-iteration |
|
|
//| subroutines. special threshold is needed because these |
|
|
//| subroutines may have much more larger error than QR-based |
|
|
//| algorithms. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestEVDUnit::TestEVDSet(const int n,const double threshold,
|
|
double bithreshold,int &failc,
|
|
int &runs,bool &nserrors,bool &serrors,
|
|
bool &herrors,bool &tderrors,bool &sbierrors,
|
|
bool &hbierrors,bool &tdbierrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int mkind=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixDouble ral;
|
|
CMatrixDouble rau;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cal;
|
|
CMatrixComplex cau;
|
|
//--- Test symmetric problems
|
|
ra.Resize(n,n);
|
|
ral.Resize(n,n);
|
|
rau.Resize(n,n);
|
|
ca.Resize(n,n);
|
|
cal.Resize(n,n);
|
|
cau.Resize(n,n);
|
|
//--- Zero matrices
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,0);
|
|
ca.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs);
|
|
TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs);
|
|
//--- Random matrix
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
ra.Set(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
ra.Set(j,i,ra.Get(i,j));
|
|
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
|
|
}
|
|
//--- change values
|
|
ra.Set(i,i,2*CMath::RandomReal()-1);
|
|
ca.Set(i,i,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
//--- Random diagonally dominant matrix with distinct eigenvalues
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
ra.Set(i,j,0.1*(2*CMath::RandomReal()-1)/n);
|
|
ca.SetRe(i,j,0.1*(2*CMath::RandomReal()-1)/n);
|
|
ca.SetIm(i,j,0.1*(2*CMath::RandomReal()-1)/n);
|
|
ra.Set(j,i,ra.Get(i,j));
|
|
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
|
|
}
|
|
//--- change values
|
|
ra.Set(i,i,0.1*(2*CMath::RandomReal()-1)+i);
|
|
ca.Set(i,i,0.1*(2*CMath::RandomReal()-1)+i);
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
TestSEVDBiProblem(ra,ral,rau,n,true,bithreshold,sbierrors,failc,runs);
|
|
TestHEVDBiProblem(ca,cal,cau,n,true,bithreshold,hbierrors,failc,runs);
|
|
//--- Sparse matrices
|
|
RMatrixFillSparseA(ra,n,n,0.995);
|
|
CMatrixFillSparseA(ca,n,n,0.995);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
ra.Set(j,i,ra.Get(i,j));
|
|
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
|
|
}
|
|
ca.SetIm(i,i,0);
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs);
|
|
TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs);
|
|
//--- testing tridiagonal problems
|
|
for(mkind=0; mkind<=7; mkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(d,n);
|
|
if(n>1)
|
|
ArrayResize(e,n-1);
|
|
//--- check
|
|
if(mkind==0)
|
|
{
|
|
//--- Zero matrix
|
|
for(i=0; i<n; i++)
|
|
d[i]=0;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=0;
|
|
}
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- Diagonal matrix
|
|
for(i=0; i<n; i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=0;
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
//--- Off-diagonal matrix
|
|
for(i=0; i<n; i++)
|
|
d[i]=0;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(mkind==3)
|
|
{
|
|
//--- Dense matrix with blocks
|
|
for(i=0; i<n; i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
//--- change values
|
|
j=1;
|
|
i=2;
|
|
while(j<=n-2)
|
|
{
|
|
e[j]=0;
|
|
j=j+i;
|
|
i=i+1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==4)
|
|
{
|
|
//--- dense matrix
|
|
for(i=0; i<n; i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(mkind==5)
|
|
{
|
|
//--- Diagonal matrix with distinct eigenvalues
|
|
for(i=0; i<n; i++)
|
|
d[i]=0.1*(2*CMath::RandomReal()-1)+i;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=0;
|
|
}
|
|
//--- check
|
|
if(mkind==6)
|
|
{
|
|
//--- Off-diagonal matrix with distinct eigenvalues
|
|
for(i=0; i<n; i++)
|
|
d[i]=0;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=0.1*(2*CMath::RandomReal()-1)+i+1;
|
|
}
|
|
//--- check
|
|
if(mkind==7)
|
|
{
|
|
//--- dense matrix with distinct eigenvalues
|
|
for(i=0; i<n; i++)
|
|
d[i]=0.1*(2*CMath::RandomReal()-1)+i+1;
|
|
for(i=0; i<=n-2; i++)
|
|
e[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
TestTdEVDProblem(d,e,n,threshold,tderrors,failc,runs);
|
|
TestTdEVDBiProblem(d,e,n,(mkind==5||mkind==6)||mkind==7,bithreshold,tdbierrors,failc,runs);
|
|
}
|
|
//--- Test non-symmetric problems
|
|
//--- Test non-symmetric problems: zero,random,sparse matrices.
|
|
ra.Resize(n,n);
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,0);
|
|
ca.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
ca.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
|
|
RMatrixFillSparseA(ra,n,n,0.995);
|
|
CMatrixFillSparseA(ca,n,n,0.995);
|
|
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMatGen |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMatGenUnit
|
|
{
|
|
public:
|
|
//--- class constant
|
|
static const int m_maxsvditerations;
|
|
//--- public method
|
|
static bool TestMatGen(const bool silent);
|
|
|
|
private:
|
|
static void Unset2D(CMatrixDouble &a);
|
|
static void Unset2DC(CMatrixComplex &a);
|
|
static bool IsSPD(CMatrixDouble &ca,const int n,const bool isupper);
|
|
static bool IsHPD(CMatrixComplex &ca,const int n);
|
|
static double SVDCond(CMatrixDouble &a,const int n);
|
|
static bool ObsoleteSVDDecomposition(CMatrixDouble &a,const int m,const int n,double &w[],CMatrixDouble &v);
|
|
static double ExtSign(const double a,const double b);
|
|
static double MyMax(const double a,const double b);
|
|
static double PyThag(const double a,const double b);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constant |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestMatGenUnit::m_maxsvditerations=60;
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMatGen |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatGenUnit::TestMatGen(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int equal_number=0;
|
|
bool waserrors;
|
|
double cond=0;
|
|
double threshold=0;
|
|
double vt=0;
|
|
complex ct=0;
|
|
double minw=0;
|
|
double maxw=0;
|
|
bool serr;
|
|
bool herr;
|
|
bool spderr;
|
|
bool hpderr;
|
|
bool rerr;
|
|
bool cerr;
|
|
int i_=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble u;
|
|
CMatrixDouble v;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cb;
|
|
CMatrixDouble r1;
|
|
CMatrixDouble r2;
|
|
CMatrixComplex c1;
|
|
CMatrixComplex c2;
|
|
//--- initialization
|
|
rerr=false;
|
|
cerr=false;
|
|
serr=false;
|
|
herr=false;
|
|
spderr=false;
|
|
hpderr=false;
|
|
waserrors=false;
|
|
maxn=20;
|
|
passcount=15;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Testing orthogonal
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- allocation
|
|
r1.Resize(n,2*n);
|
|
r2.Resize(2*n,n);
|
|
c1.Resize(n,2*n);
|
|
c2.Resize(2*n,n);
|
|
//--- Random orthogonal,real
|
|
Unset2D(a);
|
|
Unset2D(b);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonal(n,a);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonal(n,b);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- orthogonality test
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=a.Get(i,i_)*a.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr||MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr||MathAbs(vt)>threshold;
|
|
//--- change value
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=b.Get(i,i_)*b.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr||MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr||MathAbs(vt)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
rerr=rerr||a.Get(i,j)==b.Get(i,j);
|
|
}
|
|
}
|
|
//--- Random orthogonal,complex
|
|
Unset2DC(ca);
|
|
Unset2DC(cb);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonal(n,ca);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonal(n,cb);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- orthogonality test
|
|
ct=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
ct+=ca.Get(i,i_)*CMath::Conj(ca.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr||CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr||CMath::AbsComplex(ct)>threshold;
|
|
//--- change value
|
|
ct=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
ct+=cb.Get(i,i_)*CMath::Conj(cb.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr||CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr||CMath::AbsComplex(ct)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
cerr=cerr||ca.Get(i,j)==cb.Get(i,j);
|
|
}
|
|
}
|
|
//--- From the right real tests:
|
|
//--- 1. E*Q is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices)
|
|
Unset2D(a);
|
|
Unset2D(b);
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,0);
|
|
b.Set(i,j,0);
|
|
}
|
|
a.Set(i,i,1);
|
|
b.Set(i,i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(a,n,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(b,n,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- orthogonality test
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=a.Get(i,i_)*a.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr||MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr||MathAbs(vt)>threshold;
|
|
//--- change value
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=b.Get(i,i_)*b.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr||MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr||MathAbs(vt)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
rerr=rerr||a.Get(i,j)==b.Get(i,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
r2.Set(i,j,2*CMath::RandomReal()-1);
|
|
r2.Set(i+n,j,r2.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(r2,2*n,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rerr=rerr||MathAbs(r2[i+n][j]-r2.Get(i,j))>threshold;
|
|
}
|
|
//--- From the left real tests:
|
|
//--- 1. Q*E is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices)
|
|
Unset2D(a);
|
|
Unset2D(b);
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,0);
|
|
b.Set(i,j,0);
|
|
}
|
|
a.Set(i,i,1);
|
|
b.Set(i,i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(a,n,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(b,n,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- orthogonality test
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=a.Get(i,i_)*a.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr||MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr||MathAbs(vt)>threshold;
|
|
//--- change value
|
|
vt=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
vt+=b.Get(i,i_)*b.Get(j,i_);
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr||MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr||MathAbs(vt)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
rerr=rerr||a.Get(i,j)==b.Get(i,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
r1.Set(i,j,2*CMath::RandomReal()-1);
|
|
r1.Set(i,j+n,r1.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(r1,n,2*n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rerr=rerr||MathAbs(r1.Get(i,j)-r1[i][j+n])>threshold;
|
|
}
|
|
//--- From the right complex tests:
|
|
//--- 1. E*Q is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices)
|
|
Unset2DC(ca);
|
|
Unset2DC(cb);
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
cb.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ca.Set(i,j,0);
|
|
cb.Set(i,j,0);
|
|
}
|
|
ca.Set(i,i,1);
|
|
cb.Set(i,i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,n,n);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(cb,n,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- orthogonality test
|
|
ct=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
ct+=ca.Get(i,i_)*CMath::Conj(ca.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr||CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr||CMath::AbsComplex(ct)>threshold;
|
|
//--- change value
|
|
ct=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
ct+=cb.Get(i,i_)*CMath::Conj(cb.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr||CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr||CMath::AbsComplex(ct)>threshold;
|
|
//--- test for difference in A and B
|
|
cerr=cerr||ca.Get(i,j)==cb.Get(i,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c2.Set(i,j,2*CMath::RandomReal()-1);
|
|
c2.Set(i+n,j,c2.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(c2,2*n,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
cerr=cerr||CMath::AbsComplex(c2[i+n][j]-c2.Get(i,j))>threshold;
|
|
}
|
|
//--- From the left complex tests:
|
|
//--- 1. Q*E is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices)
|
|
Unset2DC(ca);
|
|
Unset2DC(cb);
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
cb.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ca.Set(i,j,0);
|
|
cb.Set(i,j,0);
|
|
}
|
|
ca.Set(i,i,1);
|
|
cb.Set(i,i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,n,n);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(cb,n,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- orthogonality test
|
|
ct=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
ct+=ca.Get(i,i_)*CMath::Conj(ca.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr||CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr||CMath::AbsComplex(ct)>threshold;
|
|
//--- change value
|
|
ct=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
ct+=cb.Get(i,i_)*CMath::Conj(cb.Get(j,i_));
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr||CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr||CMath::AbsComplex(ct)>threshold;
|
|
//--- test for difference in A and B
|
|
cerr=cerr||ca.Get(i,j)==cb.Get(i,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c1.Set(i,j,2*CMath::RandomReal()-1);
|
|
c1.Set(i,j+n,c1.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(c1,n,2*n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
cerr=cerr||CMath::AbsComplex(c1.Get(i,j)-c1[i][j+n])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- Testing GCond
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- real test
|
|
Unset2D(a);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::RMatrixRndCond(n,cond,a);
|
|
//--- allocation
|
|
b.Resize(n+1,n+1);
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
b.Set(i,j,a[i-1][j-1]);
|
|
}
|
|
//--- check
|
|
if(ObsoleteSVDDecomposition(b,n,n,w,v))
|
|
{
|
|
maxw=w[1];
|
|
minw=w[1];
|
|
for(i=2; i<=n; i++)
|
|
{
|
|
//--- check
|
|
if(w[i]>maxw)
|
|
maxw=w[i];
|
|
//--- check
|
|
if(w[i]<minw)
|
|
minw=w[i];
|
|
}
|
|
vt=maxw/minw/cond;
|
|
//--- check
|
|
if(MathAbs(MathLog(vt))>MathLog(1+threshold))
|
|
rerr=true;
|
|
}
|
|
}
|
|
}
|
|
//--- Symmetric/SPD
|
|
//--- N=2 .. 30
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- SPD matrices
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate A
|
|
Unset2D(a);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::SPDMatrixRndCond(n,cond,a);
|
|
//--- test condition number
|
|
spderr=spderr||SVDCond(a,n)/cond-1>threshold;
|
|
//--- test SPD
|
|
spderr=spderr||!IsSPD(a,n,true);
|
|
//--- test that A is symmetic
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
spderr=spderr||MathAbs(a.Get(i,j)-a[j][i])>threshold;
|
|
}
|
|
//--- test for difference between A and B (subsequent matrix)
|
|
Unset2D(b);
|
|
//--- function call
|
|
CMatGen::SPDMatrixRndCond(n,cond,b);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(a.Get(i,j)==b.Get(i,j))
|
|
equal_number++;
|
|
}
|
|
}
|
|
if(equal_number>2)
|
|
spderr=true;
|
|
}
|
|
}
|
|
//--- HPD matrices
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate A
|
|
Unset2DC(ca);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::HPDMatrixRndCond(n,cond,ca);
|
|
//--- test HPD
|
|
hpderr=hpderr||!IsHPD(ca,n);
|
|
//--- test that A is Hermitian
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
hpderr=hpderr||CMath::AbsComplex(ca.Get(i,j)-CMath::Conj(ca[j][i]))>threshold;
|
|
}
|
|
//--- test for difference between A and B (subsequent matrix)
|
|
Unset2DC(cb);
|
|
//--- function call
|
|
CMatGen::HPDMatrixRndCond(n,cond,cb);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
hpderr=hpderr||ca.Get(i,j)==cb.Get(i,j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Symmetric matrices
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- test condition number
|
|
Unset2D(a);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::SMatrixRndCond(n,cond,a);
|
|
serr=serr||SVDCond(a,n)/cond-1>threshold;
|
|
//--- test for difference between A and B
|
|
Unset2D(b);
|
|
//--- function call
|
|
CMatGen::SMatrixRndCond(n,cond,b);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
serr=serr||a.Get(i,j)==b.Get(i,j);
|
|
}
|
|
}
|
|
}
|
|
//--- Hermitian matrices
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate A
|
|
Unset2DC(ca);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::HMatrixRndCond(n,cond,ca);
|
|
//--- test that A is Hermitian
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
herr=herr||CMath::AbsComplex(ca.Get(i,j)-CMath::Conj(ca[j][i]))>threshold;
|
|
}
|
|
//--- test for difference between A and B (subsequent matrix)
|
|
Unset2DC(cb);
|
|
//--- function call
|
|
CMatGen::HMatrixRndCond(n,cond,cb);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
herr=herr||ca.Get(i,j)==cb.Get(i,j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((((rerr||cerr)||serr)||spderr)||herr)||hpderr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MATRIX GENERATOR");
|
|
PrintResult("REAL TEST",!rerr);
|
|
PrintResult("COMPLEX TEST",!cerr);
|
|
PrintResult("SYMMETRIC TEST",!serr);
|
|
PrintResult("HERMITIAN TEST",!herr);
|
|
PrintResult("SPD TEST",!spderr);
|
|
PrintResult("HPD TEST",!hpderr);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatGenUnit::Unset2D(CMatrixDouble &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatGenUnit::Unset2DC(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test whether matrix is SPD |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatGenUnit::IsSPD(CMatrixDouble &ca,const int n,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double ajj=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- copy
|
|
a=ca;
|
|
//--- Test the input parameters.
|
|
if(!CAp::Assert(n>=0,"Error in SMatrixCholesky: incorrect function arguments"))
|
|
return(false);
|
|
//--- Quick return if possible
|
|
result=true;
|
|
if(n<=0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute the Cholesky factorization A=U'*U.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Compute U(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=0; i_<j; i_++)
|
|
v+=a.Get(i_,j)*a.Get(i_,j);
|
|
ajj=a[j][j]-v;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
ajj=MathSqrt(ajj);
|
|
a.Set(j,j,ajj);
|
|
//--- Compute elements J+1:N of row J.
|
|
if(j<n-1)
|
|
{
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<j; i_++)
|
|
v+=a.Get(i_,i)*a.Get(i_,j);
|
|
a.Set(j,i,a[j][i]-v);
|
|
}
|
|
v=1/ajj;
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(j,i_,v*a.Get(j,i_));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the Cholesky factorization A=L*L'.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Compute L(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=0; i_<j; i_++)
|
|
v+=a.Get(j,i_)*a.Get(j,i_);
|
|
ajj=a[j][j]-v;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
a.Set(j,j,ajj);
|
|
//--- Compute elements J+1:N of column J.
|
|
if(j<n-1)
|
|
{
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<j; i_++)
|
|
v+=a.Get(i,i_)*a.Get(j,i_);
|
|
a.Set(i,j,a.Get(i,j)-v);
|
|
}
|
|
//--- change values
|
|
v=1/ajj;
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(i_,j,v*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether A is HPD |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatGenUnit::IsHPD(CMatrixComplex &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int j=0;
|
|
double ajj=0;
|
|
complex v=0;
|
|
double r=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex t[];
|
|
complex t2[];
|
|
complex t3[];
|
|
//--- create matrix
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a;
|
|
//--- copy
|
|
a=ca;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
ArrayResize(t2,n);
|
|
ArrayResize(t3,n);
|
|
//--- initialization
|
|
result=true;
|
|
//--- Compute the Cholesky factorization A=U'*U.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Compute U(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=0; i_<j; i_++)
|
|
v+=CMath::Conj(a.Get(i_,j))*a.Get(i_,j);
|
|
ajj=(a[j][j]-v).real;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
a.Set(j,j,ajj);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
a.Set(j,j,ajj);
|
|
//--- Compute elements J+1:N-1 of row J.
|
|
if(j<n-1)
|
|
{
|
|
for(i_=0; i_<j; i_++)
|
|
t2[i_]=CMath::Conj(a.Get(i_,j));
|
|
for(i_=j+1; i_<n; i_++)
|
|
t3[i_]=a.Get(j,i_);
|
|
//--- calculation
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<j; i_++)
|
|
v+=a.Get(i_,i)*t2[i_];
|
|
t3[i]=t3[i]-v;
|
|
}
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(j,i_,t3[i_]);
|
|
//--- change values
|
|
r=1/ajj;
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(j,i_,a.Get(j,i_)*r);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SVD condition number |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMatGenUnit::SVDCond(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
double minw=0;
|
|
double maxw=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble v;
|
|
//--- allocation
|
|
a1.Resize(n+1,n+1);
|
|
//--- change values
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
a1.Set(i,j,a[i-1][j-1]);
|
|
}
|
|
//--- check
|
|
if(!ObsoleteSVDDecomposition(a1,n,n,w,v))
|
|
{
|
|
//--- return result
|
|
return(0);
|
|
}
|
|
//--- change values
|
|
minw=w[1];
|
|
maxw=w[1];
|
|
for(i=2; i<=n; i++)
|
|
{
|
|
//--- check
|
|
if(w[i]<minw)
|
|
minw=w[i];
|
|
//--- check
|
|
if(w[i]>maxw)
|
|
maxw=w[i];
|
|
}
|
|
//--- return result
|
|
return(maxw/minw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatGenUnit::ObsoleteSVDDecomposition(CMatrixDouble &a,
|
|
const int m,
|
|
const int n,
|
|
double &w[],
|
|
CMatrixDouble &v)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int nm=0;
|
|
int minmn=0;
|
|
int l=0;
|
|
int k=0;
|
|
int j=0;
|
|
int jj=0;
|
|
int its=0;
|
|
int i=0;
|
|
double z=0;
|
|
double y=0;
|
|
double x=0;
|
|
double vscale=0;
|
|
double s=0;
|
|
double h=0;
|
|
double g=0;
|
|
double f=0;
|
|
double c=0;
|
|
double anorm=0;
|
|
bool flag;
|
|
//--- create array
|
|
double rv1[];
|
|
//--- allocation
|
|
ArrayResize(rv1,n+1);
|
|
ArrayResize(w,n+1);
|
|
v.Resize(n+1,n+1);
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(m<n)
|
|
minmn=m;
|
|
else
|
|
minmn=n;
|
|
//--- initialization
|
|
g=0.0;
|
|
vscale=0.0;
|
|
anorm=0.0;
|
|
//--- calculation
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
l=i+1;
|
|
rv1[i]=vscale*g;
|
|
g=0;
|
|
s=0;
|
|
vscale=0;
|
|
//--- check
|
|
if(i<=m)
|
|
{
|
|
for(k=i; k<=m; k++)
|
|
vscale=vscale+MathAbs(a[k][i]);
|
|
//--- check
|
|
if(vscale!=0.0)
|
|
{
|
|
for(k=i; k<=m; k++)
|
|
{
|
|
a.Set(k,i,a[k][i]/vscale);
|
|
s=s+a[k][i]*a[k][i];
|
|
}
|
|
//--- calculation
|
|
f=a[i][i];
|
|
g=-ExtSign(MathSqrt(s),f);
|
|
h=f*g-s;
|
|
a.Set(i,i,f-g);
|
|
//--- check
|
|
if(i!=n)
|
|
{
|
|
for(j=l; j<=n; j++)
|
|
{
|
|
s=0.0;
|
|
for(k=i; k<=m; k++)
|
|
s=s+a[k][i]*a[k][j];
|
|
//--- calculation
|
|
f=s/h;
|
|
for(k=i; k<=m; k++)
|
|
a.Set(k,j,a[k][j]+f*a[k][i]);
|
|
}
|
|
}
|
|
for(k=i; k<=m; k++)
|
|
a.Set(k,i,vscale*a[k][i]);
|
|
}
|
|
}
|
|
//--- change values
|
|
w[i]=vscale*g;
|
|
g=0.0;
|
|
s=0.0;
|
|
vscale=0.0;
|
|
//--- check
|
|
if(i<=m && i!=n)
|
|
{
|
|
for(k=l; k<=n; k++)
|
|
vscale=vscale+MathAbs(a[i][k]);
|
|
//--- check
|
|
if(vscale!=0.0)
|
|
{
|
|
for(k=l; k<=n; k++)
|
|
{
|
|
a.Set(i,k,a[i][k]/vscale);
|
|
s=s+a[i][k]*a[i][k];
|
|
}
|
|
//--- calculation
|
|
f=a[i][l];
|
|
g=-ExtSign(MathSqrt(s),f);
|
|
h=f*g-s;
|
|
a.Set(i,l,f-g);
|
|
for(k=l; k<=n; k++)
|
|
rv1[k]=a[i][k]/h;
|
|
//--- check
|
|
if(i!=m)
|
|
{
|
|
//--- calculation
|
|
for(j=l; j<=m; j++)
|
|
{
|
|
s=0.0;
|
|
for(k=l; k<=n; k++)
|
|
s=s+a[j][k]*a[i][k];
|
|
for(k=l; k<=n; k++)
|
|
a.Set(j,k,a[j][k]+s*rv1[k]);
|
|
}
|
|
}
|
|
for(k=l; k<=n; k++)
|
|
a.Set(i,k,vscale*a[i][k]);
|
|
}
|
|
}
|
|
//--- change value
|
|
anorm=MyMax(anorm,MathAbs(w[i])+MathAbs(rv1[i]));
|
|
}
|
|
for(i=n; i>=1; i--)
|
|
{
|
|
//--- check
|
|
if(i<n)
|
|
{
|
|
//--- check
|
|
if(g!=0.0)
|
|
{
|
|
for(j=l; j<=n; j++)
|
|
v.Set(j,i,a.Get(i,j)/a[i][l]/g);
|
|
//--- calculation
|
|
for(j=l; j<=n; j++)
|
|
{
|
|
s=0.0;
|
|
for(k=l; k<=n; k++)
|
|
s=s+a[i][k]*v[k][j];
|
|
for(k=l; k<=n; k++)
|
|
v.Set(k,j,v[k][j]+s*v[k][i]);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=l; j<=n; j++)
|
|
{
|
|
v.Set(i,j,0.0);
|
|
v.Set(j,i,0.0);
|
|
}
|
|
}
|
|
//--- change values
|
|
v.Set(i,i,1.0);
|
|
g=rv1[i];
|
|
l=i;
|
|
}
|
|
//--- calculation
|
|
for(i=minmn; i>=1; i--)
|
|
{
|
|
l=i+1;
|
|
g=w[i];
|
|
//--- check
|
|
if(i<n)
|
|
{
|
|
for(j=l; j<=n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- check
|
|
if(g!=0.0)
|
|
{
|
|
g=1.0/g;
|
|
//--- check
|
|
if(i!=n)
|
|
{
|
|
//--- calculation
|
|
for(j=l; j<=n; j++)
|
|
{
|
|
s=0.0;
|
|
for(k=l; k<=m; k++)
|
|
s=s+a[k][i]*a[k][j];
|
|
//--- change values
|
|
f=s/a[i][i]*g;
|
|
for(k=i; k<=m; k++)
|
|
a.Set(k,j,a[k][j]+f*a[k][i]);
|
|
}
|
|
}
|
|
for(j=i; j<=m; j++)
|
|
a.Set(j,i,a[j][i]*g);
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=i; j<=m; j++)
|
|
a.Set(j,i,0.0);
|
|
}
|
|
a.Set(i,i,a[i][i]+1.0);
|
|
}
|
|
//--- calculation
|
|
for(k=n; k>=1; k--)
|
|
{
|
|
for(its=1; its<=m_maxsvditerations; its++)
|
|
{
|
|
flag=true;
|
|
for(l=k; l>=1; l--)
|
|
{
|
|
nm=l-1;
|
|
//--- check
|
|
if(MathAbs(rv1[l])+anorm==anorm)
|
|
{
|
|
flag=false;
|
|
break;
|
|
}
|
|
//--- check
|
|
if(MathAbs(w[nm])+anorm==anorm)
|
|
break;
|
|
}
|
|
//--- check
|
|
if(flag)
|
|
{
|
|
c=0.0;
|
|
s=1.0;
|
|
//--- calculation
|
|
for(i=l; i<=k; i++)
|
|
{
|
|
f=s*rv1[i];
|
|
//--- check
|
|
if(MathAbs(f)+anorm!=anorm)
|
|
{
|
|
//--- change values
|
|
g=w[i];
|
|
h=PyThag(f,g);
|
|
w[i]=h;
|
|
h=1.0/h;
|
|
c=g*h;
|
|
s=-(f*h);
|
|
for(j=1; j<=m; j++)
|
|
{
|
|
y=a[j][nm];
|
|
z=a[j][i];
|
|
a.Set(j,nm,y*c+z*s);
|
|
a.Set(j,i,-(y*s)+z*c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
z=w[k];
|
|
//--- check
|
|
if(l==k)
|
|
{
|
|
//--- check
|
|
if(z<0.0)
|
|
{
|
|
w[k]=-z;
|
|
for(j=1; j<=n; j++)
|
|
v.Set(j,k,-v[j][k]);
|
|
}
|
|
break;
|
|
}
|
|
//--- check
|
|
if(its==m_maxsvditerations)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
x=w[l];
|
|
nm=k-1;
|
|
y=w[nm];
|
|
g=rv1[nm];
|
|
h=rv1[k];
|
|
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
|
|
g=PyThag(f,1);
|
|
f=((x-z)*(x+z)+h*(y/(f+ExtSign(g,f))-h))/x;
|
|
c=1.0;
|
|
s=1.0;
|
|
//--- calculation
|
|
for(j=l; j<=nm; j++)
|
|
{
|
|
i=j+1;
|
|
g=rv1[i];
|
|
y=w[i];
|
|
h=s*g;
|
|
g=c*g;
|
|
z=PyThag(f,h);
|
|
rv1[j]=z;
|
|
c=f/z;
|
|
s=h/z;
|
|
f=x*c+g*s;
|
|
g=-(x*s)+g*c;
|
|
h=y*s;
|
|
y=y*c;
|
|
for(jj=1; jj<=n; jj++)
|
|
{
|
|
x=v.Get(jj,j);
|
|
z=v.Get(jj,i);
|
|
v.Set(jj,j,x*c+z*s);
|
|
v.Set(jj,i,-(x*s)+z*c);
|
|
}
|
|
z=PyThag(f,h);
|
|
w[j]=z;
|
|
//--- check
|
|
if(z!=0.0)
|
|
{
|
|
z=1.0/z;
|
|
c=f*z;
|
|
s=h*z;
|
|
}
|
|
//--- calculation
|
|
f=c*g+s*y;
|
|
x=-(s*g)+c*y;
|
|
for(jj=1; jj<=m; jj++)
|
|
{
|
|
y=a.Get(jj,j);
|
|
z=a.Get(jj,i);
|
|
a.Set(jj,j,y*c+z*s);
|
|
a.Set(jj,i,-(y*s)+z*c);
|
|
}
|
|
}
|
|
//--- change values
|
|
rv1[l]=0.0;
|
|
rv1[k]=f;
|
|
w[k]=x;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMatGenUnit::ExtSign(const double a,const double b)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(b>=0.0)
|
|
result=MathAbs(a);
|
|
else
|
|
result=-MathAbs(a);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMatGenUnit::MyMax(const double a,const double b)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(a>b)
|
|
result=a;
|
|
else
|
|
result=b;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMatGenUnit::PyThag(const double a,const double b)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(MathAbs(a)<MathAbs(b))
|
|
result=MathAbs(b)*MathSqrt(1+CMath::Sqr(a/b));
|
|
else
|
|
result=MathAbs(a)*MathSqrt(1+CMath::Sqr(b/a));
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTrFac |
|
|
//+------------------------------------------------------------------+
|
|
class CTestTrFacUnit
|
|
{
|
|
public:
|
|
static bool TestTrFac(const bool silent);
|
|
|
|
private:
|
|
static void TestCLUProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &err,bool &properr);
|
|
static void TestRLUProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &err,bool &properr);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTrFac |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestTrFacUnit::TestTrFac(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int maxmn=0;
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
double vr=0;
|
|
bool waserrors;
|
|
bool spderr;
|
|
bool hpderr;
|
|
bool rerr;
|
|
bool cerr;
|
|
bool properr;
|
|
double threshold=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixDouble ral;
|
|
CMatrixDouble rau;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cal;
|
|
CMatrixComplex cau;
|
|
//--- initialization
|
|
rerr=false;
|
|
spderr=false;
|
|
cerr=false;
|
|
hpderr=false;
|
|
properr=false;
|
|
waserrors=false;
|
|
maxmn=4*CAblas::AblasBlockSize()+1;
|
|
threshold=1000*CMath::m_machineepsilon*maxmn;
|
|
//--- test LU
|
|
for(mx=1; mx<=maxmn; mx++)
|
|
{
|
|
//--- Initialize N/M,both are <=MX,
|
|
//--- at least one of them is exactly equal to MX
|
|
n=1+CMath::RandomInteger(mx);
|
|
m=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
n=mx;
|
|
else
|
|
m=mx;
|
|
//--- First,test on zero matrix
|
|
ra.Resize(m,n);
|
|
ca.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,0);
|
|
ca.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestCLUProblem(ca,m,n,threshold,cerr,properr);
|
|
TestRLUProblem(ra,m,n,threshold,rerr,properr);
|
|
//--- Second,random matrix with moderate condition number
|
|
ra.Resize(m,n);
|
|
ca.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ra.Set(i,j,0);
|
|
ca.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<=MathMin(m,n)-1; i++)
|
|
{
|
|
ra.Set(i,i,1+10*CMath::RandomReal());
|
|
ca.Set(i,i,1+10*CMath::RandomReal());
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,m,n);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,m,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(ra,m,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(ra,m,n);
|
|
//--- function calls
|
|
TestCLUProblem(ca,m,n,threshold,cerr,properr);
|
|
TestRLUProblem(ra,m,n,threshold,rerr,properr);
|
|
}
|
|
//--- Test Cholesky
|
|
for(n=1; n<=maxmn; n++)
|
|
{
|
|
//--- Load CA (HPD matrix with low condition number),
|
|
//--- CAL and CAU - its lower and upper triangles
|
|
CMatGen::HPDMatrixRndCond(n,1+50*CMath::RandomReal(),ca);
|
|
//--- allocation
|
|
cal.Resize(n,n);
|
|
cau.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
cal.Set(i,j,i);
|
|
cau.Set(i,j,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<=i; i_++)
|
|
cal.Set(i,i_,ca.Get(i,i_));
|
|
for(i_=i; i_<n; i_++)
|
|
cau.Set(i,i_,ca.Get(i,i_));
|
|
}
|
|
//--- Test HPDMatrixCholesky:
|
|
//--- 1. it must leave upper (lower) part unchanged
|
|
//--- 2. max(A-L*L^H) must be small
|
|
if(CTrFac::HPDMatrixCholesky(cal,n,false))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(j>i)
|
|
hpderr=hpderr||cal.Get(i,j)!=i;
|
|
else
|
|
{
|
|
vc=0.0;
|
|
for(i_=0; i_<=j; i_++)
|
|
vc+=cal.Get(i,i_)*CMath::Conj(cal.Get(j,i_));
|
|
//--- search errors
|
|
hpderr=hpderr||CMath::AbsComplex(ca.Get(i,j)-vc)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hpderr=true;
|
|
//--- check
|
|
if(CTrFac::HPDMatrixCholesky(cau,n,true))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
hpderr=hpderr||cau.Get(i,j)!=j;
|
|
else
|
|
{
|
|
vc=0.0;
|
|
for(i_=0; i_<=i; i_++)
|
|
vc+=CMath::Conj(cau.Get(i_,i))*cau.Get(i_,j);
|
|
//--- search errors
|
|
hpderr=hpderr||CMath::AbsComplex(ca.Get(i,j)-vc)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hpderr=true;
|
|
//--- Load RA (SPD matrix with low condition number),
|
|
//--- RAL and RAU - its lower and upper triangles
|
|
CMatGen::SPDMatrixRndCond(n,1+50*CMath::RandomReal(),ra);
|
|
//--- allocation
|
|
ral.Resize(n,n);
|
|
rau.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ral.Set(i,j,i);
|
|
rau.Set(i,j,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<=i; i_++)
|
|
ral.Set(i,i_,ra.Get(i,i_));
|
|
for(i_=i; i_<n; i_++)
|
|
rau.Set(i,i_,ra.Get(i,i_));
|
|
}
|
|
//--- Test SPDMatrixCholesky:
|
|
//--- 1. it must leave upper (lower) part unchanged
|
|
//--- 2. max(A-L*L^H) must be small
|
|
if(CTrFac::SPDMatrixCholesky(ral,n,false))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(j>i)
|
|
spderr=spderr||ral.Get(i,j)!=i;
|
|
else
|
|
{
|
|
vr=0.0;
|
|
for(i_=0; i_<=j; i_++)
|
|
vr+=ral.Get(i,i_)*ral.Get(j,i_);
|
|
//--- search errors
|
|
spderr=spderr||MathAbs(ra.Get(i,j)-vr)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
spderr=true;
|
|
//--- check
|
|
if(CTrFac::SPDMatrixCholesky(rau,n,true))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
spderr=spderr||rau.Get(i,j)!=j;
|
|
else
|
|
{
|
|
vr=0.0;
|
|
for(i_=0; i_<=i; i_++)
|
|
vr+=rau.Get(i_,i)*rau.Get(i_,j);
|
|
//--- search errors
|
|
spderr=spderr||MathAbs(ra.Get(i,j)-vr)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
spderr=true;
|
|
}
|
|
//--- report
|
|
waserrors=(((rerr||spderr)||cerr)||hpderr)||properr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING TRIANGULAR FACTORIZATIONS");
|
|
PrintResult("* REAL",!rerr);
|
|
PrintResult("* SPD",!spderr);
|
|
PrintResult("* COMPLEX",!cerr);
|
|
PrintResult("* HPD",!hpderr);
|
|
PrintResult("* OTHER PROPERTIES",!properr);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTrFacUnit::TestCLUProblem(CMatrixComplex &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &err,bool &properr)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int minmn=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex ct[];
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cl;
|
|
CMatrixComplex cu;
|
|
CMatrixComplex ca2;
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
//--- PLU test
|
|
ca.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
ca.Set(i,i_,a.Get(i,i_));
|
|
}
|
|
//--- function call
|
|
CTrFac::CMatrixPLU(ca,m,n,p);
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=m)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
for(j=0; j<=minmn-1; j++)
|
|
{
|
|
for(i=0; i<j; i++)
|
|
cl.Set(i,j,0.0);
|
|
//--- change values
|
|
cl.Set(j,j,1.0);
|
|
for(i=j+1; i<m; i++)
|
|
cl.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
cu.Set(i,j,0.0);
|
|
for(j=i; j<n; j++)
|
|
cu.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=minmn-1; i_++)
|
|
v+=cl.Get(i,i_)*cu.Get(i_,j);
|
|
ca2.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,n);
|
|
//--- change values
|
|
for(i=minmn-1; i>=0; i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
ct[i_]=ca2.Get(i,i_);
|
|
for(i_=0; i_<n; i_++)
|
|
ca2.Set(i,i_,ca2[p[i]][i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
ca2.Set(p[i],i_,ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||CMath::AbsComplex(a.Get(i,j)-ca2.Get(i,j))>threshold;
|
|
}
|
|
//--- LUP test
|
|
ca.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
ca.Set(i,i_,a.Get(i,i_));
|
|
}
|
|
//--- function call
|
|
CTrFac::CMatrixLUP(ca,m,n,p);
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=n)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
//--- change values
|
|
for(j=0; j<=minmn-1; j++)
|
|
{
|
|
for(i=0; i<j; i++)
|
|
cl.Set(i,j,0.0);
|
|
for(i=j; i<m; i++)
|
|
cl.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
cu.Set(i,j,0.0);
|
|
cu.Set(i,i,1.0);
|
|
for(j=i+1; j<n; j++)
|
|
cu.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=minmn-1; i_++)
|
|
v+=cl.Get(i,i_)*cu.Get(i_,j);
|
|
ca2.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,m);
|
|
//--- change values
|
|
for(i=minmn-1; i>=0; i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
for(i_=0; i_<m; i_++)
|
|
ct[i_]=ca2.Get(i_,i);
|
|
for(i_=0; i_<m; i_++)
|
|
ca2.Set(i_,i,ca2[i_][p[i]]);
|
|
for(i_=0; i_<m; i_++)
|
|
ca2.Set(i_,p[i],ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||CMath::AbsComplex(a.Get(i,j)-ca2.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTrFacUnit::TestRLUProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &err,bool &properr)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int minmn=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double ct[];
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixDouble ca=a;
|
|
CMatrixDouble cl;
|
|
CMatrixDouble cu;
|
|
CMatrixDouble ca2;
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
//--- PLU test
|
|
ca.Resize(m,n);
|
|
//--- function call
|
|
CTrFac::RMatrixPLU(ca,m,n,p);
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=m)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
//--- change values
|
|
for(j=0; j<=minmn-1; j++)
|
|
{
|
|
for(i=0; i<j; i++)
|
|
cl.Set(i,j,0.0);
|
|
cl.Set(j,j,1.0);
|
|
for(i=j+1; i<m; i++)
|
|
cl.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
cu.Set(i,j,0.0);
|
|
for(j=i; j<n; j++)
|
|
cu.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=minmn-1; i_++)
|
|
v+=cl.Get(i,i_)*cu.Get(i_,j);
|
|
ca2.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,n);
|
|
for(i=minmn-1; i>=0; i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
ct[i_]=ca2.Get(i,i_);
|
|
for(i_=0; i_<n; i_++)
|
|
ca2.Set(i,i_,ca2[p[i]][i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
ca2.Set(p[i],i_,ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||MathAbs(a.Get(i,j)-ca2.Get(i,j))>threshold;
|
|
}
|
|
//--- LUP test
|
|
ca.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
ca.Set(i,i_,a.Get(i,i_));
|
|
}
|
|
//--- function call
|
|
CTrFac::RMatrixLUP(ca,m,n,p);
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=n)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
//--- change values
|
|
for(j=0; j<=minmn-1; j++)
|
|
{
|
|
for(i=0; i<j; i++)
|
|
cl.Set(i,j,0.0);
|
|
for(i=j; i<m; i++)
|
|
cl.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
cu.Set(i,j,0.0);
|
|
cu.Set(i,i,1.0);
|
|
for(j=i+1; j<n; j++)
|
|
cu.Set(i,j,ca.Get(i,j));
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=minmn-1; i_++)
|
|
v+=cl.Get(i,i_)*cu.Get(i_,j);
|
|
ca2.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,m);
|
|
for(i=minmn-1; i>=0; i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
//--- change values
|
|
for(i_=0; i_<m; i_++)
|
|
ct[i_]=ca2.Get(i_,i);
|
|
for(i_=0; i_<m; i_++)
|
|
ca2.Set(i_,i,ca2[i_][p[i]]);
|
|
for(i_=0; i_<m; i_++)
|
|
ca2.Set(i_,p[i],ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||MathAbs(a.Get(i,j)-ca2.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTrLinSolve |
|
|
//+------------------------------------------------------------------+
|
|
class CTestTrLinSolveUnit
|
|
{
|
|
public:
|
|
static bool TestTrLinSolve(const bool silent);
|
|
|
|
private:
|
|
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Main unittest subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestTrLinSolveUnit::TestTrLinSolve(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxmn=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int cnts=0;
|
|
int cntu=0;
|
|
int cntt=0;
|
|
int cntm=0;
|
|
bool waserrors;
|
|
bool isupper;
|
|
bool istrans;
|
|
bool isunit;
|
|
double v=0;
|
|
double s=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double xe[];
|
|
double b[];
|
|
//--- create matrix
|
|
CMatrixDouble aeffective;
|
|
CMatrixDouble aparam;
|
|
//--- initialization
|
|
waserrors=false;
|
|
maxmn=15;
|
|
passcount=15;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Different problems
|
|
for(n=1; n<=maxmn; n++)
|
|
{
|
|
//--- allocation
|
|
aeffective.Resize(n,n);
|
|
aparam.Resize(n,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(cnts=0; cnts<=1; cnts++)
|
|
{
|
|
for(cntu=0; cntu<=1; cntu++)
|
|
{
|
|
for(cntt=0; cntt<=1; cntt++)
|
|
{
|
|
for(cntm=0; cntm<=2; cntm++)
|
|
{
|
|
isupper=cnts==0;
|
|
isunit=cntu==0;
|
|
istrans=cntt==0;
|
|
//--- Skip meaningless combinations of parameters:
|
|
//--- (matrix is singular) AND (matrix is unit diagonal)
|
|
if(cntm==2 && isunit)
|
|
continue;
|
|
//--- Clear matrices
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
aeffective.Set(i,j,0);
|
|
aparam.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- Prepare matrices
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
aeffective.Set(i,j,0.9*(2*CMath::RandomReal()-1));
|
|
aparam.Set(i,j,aeffective.Get(i,j));
|
|
}
|
|
//--- change values
|
|
aeffective.Set(i,i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
|
|
aparam.Set(i,i,aeffective[i][i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=i; j++)
|
|
{
|
|
aeffective.Set(i,j,0.9*(2*CMath::RandomReal()-1));
|
|
aparam.Set(i,j,aeffective.Get(i,j));
|
|
}
|
|
//--- change values
|
|
aeffective.Set(i,i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
|
|
aparam.Set(i,i,aeffective[i][i]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
aeffective.Set(i,i,1);
|
|
aparam.Set(i,i,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(istrans)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
aeffective.Set(j,i,aeffective.Get(i,j));
|
|
aeffective.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
aeffective.Set(i,j,aeffective[j][i]);
|
|
aeffective.Set(j,i,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Prepare task,solve,compare
|
|
for(i=0; i<n; i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=aeffective.Get(i,i_)*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- function call
|
|
CTrLinSolve::RMatrixTrSafeSolve(aparam,n,b,s,isupper,istrans,isunit);
|
|
//--- calculation
|
|
for(i_=0; i_<n; i_++)
|
|
xe[i_]=s*xe[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
xe[i_]=xe[i_]-b[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=xe[i_]*xe[i_];
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
waserrors=waserrors||v>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RMatrixTrSafeSolve");
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestTrLinSolveUnit::MakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSafeSolve |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSafeSolveUnit
|
|
{
|
|
public:
|
|
static bool TestSafeSolve(const bool silent);
|
|
|
|
private:
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Main unittest subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSafeSolveUnit::TestSafeSolve(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxmn=0;
|
|
double threshold=0;
|
|
bool rerrors;
|
|
bool cerrors;
|
|
bool waserrors;
|
|
bool isupper;
|
|
int trans=0;
|
|
bool isunit;
|
|
double scalea=0;
|
|
double growth=0;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
complex cv=0;
|
|
double rv=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex cxs[];
|
|
complex cxe[];
|
|
double rxs[];
|
|
double rxe[];
|
|
//--- create matrix
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cea;
|
|
CMatrixComplex ctmpa;
|
|
CMatrixDouble ra;
|
|
CMatrixDouble rea;
|
|
CMatrixDouble rtmpa;
|
|
//--- initialization
|
|
maxmn=30;
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
rerrors=false;
|
|
cerrors=false;
|
|
waserrors=false;
|
|
//--- Different problems: general tests
|
|
for(n=1; n<=maxmn; n++)
|
|
{
|
|
//--- test complex solver with well-conditioned matrix:
|
|
//--- 1. generate A: fill off-diagonal elements with small values,
|
|
//--- diagonal elements are filled with larger values
|
|
//--- 2. generate 'effective' A
|
|
//--- 3. prepare task (exact X is stored in CXE,right part - in CXS),
|
|
//--- solve and compare CXS and CXE
|
|
isupper=CMath::RandomReal()>0.5;
|
|
trans=CMath::RandomInteger(3);
|
|
isunit=CMath::RandomReal()>0.5;
|
|
scalea=CMath::RandomReal()+0.5;
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
{
|
|
ca.SetRe(i,j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
|
|
ca.SetIm(i,j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
|
|
}
|
|
else
|
|
{
|
|
ca.SetRe(i,j,0.2*CMath::RandomReal()-0.1);
|
|
ca.SetIm(i,j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatrixMakeACopy(ca,n,n,ctmpa);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
for(j=j1; j<=j2; j++)
|
|
ctmpa.Set(i,j,0);
|
|
//--- check
|
|
if(isunit)
|
|
ctmpa.Set(i,i,1);
|
|
}
|
|
//--- allocation
|
|
cea.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(trans==0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
cea.Set(i,i_,ctmpa.Get(i,i_)*scalea);
|
|
}
|
|
//--- check
|
|
if(trans==1)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
cea.Set(i_,i,ctmpa.Get(i,i_)*scalea);
|
|
}
|
|
//--- check
|
|
if(trans==2)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
cea.Set(i_,i,CMath::Conj(ctmpa.Get(i,i_))*scalea);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cxe,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
cxe[i].real=2*CMath::RandomReal()-1;
|
|
cxe[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cxs,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
cv=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
cv+=cea.Get(i,i_)*cxe[i_];
|
|
cxs[i]=cv;
|
|
}
|
|
//--- check
|
|
if(CSafeSolve::CMatrixScaledTrSafeSolve(ca,scalea,n,cxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber)))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
cerrors=cerrors||CMath::AbsComplex(cxs[i]-cxe[i])>threshold;
|
|
}
|
|
else
|
|
cerrors=true;
|
|
//--- same with real
|
|
isupper=CMath::RandomReal()>0.5;
|
|
trans=CMath::RandomInteger(2);
|
|
isunit=CMath::RandomReal()>0.5;
|
|
scalea=CMath::RandomReal()+0.5;
|
|
//--- allocation
|
|
ra.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
ra.Set(i,j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
|
|
else
|
|
ra.Set(i,j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
}
|
|
//--- function call
|
|
RMatrixMakeACopy(ra,n,n,rtmpa);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
for(j=j1; j<=j2; j++)
|
|
rtmpa.Set(i,j,0);
|
|
//--- check
|
|
if(isunit)
|
|
rtmpa.Set(i,i,1);
|
|
}
|
|
//--- allocation
|
|
rea.Resize(n,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(trans==0)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
rea.Set(i,i_,scalea*rtmpa.Get(i,i_));
|
|
}
|
|
//--- check
|
|
if(trans==1)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
rea.Set(i_,i,scalea*rtmpa.Get(i,i_));
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(rxe,n);
|
|
for(i=0; i<n; i++)
|
|
rxe[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rxs,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
rv=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
rv+=rea.Get(i,i_)*rxe[i_];
|
|
rxs[i]=rv;
|
|
}
|
|
//--- check
|
|
if(CSafeSolve::RMatrixScaledTrSafeSolve(ra,scalea,n,rxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber)))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
rerrors=rerrors||MathAbs(rxs[i]-rxe[i])>threshold;
|
|
}
|
|
else
|
|
rerrors=true;
|
|
}
|
|
//--- Special test with diagonal ill-conditioned matrix:
|
|
//--- * ability to solve it when resulting growth is less than threshold
|
|
//--- * ability to Stop solve when resulting growth is greater than threshold
|
|
//--- A=diag(1,1/growth)
|
|
//--- b=(1,0.5)
|
|
n=2;
|
|
growth=10;
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
ca.Set(0,0,1);
|
|
ca.Set(0,1,0);
|
|
ca.Set(1,0,0);
|
|
ca.Set(1,1,1/growth);
|
|
//--- allocation
|
|
ArrayResize(cxs,n);
|
|
//--- change values
|
|
cxs[0]=1.0;
|
|
cxs[1]=0.5;
|
|
//--- search errors
|
|
cerrors=cerrors||!CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,1.05*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0));
|
|
cerrors=cerrors||!CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,0.95*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0));
|
|
//--- allocation
|
|
ra.Resize(n,n);
|
|
//--- change values
|
|
ra.Set(0,0,1);
|
|
ra.Set(0,1,0);
|
|
ra.Set(1,0,0);
|
|
ra.Set(1,1,1/growth);
|
|
//--- allocation
|
|
ArrayResize(rxs,n);
|
|
//--- change values
|
|
rxs[0]=1.0;
|
|
rxs[1]=0.5;
|
|
//--- search errors
|
|
rerrors=rerrors||!CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,1.05*MathMax(MathAbs(rxs[1])*growth,1.0));
|
|
rerrors=rerrors||!CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,0.95*MathMax(MathAbs(rxs[1])*growth,1.0));
|
|
//--- Special test with diagonal degenerate matrix:
|
|
//--- * ability to solve it when resulting growth is less than threshold
|
|
//--- * ability to Stop solve when resulting growth is greater than threshold
|
|
//--- A=diag(1,0)
|
|
//--- b=(1,0.5)
|
|
n=2;
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
ca.Set(0,0,1);
|
|
ca.Set(0,1,0);
|
|
ca.Set(1,0,0);
|
|
ca.Set(1,1,0);
|
|
//--- allocation
|
|
ArrayResize(cxs,n);
|
|
//--- change values
|
|
cxs[0]=1.0;
|
|
cxs[1]=0.5;
|
|
//--- search errors
|
|
cerrors=cerrors||CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,MathSqrt(CMath::m_maxrealnumber));
|
|
//--- allocation
|
|
ra.Resize(n,n);
|
|
//--- change values
|
|
ra.Set(0,0,1);
|
|
ra.Set(0,1,0);
|
|
ra.Set(1,0,0);
|
|
ra.Set(1,1,0);
|
|
//--- allocation
|
|
ArrayResize(rxs,n);
|
|
//--- change values
|
|
rxs[0]=1.0;
|
|
rxs[1]=0.5;
|
|
//--- search errors
|
|
rerrors=rerrors||CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,MathSqrt(CMath::m_maxrealnumber));
|
|
//--- report
|
|
waserrors=rerrors||cerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SAFE TR SOLVER");
|
|
PrintResult("REAL",!rerrors);
|
|
PrintResult("COMPLEX",!cerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSafeSolveUnit::RMatrixMakeACopy(CMatrixDouble &a,
|
|
const int m,const int n,
|
|
CMatrixDouble &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSafeSolveUnit::CMatrixMakeACopy(CMatrixComplex &a,
|
|
const int m,const int n,
|
|
CMatrixComplex &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRCond |
|
|
//+------------------------------------------------------------------+
|
|
class CTestRCondUnit
|
|
{
|
|
public:
|
|
//--- class constants
|
|
static const double m_threshold50;
|
|
static const double m_threshold90;
|
|
//--- public method
|
|
static bool TestRCond(const bool silent);
|
|
|
|
private:
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
|
|
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
|
|
static void RMatrixGenZero(CMatrixDouble &a0,const int n);
|
|
static bool RMatrixInvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],const int n);
|
|
static bool RMatrixInvMat(CMatrixDouble &a,const int n);
|
|
static void RMatrixRefRCond(CMatrixDouble &a,const int n,double &rc1,double &rcinf);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static void CMatrixGenZero(CMatrixComplex &a0,const int n);
|
|
static bool CMatrixInvMatTr(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],const int n);
|
|
static bool CMatrixInvMat(CMatrixComplex &a,const int n);
|
|
static void CMatrixRefRCond(CMatrixComplex &a,const int n,double &rc1,double &rcinf);
|
|
static bool TestRMatrixTrRCond(const int maxn,const int passcount);
|
|
static bool TestCMatrixTrRCond(const int maxn,const int passcount);
|
|
static bool TestRMatrixRCond(const int maxn,const int passcount);
|
|
static bool TestSPDMatrixRCond(const int maxn,const int passcount);
|
|
static bool TestCMatrixRCond(const int maxn,const int passcount);
|
|
static bool TestHPDMatrixRCond(const int maxn,const int passcount);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const double CTestRCondUnit::m_threshold50=0.25;
|
|
const double CTestRCondUnit::m_threshold90=0.10;
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRCond |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestRCond(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxn=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
bool rtrerr;
|
|
bool ctrerr;
|
|
bool rerr;
|
|
bool cerr;
|
|
bool spderr;
|
|
bool hpderr;
|
|
//--- initialization
|
|
maxn=10;
|
|
passcount=100;
|
|
//--- report
|
|
rtrerr=!TestRMatrixTrRCond(maxn,passcount);
|
|
ctrerr=!TestCMatrixTrRCond(maxn,passcount);
|
|
rerr=!TestRMatrixRCond(maxn,passcount);
|
|
cerr=!TestCMatrixRCond(maxn,passcount);
|
|
spderr=!TestSPDMatrixRCond(maxn,passcount);
|
|
hpderr=!TestHPDMatrixRCond(maxn,passcount);
|
|
waserrors=((((rtrerr||ctrerr)||rerr)||cerr)||spderr)||hpderr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RCOND");
|
|
PrintResult("REAL TRIANGULAR",!rtrerr);
|
|
PrintResult("COMPLEX TRIANGULAR",!ctrerr);
|
|
PrintResult("REAL",!rerr);
|
|
PrintResult("SPD",!spderr);
|
|
PrintResult("HPD",!hpderr);
|
|
PrintResult("COMPLEX",!cerr);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate matrix with given condition number C (2-norm) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::RMatrixGenZero(CMatrixDouble &a0,const int n)
|
|
{
|
|
//--- allocation
|
|
a0.Resize(n,n);
|
|
//--- make zero
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
a0.Set(i,j,0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::RMatrixInvMatTr(CMatrixDouble &a,const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a.Set(j,j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0; i_<j; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=0; i<j; i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1; i_<j; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- calculation
|
|
for(i_=0; i_<j; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a.Set(j,j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1; i_<n; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1; i_<i; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- calculation
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- Quick return if possible
|
|
if(n==0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(work,n);
|
|
//--- Form inv(U)
|
|
if(!RMatrixInvMatTr(a,n,true,false))
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
work[i]=a.Get(i,j);
|
|
a.Set(i,j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1; i_<n; i_++)
|
|
v+=a.Get(i,i_)*work[i_];
|
|
a.Set(i,j,a.Get(i,j)-v);
|
|
}
|
|
}
|
|
}
|
|
//--- Apply column interchanges.
|
|
for(j=n-2; j>=0; j--)
|
|
{
|
|
jp=pivots[j];
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
work[i_]=a.Get(i_,j);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,j,a[i_][jp]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,jp,work[i_]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::RMatrixInvMat(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- function call
|
|
CTrFac::RMatrixLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(RMatrixInvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| reference RCond |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::RMatrixRefRCond(CMatrixDouble &a,const int n,
|
|
double &rc1,double &rcinf)
|
|
{
|
|
//--- create variables
|
|
double nrm1a=0;
|
|
double nrminfa=0;
|
|
double nrm1inva=0;
|
|
double nrminfinva=0;
|
|
double v=0;
|
|
int k=0;
|
|
int i=0;
|
|
//--- create matrix
|
|
CMatrixDouble inva;
|
|
//--- initialization
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- inv A
|
|
RMatrixMakeACopy(a,n,n,inva);
|
|
//--- check
|
|
if(!RMatrixInvMat(inva,n))
|
|
{
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- norm A
|
|
nrm1a=0;
|
|
nrminfa=0;
|
|
//--- calculation
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- change values
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=MathAbs(a[i][k]);
|
|
nrm1a=MathMax(nrm1a,v);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=MathAbs(a[k][i]);
|
|
nrminfa=MathMax(nrminfa,v);
|
|
}
|
|
//--- norm inv A
|
|
nrm1inva=0;
|
|
nrminfinva=0;
|
|
//--- calculation
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- change values
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=MathAbs(inva[i][k]);
|
|
nrm1inva=MathMax(nrm1inva,v);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=MathAbs(inva[k][i]);
|
|
nrminfinva=MathMax(nrminfinva,v);
|
|
}
|
|
//--- result
|
|
rc1=nrm1inva*nrm1a;
|
|
rcinf=nrminfinva*nrminfa;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m,
|
|
const int n,CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate matrix with given condition number C (2-norm) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::CMatrixGenZero(CMatrixComplex &a0,const int n)
|
|
{
|
|
//--- allocation
|
|
a0.Resize(n,n);
|
|
//--- copy
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
a0.Set(i,j,0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| triangular inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::CMatrixInvMatTr(CMatrixComplex &a,const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
complex ajj=0;
|
|
complex one=1;
|
|
int i_=0;
|
|
//--- create array
|
|
complex t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a.Set(j,j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0; i_<j; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
//--- calculation
|
|
for(i=0; i<j; i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1; i_<j; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
for(i_=0; i_<j; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a.Set(j,j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1; i_<n; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
//--- calculation
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1; i_<i; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex work[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- Quick return if possible
|
|
if(n==0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(work,n);
|
|
//--- Form inv(U)
|
|
if(!CMatrixInvMatTr(a,n,true,false))
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
work[i]=a.Get(i,j);
|
|
a.Set(i,j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1; i_<n; i_++)
|
|
v+=a.Get(i,i_)*work[i_];
|
|
a.Set(i,j,a.Get(i,j)-v);
|
|
}
|
|
}
|
|
}
|
|
//--- Apply column interchanges.
|
|
for(j=n-2; j>=0; j--)
|
|
{
|
|
jp=pivots[j];
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
work[i_]=a.Get(i_,j);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,j,a[i_][jp]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,jp,work[i_]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::CMatrixInvMat(CMatrixComplex &a,const int n)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- function call
|
|
CTrFac::CMatrixLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(CMatrixInvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| reference RCond |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRCondUnit::CMatrixRefRCond(CMatrixComplex &a,const int n,
|
|
double &rc1,double &rcinf)
|
|
{
|
|
//--- create variables
|
|
double nrm1a=0;
|
|
double nrminfa=0;
|
|
double nrm1inva=0;
|
|
double nrminfinva=0;
|
|
double v=0;
|
|
int k=0;
|
|
int i=0;
|
|
//--- create matrix
|
|
CMatrixComplex inva;
|
|
//--- initialization
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- inv A
|
|
CMatrixMakeACopy(a,n,n,inva);
|
|
//--- check
|
|
if(!CMatrixInvMat(inva,n))
|
|
{
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- norm A
|
|
nrm1a=0;
|
|
nrminfa=0;
|
|
//--- calculation
|
|
for(k=0; k<n; k++)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::AbsComplex(a[i][k]);
|
|
nrm1a=MathMax(nrm1a,v);
|
|
//--- change value
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::AbsComplex(a[k][i]);
|
|
nrminfa=MathMax(nrminfa,v);
|
|
}
|
|
//--- norm inv A
|
|
nrm1inva=0;
|
|
nrminfinva=0;
|
|
//--- calculation
|
|
for(k=0; k<n; k++)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::AbsComplex(inva[i][k]);
|
|
nrm1inva=MathMax(nrm1inva,v);
|
|
//--- change value
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::AbsComplex(inva[k][i]);
|
|
nrminfinva=MathMax(nrminfinva,v);
|
|
}
|
|
//--- result
|
|
rc1=nrm1inva*nrm1a;
|
|
rcinf=nrminfinva*nrminfa;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestRMatrixTrRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble ea;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
//--- calculation
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
RMatrixGenZero(a,n);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- change values
|
|
isupper=CMath::RandomReal()>0.5;
|
|
isunit=CMath::RandomReal()>0.5;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CMath::RandomReal()-0.5);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1+CMath::RandomReal());
|
|
//--- function call
|
|
RMatrixMakeACopy(a,n,n,ea);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
//--- change values
|
|
for(j=j1; j<=j2; j++)
|
|
ea.Set(i,j,0);
|
|
//--- check
|
|
if(isunit)
|
|
ea.Set(i,i,1);
|
|
}
|
|
//--- function call
|
|
RMatrixRefRCond(ea,n,erc1,ercinf);
|
|
//--- 1-norm
|
|
v=1/CRCond::RMatrixTrRCond1(a,n,isupper,isunit);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
//--- Inf-norm
|
|
v=1/CRCond::RMatrixTrRCondInf(a,n,isupper,isunit);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=(double)(m_threshold90*ercinf))
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>ercinf*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
err50=err50||q50[i]<0.5;
|
|
err90=err90||q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
a.Set(0,0,1);
|
|
a.Set(n-1,n-1,1);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a.Set(i,i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50||err90)||errless)||errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestCMatrixTrRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex ea;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
//--- calculation
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
CMatrixGenZero(a,n);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- change values
|
|
isupper=CMath::RandomReal()>0.5;
|
|
isunit=CMath::RandomReal()>0.5;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,CMath::RandomReal()-0.5);
|
|
a.SetIm(i,j,CMath::RandomReal()-0.5);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.SetRe(i,i,1+CMath::RandomReal());
|
|
a.SetIm(i,i,1+CMath::RandomReal());
|
|
}
|
|
//--- function call
|
|
CMatrixMakeACopy(a,n,n,ea);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
for(j=j1; j<=j2; j++)
|
|
ea.Set(i,j,0);
|
|
//--- check
|
|
if(isunit)
|
|
ea.Set(i,i,1);
|
|
}
|
|
//--- function call
|
|
CMatrixRefRCond(ea,n,erc1,ercinf);
|
|
//--- 1-norm
|
|
v=1/CRCond::CMatrixTrRCond1(a,n,isupper,isunit);
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=(double)(m_threshold90*erc1))
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>(double)(erc1*1.001);
|
|
//--- Inf-norm
|
|
v=1/CRCond::CMatrixTrRCondInf(a,n,isupper,isunit);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=(double)(m_threshold90*ercinf))
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>(double)(ercinf*1.001);
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
err50=err50||q50[i]<0.5;
|
|
err90=err90||q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
a.Set(0,0,1);
|
|
a.Set(n-1,n-1,1);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a.Set(i,i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50||err90)||errless)||errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestRMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create array
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble lua;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,4);
|
|
ArrayResize(q90,4);
|
|
//--- calculation
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
RMatrixGenZero(a,n);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::RMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixLURCond1(lua,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixLURCondInf(lua,n)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0; i<=3; i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- function call
|
|
CMatGen::RMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
RMatrixRefRCond(a,n,erc1,ercinf);
|
|
//--- 1-norm,normal
|
|
v=1/CRCond::RMatrixRCond1(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>(double)(erc1*1.001);
|
|
//--- 1-norm,LU
|
|
v=1/CRCond::RMatrixLURCond1(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
//--- Inf-norm,normal
|
|
v=1/CRCond::RMatrixRCondInf(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[2]=q50[2]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[2]=q90[2]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>ercinf*1.001;
|
|
//--- Inf-norm,LU
|
|
v=1/CRCond::RMatrixLURCondInf(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[3]=q50[3]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[3]=q90[3]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>ercinf*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=3; i++)
|
|
{
|
|
err50=err50||q50[i]<0.5;
|
|
err90=err90||q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
a.Set(0,0,1);
|
|
a.Set(n-1,n-1,1);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::RMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a.Set(i,i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::RMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::RMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50||err90)||errless)||errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestSPDMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
bool isupper;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble cha;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
//--- calculation
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- function calls
|
|
CMatGen::SPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
RMatrixRefRCond(a,n,erc1,ercinf);
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
CTrFac::SPDMatrixCholesky(cha,n,isupper);
|
|
//--- normal
|
|
v=1/CRCond::SPDMatrixRCond(a,n,isupper);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
//--- Cholesky
|
|
v=1/CRCond::SPDMatrixCholeskyRCond(cha,n,isupper);
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
err50=err50||q50[i]<0.5;
|
|
err90=err90||q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
a.Set(0,0,1);
|
|
a.Set(n-1,n-1,1);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::SPDMatrixRCond(a,n,isupper)!=-1.0;
|
|
errspec=errspec||CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a.Set(i,i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::SPDMatrixRCond(a,n,isupper)!=0.0;
|
|
errspec=errspec||CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50||err90)||errless)||errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestCMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errless;
|
|
bool errspec;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex lua;
|
|
//--- allocation
|
|
ArrayResize(q50,4);
|
|
ArrayResize(q90,4);
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- process
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
CMatrixGenZero(a,n);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::CMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixLURCond1(lua,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixLURCondInf(lua,n)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0; i<=3; i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- function calls
|
|
CMatGen::CMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
CMatrixRefRCond(a,n,erc1,ercinf);
|
|
//--- 1-norm,normal
|
|
v=1/CRCond::CMatrixRCond1(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
//--- 1-norm,LU
|
|
v=1/CRCond::CMatrixLURCond1(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
//--- Inf-norm,normal
|
|
v=1/CRCond::CMatrixRCondInf(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[2]=q50[2]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[2]=q90[2]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>ercinf*1.001;
|
|
//--- Inf-norm,LU
|
|
v=1/CRCond::CMatrixLURCondInf(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[3]=q50[3]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[3]=q90[3]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>ercinf*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=3; i++)
|
|
{
|
|
err50=err50||q50[i]<0.5;
|
|
err90=err90||q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
a.Set(0,0,1);
|
|
a.Set(n-1,n-1,1);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::CMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a.Set(i,i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::CMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec||CRCond::CMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50||err90)||errless)||errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRCondUnit::TestHPDMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
bool isupper;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex cha;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- function calls
|
|
CMatGen::HPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
CMatrixRefRCond(a,n,erc1,ercinf);
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
CTrFac::HPDMatrixCholesky(cha,n,isupper);
|
|
//--- normal
|
|
v=1/CRCond::HPDMatrixRCond(a,n,isupper);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
//--- Cholesky
|
|
v=1/CRCond::HPDMatrixCholeskyRCond(cha,n,isupper);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless||v>erc1*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
err50=err50||q50[i]<0.5;
|
|
err90=err90||q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
a.Set(0,0,1);
|
|
a.Set(n-1,n-1,1);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::HPDMatrixRCond(a,n,isupper)!=-1.0;
|
|
errspec=errspec||CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a.Set(i,i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec||CRCond::HPDMatrixRCond(a,n,isupper)!=0.0;
|
|
errspec=errspec||CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50||err90)||errless)||errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMatInv |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMatInvUnit
|
|
{
|
|
public:
|
|
static bool TestMatInv(const bool silent);
|
|
|
|
private:
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static bool RMatrixCheckInverse(CMatrixDouble &a,CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool SPDMatrixCheckInverse(CMatrixDouble &ca,CMatrixDouble &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool HPDMatrixCheckInverse(CMatrixComplex &ca,CMatrixComplex &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool RMatrixCheckInverseSingular(CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool CMatrixCheckInverse(CMatrixComplex &a,CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool CMatrixCheckInverseSingular(CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
|
|
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
|
|
static void TestRTRInv(const int maxn,const int passcount,const double threshold,bool &rtrerrors);
|
|
static void TestCTRInv(const int maxn,const int passcount,const double threshold,bool &ctrerrors);
|
|
static void TesTrInv(const int maxn,const int passcount,const double threshold,bool &rerrors);
|
|
static void TestCInv(const int maxn,const int passcount,const double threshold,bool &cerrors);
|
|
static void TestSPDInv(const int maxn,const int passcount,const double threshold,bool &spderrors);
|
|
static void TestHPDInv(const int maxn,const int passcount,const double threshold,bool &hpderrors);
|
|
static void Unset2D(CMatrixDouble &x);
|
|
static void Unset1D(double &x[]);
|
|
static void CUnset2D(CMatrixComplex &x);
|
|
static void CUnset1D(double &x[]);
|
|
static void UnsetRep(CMatInvReport &r);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::TestMatInv(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxrn=0;
|
|
int maxcn=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
double rcondtol=0;
|
|
bool rtrerrors;
|
|
bool ctrerrors;
|
|
bool rerrors;
|
|
bool cerrors;
|
|
bool spderrors;
|
|
bool hpderrors;
|
|
bool waserrors;
|
|
//--- create matrix
|
|
CMatrixDouble emptyra;
|
|
CMatrixDouble emptyca;
|
|
//--- initialization
|
|
maxrn=3*CAblas::AblasBlockSize()+1;
|
|
maxcn=3*CAblas::AblasBlockSize()+1;
|
|
passcount=1;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
rcondtol=0.01;
|
|
rtrerrors=false;
|
|
ctrerrors=false;
|
|
rerrors=false;
|
|
cerrors=false;
|
|
spderrors=false;
|
|
hpderrors=false;
|
|
//--- function calls
|
|
TestRTRInv(maxrn,passcount,threshold,rtrerrors);
|
|
TestCTRInv(maxcn,passcount,threshold,ctrerrors);
|
|
TesTrInv(maxrn,passcount,threshold,rerrors);
|
|
TestSPDInv(maxrn,passcount,threshold,spderrors);
|
|
TestCInv(maxcn,passcount,threshold,cerrors);
|
|
TestHPDInv(maxcn,passcount,threshold,hpderrors);
|
|
//--- search errors
|
|
waserrors=((((rtrerrors||ctrerrors)||rerrors)||cerrors)||spderrors)||hpderrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MATINV");
|
|
PrintResult("* REAL TRIANGULAR",!rtrerrors);
|
|
PrintResult("* COMPLEX TRIANGULAR",!ctrerrors);
|
|
PrintResult("* REAL",!rerrors);
|
|
PrintResult("* COMPLEX",!cerrors);
|
|
PrintResult("* SPD",!spderrors);
|
|
PrintResult("* HPD",!hpderrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::RMatrixMakeACopy(CMatrixDouble &a,
|
|
const int m,const int n,
|
|
CMatrixDouble &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::CMatrixMakeACopy(CMatrixComplex &a,
|
|
const int m,const int n,
|
|
CMatrixComplex &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::RMatrixCheckInverse(CMatrixDouble &a,
|
|
CMatrixDouble &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*inva.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && MathAbs(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::SPDMatrixCheckInverse(CMatrixDouble &ca,
|
|
CMatrixDouble &cinva,
|
|
const bool isupper,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble inva;
|
|
//--- copy
|
|
a=ca;
|
|
inva=cinva;
|
|
//--- calculation
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- change values
|
|
for(i_=i+1; i_<n; i_++)
|
|
a.Set(i_,i,a.Get(i,i_));
|
|
for(i_=i+1; i_<n; i_++)
|
|
inva.Set(i_,i,inva.Get(i,i_));
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(i_=i+1; i_<n; i_++)
|
|
a.Set(i,i_,a.Get(i_,i));
|
|
for(i_=i+1; i_<n; i_++)
|
|
inva.Set(i,i_,inva.Get(i_,i));
|
|
}
|
|
}
|
|
//--- change value
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*inva.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && MathAbs(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::HPDMatrixCheckInverse(CMatrixComplex &ca,
|
|
CMatrixComplex &cinva,
|
|
const bool isupper,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex inva;
|
|
//--- copy
|
|
a=ca;
|
|
inva=cinva;
|
|
//--- calculation
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- change values
|
|
for(i_=i+1; i_<n; i_++)
|
|
a.Set(i_,i,CMath::Conj(a.Get(i,i_)));
|
|
for(i_=i+1; i_<n; i_++)
|
|
inva.Set(i_,i,CMath::Conj(inva.Get(i,i_)));
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(i_=i+1; i_<n; i_++)
|
|
a.Set(i,i_,CMath::Conj(a.Get(i_,i)));
|
|
for(i_=i+1; i_<n; i_++)
|
|
inva.Set(i,i_,CMath::Conj(inva.Get(i_,i)));
|
|
}
|
|
}
|
|
//--- change value
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*inva.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && CMath::AbsComplex(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inversion result indicate singular matrix |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::RMatrixCheckInverseSingular(CMatrixDouble &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
bool result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
result=result && inva.Get(i,j)==0.0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::CMatrixCheckInverse(CMatrixComplex &a,
|
|
CMatrixComplex &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result=true;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*inva.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && CMath::AbsComplex(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inversion result indicate singular matrix |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMatInvUnit::CMatrixCheckInverseSingular(CMatrixComplex &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
bool result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
result=result && inva.Get(i,j)==0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real TR inverse |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::TestRTRInv(const int maxn,const int passcount,
|
|
const double threshold,bool &rtrerrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int task=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
double v=0;
|
|
bool waserrors;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- Test
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- calculation
|
|
for(task=0; task<=3; task++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Determine task
|
|
isupper=task%2==0;
|
|
isunit=task/2%2==0;
|
|
//--- Generate matrix
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a.Set(i,i,1+CMath::RandomReal());
|
|
else
|
|
a.Set(i,j,0.2*CMath::RandomReal()-0.1);
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- Inverse
|
|
CMatInv::RMatrixTrInverse(b,n,isupper,isunit,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
rtrerrors=true;
|
|
return;
|
|
}
|
|
//--- Structural test
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
rtrerrors=rtrerrors||a[i][i]!=b[i][i];
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
rtrerrors=rtrerrors||a.Get(i,j)!=b.Get(i,j);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
rtrerrors=rtrerrors||a.Get(i,j)!=b.Get(i,j);
|
|
}
|
|
}
|
|
//--- Inverse test
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((j<i && isupper)||(j>i && !isupper))
|
|
{
|
|
a.Set(i,j,0);
|
|
b.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,1);
|
|
b.Set(i,i,1);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*b.Get(i_,j);
|
|
//--- check
|
|
if(j!=i)
|
|
rtrerrors=rtrerrors||MathAbs(v)>threshold;
|
|
else
|
|
rtrerrors=rtrerrors||MathAbs(v-1)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex TR inverse |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::TestCTRInv(const int maxn,const int passcount,
|
|
const double threshold,bool &ctrerrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int task=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
complex v=0;
|
|
bool waserrors;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
CMatrixComplex a;
|
|
CMatrixComplex b;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- Test
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- calculation
|
|
for(task=0; task<=3; task++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Determine task
|
|
isupper=task%2==0;
|
|
isunit=task/2%2==0;
|
|
//--- Generate matrix
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
{
|
|
a.SetRe(i,i,1+CMath::RandomReal());
|
|
a.SetIm(i,i,1+CMath::RandomReal());
|
|
}
|
|
else
|
|
{
|
|
a.SetRe(i,j,0.2*CMath::RandomReal()-0.1);
|
|
a.SetIm(i,j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- Inverse
|
|
CMatInv::CMatrixTrInverse(b,n,isupper,isunit,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
ctrerrors=true;
|
|
return;
|
|
}
|
|
//--- Structural test
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
ctrerrors=ctrerrors||a[i][i]!=b[i][i];
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
ctrerrors=ctrerrors||a.Get(i,j)!=b.Get(i,j);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
ctrerrors=ctrerrors||a.Get(i,j)!=b.Get(i,j);
|
|
}
|
|
}
|
|
//--- Inverse test
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
{
|
|
a.Set(i,j,0);
|
|
b.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,1);
|
|
b.Set(i,i,1);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*b.Get(i_,j);
|
|
//--- check
|
|
if(j!=i)
|
|
ctrerrors=ctrerrors||CMath::AbsComplex(v)>threshold;
|
|
else
|
|
ctrerrors=ctrerrors||CMath::AbsComplex(v-1)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::TesTrInv(const int maxn,const int passcount,
|
|
const double threshold,bool &rerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create array
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble lua;
|
|
CMatrixDouble inva;
|
|
CMatrixDouble invlua;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::RMatrixRndCond(n,1000,a);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
RMatrixMakeACopy(a,n,n,inva);
|
|
RMatrixMakeACopy(lua,n,n,invlua);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixInverse(inva,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckInverse(a,inva,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixLUInverse(invlua,p,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckInverse(a,invlua,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. test different methods
|
|
for(taskkind=0; taskkind<=4; taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,0*a[k][i_]);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(0,i_,a[k][i_]);
|
|
}
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixInverse(a,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckInverseSingular(a,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixLUInverse(lua,p,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckInverseSingular(lua,n,threshold,info,rep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::TestCInv(const int maxn,const int passcount,
|
|
const double threshold,bool &cerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create array
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex lua;
|
|
CMatrixComplex inva;
|
|
CMatrixComplex invlua;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::CMatrixRndCond(n,1000,a);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
CMatrixMakeACopy(a,n,n,inva);
|
|
CMatrixMakeACopy(lua,n,n,invlua);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixInverse(inva,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckInverse(a,inva,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixLUInverse(invlua,p,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckInverse(a,invlua,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. test different methods
|
|
for(taskkind=0; taskkind<=4; taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,a[i_][k]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change value
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(0,i_,a[k][i_]);
|
|
}
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixInverse(a,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckInverseSingular(a,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixLUInverse(lua,p,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckInverseSingular(lua,n,threshold,info,rep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SPD test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::TestSPDInv(const int maxn,const int passcount,
|
|
const double threshold,bool &spderrors)
|
|
{
|
|
//--- create variables
|
|
bool isupper;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble cha;
|
|
CMatrixDouble inva;
|
|
CMatrixDouble invcha;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::SPDMatrixRndCond(n,1000,a);
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(cha,n,isupper))
|
|
continue;
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,inva);
|
|
RMatrixMakeACopy(cha,n,n,invcha);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::SPDMatrixInverse(inva,n,isupper,info,rep);
|
|
//--- search errors
|
|
spderrors=spderrors||!SPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::SPDMatrixCholeskyInverse(invcha,n,isupper,info,rep);
|
|
//--- search errors
|
|
spderrors=spderrors||!SPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- 2. test different methods
|
|
for(taskkind=0; taskkind<=2; taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,0*a[k][i_]);
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::SPDMatrixCholeskyInverse(a,n,isupper,info,rep);
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
spderrors=true;
|
|
else
|
|
{
|
|
spderrors=(spderrors||rep.m_r1<0.0)||rep.m_r1>1000*CMath::m_machineepsilon;
|
|
spderrors=(spderrors||rep.m_rinf<0.0)||rep.m_rinf>1000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| HPD test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::TestHPDInv(const int maxn,const int passcount,
|
|
const double threshold,bool &hpderrors)
|
|
{
|
|
//--- create variables
|
|
bool isupper;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex cha;
|
|
CMatrixComplex inva;
|
|
CMatrixComplex invcha;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::HPDMatrixRndCond(n,1000,a);
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::HPDMatrixCholesky(cha,n,isupper))
|
|
continue;
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,inva);
|
|
CMatrixMakeACopy(cha,n,n,invcha);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::HPDMatrixInverse(inva,n,isupper,info,rep);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!HPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::HPDMatrixCholeskyInverse(invcha,n,isupper,info,rep);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!HPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- 2. test different methods
|
|
for(taskkind=0; taskkind<=2; taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,a[i_][k]*0);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,a[k][i_]*0);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,a[i_][k]*0);
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::HPDMatrixCholeskyInverse(a,n,isupper,info,rep);
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
hpderrors=true;
|
|
else
|
|
{
|
|
hpderrors=(hpderrors||rep.m_r1<0.0)||rep.m_r1>1000*CMath::m_machineepsilon;
|
|
hpderrors=(hpderrors||rep.m_rinf<0.0)||rep.m_rinf>1000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::Unset2D(CMatrixDouble &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::CUnset2D(CMatrixComplex &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::CUnset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMatInvUnit::UnsetRep(CMatInvReport &r)
|
|
{
|
|
//--- change values
|
|
r.m_r1=-1;
|
|
r.m_rinf=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLDA |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLDAUnit
|
|
{
|
|
public:
|
|
static bool TestLDA(const bool silent);
|
|
|
|
private:
|
|
static void GenSimpleSet(const int nfeatures,const int nclasses,const int nsamples,const int axis,CMatrixDouble &xy);
|
|
static void GenDeg1Set(const int nfeatures,const int nclasses,const int nsamples,int axis,CMatrixDouble &xy);
|
|
static double GenerateNormal(const double mean,const double sigma);
|
|
static bool TestWN(CMatrixDouble &xy,CMatrixDouble &wn,const int ns,const int nf,const int nc,const int ndeg);
|
|
static double CalcJ(const int nf,CMatrixDouble &st,CMatrixDouble &sw,double &w[],double &p,double &q);
|
|
static void Fishers(CMatrixDouble &xy,int npoints,const int nfeatures,const int nclasses,CMatrixDouble &st,CMatrixDouble &sw);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLDA |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLDAUnit::TestLDA(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxnf=0;
|
|
int maxns=0;
|
|
int maxnc=0;
|
|
int passcount=0;
|
|
bool ldanerrors;
|
|
bool lda1errors;
|
|
bool waserrors;
|
|
int nf=0;
|
|
int nc=0;
|
|
int ns=0;
|
|
int i=0;
|
|
int info=0;
|
|
int pass=0;
|
|
int axis=0;
|
|
//--- create array
|
|
double w1[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble wn;
|
|
//--- Primary settings
|
|
maxnf=10;
|
|
maxns=1000;
|
|
maxnc=5;
|
|
passcount=1;
|
|
waserrors=false;
|
|
ldanerrors=false;
|
|
lda1errors=false;
|
|
//--- General tests
|
|
for(nf=1; nf<=maxnf; nf++)
|
|
{
|
|
for(nc=2; nc<=maxnc; nc++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Simple test for LDA-N/LDA-1
|
|
axis=CMath::RandomInteger(nf);
|
|
ns=maxns/2+CMath::RandomInteger(maxns/2);
|
|
//--- function calls
|
|
GenSimpleSet(nf,nc,ns,axis,xy);
|
|
CLDA::FisherLDAN(xy,ns,nf,nc,info,wn);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
ldanerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
ldanerrors=ldanerrors||!TestWN(xy,wn,ns,nf,nc,0);
|
|
ldanerrors=ldanerrors||MathAbs(wn[axis][0])<=0.75;
|
|
//--- function call
|
|
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
|
|
//--- search errors
|
|
for(i=0; i<nf; i++)
|
|
lda1errors=lda1errors||w1[i]!=wn[i][0];
|
|
//--- Degenerate test for LDA-N
|
|
if(nf>=3)
|
|
{
|
|
ns=maxns/2+CMath::RandomInteger(maxns/2);
|
|
//--- there are two duplicate features,
|
|
//--- axis is oriented along non-duplicate feature
|
|
axis=CMath::RandomInteger(nf-2);
|
|
GenDeg1Set(nf,nc,ns,axis,xy);
|
|
CLDA::FisherLDAN(xy,ns,nf,nc,info,wn);
|
|
//--- check
|
|
if(info!=2)
|
|
{
|
|
ldanerrors=true;
|
|
continue;
|
|
}
|
|
//--- function calls
|
|
ldanerrors=ldanerrors||wn.Get(axis,0)<=0.75;
|
|
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
|
|
//--- search errors
|
|
for(i=0; i<nf; i++)
|
|
lda1errors=lda1errors||w1[i]!=wn[i][0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Final report
|
|
waserrors=ldanerrors||lda1errors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("LDA TEST");
|
|
PrintResult("FISHER LDA-N",!ldanerrors);
|
|
PrintResult("FISHER LDA-1",!lda1errors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates 'simple' set - a sequence of unit 'balls' at |
|
|
//| (0,0),(1,0),(2,0) and so on. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLDAUnit::GenSimpleSet(const int nfeatures,const int nclasses,
|
|
const int nsamples,const int axis,
|
|
CMatrixDouble &xy)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenSimpleSet: wrong Axis!"))
|
|
return;
|
|
//--- allocation
|
|
xy.Resize(nsamples,nfeatures+1);
|
|
//--- calculation
|
|
for(int i=0; i<=nsamples-1; i++)
|
|
{
|
|
for(int j=0; j<nfeatures; j++)
|
|
xy.Set(i,j,GenerateNormal(0.0,1.0));
|
|
//--- change values
|
|
int c=i%nclasses;
|
|
xy.Set(i,axis,xy[i][axis]+c);
|
|
xy.Set(i,nfeatures,c);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates 'degenerate' set #1. |
|
|
//| NFeatures>=3. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLDAUnit::GenDeg1Set(const int nfeatures,const int nclasses,
|
|
const int nsamples,int axis,
|
|
CMatrixDouble &xy)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenDeg1Set: wrong Axis!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nfeatures>=3,"GenDeg1Set: wrong NFeatures!"))
|
|
return;
|
|
//--- allocation
|
|
xy.Resize(nsamples,nfeatures+1);
|
|
//--- check
|
|
if(axis>=nfeatures-2)
|
|
axis=nfeatures-3;
|
|
//--- calculation
|
|
for(int i=0; i<=nsamples-1; i++)
|
|
{
|
|
for(int j=0; j<=nfeatures-2; j++)
|
|
xy.Set(i,j,GenerateNormal(0.0,1.0));
|
|
//--- change values
|
|
xy.Set(i,nfeatures-1,xy[i][nfeatures-2]);
|
|
int c=i%nclasses;
|
|
xy.Set(i,axis,xy[i][axis]+c);
|
|
xy.Set(i,nfeatures,c);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Normal random number |
|
|
//+------------------------------------------------------------------+
|
|
double CTestLDAUnit::GenerateNormal(const double mean,const double sigma)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double sum=0;
|
|
//--- initialization
|
|
result=mean;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- change values
|
|
u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
sum=u*u+v*v;
|
|
//--- check
|
|
if(sum<1.0 && sum>0.0)
|
|
{
|
|
sum=MathSqrt(-(2*MathLog(sum)/sum));
|
|
result=sigma*u*sum+mean;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests WN for correctness |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLDAUnit::TestWN(CMatrixDouble &xy,CMatrixDouble &wn,
|
|
const int ns,const int nf,
|
|
const int nc,const int ndeg)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double wprev=0;
|
|
double tol=0;
|
|
double p=0;
|
|
double q=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tx[];
|
|
double jp[];
|
|
double jq[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble st;
|
|
CMatrixDouble sw;
|
|
CMatrixDouble a;
|
|
CMatrixDouble z;
|
|
//--- initialization
|
|
tol=10000;
|
|
result=true;
|
|
Fishers(xy,ns,nf,nc,st,sw);
|
|
//--- Test for decreasing of J
|
|
ArrayResize(tx,nf);
|
|
ArrayResize(jp,nf);
|
|
ArrayResize(jq,nf);
|
|
//--- calculation
|
|
for(j=0; j<nf; j++)
|
|
{
|
|
for(i_=0; i_<nf; i_++)
|
|
tx[i_]=wn.Get(i_,j);
|
|
v=CalcJ(nf,st,sw,tx,p,q);
|
|
jp[j]=p;
|
|
jq[j]=q;
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=nf-1-ndeg; i++)
|
|
result=result && jp[i-1]/jq[i-1]>=(1-tol*CMath::m_machineepsilon)*jp[i]/jq[i];
|
|
for(i=nf-1-ndeg+1; i<nf; i++)
|
|
result=result && jp[i]<=tol*CMath::m_machineepsilon*jp[0];
|
|
//--- Test for J optimality
|
|
for(i_=0; i_<nf; i_++)
|
|
tx[i_]=wn[i_][0];
|
|
v=CalcJ(nf,st,sw,tx,p,q);
|
|
//--- calculation
|
|
for(i=0; i<nf; i++)
|
|
{
|
|
wprev=tx[i];
|
|
tx[i]=wprev+0.01;
|
|
result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q);
|
|
tx[i]=wprev-0.01;
|
|
result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q);
|
|
tx[i]=wprev;
|
|
}
|
|
//--- Test for linear independence of W
|
|
ArrayResize(work,nf+1);
|
|
a.Resize(nf,nf);
|
|
//--- function call
|
|
CBlas::MatrixMatrixMultiply(wn,0,nf-1,0,nf-1,false,wn,0,nf-1,0,nf-1,true,1.0,a,0,nf-1,0,nf-1,0.0,work);
|
|
//--- check
|
|
if(CEigenVDetect::SMatrixEVD(a,nf,1,true,tx,z))
|
|
result=result && tx[0]>tx[nf-1]*1000*CMath::m_machineepsilon;
|
|
//--- Test for other properties
|
|
for(j=0; j<nf; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nf; i_++)
|
|
v+=wn.Get(i_,j)*wn.Get(i_,j);
|
|
//--- change values
|
|
v=MathSqrt(v);
|
|
result=result && MathAbs(v-1)<=1000*CMath::m_machineepsilon;
|
|
v=0;
|
|
for(i=0; i<nf; i++)
|
|
v+=wn.Get(i,j);
|
|
result=result && v>=0.0;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates J |
|
|
//+------------------------------------------------------------------+
|
|
double CTestLDAUnit::CalcJ(const int nf,CMatrixDouble &st,
|
|
CMatrixDouble &sw,double &w[],
|
|
double &p,double &q)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double tx[];
|
|
//--- initialization
|
|
p=0;
|
|
q=0;
|
|
//--- allocation
|
|
ArrayResize(tx,nf);
|
|
//--- calculation
|
|
for(i=0; i<nf; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nf; i_++)
|
|
v+=st.Get(i,i_)*w[i_];
|
|
tx[i]=v;
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nf; i_++)
|
|
v+=w[i_]*tx[i_];
|
|
p=v;
|
|
for(i=0; i<nf; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nf; i_++)
|
|
v+=sw.Get(i,i_)*w[i_];
|
|
tx[i]=v;
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nf; i_++)
|
|
v+=w[i_]*tx[i_];
|
|
q=v;
|
|
result=p/q;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates ST/SW |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLDAUnit::Fishers(CMatrixDouble &xy,int npoints,
|
|
const int nfeatures,const int nclasses,
|
|
CMatrixDouble &st,CMatrixDouble &sw)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int c[];
|
|
double mu[];
|
|
int nc[];
|
|
double tf[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble muc;
|
|
//--- Prepare temporaries
|
|
ArrayResize(tf,nfeatures);
|
|
ArrayResize(work,nfeatures+1);
|
|
//--- Convert class labels from reals to integers (just for convenience)
|
|
ArrayResize(c,npoints);
|
|
for(i=0; i<npoints ; i++)
|
|
c[i]=(int)MathRound(xy.Get(i,nfeatures));
|
|
//--- Calculate class sizes and means
|
|
ArrayResize(mu,nfeatures);
|
|
muc.Resize(nclasses,nfeatures);
|
|
ArrayResize(nc,nclasses);
|
|
//--- change values
|
|
for(j=0; j<nfeatures; j++)
|
|
mu[j]=0;
|
|
for(i=0; i<nclasses; i++)
|
|
{
|
|
nc[i]=0;
|
|
for(j=0; j<nfeatures; j++)
|
|
muc.Set(i,j,0);
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
mu[i_]=mu[i_]+xy.Get(i,i_);
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
muc.Set(c[i],i_,muc[c[i]][i_]+xy.Get(i,i_));
|
|
nc[c[i]]=nc[c[i]]+1;
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<nclasses; i++)
|
|
{
|
|
v=1.0/(double)nc[i];
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
muc.Set(i,i_,v*muc.Get(i,i_));
|
|
}
|
|
//--- change values
|
|
v=1.0/(double)npoints;
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
mu[i_]=v*mu[i_];
|
|
//--- Create ST matrix
|
|
st.Resize(nfeatures,nfeatures);
|
|
//--- change values
|
|
for(i=0; i<nfeatures; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
st.Set(i,j,0);
|
|
}
|
|
//--- calculation
|
|
for(k=0; k<npoints ; k++)
|
|
{
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
tf[i_]=xy[k][i_];
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
tf[i_]=tf[i_]-mu[i_];
|
|
//--- calculation
|
|
for(i=0; i<nfeatures; i++)
|
|
{
|
|
v=tf[i];
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
st.Set(i,i_,st.Get(i,i_)+v*tf[i_]);
|
|
}
|
|
}
|
|
//--- Create SW matrix
|
|
sw.Resize(nfeatures,nfeatures);
|
|
for(i=0; i<nfeatures; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
sw.Set(i,j,0);
|
|
}
|
|
//--- calculation
|
|
for(k=0; k<npoints ; k++)
|
|
{
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
tf[i_]=xy[k][i_];
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
tf[i_]=tf[i_]-muc[c[k]][i_];
|
|
//--- calculation
|
|
for(i=0; i<nfeatures; i++)
|
|
{
|
|
v=tf[i];
|
|
for(i_=0; i_<nfeatures; i_++)
|
|
sw.Set(i,i_,sw.Get(i,i_)+v*tf[i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGammaFunc |
|
|
//+------------------------------------------------------------------+
|
|
class CTestGammaFuncUnit
|
|
{
|
|
public:
|
|
static bool TestGammaFunc(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGammaFunc |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestGammaFuncUnit::TestGammaFunc(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double v=0;
|
|
double s=0;
|
|
bool waserrors;
|
|
bool gammaerrors;
|
|
bool lngammaerrors;
|
|
//--- initialization
|
|
gammaerrors=false;
|
|
lngammaerrors=false;
|
|
waserrors=false;
|
|
threshold=100*CMath::m_machineepsilon;
|
|
//--- search errors
|
|
gammaerrors=gammaerrors||MathAbs(CGammaFunc::GammaFunc(0.5)-MathSqrt(M_PI))>threshold;
|
|
gammaerrors=gammaerrors||MathAbs(CGammaFunc::GammaFunc(1.5)-0.5*MathSqrt(M_PI))>threshold;
|
|
//--- function call
|
|
v=CGammaFunc::LnGamma(0.5,s);
|
|
//--- search errors
|
|
lngammaerrors=(lngammaerrors||MathAbs(v-MathLog(MathSqrt(M_PI)))>threshold)||s!=1.0;
|
|
//--- function call
|
|
v=CGammaFunc::LnGamma(1.5,s);
|
|
//--- search errors
|
|
lngammaerrors=(lngammaerrors||MathAbs(v-MathLog(0.5*MathSqrt(M_PI)))>threshold)||s!=1.0;
|
|
//--- report
|
|
waserrors=gammaerrors||lngammaerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING GAMMA FUNCTION");
|
|
PrintResult("GAMMA",!gammaerrors);
|
|
PrintResult("LN GAMMA",!lngammaerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBdSingValueDecompose |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBdSVDUnit
|
|
{
|
|
public:
|
|
static bool TestBdSVD(const bool silent);
|
|
|
|
private:
|
|
static void FillIdentity(CMatrixDouble &a,const int n);
|
|
static void FillSparseDE(double &d[],double &e[],const int n,const double sparcity);
|
|
static void GetBdSVDError(double &d[],double &e[],const int n,const bool isupper,CMatrixDouble &u,CMatrixDouble &c,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted);
|
|
static void TestBdSVDProblem(double &d[],double &e[],const int n,double &materr,double &orterr,bool &wsorted,bool &wfailed,int &failcount,int &succcount);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing bidiagonal SVD decomposition subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestBdSVDUnit::TestBdSVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=15;
|
|
int i=0;
|
|
int pass=0;
|
|
bool waserrors=false;
|
|
bool wsorted=true;
|
|
bool wfailed=false;
|
|
bool failcase;
|
|
double materr=0;
|
|
double orterr=0;
|
|
double threshold=5*100*CMath::m_machineepsilon;
|
|
double failthreshold=1.0E-2;
|
|
double failr=0;
|
|
int failcount=0;
|
|
int succcount=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble mempty;
|
|
//--- allocation
|
|
ArrayResize(d,maxn);
|
|
ArrayResize(e,maxn-1);
|
|
//--- special case: fail matrix
|
|
n=5;
|
|
d[0]=-8.27448347422711894000e-01;
|
|
d[1]=-8.16705832087160854600e-01;
|
|
d[2]=-2.53974358904729382800e-17;
|
|
d[3]=-1.24626684881972815700e+00;
|
|
d[4]=-4.64744131545637651000e-01;
|
|
e[0]=-3.25785088656270038800e-01;
|
|
e[1]=-1.03732413708914436580e-01;
|
|
e[2]=-9.57365642262031357700e-02;
|
|
e[3]=-2.71564153973817390400e-01;
|
|
failcase=CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0);
|
|
//--- special case: zero divide matrix
|
|
//--- unfixed LAPACK routine should fail on this problem
|
|
n=7;
|
|
d[0]=-6.96462904751731892700e-01;
|
|
d[1]=0.00000000000000000000e+00;
|
|
d[2]=-5.73827770385971991400e-01;
|
|
d[3]=-6.62562624399371191700e-01;
|
|
d[4]=5.82737148001782223600e-01;
|
|
d[5]=3.84825263580925003300e-01;
|
|
d[6]=9.84087420830525472200e-01;
|
|
e[0]=-7.30307931760612871800e-02;
|
|
e[1]=-2.30079042939542843800e-01;
|
|
e[2]=-6.87824621739351216300e-01;
|
|
e[3]=-1.77306437707837570600e-02;
|
|
e[4]=1.78285126526551632000e-15;
|
|
e[5]=-4.89434737751289969400e-02;
|
|
CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0);
|
|
//--- zero matrix,several cases
|
|
for(i=0; i<=maxn-1; i++)
|
|
d[i]=0;
|
|
for(i=0; i<=maxn-2; i++)
|
|
e[i]=0;
|
|
for(n=1; n<=maxn; n++)
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
//--- Dense matrix
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(i=0; i<=maxn-1; i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<=maxn-2; i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- function calls
|
|
FillSparseDE(d,e,n,0.5);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
FillSparseDE(d,e,n,0.8);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
FillSparseDE(d,e,n,0.9);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
FillSparseDE(d,e,n,0.95);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
//--- report
|
|
failr=(double)failcount/(double)(succcount+failcount);
|
|
waserrors=((materr>threshold||orterr>threshold)||!wsorted)||failr>failthreshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BIDIAGONAL SVD DECOMPOSITION");
|
|
PrintFormat("SVD decomposition error: %5.3E",materr);
|
|
PrintFormat("SVD orthogonality error: %5.3E}",orterr);
|
|
PrintResult("Singular values order",wsorted);
|
|
PrintFormat("Always converged: %s",(!wfailed ? "YES" : "NO"));
|
|
if(wfailed)
|
|
PrintFormat("Fail ratio: %5.3f",failr);
|
|
PrintFormat("Fail matrix test: %s",(!failcase ? "AS EXPECTED" : "CONVERGED (UNEXPECTED)"));
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSVDUnit::FillIdentity(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- change values
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a.Set(i,j,1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSVDUnit::FillSparseDE(double &d[],double &e[],
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- allocation
|
|
ArrayResize(d,n);
|
|
ArrayResize(e,(int)(MathMax(0,n-2))+1);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
d[i]=0;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
e[i]=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSVDUnit::GetBdSVDError(double &d[],double &e[],
|
|
const int n,const bool isupper,
|
|
CMatrixDouble &u,CMatrixDouble &c,
|
|
double &w[],CMatrixDouble &vt,
|
|
double &materr,double &orterr,
|
|
bool &wsorted)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double locerr=0;
|
|
double sm=0;
|
|
int i_=0;
|
|
//--- decomposition error
|
|
locerr=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
sm=0;
|
|
for(k=0; k<n; k++)
|
|
sm=sm+w[k]*u[i][k]*vt[k][j];
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
locerr=MathMax(locerr,MathAbs(d[i]-sm));
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==j-1)
|
|
locerr=MathMax(locerr,MathAbs(e[i]-sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
locerr=MathMax(locerr,MathAbs(d[i]-sm));
|
|
else
|
|
{
|
|
//--- check
|
|
if(i-1==j)
|
|
locerr=MathMax(locerr,MathAbs(e[j]-sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
materr=MathMax(materr,locerr);
|
|
//--- check for C=U'
|
|
//--- we consider it as decomposition error
|
|
locerr=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
locerr=MathMax(locerr,MathAbs(u.Get(i,j)-c[j][i]));
|
|
}
|
|
materr=MathMax(materr,locerr);
|
|
//--- orthogonality error
|
|
locerr=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
sm+=u.Get(i_,i)*u.Get(i_,j);
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
sm+=vt.Get(i,i_)*vt.Get(j,i_);
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
}
|
|
}
|
|
orterr=MathMax(orterr,locerr);
|
|
//--- values order error
|
|
for(i=1; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(w[i]>w[i-1])
|
|
wsorted=false;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestBdSVDUnit::TestBdSVDProblem(double &d[],double &e[],
|
|
const int n,double &materr,
|
|
double &orterr,bool &wsorted,
|
|
bool &wfailed,int &failcount,
|
|
int &succcount)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double mx=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
CMatrixDouble c;
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(d[i])>mx)
|
|
mx=MathAbs(d[i]);
|
|
}
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(e[i])>mx)
|
|
mx=MathAbs(e[i]);
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- Upper BDSVD tests
|
|
ArrayResize(w,n);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
for(i=0; i<n; i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,false,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,true,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted);
|
|
//--- Lower BDSVD tests
|
|
ArrayResize(w,n);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,false,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,true,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted);
|
|
//--- update counter
|
|
succcount=succcount+1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSingValueDecompose |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSVDUnit
|
|
{
|
|
public:
|
|
static bool TestSVD(const bool silent);
|
|
|
|
private:
|
|
static void FillsParseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
|
|
static void GetSVDError(CMatrixDouble &a,const int m,const int n,CMatrixDouble &u,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted);
|
|
static void TestSVDProblem(CMatrixDouble &a,const int m,const int n,double &materr,double &orterr,double &othererr,bool &wsorted,bool &wfailed,int &failcount,int &succcount);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing SVD decomposition subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSVDUnit::TestSVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int maxmn=30;
|
|
int i=0;
|
|
int j=0;
|
|
int gpass=0;
|
|
int pass=0;
|
|
bool waserrors=false;
|
|
bool wsorted=true;
|
|
bool wfailed=false;
|
|
double materr=0;
|
|
double orterr=0;
|
|
double othererr=0;
|
|
double threshold=5*100*CMath::m_machineepsilon;
|
|
double failthreshold=5.0E-3;
|
|
double failr=0;
|
|
int failcount=0;
|
|
int succcount=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- allocation
|
|
a.Resize(maxmn,maxmn);
|
|
//--- TODO: div by zero fail,convergence fail
|
|
for(gpass=1; gpass<=1; gpass++)
|
|
{
|
|
//--- zero matrix,several cases
|
|
for(i=0; i<=maxmn-1; i++)
|
|
{
|
|
for(j=0; j<=maxmn-1; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
//--- function calls
|
|
for(i=1; i<=MathMin(5,maxmn); i++)
|
|
{
|
|
for(j=1; j<=MathMin(5,maxmn); j++)
|
|
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
//--- Long dense matrix
|
|
for(i=0; i<=maxmn-1; i++)
|
|
{
|
|
for(j=0; j<=MathMin(5,maxmn)-1; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
for(i=1; i<=maxmn; i++)
|
|
{
|
|
for(j=1; j<=MathMin(5,maxmn); j++)
|
|
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<=MathMin(5,maxmn)-1; i++)
|
|
{
|
|
for(j=0; j<=maxmn-1; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
for(i=1; i<=MathMin(5,maxmn); i++)
|
|
{
|
|
for(j=1; j<=maxmn; j++)
|
|
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
//--- Dense matrices
|
|
for(m=1; m<=MathMin(10,maxmn); m++)
|
|
{
|
|
for(n=1; n<=MathMin(10,maxmn); n++)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
|
|
for(m=1; m<=10; m++)
|
|
{
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=2; pass++)
|
|
{
|
|
//--- function calls
|
|
FillsParseA(a,m,n,0.8);
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
FillsParseA(a,m,n,0.9);
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
FillsParseA(a,m,n,0.95);
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
failr=(double)failcount/(double)(succcount+failcount);
|
|
waserrors=(((materr>threshold||orterr>threshold)||othererr>threshold)||!wsorted)||failr>failthreshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SVD DECOMPOSITION");
|
|
PrintFormat("SVD decomposition error: %5.3E",materr);
|
|
PrintFormat("SVD orthogonality error: %5.3E",orterr);
|
|
PrintFormat("SVD with different parameters error: %5.3E",othererr);
|
|
PrintResult("Singular values order",wsorted);
|
|
PrintFormat("Always converged: %s",(!wfailed ? "YES" : "NO"));
|
|
if(wfailed)
|
|
PrintFormat("Fail ratio: %5.3f",failr);
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSVDUnit::FillsParseA(CMatrixDouble &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- change values
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSVDUnit::GetSVDError(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &u,
|
|
double &w[],CMatrixDouble &vt,
|
|
double &materr,double &orterr,
|
|
bool &wsorted)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int minmn=0;
|
|
double locerr=0;
|
|
double sm=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
//--- decomposition error
|
|
locerr=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
sm=0;
|
|
for(k=0; k<=minmn-1; k++)
|
|
sm=sm+w[k]*u[i][k]*vt[k][j];
|
|
locerr=MathMax(locerr,MathAbs(a.Get(i,j)-sm));
|
|
}
|
|
}
|
|
materr=MathMax(materr,locerr);
|
|
//--- orthogonality error
|
|
locerr=0;
|
|
for(i=0; i<=minmn-1; i++)
|
|
{
|
|
for(j=i; j<=minmn-1; j++)
|
|
{
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
sm+=u.Get(i_,i)*u.Get(i_,j);
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
sm+=vt.Get(i,i_)*vt.Get(j,i_);
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
}
|
|
}
|
|
orterr=MathMax(orterr,locerr);
|
|
//--- values order error
|
|
for(i=1; i<=minmn-1; i++)
|
|
{
|
|
//--- check
|
|
if(w[i]>w[i-1])
|
|
wsorted=false;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSVDUnit::TestSVDProblem(CMatrixDouble &a,const int m,
|
|
const int n,double &materr,
|
|
double &orterr,double &othererr,
|
|
bool &wsorted,bool &wfailed,
|
|
int &failcount,int &succcount)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int ujob=0;
|
|
int vtjob=0;
|
|
int memjob=0;
|
|
int ucheck=0;
|
|
int vtcheck=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double w2[];
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
CMatrixDouble u2;
|
|
CMatrixDouble vt2;
|
|
//--- Main SVD test
|
|
if(!CSingValueDecompose::RMatrixSVD(a,m,n,2,2,2,w,u,vt))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- function call
|
|
GetSVDError(a,m,n,u,w,vt,materr,orterr,wsorted);
|
|
//--- Additional SVD tests
|
|
for(ujob=0; ujob<=2; ujob++)
|
|
{
|
|
for(vtjob=0; vtjob<=2; vtjob++)
|
|
{
|
|
for(memjob=0; memjob<=2; memjob++)
|
|
{
|
|
//--- check
|
|
if(!CSingValueDecompose::RMatrixSVD(a,m,n,ujob,vtjob,memjob,w2,u2,vt2))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- change value
|
|
ucheck=0;
|
|
//--- check
|
|
if(ujob==1)
|
|
ucheck=MathMin(m,n);
|
|
//--- check
|
|
if(ujob==2)
|
|
ucheck=m;
|
|
//--- change value
|
|
vtcheck=0;
|
|
//--- check
|
|
if(vtjob==1)
|
|
vtcheck=MathMin(m,n);
|
|
//--- check
|
|
if(vtjob==2)
|
|
vtcheck=n;
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=ucheck-1; j++)
|
|
othererr=MathMax(othererr,MathAbs(u.Get(i,j)-u2.Get(i,j)));
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=vtcheck-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
othererr=MathMax(othererr,MathAbs(vt.Get(i,j)-vt2.Get(i,j)));
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=MathMin(m,n)-1; i++)
|
|
othererr=MathMax(othererr,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
}
|
|
//--- update counter
|
|
succcount=succcount+1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLinReg |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLinRegUnit
|
|
{
|
|
public:
|
|
static bool TestLinReg(const bool silent);
|
|
static void GenerateRandomTask(const double xl,const double xr,const bool randomx,const double ymin,const double ymax,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]);
|
|
static void GenerateTask(const double a,const double b,const double xl,const double xr,const bool randomx,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]);
|
|
static void FillTaskWithY(const double a,const double b,const int n,CMatrixDouble &xy,double &s[]);
|
|
static double GenerateNormal(const double mean,const double sigma);
|
|
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
|
|
static void UnsetLR(CLinearModel &lr);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLinReg |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinRegUnit::TestLinReg(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double sigmathreshold=0;
|
|
int maxn=0;
|
|
int maxm=0;
|
|
int passcount=0;
|
|
int estpasscount=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int tmpi=0;
|
|
int pass=0;
|
|
int epass=0;
|
|
int m=0;
|
|
int tasktype=0;
|
|
int modeltype=0;
|
|
int m1=0;
|
|
int m2=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double y1=0;
|
|
double y2=0;
|
|
bool allsame;
|
|
double ea=0;
|
|
double eb=0;
|
|
double varatested=0;
|
|
double varbtested=0;
|
|
double a=0;
|
|
double b=0;
|
|
double vara=0;
|
|
double varb=0;
|
|
double a2=0;
|
|
double b2=0;
|
|
double covab=0;
|
|
double corrab=0;
|
|
double p=0;
|
|
int qcnt=0;
|
|
double f=0;
|
|
double fp=0;
|
|
double fm=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double cvrmserror=0;
|
|
double cvavgerror=0;
|
|
double cvavgrelerror=0;
|
|
double rmserror=0;
|
|
double avgerror=0;
|
|
double avgrelerror=0;
|
|
bool nondefect;
|
|
double sinshift=0;
|
|
double tasklevel=0;
|
|
double noiselevel=0;
|
|
double hstep=0;
|
|
double sigma=0;
|
|
double mean=0;
|
|
double means=0;
|
|
double stddev=0;
|
|
double stddevs=0;
|
|
bool slcerrors;
|
|
bool slerrors;
|
|
bool grcoverrors;
|
|
bool gropterrors;
|
|
bool gresterrors;
|
|
bool grothererrors;
|
|
bool grconverrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double s[];
|
|
double s2[];
|
|
double w2[];
|
|
double x[];
|
|
double ta[];
|
|
double tb[];
|
|
double tc[];
|
|
double xy0[];
|
|
double tmpweights[];
|
|
double x1[];
|
|
double x2[];
|
|
double qtbl[];
|
|
double qvals[];
|
|
double qsigma[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
//--- objects of classes
|
|
CLinearModel w;
|
|
CLinearModel wt;
|
|
CLinearModel wt2;
|
|
CLRReport ar;
|
|
CLRReport ar2;
|
|
//--- Primary settings
|
|
maxn=40;
|
|
maxm=5;
|
|
passcount=3;
|
|
estpasscount=1000;
|
|
sigmathreshold=7;
|
|
threshold=1000000*CMath::m_machineepsilon;
|
|
slerrors=false;
|
|
slcerrors=false;
|
|
grcoverrors=false;
|
|
gropterrors=false;
|
|
gresterrors=false;
|
|
grothererrors=false;
|
|
grconverrors=false;
|
|
waserrors=false;
|
|
//--- Quantiles table setup
|
|
qcnt=5;
|
|
ArrayResize(qtbl,qcnt);
|
|
ArrayResize(qvals,qcnt);
|
|
ArrayResize(qsigma,qcnt);
|
|
qtbl[0]=0.5;
|
|
qtbl[1]=0.25;
|
|
qtbl[2]=0.10;
|
|
qtbl[3]=0.05;
|
|
qtbl[4]=0.025;
|
|
for(i=0; i<=qcnt-1; i++)
|
|
qsigma[i]=MathSqrt(qtbl[i]*(1-qtbl[i])/estpasscount);
|
|
//--- Other setup
|
|
ArrayResize(ta,estpasscount);
|
|
ArrayResize(tb,estpasscount);
|
|
//--- Test straight line regression
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- Fail/pass test
|
|
GenerateRandomTask(-1,1,false,-1,1,1,2,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors||info!=1;
|
|
//--- function calls
|
|
GenerateRandomTask(1,1,false,-1,1,1,2,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors||info!=-3;
|
|
//--- function calls
|
|
GenerateRandomTask(-1,1,false,-1,1,-1,-1,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors||info!=-2;
|
|
//--- function calls
|
|
GenerateRandomTask(-1,1,false,-1,1,2,1,2,xy,s);
|
|
CLinReg::LRLines(xy,s,1,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors||info!=-1;
|
|
//--- Multipass tests
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Test S variant against non-S variant
|
|
ea=2*CMath::RandomReal()-1;
|
|
eb=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),CMath::RandomReal()>0.5,1,1,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
CLinReg::LRLine(xy,n,info2,a2,b2);
|
|
//--- check
|
|
if(info!=1 || info2!=1)
|
|
slcerrors=true;
|
|
else
|
|
slerrors=(slerrors||MathAbs(a-a2)>threshold)||MathAbs(b-b2)>threshold;
|
|
//--- Test for A/B
|
|
//--- Generate task with exact,non-perturbed y[i],
|
|
//--- then make non-zero s[i]
|
|
ea=2*CMath::RandomReal()-1;
|
|
eb=2*CMath::RandomReal()-1;
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,0.0,0.0,n,xy,s);
|
|
for(i=0; i<n; i++)
|
|
s[i]=CMath::RandomReal()+1;
|
|
//--- function call
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
slcerrors=true;
|
|
else
|
|
slerrors=(slerrors||MathAbs(a-ea)>0.001)||MathAbs(b-eb)>0.001;
|
|
//--- Test for VarA,VarB,P (P is being tested only for N>2)
|
|
for(i=0; i<=qcnt-1; i++)
|
|
qvals[i]=0;
|
|
ea=2*CMath::RandomReal()-1;
|
|
eb=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,1.0,2.0,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- change values
|
|
varatested=vara;
|
|
varbtested=varb;
|
|
//--- calculation
|
|
for(epass=0; epass<=estpasscount-1; epass++)
|
|
{
|
|
//--- Generate
|
|
FillTaskWithY(ea,eb,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- A,B,P
|
|
//--- (P is being tested for uniformity,additional p-tests are below)
|
|
ta[epass]=a;
|
|
tb[epass]=b;
|
|
for(i=0; i<=qcnt-1; i++)
|
|
{
|
|
//--- check
|
|
if(p<=qtbl[i])
|
|
qvals[i]=qvals[i]+1.0/(double)estpasscount;
|
|
}
|
|
}
|
|
//--- function call
|
|
CalculateMV(ta,estpasscount,mean,means,stddev,stddevs);
|
|
//--- search errors
|
|
slerrors=slerrors||MathAbs(mean-ea)/means>=sigmathreshold;
|
|
slerrors=slerrors||MathAbs(stddev-MathSqrt(varatested))/stddevs>=sigmathreshold;
|
|
//--- function call
|
|
CalculateMV(tb,estpasscount,mean,means,stddev,stddevs);
|
|
//--- search errors
|
|
slerrors=slerrors||MathAbs(mean-eb)/means>=sigmathreshold;
|
|
slerrors=slerrors||MathAbs(stddev-MathSqrt(varbtested))/stddevs>=sigmathreshold;
|
|
//--- check
|
|
if(n>2)
|
|
{
|
|
for(i=0; i<=qcnt-1; i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(qtbl[i]-qvals[i])/qsigma[i]>sigmathreshold)
|
|
slerrors=true;
|
|
}
|
|
}
|
|
//--- Additional tests for P: correlation with fit quality
|
|
if(n>2)
|
|
{
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,0.0,0.0,n,xy,s);
|
|
for(i=0; i<n; i++)
|
|
s[i]=1+CMath::RandomReal();
|
|
//--- function call
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
slerrors=slerrors||p<(double)(0.999);
|
|
//--- function call
|
|
GenerateTask(0,0,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,1.0,1.0,n,xy,s);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i%2==0)
|
|
xy.Set(i,1,5.0);
|
|
else
|
|
xy.Set(i,1,-5.0);
|
|
}
|
|
//--- check
|
|
if(n%2!=0)
|
|
xy.Set(n-1,1,0);
|
|
//--- function call
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
slerrors=slerrors||p>0.001;
|
|
}
|
|
}
|
|
}
|
|
//--- General regression tests:
|
|
//--- Simple linear tests (small sample,optimum point,covariance)
|
|
for(n=3; n<=maxn; n++)
|
|
{
|
|
ArrayResize(s,n);
|
|
//--- Linear tests:
|
|
//--- a. random points,sigmas
|
|
//--- b. no sigmas
|
|
xy.Resize(n,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(i,0,2*CMath::RandomReal()-1);
|
|
xy.Set(i,1,2*CMath::RandomReal()-1);
|
|
s[i]=1+CMath::RandomReal();
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuildS(xy,s,n,1,info,wt,ar);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
//--- search errors
|
|
CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p);
|
|
gropterrors=gropterrors||MathAbs(a-tmpweights[1])>threshold;
|
|
gropterrors=gropterrors||MathAbs(b-tmpweights[0])>threshold;
|
|
grcoverrors=grcoverrors||MathAbs(vara-ar.m_c[1][1])>threshold;
|
|
grcoverrors=grcoverrors||MathAbs(varb-ar.m_c[0][0])>threshold;
|
|
grcoverrors=grcoverrors||MathAbs(covab-ar.m_c[1][0])>threshold;
|
|
grcoverrors=grcoverrors||MathAbs(covab-ar.m_c[0][1])>threshold;
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,n,1,info,wt,ar);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
CLinReg::LRLine(xy,n,info2,a,b);
|
|
//--- search errors
|
|
gropterrors=gropterrors||MathAbs(a-tmpweights[1])>threshold;
|
|
gropterrors=gropterrors||MathAbs(b-tmpweights[0])>threshold;
|
|
}
|
|
//--- S covariance versus S-less covariance.
|
|
//--- Slightly skewed task,large sample size.
|
|
//--- Will S-less subroutine estimate covariance matrix good enough?
|
|
n=1000+CMath::RandomInteger(3000);
|
|
sigma=0.1+CMath::RandomReal()*1.9;
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
ArrayResize(s,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(i,0,1.5*CMath::RandomReal()-0.5);
|
|
xy.Set(i,1,1.2*xy[i][0]-0.3+GenerateNormal(0,sigma));
|
|
s[i]=sigma;
|
|
}
|
|
//--- function calls
|
|
CLinReg::LRBuild(xy,n,1,info,wt,ar);
|
|
CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1 || info2!=1)
|
|
grconverrors=true;
|
|
else
|
|
{
|
|
grcoverrors=grcoverrors||MathAbs(MathLog(ar.m_c[0][0]/varb))>MathLog(1.2);
|
|
grcoverrors=grcoverrors||MathAbs(MathLog(ar.m_c[1][1]/vara))>MathLog(1.2);
|
|
grcoverrors=grcoverrors||MathAbs(MathLog(ar.m_c[0][1]/covab))>MathLog(1.2);
|
|
grcoverrors=grcoverrors||MathAbs(MathLog(ar.m_c[1][0]/covab))>MathLog(1.2);
|
|
}
|
|
//--- General tests:
|
|
//--- * basis functions - up to cubic
|
|
//--- * task types:
|
|
//--- * data set is noisy sine half-period with random shift
|
|
//--- * tests:
|
|
//--- unpacking/packing
|
|
//--- optimality
|
|
//--- error estimates
|
|
//--- * tasks:
|
|
//--- 0=noised sine
|
|
//--- 1=degenerate task with 1-of-n encoded categorical variables
|
|
//--- 2=random task with large variation (for 1-type models)
|
|
//--- 3=random task with small variation (for 1-type models)
|
|
//--- Additional tasks TODO
|
|
//--- specially designed task with defective vectors which leads to
|
|
//--- the failure of the fast CV formula.
|
|
for(modeltype=0; modeltype<=1; modeltype++)
|
|
{
|
|
for(tasktype=0; tasktype<=3; tasktype++)
|
|
{
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
m1=1;
|
|
m2=3;
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
m1=9;
|
|
m2=9;
|
|
}
|
|
//--- check
|
|
if(tasktype==2 || tasktype==3)
|
|
{
|
|
m1=9;
|
|
m2=9;
|
|
}
|
|
//--- calculation
|
|
for(m=m1; m<=m2; m++)
|
|
{
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
n1=m+3;
|
|
n2=m+20;
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
n1=70+CMath::RandomInteger(70);
|
|
n2=n1;
|
|
}
|
|
//--- check
|
|
if(tasktype==2 || tasktype==3)
|
|
{
|
|
n1=100;
|
|
n2=n1;
|
|
}
|
|
for(n=n1; n<=n2; n++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(n,m+1);
|
|
ArrayResize(xy0,n);
|
|
ArrayResize(s,n);
|
|
hstep=0.001;
|
|
noiselevel=0.2;
|
|
//--- Prepare task
|
|
if(tasktype==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,0,2*CMath::RandomReal()-1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=1; j<m; j++)
|
|
xy.Set(i,j,xy[i][0]*xy[i][j-1]);
|
|
}
|
|
sinshift=CMath::RandomReal()*M_PI;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy0[i]=MathSin(sinshift+M_PI*0.5*(xy[i][0]+1));
|
|
xy.Set(i,m,xy0[i]+noiselevel*GenerateNormal(0,1));
|
|
}
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m==9))
|
|
return(false);
|
|
//--- allocation
|
|
ArrayResize(ta,9);
|
|
//--- change values
|
|
ta[0]=1;
|
|
ta[1]=2;
|
|
ta[2]=3;
|
|
ta[3]=0.25;
|
|
ta[4]=0.5;
|
|
ta[5]=0.75;
|
|
ta[6]=0.06;
|
|
ta[7]=0.12;
|
|
ta[8]=0.18;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xy.Set(i,j,0);
|
|
xy.Set(i,i%3,1);
|
|
xy.Set(i,3+i/3%3,1);
|
|
xy.Set(i,6+i/9%3,1);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=8; i_++)
|
|
v+=xy.Get(i,i_)*ta[i_];
|
|
xy0[i]=v;
|
|
xy.Set(i,m,v+noiselevel*GenerateNormal(0,1));
|
|
}
|
|
}
|
|
//--- check
|
|
if(tasktype==2 || tasktype==3)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m==9))
|
|
return(false);
|
|
//--- allocation
|
|
ArrayResize(ta,9);
|
|
//--- change values
|
|
ta[0]=1;
|
|
ta[1]=-2;
|
|
ta[2]=3;
|
|
ta[3]=0.25;
|
|
ta[4]=-0.5;
|
|
ta[5]=0.75;
|
|
ta[6]=-0.06;
|
|
ta[7]=0.12;
|
|
ta[8]=-0.18;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- check
|
|
if(tasktype==2)
|
|
xy.Set(i,j,1+GenerateNormal(0,3));
|
|
else
|
|
xy.Set(i,j,1+GenerateNormal(0,0.05));
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=8; i_++)
|
|
v+=xy.Get(i,i_)*ta[i_];
|
|
xy0[i]=v;
|
|
xy.Set(i,m,v+noiselevel*GenerateNormal(0,1));
|
|
}
|
|
}
|
|
for(i=0; i<n; i++)
|
|
s[i]=1+CMath::RandomReal();
|
|
//--- Solve (using S-variant,non-S-variant is not tested)
|
|
if(modeltype==0)
|
|
CLinReg::LRBuildS(xy,s,n,m,info,wt,ar);
|
|
else
|
|
CLinReg::LRBuildZS(xy,s,n,m,info,wt,ar);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
//--- LRProcess test
|
|
ArrayResize(x,m);
|
|
//--- change values
|
|
v=tmpweights[m];
|
|
for(i=0; i<m; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v+=tmpweights[i]*x[i];
|
|
}
|
|
//--- search errors
|
|
grothererrors=grothererrors||MathAbs(v-CLinReg::LRProcess(wt,x))/MathMax(MathAbs(v),1)>threshold;
|
|
//--- LRPack test
|
|
CLinReg::LRPack(tmpweights,m,wt2);
|
|
ArrayResize(x,m);
|
|
for(i=0; i<m; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v=CLinReg::LRProcess(wt,x);
|
|
grothererrors=grothererrors||MathAbs(v-CLinReg::LRProcess(wt2,x))/MathAbs(v)>threshold;
|
|
//--- Optimality test
|
|
for(k=0; k<=m; k++)
|
|
{
|
|
//--- check
|
|
if(modeltype==1 && k==m)
|
|
{
|
|
//--- 0-type models (with non-zero constant term)
|
|
//--- are tested for optimality of all coefficients.
|
|
//--- 1-type models (with zero constant term)
|
|
//--- are tested for optimality of non-constant terms only.
|
|
continue;
|
|
}
|
|
//--- change values
|
|
f=0;
|
|
fp=0;
|
|
fm=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=tmpweights[m];
|
|
for(j=0; j<m; j++)
|
|
v+=xy.Get(i,j)*tmpweights[j];
|
|
f=f+CMath::Sqr((v-xy[i][m])/s[i]);
|
|
//--- check
|
|
if(k<m)
|
|
vv=xy[i][k];
|
|
else
|
|
vv=1;
|
|
//--- calculation
|
|
fp=fp+CMath::Sqr((v+vv*hstep-xy[i][m])/s[i]);
|
|
fm=fm+CMath::Sqr((v-vv*hstep-xy[i][m])/s[i]);
|
|
}
|
|
//--- search errors
|
|
gropterrors=(gropterrors||f>fp)||f>fm;
|
|
}
|
|
//--- Covariance matrix test:
|
|
//--- generate random vector,project coefficients on it,
|
|
//--- compare variance of projection with estimate provided
|
|
//--- by cov.matrix
|
|
ArrayResize(ta,estpasscount);
|
|
ArrayResize(tb,m+1);
|
|
ArrayResize(tc,m+1);
|
|
xy2.Resize(n,m+1);
|
|
for(i=0; i<=m; i++)
|
|
tb[i]=GenerateNormal(0,1);
|
|
//--- calculation
|
|
for(epass=0; epass<=estpasscount-1; epass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<m; i_++)
|
|
xy2.Set(i,i_,xy.Get(i,i_));
|
|
xy2.Set(i,m,xy0[i]+s[i]*GenerateNormal(0,1));
|
|
}
|
|
//--- check
|
|
if(modeltype==0)
|
|
CLinReg::LRBuildS(xy2,s,n,m,info,wt,ar2);
|
|
else
|
|
CLinReg::LRBuildZS(xy2,s,n,m,info,wt,ar2);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
ta[epass]=0;
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CLinReg::LRUnpack(wt,w2,tmpi);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=m; i_++)
|
|
v+=tb[i_]*w2[i_];
|
|
ta[epass]=v;
|
|
}
|
|
//--- function call
|
|
CalculateMV(ta,estpasscount,mean,means,stddev,stddevs);
|
|
for(i=0; i<=m; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=m; i_++)
|
|
v+=tb[i_]*ar.m_c.Get(i_,i);
|
|
tc[i]=v;
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=m; i_++)
|
|
v+=tc[i_]*tb[i_];
|
|
//--- search errors
|
|
grcoverrors=grcoverrors||MathAbs((MathSqrt(v)-stddev)/stddevs)>=sigmathreshold;
|
|
//--- Test for the fast CV error:
|
|
//--- calculate CV error by definition (leaving out N
|
|
//--- points and recalculating solution).
|
|
//--- Test for the training set error
|
|
cvrmserror=0;
|
|
cvavgerror=0;
|
|
cvavgrelerror=0;
|
|
rmserror=0;
|
|
avgerror=0;
|
|
avgrelerror=0;
|
|
xy2.Resize(n-1,m+1);
|
|
ArrayResize(s2,n-1);
|
|
//--- change values
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
for(i_=0; i_<=m; i_++)
|
|
xy2.Set(i,i_,xy[i+1][i_]);
|
|
s2[i]=s[i+1];
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- Trn
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=xy.Get(i,i_)*tmpweights[i_];
|
|
v+=tmpweights[m];
|
|
//--- search errors
|
|
rmserror=rmserror+CMath::Sqr(v-xy[i][m]);
|
|
avgerror=avgerror+MathAbs(v-xy[i][m]);
|
|
avgrelerror=avgrelerror+MathAbs((v-xy[i][m])/xy[i][m]);
|
|
//--- CV: non-defect vectors only
|
|
nondefect=true;
|
|
for(k=0; k<=ar.m_ncvdefects-1; k++)
|
|
{
|
|
//--- check
|
|
if(ar.m_cvdefects[k]==i)
|
|
nondefect=false;
|
|
}
|
|
//--- check
|
|
if(nondefect)
|
|
{
|
|
//--- check
|
|
if(modeltype==0)
|
|
CLinReg::LRBuildS(xy2,s2,n-1,m,info2,wt,ar2);
|
|
else
|
|
CLinReg::LRBuildZS(xy2,s2,n-1,m,info2,wt,ar2);
|
|
//--- check
|
|
if(info2!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CLinReg::LRUnpack(wt,w2,tmpi);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=xy.Get(i,i_)*w2[i_];
|
|
v+=w2[m];
|
|
//--- search errors
|
|
cvrmserror=cvrmserror+CMath::Sqr(v-xy[i][m]);
|
|
cvavgerror=cvavgerror+MathAbs(v-xy[i][m]);
|
|
cvavgrelerror=cvavgrelerror+MathAbs((v-xy[i][m])/xy[i][m]);
|
|
}
|
|
//--- Next set
|
|
if(i!=n-1)
|
|
{
|
|
for(i_=0; i_<=m; i_++)
|
|
xy2.Set(i,i_,xy.Get(i,i_));
|
|
s2[i]=s[i];
|
|
}
|
|
}
|
|
//--- search errors
|
|
cvrmserror=MathSqrt(cvrmserror/(n-ar.m_ncvdefects));
|
|
cvavgerror=cvavgerror/(n-ar.m_ncvdefects);
|
|
cvavgrelerror=cvavgrelerror/(n-ar.m_ncvdefects);
|
|
rmserror=MathSqrt(rmserror/n);
|
|
avgerror=avgerror/n;
|
|
avgrelerror=avgrelerror/n;
|
|
gresterrors=gresterrors||MathAbs(MathLog(ar.m_cvrmserror/cvrmserror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors||MathAbs(MathLog(ar.m_cvavgerror/cvavgerror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors||MathAbs(MathLog(ar.m_cvavgrelerror/cvavgrelerror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors||MathAbs(MathLog(ar.m_RMSError/rmserror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors||MathAbs(MathLog(ar.m_AvgError/avgerror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors||MathAbs(MathLog(ar.m_AvgRelError/avgrelerror))>MathLog(1+1.0E-5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Additional subroutines
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
n=2;
|
|
//--- cycle
|
|
do
|
|
{
|
|
noiselevel=CMath::RandomReal()+0.1;
|
|
tasklevel=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(noiselevel-tasklevel)<=0.05);
|
|
//--- allocation
|
|
xy.Resize(3*n,2);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(3*i,0,i);
|
|
xy.Set(3*i+1,0,i);
|
|
xy.Set(3*i+2,0,i);
|
|
xy.Set(3*i,1,tasklevel-noiselevel);
|
|
xy.Set(3*i+1,1,tasklevel);
|
|
xy.Set(3*i+2,1,tasklevel+noiselevel);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,3*n,1,info,wt,ar);
|
|
//--- check
|
|
if(info==1)
|
|
{
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
v=CLinReg::LRRMSError(wt,xy,3*n);
|
|
//--- search errors
|
|
grothererrors=grothererrors||MathAbs(v-noiselevel*MathSqrt(2.0/3.0))>threshold;
|
|
//--- function call
|
|
v=CLinReg::LRAvgError(wt,xy,3*n);
|
|
//--- search errors
|
|
grothererrors=grothererrors||MathAbs(v-noiselevel*(2.0/3.0))>threshold;
|
|
//--- function call
|
|
v=CLinReg::LRAvgRelError(wt,xy,3*n);
|
|
vv=(MathAbs(noiselevel/(tasklevel-noiselevel))+MathAbs(noiselevel/(tasklevel+noiselevel)))/3;
|
|
//--- search errors
|
|
grothererrors=grothererrors||MathAbs(v-vv)>threshold*vv;
|
|
}
|
|
else
|
|
grothererrors=true;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(3*i,0,i);
|
|
xy.Set(3*i+1,0,i);
|
|
xy.Set(3*i+2,0,i);
|
|
xy.Set(3*i,1,-noiselevel);
|
|
xy.Set(3*i+1,1,0);
|
|
xy.Set(3*i+2,1,noiselevel);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,3*n,1,info,wt,ar);
|
|
//--- check
|
|
if(info==1)
|
|
{
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
v=CLinReg::LRAvgRelError(wt,xy,3*n);
|
|
//--- search errors
|
|
grothererrors=grothererrors||MathAbs(v-1)>threshold;
|
|
}
|
|
else
|
|
grothererrors=true;
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
m=1+CMath::RandomInteger(5);
|
|
n=10+CMath::RandomInteger(10);
|
|
//--- allocation
|
|
xy.Resize(n,m+1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m; j++)
|
|
xy.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,n,m,info,w,ar);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
grothererrors=true;
|
|
break;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x1,m);
|
|
ArrayResize(x2,m);
|
|
//--- Same inputs on original leads to same outputs
|
|
//--- on copy created using LRCopy
|
|
UnsetLR(wt);
|
|
CLinReg::LRCopy(w,wt);
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
//--- change values
|
|
y1=CLinReg::LRProcess(w,x1);
|
|
y2=CLinReg::LRProcess(wt,x2);
|
|
allsame=y1==y2;
|
|
//--- search errors
|
|
grothererrors=grothererrors||!allsame;
|
|
}
|
|
//--- TODO: Degenerate tests (when design matrix and right part are zero)
|
|
//--- Final report
|
|
waserrors=(((((slerrors||slcerrors)||gropterrors)||grcoverrors)||gresterrors)||grothererrors)||grconverrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("REGRESSION TEST");
|
|
PrintResult("STRAIGHT LINE REGRESSION",!slerrors);
|
|
PrintResult("STRAIGHT LINE REGRESSION CONVERGENCE",!slcerrors);
|
|
PrintResult("GENERAL LINEAR REGRESSION",!(gropterrors || grcoverrors || gresterrors || grothererrors || grconverrors));
|
|
PrintResult("* OPTIMALITY",!gropterrors);
|
|
PrintResult("* COV. MATRIX",!grcoverrors);
|
|
PrintResult("* ERROR ESTIMATES",!gresterrors);
|
|
PrintResult("* CONVERGENCE",!grconverrors);
|
|
PrintResult("* OTHER SUBROUTINES",!grothererrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Task generation. Meaningless task,just random numbers. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinRegUnit::GenerateRandomTask(const double xl,const double xr,
|
|
const bool randomx,const double ymin,
|
|
const double ymax,const double smin,
|
|
const double smax,const int n,
|
|
CMatrixDouble &xy,double &s[])
|
|
{
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
ArrayResize(s,n);
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(randomx)
|
|
xy.Set(i,0,xl+(xr-xl)*CMath::RandomReal());
|
|
else
|
|
xy.Set(i,0,xl+(xr-xl)*i/(n-1));
|
|
//--- calculation
|
|
xy.Set(i,1,ymin+(ymax-ymin)*CMath::RandomReal());
|
|
s[i]=smin+(smax-smin)*CMath::RandomReal();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Task generation. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinRegUnit::GenerateTask(const double a,const double b,
|
|
const double xl,const double xr,
|
|
const bool randomx,const double smin,
|
|
const double smax,const int n,
|
|
CMatrixDouble &xy,double &s[])
|
|
{
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
ArrayResize(s,n);
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(randomx)
|
|
xy.Set(i,0,xl+(xr-xl)*CMath::RandomReal());
|
|
else
|
|
xy.Set(i,0,xl+(xr-xl)*i/(n-1));
|
|
//--- change values
|
|
s[i]=smin+(smax-smin)*CMath::RandomReal();
|
|
xy.Set(i,1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Task generation. |
|
|
//| y[i] are filled based on A,B,X[I],S[I] |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinRegUnit::FillTaskWithY(const double a,const double b,
|
|
const int n,CMatrixDouble &xy,
|
|
double &s[])
|
|
{
|
|
//--- change values
|
|
for(int i=0; i<n; i++)
|
|
xy.Set(i,1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Normal random numbers |
|
|
//+------------------------------------------------------------------+
|
|
double CTestLinRegUnit::GenerateNormal(const double mean,const double sigma)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double sum=0;
|
|
//--- initialization
|
|
result=mean;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- change values
|
|
u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
sum=u*u+v*v;
|
|
//--- check
|
|
if(sum<1.0 && sum>0.0)
|
|
{
|
|
sum=MathSqrt(-(2*MathLog(sum)/sum));
|
|
result=sigma*u*sum+mean;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Moments estimates and their errors |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinRegUnit::CalculateMV(double &x[],const int n,double &mean,
|
|
double &means,double &stddev,
|
|
double &stddevs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double variance=0;
|
|
//--- initialization
|
|
mean=0;
|
|
means=1;
|
|
stddev=0;
|
|
stddevs=1;
|
|
variance=0;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- Mean
|
|
for(i=0; i<n; i++)
|
|
mean=mean+x[i];
|
|
mean=mean/n;
|
|
//--- Variance (using corrected two-pass algorithm)
|
|
if(n!=1)
|
|
{
|
|
//--- change value
|
|
v1=0;
|
|
for(i=0; i<n; i++)
|
|
v1+=CMath::Sqr(x[i]-mean);
|
|
//--- change value
|
|
v2=0;
|
|
for(i=0; i<n; i++)
|
|
v2=v2+(x[i]-mean);
|
|
v2=CMath::Sqr(v2)/n;
|
|
//--- calculation
|
|
variance=(v1-v2)/(n-1);
|
|
//--- check
|
|
if(variance<0.0)
|
|
variance=0;
|
|
stddev=MathSqrt(variance);
|
|
}
|
|
//--- Errors
|
|
means=stddev/MathSqrt(n);
|
|
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets LR |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinRegUnit::UnsetLR(CLinearModel &lr)
|
|
{
|
|
int info=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CLRReport rep;
|
|
//--- allocation
|
|
xy.Resize(6,2);
|
|
//--- change values
|
|
for(int i=0; i<=5; i++)
|
|
{
|
|
xy.Set(i,0,0);
|
|
xy.Set(i,1,0);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,6,1,info,lr,rep);
|
|
//--- check
|
|
if(!CAp::Assert(info>0))
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CXblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestXBlasUnit
|
|
{
|
|
public:
|
|
static bool TestXBlas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CXblas |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestXBlasUnit::TestXBlas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool approxerrors;
|
|
bool exactnesserrors;
|
|
bool waserrors;
|
|
double approxthreshold=0;
|
|
int maxn=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int i=0;
|
|
int pass=0;
|
|
double rv1=0;
|
|
double rv2=0;
|
|
double rv2err=0;
|
|
complex cv1=0;
|
|
complex cv2=0;
|
|
double cv2err=0;
|
|
double s=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double rx[];
|
|
double ry[];
|
|
complex cx[];
|
|
complex cy[];
|
|
double temp[];
|
|
//--- initialization
|
|
approxerrors=false;
|
|
exactnesserrors=false;
|
|
waserrors=false;
|
|
approxthreshold=1000*CMath::m_machineepsilon;
|
|
maxn=1000;
|
|
passcount=10;
|
|
//--- tests:
|
|
//--- 1. ability to calculate dot product
|
|
//--- 2. higher precision
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- ability to approximately calculate real dot product
|
|
ArrayResize(rx,n);
|
|
ArrayResize(ry,n);
|
|
ArrayResize(temp,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
rx[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
rx[i]=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
ry[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
ry[i]=0;
|
|
}
|
|
//--- change value
|
|
rv1=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
rv1+=rx[i_]*ry[i_];
|
|
//--- function call
|
|
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
|
|
//--- search errors
|
|
approxerrors=approxerrors||MathAbs(rv1-rv2)>approxthreshold;
|
|
//--- ability to approximately calculate complex dot product
|
|
ArrayResize(cx,n);
|
|
ArrayResize(cy,n);
|
|
ArrayResize(temp,2*n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
{
|
|
cx[i].real=2*CMath::RandomReal()-1;
|
|
cx[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
cx[i]=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
{
|
|
cy[i].real=2*CMath::RandomReal()-1;
|
|
cy[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
cy[i]=0;
|
|
}
|
|
//--- change value
|
|
cv1=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
cv1+=cx[i_]*cy[i_];
|
|
//--- function call
|
|
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
|
|
//--- search errors
|
|
approxerrors=approxerrors||CMath::AbsComplex(cv1-cv2)>approxthreshold;
|
|
}
|
|
}
|
|
//--- test of precision: real
|
|
n=50000;
|
|
ArrayResize(rx,n);
|
|
ArrayResize(ry,n);
|
|
ArrayResize(temp,n);
|
|
//--- calculation
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n%2==0))
|
|
return(false);
|
|
//--- First test: X + X + ... + X - X - X - ... - X=1*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1; i<n; i++)
|
|
ry[i]=ry[0];
|
|
for(i=0; i<=n/2-1; i++)
|
|
rx[i]=1;
|
|
for(i=n/2; i<=n-2; i++)
|
|
rx[i]=-1;
|
|
rx[n-1]=0;
|
|
//--- function call
|
|
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors||rv2err<0.0;
|
|
exactnesserrors=exactnesserrors||rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0]);
|
|
exactnesserrors=exactnesserrors||MathAbs(rv2-ry[0])>rv2err;
|
|
//--- First test: X + X + ... + X=N*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1; i<n; i++)
|
|
ry[i]=ry[0];
|
|
for(i=0; i<n; i++)
|
|
rx[i]=01;
|
|
//--- function call
|
|
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors||rv2err<0.0;
|
|
exactnesserrors=exactnesserrors||rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0])*n;
|
|
exactnesserrors=exactnesserrors||MathAbs(rv2-n*ry[0])>rv2err;
|
|
}
|
|
//--- test of precision: complex
|
|
n=50000;
|
|
ArrayResize(cx,n);
|
|
ArrayResize(cy,n);
|
|
ArrayResize(temp,2*n);
|
|
//--- calculation
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n%2==0))
|
|
return(false);
|
|
//--- First test: X + X + ... + X - X - X - ... - X=1*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
cy[0].real=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
cy[0].imag=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1; i<n; i++)
|
|
cy[i]=cy[0];
|
|
for(i=0; i<=n/2-1; i++)
|
|
cx[i]=1;
|
|
for(i=n/2; i<=n-2; i++)
|
|
cx[i]=-1;
|
|
cx[n-1]=0;
|
|
//--- function call
|
|
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors||cv2err<0.0;
|
|
exactnesserrors=exactnesserrors||cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0]);
|
|
exactnesserrors=exactnesserrors||CMath::AbsComplex(cv2-cy[0])>cv2err;
|
|
//--- First test: X + X + ... + X=N*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
cy[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1; i<n; i++)
|
|
cy[i]=cy[0];
|
|
for(i=0; i<n; i++)
|
|
cx[i]=1;
|
|
//--- function call
|
|
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors||cv2err<0.0;
|
|
exactnesserrors=exactnesserrors||cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0])*n;
|
|
exactnesserrors=exactnesserrors||CMath::AbsComplex(cv2-cy[0]*n)>cv2err;
|
|
}
|
|
//--- report
|
|
waserrors=approxerrors||exactnesserrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING XBLAS");
|
|
PrintResult("APPROX.TESTS",!approxerrors);
|
|
PrintResult("EXACT TESTS",!exactnesserrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CDenseSolver |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDenseSolverUnit
|
|
{
|
|
public:
|
|
static bool TestDenseSolver(const bool silent);
|
|
|
|
private:
|
|
static bool RMatrixCheckSolutionM(CMatrixDouble &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixDouble &xs);
|
|
static bool RMatrixCheckSolution(CMatrixDouble &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,double &xs[]);
|
|
static bool RMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixDouble &xs);
|
|
static bool RMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,double &xs[]);
|
|
static bool CMatrixCheckSolutionM(CMatrixComplex &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixComplex &xs);
|
|
static bool CMatrixCheckSolution(CMatrixComplex &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,complex &xs[]);
|
|
static bool CMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixComplex &xs);
|
|
static bool CMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,complex &xs[]);
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
|
|
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
|
|
static void TestRSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &rerrors,bool &rfserrors);
|
|
static void TestSPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &spderrors,bool &rfserrors);
|
|
static void TestCSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &cerrors,bool &rfserrors);
|
|
static void TestHPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &hpderrors,bool &rfserrors);
|
|
static void Unset2D(CMatrixDouble &x);
|
|
static void Unset1D(double &x[]);
|
|
static void CUnset2D(CMatrixComplex &x);
|
|
static void CUnset1D(complex &x[]);
|
|
static void UnsetRep(CDenseSolverReport &r);
|
|
static void UnsetLSRep(CDenseSolverLSReport &r);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::TestDenseSolver(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxn=0;
|
|
int maxm=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
bool rerrors;
|
|
bool cerrors;
|
|
bool spderrors;
|
|
bool hpderrors;
|
|
bool rfserrors;
|
|
bool waserrors;
|
|
//--- initialization
|
|
maxn=10;
|
|
maxm=5;
|
|
passcount=5;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
rfserrors=false;
|
|
rerrors=false;
|
|
cerrors=false;
|
|
spderrors=false;
|
|
hpderrors=false;
|
|
//--- function calls
|
|
TestRSolver(maxn,maxm,passcount,threshold,rerrors,rfserrors);
|
|
TestSPDSolver(maxn,maxm,passcount,threshold,spderrors,rfserrors);
|
|
TestCSolver(maxn,maxm,passcount,threshold,cerrors,rfserrors);
|
|
TestHPDSolver(maxn,maxm,passcount,threshold,hpderrors,rfserrors);
|
|
//--- search errors
|
|
waserrors=(((rerrors||cerrors)||spderrors)||hpderrors)||rfserrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING DENSE SOLVER");
|
|
PrintResult("* REAL",!rerrors);
|
|
PrintResult("* COMPLEX",!cerrors);
|
|
PrintResult("* SPD",!spderrors);
|
|
PrintResult("* HPD",!hpderrors);
|
|
PrintResult("* ITERATIVE IMPROVEMENT",!rfserrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::RMatrixCheckSolutionM(CMatrixDouble &xe,
|
|
const int n,const int m,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &xs)
|
|
{
|
|
bool result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<m; j++)
|
|
result=result && MathAbs(xe.Get(i,j)-xs.Get(i,j))<=threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::RMatrixCheckSolution(CMatrixDouble &xe,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
double &xs[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(int i_=0; i_<n; i_++)
|
|
xsm.Set(i_,0,xs[i_]);
|
|
//--- return result
|
|
return(RMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::RMatrixCheckSingularM(const int n,
|
|
const int m,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &xs)
|
|
{
|
|
bool result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<m; j++)
|
|
result=result && xs.Get(i,j)==0.0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::RMatrixCheckSingular(const int n,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
double &xs[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(int i_=0; i_<n; i_++)
|
|
xsm.Set(i_,0,xs[i_]);
|
|
//--- return result
|
|
return(RMatrixCheckSingularM(n,1,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::CMatrixCheckSolutionM(CMatrixComplex &xe,
|
|
const int n,const int m,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &xs)
|
|
{
|
|
bool result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<m; j++)
|
|
result=result && CMath::AbsComplex(xe.Get(i,j)-xs.Get(i,j))<=threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::CMatrixCheckSolution(CMatrixComplex &xe,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
complex &xs[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(int i_=0; i_<n; i_++)
|
|
xsm.Set(i_,0,xs[i_]);
|
|
//--- return result
|
|
return(CMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::CMatrixCheckSingularM(const int n,
|
|
const int m,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &xs)
|
|
{
|
|
bool result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<m; j++)
|
|
result=result && xs.Get(i,j)==0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDenseSolverUnit::CMatrixCheckSingular(const int n,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
complex &xs[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(int i_=0; i_<n; i_++)
|
|
xsm.Set(i_,0,xs[i_]);
|
|
//--- return result
|
|
return(CMatrixCheckSingularM(n,1,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::RMatrixMakeACopy(CMatrixDouble &a,
|
|
const int m,
|
|
const int n,
|
|
CMatrixDouble &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::CMatrixMakeACopy(CMatrixComplex &a,
|
|
const int m,
|
|
const int n,
|
|
CMatrixComplex &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::RMatrixDropHalf(CMatrixDouble &a,
|
|
const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::CMatrixDropHalf(CMatrixComplex &a,
|
|
const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a.Set(i,j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::TestRSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &rerrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
double v=0;
|
|
double verr=0;
|
|
int info=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double bv[];
|
|
double xv[];
|
|
double y[];
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble lua;
|
|
CMatrixDouble atmp;
|
|
CMatrixDouble xe;
|
|
CMatrixDouble b;
|
|
CMatrixDouble x;
|
|
//--- objects of classes
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::RMatrixRndCond(n,1000,a);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xe.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- Test DenseSolverRLS():
|
|
//--- * test on original system A*x=b
|
|
//--- * test on overdetermined system with the same solution: (A' A')'*x=(b' b')'
|
|
//--- * test on underdetermined system with the same solution: (A 0 0 0 ) * z=b
|
|
info=0;
|
|
UnsetLSRep(repls);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rerrors=(rerrors||repls.m_r2<100*CMath::m_machineepsilon)||repls.m_r2>1+1000*CMath::m_machineepsilon;
|
|
rerrors=(rerrors||repls.m_n!=n)||repls.m_k!=0;
|
|
for(i=0; i<n; i++)
|
|
rerrors=rerrors||MathAbs(xe[i][0]-xv[i])>threshold;
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetLSRep(repls);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,2*n);
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- change value
|
|
i1_=-n;
|
|
for(i_=n; i_<=2*n-1; i_++)
|
|
bv[i_]=b[i_+i1_][0];
|
|
//--- allocation
|
|
atmp.Resize(2*n,n);
|
|
//--- function calls
|
|
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1);
|
|
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,n,2*n-1,0,n-1);
|
|
CDenseSolver::RMatrixSolveLS(atmp,2*n,n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rerrors=(rerrors||repls.m_r2<100*CMath::m_machineepsilon)||repls.m_r2>1+1000*CMath::m_machineepsilon;
|
|
rerrors=(rerrors||repls.m_n!=n)||repls.m_k!=0;
|
|
for(i=0; i<n; i++)
|
|
rerrors=rerrors||MathAbs(xe[i][0]-xv[i])>threshold;
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetLSRep(repls);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- allocation
|
|
atmp.Resize(n,2*n);
|
|
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=n; j<=2*n-1; j++)
|
|
atmp.Set(i,j,0);
|
|
}
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolveLS(atmp,n,2*n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rerrors=rerrors||repls.m_r2!=0.0;
|
|
rerrors=(rerrors||repls.m_n!=2*n)||repls.m_k!=n;
|
|
for(i=0; i<n; i++)
|
|
rerrors=rerrors||MathAbs(xe[i][0]-xv[i])>threshold;
|
|
for(i=n; i<=2*n-1; i++)
|
|
rerrors=rerrors||MathAbs(xv[i])>threshold;
|
|
}
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
//
|
|
for(taskkind=0; taskkind<=4; taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,0*a[k][i_]);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- change values
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- change values
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(0,i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xe.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- Test RMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test RMatrixSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test RMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test RMatrixLUSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test RMatrixMixedSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test RMatrixMixedSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors||!RMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test iterative improvement
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Test iterative improvement matrices
|
|
//--- A matrix/right part are constructed such that both matrix
|
|
//--- and solution components are within (-1,+1). Such matrix/right part
|
|
//--- have nice properties - system can be solved using iterative
|
|
//--- improvement with ||A*x-b|| about several ulps of max(1,||b||).
|
|
n=100;
|
|
a.Resize(n,n);
|
|
b.Resize(n,1);
|
|
ArrayResize(bv,n);
|
|
ArrayResize(tx,n);
|
|
ArrayResize(xv,n);
|
|
ArrayResize(y,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
xv[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
bv[i]=v;
|
|
}
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
b.Set(i_,0,bv[i_]);
|
|
//--- Test RMatrixSolveM()
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixSolveM(a,n,b,1,true,info,rep,x);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xv,n);
|
|
for(i_=0; i_<n; i_++)
|
|
xv[i_]=x[i_][0];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors||MathAbs(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(b[i][0]));
|
|
}
|
|
}
|
|
//--- Test RMatrixSolve()
|
|
Unset1D(xv);
|
|
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors||MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i]));
|
|
}
|
|
}
|
|
//--- Test LS-solver on the same matrix
|
|
CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors||MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SPD test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::TestSPDSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &spderrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
double v=0;
|
|
bool isupper;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double bv[];
|
|
double xv[];
|
|
double y[];
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble cha;
|
|
CMatrixDouble atmp;
|
|
CMatrixDouble xe;
|
|
CMatrixDouble b;
|
|
CMatrixDouble x;
|
|
//--- objects of class
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
isupper=CMath::RandomReal()>0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1000,a);
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(cha,n,isupper))
|
|
{
|
|
spderrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xe.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixDropHalf(cha,n,isupper);
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
for(taskkind=0; taskkind<=3; taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,0*a[i_][k]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,0*a[k][i_]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,0*a[k][i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns/rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,0,a[i_][k]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(0,i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xe.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixDropHalf(cha,n,isupper);
|
|
//--- Test SPDMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixSolve()
|
|
info=0;
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSingular(n,info,rep,xv);
|
|
//--- 'equal columns/rows' are degenerate,but
|
|
//--- Cholesky matrix with equal columns/rows IS NOT degenerate,
|
|
//--- so it is not used for testing purposes.
|
|
if(taskkind!=3)
|
|
{
|
|
//--- Test SPDMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixLUSolve()
|
|
info=0;
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors||!RMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::TestCSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &cerrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
double verr=0;
|
|
complex v=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
complex bv[];
|
|
complex xv[];
|
|
complex y[];
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex lua;
|
|
CMatrixComplex atmp;
|
|
CMatrixComplex xe;
|
|
CMatrixComplex b;
|
|
CMatrixComplex x;
|
|
//--- objects of classes
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::CMatrixRndCond(n,1000,a);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
xe.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
xe.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
for(taskkind=0; taskkind<=4; taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,a[i_][k]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(0,i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xe.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- Test CMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test CMatrixSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test CMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test CMatrixLUSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test CMatrixMixedSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test CMatrixMixedSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors||!CMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test iterative improvement
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Test iterative improvement matrices
|
|
//--- A matrix/right part are constructed such that both matrix
|
|
//--- and solution components magnitudes are within (-1,+1).
|
|
//--- Such matrix/right part have nice properties - system can
|
|
//--- be solved using iterative improvement with ||A*x-b|| about
|
|
//--- several ulps of max(1,||b||).
|
|
n=100;
|
|
a.Resize(n,n);
|
|
b.Resize(n,1);
|
|
ArrayResize(bv,n);
|
|
ArrayResize(tx,2*n);
|
|
ArrayResize(xv,n);
|
|
ArrayResize(y,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xv[i].real=2*CMath::RandomReal()-1;
|
|
xv[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XCDot(y,xv,n,tx,v,verr);
|
|
bv[i]=v;
|
|
}
|
|
for(i_=0; i_<n; i_++)
|
|
b.Set(i_,0,bv[i_]);
|
|
//--- Test CMatrixSolveM()
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixSolveM(a,n,b,1,true,info,rep,x);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xv,n);
|
|
for(i_=0; i_<n; i_++)
|
|
xv[i_]=x[i_][0];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XCDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors||CMath::AbsComplex(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(b[i][0]));
|
|
}
|
|
}
|
|
//--- Test CMatrixSolve()
|
|
CUnset1D(xv);
|
|
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
y[i_]=a.Get(i,i_);
|
|
//--- function call
|
|
CXblas::XCDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors||CMath::AbsComplex(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(bv[i]));
|
|
}
|
|
}
|
|
//--- TODO: Test LS-solver on the same matrix
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| HPD test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::TestHPDSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &hpderrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
complex v=0;
|
|
bool isupper;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
complex bv[];
|
|
complex xv[];
|
|
complex y[];
|
|
complex tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex cha;
|
|
CMatrixComplex atmp;
|
|
CMatrixComplex xe;
|
|
CMatrixComplex b;
|
|
CMatrixComplex x;
|
|
//--- objects of classes
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
isupper=CMath::RandomReal()>0.5;
|
|
CMatGen::HPDMatrixRndCond(n,1000,a);
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::HPDMatrixCholesky(cha,n,isupper))
|
|
{
|
|
hpderrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
xe.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
xe.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixDropHalf(cha,n,isupper);
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
for(taskkind=0; taskkind<=3; taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(i==j)
|
|
a.SetIm(i,j,0);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,a[i_][k]*0);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(i==j)
|
|
a.SetIm(i,j,0);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(k,i_,a[k][i_]*0);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,k,a[i_][k]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns/rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i; j<n; j++)
|
|
{
|
|
a.SetRe(i,j,2*CMath::RandomReal()-1);
|
|
a.SetIm(i,j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(i==j)
|
|
a.SetIm(i,j,0);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,0,a[i_][k]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(0,i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
xe.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe.Get(i_,j);
|
|
b.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixDropHalf(cha,n,isupper);
|
|
//--- Test SPDMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSingular(n,info,rep,xv);
|
|
//--- 'equal columns/rows' are degenerate,but
|
|
//--- Cholesky matrix with equal columns/rows IS NOT degenerate,
|
|
//--- so it is not used for testing purposes.
|
|
if(taskkind!=3)
|
|
{
|
|
//--- Test SPDMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixLUSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors||!CMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::Unset2D(CMatrixDouble &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::CUnset2D(CMatrixComplex &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::CUnset1D(complex &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::UnsetRep(CDenseSolverReport &r)
|
|
{
|
|
//--- change values
|
|
r.m_r1=-1;
|
|
r.m_rinf=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDenseSolverUnit::UnsetLSRep(CDenseSolverLSReport &r)
|
|
{
|
|
//--- change values
|
|
r.m_r2=-1;
|
|
r.m_n=-1;
|
|
r.m_k=-1;
|
|
//--- function call
|
|
Unset2D(r.m_cx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLinMin |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLinMinUnit
|
|
{
|
|
public:
|
|
static bool TestLinMin(const bool silent)
|
|
{
|
|
bool waserrors=false;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
PrintResult("TESTING LINMIN",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinCG |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinCGUnit
|
|
{
|
|
public:
|
|
static bool TestMinCG(const bool silent);
|
|
static void TestOther(bool &err);
|
|
|
|
private:
|
|
static void TestFunc1(CMinCGState &state);
|
|
static void TestFunc2(CMinCGState &state);
|
|
static void TestFunc3(CMinCGState &state);
|
|
static void CalcIIP2(CMinCGState &state,const int n);
|
|
static void CalcLowRank(CMinCGState &state,const int n,const int vcnt,double &d[],CMatrixDouble &v,double &vd[],double &x0[]);
|
|
static void TestPreconditioning(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinCG |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinCGUnit::TestMinCG(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool referror;
|
|
bool eqerror;
|
|
bool linerror1;
|
|
bool linerror2;
|
|
bool restartserror;
|
|
bool precerror;
|
|
bool converror;
|
|
bool othererrors;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int cgtype=0;
|
|
int difftype=0;
|
|
double diffstep=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xe[];
|
|
double b[];
|
|
double xlast[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinCGState state;
|
|
CMinCGReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
referror=false;
|
|
linerror1=false;
|
|
linerror2=false;
|
|
eqerror=false;
|
|
converror=false;
|
|
restartserror=false;
|
|
othererrors=false;
|
|
precerror=false;
|
|
//--- function calls
|
|
TestPreconditioning(precerror);
|
|
//--- TestOther(othererrors);
|
|
//--- calculation
|
|
for(difftype=0; difftype<=1; difftype++)
|
|
{
|
|
for(cgtype=-1; cgtype<=1; cgtype++)
|
|
{
|
|
//--- Reference problem
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
diffstep=1.0E-6;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
x[1]=100*CMath::RandomReal()-50;
|
|
x[2]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
referror=(((referror||rep.m_terminationtype<=0)||MathAbs(x[0]-2)>0.001)||MathAbs(x[1])>0.001)||MathAbs(x[2]-2)>0.001;
|
|
//--- F2 problem with restarts:
|
|
//--- * make several iterations and restart BEFORE termination
|
|
//--- * iterate and restart AFTER termination
|
|
//--- NOTE: step is bounded from above to avoid premature convergence
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
diffstep=1.0E-6;
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
CMinCG::MinCGSetStpMax(state,0.1);
|
|
CMinCG::MinCGSetCond(state,0.0000001,0.0,0.0,0);
|
|
//--- calculation
|
|
for(i=0; i<=10; i++)
|
|
{
|
|
//--- check
|
|
if(!CMinCG::MinCGIteration(state))
|
|
break;
|
|
//--- function call
|
|
TestFunc2(state);
|
|
}
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01;
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01;
|
|
//--- 1D problem #1
|
|
ArrayResize(x,1);
|
|
//--- change values
|
|
n=1;
|
|
diffstep=1.0E-6;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=-MathCos(state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(0,MathSin(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
linerror1=(linerror1||rep.m_terminationtype<=0)||MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001;
|
|
//--- 1D problem #2
|
|
ArrayResize(x,1);
|
|
//--- change values
|
|
n=1;
|
|
diffstep=1.0E-6;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(0,(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0])));
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
linerror2=(linerror2||rep.m_terminationtype<=0)||MathAbs(x[0])>0.001;
|
|
//--- Linear equations
|
|
diffstep=1.0E-6;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Prepare task
|
|
a.Resize(n,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
for(i=0; i<n; i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(i,i,a[i][i]+3*MathSign(a[i][i]));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- Solve task
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
state.m_g.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*state.m_x[i_];
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr(v-b[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
state.m_g.Add(j,2*(v-b[i])*a.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
eqerror=eqerror||rep.m_terminationtype<=0;
|
|
for(i=0; i<n; i++)
|
|
eqerror=eqerror||MathAbs(x[i]-xe[i])>0.001;
|
|
}
|
|
//--- Testing convergence properties
|
|
diffstep=1.0E-6;
|
|
n=3;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.001,0.0,0.0,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||rep.m_terminationtype!=4;
|
|
for(i=0; i<n; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.0,0.001,0.0,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||rep.m_terminationtype!=1;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.0,0.0,0.001,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||rep.m_terminationtype!=2;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.0,0.0,0.0,10);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||!((rep.m_terminationtype==5 && rep.m_iterationscount==10)||rep.m_terminationtype==7);
|
|
}
|
|
}
|
|
//--- end
|
|
waserrors=((((((referror||eqerror)||linerror1)||linerror2)||converror)||othererrors)||restartserror)||precerror;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CG OPTIMIZATION");
|
|
PrintResult("REFERENCE PROBLEM",!referror);
|
|
PrintResult("LIN-1 PROBLEM",!linerror1);
|
|
PrintResult("LIN-2 PROBLEM",!linerror2);
|
|
PrintResult("LINEAR EQUATIONS",!eqerror);
|
|
PrintResult("RESTARTS",!restartserror);
|
|
PrintResult("PRECONDITIONING",!precerror);
|
|
PrintResult("CONVERGENCE PROPERTIES",!converror);
|
|
PrintResult("OTHER PROPERTIES",!othererrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Other properties |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double stpmax=0;
|
|
int i=0;
|
|
int cgtype=0;
|
|
double tmpeps=0;
|
|
double epsg=0;
|
|
double v=0;
|
|
double r=0;
|
|
bool hasxlast;
|
|
double lastscaledstep=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
int dkind=0;
|
|
double diffstep=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double s[];
|
|
double a[];
|
|
double h[];
|
|
double xlast[];
|
|
//--- objects of classes
|
|
CMinCGState state;
|
|
CMinCGReport rep;
|
|
//--- calculation
|
|
for(cgtype=-1; cgtype<=1; cgtype++)
|
|
{
|
|
//--- Test reports (F should form monotone sequence)
|
|
n=50;
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
x[i]=1;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCond(state,0,0,0,100);
|
|
CMinCG::MinCGSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
err=err||state.m_f>fprev;
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
err=err||state.m_x[i]!=x[i];
|
|
}
|
|
//--- change values
|
|
fprev=state.m_f;
|
|
for(i_=0; i_<n; i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
err=err||x[i]!=xlast[i];
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests,second one issues NeedFG requests)
|
|
n=50;
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCond(state,0,0,0,n/2);
|
|
//--- change values
|
|
wasf=false;
|
|
wasfg=false;
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
//--- search errors
|
|
wasf=wasf||state.m_needf;
|
|
wasfg=wasfg||state.m_needfg;
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(dkind==0)
|
|
err=(err||wasf)||!wasfg;
|
|
//--- check
|
|
if(dkind==1)
|
|
err=(err||!wasf)||wasfg;
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S,then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
ArrayResize(x,1);
|
|
ArrayResize(s,1);
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s[0]=MathExp(CMath::RandomReal()*4-2);
|
|
x[0]=0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreateF(1,x,diffstep,state);
|
|
CMinCG::MinCGSetCond(state,1.0E-6,0,0,0);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
v=0;
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
//--- search errors
|
|
err=err||MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
|
|
//--- Test maximum step
|
|
n=1;
|
|
ArrayResize(x,n);
|
|
x[0]=100;
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCond(state,1.0E-9,0,0,0);
|
|
CMinCG::MinCGSetStpMax(state,stpmax);
|
|
CMinCG::MinCGSetXRep(state,true);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
|
|
//--- search errors
|
|
err=err||MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax;
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- search errors
|
|
err=err||MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax;
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set stringent stopping conditions (we try EpsG and EpsX)
|
|
//--- * and we test that in the extremum stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
tmpeps=1.0E-10;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pkind=0; pkind<=2; pkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
ArrayResize(a,n);
|
|
ArrayResize(s,n);
|
|
ArrayResize(h,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=CMath::RandomReal()+1;
|
|
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
CMinCG::MinCGSetXRep(state,true);
|
|
//--- check
|
|
if(pkind==1)
|
|
CMinCG::MinCGSetPrecDiag(state,h);
|
|
//--- check
|
|
if(pkind==2)
|
|
CMinCG::MinCGSetPrecScale(state);
|
|
//--- Test gradient-based stopping condition
|
|
for(i=0; i<n; i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,tmpeps,0,0,0);
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g.Set(i,4*a[i]*MathPow(state.m_x[i],3));
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
err=err||v>tmpeps;
|
|
//--- Test step-based stopping condition
|
|
for(i=0; i<n; i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
hasxlast=false;
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0,0,tmpeps,0);
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g.Set(i,4*a[i]*MathPow(state.m_x[i],3));
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(hasxlast)
|
|
{
|
|
lastscaledstep=0;
|
|
for(i=0; i<n; i++)
|
|
lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]);
|
|
lastscaledstep=MathSqrt(lastscaledstep);
|
|
}
|
|
else
|
|
lastscaledstep=0;
|
|
for(i_=0; i_<n; i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
hasxlast=true;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err||lastscaledstep>tmpeps;
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
|
|
//--- f(x)={
|
|
//--- { M,if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
for(mkind=0; mkind<=2; mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
//--- check
|
|
if(ckind==1)
|
|
vc=1.0E+6;
|
|
//--- check
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
//--- check
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
//--- check
|
|
if(mkind==2)
|
|
vm=CInfOrNaN::PositiveInfinity();
|
|
//--- Create optimizer,solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
ArrayResize(x,1);
|
|
x[0]=0.0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(1,x,state);
|
|
CMinCG::MinCGSetCond(state,epsg,0,0,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- check
|
|
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g.Set(0,1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc);
|
|
}
|
|
else
|
|
state.m_f=vm;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err||MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #1 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::TestFunc1(CMinCGState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber));
|
|
state.m_g.Set(1,0);
|
|
state.m_g.Set(2,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #2 |
|
|
//| Simple variation of #1,much more nonlinear,which makes unlikely |
|
|
//| premature convergence of algorithm . |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::TestFunc2(CMinCGState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,4*state.m_x[1]*CMath::Sqr(state.m_x[1]));
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber));
|
|
state.m_g.Set(1,0);
|
|
state.m_g.Set(2,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #3 |
|
|
//| Simple variation of #1,much more nonlinear,with non-zero value at|
|
|
//| minimum. |
|
|
//| It achieve two goals: |
|
|
//| * makes unlikely premature convergence of algorithm. |
|
|
//| * solves some issues with EpsF stopping condition which arise |
|
|
//| when F(minimum) is zero |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::TestFunc3(CMinCGState &state)
|
|
{
|
|
double s=0.001;
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber));
|
|
state.m_g.Set(1,0);
|
|
state.m_g.Set(2,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::CalcIIP2(CMinCGState &state,const int n)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,CMath::Sqr(i*i+1)*2*state.m_x[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::CalcLowRank(CMinCGState &state,const int n,
|
|
const int vcnt,double &d[],
|
|
CMatrixDouble &v,double &vd[],
|
|
double &x0[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double dx=0;
|
|
double t=0;
|
|
double t2=0;
|
|
//--- change values
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
dx=state.m_x[i]-x0[i];
|
|
state.m_f+=0.5*dx*d[i]*dx;
|
|
state.m_g.Set(i,d[i]*dx);
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<=vcnt-1; i++)
|
|
{
|
|
t=(v[i]*(state.m_x-x0)).Sum();
|
|
//--- change values
|
|
state.m_f+=0.5*t*vd[i]*t;
|
|
t2=t*vd[i];
|
|
state.m_g+=v[i]*t2;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinCGUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int vs=0;
|
|
int cntb1=0;
|
|
int cntg1=0;
|
|
int cntb2=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
int cgtype=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x0[];
|
|
double vd[];
|
|
double d[];
|
|
double s[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble v;
|
|
//--- objects of classes
|
|
CMinCGState state;
|
|
CMinCGReport rep;
|
|
//--- initialization
|
|
k=50;
|
|
epsg=1.0E-10;
|
|
//--- calculation
|
|
for(cgtype=-1; cgtype<=1; cgtype++)
|
|
{
|
|
//--- Preconditioner test 1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner
|
|
//--- * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix
|
|
//--- then "bad" preconditioner is worse than "good" one.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1).
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
for(n=10; n<=15; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- Test it with default preconditioner
|
|
CMinCG::MinCGSetPrecDefault(state);
|
|
//--- change values
|
|
cntb1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntb1+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with perturbed diagonal preconditioner
|
|
ArrayResize(diagh,n);
|
|
for(i=0; i<n; i++)
|
|
diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal());
|
|
//--- function call
|
|
CMinCG::MinCGSetPrecDiag(state,diagh);
|
|
//--- change values
|
|
cntg1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntg1+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Compare
|
|
err=err||cntb1<cntg1;
|
|
}
|
|
//--- Preconditioner test 2.
|
|
//--- If
|
|
//--- * B1 is default preconditioner
|
|
//--- * G1 is low rank exact preconditioner
|
|
//--- then "bad" preconditioner is worse than "good" one.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- Target function is f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
for(n=10; n<=15; n++)
|
|
{
|
|
for(vs=0; vs<=5; vs++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(x0,n);
|
|
ArrayResize(d,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=0;
|
|
x0[i]=2*CMath::RandomReal()-1;
|
|
d[i]=MathExp(2*CMath::RandomReal());
|
|
}
|
|
//--- check
|
|
if(vs>0)
|
|
{
|
|
//--- allocation
|
|
v.Resize(vs,n);
|
|
ArrayResize(vd,vs);
|
|
//--- change values
|
|
for(i=0; i<vs; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
v.Set(i,j,2*CMath::RandomReal()-1);
|
|
vd[i]=MathExp(2*CMath::RandomReal());
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- Test it with default preconditioner
|
|
CMinCG::MinCGSetPrecDefault(state);
|
|
//--- change values
|
|
cntb1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcLowRank(state,n,vs,d,v,vd,x0);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntb1+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with low rank preconditioner
|
|
CMinCG::MinCGSetPrecLowRankFast(state,d,vd,v,vs);
|
|
//--- change values
|
|
cntg1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcLowRank(state,n,vs,d,v,vd,x0);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntg1+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Compare
|
|
err=err||cntb1<cntg1;
|
|
}
|
|
}
|
|
//--- Preconditioner test 3.
|
|
//--- If
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
for(n=10; n<=15; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayInitialize(x,0);
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
ArrayResize(s,n);
|
|
for(i=0; i<n; i++)
|
|
s[i]=1/MathSqrt(2*MathPow(i*i+1,2)*(0.8+0.4*CMath::RandomReal()));
|
|
//--- function calls
|
|
CMinCG::MinCGSetPrecDefault(state);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
//--- change values
|
|
cntb2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntb2+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- function calls
|
|
CMinCG::MinCGSetPrecScale(state);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
//--- change values
|
|
cntg2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntg2+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err||cntb2<cntg2;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinBLEIC |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinBLEICUnit
|
|
{
|
|
public:
|
|
static bool TestMinBLEIC(const bool silent);
|
|
|
|
private:
|
|
static void CheckBounds(double &x[],double &bndl[],double &bndu[],const int n,bool &err);
|
|
static void CalcIIP2(CMinBLEICState &state,const int n,const int fk);
|
|
static void TestFeasibility(bool &feaserr,bool &converr,bool &interr);
|
|
static void TestOther(bool &err);
|
|
static void TestConv(bool &err);
|
|
static void TestPreconditioning(bool &err);
|
|
static void SetRandomPreconditioner(CMinBLEICState &state,const int n,const int preckind);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinBLEIC |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinBLEICUnit::TestMinBLEIC(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool feasibilityerrors;
|
|
bool othererrors;
|
|
bool precerrors;
|
|
bool interrors;
|
|
bool converrors;
|
|
//--- initialization
|
|
waserrors=false;
|
|
feasibilityerrors=false;
|
|
othererrors=false;
|
|
precerrors=false;
|
|
interrors=false;
|
|
converrors=false;
|
|
//--- function calls
|
|
TestFeasibility(feasibilityerrors,converrors,interrors);
|
|
TestOther(othererrors);
|
|
TestConv(converrors);
|
|
TestPreconditioning(precerrors);
|
|
//--- end
|
|
waserrors=(((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BLEIC OPTIMIZATION");
|
|
PrintResult("FEASIBILITY PROPERTIES",!feasibilityerrors);
|
|
PrintResult("PRECONDITIONING",!precerrors);
|
|
PrintResult("OTHER PROPERTIES",!othererrors);
|
|
PrintResult("CONVERGENCE PROPERTIES",!converrors);
|
|
PrintResult("INTERNAL ERRORS",!interrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks that X is bounded with respect to BndL/BndU. |
|
|
//| If it is not,True is assigned to the Err variable (which is not |
|
|
//| changed otherwise). |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::CheckBounds(double &x[],double &bndl[],
|
|
double &bndu[],const int n,
|
|
bool &err)
|
|
{
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(x[i]<bndl[i] || x[i]>bndu[i])
|
|
err=true;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::CalcIIP2(CMinBLEICState &state,const int n,
|
|
const int fk)
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f+=MathPow(i*i+1,2*fk)*CMath::Sqr(state.m_x[i]);
|
|
state.m_g.Set(i,MathPow(i*i+1,2*fk)*2*state.m_x[i]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function test feasibility properties. |
|
|
//| It launches a sequence of problems and examines their solutions. |
|
|
//| Most of the attention is directed towards feasibility properties,|
|
|
//| although we make some quick checks to ensure that actual solution|
|
|
//| is found. |
|
|
//| On failure sets FeasErr (or ConvErr,depending on failure type) |
|
|
//| to True, or leaves it unchanged otherwise. |
|
|
//| IntErr is set to True on internal errors (errors in the control |
|
|
//| flow). |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::TestFeasibility(bool &feaserr,bool &converr,
|
|
bool &interr)
|
|
{
|
|
//--- create variables
|
|
int pkind=0;
|
|
int preckind=0;
|
|
int passcount=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int nmax=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int p=0;
|
|
double v=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double vv=0;
|
|
double epsc=0;
|
|
double epsg=0;
|
|
int dkind=0;
|
|
double diffstep=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble x;
|
|
CRowDouble g;
|
|
CRowDouble x0;
|
|
CRowDouble xc;
|
|
CRowDouble xs;
|
|
CRowInt ct;
|
|
CRowDouble svdw;
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
CMatrixDouble svdvt,svdu;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- initialization
|
|
nmax=5;
|
|
double weakepsg=1.0E-4;
|
|
double epsx=1.0E-4;
|
|
double epsfeas=1.0E-6;
|
|
passcount=10;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Test problem 1:
|
|
//--- * no boundary and inequality constraints
|
|
//--- * randomly generated plane as equality constraint
|
|
//--- * random point (not necessarily on the plane)
|
|
//--- * f = |x|^P, P = {2, 4} is used as target function
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * either analytic gradient or numerical differentiation are used
|
|
//--- * we check that after work is over we are on the plane and
|
|
//--- that we are in the stationary point of constrained F
|
|
//
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU, CT and left part of C.
|
|
//--- Right part of C is generated using somewhat complex algo:
|
|
//--- * we generate random vector and multiply it by C.
|
|
//--- * result is used as the right part.
|
|
//--- * calculations are done on the fly, vector itself is not stored
|
|
//--- We use such algo to be sure that our system is consistent.
|
|
p=2*pkind;
|
|
x=vector<double>::Zeros(n);
|
|
g=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(1,n+1);
|
|
ct.Resize(1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
c.Set(0,i,2*CMath::RandomReal()-1);
|
|
v=2*CMath::RandomReal()-1;
|
|
c.Add(0,n,c.Get(0,i)*v);
|
|
}
|
|
ct.Set(0,0);
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
{
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
}
|
|
if(dkind==1)
|
|
{
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
}
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=(state.m_x.Pow(p)+0).Sum();
|
|
if(state.m_needfg)
|
|
state.m_g=state.m_x.Pow(p-1)*p;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- Test feasibility of solution
|
|
v=CAblasF::RDotVR(n,x,c,0);
|
|
feaserr=feaserr||MathAbs(v-c.Get(0,n))>epsfeas;
|
|
//--- if C is nonzero, test that result is
|
|
//--- a stationary point of constrained F.
|
|
//--- NOTE: this check is done only if C is nonzero
|
|
vv=CAblasF::RDotRR(n,c,0,c,0);
|
|
if(vv!=0.0)
|
|
{
|
|
//--- Calculate gradient at the result
|
|
//--- Project gradient into C
|
|
//--- Check projected norm
|
|
g=x.Pow(p-1)*p;
|
|
v2=CAblasF::RDotRR(n,c,0,c,0);
|
|
v=CAblasF::RDotVR(n,g,c,0);
|
|
vv=v/v2;
|
|
for(i_=0; i_<n; i_++)
|
|
g.Add(i_,-vv*c.Get(0,i_));
|
|
v3=g.Dot(g);
|
|
converr=converr||MathSqrt(v3)>weakepsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test problem 2 (multiple equality constraints):
|
|
//--- * 1<=N<=NMax, 1<=K<=N
|
|
//--- * no boundary constraints
|
|
//--- * N-dimensional space
|
|
//--- * randomly generated point xs
|
|
//--- * K randomly generated hyperplanes which all pass through xs
|
|
//--- define K equality constraints: (a[k],x)=b[k]
|
|
//--- * equality constraints are checked for being well conditioned
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * f(x) = |x-x0|^2, x0 = xs+a[0]
|
|
//--- * either analytic gradient or numerical differentiation are used
|
|
//--- * extremum of f(x) is exactly xs because:
|
|
//--- * xs is the closest point in the plane defined by (a[0],x)=b[0]
|
|
//--- * xs is feasible by definition
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(n=2; n<=nmax; n++)
|
|
{
|
|
for(k=1; k<=n; k++)
|
|
{
|
|
//--- Generate X, X0, XS, BL, BU, CT and left part of C.
|
|
//--- Right part of C is generated using somewhat complex algo:
|
|
//--- * we generate random vector and multiply it by C.
|
|
//--- * result is used as the right part.
|
|
//--- * calculations are done on the fly, vector itself is not stored
|
|
//--- We use such algo to be sure that our system is consistent.
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xs=vector<double>::Zeros(n);
|
|
g=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
xs.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
do
|
|
{
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,xs,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
feaserr=feaserr||!CSingValueDecompose::RMatrixSVD(c,k,n,0,0,0,svdw,svdu,svdvt);
|
|
}
|
|
while(!(svdw[0]>0.0 && svdw[k-1]>(0.001*svdw[0])));
|
|
for(i_=0; i_<n; i_++)
|
|
x0.Set(i_,xs[i_]+c.Get(0,i_));
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,k);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),2.0).Sum();
|
|
if(state.m_needfg)
|
|
state.m_g=(state.m_x-x0)*2.0;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- check feasiblity properties
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x,c,i);
|
|
feaserr=feaserr||MathAbs(v-c.Get(i,n))>epsx;
|
|
}
|
|
//--- Compare with XS
|
|
v=MathPow(x.ToVector()-xs.ToVector(),2.0).Sum();
|
|
v=MathSqrt(v);
|
|
converr=converr||MathAbs(v)>0.001;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Another simple problem:
|
|
//--- * bound constraints 0 <= x[i] <= 1
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple boundaries and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1)
|
|
//--- * we also check that both final solution and subsequent iterates
|
|
//--- are strictly feasible
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Ones(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
}
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
|
|
if(state.m_needfg)
|
|
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
|
|
feaserr=feaserr||state.m_x.Min()<0.0;
|
|
feaserr=feaserr||state.m_x.Max()>1.0;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x[i]>0.0 && x[i]<1.0)
|
|
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
|
|
feaserr=feaserr||x[i]<0.0;
|
|
feaserr=feaserr||x[i]>1.0;
|
|
}
|
|
converr=converr||MathSqrt(v)>weakepsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Same as previous problem, but with minor modifications:
|
|
//--- * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple boundaries and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1)
|
|
//--- * we also check that both final solution and subsequent iterates
|
|
//--- are strictly feasible
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
bl.Set(i,0);
|
|
bu.Set(i,1);
|
|
}
|
|
else
|
|
{
|
|
bl.Set(i,CMath::RandomReal());
|
|
bu.Set(i,bl[i]);
|
|
}
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
}
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
|
|
if(state.m_needfg)
|
|
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
feaserr=feaserr||state.m_x[i]<bl[i];
|
|
feaserr=feaserr||state.m_x[i]>bu[i];
|
|
}
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x[i]>bl[i] && x[i]<bu[i])
|
|
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
|
|
feaserr=feaserr||x[i]<bl[i];
|
|
feaserr=feaserr||x[i]>bu[i];
|
|
}
|
|
converr=converr||MathSqrt(v)>weakepsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Same as previous one, but with bound constraints posed
|
|
//--- as general linear ones:
|
|
//--- * no bound constraints
|
|
//--- * 2*N linear constraints 0 <= x[i] <= 1
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple constraints and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1).
|
|
//--- * however, we can't guarantee that solution is strictly feasible
|
|
//--- with respect to nonlinearity constraint, so we check
|
|
//--- for approximate feasibility.
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
c.Row(2*i,vector<double>::Zeros(n+1));
|
|
c.Row(2*i+1,vector<double>::Zeros(n+1));
|
|
c.Set(2*i,i,1);
|
|
c.Set(2*i+0,n,0);
|
|
ct.Set(2*i,1);
|
|
c.Set(2*i+1,i,1);
|
|
c.Set(2*i+1,n,1);
|
|
ct.Set(2*i+1,-1);
|
|
}
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,2*n);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
|
|
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x[i]>0.02 && x[i]<0.98)
|
|
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
|
|
feaserr=feaserr||x[i]<(0.0-epsfeas);
|
|
feaserr=feaserr||x[i]>(1.0+epsfeas);
|
|
}
|
|
converr=converr||MathSqrt(v)>weakepsg;
|
|
}
|
|
}
|
|
}
|
|
//--- Feasibility problem:
|
|
//--- * bound constraints 0<=x[i]<=1
|
|
//--- * starting point xs with xs[i] in [-1,+2]
|
|
//--- * random point xc from [0,1] is used to generate K<=N
|
|
//--- random linear equality/inequality constraints of the form
|
|
//--- (c,x-xc)=0.0 (or, alternatively, >= or <=), where
|
|
//--- c is a random vector.
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * we do not know analytic form of the solution, and, if fact, we do not
|
|
//--- check for solution correctness. We just check that algorithm converges
|
|
//--- to the feasible points.
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
for(k=1; k<=n; k++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
xs=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Ones(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
xs.Set(i,3*CMath::RandomReal()-1);
|
|
xc.Set(i,0.1+0.8*CMath::RandomReal());
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
c.Add(i,n,c.Get(i,j)*xc[j]);
|
|
}
|
|
ct.Set(i,CMath::RandomInteger(3)-1);
|
|
}
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,xs,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,k);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
|
|
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- Check feasibility
|
|
for(i=0; i<n; i++)
|
|
{
|
|
feaserr=feaserr||x[i]<0.0;
|
|
feaserr=feaserr||x[i]>1.0;
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x,c,i);
|
|
v-=c.Get(i,n);
|
|
if(ct[i]==0)
|
|
feaserr=feaserr||MathAbs(v)>epsfeas;
|
|
if(ct[i]<0)
|
|
feaserr=feaserr||v>epsfeas;
|
|
if(ct[i]>0)
|
|
feaserr=feaserr||v<(-epsfeas);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Infeasible problem:
|
|
//--- * all bound constraints are 0 <= x[i] <= 1 except for one
|
|
//--- * that one is 0 >= x[i] >= 1
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from detecting
|
|
//--- infeasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * algorithm must return correct error code on such problem
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Ones(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
}
|
|
i=CMath::RandomInteger(n);
|
|
bl.Set(i,1);
|
|
bu.Set(i,0);
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
|
|
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
feaserr=feaserr||rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
//--- Infeasible problem (2):
|
|
//--- * no bound and inequality constraints
|
|
//--- * 1<=K<=N arbitrary equality constraints
|
|
//--- * (K+1)th constraint which is equal to the first constraint a*x=c,
|
|
//--- but with c:=c+1. I.e. we have both a*x=c and a*x=c+1, which can't
|
|
//--- be true (other constraints may be inconsistent too, but we don't
|
|
//--- have to check it).
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from detecting
|
|
//--- infeasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x|^P, where P={2,4}
|
|
//--- * algorithm must return correct error code on such problem
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
for(k=1; k<=n; k++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
x=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k+1,n+1);
|
|
ct.Resize(k+1);
|
|
ct.Fill(0);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,CMath::RandomReal());
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(k,i_,c.Get(0,i_));
|
|
c.Set(k,n,c.Get(0,n)+1);
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,k+1);
|
|
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=(state.m_x.Pow(p)+0).Sum();
|
|
state.m_g=state.m_x.Pow(p-1)*p;
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
feaserr=feaserr||rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function additional properties. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int nmax=0;
|
|
int i=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double stpmax=0;
|
|
double v=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
double epsc=0;
|
|
double epsg=0;
|
|
double tmpeps=0;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
double r=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double bl[];
|
|
double bu[];
|
|
double x[];
|
|
double xf[];
|
|
double xlast[];
|
|
double a[];
|
|
double s[];
|
|
double h[];
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- initialization
|
|
nmax=5;
|
|
epsc=1.0E-4;
|
|
epsg=1.0E-8;
|
|
passcount=10;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Test reports:
|
|
//--- * first value must be starting point
|
|
//--- * last value must be last point
|
|
n=50;
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=10;
|
|
bl[i]=2*CMath::RandomReal()-1;
|
|
bu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,0,0,0);
|
|
CMinBLEIC::MinBLEICSetMaxIts(state,10);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-64,1.0E-64);
|
|
CMinBLEIC::MinBLEICSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=err||state.m_x[i]!=x[i];
|
|
}
|
|
}
|
|
//--- change values
|
|
fprev=state.m_f;
|
|
for(i_=0; i_<n; i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
err=err||x[i]!=xlast[i];
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests,second one issues NeedFG requests)
|
|
n=50;
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-10,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
|
|
wasf=false;
|
|
wasfg=false;
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
//--- search errors
|
|
wasf=wasf||state.m_needf;
|
|
wasfg=wasfg||state.m_needfg;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(dkind==0)
|
|
err=(err||wasf)||!wasfg;
|
|
//--- check
|
|
if(dkind==1)
|
|
err=(err||!wasf)||wasfg;
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S,then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
ArrayResize(x,1);
|
|
ArrayResize(s,1);
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s[0]=MathExp(CMath::RandomReal()*4-2);
|
|
x[0]=0;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreateF(1,x,diffstep,state);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-6,0,0);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
v=0;
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
//--- search errors
|
|
err=err||MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
|
|
//--- Test stpmax
|
|
n=1;
|
|
ArrayResize(x,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
//--- change values
|
|
x[0]=100;
|
|
bl[0]=2*CMath::RandomReal()-1;
|
|
bu[0]=CInfOrNaN::PositiveInfinity();
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
CMinBLEIC::MinBLEICSetXRep(state,true);
|
|
CMinBLEIC::MinBLEICSetStpMax(state,stpmax);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
|
|
err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
//--- Ability to solve problems with function which is unbounded from below
|
|
n=1;
|
|
ArrayResize(x,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
bl[0]=4*CMath::RandomReal()+1;
|
|
bu[0]=bl[0]+1;
|
|
x[0]=0.5*(bl[0]+bu[0]);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0]));
|
|
state.m_g.Set(0,-(2.0E8*state.m_x[0]));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- search errors
|
|
err=err||MathAbs(x[0]-bu[0])>epsc;
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
|
|
//--- * function is EFFECTIVELY unconstrained;it has formal constraints,
|
|
//--- but they are inactive at the solution;we try different variants
|
|
//--- in order to explore different control paths of the optimizer:
|
|
//--- 0) absense of constraints
|
|
//--- 1) bound constraints -100000<=x[i]<=100000
|
|
//--- 2) one linear constraint 0*x=0
|
|
//--- 3) combination of (1) and (2)
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set very mild outer stopping conditions - OuterEpsX=1.0,but
|
|
//--- inner conditions are very stringent
|
|
//--- * and we test that in the extremum inner stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
tmpeps=1.0E-10;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(ckind=0; ckind<=3; ckind++)
|
|
{
|
|
for(pkind=0; pkind<=2; pkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(a,n);
|
|
ArrayResize(s,n);
|
|
ArrayResize(h,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
c.Resize(1,n+1);
|
|
ArrayResize(ct,1);
|
|
ct[0]=0;
|
|
c.Set(0,n,0);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=CMath::RandomReal()+1;
|
|
bl[i]=-100000;
|
|
bu[i]=100000;
|
|
c.Set(0,i,0);
|
|
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
}
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(ckind==1 || ckind==3)
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
//--- check
|
|
if(ckind==2 || ckind==3)
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
|
|
//--- check
|
|
if(pkind==1)
|
|
CMinBLEIC::MinBLEICSetPrecDiag(state,h);
|
|
//--- check
|
|
if(pkind==2)
|
|
CMinBLEIC::MinBLEICSetPrecScale(state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,tmpeps,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,0.0,1.0E-8);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=a[i]*MathPow(state.m_x[i],2);
|
|
state.m_g.Set(i,2*a[i]*state.m_x[i]);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
err=err||v>tmpeps;
|
|
}
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
|
|
//--- f(x)={
|
|
//--- { M,if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
//
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
for(mkind=0; mkind<=2; mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
//--- check
|
|
if(ckind==1)
|
|
vc=1.0E+6;
|
|
//--- check
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
//--- check
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
//--- check
|
|
if(mkind==2)
|
|
vm=CInfOrNaN::PositiveInfinity();
|
|
//--- Create optimizer,solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
ArrayResize(x,1);
|
|
x[0]=0.0;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(1,x,state);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- check
|
|
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g.Set(0,1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc);
|
|
}
|
|
else
|
|
state.m_f=vm;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err||MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests convergence properties. |
|
|
//| We solve several simple problems with different combinations of |
|
|
//| constraints |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::TestConv(bool &err)
|
|
{
|
|
//--- create variables
|
|
int passcount=10;
|
|
int pass=0;
|
|
double epsc=1.0E-4;
|
|
double epsg=1.0E-8;
|
|
double tol=0.001;
|
|
//--- create arrays
|
|
double bl[];
|
|
double bu[];
|
|
double x[];
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- Three closely connected problems:
|
|
//--- * 2-dimensional space
|
|
//--- * octagonal area bounded by:
|
|
//--- * -1<=x<=+1
|
|
//--- * -1<=y<=+1
|
|
//--- * x+y<=1.5
|
|
//--- * x-y<=1.5
|
|
//--- * -x+y<=1.5
|
|
//--- * -x-y<=1.5
|
|
//--- * several target functions:
|
|
//--- * f0=x+0.001*y,minimum at x=-1,y=-0.5
|
|
//--- * f1=(x+10)^2+y^2,minimum at x=-1,y=0
|
|
//--- * f2=(x+10)^2+(y-0.6)^2,minimum at x=-1,y=0.5
|
|
ArrayResize(x,2);
|
|
ArrayResize(bl,2);
|
|
ArrayResize(bu,2);
|
|
c.Resize(4,3);
|
|
ArrayResize(ct,4);
|
|
//--- change values
|
|
bl[0]=-1;
|
|
bl[1]=-1;
|
|
bu[0]=1;
|
|
bu[1]=1;
|
|
c.Set(0,0,1);
|
|
c.Set(0,1,1);
|
|
c.Set(0,2,1.5);
|
|
ct[0]=-1;
|
|
c.Set(1,0,1);
|
|
c.Set(1,1,-1);
|
|
c.Set(1,2,1.5);
|
|
ct[1]=-1;
|
|
c.Set(2,0,-1);
|
|
c.Set(2,1,1);
|
|
c.Set(2,2,1.5);
|
|
ct[2]=-1;
|
|
c.Set(3,0,-1);
|
|
c.Set(3,1,-1);
|
|
c.Set(3,2,1.5);
|
|
ct[3]=-1;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- f0
|
|
x[0]=0.2*CMath::RandomReal()-0.1;
|
|
x[1]=0.2*CMath::RandomReal()-0.1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICCreate(2,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=state.m_x[0]+0.001*state.m_x[1];
|
|
state.m_g.Set(0,1);
|
|
state.m_g.Set(1,0.001);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
//--- search errors
|
|
err=err||MathAbs(x[0]+1)>tol;
|
|
err=err||MathAbs(x[1]+0.5)>tol;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- f1
|
|
x[0]=0.2*CMath::RandomReal()-0.1;
|
|
x[1]=0.2*CMath::RandomReal()-0.1;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(2,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]);
|
|
state.m_g.Set(0,2*(state.m_x[0]+10));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
//--- search errors
|
|
err=err||MathAbs(x[0]+1)>tol;
|
|
err=err||MathAbs(x[1])>tol;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- f2
|
|
x[0]=0.2*CMath::RandomReal()-0.1;
|
|
x[1]=0.2*CMath::RandomReal()-0.1;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(2,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]-0.6);
|
|
state.m_g.Set(0,2*(state.m_x[0]+10));
|
|
state.m_g.Set(1,2*(state.m_x[1]-0.6));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
//--- search errors
|
|
err=err||MathAbs(x[0]+1)>tol;
|
|
err=err||MathAbs(x[1]-0.5)>tol;
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int cntb1=0;
|
|
int cntb2=0;
|
|
int cntg1=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
int fkind=0;
|
|
int ckind=0;
|
|
int fk=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x0[];
|
|
int ct[];
|
|
double bl[];
|
|
double bu[];
|
|
double vd[];
|
|
double d[];
|
|
double units[];
|
|
double s[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble v;
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- Preconditioner test 1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner with unit scale
|
|
//--- * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B1 is worse than G1,B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- Test problem setup:
|
|
//--- * f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1)
|
|
//--- * FK is either +1 or -1 (we try both to test different aspects of preconditioning)
|
|
//--- * constraints:
|
|
//--- 0) absent
|
|
//--- 1) boundary only
|
|
//--- 2) linear equality only
|
|
//--- 3) combination of boundary and linear equality constraints
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
k=30;
|
|
epsg=1.0E-10;
|
|
for(n=5; n<=8; n++)
|
|
{
|
|
for(fkind=0; fkind<=1; fkind++)
|
|
{
|
|
for(ckind=0; ckind<=3; ckind++)
|
|
{
|
|
fk=1-2*fkind;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(units,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=0;
|
|
units[i]=1;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetCond(state,epsg,0.0,0.0,0);
|
|
//--- check
|
|
if(ckind==1 || ckind==3)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl[i]=-1;
|
|
bu[i]=1;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
}
|
|
//--- check
|
|
if(ckind==2 || ckind==3)
|
|
{
|
|
//--- allocation
|
|
c.Resize(1,n+1);
|
|
ArrayResize(ct,1);
|
|
//--- change value
|
|
ct[0]=CMath::RandomInteger(3)-1;
|
|
for(i=0; i<n; i++)
|
|
c.Set(0,i,2*CMath::RandomReal()-1);
|
|
c.Set(0,n,0);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
|
|
}
|
|
//--- Test it with default preconditioner VS. perturbed diagonal preconditioner
|
|
CMinBLEIC::MinBLEICSetPrecDefault(state);
|
|
CMinBLEIC::MinBLEICSetScale(state,units);
|
|
//--- calculation
|
|
cntb1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntb1+=rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(diagh,n);
|
|
for(i=0; i<n; i++)
|
|
diagh[i]=2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal());
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetPrecDiag(state,diagh);
|
|
CMinBLEIC::MinBLEICSetScale(state,units);
|
|
//--- calculation
|
|
cntg1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntg1+=rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err||cntb1<cntg1;
|
|
//--- Test it with scale-based preconditioner
|
|
ArrayResize(s,n);
|
|
for(i=0; i<n; i++)
|
|
s[i]=1.0/MathSqrt(2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal()));
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetPrecDefault(state);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
//--- change values
|
|
cntb2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntb2+=rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetPrecScale(state);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
//--- change values
|
|
cntg2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntg2+=rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err||cntb2<cntg2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets random preconditioner: |
|
|
//| * unit one,for PrecKind=0 |
|
|
//| * diagonal-based one,for PrecKind=1 |
|
|
//| * scale-based one,for PrecKind=2 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBLEICUnit::SetRandomPreconditioner(CMinBLEICState &state,
|
|
const int n,
|
|
const int preckind)
|
|
{
|
|
//--- create array
|
|
double p[];
|
|
//--- check
|
|
if(preckind==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(p,n);
|
|
for(int i=0; i<n; i++)
|
|
p[i]=MathExp(10*CMath::RandomReal()-5);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICSetPrecDiag(state,p);
|
|
}
|
|
else
|
|
CMinBLEIC::MinBLEICSetPrecDefault(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMarkovCPD |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMCPDUnit
|
|
{
|
|
public:
|
|
static bool TestMCPD(const bool silent);
|
|
|
|
private:
|
|
static void TestSimple(bool &err);
|
|
static void TestEntryExit(bool &err);
|
|
static void TestEC(bool &err);
|
|
static void TestBC(bool &err);
|
|
static void TestLC(bool &err);
|
|
static void CreateEE(const int n,const int entrystate,const int exitstate,CMCPDState &s);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMarkovCPD |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMCPDUnit::TestMCPD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool simpleerrors;
|
|
bool entryexiterrors;
|
|
bool ecerrors;
|
|
bool bcerrors;
|
|
bool lcerrors;
|
|
bool othererrors;
|
|
//--- Init
|
|
waserrors=false;
|
|
othererrors=false;
|
|
simpleerrors=false;
|
|
entryexiterrors=false;
|
|
ecerrors=false;
|
|
bcerrors=false;
|
|
lcerrors=false;
|
|
//--- Test
|
|
TestSimple(simpleerrors);
|
|
TestEntryExit(entryexiterrors);
|
|
TestEC(ecerrors);
|
|
TestBC(bcerrors);
|
|
TestLC(lcerrors);
|
|
//--- Final report
|
|
waserrors=((((othererrors||simpleerrors)||entryexiterrors)||ecerrors)||bcerrors)||lcerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("MCPD TEST");
|
|
PrintResult("* SIMPLE",!simpleerrors);
|
|
PrintResult("* ENTRY/EXIT",!entryexiterrors);
|
|
PrintResult("* EQUALITY CONSTRAINTS",!ecerrors);
|
|
PrintResult("* BOUND CONSTRAINTS",!bcerrors);
|
|
PrintResult("* LINEAR CONSTRAINTS",!lcerrors);
|
|
PrintResult("* OTHER PROPERTIES",!othererrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Simple test with no "entry"/"exit" states |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMCPDUnit::TestSimple(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
double threshold=1.0E-2;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double offdiagonal=0;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- create matrix
|
|
CMatrixDouble pexact;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble p;
|
|
//--- First test:
|
|
//--- * N-dimensional problem
|
|
//--- * proportional data
|
|
//---*no "entry"/"exit" states
|
|
//--- * N tracks,each includes only two states
|
|
//--- * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
|
|
//--- * all tracks are modelled using randomly generated transition matrix P
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- Initialize "exact" P:
|
|
//--- * fill by random values
|
|
//--- * make sure that each column sums to non-zero value
|
|
//--- * normalize
|
|
pexact.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
pexact.Set(i,j,CMath::RandomReal());
|
|
}
|
|
for(j=0; j<n; j++)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
pexact.Set(i,j,pexact.Get(i,j)+0.1);
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=pexact.Get(i,j);
|
|
for(i=0; i<n; i++)
|
|
pexact.Set(i,j,pexact.Get(i,j)/v);
|
|
}
|
|
//--- Initialize solver:
|
|
//--- * create object
|
|
//--- * add tracks
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Resize(2,n);
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
xy.Set(0,j,0);
|
|
xy.Set(0,i,1);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,pexact[j][i]);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,2);
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||MathAbs(p.Get(i,j)-pexact.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
//--- Second test:
|
|
//--- * N-dimensional problem
|
|
//--- * proportional data
|
|
//---*no "entry"/"exit" states
|
|
//--- * N tracks,each includes only two states
|
|
//--- * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
|
|
//--- * all tracks are modelled using randomly generated transition matrix P
|
|
offdiagonal=0.1;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- Initialize "exact" P:
|
|
//--- * fill by random values
|
|
//--- * make sure that each column sums to non-zero value
|
|
//--- * normalize
|
|
pexact.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
pexact.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
pexact.Set(i,j,pexact.Get(i,j)+0.1);
|
|
}
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=pexact.Get(i,j);
|
|
for(i=0; i<n; i++)
|
|
pexact.Set(i,j,pexact.Get(i,j)/v);
|
|
}
|
|
//--- Initialize solver:
|
|
//--- * create object
|
|
//--- * add tracks
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(2,n);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(0,j,0);
|
|
//--- "main" element
|
|
xy.Set(0,i,1.0-2*offdiagonal);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,(1.0-2*offdiagonal)*pexact[j][i]);
|
|
//--- off-diagonal ones
|
|
if(i>0)
|
|
{
|
|
xy.Set(0,i-1,offdiagonal);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,xy[1][j]+offdiagonal*pexact[j][i-1]);
|
|
}
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
xy.Set(0,i+1,offdiagonal);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,xy[1][j]+offdiagonal*pexact[j][i+1]);
|
|
}
|
|
//--- function call
|
|
CMarkovCPD::MCPDAddTrack(s,xy,2);
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||MathAbs(p.Get(i,j)-pexact.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
//--- Third test:
|
|
//--- * N-dimensional problem
|
|
//--- * population data
|
|
//---*no "entry"/"exit" states
|
|
//--- * N tracks,each includes only two states
|
|
//--- * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position,V in [1,10]
|
|
//--- * all tracks are modelled using randomly generated transition matrix P
|
|
offdiagonal=0.1;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- Initialize "exact" P:
|
|
//--- * fill by random values
|
|
//--- * make sure that each column sums to non-zero value
|
|
//--- * normalize
|
|
pexact.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
pexact.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
pexact.Set(i,j,pexact.Get(i,j)+0.1);
|
|
}
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=pexact.Get(i,j);
|
|
for(i=0; i<n; i++)
|
|
pexact.Set(i,j,pexact.Get(i,j)/v);
|
|
}
|
|
//--- Initialize solver:
|
|
//--- * create object
|
|
//--- * add tracks
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(2,n);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(0,j,0);
|
|
//--- "main" element
|
|
v0=9*CMath::RandomReal()+1;
|
|
xy.Set(0,i,v0*(1.0-2*offdiagonal));
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,v0*(1.0-2*offdiagonal)*pexact[j][i]);
|
|
//--- off-diagonal ones
|
|
if(i>0)
|
|
{
|
|
xy.Set(0,i-1,v0*offdiagonal);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,xy[1][j]+v0*offdiagonal*pexact[j][i-1]);
|
|
}
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
xy.Set(0,i+1,v0*offdiagonal);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(1,j,xy[1][j]+v0*offdiagonal*pexact[j][i+1]);
|
|
}
|
|
//--- function call
|
|
CMarkovCPD::MCPDAddTrack(s,xy,2);
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||MathAbs(p.Get(i,j)-pexact.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for different combinations of "entry"/"exit" models |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMCPDUnit::TestEntryExit(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
double threshold=1.0E-3;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int popkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble pexact;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- calculation
|
|
for(n=2; n<=5; n++)
|
|
{
|
|
for(entrykind=0; entrykind<=1; entrykind++)
|
|
{
|
|
for(exitkind=0; exitkind<=1; exitkind++)
|
|
{
|
|
for(popkind=0; popkind<=1; popkind++)
|
|
{
|
|
//--- Generate EntryState/ExitState such that one of the following is True:
|
|
//--- * EntryState<>ExitState
|
|
//--- * EntryState=-1 or ExitState=-1
|
|
do
|
|
{
|
|
//--- check
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=CMath::RandomInteger(n);
|
|
}
|
|
while(!((entrystate==-1 || exitstate==-1) || entrystate!=exitstate));
|
|
//--- Generate transition matrix P such that:
|
|
//--- * columns corresponding to non-exit states sums to 1.0
|
|
//--- * columns corresponding to exit states sums to 0.0
|
|
//--- * rows corresponding to entry states are zero
|
|
pexact.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
pexact.Set(i,j,1+CMath::RandomInteger(5));
|
|
//--- check
|
|
if(i==entrystate)
|
|
pexact.Set(i,j,0.0);
|
|
//--- check
|
|
if(j==exitstate)
|
|
pexact.Set(i,j,0.0);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
v+=pexact.Get(i,j);
|
|
//--- check
|
|
if(v!=0.0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
pexact.Set(i,j,pexact.Get(i,j)/v);
|
|
}
|
|
}
|
|
//--- Create MCPD solver
|
|
if(entrystate<0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreateEntry(n,entrystate,s);
|
|
//--- check
|
|
if(entrystate<0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateExit(n,exitstate,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s);
|
|
//--- Add N tracks.
|
|
//--- K-th track starts from vector with large value of
|
|
//--- K-th component and small random noise in other components.
|
|
//--- Track contains from 2 to 4 elements.
|
|
//--- Tracks contain proportional (normalized) or
|
|
//--- population data,depending on PopKind variable.
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- Generate track whose length is in 2..4
|
|
xy.Resize(2+CMath::RandomInteger(3),n);
|
|
for(j=0; j<n; j++)
|
|
xy.Set(0,j,0.05*CMath::RandomReal());
|
|
xy.Set(0,k,1+CMath::RandomReal());
|
|
//--- calculation
|
|
for(i=1; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(j!=entrystate)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=pexact.Get(j,i_)*xy[i-1][i_];
|
|
xy.Set(i,j,v);
|
|
}
|
|
else
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
}
|
|
}
|
|
//--- Normalize,if needed
|
|
if(popkind==1)
|
|
{
|
|
for(i=0; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(j=0; j<n; j++)
|
|
v+=xy.Get(i,j);
|
|
//--- check
|
|
if(v>0.0)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
xy.Set(i,j,xy.Get(i,j)/v);
|
|
}
|
|
}
|
|
}
|
|
//--- Add track
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=err||MathAbs(p.Get(i,j)-pexact.Get(i,j))>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test equality constraints. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMCPDUnit::TestEC(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ic=0;
|
|
int jc=0;
|
|
double vc=0;
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble ec;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- We try different problems with following properties:
|
|
//--- * N is large enough - we won't have problems with inconsistent constraints
|
|
//---*first state is either "entry" or "normal"
|
|
//---*last state is either "exit" or "normal"
|
|
//--- * we have one long random track
|
|
//--- We test several properties which are described in comments below
|
|
for(n=4; n<=6; n++)
|
|
{
|
|
for(entrykind=0; entrykind<=1; entrykind++)
|
|
{
|
|
for(exitkind=0; exitkind<=1; exitkind++)
|
|
{
|
|
//--- Prepare problem
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=0;
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=n-1;
|
|
//--- allocation
|
|
xy.Resize(2*n,n);
|
|
for(i=0; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
for(j=0; j<=CAp::Cols(xy)-1; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- Test that single equality constraint on non-entry
|
|
//--- non-exit elements of P is satisfied.
|
|
//--- NOTE: this test needs N>=4 because smaller values
|
|
//--- can give us inconsistent constraints
|
|
if(!CAp::Assert(n>=4,"TestEC: expectation failed"))
|
|
return;
|
|
ic=1+CMath::RandomInteger(n-2);
|
|
jc=1+CMath::RandomInteger(n-2);
|
|
vc=CMath::RandomReal();
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,ic,jc,vc);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
err=err||p[ic][jc]!=vc;
|
|
else
|
|
err=true;
|
|
//--- Test interaction with default "sum-to-one" constraint
|
|
//--- on columns of P.
|
|
//--- We set N-1 equality constraints on random non-exit column
|
|
//--- of P,which are inconsistent with this default constraint
|
|
//--- (sum will be greater that 1.0).
|
|
//--- Algorithm must detect inconsistency.
|
|
//--- NOTE:
|
|
//--- 1. we do not set constraints for the first element of
|
|
//--- the column,because this element may be constrained by
|
|
//--- "exit state" constraint.
|
|
//--- 2. this test needs N>=3
|
|
if(!CAp::Assert(n>=3,"TestEC: expectation failed"))
|
|
return;
|
|
jc=CMath::RandomInteger(n-1);
|
|
vc=0.95;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
for(i=1; i<n; i++)
|
|
CMarkovCPD::MCPDAddEC(s,i,jc,vc);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
//--- Test interaction with constrains on entry states.
|
|
//--- When model has entry state,corresponding row of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this row:
|
|
//--- * zero equality constraint,which must be consistent
|
|
//--- * non-zero equality constraint,which must be inconsistent
|
|
if(entrystate>=0)
|
|
{
|
|
jc=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.5);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test interaction with constrains on exit states.
|
|
//--- When model has exit state,corresponding column of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this column:
|
|
//--- * zero equality constraint,which must be consistent
|
|
//--- * non-zero equality constraint,which must be inconsistent
|
|
if(exitstate>=0)
|
|
{
|
|
ic=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.5);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test SetEC() call - we constrain subset of non-entry
|
|
//--- non-exit elements and test it.
|
|
if(!CAp::Assert(n>=4,"TestEC: expectation failed"))
|
|
return;
|
|
//--- allocation
|
|
ec.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ec.Set(i,j,CInfOrNaN::NaN());
|
|
}
|
|
for(j=1; j<=n-2; j++)
|
|
ec.Set(1+CMath::RandomInteger(n-2),j,0.1+0.1*CMath::RandomReal());
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetEC(s,ec);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::IsFinite(ec.Get(i,j)))
|
|
err=err||p.Get(i,j)!=ec.Get(i,j);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test bound constraints. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMCPDUnit::TestBC(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ic=0;
|
|
int jc=0;
|
|
double vl=0;
|
|
double vu=0;
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble bndl;
|
|
CMatrixDouble bndu;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- We try different problems with following properties:
|
|
//--- * N is large enough - we won't have problems with inconsistent constraints
|
|
//---*first state is either "entry" or "normal"
|
|
//---*last state is either "exit" or "normal"
|
|
//--- * we have one long random track
|
|
//--- We test several properties which are described in comments below
|
|
for(n=4; n<=6; n++)
|
|
{
|
|
for(entrykind=0; entrykind<=1; entrykind++)
|
|
{
|
|
for(exitkind=0; exitkind<=1; exitkind++)
|
|
{
|
|
//--- Prepare problem
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=0;
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=n-1;
|
|
//--- allocation
|
|
xy.Resize(2*n,n);
|
|
for(i=0; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
for(j=0; j<=CAp::Cols(xy)-1; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- Test that single bound constraint on non-entry
|
|
//--- non-exit elements of P is satisfied.
|
|
//--- NOTE 1: this test needs N>=4 because smaller values
|
|
//--- can give us inconsistent constraints
|
|
if(!CAp::Assert(n>=4,"TestBC: expectation failed"))
|
|
return;
|
|
//--- change values
|
|
ic=1+CMath::RandomInteger(n-2);
|
|
jc=1+CMath::RandomInteger(n-2);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
vl=0.3*CMath::RandomReal();
|
|
else
|
|
vl=CInfOrNaN::NegativeInfinity();
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
vu=0.5+0.3*CMath::RandomReal();
|
|
else
|
|
vu=CInfOrNaN::PositiveInfinity();
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,ic,jc,vl,vu);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
err=err||p[ic][jc]<vl;
|
|
err=err||p[ic][jc]>vu;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- Test interaction with default "sum-to-one" constraint
|
|
//--- on columns of P.
|
|
//--- We set N-1 bound constraints on random non-exit column
|
|
//--- of P,which are inconsistent with this default constraint
|
|
//--- (sum will be greater that 1.0).
|
|
//--- Algorithm must detect inconsistency.
|
|
//--- NOTE:
|
|
//--- 1. we do not set constraints for the first element of
|
|
//--- the column,because this element may be constrained by
|
|
//--- "exit state" constraint.
|
|
//--- 2. this test needs N>=3
|
|
if(!CAp::Assert(n>=3,"TestEC: expectation failed"))
|
|
return;
|
|
jc=CMath::RandomInteger(n-1);
|
|
vl=0.85;
|
|
vu=0.95;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
for(i=1; i<n; i++)
|
|
CMarkovCPD::MCPDAddBC(s,i,jc,vl,vu);
|
|
//--- function calls
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
//--- Test interaction with constrains on entry states.
|
|
//--- When model has entry state,corresponding row of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this row:
|
|
//--- * bound constraint with zero lower bound,which must be consistent
|
|
//--- * bound constraint with non-zero lower bound,which must be inconsistent
|
|
if(entrystate>=0)
|
|
{
|
|
jc=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.0,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.5,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test interaction with constrains on exit states.
|
|
//--- When model has exit state,corresponding column of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this column:
|
|
//--- * bound constraint with zero lower bound,which must be consistent
|
|
//--- * bound constraint with non-zero lower bound,which must be inconsistent
|
|
if(exitstate>=0)
|
|
{
|
|
ic=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.0,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.5,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test SetBC() call - we constrain subset of non-entry
|
|
//--- non-exit elements and test it.
|
|
if(!CAp::Assert(n>=4,"TestBC: expectation failed"))
|
|
return;
|
|
//--- allocation
|
|
bndl.Resize(n,n);
|
|
bndu.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
bndl.Set(i,j,CInfOrNaN::NegativeInfinity());
|
|
bndu.Set(i,j,CInfOrNaN::PositiveInfinity());
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=1; j<=n-2; j++)
|
|
{
|
|
i=1+CMath::RandomInteger(n-2);
|
|
bndl.Set(i,j,0.5-0.1*CMath::RandomReal());
|
|
bndu.Set(i,j,0.5+0.1*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetBC(s,bndl,bndu);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
err=err||p.Get(i,j)<bndl.Get(i,j);
|
|
err=err||p.Get(i,j)>bndu.Get(i,j);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test bound constraints. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMCPDUnit::TestLC(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int t=0;
|
|
int jc=0;
|
|
double v=0;
|
|
double threshold=0;
|
|
//--- create array
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble c;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- initialization
|
|
threshold=1.0E5*CMath::m_machineepsilon;
|
|
//--- We try different problems with following properties:
|
|
//--- * N is large enough - we won't have problems with inconsistent constraints
|
|
//---*first state is either "entry" or "normal"
|
|
//---*last state is either "exit" or "normal"
|
|
//--- * we have one long random track
|
|
//--- We test several properties which are described in comments below
|
|
for(n=4; n<=6; n++)
|
|
{
|
|
for(entrykind=0; entrykind<=1; entrykind++)
|
|
{
|
|
for(exitkind=0; exitkind<=1; exitkind++)
|
|
{
|
|
//--- Prepare problem
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=0;
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=n-1;
|
|
//--- allocation
|
|
xy.Resize(2*n,n);
|
|
for(i=0; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
for(j=0; j<=CAp::Cols(xy)-1; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- Test that single linear equality/inequality constraint
|
|
//--- on non-entry non-exit elements of P is satisfied.
|
|
//--- NOTE 1: this test needs N>=4 because smaller values
|
|
//--- can give us inconsistent constraints
|
|
//--- NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
|
|
//--- is always feasible. It guarantees that there always exists
|
|
//--- at least one feasible point
|
|
//--- NOTE 3: If we have inequality constraint,we "shift" right part
|
|
//--- in order to make feasible some neighborhood of P=(1/N ... 1/N).
|
|
if(!CAp::Assert(n>=4,"TestLC: expectation failed"))
|
|
return;
|
|
//--- allocation
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
//--- calculation
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(((i==0 || i==n-1) || j==0) || j==n-1)
|
|
c.Set(0,i*n+j,0);
|
|
else
|
|
{
|
|
c.Set(0,i*n+j,CMath::RandomReal());
|
|
v+=c[0][i*n+j]*(1.0/(double)n);
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
c.Set(0,n*n,v);
|
|
ct[0]=CMath::RandomInteger(3)-1;
|
|
//--- check
|
|
if(ct[0]<0)
|
|
c.Set(0,n*n,c[0][n*n]+0.1);
|
|
//--- check
|
|
if(ct[0]>0)
|
|
c.Set(0,n*n,c[0][n*n]-0.1);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
v+=p.Get(i,j)*c[0][i*n+j];
|
|
}
|
|
//--- check
|
|
if(ct[0]<0)
|
|
err=err||v>=c[0][n*n]+threshold;
|
|
//--- check
|
|
if(ct[0]==0)
|
|
err=err||MathAbs(v-c[0][n*n])>=threshold;
|
|
//--- check
|
|
if(ct[0]>0)
|
|
err=err||v<=c[0][n*n]-threshold;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- Test interaction with default "sum-to-one" constraint
|
|
//--- on columns of P.
|
|
//--- We set linear constraint which has for "sum-to-X" on
|
|
//--- on random non-exit column of P. This constraint can be
|
|
//--- either consistent (X=1.0) or inconsistent (X<>1.0) with
|
|
//--- this default constraint.
|
|
//--- Algorithm must detect inconsistency.
|
|
//--- NOTE:
|
|
//--- 1. this test needs N>=2
|
|
if(!CAp::Assert(n>=2,"TestLC: expectation failed"))
|
|
return;
|
|
jc=CMath::RandomInteger(n-1);
|
|
//--- allocation
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
//--- change values
|
|
for(i=0; i<=n*n-1; i++)
|
|
c.Set(0,i,0.0);
|
|
for(i=0; i<n; i++)
|
|
c.Set(0,n*i+jc,1.0);
|
|
c.Set(0,n*n,1.0);
|
|
ct[0]=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
c.Set(0,n*n,2.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
//--- Test interaction with constrains on entry states.
|
|
//--- When model has entry state,corresponding row of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on elements of this row:
|
|
//--- * sums-to-zero constraint,which must be consistent
|
|
//--- * sums-to-one constraint,which must be inconsistent
|
|
if(entrystate>=0)
|
|
{
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
for(i=0; i<=n*n-1; i++)
|
|
c.Set(0,i,0.0);
|
|
for(j=0; j<n; j++)
|
|
c.Set(0,n*entrystate+j,1.0);
|
|
ct[0]=0;
|
|
c.Set(0,n*n,0.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
c.Set(0,n*n,1.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test interaction with constrains on exit states.
|
|
//--- When model has exit state,corresponding column of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on elements of this column:
|
|
//--- * sums-to-zero constraint,which must be consistent
|
|
//--- * sums-to-one constraint,which must be inconsistent
|
|
if(exitstate>=0)
|
|
{
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
//--- change values
|
|
for(i=0; i<=n*n-1; i++)
|
|
c.Set(0,i,0.0);
|
|
for(i=0; i<n; i++)
|
|
c.Set(0,n*i+exitstate,1.0);
|
|
ct[0]=0;
|
|
c.Set(0,n*n,0.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
c.Set(0,n*n,1.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Final test - we generate several random constraints and
|
|
//--- test SetLC() function.
|
|
//--- NOTES:
|
|
//--- 1. Constraints are generated is such a way that P=(1/N ... 1/N)
|
|
//--- is always feasible. It guarantees that there always exists
|
|
//--- at least one feasible point
|
|
//--- 2. For simplicity of the test we do not use entry/exit states
|
|
//--- in our model
|
|
for(n=1; n<=4; n++)
|
|
{
|
|
for(k=1; k<=2*n; k++)
|
|
{
|
|
//--- Generate track
|
|
xy.Resize(2*n,n);
|
|
for(i=0; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
for(j=0; j<=CAp::Cols(xy)-1; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
}
|
|
//--- Generate random constraints
|
|
c.Resize(k,n*n+1);
|
|
ArrayResize(ct,k);
|
|
//--- calculation
|
|
for(i=0; i<k; i++)
|
|
{
|
|
//--- Generate constraint and its right part
|
|
c.Set(i,n*n,0);
|
|
for(j=0; j<=n*n-1; j++)
|
|
{
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
c.Set(i,n*n,c[i][n*n]+c.Get(i,j)*(1.0/(double)n));
|
|
}
|
|
ct[i]=CMath::RandomInteger(3)-1;
|
|
//--- If we have inequality constraint,we "shift" right part
|
|
//--- in order to make feasible some neighborhood of P=(1/N ... 1/N).
|
|
if(ct[i]<0)
|
|
c.Set(i,n*n,c[i][n*n]+0.1);
|
|
//--- check
|
|
if(ct[i]>0)
|
|
c.Set(i,n*n,c[i][n*n]-0.1);
|
|
}
|
|
//--- Test
|
|
CreateEE(n,-1,-1,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,k);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(t=0; t<k; t++)
|
|
{
|
|
//--- change values
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
v+=p.Get(i,j)*c[t][i*n+j];
|
|
}
|
|
//--- check
|
|
if(ct[t]<0)
|
|
err=err||v>=c[t][n*n]+threshold;
|
|
//--- check
|
|
if(ct[t]==0)
|
|
err=err||MathAbs(v-c[t][n*n])>=threshold;
|
|
//--- check
|
|
if(ct[t]>0)
|
|
err=err||v<=c[t][n*n]-threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to create MCPD object with arbitrary |
|
|
//| combination of entry and exit states |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMCPDUnit::CreateEE(const int n,const int entrystate,
|
|
const int exitstate,CMCPDState &s)
|
|
{
|
|
//--- check
|
|
if(entrystate<0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreateEntry(n,entrystate,s);
|
|
//--- check
|
|
if(entrystate<0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateExit(n,exitstate,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CFbls |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFblsUnit
|
|
{
|
|
public:
|
|
static bool TestFbls(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFblsUnit::TestFbls(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool waserrors;
|
|
bool cgerrors;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double alpha=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
CRowDouble tmp1;
|
|
CRowDouble tmp2;
|
|
double b[];
|
|
CRowDouble x;
|
|
double xe[];
|
|
double buf[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- object of class
|
|
CFblsLinCgState cgstate;
|
|
//--- initialization
|
|
mx=10;
|
|
waserrors=false;
|
|
cgerrors=false;
|
|
//--- Test CG solver:
|
|
//--- * generate problem (A,B,Alpha,XE - exact solution) and initial approximation X
|
|
//--- * E1=||A'A*x-b||
|
|
//--- * solve
|
|
//--- * E2=||A'A*x-b||
|
|
//--- * test that E2<0.001*E1
|
|
for(n=1; n<=mx; n++)
|
|
{
|
|
for(m=1; m<=mx; m++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(m,n);
|
|
ArrayResize(b,n);
|
|
x.Resize(n);
|
|
ArrayResize(xe,n);
|
|
tmp1.Resize(m);
|
|
tmp2.Resize(n);
|
|
//--- init A,alpha,B,X (initial approximation),XE (exact solution)
|
|
//--- X is initialized in such way that is has no chances to be equal to XE.
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
alpha=CMath::RandomReal()+0.1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b[i]=2*CMath::RandomReal()-1;
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
x.Set(i,(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal()));
|
|
}
|
|
//--- Test dense CG (which solves A'A*x=b and accepts dense A)
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal()));
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
tmp2+=x*alpha+0;
|
|
tmp2-=b;
|
|
//--- change value
|
|
v=CAblasF::RDotV2(n,tmp2);
|
|
e1=MathSqrt(v);
|
|
//--- function calls
|
|
double X[];
|
|
x.ToArray(X);
|
|
CFbls::FblsSolveCGx(a,m,n,alpha,b,X,buf);
|
|
x=X;
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
tmp2+=x*alpha+0;
|
|
tmp2-=b;
|
|
//--- change value
|
|
v=CAblasF::RDotV2(n,tmp2);
|
|
e2=MathSqrt(v);
|
|
//--- search errors
|
|
cgerrors=cgerrors||e2>0.001*e1;
|
|
//--- Test sparse CG (which relies on reverse communication)
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal()));
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
tmp2+=x*alpha+0;
|
|
tmp2-=b;
|
|
//--- change value
|
|
v=CAblasF::RDotV2(n,tmp2);
|
|
e1=MathSqrt(v);
|
|
//--- function call
|
|
x.ToArray(X);
|
|
CFbls::FblsCGCreate(X,b,n,cgstate);
|
|
x=X;
|
|
//--- cycle
|
|
while(CFbls::FblsCGIteration(cgstate))
|
|
{
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,cgstate.m_ax,0);
|
|
cgstate.m_ax+=cgstate.m_x*alpha+0;
|
|
//--- change values
|
|
v1=CAblasF::RDotV2(m,tmp1);
|
|
v2=CAblasF::RDotV2(n,cgstate.m_x);
|
|
cgstate.m_xax=v1+alpha*v2;
|
|
}
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_xk,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
tmp2+=cgstate.m_xk*alpha+0;
|
|
tmp2-=b;
|
|
//--- change value
|
|
v=CAblasF::RDotV2(n,tmp2);
|
|
e2=MathSqrt(v);
|
|
//--- search errors
|
|
cgerrors=cgerrors||MathAbs(e1-cgstate.m_e1)>100*CMath::m_machineepsilon*e1;
|
|
cgerrors=cgerrors||MathAbs(e2-cgstate.m_e2)>100*CMath::m_machineepsilon*e1;
|
|
cgerrors=cgerrors||e2>0.001*e1;
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=cgerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FBLS");
|
|
PrintResult("CG ERRORS",!cgerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLBFGS |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinLBFGSUnit
|
|
{
|
|
public:
|
|
static bool TestMinLBFGS(const bool silent);
|
|
|
|
private:
|
|
static void TestFunc1(CMinLBFGSState &state);
|
|
static void TestFunc2(CMinLBFGSState &state);
|
|
static void TestFunc3(CMinLBFGSState &state);
|
|
static void CalcIIP2(CMinLBFGSState &state,const int n);
|
|
static void TestPreconditioning(bool &err);
|
|
static void TestOther(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLBFGS |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinLBFGSUnit::TestMinLBFGS(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool referror;
|
|
bool nonconverror;
|
|
bool eqerror;
|
|
bool converror;
|
|
bool crashtest;
|
|
bool othererrors;
|
|
bool restartserror;
|
|
bool precerror;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int maxits=0;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xe[];
|
|
double b[];
|
|
double xlast[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinLBFGSState state;
|
|
CMinLBFGSReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
precerror=false;
|
|
nonconverror=false;
|
|
restartserror=false;
|
|
eqerror=false;
|
|
converror=false;
|
|
crashtest=false;
|
|
othererrors=false;
|
|
referror=false;
|
|
//--- function calls
|
|
TestPreconditioning(precerror);
|
|
TestOther(othererrors);
|
|
//--- Reference problem
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
n=3;
|
|
m=2;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
x[1]=100*CMath::RandomReal()-50;
|
|
x[2]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
referror=((rep.m_terminationtype<=0||MathAbs(x[0]-2)>0.001)||MathAbs(x[1])>0.001)||MathAbs(x[2]-2)>0.001;
|
|
}
|
|
//--- nonconvex problems with complex surface: we start from point with very small
|
|
//--- gradient,but we need ever smaller gradient in the next step due to
|
|
//--- Wolfe conditions.
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
n=1;
|
|
m=1;
|
|
v=-100;
|
|
//--- calculation
|
|
while(v<0.1)
|
|
{
|
|
x[0]=v;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(0,(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0])));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
nonconverror=(nonconverror||rep.m_terminationtype<=0)||MathAbs(x[0])>0.001;
|
|
v+=0.1;
|
|
}
|
|
}
|
|
//--- F2 problem with restarts:
|
|
//--- * make several iterations and restart BEFORE termination
|
|
//--- * iterate and restart AFTER termination
|
|
//--- NOTE: step is bounded from above to avoid premature convergence
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
m=2;
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetStpMax(state,0.1);
|
|
CMinLBFGS::MinLBFGSSetCond(state,0.0000001,0.0,0.0,0);
|
|
//--- calculation
|
|
for(i=0; i<=10; i++)
|
|
{
|
|
//--- check
|
|
if(!CMinLBFGS::MinLBFGSIteration(state))
|
|
break;
|
|
TestFunc2(state);
|
|
}
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01;
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01;
|
|
}
|
|
//--- Linear equations
|
|
diffstep=1.0E-6;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Prepare task
|
|
a.Resize(n,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(i,i,a[i][i]+3*MathSign(a[i][i]));
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- Test different M/DKind
|
|
for(m=1; m<=n; m++)
|
|
{
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- Solve task
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
state.m_g.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*state.m_x[i_];
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr(v-b[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
state.m_g.Add(j,2*(v-b[i])*a.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
eqerror=eqerror||rep.m_terminationtype<=0;
|
|
for(i=0; i<n; i++)
|
|
eqerror=eqerror||MathAbs(x[i]-xe[i])>0.001;
|
|
}
|
|
}
|
|
}
|
|
//--- Testing convergence properties
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
m=2;
|
|
for(i=0; i<=2; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0.001,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||rep.m_terminationtype!=4;
|
|
//--- change values
|
|
for(i=0; i<=2; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0.001,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||rep.m_terminationtype!=1;
|
|
//--- change values
|
|
for(i=0; i<=2; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0.001,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror||rep.m_terminationtype!=2;
|
|
//--- change values
|
|
for(i=0; i<=2; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,10);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=(converror||rep.m_terminationtype!=5)||rep.m_iterationscount!=10;
|
|
}
|
|
//--- Crash test: too many iterations on a simple tasks
|
|
//--- May fail when encounter zero step,underflow or something like that
|
|
ArrayResize(x,3);
|
|
n=3;
|
|
m=2;
|
|
maxits=10000;
|
|
for(i=0; i<=2; i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,maxits);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
crashtest=crashtest||rep.m_terminationtype<=0;
|
|
//--- end
|
|
waserrors=((((((referror||nonconverror)||eqerror)||converror)||crashtest)||othererrors)||restartserror)||precerror;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING L-BFGS OPTIMIZATION");
|
|
PrintResult("REFERENCE PROBLEM",!referror);
|
|
PrintResult("NON-CONVEX PROBLEM",!nonconverror);
|
|
PrintResult("LINEAR EQUATIONS",!eqerror);
|
|
PrintResult("RESTARTS",!restartserror);
|
|
PrintResult("PRECONDITIONER",!precerror);
|
|
PrintResult("CONVERGENCE PROPERTIES",!converror);
|
|
PrintResult("CRASH TEST",!crashtest);
|
|
PrintResult("OTHER PROPERTIES",!othererrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #1 |
|
|
//| It may show very interesting behavior when optimized with |
|
|
//| 'x[0]>=ln(2)' constraint. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLBFGSUnit::TestFunc1(CMinLBFGSState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber));
|
|
state.m_g.Set(1,0);
|
|
state.m_g.Set(2,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #2 |
|
|
//| Simple variation of #1,much more nonlinear,which makes unlikely |
|
|
//| premature convergence of algorithm. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLBFGSUnit::TestFunc2(CMinLBFGSState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,4*state.m_x[1]*CMath::Sqr(state.m_x[1]));
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber));
|
|
state.m_g.Set(1,0);
|
|
state.m_g.Set(2,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #3 |
|
|
//| Simple variation of #1, much more nonlinear, with non-zero value |
|
|
//| at minimum. It achieve two goals: |
|
|
//| * makes unlikely premature convergence of algorithm . |
|
|
//| * solves some issues with EpsF stopping condition which arise |
|
|
//| when F(minimum) is zero |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLBFGSUnit::TestFunc3(CMinLBFGSState &state)
|
|
{
|
|
double s=0.001;
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber));
|
|
state.m_g.Set(1,0);
|
|
state.m_g.Set(2,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLBFGSUnit::CalcIIP2(CMinLBFGSState &state,const int n)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,CMath::Sqr(i*i+1)*2*state.m_x[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLBFGSUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int cntb1=0;
|
|
int cntb2=0;
|
|
int cntg1=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
int pkind=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double s[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinLBFGSState state;
|
|
CMinLBFGSReport rep;
|
|
//--- initialization
|
|
m=1;
|
|
k=50;
|
|
epsg=1.0E-10;
|
|
//--- Preconditioner test1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner
|
|
//--- * B2 is Cholesky preconditioner with unit diagonal
|
|
//--- * G1 is Cholesky preconditioner based on exact Hessian with perturbations
|
|
//--- * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix
|
|
//--- then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..).
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) and L-BFGS
|
|
//--- optimizer with deliberately small M=1.
|
|
//--- N - problem size
|
|
//--- PKind - zero for upper triangular preconditioner,one for lower triangular.
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
for(n=10; n<=15; n++)
|
|
{
|
|
pkind=CMath::RandomInteger(2);
|
|
ArrayResize(x,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=0;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- Test it with default preconditioner
|
|
CMinLBFGS::MinLBFGSSetPrecDefault(state);
|
|
cntb1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntb1+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with unit preconditioner
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a.Set(i,i,1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
|
|
//--- change values
|
|
cntb2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntb2+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with perturbed Hessian preconditioner
|
|
a.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a.Set(i,i,(i*i+1)*(0.8+0.4*CMath::RandomReal()));
|
|
else
|
|
{
|
|
//--- check
|
|
if((pkind==0 && j>i) || (pkind==1 && j<i))
|
|
a.Set(i,j,0.1*CMath::RandomReal()-0.05);
|
|
else
|
|
a.Set(i,j,CInfOrNaN::NaN());
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
|
|
cntg1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntg1+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with perturbed diagonal preconditioner
|
|
ArrayResize(diagh,n);
|
|
for(i=0; i<n; i++)
|
|
diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal());
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecDiag(state,diagh);
|
|
cntg2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntg2+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- Compare
|
|
err=err||cntb1<cntg1;
|
|
err=err||cntb2<cntg1;
|
|
err=err||cntb1<cntg2;
|
|
err=err||cntb2<cntg2;
|
|
}
|
|
//--- Preconditioner test 2.
|
|
//--- If
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
for(n=10; n<=15; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=0;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- allocation
|
|
ArrayResize(s,n);
|
|
for(i=0; i<n; i++)
|
|
s[i]=1/MathSqrt(2*MathPow(i*i+1,2)*(0.8+0.4*CMath::RandomReal()));
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetPrecDefault(state);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
cntb2=0;
|
|
//--- calculation
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntb2+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetPrecScale(state);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
cntg2=0;
|
|
//--- calculation
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntg2+=rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err||rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err||cntb2<cntg2;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests other properties |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLBFGSUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
bool hasxlast;
|
|
double lastscaledstep=0;
|
|
int i=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double v=0;
|
|
double stpmax=0;
|
|
double tmpeps=0;
|
|
double epsg=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
double r=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double a[];
|
|
double s[];
|
|
double h[];
|
|
double xlast[];
|
|
//--- objects of classes
|
|
CMinLBFGSState state;
|
|
CMinLBFGSReport rep;
|
|
//--- Test reports (F should form monotone sequence)
|
|
n=50;
|
|
m=2;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=1;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,100);
|
|
CMinLBFGS::MinLBFGSSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
err=err||state.m_f>fprev;
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
err=err||state.m_x[i]!=x[i];
|
|
}
|
|
//--- change values
|
|
fprev=state.m_f;
|
|
for(i_=0; i_<n; i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
err=err||x[i]!=xlast[i];
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests,second one issues NeedFG requests)
|
|
n=50;
|
|
m=5;
|
|
diffstep=1.0E-6;
|
|
//--- calculation
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0; i<n; i++)
|
|
x[i]=1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,n/2);
|
|
wasf=false;
|
|
wasfg=false;
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
//--- search errors
|
|
wasf=wasf||state.m_needf;
|
|
wasfg=wasfg||state.m_needfg;
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(dkind==0)
|
|
err=(err||wasf)||!wasfg;
|
|
//--- check
|
|
if(dkind==1)
|
|
err=(err||!wasf)||wasfg;
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S,then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
ArrayResize(x,1);
|
|
ArrayResize(s,1);
|
|
//--- change values
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s[0]=MathExp(CMath::RandomReal()*4-2);
|
|
x[0]=0;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreateF(1,1,x,diffstep,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,1.0E-6,0,0,0);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
v=0;
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
//--- search errors
|
|
err=err||MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
|
|
//--- test maximum step
|
|
n=1;
|
|
m=1;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
x[0]=100;
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0);
|
|
CMinLBFGS::MinLBFGSSetStpMax(state,stpmax);
|
|
CMinLBFGS::MinLBFGSSetXRep(state,true);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
|
|
//--- search errors
|
|
err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- search errors
|
|
err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set stringent stopping conditions (we try EpsG and EpsX)
|
|
//--- * and we test that in the extremum stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
tmpeps=1.0E-10;
|
|
m=1;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pkind=0; pkind<=2; pkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
ArrayResize(a,n);
|
|
ArrayResize(s,n);
|
|
ArrayResize(h,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=CMath::RandomReal()+1;
|
|
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
CMinLBFGS::MinLBFGSSetXRep(state,true);
|
|
//--- check
|
|
if(pkind==1)
|
|
CMinLBFGS::MinLBFGSSetPrecDiag(state,h);
|
|
//--- check
|
|
if(pkind==2)
|
|
CMinLBFGS::MinLBFGSSetPrecScale(state);
|
|
//--- Test gradient-based stopping condition
|
|
for(i=0; i<n; i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetCond(state,tmpeps,0,0,0);
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g.Set(i,4*a[i]*MathPow(state.m_x[i],3));
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
err=err||v>tmpeps;
|
|
//--- Test step-based stopping condition
|
|
for(i=0; i<n; i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
hasxlast=false;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,tmpeps,0);
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g.Set(i,4*a[i]*MathPow(state.m_x[i],3));
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(hasxlast)
|
|
{
|
|
lastscaledstep=0;
|
|
for(i=0; i<n; i++)
|
|
lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]);
|
|
lastscaledstep=MathSqrt(lastscaledstep);
|
|
}
|
|
else
|
|
lastscaledstep=0;
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
hasxlast=true;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err||lastscaledstep>tmpeps;
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
|
|
//--- f(x)={
|
|
//--- { M,if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
for(mkind=0; mkind<=2; mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
//--- check
|
|
if(ckind==1)
|
|
vc=1.0E+6;
|
|
//--- check
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
//--- check
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
//--- check
|
|
if(mkind==2)
|
|
vm=CInfOrNaN::PositiveInfinity();
|
|
//--- Create optimizer,solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
ArrayResize(x,1);
|
|
x[0]=0.0;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(1,1,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,epsg,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- check
|
|
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g.Set(0,1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc);
|
|
}
|
|
else
|
|
state.m_f=vm;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err||MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPBase and CMLPTrain |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMLPTrainUnit
|
|
{
|
|
public:
|
|
static bool TestMLPTrain(const bool silent);
|
|
|
|
private:
|
|
static void CreateNetwork(CMultilayerPerceptron &network,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout);
|
|
static void UnsetNetwork(CMultilayerPerceptron &network);
|
|
static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
static void TestGradient(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
static void TestHessian(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPBase and CMLPTrain |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPTrainUnit::TestMLPTrain(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxhid=0;
|
|
int info=0;
|
|
int nf=0;
|
|
int nl=0;
|
|
int nhid1=0;
|
|
int nhid2=0;
|
|
int nkind=0;
|
|
int i=0;
|
|
int ncount=0;
|
|
bool inferrors;
|
|
bool procerrors;
|
|
bool graderrors;
|
|
bool hesserrors;
|
|
bool trnerrors;
|
|
//--- objects of classes
|
|
CMultilayerPerceptron network;
|
|
CMultilayerPerceptron network2;
|
|
CMLPReport rep;
|
|
CMLPCVReport cvrep;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble valxy;
|
|
//--- initialization
|
|
waserrors=false;
|
|
inferrors=false;
|
|
procerrors=false;
|
|
graderrors=false;
|
|
hesserrors=false;
|
|
trnerrors=false;
|
|
passcount=10;
|
|
maxn=4;
|
|
maxhid=4;
|
|
//--- General multilayer network tests
|
|
for(nf=1; nf<=maxn; nf++)
|
|
{
|
|
for(nl=1; nl<=maxn; nl++)
|
|
{
|
|
for(nhid1=0; nhid1<=maxhid; nhid1++)
|
|
{
|
|
for(nhid2=0; nhid2<=0; nhid2++)
|
|
{
|
|
for(nkind=0; nkind<=3; nkind++)
|
|
{
|
|
//--- Skip meaningless parameters combinations
|
|
if(nkind==1 && nl<2)
|
|
continue;
|
|
//--- check
|
|
if(nhid1==0 && nhid2!=0)
|
|
continue;
|
|
//--- Tests
|
|
TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors);
|
|
TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors);
|
|
TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,graderrors);
|
|
TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test network training on simple XOR problem
|
|
xy.Resize(4,3);
|
|
xy.Set(0,0,-1);
|
|
xy.Set(0,1,-1);
|
|
xy.Set(0,2,-1);
|
|
xy.Set(1,0,1);
|
|
xy.Set(1,1,-1);
|
|
xy.Set(1,2,1);
|
|
xy.Set(2,0,-1);
|
|
xy.Set(2,1,1);
|
|
xy.Set(2,2,1);
|
|
xy.Set(3,0,1);
|
|
xy.Set(3,1,1);
|
|
xy.Set(3,2,-1);
|
|
//--- function calls
|
|
CMLPBase::MLPCreate1(2,2,1,network);
|
|
CMLPTrain::MLPTrainLM(network,xy,4,0.001,10,info,rep);
|
|
//--- search errors
|
|
trnerrors=trnerrors||CMLPBase::MLPRMSError(network,xy,4)>0.1;
|
|
//--- Test CV on random noisy problem
|
|
ncount=100;
|
|
xy.Resize(ncount,2);
|
|
//--- change values
|
|
for(i=0; i<=ncount-1; i++)
|
|
{
|
|
xy.Set(i,0,2*CMath::RandomReal()-1);
|
|
xy.Set(i,1,CMath::RandomInteger(4));
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPCreateC0(1,4,network);
|
|
CMLPTrain::MLPKFoldCVLM(network,xy,ncount,0.001,5,10,info,rep,cvrep);
|
|
//--- Final report
|
|
waserrors=(((inferrors||procerrors)||graderrors)||hesserrors)||trnerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("MLP TEST");
|
|
PrintResult("INFORMATIONAL FUNCTIONS",!inferrors);
|
|
PrintResult("BASIC PROCESSING",!procerrors);
|
|
PrintResult("GRADIENT CALCULATION",!graderrors);
|
|
PrintResult("HESSIAN CALCULATION",!hesserrors);
|
|
PrintResult("TRAINING",!trnerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Network creation |
|
|
//| This function creates network with desired structure. Network is |
|
|
//| created using one of the three methods: |
|
|
//| a) straighforward creation using MLPCreate???() |
|
|
//| b) MLPCreate???() for proxy object,which is copied with |
|
|
//| PassThroughSerializer() |
|
|
//| c) MLPCreate???() for proxy object,which is copied with MLPCopy()|
|
|
//| One of these methods is chosen with probability 1/3. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPTrainUnit::CreateNetwork(CMultilayerPerceptron &network,
|
|
const int nkind,const double a1,
|
|
const double a2,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout)
|
|
{
|
|
//--- create a variable
|
|
int mkind=0;
|
|
//--- object of class
|
|
CMultilayerPerceptron tmp;
|
|
//--- check
|
|
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
|
|
return;
|
|
//--- change value
|
|
mkind=CMath::RandomInteger(3);
|
|
//--- check
|
|
if(nhid1==0)
|
|
{
|
|
//--- No hidden layers
|
|
if(nkind==0)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate0(nin,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate0(nin,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate0(nin,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC0(nin,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateC0(nin,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC0(nin,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
return;
|
|
}
|
|
//--- check
|
|
if(nhid2==0)
|
|
{
|
|
//--- One hidden layer
|
|
if(nkind==0)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
return;
|
|
}
|
|
//--- Two hidden layers
|
|
if(nkind==0)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets network (initialize it to smallest network possible |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPTrainUnit::UnsetNetwork(CMultilayerPerceptron &network)
|
|
{
|
|
CMLPBase::MLPCreate0(1,1,network);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Informational functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPTrainUnit::TestInformational(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double threshold=0;
|
|
int nlayers=0;
|
|
int nmax=0;
|
|
bool issoftmax;
|
|
double mean=0;
|
|
double sigma=0;
|
|
int fkind=0;
|
|
double c=0;
|
|
double f=0;
|
|
double df=0;
|
|
double d2f=0;
|
|
double s=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble neurons;
|
|
//--- object of class
|
|
CMultilayerPerceptron network;
|
|
//--- initialization
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
//--- function call
|
|
CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout);
|
|
//--- test MLPProperties()
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- search errors
|
|
err=((err||n1!=nin)||n2!=nout)||wcount<=0;
|
|
//--- Test network geometry functions
|
|
//--- In order to do this we calculate neural network output using
|
|
//--- informational functions only,and compare results with ones
|
|
//--- obtained with MLPProcess():
|
|
//--- 1. we allocate 2-dimensional array of neurons and fill it by zeros
|
|
//--- 2. we full first layer of neurons by input values
|
|
//--- 3. we move through array,calculating values of subsequent layers
|
|
//--- 4. if we have classification network,we SOFTMAX-normalize output layer
|
|
//--- 5. we apply scaling to the outputs
|
|
//--- 6. we compare results with ones obtained by MLPProcess()
|
|
//--- NOTE: it is important to do (4) before (5),because on SOFTMAX network
|
|
//--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
|
|
//--- to test it implicitly,we apply it to the classifier results
|
|
//--- (already normalized). If one of the coefficients deviates from
|
|
//--- expected values,we will get error during (6).
|
|
nlayers=2;
|
|
nmax=MathMax(nin,nout);
|
|
issoftmax=nkind==1;
|
|
//--- check
|
|
if(nhid1!=0)
|
|
{
|
|
nlayers=3;
|
|
nmax=MathMax(nmax,nhid1);
|
|
}
|
|
//--- check
|
|
if(nhid2!=0)
|
|
{
|
|
nlayers=4;
|
|
nmax=MathMax(nmax,nhid2);
|
|
}
|
|
//--- allocation
|
|
neurons.Resize(nlayers,nmax);
|
|
for(i=0; i<=nlayers-1; i++)
|
|
{
|
|
for(j=0; j<=nmax-1; j++)
|
|
neurons.Set(i,j,0);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,nin);
|
|
//--- change values
|
|
for(i=0; i<nin ; i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(y,nout);
|
|
//--- change values
|
|
for(i=0; i<nout; i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
for(j=0; j<nin ; j++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPGetInputScaling(network,j,mean,sigma);
|
|
neurons.Set(0,j,(x[j]-mean)/sigma);
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=nlayers-1; i++)
|
|
{
|
|
for(j=0; j<=CMLPBase::MLPGetLayerSize(network,i)-1; j++)
|
|
{
|
|
for(k=0; k<=CMLPBase::MLPGetLayerSize(network,i-1)-1; k++)
|
|
neurons.Set(i,j,neurons.Get(i,j)+CMLPBase::MLPGetWeight(network,i-1,k,i,j)*neurons[i-1][k]);
|
|
//--- function calls
|
|
CMLPBase::MLPGetNeuronInfo(network,i,j,fkind,c);
|
|
CMLPBase::MLPActivationFunction(neurons.Get(i,j)-c,fkind,f,df,d2f);
|
|
neurons.Set(i,j,f);
|
|
}
|
|
}
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
s=0;
|
|
for(j=0; j<nout; j++)
|
|
s=s+MathExp(neurons[nlayers-1][j]);
|
|
for(j=0; j<nout; j++)
|
|
neurons.Set(nlayers-1,j,MathExp(neurons[nlayers-1][j])/s);
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPGetOutputScaling(network,j,mean,sigma);
|
|
neurons.Set(nlayers-1,j,neurons[nlayers-1][j]*sigma+mean);
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y);
|
|
//--- search errors
|
|
for(j=0; j<nout; j++)
|
|
err=err||MathAbs(neurons[nlayers-1][j]-y[j])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPTrainUnit::TestProcessing(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
bool zeronet;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
bool allsame;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- objects of classes
|
|
CMultilayerPerceptron network;
|
|
CMultilayerPerceptron network2;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
return;
|
|
//--- Prepare network
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- Initialize arrays
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
//--- Main cycle
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Last run is made on zero network
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
zeronet=false;
|
|
//--- check
|
|
if(pass==passcount)
|
|
{
|
|
for(i_=0; i_<wcount; i_++)
|
|
network.m_weights.Set(i_,0);
|
|
zeronet=true;
|
|
}
|
|
//--- Same inputs leads to same outputs
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPCopy
|
|
UnsetNetwork(network2);
|
|
CMLPBase::MLPCopy(network,network2);
|
|
//--- change values
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPSerialize
|
|
UnsetNetwork(network2);
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,network);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network2);
|
|
_local_serializer.Stop();
|
|
//--- change values
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Different inputs leads to different outputs (non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||allsame;
|
|
}
|
|
//--- Randomization changes outputs (when inputs are unchanged,non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomize(network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||allsame;
|
|
}
|
|
//--- Full randomization changes outputs (when inputs are unchanged,non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomizeFull(network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||allsame;
|
|
}
|
|
//--- Normalization properties
|
|
if(nkind==1)
|
|
{
|
|
//--- Classifier network outputs are normalized
|
|
for(i=0; i<nin ; i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
v=0;
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
v+=y1[i];
|
|
//--- search errors
|
|
err=err||y1[i]<0.0;
|
|
}
|
|
//--- search errors
|
|
err=err||MathAbs(v-1)>1000*CMath::m_machineepsilon;
|
|
}
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- B-type network outputs are bounded from above/below
|
|
for(i=0; i<nin ; i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
//--- check
|
|
if(a2>=0.0)
|
|
err=err||y1[i]<a1;
|
|
else
|
|
err=err||y1[i]>a1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
|
|
for(i=0; i<nin ; i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
//--- search errors
|
|
for(i=0; i<nout; i++)
|
|
err=(err||y1[i]<MathMin(a1,a2))||y1[i]>MathMax(a1,a2);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gradient functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPTrainUnit::TestGradient(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double h=0;
|
|
double etol=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ssize=0;
|
|
double v=0;
|
|
double e=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double v4=0;
|
|
double wprev=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double grad1[];
|
|
double grad2[];
|
|
double x[];
|
|
double y[];
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CMultilayerPerceptron network;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
return;
|
|
//--- change values
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- change values
|
|
h=0.0001;
|
|
etol=0.01;
|
|
//--- Initialize
|
|
ArrayResize(x,nin);
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y,nout);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
ArrayResize(grad1,wcount);
|
|
ArrayResize(grad2,wcount);
|
|
//--- Process
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
//--- Test error/gradient calculation (least squares)
|
|
xy.Resize(1,nin+nout);
|
|
for(i=0; i<nin ; i++)
|
|
x[i]=4*CMath::RandomReal()-2;
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(0,i_,x[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y[i]=0;
|
|
xy.Set(0,nin,CMath::RandomInteger(nout));
|
|
y[(int)MathRound(xy[0][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y[i]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(0,i_,y[i_+i1_]);
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPGrad(network,x,y,e,grad2);
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
for(i_=0; i_<nout; i_++)
|
|
y2[i_]=y2[i_]-y[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<nout; i_++)
|
|
v+=y2[i_]*y2[i_];
|
|
v=v/2;
|
|
//--- search errors
|
|
err=err||MathAbs((v-e)/v)>etol;
|
|
err=err||MathAbs((CMLPBase::MLPError(network,xy,1)-v)/v)>etol;
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
wprev=network.m_weights[i];
|
|
network.m_weights.Set(i,wprev-2*h);
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0; i_<nout; i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v1=0.0;
|
|
for(i_=0; i_<nout; i_++)
|
|
v1+=y1[i_]*y1[i_];
|
|
v1=v1/2;
|
|
network.m_weights.Set(i,wprev-h);
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0; i_<nout; i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v2=0.0;
|
|
for(i_=0; i_<nout; i_++)
|
|
v2+=y1[i_]*y1[i_];
|
|
v2=v2/2;
|
|
network.m_weights.Set(i,wprev+h);
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0; i_<nout; i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v3=0.0;
|
|
for(i_=0; i_<nout; i_++)
|
|
v3+=y1[i_]*y1[i_];
|
|
v3=v3/2;
|
|
network.m_weights.Set(i,wprev+2*h);
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0; i_<nout; i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v4=0.0;
|
|
for(i_=0; i_<nout; i_++)
|
|
v4+=y1[i_]*y1[i_];
|
|
v4=v4/2;
|
|
network.m_weights.Set(i,wprev);
|
|
grad1[i]=(v1-8*v2+8*v3-v4)/(12*h);
|
|
//--- check
|
|
if(MathAbs(grad1[i])>1.0E-3)
|
|
err=err||MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err||MathAbs(grad2[i]-grad1[i])>etol;
|
|
}
|
|
//--- Test error/gradient calculation (natural).
|
|
//--- Testing on non-random structure networks
|
|
//--- (because NKind is representative only in that case).
|
|
xy.Resize(1,nin+nout);
|
|
for(i=0; i<nin ; i++)
|
|
x[i]=4*CMath::RandomReal()-2;
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(0,i_,x[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y[i]=0;
|
|
xy.Set(0,nin,CMath::RandomInteger(nout));
|
|
y[(int)MathRound(xy[0][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y[i]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(0,i_,y[i_+i1_]);
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPGradN(network,x,y,e,grad2);
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
v=0;
|
|
//--- check
|
|
if(nkind!=1)
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
v+=0.5*CMath::Sqr(y2[i]-y[i]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
//--- check
|
|
if(y[i]!=0.0)
|
|
{
|
|
//--- check
|
|
if(y2[i]==0.0)
|
|
v+=y[i]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v+=y[i]*MathLog(y[i]/y2[i]);
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
err=err||MathAbs((v-e)/v)>etol;
|
|
err=err||MathAbs((CMLPBase::MLPErrorN(network,xy,1)-v)/v)>etol;
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
wprev=network.m_weights[i];
|
|
network.m_weights.Set(i,wprev+h);
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
network.m_weights.Set(i,wprev-h);
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
network.m_weights.Set(i,wprev);
|
|
v=0;
|
|
//--- check
|
|
if(nkind!=1)
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
v+=0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
//--- check
|
|
if(y[j]!=0.0)
|
|
{
|
|
//--- check
|
|
if(y2[j]==0.0)
|
|
v+=y[j]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v+=y[j]*MathLog(y[j]/y2[j]);
|
|
//--- check
|
|
if(y1[j]==0.0)
|
|
v=v-y[j]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v=v-y[j]*MathLog(y[j]/y1[j]);
|
|
}
|
|
}
|
|
v=v/(2*h);
|
|
}
|
|
grad1[i]=v;
|
|
//--- check
|
|
if(MathAbs(grad1[i])>1.0E-3)
|
|
err=err||MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err||MathAbs(grad2[i]-grad1[i])>etol;
|
|
}
|
|
//--- Test gradient calculation: batch (least squares)
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy.Resize(ssize,nin+nout);
|
|
for(i=0; i<wcount; i++)
|
|
grad1[i]=0;
|
|
//--- calculation
|
|
e1=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
x1[j]=4*CMath::RandomReal()-2;
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1[j]=0;
|
|
//--- change values
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
y1[(int)MathRound(xy[i][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=0; j<nout; j++)
|
|
y1[j]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
//--- change values
|
|
e1+=v;
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad1[i_]+=grad2[i_];
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGradBatch(network,xy,ssize,e2,grad2);
|
|
//--- search errors
|
|
err=err||MathAbs(e1-e2)/e1>0.01;
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
//--- check
|
|
if(grad1[i]!=0.0)
|
|
err=err||MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err||grad2[i]!=grad1[i];
|
|
}
|
|
//--- Test gradient calculation: batch (natural error func)
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy.Resize(ssize,nin+nout);
|
|
for(i=0; i<wcount; i++)
|
|
grad1[i]=0;
|
|
//--- calculation
|
|
e1=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
x1[j]=4*CMath::RandomReal()-2;
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1[j]=0;
|
|
//--- change values
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
y1[(int)MathRound(xy[i][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=0; j<nout; j++)
|
|
y1[j]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
//--- change values
|
|
e1+=v;
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad1[i_]+=grad2[i_];
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGradNBatch(network,xy,ssize,e2,grad2);
|
|
//--- search errors
|
|
err=err||MathAbs(e1-e2)/e1>etol;
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
//--- check
|
|
if(grad1[i]!=0.0)
|
|
err=err||MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err||grad2[i]!=grad1[i];
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Hessian functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPTrainUnit::TestHessian(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int hkind=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double h=0;
|
|
double etol=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ssize=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
double v=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double wprev=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double grad1[];
|
|
double grad2[];
|
|
double grad3[];
|
|
double x[];
|
|
double y[];
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble h1;
|
|
CMatrixDouble h2;
|
|
//--- object of class
|
|
CMultilayerPerceptron network;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
return;
|
|
//--- change values
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- change values
|
|
h=0.0001;
|
|
etol=0.05;
|
|
//--- Initialize
|
|
ArrayResize(x,nin);
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y,nout);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
ArrayResize(grad1,wcount);
|
|
ArrayResize(grad2,wcount);
|
|
ArrayResize(grad3,wcount);
|
|
h1.Resize(wcount,wcount);
|
|
h2.Resize(wcount,wcount);
|
|
//--- Process
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
//--- Test hessian calculation .
|
|
//--- E1 contains total error (calculated using MLPGrad/MLPGradN)
|
|
//--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
|
|
//--- H1 contains Hessian calculated using differences of gradients
|
|
//--- E2,Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
|
|
for(hkind=0; hkind<=1; hkind++)
|
|
{
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy.Resize(ssize,nin+nout);
|
|
for(i=0; i<wcount; i++)
|
|
grad1[i]=0;
|
|
//--- change values
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
for(j=0; j<wcount; j++)
|
|
h1.Set(i,j,0);
|
|
}
|
|
//--- calculation
|
|
e1=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
//--- X,Y
|
|
for(j=0; j<nin ; j++)
|
|
x1[j]=4*CMath::RandomReal()-2;
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1[j]=0;
|
|
//--- change values
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
y1[(int)MathRound(xy[i][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=0; j<nout; j++)
|
|
y1[j]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
//--- E1,Grad1
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
//--- change values
|
|
e1+=v;
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad1[i_]+=grad2[i_];
|
|
//--- H1
|
|
for(j=0; j<wcount; j++)
|
|
{
|
|
wprev=network.m_weights[j];
|
|
network.m_weights.Set(j,wprev-2*h);
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
network.m_weights.Set(j,wprev-h);
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
//--- change values
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad2[i_]-=8*grad3[i_];
|
|
network.m_weights.Set(j,wprev+h);
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
//--- change values
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad2[i_]+=8*grad3[i_];
|
|
network.m_weights.Set(j,wprev+2*h);
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
//--- change values
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad2[i_]-=grad3[i_];
|
|
v=1/(12*h);
|
|
for(i_=0; i_<wcount; i_++)
|
|
h1.Set(j,i_,h1.Get(j,i_)+v*grad2[i_]);
|
|
network.m_weights.Set(j,wprev);
|
|
}
|
|
}
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPHessianBatch(network,xy,ssize,e2,grad2,h2);
|
|
else
|
|
CMLPBase::MLPHessianNBatch(network,xy,ssize,e2,grad2,h2);
|
|
//--- search errors
|
|
err=err||MathAbs(e1-e2)/e1>etol;
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(grad1[i])>1.0E-2)
|
|
err=err||MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err||MathAbs(grad2[i]-grad1[i])>etol;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
for(j=0; j<wcount; j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(h1.Get(i,j))>5.0E-2)
|
|
err=err||MathAbs((h1.Get(i,j)-h2.Get(i,j))/h1.Get(i,j))>etol;
|
|
else
|
|
err=err||MathAbs(h2.Get(i,j)-h1.Get(i,j))>etol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPE |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMLPEUnit
|
|
{
|
|
public:
|
|
static bool TestMLPE(const bool silent);
|
|
|
|
private:
|
|
static bool TestMLPTrainES(void);
|
|
static bool TestMLPTrainRegr(void);
|
|
static bool TestMLPXORRegr(void);
|
|
static bool TestMLPTrainClass(void);
|
|
static bool TestMLPXORCls(void);
|
|
static bool TestMLPZeroWeights(void);
|
|
static bool TestMLPRestarts(void);
|
|
static bool TestMLPTrainEns(void);
|
|
static bool TestMLPTrainEnsRegr(void);
|
|
static bool TestMLPTrainEnsCls(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPE |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPE(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors=false;
|
|
int info=0;
|
|
CMultilayerPerceptronShell network;
|
|
CMultilayerPerceptronShell network2;
|
|
CMLPReportShell rep;
|
|
CMLPCVReportShell cvrep;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble valxy;
|
|
bool trnerrors=false;
|
|
bool mlptrainregrerr=false;
|
|
bool mlptrainclasserr=false;
|
|
bool mlprestartserr=false;
|
|
bool mlpxorregrerr=false;
|
|
bool mlpxorclserr=false;
|
|
bool mlptrainenserrors=false;
|
|
//--- Test network training on simple XOR problem
|
|
xy.Resize(4,3);
|
|
xy.Set(0,0,-1);
|
|
xy.Set(0,1,-1);
|
|
xy.Set(0,2,-1);
|
|
xy.Set(1,0,1);
|
|
xy.Set(1,1,-1);
|
|
xy.Set(1,2,1);
|
|
xy.Set(2,0,-1);
|
|
xy.Set(2,1,1);
|
|
xy.Set(2,2,1);
|
|
xy.Set(3,0,1);
|
|
xy.Set(3,1,1);
|
|
xy.Set(3,2,-1);
|
|
CMLPBase::MLPCreate1(2,2,1,network.GetInnerObj());
|
|
CMLPTrain::MLPTrainLM(network.GetInnerObj(),xy,4,0.0001,20,info,rep.GetInnerObj());
|
|
trnerrors=trnerrors||CMLPBase::MLPRMSError(network.GetInnerObj(),xy,4)>0.1;
|
|
//--- Test early stopping
|
|
trnerrors=trnerrors||TestMLPTrainES();
|
|
//--- Test for training functions
|
|
mlptrainregrerr=TestMLPTrainRegr()||TestMLPZeroWeights();
|
|
mlptrainclasserr=TestMLPTrainClass();
|
|
mlprestartserr=TestMLPRestarts();
|
|
mlpxorregrerr=TestMLPXORRegr();
|
|
mlpxorclserr=TestMLPXORCls();
|
|
//--- Training for ensembles
|
|
mlptrainenserrors=(TestMLPTrainEns()||TestMLPTrainEnsRegr()||TestMLPTrainEnsCls());
|
|
//--- Final report
|
|
waserrors=(trnerrors||mlptrainregrerr||mlptrainclasserr||mlprestartserr||mlpxorregrerr||mlpxorclserr||mlptrainenserrors);
|
|
if(!silent)
|
|
{
|
|
Print("MLPE TEST");
|
|
PrintResult("TRAINING",!trnerrors);
|
|
PrintResult("TRAIN -LM -LBFGS FOR REGRESSION",!mlptrainregrerr);
|
|
PrintResult("TRAIN -LM -LBFGS FOR CLASSIFIER",!mlptrainclasserr);
|
|
PrintResult("PARAMETER RESTARTS IN TRAIN -LBFGS",!mlprestartserr);
|
|
PrintResult("TRAINIG WITH TRAINER FOR REGRESSION",!mlpxorregrerr);
|
|
PrintResult("TRAINIG WITH TRAINER FOR CLASSIFIER",!mlpxorclserr);
|
|
PrintResult("TRAINING ENSEMBLES",!mlptrainenserrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests MLPTrainES(). |
|
|
//| It returns True in case of errors, False when no errors were |
|
|
//| detected |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPTrainES(void)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
CMultilayerPerceptron network;
|
|
CMatrixDouble trnxy;
|
|
CMatrixDouble valxy;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int nrestarts=0;
|
|
int info=0;
|
|
CMLPReportShell rep;
|
|
//--- First test checks that MLPTrainES() - when training set is equal to the validation
|
|
//--- set, MLPTrainES() behaves just like a "normal" training algorithm.
|
|
//--- Test sequence:
|
|
//--- * generate training set - 100 random points from 2D square [-1,+1]*[-1,+1]
|
|
//--- * generate network with 2 inputs, no hidden layers, nonlinear output layer,
|
|
//--- use its outputs as target values for the test set
|
|
//--- * randomize network
|
|
//--- * train with MLPTrainES(), using original set as both training and validation set
|
|
//--- * trained network must reproduce training set with good precision
|
|
//--- NOTE: it is important to test algorithm on nonlinear network because linear
|
|
//--- problems converge too fast. Slow convergence is important to detect
|
|
//--- some kinds of bugs.
|
|
//--- NOTE: it is important to have NRestarts at least equal to 5, because with just
|
|
//--- one restart algorithm fails test about once in several thousands of passes.
|
|
passcount=10;
|
|
nrestarts=5;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Create network, generate training/validation sets
|
|
CMLPBase::MLPCreateR0(2,1,-2.0,2.0,network);
|
|
CMLPBase::MLPRandomize(network);
|
|
n=100;
|
|
trnxy.Resize(n,3);
|
|
valxy.Resize(n,3);
|
|
x.Resize(2);
|
|
y.Resize(1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=1; j++)
|
|
{
|
|
trnxy.Set(i,j,2*CMath::RandomReal()-1);
|
|
valxy.Set(i,j,trnxy.Get(i,j));
|
|
x.Set(j,trnxy.Get(i,j));
|
|
}
|
|
CMLPBase::MLPProcess(network,x,y);
|
|
trnxy.Set(i,2,y[0]);
|
|
valxy.Set(i,2,y[0]);
|
|
}
|
|
CMLPBase::MLPRandomize(network);
|
|
CMLPTrain::MLPTrainES(network,trnxy,n,valxy,n,0.0001,nrestarts,info,rep.GetInnerObj());
|
|
if(info<=0)
|
|
return(true);
|
|
if(MathSqrt(CMLPBase::MLPError(network,valxy,n)/n)>0.01)
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests MLPTrainLM, MLPTrainLBFGS and |
|
|
//| MLPTrainNetwork functions for regression. It check that train |
|
|
//| functions work correctly. |
|
|
//| Test use Create1 with 10 neurons. |
|
|
//| Test function is f(x,y)=X^2+cos(3*Pi*y). |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPTrainRegr(void)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptronShell net;
|
|
CMLPTrainer trainer;
|
|
CMLPReport rep;
|
|
int info=0;
|
|
CMatrixDouble xy;
|
|
CSparseMatrix sm;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int n=0;
|
|
int sn=0;
|
|
int nneurons=0;
|
|
double vdecay=0;
|
|
double averr=0;
|
|
double st=0;
|
|
double eps=0;
|
|
double traineps=0;
|
|
int nneedrest=0;
|
|
int trainits=0;
|
|
int shift=0;
|
|
int i=0;
|
|
int j=0;
|
|
int vtrain=0;
|
|
//--- initialization
|
|
eps=0.02;
|
|
vdecay=0.001;
|
|
nneurons=10;
|
|
nneedrest=5;
|
|
traineps=1.0E-3;
|
|
trainits=0;
|
|
sn=5;
|
|
n=sn*sn;
|
|
st=(double)2/(double)(sn-1);
|
|
CSparse::SparseCreate(n,3,n*3,sm);
|
|
xy.Resize(n,3);
|
|
x.Resize(2);
|
|
for(vtrain=0; vtrain<=3; vtrain++)
|
|
{
|
|
averr=0;
|
|
//--- Create a train set(uniformly distributed set of points).
|
|
for(i=0; i<sn; i++)
|
|
{
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
shift=i*sn+j;
|
|
xy.Set(shift,0,i*st-1.0);
|
|
xy.Set(shift,1,j*st-1.0);
|
|
xy.Set(shift,2,CMath::Sqr(xy.Get(shift,0))+MathCos(3*M_PI*xy.Get(shift,1)));
|
|
}
|
|
}
|
|
//--- Create and train a neural network
|
|
CMLPBase::MLPCreate1(2,nneurons,1,net.GetInnerObj());
|
|
switch(vtrain)
|
|
{
|
|
case 0:
|
|
CMLPTrain::MLPTrainLM(net.GetInnerObj(),xy,n,vdecay,nneedrest,info,rep);
|
|
break;
|
|
case 1:
|
|
CMLPTrain::MLPTrainLBFGS(net.GetInnerObj(),xy,n,vdecay,nneedrest,traineps,trainits,info,rep);
|
|
break;
|
|
//--- Train with trainer, using:
|
|
//--- * dense matrix;
|
|
case 2:
|
|
CMLPTrain::MLPCreateTrainer(2,1,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep);
|
|
break;
|
|
//--- * sparse matrix.
|
|
case 3:
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<=2; j++)
|
|
CSparse::SparseSet(sm,i,j,xy.Get(i,j));
|
|
CMLPTrain::MLPCreateTrainer(2,1,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep);
|
|
break;
|
|
}
|
|
//--- Check that network is trained correctly
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x=xy[i]+0;
|
|
CMLPBase::MLPProcess(net.GetInnerObj(),x,y);
|
|
//--- Calculate average error
|
|
averr=averr+MathAbs(y[0]-xy.Get(i,2));
|
|
}
|
|
if((averr/n)>eps)
|
|
return(true);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests MLPTrainNetwork / MLPStartTraining / |
|
|
//| MLPContinueTraining functions for regression. It check that train|
|
|
//| functions work correctly. |
|
|
//| Test use Create1 with 2 neurons. |
|
|
//| Test function is XOR(x,y). |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPXORRegr(void)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptronShell net;
|
|
CMLPTrainer trainer;
|
|
CMLPReportShell rep;
|
|
CMatrixDouble xy;
|
|
CSparseMatrix sm;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int n=0;
|
|
int sn=0;
|
|
int nneurons=0;
|
|
double vdecay=0;
|
|
double averr=0;
|
|
double eps=0;
|
|
int numxp=0;
|
|
double traineps=0;
|
|
int nneedrest=0;
|
|
int trainits=0;
|
|
int shift=0;
|
|
int i=0;
|
|
int j=0;
|
|
int vtrain=0;
|
|
int xp=0;
|
|
int nfailures=0;
|
|
|
|
eps=0.01;
|
|
numxp=15;
|
|
vdecay=0.0001;
|
|
nneurons=4;
|
|
nneedrest=1;
|
|
traineps=1.0E-4;
|
|
trainits=0;
|
|
sn=2;
|
|
n=sn*sn;
|
|
CSparse::SparseCreate(n,3,n*3,sm);
|
|
xy.Resize(n,3);
|
|
x.Resize(2);
|
|
nfailures=0;
|
|
for(xp=1; xp<=numxp; xp++)
|
|
{
|
|
for(vtrain=0; vtrain<=3; vtrain++)
|
|
{
|
|
//--- Create a train set(uniformly distributed set of points).
|
|
for(i=0; i<sn; i++)
|
|
{
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
shift=i*sn+j;
|
|
xy.Set(shift,0,i);
|
|
xy.Set(shift,1,j);
|
|
if(xy.Get(shift,0)==xy.Get(shift,1))
|
|
xy.Set(shift,2,0);
|
|
else
|
|
xy.Set(shift,2,1);
|
|
}
|
|
}
|
|
//--- Create and train a neural network
|
|
CMLPBase::MLPCreate1(2,nneurons,1,net.GetInnerObj());
|
|
//--- Train with trainer, using:
|
|
//--- * dense matrix;
|
|
switch(vtrain)
|
|
{
|
|
case 0:
|
|
CMLPTrain::MLPCreateTrainer(2,1,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep.GetInnerObj());
|
|
break;
|
|
case 1:
|
|
CMLPTrain::MLPCreateTrainer(2,1,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPStartTraining(trainer,net.GetInnerObj(),true);
|
|
while(CMLPTrain::MLPContinueTraining(trainer,net.GetInnerObj()))
|
|
{
|
|
}
|
|
break;
|
|
//--- * sparse matrix.
|
|
case 2:
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<=2; j++)
|
|
CSparse::SparseSet(sm,i,j,xy.Get(i,j));
|
|
CMLPTrain::MLPCreateTrainer(2,1,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep.GetInnerObj());
|
|
break;
|
|
case 3:
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<=2; j++)
|
|
CSparse::SparseSet(sm,i,j,xy.Get(i,j));
|
|
CMLPTrain::MLPCreateTrainer(2,1,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPStartTraining(trainer,net.GetInnerObj(),true);
|
|
while(CMLPTrain::MLPContinueTraining(trainer,net.GetInnerObj()))
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
//--- Check that network is trained correctly
|
|
averr=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x=xy[i]+0;
|
|
CMLPBase::MLPProcess(net.GetInnerObj(),x,y);
|
|
//--- Calculate average error
|
|
averr=averr+MathAbs(y[0]-xy.Get(i,2));
|
|
}
|
|
if((averr/n)>eps)
|
|
nfailures++;
|
|
}
|
|
}
|
|
if(nfailures>=2)
|
|
{
|
|
Print("testmlptrainunit.ap:580");
|
|
return(true);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests MLPTrainLM, MLPTrainLBFGS and |
|
|
//| MLPTrainNetwork functions for classification problems. It check |
|
|
//| that train functions work correctly when is used CreateC1 |
|
|
//| function. Here the network tries to distinguish positive from|
|
|
//| negative numbers. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPTrainClass(void)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron net;
|
|
CMLPTrainer trainer;
|
|
CMLPReportShell rep;
|
|
int info=0;
|
|
CMatrixDouble xy;
|
|
CSparseMatrix sm;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int n=0;
|
|
double vdecay=0;
|
|
double traineps=0;
|
|
int nneedrest=0;
|
|
int trainits=0;
|
|
double tmp=0;
|
|
double mnc=0;
|
|
double mxc=0;
|
|
int nxp=0;
|
|
int i=0;
|
|
int rndind=0;
|
|
int vtrain=0;
|
|
int xp=0;
|
|
//--- initialization
|
|
mnc=10;
|
|
mxc=11;
|
|
nxp=15;
|
|
vdecay=0.0001;
|
|
nneedrest=10;
|
|
traineps=1.0E-4;
|
|
trainits=0;
|
|
n=100;
|
|
CSparse::SparseCreate(n,2,n*2,sm);
|
|
x.Resize(1);
|
|
xy.Resize(n,2);
|
|
for(xp=1; xp<=nxp; xp++)
|
|
{
|
|
for(vtrain=0; vtrain<=3; vtrain++)
|
|
{
|
|
//--- Initialization:
|
|
//--- * create negative part of the set;
|
|
for(i=0; i<=n/2-1; i++)
|
|
{
|
|
xy.Set(i,0,-(1*((mxc-mnc)*CMath::RandomReal()+mnc)));
|
|
xy.Set(i,1,0);
|
|
}
|
|
//--- * create positive part of the set;
|
|
for(i=n/2; i<n; i++)
|
|
{
|
|
xy.Set(i,0,(mxc-mnc)*CMath::RandomReal()+mnc);
|
|
xy.Set(i,1,1);
|
|
}
|
|
//--- * mix two parts.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
rndind=CMath::RandomInteger(n);
|
|
}
|
|
while(rndind==i);
|
|
tmp=xy.Get(i,0);
|
|
xy.Set(i,0,xy.Get(rndind,0));
|
|
xy.Set(rndind,0,tmp);
|
|
tmp=xy.Get(i,1);
|
|
xy.Set(i,1,xy.Get(rndind,1));
|
|
xy.Set(rndind,1,tmp);
|
|
}
|
|
//--- Create and train a neural network
|
|
CMLPBase::MLPCreateC0(1,2,net);
|
|
switch(vtrain)
|
|
{
|
|
case 0:
|
|
CMLPTrain::MLPTrainLM(net,xy,n,vdecay,nneedrest,info,rep.GetInnerObj());
|
|
break;
|
|
case 1:
|
|
CMLPTrain::MLPTrainLBFGS(net,xy,n,vdecay,nneedrest,traineps,trainits,info,rep.GetInnerObj());
|
|
break;
|
|
//--- Train with trainer, using:
|
|
//--- * dense matrix;
|
|
case 2:
|
|
CMLPTrain::MLPCreateTrainerCls(1,2,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
|
|
break;
|
|
//--- * sparse matrix.
|
|
case 3:
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
|
|
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
|
|
}
|
|
CMLPTrain::MLPCreateTrainerCls(1,2,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
|
|
break;
|
|
}
|
|
//--- Test on training set
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(0,xy.Get(i,0));
|
|
CMLPBase::MLPProcess(net,x,y);
|
|
//--- Negative number has to be negative and
|
|
//--- positive number has to be positive.
|
|
if((x[0]<0.0 && y[0]<0.95 && y[1]>0.05) || (x[0]>=0.0 && y[0]>0.05 && y[1]<0.95))
|
|
{
|
|
Print("testmlptrainunit.ap:730");
|
|
return(true);
|
|
}
|
|
}
|
|
//--- Test on random set
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(0,MathPow(-1,CMath::RandomInteger(2))*((mxc-mnc)*CMath::RandomReal()+mnc));
|
|
CMLPBase::MLPProcess(net,x,y);
|
|
if((x[0]<0.0 && y[0]<0.95 && y[1]>0.05) || (x[0]>=0.0 && y[0]>0.05 && y[1]<0.95))
|
|
{
|
|
Print("testmlptrainunit.ap:745");
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests MLPTrainNetwork / MLPStartTraining / |
|
|
//| MLPContinueTraining functions for classification problems. It |
|
|
//| check that train functions work correctly when is used CreateC1 |
|
|
//| function. Here the network tries to distinguish positive from |
|
|
//| negative numbers. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPXORCls(void)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron net;
|
|
CMLPTrainer trainer;
|
|
CMLPReportShell rep;
|
|
CMatrixDouble xy;
|
|
CSparseMatrix sm;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int n=0;
|
|
int nin=0;
|
|
int nout=0;
|
|
int wcount=0;
|
|
double e=0;
|
|
double ebest=0;
|
|
double v=0;
|
|
CRowDouble wbest;
|
|
double vdecay=0;
|
|
double traineps=0;
|
|
int nneurons=0;
|
|
int nneedrest=0;
|
|
int trainits=0;
|
|
int nxp=0;
|
|
int i=0;
|
|
int vtrain=0;
|
|
int xp=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
nxp=15;
|
|
nneurons=4;
|
|
vdecay=0.001;
|
|
nneedrest=3;
|
|
traineps=1.0E-4;
|
|
trainits=0;
|
|
n=4;
|
|
CSparse::SparseCreate(n,3,n*3,sm);
|
|
x.Resize(2);
|
|
xy.Resize(n,3);
|
|
//--- Initialization:
|
|
xy.Set(0,0,0);
|
|
xy.Set(0,1,0);
|
|
xy.Set(0,2,0);
|
|
xy.Set(1,0,0);
|
|
xy.Set(1,1,1);
|
|
xy.Set(1,2,1);
|
|
xy.Set(2,0,1);
|
|
xy.Set(2,1,0);
|
|
xy.Set(2,2,1);
|
|
xy.Set(3,0,1);
|
|
xy.Set(3,1,1);
|
|
xy.Set(3,2,0);
|
|
//--- Create a neural network
|
|
CMLPBase::MLPCreateC1(2,nneurons,2,net);
|
|
CMLPBase::MLPProperties(net,nin,nout,wcount);
|
|
wbest.Resize(wcount);
|
|
//--- Test
|
|
for(xp=1; xp<=nxp; xp++)
|
|
{
|
|
for(vtrain=0; vtrain<=3; vtrain++)
|
|
{
|
|
//--- Train with trainer, using:
|
|
//--- * dense matrix;
|
|
switch(vtrain)
|
|
{
|
|
case 0:
|
|
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
|
|
break;
|
|
case 1:
|
|
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
ebest=CMath::m_maxrealnumber;
|
|
for(i=1; i<=nneedrest; i++)
|
|
{
|
|
CMLPTrain::MLPStartTraining(trainer,net,true);
|
|
while(CMLPTrain::MLPContinueTraining(trainer,net))
|
|
{
|
|
}
|
|
v=CAblasF::RDotV2(wcount,net.m_weights);
|
|
e=CMLPBase::MLPError(net,xy,n)+0.5*vdecay*v;
|
|
//--- Compare with the best answer.
|
|
if(e<ebest)
|
|
{
|
|
wbest=net.m_weights;
|
|
ebest=e;
|
|
}
|
|
}
|
|
//--- The best result
|
|
net.m_weights=wbest;
|
|
break;
|
|
//--- * sparse matrix.
|
|
case 2:
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
|
|
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
|
|
CSparse::SparseSet(sm,i,2,xy.Get(i,2));
|
|
}
|
|
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
|
|
break;
|
|
case 3:
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
|
|
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
|
|
CSparse::SparseSet(sm,i,2,xy.Get(i,2));
|
|
}
|
|
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
ebest=CMath::m_maxrealnumber;
|
|
for(i=1; i<=nneedrest; i++)
|
|
{
|
|
CMLPTrain::MLPStartTraining(trainer,net,true);
|
|
while(CMLPTrain::MLPContinueTraining(trainer,net))
|
|
{
|
|
}
|
|
v=CAblasF::RDotV2(wcount,net.m_weights);
|
|
e=CMLPBase::MLPError(net,xy,n)+0.5*vdecay*v;
|
|
//--- Compare with the best answer.
|
|
if(e<ebest)
|
|
{
|
|
wbest=net.m_weights;
|
|
ebest=e;
|
|
}
|
|
}
|
|
//--- The best result
|
|
net.m_weights=wbest;
|
|
break;
|
|
}
|
|
//--- Test on training set
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x=xy[i]+0;
|
|
CMLPBase::MLPProcess(net,x,y);
|
|
if((x[0]==x[1] && y[0]<0.95 && y[1]>0.05) || (x[0]!=x[1] && y[0]>0.05 && y[1]<0.95))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test check, that all weights are zero after training with|
|
|
//| trainer using empty dataset(either zero size or is't used |
|
|
//| MLPSetDataSet function). Test on regression and classification |
|
|
//| problems given by dense or sparse matrix. |
|
|
//| NOTE: Result of the function is written in MLPTrainRegrErr |
|
|
//| variable in unit test. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPZeroWeights(void)
|
|
{
|
|
//--- create variables
|
|
CMLPTrainer trainer;
|
|
CMultilayerPerceptron net;
|
|
CMLPReportShell rep;
|
|
int nin=0;
|
|
int nout=0;
|
|
int wcount=0;
|
|
int mxnin=0;
|
|
int mxnout=0;
|
|
double vdecay=0;
|
|
double traineps=0;
|
|
int trainits=0;
|
|
int nneedrest=0;
|
|
CMatrixDouble dds;
|
|
CSparseMatrix sds;
|
|
bool iscls;
|
|
bool issparse;
|
|
int c=0;
|
|
int n=0;
|
|
int xp=0;
|
|
int nxp=0;
|
|
//--- initialization
|
|
mxnin=10;
|
|
mxnout=10;
|
|
vdecay=1.0E-3;
|
|
nneedrest=1;
|
|
traineps=1.0E-3;
|
|
trainits=0;
|
|
CSparse::SparseCreate(1,1,0,sds);
|
|
CSparse::SparseConvertToCRS(sds);
|
|
nxp=10;
|
|
for(xp=1; xp<=nxp; xp++)
|
|
{
|
|
c=CMath::RandomInteger(2);
|
|
iscls=c==1;
|
|
c=CMath::RandomInteger(2);
|
|
issparse=c==1;
|
|
//--- Create trainer and network
|
|
if(!iscls)
|
|
{
|
|
//--- Regression
|
|
nin=CMath::RandomInteger(mxnin)+1;
|
|
nout=CMath::RandomInteger(mxnout)+1;
|
|
CMLPTrain::MLPCreateTrainer(nin,nout,trainer);
|
|
CMLPBase::MLPCreate0(nin,nout,net);
|
|
}
|
|
else
|
|
{
|
|
//--- Classification
|
|
nin=CMath::RandomInteger(mxnin)+1;
|
|
nout=CMath::RandomInteger(mxnout)+2;
|
|
CMLPTrain::MLPCreateTrainerCls(nin,nout,trainer);
|
|
CMLPBase::MLPCreateC0(nin,nout,net);
|
|
}
|
|
n=CMath::RandomInteger(2)-1;
|
|
if(n==0)
|
|
{
|
|
if(!issparse)
|
|
CMLPTrain::MLPSetDataset(trainer,dds,n);
|
|
else
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sds,n);
|
|
}
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
c=CMath::RandomInteger(2);
|
|
if(c==0)
|
|
{
|
|
CMLPTrain::MLPStartTraining(trainer,net,true);
|
|
while(CMLPTrain::MLPContinueTraining(trainer,net))
|
|
{
|
|
}
|
|
}
|
|
if(c==1)
|
|
{
|
|
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
|
|
}
|
|
//--- Check weights
|
|
CMLPBase::MLPProperties(net,nin,nout,wcount);
|
|
for(c=0; c<wcount; c++)
|
|
{
|
|
if(net.m_weights[c]!=0.0)
|
|
return(true);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests that increasing numbers of restarts lead to |
|
|
//| statistical improvement quality of solution. |
|
|
//| Neural network created by Create1(10 neurons) and trained by |
|
|
//| MLPTrainLBFGS. |
|
|
//| TEST's DISCRIPTION: |
|
|
//| Net0 - network trained with one restart (denoted as R1) |
|
|
//| Net1 - network trained with more than one restart (denoted |
|
|
//| as Rn) |
|
|
//| We must refuse hypothesis that R1 equivalent to Rn. |
|
|
//| Here Mean = N/2, Sigma = Sqrt(N)/2. |
|
|
//| _ |
|
|
//| | 0 - R1 worse than Rn; |
|
|
//| ri = | |
|
|
//| |_1 - Rn same or worse then R1. |
|
|
//| If Sum(ri)<Mean-5*Sigma then hypothesis is refused and test is |
|
|
//| passed. In another case if Mean-5*Sigma<=Sum(ri)<=Mean+5*Sigma |
|
|
//| then hypothesis is't refused and test is broken; and if |
|
|
//| Mean + 5 * Sigma < Sum(ri) then test broken too hard! |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPRestarts(void)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron net0;
|
|
CMultilayerPerceptron net1;
|
|
CMLPTrainer trainer;
|
|
CMLPReportShell rep;
|
|
int info=0;
|
|
CSparseMatrix sm;
|
|
CMatrixDouble xy;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int n=0;
|
|
int nneurons=0;
|
|
double vdecay=0;
|
|
int wcount0=0;
|
|
int wcount1=0;
|
|
int nin=0;
|
|
int nout=0;
|
|
double avval=0;
|
|
double e0=0;
|
|
double e1=0;
|
|
double mean=0;
|
|
double numsigma=0;
|
|
int numxp=0;
|
|
double traineps=0;
|
|
int nneedrest=0;
|
|
int trainits=0;
|
|
int i=0;
|
|
int vtrain=0;
|
|
int xp=0;
|
|
int i_=0;
|
|
|
|
vdecay=0.001;
|
|
nneurons=4;
|
|
nneedrest=3;
|
|
traineps=0.00;
|
|
trainits=2;
|
|
n=20;
|
|
numxp=400;
|
|
xy.Resize(n,2);
|
|
x.Resize(1);
|
|
CSparse::SparseCreate(n,2,n*2,sm);
|
|
mean=numxp/2.0;
|
|
numsigma=5.0*MathSqrt(numxp)/2.0;
|
|
for(vtrain=0; vtrain<=2; vtrain++)
|
|
{
|
|
avval=0;
|
|
for(xp=1; xp<=numxp; xp++)
|
|
{
|
|
//--- Create a train set
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(i,0,2*CMath::RandomReal()-1);
|
|
xy.Set(i,1,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- Create and train a neural network
|
|
CMLPBase::MLPCreate1(1,nneurons,1,net0);
|
|
CMLPBase::MLPCreate1(1,nneurons,1,net1);
|
|
switch(vtrain)
|
|
{
|
|
case 0:
|
|
CMLPTrain::MLPTrainLBFGS(net0,xy,n,vdecay,1,traineps,trainits,info,rep.GetInnerObj());
|
|
CMLPTrain::MLPTrainLBFGS(net1,xy,n,vdecay,nneedrest,traineps,trainits,info,rep.GetInnerObj());
|
|
break;
|
|
case 1:
|
|
CMLPTrain::MLPCreateTrainer(1,1,trainer);
|
|
CMLPTrain::MLPSetDataset(trainer,xy,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net0,1,rep.GetInnerObj());
|
|
CMLPTrain::MLPTrainNetwork(trainer,net1,nneedrest,rep.GetInnerObj());
|
|
break;
|
|
case 2:
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
|
|
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
|
|
}
|
|
CMLPTrain::MLPCreateTrainer(1,1,trainer);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
|
|
CMLPTrain::MLPSetDecay(trainer,vdecay);
|
|
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
|
|
CMLPTrain::MLPTrainNetwork(trainer,net0,1,rep.GetInnerObj());
|
|
CMLPTrain::MLPTrainNetwork(trainer,net1,nneedrest,rep.GetInnerObj());
|
|
break;
|
|
}
|
|
//--- Calculate errors for...
|
|
//--- ...for Net0, trained with 1 restart.
|
|
CMLPBase::MLPProperties(net0,nin,nout,wcount0);
|
|
e0=CAblasF::RDotV2(wcount0,net0.m_weights);
|
|
e0=CMLPBase::MLPErrorN(net0,xy,n)+0.5*vdecay*e0;
|
|
//--- ...for Net1, trained with NNeedRest>1 restarts.
|
|
CMLPBase::MLPProperties(net1,nin,nout,wcount1);
|
|
e1=CAblasF::RDotV2(wcount1,net1.m_weights);
|
|
e1=CMLPBase::MLPErrorN(net1,xy,n)+0.5*vdecay*e1;
|
|
if(e0<=e1)
|
|
avval++;
|
|
}
|
|
if((double)(mean-numsigma)<(double)(avval))
|
|
return(true);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The function tests functions for training ensembles : |
|
|
//| MLPEBaggingLM, MLPEBaggingLBFGS. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPTrainEns(void)
|
|
{
|
|
//--- create variables
|
|
CMLPEnsembleShell ensemble;
|
|
CMLPReportShell rep;
|
|
CMLPCVReportShell oobrep;
|
|
int info=0;
|
|
CMatrixDouble xy;
|
|
int nin=0;
|
|
int nout=0;
|
|
int npoints=0;
|
|
CMatrixDouble hf;
|
|
int nhid=0;
|
|
int algtype=0;
|
|
int tasktype=0;
|
|
int pass=0;
|
|
double e=0;
|
|
int nless=0;
|
|
int nall=0;
|
|
int nclasses=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CHighQualityRandStateShell rs;
|
|
CHighQualityRand::HQRndRandomize(rs.GetInnerObj());
|
|
//--- network training must reduce error
|
|
//--- test on random regression task
|
|
nin=3;
|
|
nout=2;
|
|
nhid=5;
|
|
npoints=100;
|
|
nless=0;
|
|
nall=0;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(algtype=0; algtype<=1; algtype++)
|
|
{
|
|
for(tasktype=0; tasktype<=1; tasktype++)
|
|
{
|
|
if(tasktype==0)
|
|
{
|
|
hf.Resize(nout,nin+1);
|
|
for(i=0; i<nout; i++)
|
|
for(j=0; j<=nin; j++)
|
|
hf.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
//---
|
|
xy.Resize(npoints,nin+nout);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(j=0; j<nin; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
for(j=0; j<nout; j++)
|
|
xy.Set(i,nin+j,CHighQualityRand::HQRndNormal(rs.GetInnerObj())+CAblasF::RDotRR(nin,hf,j,xy,i));
|
|
}
|
|
CMLPE::MLPECreate1(nin,nhid,nout,1+CMath::RandomInteger(3),ensemble.GetInnerObj());
|
|
}
|
|
else
|
|
{
|
|
xy.Resize(npoints,nin+1);
|
|
nclasses=2+CMath::RandomInteger(2);
|
|
//--- check
|
|
if(!CAp::Assert(nclasses<=nin,__FUNCTION__+": bug in test"))
|
|
return(true);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(j=0; j<nin; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
k=0;
|
|
for(j=0; j<nclasses; j++)
|
|
{
|
|
if(xy.Get(i,j)>xy.Get(i,k))
|
|
k=j;
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
|
|
xy.Set(i,nin,k);
|
|
else
|
|
xy.Set(i,nin,CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),nclasses));
|
|
}
|
|
CMLPE::MLPECreateC1(nin,nhid,nclasses,1+CMath::RandomInteger(3),ensemble.GetInnerObj());
|
|
}
|
|
e=CMLPE::MLPERMSError(ensemble.GetInnerObj(),xy,npoints);
|
|
if(algtype==0)
|
|
CMLPTrain::MLPEBaggingLM(ensemble.GetInnerObj(),xy,npoints,0.001,1,info,rep.GetInnerObj(),oobrep.GetInnerObj());
|
|
else
|
|
CMLPTrain::MLPEBaggingLBFGS(ensemble.GetInnerObj(),xy,npoints,0.001,1,0.01,0,info,rep.GetInnerObj(),oobrep.GetInnerObj());
|
|
if(info<0)
|
|
return(true);
|
|
if(CMLPE::MLPERMSError(ensemble.GetInnerObj(),xy,npoints)<e)
|
|
nless++;
|
|
nall++;
|
|
}
|
|
}
|
|
}
|
|
if((double)(nall-nless)>(double)(0.3*nall))
|
|
{
|
|
Print("testmlptrainunit.ap:1636");
|
|
return(true);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing for functions MLPETrainES and MLPTrainEnsembleES on |
|
|
//| regression problems. Returns TRUE for errors, FALSE for success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPTrainEnsRegr(void)
|
|
{
|
|
//--- return result
|
|
CMLPTrainer trainer;
|
|
CMLPEnsembleShell netens;
|
|
CMLPReportShell rep;
|
|
CModelErrors repx;
|
|
int info=0;
|
|
CSparseMatrix xytrainsp;
|
|
CMatrixDouble xytrain;
|
|
CMatrixDouble xytest;
|
|
int nin=0;
|
|
int nout=0;
|
|
int nneurons=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
double decay=0;
|
|
double wstep=0;
|
|
int maxits=0;
|
|
int nneedrest=0;
|
|
int enssize=0;
|
|
double mnval=0;
|
|
double mxval=0;
|
|
int ntrain=0;
|
|
int ntest=0;
|
|
double avgerr=0;
|
|
int issparse=0;
|
|
int withtrainer=0;
|
|
double eps=0;
|
|
int xp=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
//--- This test checks ability to train ensemble on simple regression
|
|
//--- problem "f(x0,x1,x2,...) = x0 + x1 + x2 + ...".
|
|
eps=5.0E-2;
|
|
mnval=-1;
|
|
mxval=1;
|
|
ntrain=40;
|
|
ntest=20;
|
|
decay=1.0E-3;
|
|
wstep=1.0E-3;
|
|
maxits=20;
|
|
nneedrest=1;
|
|
nneurons=20;
|
|
nout=1;
|
|
enssize=100;
|
|
for(xp=1; xp<=2; xp++)
|
|
{
|
|
nin=CMath::RandomInteger(3)+1;
|
|
CApServ::RVectorSetLengthAtLeast(x,nin);
|
|
CMLPTrain::MLPCreateTrainer(nin,nout,trainer);
|
|
CMLPTrain::MLPSetDecay(trainer,decay);
|
|
CMLPTrain::MLPSetCond(trainer,wstep,maxits);
|
|
CApServ::RMatrixSetLengthAtLeast(xytrain,ntrain,nin+nout);
|
|
CApServ::RMatrixSetLengthAtLeast(xytest,ntest,nin+nout);
|
|
withtrainer=CMath::RandomInteger(2);
|
|
issparse=0;
|
|
if(withtrainer==0)
|
|
issparse=0;
|
|
if(withtrainer==1)
|
|
issparse=CMath::RandomInteger(2);
|
|
//--- Training set
|
|
for(i=0; i<ntrain; i++)
|
|
{
|
|
for(j=0; j<nin; j++)
|
|
xytrain.Set(i,j,(mxval-mnval)*CMath::RandomReal()+mnval);
|
|
xytrain.Set(i,nin,0);
|
|
for(j=0; j<nin; j++)
|
|
xytrain.Add(i,nin,xytrain.Get(i,j));
|
|
}
|
|
if(withtrainer==1)
|
|
{
|
|
//--- Dense matrix
|
|
if(issparse==0)
|
|
CMLPTrain::MLPSetDataset(trainer,xytrain,ntrain);
|
|
//--- Sparse matrix
|
|
if(issparse==1)
|
|
{
|
|
CSparse::SparseCreate(ntrain,nin+nout,ntrain*(nin+nout),xytrainsp);
|
|
//--- Just copy dense matrix to sparse matrix(using SparseGet() is too expensive).
|
|
for(i=0; i<ntrain; i++)
|
|
{
|
|
for(j=0; j<nin+nout; j++)
|
|
CSparse::SparseSet(xytrainsp,i,j,xytrain.Get(i,j));
|
|
}
|
|
CSparse::SparseConvertToCRS(xytrainsp);
|
|
CMLPTrain::MLPSetSparseDataset(trainer,xytrainsp,ntrain);
|
|
}
|
|
}
|
|
//--- Test set
|
|
for(i=0; i<ntest; i++)
|
|
{
|
|
for(j=0; j<nin; j++)
|
|
xytest.Set(i,j,(mxval-mnval)*CMath::RandomReal()+mnval);
|
|
xytest.Set(i,nin,0);
|
|
for(j=0; j<nin; j++)
|
|
xytest.Add(i,nin,xytest.Get(i,j));
|
|
}
|
|
//--- Create ensemble
|
|
CMLPE::MLPECreate1(nin,nneurons,nout,enssize,netens.GetInnerObj());
|
|
//--- Train ensembles:
|
|
//--- * without trainer;
|
|
if(withtrainer==0)
|
|
CMLPTrain::MLPETrainES(netens.GetInnerObj(),xytrain,ntrain,decay,nneedrest,info,rep.GetInnerObj());
|
|
//--- * with trainer.
|
|
if(withtrainer==1)
|
|
CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj());
|
|
//--- Test that Rep contains correct error values
|
|
CMLPE::MLPEAllErrorsX(netens.GetInnerObj(),xytrain,xytrainsp,ntrain,0,netens.GetInnerObj().m_network.m_dummyidx,0,ntrain,0,repx);
|
|
if(CheckErrorDiff(rep.GetInnerObj().m_RelCLSError,repx.m_RelCLSError,1.0E-4,1.0E-2) ||
|
|
CheckErrorDiff(rep.GetInnerObj().m_AvgCE,repx.m_AvgCE,1.0E-4,1.0E-2) ||
|
|
CheckErrorDiff(rep.GetInnerObj().m_RMSError,repx.m_RMSError,1.0E-4,1.0E-2) ||
|
|
CheckErrorDiff(rep.GetInnerObj().m_AvgError,repx.m_AvgError,1.0E-4,1.0E-2) ||
|
|
CheckErrorDiff(rep.GetInnerObj().m_AvgRelError,repx.m_AvgRelError,1.0E-4,1.0E-2))
|
|
{
|
|
PrintFormat("%s: diff error",__FUNCTION__);
|
|
return(true);
|
|
}
|
|
//--- Test that network fits data well. Calculate average error:
|
|
//--- * on training dataset;
|
|
//--- * on test dataset. (here we reduce the accuracy
|
|
//--- requirements - average error is compared with 2*Eps).
|
|
avgerr=0;
|
|
for(i=0; i<ntrain; i++)
|
|
{
|
|
if(issparse==0)
|
|
{
|
|
for(i_=0; i_<nin; i_++)
|
|
x.Set(i_,xytrain.Get(i,i_));
|
|
}
|
|
if(issparse==1)
|
|
CSparse::SparseGetRow(xytrainsp,i,x);
|
|
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
|
|
avgerr+=MathAbs(y[0]-xytrain.Get(i,nin));
|
|
}
|
|
avgerr=avgerr/ntrain;
|
|
if((double)(avgerr)>eps)
|
|
{
|
|
Print("testmlptrainunit.ap:1817");
|
|
return(true);
|
|
}
|
|
avgerr=0;
|
|
for(i=0; i<ntest; i++)
|
|
{
|
|
for(i_=0; i_<nin; i_++)
|
|
x.Set(i_,xytest.Get(i,i_));
|
|
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
|
|
avgerr+=MathAbs(y[0]-xytest.Get(i,nin));
|
|
}
|
|
avgerr=avgerr/ntest;
|
|
if((double)(avgerr)>(double)(2*eps))
|
|
{
|
|
Print("testmlptrainunit.ap:1826");
|
|
return(true);
|
|
}
|
|
}
|
|
//--- Catch bug in implementation of MLPTrainEnsembleX:
|
|
//--- test ensemble training on empty dataset.
|
|
//--- Unfixed version should crash with violation of array
|
|
//--- bounds (at least in C#).
|
|
nin=2;
|
|
nout=2;
|
|
nneurons=3;
|
|
enssize=3;
|
|
nneedrest=2;
|
|
wstep=0.001;
|
|
maxits=2;
|
|
CMLPTrain::MLPCreateTrainer(nin,nout,trainer);
|
|
CMLPTrain::MLPSetCond(trainer,wstep,maxits);
|
|
CMLPE::MLPECreate1(nin,nneurons,nout,enssize,netens.GetInnerObj());
|
|
CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj());
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing for functions MLPETrainES and MLPTrainEnsembleES on |
|
|
//| classification problems. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPEUnit::TestMLPTrainEnsCls(void)
|
|
{
|
|
//--- create variables
|
|
CMLPTrainer trainer;
|
|
CMLPEnsembleShell netens;
|
|
CMLPReportShell rep;
|
|
int info=0;
|
|
CSparseMatrix xytrainsp;
|
|
CMatrixDouble xytrain;
|
|
CMatrixDouble xytest;
|
|
int nin=0;
|
|
int nout=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
double decay=0;
|
|
double wstep=0;
|
|
int maxits=0;
|
|
int nneedrest=0;
|
|
int enssize=0;
|
|
int val=0;
|
|
int ntrain=0;
|
|
int ntest=0;
|
|
double avgerr=0;
|
|
double eps=0;
|
|
double delta=0;
|
|
int issparse=0;
|
|
int withtrainer=0;
|
|
int xp=0;
|
|
int nxp=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
eps=5.0E-2;
|
|
delta=0.1;
|
|
ntrain=90;
|
|
ntest=90;
|
|
nin=3;
|
|
nout=3;
|
|
CApServ::RVectorSetLengthAtLeast(x,nin);
|
|
CApServ::RMatrixSetLengthAtLeast(xytrain,ntrain,nin+1);
|
|
CApServ::RMatrixSetLengthAtLeast(xytest,ntest,nin+1);
|
|
decay=1.0E-3;
|
|
wstep=1.0E-3;
|
|
maxits=100;
|
|
nneedrest=1;
|
|
CMLPTrain::MLPCreateTrainerCls(nin,nout,trainer);
|
|
CMLPTrain::MLPSetDecay(trainer,decay);
|
|
CMLPTrain::MLPSetCond(trainer,wstep,maxits);
|
|
nxp=5;
|
|
for(xp=1; xp<=nxp; xp++)
|
|
{
|
|
enssize=(int)MathRound(MathPow(10,CMath::RandomInteger(2)+1));
|
|
withtrainer=CMath::RandomInteger(2);
|
|
issparse=0;
|
|
if(withtrainer==0)
|
|
issparse=0;
|
|
if(withtrainer==1)
|
|
issparse=CMath::RandomInteger(2);
|
|
for(i=0; i<ntrain; i++)
|
|
{
|
|
val=i%nin;
|
|
for(j=0; j<nin; j++)
|
|
xytrain.Set(i,j,delta*(CMath::RandomReal()-1));
|
|
xytrain.Add(i,val,1);
|
|
xytrain.Set(i,nin,val);
|
|
}
|
|
//--- Set dense dataset in trainer
|
|
if(issparse==0)
|
|
CMLPTrain::MLPSetDataset(trainer,xytrain,ntrain);
|
|
//--- * Sparse dataset(create it with using dense dataset).
|
|
if(issparse==1)
|
|
{
|
|
CSparse::SparseCreate(ntrain,nin+1,ntrain*(nin+1),xytrainsp);
|
|
for(i=0; i<ntrain; i++)
|
|
{
|
|
for(j=0; j<nin; j++)
|
|
CSparse::SparseSet(xytrainsp,i,j,xytrain.Get(i,j));
|
|
CSparse::SparseSet(xytrainsp,i,nin,xytrain.Get(i,nin));
|
|
}
|
|
CSparse::SparseConvertToCRS(xytrainsp);
|
|
//--- Set sparse dataset in trainer
|
|
CMLPTrain::MLPSetSparseDataset(trainer,xytrainsp,ntrain);
|
|
}
|
|
//--- Create test set
|
|
for(i=0; i<ntest; i++)
|
|
{
|
|
val=CMath::RandomInteger(nin);
|
|
for(j=0; j<nin; j++)
|
|
xytest.Set(i,j,delta*(CMath::RandomReal()-1));
|
|
xytest.Add(i,val,1);
|
|
xytest.Set(i,nin,val);
|
|
}
|
|
//--- Create ensemble
|
|
CMLPE::MLPECreateC0(nin,nout,enssize,netens.GetInnerObj());
|
|
//--- Train ensembles:
|
|
//--- * without trainer;
|
|
if(withtrainer==0)
|
|
CMLPTrain::MLPETrainES(netens.GetInnerObj(),xytrain,ntrain,decay,nneedrest,info,rep.GetInnerObj());
|
|
//--- * with trainer.
|
|
if(withtrainer==1)
|
|
CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj());
|
|
//--- Calculate average error:
|
|
//--- * on training dataset;
|
|
avgerr=0;
|
|
for(i=0; i<ntrain; i++)
|
|
{
|
|
if(issparse==0)
|
|
{
|
|
for(i_=0; i_<nin; i_++)
|
|
x.Set(i_,xytrain.Get(i,i_));
|
|
}
|
|
if(issparse==1)
|
|
CSparse::SparseGetRow(xytrainsp,i,x);
|
|
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
if((double)(j)!=xytrain.Get(i,nin))
|
|
avgerr+=y[j];
|
|
else
|
|
avgerr+=(1-y[j]);
|
|
}
|
|
}
|
|
avgerr=avgerr/(ntrain*nout);
|
|
if((double)(avgerr)>eps)
|
|
return(true);
|
|
//--- * on test dataset.
|
|
avgerr=0;
|
|
for(i=0; i<ntest; i++)
|
|
{
|
|
for(i_=0; i_<nin; i_++)
|
|
x.Set(i_,xytest.Get(i,i_));
|
|
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
if((double)(j)!=(double)(xytest.Get(i,nin)))
|
|
avgerr+=y[j];
|
|
else
|
|
avgerr+=(1-y[j]);
|
|
}
|
|
}
|
|
avgerr=avgerr/(ntest*nout);
|
|
if((double)(avgerr)>eps)
|
|
return(true);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPCAnalysis |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPCAUnit
|
|
{
|
|
public:
|
|
static bool TestPCA(const bool silent);
|
|
|
|
private:
|
|
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPCAnalysis |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestPCAUnit::TestPCA(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxm=0;
|
|
double threshold=0;
|
|
int m=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int info=0;
|
|
double t=0;
|
|
double h=0;
|
|
double tmean=0;
|
|
double tmeans=0;
|
|
double tstddev=0;
|
|
double tstddevs=0;
|
|
double tmean2=0;
|
|
double tmeans2=0;
|
|
double tstddev2=0;
|
|
double tstddevs2=0;
|
|
bool pcaconverrors;
|
|
bool pcaorterrors;
|
|
bool pcavarerrors;
|
|
bool pcaopterrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double means[];
|
|
double s[];
|
|
double t2[];
|
|
double t3[];
|
|
//--- create matrix
|
|
CMatrixDouble v;
|
|
CMatrixDouble x;
|
|
//--- Primary settings
|
|
maxm=10;
|
|
maxn=100;
|
|
passcount=1;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
waserrors=false;
|
|
pcaconverrors=false;
|
|
pcaorterrors=false;
|
|
pcavarerrors=false;
|
|
pcaopterrors=false;
|
|
//--- Test 1: N random points in M-dimensional space
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- Generate task
|
|
x.Resize(n,m);
|
|
ArrayResize(means,m);
|
|
//--- change values
|
|
for(j=0; j<m; j++)
|
|
means[j]=1.5*CMath::RandomReal()-0.75;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
x.Set(i,j,means[j]+(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Solve
|
|
CPCAnalysis::PCABuildBasis(x,n,m,info,s,v);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
pcaconverrors=true;
|
|
continue;
|
|
}
|
|
//--- Orthogonality test
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
t=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
t+=v.Get(i_,i)*v.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
t=t-1;
|
|
//--- search errors
|
|
pcaorterrors=pcaorterrors||MathAbs(t)>threshold;
|
|
}
|
|
}
|
|
//--- Variance test
|
|
ArrayResize(t2,n);
|
|
for(k=0; k<m; k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
t=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
t+=x.Get(i,i_)*v[i_][k];
|
|
t2[i]=t;
|
|
}
|
|
//--- function call
|
|
CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs);
|
|
//--- check
|
|
if(n!=1)
|
|
t=CMath::Sqr(tstddev)*n/(n-1);
|
|
else
|
|
t=0;
|
|
//--- search errors
|
|
pcavarerrors=pcavarerrors||MathAbs(t-s[k])>threshold;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<=m-2; k++)
|
|
pcavarerrors=pcavarerrors||s[k]<s[k+1];
|
|
//--- Optimality: different perturbations in V[..,0] can't
|
|
//--- increase variance of projection - can only decrease.
|
|
ArrayResize(t2,n);
|
|
ArrayResize(t3,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change values
|
|
t=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
t+=x.Get(i,i_)*v[i_][0];
|
|
t2[i]=t;
|
|
}
|
|
//--- function call
|
|
CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs);
|
|
//--- calculation
|
|
for(k=0; k<=2*m-1; k++)
|
|
{
|
|
h=0.001;
|
|
//--- check
|
|
if(k%2!=0)
|
|
h=-h;
|
|
for(i_=0; i_<n; i_++)
|
|
t3[i_]=t2[i_];
|
|
for(i_=0; i_<n; i_++)
|
|
t3[i_]=t3[i_]+h*x[i_][k/2];
|
|
//--- change value
|
|
t=0;
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- check
|
|
if(j!=k/2)
|
|
t=t+CMath::Sqr(v[j][0]);
|
|
else
|
|
t=t+CMath::Sqr(v[j][0]+h);
|
|
}
|
|
t=1/MathSqrt(t);
|
|
for(i_=0; i_<n; i_++)
|
|
t3[i_]=t*t3[i_];
|
|
//--- function call
|
|
CalculateMV(t3,n,tmean2,tmeans2,tstddev2,tstddevs2);
|
|
//--- search errors
|
|
pcaopterrors=pcaopterrors||tstddev2>tstddev+threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- Special test for N=0
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
//--- Solve
|
|
CPCAnalysis::PCABuildBasis(x,0,m,info,s,v);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
pcaconverrors=true;
|
|
continue;
|
|
}
|
|
//--- Orthogonality test
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- change value
|
|
t=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
t+=v.Get(i_,i)*v.Get(i_,j);
|
|
//--- check
|
|
if(i==j)
|
|
t=t-1;
|
|
//--- search errors
|
|
pcaorterrors=pcaorterrors||MathAbs(t)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- Final report
|
|
waserrors=((pcaconverrors||pcaorterrors)||pcavarerrors)||pcaopterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("PCA TEST");
|
|
PrintResult("TOTAL RESULTS",!waserrors);
|
|
PrintResult("* CONVERGENCE",!pcaconverrors);
|
|
PrintResult("* ORTOGONALITY",!pcaorterrors);
|
|
PrintResult("* VARIANCE REPORT",!pcavarerrors);
|
|
PrintResult("* OPTIMALITY",!pcaopterrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Moments estimates and their errors |
|
|
//+------------------------------------------------------------------+
|
|
void CTestPCAUnit::CalculateMV(double &x[],const int n,double &mean,
|
|
double &means,double &stddev,double &stddevs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double variance=0;
|
|
//--- initialization
|
|
mean=0;
|
|
means=1;
|
|
stddev=0;
|
|
stddevs=1;
|
|
variance=0;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- Mean
|
|
for(i=0; i<n; i++)
|
|
mean=mean+x[i];
|
|
mean=mean/n;
|
|
//--- Variance (using corrected two-pass algorithm)
|
|
if(n!=1)
|
|
{
|
|
//--- change values
|
|
v1=0;
|
|
for(i=0; i<n; i++)
|
|
v1+=CMath::Sqr(x[i]-mean);
|
|
v2=0;
|
|
for(i=0; i<n; i++)
|
|
v2=v2+(x[i]-mean);
|
|
v2=CMath::Sqr(v2)/n;
|
|
variance=(v1-v2)/n;
|
|
//--- check
|
|
if(variance<0.0)
|
|
variance=0;
|
|
stddev=MathSqrt(variance);
|
|
}
|
|
//--- Errors
|
|
means=stddev/MathSqrt(n);
|
|
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CODESolver |
|
|
//+------------------------------------------------------------------+
|
|
class CTestODESolverUnit
|
|
{
|
|
public:
|
|
static bool TestODESolver(const bool silent);
|
|
|
|
private:
|
|
static void Unset2D(CMatrixDouble &x);
|
|
static void Unset1D(double &x[]);
|
|
static void UnsetRep(CODESolverReport &rep);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestODESolverUnit::TestODESolver(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
bool curerrors;
|
|
bool rkckerrors;
|
|
bool waserrors;
|
|
double h=0;
|
|
double eps=0;
|
|
int solver=0;
|
|
int pass=0;
|
|
int mynfev=0;
|
|
double v=0;
|
|
int m=0;
|
|
int m2=0;
|
|
int i=0;
|
|
double err=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double xtbl[];
|
|
double xg[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble ytbl;
|
|
//--- objects of classes
|
|
CODESolverReport rep;
|
|
CODESolverState state;
|
|
//--- initialization
|
|
rkckerrors=false;
|
|
waserrors=false;
|
|
passcount=10;
|
|
//--- simple test: just A*sin(x)+B*cos(x)
|
|
if(!CAp::Assert(passcount>=2))
|
|
return(false);
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
for(solver=0; solver<=0; solver++)
|
|
{
|
|
//--- prepare
|
|
h=1.0E-2;
|
|
eps=1.0E-5;
|
|
//--- check
|
|
if(pass%2==0)
|
|
eps=-eps;
|
|
//--- allocation
|
|
ArrayResize(y,2);
|
|
//--- change values
|
|
for(i=0; i<=1; i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
m=2+CMath::RandomInteger(10);
|
|
//--- allocation
|
|
ArrayResize(xg,m);
|
|
xg[0]=(m-1)*CMath::RandomReal();
|
|
for(i=1; i<m; i++)
|
|
xg[i]=xg[i-1]+CMath::RandomReal();
|
|
//--- change values
|
|
v=2*M_PI/(xg[m-1]-xg[0]);
|
|
for(i_=0; i_<m; i_++)
|
|
xg[i_]=v*xg[i_];
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
for(i_=0; i_<m; i_++)
|
|
xg[i_]=-1*xg[i_];
|
|
}
|
|
mynfev=0;
|
|
//--- choose solver
|
|
if(solver==0)
|
|
CODESolver::ODESolverRKCK(y,2,xg,m,eps,h,state);
|
|
//--- solve
|
|
while(CODESolver::ODESolverIteration(state))
|
|
{
|
|
state.m_dy[0]=state.m_y[1];
|
|
state.m_dy[1]=-state.m_y[0];
|
|
mynfev=mynfev+1;
|
|
}
|
|
//--- function call
|
|
CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep);
|
|
//--- check results
|
|
curerrors=false;
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
curerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
curerrors=curerrors||m2!=m;
|
|
err=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(ytbl[i][0]-(y[0]*MathCos(xtbl[i]-xtbl[0])+y[1]*MathSin(xtbl[i]-xtbl[0]))));
|
|
err=MathMax(err,MathAbs(ytbl[i][1]-(-(y[0]*MathSin(xtbl[i]-xtbl[0]))+y[1]*MathCos(xtbl[i]-xtbl[0]))));
|
|
}
|
|
curerrors=curerrors||err>10*MathAbs(eps);
|
|
curerrors=curerrors||mynfev!=rep.m_nfev;
|
|
}
|
|
//--- check
|
|
if(solver==0)
|
|
rkckerrors=rkckerrors||curerrors;
|
|
}
|
|
}
|
|
//--- another test:
|
|
//--- y(0)=0
|
|
//--- dy/dx=f(x,y)
|
|
//--- f(x,y)=0, x<1
|
|
//--- x-1,x>=1
|
|
//--- with BOTH absolute and fractional tolerances.
|
|
//--- Starting from zero will be real challenge for
|
|
//--- fractional tolerance.
|
|
if(!CAp::Assert(passcount>=2))
|
|
return(false);
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
h=1.0E-4;
|
|
eps=1.0E-4;
|
|
//--- check
|
|
if(pass%2==0)
|
|
eps=-eps;
|
|
//--- allocation
|
|
ArrayResize(y,1);
|
|
y[0]=0;
|
|
m=21;
|
|
//--- allocation
|
|
ArrayResize(xg,m);
|
|
for(i=0; i<m; i++)
|
|
xg[i]=(double)(2*i)/(double)(m-1);
|
|
mynfev=0;
|
|
//--- function call
|
|
CODESolver::ODESolverRKCK(y,1,xg,m,eps,h,state);
|
|
//--- cycle
|
|
while(CODESolver::ODESolverIteration(state))
|
|
{
|
|
state.m_dy[0]=MathMax(state.m_x-1,0);
|
|
mynfev=mynfev+1;
|
|
}
|
|
//--- function call
|
|
CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
rkckerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rkckerrors=rkckerrors||m2!=m;
|
|
err=0;
|
|
for(i=0; i<m; i++)
|
|
err=MathMax(err,MathAbs(ytbl[i][0]-CMath::Sqr(MathMax(xg[i]-1,0))/2));
|
|
rkckerrors=rkckerrors||err>MathAbs(eps);
|
|
rkckerrors=rkckerrors||mynfev!=rep.m_nfev;
|
|
}
|
|
}
|
|
//--- end
|
|
waserrors=rkckerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ODE SOLVER");
|
|
PrintResult("* RK CASH-KARP",!rkckerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
void CTestODESolverUnit::Unset2D(CMatrixDouble &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x.Set(0,0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
void CTestODESolverUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
void CTestODESolverUnit::UnsetRep(CODESolverReport &rep)
|
|
{
|
|
rep.m_nfev=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CFastFourierTransform |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFFTUnit
|
|
{
|
|
public:
|
|
static bool TestFFT(const bool silent);
|
|
|
|
private:
|
|
static void RefFFTC1D(complex &a[],const int n);
|
|
static void RefFFTC1DInv(complex &a[],const int n);
|
|
static void RefInternalCFFT(double &a[],const int nn,const bool inversefft);
|
|
static void RefInternalRFFT(double &a[],const int nn,complex &f[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFFTUnit::TestFFT(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int maxn=0;
|
|
double bidierr=0;
|
|
double bidirerr=0;
|
|
double referr=0;
|
|
double refrerr=0;
|
|
double reinterr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool bidierrors;
|
|
bool refrerrors;
|
|
bool bidirerrors;
|
|
bool reinterrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex a1[];
|
|
complex a2[];
|
|
complex a3[];
|
|
double r1[];
|
|
double r2[];
|
|
double buf[];
|
|
//--- object of class
|
|
CFtPlan plan;
|
|
//--- initialization
|
|
maxn=128;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
bidierrors=false;
|
|
referrors=false;
|
|
bidirerrors=false;
|
|
refrerrors=false;
|
|
reinterrors=false;
|
|
waserrors=false;
|
|
//--- Test bi-directional error: norm(x-invFFT(FFT(x)))
|
|
bidierr=0;
|
|
bidirerr=0;
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- Complex FFT/invFFT
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a1[i].real=2*CMath::RandomReal()-1;
|
|
a1[i].imag=2*CMath::RandomReal()-1;
|
|
a2[i]=a1[i];
|
|
a3[i]=a1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTC1D(a2,n);
|
|
CFastFourierTransform::FFTC1DInv(a2,n);
|
|
CFastFourierTransform::FFTC1DInv(a3,n);
|
|
CFastFourierTransform::FFTC1D(a3,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a3[i]));
|
|
}
|
|
//--- Real
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function call
|
|
CFastFourierTransform::FFTR1D(r2,n,a1);
|
|
for(i_=0; i_<n; i_++)
|
|
r2[i_]=0*r2[i_];
|
|
//--- function call
|
|
CFastFourierTransform::FFTR1DInv(a1,n,r2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
bidirerr=MathMax(bidirerr,CMath::AbsComplex(r1[i]-r2[i]));
|
|
}
|
|
//--- search errors
|
|
bidierrors=bidierrors||bidierr>errtol;
|
|
bidirerrors=bidirerrors||bidirerr>errtol;
|
|
//--- Test against reference O(N^2) implementation
|
|
referr=0;
|
|
refrerr=0;
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- Complex FFT
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a1[i].real=2*CMath::RandomReal()-1;
|
|
a1[i].imag=2*CMath::RandomReal()-1;
|
|
a2[i]=a1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTC1D(a1,n);
|
|
RefFFTC1D(a2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
//--- Complex inverse FFT
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a1[i].real=2*CMath::RandomReal()-1;
|
|
a1[i].imag=2*CMath::RandomReal()-1;
|
|
a2[i]=a1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTC1DInv(a1,n);
|
|
RefFFTC1DInv(a2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
//--- Real forward/inverse FFT:
|
|
//--- * calculate and check forward FFT
|
|
//--- * use precalculated FFT to check backward FFT
|
|
//--- fill unused parts of frequencies array with random numbers
|
|
//--- to ensure that they are not really used
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTR1D(r1,n,a1);
|
|
RefInternalRFFT(r2,n,a2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
refrerr=MathMax(refrerr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
//--- allocation
|
|
ArrayResize(a3,(int)MathFloor((double)n/2.0)+1);
|
|
for(i=0; i<=(int)MathFloor((double)n/2.0); i++)
|
|
a3[i]=a2[i];
|
|
a3[0].imag=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(n%2==0)
|
|
a3[(int)MathFloor((double)n/2.0)].imag=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
r1[i]=0;
|
|
//--- function call
|
|
CFastFourierTransform::FFTR1DInv(a3,n,r1);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
refrerr=MathMax(refrerr,MathAbs(r2[i]-r1[i]));
|
|
}
|
|
//--- search errors
|
|
referrors=referrors||referr>errtol;
|
|
refrerrors=refrerrors||refrerr>errtol;
|
|
//--- test internal real even FFT
|
|
reinterr=0;
|
|
for(k=1; k<=maxn/2; k++)
|
|
{
|
|
n=2*k;
|
|
//--- Real forward FFT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function call
|
|
CFtBase::FtComplexFFTPlan(n/2,1,plan);
|
|
//--- allocation
|
|
ArrayResize(buf,n);
|
|
//--- function calls
|
|
CFastFourierTransform::FFTR1DInternalEven(r1,n,buf,plan);
|
|
RefInternalRFFT(r2,n,a2);
|
|
//--- search errors
|
|
reinterr=MathMax(reinterr,MathAbs(r1[0]-a2[0].real));
|
|
reinterr=MathMax(reinterr,MathAbs(r1[1]-a2[n/2].real));
|
|
for(i=1; i<=n/2-1; i++)
|
|
{
|
|
reinterr=MathMax(reinterr,MathAbs(r1[2*i+0]-a2[i].real));
|
|
reinterr=MathMax(reinterr,MathAbs(r1[2*i+1]-a2[i].imag));
|
|
}
|
|
//--- Real backward FFT
|
|
ArrayResize(r1,n);
|
|
for(i=0; i<n; i++)
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
ArrayResize(a2,(int)MathFloor((double)n/2.0)+1);
|
|
a2[0]=r1[0];
|
|
for(i=1; i<=(int)MathFloor((double)n/2.0)-1; i++)
|
|
{
|
|
a2[i].real=r1[2*i+0];
|
|
a2[i].imag=r1[2*i+1];
|
|
}
|
|
a2[(int)MathFloor((double)n/2.0)]=r1[1];
|
|
//--- function call
|
|
CFtBase::FtComplexFFTPlan(n/2,1,plan);
|
|
//--- allocation
|
|
ArrayResize(buf,n);
|
|
//--- function calls
|
|
CFastFourierTransform::FFTR1DInvInternalEven(r1,n,buf,plan);
|
|
CFastFourierTransform::FFTR1DInv(a2,n,r2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
reinterr=MathMax(reinterr,MathAbs(r1[i]-r2[i]));
|
|
}
|
|
//--- search errors
|
|
reinterrors=reinterrors||reinterr>errtol;
|
|
//--- end
|
|
waserrors=(((bidierrors||bidirerrors)||referrors)||refrerrors)||reinterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FFT");
|
|
PrintResult("FINAL RESULT",!waserrors);
|
|
PrintResult("* BI-DIRECTIONAL COMPLEX TEST",!bidierrors);
|
|
PrintResult("* AGAINST REFERENCE COMPLEX FFT",!referrors);
|
|
PrintResult("* BI-DIRECTIONAL REAL TEST",!bidirerrors);
|
|
PrintResult("* AGAINST REFERENCE REAL FFT",!refrerrors);
|
|
PrintResult("* INTERNAL EVEN FFT",!reinterrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FFT |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFFTUnit::RefFFTC1D(complex &a[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"FFTC1D: incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(buf,2*n);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
{
|
|
buf[2*i+0]=a[i].real;
|
|
buf[2*i+1]=a[i].imag;
|
|
}
|
|
//--- function call
|
|
RefInternalCFFT(buf,n,false);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i].real=buf[2*i+0];
|
|
a[i].imag=buf[2*i+1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference inverse FFT |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFFTUnit::RefFFTC1DInv(complex &a[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"FFTC1DInv: incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(buf,2*n);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
{
|
|
buf[2*i+0]=a[i].real;
|
|
buf[2*i+1]=a[i].imag;
|
|
}
|
|
//--- function call
|
|
RefInternalCFFT(buf,n,true);
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a[i].real=buf[2*i+0];
|
|
a[i].imag=buf[2*i+1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal complex FFT stub. |
|
|
//| Uses straightforward formula with O(N^2) complexity. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFFTUnit::RefInternalCFFT(double &a[],const int nn,
|
|
const bool inversefft)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double hre=0;
|
|
double him=0;
|
|
double c=0;
|
|
double s=0;
|
|
double real=0;
|
|
double imag=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- allocation
|
|
ArrayResize(tmp,2*nn);
|
|
//--- check
|
|
if(!inversefft)
|
|
{
|
|
for(i=0; i<=nn-1; i++)
|
|
{
|
|
//--- change values
|
|
hre=0;
|
|
him=0;
|
|
//--- calculation
|
|
for(k=0; k<=nn-1; k++)
|
|
{
|
|
real=a[2*k];
|
|
imag=a[2*k+1];
|
|
c=MathCos(-(2*M_PI*k*i/nn));
|
|
s=MathSin(-(2*M_PI*k*i/nn));
|
|
hre=hre+c*real-s*imag;
|
|
him=him+c*imag+s*real;
|
|
}
|
|
//--- change values
|
|
tmp[2*i]=hre;
|
|
tmp[2*i+1]=him;
|
|
}
|
|
for(i=0; i<=2*nn-1; i++)
|
|
a[i]=tmp[i];
|
|
}
|
|
else
|
|
{
|
|
for(k=0; k<=nn-1; k++)
|
|
{
|
|
//--- change values
|
|
hre=0;
|
|
him=0;
|
|
//--- calculation
|
|
for(i=0; i<=nn-1; i++)
|
|
{
|
|
real=a[2*i];
|
|
imag=a[2*i+1];
|
|
c=MathCos(2*M_PI*k*i/nn);
|
|
s=MathSin(2*M_PI*k*i/nn);
|
|
hre=hre+c*real-s*imag;
|
|
him=him+c*imag+s*real;
|
|
}
|
|
//--- change values
|
|
tmp[2*k]=hre/nn;
|
|
tmp[2*k+1]=him/nn;
|
|
}
|
|
for(i=0; i<=2*nn-1; i++)
|
|
a[i]=tmp[i];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal real FFT stub. |
|
|
//| Uses straightforward formula with O(N^2) complexity. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFFTUnit::RefInternalRFFT(double &a[],const int nn,complex &f[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- allocation
|
|
ArrayResize(tmp,2*nn);
|
|
//--- copy
|
|
for(i=0; i<=nn-1; i++)
|
|
{
|
|
tmp[2*i]=a[i];
|
|
tmp[2*i+1]=0;
|
|
}
|
|
//--- function call
|
|
RefInternalCFFT(tmp,nn,false);
|
|
//--- allocation
|
|
ArrayResize(f,nn);
|
|
//--- copy
|
|
for(i=0; i<=nn-1; i++)
|
|
{
|
|
f[i].real=tmp[2*i+0];
|
|
f[i].imag=tmp[2*i+1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CConv |
|
|
//+------------------------------------------------------------------+
|
|
class CTestConvUnit
|
|
{
|
|
public:
|
|
static bool TestConv(const bool silent);
|
|
|
|
private:
|
|
static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestConvUnit::TestConv(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int i=0;
|
|
int rkind=0;
|
|
int circkind=0;
|
|
int maxn=0;
|
|
double referr=0;
|
|
double refrerr=0;
|
|
double inverr=0;
|
|
double invrerr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool refrerrors;
|
|
bool inverrors;
|
|
bool invrerrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double ra[];
|
|
double rb[];
|
|
double rr1[];
|
|
double rr2[];
|
|
complex ca[];
|
|
complex cb[];
|
|
complex cr1[];
|
|
complex cr2[];
|
|
//--- initialization
|
|
maxn=32;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
referrors=false;
|
|
refrerrors=false;
|
|
inverrors=false;
|
|
invrerrors=false;
|
|
waserrors=false;
|
|
//--- Test against reference O(N^2) implementation.
|
|
//--- Automatic ConvC1D() and different algorithms of ConvC1DX() are tested.
|
|
referr=0;
|
|
refrerr=0;
|
|
for(m=1; m<=maxn; m++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(circkind=0; circkind<=1; circkind++)
|
|
{
|
|
for(rkind=-3; rkind<=1; rkind++)
|
|
{
|
|
//--- skip impossible combinations of parameters:
|
|
//--- * circular convolution,M<N,RKind<>-3 - internal subroutine does not support M<N.
|
|
if((circkind!=0 && m<n) && rkind!=-3)
|
|
continue;
|
|
//--- Complex convolution
|
|
ArrayResize(ca,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
ca[i].real=2*CMath::RandomReal()-1;
|
|
ca[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cb,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
cb[i].real=2*CMath::RandomReal()-1;
|
|
cb[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
//--- check
|
|
if(rkind==-3)
|
|
{
|
|
//--- test wrapper subroutine:
|
|
//--- * circular/non-circular
|
|
if(circkind==0)
|
|
CConv::ConvC1D(ca,m,cb,n,cr1);
|
|
else
|
|
CConv::ConvC1DCircular(ca,m,cb,n,cr1);
|
|
}
|
|
else
|
|
{
|
|
//--- test internal subroutine
|
|
if(m>=n)
|
|
{
|
|
//--- test internal subroutine:
|
|
//--- * circular/non-circular mode
|
|
CConv::ConvC1DX(ca,m,cb,n,circkind!=0,rkind,0,cr1);
|
|
}
|
|
else
|
|
{
|
|
//--- test internal subroutine - circular mode only
|
|
if(!CAp::Assert(circkind==0,"Convolution test: internal error!"))
|
|
return(false);
|
|
//--- function call
|
|
CConv::ConvC1DX(cb,n,ca,m,false,rkind,0,cr1);
|
|
}
|
|
}
|
|
//--- check
|
|
if(circkind==0)
|
|
RefConvC1D(ca,m,cb,n,cr2);
|
|
else
|
|
RefConvC1DCircular(ca,m,cb,n,cr2);
|
|
//--- check
|
|
if(circkind==0)
|
|
{
|
|
for(i=0; i<=m+n-2; i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
}
|
|
//--- Real convolution
|
|
ArrayResize(ra,m);
|
|
for(i=0; i<m; i++)
|
|
ra[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rb,n);
|
|
for(i=0; i<n; i++)
|
|
rb[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
//--- check
|
|
if(rkind==-3)
|
|
{
|
|
//--- test wrapper subroutine:
|
|
//--- * circular/non-circular
|
|
if(circkind==0)
|
|
CConv::ConvR1D(ra,m,rb,n,rr1);
|
|
else
|
|
CConv::ConvR1DCircular(ra,m,rb,n,rr1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- test internal subroutine:
|
|
//--- * circular/non-circular mode
|
|
CConv::ConvR1DX(ra,m,rb,n,circkind!=0,rkind,0,rr1);
|
|
}
|
|
else
|
|
{
|
|
//--- test internal subroutine - non-circular mode only
|
|
CConv::ConvR1DX(rb,n,ra,m,circkind!=0,rkind,0,rr1);
|
|
}
|
|
}
|
|
//--- check
|
|
if(circkind==0)
|
|
RefConvR1D(ra,m,rb,n,rr2);
|
|
else
|
|
RefConvR1DCircular(ra,m,rb,n,rr2);
|
|
//--- check
|
|
if(circkind==0)
|
|
{
|
|
for(i=0; i<=m+n-2; i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
referrors=referrors||referr>errtol;
|
|
refrerrors=refrerrors||refrerr>errtol;
|
|
//--- Test inverse convolution
|
|
inverr=0;
|
|
invrerr=0;
|
|
for(m=1; m<=maxn; m++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- Complex circilar and non-circular
|
|
ArrayResize(ca,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
ca[i].real=2*CMath::RandomReal()-1;
|
|
ca[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cb,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
cb[i].real=2*CMath::RandomReal()-1;
|
|
cb[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
ArrayResize(cr2,1);
|
|
//--- function calls
|
|
CConv::ConvC1D(ca,m,cb,n,cr2);
|
|
CConv::ConvC1DInv(cr2,m+n-1,cb,n,cr1);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
{
|
|
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
ArrayResize(cr2,1);
|
|
//--- function calls
|
|
CConv::ConvC1DCircular(ca,m,cb,n,cr2);
|
|
CConv::ConvC1DCircularInv(cr2,m,cb,n,cr1);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
|
|
//--- Real circilar and non-circular
|
|
ArrayResize(ra,m);
|
|
for(i=0; i<m; i++)
|
|
ra[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rb,n);
|
|
for(i=0; i<n; i++)
|
|
rb[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
ArrayResize(rr2,1);
|
|
//--- function calls
|
|
CConv::ConvR1D(ra,m,rb,n,rr2);
|
|
CConv::ConvR1DInv(rr2,m+n-1,rb,n,rr1);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i]));
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
ArrayResize(rr2,1);
|
|
//--- function calls
|
|
CConv::ConvR1DCircular(ra,m,rb,n,rr2);
|
|
CConv::ConvR1DCircularInv(rr2,m,rb,n,rr1);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
inverrors=inverrors||inverr>errtol;
|
|
invrerrors=invrerrors||invrerr>errtol;
|
|
//--- end
|
|
waserrors=((referrors||refrerrors)||inverrors)||invrerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CONVOLUTION");
|
|
PrintResult("FINAL RESULT",!waserrors);
|
|
PrintResult("* AGAINST REFERENCE COMPLEX CONV",!referrors);
|
|
PrintResult("* AGAINST REFERENCE REAL CONV",!refrerrors);
|
|
PrintResult("* COMPLEX INVERSE",!inverrors);
|
|
PrintResult("* REAL INVERSE",!invrerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestConvUnit::RefConvC1D(complex &a[],const int m,complex &b[],
|
|
const int n,complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0; i<=m+n-2; i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i; i_<=i+n-1; i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestConvUnit::RefConvC1DCircular(complex &a[],const int m,
|
|
complex &b[],const int n,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex buf[];
|
|
//--- function call
|
|
RefConvC1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0; i_<m; i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0; i_<=j2; i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FFT |
|
|
//+------------------------------------------------------------------+
|
|
void CTestConvUnit::RefConvR1D(double &a[],const int m,double &b[],
|
|
const int n,double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0; i<=m+n-2; i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i; i_<=i+n-1; i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestConvUnit::RefConvR1DCircular(double &a[],const int m,
|
|
double &b[],const int n,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- function call
|
|
RefConvR1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0; i_<m; i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0; i_<=j2; i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CCorr |
|
|
//+------------------------------------------------------------------+
|
|
class CTestCorrUnit
|
|
{
|
|
public:
|
|
static bool TestCorr(const bool silent);
|
|
|
|
private:
|
|
static void RefCorrC1D(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]);
|
|
static void RefCorrC1DCircular(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]);
|
|
static void RefCorrR1D(double &signal[],const int n,double &pattern[],const int m,double &r[]);
|
|
static void RefCorrR1DCircular(double &signal[],const int n,double &pattern[],const int m,double &r[]);
|
|
static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CCorr |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestCorrUnit::TestCorr(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int i=0;
|
|
int maxn=0;
|
|
double referr=0;
|
|
double refrerr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool refrerrors;
|
|
bool inverrors;
|
|
bool invrerrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double ra[];
|
|
double rb[];
|
|
double rr1[];
|
|
double rr2[];
|
|
complex ca[];
|
|
complex cb[];
|
|
complex cr1[];
|
|
complex cr2[];
|
|
//--- initialization
|
|
maxn=32;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
referrors=false;
|
|
refrerrors=false;
|
|
inverrors=false;
|
|
invrerrors=false;
|
|
waserrors=false;
|
|
//--- Test against reference O(N^2) implementation.
|
|
referr=0;
|
|
refrerr=0;
|
|
for(m=1; m<=maxn; m++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- Complex correlation
|
|
ArrayResize(ca,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
ca[i].real=2*CMath::RandomReal()-1;
|
|
ca[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cb,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
cb[i].real=2*CMath::RandomReal()-1;
|
|
cb[i].imag=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
//--- function calls
|
|
CCorr::CorrC1D(ca,m,cb,n,cr1);
|
|
RefCorrC1D(ca,m,cb,n,cr2);
|
|
//--- search errors
|
|
for(i=0; i<=m+n-2; i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
//--- function calls
|
|
CCorr::CorrC1DCircular(ca,m,cb,n,cr1);
|
|
RefCorrC1DCircular(ca,m,cb,n,cr2);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
//--- Real correlation
|
|
ArrayResize(ra,m);
|
|
for(i=0; i<m; i++)
|
|
ra[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rb,n);
|
|
for(i=0; i<n; i++)
|
|
rb[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
//--- function calls
|
|
CCorr::CorrR1D(ra,m,rb,n,rr1);
|
|
RefCorrR1D(ra,m,rb,n,rr2);
|
|
//--- search errors
|
|
for(i=0; i<=m+n-2; i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
//--- function calls
|
|
CCorr::CorrR1DCircular(ra,m,rb,n,rr1);
|
|
RefCorrR1DCircular(ra,m,rb,n,rr2);
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
referrors=referrors||referr>errtol;
|
|
refrerrors=refrerrors||refrerr>errtol;
|
|
//--- end
|
|
waserrors=referrors||refrerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CORRELATION");
|
|
PrintResult("FINAL RESULT",!waserrors);
|
|
PrintResult("* AGAINST REFERENCE COMPLEX CORR",!referrors);
|
|
PrintResult("* AGAINST REFERENCE REAL CORR",!refrerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefCorrC1D(complex &signal[],const int n,
|
|
complex &pattern[],const int m,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex s[];
|
|
//--- allocation
|
|
ArrayResize(s,m+n-1);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
s[i_]=signal[i_];
|
|
for(i=n; i<=m+n-2; i++)
|
|
s[i]=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- check
|
|
if(i+j>=n)
|
|
break;
|
|
v+=CMath::Conj(pattern[j])*s[i+j];
|
|
}
|
|
r[i]=v;
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<m; i++)
|
|
{
|
|
v=0;
|
|
for(j=i; j<m; j++)
|
|
v+=CMath::Conj(pattern[j])*s[j-i];
|
|
r[m+n-1-i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefCorrC1DCircular(complex &signal[],const int n,
|
|
complex &pattern[],const int m,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
//--- allocation
|
|
ArrayResize(r,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0;
|
|
for(j=0; j<m; j++)
|
|
v+=CMath::Conj(pattern[j])*signal[(i+j)%n];
|
|
r[i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefCorrR1D(double &signal[],const int n,
|
|
double &pattern[],const int m,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double s[];
|
|
//--- allocation
|
|
ArrayResize(s,m+n-1);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
s[i_]=signal[i_];
|
|
for(i=n; i<=m+n-2; i++)
|
|
s[i]=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- check
|
|
if(i+j>=n)
|
|
break;
|
|
v+=pattern[j]*s[i+j];
|
|
}
|
|
r[i]=v;
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<m; i++)
|
|
{
|
|
v=0;
|
|
for(j=i; j<m; j++)
|
|
v+=pattern[j]*s[-i+j];
|
|
r[m+n-1-i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefCorrR1DCircular(double &signal[],const int n,
|
|
double &pattern[],const int m,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
//--- allocation
|
|
ArrayResize(r,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<m; j++)
|
|
v+=pattern[j]*signal[(i+j)%n];
|
|
r[i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefConvC1D(complex &a[],const int m,complex &b[],
|
|
const int n,complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0; i<=m+n-2; i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i; i_<=i+n-1; i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefConvC1DCircular(complex &a[],const int m,
|
|
complex &b[],const int n,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex buf[];
|
|
//--- function call
|
|
RefConvC1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0; i_<m; i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0; i_<=j2; i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FFT |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefConvR1D(double &a[],const int m,double &b[],
|
|
const int n,double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0; i<=m+n-2; i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i; i_<=i+n-1; i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestCorrUnit::RefConvR1DCircular(double &a[],const int m,
|
|
double &b[],const int n,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- function call
|
|
RefConvR1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0; i_<m; i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0; i_<=j2; i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CFastHartleyTransform |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFHTUnit
|
|
{
|
|
public:
|
|
static bool TestFHT(const bool silent);
|
|
|
|
private:
|
|
static void RefFHTR1D(double &a[],const int n);
|
|
static void RefFHTR1DInv(double &a[],const int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFHTUnit::TestFHT(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int maxn=0;
|
|
double bidierr=0;
|
|
double referr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool bidierrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double r1[];
|
|
double r2[];
|
|
double r3[];
|
|
//--- initialization
|
|
maxn=128;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
bidierrors=false;
|
|
referrors=false;
|
|
waserrors=false;
|
|
//--- Test bi-directional error: norm(x-invFHT(FHT(x)))
|
|
bidierr=0;
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- FHT/invFHT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
ArrayResize(r3,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
r3[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastHartleyTransform::FHTR1D(r2,n);
|
|
CFastHartleyTransform::FHTR1DInv(r2,n);
|
|
CFastHartleyTransform::FHTR1DInv(r3,n);
|
|
CFastHartleyTransform::FHTR1D(r3,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bidierr=MathMax(bidierr,MathAbs(r1[i]-r2[i]));
|
|
bidierr=MathMax(bidierr,MathAbs(r1[i]-r3[i]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
bidierrors=bidierrors||bidierr>errtol;
|
|
//--- Test against reference O(N^2) implementation
|
|
referr=0;
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- FHT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastHartleyTransform::FHTR1D(r1,n);
|
|
RefFHTR1D(r2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
|
|
//--- inverse FHT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastHartleyTransform::FHTR1DInv(r1,n);
|
|
RefFHTR1DInv(r2,n);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
|
|
}
|
|
//--- search errors
|
|
referrors=referrors||referr>errtol;
|
|
//--- end
|
|
waserrors=bidierrors||referrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FHT");
|
|
PrintResult("FINAL RESULT",!waserrors);
|
|
PrintResult("* BI-DIRECTIONAL TEST",!bidierrors);
|
|
PrintResult("* AGAINST REFERENCE FHT",!referrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FHT |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFHTUnit::RefFHTR1D(double &a[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"RefFHTR1D: incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(buf,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=a[j]*(MathCos(2*M_PI*i*j/n)+MathSin(2*M_PI*i*j/n));
|
|
buf[i]=v;
|
|
}
|
|
//--- copy
|
|
for(i=0; i<n; i++)
|
|
a[i]=buf[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference inverse FHT |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFHTUnit::RefFHTR1DInv(double &a[],const int n)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"RefFHTR1DInv: incorrect N!"))
|
|
return;
|
|
//--- function call
|
|
RefFHTR1D(a,n);
|
|
//--- change values
|
|
for(int i=0; i<n; i++)
|
|
a[i]=a[i]/n;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGaussQ |
|
|
//+------------------------------------------------------------------+
|
|
class CTestGQUnit
|
|
{
|
|
public:
|
|
static bool TestGQ(const bool silent);
|
|
|
|
private:
|
|
static double MapKind(const int k);
|
|
static void BuildGaussLegendreQuadrature(const int n,double &x[],double &w[]);
|
|
static void BuildGaussJacobiQuadrature(const int n,const double alpha,const double beta,double &x[],double &w[]);
|
|
static void BuildGaussLaguerreQuadrature(const int n,const double alpha,double &x[],double &w[]);
|
|
static void BuildGaussHermiteQuadrature(const int n,double &x[],double &w[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestGQUnit::TestGQ(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
int n=0;
|
|
int i=0;
|
|
int info=0;
|
|
int akind=0;
|
|
int bkind=0;
|
|
double alphac=0;
|
|
double betac=0;
|
|
double errtol=0;
|
|
double nonstricterrtol=0;
|
|
double stricterrtol=0;
|
|
bool recerrors;
|
|
bool specerrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double alpha[];
|
|
double beta[];
|
|
double x[];
|
|
double w[];
|
|
double x2[];
|
|
double w2[];
|
|
//--- initialization
|
|
recerrors=false;
|
|
specerrors=false;
|
|
waserrors=false;
|
|
errtol=1.0E-12;
|
|
nonstricterrtol=1.0E-6;
|
|
stricterrtol=1000*CMath::m_machineepsilon;
|
|
//--- Three tests for rec-based Gauss quadratures with known weights/nodes:
|
|
//--- 1. Gauss-Legendre with N=2
|
|
//--- 2. Gauss-Legendre with N=5
|
|
//--- 3. Gauss-Chebyshev with N=1,2,4,8,...,512
|
|
err=0;
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,2);
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
beta[1]=1.0/(double)(4*1*1-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateRec(alpha,beta,2.0,2,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+MathSqrt(3)/3));
|
|
err=MathMax(err,MathAbs(x[1]-MathSqrt(3)/3));
|
|
err=MathMax(err,MathAbs(w[0]-1));
|
|
err=MathMax(err,MathAbs(w[1]-1));
|
|
for(i=0; i<=0; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,5);
|
|
ArrayResize(beta,5);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
for(i=1; i<=4; i++)
|
|
{
|
|
alpha[i]=0;
|
|
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
|
|
}
|
|
//--- function call
|
|
CGaussQ::GQGenerateRec(alpha,beta,2.0,5,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+MathSqrt(245+14*MathSqrt(70))/21));
|
|
err=MathMax(err,MathAbs(x[0]+x[4]));
|
|
err=MathMax(err,MathAbs(x[1]+MathSqrt(245-14*MathSqrt(70))/21));
|
|
err=MathMax(err,MathAbs(x[1]+x[3]));
|
|
err=MathMax(err,MathAbs(x[2]));
|
|
err=MathMax(err,MathAbs(w[0]-(322-13*MathSqrt(70))/900));
|
|
err=MathMax(err,MathAbs(w[0]-w[4]));
|
|
err=MathMax(err,MathAbs(w[1]-(322+13*MathSqrt(70))/900));
|
|
err=MathMax(err,MathAbs(w[1]-w[3]));
|
|
err=MathMax(err,MathAbs(w[2]-128.0/225.0));
|
|
for(i=0; i<=3; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- calculation
|
|
n=1;
|
|
while(n<=512)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(alpha,n);
|
|
ArrayResize(beta,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
alpha[i]=0;
|
|
//--- check
|
|
if(i==0)
|
|
beta[i]=0;
|
|
//--- check
|
|
if(i==1)
|
|
beta[i]=1.0/2.0;
|
|
//--- check
|
|
if(i>1)
|
|
beta[i]=1.0/4.0;
|
|
}
|
|
//--- function call
|
|
CGaussQ::GQGenerateRec(alpha,beta,M_PI,n,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-MathCos(M_PI*(n-i-0.5)/n)));
|
|
err=MathMax(err,MathAbs(w[i]-M_PI/n));
|
|
}
|
|
for(i=0; i<=n-2; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
n=n*2;
|
|
}
|
|
//--- search errors
|
|
recerrors=recerrors||err>errtol;
|
|
//--- Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes:
|
|
//--- 1. Gauss-Lobatto with N=3
|
|
//--- 2. Gauss-Lobatto with N=4
|
|
//--- 3. Gauss-Lobatto with N=6
|
|
err=0;
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,2);
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,3,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]));
|
|
err=MathMax(err,MathAbs(x[2]-1));
|
|
err=MathMax(err,MathAbs(w[0]-1.0/3.0));
|
|
err=MathMax(err,MathAbs(w[1]-4.0/3.0));
|
|
err=MathMax(err,MathAbs(w[2]-1.0/3.0));
|
|
for(i=0; i<=1; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,3);
|
|
ArrayResize(beta,3);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
alpha[2]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
beta[2]=(double)(2*2)/(double)(4*2*2-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,4,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]+MathSqrt(5)/5));
|
|
err=MathMax(err,MathAbs(x[2]-MathSqrt(5)/5));
|
|
err=MathMax(err,MathAbs(x[3]-1));
|
|
err=MathMax(err,MathAbs(w[0]-1.0/6.0));
|
|
err=MathMax(err,MathAbs(w[1]-5.0/6.0));
|
|
err=MathMax(err,MathAbs(w[2]-5.0/6.0));
|
|
err=MathMax(err,MathAbs(w[3]-1.0/6.0));
|
|
for(i=0; i<=2; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,5);
|
|
ArrayResize(beta,5);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
alpha[2]=0;
|
|
alpha[3]=0;
|
|
alpha[4]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
beta[2]=(double)(2*2)/(double)(4*2*2-1);
|
|
beta[3]=(double)(3*3)/(double)(4*3*3-1);
|
|
beta[4]=(double)(4*4)/(double)(4*4*4-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,6,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]+MathSqrt((7+2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[2]+MathSqrt((7-2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[3]-MathSqrt((7-2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[4]-MathSqrt((7+2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[5]-1));
|
|
err=MathMax(err,MathAbs(w[0]-1.0/(double)15));
|
|
err=MathMax(err,MathAbs(w[1]-(14-MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[2]-(14+MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[3]-(14+MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[4]-(14-MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[5]-1.0/(double)15));
|
|
for(i=0; i<=4; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
recerrors=recerrors||err>errtol;
|
|
//--- Three tests for rec-based Gauss-Radau quadratures with known weights/nodes:
|
|
//--- 1. Gauss-Radau with N=2
|
|
//--- 2. Gauss-Radau with N=3
|
|
//--- 3. Gauss-Radau with N=3 (another case)
|
|
err=0;
|
|
ArrayResize(alpha,1);
|
|
ArrayResize(beta,2);
|
|
alpha[0]=0;
|
|
beta[0]=0;
|
|
beta[1]=1.0/3.0;
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,2,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]-1.0/3.0));
|
|
err=MathMax(err,MathAbs(w[0]-0.5));
|
|
err=MathMax(err,MathAbs(w[1]-1.5));
|
|
for(i=0; i<=0; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,3);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
for(i=0; i<=2; i++)
|
|
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,3,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]-(1-MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(x[2]-(1+MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(w[0]-2.0/9.0));
|
|
err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18));
|
|
err=MathMax(err,MathAbs(w[2]-(16-MathSqrt(6))/18));
|
|
for(i=0; i<=1; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,3);
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
for(i=0; i<=2; i++)
|
|
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,1,3,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[2]-1));
|
|
err=MathMax(err,MathAbs(x[1]+(1-MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(x[0]+(1+MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(w[2]-2.0/9.0));
|
|
err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18));
|
|
err=MathMax(err,MathAbs(w[0]-(16-MathSqrt(6))/18));
|
|
for(i=0; i<=1; i++)
|
|
recerrors=recerrors||x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- search errors
|
|
recerrors=recerrors||err>errtol;
|
|
//--- test recurrence-based special cases (Legendre,Jacobi,Hermite,...)
|
|
//--- against another implementation (polynomial root-finder)
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
//--- test gauss-legendre
|
|
err=0;
|
|
CGaussQ::GQGenerateGaussLegendre(n,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussLegendreQuadrature(n,x2,w2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
//--- search errors
|
|
specerrors=specerrors||err>errtol;
|
|
//--- Test Gauss-Jacobi.
|
|
//--- Since task is much more difficult we will use less strict
|
|
//--- threshold.
|
|
err=0;
|
|
for(akind=0; akind<=9; akind++)
|
|
{
|
|
for(bkind=0; bkind<=9; bkind++)
|
|
{
|
|
alphac=MapKind(akind);
|
|
betac=MapKind(bkind);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussJacobi(n,alphac,betac,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussJacobiQuadrature(n,alphac,betac,x2,w2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
}
|
|
}
|
|
//--- search errors
|
|
specerrors=specerrors||err>nonstricterrtol;
|
|
//--- special test for Gauss-Jacobi (Chebyshev weight
|
|
//--- function with analytically known nodes/weights)
|
|
err=0;
|
|
CGaussQ::GQGenerateGaussJacobi(n,-0.5,-0.5,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]+MathCos(M_PI*(i+0.5)/n)));
|
|
err=MathMax(err,MathAbs(w[i]-M_PI/n));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
//--- search errors
|
|
specerrors=specerrors||err>stricterrtol;
|
|
//--- Test Gauss-Laguerre
|
|
err=0;
|
|
for(akind=0; akind<=9; akind++)
|
|
{
|
|
alphac=MapKind(akind);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLaguerre(n,alphac,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussLaguerreQuadrature(n,alphac,x2,w2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
}
|
|
//--- search errors
|
|
specerrors=specerrors||err>nonstricterrtol;
|
|
//--- Test Gauss-Hermite
|
|
err=0;
|
|
CGaussQ::GQGenerateGaussHermite(n,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussHermiteQuadrature(n,x2,w2);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
//--- search errors
|
|
specerrors=specerrors||err>nonstricterrtol;
|
|
}
|
|
//--- end
|
|
waserrors=recerrors||specerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING GAUSS QUADRATURES");
|
|
PrintResult("FINAL RESULT",!waserrors);
|
|
PrintResult("* SPECIAL CASES (LEGENDRE/JACOBI/..)",!specerrors);
|
|
PrintResult("* RECURRENCE-BASED",!recerrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Maps: |
|
|
//| 0=> -0.9 |
|
|
//| 1=> -0.5 |
|
|
//| 2=> -0.1 |
|
|
//| 3=> 0.0 |
|
|
//| 4=> +0.1 |
|
|
//| 5=> +0.5 |
|
|
//| 6=> +0.9 |
|
|
//| 7=> +1.0 |
|
|
//| 8=> +1.5 |
|
|
//| 9=> +2.0 |
|
|
//+------------------------------------------------------------------+
|
|
double CTestGQUnit::MapKind(const int k)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(k==0)
|
|
result=-0.9;
|
|
//--- check
|
|
if(k==1)
|
|
result=-0.5;
|
|
//--- check
|
|
if(k==2)
|
|
result=-0.1;
|
|
//--- check
|
|
if(k==3)
|
|
result=0.0;
|
|
//--- check
|
|
if(k==4)
|
|
result=0.1;
|
|
//--- check
|
|
if(k==5)
|
|
result=0.5;
|
|
//--- check
|
|
if(k==6)
|
|
result=0.9;
|
|
//--- check
|
|
if(k==7)
|
|
result=1.0;
|
|
//--- check
|
|
if(k==8)
|
|
result=1.5;
|
|
//--- check
|
|
if(k==9)
|
|
result=2.0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Legendre, another variant |
|
|
//+------------------------------------------------------------------+
|
|
void CTestGQUnit::BuildGaussLegendreQuadrature(const int n,double &x[],
|
|
double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double dp3=0;
|
|
double tmp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
for(i=0; i<=(n+1)/2-1; i++)
|
|
{
|
|
r=MathCos(M_PI*(4*i+3)/(4*n+2));
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- change values
|
|
p2=0;
|
|
p3=1;
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
p1=p2;
|
|
p2=p3;
|
|
p3=((2*j+1)*r*p2-j*p1)/(j+1);
|
|
}
|
|
dp3=n*(r*p3-p2)/(r*r-1);
|
|
r1=r;
|
|
r=r-p3/dp3;
|
|
}
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- calculation
|
|
x[i]=r;
|
|
x[n-1-i]=-r;
|
|
w[i]=2/((1-r*r)*dp3*dp3);
|
|
w[n-1-i]=2/((1-r*r)*dp3*dp3);
|
|
}
|
|
//--- shift
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=n-2-i; j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Jacobi, another variant |
|
|
//+------------------------------------------------------------------+
|
|
void CTestGQUnit::BuildGaussJacobiQuadrature(const int n,const double alpha,
|
|
const double beta,double &x[],
|
|
double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double t1=0;
|
|
double t2=0;
|
|
double t3=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double pp=0;
|
|
double an=0;
|
|
double bn=0;
|
|
double a=0;
|
|
double b=0;
|
|
double c=0;
|
|
double tmpsgn=0;
|
|
double tmp=0;
|
|
double alfbet=0;
|
|
double temp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
{
|
|
//--- calculation
|
|
an=alpha/n;
|
|
bn=beta/n;
|
|
t1=(1+alpha)*(2.78/(4+n*n)+0.768*an/n);
|
|
t2=1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn;
|
|
r=(t2-t1)/t2;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==1)
|
|
{
|
|
//--- calculation
|
|
t1=(4.1+alpha)/((1+alpha)*(1+0.156*alpha));
|
|
t2=1+0.06*(n-8)*(1+0.12*alpha)/n;
|
|
t3=1+0.012*beta*(1+0.25*MathAbs(alpha))/n;
|
|
r=r-t1*t2*t3*(1-r);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==2)
|
|
{
|
|
//--- calculation
|
|
t1=(1.67+0.28*alpha)/(1+0.37*alpha);
|
|
t2=1+0.22*(n-8)/n;
|
|
t3=1+8*beta/((6.28+beta)*n*n);
|
|
r=r-t1*t2*t3*(x[0]-r);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i<n-2)
|
|
r=3*x[i-1]-3*x[i-2]+x[i-3];
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==n-2)
|
|
{
|
|
//--- calculation
|
|
t1=(1+0.235*beta)/(0.766+0.119*beta);
|
|
t2=1/(1+0.639*(n-4)/(1+0.71*(n-4)));
|
|
t3=1/(1+20*alpha/((7.5+alpha)*n*n));
|
|
r=r+t1*t2*t3*(r-x[i-2]);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==n-1)
|
|
{
|
|
//--- calculation
|
|
t1=(1+0.37*beta)/(1.67+0.28*beta);
|
|
t2=1/(1+0.22*(n-8)/n);
|
|
t3=1/(1+8*alpha/((6.28+alpha)*n*n));
|
|
r=r+t1*t2*t3*(r-x[i-2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
alfbet=alpha+beta;
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- change values
|
|
temp=2+alfbet;
|
|
p1=(alpha-beta+temp*r)*0.5;
|
|
p2=1;
|
|
//--- calculation
|
|
for(j=2; j<=n; j++)
|
|
{
|
|
p3=p2;
|
|
p2=p1;
|
|
temp=2*j+alfbet;
|
|
a=2*j*(j+alfbet)*(temp-2);
|
|
b=(temp-1)*(alpha*alpha-beta*beta+temp*(temp-2)*r);
|
|
c=2*(j-1+alpha)*(j-1+beta)*temp;
|
|
p1=(b*p2-c*p3)/a;
|
|
}
|
|
pp=(n*(alpha-beta-temp*r)*p1+2*(n+alpha)*(n+beta)*p2)/(temp*(1-r*r));
|
|
r1=r;
|
|
r=r1-p1/pp;
|
|
}
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- change values
|
|
x[i]=r;
|
|
w[i]=MathExp(CGammaFunc::LnGamma(alpha+n,tmpsgn)+CGammaFunc::LnGamma(beta+n,tmpsgn)-CGammaFunc::LnGamma(n+1,tmpsgn)-CGammaFunc::LnGamma(n+alfbet+1,tmpsgn))*temp*MathPow(2,alfbet)/(pp*p2);
|
|
}
|
|
//--- shift
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=n-2-i; j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Laguerre, another variant |
|
|
//+------------------------------------------------------------------+
|
|
void CTestGQUnit::BuildGaussLaguerreQuadrature(const int n,const double alpha,
|
|
double &x[],double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double dp3=0;
|
|
double tsg=0;
|
|
double tmp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
r=(1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha);
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==1)
|
|
r=r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n);
|
|
else
|
|
r=r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x[i-2]);
|
|
}
|
|
do
|
|
{
|
|
//--- change values
|
|
p2=0;
|
|
p3=1;
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
p1=p2;
|
|
p2=p3;
|
|
p3=((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1);
|
|
}
|
|
dp3=(n*p3-(n+alpha)*p2)/r;
|
|
r1=r;
|
|
r=r-p3/dp3;
|
|
}
|
|
//--- change values
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- change values
|
|
x[i]=r;
|
|
w[i]=-(MathExp(CGammaFunc::LnGamma(alpha+n,tsg)-CGammaFunc::LnGamma(n,tsg))/(dp3*n*p2));
|
|
}
|
|
//--- shift
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=n-2-i; j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Hermite, another variant |
|
|
//+------------------------------------------------------------------+
|
|
void CTestGQUnit::BuildGaussHermiteQuadrature(const int n,double &x[],
|
|
double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double dp3=0;
|
|
double pipm4=0;
|
|
double tmp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
pipm4=MathPow(M_PI,-0.25);
|
|
for(i=0; i<=(n+1)/2-1; i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
r=MathSqrt(2*n+1)-1.85575*MathPow(2*n+1,-(1.0/6.0));
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==1)
|
|
r=r-1.14*MathPow(n,0.426)/r;
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==2)
|
|
r=1.86*r-0.86*x[0];
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==3)
|
|
r=1.91*r-0.91*x[1];
|
|
else
|
|
r=2*r-x[i-2];
|
|
}
|
|
}
|
|
}
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- change values
|
|
p2=0;
|
|
p3=pipm4;
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
p1=p2;
|
|
p2=p3;
|
|
p3=p2*r*MathSqrt(2.0/(double)(j+1))-p1*MathSqrt((double)j/(double)(j+1));
|
|
}
|
|
dp3=MathSqrt(2*j)*p2;
|
|
r1=r;
|
|
r=r-p3/dp3;
|
|
}
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- change values
|
|
x[i]=r;
|
|
w[i]=2/(dp3*dp3);
|
|
x[n-1-i]=-x[i];
|
|
w[n-1-i]=w[i];
|
|
}
|
|
//--- shift
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=n-2-i; j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGaussKronrodQ |
|
|
//+------------------------------------------------------------------+
|
|
class CTestGKQUnit
|
|
{
|
|
public:
|
|
static bool TestGKQ(const bool silent);
|
|
|
|
private:
|
|
static double MapKind(const int k);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestGKQUnit::TestGKQ(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int pkind=0;
|
|
double errtol=0;
|
|
double eps=0;
|
|
double nonstricterrtol=0;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int info=0;
|
|
double err=0;
|
|
int akind=0;
|
|
int bkind=0;
|
|
double alphac=0;
|
|
double betac=0;
|
|
int info1=0;
|
|
int info2=0;
|
|
bool successatleastonce;
|
|
bool intblerrors;
|
|
bool vstblerrors;
|
|
bool generrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double x1[];
|
|
double wg1[];
|
|
double wk1[];
|
|
double x2[];
|
|
double wg2[];
|
|
double wk2[];
|
|
//--- initialization
|
|
intblerrors=false;
|
|
vstblerrors=false;
|
|
generrors=false;
|
|
waserrors=false;
|
|
errtol=10000*CMath::m_machineepsilon;
|
|
nonstricterrtol=1000*errtol;
|
|
//--- test recurrence-based Legendre nodes against the precalculated table
|
|
for(pkind=0; pkind<=5; pkind++)
|
|
{
|
|
n=0;
|
|
//--- check
|
|
if(pkind==0)
|
|
n=15;
|
|
//--- check
|
|
if(pkind==1)
|
|
n=21;
|
|
//--- check
|
|
if(pkind==2)
|
|
n=31;
|
|
//--- check
|
|
if(pkind==3)
|
|
n=41;
|
|
//--- check
|
|
if(pkind==4)
|
|
n=51;
|
|
//--- check
|
|
if(pkind==5)
|
|
n=61;
|
|
//--- function calls
|
|
CGaussKronrodQ::GKQLegendreCalc(n,info,x1,wk1,wg1);
|
|
CGaussKronrodQ::GKQLegendreTbl(n,x2,wk2,wg2,eps);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
generrors=true;
|
|
break;
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vstblerrors=vstblerrors||MathAbs(x1[i]-x2[i])>errtol;
|
|
vstblerrors=vstblerrors||MathAbs(wk1[i]-wk2[i])>errtol;
|
|
vstblerrors=vstblerrors||MathAbs(wg1[i]-wg2[i])>errtol;
|
|
}
|
|
}
|
|
//--- Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes
|
|
//--- calculated with subroutines from GQ unit.
|
|
for(k=1; k<=30; k++)
|
|
{
|
|
n=2*k+1;
|
|
//--- Gauss-Legendre
|
|
err=0;
|
|
CGaussKronrodQ::GKQGenerateGaussLegendre(n,info1,x1,wk1,wg1);
|
|
CGaussQ::GQGenerateGaussLegendre(k,info2,x2,wg2);
|
|
//--- check
|
|
if(info1>0 && info2>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<k; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x1[2*i+1]-x2[i]));
|
|
err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i]));
|
|
}
|
|
}
|
|
else
|
|
generrors=true;
|
|
//--- search errors
|
|
generrors=generrors||err>errtol;
|
|
}
|
|
//--- calculation
|
|
for(k=1; k<=15; k++)
|
|
{
|
|
n=2*k+1;
|
|
//--- Gauss-Jacobi
|
|
successatleastonce=false;
|
|
err=0;
|
|
for(akind=0; akind<=9; akind++)
|
|
{
|
|
for(bkind=0; bkind<=9; bkind++)
|
|
{
|
|
//--- change values
|
|
alphac=MapKind(akind);
|
|
betac=MapKind(bkind);
|
|
//--- function calls
|
|
CGaussKronrodQ::GKQGenerateGaussJacobi(n,alphac,betac,info1,x1,wk1,wg1);
|
|
CGaussQ::GQGenerateGaussJacobi(k,alphac,betac,info2,x2,wg2);
|
|
//--- check
|
|
if(info1>0 && info2>0)
|
|
{
|
|
successatleastonce=true;
|
|
//--- search errors
|
|
for(i=0; i<k; i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x1[2*i+1]-x2[i]));
|
|
err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i]));
|
|
}
|
|
}
|
|
else
|
|
generrors=generrors||info1!=-5;
|
|
}
|
|
}
|
|
//--- search errors
|
|
generrors=(generrors||err>errtol)||!successatleastonce;
|
|
}
|
|
//--- end
|
|
waserrors=(intblerrors||vstblerrors)||generrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING GAUSS-KRONROD QUADRATURES");
|
|
PrintResult("FINAL RESULT",!waserrors);
|
|
PrintResult("* PRE-CALCULATED TABLE",!intblerrors);
|
|
PrintResult("* CALCULATED AGAINST THE TABLE",!vstblerrors);
|
|
PrintResult("* GENERAL PROPERTIES",!generrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Maps: |
|
|
//| 0=> -0.9 |
|
|
//| 1=> -0.5 |
|
|
//| 2=> -0.1 |
|
|
//| 3=> 0.0 |
|
|
//| 4=> +0.1 |
|
|
//| 5=> +0.5 |
|
|
//| 6=> +0.9 |
|
|
//| 7=> +1.0 |
|
|
//| 8=> +1.5 |
|
|
//| 9=> +2.0 |
|
|
//+------------------------------------------------------------------+
|
|
double CTestGKQUnit::MapKind(const int k)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
//--- check
|
|
if(k==0)
|
|
result=-0.9;
|
|
//--- check
|
|
if(k==1)
|
|
result=-0.5;
|
|
//--- check
|
|
if(k==2)
|
|
result=-0.1;
|
|
//--- check
|
|
if(k==3)
|
|
result=0.0;
|
|
//--- check
|
|
if(k==4)
|
|
result=0.1;
|
|
//--- check
|
|
if(k==5)
|
|
result=0.5;
|
|
//--- check
|
|
if(k==6)
|
|
result=0.9;
|
|
//--- check
|
|
if(k==7)
|
|
result=1.0;
|
|
//--- check
|
|
if(k==8)
|
|
result=1.5;
|
|
//--- check
|
|
if(k==9)
|
|
result=2.0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CAutoGK |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAutoGKUnit
|
|
{
|
|
public:
|
|
static bool TestAutoGK(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAutoGKUnit::TestAutoGK(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double a=0;
|
|
double b=0;
|
|
double v=0;
|
|
double exact=0;
|
|
double eabs=0;
|
|
double alpha=0;
|
|
int pkind=0;
|
|
double errtol=0;
|
|
bool simpleerrors;
|
|
bool sngenderrors;
|
|
bool waserrors;
|
|
//--- objects of classes
|
|
CAutoGKState state;
|
|
CAutoGKReport rep;
|
|
//--- initialization
|
|
simpleerrors=false;
|
|
sngenderrors=false;
|
|
waserrors=false;
|
|
errtol=10000*CMath::m_machineepsilon;
|
|
//--- Simple test: integral(exp(x),+-1,+-2),no maximum width requirements
|
|
a=(2*CMath::RandomInteger(2)-1)*1.0;
|
|
b=(2*CMath::RandomInteger(2)-1)*2.0;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmooth(a,b,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathExp(state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=MathExp(b)-MathExp(a);
|
|
eabs=MathAbs(MathExp(b)-MathExp(a));
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs;
|
|
//--- Simple test: integral(exp(x),+-1,+-2),XWidth=0.1
|
|
a=(2*CMath::RandomInteger(2)-1)*1.0;
|
|
b=(2*CMath::RandomInteger(2)-1)*2.0;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmoothW(a,b,0.1,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathExp(state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=MathExp(b)-MathExp(a);
|
|
eabs=MathAbs(MathExp(b)-MathExp(a));
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs;
|
|
//--- Simple test: integral(cos(100*x),0,2*pi),no maximum width requirements
|
|
a=0;
|
|
b=2*M_PI;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmooth(a,b,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathCos(100*state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=0;
|
|
eabs=4;
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs;
|
|
//--- Simple test: integral(cos(100*x),0,2*pi),XWidth=0.3
|
|
a=0;
|
|
b=2*M_PI;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmoothW(a,b,0.3,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathCos(100*state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=0;
|
|
eabs=4;
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs;
|
|
//--- singular problem on [a,b]=[0.1,0.5]
|
|
//--- f2(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1
|
|
for(pkind=0; pkind<=6; pkind++)
|
|
{
|
|
a=0.1;
|
|
b=0.5;
|
|
//--- check
|
|
if(pkind==0)
|
|
alpha=-0.9;
|
|
//--- check
|
|
if(pkind==1)
|
|
alpha=-0.5;
|
|
//--- check
|
|
if(pkind==2)
|
|
alpha=-0.1;
|
|
//--- check
|
|
if(pkind==3)
|
|
alpha=0.0;
|
|
//--- check
|
|
if(pkind==4)
|
|
alpha=0.1;
|
|
//--- check
|
|
if(pkind==5)
|
|
alpha=0.5;
|
|
//--- check
|
|
if(pkind==6)
|
|
alpha=0.9;
|
|
//--- f1(x)=(1+x)*(x-a)^alpha,-1 < alpha < 1
|
|
//--- 1. use singular integrator for [a,b]
|
|
//--- 2. use singular integrator for [b,a]
|
|
exact=MathPow(b-a,alpha+2)/(alpha+2)+(1+a)*MathPow(b-a,alpha+1)/(alpha+1);
|
|
eabs=MathAbs(exact);
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(a,b,alpha,0.0,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_xminusa<0.01)
|
|
state.m_f=MathPow(state.m_xminusa,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors||MathAbs(v-exact)>errtol*eabs;
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(b,a,0.0,alpha,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_bminusx>-0.01)
|
|
state.m_f=MathPow(-state.m_bminusx,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors||MathAbs(-v-exact)>errtol*eabs;
|
|
//--- f1(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1
|
|
//--- 1. use singular integrator for [a,b]
|
|
//--- 2. use singular integrator for [b,a]
|
|
exact=(1+b)*MathPow(b-a,alpha+1)/(alpha+1)-MathPow(b-a,alpha+2)/(alpha+2);
|
|
eabs=MathAbs(exact);
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(a,b,0.0,alpha,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_bminusx<0.01)
|
|
state.m_f=MathPow(state.m_bminusx,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors||MathAbs(v-exact)>errtol*eabs;
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(b,a,alpha,0.0,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_xminusa>-0.01)
|
|
state.m_f=MathPow(-state.m_xminusa,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors||MathAbs(-v-exact)>errtol*eabs;
|
|
}
|
|
//--- end
|
|
waserrors=simpleerrors||sngenderrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING AUTOGK");
|
|
PrintResult("INTEGRATION WITH GIVEN ACCURACY",!(simpleerrors || sngenderrors));
|
|
PrintResult("* SIMPLE PROBLEMS",!simpleerrors);
|
|
PrintResult("* SINGULAR PROBLEMS (ENDS OF INTERVAL)",!sngenderrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CIDWInt |
|
|
//+------------------------------------------------------------------+
|
|
class CTestIDWIntUnit
|
|
{
|
|
public:
|
|
static bool TestIDWInt(const bool silent);
|
|
|
|
private:
|
|
static void TestContinuity(CIDWModel &model,int nx,int ny,CRowDouble &x0,CRowDouble &x1,int nsteps,int d,bool &err);
|
|
static void TestCommon(bool &err);
|
|
static void TestMSTAB(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing IDW interpolation |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestIDWIntUnit::TestIDWInt(const bool silent)
|
|
{
|
|
bool commonerrors=false;
|
|
bool mstaberrors=false;
|
|
|
|
TestCommon(commonerrors);
|
|
TestMSTAB(mstaberrors);
|
|
bool waserrors=commonerrors||mstaberrors;
|
|
|
|
if(!silent)
|
|
{
|
|
Print("TESTING INVERSE DISTANCE WEIGHTING");
|
|
PrintResult("* common properties",!commonerrors);
|
|
PrintResult("* MSTAB-specific tests",!mstaberrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing continuity properties: C0 (D=0) or C1 (D=1) continuity. |
|
|
//| Error flag is modified on failure, unchanged on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestIDWIntUnit::TestContinuity(CIDWModel &model,int nx,
|
|
int ny,CRowDouble &x0,CRowDouble &x1,
|
|
int nsteps,
|
|
int d,bool &err)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int cidx=0;
|
|
double t=0;
|
|
CRowDouble xc;
|
|
CRowDouble yc;
|
|
CMatrixDouble yy;
|
|
double lc1=0;
|
|
double lc2=0;
|
|
//--- check
|
|
if(!CAp::Assert(nsteps>=10,"TestContinuity: NSteps is too small"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
if(!CAp::Assert(d==0 || d==1,"TestContinuity: incorrect D"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- Compute sequence of function values
|
|
xc.Resize(nx);
|
|
yy.Resize(nsteps,ny);
|
|
for(i=0; i<nsteps; i++)
|
|
{
|
|
t=(double)i/(double)(nsteps-1);
|
|
for(j=0; j<nx; j++)
|
|
xc.Set(j,x0[j]*t+x1[j]*(1-t));
|
|
CIDWInt::IDWCalcBuf(model,xc,yc);
|
|
for(j=0; j<ny; j++)
|
|
yy.Set(i,j,yc[j]);
|
|
}
|
|
//--- Evaluate all differentiability levels (C0, C1) requested by user
|
|
for(cidx=0; cidx<=d; cidx++)
|
|
{
|
|
//--- Compute Lipschitz constant for original and increased steps
|
|
lc1=0;
|
|
lc2=0;
|
|
for(i=0; i<=nsteps-3; i++)
|
|
{
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
lc1=MathMax(lc1,MathAbs(yy.Get(i,j)-yy[i+1][ j]));
|
|
lc2=MathMax(lc2,MathAbs(yy.Get(i,j)-yy[i+2][j])/2);
|
|
}
|
|
}
|
|
if(lc2>1.0E-4 && lc1>(1.750*lc2))
|
|
{
|
|
Print("testidwunit.ap:67");
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- Differentiate function, repeat one more time
|
|
for(i=0; i<=nsteps-2; i++)
|
|
{
|
|
for(j=0; j<ny; j++)
|
|
yy.Set(i,j,yy.Get(i+1,j)-yy.Get(i,j));
|
|
}
|
|
nsteps --;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test MSTAB; Err is set to True on failure, unchanged otherwise. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestIDWIntUnit::TestCommon(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int algotype=0;
|
|
int i=0;
|
|
int i0=0;
|
|
int i1=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
CMatrixDouble xy;
|
|
double v=0;
|
|
double vv=0;
|
|
bool initdone;
|
|
CIDWModelShell model;
|
|
CIDWModelShell model1;
|
|
CIDWBuilder builder;
|
|
CIDWReport rep;
|
|
CIDWCalcBuffer buffer;
|
|
double shepardp=0;
|
|
double rbase=0;
|
|
double tol=0;
|
|
double mindistinf=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refmax=0;
|
|
double refr2=0;
|
|
double refrss=0;
|
|
double reftss=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
CHighQualityRandStateShell rs;
|
|
double x0=0;
|
|
double x1=0;
|
|
double x2=0;
|
|
int continuitytesting=0;
|
|
CRowDouble x;
|
|
CRowDouble xx;
|
|
CRowDouble y;
|
|
CRowDouble meany;
|
|
CHighQualityRand::HQRndRandomize(rs.GetInnerObj());
|
|
tol=1.0E-10;
|
|
mindistinf=0.05;
|
|
//--- Try all algorithms
|
|
for(algotype=0; (algotype<=2 && !err); algotype++)
|
|
{
|
|
//--- Test empty dataset
|
|
for(nx=1; (nx<=5 && !err); nx++)
|
|
{
|
|
for(ny=1; (ny<=5 && !err); ny++)
|
|
{
|
|
initdone=false;
|
|
CIDWInt::IDWBuilderCreate(nx,ny,builder);
|
|
if(algotype==0)
|
|
{
|
|
initdone=true;
|
|
shepardp=1+(nx+1)*CHighQualityRand::HQRndUniformR(rs.GetInnerObj());
|
|
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
|
|
}
|
|
if(algotype==1)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
|
|
}
|
|
if(algotype==2)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(initdone,__FUNCTION__+": unexpected AlgoType"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
|
|
{
|
|
//--- Fit and store result directly into the variable
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
}
|
|
else
|
|
{
|
|
//--- Fit, store result to temporary, pass through the CSerializer
|
|
CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj());
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj());
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj());
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer);
|
|
//--- Test report
|
|
if(rep.m_rmserror!=0.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:169");
|
|
return;
|
|
}
|
|
if(rep.m_avgerror!=0.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:170");
|
|
return;
|
|
}
|
|
if(rep.m_maxerror!=0.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:171");
|
|
return;
|
|
}
|
|
if(rep.m_r2!=1.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:172");
|
|
return;
|
|
}
|
|
//--- Test simplified evaluation
|
|
x0=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
|
|
x1=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
|
|
x2=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
|
|
if(nx==1 && ny==1 && CIDWInt::IDWCalc1(model.GetInnerObj(),x0)!=0.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:181");
|
|
return;
|
|
}
|
|
if(nx==2 && ny==1 && CIDWInt::IDWCalc2(model.GetInnerObj(),x0,x1)!=0.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:183");
|
|
return;
|
|
}
|
|
if(nx==3 && ny==1 && CIDWInt::IDWCalc3(model.GetInnerObj(),x0,x1,x2)!=0.0)
|
|
{
|
|
err=true;
|
|
Print("testidwunit.ap:185");
|
|
return;
|
|
}
|
|
//--- Test generic evaluation
|
|
x.Resize(nx);
|
|
for(i=0; i<nx; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
y.Resize(0);
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:195"))
|
|
return;
|
|
for(i=0; i<ny; i++)
|
|
if(CAp::SetErrorFlag(err,y[i]!=0.0,"testidwunit.ap:199"))
|
|
return;
|
|
y.Resize(0);
|
|
CIDWInt::IDWCalcBuf(model.GetInnerObj(),x,y);
|
|
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:202"))
|
|
return;
|
|
for(i=0; i<ny; i++)
|
|
if(CAp::SetErrorFlag(err,y[i]!=0.0,"testidwunit.ap:206"))
|
|
return;
|
|
y.Resize(0);
|
|
CIDWInt::IDWTsCalcBuf(model.GetInnerObj(),buffer,x,y);
|
|
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:209"))
|
|
return;
|
|
for(i=0; i<ny; i++)
|
|
if(CAp::SetErrorFlag(err,y[i]!=0.0,"testidwunit.ap:213"))
|
|
return;
|
|
}
|
|
}
|
|
//--- Generate random dataset with distinct points, test interpolation
|
|
//--- properties (target function is reproduced almost exactly, the model
|
|
//--- is continuous)
|
|
for(pass=1; pass<=20; pass++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),25);
|
|
nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
|
|
x.Resize(nx);
|
|
xx.Resize(nx);
|
|
y.Resize(ny);
|
|
//--- Generate dataset with distinct points
|
|
xy.Resize(n,nx+ny);
|
|
meany=vector<double>::Zeros(ny);
|
|
i=0;
|
|
while(i<n)
|
|
{
|
|
//--- Generate random point
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
//--- Test distance between newly generated point and other ones.
|
|
//--- Repeat point generation if it is too close to some other point.
|
|
v=CMath::m_maxrealnumber;
|
|
for(i0=0; i0<i; i0++)
|
|
{
|
|
vv=0;
|
|
for(j=0; j<nx ; j++)
|
|
vv=MathMax(vv,MathAbs(xy.Get(i,j)-xy.Get(i0,j)));
|
|
v=MathMin(v,vv);
|
|
}
|
|
if(v<mindistinf)
|
|
continue;
|
|
//--- Point is accepted
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy.Set(i,nx+j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
meany.Add(j,xy.Get(i,nx+j)/n);
|
|
}
|
|
i++;
|
|
}
|
|
//--- Build IDW model
|
|
initdone=false;
|
|
CIDWInt::IDWBuilderCreate(nx,ny,builder);
|
|
if(algotype==0)
|
|
{
|
|
initdone=true;
|
|
shepardp=1+(nx+1)*CHighQualityRand::HQRndUniformR(rs.GetInnerObj());
|
|
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
|
|
}
|
|
if(algotype==1)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
|
|
}
|
|
if(algotype==2)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(initdone,"TestCommon: unexpected AlgoType"))
|
|
return;
|
|
CIDWInt::IDWBuilderSetPoints(builder,xy,n);
|
|
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
|
|
{
|
|
//--- Fit and store result directly into the variable
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
}
|
|
else
|
|
{
|
|
//--- Fit, store result to temporary, pass through the CSerializer
|
|
CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj());
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj());
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj());
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer);
|
|
//--- Test error metrics
|
|
//--- NOTE: we expect that dataset is reproduced exactly
|
|
if(CAp::SetErrorFlag(err,MathAbs(rep.m_rmserror)>tol,"testidwunit.ap:320") ||
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_avgerror)>tol,"testidwunit.ap:321") ||
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_maxerror)>tol,"testidwunit.ap:322") ||
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_r2)<(1.0-tol),"testidwunit.ap:323"))
|
|
return;
|
|
//--- Test that dataset is actually exactly reproduced
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- Test generic evaluation
|
|
x=xy[i]+0;
|
|
y.Resize(0);
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:337"))
|
|
return;
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-xy.Get(i,nx+j))>tol,"testidwunit.ap:339"))
|
|
return;
|
|
k=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2*ny+1);
|
|
y.Resize(k);
|
|
CIDWInt::IDWCalcBuf(model.GetInnerObj(),x,y);
|
|
if(CAp::SetErrorFlag(err,CAp::Len(y)!=MathMax(ny,k),"testidwunit.ap:346"))
|
|
return;
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-xy.Get(i,nx+j))>tol,"testidwunit.ap:348"))
|
|
return;
|
|
k=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2*ny+1);
|
|
y.Resize(k);
|
|
CIDWInt::IDWTsCalcBuf(model.GetInnerObj(),buffer,x,y);
|
|
if(CAp::SetErrorFlag(err,CAp::Len(y)!=MathMax(ny,k),"testidwunit.ap:355"))
|
|
return;
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-xy.Get(i,nx+j))>tol,"testidwunit.ap:357"))
|
|
return;
|
|
//--- Specialized 1, 2, 3-dimensional cases
|
|
if(ny==1)
|
|
{
|
|
if(nx==1 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc1(model.GetInnerObj(),x[0])-xy.Get(i,nx))>tol,"testidwunit.ap:366"))
|
|
return;
|
|
if(nx==2 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),x[0],x[1])-xy.Get(i,nx))>tol,"testidwunit.ap:368"))
|
|
return;
|
|
if(nx==3 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc3(model.GetInnerObj(),x[0],x[1],x[2])-xy.Get(i,nx))>tol,"testidwunit.ap:370"))
|
|
return;
|
|
}
|
|
}
|
|
//--- Test continuity properties:
|
|
//--- * continuity is guaranteed for original Shepard's method, MSTAB and MSMOOTH
|
|
//--- * modified Shepard method does not guarantee continuity of the model, but
|
|
//--- we can be sure that model is continuous along line connecting two nearest
|
|
//--- points
|
|
for(k=0; k<=1; k++)
|
|
{
|
|
i0=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
|
|
x=xy[i0]+0;
|
|
i1=-1;
|
|
v=CMath::m_maxrealnumber;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vv=0;
|
|
for(j=0; j<nx; j++)
|
|
vv+=CMath::Sqr(x[j]-xy.Get(i,j));
|
|
if(vv<v && vv>0.0)
|
|
{
|
|
i1=i;
|
|
xx=xy[i]+0;
|
|
v=vv;
|
|
}
|
|
}
|
|
if(i1<0)
|
|
{
|
|
i1=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
|
|
xx=xy[i1]+0;
|
|
}
|
|
continuitytesting=1;
|
|
if(algotype==0)
|
|
continuitytesting=0;
|
|
if(algotype==1)
|
|
continuitytesting=-1;
|
|
if(continuitytesting>=0)
|
|
TestContinuity(model.GetInnerObj(),nx,ny,x,xx,10000,continuitytesting,err);
|
|
if(err)
|
|
return;
|
|
}
|
|
//--- Test evaluation at remote points
|
|
x.Resize(nx);
|
|
for(j=0; j<nx; j++)
|
|
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-meany[j])>tol,"testidwunit.ap:424"))
|
|
return;
|
|
}
|
|
//--- Generate random dataset with NONDISTINCT points, test approximation
|
|
//--- properties and error reports.
|
|
for(pass=1; pass<=20; pass++)
|
|
{
|
|
n=2*(1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),10));
|
|
nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
|
|
x.Resize(nx);
|
|
xx.Resize(nx);
|
|
y.Resize(ny);
|
|
meany=vector<double>::Zeros(ny);
|
|
//--- Generate dataset with nondistinct points, each point is repeated;
|
|
//--- compute reference values of the error metrics
|
|
xy.Resize(n,nx+ny);
|
|
refrms=0;
|
|
refavg=0;
|
|
refmax=0;
|
|
refrss=0;
|
|
reftss=0;
|
|
i=0;
|
|
while(i<=n/2-1)
|
|
{
|
|
//--- Generate two copies of the same point
|
|
for(j=0; j<nx+ny; j++)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
|
|
xy.Set(2*i+0,j,v);
|
|
xy.Set(2*i+1,j,v);
|
|
}
|
|
//--- Test distance between newly generated point and other ones.
|
|
//--- Repeat point generation if it is too close to some other point.
|
|
v=CMath::m_maxrealnumber;
|
|
for(i0=0; i0<2*i; i0++)
|
|
{
|
|
vv=0;
|
|
for(j=0; j<nx; j++)
|
|
vv=MathMax(vv,MathAbs(xy.Get(2*i+0,j)-xy.Get(i0,j)));
|
|
v=MathMin(v,vv);
|
|
}
|
|
if(v<mindistinf)
|
|
continue;
|
|
//--- Update meanY
|
|
for(j=0; j<ny; j++)
|
|
meany.Add(j,(xy.Get(2*i,nx+j)+xy.Get(2*i+1,nx+j))/n);
|
|
//--- Apply perturbation to the target value
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
v=MathPow(2,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
xy.Add(2*i,nx+j,v);
|
|
xy.Add(2*i+1,nx+j,- v);
|
|
v=MathAbs(v);
|
|
refrms=refrms+2*v*v;
|
|
refavg=refavg+2*v;
|
|
refmax=MathMax(refmax,v);
|
|
refrss=refrss+2*v*v;
|
|
}
|
|
//--- Next I
|
|
i++;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<ny; j++)
|
|
reftss+=CMath::Sqr(xy.Get(i,nx+j)-meany[j]);
|
|
}
|
|
refrms=MathSqrt(refrms/(n*ny));
|
|
refavg=refavg/(n*ny);
|
|
refr2=1.0-refrss/CApServ::Coalesce(reftss,1);
|
|
//--- Build IDW model
|
|
initdone=false;
|
|
CIDWInt::IDWBuilderCreate(nx,ny,builder);
|
|
if(algotype==0)
|
|
{
|
|
initdone=true;
|
|
shepardp=nx*(1+CHighQualityRand::HQRndUniformR(rs.GetInnerObj()));
|
|
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
|
|
}
|
|
if(algotype==1)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
|
|
}
|
|
if(algotype==2)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(initdone,"TestCommon: unexpected AlgoType"))
|
|
return;
|
|
CIDWInt::IDWBuilderSetPoints(builder,xy,n);
|
|
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
|
|
{
|
|
//--- Fit and store result directly into the variable
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
}
|
|
else
|
|
{
|
|
//--- Fit, store result to temporary, pass through the CSerializer
|
|
CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj());
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj());
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj());
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer);
|
|
//--- Test error metrics
|
|
if(CAp::SetErrorFlag(err,MathAbs(rep.m_rmserror-refrms)>tol,"testidwunit.ap:562") ||
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_avgerror-refavg)>tol,"testidwunit.ap:563") ||
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_maxerror-refmax)>tol,"testidwunit.ap:564") ||
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_r2-refr2)>tol,"testidwunit.ap:565"))
|
|
return;
|
|
//--- Test ability to reproduce mean over non-distinct points
|
|
//--- NOTE: we do not test all evaluation functions, just IDWCalc()
|
|
for(i=0; i<n/2; i++)
|
|
{
|
|
x=xy[2*i]+0;
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-0.5*(xy.Get(2*i+0,nx+j)+xy.Get(2*i+1,nx+j)))>tol,"testidwunit.ap:578"))
|
|
return;
|
|
}
|
|
//--- Test continuity properties:
|
|
//--- * continuity is guaranteed for original Shepard's method, MSTAB and MSMOOTH
|
|
//--- * modified Shepard method does not guarantee continuity of the model, but
|
|
//--- we can be sure that model is continuous along line connecting two nearest
|
|
//--- points
|
|
for(k=0; k<=1; k++)
|
|
{
|
|
i0=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
|
|
x=xy[i0]+0;
|
|
i1=-1;
|
|
v=CMath::m_maxrealnumber;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vv=0;
|
|
for(j=0; j<nx ; j++)
|
|
vv+=CMath::Sqr(x[j]-xy.Get(i,j));
|
|
if(vv<v && vv>0.0)
|
|
{
|
|
i1=i;
|
|
xx=xy[i]+0;
|
|
v=vv;
|
|
}
|
|
}
|
|
if(i1<0)
|
|
{
|
|
i1=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
|
|
xx=xy[i1]+0;
|
|
}
|
|
continuitytesting=1;
|
|
if(algotype==0)
|
|
continuitytesting=0;
|
|
if(algotype==1)
|
|
continuitytesting=-1;
|
|
if(continuitytesting>=0)
|
|
TestContinuity(model.GetInnerObj(),nx,ny,x,xx,10000,continuitytesting,err);
|
|
if(err)
|
|
return;
|
|
}
|
|
}
|
|
//--- Test correct handling of the prior term
|
|
n=10;
|
|
for(pass=1; pass<=20; pass++)
|
|
{
|
|
nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
|
|
x.Resize(nx);
|
|
y.Resize(ny);
|
|
xy.Resize(n,nx+ny);
|
|
meany=vector<double>::Zeros(ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy.Set(i,nx+j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
|
|
meany.Add(j,xy.Get(i,nx+j)/n);
|
|
}
|
|
}
|
|
initdone=false;
|
|
CIDWInt::IDWBuilderCreate(nx,ny,builder);
|
|
if(algotype==0)
|
|
{
|
|
initdone=true;
|
|
shepardp=1+(nx+1)*CHighQualityRand::HQRndUniformR(rs.GetInnerObj());
|
|
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
|
|
}
|
|
if(algotype==1)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
|
|
}
|
|
if(algotype==2)
|
|
{
|
|
initdone=true;
|
|
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(initdone,"TestCommon: unexpected AlgoType (prior test)"))
|
|
return;
|
|
CIDWInt::IDWBuilderSetPoints(builder,xy,n);
|
|
//--- Zero prior (not tested with textbook Shepard method)
|
|
if(algotype!=0)
|
|
{
|
|
CIDWInt::IDWBuilderSetZeroTerm(builder);
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
for(j=0; j<nx; j++)
|
|
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j])>tol,"testidwunit.ap:684"))
|
|
return;
|
|
}
|
|
//--- Mean prior
|
|
CIDWInt::IDWBuilderSetConstTerm(builder);
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
for(j=0; j<nx; j++)
|
|
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-meany[j])>tol,"testidwunit.ap:696"))
|
|
return;
|
|
//--- User-specified prior (not tested with textbook Shepard method)
|
|
if(algotype!=0)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
|
|
CIDWInt::IDWBuilderSetUserTerm(builder,v);
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
for(j=0; j<nx; j++)
|
|
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
|
|
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(CAp::SetErrorFlag(err,MathAbs(y[j]-v)>tol,"testidwunit.ap:710"))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test MSTAB; |
|
|
//| Err is set to True on failure, unchanged otherwise. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestIDWIntUnit::TestMSTAB(bool &err)
|
|
{
|
|
CMatrixDouble xy;
|
|
int i=0;
|
|
int n=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double x0=0;
|
|
double x1=0;
|
|
double rbase=0;
|
|
CIDWModelShell model;
|
|
CIDWBuilder builder;
|
|
CIDWReport rep;
|
|
CHighQualityRandStateShell rs;
|
|
CHighQualityRand::HQRndRandomize(rs.GetInnerObj());
|
|
//--- Basic test #1: nonzero derivative
|
|
//--- * XY = [[-1,-1],[0,0,],[1,1]]
|
|
//--- * RBase>=2
|
|
//--- * derivative at x=0 must be positive and bigger than 0.1
|
|
xy.Resize(3,2);
|
|
for(i=0; i<=2; i++)
|
|
{
|
|
xy.Set(i,0,i-1);
|
|
xy.Set(i,1,i-1);
|
|
}
|
|
CIDWInt::IDWBuilderCreate(1,1,builder);
|
|
rbase=MathPow(2.0,1.0+CHighQualityRand::HQRndUniformR(rs.GetInnerObj()));
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
CIDWInt::IDWBuilderSetPoints(builder,xy,3);
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
v=0.01;
|
|
if(CAp::SetErrorFlag(err,((CIDWInt::IDWCalc1(model.GetInnerObj(),v)-CIDWInt::IDWCalc1(model.GetInnerObj(),-v))/(2*v))<0.1,"testidwunit.ap:758"))
|
|
return;
|
|
//--- Basic test #2: good smoothness
|
|
//--- * 2D task, dataset is composed from 3 parallel lines
|
|
//--- along y=-0.1, y=0, y=+0.1, with outer lines having
|
|
//--- constant zero target value, inner line having constant
|
|
//--- target equal to 1
|
|
//--- * RBase=1 is used
|
|
//--- * we test that function value does not change significantly
|
|
//--- along the line
|
|
n=100;
|
|
xy.Resize(3*n,3);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(3*i,0,(double)i/(double)(n-1));
|
|
xy.Set(3*i,1,-0.1);
|
|
xy.Set(3*i,2,0);
|
|
xy.Set(3*i+1,0,(double)i/(double)(n-1));
|
|
xy.Set(3*i+1,1,0);
|
|
xy.Set(3*i+1,2,1);
|
|
xy.Set(3*i+2,0,(double)i/(double)(n-1));
|
|
xy.Set(3*i+2,1,0.1);
|
|
xy.Set(3*i+2,2,0);
|
|
}
|
|
rbase=1.0;
|
|
CIDWInt::IDWBuilderCreate(2,1,builder);
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
CIDWInt::IDWBuilderSetPoints(builder,xy,3*n);
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
v=0;
|
|
for(i=0; i<=1000; i++)
|
|
{
|
|
v=MathMax(v,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),CHighQualityRand::HQRndUniformR(rs.GetInnerObj()),-0.1)));
|
|
v=MathMax(v,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),CHighQualityRand::HQRndUniformR(rs.GetInnerObj()),0.1)));
|
|
v=MathMax(v,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),CHighQualityRand::HQRndUniformR(rs.GetInnerObj()),0)-1));
|
|
}
|
|
CAp::SetErrorFlag(err,v>0.001,"testidwunit.ap:796");
|
|
//--- Continuity when moving away from the dataset
|
|
xy.Resize(1,2);
|
|
xy.Set(0,0,0);
|
|
xy.Set(0,1,1);
|
|
rbase=1.0;
|
|
CIDWInt::IDWBuilderCreate(1,1,builder);
|
|
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
|
|
CIDWInt::IDWBuilderSetPoints(builder,xy,1);
|
|
CIDWInt::IDWBuilderSetZeroTerm(builder);
|
|
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
|
|
CAp::SetErrorFlag(err,CIDWInt::IDWCalc1(model.GetInnerObj(),100000)!=0.0,"testidwunit.ap:810");
|
|
v=0;
|
|
for(i=0; i<=500; i++)
|
|
{
|
|
x0=1.2*rbase*((double)i/(double)500);
|
|
x1=1.2*rbase*((double)(i+1)/(double)500);
|
|
v=MathMax(v,MathAbs((CIDWInt::IDWCalc1(model.GetInnerObj(),x1)-CIDWInt::IDWCalc1(model.GetInnerObj(),x0))/(x1-x0)));
|
|
}
|
|
vv=0;
|
|
for(i=0; i<=1000; i++)
|
|
{
|
|
x0=1.2*rbase*((double)i/1000.0);
|
|
x1=1.2*rbase*((double)(i+1)/1000.0);
|
|
vv=MathMax(vv,MathAbs((CIDWInt::IDWCalc1(model.GetInnerObj(),x1)-CIDWInt::IDWCalc1(model.GetInnerObj(),x0))/(x1-x0)));
|
|
}
|
|
CAp::SetErrorFlag(err,(vv/v)>1.333,"testidwunit.ap:825");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRatInt |
|
|
//+------------------------------------------------------------------+
|
|
class CTestRatIntUnit
|
|
{
|
|
public:
|
|
static bool TestRatInt(const bool silent);
|
|
|
|
private:
|
|
static void PolDiff2(double &x[],double &cf[],int n,double t,double &p,double &dp,double &d2p);
|
|
static void BRCunSet(CBarycentricInterpolant &b);
|
|
static bool Is1DSolution(const int n,double &y[],double &w[],double c);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRatInt |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRatIntUnit::TestRatInt(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool bcerrors;
|
|
bool nperrors;
|
|
double threshold=0;
|
|
double lipschitztol=0;
|
|
int maxn=0;
|
|
int passcount=0;
|
|
double h=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
int n=0;
|
|
int n2=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int d=0;
|
|
int pass=0;
|
|
double maxerr=0;
|
|
double t=0;
|
|
double a=0;
|
|
double b=0;
|
|
double s=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double d0=0;
|
|
double d1=0;
|
|
double d2=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x2[];
|
|
double y[];
|
|
double y2[];
|
|
double w[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant b1;
|
|
CBarycentricInterpolant b2;
|
|
//--- initialization
|
|
nperrors=false;
|
|
bcerrors=false;
|
|
waserrors=false;
|
|
//--- PassCount number of repeated passes
|
|
//--- Threshold error tolerance
|
|
//--- LipschitzTol Lipschitz constant increase allowed
|
|
//--- when calculating constant on a twice denser grid
|
|
passcount=5;
|
|
maxn=15;
|
|
threshold=1000000*CMath::m_machineepsilon;
|
|
lipschitztol=1.3;
|
|
//--- Basic barycentric functions
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- randomized tests
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- generate weights from polynomial interpolation
|
|
v0=1+0.4*CMath::RandomReal()-0.2;
|
|
v1=2*CMath::RandomReal()-1;
|
|
v2=2*CMath::RandomReal()-1;
|
|
v3=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
x[i]=0;
|
|
else
|
|
x[i]=v0*MathCos(i*M_PI/(n-1));
|
|
y[i]=MathSin(v1*x[i])+MathCos(v2*x[i])+MathExp(v3*x[i]);
|
|
}
|
|
//--- calculation
|
|
for(j=0; j<n; j++)
|
|
{
|
|
w[j]=1;
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- check
|
|
if(k!=j)
|
|
w[j]=w[j]/(x[j]-x[k]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,b1);
|
|
//--- unpack,then pack again and compare
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricUnpack(b1,n2,x2,y2,w2);
|
|
//--- search errors
|
|
bcerrors=bcerrors||n2!=n;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x2,y2,w2,n2,b2);
|
|
t=2*CMath::RandomReal()-1;
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- copy,compare
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
t=2*CMath::RandomReal()-1;
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- test interpolation properties
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- test interpolation at nodes
|
|
bcerrors=bcerrors||MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold*MathAbs(y[i]);
|
|
//--- compare with polynomial interpolation
|
|
t=2*CMath::RandomReal()-1;
|
|
PolDiff2(x,y,n,t,v0,v1,v2);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(CRatInt::BarycentricCalc(b1,t)-v0)>threshold*MathMax(MathAbs(v0),1);
|
|
//--- test continuity between nodes
|
|
//--- calculate Lipschitz constant on two grids -
|
|
//--- dense and even more dense. If Lipschitz constant
|
|
//--- on a denser grid is significantly increased,
|
|
//--- continuity test is failed
|
|
t=3.0;
|
|
k=100;
|
|
s1=0;
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v1=x[i]+(t-x[i])*j/k;
|
|
v2=x[i]+(t-x[i])*(j+1)/k;
|
|
s1=MathMax(s1,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1));
|
|
}
|
|
//--- change values
|
|
k=2*k;
|
|
s2=0;
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v1=x[i]+(t-x[i])*j/k;
|
|
v2=x[i]+(t-x[i])*(j+1)/k;
|
|
s2=MathMax(s2,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1));
|
|
}
|
|
//--- search errors
|
|
bcerrors=bcerrors||(s2>lipschitztol*s1 && s1>threshold*k);
|
|
}
|
|
//--- test differentiation properties
|
|
for(i=0; i<n; i++)
|
|
{
|
|
t=2*CMath::RandomReal()-1;
|
|
PolDiff2(x,y,n,t,v0,v1,v2);
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b1,t,d0,d1);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1);
|
|
bcerrors=bcerrors||MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1);
|
|
//--- change values
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff2(b1,t,d0,d1,d2);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1);
|
|
bcerrors=bcerrors||MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1);
|
|
bcerrors=bcerrors||MathAbs(v2-d2)>MathSqrt(threshold)*MathMax(MathAbs(v2),1);
|
|
}
|
|
//--- test linear translation
|
|
t=2*CMath::RandomReal()-1;
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
CRatInt::BarycentricLinTransX(b2,a,b);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- change values
|
|
a=0;
|
|
b=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
CRatInt::BarycentricLinTransX(b2,a,b);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- change values
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
CRatInt::BarycentricLinTransY(b2,a,b);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(a*CRatInt::BarycentricCalc(b1,t)+b-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(pass=0; pass<=3; pass++)
|
|
{
|
|
//--- Crash-test: small numbers,large numbers
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
h=1;
|
|
//--- check
|
|
if(pass%2==0)
|
|
h=100*CMath::m_minrealnumber;
|
|
//--- check
|
|
if(pass%2==1)
|
|
h=0.01*CMath::m_maxrealnumber;
|
|
//--- change values
|
|
x[0]=0*h;
|
|
x[1]=1*h;
|
|
x[2]=2*h;
|
|
x[3]=3*h;
|
|
y[0]=0*h;
|
|
y[1]=1*h;
|
|
y[2]=2*h;
|
|
y[3]=3*h;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
|
|
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
|
|
w[3]=1/(x[3]-x[2]);
|
|
//--- check
|
|
if(pass/2==0)
|
|
v0=0;
|
|
//--- check
|
|
if(pass/2==1)
|
|
v0=0.6*h;
|
|
//--- function calls
|
|
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
|
|
t=CRatInt::BarycentricCalc(b1,v0);
|
|
//--- change values
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b1,v0,d0,d1);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(t-v0)>threshold*v0;
|
|
bcerrors=bcerrors||MathAbs(d0-v0)>threshold*v0;
|
|
bcerrors=bcerrors||MathAbs(d1-1)>1000*threshold;
|
|
}
|
|
//--- crash test: large abscissas,small argument
|
|
//--- test for errors in D0 is not very strict
|
|
//--- because renormalization used in Diff1()
|
|
//--- destroys part of precision.
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
h=0.01*CMath::m_maxrealnumber;
|
|
x[0]=0*h;
|
|
x[1]=1*h;
|
|
x[2]=2*h;
|
|
x[3]=3*h;
|
|
y[0]=0*h;
|
|
y[1]=1*h;
|
|
y[2]=2*h;
|
|
y[3]=3*h;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
|
|
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
|
|
w[3]=1/(x[3]-x[2]);
|
|
v0=100*CMath::m_minrealnumber;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
|
|
t=CRatInt::BarycentricCalc(b1,v0);
|
|
//--- change values
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b1,v0,d0,d1);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(t)>v0*(1+threshold);
|
|
bcerrors=bcerrors||MathAbs(d0)>v0*(1+threshold);
|
|
bcerrors=bcerrors||MathAbs(d1-1)>1000*threshold;
|
|
//--- crash test: test safe barycentric formula
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
h=2*CMath::m_minrealnumber;
|
|
x[0]=0*h;
|
|
x[1]=1*h;
|
|
x[2]=2*h;
|
|
x[3]=3*h;
|
|
y[0]=0*h;
|
|
y[1]=1*h;
|
|
y[2]=2*h;
|
|
y[3]=3*h;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
|
|
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
|
|
w[3]=1/(x[3]-x[2]);
|
|
v0=CMath::m_minrealnumber;
|
|
//--- function calls
|
|
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
|
|
t=CRatInt::BarycentricCalc(b1,v0);
|
|
//--- search errors
|
|
bcerrors=bcerrors||MathAbs(t-v0)/v0>threshold;
|
|
//--- Testing "No Poles" interpolation
|
|
maxerr=0;
|
|
for(pass=1; pass<=passcount-1; pass++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,1);
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
y[0]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,1,1,b1);
|
|
//--- search errors
|
|
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,2*CMath::RandomReal()-1)-y[0]));
|
|
}
|
|
//--- calculation
|
|
for(n=2; n<=10; n++)
|
|
{
|
|
//--- compare interpolant built by subroutine
|
|
//--- with interpolant built by hands
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(w2,n);
|
|
//--- D=1,non-equidistant nodes
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Initialize X,Y,W
|
|
a=-1-1*CMath::RandomReal();
|
|
b=1+1*CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
x[i]=MathArctan((b-a)*i/(n-1)+a);
|
|
for(i=0; i<n; i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
s=1;
|
|
//--- calculation
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
w[i]=s*(1/(x[i]-x[i-1])+1/(x[i+1]-x[i]));
|
|
s=-s;
|
|
}
|
|
w[n-1]=s/(x[n-1]-x[n-2]);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=x[i];
|
|
x[i]=x[k];
|
|
x[k]=t;
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
t=w[i];
|
|
w[i]=w[k];
|
|
w[k]=t;
|
|
}
|
|
}
|
|
//--- Build and test
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,n,1,b1);
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,b2);
|
|
//--- search errors
|
|
for(i=1; i<=2*n; i++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t)));
|
|
}
|
|
}
|
|
//--- D=0,1,2. Equidistant nodes.
|
|
for(d=0; d<=2; d++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Skip incorrect (N,D) pairs
|
|
if(n<2*d)
|
|
continue;
|
|
//--- Initialize X,Y,W
|
|
a=-1-1*CMath::RandomReal();
|
|
b=1+1*CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
x[i]=(b-a)*i/(n-1)+a;
|
|
for(i=0; i<n; i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
s=1;
|
|
//--- check
|
|
if(d==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
w[i]=s;
|
|
s=-s;
|
|
}
|
|
}
|
|
//--- check
|
|
if(d==1)
|
|
{
|
|
w[0]=-s;
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
w[i]=2*s;
|
|
s=-s;
|
|
}
|
|
w[n-1]=s;
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
//--- calculation
|
|
w[0]=s;
|
|
w[1]=-(3*s);
|
|
for(i=2; i<=n-3; i++)
|
|
{
|
|
w[i]=4*s;
|
|
s=-s;
|
|
}
|
|
w[n-2]=3*s;
|
|
w[n-1]=-s;
|
|
}
|
|
//--- Mix
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=x[i];
|
|
x[i]=x[k];
|
|
x[k]=t;
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
t=w[i];
|
|
w[i]=w[k];
|
|
w[k]=t;
|
|
}
|
|
}
|
|
//--- Build and test
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,n,d,b1);
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,b2);
|
|
//--- search errors
|
|
for(i=1; i<=2*n; i++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(maxerr>threshold)
|
|
nperrors=true;
|
|
//--- report
|
|
waserrors=bcerrors||nperrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RATIONAL INTERPOLATION");
|
|
PrintResult("BASIC BARYCENTRIC FUNCTIONS",!bcerrors);
|
|
PrintResult("FLOATER-HORMANN",!nperrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRatIntUnit::PolDiff2(double &x[],double &cf[],int n,
|
|
double t,double &p,double &dp,
|
|
double &d2p)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int i=0;
|
|
//--- create arrays
|
|
double df[];
|
|
double d2f[];
|
|
double f[];
|
|
//--- copy
|
|
ArrayCopy(f,cf);
|
|
//--- change values
|
|
p=0;
|
|
dp=0;
|
|
d2p=0;
|
|
n=n-1;
|
|
//--- allocation
|
|
ArrayResize(df,n+1);
|
|
ArrayResize(d2f,n+1);
|
|
for(i=0; i<=n; i++)
|
|
{
|
|
d2f[i]=0;
|
|
df[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(m=1; m<=n; m++)
|
|
{
|
|
for(i=0; i<=n-m; i++)
|
|
{
|
|
d2f[i]=((t-x[i+m])*d2f[i]+(x[i]-t)*d2f[i+1]+2*df[i]-2*df[i+1])/(x[i]-x[i+m]);
|
|
df[i]=((t-x[i+m])*df[i]+f[i]+(x[i]-t)*df[i+1]-f[i+1])/(x[i]-x[i+m]);
|
|
f[i]=((t-x[i+m])*f[i]+(x[i]-t)*f[i+1])/(x[i]-x[i+m]);
|
|
}
|
|
}
|
|
//--- change values
|
|
p=f[0];
|
|
dp=df[0];
|
|
d2p=d2f[0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRatIntUnit::BRCunSet(CBarycentricInterpolant &b)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,1);
|
|
ArrayResize(w,1);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=0;
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether constant C is solution of 1D LLS problem |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRatIntUnit::Is1DSolution(const int n,double &y[],
|
|
double &w[],double c)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
double delta=0;
|
|
//--- initialization
|
|
delta=0.001;
|
|
//--- Test result
|
|
s1=0;
|
|
for(i=0; i<n; i++)
|
|
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
|
|
//--- calculation
|
|
s2=0;
|
|
s3=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s2=s2+CMath::Sqr(w[i]*(c+delta-y[i]));
|
|
s3=s3+CMath::Sqr(w[i]*(c-delta-y[i]));
|
|
}
|
|
result=s2>=s1 && s3>=s1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPolInt |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPolIntUnit
|
|
{
|
|
public:
|
|
static bool TestPolInt(const bool silent);
|
|
|
|
private:
|
|
static double InternalPolInt(double &x[],double &cf[],int n,const double t);
|
|
static void BRCunSet(CBarycentricInterpolant &b);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Unit test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestPolIntUnit::TestPolInt(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool interrors;
|
|
double threshold=0;
|
|
double a=0;
|
|
double b=0;
|
|
double t=0;
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double v4=0;
|
|
double pscale=0;
|
|
double poffset=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double c[];
|
|
double x2[];
|
|
double y2[];
|
|
double w2[];
|
|
double xfull[];
|
|
double yfull[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant p;
|
|
CBarycentricInterpolant p1;
|
|
CBarycentricInterpolant p2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
interrors=false;
|
|
maxn=5;
|
|
passcount=20;
|
|
threshold=1.0E8*CMath::m_machineepsilon;
|
|
//--- Test equidistant interpolation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- prepare task:
|
|
//--- * equidistant points
|
|
//--- * random Y
|
|
//--- * T in [A,B] or near (within 10% of its width)
|
|
do
|
|
{
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(a-b)<=0.2);
|
|
//--- change value
|
|
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(a,b,n,x,y);
|
|
//--- test "fast" equidistant interpolation (no barycentric model)
|
|
interrors=interrors||MathAbs(CPolInt::PolynomialCalcEqDist(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "slow" equidistant interpolation (create barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "fast" interpolation (create "fast" barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuildEqDist(a,b,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
}
|
|
}
|
|
//--- Test Chebyshev-1 interpolation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- prepare task:
|
|
//--- * equidistant points
|
|
//--- * random Y
|
|
//--- * T in [A,B] or near (within 10% of its width)
|
|
do
|
|
{
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(a-b)<=0.2);
|
|
//--- change value
|
|
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DCheb1(a,b,n,x,y);
|
|
//--- test "fast" interpolation (no barycentric model)
|
|
interrors=interrors||MathAbs(CPolInt::PolynomialCalcCheb1(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "slow" interpolation (create barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "fast" interpolation (create "fast" barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuildCheb1(a,b,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
}
|
|
}
|
|
//--- Test Chebyshev-2 interpolation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- prepare task:
|
|
//--- * equidistant points
|
|
//--- * random Y
|
|
//--- * T in [A,B] or near (within 10% of its width)
|
|
do
|
|
{
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(a-b)<=0.2);
|
|
//--- change value
|
|
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DCheb2(a,b,n,x,y);
|
|
//--- test "fast" interpolation (no barycentric model)
|
|
interrors=interrors||MathAbs(CPolInt::PolynomialCalcCheb2(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "slow" interpolation (create barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "fast" interpolation (create "fast" barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuildCheb2(a,b,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
}
|
|
}
|
|
//--- Testing conversion Barycentric<->Chebyshev
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(k=1; k<=3; k++)
|
|
{
|
|
//--- Allocate
|
|
ArrayResize(x,k);
|
|
ArrayResize(y,k);
|
|
//--- Generate problem
|
|
a=2*CMath::RandomReal()-1;
|
|
b=a+(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1);
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
v2=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(k==1)
|
|
{
|
|
x[0]=0.5*(a+b);
|
|
y[0]=v0;
|
|
}
|
|
//--- check
|
|
if(k==2)
|
|
{
|
|
x[0]=a;
|
|
y[0]=v0-v1;
|
|
x[1]=b;
|
|
y[1]=v0+v1;
|
|
}
|
|
//--- check
|
|
if(k==3)
|
|
{
|
|
x[0]=a;
|
|
y[0]=v0-v1+v2;
|
|
x[1]=0.5*(a+b);
|
|
y[1]=v0-v2;
|
|
x[2]=b;
|
|
y[2]=v0+v1+v2;
|
|
}
|
|
//--- Test forward conversion
|
|
CPolInt::PolynomialBuild(x,y,k,p);
|
|
ArrayResize(c,1);
|
|
CPolInt::PolynomialBar2Cheb(p,a,b,c);
|
|
//--- search errors
|
|
interrors=interrors||CAp::Len(c)!=k;
|
|
//--- check
|
|
if(k>=1)
|
|
interrors=interrors||MathAbs(c[0]-v0)>threshold;
|
|
//--- check
|
|
if(k>=2)
|
|
interrors=interrors||MathAbs(c[1]-v1)>threshold;
|
|
//--- check
|
|
if(k>=3)
|
|
interrors=interrors||MathAbs(c[2]-v2)>threshold;
|
|
//--- Test backward conversion
|
|
CPolInt::PolynomialCheb2Bar(c,k,a,b,p2);
|
|
v=a+CMath::RandomReal()*(b-a);
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold;
|
|
}
|
|
}
|
|
//--- Testing conversion Barycentric<->Power
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(k=1; k<=5; k++)
|
|
{
|
|
//--- Allocate
|
|
ArrayResize(x,k);
|
|
ArrayResize(y,k);
|
|
//--- Generate problem
|
|
poffset=2*CMath::RandomReal()-1;
|
|
pscale=(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1);
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
v2=2*CMath::RandomReal()-1;
|
|
v3=2*CMath::RandomReal()-1;
|
|
v4=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(k==1)
|
|
{
|
|
x[0]=poffset;
|
|
y[0]=v0;
|
|
}
|
|
//--- check
|
|
if(k==2)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1;
|
|
x[1]=poffset+pscale;
|
|
y[1]=v0+v1;
|
|
}
|
|
//--- check
|
|
if(k==3)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1+v2;
|
|
x[1]=poffset;
|
|
y[1]=v0;
|
|
x[2]=poffset+pscale;
|
|
y[2]=v0+v1+v2;
|
|
}
|
|
//--- check
|
|
if(k==4)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1+v2-v3;
|
|
x[1]=poffset-0.5*pscale;
|
|
y[1]=v0-0.5*v1+0.25*v2-0.125*v3;
|
|
x[2]=poffset+0.5*pscale;
|
|
y[2]=v0+0.5*v1+0.25*v2+0.125*v3;
|
|
x[3]=poffset+pscale;
|
|
y[3]=v0+v1+v2+v3;
|
|
}
|
|
//--- check
|
|
if(k==5)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1+v2-v3+v4;
|
|
x[1]=poffset-0.5*pscale;
|
|
y[1]=v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4;
|
|
x[2]=poffset;
|
|
y[2]=v0;
|
|
x[3]=poffset+0.5*pscale;
|
|
y[3]=v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4;
|
|
x[4]=poffset+pscale;
|
|
y[4]=v0+v1+v2+v3+v4;
|
|
}
|
|
//--- Test forward conversion
|
|
CPolInt::PolynomialBuild(x,y,k,p);
|
|
ArrayResize(c,1);
|
|
CPolInt::PolynomialBar2Pow(p,poffset,pscale,c);
|
|
//--- search errors
|
|
interrors=interrors||CAp::Len(c)!=k;
|
|
//--- check
|
|
if(k>=1)
|
|
interrors=interrors||MathAbs(c[0]-v0)>threshold;
|
|
//--- check
|
|
if(k>=2)
|
|
interrors=interrors||MathAbs(c[1]-v1)>threshold;
|
|
//--- check
|
|
if(k>=3)
|
|
interrors=interrors||MathAbs(c[2]-v2)>threshold;
|
|
//--- check
|
|
if(k>=4)
|
|
interrors=interrors||MathAbs(c[3]-v3)>threshold;
|
|
//--- check
|
|
if(k>=5)
|
|
interrors=interrors||MathAbs(c[4]-v4)>threshold;
|
|
//--- Test backward conversion
|
|
CPolInt::PolynomialPow2Bar(c,k,poffset,pscale,p2);
|
|
v=poffset+(2*CMath::RandomReal()-1)*pscale;
|
|
//--- search errors
|
|
interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold;
|
|
}
|
|
}
|
|
//--- crash-test: ability to solve tasks which will overflow/underflow
|
|
//--- weights with straightforward implementation
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
a=-(0.1*CMath::m_maxrealnumber);
|
|
b=0.1*CMath::m_maxrealnumber;
|
|
CApServ::TaskGenInt1DEquidist(a,b,n,x,y);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
interrors=interrors||p.m_w[i]==0.0;
|
|
}
|
|
//--- report
|
|
waserrors=interrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING POLYNOMIAL INTERPOLATION");
|
|
//--- Normal tests
|
|
PrintResult("INTERPOLATION TEST",!interrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
double CTestPolIntUnit::InternalPolInt(double &x[],double &cf[],
|
|
int n,const double t)
|
|
{
|
|
//--- create array
|
|
double f[];
|
|
//--- copy
|
|
ArrayCopy(f,cf);
|
|
//--- calculation
|
|
n=n-1;
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
for(int i=j+1; i<=n; i++)
|
|
f[i]=((t-x[j])*f[i]-(t-x[i])*f[j])/(x[i]-x[j]);
|
|
}
|
|
//--- return result
|
|
return(f[n]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestPolIntUnit::BRCunSet(CBarycentricInterpolant &b)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,1);
|
|
ArrayResize(w,1);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=0;
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline1D |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpline1DUnit
|
|
{
|
|
public:
|
|
static bool TestSpline1D(const bool silent);
|
|
|
|
private:
|
|
static void LConst(const double a,const double b,CSpline1DInterpolant &c,const double lstep,double &l0,double &l1,double &l2);
|
|
static bool TestUnpack(CSpline1DInterpolant &c,double &x[]);
|
|
static void UnsetSpline1D(CSpline1DInterpolant &c);
|
|
static void Unset1D(double &x[]);
|
|
static bool Is1DSolution(const int n,double &y[],double &w[],const double c);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline1D |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline1DUnit::TestSpline1D(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool crserrors;
|
|
bool cserrors;
|
|
bool hserrors;
|
|
bool aserrors;
|
|
bool lserrors;
|
|
bool dserrors;
|
|
bool uperrors;
|
|
bool cperrors;
|
|
bool lterrors;
|
|
bool ierrors;
|
|
double nonstrictthreshold=0;
|
|
double threshold=0;
|
|
int passcount=0;
|
|
double lstep=0;
|
|
double h=0;
|
|
int maxn=0;
|
|
int bltype=0;
|
|
int brtype=0;
|
|
bool periodiccond;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double a=0;
|
|
double b=0;
|
|
double bl=0;
|
|
double br=0;
|
|
double t=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
int n2=0;
|
|
double v=0;
|
|
double l10=0;
|
|
double l11=0;
|
|
double l12=0;
|
|
double l20=0;
|
|
double l21=0;
|
|
double l22=0;
|
|
double p0=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double s=0;
|
|
double ds=0;
|
|
double d2s=0;
|
|
double s2=0;
|
|
double ds2=0;
|
|
double d2s2=0;
|
|
double vl=0;
|
|
double vm=0;
|
|
double vr=0;
|
|
double err=0;
|
|
double tension=0;
|
|
double intab=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double yp[];
|
|
double w[];
|
|
double w2[];
|
|
double y2[];
|
|
double d[];
|
|
double xc[];
|
|
double yc[];
|
|
double tmp0[];
|
|
double tmp1[];
|
|
double tmp2[];
|
|
double tmpx[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CSpline1DInterpolant c;
|
|
CSpline1DInterpolant c2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
passcount=20;
|
|
lstep=0.005;
|
|
h=0.00001;
|
|
maxn=10;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nonstrictthreshold=0.00001;
|
|
lserrors=false;
|
|
cserrors=false;
|
|
crserrors=false;
|
|
hserrors=false;
|
|
aserrors=false;
|
|
dserrors=false;
|
|
cperrors=false;
|
|
uperrors=false;
|
|
lterrors=false;
|
|
ierrors=false;
|
|
//--- General test: linear,cubic,Hermite,Akima
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(yp,n);
|
|
ArrayResize(d,n);
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Prepare task:
|
|
//--- * X contains abscissas from [A,B]
|
|
//--- * Y contains function values
|
|
//--- * YP contains periodic function values
|
|
a=-1-CMath::RandomReal();
|
|
b=1+CMath::RandomReal();
|
|
bl=2*CMath::RandomReal()-1;
|
|
br=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=0.5*(b+a)+0.5*(b-a)*MathCos(M_PI*(2*i+1)/(2*n));
|
|
//--- check
|
|
if(i==0)
|
|
x[i]=a;
|
|
//--- check
|
|
if(i==n-1)
|
|
x[i]=b;
|
|
//--- change values
|
|
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
|
|
yp[i]=y[i];
|
|
d[i]=-(1.3*M_PI*MathSin(1.3*M_PI*x[i]+0.4));
|
|
}
|
|
yp[n-1]=yp[0];
|
|
//--- swap
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=x[i];
|
|
x[i]=x[k];
|
|
x[k]=t;
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
t=yp[i];
|
|
yp[i]=yp[k];
|
|
yp[k]=t;
|
|
t=d[i];
|
|
d[i]=d[k];
|
|
d[k]=t;
|
|
}
|
|
}
|
|
//--- Build linear spline
|
|
//--- Test for general interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * continuous function
|
|
//--- Test for specific properties is implemented below.
|
|
CSpline1D::Spline1DBuildLinear(x,y,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0; i<n; i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
//--- search errors
|
|
lserrors=lserrors||err>threshold;
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- search errors
|
|
lserrors=lserrors||l20/l10>1.2;
|
|
//--- Build cubic spline.
|
|
//--- Test for interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * boundary conditions
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
//--- * continuous second derivative
|
|
//--- * periodicity properties
|
|
//--- * Spline1DGridDiff(),Spline1DGridDiff2() and Spline1DDiff()
|
|
//--- calls must return same results
|
|
for(bltype=-1; bltype<=2; bltype++)
|
|
{
|
|
for(brtype=-1; brtype<=2; brtype++)
|
|
{
|
|
//--- skip meaningless combination of boundary conditions
|
|
//--- (one condition is periodic,another is not)
|
|
periodiccond=bltype==-1||brtype==-1;
|
|
//--- check
|
|
if(periodiccond && bltype!=brtype)
|
|
continue;
|
|
//--- build
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DBuildCubic(x,yp,n,bltype,bl,brtype,br,c);
|
|
else
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,bltype,bl,brtype,br,c);
|
|
//--- interpolation properties
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
{
|
|
//--- * check values at nodes;spline is periodic so
|
|
//--- we add random number of periods to nodes
|
|
//--- * we also test for periodicity of derivatives
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x[i];
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
t=yp[i]-CSpline1D::Spline1DCalc(c,vm);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
err=MathMax(err,MathAbs(d2s-d2s2));
|
|
}
|
|
//--- periodicity between nodes
|
|
v=a+(b-a)*CMath::RandomReal();
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm)));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
err=MathMax(err,MathAbs(d2s-d2s2));
|
|
}
|
|
else
|
|
{
|
|
//--- * check values at nodes
|
|
for(i=0; i<n; i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors||err>threshold;
|
|
//--- check boundary conditions
|
|
err=0;
|
|
//--- check
|
|
if(bltype==0)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
}
|
|
//--- check
|
|
if(bltype==1)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,a+h)-CSpline1D::Spline1DCalc(c,a-h))/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(bl-t));
|
|
}
|
|
//--- check
|
|
if(bltype==2)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,a+h)-2*CSpline1D::Spline1DCalc(c,a)+CSpline1D::Spline1DCalc(c,a-h))/CMath::Sqr(h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(bl-t));
|
|
}
|
|
//--- check
|
|
if(brtype==0)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
}
|
|
//--- check
|
|
if(brtype==1)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,b+h)-CSpline1D::Spline1DCalc(c,b-h))/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(br-t));
|
|
}
|
|
//--- check
|
|
if(brtype==2)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,b+h)-2*CSpline1D::Spline1DCalc(c,b)+CSpline1D::Spline1DCalc(c,b-h))/CMath::Sqr(h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(br-t));
|
|
}
|
|
//--- check
|
|
if(bltype==-1 || brtype==-1)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
err=MathMax(err,MathAbs(d2s-d2s2));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors||err>1.0E-3;
|
|
//--- Check Lipschitz continuity
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- check
|
|
if(l10>1.0E-6)
|
|
cserrors=cserrors||l20/l10>1.2;
|
|
//--- check
|
|
if(l11>1.0E-6)
|
|
cserrors=cserrors||l21/l11>1.2;
|
|
//--- check
|
|
if(l12>1.0E-6)
|
|
cserrors=cserrors||l22/l12>1.2;
|
|
//--- compare spline1dgriddiff() and spline1ddiff() results
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DGridDiffCubic(x,yp,n,bltype,bl,brtype,br,tmp1);
|
|
else
|
|
CSpline1D::Spline1DGridDiffCubic(x,y,n,bltype,bl,brtype,br,tmp1);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(tmp1)>=n))
|
|
return(false);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
}
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DGridDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmp1,tmp2);
|
|
else
|
|
CSpline1D::Spline1DGridDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmp1,tmp2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
err=MathMax(err,MathAbs(d2s-tmp2[i]));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors||err>threshold;
|
|
//--- compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results
|
|
n2=2+CMath::RandomInteger(2*n);
|
|
ArrayResize(tmpx,n2);
|
|
//--- calculation
|
|
for(i=0; i<n2 ; i++)
|
|
tmpx[i]=0.5*(a+b)+(a-b)*(2*CMath::RandomReal()-1);
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DConvCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0);
|
|
else
|
|
CSpline1D::Spline1DConvCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0);
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-tmp0[i]));
|
|
}
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DConvDiffCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1);
|
|
else
|
|
CSpline1D::Spline1DConvDiffCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1);
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-tmp0[i]));
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
}
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DConvDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2);
|
|
else
|
|
CSpline1D::Spline1DConvDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2);
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-tmp0[i]));
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
err=MathMax(err,MathAbs(d2s-tmp2[i]));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors||err>threshold;
|
|
}
|
|
}
|
|
//--- Build Catmull-Rom spline.
|
|
//--- Test for interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * boundary conditions
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
//--- * periodicity properties
|
|
for(bltype=-1; bltype<=0; bltype++)
|
|
{
|
|
periodiccond=bltype==-1;
|
|
//--- select random tension value,then build
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
tension=0;
|
|
else
|
|
tension=1;
|
|
}
|
|
else
|
|
tension=CMath::RandomReal();
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DBuildCatmullRom(x,yp,n,bltype,tension,c);
|
|
else
|
|
CSpline1D::Spline1DBuildCatmullRom(x,y,n,bltype,tension,c);
|
|
//--- interpolation properties
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
{
|
|
//--- * check values at nodes;spline is periodic so
|
|
//--- we add random number of periods to nodes
|
|
//--- * we also test for periodicity of first derivative
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x[i];
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
t=yp[i]-CSpline1D::Spline1DCalc(c,vm);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
}
|
|
//--- periodicity between nodes
|
|
v=a+(b-a)*CMath::RandomReal();
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm)));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
}
|
|
else
|
|
{
|
|
//--- * check values at nodes
|
|
for(i=0; i<n; i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
}
|
|
//--- search errors
|
|
crserrors=crserrors||err>threshold;
|
|
//--- check boundary conditions
|
|
err=0;
|
|
//--- check
|
|
if(bltype==0)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
}
|
|
//--- check
|
|
if(bltype==-1)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
}
|
|
//--- search errors
|
|
crserrors=crserrors||err>1.0E-3;
|
|
//--- Check Lipschitz continuity
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- check
|
|
if(l10>1.0E-6)
|
|
crserrors=crserrors||l20/l10>1.2;
|
|
//--- check
|
|
if(l11>1.0E-6)
|
|
crserrors=crserrors||l21/l11>1.2;
|
|
}
|
|
//--- Build Hermite spline.
|
|
//--- Test for interpolation scheme properties:
|
|
//--- * values and derivatives at nodes
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0; i<n; i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
//--- search errors
|
|
hserrors=hserrors||err>threshold;
|
|
err=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,x[i]+h)-CSpline1D::Spline1DCalc(c,x[i]-h))/(2*h);
|
|
err=MathMax(err,MathAbs(d[i]-t));
|
|
}
|
|
//--- search errors
|
|
hserrors=hserrors||err>1.0E-3;
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- search errors
|
|
hserrors=hserrors||l20/l10>1.2;
|
|
hserrors=hserrors||l21/l11>1.2;
|
|
//--- Build Akima spline
|
|
//--- Test for general interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
//--- Test for specific properties is implemented below.
|
|
if(n>=5)
|
|
{
|
|
CSpline1D::Spline1DBuildAkima(x,y,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0; i<n; i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
//--- search errors
|
|
aserrors=aserrors||err>threshold;
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- search errors
|
|
hserrors=hserrors||l20/l10>1.2;
|
|
hserrors=hserrors||l21/l11>1.2;
|
|
}
|
|
}
|
|
}
|
|
//--- Special linear spline test:
|
|
//--- test for linearity between x[i] and x[i+1]
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
//--- Prepare task
|
|
a=-1;
|
|
b=1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildLinear(x,y,n,c);
|
|
//--- Test
|
|
err=0;
|
|
for(k=0; k<=n-2; k++)
|
|
{
|
|
a=x[k];
|
|
b=x[k+1];
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
v=y[k]+(t-a)/(b-a)*(y[k+1]-y[k]);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-v));
|
|
}
|
|
}
|
|
//--- search errors
|
|
lserrors=lserrors||err>threshold;
|
|
}
|
|
//--- Special Akima test: test outlier sensitivity
|
|
//--- Spline value at (x[i],x[i+1]) should depend from
|
|
//--- f[i-2],f[i-1],f[i],f[i+1],f[i+2],f[i+3] only.
|
|
for(n=5; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(y2,n);
|
|
//--- Prepare unperturbed Akima spline
|
|
a=-1;
|
|
b=1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(x,y,n,c);
|
|
//--- Process perturbed tasks
|
|
err=0;
|
|
for(k=0; k<n; k++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
y2[i_]=y[i_];
|
|
y2[k]=5;
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(x,y2,n,c2);
|
|
//--- Test left part independence
|
|
if(k-3>=1)
|
|
{
|
|
a=-1;
|
|
b=x[k-3];
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
}
|
|
//--- Test right part independence
|
|
if(k+3<=n-2)
|
|
{
|
|
a=x[k+3];
|
|
b=1;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
aserrors=aserrors||err>threshold;
|
|
}
|
|
//--- Differentiation,copy/unpack test
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
//--- Prepare cubic spline
|
|
a=-1-CMath::RandomReal();
|
|
b=1+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,2,0.0,2,0.0,c);
|
|
//--- Test diff
|
|
err=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,t,s,ds,d2s);
|
|
vl=CSpline1D::Spline1DCalc(c,t-h);
|
|
vm=CSpline1D::Spline1DCalc(c,t);
|
|
vr=CSpline1D::Spline1DCalc(c,t+h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-vm));
|
|
err=MathMax(err,MathAbs(ds-(vr-vl)/(2*h)));
|
|
err=MathMax(err,MathAbs(d2s-(vr-2*vm+vl)/CMath::Sqr(h)));
|
|
}
|
|
//--- search errors
|
|
dserrors=dserrors||err>0.001;
|
|
//--- Test copy
|
|
UnsetSpline1D(c2);
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
//--- search errors
|
|
err=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
//--- search errors
|
|
cperrors=cperrors||err>threshold;
|
|
//--- Test unpack
|
|
uperrors=uperrors||!TestUnpack(c,x);
|
|
//--- Test lin.trans.
|
|
err=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- LinTransX,general A
|
|
sa=4*CMath::RandomReal()-2;
|
|
sb=2*CMath::RandomReal()-1;
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
CSpline1D::Spline1DLinTransX(c2,sa,sb);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,(t-sb)/sa)));
|
|
//--- LinTransX,special case: A=0
|
|
sb=2*CMath::RandomReal()-1;
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
CSpline1D::Spline1DLinTransX(c2,0,sb);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,sb)-CSpline1D::Spline1DCalc(c2,t)));
|
|
//--- LinTransY
|
|
sa=2*CMath::RandomReal()-1;
|
|
sb=2*CMath::RandomReal()-1;
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
CSpline1D::Spline1DLinTransY(c2,sa,sb);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(sa*CSpline1D::Spline1DCalc(c,t)+sb-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
//--- search errors
|
|
lterrors=lterrors||err>threshold;
|
|
}
|
|
//--- Testing integration.
|
|
//--- Three tests are performed:
|
|
//--- * approximate test (well behaved smooth function,many points,
|
|
//--- integration inside [a,b]),non-periodic spline
|
|
//--- * exact test (integration of parabola,outside of [a,b],non-periodic spline
|
|
//--- * approximate test for periodic splines. F(x)=cos(2*pi*x)+1.
|
|
//--- Period length is equals to 1.0,so all operations with
|
|
//--- multiples of period are done exactly. For each value of PERIOD
|
|
//--- we calculate and test integral at four points:
|
|
//--- - 0 < t0 < PERIOD
|
|
//--- - t1=PERIOD-eps
|
|
//--- - t2=PERIOD
|
|
//--- - t3=PERIOD+eps
|
|
err=0;
|
|
for(n=20; n<=35; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Prepare cubic spline
|
|
a=-1-0.2*CMath::RandomReal();
|
|
b=1+0.2*CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=MathSin(M_PI*x[i]+0.4)+MathExp(x[i]);
|
|
}
|
|
//--- change values
|
|
bl=M_PI*MathCos(M_PI*a+0.4)+MathExp(a);
|
|
br=M_PI*MathCos(M_PI*b+0.4)+MathExp(b);
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,1,bl,1,br,c);
|
|
//--- Test
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
v=-(MathCos(M_PI*a+0.4)/M_PI)+MathExp(a);
|
|
v=-(MathCos(M_PI*t+0.4)/M_PI)+MathExp(t)-v;
|
|
v=v-CSpline1D::Spline1DIntegrate(c,t);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v));
|
|
}
|
|
}
|
|
//--- search errors
|
|
ierrors=ierrors||err>0.001;
|
|
p0=2*CMath::RandomReal()-1;
|
|
p1=2*CMath::RandomReal()-1;
|
|
p2=2*CMath::RandomReal()-1;
|
|
a=-CMath::RandomReal()-0.5;
|
|
b=CMath::RandomReal()+0.5;
|
|
n=2;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(d,n);
|
|
x[0]=a;
|
|
y[0]=p0+p1*a+p2*CMath::Sqr(a);
|
|
d[0]=p1+2*p2*a;
|
|
x[1]=b;
|
|
y[1]=p0+p1*b+p2*CMath::Sqr(b);
|
|
d[1]=p1+2*p2*b;
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,n,c);
|
|
bl=MathMin(a,b)-MathAbs(b-a);
|
|
br=MathMin(a,b)+MathAbs(b-a);
|
|
err=0;
|
|
//--- calculation
|
|
for(pass=1; pass<=100; pass++)
|
|
{
|
|
t=bl+(br-bl)*CMath::RandomReal();
|
|
v=p0*t+p1*CMath::Sqr(t)/2+p2*CMath::Sqr(t)*t/3-(p0*a+p1*CMath::Sqr(a)/2+p2*CMath::Sqr(a)*a/3);
|
|
v=v-CSpline1D::Spline1DIntegrate(c,t);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v));
|
|
}
|
|
//--- search errors
|
|
ierrors=ierrors||err>threshold;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=MathCos(2*M_PI*x[i])+1;
|
|
}
|
|
//--- change values
|
|
y[0]=2;
|
|
y[n-1]=2;
|
|
//--- function calls
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,-1,0.0,-1,0.0,c);
|
|
intab=CSpline1D::Spline1DIntegrate(c,1.0);
|
|
v=CMath::RandomReal();
|
|
vr=CSpline1D::Spline1DIntegrate(c,v);
|
|
//--- search errors
|
|
ierrors=ierrors||MathAbs(intab-1)>0.001;
|
|
for(i=-10; i<=10; i++)
|
|
{
|
|
ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i+v)-(i*intab+vr))>0.001;
|
|
ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i-1000*CMath::m_machineepsilon)-i*intab)>0.001;
|
|
ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i)-i*intab)>0.001;
|
|
ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i+1000*CMath::m_machineepsilon)-i*intab)>0.001;
|
|
}
|
|
//--- report
|
|
waserrors=((((((((lserrors||cserrors)||crserrors)||hserrors)||aserrors)||dserrors)||cperrors)||uperrors)||lterrors)||ierrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SPLINE INTERPOLATION");
|
|
//--- Normal tests
|
|
PrintResult("LINEAR SPLINE TEST",!lserrors);
|
|
PrintResult("CUBIC SPLINE TEST",!cserrors);
|
|
PrintResult("CATMULL-ROM SPLINE TEST",!crserrors);
|
|
PrintResult("HERMITE SPLINE TEST",!hserrors);
|
|
PrintResult("AKIMA SPLINE TEST",!aserrors);
|
|
PrintResult("DIFFERENTIATION TEST",!dserrors);
|
|
PrintResult("COPY/SERIALIZATION TEST",!cperrors);
|
|
PrintResult("UNPACK TEST",!uperrors);
|
|
PrintResult("LIN.TRANS. TEST",!lterrors);
|
|
PrintResult("INTEGRATION TEST",!ierrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lipschitz constants for spline inself,first and second |
|
|
//| derivatives. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline1DUnit::LConst(const double a,const double b,
|
|
CSpline1DInterpolant &c,
|
|
const double lstep,double &l0,
|
|
double &l1,double &l2)
|
|
{
|
|
//--- create variables
|
|
double t=0;
|
|
double vl=0;
|
|
double vm=0;
|
|
double vr=0;
|
|
double prevf=0;
|
|
double prevd=0;
|
|
double prevd2=0;
|
|
double f=0;
|
|
double d=0;
|
|
double d2=0;
|
|
//--- change values
|
|
l0=0;
|
|
l1=0;
|
|
l2=0;
|
|
t=a-0.1;
|
|
vl=CSpline1D::Spline1DCalc(c,t-2*lstep);
|
|
vm=CSpline1D::Spline1DCalc(c,t-lstep);
|
|
vr=CSpline1D::Spline1DCalc(c,t);
|
|
f=vm;
|
|
d=(vr-vl)/(2*lstep);
|
|
d2=(vr-2*vm+vl)/CMath::Sqr(lstep);
|
|
//--- calculation
|
|
while(t<=b+0.1)
|
|
{
|
|
//--- change values
|
|
prevf=f;
|
|
prevd=d;
|
|
prevd2=d2;
|
|
vl=vm;
|
|
vm=vr;
|
|
vr=CSpline1D::Spline1DCalc(c,t+lstep);
|
|
f=vm;
|
|
d=(vr-vl)/(2*lstep);
|
|
d2=(vr-2*vm+vl)/CMath::Sqr(lstep);
|
|
l0=MathMax(l0,MathAbs((f-prevf)/lstep));
|
|
l1=MathMax(l1,MathAbs((d-prevd)/lstep));
|
|
l2=MathMax(l2,MathAbs((d2-prevd2)/lstep));
|
|
t=t+lstep;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpack testing |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline1DUnit::TestUnpack(CSpline1DInterpolant &c,double &x[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int n=0;
|
|
double err=0;
|
|
double t=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create matrix
|
|
CMatrixDouble tbl;
|
|
//--- initialization
|
|
passcount=20;
|
|
err=0;
|
|
//--- function call
|
|
CSpline1D::Spline1DUnpack(c,n,tbl);
|
|
//--- calculation
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
t=CMath::RandomReal()*(tbl[i][1]-tbl[i][0]);
|
|
v1=tbl[i][2]+t*tbl[i][3]+CMath::Sqr(t)*tbl[i][4]+t*CMath::Sqr(t)*tbl[i][5];
|
|
v2=CSpline1D::Spline1DCalc(c,tbl[i][0]+t);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0; i<=n-2; i++)
|
|
err=MathMax(err,MathAbs(x[i]-tbl[i][0]));
|
|
//--- search errors
|
|
for(i=0; i<=n-2; i++)
|
|
err=MathMax(err,MathAbs(x[i+1]-tbl[i][1]));
|
|
//--- get result
|
|
result=err<100*CMath::m_machineepsilon;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline,i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline1DUnit::UnsetSpline1D(CSpline1DInterpolant &c)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double d[];
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
ArrayResize(d,2);
|
|
//--- change values
|
|
x[0]=-1;
|
|
y[0]=CMath::RandomReal();
|
|
d[0]=CMath::RandomReal();
|
|
x[1]=1;
|
|
y[1]=CMath::RandomReal();
|
|
d[1]=CMath::RandomReal();
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,2,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline1DUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether constant C is solution of 1D LLS problem |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline1DUnit::Is1DSolution(const int n,double &y[],
|
|
double &w[],const double c)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
double delta=0;
|
|
//--- initialization
|
|
delta=0.001;
|
|
//--- Test result
|
|
s1=0;
|
|
for(i=0; i<n; i++)
|
|
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
|
|
s2=0;
|
|
s3=0;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
s2=s2+CMath::Sqr(w[i]*(c+delta-y[i]));
|
|
s3=s3+CMath::Sqr(w[i]*(c-delta-y[i]));
|
|
result=s2>=s1 && s3>=s1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLM |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinLMUnit
|
|
{
|
|
public:
|
|
static bool TestMinLM(const bool silent);
|
|
|
|
private:
|
|
static void TestU(bool &errorflag,bool &statefieldsconsistencyflag);
|
|
static void TestBC(bool &errorflag);
|
|
static void TestLC(bool &errorflag);
|
|
static void TestOther(bool &errorflag,bool &statefieldsconsistencyflag);
|
|
static void TestOptGuard(bool &waserrors);
|
|
static bool RKindVSStateCheck(int rkind,CMinLMState &state);
|
|
static void AXMB(CMinLMState &state,CMatrixDouble &a,CRowDouble &b,int n);
|
|
static void TryReproduceFixedBugs(bool &err);
|
|
static void TestFunc1(int n,int m,CMatrixDouble &c,CRowDouble &x,double &f,bool needf,CRowDouble &fi,bool needfi,CMatrixDouble &jac,bool needjac);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLM |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinLMUnit::TestMinLM(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
bool waserrors=false;
|
|
bool uerrors=false;
|
|
bool bcerrors=false;
|
|
bool lcerrors=false;
|
|
bool scerror=false;
|
|
bool othererrors=false;
|
|
bool optguarderr=false;
|
|
//--- Various tests
|
|
TestOther(othererrors,scerror);
|
|
//--- Tests sorted by constraint types
|
|
TestU(uerrors,scerror);
|
|
TestBC(bcerrors);
|
|
TestLC(lcerrors);
|
|
//--- Try to reproduce previously fixed bugs
|
|
TryReproduceFixedBugs(othererrors);
|
|
//--- Test for MinLMGradientCheck
|
|
optguarderr=false;
|
|
TestOptGuard(optguarderr);
|
|
//--- end
|
|
waserrors=(uerrors||bcerrors||lcerrors||scerror||othererrors||optguarderr);
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LEVENBERG-MARQUARDT OPTIMIZATION");
|
|
Print("PROBLEM TYPES:");
|
|
PrintResult("* UNCONSTRAINED",!uerrors);
|
|
PrintResult("* BOX CONSTRAINED",!bcerrors);
|
|
PrintResult("* LINEARLY CONSTRAINED",!lcerrors);
|
|
PrintResult("STATE FIELDS CONSISTENCY",!scerror);
|
|
PrintResult("OTHER PROPERTIES",!othererrors);
|
|
PrintResult("OPTGUARD",!optguarderr);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Test for unconstrained problems. |
|
|
//| On failure sets error flag, leaves it unchanged on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TestU(bool &errorflag,
|
|
bool &statefieldsconsistencyflag)
|
|
{
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
CRowDouble x;
|
|
CRowDouble xe;
|
|
CRowDouble b;
|
|
double h=0;
|
|
int rkind=0;
|
|
CMatrixDouble a;
|
|
double v=0;
|
|
double s=0;
|
|
double eps=0;
|
|
double epsx=0;
|
|
int maxits=0;
|
|
int ckind=0;
|
|
CMinLMState state;
|
|
CMinLMReport rep;
|
|
int i_=0;
|
|
//--- Reference problem.
|
|
//--- See comments for RKindVsStateCheck() for more info about RKind.
|
|
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
|
|
//--- which use approximate finite difference Jacobian.
|
|
x=vector<double>::Zeros(3);
|
|
n=3;
|
|
m=3;
|
|
h=0.0001;
|
|
for(rkind=-2; rkind<=5; rkind++)
|
|
{
|
|
x.Set(0,100*CMath::RandomReal()-50);
|
|
x.Set(1,100*CMath::RandomReal()-50);
|
|
x.Set(2,100*CMath::RandomReal()-50);
|
|
switch(rkind)
|
|
{
|
|
case -2:
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case -1:
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 0:
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
break;
|
|
case 1:
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
break;
|
|
case 2:
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
break;
|
|
case 3:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 4:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case 5:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
break;
|
|
}
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- (x-2)^2 + y^2 + (z-x)^2
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,state.m_x[0]-2);
|
|
state.m_fi.Set(1,state.m_x[1]);
|
|
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,state.m_x[0]-2);
|
|
state.m_fi.Set(1,state.m_x[1]);
|
|
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
|
|
state.m_j.Set(0,0,1);
|
|
state.m_j.Set(0,1,0);
|
|
state.m_j.Set(0,2,0);
|
|
state.m_j.Set(1,0,0);
|
|
state.m_j.Set(1,1,1);
|
|
state.m_j.Set(1,2,0);
|
|
state.m_j.Set(2,0,-1);
|
|
state.m_j.Set(2,1,0);
|
|
state.m_j.Set(2,2,1);
|
|
}
|
|
if(state.m_needf || state.m_needfg || state.m_needfgh)
|
|
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
state.m_g.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h.Set(0,0,4);
|
|
state.m_h.Set(0,1,0);
|
|
state.m_h.Set(0,2,-2);
|
|
state.m_h.Set(1,0,0);
|
|
state.m_h.Set(1,1,2);
|
|
state.m_h.Set(1,2,0);
|
|
state.m_h.Set(2,0,-2);
|
|
state.m_h.Set(2,1,0);
|
|
state.m_h.Set(2,2,2);
|
|
}
|
|
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:212");
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:215");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[0]-2)>0.001,"testminlmunit.ap:216");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[1])>0.001,"testminlmunit.ap:217");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[2]-2)>0.001,"testminlmunit.ap:218");
|
|
}
|
|
//--- 1D problem #1
|
|
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
|
|
//--- which use approximate finite difference Jacobian.
|
|
for(rkind=-2; rkind<=5; rkind++)
|
|
{
|
|
x.Resize(1);
|
|
n=1;
|
|
m=1;
|
|
h=0.00001;
|
|
x.Set(0,100*CMath::RandomReal()-50);
|
|
switch(rkind)
|
|
{
|
|
case -2:
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case -1:
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 0:
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
break;
|
|
case 1:
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
break;
|
|
case 2:
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
break;
|
|
case 3:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 4:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case 5:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
break;
|
|
}
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
state.m_fi.Set(0,MathSin(state.m_x[0]));
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,MathSin(state.m_x[0]));
|
|
state.m_j.Set(0,0,MathCos(state.m_x[0]));
|
|
}
|
|
if(state.m_needf || state.m_needfg || state.m_needfgh)
|
|
state.m_f=CMath::Sqr(MathSin(state.m_x[0]));
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g.Set(0,2*MathSin(state.m_x[0])*MathCos(state.m_x[0]));
|
|
if(state.m_needfgh)
|
|
state.m_h.Set(0,0,2*(MathCos(state.m_x[0])*MathCos(state.m_x[0])-MathSin(state.m_x[0])*MathSin(state.m_x[0])));
|
|
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:281");
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:284");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001,"testminlmunit.ap:285");
|
|
}
|
|
//--- Linear equations: test normal optimization and optimization with restarts
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Prepare task
|
|
h=0.00001;
|
|
CMatGen::RMatrixRndCond(n,100,a);
|
|
x.Resize(n);
|
|
xe.Resize(n);
|
|
b.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
xe.Set(i,2*CMath::RandomReal()-1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xe,a,i);
|
|
b.Set(i,v);
|
|
}
|
|
//--- Test different RKind
|
|
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
|
|
//--- which use approximate finite difference Jacobian.
|
|
for(rkind=-2; rkind<=5; rkind++)
|
|
{
|
|
//--- Solve task (first attempt)
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
switch(rkind)
|
|
{
|
|
case -2:
|
|
CMinLM::MinLMCreateV(n,n,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case -1:
|
|
CMinLM::MinLMCreateV(n,n,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 0:
|
|
CMinLM::MinLMCreateFJ(n,n,x,state);
|
|
break;
|
|
case 1:
|
|
CMinLM::MinLMCreateFGJ(n,n,x,state);
|
|
break;
|
|
case 2:
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
break;
|
|
case 3:
|
|
CMinLM::MinLMCreateVJ(n,n,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 4:
|
|
CMinLM::MinLMCreateVJ(n,n,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case 5:
|
|
CMinLM::MinLMCreateVJ(n,n,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
break;
|
|
}
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
AXMB(state,a,b,n);
|
|
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:357");
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:360");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-xe[i])>0.001,"testminlmunit.ap:364");
|
|
//--- Now we try to restart algorithm from new point
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
CMinLM::MinLMRestartFrom(state,x);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
AXMB(state,a,b,n);
|
|
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:375");
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:378");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-xe[i])>0.001,"testminlmunit.ap:382");
|
|
}
|
|
}
|
|
//--- Testing convergence properties using
|
|
//--- different optimizer types and different conditions.
|
|
//--- Only limited subset of optimizers is tested because some
|
|
//--- optimizers converge too quickly.
|
|
s=100;
|
|
for(rkind=0; rkind<=5; rkind++)
|
|
{
|
|
//--- Skip FGH optimizer - it converges too quickly
|
|
if(rkind==2)
|
|
continue;
|
|
//--- Test
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
eps=0;
|
|
epsx=0;
|
|
maxits=0;
|
|
if(ckind==0)
|
|
{
|
|
epsx=1.0E-6;
|
|
eps=1.0E-4;
|
|
}
|
|
if(ckind==1)
|
|
{
|
|
maxits=2;
|
|
eps=0.05;
|
|
}
|
|
x.Resize(3);
|
|
n=3;
|
|
m=3;
|
|
for(i=0; i<=2; i++)
|
|
x.Set(i,6);
|
|
if(!CAp::Assert(rkind!=2))
|
|
return;
|
|
switch(rkind)
|
|
{
|
|
case 0:
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
break;
|
|
case 1:
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
break;
|
|
case 3:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 4:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case 5:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
break;
|
|
}
|
|
CMinLM::MinLMSetCond(state,epsx,maxits);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi || state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,s*(MathExp(state.m_x[0])-2));
|
|
state.m_fi.Set(1,CMath::Sqr(state.m_x[1])+1);
|
|
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_j.Set(0,0,s*MathExp(state.m_x[0]));
|
|
state.m_j.Set(0,1,0);
|
|
state.m_j.Set(0,2,0);
|
|
state.m_j.Set(1,0,0);
|
|
state.m_j.Set(1,1,2*state.m_x[1]);
|
|
state.m_j.Set(1,2,0);
|
|
state.m_j.Set(2,0,-1);
|
|
state.m_j.Set(2,1,0);
|
|
state.m_j.Set(2,2,1);
|
|
}
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
{
|
|
state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+1)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
state.m_g.Set(0,s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*(CMath::Sqr(state.m_x[1])+1)*2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h.Set(0,0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
|
|
state.m_h.Set(0,1,0);
|
|
state.m_h.Set(0,2,-2);
|
|
state.m_h.Set(1,0,0);
|
|
state.m_h.Set(1,1,12*CMath::Sqr(state.m_x[1])+4);
|
|
state.m_h.Set(1,2,0);
|
|
state.m_h.Set(2,0,-2);
|
|
state.m_h.Set(2,1,0);
|
|
state.m_h.Set(2,2,2);
|
|
}
|
|
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:486");
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:489");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
if(ckind==0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[0]-MathLog(2))>eps,"testminlmunit.ap:494");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[1])>eps,"testminlmunit.ap:495");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[2]-MathLog(2))>eps,"testminlmunit.ap:496");
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=2,"testminlmunit.ap:497");
|
|
}
|
|
if(ckind==1)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=5,"testminlmunit.ap:501");
|
|
CAp::SetErrorFlag(errorflag,rep.m_iterationscount!=maxits,"testminlmunit.ap:502");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for box constrained problems. |
|
|
//| On failure sets error flag, leaves it unchanged on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TestBC(bool &errorflag)
|
|
{
|
|
CMinLMState state;
|
|
CMinLMReport rep;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
int n=0;
|
|
int m=0;
|
|
CRowDouble x0;
|
|
CRowDouble x;
|
|
CRowDouble xe;
|
|
CRowDouble x1;
|
|
CRowDouble d;
|
|
CMatrixDouble c;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double h=0;
|
|
int tmpkind=0;
|
|
CHighQualityRandState rs;
|
|
double epsx=0;
|
|
double tolf=0;
|
|
double f0=0;
|
|
double f1=0;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Reference box constrained problem:
|
|
//--- min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1
|
|
//--- NOTES:
|
|
//--- 1. we test only two optimization modes - V and FGH,
|
|
//--- because from algorithm internals we can assume that actual
|
|
//--- mode being used doesn't matter for bound constrained optimization
|
|
//--- process.
|
|
for(tmpkind=0; tmpkind<=1; tmpkind++)
|
|
{
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
bl.Resize(n);
|
|
bu.Resize(n);
|
|
xe.Resize(n);
|
|
x.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,0);
|
|
bu.Set(i,1);
|
|
xe.Set(i,3*CMath::RandomReal()-1);
|
|
x.Set(i,CMath::RandomReal());
|
|
}
|
|
switch(tmpkind)
|
|
{
|
|
case 0:
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
break;
|
|
case 1:
|
|
CMinLM::MinLMCreateV(n,n,x,1.0E-5,state);
|
|
break;
|
|
}
|
|
CMinLM::MinLMSetCond(state,1.0E-6,0);
|
|
CMinLM::MinLMSetBC(state,bl,bu);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
state.m_fi=MathPow(state.m_x.ToVector()-xe.ToVector(),2.0);
|
|
if(state.m_needf || state.m_needfg || state.m_needfgh)
|
|
state.m_f=MathPow(state.m_x.ToVector()-xe.ToVector(),4.0).Sum();
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g=MathPow(state.m_x.ToVector()-xe.ToVector(),3.0)*4.0;
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h=matrix<double>::Zeros(n,n);
|
|
state.m_h.Diag(MathPow(state.m_x.ToVector()-xe.ToVector(),2.0)*12);
|
|
}
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-CApServ::BoundVal(xe[i],bl[i],bu[i]))>(double)(5.0E-2),"testminlmunit.ap:594");
|
|
}
|
|
else
|
|
CAp::SetErrorFlag(errorflag,true,"testminlmunit.ap:597");
|
|
}
|
|
}
|
|
//--- Minimize
|
|
//--- [ [ ]2 ]
|
|
//--- SUM_i[ SUM_j[ power(x_j,3)*c_ij ] ]
|
|
//--- [ [ ] ]
|
|
//--- subject to non-negativity constraints on x_j
|
|
epsx=1.0E-9;
|
|
tolf=1.0E-10;
|
|
for(tmpkind=0; tmpkind<=1; tmpkind++)
|
|
{
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
m=n+CHighQualityRand::HQRndUniformI(rs,n);
|
|
bl.Resize(n);
|
|
bu.Resize(n);
|
|
x0.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,0);
|
|
bu.Set(i,AL_POSINF);
|
|
x0.Set(i,1+CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
c.Resize(m,n+1);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
if(tmpkind==0)
|
|
CMinLM::MinLMCreateV(n,m,x0,10*epsx,state);
|
|
if(tmpkind==1)
|
|
CMinLM::MinLMCreateVJ(n,m,x0,state);
|
|
CMinLM::MinLMSetCond(state,epsx,0);
|
|
CMinLM::MinLMSetBC(state,bl,bu);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]<bl[i],"testminlmunit.ap:639");
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]>bu[i],"testminlmunit.ap:640");
|
|
}
|
|
if(state.m_needfi)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,"MinLM test: integrity check failed"))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:655");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
|
|
x1.Resize(n);
|
|
h=0.001;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,x[i]<bl[i],"testminlmunit.ap:664");
|
|
CAp::SetErrorFlag(errorflag,x[i]>bu[i],"testminlmunit.ap:665");
|
|
if((x[i]+h)>=bl[i])
|
|
{
|
|
x1=x;
|
|
x1.Set(i,x[i]+h);
|
|
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
|
|
CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:674");
|
|
}
|
|
if((x[i]-h)>=bl[i])
|
|
{
|
|
x1=x;
|
|
x1.Set(i,x[i]-h);
|
|
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
|
|
CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:684");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Minimize
|
|
//--- [ [ ]2 ]
|
|
//--- SUM_i[ SUM_j[ power(x_j,3)*c_ij ] ]
|
|
//--- [ [ ] ]
|
|
//--- subject to random box constraints on x_j
|
|
epsx=1.0E-9;
|
|
tolf=1.0E-10;
|
|
for(tmpkind=0; tmpkind<=1; tmpkind++)
|
|
{
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
m=n+CHighQualityRand::HQRndUniformI(rs,n);
|
|
bl.Resize(n);
|
|
bu.Resize(n);
|
|
x0.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bu.Set(i,bl[i]+CHighQualityRand::HQRndUniformR(rs));
|
|
x0.Set(i,1+CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
c.Resize(m,n+1);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
if(tmpkind==0)
|
|
CMinLM::MinLMCreateV(n,m,x0,10*epsx,state);
|
|
if(tmpkind==1)
|
|
CMinLM::MinLMCreateVJ(n,m,x0,state);
|
|
CMinLM::MinLMSetCond(state,epsx,0);
|
|
CMinLM::MinLMSetBC(state,bl,bu);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]<bl[i],"testminlmunit.ap:728");
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]>bu[i],"testminlmunit.ap:729");
|
|
}
|
|
if(state.m_needfi)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,"minlm test: integrity check failed"))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:744");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
|
|
x1.Resize(n);
|
|
h=0.001;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,x[i]<bl[i],"testminlmunit.ap:753");
|
|
CAp::SetErrorFlag(errorflag,x[i]>bu[i],"testminlmunit.ap:754");
|
|
if((x[i]+h)>=bl[i] && (x[i]+h)<=bu[i])
|
|
{
|
|
x1=x;
|
|
x1.Set(i,x[i]+h);
|
|
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
|
|
CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:763");
|
|
}
|
|
if((x[i]-h)>=bl[i] && (x[i]-h)<=bu[i])
|
|
{
|
|
x1=x;
|
|
x1.Set(i,x[i]-h);
|
|
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
|
|
CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:773");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for linearly constrained problems. |
|
|
//| On failure sets error flag, leaves it unchanged on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TestLC(bool &errorflag)
|
|
{
|
|
CMinLMState state;
|
|
CMinLMReport rep;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
int n=0;
|
|
int m=0;
|
|
int m1=0;
|
|
int m2=0;
|
|
CRowDouble x0;
|
|
CRowDouble x;
|
|
CRowDouble xe;
|
|
CRowDouble x1;
|
|
CRowDouble x12;
|
|
CRowDouble d;
|
|
CMatrixDouble rawc;
|
|
CRowInt rawct;
|
|
int rawccnt=0;
|
|
CMatrixDouble c;
|
|
CMatrixDouble c12;
|
|
CMatrixDouble z;
|
|
int trialidx=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double h=0;
|
|
int optkind=0;
|
|
CHighQualityRandState rs;
|
|
double epsx=0;
|
|
double xtol=0;
|
|
double tolf=0;
|
|
double f0=0;
|
|
double f1=0;
|
|
bool bflag=false;
|
|
int i_=0;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Minimize
|
|
//--- [ [ ]2 ]
|
|
//--- SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ] ]
|
|
//--- [ [ ] ]
|
|
//--- subject to mix of box and linear inequality constraints on x_j
|
|
//--- We check correctness of solution by sampling a few random points
|
|
//--- around one returned by optimizer, and comparing function value
|
|
//--- with target. Sampling is performed with respect to inequality
|
|
//--- constraints.
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-8;
|
|
tolf=1.0E-10;
|
|
for(optkind=0; optkind<=1; optkind++)
|
|
{
|
|
for(n=5; n<=20; n++)
|
|
{
|
|
//--- Generate problem
|
|
m=n+CHighQualityRand::HQRndUniformI(rs,n);
|
|
bl.Resize(n);
|
|
bu.Resize(n);
|
|
x0.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bu.Set(i,bl[i]+CHighQualityRand::HQRndUniformR(rs));
|
|
x0.Set(i,bl[i]+(bu[i]-bl[i])*CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
c.Resize(m,n+1);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(n>=5))
|
|
return;
|
|
rawccnt=3;
|
|
rawc.Resize(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v+=x0[j]*rawc.Get(i,j);
|
|
}
|
|
rawc.Set(i,n,v);
|
|
rawct.Set(i,2*CHighQualityRand::HQRndUniformI(rs,2)-1);
|
|
}
|
|
//--- Solve
|
|
if(optkind==0)
|
|
CMinLM::MinLMCreateV(n,m,x0,10*epsx,state);
|
|
if(optkind==1)
|
|
CMinLM::MinLMCreateVJ(n,m,x0,state);
|
|
CMinLM::MinLMSetCond(state,epsx,0);
|
|
CMinLM::MinLMSetBC(state,bl,bu);
|
|
CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]<bl[i],"testminlmunit.ap:878");
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]>bu[i],"testminlmunit.ap:879");
|
|
}
|
|
if(state.m_needfi)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,"MinLM test: integrity check failed"))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:894");
|
|
if(errorflag)
|
|
return;
|
|
//--- Test feasibility w.r.t. box and linear inequality constraints
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,x[i]<bl[i],"testminlmunit.ap:903");
|
|
CAp::SetErrorFlag(errorflag,x[i]>bu[i],"testminlmunit.ap:904");
|
|
}
|
|
for(i=0; i<=rawccnt-1; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x,rawc,i);
|
|
v-=rawc.Get(i,n);
|
|
if(rawct[i]>0)
|
|
CAp::SetErrorFlag(errorflag,v<(-xtol),"testminlmunit.ap:911");
|
|
if(rawct[i]<0)
|
|
CAp::SetErrorFlag(errorflag,v>xtol,"testminlmunit.ap:913");
|
|
}
|
|
//--- Make several random trial steps and:
|
|
//--- 0) generate small random trial step
|
|
//--- 1) if step is infeasible, skip to next trial
|
|
//--- 2) compare function value in the trial point against one in other points
|
|
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
|
|
x1.Resize(n);
|
|
for(trialidx=0; trialidx<=10*n; trialidx++)
|
|
{
|
|
h=0.001;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
x1.Set(i,x[i]+(CHighQualityRand::HQRndUniformR(rs)*2-1)*h);
|
|
}
|
|
while(!(x1[i]>=bl[i] && x1[i]<=bu[i]));
|
|
}
|
|
bflag=false;
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(rawct[i]!=0))
|
|
return;
|
|
v=CAblasF::RDotVR(n,x1,rawc,i)-rawc.Get(i,n);
|
|
bflag=bflag||(rawct[i]>0 && v<0.0);
|
|
bflag=bflag||(rawct[i]<0 && v>0.0);
|
|
}
|
|
if(bflag)
|
|
continue;
|
|
//---
|
|
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
|
|
CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:950");
|
|
}
|
|
}
|
|
}
|
|
//--- Minimize
|
|
//--- [ [ ]2 ]
|
|
//--- SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ] ]
|
|
//--- [ [ ] ]
|
|
//--- subject to linear EQUALITY constraints on x_j.
|
|
//--- We check correctness of solution by sampling a few random points
|
|
//--- around one returned by optimizer, and comparing function value
|
|
//--- with target. Sampling is performed with respect to equality
|
|
//--- constraints. In order to simplify algorithm we use orthogonal
|
|
//--- equality constraints.
|
|
//--- NOTE: we solve problem using VJ mode (analytic Jacobian) because
|
|
//--- roundoff errors from numerical differentiation sometimes
|
|
//--- prevent us from converging with good precision.
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-8;
|
|
tolf=1.0E-10;
|
|
optkind=1;
|
|
for(n=10; n<=20; n++)
|
|
{
|
|
//--- Generate problem
|
|
m=n+CHighQualityRand::HQRndUniformI(rs,n);
|
|
x0.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
c.Resize(m,n+1);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(n>=5))
|
|
return;
|
|
rawccnt=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
CMatGen::RMatrixRndOrthogonal(n,z);
|
|
rawc.Resize(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
rawct.Fill(0);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
rawc.Set(i,j,z.Get(i,j));
|
|
v+=x0[j]*rawc.Get(i,j);
|
|
}
|
|
rawc.Set(i,n,v);
|
|
}
|
|
//--- Solve
|
|
if(optkind==0)
|
|
CMinLM::MinLMCreateV(n,m,x0,1.0E-12,state);
|
|
if(optkind==1)
|
|
CMinLM::MinLMCreateVJ(n,m,x0,state);
|
|
CMinLM::MinLMSetCond(state,epsx,0);
|
|
CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,"MinLM Test: integrity check failed"))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1031");
|
|
if(errorflag)
|
|
return;
|
|
//--- Test feasibility w.r.t. linear equality constraints
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x,rawc,i)-rawc.Get(i,n);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(v)>xtol,"testminlmunit.ap:1042");
|
|
}
|
|
//--- Make several random trial steps and:
|
|
//--- 0) generate small random trial step
|
|
//--- 1) project it onto equality constrained subspace
|
|
//--- 2) compare function value in the trial point against one in other points
|
|
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
|
|
x1.Resize(n);
|
|
for(trialidx=0; trialidx<=10*n; trialidx++)
|
|
{
|
|
h=0.001;
|
|
for(i=0; i<n; i++)
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,rawc,i);
|
|
for(i_=0; i_<n; i_++)
|
|
x1.Add(i_,- v*rawc.Get(i,i_));
|
|
}
|
|
v=x1.Dot(x1);
|
|
//--- check
|
|
if(!CAp::Assert(v>0.0))
|
|
return;
|
|
v=h/MathSqrt(v);
|
|
x1*=v;
|
|
x1+=x;
|
|
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
|
|
CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:1072");
|
|
}
|
|
}
|
|
//--- Minimize
|
|
//--- [ [ ]2 ] [ [ ]2 ]
|
|
//--- SUM_i[ SUM_j[ (0.1*x_j+power(x0_j,3))*c0_ij ] ] + SUM_i[ SUM_j[ (0.1*x_j+power(x1_j,3))*c1_ij ] ]
|
|
//--- [ [ ] ] [ [ ] ]
|
|
//--- for two sets of unknowns (x0_j and x1_j) and two sets of
|
|
//--- coefficients (c0_ij and c1_ij, M1*N and M2*N matrices) subject
|
|
//--- to equality constraint
|
|
//--- x0_j=x1_j for all j
|
|
//--- Such optimization problem arises when we fit same model to
|
|
//--- two distinct datasets and want to share SOME of coefficients
|
|
//--- between fits. If we share ALL coefficients, it is equal to
|
|
//--- fitting one model to combination of two datasets.
|
|
//--- Our test checks that such "combined" 2N-dimensional problem
|
|
//--- solved with general linear constraints which "glue" two datasets
|
|
//--- together returns same answer as N-dimensional problem on (M1+M2)-point
|
|
//--- dataset.
|
|
//--- NOTE: we solve problem using VJ mode (analytic Jacobian) because
|
|
//--- roundoff errors from numerical differentiation prevent us
|
|
//--- from converging with good precision.
|
|
epsx=1.0E-12;
|
|
for(n=5; n<=20; n++)
|
|
{
|
|
//--- Generate problems
|
|
m1=n+CHighQualityRand::HQRndUniformI(rs,n);
|
|
m2=n+CHighQualityRand::HQRndUniformI(rs,n);
|
|
x0.Resize(n);
|
|
x12.Resize(2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x12.Set(2*i+0,CHighQualityRand::HQRndNormal(rs));
|
|
x12.Set(2*i+1,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
c.Resize(m1+m2,n+1);
|
|
c12=matrix<double>::Zeros(m1+m2,2*n+1);
|
|
for(i=0; i<=m1+m2-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
if(i<m1)
|
|
c12.Set(i,j,c.Get(i,j));
|
|
else
|
|
c12.Set(i,n+j,c.Get(i,j));
|
|
}
|
|
c.Set(i,n,CHighQualityRand::HQRndNormal(rs));
|
|
c12.Set(i,2*n,c.Get(i,n));
|
|
}
|
|
rawccnt=n;
|
|
rawc=matrix<double>::Identity(rawccnt,2*n+1);
|
|
rawct.Resize(rawccnt);
|
|
rawct.Fill(0);
|
|
rawc.Diag(vector<double>::Full(n,-1),n);
|
|
//--- Solve N-dimensional "combined" problem, store result to X1
|
|
CMinLM::MinLMCreateVJ(n,m1+m2,x0,state);
|
|
CMinLM::MinLMSetCond(state,epsx,0);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
TestFunc1(n,m1+m2,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
TestFunc1(n,m1+m2,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,"MinLM Test: integrity check failed"))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1170");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
//--- Solve N-dimensional "glued" problem, store result to X12
|
|
CMinLM::MinLMCreateVJ(2*n,m1+m2,x12,state);
|
|
CMinLM::MinLMSetCond(state,epsx,0);
|
|
CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
TestFunc1(2*n,m1+m2,c12,state.m_x,v,false,state.m_fi,true,state.m_j,false);
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
TestFunc1(2*n,m1+m2,c12,state.m_x,v,false,state.m_fi,true,state.m_j,true);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,"minlm test: integrity check failed"))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x12,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1195");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
//--- Compare solutions
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-x12[i])>1.0E-3,"testminlmunit.ap:1204");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-x12[n+i])>1.0E-3,"testminlmunit.ap:1205");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x12[i]-x12[n+i])>1.0E-6,"testminlmunit.ap:1206");
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Test other properties |
|
|
//| On failure sets error flag, leaves it unchanged on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TestOther(bool &errorflag,bool &statefieldsconsistencyflag)
|
|
{
|
|
CMinLMState state;
|
|
CMinLMReport rep;
|
|
int n=0;
|
|
int m=0;
|
|
double v=0;
|
|
double s=0;
|
|
CRowDouble x;
|
|
CRowDouble xlast;
|
|
int i=0;
|
|
int j=0;
|
|
int rkind=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double stpmax=0;
|
|
int stopcallidx=0;
|
|
int callidx=0;
|
|
int maxits=0;
|
|
bool terminationrequested=false;
|
|
int pass=0;
|
|
int spoilcnt=0;
|
|
double mx=0;
|
|
int i_=0;
|
|
//--- Other properties:
|
|
//--- 1. test reports (F should form monotone sequence)
|
|
//--- 2. test maximum step
|
|
for(rkind=0; rkind<=5; rkind++)
|
|
{
|
|
//--- reports:
|
|
//--- * check that first report is initial point
|
|
//--- * check that F is monotone decreasing
|
|
//--- * check that last report is final result
|
|
n=3;
|
|
m=3;
|
|
s=100;
|
|
x.Resize(n);
|
|
xlast.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,6+CMath::RandomReal());
|
|
switch(rkind)
|
|
{
|
|
case 0:
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
break;
|
|
case 1:
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
break;
|
|
case 2:
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
break;
|
|
case 3:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
break;
|
|
case 4:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
break;
|
|
case 5:
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
break;
|
|
}
|
|
CMinLM::MinLMSetCond(state,0,4);
|
|
CMinLM::MinLMSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi || state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,MathSqrt(s)*(MathExp(state.m_x[0])-2));
|
|
state.m_fi.Set(1,state.m_x[1]);
|
|
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_j.Set(0,0,MathSqrt(s)*MathExp(state.m_x[0]));
|
|
state.m_j.Set(0,1,0);
|
|
state.m_j.Set(0,2,0);
|
|
state.m_j.Set(1,0,0);
|
|
state.m_j.Set(1,1,1);
|
|
state.m_j.Set(1,2,0);
|
|
state.m_j.Set(2,0,-1);
|
|
state.m_j.Set(2,1,0);
|
|
state.m_j.Set(2,2,1);
|
|
}
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
state.m_g.Set(0,s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]));
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
}
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h.Set(0,0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
|
|
state.m_h.Set(0,1,0);
|
|
state.m_h.Set(0,2,-2);
|
|
state.m_h.Set(1,0,0);
|
|
state.m_h.Set(1,1,2);
|
|
state.m_h.Set(1,2,0);
|
|
state.m_h.Set(2,0,-2);
|
|
state.m_h.Set(2,1,0);
|
|
state.m_h.Set(2,2,2);
|
|
}
|
|
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:1320");
|
|
if(state.m_xupdated)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,state.m_f>fprev,"testminlmunit.ap:1323");
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,state.m_x[i]!=x[i],"testminlmunit.ap:1326");
|
|
}
|
|
fprev=state.m_f;
|
|
xlast=state.m_x;
|
|
}
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1332");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,x[i]!=xlast[i],"testminlmunit.ap:1336");
|
|
}
|
|
n=1;
|
|
x=vector<double>::Zeros(n);
|
|
x.Set(0,100);
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
CMinLM::MinLMSetCond(state,1.0E-12,0);
|
|
CMinLM::MinLMSetStpMax(state,stpmax);
|
|
CMinLM::MinLMSetXRep(state,true);
|
|
xprev=x[0];
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
|
|
if(state.m_needfgh)
|
|
state.m_h.Set(0,0,MathExp(state.m_x[0])+MathExp(-state.m_x[0]));
|
|
CAp::SetErrorFlag(errorflag,MathAbs(state.m_x[0]-xprev)>((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminlmunit.ap:1355");
|
|
if(state.m_xupdated)
|
|
xprev=state.m_x[0];
|
|
}
|
|
//--- Check algorithm ability to handle request for termination:
|
|
//--- * to terminate with correct return code = 8
|
|
//---*to return point which was "current" at the moment of termination
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
n=3;
|
|
m=3;
|
|
s=100;
|
|
x=vector<double>::Zeros(n);
|
|
xlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,6+CMath::RandomReal());
|
|
stopcallidx=CMath::RandomInteger(20);
|
|
maxits=25;
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetCond(state,0,maxits);
|
|
CMinLM::MinLMSetXRep(state,true);
|
|
callidx=0;
|
|
terminationrequested=false;
|
|
xlast=x;
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi || state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,MathSqrt(s)*(MathExp(state.m_x[0])-2));
|
|
state.m_fi.Set(1,state.m_x[1]);
|
|
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_j.Set(0,0,MathSqrt(s)*MathExp(state.m_x[0]));
|
|
state.m_j.Set(0,1,0);
|
|
state.m_j.Set(0,2,0);
|
|
state.m_j.Set(1,0,0);
|
|
state.m_j.Set(1,1,1);
|
|
state.m_j.Set(1,2,0);
|
|
state.m_j.Set(2,0,-1);
|
|
state.m_j.Set(2,1,0);
|
|
state.m_j.Set(2,2,1);
|
|
}
|
|
if(callidx==stopcallidx)
|
|
{
|
|
CMinLM::MinLMRequestTermination(state);
|
|
terminationrequested=true;
|
|
}
|
|
callidx++;
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
if(!terminationrequested)
|
|
xlast=state.m_x;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=8,"testminlmunit.ap:1419");
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,x[i]!=(double)(xlast[i]),"testminlmunit.ap:1421");
|
|
}
|
|
//--- Test ability to detect NAN/INF values.
|
|
//--- We use Rosenbrock's function which is modified to return
|
|
//--- NAN/INF (randomly) when near solution. Algorithm should set
|
|
//--- appropriate error code (-8) on exit.
|
|
n=10;
|
|
m=2*(n-1);
|
|
s=10;
|
|
x=vector<double>::Zeros(n);
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetCond(state,1.0E-12,0);
|
|
spoilcnt=0;
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(i,j,0);
|
|
}
|
|
for(i=0; i<n-1; i++)
|
|
{
|
|
state.m_fi.Set(2*i+0,s*(state.m_x[i+1]-CMath::Sqr(state.m_x[i])));
|
|
state.m_j.Set(2*i+0,i,-(s*2*state.m_x[i]));
|
|
state.m_j.Set(2*i+0,i+1,s);
|
|
state.m_fi.Set(2*i+1,1-state.m_x[i]);
|
|
state.m_j.Set(2*i+1,i,-1);
|
|
}
|
|
mx=MathAbs(state.m_x.ToVector()-1.0).Max();
|
|
if(mx<1.0E-2)
|
|
{
|
|
i=CMath::RandomInteger(3);
|
|
v=AL_NaN;
|
|
if(i==1)
|
|
v=AL_POSINF;
|
|
if(i==2)
|
|
v=AL_NEGINF;
|
|
if(CMath::RandomReal()>0.5)
|
|
state.m_fi.Set(CMath::RandomInteger(m),v);
|
|
else
|
|
state.m_j.Set(CMath::RandomInteger(m),CMath::RandomInteger(n),v);
|
|
spoilcnt++;
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_needfi)
|
|
{
|
|
for(i=0; i<n-1; i++)
|
|
{
|
|
state.m_fi.Set(2*i+0,s*(state.m_x[i+1]-CMath::Sqr(state.m_x[i])));
|
|
state.m_fi.Set(2*i+1,1-state.m_x[i]);
|
|
}
|
|
mx=MathAbs(state.m_x.ToVector()-1.0).Max();
|
|
if(mx<1.0E-2)
|
|
{
|
|
i=CMath::RandomInteger(3);
|
|
v=AL_NaN;
|
|
if(i==1)
|
|
v=AL_POSINF;
|
|
if(i==2)
|
|
v=AL_NEGINF;
|
|
state.m_fi.Set(CMath::RandomInteger(m),v);
|
|
spoilcnt++;
|
|
}
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=-8,"testminlmunit.ap:1513");
|
|
CAp::SetErrorFlag(errorflag,spoilcnt!=1,"testminlmunit.ap:1514");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests OptGuard |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TestOptGuard(bool &waserrors)
|
|
{
|
|
CHighQualityRandState rs;
|
|
double v=0;
|
|
CMinLMState state;
|
|
CMinLMReport rep;
|
|
COptGuardReport ogrep;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble a1;
|
|
CRowDouble s;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
double diffstep=0;
|
|
int defecttype=0;
|
|
int funcidx=0;
|
|
int varidx=0;
|
|
int skind=0;
|
|
CMatrixDouble jactrue;
|
|
CMatrixDouble jacdefect;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Check that gradient verification is disabled by default:
|
|
//--- gradient checking for bad problem must return nothing
|
|
n=10;
|
|
x0.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,1.0+0.1*i);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
|
|
CMinLM::MinLMCreateVJ(n,1,x0,state);
|
|
CMinLM::MinLMSetCond(state,1.0E-9,10);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*v);
|
|
}
|
|
state.m_j.Row(0,vector<double>::Zeros(n));
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x1,rep);
|
|
CMinLM::MinLMOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminlmunit.ap:1575");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminlmunit.ap:1576");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminlmunit.ap:1579");
|
|
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminlmunit.ap:1580");
|
|
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminlmunit.ap:1581");
|
|
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminlmunit.ap:1582");
|
|
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminlmunit.ap:1583");
|
|
if(waserrors)
|
|
return;
|
|
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminlmunit.ap:1586");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminlmunit.ap:1587");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminlmunit.ap:1588");
|
|
//--- Test gradient checking functionality, try various
|
|
//--- defect types:
|
|
//--- * accidental zeroing of some gradient component
|
|
//--- * accidental addition of 1.0 to some component
|
|
//--- * accidental multiplication by 2.0
|
|
//--- Try distorting both target and constraints.
|
|
diffstep=0.001;
|
|
n=10;
|
|
for(skind=0; skind<=1; skind++)
|
|
{
|
|
for(funcidx=0; funcidx<=1; funcidx++)
|
|
{
|
|
for(defecttype=-1; defecttype<=2; defecttype++)
|
|
{
|
|
varidx=CHighQualityRand::HQRndUniformI(rs,n);
|
|
x0.Resize(n);
|
|
s.Resize(n);
|
|
bndl.Resize(n);
|
|
bndu.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(10,skind*(30*CHighQualityRand::HQRndUniformR(rs)-15)));
|
|
x0.Set(i,(1.0+0.1*i)*s[i]);
|
|
j=CHighQualityRand::HQRndUniformI(rs,3);
|
|
bndl.Set(i,-(100*s[i]));
|
|
bndu.Set(i,100*s[i]);
|
|
if(j==1)
|
|
bndl.Set(i,x0[i]);
|
|
if(j==2)
|
|
bndu.Set(i,x0[i]);
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
|
|
CMinLM::MinLMCreateVJ(n,2,x0,state);
|
|
CMinLM::MinLMOptGuardGradient(state,diffstep);
|
|
CMinLM::MinLMSetCond(state,1.0E-9,10);
|
|
CMinLM::MinLMSetScale(state,s);
|
|
CMinLM::MinLMSetBC(state,bndl,bndu);
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
if(state.m_needfi || state.m_needfij)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminlmunit.ap:1637");
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminlmunit.ap:1638");
|
|
}
|
|
state.m_fi.Set(0,0);
|
|
vector<double> xs=state.m_x/s;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=xs.Dot(a[i]+0);
|
|
state.m_fi.Add(0,0.5*(state.m_x[i]/s[i])*v);
|
|
if(state.m_needfij)
|
|
state.m_j.Set(0,i,v);
|
|
}
|
|
state.m_fi.Set(1,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=xs.Dot(a1[i]+0);
|
|
state.m_fi.Add(1,0.5*(state.m_x[i]/s[i])*v);
|
|
if(state.m_needfij)
|
|
state.m_j.Set(1,i,v);
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
if(defecttype==0)
|
|
state.m_j.Set(funcidx,varidx,0);
|
|
if(defecttype==1)
|
|
state.m_j.Add(funcidx,varidx,1);
|
|
if(defecttype==2)
|
|
state.m_j.Mul(funcidx,varidx,2);
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_j.Row(0,state.m_j[0]/s.ToVector());
|
|
state.m_j.Row(1,state.m_j[1]/s.ToVector());
|
|
}
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false))
|
|
return;
|
|
}
|
|
CMinLM::MinLMResults(state,x1,rep);
|
|
CMinLM::MinLMOptGuardResults(state,ogrep);
|
|
//--- Check that something is returned
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminlmunit.ap:1695");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminlmunit.ap:1696");
|
|
if(waserrors)
|
|
return;
|
|
//---
|
|
//--- Compute reference values for true and spoiled Jacobian at X0
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(ogrep.m_badgradxbase,n),"testminlmunit.ap:1703");
|
|
if(waserrors)
|
|
return;
|
|
//---
|
|
jactrue.Resize(2,n);
|
|
jacdefect.Resize(2,n);
|
|
vector<double> bs=ogrep.m_badgradxbase/s;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=bs.Dot(a[i]+0);
|
|
jactrue.Set(0,i,v);
|
|
jacdefect.Set(0,i,v);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=bs.Dot(a1[i]+0);
|
|
jactrue.Set(1,i,v);
|
|
jacdefect.Set(1,i,v);
|
|
}
|
|
if(defecttype==0)
|
|
jacdefect.Set(funcidx,varidx,0);
|
|
if(defecttype==1)
|
|
jacdefect.Add(funcidx,varidx,1);
|
|
if(defecttype==2)
|
|
jacdefect.Mul(funcidx,varidx,2);
|
|
jactrue.Row(0,jactrue[0]/s.ToVector());
|
|
jactrue.Row(1,jactrue[1]/s.ToVector());
|
|
jacdefect.Row(0,jacdefect[0]/s.ToVector());
|
|
jacdefect.Row(1,jacdefect[1]/s.ToVector());
|
|
}
|
|
//--- Check OptGuard report
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgraduser,2,n),"testminlmunit.ap:1741");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgradnum,2,n),"testminlmunit.ap:1742");
|
|
if(waserrors)
|
|
return;
|
|
//---
|
|
if(defecttype>=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminlmunit.ap:1747");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=funcidx,"testminlmunit.ap:1748");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminlmunit.ap:1749");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminlmunit.ap:1753");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminlmunit.ap:1754");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminlmunit.ap:1755");
|
|
}
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathAbs(jactrue.Get(i,j)-ogrep.m_badgradnum.Get(i,j))>(0.01/s[j]),"testminlmunit.ap:1760");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(jacdefect.Get(i,j)-ogrep.m_badgraduser.Get(i,j))>(0.01/s[j]),"testminlmunit.ap:1761");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Asserts that State fields are consistent with RKind. |
|
|
//| Returns False otherwise. |
|
|
//| RKind is an algorithm selector: |
|
|
//| * -2 = V, AccType = 1 |
|
|
//| * -1 = V, AccType = 0 |
|
|
//| * 0 = FJ |
|
|
//| * 1 = FGJ |
|
|
//| * 2 = FGH |
|
|
//| * 3 = VJ, AccType = 0 |
|
|
//| * 4 = VJ, AccType = 1 |
|
|
//| * 5 = VJ, AccType = 2 |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinLMUnit::RKindVSStateCheck(int rkind,CMinLMState &state)
|
|
{
|
|
int nset=0;
|
|
//--- check
|
|
if(state.m_needfi)
|
|
nset++;
|
|
if(state.m_needf)
|
|
nset++;
|
|
if(state.m_needfg)
|
|
nset++;
|
|
if(state.m_needfij)
|
|
nset++;
|
|
if(state.m_needfgh)
|
|
nset++;
|
|
if(state.m_xupdated)
|
|
nset++;
|
|
//--- check
|
|
if(nset!=1)
|
|
return(false);
|
|
switch(rkind)
|
|
{
|
|
case -2:
|
|
return (state.m_needfi || state.m_xupdated);
|
|
case -1:
|
|
return (state.m_needfi || state.m_xupdated);
|
|
case 0:
|
|
return (state.m_needf || state.m_needfij || state.m_xupdated);
|
|
case 1:
|
|
return (state.m_needf || state.m_needfij || state.m_needfg || state.m_xupdated);
|
|
case 2:
|
|
return (state.m_needf || state.m_needfg || state.m_needfgh || state.m_xupdated);
|
|
case 3:
|
|
return (state.m_needfi || state.m_needfij || state.m_xupdated);
|
|
case 4:
|
|
return (state.m_needfi || state.m_needfij || state.m_xupdated);
|
|
case 5:
|
|
return (state.m_needfi || state.m_needfij || state.m_xupdated);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates FI / F / G / H for problem min( || Ax - b ||) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::AXMB(CMinLMState &state,CMatrixDouble &a,
|
|
CRowDouble &b,int n)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
|
|
if(state.m_needf || state.m_needfg || state.m_needfgh)
|
|
state.m_f=0;
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g=vector<double>::Zeros(n);
|
|
if(state.m_needfgh)
|
|
state.m_h=matrix<double>::Zeros(n,n);
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=state.m_x.Dot(a[i]+0);
|
|
if(state.m_needf || state.m_needfg || state.m_needfgh)
|
|
state.m_f+=CMath::Sqr(v-b[i]);
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g+=a[i]*2*(v-b[i]);
|
|
if(state.m_needfgh)
|
|
for(int j=0; j<n; j++)
|
|
for(int k=0; k<n; k++)
|
|
state.m_h.Add(j,k,2*a.Get(i,j)*a.Get(i,k));
|
|
if(state.m_needfi)
|
|
state.m_fi.Set(i,v-b[i]);
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(i,v-b[i]);
|
|
state.m_j.Row(i,a[i]+0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tries to reproduce previously fixed bugs; |
|
|
//| in case of bug being present sets Err to True; |
|
|
//| leaves it unchanged otherwise. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TryReproduceFixedBugs(bool &err)
|
|
{
|
|
CMinLMState s;
|
|
CMinLMReport rep;
|
|
CRowDouble bl=vector<double>::Full(2,-1);
|
|
CRowDouble bu=vector<double>::Full(2,1);;
|
|
CRowDouble x=vector<double>::Full(2,2);
|
|
//--- Reproduce bug reported by ISS:
|
|
//--- when solving bound constrained problem with numerical differentiation
|
|
//--- and starting from infeasible point, we won't Stop at the feasible point
|
|
CMinLM::MinLMCreateV(2,2,x,0.001,s);
|
|
CMinLM::MinLMSetBC(s,bl,bu);
|
|
while(CMinLM::MinLMIteration(s))
|
|
{
|
|
if(s.m_needfi)
|
|
s.m_fi=s.m_x.Pow(2)+0;
|
|
}
|
|
CMinLM::MinLMResults(s,x,rep);
|
|
CAp::SetErrorFlag(err,(x[0]<bl[0] || x[0]>bu[0] || x[1]<bl[1] || x[1]>bu[1]),"testminlmunit.ap:1923");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test function 1: F(N, M, C, X) = SUM(f_i ^ 2) |
|
|
//| f_i = SUM((power(x_j, 3) + alpha*x_j) * c_ij) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLMUnit::TestFunc1(int n,int m,CMatrixDouble &c,
|
|
CRowDouble &x,double &f,
|
|
bool needf,CRowDouble &fi,
|
|
bool needfi,CMatrixDouble &jac,
|
|
bool needjac)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
double alpha=0.01;
|
|
|
|
if(needf)
|
|
f=0;
|
|
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
v=c.Get(i,n);
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
v+=(alpha*x[j]+MathPow(x[j],3))*c.Get(i,j);
|
|
if(needjac)
|
|
jac.Set(i,j,(alpha+3*MathPow(x[j],2))*c.Get(i,j));
|
|
}
|
|
if(needfi)
|
|
fi.Set(i,v);
|
|
if(needf)
|
|
f+=v*v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLSFit |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLSFitUnit
|
|
{
|
|
public:
|
|
static bool TestLSFit(const bool silent);
|
|
|
|
private:
|
|
static void TestPolynomialFitting(bool &fiterrors);
|
|
static void TestRationalFitting(bool &fiterrors);
|
|
static void TestSplineFitting(bool &fiterrors);
|
|
static void TestGeneralFitting(bool &llserrors,bool &nlserrors);
|
|
static bool IsGLSSolution(const int n,const int m,const int k,double &y[],double &w[],CMatrixDouble &fmatrix,CMatrixDouble &cmatrix,double &cc[]);
|
|
static double GetGLSError(const int n,const int m,double &y[],double &w[],CMatrixDouble &fmatrix,double &c[]);
|
|
static void FitLinearNonlinear(const int m,const int deravailable,CMatrixDouble &xy,CLSFitState &state,bool &nlserrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLSFit |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLSFitUnit::TestLSFit(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool llserrors;
|
|
bool nlserrors;
|
|
bool polfiterrors;
|
|
bool ratfiterrors;
|
|
bool splfiterrors;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- function calls
|
|
TestPolynomialFitting(polfiterrors);
|
|
TestRationalFitting(ratfiterrors);
|
|
TestSplineFitting(splfiterrors);
|
|
TestGeneralFitting(llserrors,nlserrors);
|
|
//--- report
|
|
waserrors=(((llserrors||nlserrors)||polfiterrors)||ratfiterrors)||splfiterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LEAST SQUARES");
|
|
PrintResult("POLYNOMIAL LEAST SQUARES",!polfiterrors);
|
|
PrintResult("RATIONAL LEAST SQUARES",!ratfiterrors);
|
|
PrintResult("SPLINE LEAST SQUARES",!splfiterrors);
|
|
PrintResult("LINEAR LEAST SQUARES",!llserrors);
|
|
PrintResult("NON-LINEAR LEAST SQUARES",!nlserrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unit test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLSFitUnit::TestPolynomialFitting(bool &fiterrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double t=0;
|
|
int i=0;
|
|
int k=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double s=0;
|
|
double xmin=0;
|
|
double xmax=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
int n=0;
|
|
int m=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double x2[];
|
|
double y2[];
|
|
double w2[];
|
|
double xfull[];
|
|
double yfull[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant p;
|
|
CBarycentricInterpolant p1;
|
|
CBarycentricInterpolant p2;
|
|
CPolynomialFitReport rep;
|
|
CPolynomialFitReport rep2;
|
|
//--- initialization
|
|
fiterrors=false;
|
|
maxn=5;
|
|
passcount=20;
|
|
threshold=1.0E8*CMath::m_machineepsilon;
|
|
//--- Test polunomial fitting
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- N=M+K fitting (i.e. interpolation)
|
|
for(k=0; k<n; k++)
|
|
{
|
|
CApServ::TaskGenInt1D(-1,1,n,xfull,yfull);
|
|
//--- allocation
|
|
ArrayResize(x,n-k);
|
|
ArrayResize(y,n-k);
|
|
ArrayResize(w,n-k);
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xc,k);
|
|
ArrayResize(yc,k);
|
|
ArrayResize(dc,k);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<=n-k-1; i++)
|
|
{
|
|
x[i]=xfull[i];
|
|
y[i]=yfull[i];
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
//--- change values
|
|
for(i=0; i<k; i++)
|
|
{
|
|
xc[i]=xfull[n-k+i];
|
|
yc[i]=yfull[n-k+i];
|
|
dc[i]=0;
|
|
}
|
|
//--- function call
|
|
CLSFit::PolynomialFitWC(x,y,w,n-k,xc,yc,dc,k,n,info,p1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<=n-k-1; i++)
|
|
fiterrors=fiterrors||MathAbs(CRatInt::BarycentricCalc(p1,x[i])-y[i])>threshold;
|
|
for(i=0; i<k; i++)
|
|
fiterrors=fiterrors||MathAbs(CRatInt::BarycentricCalc(p1,xc[i])-yc[i])>threshold;
|
|
}
|
|
}
|
|
//--- Testing constraints on derivatives.
|
|
//--- Special tasks which will always have solution:
|
|
//--- 1. P(0)=YC[0]
|
|
//--- 2. P(0)=YC[0],P'(0)=YC[1]
|
|
if(n>1)
|
|
{
|
|
for(m=3; m<=5; m++)
|
|
{
|
|
for(k=1; k<=2; k++)
|
|
{
|
|
CApServ::TaskGenInt1D(-1,1,n,x,y);
|
|
//--- allocation
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,2);
|
|
ArrayResize(yc,2);
|
|
ArrayResize(dc,2);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
w[i]=1+CMath::RandomReal();
|
|
xc[0]=0;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=0;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=1;
|
|
//--- function call
|
|
CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,k,m,info,p1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(p1,0.0,v0,v1);
|
|
fiterrors=fiterrors||MathAbs(v0-yc[0])>threshold;
|
|
//--- check
|
|
if(k==2)
|
|
fiterrors=fiterrors||MathAbs(v1-yc[1])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=2; m<=8; m++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- General fitting
|
|
//--- interpolating function through M nodes should have
|
|
//--- greater RMS error than fitting it through the same M nodes
|
|
n=100;
|
|
ArrayResize(x2,n);
|
|
ArrayResize(y2,n);
|
|
ArrayResize(w2,n);
|
|
xmin=0;
|
|
xmax=2*M_PI;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x2[i]=2*M_PI*CMath::RandomReal();
|
|
y2[i]=MathSin(x2[i]);
|
|
w2[i]=1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,m);
|
|
ArrayResize(y,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
x[i]=xmin+(xmax-xmin)*i/(m-1);
|
|
y[i]=MathSin(x[i]);
|
|
}
|
|
//--- function calls
|
|
CPolInt::PolynomialBuild(x,y,m,p1);
|
|
CLSFit::PolynomialFitWC(x2,y2,w2,n,xc,yc,dc,0,m,info,p2,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate P1 (interpolant) RMS error,compare with P2 error
|
|
v1=0;
|
|
v2=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v1+=CMath::Sqr(CRatInt::BarycentricCalc(p1,x2[i])-y2[i]);
|
|
v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(p2,x2[i])-y2[i]);
|
|
}
|
|
v1=MathSqrt(v1/n);
|
|
v2=MathSqrt(v2/n);
|
|
//--- search errors
|
|
fiterrors=fiterrors||v2>v1;
|
|
fiterrors=fiterrors||MathAbs(v2-rep.m_rmserror)>threshold;
|
|
}
|
|
//--- compare weighted and non-weighted
|
|
n=20;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1;
|
|
}
|
|
//--- function calls
|
|
CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,0,m,info,p1,rep);
|
|
CLSFit::PolynomialFit(x,y,n,m,info2,p2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate P1 (interpolant),compare with P2 error
|
|
//--- compare RMS errors
|
|
t=2*CMath::RandomReal()-1;
|
|
v1=CRatInt::BarycentricCalc(p1,t);
|
|
v2=CRatInt::BarycentricCalc(p2,t);
|
|
//--- search errors
|
|
fiterrors=fiterrors||v2!=v1;
|
|
fiterrors=fiterrors||rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors||rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors||rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors||rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=1; m<=maxn; m++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
|
|
return;
|
|
//--- solve simple task (all X[] are the same,Y[] are specially
|
|
//--- calculated to ensure simple form of all types of errors)
|
|
//--- and check correctness of the errors calculated by subroutines
|
|
//--- First pass is done with zero Y[],other passes - with random Y[].
|
|
//--- It should test both ability to correctly calculate errors and
|
|
//--- ability to not fail while working with zeros :)
|
|
n=4*maxn;
|
|
//--- check
|
|
if(pass==1)
|
|
{
|
|
v1=0;
|
|
v2=0;
|
|
v=0;
|
|
}
|
|
else
|
|
{
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- change values
|
|
for(i=0; i<=maxn-1; i++)
|
|
{
|
|
x[4*i+0]=i;
|
|
y[4*i+0]=v-v2;
|
|
w[4*i+0]=1;
|
|
x[4*i+1]=i;
|
|
y[4*i+1]=v-v1;
|
|
w[4*i+1]=1;
|
|
x[4*i+2]=i;
|
|
y[4*i+2]=v+v1;
|
|
w[4*i+2]=1;
|
|
x[4*i+3]=i;
|
|
y[4*i+3]=v+v2;
|
|
w[4*i+3]=1;
|
|
}
|
|
//--- change values
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
//--- check
|
|
if(pass==1)
|
|
refavgrel=0;
|
|
else
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test errors correctness
|
|
CLSFit::PolynomialFit(x,y,n,m,info,p,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CRatInt::BarycentricCalc(p,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLSFitUnit::TestRationalFitting(bool &fiterrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
int maxn=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double t=0;
|
|
double s=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double xmin=0;
|
|
double xmax=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x2[];
|
|
double y[];
|
|
double y2[];
|
|
double w[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant b1;
|
|
CBarycentricInterpolant b2;
|
|
CBarycentricFitReport rep;
|
|
CBarycentricFitReport rep2;
|
|
//--- initialization
|
|
fiterrors=false;
|
|
//--- PassCount number of repeated passes
|
|
//--- Threshold error tolerance
|
|
//--- LipschitzTol Lipschitz constant increase allowed
|
|
//--- when calculating constant on a twice denser grid
|
|
passcount=5;
|
|
maxn=15;
|
|
threshold=1000000*CMath::m_machineepsilon;
|
|
//--- Test rational fitting:
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- N=M+K fitting (i.e. interpolation)
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n-k);
|
|
ArrayResize(y,n-k);
|
|
ArrayResize(w,n-k);
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xc,k);
|
|
ArrayResize(yc,k);
|
|
ArrayResize(dc,k);
|
|
}
|
|
//--- change values
|
|
for(i=0; i<=n-k-1; i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
xc[i]=(double)(n-k+i)/(double)(n-1);
|
|
yc[i]=2*CMath::RandomReal()-1;
|
|
dc[i]=0;
|
|
}
|
|
//--- function call
|
|
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n-k,xc,yc,dc,k,n,info,b1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<=n-k-1; i++)
|
|
fiterrors=fiterrors||MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold;
|
|
for(i=0; i<k; i++)
|
|
fiterrors=fiterrors||MathAbs(CRatInt::BarycentricCalc(b1,xc[i])-yc[i])>threshold;
|
|
}
|
|
}
|
|
//--- Testing constraints on derivatives:
|
|
//--- * several M's are tried
|
|
//--- * several K's are tried - 1,2.
|
|
//--- * constraints at the ends of the interval
|
|
for(m=3; m<=5; m++)
|
|
{
|
|
for(k=1; k<=2; k++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,2);
|
|
ArrayResize(yc,2);
|
|
ArrayResize(dc,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
//--- change values
|
|
xc[0]=-1;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=1;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=0;
|
|
//--- function call
|
|
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,k,m,info,b1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<k; i++)
|
|
{
|
|
CRatInt::BarycentricDiff1(b1,xc[i],v0,v1);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(v0-yc[i])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=2; m<=8; m++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- General fitting
|
|
//--- interpolating function through M nodes should have
|
|
//--- greater RMS error than fitting it through the same M nodes
|
|
n=100;
|
|
ArrayResize(x2,n);
|
|
ArrayResize(y2,n);
|
|
ArrayResize(w2,n);
|
|
//--- change values
|
|
xmin=CMath::m_maxrealnumber;
|
|
xmax=-CMath::m_maxrealnumber;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x2[i]=2*M_PI*CMath::RandomReal();
|
|
y2[i]=MathSin(x2[i]);
|
|
w2[i]=1;
|
|
xmin=MathMin(xmin,x2[i]);
|
|
xmax=MathMax(xmax,x2[i]);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,m);
|
|
ArrayResize(y,m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
x[i]=xmin+(xmax-xmin)*i/(m-1);
|
|
y[i]=MathSin(x[i]);
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,m,3,b1);
|
|
CLSFit::BarycentricFitFloaterHormannWC(x2,y2,w2,n,xc,yc,dc,0,m,info,b2,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate B1 (interpolant) RMS error,compare with B2 error
|
|
v1=0;
|
|
v2=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v1+=CMath::Sqr(CRatInt::BarycentricCalc(b1,x2[i])-y2[i]);
|
|
v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(b2,x2[i])-y2[i]);
|
|
}
|
|
v1=MathSqrt(v1/n);
|
|
v2=MathSqrt(v2/n);
|
|
//--- search errors
|
|
fiterrors=fiterrors||v2>v1;
|
|
fiterrors=fiterrors||MathAbs(v2-rep.m_rmserror)>threshold;
|
|
}
|
|
//--- compare weighted and non-weighted
|
|
n=20;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1;
|
|
}
|
|
//--- function calls
|
|
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,0,m,info,b1,rep);
|
|
CLSFit::BarycentricFitFloaterHormann(x,y,n,m,info2,b2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate B1 (interpolant),compare with B2
|
|
//--- compare RMS errors
|
|
t=2*CMath::RandomReal()-1;
|
|
v1=CRatInt::BarycentricCalc(b1,t);
|
|
v2=CRatInt::BarycentricCalc(b2,t);
|
|
//--- search errors
|
|
fiterrors=fiterrors||v2!=v1;
|
|
fiterrors=fiterrors||rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors||rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors||rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors||rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
|
|
return;
|
|
//--- solve simple task (all X[] are the same,Y[] are specially
|
|
//--- calculated to ensure simple form of all types of errors)
|
|
//--- and check correctness of the errors calculated by subroutines
|
|
//--- First pass is done with zero Y[],other passes - with random Y[].
|
|
//--- It should test both ability to correctly calculate errors and
|
|
//--- ability to not fail while working with zeros :)
|
|
n=4;
|
|
if(pass==1)
|
|
{
|
|
v1=0;
|
|
v2=0;
|
|
v=0;
|
|
}
|
|
else
|
|
{
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=v-v2;
|
|
w[0]=1;
|
|
x[1]=0;
|
|
y[1]=v-v1;
|
|
w[1]=1;
|
|
x[2]=0;
|
|
y[2]=v+v1;
|
|
w[2]=1;
|
|
x[3]=0;
|
|
y[3]=v+v2;
|
|
w[3]=1;
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
//--- check
|
|
if(pass==1)
|
|
refavgrel=0;
|
|
else
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test errors correctness
|
|
CLSFit::BarycentricFitFloaterHormann(x,y,4,2,info,b1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CRatInt::BarycentricCalc(b1,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLSFitUnit::TestSplineFitting(bool &fiterrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double nonstrictthreshold=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
int info=0;
|
|
int info1=0;
|
|
int info2=0;
|
|
double s=0;
|
|
double ds=0;
|
|
double d2s=0;
|
|
int stype=0;
|
|
double t=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
double rho=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
double d[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CSpline1DInterpolant c;
|
|
CSpline1DInterpolant c2;
|
|
CSpline1DFitReport rep;
|
|
CSpline1DFitReport rep2;
|
|
//--- Valyes:
|
|
//--- * pass count
|
|
//--- * threshold - for tests which must be satisfied exactly
|
|
//--- * nonstrictthreshold - for approximate tests
|
|
passcount=20;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nonstrictthreshold=1.0E-4;
|
|
fiterrors=false;
|
|
//--- Test fitting by Cubic and Hermite splines (obsolete,but still supported)
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Cubic splines
|
|
//--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx).
|
|
for(m=4; m<=8; m++)
|
|
{
|
|
for(k=1; k<=4; k++)
|
|
{
|
|
//--- check
|
|
if(k>=m)
|
|
continue;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,4);
|
|
ArrayResize(yc,4);
|
|
ArrayResize(dc,4);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=sb;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=1;
|
|
xc[2]=sa+sb;
|
|
yc[2]=2*CMath::RandomReal()-1;
|
|
dc[2]=0;
|
|
xc[3]=sa+sb;
|
|
yc[3]=2*CMath::RandomReal()-1;
|
|
dc[3]=1;
|
|
//--- function call
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
for(i=0; i<k; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s);
|
|
//--- check
|
|
if(dc[i]==0)
|
|
fiterrors=fiterrors||MathAbs(s-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==1)
|
|
fiterrors=fiterrors||MathAbs(ds-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==2)
|
|
fiterrors=fiterrors||MathAbs(d2s-yc[i])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Cubic splines
|
|
//--- Ability to handle one internal constraint
|
|
for(m=4; m<=8; m++)
|
|
{
|
|
n=100;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,1);
|
|
ArrayResize(yc,1);
|
|
ArrayResize(dc,1);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sa*CMath::RandomReal()+sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s);
|
|
//--- check
|
|
if(dc[0]==0)
|
|
fiterrors=fiterrors||MathAbs(s-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==1)
|
|
fiterrors=fiterrors||MathAbs(ds-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==2)
|
|
fiterrors=fiterrors||MathAbs(d2s-yc[0])>threshold;
|
|
}
|
|
}
|
|
//--- Hermite splines
|
|
//--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx).
|
|
for(m=4; m<=8; m++)
|
|
{
|
|
for(k=1; k<=4; k++)
|
|
{
|
|
//--- check
|
|
if(k>=m)
|
|
continue;
|
|
//--- check
|
|
if(m%2!=0)
|
|
continue;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,4);
|
|
ArrayResize(yc,4);
|
|
ArrayResize(dc,4);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=sb;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=1;
|
|
xc[2]=sa+sb;
|
|
yc[2]=2*CMath::RandomReal()-1;
|
|
dc[2]=0;
|
|
xc[3]=sa+sb;
|
|
yc[3]=2*CMath::RandomReal()-1;
|
|
dc[3]=1;
|
|
//--- function call
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
for(i=0; i<k; i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s);
|
|
//--- check
|
|
if(dc[i]==0)
|
|
fiterrors=fiterrors||MathAbs(s-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==1)
|
|
fiterrors=fiterrors||MathAbs(ds-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==2)
|
|
fiterrors=fiterrors||MathAbs(d2s-yc[i])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Hermite splines
|
|
//--- Ability to handle one internal constraint
|
|
for(m=4; m<=8; m++)
|
|
{
|
|
//--- check
|
|
if(m%2!=0)
|
|
continue;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,1);
|
|
ArrayResize(yc,1);
|
|
ArrayResize(dc,1);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sa*CMath::RandomReal()+sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s);
|
|
//--- check
|
|
if(dc[0]==0)
|
|
fiterrors=fiterrors||MathAbs(s-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==1)
|
|
fiterrors=fiterrors||MathAbs(ds-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==2)
|
|
fiterrors=fiterrors||MathAbs(d2s-yc[0])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=4; m<=8; m++)
|
|
{
|
|
for(stype=0; stype<=1; stype++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- check
|
|
if(stype==1 && m%2!=0)
|
|
continue;
|
|
//--- cubic/Hermite spline fitting:
|
|
//---*generate "template spline" C2
|
|
//--- * generate 2*N points from C2,such that result of
|
|
//--- ideal fit should be equal to C2
|
|
//--- * fit,store in C
|
|
//--- * compare C and C2
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(stype==0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,m-2);
|
|
ArrayResize(y,m-2);
|
|
//--- change values
|
|
for(i=0; i<=m-2-1; i++)
|
|
{
|
|
x[i]=sa*i/(m-2-1)+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(x,y,m-2,1,2*CMath::RandomReal()-1,1,2*CMath::RandomReal()-1,c2);
|
|
}
|
|
//--- check
|
|
if(stype==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,m/2);
|
|
ArrayResize(y,m/2);
|
|
ArrayResize(d,m/2);
|
|
//--- change values
|
|
for(i=0; i<=m/2-1; i++)
|
|
{
|
|
x[i]=sa*i/(m/2-1)+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,m/2,c2);
|
|
}
|
|
n=50;
|
|
//--- allocation
|
|
ArrayResize(x,2*n);
|
|
ArrayResize(y,2*n);
|
|
ArrayResize(w,2*n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- "if i=0" and "if i=1" are needed to
|
|
//--- synchronize interval size for C2 and
|
|
//--- spline being fitted (i.e. C).
|
|
t=CMath::RandomReal();
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
//--- check
|
|
if(i==0)
|
|
x[i]=sb;
|
|
//--- check
|
|
if(i==1)
|
|
x[i]=sa+sb;
|
|
//--- change values
|
|
v=CSpline1D::Spline1DCalc(c2,x[i]);
|
|
y[i]=v+t;
|
|
w[i]=1+CMath::RandomReal();
|
|
x[n+i]=x[i];
|
|
y[n+i]=v-t;
|
|
w[n+i]=w[i];
|
|
}
|
|
//--- check
|
|
if(stype==0)
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep);
|
|
//--- check
|
|
if(stype==1)
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=sa*CMath::RandomReal()+sb;
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c2,v))>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=4; m<=8; m++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- prepare points/weights
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
n=10+CMath::RandomInteger(10);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1;
|
|
}
|
|
//--- Fit cubic with unity weights,without weights,then compare
|
|
if(m>=4)
|
|
{
|
|
//--- function calls
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep);
|
|
CLSFit::Spline1DFitCubic(x,y,n,m,info2,c2,rep2);
|
|
//--- check
|
|
if(info1<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=sa*CMath::RandomReal()+sb;
|
|
//--- search errors
|
|
fiterrors=fiterrors||CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v);
|
|
fiterrors=fiterrors||rep.m_taskrcond!=rep2.m_taskrcond;
|
|
fiterrors=fiterrors||rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors||rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors||rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors||rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
//--- Fit Hermite with unity weights,without weights,then compare
|
|
if(m>=4 && m%2==0)
|
|
{
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep);
|
|
CLSFit::Spline1DFitHermite(x,y,n,m,info2,c2,rep2);
|
|
//--- check
|
|
if(info1<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=sa*CMath::RandomReal()+sb;
|
|
//--- search errors
|
|
fiterrors=fiterrors||CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v);
|
|
fiterrors=fiterrors||rep.m_taskrcond!=rep2.m_taskrcond;
|
|
fiterrors=fiterrors||rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors||rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors||rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors||rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- check basic properties of penalized splines which are
|
|
//--- preserved independently of Rho parameter.
|
|
for(m=4; m<=10; m++)
|
|
{
|
|
for(k=-5; k<=5; k++)
|
|
{
|
|
rho=k;
|
|
//--- when we have two points (even with different weights),
|
|
//--- resulting spline must be equal to the straight line
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
ArrayResize(w,2);
|
|
x[0]=-0.5-CMath::RandomReal();
|
|
y[0]=0.5+CMath::RandomReal();
|
|
w[0]=1+CMath::RandomReal();
|
|
x[1]=0.5+CMath::RandomReal();
|
|
y[1]=0.5+CMath::RandomReal();
|
|
w[1]=1+CMath::RandomReal();
|
|
//--- function call
|
|
CIntComp::Spline1DFitPenalized(x,y,2,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
v=2*CMath::RandomReal()-1;
|
|
v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
//--- function call
|
|
CIntComp::Spline1DFitPenalizedW(x,y,w,2,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
v=2*CMath::RandomReal()-1;
|
|
v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
//--- spline fitting is invariant with respect to
|
|
//--- scaling of weights (of course,ANY fitting algorithm
|
|
//--- must be invariant,but we want to test this property
|
|
//--- just to be sure that it is correctly implemented)
|
|
for(n=2; n<=2*m; n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(w2,n);
|
|
//--- change values
|
|
s=1+MathExp(10*CMath::RandomReal());
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=CMath::RandomReal();
|
|
w[i]=0.1+CMath::RandomReal();
|
|
w2[i]=w[i]*s;
|
|
}
|
|
//--- function calls
|
|
CIntComp::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
|
|
CIntComp::Spline1DFitPenalizedW(x,y,w2,n,m,rho,info2,c2,rep2);
|
|
//--- check
|
|
if(info>0 && info2>0)
|
|
{
|
|
v=CMath::RandomReal();
|
|
v1=CSpline1D::Spline1DCalc(c,v);
|
|
v2=CSpline1D::Spline1DCalc(c2,v);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(v1-v2)>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
}
|
|
}
|
|
}
|
|
//--- Advanced proprties:
|
|
//--- * penalized spline with M about 5*N and sufficiently small Rho
|
|
//--- must pass through all points on equidistant grid
|
|
for(n=2; n<=10; n++)
|
|
{
|
|
m=5*n;
|
|
rho=-5;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=CMath::RandomReal();
|
|
w[i]=0.1+CMath::RandomReal();
|
|
}
|
|
//--- function call
|
|
CIntComp::Spline1DFitPenalized(x,y,n,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
fiterrors=fiterrors||MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
//--- function call
|
|
CIntComp::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<n; i++)
|
|
fiterrors=fiterrors||MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
}
|
|
//--- Check correctness of error reports
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
|
|
return;
|
|
//--- solve simple task (all X[] are the same,Y[] are specially
|
|
//--- calculated to ensure simple form of all types of errors)
|
|
//--- and check correctness of the errors calculated by subroutines
|
|
//--- First pass is done with zero Y[],other passes - with random Y[].
|
|
//--- It should test both ability to correctly calculate errors and
|
|
//--- ability to not fail while working with zeros :)
|
|
n=4;
|
|
if(pass==1)
|
|
{
|
|
v1=0;
|
|
v2=0;
|
|
v=0;
|
|
}
|
|
else
|
|
{
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=v-v2;
|
|
w[0]=1;
|
|
x[1]=0;
|
|
y[1]=v-v1;
|
|
w[1]=1;
|
|
x[2]=0;
|
|
y[2]=v+v1;
|
|
w[2]=1;
|
|
x[3]=0;
|
|
y[3]=v+v2;
|
|
w[3]=1;
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
//--- check
|
|
if(pass==1)
|
|
refavgrel=0;
|
|
else
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test penalized spline
|
|
CIntComp::Spline1DFitPenalizedW(x,y,w,4,4,0.0,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CSpline1D::Spline1DCalc(c,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
//--- Test cubic fitting
|
|
CLSFit::Spline1DFitCubic(x,y,4,4,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CSpline1D::Spline1DCalc(c,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
//--- Test Hermite fitting
|
|
CLSFit::Spline1DFitHermite(x,y,4,4,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CSpline1D::Spline1DCalc(c,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors||MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLSFitUnit::TestGeneralFitting(bool &llserrors,bool &nlserrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double nlthreshold=0;
|
|
int maxn=0;
|
|
int maxm=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double xscale=0;
|
|
double diffstep=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double w2[];
|
|
double c[];
|
|
double c2[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble cm;
|
|
//--- objects of classes
|
|
CLSFitReport rep;
|
|
CLSFitReport rep2;
|
|
CLSFitState state;
|
|
//--- initialization
|
|
llserrors=false;
|
|
nlserrors=false;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nlthreshold=0.00001;
|
|
diffstep=0.0001;
|
|
maxn=6;
|
|
maxm=6;
|
|
passcount=4;
|
|
//--- Testing unconstrained least squares (linear/nonlinear)
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Solve non-degenerate linear least squares task
|
|
//--- Use Chebyshev basis. Its condition number is very good.
|
|
a.Resize(n,m);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
xscale=0.9+0.1*CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
x[i]=xscale*((double)(2*i)/(double)(n-1)-1);
|
|
//--- change values
|
|
y[i]=3*x[i]+MathExp(x[i]);
|
|
w[i]=1+CMath::RandomReal();
|
|
a.Set(i,0,1);
|
|
//--- check
|
|
if(m>1)
|
|
a.Set(i,1,x[i]);
|
|
for(j=2; j<m; j++)
|
|
a.Set(i,j,2*x[i]*a.Get(i,j-1)-a.Get(i,j-2));
|
|
}
|
|
//--- 1. test weighted fitting (optimality)
|
|
//--- 2. Solve degenerate least squares task built on the basis
|
|
//--- of previous task
|
|
CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
llserrors=llserrors||!IsGLSSolution(n,m,0,y,w,a,cm,c);
|
|
//--- allocation
|
|
a2.Resize(n,2*m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
{
|
|
a2.Set(i,2*j+0,a.Get(i,j));
|
|
a2.Set(i,2*j+1,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitLinearW(y,w,a2,n,2*m,info,c2,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- test answer correctness using design matrix properties
|
|
//--- and previous task solution
|
|
for(j=0; j<m; j++)
|
|
llserrors=llserrors||MathAbs(c2[2*j+0]+c2[2*j+1]-c[j])>threshold;
|
|
}
|
|
//--- test non-weighted fitting
|
|
ArrayResize(w2,n);
|
|
for(i=0; i<n; i++)
|
|
w2[i]=1;
|
|
//--- function calls
|
|
CLSFit::LSFitLinearW(y,w2,a,n,m,info,c,rep);
|
|
CLSFit::LSFitLinear(y,a,n,m,info2,c2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- test answer correctness
|
|
for(j=0; j<m; j++)
|
|
llserrors=llserrors||MathAbs(c[j]-c2[j])>threshold;
|
|
//--- search errors
|
|
llserrors=llserrors||MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold;
|
|
}
|
|
//--- test nonlinear fitting on the linear task
|
|
//--- (only non-degenerate tasks are tested)
|
|
//--- and compare with answer from linear fitting subroutine
|
|
if(n>=m)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(c2,m);
|
|
//--- test function/gradient/Hessian-based weighted fitting
|
|
CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep);
|
|
for(i=0; i<m; i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateWF(a,y,w,c2,n,m,m,diffstep,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
FitLinearNonlinear(m,0,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateWFG(a,y,w,c2,n,m,m,CMath::RandomReal()>0.5,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
FitLinearNonlinear(m,1,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateWFGH(a,y,w,c2,n,m,m,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
FitLinearNonlinear(m,2,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- test gradient-only or Hessian-based fitting without weights
|
|
CLSFit::LSFitLinear(y,a,n,m,info,c,rep);
|
|
for(i=0; i<m; i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateF(a,y,c2,n,m,m,diffstep,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
FitLinearNonlinear(m,0,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateFG(a,y,c2,n,m,m,CMath::RandomReal()>0.5,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
FitLinearNonlinear(m,1,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<m; i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateFGH(a,y,c2,n,m,m,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
FitLinearNonlinear(m,2,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test correctness of the RCond field
|
|
a.Resize(n,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- change values
|
|
v1=CMath::m_maxrealnumber;
|
|
v2=CMath::m_minrealnumber;
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x[i]=0.1+0.9*CMath::RandomReal();
|
|
y[i]=0.1+0.9*CMath::RandomReal();
|
|
w[i]=1;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
{
|
|
a.Set(i,i,0.1+0.9*CMath::RandomReal());
|
|
v1=MathMin(v1,a[i][i]);
|
|
v2=MathMax(v2,a[i][i]);
|
|
}
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitLinearW(y,w,a,n,n,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
llserrors=llserrors||MathAbs(rep.m_taskrcond-v1/v2)>threshold;
|
|
}
|
|
//--- Test constrained least squares
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(m=1; m<=maxm; m++)
|
|
{
|
|
//--- test for K<>0
|
|
for(k=1; k<m; k++)
|
|
{
|
|
//--- Prepare Chebyshev basis. Its condition number is very good.
|
|
//--- Prepare constraints (random numbers)
|
|
a.Resize(n,m);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
xscale=0.9+0.1*CMath::RandomReal();
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
x[i]=xscale*((double)(2*i)/(double)(n-1)-1);
|
|
//--- change values
|
|
y[i]=3*x[i]+MathExp(x[i]);
|
|
w[i]=1+CMath::RandomReal();
|
|
a.Set(i,0,1);
|
|
//--- check
|
|
if(m>1)
|
|
a.Set(i,1,x[i]);
|
|
for(j=2; j<m; j++)
|
|
a.Set(i,j,2*x[i]*a[i][j-1]-a[i][j-2]);
|
|
}
|
|
//--- allocation
|
|
cm.Resize(k,m+1);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<=m; j++)
|
|
cm.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- Solve constrained task
|
|
CLSFit::LSFitLinearWC(y,w,a,cm,n,m,k,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
llserrors=llserrors||!IsGLSSolution(n,m,k,y,w,a,cm,c);
|
|
//--- test non-weighted fitting
|
|
ArrayResize(w2,n);
|
|
for(i=0; i<n; i++)
|
|
w2[i]=1;
|
|
//--- function calls
|
|
CLSFit::LSFitLinearWC(y,w2,a,cm,n,m,k,info,c,rep);
|
|
CLSFit::LSFitLinearC(y,a,cm,n,m,k,info2,c2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- test answer correctness
|
|
for(j=0; j<m; j++)
|
|
llserrors=llserrors||MathAbs(c[j]-c2[j])>threshold;
|
|
llserrors=llserrors||MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- nonlinear task for nonlinear fitting:
|
|
//--- f(X,C)=1/(1+C*X^2),
|
|
//--- C(true)=2.
|
|
n=100;
|
|
ArrayResize(c,1);
|
|
c[0]=1+2*CMath::RandomReal();
|
|
//--- allocation
|
|
a.Resize(n,1);
|
|
ArrayResize(y,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,0,4*CMath::RandomReal()-2);
|
|
y[i]=1/(1+2*CMath::Sqr(a[i][0]));
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitCreateFG(a,y,c,n,1,1,true,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
//--- cycle
|
|
while(CLSFit::LSFitIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf)
|
|
state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0]));
|
|
state.m_g.Set(0,-(CMath::Sqr(state.m_x[0])/CMath::Sqr(1+state.m_c[0]*CMath::Sqr(state.m_x[0]))));
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitResults(state,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
nlserrors=nlserrors||MathAbs(c[0]-2)>100*nlthreshold;
|
|
//--- solve simple task (fitting by constant function) and check
|
|
//--- correctness of the errors calculated by subroutines
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- test on task with non-zero Yi
|
|
n=4;
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
//--- allocation
|
|
ArrayResize(c,1);
|
|
c[0]=1+2*CMath::RandomReal();
|
|
//--- allocation
|
|
a.Resize(4,1);
|
|
ArrayResize(y,4);
|
|
//--- change values
|
|
a.Set(0,0,1);
|
|
y[0]=v-v2;
|
|
a.Set(1,0,1);
|
|
y[1]=v-v1;
|
|
a.Set(2,0,1);
|
|
y[2]=v+v1;
|
|
a.Set(3,0,1);
|
|
y[3]=v+v2;
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test LLS
|
|
CLSFit::LSFitLinear(y,a,4,1,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
llserrors=llserrors||MathAbs(c[0]-v)>threshold;
|
|
llserrors=llserrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
llserrors=llserrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
llserrors=llserrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
llserrors=llserrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
//--- Test NLS
|
|
CLSFit::LSFitCreateFG(a,y,c,4,1,1,true,state);
|
|
CLSFit::LSFitSetCond(state,nlthreshold,0);
|
|
//--- cycle
|
|
while(CLSFit::LSFitIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf)
|
|
state.m_f=state.m_c[0];
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=state.m_c[0];
|
|
state.m_g.Set(0,1);
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitResults(state,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
nlserrors=nlserrors||MathAbs(c[0]-v)>threshold;
|
|
nlserrors=nlserrors||MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
nlserrors=nlserrors||MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
nlserrors=nlserrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
nlserrors=nlserrors||MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether C is solution of (possibly) constrained LLS problem|
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLSFitUnit::IsGLSSolution(const int n,const int m,const int k,
|
|
double &y[],double &w[],CMatrixDouble &fmatrix,
|
|
CMatrixDouble &cmatrix,double &cc[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
double delta=0;
|
|
double threshold=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double c[];
|
|
double c2[];
|
|
double sv[];
|
|
double deltac[];
|
|
double deltaproj[];
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
//--- copy array
|
|
ArrayCopy(c,cc);
|
|
//--- Setup.
|
|
//--- Threshold is small because CMatrix may be ill-conditioned
|
|
delta=0.001;
|
|
threshold=MathSqrt(CMath::m_machineepsilon);
|
|
//--- allocation
|
|
ArrayResize(c2,m);
|
|
ArrayResize(deltac,m);
|
|
ArrayResize(deltaproj,m);
|
|
//--- test whether C is feasible point or not (projC must be close to C)
|
|
for(i=0; i<k; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=cmatrix.Get(i,i_)*c[i_];
|
|
//--- check
|
|
if(MathAbs(v-cmatrix[i][m])>threshold)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
}
|
|
//--- find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1)
|
|
if(k>0)
|
|
CSingValueDecompose::RMatrixSVD(cmatrix,k,m,0,2,2,sv,u,vt);
|
|
//--- Test result
|
|
result=true;
|
|
s1=GetGLSError(n,m,y,w,fmatrix,c);
|
|
//--- calculation
|
|
for(j=0; j<m; j++)
|
|
{
|
|
//--- prepare modification of C which leave us in the feasible set.
|
|
//--- let deltaC be increment on Jth coordinate,then project
|
|
//--- deltaC in the Null(CMatrix) and store result in DeltaProj
|
|
for(i_=0; i_<m; i_++)
|
|
c2[i_]=c[i_];
|
|
for(i=0; i<m; i++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
deltac[i]=delta;
|
|
else
|
|
deltac[i]=0;
|
|
}
|
|
//--- check
|
|
if(k==0)
|
|
{
|
|
for(i_=0; i_<m; i_++)
|
|
deltaproj[i_]=deltac[i_];
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
deltaproj[i]=0;
|
|
for(i=k; i<m; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=vt.Get(i,i_)*deltac[i_];
|
|
for(i_=0; i_<m; i_++)
|
|
deltaproj[i_]=deltaproj[i_]+v*vt.Get(i,i_);
|
|
}
|
|
}
|
|
//--- now we have DeltaProj such that if C is feasible,
|
|
//--- then C+DeltaProj is feasible too
|
|
for(i_=0; i_<m; i_++)
|
|
c2[i_]=c[i_];
|
|
for(i_=0; i_<m; i_++)
|
|
c2[i_]=c2[i_]+deltaproj[i_];
|
|
s2=GetGLSError(n,m,y,w,fmatrix,c2);
|
|
for(i_=0; i_<m; i_++)
|
|
c2[i_]=c[i_];
|
|
for(i_=0; i_<m; i_++)
|
|
c2[i_]=c2[i_]-deltaproj[i_];
|
|
s3=GetGLSError(n,m,y,w,fmatrix,c2);
|
|
result=(result && s2>=(double)(s1/(1+threshold))) && s3>=(double)(s1/(1+threshold));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether C is solution of LLS problem |
|
|
//+------------------------------------------------------------------+
|
|
double CTestLSFitUnit::GetGLSError(const int n,const int m,double &y[],
|
|
double &w[],CMatrixDouble &fmatrix,double &c[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double v=0;
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0; i_<m; i_++)
|
|
v+=fmatrix.Get(i,i_)*c[i_];
|
|
result+=CMath::Sqr(w[i]*(v-y[i]));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for nonlinear fitting of linear problem |
|
|
//| DerAvailable: |
|
|
//| * 0 when only function value should be used |
|
|
//| * 1 when we can provide gradient/function |
|
|
//| * 2 when we can provide Hessian/gradient/function |
|
|
//| When something which is not permitted by DerAvailable is |
|
|
//| requested, this function sets NLSErrors to True. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLSFitUnit::FitLinearNonlinear(const int m,const int deravailable,
|
|
CMatrixDouble &xy,CLSFitState &state,
|
|
bool &nlserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- cycle
|
|
while(CLSFit::LSFitIteration(state))
|
|
{
|
|
//--- assume that one and only one of flags is set
|
|
//--- test that we didn't request hessian in hessian-free setting
|
|
if(deravailable<1 && state.m_needfg)
|
|
nlserrors=true;
|
|
//--- check
|
|
if(deravailable<2 && state.m_needfgh)
|
|
nlserrors=true;
|
|
i=0;
|
|
//--- check
|
|
if(state.m_needf)
|
|
i=i+1;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
i=i+1;
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
i=i+1;
|
|
//--- check
|
|
if(i!=1)
|
|
nlserrors=true;
|
|
//--- test that PointIndex is consistent with actual point passed
|
|
for(i=0; i<m; i++)
|
|
nlserrors=nlserrors||xy[state.m_pointindex][i]!=state.m_x[i];
|
|
//--- calculate
|
|
if(state.m_needf)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=state.m_x[i_]*state.m_c[i_];
|
|
state.m_f=v;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=state.m_x[i_]*state.m_c[i_];
|
|
state.m_f=v;
|
|
//--- copy
|
|
for(i_=0; i_<m; i_++)
|
|
state.m_g.Set(i_,state.m_x[i_]);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=state.m_x[i_]*state.m_c[i_];
|
|
state.m_f=v;
|
|
//--- copy
|
|
for(i_=0; i_<m; i_++)
|
|
state.m_g.Set(i_,state.m_x[i_]);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
state.m_h.Set(i,j,0);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPSpline |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPSplineUnit
|
|
{
|
|
public:
|
|
static bool TestPSpline(const bool silent);
|
|
|
|
private:
|
|
static void UnsetP2(CPSpline2Interpolant &p);
|
|
static void UnsetP3(CPSpline3Interpolant &p);
|
|
static void Unset1D(double &x[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPSpline |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestPSplineUnit::TestPSpline(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool p2errors;
|
|
bool p3errors;
|
|
double nonstrictthreshold=0;
|
|
double threshold=0;
|
|
int passcount=0;
|
|
double lstep=0;
|
|
double h=0;
|
|
int maxn=0;
|
|
int periodicity=0;
|
|
int skind=0;
|
|
int pkind=0;
|
|
bool periodic;
|
|
double a=0;
|
|
double b=0;
|
|
int n=0;
|
|
int tmpn=0;
|
|
int i=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double vz=0;
|
|
double vx2=0;
|
|
double vy2=0;
|
|
double vz2=0;
|
|
double vdx=0;
|
|
double vdy=0;
|
|
double vdz=0;
|
|
double vdx2=0;
|
|
double vdy2=0;
|
|
double vdz2=0;
|
|
double vd2x=0;
|
|
double vd2y=0;
|
|
double vd2z=0;
|
|
double vd2x2=0;
|
|
double vd2y2=0;
|
|
double vd2z2=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double z[];
|
|
double t[];
|
|
double t2[];
|
|
double t3[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xyz;
|
|
//--- objects of classes
|
|
CPSpline2Interpolant p2;
|
|
CPSpline3Interpolant p3;
|
|
CSpline1DInterpolant s;
|
|
//--- initialization
|
|
waserrors=false;
|
|
passcount=20;
|
|
lstep=0.005;
|
|
h=0.00001;
|
|
maxn=10;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nonstrictthreshold=0.00001;
|
|
p2errors=false;
|
|
p3errors=false;
|
|
//--- Test basic properties of 2- and 3-dimensional splines:
|
|
//--- * PSpline2ParameterValues() properties
|
|
//--- * values at nodes
|
|
//--- * for periodic splines - periodicity properties
|
|
//--- Variables used:
|
|
//--- * N points count
|
|
//--- * SKind spline
|
|
//--- * PKind parameterization
|
|
//--- * Periodicity whether we have periodic spline or not
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
for(skind=0; skind<=2; skind++)
|
|
{
|
|
for(pkind=0; pkind<=2; pkind++)
|
|
{
|
|
for(periodicity=0; periodicity<=1; periodicity++)
|
|
{
|
|
periodic=periodicity==1;
|
|
//--- skip unsupported combinations of parameters
|
|
if(periodic && n<3)
|
|
continue;
|
|
//--- check
|
|
if(periodic && skind==0)
|
|
continue;
|
|
//--- check
|
|
if(n<5 && skind==0)
|
|
continue;
|
|
//--- init
|
|
xy.Resize(n,2);
|
|
xyz.Resize(n,3);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,x);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xy.Set(i_,0,x[i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
xyz.Set(i_,0,x[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,y);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xy.Set(i_,1,y[i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
xyz.Set(i_,1,y[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,z);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xyz.Set(i_,2,z[i_]);
|
|
//--- function calls
|
|
UnsetP2(p2);
|
|
UnsetP3(p3);
|
|
//--- check
|
|
if(periodic)
|
|
{
|
|
CPSpline::PSpline2BuildPeriodic(xy,n,skind,pkind,p2);
|
|
CPSpline::PSpline3BuildPeriodic(xyz,n,skind,pkind,p3);
|
|
}
|
|
else
|
|
{
|
|
CPSpline::PSpline2Build(xy,n,skind,pkind,p2);
|
|
CPSpline::PSpline3Build(xyz,n,skind,pkind,p3);
|
|
}
|
|
//--- PSpline2ParameterValues() properties
|
|
CPSpline::PSpline2ParameterValues(p2,tmpn,t2);
|
|
//--- check
|
|
if(tmpn!=n)
|
|
{
|
|
p2errors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CPSpline::PSpline3ParameterValues(p3,tmpn,t3);
|
|
//--- check
|
|
if(tmpn!=n)
|
|
{
|
|
p3errors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
p2errors=p2errors||t2[0]!=0.0;
|
|
p3errors=p3errors||t3[0]!=0.0;
|
|
for(i=1; i<n; i++)
|
|
{
|
|
p2errors=p2errors||t2[i]<=t2[i-1];
|
|
p3errors=p3errors||t3[i]<=t3[i-1];
|
|
}
|
|
//--- check
|
|
if(periodic)
|
|
{
|
|
p2errors=p2errors||t2[n-1]>=1.0;
|
|
p3errors=p3errors||t3[n-1]>=1.0;
|
|
}
|
|
else
|
|
{
|
|
p2errors=p2errors||t2[n-1]!=1.0;
|
|
p3errors=p3errors||t3[n-1]!=1.0;
|
|
}
|
|
//--- Now we have parameter values stored at T,
|
|
//--- and want to test whether the actully correspond to
|
|
//--- points
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- 2-dimensional test
|
|
CPSpline::PSpline2Calc(p2,t2[i],vx,vy);
|
|
p2errors=p2errors||MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors||MathAbs(vy-y[i])>threshold;
|
|
//--- 3-dimensional test
|
|
CPSpline::PSpline3Calc(p3,t3[i],vx,vy,vz);
|
|
p3errors=p3errors||MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vz-z[i])>threshold;
|
|
}
|
|
//--- Test periodicity (if needed)
|
|
if(periodic)
|
|
{
|
|
//--- periodicity at nodes
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- 2-dimensional test
|
|
CPSpline::PSpline2Calc(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vy);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors||MathAbs(vy-y[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors||MathAbs(vy-y[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff2(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors||MathAbs(vy-y[i])>threshold;
|
|
//--- 3-dimensional test
|
|
CPSpline::PSpline3Calc(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vy,vz);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vz-z[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy,vz,vdz);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vz-z[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff2(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors||MathAbs(vz-z[i])>threshold;
|
|
}
|
|
//--- periodicity between nodes
|
|
v0=CMath::RandomReal();
|
|
CPSpline::PSpline2Calc(p2,v0,vx,vy);
|
|
CPSpline::PSpline2Calc(p2,v0+CMath::RandomInteger(10)-5,vx2,vy2);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
//--- function calls
|
|
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
|
|
CPSpline::PSpline3Calc(p3,v0+CMath::RandomInteger(10)-5,vx2,vy2,vz2);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
//--- near-boundary test for continuity of function values and derivatives:
|
|
//--- 2-dimensional curve
|
|
if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!"))
|
|
return(false);
|
|
//--- change values
|
|
v0=100*CMath::m_machineepsilon;
|
|
v1=1-v0;
|
|
//--- function calls
|
|
CPSpline::PSpline2Calc(p2,v0,vx,vy);
|
|
CPSpline::PSpline2Calc(p2,v1,vx2,vy2);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
//--- function calls
|
|
CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy);
|
|
CPSpline::PSpline2Diff(p2,v1,vx2,vdx2,vy2,vdy2);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors||MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p2errors=p2errors||MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
//--- function calls
|
|
CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y);
|
|
CPSpline::PSpline2Diff2(p2,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors||MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p2errors=p2errors||MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
//--- check
|
|
if(skind==2)
|
|
{
|
|
//--- second derivative test only for cubic splines
|
|
p2errors=p2errors||MathAbs(vd2x-vd2x2)>nonstrictthreshold;
|
|
p2errors=p2errors||MathAbs(vd2y-vd2y2)>nonstrictthreshold;
|
|
}
|
|
//--- near-boundary test for continuity of function values and derivatives:
|
|
//--- 3-dimensional curve
|
|
if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!"))
|
|
return(false);
|
|
//--- change values
|
|
v0=100*CMath::m_machineepsilon;
|
|
v1=1-v0;
|
|
//--- function calls
|
|
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
|
|
CPSpline::PSpline3Calc(p3,v1,vx2,vy2,vz2);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
//--- function calls
|
|
CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz);
|
|
CPSpline::PSpline3Diff(p3,v1,vx2,vdx2,vy2,vdy2,vz2,vdz2);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(vdz-vdz2)>nonstrictthreshold;
|
|
//--- function calls
|
|
CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
|
|
CPSpline::PSpline3Diff2(p3,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2,vz2,vdz2,vd2z2);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(vdz-vdz2)>nonstrictthreshold;
|
|
//--- check
|
|
if(skind==2)
|
|
{
|
|
//--- second derivative test only for cubic splines
|
|
p3errors=p3errors||MathAbs(vd2x-vd2x2)>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(vd2y-vd2y2)>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(vd2z-vd2z2)>nonstrictthreshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test differentiation,tangents,calculation between nodes.
|
|
//--- Because differentiation is done in parameterization/spline/periodicity
|
|
//--- oblivious manner,we don't have to test all possible combinations
|
|
//--- of spline types and parameterizations.
|
|
//--- Actually we test special combination with properties which allow us
|
|
//--- to easily solve this problem:
|
|
//--- * 2 (3) variables
|
|
//--- * first variable is sampled from equidistant grid on [0][1]
|
|
//--- * other variables are random
|
|
//--- * uniform parameterization is used
|
|
//--- * periodicity - none
|
|
//--- * spline type - any (we use cubic splines)
|
|
//--- Same problem allows us to test calculation BETWEEN nodes.
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- init
|
|
xy.Resize(n,2);
|
|
xyz.Resize(n,3);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(0,1,n,t,x);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xy.Set(i_,0,x[i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
xyz.Set(i_,0,x[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(0,1,n,t,y);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xy.Set(i_,1,y[i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
xyz.Set(i_,1,y[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(0,1,n,t,z);
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xyz.Set(i_,2,z[i_]);
|
|
//--- function call
|
|
UnsetP2(p2);
|
|
UnsetP3(p3);
|
|
CPSpline::PSpline2Build(xy,n,2,0,p2);
|
|
CPSpline::PSpline3Build(xyz,n,2,0,p3);
|
|
//--- Test 2D/3D spline:
|
|
//--- * build non-parametric cubic spline from T and X/Y
|
|
//--- * calculate its value and derivatives at V0
|
|
//--- * compare with Spline2Calc/Spline2Diff/Spline2Diff2
|
|
//--- Because of task properties both variants should
|
|
//--- return same answer.
|
|
v0=CMath::RandomReal();
|
|
CSpline1D::Spline1DBuildCubic(t,x,n,0,0.0,0,0.0,s);
|
|
CSpline1D::Spline1DDiff(s,v0,vx2,vdx2,vd2x2);
|
|
CSpline1D::Spline1DBuildCubic(t,y,n,0,0.0,0,0.0,s);
|
|
CSpline1D::Spline1DDiff(s,v0,vy2,vdy2,vd2y2);
|
|
CSpline1D::Spline1DBuildCubic(t,z,n,0,0.0,0,0.0,s);
|
|
CSpline1D::Spline1DDiff(s,v0,vz2,vdz2,vd2z2);
|
|
//--- 2D test
|
|
CPSpline::PSpline2Calc(p2,v0,vx,vy);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors||MathAbs(vdx-vdx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vdy-vdy2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors||MathAbs(vdx-vdx2)>threshold;
|
|
p2errors=p2errors||MathAbs(vdy-vdy2)>threshold;
|
|
p2errors=p2errors||MathAbs(vd2x-vd2x2)>threshold;
|
|
p2errors=p2errors||MathAbs(vd2y-vd2y2)>threshold;
|
|
//--- 3D test
|
|
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdx-vdx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdy-vdy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdz-vdz2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdx-vdx2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdy-vdy2)>threshold;
|
|
p3errors=p3errors||MathAbs(vdz-vdz2)>threshold;
|
|
p3errors=p3errors||MathAbs(vd2x-vd2x2)>threshold;
|
|
p3errors=p3errors||MathAbs(vd2y-vd2y2)>threshold;
|
|
p3errors=p3errors||MathAbs(vd2z-vd2z2)>threshold;
|
|
//--- Test tangents for 2D/3D
|
|
CPSpline::PSpline2Tangent(p2,v0,vx,vy);
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(vx-vdx2/CApServ::SafePythag2(vdx2,vdy2))>threshold;
|
|
p2errors=p2errors||MathAbs(vy-vdy2/CApServ::SafePythag2(vdx2,vdy2))>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Tangent(p3,v0,vx,vy,vz);
|
|
//--- search errors
|
|
p3errors=p3errors||MathAbs(vx-vdx2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
|
|
p3errors=p3errors||MathAbs(vy-vdy2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
|
|
p3errors=p3errors||MathAbs(vz-vdz2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
|
|
}
|
|
//--- Arc length test.
|
|
//--- Simple problem with easy solution (points on a straight line with
|
|
//--- uniform parameterization).
|
|
for(n=2; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
xyz.Resize(n,3);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xy.Set(i,0,i);
|
|
xy.Set(i,1,i);
|
|
xyz.Set(i,0,i);
|
|
xyz.Set(i,1,i);
|
|
xyz.Set(i,2,i);
|
|
}
|
|
//--- function calls
|
|
CPSpline::PSpline2Build(xy,n,1,0,p2);
|
|
CPSpline::PSpline3Build(xyz,n,1,0,p3);
|
|
a=CMath::RandomReal();
|
|
b=CMath::RandomReal();
|
|
//--- search errors
|
|
p2errors=p2errors||MathAbs(CPSpline::PSpline2ArcLength(p2,a,b)-(b-a)*MathSqrt(2)*(n-1))>nonstrictthreshold;
|
|
p3errors=p3errors||MathAbs(CPSpline::PSpline3ArcLength(p3,a,b)-(b-a)*MathSqrt(3)*(n-1))>nonstrictthreshold;
|
|
}
|
|
//--- report
|
|
waserrors=p2errors||p3errors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SPLINE INTERPOLATION");
|
|
//--- Normal tests
|
|
PrintResult("2D TEST",!p2errors);
|
|
PrintResult("3D TEST",!p3errors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline, i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
void CTestPSplineUnit::UnsetP2(CPSpline2Interpolant &p)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- allocation
|
|
xy.Resize(2,2);
|
|
//--- initialization
|
|
xy.Set(0,0,-1);
|
|
xy.Set(0,1,-1);
|
|
xy.Set(1,0,1);
|
|
xy.Set(1,1,1);
|
|
//--- function call
|
|
CPSpline::PSpline2Build(xy,2,1,0,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline, i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
void CTestPSplineUnit::UnsetP3(CPSpline3Interpolant &p)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- allocation
|
|
xy.Resize(2,3);
|
|
//--- initialization
|
|
xy.Set(0,0,-1);
|
|
xy.Set(0,1,-1);
|
|
xy.Set(0,2,-1);
|
|
xy.Set(1,0,1);
|
|
xy.Set(1,1,1);
|
|
xy.Set(1,2,1);
|
|
//--- function call
|
|
CPSpline::PSpline3Build(xy,2,1,0,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
void CTestPSplineUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline2D |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpline2DUnit
|
|
{
|
|
public:
|
|
static bool TestSpline2D(const bool silent);
|
|
static void LConst(CSpline2DInterpolant &c,double &lx[],double &ly[],const int m,const int n,const double lstep,double &lc,double &lcx,double &lcy,double &lcxy);
|
|
static void TwodNumder(CSpline2DInterpolant &c,const double x,const double y,const double h,double &f,double &fx,double &fy,double &fxy);
|
|
static bool TestUnpack(CSpline2DInterpolant &c,double &lx[],double &ly[]);
|
|
static bool TestLinTrans(CSpline2DInterpolant &c,const double ax,const double bx,const double ay,const double by);
|
|
static void UnsetSpline2D(CSpline2DInterpolant &c);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline2D |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline2DUnit::TestSpline2D(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool blerrors;
|
|
bool bcerrors;
|
|
bool dserrors;
|
|
bool cperrors;
|
|
bool uperrors;
|
|
bool lterrors;
|
|
bool syerrors;
|
|
bool rlerrors;
|
|
bool rcerrors;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int jobtype=0;
|
|
double lstep=0;
|
|
double h=0;
|
|
double ax=0;
|
|
double ay=0;
|
|
double bx=0;
|
|
double by=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int n2=0;
|
|
int m2=0;
|
|
double err=0;
|
|
double t=0;
|
|
double t1=0;
|
|
double t2=0;
|
|
double l1=0;
|
|
double l1x=0;
|
|
double l1y=0;
|
|
double l1xy=0;
|
|
double l2=0;
|
|
double l2x=0;
|
|
double l2y=0;
|
|
double l2xy=0;
|
|
double fm=0;
|
|
double f1=0;
|
|
double f2=0;
|
|
double f3=0;
|
|
double f4=0;
|
|
double v1=0;
|
|
double v1x=0;
|
|
double v1y=0;
|
|
double v1xy=0;
|
|
double v2=0;
|
|
double v2x=0;
|
|
double v2y=0;
|
|
double v2xy=0;
|
|
double mf=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double lx[];
|
|
double ly[];
|
|
//--- create matrix
|
|
CMatrixDouble f;
|
|
CMatrixDouble fr;
|
|
CMatrixDouble ft;
|
|
//--- objects of classes
|
|
CSpline2DInterpolant c;
|
|
CSpline2DInterpolant c2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
passcount=10;
|
|
h=0.00001;
|
|
lstep=0.001;
|
|
blerrors=false;
|
|
bcerrors=false;
|
|
dserrors=false;
|
|
cperrors=false;
|
|
uperrors=false;
|
|
lterrors=false;
|
|
syerrors=false;
|
|
rlerrors=false;
|
|
rcerrors=false;
|
|
//--- Test: bilinear,bicubic
|
|
for(n=2; n<=7; n++)
|
|
{
|
|
for(m=2; m<=7; m++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,m);
|
|
ArrayResize(lx,2*n-2+1);
|
|
ArrayResize(ly,2*m-2+1);
|
|
f.Resize(m,n);
|
|
//--- calculation
|
|
ft.Resize(n,m);
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Prepare task:
|
|
//--- * X and Y stores grid
|
|
//--- * F stores function values
|
|
//--- * LX and LY stores twice dense grid (for Lipschitz testing)
|
|
ax=-1-CMath::RandomReal();
|
|
bx=1+CMath::RandomReal();
|
|
ay=-1-CMath::RandomReal();
|
|
by=1+CMath::RandomReal();
|
|
for(j=0; j<n; j++)
|
|
{
|
|
x[j]=0.5*(bx+ax)-0.5*(bx-ax)*MathCos(M_PI*(2*j+1)/(2*n));
|
|
//--- check
|
|
if(j==0)
|
|
x[j]=ax;
|
|
//--- check
|
|
if(j==n-1)
|
|
x[j]=bx;
|
|
lx[2*j]=x[j];
|
|
//--- check
|
|
if(j>0)
|
|
lx[2*j-1]=0.5*(x[j]+x[j-1]);
|
|
}
|
|
//--- swap
|
|
for(j=0; j<n; j++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=j)
|
|
{
|
|
t=x[j];
|
|
x[j]=x[k];
|
|
x[k]=t;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
y[i]=0.5*(by+ay)-0.5*(by-ay)*MathCos(M_PI*(2*i+1)/(2*m));
|
|
//--- check
|
|
if(i==0)
|
|
y[i]=ay;
|
|
//--- check
|
|
if(i==m-1)
|
|
y[i]=by;
|
|
ly[2*i]=y[i];
|
|
//--- check
|
|
if(i>0)
|
|
ly[2*i-1]=0.5*(y[i]+y[i-1]);
|
|
}
|
|
//--- swap
|
|
for(i=0; i<m; i++)
|
|
{
|
|
k=CMath::RandomInteger(m);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
}
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
f.Set(i,j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i]));
|
|
}
|
|
//--- Test bilinear interpolation:
|
|
//--- * interpolation at the nodes
|
|
//--- * linearity
|
|
//--- * continuity
|
|
//--- * differentiation in the inner points
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=MathMax(err,MathAbs(f.Get(i,j)-CSpline2D::Spline2DCalc(c,x[j],y[i])));
|
|
}
|
|
//--- search errors
|
|
blerrors=blerrors||err>10000*CMath::m_machineepsilon;
|
|
err=0;
|
|
for(i=0; i<=m-2; i++)
|
|
{
|
|
for(j=0; j<=n-2; j++)
|
|
{
|
|
//--- Test for linearity between grid points
|
|
//--- (test point - geometric center of the cell)
|
|
fm=CSpline2D::Spline2DCalc(c,lx[2*j+1],ly[2*i+1]);
|
|
f1=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i]);
|
|
f2=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i]);
|
|
f3=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i+2]);
|
|
f4=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i+2]);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(0.25*(f1+f2+f3+f4)-fm));
|
|
}
|
|
}
|
|
//--- search errors
|
|
blerrors=blerrors||err>10000*CMath::m_machineepsilon;
|
|
//--- function calls
|
|
LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy);
|
|
LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy);
|
|
//--- search errors
|
|
blerrors=blerrors||l2/l1>1.2;
|
|
err=0;
|
|
for(i=0; i<=m-2; i++)
|
|
{
|
|
for(j=0; j<=n-2; j++)
|
|
{
|
|
CSpline2D::Spline2DDiff(c,lx[2*j+1],ly[2*i+1],v1,v1x,v1y,v1xy);
|
|
TwodNumder(c,lx[2*j+1],ly[2*i+1],h,v2,v2x,v2y,v2xy);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
err=MathMax(err,MathAbs(v1x-v2x));
|
|
err=MathMax(err,MathAbs(v1y-v2y));
|
|
err=MathMax(err,MathAbs(v1xy-v2xy));
|
|
}
|
|
}
|
|
//--- search errors
|
|
dserrors=dserrors||err>1.0E-3;
|
|
uperrors=uperrors||!TestUnpack(c,lx,ly);
|
|
lterrors=lterrors||!TestLinTrans(c,ax,bx,ay,by);
|
|
//--- Test bicubic interpolation.
|
|
//--- * interpolation at the nodes
|
|
//--- * smoothness
|
|
//--- * differentiation
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
err=MathMax(err,MathAbs(f.Get(i,j)-CSpline2D::Spline2DCalc(c,x[j],y[i])));
|
|
}
|
|
//--- search errors
|
|
bcerrors=bcerrors||err>10000*CMath::m_machineepsilon;
|
|
LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy);
|
|
LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy);
|
|
//--- search errors
|
|
bcerrors=bcerrors||l2/l1>1.2;
|
|
bcerrors=bcerrors||l2x/l1x>1.2;
|
|
bcerrors=bcerrors||l2y/l1y>1.2;
|
|
//--- check
|
|
if(l2xy>0.01 && l1xy>0.01)
|
|
{
|
|
//--- Cross-derivative continuity is tested only when
|
|
//--- bigger than 0.01. When the task size is too
|
|
//--- small,the d2F/dXdY is nearly zero and Lipschitz
|
|
//--- constant ratio is ill-conditioned.
|
|
bcerrors=bcerrors||l2xy/l1xy>1.2;
|
|
}
|
|
err=0;
|
|
for(i=0; i<=2*m-2; i++)
|
|
{
|
|
for(j=0; j<=2*n-2; j++)
|
|
{
|
|
CSpline2D::Spline2DDiff(c,lx[j],ly[i],v1,v1x,v1y,v1xy);
|
|
TwodNumder(c,lx[j],ly[i],h,v2,v2x,v2y,v2xy);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
err=MathMax(err,MathAbs(v1x-v2x));
|
|
err=MathMax(err,MathAbs(v1y-v2y));
|
|
err=MathMax(err,MathAbs(v1xy-v2xy));
|
|
}
|
|
}
|
|
//--- search errors
|
|
dserrors=dserrors||err>1.0E-3;
|
|
uperrors=uperrors||!TestUnpack(c,lx,ly);
|
|
lterrors=lterrors||!TestLinTrans(c,ax,bx,ay,by);
|
|
//--- Copy/Serialise test
|
|
if(CMath::RandomReal()>0.5)
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
else
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
//--- function calls
|
|
UnsetSpline2D(c2);
|
|
CSpline2D::Spline2DCopy(c,c2);
|
|
//--- calculation
|
|
err=0;
|
|
for(i=1; i<=5; i++)
|
|
{
|
|
t1=ax+(bx-ax)*CMath::RandomReal();
|
|
t2=ay+(by-ay)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t1,t2)));
|
|
}
|
|
//--- search errors
|
|
cperrors=cperrors||err>10000*CMath::m_machineepsilon;
|
|
//--- Special symmetry test
|
|
err=0;
|
|
for(jobtype=0; jobtype<=1; jobtype++)
|
|
{
|
|
//--- Prepare
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ft.Set(j,i,f.Get(i,j));
|
|
}
|
|
//--- check
|
|
if(jobtype==0)
|
|
{
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
CSpline2D::Spline2DBuildBilinear(y,x,ft,n,m,c2);
|
|
}
|
|
else
|
|
{
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
CSpline2D::Spline2DBuildBicubic(y,x,ft,n,m,c2);
|
|
}
|
|
//--- Test
|
|
for(i=1; i<=10; i++)
|
|
{
|
|
t1=ax+(bx-ax)*CMath::RandomReal();
|
|
t2=ay+(by-ay)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t2,t1)));
|
|
}
|
|
}
|
|
//--- search errors
|
|
syerrors=syerrors||err>10000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//--- Test resample
|
|
for(m=2; m<=6; m++)
|
|
{
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
//--- allocation
|
|
f.Resize(m,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,m);
|
|
//--- change values
|
|
for(j=0; j<n; j++)
|
|
x[j]=(double)j/(double)(n-1);
|
|
for(i=0; i<m; i++)
|
|
y[i]=(double)i/(double)(m-1);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
f.Set(i,j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i]));
|
|
}
|
|
//--- calculation
|
|
for(m2=2; m2<=6; m2++)
|
|
{
|
|
for(n2=2; n2<=6; n2++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(jobtype=0; jobtype<=1; jobtype++)
|
|
{
|
|
//--- check
|
|
if(jobtype==0)
|
|
{
|
|
CSpline2D::Spline2DResampleBilinear(f,m,n,fr,m2,n2);
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
}
|
|
//--- check
|
|
if(jobtype==1)
|
|
{
|
|
CSpline2D::Spline2DResampleBicubic(f,m,n,fr,m2,n2);
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
}
|
|
//--- change values
|
|
err=0;
|
|
mf=0;
|
|
//--- calculation
|
|
for(i=0; i<=m2-1; i++)
|
|
{
|
|
for(j=0; j<n2 ; j++)
|
|
{
|
|
v1=CSpline2D::Spline2DCalc(c,(double)j/(double)(n2-1),(double)i/(double)(m2-1));
|
|
v2=fr.Get(i,j);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
mf=MathMax(mf,MathAbs(v1));
|
|
}
|
|
}
|
|
//--- check
|
|
if(jobtype==0)
|
|
rlerrors=rlerrors||err/mf>10000*CMath::m_machineepsilon;
|
|
//--- check
|
|
if(jobtype==1)
|
|
rcerrors=rcerrors||err/mf>10000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=(((((((blerrors||bcerrors)||dserrors)||cperrors)||uperrors)||lterrors)||syerrors)||rlerrors)||rcerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING 2D INTERPOLATION");
|
|
//--- Normal tests
|
|
PrintResult("BILINEAR TEST",!blerrors);
|
|
PrintResult("BICUBIC TEST",!bcerrors);
|
|
PrintResult("DIFFERENTIATION TEST",!dserrors);
|
|
PrintResult("COPY/SERIALIZE TEST",!cperrors);
|
|
PrintResult("UNPACK TEST",!uperrors);
|
|
PrintResult("LIN.TRANS. TEST",!lterrors);
|
|
PrintResult("SPECIAL SYMMETRY TEST",!syerrors);
|
|
PrintResult("BILINEAR RESAMPLING TEST",!rlerrors);
|
|
PrintResult("BICUBIC RESAMPLING TEST",!rcerrors);
|
|
//--- Summary
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lipschitz constants for spline inself,first and second |
|
|
//| derivatives. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline2DUnit::LConst(CSpline2DInterpolant &c,double &lx[],
|
|
double &ly[],const int m,const int n,
|
|
const double lstep,double &lc,
|
|
double &lcx,double &lcy,double &lcxy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double f1=0;
|
|
double f2=0;
|
|
double f3=0;
|
|
double f4=0;
|
|
double fx1=0;
|
|
double fx2=0;
|
|
double fx3=0;
|
|
double fx4=0;
|
|
double fy1=0;
|
|
double fy2=0;
|
|
double fy3=0;
|
|
double fy4=0;
|
|
double fxy1=0;
|
|
double fxy2=0;
|
|
double fxy3=0;
|
|
double fxy4=0;
|
|
double s2lstep=0;
|
|
//--- initialization
|
|
lc=0;
|
|
lcx=0;
|
|
lcy=0;
|
|
lcxy=0;
|
|
s2lstep=MathSqrt(2)*lstep;
|
|
//--- calculation
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- Calculate
|
|
TwodNumder(c,lx[j]-lstep/2,ly[i]-lstep/2,lstep/4,f1,fx1,fy1,fxy1);
|
|
TwodNumder(c,lx[j]+lstep/2,ly[i]-lstep/2,lstep/4,f2,fx2,fy2,fxy2);
|
|
TwodNumder(c,lx[j]+lstep/2,ly[i]+lstep/2,lstep/4,f3,fx3,fy3,fxy3);
|
|
TwodNumder(c,lx[j]-lstep/2,ly[i]+lstep/2,lstep/4,f4,fx4,fy4,fxy4);
|
|
//--- Lipschitz constant for the function itself
|
|
lc=MathMax(lc,MathAbs((f1-f2)/lstep));
|
|
lc=MathMax(lc,MathAbs((f2-f3)/lstep));
|
|
lc=MathMax(lc,MathAbs((f3-f4)/lstep));
|
|
lc=MathMax(lc,MathAbs((f4-f1)/lstep));
|
|
lc=MathMax(lc,MathAbs((f1-f3)/s2lstep));
|
|
lc=MathMax(lc,MathAbs((f2-f4)/s2lstep));
|
|
//--- Lipschitz constant for the first derivative
|
|
lcx=MathMax(lcx,MathAbs((fx1-fx2)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx2-fx3)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx3-fx4)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx4-fx1)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx1-fx3)/s2lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx2-fx4)/s2lstep));
|
|
//--- Lipschitz constant for the first derivative
|
|
lcy=MathMax(lcy,MathAbs((fy1-fy2)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy2-fy3)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy3-fy4)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy4-fy1)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy1-fy3)/s2lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy2-fy4)/s2lstep));
|
|
//--- Lipschitz constant for the cross-derivative
|
|
lcxy=MathMax(lcxy,MathAbs((fxy1-fxy2)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy2-fxy3)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy3-fxy4)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy4-fxy1)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy1-fxy3)/s2lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy2-fxy4)/s2lstep));
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Numerical differentiation. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline2DUnit::TwodNumder(CSpline2DInterpolant &c,const double x,
|
|
const double y,const double h,
|
|
double &f,double &fx,double &fy,
|
|
double &fxy)
|
|
{
|
|
//--- calculation
|
|
f=CSpline2D::Spline2DCalc(c,x,y);
|
|
fx=(CSpline2D::Spline2DCalc(c,x+h,y)-CSpline2D::Spline2DCalc(c,x-h,y))/(2*h);
|
|
fy=(CSpline2D::Spline2DCalc(c,x,y+h)-CSpline2D::Spline2DCalc(c,x,y-h))/(2*h);
|
|
fxy=(CSpline2D::Spline2DCalc(c,x+h,y+h)-CSpline2D::Spline2DCalc(c,x-h,y+h)-CSpline2D::Spline2DCalc(c,x+h,y-h)+CSpline2D::Spline2DCalc(c,x-h,y-h))/CMath::Sqr(2*h);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpack test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline2DUnit::TestUnpack(CSpline2DInterpolant &c,double &lx[],
|
|
double &ly[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int ci=0;
|
|
int cj=0;
|
|
int p=0;
|
|
double err=0;
|
|
double tx=0;
|
|
double ty=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create matrix
|
|
CMatrixDouble tbl;
|
|
//--- initialization
|
|
passcount=20;
|
|
err=0;
|
|
//--- function call
|
|
CSpline2D::Spline2DUnpack(c,m,n,tbl);
|
|
//--- calculation
|
|
for(i=0; i<=m-2; i++)
|
|
{
|
|
for(j=0; j<=n-2; j++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
p=(n-1)*i+j;
|
|
tx=(0.001+0.999*CMath::RandomReal())*(tbl[p][1]-tbl[p][0]);
|
|
ty=(0.001+0.999*CMath::RandomReal())*(tbl[p][3]-tbl[p][2]);
|
|
//--- Interpolation properties
|
|
v1=0;
|
|
for(ci=0; ci<=3; ci++)
|
|
{
|
|
for(cj=0; cj<=3; cj++)
|
|
v1+=tbl[p][4+ci*4+cj]*MathPow(tx,ci)*MathPow(ty,cj);
|
|
}
|
|
v2=CSpline2D::Spline2DCalc(c,tbl[p][0]+tx,tbl[p][2]+ty);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
//--- Grid correctness
|
|
err=MathMax(err,MathAbs(lx[2*j]-tbl[p][0]));
|
|
err=MathMax(err,MathAbs(lx[2*(j+1)]-tbl[p][1]));
|
|
err=MathMax(err,MathAbs(ly[2*i]-tbl[p][2]));
|
|
err=MathMax(err,MathAbs(ly[2*(i+1)]-tbl[p][3]));
|
|
}
|
|
}
|
|
}
|
|
//--- get result
|
|
result=err<10000*CMath::m_machineepsilon;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LinTrans test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline2DUnit::TestLinTrans(CSpline2DInterpolant &c,
|
|
const double ax,const double bx,
|
|
const double ay,const double by)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
double err=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
double b1=0;
|
|
double b2=0;
|
|
double tx=0;
|
|
double ty=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int xjob=0;
|
|
int yjob=0;
|
|
//--- object of class
|
|
CSpline2DInterpolant c2;
|
|
//--- initialization
|
|
passcount=5;
|
|
err=0;
|
|
//--- calculation
|
|
for(xjob=0; xjob<=1; xjob++)
|
|
{
|
|
for(yjob=0; yjob<=1; yjob++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Prepare
|
|
do
|
|
{
|
|
a1=2*CMath::RandomReal()-1;
|
|
}
|
|
while(a1==0.0);
|
|
//--- change values
|
|
a1=a1*xjob;
|
|
b1=2*CMath::RandomReal()-1;
|
|
do
|
|
{
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
while(a2==0.0);
|
|
//--- change values
|
|
a2=a2*yjob;
|
|
b2=2*CMath::RandomReal()-1;
|
|
//--- Test XY
|
|
CSpline2D::Spline2DCopy(c,c2);
|
|
CSpline2D::Spline2DLinTransXY(c2,a1,b1,a2,b2);
|
|
tx=ax+CMath::RandomReal()*(bx-ax);
|
|
ty=ay+CMath::RandomReal()*(by-ay);
|
|
//--- check
|
|
if(xjob==0)
|
|
{
|
|
tx=b1;
|
|
vx=ax+CMath::RandomReal()*(bx-ax);
|
|
}
|
|
else
|
|
vx=(tx-b1)/a1;
|
|
//--- check
|
|
if(yjob==0)
|
|
{
|
|
ty=b2;
|
|
vy=ay+CMath::RandomReal()*(by-ay);
|
|
}
|
|
else
|
|
vy=(ty-b2)/a2;
|
|
v1=CSpline2D::Spline2DCalc(c,tx,ty);
|
|
v2=CSpline2D::Spline2DCalc(c2,vx,vy);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
//--- Test F
|
|
CSpline2D::Spline2DCopy(c,c2);
|
|
CSpline2D::Spline2DLinTransF(c2,a1,b1);
|
|
//--- change values
|
|
tx=ax+CMath::RandomReal()*(bx-ax);
|
|
ty=ay+CMath::RandomReal()*(by-ay);
|
|
v1=CSpline2D::Spline2DCalc(c,tx,ty);
|
|
v2=CSpline2D::Spline2DCalc(c2,tx,ty);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(a1*v1+b1-v2));
|
|
}
|
|
}
|
|
}
|
|
//--- get result
|
|
result=err<10000*CMath::m_machineepsilon;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline,i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline2DUnit::UnsetSpline2D(CSpline2DInterpolant &c)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble f;
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
f.Resize(2,2);
|
|
//--- initialization
|
|
x[0]=-1;
|
|
x[1]=1;
|
|
y[0]=-1;
|
|
y[1]=1;
|
|
f.Set(0,0,0);
|
|
f.Set(0,1,0);
|
|
f.Set(1,0,0);
|
|
f.Set(1,1,0);
|
|
//--- function call
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,2,2,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpdGEVD |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpdGEVDUnit
|
|
{
|
|
public:
|
|
static bool TestSpdGEVD(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpdGEVD |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpdGEVDUnit::TestSpdGEVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int atask=0;
|
|
int btask=0;
|
|
bool isuppera;
|
|
bool isupperb;
|
|
int i=0;
|
|
int j=0;
|
|
int minij=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double err=0;
|
|
double valerr=0;
|
|
double threshold=0;
|
|
bool waserrors;
|
|
bool wfailed;
|
|
bool wnsorted;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double t1[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble afull;
|
|
CMatrixDouble bfull;
|
|
CMatrixDouble l;
|
|
CMatrixDouble z;
|
|
//--- initialization
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
valerr=0;
|
|
wfailed=false;
|
|
wnsorted=false;
|
|
maxn=20;
|
|
passcount=5;
|
|
//--- Main cycle
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(atask=0; atask<=1; atask++)
|
|
{
|
|
for(btask=0; btask<=1; btask++)
|
|
{
|
|
isuppera=atask==0;
|
|
isupperb=btask==0;
|
|
//--- Initialize A,B,AFull,BFull
|
|
ArrayResize(t1,n);
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
afull.Resize(n,n);
|
|
bfull.Resize(n,n);
|
|
l.Resize(n,n);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
afull.Set(i,j,a.Get(i,j));
|
|
afull.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
l.Set(i,j,CMath::RandomReal());
|
|
l.Set(j,i,l.Get(i,j));
|
|
}
|
|
l.Set(i,i,1.5+CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
minij=MathMin(i,j);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=minij; i_++)
|
|
{
|
|
v+=l.Get(i,i_)*l.Get(i_,j);
|
|
}
|
|
b.Set(i,j,v);
|
|
b.Set(j,i,v);
|
|
bfull.Set(i,j,v);
|
|
bfull.Set(j,i,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(isuppera)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i<j)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- check
|
|
if(isupperb)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
b.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i<j)
|
|
b.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
}
|
|
//--- Problem 1
|
|
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,1,d,z))
|
|
{
|
|
wfailed=true;
|
|
continue;
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v1=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v1+=afull.Get(i,i_)*z.Get(i_,j);
|
|
v2=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v2+=bfull.Get(i,i_)*z.Get(i_,j);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-d[j]*v2));
|
|
}
|
|
}
|
|
//--- search errors
|
|
valerr=MathMax(err,valerr);
|
|
//--- Problem 2
|
|
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,2,d,z))
|
|
{
|
|
wfailed=true;
|
|
continue;
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v1=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v1+=bfull.Get(i,i_)*z.Get(i_,j);
|
|
t1[i]=v1;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v2=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v2+=afull.Get(i,i_)*t1[i_];
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v2-d[j]*z.Get(i,j)));
|
|
}
|
|
}
|
|
//--- search errors
|
|
valerr=MathMax(err,valerr);
|
|
//--- Test problem 3
|
|
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,3,d,z))
|
|
{
|
|
wfailed=true;
|
|
continue;
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v1=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v1+=afull.Get(i,i_)*z.Get(i_,j);
|
|
t1[i]=v1;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v2=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v2+=bfull.Get(i,i_)*t1[i_];
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v2-d[j]*z.Get(i,j)));
|
|
}
|
|
}
|
|
//--- search errors
|
|
valerr=MathMax(err,valerr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=(valerr>threshold||wfailed)||wnsorted;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SYMMETRIC GEVD");
|
|
PrintFormat("Av-lambdav error (generalized): %5.3E",valerr);
|
|
PrintResult("Eigen values order",!wnsorted);
|
|
PrintFormat("Always converged: " + (!wfailed ? "YES" : "NO"));
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CInverseUpdate |
|
|
//+------------------------------------------------------------------+
|
|
class CTestInverseUpdateUnit
|
|
{
|
|
public:
|
|
static bool TestInverseUpdate(const bool silent);
|
|
|
|
private:
|
|
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void MatLU(CMatrixDouble &a,const int m,const int n,int &pivots[]);
|
|
static void GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,const int n);
|
|
static void GenerateRandomMatrixCond(CMatrixDouble &a0,const int n,const double c);
|
|
static bool InvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool InvMatLU(CMatrixDouble &a,int &pivots[],const int n);
|
|
static bool InvMat(CMatrixDouble &a,const int n);
|
|
static double MatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n);
|
|
static bool UpdAndInv(CMatrixDouble &a,double &u[],double &v[],const int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CInverseUpdate |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestInverseUpdateUnit::TestInverseUpdate(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int updrow=0;
|
|
int updcol=0;
|
|
double val=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
double threshold=0;
|
|
double c=0;
|
|
//--- create arrays
|
|
double u[];
|
|
double v[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble inva;
|
|
CMatrixDouble b1;
|
|
CMatrixDouble b2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
maxn=10;
|
|
passcount=100;
|
|
threshold=1.0E-6;
|
|
//--- process
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b1.Resize(n,n);
|
|
b2.Resize(n,n);
|
|
ArrayResize(u,n);
|
|
ArrayResize(v,n);
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
c=MathExp(CMath::RandomReal()*MathLog(10));
|
|
GenerateRandomMatrixCond(a,n,c);
|
|
MakeACopy(a,n,n,inva);
|
|
//--- check
|
|
if(!InvMat(inva,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- Test simple update
|
|
updrow=CMath::RandomInteger(n);
|
|
updcol=CMath::RandomInteger(n);
|
|
val=0.1*(2*CMath::RandomReal()-1);
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i==updrow)
|
|
u[i]=val;
|
|
else
|
|
u[i]=0;
|
|
//--- check
|
|
if(i==updcol)
|
|
v[i]=1;
|
|
else
|
|
v[i]=0;
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateSimple(b2,n,updrow,updcol,val);
|
|
//--- search errors
|
|
waserrors=waserrors||MatrixDiff(b1,b2,n,n)>threshold;
|
|
//--- Test row update
|
|
updrow=CMath::RandomInteger(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i==updrow)
|
|
u[i]=1;
|
|
else
|
|
u[i]=0;
|
|
v[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateRow(b2,n,updrow,v);
|
|
//--- search errors
|
|
waserrors=waserrors||MatrixDiff(b1,b2,n,n)>threshold;
|
|
//--- Test column update
|
|
updcol=CMath::RandomInteger(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i==updcol)
|
|
v[i]=1;
|
|
else
|
|
v[i]=0;
|
|
u[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateColumn(b2,n,updcol,u);
|
|
//--- search errors
|
|
waserrors=waserrors||MatrixDiff(b1,b2,n,n)>threshold;
|
|
//--- Test full update
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v[i]=0.1*(2*CMath::RandomReal()-1);
|
|
u[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateUV(b2,n,u,v);
|
|
//--- search errors
|
|
waserrors=waserrors||MatrixDiff(b1,b2,n,n)>threshold;
|
|
}
|
|
}
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING INVERSE UPDATE (REAL)");
|
|
//--- check
|
|
PrintResult("TEST",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CTestInverseUpdateUnit::MakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU decomposition |
|
|
//+------------------------------------------------------------------+
|
|
void CTestInverseUpdateUnit::MatLU(CMatrixDouble &a,const int m,
|
|
const int n,int &pivots[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double s=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t1[];
|
|
//--- allocation
|
|
ArrayResize(pivots,MathMin(m-1,n-1)+1);
|
|
ArrayResize(t1,MathMax(m-1,n-1)+1);
|
|
//--- check
|
|
if(!CAp::Assert(m>=0 && n>=0,"Error in LUDecomposition: incorrect function arguments"))
|
|
return;
|
|
//--- Quick return if possible
|
|
if(m==0 || n==0)
|
|
return;
|
|
//--- calculation
|
|
for(j=0; j<=MathMin(m-1,n-1); j++)
|
|
{
|
|
//--- Find pivot and test for singularity.
|
|
jp=j;
|
|
for(i=j+1; i<m; i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(a.Get(i,j))>MathAbs(a[jp][j]))
|
|
jp=i;
|
|
}
|
|
pivots[j]=jp;
|
|
//--- check
|
|
if(a[jp][j]!=0.0)
|
|
{
|
|
//--- Apply the interchange to rows
|
|
if(jp!=j)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
t1[i_]=a.Get(j,i_);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(j,i_,a[jp][i_]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(jp,i_,t1[i_]);
|
|
}
|
|
//--- Compute elements J+1:M of J-th column.
|
|
if(j<m)
|
|
{
|
|
jp=j+1;
|
|
s=1/a[j][j];
|
|
for(i_=jp; i_<m; i_++)
|
|
a.Set(i_,j,s*a.Get(i_,j));
|
|
}
|
|
}
|
|
//--- check
|
|
if(j<MathMin(m,n)-1)
|
|
{
|
|
//--- Update trailing submatrix.
|
|
jp=j+1;
|
|
for(i=j+1; i<m; i++)
|
|
{
|
|
s=a.Get(i,j);
|
|
for(i_=jp; i_<n; i_++)
|
|
a.Set(i,i_,a.Get(i,i_)-s*a.Get(j,i_));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate matrix with given condition number C (2-norm) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestInverseUpdateUnit::GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
double t=0;
|
|
double lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
int j=0;
|
|
double u1=0;
|
|
double u2=0;
|
|
double sm=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double v[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- check
|
|
if(n<=0)
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(w,n+1);
|
|
ArrayResize(v,n+1);
|
|
a.Resize(n+1,n+1);
|
|
a0.Resize(n,n);
|
|
//--- Prepare A
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a.Set(i,j,1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- Calculate A using Stewart algorithm
|
|
for(s=2; s<=n; s++)
|
|
{
|
|
//--- Prepare v and Lambda=v'*v
|
|
do
|
|
{
|
|
i=1;
|
|
while(i<=s)
|
|
{
|
|
//--- change values
|
|
u1=2*CMath::RandomReal()-1;
|
|
u2=2*CMath::RandomReal()-1;
|
|
sm=u1*u1+u2*u2;
|
|
//--- check
|
|
if(sm==0.0 || sm>1.0)
|
|
continue;
|
|
sm=MathSqrt(-(2*MathLog(sm)/sm));
|
|
v[i]=u1*sm;
|
|
//--- check
|
|
if(i+1<=s)
|
|
v[i+1]=u2*sm;
|
|
i=i+2;
|
|
}
|
|
//--- change value
|
|
lambdav=0.0;
|
|
for(i_=1; i_<=s; i_++)
|
|
lambdav+=v[i_]*v[i_];
|
|
}
|
|
while((double)(lambdav)==0.0);
|
|
lambdav=2/lambdav;
|
|
//--- A * (I - 2 vv'/v'v )=
|
|
//--- =A - (2/v'v) * A * v * v'=
|
|
//--- =A - (2/v'v) * w * v'
|
|
//--- where w=Av
|
|
for(i=1; i<=s; i++)
|
|
{
|
|
t=0.0;
|
|
for(i_=1; i_<=s; i_++)
|
|
t+=a.Get(i,i_)*v[i_];
|
|
w[i]=t;
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<=s; i++)
|
|
{
|
|
t=w[i]*lambdav;
|
|
for(i_=1; i_<=s; i_++)
|
|
a.Set(i,i_,a.Get(i,i_)-t*v[i_]);
|
|
}
|
|
}
|
|
//--- copy
|
|
for(i=1; i<=n; i++)
|
|
{
|
|
for(j=1; j<=n; j++)
|
|
a0.Set(i-1,j-1,a.Get(i,j));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestInverseUpdateUnit::GenerateRandomMatrixCond(CMatrixDouble &a0,
|
|
const int n,
|
|
const double c)
|
|
{
|
|
//--- create variables
|
|
double l1=0;
|
|
double l2=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
//--- create array
|
|
double cc[];
|
|
//--- create matrix
|
|
CMatrixDouble q1;
|
|
CMatrixDouble q2;
|
|
//--- function calls
|
|
GenerateRandomOrthogonalMatrix(q1,n);
|
|
GenerateRandomOrthogonalMatrix(q2,n);
|
|
//--- allocation
|
|
ArrayResize(cc,n);
|
|
//--- change values
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
cc[0]=MathExp(l1);
|
|
for(i=1; i<=n-2; i++)
|
|
cc[i]=MathExp(CMath::RandomReal()*(l2-l1)+l1);
|
|
cc[n-1]=MathExp(l2);
|
|
//--- allocation
|
|
a0.Resize(n,n);
|
|
//--- calculation
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a0.Set(i,j,0);
|
|
for(k=0; k<n; k++)
|
|
a0.Set(i,j,a0.Get(i,j)+q1[i][k]*cc[k]*q2[j][k]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| triangular inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestInverseUpdateUnit::InvMatTr(CMatrixDouble &a,const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a.Set(j,j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0; i_<j; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=0; i<j; i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1; i_<j; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=0; i_<j; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a.Set(j,j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1; i_<n; i_++)
|
|
t[i_]=a.Get(i_,j);
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1; i_<i; i_++)
|
|
v+=a.Get(i,i_)*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a.Set(i,j,v+a[i][i]*t[i]);
|
|
else
|
|
a.Set(i,j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=j+1; i_<n; i_++)
|
|
a.Set(i_,j,ajj*a.Get(i_,j));
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestInverseUpdateUnit::InvMatLU(CMatrixDouble &a,int &pivots[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- Quick return if possible
|
|
if(n==0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(work,n);
|
|
//--- Form inv(U)
|
|
if(!InvMatTr(a,n,true,false))
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
|
|
for(j=n-1; j>=0; j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1; i<n; i++)
|
|
{
|
|
work[i]=a.Get(i,j);
|
|
a.Set(i,j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1; i_<n; i_++)
|
|
v+=a.Get(i,i_)*work[i_];
|
|
a.Set(i,j,a.Get(i,j)-v);
|
|
}
|
|
}
|
|
}
|
|
//--- Apply column interchanges.
|
|
for(j=n-2; j>=0; j--)
|
|
{
|
|
jp=pivots[j];
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
//--- copy
|
|
for(i_=0; i_<n; i_++)
|
|
work[i_]=a.Get(i_,j);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,j,a[i_][jp]);
|
|
for(i_=0; i_<n; i_++)
|
|
a.Set(i_,jp,work[i_]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestInverseUpdateUnit::InvMat(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- function call
|
|
MatLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(InvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Diff |
|
|
//+------------------------------------------------------------------+
|
|
double CTestInverseUpdateUnit::MatrixDiff(CMatrixDouble &a,
|
|
CMatrixDouble &b,
|
|
const int m,const int n)
|
|
{
|
|
double result=0;
|
|
//--- calculation
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
result=MathMax(result,MathAbs(b.Get(i,j)-a.Get(i,j)));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Update and inverse |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestInverseUpdateUnit::UpdAndInv(CMatrixDouble &a,double &u[],
|
|
double &v[],const int n)
|
|
{
|
|
double r=0;
|
|
//--- create array
|
|
int pivots[];
|
|
//--- calculation
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
r=u[i];
|
|
for(int i_=0; i_<n; i_++)
|
|
a.Set(i,i_,a.Get(i,i_)+r*v[i_]);
|
|
}
|
|
//--- function call
|
|
MatLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(InvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSchur |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSchurUnit
|
|
{
|
|
public:
|
|
static bool TestSchur(const bool silent);
|
|
|
|
private:
|
|
static void FillsParseA(CMatrixDouble &a,const int n,const double sparcity);
|
|
static void TestSchurProblem(CMatrixDouble &a,const int n,double &materr,double &orterr,bool &errstruct,bool &wfailed);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing Schur decomposition subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSchurUnit::TestSchur(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
bool errstruct;
|
|
bool wfailed;
|
|
double materr=0;
|
|
double orterr=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- initialization
|
|
materr=0;
|
|
orterr=0;
|
|
errstruct=false;
|
|
wfailed=false;
|
|
waserrors=false;
|
|
maxn=70;
|
|
passcount=1;
|
|
threshold=5*100*CMath::m_machineepsilon;
|
|
//--- allocation
|
|
a.Resize(maxn,maxn);
|
|
//--- zero matrix,several cases
|
|
for(i=0; i<=maxn-1; i++)
|
|
{
|
|
for(j=0; j<=maxn-1; j++)
|
|
a.Set(i,j,0);
|
|
}
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- check
|
|
if(n>30 && n%2==0)
|
|
continue;
|
|
//--- function call
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
}
|
|
//--- Dense matrix
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- check
|
|
if(n>30 && n%2==0)
|
|
continue;
|
|
//--- change values
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
}
|
|
}
|
|
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
|
|
for(pass=1; pass<=1; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- check
|
|
if(n>30 && n%3!=0)
|
|
continue;
|
|
//--- function calls
|
|
FillsParseA(a,n,0.8);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
FillsParseA(a,n,0.9);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
FillsParseA(a,n,0.95);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
FillsParseA(a,n,0.997);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((materr>threshold||orterr>threshold)||errstruct)||wfailed;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SCHUR DECOMPOSITION");
|
|
PrintFormat("Schur decomposition error: %5.3E",materr);
|
|
PrintFormat("Schur orthogonality error: %5.3E",orterr);
|
|
PrintResult("T matrix structure",!errstruct);
|
|
PrintFormat("Always converged: %s",(!wfailed ? "OK" : "FAILED"));
|
|
PrintFormat("Threshold: %5.3E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSchurUnit::FillsParseA(CMatrixDouble &a,const int n,
|
|
const double sparcity)
|
|
{
|
|
//--- change values
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSchurUnit::TestSchurProblem(CMatrixDouble &a,const int n,
|
|
double &materr,double &orterr,
|
|
bool &errstruct,bool &wfailed)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double locerr=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double sr[];
|
|
double astc[];
|
|
double sastc[];
|
|
//--- create matrix
|
|
CMatrixDouble s;
|
|
CMatrixDouble t;
|
|
//--- allocation
|
|
ArrayResize(sr,n);
|
|
ArrayResize(astc,n);
|
|
ArrayResize(sastc,n);
|
|
//--- Schur decomposition,convergence test
|
|
t.Resize(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
t.Set(i,j,a.Get(i,j));
|
|
}
|
|
//--- check
|
|
if(!CSchur::RMatrixSchur(t,n,s))
|
|
{
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- decomposition error
|
|
locerr=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
sr[i_]=s.Get(j,i_);
|
|
//--- calculation
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=t[k][i_]*sr[i_];
|
|
astc[k]=v;
|
|
}
|
|
//--- calculation
|
|
for(k=0; k<n; k++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=s[k][i_]*astc[i_];
|
|
sastc[k]=v;
|
|
}
|
|
//--- search errors
|
|
for(k=0; k<n; k++)
|
|
locerr=MathMax(locerr,MathAbs(sastc[k]-a[k][j]));
|
|
}
|
|
//--- search errors
|
|
materr=MathMax(materr,locerr);
|
|
//--- orthogonality error
|
|
locerr=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=s.Get(i_,i)*s.Get(i_,j);
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(v));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(v-1));
|
|
}
|
|
}
|
|
//--- search errors
|
|
orterr=MathMax(orterr,locerr);
|
|
//--- T matrix structure
|
|
for(j=0; j<n; j++)
|
|
{
|
|
for(i=j+2; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(t.Get(i,j)!=0.0)
|
|
errstruct=true;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNlEq |
|
|
//+------------------------------------------------------------------+
|
|
class CTestNlEqUnit
|
|
{
|
|
public:
|
|
static bool TestNlEq(const bool silent);
|
|
|
|
private:
|
|
static void TestFuncHBM(CNlEqState &state);
|
|
static void TestFuncHB1(CNlEqState &state);
|
|
static void TestFuncSHBM(CNlEqState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNlEq |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestNlEqUnit::TestNlEq(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool basicserrors;
|
|
bool converror;
|
|
bool othererrors;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
double flast=0;
|
|
bool firstrep;
|
|
int nfunc=0;
|
|
int njac=0;
|
|
int itcnt=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
double epsf=0;
|
|
double stpmax=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xlast[];
|
|
//--- objects of classes
|
|
CNlEqState state;
|
|
CNlEqReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
basicserrors=false;
|
|
converror=false;
|
|
othererrors=false;
|
|
//--- Basic tests
|
|
//--- Test with Himmelblau's function (M):
|
|
//--- * ability to find correct result
|
|
//--- * ability to work after soft restart (restart after finish)
|
|
//--- * ability to work after hard restart (restart in the middle of optimization)
|
|
passcount=100;
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
//--- Ability to find correct result
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,2,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors||CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
|
|
else
|
|
basicserrors=true;
|
|
//--- Ability to work after soft restart
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,2,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors||CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
|
|
else
|
|
basicserrors=true;
|
|
//--- Ability to work after hard restart:
|
|
//--- * stopping condition: small F
|
|
//--- * StpMax is so small that we need about 10000 iterations to
|
|
//--- find solution (steps are small)
|
|
//--- * choose random K significantly less that 9999
|
|
//--- * iterate for some time,then break,restart optimization
|
|
ArrayResize(x,2);
|
|
x[0]=100;
|
|
x[1]=100;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,2,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function calls
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
CNlEq::NlEqSetStpMax(state,0.01);
|
|
k=1+CMath::RandomInteger(100);
|
|
//--- calculation
|
|
for(i=0; i<k; i++)
|
|
{
|
|
//--- check
|
|
if(!CNlEq::NlEqIteration(state))
|
|
break;
|
|
TestFuncHBM(state);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors||CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
|
|
else
|
|
basicserrors=true;
|
|
}
|
|
//--- Basic tests
|
|
//--- Test with Himmelblau's function (1):
|
|
//--- * ability to find correct result
|
|
passcount=100;
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
//--- Ability to find correct result
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,1,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHB1(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors||CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>(double)(epsf);
|
|
else
|
|
basicserrors=true;
|
|
}
|
|
//--- Basic tests
|
|
//--- Ability to detect situation when we can't find minimum
|
|
passcount=100;
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,3,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncSHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- search errors
|
|
basicserrors=basicserrors||rep.m_terminationtype!=-4;
|
|
}
|
|
//--- Test correctness of intermediate reports and final report:
|
|
//--- * first report is starting point
|
|
//--- * function value decreases on subsequent reports
|
|
//--- * function value is correctly reported
|
|
//--- * last report is final point
|
|
//--- * NFunc and NJac are compared with values counted directly
|
|
//--- * IterationsCount is compared with value counter directly
|
|
n=2;
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
xlast[0]=CMath::m_maxrealnumber;
|
|
xlast[1]=CMath::m_maxrealnumber;
|
|
//--- function calls
|
|
CNlEq::NlEqCreateLM(n,2,x,state);
|
|
CNlEq::NlEqSetCond(state,1.0E-6,0);
|
|
CNlEq::NlEqSetXRep(state,true);
|
|
//--- change values
|
|
firstrep=true;
|
|
flast=CMath::m_maxrealnumber;
|
|
nfunc=0;
|
|
njac=0;
|
|
itcnt=0;
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- first report must be starting point
|
|
if(firstrep)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
othererrors=othererrors||state.m_x[i]!=x[i];
|
|
firstrep=false;
|
|
}
|
|
//--- function value must decrease
|
|
othererrors=othererrors||state.m_f>flast;
|
|
//--- check correctness of function value
|
|
v=CMath::Sqr(state.m_x[0]*state.m_x[0]+state.m_x[1]-11)+CMath::Sqr(state.m_x[0]+state.m_x[1]*state.m_x[1]-7);
|
|
othererrors=othererrors||MathAbs(v-state.m_f)/MathMax(v,1)>100*CMath::m_machineepsilon;
|
|
//--- update info and continue
|
|
for(i_=0; i_<n; i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
flast=state.m_f;
|
|
itcnt=itcnt+1;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_needf)
|
|
nfunc=nfunc+1;
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
nfunc=nfunc+1;
|
|
njac=njac+1;
|
|
}
|
|
//--- function call
|
|
TestFuncHBM(state);
|
|
}
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
othererrors=(othererrors||xlast[0]!=x[0])||xlast[1]!=x[1];
|
|
v=CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7);
|
|
othererrors=othererrors||MathAbs(flast-v)/MathMax(v,1)>100*CMath::m_machineepsilon;
|
|
}
|
|
else
|
|
converror=true;
|
|
//--- search errors
|
|
othererrors=othererrors||rep.m_nfunc!=nfunc;
|
|
othererrors=othererrors||rep.m_njac!=njac;
|
|
othererrors=othererrors||rep.m_iterationscount!=itcnt-1;
|
|
//--- Test ability to set limit on algorithm steps
|
|
ArrayResize(x,2);
|
|
ArrayResize(xlast,2);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()+20;
|
|
x[1]=20*CMath::RandomReal()+20;
|
|
xlast[0]=x[0];
|
|
xlast[1]=x[1];
|
|
stpmax=0.1+0.1*CMath::RandomReal();
|
|
epsf=1.0E-9;
|
|
//--- function calls
|
|
CNlEq::NlEqCreateLM(2,3,x,state);
|
|
CNlEq::NlEqSetStpMax(state,stpmax);
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
CNlEq::NlEqSetXRep(state,true);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfij)
|
|
TestFuncHBM(state);
|
|
//--- check
|
|
if((state.m_needf || state.m_needfij) || state.m_xupdated)
|
|
othererrors=othererrors||MathSqrt(CMath::Sqr(state.m_x[0]-xlast[0])+CMath::Sqr(state.m_x[1]-xlast[1]))>1.00001*stpmax;
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
xlast[0]=state.m_x[0];
|
|
xlast[1]=state.m_x[1];
|
|
}
|
|
}
|
|
//--- end
|
|
waserrors=(basicserrors||converror)||othererrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING NLEQ SOLVER");
|
|
PrintResult("BASIC FUNCTIONALITY",!basicserrors);
|
|
PrintResult("CONVERGENCE",!converror);
|
|
PrintResult("OTHER PROPERTIES",!othererrors);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Himmelblau's function |
|
|
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 |
|
|
//| posed as system of M functions: |
|
|
//| f0=x^2+y-11 |
|
|
//| f1=x+y^2-7 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNlEqUnit::TestFuncHBM(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
double x=0;
|
|
double y=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
|
|
return;
|
|
//--- change values
|
|
x=state.m_x[0];
|
|
y=state.m_x[1];
|
|
//--- check
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7);
|
|
return;
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=x*x+y-11;
|
|
state.m_fi[1]=x+y*y-7;
|
|
state.m_j.Set(0,0,2*x);
|
|
state.m_j.Set(0,1,1);
|
|
state.m_j.Set(1,0,1);
|
|
state.m_j.Set(1,1,2*y);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Himmelblau's function |
|
|
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 |
|
|
//| posed as system of 1 function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNlEqUnit::TestFuncHB1(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
double x=0;
|
|
double y=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
|
|
return;
|
|
//--- change values
|
|
x=state.m_x[0];
|
|
y=state.m_x[1];
|
|
//--- check
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=CMath::Sqr(CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7));
|
|
return;
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7);
|
|
state.m_j.Set(0,0,2*(x*x+y-11)*2*x+2*(x+y*y-7));
|
|
state.m_j.Set(0,1,2*(x*x+y-11)+2*(x+y*y-7)*2*y);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Shifted Himmelblau's function |
|
|
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 + 1 |
|
|
//| posed as system of M functions: |
|
|
//| f0=x^2+y-11 |
|
|
//| f1=x+y^2-7 |
|
|
//| f2=1 |
|
|
//| This function is used to test algorithm on problem which has no |
|
|
//| solution. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNlEqUnit::TestFuncSHBM(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
double x=0;
|
|
double y=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
|
|
return;
|
|
//--- change values
|
|
x=state.m_x[0];
|
|
y=state.m_x[1];
|
|
//--- check
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=x*x+y-11;
|
|
state.m_fi[1]=x+y*y-7;
|
|
state.m_fi[2]=1;
|
|
state.m_j.Set(0,0,2*x);
|
|
state.m_j.Set(0,1,1);
|
|
state.m_j.Set(1,0,1);
|
|
state.m_j.Set(1,1,2*y);
|
|
state.m_j.Set(2,0,0);
|
|
state.m_j.Set(2,1,0);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CChebyshev |
|
|
//+------------------------------------------------------------------+
|
|
class CTestChebyshevUnit
|
|
{
|
|
public:
|
|
static bool TestChebyshev(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CChebyshev |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestChebyshevUnit::TestChebyshev(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double ferr=0;
|
|
double threshold=0;
|
|
double x=0;
|
|
double v=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double c[];
|
|
double p1[];
|
|
double p2[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- initialization
|
|
err=0;
|
|
sumerr=0;
|
|
cerr=0;
|
|
ferr=0;
|
|
threshold=1.0E-9;
|
|
waserrors=false;
|
|
//--- Testing Chebyshev polynomials of the first kind
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.00)-1));
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.33)-1));
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,-0.42)-1));
|
|
x=0.2;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.2));
|
|
x=0.4;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.4));
|
|
x=0.6;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.6));
|
|
x=0.8;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.8));
|
|
x=1.0;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-1.0));
|
|
x=0.2;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.92));
|
|
x=0.4;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.68));
|
|
x=0.6;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.28));
|
|
x=0.8;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-0.28));
|
|
x=1.0;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-1.00));
|
|
n=10;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)-0.4284556288));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7996160205));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7483020370));
|
|
//--- Testing Chebyshev polynomials of the second kind
|
|
n=0;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-1.0000000000));
|
|
n=1;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.4000000000));
|
|
n=2;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8400000000));
|
|
n=3;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.7360000000));
|
|
n=4;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.5456000000));
|
|
n=10;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.6128946176));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.6770370970));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8837094564));
|
|
//--- Testing Clenshaw summation
|
|
maxn=20;
|
|
ArrayResize(c,maxn+1);
|
|
for(k=1; k<=2; k++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
//--- calculation
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v+=CChebyshev::ChebyshevCalculate(k,n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CChebyshev::ChebyshevSum(c,k,n,x)));
|
|
}
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CChebyshev::ChebyshevCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-1));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+1));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-2));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+3));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-4));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]+8));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-8));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(9,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-9));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+120));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-432));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[7]+576));
|
|
cerr=MathMax(cerr,MathAbs(c[8]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[9]-256));
|
|
//--- Testing FromChebyshev
|
|
maxn=10;
|
|
a.Resize(maxn+1,maxn+1);
|
|
for(i=0; i<=maxn; i++)
|
|
{
|
|
for(j=0; j<=maxn; j++)
|
|
a.Set(i,j,0);
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(i,c);
|
|
for(i_=0; i_<=i; i_++)
|
|
a.Set(i,i_,c[i_]);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(c,maxn+1);
|
|
ArrayResize(p1,maxn+1);
|
|
//--- calculation
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(i=0; i<=n; i++)
|
|
p1[i]=0;
|
|
for(i=0; i<=n; i++)
|
|
{
|
|
//--- change values
|
|
c[i]=2*CMath::RandomReal()-1;
|
|
v=c[i];
|
|
for(i_=0; i_<=i; i_++)
|
|
p1[i_]=p1[i_]+v*a.Get(i,i_);
|
|
}
|
|
//--- function call
|
|
CChebyshev::FromChebyshev(c,n,p2);
|
|
for(i=0; i<=n; i++)
|
|
ferr=MathMax(ferr,MathAbs(p1[i]-p2[i]));
|
|
}
|
|
}
|
|
//--- Reporting
|
|
waserrors=((err>threshold||sumerr>threshold)||cerr>threshold)||ferr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS");
|
|
PrintFormat("Max error against table %5.2E",err);
|
|
PrintFormat("Summation error %5.2E",sumerr);
|
|
PrintFormat("Coefficients error %5.2E",cerr);
|
|
PrintFormat("FrobChebyshev error %5.2E",ferr);
|
|
PrintFormat("Threshold %5.2E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHermite |
|
|
//+------------------------------------------------------------------+
|
|
class CTestHermiteUnit
|
|
{
|
|
public:
|
|
static bool TestHermite(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHermite |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestHermiteUnit::TestHermite(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double threshold=1.0E-9;
|
|
int n=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
double x=0;
|
|
double v=0;
|
|
bool waserrors=false;
|
|
//--- create array
|
|
double c[];
|
|
//--- Testing Hermite polynomials
|
|
n=0;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-1));
|
|
n=1;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2));
|
|
n=2;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2));
|
|
n=3;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+4));
|
|
n=4;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+20));
|
|
n=5;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+8));
|
|
n=6;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-184));
|
|
n=7;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-464));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-230848));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-280768));
|
|
//--- Testing Clenshaw summation
|
|
maxn=10;
|
|
ArrayResize(c,maxn+1);
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
//--- calculation
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v+=CHermite::HermiteCalculate(n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CHermite::HermiteSum(c,n,x)));
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CHermite::HermiteCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-2));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+2));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-4));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+12));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-8));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-12));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]+48));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-16));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(5,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-120));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+160));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-32));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(6,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+120));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-720));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]+480));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-64));
|
|
//--- Reporting
|
|
waserrors=(err>threshold||sumerr>threshold)||cerr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE HERMITE POLYNOMIALS");
|
|
PrintFormat("Max error %5.2E}",err);
|
|
PrintFormat("Summation error %5.2E",sumerr);
|
|
PrintFormat("Coefficients error %5.2E",cerr);
|
|
PrintFormat("Threshold %5.2E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLaguerre |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLaguerreUnit
|
|
{
|
|
public:
|
|
static bool TestLaguerre(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLaguerre |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLaguerreUnit::TestLaguerre(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double threshold=1.0E-9;
|
|
int n=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
double x=0;
|
|
double v=0;
|
|
bool waserrors=false;
|
|
//--- create array
|
|
double c[];
|
|
//--- Testing Laguerre polynomials
|
|
n=0;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-1.0000000000));
|
|
n=1;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.5000000000));
|
|
n=2;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.1250000000));
|
|
n=3;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.1458333333));
|
|
n=4;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3307291667));
|
|
n=5;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4455729167));
|
|
n=6;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5041449653));
|
|
n=7;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5183392237));
|
|
n=8;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4983629984));
|
|
n=9;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4529195204));
|
|
n=10;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3893744141));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3139072988));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.2316496389));
|
|
//--- Testing Clenshaw summation
|
|
maxn=20;
|
|
ArrayResize(c,maxn+1);
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
//--- calculation
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v+=CLaguerre::LaguerreCalculate(n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CLaguerre::LaguerreSum(c,n,x)));
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CLaguerre::LaguerreCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+1));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-2.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+4.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-1.0/2.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-6.0/6.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+18.0/6.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-9.0/6.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+1.0/6.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-24.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+96.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-72.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+16.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-1.0/24.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(5,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-120.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+600.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-600.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+200.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-25.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]+1.0/120.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(6,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-720.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+4320.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-5400.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+2400.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-450.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]+36.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-1.0/720.0));
|
|
//--- Reporting
|
|
waserrors=(err>threshold||sumerr>threshold)||cerr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS");
|
|
PrintFormat("Max error %5.2E",err);
|
|
PrintFormat("Summation error %5.2E",sumerr);
|
|
PrintFormat("Coefficients error %5.2E",cerr);
|
|
PrintFormat("Threshold %5.2E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLegendre |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLegendreUnit
|
|
{
|
|
public:
|
|
static bool TestLegendre(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLegendre |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLegendreUnit::TestLegendre(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double threshold=1.0E-9;
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int pass=0;
|
|
double x=0;
|
|
double v=0;
|
|
double t=0;
|
|
bool waserrors=false;
|
|
//--- create array
|
|
double c[];
|
|
//--- Testing Legendre polynomials values
|
|
for(n=0; n<=10; n++)
|
|
{
|
|
//--- function call
|
|
CLegendre::LegendreCoefficients(n,c);
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- calculation
|
|
x=2*CMath::RandomReal()-1;
|
|
v=CLegendre::LegendreCalculate(n,x);
|
|
t=1;
|
|
for(i=0; i<=n; i++)
|
|
{
|
|
v=v-c[i]*t;
|
|
t=t*x;
|
|
}
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v));
|
|
}
|
|
}
|
|
//--- Testing Clenshaw summation
|
|
maxn=20;
|
|
ArrayResize(c,maxn+1);
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- change values
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v+=CLegendre::LegendreCalculate(n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CLegendre::LegendreSum(c,n,x)));
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CLegendre::LegendreCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-1));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+1.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-3.0/2.0));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+3.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-5.0/2.0));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-3.0/8.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]+30.0/8.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-35.0/8.0));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(9,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-315.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+4620.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-18018.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[7]+25740.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[8]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[9]-12155.0/128.0));
|
|
//--- Reporting
|
|
waserrors=(err>threshold||sumerr>threshold)||cerr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS");
|
|
PrintFormat("Max error %5.2E",err);
|
|
PrintFormat("Summation error %5.2E",sumerr);
|
|
PrintFormat("Coefficients error %5.2E",cerr);
|
|
PrintFormat("Threshold %5.2E",threshold);
|
|
//--- check
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary class |
|
|
//+------------------------------------------------------------------+
|
|
class CRec1
|
|
{
|
|
public:
|
|
//--- class variables
|
|
bool m_bfield;
|
|
double m_rfield;
|
|
int m_ifield;
|
|
complex m_cfield;
|
|
//--- arrays
|
|
bool m_b1field[];
|
|
double m_r1field[];
|
|
int m_i1field[];
|
|
complex m_c1field[];
|
|
//--- matrix
|
|
CMatrixInt m_b2field;
|
|
CMatrixDouble m_r2field;
|
|
CMatrixInt m_i2field;
|
|
CMatrixComplex m_c2field;
|
|
//--- constructor, destructor
|
|
CRec1(void) {}
|
|
~CRec1(void) {}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary class |
|
|
//+------------------------------------------------------------------+
|
|
class CRec4Serialization
|
|
{
|
|
public:
|
|
//--- arrays
|
|
bool m_b[];
|
|
int m_i[];
|
|
double m_r[];
|
|
//--- constructor, destructor
|
|
CRec4Serialization(void) {}
|
|
~CRec4Serialization(void) {}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing the basic functions |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAlglibBasicsUnit
|
|
{
|
|
public:
|
|
static void Rec4SerializationAlloc(CSerializer &s,CRec4Serialization &v);
|
|
static void Rec4SerializationSerialize(CSerializer &s,CRec4Serialization &v);
|
|
static void Rec4SerializationUnserialize(CSerializer &s,CRec4Serialization &v);
|
|
static bool TestAlglibBasics(const bool silent);
|
|
|
|
private:
|
|
static bool TestComplexArithmetics(const bool silent);
|
|
static bool TestIEEESpecial(const bool silent);
|
|
static bool TestSwapFunctions(const bool silent);
|
|
static bool TestSerializationFunctions(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAlglibBasicsUnit::Rec4SerializationAlloc(CSerializer &s,
|
|
CRec4Serialization &v)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- boolean fields
|
|
s.Alloc_Entry();
|
|
for(i=0; i<=CAp::Len(v.m_b)-1; i++)
|
|
s.Alloc_Entry();
|
|
//--- integer fields
|
|
s.Alloc_Entry();
|
|
for(i=0; i<=CAp::Len(v.m_i)-1; i++)
|
|
s.Alloc_Entry();
|
|
//--- real fields
|
|
s.Alloc_Entry();
|
|
for(i=0; i<=CAp::Len(v.m_r)-1; i++)
|
|
s.Alloc_Entry();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAlglibBasicsUnit::Rec4SerializationSerialize(CSerializer &s,
|
|
CRec4Serialization &v)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- boolean fields
|
|
s.Serialize_Int(CAp::Len(v.m_b));
|
|
for(i=0; i<=CAp::Len(v.m_b)-1; i++)
|
|
s.Serialize_Bool(v.m_b[i]);
|
|
//--- integer fields
|
|
s.Serialize_Int(CAp::Len(v.m_i));
|
|
for(i=0; i<=CAp::Len(v.m_i)-1; i++)
|
|
s.Serialize_Int(v.m_i[i]);
|
|
//--- real fields
|
|
s.Serialize_Int(CAp::Len(v.m_r));
|
|
for(i=0; i<=CAp::Len(v.m_r)-1; i++)
|
|
s.Serialize_Double(v.m_r[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAlglibBasicsUnit::Rec4SerializationUnserialize(CSerializer &s,
|
|
CRec4Serialization &v)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
bool bv;
|
|
int iv=0;
|
|
double rv=0;
|
|
//--- boolean fields
|
|
k=s.Unserialize_Int();
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(v.m_b,k);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
bv=s.Unserialize_Bool();
|
|
v.m_b[i]=bv;
|
|
}
|
|
}
|
|
//--- integer fields
|
|
k=s.Unserialize_Int();
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(v.m_i,k);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
iv=s.Unserialize_Int();
|
|
v.m_i[i]=iv;
|
|
}
|
|
}
|
|
//--- real fields
|
|
k=s.Unserialize_Int();
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(v.m_r,k);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
rv=s.Unserialize_Double();
|
|
v.m_r[i]=rv;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing the basic functions |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAlglibBasicsUnit::TestAlglibBasics(const bool silent)
|
|
{
|
|
//--- create a variable
|
|
bool result=true;
|
|
//--- function calls
|
|
result=result && TestComplexArithmetics(silent);
|
|
result=result && TestIEEESpecial(silent);
|
|
result=result && TestSwapFunctions(silent);
|
|
result=result && TestSerializationFunctions(silent);
|
|
//--- check
|
|
if(!silent)
|
|
Print("");
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex arithmetics test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAlglibBasicsUnit::TestComplexArithmetics(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool absc;
|
|
bool addcc;
|
|
bool addcr;
|
|
bool addrc;
|
|
bool subcc;
|
|
bool subcr;
|
|
bool subrc;
|
|
bool mulcc;
|
|
bool mulcr;
|
|
bool mulrc;
|
|
bool divcc;
|
|
bool divcr;
|
|
bool divrc;
|
|
complex ca=0;
|
|
complex cb=0;
|
|
complex res=0;
|
|
double ra=0;
|
|
double rb=0;
|
|
double threshold=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- initialization
|
|
threshold=100*CMath::m_machineepsilon;
|
|
passcount=1000;
|
|
result=true;
|
|
absc=true;
|
|
addcc=true;
|
|
addcr=true;
|
|
addrc=true;
|
|
subcc=true;
|
|
subcr=true;
|
|
subrc=true;
|
|
mulcc=true;
|
|
mulcr=true;
|
|
mulrc=true;
|
|
divcc=true;
|
|
divcr=true;
|
|
divrc=true;
|
|
//--- calculation
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Test AbsC
|
|
ca.real=2*CMath::RandomReal()-1;
|
|
ca.imag=2*CMath::RandomReal()-1;
|
|
ra=CMath::AbsComplex(ca);
|
|
absc=absc && MathAbs(ra-MathSqrt(CMath::Sqr(ca.real)+CMath::Sqr(ca.imag)))<threshold;
|
|
//--- test Add
|
|
ca.real=2*CMath::RandomReal()-1;
|
|
ca.imag=2*CMath::RandomReal()-1;
|
|
cb.real=2*CMath::RandomReal()-1;
|
|
cb.imag=2*CMath::RandomReal()-1;
|
|
ra=2*CMath::RandomReal()-1;
|
|
rb=2*CMath::RandomReal()-1;
|
|
res=ca+cb;
|
|
addcc=(addcc && MathAbs(res.real-ca.real-cb.real)<threshold) && MathAbs(res.imag-ca.imag-cb.imag)<threshold;
|
|
res=ca+rb;
|
|
addcr=(addcr && MathAbs(res.real-ca.real-rb)<threshold) && MathAbs(res.imag-ca.imag)<threshold;
|
|
res=cb+ra;
|
|
addrc=(addrc && MathAbs(res.real-ra-cb.real)<threshold) && MathAbs(res.imag-cb.imag)<threshold;
|
|
//--- test Sub
|
|
ca.real=2*CMath::RandomReal()-1;
|
|
ca.imag=2*CMath::RandomReal()-1;
|
|
cb.real=2*CMath::RandomReal()-1;
|
|
cb.imag=2*CMath::RandomReal()-1;
|
|
ra=2*CMath::RandomReal()-1;
|
|
rb=2*CMath::RandomReal()-1;
|
|
res=ca-cb;
|
|
subcc=(subcc && MathAbs(res.real-(ca.real-cb.real))<threshold) && MathAbs(res.imag-(ca.imag-cb.imag))<threshold;
|
|
res=ca-rb;
|
|
subcr=(subcr && MathAbs(res.real-(ca.real-rb))<threshold) && MathAbs(res.imag-ca.imag)<threshold;
|
|
res=-cb+ra;
|
|
subrc=(subrc && MathAbs(res.real-(ra-cb.real))<threshold) && MathAbs(res.imag+cb.imag)<threshold;
|
|
//--- test Mul
|
|
ca.real=2*CMath::RandomReal()-1;
|
|
ca.imag=2*CMath::RandomReal()-1;
|
|
cb.real=2*CMath::RandomReal()-1;
|
|
cb.imag=2*CMath::RandomReal()-1;
|
|
ra=2*CMath::RandomReal()-1;
|
|
rb=2*CMath::RandomReal()-1;
|
|
res=ca*cb;
|
|
mulcc=(mulcc && MathAbs(res.real-(ca.real*cb.real-ca.imag*cb.imag))<threshold) && MathAbs(res.imag-(ca.real*cb.imag+ca.imag*cb.real))<threshold;
|
|
res=ca*rb;
|
|
mulcr=(mulcr && MathAbs(res.real-ca.real*rb)<threshold) && MathAbs(res.imag-ca.imag*rb)<threshold;
|
|
res=cb*ra;
|
|
mulrc=(mulrc && MathAbs(res.real-ra*cb.real)<threshold) && MathAbs(res.imag-ra*cb.imag)<threshold;
|
|
//--- test Div
|
|
ca.real=2*CMath::RandomReal()-1;
|
|
ca.imag=2*CMath::RandomReal()-1;
|
|
do
|
|
{
|
|
cb.real=2*CMath::RandomReal()-1;
|
|
cb.imag=2*CMath::RandomReal()-1;
|
|
}
|
|
while(CMath::AbsComplex(cb)<=0.5);
|
|
ra=2*CMath::RandomReal()-1;
|
|
do
|
|
{
|
|
rb=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(rb)<=0.5);
|
|
res=ca/cb;
|
|
divcc=(divcc && MathAbs((res*cb).real-ca.real)<threshold) && MathAbs((res*cb).imag-ca.imag)<threshold;
|
|
res=ca/rb;
|
|
divcr=(divcr && MathAbs(res.real-ca.real/rb)<threshold) && MathAbs(res.imag-ca.imag/rb)<threshold;
|
|
complex cra=ra;
|
|
res=cra/cb;
|
|
divrc=(divrc && MathAbs((res*cb).real-ra)<threshold) && MathAbs((res*cb).imag)<threshold;
|
|
}
|
|
//--- summary
|
|
result=result && absc;
|
|
result=result && addcc;
|
|
result=result && addcr;
|
|
result=result && addrc;
|
|
result=result && subcc;
|
|
result=result && subcr;
|
|
result=result && subrc;
|
|
result=result && mulcc;
|
|
result=result && mulcr;
|
|
result=result && mulrc;
|
|
result=result && divcc;
|
|
result=result && divcr;
|
|
result=result && divrc;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
{
|
|
PrintResult("COMPLEX ARITHMETICS",true);
|
|
}
|
|
else
|
|
{
|
|
PrintResult("COMPLEX ARITHMETICS",false);
|
|
PrintResult("* AddCC ",addcc);
|
|
PrintResult("* AddCR ",addcr);
|
|
PrintResult("* AddRC ",addrc);
|
|
PrintResult("* SubCC ",subcc);
|
|
PrintResult("* SubCR ",subcr);
|
|
PrintResult("* SubRC ",subrc);
|
|
PrintResult("* MulCC ",mulcc);
|
|
PrintResult("* MulCR ",mulcr);
|
|
PrintResult("* MulRC ",mulrc);
|
|
PrintResult("* DivCC ",divcc);
|
|
PrintResult("* DivCR ",divcr);
|
|
PrintResult("* DivRC ",divrc);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests for IEEE special quantities |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAlglibBasicsUnit::TestIEEESpecial(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool oknan;
|
|
bool okinf;
|
|
bool okother;
|
|
double v1=0;
|
|
double v2=0;
|
|
//--- initialization
|
|
result=true;
|
|
oknan=true;
|
|
okinf=true;
|
|
okother=true;
|
|
//--- Test classification functions
|
|
okother=okother && !CInfOrNaN::IsInfinity(CInfOrNaN::NaN());
|
|
okother=okother && CInfOrNaN::IsInfinity(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsInfinity(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsInfinity(1.0);
|
|
okother=okother && !CInfOrNaN::IsInfinity(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsInfinity(0.0);
|
|
okother=okother && !CInfOrNaN::IsInfinity(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsInfinity(-1.0);
|
|
okother=okother && !CInfOrNaN::IsInfinity(-CMath::m_maxrealnumber);
|
|
okother=okother && CInfOrNaN::IsInfinity(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NaN());
|
|
okother=okother && CInfOrNaN::IsPositiveInfinity(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(1.0);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(0.0);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(-1.0);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(-CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NaN());
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(1.0);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(0.0);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(-1.0);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(-CMath::m_maxrealnumber);
|
|
okother=okother && CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && CInfOrNaN::IsNaN(CInfOrNaN::NaN());
|
|
okother=okother && !CInfOrNaN::IsNaN(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsNaN(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(1.0);
|
|
okother=okother && !CInfOrNaN::IsNaN(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(0.0);
|
|
okother=okother && !CInfOrNaN::IsNaN(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(-1.0);
|
|
okother=okother && !CInfOrNaN::IsNaN(-CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && !CMath::IsFinite(CInfOrNaN::NaN());
|
|
okother=okother && !CMath::IsFinite(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && CMath::IsFinite(CMath::m_maxrealnumber);
|
|
okother=okother && CMath::IsFinite(1.0);
|
|
okother=okother && CMath::IsFinite(CMath::m_minrealnumber);
|
|
okother=okother && CMath::IsFinite(0.0);
|
|
okother=okother && CMath::IsFinite(-CMath::m_minrealnumber);
|
|
okother=okother && CMath::IsFinite(-1.0);
|
|
okother=okother && CMath::IsFinite(-CMath::m_maxrealnumber);
|
|
okother=okother && !CMath::IsFinite(CInfOrNaN::NegativeInfinity());
|
|
//--- Test NAN
|
|
v1=CInfOrNaN::NaN();
|
|
oknan=oknan && CInfOrNaN::IsNaN(v1);
|
|
//--- Test INF:
|
|
//--- * basic properties
|
|
//--- * comparisons involving PosINF on one of the sides
|
|
//--- * comparisons involving NegINF on one of the sides
|
|
v1=CInfOrNaN::PositiveInfinity();
|
|
v2=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::IsInfinity(CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::IsInfinity(v1);
|
|
okinf=okinf && CInfOrNaN::IsInfinity(CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::IsInfinity(v2);
|
|
okinf=okinf && CInfOrNaN::IsPositiveInfinity(CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::IsPositiveInfinity(v1);
|
|
okinf=okinf && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !CInfOrNaN::IsPositiveInfinity(v2);
|
|
okinf=okinf && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !CInfOrNaN::IsNegativeInfinity(v1);
|
|
okinf=okinf && CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::IsNegativeInfinity(v2);
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()==CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()==v1;
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==v2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==0.0);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==1.2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==-1.2);
|
|
okinf=okinf && v1==CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(v2==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(0.0==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(1.2==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(-1.2==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()!=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()!=v1);
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=v2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=0.0;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=1.2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=-1.2;
|
|
okinf=okinf && !(v1!=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && v2!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 0.0!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 1.2!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && -1.2!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<v1);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<v2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<0.0);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<1.2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<-1.2);
|
|
okinf=okinf && !(v1<CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && v2<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 0.0<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 1.2<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && -1.2<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()<=v1;
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=v2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=0.0);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=1.2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=-1.2);
|
|
okinf=okinf && v1<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && v2<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 0.0<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 1.2<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && -1.2<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()>v1);
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>v2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>0.0;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>1.2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>-1.2;
|
|
okinf=okinf && !(v1>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(v2>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(0.0>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(1.2>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(-1.2>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=v1;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=v2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=0.0;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=1.2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=-1.2;
|
|
okinf=okinf && v1>=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(v2>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(0.0>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(1.2>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(-1.2>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==v1);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()==CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()==v2;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==0.0);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==1.2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==-1.2);
|
|
okinf=okinf && !(v1==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()==CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && v2==CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(0.0==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(1.2==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(-1.2==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=v1;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=v2);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=0.0;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=1.2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=-1.2;
|
|
okinf=okinf && v1!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(v2!=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && 0.0!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 1.2!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && -1.2!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<v1;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<v2);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<0.0;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<1.2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<-1.2;
|
|
okinf=okinf && !(v1<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(v2<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(0.0<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(1.2<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(-1.2<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=v1;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=v2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=0.0;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=1.2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=-1.2;
|
|
okinf=okinf && !(v1<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && v2<=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(0.0<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(1.2<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(-1.2<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>v1);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>v2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>0.0);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>1.2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>-1.2);
|
|
okinf=okinf && v1>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(v2>CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && 0.0>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 1.2>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && -1.2>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=v1);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()>=v2;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=0.0);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=1.2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=-1.2);
|
|
okinf=okinf && v1>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && v2>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 0.0>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 1.2>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && -1.2>=CInfOrNaN::NegativeInfinity();
|
|
//--- summary
|
|
result=result && oknan;
|
|
result=result && okinf;
|
|
result=result && okother;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
{
|
|
PrintResult("IEEE SPECIAL VALUES",true);
|
|
}
|
|
else
|
|
{
|
|
PrintResult("IEEE SPECIAL VALUES",false);
|
|
PrintResult("* NAN",oknan);
|
|
PrintResult("* INF",okinf);
|
|
PrintResult("* FUNCTIONS",okother);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests for swapping functions |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAlglibBasicsUnit::TestSwapFunctions(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool okb1;
|
|
bool okb2;
|
|
bool oki1;
|
|
bool oki2;
|
|
bool okr1;
|
|
bool okr2;
|
|
bool okc1;
|
|
bool okc2;
|
|
//--- create arrays
|
|
bool b11[];
|
|
bool b12[];
|
|
int i11[];
|
|
int i12[];
|
|
double r11[];
|
|
double r12[];
|
|
complex c11[];
|
|
complex c12[];
|
|
//--- create matrix
|
|
CMatrixInt b21;
|
|
CMatrixInt b22;
|
|
CMatrixInt i21;
|
|
CMatrixInt i22;
|
|
CMatrixDouble r21;
|
|
CMatrixDouble r22;
|
|
CMatrixComplex c21;
|
|
CMatrixComplex c22;
|
|
//--- initialization
|
|
result=true;
|
|
okb1=true;
|
|
okb2=true;
|
|
oki1=true;
|
|
oki2=true;
|
|
okr1=true;
|
|
okr2=true;
|
|
okc1=true;
|
|
okc2=true;
|
|
//--- Test B1 swaps
|
|
ArrayResize(b11,1);
|
|
ArrayResize(b12,2);
|
|
//--- change values
|
|
b11[0]=true;
|
|
b12[0]=false;
|
|
b12[1]=true;
|
|
//--- function call
|
|
CAp::Swap(b11,b12);
|
|
//--- check
|
|
if(CAp::Len(b11)==2 && CAp::Len(b12)==1)
|
|
{
|
|
okb1=okb1 && !b11[0];
|
|
okb1=okb1 && b11[1];
|
|
okb1=okb1 && b12[0];
|
|
}
|
|
else
|
|
okb1=false;
|
|
//--- Test I1 swaps
|
|
ArrayResize(i11,1);
|
|
ArrayResize(i12,2);
|
|
//--- change values
|
|
i11[0]=1;
|
|
i12[0]=2;
|
|
i12[1]=3;
|
|
//--- function call
|
|
CAp::Swap(i11,i12);
|
|
//--- check
|
|
if(CAp::Len(i11)==2 && CAp::Len(i12)==1)
|
|
{
|
|
oki1=oki1 && i11[0]==2;
|
|
oki1=oki1 && i11[1]==3;
|
|
oki1=oki1 && i12[0]==1;
|
|
}
|
|
else
|
|
oki1=false;
|
|
//--- Test R1 swaps
|
|
ArrayResize(r11,1);
|
|
ArrayResize(r12,2);
|
|
//--- change values
|
|
r11[0]=1.5;
|
|
r12[0]=2.5;
|
|
r12[1]=3.5;
|
|
//--- function call
|
|
CAp::Swap(r11,r12);
|
|
//--- check
|
|
if(CAp::Len(r11)==2 && CAp::Len(r12)==1)
|
|
{
|
|
okr1=okr1 && r11[0]==2.5;
|
|
okr1=okr1 && r11[1]==3.5;
|
|
okr1=okr1 && r12[0]==1.5;
|
|
}
|
|
else
|
|
okr1=false;
|
|
//--- Test C1 swaps
|
|
ArrayResize(c11,1);
|
|
ArrayResize(c12,2);
|
|
//--- change values
|
|
c11[0]=1;
|
|
c12[0]=2;
|
|
c12[1]=3;
|
|
//--- function call
|
|
CAp::Swap(c11,c12);
|
|
//--- check
|
|
if(CAp::Len(c11)==2 && CAp::Len(c12)==1)
|
|
{
|
|
okc1=okc1 && c11[0]==2;
|
|
okc1=okc1 && c11[1]==3;
|
|
okc1=okc1 && c12[0]==1;
|
|
}
|
|
else
|
|
okc1=false;
|
|
//--- Test B2 swaps
|
|
b21.Resize(1,2);
|
|
b22.Resize(2,1);
|
|
//--- change values
|
|
b21.Set(0,0,true);
|
|
b21.Set(0,1,false);
|
|
b22.Set(0,0,false);
|
|
b22.Set(1,0,true);
|
|
//--- function call
|
|
CAp::Swap(b21,b22);
|
|
//--- check
|
|
if(((CAp::Rows(b21)==2 && CAp::Cols(b21)==1) && CAp::Rows(b22)==1) && CAp::Cols(b22)==2)
|
|
{
|
|
okb2=okb2 && !b21[0][0];
|
|
okb2=okb2 && b21[1][0];
|
|
okb2=okb2 && b22[0][0];
|
|
okb2=okb2 && !b22[0][1];
|
|
}
|
|
else
|
|
okb2=false;
|
|
//--- Test I2 swaps
|
|
i21.Resize(1,2);
|
|
i22.Resize(2,1);
|
|
//--- change values
|
|
i21.Set(0,0,1);
|
|
i21.Set(0,1,2);
|
|
i22.Set(0,0,3);
|
|
i22.Set(1,0,4);
|
|
//--- function call
|
|
CAp::Swap(i21,i22);
|
|
//--- check
|
|
if(((CAp::Rows(i21)==2 && CAp::Cols(i21)==1) && CAp::Rows(i22)==1) && CAp::Cols(i22)==2)
|
|
{
|
|
oki2=oki2 && i21[0][0]==3;
|
|
oki2=oki2 && i21[1][0]==4;
|
|
oki2=oki2 && i22[0][0]==1;
|
|
oki2=oki2 && i22[0][1]==2;
|
|
}
|
|
else
|
|
oki2=false;
|
|
//--- Test R2 swaps
|
|
r21.Resize(1,2);
|
|
r22.Resize(2,1);
|
|
//--- change values
|
|
r21.Set(0,0,1);
|
|
r21.Set(0,1,2);
|
|
r22.Set(0,0,3);
|
|
r22.Set(1,0,4);
|
|
//--- function call
|
|
CAp::Swap(r21,r22);
|
|
//--- check
|
|
if(((CAp::Rows(r21)==2 && CAp::Cols(r21)==1) && CAp::Rows(r22)==1) && CAp::Cols(r22)==2)
|
|
{
|
|
okr2=okr2 && r21[0][0]==3.0;
|
|
okr2=okr2 && r21[1][0]==4.0;
|
|
okr2=okr2 && r22[0][0]==1.0;
|
|
okr2=okr2 && r22[0][1]==2.0;
|
|
}
|
|
else
|
|
okr2=false;
|
|
//--- Test C2 swaps
|
|
c21.Resize(1,2);
|
|
c22.Resize(2,1);
|
|
//--- change values
|
|
c21.Set(0,0,1);
|
|
c21.Set(0,1,2);
|
|
c22.Set(0,0,3);
|
|
c22.Set(1,0,4);
|
|
//--- function call
|
|
CAp::Swap(c21,c22);
|
|
//--- check
|
|
if(((CAp::Rows(c21)==2 && CAp::Cols(c21)==1) && CAp::Rows(c22)==1) && CAp::Cols(c22)==2)
|
|
{
|
|
okc2=okc2 && c21[0][0]==3;
|
|
okc2=okc2 && c21[1][0]==4;
|
|
okc2=okc2 && c22[0][0]==1;
|
|
okc2=okc2 && c22[0][1]==2;
|
|
}
|
|
else
|
|
okc2=false;
|
|
//--- summary
|
|
result=result && okb1;
|
|
result=result && okb2;
|
|
result=result && oki1;
|
|
result=result && oki2;
|
|
result=result && okr1;
|
|
result=result && okr2;
|
|
result=result && okc1;
|
|
result=result && okc2;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
PrintResult("SWAPPING FUNCTIONS",result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests for swapping functions |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAlglibBasicsUnit::TestSerializationFunctions(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool okb;
|
|
bool oki;
|
|
bool okr;
|
|
int nb=0;
|
|
int ni=0;
|
|
int nr=0;
|
|
int i=0;
|
|
//--- objects of classes
|
|
CRec4Serialization r0;
|
|
CRec4Serialization r1;
|
|
//--- initialization
|
|
result=true;
|
|
okb=true;
|
|
oki=true;
|
|
okr=true;
|
|
//--- calculation
|
|
for(nb=1; nb<=4; nb++)
|
|
{
|
|
for(ni=1; ni<=4; ni++)
|
|
{
|
|
for(nr=1; nr<=4; nr++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(r0.m_b,nb);
|
|
for(i=0; i<=nb-1; i++)
|
|
r0.m_b[i]=CMath::RandomInteger(2)!=0;
|
|
//--- allocation
|
|
ArrayResize(r0.m_i,ni);
|
|
for(i=0; i<=ni-1; i++)
|
|
r0.m_i[i]=CMath::RandomInteger(10)-5;
|
|
//--- allocation
|
|
ArrayResize(r0.m_r,nr);
|
|
for(i=0; i<=nr-1; i++)
|
|
r0.m_r[i]=2*CMath::RandomReal()-1;
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CTestAlglibBasicsUnit::Rec4SerializationAlloc(_local_serializer,r0);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CTestAlglibBasicsUnit::Rec4SerializationSerialize(_local_serializer,r0);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CTestAlglibBasicsUnit::Rec4SerializationUnserialize(_local_serializer,r1);
|
|
_local_serializer.Stop();
|
|
}
|
|
//--- check
|
|
if((CAp::Len(r0.m_b)==CAp::Len(r1.m_b) && CAp::Len(r0.m_i)==CAp::Len(r1.m_i)) && CAp::Len(r0.m_r)==CAp::Len(r1.m_r))
|
|
{
|
|
//--- change value
|
|
for(i=0; i<=nb-1; i++)
|
|
okb=okb && ((r0.m_b[i] && r1.m_b[i])||(!r0.m_b[i] && !r1.m_b[i]));
|
|
for(i=0; i<=ni-1; i++)
|
|
oki=oki && r0.m_i[i]==r1.m_i[i];
|
|
for(i=0; i<=nr-1; i++)
|
|
okr=okr && r0.m_r[i]==r1.m_r[i];
|
|
}
|
|
else
|
|
oki=false;
|
|
}
|
|
}
|
|
}
|
|
//--- summary
|
|
result=result && okb;
|
|
result=result && oki;
|
|
result=result && okr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
{
|
|
PrintResult("SERIALIZATION FUNCTIONS",true);
|
|
}
|
|
else
|
|
{
|
|
PrintResult("SERIALIZATION FUNCTIONS",false);
|
|
PrintResult("* BOOLEAN ",okb);
|
|
PrintResult("* INTEGER ",oki);
|
|
PrintResult("* REAL ",okr);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test sparse matrix |
|
|
//+------------------------------------------------------------------+
|
|
class CSparseGenerator
|
|
{
|
|
public:
|
|
int m_m;
|
|
int m_matkind;
|
|
int m_n;
|
|
int m_triangle;
|
|
RCommState m_rcs;
|
|
CMatrixDouble m_bufa;
|
|
CHighQualityRandState m_rs;
|
|
|
|
CSparseGenerator(void) {}
|
|
~CSparseGenerator(void) {}
|
|
void Copy(const CSparseGenerator &obj);
|
|
//--- overloading
|
|
void operator=(const CSparseGenerator &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CSparseGenerator::Copy(const CSparseGenerator &obj)
|
|
{
|
|
m_m=obj.m_m;
|
|
m_matkind=obj.m_matkind;
|
|
m_n=obj.m_n;
|
|
m_triangle=obj.m_triangle;
|
|
m_rcs=obj.m_rcs;
|
|
m_bufa=obj.m_bufa;
|
|
m_rs=obj.m_rs;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSparseUnit
|
|
{
|
|
public:
|
|
static const int m_maxtype;
|
|
|
|
static bool TestSparse(bool silent);
|
|
static bool SKSTest(void);
|
|
static void CRSTest(bool errorflag);
|
|
static void TestSerialize(bool errorflag);
|
|
static bool BasicFuncTest(void);
|
|
static bool TestLevel2Unsymmetric(void);
|
|
static bool TestLevel2Triangular(void);
|
|
static bool TestLevel3Unsymmetric(void);
|
|
static bool TestLevel2Symmetric(void);
|
|
static bool TestLevel3Symmetric(void);
|
|
static bool TestSymmetricPerm(void);
|
|
static bool BasicFuncRandomTest(void);
|
|
static bool LinearFunctionsTest(void);
|
|
static bool LinearFunctionSSTest(void);
|
|
static bool LinearFunctionSMMTest(void);
|
|
static bool LinearFunctionsSMMTest(void);
|
|
static bool BasicCopyFuncTest(bool silent);
|
|
static bool CopyFuncTest(bool silent);
|
|
|
|
private:
|
|
static void InitGenerator(int m,int n,int matkind,int triangle,CSparseGenerator &g);
|
|
static bool GenerateNext(CSparseGenerator &g,CMatrixDouble &da,CSparseMatrix &sa);
|
|
static void CreateRandom(int m,int n,int pkind,int ckind,int p0,int p1,CMatrixDouble &da,CSparseMatrix &sa);
|
|
static bool EnumerateTest(void);
|
|
static bool RewriteExistingTest(void);
|
|
static void TestGetRow(bool &err);
|
|
static bool TestConvertSM(void);
|
|
static bool TestGCMatrixType(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestSparseUnit::m_maxtype=2;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestSparse(bool silent)
|
|
{
|
|
bool result=false;
|
|
bool waserrors=false;
|
|
bool basicerrors=false;
|
|
bool linearerrors=false;
|
|
bool basicrnderrors=false;
|
|
bool level2unsymmetricerrors=false;
|
|
bool level2symmetricerrors=false;
|
|
bool level2triangularerrors=false;
|
|
bool level3unsymmetricerrors=false;
|
|
bool level3symmetricerrors=false;
|
|
bool symmetricpermerrors=false;
|
|
bool linearserrors=false;
|
|
bool linearmmerrors=false;
|
|
bool linearsmmerrors=false;
|
|
bool getrowerrors=false;
|
|
bool serializeerrors=false;
|
|
bool copyerrors=false;
|
|
bool basiccopyerrors=false;
|
|
bool enumerateerrors=false;
|
|
bool rewriteexistingerr=false;
|
|
bool skserrors=false;
|
|
bool crserrors=false;
|
|
skserrors=SKSTest();
|
|
CRSTest(crserrors);
|
|
basicerrors=BasicFuncTest()||TestGCMatrixType();
|
|
basicrnderrors=BasicFuncRandomTest();
|
|
linearerrors=LinearFunctionsTest();
|
|
level2unsymmetricerrors=TestLevel2Unsymmetric();
|
|
level2symmetricerrors=TestLevel2Symmetric();
|
|
level2triangularerrors=TestLevel2Triangular();
|
|
level3unsymmetricerrors=TestLevel3Unsymmetric();
|
|
level3symmetricerrors=TestLevel3Symmetric();
|
|
symmetricpermerrors=TestSymmetricPerm();
|
|
linearserrors=LinearFunctionSSTest();
|
|
linearmmerrors=LinearFunctionSMMTest();
|
|
linearsmmerrors=LinearFunctionsSMMTest();
|
|
copyerrors=CopyFuncTest(true)||TestConvertSM();
|
|
basiccopyerrors=BasicCopyFuncTest(true);
|
|
enumerateerrors=EnumerateTest();
|
|
rewriteexistingerr=RewriteExistingTest();
|
|
TestGetRow(getrowerrors);
|
|
TestSerialize(serializeerrors);
|
|
//--- report
|
|
waserrors=(skserrors||crserrors||getrowerrors||serializeerrors ||
|
|
basicerrors||linearerrors||basicrnderrors||level2unsymmetricerrors ||
|
|
level2symmetricerrors||level2triangularerrors||level3unsymmetricerrors ||
|
|
level3symmetricerrors||symmetricpermerrors||linearserrors||linearmmerrors ||
|
|
linearsmmerrors||copyerrors||basiccopyerrors||enumerateerrors ||
|
|
rewriteexistingerr);
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SPARSE");
|
|
Print("STORAGE FORMAT SPECIFICS:");
|
|
PrintResult("* SKS",!skserrors);
|
|
PrintResult("* CRS",!crserrors);
|
|
Print("OPERATIONS:");
|
|
PrintResult("* GETROW",!getrowerrors);
|
|
PrintResult("* SERIALIZE",!serializeerrors);
|
|
Print("BLAS:");
|
|
PrintResult("* LEVEL 2 GENERAL",!level2unsymmetricerrors);
|
|
PrintResult("* LEVEL 2 SYMMETRIC",!level2symmetricerrors);
|
|
PrintResult("* LEVEL 2 TRIANGULAR",!level2triangularerrors);
|
|
PrintResult("* LEVEL 3 GENERAL",!level3unsymmetricerrors);
|
|
PrintResult("* LEVEL 3 SYMMETRIC",!level3symmetricerrors);
|
|
PrintResult("* PERMUTATIONS (SYMMETRIC)",!symmetricpermerrors);
|
|
PrintResult("BASIC TEST",!basicerrors);
|
|
PrintResult("COPY TEST",!copyerrors);
|
|
PrintResult("BASIC_COPY TEST",!basiccopyerrors);
|
|
PrintResult("BASIC_RND TEST",!basicrnderrors);
|
|
PrintResult("LINEAR TEST",!linearerrors);
|
|
PrintResult("LINEAR TEST FOR SYMMETRIC MATRICES",!linearserrors);
|
|
PrintResult("LINEAR MxM TEST",!linearmmerrors);
|
|
PrintResult("LINEAR MxM TEST FOR SYMMETRIC MATRICES",!linearsmmerrors);
|
|
PrintResult("ENUMERATE TEST",!enumerateerrors);
|
|
PrintResult("REWRITE EXISTING TEST",!rewriteexistingerr);
|
|
//---
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n\n");
|
|
}
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic SKS functional. |
|
|
//| Returns True on errors, False on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::SKSTest(void)
|
|
{
|
|
bool result=false;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix s1;
|
|
CSparseMatrix s2;
|
|
CSparseMatrix s3;
|
|
CSparseMatrix s4;
|
|
CSparseMatrix s5;
|
|
CSparseMatrix s6;
|
|
int n=0;
|
|
int nz=0;
|
|
double pnz=0;
|
|
int i=0;
|
|
int j=0;
|
|
int t0=0;
|
|
int t1=0;
|
|
CMatrixDouble a;
|
|
CMatrixInt wasenumerated;
|
|
CRowInt d;
|
|
CRowInt u;
|
|
CHighQualityRandState rs;
|
|
double v0=0;
|
|
double v1=0;
|
|
int uppercnt=0;
|
|
int lowercnt=0;
|
|
int bw=0;
|
|
|
|
result=false;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
nz=n*n-n;
|
|
while(true)
|
|
{
|
|
//--- Generate N*N matrix where probability of non-diagonal element
|
|
//--- being non-zero is PNZ. We also generate D and U - subdiagonal
|
|
//--- and superdiagonal profile sizes.
|
|
//--- Create matrix with either general SKS or banded SKS constructor function
|
|
if(n>1)
|
|
pnz=(double)nz/(double)(n*n-n);
|
|
else
|
|
pnz=1.0;
|
|
d.Resize(n);
|
|
u.Resize(n);
|
|
d.Fill(0);
|
|
u.Fill(0);
|
|
a=matrix<double>::Zeros(n,n);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
//--- Test SparseCreateSKS() functionality
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i==j||CHighQualityRand::HQRndUniformR(rs)<=pnz)
|
|
{
|
|
a.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
if(j<i)
|
|
d.Set(i,MathMax(d[i],i-j));
|
|
else
|
|
u.Set(j,MathMax(u[j],j-i));
|
|
}
|
|
else
|
|
a.Set(i,j,0.0);
|
|
}
|
|
CSparse::SparseCreateSKS(n,n,d,u,s0);
|
|
}
|
|
else
|
|
{
|
|
//--- Test SparseCreateSKSBand() functionality
|
|
bw=CHighQualityRand::HQRndUniformI(rs,n+1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
d.Set(i,MathMin(bw,i));
|
|
u.Set(i,MathMin(bw,i));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(MathAbs(i-j)<=bw && CHighQualityRand::HQRndUniformR(rs)<=pnz)
|
|
a.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
else
|
|
a.Set(i,j,0.0);
|
|
}
|
|
}
|
|
CSparse::SparseCreateSKSBand(n,n,bw,s0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if(a.Get(i,j)!=0.0)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAp::SetErrorFlag(result,!CSparse::SparseRewriteExisting(s0,i,j,a.Get(i,j)),"testsparseunit.ap:456");
|
|
else
|
|
CSparse::SparseSet(s0,i,j,a.Get(i,j));
|
|
}
|
|
uppercnt=0;
|
|
lowercnt=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
uppercnt=uppercnt+u[i];
|
|
lowercnt=lowercnt+d[i];
|
|
}
|
|
//--- Check correctness of SparseExists()
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i>=j && i-j>d[i])
|
|
CAp::SetErrorFlag(result,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:475");
|
|
if(i>=j && i-j<=d[i])
|
|
CAp::SetErrorFlag(result,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:477");
|
|
if(i<=j && j-i>u[j])
|
|
CAp::SetErrorFlag(result,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:479");
|
|
if(i<=j && j-i<=u[j])
|
|
CAp::SetErrorFlag(result,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:481");
|
|
}
|
|
//--- Try to call SparseRewriteExisting() for out-of-band elements, make sure that it returns False.
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((i>=j && i-j>d[i]) || (i<=j && j-i>u[j]))
|
|
CAp::SetErrorFlag(result,CSparse::SparseRewriteExisting(s0,i,j,1.0),"testsparseunit.ap:490");
|
|
//--- Convert to several different formats, check their contents with SparseGet().
|
|
CSparse::SparseCopy(s0,s1);
|
|
CSparse::SparseConvertToCRS(s1);
|
|
CSparse::SparseCopyToCRS(s0,s2);
|
|
CSparse::SparseCopyToCRSBuf(s0,s3);
|
|
CSparse::SparseCopyToHash(s0,s4);
|
|
CSparse::SparseCopyToHashBuf(s0,s5);
|
|
CSparse::SparseCopy(s0,s6);
|
|
CSparse::SparseConvertToHash(s6);
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetNRows(s0)!=n,"testsparseunit.ap:503");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetNCols(s0)!=n,"testsparseunit.ap:504");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetMatrixType(s0)!=2,"testsparseunit.ap:505");
|
|
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s0),"testsparseunit.ap:506");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s0),"testsparseunit.ap:507");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s0),"testsparseunit.ap:508");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsSKS(s1),"testsparseunit.ap:509");
|
|
CAp::SetErrorFlag(result,!CSparse::SparseIsCRS(s1),"testsparseunit.ap:510");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s1),"testsparseunit.ap:511");
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v1=a.Get(i,i);
|
|
v0=CSparse::SparseGetDiagonal(s0,i);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:516");
|
|
}
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v1=a.Get(i,j);
|
|
v0=CSparse::SparseGet(s0,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:523");
|
|
v0=CSparse::SparseGet(s1,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:525");
|
|
v0=CSparse::SparseGet(s2,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:527");
|
|
v0=CSparse::SparseGet(s3,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:529");
|
|
v0=CSparse::SparseGet(s4,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:531");
|
|
v0=CSparse::SparseGet(s5,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:533");
|
|
v0=CSparse::SparseGet(s6,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:535");
|
|
}
|
|
//--- Check enumeration capabilities:
|
|
//--- * each element returned by SparseEnumerate() is returned only once
|
|
//--- * each non-zero element of A was enumerated
|
|
wasenumerated.Resize(n,n);
|
|
wasenumerated.Fill((int)false);
|
|
t0=0;
|
|
t1=0;
|
|
while(CSparse::SparseEnumerate(s0,t0,t1,i,j,v0))
|
|
{
|
|
CAp::SetErrorFlag(result,(bool)wasenumerated.Get(i,j),"testsparseunit.ap:551");
|
|
wasenumerated.Set(i,j,(int)true);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if(a.Get(i,j)!=0.0)
|
|
CAp::SetErrorFlag(result,!wasenumerated.Get(i,j),"testsparseunit.ap:557");
|
|
//--- Check UpperCnt()/LowerCnt()
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetUpperCount(s0)!=uppercnt,"testsparseunit.ap:562");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetLowerCount(s0)!=lowercnt,"testsparseunit.ap:563");
|
|
//--- Check in-place transposition
|
|
CSparse::SparseCopy(s0,s1);
|
|
CSparse::SparseTransposeSKS(s1);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v0=CSparse::SparseGet(s0,i,j);
|
|
v1=CSparse::SparseGet(s1,j,i);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:575");
|
|
}
|
|
//--- One more check - matrix is initially created in some other format
|
|
//--- (CRS or Hash) and converted to SKS later.
|
|
CSparse::SparseCreate(n,n,0,s0);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if(a.Get(i,j)!=0.0)
|
|
CSparse::SparseSet(s0,i,j,a.Get(i,j));
|
|
//---
|
|
CSparse::SparseCopy(s0,s1);
|
|
CSparse::SparseConvertToSKS(s1);
|
|
CSparse::SparseCopyToSKS(s0,s2);
|
|
CSparse::SparseCopyToSKSBuf(s0,s3);
|
|
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s1),"testsparseunit.ap:591");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s1),"testsparseunit.ap:592");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s1),"testsparseunit.ap:593");
|
|
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s2),"testsparseunit.ap:594");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s2),"testsparseunit.ap:595");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s2),"testsparseunit.ap:596");
|
|
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s3),"testsparseunit.ap:597");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s3),"testsparseunit.ap:598");
|
|
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s3),"testsparseunit.ap:599");
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v1=a.Get(i,j);
|
|
v0=CSparse::SparseGet(s1,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:605");
|
|
v0=CSparse::SparseGet(s2,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:607");
|
|
v0=CSparse::SparseGet(s3,i,j);
|
|
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:609");
|
|
}
|
|
//--- Increase problem sparcity and try one more time.
|
|
//--- Stop after testing NZ=0.
|
|
if(nz==0)
|
|
break;
|
|
nz=(int)MathMin((int)MathRound(nz*0.95),nz-1);
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing CRS-specific functionality. |
|
|
//| On failure sets ErrorFlag, on success does not touch it. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSparseUnit::CRSTest(bool errorflag)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int nz=0;
|
|
double pnz=0;
|
|
CHighQualityRandState rs;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix s1;
|
|
CSparseMatrix s2;
|
|
CSparseMatrix s3;
|
|
CSparseMatrix s4;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(m=1; m<=10; m++)
|
|
{
|
|
nz=n*m;
|
|
while(true)
|
|
{
|
|
//--- Generate N*N matrix where probability of non-diagonal element
|
|
//--- being non-zero is PNZ. We also generate D and U - subdiagonal
|
|
//--- and superdiagonal profile sizes.
|
|
//--- Create matrix with either general SKS or banded SKS constructor function
|
|
if(n>1)
|
|
pnz=(double)nz/(double)(m*n);
|
|
else
|
|
pnz=1.0;
|
|
//--- Generate random matrix in HASH format (testing SparseExists()
|
|
//--- during process), copy it to CRS format, compare with original
|
|
CSparse::SparseCreate(m,n,(int)MathRound(nz*CHighQualityRand::HQRndUniformR(rs)),s0);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:676");
|
|
if(CHighQualityRand::HQRndUniformR(rs)<=pnz)
|
|
{
|
|
CSparse::SparseSet(s0,i,j,CHighQualityRand::HQRndNormal(rs));
|
|
CAp::SetErrorFlag(errorflag,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:680");
|
|
}
|
|
}
|
|
CSparse::SparseCopyToCRS(s0,s1);
|
|
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s1),"testsparseunit.ap:684");
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s0,i,j)!=CSparse::SparseGet(s1,i,j),"testsparseunit.ap:688");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s0,i,j) && !CSparse::SparseExists(s1,i,j),"testsparseunit.ap:689");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s1,i,j) && !CSparse::SparseExists(s0,i,j),"testsparseunit.ap:690");
|
|
}
|
|
if(errorflag)
|
|
return;
|
|
//--- Check transposition
|
|
CSparse::SparseCopyToCRS(s1,s2);
|
|
CSparse::SparseTransposeCRS(s2);
|
|
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s2),"testsparseunit.ap:700");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s1)!=CSparse::SparseGetNCols(s2),"testsparseunit.ap:701");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s1)!=CSparse::SparseGetNRows(s2),"testsparseunit.ap:702");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s1,i,j)!=CSparse::SparseGet(s2,j,i),"testsparseunit.ap:707");
|
|
if(errorflag)
|
|
return;
|
|
//--- Check transposition
|
|
CSparse::SparseCopyTransposeCRS(s1,s3);
|
|
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s3),"testsparseunit.ap:715");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s1)!=CSparse::SparseGetNCols(s3),"testsparseunit.ap:716");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s1)!=CSparse::SparseGetNRows(s3),"testsparseunit.ap:717");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s1,i,j)!=CSparse::SparseGet(s3,j,i),"testsparseunit.ap:722");
|
|
if(errorflag)
|
|
return;
|
|
//--- Check transposition
|
|
CSparse::SparseCopyTransposeCRS(s1,s4);
|
|
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s4),"testsparseunit.ap:730");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s1)!=CSparse::SparseGetNCols(s4),"testsparseunit.ap:731");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s1)!=CSparse::SparseGetNRows(s4),"testsparseunit.ap:732");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s1,i,j)!=CSparse::SparseGet(s4,j,i),"testsparseunit.ap:737");
|
|
if(errorflag)
|
|
return;
|
|
//--- Increase problem sparcity and try one more time.
|
|
//--- Stop after testing NZ=0.
|
|
if(nz==0)
|
|
break;
|
|
nz=MathMin((int)MathRound(nz*0.95),nz-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing serialization. |
|
|
//| On failure sets ErrorFlag, on success does not touch it. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSparseUnit::TestSerialize(bool errorflag)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int mtype=0;
|
|
int nz=0;
|
|
double pnz=0;
|
|
CHighQualityRandState rs;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix s1;
|
|
CSparseMatrix s2;
|
|
CSparseMatrix s3;
|
|
CSparseMatrix s4;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(m=1; m<=10; m++)
|
|
{
|
|
for(mtype=0; mtype<=m_maxtype; mtype++)
|
|
{
|
|
if(mtype==2 && m!=n)
|
|
continue;
|
|
nz=n*m;
|
|
while(true)
|
|
{
|
|
//--- Generate N*N matrix where probability of non-diagonal element
|
|
//--- being non-zero is PNZ.
|
|
if(n>1)
|
|
pnz=(double)nz/(double)(m*n);
|
|
else
|
|
pnz=0.5;
|
|
//--- Generate random matrix in HASH format (testing SparseExists()
|
|
//--- during process), convert it to HASH/CRS/SKS format
|
|
CSparse::SparseCreate(m,n,(int)MathRound(nz*CHighQualityRand::HQRndUniformR(rs)),s0);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:797");
|
|
if(CHighQualityRand::HQRndUniformR(rs)<=pnz)
|
|
{
|
|
CSparse::SparseSet(s0,i,j,CHighQualityRand::HQRndNormal(rs));
|
|
CAp::SetErrorFlag(errorflag,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:801");
|
|
}
|
|
}
|
|
CSparse::SparseConvertTo(s0,mtype);
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetMatrixType(s0)!=mtype,"testsparseunit.ap:805");
|
|
//--- Check copy-through-serialization
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CSparse::SparseAlloc(_local_serializer,s0);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CSparse::SparseSerialize(_local_serializer,s0);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CSparse::SparseUnserialize(_local_serializer,s1);
|
|
_local_serializer.Stop();
|
|
}
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetMatrixType(s1)!=mtype,"testsparseunit.ap:811");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s0)!=CSparse::SparseGetNRows(s1),"testsparseunit.ap:812");
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s0)!=CSparse::SparseGetNCols(s1),"testsparseunit.ap:813");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s0,i,j)!=(double)(CSparse::SparseGet(s1,i,j)),"testsparseunit.ap:818");
|
|
if(errorflag)
|
|
return;
|
|
//--- Increase problem sparcity and try one more time.
|
|
//--- Stop after testing NZ=0.
|
|
if(nz==0)
|
|
break;
|
|
nz=MathMin((int)MathRound(nz*0.95),nz-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functional |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::BasicFuncTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
int uppercnt=0;
|
|
int lowercnt=0;
|
|
CMatrixDouble a;
|
|
|
|
n=10;
|
|
m=10;
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<n; j++)
|
|
{
|
|
CSparse::SparseCreate(i,j,1,s);
|
|
a=matrix<double>::Zeros(i,j);
|
|
//--- Checking for Matrix with hash table type
|
|
uppercnt=0;
|
|
lowercnt=0;
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
if(j1>i1)
|
|
uppercnt++;
|
|
if(j1<i1)
|
|
lowercnt++;
|
|
a.Set(i1,j1,i1+j1+(double)((i+j)*(m+n))/2.0+1);
|
|
CSparse::SparseSet(s,i1,j1,i1+j1+(double)((i+j)*(m+n))/(double)2);
|
|
CSparse::SparseAdd(s,i1,j1,1);
|
|
if(a.Get(i1,j1)!=CSparse::SparseGet(s,i1,j1))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
for(i1=0; i1<MathMin(i,j); i1++)
|
|
if(a.Get(i1,i1)!=CSparse::SparseGetDiagonal(s,i1))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetUpperCount(s)!=uppercnt,"testsparseunit.ap:887");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetLowerCount(s)!=lowercnt,"testsparseunit.ap:888");
|
|
//--- Checking for Matrix with CRS type
|
|
CSparse::SparseConvertToCRS(s);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
if(a.Get(i1,j1)!=CSparse::SparseGet(s,i1,j1))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i1=0; i1<MathMin(i,j); i1++)
|
|
if(a.Get(i1,i1)!=CSparse::SparseGetDiagonal(s,i1))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetUpperCount(s)!=uppercnt,"testsparseunit.ap:907");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetLowerCount(s)!=lowercnt,"testsparseunit.ap:908");
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing Level 2 unsymmetric linear algebra functions|
|
|
//| Additionally it tests SparseGet() for several matrix formats. |
|
|
//| Returns True on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestLevel2Unsymmetric(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int m=0;
|
|
int n=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble y0;
|
|
CRowDouble y1;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix sa;
|
|
double eps=0;
|
|
double v=0;
|
|
int ix=0;
|
|
int iy=0;
|
|
double alpha=0;
|
|
double beta=0;
|
|
int ops=0;
|
|
int opm=0;
|
|
int opn=0;
|
|
int alphakind=0;
|
|
int betakind=0;
|
|
CSparseGenerator g;
|
|
CHighQualityRandState rs;
|
|
int i_=0;
|
|
|
|
eps=10000*CMath::m_machineepsilon;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test linear algebra functions
|
|
for(m=1; m<=20; m++)
|
|
{
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
InitGenerator(m,n,0,0,g);
|
|
while(GenerateNext(g,a,sa))
|
|
{
|
|
//--- Convert SA to desired storage format:
|
|
//--- * to CRS if M<>N
|
|
//--- * with 50% probability to CRS or SKS, if M=N
|
|
if(m!=n || CHighQualityRand::HQRndUniformR(rs)<0.5)
|
|
CSparse::SparseCopyToCRS(sa,s0);
|
|
else
|
|
CSparse::SparseCopyToSKS(sa,s0);
|
|
//--- Test SparseGet() for SA and S0 against matrix returned in A
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(sa,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:966");
|
|
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:967");
|
|
}
|
|
//--- Test SparseMV
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseMV(s0,x0,y0);
|
|
CAp::SetErrorFlag(result,CAp::Len(y0)<m,"testsparseunit.ap:981");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0[i])>eps,"testsparseunit.ap:987");
|
|
}
|
|
//--- Test SparseMTV
|
|
x0=vector<double>::Zeros(m);
|
|
for(j=0; j<m; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseMTV(s0,x0,y0);
|
|
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1001");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=x1.DotC(a,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0[j])>eps,"testsparseunit.ap:1007");
|
|
}
|
|
//--- Sparse GEMV
|
|
for(ops=0; ops<=1; ops++)
|
|
{
|
|
for(alphakind=0; alphakind<=1; alphakind++)
|
|
{
|
|
for(betakind=0; betakind<=1; betakind++)
|
|
{
|
|
//--- Prepare inputs for testing
|
|
ix=CHighQualityRand::HQRndUniformI(rs,50);
|
|
iy=CHighQualityRand::HQRndUniformI(rs,50);
|
|
alpha=alphakind*CHighQualityRand::HQRndNormal(rs);
|
|
beta=betakind*CHighQualityRand::HQRndNormal(rs);
|
|
if(ops==0)
|
|
{
|
|
opm=m;
|
|
opn=n;
|
|
}
|
|
else
|
|
{
|
|
opm=n;
|
|
opn=m;
|
|
}
|
|
x0=vector<double>::Zeros(ix+opn);
|
|
for(j=0; j<ix+opn; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
y0=vector<double>::Zeros(iy+opm);
|
|
for(j=0; j<iy+opm; j++)
|
|
y0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
y1=y0;
|
|
//--- Prepare reference result in Y1
|
|
for(i=0; i<opm; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<=opn-1; j++)
|
|
{
|
|
if(ops==0)
|
|
v+=a.Get(i,j)*x1[ix+j];
|
|
else
|
|
v+=a.Get(j,i)*x1[ix+j];
|
|
}
|
|
y1.Set(iy+i,alpha*v+beta*y1[iy+i]);
|
|
}
|
|
//--- Test
|
|
CSparse::SparseGemV(s0,alpha,ops,x0,ix,beta,y0,iy);
|
|
for(j=0; j<iy+opm; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y0[j]-y1[j])>eps,"testsparseunit.ap:1068");
|
|
}
|
|
}
|
|
}
|
|
//--- Test SparseMV2
|
|
if(m==n)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseMV2(s0,x0,y0,y1);
|
|
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1084");
|
|
CAp::SetErrorFlag(result,CAp::Len(y1)<n,"testsparseunit.ap:1085");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=x1.DotR(a,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0[j])>eps,"testsparseunit.ap:1091");
|
|
v=x1.DotC(a,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y1[j])>eps,"testsparseunit.ap:1093");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing Level 3 unsymmetric linear algebra functions|
|
|
//| Additionally it tests SparseGet() for several matrix formats. |
|
|
//| Returns True on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestLevel3Unsymmetric(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int m=0;
|
|
int n=0;
|
|
int k=0;
|
|
CMatrixDouble x0;
|
|
CMatrixDouble x1;
|
|
CMatrixDouble y0;
|
|
CMatrixDouble y1;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix sa;
|
|
double eps=0;
|
|
double v=0;
|
|
CSparseGenerator g;
|
|
CHighQualityRandState rs;
|
|
int i_=0;
|
|
|
|
eps=10000*CMath::m_machineepsilon;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test linear algebra functions
|
|
for(m=1; m<=20; m++)
|
|
{
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
InitGenerator(m,n,0,0,g);
|
|
while(GenerateNext(g,a,sa))
|
|
{
|
|
//--- Choose matrix width K
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,20);
|
|
//--- Convert SA to desired storage format:
|
|
//--- * to CRS if M<>N
|
|
//--- * with 50% probability to CRS or SKS, if M=N
|
|
if(m!=n || CHighQualityRand::HQRndUniformR(rs)<0.5)
|
|
CSparse::SparseCopyToCRS(sa,s0);
|
|
else
|
|
CSparse::SparseCopyToSKS(sa,s0);
|
|
//--- Test SparseGet() for SA and S0 against matrix returned in A
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(result,CSparse::SparseGet(sa,i,j)!=a.Get(i,j),"testsparseunit.ap:1156");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGet(s0,i,j)!=a.Get(i,j),"testsparseunit.ap:1157");
|
|
}
|
|
//--- Test SparseMV
|
|
x0=matrix<double>::Zeros(n,k);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<k; j++)
|
|
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseMM(s0,x0,k,y0);
|
|
CAp::SetErrorFlag(result,CAp::Rows(y0)<m,"testsparseunit.ap:1172");
|
|
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1173");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v=0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*x1.Get(i_,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1180");
|
|
}
|
|
//--- Test SparseMTM
|
|
x0=matrix<double>::Zeros(m,k);
|
|
x1=matrix<double>::Zeros(m,k);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<k; j++)
|
|
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseMTM(s0,x0,k,y0);
|
|
CAp::SetErrorFlag(result,CAp::Rows(y0)<n,"testsparseunit.ap:1195");
|
|
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1196");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=a.Get(i_,i)*x1.Get(i_,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1203");
|
|
}
|
|
//--- Test SparseMM2
|
|
if(m==n)
|
|
{
|
|
x0=matrix<double>::Zeros(n,k);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<k; j++)
|
|
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseMM2(s0,x0,k,y0,y1);
|
|
CAp::SetErrorFlag(result,CAp::Rows(y0)<n,"testsparseunit.ap:1220");
|
|
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1221");
|
|
CAp::SetErrorFlag(result,CAp::Rows(y1)<n,"testsparseunit.ap:1222");
|
|
CAp::SetErrorFlag(result,CAp::Cols(y1)<k,"testsparseunit.ap:1223");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*x1.Get(i_,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1230");
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i_,i)*x1.Get(i_,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y1.Get(i,j))>eps,"testsparseunit.ap:1232");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing Level 2 symmetric linear algebra functions. |
|
|
//| Additionally it tests SparseGet() for several matrix formats. |
|
|
//| Returns True on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestLevel2Symmetric(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble y0;
|
|
CRowDouble y1;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix s1;
|
|
CSparseMatrix sa;
|
|
double eps=0;
|
|
double v=0;
|
|
double va=0;
|
|
double vb=0;
|
|
CSparseGenerator g;
|
|
CHighQualityRandState rs;
|
|
bool isupper=false;
|
|
int triangletype=0;
|
|
int i_=0;
|
|
|
|
eps=10000*CMath::m_machineepsilon;
|
|
result=false;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test linear algebra functions
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
for(triangletype=-1; triangletype<=1; triangletype++)
|
|
{
|
|
isupper=(CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
if(triangletype<0)
|
|
isupper=false;
|
|
if(triangletype>0)
|
|
isupper=true;
|
|
InitGenerator(n,n,0,triangletype,g);
|
|
while(GenerateNext(g,a,sa))
|
|
{
|
|
//--- Convert SA to desired storage format:
|
|
//--- * S0 stores unmodified copy
|
|
//--- * S1 stores copy with unmodified triangle corresponding
|
|
//--- to IsUpper and another triangle being spoiled by random
|
|
//--- trash
|
|
CSparse::SparseCopyToHash(sa,s1);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
|
|
{
|
|
CSparse::SparseCopyToCRS(sa,s0);
|
|
CSparse::SparseConvertToCRS(s1);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseCopyToSKS(sa,s0);
|
|
CSparse::SparseConvertToSKS(s1);
|
|
}
|
|
//--- Test SparseGet() for SA and S0 against matrix returned in A
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(sa,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:1311");
|
|
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:1312");
|
|
CAp::SetErrorFlag(result,(j<i && triangletype==1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1313");
|
|
CAp::SetErrorFlag(result,(j>i && triangletype==-1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1314");
|
|
}
|
|
//--- Before we proceed with testing, update empty triangle of A
|
|
//--- with its copy from another part of the matrix.
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
a.Set(i,j,a.Get(j,i));
|
|
//--- Test SparseSMV
|
|
x0=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseSMV(s0,isupper,x0,y0);
|
|
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1337");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x1.DotR(a,i);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0[i])>eps,"testsparseunit.ap:1343");
|
|
}
|
|
CSparse::SparseSMV(s1,isupper,x0,y1);
|
|
CAp::SetErrorFlag(result,CAp::Len(y1)<n,"testsparseunit.ap:1346");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x1.DotR(a,i);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y1[i])>eps,"testsparseunit.ap:1352");
|
|
}
|
|
//--- Test SparseVSMV
|
|
x0=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
vb=0.0;
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
vb+=x1[i]*a.Get(i,j)*x1[j];
|
|
va=CSparse::SparseVSMV(s0,isupper,x0);
|
|
CAp::SetErrorFlag(result,MathAbs(va-vb)>eps,"testsparseunit.ap:1370");
|
|
va=CSparse::SparseVSMV(s1,isupper,x0);
|
|
CAp::SetErrorFlag(result,MathAbs(va-vb)>eps,"testsparseunit.ap:1372");
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing Level 2 symmetric linear algebra functions. |
|
|
//| Additionally it tests SparseGet() for several matrix formats. |
|
|
//| Returns True on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestLevel3Symmetric(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int k=0;
|
|
CMatrixDouble x0;
|
|
CMatrixDouble x1;
|
|
CMatrixDouble y0;
|
|
CMatrixDouble y1;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix s1;
|
|
CSparseMatrix sa;
|
|
double eps=0;
|
|
double v=0;
|
|
CSparseGenerator g;
|
|
CHighQualityRandState rs;
|
|
bool isupper=false;
|
|
int triangletype=0;
|
|
int i_=0;
|
|
|
|
eps=10000*CMath::m_machineepsilon;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test linear algebra functions
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
for(triangletype=-1; triangletype<=1; triangletype++)
|
|
{
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
|
|
if(triangletype<0)
|
|
isupper=false;
|
|
if(triangletype>0)
|
|
isupper=true;
|
|
InitGenerator(n,n,0,triangletype,g);
|
|
while(GenerateNext(g,a,sa))
|
|
{
|
|
//--- Choose matrix width K
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,20);
|
|
//--- Convert SA to desired storage format:
|
|
//--- * S0 stores unmodified copy
|
|
//--- * S1 stores copy with unmodified triangle corresponding
|
|
//--- to IsUpper and another triangle being spoiled by random
|
|
//--- trash
|
|
CSparse::SparseCopyToHash(sa,s1);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
|
|
{
|
|
CSparse::SparseCopyToCRS(sa,s0);
|
|
CSparse::SparseConvertToCRS(s1);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseCopyToSKS(sa,s0);
|
|
CSparse::SparseConvertToSKS(s1);
|
|
}
|
|
//--- Test SparseGet() for SA and S0 against matrix returned in A
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(CSparse::SparseGet(sa,i,j)-a.Get(i,j)))>eps,"testsparseunit.ap:1453");
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j)))>eps,"testsparseunit.ap:1454");
|
|
CAp::SetErrorFlag(result,(j<i && triangletype==1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1455");
|
|
CAp::SetErrorFlag(result,(j>i && triangletype==-1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1456");
|
|
}
|
|
//--- Before we proceed with testing, update empty triangle of A
|
|
//--- with its copy from another part of the matrix.
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
a.Set(i,j,a.Get(j,i));
|
|
//--- Test SparseSMM
|
|
x0=matrix<double>::Zeros(n,k);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<k; j++)
|
|
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseSMM(s0,isupper,x0,k,y0);
|
|
CAp::SetErrorFlag(result,CAp::Rows(y0)<n,"testsparseunit.ap:1480");
|
|
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1481");
|
|
if(result)
|
|
return(result);
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=a.Get(i,i_)*x1.Get(i_,j);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1488");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing sparse symmetric permutations |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestSymmetricPerm(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int functype=0;
|
|
int i=0;
|
|
int j=0;
|
|
CHighQualityRandState rs;
|
|
bool isupper=false;
|
|
double eps=0;
|
|
double nzprob=0;
|
|
CMatrixDouble da;
|
|
CMatrixDouble db;
|
|
CSparseMatrix sa;
|
|
CSparseMatrix sb;
|
|
CRowInt ptbl;
|
|
CRowInt pprod;
|
|
|
|
eps=10*CMath::m_machineepsilon;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Try various N and fill factors
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
nzprob=1.0;
|
|
while((double)(nzprob)>=(double)(0.1/(n*n)))
|
|
{
|
|
//--- Generate matrix with desired fill factor, randomly select one triangle,
|
|
//--- generate random permutation (in table and product form)
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
da=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<(double)(nzprob))
|
|
{
|
|
da.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
CSparse::SparseSet(sa,i,j,da.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(sa);
|
|
isupper=CHighQualityRand::HQRndNormal(rs)>0.0;
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<=i; j++)
|
|
{
|
|
if(isupper)
|
|
da.Set(i,j,da.Get(j,i));
|
|
else
|
|
da.Set(j,i,da.Get(i,j));
|
|
}
|
|
ptbl.Resize(n);
|
|
pprod.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
ptbl.Set(i,i);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
pprod.Set(i,i+CHighQualityRand::HQRndUniformI(rs,n-i));
|
|
j=ptbl[i];
|
|
ptbl.Set(i,ptbl[pprod[i]]);
|
|
ptbl.Set(pprod[i],j);
|
|
}
|
|
db=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
db.Set(ptbl[i],ptbl[j],da.Get(i,j));
|
|
//--- Select function to test, run and test
|
|
functype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
if(functype==0)
|
|
CSparse::SparseSymmPermTblBuf(sa,isupper,ptbl,sb);
|
|
if(functype==1)
|
|
CSparse::SparseSymmPermTbl(sa,isupper,ptbl,sb);
|
|
CAp::SetErrorFlag(result,!CSparse::SparseIsCRS(sb),"testsparseunit.ap:1582");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetNRows(sb)!=n,"testsparseunit.ap:1583");
|
|
CAp::SetErrorFlag(result,CSparse::SparseGetNCols(sb)!=n,"testsparseunit.ap:1584");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(result,(isupper && j>=i) && MathAbs(db.Get(i,j)-CSparse::SparseGet(sb,i,j))>eps,"testsparseunit.ap:1590");
|
|
CAp::SetErrorFlag(result,(isupper && j<i) && CSparse::SparseGet(sb,i,j)!=0.0,"testsparseunit.ap:1591");
|
|
}
|
|
//--- Increase sparsity
|
|
nzprob*=0.75;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing Level 2 triangular linear algebra functions.|
|
|
//| Returns True on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestLevel2Triangular(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble y0;
|
|
CRowDouble y1;
|
|
CRowDouble ey;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble ea;
|
|
CSparseMatrix s0;
|
|
CSparseMatrix s1;
|
|
CSparseMatrix sa;
|
|
double eps=0;
|
|
double v=0;
|
|
CSparseGenerator g;
|
|
CHighQualityRandState rs;
|
|
bool isupper=false;
|
|
bool isunit=false;
|
|
int optype=0;
|
|
int triangletype=0;
|
|
int i_=0;
|
|
|
|
eps=10000*CMath::m_machineepsilon;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test sparseTRMV
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
for(triangletype=-1; triangletype<=1; triangletype++)
|
|
{
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
|
|
if(triangletype<0)
|
|
isupper=false;
|
|
if(triangletype>0)
|
|
isupper=true;
|
|
InitGenerator(n,n,0,triangletype,g);
|
|
while(GenerateNext(g,a,sa))
|
|
{
|
|
//--- Settings (IsUpper was already set, handle the rest)
|
|
isunit=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
optype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
//--- Convert SA to desired storage format:
|
|
//--- * S0 stores unmodified copy
|
|
//--- * S1 stores copy with unmodified triangle corresponding
|
|
//--- to IsUpper and another triangle being spoiled by random
|
|
//--- trash
|
|
CSparse::SparseCopyToHash(sa,s1);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
|
|
{
|
|
CSparse::SparseCopyToCRS(sa,s0);
|
|
CSparse::SparseConvertToCRS(s1);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseCopyToSKS(sa,s0);
|
|
CSparse::SparseConvertToSKS(s1);
|
|
}
|
|
//--- Generate "effective A"
|
|
ea=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
{
|
|
i1=i;
|
|
j1=j;
|
|
if(optype==1)
|
|
CApServ::Swap(i1,j1);
|
|
ea.Set(i1,j1,a.Get(i,j));
|
|
if(isunit && i1==j1)
|
|
ea.Set(i1,j1,1.0);
|
|
}
|
|
//--- Test SparseTRMV
|
|
x0=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1=x0;
|
|
CSparse::SparseTRMV(s0,isupper,isunit,optype,x0,y0);
|
|
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1706");
|
|
if(result)
|
|
return(result);
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x1.DotR(ea,i);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y0[i])>eps,"testsparseunit.ap:1712");
|
|
}
|
|
//---
|
|
CSparse::SparseTRMV(s0,isupper,isunit,optype,x0,y1);
|
|
CAp::SetErrorFlag(result,CAp::Len(y1)<n,"testsparseunit.ap:1715");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x1.DotR(ea,i);
|
|
CAp::SetErrorFlag(result,MathAbs(v-y1[i])>eps,"testsparseunit.ap:1721");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test sparseTRSV
|
|
for(n=1; n<=20; n++)
|
|
{
|
|
for(triangletype=-1; triangletype<=1; triangletype++)
|
|
{
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
|
|
if(triangletype==-1)
|
|
isupper=false;
|
|
if(triangletype==1)
|
|
isupper=true;
|
|
InitGenerator(n,n,1,triangletype,g);
|
|
while(GenerateNext(g,a,sa))
|
|
{
|
|
//--- Settings (IsUpper was already set, handle the rest)
|
|
isunit=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
optype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
//--- Convert SA to desired storage format:
|
|
//--- * S0 stores unmodified copy
|
|
//--- * S1 stores copy with unmodified triangle corresponding
|
|
//--- to IsUpper and another triangle being spoiled by random
|
|
//--- trash
|
|
CSparse::SparseCopyToHash(sa,s1);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
|
|
{
|
|
CSparse::SparseCopyToCRS(sa,s0);
|
|
CSparse::SparseConvertToCRS(s1);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseCopyToSKS(sa,s0);
|
|
CSparse::SparseConvertToSKS(s1);
|
|
}
|
|
//--- Generate "effective A" and EY = inv(EA)*x0
|
|
ea=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
{
|
|
i1=i;
|
|
j1=j;
|
|
if(optype==1)
|
|
CApServ::Swap(i1,j1);
|
|
ea.Set(i1,j1,a.Get(i,j));
|
|
if(isunit && i1==j1)
|
|
ea.Set(i1,j1,1.0);
|
|
}
|
|
ey=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
ey.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=ey.DotR(ea,i);
|
|
x0.Set(i,v);
|
|
}
|
|
x1=x0;
|
|
//--- Test SparseTRSV
|
|
CSparse::SparseTRSV(s0,isupper,isunit,optype,x0);
|
|
CAp::SetErrorFlag(result,CAp::Len(x0)<n,"testsparseunit.ap:1805");
|
|
if(result)
|
|
return(result);
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(ey[i]-x0[i])>eps,"testsparseunit.ap:1809");
|
|
CSparse::SparseTRSV(s1,isupper,isunit,optype,x1);
|
|
CAp::SetErrorFlag(result,CAp::Len(x1)<n,"testsparseunit.ap:1811");
|
|
if(result)
|
|
return(result);
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(ey[i]-x1[i])>eps,"testsparseunit.ap:1815");
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functional |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::BasicFuncRandomTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
CMatrixDouble a;
|
|
int mfigure=0;
|
|
int temp=0;
|
|
|
|
n=20;
|
|
m=20;
|
|
mfigure=10;
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<n; j++)
|
|
{
|
|
CSparse::SparseCreate(i,j,0,s);
|
|
a=matrix<double>::Zeros(i,j);
|
|
//--- Checking for Matrix with hash table type
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
temp=2*CMath::RandomInteger(mfigure)-mfigure;
|
|
a.Set(i1,j1,temp);
|
|
if(CMath::RandomInteger(2)==0)
|
|
{
|
|
CSparse::SparseSet(s,i1,j1,temp);
|
|
CSparse::SparseSet(s,i1,j1,temp);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseAdd(s,i1,j1,temp);
|
|
CSparse::SparseAdd(s,i1,j1,0);
|
|
}
|
|
if(a.Get(i1,j1)!=CSparse::SparseGet(s,i1,j1))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Nulling all elements
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
if(CMath::RandomInteger(2)==0)
|
|
CSparse::SparseSet(s,i1,j1,0);
|
|
else
|
|
CSparse::SparseAdd(s,i1,j1,-(1*CSparse::SparseGet(s,i1,j1)));
|
|
//--- Again initialization of the matrix and check new values
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
temp=2*CMath::RandomInteger(mfigure)-mfigure;
|
|
a.Set(i1,j1,temp);
|
|
if(CMath::RandomInteger(2)==0)
|
|
CSparse::SparseSet(s,i1,j1,temp);
|
|
else
|
|
CSparse::SparseAdd(s,i1,j1,temp);
|
|
if((double)(a.Get(i1,j1))!=(double)(CSparse::SparseGet(s,i1,j1)))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Checking for Matrix with CRS type
|
|
CSparse::SparseConvertToCRS(s);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
if((double)(a.Get(i1,j1))!=(double)(CSparse::SparseGet(s,i1,j1)))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing multyplication matrix with vector |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::LinearFunctionsTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
double lb=0;
|
|
double rb=0;
|
|
CMatrixDouble a;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble ty;
|
|
CRowDouble tyt;
|
|
CRowDouble y;
|
|
CRowDouble yt;
|
|
CRowDouble y0;
|
|
CRowDouble yt0;
|
|
double eps=0;
|
|
//--- Accuracy
|
|
eps=1000*CMath::m_machineepsilon;
|
|
//--- Size of the matrix (m*n)
|
|
n=10;
|
|
m=10;
|
|
//--- Left and right borders, limiting matrix values
|
|
lb=-10;
|
|
rb=10;
|
|
//--- Test linear algebra functions for:
|
|
//--- a) sparse matrix converted to CRS from Hash-Table
|
|
//--- b) sparse matrix initially created as CRS
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<n; j++)
|
|
{
|
|
//--- Prepare test problem
|
|
CreateRandom(i,j,-1,-1,-1,-1,a,s);
|
|
//--- Initialize temporaries
|
|
ty=vector<double>::Zeros(i);
|
|
tyt=vector<double>::Zeros(j);
|
|
x0=vector<double>::Zeros(j);
|
|
x1=vector<double>::Zeros(i);
|
|
for(i1=0; i1<j; i1++)
|
|
x0.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
|
|
for(i1=0; i1<i; i1++)
|
|
x1.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
|
|
//--- Consider two cases: square matrix, and non-square matrix
|
|
if(i!=j)
|
|
{
|
|
//--- Searching true result
|
|
ty=x0.MatMul(a.Transpose()+0)+0;
|
|
tyt=x1.MatMul(a)+0;
|
|
//--- Multiplication
|
|
CSparse::SparseMV(s,x0,y);
|
|
CSparse::SparseMTV(s,x1,yt);
|
|
//--- Check for MV-result
|
|
for(i1=0; i1<i; i1++)
|
|
if(MathAbs(y[i1]-ty[i1])>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check for MTV-result
|
|
for(i1=0; i1<j; i1++)
|
|
if(MathAbs(yt[i1]-tyt[i1])>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Searching true result
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
|
|
tyt.Add(j1,a.Get(i1,j1)*x0[i1]);
|
|
}
|
|
CSparse::SparseMV(s,x0,y);
|
|
CSparse::SparseMTV(s,x0,yt);
|
|
CSparse::SparseMV2(s,x0,y0,yt0);
|
|
//--- Check for MV2-result
|
|
for(i1=0; i1<i; i1++)
|
|
if(MathAbs(y0[i1]-ty[i1])>=eps || MathAbs(yt0[i1]-tyt[i1])>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check for MV- and MTV-result by help MV2
|
|
for(i1=0; i1<i; i1++)
|
|
if(MathAbs(y0[i1]-y[i1])>eps || MathAbs(yt0[i1]-yt[i1])>eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing multyplication for simmetric matrix with |
|
|
//| vector |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::LinearFunctionSSTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
int m=0;
|
|
int i=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
double lb=0;
|
|
double rb=0;
|
|
CMatrixDouble a;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble ty;
|
|
CRowDouble tyt;
|
|
CRowDouble y;
|
|
CRowDouble yt;
|
|
double eps=0;
|
|
//--- Accuracy
|
|
eps=1000*CMath::m_machineepsilon;
|
|
//--- Size of the matrix (m*m)
|
|
m=10;
|
|
//--- Left and right borders, limiting matrix values
|
|
lb=-10;
|
|
rb=10;
|
|
//--- Test linear algebra functions for:
|
|
//--- a) sparse matrix converted to CRS from Hash-Table
|
|
//--- b) sparse matrix initially created as CRS
|
|
for(i=1; i<m; i++)
|
|
{
|
|
//--- Prepare test problem
|
|
CreateRandom(i,i,-1,-1,-1,-1,a,s);
|
|
//--- Initialize temporaries
|
|
ty=vector<double>::Zeros(i);
|
|
tyt=vector<double>::Zeros(i);
|
|
x0=vector<double>::Zeros(i);
|
|
x1=vector<double>::Zeros(i);
|
|
for(i1=0; i1<i; i1++)
|
|
{
|
|
x0.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
|
|
x1.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
|
|
}
|
|
//--- Searching true result for upper and lower triangles
|
|
//--- of the matrix
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=i1; j1<i; j1++)
|
|
{
|
|
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
|
|
if(i1!=j1)
|
|
ty.Add(j1,a.Get(i1,j1)*x0[i1]);
|
|
}
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<=i1; j1++)
|
|
{
|
|
tyt.Add(i1,a.Get(i1,j1)*x1[j1]);
|
|
if(i1!=j1)
|
|
tyt.Add(j1,a.Get(i1,j1)*x1[i1]);
|
|
}
|
|
//--- Multiplication
|
|
CSparse::SparseSMV(s,true,x0,y);
|
|
CSparse::SparseSMV(s,false,x1,yt);
|
|
//--- Check for SMV-result
|
|
for(i1=0; i1<i; i1++)
|
|
if((double)(MathAbs(y[i1]-ty[i1]))>=eps || (double)(MathAbs(yt[i1]-tyt[i1]))>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing multyplication sparse matrix with nerrow |
|
|
//| dense matrix |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::LinearFunctionSMMTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
int n=0;
|
|
int m=0;
|
|
int kmax=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
int k1=0;
|
|
double lb=0;
|
|
double rb=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble x0;
|
|
CMatrixDouble x1;
|
|
CMatrixDouble ty;
|
|
CMatrixDouble tyt;
|
|
CMatrixDouble y;
|
|
CMatrixDouble yt;
|
|
CMatrixDouble y0;
|
|
CMatrixDouble yt0;
|
|
double eps=0;
|
|
//--- Accuracy
|
|
eps=1000*CMath::m_machineepsilon;
|
|
//--- Size of the matrix (m*n)
|
|
n=32;
|
|
m=32;
|
|
kmax=32;
|
|
//--- Left and right borders, limiting matrix values
|
|
lb=-10;
|
|
rb=10;
|
|
//--- Test linear algebra functions for:
|
|
//--- a) sparse matrix converted to CRS from Hash-Table
|
|
//--- b) sparse matrix initially created as CRS
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<n; j++)
|
|
{
|
|
//--- Prepare test problem
|
|
CreateRandom(i,j,-1,-1,-1,-1,a,s);
|
|
x0=matrix<double>::Zeros(j,kmax);
|
|
x1=matrix<double>::Zeros(i,kmax);
|
|
for(i1=0; i1<j; i1++)
|
|
for(j1=0; j1<=kmax-1; j1++)
|
|
x0.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<=kmax-1; j1++)
|
|
x1.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
|
|
ty=matrix<double>::Zeros(i,kmax);
|
|
tyt=matrix<double>::Zeros(j,kmax);
|
|
if(i!=j)
|
|
{
|
|
for(i1=0; i1<i; i1++)
|
|
for(k1=0; k1<=kmax-1; k1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
ty.Add(i1,k1,a.Get(i1,j1)*x0.Get(j1,k1));
|
|
tyt.Add(j1,k1,a.Get(i1,j1)*x1.Get(i1,k1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i1=0; i1<i; i1++)
|
|
for(k1=0; k1<=kmax-1; k1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
ty.Add(i1,k1,a.Get(i1,j1)*x0.Get(j1,k1));
|
|
tyt.Add(j1,k1,a.Get(i1,j1)*x0.Get(i1,k1));
|
|
}
|
|
}
|
|
for(k=1; k<=kmax; k++)
|
|
{
|
|
//--- Consider two cases: square matrix, and non-square matrix
|
|
if(i!=j)
|
|
{
|
|
//--- Multiplication
|
|
CSparse::SparseMM(s,x0,k,y);
|
|
CSparse::SparseMTM(s,x1,k,yt);
|
|
//--- Check for MM-result
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<k; j1++)
|
|
if(MathAbs(y.Get(i1,j1)-ty.Get(i1,j1))>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check for MTM-result
|
|
for(i1=0; i1<j; i1++)
|
|
for(j1=0; j1<k; j1++)
|
|
if(MathAbs(yt.Get(i1,j1)-tyt.Get(i1,j1))>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseMM(s,x0,k,y);
|
|
CSparse::SparseMTM(s,x0,k,yt);
|
|
CSparse::SparseMM2(s,x0,k,y0,yt0);
|
|
//--- Check for MM2-result
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<k; j1++)
|
|
if(MathAbs(y0.Get(i1,j1)-ty.Get(i1,j1))>=eps || MathAbs(yt0.Get(i1,j1)-tyt.Get(i1,j1))>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check for MV- and MTM-result by help MV2
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<k; j1++)
|
|
if(MathAbs(y0.Get(i1,j1)-y.Get(i1,j1))>eps || MathAbs(yt0.Get(i1,j1)-yt.Get(i1,j1))>eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing multyplication for simmetric sparse matrix |
|
|
//| with narrow dense matrix |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::LinearFunctionsSMMTest(void)
|
|
{
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
int m=0;
|
|
int k=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
int k1=0;
|
|
double lb=0;
|
|
double rb=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble x0;
|
|
CMatrixDouble x1;
|
|
CMatrixDouble ty;
|
|
CMatrixDouble tyt;
|
|
CMatrixDouble y;
|
|
CMatrixDouble yt;
|
|
double eps=0;
|
|
//--- Accuracy
|
|
eps=1000*CMath::m_machineepsilon;
|
|
//--- Size of the matrix (m*m)
|
|
m=32;
|
|
k=32;
|
|
//--- Left and right borders, limiting matrix values
|
|
lb=-10;
|
|
rb=10;
|
|
//--- Test linear algebra functions for:
|
|
//--- a) sparse matrix converted to CRS from Hash-Table
|
|
//--- b) sparse matrix initially created as CRS
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<k; j++)
|
|
{
|
|
//--- Prepare test problem
|
|
CreateRandom(i,i,-1,-1,-1,-1,a,s);
|
|
//--- Initialize temporaries
|
|
ty=matrix<double>::Zeros(i,j);
|
|
tyt=matrix<double>::Zeros(i,j);
|
|
x0=matrix<double>::Zeros(i,j);
|
|
x1=matrix<double>::Zeros(i,j);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
x0.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
|
|
x1.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
|
|
}
|
|
//--- Searching true result for upper and lower triangles
|
|
//--- of the matrix
|
|
for(k1=0; k1<j; k1++)
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=i1; j1<i; j1++)
|
|
{
|
|
ty.Add(i1,k1,a.Get(i1,j1)*x0.Get(j1,k1));
|
|
if(i1!=j1)
|
|
ty.Add(j1,k1,a.Get(i1,j1)*x0.Get(i1,k1));
|
|
}
|
|
for(k1=0; k1<j; k1++)
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<=i1; j1++)
|
|
{
|
|
tyt.Add(i1,k1,a.Get(i1,j1)*x1.Get(j1,k1));
|
|
if(i1!=j1)
|
|
tyt.Add(j1,k1,a.Get(i1,j1)*x1.Get(i1,k1));
|
|
}
|
|
//--- Multiplication
|
|
CSparse::SparseSMM(s,true,x0,j,y);
|
|
CSparse::SparseSMM(s,false,x1,j,yt);
|
|
//--- Check for SMM-result
|
|
for(k1=0; k1<j; k1++)
|
|
for(i1=0; i1<i; i1++)
|
|
if(MathAbs(y.Get(i1,k1)-ty.Get(i1,k1))>=eps || MathAbs(yt.Get(i1,k1)-tyt.Get(i1,k1))>=eps)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for basic test SparseCopy |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::BasicCopyFuncTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
CSparseMatrix ss;
|
|
CSparseMatrix sss;
|
|
int n=0;
|
|
int m=0;
|
|
CRowInt ner;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
CMatrixDouble a;
|
|
double a0=0;
|
|
double a1=0;
|
|
|
|
n=30;
|
|
m=30;
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<n; j++)
|
|
{
|
|
CSparse::SparseCreate(i,j,1,s);
|
|
a=matrix<double>::Zeros(i,j);
|
|
ner.Resize(i);
|
|
for(i1=0; i1<i; i1++)
|
|
{
|
|
if(i1<=j-3)
|
|
ner.Set(i1,2);
|
|
else
|
|
{
|
|
if(j-3<i1 && i1<=j-2)
|
|
ner.Set(i1,1);
|
|
else
|
|
ner.Set(i1,0);
|
|
}
|
|
}
|
|
CSparse::SparseCreateCRS(i,j,ner,sss);
|
|
//--- Checking for Matrix with hash table type
|
|
for(i1=0; i1<i; i1++)
|
|
{
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
if(j1>i1 && j1<=i1+2)
|
|
{
|
|
a.Set(i1,j1,i1+j1+1);
|
|
CSparse::SparseSet(s,i1,j1,a.Get(i1,j1));
|
|
CSparse::SparseAdd(s,i1,j1,0);
|
|
CSparse::SparseSet(sss,i1,j1,a.Get(i1,j1));
|
|
}
|
|
else
|
|
{
|
|
a.Set(i1,j1,0);
|
|
CSparse::SparseSet(s,i1,j1,a.Get(i1,j1));
|
|
CSparse::SparseAdd(s,i1,j1,0);
|
|
}
|
|
//--- Check for SparseCreate
|
|
CSparse::SparseCopy(s,ss);
|
|
a0=CSparse::SparseGet(s,i1,j1);
|
|
a1=CSparse::SparseGet(ss,i1,j1);
|
|
if(a0!=a1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("BasicCopyFuncTest::Report::SparseGet");
|
|
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
|
|
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
|
|
Print(" TEST FAILED");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- Check for SparseCreateCRS
|
|
for(i1=0; i1<i; i1++)
|
|
{
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
CSparse::SparseCopy(sss,ss);
|
|
a0=CSparse::SparseGet(sss,i1,j1);
|
|
a1=CSparse::SparseGet(ss,i1,j1);
|
|
if(a0!=a1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("BasicCopyFuncTest::Report::SparseGet\n");
|
|
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
|
|
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
|
|
Print(" TEST FAILED");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- Check for Matrix with CRS type
|
|
CSparse::SparseConvertToCRS(s);
|
|
CSparse::SparseCopy(s,ss);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
a0=CSparse::SparseGet(s,i1,j1);
|
|
a1=CSparse::SparseGet(ss,i1,j1);
|
|
if(a0!=a1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("BasicCopyFuncTest::Report::SparseGet");
|
|
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
|
|
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
|
|
Print(" TEST FAILED.\n");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!silent)
|
|
Print(" TEST IS PASSED");
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing SparseCopy |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::CopyFuncTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
CSparseMatrix ss;
|
|
int n=0;
|
|
int m=0;
|
|
int mtype=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int j1=0;
|
|
double lb=0;
|
|
double rb=0;
|
|
CMatrixDouble a;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble ty;
|
|
CRowDouble tyt;
|
|
CRowDouble y;
|
|
CRowDouble yt;
|
|
CRowDouble y0;
|
|
CRowDouble yt0;
|
|
CRowDouble cpy;
|
|
CRowDouble cpyt;
|
|
CRowDouble cpy0;
|
|
CRowDouble cpyt0;
|
|
double eps=0;
|
|
double a0=0;
|
|
double a1=0;
|
|
//--- Accuracy
|
|
eps=1000*CMath::m_machineepsilon;
|
|
//--- Size of the matrix (m*n)
|
|
n=30;
|
|
m=30;
|
|
//--- Left and right borders, limiting matrix values
|
|
lb=-10;
|
|
rb=10;
|
|
//--- Test linear algebra functions for:
|
|
//--- a) sparse matrix converted to CRS from Hash-Table
|
|
//--- b) sparse matrix initially created as CRS
|
|
for(i=1; i<m; i++)
|
|
{
|
|
for(j=1; j<n; j++)
|
|
{
|
|
for(mtype=0; mtype<=1; mtype++)
|
|
{
|
|
//--- Prepare test problem
|
|
CreateRandom(i,j,-1,mtype,-1,-1,a,s);
|
|
CSparse::SparseCopy(s,ss);
|
|
//--- Initialize temporaries
|
|
ty=vector<double>::Zeros(i);
|
|
tyt=vector<double>::Zeros(j);
|
|
x0=vector<double>::Zeros(j);
|
|
x1=vector<double>::Zeros(i);
|
|
for(i1=0; i1<j; i1++)
|
|
x0.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
|
|
for(i1=0; i1<i; i1++)
|
|
x1.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
|
|
//--- Consider two cases: square matrix, and non-square matrix
|
|
if(i!=j)
|
|
{
|
|
//--- Searching true result
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
|
|
tyt.Add(j1,a.Get(i1,j1)*x1[i1]);
|
|
}
|
|
//--- Multiplication
|
|
CSparse::SparseMV(s,x0,y);
|
|
CSparse::SparseMTV(s,x1,yt);
|
|
CSparse::SparseMV(ss,x0,cpy);
|
|
CSparse::SparseMTV(ss,x1,cpyt);
|
|
//--- Check for MV-result
|
|
for(i1=0; i1<i; i1++)
|
|
{
|
|
if(MathAbs(y[i1]-ty[i1])>=eps || MathAbs(cpy[i1]-ty[i1])>=eps || (cpy[i1]-y[i1])!=0.0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("CopyFuncTest::Report::RES_MV");
|
|
PrintFormat("Y[%d]=%.5f; tY[%d]=%.5f",i1,y[i1],i1,ty[i1]);
|
|
PrintFormat("cpY[%d]=%.5f;",i1,cpy[i1]);
|
|
Print(" TEST FAILED.\n");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Check for MTV-result
|
|
for(i1=0; i1<j; i1++)
|
|
if(((double)(MathAbs(yt[i1]-tyt[i1]))>=eps || (double)(MathAbs(cpyt[i1]-tyt[i1]))>=eps) || (double)(cpyt[i1]-yt[i1])!=0.0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("CopyFuncTest::Report::RES_MTV");
|
|
PrintFormat("Yt[%d]=%.5f; tYt[%d]=%.5f",i1,yt[i1],i1,tyt[i1]);
|
|
PrintFormat("cpYt[%d]=%.5f;",i1,cpyt[i1]);
|
|
Print(" TEST FAILED.");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CSparse::SparseCopy(s,ss);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
a0=CSparse::SparseGet(s,i1,j1);
|
|
a1=CSparse::SparseGet(ss,i1,j1);
|
|
if(a0!=a1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("CopyFuncTest::Report::SparseGet");
|
|
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
|
|
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
|
|
Print(" TEST FAILED.");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Searching true result
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
|
|
tyt.Add(j1,a.Get(i1,j1)*x0[i1]);
|
|
}
|
|
//--- Multiplication
|
|
CSparse::SparseMV(s,x0,y);
|
|
CSparse::SparseMTV(s,x0,yt);
|
|
CSparse::SparseMV2(s,x0,y0,yt0);
|
|
CSparse::SparseMV(ss,x0,cpy);
|
|
CSparse::SparseMTV(ss,x0,cpyt);
|
|
CSparse::SparseMV2(ss,x0,cpy0,cpyt0);
|
|
//--- Check for MV2-result
|
|
for(i1=0; i1<i; i1++)
|
|
if(MathAbs(y0[i1]-ty[i1])>=eps || MathAbs(yt0[i1]-tyt[i1])>=eps || MathAbs(cpy0[i1]-ty[i1])>=eps || MathAbs(cpyt0[i1]-tyt[i1])>=eps || (cpy0[i1]-y0[i1])!=0.0 || (cpyt0[i1]-yt0[i1])!=0.0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("CopyFuncTest::Report::RES_MV2");
|
|
PrintFormat("Y0[%d]=%.5f; tY[%d]=%.5f",i1,y0[i1],i1,ty[i1]);
|
|
PrintFormat("Yt0[%d]=%.5f; tYt[%d]=%.5f",i1,yt0[i1],i1,tyt[i1]);
|
|
PrintFormat("cpY0[%d]=%.5f;",i1,cpy0[i1]);
|
|
PrintFormat("cpYt0[%d]=%.5f;",i1,cpyt0[i1]);
|
|
Print(" TEST FAILED.");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check for MV- and MTV-result by help MV2
|
|
for(i1=0; i1<i; i1++)
|
|
if(MathAbs(y0[i1]-y[i1])>eps || MathAbs(yt0[i1]-yt[i1])>eps || MathAbs(cpy0[i1]-cpy[i1])>eps || MathAbs(cpyt0[i1]-cpyt[i1])>eps)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("CopyFuncTest::Report::RES_MV_MVT");
|
|
PrintFormat("Y0[%d]=%.5f; Y[%d]=%.5f",i1,y0[i1],i1,y[i1]);
|
|
PrintFormat("Yt0[%d]=%.5f; Yt[%d]=%.5f",i1,yt0[i1],i1,yt[i1]);
|
|
PrintFormat("cpY0[%d]=%.5f; cpY[%d]=%.5f",i1,cpy0[i1],i1,cpy[i1]);
|
|
PrintFormat("cpYt0[%d]=%.5f; cpYt[%d]=%.5f",i1,cpyt0[i1],i1,cpyt[i1]);
|
|
Print(" TEST FAILED.");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CSparse::SparseCopy(s,ss);
|
|
for(i1=0; i1<i; i1++)
|
|
for(j1=0; j1<j; j1++)
|
|
{
|
|
a0=CSparse::SparseGet(s,i1,j1);
|
|
a1=CSparse::SparseGet(ss,i1,j1);
|
|
if(a0!=a1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
Print("CopyFuncTest::Report::SparseGet");
|
|
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
|
|
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
|
|
Print(" TEST FAILED.");
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!silent)
|
|
Print(" TEST IS PASSED.\n");
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function initializes sparse matrix generator, which is used |
|
|
//| to generate a set of matrices with sequentially increasing |
|
|
//| sparsity. |
|
|
//| PARAMETERS: |
|
|
//| M, N - matrix size. If M=0, then matrix is square N*N. |
|
|
//| N and M must be small enough to store N*M dense |
|
|
//| matrix. |
|
|
//| MatKind - matrix properties: |
|
|
//| * 0 - general sparse (no structure) |
|
|
//| * 1 - general sparse, but diagonal is |
|
|
//| always present and non-zero |
|
|
//| * 2 - diagonally dominant, SPD |
|
|
//| Triangle - triangle being returned: |
|
|
//| * +1 - upper triangle |
|
|
//| * -1 - lower triangle |
|
|
//| * 0 - full matrix is returned |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| G - generator |
|
|
//| A - matrix A in dense format |
|
|
//| S - matrix A in sparse format (hash-table storage) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSparseUnit::InitGenerator(int m,int n,int matkind,int triangle,
|
|
CSparseGenerator &g)
|
|
{
|
|
g.m_n=n;
|
|
g.m_m=m;
|
|
g.m_matkind=matkind;
|
|
g.m_triangle=triangle;
|
|
CHighQualityRand::HQRndRandomize(g.m_rs);
|
|
g.m_rcs.ia.Resize(5+1);
|
|
g.m_rcs.ra=vector<double>::Zeros(1+1);
|
|
g.m_rcs.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::GenerateNext(CSparseGenerator &g,
|
|
CMatrixDouble &da,
|
|
CSparseMatrix &sa)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int m=0;
|
|
int nz=0;
|
|
int nzd=0;
|
|
double pnz=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int label=-1;
|
|
|
|
da.Resize(0,0);
|
|
//--- Reverse communication preparations
|
|
//--- I know it looks ugly, but it works the same way
|
|
//--- anywhere from C++ to Python.
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(g.m_rcs.stage>=0)
|
|
{
|
|
n=g.m_rcs.ia[0];
|
|
m=g.m_rcs.ia[1];
|
|
nz=g.m_rcs.ia[2];
|
|
nzd=g.m_rcs.ia[3];
|
|
i=g.m_rcs.ia[4];
|
|
j=g.m_rcs.ia[5];
|
|
pnz=g.m_rcs.ra[0];
|
|
v=g.m_rcs.ra[1];
|
|
}
|
|
else
|
|
{
|
|
n=359;
|
|
m=-58;
|
|
nz=-919;
|
|
nzd=-909;
|
|
i=81;
|
|
j=255;
|
|
pnz=74;
|
|
v=-788;
|
|
}
|
|
switch(g.m_rcs.stage)
|
|
{
|
|
case 0:
|
|
label=0;
|
|
break;
|
|
case 1:
|
|
label=1;
|
|
break;
|
|
default:
|
|
//--- Routine body
|
|
n=g.m_n;
|
|
if(g.m_m==0)
|
|
m=n;
|
|
else
|
|
m=g.m_m;
|
|
if(!CAp::Assert(m>0 && n>0,__FUNCTION__+": incorrect N/M"))
|
|
return(false);
|
|
//--- Generate general sparse matrix
|
|
if(g.m_matkind!=0)
|
|
{
|
|
label=2;
|
|
break;
|
|
}
|
|
nz=n*m;
|
|
label=4;
|
|
break;
|
|
}
|
|
|
|
while(label>=0)
|
|
switch(label)
|
|
{
|
|
case 4:
|
|
//--- Generate dense N*N matrix where probability of element
|
|
//--- being non-zero is PNZ.
|
|
pnz=(double)nz/(double)(n*m);
|
|
g.m_bufa=matrix<double>::Zeros(m,n);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if((double)(CHighQualityRand::HQRndUniformR(g.m_rs))<=pnz)
|
|
g.m_bufa.Set(i,j,CHighQualityRand::HQRndUniformR(g.m_rs)-0.5);
|
|
//--- Output matrix and RComm
|
|
da=matrix<double>::Zeros(m,n);
|
|
CSparse::SparseCreate(m,n,(int)MathRound(pnz*m*n),sa);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<=i && g.m_triangle<=0) || (j>=i && g.m_triangle>=0))
|
|
{
|
|
da.Set(i,j,g.m_bufa.Get(i,j));
|
|
CSparse::SparseSet(sa,i,j,g.m_bufa.Get(i,j));
|
|
}
|
|
g.m_rcs.stage=0;
|
|
label=-1;
|
|
break;
|
|
case 0:
|
|
//--- Increase problem sparcity and try one more time.
|
|
//--- Stop after testing NZ=0.
|
|
if(nz==0)
|
|
{
|
|
label=5;
|
|
break;
|
|
}
|
|
nz=nz/2;
|
|
label=4;
|
|
break;
|
|
case 5:
|
|
result=false;
|
|
return(result);
|
|
case 2:
|
|
//--- Generate general sparse matrix with non-zero diagonal
|
|
if(g.m_matkind!=1)
|
|
{
|
|
label=6;
|
|
break;
|
|
}
|
|
if(!CAp::Assert(n==m,__FUNCTION__+": non-square matrix for MatKind=1"))
|
|
return(false);
|
|
nz=n*n-n;
|
|
case 8:
|
|
//--- Generate dense N*N matrix where probability of non-diagonal element
|
|
//--- being non-zero is PNZ.
|
|
if(n>1)
|
|
pnz=(double)nz/(double)(n*n-n);
|
|
else
|
|
pnz=1;
|
|
g.m_bufa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i==j)
|
|
{
|
|
do
|
|
{
|
|
g.m_bufa.Set(i,i,CHighQualityRand::HQRndUniformR(g.m_rs)-0.5);
|
|
}
|
|
while(g.m_bufa.Get(i,i)==0.0);
|
|
g.m_bufa.Add(i,i,1.5*MathSign(g.m_bufa.Get(i,i)));
|
|
continue;
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(g.m_rs)<=pnz)
|
|
g.m_bufa.Set(i,j,CHighQualityRand::HQRndUniformR(g.m_rs)-0.5);
|
|
}
|
|
//--- Output matrix and RComm
|
|
da=matrix<double>::Zeros(n,n);
|
|
CSparse::SparseCreate(n,n,(int)MathRound(pnz*(n*n-n)+n),sa);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
if((j<=i && g.m_triangle<=0) || (j>=i && g.m_triangle>=0))
|
|
{
|
|
da.Set(i,j,g.m_bufa.Get(i,j));
|
|
CSparse::SparseSet(sa,i,j,g.m_bufa.Get(i,j));
|
|
}
|
|
g.m_rcs.stage=1;
|
|
label=-1;
|
|
break;
|
|
case 1:
|
|
//--- Increase problem sparcity and try one more time.
|
|
//--- Stop after testing NZ=0.
|
|
if(nz==0)
|
|
{
|
|
label=9;
|
|
break;
|
|
}
|
|
nz=nz/2;
|
|
label=8;
|
|
break;
|
|
case 9:
|
|
result=false;
|
|
return(result);
|
|
case 6:
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- Saving state
|
|
result=true;
|
|
g.m_rcs.ia.Set(0,n);
|
|
g.m_rcs.ia.Set(1,m);
|
|
g.m_rcs.ia.Set(2,nz);
|
|
g.m_rcs.ia.Set(3,nzd);
|
|
g.m_rcs.ia.Set(4,i);
|
|
g.m_rcs.ia.Set(5,j);
|
|
g.m_rcs.ra.Set(0,pnz);
|
|
g.m_rcs.ra.Set(1,v);
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function creates random sparse matrix with some prescribed |
|
|
//| pattern. |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| PKind - sparsity pattern: |
|
|
//| *-1 = pattern is chosen at random as well as P0/P1 |
|
|
//| * 0 = matrix with up to P0 non-zero elements at |
|
|
//| random locations (however, actual number of |
|
|
//| non-zero elements can be less than P0, and in|
|
|
//| fact can be zero) |
|
|
//| * 1 = band matrix with P0 non-zero elements below |
|
|
//| diagonal and P1 non-zero element above |
|
|
//| diagonal |
|
|
//| * 2 = matrix with random number of contiguous |
|
|
//| non-zero elements in the each row |
|
|
//| CKind - creation type: |
|
|
//| *-1 = CKind is chosen at random |
|
|
//| * 0 = matrix is created in Hash-Table format and |
|
|
//| converted to CRS representation |
|
|
//| * 1 = matrix is created in CRS format |
|
|
//| * 2 = matrix is created in Hash-Table format and |
|
|
//| converted to SKS representation |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| DA - dense representation of A, array[M,N] |
|
|
//| SA - sparse representation of A, in CRS format |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSparseUnit::CreateRandom(int m,int n,int pkind,int ckind,
|
|
int p0,int p1,CMatrixDouble &da,
|
|
CSparseMatrix &sa)
|
|
{
|
|
//--- create variables
|
|
int maxpkind=0;
|
|
int maxckind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
CRowInt c0;
|
|
CRowInt c1;
|
|
CRowInt rowsizes;
|
|
|
|
maxpkind=2;
|
|
maxckind=2;
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": incorrect parameters"))
|
|
return;
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": incorrect parameters"))
|
|
return;
|
|
if(!CAp::Assert(pkind>=-1 && pkind<=maxpkind,__FUNCTION__+": incorrect parameters"))
|
|
return;
|
|
if(!CAp::Assert(ckind>=-1 && ckind<=maxckind,__FUNCTION__+": incorrect parameters"))
|
|
return;
|
|
if(!CAp::Assert(!(ckind==2 && m!=n),__FUNCTION__+": incorrect parameters"))
|
|
return;
|
|
|
|
if(pkind==-1)
|
|
{
|
|
pkind=CMath::RandomInteger(maxpkind+1);
|
|
if(pkind==0)
|
|
p0=CMath::RandomInteger(m*n);
|
|
if(pkind==1)
|
|
{
|
|
p0=CMath::RandomInteger(MathMin(m,n));
|
|
p1=CMath::RandomInteger(MathMin(m,n));
|
|
}
|
|
}
|
|
if(ckind==-1)
|
|
{
|
|
do
|
|
{
|
|
ckind=CMath::RandomInteger(maxckind+1);
|
|
}
|
|
while(ckind==2 && m!=n);
|
|
}
|
|
if(pkind==0)
|
|
{
|
|
//--- Matrix with elements at random locations
|
|
da=matrix<double>::Zeros(m,n);
|
|
if(ckind==0 || ckind==2)
|
|
{
|
|
//--- Create matrix in Hash format, convert to CRS or SKS
|
|
CSparse::SparseCreate(m,n,1,sa);
|
|
for(k=0; k<=p0-1; k++)
|
|
{
|
|
i=CMath::RandomInteger(m);
|
|
j=CMath::RandomInteger(n);
|
|
v=(double)(CMath::RandomInteger(17)-8)/8.0;
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
da.Set(i,j,v);
|
|
CSparse::SparseSet(sa,i,j,v);
|
|
}
|
|
else
|
|
{
|
|
da.Add(i,j,v);
|
|
CSparse::SparseAdd(sa,i,j,v);
|
|
}
|
|
}
|
|
if(ckind!=2)
|
|
CSparse::SparseConvertToCRS(sa);
|
|
else
|
|
CSparse::SparseConvertToSKS(sa);
|
|
return;
|
|
}
|
|
if(ckind==1)
|
|
{
|
|
//--- Create matrix in CRS format
|
|
for(k=0; k<=p0-1; k++)
|
|
{
|
|
i=CMath::RandomInteger(m);
|
|
j=CMath::RandomInteger(n);
|
|
v=(double)(CMath::RandomInteger(17)-8)/8.0;
|
|
da.Set(i,j,v);
|
|
}
|
|
rowsizes.Resize(m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
rowsizes.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
if(da.Get(i,j)!=0.0)
|
|
rowsizes.Add(i,1);
|
|
}
|
|
CSparse::SparseCreateCRS(m,n,rowsizes,sa);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(da.Get(i,j)!=0.0)
|
|
CSparse::SparseSet(sa,i,j,da.Get(i,j));
|
|
return;
|
|
}
|
|
CAp::Assert(false,__FUNCTION__+": internal error");
|
|
return;
|
|
}
|
|
if(pkind==1)
|
|
{
|
|
//--- Band matrix
|
|
da=matrix<double>::Zeros(m,n);
|
|
rowsizes.Resize(m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=MathMax(i-p0,0); j<=MathMin(i+p1,n-1); j++)
|
|
{
|
|
do
|
|
{
|
|
da.Set(i,j,(double)(CMath::RandomInteger(17)-8)/8.0);
|
|
}
|
|
while(da.Get(i,j)==0.0);
|
|
}
|
|
rowsizes.Set(i,MathMax(MathMin(i+p1,n-1)-MathMax(i-p0,0)+1,0));
|
|
}
|
|
if(ckind==0)
|
|
CSparse::SparseCreate(m,n,1,sa);
|
|
if(ckind==1)
|
|
CSparse::SparseCreateCRS(m,n,rowsizes,sa);
|
|
if(ckind==2)
|
|
CSparse::SparseCreate(m,n,1,sa);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(da.Get(i,j)!=0.0)
|
|
CSparse::SparseSet(sa,i,j,da.Get(i,j));
|
|
if(ckind!=2)
|
|
CSparse::SparseConvertToCRS(sa);
|
|
else
|
|
CSparse::SparseConvertToSKS(sa);
|
|
return;
|
|
}
|
|
if(pkind==2)
|
|
{
|
|
//--- Matrix with one contiguous sequence of non-zero elements per row
|
|
da=matrix<double>::Zeros(m,n);
|
|
rowsizes.Resize(m);
|
|
c0.Resize(m);
|
|
c1.Resize(m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
c0.Set(i,CMath::RandomInteger(n));
|
|
c1.Set(i,c0[i]+CMath::RandomInteger(n-c0[i]+1));
|
|
rowsizes.Set(i,c1[i]-c0[i]);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
for(j=c0[i]; j<=c1[i]-1; j++)
|
|
{
|
|
do
|
|
{
|
|
da.Set(i,j,(double)(CMath::RandomInteger(17)-8)/8.0);
|
|
}
|
|
while(da.Get(i,j)==0.0);
|
|
}
|
|
if(ckind==0)
|
|
CSparse::SparseCreate(m,n,1,sa);
|
|
if(ckind==1)
|
|
CSparse::SparseCreateCRS(m,n,rowsizes,sa);
|
|
if(ckind==2)
|
|
CSparse::SparseCreate(m,n,1,sa);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(da.Get(i,j)!=0.0)
|
|
CSparse::SparseSet(sa,i,j,da.Get(i,j));
|
|
if(ckind!=2)
|
|
CSparse::SparseConvertToCRS(sa);
|
|
else
|
|
CSparse::SparseConvertToSKS(sa);
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function does test for SparseEnumerate function. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::EnumerateTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix spa;
|
|
CMatrixDouble a;
|
|
CMatrixInt ta;
|
|
int m=0;
|
|
int n=0;
|
|
double r=0;
|
|
double v=0;
|
|
int ne=0;
|
|
int t0=0;
|
|
int t1=0;
|
|
int counter=0;
|
|
int c=0;
|
|
int hashcrs=0;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
r=10.5;
|
|
for(m=1; m<=30; m++)
|
|
{
|
|
for(n=1; n<=30; n++)
|
|
{
|
|
ne=0;
|
|
//--- Create matrix with non-zero elements inside the region:
|
|
//--- 0<=I<S.M and 0<=J<S.N
|
|
a=matrix<double>::Zeros(m,n);
|
|
ta.Resize(m,n);
|
|
ta.Fill((int)false);
|
|
//---
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c=CMath::RandomInteger(2);
|
|
if(c!=0)
|
|
{
|
|
do
|
|
{
|
|
a.Set(i,j,r*(2*CMath::RandomReal()-1));
|
|
}
|
|
while(a.Get(i,j)==0.0);
|
|
//--- Number of non-zero elements
|
|
ne++;
|
|
}
|
|
}
|
|
for(hashcrs=0; hashcrs<=1; hashcrs++)
|
|
{
|
|
CSparse::SparseCreate(m,n,m*n,spa);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(spa,i,j,a.Get(i,j));
|
|
if(hashcrs==1)
|
|
CSparse::SparseConvertToCRS(spa);
|
|
t0=0;
|
|
t1=0;
|
|
counter=0;
|
|
while(CSparse::SparseEnumerate(spa,t0,t1,i,j,v))
|
|
{
|
|
ta.Set(i,j,(int)true);
|
|
counter++;
|
|
if(v!=a.Get(i,j))
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testsparseunit.ap:3112");
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Check that all non-zero elements was enumerated
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(ta.Get(i,j) && a.Get(i,j)==0.0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testsparseunit.ap:3124");
|
|
return(result);
|
|
}
|
|
if(ne!=counter)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testsparseunit.ap:3130");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function does test for SparseRewriteExisting function. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::RewriteExistingTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix spa;
|
|
double spaval=0;
|
|
CMatrixDouble a;
|
|
CMatrixInt ta;
|
|
int m=0;
|
|
int n=0;
|
|
int c=0;
|
|
int ne=0;
|
|
int nr=0;
|
|
double r=0;
|
|
double v=0;
|
|
int hashcrs=0;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
r=20.0;
|
|
for(m=1; m<=30; m++)
|
|
{
|
|
for(n=1; n<=30; n++)
|
|
{
|
|
ta.Resize(m,n);
|
|
for(hashcrs=0; hashcrs<=1; hashcrs++)
|
|
{
|
|
a=matrix<double>::Zeros(m,n);
|
|
ta.Fill((int)false);
|
|
v=r*(2*CMath::RandomReal()-1);
|
|
//--- Creating and filling of the matrix
|
|
ne=0;
|
|
CSparse::SparseCreate(m,n,m*n,spa);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c=CMath::RandomInteger(2);
|
|
if(c==1)
|
|
{
|
|
do
|
|
{
|
|
a.Set(i,j,r*(2*CMath::RandomReal()-1));
|
|
}
|
|
while(a.Get(i,j)==0.0);
|
|
CSparse::SparseSet(spa,i,j,a.Get(i,j));
|
|
ne++;
|
|
}
|
|
}
|
|
if(hashcrs==1)
|
|
CSparse::SparseConvertToCRS(spa);
|
|
//--- Rewrite some elements
|
|
nr=0;
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c=CMath::RandomInteger(2);
|
|
if(c==1)
|
|
{
|
|
ta.Set(i,j,(int)CSparse::SparseRewriteExisting(spa,i,j,v));
|
|
if(ta.Get(i,j))
|
|
{
|
|
a.Set(i,j,v);
|
|
nr++;
|
|
}
|
|
}
|
|
}
|
|
//--- Now we have to be sure, that all changes had made correctly
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(ta.Get(i,j))
|
|
{
|
|
spaval=CSparse::SparseGet(spa,i,j);
|
|
nr--;
|
|
if(spaval!=v || spaval!=a.Get(i,j))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(nr!=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Rewrite all elements
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ta.Set(i,j,(int)CSparse::SparseRewriteExisting(spa,i,j,v));
|
|
if(ta.Get(i,j))
|
|
a.Set(i,j,v);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(ta.Get(i,j))
|
|
ne--;
|
|
if(ne!=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
spaval=CSparse::SparseGet(spa,i,j);
|
|
if(ta.Get(i,j))
|
|
{
|
|
if(spaval!=v || spaval!=a.Get(i,j))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(spaval!=0.0 || spaval!=a.Get(i,j))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for SparseGetRow/GetCompressedRow function. It creates |
|
|
//| random dense and sparse matrices; then get every row from sparse |
|
|
//| matrix and compares it with every row in dense matrix. |
|
|
//| On failure sets error flag, on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSparseUnit::TestGetRow(bool &err)
|
|
{
|
|
//--- create variables
|
|
CSparseMatrix s;
|
|
CMatrixDouble a;
|
|
int m=0;
|
|
int n=0;
|
|
int msize=0;
|
|
int nsize=0;
|
|
int nz=0;
|
|
CRowDouble vals;
|
|
CRowDouble mrow;
|
|
CRowInt colidx;
|
|
CRowInt wasreturned;
|
|
int mtype=0;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
msize=15;
|
|
nsize=15;
|
|
for(mtype=1; mtype<=2; mtype++)
|
|
{
|
|
for(m=1; m<=msize; m++)
|
|
{
|
|
for(n=1; n<=nsize; n++)
|
|
{
|
|
//--- Skip nonrectangular SKS matrices - not supported
|
|
if(mtype==2 && m!=n)
|
|
continue;
|
|
//--- Create "reference" and sparse matrices
|
|
a=matrix<double>::Zeros(m,n);
|
|
CSparse::SparseCreate(m,n,1,s);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(CMath::RandomInteger(5)==3)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
CSparse::SparseSet(s,i,j,a.Get(i,j));
|
|
}
|
|
//--- Choose matrix type to test
|
|
if(mtype==1)
|
|
CSparse::SparseConvertToCRS(s);
|
|
else
|
|
CSparse::SparseConvertToSKS(s);
|
|
//--- Test SparseGetRow()
|
|
for(i=0; i<m; i++)
|
|
{
|
|
CSparse::SparseGetRow(s,i,mrow);
|
|
for(j=0; j<n; j++)
|
|
if(mrow[j]!=a.Get(i,j) || mrow[j]!=CSparse::SparseGet(s,i,j))
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testsparseunit.ap:3355");
|
|
return;
|
|
}
|
|
}
|
|
//--- Test SparseGetCompressedRow()
|
|
wasreturned.Resize(n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
CSparse::SparseGetCompressedRow(s,i,colidx,vals,nz);
|
|
if(nz<0 || nz>n)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testsparseunit.ap:3369");
|
|
return;
|
|
}
|
|
wasreturned.Fill((int)false);
|
|
for(j=0; j<=nz-1; j++)
|
|
{
|
|
if(colidx[j]<0 || colidx[j]>n)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testsparseunit.ap:3378");
|
|
return;
|
|
}
|
|
CAp::SetErrorFlag(err,j>0 && colidx[j]<=colidx[j-1],"testsparseunit.ap:3381");
|
|
CAp::SetErrorFlag(err,vals[j]!=a.Get(i,colidx[j]) || vals[j]!=CSparse::SparseGet(s,i,colidx[j]),"testsparseunit.ap:3382");
|
|
wasreturned.Set(colidx[j],true);
|
|
}
|
|
for(j=0; j<n; j++)
|
|
CAp::SetErrorFlag(err,a.Get(i,j)!=0.0 && !wasreturned[j],"testsparseunit.ap:3386");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for SparseConvert functions(isn't tested ConvertToCRS |
|
|
//| function). The function create random dense and sparse matrices |
|
|
//| in CRS format. Then convert sparse matrix to some format by |
|
|
//| CONVERT_TO/COPY_TO functions, then it does some modification in |
|
|
//| matrices and compares that marices are identical. |
|
|
//| NOTE: |
|
|
//| Result of the function assigned to variable CopyErrors in unit |
|
|
//| test. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestConvertSM(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
CSparseMatrix cs;
|
|
CMatrixDouble a;
|
|
int m=0;
|
|
int n=0;
|
|
int msize=0;
|
|
int nsize=0;
|
|
CRowInt ner;
|
|
double tmp=0;
|
|
int i=0;
|
|
int j=0;
|
|
int vartf=0;
|
|
|
|
msize=15;
|
|
nsize=15;
|
|
for(m=1; m<=msize; m++)
|
|
{
|
|
for(n=1; n<=nsize; n++)
|
|
{
|
|
for(vartf=0; vartf<=2; vartf++)
|
|
{
|
|
a=matrix<double>::Zeros(m,n);
|
|
ner.Resize(m);
|
|
ner.Fill(0);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(CMath::RandomInteger(5)==3)
|
|
{
|
|
ner.Add(i,1);
|
|
do
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
while(a.Get(i,j)==0.0);
|
|
}
|
|
//--- Create sparse matrix
|
|
CSparse::SparseCreateCRS(m,n,ner,s);
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(a.Get(i,j)!=0.0)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
CSparse::SparseSet(s,i,j,a.Get(i,j));
|
|
}
|
|
//--- Set matrix type(we have to be sure that all formats
|
|
//--- converted correctly)
|
|
i=CMath::RandomInteger(2);
|
|
if(i==0)
|
|
CSparse::SparseConvertToHash(s);
|
|
if(i==1)
|
|
CSparse::SparseConvertToCRS(s);
|
|
//--- Start test
|
|
if(vartf==0)
|
|
{
|
|
CSparse::SparseConvertToHash(s);
|
|
CSparse::SparseCopy(s,cs);
|
|
}
|
|
if(vartf==1)
|
|
CSparse::SparseCopyToHash(s,cs);
|
|
if(vartf==2)
|
|
CSparse::SparseCopyToCRS(s,cs);
|
|
//--- Change some elements in row
|
|
if(vartf!=2)
|
|
for(i=0; i<m; i++)
|
|
{
|
|
tmp=2*CMath::RandomReal()-1;
|
|
j=CMath::RandomInteger(n);
|
|
a.Set(i,j,tmp);
|
|
CSparse::SparseSet(cs,i,j,tmp);
|
|
tmp=2*CMath::RandomReal()-1;
|
|
j=CMath::RandomInteger(n);
|
|
a.Add(i,j,tmp);
|
|
CSparse::SparseAdd(cs,i,j,tmp);
|
|
}
|
|
//--- Check that A is identical to S
|
|
for(i=0; i<m; i++)
|
|
for(j=0; j<n; j++)
|
|
if(a.Get(i,j)!=(double)(CSparse::SparseGet(cs,i,j)))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for check/get type functions. The function create sparse |
|
|
//| matrix, converts it to desired type then check this type. |
|
|
//| NOTE: |
|
|
//| Result of the function assigned to variable BasicErrors in unit|
|
|
//| test. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSparseUnit::TestGCMatrixType(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CSparseMatrix s;
|
|
CSparseMatrix cs;
|
|
int m=0;
|
|
int n=0;
|
|
int msize=0;
|
|
int nsize=0;
|
|
|
|
msize=5;
|
|
nsize=5;
|
|
for(m=1; m<=msize; m++)
|
|
{
|
|
for(n=1; n<=nsize; n++)
|
|
{
|
|
CSparse::SparseCreate(m,n,1,s);
|
|
CSparse::SparseConvertToCRS(s);
|
|
if((CSparse::SparseIsHash(s) || !CSparse::SparseIsCRS(s)) || CSparse::SparseGetMatrixType(s)!=1)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CSparse::SparseConvertToHash(s);
|
|
if((!CSparse::SparseIsHash(s) || CSparse::SparseIsCRS(s)) || CSparse::SparseGetMatrixType(s)!=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CSparse::SparseCopyToCRS(s,cs);
|
|
if((CSparse::SparseIsHash(cs) || !CSparse::SparseIsCRS(cs)) || CSparse::SparseGetMatrixType(cs)!=1)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CSparse::SparseCopyToHash(cs,s);
|
|
if((!CSparse::SparseIsHash(s) || CSparse::SparseIsCRS(s)) || CSparse::SparseGetMatrixType(s)!=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CAblasFPlayground
|
|
{
|
|
public:
|
|
double m_v0;
|
|
bool m_bx0[];
|
|
bool m_bx1[];
|
|
CRowInt m_ix0;
|
|
CRowInt m_ix1;
|
|
CRowDouble m_x0;
|
|
CRowDouble m_x1;
|
|
CRowDouble m_x2;
|
|
CRowDouble m_x3;
|
|
CMatrixDouble m_a0;
|
|
CMatrixDouble m_a1;
|
|
|
|
CAblasFPlayground() {}
|
|
~CAblasFPlayground() {}
|
|
|
|
void Copy(CAblasFPlayground &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CAblasFPlayground::Copy(CAblasFPlayground &obj)
|
|
{
|
|
m_v0=obj.m_v0;
|
|
ArrayCopy(m_bx0,obj.m_bx0);
|
|
ArrayCopy(m_bx1,obj.m_bx1);
|
|
m_ix0=obj.m_ix0;
|
|
m_ix1=obj.m_ix1;
|
|
m_x0=obj.m_x0;
|
|
m_x1=obj.m_x1;
|
|
m_x2=obj.m_x2;
|
|
m_x3=obj.m_x3;
|
|
m_a0=obj.m_a0;
|
|
m_a1=obj.m_a1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAblasFUnit
|
|
{
|
|
public:
|
|
static bool TestAblasF(bool silent);
|
|
static double RefRDotV(int n,CRowDouble &x,CRowDouble &y);
|
|
static double RefRDotVR(int n,CRowDouble &x,CMatrixDouble &a,int i);
|
|
static double RefRDotRR(int n,CMatrixDouble &a,int ia,CMatrixDouble &b,int ib);
|
|
static double RefRDotV2(int n,CRowDouble &x);
|
|
static void RefRAddV(int n,double alpha,CRowDouble &y,CRowDouble &x);
|
|
static void RefRAddVX(int n,double alpha,CRowDouble &y,int offsy,CRowDouble &x,int offsx);
|
|
static void RefRAddVC(int n,double alpha,CRowDouble &y,CMatrixDouble &x,int colidx);
|
|
static void RefRAddVR(int n,double alpha,CRowDouble &y,CMatrixDouble &x,int rowidx);
|
|
static void RefRAddRV(int n,double alpha,CMatrixDouble &y,int ridx,CRowDouble &x);
|
|
static void RefRAddRR(int n,double alpha,CMatrixDouble &y,int ridxsrc,CMatrixDouble &x,int ridxdst);
|
|
static void RefRSetV(int n,double v,CRowDouble &x);
|
|
static void RefRSetVX(int n,double v,CRowDouble &x,int offsx);
|
|
static void RefISetV(int n,int v,CRowInt &x);
|
|
static void RefBSetV(int n,bool v,bool &x[]);
|
|
static void RefRSetM(int m,int n,double v,CMatrixDouble &a);
|
|
static void RefRSetR(int n,double v,CMatrixDouble &a,int i);
|
|
static void RefRSetC(int n,double v,CMatrixDouble &a,int j);
|
|
static void RefRCopyV(int n,CRowDouble &x,CRowDouble &y);
|
|
static void RefBCopyV(int n,bool &x[],bool &y[]);
|
|
static void RefRCopyVX(int n,CRowDouble &x,int offsx,CRowDouble &y,int offsy);
|
|
static void RefICopyV(int n,CRowInt &x,CRowInt &y);
|
|
static void RefICopyVX(int n,CRowInt &x,int offsx,CRowInt &y,int offsy);
|
|
static void RefRCopyVR(int n,CRowDouble &x,CMatrixDouble &a,int i);
|
|
static void RefRCopyRV(int n,CMatrixDouble &a,int i,CRowDouble &x);
|
|
static void RefRCopyRR(int n,CMatrixDouble &a,int i,CMatrixDouble &b,int k);
|
|
static void RefRCopyVC(int n,CRowDouble &x,CMatrixDouble &a,int j);
|
|
static void RefRCopyCV(int n,CMatrixDouble &a,int j,CRowDouble &x);
|
|
static void RefRCopyMulV(int n,double v,CRowDouble &x,CRowDouble &y);
|
|
static void RefRCopyMulVR(int n,double v,CRowDouble &x,CMatrixDouble &y,int ridx);
|
|
static void RefRCopyMulVC(int n,double v,CRowDouble &x,CMatrixDouble &y,int cidx);
|
|
static void RefRMulV(int n,double v,CRowDouble &x);
|
|
static void RefRMulR(int n,double v,CMatrixDouble &x,int rowidx);
|
|
static void RefRSqrtV(int n,CRowDouble &x);
|
|
static void RefRSqrtR(int n,CMatrixDouble &x,int rowidx);
|
|
static void RefRMulVX(int n,double v,CRowDouble &x,int offsx);
|
|
static void RefRMergeMulV(int n,CRowDouble &y,CRowDouble &x);
|
|
static void RefRMergeMulVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
|
|
static void RefRMergeMulRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
|
|
static void RefRMergeDivV(int n,CRowDouble &y,CRowDouble &x);
|
|
static void RefRMergeDivVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
|
|
static void RefRMergeDivRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
|
|
static void RefRMergeMaxV(int n,CRowDouble &y,CRowDouble &x);
|
|
static void RefRMergeMaxVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
|
|
static void RefRMergeMaxRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
|
|
static void RefRMergeMinV(int n,CRowDouble &y,CRowDouble &x);
|
|
static void RefRMergeMinVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
|
|
static void RefRMergeMinRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
|
|
static double RefRMaxV(int n,CRowDouble &x);
|
|
static double RefRMaxAbsV(int n,CRowDouble &x);
|
|
static double RefRMaxR(int n,CMatrixDouble &x,int rowidx);
|
|
static double RefRMaxAbsR(int n,CMatrixDouble &x,int rowidx);
|
|
static void RefRGemVX(int m,int n,double alpha,CMatrixDouble &a,int ia,int ja,int opa,CRowDouble &x,int ix,double beta,CRowDouble &y,int iy);
|
|
static void RefTrsVX(int n,CMatrixDouble &a,int ia,int ja,bool isupper,bool isunit,int optype,CRowDouble &x,int ix);
|
|
|
|
private:
|
|
static bool TestXDot(int maxn,double tol);
|
|
static bool TestXSet(int maxn,double tol);
|
|
static bool TestXAdd(int maxn,double tol);
|
|
static bool TestXMulAdd(int maxn,double tol);
|
|
static bool TestXMul(int maxn,double tol);
|
|
static bool TestXSqrt(int maxn,double tol);
|
|
static bool TestXMax(int maxn,double tol);
|
|
static bool TestXMerge(int maxn,double tol);
|
|
static bool TestXCopy(int maxn,double tol);
|
|
static bool TestXGemV(int maxn,double tol);
|
|
static bool TestXGer(int maxn,double tol);
|
|
static bool TestXTrsV(int maxn,double tol);
|
|
static void InitPlayground(int minlen,int iseed,CAblasFPlayground &s);
|
|
static double ComparePlaygrounds(CAblasFPlayground &s0,CAblasFPlayground &s1);
|
|
static int PseudoRandomInit1(CRowDouble &x,int iseed);
|
|
static int PseudoRandomInit1I(CRowInt &x,int iseed);
|
|
static int PseudoRandomInit1B(bool &x[],int iseed);
|
|
static int PseudoRandomInit2(CMatrixDouble &x,int iseed);
|
|
static double rmx3(double r0,double r1,double r2);
|
|
static double rcmp1(CRowDouble &x,CRowDouble &y);
|
|
static double icmp1(CRowInt &x,CRowInt &y);
|
|
static double bcmp1(bool &x[],bool &y[]);
|
|
static double rcmp2(CMatrixDouble &x,CMatrixDouble &y);
|
|
static void RefRMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x);
|
|
static void RefRNegMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x);
|
|
static void RefRCopyMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x,CRowDouble &r);
|
|
static void RefRCopyNegMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x,CRowDouble &r);
|
|
static void RefGerX(int m,int n,CMatrixDouble &a,int ia,int ja,double alpha,CRowDouble &u,int iu,CRowDouble &v,int iv);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestAblasF(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int maxn=100;
|
|
double tol=10000*CMath::m_machineepsilon;
|
|
bool waserrors;
|
|
bool xdoterrors;
|
|
bool xseterrors;
|
|
bool xadderrors;
|
|
bool xmulerrors;
|
|
bool xmaxerrors;
|
|
bool xsqrterrors;
|
|
bool xmergeerrors;
|
|
bool xcopyerrors;
|
|
bool xmuladderrors;
|
|
bool xgemverrors;
|
|
bool xgererrors;
|
|
bool xtrsverrors;
|
|
//--- check
|
|
xdoterrors=TestXDot(maxn,tol);
|
|
xseterrors=TestXSet(maxn,tol);
|
|
xadderrors=TestXAdd(maxn,tol);
|
|
xmulerrors=TestXMul(maxn,tol);
|
|
xsqrterrors=TestXSqrt(maxn,tol);
|
|
xmaxerrors=TestXMax(maxn,tol);
|
|
xmergeerrors=TestXMerge(maxn,tol);
|
|
xcopyerrors=TestXCopy(maxn,tol);
|
|
xmuladderrors=TestXMulAdd(maxn,tol);
|
|
xgemverrors=TestXGemV(maxn,tol);
|
|
xgererrors=TestXGer(maxn,tol);
|
|
xtrsverrors=TestXTrsV(maxn,tol);
|
|
waserrors=((((((((((xdoterrors||xseterrors)||xadderrors)||xmulerrors)||xsqrterrors)||xmaxerrors)||xmergeerrors)||xcopyerrors)||xmuladderrors)||xgemverrors)||xgererrors)||xtrsverrors;
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ABLASF");
|
|
PrintResult("xDotXX ",!xdoterrors);
|
|
PrintResult("xSetXX",!xseterrors);
|
|
PrintResult("xAddXX",!xadderrors);
|
|
PrintResult("xMulXX",!xmulerrors);
|
|
PrintResult("xSqrtXX",!xsqrterrors);
|
|
PrintResult("xMaxXX",!xmaxerrors);
|
|
PrintResult("xMergeXX",!xmergeerrors);
|
|
PrintResult("xMulAddXX",!xmuladderrors);
|
|
PrintResult("xCopyXX",!xcopyerrors);
|
|
PrintResult("xGEMVx",!xgemverrors);
|
|
PrintResult("xGERx",!xgererrors);
|
|
PrintResult("xTRSVx",!xtrsverrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRDotV(int n,CRowDouble &x,CRowDouble &y)
|
|
{
|
|
double result=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
result+=x[i]*y[i];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRDotVR(int n,CRowDouble &x,
|
|
CMatrixDouble &a,int i)
|
|
{
|
|
double result=0;
|
|
|
|
for(int j=0; j<n; j++)
|
|
result+=x[j]*a.Get(i,j);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRDotRR(int n,CMatrixDouble &a,int ia,
|
|
CMatrixDouble &b,int ib)
|
|
{
|
|
double result=0;
|
|
|
|
for(int j=0; j<n; j++)
|
|
result+=a.Get(ia,j)*b.Get(ib,j);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRDotV2(int n,CRowDouble &x)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double v=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=x[i];
|
|
result+=v*v;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRAddV(int n,double alpha,CRowDouble &y,
|
|
CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(i,alpha*y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRAddVX(int n,double alpha,
|
|
CRowDouble &y,int offsy,
|
|
CRowDouble &x,int offsx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(offsx+i,alpha*y[offsy+i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRAddVC(int n,double alpha,CRowDouble &y,
|
|
CMatrixDouble &x,int colidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(i,colidx,alpha*y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRAddVR(int n,double alpha,CRowDouble &y,
|
|
CMatrixDouble &x,int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(rowidx,i,alpha*y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRAddRV(int n,double alpha,CMatrixDouble &y,
|
|
int ridx,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(i,alpha*y.Get(ridx,i));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRAddRR(int n,double alpha,
|
|
CMatrixDouble &y,int ridxsrc,
|
|
CMatrixDouble &x,int ridxdst)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(ridxdst,i,alpha*y.Get(ridxsrc,i));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSetV(int n,double v,CRowDouble &x)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
x.Set(j,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSetVX(int n,double v,
|
|
CRowDouble &x,int offsx)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
x.Set(offsx+j,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefISetV(int n,int v,CRowInt &x)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
x.Set(j,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefBSetV(int n,bool v,bool &x[])
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
x[j]=v;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSetM(int m,int n,double v,CMatrixDouble &a)
|
|
{
|
|
for(int i=0; i<m; i++)
|
|
for(int j=0; j<n; j++)
|
|
a.Set(i,j,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSetR(int n,double v,
|
|
CMatrixDouble &a,int i)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
a.Set(i,j,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSetC(int n,double v,
|
|
CMatrixDouble &a,int j)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
a.Set(i,j,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyV(int n,CRowDouble &x,CRowDouble &y)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
y.Set(j,x[j]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefBCopyV(int n,bool &x[],bool &y[])
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
y[j]=x[j];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyVX(int n,CRowDouble &x,int offsx,
|
|
CRowDouble &y,int offsy)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
y.Set(offsy+j,x[offsx+j]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefICopyV(int n,CRowInt &x,CRowInt &y)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
y.Set(j,x[j]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefICopyVX(int n,CRowInt &x,int offsx,
|
|
CRowInt &y,int offsy)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
y.Set(offsy+j,x[offsx+j]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyVR(int n,CRowDouble &x,
|
|
CMatrixDouble &a,int i)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
a.Set(i,j,x[j]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyRV(int n,CMatrixDouble &a,int i,
|
|
CRowDouble &x)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
x.Set(j,a.Get(i,j));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyRR(int n,CMatrixDouble &a,int i,
|
|
CMatrixDouble &b,int k)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
b.Set(k,j,a.Get(i,j));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyVC(int n,CRowDouble &x,
|
|
CMatrixDouble &a,int j)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
a.Set(i,j,x[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyCV(int n,CMatrixDouble &a,int j,
|
|
CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(i,a.Get(i,j));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyMulV(int n,double v,CRowDouble &x,
|
|
CRowDouble &y)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
y.Set(i,v*x[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyMulVR(int n,double v,CRowDouble &x,
|
|
CMatrixDouble &y,int ridx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
y.Set(ridx,i,v*x[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyMulVC(int n,double v,CRowDouble &x,
|
|
CMatrixDouble &y,int cidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
y.Set(i,cidx,v*x[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMulV(int n,double v,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(i,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMulR(int n,double v,CMatrixDouble &x,
|
|
int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(rowidx,i,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSqrtV(int n,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(i,MathSqrt(x[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRSqrtR(int n,CMatrixDouble &x,int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(rowidx,i,MathSqrt(x.Get(rowidx,i)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMulVX(int n,double v,
|
|
CRowDouble &x,int offsx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(offsx+i,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMulV(int n,CRowDouble &y,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(i,y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMulVR(int n,CRowDouble &y,
|
|
CMatrixDouble &x,int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(rowidx,i,y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMulRV(int n,CMatrixDouble &y,int rowidx,
|
|
CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(i,y.Get(rowidx,i));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeDivV(int n,CRowDouble &y,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(i,1.0/y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeDivVR(int n,CRowDouble &y,
|
|
CMatrixDouble &x,int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(rowidx,i,1.0/y[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeDivRV(int n,CMatrixDouble &y,int rowidx,
|
|
CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Mul(i,1.0/y.Get(rowidx,i));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMaxV(int n,CRowDouble &y,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(i,MathMax(x[i],y[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMaxVR(int n,CRowDouble &y,
|
|
CMatrixDouble &x,int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(rowidx,i,MathMax(x.Get(rowidx,i),y[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMaxRV(int n,CMatrixDouble &y,int rowidx,
|
|
CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(i,MathMax(x[i],y.Get(rowidx,i)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMinV(int n,CRowDouble &y,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(i,MathMin(x[i],y[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMinVR(int n,CRowDouble &y,
|
|
CMatrixDouble &x,int rowidx)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(rowidx,i,MathMin(x.Get(rowidx,i),y[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMergeMinRV(int n,CMatrixDouble &y,int rowidx,
|
|
CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Set(i,MathMin(x[i],y.Get(rowidx,i)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRMaxV(int n,CRowDouble &x)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double v=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
result=0;
|
|
return(result);
|
|
}
|
|
|
|
result=x[0];
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=x[i];
|
|
if(v>result)
|
|
result=v;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRMaxAbsV(int n,CRowDouble &x)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double v=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=MathAbs(x[i]);
|
|
if(v>result)
|
|
result=v;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRMaxR(int n,CMatrixDouble &x,int rowidx)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double v=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
result=0;
|
|
return(result);
|
|
}
|
|
|
|
result=x.Get(rowidx,0);
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=x.Get(rowidx,i);
|
|
if(v>result)
|
|
result=v;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::RefRMaxAbsR(int n,CMatrixDouble &x,int rowidx)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double v=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=MathAbs(x.Get(rowidx,i));
|
|
if(v>result)
|
|
result=v;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRGemVX(int m,int n,double alpha,
|
|
CMatrixDouble &a,int ia,
|
|
int ja,int opa,
|
|
CRowDouble &x,int ix,double beta,
|
|
CRowDouble &y,int iy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- Quick exit for M=0, N=0 or Alpha=0.
|
|
//--- After this block we have M>0, N>0, Alpha<>0.
|
|
if(m<=0)
|
|
return;
|
|
if(n<=0 || alpha==0.0)
|
|
{
|
|
if(beta!=0.0)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
y.Mul(iy+i,beta);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
y.Set(iy+i,0.0);
|
|
}
|
|
return;
|
|
}
|
|
//--- Generic code
|
|
if(opa==0)
|
|
{
|
|
//--- y = A*x
|
|
for(i=0; i<m; i++)
|
|
{
|
|
i1_=(ix)-(ja);
|
|
v=0.0;
|
|
for(i_=ja; i_<ja+n; i_++)
|
|
v+=a.Get(ia+i,i_)*x[i_+i1_];
|
|
if(beta==0.0)
|
|
y.Set(iy+i,alpha*v);
|
|
else
|
|
y.Set(iy+i,alpha*v+beta*y[iy+i]);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(opa==1)
|
|
{
|
|
//--- Prepare output array
|
|
if(beta==0.0)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
y.Set(iy+i,0);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<m; i++)
|
|
y.Mul(iy+i,beta);
|
|
}
|
|
//--- y += A^T*x
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=alpha*x[ix+i];
|
|
i1_=(ja)-(iy);
|
|
for(i_=iy; i_<iy+m; i_++)
|
|
y.Add(i_,v*a.Get(ia+i,i_+i1_));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference TRSV |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefTrsVX(int n,CMatrixDouble &a,int ia,int ja,
|
|
bool isupper,bool isunit,int optype,
|
|
CRowDouble &x,int ix)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
//--- Quick exit
|
|
if(n<=0)
|
|
return;
|
|
//--- Generic code
|
|
if(optype==0 && isupper)
|
|
{
|
|
for(i=n-1; i>=0; i--)
|
|
{
|
|
v=x[ix+i];
|
|
for(j=i+1; j<n; j++)
|
|
v-=a.Get(ia+i,ja+j)*x[ix+j];
|
|
if(!isunit)
|
|
v/=a.Get(ia+i,ja+i);
|
|
x.Set(ix+i,v);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(optype==0 && !isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x[ix+i];
|
|
for(j=0; j<i; j++)
|
|
v-=a.Get(ia+i,ja+j)*x[ix+j];
|
|
if(!isunit)
|
|
v/=a.Get(ia+i,ja+i);
|
|
x.Set(ix+i,v);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(optype==1 && isupper)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=x[ix+i];
|
|
if(!isunit)
|
|
v/=a.Get(ia+i,ja+i);
|
|
x.Set(ix+i,v);
|
|
if(v==0)
|
|
continue;
|
|
for(j=i+1; j<n; j++)
|
|
x.Add(ix+j,- v*a.Get(ia+i,ja+j));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(optype==1 && !isupper)
|
|
{
|
|
for(i=n-1; i>=0; i--)
|
|
{
|
|
v=x[ix+i];
|
|
if(!isunit)
|
|
v/=a.Get(ia+i,ja+i);
|
|
x.Set(ix+i,v);
|
|
if(v==0)
|
|
continue;
|
|
for(j=0; j<i; j++)
|
|
x.Add(ix+j,- v*a.Get(ia+i,ja+j));
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
CAp::Assert(false,"RMatrixTRSV: unexpected operation type");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXDot(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
int ridx=0;
|
|
int ridx2=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
iseed ++;
|
|
//--- Compute each rDot version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
ridx2=CMath::RandomInteger(MathMax(n,1));
|
|
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotV(n,s0.m_x0,s0.m_x1)-RefRDotV(n,s1.m_x0,s1.m_x1)))>tol,"testablasfunit.ap:156");
|
|
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotVR(n,s0.m_x0,s0.m_a0,ridx)-RefRDotVR(n,s1.m_x0,s1.m_a0,ridx)))>tol,"testablasfunit.ap:157");
|
|
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotRR(n,s0.m_a0,ridx,s0.m_a1,ridx2)-RefRDotRR(n,s1.m_a0,ridx,s1.m_a1,ridx2)))>tol,"testablasfunit.ap:158");
|
|
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotV2(n,s0.m_x0)-RefRDotV2(n,s1.m_x0)))>tol,"testablasfunit.ap:159");
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXSet(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
double alpha=0;
|
|
int ialpha=0;
|
|
bool balpha;
|
|
int ridx=0;
|
|
int cidx=0;
|
|
int m0=0;
|
|
int m1=0;
|
|
int offsx=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xSetXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
m0=CMath::RandomInteger(MathMax(n,1));
|
|
m1=CMath::RandomInteger(MathMax(n,1));
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
cidx=CMath::RandomInteger(MathMax(n,1));
|
|
offsx=CMath::RandomInteger(n/2+1);
|
|
alpha=2*CMath::RandomReal()-1;
|
|
ialpha=CMath::RandomInteger(21)-10;
|
|
balpha=CMath::RandomReal()>0.5;
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSetV(n,alpha,s0.m_x0);
|
|
RefRSetV(n,alpha,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:197");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSetR(n,alpha,s0.m_a0,ridx);
|
|
RefRSetR(n,alpha,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:203");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSetC(n,alpha,s0.m_a0,cidx);
|
|
RefRSetC(n,alpha,s1.m_a0,cidx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:209");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSetM(m0,m1,alpha,s0.m_a0);
|
|
RefRSetM(m0,m1,alpha,s1.m_a0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:215");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::ISetV(n,ialpha,s0.m_ix0);
|
|
RefISetV(n,ialpha,s1.m_ix0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:221");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::BSetV(n,balpha,s0.m_bx0);
|
|
RefBSetV(n,balpha,s1.m_bx0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:227");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSetVX(n/2,alpha,s0.m_x0,offsx);
|
|
RefRSetVX(n/2,alpha,s1.m_x0,offsx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:233");
|
|
//--- Increment seed
|
|
iseed ++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXAdd(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
double alpha=0;
|
|
int ridx=0;
|
|
int ridx2=0;
|
|
int cidx=0;
|
|
int offsx=0;
|
|
int offsy=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
ridx2=CMath::RandomInteger(MathMax(n,1));
|
|
cidx=CMath::RandomInteger(MathMax(n,1));
|
|
offsx=CMath::RandomInteger(n/2+1);
|
|
offsy=CMath::RandomInteger(n/2+1);
|
|
alpha=2*CMath::RandomReal()-1;
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RAddV(n,alpha,s0.m_x0,s0.m_x1);
|
|
RefRAddV(n,alpha,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:271");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RAddVR(n,alpha,s0.m_x0,s0.m_a0,cidx);
|
|
RefRAddVR(n,alpha,s1.m_x0,s1.m_a0,cidx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:277");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RAddVC(n,alpha,s0.m_x0,s0.m_a0,cidx);
|
|
RefRAddVC(n,alpha,s1.m_x0,s1.m_a0,cidx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:283");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RAddRV(n,alpha,s0.m_a0,ridx,s0.m_x0);
|
|
RefRAddRV(n,alpha,s1.m_a0,ridx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:289");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RAddRR(n,alpha,s0.m_a0,ridx,s0.m_a1,ridx2);
|
|
RefRAddRR(n,alpha,s1.m_a0,ridx,s1.m_a1,ridx2);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:295");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RAddVX(n/2,alpha,s0.m_x0,offsx,s0.m_x1,offsy);
|
|
RefRAddVX(n/2,alpha,s1.m_x0,offsx,s1.m_x1,offsy);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:301");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXMulAdd(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xMulAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2);
|
|
RefRMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:329");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RNegMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2);
|
|
RefRNegMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:335");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2,s0.m_x3);
|
|
RefRCopyMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2,s1.m_x3);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:341");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyNegMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2,s0.m_x3);
|
|
RefRCopyNegMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2,s1.m_x3);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:347");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXMul(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
double alpha=0;
|
|
int ridx=0;
|
|
int offsx=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
offsx=CMath::RandomInteger(n/2+1);
|
|
alpha=2*CMath::RandomReal()-1;
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMulV(n,alpha,s0.m_x0);
|
|
RefRMulV(n,alpha,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:382");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMulR(n,alpha,s0.m_a0,ridx);
|
|
RefRMulR(n,alpha,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:388");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMulVX(n/2,alpha,s0.m_x0,offsx);
|
|
RefRMulVX(n/2,alpha,s1.m_x0,offsx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:394");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXSqrt(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
int ridx=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSqrtV(n,s0.m_x0);
|
|
RefRSqrtV(n,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:425");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RSqrtR(n,s0.m_a0,ridx);
|
|
RefRSqrtR(n,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:431");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXMax(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
int ridx=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
s0.m_v0=CAblasF::RMaxV(n,s0.m_x0);
|
|
s1.m_v0=RefRMaxV(n,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:462");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
s0.m_v0=CAblasF::RMaxAbsV(n,s0.m_x0);
|
|
s1.m_v0=RefRMaxAbsV(n,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:468");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
s0.m_v0=CAblasF::RMaxR(n,s0.m_a0,ridx);
|
|
s1.m_v0=RefRMaxR(n,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:474");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
s0.m_v0=CAblasF::RMaxAbsR(n,s0.m_a0,ridx);
|
|
s1.m_v0=RefRMaxAbsR(n,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:480");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXMerge(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int i=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
int ridx=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMulV(n,s0.m_x0,s0.m_x1);
|
|
RefRMergeMulV(n,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:511");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMulVR(n,s0.m_x0,s0.m_a0,ridx);
|
|
RefRMergeMulVR(n,s1.m_x0,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:517");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMulRV(n,s0.m_a0,ridx,s0.m_x0);
|
|
RefRMergeMulRV(n,s1.m_a0,ridx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:523");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s0.m_x0.Add(i,0.01*CApServ::PosSign(s0.m_x0[i]));
|
|
s1.m_x0.Add(i,0.01*CApServ::PosSign(s1.m_x0[i]));
|
|
}
|
|
CAblasF::RMergeDivV(n,s0.m_x0,s0.m_x1);
|
|
RefRMergeDivV(n,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:534");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s0.m_x0.Add(i,0.01*CApServ::PosSign(s0.m_x0[i]));
|
|
s1.m_x0.Add(i,0.01*CApServ::PosSign(s1.m_x0[i]));
|
|
}
|
|
CAblasF::RMergeDivVR(n,s0.m_x0,s0.m_a0,ridx);
|
|
RefRMergeDivVR(n,s1.m_x0,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:545");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s0.m_a0.Add(ridx,i,0.01*CApServ::PosSign(s0.m_a0.Get(ridx,i)));
|
|
s1.m_a0.Add(ridx,i,0.01*CApServ::PosSign(s1.m_a0.Get(ridx,i)));
|
|
}
|
|
CAblasF::RMergeDivRV(n,s0.m_a0,ridx,s0.m_x0);
|
|
RefRMergeDivRV(n,s1.m_a0,ridx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:556");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMaxV(n,s0.m_x0,s0.m_x1);
|
|
RefRMergeMaxV(n,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:562");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMaxVR(n,s0.m_x0,s0.m_a0,ridx);
|
|
RefRMergeMaxVR(n,s1.m_x0,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:568");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMaxRV(n,s0.m_a0,ridx,s0.m_x0);
|
|
RefRMergeMaxRV(n,s1.m_a0,ridx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:574");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMinV(n,s0.m_x0,s0.m_x1);
|
|
RefRMergeMinV(n,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:580");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMinVR(n,s0.m_x0,s0.m_a0,ridx);
|
|
RefRMergeMinVR(n,s1.m_x0,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:586");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RMergeMinRV(n,s0.m_a0,ridx,s0.m_x0);
|
|
RefRMergeMinRV(n,s1.m_a0,ridx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:592");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXCopy(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
double alpha=0;
|
|
int ridx=0;
|
|
int ridx2=0;
|
|
int cidx=0;
|
|
int offsx=0;
|
|
int offsy=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each xAddXX version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
ridx=CMath::RandomInteger(MathMax(n,1));
|
|
ridx2=CMath::RandomInteger(MathMax(n,1));
|
|
cidx=CMath::RandomInteger(MathMax(n,1));
|
|
offsx=CMath::RandomInteger(n/2+1);
|
|
offsy=CMath::RandomInteger(n/2+1);
|
|
alpha=2*CMath::RandomReal()-1;
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyV(n,s0.m_x0,s0.m_x1);
|
|
RefRCopyV(n,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:630");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::ICopyV(n,s0.m_ix0,s0.m_ix1);
|
|
RefICopyV(n,s1.m_ix0,s1.m_ix1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:636");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::BCopyV(n,s0.m_bx0,s0.m_bx1);
|
|
RefBCopyV(n,s1.m_bx0,s1.m_bx1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:642");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyVR(n,s0.m_x0,s0.m_a0,ridx);
|
|
RefRCopyVR(n,s1.m_x0,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:648");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyRV(n,s0.m_a0,ridx,s0.m_x0);
|
|
RefRCopyRV(n,s1.m_a0,ridx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:654");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyRR(n,s0.m_a0,ridx,s0.m_a1,ridx2);
|
|
RefRCopyRR(n,s1.m_a0,ridx,s1.m_a1,ridx2);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:660");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyVC(n,s0.m_x0,s0.m_a0,cidx);
|
|
RefRCopyVC(n,s1.m_x0,s1.m_a0,cidx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:666");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyCV(n,s0.m_a0,cidx,s0.m_x0);
|
|
RefRCopyCV(n,s1.m_a0,cidx,s1.m_x0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:672");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyVX(n/2,s0.m_x0,offsx,s0.m_x1,offsy);
|
|
RefRCopyVX(n/2,s1.m_x0,offsx,s1.m_x1,offsy);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:678");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::ICopyVX(n/2,s0.m_ix0,offsx,s0.m_ix1,offsy);
|
|
RefICopyVX(n/2,s1.m_ix0,offsx,s1.m_ix1,offsy);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:684");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyMulV(n,alpha,s0.m_x0,s0.m_x1);
|
|
RefRCopyMulV(n,alpha,s1.m_x0,s1.m_x1);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:690");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyMulVR(n,alpha,s0.m_x0,s0.m_a0,ridx);
|
|
RefRCopyMulVR(n,alpha,s1.m_x0,s1.m_a0,ridx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:696");
|
|
//---
|
|
InitPlayground(n,iseed,s0);
|
|
s1=s0;
|
|
CAblasF::RCopyMulVC(n,alpha,s0.m_x0,s0.m_a0,cidx);
|
|
RefRCopyMulVC(n,alpha,s1.m_x0,s1.m_a0,cidx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:702");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXGemV(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int m=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
double alpha=0;
|
|
double beta=0;
|
|
int offs0=0;
|
|
int offs1=0;
|
|
int offsx=0;
|
|
int offsy=0;
|
|
int padding=0;
|
|
int opa=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each GEMV version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
padding=CMath::RandomInteger(10);
|
|
m=CMath::RandomInteger(n+1);
|
|
offs0=CMath::RandomInteger(MathMax(n,1));
|
|
offs1=CMath::RandomInteger(MathMax(n,1));
|
|
offsx=CMath::RandomInteger(MathMax(n,1));
|
|
offsy=CMath::RandomInteger(MathMax(n,1));
|
|
alpha=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2);
|
|
beta=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2);
|
|
opa=CMath::RandomInteger(2);
|
|
//---
|
|
InitPlayground(2*n+padding,iseed,s0);
|
|
InitPlayground(2*n+padding,iseed,s1);
|
|
CAblasF::RGemV(m,n,alpha,s0.m_a0,opa,s0.m_x0,beta,s0.m_x1);
|
|
RefRGemVX(m,n,alpha,s1.m_a0,0,0,opa,s1.m_x0,0,beta,s1.m_x1,0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:745");
|
|
//---
|
|
InitPlayground(2*n+padding,iseed,s0);
|
|
InitPlayground(2*n+padding,iseed,s1);
|
|
CAblasF::RGemVX(m,n,alpha,s0.m_a0,offs0,offs1,opa,s0.m_x0,offsx,beta,s0.m_x1,offsy);
|
|
RefRGemVX(m,n,alpha,s1.m_a0,offs0,offs1,opa,s1.m_x0,offsx,beta,s1.m_x1,offsy);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:751");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXGer(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int m=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
double alpha=0;
|
|
int padding=0;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each GER version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
padding=CMath::RandomInteger(10);
|
|
m=CMath::RandomInteger(n+1);
|
|
alpha=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2);
|
|
//---
|
|
InitPlayground(2*n+padding,iseed,s0);
|
|
InitPlayground(2*n+padding,iseed,s1);
|
|
CAblasF::RGer(m,n,alpha,s0.m_x0,s0.m_x1,s0.m_a0);
|
|
RefGerX(m,n,s1.m_a0,0,0,alpha,s1.m_x0,0,s1.m_x1,0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:786");
|
|
//---
|
|
InitPlayground(2*n+padding,iseed,s0);
|
|
InitPlayground(2*n+padding,iseed,s1);
|
|
CAblasF::RGer(n,m,alpha,s0.m_x0,s0.m_x1,s0.m_a0);
|
|
RefGerX(n,m,s1.m_a0,0,0,alpha,s1.m_x0,0,s1.m_x1,0);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:792");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestAblasFUnit::TestXTrsV(int maxn,double tol)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int iseed=CMath::RandomInteger(10000);
|
|
int offs0=0;
|
|
int offs1=0;
|
|
int offsx=0;
|
|
int padding=0;
|
|
int opa=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
CAblasFPlayground s0;
|
|
CAblasFPlayground s1;
|
|
|
|
for(n=0; n<=maxn; n++)
|
|
{
|
|
//--- Prepare two identical playground structures
|
|
//--- Compute each GEMV version twice - reference vs library.
|
|
//--- Compare playground snapshots - should be identical.
|
|
padding=CMath::RandomInteger(10);
|
|
offs0=CMath::RandomInteger(MathMax(n,1));
|
|
offs1=CMath::RandomInteger(MathMax(n,1));
|
|
offsx=CMath::RandomInteger(MathMax(n,1));
|
|
opa=CMath::RandomInteger(2);
|
|
isupper=CMath::RandomReal()>0.5;
|
|
isunit=CMath::RandomReal()>0.5;
|
|
InitPlayground(2*n+padding,iseed,s0);
|
|
InitPlayground(2*n+padding,iseed,s1);
|
|
CAblasF::RTrsVX(n,s0.m_a0,offs0,offs1,isupper,isunit,opa,s0.m_x0,offsx);
|
|
RefTrsVX(n,s1.m_a0,offs0,offs1,isupper,isunit,opa,s1.m_x0,offsx);
|
|
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:833");
|
|
//--- Increment seed
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::InitPlayground(int minlen,int iseed,CAblasFPlayground &s)
|
|
{
|
|
int maxpad=10;
|
|
int k=iseed+iseed*iseed*iseed;
|
|
|
|
minlen=MathMax(minlen,1);
|
|
s.m_x0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
s.m_x1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
s.m_x2.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
s.m_x3.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
s.m_ix0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
s.m_ix1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
ArrayResize(s.m_bx0,minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
ArrayResize(s.m_bx1,minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
|
|
k++;
|
|
s.m_a0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))),minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k+1))));
|
|
k+=2;
|
|
s.m_a1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))),minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k+1))));
|
|
k+=2;
|
|
s.m_v0=0;
|
|
k=PseudoRandomInit1(s.m_x0,k);
|
|
k=PseudoRandomInit1(s.m_x1,k);
|
|
k=PseudoRandomInit1(s.m_x2,k);
|
|
k=PseudoRandomInit1(s.m_x3,k);
|
|
k=PseudoRandomInit1I(s.m_ix0,k);
|
|
k=PseudoRandomInit1I(s.m_ix1,k);
|
|
k=PseudoRandomInit1B(s.m_bx0,k);
|
|
k=PseudoRandomInit1B(s.m_bx1,k);
|
|
k=PseudoRandomInit2(s.m_a0,k);
|
|
k=PseudoRandomInit2(s.m_a1,k);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::ComparePlaygrounds(CAblasFPlayground &s0,
|
|
CAblasFPlayground &s1)
|
|
{
|
|
double result=0;
|
|
|
|
result=MathMax(result,MathAbs(s0.m_v0-s1.m_v0)/rmx3(MathAbs(s0.m_v0),MathAbs(s1.m_v0),1));
|
|
result=MathMax(result,rcmp1(s0.m_x0,s1.m_x0));
|
|
result=MathMax(result,rcmp1(s0.m_x1,s1.m_x1));
|
|
result=MathMax(result,icmp1(s0.m_ix0,s1.m_ix0));
|
|
result=MathMax(result,icmp1(s0.m_ix1,s1.m_ix1));
|
|
result=MathMax(result,bcmp1(s0.m_bx0,s1.m_bx0));
|
|
result=MathMax(result,bcmp1(s0.m_bx1,s1.m_bx1));
|
|
result=MathMax(result,rcmp1(s0.m_x2,s1.m_x2));
|
|
result=MathMax(result,rcmp1(s0.m_x3,s1.m_x3));
|
|
result=MathMax(result,rcmp2(s0.m_a0,s1.m_a0));
|
|
result=MathMax(result,rcmp2(s0.m_a1,s1.m_a1));
|
|
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int CTestAblasFUnit::PseudoRandomInit1(CRowDouble &x,int iseed)
|
|
{
|
|
for(int i=0; i<CAp::Len(x); i++)
|
|
{
|
|
x.Set(i,MathSin(iseed+MathSin(i)));
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(iseed);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int CTestAblasFUnit::PseudoRandomInit1I(CRowInt &x,int iseed)
|
|
{
|
|
for(int i=0; i<CAp::Len(x); i++)
|
|
{
|
|
x.Set(i,(int)MathRound(100*MathSin(iseed+MathSin(i))));
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(iseed);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int CTestAblasFUnit::PseudoRandomInit1B(bool &x[],int iseed)
|
|
{
|
|
for(int i=0; i<CAp::Len(x); i++)
|
|
{
|
|
x[i]=(double)(MathSin(iseed+MathSin(i)))>0.0;
|
|
iseed++;
|
|
}
|
|
//--- return result
|
|
return(iseed);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int CTestAblasFUnit::PseudoRandomInit2(CMatrixDouble &x,int iseed)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
CRowDouble xr;
|
|
CRowDouble xc;
|
|
xr=vector<double>::Zeros(CAp::Rows(x));
|
|
for(i=0; i<CAp::Rows(x); i++)
|
|
xr.Set(i,MathSin(13+2*i+iseed));
|
|
iseed++;
|
|
xc=vector<double>::Zeros(CAp::Cols(x));
|
|
for(j=0; j<CAp::Cols(x); j++)
|
|
xc.Set(j,MathSin(17+3*j+iseed));
|
|
iseed++;
|
|
for(i=0; i<CAp::Rows(x); i++)
|
|
{
|
|
for(j=0; j<CAp::Cols(x); j++)
|
|
{
|
|
x.Set(i,j,xr[i]+xc[j]);
|
|
while(x.Get(i,j)>1.0)
|
|
x.Add(i,j,- 1);
|
|
while(x.Get(i,j)<(-1.0))
|
|
x.Add(i,j,1);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(iseed);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::rmx3(double r0,double r1,double r2)
|
|
{
|
|
double result=r0;
|
|
|
|
if(r1>result)
|
|
result=r1;
|
|
if(r2>result)
|
|
result=r2;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::rcmp1(CRowDouble &x,CRowDouble &y)
|
|
{
|
|
double result=0;
|
|
int i=0;
|
|
double mx=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match"))
|
|
return(EMPTY_VALUE);
|
|
|
|
result=0;
|
|
mx=1;
|
|
for(i=0; i<CAp::Len(x); i++)
|
|
{
|
|
if(CInfOrNaN::IsNaN(x[i]) && CInfOrNaN::IsNaN(y[i]))
|
|
continue;
|
|
result=MathMax(result,MathAbs(x[i]-y[i]));
|
|
mx=MathMax(mx,MathAbs(x[i]));
|
|
mx=MathMax(mx,MathAbs(y[i]));
|
|
}
|
|
result=result/mx;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::icmp1(CRowInt &x,CRowInt &y)
|
|
{
|
|
double result=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match"))
|
|
return(result);
|
|
|
|
for(int i=0; i<CAp::Len(x) ; i++)
|
|
result=MathMax(result,MathAbs(x[i]-y[i]));
|
|
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::bcmp1(bool &x[],bool &y[])
|
|
{
|
|
double result=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match"))
|
|
return(result);
|
|
|
|
for(int i=0; i<CAp::Len(x); i++)
|
|
{
|
|
if((x[i] && !y[i]) || (y[i] && !x[i]))
|
|
result=1;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTestAblasFUnit::rcmp2(CMatrixDouble &x,CMatrixDouble &y)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double mx=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)==CAp::Rows(y),"rcmp2: rows do not match"))
|
|
return(result);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)==CAp::Cols(y),"rcmp2: cols do not match"))
|
|
return(result);
|
|
|
|
mx=1;
|
|
for(int i=0; i<CAp::Rows(x); i++)
|
|
{
|
|
for(int j=0; j<CAp::Cols(x); j++)
|
|
{
|
|
result=MathMax(result,MathAbs(x.Get(i,j)-y.Get(i,j)));
|
|
mx=MathMax(mx,MathAbs(x.Get(i,j)));
|
|
mx=MathMax(mx,MathAbs(y.Get(i,j)));
|
|
}
|
|
}
|
|
result=result/mx;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRMulAddV(int n,CRowDouble &y,
|
|
CRowDouble &z,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(i,y[i]*z[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRNegMulAddV(int n,CRowDouble &y,
|
|
CRowDouble &z,CRowDouble &x)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
x.Add(i,- y[i]*z[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyMulAddV(int n,CRowDouble &y,CRowDouble &z,
|
|
CRowDouble &x,CRowDouble &r)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
r.Set(i,x[i]+y[i]*z[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefRCopyNegMulAddV(int n,CRowDouble &y,
|
|
CRowDouble &z,
|
|
CRowDouble &x,
|
|
CRowDouble &r)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
r.Set(i,x[i]-y[i]*z[i]);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Reference code |
|
|
//+------------------------------------------------------------------+
|
|
void CTestAblasFUnit::RefGerX(int m,int n,CMatrixDouble &a,int ia,
|
|
int ja,double alpha,
|
|
CRowDouble &u,int iu,
|
|
CRowDouble &v,int iv)
|
|
{
|
|
double s=0;
|
|
|
|
if((m<=0 || n<=0) || alpha==0.0)
|
|
return;
|
|
|
|
for(int i=0; i<m; i++)
|
|
{
|
|
s=alpha*u[iu+i];
|
|
for(int j=0; j<n; j++)
|
|
a.Add(ia+i,ja+j,s*v[iv+j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPolynomialSolverUnit
|
|
{
|
|
public:
|
|
static bool TestPolynomialSolver(bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestPolynomialSolverUnit::TestPolynomialSolver(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors=false;
|
|
CRowDouble a;
|
|
CRowComplex x;
|
|
double eps=0;
|
|
int n=0;
|
|
CPolynomialSolverReport rep;
|
|
//--- Basic tests
|
|
eps=1.0E-6;
|
|
n=1;
|
|
a=vector<double>::Zeros(n+1);
|
|
a.Set(0,2);
|
|
a.Set(1,3);
|
|
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(x[0].real+2.0/3.0))>eps,"testpolynomialsolverunit.ap:33");
|
|
CAp::SetErrorFlag(waserrors,(double)(x[0].imag)!=0.0,"testpolynomialsolverunit.ap:34");
|
|
CAp::SetErrorFlag(waserrors,(double)(rep.m_maxerr)>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:35");
|
|
n=2;
|
|
a=vector<double>::Zeros(n+1);
|
|
a.Set(0,1);
|
|
a.Set(1,-2);
|
|
a.Set(2,1);
|
|
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
|
|
CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[0]-1)>eps,"testpolynomialsolverunit.ap:43");
|
|
CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[1]-1)>eps,"testpolynomialsolverunit.ap:44");
|
|
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:45");
|
|
n=2;
|
|
a=vector<double>::Zeros(n+1);
|
|
a.Set(0,2);
|
|
a.Set(1,-3);
|
|
a.Set(2,1);
|
|
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
|
|
if(x[0].real<x[1].real)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x[0].real-1)>eps,"testpolynomialsolverunit.ap:55");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x[1].real-2)>eps,"testpolynomialsolverunit.ap:56");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x[0].real-2)>eps,"testpolynomialsolverunit.ap:60");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x[1].real-1)>eps,"testpolynomialsolverunit.ap:61");
|
|
}
|
|
CAp::SetErrorFlag(waserrors,x[0].imag!=0.0,"testpolynomialsolverunit.ap:63");
|
|
CAp::SetErrorFlag(waserrors,x[1].imag!=0.0,"testpolynomialsolverunit.ap:64");
|
|
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:65");
|
|
n=2;
|
|
a=vector<double>::Zeros(n+1);
|
|
a.Set(0,1);
|
|
a.Set(1,0);
|
|
a.Set(2,1);
|
|
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
|
|
CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[0]*x[0]+1)>eps,"testpolynomialsolverunit.ap:73");
|
|
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:74");
|
|
n=4;
|
|
a=vector<double>::Zeros(n+1);
|
|
a.Set(4,1);
|
|
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
|
|
CAp::SetErrorFlag(waserrors,x[0]!=0,"testpolynomialsolverunit.ap:84");
|
|
CAp::SetErrorFlag(waserrors,x[1]!=0,"testpolynomialsolverunit.ap:85");
|
|
CAp::SetErrorFlag(waserrors,x[2]!=0,"testpolynomialsolverunit.ap:86");
|
|
CAp::SetErrorFlag(waserrors,x[3]!=0,"testpolynomialsolverunit.ap:87");
|
|
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:88");
|
|
n=2;
|
|
a=vector<double>::Zeros(n+1);
|
|
a.Set(1,3);
|
|
a.Set(2,2);
|
|
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
|
|
if(x[0].real>x[1].real)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,x[0]!=0,"testpolynomialsolverunit.ap:98");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x[1].real+3.0/2.0)>eps,"testpolynomialsolverunit.ap:99");
|
|
CAp::SetErrorFlag(waserrors,x[1].imag!=0.0,"testpolynomialsolverunit.ap:100");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,x[1]!=0,"testpolynomialsolverunit.ap:104");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x[0].real+3.0/2.0)>eps,"testpolynomialsolverunit.ap:105");
|
|
CAp::SetErrorFlag(waserrors,x[0].imag!=0.0,"testpolynomialsolverunit.ap:106");
|
|
}
|
|
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:108");
|
|
if(!silent)
|
|
PrintResult("TESTING POLYNOMIAL SOLVER",!waserrors);
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDirectSparseSolversUnit
|
|
{
|
|
public:
|
|
static bool TestDirectSparseSolvers(bool silent);
|
|
|
|
private:
|
|
static void TestSKS(bool &errorflag);
|
|
static void TestCholesky(bool &errorflag);
|
|
static void TestGen(bool &errorflag);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestDirectSparseSolversUnit::TestDirectSparseSolvers(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool rskserrors;
|
|
bool rcholerrors;
|
|
bool rgenerrors;
|
|
bool waserrors;
|
|
//--- initialization
|
|
rskserrors=false;
|
|
rcholerrors=false;
|
|
rgenerrors=false;
|
|
TestSKS(rskserrors);
|
|
TestCholesky(rcholerrors);
|
|
TestGen(rgenerrors);
|
|
waserrors=(rskserrors||rcholerrors)||rgenerrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING DIRECT SPARSE SOLVERS:");
|
|
PrintResult("* SPD-SKS (real)",!rskserrors);
|
|
PrintResult("* SPD-CRS (real)",!rcholerrors);
|
|
PrintResult("* GENERAL (real)",!rgenerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SPD SKS test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDirectSparseSolversUnit::TestSKS(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
double threshold=0;
|
|
int bw=0;
|
|
bool isupper;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
CRowDouble xe;
|
|
CRowDouble b;
|
|
CRowDouble xs;
|
|
CSparseMatrix sa;
|
|
CSparseMatrix sa2;
|
|
CSparseSolverReport rep;
|
|
CHighQualityRandState rs;
|
|
//--- initialization
|
|
passcount=10;
|
|
maxn=30;
|
|
threshold=1.0E-6;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Well conditioned SPD problems solved with SKS solver
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
isupper=CHighQualityRand::HQRndNormal(rs)>0.5;
|
|
bw=CHighQualityRand::HQRndUniformI(rs,n+1);
|
|
a=matrix<double>::Zeros(n,n);
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,1+CHighQualityRand::HQRndUniformR(rs));
|
|
CSparse::SparseSet(sa,i,i,a.Get(i,i));
|
|
for(j=i+1; j<=MathMin(i+bw,n-1); j++)
|
|
{
|
|
a.Set(i,j,(CHighQualityRand::HQRndUniformR(rs)-0.5)/(double)n);
|
|
a.Set(j,i,a.Get(i,j));
|
|
if(isupper)
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
else
|
|
CSparse::SparseSet(sa,j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.25)
|
|
CSparse::SparseSet(sa,i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3));
|
|
xe.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
xe.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b=vector<double>::Zeros(n);
|
|
CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0);
|
|
//--- Test SKS solver
|
|
CSparse::SparseCopyToSKS(sa,sa2);
|
|
CDirectSparseSolvers::SparseSPDSolveSKS(sa2,isupper,b,xs,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:88");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:89");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:93");
|
|
//--- Test solver #2
|
|
CSparse::SparseCopyToSKS(sa,sa2);
|
|
if(!CTrFac::SparseCholeskySkyLine(sa2,n,isupper))
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testdirectsparsesolversunit.ap:102");
|
|
return;
|
|
}
|
|
CDirectSparseSolvers::SparseSPDCholeskySolve(sa2,isupper,b,xs,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:107");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:108");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:112");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SPD SKS test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDirectSparseSolversUnit::TestCholesky(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
double threshold=0;
|
|
int bw=0;
|
|
bool isupper;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
CRowDouble xe;
|
|
CRowDouble b;
|
|
CRowDouble xs;
|
|
CSparseMatrix sa;
|
|
CSparseMatrix sa2;
|
|
CSparseSolverReport rep;
|
|
CHighQualityRandState rs;
|
|
//--- initialization
|
|
passcount=10;
|
|
maxn=30;
|
|
threshold=1.0E-6;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Well conditioned SPD problems solved with Cholesky solver
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
isupper=CHighQualityRand::HQRndNormal(rs)>0.5;
|
|
bw=CHighQualityRand::HQRndUniformI(rs,n+1);
|
|
a=matrix<double>::Zeros(n,n);
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,1+CHighQualityRand::HQRndUniformR(rs));
|
|
CSparse::SparseSet(sa,i,i,a.Get(i,i));
|
|
for(j=i+1; j<=MathMin(i+bw,n-1); j++)
|
|
{
|
|
a.Set(i,j,(CHighQualityRand::HQRndUniformR(rs)-0.5)/(double)n);
|
|
a.Set(j,i,a.Get(i,j));
|
|
if(isupper)
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
else
|
|
CSparse::SparseSet(sa,j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.25)
|
|
CSparse::SparseSet(sa,i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3));
|
|
xe=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xe.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b=vector<double>::Zeros(n);
|
|
CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0);
|
|
//--- Test CRS/AMD solver
|
|
CSparse::SparseCopy(sa,sa2);
|
|
CDirectSparseSolvers::SparseSPDSolve(sa2,isupper,b,xs,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:196");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:197");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
if(CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:201"))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| General linear test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestDirectSparseSolversUnit::TestGen(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int noffdiag=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CMatrixDouble a;
|
|
CRowDouble xe;
|
|
CRowDouble b;
|
|
CRowDouble xs;
|
|
CRowDouble xs2;
|
|
CRowInt pivp;
|
|
CRowInt pivq;
|
|
CSparseMatrix sa;
|
|
CSparseMatrix sa2;
|
|
CSparseSolverReport rep;
|
|
CHighQualityRandState rs;
|
|
//--- initialization
|
|
passcount=10;
|
|
maxn=30;
|
|
threshold=1.0E-6;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Well conditioned general linear problems solved with LU solver
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Select number of off-diagonal entries, we want to try matrices
|
|
//--- from dense to sparse
|
|
noffdiag=n*(n-1);
|
|
while(true)
|
|
{
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. apply row/col permutation
|
|
//--- 3. generate random solution vector xe
|
|
//--- 4. generate right part b=A*xe
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(k=0; k<noffdiag; k++)
|
|
{
|
|
i=CHighQualityRand::HQRndUniformI(rs,n);
|
|
j=CHighQualityRand::HQRndUniformI(rs,n);
|
|
a.Set(i,j,0.01*CHighQualityRand::HQRndNormal(rs)/(double)n);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1.0+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
for(i=0; i<n; i++)
|
|
CApServ::SwapRows(a,i,i+CHighQualityRand::HQRndUniformI(rs,n-i),n);
|
|
for(i=0; i<n; i++)
|
|
CApServ::SwapCols(a,i,i+CHighQualityRand::HQRndUniformI(rs,n-i),n);
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3));
|
|
xe=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xe.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b=vector<double>::Zeros(n);
|
|
CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0);
|
|
//--- Test solver #1
|
|
CDirectSparseSolvers::SparseSolve(sa,b,xs,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:285");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:286");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:290");
|
|
//--- Test solver #2
|
|
CSparse::SparseCopyToCRS(sa,sa2);
|
|
CTrFac::SparseLU(sa2,0,pivp,pivq);
|
|
CDirectSparseSolvers::SparseLUSolve(sa2,pivp,pivq,b,xs2,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:300");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs2)!=n,"testdirectsparsesolversunit.ap:301");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
if(CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs2[i])>threshold,"testdirectsparsesolversunit.ap:305"))
|
|
return;
|
|
//--- Update fill factor
|
|
if(noffdiag==0)
|
|
break;
|
|
noffdiag=noffdiag/2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestIterativeSparseUnit
|
|
{
|
|
public:
|
|
static bool TestIterativeSparse(bool silent);
|
|
|
|
private:
|
|
static void TestGMRES(int maxn,bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestIterativeSparseUnit::TestIterativeSparse(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int maxn=50;
|
|
bool gmreserrors;
|
|
bool waserrors;
|
|
//--- Prepare error flags
|
|
gmreserrors=false;
|
|
//--- Run tests
|
|
TestGMRES(maxn,gmreserrors);
|
|
//--- report
|
|
waserrors=gmreserrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ITERATIVE SPARSE SOLVERS:");
|
|
PrintResult("* GMRES",!gmreserrors);
|
|
//--- Were errors?
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test GMRES solver. |
|
|
//| on failure sets error flag, on success does not touch it |
|
|
//+------------------------------------------------------------------+
|
|
void CTestIterativeSparseUnit::TestGMRES(int maxn,bool &err)
|
|
{
|
|
//--- create variables
|
|
CSparseMatrix a;
|
|
CSparseMatrix crsa;
|
|
CRowDouble xref;
|
|
CRowDouble x;
|
|
CRowDouble b;
|
|
CRowDouble b2;
|
|
CRowDouble ax;
|
|
CRowDouble xr;
|
|
CRowDouble x0;
|
|
CRowDouble repfirst;
|
|
CRowDouble replast;
|
|
CRowDouble tmp;
|
|
CMatrixDouble z;
|
|
int nnz=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int gmresk=0;
|
|
CSparseSolverState solver;
|
|
CHighQualityRandStateShell rs;
|
|
double epsf=0;
|
|
bool isupper;
|
|
CSparseSolverReport rep;
|
|
double v=0;
|
|
double tol=0;
|
|
double rprev=0;
|
|
int requesttype=0;
|
|
int nmv=0;
|
|
int nreports=0;
|
|
|
|
CAlglib::HQRndRandomize(rs);
|
|
epsf=MathPow(10,-4-CAlglib::HQRndUniformI(rs,3));
|
|
//--- Test that GMRES is a Krylov subspace method, i.e. its iterate belongs to span(b,A*b,A*A*b,...)
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
//--- Generate sparse matrix and RHS.
|
|
CSparse::SparseCreate(n,n,0,a);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(a,i,j,CAlglib::HQRndNormal(rs));
|
|
}
|
|
CSparse::SparseCopyToCRS(a,crsa);
|
|
CAlglib::HQRndNormalV(rs,n,xref);
|
|
//--- Test using nonsymmetric solve
|
|
for(gmresk=1; gmresk<MathMin(6,n); gmresk++)
|
|
{
|
|
CSparse::SparseMV(crsa,xref,b);
|
|
CIterativeSparse::SparseSolverCreate(n,solver);
|
|
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
|
|
CIterativeSparse::SparseSolverSetCond(solver,0,gmresk);
|
|
CIterativeSparse::SparseSolverSolve(solver,a,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:107");
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testiterativesparseunit.ap:108");
|
|
if(err)
|
|
return;
|
|
//--- Test that the solution belongs to the Krylov subspace
|
|
z=matrix<double>::Zeros(gmresk+1,n);
|
|
CAblasF::RCopyAllocV(n,b,xr);
|
|
for(k=0; k<gmresk; k++)
|
|
{
|
|
CAblasF::RCopyVR(n,xr,z,k);
|
|
CSparse::SparseMV(crsa,xr,ax);
|
|
CAblasF::RCopyV(n,ax,xr);
|
|
}
|
|
CAblasF::RCopyVR(n,x,z,gmresk);
|
|
COrtFac::RMatrixLQ(z,gmresk+1,n,tmp);
|
|
CAp::SetErrorFlag(err,MathAbs(z.Get(gmresk,gmresk))>1.0E-6,"testiterativesparseunit.ap:125");
|
|
//--- Additional test - R2 field is correct
|
|
CSparse::SparseMV(crsa,x,ax);
|
|
CAblasF::RAddV(n,-1.0,b,ax);
|
|
if(CAp::SetErrorFlag(err,MathAbs(rep.m_r2-CAblasF::RDotV2(n,ax))>1.0E-6,"testiterativesparseunit.ap:132"))
|
|
return;
|
|
}
|
|
}
|
|
//--- Randomly generated sparse problem, possibly degenerate.
|
|
//--- Iterate until convergence.
|
|
//--- Try various sparsity patterns.
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
nnz=n*n;
|
|
while(true)
|
|
{
|
|
//--- Generate sparse matrix and RHS.
|
|
//--- The matrix is regularized by placing large elements on its diagonal
|
|
CSparse::SparseCreate(n,n,0,a);
|
|
for(k=0; k<nnz; k++)
|
|
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndNormal(rs));
|
|
for(i=0; i<n; i++)
|
|
CSparse::SparseSet(a,i,i,(2*CAlglib::HQRndUniformI(rs,2)-1)*(1+3*((double)nnz/(double)n)+CMath::Sqr(CAlglib::HQRndNormal(rs))));
|
|
k=CAlglib::HQRndUniformI(rs,3);
|
|
if(k==1)
|
|
CSparse::SparseConvertToCRS(a);
|
|
if(k==2)
|
|
CSparse::SparseConvertToSKS(a);
|
|
CSparse::SparseCopyToCRS(a,crsa);
|
|
CAlglib::HQRndNormalV(rs,n,xref);
|
|
//--- Symmetric solve:
|
|
//--- * solver object API
|
|
//--- * easy access function
|
|
//--- We do not test out-of-core API because it is used internally by the
|
|
//--- SparseSolve/SparseSolveSymmetric pair of functions.
|
|
gmresk=5+CAlglib::HQRndUniformI(rs,2*n+1);
|
|
isupper=CAlglib::HQRndNormal(rs)>0.0;
|
|
CSparse::SparseSMV(crsa,isupper,xref,b);
|
|
CIterativeSparse::SparseSolverCreate(n,solver);
|
|
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
|
|
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
|
|
CIterativeSparse::SparseSolverSolveSymmetric(solver,a,isupper,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:180");
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:181");
|
|
if(err)
|
|
{
|
|
int r=CSparse::SparseGetNRows(a);
|
|
int c=CSparse::SparseGetNCols(a);
|
|
matrix m=matrix<double>::Zeros(r,c);
|
|
CRowDouble sr;
|
|
for(int row=0; row<r; row++)
|
|
{
|
|
CAlglib::SparseGetRow(a,row,sr);
|
|
m.Row(sr.ToVector()+0,row);
|
|
}
|
|
Print(m);
|
|
Print(xref.ToVector());
|
|
Print(isupper);
|
|
Print(gmresk);
|
|
return;
|
|
}
|
|
CSparse::SparseSMV(crsa,isupper,x,b2);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(b[i]-b2[i]);
|
|
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
|
|
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:189");
|
|
CIterativeSparse::SparseSolveSymmetricGMRES(a,isupper,b,gmresk,epsf,0,x,rep);
|
|
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:193");
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:194");
|
|
if(err)
|
|
return;
|
|
CSparse::SparseSMV(crsa,isupper,x,b2);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(b[i]-b2[i]);
|
|
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
|
|
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:202");
|
|
//--- Nonsymmetric solve
|
|
gmresk=5+CAlglib::HQRndUniformI(rs,2*n+1);
|
|
CSparse::SparseMV(crsa,xref,b);
|
|
CIterativeSparse::SparseSolverCreate(n,solver);
|
|
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
|
|
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
|
|
CIterativeSparse::SparseSolverSolve(solver,a,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:215");
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:216");
|
|
if(err)
|
|
return;
|
|
CSparse::SparseMV(crsa,x,b2);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(b[i]-b2[i]);
|
|
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
|
|
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:224");
|
|
CIterativeSparse::SparseSolveGMRES(a,b,gmresk,epsf,0,x,rep);
|
|
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:228");
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:229");
|
|
if(err)
|
|
return;
|
|
CSparse::SparseMV(crsa,x,b2);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(b[i]-b2[i]);
|
|
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
|
|
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:237");
|
|
//--- Increase sparsity
|
|
if(nnz==0)
|
|
break;
|
|
nnz/=2;
|
|
}
|
|
}
|
|
//--- Check that initial point is actually used and improves the situation:
|
|
//--- * generate big random problem
|
|
//--- * solve it with GMRES(1), EpsF=0.1
|
|
//--- * restart using previous solution as initial point
|
|
//--- Restarted solution must be achieved in 1 iteration
|
|
//--- To be exact, we perform three tests:
|
|
//--- * initial solution needs more than 1 iteration
|
|
//--- * attempt to solve second time without starting point again needs more than 1 iteration
|
|
//--- * solution with starting point needs 1 iteration
|
|
n=100;
|
|
nnz=10*n;
|
|
epsf=0.001;
|
|
gmresk=10;
|
|
CSparse::SparseCreate(n,n,0,a);
|
|
for(k=0; k<nnz; k++)
|
|
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),0.1*CAlglib::HQRndNormal(rs));
|
|
for(k=0; k<n; k++)
|
|
CSparse::SparseSet(a,k,k,(2*CAlglib::HQRndUniformI(rs,2)-1)*((double)nnz/(double)n+CMath::Sqr(CAlglib::HQRndNormal(rs))));
|
|
CSparse::SparseCopyToCRS(a,crsa);
|
|
CAlglib::HQRndNormalV(rs,n,xref);
|
|
CSparse::SparseMV(crsa,xref,b);
|
|
CIterativeSparse::SparseSolverCreate(n,solver);
|
|
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
|
|
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
|
|
CIterativeSparse::SparseSolverSolve(solver,a,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:278");
|
|
CAp::SetErrorFlag(err,rep.m_iterationscount<=1,"testiterativesparseunit.ap:279");
|
|
if(err)
|
|
return;
|
|
CIterativeSparse::SparseSolverSolve(solver,a,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:285");
|
|
CAp::SetErrorFlag(err,rep.m_iterationscount<=1,"testiterativesparseunit.ap:286");
|
|
if(err)
|
|
return;
|
|
CIterativeSparse::SparseSolverSetStartingPoint(solver,x);
|
|
CIterativeSparse::SparseSolverSolve(solver,a,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:293");
|
|
CAp::SetErrorFlag(err,rep.m_iterationscount>1,"testiterativesparseunit.ap:294");
|
|
if(err)
|
|
return;
|
|
//--- Check termination request handling by the out-of-core API.
|
|
//--- Solve long-running problem, at each OOC call randomly decide whether
|
|
//--- we want to terminate the process or not.
|
|
n=100;
|
|
nnz=10*n;
|
|
epsf=1.0E-50;
|
|
gmresk=1;
|
|
CSparse::SparseCreate(n,n,0,a);
|
|
for(k=0; k<nnz; k++)
|
|
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),0.1*CAlglib::HQRndNormal(rs));
|
|
for(k=0; k<n; k++)
|
|
CSparse::SparseSet(a,k,k,1+CMath::Sqr(CAlglib::HQRndNormal(rs)));
|
|
CSparse::SparseCopyToCRS(a,crsa);
|
|
CAlglib::HQRndNormalV(rs,n,xref);
|
|
CSparse::SparseMV(crsa,xref,b);
|
|
CIterativeSparse::SparseSolverCreate(n,solver);
|
|
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
|
|
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
|
|
CIterativeSparse::SparseSolverOOCStart(solver,b);
|
|
while(CIterativeSparse::SparseSolverOOCContinue(solver))
|
|
{
|
|
if(!CAp::Assert(solver.m_requesttype==0,"SparseSolverSolve: integrity check 7372 failed"))
|
|
return;
|
|
CSparse::SparseMV(crsa,solver.m_x,solver.m_ax);
|
|
if(CAlglib::HQRndNormal(rs)<0.0)
|
|
CIterativeSparse::SparseSolverRequestTermination(solver);
|
|
}
|
|
CIterativeSparse::SparseSolverOOCStop(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testiterativesparseunit.ap:329");
|
|
//--- Check OOC API, check reports:
|
|
//--- * OOC API works correctly
|
|
//--- * solver reports a sequence of decreased residuals
|
|
//--- * first report is equal to the initial point, last report is equal to the final point
|
|
//--- * when XRep is activated, it does not crash SparseSolve() or SparseSolveSymmetric()
|
|
n=100;
|
|
nnz=10*n;
|
|
epsf=1.0E-3;
|
|
gmresk=5;
|
|
tol=MathSqrt(CMath::m_machineepsilon);
|
|
CSparse::SparseCreate(n,n,0,a);
|
|
for(k=0; k<nnz; k++)
|
|
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),0.1*CAlglib::HQRndNormal(rs));
|
|
for(k=0; k<n; k++)
|
|
CSparse::SparseSet(a,k,k,(double)nnz/(double)n+CMath::Sqr(CAlglib::HQRndNormal(rs)));
|
|
CSparse::SparseCopyToCRS(a,crsa);
|
|
CAlglib::HQRndNormalV(rs,n,xref);
|
|
CAlglib::HQRndNormalV(rs,n,x0);
|
|
CSparse::SparseMV(crsa,xref,b);
|
|
CIterativeSparse::SparseSolverCreate(n,solver);
|
|
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
|
|
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
|
|
CIterativeSparse::SparseSolverSetXRep(solver,true);
|
|
CIterativeSparse::SparseSolverSetStartingPoint(solver,x0);
|
|
CIterativeSparse::SparseSolverOOCStart(solver,b);
|
|
rprev=CMath::m_maxrealnumber;
|
|
nmv=0;
|
|
nreports=0;
|
|
while(CIterativeSparse::SparseSolverOOCContinue(solver))
|
|
{
|
|
CIterativeSparse::SparseSolverOOCGetRequestInfo(solver,requesttype);
|
|
if(requesttype==-1)
|
|
{
|
|
CIterativeSparse::SparseSolverOOCGetRequestData(solver,xr);
|
|
CIterativeSparse::SparseSolverOOCGetRequestData1(solver,v);
|
|
if(CAp::Len(repfirst)==0)
|
|
CAblasF::RCopyAllocV(n,xr,repfirst);
|
|
CAblasF::RCopyAllocV(n,xr,replast);
|
|
CSparse::SparseMV(crsa,xr,ax);
|
|
CAblasF::RAddV(n,-1.0,b,ax);
|
|
CAp::SetErrorFlag(err,MathAbs(CAblasF::RDotV2(n,ax)-v)>(tol*(1+CAblasF::RDotV2(n,ax))),"testiterativesparseunit.ap:376");
|
|
CAp::SetErrorFlag(err,v>(rprev+tol),"testiterativesparseunit.ap:377");
|
|
rprev=v;
|
|
nreports++;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(requesttype==0,"SparseSolverSolve: integrity check 5364 failed"))
|
|
return;
|
|
CIterativeSparse::SparseSolverOOCGetRequestData(solver,xr);
|
|
CSparse::SparseMV(crsa,xr,ax);
|
|
nmv++;
|
|
CIterativeSparse::SparseSolverOOCSendResult(solver,ax);
|
|
}
|
|
CIterativeSparse::SparseSolverOOCStop(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testiterativesparseunit.ap:389");
|
|
CAp::SetErrorFlag(err,rep.m_nmv!=nmv,"testiterativesparseunit.ap:390");
|
|
CAp::SetErrorFlag(err,rep.m_iterationscount<(nreports-2)*gmresk,"testiterativesparseunit.ap:391");
|
|
CAp::SetErrorFlag(err,CAp::Len(repfirst)!=n,"testiterativesparseunit.ap:392");
|
|
CAp::SetErrorFlag(err,CAp::Len(replast)!=n,"testiterativesparseunit.ap:393");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(repfirst[i]-x0[i])>(100.0*CMath::m_machineepsilon),"testiterativesparseunit.ap:398");
|
|
CAp::SetErrorFlag(err,MathAbs(replast[i]-x[i])>(double)(100*CMath::m_machineepsilon),"testiterativesparseunit.ap:399");
|
|
}
|
|
CIterativeSparse::SparseSolverSolve(solver,a,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:403");
|
|
CIterativeSparse::SparseSolverSolveSymmetric(solver,a,CAlglib::HQRndNormal(rs)>0.0,b);
|
|
CIterativeSparse::SparseSolverResults(solver,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:406");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLinCGUnit
|
|
{
|
|
public:
|
|
static const double m_e0;
|
|
static const double m_maxcond;
|
|
|
|
static bool TestLinCG(bool silent);
|
|
|
|
private:
|
|
static bool ComplexTest(bool silent);
|
|
static bool ComplexRes(bool silent);
|
|
static bool BasicTestX(bool silent);
|
|
static bool TestRCorrectness(bool silent);
|
|
static bool BasicTestIters(bool silent);
|
|
static bool KrylovSubspaceTest(bool silent);
|
|
static bool SparseTest(bool silent);
|
|
static bool PrecondTest(bool silent);
|
|
static void GramShmidtOrtnorm(CMatrixDouble &a,int n,int k,double eps,CMatrixDouble &b,int &k2);
|
|
static bool FromBasis(CRowDouble &x,CMatrixDouble &basis,int n,int k,double eps);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const double CTestLinCGUnit::m_e0=1.0E-6;
|
|
const double CTestLinCGUnit::m_maxcond=30;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::TestLinCG(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool basictestxerrors;
|
|
bool basictestiterserr;
|
|
bool complexreserrors;
|
|
bool complexerrors;
|
|
bool rcorrectness;
|
|
bool krylovsubspaceerr;
|
|
bool sparseerrors;
|
|
bool preconderrors;
|
|
bool waserrors;
|
|
//--- Tests
|
|
basictestxerrors=BasicTestX(silent);
|
|
basictestiterserr=BasicTestIters(silent);
|
|
complexreserrors=ComplexRes(silent);
|
|
complexerrors=ComplexTest(silent);
|
|
rcorrectness=TestRCorrectness(silent);
|
|
krylovsubspaceerr=KrylovSubspaceTest(silent);
|
|
sparseerrors=SparseTest(silent);
|
|
preconderrors=PrecondTest(silent);
|
|
//--- report
|
|
waserrors=((((((basictestxerrors||complexreserrors)||complexerrors)||rcorrectness)||basictestiterserr)||krylovsubspaceerr)||sparseerrors)||preconderrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LinCG");
|
|
PrintResult("BasicTestX",!basictestxerrors);
|
|
PrintResult("BasicTestIters",!basictestiterserr);
|
|
PrintResult("ComplexResTest",!complexreserrors);
|
|
PrintResult("ComplexTest",!complexerrors);
|
|
PrintResult("R2 correctness",!rcorrectness);
|
|
PrintResult("KrylovSubSpaceTest",!krylovsubspaceerr);
|
|
PrintResult("SparseTest",!sparseerrors);
|
|
PrintResult("PrecondTest",!preconderrors);
|
|
//--- was errors?
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing LinCGIteration function(custom option), |
|
|
//| which to solve Ax=b (here A is random positive definite matrix |
|
|
//| NxN, b is random vector). It uses the default stopping criterion |
|
|
//| (RNorm<FEps=10^-6). If algorithm does more iterations than size |
|
|
//| of the problem, then some errors are possible. |
|
|
//| The test verifies the following propirties: |
|
|
//| 1. (A*pk,pm)=0 for any m<>k; |
|
|
//| 2. (rk,rm)=0 for any m<>k; |
|
|
//| 3. (rk,pm)=0 for any m<>k; |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::ComplexTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
double mx=100;
|
|
int n=5;
|
|
CLinCGState state;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
double c=0;
|
|
CRowDouble x0;
|
|
CRowDouble residual;
|
|
double normofresidual=0;
|
|
double sclr=0;
|
|
double na=0;
|
|
double nv0=0;
|
|
double nv1=0;
|
|
int sz=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
double tmp=0;
|
|
CMatrixDouble mtx;
|
|
CMatrixDouble mtp;
|
|
CMatrixDouble mtr;
|
|
double getrnorm=0;
|
|
int numofit=0;
|
|
|
|
for(sz=1; sz<=n; sz++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with norm NA (equal to 1.0),
|
|
//--- * random right part B whose elements are uniformly distributed in [-MX,+MX]
|
|
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
|
|
c=15+15*CMath::RandomReal();
|
|
CMatGen::SPDMatrixRndCond(sz,c,a);
|
|
na=1;
|
|
b=vector<double>::Zeros(sz);
|
|
x0=vector<double>::Zeros(sz);
|
|
for(i=0; i<sz; i++)
|
|
{
|
|
b.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
x0.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
}
|
|
mtx=matrix<double>::Zeros(sz+1,sz);
|
|
//--- Start optimization, record its progress for further analysis
|
|
//--- NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
|
|
CLinCG::LinCGCreate(sz,state);
|
|
CLinCG::LinCGSetXRep(state,true);
|
|
CLinCG::LinCGSetB(state,b);
|
|
CLinCG::LinCGSetStartingPoint(state,x0);
|
|
CLinCG::LinCGSetCond(state,0,sz);
|
|
CLinCG::LinCGSetRUpdateFreq(state,2);
|
|
numofit=0;
|
|
getrnorm=CMath::m_maxrealnumber;
|
|
while(CLinCG::LinCGIteration(state))
|
|
{
|
|
if(state.m_needmv)
|
|
{
|
|
for(i=0; i<sz; i++)
|
|
state.m_mv.Set(i,CAblasF::RDotVR(sz,state.m_x,a,i));
|
|
}
|
|
if(state.m_needvmv)
|
|
{
|
|
state.m_vmv=0;
|
|
for(i=0; i<sz; i++)
|
|
{
|
|
state.m_mv.Set(i,CAblasF::RDotVR(sz,state.m_x,a,i));
|
|
state.m_vmv+=state.m_mv[i]*state.m_x[i];
|
|
}
|
|
}
|
|
if(state.m_needprec)
|
|
state.m_pv=state.m_x;
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- Save current point to MtX, it will be used later for additional tests
|
|
if(numofit>=CAp::Rows(mtx))
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:208");
|
|
return(result);
|
|
}
|
|
mtx.Row(numofit,state.m_x);
|
|
getrnorm=state.m_r2;
|
|
numofit++;
|
|
}
|
|
}
|
|
CLinCG::LinCGResult(state,x0,rep);
|
|
if(getrnorm!=rep.m_r2)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintFormat("IterationsCount=%d;\nNMV=&d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
|
|
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::GetRNorm<>Rep.R2!(%0.2E<>%0.2E)\n",sz,c,getrnorm,rep.m_r2);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:227");
|
|
return(result);
|
|
}
|
|
//--- Calculate residual, check result
|
|
residual=vector<double>::Zeros(sz);
|
|
for(i=0; i<sz; i++)
|
|
{
|
|
tmp=0;
|
|
for(j=0; j<sz; j++)
|
|
{
|
|
tmp=tmp+a.Get(i,j)*x0[j];
|
|
}
|
|
residual.Set(i,b[i]-tmp);
|
|
}
|
|
normofresidual=0;
|
|
for(i=0; i<sz; i++)
|
|
{
|
|
if(MathAbs(residual[i])>m_e0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
|
|
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::Discripancy[%d]>E0!(%0.2E>%0.2E)\n",sz,c,i,residual[i],m_e0);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:254");
|
|
return(result);
|
|
}
|
|
normofresidual+=residual[i]*residual[i];
|
|
}
|
|
if(MathAbs(normofresidual-rep.m_r2)>m_e0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
|
|
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::||NormOfResidual-Rep.R2||>E0!(%0.2E>%0.2E)\n",sz,c,MathAbs(normofresidual - rep.m_r2),m_e0);
|
|
PrintFormat("NormOfResidual=%0.2E; Rep.R2=%0.2E\n",normofresidual,rep.m_r2);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:270");
|
|
return(result);
|
|
}
|
|
//--- Check algorithm properties (conjugacy/orthogonality).
|
|
//--- Here we use MtX which was filled during algorithm progress towards solution.
|
|
//--- NOTE: this test is skipped when algorithm converged in less than SZ iterations.
|
|
if(sz>1 && rep.m_iterationscount==sz)
|
|
{
|
|
mtp=matrix<double>::Zeros(sz,sz);
|
|
mtr=matrix<double>::Zeros(sz,sz);
|
|
for(i=0; i<sz; i++)
|
|
for(j=0; j<sz; j++)
|
|
{
|
|
mtp.Set(i,j,mtx.Get(i+1,j)-mtx.Get(i,j));
|
|
tmp=CAblasF::RDotRR(sz,a,j,mtx,i);
|
|
mtr.Set(i,j,b[j]-tmp);
|
|
}
|
|
//--- (Api,pj)=0?
|
|
sclr=0;
|
|
nv0=0;
|
|
nv1=0;
|
|
for(i=0; i<sz; i++)
|
|
{
|
|
for(j=0; j<sz; j++)
|
|
{
|
|
if(i==j)
|
|
continue;
|
|
for(k=0; k<sz; k++)
|
|
{
|
|
tmp=CAblasF::RDotRR(sz,a,k,mtp,i);
|
|
sclr+=tmp*mtp.Get(j,k);
|
|
nv0+=mtp.Get(i,k)*mtp.Get(i,k);
|
|
nv1+=mtp.Get(j,k)*mtp.Get(j,k);
|
|
}
|
|
nv0=MathSqrt(nv0);
|
|
nv1=MathSqrt(nv1);
|
|
if(MathAbs(sclr)>(m_e0*na*MathMax(nv0,1)*MathMax(nv1,1)))
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
|
|
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(Ap%d,p%d)!=0\n{{Sclr=%0.15f; NA=%0.15f NV0=%0.15f NV1=%0.15f;}}\n",sz,c,i,j,sclr,na,nv0,nv1);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:327");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- (ri,pj)=0?
|
|
for(i=1; i<sz; i++)
|
|
{
|
|
for(j=0; j<i; j++)
|
|
{
|
|
sclr=0;
|
|
nv0=0;
|
|
nv1=0;
|
|
for(k=0; k<sz; k++)
|
|
{
|
|
sclr+=mtr.Get(i,k)*mtp.Get(j,k);
|
|
nv0+=mtr.Get(i,k)*mtr.Get(i,k);
|
|
nv1=nv1+mtp.Get(j,k)*mtp.Get(j,k);
|
|
}
|
|
nv0=MathSqrt(nv0);
|
|
nv1=MathSqrt(nv1);
|
|
if(MathAbs(sclr)>(m_e0*MathMax(nv0,1)*MathMax(nv1,1)))
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
|
|
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(r%d,p%d)!=0\n{{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}}\n",sz,c,i,j,sclr,nv0,nv1);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:360");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- (ri,rj)=0?
|
|
for(i=0; i<sz; i++)
|
|
{
|
|
for(j=i+1; j<sz; j++)
|
|
{
|
|
sclr=0;
|
|
nv0=0;
|
|
nv1=0;
|
|
for(k=0; k<sz; k++)
|
|
{
|
|
sclr+=mtr.Get(i,k)*mtr.Get(j,k);
|
|
nv0+=mtr.Get(i,k)*mtr.Get(i,k);
|
|
nv1+=mtr.Get(j,k)*mtr.Get(j,k);
|
|
}
|
|
nv0=MathSqrt(nv0);
|
|
nv1=MathSqrt(nv1);
|
|
if(MathAbs(sclr)>(m_e0*MathMax(nv0,1)*MathMax(nv1,1)))
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
|
|
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(rm,rk)!=0\n{{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}}\n",sz,c,sclr,nv0,nv1);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:392");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!silent)
|
|
PrintResult("ComplexTest",true);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function prepare problem with a known solution 'Xs' |
|
|
//| (A*Xs-b=0). There b is A*Xs. After, function check algorithm |
|
|
//| result and 'Xs'. |
|
|
//| There used two stopping criterions: |
|
|
//| 1. achieved the required precision(StCrit=0); |
|
|
//| 2. execution of the required number of iterations(StCrit=1). |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::ComplexRes(bool silent)
|
|
{
|
|
//--- create variables
|
|
int sz=5;
|
|
double mx=100;
|
|
int nxp=100;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble xs;
|
|
CRowDouble x0;
|
|
double err=0;
|
|
int n=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
int stcrit=0;
|
|
double tmp=0;
|
|
double eps=0;
|
|
int xp=0;
|
|
|
|
for(xp=0; xp<nxp; xp++)
|
|
{
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
for(stcrit=0; stcrit<=1; stcrit++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with norm NA (equal to 1.0),
|
|
//--- * random solution XS whose elements are uniformly distributed in [-MX,+MX]
|
|
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
|
|
//--- * B = A*Xs
|
|
c=(m_maxcond-1)*CMath::RandomReal()+1;
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xs=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
xs.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
}
|
|
eps=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CAblasF::RDotVR(n,xs,a,i));
|
|
eps+=b[i]*b[i];
|
|
}
|
|
eps=1.0E-6*MathSqrt(eps);
|
|
//--- Solve with different stopping criteria
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
CLinCG::LinCGSetStartingPoint(s,x0);
|
|
CLinCG::LinCGSetXRep(s,true);
|
|
if(stcrit==0)
|
|
CLinCG::LinCGSetCond(s,1.0E-6,0);
|
|
else
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
}
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
//--- Check result
|
|
err=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tmp=CAblasF::RDotVR(n,s.m_x,a,i);
|
|
err+=CMath::Sqr(b[i]-tmp);
|
|
}
|
|
err=MathSqrt(err);
|
|
if(err>eps)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("ComplexRes",false);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
Print("X and Xs...\n");
|
|
for(j=0; j<n; j++)
|
|
PrintFormat("x[%d]=%0.10f; xs[%d]=%0.10f\n",j,x0[j],j,xs[j]);
|
|
}
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test has been passed
|
|
if(!silent)
|
|
PrintResult("ComplexRes",true);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function check, that XUpdated return State.X=X0 at zero |
|
|
//| iteration and State.X=X(algorithm result) at last. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::BasicTestX(bool silent)
|
|
{
|
|
//--- create variables
|
|
int sz=5;
|
|
double mx=100;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
CRowDouble x00;
|
|
CRowDouble x01;
|
|
int n=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
int iters=0;
|
|
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with norm NA (equal to 1.0),
|
|
//--- * random right part B whose elements are uniformly distributed in [-MX,+MX]
|
|
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
|
|
c=(m_maxcond-1)*CMath::RandomReal()+1;
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x00=vector<double>::Zeros(n);
|
|
x01=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
b.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Solve, save first and last reported points to x00 and x01
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
CLinCG::LinCGSetStartingPoint(s,x0);
|
|
CLinCG::LinCGSetXRep(s,true);
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
iters=0;
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
if(s.m_xupdated)
|
|
{
|
|
if(iters==0)
|
|
x00=s.m_x;
|
|
if(iters==n)
|
|
x01=s.m_x;
|
|
iters++;
|
|
}
|
|
}
|
|
//--- Check first and last points
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x00[i]!=x0[i])
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("BasicTestX",false);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
for(j=0; j<n; j++)
|
|
PrintFormat("x0=%0.5f; x00=%0.5f;\n",x0[j],x00[j]);
|
|
}
|
|
return(true);
|
|
}
|
|
}
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x01[i]!=x0[i])
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("BasicTestX",false);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
for(j=0; j<n; j++)
|
|
PrintFormat("x0=%0.5f; x01=%0.5f;\n",x0[j],x01[j]);
|
|
}
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- test has been passed
|
|
if(!silent)
|
|
PrintResult("BasicTestIters",true);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function checks that XUpdated returns correct State.R2. It |
|
|
//| creates large badly conditioned problem (N=50), which should be |
|
|
//| large enough and ill-conditioned enough to cause periodic |
|
|
//| recalculation of R. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::TestRCorrectness(bool silent)
|
|
{
|
|
//--- create variables
|
|
double rtol=1.0E6*CMath::m_machineepsilon;
|
|
int n=50;
|
|
int maxits=n/2;
|
|
double c=10000;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
int i=0;
|
|
int j=0;
|
|
double r2=0;
|
|
double v=0;
|
|
//--- initialization
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
CLinCG::LinCGSetXRep(s,true);
|
|
CLinCG::LinCGSetCond(s,0,maxits);
|
|
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
if(s.m_xupdated)
|
|
{
|
|
//--- calculate R2, compare with value returned in state.R2
|
|
r2=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,s.m_x,a,i);
|
|
r2=r2+CMath::Sqr(v-b[i]);
|
|
}
|
|
if(MathAbs(r2-s.m_r2)>rtol)
|
|
return(true);
|
|
}
|
|
}
|
|
CLinCG::LinCGResult(s,b,rep);
|
|
if(rep.m_iterationscount!=maxits)
|
|
return(true);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function check, that number of iterations are't more than |
|
|
//| MaxIts. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::BasicTestIters(bool silent)
|
|
{
|
|
//--- create variables
|
|
int sz=5;
|
|
double mx=100;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
int n=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
int iters=0;
|
|
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with norm NA (equal to 1.0),
|
|
//--- * random right part B whose elements are uniformly distributed in [-MX,+MX]
|
|
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
|
|
c=(m_maxcond-1)*CMath::RandomReal()+1;
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
b.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Solve
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
CLinCG::LinCGSetStartingPoint(s,x0);
|
|
CLinCG::LinCGSetXRep(s,true);
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
iters=0;
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
if(s.m_xupdated)
|
|
iters++;
|
|
}
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
//--- Check
|
|
if(iters!=rep.m_iterationscount+1 || iters>n+1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("BasicTestIters",false);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
PrintFormat("Iters=%d\n",iters);
|
|
}
|
|
return(true);
|
|
}
|
|
//--- Restart problem
|
|
c=(m_maxcond-1)*CMath::RandomReal()+1;
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
b.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
}
|
|
CLinCG::LinCGSetStartingPoint(s,x0);
|
|
CLinCG::LinCGRestart(s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
iters=0;
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
if(s.m_xupdated)
|
|
iters++;
|
|
}
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
//--- check
|
|
if(iters!=rep.m_iterationscount+1 || iters>n+1)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("BasicTestIters",false);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
PrintFormat("Iters=%d\n",iters);
|
|
}
|
|
return(true);
|
|
}
|
|
}
|
|
//--- test has been passed
|
|
if(!silent)
|
|
PrintResult("BasicTestIters",true);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function check, that programmed method is Krylov subspace |
|
|
//| methed. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::KrylovSubspaceTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
double eps=1.0E-6;
|
|
int maxits=3;
|
|
int sz=5;
|
|
double mx=100;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
CMatrixDouble ksr;
|
|
CRowDouble r0;
|
|
CRowDouble tarray;
|
|
CMatrixDouble mtx;
|
|
int n=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
int l=0;
|
|
int m=0;
|
|
double tmp=0;
|
|
double normr0=0;
|
|
int numofit=0;
|
|
int k2=0;
|
|
//--- initialization
|
|
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with unit norm
|
|
//--- * cond(A) in [0.5*MaxCond, 1.0*MaxCond]
|
|
//--- * random x0 and b such that |A*x0-b| is large enough for algorithm to make at least one iteration.
|
|
//--- IMPORTANT: it is very important to have cond(A) both (1) not very large and
|
|
//--- (2) not very small. Large cond(A) will make our problem ill-conditioned,
|
|
//--- thus analytic properties won't hold. Small cond(A), from the other side,
|
|
//--- will give us rapid convergence of the algorithm - in fact, too rapid.
|
|
//--- Krylov basis will be dominated by numerical noise and test may fail.
|
|
c=m_maxcond*(0.5*CMath::RandomReal()+0.5);
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
mtx=matrix<double>::Zeros(n+1,n);
|
|
ksr=matrix<double>::Zeros(n,n);
|
|
r0=vector<double>::Zeros(n);
|
|
tarray=vector<double>::Zeros(n);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
//---
|
|
do
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
b.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
}
|
|
normr0=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tmp=CAblasF::RDotVR(n,x0,a,i);
|
|
r0.Set(i,b[i]-tmp);
|
|
normr0+=r0[i]*r0[i];
|
|
}
|
|
}
|
|
while(MathSqrt(normr0)<=eps);
|
|
//--- Fill KSR by {r0, A*r0, A^2*r0, ... }
|
|
for(i=0; i<n; i++)
|
|
{
|
|
ksr.Row(i,r0);
|
|
for(j=0; j<i; j++)
|
|
{
|
|
for(l=0; l<n; l++)
|
|
tarray.Set(l,CAblasF::RDotRR(n,a,l,ksr,i));
|
|
ksr.Row(i,tarray);
|
|
}
|
|
}
|
|
//--- Solve system, record intermediate points for futher analysis.
|
|
//--- NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
CLinCG::LinCGSetStartingPoint(s,x0);
|
|
CLinCG::LinCGSetXRep(s,true);
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
CLinCG::LinCGSetRUpdateFreq(s,2);
|
|
numofit=0;
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
if(s.m_xupdated)
|
|
{
|
|
mtx.Row(numofit,s.m_x);
|
|
numofit++;
|
|
}
|
|
}
|
|
//--- Check that I-th step S_i=X[I+1]-X[i] belongs to I-th Krylov subspace.
|
|
//--- Checks are done for first K2 steps, with K2 small enough to avoid
|
|
//--- numerical errors.
|
|
if(n<=maxits)
|
|
k2=n;
|
|
else
|
|
k2=maxits;
|
|
//---
|
|
for(i=0; i<k2; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
tarray.Set(j,mtx.Get(i+1,j)-mtx.Get(i,j));
|
|
if(!FromBasis(tarray,ksr,n,i+1,m_e0))
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("KrylovSubspaceTest",false);
|
|
PrintFormat("Size=%d; Iters=%d;\n",n,i);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlincgunit.ap:1129");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
if(!silent)
|
|
PrintResult("KrylovSubspaceTest",true);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing LinCgSolveSparse. This function prepare |
|
|
//| problem with a known solution 'Xs'(A * Xs - b = 0). There b is |
|
|
//| A * Xs. After, function calculate result by LinCGSolveSparse and |
|
|
//| compares it with 'Xs'. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::SparseTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
int sz=5;
|
|
double mx=100;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble xs;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CSparseMatrix uppera;
|
|
CSparseMatrix lowera;
|
|
//--- initialization
|
|
double err=0;
|
|
int n=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
double eps=0;
|
|
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with unit norm
|
|
//--- * random X0 (starting point) and XS (known solution)
|
|
//--- Copy dense A to sparse SA
|
|
c=(m_maxcond-1)*CMath::RandomReal()+1;
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
b=vector<double>::Zeros(n);
|
|
xs=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xs.Set(i,mx*(2*CMath::RandomReal()-1));
|
|
eps=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CAblasF::RDotVR(n,xs,a,i));
|
|
eps+=b[i]*b[i];
|
|
}
|
|
eps=1.0E-6*MathSqrt(eps);
|
|
CSparse::SparseCreate(n,n,0,uppera);
|
|
CSparse::SparseCreate(n,n,0,lowera);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(j>=i)
|
|
CSparse::SparseSet(uppera,i,j,a.Get(i,j));
|
|
if(j<=i)
|
|
CSparse::SparseSet(lowera,i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(uppera);
|
|
CSparse::SparseConvertToCRS(lowera);
|
|
//--- Test upper triangle
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
CLinCG::LinCGSolveSparse(s,uppera,true,b);
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
err=0;
|
|
for(i=0; i<n; i++)
|
|
err+=CMath::Sqr(x0[i]-xs[i]);
|
|
err=MathSqrt(err);
|
|
if(err>eps)
|
|
return(true);
|
|
//--- Test lower triangle
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
CLinCG::LinCGSolveSparse(s,lowera,false,b);
|
|
CLinCG::LinCGResult(s,x1,rep);
|
|
err=0;
|
|
for(i=0; i<n; i++)
|
|
err+=CMath::Sqr(x1[i]-xs[i]);
|
|
err=MathSqrt(err);
|
|
if(err>eps)
|
|
return(true);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing the preconditioned conjugate gradient method|
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::PrecondTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CLinCGState s;
|
|
CLinCGReport rep;
|
|
CMatrixDouble a;
|
|
CMatrixDouble ta;
|
|
CSparseMatrix sa;
|
|
CRowDouble m;
|
|
CMatrixDouble mtx;
|
|
CMatrixDouble mtprex;
|
|
CRowDouble de;
|
|
CRowDouble rde;
|
|
CRowDouble b;
|
|
CRowDouble tb;
|
|
CRowDouble d;
|
|
CRowDouble xe;
|
|
CRowDouble x0;
|
|
CRowDouble tx0;
|
|
CRowDouble err;
|
|
//--- initialization
|
|
int n=0;
|
|
int sz=5;
|
|
int numofit=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double eps=0;
|
|
bool bflag;
|
|
//--- Test 1.
|
|
//--- Preconditioned CG for A*x=b with preconditioner M=E*E' is algebraically
|
|
//--- equivalent to non-preconditioned CG for (inv(E)*A*inv(E'))*z = inv(E)*b
|
|
//--- with z=E'*x.
|
|
//--- We test it by generating random preconditioner, running algorithm twice -
|
|
//--- one time for original problem with preconditioner , another one for
|
|
//--- modified problem without preconditioner.
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
//--- Generate:
|
|
//--- * random A with unit norm
|
|
//--- * random positive definite diagonal preconditioner M
|
|
//--- * dE=sqrt(M)
|
|
//--- * rdE=dE^(-1)
|
|
//--- * tA = rdE*A*rdE
|
|
//--- * random x0 and b - for original preconditioned problem
|
|
//--- * tx0 and tb - for modified problem
|
|
c=(m_maxcond-1)*CMath::RandomReal()+1;
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
ta=matrix<double>::Zeros(n,n);
|
|
mtx=matrix<double>::Zeros(n+1,n);
|
|
mtprex=matrix<double>::Zeros(n+1,n);
|
|
m=vector<double>::Zeros(n);
|
|
de=vector<double>::Zeros(n);
|
|
rde=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
m.Set(i,CMath::RandomReal()+0.5);
|
|
de.Set(i,MathSqrt(m[i]));
|
|
rde.Set(i,1/de[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ta.Set(i,j,rde[i]*a.Get(i,j)*rde[j]);
|
|
}
|
|
b=vector<double>::Zeros(n);
|
|
tb=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
tx0=vector<double>::Zeros(n);
|
|
err=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
eps=1.0E-5;
|
|
tx0=de*x0+0;
|
|
tb=rde*b+0;
|
|
//--- Solve two problems, intermediate points are saved to MtX and MtPreX
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetB(s,b);
|
|
CLinCG::LinCGSetStartingPoint(s,x0);
|
|
CLinCG::LinCGSetXRep(s,true);
|
|
CLinCG::LinCGSetCond(s,0,n);
|
|
numofit=0;
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x/m+0;
|
|
if(s.m_xupdated)
|
|
{
|
|
if(numofit>=CAp::Rows(mtx))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
mtx.Row(numofit,s.m_x);
|
|
numofit=numofit+1;
|
|
}
|
|
}
|
|
CLinCG::LinCGSetStartingPoint(s,tx0);
|
|
CLinCG::LinCGSetB(s,tb);
|
|
CLinCG::LinCGRestart(s);
|
|
numofit=0;
|
|
//---
|
|
while(CLinCG::LinCGIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,ta,i));
|
|
}
|
|
if(s.m_needvmv)
|
|
{
|
|
s.m_vmv=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,ta,i));
|
|
s.m_vmv+=s.m_mv[i]*s.m_x[i];
|
|
}
|
|
}
|
|
if(s.m_needprec)
|
|
s.m_pv=s.m_x;
|
|
if(s.m_xupdated)
|
|
{
|
|
if(numofit>=CAp::Rows(mtprex))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
mtprex.Row(numofit,s.m_x);
|
|
numofit=numofit+1;
|
|
}
|
|
}
|
|
//--- Compare results - sequence of points generated when solving original problem with
|
|
//--- points generated by modified problem.
|
|
for(i=0; i<numofit; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(MathAbs(mtx.Get(i,j)-rde[j]*mtprex.Get(i,j))>eps)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("PrecondTest",false);
|
|
PrintFormat("Size=%d\n",n);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
Print("X and X^...\n");
|
|
for(k=0; k<n; k++)
|
|
PrintFormat("I=%d; mtx[%d]=%0.10f; mtx^[%d]=%0.10f\n",i,k,mtx.Get(i,k),k,mtprex.Get(i,k));
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test 2.
|
|
//--- We test automatic diagonal preconditioning used by SolveSparse.
|
|
//--- In order to do so we:
|
|
//--- 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
|
|
//--- 2. generate random "exact" solution xe and right part b=A0*xe
|
|
//--- 3. generate random ill-conditioned diagonal scaling matrix D with
|
|
//--- condition number equal to 1.0E50:
|
|
//--- 4. transform A*x=b into badly scaled problem:
|
|
//--- A0*x0=b0
|
|
//--- A0*D*(inv(D)*x0)=b0
|
|
//--- (D*A0*D)*(inv(D)*x0)=(D*b0)
|
|
//--- finally we got new problem A*x=b with A=D*A0*D, b=D*b0, x=inv(D)*x0
|
|
//--- Then we solve A*x=b:
|
|
//--- 1. with default preconditioner
|
|
//--- 2. with explicitly activayed diagonal preconditioning
|
|
//--- 3. with unit preconditioner.
|
|
//--- 1st and 2nd solutions must be close to xe, 3rd solution must be very
|
|
//--- far from the true one.
|
|
n=20;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E1,ta);
|
|
xe=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xe.Set(i,CApServ::RandomNormal());
|
|
b=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CAblasF::RDotVR(n,xe,ta,i));
|
|
d.Set(i,MathPow(10,100*CMath::RandomReal()-50));
|
|
}
|
|
a=matrix<double>::Zeros(n,n);
|
|
CSparse::SparseCreate(n,n,n*n,sa);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,d[i]*ta.Get(i,j)*d[j]);
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
}
|
|
b.Mul(i,d[i]);
|
|
xe.Mul(i,1.0/d[i]);
|
|
}
|
|
CSparse::SparseConvertToCRS(sa);
|
|
CLinCG::LinCGCreate(n,s);
|
|
CLinCG::LinCGSetCond(s,0,2*n);
|
|
CLinCG::LinCGSolveSparse(s,sa,true,b);
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
CLinCG::LinCGSetPrecUnit(s);
|
|
CLinCG::LinCGSolveSparse(s,sa,true,b);
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
bflag=false;
|
|
for(i=0; i<n; i++)
|
|
bflag=bflag||MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]);
|
|
if(!bflag)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
CLinCG::LinCGSetPrecDiag(s);
|
|
CLinCG::LinCGSolveSparse(s,sa,true,b);
|
|
CLinCG::LinCGResult(s,x0,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- test has been passed
|
|
if(!silent)
|
|
PrintResult("PrecondTest",true);
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Orthogonalization by Gram - Shmidt method. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinCGUnit::GramShmidtOrtnorm(CMatrixDouble &a,
|
|
int n,
|
|
int k,
|
|
double eps,
|
|
CMatrixDouble &b,
|
|
int &k2)
|
|
{
|
|
//--- create variables
|
|
double scaling=0;
|
|
double tmp=0;
|
|
double e=0;
|
|
int i=0;
|
|
int j=0;
|
|
int l=0;
|
|
int m=0;
|
|
double sc=0;
|
|
//--- initialization
|
|
k2=0;
|
|
k2=0;
|
|
scaling=0;
|
|
b=matrix<double>::Zeros(k,n);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
tmp=CAblasF::RDotRR(n,a,i,a,i);
|
|
if(tmp>scaling)
|
|
scaling=tmp;
|
|
}
|
|
scaling=MathSqrt(scaling);
|
|
e=eps*scaling;
|
|
for(i=0; i<k; i++)
|
|
{
|
|
tmp=CAblasF::RDotRR(n,a,i,a,i);
|
|
b.Row(k2,a,i);
|
|
tmp=MathSqrt(tmp);
|
|
if(tmp<=e)
|
|
continue;
|
|
for(j=0; j<k2; j++)
|
|
{
|
|
sc=CAblasF::RDotRR(n,b,k2,b,j);
|
|
b.Row(k2,b[k2]-b[j]*sc);
|
|
}
|
|
tmp=CAblasF::RDotRR(n,b,k2,b,k2);
|
|
tmp=MathSqrt(tmp);
|
|
if(tmp<=e)
|
|
continue;
|
|
else
|
|
b.Row(k2,b[k2]/tmp);
|
|
k2++;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks that a vector belongs to the basis. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinCGUnit::FromBasis(CRowDouble &x,CMatrixDouble &basis,
|
|
int n,int k,double eps)
|
|
{
|
|
//--- create variables
|
|
double normx=0;
|
|
CMatrixDouble ortnormbasis;
|
|
int k2=0;
|
|
int i=0;
|
|
int j=0;
|
|
double alpha=0;
|
|
CRowDouble alphas;
|
|
alphas=vector<double>::Zeros(k);
|
|
//--- calculating NORM for X
|
|
normx=CAblasF::RDotV2(n,x);
|
|
normx=MathSqrt(normx);
|
|
//--- Gram-Shmidt method
|
|
GramShmidtOrtnorm(basis,n,k,MathPow(CMath::m_machineepsilon,0.75),ortnormbasis,k2);
|
|
for(i=0; i<k2; i++)
|
|
{
|
|
alpha=CAblasF::RDotVR(n,x,ortnormbasis,i);
|
|
alphas.Set(i,alpha);
|
|
}
|
|
//--- check
|
|
for(i=0; i<n; i++)
|
|
{
|
|
alpha=0;
|
|
for(j=0; j<k2; j++)
|
|
alpha+=alphas[j]*ortnormbasis.Get(j,i);
|
|
if(MathAbs(x[i]-alpha)>(normx*eps))
|
|
return(false);
|
|
}
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestNormEstimatorUnit
|
|
{
|
|
public:
|
|
static bool TestNormEstimator(bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestNormEstimatorUnit::TestNormEstimator(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
double tol=0.01;
|
|
int maxmn=5;
|
|
bool waserrors=false;
|
|
int m=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
CMatrixDouble a;
|
|
CRowInt rowsizes;
|
|
CSparseMatrix s;
|
|
double snorm=0;
|
|
double enorm=0;
|
|
double enorm2=0;
|
|
int nbetter=0;
|
|
double sigma=0;
|
|
int i=0;
|
|
int j=0;
|
|
CNormEstimatorState e;
|
|
CNormEstimatorState e2;
|
|
//--- First test: algorithm must correctly determine matrix norm
|
|
for(m=1; m<=maxmn; m++)
|
|
{
|
|
for(n=1; n<=maxmn; n++)
|
|
{
|
|
//--- Create estimator with quite large NStart and NIts.
|
|
//--- It should guarantee that we converge to the correct solution.
|
|
CNormEstimator::NormEstimatorCreate(m,n,15,15,e);
|
|
//--- Try with zero A first
|
|
CSparse::SparseCreate(m,n,1,s);
|
|
CSparse::SparseConvertToCRS(s);
|
|
CNormEstimator::NormEstimatorEstimateSparse(e,s);
|
|
CNormEstimator::NormEstimatorResults(e,enorm);
|
|
waserrors=waserrors||enorm!=0.0;
|
|
//--- Choose random norm, try with non-zero matrix
|
|
//--- with specified norm.
|
|
snorm=MathExp(10*CMath::RandomReal()-5);
|
|
CSparse::SparseCreate(m,n,1,s);
|
|
if(m>=n)
|
|
{
|
|
//--- Generate random orthogonal M*M matrix,
|
|
//--- use N leading columns as columns of A
|
|
CMatGen::RMatrixRndOrthogonal(m,a);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Generate random orthogonal N*N matrix,
|
|
//--- use M leading rows as rows of A
|
|
//
|
|
CMatGen::RMatrixRndOrthogonal(n,a);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(s);
|
|
CNormEstimator::NormEstimatorEstimateSparse(e,s);
|
|
CNormEstimator::NormEstimatorResults(e,enorm);
|
|
waserrors=(waserrors||enorm>(snorm*(1+tol)))||enorm<(snorm*(1-tol));
|
|
}
|
|
}
|
|
//--- NStart=10 should give statistically better results than NStart=1.
|
|
//--- In order to test it we perform PassCount attempts to solve random
|
|
//--- problem by means of two estimators: one with NStart=10 and another
|
|
//--- one with NStart=1. Every time we compare two estimates and choose
|
|
//--- better one.
|
|
//--- Random variable NBetter is a number of cases when NStart=10 was better.
|
|
//--- Under null hypothesis (no difference) it is binomially distributed
|
|
//--- with mean PassCount/2 and variance PassCount/4. However, we expect
|
|
//--- to have significant deviation to the right, in the area of larger
|
|
//--- values.
|
|
//--- NOTE: we use fixed N because this test is independent of problem size.
|
|
n=3;
|
|
CNormEstimator::NormEstimatorCreate(n,n,1,1,e);
|
|
CNormEstimator::NormEstimatorCreate(n,n,10,1,e2);
|
|
CNormEstimator::NormEstimatorSetSeed(e,0);
|
|
CNormEstimator::NormEstimatorSetSeed(e2,0);
|
|
nbetter=0;
|
|
passcount=2000;
|
|
sigma=5.0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
snorm=MathPow(10.0,2*CMath::RandomReal()-1);
|
|
CSparse::SparseCreate(n,n,1,s);
|
|
CMatGen::RMatrixRndCond(n,2.0,a);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
|
|
}
|
|
CSparse::SparseConvertToCRS(s);
|
|
CNormEstimator::NormEstimatorEstimateSparse(e,s);
|
|
CNormEstimator::NormEstimatorResults(e,enorm);
|
|
CNormEstimator::NormEstimatorEstimateSparse(e2,s);
|
|
CNormEstimator::NormEstimatorResults(e2,enorm2);
|
|
if(MathAbs(enorm2-snorm)<MathAbs(enorm-snorm))
|
|
nbetter++;
|
|
}
|
|
waserrors=waserrors||nbetter<(0.5*passcount+sigma*MathSqrt(0.25*passcount));
|
|
//--- Same as previous one (for NStart), but tests dependence on NIts.
|
|
n=3;
|
|
CNormEstimator::NormEstimatorCreate(n,n,1,1,e);
|
|
CNormEstimator::NormEstimatorCreate(n,n,1,10,e2);
|
|
CNormEstimator::NormEstimatorSetSeed(e,0);
|
|
CNormEstimator::NormEstimatorSetSeed(e2,0);
|
|
nbetter=0;
|
|
passcount=2000;
|
|
sigma=5.0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
snorm=MathPow(10.0,2*CMath::RandomReal()-1);
|
|
CSparse::SparseCreate(n,n,1,s);
|
|
CMatGen::RMatrixRndCond(n,2.0,a);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
|
|
}
|
|
CSparse::SparseConvertToCRS(s);
|
|
CNormEstimator::NormEstimatorEstimateSparse(e,s);
|
|
CNormEstimator::NormEstimatorResults(e,enorm);
|
|
CNormEstimator::NormEstimatorEstimateSparse(e2,s);
|
|
CNormEstimator::NormEstimatorResults(e2,enorm2);
|
|
if(MathAbs(enorm2-snorm)<MathAbs(enorm-snorm))
|
|
nbetter++;
|
|
}
|
|
waserrors=waserrors||nbetter<(0.5*passcount+sigma*MathSqrt(0.25*passcount));
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING NORM ESTIMATOR");
|
|
PrintResult("TEST",!waserrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLinLSQRUnit
|
|
{
|
|
public:
|
|
static const double m_e0;
|
|
static const double m_tolort;
|
|
static const double m_emergencye0;
|
|
|
|
static bool TestLinLSQR(bool silent);
|
|
|
|
private:
|
|
static bool SVDTest(bool silent);
|
|
static bool MWCRankSVDTest(bool silent);
|
|
static bool MWICRankSVDTest(bool silent);
|
|
static bool BiDiagonalTest(bool silent);
|
|
static bool ZeroMatrixTest(bool silent);
|
|
static bool ReportCorrectnessTest(bool silent);
|
|
static bool StopPingCriteriaTest(bool silent);
|
|
static bool AnalyticTest(bool silent);
|
|
static void TestTerminationRequests(bool &err);
|
|
static bool IsItGoodSolution(CMatrixDouble &a,CRowDouble &b,int m,int n,double lambdav,CRowDouble &x,double epserr,double epsort);
|
|
static bool PreconditionerTest(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const double CTestLinLSQRUnit::m_e0=1.0E-6;
|
|
const double CTestLinLSQRUnit::m_tolort=1.0E-4;
|
|
const double CTestLinLSQRUnit::m_emergencye0=1.0E-12;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::TestLinLSQR(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool svdtesterrors;
|
|
bool mwcranksvderr;
|
|
bool mwicranksvderr;
|
|
bool bidiagonalerr;
|
|
bool zeromatrixerr;
|
|
bool reportcorrectnesserr;
|
|
bool stoppingcriteriaerr;
|
|
bool analytictesterrors;
|
|
bool prectesterrors;
|
|
bool termreqerrors;
|
|
bool waserrors;
|
|
//--- initialization
|
|
termreqerrors=false;
|
|
svdtesterrors=SVDTest(silent);
|
|
mwcranksvderr=MWCRankSVDTest(silent);
|
|
mwicranksvderr=MWICRankSVDTest(silent);
|
|
bidiagonalerr=BiDiagonalTest(silent);
|
|
zeromatrixerr=ZeroMatrixTest(silent);
|
|
reportcorrectnesserr=ReportCorrectnessTest(silent);
|
|
stoppingcriteriaerr=StopPingCriteriaTest(silent);
|
|
analytictesterrors=AnalyticTest(silent);
|
|
prectesterrors=PreconditionerTest();
|
|
TestTerminationRequests(termreqerrors);
|
|
//--- report
|
|
waserrors=(svdtesterrors||mwcranksvderr||mwicranksvderr||bidiagonalerr||zeromatrixerr||reportcorrectnesserr ||
|
|
stoppingcriteriaerr||analytictesterrors||prectesterrors||termreqerrors);
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LinLSQR");
|
|
Print("Different matrix types:");
|
|
PrintResult("* general M*N",!svdtesterrors);
|
|
PrintResult("* well conditioned M*N",!mwcranksvderr);
|
|
PrintResult("* rank deficient M*N",!mwicranksvderr);
|
|
PrintResult("* sparse bidiagonal",!bidiagonalerr);
|
|
PrintResult("* zero",!zeromatrixerr);
|
|
Print("Other properties:");
|
|
PrintResult("* report correctness",!reportcorrectnesserr);
|
|
PrintResult("* stopping criteria",!stoppingcriteriaerr);
|
|
PrintResult("* analytic properties",!analytictesterrors)
|
|
PrintResult("* preconditioner test",!prectesterrors);
|
|
PrintResult("* termination requests",!termreqerrors);
|
|
//--- was errors?
|
|
//
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function generates random MxN problem, solves it with|
|
|
//| LSQR and compares with results obtained from SVD solver. Matrix A|
|
|
//| is generated as MxN matrix with uniformly distributed random |
|
|
//| entries, i.e. it has no special properties (like conditioning or |
|
|
//| separation of singular values). |
|
|
//| We apply random amount regularization to our problem (from zero |
|
|
//| to large) in order to test regularizer. Default stopping |
|
|
//| criteria are used. Preconditioning is turned off because it |
|
|
//| skews results for rank-deficient problems. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::SVDTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
int szm=5;
|
|
int szn=5;
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CSparseMatrix spa;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
int n=0;
|
|
int m=0;
|
|
double lambdai=0;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
for(m=1; m<=szm; m++)
|
|
{
|
|
for(n=1; n<=szn; n++)
|
|
{
|
|
//--- Prepare MxN matrix A, right part B, lambda
|
|
lambdai=CMath::RandomReal();
|
|
a=matrix<double>::Zeros(m,n);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
CSparse::SparseCreate(m,n,1,spa);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(spa,i,j,a.Get(i,j));
|
|
}
|
|
CSparse::SparseConvertToCRS(spa);
|
|
b=vector<double>::Zeros(m);
|
|
for(i=0; i<m; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Solve by calling LinLSQRIteration
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
|
|
CLinLSQR::LinLSQRSetPrecUnit(s);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
|
|
return(true);
|
|
//--- test LinLSQRRestart and LinLSQRSolveSparse
|
|
CLinLSQR::LinLSQRRestart(s);
|
|
CLinLSQR::LinLSQRSolveSparse(s,spa,b);
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
|
|
return(true);
|
|
}
|
|
}
|
|
if(!silent)
|
|
PrintResult("SVDTest",true);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test checks that algorithm can solve MxN (with N<=M) |
|
|
//| well-conditioned problems - and can do so within exactly N |
|
|
//| iterations. We use moderate condition numbers, from 1.0 to 10.0, |
|
|
//| because larger condition number may require several additional |
|
|
//| iterations to converge. |
|
|
//| We try different scalings of the A and B. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function does not outputs results |
|
|
//| to console |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::MWCRankSVDTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
double bnorm=0;
|
|
CRowDouble x0;
|
|
int szm=5;
|
|
int n=0;
|
|
int m=0;
|
|
int ns0=0;
|
|
int ns1=0;
|
|
int nlambdai=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
double lambdai=0;
|
|
double c=0;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
for(m=1; m<=szm; m++)
|
|
{
|
|
for(n=1; n<=m; n++)
|
|
{
|
|
for(nlambdai=0; nlambdai<=3; nlambdai++)
|
|
{
|
|
for(ns0=-1; ns0<=1; ns0++)
|
|
{
|
|
for(ns1=-1; ns1<=1; ns1++)
|
|
{
|
|
//--- Generate problem:
|
|
//--- * scale factors s0, s1
|
|
//--- * MxN well conditioned A (with condition number C in [1,10] and norm s0)
|
|
//--- * regularization coefficient LambdaI
|
|
//--- * right part b, with |b|=s1
|
|
s0=MathPow(10,10*ns0);
|
|
s1=MathPow(10,10*ns1);
|
|
lambdai=0;
|
|
switch(nlambdai)
|
|
{
|
|
case 0:
|
|
lambdai=0;
|
|
break;
|
|
case 1:
|
|
lambdai=s0/1000;
|
|
break;
|
|
case 2:
|
|
lambdai=s0;
|
|
break;
|
|
case 3:
|
|
lambdai=s0*1000;
|
|
break;
|
|
}
|
|
c=(10-1)*CMath::RandomReal()+1;
|
|
CMatGen::RMatrixRndCond(m,c,a);
|
|
for(j=0; j<n; j++)
|
|
a.Col(j,a.Col(j)*s0);
|
|
b=vector<double>::Zeros(m);
|
|
do
|
|
{
|
|
bnorm=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bnorm+=b[i]*b[i];
|
|
}
|
|
bnorm=MathSqrt(bnorm);
|
|
}
|
|
while(bnorm<=m_e0);
|
|
b*=s1/bnorm;
|
|
//--- Solve by LSQR method
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,n);
|
|
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test checks that algorithm can find a solution with minimum |
|
|
//| norm for a singular rectangular problem. System matrix has |
|
|
//| special property - singular values are either zero or well |
|
|
//| separated from zero. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::MWICRankSVDTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CSparseMatrix spa;
|
|
CRowDouble b;
|
|
double bnorm=0;
|
|
CRowDouble x0;
|
|
int szm=5;
|
|
int n=0;
|
|
int m=0;
|
|
int nz=0;
|
|
int ns0=0;
|
|
int ns1=0;
|
|
int nlambdai=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
double lambdai=0;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble a;
|
|
|
|
for(m=1; m<=szm; m++)
|
|
{
|
|
for(n=1; n<=m; n++)
|
|
{
|
|
for(nlambdai=0; nlambdai<=2; nlambdai++)
|
|
{
|
|
for(ns0=-1; ns0<=1; ns0++)
|
|
{
|
|
for(ns1=-1; ns1<=1; ns1++)
|
|
{
|
|
for(nz=0; nz<n; nz++)
|
|
{
|
|
//--- Generate problem:
|
|
//--- * scale coefficients s0, s1
|
|
//--- * regularization coefficient LambdaI
|
|
//--- * MxN matrix A, norm(A)=s0, with NZ zero singular values and N-NZ nonzero ones
|
|
//--- * right part b with norm(b)=s1
|
|
s0=MathPow(10,10*ns0);
|
|
s1=MathPow(10,10*ns1);
|
|
lambdai=0;
|
|
switch(nlambdai)
|
|
{
|
|
case 0:
|
|
lambdai=0;
|
|
break;
|
|
case 1:
|
|
lambdai=s0;
|
|
break;
|
|
case 2:
|
|
lambdai=s0*1000;
|
|
break;
|
|
}
|
|
a=matrix<double>::Zeros(m,n);
|
|
for(i=0; i<n-nz; i++)
|
|
a.Set(i,i,s0*(0.1+0.9*CMath::RandomReal()));
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(a,m,n);
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(a,m,n);
|
|
b=vector<double>::Zeros(m);
|
|
do
|
|
{
|
|
bnorm=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bnorm+=b[i]*b[i];
|
|
}
|
|
bnorm=MathSqrt(bnorm);
|
|
}
|
|
while(bnorm<=m_e0);
|
|
b*=s1/bnorm;
|
|
//--- Solve by LSQR method
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,m_emergencye0,m_emergencye0,n);
|
|
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
//--- Check
|
|
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test does check, that algorithm can find a solution with |
|
|
//| minimum norm, if a problem has bidiagonal matrix on diagonals of |
|
|
//| a lot of zeros. This problem has to lead to case when State. |
|
|
//| Alpha and State.Beta are zero, and we we can be sure that the |
|
|
//| algorithm correctly handles it. |
|
|
//| We do not use iteration count as stopping condition, because |
|
|
//| problem can be degenerate and we may need more than N iterations |
|
|
//| to converge. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::BiDiagonalTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
double bnorm=0;
|
|
CRowDouble x0;
|
|
int sz=5;
|
|
int n=0;
|
|
int m=0;
|
|
int minmn=0;
|
|
int ns0=0;
|
|
int ns1=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
int i=0;
|
|
int j=0;
|
|
int p=0;
|
|
int diag=0;
|
|
double pz=0;
|
|
|
|
for(m=1; m<=sz; m++)
|
|
{
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
minmn=MathMin(m,n);
|
|
for(p=0; p<=2; p++)
|
|
{
|
|
for(ns0=-1; ns0<=1; ns0++)
|
|
{
|
|
for(ns1=-1; ns1<=1; ns1++)
|
|
{
|
|
for(diag=0; diag<=1; diag++)
|
|
{
|
|
//--- Generate problem:
|
|
//--- * scaling coefficients s0, s1
|
|
//--- * bidiagonal A, with probability of having zero element at diagonal equal to PZ
|
|
s0=MathPow(10,10*ns0);
|
|
s1=MathPow(10,10*ns1);
|
|
pz=(p+1)*0.25;
|
|
a=matrix<double>::Zeros(m,n);
|
|
for(i=0; i<minmn; i++)
|
|
{
|
|
if(CMath::RandomReal()>=pz)
|
|
a.Set(i,i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=1; i<minmn; i++)
|
|
{
|
|
if(CMath::RandomReal()>=pz)
|
|
{
|
|
if(diag==0)
|
|
a.Set(i-1,i,2*CMath::RandomReal()-1);
|
|
if(diag==1)
|
|
a.Set(i,i-1,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
a*=s0;
|
|
b=vector<double>::Zeros(m);
|
|
do
|
|
{
|
|
bnorm=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bnorm+=b[i]*b[i];
|
|
}
|
|
bnorm=MathSqrt(bnorm);
|
|
}
|
|
while(bnorm<=m_e0);
|
|
b*=s1/bnorm;
|
|
//--- LSQR solution
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,m_e0,m_e0,0);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
//--- Check
|
|
if(!IsItGoodSolution(a,b,m,n,0.0,x0,m_e0,m_tolort))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test does check, that algorithm correctly solves a problem in|
|
|
//| cases: |
|
|
//| 1. A=0, B<>0; |
|
|
//| 2. A<>0, B=0; |
|
|
//| 3. A=0, B=0. |
|
|
//| If some part is not zero then it filled with ones. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::ZeroMatrixTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
int sz=5;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int nzeropart=0;
|
|
|
|
for(m=1; m<=sz; m++)
|
|
{
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
for(nzeropart=0; nzeropart<=2; nzeropart++)
|
|
{
|
|
//--- Initialize A, b
|
|
if(nzeropart==0 || nzeropart==2)
|
|
a=matrix<double>::Zeros(m,n);
|
|
else
|
|
a=matrix<double>::Ones(m,n);
|
|
if(nzeropart==1 || nzeropart==2)
|
|
b=vector<double>::Zeros(m);
|
|
else
|
|
b=vector<double>::Ones(m);
|
|
//--- LSQR
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,n);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
//--- Check
|
|
if(!IsItGoodSolution(a,b,m,n,0.0,x0,m_e0,m_tolort))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test does check, that algorithm correctly displays a progress|
|
|
//| report. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::ReportCorrectnessTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
double eps=0.001;
|
|
int sz=5;
|
|
double mn=-100;
|
|
double mx=100;
|
|
double c=100;
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CMatrixDouble u;
|
|
CMatrixDouble v;
|
|
CRowDouble w;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
CRowDouble firstx;
|
|
CRowDouble lastx;
|
|
double rnorm=0;
|
|
double tnorm=0;
|
|
int n=0;
|
|
int m=0;
|
|
int lambdai=0;
|
|
int i=0;
|
|
int j=0;
|
|
int its=0;
|
|
double tmp=0;
|
|
|
|
for(m=1; m<=sz; m++)
|
|
{
|
|
for(n=1; n<=m; n++)
|
|
{
|
|
for(lambdai=0; lambdai<=1; lambdai++)
|
|
{
|
|
its=-1;
|
|
//--- initialize matrix A
|
|
CMatGen::SPDMatrixRndCond(m+n,c,a);
|
|
for(i=m; i<m+n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i-m==j)
|
|
a.Set(i,j,lambdai);
|
|
else
|
|
a.Set(i,j,0);
|
|
}
|
|
}
|
|
//--- initialize b
|
|
b=vector<double>::Zeros(m+n);
|
|
rnorm=0;
|
|
for(i=0; i<m+n; i++)
|
|
{
|
|
if(i<m)
|
|
{
|
|
b.Set(i,(mx-mn)*CMath::RandomReal()+mn);
|
|
rnorm+=b[i]*b[i];
|
|
}
|
|
else
|
|
b.Set(i,0);
|
|
}
|
|
//--- initialize FirstX and LastX
|
|
firstx=vector<double>::Zeros(n);
|
|
lastx=vector<double>::Zeros(n);
|
|
//--- calculating by LSQR method
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,n);
|
|
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
|
|
CLinLSQR::LinLSQRSetXRep(s,true);
|
|
CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=0,"testlinlsqrunit.ap:811");
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
if(s.m_xupdated)
|
|
{
|
|
tnorm=0;
|
|
for(i=0; i<=m+n-1; i++)
|
|
{
|
|
tmp=CAblasF::RDotVR(n,s.m_x,a,i);
|
|
tnorm+=CMath::Sqr(b[i]-tmp);
|
|
}
|
|
//--- check, that RNorm is't more than S.R2
|
|
//--- and difference between S.R2 and TNorm
|
|
//--- is't more than 'eps'(here S.R2=||rk||,
|
|
//--- calculated by the algorithm for LSQR, and
|
|
//--- TNorm=||A*S.m_x-b||, calculated by test function).
|
|
if(s.m_r2>(rnorm+1000*CMath::m_machineepsilon*MathMax(rnorm,1)))
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:853");
|
|
return(result);
|
|
}
|
|
if(MathAbs(s.m_r2-tnorm)>eps)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:858");
|
|
return(result);
|
|
}
|
|
rnorm=s.m_r2;
|
|
its++;
|
|
//get X value from first iteration
|
|
//--- and from last iteration.
|
|
if(its==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
firstx.Set(i,s.m_x[i]);
|
|
}
|
|
if(its==n)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
lastx.Set(i,s.m_x[i]);
|
|
}
|
|
//--- check iterations counter
|
|
CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=its,"testlinlsqrunit.ap:878");
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=n,"testlinlsqrunit.ap:882");
|
|
//--- check, that FirstX is equal to zero and LastX
|
|
//--- is equal to x0.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(firstx[i]!=0.0 || lastx[i]!=x0[i])
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("ReportCorrectnessTest",false);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
Print("X and LastX...");
|
|
for(j=0; j<n; j++)
|
|
PrintFormat("x[%d]=%0.10E; LastX[%d]=%0.10E\n",j,x0[j],j,lastx[j]);
|
|
}
|
|
CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:902");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!silent)
|
|
PrintResult("ReportCorrectnessTest",true);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test does check, that correctly executed Stop criteria by |
|
|
//| algorithm. |
|
|
//| INPUT: |
|
|
//| Silent - if true then function output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::StopPingCriteriaTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int sz=5;
|
|
double mn=-100;
|
|
double mx=100;
|
|
double c=100;
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CMatrixDouble u;
|
|
CMatrixDouble v;
|
|
CRowDouble w;
|
|
CRowDouble b;
|
|
double bnorm=0;
|
|
CRowDouble x0;
|
|
int n=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
CRowDouble ark;
|
|
double anorm=0;
|
|
double arknorm=0;
|
|
double rknorm=0;
|
|
CRowDouble rk;
|
|
int maxits=0;
|
|
int i=0;
|
|
int j=0;
|
|
double tmp=0;
|
|
double eps=0;
|
|
double epsmod=0;
|
|
int i_=0;
|
|
|
|
for(n=1; n<=sz; n++)
|
|
{
|
|
//--- Initialize A, unit b
|
|
CMatGen::SPDMatrixRndCond(n,c,a);
|
|
b=vector<double>::Zeros(n);
|
|
bnorm=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,(mx-mn)*CMath::RandomReal()+mn);
|
|
bnorm+=b[i]*b[i];
|
|
}
|
|
bnorm=MathSqrt(bnorm);
|
|
//--- Test MaxIts
|
|
//--- NOTE: we do not check TerminationType because algorithm may terminate for
|
|
//--- several reasons. The only thing which is guaranteed is that stopping condition
|
|
//--- MaxIts holds.
|
|
maxits=1+CMath::RandomInteger(n);
|
|
CLinLSQR::LinLSQRCreate(n,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,maxits);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(rep.m_iterationscount>maxits || rep.m_terminationtype<=0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("StoppingCriteriaTestn",false);
|
|
PrintFormat("N=%d\n",n);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Test EpsB.
|
|
//--- Set EpsB=eps, check that |r|<epsMod*|b|, where epsMod=1.1*eps.
|
|
//--- This modified epsilon is used to avoid influence of the numerical errors.
|
|
//--- NOTE: we do not check TerminationType because algorithm may terminate for
|
|
//--- several reasons. The only thing which is guaranteed is that stopping condition
|
|
//--- EpsB holds.
|
|
eps=MathPow(10,-(2+CMath::RandomInteger(3)));
|
|
epsmod=1.1*eps;
|
|
rk=vector<double>::Zeros(n);
|
|
CLinLSQR::LinLSQRCreate(n,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,eps,0);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
rknorm=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tmp=CAblasF::RDotVR(n,x0,a,i);
|
|
rknorm+=CMath::Sqr(tmp-b[i]);
|
|
}
|
|
rknorm=MathSqrt(rknorm);
|
|
if(rknorm>(epsmod*bnorm) || rep.m_terminationtype<=0)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("StoppingCriteriaTest",false);
|
|
PrintFormat("rkNorm=%0.2E\n",rknorm);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Test EpsA.
|
|
//--- Generate well conditioned underdetermined system with nonzero residual
|
|
//--- at the solution. Such system can be generated by generating random
|
|
//--- orthogonal matrix (N>=2) and using first N-1 columns as rectangular
|
|
//--- system matrix, and sum of all columns with random non-zero coefficients
|
|
//--- as right part.
|
|
for(n=2; n<=sz; n++)
|
|
{
|
|
for(k0=-1; k0<=1; k0++)
|
|
{
|
|
for(k1=-1; k1<=1; k1++)
|
|
{
|
|
//--- Initialize A with non-unit norm 10^(10*K0), b with non-unit norm 10^(10*K1)
|
|
anorm=MathPow(10,10*k0);
|
|
CMatGen::RMatrixRndOrthogonal(n,a);
|
|
a*=anorm;
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tmp=1+CMath::RandomReal();
|
|
for(i_=0; i_<n; i_++)
|
|
b.Add(i_,tmp*a.Get(i_,i));
|
|
}
|
|
tmp=CAblasF::RDotV2(n,b);
|
|
tmp=MathPow(10,10*k1)/MathSqrt(tmp);
|
|
b*=tmp;
|
|
//--- Test EpsA
|
|
//--- NOTE: it is guaranteed that algorithm will terminate with correct
|
|
//--- TerminationType because other stopping criteria (EpsB) won't be satisfied
|
|
//--- on such system.
|
|
eps=MathPow(10,-(2+CMath::RandomInteger(3)));
|
|
epsmod=1.1*eps;
|
|
CLinLSQR::LinLSQRCreate(n,n-1,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,eps,0,0);
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n-1,s.m_x,a,i));
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n-1; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
//--- Check condition
|
|
rk=b;
|
|
ark=vector<double>::Zeros(n-1);
|
|
rknorm=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
rk.Add(i,-CAblasF::RDotVR(n-1,x0,a,i));
|
|
rknorm+=CMath::Sqr(rk[i]);
|
|
}
|
|
rknorm=MathSqrt(rknorm);
|
|
arknorm=0;
|
|
for(i=0; i<n-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ark.Add(i,a.Get(j,i)*rk[j]);
|
|
arknorm+=CMath::Sqr(ark[i]);
|
|
}
|
|
arknorm=MathSqrt(arknorm);
|
|
if((arknorm/(anorm*rknorm))>(epsmod) || rep.m_terminationtype!=4)
|
|
{
|
|
if(!silent)
|
|
{
|
|
PrintResult("StoppingCriteriaTestn",false);
|
|
PrintFormat("N=%d\n",n);
|
|
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
|
|
PrintFormat("NMV=%d\n",rep.m_nmv);
|
|
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
|
|
}
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!silent)
|
|
PrintResult("StoppingCriteriaTest",true);
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This test compares LSQR for original system A*x=b against |
|
|
//| CG for a modified system (A'*A)x = A*b. Both algorithms should |
|
|
//| give same sequences of trial points (under exact arithmetics, or|
|
|
//| for very good conditioned systems). |
|
|
//| INPUT: |
|
|
//| Silent - if true then function does not output report |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::AnalyticTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CMatrixDouble xk;
|
|
CMatrixDouble ap;
|
|
CMatrixDouble r;
|
|
CRowDouble b;
|
|
CRowDouble tmp;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int smallk=4;
|
|
int pointsstored=0;
|
|
double v=0;
|
|
double tol=1.0E-7;
|
|
int i_=0;
|
|
//--- Set:
|
|
//--- * SmallK - number of steps to check, must be small number in order
|
|
//--- to reduce influence of the rounding errors
|
|
//--- * Tol - error tolerance for orthogonality/conjugacy criteria
|
|
for(m=smallk; m<=smallk+5; m++)
|
|
{
|
|
for(n=smallk; n<=m; n++)
|
|
{
|
|
//--- Prepare problem:
|
|
//--- * MxN matrix A, Mx1 vector B
|
|
//--- * A is filled with random values from [-1,+1]
|
|
//--- * diagonal elements are filled with large positive values
|
|
//--- (should make system better conditioned)
|
|
a=matrix<double>::Zeros(m,n);
|
|
b=vector<double>::Zeros(m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,10*(1+CMath::RandomReal()));
|
|
//--- Solve with LSQR, save trial points into XK[] array
|
|
xk=matrix<double>::Zeros(smallk+1,n);
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,smallk);
|
|
CLinLSQR::LinLSQRSetXRep(s,true);
|
|
pointsstored=0;
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
}
|
|
if(s.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(pointsstored<CAp::Rows(xk),"LinLSQR test: internal error"))
|
|
return(false);
|
|
xk.Row(pointsstored,s.m_x);
|
|
pointsstored++;
|
|
}
|
|
}
|
|
if(pointsstored<3)
|
|
{
|
|
//--- At least two iterations should be performed
|
|
//--- (our task is not that easy to solve)
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- We have recorded sequence of points generated by LSQR,
|
|
//--- and now we want to make a comparion against linear CG.
|
|
//--- Of course, we could just perform CG solution of (A'*A)*x = A'*b,
|
|
//--- but it will need a CG solver, and we do not want to reference one
|
|
//--- just to perform testing.
|
|
//--- However, we can do better - we can check that sequence of steps
|
|
//--- satisfies orthogonality/conjugacy conditions, which are stated
|
|
//--- as follows:
|
|
//--- * (r[i]^T)*r[j]=0 for i<>j
|
|
//--- * (p[i]^T)*A'*A*p[j]=0 for i<>j
|
|
//--- where r[i]=(A'*A)*x[i]-A'*b is I-th residual , p[i] is I-th step.
|
|
//--- In order to test these criteria we generate two matrices:
|
|
//--- * (PointsStored-1)*M matrix AP (matrix of A*p products)
|
|
//--- * (PointsStored-1)*N matrix R (matrix of residuals)
|
|
//--- Then, we check that each matrix has orthogonal rows.
|
|
ap=matrix<double>::Zeros(pointsstored-1,m);
|
|
r=matrix<double>::Zeros(pointsstored-1,n);
|
|
tmp=vector<double>::Zeros(m);
|
|
for(k=0; k<pointsstored-1; k++)
|
|
{
|
|
//--- Calculate K-th row of AP
|
|
for(i=0; i<m; i++)
|
|
{
|
|
ap.Set(k,i,0.0);
|
|
for(j=0; j<n; j++)
|
|
ap.Add(k,i,a.Get(i,j)*(xk.Get(k+1,j)-xk.Get(k,j)));
|
|
}
|
|
//--- Calculate K-th row of R
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=CAblasF::RDotRR(n,a,i,xk,k);
|
|
tmp.Set(i,v-b[i]);
|
|
}
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<m; i_++)
|
|
v+=a.Get(i_,j)*tmp[i_];
|
|
r.Set(k,j,v);
|
|
}
|
|
}
|
|
for(i=0; i<pointsstored-1; i++)
|
|
{
|
|
for(j=0; j<pointsstored-1; j++)
|
|
{
|
|
if(i!=j)
|
|
{
|
|
v=CAblasF::RDotRR(m,ap,i,ap,j);
|
|
result=result||MathAbs(v)>tol;
|
|
v=CAblasF::RDotRR(n,r,i,r,j);
|
|
result=result||MathAbs(v)>tol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This test checks behavior of the termination requests. Sets error|
|
|
//| flag on failure, leaves it unchanged on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestLinLSQRUnit::TestTerminationRequests(bool &err)
|
|
{
|
|
//--- create variables
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CHighQualityRandState rs;
|
|
int pass=0;
|
|
CMatrixDouble a;
|
|
CRowDouble b;
|
|
CRowDouble x1;
|
|
int n=0;
|
|
int m=0;
|
|
int callsleft=0;
|
|
int reportsafterrequest=0;
|
|
bool firstpointreported;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
//--- Prepare problem
|
|
callsleft=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
n=callsleft+100+CHighQualityRand::HQRndUniformI(rs,50);
|
|
m=n+CHighQualityRand::HQRndUniformI(rs,50);
|
|
a=matrix<double>::Zeros(m,n);
|
|
b=vector<double>::Zeros(m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Solve with LSQR, terminate after specified amount of A*x/A^T*x requests.
|
|
CLinLSQR::LinLSQRCreate(m,n,s);
|
|
CLinLSQR::LinLSQRSetB(s,b);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,n);
|
|
CLinLSQR::LinLSQRSetXRep(s,true);
|
|
firstpointreported=false;
|
|
reportsafterrequest=0;
|
|
while(CLinLSQR::LinLSQRIteration(s))
|
|
{
|
|
if(s.m_needmv)
|
|
{
|
|
for(i=0; i<m; i++)
|
|
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
|
|
if(firstpointreported)
|
|
callsleft--;
|
|
if(callsleft==0)
|
|
CLinLSQR::LinLSQRRequestTermination(s);
|
|
}
|
|
if(s.m_needmtv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_mtv.Set(i,0);
|
|
for(j=0; j<m; j++)
|
|
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
|
|
}
|
|
if(firstpointreported)
|
|
callsleft--;
|
|
if(callsleft==0)
|
|
CLinLSQR::LinLSQRRequestTermination(s);
|
|
}
|
|
if(s.m_xupdated)
|
|
{
|
|
firstpointreported=true;
|
|
if(callsleft<=0)
|
|
reportsafterrequest++;
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRResults(s,x1,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testlinlsqrunit.ap:1460");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(x1,n),"testlinlsqrunit.ap:1461");
|
|
CAp::SetErrorFlag(err,reportsafterrequest!=1,"testlinlsqrunit.ap:1462");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function compares solution calculated by LSQR with one |
|
|
//| calculated by SVD solver. Following comparisons are performed: |
|
|
//| 1. either: |
|
|
//| 1.a) residual norm |Rk| for LSQR solution is at most |
|
|
//| epsErr*|B| |
|
|
//| 1.b) |A^T*Rk|/(|A|*|Rk|)<=EpsOrt |
|
|
//| 2. norm(LSQR_solution) is at most 1.2*norm(SVD_solution) |
|
|
//| Test(1) verifies that LSQR found good solution, test(2) verifies |
|
|
//| that LSQR finds solution with close-to-minimum norm. We use |
|
|
//| factor as large as 1.2 to test deviation from SVD solution |
|
|
//| because LSQR is not very good at solving degenerate problems. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - array[M,N], system matrix |
|
|
//| B - right part |
|
|
//| M, N - problem size |
|
|
//| LambdaV - regularization value for the problem, >=0 |
|
|
//| X - array[N], solution found by LSQR |
|
|
//| EpsErr - tolerance for |A*x-b| |
|
|
//| EpsOrt - tolerance for |A^T*Rk|/(|A|*|Rk|) |
|
|
//| RESULT |
|
|
//| True, for solution which passess all the tests |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::IsItGoodSolution(CMatrixDouble &a,
|
|
CRowDouble &b,int m,int n,
|
|
double lambdav,
|
|
CRowDouble &x,
|
|
double epserr,double epsort)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CMatrixDouble svda;
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
CRowDouble w;
|
|
CRowDouble svdx;
|
|
CRowDouble tmparr;
|
|
CRowDouble r;
|
|
int i=0;
|
|
int j=0;
|
|
int minmn=0;
|
|
bool svdresult;
|
|
double v=0;
|
|
double rnorm=0;
|
|
double bnorm=0;
|
|
double anorm=0;
|
|
double atrnorm=0;
|
|
double xnorm=0;
|
|
double svdxnorm=0;
|
|
bool clause1holds;
|
|
bool clause2holds;
|
|
int i_=0;
|
|
//--- Solve regularized problem with SVD solver
|
|
svda=a;
|
|
svda.Resize(m+n,n);
|
|
for(i=m; i<=m+n-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i-m==j)
|
|
svda.Set(i,j,lambdav);
|
|
else
|
|
svda.Set(i,j,0);
|
|
}
|
|
}
|
|
svdresult=CSingValueDecompose::RMatrixSVD(svda,m+n,n,1,1,0,w,u,vt);
|
|
//--- check
|
|
if(!CAp::Assert(svdresult,"LINLSQR: internal error in unit test (SVD failed)"))
|
|
return(false);
|
|
minmn=MathMin(m,n);
|
|
svdx=vector<double>::Zeros(n);
|
|
tmparr=vector<double>::Zeros(minmn);
|
|
for(i=0; i<minmn; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
tmparr.Add(i,u.Get(j,i)*b[j]);
|
|
if(w[i]<=(MathSqrt(CMath::m_machineepsilon)*w[0]))
|
|
tmparr.Set(i,0);
|
|
else
|
|
tmparr.Mul(i,1.0/w[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
svdx.Set(i,0);
|
|
for(j=0; j<minmn; j++)
|
|
svdx.Add(i,vt.Get(j,i)*tmparr[j]);
|
|
}
|
|
//--- Calculate residual, perform checks 1.a and 1.b:
|
|
//--- * first, we check 1.a
|
|
//--- * in case 1.a fails we check 1.b
|
|
r=vector<double>::Zeros(m+n);
|
|
for(i=0; i<m+n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x,svda,i);
|
|
r.Set(i,v);
|
|
if(i<m)
|
|
r.Add(i,-b[i]);
|
|
}
|
|
v=CAblasF::RDotV2(m+n,r);
|
|
rnorm=MathSqrt(v);
|
|
v=CAblasF::RDotV2(m,b);
|
|
bnorm=MathSqrt(v);
|
|
if(rnorm<=(epserr*bnorm))
|
|
{
|
|
//--- 1.a is true, no further checks is needed
|
|
clause1holds=true;
|
|
}
|
|
else
|
|
{
|
|
//--- 1.a is false, we have to check 1.b
|
|
//--- In order to do so, we calculate ||A|| and ||A^T*Rk||. We do
|
|
//--- not store product of A and Rk to some array, all we need is
|
|
//--- just one component of product at time, stored in V.
|
|
anorm=0;
|
|
atrnorm=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<m+n; j++)
|
|
{
|
|
v+=svda.Get(j,i)*r[j];
|
|
anorm+=CMath::Sqr(svda.Get(j,i));
|
|
}
|
|
atrnorm+=CMath::Sqr(v);
|
|
}
|
|
anorm=MathSqrt(anorm);
|
|
atrnorm=MathSqrt(atrnorm);
|
|
clause1holds=(anorm*rnorm)==0.0||(atrnorm/(anorm*rnorm))<=(epsort);
|
|
}
|
|
//--- Check (2).
|
|
//--- Here we assume that Result=True when we enter this block.
|
|
v=CAblasF::RDotV2(n,x);
|
|
xnorm=MathSqrt(v);
|
|
v=CAblasF::RDotV2(n,svdx);
|
|
svdxnorm=MathSqrt(v);
|
|
clause2holds=xnorm<=(1.2*svdxnorm);
|
|
//--- End
|
|
result=clause1holds && clause2holds;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing preconditioned LSQR method. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestLinLSQRUnit::PreconditionerTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CLinLSQRState s;
|
|
CLinLSQRReport rep;
|
|
CMatrixDouble a;
|
|
CMatrixDouble ta;
|
|
CSparseMatrix sa;
|
|
CRowDouble b;
|
|
CRowDouble d;
|
|
CRowDouble xe;
|
|
CRowDouble x0;
|
|
bool bflag;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
//--- Test 1.
|
|
//--- We test automatic diagonal preconditioning used by SolveSparse.
|
|
//--- In order to do so we:
|
|
//--- 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
|
|
//--- 2. generate random "exact" solution xe and right part b=A0*xe
|
|
//--- 3. generate random ill-conditioned diagonal scaling matrix D with
|
|
//--- condition number equal to 1.0E50:
|
|
//--- 4. transform A*x=b into badly scaled problem:
|
|
//--- A0*x0=b0
|
|
//--- (A0*D)*(inv(D)*x0)=b0
|
|
//--- finally we got new problem A*x=b with A=A0*D, b=b0, x=inv(D)*x0
|
|
//--- Then we solve A*x=b:
|
|
//--- 1. with default preconditioner
|
|
//--- 2. with explicitly activayed diagonal preconditioning
|
|
//--- 3. with unit preconditioner.
|
|
//--- 1st and 2nd solutions must be close to xe, 3rd solution must be very
|
|
//--- far from the true one.
|
|
n=20;
|
|
CMatGen::RMatrixRndCond(n,1.0E1,ta);
|
|
xe=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xe.Set(i,CApServ::RandomNormal());
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,CAblasF::RDotVR(n,xe,ta,i));
|
|
d=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
d.Set(i,MathPow(10,100*CMath::RandomReal()-50));
|
|
a=matrix<double>::Zeros(n,n);
|
|
CSparse::SparseCreate(n,n,n*n,sa);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,ta.Get(i,j)*d[j]);
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
}
|
|
xe.Set(i,xe[i]/d[i]);
|
|
}
|
|
CSparse::SparseConvertToCRS(sa);
|
|
CLinLSQR::LinLSQRCreate(n,n,s);
|
|
CLinLSQR::LinLSQRSetCond(s,0,0,2*n);
|
|
CLinLSQR::LinLSQRSolveSparse(s,sa,b);
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRSetPrecUnit(s);
|
|
CLinLSQR::LinLSQRSolveSparse(s,sa,b);
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
bflag=false;
|
|
for(i=0; i<n; i++)
|
|
bflag=bflag||MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]);
|
|
if(!bflag)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
CLinLSQR::LinLSQRSetPrecDiag(s);
|
|
CLinLSQR::LinLSQRSolveSparse(s,sa,b);
|
|
CLinLSQR::LinLSQRResults(s,x0,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- test has been passed
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestOptservUnit
|
|
{
|
|
public:
|
|
static bool TestOptserv(bool silent);
|
|
|
|
private:
|
|
static void TestPrec(bool &waserrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestOptservUnit::TestOptserv(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool precerrors;
|
|
bool waserrors;
|
|
//--- initialization
|
|
precerrors=false;
|
|
TestPrec(precerrors);
|
|
//--- report
|
|
waserrors=precerrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING OPTSERV");
|
|
Print("TESTS:");
|
|
PrintResult("* PRECONDITIONERS",!precerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function checks preconditioning functions |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestOptservUnit::TestPrec(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int k=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i0=0;
|
|
int j0=0;
|
|
int j1=0;
|
|
double v=0;
|
|
double rho=0;
|
|
double theta=0;
|
|
double tolg=0;
|
|
CMatrixDouble va;
|
|
CRowDouble vc;
|
|
CRowDouble vd;
|
|
CRowDouble vb;
|
|
CRowDouble s0;
|
|
CRowDouble s1;
|
|
CRowDouble s2;
|
|
CRowDouble g;
|
|
CPrecBufLBFGS buf;
|
|
CPrecBufLowRank lowrankbuf;
|
|
CRowDouble norms;
|
|
CMatrixDouble sk;
|
|
CMatrixDouble yk;
|
|
CMatrixDouble bk;
|
|
CRowDouble bksk;
|
|
CRowDouble tmp;
|
|
CMatInvReport rep;
|
|
CHighQualityRandState rs;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test for inexact L-BFGS preconditioner.
|
|
//--- We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
|
|
//--- Different K's, from 0 to N, are tried. We test preconditioner
|
|
//--- code which uses compact L-BFGS update against reference implementation
|
|
//--- which uses non-compact BFGS scheme.
|
|
//--- For each K we perform two tests: first for KxN non-zero matrix V,
|
|
//--- second one for NxN matrix V with last N-K rows set to zero. Last test
|
|
//--- checks algorithm's ability to handle zero updates.
|
|
tolg=1.0E-9;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(k=0; k<=n; k++)
|
|
{
|
|
//--- Prepare problem:
|
|
//--- * VD, VC, VA, with VC/VA reordered by ascending of VC[i]*norm(VA[i,...])^2
|
|
//--- * trial vector S (copies are stored to S0,S1,S2)
|
|
vd=vector<double>::Zeros(n);
|
|
s0=vector<double>::Zeros(n);
|
|
s1=vector<double>::Zeros(n);
|
|
s2=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vd.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
|
|
s0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
s1.Set(i,s0[i]);
|
|
s2.Set(i,s0[i]);
|
|
}
|
|
CMatGen::RMatrixRndCond(n,1.0E2,va);
|
|
CApServ::RVectorSetLengthAtLeast(vc,n);
|
|
for(i=0; i<k; i++)
|
|
vc.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
|
|
for(i=k; i<n; i++)
|
|
{
|
|
vc.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
va.Set(i,j,0.0);
|
|
}
|
|
norms=vector<double>::Zeros(k);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotRR(n,va,i,va,i);
|
|
norms.Set(i,v*vc[i]);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<=k-2; j++)
|
|
{
|
|
if(norms[j]>norms[j+1])
|
|
{
|
|
//--- Swap elements J and J+1
|
|
norms.Swap(j,j+1);
|
|
vc.Swap(j,j+1);
|
|
va.SwapRows(j,j+1);
|
|
}
|
|
}
|
|
}
|
|
//--- Generate reference model and apply it to S2:
|
|
//--- * generate approximate Hessian Bk
|
|
//--- * calculate inv(Bk)
|
|
//--- * calculate inv(Bk)*S2, store to S2
|
|
CApServ::RMatrixSetLengthAtLeast(sk,k,n);
|
|
CApServ::RMatrixSetLengthAtLeast(yk,k,n);
|
|
bk=matrix<double>::Zeros(n,n);
|
|
bksk=vector<double>::Zeros(n);
|
|
tmp=vector<double>::Zeros(n);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
sk.Row(i,va[i]+0);
|
|
v=CAblasF::RDotRR(n,sk,i,va,i)*vc[i];
|
|
for(j=0; j<n; j++)
|
|
yk.Set(i,j,vd[j]*sk.Get(i,j)+va.Get(i,j)*v);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i==j)
|
|
bk.Set(i,i,vd[i]);
|
|
else
|
|
bk.Set(j,i,0.0);
|
|
}
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
theta=0.0;
|
|
for(j0=0; j0<n; j0++)
|
|
{
|
|
bksk.Set(j0,0);
|
|
for(j1=0; j1<n; j1++)
|
|
{
|
|
theta+=sk.Get(i,j0)*bk.Get(j0,j1)*sk.Get(i,j1);
|
|
bksk.Add(j0,bk.Get(j0,j1)*sk.Get(i,j1));
|
|
}
|
|
}
|
|
theta=1/theta;
|
|
rho=CAblasF::RDotRR(n,sk,i,yk,i);
|
|
rho=1/rho;
|
|
for(j0=0; j0<n; j0++)
|
|
{
|
|
for(j1=0; j1<n; j1++)
|
|
bk.Add(j0,j1,rho*yk.Get(i,j0)*yk.Get(i,j1));
|
|
}
|
|
for(j0=0; j0<n; j0++)
|
|
{
|
|
for(j1=0; j1<n; j1++)
|
|
bk.Add(j0,j1,-theta*bksk[j0]*bksk[j1]);
|
|
}
|
|
}
|
|
CMatInv::RMatrixInverse(bk,n,j0,rep);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,s2,bk,i);
|
|
tmp.Set(i,v);
|
|
}
|
|
s2=tmp;
|
|
//--- First test for non-zero V:
|
|
//--- * apply preconditioner to X0
|
|
//--- * compare reference model against implementation being tested
|
|
COptServ::InexactLBFGSPreconditioner(s0,n,vd,vc,va,k,buf);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s0[i])>tolg,"testoptservunit.ap:236");
|
|
//--- Second test - N-K zero rows appended to V and rows are
|
|
//--- randomly reordered. Doing so should not change result,
|
|
//--- algorithm must be able to order rows according to second derivative
|
|
//--- and skip zero updates.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
i0=i+CHighQualityRand::HQRndUniformI(rs,n-i);
|
|
vc.Swap(i,i0);
|
|
va.SwapRows(i,i0);
|
|
}
|
|
COptServ::InexactLBFGSPreconditioner(s1,n,vd,vc,va,n,buf);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s1[i])>tolg,"testoptservunit.ap:259");
|
|
}
|
|
}
|
|
//--- Test for exact low-rank preconditioner.
|
|
//--- We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
|
|
//--- Different K's, from 0 to N, are tried. We test preconditioner
|
|
//--- code which uses Woodbury update against reference implementation
|
|
//--- which performs straightforward matrix inversion.
|
|
//--- For each K we perform two tests: first for KxN non-zero matrix V,
|
|
//--- second one for NxN matrix V with randomly appended N-K zero rows.
|
|
//--- Last test checks algorithm's ability to handle zero updates.
|
|
tolg=1.0E-9;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(k=0; k<=n; k++)
|
|
{
|
|
//--- Prepare problem:
|
|
//--- * VD, VC, VA
|
|
//--- * trial vector S (copies are stored to S0,S1,S2)
|
|
vd=vector<double>::Zeros(n);
|
|
s0=vector<double>::Zeros(n);
|
|
s1=vector<double>::Zeros(n);
|
|
s2=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vd.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
|
|
s0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
s1.Set(i,s0[i]);
|
|
s2.Set(i,s0[i]);
|
|
}
|
|
CMatGen::RMatrixRndCond(n,1.0E2,va);
|
|
CApServ::RVectorSetLengthAtLeast(vc,n);
|
|
for(i=0; i<k; i++)
|
|
vc.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
|
|
for(i=k; i<n; i++)
|
|
{
|
|
vc.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
va.Set(j,i,0.0);
|
|
}
|
|
//--- Generate reference model and apply it to S2
|
|
bk=matrix<double>::Zeros(n,n);
|
|
tmp=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(i==j)
|
|
v=vd[i];
|
|
else
|
|
v=0.0;
|
|
for(j1=0; j1<k; j1++)
|
|
{
|
|
v+=va.Get(j1,i)*vc[j1]*va.Get(j1,j);
|
|
}
|
|
bk.Set(j,i,v);
|
|
}
|
|
}
|
|
CMatInv::RMatrixInverse(bk,n,j,rep);
|
|
//--- check
|
|
if(!CAp::Assert(j>0))
|
|
{
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,s2,bk,i);
|
|
tmp.Set(i,v);
|
|
}
|
|
s2=tmp;
|
|
//--- First test for non-zero V:
|
|
//--- * apply preconditioner to X0
|
|
//--- * compare reference model against implementation being tested
|
|
COptServ::PrepareLowRankPreconditioner(vd,vc,va,n,k,lowrankbuf);
|
|
COptServ::ApplyLowRankPreconditioner(s0,lowrankbuf);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s0[i])>tolg,"testoptservunit.ap:341");
|
|
//--- Second test - N-K zero rows appended to V and rows are
|
|
//--- randomly reordered. Doing so should not change result,
|
|
//--- algorithm must be able to order rows according to second derivative
|
|
//--- and skip zero updates.
|
|
//
|
|
for(i=0; i<n; i++)
|
|
{
|
|
i0=i+CHighQualityRand::HQRndUniformI(rs,n-i);
|
|
vc.Swap(i,i0);
|
|
va.SwapRows(i,i0);
|
|
}
|
|
COptServ::PrepareLowRankPreconditioner(vd,vc,va,n,n,lowrankbuf);
|
|
COptServ::ApplyLowRankPreconditioner(s1,lowrankbuf);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s1[i])>tolg,"testoptservunit.ap:365");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestCQModelsUnit
|
|
{
|
|
public:
|
|
static bool TestCQModels(bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestCQModelsUnit::TestCQModels(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool eval0errors;
|
|
bool eval1errors;
|
|
bool eval2errors;
|
|
bool newton0errors;
|
|
bool newton1errors;
|
|
bool newton2errors;
|
|
bool waserrors;
|
|
CConvexQuadraticModel s;
|
|
int nkind=0;
|
|
int kmax=0;
|
|
int n=0;
|
|
int k=0;
|
|
int i=0;
|
|
int pass=0;
|
|
int j=0;
|
|
double alpha=0;
|
|
double theta=0;
|
|
double tau=0;
|
|
double v=0;
|
|
double v2=0;
|
|
double h=0;
|
|
double f0=0;
|
|
double mkind=0;
|
|
double xtadx2=0;
|
|
double noise=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble q;
|
|
CRowDouble b;
|
|
CRowDouble r;
|
|
CRowDouble x;
|
|
CRowDouble x0;
|
|
CRowDouble xc;
|
|
CRowDouble d;
|
|
CRowDouble ge;
|
|
CRowDouble gt;
|
|
CRowDouble tmp0;
|
|
CRowDouble adx;
|
|
CRowDouble adxe;
|
|
bool activeset[];
|
|
int i_=0;
|
|
|
|
waserrors=false;
|
|
//--- Eval0 test: unconstrained model evaluation
|
|
eval0errors=false;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
for(k=0; k<=2*n; k++)
|
|
{
|
|
//--- Allocate place
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
ge=vector<double>::Zeros(n);
|
|
gt=vector<double>::Zeros(n);
|
|
tmp0=vector<double>::Zeros(n);
|
|
if(k>0)
|
|
{
|
|
q=matrix<double>::Zeros(k,n);
|
|
r=vector<double>::Zeros(k);
|
|
}
|
|
//--- Generate problem
|
|
alpha=CMath::RandomReal()+1.0;
|
|
theta=CMath::RandomReal()+1.0;
|
|
tau=CMath::RandomReal()*CMath::RandomInteger(2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,10*(1+CMath::RandomReal()));
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,CMath::RandomReal()+1);
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
v=0.1*CMath::RandomReal()-0.05;
|
|
a.Set(i,j,v);
|
|
a.Set(j,i,v);
|
|
}
|
|
for(j=0; j<k; j++)
|
|
q.Set(j,i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
r.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Build model
|
|
CCQModels::CQMInit(n,s);
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
CCQModels::CQMSetB(s,b);
|
|
CCQModels::CQMSetQ(s,q,r,k,theta);
|
|
CCQModels::CQMSetD(s,d,tau);
|
|
//--- Evaluate and compare:
|
|
//--- * X - random point
|
|
//---*GE - "exact" gradient
|
|
//--- * XTADX2 - x'*(alpha*A+tau*D)*x/2
|
|
//--- * ADXE - (alpha*A+tau*D)*x
|
|
//--- * V - model value at X
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
ge.Set(i,0.0);
|
|
}
|
|
v=0.0;
|
|
xtadx2=0.0;
|
|
adxe=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v+=x[i]*b[i];
|
|
ge.Add(i,b[i]);
|
|
v+=0.5*CMath::Sqr(x[i])*tau*d[i];
|
|
ge.Add(i,x[i]*tau*d[i]);
|
|
adxe.Add(i,x[i]*tau*d[i]);
|
|
xtadx2+=0.5*CMath::Sqr(x[i])*tau*d[i];
|
|
for(j=0; j<n; j++)
|
|
{
|
|
v+=0.5*alpha*x[i]*a.Get(i,j)*x[j];
|
|
ge.Add(i,alpha*a.Get(i,j)*x[j]);
|
|
adxe.Add(i,alpha*a.Get(i,j)*x[j]);
|
|
xtadx2+=0.5*alpha*x[i]*a.Get(i,j)*x[j];
|
|
}
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v2=CAblasF::RDotVR(n,x,q,i);
|
|
v+=0.5*theta*CMath::Sqr(v2-r[i]);
|
|
for(j=0; j<n; j++)
|
|
ge.Add(j,theta*(v2-r[i])*q.Get(i,j));
|
|
}
|
|
v2=CCQModels::CQMEval(s,x);
|
|
CAp::SetErrorFlag(eval0errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
CCQModels::CQMEvalX(s,x,v2,noise);
|
|
CAp::SetErrorFlag(eval0errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
CAp::SetErrorFlag(eval0errors,(noise<0.0 || noise>(10000.0*CMath::m_machineepsilon)),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
v2=CCQModels::CQMXTADX2(s,x,tmp0);
|
|
CAp::SetErrorFlag(eval0errors,MathAbs(xtadx2-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
CCQModels::CQMGradUnconstrained(s,x,gt);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(eval0errors,MathAbs(ge[i]-gt[i])>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
CCQModels::CQMADX(s,x,adx);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(eval0errors,MathAbs(adx[i]-adxe[i])>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
}
|
|
}
|
|
waserrors=waserrors||eval0errors;
|
|
//--- Eval1 test: constrained model evaluation
|
|
eval1errors=false;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
for(k=0; k<=2*n; k++)
|
|
{
|
|
//--- Allocate place
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
ArrayResize(activeset,n);
|
|
if(k>0)
|
|
{
|
|
q=matrix<double>::Zeros(k,n);
|
|
r=vector<double>::Zeros(k);
|
|
}
|
|
//--- Generate problem
|
|
alpha=CMath::RandomReal()+1.0;
|
|
theta=CMath::RandomReal()+1.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,10*(1+CMath::RandomReal()));
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
activeset[i]=CMath::RandomReal()>0.5;
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
v=0.1*CMath::RandomReal()-0.05;
|
|
a.Set(i,j,v);
|
|
a.Set(j,i,v);
|
|
}
|
|
for(j=0; j<k; j++)
|
|
q.Set(j,i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
r.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Build model, evaluate at random point X, compare
|
|
CCQModels::CQMInit(n,s);
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
CCQModels::CQMSetB(s,b);
|
|
CCQModels::CQMSetQ(s,q,r,k,theta);
|
|
CCQModels::CQMSetActiveSet(s,xc,activeset);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
if(!activeset[i])
|
|
xc.Set(i,x[i]);
|
|
}
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v+=xc[i]*b[i];
|
|
for(j=0; j<n; j++)
|
|
v+=0.5*alpha*xc[i]*a.Get(i,j)*xc[j];
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v2=CAblasF::RDotVR(n,xc,q,i);
|
|
v+=0.5*theta*CMath::Sqr(v2-r[i]);
|
|
}
|
|
CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMEval(s,xc))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMDebugConstrainedEvalT(s,x))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMDebugConstrainedEvalE(s,x))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
}
|
|
}
|
|
waserrors=waserrors||eval1errors;
|
|
//--- Eval2 test: we generate empty problem and apply sequence of random transformations,
|
|
//--- re-evaluating and re-checking model after each modification.
|
|
//--- The purpose of such test is to ensure that our caching strategy works correctly.
|
|
eval2errors=false;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
kmax=2*n;
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
d=vector<double>::Ones(n);
|
|
x=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
q=matrix< double>::Zeros(kmax,n);
|
|
r=vector< double>::Zeros(kmax);
|
|
ArrayResize(activeset,n);
|
|
tmp0=vector<double>::Zeros(n);
|
|
alpha=0.0;
|
|
theta=0.0;
|
|
k=0;
|
|
tau=1.0+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
activeset[i]=false;
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
CCQModels::CQMInit(n,s);
|
|
CCQModels::CQMSetD(s,d,tau);
|
|
for(pass=1; pass<=100; pass++)
|
|
{
|
|
//--- Select random modification type, apply modification.
|
|
//--- MKind is a random integer in [0,7] - number of specific
|
|
//--- modification to apply.
|
|
mkind=CMath::RandomInteger(8);
|
|
if(mkind==0.0)
|
|
{
|
|
//--- Set non-zero D
|
|
tau=1.0+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
d.Set(i,2*CMath::RandomReal()+1);
|
|
CCQModels::CQMSetD(s,d,tau);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==1.0)
|
|
{
|
|
//--- Set zero D.
|
|
//--- In case Alpha=0, set non-zero A.
|
|
if(alpha==0.0)
|
|
{
|
|
alpha=1.0+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
a.Set(i,j,0.2*CMath::RandomReal()-0.1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,4+2*CMath::RandomReal());
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
}
|
|
tau=0.0;
|
|
d.Fill(0.0);
|
|
CCQModels::CQMSetD(s,d,0.0);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==2.0)
|
|
{
|
|
//--- Set non-zero A
|
|
alpha=1.0+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
a.Set(i,j,0.2*CMath::RandomReal()-0.1);
|
|
a.Set(j,i,a.Get(i,j));
|
|
}
|
|
}
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,4+2*CMath::RandomReal());
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==3.0)
|
|
{
|
|
//--- Set zero A.
|
|
//--- In case Tau=0, set non-zero D.
|
|
if(tau==0.0)
|
|
{
|
|
tau=1.0+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
d.Set(i,2*CMath::RandomReal()+1);
|
|
CCQModels::CQMSetD(s,d,tau);
|
|
}
|
|
alpha=0.0;
|
|
a=matrix<double>::Zeros(n,n);
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==4.0)
|
|
{
|
|
//--- Set B.
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
CCQModels::CQMSetB(s,b);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==5.0)
|
|
{
|
|
//--- Set Q.
|
|
k=CMath::RandomInteger(kmax+1);
|
|
theta=1.0+CMath::RandomReal();
|
|
for(i=0; i<k; i++)
|
|
{
|
|
r.Set(i,2*CMath::RandomReal()-1);
|
|
for(j=0; j<n; j++)
|
|
q.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
CCQModels::CQMSetQ(s,q,r,k,theta);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==6.0)
|
|
{
|
|
//--- Set active set
|
|
for(i=0; i<n; i++)
|
|
{
|
|
activeset[i]=CMath::RandomReal()>0.5;
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
CCQModels::CQMSetActiveSet(s,xc,activeset);
|
|
}
|
|
else
|
|
{
|
|
if(mkind==7.0)
|
|
{
|
|
//--- Rewrite main diagonal
|
|
if(alpha==0.0)
|
|
alpha=1.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
tmp0.Set(i,1+CMath::RandomReal());
|
|
a.Set(i,i,tmp0[i]/alpha);
|
|
}
|
|
CCQModels::CQMRewriteDenseDiagonal(s,tmp0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- generate random point with respect to constraints,
|
|
//--- test model at this point
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
if(activeset[i])
|
|
x.Set(i,xc[i]);
|
|
}
|
|
v=CAblasF::RDotV(n,x,b);
|
|
if(tau>0.0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
v+=0.5*tau*d[i]*CMath::Sqr(x[i]);
|
|
}
|
|
if(alpha>0.0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
v+=0.5*alpha*x[i]*a.Get(i,j)*x[j];
|
|
}
|
|
if(theta>0.0)
|
|
{
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v2=CAblasF::RDotVR(n,x,q,i);
|
|
v+=0.5*theta*CMath::Sqr(v2-r[i]);
|
|
}
|
|
}
|
|
v2=CCQModels::CQMEval(s,x);
|
|
CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
v2=CCQModels::CQMDebugConstrainedEvalT(s,x);
|
|
CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
v2=CCQModels::CQMDebugConstrainedEvalE(s,x);
|
|
CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
|
|
}
|
|
}
|
|
waserrors=waserrors||eval2errors;
|
|
//--- Newton0 test: unconstrained optimization
|
|
newton0errors=false;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
for(k=0; k<=2*n; k++)
|
|
{
|
|
//--- Allocate place
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
if(k>0)
|
|
{
|
|
q=matrix<double>::Zeros(k,n);
|
|
r=vector<double>::Zeros(k);
|
|
}
|
|
//--- Generate problem with known solution x0:
|
|
//--- min f(x),
|
|
//--- f(x) = 0.5*(x-x0)'*A*(x-x0)
|
|
//--- = 0.5*x'*A*x + (-x0'*A)*x + 0.5*x0'*A*x0'
|
|
alpha=CMath::RandomReal()+1.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
a.Set(i,i,10*(1+CMath::RandomReal()));
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
v=0.1*CMath::RandomReal()-0.05;
|
|
a.Set(i,j,v);
|
|
a.Set(j,i,v);
|
|
}
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x0,a,i);
|
|
b.Set(i,-(alpha*v));
|
|
}
|
|
theta=CMath::RandomReal()+1.0;
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
q.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
r.Set(i,v);
|
|
}
|
|
//--- Build model, evaluate at random point X, compare
|
|
CCQModels::CQMInit(n,s);
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
CCQModels::CQMSetB(s,b);
|
|
CCQModels::CQMSetQ(s,q,r,k,theta);
|
|
CCQModels::CQMConstrainedOptimum(s,x);
|
|
for(i=0; i<n; i++)
|
|
newton0errors=newton0errors||MathAbs(x[i]-x0[i])>(1.0E6*CMath::m_machineepsilon);
|
|
}
|
|
}
|
|
waserrors=waserrors||newton0errors;
|
|
//--- Newton1 test: constrained optimization
|
|
newton1errors=false;
|
|
h=1.0E-3;
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
for(k=0; k<=2*n; k++)
|
|
{
|
|
//--- Allocate place
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
ArrayResize(activeset,n);
|
|
if(k>0)
|
|
{
|
|
q=matrix<double>::Zeros(k,n);
|
|
r=vector<double>::Zeros(k);
|
|
}
|
|
//--- Generate test problem with unknown solution.
|
|
alpha=CMath::RandomReal()+1.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
a.Set(i,i,10*(1+CMath::RandomReal()));
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
activeset[i]=CMath::RandomReal()>0.5;
|
|
for(j=i+1; j<n; j++)
|
|
{
|
|
v=0.1*CMath::RandomReal()-0.05;
|
|
a.Set(i,j,v);
|
|
a.Set(j,i,v);
|
|
}
|
|
}
|
|
theta=CMath::RandomReal()+1.0;
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
q.Set(i,j,2*CMath::RandomReal()-1);
|
|
r.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- Build model, find solution
|
|
CCQModels::CQMInit(n,s);
|
|
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
|
|
CCQModels::CQMSetB(s,b);
|
|
CCQModels::CQMSetQ(s,q,r,k,theta);
|
|
CCQModels::CQMSetActiveSet(s,xc,activeset);
|
|
if(CCQModels::CQMConstrainedOptimum(s,x))
|
|
{
|
|
//--- Check that constraints are satisfied,
|
|
//--- and that solution is true optimum
|
|
f0=CCQModels::CQMEval(s,x);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
newton1errors=newton1errors||(activeset[i] && x[i]!=xc[i]);
|
|
if(!activeset[i])
|
|
{
|
|
v=x[i];
|
|
x.Set(i,v+h);
|
|
v2=CCQModels::CQMEval(s,x);
|
|
newton1errors=newton1errors||v2<f0;
|
|
x.Set(i,v-h);
|
|
v2=CCQModels::CQMEval(s,x);
|
|
newton1errors=newton1errors||v2<f0;
|
|
x.Set(i,v);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
newton1errors=true;
|
|
}
|
|
}
|
|
waserrors=waserrors||newton1errors;
|
|
//--- Newton2 test: we test ability to work with diagonal matrices, including
|
|
//--- very large ones (up to 100.000 elements). This test checks that:
|
|
//--- a) we can work with Alpha=0, i.e. when we have strictly diagonal A
|
|
//--- b) diagonal problems are handled efficiently, i.e. algorithm will
|
|
//--- successfully solve problem with N=100.000
|
|
//--- Test problem:
|
|
//--- * diagonal term D and rank-K term Q
|
|
//--- * known solution X0,
|
|
//--- * about 50% of constraints are active and equal to components of X0
|
|
newton2errors=false;
|
|
for(nkind=0; nkind<=5; nkind++)
|
|
{
|
|
for(k=0; k<=5; k++)
|
|
{
|
|
n=(int)MathRound(MathPow(n,nkind));
|
|
//--- generate problem
|
|
d=vector<double>::Zeros(n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
ArrayResize(activeset,n);
|
|
if(k>0)
|
|
{
|
|
q=matrix<double>::Zeros(k,n);
|
|
r=vector<double>::Zeros(k);
|
|
}
|
|
tau=1+CMath::RandomReal();
|
|
theta=1+CMath::RandomReal();
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,1+CMath::RandomReal());
|
|
b.Set(i,-(x0[i]*tau*d[i]));
|
|
activeset[i]=CMath::RandomReal()>0.5;
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=0.0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
q.Set(i,j,2*CMath::RandomReal()-1);
|
|
v+=q.Get(i,j)*x0[j];
|
|
}
|
|
r.Set(i,v);
|
|
}
|
|
//--- Solve, test
|
|
CCQModels::CQMInit(n,s);
|
|
CCQModels::CQMSetB(s,b);
|
|
CCQModels::CQMSetD(s,d,tau);
|
|
CCQModels::CQMSetQ(s,q,r,k,theta);
|
|
CCQModels::CQMSetActiveSet(s,x0,activeset);
|
|
if(CCQModels::CQMConstrainedOptimum(s,x))
|
|
{
|
|
//--- Check that constraints are satisfied,
|
|
//--- and that solution is true optimum
|
|
f0=CCQModels::CQMEval(s,x);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
newton2errors=newton2errors||(activeset[i] && x[i]!=x0[i]);
|
|
newton2errors=newton2errors||(!activeset[i] && MathAbs(x[i]-x0[i])>(1000.0*CMath::m_machineepsilon));
|
|
}
|
|
//--- Check that constrained evaluation at some point gives correct results
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(activeset[i])
|
|
x.Set(i,x0[i]);
|
|
else
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
v+=0.5*tau*d[i]*CMath::Sqr(x[i])+x[i]*b[i];
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v2=CAblasF::RDotVR(n,x,q,i);
|
|
v+=0.5*theta*CMath::Sqr(v2-r[i]);
|
|
}
|
|
v2=CCQModels::CQMEval(s,x);
|
|
newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon);
|
|
v2=CCQModels::CQMDebugConstrainedEvalT(s,x);
|
|
newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon);
|
|
v2=CCQModels::CQMDebugConstrainedEvalE(s,x);
|
|
newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon);
|
|
}
|
|
else
|
|
newton2errors=true;
|
|
}
|
|
}
|
|
waserrors=waserrors||newton2errors;
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CONVEX QUADRATIC MODELS");
|
|
PrintResult("Eval0 test",!eval0errors);
|
|
PrintResult("Eval1 test",!eval1errors);
|
|
PrintResult("Eval2 test",!eval2errors);
|
|
PrintResult("Newton0 test",!newton0errors);
|
|
PrintResult("Newton1 test",!newton1errors);
|
|
PrintResult("Newton2 test",!newton2errors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSNNLSUnit
|
|
{
|
|
public:
|
|
static bool TestSNNLS(bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSNNLSUnit::TestSNNLS(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool test0errors=false;
|
|
bool test1errors=false;
|
|
bool test2errors=false;
|
|
bool testnewtonerrors=false;
|
|
bool waserrors=false;
|
|
double eps=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int ns=0;
|
|
int nd=0;
|
|
int nr=0;
|
|
CMatrixDouble densea;
|
|
CMatrixDouble effectivea;
|
|
bool isconstrained[];
|
|
CRowDouble g;
|
|
CRowDouble b;
|
|
CRowDouble x;
|
|
CRowDouble xs;
|
|
CSNNLSSolver s;
|
|
CHighQualityRandState rs;
|
|
double rho=0;
|
|
double xtol=0;
|
|
int nmax=10;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test 2 (comes first because it is very basic):
|
|
//--- * NS=0
|
|
//--- * ND in [1,NMAX]
|
|
//--- * NR=ND
|
|
//--- * DenseA is diagonal with positive entries
|
|
//--- * B is random
|
|
//--- * random constraints
|
|
//--- Exact solution is known and can be tested
|
|
eps=1.0E-12;
|
|
for(nd=1; nd<=nmax; nd++)
|
|
{
|
|
//--- Generate problem
|
|
ns=0;
|
|
nr=nd;
|
|
densea=matrix<double>::Zeros(nd,nd);
|
|
b=vector<double>::Zeros(nd);
|
|
ArrayResize(isconstrained,nd);
|
|
for(i=0; i<nd; i++)
|
|
{
|
|
densea.Set(i,i,1+CHighQualityRand::HQRndUniformI(rs,2));
|
|
b.Set(i,(1+CHighQualityRand::HQRndUniformI(rs,2))*(2*CHighQualityRand::HQRndUniformI(rs,2)-1));
|
|
isconstrained[i]=CHighQualityRand::HQRndUniformR(rs)>0.5;
|
|
}
|
|
//--- Solve with SNNLS solver
|
|
CSNNLS::SNNLSInit(0,0,0,s);
|
|
CSNNLS::SNNLSSetProblem(s,densea,b,0,nd,nd);
|
|
for(i=0; i<nd; i++)
|
|
{
|
|
if(!isconstrained[i])
|
|
CSNNLS::SNNLSDropNNC(s,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(s,x);
|
|
//--- Check
|
|
for(i=0; i<nd; i++)
|
|
{
|
|
if(isconstrained[i])
|
|
{
|
|
CAp::SetErrorFlag(test2errors,MathAbs(x[i]-MathMax(b[i]/densea.Get(i,i),0.0))>eps,"testsnnlsunit.ap:86");
|
|
CAp::SetErrorFlag(test2errors,x[i]<0.0,"testsnnlsunit.ap:87");
|
|
}
|
|
else
|
|
CAp::SetErrorFlag(test2errors,MathAbs(x[i]-b[i]/densea.Get(i,i))>eps,"testsnnlsunit.ap:90");
|
|
}
|
|
}
|
|
//--- Test 0:
|
|
//--- * NS in [0,NMAX]
|
|
//--- * ND in [0,NMAX]
|
|
//--- * NR in [NS,NS+ND+NMAX]
|
|
//--- * NS+ND>0, NR>0
|
|
//--- * about 50% of variables are constrained
|
|
//--- * we check that constrained gradient is small at the solution
|
|
eps=1.0E-5;
|
|
for(ns=0; ns<=nmax; ns++)
|
|
{
|
|
for(nd=0; nd<=nmax; nd++)
|
|
{
|
|
for(nr=ns; nr<=ns+nd+nmax; nr++)
|
|
{
|
|
//--- Skip NS+ND=0, NR=0
|
|
if(ns+nd==0)
|
|
continue;
|
|
if(nr==0)
|
|
continue;
|
|
//--- Generate problem:
|
|
//--- * DenseA, array[NR,ND]
|
|
//--- * EffectiveA, array[NR,NS+ND]
|
|
//--- * B, array[NR]
|
|
//--- * IsConstrained, array[NS+ND]
|
|
if(nd>0)
|
|
{
|
|
densea=matrix<double>::Zeros(nr,nd);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
for(j=0; j<nd; j++)
|
|
densea.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
effectivea=matrix<double>::Zeros(nr,ns+nd);
|
|
for(i=0; i<=ns-1; i++)
|
|
effectivea.Set(i,i,1.0);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
for(j=0; j<nd; j++)
|
|
effectivea.Set(i,ns+j,densea.Get(i,j));
|
|
}
|
|
b=vector<double>::Zeros(nr);
|
|
for(i=0; i<nr; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
ArrayResize(isconstrained,ns+nd);
|
|
for(i=0; i<ns+nd; i++)
|
|
isconstrained[i]=CMath::RandomReal()>0.5;
|
|
//--- Solve with SNNLS solver
|
|
CSNNLS::SNNLSInit(0,0,0,s);
|
|
CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr);
|
|
for(i=0; i<ns+nd; i++)
|
|
{
|
|
if(!isconstrained[i])
|
|
CSNNLS::SNNLSDropNNC(s,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(s,x);
|
|
//--- Check non-negativity
|
|
for(i=0; i<ns+nd; i++)
|
|
CAp::SetErrorFlag(test0errors,isconstrained[i] && x[i]<0.0,"testsnnlsunit.ap:160");
|
|
//--- Calculate gradient A'*A*x-b'*A.
|
|
//--- Check projected gradient (each component must be less than Eps).
|
|
g=vector<double>::Zeros(ns+nd);
|
|
for(i=0; i<ns+nd; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<nr; i_++)
|
|
v+=b[i_]*effectivea.Get(i_,i);
|
|
g.Set(i,-v);
|
|
}
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<ns+nd; i_++)
|
|
v+=effectivea.Get(i,i_)*x[i_];
|
|
for(i_=0; i_<ns+nd; i_++)
|
|
g.Add(i_,v*effectivea.Get(i,i_));
|
|
}
|
|
for(i=0; i<ns+nd; i++)
|
|
{
|
|
if(!isconstrained[i] || x[i]>0.0)
|
|
CAp::SetErrorFlag(test0errors,MathAbs(g[i])>eps,"testsnnlsunit.ap:179");
|
|
else
|
|
CAp::SetErrorFlag(test0errors,g[i]<(-eps),"testsnnlsunit.ap:181");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test 1: ability of the solver to take very short steps.
|
|
//--- We solve problem similar to one solver in test 0, but with
|
|
//--- progressively decreased magnitude of variables. We generate
|
|
//--- problem with already-known solution and compare results against it.
|
|
xtol=1.0E-7;
|
|
for(ns=0; ns<=nmax; ns++)
|
|
{
|
|
for(nd=0; nd<=nmax; nd++)
|
|
{
|
|
for(nr=ns; nr<=ns+nd+nmax; nr++)
|
|
{
|
|
for(k=0; k<=20; k++)
|
|
{
|
|
//--- Skip NS+ND=0, NR=0
|
|
//--- Skip degenerate problems (NR<NS+ND) - important for this particular test.
|
|
if(ns+nd==0)
|
|
continue;
|
|
if(nr==0)
|
|
continue;
|
|
if(nr<ns+nd)
|
|
continue;
|
|
//--- Generate problem:
|
|
//--- * DenseA, array[NR,ND]
|
|
//--- * EffectiveA, array[NR,NS+ND]
|
|
//--- * B, array[NR]
|
|
//--- * IsConstrained, array[NS+ND]
|
|
rho=MathPow(10,-k);
|
|
if(nd>0)
|
|
{
|
|
densea=matrix<double>::Zeros(nr,nd);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
for(j=0; j<nd; j++)
|
|
densea.Set(i,j,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
}
|
|
effectivea=matrix<double>::Zeros(nr,ns+nd);
|
|
for(i=0; i<=ns-1; i++)
|
|
effectivea.Set(i,i,1.0);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
for(j=0; j<nd; j++)
|
|
effectivea.Set(i,ns+j,densea.Get(i,j));
|
|
}
|
|
xs=vector<double>::Zeros(ns+nd);
|
|
ArrayResize(isconstrained,ns+nd);
|
|
for(i=0; i<ns+nd; i++)
|
|
{
|
|
xs.Set(i,rho*(CHighQualityRand::HQRndUniformR(rs)-0.5));
|
|
isconstrained[i]=(xs[i])>0.0;
|
|
}
|
|
b=vector<double>::Zeros(nr);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
v=CAblasF::RDotVR(ns+nd,xs,effectivea,i);
|
|
b.Set(i,v);
|
|
}
|
|
//--- Solve with SNNLS solver
|
|
CSNNLS::SNNLSInit(0,0,0,s);
|
|
CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr);
|
|
for(i=0; i<=ns+nd-1; i++)
|
|
{
|
|
if(!isconstrained[i])
|
|
CSNNLS::SNNLSDropNNC(s,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(s,x);
|
|
//--- Check non-negativity
|
|
for(i=0; i<ns+nd; i++)
|
|
CAp::SetErrorFlag(test1errors,isconstrained[i] && x[i]<0.0,"testsnnlsunit.ap:263");
|
|
//--- Compare with true solution
|
|
for(i=0; i<ns+nd; i++)
|
|
CAp::SetErrorFlag(test1errors,(double)(MathAbs(xs[i]-x[i]))>(double)(rho*xtol),"testsnnlsunit.ap:269");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test for Newton phase:
|
|
//--- * NS in [0,NMAX]
|
|
//--- * ND in [0,NMAX]
|
|
//--- * NR in [NS,NS+ND+NMAX]
|
|
//--- * NS+ND>0, NR>0
|
|
//--- * all variables are unconstrained
|
|
//--- * S.DebugMaxNewton is set to 1, S.RefinementIts is set to 1,
|
|
//--- i.e. algorithm is terminated after one Newton iteration, and no
|
|
//--- iterative refinement is used.
|
|
//--- * we test that gradient is small at solution, i.e. one Newton iteration
|
|
//--- on unconstrained problem is enough to find solution. In case of buggy
|
|
//--- Newton solver one iteration won't move us to the solution - it may
|
|
//--- decrease function value, but won't find exact solution.
|
|
//--- This test is intended to catch subtle bugs in the Newton solver which
|
|
//--- do NOT prevent algorithm from converging to the solution, but slow it
|
|
//--- down (convergence becomes linear or even slower).
|
|
eps=1.0E-4;
|
|
for(ns=0; ns<=nmax; ns++)
|
|
{
|
|
for(nd=0; nd<=nmax; nd++)
|
|
{
|
|
for(nr=ns; nr<=ns+nd+nmax; nr++)
|
|
{
|
|
//--- Skip NS+ND=0, NR=0
|
|
if(ns+nd==0)
|
|
continue;
|
|
if(nr==0)
|
|
continue;
|
|
//--- Generate problem:
|
|
//--- * DenseA, array[NR,ND]
|
|
//--- * EffectiveA, array[NR,NS+ND]
|
|
//--- * B, array[NR]
|
|
//--- * IsConstrained, array[NS+ND]
|
|
if(nd>0)
|
|
{
|
|
densea=matrix<double>::Zeros(nr,nd);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
for(j=0; j<nd; j++)
|
|
densea.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
}
|
|
effectivea=matrix<double>::Zeros(nr,ns+nd);
|
|
for(i=0; i<=ns-1; i++)
|
|
effectivea.Set(i,i,1.0);
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
for(j=0; j<nd; j++)
|
|
effectivea.Set(i,ns+j,densea.Get(i,j));
|
|
}
|
|
b=vector<double>::Zeros(nr);
|
|
for(i=0; i<nr; i++)
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
//--- Solve with SNNLS solver
|
|
CSNNLS::SNNLSInit(0,0,0,s);
|
|
CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr);
|
|
for(i=0; i<ns+nd; i++)
|
|
CSNNLS::SNNLSDropNNC(s,i);
|
|
s.m_debugmaxinnerits=1;
|
|
CSNNLS::SNNLSSolve(s,x);
|
|
//--- Calculate gradient A'*A*x-b'*A.
|
|
//--- Check projected gradient (each component must be less than Eps).
|
|
g=vector<double>::Zeros(ns+nd);
|
|
for(i=0; i<ns+nd; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<nr; i_++)
|
|
v+=b[i_]*effectivea.Get(i_,i);
|
|
g.Set(i,-v);
|
|
}
|
|
for(i=0; i<nr; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<ns+nd; i_++)
|
|
v+=effectivea.Get(i,i_)*x[i_];
|
|
for(i_=0; i_<ns+nd; i_++)
|
|
g.Add(i_,v*effectivea.Get(i,i_));
|
|
}
|
|
for(i=0; i<ns+nd; i++)
|
|
CAp::SetErrorFlag(testnewtonerrors,MathAbs(g[i])>eps,"testsnnlsunit.ap:358");
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((test0errors||test1errors)||test2errors)||testnewtonerrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SPECIAL NNLS SOLVER");
|
|
PrintResult("TEST 0",!test0errors);
|
|
PrintResult("TEST 1",!test1errors);
|
|
PrintResult("TEST 2",!test2errors);
|
|
PrintResult("NEWTON PHASE",!testnewtonerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSActiveSetsUnit
|
|
{
|
|
public:
|
|
static bool TestSActiveSets(bool silent);
|
|
|
|
private:
|
|
static void CTestSActiveSetsUnit::TestSpecProperties(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSActiveSetsUnit::TestSActiveSets(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool specerr=false;
|
|
//--- initialization
|
|
TestSpecProperties(specerr);
|
|
//--- report
|
|
waserrors=specerr;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ACTIVE SETS");
|
|
PrintResult("* SPECIAL PROPERTIES",!specerr);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests special properties. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSActiveSetsUnit::TestSpecProperties(bool &err)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int nec=0;
|
|
int nic=0;
|
|
double v=0;
|
|
double vv=0;
|
|
CSActiveSet state;
|
|
CHighQualityRandState rs;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble x;
|
|
CRowDouble s;
|
|
CMatrixDouble c;
|
|
CRowInt ct;
|
|
int scaletype=0;
|
|
int pass=0;
|
|
int distortidx=0;
|
|
double distortmag=0;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- N-dimensional problem with Ne equality and Ni inequality constraints.
|
|
//--- Check that SActiveSet object uses efficient algorithm
|
|
//--- to determine initial point: it avoids expensive (N+Ni)-dimensional
|
|
//--- QP subproblem when initial point is feasible w.r.t. constraints.
|
|
//--- In order to do so we try to find initial point for a problem with
|
|
//--- 2 equality constraints and 1000000 inequality constraints (+box
|
|
//--- constraints). Inefficient algorithm will simply fail to allocate
|
|
//--- enough memory, so we do not have to perform any checks here.
|
|
n=5;
|
|
nec=2;
|
|
nic=1000000;
|
|
x=vector<double>::Zeros(n);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
for(scaletype=0; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate problem
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
bl.Set(i,x[i]-CHighQualityRand::HQRndUniformR(rs)*CHighQualityRand::HQRndUniformI(rs,2));
|
|
bu.Set(i,x[i]+CHighQualityRand::HQRndUniformR(rs)*CHighQualityRand::HQRndUniformI(rs,2));
|
|
}
|
|
c=matrix<double>::Zeros(nec+nic,n+1);
|
|
ct.Resize(nec+nic);
|
|
for(i=0; i<nec+nic; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v+=c.Get(i,j)*x[j];
|
|
}
|
|
c.Set(i,n,v);
|
|
if(i<nec)
|
|
ct.Set(i,0);
|
|
else
|
|
{
|
|
c.Add(i,n,0.1);
|
|
ct.Set(i,-1);
|
|
}
|
|
}
|
|
//--- Apply scaling (if needed), then randomly multiply C by
|
|
//--- some large/small value in order to distort magnitude of
|
|
//--- linear constraints.
|
|
//--- Correct SActiveSet implementation must be able to renormalize
|
|
//--- constraints prior to checking feasibility of initial point.
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,1);
|
|
if(scaletype!=0)
|
|
s.Set(i,MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20));
|
|
x.Mul(i,s[i]);
|
|
bl.Mul(i,s[i]);
|
|
bu.Mul(i,s[i]);
|
|
for(j=0; j<nec+nic; j++)
|
|
c.Mul(j,i,1.0/s[i]);
|
|
}
|
|
for(i=0; i<nec+nic; i++)
|
|
{
|
|
v=MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20);
|
|
for(i_=0; i_<=n; i_++)
|
|
c.Mul(i,i_,v);
|
|
}
|
|
//--- Solve.
|
|
//--- Simply being able to survive SASStartOptimization() call is enough for this test
|
|
CSActiveSets::SASInit(n,state);
|
|
CSActiveSets::SASSetScale(state,s);
|
|
CSActiveSets::SASSetBC(state,bl,bu);
|
|
CSActiveSets::SASSetLC(state,c,ct,nec+nic);
|
|
CSActiveSets::SASStartOptimization(state,x);
|
|
}
|
|
//--- Test that algorithm can correctly distinguish between feasible
|
|
//--- and non-feasible initial points. In order to do so we try to
|
|
//--- call SASStartOptimization() for moderately sized problem and
|
|
//--- examine State.FeasInitPt flag after call is done.
|
|
//--- This test is performed for equality-only constraints
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
n=50+CHighQualityRand::HQRndUniformI(rs,10);
|
|
nec=10+CHighQualityRand::HQRndUniformI(rs,10);
|
|
nic=0;
|
|
x=vector<double>::Zeros(n);
|
|
for(scaletype=0; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate problem, prepare distortion which introduces infeasibility
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
c=matrix<double>::Zeros(nec+nic,n+1);
|
|
ct.Resize(nec+nic);
|
|
for(i=0; i<nec+nic; i++)
|
|
{
|
|
v=0;
|
|
vv=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
vv+=CMath::Sqr(c.Get(i,j));
|
|
v+=c.Get(i,j)*x[j];
|
|
}
|
|
c.Set(i,n,v);
|
|
vv=CApServ::Coalesce(vv,1);
|
|
vv=1/MathSqrt(vv);
|
|
for(i_=0; i_<=n; i_++)
|
|
c.Mul(i,i_,vv);
|
|
ct.Set(i,0);
|
|
}
|
|
distortidx=CHighQualityRand::HQRndUniformI(rs,n);
|
|
distortmag=0;
|
|
for(i=0; i<nec+nic; i++)
|
|
distortmag=MathMax(distortmag,MathAbs(c.Get(i,distortidx)));
|
|
//--- check
|
|
if(!CAp::Assert(distortmag>0.0))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
distortmag=1.0E7*CMath::m_machineepsilon/distortmag;
|
|
//--- Apply scaling (if needed), then randomly multiply C by
|
|
//--- some large/small value in order to distort magnitude of
|
|
//--- linear constraints.
|
|
//--- Correct SActiveSet implementation must be able to renormalize
|
|
//--- constraints prior to checking feasibility of initial point.
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,1);
|
|
if(scaletype!=0)
|
|
s.Set(i,MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20));
|
|
x.Mul(i,s[i]);
|
|
for(j=0; j<nec+nic; j++)
|
|
c.Mul(j,i,1.0/s[i]);
|
|
}
|
|
for(i=0; i<nec+nic; i++)
|
|
{
|
|
v=MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20);
|
|
for(i_=0; i_<=n; i_++)
|
|
c.Mul(i,i_,v);
|
|
}
|
|
//--- Solve for feasible initial X and check State.FeasInitPt flag
|
|
CSActiveSets::SASInit(n,state);
|
|
CSActiveSets::SASSetScale(state,s);
|
|
CSActiveSets::SASSetLC(state,c,ct,nec+nic);
|
|
CSActiveSets::SASStartOptimization(state,x);
|
|
CAp::SetErrorFlag(err,!state.m_feasinitpt,"testsactivesetsunit.ap:225");
|
|
//--- Solve for infeasible initial X and check State.FeasInitPt flag
|
|
x.Add(distortidx,s[distortidx]*distortmag);
|
|
CSActiveSets::SASInit(n,state);
|
|
CSActiveSets::SASSetScale(state,s);
|
|
CSActiveSets::SASSetLC(state,c,ct,nec+nic);
|
|
CSActiveSets::SASStartOptimization(state,x);
|
|
CAp::SetErrorFlag(err,state.m_feasinitpt,"testsactivesetsunit.ap:235");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinQPUnit
|
|
{
|
|
public:
|
|
static bool TestMinQP(bool silent);
|
|
static bool SimpleTest(void);
|
|
static bool FuncTest1(void);
|
|
static bool FuncTest2(void);
|
|
static bool ConsoleTest(void);
|
|
static bool QuickQPTests(void);
|
|
static bool BLEICTests(void);
|
|
|
|
private:
|
|
static void BCQPTest(bool &waserrors);
|
|
static bool ECQPTest(void);
|
|
static void ICQPTest(bool &err);
|
|
static void GeneralLCQPTest(bool &errorflag);
|
|
static bool SpecialICQPTests(void);
|
|
static void DenseAULTests(bool &errorflag);
|
|
static void IPMTests(bool &errorflag);
|
|
static void SpecTests(bool &errorflag);
|
|
static double ProjectedAntiGradNorm(int n,CRowDouble &x,CRowDouble &g,CRowDouble &bndl,CRowDouble &bndu);
|
|
static void TestBCGradAndFeasibility(CMatrixDouble &a,CRowDouble &b,CRowDouble &bndl,CRowDouble &bndu,int n,CRowDouble &x,double eps,bool &errorflag);
|
|
static int SetRandomAlgoAllModern(CMinQPState &s,double &bctol,double &lctol);
|
|
static void SetRandomAlgoNonConvex(CMinQPState &s);
|
|
static void SetRandomAlgoSemiDefinite(CMinQPState &s,double &bctol,double &lctol);
|
|
static void SetRandomAlgoBC(CMinQPState &s);
|
|
static void SetRandomAlgoConvexLC(CMinQPState &s);
|
|
static void SetRandomAlgoNonConvexLC(CMinQPState &s);
|
|
static void DenseToSparse(CMatrixDouble &a,int n,CSparseMatrix &s);
|
|
static void RandomlySplitLC(CMatrixDouble &rawc,CRowInt &rawct,int rawccnt,int n,CSparseMatrix &sparsec,CRowInt &sparsect,int &sparseccnt,CMatrixDouble &densec,CRowInt &densect,int &denseccnt,CHighQualityRandState &rs);
|
|
static void RandomlySplitAndSetLCLegacy(CMatrixDouble &rawc,CRowInt &rawct,int rawccnt,int n,CMinQPState &state,CHighQualityRandState &rs);
|
|
static void RandomlySplitAndSetLC2(CMatrixDouble &rawc,CRowDouble &rawcl,CRowDouble &rawcu,int rawccnt,int n,CMinQPState &state,CHighQualityRandState &rs);
|
|
static void RandomlySelectConvertAndSetQuadraticTerm(CMatrixDouble &a,int n,CMinQPState &state,CHighQualityRandState &rs);
|
|
static double GetConstraintRCond(CMatrixDouble &c,int k,int n);
|
|
static double QuadraticTarget(CMatrixDouble &a,CRowDouble &b,int n,CRowDouble &x);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::TestMinQP(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool simpleerrors;
|
|
bool func1errors;
|
|
bool func2errors;
|
|
bool bcqperrors;
|
|
bool ecqperrors;
|
|
bool icqperrors;
|
|
bool lcqperrors;
|
|
bool quickqperrors;
|
|
bool bleicerrors;
|
|
bool denseaulerrors;
|
|
bool ipmerrors;
|
|
bool specerrors;
|
|
bool waserrors;
|
|
//--- The VERY basic tests for Cholesky and BLEIC
|
|
simpleerrors=SimpleTest();
|
|
func1errors=FuncTest1();
|
|
func2errors=FuncTest2();
|
|
//--- Solver-specific tests
|
|
denseaulerrors=false;
|
|
ipmerrors=false;
|
|
quickqperrors=QuickQPTests();
|
|
bleicerrors=BLEICTests();
|
|
DenseAULTests(denseaulerrors);
|
|
IPMTests(ipmerrors);
|
|
|
|
icqperrors=false;
|
|
lcqperrors=false;
|
|
bcqperrors=false;
|
|
BCQPTest(bcqperrors);
|
|
ecqperrors=ECQPTest();
|
|
ICQPTest(icqperrors);
|
|
icqperrors=icqperrors||SpecialICQPTests();
|
|
GeneralLCQPTest(lcqperrors);
|
|
specerrors=false;
|
|
SpecTests(specerrors);
|
|
//--- report
|
|
waserrors=((((((((((simpleerrors||func1errors)||func2errors)||bcqperrors)||ecqperrors)||icqperrors)||lcqperrors)||quickqperrors)||bleicerrors)||denseaulerrors)||ipmerrors)||specerrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MinQP");
|
|
Print("BASIC TESTS:");
|
|
PrintResult("* SimpleTest",!simpleerrors);
|
|
PrintResult("* Func1Test",!func1errors);
|
|
PrintResult("* Func2Test",!func2errors);
|
|
Print("GENERIC QP TESTS:");
|
|
PrintResult("* box constrained",!bcqperrors);
|
|
PrintResult("* linearly constrained",!(ecqperrors || icqperrors || lcqperrors));
|
|
Print("SOLVER-SPECIFIC TESTS:");
|
|
PrintResult("* QuickQP solver tests",!quickqperrors);
|
|
PrintResult("* BLEIC solver tests",!bleicerrors);
|
|
PrintResult("* DENSE-AUL solver tests",!denseaulerrors);
|
|
PrintResult("* IPM solver tests",!ipmerrors);
|
|
PrintResult("SPECIAL PROPERTIES",!specerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function to test: 'MinQPCreate', 'MinQPSetQuadraticTerm', |
|
|
//| 'MinQPSetBC', 'MinQPSetOrigin', 'MinQPSetStartingPoint', |
|
|
//| 'MinQPOptimize', 'MinQPResults'. |
|
|
//| Test problem: |
|
|
//| A = diag(aii), aii>0 (random) |
|
|
//| b = 0 random bounds (either no bounds, one bound, two bounds |
|
|
//| a<b, two bounds a=b) random start point dimension - |
|
|
//| from 1 to 5. |
|
|
//| Returns True on success, False on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::SimpleTest()
|
|
{
|
|
//--- create variables
|
|
double eps=0.001;
|
|
int msn=5;
|
|
double maxstb=10;
|
|
int nexp=1000;
|
|
double maxn=10;
|
|
double maxnb=1000;
|
|
double minnb=-1000;
|
|
CMinQPState state;
|
|
int sn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int infd=0;
|
|
CMatrixDouble a;
|
|
CRowDouble ub;
|
|
CRowDouble db;
|
|
CRowDouble x;
|
|
CRowDouble tx;
|
|
CRowDouble stx;
|
|
CRowDouble xori;
|
|
CMinQPReport rep;
|
|
|
|
for(sn=1; sn<=msn; sn++)
|
|
{
|
|
tx=vector<double>::Zeros(sn);
|
|
xori=vector<double>::Zeros(sn);
|
|
stx=vector<double>::Zeros(sn);
|
|
db=vector<double>::Zeros(sn);
|
|
ub=vector<double>::Zeros(sn);
|
|
a=matrix<double>::Zeros(sn,sn);
|
|
for(i=0; i<=nexp; i++)
|
|
{
|
|
//--- create diagonal matrix
|
|
for(k=0; k<sn; k++)
|
|
a.Set(k,k,maxn*CMath::RandomReal()+1);
|
|
CMinQP::MinQPCreate(sn,state);
|
|
SetRandomAlgoBC(state);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,false);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
infd=CMath::RandomInteger(5);
|
|
if(infd==0)
|
|
{
|
|
db.Set(j,AL_NEGINF);
|
|
ub.Set(j,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
if(infd==1)
|
|
{
|
|
db.Set(j,AL_NEGINF);
|
|
ub.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
}
|
|
else
|
|
{
|
|
if(infd==2)
|
|
{
|
|
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
ub.Set(j,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
if(infd==3)
|
|
{
|
|
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
ub.Set(j,db[j]+maxstb*CMath::RandomReal()+0.01);
|
|
}
|
|
else
|
|
{
|
|
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
ub.Set(j,db[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CMinQP::MinQPSetBC(state,db,ub);
|
|
//--- initialization for shifting
|
|
//--- initial value for 'XORi'
|
|
//--- and searching true results
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
xori.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
tx.Set(j,CApServ::BoundVal(xori[j],db[j],ub[j]));
|
|
}
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
//--- initialization for starting point
|
|
for(j=0; j<sn; j++)
|
|
stx.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
CMinQP::MinQPSetStartingPoint(state,stx);
|
|
//--- optimize and get result
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x,rep);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
if(MathAbs(tx[j]-x[j])>eps || x[j]<db[j] || x[j]>ub[j])
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', |
|
|
//| 'MinQPSetQuadraticTerm','MinQPSetOrigin','MinQPSetStartingPoint',|
|
|
//| 'MinQPOptimize', 'MinQPResults'. |
|
|
//| Test problem: |
|
|
//| A = positive-definite matrix, obtained by 'SPDMatrixRndCond' |
|
|
//| function |
|
|
//| b <> 0 without bounds random start point dimension-from 1 to 5.|
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::FuncTest1(void)
|
|
{
|
|
//--- create variables
|
|
double eps=0.001;
|
|
int msn=5;
|
|
int c2=1000;
|
|
int nexp=1000;
|
|
CMinQPState state;
|
|
int sn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CMatrixDouble a;
|
|
CRowDouble ub;
|
|
CRowDouble db;
|
|
CRowDouble x;
|
|
CRowDouble tx;
|
|
CRowDouble stx;
|
|
CRowDouble xori;
|
|
CRowDouble xoric;
|
|
CMinQPReport rep;
|
|
CRowDouble b;
|
|
|
|
for(sn=1; sn<=msn; sn++)
|
|
{
|
|
b=vector<double>::Zeros(sn);
|
|
tx=vector<double>::Zeros(sn);
|
|
xori=vector<double>::Zeros(sn);
|
|
xoric=vector<double>::Zeros(sn);
|
|
stx=vector<double>::Zeros(sn);
|
|
for(i=0; i<=nexp; i++)
|
|
{
|
|
//--- create simmetric matrix 'A'
|
|
CMatGen::SPDMatrixRndCond(sn,MathExp(CMath::RandomReal()*MathLog(c2)),a);
|
|
CMinQP::MinQPCreate(sn,state);
|
|
SetRandomAlgoBC(state);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,false);
|
|
for(j=0; j<sn; j++)
|
|
xoric.Set(j,2*CMath::RandomReal()-1);
|
|
//--- create linear part
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
b.Set(j,0);
|
|
for(k=0; k<sn; k++)
|
|
b.Add(j,-xoric[k]*a.Get(k,j));
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
//--- initialization for shifting
|
|
//--- initial value for 'XORi'
|
|
//--- and searching true results
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
xori.Set(j,2*CMath::RandomReal()-1);
|
|
tx.Set(j,xori[j]+xoric[j]);
|
|
}
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
//--- initialization for starting point
|
|
for(j=0; j<sn; j++)
|
|
stx.Set(j,2*CMath::RandomReal()-1);
|
|
CMinQP::MinQPSetStartingPoint(state,stx);
|
|
//--- optimize and get result
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x,rep);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
if(MathAbs(tx[j]-x[j])>eps)
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', |
|
|
//| 'MinQPSetQuadraticTerm', 'MinQPSetBC', 'MinQPSetOrigin', |
|
|
//| 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'. |
|
|
//| Test problem: |
|
|
//| A = positive-definite matrix, obtained by 'SPDMatrixRndCond' |
|
|
//| function |
|
|
//| b <> 0 boundary constraints random start point dimension - |
|
|
//| from 1 to 5. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::FuncTest2(void)
|
|
{
|
|
//--- create variables
|
|
CMinQPState state;
|
|
double eps=0.001;
|
|
int msn=5;
|
|
int nexp=1000;
|
|
int c2=1000;
|
|
double maxstb=10;
|
|
double maxnb=1000;
|
|
double minnb=-1000;
|
|
int sn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int infd=0;
|
|
double anti=0;
|
|
CMatrixDouble a;
|
|
CRowDouble ub;
|
|
CRowDouble db;
|
|
CRowDouble x;
|
|
CRowDouble tmpx;
|
|
CRowDouble stx;
|
|
CRowDouble xori;
|
|
CRowDouble xoric;
|
|
CMinQPReport rep;
|
|
CRowDouble b;
|
|
CRowDouble g;
|
|
CRowDouble c;
|
|
CRowDouble y0;
|
|
CRowDouble y1;
|
|
|
|
for(sn=1; sn<=msn; sn++)
|
|
{
|
|
tmpx=vector<double>::Zeros(sn);
|
|
b=vector<double>::Zeros(sn);
|
|
c=vector<double>::Zeros(sn);
|
|
g=vector<double>::Zeros(sn);
|
|
xori=vector<double>::Zeros(sn);
|
|
xoric=vector<double>::Zeros(sn);
|
|
stx=vector<double>::Zeros(sn);
|
|
db=vector<double>::Zeros(sn);
|
|
ub=vector<double>::Zeros(sn);
|
|
y0=vector<double>::Zeros(sn);
|
|
y1=vector<double>::Zeros(sn);
|
|
for(i=0; i<=nexp; i++)
|
|
{
|
|
//--- create simmetric matrix 'A'
|
|
CMatGen::SPDMatrixRndCond(sn,MathExp(CMath::RandomReal()*MathLog(c2)),a);
|
|
CMinQP::MinQPCreate(sn,state);
|
|
SetRandomAlgoBC(state);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,false);
|
|
for(j=0; j<sn; j++)
|
|
xoric.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
//--- create linear part
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
b.Set(j,0);
|
|
for(k=0; k<sn; k++)
|
|
b.Add(j,-xoric[k]*a.Get(k,j));
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
infd=CMath::RandomInteger(4);
|
|
if(infd==0)
|
|
{
|
|
db.Set(j,AL_NEGINF);
|
|
ub.Set(j,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
if(infd==1)
|
|
{
|
|
db.Set(j,AL_NEGINF);
|
|
ub.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
}
|
|
else
|
|
{
|
|
if(infd==2)
|
|
{
|
|
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
ub.Set(j,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
ub.Set(j,db[j]+maxstb*CMath::RandomReal()+0.01);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CMinQP::MinQPSetBC(state,db,ub);
|
|
//--- initialization for shifting
|
|
//--- initial value for 'XORi'
|
|
//--- and searching true results
|
|
for(j=0; j<sn; j++)
|
|
xori.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
c.Set(j,0);
|
|
for(k=0; k<sn; k++)
|
|
c.Add(j,-xori[k]*a.Get(k,j));
|
|
}
|
|
//--- initialization for starting point
|
|
for(j=0; j<sn; j++)
|
|
stx.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
|
|
CMinQP::MinQPSetStartingPoint(state,stx);
|
|
//--- optimize and get result
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x,rep);
|
|
CAblas::RMatrixMVect(sn,sn,a,0,0,0,x,0,y0,0);
|
|
for(j=0; j<sn; j++)
|
|
g.Set(j,y0[j]+c[j]+b[j]);
|
|
anti=ProjectedAntiGradNorm(sn,x,g,db,ub);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
if(MathAbs(anti)>eps)
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| ConsoleTest. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::ConsoleTest(void)
|
|
{
|
|
//--- create variables
|
|
double eps=0.001;
|
|
int msn=2;
|
|
CMinQPState state;
|
|
int nexp=0;
|
|
int sn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CMatrixDouble a;
|
|
CRowDouble ub;
|
|
CRowDouble db;
|
|
CRowDouble x;
|
|
CRowDouble stx;
|
|
CRowDouble xori;
|
|
CRowDouble xoric;
|
|
CMinQPReport rep;
|
|
CRowDouble b;
|
|
CRowDouble g;
|
|
CRowDouble y0;
|
|
CRowDouble y1;
|
|
double c=0;
|
|
double anti=0;
|
|
|
|
for(sn=2; sn<=msn; sn++)
|
|
{
|
|
b=vector<double>::Zeros(sn);
|
|
g=vector<double>::Zeros(sn);
|
|
xori=vector<double>::Zeros(sn);
|
|
xoric=vector<double>::Zeros(sn);
|
|
stx=vector<double>::Zeros(sn);
|
|
db=vector<double>::Zeros(sn);
|
|
ub=vector<double>::Zeros(sn);
|
|
y0=vector<double>::Zeros(sn);
|
|
y1=vector<double>::Zeros(sn);
|
|
for(i=0; i<=nexp; i++)
|
|
{
|
|
//--- create simmetric matrix 'A'
|
|
a=matrix<double>::Identity(sn,sn);
|
|
CMinQP::MinQPCreate(sn,state);
|
|
SetRandomAlgoBC(state);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,false);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
xoric.Set(j,1);
|
|
PrintFormat("XoriC=%0.5f \n",xoric[j]);
|
|
}
|
|
//--- create linear part
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
b.Set(j,0);
|
|
for(k=0; k<sn; k++)
|
|
b.Add(j,-xoric[k]*a.Get(k,j));
|
|
PrintFormat("B[%d]=%0.5f\n",j,b[j]);
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
db.Set(j,10);
|
|
ub.Set(j,20);
|
|
}
|
|
CMinQP::MinQPSetBC(state,db,ub);
|
|
//--- initialization for shifting
|
|
//--- initial value for 'XORi'
|
|
//--- and searching true results
|
|
for(j=0; j<sn; j++)
|
|
xori.Set(j,1);
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
//--- optimize and get result
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x,rep);
|
|
CAblas::RMatrixMVect(sn,sn,a,0,0,0,x,0,y0,0);
|
|
CAblas::RMatrixMVect(sn,sn,a,0,0,0,x,0,y1,0);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
c=0;
|
|
for(k=0; k<sn; k++)
|
|
c-=xori[k]*a.Get(k,j);
|
|
g.Set(j,b[j]+c+y0[j]+y1[j]);
|
|
}
|
|
anti=ProjectedAntiGradNorm(sn,x,b,db,ub);
|
|
PrintFormat("SN=%d\n",sn);
|
|
PrintFormat("NEXP=%d\n",i);
|
|
PrintFormat("TermType=%d\n",rep.m_terminationtype);
|
|
for(j=0; j<sn; j++)
|
|
{
|
|
PrintFormat("X[%d]=%0.5f;\n",j,x[j]);
|
|
PrintFormat("DB[%d]=%0.5f; UB[%d]=%0.5f\n",j,db[j],j,ub[j]);
|
|
PrintFormat("XORi[%d]=%0.5f; XORiC[%d]=%0.5f;\n",j,xori[j],j,xoric[j]);
|
|
PrintFormat("Anti[%d]=%0.5f;\n",j,anti);
|
|
if(MathAbs(anti)>eps)
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function performs tests specific for QuickQP solver |
|
|
//| Returns True on failure. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::QuickQPTests(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double g=0;
|
|
double gnorm=0;
|
|
bool flag;
|
|
int origintype=0;
|
|
int scaletype=0;
|
|
bool isupper;
|
|
bool issparse;
|
|
int itscnt=0;
|
|
CRowInt nlist;
|
|
int nidx=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble za;
|
|
CMatrixDouble fulla;
|
|
CMatrixDouble halfa;
|
|
CMatrixDouble c;
|
|
CSparseMatrix sa;
|
|
CRowInt ct;
|
|
CRowDouble b;
|
|
CRowDouble zb;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble xend0;
|
|
CRowDouble xend1;
|
|
CRowDouble xori;
|
|
CRowDouble xz;
|
|
CRowDouble s;
|
|
double eps=0;
|
|
CHighQualityRandState rs;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Convex unconstrained test:
|
|
//--- * N dimensions
|
|
//--- * positive-definite A
|
|
//--- * algorithm randomly choose dense or sparse A, and for
|
|
//--- sparse matrix it randomly choose format.
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- * random origin (zero or non-zero) and scale (unit or
|
|
//--- non-unit) are generated
|
|
eps=1.0E-5;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
origintype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xori=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(origintype==0)
|
|
xori.Set(i,0);
|
|
else
|
|
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
//--- Solve problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(issparse)
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
if(origintype!=0)
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
if(scaletype!=0)
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5703");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i];
|
|
for(j=0; j<n; j++)
|
|
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
|
|
gnorm+=CMath::Sqr(g);
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5721");
|
|
}
|
|
}
|
|
//--- Convex test:
|
|
//--- * N dimensions
|
|
//--- * random number (0..N) of random boundary constraints
|
|
//--- * positive-definite A
|
|
//--- * algorithm randomly choose dense or sparse A, and for
|
|
//--- sparse matrix it randomly choose format.
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- * random origin (zero or non-zero) and scale (unit or
|
|
//--- non-unit) are generated
|
|
eps=1.0E-5;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
origintype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xori=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(origintype==0)
|
|
xori.Set(i,0);
|
|
else
|
|
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
|
|
j=CHighQualityRand::HQRndUniformI(rs,5);
|
|
if(j==0)
|
|
{
|
|
bndl.Set(i,0);
|
|
x0.Set(i,MathAbs(x0[i]));
|
|
}
|
|
if(j==1)
|
|
{
|
|
bndu.Set(i,0);
|
|
x0.Set(i,-MathAbs(x0[i]));
|
|
}
|
|
if(j==2)
|
|
{
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]);
|
|
x0.Set(i,bndl[i]);
|
|
}
|
|
if(j==3)
|
|
{
|
|
bndl.Set(i,-0.1);
|
|
bndu.Set(i,0.1);
|
|
x0.Set(i,0.2*CHighQualityRand::HQRndUniformR(rs)-0.1);
|
|
}
|
|
}
|
|
//--- Solve problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(issparse)
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
if(origintype!=0)
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
if(scaletype!=0)
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5818");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i];
|
|
for(j=0; j<n; j++)
|
|
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
|
|
if(x1[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x1[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:5838");
|
|
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:5839");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5842");
|
|
}
|
|
}
|
|
//--- Strongly non-convex test:
|
|
//--- * N dimensions, N>=2
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
|
|
//--- singular value equal to 1.0E-3, I is identity matrix
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- We perform two tests:
|
|
//--- * unconstrained problem must be recognized as unbounded
|
|
//--- * constrained problem can be successfully solved
|
|
//--- NOTE: it is important to have N>=2, because formula for A
|
|
//--- can be applied only to matrix with at least two
|
|
//--- singular values
|
|
eps=1.0E-5;
|
|
for(n=2; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
for(i=0; i<n; i++)
|
|
fulla.Add(i,i,-0.5);
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:5907");
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5911");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,fulla,i);
|
|
g=v+b[i];
|
|
if(x1[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x1[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:5930");
|
|
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:5931");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5934");
|
|
}
|
|
}
|
|
//--- Basic semi-definite test:
|
|
//--- * N dimensions, N>=2
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- [ 1 1 ... 1 1 ]
|
|
//--- * A = [ ... ... ... ], with one (random) diagonal entry set to -1
|
|
//--- [ 1 1 ... 1 1 ]
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- We perform two tests:
|
|
//--- * unconstrained problem must be recognized as unbounded
|
|
//--- * constrained problem must be recognized as bounded and
|
|
//--- successfully solved
|
|
//--- Both problems require subtle programming when we work
|
|
//--- with semidefinite QP.
|
|
//--- NOTE: unlike BLEIC-QP algorthm, QQP may detect unboundedness
|
|
//--- of the problem when started from any x0, with any b.
|
|
//--- BLEIC-based solver requires carefully chosen x0 and b
|
|
//--- to find direction of zero curvature, but this solver
|
|
//--- can find it from any point.
|
|
nlist.Resize(12);
|
|
nlist.Set(0,2);
|
|
nlist.Set(1,3);
|
|
nlist.Set(2,4);
|
|
nlist.Set(3,5);
|
|
nlist.Set(4,6);
|
|
nlist.Set(5,7);
|
|
nlist.Set(6,8);
|
|
nlist.Set(7,9);
|
|
nlist.Set(8,10);
|
|
nlist.Set(9,20);
|
|
nlist.Set(10,40);
|
|
nlist.Set(11,80);
|
|
eps=1.0E-5;
|
|
for(nidx=0; nidx<CAp::Len(nlist); nidx++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
n=nlist[nidx];
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
while(b[i]==0.0);
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
a=matrix<double>::Ones(n,n);
|
|
j=CHighQualityRand::HQRndUniformI(rs,n);
|
|
a.Set(j,j,-1.0);
|
|
DenseToSparse(a,n,sa);
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6017");
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6021");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i]+CAblasF::RDotVR(n,x1,a,i);
|
|
if(x1[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x1[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:6042");
|
|
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:6043");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6046");
|
|
}
|
|
}
|
|
//--- Linear (zero-quadratic) test:
|
|
//--- * N dimensions, N>=1
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- * A = 0
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- We perform two tests:
|
|
//--- * unconstrained problem must be recognized as unbounded
|
|
//--- * constrained problem can be successfully solved
|
|
//--- NOTE: we may explicitly set zero A, or assume that by
|
|
//--- default it is zero. During test we will try both
|
|
//--- ways.
|
|
eps=1.0E-5;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
while(b[i]==0.0);
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
a=matrix<double>::Zeros(n,n);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
}
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6104");
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6108");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(result,b[i]>0.0 && x1[i]>bndl[i],"testminqpunit.ap:6118");
|
|
CAp::SetErrorFlag(result,b[i]<0.0 && x1[i]<bndu[i],"testminqpunit.ap:6119");
|
|
}
|
|
}
|
|
}
|
|
//--- Test for Newton phase of QQP algorithm - we test that Newton
|
|
//--- phase can find good solution within one step. In order to do
|
|
//--- so we:
|
|
//--- * solve convex QP problem (dense or sparse)
|
|
//--- * with K<=N equality-only constraints ai=x=bi
|
|
//--- * with number of outer iterations limited to just 1
|
|
//--- * and with CG phase turned off (we modify internal structures
|
|
//--- of the QQP solver in order to make it)
|
|
eps=1.0E-5;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
n=50+CHighQualityRand::HQRndUniformI(rs,51);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
DenseToSparse(a,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]);
|
|
}
|
|
}
|
|
//--- Solve problem
|
|
//--- NOTE: we modify internal structures of QQP solver in order
|
|
//--- to deactivate CG phase
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,1,true);
|
|
state.m_qqpsettingsuser.m_cgphase=false;
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CHighQualityRand::HQRndNormal(rs)>0.0);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,CHighQualityRand::HQRndNormal(rs)>0.0);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6179");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i]+CAblasF::RDotVR(n,x1,a,i);
|
|
if(x1[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x1[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:6199");
|
|
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:6200");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6203");
|
|
}
|
|
//--- Test for Newton phase of QQP algorithm - we test that Newton
|
|
//--- updates work correctly, i.e. that CNewtonUpdate() internal
|
|
//--- function correctly updates inverse Hessian matrix.
|
|
//--- To test it we:
|
|
//--- * solve ill conditioned convex QP problem
|
|
//--- * with unconstrained solution XZ whose components are within [-0.5,+0.5]
|
|
//--- * with one inequality constraint X[k]>=5
|
|
//--- * with initial point such that:
|
|
//--- * X0[i] = 100 for i<>k
|
|
//--- * X0[k] = 5+1.0E-5
|
|
//--- * with number of outer iterations limited to just 1
|
|
//--- * and with CG phase turned off (we modify internal structures
|
|
//--- of the QQP solver in order to make it)
|
|
//--- The idea is that single Newton step is not enough to find solution,
|
|
//--- but with just one update we can move exactly to the solution.
|
|
//--- We perform two tests:
|
|
//--- * first one with State.QQP.NewtMaxIts set to 1, in order to
|
|
//--- make sure that algorithm fails with just one iteration
|
|
//--- * second one with State.QQP.NewtMaxIts set to 2, in order to
|
|
//--- make sure that algorithm converges when it can perform update
|
|
eps=1.0E-5;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
n=20+CHighQualityRand::HQRndUniformI(rs,20);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E5,a);
|
|
DenseToSparse(a,n,sa);
|
|
CSparse::SparseConvertToCRS(sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xz=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xz.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x0.Set(i,100);
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
k=CHighQualityRand::HQRndUniformI(rs,n);
|
|
x0.Set(k,5.00001);
|
|
bndl.Set(k,5.0);
|
|
CSparse::SparseMV(sa,xz,b);
|
|
b*=-1.0;
|
|
//--- Create solver
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,1,true);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CHighQualityRand::HQRndNormal(rs)>0.0);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
//--- Solve problem. First time, with no Newton updates.
|
|
//--- It must fail.
|
|
//--- NOTE: we modify internal structures of QQP solver in order
|
|
//--- to deactivate CG phase and turn off Newton updates.
|
|
state.m_qqpsettingsuser.m_cgphase=false;
|
|
state.m_qqpsettingsuser.m_cnphase=true;
|
|
state.m_qqpsettingsuser.m_cnmaxupdates=0;
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6282");
|
|
if(result)
|
|
return(result);
|
|
flag=false;
|
|
TestBCGradAndFeasibility(a,b,bndl,bndu,n,x1,eps,flag);
|
|
CAp::SetErrorFlag(result,!flag,"testminqpunit.ap:6287");
|
|
//--- Now with Newton updates - it must succeeed.
|
|
state.m_qqpsettingsuser.m_cgphase=false;
|
|
state.m_qqpsettingsuser.m_cnmaxupdates=n;
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6296");
|
|
if(result)
|
|
return(result);
|
|
flag=false;
|
|
TestBCGradAndFeasibility(a,b,bndl,bndu,n,x1,eps,flag);
|
|
CAp::SetErrorFlag(result,flag,"testminqpunit.ap:6301");
|
|
}
|
|
//--- Check that problem with general constraints results in
|
|
//--- correct error code (-5 should be returned).
|
|
c=matrix<double>::Ones(1,3);
|
|
ct.Resize(1);
|
|
c.Set(0,2,2.0);
|
|
ct.Set(0,0);
|
|
CMinQP::MinQPCreate(2,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLC(state,c,ct,1);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-5,"testminqpunit.ap:6319");
|
|
//--- Test sparse functionality. QQP solver must perform
|
|
//--- same steps independently of matrix type (dense or sparse).
|
|
//--- We generate random unconstrained test problem and solve it
|
|
//--- twice - first time we solve dense version, second time -
|
|
//--- sparse version is solved.
|
|
//--- During this test we:
|
|
//--- * use stringent stopping criteria (one outer iteration)
|
|
//--- * turn off Newton phase of the algorithm to slow down
|
|
//--- convergence
|
|
eps=1.0E-3;
|
|
itscnt=1;
|
|
n=20;
|
|
isupper=CMath::RandomReal()>0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,za);
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(j>=i && isupper)
|
|
{
|
|
CSparse::SparseSet(sa,i,j,za.Get(i,j));
|
|
a.Set(i,j,za.Get(i,j));
|
|
}
|
|
if(j<=i && !isupper)
|
|
{
|
|
CSparse::SparseSet(sa,i,j,za.Get(i,j));
|
|
a.Set(i,j,za.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
b=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CApServ::RandomNormal());
|
|
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,isupper);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend1,rep);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(xend0[i]-xend1[i])>eps,"testminqpunit.ap:6437");
|
|
//--- Test scale-invariance. QQP performs same steps on scaled and
|
|
//--- unscaled problems (assuming that scale of the variables is known).
|
|
//--- We generate random scale matrix S and random well-conditioned and
|
|
//--- well scaled matrix A. Then we solve two problems:
|
|
//--- (1) f = 0.5*x'*A*x+b'*x
|
|
//--- (identity scale matrix is used)
|
|
//--- and
|
|
//--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
|
|
//--- (scale matrix S is used)
|
|
//--- Solution process is started from X=0, we perform ItsCnt=1 outer
|
|
//--- iterations with Newton phase turned off (to slow down convergence;
|
|
//--- we want to prevent algorithm from converging to exact solution which
|
|
//--- is exactly same for both problems; the idea is to test that same
|
|
//--- intermediate tests are taken).
|
|
//--- As result, we must get S*x=y
|
|
eps=1.0E-3;
|
|
itscnt=1;
|
|
n=100;
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
za=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
za.Set(i,j,a.Get(i,j)/(s[i]*s[j]));
|
|
}
|
|
b=vector<double>::Zeros(n);
|
|
zb=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CApServ::RandomNormal());
|
|
zb.Set(i,b[i]/s[i]);
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
|
|
CMinQP::MinQPSetLinearTerm(state,zb);
|
|
CMinQP::MinQPSetQuadraticTerm(state,za,true);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend1,rep);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(s[i]*xend0[i]-xend1[i])>eps,"testminqpunit.ap:6494");
|
|
//--- Test that QQP can efficiently use sparse matrices (i.e. it is
|
|
//--- not disguised version of some dense QP solver). In order to test
|
|
//--- it we create very large and very sparse problem (diagonal matrix
|
|
//--- with N=40.000) and perform 10 iterations of QQP solver.
|
|
//--- In case QP solver uses some form of dense linear algebra to solve
|
|
//--- this problem, it will take TOO much time to solve it. And we will
|
|
//--- notice it by EXTREME slowdown during testing.
|
|
n=40000;
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
CSparse::SparseSet(sa,i,i,MathPow(10.0,-(3*CMath::RandomReal())));
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,CApServ::RandomNormal());
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,10,CHighQualityRand::HQRndUniformR(rs)>0.5);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function performs tests specific for BLEIC solver |
|
|
//| Returns True on error, False on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::BLEICTests(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
CRowInt nlist;
|
|
int nidx=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble za;
|
|
CMatrixDouble c;
|
|
CRowDouble b;
|
|
CRowDouble zb;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble s;
|
|
CRowDouble x;
|
|
CRowInt ct;
|
|
CSparseMatrix sa;
|
|
int n=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CHighQualityRandState rs;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
CRowDouble xend0;
|
|
CRowDouble xend1;
|
|
double eps=0;
|
|
double v=0;
|
|
double g=0;
|
|
double gnorm=0;
|
|
int itscnt=0;
|
|
bool isupper;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test sparse functionality. BLEIC-based solver must perform
|
|
//--- same steps independently of matrix type (dense or sparse).
|
|
//--- We generate random unconstrained test problem and solve it
|
|
//--- twice - first time we solve dense version, second time -
|
|
//--- sparse version is solved.
|
|
eps=1.0E-3;
|
|
itscnt=5;
|
|
n=20;
|
|
isupper=CMath::RandomReal()>0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,za);
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
a=matrix<double>::Zeros(n,n);
|
|
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(j>=i && isupper)
|
|
{
|
|
CSparse::SparseSet(sa,i,j,za.Get(i,j));
|
|
a.Set(i,j,za.Get(i,j));
|
|
}
|
|
if(j<=i && !isupper)
|
|
{
|
|
CSparse::SparseSet(sa,i,j,za.Get(i,j));
|
|
a.Set(i,j,za.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
b=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CApServ::RandomNormal());
|
|
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,isupper);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend1,rep);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(xend0[i]-xend1[i])>eps,"testminqpunit.ap:6609");
|
|
//--- Test scale-invariance. BLEIC performs same steps on scaled and
|
|
//--- unscaled problems (assuming that scale of the variables is known).
|
|
//--- We generate random scale matrix S and random well-conditioned and
|
|
//--- well scaled matrix A. Then we solve two problems:
|
|
//--- (1) f = 0.5*x'*A*x+b'*x
|
|
//--- (identity scale matrix is used)
|
|
//--- and
|
|
//--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
|
|
//--- (scale matrix S is used)
|
|
//--- Solution process is started from X=0, we perform ItsCnt=5 steps.
|
|
//--- As result, we must get S*x=y
|
|
eps=1.0E-3;
|
|
itscnt=5;
|
|
n=20;
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
za=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
za.Set(i,j,a.Get(i,j)/(s[i]*s[j]));
|
|
}
|
|
b=vector<double>::Zeros(n);
|
|
zb=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CApServ::RandomNormal());
|
|
zb.Set(i,b[i]/s[i]);
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
|
|
CMinQP::MinQPSetLinearTerm(state,zb);
|
|
CMinQP::MinQPSetQuadraticTerm(state,za,true);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend1,rep);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(s[i]*xend0[i]-xend1[i])>eps,"testminqpunit.ap:6662");
|
|
//--- Test that BLEIC can efficiently use sparse matrices (i.e. it is
|
|
//--- not disguised version of some dense QP solver). In order to test
|
|
//--- it we create very large and very sparse problem (diagonal matrix
|
|
//--- with N=20.000) and perform 10 iterations of BLEIC-based QP solver.
|
|
//--- In case QP solver uses some form of dense linear algebra to solve
|
|
//--- this problem, it will take TOO much time to solve it. And we will
|
|
//--- notice it by EXTREME slowdown during testing.
|
|
n=20000;
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
CSparse::SparseSet(sa,i,i,MathPow(10.0,-(3*CMath::RandomReal())));
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,CApServ::RandomNormal());
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,10);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
//--- Special semi-definite test:
|
|
//--- * N dimensions, N>=2 (important!)
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- [ 1 1 ... 1 1 ]
|
|
//--- * A = [ ... ... ... ]
|
|
//--- [ 1 1 ... 1 1 ]
|
|
//--- * random B such that SUM(b[i])=0.0 (important!)
|
|
//--- * initial point x0 is chosen in such way that SUM(x[i])=0.0
|
|
//--- (important!)
|
|
//--- We perform two tests:
|
|
//--- * unconstrained problem must be recognized as unbounded
|
|
//--- (when starting from x0!)
|
|
//--- * constrained problem must be recognized as bounded
|
|
//--- and successfully solved
|
|
//--- Both problems require subtle programming when we work
|
|
//--- with semidefinite QP.
|
|
//--- NOTE: it is very important to have N>=2 (otherwise problem
|
|
//--- will be bounded from below even without boundary
|
|
//--- constraints) and to have x0/b0 such that sum of
|
|
//--- components is zero (such x0 is exact minimum of x'*A*x,
|
|
//--- which allows algorithm to find direction of zero curvature
|
|
//--- at the very first step). If x0/b are chosen in other way,
|
|
//--- algorithm may be unable to find direction of zero
|
|
//--- curvature and will cycle forever, slowly decreasing
|
|
//--- function value at each iteration.
|
|
//--- This is major difference from similar test for QQP solver -
|
|
//--- QQP can find direction of zero curvature from almost any
|
|
//--- point due to internal CG solver which favors such directions.
|
|
//--- BLEIC uses LBFGS, which is less able to find direction of
|
|
//--- zero curvature.
|
|
nlist.Resize(12);
|
|
nlist.Set(0,2);
|
|
nlist.Set(1,3);
|
|
nlist.Set(2,4);
|
|
nlist.Set(3,5);
|
|
nlist.Set(4,6);
|
|
nlist.Set(5,7);
|
|
nlist.Set(6,8);
|
|
nlist.Set(7,9);
|
|
nlist.Set(8,10);
|
|
nlist.Set(9,20);
|
|
nlist.Set(10,40);
|
|
nlist.Set(11,80);
|
|
eps=1.0E-5;
|
|
for(nidx=0; nidx<CAp::Len(nlist); nidx++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
n=nlist[nidx];
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
while(b[i]==0.0);
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
x0-=x0.Sum()/(double)n;
|
|
b-=b.Sum()/(double)n;
|
|
a=matrix<double>::Ones(n,n);
|
|
DenseToSparse(a,n,sa);
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,0);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6790");
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6794");
|
|
if(rep.m_terminationtype<=0)
|
|
return(result);
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i]+CAblasF::RDotVR(n,x1,a,i);
|
|
if(x1[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x1[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:6814");
|
|
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:6815");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6818");
|
|
}
|
|
}
|
|
//--- Test that BLEIC-based QP solver can solve non-convex problems
|
|
//--- which are bounded from below on the feasible set:
|
|
//--- min -||x||^2 s.t. x[i] in [-1,+1]
|
|
//--- We also test ability of the solver to detect unbounded problems
|
|
//--- (we remove one of the constraints and repeat solution process).
|
|
n=20;
|
|
eps=1.0E-14;
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
CSparse::SparseSet(sa,i,i,-1);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x.Set(i,CMath::RandomReal()-0.5);
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetStartingPoint(state,x);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6851");
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,xend0[i]!=(-1.0) && xend0[i]!=1.0,"testminqpunit.ap:6854");
|
|
}
|
|
i=CMath::RandomInteger(n);
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6861");
|
|
//--- Test that BLEIC-based QP solver can solve non-convex problems
|
|
//--- which are bounded from below on the feasible set:
|
|
//--- min -||x||^2 s.t. x[i] in [-1,+1],
|
|
//--- with inequality constraints handled as general linear ones
|
|
//--- We also test ability of the solver to detect unbounded problems
|
|
//--- (we remove last pair of constraints and try to solve modified
|
|
//--- problem).
|
|
n=20;
|
|
eps=1.0E-14;
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
CSparse::SparseSet(sa,i,i,-1);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(2*i,i,1.0);
|
|
c.Set(2*i,n,1.0);
|
|
ct.Set(2*i,-1);
|
|
c.Set(2*i+1,i,1.0);
|
|
c.Set(2*i+1,n,-1.0);
|
|
ct.Set(2*i+1,1);
|
|
}
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,CMath::RandomReal()-0.5);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPSetLC(state,c,ct,2*n);
|
|
CMinQP::MinQPSetStartingPoint(state,x);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6906");
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(xend0[i]+1)>(100.0*CMath::m_machineepsilon) && MathAbs(xend0[i]-1)>(100.0*CMath::m_machineepsilon),"testminqpunit.ap:6909");
|
|
}
|
|
CMinQP::MinQPSetLC(state,c,ct,2*(n-1));
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6913");
|
|
//--- Test that BLEIC-based QP solver can solve QP problems with
|
|
//--- zero quadratic term:
|
|
//--- min b'*x s.t. x[i] in [-1,+1]
|
|
//--- It means that QP solver can be used as linear programming solver
|
|
//--- (altough performance of such solver is worse than that of specialized
|
|
//--- LP solver).
|
|
//--- NOTE: we perform this test twice - first time without explicitly setting
|
|
//--- quadratic term (we test that default quadratic term is zero), and
|
|
//--- second time - with explicitly set quadratic term.
|
|
n=20;
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
b.Set(i,CApServ::RandomNormal());
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6947");
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(result,b[i]>0.0 && xend0[i]!=bndl[i],"testminqpunit.ap:6951");
|
|
CAp::SetErrorFlag(result,b[i]<0.0 && xend0[i]!=bndu[i],"testminqpunit.ap:6952");
|
|
}
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6961");
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(result,b[i]>0.0 && xend0[i]!=bndl[i],"testminqpunit.ap:6965");
|
|
CAp::SetErrorFlag(result,b[i]<0.0 && xend0[i]!=bndu[i],"testminqpunit.ap:6966");
|
|
}
|
|
}
|
|
//--- Test specific problem sent by V.Semenenko, which resulted in
|
|
//--- the initinite loop in FindFeasiblePoint (before fix). We do
|
|
//--- not test results returned by solver - simply being able to
|
|
//--- Stop is enough for this test.
|
|
//--- NOTE: it is important that modifications to problem are applied
|
|
//--- sequentially. Test fails after 100-5000 such modifications.
|
|
//--- One modification is not enough to cause failure.
|
|
n=3;
|
|
a=matrix<double>::Zeros(n,n);
|
|
a.Set(0,0,1.222990);
|
|
a.Set(1,1,1.934900);
|
|
a.Set(2,2,0.603924);
|
|
b=vector<double>::Zeros(n);
|
|
b.Set(0,-4.97245);
|
|
b.Set(1,-9.09039);
|
|
b.Set(2,-4.63856);
|
|
c=matrix<double>::Zeros(8,n+1);
|
|
c.Set(0,0,1);
|
|
c.Set(0,n,4.94298);
|
|
c.Set(1,0,1);
|
|
c.Set(1,n,4.79981);
|
|
c.Set(2,1,1);
|
|
c.Set(2,n,-0.4848);
|
|
c.Set(3,1,1);
|
|
c.Set(3,n,-0.73804);
|
|
c.Set(4,2,1);
|
|
c.Set(4,n,0.575729);
|
|
c.Set(5,2,1);
|
|
c.Set(5,n,0.458645);
|
|
c.Set(6,0,1);
|
|
c.Set(6,2,-1);
|
|
c.Set(6,n,-0.0546574);
|
|
c.Set(7,0,1);
|
|
c.Set(7,2,-1);
|
|
c.Set(7,n,-0.5900440);
|
|
ct.Resize(8);
|
|
ct.Set(0,-1);
|
|
ct.Set(1,1);
|
|
ct.Set(2,-1);
|
|
ct.Set(3,1);
|
|
ct.Set(4,-1);
|
|
ct.Set(5,1);
|
|
ct.Set(6,-1);
|
|
ct.Set(7,1);
|
|
s=vector<double>::Zeros(n);
|
|
s.Set(0,0.143171);
|
|
s.Set(1,0.253240);
|
|
s.Set(2,0.117084);
|
|
x0=vector<double>::Zeros(n);
|
|
x0.Set(0,3.51126);
|
|
x0.Set(1,4.05731);
|
|
x0.Set(2,6.63307);
|
|
for(pass=1; pass<=10000; pass++)
|
|
{
|
|
//--- Apply random distortion
|
|
for(j=0; j<n; j++)
|
|
b.Add(j,(2*CHighQualityRand::HQRndUniformI(rs,2)-1)*0.1);
|
|
for(j=0; j<6; j++)
|
|
c.Add(j,n,(2*CHighQualityRand::HQRndUniformI(rs,2)-1)*0.1);
|
|
//--- Solve
|
|
CMinQP::MinQPCreate(3,state);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetLC(state,c,ct,8);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,0);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests bound constrained quadratic programming |
|
|
//| algorithm. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::BCQPTest(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double g=0;
|
|
double gnorm=0;
|
|
int origintype=0;
|
|
int scaletype=0;
|
|
bool isupper;
|
|
bool issparse;
|
|
double bctol=0;
|
|
double lctol=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble fulla;
|
|
CMatrixDouble halfa;
|
|
CMatrixDouble c;
|
|
CSparseMatrix sa;
|
|
CRowInt ct;
|
|
CRowDouble b;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble gtrial;
|
|
double vl=0;
|
|
double vu=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble xori;
|
|
CRowDouble xz;
|
|
CRowDouble s;
|
|
double eps=0;
|
|
CHighQualityRandState rs;
|
|
int solvertype=0;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Convex test:
|
|
//--- * N dimensions
|
|
//--- * random number (0..N) of random boundary constraints
|
|
//--- * positive-definite A
|
|
//--- * algorithm randomly choose dense or sparse A, and for
|
|
//--- sparse matrix it randomly choose format.
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- * random origin (zero or non-zero) and scale (unit or
|
|
//--- non-unit) are generated
|
|
eps=1.0E-3;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
origintype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xori=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(origintype==0)
|
|
xori.Set(i,0);
|
|
else
|
|
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
|
|
j=CHighQualityRand::HQRndUniformI(rs,5);
|
|
if(j==0)
|
|
{
|
|
bndl.Set(i,0);
|
|
x0.Set(i,MathAbs(x0[i]));
|
|
}
|
|
if(j==1)
|
|
{
|
|
bndu.Set(i,0);
|
|
x0.Set(i,-MathAbs(x0[i]));
|
|
}
|
|
if(j==2)
|
|
{
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]);
|
|
x0.Set(i,bndl[i]);
|
|
}
|
|
if(j==3)
|
|
{
|
|
bndl.Set(i,-0.1);
|
|
bndu.Set(i,0.1);
|
|
x0.Set(i,0.2*CHighQualityRand::HQRndUniformR(rs)-0.1);
|
|
}
|
|
}
|
|
//--- Solve problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
solvertype=SetRandomAlgoAllModern(state,bctol,lctol);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
if(issparse)
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
if(origintype!=0)
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
if(scaletype!=0)
|
|
CMinQP::MinQPSetScale(state,s);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
else
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CMinQP::MinQPSetBCI(state,i,bndl[i],bndu[i]);
|
|
}
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:882");
|
|
CAp::SetErrorFlag(waserrors,CAp::Len(x1)<n,"testminqpunit.ap:883");
|
|
CAp::SetErrorFlag(waserrors,CAp::Len(rep.m_lagbc)<n,"testminqpunit.ap:884");
|
|
if(waserrors)
|
|
return;
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i];
|
|
for(j=0; j<n; j++)
|
|
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
|
|
if(x1[i]<=(bndl[i]+bctol) && g>0.0)
|
|
g=0;
|
|
if(x1[i]>=(bndu[i]-bctol) && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(waserrors,x1[i]<bndl[i],"testminqpunit.ap:904");
|
|
CAp::SetErrorFlag(waserrors,x1[i]>bndu[i],"testminqpunit.ap:905");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:908");
|
|
//--- Test Lagrange multipliers returned by the solver (we skip
|
|
//--- BLEIC solver because it does not return Lagrange vals).
|
|
if(solvertype!=-1 && solvertype!=0)
|
|
{
|
|
gtrial=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=b[i];
|
|
for(j=0; j<n; j++)
|
|
v+=fulla.Get(i,j)*(x1[j]-xori[j]);
|
|
gtrial.Set(i,v);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
gtrial.Add(i,rep.m_lagbc[i]);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(gtrial[i]))>1.0E-3,"testminqpunit.ap:928");
|
|
}
|
|
}
|
|
}
|
|
//--- Convex test, same box constraints for all variables:
|
|
//--- * N dimensions
|
|
//--- * box constraints set with MinQPSetBCAll()
|
|
//--- * positive-definite A
|
|
//--- * algorithm randomly choose dense or sparse A, and for
|
|
//--- sparse matrix it randomly choose format.
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- * random origin (zero or non-zero) and scale (unit or
|
|
//--- non-unit) are generated
|
|
eps=1.0E-3;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
origintype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xori=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(origintype==0)
|
|
xori.Set(i,0);
|
|
else
|
|
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
vl=AL_NEGINF;
|
|
vu=AL_POSINF;
|
|
j=CHighQualityRand::HQRndUniformI(rs,5);
|
|
switch(j)
|
|
{
|
|
case 0:
|
|
vl=0;
|
|
break;
|
|
case 1:
|
|
vu=0;
|
|
break;
|
|
case 2:
|
|
vl=CHighQualityRand::HQRndNormal(rs);
|
|
vu=vl;
|
|
break;
|
|
case 3:
|
|
vl=-0.1-MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
vu=0.1+MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
break;
|
|
}
|
|
//--- Solve problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoAllModern(state,bctol,lctol);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
if(issparse)
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
if(origintype!=0)
|
|
CMinQP::MinQPSetOrigin(state,xori);
|
|
if(scaletype!=0)
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPSetBCAll(state,vl,vu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1017");
|
|
if(rep.m_terminationtype<=0)
|
|
return;
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i];
|
|
for(j=0; j<n; j++)
|
|
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
|
|
if(x1[i]<=(vl+bctol) && g>0.0)
|
|
g=0;
|
|
if(x1[i]>=(vu-bctol) && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(waserrors,x1[i]<vl,"testminqpunit.ap:1037");
|
|
CAp::SetErrorFlag(waserrors,x1[i]>vu,"testminqpunit.ap:1038");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1041");
|
|
}
|
|
}
|
|
//--- Semidefinite test:
|
|
//--- * N dimensions
|
|
//--- * nonnegativity constraints
|
|
//--- * A = [ 1 1 ... 1 1 ; 1 1 ... 1 1 ; .... ; 1 1 ... 1 1 ]
|
|
//--- * algorithm randomly choose dense or sparse A, and for
|
|
//--- sparse matrix it randomly choose format.
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
eps=1.0E-4;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
fulla=matrix<double>::Zeros(n,n);
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
fulla.Set(i,j,1.0);
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,CHighQualityRand::HQRndUniformI(rs,2));
|
|
}
|
|
//--- Solve problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoSemiDefinite(state,bctol,lctol);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(issparse)
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1101");
|
|
if(rep.m_terminationtype<=0)
|
|
return;
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i];
|
|
for(j=0; j<n; j++)
|
|
g+=fulla.Get(i,j)*x1[j];
|
|
if(x1[i]<=(bndl[i]+bctol) && g>0.0)
|
|
g=0;
|
|
if(x1[i]>=(bndu[i]-bctol) && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(waserrors,x1[i]<bndl[i],"testminqpunit.ap:1121");
|
|
CAp::SetErrorFlag(waserrors,x1[i]>bndu[i],"testminqpunit.ap:1122");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1125");
|
|
}
|
|
}
|
|
//--- Non-convex test:
|
|
//--- * N dimensions, N>=2
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
|
|
//--- singular value equal to 1.0E-3, I is identity matrix
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- We perform two tests:
|
|
//--- * unconstrained problem must be recognized as unbounded
|
|
//--- * constrained problem can be successfully solved
|
|
//--- NOTE: it is important to have N>=2, because formula for A
|
|
//--- can be applied only to matrix with at least two
|
|
//--- singular values
|
|
eps=1.0E-4;
|
|
for(n=2; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
for(i=0; i<n; i++)
|
|
fulla.Add(i,i,-0.5);
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
|
|
halfa=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((j>=i && isupper) || (j<=i && !isupper))
|
|
halfa.Set(i,j,fulla.Get(i,j));
|
|
else
|
|
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
DenseToSparse(halfa,n,sa);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoNonConvex(state);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
|
|
else
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-4,"testminqpunit.ap:1192");
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1196");
|
|
if(rep.m_terminationtype<=0)
|
|
return;
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,fulla,i);
|
|
g=v+b[i];
|
|
if(x1[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x1[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
CAp::SetErrorFlag(waserrors,x1[i]<bndl[i],"testminqpunit.ap:1215");
|
|
CAp::SetErrorFlag(waserrors,x1[i]>bndu[i],"testminqpunit.ap:1216");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1219");
|
|
}
|
|
}
|
|
//--- Linear (zero-quadratic) test:
|
|
//--- * N dimensions, N>=1
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- * A = 0
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- We perform two tests:
|
|
//--- * unconstrained problem must be recognized as unbounded
|
|
//--- * constrained problem can be successfully solved
|
|
//--- NOTE: we may explicitly set zero A, or assume that by
|
|
//--- default it is zero. During test we will try both
|
|
//--- ways.
|
|
eps=1.0E-4;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate problem
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
while(b[i]==0.0);
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoSemiDefinite(state,bctol,lctol);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
a=matrix<double>::Zeros(n,n);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
}
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-4 && rep.m_terminationtype!=-2,"testminqpunit.ap:1278");
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1283");
|
|
if(rep.m_terminationtype<=0)
|
|
return;
|
|
//--- Test - calculate constrained gradient at solution,
|
|
//--- check its norm.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,b[i]>0.0 && x1[i]>(bndl[i]+bctol),"testminqpunit.ap:1293");
|
|
CAp::SetErrorFlag(waserrors,b[i]<0.0 && x1[i]<(bndu[i]-bctol),"testminqpunit.ap:1294");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests equality constrained quadratic programming |
|
|
//| algorithm. |
|
|
//| Returns True on errors. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::ECQPTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int k=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble q;
|
|
CMatrixDouble c;
|
|
CMatrixDouble a2;
|
|
CRowDouble b;
|
|
CRowDouble b2;
|
|
CRowDouble xstart;
|
|
CRowDouble xstart2;
|
|
CRowDouble xend;
|
|
CRowDouble xend2;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble xd;
|
|
CRowDouble xs;
|
|
CRowDouble tmp;
|
|
CRowDouble g;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble xorigin;
|
|
CRowInt ct;
|
|
double eps=0;
|
|
double theta=0;
|
|
double f0=0;
|
|
double f1=0;
|
|
CMinQPState state;
|
|
CMinQPState state2;
|
|
CMinQPReport rep;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int rk=0;
|
|
double v=0;
|
|
int aulits=0;
|
|
bool waserrors=false;
|
|
int i_=0;
|
|
//--- First test:
|
|
//--- * N*N identity A
|
|
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
|
|
//--- orthogonal K*N matrix, x0 is some random vector
|
|
//--- * x1 is some random vector such that Q*x1=0. It is always possible
|
|
//--- to find such x1, because K<N
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x
|
|
//--- * exact solution must be equal to x0
|
|
eps=1.0E-4;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
a=matrix<double>::Identity(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
x1.Set(i,x0[i]);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
v=2*CMath::RandomReal()-1;
|
|
x1+=q[i]*v;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
//--- Compare with analytic solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1400");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(xend[i]-x0[i])>eps,"testminqpunit.ap:1404");
|
|
}
|
|
}
|
|
//--- Second test:
|
|
//--- * N*N SPD A
|
|
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
|
|
//--- orthogonal K*N matrix, x0 is some random vector
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
|
|
//--- where x1 is some random vector
|
|
//--- * we check feasibility properties of the solution
|
|
//--- * we do not know analytic form of the exact solution,
|
|
//--- but we know that projection of gradient into equality constrained
|
|
//--- subspace must be zero at the solution
|
|
eps=1.0E-4;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
//--- Calculate gradient, check projection
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1472");
|
|
continue;
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:1478");
|
|
}
|
|
g=b;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,a,i);
|
|
g.Add(i,v);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,g,c,i);
|
|
for(i_=0; i_<n; i_++)
|
|
g.Add(i_,- v*c.Get(i,i_));
|
|
}
|
|
v=CAblasF::RDotV2(n,g);
|
|
CAp::SetErrorFlag(waserrors,MathSqrt(v)>eps,"testminqpunit.ap:1493");
|
|
}
|
|
}
|
|
//--- Boundary and linear equality constrained QP problem:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<N equality constraints C*x = C*x0, where Q is random
|
|
//--- K*N matrix, x0 is some random vector from the
|
|
//--- feasible hypercube (0<=x0[i]<=1)
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
|
|
//--- where x1 is some random vector with -1<=x1[i]<=+1.
|
|
//--- (sometimes solution is in the inner area, sometimes at the boundary)
|
|
//--- * every component of the initial point XStart is either 0 or 1
|
|
//--- (point is located at the vertices of the feasible hypercube)
|
|
//--- Solution of such problem is calculated using two methods:
|
|
//--- a) boundary and linearly constrained QP
|
|
//--- b) augmented Lagrangian boundary constrained QP: we add explicit quadratic
|
|
//--- penalty to the problem; we also add Lagrangian terms and perform many
|
|
//--- subsequent iterations to find good estimates of the Lagrange multipliers.
|
|
//--- Sometimes augmented Largangian converges to slightly different point
|
|
//--- (boundary constraints lead to extremely slow, non-smooth convergence of
|
|
//--- the Lagrange multipliers). In order to correctly handle such situations
|
|
//--- we compare function values instead of final points - and use relaxed criteria
|
|
//--- to test for convergence.
|
|
//--- NOTE: sometimes we need as much as 300 Augmented Lagrangian iterations for
|
|
//--- method to converge.
|
|
eps=5.0E-2;
|
|
theta=1.0E+4;
|
|
aulits=300;
|
|
for(n=4; n<=6; n++)
|
|
{
|
|
for(k=1; k<=n-3; k++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CMath::RandomReal());
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Create exact optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1580");
|
|
continue;
|
|
}
|
|
//--- Solve problem using barrier functions (quadrative objective, boundary constraints,
|
|
//--- explicit penalty term added to the main quadratic matrix. Lagrangian terms improve
|
|
//--- solution quality):
|
|
//--- * A2 := A+C'*C
|
|
//--- * b2 := b-r'*C
|
|
//--- * b2 is iteratively updated using augmented Lagrangian update
|
|
//--- NOTE: we may need many outer iterations to converge to the optimal values
|
|
//--- of Lagrange multipliers. Convergence is slowed down by the presense
|
|
//--- of boundary constraints, whose activation/deactivation slows down
|
|
//--- process.
|
|
a2=matrix<double>::Zeros(n,n);
|
|
CAblas::RMatrixCopy(n,n,a,0,0,a2,0,0);
|
|
CAblas::RMatrixSyrk(n,k,theta,c,0,0,2,1.0,a2,0,0,true);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=i+1; j<n; j++)
|
|
a2.Set(j,i,a2.Get(i,j));
|
|
}
|
|
b2=b;
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=c.Get(i,n)*theta;
|
|
for(i_=0; i_<n; i_++)
|
|
b2.Add(i_,- v*c.Get(i,i_));
|
|
}
|
|
CMinQP::MinQPCreate(n,state2);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a2,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state2,xstart);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
for(i=1; i<=aulits; i++)
|
|
{
|
|
//--- Solve, update B2 according to augmented Lagrangian algorithm
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,xend2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1627");
|
|
continue;
|
|
}
|
|
for(j=0; j<k; j++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend2,c,j);
|
|
v=theta*(v-c.Get(j,n));
|
|
for(i_=0; i_<n; i_++)
|
|
b2.Add(i_,v*c.Get(j,i_));
|
|
}
|
|
}
|
|
//--- Calculate function value and XEnd and XEnd2
|
|
f0=0.0;
|
|
f1=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*xend[i]*a.Get(i,j)*xend[j];
|
|
f1+=0.5*xend2[i]*a.Get(i,j)*xend2[j];
|
|
}
|
|
f0+=xend[i]*b[i];
|
|
f1+=xend2[i]*b[i];
|
|
}
|
|
//--- Check feasibility properties and compare
|
|
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>eps,"testminqpunit.ap:1657");
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>(1.0E6*CMath::m_machineepsilon),"testminqpunit.ap:1661");
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,xend[i]<0.0,"testminqpunit.ap:1665");
|
|
CAp::SetErrorFlag(waserrors,xend[i]>1.0,"testminqpunit.ap:1666");
|
|
}
|
|
}
|
|
}
|
|
//--- Boundary and linear equality constrained QP problem,
|
|
//--- test for correct handling of non-zero XOrigin:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
|
|
//--- orthogonal K*N matrix, x0 is some random vector from the
|
|
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
|
|
//--- where b is some random vector with -1<=b[i]<=+1.
|
|
//--- (sometimes solution is in the inner area, sometimes at the boundary)
|
|
//--- * every component of the initial point XStart is random from [-1,1]
|
|
//--- Solution of such problem is calculated using two methods:
|
|
//--- a) QP with SetOrigin() call
|
|
//--- b) QP with XOrigin explicitly added to the quadratic function,
|
|
//--- Both methods should give same results; any significant difference is
|
|
//--- evidence of some error in the QP implementation.
|
|
eps=1.0E-4;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
|
|
//--- Additionally, we compute modified b: b2 = b-xorigin'*A
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
b2=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xorigin=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
xorigin.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xorigin,a,i);
|
|
b2.Set(i,b[i]-v);
|
|
}
|
|
//--- Solve with SetOrigin() call
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetOrigin(state,xorigin);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1750");
|
|
continue;
|
|
}
|
|
//--- Solve problem using explicit origin
|
|
CMinQP::MinQPCreate(n,state2);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state2,xstart);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state2,c,ct,k);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,xend2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1768");
|
|
continue;
|
|
}
|
|
//--- Check feasibility properties and compare solutions
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:1778");
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(xend[i]-xend2[i]))>eps,"testminqpunit.ap:1782");
|
|
CAp::SetErrorFlag(waserrors,xend[i]<0.0,"testminqpunit.ap:1783");
|
|
CAp::SetErrorFlag(waserrors,xend[i]>1.0,"testminqpunit.ap:1784");
|
|
}
|
|
}
|
|
}
|
|
//--- Boundary and linear equality constrained QP problem with excessive
|
|
//--- equality constraints:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
|
|
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x-b*x,
|
|
//--- where b is some random vector
|
|
//--- * because constraints are excessive, the main problem is to find
|
|
//--- feasible point; the only existing feasible point is solution,
|
|
//--- so we have to check only feasibility
|
|
eps=1.0E-4;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
|
|
k=2*n;
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
//--- Check feasibility properties of the solution
|
|
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
|
|
if(CAp::Len(xend)!=n)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1858");
|
|
continue;
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:1864");
|
|
}
|
|
}
|
|
//--- Boundary and linear equality constrained QP problem,
|
|
//--- test checks that different starting points yield same final point:
|
|
//--- * random N from [1..6], random K from [1..2*N]
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<N random linear equality constraints C*x = C*x0,
|
|
//--- where x0 is some random vector from the inner area of the
|
|
//--- feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x+b*x,
|
|
//--- where b is some random vector with -5<=b[i]<=+5
|
|
//--- * every component of the initial point XStart is random from [-2,+2]
|
|
//--- * we perform two starts from random different XStart and compare values
|
|
//--- of the target function (although final points may be slightly different,
|
|
//--- function values should match each other)
|
|
//--- Both points should give same results; any significant difference is
|
|
//--- evidence of some error in the QP implementation.
|
|
eps=1.0E-4;
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
//--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
|
|
n=CMath::RandomInteger(5)+2;
|
|
k=CMath::RandomInteger(n-1)+1;
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
b2=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
xstart2=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,4*CMath::RandomReal()-2);
|
|
xstart2.Set(i,4*CMath::RandomReal()-2);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
//--- Solve with XStart
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1938");
|
|
continue;
|
|
}
|
|
//--- Solve with XStart2
|
|
CMinQP::MinQPSetStartingPoint(state,xstart2);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1950");
|
|
continue;
|
|
}
|
|
//--- Calculate function value and XEnd and XEnd2, compare solutions
|
|
f0=0.0;
|
|
f1=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*xend[i]*a.Get(i,j)*xend[j];
|
|
f1+=0.5*xend2[i]*a.Get(i,j)*xend2[j];
|
|
}
|
|
f0+=xend[i]*b[i];
|
|
f1+=xend2[i]*b[i];
|
|
}
|
|
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>eps,"testminqpunit.ap:1969");
|
|
}
|
|
//--- Test ability to correctly handle situation where algorithm
|
|
//--- either:
|
|
//--- (1) starts from point with gradient whose projection to
|
|
//--- active set is almost zero (but not exactly zero)
|
|
//--- (2) performs step to such point
|
|
//--- In order to do this we solve problem
|
|
//--- * min 0.5*x'*x - (x0+c)'*x
|
|
//--- * subject to c'*x = c'*x0, with c and x0 random unit vectors
|
|
//--- * with initial point xs = x0+r*xd, where r is scalar,
|
|
//--- xd is vector which is orthogonal to c.
|
|
//--- * we try different r=power(2,-rk) for rk=0...70. The idea
|
|
//--- is that as we approach closer and closer to x0, which is
|
|
//--- a solution of the constrained problem, constrained gradient
|
|
//--- of the function rapidly vanishes.
|
|
eps=1.0E-6;
|
|
for(rk=0; rk<=70; rk++)
|
|
{
|
|
n=10;
|
|
//--- Generate x0, c, xd, xs, generate unit A
|
|
CApServ::RandomUnit(n,x0);
|
|
CApServ::RandomUnit(n,xd);
|
|
CApServ::RandomUnit(n,tmp);
|
|
c=matrix<double>::Zeros(1,n+1);
|
|
ct.Resize(1);
|
|
xs=vector<double>::Zeros(n);
|
|
b=vector<double>::Zeros(n);
|
|
ct.Set(0,0);
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(0,i,tmp[i]);
|
|
c.Add(0,n,tmp[i]*x0[i]);
|
|
b.Set(i,-(x0[i]+tmp[i]));
|
|
v+=tmp[i]*xd[i];
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xd.Add(i,-v*tmp[i]);
|
|
xs.Set(i,x0[i]+xd[i]*MathPow(2,-rk));
|
|
}
|
|
a=matrix<double>::Identity(n,n);
|
|
//--- Create and solve optimization problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
CMinQP::MinQPSetStartingPoint(state,xs);
|
|
CMinQP::MinQPSetLC(state,c,ct,1);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:2040");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(xend[i]-x0[i])>eps,"testminqpunit.ap:2044");
|
|
v=CAblasF::RDotVR(n,xend,c,0)-c.Get(0,n);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v)>(1.0E5*CMath::m_machineepsilon),"testminqpunit.ap:2048");
|
|
}
|
|
//--- return result
|
|
result=waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests inequality constrained quadratic programming |
|
|
//| algorithm. |
|
|
//| On failure sets Err to True; on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::ICQPTest(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int k=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble q;
|
|
CMatrixDouble c;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble t2;
|
|
CMatrixDouble t3;
|
|
CMatrixDouble ce;
|
|
CRowDouble xs0;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble tmp;
|
|
CRowDouble x;
|
|
CRowDouble xstart;
|
|
CRowDouble xstart2;
|
|
CRowDouble xend;
|
|
CRowDouble xend2;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble b;
|
|
CRowDouble b2;
|
|
CRowDouble g;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble xorigin;
|
|
CRowDouble tmp0;
|
|
CRowDouble tmp1;
|
|
CRowDouble da;
|
|
CRowInt ct;
|
|
bool nonnegative[];
|
|
double eps=0;
|
|
CMinQPState state;
|
|
CMinQPState state2;
|
|
CMinQPReport rep;
|
|
CMinQPReport rep2;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double f0=0;
|
|
double f1=0;
|
|
double tolconstr=0;
|
|
int bscale=0;
|
|
int akind=0;
|
|
int shiftkind=0;
|
|
int ccnt=0;
|
|
CHighQualityRandState rs;
|
|
CSNNLSSolver nnls;
|
|
bool isnonconvex;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Inequality constrained problem:
|
|
//--- * N*N diagonal A
|
|
//--- * one inequality constraint q'*x>=0, where q is random unit vector
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
|
|
//--- where x1 is some random vector
|
|
//--- * either:
|
|
//--- a) x1 is feasible => we must Stop at x1
|
|
//--- b) x1 is infeasible => we must Stop at the boundary q'*x=0 and
|
|
//--- projection of gradient onto q*x=0 must be zero
|
|
//--- NOTE: we make several passes because some specific kind of errors is rarely
|
|
//--- caught by this test, so we need several repetitions.
|
|
eps=1.0E-4;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(pass=0; pass<=4; pass++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(1,n+1);
|
|
ct.Resize(1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
do
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(0,i,2*CMath::RandomReal()-1);
|
|
v+=CMath::Sqr(c.Get(0,i));
|
|
}
|
|
v=MathSqrt(v);
|
|
}
|
|
while(v==0.0);
|
|
for(i=0; i<n; i++)
|
|
c.Mul(0,i,1.0/v);
|
|
c.Set(0,n,0);
|
|
ct.Set(0,1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetLC(state,c,ct,1);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
//--- Test
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2160");
|
|
continue;
|
|
}
|
|
v=CAblasF::RDotVR(n,x1,c,0);
|
|
if(v>=0.0)
|
|
{
|
|
//--- X1 is feasible, compare target function values at XEnd and X1
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,MathAbs(xend[i]-x1[i])>eps,"testminqpunit.ap:2173");
|
|
}
|
|
else
|
|
{
|
|
//--- X1 is infeasible:
|
|
//--- * XEnd must be approximately feasible
|
|
//--- * gradient projection onto c'*x=0 must be zero
|
|
v=CAblasF::RDotVR(n,xend,c,0);
|
|
CAp::SetErrorFlag(err,v<(-eps),"testminqpunit.ap:2183");
|
|
g=b;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,a,i);
|
|
g.Add(i,v);
|
|
}
|
|
v=CAblasF::RDotVR(n,g,c,0);
|
|
for(i_=0; i_<n; i_++)
|
|
g.Add(i_,- v*c.Get(0,i_));
|
|
v=CAblasF::RDotV2(n,g);
|
|
CAp::SetErrorFlag(err,MathSqrt(v)>eps,"testminqpunit.ap:2194");
|
|
}
|
|
}
|
|
}
|
|
//--- Boundary and linear equality/inequality constrained QP problem,
|
|
//--- test for correct handling of non-zero XOrigin:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<N linear equality/inequality constraints Q*x = Q*x0, where
|
|
//--- Q is random orthogonal K*N matrix, x0 is some random vector from the
|
|
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
|
|
//--- where b is some random vector with -1<=b[i]<=+1.
|
|
//--- (sometimes solution is in the inner area, sometimes at the boundary)
|
|
//--- * every component of the initial point XStart is random from [-1,1]
|
|
//--- Solution of such problem is calculated using two methods:
|
|
//--- a) QP with SetOrigin() call
|
|
//--- b) QP with XOrigin explicitly added to the quadratic function,
|
|
//--- Both methods should give same results; any significant difference is
|
|
//--- evidence of some error in the QP implementation.
|
|
eps=1.0E-4;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
|
|
//--- Additionally, we compute modified b: b2 = b-xorigin'*A
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
b2=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xorigin=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
xorigin.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,CMath::RandomInteger(3)-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xorigin,a,i);
|
|
b2.Set(i,b[i]-v);
|
|
}
|
|
//--- Solve with SetOrigin() call
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetOrigin(state,xorigin);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2278");
|
|
continue;
|
|
}
|
|
//--- Solve problem using explicit origin
|
|
CMinQP::MinQPCreate(n,state2);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state2,xstart);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state2,c,ct,k);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,xend2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2296");
|
|
continue;
|
|
}
|
|
//--- Calculate function value and XEnd and XEnd2
|
|
f0=0.0;
|
|
f1=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*(xend[i]-xorigin[i])*a.Get(i,j)*(xend[j]-xorigin[j]);
|
|
f1+=0.5*(xend2[i]-xorigin[i])*a.Get(i,j)*(xend2[j]-xorigin[j]);
|
|
}
|
|
f0+=(xend[i]-xorigin[i])*b[i];
|
|
f1+=(xend2[i]-xorigin[i])*b[i];
|
|
}
|
|
//--- Check feasibility properties and compare solutions
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
if(ct[i]==0)
|
|
CAp::SetErrorFlag(err,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:2323");
|
|
if(ct[i]>0)
|
|
CAp::SetErrorFlag(err,v<(c.Get(i,n)-eps),"testminqpunit.ap:2325");
|
|
if(ct[i]<0)
|
|
CAp::SetErrorFlag(err,v>(c.Get(i,n)+eps),"testminqpunit.ap:2327");
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(xend[i]-xend2[i])>eps,"testminqpunit.ap:2331");
|
|
CAp::SetErrorFlag(err,xend[i]<0.0,"testminqpunit.ap:2332");
|
|
CAp::SetErrorFlag(err,xend[i]>1.0,"testminqpunit.ap:2333");
|
|
}
|
|
}
|
|
}
|
|
//--- Boundary constraints vs linear ones:
|
|
//--- * N*N SPD A
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
|
|
//--- where x1 is some random vector from [-1,+1]
|
|
//--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
|
|
//--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
|
|
//--- * initial point xstart is from [-1,+2]
|
|
//--- * we solve two related QP problems:
|
|
//--- a) one with constraints posed as boundary ones
|
|
//--- b) another one with same constraints posed as general linear ones
|
|
//--- both problems must have same solution.
|
|
//--- Here we test that boundary constrained and linear inequality constrained
|
|
//--- solvers give same results.
|
|
eps=1.0E-3;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, x0, XStart, C, CT
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,3*CMath::RandomReal()-1);
|
|
bndl.Set(i,-(0.1+0.9*CMath::RandomReal()));
|
|
bndu.Set(i,0.1+0.9*CMath::RandomReal());
|
|
c.Set(2*i,i,1);
|
|
c.Set(2*i,n,bndl[i]);
|
|
ct.Set(2*i,1);
|
|
c.Set(2*i+1,i,1);
|
|
c.Set(2*i+1,n,bndu[i]);
|
|
ct.Set(2*i+1,-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Solve linear inequality constrained problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetLC(state,c,ct,2*n);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
//--- Solve boundary constrained problem
|
|
CMinQP::MinQPCreate(n,state2);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state2,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state2,xstart);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,xend2,rep2);
|
|
//--- Calculate gradient, check projection
|
|
if(rep.m_terminationtype<=0 || rep2.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2421");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,xend[i]<(bndl[i]-eps),"testminqpunit.ap:2426");
|
|
CAp::SetErrorFlag(err,xend[i]>(bndu[i]+eps),"testminqpunit.ap:2427");
|
|
CAp::SetErrorFlag(err,MathAbs(xend[i]-xend2[i])>eps,"testminqpunit.ap:2428");
|
|
}
|
|
}
|
|
//--- Boundary constraints posed as general linear ones:
|
|
//--- * no bound constraints
|
|
//--- * 2*N linear constraints 0 <= x[i] <= 1
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple constraints and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1).
|
|
//--- * however, we can't guarantee that solution is strictly feasible
|
|
//--- with respect to nonlinearity constraint, so we check
|
|
//--- for approximate feasibility.
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
a=matrix<double>::Identity(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xstart.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
b.Set(i,-x0[i]);
|
|
c.Set(2*i,i,1);
|
|
c.Set(2*i,n,0);
|
|
ct.Set(2*i,1);
|
|
c.Set(2*i+1,i,1);
|
|
c.Set(2*i+1,n,1);
|
|
ct.Set(2*i+1,-1);
|
|
}
|
|
//--- Create and optimize
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLC(state,c,ct,2*n);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2497");
|
|
continue;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(xend[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.05,"testminqpunit.ap:2507");
|
|
CAp::SetErrorFlag(err,xend[i]<(-1.0E-6),"testminqpunit.ap:2508");
|
|
CAp::SetErrorFlag(err,xend[i]>(1.0+1.0E-6),"testminqpunit.ap:2509");
|
|
}
|
|
}
|
|
//--- Boundary and linear equality/inequality constrained QP problem with
|
|
//--- excessive constraints:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
|
|
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x-b*x,
|
|
//--- where b is some random vector
|
|
//--- * because constraints are excessive, the main problem is to find
|
|
//--- feasible point; usually, the only existing feasible point is solution,
|
|
//--- so we have to check only feasibility
|
|
eps=1.0E-4;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
|
|
k=2*n;
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
ct.Set(i,CMath::RandomInteger(3)-1);
|
|
if(ct[i]==0)
|
|
c.Set(i,n,v);
|
|
if(ct[i]>0)
|
|
c.Set(i,n,v-1.0E-3);
|
|
if(ct[i]<0)
|
|
c.Set(i,n,v+1.0E-3);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
//--- Check feasibility properties of the solution
|
|
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
|
|
if(CAp::Len(xend)!=n)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2588");
|
|
continue;
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
if(ct[i]==0)
|
|
CAp::SetErrorFlag(err,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:2595");
|
|
if(ct[i]>0)
|
|
CAp::SetErrorFlag(err,v<(c.Get(i,n)-eps),"testminqpunit.ap:2597");
|
|
if(ct[i]<0)
|
|
CAp::SetErrorFlag(err,v>(c.Get(i,n)+eps),"testminqpunit.ap:2599");
|
|
}
|
|
}
|
|
//--- General inequality constrained problem:
|
|
//--- * N*N SPD diagonal A with moderate condtion number
|
|
//--- * no boundary constraints
|
|
//--- * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
|
|
//--- matrix, x0 is some random vector [-1,+1]
|
|
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
|
|
//--- where b is random vector from [-1,+1]
|
|
//--- * using duality, we can obtain solution of QP problem as follows:
|
|
//--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
|
|
//--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
|
|
//--- b) after we got dual solution ys, we calculate primal solution
|
|
//--- xs = inv(A)*(C'*ys-b)
|
|
eps=1.0E-3;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem
|
|
da=vector<double>::Zeros(n);
|
|
a=matrix<double>::Zeros(n,n);
|
|
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(n,n+1);
|
|
ct.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
da.Set(i,MathExp(8*CMath::RandomReal()-4));
|
|
a.Set(i,i,da[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,t2.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,1);
|
|
}
|
|
//--- Solve primal problem, check feasibility
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetLC(state,c,ct,n);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2665");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xend,c,i);
|
|
CAp::SetErrorFlag(err,v<(double)(c.Get(i,n)-eps),"testminqpunit.ap:2671");
|
|
}
|
|
//--- Generate dual problem:
|
|
//--- * A2 stores new quadratic term
|
|
//--- * B2 stores new linear term
|
|
//--- * BndL/BndU store boundary constraints
|
|
t3=matrix<double>::Zeros(n,n);
|
|
a2=matrix<double>::Zeros(n,n);
|
|
CAblas::RMatrixTranspose(n,n,c,0,0,t3,0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=1/MathSqrt(da[i]);
|
|
for(i_=0; i_<n; i_++)
|
|
t3.Mul(i,i_,v);
|
|
}
|
|
CAblas::RMatrixSyrk(n,n,1.0,t3,0,0,2,0.0,a2,0,0,true);
|
|
tmp0=b/da+0;
|
|
b2=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,tmp0,c,i);
|
|
b2.Set(i,-(v+c.Get(i,n)));
|
|
}
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
CMinQP::MinQPCreate(n,state2);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a2,true);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,xend2,rep2);
|
|
if(rep2.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2726");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=c.Get(i_,i)*xend2[i_];
|
|
tmp0.Set(i,(v-b[i])/da[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,MathAbs(tmp0[i]-xend[i])>(eps*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:2739");
|
|
}
|
|
//--- Boundary and linear equality/inequality constrained QP problem,
|
|
//--- test checks that different starting points yield same final point:
|
|
//--- * random N from [1..6], random K from [1..2*N]
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<2*N linear inequality constraints Q*x <= Q*x0, where
|
|
//--- Q is random K*N matrix, x0 is some random vector from the
|
|
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x+b*x,
|
|
//--- where b is some random vector with -5<=b[i]<=+5
|
|
//--- * every component of the initial point XStart is random from [-2,+2]
|
|
//--- * we perform two starts from random different XStart and compare values
|
|
//--- of the target function (although final points may be slightly different,
|
|
//--- function values should match each other)
|
|
eps=1.0E-4;
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
//--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
|
|
n=CMath::RandomInteger(5)+2;
|
|
k=CMath::RandomInteger(2*n)+1;
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
b2=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
xstart2=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,4*CMath::RandomReal()-2);
|
|
xstart2.Set(i,4*CMath::RandomReal()-2);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
//--- Generate I-th row of C
|
|
//--- Avoid excessive (more than N/2) equality constraints.
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,CMath::RandomInteger(3)-1);
|
|
if(i>=(0.5*n) && ct[i]==0)
|
|
ct.Set(i,1);
|
|
if(ct[i]<0)
|
|
c.Set(i,n,c.Get(i,n)+0.1);
|
|
if(ct[i]>0)
|
|
c.Set(i,n,c.Get(i,n)-0.1);
|
|
}
|
|
//--- Solve with XStart
|
|
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
|
|
CMinQP::MinQPCreate(n,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,k);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(CAp::Len(xend)!=n || !CApServ::IsFiniteVector(xend,n))
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2821");
|
|
continue;
|
|
}
|
|
//--- Solve with XStart2
|
|
CMinQP::MinQPSetStartingPoint(state,xstart2);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend2,rep);
|
|
if(CAp::Len(xend2)!=n || !CApServ::IsFiniteVector(xend2,n))
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2834");
|
|
continue;
|
|
}
|
|
//--- Calculate function value and XEnd and XEnd2, compare solutions
|
|
f0=0.0;
|
|
f1=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*xend[i]*a.Get(i,j)*xend[j];
|
|
f1+=0.5*xend2[i]*a.Get(i,j)*xend2[j];
|
|
}
|
|
f0+=xend[i]*b[i];
|
|
f1+=xend2[i]*b[i];
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(f0-f1)>eps,"testminqpunit.ap:2853");
|
|
}
|
|
//--- Convex/nonconvex optimization problem with excessive constraints:
|
|
//--- * N=2..5
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * b has normally distributed entries with scale 10^BScale
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank SPD, low rank indefinite
|
|
//--- * box constraints: x[i] in [-1,+1]
|
|
//---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
|
|
//--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is
|
|
//--- a shift parameter:
|
|
//--- * with zero v_shift such constraints are degenerate (each vertex has
|
|
//--- N box constraints and one "redundant" linear constraint)
|
|
//--- * with positive v_shift linear constraint is always inactive
|
|
//--- * with small (about machine epsilon) but negative v_shift,
|
|
//--- constraint is close to degenerate - but not exactly
|
|
//--- We check that constrained gradient is close to zero at solution.
|
|
//--- Box constraint is considered active if distance to boundary is less
|
|
//--- than TolConstr.
|
|
//--- NOTE: TolConstr must be large enough so it won't conflict with
|
|
//--- perturbation introduced by v_shift
|
|
tolconstr=1.0E-3;
|
|
for(n=2; n<=5; n++)
|
|
{
|
|
for(akind=0; akind<=4; akind++)
|
|
{
|
|
for(shiftkind=-5; shiftkind<=1; shiftkind++)
|
|
{
|
|
for(bscale=0; bscale>=-2; bscale--)
|
|
{
|
|
//--- Generate A, B and initial point
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
|
|
x.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
isnonconvex=false;
|
|
switch(akind)
|
|
{
|
|
case 1:
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(n,50.0,a);
|
|
break;
|
|
case 2:
|
|
//--- Dense well conditioned indefinite
|
|
CMatGen::SMatrixRndCond(n,50.0,a);
|
|
isnonconvex=true;
|
|
break;
|
|
case 3:
|
|
//--- Low rank semidefinite
|
|
tmp=vector<double>::Zeros(n);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
break;
|
|
case 4:
|
|
//--- Low rank indefinite
|
|
tmp=vector<double>::Zeros(n);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
isnonconvex=true;
|
|
break;
|
|
}
|
|
//--- Generate constraints
|
|
bl=vector<double>::Full(n,-1.0);
|
|
bu=vector<double>::Full(n,1.0);
|
|
ccnt=(int)MathRound(MathPow(2,n));
|
|
c=matrix<double>::Zeros(ccnt,n+1);
|
|
ct.Resize(ccnt);
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
ct.Set(i,-1);
|
|
k=i;
|
|
c.Set(i,n,MathSign(shiftkind)*MathPow(10,MathAbs(shiftkind))*CMath::m_machineepsilon);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,2*(k%2)-1);
|
|
c.Add(i,n,c.Get(i,j)*c.Get(i,j));
|
|
k=k/2;
|
|
}
|
|
}
|
|
//--- Create and optimize
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetStartingPoint(state,x);
|
|
if(isnonconvex)
|
|
SetRandomAlgoNonConvexLC(state);
|
|
else
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetBC(state,bl,bu);
|
|
CMinQP::MinQPSetLC(state,c,ct,ccnt);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xs0,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminqpunit.ap:3000");
|
|
if(err)
|
|
return;
|
|
//--- Evaluate gradient at solution and test
|
|
vv=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs0,a,i);
|
|
v+=b[i];
|
|
if(xs0[i]<=(bl[i]+tolconstr) && v>0.0)
|
|
v=0.0;
|
|
if(xs0[i]>=(bu[i]-tolconstr) && v<0.0)
|
|
v=0.0;
|
|
vv+=CMath::Sqr(v);
|
|
}
|
|
vv=MathSqrt(vv);
|
|
CAp::SetErrorFlag(err,vv>(1.0E-3),"testminqpunit.ap:3024");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Convex/nonconvex optimization problem with combination of
|
|
//--- box and linear constraints:
|
|
//--- * N=2..8
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * b has normally distributed entries with scale 10^BScale
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD,
|
|
//--- well conditioned indefinite, low rank semidefinite, low rank indefinite
|
|
//--- * box constraints: x[i] in [-1,+1]
|
|
//--- * initial point x0 = [0 0 ... 0 0]
|
|
//--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
|
|
//--- random mix of equality/inequality constraints is tried.
|
|
//--- x0 is guaranteed to be feasible.
|
|
//--- We check that constrained gradient is close to zero at solution.
|
|
//--- Inequality constraint is considered active if distance to boundary
|
|
//--- is less than TolConstr. We use nonnegative least squares solver
|
|
//--- in order to compute constrained gradient.
|
|
tolconstr=1.0E-3;
|
|
for(n=2; n<=8; n++)
|
|
{
|
|
for(akind=0; akind<=4; akind++)
|
|
{
|
|
for(bscale=0; bscale>=-2; bscale--)
|
|
{
|
|
//--- Generate A, B and initial point
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
|
|
isnonconvex=false;
|
|
switch(akind)
|
|
{
|
|
case 1:
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(n,50.0,a);
|
|
break;
|
|
case 2:
|
|
//--- Dense well conditioned indefinite
|
|
CMatGen::SMatrixRndCond(n,50.0,a);
|
|
isnonconvex=true;
|
|
break;
|
|
case 3:
|
|
//--- Low rank
|
|
tmp=vector<double>::Zeros(n);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
break;
|
|
case 4:
|
|
//--- Low rank indefinite
|
|
tmp=vector<double>::Zeros(n);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
isnonconvex=true;
|
|
break;
|
|
}
|
|
//--- Generate constraints
|
|
bl=vector<double>::Full(n,-1.0);
|
|
bu=vector<double>::Full(n,1.0);
|
|
ccnt=MathMin(3,n-1);
|
|
c=matrix<double>::Zeros(ccnt,n+1);
|
|
ct.Resize(ccnt);
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
ct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
//--- Create and optimize
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetStartingPoint(state,x);
|
|
if(isnonconvex)
|
|
SetRandomAlgoNonConvexLC(state);
|
|
else
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetBC(state,bl,bu);
|
|
CMinQP::MinQPSetLC(state,c,ct,ccnt);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xs0,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminqpunit.ap:3159");
|
|
if(err)
|
|
return;
|
|
//--- 1. evaluate unconstrained gradient at solution
|
|
//--- 2. calculate constrained gradient (NNLS solver is used
|
|
//--- to evaluate gradient subject to active constraints).
|
|
//--- In order to do this we form CE matrix, matrix of active
|
|
//--- constraints (columns store constraint vectors). Then
|
|
//--- we try to approximate gradient vector by columns of CE,
|
|
//--- subject to non-negativity restriction placed on variables
|
|
//--- corresponding to inequality constraints.
|
|
//--- Residual from such regression is a constrained gradient vector.
|
|
g=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs0,a,i);
|
|
g.Set(i,v+b[i]);
|
|
}
|
|
ce=matrix<double>::Zeros(n,n+ccnt);
|
|
ArrayResize(nonnegative,n+ccnt);
|
|
k=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,xs0[i]<(double)(bl[i]),"testminqpunit.ap:3187");
|
|
CAp::SetErrorFlag(err,xs0[i]>(double)(bu[i]),"testminqpunit.ap:3188");
|
|
if(xs0[i]<=(double)(bl[i]+tolconstr))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,0.0);
|
|
ce.Set(i,k,1.0);
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
if(xs0[i]>=(double)(bu[i]-tolconstr))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,0.0);
|
|
ce.Set(i,k,-1.0);
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
}
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs0,c,i);
|
|
v-=c.Get(i,n);
|
|
CAp::SetErrorFlag(err,ct[i]==0 && MathAbs(v)>tolconstr,"testminqpunit.ap:3212");
|
|
CAp::SetErrorFlag(err,ct[i]>0 && v<(-tolconstr),"testminqpunit.ap:3213");
|
|
CAp::SetErrorFlag(err,ct[i]<0 && v>tolconstr,"testminqpunit.ap:3214");
|
|
if(ct[i]==0)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,c.Get(i,j));
|
|
nonnegative[k]=false;
|
|
k++;
|
|
continue;
|
|
}
|
|
if((ct[i]>0 && v<=tolconstr) || (ct[i]<0 && v>=(-tolconstr)))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,MathSign(ct[i])*c.Get(i,j));
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
}
|
|
CSNNLS::SNNLSInit(0,0,0,nnls);
|
|
CSNNLS::SNNLSSetProblem(nnls,ce,g,0,k,n);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
if(!nonnegative[i])
|
|
CSNNLS::SNNLSDropNNC(nnls,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(nnls,tmp);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
g.Add(j,-tmp[i]*ce.Get(j,i));
|
|
}
|
|
vv=CAblasF::RDotV2(n,g);
|
|
vv=MathSqrt(vv);
|
|
if(akind==3 || akind==4)
|
|
CAp::SetErrorFlag(err,vv>(1.0E-2),"testminqpunit.ap:3244");
|
|
else
|
|
CAp::SetErrorFlag(err,vv>(1.0E-3),"testminqpunit.ap:3246");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests linearly constrained QP solvers. |
|
|
//| On failure sets Err to True; on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::GeneralLCQPTest(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int solvertype=0;
|
|
double v=0;
|
|
double vv=0;
|
|
bool bflag;
|
|
int pass=0;
|
|
CMatrixDouble rawa;
|
|
CMatrixDouble a;
|
|
CSparseMatrix sa;
|
|
CRowDouble b;
|
|
CRowDouble b2;
|
|
CRowDouble xs;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble xf;
|
|
CRowDouble xorigin;
|
|
CRowDouble s;
|
|
CRowDouble da;
|
|
CRowDouble xstart;
|
|
CRowDouble g;
|
|
CRowDouble gtrial;
|
|
CRowDouble gs;
|
|
CRowDouble tmp;
|
|
CRowDouble tmp0;
|
|
CRowDouble tmp1;
|
|
CRowDouble tmp2;
|
|
CRowDouble lagrange;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CMatrixDouble rawc;
|
|
CRowDouble rawcl;
|
|
CRowDouble rawcu;
|
|
CRowInt rawct;
|
|
int rawccnt=0;
|
|
CMatrixDouble densec;
|
|
CRowInt densect;
|
|
int denseccnt=0;
|
|
CSparseMatrix sparsec;
|
|
CRowInt sparsect;
|
|
int sparseccnt=0;
|
|
CMatrixDouble activeset;
|
|
bool activeeq[];
|
|
int nactive=0;
|
|
CSNNLSSolver nnls;
|
|
double constraintsrcond=0;
|
|
CRowDouble svdw;
|
|
CMatrixDouble svdu;
|
|
CMatrixDouble svdvt;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
CMinQPState state2;
|
|
CMinQPReport rep2;
|
|
double f0=0;
|
|
double f1=0;
|
|
double xtol=0;
|
|
double ftol=0;
|
|
double gtol=0;
|
|
double tolconstr=0;
|
|
int bscale=0;
|
|
int akind=0;
|
|
CMatrixDouble ce;
|
|
CMatrixDouble q;
|
|
CMatrixDouble t2;
|
|
CMatrixDouble t3;
|
|
CMatrixDouble a2;
|
|
CRowDouble lagbc;
|
|
CRowDouble laglc;
|
|
bool nonnegative[];
|
|
double mx=0;
|
|
int shiftkind=0;
|
|
int nnz=0;
|
|
bool issemidefinite;
|
|
bool skiptest;
|
|
double bleicepsx=0;
|
|
double aulepsx=0;
|
|
double aulrho=0;
|
|
int aulits=0;
|
|
double ipmeps=0;
|
|
CMatrixDouble kkt;
|
|
CRowDouble kktright;
|
|
double minushuge=0;
|
|
double plushuge=0;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
bleicepsx=1.0E-9;
|
|
ipmeps=1.0E-12;
|
|
aulepsx=1.0E-12;
|
|
aulrho=5.0E3;
|
|
aulits=15;
|
|
//--- SMALL-SCALE TESTS: many tests for small N's
|
|
for(solvertype=0; solvertype<=3; solvertype++)
|
|
{
|
|
//--- Test random linearly constrained convex QP problem with known answer:
|
|
//--- * generate random A and b
|
|
//--- * generate random solution XS
|
|
//--- * calculate unconstrained gradient GS at XS
|
|
//--- * generate random box/linear constraints C, with some of them being
|
|
//--- active at XS, and some being inactive. Calculate residual gradient
|
|
//--- GP after projection of GS onto active set, add one more constraint
|
|
//--- equal to +-(GP-GS).
|
|
//--- We test here BLEIC and Dense-AUL solvers, with A being passed
|
|
//--- in dense or sparse format, and linear constraints C being passed
|
|
//--- as dense, sparse or mixed ones.
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Generate random A, b and xs
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xs=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xs.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Generate well conditioned "raw" constraints:
|
|
//--- * generate random box and CCnt-1 linear constraints
|
|
//--- * determine active set, calculate its condition number
|
|
//--- * repeat until condition number is good enough
|
|
//--- (better than 1E2; larger values sometimes result in
|
|
//--- spurious failures)
|
|
gs=vector<double>::Zeros(n);
|
|
tmp=vector<double>::Zeros(n);
|
|
CAblas::RMatrixMVect(n,n,rawa,0,0,0,xs,0,gs,0);
|
|
for(i=0; i<n; i++)
|
|
gs.Add(i,b[i]);
|
|
rawccnt=1+CHighQualityRand::HQRndUniformI(rs,n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
activeset=matrix<double>::Zeros(n,n+rawccnt);
|
|
ArrayResize(activeeq,n+rawccnt);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
do
|
|
{
|
|
nactive=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,xs[i]-1-CHighQualityRand::HQRndUniformR(rs));
|
|
bndu.Set(i,xs[i]+1+CHighQualityRand::HQRndUniformR(rs));
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.66)
|
|
{
|
|
//--- I-th box constraint is inactive
|
|
continue;
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
|
|
{
|
|
//--- I-th box constraint is equality one
|
|
bndl.Set(i,xs[i]);
|
|
bndu.Set(i,xs[i]);
|
|
activeset.Col(nactive,vector<double>::Zeros(n));
|
|
activeset.Set(i,nactive,1);
|
|
activeeq[nactive]=true;
|
|
nactive++;
|
|
}
|
|
else
|
|
{
|
|
//--- I-th box constraint is inequality one
|
|
activeset.Col(nactive,vector<double>::Zeros(n));
|
|
if(gs[i]>0.0)
|
|
{
|
|
bndl.Set(i,xs[i]);
|
|
activeset.Set(i,nactive,-1);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
bndu.Set(i,xs[i]);
|
|
activeset.Set(i,nactive,1);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
|
|
bndl.Set(i,AL_NEGINF);
|
|
}
|
|
activeeq[nactive]=false;
|
|
nactive++;
|
|
}
|
|
}
|
|
for(i=0; i<rawccnt-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.50)
|
|
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
else
|
|
rawc.Set(i,j,0);
|
|
}
|
|
rawc.Set(i,CHighQualityRand::HQRndUniformI(rs,n),CHighQualityRand::HQRndNormal(rs));
|
|
rawc.Set(i,n,0);
|
|
for(j=0; j<n; j++)
|
|
rawc.Add(i,n,rawc.Get(i,j)*xs[j]);
|
|
rawct.Set(i,-1);
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.66)
|
|
{
|
|
//--- I-th box constraint is inactive
|
|
rawc.Add(i,n,1+CHighQualityRand::HQRndUniformR(rs));
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
|
|
{
|
|
for(i_=0; i_<=n; i_++)
|
|
rawc.Mul(i,i_,-1);
|
|
rawct.Mul(i,-1);
|
|
}
|
|
continue;
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
|
|
{
|
|
//--- I-th box constraint is equality one
|
|
rawct.Set(i,0);
|
|
activeset.Col(nactive,rawc[i]+0);
|
|
activeeq[nactive]=true;
|
|
nactive++;
|
|
}
|
|
else
|
|
{
|
|
//--- I-th box constraint is inequality one
|
|
v=CAblasF::RDotVR(n,gs,rawc,i);
|
|
if(v>0.0)
|
|
{
|
|
rawct.Set(i,1);
|
|
activeset.Col(nactive,rawc[i]*(-1));
|
|
}
|
|
else
|
|
{
|
|
rawct.Set(i,-1);
|
|
activeset.Col(nactive,rawc[i]+0);
|
|
}
|
|
activeeq[nactive]=false;
|
|
nactive++;
|
|
}
|
|
}
|
|
tmp=gs.ToVector()*(-1);
|
|
CSNNLS::SNNLSInit(0,0,0,nnls);
|
|
CSNNLS::SNNLSSetProblem(nnls,activeset,tmp,0,nactive,n);
|
|
for(i=0; i<nactive; i++)
|
|
{
|
|
if(activeeq[i])
|
|
CSNNLS::SNNLSDropNNC(nnls,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(nnls,lagrange);
|
|
tmp=gs.ToVector()*(-1);
|
|
for(i=0; i<nactive; i++)
|
|
{
|
|
v=lagrange[i];
|
|
tmp-=activeset.Col(i)*v;
|
|
}
|
|
rawc.Row(rawccnt-1,tmp);
|
|
v=CAblasF::RDotV(n,tmp,xs);
|
|
rawc.Set(rawccnt-1,n,v);
|
|
rawct.Set(rawccnt-1,-1);
|
|
//--- Calculate reciprocal condition number
|
|
if(nactive>0)
|
|
{
|
|
bflag=CSingValueDecompose::RMatrixSVD(activeset,n,nactive,0,0,0,svdw,svdu,svdvt);
|
|
//--- check
|
|
if(!CAp::Assert(bflag,"MinQPTest: integrity failure"))
|
|
{
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
constraintsrcond=svdw[MathMin(nactive,n)-1]/svdw[0];
|
|
}
|
|
else
|
|
constraintsrcond=1;
|
|
//--- Check RCond
|
|
}
|
|
while(!(constraintsrcond>=0.01 && nactive<n));
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
//--- Test
|
|
//--- Because constrained problems are often ill-conditioned,
|
|
//--- we do NOT compare X1 with XS directly. Instead, we:
|
|
//--- a) compare function values at X1 and XS with good precision
|
|
//--- b) check constraint violation with good precision
|
|
//--- c) perform comparison for |X1-XS| with LOW precision
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:3569");
|
|
return;
|
|
}
|
|
f0=0;
|
|
f1=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
f0+=b[i]*xs[i];
|
|
f1+=b[i]*x1[i];
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*xs[i]*rawa.Get(i,j)*xs[j];
|
|
f1+=0.5*x1[i]*rawa.Get(i,j)*x1[j];
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>1.0E-3,"testminqpunit.ap:3584");
|
|
}
|
|
//--- Inequality constrained convex problem:
|
|
//--- * N*N diagonal A
|
|
//--- * one inequality constraint q'*x>=0, where q is random unit vector
|
|
//--- * optimization problem has form 0.5*x'*A*x-(xs*A)*x,
|
|
//--- where xs is some random vector
|
|
//--- * either:
|
|
//--- a) xs is feasible => we must Stop at xs
|
|
//--- b) xs is infeasible => we must Stop at the boundary q'*x=0 and
|
|
//--- projection of gradient onto q*x=0 must be zero
|
|
//--- NOTE: we make several passes because some specific kind of errors is rarely
|
|
//--- caught by this test, so we need several repetitions.
|
|
xtol=1.0E-4;
|
|
gtol=1.0E-4;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(pass=0; pass<=4; pass++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
xs=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(1,n+1);
|
|
rawct.Resize(1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xs.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
do
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
rawc.Set(0,i,2*CMath::RandomReal()-1);
|
|
v+=CMath::Sqr(rawc.Get(0,i));
|
|
}
|
|
v=MathSqrt(v);
|
|
}
|
|
while(v==0.0);
|
|
for(i=0; i<n; i++)
|
|
rawc.Mul(0,i,1.0/v);
|
|
rawc.Set(0,n,0);
|
|
rawct.Set(0,1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,1,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
//--- Test
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:3667");
|
|
return;
|
|
}
|
|
v=CAblasF::RDotVR(n,xs,rawc,0);
|
|
if(v>=0.0)
|
|
{
|
|
//--- XS is feasible
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-xs[i])>xtol,"testminqpunit.ap:3677");
|
|
}
|
|
else
|
|
{
|
|
//--- XS is infeasible:
|
|
//--- * X1 must be approximately feasible
|
|
//--- * gradient projection onto c'*x=0 must be zero
|
|
v=CAblasF::RDotVR(n,x1,rawc,0);
|
|
CAp::SetErrorFlag(errorflag,v<(-xtol),"testminqpunit.ap:3687");
|
|
g=b;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
g.Add(i,v);
|
|
}
|
|
v=CAblasF::RDotVR(n,g,rawc,0);
|
|
for(i_=0; i_<n; i_++)
|
|
g.Add(i_,- v*rawc.Get(0,i_));
|
|
v=CAblasF::RDotV2(n,g);
|
|
CAp::SetErrorFlag(errorflag,MathSqrt(v)>gtol,"testminqpunit.ap:3698");
|
|
}
|
|
}
|
|
}
|
|
//--- Box constraints vs linear ones:
|
|
//--- * N*N SPD A
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
|
|
//--- where x1 is some random vector from [-1,+1]
|
|
//--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
|
|
//--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
|
|
//--- * initial point xstart is from [-1,+2]
|
|
//--- * we solve two related QP problems:
|
|
//--- a) one with constraints posed as boundary ones
|
|
//--- b) another one with same constraints posed as general linear ones
|
|
//--- both problems must have same solution.
|
|
//--- Here we test that boundary constrained and linear inequality constrained
|
|
//--- solvers give same results.
|
|
xtol=1.0E-5;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, x0, XStart, C, CT
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(2*n,n+1);
|
|
rawct.Resize(2*n);
|
|
bndl=vector<double>::Full(n,AL_NEGINF);
|
|
bndu=vector<double>::Full(n,AL_POSINF);
|
|
for(i=0; i<n ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,3*CMath::RandomReal()-1);
|
|
bndl.Set(i,-(0.1+0.9*CMath::RandomReal()));
|
|
bndu.Set(i,0.1+0.9*CMath::RandomReal());
|
|
rawc.Set(2*i,i,1);
|
|
rawc.Set(2*i,n,bndl[i]);
|
|
rawct.Set(2*i,1);
|
|
rawc.Set(2*i+1,i,1);
|
|
rawc.Set(2*i+1,n,bndu[i]);
|
|
rawct.Set(2*i+1,-1);
|
|
}
|
|
for(i=0; i<n ; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Solve linear inequality constrained problem
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,2*n,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
//--- Solve boundary constrained problem
|
|
CMinQP::MinQPCreate(n,state2);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state2,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state2,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state2,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state2,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state2,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state2,rs);
|
|
CMinQP::MinQPSetStartingPoint(state2,xstart);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,x2,rep2);
|
|
//--- Calculate gradient, check projection
|
|
if(rep.m_terminationtype<=0 || rep2.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:3804");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,x1[i]<(bndl[i]-xtol),"testminqpunit.ap:3809");
|
|
CAp::SetErrorFlag(errorflag,x1[i]>(bndu[i]+xtol),"testminqpunit.ap:3810");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-x2[i])>xtol,"testminqpunit.ap:3811");
|
|
}
|
|
}
|
|
//--- Convex/nonconvex optimization problem with combination of
|
|
//--- box and linear constraints:
|
|
//--- * N=2..8
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * b has normally distributed entries with scale 10^BScale
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD,
|
|
//--- well conditioned definite, low rank semidefinity
|
|
//--- * box constraints: x[i] in [-1,+1]
|
|
//--- * initial point x0 = [0 0 ... 0 0]
|
|
//--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
|
|
//--- random mix of equality/inequality constraints is tried, moderate
|
|
//--- condition number is guaranteed, x0 is guaranteed to be feasible.
|
|
//--- We check that constrained gradient is close to zero at solution.
|
|
//--- Inequality constraint is considered active if distance to boundary
|
|
//--- is less than TolConstr. We use nonnegative least squares solver
|
|
//--- in order to compute constrained gradient.
|
|
tolconstr=-99999;
|
|
for(n=2; n<=8; n++)
|
|
{
|
|
for(akind=0; akind<=3; akind++)
|
|
{
|
|
for(bscale=1; bscale>=-1; bscale--)
|
|
{
|
|
//--- Dense-AUL solver has lower precision on rank-deficient
|
|
//--- problems, so we skip AKind=3.
|
|
//--- IPM solvers can not work with indefinite problems.
|
|
if(solvertype==1 && akind==3)
|
|
continue;
|
|
if((solvertype==2 || solvertype==3) && (akind==2 || akind==4))
|
|
continue;
|
|
//--- Set up tolerances
|
|
if(solvertype==0)
|
|
tolconstr=1.0E-8;
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
{
|
|
tolconstr=1.0E-3;
|
|
if(akind==3)
|
|
tolconstr=tolconstr*5;
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
gtol=1.0E-4;
|
|
//--- Generate A, B and initial point
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
|
|
if(akind==1)
|
|
{
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(n,50.0,a);
|
|
}
|
|
if(akind==2)
|
|
{
|
|
//--- Dense well conditioned indefinite
|
|
CMatGen::SMatrixRndCond(n,50.0,a);
|
|
}
|
|
if(akind==3)
|
|
{
|
|
//--- Low rank semidefinite
|
|
tmp=vector<double>::Zeros(n);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
}
|
|
//--- Generate constraints
|
|
bndl=vector<double>::Full(n,-1.0);
|
|
bndu=vector<double>::Full(n,1.0);
|
|
rawccnt=MathMin(3,n-1);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
do
|
|
{
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
for(j=0; j<n; j++)
|
|
rawc.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
}
|
|
while(GetConstraintRCond(rawc,rawccnt,n)<=0.01);
|
|
//--- Create and optimize
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:3961");
|
|
if(errorflag)
|
|
return;
|
|
//--- 1. evaluate unconstrained gradient at solution
|
|
//--- 2. calculate constrained gradient (NNLS solver is used
|
|
//--- to evaluate gradient subject to active constraints).
|
|
//--- In order to do this we form CE matrix, matrix of active
|
|
//--- constraints (columns store constraint vectors). Then
|
|
//--- we try to approximate gradient vector by columns of CE,
|
|
//--- subject to non-negativity restriction placed on variables
|
|
//--- corresponding to inequality constraints.
|
|
//--- Residual from such regression is a constrained gradient vector.
|
|
g=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
g.Set(i,v+b[i]);
|
|
}
|
|
ce=matrix<double>::Zeros(n,n+rawccnt);
|
|
ArrayResizeAL(nonnegative,n+rawccnt);
|
|
k=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,x1[i]<bndl[i],"testminqpunit.ap:3989");
|
|
CAp::SetErrorFlag(errorflag,x1[i]>bndu[i],"testminqpunit.ap:3990");
|
|
if(x1[i]<=(bndl[i]+tolconstr))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,0.0);
|
|
ce.Set(i,k,1.0);
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
if(x1[i]>=(double)(bndu[i]-tolconstr))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,0.0);
|
|
ce.Set(i,k,-1.0);
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,rawc,i);
|
|
v-=rawc.Get(i,n);
|
|
CAp::SetErrorFlag(errorflag,rawct[i]==0 && MathAbs(v)>tolconstr,"testminqpunit.ap:4014");
|
|
CAp::SetErrorFlag(errorflag,rawct[i]>0 && v<(-tolconstr),"testminqpunit.ap:4015");
|
|
CAp::SetErrorFlag(errorflag,rawct[i]<0 && v>tolconstr,"testminqpunit.ap:4016");
|
|
if(rawct[i]==0)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,rawc.Get(i,j));
|
|
nonnegative[k]=false;
|
|
k++;
|
|
continue;
|
|
}
|
|
if((rawct[i]>0 && v<=tolconstr) || (rawct[i]<0 && v>=(-tolconstr)))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,MathSign(rawct[i])*rawc.Get(i,j));
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
}
|
|
CSNNLS::SNNLSInit(0,0,0,nnls);
|
|
CSNNLS::SNNLSSetProblem(nnls,ce,g,0,k,n);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
if(!nonnegative[i])
|
|
CSNNLS::SNNLSDropNNC(nnls,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(nnls,tmp);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
g.Add(j,-tmp[i]*ce.Get(j,i));
|
|
}
|
|
vv=CAblasF::RDotV2(n,g);
|
|
vv=MathSqrt(vv);
|
|
CAp::SetErrorFlag(errorflag,vv>gtol,"testminqpunit.ap:4045");
|
|
}
|
|
}
|
|
}
|
|
//--- Boundary and linear equality/inequality constrained QP problem,
|
|
//--- test for correct handling of non-zero XOrigin:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<N linear equality/inequality constraints Q*x = Q*x0, where
|
|
//--- Q is random orthogonal K*N matrix, x0 is some random vector from the
|
|
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
|
|
//--- where b is some random vector with -1<=b[i]<=+1.
|
|
//--- (sometimes solution is in the inner area, sometimes at the boundary)
|
|
//--- * every component of the initial point XStart is random from [-1,1]
|
|
//--- Solution of such problem is calculated using two methods:
|
|
//--- a) QP with SetOrigin() call
|
|
//--- b) QP with XOrigin explicitly added to the quadratic function,
|
|
//--- Both methods should give same results; any significant difference is
|
|
//--- evidence of some error in the QP implementation.
|
|
ftol=-99999;
|
|
xtol=-99999;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
//--- Set up tolerances
|
|
if(solvertype==0)
|
|
{
|
|
ftol=1.0E-5;
|
|
xtol=1.0E-5;
|
|
}
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
{
|
|
ftol=1.0E-3;
|
|
xtol=1.0E-3;
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
|
|
//--- Additionally, we compute modified b: b2 = b-xorigin'*A
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
b2=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xorigin=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(k,n+1);
|
|
rawct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
xorigin.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
rawc.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
rawc.Set(i,n,v);
|
|
rawct.Set(i,CMath::RandomInteger(3)-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xorigin,a,i);
|
|
b2.Set(i,b[i]-v);
|
|
}
|
|
//--- Solve with SetOrigin() call
|
|
CMinQP::MinQPCreate(n,state);
|
|
if(solvertype==0)
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
else
|
|
if(solvertype==1)
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
else
|
|
if(solvertype==2)
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
else
|
|
if(solvertype==3)
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetOrigin(state,xorigin);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4153");
|
|
continue;
|
|
}
|
|
//--- Solve problem using explicit origin
|
|
CMinQP::MinQPCreate(n,state2);
|
|
if(solvertype==0)
|
|
CMinQP::MinQPSetAlgoBLEIC(state2,0.0,0.0,bleicepsx,0);
|
|
else
|
|
if(solvertype==1)
|
|
CMinQP::MinQPSetAlgoDenseAUL(state2,aulepsx,aulrho,aulits);
|
|
else
|
|
if(solvertype==2)
|
|
CMinQP::MinQPSetAlgoDenseIPM(state2,ipmeps);
|
|
else
|
|
if(solvertype==3)
|
|
CMinQP::MinQPSetAlgoSparseIPM(state2,ipmeps);
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state2,rs);
|
|
CMinQP::MinQPSetStartingPoint(state2,xstart);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state2,rs);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,x2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4180");
|
|
continue;
|
|
}
|
|
//--- Calculate function value at X1/X2.
|
|
//--- Compare solutions.
|
|
f0=0.0;
|
|
f1=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*(x1[i]-xorigin[i])*a.Get(i,j)*(x1[j]-xorigin[j]);
|
|
f1+=0.5*(x2[i]-xorigin[i])*a.Get(i,j)*(x2[j]-xorigin[j]);
|
|
}
|
|
f0+=(x1[i]-xorigin[i])*b[i];
|
|
f1+=(x2[i]-xorigin[i])*b[i];
|
|
}
|
|
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>ftol,"testminqpunit.ap:4200");
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-x2[i])>xtol,"testminqpunit.ap:4202");
|
|
}
|
|
}
|
|
//--- Test scale-invariance. Solver performs same steps on scaled and
|
|
//--- unscaled problems (assuming that scale of the variables is known
|
|
//--- and is 2^k for some integer k; latter guarantees that DENSE-AUL solver
|
|
//--- performs EXACTLY same steps even under finite-precision arithmetics).
|
|
//--- We generate random scale matrix S and random well-conditioned and
|
|
//--- well scaled matrix A. Then we solve two problems:
|
|
//--- (1) f = 0.5*x'*A*x+b'*x
|
|
//--- (identity scale matrix is used)
|
|
//--- and
|
|
//--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
|
|
//--- (scale matrix S is used)
|
|
//--- with correspondingly scaled constraints, origin and starting point.
|
|
//--- As result, we must get S*x=y
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<=MathMax(n-2,1); k++)
|
|
{
|
|
//--- Set up tolerances:
|
|
//--- * BLEIC or IPM do not guarantee that we perform steps exactly even
|
|
//--- when scale is 2^k, so we use non-zero XTol=1E-3
|
|
//--- * DENSE-AUL provides such guarantee, so XTol=0
|
|
if((solvertype==0 || solvertype==2) || solvertype==3)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
xtol=0;
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Generate "raw" problem: A, b, BndL, BndU, C/CT, XOrigin, XStart.
|
|
//--- Solve it immediately.
|
|
//--- NOTE: we make sure that there exists at least one feasible point X0.
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
xorigin=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(k,n+1);
|
|
rawct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-1-CHighQualityRand::HQRndUniformR(rs));
|
|
bndu.Set(i,1+CHighQualityRand::HQRndUniformR(rs));
|
|
xstart.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xorigin.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v+=rawc.Get(i,j)*x0[j];
|
|
}
|
|
rawc.Set(i,n,v);
|
|
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
if(rawct[i]<0)
|
|
rawc.Add(i,n,0.1);
|
|
if(rawct[i]>0)
|
|
rawc.Add(i,n,-0.1);
|
|
}
|
|
RandomlySplitLC(rawc,rawct,k,n,sparsec,sparsect,sparseccnt,densec,densect,denseccnt,rs);
|
|
CMinQP::MinQPCreate(n,state);
|
|
state.m_dbgskipconstraintnormalization=true;
|
|
if(solvertype==0)
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
else
|
|
if(solvertype==1)
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
else
|
|
if(solvertype==2)
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
else
|
|
if(solvertype==3)
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetOrigin(state,xorigin);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4306");
|
|
continue;
|
|
}
|
|
//--- Scale problem and solve one more time.
|
|
//--- Randomly choose between dense and sparse versions.
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndUniformI(rs,7)-3));
|
|
akind=CHighQualityRand::HQRndUniformI(rs,2);
|
|
if(akind==0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Mul(i,j,1.0/(s[i]*s[j]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseCreate(n,n,0,sa);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j)/(s[i]*s[j]));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Mul(i,1.0/s[i]);
|
|
xstart.Mul(i,s[i]);
|
|
xorigin.Mul(i,s[i]);
|
|
bndl.Mul(i,s[i]);
|
|
bndu.Mul(i,s[i]);
|
|
}
|
|
for(i=0; i<denseccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
densec.Mul(i,j,1.0/s[j]);
|
|
}
|
|
for(i=0; i<sparseccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(sparsec,i,j,CSparse::SparseGet(sparsec,i,j)/s[j]);
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
state.m_dbgskipconstraintnormalization=true;
|
|
if(solvertype==0)
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
else
|
|
if(solvertype==1)
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
else
|
|
if(solvertype==2)
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
else
|
|
if(solvertype==3)
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
if(akind==0)
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
|
|
if(akind==1)
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sa,CMath::RandomReal()>0.5);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetOrigin(state,xorigin);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4371");
|
|
continue;
|
|
}
|
|
//--- Compare
|
|
mx=0;
|
|
for(i=0; i<n; i++)
|
|
mx=MathMax(mx,MathAbs(x1[i]));
|
|
mx=MathMax(mx,1);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(x1[i]-x2[i]/s[i]))>(double)(xtol*mx),"testminqpunit.ap:4383");
|
|
}
|
|
}
|
|
//--- General inequality constrained problem:
|
|
//--- * N*N SPD diagonal A with moderate condtion number
|
|
//--- * no boundary constraints
|
|
//--- * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
|
|
//--- matrix, x0 is some random vector [-1,+1]
|
|
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
|
|
//--- where b is random vector from [-1,+1]
|
|
//--- * using duality, we can obtain solution of QP problem as follows:
|
|
//--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
|
|
//--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
|
|
//--- b) after we got dual solution ys, we calculate primal solution
|
|
//--- xs = inv(A)*(C'*ys-b)
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Set up tolerances
|
|
xtol=1.0E-3;
|
|
//--- Generate problem
|
|
da=vector<double>::Zeros(n);
|
|
a=matrix<double>::Zeros(n,n);
|
|
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(n,n+1);
|
|
rawct.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
da.Set(i,MathExp(6*CMath::RandomReal()-3));
|
|
a.Set(i,i,da[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
rawc.Set(i,i_,t2.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,rawc,i);
|
|
rawc.Set(i,n,v);
|
|
rawct.Set(i,1);
|
|
}
|
|
//--- Solve primal problem, check feasibility
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=false;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetLC(state,rawc,rawct,n);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4462");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,rawc,i);
|
|
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:4468");
|
|
}
|
|
//--- Generate dual problem:
|
|
//--- * A2 stores new quadratic term
|
|
//--- * B2 stores new linear term
|
|
//--- * BndL/BndU store boundary constraints
|
|
t3=matrix<double>::Zeros(n,n);
|
|
a2=matrix<double>::Zeros(n,n);
|
|
CAblas::RMatrixTranspose(n,n,rawc,0,0,t3,0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=1/MathSqrt(da[i]);
|
|
for(i_=0; i_<n; i_++)
|
|
t3.Mul(i,i_,v);
|
|
}
|
|
CAblas::RMatrixSyrk(n,n,1.0,t3,0,0,2,0.0,a2,0,0,true);
|
|
tmp0=b/da+0;
|
|
b2=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,tmp0,rawc,i);
|
|
b2.Set(i,-(v+rawc.Get(i,n)));
|
|
}
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Full(n,AL_POSINF);
|
|
CMinQP::MinQPCreate(n,state2);
|
|
CMinQP::MinQPSetAlgoQuickQP(state2,0.0,0.0,1.0E-9,0,true);
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a2,true);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,x2,rep2);
|
|
if(rep2.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4523");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<n; i_++)
|
|
v+=rawc.Get(i_,i)*x2[i_];
|
|
tmp0.Set(i,(v-b[i])/da[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(tmp0[i]-x1[i]))>(double)(xtol*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:4536");
|
|
}
|
|
//--- Boundary and linear equality/inequality constrained QP problem with
|
|
//--- excessive constraints:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
|
|
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x-b*x,
|
|
//--- where b is some random vector
|
|
//--- * because constraints are excessive, the main problem is to find
|
|
//--- feasible point; usually, the only existing feasible point is solution,
|
|
//--- so we have to check only feasibility
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Set up tolerances
|
|
if(solvertype==0)
|
|
xtol=1.0E-5;
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
xtol=5.0E-3;
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
|
|
k=2*n;
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Ones(n);
|
|
x0=vector<double>::Zeros(n);
|
|
x1=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(k,n+1);
|
|
rawct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rawc.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,rawc,i);
|
|
rawct.Set(i,CMath::RandomInteger(3)-1);
|
|
if(rawct[i]==0)
|
|
rawc.Set(i,n,v);
|
|
if(rawct[i]>0)
|
|
rawc.Set(i,n,v-50*xtol);
|
|
if(rawct[i]<0)
|
|
rawc.Set(i,n,v+50*xtol);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
//--- Check feasibility properties of the solution
|
|
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
|
|
if(CAp::Len(x1)!=n)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4637");
|
|
continue;
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,rawc,i);
|
|
if(rawct[i]==0)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(v-rawc.Get(i,n))>xtol,"testminqpunit.ap:4644");
|
|
if(rawct[i]>0)
|
|
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:4646");
|
|
if(rawct[i]<0)
|
|
CAp::SetErrorFlag(errorflag,v>(rawc.Get(i,n)+xtol),"testminqpunit.ap:4648");
|
|
}
|
|
}
|
|
//--- Boundary constraints posed as general linear ones:
|
|
//--- * no bound constraints
|
|
//--- * 2*N linear constraints 0 <= x[i] <= 1
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple constraints and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1).
|
|
//--- * however, we can't guarantee that solution is strictly feasible
|
|
//--- with respect to nonlinearity constraint, so we check
|
|
//--- for approximate feasibility.
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- Set up tolerances
|
|
if(solvertype==0)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Generate X, BL, BU.
|
|
a=matrix<double>::Identity(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(2*n,n+1);
|
|
rawct.Resize(2*n);
|
|
k=2*n;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xstart.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
b.Set(i,-x0[i]);
|
|
rawc.Set(2*i,i,1);
|
|
rawc.Set(2*i,n,0);
|
|
rawct.Set(2*i,1);
|
|
rawc.Set(2*i+1,i,1);
|
|
rawc.Set(2*i+1,n,1);
|
|
rawct.Set(2*i+1,-1);
|
|
}
|
|
//--- Create and optimize
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state,rs);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4735");
|
|
continue;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-CApServ::BoundVal(x0[i],0.0,1.0))>xtol,"testminqpunit.ap:4745");
|
|
CAp::SetErrorFlag(errorflag,x1[i]<(0.0-xtol),"testminqpunit.ap:4746");
|
|
CAp::SetErrorFlag(errorflag,x1[i]>(1.0+xtol),"testminqpunit.ap:4747");
|
|
}
|
|
}
|
|
//--- Convex optimization problem with excessive constraints:
|
|
//--- * N=2..5
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * b has normally distributed entries
|
|
//--- * A is diagonal with log-normally distributed entries
|
|
//--- * box constraints: x[i] in [-1,+1]
|
|
//---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
|
|
//--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is
|
|
//--- a shift parameter:
|
|
//--- * with zero v_shift such constraints are degenerate (each vertex has
|
|
//--- N box constraints and one "redundant" linear constraint)
|
|
//--- * with positive v_shift linear constraint is always inactive
|
|
//--- * with small (about machine epsilon) but negative v_shift,
|
|
//--- constraint is close to degenerate - but not exactly
|
|
//--- Because A is diagonal, we can easily find out solution analytically.
|
|
//--- NOTE: TolConstr must be large enough so it won't conflict with
|
|
//--- perturbation introduced by v_shift
|
|
for(n=2; n<=5; n++)
|
|
{
|
|
for(shiftkind=-5; shiftkind<=1; shiftkind++)
|
|
{
|
|
//--- Set up tolerances
|
|
if(solvertype==0)
|
|
{
|
|
tolconstr=1.0E-6;
|
|
gtol=1.0E-6;
|
|
}
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
{
|
|
tolconstr=1.0E-4;
|
|
gtol=1.0E-4;
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Generate A, B and initial point
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xstart.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
//--- Generate constraints
|
|
bndl=vector<double>::Full(n,-1.0);
|
|
bndu=vector<double>::Full(n,1.0);
|
|
rawccnt=(int)MathRound(MathPow(2,n));
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
rawct.Set(i,-1);
|
|
k=i;
|
|
rawc.Set(i,n,MathSign(shiftkind)*MathPow(10,MathAbs(shiftkind))*CMath::m_machineepsilon);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
rawc.Set(i,j,2*(k%2)-1);
|
|
rawc.Add(i,n,rawc.Get(i,j)*rawc.Get(i,j));
|
|
k=k/2;
|
|
}
|
|
}
|
|
//--- Create and optimize
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:4858");
|
|
if(errorflag)
|
|
return;
|
|
//--- Evaluate gradient at solution and test
|
|
vv=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,a,i);
|
|
v+=b[i];
|
|
if(x1[i]<=(bndl[i]+tolconstr) && v>0.0)
|
|
v=0.0;
|
|
if(x1[i]>=(bndu[i]-tolconstr) && v<0.0)
|
|
v=0.0;
|
|
vv+=CMath::Sqr(v);
|
|
}
|
|
vv=MathSqrt(vv);
|
|
CAp::SetErrorFlag(errorflag,vv>gtol,"testminqpunit.ap:4882");
|
|
}
|
|
}
|
|
//--- Convex optimization problem with known answer (we generate constraints
|
|
//--- and Lagrange coefficients, and then generate quadratic and linear term
|
|
//--- which satisfy KKT conditions for given Lagrange coefficients):
|
|
//--- * N=2..8
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD, low rank
|
|
//--- * initial point x0 = [0 0 ... 0 0]
|
|
//--- * absent bounds can be set to +-INF or to some huge number (randomly)
|
|
//--- * first, we generate set of linear constraints (without bounds),
|
|
//--- solution point X and Lagrange multipliers for linear and box constraints
|
|
//--- * then we determine bounds on variables and linear constraints which
|
|
//--- align with values/signs of Lagrange multipliers
|
|
//--- * then, having quadratic term A and known constraints and Lagrange
|
|
//--- multipliers we determine linear term B which makes KKT conditions true
|
|
for(n=1; n<=8; n++)
|
|
{
|
|
for(akind=0; akind<=2; akind++)
|
|
{
|
|
//--- Select number used for absent bounds:
|
|
//--- * +- INF
|
|
//--- * 1e9
|
|
//--- * 1e11
|
|
i=CHighQualityRand::HQRndUniformI(rs,3);
|
|
minushuge=AL_NEGINF;
|
|
plushuge=AL_POSINF;
|
|
if(i==1)
|
|
{
|
|
minushuge=-1.0E9;
|
|
plushuge=1.0E9;
|
|
}
|
|
if(i==2)
|
|
{
|
|
minushuge=-1.0E11;
|
|
plushuge=1.0E11;
|
|
}
|
|
//--- Generate quadratic term A, origin and scale
|
|
a=matrix<double>::Zeros(n,n);
|
|
issemidefinite=true;
|
|
if(akind==1)
|
|
{
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(n,50.0,a);
|
|
issemidefinite=false;
|
|
}
|
|
if(akind==2)
|
|
{
|
|
//--- Low rank semidefinite
|
|
tmp=vector<double>::Zeros(n);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
issemidefinite=true;
|
|
}
|
|
s=vector<double>::Zeros(n);
|
|
xorigin=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
xorigin.Set(j,CHighQualityRand::HQRndNormal(rs));
|
|
s.Set(j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
//--- Generate linear constraints (left parts)
|
|
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n+1);
|
|
rawc=matrix<double>::Zeros(rawccnt,n);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
nnz=CHighQualityRand::HQRndUniformI(rs,n+1);
|
|
for(k=0; k<nnz; k++)
|
|
{
|
|
j=CHighQualityRand::HQRndUniformI(rs,n);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
v+=0.1*CApServ::PosSign(v);
|
|
rawc.Set(i,j,v);
|
|
}
|
|
}
|
|
//--- Generate Lagrange multipliers, with at most NActive<N being non-zero
|
|
lagbc=vector<double>::Zeros(n);
|
|
laglc=vector<double>::Zeros(rawccnt);
|
|
nactive=CHighQualityRand::HQRndUniformI(rs,n);
|
|
k=CHighQualityRand::HQRndUniformI(rs,MathMin(nactive,rawccnt)+1);
|
|
for(i=0; i<k; i++)
|
|
laglc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),CHighQualityRand::HQRndNormal(rs));
|
|
for(i=k; i<nactive; i++)
|
|
lagbc.Set(CHighQualityRand::HQRndUniformI(rs,n),CHighQualityRand::HQRndNormal(rs));
|
|
//--- Generate solution and gradient at the solution, set B to -G
|
|
xf=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xf.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
g=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=a.Get(i,j)*(xf[j]-xorigin[j]);
|
|
g.Set(i,v+lagbc[i]);
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
g.Add(j,laglc[i]*rawc.Get(i,j));
|
|
}
|
|
b=g*(-1.0)+0;
|
|
//--- Set up bounds according to Lagrange multipliers
|
|
//--- NOTE: for semidefinite problems we set all variable bounds
|
|
bndl=vector<double>::Full(n,minushuge);
|
|
bndu=vector<double>::Full(n,plushuge);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite)
|
|
bndl.Set(j,xf[j]-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite)
|
|
bndu.Set(j,xf[j]+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(lagbc[j]!=0.0)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
|
|
{
|
|
bndl.Set(j,xf[j]);
|
|
bndu.Set(j,xf[j]);
|
|
}
|
|
else
|
|
{
|
|
if(lagbc[j]<0.0)
|
|
bndl.Set(j,xf[j]);
|
|
if(lagbc[j]>0.0)
|
|
bndu.Set(j,xf[j]);
|
|
}
|
|
}
|
|
}
|
|
rawcl=vector<double>::Zeros(rawccnt);
|
|
rawcu=vector<double>::Zeros(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xf,rawc,i);
|
|
rawcl.Set(i,minushuge);
|
|
rawcu.Set(i,plushuge);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
rawcl.Set(i,v-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
rawcu.Set(i,v+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(laglc[i]!=0.0)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
|
|
{
|
|
//--- Active equality constraint
|
|
rawcl.Set(i,v);
|
|
rawcu.Set(i,v);
|
|
}
|
|
else
|
|
{
|
|
//--- Active inequality constraint
|
|
if(laglc[i]<0.0)
|
|
rawcl.Set(i,v);
|
|
if(laglc[i]>0.0)
|
|
rawcu.Set(i,v);
|
|
}
|
|
}
|
|
if((!MathIsValidNumber(rawcl[i]) || rawcl[i]==minushuge) && (!MathIsValidNumber(rawcu[i]) || rawcu[i]==plushuge))
|
|
{
|
|
//--- At least one bound must be present for linear constraint
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
rawcl.Set(i,v-10);
|
|
else
|
|
rawcu.Set(i,v+10);
|
|
}
|
|
}
|
|
//--- Completely skip some solvers depending on their properties:
|
|
//--- * Dense-AUL solver has lower precision on rank-deficient
|
|
//--- problems, so we skip AKind=0 and AKind=2.
|
|
//--- * BLEIC solver is always skipped
|
|
if(solvertype==1 && akind!=1)
|
|
continue;
|
|
if(solvertype==0)
|
|
continue;
|
|
//--- Solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLC2(rawc,rawcl,rawcu,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPSetOrigin(state,xorigin);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:5138");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(x1)<n,"testminqpunit.ap:5139");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(rep.m_lagbc)<n,"testminqpunit.ap:5140");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(rep.m_laglc)<rawccnt,"testminqpunit.ap:5141");
|
|
if(errorflag)
|
|
return;
|
|
//--- Test function value at the solution
|
|
f0=QuadraticTarget(a,b,n,x1);
|
|
f1=QuadraticTarget(a,b,n,xf);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>(CApServ::RMaxAbs3(f0,f1,1.0)*1.0E-3),"testminqpunit.ap:5150");
|
|
//--- Test Lagrange multipliers returned by the solver; test is skipped:
|
|
//--- * for BLEIC solver
|
|
//--- * for overconstrained DENSE-AUL
|
|
skiptest=false;
|
|
skiptest=skiptest||solvertype==0;
|
|
skiptest=skiptest||(solvertype==1 && (double)(nactive)>(double)(0.5*(n-1)));
|
|
if(!skiptest)
|
|
{
|
|
gtrial=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=b[i];
|
|
for(j=0; j<n; j++)
|
|
v+=a.Get(i,j)*(x1[j]-xorigin[j]);
|
|
gtrial.Set(i,v+rep.m_lagbc[i]);
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
gtrial+=rawc[i]*rep.m_laglc[i];
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(gtrial[i])>1.0E-3,"testminqpunit.ap:5176");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Large-scale tests: a few selected tests for large N's
|
|
for(solvertype=0; solvertype<=3; solvertype++)
|
|
{
|
|
//--- General equality constrained problem:
|
|
//--- * N*N SPD (non-diagonal) A with moderate condition number
|
|
//--- * no box constraints
|
|
//--- * K<N equality constraints C*x = C*x0, where C is K*N well conditioned
|
|
//--- matrix, x0 is some random vector [-1,+1]
|
|
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
|
|
//--- where b is random vector from [-1,+1]
|
|
//--- * true solution of QP problem is obtained with KKT matrix;
|
|
//--- we compare value returned by solver against one calculated
|
|
//--- via KKT conditions.
|
|
n=60;
|
|
rawccnt=40;
|
|
if(solvertype==0)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
|
|
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
rawc.Set(i,i_,t2.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,rawc,i);
|
|
rawc.Set(i,n,v);
|
|
rawct.Set(i,0);
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5255");
|
|
continue;
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,rawc,i);
|
|
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:5261");
|
|
}
|
|
kkt=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
|
|
kktright=vector<double>::Zeros(n+rawccnt);
|
|
CAblas::RMatrixCopy(n,n,a,0,0,kkt,0,0);
|
|
CAblas::RMatrixCopy(rawccnt,n,rawc,0,0,kkt,n,0);
|
|
CAblas::RMatrixTranspose(rawccnt,n,rawc,0,0,kkt,0,n);
|
|
for(i=0; i<n; i++)
|
|
kktright.Set(i,-b[i]);
|
|
for(i=0; i<rawccnt; i++)
|
|
kktright.Set(n+i,rawc.Get(i,n));
|
|
CFbls::FblsSolveLS(kkt,kktright,n+rawccnt,n+rawccnt,tmp0,tmp1,tmp2);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(kktright[i]-x1[i])>(xtol*MathMax(MathAbs(kktright[i]),1.0)),"testminqpunit.ap:5280");
|
|
//--- General inequality constrained problem:
|
|
//--- * N*N SPD diagonal A with moderate condtion number
|
|
//--- * no box constraints
|
|
//--- * K<N inequality constraints C*x >= C*x0, where C is N*N well conditioned
|
|
//--- matrix, x0 is some random vector [-1,+1]
|
|
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
|
|
//--- where b is random vector from [-1,+1]
|
|
//--- * using duality, we can obtain solution of QP problem as follows:
|
|
//--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
|
|
//--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
|
|
//--- b) after we got dual solution ys, we calculate primal solution
|
|
//--- xs = inv(A)*(C'*ys-b)
|
|
n=60;
|
|
rawccnt=40;
|
|
if(solvertype==0)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
if((solvertype==1 || solvertype==2) || solvertype==3)
|
|
xtol=1.0E-3;
|
|
else
|
|
{
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
da=vector<double>::Zeros(n);
|
|
a=matrix<double>::Zeros(n,n);
|
|
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
da.Set(i,MathExp(8*CMath::RandomReal()-4));
|
|
a.Set(i,i,da[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
rawc.Set(i,i_,t2.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,rawc,i);
|
|
rawc.Set(i,n,v);
|
|
rawct.Set(i,1);
|
|
}
|
|
CMinQP::MinQPCreate(n,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"unexpected solver type");
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,x1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5362");
|
|
continue;
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,rawc,i);
|
|
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:5368");
|
|
}
|
|
t3=matrix<double>::Zeros(n,rawccnt);
|
|
a2=matrix<double>::Zeros(rawccnt,rawccnt);
|
|
CAblas::RMatrixTranspose(rawccnt,n,rawc,0,0,t3,0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=1/MathSqrt(da[i]);
|
|
for(i_=0; i_<rawccnt; i_++)
|
|
t3.Mul(i,i_,v);
|
|
}
|
|
CAblas::RMatrixSyrk(rawccnt,n,1.0,t3,0,0,2,0.0,a2,0,0,true);
|
|
tmp0=b/da+0;
|
|
b2=vector<double>::Zeros(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,tmp0,rawc,i);
|
|
b2.Set(i,-(v+rawc.Get(i,n)));
|
|
}
|
|
bndl=vector<double>::Zeros(rawccnt);
|
|
bndu=vector<double>::Full(rawccnt,AL_POSINF);
|
|
CMinQP::MinQPCreate(rawccnt,state2);
|
|
CMinQP::MinQPSetAlgoQuickQP(state2,0.0,0.0,1.0E-9,0,true);
|
|
CMinQP::MinQPSetLinearTerm(state2,b2);
|
|
CMinQP::MinQPSetQuadraticTerm(state2,a2,true);
|
|
CMinQP::MinQPSetBC(state2,bndl,bndu);
|
|
CMinQP::MinQPOptimize(state2);
|
|
CMinQP::MinQPResults(state2,x2,rep2);
|
|
if(rep2.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5421");
|
|
continue;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<rawccnt; i_++)
|
|
v+=rawc.Get(i_,i)*x2[i_];
|
|
tmp0.Set(i,(v-b[i])/da[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(tmp0[i]-x1[i]))>(double)(xtol*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:5434");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests special inequality constrained QP problems. |
|
|
//| Returns True on errors. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinQPUnit::SpecialICQPTests(void)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CMatrixDouble a;
|
|
CMatrixDouble c;
|
|
CRowDouble xstart;
|
|
CRowDouble xend;
|
|
CRowDouble xexact;
|
|
CRowDouble b;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowInt ct;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
bool waserrors=false;
|
|
int i=0;
|
|
int j=0;
|
|
//--- Test 1: reported by Vanderlande Industries.
|
|
//--- Tests algorithm ability to handle degenerate constraints.
|
|
a=matrix<double>::Identity(3,3);
|
|
b.Resize(3);
|
|
b.Set(0,-50);
|
|
b.Set(1,-50);
|
|
b.Set(2,-75);
|
|
bndl=vector<double>::Zeros(3);
|
|
bndu=vector<double>::Full(3,100);
|
|
bndu.Set(2,150);
|
|
xstart=vector<double>::Zeros(3);
|
|
xstart.Set(1,100);
|
|
xexact=xstart;
|
|
xexact.Set(2,50);
|
|
c=matrix<double>::Zeros(3,4);
|
|
c.Set(0,0,1);
|
|
c.Set(0,1,-1);
|
|
c.Set(0,2,0);
|
|
c.Set(0,3,-100);
|
|
c.Set(1,0,1);
|
|
c.Set(1,1,0);
|
|
c.Set(1,2,-1);
|
|
c.Set(1,3,0);
|
|
c.Set(2,0,-1);
|
|
c.Set(2,1,0);
|
|
c.Set(2,2,1);
|
|
c.Set(2,3,50);
|
|
ct.Resize(3);
|
|
ct.Set(0,-1);
|
|
ct.Set(1,-1);
|
|
ct.Set(2,-1);
|
|
CMinQP::MinQPCreate(3,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,3);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<=2; i++)
|
|
waserrors=waserrors||(double)(MathAbs(xend[i]-xexact[i]))>(1.0E6*CMath::m_machineepsilon);
|
|
}
|
|
else
|
|
waserrors=true;
|
|
//--- Test 2: reported by Vanderlande Industries.
|
|
//--- Tests algorithm ability to handle degenerate constraints.
|
|
a=matrix<double>::Identity(3,3);
|
|
b=vector<double>::Full(3,-50);
|
|
b.Set(2,-75);
|
|
bndl=vector<double>::Zeros(3);
|
|
bndu=vector<double>::Full(3,100);
|
|
bndu.Set(2,150);
|
|
xstart=bndu;
|
|
xstart.Set(0,0);
|
|
xexact=xstart;
|
|
xexact.Set(2,100);
|
|
c=matrix<double>::Zeros(3,4);
|
|
c.Set(0,0,1);
|
|
c.Set(0,1,-1);
|
|
c.Set(0,3,-100);
|
|
c.Set(1,1,1);
|
|
c.Set(1,2,-1);
|
|
c.Set(2,1,-1);
|
|
c.Set(2,2,1);
|
|
c.Set(2,3,50);
|
|
ct.Resize(3);
|
|
ct.Fill(-1);
|
|
CMinQP::MinQPCreate(3,state);
|
|
SetRandomAlgoConvexLC(state);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetQuadraticTerm(state,a,true);
|
|
CMinQP::MinQPSetStartingPoint(state,xstart);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetLC(state,c,ct,3);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xend,rep);
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<=2; i++)
|
|
waserrors=waserrors||(double)(MathAbs(xend[i]-xexact[i]))>(1.0E6*CMath::m_machineepsilon);
|
|
}
|
|
else
|
|
waserrors=true;
|
|
//--- return result
|
|
result=waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests linearly constrained DENSE-AUL solver |
|
|
//| On failure sets Err to True; on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::DenseAULTests(bool &errorflag)
|
|
{
|
|
CHighQualityRandState rs;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double vv=0;
|
|
int scaletype=0;
|
|
CMatrixDouble rawa;
|
|
CMatrixDouble z;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CMatrixDouble rawc;
|
|
CRowInt rawct;
|
|
int rawccnt=0;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
CRowDouble xf;
|
|
CRowDouble r;
|
|
CRowDouble xsol;
|
|
CRowDouble s;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
double epsx=0;
|
|
double xtol=0;
|
|
double rho=0;
|
|
int outerits=0;
|
|
CDenseSolverReport svrep;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test that unconstrained problem is solved with high precision,
|
|
//--- independently of Rho and/or outer iterations count.
|
|
//--- 50% of problems are rescaled wildly (with scale being passed to
|
|
//--- the solver).
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-7;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(scaletype=0; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate random A, b, X0 and XSOL
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xsol,rawa,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Generate scale vector, apply it
|
|
s=vector<double>::Ones(n);
|
|
if(scaletype>0)
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
rawa.Mul(i,j,1.0/(s[i]*s[j]));
|
|
b/=s;
|
|
x0*=s;
|
|
xsol*=s;
|
|
//--- Create optimizer, solve
|
|
rho=MathPow(10,2*CHighQualityRand::HQRndUniformR(rs));
|
|
outerits=CHighQualityRand::HQRndUniformI(rs,5);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7154");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7158");
|
|
}
|
|
}
|
|
//--- Test that problem with zero constraint matrix can be solved
|
|
//--- (with high precision). We do not perform any additional "tweaks"
|
|
//--- like scaling of variables, just want to test ability to handle
|
|
//--- zero matrices.
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-7;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Generate random A, b, X0 and XSOL
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xsol,rawa,i);
|
|
b.Set(i,-v);
|
|
}
|
|
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
//--- Create optimizer, solve
|
|
rho=MathPow(10,2*CHighQualityRand::HQRndUniformR(rs));
|
|
outerits=CHighQualityRand::HQRndUniformI(rs,5);
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
CMinQP::MinQPSetLC(state,rawc,rawct,rawccnt);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7218");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(xf[i]-xsol[i])>xtol,"testminqpunit.ap:7222");
|
|
}
|
|
//--- Test that box/linearly inequality constrained problem with ALL constraints
|
|
//--- being inactive at BOTH initial and final points is solved with high precision.
|
|
//--- 50% of problems are rescaled wildly (with scale being passed to
|
|
//--- the solver).
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-7;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(scaletype=0; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate random A, b, X0 and XSOL
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xsol,rawa,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Generate such set of inequality constraints that ALL
|
|
//--- constraints are inactive at both X0 and XSOL.
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,MathMin(x0[i],xsol[i])-1-CHighQualityRand::HQRndUniformR(rs));
|
|
bndu.Set(i,MathMax(x0[i],xsol[i])+1+CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=0;
|
|
vv=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v+=rawc.Get(i,j)*x0[j];
|
|
vv+=rawc.Get(i,j)*xsol[j];
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
rawct.Set(i,1);
|
|
rawc.Set(i,n,MathMin(v,vv)-1-CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
else
|
|
{
|
|
rawct.Set(i,-1);
|
|
rawc.Set(i,n,MathMax(v,vv)+1+CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
}
|
|
//--- Generate scale vector, apply it
|
|
s=vector<double>::Ones(n);
|
|
if(scaletype>0)
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
rawa.Mul(i,j,1.0/(s[i]*s[j]));
|
|
b/=s;
|
|
x0*=s;
|
|
xsol*=s;
|
|
bndl*=s;
|
|
bndu*=s;
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rawc.Mul(i,j,1.0/s[j]);
|
|
}
|
|
//--- Create optimizer, solve
|
|
rho=100.0;
|
|
outerits=1;
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7339");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7343");
|
|
}
|
|
}
|
|
//--- Test that linear equality constrained problem is solved with high precision.
|
|
//--- 50% of problems are rescaled wildly (variable scaling, with scale being
|
|
//--- passed to the solver).
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-6;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(scaletype=0; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate random A, b, X0, constraints
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CMatGen::RMatrixRndCond(n,10,z);
|
|
rawccnt=MathMax(n-2,0);
|
|
if(rawccnt>0)
|
|
{
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rawc.Set(i,j,z.Get(i,j));
|
|
rawct.Set(i,0);
|
|
rawc.Set(i,n,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
}
|
|
//--- Generate scale vector, apply it
|
|
s=vector<double>::Ones(n);
|
|
if(scaletype>0)
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
rawa.Mul(i,j,1.0/(s[i]*s[j]));
|
|
b/=s;
|
|
x0*=s;
|
|
xsol*=s;
|
|
for(i=0; i<rawccnt; i++)
|
|
for(j=0; j<n; j++)
|
|
rawc.Mul(i,j,1.0/s[j]);
|
|
//--- Create optimizer, solve
|
|
rho=100.0;
|
|
outerits=3;
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Solve problem analytically using Lagrangian approach
|
|
z=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
|
|
r=vector<double>::Zeros(n+rawccnt);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
z.Set(i,j,rawa.Get(i,j));
|
|
for(i=0; i<n; i++)
|
|
r.Set(i,-b[i]);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
z.Set(n+i,j,rawc.Get(i,j));
|
|
z.Set(j,n+i,rawc.Get(i,j));
|
|
}
|
|
r.Set(n+i,rawc.Get(i,n));
|
|
}
|
|
CDenseSolver::RMatrixSolve(z,n+rawccnt,r,k,svrep,xsol);
|
|
//--- check
|
|
if(!CAp::Assert(k>0,"MinQPTest: integrity check failed"))
|
|
{
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7453");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7457");
|
|
}
|
|
}
|
|
//--- MEDIUM-SCALE VERSION OF PREVIOUS TEST.
|
|
//--- Test that linear equality constrained problem is solved with high precision.
|
|
//--- 50% of problems are rescaled wildly (variable scaling, with scale being
|
|
//--- passed to the solver).
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-6;
|
|
for(n=99; n<=101; n++)
|
|
{
|
|
for(scaletype=0; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate random A, b, X0, constraints
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CMatGen::RMatrixRndCond(n,10,z);
|
|
rawccnt=MathMax(n-2,0);
|
|
if(rawccnt>0)
|
|
{
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rawc.Set(i,j,z.Get(i,j));
|
|
rawct.Set(i,0);
|
|
rawc.Set(i,n,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
}
|
|
//--- Generate scale vector, apply it
|
|
s=vector<double>::Ones(n);
|
|
if(scaletype>0)
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
rawa.Mul(i,j,1.0/(s[i]*s[j]));
|
|
b/=s;
|
|
x0*=s;
|
|
xsol*=s;
|
|
for(i=0; i<rawccnt; i++)
|
|
for(j=0; j<n; j++)
|
|
rawc.Mul(i,j,1.0/s[j]);
|
|
//--- Create optimizer, solve
|
|
rho=100.0;
|
|
outerits=3;
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Solve problem analytically using Lagrangian approach
|
|
z=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
|
|
r=vector<double>::Zeros(n+rawccnt);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
z.Set(i,j,rawa.Get(i,j));
|
|
for(i=0; i<n; i++)
|
|
r.Set(i,-b[i]);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
z.Set(n+i,j,rawc.Get(i,j));
|
|
z.Set(j,n+i,rawc.Get(i,j));
|
|
}
|
|
r.Set(n+i,rawc.Get(i,n));
|
|
}
|
|
CDenseSolver::RMatrixSolve(z,n+rawccnt,r,k,svrep,xsol);
|
|
if(!CAp::Assert(k>0,"MinQPTest: integrity check failed"))
|
|
{
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7569");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7573");
|
|
}
|
|
}
|
|
//--- Test that constraints are automatically scaled to adapt to problem curvature
|
|
//--- (that multiplication of A and b by some large/small number does not affect
|
|
//--- solver).
|
|
//--- We generate random well-scaled problem, and multiply A/b by some large/small number,
|
|
//--- and test that problem is still solved with high precision.
|
|
//--- NOTE: just to make things worse, we rescale variables randomly, but primary
|
|
//--- idea of this test is to check for multiplication of A/B
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-6;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(scaletype=-1; scaletype<=1; scaletype++)
|
|
{
|
|
//--- Generate random A, b, X0, constraints
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CMatGen::RMatrixRndCond(n,10,z);
|
|
rawccnt=MathMax(n-2,0);
|
|
if(rawccnt>0)
|
|
{
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rawc.Set(i,j,z.Get(i,j));
|
|
rawct.Set(i,0);
|
|
rawc.Set(i,n,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
}
|
|
//--- Solve problem analytically using Lagrangian approach
|
|
z=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
|
|
r=vector<double>::Zeros(n+rawccnt);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
z.Set(i,j,rawa.Get(i,j));
|
|
for(i=0; i<n; i++)
|
|
r.Set(i,-b[i]);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
z.Set(n+i,j,rawc.Get(i,j));
|
|
z.Set(j,n+i,rawc.Get(i,j));
|
|
}
|
|
r.Set(n+i,rawc.Get(i,n));
|
|
}
|
|
CDenseSolver::RMatrixSolve(z,n+rawccnt,r,k,svrep,xsol);
|
|
if(rawccnt>0)
|
|
xsol.Resize(n);
|
|
if(!CAp::Assert(k>0,"MinQPTest: integrity check failed"))
|
|
{
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Generate scale vector, apply it
|
|
s=vector<double>::Ones(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
rawa.Mul(i,j,1.0/(s[i]*s[j]));
|
|
b/=s;
|
|
x0*=s;
|
|
xsol*=s;
|
|
for(i=0; i<rawccnt; i++)
|
|
for(j=0; j<n; j++)
|
|
rawc.Mul(i,j,1.0/s[j]);
|
|
//--- Multiply A/B by some coefficient with wild magnitude
|
|
v=MathPow(10,10*scaletype);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
rawa.Mul(i,j,v);
|
|
b*=v;
|
|
//--- Create optimizer, solve
|
|
rho=100.0;
|
|
outerits=3;
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7696");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7700");
|
|
}
|
|
}
|
|
//--- Test that problem with slight negative curvature f = -0.001*|x|^2
|
|
//--- subject to general linear (!) constraints -1 <= x[i] <= +1 is
|
|
//--- correctly solved. Initial point is in [-0.001,+0.001] range.
|
|
//--- NOTE: this test is mostly intended for working set selection algorithm;
|
|
//--- it must correctly detect non-SPD problems and select full working
|
|
//--- set; starting with zero working set will result in recognition of
|
|
//--- problem as unbounded one and premature termination of algorithm.
|
|
epsx=1.0E-12;
|
|
xtol=1.0E-6;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Generate A, b, X0, constraints
|
|
rawa=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
rawa.Set(i,i,-0.001);
|
|
x0.Set(i,0.001*CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
rawc=matrix<double>::Zeros(2*n,n+1);
|
|
rawct.Resize(2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
rawc.Set(2*i,i,1);
|
|
rawc.Set(2*i+1,i,1);
|
|
rawc.Set(2*i,n,-1);
|
|
rawc.Set(2*i+1,n,1);
|
|
rawct.Set(2*i,1);
|
|
rawct.Set(2*i+1,-1);
|
|
}
|
|
//--- Create optimizer, solve
|
|
rho=100.0;
|
|
outerits=5;
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPSetQuadraticTerm(state,rawa,true);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
CMinQP::MinQPSetLC(state,rawc,rawct,2*n);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Test
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:7765");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(MathAbs(xf[i])-1))>xtol,"testminqpunit.ap:7769");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests IPM QP solver |
|
|
//| On failure sets Err to True; on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::IPMTests(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
int n=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int nnz=0;
|
|
int nactive=0;
|
|
double f0=0;
|
|
double f1=0;
|
|
CMatrixDouble maina;
|
|
CMatrixDouble fulla;
|
|
CRowDouble s;
|
|
CRowDouble xorigin;
|
|
CRowDouble xf;
|
|
CRowDouble x1;
|
|
CRowDouble g;
|
|
CRowDouble b;
|
|
CRowDouble gtrial;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
int akind=0;
|
|
CMatrixDouble rawc;
|
|
CRowDouble rawcl;
|
|
CRowDouble rawcu;
|
|
int rawccnt=0;
|
|
CSparseMatrix sparsec;
|
|
int sparseccnt=0;
|
|
CMatrixDouble densec;
|
|
int denseccnt=0;
|
|
bool issemidefinite;
|
|
CRowDouble tmp;
|
|
CRowDouble lagbc;
|
|
CRowDouble laglc;
|
|
CSparseMatrix dummysparse;
|
|
CVIPMState vsolver;
|
|
CRowDouble replagbc;
|
|
CRowDouble replaglc;
|
|
int repterminationtype=0;
|
|
double epsx=0;
|
|
double xtol=0;
|
|
CRowDouble xsol;
|
|
CRowDouble x0;
|
|
CRowInt rawct;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test handling of slack variables by IPM solver. We use IPM directly,
|
|
//--- without wrapping it with MinQP interface layer.
|
|
//--- Convex optimization problem with known answer (we generate constraints
|
|
//--- and Lagrange coefficients, and then generate quadratic and linear term
|
|
//--- which satisfy KKT conditions for given Lagrange coefficients):
|
|
//--- * N=2..8
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD, low rank
|
|
//--- * initial point x0 = [0 0 ... 0 0]
|
|
//--- * first, we generate set of linear constraints (without bounds),
|
|
//--- solution point X and Lagrange multipliers for linear and box constraints
|
|
//--- * then we determine bounds on variables and linear constraints which
|
|
//--- align with values/signs of Lagrange multipliers
|
|
//--- * then, having quadratic term A and known constraints and Lagrange
|
|
//--- multipliers we determine linear term B which makes KKT conditions true
|
|
epsx=1.0E-12;
|
|
for(nmain=1; nmain<=8; nmain++)
|
|
{
|
|
for(nslack=0; nslack<=8; nslack++)
|
|
{
|
|
for(akind=0; akind<=2; akind++)
|
|
{
|
|
n=nmain+nslack;
|
|
//--- Generate quadratic term A, origin and scale
|
|
maina=matrix<double>::Zeros(nmain,nmain);
|
|
issemidefinite=true;
|
|
if(akind==1)
|
|
{
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(nmain,50.0,maina);
|
|
issemidefinite=false;
|
|
}
|
|
if(akind==2)
|
|
{
|
|
//--- Low rank semidefinite
|
|
tmp=vector<double>::Zeros(nmain);
|
|
for(k=1; k<=MathMin(3,nmain-1); k++)
|
|
{
|
|
for(i=0; i<nmain; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<nmain; i++)
|
|
for(j=0; j<nmain; j++)
|
|
maina.Add(i,j,tmp[i]*tmp[j]);
|
|
}
|
|
issemidefinite=true;
|
|
}
|
|
issemidefinite=issemidefinite||nslack>0;
|
|
fulla=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<nmain; i++)
|
|
for(j=0; j<nmain; j++)
|
|
fulla.Set(i,j,maina.Get(i,j));
|
|
s=vector<double>::Zeros(n);
|
|
xorigin=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
xorigin.Set(j,CHighQualityRand::HQRndNormal(rs));
|
|
s.Set(j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
//--- Generate linear constraints (left parts)
|
|
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n+1);
|
|
if(rawccnt>0)
|
|
{
|
|
rawc=matrix<double>::Zeros(rawccnt,n);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
nnz=CHighQualityRand::HQRndUniformI(rs,nmain+1);
|
|
for(k=0; k<nnz; k++)
|
|
{
|
|
j=CHighQualityRand::HQRndUniformI(rs,nmain);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
v+=0.1*CApServ::PosSign(v);
|
|
rawc.Set(i,j,v);
|
|
}
|
|
}
|
|
for(j=nmain; j<n; j++)
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
v+=0.1*CApServ::PosSign(v);
|
|
rawc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),j,v);
|
|
}
|
|
}
|
|
//--- Generate Lagrange multipliers, with 0<=NActive<=N-1 being non-zero
|
|
lagbc=vector<double>::Zeros(n);
|
|
laglc=vector<double>::Zeros(rawccnt);
|
|
nactive=CHighQualityRand::HQRndUniformI(rs,n);
|
|
k=CHighQualityRand::HQRndUniformI(rs,MathMin(nactive,rawccnt)+1);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
v+=0.01*CApServ::PosSign(v);
|
|
laglc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),v);
|
|
}
|
|
for(i=k; i<nactive; i++)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
v+=0.01*CApServ::PosSign(v);
|
|
lagbc.Set(CHighQualityRand::HQRndUniformI(rs,n),v);
|
|
}
|
|
//--- Generate solution and gradient at the solution, set B to -G
|
|
xf=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xf.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
g=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=fulla.Get(i,j)*(xf[j]-xorigin[j]);
|
|
g.Set(i,v+lagbc[i]);
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
for(j=0; j<n; j++)
|
|
g.Add(j,laglc[i]*rawc.Get(i,j));
|
|
b=g*(-1.0)+0;
|
|
//--- Set up bounds according to Lagrange multipliers
|
|
//--- NOTE: for semidefinite problems we set all variable bounds
|
|
bndl=vector<double>::Full(n,AL_NEGINF);
|
|
bndu=vector<double>::Full(n,AL_POSINF);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if((CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite) || j>=nmain)
|
|
bndl.Set(j,xf[j]-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if((CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite) || j>=nmain)
|
|
bndu.Set(j,xf[j]+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(lagbc[j]!=0.0)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
|
|
{
|
|
bndl.Set(j,xf[j]);
|
|
bndu.Set(j,xf[j]);
|
|
}
|
|
else
|
|
{
|
|
if(lagbc[j]<0.0)
|
|
bndl.Set(j,xf[j]);
|
|
if(lagbc[j]>0.0)
|
|
bndu.Set(j,xf[j]);
|
|
}
|
|
}
|
|
}
|
|
rawcl=vector<double>::Full(rawccnt,AL_NEGINF);
|
|
rawcu=vector<double>::Full(rawccnt,AL_POSINF);
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xf,rawc,i);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
rawcl.Set(i,v-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
rawcu.Set(i,v+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
if(laglc[i]!=0.0)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
|
|
{
|
|
//--- Active equality constraint
|
|
rawcl.Set(i,v);
|
|
rawcu.Set(i,v);
|
|
}
|
|
else
|
|
{
|
|
//--- Active inequality constraint
|
|
if(laglc[i]<0.0)
|
|
rawcl.Set(i,v);
|
|
if(laglc[i]>0.0)
|
|
rawcu.Set(i,v);
|
|
}
|
|
}
|
|
if(!MathIsValidNumber(rawcl[i]) && !MathIsValidNumber(rawcu[i]))
|
|
{
|
|
//--- At least one bound must be present for linear constraint
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
rawcl.Set(i,v-10);
|
|
else
|
|
rawcu.Set(i,v+10);
|
|
}
|
|
}
|
|
//--- Randomly split constraints into dense and sparse parts
|
|
sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1);
|
|
denseccnt=rawccnt-sparseccnt;
|
|
if(sparseccnt>0)
|
|
{
|
|
CSparse::SparseCreate(sparseccnt,n,0,sparsec);
|
|
for(i=0; i<sparseccnt; i++)
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(sparsec,i,j,rawc.Get(i,j));
|
|
CSparse::SparseConvertToCRS(sparsec);
|
|
}
|
|
if(denseccnt>0)
|
|
{
|
|
densec=matrix<double>::Zeros(denseccnt,n);
|
|
for(i=0; i<denseccnt; i++)
|
|
for(j=0; j<n; j++)
|
|
densec.Set(i,j,rawc.Get(i+sparseccnt,j));
|
|
}
|
|
//--- Solve
|
|
CVIPMSolver::VIPMInitDenseWithSlacks(vsolver,s,xorigin,nmain,n);
|
|
CVIPMSolver::VIPMSetQuadraticLinear(vsolver,fulla,dummysparse,0,CHighQualityRand::HQRndNormal(rs)>0.0,b);
|
|
CVIPMSolver::VIPMSetConstraints(vsolver,bndl,bndu,sparsec,sparseccnt,densec,denseccnt,rawcl,rawcu);
|
|
CVIPMSolver::VIPMSetCond(vsolver,epsx,epsx,epsx);
|
|
CVIPMSolver::VIPMOptimize(vsolver,false,x1,replagbc,replaglc,repterminationtype);
|
|
CAp::SetErrorFlag(errorflag,repterminationtype<=0,"testminqpunit.ap:8095");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(x1)<n,"testminqpunit.ap:8096");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(replagbc)<n,"testminqpunit.ap:8097");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(replaglc)<rawccnt,"testminqpunit.ap:8098");
|
|
if(errorflag)
|
|
return;
|
|
//--- Test function value at the solution
|
|
f0=QuadraticTarget(fulla,b,n,x1);
|
|
f1=QuadraticTarget(fulla,b,n,xf);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>(CApServ::RMaxAbs3(f0,f1,1.0)*1.0E-3),"testminqpunit.ap:8107");
|
|
//--- Test Lagrange multipliers returned by the solver
|
|
gtrial=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=b[i];
|
|
for(j=0; j<n; j++)
|
|
v+=fulla.Get(i,j)*(x1[j]-xorigin[j]);
|
|
gtrial.Set(i,v+replagbc[i]);
|
|
}
|
|
for(i=0; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
gtrial.Add(j,replaglc[i]*rawc.Get(i,j));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(gtrial[i]))>1.0E-3,"testminqpunit.ap:8126");
|
|
}
|
|
}
|
|
}
|
|
//--- Test that problem with zero constraint matrix can be solved
|
|
//--- (with high precision). We do not perform any additional "tweaks"
|
|
//--- like scaling of variables, just want to test ability to handle
|
|
//--- zero matrices.
|
|
epsx=1.0E-8;
|
|
xtol=1.0E-5;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Generate random A, b, X0 and XSOL
|
|
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),fulla);
|
|
b=vector<double>::Zeros(n);
|
|
xsol=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xsol,fulla,i);
|
|
b.Set(i,-v);
|
|
}
|
|
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
rawc=matrix<double>::Zeros(rawccnt,n+1);
|
|
rawct.Resize(rawccnt);
|
|
for(i=0; i<rawccnt; i++)
|
|
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
//--- Create optimizer, solve
|
|
CMinQP::MinQPCreate(n,state);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinQP::MinQPSetAlgoDenseIPM(state,epsx);
|
|
else
|
|
CMinQP::MinQPSetAlgoSparseIPM(state,epsx);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(fulla,n,state,rs);
|
|
CMinQP::MinQPSetLC(state,rawc,rawct,rawccnt);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xf,rep);
|
|
//--- Compare against analytically known solution
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:8187");
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xf[i]-xsol[i]))>xtol,"testminqpunit.ap:8191");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests various special properties |
|
|
//| On failure sets Err to True; on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::SpecTests(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
CMinQPState state;
|
|
CMinQPReport rep;
|
|
CMatrixDouble rawa;
|
|
CRowDouble b;
|
|
CRowDouble s;
|
|
CRowDouble x0;
|
|
CRowDouble xs;
|
|
CRowDouble xs2;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
double xtol=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test correctness of MinQPSetScaleDiagAuto():
|
|
//--- * that it correctly handles matrices with positive diagonals
|
|
//--- * that it correctly handles non-positive elements
|
|
//--- First test is performed by comparing one step of automatically
|
|
//--- scaled QP-BLEIC with one step of manually scaled QP-BLEIC.
|
|
xtol=1.0E-6;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
//--- Generate random box constrained QP problem with specially
|
|
//--- crafted diagonal.
|
|
rawa=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Full(n,-1.0);
|
|
bndu=vector<double>::Full(n,1.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
rawa.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
rawa.Set(i,i,MathPow(2,CHighQualityRand::HQRndUniformI(rs,9)-4));
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Create solver
|
|
CMinQP::MinQPCreate(n,state);
|
|
CMinQP::MinQPSetAlgoBLEIC(state,0,0,0,1);
|
|
CMinQP::MinQPSetLinearTerm(state,b);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
CMinQP::MinQPSetBC(state,bndl,bndu);
|
|
CMinQP::MinQPSetStartingPoint(state,x0);
|
|
//--- Solve with automatic scaling
|
|
//--- Solve with manual scaling
|
|
//--- Compare
|
|
CMinQP::MinQPSetScaleAutoDiag(state);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xs,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:8266");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testminqpunit.ap:8267");
|
|
if(errorflag)
|
|
return;
|
|
//---
|
|
s=MathPow(rawa.Diag()+0,(-0.5));
|
|
CMinQP::MinQPSetScale(state,s);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xs2,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:8276");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(xs2)!=n,"testminqpunit.ap:8277");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xs[i]-xs2[i]))>xtol,"testminqpunit.ap:8281");
|
|
//--- Check that automatic scaling fails for matrices with zero or negative elements
|
|
i=CHighQualityRand::HQRndUniformI(rs,n);
|
|
j=CHighQualityRand::HQRndUniformI(rs,2)-1;
|
|
rawa.Set(i,i,j);
|
|
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
|
|
CMinQP::MinQPSetScaleAutoDiag(state);
|
|
CMinQP::MinQPOptimize(state);
|
|
CMinQP::MinQPResults(state,xs,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=-9,"testminqpunit.ap:8294");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function normal |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMinQPUnit::ProjectedAntiGradNorm(int n,CRowDouble &x,
|
|
CRowDouble &g,
|
|
CRowDouble &bndl,
|
|
CRowDouble &bndu)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
double r=0;
|
|
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(x[i]>=bndl[i] && x[i]<=bndu[i],"ProjectedAntiGradNormal: boundary constraints violation"))
|
|
return(EMPTY_VALUE);
|
|
if(((x[i]>bndl[i] && x[i]<bndu[i]) || (x[i]==bndl[i] && (double)(-g[i])>0.0)) || (x[i]==bndu[i] && (double)(-g[i])<0.0))
|
|
r+=g[i]*g[i];
|
|
}
|
|
//--- return result
|
|
result=MathSqrt(r);
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests that norm of bound-constrained gradient at |
|
|
//| point X is less than Eps: |
|
|
//| * unconstrained gradient is A*x+b |
|
|
//| * if I-th component is at the boundary, and antigradient points|
|
|
//| outside of the feasible area, I-th component of constrained |
|
|
//| gradient is zero |
|
|
//| This function accepts QP terms A and B, bound constraints, |
|
|
//| current point, and performs test. Additionally, it checks that |
|
|
//| point is feasible w.r.t. boundary constraints. |
|
|
//| In case of failure, error flag is set. Otherwise, it is not |
|
|
//| modified. |
|
|
//| IMPORTANT: this function does NOT use SetErrorFlag() to modify |
|
|
//| flag. If you want to use SetErrorFlag() for easier |
|
|
//| tracking of errors, you should store flag returned by |
|
|
//| this function into separate variable TmpFlag and call |
|
|
//| SetErrorFlag(ErrorFlag, TmpFlag) yourself. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::TestBCGradAndFeasibility(CMatrixDouble &a,
|
|
CRowDouble &b,
|
|
CRowDouble &bndl,
|
|
CRowDouble &bndu,
|
|
int n,CRowDouble &x,
|
|
double eps,
|
|
bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
double g=0;
|
|
double gnorm=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
g=b[i]+CAblasF::RDotVR(n,x,a,i);
|
|
if(x[i]==bndl[i] && g>0.0)
|
|
g=0;
|
|
if(x[i]==bndu[i] && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
if(x[i]<bndl[i])
|
|
errorflag=true;
|
|
if(x[i]>bndu[i])
|
|
errorflag=true;
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
if(gnorm>eps)
|
|
errorflag=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| set random type of the QP solver. |
|
|
//| All "modern" solvers can be chosen. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| BCTol - expected precision of box constraints handling |
|
|
//| assuming unit scale of variables. |
|
|
//| LCTol - expected precinion of linear constraints |
|
|
//| handling assuming unit scale of variables. |
|
|
//| RESULT: |
|
|
//| Solver type: |
|
|
//| *-1 for QuickQP |
|
|
//| * 0 for BLEIC-QP |
|
|
//| * 1 for DENSE-AUL |
|
|
//| * 2 for DENSE-IPM |
|
|
//| * 3 for SPARSE-IPM |
|
|
//| BCTol and LCTol have following meaning - if some constraint is |
|
|
//| active, it means that we should be at most TOL units away from |
|
|
//| boundary. It is possible that zero value is returned. |
|
|
//| From definition it follows that if we stopped at more than TOL |
|
|
//| units away from the boundary, gradient in corresponding direction|
|
|
//| is nearly zero. |
|
|
//+------------------------------------------------------------------+
|
|
int CTestMinQPUnit::SetRandomAlgoAllModern(CMinQPState &s,
|
|
double &bctol,
|
|
double &lctol)
|
|
{
|
|
//--- create variables
|
|
int result=0;
|
|
|
|
bctol=0;
|
|
lctol=0;
|
|
|
|
result=CMath::RandomInteger(5)-1;
|
|
switch(result)
|
|
{
|
|
case -1:
|
|
CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5);
|
|
bctol=0;
|
|
lctol=0;
|
|
break;
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
|
|
bctol=0;
|
|
lctol=1.0E-8;
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoDenseAUL(s,1.0E-12,1000.0,10);
|
|
bctol=1.0E-3;
|
|
lctol=1.0E-3;
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12);
|
|
bctol=1.0E-3;
|
|
lctol=1.0E-3;
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12);
|
|
bctol=1.0E-3;
|
|
lctol=1.0E-3;
|
|
break;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| set random type of theQP solver |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::SetRandomAlgoNonConvex(CMinQPState &s)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
|
|
i=1+CMath::RandomInteger(2);
|
|
if(i==1)
|
|
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
|
|
if(i==2)
|
|
CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| set random type of theQP solver |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::SetRandomAlgoSemiDefinite(CMinQPState &s,
|
|
double &bctol,
|
|
double &lctol)
|
|
{
|
|
bctol=0;
|
|
lctol=0;
|
|
|
|
int i=1+CMath::RandomInteger(4);
|
|
|
|
switch(i)
|
|
{
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
|
|
bctol=0;
|
|
lctol=1.0E-8;
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5);
|
|
bctol=0;
|
|
lctol=0;
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12);
|
|
bctol=1.0E-3;
|
|
lctol=1.0E-3;
|
|
break;
|
|
case 4:
|
|
CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12);
|
|
bctol=1.0E-3;
|
|
lctol=1.0E-3;
|
|
break;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| set random type of the QP solver, must support boundary |
|
|
//| constraints |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::SetRandomAlgoBC(CMinQPState &s)
|
|
{
|
|
int i=CMath::RandomInteger(2);
|
|
|
|
if(i==0)
|
|
CMinQP::MinQPSetAlgoQuickQP(s,0.0,0.0,1.0E-13,0,true);
|
|
if(i==1)
|
|
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| set random type of the QP solver, must support convex problems |
|
|
//| with boundary/linear constraints |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::SetRandomAlgoConvexLC(CMinQPState &s)
|
|
{
|
|
int i=CMath::RandomInteger(4);
|
|
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
CMinQP::MinQPSetAlgoDenseAUL(s,1.0E-12,10000,15);
|
|
break;
|
|
case 1:
|
|
CMinQP::MinQPSetAlgoBLEIC(s,0.0,0.0,1.0E-12,0);
|
|
break;
|
|
case 2:
|
|
CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12);
|
|
break;
|
|
case 3:
|
|
CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12);
|
|
break;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| set random type of the QP solver, |
|
|
//| must support nonconvex problems with boundary/linear constraints |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::SetRandomAlgoNonConvexLC(CMinQPState &s)
|
|
{
|
|
int i=CMath::RandomInteger(1);
|
|
|
|
if(i==0)
|
|
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Convert dense matrix to sparse matrix using random format |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::DenseToSparse(CMatrixDouble &a,int n,
|
|
CSparseMatrix &s)
|
|
{
|
|
//--- create variables
|
|
CSparseMatrix s0;
|
|
|
|
CSparse::SparseCreate(n,n,n*n,s0);
|
|
for(int i=0; i<n; i++)
|
|
for(int j=0; j<n; j++)
|
|
CSparse::SparseSet(s0,i,j,a.Get(i,j));
|
|
CSparse::SparseCopyToBuf(s0,CMath::RandomInteger(3),s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Randomly split constraints into dense and sparse parts |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::RandomlySplitLC(CMatrixDouble &rawc,
|
|
CRowInt &rawct,
|
|
int rawccnt,int n,
|
|
CSparseMatrix &sparsec,
|
|
CRowInt &sparsect,
|
|
int &sparseccnt,
|
|
CMatrixDouble &densec,
|
|
CRowInt &densect,
|
|
int &denseccnt,
|
|
CHighQualityRandState &rs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
|
|
sparseccnt=0;
|
|
denseccnt=0;
|
|
//--- Split "raw" constraints into dense and sparse parts
|
|
sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1);
|
|
denseccnt=rawccnt-sparseccnt;
|
|
if(sparseccnt>0)
|
|
{
|
|
CSparse::SparseCreate(sparseccnt,n+1,0,sparsec);
|
|
sparsect.Resize(sparseccnt);
|
|
for(i=0; i<sparseccnt; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
CSparse::SparseSet(sparsec,i,j,rawc.Get(i,j));
|
|
sparsect.Set(i,rawct[i]);
|
|
}
|
|
}
|
|
if(denseccnt>0)
|
|
{
|
|
densec=matrix<double>::Zeros(denseccnt,n+1);
|
|
densect.Resize(denseccnt);
|
|
for(i=0; i<denseccnt; i++)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
densec.Set(i,j,rawc.Get(sparseccnt+i,j));
|
|
densect.Set(i,rawct[sparseccnt+i]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Randomly split constraints into dense and sparse parts and set |
|
|
//| them; legacy API is used. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::RandomlySplitAndSetLCLegacy(CMatrixDouble &rawc,
|
|
CRowInt &rawct,
|
|
int rawccnt,int n,
|
|
CMinQPState &state,
|
|
CHighQualityRandState &rs)
|
|
{
|
|
//--- create variables
|
|
CMatrixDouble densec;
|
|
CRowInt densect;
|
|
CSparseMatrix sparsec;
|
|
CRowInt sparsect;
|
|
int denseccnt=0;
|
|
int sparseccnt=0;
|
|
//--- reset constraints
|
|
CMinQP::MinQPSetLC(state,densec,densect,0);
|
|
//--- split and set
|
|
RandomlySplitLC(rawc,rawct,rawccnt,n,sparsec,sparsect,sparseccnt,densec,densect,denseccnt,rs);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.5 || denseccnt*sparseccnt>0)
|
|
CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt);
|
|
else
|
|
{
|
|
if(denseccnt>0)
|
|
CMinQP::MinQPSetLC(state,densec,densect,denseccnt);
|
|
if(sparseccnt>0)
|
|
CMinQP::MinQPSetLCSparse(state,sparsec,sparsect,sparseccnt);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Randomly split 2-sided constraints into dense and sparse parts |
|
|
//| and set them; |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::RandomlySplitAndSetLC2(CMatrixDouble &rawc,
|
|
CRowDouble &rawcl,
|
|
CRowDouble &rawcu,
|
|
int rawccnt,int n,
|
|
CMinQPState &state,
|
|
CHighQualityRandState &rs)
|
|
{
|
|
//--- create variables
|
|
CMatrixDouble densec;
|
|
CRowDouble densecl;
|
|
CRowDouble densecu;
|
|
CSparseMatrix sparsec;
|
|
CRowDouble sparsecl;
|
|
CRowDouble sparsecu;
|
|
CRowDouble wrkcl;
|
|
CRowDouble wrkcu;
|
|
int denseccnt=0;
|
|
int sparseccnt=0;
|
|
int i=0;
|
|
int j=0;
|
|
int appenddense=0;
|
|
int appendsparse=0;
|
|
CRowDouble cv;
|
|
CRowInt ci;
|
|
int nnz=0;
|
|
//--- reset constraints
|
|
CMinQP::MinQPSetLC2Dense(state,densec,densecl,densecu,0);
|
|
//--- Split "raw" constraints into dense and sparse parts
|
|
sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1);
|
|
denseccnt=rawccnt-sparseccnt;
|
|
appenddense=CHighQualityRand::HQRndUniformI(rs,denseccnt+1);
|
|
denseccnt=denseccnt-appenddense;
|
|
appendsparse=CHighQualityRand::HQRndUniformI(rs,sparseccnt+1);
|
|
sparseccnt=sparseccnt-appendsparse;
|
|
wrkcl=vector<double>::Zeros(sparseccnt+denseccnt);
|
|
wrkcu=vector<double>::Zeros(sparseccnt+denseccnt);
|
|
if(sparseccnt>0)
|
|
{
|
|
CSparse::SparseCreate(sparseccnt,n,0,sparsec);
|
|
sparsecl=vector<double>::Zeros(sparseccnt);
|
|
sparsecu=vector<double>::Zeros(sparseccnt);
|
|
for(i=0; i<sparseccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
CSparse::SparseSet(sparsec,i,j,rawc.Get(i,j));
|
|
sparsecl.Set(i,rawcl[i]);
|
|
sparsecu.Set(i,rawcu[i]);
|
|
wrkcl.Set(i,sparsecl[i]);
|
|
wrkcu.Set(i,sparsecu[i]);
|
|
}
|
|
}
|
|
if(denseccnt>0)
|
|
{
|
|
densec=matrix<double>::Zeros(denseccnt,n);
|
|
densecl=vector<double>::Zeros(denseccnt);
|
|
densecu=vector<double>::Zeros(denseccnt);
|
|
for(i=0; i<denseccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
densec.Set(i,j,rawc.Get(sparseccnt+appendsparse+i,j));
|
|
densecl.Set(i,rawcl[sparseccnt+appendsparse+i]);
|
|
densecu.Set(i,rawcu[sparseccnt+appendsparse+i]);
|
|
wrkcl.Set(sparseccnt+i,densecl[i]);
|
|
wrkcu.Set(sparseccnt+i,densecu[i]);
|
|
}
|
|
}
|
|
//--- split and set
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.5 || denseccnt*sparseccnt>0)
|
|
CMinQP::MinQPSetLC2Mixed(state,sparsec,sparseccnt,densec,denseccnt,wrkcl,wrkcu);
|
|
else
|
|
{
|
|
if(denseccnt>0)
|
|
CMinQP::MinQPSetLC2Dense(state,densec,densecl,densecu,denseccnt);
|
|
if(sparseccnt>0)
|
|
CMinQP::MinQPSetLC2(state,sparsec,sparsecl,sparsecu,sparseccnt);
|
|
}
|
|
cv=vector<double>::Zeros(2*n);
|
|
ci.Resize(2*n);
|
|
for(i=sparseccnt; i<sparseccnt+appendsparse; i++)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
//--- Add sparse constraint using AddLC2()
|
|
//--- First, generate sparse representation
|
|
nnz=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(rawc.Get(i,j)!=0.0)
|
|
{
|
|
cv.Set(nnz,rawc.Get(i,j));
|
|
ci.Set(nnz,j);
|
|
nnz++;
|
|
}
|
|
}
|
|
//--- Add duplicates which do not change constraint (after simplification)
|
|
while((nnz>0 && nnz<CAp::Len(ci)) && CHighQualityRand::HQRndUniformR(rs)<(double)(0.75))
|
|
{
|
|
j=CHighQualityRand::HQRndUniformI(rs,nnz);
|
|
ci.Set(nnz,ci[j]);
|
|
cv.Set(nnz,CHighQualityRand::HQRndNormal(rs));
|
|
cv.Add(j,-cv[nnz]);
|
|
nnz++;
|
|
}
|
|
//--- Add constraint to the set
|
|
CMinQP::MinQPAddLC2(state,ci,cv,nnz,rawcl[i],rawcu[i]);
|
|
}
|
|
else
|
|
{
|
|
//--- Add sparse constraint using AddLC2SparseFromDense()
|
|
for(j=0; j<n; j++)
|
|
cv.Set(j,rawc.Get(i,j));
|
|
CMinQP::MinQPAddLC2SparseFromDense(state,cv,rawcl[i],rawcu[i]);
|
|
}
|
|
}
|
|
for(i=rawccnt-appenddense; i<rawccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
cv.Set(j,rawc.Get(i,j));
|
|
CMinQP::MinQPAddLC2Dense(state,cv,rawcl[i],rawcu[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Randomly selects triangle of full symmetric matrix, converts it |
|
|
//| to one of the matrix storage formats (dense or sparse) and sets. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinQPUnit::RandomlySelectConvertAndSetQuadraticTerm(CMatrixDouble &a,
|
|
int n,
|
|
CMinQPState &state,
|
|
CHighQualityRandState &rs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble densea;
|
|
CSparseMatrix sparsea;
|
|
bool isupper;
|
|
bool isdense;
|
|
|
|
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
|
|
isdense=CHighQualityRand::HQRndUniformR(rs)>0.5;
|
|
if(isupper && isdense)
|
|
{
|
|
densea=a.TriU()+0;
|
|
CMinQP::MinQPSetQuadraticTerm(state,densea,isupper);
|
|
return;
|
|
}
|
|
if(!isupper && isdense)
|
|
{
|
|
densea=a.TriL()+0;
|
|
CMinQP::MinQPSetQuadraticTerm(state,densea,isupper);
|
|
return;
|
|
}
|
|
if(isupper && !isdense)
|
|
{
|
|
CSparse::SparseCreate(n,n,0,sparsea);
|
|
for(i=0; i<n; i++)
|
|
for(j=i; j<n; j++)
|
|
CSparse::SparseSet(sparsea,i,j,a.Get(i,j));
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sparsea,isupper);
|
|
return;
|
|
}
|
|
if(!isupper && !isdense)
|
|
{
|
|
CSparse::SparseCreate(n,n,0,sparsea);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<=i; j++)
|
|
CSparse::SparseSet(sparsea,i,j,a.Get(i,j));
|
|
CMinQP::MinQPSetQuadraticTermSparse(state,sparsea,isupper);
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function returns reciprocal of condition number of general |
|
|
//| linear constraints. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMinQPUnit::GetConstraintRCond(CMatrixDouble &c,int k,int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
CRowDouble svdw;
|
|
CMatrixDouble svdu;
|
|
CMatrixDouble svdvt;
|
|
|
|
bool bflag=CSingValueDecompose::RMatrixSVD(c,k,n,0,0,0,svdw,svdu,svdvt);
|
|
//--- check
|
|
if(!CAp::Assert(bflag,"MinQPTest: integrity failure"))
|
|
return(0);
|
|
if(svdw[0]>0.0)
|
|
result=svdw[MathMin(k,n)-1]/svdw[0];
|
|
else
|
|
result=1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Computes target function 0.5*x'*H*x+c'*x |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMinQPUnit::QuadraticTarget(CMatrixDouble &a,
|
|
CRowDouble &b,int n,
|
|
CRowDouble &x)
|
|
{
|
|
double result=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
result=result+b[i]*x[i];
|
|
for(int j=0; j<n; j++)
|
|
result+=0.5*x[i]*a.Get(i,j)*x[j];
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinLPUnit
|
|
{
|
|
public:
|
|
static const int m_solverscount;
|
|
|
|
static bool TestMinLP(bool silent);
|
|
|
|
private:
|
|
static void BasicTests(bool &err);
|
|
static void SingleCallTests(bool &err);
|
|
static void ValidateSolution(CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,int n,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int m,CRowDouble &xcand,CMinLPReport &rep,int solvertype,double &errprim,double &errdual,double &errslack);
|
|
static void GenerateLPProblem(CHighQualityRandState &rs,int n,CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int &m);
|
|
static void GenerateUnboundedLPProblem(CHighQualityRandState &rs,int n,CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int &m);
|
|
static void GenerateInfeasibleLPProblem(CHighQualityRandState &rs,int n,CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int &m);
|
|
static void GenerateCABXD(CHighQualityRandState &rs,int n,int m,CRowDouble &c,CMatrixDouble &a,CRowDouble &xx,CRowDouble &d,CRowInt &basicnonbasic);
|
|
static void GenerateBounds(CHighQualityRandState &rs,int n,int m,CRowDouble &xx,CRowDouble &d,CRowInt &basicnonbasic,CRowDouble &bndl,CRowDouble &bndu,CRowDouble &al,CRowDouble &au);
|
|
static void ModifyAndSendConstraintsTo(int n,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int m,CHighQualityRandState &rs,CMinLPState &state);
|
|
static void SelectRandomSolver(CMinLPState &state);
|
|
static void SelectSolver(CMinLPState &state,int st);
|
|
static void ShiftFromZero(CRowDouble &c,int n,double s);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestMinLPUnit::m_solverscount=2;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinLPUnit::TestMinLP(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result=true;
|
|
bool basicerrors=false;
|
|
bool singlecall=false;
|
|
bool waserrors;
|
|
|
|
BasicTests(basicerrors);
|
|
SingleCallTests(singlecall);
|
|
//--- report
|
|
waserrors=basicerrors||singlecall;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MinLP");
|
|
PrintResult("BASIC TESTS",!basicerrors);
|
|
Print("COMMON TESTS:");
|
|
PrintResult("* single call usage",!singlecall);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic tests; sets error flag on failure, does not touch it on |
|
|
//| success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::BasicTests(bool &err)
|
|
{
|
|
//--- create variables
|
|
double primtol=1.0E-4;
|
|
double dualtol=1.0E-4;
|
|
double slacktol=1.0E-4;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int ccnt=0;
|
|
int pass=0;
|
|
CMinLPState state;
|
|
CMinLPReport rep;
|
|
CRowDouble c;
|
|
CRowDouble x;
|
|
CRowDouble x0;
|
|
CRowDouble x2;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble al;
|
|
CRowDouble au;
|
|
CMatrixDouble a;
|
|
double v0=0;
|
|
double v1=0;
|
|
double errslack=0;
|
|
int solvertype=0;
|
|
CHighQualityRandState rs;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test default state of the solver
|
|
for(pass=1; pass<=5; pass++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectRandomSolver(state);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:100");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:101");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,x[i]!=0.0,"testminlpunit.ap:105");
|
|
}
|
|
//--- Test box constrained problems without linear constraints
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=20; pass++)
|
|
{
|
|
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
|
|
c=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
//--- Feasible bounded problems
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]+CHighQualityRand::HQRndUniformI(rs,3));
|
|
}
|
|
ShiftFromZero(c,n,0.01);
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:135");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:136");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:141");
|
|
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:142");
|
|
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:143");
|
|
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:144");
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v0=CHighQualityRand::HQRndNormal(rs);
|
|
v1=CHighQualityRand::HQRndNormal(rs);
|
|
bndl.Set(i,MathMin(v0,v1));
|
|
bndu.Set(i,MathMax(v0,v1));
|
|
if(c[i]>0.0)
|
|
bndu.Set(i,AL_POSINF);
|
|
if(c[i]<0.0)
|
|
bndl.Set(i,AL_NEGINF);
|
|
}
|
|
ShiftFromZero(c,n,0.01);
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:166");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:167");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:172");
|
|
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:173");
|
|
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:174");
|
|
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:175");
|
|
}
|
|
//--- Feasible bounded problems with zeros in cost vector
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]+CHighQualityRand::HQRndUniformI(rs,3));
|
|
}
|
|
ShiftFromZero(c,n,0.01);
|
|
k=CHighQualityRand::HQRndUniformI(rs,n);
|
|
c.Set(k,0);
|
|
bndl.Set(k,AL_NEGINF);
|
|
bndu.Set(k,AL_POSINF);
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:199");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:200");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:205");
|
|
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:206");
|
|
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:207");
|
|
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:208");
|
|
}
|
|
CAp::SetErrorFlag(err,x[k]!=0.0,"testminlpunit.ap:210");
|
|
bndl.Set(k,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(k,AL_POSINF);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:217");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:218");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:223");
|
|
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:224");
|
|
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:225");
|
|
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:226");
|
|
}
|
|
CAp::SetErrorFlag(err,x[k]<bndl[k],"testminlpunit.ap:228");
|
|
CAp::SetErrorFlag(err,solvertype==0 && x[k]!=(double)(bndl[k]),"testminlpunit.ap:229");
|
|
bndl.Set(k,AL_NEGINF);
|
|
bndu.Set(k,CHighQualityRand::HQRndNormal(rs));
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:236");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:237");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:242");
|
|
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:243");
|
|
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:244");
|
|
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:245");
|
|
}
|
|
CAp::SetErrorFlag(err,x[k]>bndu[k],"testminlpunit.ap:247");
|
|
CAp::SetErrorFlag(err,solvertype==0 && x[k]!=bndu[k],"testminlpunit.ap:248");
|
|
//--- Infeasible problems
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Add(i,CHighQualityRand::HQRndUniformI(rs,3));
|
|
}
|
|
k=CHighQualityRand::HQRndUniformI(rs,n);
|
|
bndu.Set(k,bndl[k]-1);
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=-3,"testminlpunit.ap:268");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:269");
|
|
if(err)
|
|
return;
|
|
//--- Unbounded problems
|
|
//--- NOTE: we solve it without regularization, because with regularizer any LP problem is bounded
|
|
if(solvertype!=1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
while(c[i]==0.0);
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]+CHighQualityRand::HQRndUniformI(rs,3));
|
|
}
|
|
k=CHighQualityRand::HQRndUniformI(rs,n);
|
|
if(c[k]>0.0)
|
|
bndl.Set(k,AL_NEGINF);
|
|
else
|
|
bndu.Set(k,AL_POSINF);
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=-4,"testminlpunit.ap:300");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:301");
|
|
if(err)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//--- Test linearly constrained problems with all variables being boxed ones
|
|
//--- and with all linear constraints being inequality ones satisfied in the
|
|
//--- box internals.
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
|
|
ccnt=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
c=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
a=matrix<double>::Zeros(ccnt,n);
|
|
al=vector<double>::Zeros(ccnt);
|
|
au=vector<double>::Zeros(ccnt);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-0.5-0.5*CHighQualityRand::HQRndUniformR(rs));
|
|
bndu.Set(i,0.5+0.5*CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
ShiftFromZero(c,n,0.01);
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
al.Set(i,-(2*n));
|
|
au.Set(i,2*n);
|
|
}
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state,a,al,au,ccnt);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:343");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:344");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:349");
|
|
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:350");
|
|
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:351");
|
|
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:352");
|
|
}
|
|
}
|
|
//--- Test linearly constrained problems with all variables being boxed ones
|
|
//--- and with random linear constraints, with at least one feasible point
|
|
//--- in the box internals.
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
|
|
ccnt=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
c=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
a=matrix<double>::Zeros(ccnt,n);
|
|
al=vector<double>::Zeros(ccnt);
|
|
au=vector<double>::Zeros(ccnt);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-0.5-0.5*CHighQualityRand::HQRndUniformR(rs));
|
|
bndu.Set(i,0.5+0.5*CHighQualityRand::HQRndUniformR(rs));
|
|
x0.Set(i,0.5*(bndl[i]+bndu[i]));
|
|
}
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
v0=0.0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v0+=a.Get(i,j)*x0[j];
|
|
}
|
|
al.Set(i,v0-0.25*CHighQualityRand::HQRndUniformR(rs));
|
|
au.Set(i,v0+0.25*CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
CMinLP::MinLPCreate(n,state);
|
|
SelectSolver(state,solvertype);
|
|
CMinLP::MinLPSetCost(state,c);
|
|
CMinLP::MinLPSetBC(state,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state,a,al,au,ccnt);
|
|
CMinLP::MinLPOptimize(state);
|
|
CMinLP::MinLPResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:397");
|
|
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testminlpunit.ap:398");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_y)!=ccnt,"testminlpunit.ap:399");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stats)!=n+ccnt,"testminlpunit.ap:400");
|
|
if(err)
|
|
return;
|
|
ValidateSolution(c,bndl,bndu,n,a,al,au,ccnt,x,rep,solvertype,v0,v1,errslack);
|
|
CAp::SetErrorFlag(err,MathAbs(v0)>primtol,"testminlpunit.ap:404");
|
|
CAp::SetErrorFlag(err,MathAbs(v1)>dualtol,"testminlpunit.ap:405");
|
|
CAp::SetErrorFlag(err,MathAbs(errslack)>slacktol,"testminlpunit.ap:406");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Single - call tests; |
|
|
//| sets error flag on failure, does not touch it on success. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::SingleCallTests(bool &err)
|
|
{
|
|
//--- create variables
|
|
double primtol=1.0E-3;
|
|
double dualtol=1.0E-3;
|
|
double slacktol=1.0E-3;
|
|
double etol=1.0E-8;
|
|
double ftol=1.0E-3;
|
|
int n=0;
|
|
int pass=0;
|
|
CMinLPState state0;
|
|
CMinLPReport rep0;
|
|
CMinLPState state1;
|
|
CMinLPReport rep1;
|
|
int i=0;
|
|
int j=0;
|
|
CRowDouble c;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble al;
|
|
CRowDouble au;
|
|
CRowDouble s;
|
|
CRowDouble bndls;
|
|
CRowDouble bndus;
|
|
CRowDouble cs;
|
|
CMatrixDouble a;
|
|
CMatrixDouble a1;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
int m=0;
|
|
int n0=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double f=0;
|
|
double f1=0;
|
|
double errp=0;
|
|
double errd=0;
|
|
double errs=0;
|
|
double alpha=0;
|
|
CHighQualityRandState rs;
|
|
int solvertype=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Try different feasible problems
|
|
for(n=1; n<=50; n++)
|
|
{
|
|
for(pass=1; pass<=30; pass++)
|
|
{
|
|
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
|
|
//--- Generate random feasible problem and solve it using basic MinLPSetLC2Dense() API
|
|
GenerateLPProblem(rs,n,c,bndl,bndu,a,al,au,m);
|
|
CMinLP::MinLPCreate(n,state0);
|
|
SelectSolver(state0,solvertype);
|
|
CMinLP::MinLPSetCost(state0,c);
|
|
CMinLP::MinLPSetBC(state0,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
|
|
CMinLP::MinLPOptimize(state0);
|
|
CMinLP::MinLPResults(state0,x0,rep0);
|
|
CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:470");
|
|
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:471");
|
|
if(err)
|
|
return;
|
|
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
|
|
CAp::SetErrorFlag(err,MathAbs(errp)>primtol,"testminlpunit.ap:475");
|
|
CAp::SetErrorFlag(err,MathAbs(errd)>dualtol,"testminlpunit.ap:476");
|
|
CAp::SetErrorFlag(err,MathAbs(errs)>slacktol,"testminlpunit.ap:477");
|
|
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,1)),"testminlpunit.ap:478");
|
|
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,1)),"testminlpunit.ap:479");
|
|
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,1)),"testminlpunit.ap:480");
|
|
//--- Apply random modification to the problem (and to the way
|
|
//--- we pass constraints to the solver), try solving one more time
|
|
//--- and compare with the original solution.
|
|
//--- NOTE: because our test suite has degenerate problems, we do
|
|
//--- not compare points returned - only target values are
|
|
//--- compared.
|
|
CMinLP::MinLPCreate(n,state1);
|
|
SelectSolver(state1,solvertype);
|
|
CMinLP::MinLPSetCost(state1,c);
|
|
CMinLP::MinLPSetBC(state1,bndl,bndu);
|
|
ModifyAndSendConstraintsTo(n,a,al,au,m,rs,state1);
|
|
CMinLP::MinLPOptimize(state1);
|
|
CMinLP::MinLPResults(state1,x1,rep1);
|
|
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:500");
|
|
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:501");
|
|
if(err)
|
|
return;
|
|
f=CAblasF::RDotV(n,x0,c);
|
|
f1=CAblasF::RDotV(n,x1,c);
|
|
CAp::SetErrorFlag(err,MathAbs(f1-f)>ftol,"testminlpunit.ap:511");
|
|
//--- Test scaling (random scale is applied, target values for
|
|
//--- original and scaled problems are compared).
|
|
//--- NOTE: because our test suite has degenerate problems, we do
|
|
//--- not compare points returned - only target values.
|
|
s=vector<double>::Zeros(n);
|
|
cs=vector<double>::Zeros(n);
|
|
bndls=vector<double>::Zeros(n);
|
|
bndus=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndUniformI(rs,21)-10));
|
|
cs=c/s+0;
|
|
bndls=bndl*s+0;
|
|
bndus=bndu*s+0;
|
|
a1=matrix<double>::Zeros(m,n);
|
|
for(i=0; i<m; i++)
|
|
a1.Row(i,a[i]/s.ToVector());
|
|
CMinLP::MinLPCreate(n,state1);
|
|
SelectSolver(state1,solvertype);
|
|
CMinLP::MinLPSetScale(state1,s);
|
|
CMinLP::MinLPSetCost(state1,cs);
|
|
CMinLP::MinLPSetBC(state1,bndls,bndus);
|
|
CMinLP::MinLPSetLC2Dense(state1,a1,al,au,m);
|
|
CMinLP::MinLPOptimize(state1);
|
|
CMinLP::MinLPResults(state1,x1,rep1);
|
|
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:544");
|
|
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:545");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,(double)(MathAbs(x0[i]-x1[i]/s[i]))>(1000.0*CMath::m_machineepsilon),"testminlpunit.ap:557");
|
|
}
|
|
}
|
|
//--- Test infeasible/unbounded problems
|
|
for(n=1; n<=50; n++)
|
|
{
|
|
for(pass=1; pass<=30; pass++)
|
|
{
|
|
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
|
|
//--- Generate random primal unbounded
|
|
//--- NOTE: because we use constraint validation code, we can not
|
|
//--- use ModifyAndSendConstraintsTo() function - it permutes
|
|
//--- constraints order and prevents correct validation.
|
|
GenerateUnboundedLPProblem(rs,n,c,bndl,bndu,a,al,au,m);
|
|
CMinLP::MinLPCreate(n,state0);
|
|
SelectSolver(state0,solvertype);
|
|
CMinLP::MinLPSetCost(state0,c);
|
|
CMinLP::MinLPSetBC(state0,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
|
|
CMinLP::MinLPOptimize(state0);
|
|
CMinLP::MinLPResults(state0,x0,rep0);
|
|
CAp::SetErrorFlag(err,rep0.m_terminationtype!=-4 && rep0.m_terminationtype!=-2,"testminlpunit.ap:584");
|
|
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:585");
|
|
if(err)
|
|
return;
|
|
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
|
|
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:589");
|
|
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:590");
|
|
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:591");
|
|
//--- Generate random primal infeasible
|
|
//--- NOTE: because we use constraint validation code, we can not
|
|
//--- use ModifyAndSendConstraintsTo() function - it permutes
|
|
//--- constraints order and prevents correct validation.
|
|
GenerateInfeasibleLPProblem(rs,n,c,bndl,bndu,a,al,au,m);
|
|
CMinLP::MinLPCreate(n,state0);
|
|
SelectSolver(state0,solvertype);
|
|
CMinLP::MinLPSetCost(state0,c);
|
|
CMinLP::MinLPSetBC(state0,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
|
|
CMinLP::MinLPOptimize(state0);
|
|
CMinLP::MinLPResults(state0,x0,rep0);
|
|
CAp::SetErrorFlag(err,rep0.m_terminationtype!=-3 && rep0.m_terminationtype!=-2,"testminlpunit.ap:610");
|
|
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:611");
|
|
if(err)
|
|
return;
|
|
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
|
|
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:615");
|
|
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:616");
|
|
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:617");
|
|
}
|
|
}
|
|
for(n=2; n<=50; n++)
|
|
{
|
|
for(pass=1; pass<=30; pass++)
|
|
{
|
|
//--- Special test for simplex solver.
|
|
//--- Generate carefully crafted primal infeasible - the problem is ALMOST feasible.
|
|
//--- So, we expect simplex solver to Stop at the best point possible. We also test
|
|
//--- that X and Y are correctly unscaled on return.
|
|
//--- check
|
|
if(!CAp::Assert(n>=2,"LPTEST: integrity check failed"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
n0=n/2;
|
|
//--- check
|
|
if(!CAp::Assert(n0<n,"LPTEST: integrity check failed"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
alpha=0.0001;
|
|
c=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(i<n0)
|
|
c.Set(i,-MathPow(2,4*CHighQualityRand::HQRndUniformR(rs)-2));
|
|
else
|
|
c.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
bndl.Set(i,-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
bndu.Set(i,1.0);
|
|
s.Set(i,MathPow(2,2*CHighQualityRand::HQRndUniformR(rs)-1));
|
|
}
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,n);
|
|
a=matrix<double>::Zeros(m,n);
|
|
al=vector<double>::Zeros(m);
|
|
au=vector<double>::Zeros(m);
|
|
for(i=0; i<=m-2; i++)
|
|
{
|
|
v0=0;
|
|
for(j=n0; j<n; j++)
|
|
{
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v0+=0.5*a.Get(i,j);
|
|
}
|
|
al.Set(i,v0-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
au.Set(i,v0+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
v0=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(i<n0)
|
|
a.Set(m-1,i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
else
|
|
a.Set(m-1,i,0);
|
|
v0+=a[m-1][ i];
|
|
}
|
|
al.Set(m-1,v0+alpha);
|
|
au.Set(m-1,al[m-1]);
|
|
CMinLP::MinLPCreate(n,state0);
|
|
CMinLP::MinLPSetAlgoDSS(state0,0.0);
|
|
CMinLP::MinLPSetCost(state0,c);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinLP::MinLPSetScale(state0,s);
|
|
CMinLP::MinLPSetBC(state0,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
|
|
CMinLP::MinLPOptimize(state0);
|
|
CMinLP::MinLPResults(state0,x0,rep0);
|
|
CAp::SetErrorFlag(err,rep0.m_terminationtype!=-3 && rep0.m_terminationtype!=-2,"testminlpunit.ap:686");
|
|
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:687");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n0 ; i++)
|
|
CAp::SetErrorFlag(err,x0[i]<1.0,"testminlpunit.ap:691");
|
|
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
|
|
CAp::SetErrorFlag(err,MathAbs(errd)>0.001,"testminlpunit.ap:693");
|
|
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,1)),"testminlpunit.ap:694");
|
|
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,1)),"testminlpunit.ap:695");
|
|
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,1)),"testminlpunit.ap:696");
|
|
}
|
|
}
|
|
//--- Check SetBCAll() and SetBCi()
|
|
//--- We generate random problem with box constraints
|
|
//--- L<=x[i]<=U, with L<0<U, and random linear constraints
|
|
//--- feasible at x=0.
|
|
//--- In order to test SetBCAll() we solve it two times,
|
|
//--- first one with box constraints specified via setbc(),
|
|
//--- second one with setbcall(). Both solutions are compared.
|
|
//--- After that we rewrite box constraints to be different
|
|
//--- for each variable, and send them to the first solver
|
|
//--- via setbc() and to the second one via sequential setbci().
|
|
//--- Both solutions are compared.
|
|
for(n=1; n<=30; n++)
|
|
{
|
|
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
|
|
v0=-MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
v1=MathPow(2,CHighQualityRand::HQRndNormal(rs));
|
|
m=CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
c=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Full(n,v0);
|
|
bndu=vector<double>::Full(n,v1);
|
|
a=matrix<double>::Zeros(m,n);
|
|
al=vector<double>::Zeros(m);
|
|
au=vector<double>::Zeros(m);
|
|
for(i=0; i<n; i++)
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndNormal(rs));
|
|
al.Set(i,-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
au.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
//--- SetBCAll() vs SetBC()
|
|
x0=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
|
|
x1=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
|
|
CMinLP::MinLPCreate(n,state0);
|
|
SelectSolver(state0,solvertype);
|
|
CMinLP::MinLPSetCost(state0,c);
|
|
CMinLP::MinLPSetBC(state0,bndl,bndu);
|
|
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
|
|
CMinLP::MinLPOptimize(state0);
|
|
CMinLP::MinLPResults(state0,x0,rep0);
|
|
CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:754");
|
|
CAp::SetErrorFlag(err,CAp::Len(x0)!=n,"testminlpunit.ap:755");
|
|
if(err)
|
|
return;
|
|
CMinLP::MinLPCreate(n,state1);
|
|
SelectSolver(state1,solvertype);
|
|
CMinLP::MinLPSetCost(state1,c);
|
|
CMinLP::MinLPSetBCAll(state1,v0,v1);
|
|
CMinLP::MinLPSetLC2Dense(state1,a,al,au,m);
|
|
CMinLP::MinLPOptimize(state1);
|
|
CMinLP::MinLPResults(state1,x1,rep1);
|
|
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:765");
|
|
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:766");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,x0[i]!=x1[i],"testminlpunit.ap:770");
|
|
//--- SetBCi() vs SetBC()
|
|
x0=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
|
|
x1=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
bndu.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
CMinLP::MinLPSetBCi(state1,i,bndl[i],bndu[i]);
|
|
}
|
|
CMinLP::MinLPSetBC(state0,bndl,bndu);
|
|
CMinLP::MinLPOptimize(state0);
|
|
CMinLP::MinLPResults(state0,x0,rep0);
|
|
CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:786");
|
|
CAp::SetErrorFlag(err,CAp::Len(x0)!=n,"testminlpunit.ap:787");
|
|
if(err)
|
|
return;
|
|
CMinLP::MinLPOptimize(state1);
|
|
CMinLP::MinLPResults(state1,x1,rep1);
|
|
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:792");
|
|
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:793");
|
|
if(err)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,x0[i]!=x1[i],"testminlpunit.ap:797");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function validates solution returned by LP solver. It |
|
|
//| returns primal and dual feasibility errors for this solution. |
|
|
//| SolverType: |
|
|
//| * 0 for dual simplex solver; additional test relying on |
|
|
//| explicit information about constraint statuses is performed|
|
|
//| * 1 for interior point method; only straightforward dual |
|
|
//| feasibility is tested |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::ValidateSolution(CRowDouble &c,CRowDouble &bndl,
|
|
CRowDouble &bndu,int n,
|
|
CMatrixDouble &a,CRowDouble &al,
|
|
CRowDouble &au,int m,
|
|
CRowDouble &xcand,CMinLPReport &rep,
|
|
int solvertype,double &errprim,
|
|
double &errdual,double &errslack)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
bool checkstats;
|
|
CRowDouble d;
|
|
|
|
errprim=0;
|
|
errdual=0;
|
|
errslack=0;
|
|
checkstats=solvertype==0;
|
|
errprim=0;
|
|
errdual=0;
|
|
errslack=0;
|
|
//--- Test primal feasibility
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(MathIsValidNumber(bndl[i]))
|
|
errprim=MathMax(errprim,bndl[i]-xcand[i]);
|
|
if(MathIsValidNumber(bndu[i]))
|
|
errprim=MathMax(errprim,xcand[i]-bndu[i]);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xcand,a,i);
|
|
if(MathIsValidNumber(al[i]))
|
|
errprim=MathMax(errprim,al[i]-v);
|
|
if(MathIsValidNumber(au[i]))
|
|
errprim=MathMax(errprim,v-au[i]);
|
|
}
|
|
//--- Test dual feasibility using Lagrange multipliers
|
|
d=c;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
d.Add(j,rep.m_laglc[i]*a.Get(i,j));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
d.Add(i,rep.m_lagbc[i]);
|
|
errdual=MathMax(errdual,CAblasF::RMaxAbsV(n,d));
|
|
//--- Test complementary slackness
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(MathIsValidNumber(bndl[i]))
|
|
errslack=MathMax(errslack,MathMax(xcand[i]-bndl[i],0.0)*MathMax(-rep.m_lagbc[i],0.0));
|
|
if(MathIsValidNumber(bndu[i]))
|
|
errslack=MathMax(errslack,MathMax(bndu[i]-xcand[i],0.0)*MathMax(rep.m_lagbc[i],0.0));
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xcand,a,i);
|
|
if(MathIsValidNumber(al[i]))
|
|
errslack=MathMax(errslack,MathMax(v-al[i],0.0)*MathMax(-rep.m_laglc[i],0.0));
|
|
if(MathIsValidNumber(au[i]))
|
|
errslack=MathMax(errslack,MathMax(au[i]-v,0.0)*MathMax(rep.m_laglc[i],0.0));
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| This function generates random LP problem with user - specified |
|
|
//| number of variables, with several problem types being randomly |
|
|
//| chosen internally. |
|
|
//| Constraint counts and types are generated randomly; it is |
|
|
//| possible that zero amount of constraints is generated. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::GenerateLPProblem(CHighQualityRandState &rs,
|
|
int n,CRowDouble &c,
|
|
CRowDouble &bndl,
|
|
CRowDouble &bndu,
|
|
CMatrixDouble &a,
|
|
CRowDouble &al,
|
|
CRowDouble &au,int &m)
|
|
{
|
|
//--- create variables
|
|
int ptype=0;
|
|
int pcount=0;
|
|
CRowDouble x0;
|
|
CRowDouble xx;
|
|
CRowDouble d;
|
|
CRowInt basicnonbasic;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int kk=0;
|
|
int kt=0;
|
|
double v=0;
|
|
double q=0;
|
|
double big=0;
|
|
double mincoeff=0;
|
|
int nprimal=0;
|
|
int ndual=0;
|
|
|
|
m=0;
|
|
x0=vector<double>::Zeros(n);
|
|
c=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
//--- Choose problem type
|
|
pcount=4;
|
|
ptype=CHighQualityRand::HQRndUniformI(rs,pcount);
|
|
mincoeff=0.001;
|
|
//--- A linearly constrained LP problem with all variables/linear constraints
|
|
//--- being boxed (easy start).
|
|
if(ptype==0)
|
|
{
|
|
q=5.0;
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
a=matrix<double>::Zeros(m,n);
|
|
al=vector<double>::Zeros(m);
|
|
au=vector<double>::Zeros(m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
bndu.Set(i,bndl[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
v=0.01+0.98*CHighQualityRand::HQRndUniformR(rs);
|
|
x0.Set(i,(1-v)*bndl[i]+v*bndu[i]);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=0.0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs)*CHighQualityRand::HQRndUniformI(rs,2));
|
|
a.Add(i,j,MathSign(a.Get(i,j))*mincoeff);
|
|
v+=a.Get(i,j)*x0[j];
|
|
}
|
|
al.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
au.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
return;
|
|
}
|
|
//--- A linearly constrained LP problem with up to M variables (linear
|
|
//--- constraints) being non-boxed (free or having just one constraint).
|
|
//--- A bit harder than all-boxed version.
|
|
//--- NOTE: components of C[] corresponding to these non-boxed variables
|
|
//--- are chosen in a way which guarantees dual feasibility of the
|
|
//--- problem.
|
|
if(ptype==1)
|
|
{
|
|
q=2.0;
|
|
big=100.00;
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
a=matrix<double>::Zeros(m,n);
|
|
al=vector<double>::Zeros(m);
|
|
au=vector<double>::Zeros(m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
bndu.Set(i,bndl[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
v=0.01+0.98*CHighQualityRand::HQRndUniformR(rs);
|
|
x0.Set(i,(1-v)*bndl[i]+v*bndu[i]);
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=0.0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
a.Add(i,j,MathSign(a.Get(i,j))*mincoeff);
|
|
v+=a.Get(i,j)*x0[j];
|
|
}
|
|
al.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
au.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
kk=1+CHighQualityRand::HQRndUniformI(rs,m);
|
|
for(k=0; k<=kk-1; k++)
|
|
{
|
|
i=CHighQualityRand::HQRndUniformI(rs,n+m);
|
|
if(i<n)
|
|
{
|
|
kt=CHighQualityRand::HQRndUniformI(rs,3);
|
|
if(kt==0)
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
c.Set(i,-(big*(1+CHighQualityRand::HQRndUniformR(rs))));
|
|
}
|
|
if(kt==1)
|
|
{
|
|
bndu.Set(i,AL_POSINF);
|
|
c.Set(i,big*(1+CHighQualityRand::HQRndUniformR(rs)));
|
|
}
|
|
if(kt==2)
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kt=CHighQualityRand::HQRndUniformI(rs,3);
|
|
if(kt==0)
|
|
au.Set(i-n,AL_POSINF);
|
|
if(kt==1)
|
|
al.Set(i-n,AL_NEGINF);
|
|
if(kt==2)
|
|
{
|
|
al.Set(i-n,AL_NEGINF);
|
|
au.Set(i-n,AL_POSINF);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- A randomly generated non-degenerate LP problem. A mix of fixed,
|
|
//--- range, upper/lower bounds and free variables.
|
|
if(ptype==2)
|
|
{
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
GenerateCABXD(rs,n,m,c,a,xx,d,basicnonbasic);
|
|
GenerateBounds(rs,n,m,xx,d,basicnonbasic,bndl,bndu,al,au);
|
|
return;
|
|
}
|
|
//--- A randomly generated primal/dual degenerate LP problem. A mix of
|
|
//--- fixed, range, upper/lower bounds and free variables.
|
|
if(ptype==3)
|
|
{
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
GenerateCABXD(rs,n,m,c,a,xx,d,basicnonbasic);
|
|
GenerateBounds(rs,n,m,xx,d,basicnonbasic,bndl,bndu,al,au);
|
|
ndual=1+CHighQualityRand::HQRndUniformI(rs,m);
|
|
for(i=0; i<=ndual-1; i++)
|
|
{
|
|
j=basicnonbasic[m+CHighQualityRand::HQRndUniformI(rs,n)];
|
|
if(j<n)
|
|
{
|
|
c.Add(j,- d[j]);
|
|
d.Set(j,0);
|
|
}
|
|
}
|
|
nprimal=1+CHighQualityRand::HQRndUniformI(rs,m);
|
|
for(i=0; i<nprimal; i++)
|
|
{
|
|
j=basicnonbasic[CHighQualityRand::HQRndUniformI(rs,m)];
|
|
if(j<n)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
bndl.Set(j,xx[j]);
|
|
else
|
|
bndu.Set(j,xx[j]);
|
|
}
|
|
else
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
al.Set(j-n,xx[j]);
|
|
else
|
|
au.Set(j-n,xx[j]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
CAp::Assert(false,"GenerateLPProblem failed");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function generates random primal unbounded LP problem. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::GenerateUnboundedLPProblem(CHighQualityRandState &rs,
|
|
int n,CRowDouble &c,
|
|
CRowDouble &bndl,
|
|
CRowDouble &bndu,
|
|
CMatrixDouble &a,
|
|
CRowDouble &al,
|
|
CRowDouble &au,int &m)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int ptype=0;
|
|
int pcount=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double q=0;
|
|
CRowDouble x0;
|
|
|
|
m=0;
|
|
c=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
//--- Choose problem type
|
|
pcount=2;
|
|
ptype=CHighQualityRand::HQRndUniformI(rs,pcount);
|
|
//--- Boundary-only constraints, can be easily made primal unbounded
|
|
if(ptype==0)
|
|
{
|
|
q=5.0;
|
|
m=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
if(c[i]>0.0)
|
|
{
|
|
bndl.Set(i,v);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,v);
|
|
}
|
|
}
|
|
i=CHighQualityRand::HQRndUniformI(rs,n);
|
|
do
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
while(c[i]==0.0);
|
|
c.Add(i,0.1*MathSign(c[i]));
|
|
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
if(c[i]<0.0)
|
|
{
|
|
bndl.Set(i,v);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,v);
|
|
}
|
|
return;
|
|
}
|
|
//--- Boundary and single-sided linear constraints; a bit more tricky to make it primal unbounded
|
|
if(ptype==1)
|
|
{
|
|
q=5.0;
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
a=matrix<double>::Zeros(m,n);
|
|
al=vector<double>::Zeros(m);
|
|
au=vector<double>::Zeros(m);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(c[i]>=0.0)
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,x0[i]);
|
|
}
|
|
else
|
|
{
|
|
bndl.Set(i,x0[i]);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
}
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=0;
|
|
vv=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v+=a.Get(i,j)*x0[j];
|
|
vv+=a.Get(i,j)*c[j];
|
|
}
|
|
if(vv>=0.0)
|
|
{
|
|
al.Set(i,AL_NEGINF);
|
|
au.Set(i,v);
|
|
}
|
|
else
|
|
{
|
|
al.Set(i,v);
|
|
au.Set(i,AL_POSINF);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
CAp::Assert(false,"GenerateUnboundedLPProblem failed");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function generates random primal infeasible LP problem |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::GenerateInfeasibleLPProblem(CHighQualityRandState &rs,
|
|
int n,CRowDouble &c,
|
|
CRowDouble &bndl,
|
|
CRowDouble &bndu,
|
|
CMatrixDouble &a,
|
|
CRowDouble &al,
|
|
CRowDouble &au,int &m)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ptype=0;
|
|
int pcount=0;
|
|
double v=0;
|
|
double q=0;
|
|
double minerr=0;
|
|
CRowDouble xx;
|
|
CRowDouble d;
|
|
CRowDouble x0;
|
|
CRowInt tmpi;
|
|
|
|
m=0;
|
|
c=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
//--- Choose problem type
|
|
pcount=3;
|
|
ptype=CHighQualityRand::HQRndUniformI(rs,pcount);
|
|
//--- Boundary-only constraints, can be easily made primal infeasible
|
|
if(ptype==0)
|
|
{
|
|
q=5.0;
|
|
m=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
bndu.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
i=CHighQualityRand::HQRndUniformI(rs,n);
|
|
v=bndl[i];
|
|
bndl.Set(i,bndu[i]);
|
|
bndu.Set(i,v);
|
|
return;
|
|
}
|
|
//--- Linearly constrained problem with infeasible box constraints
|
|
if(ptype==1)
|
|
{
|
|
q=5;
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
GenerateCABXD(rs,n,m,c,a,xx,d,tmpi);
|
|
GenerateBounds(rs,n,m,xx,d,tmpi,bndl,bndu,al,au);
|
|
i=CHighQualityRand::HQRndUniformI(rs,n);
|
|
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
bndu.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
return;
|
|
}
|
|
//--- Linearly constrained problem with infeasible linear constraints
|
|
if(ptype==2)
|
|
{
|
|
q=5;
|
|
minerr=0.01;
|
|
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
|
|
GenerateCABXD(rs,n,m,c,a,xx,d,tmpi);
|
|
GenerateBounds(rs,n,m,xx,d,tmpi,bndl,bndu,al,au);
|
|
CApServ::RMatrixResize(a,CAp::Rows(a)+1,CAp::Cols(a));
|
|
al.Resize(al.Size()+1);
|
|
au.Resize(au.Size()+1);
|
|
for(i=0; i<n; i++)
|
|
a.Set(m,i,a.Get(m-1,i));
|
|
v=minerr+MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
v=v*MathMax(1.0,MathSqrt(CAblasF::RDotRR(n,a,m,a,m)));
|
|
if(MathIsValidNumber(al[m-1]) && MathIsValidNumber(au[m-1]))
|
|
{
|
|
al.Set(m,au[m-1]+v);
|
|
au.Set(m,al[m]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
if(MathIsValidNumber(al[m-1]) && !MathIsValidNumber(au[m-1]))
|
|
{
|
|
au.Set(m,al[m-1]-v);
|
|
al.Set(m,au[m]-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
if(!MathIsValidNumber(al[m-1]) && MathIsValidNumber(au[m-1]))
|
|
{
|
|
al.Set(m,au[m-1]+v);
|
|
au.Set(m,al[m]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
if(!MathIsValidNumber(al[m-1]) && !MathIsValidNumber(au[m-1]))
|
|
{
|
|
al.Set(m,v);
|
|
au.Set(m,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
m++;
|
|
return;
|
|
}
|
|
CAp::Assert(false,"GenerateUnboundedLPProblem failed");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function generates random C[] and A[] as well as feasible |
|
|
//| point XX[N + M], with XX[0..N - 1] being structural variables and|
|
|
//| XX[N..N + M - 1] being values of logical variables. |
|
|
//| It also splits structural / logical variables into basic/nonbasic|
|
|
//| ones and generates BasicNonbasic[] array, with indexes of basic |
|
|
//| variables being stored in first M positions, and indexes of |
|
|
//| non-basic variables being stored in the next N positions. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::GenerateCABXD(CHighQualityRandState &rs,
|
|
int n,
|
|
int m,
|
|
CRowDouble &c,
|
|
CMatrixDouble &a,
|
|
CRowDouble &xx,
|
|
CRowDouble &d,
|
|
CRowInt &basicnonbasic)
|
|
{
|
|
//--- create variables
|
|
CRowDouble y;
|
|
CRowDouble cx;
|
|
CMatrixDouble ax;
|
|
CRowInt pivots;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int kk=0;
|
|
double v=0;
|
|
double mincoeff=0.001;
|
|
//--- Randomly partition columns into basic and nonbasic ones
|
|
basicnonbasic.Resize(n+m);
|
|
for(i=0; i<=n+m-1; i++)
|
|
basicnonbasic.Set(i,i);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
k=i+CHighQualityRand::HQRndUniformI(rs,n+m-i);
|
|
kk=basicnonbasic[i];
|
|
basicnonbasic.Set(i,basicnonbasic[k]);
|
|
basicnonbasic.Set(k,kk);
|
|
}
|
|
//--- Generate constraint matrix A
|
|
a=matrix<double>::Zeros(m,n);
|
|
ax=matrix<double>::Zeros(m,n+m);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
ax.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
ax.Add(i,j,MathSign(ax.Get(i,j))*mincoeff);
|
|
a.Set(i,j,ax.Get(i,j));
|
|
}
|
|
ax.Set(i,n+i,-1);
|
|
}
|
|
//--- Generate feasible point
|
|
xx=vector<double>::Zeros(n+m);
|
|
for(i=0; i<n; i++)
|
|
xx.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<m; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=xx[j]*ax.Get(i,j);
|
|
xx.Set(n+i,v);
|
|
}
|
|
//--- Generate random Y.
|
|
//--- Entries corresponding to basic constraints must be zero.
|
|
y=vector<double>::Zeros(m);
|
|
for(i=0; i<m; i++)
|
|
y.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<m; i++)
|
|
if(basicnonbasic[i]>=n)
|
|
y.Set(basicnonbasic[i]-n,0);
|
|
//--- Generate D and C
|
|
d=vector<double>::Zeros(n+m);
|
|
CAblas::RMatrixGemVect(n+m,m,-1.0,ax,0,0,1,y,0,0.0,d,0);
|
|
cx=vector<double>::Zeros(n+m);
|
|
for(i=0; i<n; i++)
|
|
cx.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<m; i++)
|
|
cx.Set(basicnonbasic[i],-d[basicnonbasic[i]]);
|
|
CAblasF::RAddV(n+m,1.0,cx,d);
|
|
c=vector<double>::Zeros(n);
|
|
CAblasF::RCopyV(n,cx,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function generates random non-degenerate bounds for given XX|
|
|
//| and D. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::GenerateBounds(CHighQualityRandState &rs,
|
|
int n,int m,
|
|
CRowDouble &xx,
|
|
CRowDouble &d,
|
|
CRowInt &basicnonbasic,
|
|
CRowDouble &bndl,
|
|
CRowDouble &bndu,
|
|
CRowDouble &al,
|
|
CRowDouble &au)
|
|
{
|
|
//--- create variables
|
|
double v0=0;
|
|
double v1=0;
|
|
double q=0;
|
|
int i=0;
|
|
int kk=0;
|
|
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
al=vector<double>::Zeros(m);
|
|
au=vector<double>::Zeros(m);
|
|
//--- Choose bounds in such a way that non-basic variables are
|
|
//--- strictly at the bounds and dual feasible, and all basic
|
|
//--- variables are strictly within bounds.
|
|
q=5;
|
|
for(kk=0; kk<=n+m-1; kk++)
|
|
{
|
|
i=basicnonbasic[kk];
|
|
v0=xx[i]-MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
v1=xx[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
v0=AL_NEGINF;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
v1=AL_POSINF;
|
|
if(kk>=m)
|
|
{
|
|
if((double)(d[i])>=0.0)
|
|
{
|
|
v0=xx[i];
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.05)
|
|
v1=v0;
|
|
}
|
|
else
|
|
{
|
|
v1=xx[i];
|
|
if(CHighQualityRand::HQRndUniformR(rs)<0.05)
|
|
v0=v1;
|
|
}
|
|
}
|
|
if(i<n)
|
|
{
|
|
bndl.Set(i,v0);
|
|
bndu.Set(i,v1);
|
|
}
|
|
else
|
|
{
|
|
al.Set(i-n,v0);
|
|
au.Set(i-n,v1);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets linear constraints using randomly chosen |
|
|
//| sequence of SetLC / AddLC calls. |
|
|
//| It may also modify constraints: |
|
|
//| * split two-sided constraint into two one-sided constraints |
|
|
//| * create duplicates |
|
|
//| * insert zero dummies |
|
|
//| Thus, you should not use this function when you want to test |
|
|
//| things like correctness of Lagrange multipliers and so on. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::ModifyAndSendConstraintsTo(int n,CMatrixDouble &a,
|
|
CRowDouble &al,
|
|
CRowDouble &au,
|
|
int m,
|
|
CHighQualityRandState &rs,
|
|
CMinLPState &state)
|
|
{
|
|
//--- create variables
|
|
int stype=0;
|
|
CRowDouble ai;
|
|
CRowInt idxi;
|
|
int nz=0;
|
|
int nzmod=0;
|
|
CMatrixDouble a1;
|
|
CSparseMatrix sa;
|
|
CRowInt ct;
|
|
int ccnt=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int t=0;
|
|
int minit=0;
|
|
int nadd=0;
|
|
int ndup=0;
|
|
double v=0;
|
|
//--- Choose sequence type
|
|
stype=CHighQualityRand::HQRndUniformI(rs,4);
|
|
//--- Straightforward SetLC2() call
|
|
if(stype==0)
|
|
{
|
|
CMinLP::MinLPSetLC2Dense(state,a,al,au,m);
|
|
return;
|
|
}
|
|
//--- SetLC1() call (conversion to other format)
|
|
if(stype==1)
|
|
{
|
|
a1=matrix<double>::Zeros(2*m,n+1);
|
|
ct.Resize(2*m);
|
|
ccnt=0;
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
a1.Set(ccnt+0,j,a.Get(i,j));
|
|
a1.Set(ccnt+1,j,a.Get(i,j));
|
|
}
|
|
if((MathIsValidNumber(al[i]) && MathIsValidNumber(au[i])) && al[i]==au[i])
|
|
{
|
|
a1.Set(ccnt,n,al[i]);
|
|
ct.Set(ccnt,0);
|
|
ccnt++;
|
|
}
|
|
else
|
|
{
|
|
if(MathIsValidNumber(al[i]))
|
|
{
|
|
a1.Set(ccnt,n,al[i]);
|
|
ct.Set(ccnt,1);
|
|
ccnt++;
|
|
}
|
|
if(MathIsValidNumber(au[i]))
|
|
{
|
|
a1.Set(ccnt,n,au[i]);
|
|
ct.Set(ccnt,-1);
|
|
ccnt++;
|
|
}
|
|
}
|
|
}
|
|
CMinLP::MinLPSetLC(state,a1,ct,ccnt);
|
|
return;
|
|
}
|
|
//--- Straightforward SetLC2Sparse() call
|
|
if(stype==2)
|
|
{
|
|
if(m==0)
|
|
{
|
|
CMinLP::MinLPSetLC2(state,sa,al,au,0);
|
|
return;
|
|
}
|
|
CSparse::SparseCreate(m,n,0,sa);
|
|
for(i=0; i<m; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
if(a.Get(i,j)!=0.0)
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CSparse::SparseConvertToCRS(sa);
|
|
CMinLP::MinLPSetLC2(state,sa,al,au,m);
|
|
return;
|
|
}
|
|
//--- A few rows are added with SetLC2Dense() call, the rest
|
|
//--- is processed with random mix of AddLC2Dense()/AddLC2()
|
|
if(stype==3)
|
|
{
|
|
if(m==0)
|
|
{
|
|
CMinLP::MinLPSetLC2(state,sa,al,au,0);
|
|
return;
|
|
}
|
|
minit=CHighQualityRand::HQRndUniformI(rs,m);
|
|
CMinLP::MinLPSetLC2Dense(state,a,al,au,minit);
|
|
for(i=minit; i<m; i++)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformI(rs,2)==0)
|
|
{
|
|
//--- Add as dense row
|
|
ai=vector<double>::Zeros(n);
|
|
for(j=0; j<n; j++)
|
|
ai.Set(j,a.Get(i,j));
|
|
CMinLP::MinLPAddLC2Dense(state,ai,al[i],au[i]);
|
|
}
|
|
else
|
|
{
|
|
//--- Add as sparse row with shuffle and possible duplicates
|
|
ai=vector<double>::Zeros(n);
|
|
idxi.Resize(n);
|
|
nz=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(a.Get(i,j)!=0.0)
|
|
{
|
|
ai.Set(nz,a.Get(i,j));
|
|
idxi.Set(nz,j);
|
|
nz++;
|
|
}
|
|
}
|
|
ai.Resize(nz);
|
|
idxi.Resize(nz);
|
|
//--- Already existing elements are split in two
|
|
nadd=0;
|
|
if(nz!=0)
|
|
{
|
|
nadd=CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndUniformI(rs,4);
|
|
ai.Resize(nz+nadd);
|
|
idxi.Resize(nz+nadd);
|
|
for(j=0; j<=nadd-1; j++)
|
|
{
|
|
k=CHighQualityRand::HQRndUniformI(rs,nz);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
idxi.Set(nz+j,idxi[k]);
|
|
ai.Set(nz+j,v);
|
|
ai.Set(k,ai[k]-v);
|
|
}
|
|
}
|
|
//--- Possibly nonexistent elements are added as +V and -V
|
|
//--- Do not performed for NZ=0 rows because it may introduce slightly nonzero coefficients
|
|
//--- to exactly zero row (constraint normalization goes crazy).
|
|
ndup=CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndUniformI(rs,4);
|
|
if(nz==0)
|
|
ndup=0;
|
|
ai.Resize(nz+nadd+2*ndup);
|
|
idxi.Resize(nz+nadd+2*ndup);
|
|
for(j=0; j<=ndup-1; j++)
|
|
{
|
|
k=CHighQualityRand::HQRndUniformI(rs,n);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
idxi.Set(nz+nadd+2*j+0,k);
|
|
idxi.Set(nz+nadd+2*j+1,k);
|
|
ai.Set(nz+nadd+2*j+0,v);
|
|
ai.Set(nz+nadd+2*j+1,-v);
|
|
}
|
|
nzmod=nz+nadd+2*ndup;
|
|
for(j=0; j<=nzmod-1; j++)
|
|
{
|
|
k=j+CHighQualityRand::HQRndUniformI(rs,nzmod-j);
|
|
t=idxi[j];
|
|
idxi.Set(j,idxi[k]);
|
|
idxi.Set(k,t);
|
|
v=ai[j];
|
|
ai.Set(j,ai[k]);
|
|
ai.Set(k,v);
|
|
}
|
|
CMinLP::MinLPAddLC2(state,idxi,ai,nzmod,al[i],au[i]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
CAp::Assert(false,"MINLPTest: integrity check failed");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function selects random LP solver |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::SelectRandomSolver(CMinLPState &state)
|
|
{
|
|
int k=CMath::RandomInteger(2);
|
|
|
|
if(k==0)
|
|
CMinLP::MinLPSetAlgoDSS(state,0.0);
|
|
if(k==1)
|
|
CMinLP::MinLPSetAlgoIPM(state,0.0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function selects specific LP solver. |
|
|
//| Reg parameter is used to regularize IPM algo, ignore for DSS |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::SelectSolver(CMinLPState &state,int st)
|
|
{
|
|
if(st==0)
|
|
{
|
|
CMinLP::MinLPSetAlgoDSS(state,0.0);
|
|
return;
|
|
}
|
|
if(st==1)
|
|
{
|
|
CMinLP::MinLPSetAlgoIPM(state,0.0);
|
|
return;
|
|
}
|
|
CAp::Assert(false,"SelectSolver: unexpected solver");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Shifts nonzero elements of C[] away from zero |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinLPUnit::ShiftFromZero(CRowDouble &c,int n,double s)
|
|
{
|
|
for(int i=0; i<n; i++)
|
|
c.Add(i,MathSign(c[i])*s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinNLCUnit
|
|
{
|
|
public:
|
|
static const int m_maxsolvertype;
|
|
static const int m_maxoptguardlevel;
|
|
|
|
static bool CTestMinNLCUnit::TestMinNLC(bool silent);
|
|
|
|
private:
|
|
static void TestBC(bool &waserrors);
|
|
static void TestLC(bool &waserrors);
|
|
static void TestNLC(bool &waserrors);
|
|
static void TestOther(bool &waserrors);
|
|
static void TestOptGuard(bool &waserrors);
|
|
static void TestBugs(bool &waserrors);
|
|
static void TestOptGuardC1Test0ReportForTask0(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n);
|
|
static void TestOptGuardC1Test1ReportForTask0(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n);
|
|
static void TestOptGuardC1Test0ReportForTask1(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n,int goodidx);
|
|
static void TestOptGuardC1Test1ReportForTask1(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n,int goodidx);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestMinNLCUnit::m_maxsolvertype=2;
|
|
const int CTestMinNLCUnit::m_maxoptguardlevel=1;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinNLCUnit::TestMinNLC(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool bcerr;
|
|
bool lcerr;
|
|
bool nlcerr;
|
|
bool othererr;
|
|
bool optguarderr;
|
|
bool bugs;
|
|
|
|
waserrors=false;
|
|
bcerr=false;
|
|
lcerr=false;
|
|
nlcerr=false;
|
|
othererr=false;
|
|
optguarderr=false;
|
|
bugs=false;
|
|
TestBugs(bugs);
|
|
TestBC(bcerr);
|
|
TestLC(lcerr);
|
|
TestNLC(nlcerr);
|
|
TestOther(othererr);
|
|
TestOptGuard(optguarderr);
|
|
//--- end
|
|
waserrors=((((bcerr||lcerr)||nlcerr)||othererr)||bugs)||optguarderr;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MINNLC OPTIMIZATION");
|
|
Print("GENERIC TESTS:");
|
|
PrintResult("* box constrained",!bcerr);
|
|
PrintResult("* linearly constrained",!lcerr);
|
|
PrintResult("* nonlinearly constrained",!nlcerr);
|
|
PrintResult("* other properties",!othererr);
|
|
PrintResult("* optguard integrity monitor",!optguarderr);
|
|
PrintResult("* fixed bugs",!bugs);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests bound constrained quadratic programming |
|
|
//| algorithm. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestBC(bool &waserrors)
|
|
{
|
|
CMinNLCState state;
|
|
CMinNLCReport rep;
|
|
COptGuardReport ogrep;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aulits=0;
|
|
double tolx=0;
|
|
double tolg=0;
|
|
int scaletype=0;
|
|
double rho=0;
|
|
int solvertype=0;
|
|
CMatrixDouble fulla;
|
|
CRowDouble b;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble s;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
double gnorm=0;
|
|
double g=0;
|
|
int prectype=0;
|
|
CHighQualityRandState rs;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
//--- Convex test:
|
|
//--- * N dimensions
|
|
//--- * random number (0..N) of random boundary constraints
|
|
//--- * positive-definite quadratic programming problem
|
|
//--- * initial point is random (maybe infeasible!)
|
|
//--- * random scale (unit or non-unit)
|
|
aulits=10;
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolg=0.01;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(prectype=0; prectype<=2; prectype++)
|
|
{
|
|
//--- Generate well-conditioned problem with unit scale
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
j=CHighQualityRand::HQRndUniformI(rs,5);
|
|
if(j==0)
|
|
bndl.Set(i,0);
|
|
if(j==1)
|
|
bndu.Set(i,0);
|
|
if(j==2)
|
|
{
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]);
|
|
}
|
|
if(j==3)
|
|
{
|
|
bndl.Set(i,-0.1);
|
|
bndu.Set(i,0.1);
|
|
}
|
|
}
|
|
//--- Apply scaling to quadratic/linear term, so problem becomes
|
|
//--- well-conditioned in the scaled coordinates.
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(5*CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
fulla.Mul(i,j,1.0/(s[i]*s[j]));
|
|
}
|
|
x0*=s;
|
|
bndl*=s;
|
|
bndu*=s;
|
|
b/=s;
|
|
//--- Solve problem
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
if(solvertype==0)
|
|
{
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
if(prectype==1)
|
|
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
|
|
if(prectype==2)
|
|
CMinNLC::MinNLCSetPrecExactRobust(state,0);
|
|
}
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(scaletype!=0)
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:240");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:241");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:245");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr>tolx,"testminnlcunit.ap:253");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=n,"testminnlcunit.ap:254");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr>0.0,"testminnlcunit.ap:255");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=0,"testminnlcunit.ap:256");
|
|
//--- Check feasibility properties
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndl[i]) && x1[i]<=(double)(bndl[i]-tolx*s[i]),"testminnlcunit.ap:263");
|
|
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndu[i]) && x1[i]>=(double)(bndu[i]+tolx*s[i]),"testminnlcunit.ap:264");
|
|
}
|
|
//--- Test - calculate scaled constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i]+CAblasF::RDotVR(n,x1,fulla,i);
|
|
g*=s[i];
|
|
if((MathIsValidNumber(bndl[i]) && MathAbs(x1[i]-bndl[i])<(double)(tolx*s[i])) && g>0.0)
|
|
g=0;
|
|
if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<(double)(tolx*s[i])) && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(waserrors,gnorm>tolg,"testminnlcunit.ap:286");
|
|
}
|
|
}
|
|
}
|
|
//--- Non-convex test:
|
|
//--- * N dimensions, N>=2
|
|
//--- * box constraints, x[i] in [-1,+1]
|
|
//--- * A is symmetric indefinite with condition number 50.0
|
|
//--- * random B with normal entries
|
|
//--- * initial point is random, feasible
|
|
//--- * scale is always unit
|
|
//--- We check that constrained problem can be successfully solved.
|
|
//--- We do not check ability to detect unboundedness of unconstrained
|
|
//--- problem because there is such functionality in MinNLC.
|
|
aulits=50;
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolg=0.01;
|
|
for(n=2; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(prectype=0; prectype<=2; prectype++)
|
|
{
|
|
//--- Generate problem
|
|
fulla.Fill(0.0);
|
|
for(i=0; i<n; i++)
|
|
fulla.Set(i,i,-1-CHighQualityRand::HQRndUniformR(rs));
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,0.05*CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-1);
|
|
bndu.Set(i,1);
|
|
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
}
|
|
//--- Solve problem:
|
|
//--- * without constraints we expect failure
|
|
//--- * with constraints algorithm must succeed
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
if(solvertype==0)
|
|
{
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
if(prectype==0)
|
|
CMinNLC::MinNLCSetPrecInexact(state);
|
|
if(prectype==1)
|
|
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
|
|
if(prectype==2)
|
|
CMinNLC::MinNLCSetPrecExactRobust(state,0);
|
|
}
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:378");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:379");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:383");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check feasibility properties
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndl[i]) && x1[i]<=(double)(bndl[i]-tolx),"testminnlcunit.ap:393");
|
|
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndu[i]) && x1[i]>=(double)(bndu[i]+tolx),"testminnlcunit.ap:394");
|
|
}
|
|
//--- Test - calculate scaled constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g=b[i]+CAblasF::RDotVR(n,x1,fulla,i);
|
|
if((MathIsValidNumber(bndl[i]) && MathAbs(x1[i]-bndl[i])<tolx) && g>0.0)
|
|
g=0;
|
|
if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<tolx) && g<0.0)
|
|
g=0;
|
|
gnorm+=CMath::Sqr(g);
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(waserrors,gnorm>tolg,"testminnlcunit.ap:415");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests linearly constrained quadratic programming |
|
|
//| algorithm. |
|
|
//| Sets error flag on failure. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestLC(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int k=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
CMatrixDouble q;
|
|
CMatrixDouble fulla;
|
|
double v=0;
|
|
double vv=0;
|
|
CRowDouble tmp;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble b;
|
|
CRowDouble xs0;
|
|
CRowDouble xstart;
|
|
CRowDouble x;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble xm;
|
|
CRowDouble s;
|
|
CRowDouble g;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CMatrixDouble a;
|
|
CMatrixDouble c;
|
|
CMatrixDouble ce;
|
|
CRowInt ct;
|
|
bool nonnegative[];
|
|
double tolx=0;
|
|
double tolg=0;
|
|
double tolf=0;
|
|
int aulits=0;
|
|
double rho=0;
|
|
CMinNLCState state;
|
|
CMinNLCReport rep;
|
|
COptGuardReport ogrep;
|
|
int scaletype=0;
|
|
double f0=0;
|
|
double f1=0;
|
|
double tolconstr=0;
|
|
int bscale=0;
|
|
int akind=0;
|
|
int ccnt=0;
|
|
int shiftkind=0;
|
|
int prectype=0;
|
|
int solvertype=0;
|
|
double gnrm2=0;
|
|
CHighQualityRandState rs;
|
|
CSNNLSSolver nnls;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
//--- First test:
|
|
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
|
|
//--- orthogonal K*N matrix, x0 is some random vector
|
|
//--- * quadratic programming problem with identity quadratic term A and
|
|
//--- linear term equal to xm*A, where xm is some random vector such
|
|
//--- that Q*xm=0. It is always possible to find such xm, because K<N
|
|
//--- Thus, optimization problem has form 0.5*x'*I*x-xm'*x.
|
|
//--- * exact solution must be equal to x0
|
|
//--- NOTE: this test is important because it is the only linearly constrained one
|
|
//--- which uses non-unit scaling!
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolf=0.0001;
|
|
aulits=50;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
for(prectype=-1; prectype<=2; prectype++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xm=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xm.Set(i,x0[i]);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
v=2*CMath::RandomReal()-1;
|
|
for(i_=0; i_<n; i_++)
|
|
xm.Add(i_,v*q.Get(i,i_));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,-xm[i]);
|
|
//--- Apply scaling to linear term and known solution,
|
|
//--- so problem becomes well-conditioned in the scaled coordinates.
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(5*CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
x0*=s;
|
|
xstart*=s;
|
|
b/=s;
|
|
for(i=0; i<k; i++)
|
|
for(j=0; j<n; j++)
|
|
c.Mul(i,j,1.0/s[j]);
|
|
//--- Create optimizer, solve
|
|
CMinNLC::MinNLCCreate(n,xstart,state);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
if(solvertype==0)
|
|
{
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
if(prectype==0)
|
|
CMinNLC::MinNLCSetPrecInexact(state);
|
|
if(prectype==1)
|
|
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
|
|
if(prectype==2)
|
|
CMinNLC::MinNLCSetPrecExactRobust(state,0);
|
|
}
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]+0.5*CMath::Sqr(state.m_x[i])/CMath::Sqr(s[i]));
|
|
state.m_j.Set(0,i,b[i]+state.m_x[i]/CMath::Sqr(s[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:580");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:581");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:585");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:593");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:594");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:595");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:596");
|
|
//--- Compare with analytic solution
|
|
f0=0;
|
|
f1=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
f0+=b[i]*x0[i]+0.5*CMath::Sqr(x0[i]/s[i]);
|
|
f1+=b[i]*x1[i]+0.5*CMath::Sqr(x1[i]/s[i]);
|
|
}
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(f1-f0))>(double)(tolf),"testminnlcunit.ap:608");
|
|
}
|
|
}
|
|
}
|
|
//--- Inequality constrained problem:
|
|
//--- * N*N diagonal A
|
|
//--- * one inequality constraint q'*x>=0, where q is random unit vector
|
|
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
|
|
//--- where x1 is some random vector
|
|
//--- * either:
|
|
//--- a) x1 is feasible => we must Stop at x1
|
|
//--- b) x1 is infeasible => we must Stop at the boundary q'*x=0 and
|
|
//--- projection of gradient onto q*x=0 must be zero
|
|
//--- NOTE: we make several passes because some specific kind of errors is rarely
|
|
//--- caught by this test, so we need several repetitions.
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolg=0.01;
|
|
aulits=50;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(pass=0; pass<=4; pass++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
xm=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(1,n+1);
|
|
ct.Resize(1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xm.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
do
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
c.Set(0,i,2*CMath::RandomReal()-1);
|
|
v+=CMath::Sqr(c.Get(0,i));
|
|
}
|
|
v=MathSqrt(v);
|
|
}
|
|
while(v==0.0);
|
|
for(i=0; i<n; i++)
|
|
c.Mul(0,i,1.0/v);
|
|
c.Set(0,n,0);
|
|
ct.Set(0,1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xm,fulla,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Apply scaling to linear term and known solution,
|
|
//--- so problem becomes well-conditioned in the scaled coordinates.
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
fulla.Mul(i,j,1.0/(s[i]*s[j]));
|
|
xm*=s;
|
|
xstart*=s;
|
|
b/=s;
|
|
for(j=0; j<n; j++)
|
|
c.Mul(0,j,1.0/s[j]);
|
|
//--- Create optimizer, solve
|
|
CMinNLC::MinNLCCreate(n,xstart,state);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetLC(state,c,ct,1);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:730");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:731");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:735");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:743");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:744");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:745");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:746");
|
|
//--- Test solution
|
|
g=b;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,fulla,i);
|
|
g.Add(i,v);
|
|
}
|
|
v=CAblasF::RDotVR(n,x1,c,0);
|
|
CAp::SetErrorFlag(waserrors,v<(double)(-tolx),"testminnlcunit.ap:759");
|
|
if(v<tolx)
|
|
{
|
|
//--- Point at the boundary, project gradient into
|
|
//--- equality-constrained subspace.
|
|
v=CAblasF::RDotVR(n,g,c,0);
|
|
vv=CAblasF::RDotRR(n,c,0,c,0);
|
|
v=v/vv;
|
|
for(i_=0; i_<n; i_++)
|
|
g.Add(i_,- c.Get(0,i_)*v);
|
|
}
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(g[i]*s[i]);
|
|
CAp::SetErrorFlag(waserrors,MathSqrt(v)>tolg,"testminnlcunit.ap:779");
|
|
}
|
|
}
|
|
//--- Equality-constrained test:
|
|
//--- * N*N SPD A
|
|
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
|
|
//--- orthogonal K*N matrix, x0 is some random vector
|
|
//--- * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
|
|
//--- where xm is some random vector
|
|
//--- * we check feasibility properties of the solution
|
|
//--- * we do not know analytic form of the exact solution,
|
|
//--- but we know that projection of gradient into equality constrained
|
|
//--- subspace must be zero at the solution
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolg=0.01;
|
|
aulits=50;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(k=1; k<n; k++)
|
|
{
|
|
//--- Generate problem: A, b, CMatrix, x0, XStart
|
|
CMatGen::RMatrixRndOrthogonal(n,q);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xm=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xm.Set(i,2*CMath::RandomReal()-1);
|
|
xstart.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
c.Set(i,i_,q.Get(i,i_));
|
|
v=CAblasF::RDotVR(n,x0,q,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xm,fulla,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Create optimizer, solve
|
|
CMinNLC::MinNLCCreate(n,xstart,state);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:874");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:875");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:879");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:887");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:888");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:889");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:890");
|
|
//--- Check feasibility properties and gradient projection
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,c,i);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>tolx,"testminnlcunit.ap:898");
|
|
}
|
|
g=b;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,fulla,i);
|
|
g.Add(i,v);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,g,c,i);
|
|
for(i_=0; i_<n; i_++)
|
|
g.Add(i_,- c.Get(i,i_)*v);
|
|
}
|
|
v=CAblasF::RDotV2(n,g);
|
|
CAp::SetErrorFlag(waserrors,MathSqrt(v)>tolg,"testminnlcunit.ap:913");
|
|
}
|
|
}
|
|
//--- Boundary constraints vs linear ones:
|
|
//--- * N*N SPD A
|
|
//--- * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
|
|
//--- where xm is some random vector from [-1,+1]
|
|
//--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
|
|
//--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
|
|
//--- * initial point xstart is from [-1,+2]
|
|
//--- * we solve two related QP problems:
|
|
//--- a) one with constraints posed as boundary ones
|
|
//--- b) another one with same constraints posed as general linear ones
|
|
//--- both problems must have same solution.
|
|
//--- Here we test that boundary constrained and linear inequality constrained
|
|
//--- solvers give same results.
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolf=0.00001;
|
|
aulits=50;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, x0, XStart, C, CT
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
xm=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xm.Set(i,2*CMath::RandomReal()-1);
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
bndl.Set(i,-(0.1+0.9*CMath::RandomReal()));
|
|
bndu.Set(i,0.1+0.9*CMath::RandomReal());
|
|
c.Set(2*i+0,i,1);
|
|
c.Set(2*i+0,n,bndl[i]);
|
|
ct.Set(2*i+0,1);
|
|
c.Set(2*i+1,i,1);
|
|
c.Set(2*i+1,n,bndu[i]);
|
|
ct.Set(2*i+1,-1);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xm,fulla,i);
|
|
b.Set(i,-v);
|
|
}
|
|
//--- Solve linear inequality constrained problem
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetLC(state,c,ct,2*n);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1013");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1014");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1018");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Solve boundary constrained problem
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x2,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x2,n),"testminnlcunit.ap:1060");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1061");
|
|
if(waserrors)
|
|
return;
|
|
//--- Compare solutions
|
|
f0=0;
|
|
f1=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
f0+=b[i]*x1[i];
|
|
f1+=b[i]*x2[i];
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*x1[i]*fulla.Get(i,j)*x1[j];
|
|
f1+=0.5*x2[i]*fulla.Get(i,j)*x2[j];
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>(double)(tolf),"testminnlcunit.ap:1080");
|
|
}
|
|
//--- Boundary and linear equality constrained QP problem with excessive
|
|
//--- equality constraints:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
|
|
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x-b*x,
|
|
//--- where b is some random vector
|
|
//--- * because constraints are excessive, the main problem is to find
|
|
//--- feasible point; the only existing feasible point is solution,
|
|
//--- so we have to check only feasibility
|
|
rho=1000.0;
|
|
tolx=0.0005;
|
|
aulits=10;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, xm, XStart
|
|
k=2*n;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xm=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
xm.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
xstart.Set(i,CMath::RandomInteger(2));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
//--- Create optimizer, solve
|
|
CMinNLC::MinNLCCreate(n,xstart,state);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1177");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1178");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1182");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,c,i);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>tolx,"testminnlcunit.ap:1189");
|
|
}
|
|
}
|
|
//--- Boundary and linear equality/inequality constrained QP problem with
|
|
//--- excessive constraints:
|
|
//--- * N*N SPD A with moderate condtion number (up to 100)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K=2*N equality/inequality constraints:
|
|
//--- * N/2 equality ones q'*x = q'*xm for random vector q
|
|
//--- * the rest are inequality ones, feasible at xm (xm is an inner point for these constraints)
|
|
//--- where xm is some random vector from the feasible hypercube (0.1<=xm[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x-b*x,
|
|
//--- where b is some random vector
|
|
//--- * because constraints are excessive, the main problem is to find
|
|
//--- feasible point; we do not check that algorithm found a solution,
|
|
//--- we just check that it found feasible point.
|
|
//--- NOTE: this problem is difficult one (estimates of Lagrange multipliers converge
|
|
//--- slowly), so we use relaxed tolerances - 0.010 for AUL solver
|
|
rho=1000.0;
|
|
aulits=30;
|
|
for(n=1; n<=6; n++)
|
|
{
|
|
//--- Generate problem: A, b, BndL, BndU, CMatrix, xm, x1, XStart
|
|
k=2*n;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
xm=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xm.Set(i,0.1+0.8*CMath::RandomReal());
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
x0.Set(i,CMath::RandomInteger(2));
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<n/2; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
v+=c.Get(i,j)*xm[j];
|
|
}
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
for(i=n/2; i<k; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs)/MathSqrt(n));
|
|
v+=c.Get(i,j)*xm[j];
|
|
}
|
|
c.Set(i,n,v);
|
|
ct.Set(i,2*CHighQualityRand::HQRndUniformI(rs,2)-1);
|
|
if(ct[i]>0)
|
|
c.Add(i,n,-0.1);
|
|
else
|
|
c.Add(i,n,0.1);
|
|
}
|
|
//--- Create optimizer, solve
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
if(solvertype==0)
|
|
{
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
tolx=0.0050;
|
|
}
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
{
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
tolx=0.0010;
|
|
}
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
{
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
tolx=0.0010;
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1309");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1310");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1314");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
for(i=0; i<k; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,x1,c,i);
|
|
if(ct[i]==0)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>tolx,"testminnlcunit.ap:1322");
|
|
if(ct[i]>0)
|
|
CAp::SetErrorFlag(waserrors,v<(c.Get(i,n)-tolx),"testminnlcunit.ap:1324");
|
|
if(ct[i]<0)
|
|
CAp::SetErrorFlag(waserrors,v>(c.Get(i,n)+tolx),"testminnlcunit.ap:1326");
|
|
}
|
|
}
|
|
//--- Boundary and linear equality constrained QP problem,
|
|
//--- test checks that different starting points yield same final point:
|
|
//--- * random N from [1..6], random K from [1..N-1]
|
|
//--- * N*N SPD A with moderate condtion number (important!)
|
|
//--- * boundary constraints 0<=x[i]<=1
|
|
//--- * K<N random linear equality constraints C*x = C*x0,
|
|
//--- where x0 is some random vector from the inner area of the
|
|
//--- feasible hypercube (0.1<=x0[i]<=0.9)
|
|
//--- * optimization problem has form 0.5*x'*A*x+b*x,
|
|
//--- where b is some random vector with -5<=b[i]<=+5
|
|
//--- We solve this problem two times:
|
|
//--- * each time from different initial point XStart in [-2,+2]
|
|
//--- * we compare values of the target function (although final points
|
|
//--- may be slightly different, function values should match each other)
|
|
//--- Both points should give same results; any significant difference is
|
|
//--- evidence of some error in the QP implementation.
|
|
rho=1000.0;
|
|
tolf=0.0001;
|
|
aulits=10;
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
//--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, xm, XStart.
|
|
n=CMath::RandomInteger(5)+2;
|
|
k=1+CMath::RandomInteger(n-1);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xstart=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,0.1+0.8*CMath::RandomReal());
|
|
b.Set(i,2*CMath::RandomReal()-1);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,1.0);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,2*CMath::RandomReal()-1);
|
|
c.Add(i,i,4);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,0);
|
|
}
|
|
//--- Start from first point
|
|
for(i=0; i<n; i++)
|
|
xstart.Set(i,4*CMath::RandomReal()-2);
|
|
CMinNLC::MinNLCCreate(n,xstart,state);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x0,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x0,n),"testminnlcunit.ap:1430");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1431");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1435");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Start from another point
|
|
for(i=0; i<n; i++)
|
|
xstart.Set(i,4*CMath::RandomReal()-2);
|
|
CMinNLC::MinNLCRestartFrom(state,xstart);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1466");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1467");
|
|
if(waserrors)
|
|
return;
|
|
//--- Calculate function value at X0 and X1, compare solutions
|
|
f0=0.0;
|
|
f1=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
f0+=0.5*x0[i]*fulla.Get(i,j)*x0[j];
|
|
f1+=0.5*x1[i]*fulla.Get(i,j)*x1[j];
|
|
}
|
|
f0+=x0[i]*b[i];
|
|
f1+=x1[i]*b[i];
|
|
}
|
|
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>(double)(tolf),"testminnlcunit.ap:1486");
|
|
}
|
|
//--- Convex/nonconvex optimization problem with excessive
|
|
//--- (degenerate constraints):
|
|
//--- * N=2..7
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * b has normally distributed entries with scale 10^BScale
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
|
|
//--- * box constraints: x[i] in [-1,+1]
|
|
//---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
|
|
//--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is
|
|
//--- a shift parameter:
|
|
//--- * with zero v_shift such constraints are degenerate (each vertex has
|
|
//--- N box constraints and one "redundant" linear constraint)
|
|
//--- * with positive v_shift linear constraint is always inactive
|
|
//--- * with small (about machine epsilon) but negative v_shift,
|
|
//--- constraint is close to degenerate - but not exactly
|
|
//--- We check that constrained gradient is close to zero at solution.
|
|
//--- Box constraint is considered active if distance to boundary is less
|
|
//--- than TolConstr.
|
|
//--- NOTE: because AUL algorithm is less exact than its active set counterparts,
|
|
//--- VERY loose tolerances are used for this test.
|
|
tolconstr=1.0E-2;
|
|
for(n=2; n<=6; n++)
|
|
{
|
|
for(akind=0; akind<=3; akind++)
|
|
{
|
|
//--- Choose random parameters
|
|
shiftkind=CHighQualityRand::HQRndUniformI(rs,7)-5;
|
|
bscale=CHighQualityRand::HQRndUniformI(rs,3)-2;
|
|
//--- Generate A, B and initial point
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
|
|
x.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
}
|
|
if(akind==1)
|
|
{
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(n,50.0,a);
|
|
}
|
|
if(akind==2)
|
|
{
|
|
//--- Dense well conditioned indefinite
|
|
CMatGen::SMatrixRndCond(n,50.0,a);
|
|
}
|
|
if(akind==3)
|
|
{
|
|
//--- Low rank
|
|
tmp=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1.0E-9);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
}
|
|
//--- Generate constraints
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,-1.0);
|
|
bu.Set(i,1.0);
|
|
}
|
|
ccnt=(int)MathRound(MathPow(2,n));
|
|
c=matrix<double>::Zeros(ccnt,n+1);
|
|
ct.Resize(ccnt);
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
ct.Set(i,-1);
|
|
k=i;
|
|
c.Set(i,n,MathSign(shiftkind)*MathPow(10,MathAbs(shiftkind))*CMath::m_machineepsilon);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,2*(k%2)-1);
|
|
c.Add(i,n,c.Get(i,j)*c.Get(i,j));
|
|
k=k/2;
|
|
}
|
|
}
|
|
//--- Create and optimize
|
|
CMinNLC::MinNLCCreate(n,x,state);
|
|
CMinNLC::MinNLCSetBC(state,bl,bu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,ccnt);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-12,0);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
CAp::Assert(state.m_needfij);
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,state.m_x[i]*b[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*v);
|
|
state.m_j.Add(0,i,v);
|
|
}
|
|
}
|
|
CMinNLC::MinNLCResults(state,xs0,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1641");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1645");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Evaluate gradient at solution and test
|
|
vv=0.0;
|
|
gnrm2=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs0,a,i);
|
|
v+=b[i];
|
|
gnrm2+=v*v;
|
|
if(xs0[i]<=(bl[i]+tolconstr) && v>0.0)
|
|
v=0.0;
|
|
if(xs0[i]>=(bu[i]-tolconstr) && v<0.0)
|
|
v=0.0;
|
|
vv+=CMath::Sqr(v);
|
|
}
|
|
vv=MathSqrt(vv/(gnrm2+1.0));
|
|
CAp::SetErrorFlag(waserrors,vv>(1.0E-2),"testminnlcunit.ap:1672");
|
|
}
|
|
}
|
|
//--- Linear/convex optimization problem with combination of
|
|
//--- box and linear constraints:
|
|
//--- * N=2..8
|
|
//--- * f = 0.5*x'*A*x+b'*x
|
|
//--- * b has normally distributed entries with scale 10^BScale
|
|
//--- * several kinds of A are tried: zero, well conditioned SPD
|
|
//--- * box constraints: x[i] in [-1,+1]
|
|
//--- * initial point x0 = [0 0 ... 0 0]
|
|
//--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
|
|
//--- random mix of equality/inequality constraints is tried.
|
|
//--- x0 is guaranteed to be feasible.
|
|
//--- We check that constrained gradient is close to zero at solution.
|
|
//--- Inequality constraint is considered active if distance to boundary
|
|
//--- is less than TolConstr. We use nonnegative least squares solver
|
|
//--- in order to compute constrained gradient.
|
|
tolconstr=1.0E-2;
|
|
for(n=2; n<=8; n++)
|
|
{
|
|
for(akind=0; akind<=1; akind++)
|
|
{
|
|
for(bscale=0; bscale>=-2; bscale--)
|
|
{
|
|
//--- Generate A, B and initial point
|
|
a=matrix<double>::Zeros(n,n);
|
|
b=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
|
|
x.Set(i,0.0);
|
|
}
|
|
if(akind==1)
|
|
{
|
|
//--- Dense well conditioned SPD
|
|
CMatGen::SPDMatrixRndCond(n,50.0,a);
|
|
}
|
|
if(solvertype!=0)
|
|
{
|
|
//--- AUL performs poorly on such problems,
|
|
//--- but SLP works goo.
|
|
if(akind==2)
|
|
{
|
|
//--- Dense well conditioned indefinite
|
|
CMatGen::SMatrixRndCond(n,50.0,a);
|
|
}
|
|
if(akind==3)
|
|
{
|
|
//--- Low rank
|
|
tmp=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
a.Set(i,i,1.0E-9);
|
|
for(k=1; k<=MathMin(3,n-1); k++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Add(i,j,v*tmp[i]*tmp[j]);
|
|
}
|
|
}
|
|
}
|
|
//--- Generate constraints
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,-1.0);
|
|
bu.Set(i,1.0);
|
|
}
|
|
ccnt=MathMin(3,n-1);
|
|
c=matrix<double>::Zeros(ccnt,n+1);
|
|
ct.Resize(ccnt);
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
ct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
c.Set(i,n,0.0);
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
c.Add(i,i,4);
|
|
}
|
|
//--- Create and optimize
|
|
CMinNLC::MinNLCCreate(n,x,state);
|
|
CMinNLC::MinNLCSetBC(state,bl,bu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,ccnt);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(solvertype==0)
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,10);
|
|
else
|
|
{
|
|
if(solvertype==1)
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
else
|
|
{
|
|
if(solvertype==2)
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
else
|
|
CAp::Assert(false);
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
CAp::Assert(state.m_needfij);
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,state.m_x[i]*b[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*v);
|
|
state.m_j.Add(0,i,v);
|
|
}
|
|
}
|
|
CMinNLC::MinNLCResults(state,xs0,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1817");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1821");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- 1. evaluate unconstrained gradient at solution
|
|
//--- 2. calculate constrained gradient (NNLS solver is used
|
|
//--- to evaluate gradient subject to active constraints).
|
|
//--- In order to do this we form CE matrix, matrix of active
|
|
//--- constraints (columns store constraint vectors). Then
|
|
//--- we try to approximate gradient vector by columns of CE,
|
|
//--- subject to non-negativity restriction placed on variables
|
|
//--- corresponding to inequality constraints.
|
|
//--- Residual from such regression is a constrained gradient vector.
|
|
g=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs0,a,i);
|
|
g.Set(i,v+b[i]);
|
|
}
|
|
ce=matrix<double>::Zeros(n,n+ccnt);
|
|
ArrayResize(nonnegative,n+ccnt);
|
|
k=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,xs0[i]<(bl[i]-tolconstr),"testminnlcunit.ap:1850");
|
|
CAp::SetErrorFlag(waserrors,xs0[i]>(bu[i]+tolconstr),"testminnlcunit.ap:1851");
|
|
if(xs0[i]<=(bl[i]+tolconstr))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,0.0);
|
|
ce.Set(i,k,1.0);
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
if(xs0[i]>=(bu[i]-tolconstr))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,0.0);
|
|
ce.Set(i,k,-1.0);
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
}
|
|
for(i=0; i<ccnt; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,xs0,c,i);
|
|
v-=c.Get(i,n);
|
|
CAp::SetErrorFlag(waserrors,ct[i]==0 && MathAbs(v)>tolconstr,"testminnlcunit.ap:1875");
|
|
CAp::SetErrorFlag(waserrors,ct[i]>0 && v<(-tolconstr),"testminnlcunit.ap:1876");
|
|
CAp::SetErrorFlag(waserrors,ct[i]<0 && v>tolconstr,"testminnlcunit.ap:1877");
|
|
if(ct[i]==0)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,c.Get(i,j));
|
|
nonnegative[k]=false;
|
|
k++;
|
|
continue;
|
|
}
|
|
if((ct[i]>0 && v<=tolconstr) || (ct[i]<0 && v>=(-tolconstr)))
|
|
{
|
|
for(j=0; j<n; j++)
|
|
ce.Set(j,k,MathSign(ct[i])*c.Get(i,j));
|
|
nonnegative[k]=true;
|
|
k++;
|
|
continue;
|
|
}
|
|
}
|
|
CSNNLS::SNNLSInit(0,0,0,nnls);
|
|
CSNNLS::SNNLSSetProblem(nnls,ce,g,0,k,n);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
if(!nonnegative[i])
|
|
CSNNLS::SNNLSDropNNC(nnls,i);
|
|
}
|
|
CSNNLS::SNNLSSolve(nnls,tmp);
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
g.Add(j,-tmp[i]*ce.Get(j,i));
|
|
}
|
|
vv=CAblasF::RDotV2(n,g);
|
|
vv=MathSqrt(vv);
|
|
CAp::SetErrorFlag(waserrors,vv>(1.0E-3),"testminnlcunit.ap:1906");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests nonlinearly constrained quadratic programming|
|
|
//| algorithm. |
|
|
//| Sets error flag on failure. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestNLC(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int n2=0;
|
|
double tolx=0;
|
|
double tolf=0;
|
|
double tolg=0;
|
|
int aulits=0;
|
|
double rho=0;
|
|
CMinNLCState state;
|
|
CMinNLCReport rep;
|
|
COptGuardReport ogrep;
|
|
int scaletype=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble b;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble s;
|
|
CRowDouble g;
|
|
CRowDouble rnlc;
|
|
CRowDouble xref;
|
|
CRowDouble lagmult;
|
|
CRowInt ckind;
|
|
CMatrixDouble fulla;
|
|
CMatrixDouble c;
|
|
CRowInt ct;
|
|
int cntbc=0;
|
|
int cntlc=0;
|
|
int cntnlec=0;
|
|
int cntnlic=0;
|
|
int cntactive=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int pass=0;
|
|
int klc=0;
|
|
int knlec=0;
|
|
int knlic=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double gnorm2=0;
|
|
double rawgnorm2=0;
|
|
double fref=0;
|
|
double diffstep=0;
|
|
CHighQualityRandState rs;
|
|
int prectype=0;
|
|
int solvertype=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
//--- Basic test:
|
|
//--- * 2-dimensional problem
|
|
//--- * target function F(x0,x1) = (x0-1)^2 + (x1-1)^2
|
|
//--- * one nonlinear constraint Z(x0,x1) = x0^2+x1^2-1,
|
|
//--- which is tried as equality and inequality one
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
aulits=50;
|
|
n=2;
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
|
|
state.m_j.Set(0,0,2*(state.m_x[0]-1));
|
|
state.m_j.Set(0,1,2*(state.m_x[1]-1));
|
|
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
|
|
state.m_j.Set(1,0,2*state.m_x[0]);
|
|
state.m_j.Set(1,1,2*state.m_x[1]);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1996");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1997");
|
|
if(waserrors)
|
|
return;
|
|
//---
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2000");
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2001");
|
|
CMinNLC::MinNLCSetNLC(state,1,0);
|
|
CMinNLC::MinNLCRestartFrom(state,x0);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
|
|
state.m_j.Set(0,0,2*(state.m_x[0]-1));
|
|
state.m_j.Set(0,1,2*(state.m_x[1]-1));
|
|
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
|
|
state.m_j.Set(1,0,2*state.m_x[0]);
|
|
state.m_j.Set(1,1,2*state.m_x[1]);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2021");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2022");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2026");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2030");
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2031");
|
|
//--- This test checks correctness of scaling being applied to nonlinear
|
|
//--- constraints. We solve bound constrained scaled problem and check
|
|
//--- that solution is correct.
|
|
aulits=50;
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolg=0.01;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Generate well-conditioned problem with unit scale
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
cntnlec=CHighQualityRand::HQRndUniformI(rs,n);
|
|
cntnlic=n-cntnlec;
|
|
for(i=0; i<cntnlec ; i++)
|
|
{
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]);
|
|
}
|
|
for(i=cntnlec; i<n; i++)
|
|
{
|
|
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndu.Set(i,bndl[i]+0.5);
|
|
}
|
|
//--- Apply scaling to quadratic/linear term, so problem becomes
|
|
//--- well-conditioned in the scaled coordinates.
|
|
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(scaletype==0)
|
|
s.Set(i,1);
|
|
else
|
|
s.Set(i,MathExp(5*CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
fulla.Mul(i,j,1.0/(s[i]*s[j]));
|
|
}
|
|
x0*=s;
|
|
bndl*=s;
|
|
bndu*=s;
|
|
b/=s;
|
|
//--- Solve problem with boundary constraints posed as nonlinear ones
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCSetNLC(state,cntnlec,2*cntnlic);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
for(i=0; i<=cntnlec+2*cntnlic; i++)
|
|
{
|
|
state.m_fi.Set(i,0);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(i,j,0);
|
|
}
|
|
//--- Function itself
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
//--- Equality constraints
|
|
for(i=0; i<cntnlec ; i++)
|
|
{
|
|
state.m_fi.Set(1+i,(state.m_x[i]-bndl[i])/s[i]);
|
|
state.m_j.Set(1+i,i,1/s[i]);
|
|
}
|
|
//--- Inequality constraints
|
|
for(i=0; i<cntnlic; i++)
|
|
{
|
|
k=cntnlec+i;
|
|
state.m_fi.Set(1+cntnlec+2*i+0,(bndl[k]-state.m_x[k])/s[k]);
|
|
state.m_j.Set(1+cntnlec+2*i+0,k,-(1/s[k]));
|
|
state.m_fi.Set(1+cntnlec+2*i+1,(state.m_x[k]-bndu[k])/s[k]);
|
|
state.m_j.Set(1+cntnlec+2*i+1,k,1/s[k]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2165");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2166");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2170");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check feasibility properties
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndl[i]) && x1[i]<=(double)(bndl[i]-tolx*s[i]),"testminnlcunit.ap:2180");
|
|
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndu[i]) && x1[i]>=(double)(bndu[i]+tolx*s[i]),"testminnlcunit.ap:2181");
|
|
}
|
|
//--- Test - calculate scaled constrained gradient at solution,
|
|
//--- check its norm.
|
|
g=vector<double>::Zeros(n);
|
|
gnorm2=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
g.Set(i,b[i]+CAblasF::RDotVR(n,x1,fulla,i));
|
|
g.Set(i,s[i]*g[i]);
|
|
if((MathIsValidNumber(bndl[i]) && MathAbs(x1[i]-bndl[i])<(double)(tolx*s[i])) && (double)(g[i])>0.0)
|
|
g.Set(i,0);
|
|
if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<(double)(tolx*s[i])) && (double)(g[i])<0.0)
|
|
g.Set(i,0);
|
|
gnorm2+=CMath::Sqr(g[i]);
|
|
}
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)),"testminnlcunit.ap:2203");
|
|
}
|
|
}
|
|
//--- Complex problem with mix of boundary, linear and nonlinear constraints:
|
|
//--- * quadratic target function f(x) = 0.5*x'*A*x + b'*x
|
|
//--- * unit scaling is used
|
|
//--- * problem size N is even
|
|
//--- * all variables are divided into pairs: x[0] and x[1], x[2] and x[3], ...
|
|
//--- * constraints are set for pairs of variables, i.e. each constraint involves
|
|
//--- only pair of adjacent variables (x0/x1, x2/x3, x4/x5 and so on), and each
|
|
//--- pair of variables has at most one constraint which binds them
|
|
//--- * for variables u and v following kinds of constraints can be randomly set:
|
|
//--- * CKind=0 no constraint
|
|
//--- * CKind=1 boundary equality constraint: u=a, v=b
|
|
//--- * CKind=2 boundary inequality constraint: a0<=u<=b0, a1<=v<=b1
|
|
//--- * CKind=3 linear equality constraint: a*u+b*v = c
|
|
//--- * CKind=4 linear inequality constraint: a*u+b*v <= c
|
|
//--- * CKind=5 nonlinear equality constraint: u^2+v^2 = 1
|
|
//--- * CKind=6 nonlinear inequality constraint: u^2+v^2 <= 1
|
|
//--- * it is relatively easy to calculated projected gradient for such problem
|
|
aulits=50;
|
|
rho=200.0;
|
|
tolx=0.0005;
|
|
tolg=0.005;
|
|
n=20;
|
|
n2=n/2;
|
|
for(pass=1; pass<=5; pass++)
|
|
{
|
|
for(prectype=1; prectype<=2; prectype++)
|
|
{
|
|
//--- Generate well-conditioned problem with unit scale
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(n,n+1);
|
|
ct.Resize(n);
|
|
x0=vector<double>::Zeros(n);
|
|
ckind.Resize(n2);
|
|
rnlc=vector<double>::Zeros(n2);
|
|
cntbc=0;
|
|
cntlc=0;
|
|
cntnlec=0;
|
|
cntnlic=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
b.Set(i,10*CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
ckind.Set(i,CHighQualityRand::HQRndUniformI(rs,7));
|
|
rnlc.Set(i,0);
|
|
if(ckind[i]==0)
|
|
{
|
|
//--- Unconstrained
|
|
continue;
|
|
}
|
|
if(ckind[i]==1)
|
|
{
|
|
//--- Bound equality constrained
|
|
bndl.Set(2*i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
bndu.Set(2*i,bndl[2*i+0]);
|
|
bndl.Set(2*i+1,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
bndu.Set(2*i+1,bndl[2*i+1]);
|
|
cntbc++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==2)
|
|
{
|
|
//--- Bound inequality constrained
|
|
bndl.Set(2*i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
bndu.Set(2*i,bndl[2*i+0]+0.5);
|
|
bndl.Set(2*i+1,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
bndu.Set(2*i+1,bndl[2*i+1]+0.5);
|
|
cntbc++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==3)
|
|
{
|
|
//--- Linear equality constrained
|
|
for(j=0; j<=n; j++)
|
|
c.Set(cntlc,j,0.0);
|
|
vx=CHighQualityRand::HQRndUniformR(rs)-0.5;
|
|
vy=CHighQualityRand::HQRndUniformR(rs)-0.5;
|
|
c.Set(cntlc,2*i+0,vx);
|
|
c.Set(cntlc,2*i+1,vy);
|
|
c.Set(cntlc,n,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
ct.Set(cntlc,0);
|
|
cntlc++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==4)
|
|
{
|
|
//--- Linear inequality constrained
|
|
for(j=0; j<=n; j++)
|
|
c.Set(cntlc,j,0.0);
|
|
vx=CHighQualityRand::HQRndUniformR(rs)-0.5;
|
|
vy=CHighQualityRand::HQRndUniformR(rs)-0.5;
|
|
c.Set(cntlc,2*i,vx);
|
|
c.Set(cntlc,2*i+1,vy);
|
|
c.Set(cntlc,n,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
ct.Set(cntlc,-1);
|
|
cntlc++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==5)
|
|
{
|
|
//--- Nonlinear equality constrained
|
|
rnlc.Set(i,0.5+CHighQualityRand::HQRndUniformR(rs));
|
|
cntnlec++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==6)
|
|
{
|
|
//--- Nonlinear inequality constrained
|
|
rnlc.Set(i,0.5+CHighQualityRand::HQRndUniformR(rs));
|
|
cntnlic++;
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
//--- Solve problem
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(prectype==0)
|
|
CMinNLC::MinNLCSetPrecInexact(state);
|
|
if(prectype==1)
|
|
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
|
|
if(prectype==2)
|
|
CMinNLC::MinNLCSetPrecExactRobust(state,0);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,cntlc);
|
|
CMinNLC::MinNLCSetNLC(state,cntnlec,cntnlic);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
//--- Evaluate target function
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
//--- Evaluate constraint functions
|
|
knlec=1;
|
|
knlic=1+cntnlec;
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
if(ckind[i]==5)
|
|
{
|
|
state.m_fi.Set(knlec,0);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(knlec,j,0.0);
|
|
state.m_fi.Set(knlec,CMath::Sqr(state.m_x[2*i+0])+CMath::Sqr(state.m_x[2*i+1])-rnlc[i]);
|
|
state.m_j.Set(knlec,2*i+0,2*state.m_x[2*i+0]);
|
|
state.m_j.Set(knlec,2*i+1,2*state.m_x[2*i+1]);
|
|
knlec++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==6)
|
|
{
|
|
state.m_fi.Set(knlic,0);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(knlic,j,0.0);
|
|
state.m_fi.Set(knlic,CMath::Sqr(state.m_x[2*i+0])+CMath::Sqr(state.m_x[2*i+1])-rnlc[i]);
|
|
state.m_j.Set(knlic,2*i+0,2*state.m_x[2*i+0]);
|
|
state.m_j.Set(knlic,2*i+1,2*state.m_x[2*i+1]);
|
|
knlic++;
|
|
continue;
|
|
}
|
|
}
|
|
if(!CAp::Assert(knlec==1+cntnlec) ||
|
|
!CAp::Assert(knlic==1+cntnlec+cntnlic))
|
|
{
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2434");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2435");
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2439");
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
//--- Check feasibility properties
|
|
klc=0;
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
if(ckind[i]==0)
|
|
{
|
|
//--- Unconstrained
|
|
continue;
|
|
}
|
|
if(ckind[i]==1)
|
|
{
|
|
//--- Bound equality constrained
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x1[2*i+0]-bndl[2*i+0])>tolx,"testminnlcunit.ap:2462");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x1[2*i+1]-bndl[2*i+1])>tolx,"testminnlcunit.ap:2463");
|
|
continue;
|
|
}
|
|
if(ckind[i]==2)
|
|
{
|
|
//--- Bound inequality constrained
|
|
CAp::SetErrorFlag(waserrors,x1[2*i ]<(bndl[2*i+0]-tolx),"testminnlcunit.ap:2471");
|
|
CAp::SetErrorFlag(waserrors,x1[2*i ]>(bndu[2*i+0]+tolx),"testminnlcunit.ap:2472");
|
|
CAp::SetErrorFlag(waserrors,x1[2*i+1]<(bndl[2*i+1]-tolx),"testminnlcunit.ap:2473");
|
|
CAp::SetErrorFlag(waserrors,x1[2*i+1]>(bndu[2*i+1]+tolx),"testminnlcunit.ap:2474");
|
|
continue;
|
|
}
|
|
if(ckind[i]==3)
|
|
{
|
|
//--- Linear equality constrained
|
|
v=x1[2*i+0]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][n];
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v)>tolx,"testminnlcunit.ap:2483");
|
|
klc++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==4)
|
|
{
|
|
//--- Linear inequality constrained
|
|
v=x1[2*i+0]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][n];
|
|
CAp::SetErrorFlag(waserrors,v>tolx,"testminnlcunit.ap:2493");
|
|
klc++;
|
|
continue;
|
|
}
|
|
if(ckind[i]==5)
|
|
{
|
|
//--- Nonlinear equality constrained
|
|
v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i];
|
|
CAp::SetErrorFlag(waserrors,MathAbs(v)>tolx,"testminnlcunit.ap:2503");
|
|
continue;
|
|
}
|
|
if(ckind[i]==6)
|
|
{
|
|
//--- Nonlinear inequality constrained
|
|
v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i];
|
|
CAp::SetErrorFlag(waserrors,v>tolx,"testminnlcunit.ap:2512");
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
//--- Test - calculate scaled constrained gradient at solution,
|
|
//--- check its norm.
|
|
gnorm2=0.0;
|
|
rawgnorm2=0;
|
|
g=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=b[i]+CAblasF::RDotVR(n,x1,fulla,i);
|
|
g.Set(i,v);
|
|
rawgnorm2+=v*v;
|
|
}
|
|
klc=0;
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
if(ckind[i]==0)
|
|
{
|
|
//--- Unconstrained
|
|
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2542");
|
|
continue;
|
|
}
|
|
if(ckind[i]==1)
|
|
{
|
|
//--- Bound equality constrained, unconditionally set gradient to zero
|
|
g.Set(2*i+0,0.0);
|
|
g.Set(2*i+1,0.0);
|
|
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2553");
|
|
continue;
|
|
}
|
|
if(ckind[i]==2)
|
|
{
|
|
//--- Bound inequality constrained, conditionally set gradient to zero
|
|
//--- (when constraint is active)
|
|
if(x1[2*i ]<(bndl[2*i]+tolx) || x1[2*i ]>(bndu[2*i]-tolx))
|
|
g.Set(2*i,0.0);
|
|
if(x1[2*i+1]<(bndl[2*i+1]+tolx) || x1[2*i+1]>(bndu[2*i+1]-tolx))
|
|
g.Set(2*i+1,0.0);
|
|
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2567");
|
|
continue;
|
|
}
|
|
if(ckind[i]==3)
|
|
{
|
|
//--- Linear equality constrained, unconditionally project gradient into
|
|
//--- equality constrained subspace
|
|
v=g[2*i+0]*c[klc][ 2*i]+g[2*i+1]*c[klc][ 2*i+1];
|
|
vv=CMath::Sqr(c[klc][ 2*i])+CMath::Sqr(c[klc][ 2*i+1]);
|
|
g.Add(2*i,c[klc][ 2*i]*(-v/vv));
|
|
g.Add(2*i+1,c[klc][ 2*i+1]*(-v/vv));
|
|
klc++;
|
|
gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2582");
|
|
continue;
|
|
}
|
|
if(ckind[i]==4)
|
|
{
|
|
//--- Linear inequality constrained, conditionally project gradient
|
|
//--- (when constraint is active)
|
|
v=x1[2*i]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][ n];
|
|
if(v>(-tolx))
|
|
{
|
|
v=g[2*i+0]*c[klc][ 2*i]+g[2*i+1]*c[klc][ 2*i+1];
|
|
vv=CMath::Sqr(c[klc][ 2*i])+CMath::Sqr(c[klc][ 2*i+1]);
|
|
g.Add(2*i,c[klc][ 2*i]*(-v/vv));
|
|
g.Add(2*i+1,c[klc][ 2*i+1]*(-v/vv));
|
|
}
|
|
klc++;
|
|
gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2601");
|
|
continue;
|
|
}
|
|
if(ckind[i]==5)
|
|
{
|
|
//--- Nonlinear equality constrained, unconditionally project gradient
|
|
//--- NOTE: here we rely on the fact that corresponding components of X
|
|
//--- sum to one.
|
|
v=CApServ::Coalesce(MathSqrt(CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])),1.0);
|
|
vx=x1[2*i]/v;
|
|
vy=x1[2*i+1]/v;
|
|
v=g[2*i]*vx+g[2*i+1]*vy;
|
|
g.Add(2*i,- vx*v);
|
|
g.Add(2*i+1,- vy*v);
|
|
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2619");
|
|
continue;
|
|
}
|
|
if(ckind[i]==6)
|
|
{
|
|
//--- Nonlinear inequality constrained, conditionally project gradient
|
|
//--- (when constraint is active)
|
|
//--- NOTE: here we rely on the fact that corresponding components of X
|
|
//--- sum to one.
|
|
v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i];
|
|
if(v>(double)(-tolx))
|
|
{
|
|
v=CApServ::Coalesce(MathSqrt(CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])),1.0);
|
|
vx=x1[2*i ]/v;
|
|
vy=x1[2*i+1]/v;
|
|
v=g[2*i]*vx+g[2*i+1]*vy;
|
|
g.Add(2*i,- vx*v);
|
|
g.Add(2*i+1,- vy*v);
|
|
}
|
|
gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]);
|
|
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2642");
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test a sequence of inequality constrained problems with varying N/NLIC and
|
|
//--- known answer. The problem formulation is prepared as follows:
|
|
//--- * first, we generate a solution point and active constraints with Lagrange
|
|
//--- multipliers of our choice; at least one constraint is active at the solution.
|
|
//--- * then we select the target function to match our constraints
|
|
//--- * finally, we add inactive constraints
|
|
rho=1000.0;
|
|
tolf=0.002;
|
|
aulits=10;
|
|
diffstep=0.0001;
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(cntnlic=1; cntnlic<=10; cntnlic++)
|
|
{
|
|
//--- Create constraint matrix C that stores coefficients of quadratic
|
|
//--- constraints: first N columns store linear terms, next N columns
|
|
//--- store diagonal of the quadratic term, next column stores constant term.
|
|
CHighQualityRand::HQRndNormalV(rs,n,xref);
|
|
cntactive=1+CHighQualityRand::HQRndUniformI(rs,n);
|
|
c=matrix<double>::Zeros(cntnlic,2*n+1);
|
|
lagmult=vector<double>::Zeros(cntnlic);
|
|
CAblasF::RSetAllocV(n,0.0,g);
|
|
for(i=0; i<cntnlic; i++)
|
|
{
|
|
//--- Generate linear and quadratic terms for a constraint, adjust its
|
|
//--- constant term depending on whether it is active or not
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
c.Set(i,n+j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
v+=c.Get(i,j)*xref[j]+c.Get(i,n+j)*CMath::Sqr(xref[j]);
|
|
}
|
|
c.Set(i,2*n,-v);
|
|
if(i<cntactive)
|
|
{
|
|
//--- Constraint is active, accumulate its gradient in G
|
|
lagmult.Set(i,0.05+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
for(j=0; j<n; j++)
|
|
g.Add(j,lagmult[i]*(c.Get(i,j)+2*c.Get(i,n+j)*xref[j]));
|
|
}
|
|
else
|
|
{
|
|
//--- Constraint is inactive, adjust its constant term
|
|
lagmult.Set(i,0.0);
|
|
c.Add(i,2*n,-0.1-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
}
|
|
for(i=0; i<cntnlic; i++)
|
|
{
|
|
j=i+CHighQualityRand::HQRndUniformI(rs,cntnlic-i);
|
|
if(j!=i)
|
|
{
|
|
CApServ::SwapRows(c,i,j,2*n+1);
|
|
CApServ::SwapElements(lagmult,i,j);
|
|
}
|
|
}
|
|
//--- Generate target function F that has gradient at XRef[] exactly equal to -G
|
|
CAblasF::RAllocV(n,b);
|
|
CAblasF::RCopyMulV(n,-1.0,g,b);
|
|
fref=CAblasF::RDotV(n,b,xref);
|
|
//--- Solve the problem
|
|
CHighQualityRand::HQRndNormalV(rs,n,x0);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
else
|
|
CMinNLC::MinNLCCreateF(n,x0,diffstep,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
prectype=CHighQualityRand::HQRndUniformI(rs,3);
|
|
if(prectype==0)
|
|
CMinNLC::MinNLCSetPrecInexact(state);
|
|
if(prectype==1)
|
|
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
|
|
if(prectype==2)
|
|
CMinNLC::MinNLCSetPrecExactRobust(state,0);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetNLC(state,0,cntnlic);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
//--- Evaluate target function
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
//--- Evaluate constraint functions
|
|
k=1;
|
|
for(i=0; i<cntnlic; i++)
|
|
{
|
|
state.m_fi.Set(k,c.Get(i,2*n));
|
|
for(j=0; j<n; j++)
|
|
state.m_fi.Add(k,c.Get(i,j)*state.m_x[j]+c.Get(i,n+j)*CMath::Sqr(state.m_x[j]));
|
|
k++;
|
|
}
|
|
if(!CAp::Assert(k==1+cntnlic))
|
|
{
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_needfij)
|
|
{
|
|
//--- Evaluate target function
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
}
|
|
//--- Evaluate constraint functions
|
|
k=1;
|
|
for(i=0; i<cntnlic; i++)
|
|
{
|
|
state.m_fi.Set(k,c.Get(i,2*n));
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(k,c.Get(i,j)*state.m_x[j]+c.Get(i,n+j)*CMath::Sqr(state.m_x[j]));
|
|
state.m_j.Set(k,j,c.Get(i,j)+2*c.Get(i,n+j)*state.m_x[j]);
|
|
}
|
|
k++;
|
|
}
|
|
if(!CAp::Assert(k==1+cntnlic))
|
|
{
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2818");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2819");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,MathAbs(fref-CAblasF::RDotV(n,x1,b))>(tolf*(CAblasF::RMaxAbsV(n,b)+1)),"testminnlcunit.ap:2824");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function performs additional tests |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestOther(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
double v=0;
|
|
double h=0;
|
|
double fl=0;
|
|
double fr=0;
|
|
double fl2=0;
|
|
double fr2=0;
|
|
double dfl=0;
|
|
double dfr=0;
|
|
double dfl2=0;
|
|
double dfr2=0;
|
|
double d2fl=0;
|
|
double d2fr=0;
|
|
double d2fl2=0;
|
|
double d2fr2=0;
|
|
double f0=0;
|
|
double df=0;
|
|
double d2f=0;
|
|
double ndf=0;
|
|
double nd2f=0;
|
|
double dtol=0;
|
|
double diffstep=0;
|
|
CMinNLCState state;
|
|
CMinNLCReport rep;
|
|
COptGuardReport ogrep;
|
|
double rho=0;
|
|
int aulits=0;
|
|
double tolx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
CRowDouble s;
|
|
CRowDouble b;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble x3;
|
|
CRowDouble xlast;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble xu;
|
|
double condv=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble c;
|
|
CMatrixDouble fulla;
|
|
CRowInt ct;
|
|
int nlbfgs=0;
|
|
int nexactlowrank=0;
|
|
int nexactrobust=0;
|
|
int nnone=0;
|
|
int ctype=0;
|
|
int trialidx=0;
|
|
int blocksize=0;
|
|
int blockcnt=0;
|
|
int maxits=0;
|
|
int spoiliteration=0;
|
|
int stopiteration=0;
|
|
int spoilvar=0;
|
|
double spoilval=0;
|
|
int pass=0;
|
|
int solvertype=0;
|
|
int badidx0=0;
|
|
int badidx1=0;
|
|
int nlec=0;
|
|
int nlic=0;
|
|
double ss=0;
|
|
int stopcallidx=0;
|
|
int callidx=0;
|
|
bool terminationrequested;
|
|
bool firstrep;
|
|
int i_=0;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test equality penalty function (correctly calculated and smooth)
|
|
h=1.0E-4;
|
|
v=-0.98;
|
|
dtol=1.0E-3;
|
|
while(v<=0.98)
|
|
{
|
|
//--- Test numerical derivative; this test also checks continuity of the
|
|
//--- function
|
|
CMinNLC::MinNLCEqualityPenaltyFunction(v-2*h,fl2,dfl2,d2fl2);
|
|
CMinNLC::MinNLCEqualityPenaltyFunction(v-h,fl,dfl,d2fl);
|
|
CMinNLC::MinNLCEqualityPenaltyFunction(v+h,fr,dfr,d2fr);
|
|
CMinNLC::MinNLCEqualityPenaltyFunction(v+2*h,fr2,dfr2,d2fr2);
|
|
CMinNLC::MinNLCEqualityPenaltyFunction(v,f0,df,d2f);
|
|
ndf=(-fr2+8*fr-8*fl+fl2)/(12*h);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(ndf-df)>(dtol*MathMax(MathAbs(ndf),1)),"testminnlcunit.ap:2903");
|
|
nd2f=(-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(nd2f-d2f))>(dtol*MathMax(MathAbs(nd2f),1)),"testminnlcunit.ap:2905");
|
|
//--- Next point
|
|
v+=h;
|
|
}
|
|
CMinNLC::MinNLCEqualityPenaltyFunction(0.0,f0,df,d2f);
|
|
CAp::SetErrorFlag(waserrors,f0!=0.0,"testminnlcunit.ap:2913");
|
|
CAp::SetErrorFlag(waserrors,(double)(df)!=0.0,"testminnlcunit.ap:2914");
|
|
//--- Test inequality penalty function (correctly calculated and smooth)
|
|
h=1.0E-4;
|
|
v=0.02;
|
|
dtol=1.0E-3;
|
|
while(v<=2.0)
|
|
{
|
|
//--- Test numerical derivative; this test also checks continuity of the
|
|
//--- function
|
|
CMinNLC::MinNLCInequalityShiftFunction(v-2*h,fl2,dfl2,d2fl2);
|
|
CMinNLC::MinNLCInequalityShiftFunction(v-h,fl,dfl,d2fl);
|
|
CMinNLC::MinNLCInequalityShiftFunction(v+h,fr,dfr,d2fr);
|
|
CMinNLC::MinNLCInequalityShiftFunction(v+2*h,fr2,dfr2,d2fr2);
|
|
CMinNLC::MinNLCInequalityShiftFunction(v,f0,df,d2f);
|
|
ndf=(-fr2+8*fr-8*fl+fl2)/(12*h);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(ndf-df)>(dtol*MathMax(MathAbs(ndf),1)),"testminnlcunit.ap:2938");
|
|
nd2f=(-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(nd2f-d2f)>(dtol*MathMax(MathAbs(nd2f),1)),"testminnlcunit.ap:2940");
|
|
//--- Next point
|
|
v+=h;
|
|
}
|
|
CMinNLC::MinNLCInequalityShiftFunction(1.0,f0,df,d2f);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(f0)>(1.0E-6),"testminnlcunit.ap:2948");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(df+1)>1.0E-6,"testminnlcunit.ap:2949");
|
|
//--- Test different properties shared by all solvers
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
//--- Test location reports
|
|
aulits=50;
|
|
rho=200.0;
|
|
n=2;
|
|
x0=vector<double>::Zeros(n);
|
|
xlast=vector<double>::Zeros(n);
|
|
x0.Set(0,0.1+0.1*CHighQualityRand::HQRndUniformR(rs));
|
|
x0.Set(1,0.2+0.1*CHighQualityRand::HQRndUniformR(rs));
|
|
xlast.Set(0,0);
|
|
xlast.Set(1,0);
|
|
firstrep=true;
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
CMinNLC::MinNLCSetXRep(state,true);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
|
|
state.m_j.Set(0,0,2*(state.m_x[0]-1));
|
|
state.m_j.Set(0,1,2*(state.m_x[1]-1));
|
|
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
|
|
state.m_j.Set(1,0,2*state.m_x[0]);
|
|
state.m_j.Set(1,1,2*state.m_x[1]);
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- If first point reported, compare with initial one
|
|
if(firstrep)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,MathAbs(state.m_x[0]-x0[0])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3002");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(state.m_x[1]-x0[1])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3003");
|
|
}
|
|
firstrep=false;
|
|
//--- Save last point
|
|
xlast.Set(0,state.m_x[0]);
|
|
xlast.Set(1,state.m_x[1]);
|
|
//--- Done
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3021");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3022");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x1[0]-xlast[0])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3025");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x1[1]-xlast[1])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3026");
|
|
//--- Test numerical differentiation
|
|
aulits=50;
|
|
rho=200.0;
|
|
tolx=0.001;
|
|
diffstep=0.0001;
|
|
n=2;
|
|
x0=vector<double>::Zeros(n);
|
|
x0.Set(0,0.1);
|
|
x0.Set(1,0.2);
|
|
CMinNLC::MinNLCCreateF(n,x0,diffstep,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
|
|
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3062");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3063");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:3066");
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:3067");
|
|
//--- Test integrity checks for NAN/INF:
|
|
//--- * algorithm solves optimization problem, which is normal for some time (quadratic)
|
|
//--- * after 5-th step we choose random component of gradient and consistently spoil
|
|
//--- it by NAN or INF.
|
|
//--- * we check that correct termination code is returned (-8)
|
|
n=100;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
spoiliteration=5;
|
|
stopiteration=8;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
//--- Gradient can be spoiled by +INF, -INF, NAN
|
|
spoilvar=CHighQualityRand::HQRndUniformI(rs,n);
|
|
i=CHighQualityRand::HQRndUniformI(rs,3);
|
|
spoilval=AL_NaN;
|
|
if(i==0)
|
|
spoilval=AL_NEGINF;
|
|
if(i==1)
|
|
spoilval=AL_POSINF;
|
|
}
|
|
else
|
|
{
|
|
//--- Function value can be spoiled only by NAN
|
|
//--- (+INF can be recognized as legitimate value during optimization)
|
|
spoilvar=-1;
|
|
spoilval=AL_NaN;
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,1.0E5,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
aulits=5;
|
|
rho=1.0E3;
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,0.0,stopiteration);
|
|
CMinNLC::MinNLCSetXRep(state,true);
|
|
k=-1;
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
if(k>=spoiliteration)
|
|
{
|
|
if(spoilvar<0)
|
|
state.m_fi.Set(0,spoilval);
|
|
else
|
|
state.m_j.Set(0,spoilvar,spoilval);
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
k++;
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-8,"testminnlcunit.ap:3160");
|
|
}
|
|
//--- Test that optimizer respects box constraints in all
|
|
//--- intermediate points:
|
|
//--- * test that analytic Jacobian respects them
|
|
//--- * test that numerical Jacobian respects them
|
|
//--- NOTE: we skip SolverType=0 (AUL) because AUL optimizer
|
|
//--- does not provide such guarantee
|
|
if(solvertype!=0)
|
|
{
|
|
n=10;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
bndl.Set(i,0);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
else
|
|
{
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,0);
|
|
}
|
|
}
|
|
//--- Check analytic Jacobian
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-8,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminnlcunit.ap:3215");
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminnlcunit.ap:3216");
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3230");
|
|
//--- Check numerical Jacobian
|
|
CMinNLC::MinNLCCreateF(n,x0,1.0E-4,state);
|
|
switch(solvertype)
|
|
{
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-8,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminnlcunit.ap:3251");
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminnlcunit.ap:3252");
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
for(j=0; j<n; j++)
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3262");
|
|
}
|
|
//--- Test constraint violation reports for completely unconstrained
|
|
//--- problems and problems with all constraints being satisfied exactly
|
|
tolx=0.01;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,10);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,200);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,MathPow(state.m_x[i],4));
|
|
state.m_j.Set(0,i,4*MathPow(state.m_x[i],3));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
//--- Check solution itself
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3305");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3306");
|
|
if(waserrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,MathAbs(x1[i])>tolx,"testminnlcunit.ap:3310");
|
|
//--- Check constraint violation reports
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3315");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3316");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3317");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3318");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3319");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3320");
|
|
}
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
x0=vector<double>::Zeros(n);
|
|
xu=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Full(n,-1000.0);
|
|
bndu=vector<double>::Full(n,1000.0);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xu.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
c.Set(i,n,1000);
|
|
ct.Set(i,-1);
|
|
}
|
|
else
|
|
{
|
|
c.Set(i,n,-1000);
|
|
ct.Set(i,1);
|
|
}
|
|
}
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,MathPow(state.m_x[i]-xu[i],2));
|
|
state.m_j.Set(0,i,2*(state.m_x[i]-xu[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
//--- Check solution itself, calculate reference violation values
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3385");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3386");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3393");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3394");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3395");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3396");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3397");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3398");
|
|
}
|
|
//--- Test constraint violation reports for box/linearly constrained
|
|
//--- problems. We generate a problem which can not satisfy one (and
|
|
//--- just one) general linear constraint.
|
|
tolx=0.001;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Formulate problem with inconsistent constraints
|
|
n=2+CHighQualityRand::HQRndUniformI(rs,5);
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
s=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xu=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
ct.Resize(k);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xu.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-(0.5*CHighQualityRand::HQRndUniformR(rs))-0.1);
|
|
bndu.Set(i,0.5*CHighQualityRand::HQRndUniformR(rs)+0.1);
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
c.Set(i,n,1000);
|
|
ct.Set(i,-1);
|
|
}
|
|
else
|
|
{
|
|
c.Set(i,n,-1000);
|
|
ct.Set(i,1);
|
|
}
|
|
}
|
|
CAp::Assert(n>=2,"NLCTest: integrity check failed");
|
|
badidx0=CHighQualityRand::HQRndUniformI(rs,k);
|
|
badidx1=CHighQualityRand::HQRndUniformI(rs,n);
|
|
for(j=0; j<n; j++)
|
|
c.Set(badidx0,j,0);
|
|
for(j=0; j<k; j++)
|
|
c.Set(j,badidx1,0);
|
|
c.Set(badidx0,badidx1,1);
|
|
c.Set(badidx0,n,10*(2*CHighQualityRand::HQRndUniformI(rs,2)-1));
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
ct.Set(badidx0,0);
|
|
else
|
|
ct.Set(badidx0,(int)MathSign(c.Get(badidx0,n)));
|
|
//--- Create and try to solve
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetLC(state,c,ct,k);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,MathPow(state.m_x[i]-xu[i],2));
|
|
state.m_j.Set(0,i,2*(state.m_x[i]-xu[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
//--- Check solution itself, calculate reference violation values
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3493");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3494");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
if(rep.m_bcerr>0.0)
|
|
{
|
|
v=MathMax(bndl[badidx1]-x1[badidx1],x1[badidx1]-bndu[badidx1]);
|
|
v=v/s[badidx1];
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=badidx1,"testminnlcunit.ap:3505");
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_bcerr-v))>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3507");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3512");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3513");
|
|
CAp::SetErrorFlag(waserrors,x1[badidx1]<bndl[badidx1] || x1[badidx1]>bndu[badidx1],"testminnlcunit.ap:3514");
|
|
}
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=badidx0,"testminnlcunit.ap:3516");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(rep.m_lcerr-MathAbs(x1[badidx1]-c.Get(badidx0,n))/s[badidx1])>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3517");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3518");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3519");
|
|
}
|
|
//--- Test constraint violation reports for box/nonlinearly constrained
|
|
//--- problems. We generate a problem which can not satisfy one (and
|
|
//--- just one) general linear constraint.
|
|
//--- NOTE: it is important to have N>=NLEC+NLIC
|
|
tolx=0.001;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Formulate problem with inconsistent constraints
|
|
nlec=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
nlic=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
k=nlec+nlic;
|
|
n=k+CHighQualityRand::HQRndUniformI(rs,5);
|
|
s=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xu=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(k,n+1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
xu.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,-(0.5*CHighQualityRand::HQRndUniformR(rs))-0.1);
|
|
bndu.Set(i,0.5*CHighQualityRand::HQRndUniformR(rs)+0.1);
|
|
}
|
|
for(i=0; i<=nlec-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,0);
|
|
c.Set(i,i,1);
|
|
c.Set(i,n,bndl[i]+CHighQualityRand::HQRndUniformR(rs)*(bndu[i]-bndl[i]));
|
|
}
|
|
for(i=nlec; i<k; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,0);
|
|
c.Set(i,i,1);
|
|
c.Set(i,n,1000);
|
|
}
|
|
badidx0=CHighQualityRand::HQRndUniformI(rs,k);
|
|
if(badidx0<nlec)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
c.Set(badidx0,n,bndu[badidx0]+10);
|
|
else
|
|
c.Set(badidx0,n,bndl[badidx0]-10);
|
|
}
|
|
else
|
|
c.Set(badidx0,n,-1000);
|
|
//--- Create and try to solve
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetNLC(state,nlec,nlic);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,MathPow(state.m_x[i]-xu[i],2));
|
|
state.m_j.Set(0,i,2*(state.m_x[i]-xu[i]));
|
|
}
|
|
for(i=0; i<k; i++)
|
|
{
|
|
state.m_fi.Set(i+1,-c.Get(i,n));
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Set(i+1,state.m_fi[i+1]+c.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Set(i+1,j,c.Get(i,j));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
//--- Check solution itself, calculate reference violation values
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3622");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3623");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check constraint violation reports
|
|
if(badidx0<nlec)
|
|
{
|
|
if(rep.m_bcerr>0.0)
|
|
{
|
|
v=MathMax(bndl[badidx0]-x1[badidx0],x1[badidx0]-bndu[badidx0]);
|
|
v=v/s[badidx0];
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=badidx0,"testminnlcunit.ap:3636");
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_bcerr-v))>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3638");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3643");
|
|
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3644");
|
|
CAp::SetErrorFlag(waserrors,(double)(x1[badidx0])<(double)(bndl[badidx0]) || (double)(x1[badidx0])>(double)(bndu[badidx0]),"testminnlcunit.ap:3645");
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3648");
|
|
CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3649");
|
|
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=badidx0,"testminnlcunit.ap:3650");
|
|
CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_nlcerr-MathAbs(x1[badidx0]-c.Get(badidx0,n))))>(double)(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3651");
|
|
}
|
|
//--- Test support for termination requests:
|
|
//--- * to terminate with correct return code = 8
|
|
//---*to return point which was "current" at the moment of termination
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
n=3;
|
|
ss=100;
|
|
x0=vector<double>::Zeros(n);
|
|
xlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,6+CHighQualityRand::HQRndUniformR(rs));
|
|
stopcallidx=CHighQualityRand::HQRndUniformI(rs,20);
|
|
maxits=25;
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,0,maxits);
|
|
CMinNLC::MinNLCSetXRep(state,true);
|
|
callidx=0;
|
|
terminationrequested=false;
|
|
xlast=x0;
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,ss*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]));
|
|
state.m_j.Set(0,0,2*ss*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[2]-state.m_x[0])*-1);
|
|
state.m_j.Set(0,1,2*state.m_x[1]);
|
|
state.m_j.Set(0,2,2*(state.m_x[2]-state.m_x[0]));
|
|
if(callidx==stopcallidx)
|
|
{
|
|
CMinNLC::MinNLCRequestTermination(state);
|
|
terminationrequested=true;
|
|
}
|
|
callidx++;
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
if(!terminationrequested)
|
|
xlast=state.m_x;
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=8,"testminnlcunit.ap:3708");
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(waserrors,x1[i]!=(double)(xlast[i]),"testminnlcunit.ap:3710");
|
|
}
|
|
}
|
|
//--- AUL-specific test.
|
|
//--- Test preconditioning:
|
|
//--- * compare number of iterations required to solve problem with
|
|
//--- different preconditioners (LBFGS, exact, none)
|
|
//--- * a set of trials is performed (100 trials)
|
|
//--- * each trial is a solution of boundary/linearly constrained problem
|
|
//--- (linear constraints may be posed as nonlinear ones) with normalized
|
|
//--- constraint matrix. Normalization is essential for reproducibility
|
|
//--- of results.
|
|
//--- Outer loop checks handling of different types of constraints
|
|
//--- (posed as linear or nonlinear ones)
|
|
n=30;
|
|
blocksize=3;
|
|
blockcnt=3;
|
|
rho=1.0E3;
|
|
aulits=5;
|
|
condv=1.0E2;
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(blocksize*blockcnt,n+1);
|
|
ct.Resize(blocksize*blockcnt);
|
|
for(ctype=0; ctype<=1; ctype++)
|
|
{
|
|
//--- First, initialize iteration counters
|
|
nlbfgs=0;
|
|
nexactlowrank=0;
|
|
nexactrobust=0;
|
|
nnone=0;
|
|
//--- Perform trials
|
|
for(trialidx=0; trialidx<=99; trialidx++)
|
|
{
|
|
//--- Generate:
|
|
//--- * boundary constraints BndL/BndU and initial point X0
|
|
//--- * block-diagonal matrix of linear constraints C such
|
|
//--- that X0 is feasible w.r.t. constraints given by C
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
bndl.Set(i,0);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
}
|
|
else
|
|
{
|
|
bndl.Set(i,0);
|
|
bndu.Set(i,0);
|
|
x0.Set(i,0);
|
|
}
|
|
}
|
|
for(i=0; i<=blocksize*blockcnt-1; i++)
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,0.0);
|
|
for(k=0; k<=blockcnt-1; k++)
|
|
{
|
|
CMatGen::RMatrixRndCond(blocksize,condv,a);
|
|
for(i=0; i<=blocksize-1; i++)
|
|
for(j=0; j<=blocksize-1; j++)
|
|
c.Set(k*blocksize+i,k*blocksize+j,a.Get(i,j));
|
|
}
|
|
for(i=0; i<=blocksize*blockcnt-1; i++)
|
|
{
|
|
v=CAblasF::RDotRR(n,c,i,c,i);
|
|
v=1/MathSqrt(v);
|
|
for(i_=0; i_<n; i_++)
|
|
c.Mul(i,i_,v);
|
|
v=CAblasF::RDotVR(n,x0,c,i);
|
|
c.Set(i,n,v);
|
|
ct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
}
|
|
//--- Test unpreconditioned iteration
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(ctype==0)
|
|
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
|
|
else
|
|
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
|
|
CMinNLC::MinNLCSetPrecNone(state);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
|
|
state.m_j.Set(0,i,2*state.m_x[i]);
|
|
}
|
|
if(ctype==1)
|
|
{
|
|
for(i=0; i<=blocksize*blockcnt-1; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,c,i);
|
|
state.m_fi.Set(1+i,v-c.Get(i,n));
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_j.Set(1+i,i_,c.Get(i,i_));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3834");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3835");
|
|
if(waserrors)
|
|
return;
|
|
nnone+=rep.m_iterationscount;
|
|
//--- Test LBFGS preconditioned iteration
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(ctype==0)
|
|
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
|
|
else
|
|
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
|
|
CMinNLC::MinNLCSetPrecInexact(state);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
|
|
state.m_j.Set(0,i,2*state.m_x[i]);
|
|
}
|
|
if(ctype==1)
|
|
{
|
|
for(i=0; i<=blocksize*blockcnt-1; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,c,i);
|
|
state.m_fi.Set(1+i,v-c.Get(i,n));
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_j.Set(1+i,i_,c.Get(i,i_));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3875");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3876");
|
|
if(waserrors)
|
|
return;
|
|
nlbfgs+=rep.m_iterationscount;
|
|
//--- Test exact low rank preconditioner
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(ctype==0)
|
|
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
|
|
else
|
|
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
|
|
CMinNLC::MinNLCSetPrecExactLowRank(state,3);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
|
|
state.m_j.Set(0,i,2*state.m_x[i]);
|
|
}
|
|
if(ctype==1)
|
|
{
|
|
for(i=0; i<=blocksize*blockcnt-1; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,c,i);
|
|
state.m_fi.Set(1+i,v-c.Get(i,n));
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_j.Set(1+i,i_,c.Get(i,i_));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3916");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3917");
|
|
if(waserrors)
|
|
return;
|
|
nexactlowrank+=rep.m_iterationscount;
|
|
//--- Test exact robust preconditioner
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
|
|
if(ctype==0)
|
|
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
|
|
else
|
|
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
|
|
CMinNLC::MinNLCSetPrecExactRobust(state,3);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
|
|
state.m_j.Set(0,i,2*state.m_x[i]);
|
|
}
|
|
if(ctype==1)
|
|
{
|
|
for(i=0; i<=blocksize*blockcnt-1; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,c,i);
|
|
state.m_fi.Set(1+i,v-c.Get(i,n));
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_j.Set(1+i,i_,c.Get(i,i_));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3957");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3958");
|
|
if(waserrors)
|
|
return;
|
|
nexactrobust+=rep.m_iterationscount;
|
|
}
|
|
//--- Compare.
|
|
//--- Preconditioners must be significantly different,
|
|
//--- with exact being best one, inexact being second,
|
|
//--- "none" being worst option.
|
|
CAp::SetErrorFlag(waserrors,!((double)(nexactlowrank)<(double)(0.9*nlbfgs)),"testminnlcunit.ap:3971");
|
|
CAp::SetErrorFlag(waserrors,!((double)(nexactrobust)<(double)(0.9*nlbfgs)),"testminnlcunit.ap:3972");
|
|
CAp::SetErrorFlag(waserrors,!((double)(nlbfgs)<(double)(0.9*nnone)),"testminnlcunit.ap:3973");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests OptGuard |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestOptGuard(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
double v=0;
|
|
CMinNLCState state;
|
|
CMinNLCReport rep;
|
|
COptGuardReport ogrep;
|
|
COptGuardNonC1Test0Report ognonc1test0strrep;
|
|
COptGuardNonC1Test0Report ognonc1test0lngrep;
|
|
COptGuardNonC1Test1Report ognonc1test1strrep;
|
|
COptGuardNonC1Test1Report ognonc1test1lngrep;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble a1;
|
|
CRowDouble s;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble xlast;
|
|
CRowDouble b;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble xu;
|
|
double diffstep=0;
|
|
int pass=0;
|
|
int solvertype=0;
|
|
double vbnd=0;
|
|
double fscale=0;
|
|
int defecttype=0;
|
|
double vshift=0;
|
|
double vpower=0;
|
|
int cntabove=0;
|
|
int cntbelow=0;
|
|
bool linesearchstarted;
|
|
bool wasgoodlinesearch0;
|
|
bool wasgoodlinesearch1;
|
|
int shortsessions=0;
|
|
int maxshortsessions=0;
|
|
double stplen=0;
|
|
double shortstplen=0;
|
|
bool failed;
|
|
int passcount=0;
|
|
int maxfails=0;
|
|
int failurecounter=0;
|
|
int maxc1test0fails=0;
|
|
int maxc1test1fails=0;
|
|
int c1test0fails=0;
|
|
int c1test1fails=0;
|
|
int goodidx=0;
|
|
double avgstr0len=0;
|
|
double avglng0len=0;
|
|
double avgstr1len=0;
|
|
double avglng1len=0;
|
|
int funcidx=0;
|
|
int varidx=0;
|
|
int skind=0;
|
|
CMatrixDouble jactrue;
|
|
CMatrixDouble jacdefect;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test functionality which should work in all NLC solvers
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
//--- Check that gradient verification is disabled by default:
|
|
//--- gradient checking for bad problem must return nothing
|
|
n=10;
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,1.0+0.1*i);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,5);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,10);
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*v);
|
|
}
|
|
state.m_fi.Set(1,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a1,i);
|
|
state.m_fi.Add(1,0.5*state.m_x[i]*v);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_j.Set(0,i,0);
|
|
state.m_j.Set(1,i,0);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4085");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4086");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminnlcunit.ap:4089");
|
|
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminnlcunit.ap:4090");
|
|
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminnlcunit.ap:4091");
|
|
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminnlcunit.ap:4092");
|
|
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminnlcunit.ap:4093");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminnlcunit.ap:4096");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminnlcunit.ap:4097");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminnlcunit.ap:4098");
|
|
//--- Test that C0/C1 continuity monitoring is disabled by default;
|
|
//--- we solve nonsmooth problem and test that nothing is returned
|
|
//--- by OptGuard.
|
|
n=10;
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,1);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-9,50);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
state.m_j.Set(0,i,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,MathAbs(v));
|
|
v=MathSign(v);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Add(0,j,v*a.Get(i,j));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4146");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4147");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4150");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1suspected,"testminnlcunit.ap:4151");
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4152");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4153");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>=0,"testminnlcunit.ap:4154");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test0positive,"testminnlcunit.ap:4155");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test1positive,"testminnlcunit.ap:4156");
|
|
//--- Test gradient checking functionality, try various
|
|
//--- defect types:
|
|
//--- * accidental zeroing of some gradient component
|
|
//--- * accidental addition of 1.0 to some component
|
|
//--- * accidental multiplication by 2.0
|
|
//--- Try distorting both target and constraints.
|
|
diffstep=0.001;
|
|
n=10;
|
|
for(skind=0; skind<=1; skind++)
|
|
{
|
|
for(funcidx=0; funcidx<=1; funcidx++)
|
|
{
|
|
for(defecttype=-1; defecttype<=2; defecttype++)
|
|
{
|
|
varidx=CHighQualityRand::HQRndUniformI(rs,n);
|
|
x0=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(10,skind*(30*CHighQualityRand::HQRndUniformR(rs)-15)));
|
|
x0.Set(i,(1.0+0.1*i)*s[i]);
|
|
j=CHighQualityRand::HQRndUniformI(rs,3);
|
|
bndl.Set(i,-(100*s[i]));
|
|
bndu.Set(i,100*s[i]);
|
|
if(j==1)
|
|
bndl.Set(i,x0[i]);
|
|
if(j==2)
|
|
bndu.Set(i,x0[i]);
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,5);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCOptGuardGradient(state,diffstep);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,10);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
if(solvertype!=0)
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminnlcunit.ap:4216");
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminnlcunit.ap:4217");
|
|
}
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=state.m_x[j]/s[j]*a.Get(i,j);
|
|
state.m_fi.Add(0,0.5*(state.m_x[i]/s[i])*v);
|
|
state.m_j.Set(0,i,v);
|
|
}
|
|
state.m_fi.Set(1,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=state.m_x[j]/s[j]*a1.Get(i,j);
|
|
state.m_fi.Add(1,0.5*(state.m_x[i]/s[i])*v);
|
|
state.m_j.Set(1,i,v);
|
|
}
|
|
if(defecttype==0)
|
|
state.m_j.Set(funcidx,varidx,0);
|
|
if(defecttype==1)
|
|
state.m_j.Set(funcidx,varidx,state.m_j.Get(funcidx,varidx)+1);
|
|
if(defecttype==2)
|
|
state.m_j.Mul(funcidx,varidx,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_j.Mul(0,i,1.0/s[i]);
|
|
state.m_j.Mul(1,i,1.0/s[i]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
//--- Check that something is returned
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4267");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4268");
|
|
if(waserrors)
|
|
return;
|
|
//--- Compute reference values for true and spoiled Jacobian at X0
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(ogrep.m_badgradxbase,n),"testminnlcunit.ap:4275");
|
|
if(waserrors)
|
|
return;
|
|
jactrue=matrix<double>::Zeros(2,n);
|
|
jacdefect=matrix<double>::Zeros(2,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=ogrep.m_badgradxbase[j]/s[j]*a.Get(i,j);
|
|
jactrue.Set(0,i,v);
|
|
jacdefect.Set(0,i,v);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=ogrep.m_badgradxbase[j]/s[j]*a1.Get(i,j);
|
|
jactrue.Set(1,i,v);
|
|
jacdefect.Set(1,i,v);
|
|
}
|
|
if(defecttype==0)
|
|
jacdefect.Set(funcidx,varidx,0);
|
|
if(defecttype==1)
|
|
jacdefect.Add(funcidx,varidx,1);
|
|
if(defecttype==2)
|
|
jacdefect.Mul(funcidx,varidx,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
jactrue.Mul(0,i,1.0/s[i]);
|
|
jactrue.Mul(1,i,1.0/s[i]);
|
|
jacdefect.Mul(0,i,1.0/s[i]);
|
|
jacdefect.Mul(1,i,1.0/s[i]);
|
|
}
|
|
//--- Check OptGuard report
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgraduser,2,n),"testminnlcunit.ap:4313");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgradnum,2,n),"testminnlcunit.ap:4314");
|
|
if(waserrors)
|
|
return;
|
|
if(defecttype>=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminnlcunit.ap:4319");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=funcidx,"testminnlcunit.ap:4320");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminnlcunit.ap:4321");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminnlcunit.ap:4325");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminnlcunit.ap:4326");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminnlcunit.ap:4327");
|
|
}
|
|
for(i=0; i<=1; i++)
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(jactrue.Get(i,j)-ogrep.m_badgradnum.Get(i,j)))>(0.01/s[j]),"testminnlcunit.ap:4332");
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(jacdefect.Get(i,j)-ogrep.m_badgraduser.Get(i,j)))>(0.01/s[j]),"testminnlcunit.ap:4333");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Make sure than no false positives are reported for larger
|
|
//--- problems where numerical noise can be an issue:
|
|
//--- * N=100 dimensions
|
|
//--- * nonnegativity constraints
|
|
//--- * positive-definite quadratic programming problem
|
|
//--- * upper limit on iterations count, MaxIts=25
|
|
//--- We simply test that OptGuard does not return error code.
|
|
n=100;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,a);
|
|
b=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
bndl.Set(i,0);
|
|
bndu.Set(i,AL_POSINF);
|
|
x0.Set(i,MathPow(2.0,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,3);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,25);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*a.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Set(0,i,a.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4391");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4392");
|
|
if(waserrors)
|
|
return;
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4396");
|
|
}
|
|
//--- Test functionality which works only in some NLC solvers
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
//--- Skip
|
|
if(solvertype==2)
|
|
continue;
|
|
//--- Test detection of discontinuities in the target function
|
|
//--- and its gradient (analytic gradient is used).
|
|
//--- Target function is convex quadratic modified by addition of
|
|
//--- nonsmooth/discontinuous (depending on DefectType) perturbation.
|
|
//--- This test is complicated because OptGuard does NOT guarantee
|
|
//--- that C0/C1 violations are ALWAYS caught. OptGuard needs line
|
|
//--- search to perform
|
|
//--- * at least 4 function evaluations, with discontinuity in the
|
|
//--- middle of them (at least two function values from the left,
|
|
//--- at least two from the right)
|
|
//--- * at least 7 function evaluations to catch C1 violation
|
|
//--- Furthermore, it is possible that optimizer will perform a few
|
|
//--- function evaluations BEFORE and AFTER line search starts,
|
|
//--- which complicates everything.
|
|
//--- N, VPower and VBnd are selected randomly at the start of the test.
|
|
for(defecttype=0; defecttype<=1; defecttype++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
vpower=10*MathPow(10,-(0.2*CHighQualityRand::HQRndUniformR(rs)));
|
|
vbnd=1*MathPow(10,-(0.2*CHighQualityRand::HQRndUniformR(rs)));
|
|
fscale=0.1;
|
|
maxshortsessions=4;
|
|
shortstplen=1.0E-6;
|
|
shortsessions=0;
|
|
for(pass=1; pass<=100; pass++)
|
|
{
|
|
//--- Formulate problem
|
|
s=vector<double>::Zeros(n);
|
|
xlast=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
xu=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
do
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xu.Set(i,0.1+CHighQualityRand::HQRndUniformR(rs));
|
|
v+=CMath::Sqr(xu[i]);
|
|
}
|
|
v=MathSqrt(v);
|
|
}
|
|
while(v<=0.0);
|
|
for(i=0; i<n; i++)
|
|
xu.Mul(i,1.0/v);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xlast.Set(i,0);
|
|
x0.Set(i,2*xu[i]+0.1*CHighQualityRand::HQRndUniformR(rs));
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,AL_POSINF);
|
|
s.Set(i,MathPow(2,0.1*CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
if(defecttype==0)
|
|
vshift=1;
|
|
else
|
|
vshift=0;
|
|
//--- Prepare code which detects "good" (long enough) line searches
|
|
cntbelow=0;
|
|
cntabove=0;
|
|
wasgoodlinesearch0=false;
|
|
wasgoodlinesearch1=false;
|
|
linesearchstarted=false;
|
|
//--- Create and try to solve
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,5);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,1000);
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
CMinNLC::MinNLCSetXRep(state,true);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,fscale*MathPow(state.m_x[i],2));
|
|
state.m_j.Set(0,i,2*fscale*state.m_x[i]);
|
|
}
|
|
v=CAblasF::RDotV(n,state.m_x,xu);
|
|
if(v<vbnd)
|
|
{
|
|
state.m_fi.Add(0,(vshift+vpower*(vbnd-v)));
|
|
for(i=0; i<n; i++)
|
|
state.m_j.Add(0,i,- vpower*xu[i]);
|
|
if(linesearchstarted)
|
|
cntbelow++;
|
|
}
|
|
else
|
|
{
|
|
if(linesearchstarted)
|
|
cntabove++;
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- Finalize previous line search
|
|
if(linesearchstarted)
|
|
{
|
|
stplen=0;
|
|
for(i=0; i<n; i++)
|
|
stplen+=CMath::Sqr(state.m_x[i]-xlast[i]);
|
|
stplen=MathSqrt(stplen);
|
|
wasgoodlinesearch0=wasgoodlinesearch0||((cntbelow>=2 && cntabove>=2) && (double)(stplen)>=(double)(shortstplen));
|
|
wasgoodlinesearch1=wasgoodlinesearch1||((cntbelow>=2 && cntabove>=2) && (double)(stplen)>=(double)(shortstplen));
|
|
}
|
|
//--- Start new line search
|
|
linesearchstarted=true;
|
|
cntbelow=0;
|
|
cntabove=0;
|
|
xlast=state.m_x;
|
|
v=CAblasF::RDotV(n,state.m_x,xu);
|
|
if(v<vbnd)
|
|
cntbelow++;
|
|
else
|
|
cntabove++;
|
|
//--- Done
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
//--- Check basic properties of the solution
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4573");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4574");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check OptGuard report, increase
|
|
if(defecttype==0)
|
|
{
|
|
if(wasgoodlinesearch0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4585");
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc0suspected,"testminnlcunit.ap:4586");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx!=0,"testminnlcunit.ap:4587");
|
|
}
|
|
else
|
|
shortsessions++;
|
|
}
|
|
if(defecttype==1)
|
|
{
|
|
if(wasgoodlinesearch1)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4596");
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4597");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4598");
|
|
}
|
|
else
|
|
shortsessions++;
|
|
}
|
|
if(waserrors)
|
|
return;
|
|
}
|
|
//--- Check that short optimization sessions are rare.
|
|
CAp::SetErrorFlag(waserrors,shortsessions>maxshortsessions,"testminnlcunit.ap:4610");
|
|
}
|
|
//--- One more test for detection of C1 continuity violations in the target.
|
|
//--- Target function is a sum of |(x,c_i)| for i=1..N.
|
|
//--- No constraints is present.
|
|
//--- Analytic gradient is provided.
|
|
//--- OptGuard should be able to detect violations in more than
|
|
//--- 99.9% of runs; it means that 100 runs should have no more than 4
|
|
//--- failures in all cases (even after multiple repeated tests; according
|
|
//--- to the binomial distribution quantiles).
|
|
//--- We select some N and perform exhaustive search for this N.
|
|
//--- NOTE: we skip SQP for this test
|
|
passcount=100;
|
|
maxfails=4;
|
|
maxc1test0fails=10;
|
|
maxc1test1fails=10;
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
failurecounter=0;
|
|
c1test0fails=0;
|
|
c1test1fails=0;
|
|
avgstr0len=0;
|
|
avglng0len=0;
|
|
avgstr1len=0;
|
|
avglng1len=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Formulate problem
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
//--- Create and try to solve
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,1);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,50);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
state.m_j.Set(0,i,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,MathAbs(v));
|
|
v=MathSign(v);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Add(0,j,v*a.Get(i,j));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
//--- Check basic properties of the solution
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4700");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4701");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check generic OptGuard report: distinguish between "hard"
|
|
//--- failures which result in immediate termination
|
|
//--- (C0 violation being reported) and "soft" ones
|
|
//--- (C1 violation is NOT reported) which accumulate
|
|
//--- until we exhaust limit.
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4712");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4713");
|
|
failed=false;
|
|
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
|
|
failed=failed||!ogrep.m_nonc1suspected;
|
|
failed=failed||ogrep.m_nonc1fidx!=0;
|
|
if(failed)
|
|
failurecounter++;
|
|
//--- Check C1 continuity test #0
|
|
CMinNLC::MinNLCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep);
|
|
CMinNLC::MinNLCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep);
|
|
if(ogrep.m_nonc1test0positive)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4728");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminnlcunit.ap:4729");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4730");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4731");
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
|
|
avgstr0len+=(double)ognonc1test0strrep.m_cnt/(double)passcount;
|
|
avglng0len+=(double)ognonc1test0lngrep.m_cnt/(double)passcount;
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminnlcunit.ap:4739");
|
|
CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4740");
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
|
|
c1test0fails++;
|
|
}
|
|
if(ogrep.m_nonc1test1positive)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4747");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminnlcunit.ap:4748");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminnlcunit.ap:4749");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4750");
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
|
|
avgstr1len+=(double)ognonc1test1strrep.m_cnt/(double)passcount;
|
|
avglng1len+=(double)ognonc1test1lngrep.m_cnt/(double)passcount;
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminnlcunit.ap:4758");
|
|
CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminnlcunit.ap:4759");
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
|
|
c1test1fails++;
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:4766");
|
|
CAp::SetErrorFlag(waserrors,c1test0fails>maxc1test0fails,"testminnlcunit.ap:4767");
|
|
CAp::SetErrorFlag(waserrors,c1test1fails>maxc1test1fails,"testminnlcunit.ap:4768");
|
|
CAp::SetErrorFlag(waserrors,(double)(avglng0len)<=(double)(avgstr0len),"testminnlcunit.ap:4769");
|
|
CAp::SetErrorFlag(waserrors,(double)(avglng1len)<=(double)(avgstr1len),"testminnlcunit.ap:4770");
|
|
//--- Detection of C1 continuity violations in the target under numerical differentiation:
|
|
//--- * target function is a sum of |(x,c_i)| for i=1..N.
|
|
//--- * no constraints is present.
|
|
//--- * analytic gradient is provided.
|
|
//--- OptGuard should always be able to detect violations in more than
|
|
//--- 99% of runs (note: reduced strength when compared with analytic gradient);
|
|
//--- it means that 100 runs should have no more than 10 failures in all cases
|
|
//--- (even after multiple repeated tests; according to the binomial distribution
|
|
//--- quantiles).
|
|
//--- We select some N and perform exhaustive search for this N.
|
|
//--- NOTE: we skip SQP solver for this test
|
|
diffstep=0.0001;
|
|
passcount=100;
|
|
maxfails=10;
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
failurecounter=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Formulate problem
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,0.01*MathPow(2,0.33*CHighQualityRand::HQRndNormal(rs)));
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
//--- Create and try to solve
|
|
CMinNLC::MinNLCCreateF(n,x0,diffstep,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,1);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,50);
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_fi.Add(0,MathAbs(v));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
//--- Check basic properties of the solution
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4845");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4846");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check OptGuard report: distinguish between "hard"
|
|
//--- failures which result in immediate termination
|
|
//--- (C0 violation being reported) and "soft" ones
|
|
//--- (C1 violation is NOT reported) which accumulate
|
|
//--- until we exhaust limit.
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4857");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4858");
|
|
failed=false;
|
|
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
|
|
failed=failed||!ogrep.m_nonc1suspected;
|
|
failed=failed||ogrep.m_nonc1fidx!=0;
|
|
if(failed)
|
|
failurecounter++;
|
|
}
|
|
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:4866");
|
|
//--- Detection of C1 continuity violations in the nonlinear constraints.
|
|
//--- This test is a bit tricky because optimizers are less sensitive to
|
|
//--- continuity violations in constraints, so we may have hard time collecting
|
|
//--- enough statistics. In order to do so we solve carefully designed hard
|
|
//--- problem with MULTIPLE bad constraints and only one good constraint.
|
|
//--- Optimizer may report any of bad constraints, but not good one.
|
|
//--- We select some N and perform exhaustive search for this N.
|
|
passcount=100;
|
|
maxfails=20;
|
|
n=5;
|
|
failurecounter=0;
|
|
c1test0fails=0;
|
|
c1test1fails=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Formulate problem, select constraint index to perturb with
|
|
//--- nonsmoothness; make sure that this constraint is active at
|
|
//--- the solution
|
|
x0=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,MathPow(2,0.01*CHighQualityRand::HQRndNormal(rs)));
|
|
goodidx=CHighQualityRand::HQRndUniformI(rs,n);
|
|
//--- Create and try to solve
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,3);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,50);
|
|
CMinNLC::MinNLCSetNLC(state,0,n);
|
|
CMinNLC::MinNLCSetScale(state,s);
|
|
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
state.m_j.Set(0,i,b[i]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Set(1+i,-1);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Set(1+i,state.m_fi[1+i]+a.Get(i,j)*CMath::Sqr(state.m_x[j]));
|
|
state.m_j.Set(1+i,j,2*a.Get(i,j)*state.m_x[j]);
|
|
}
|
|
if(i!=goodidx)
|
|
{
|
|
state.m_fi.Set(1+i,MathMax(state.m_fi[1+i],0.0));
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Mul(1+i,j,MathSign(state.m_fi[1+i]));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CMinNLC::MinNLCOptGuardResults(state,ogrep);
|
|
//--- Check basic properties of the solution
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4962");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4963");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check generic OptGuard report: distinguish between "hard"
|
|
//--- failures which result in immediate termination
|
|
//--- (C0 violation being reported) and "soft" ones
|
|
//--- (C1 violation is NOT reported) which accumulate
|
|
//--- until we exhaust limit.
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4974");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4975");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:4976");
|
|
failed=false;
|
|
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
|
|
failed=failed||!ogrep.m_nonc1suspected;
|
|
if(failed)
|
|
failurecounter++;
|
|
//--- Check C1 continuity tests #0 and #1
|
|
CMinNLC::MinNLCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep);
|
|
CMinNLC::MinNLCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep);
|
|
if(ogrep.m_nonc1test0positive)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4990");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminnlcunit.ap:4991");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4992");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx<1,"testminnlcunit.ap:4993");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>n,"testminnlcunit.ap:4994");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:4995");
|
|
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0strrep,a,n,goodidx);
|
|
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0lngrep,a,n,goodidx);
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminnlcunit.ap:5001");
|
|
CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminnlcunit.ap:5002");
|
|
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0strrep,a,n,goodidx);
|
|
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0lngrep,a,n,goodidx);
|
|
c1test0fails++;
|
|
}
|
|
if(ogrep.m_nonc1test1positive)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:5009");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminnlcunit.ap:5010");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminnlcunit.ap:5011");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx<1,"testminnlcunit.ap:5012");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>n,"testminnlcunit.ap:5013");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:5014");
|
|
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1strrep,a,n,goodidx);
|
|
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1lngrep,a,n,goodidx);
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminnlcunit.ap:5020");
|
|
CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminnlcunit.ap:5021");
|
|
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1strrep,a,n,goodidx);
|
|
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1lngrep,a,n,goodidx);
|
|
c1test1fails++;
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:5027");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function performs tests for fixed bugs |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestBugs(bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
int n=0;
|
|
int aulits=0;
|
|
int maxits=0;
|
|
double rho=0;
|
|
int ckind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int solvertype=0;
|
|
CMinNLCState state;
|
|
CMinNLCReport rep;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble d;
|
|
CRowDouble b;
|
|
CRowInt ct;
|
|
CMatrixDouble c;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Bug description (fixed): sometimes on non-convex problems, when
|
|
//--- Lagrange coefficient for inequality constraint becomes small,
|
|
//--- algorithm performs VERY deep step into infeasible area (step is 1E50),
|
|
//--- which de-stabilizes it and prevents from converging back to feasible area.
|
|
//--- Very rare situation, but must be fixed with additional "convexifying" term.
|
|
//--- This test reproduces situation with convexified term turned off, then
|
|
//--- checks that introduction of term solves issue.
|
|
//--- We perform three kinds of tests:
|
|
//--- * with box inequality constraint
|
|
//--- * with linear inequality constraint
|
|
//--- * with nonlinear inequality constraint
|
|
//--- In all three cases we:
|
|
//--- * first time solve non-convex problem with artificially moved stabilizing
|
|
//--- point and decreased initial value of Lagrange multiplier.
|
|
//--- * second time we solve problem with good stabilizing point, but zero Lagrange multiplier
|
|
//--- * last time solve same problem, but with default settings
|
|
aulits=1;
|
|
maxits=1;
|
|
rho=100.0;
|
|
n=1;
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Full(n,AL_POSINF);
|
|
c=matrix<double>::Zeros(1,2);
|
|
ct.Resize(1);
|
|
c.Set(0,0,1.0);
|
|
ct.Set(0,1);
|
|
for(ckind=0; ckind<=2; ckind++)
|
|
{
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
state.m_stabilizingpoint=-1.0E300;
|
|
state.m_initialinequalitymultiplier=1.0E-12;
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,0.0,maxits);
|
|
switch(ckind)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetLC(state,c,ct,1);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
break;
|
|
}
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0]));
|
|
state.m_j.Set(0,0,1-2*state.m_x[0]);
|
|
if(ckind==2)
|
|
{
|
|
state.m_fi.Set(1,-state.m_x[0]);
|
|
state.m_j.Set(1,0,-1);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5125");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,x1[0]>(-1.0E6),"testminnlcunit.ap:5128");
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
state.m_stabilizingpoint=-1.0E2;
|
|
state.m_initialinequalitymultiplier=1.0E-12;
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,0.0,maxits);
|
|
switch(ckind)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetLC(state,c,ct,1);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
break;
|
|
}
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0]));
|
|
state.m_j.Set(0,0,1-2*state.m_x[0]);
|
|
if(ckind==2)
|
|
{
|
|
state.m_fi.Set(1,-state.m_x[0]);
|
|
state.m_j.Set(1,0,-1);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5157");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,x1[0]<(double)(3*state.m_stabilizingpoint),"testminnlcunit.ap:5160");
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
|
|
CMinNLC::MinNLCSetCond(state,0.0,maxits);
|
|
switch(ckind)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetBC(state,bndl,bndu);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetLC(state,c,ct,1);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetNLC(state,0,1);
|
|
break;
|
|
}
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0]));
|
|
state.m_j.Set(0,0,1-2*state.m_x[0]);
|
|
if(ckind==2)
|
|
{
|
|
state.m_fi.Set(1,-state.m_x[0]);
|
|
state.m_j.Set(1,0,-1);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5187");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,x1[0]<(double)(3*state.m_stabilizingpoint),"testminnlcunit.ap:5190");
|
|
}
|
|
//--- This test checks report by E. Pozamantir. Relevant for SLP
|
|
//--- and related methods, but we test it for all algorithms.
|
|
//--- Description:
|
|
//--- The sequential linear programming solver performs warm-start
|
|
//--- at each iteration, i.e. it reuses previously found LP basis.
|
|
//--- However, when some initially non-zero Jacobian entries become
|
|
//--- exactly zero (possible with constraints which are nonsmooth
|
|
//--- at some distance from the boundary), our warm-start strategy
|
|
//--- may fail because warm-start basis becomes exactly degenerate.
|
|
//--- In order to test that this bug was fixed we solve carefully
|
|
//--- designed noisy test problem with Jacobian entries being randomly
|
|
//--- turned on and off. Simply being able to return from the solver
|
|
//--- without triggering critical exception is enough.
|
|
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
|
|
{
|
|
for(n=6; n<=15; n++)
|
|
{
|
|
//--- Setup problem
|
|
x0=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
d.Set(i,CMath::Sqr(CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,n/3);
|
|
c=matrix<double>::Zeros(2*k,n+1);
|
|
for(i=0; i<2*k; i++)
|
|
for(j=0; j<n; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=0; i<k; i++)
|
|
c.Set(i,n,0);
|
|
for(i=k; i<2*k; i++)
|
|
c.Set(i,n,0.1+CHighQualityRand::HQRndUniformR(rs));
|
|
//--- Create solver and solve problem
|
|
CMinNLC::MinNLCCreate(n,x0,state);
|
|
switch(solvertype)
|
|
{
|
|
case 0:
|
|
CMinNLC::MinNLCSetAlgoAUL(state,200.0,20);
|
|
break;
|
|
case 1:
|
|
CMinNLC::MinNLCSetAlgoSLP(state);
|
|
break;
|
|
case 2:
|
|
CMinNLC::MinNLCSetAlgoSQP(state);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCSetCond(state,1.0E-7,20);
|
|
CMinNLC::MinNLCSetNLC(state,k,k);
|
|
while(CMinNLC::MinNLCIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(0,0.5*d[j]*CMath::Sqr(state.m_x[j]));
|
|
state.m_j.Set(0,j,d[j]*state.m_x[j]);
|
|
}
|
|
for(i=0; i<=2*k-1; i++)
|
|
{
|
|
state.m_fi.Set(1+i,-c.Get(i,n));
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_fi.Add(1+i,c.Get(i,j)*state.m_x[j]);
|
|
state.m_j.Set(1+i,j,CHighQualityRand::HQRndUniformI(rs,2)*c.Get(i,j));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinNLC::MinNLCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:5277");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5278");
|
|
if(waserrors)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests report of "non-C1" test #0 for task #0 |
|
|
//| given by matrix A. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestOptGuardC1Test0ReportForTask0(bool &err,
|
|
COptGuardNonC1Test0Report &rep,
|
|
CMatrixDouble &a,
|
|
int n)
|
|
{
|
|
//--- create variables
|
|
double va=0;
|
|
double vb=0;
|
|
bool hasc1discontinuities;
|
|
|
|
if(rep.m_positive)
|
|
{
|
|
//--- Check positive report, first checks
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminnlcunit.ap:5307");
|
|
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5308");
|
|
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5309");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5310");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5311");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5312");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5313");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5314");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=rep.m_cnt,"testminnlcunit.ap:5315");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5316");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5317");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5318");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_f,rep.m_cnt),"testminnlcunit.ap:5319");
|
|
if(err)
|
|
return;
|
|
//--- Check consistency of Stp.
|
|
for(int k=0; k<=rep.m_cnt-2; k++)
|
|
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminnlcunit.ap:5327");
|
|
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
|
|
hasc1discontinuities=false;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
va=0;
|
|
vb=0;
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
|
|
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
|
|
}
|
|
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
|
|
}
|
|
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminnlcunit.ap:5344");
|
|
}
|
|
else
|
|
{
|
|
//--- Check negative report: fields must be empty
|
|
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5351");
|
|
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5352");
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5353");
|
|
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5354");
|
|
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5355");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5356");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5357");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5358");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=0,"testminnlcunit.ap:5359");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests report of "non-C1" test #1 for task #0 |
|
|
//| given by matrix A. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestOptGuardC1Test1ReportForTask0(bool &err,
|
|
COptGuardNonC1Test1Report &rep,
|
|
CMatrixDouble &a,int n)
|
|
{
|
|
//--- create variables
|
|
double va=0;
|
|
double vb=0;
|
|
bool tooclose;
|
|
bool hasc1discontinuities;
|
|
|
|
if(rep.m_positive)
|
|
{
|
|
//--- Check positive report, first checks
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminnlcunit.ap:5387");
|
|
CAp::SetErrorFlag(err,rep.m_vidx<0,"testminnlcunit.ap:5388");
|
|
CAp::SetErrorFlag(err,rep.m_vidx>n,"testminnlcunit.ap:5389");
|
|
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5390");
|
|
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5391");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5392");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5393");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5394");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5395");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5396");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=rep.m_cnt,"testminnlcunit.ap:5397");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5398");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5399");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5400");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_g,rep.m_cnt),"testminnlcunit.ap:5401");
|
|
if(err)
|
|
return;
|
|
//--- Check consistency of Stp
|
|
for(int k=0; k<=rep.m_cnt-2; k++)
|
|
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminnlcunit.ap:5409");
|
|
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
|
|
tooclose=false;
|
|
hasc1discontinuities=false;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
va=0;
|
|
vb=0;
|
|
for(int j=0; j<n; j++)
|
|
{
|
|
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
|
|
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
|
|
}
|
|
tooclose=(tooclose||(double)(MathAbs(va))<(double)(1.0E-8))||(double)(MathAbs(vb))<(double)(1.0E-8);
|
|
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
|
|
}
|
|
if(!tooclose)
|
|
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminnlcunit.ap:5429");
|
|
}
|
|
else
|
|
{
|
|
//--- Check negative report: fields must be empty
|
|
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5436");
|
|
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5437");
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5438");
|
|
CAp::SetErrorFlag(err,rep.m_vidx!=-1,"testminnlcunit.ap:5439");
|
|
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5440");
|
|
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5441");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5442");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5443");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5444");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=0,"testminnlcunit.ap:5445");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests report of "non-C1" test #0 for task #1 |
|
|
//| given by matrix A. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestOptGuardC1Test0ReportForTask1(bool &err,
|
|
COptGuardNonC1Test0Report &rep,
|
|
CMatrixDouble &a,
|
|
int n,
|
|
int goodidx)
|
|
{
|
|
if(rep.m_positive)
|
|
{
|
|
//--- Check positive report, first checks
|
|
CAp::SetErrorFlag(err,rep.m_fidx==goodidx+1,"testminnlcunit.ap:5469");
|
|
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5470");
|
|
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5471");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5472");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5473");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5474");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5475");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5476");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=rep.m_cnt,"testminnlcunit.ap:5477");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5478");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5479");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5480");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_f,rep.m_cnt),"testminnlcunit.ap:5481");
|
|
if(err)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- Check negative report: fields must be empty
|
|
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5490");
|
|
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5491");
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5492");
|
|
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5493");
|
|
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5494");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5495");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5496");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5497");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=0,"testminnlcunit.ap:5498");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests report of "non-C1" test #1 for task #1 given |
|
|
//| by matrix A. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNLCUnit::TestOptGuardC1Test1ReportForTask1(bool &err,
|
|
COptGuardNonC1Test1Report &rep,
|
|
CMatrixDouble &a,
|
|
int n,
|
|
int goodidx)
|
|
{
|
|
if(rep.m_positive)
|
|
{
|
|
//--- Check positive report, first checks
|
|
CAp::SetErrorFlag(err,rep.m_fidx==goodidx+1,"testminnlcunit.ap:5521");
|
|
CAp::SetErrorFlag(err,rep.m_vidx<0,"testminnlcunit.ap:5522");
|
|
CAp::SetErrorFlag(err,rep.m_vidx>n,"testminnlcunit.ap:5523");
|
|
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5524");
|
|
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5525");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5526");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5527");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5528");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5529");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5530");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=rep.m_cnt,"testminnlcunit.ap:5531");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5532");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5533");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5534");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_g,rep.m_cnt),"testminnlcunit.ap:5535");
|
|
if(err)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- Check negative report: fields must be empty
|
|
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5544");
|
|
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5545");
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5546");
|
|
CAp::SetErrorFlag(err,rep.m_vidx!=-1,"testminnlcunit.ap:5547");
|
|
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5548");
|
|
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5549");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5550");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5551");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5552");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=0,"testminnlcunit.ap:5553");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinNSUnit
|
|
{
|
|
public:
|
|
static const double m_scalingtesttol;
|
|
static const int m_scalingtestcnt;
|
|
|
|
static bool TestMinNS(bool silent);
|
|
|
|
private:
|
|
static void BasicTest0UC(bool &errors);
|
|
static void BasicTest1UC(bool &errors);
|
|
static void BasicTest0BC(bool &errors);
|
|
static void BasicTest1BC(bool &errors);
|
|
static void BasicTest0LC(bool &errors);
|
|
static void BasicTest1LC(bool &errors);
|
|
static void BasicTest0NLC(bool &errors);
|
|
static void TestUC(bool &primaryerrors,bool &othererrors);
|
|
static void TestBC(bool &primaryerrors,bool &othererrors);
|
|
static void TestLC(bool &primaryerrors,bool &othererrors);
|
|
static void TestNLC(bool &primaryerrors,bool &othererrors);
|
|
static void TestOther(bool &othererrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const double CTestMinNSUnit::m_scalingtesttol=1.0E-6;
|
|
const int CTestMinNSUnit::m_scalingtestcnt=3;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinNSUnit::TestMinNS(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool ucerrors;
|
|
bool bcerrors;
|
|
bool lcerrors;
|
|
bool nlcerrors;
|
|
bool othererrors;
|
|
|
|
waserrors=false;
|
|
ucerrors=false;
|
|
bcerrors=false;
|
|
lcerrors=false;
|
|
nlcerrors=false;
|
|
othererrors=false;
|
|
//--- Basic tests
|
|
BasicTest0NLC(nlcerrors);
|
|
BasicTest0UC(ucerrors);
|
|
BasicTest1UC(ucerrors);
|
|
BasicTest0BC(bcerrors);
|
|
BasicTest1BC(bcerrors);
|
|
BasicTest0LC(lcerrors);
|
|
BasicTest1LC(lcerrors);
|
|
//--- Special tests
|
|
TestOther(othererrors);
|
|
//--- Full scale tests
|
|
TestUC(ucerrors,othererrors);
|
|
TestBC(bcerrors,othererrors);
|
|
TestLC(lcerrors,othererrors);
|
|
TestNLC(nlcerrors,othererrors);
|
|
//--- end
|
|
waserrors=(((ucerrors||bcerrors)||lcerrors)||nlcerrors)||othererrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MINNS OPTIMIZATION");
|
|
Print("TESTS:");
|
|
PrintResult("* UNCONSTRAINED",!ucerrors);
|
|
PrintResult("* BOUND CONSTRAINED",!bcerrors);
|
|
PrintResult("* LINEARLY CONSTRAINED",!lcerrors);
|
|
PrintResult("* NONLINEARLY CONSTRAINED",!nlcerrors);
|
|
PrintResult("* OTHER PROPERTIES",!othererrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic unconstrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest0UC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
int n=5;
|
|
int passcount=10;
|
|
int i=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble d;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
double sumits=0;
|
|
double sumnfev=0;
|
|
int pass=0;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,2*CMath::RandomReal()-1));
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
s.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_fi.Add(0,d[i]*MathAbs(s.m_x[i]));
|
|
s.m_j.Set(0,i,d[i]*MathSign(s.m_x[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:143");
|
|
if(errors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || MathAbs(x1[i])>0.001,"testminnsunit.ap:147");
|
|
sumits+=(double)rep.m_iterationscount/(double)passcount;
|
|
sumnfev+=(double)rep.m_nfev/(double)passcount;
|
|
}
|
|
}
|
|
//+--------------------------------------------------------------------+
|
|
//| Basic unconstrained test: nonsmooth Rosenbrock posed as |
|
|
//| unconstrained problem. |
|
|
//| [ ]|
|
|
//|min[10*|x0^2-x1|+(1-x0)^2+100*max(sqrt(2)*x0-1,0)+100*max(2*x1-1,0)]|
|
|
//| [ ]|
|
|
//| It's exact solution is x0=1/sqrt(2), x1=1/2 |
|
|
//+--------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest1UC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
int n=2;
|
|
double v0=0;
|
|
double v1=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
v0=s.m_x[0];
|
|
v1=s.m_x[1];
|
|
s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1));
|
|
s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1));
|
|
s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1);
|
|
if((MathSqrt(2.0)*v0-1.0)>0.0)
|
|
{
|
|
s.m_fi.Add(0,100*(MathSqrt(2)*v0-1));
|
|
s.m_j.Add(0,0,100*MathSqrt(2));
|
|
}
|
|
if((2.0*v1-1.0)>0.0)
|
|
{
|
|
s.m_fi.Add(0,100.0*(2.0*v1-1.0));
|
|
s.m_j.Add(0,1,100.0*2.0);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:204");
|
|
if(errors)
|
|
return;
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (MathAbs(x1[0]-1.0/MathSqrt(2.0)))>0.001,"testminnsunit.ap:207");
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:208");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic box constrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest0BC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
int n=5;
|
|
int passcount=10;
|
|
int pass=0;
|
|
int i=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble d;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
double sumits=0;
|
|
double sumnfev=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,2*CMath::RandomReal()-1));
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
bl.Set(i,MathMin(v0,v1));
|
|
bu.Set(i,MathMax(v0,v1));
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
|
|
CMinNS::MinNSSetBC(s,bl,bu);
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
s.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_fi.Add(0,d[i]*MathAbs(s.m_x[i]));
|
|
s.m_j.Set(0,i,d[i]*MathSign(s.m_x[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:263");
|
|
if(errors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-CApServ::BoundVal(0.0,bl[i],bu[i])))>0.001,"testminnsunit.ap:267");
|
|
sumits+=(double)rep.m_iterationscount/(double)passcount;
|
|
sumnfev+=(double)rep.m_nfev/(double)passcount;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic constrained test: nonsmooth Rosenbrock posed as box |
|
|
//| constrained problem. |
|
|
//| [ ] |
|
|
//| minimize [ 10*|x0^2-x1| + (1-x0)^2 ] |
|
|
//| [ ] |
|
|
//| s.t. x0<=1/sqrt(2), x1<=0.5 |
|
|
//| It's exact solution is x0=1/sqrt(2), x1=1/2 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest1BC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
int n=2;
|
|
double v0=0;
|
|
double v1=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Full(n,AL_NEGINF);
|
|
bndu=vector<double>::Zeros(n);
|
|
bndu.Set(0,1.0/MathSqrt(2));
|
|
bndu.Set(1,0.5);
|
|
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetBC(s,bndl,bndu);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
v0=s.m_x[0];
|
|
v1=s.m_x[1];
|
|
s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1));
|
|
s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1));
|
|
s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:323");
|
|
if(errors)
|
|
return;
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (double)(MathAbs(x1[0]-1/MathSqrt(2)))>0.001,"testminnsunit.ap:326");
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (double)(MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:327");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic linearly constrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest0LC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
double d=-10.0;
|
|
int n=5;
|
|
int passcount=10;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CMatrixDouble c;
|
|
CRowInt ct;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
double sumits=0;
|
|
double sumnfev=0;
|
|
int nc=0;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
nc=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
if(CMath::RandomReal()<0.5)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
c.Set(nc,j,0.0);
|
|
c.Set(nc,i,1.0+CMath::RandomReal());
|
|
ct.Set(nc,0);
|
|
nc++;
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
{
|
|
c.Set(nc+0,j,0.0);
|
|
c.Set(nc+1,j,0.0);
|
|
}
|
|
c.Set(nc+0,i,1.0+CMath::RandomReal());
|
|
c.Set(nc+1,i,1.0+CMath::RandomReal());
|
|
ct.Set(nc+0,1);
|
|
ct.Set(nc+1,-1);
|
|
nc+=2;
|
|
}
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
|
|
CMinNS::MinNSSetLC(s,c,ct,nc);
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
s.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.m_fi.Set(0,d*CMath::Sqr(s.m_x[i]));
|
|
s.m_j.Set(0,i,d*2*s.m_x[i]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:404");
|
|
if(errors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || MathAbs(x1[i])>0.001,"testminnsunit.ap:408");
|
|
sumits+=(double)rep.m_iterationscount/(double)passcount;
|
|
sumnfev+=(double)rep.m_nfev/(double)passcount;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic constrained test: nonsmooth Rosenbrock posed as linearly |
|
|
//| constrained problem. |
|
|
//| [ ] |
|
|
//| minimize [ 10*|x0^2-x1| + (1-x0)^2 ] |
|
|
//| [ ] |
|
|
//| s.t. x0<=1/sqrt(2), x1<=0.5 |
|
|
//| It's exact solution is x0=1/sqrt(2), x1=1/2 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest1LC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
int n=2;
|
|
double v0=0;
|
|
double v1=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CMatrixDouble c;
|
|
CRowInt ct;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2,n+1);
|
|
ct.Resize(2);
|
|
c.Set(0,0,1.0);
|
|
c.Set(0,2,1/MathSqrt(2));
|
|
c.Set(1,1,1.0);
|
|
c.Set(1,2,0.5);
|
|
ct.Set(0,-1);
|
|
ct.Set(1,-1);
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetLC(s,c,ct,2);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
|
|
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
v0=s.m_x[0];
|
|
v1=s.m_x[1];
|
|
s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1));
|
|
s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1));
|
|
s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:470");
|
|
if(errors)
|
|
return;
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (double)(MathAbs(x1[0]-1/MathSqrt(2)))>0.001,"testminnsunit.ap:473");
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (double)(MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:474");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic nonlinearly constrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::BasicTest0NLC(bool &errors)
|
|
{
|
|
//--- create variables
|
|
double d=-10.0;
|
|
int n=5;
|
|
int passcount=10;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CMatrixDouble ec;
|
|
CMatrixDouble ic;
|
|
int nec=0;
|
|
int nic=0;
|
|
CMinNSState s;
|
|
CMinNSReport rep;
|
|
double sumits=0;
|
|
double sumnfev=0;
|
|
|
|
x0=vector<double>::Zeros(n);
|
|
ec=matrix<double>::Zeros(2*n,n+1);
|
|
ic=matrix<double>::Zeros(2*n,n+1);
|
|
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
nec=0;
|
|
nic=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
if(CMath::RandomReal()<0.5)
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
ec.Set(nec,j,0.0);
|
|
ec.Set(nec,i,1.0+CMath::RandomReal());
|
|
nec++;
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<=n; j++)
|
|
{
|
|
ic.Set(nic+0,j,0.0);
|
|
ic.Set(nic+1,j,0.0);
|
|
}
|
|
ic.Set(nic+0,i,1.0+CMath::RandomReal());
|
|
ic.Set(nic+1,i,-1.0-CMath::RandomReal());
|
|
nic+=2;
|
|
}
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,s);
|
|
CMinNS::MinNSSetAlgoAGS(s,0.1,100.0);
|
|
CMinNS::MinNSSetNLC(s,nec,nic);
|
|
while(CMinNS::MinNSIteration(s))
|
|
{
|
|
if(s.m_needfij)
|
|
{
|
|
s.m_fi.Set(0,0.0);
|
|
for(j=0; j<n; j++)
|
|
{
|
|
s.m_fi.Set(0,d*CMath::Sqr(s.m_x[j]));
|
|
s.m_j.Set(0,j,d*2*s.m_x[j]);
|
|
}
|
|
for(i=0; i<nec ; i++)
|
|
{
|
|
s.m_fi.Set(1+i,-ec.Get(i,n));
|
|
for(j=0; j<n; j++)
|
|
{
|
|
s.m_fi.Add(1+i,s.m_x[j]*ec.Get(i,j));
|
|
s.m_j.Set(1+i,j,ec.Get(i,j));
|
|
}
|
|
}
|
|
for(i=0; i<nic ; i++)
|
|
{
|
|
s.m_fi.Set(1+nec+i,-ic.Get(i,n));
|
|
for(j=0; j<n; j++)
|
|
{
|
|
s.m_fi.Add(1+nec+i,s.m_x[j]*ic.Get(i,j));
|
|
s.m_j.Set(1+nec+i,j,ic.Get(i,j));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
errors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(s,x1,rep);
|
|
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:567");
|
|
if(errors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || MathAbs(x1[i])>0.001,"testminnsunit.ap:571");
|
|
sumits+=(double)rep.m_iterationscount/(double)passcount;
|
|
sumnfev+=(double)rep.m_nfev/(double)passcount;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unconstrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::TestUC(bool &primaryerrors,bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
CRowDouble x0;
|
|
CRowDouble x0s;
|
|
CRowDouble x1;
|
|
CRowDouble x1s;
|
|
CRowDouble d;
|
|
CRowDouble xc;
|
|
CRowDouble s;
|
|
CRowDouble xrfirst;
|
|
CRowDouble xrlast;
|
|
CMinNSState state;
|
|
CMinNSReport rep;
|
|
double v=0;
|
|
int pass=0;
|
|
bool requirexrep;
|
|
double epsrad=0;
|
|
bool werexreports;
|
|
double repferr=0;
|
|
double xtol=0;
|
|
int i_=0;
|
|
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- First test:
|
|
//--- * test that problem is successfully solved
|
|
//--- * test that X-reports are performed correctly - present
|
|
//--- when requested, return first and last points correctly,
|
|
//--- not present by default, function value is reported
|
|
//--- correctly.
|
|
//--- * we use non-unit scale, randomly chosen one, which results
|
|
//--- in badly conditioned problems (to check robustness)
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrfirst=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,10*(2*CMath::RandomReal()-1));
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
}
|
|
requirexrep=CMath::RandomReal()>0.5;
|
|
xtol=0.01*MathPow(10,-(1*CMath::RandomReal()));
|
|
epsrad=xtol/100;
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
if(requirexrep)
|
|
CMinNS::MinNSSetXRep(state,true);
|
|
werexreports=false;
|
|
repferr=0.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
if(!werexreports)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
xrfirst.Set(i_,state.m_x[i_]);
|
|
}
|
|
for(i_=0; i_<n; i_++)
|
|
xrlast.Set(i_,state.m_x[i_]);
|
|
werexreports=true;
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
|
|
repferr=MathMax(repferr,MathAbs(v-state.m_f));
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:668");
|
|
CAp::SetErrorFlag(othererrors,werexreports && !requirexrep,"testminnsunit.ap:669");
|
|
CAp::SetErrorFlag(othererrors,requirexrep && !werexreports,"testminnsunit.ap:670");
|
|
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:671");
|
|
if(primaryerrors||othererrors)
|
|
return;
|
|
//---
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-xc[i])/s[i])>xtol,"testminnsunit.ap:676");
|
|
if(requirexrep)
|
|
{
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrfirst[i]) || (double)(MathAbs(x0[i]-xrfirst[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:679");
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1[i]-xrlast[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:680");
|
|
}
|
|
}
|
|
//--- Test numerical differentiation:
|
|
//--- * test that problem is successfully solved
|
|
//--- * test that correct function value is reported
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,10*(2*CMath::RandomReal()-1));
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
}
|
|
xtol=0.01*MathPow(10,-(1*CMath::RandomReal()));
|
|
epsrad=xtol/100;
|
|
CMinNS::MinNSCreateF(n,x0,epsrad/100,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetXRep(state,true);
|
|
repferr=0.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
|
|
repferr=MathMax(repferr,MathAbs(v-state.m_f));
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:731");
|
|
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:732");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-xc[i])/s[i])>xtol,"testminnsunit.ap:736");
|
|
//--- Test scaling: we perform several steps on unit-scale problem,
|
|
//--- then we perform same amount of steps on re-scaled problem,
|
|
//--- starting from same point (but scaled according to chosen scale).
|
|
//--- Correctly written optimizer should perform essentially same steps
|
|
//--- (up to scale) on both problems. At least, it holds within first
|
|
//--- several steps, before rounding errors start to accumulate.
|
|
//--- NOTE: we also check that correctly scaled points are reported.
|
|
//--- And, as side effect, we check MinNSRestartFrom().
|
|
//--- NOTE: we use moderate scale and diagonal coefficients in order
|
|
//--- to have well-conditioned system. We test correctness of
|
|
//--- formulae here, not robustness of algorithm.
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
x0s=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(10,2.0*CMath::RandomReal()-1.0));
|
|
d.Set(i,MathPow(10,2.0*CMath::RandomReal()-1.0));
|
|
x0.Set(i,2.0*CMath::RandomReal()-1.0);
|
|
xc.Set(i,2.0*CMath::RandomReal()-1.0);
|
|
}
|
|
x0s=x0*s+0;
|
|
//---
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
|
|
CMinNS::MinNSSetXRep(state,false);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:788");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetXRep(state,true);
|
|
CMinNS::MinNSRestartFrom(state,x0s);
|
|
werexreports=false;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
xrlast.Set(i_,state.m_x[i_]);
|
|
werexreports=true;
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1s,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:818");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i]) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(1.0E-4)),"testminnsunit.ap:823");
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1s[i]-xrlast[i]))>m_scalingtesttol,"testminnsunit.ap:827");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Box constrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::TestBC(bool &primaryerrors,bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int passcount=10;
|
|
int maxn=5;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CRowDouble x0;
|
|
CRowDouble x0s;
|
|
CRowDouble x1;
|
|
CRowDouble x1s;
|
|
CRowDouble b;
|
|
CRowDouble d;
|
|
CRowDouble xc;
|
|
CRowDouble s;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble scaledbndl;
|
|
CRowDouble scaledbndu;
|
|
CRowDouble xrfirst;
|
|
CRowDouble xrlast;
|
|
CMatrixDouble a;
|
|
CMinNSState state;
|
|
CMinNSReport rep;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
int pass=0;
|
|
bool requirexrep;
|
|
double epsrad=0;
|
|
bool werexreports;
|
|
double repferr=0;
|
|
double xtol=0;
|
|
double conda=0;
|
|
double gnorm=0;
|
|
int i_=0;
|
|
//--- First test:
|
|
//--- * sparse function
|
|
//--- * test that problem is successfully solved
|
|
//--- * non-unit scale is used, which results in badly conditioned problem
|
|
//--- * check that all iterates are feasible (box-constrained)
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrfirst=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,10*(2*CMath::RandomReal()-1));
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
k=CMath::RandomInteger(5);
|
|
if(k==1)
|
|
bndl.Set(i,2*CMath::RandomReal()-1);
|
|
if(k==2)
|
|
bndu.Set(i,2*CMath::RandomReal()-1);
|
|
if(k==3)
|
|
{
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
bndl.Set(i,MathMin(v0,v1));
|
|
bndu.Set(i,MathMax(v0,v1));
|
|
}
|
|
if(k==4)
|
|
{
|
|
bndl.Set(i,2*CMath::RandomReal()-1);
|
|
bndu.Set(i,bndl[i]);
|
|
}
|
|
}
|
|
requirexrep=CMath::RandomReal()>0.5;
|
|
xtol=0.01*MathPow(10,-(1*CMath::RandomReal()));
|
|
epsrad=xtol/100;
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetBC(state,bndl,bndu);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
if(requirexrep)
|
|
CMinNS::MinNSSetXRep(state,true);
|
|
werexreports=false;
|
|
repferr=0.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
if(!werexreports)
|
|
{
|
|
for(i_=0; i_<n; i_++)
|
|
xrfirst.Set(i_,state.m_x[i_]);
|
|
}
|
|
for(i_=0; i_<n; i_++)
|
|
xrlast.Set(i_,state.m_x[i_]);
|
|
werexreports=true;
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
|
|
repferr=MathMax(repferr,MathAbs(v-state.m_f));
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(primaryerrors,state.m_x[i]<bndl[i],"testminnsunit.ap:944");
|
|
CAp::SetErrorFlag(primaryerrors,state.m_x[i]>bndu[i],"testminnsunit.ap:945");
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:953");
|
|
CAp::SetErrorFlag(othererrors,werexreports && !requirexrep,"testminnsunit.ap:954");
|
|
CAp::SetErrorFlag(othererrors,requirexrep && !werexreports,"testminnsunit.ap:955");
|
|
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:956");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-CApServ::BoundVal(xc[i],bndl[i],bndu[i]))/s[i])>xtol,"testminnsunit.ap:961");
|
|
CAp::SetErrorFlag(primaryerrors,x1[i]<bndl[i],"testminnsunit.ap:962");
|
|
CAp::SetErrorFlag(primaryerrors,x1[i]>bndu[i],"testminnsunit.ap:963");
|
|
if(requirexrep)
|
|
{
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrfirst[i]) || (double)(MathAbs(CApServ::BoundVal(x0[i],bndl[i],bndu[i])-xrfirst[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:966");
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1[i]-xrlast[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:967");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- A bit harder test:
|
|
//--- * dense quadratic function (smooth), may be prone to different
|
|
//--- rounding-related issues
|
|
//--- * non-negativity box constraints
|
|
//--- * unit scale is used
|
|
//--- * extreme stopping criteria (EpsX=1.0E-12)
|
|
//--- * single pass for each problem size
|
|
//--- * check that constrained gradient at solution is small
|
|
conda=1.0E3;
|
|
epsrad=1.0E-12;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
b=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,1.0);
|
|
b.Set(i,CMath::RandomReal()-0.5);
|
|
bndl.Set(i,0.0);
|
|
bndu.Set(i,AL_POSINF);
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,conda,a);
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetBC(state,bndl,bndu);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
state.m_j.Set(0,i,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,b[i]*state.m_x[i]);
|
|
for(j=0; j<n; j++)
|
|
state.m_fi.Add(0,0.5*state.m_x[i]*a.Get(i,j)*state.m_x[j]);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
state.m_j.Add(0,i,b[i]+CAblasF::RDotVR(n,state.m_x,a,i));
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1030");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
gnorm=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=b[i]+CAblasF::RDotVR(n,x1,a,i);
|
|
if(x1[i]==bndl[i] && v>0.0)
|
|
v=0;
|
|
if(x1[i]==bndu[i] && v<0.0)
|
|
v=0;
|
|
gnorm+=CMath::Sqr(v);
|
|
CAp::SetErrorFlag(primaryerrors,x1[i]<bndl[i],"testminnsunit.ap:1045");
|
|
CAp::SetErrorFlag(primaryerrors,x1[i]>bndu[i],"testminnsunit.ap:1046");
|
|
}
|
|
gnorm=MathSqrt(gnorm);
|
|
CAp::SetErrorFlag(primaryerrors,gnorm>(1.0E-4),"testminnsunit.ap:1049");
|
|
}
|
|
//--- Test on HIGHLY nonconvex bound constrained problem.
|
|
//--- Algorithm should be able to Stop.
|
|
//--- NOTE: because algorithm can be attracted to saddle points,
|
|
//--- x[i] may be -1, +1 or approximately zero.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CMath::RandomReal()-0.5);
|
|
bndl.Set(i,-1.0);
|
|
bndu.Set(i,1.0);
|
|
}
|
|
epsrad=0.0001;
|
|
xtol=15.0*epsrad;
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetBC(state,bndl,bndu);
|
|
v=-1000.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v0=MathAbs(state.m_x[i]);
|
|
v1=MathSign(state.m_x[i]);
|
|
state.m_fi.Add(0,v*(v0+v0*v0));
|
|
state.m_j.Set(0,i,v*(v1+2*v0*v1));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1095");
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=MathAbs(x1[i]);
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1099");
|
|
CAp::SetErrorFlag(primaryerrors,v!=1.0 && v>xtol,"testminnsunit.ap:1100");
|
|
}
|
|
}
|
|
}
|
|
//--- Test numerical differentiation:
|
|
//--- * test that problem is successfully solved
|
|
//--- * test that correct function value is reported
|
|
//--- * test that all iterates are within bound-constrained area
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,10*(2*CMath::RandomReal()-1));
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
k=CMath::RandomInteger(5);
|
|
if(k==1)
|
|
bndl.Set(i,2*CMath::RandomReal()-1);
|
|
if(k==2)
|
|
bndu.Set(i,2*CMath::RandomReal()-1);
|
|
if(k==3)
|
|
{
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
bndl.Set(i,MathMin(v0,v1));
|
|
bndu.Set(i,MathMax(v0,v1));
|
|
}
|
|
if(k==4)
|
|
{
|
|
bndl.Set(i,2*CMath::RandomReal()-1);
|
|
bndu.Set(i,bndl[i]);
|
|
}
|
|
}
|
|
xtol=0.01*MathPow(10,-(2*CMath::RandomReal()));
|
|
epsrad=xtol/100;
|
|
CMinNS::MinNSCreateF(n,x0,epsrad/100,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetBC(state,bndl,bndu);
|
|
CMinNS::MinNSSetXRep(state,true);
|
|
repferr=0.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
CAp::SetErrorFlag(primaryerrors,state.m_x[i]<bndl[i],"testminnsunit.ap:1164");
|
|
CAp::SetErrorFlag(primaryerrors,state.m_x[i]>bndu[i],"testminnsunit.ap:1165");
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
|
|
CAp::SetErrorFlag(primaryerrors,state.m_x[i]<bndl[i],"testminnsunit.ap:1176");
|
|
CAp::SetErrorFlag(primaryerrors,state.m_x[i]>bndu[i],"testminnsunit.ap:1177");
|
|
}
|
|
repferr=MathMax(repferr,MathAbs(v-state.m_f));
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1186");
|
|
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:1187");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-CApServ::BoundVal(xc[i],bndl[i],bndu[i]))/s[i])>xtol,"testminnsunit.ap:1191");
|
|
}
|
|
}
|
|
//--- Test scaling: we perform several steps on unit-scale problem,
|
|
//--- then we perform same amount of steps on re-scaled problem,
|
|
//--- starting from same point (but scaled according to chosen scale).
|
|
//--- Correctly written optimizer should perform essentially same steps
|
|
//--- (up to scale) on both problems. At least, it holds within first
|
|
//--- several steps, before rounding errors start to accumulate.
|
|
//--- NOTE: we also check that correctly scaled points are reported.
|
|
//--- And, as side effect, we check MinNSRestartFrom().
|
|
//--- NOTE: we use very low scale and diagonal coefficients in order
|
|
//--- to have well-conditioned system. We test correctness of
|
|
//--- formulae here, not robustness of algorithm.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
x0s=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
scaledbndl=vector<double>::Zeros(n);
|
|
scaledbndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(2,CMath::RandomInteger(5)-2));
|
|
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
x0s.Set(i,x0[i]*s[i]);
|
|
bndl.Set(i,AL_NEGINF);
|
|
bndu.Set(i,AL_POSINF);
|
|
k=CMath::RandomInteger(5);
|
|
if(k==1)
|
|
bndl.Set(i,2*CMath::RandomReal()-1);
|
|
if(k==2)
|
|
bndu.Set(i,2*CMath::RandomReal()-1);
|
|
if(k==3)
|
|
{
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
bndl.Set(i,MathMin(v0,v1));
|
|
bndu.Set(i,MathMax(v0,v1));
|
|
}
|
|
if(k==4)
|
|
{
|
|
bndl.Set(i,2*CMath::RandomReal()-1);
|
|
bndu.Set(i,bndl[i]);
|
|
}
|
|
scaledbndl.Set(i,bndl[i]*s[i]);
|
|
scaledbndu.Set(i,bndu[i]*s[i]);
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.01,0.0);
|
|
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
|
|
CMinNS::MinNSSetBC(state,bndl,bndu);
|
|
CMinNS::MinNSSetXRep(state,false);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1272");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetBC(state,scaledbndl,scaledbndu);
|
|
CMinNS::MinNSRestartFrom(state,x0s);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1s,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1293");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i])) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(m_scalingtesttol),"testminnsunit.ap:1297");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Linearly constrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::TestLC(bool &primaryerrors,bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int nc=0;
|
|
CRowDouble x0;
|
|
CRowDouble x0s;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble x1s;
|
|
CRowDouble d;
|
|
CRowDouble xc;
|
|
CRowDouble s;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CMatrixDouble c;
|
|
CMatrixDouble scaledc;
|
|
CRowInt ct;
|
|
CRowDouble scaledbndl;
|
|
CRowDouble scaledbndu;
|
|
CRowDouble xrfirst;
|
|
CRowDouble xrlast;
|
|
CMinNSState state;
|
|
CMinNSReport rep;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double vv=0;
|
|
double flast0=0;
|
|
double flast1=0;
|
|
int pass=0;
|
|
double epsrad=0;
|
|
double repferr=0;
|
|
double xtol=0;
|
|
double ftol=0;
|
|
double rho=0;
|
|
int i_=0;
|
|
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
for(n=1; n<=5; n++)
|
|
{
|
|
//--- First test:
|
|
//--- * smooth problem
|
|
//--- * subject to random linear constraints
|
|
//--- * with non-unit scale
|
|
//--- We:
|
|
//--- * compare function value at constrained solution with function
|
|
//--- value for penalized unconstrained problem. We do not compare
|
|
//--- actual X-values returned, because they are highly unstable -
|
|
//--- function values at minimum show better stability.
|
|
//--- * check that correct function values are reported
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,10*(2*CMath::RandomReal()-1));
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,1+CMath::RandomReal());
|
|
s.Set(i,1+CMath::RandomReal());
|
|
}
|
|
nc=CMath::RandomInteger((n+1)/2);
|
|
if(nc>0)
|
|
{
|
|
c=matrix<double>::Zeros(nc,n+1);
|
|
ct.Resize(nc);
|
|
for(i=0; i<nc; i++)
|
|
{
|
|
ct.Set(i,CMath::RandomInteger(3)-1);
|
|
for(j=0; j<=n; j++)
|
|
c.Set(i,j,CMath::RandomReal()-0.5);
|
|
}
|
|
}
|
|
epsrad=0.00001;
|
|
ftol=0.01;
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetXRep(state,true);
|
|
CMinNS::MinNSSetLC(state,c,ct,nc);
|
|
repferr=0.0;
|
|
flast0=AL_NaN;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*CMath::Sqr(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*(2*(state.m_x[i]-xc[i])));
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
flast0=0.0;
|
|
for(i=0; i<n; i++)
|
|
flast0+=d[i]*CMath::Sqr(state.m_x[i]-xc[i]);
|
|
repferr=MathMax(repferr,MathAbs(flast0-state.m_f));
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1402");
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(flast0),"testminnsunit.ap:1403");
|
|
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:1404");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
CMinNS::MinNSSetLC(state,c,ct,0);
|
|
CMinNS::MinNSRestartFrom(state,x0);
|
|
rho=10000.0;
|
|
repferr=0.0;
|
|
flast1=AL_NaN;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*CMath::Sqr(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*(2*(state.m_x[i]-xc[i])));
|
|
}
|
|
for(i=0; i<nc; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,c,i);
|
|
v-=c.Get(i,n);
|
|
vv=0.0;
|
|
if(ct[i]<0)
|
|
{
|
|
vv=MathSign(MathMax(v,0.0));
|
|
v=MathMax(v,0.0);
|
|
}
|
|
if(ct[i]==0)
|
|
{
|
|
vv=MathSign(v);
|
|
v=MathAbs(v);
|
|
}
|
|
if(ct[i]>0)
|
|
{
|
|
vv=-MathSign(MathMax(-v,0.0));
|
|
v=MathMax(-v,0.0);
|
|
}
|
|
state.m_fi.Add(0,rho*v);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Add(0,j,rho*vv*c.Get(i,j));
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
flast1=0.0;
|
|
for(i=0; i<n; i++)
|
|
flast1+=d[i]*CMath::Sqr(state.m_x[i]-xc[i]);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x2,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1462");
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(flast1),"testminnsunit.ap:1463");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
CAp::SetErrorFlag(primaryerrors,(double)(MathAbs(flast0-flast1))>ftol,"testminnsunit.ap:1466");
|
|
//--- Test on HIGHLY nonconvex linearly constrained problem.
|
|
//--- Algorithm should be able to Stop at the bounds.
|
|
x0=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,CMath::RandomReal()-0.5);
|
|
c.Set(2*i+0,i,1.0);
|
|
c.Set(2*i+0,n,-1.0);
|
|
ct.Set(2*i+0,1);
|
|
c.Set(2*i+1,i,1.0);
|
|
c.Set(2*i+1,n,1.0);
|
|
ct.Set(2*i+1,-1);
|
|
}
|
|
epsrad=0.0001;
|
|
xtol=15.0*epsrad;
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetLC(state,c,ct,2*n);
|
|
v=-1000.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v0=MathAbs(state.m_x[i]);
|
|
v1=MathSign(state.m_x[i]);
|
|
state.m_fi.Add(0,v*(v0+v0*v0));
|
|
state.m_j.Set(0,i,v*(v1+2*v0*v1));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1516");
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1519");
|
|
CAp::SetErrorFlag(primaryerrors,((double)(MathAbs(x1[i]-1))>xtol && MathAbs(x1[i])>xtol) && (double)(MathAbs(x1[i]+1))>xtol,"testminnsunit.ap:1520");
|
|
}
|
|
//--- Test scaling: we perform several steps on unit-scale problem,
|
|
//--- then we perform same amount of steps on re-scaled problem,
|
|
//--- starting from same point (but scaled according to chosen scale).
|
|
//--- Correctly written optimizer should perform essentially same steps
|
|
//--- (up to scale) on both problems. At least, it holds within first
|
|
//--- several steps, before rounding errors start to accumulate.
|
|
//--- NOTE: we also check that correctly scaled points are reported.
|
|
//--- And, as side effect, we check MinNSRestartFrom().
|
|
//--- NOTE: we use moderate scale and diagonal coefficients in order
|
|
//--- to have well-conditioned system. We test correctness of
|
|
//--- formulae here, not robustness of algorithm.
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
x0s=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
xrlast=vector<double>::Zeros(n);
|
|
c=matrix<double>::Zeros(2*n,n+1);
|
|
scaledc=matrix<double>::Zeros(2*n,n+1);
|
|
ct.Resize(2*n);
|
|
ct.Fill(0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(10,2*CMath::RandomReal()-1));
|
|
d.Set(i,MathPow(10,2*CMath::RandomReal()-1));
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
x0s.Set(i,x0[i]*s[i]);
|
|
k=CMath::RandomInteger(5);
|
|
if(k==1)
|
|
{
|
|
c.Set(2*i+0,i,1.0);
|
|
c.Set(2*i+0,n,2*CMath::RandomReal()-1);
|
|
ct.Set(2*i+0,1);
|
|
}
|
|
if(k==2)
|
|
{
|
|
c.Set(2*i+0,i,1.0);
|
|
c.Set(2*i+0,n,2*CMath::RandomReal()-1);
|
|
ct.Set(2*i+0,-1);
|
|
}
|
|
if(k==3)
|
|
{
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
c.Set(2*i+0,i,1.0);
|
|
c.Set(2*i+0,n,MathMin(v0,v1));
|
|
c.Set(2*i+1,i,1.0);
|
|
c.Set(2*i+1,n,MathMax(v0,v1));
|
|
ct.Set(2*i+0,1);
|
|
ct.Set(2*i+1,-1);
|
|
}
|
|
if(k==4)
|
|
{
|
|
c.Set(2*i+0,i,1.0);
|
|
c.Set(2*i+0,n,2*CMath::RandomReal()-1);
|
|
ct.Set(2*i+0,0);
|
|
}
|
|
}
|
|
for(i=0; i<=2*n-1; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
scaledc.Set(i,j,c.Get(i,j)/s[j]);
|
|
scaledc.Set(i,n,c.Get(i,n));
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
|
|
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
|
|
CMinNS::MinNSSetLC(state,c,ct,2*n);
|
|
CMinNS::MinNSSetXRep(state,false);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1622");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetLC(state,scaledc,ct,2*n);
|
|
CMinNS::MinNSRestartFrom(state,x0s);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1s,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1643");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i])) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(m_scalingtesttol),"testminnsunit.ap:1647");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinearly constrained test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::TestNLC(bool &primaryerrors,bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int passcount=10;
|
|
int maxn=5;
|
|
double rho=100.0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int nc=0;
|
|
int nec=0;
|
|
CRowDouble x0;
|
|
CRowDouble x0s;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble x1s;
|
|
CRowDouble d;
|
|
CRowDouble xc;
|
|
CRowDouble s;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
CRowDouble b;
|
|
CRowDouble r;
|
|
CMatrixDouble c;
|
|
CMatrixDouble scaledc;
|
|
CRowInt ct;
|
|
CRowDouble scaledbndl;
|
|
CRowDouble scaledbndu;
|
|
CRowDouble xrfirst;
|
|
CRowDouble xrlast;
|
|
CMinNSState state;
|
|
CMinNSReport rep;
|
|
double v=0;
|
|
int pass=0;
|
|
double epsrad=0;
|
|
double xtol=0;
|
|
double diffstep=0;
|
|
//--- First test:
|
|
//--- * simple problem
|
|
//--- * subject to random nonlinear constraints of form r[i]*x[i] OPERATION 0.0,
|
|
//--- where OPERATION is <= or =
|
|
//--- * with non-unit scale
|
|
//--- We:
|
|
//--- * compare numerical solution with analytic one, which can be
|
|
//--- easily calculated
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(nc=1; nc<=n; nc++)
|
|
{
|
|
for(nec=0; nec<=nc; nec++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
r=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
s.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
r.Set(i,(2*CMath::RandomInteger(2)-1)*(0.1+CMath::RandomReal()));
|
|
}
|
|
xtol=0.01;
|
|
epsrad=xtol/100;
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,rho);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetNLC(state,nec,nc-nec);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
for(i=1; i<=nc; i++)
|
|
{
|
|
state.m_fi.Set(i,state.m_x[i-1]*r[i-1]);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(i,j,0.0);
|
|
state.m_j.Set(i,i-1,r[i-1]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1743");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=xc[i];
|
|
if(i<nec)
|
|
v=0.0;
|
|
if(i>=nec && i<nc)
|
|
{
|
|
if(r[i]>0.0)
|
|
v=MathMin(v,0.0);
|
|
if(r[i]<0.0)
|
|
v=MathMax(v,0.0);
|
|
}
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1762");
|
|
CAp::SetErrorFlag(primaryerrors,MathAbs(x1[i]-v)>(double)(xtol*s[i]),"testminnsunit.ap:1763");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Numerical differentiation test.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(nc=1; nc<=n; nc++)
|
|
{
|
|
for(nec=0; nec<=nc; nec++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
r=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
s.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
r.Set(i,(2*CMath::RandomInteger(2)-1)*(0.1+CMath::RandomReal()));
|
|
}
|
|
xtol=0.01;
|
|
epsrad=xtol/100;
|
|
diffstep=epsrad/10;
|
|
CMinNS::MinNSCreateF(n,x0,diffstep,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,rho);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSSetNLC(state,nec,nc-nec);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
for(i=1; i<=nc; i++)
|
|
state.m_fi.Set(i,state.m_x[i-1]*r[i-1]);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1813");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=xc[i];
|
|
if(i<nec)
|
|
v=0.0;
|
|
if(i>=nec && i<nc)
|
|
{
|
|
if(r[i]>0.0)
|
|
v=MathMin(v,0.0);
|
|
if(r[i]<0.0)
|
|
v=MathMax(v,0.0);
|
|
}
|
|
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1832");
|
|
CAp::SetErrorFlag(primaryerrors,MathAbs(x1[i]-v)>(xtol*s[i]),"testminnsunit.ap:1833");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test scaling: we perform several steps on unit-scale problem,
|
|
//--- then we perform same amount of steps on re-scaled problem,
|
|
//--- starting from same point (but scaled according to chosen scale).
|
|
//--- Correctly written optimizer should perform essentially same steps
|
|
//--- (up to scale) on both problems. At least, it holds within first
|
|
//--- several steps, before rounding errors start to accumulate.
|
|
//--- NOTE: we use moderate scale and diagonal coefficients in order
|
|
//--- to have well-conditioned system. We test correctness of
|
|
//--- formulae here, not robustness of algorithm.
|
|
//--- NOTE: we have to use very relaxed thresholds for this test because
|
|
//--- each AGS iteration involves solution of several nested QP
|
|
//--- subproblems, so rounding errors accumulate too quickly.
|
|
//--- It does not mean that algorithm is inexact, just that two
|
|
//--- almost identical optimization sessions diverge too fast to
|
|
//--- compare them.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(n=1; n<=maxn; n++)
|
|
{
|
|
for(nc=1; nc<=n; nc++)
|
|
{
|
|
for(nec=0; nec<=nc; nec++)
|
|
{
|
|
x0=vector<double>::Zeros(n);
|
|
x0s=vector<double>::Zeros(n);
|
|
xc=vector<double>::Zeros(n);
|
|
d=vector<double>::Zeros(n);
|
|
r=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x0.Set(i,2*CMath::RandomReal()-1);
|
|
xc.Set(i,2*CMath::RandomReal()-1);
|
|
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
s.Set(i,MathPow(10,CMath::RandomReal()-0.5));
|
|
r.Set(i,(2*CMath::RandomInteger(2)-1)*(0.1+CMath::RandomReal()));
|
|
x0s.Set(i,x0[i]*s[i]);
|
|
}
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,1.0);
|
|
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
|
|
CMinNS::MinNSSetNLC(state,nec,nc-nec);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
|
|
}
|
|
for(i=1; i<=nc; i++)
|
|
{
|
|
state.m_fi.Set(i,state.m_x[i-1]*r[i-1]);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(i,j,0.0);
|
|
state.m_j.Set(i,i-1,r[i-1]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1906");
|
|
if(primaryerrors || othererrors)
|
|
return;
|
|
CMinNS::MinNSSetScale(state,s);
|
|
CMinNS::MinNSRestartFrom(state,x0s);
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi.Set(0,0.0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
|
|
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
|
|
}
|
|
for(i=1; i<=nc; i++)
|
|
{
|
|
state.m_fi.Set(i,state.m_x[i-1]/s[i-1]*r[i-1]);
|
|
for(j=0; j<n; j++)
|
|
state.m_j.Set(i,j,0.0);
|
|
state.m_j.Set(i,i-1,r[i-1]/s[i-1]);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1s,rep);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i])) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(m_scalingtesttol),"testminnsunit.ap:1937");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Special tests |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinNSUnit::TestOther(bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int k=0;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CMinNSState state;
|
|
CMinNSReport rep;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v=0;
|
|
double xtol=0;
|
|
double epsrad=0;
|
|
double rho=0;
|
|
//--- First test:
|
|
//--- * 2D problem, minimization of F(x0,x1)=x1
|
|
//--- * two constraints, with wildly different magnitudes
|
|
//--- * G0(x0,x1)=Rho*Abs(x0+x1)=0
|
|
//--- * H0(x0,x1)=Rho*(x0-1000)<=0
|
|
//--- where Rho is some large value
|
|
//--- Optimizer should be able to deal with situation when
|
|
//--- magnitude of Jacobian components is so wildly different.
|
|
n=2;
|
|
x0=vector<double>::Zeros(n);
|
|
x0.Set(0,0.1);
|
|
x0.Set(1,1.0);
|
|
epsrad=0.00001;
|
|
xtol=0.01;
|
|
for(k=0; k<=6; k++)
|
|
{
|
|
rho=MathPow(10,k);
|
|
CMinNS::MinNSCreate(n,x0,state);
|
|
CMinNS::MinNSSetAlgoAGS(state,0.1,10.0);
|
|
CMinNS::MinNSSetCond(state,epsrad,0);
|
|
CMinNS::MinNSSetNLC(state,1,1);
|
|
v=1000.0;
|
|
while(CMinNS::MinNSIteration(state))
|
|
{
|
|
if(state.m_needfij)
|
|
{
|
|
v0=state.m_x[0];
|
|
v1=state.m_x[1];
|
|
state.m_fi.Set(0,v1);
|
|
state.m_j.Set(0,0,0.0);
|
|
state.m_j.Set(0,1,1.0);
|
|
state.m_fi.Set(1,rho*MathAbs(v0+v1));
|
|
state.m_j.Set(1,0,rho*MathSign(v0+v1));
|
|
state.m_j.Set(1,1,rho*MathSign(v0+v1));
|
|
state.m_fi.Set(2,rho*(v0-v));
|
|
state.m_j.Set(2,0,rho);
|
|
state.m_j.Set(2,1,0.0);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
CMinNS::MinNSResults(state,x1,rep);
|
|
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testminnsunit.ap:2003");
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(x1[0]),"testminnsunit.ap:2004");
|
|
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(x1[1]),"testminnsunit.ap:2005");
|
|
CAp::SetErrorFlag(othererrors,MathAbs(x1[0]-v)>xtol,"testminnsunit.ap:2006");
|
|
CAp::SetErrorFlag(othererrors,MathAbs(x1[1]+v)>xtol,"testminnsunit.ap:2007");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinBCUnit
|
|
{
|
|
public:
|
|
static const int m_maxoptguardlevel;
|
|
|
|
static bool TestMinBC(bool silent);
|
|
|
|
private:
|
|
static void CalcIIP2(CMinBCState &state,int n,int fk);
|
|
static void TestFeasibility(bool &feaserr,bool &converr,bool &interr);
|
|
static void TestOther(bool &err);
|
|
static void TestPreconditioning(bool &err);
|
|
static void TestOptGuard(bool &waserrors);
|
|
static void SetRandomPreconditioner(CMinBCState &state,int n,int preckind);
|
|
static void TestOptGuardC1Test0ReportForTask0(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n);
|
|
static void TestOptGuardC1Test1ReportForTask0(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestMinBCUnit::m_maxoptguardlevel=1;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMinBCUnit::TestMinBC(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool feasibilityerrors;
|
|
bool othererrors;
|
|
bool precerrors;
|
|
bool interrors;
|
|
bool converrors;
|
|
bool optguarderrors;
|
|
|
|
waserrors=false;
|
|
feasibilityerrors=false;
|
|
othererrors=false;
|
|
precerrors=false;
|
|
interrors=false;
|
|
converrors=false;
|
|
optguarderrors=false;
|
|
//--- tests
|
|
TestFeasibility(feasibilityerrors,converrors,interrors);
|
|
TestOther(othererrors);
|
|
TestPreconditioning(precerrors);
|
|
TestOptGuard(optguarderrors);
|
|
//--- end
|
|
waserrors=((((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors)||optguarderrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BC OPTIMIZATION");
|
|
PrintResult("FEASIBILITY PROPERTIES",!feasibilityerrors);
|
|
PrintResult("PRECONDITIONING",!precerrors);
|
|
PrintResult("OTHER PROPERTIES",!othererrors);
|
|
PrintResult("CONVERGENCE PROPERTIES",!converrors);
|
|
PrintResult("INTERNAL ERRORS",!interrors);
|
|
PrintResult("OPTGUARD",!optguarderrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::CalcIIP2(CMinBCState &state,int n,int fk)
|
|
{
|
|
if(!state.m_needfg)
|
|
return;
|
|
|
|
for(int i=0; i<n; i++)
|
|
state.m_g.Set(i,MathPow(i*i+1,2*fk)*2*state.m_x[i]);
|
|
state.m_f=CAblasF::RDotV(n,state.m_x,state.m_g)/2.0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function test feasibility properties. |
|
|
//| It launches a sequence of problems and examines their solutions. |
|
|
//| Most of the attention is directed towards feasibility properties,|
|
|
//| although we make some quick checks to ensure that actual solution|
|
|
//| is found. |
|
|
//| On failure sets FeasErr( or ConvErr, depending on failure type) |
|
|
//| to True, or leaves it unchanged otherwise. |
|
|
//| IntErr is set to True on internal errors(errors in the control |
|
|
//| flow). |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::TestFeasibility(bool &feaserr,bool &converr,
|
|
bool &interr)
|
|
{
|
|
//--- create variables
|
|
int nmax=5;
|
|
double weakepsg=1.0E-4;
|
|
int passcount=10;
|
|
int pkind=0;
|
|
int preckind=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int i=0;
|
|
int p=0;
|
|
double v=0;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble x;
|
|
CRowDouble g;
|
|
CRowDouble x0;
|
|
CRowDouble xc;
|
|
CRowDouble xs;
|
|
CRowDouble svdw;
|
|
CMatrixDouble csvdu;
|
|
CMatrixDouble svdvt;
|
|
CMinBCState state;
|
|
CMinBCReport rep;
|
|
int dkind=0;
|
|
double diffstep=0;
|
|
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Another simple problem:
|
|
//--- * bound constraints 0 <= x[i] <= 1
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple boundaries and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1)
|
|
//--- * we also check that both final solution and subsequent iterates
|
|
//--- are strictly feasible
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,0);
|
|
bu.Set(i,1);
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
}
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
if(dkind==1)
|
|
CMinBC::MinBCCreateF(n,x,diffstep,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=MathPow(state.m_x[i]-x0[i],p);
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,p*MathPow(state.m_x[i]-x0[i],p-1));
|
|
feaserr=feaserr||state.m_x[i]<0.0;
|
|
feaserr=feaserr||state.m_x[i]>1.0;
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x[i]>0.0 && x[i]<1.0)
|
|
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
|
|
feaserr=feaserr||x[i]<0.0;
|
|
feaserr=feaserr||x[i]>1.0;
|
|
}
|
|
converr=converr||MathSqrt(v)>(double)(weakepsg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Same as previous problem, but with minor modifications:
|
|
//--- * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple boundaries and function it is easy to find
|
|
//--- analytic form of solution: S[i] = bound(x0[i], 0, 1)
|
|
//--- * we also check that both final solution and subsequent iterates
|
|
//--- are strictly feasible
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
bl.Set(i,0);
|
|
bu.Set(i,1);
|
|
}
|
|
else
|
|
{
|
|
bl.Set(i,CMath::RandomReal());
|
|
bu.Set(i,bl[i]);
|
|
}
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
}
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
if(dkind==1)
|
|
CMinBC::MinBCCreateF(n,x,diffstep,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=MathPow(state.m_x[i]-x0[i],p);
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,p*MathPow(state.m_x[i]-x0[i],p-1));
|
|
feaserr=feaserr||state.m_x[i]<(double)(bl[i]);
|
|
feaserr=feaserr||state.m_x[i]>(double)(bu[i]);
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
v=0.0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(x[i]>(double)(bl[i]) && x[i]<(double)(bu[i]))
|
|
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
|
|
feaserr=feaserr||x[i]<(double)(bl[i]);
|
|
feaserr=feaserr||x[i]>(double)(bu[i]);
|
|
}
|
|
converr=converr||MathSqrt(v)>(double)(weakepsg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Infeasible problem:
|
|
//--- * all bound constraints are 0 <= x[i] <= 1 except for one
|
|
//--- * that one is 0 >= x[i] >= 1
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from detecting
|
|
//--- infeasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * algorithm must return correct error code on such problem
|
|
for(preckind=0; preckind<=2; preckind++)
|
|
{
|
|
for(pkind=1; pkind<=2; pkind++)
|
|
{
|
|
for(n=1; n<=nmax; n++)
|
|
{
|
|
//--- Generate X, BL, BU.
|
|
p=2*pkind;
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
x=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
bl.Set(i,0);
|
|
bu.Set(i,1);
|
|
x.Set(i,CMath::RandomReal());
|
|
x0.Set(i,3*CMath::RandomReal()-1);
|
|
}
|
|
i=CMath::RandomInteger(n);
|
|
bl.Set(i,1);
|
|
bu.Set(i,0);
|
|
//--- Create and optimize
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=MathPow(state.m_x[i]-x0[i],p);
|
|
state.m_g.Set(i,p*MathPow(state.m_x[i]-x0[i],p-1));
|
|
}
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
feaserr=feaserr||rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function additional properties. |
|
|
//| On failure sets Err to True(leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble x;
|
|
CRowDouble xf;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble b;
|
|
CRowDouble xlast;
|
|
CRowDouble a;
|
|
CRowDouble s;
|
|
CRowDouble h;
|
|
CMatrixDouble fulla;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double stpmax=0;
|
|
double v=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
CMinBCState state;
|
|
double epsx=0;
|
|
double epsg=0;
|
|
double eps=0;
|
|
double tmpeps=0;
|
|
CMinBCReport rep;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
double r=0;
|
|
CHighQualityRandState rs;
|
|
int spoiliteration=0;
|
|
int stopiteration=0;
|
|
int spoilvar=0;
|
|
double spoilval=0;
|
|
double ss=0;
|
|
int stopcallidx=0;
|
|
int callidx=0;
|
|
int maxits=0;
|
|
bool terminationrequested;
|
|
int i_=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
epsx=1.0E-4;
|
|
epsg=1.0E-8;
|
|
passcount=10;
|
|
//--- Try to reproduce bug 570 (optimizer hangs on problems where it is required
|
|
//--- to perform very small step - less than 1E-50 - in order to activate constraints).
|
|
//--- The problem being solved is:
|
|
//--- min x[0]+x[1]+...+x[n-1]
|
|
//--- subject to
|
|
//--- x[i]>=0, for i=0..m_n-1
|
|
//--- with initial point
|
|
//--- x[0] = 1.0E-100, x[1]=x[2]=...=0.5
|
|
//--- We try to reproduce this problem in different settings:
|
|
//--- * boundary-only constraints - we test that completion code is positive,
|
|
//--- and all x[] are EXACTLY zero
|
|
//--- * boundary constraints posed as general linear ones - we test that
|
|
//--- completion code is positive, and all x[] are APPROXIMATELY zero.
|
|
n=10;
|
|
x=vector<double>::Full(n,0.5);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Full(n,AL_POSINF);
|
|
x.Set(0,1.0E-100);
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,0,0,0,2*n);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=state.m_x[i];
|
|
state.m_g.Set(i,1.0);
|
|
}
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,xf,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminbcunit.ap:494");
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,xf[i]!=0.0,"testminbcunit.ap:497");
|
|
}
|
|
//--- Test reports:
|
|
//--- * first value must be starting point
|
|
//--- * last value must be last point
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
n=50;
|
|
x=vector<double>::Full(n,10);
|
|
xlast=vector<double>::Zeros(n);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Full(n,AL_POSINF);
|
|
for(i=0; i<n; i++)
|
|
bl.Set(i,2*CMath::RandomReal()-1);
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,1.0E-64,0,0,10);
|
|
CMinBC::MinBCSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,state.m_x[i]!=x[i],"testminbcunit.ap:538");
|
|
}
|
|
fprev=state.m_f;
|
|
xlast=state.m_x;
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,x[i]!=(double)(xlast[i]),"testminbcunit.ap:545");
|
|
}
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests, second one issues NeedFG requests)
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
n=10;
|
|
diffstep=1.0E-6;
|
|
for(dkind=0; dkind<=1; dkind++)
|
|
{
|
|
x=vector<double>::Ones(n);
|
|
xlast=vector<double>::Zeros(n);
|
|
if(dkind==0)
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
if(dkind==1)
|
|
CMinBC::MinBCCreateF(n,x,diffstep,state);
|
|
CMinBC::MinBCSetCond(state,1.0E-6,0,epsx,0);
|
|
wasf=false;
|
|
wasfg=false;
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
|
|
if(state.m_needfg)
|
|
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
|
|
}
|
|
wasf=wasf||state.m_needf;
|
|
wasfg=wasfg||state.m_needfg;
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
if(dkind==0)
|
|
CAp::SetErrorFlag(err,wasf || !wasfg,"testminbcunit.ap:585");
|
|
if(dkind==1)
|
|
CAp::SetErrorFlag(err,!wasf || wasfg,"testminbcunit.ap:587");
|
|
}
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S, then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
x=vector<double>::Zeros(1);
|
|
s=vector<double>::Zeros(1);
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s.Set(0,MathExp(CMath::RandomReal()*4-2));
|
|
x.Set(0,0);
|
|
CMinBC::MinBCCreateF(1,x,diffstep,state);
|
|
CMinBC::MinBCSetCond(state,1.0E-6,0,epsx,0);
|
|
CMinBC::MinBCSetScale(state,s);
|
|
v=0;
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
CAp::SetErrorFlag(err,MathAbs(MathLog(r))>(MathLog(1+1000*CMath::m_machineepsilon)),"testminbcunit.ap:618");
|
|
}
|
|
//--- Test stpmax
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
n=1;
|
|
x=vector<double>::Zeros(n);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
x.Set(0,100);
|
|
bl.Set(0,2*CMath::RandomReal()-1);
|
|
bu.Set(0,AL_POSINF);
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,epsg,0,epsx,0);
|
|
CMinBC::MinBCSetXRep(state,true);
|
|
CMinBC::MinBCSetStpMax(state,stpmax);
|
|
xprev=x[0];
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
|
|
CAp::SetErrorFlag(err,MathAbs(state.m_x[0]-xprev)>((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminbcunit.ap:646");
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
CAp::SetErrorFlag(err,(double)(MathAbs(state.m_x[0]-xprev))>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminbcunit.ap:650");
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
}
|
|
//--- Ability to solve problems with function which is unbounded from below
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
n=1;
|
|
x=vector<double>::Zeros(n);
|
|
bl=vector<double>::Zeros(n);
|
|
bu=vector<double>::Zeros(n);
|
|
bl.Set(0,4*CMath::RandomReal()+1);
|
|
bu.Set(0,bl[0]+1);
|
|
x.Set(0,0.5*(bl[0]+bu[0]));
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
CMinBC::MinBCSetCond(state,epsg,0,epsx,0);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0]));
|
|
state.m_g.Set(0,-(2.0E8*state.m_x[0]));
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,MathAbs(x[0]-bu[0])>epsx,"testminbcunit.ap:680");
|
|
}
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
|
|
//--- * function is EFFECTIVELY unconstrained; it has formal constraints,
|
|
//--- but they are inactive at the solution; we try different variants
|
|
//--- in order to explore different control paths of the optimizer:
|
|
//--- 0) absense of constraints
|
|
//--- 1) bound constraints -100000<=x[i]<=100000
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set very stringent stopping conditions
|
|
//--- * and we test that in the extremum stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
tmpeps=1.0E-5;
|
|
for(n=1; n<=10; n++)
|
|
{
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
for(pkind=0; pkind<=2; pkind++)
|
|
{
|
|
x=vector<double>::Zeros(n);
|
|
a=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
h=vector<double>::Zeros(n);
|
|
bl=vector<double>::Full(n,-100000);
|
|
bu=vector<double>::Full(n,100000);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,CMath::RandomReal()+1);
|
|
a.Set(i,MathExp(MathLog(10)*(2*CMath::RandomReal()-1)));
|
|
s.Set(i,MathExp(MathLog(10)*(2*CMath::RandomReal()-1)));
|
|
h.Set(i,MathExp(MathLog(10)*(2*CMath::RandomReal()-1)));
|
|
}
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
if(ckind==1)
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
if(pkind==1)
|
|
CMinBC::MinBCSetPrecDiag(state,h);
|
|
if(pkind==2)
|
|
CMinBC::MinBCSetPrecScale(state);
|
|
CMinBC::MinBCSetCond(state,tmpeps,0,0,0);
|
|
CMinBC::MinBCSetScale(state,s);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=a[i]*CMath::Sqr(state.m_x[i]);
|
|
state.m_g.Set(i,2*a[i]*state.m_x[i]);
|
|
}
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminbcunit.ap:747");
|
|
return;
|
|
}
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
v+=CMath::Sqr(s[i]*2*a[i]*x[i]);
|
|
v=MathSqrt(v);
|
|
CAp::SetErrorFlag(err,v>(double)(tmpeps),"testminbcunit.ap:754");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
|
|
//--- f(x) = {
|
|
//--- { M, if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+4, M is either 1.0E8, 1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
for(mkind=0; mkind<=2; mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
vc=1;
|
|
vm=1;
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
if(ckind==1)
|
|
vc=1.0E+4;
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
if(mkind==2)
|
|
vm=AL_POSINF;
|
|
//--- Create optimizer, solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
x=vector<double>::Zeros(1);
|
|
CMinBC::MinBCCreate(1,x,state);
|
|
CMinBC::MinBCSetCond(state,epsg,0,0,0);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
if((-0.999999)<state.m_x[0] && state.m_x[0]<(0.999999))
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g.Set(0,1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc);
|
|
}
|
|
else
|
|
{
|
|
state.m_f=vm;
|
|
state.m_g.Set(0,0);
|
|
}
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminbcunit.ap:822");
|
|
return;
|
|
}
|
|
CAp::SetErrorFlag(err,(MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc))>(double)(epsg),"testminbcunit.ap:825");
|
|
}
|
|
}
|
|
}
|
|
//--- Test behaviour on noisy functions.
|
|
//--- Consider following problem:
|
|
//--- * f(x,y) = (x+1)^2 + (y+1)^2 + 10000*MachineEpsilon*RandomReal()
|
|
//--- * boundary constraints x>=0, y>=0
|
|
//--- * starting point (x0,y0)=(10*MachineEpsilon,1.0)
|
|
//--- Such problem contains small numerical noise. Without noise its
|
|
//--- solution is (xs,ys)=(0,0), which is easy to find. However, presence
|
|
//--- of the noise makes it hard to solve:
|
|
//--- * noisy f(x,y) is monotonically decreasing only when we perform
|
|
//--- steps orders of magnitude larger than 10000*MachineEpsilon
|
|
//--- * at small scales f(x,y) is non-monotonic and non-convex
|
|
//--- * however, our first step must be done towards
|
|
//--- (x1,y1) = (0,1-some_small_value), and length of such step is
|
|
//--- many times SMALLER than 10000*MachineEpsilon
|
|
//--- * second step, from (x1,y1) to (xs,ys), will be large enough to
|
|
//--- ignore numerical noise, so the only problem is to perform
|
|
//--- first step
|
|
//--- Naive implementation of BC should fail sometimes (sometimes -
|
|
//--- due to non-deterministic nature of noise) on such problem. However,
|
|
//--- our improved implementation should solve it correctly. We test
|
|
//--- several variations of inner stopping criteria.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
eps=1.0E-9;
|
|
x.Resize(2);
|
|
bl=vector<double>::Zeros(2);
|
|
bu=vector<double>::Zeros(2);
|
|
x.Set(0,10*CMath::m_machineepsilon);
|
|
x.Set(1,1.0);
|
|
bu.Set(0,AL_POSINF);
|
|
bu.Set(1,AL_POSINF);
|
|
for(ckind=0; ckind<=2; ckind++)
|
|
{
|
|
CMinBC::MinBCCreate(2,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
if(ckind==0)
|
|
CMinBC::MinBCSetCond(state,eps,0,0,0);
|
|
if(ckind==1)
|
|
CMinBC::MinBCSetCond(state,0,eps,0,0);
|
|
if(ckind==2)
|
|
CMinBC::MinBCSetCond(state,0,0,eps,0);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]+1)+CMath::Sqr(state.m_x[1]+1)+10000*CMath::m_machineepsilon*CMath::RandomReal();
|
|
state.m_g.Set(0,2*(state.m_x[0]+1));
|
|
state.m_g.Set(1,2*(state.m_x[1]+1));
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,xf,rep);
|
|
if((rep.m_terminationtype<=0 || xf[0]!=0.0) || xf[1]!=0.0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminbcunit.ap:889");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//--- Deterministic variation of the previous problem.
|
|
//--- Consider following problem:
|
|
//--- * boundary constraints x>=0, y>=0
|
|
//--- * starting point (x0,y0)=(10*MachineEpsilon,1.0)
|
|
//--- / (x+1)^2 + (y+1)^2, for (x,y)<>(x0,y0)
|
|
//--- * f(x,y) = |
|
|
//--- \ (x+1)^2 + (y+1)^2 - 0.1, for (x,y)=(x0,y0)
|
|
//--- Such problem contains deterministic numerical noise (-0.1 at
|
|
//--- starting point). Without noise its solution is easy to find.
|
|
//--- However, presence of the noise makes it hard to solve:
|
|
//--- * our first step must be done towards (x1,y1) = (0,1-some_small_value),
|
|
//--- but such step will increase function valye by approximately 0.1 -
|
|
//--- instead of decreasing it.
|
|
//--- Naive implementation of BC should fail on such problem. However,
|
|
//--- our improved implementation should solve it correctly. We test
|
|
//--- several variations of inner stopping criteria.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
eps=1.0E-9;
|
|
x=vector<double>::Ones(2);
|
|
bl=vector<double>::Zeros(2);
|
|
bu=vector<double>::Zeros(2);
|
|
x.Set(0,10*CMath::m_machineepsilon);
|
|
bu.Set(0,AL_POSINF);
|
|
bu.Set(1,AL_POSINF);
|
|
for(ckind=0; ckind<=2; ckind++)
|
|
{
|
|
CMinBC::MinBCCreate(2,x,state);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
if(ckind==0)
|
|
CMinBC::MinBCSetCond(state,eps,0,0,0);
|
|
if(ckind==1)
|
|
CMinBC::MinBCSetCond(state,0,eps,0,0);
|
|
if(ckind==2)
|
|
CMinBC::MinBCSetCond(state,0,0,eps,0);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]+1)+CMath::Sqr(state.m_x[1]+1);
|
|
if(state.m_x[0]==x[0] && (double)(state.m_x[1])==x[1])
|
|
state.m_f-=0.1;
|
|
state.m_g.Set(0,2*(state.m_x[0]+1));
|
|
state.m_g.Set(1,2*(state.m_x[1]+1));
|
|
}
|
|
}
|
|
CMinBC::MinBCResults(state,xf,rep);
|
|
if((rep.m_terminationtype<=0 || xf[0]!=0.0) || xf[1]!=0.0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testminbcunit.ap:952");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//--- Test integrity checks for NAN/INF:
|
|
//--- * algorithm solves optimization problem, which is normal for some time (quadratic)
|
|
//--- * after 5-th step we choose random component of gradient and consistently spoil
|
|
//--- it by NAN or INF.
|
|
//--- * we check that correct termination code is returned (-8)
|
|
n=100;
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
spoiliteration=5;
|
|
stopiteration=8;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
//--- Gradient can be spoiled by +INF, -INF, NAN
|
|
spoilvar=CHighQualityRand::HQRndUniformI(rs,n);
|
|
i=CHighQualityRand::HQRndUniformI(rs,3);
|
|
spoilval=AL_NaN;
|
|
if(i==0)
|
|
spoilval=AL_NEGINF;
|
|
if(i==1)
|
|
spoilval=AL_POSINF;
|
|
}
|
|
else
|
|
{
|
|
//--- Function value can be spoiled only by NAN
|
|
//--- (+INF can be recognized as legitimate value during optimization)
|
|
spoilvar=-1;
|
|
spoilval=AL_NaN;
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,1.0E5,fulla);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CMinBC::MinBCCreate(n,x0,state);
|
|
CMinBC::MinBCSetCond(state,0.0,0.0,0.0,stopiteration);
|
|
CMinBC::MinBCSetXRep(state,true);
|
|
k=-1;
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
double temp=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
temp=CAblasF::RDotVR(n,state.m_x,fulla,i);
|
|
state.m_f+=state.m_x[i]*(b[i]+temp/2);
|
|
state.m_g.Set(i,b[i]+temp);
|
|
}
|
|
if(k>=spoiliteration)
|
|
{
|
|
if(spoilvar<0)
|
|
state.m_f=spoilval;
|
|
else
|
|
state.m_g.Set(spoilvar,spoilval);
|
|
}
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
k++;
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
err=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=-8,"testminbcunit.ap:1037");
|
|
}
|
|
//--- Check algorithm ability to handle request for termination:
|
|
//--- * to terminate with correct return code = 8
|
|
//---*to return point which was "current" at the moment of termination
|
|
//--- NOTE: we solve problem with "corrupted" preconditioner which makes it hard
|
|
//--- to converge in less than StopCallIdx iterations
|
|
for(pass=1; pass<=50; pass++)
|
|
{
|
|
n=3;
|
|
ss=100;
|
|
x=vector<double>::Zeros(n);
|
|
xlast=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,6+CMath::RandomReal());
|
|
s=vector<double>::Full(3,0.00001);
|
|
s.Set(2,10000.0);
|
|
stopcallidx=CMath::RandomInteger(20);
|
|
maxits=25;
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetCond(state,0,0,0,maxits);
|
|
CMinBC::MinBCSetXRep(state,true);
|
|
CMinBC::MinBCSetPrecDiag(state,s);
|
|
callidx=0;
|
|
terminationrequested=false;
|
|
xlast=x;
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=ss*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
state.m_g.Set(0,2*ss*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[2]-state.m_x[0])*-1);
|
|
state.m_g.Set(1,2*state.m_x[1]);
|
|
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
|
|
if(callidx==stopcallidx)
|
|
{
|
|
CMinBC::MinBCRequestTermination(state);
|
|
terminationrequested=true;
|
|
}
|
|
callidx++;
|
|
continue;
|
|
}
|
|
if(state.m_xupdated)
|
|
{
|
|
if(!terminationrequested)
|
|
xlast=state.m_x;
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
err=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testminbcunit.ap:1095");
|
|
for(i=0; i<n; i++)
|
|
CAp::SetErrorFlag(err,x[i]!=(double)(xlast[i]),"testminbcunit.ap:1097");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True(leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
CRowDouble x;
|
|
CRowDouble x0;
|
|
int i=0;
|
|
int k=0;
|
|
CMatrixDouble v;
|
|
CRowDouble bl;
|
|
CRowDouble bu;
|
|
CRowDouble vd;
|
|
CRowDouble d;
|
|
CRowDouble units;
|
|
CRowDouble s;
|
|
int cntb1=0;
|
|
int cntb2=0;
|
|
int cntg1=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
CRowDouble diagh;
|
|
CMinBCState state;
|
|
CMinBCReport rep;
|
|
int ckind=0;
|
|
int fk=0;
|
|
//--- Preconditioner test 1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner with unit scale
|
|
//--- * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B1 is worse than G1, B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- Test problem setup:
|
|
//--- * f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)
|
|
//--- * constraints:
|
|
//--- 0) absent
|
|
//--- 1) box
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
k=100;
|
|
epsg=1.0E-8;
|
|
for(n=10; n<=10; n++)
|
|
{
|
|
for(ckind=0; ckind<=1; ckind++)
|
|
{
|
|
fk=1;
|
|
x=vector<double>::Zeros(n);
|
|
units=vector<double>::Ones(n);
|
|
CMinBC::MinBCCreate(n,x,state);
|
|
CMinBC::MinBCSetCond(state,epsg,0.0,0.0,0);
|
|
if(ckind==1)
|
|
{
|
|
bl=vector<double>::Full(n,-1.0);
|
|
bu=vector<double>::Full(n,1.0);
|
|
CMinBC::MinBCSetBC(state,bl,bu);
|
|
}
|
|
//--- Test it with default preconditioner VS. perturbed diagonal preconditioner
|
|
CMinBC::MinBCSetPrecDefault(state);
|
|
CMinBC::MinBCSetScale(state,units);
|
|
cntb1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
CMinBC::MinBCRestartFrom(state,x);
|
|
while(CMinBC::MinBCIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
cntb1+=rep.m_iterationscount;
|
|
err=err||rep.m_terminationtype<=0;
|
|
if(err)
|
|
Sleep(0);
|
|
}
|
|
diagh=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
diagh.Set(i,2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal()));
|
|
CMinBC::MinBCSetPrecDiag(state,diagh);
|
|
CMinBC::MinBCSetScale(state,units);
|
|
cntg1=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
CMinBC::MinBCRestartFrom(state,x);
|
|
while(CMinBC::MinBCIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
cntg1+=rep.m_iterationscount;
|
|
err=err||rep.m_terminationtype<=0;
|
|
if(err)
|
|
Sleep(0);
|
|
}
|
|
err=err||cntb1<cntg1;
|
|
if(err)
|
|
Sleep(0);
|
|
//--- Test it with scale-based preconditioner
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,1/MathSqrt(2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal())));
|
|
CMinBC::MinBCSetPrecDefault(state);
|
|
CMinBC::MinBCSetScale(state,s);
|
|
cntb2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
CMinBC::MinBCRestartFrom(state,x);
|
|
while(CMinBC::MinBCIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
cntb2+=rep.m_iterationscount;
|
|
err=err||rep.m_terminationtype<=0;
|
|
if(err)
|
|
Sleep(0);
|
|
}
|
|
CMinBC::MinBCSetPrecScale(state);
|
|
CMinBC::MinBCSetScale(state,s);
|
|
cntg2=0;
|
|
for(pass=0; pass<k; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
CMinBC::MinBCRestartFrom(state,x);
|
|
while(CMinBC::MinBCIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
CMinBC::MinBCResults(state,x,rep);
|
|
cntg2+=rep.m_iterationscount;
|
|
err=err||rep.m_terminationtype<=0;
|
|
if(err)
|
|
Sleep(0);
|
|
}
|
|
err=err||cntb2<cntg2;
|
|
if(err)
|
|
Sleep(0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests OptGuard |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::TestOptGuard(bool &waserrors)
|
|
{
|
|
CHighQualityRandState rs;
|
|
double v=0;
|
|
CMinBCState state;
|
|
CMinBCReport rep;
|
|
COptGuardReport ogrep;
|
|
COptGuardNonC1Test0Report ognonc1test0strrep;
|
|
COptGuardNonC1Test0Report ognonc1test0lngrep;
|
|
COptGuardNonC1Test1Report ognonc1test1strrep;
|
|
COptGuardNonC1Test1Report ognonc1test1lngrep;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble a1;
|
|
CRowDouble s;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble b;
|
|
CRowDouble bndl;
|
|
CRowDouble bndu;
|
|
double diffstep=0;
|
|
int pass=0;
|
|
int defecttype=0;
|
|
bool failed;
|
|
int passcount=0;
|
|
int maxfails=0;
|
|
int failurecounter=0;
|
|
int maxc1test0fails=0;
|
|
int maxc1test1fails=0;
|
|
int c1test0fails=0;
|
|
int c1test1fails=0;
|
|
double avgstr0len=0;
|
|
double avglng0len=0;
|
|
double avgstr1len=0;
|
|
double avglng1len=0;
|
|
int varidx=0;
|
|
int skind=0;
|
|
CMatrixDouble jactrue;
|
|
CMatrixDouble jacdefect;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Check that gradient verification is disabled by default:
|
|
//--- gradient checking for bad problem must return nothing
|
|
n=10;
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,1.0+0.1*i);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
|
|
CMinBC::MinBCCreate(n,x0,state);
|
|
CMinBC::MinBCSetCond(state,0,0,1.0E-9,10);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_f+=0.5*state.m_x[i]*v;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
state.m_g.Set(i,0);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CMinBC::MinBCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1321");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1322");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminbcunit.ap:1325");
|
|
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminbcunit.ap:1326");
|
|
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminbcunit.ap:1327");
|
|
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminbcunit.ap:1328");
|
|
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminbcunit.ap:1329");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminbcunit.ap:1332");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminbcunit.ap:1333");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminbcunit.ap:1334");
|
|
//--- Test that C0/C1 continuity monitoring is disabled by default;
|
|
//--- we solve nonsmooth problem and test that nothing is returned
|
|
//--- by OptGuard.
|
|
n=10;
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
CMinBC::MinBCCreate(n,x0,state);
|
|
CMinBC::MinBCSetCond(state,0,0,1.0E-9,50);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
state.m_g.Set(i,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_f+=MathAbs(v);
|
|
v=MathSign(v);
|
|
for(j=0; j<n; j++)
|
|
state.m_g.Add(j,v*a.Get(i,j));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CMinBC::MinBCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1374");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1375");
|
|
if(waserrors)
|
|
return;
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminbcunit.ap:1378");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1suspected,"testminbcunit.ap:1379");
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminbcunit.ap:1380");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminbcunit.ap:1381");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>=0,"testminbcunit.ap:1382");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test0positive,"testminbcunit.ap:1383");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test1positive,"testminbcunit.ap:1384");
|
|
//--- Test gradient checking functionality, try various
|
|
//--- defect types:
|
|
//--- * accidental zeroing of some gradient component
|
|
//--- * accidental addition of 1.0 to some component
|
|
//--- * accidental multiplication by 2.0
|
|
//--- Try distorting both target and constraints.
|
|
diffstep=0.001;
|
|
n=10;
|
|
for(skind=0; skind<=1; skind++)
|
|
{
|
|
for(defecttype=-1; defecttype<=2; defecttype++)
|
|
{
|
|
varidx=CHighQualityRand::HQRndUniformI(rs,n);
|
|
x0=vector<double>::Zeros(n);
|
|
s=vector<double>::Zeros(n);
|
|
bndl=vector<double>::Zeros(n);
|
|
bndu=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
s.Set(i,MathPow(10,skind*(30*CHighQualityRand::HQRndUniformR(rs)-15)));
|
|
x0.Set(i,(1.0+0.1*i)*s[i]);
|
|
j=CHighQualityRand::HQRndUniformI(rs,3);
|
|
bndl.Set(i,-(100*s[i]));
|
|
bndu.Set(i,100*s[i]);
|
|
if(j==1)
|
|
bndl.Set(i,x0[i]);
|
|
if(j==2)
|
|
bndu.Set(i,x0[i]);
|
|
}
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
|
|
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
|
|
CMinBC::MinBCCreate(n,x0,state);
|
|
CMinBC::MinBCOptGuardGradient(state,diffstep);
|
|
CMinBC::MinBCSetCond(state,0,0,1.0E-9,10);
|
|
CMinBC::MinBCSetScale(state,s);
|
|
CMinBC::MinBCSetBC(state,bndl,bndu);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminbcunit.ap:1432");
|
|
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminbcunit.ap:1433");
|
|
}
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=state.m_x[j]/s[j]*a.Get(i,j);
|
|
state.m_f+=0.5*(state.m_x[i]/s[i])*v;
|
|
state.m_g.Set(i,v);
|
|
}
|
|
if(defecttype==0)
|
|
state.m_g.Set(varidx,0);
|
|
if(defecttype==1)
|
|
state.m_g.Add(varidx,1);
|
|
if(defecttype==2)
|
|
state.m_g.Mul(varidx,2);
|
|
for(i=0; i<n; i++)
|
|
state.m_g.Mul(i,1.0/s[i]);
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CMinBC::MinBCOptGuardResults(state,ogrep);
|
|
//--- Check that something is returned
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1466");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1467");
|
|
if(waserrors)
|
|
return;
|
|
//--- Compute reference values for true and spoiled Jacobian at X0
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(ogrep.m_badgradxbase,n),"testminbcunit.ap:1474");
|
|
if(waserrors)
|
|
return;
|
|
jactrue=matrix<double>::Zeros(1,n);
|
|
jacdefect=matrix<double>::Zeros(1,n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<n; j++)
|
|
v+=ogrep.m_badgradxbase[j]/s[j]*a.Get(i,j);
|
|
jactrue.Set(0,i,v);
|
|
jacdefect.Set(0,i,v);
|
|
}
|
|
if(defecttype==0)
|
|
jacdefect.Set(0,varidx,0);
|
|
if(defecttype==1)
|
|
jacdefect.Add(0,varidx,1);
|
|
if(defecttype==2)
|
|
jacdefect.Mul(0,varidx,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
jactrue.Mul(0,i,1.0/s[i]);
|
|
jacdefect.Mul(0,i,1.0/s[i]);
|
|
}
|
|
//--- Check OptGuard report
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgraduser,1,n),"testminbcunit.ap:1502");
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgradnum,1,n),"testminbcunit.ap:1503");
|
|
if(waserrors)
|
|
return;
|
|
if(defecttype>=0)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminbcunit.ap:1508");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=0,"testminbcunit.ap:1509");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminbcunit.ap:1510");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminbcunit.ap:1514");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminbcunit.ap:1515");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminbcunit.ap:1516");
|
|
}
|
|
for(j=0; j<n; j++)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(jactrue.Get(0,j)-ogrep.m_badgradnum.Get(0,j)))>(0.01/s[j]),"testminbcunit.ap:1520");
|
|
CAp::SetErrorFlag(waserrors,(MathAbs(jacdefect.Get(0,j)-ogrep.m_badgraduser.Get(0,j)))>(0.01/s[j]),"testminbcunit.ap:1521");
|
|
}
|
|
}
|
|
}
|
|
//--- A test for detection of C1 continuity violations in the target.
|
|
//--- Target function is a sum of |(x,c_i)| for i=1..N.
|
|
//--- No constraints is present.
|
|
//--- Analytic gradient is provided.
|
|
//--- OptGuard should be able to detect violations in more than
|
|
//--- 99.9% of runs; it means that 100 runs should have no more than 4
|
|
//--- failures in all cases (even after multiple repeated tests; according
|
|
//--- to the binomial distribution quantiles).
|
|
//--- We select some N and perform exhaustive search for this N.
|
|
passcount=100;
|
|
maxfails=4;
|
|
maxc1test0fails=10;
|
|
maxc1test1fails=10;
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
failurecounter=0;
|
|
c1test0fails=0;
|
|
c1test1fails=0;
|
|
avgstr0len=0;
|
|
avglng0len=0;
|
|
avgstr1len=0;
|
|
avglng1len=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Formulate problem
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
//--- Create and try to solve
|
|
CMinBC::MinBCCreate(n,x0,state);
|
|
CMinBC::MinBCSetCond(state,0,0,1.0E-9,50);
|
|
CMinBC::MinBCSetScale(state,s);
|
|
CMinBC::MinBCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
state.m_g.Set(i,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_f+=MathAbs(v);
|
|
v=MathSign(v);
|
|
for(j=0; j<n; j++)
|
|
state.m_g.Add(j,v*a.Get(i,j));
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CMinBC::MinBCOptGuardResults(state,ogrep);
|
|
//--- Check basic properties of the solution
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1602");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1603");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check generic OptGuard report: distinguish between "hard"
|
|
//--- failures which result in immediate termination
|
|
//--- (C0 violation being reported) and "soft" ones
|
|
//--- (C1 violation is NOT reported) which accumulate
|
|
//--- until we exhaust limit.
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminbcunit.ap:1614");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminbcunit.ap:1615");
|
|
failed=false;
|
|
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
|
|
failed=failed||!ogrep.m_nonc1suspected;
|
|
failed=failed||ogrep.m_nonc1fidx!=0;
|
|
if(failed)
|
|
failurecounter++;
|
|
//--- Check C1 continuity test #0
|
|
CMinBC::MinBCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep);
|
|
CMinBC::MinBCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep);
|
|
if(ogrep.m_nonc1test0positive)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminbcunit.ap:1630");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminbcunit.ap:1631");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminbcunit.ap:1632");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminbcunit.ap:1633");
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
|
|
avgstr0len+=(double)ognonc1test0strrep.m_cnt/(double)passcount;
|
|
avglng0len+=(double)ognonc1test0lngrep.m_cnt/(double)passcount;
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminbcunit.ap:1641");
|
|
CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminbcunit.ap:1642");
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
|
|
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
|
|
c1test0fails++;
|
|
}
|
|
if(ogrep.m_nonc1test1positive)
|
|
{
|
|
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminbcunit.ap:1649");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminbcunit.ap:1650");
|
|
CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminbcunit.ap:1651");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminbcunit.ap:1652");
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
|
|
avgstr1len+=(double)ognonc1test1strrep.m_cnt/(double)passcount;
|
|
avglng1len+=(double)ognonc1test1lngrep.m_cnt/(double)passcount;
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminbcunit.ap:1660");
|
|
CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminbcunit.ap:1661");
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
|
|
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
|
|
c1test1fails++;
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminbcunit.ap:1668");
|
|
CAp::SetErrorFlag(waserrors,c1test0fails>maxc1test0fails,"testminbcunit.ap:1669");
|
|
CAp::SetErrorFlag(waserrors,c1test1fails>maxc1test1fails,"testminbcunit.ap:1670");
|
|
CAp::SetErrorFlag(waserrors,(double)(avglng0len)<=(double)(avgstr0len),"testminbcunit.ap:1671");
|
|
CAp::SetErrorFlag(waserrors,(double)(avglng1len)<=(double)(avgstr1len),"testminbcunit.ap:1672");
|
|
//--- Detection of C1 continuity violations in the target under numerical differentiation:
|
|
//--- * target function is a sum of |(x,c_i)| for i=1..N.
|
|
//--- * no constraints is present.
|
|
//--- * analytic gradient is provided.
|
|
//--- OptGuard should always be able to detect violations in more than
|
|
//--- 99% of runs (note: reduced strength when compared with analytic gradient);
|
|
//--- it means that 100 runs should have no more than 10 failures in all cases
|
|
//--- (even after multiple repeated tests; according to the binomial distribution
|
|
//--- quantiles).
|
|
//--- We select some N and perform exhaustive search for this N.
|
|
diffstep=0.0001;
|
|
passcount=100;
|
|
maxfails=10;
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
failurecounter=0;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Formulate problem
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
s=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
s.Set(i,0.01*MathPow(2,0.33*CHighQualityRand::HQRndNormal(rs)));
|
|
a=matrix<double>::Zeros(n,n);
|
|
for(i=0; i<n; i++)
|
|
for(j=0; j<n; j++)
|
|
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
//--- Create and try to solve
|
|
CMinBC::MinBCCreateF(n,x0,diffstep,state);
|
|
CMinBC::MinBCSetCond(state,0,0,1.0E-9,50);
|
|
CMinBC::MinBCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=CAblasF::RDotVR(n,state.m_x,a,i);
|
|
state.m_f+=MathAbs(v);
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CMinBC::MinBCOptGuardResults(state,ogrep);
|
|
//--- Check basic properties of the solution
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1738");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1739");
|
|
if(waserrors)
|
|
return;
|
|
//--- Check OptGuard report: distinguish between "hard"
|
|
//--- failures which result in immediate termination
|
|
//--- (C0 violation being reported) and "soft" ones
|
|
//--- (C1 violation is NOT reported) which accumulate
|
|
//--- until we exhaust limit.
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminbcunit.ap:1750");
|
|
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminbcunit.ap:1751");
|
|
failed=false;
|
|
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
|
|
failed=failed||!ogrep.m_nonc1suspected;
|
|
failed=failed||ogrep.m_nonc1fidx!=0;
|
|
if(failed)
|
|
failurecounter++;
|
|
}
|
|
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminbcunit.ap:1759");
|
|
//--- Make sure than no false positives are reported for larger
|
|
//--- problems where numerical noise can be an issue:
|
|
//--- * N=100 dimensions
|
|
//--- * positive-definite quadratic programming problem
|
|
//--- * upper limit on iterations count, MaxIts=25
|
|
//--- We simply test that OptGuard does not return error code.
|
|
n=100;
|
|
CMatGen::SPDMatrixRndCond(n,1.0E2,a);
|
|
b=vector<double>::Zeros(n);
|
|
x0=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
b.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x0.Set(i,MathPow(2.0,CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
CMinBC::MinBCCreate(n,x0,state);
|
|
CMinBC::MinBCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
|
|
CMinBC::MinBCSetCond(state,0,0,1.0E-9,25);
|
|
while(CMinBC::MinBCIteration(state))
|
|
{
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
state.m_g=b;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
state.m_f+=b[i]*state.m_x[i];
|
|
for(j=0; j<n; j++)
|
|
{
|
|
state.m_f+=0.5*state.m_x[i]*a.Get(i,j)*state.m_x[j];
|
|
state.m_g.Add(i,a.Get(i,j)*state.m_x[j]);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
CAp::Assert(false);
|
|
waserrors=true;
|
|
return;
|
|
}
|
|
CMinBC::MinBCResults(state,x1,rep);
|
|
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1802");
|
|
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1803");
|
|
if(waserrors)
|
|
return;
|
|
CMinBC::MinBCOptGuardResults(state,ogrep);
|
|
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminbcunit.ap:1807");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets random preconditioner: |
|
|
//| * unit one, for PrecKind = 0 |
|
|
//| * diagonal - based one, for PrecKind = 1 |
|
|
//| * scale - based one, for PrecKind = 2 |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::SetRandomPreconditioner(CMinBCState &state,
|
|
int n,
|
|
int preckind)
|
|
{
|
|
CRowDouble p;
|
|
|
|
if(preckind==1)
|
|
{
|
|
p=vector<double>::Zeros(n);
|
|
for(int i=0; i<n; i++)
|
|
p.Set(i,MathExp(6*CMath::RandomReal()-3));
|
|
CMinBC::MinBCSetPrecDiag(state,p);
|
|
}
|
|
else
|
|
CMinBC::MinBCSetPrecDefault(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests report of "non-C1" test #0 for task #0 given |
|
|
//| by matrix A. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::TestOptGuardC1Test0ReportForTask0(bool &err,
|
|
COptGuardNonC1Test0Report &rep,
|
|
CMatrixDouble &a,
|
|
int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double va=0;
|
|
double vb=0;
|
|
bool hasc1discontinuities;
|
|
|
|
if(rep.m_positive)
|
|
{
|
|
//--- Check positive report, first checks
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminbcunit.ap:1858");
|
|
CAp::SetErrorFlag(err,rep.m_n!=n,"testminbcunit.ap:1859");
|
|
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminbcunit.ap:1860");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminbcunit.ap:1861");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminbcunit.ap:1862");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminbcunit.ap:1863");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminbcunit.ap:1864");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminbcunit.ap:1865");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=rep.m_cnt,"testminbcunit.ap:1866");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminbcunit.ap:1867");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminbcunit.ap:1868");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminbcunit.ap:1869");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_f,rep.m_cnt),"testminbcunit.ap:1870");
|
|
if(err)
|
|
return;
|
|
//--- Check consistency of X0, D, Stp and F
|
|
for(k=0; k<=rep.m_cnt-2; k++)
|
|
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminbcunit.ap:1878");
|
|
for(k=0; k<rep.m_cnt; k++)
|
|
{
|
|
v=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vv=0;
|
|
for(j=0; j<n; j++)
|
|
vv+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[k]);
|
|
v+=MathAbs(vv);
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(v-rep.m_f[k])>(1.0E-6*MathMax(MathAbs(v),1)),"testminbcunit.ap:1891");
|
|
}
|
|
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
|
|
hasc1discontinuities=false;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
va=0;
|
|
vb=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
|
|
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
|
|
}
|
|
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
|
|
}
|
|
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminbcunit.ap:1909");
|
|
}
|
|
else
|
|
{
|
|
//--- Check negative report: fields must be empty
|
|
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminbcunit.ap:1916");
|
|
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminbcunit.ap:1917");
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminbcunit.ap:1918");
|
|
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminbcunit.ap:1919");
|
|
CAp::SetErrorFlag(err,rep.m_n!=0,"testminbcunit.ap:1920");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminbcunit.ap:1921");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminbcunit.ap:1922");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminbcunit.ap:1923");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=0,"testminbcunit.ap:1924");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests report of "non-C1" test #1 for task #0 given |
|
|
//| by matrix A. |
|
|
//| On failure sets error flag. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMinBCUnit::TestOptGuardC1Test1ReportForTask0(bool &err,
|
|
COptGuardNonC1Test1Report &rep,
|
|
CMatrixDouble &a,
|
|
int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double va=0;
|
|
double vb=0;
|
|
bool tooclose;
|
|
bool hasc1discontinuities;
|
|
|
|
if(rep.m_positive)
|
|
{
|
|
//--- Check positive report, first checks
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminbcunit.ap:1952");
|
|
CAp::SetErrorFlag(err,rep.m_vidx<0,"testminbcunit.ap:1953");
|
|
CAp::SetErrorFlag(err,rep.m_vidx>n,"testminbcunit.ap:1954");
|
|
CAp::SetErrorFlag(err,rep.m_n!=n,"testminbcunit.ap:1955");
|
|
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminbcunit.ap:1956");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminbcunit.ap:1957");
|
|
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminbcunit.ap:1958");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminbcunit.ap:1959");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminbcunit.ap:1960");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminbcunit.ap:1961");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=rep.m_cnt,"testminbcunit.ap:1962");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminbcunit.ap:1963");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminbcunit.ap:1964");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminbcunit.ap:1965");
|
|
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_g,rep.m_cnt),"testminbcunit.ap:1966");
|
|
if(err)
|
|
return;
|
|
//--- Check consistency of X0, D, Stp and G
|
|
for(k=0; k<rep.m_cnt-1; k++)
|
|
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminbcunit.ap:1974");
|
|
for(k=0; k<rep.m_cnt; k++)
|
|
{
|
|
v=0;
|
|
tooclose=false;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
vv=0;
|
|
for(j=0; j<n; j++)
|
|
vv+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[k]);
|
|
v+=MathSign(vv)*a.Get(i,rep.m_vidx);
|
|
tooclose=tooclose||MathAbs(vv)<(1.0E-4);
|
|
}
|
|
if(!tooclose)
|
|
CAp::SetErrorFlag(err,MathAbs(v-rep.m_g[k])>(1.0E-6*MathMax(MathAbs(v),1)),"testminbcunit.ap:1988");
|
|
}
|
|
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
|
|
tooclose=false;
|
|
hasc1discontinuities=false;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
va=0;
|
|
vb=0;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
|
|
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
|
|
}
|
|
tooclose=(tooclose||(double)(MathAbs(va))<(double)(1.0E-8))||(double)(MathAbs(vb))<(double)(1.0E-8);
|
|
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
|
|
}
|
|
if(!tooclose)
|
|
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminbcunit.ap:2009");
|
|
}
|
|
else
|
|
{
|
|
//--- Check negative report: fields must be empty
|
|
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminbcunit.ap:2016");
|
|
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminbcunit.ap:2017");
|
|
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminbcunit.ap:2018");
|
|
CAp::SetErrorFlag(err,rep.m_vidx!=-1,"testminbcunit.ap:2019");
|
|
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminbcunit.ap:2020");
|
|
CAp::SetErrorFlag(err,rep.m_n!=0,"testminbcunit.ap:2021");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminbcunit.ap:2022");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminbcunit.ap:2023");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminbcunit.ap:2024");
|
|
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=0,"testminbcunit.ap:2025");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestNormalDistrUnit
|
|
{
|
|
public:
|
|
static bool TestNormalDistr(bool silent);
|
|
|
|
private:
|
|
static void TestNormal(bool &errorflag);
|
|
static void TestBVN(bool &errorflag);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestNormalDistrUnit::TestNormalDistr(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nrmerr;
|
|
bool bvnerr;
|
|
bool waserrors;
|
|
|
|
nrmerr=false;
|
|
bvnerr=false;
|
|
|
|
TestNormal(nrmerr);
|
|
TestBVN(bvnerr);
|
|
//--- report
|
|
waserrors=bvnerr||nrmerr;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING NORMAL DISTRIBUTION");
|
|
PrintResult("NORMAL DISTRIBUTION",!nrmerr);
|
|
PrintResult("BIVARIATE NORMALS",!bvnerr);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- end
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Normal test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNormalDistrUnit::TestNormal(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
double v0=0;
|
|
double v1=0;
|
|
double x=0;
|
|
double h=0;
|
|
CHighQualityRandState rs;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test that PDF is roughly equal to derivative of CDF
|
|
for(int k=0; k<=999; k++)
|
|
{
|
|
x=CHighQualityRand::HQRndNormal(rs);
|
|
h=1.0E-5;
|
|
v0=CNormalDistr::NormalPDF(x);
|
|
v1=(CNormalDistr::NormalCDF(x+h)-CNormalDistr::NormalCDF(x-h))/(2*h);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(v0-v1)>(1.0E-4),"testnormaldistrunit.ap:75");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Bivariate normal test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestNormalDistrUnit::TestBVN(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
double v0=0;
|
|
double v1=0;
|
|
double err=0;
|
|
double x=0;
|
|
double y=0;
|
|
double rho=0;
|
|
double h=0;
|
|
CHighQualityRandState rs;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test bivariate normal CDF values (small Rho and hard Rho)
|
|
err=0;
|
|
err=MathMax(err,MathAbs(0.142221121370770000-CAlglib::BivariateNormalCDF(-1.060937077356340000,1.523763953950230000,0.344134938671007000)));
|
|
err=MathMax(err,MathAbs(0.001090824383322160-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,0.633583566571867000,0.196737644948391000)));
|
|
err=MathMax(err,MathAbs(0.000063428666799461-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-2.584892928002350000,0.603284544092224000)));
|
|
err=MathMax(err,MathAbs(0.000000019144648137-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-2.987017621703680000,-0.659498170394145000)));
|
|
err=MathMax(err,MathAbs(0.972551066792029000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,1.919693671247900000,0.261346800934746000)));
|
|
err=MathMax(err,MathAbs(0.000000608605272790-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,2.398320119295830000,0.662485812586403000)));
|
|
err=MathMax(err,MathAbs(0.576135824931800000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,4.695461487450740000,-0.110028699320946000)));
|
|
err=MathMax(err,MathAbs(0.876357061322732000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,1.211768206209080000,0.397007061864995000)));
|
|
err=MathMax(err,MathAbs(0.002969212104812400-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,1.340051767837440000,-0.233916559318503000)));
|
|
err=MathMax(err,MathAbs(0.000377048151161364-CNormalDistr::BivariateNormalCDF(1.128363097510700000,-2.781006578069910000,-0.642877478801918000)));
|
|
err=MathMax(err,MathAbs(0.995657061734790000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,2.626841258388710000,0.291409185863929000)));
|
|
err=MathMax(err,MathAbs(0.003282876365551870-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,3.217920162027340000,-0.101773464540366000)));
|
|
err=MathMax(err,MathAbs(0.002099371685469470-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-2.262911120125770000,0.361735313431128000)));
|
|
err=MathMax(err,MathAbs(0.648944114852307000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,0.432740073549983000,0.092845182466246300)));
|
|
err=MathMax(err,MathAbs(0.000000000094851728-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-3.491204153631050000,-0.010492822687090300)));
|
|
err=MathMax(err,MathAbs(0.000416778593465223-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,1.862802982022170000,0.398642687655347000)));
|
|
err=MathMax(err,MathAbs(0.741640376816388000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,0.648415139929991000,-0.692925257444683000)));
|
|
err=MathMax(err,MathAbs(0.868844042717264000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,1.148153167494120000,0.297877516862745000)));
|
|
err=MathMax(err,MathAbs(0.999356215351682000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,3.221749932900420000,-0.257163446680127000)));
|
|
err=MathMax(err,MathAbs(0.998760969813713000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.111433954663520000,0.198012195099628000)));
|
|
CAp::SetErrorFlag(errorflag,err>(1.0E-12),"testnormaldistrunit.ap:121");
|
|
err=0;
|
|
err=MathMax(err,MathAbs(0.080575405379940000-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,1.523763953950230000,-0.999999999999000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000007-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,-1.060937077356340000,-0.999999999999000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000007-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,-1.060937077354570000,-0.999999999999000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000016-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,0.633583566571867000,-0.999999999990000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,-3.034280037001620000,-0.999999999990000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,-3.034280036974940000,-0.999999999990000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-2.584892928002350000,-0.999999999900000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-3.577198111015300000,-0.999999999900000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-3.577198111015280000,-0.999999999900000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-2.987017621703680000,-0.999999999000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000021-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-1.053171728048290000,-0.999999999000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000014-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-1.052982935276290000,-0.999999999000000000)));
|
|
err=MathMax(err,MathAbs(0.972550843757766000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,1.919693671247900000,-0.999999990000000000)));
|
|
err=MathMax(err,MathAbs(0.999998291644901000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,4.785178416287600000,-0.999999990000000000)));
|
|
err=MathMax(err,MathAbs(0.999998291644901000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,4.785178416295720000,-0.999999990000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,2.398320119295830000,-0.999999900000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,-4.852815283224590000,-0.999999900000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,-4.852815283224590000,-0.999999900000000000)));
|
|
err=MathMax(err,MathAbs(0.576135517318684000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,4.695461487450740000,-0.999999000000000000)));
|
|
err=MathMax(err,MathAbs(0.152273694692214000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,0.192020298597275000,-0.999999000000000000)));
|
|
err=MathMax(err,MathAbs(0.152273697664791000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,0.192020306187062000,-0.999999000000000000)));
|
|
err=MathMax(err,MathAbs(0.869148589647661000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,1.211768206209080000,-0.999990000000000000)));
|
|
err=MathMax(err,MathAbs(0.963898301217005000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,2.095780141251770000,-0.999990000000000000)));
|
|
err=MathMax(err,MathAbs(0.963898301217035000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,2.095780141252440000,-0.999990000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,1.340051767837440000,-0.999900000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,-2.654007687508430000,-0.999900000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,-2.654007613149620000,-0.999900000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000019-CNormalDistr::BivariateNormalCDF(1.128363097510700000,-2.781006578069910000,-0.999000000000000000)));
|
|
err=MathMax(err,MathAbs(0.740833394117601000-CNormalDistr::BivariateNormalCDF(1.128363097510700000,1.128363097510700000,-0.999000000000000000)));
|
|
err=MathMax(err,MathAbs(0.740862731628071000-CNormalDistr::BivariateNormalCDF(1.128363097510700000,1.128502099120660000,-0.999000000000000000)));
|
|
err=MathMax(err,MathAbs(0.995654456762395000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,2.626841258388710000,-0.990000000000000000)));
|
|
err=MathMax(err,MathAbs(0.999927066319845000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,3.966506850977000000,-0.990000000000000000)));
|
|
err=MathMax(err,MathAbs(0.999927066319846000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,3.966506850981670000,-0.990000000000000000)));
|
|
err=MathMax(err,MathAbs(0.002769246133985220-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,3.217920162027340000,-0.900000000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,-2.717530641700190000,-0.900000000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,-2.717530635181090000,-0.900000000000000000)));
|
|
err=MathMax(err,MathAbs(0.010079636581695100-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-2.262911120125770000,0.900000000000000000)));
|
|
err=MathMax(err,MathAbs(0.021493462833838000-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-1.811681729272450000,0.900000000000000000)));
|
|
err=MathMax(err,MathAbs(0.021493462833871500-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-1.811681729271170000,0.900000000000000000)));
|
|
err=MathMax(err,MathAbs(0.667398193291227000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,0.432740073549983000,0.990000000000000000)));
|
|
err=MathMax(err,MathAbs(0.964688949692122000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,1.861468373436860000,0.990000000000000000)));
|
|
err=MathMax(err,MathAbs(0.964688949699170000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,1.861468373617680000,0.990000000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000482786767864-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-3.491204153631050000,0.999000000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000439041574222-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-4.898527851968480000,0.999000000000000000)));
|
|
err=MathMax(err,MathAbs(0.000000439041575582-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-4.898527850755250000,0.999000000000000000)));
|
|
err=MathMax(err,MathAbs(0.000416850246666766-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,1.862802982022170000,0.999900000000000000)));
|
|
err=MathMax(err,MathAbs(0.000408379488679640-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,-3.341356669094100000,0.999900000000000000)));
|
|
err=MathMax(err,MathAbs(0.000408379488680111-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,-3.341356669093450000,0.999900000000000000)));
|
|
err=MathMax(err,MathAbs(0.741641759669778000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,0.648415139929991000,0.999990000000000000)));
|
|
err=MathMax(err,MathAbs(0.999998605095178000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,4.687494092358740000,0.999990000000000000)));
|
|
err=MathMax(err,MathAbs(0.999998606247630000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,4.687843556049730000,0.999990000000000000)));
|
|
err=MathMax(err,MathAbs(0.874547330614982000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,1.148153167494120000,0.999999000000000000)));
|
|
err=MathMax(err,MathAbs(0.991070530231016000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,2.369093655782270000,0.999999000000000000)));
|
|
err=MathMax(err,MathAbs(0.991070530231065000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,2.369093655786410000,0.999999000000000000)));
|
|
err=MathMax(err,MathAbs(0.999362948580782000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,3.221749932900420000,0.999999900000000000)));
|
|
err=MathMax(err,MathAbs(0.999993261271150000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,4.352384277131720000,0.999999900000000000)));
|
|
err=MathMax(err,MathAbs(0.999993261272904000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,4.352384391237480000,0.999999900000000000)));
|
|
err=MathMax(err,MathAbs(0.999069094420465000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.111433954663520000,0.999999990000000000)));
|
|
err=MathMax(err,MathAbs(0.999689455134645000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.422289814750960000,0.999999990000000000)));
|
|
err=MathMax(err,MathAbs(0.999689455134658000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.422289814777010000,0.999999990000000000)));
|
|
err=MathMax(err,MathAbs(0.294719708527162000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,2.177679562057720000,0.999999999000000000)));
|
|
err=MathMax(err,MathAbs(0.294713555379044000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,-0.539648565868907000,0.999999999000000000)));
|
|
err=MathMax(err,MathAbs(0.294718518285085000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,-0.539602058260055000,0.999999999000000000)));
|
|
err=MathMax(err,MathAbs(0.000002811467986141-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,2.436946780486250000,0.999999999900000000)));
|
|
err=MathMax(err,MathAbs(0.000002811392754616-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,-4.540093229575050000,0.999999999900000000)));
|
|
err=MathMax(err,MathAbs(0.000002811467986126-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,-4.540001786779430000,0.999999999900000000)));
|
|
err=MathMax(err,MathAbs(0.565106870340374000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,3.995097146641120000,0.999999999990000000)));
|
|
err=MathMax(err,MathAbs(0.565106168077456000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,0.163929988133744000,0.999999999990000000)));
|
|
err=MathMax(err,MathAbs(0.565106168096292000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,0.163929988229317000,0.999999999990000000)));
|
|
err=MathMax(err,MathAbs(0.000064751025417698-CNormalDistr::BivariateNormalCDF(3.421155338081630000,-3.827403648909790000,0.999999999999000000)));
|
|
err=MathMax(err,MathAbs(0.999688220825439000-CNormalDistr::BivariateNormalCDF(3.421155338081630000,3.421155338081630000,0.999999999999000000)));
|
|
err=MathMax(err,MathAbs(0.999688221472430000-CNormalDistr::BivariateNormalCDF(3.421155338081630000,3.421174755498880000,0.999999999999000000)));
|
|
CAp::SetErrorFlag(errorflag,err>(1.0E-12),"testnormaldistrunit.ap:195");
|
|
//--- Test that BVN PDF is roughly equal to derivative of BVN CDF
|
|
for(int k=0; k<=999; k++)
|
|
{
|
|
//--- Generate trial point
|
|
x=CHighQualityRand::HQRndNormal(rs);
|
|
y=CHighQualityRand::HQRndNormal(rs);
|
|
rho=0;
|
|
//--- Compare two values: normal PDF and differentiation of normal CDF with step H
|
|
h=1.0E-5;
|
|
v0=CNormalDistr::BivariateNormalPDF(x,y,rho);
|
|
v1=(CNormalDistr::BivariateNormalCDF(x+h,y+h,rho)+CNormalDistr::BivariateNormalCDF(x-h,y-h,rho)-CNormalDistr::BivariateNormalCDF(x+h,y-h,rho)-CNormalDistr::BivariateNormalCDF(x-h,y+h,rho))/CMath::Sqr(2*h);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(v0-v1)>(1.0E-4),"testnormaldistrunit.ap:215");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestWSRUnit
|
|
{
|
|
public:
|
|
CTestWSRUnit(void) {};
|
|
~CTestWSRUnit(void) {};
|
|
//---
|
|
static bool TestWSR(bool silent);
|
|
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestWSRUnit::TestWSR(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
CRowDouble xa;
|
|
int n=0;
|
|
int i=0;
|
|
double taill=0;
|
|
double tailr=0;
|
|
double tailb=0;
|
|
double taillprev=0;
|
|
double tailrprev=0;
|
|
double ebase=0;
|
|
double eshift=0;
|
|
|
|
waserrors=false;
|
|
//--- Test monotonicity of tail values for moving value of E
|
|
for(n=5; n<=50; n++)
|
|
{
|
|
//--- Generate uniform and sorted X spanning [0,1]
|
|
xa=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
xa.Set(i,(double)i/(double)(n-1));
|
|
//--- Test N+1 values of E
|
|
ebase=-(0.5/(n-1));
|
|
eshift=(double)1/(double)(n-1);
|
|
tailrprev=0;
|
|
taillprev=1;
|
|
for(i=0; i<=n; i++)
|
|
{
|
|
CWilcoxonSignedRank::WilcoxonSignedRankTest(xa,n,ebase+eshift*i,tailb,taill,tailr);
|
|
CAp::SetErrorFlag(waserrors,tailb!=(2*MathMin(taill,tailr)),"testwsrunit.ap:42");
|
|
CAp::SetErrorFlag(waserrors,tailrprev>tailr,"testwsrunit.ap:43");
|
|
CAp::SetErrorFlag(waserrors,taillprev<taill,"testwsrunit.ap:44");
|
|
tailrprev=tailr;
|
|
taillprev=taill;
|
|
}
|
|
}
|
|
//--- Test for integer overflow in the function: if one crucial
|
|
//--- calculation step is performed in 32-bit integer arithmetics,
|
|
//--- it will return incorrect results.
|
|
//--- We use special handcrafted N, such that in 32-bit integer
|
|
//--- arithmetics int32(N*N)<0. Such negative N leads to domain
|
|
//--- error in the sqrt() function.
|
|
n=50000;
|
|
xa=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xa.Set(i,MathSin(10*i));
|
|
}
|
|
CWilcoxonSignedRank::WilcoxonSignedRankTest(xa,n,0.0,tailb,taill,tailr);
|
|
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailb),"testwsrunit.ap:64");
|
|
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(taill),"testwsrunit.ap:65");
|
|
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailr),"testwsrunit.ap:66");
|
|
|
|
if(!silent)
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMannWhitneyUUnit
|
|
{
|
|
public:
|
|
CTestMannWhitneyUUnit(void) {};
|
|
~CTestMannWhitneyUUnit(void) {};
|
|
//---
|
|
static bool TestMannWhitneyU(bool silent);
|
|
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMannWhitneyUUnit::TestMannWhitneyU(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int testmin=0;
|
|
int testmax=0;
|
|
int testcnt=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int k=0;
|
|
double taill=0;
|
|
double tailr=0;
|
|
double tailb=0;
|
|
double taill1=0;
|
|
double tailr1=0;
|
|
double tailb1=0;
|
|
double taillprev=0;
|
|
double tailrprev=0;
|
|
double ebase=0;
|
|
double eshift=0;
|
|
int ecnt=0;
|
|
double worsterr=0;
|
|
double v=0;
|
|
CHighQualityRandState rs;
|
|
|
|
waserrors=false;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test monotonicity of tail values for monotinically moving distributions.
|
|
for(n=5; n<=20; n++)
|
|
{
|
|
for(m=5; m<=20; m++)
|
|
{
|
|
//--- Generate uniform and sorted X/Y spanning [0,1]
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,(double)i/(double)(n-1)+100*CMath::m_machineepsilon*CHighQualityRand::HQRndNormal(rs));
|
|
y=vector<double>::Zeros(m);
|
|
for(i=0; i<m; i++)
|
|
y.Set(i,(double)i/(double)(m-1)+100*CMath::m_machineepsilon*CHighQualityRand::HQRndNormal(rs));
|
|
//--- Test 100 values of E
|
|
ecnt=100;
|
|
ebase=-1.1;
|
|
eshift=-(2*ebase/(ecnt-1));
|
|
tailrprev=0;
|
|
taillprev=1;
|
|
y+=ebase;
|
|
for(k=0; k<=ecnt-1; k++)
|
|
{
|
|
CMannWhitneyU::CMannWhitneyUTest(x,n,y,m,tailb,taill,tailr);
|
|
CAp::SetErrorFlag(waserrors,tailb!=(2*MathMin(taill,tailr)),"testmannwhitneyuunit.ap:57");
|
|
CAp::SetErrorFlag(waserrors,tailrprev>tailr,"testmannwhitneyuunit.ap:58");
|
|
CAp::SetErrorFlag(waserrors,taillprev<taill,"testmannwhitneyuunit.ap:59");
|
|
tailrprev=tailr;
|
|
taillprev=taill;
|
|
y+=eshift;
|
|
}
|
|
}
|
|
}
|
|
//--- Test frequency of p-value 0.05
|
|
testmin=5;
|
|
testmax=50;
|
|
testcnt=10000;
|
|
worsterr=0.0;
|
|
for(n=testmin; n<=testmax; n++)
|
|
{
|
|
m=n+CHighQualityRand::HQRndUniformI(rs,testmax-n+1);
|
|
x=vector<double>::Zeros(n);
|
|
y=vector<double>::Zeros(m);
|
|
//--- Generate two uniformly distributed values, calculate p-value for both-tails, repeat
|
|
k=0;
|
|
for(pass=0; pass<=testcnt-1; pass++)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
for(i=0; i<m; i++)
|
|
y.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
CMannWhitneyU::CMannWhitneyUTest(x,n,y,m,tailb,taill,tailr);
|
|
if(tailb<0.05)
|
|
k++;
|
|
}
|
|
v=MathAbs((double)k/(double)testcnt-0.05);
|
|
worsterr=MathMax(worsterr,v);
|
|
//--- Test error in quantile; for different N's we have different tolerances
|
|
if(n<10)
|
|
CAp::SetErrorFlag(waserrors,v>0.035,"testmannwhitneyuunit.ap:101");
|
|
else
|
|
{
|
|
if(n<15)
|
|
CAp::SetErrorFlag(waserrors,v>0.025,"testmannwhitneyuunit.ap:103");
|
|
else
|
|
{
|
|
if(n<30)
|
|
CAp::SetErrorFlag(waserrors,v>0.020,"testmannwhitneyuunit.ap:105");
|
|
else
|
|
CAp::SetErrorFlag(waserrors,v>0.015,"testmannwhitneyuunit.ap:107");
|
|
}
|
|
}
|
|
}
|
|
//--- Test symmetry properties
|
|
for(n=5; n<=50; n++)
|
|
{
|
|
for(m=5; m<=50; m++)
|
|
{
|
|
x=vector<double>::Zeros(n);
|
|
y=vector<double>::Zeros(m);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
for(i=0; i<m; i++)
|
|
y.Set(i,CHighQualityRand::HQRndUniformR(rs));
|
|
CMannWhitneyU::CMannWhitneyUTest(x,n,y,m,tailb,taill,tailr);
|
|
CMannWhitneyU::CMannWhitneyUTest(y,m,x,n,tailb1,taill1,tailr1);
|
|
CAp::SetErrorFlag(waserrors,MathAbs(tailb-tailb1)>(1.0E-12),"testmannwhitneyuunit.ap:126");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(taill-tailr1)>(1.0E-12),"testmannwhitneyuunit.ap:127");
|
|
CAp::SetErrorFlag(waserrors,MathAbs(tailr-taill1)>(1.0E-12),"testmannwhitneyuunit.ap:128");
|
|
}
|
|
}
|
|
//--- Test for integer overflow in the function: if one crucial
|
|
//--- calculation step is performed in 32-bit integer arithmetics,
|
|
//--- it will return incorrect results.
|
|
//--- We use special handcrafted N, such that in 32-bit integer
|
|
//--- arithmetics int32(N*N)<0. Such negative N leads to domain
|
|
//--- error in the sqrt() function.
|
|
n=50000;
|
|
x=vector<double>::Zeros(n);
|
|
y=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
x.Set(i,MathSin(10*i));
|
|
y.Set(i,MathSin(13*i));
|
|
}
|
|
CMannWhitneyU::CMannWhitneyUTest(x,n,y,n,tailb,taill,tailr);
|
|
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailb),"testmannwhitneyuunit.ap:149");
|
|
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(taill),"testmannwhitneyuunit.ap:150");
|
|
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailr),"testmannwhitneyuunit.ap:151");
|
|
if(!silent)
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSTestUnit
|
|
{
|
|
public:
|
|
static bool TestSTest(bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSTestUnit::TestSTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors=false;
|
|
double eps=1.0E-3;
|
|
bool result;
|
|
CRowDouble x;
|
|
double taill=0;
|
|
double tailr=0;
|
|
double tailb=0;
|
|
//--- Test 1
|
|
x=vector<double>::Zeros(6);
|
|
x.Set(0,-3);
|
|
x.Set(1,-2);
|
|
x.Set(2,-1);
|
|
x.Set(3,1);
|
|
x.Set(4,2);
|
|
x.Set(5,3);
|
|
CSignTest::OneSampleSignTest(x,6,0.0,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(taill-0.65625)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.65625)>eps;
|
|
waserrors=waserrors||MathAbs(tailb-1.00000)>eps;
|
|
CSignTest::OneSampleSignTest(x,6,-1.0,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(taill-0.81250)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.50000)>eps;
|
|
waserrors=waserrors||MathAbs(tailb-1.00000)>eps;
|
|
CSignTest::OneSampleSignTest(x,6,-1.5,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(taill-0.89062)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.34375)>eps;
|
|
waserrors=waserrors||MathAbs(tailb-0.68750)>eps;
|
|
CSignTest::OneSampleSignTest(x,6,-3.0,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(taill-1.00000)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.03125)>eps;
|
|
waserrors=waserrors||MathAbs(tailb-0.06250)>eps;
|
|
//--- Test 2
|
|
x=vector<double>::Full(3,2);
|
|
CSignTest::OneSampleSignTest(x,3,2.0,tailb,taill,tailr);
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
waserrors=waserrors||tailb!=1.0;
|
|
//--- Final report
|
|
if(!silent)
|
|
{
|
|
PrintResult("SIGN TEST",!waserrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestStudentTestsUnit
|
|
{
|
|
public:
|
|
static bool TestStudentTests(bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestStudentTestsUnit::TestStudentTests(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors=false;
|
|
double eps=0.001;
|
|
bool result;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble xa;
|
|
CRowDouble ya;
|
|
CRowDouble xb;
|
|
CRowDouble yb;
|
|
int n=0;
|
|
int i=0;
|
|
double taill=0;
|
|
double tailr=0;
|
|
double tailb=0;
|
|
double taill1=0;
|
|
double tailr1=0;
|
|
double tailb1=0;
|
|
//--- 1-sample test
|
|
n=8;
|
|
x.Resize(8);
|
|
x.Set(0,-3.0);
|
|
x.Set(1,-1.5);
|
|
x.Set(2,-1.0);
|
|
x.Set(3,-0.5);
|
|
x.Set(4,0.5);
|
|
x.Set(5,1.0);
|
|
x.Set(6,1.5);
|
|
x.Set(7,3.0);
|
|
CStudentTests::StudentTest1(x,n,0.0,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-1.00000)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.50000)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.50000)>eps;
|
|
CStudentTests::StudentTest1(x,n,1.0,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-0.17816)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.08908)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.91092)>eps;
|
|
CStudentTests::StudentTest1(x,n,-1.0,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-0.17816)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.91092)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.08908)>eps;
|
|
x=vector<double>::Full(8,1.1);
|
|
CStudentTests::StudentTest1(x,n,1.1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest1(x,n,0.0,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=0.0;
|
|
x.Set(7,1.1);
|
|
CStudentTests::StudentTest1(x,1,1.1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest1(x,1,0.0,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=0.0;
|
|
//--- 2-sample pooled (equal variance) test
|
|
n=8;
|
|
x.Resize(8);
|
|
y.Resize(8);
|
|
x.Set(0,-3.0);
|
|
x.Set(1,-1.5);
|
|
x.Set(2,-1.0);
|
|
x.Set(3,-0.5);
|
|
x.Set(4,0.5);
|
|
x.Set(5,1.0);
|
|
x.Set(6,1.5);
|
|
x.Set(7,3.0);
|
|
y.Set(0,-2.0);
|
|
y.Set(1,-0.5);
|
|
y.Set(2,0.0);
|
|
y.Set(3,0.5);
|
|
y.Set(4,1.5);
|
|
y.Set(5,2.0);
|
|
y.Set(6,2.5);
|
|
y.Set(7,4.0);
|
|
CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-0.30780)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.15390)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.84610)>eps;
|
|
CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-0.53853)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.26927)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.73074)>eps;
|
|
CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-0.13829)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.06915)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.93086)>eps;
|
|
x=vector<double>::Full(8,-1.0);
|
|
y=vector<double>::Ones(8);
|
|
CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,n,y,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,1,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,1,y,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(y,1,x,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=0.0;
|
|
x=vector<double>::Full(8,1.1);
|
|
y=vector<double>::Full(8,1.1);
|
|
CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,n,y,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,1,y,n,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::StudentTest2(x,1,y,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
//--- 2-sample unpooled (unequal variance) test:
|
|
//--- * test on two non-constant samples
|
|
//--- * tests on different combinations of non-constant and constant samples
|
|
n=8;
|
|
xa.Resize(n);
|
|
ya.Resize(n);
|
|
xb.Resize(n);
|
|
yb.Resize(n);
|
|
xa.Set(0,-3.0);
|
|
xa.Set(1,-1.5);
|
|
xa.Set(2,-1.0);
|
|
xa.Set(3,-0.5);
|
|
xa.Set(4,0.5);
|
|
xa.Set(5,1.0);
|
|
xa.Set(6,1.5);
|
|
xa.Set(7,3.0);
|
|
ya.Set(0,-1.0);
|
|
ya.Set(1,-0.5);
|
|
ya.Set(2,0.0);
|
|
ya.Set(3,0.5);
|
|
ya.Set(4,1.5);
|
|
ya.Set(5,2.0);
|
|
ya.Set(6,2.5);
|
|
ya.Set(7,3.0);
|
|
xb.Set(0,-1.1);
|
|
xb.Set(1,-1.1);
|
|
xb.Set(2,-1.1);
|
|
xb.Set(3,-1.1);
|
|
xb.Set(4,-1.1);
|
|
xb.Set(5,-1.1);
|
|
xb.Set(6,-1.1);
|
|
xb.Set(7,-1.1);
|
|
yb.Set(0,1.1);
|
|
yb.Set(1,1.1);
|
|
yb.Set(2,1.1);
|
|
yb.Set(3,1.1);
|
|
yb.Set(4,1.1);
|
|
yb.Set(5,1.1);
|
|
yb.Set(6,1.1);
|
|
yb.Set(7,1.1);
|
|
CStudentTests::UnequalVarianceTest(xa,n,ya,n,tailb,taill,tailr);
|
|
waserrors=waserrors||MathAbs(tailb-0.25791)>eps;
|
|
waserrors=waserrors||MathAbs(taill-0.12896)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-0.87105)>eps;
|
|
CStudentTests::UnequalVarianceTest(xa,n,yb,n,tailb,taill,tailr);
|
|
CStudentTests::StudentTest1(xa,n,1.1,tailb1,taill1,tailr1);
|
|
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
|
|
waserrors=waserrors||MathAbs(taill-taill1)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-tailr1)>eps;
|
|
CStudentTests::UnequalVarianceTest(xa,n,yb,1,tailb,taill,tailr);
|
|
CStudentTests::StudentTest1(xa,n,1.1,tailb1,taill1,tailr1);
|
|
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
|
|
waserrors=waserrors||MathAbs(taill-taill1)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-tailr1)>eps;
|
|
CStudentTests::UnequalVarianceTest(xb,n,ya,n,tailb,taill,tailr);
|
|
CStudentTests::StudentTest1(ya,n,-1.1,tailb1,taill1,tailr1);
|
|
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
|
|
waserrors=waserrors||MathAbs(taill-tailr1)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-taill1)>eps;
|
|
CStudentTests::UnequalVarianceTest(xb,1,ya,n,tailb,taill,tailr);
|
|
CStudentTests::StudentTest1(ya,n,-1.1,tailb1,taill1,tailr1);
|
|
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
|
|
waserrors=waserrors||MathAbs(taill-tailr1)>eps;
|
|
waserrors=waserrors||MathAbs(tailr-taill1)>eps;
|
|
CStudentTests::UnequalVarianceTest(xb,1,yb,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=0.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
CStudentTests::UnequalVarianceTest(yb,1,xb,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=0.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=0.0;
|
|
CStudentTests::UnequalVarianceTest(xb,1,xb,1,tailb,taill,tailr);
|
|
waserrors=waserrors||tailb!=1.0;
|
|
waserrors=waserrors||taill!=1.0;
|
|
waserrors=waserrors||tailr!=1.0;
|
|
//--- Test for integer overflow in the function: if one crucial
|
|
//--- calculation step is performed in 32-bit integer arithmetics,
|
|
//--- it will return incorrect results.
|
|
//--- We use special handcrafted N, such that in 32-bit integer
|
|
//--- arithmetics int32(N*N)<0. Such negative N leads to domain
|
|
//--- error in the incomplete beta function.
|
|
n=50000;
|
|
xa=vector<double>::Zeros(n);
|
|
ya=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xa.Set(i,CMath::RandomReal());
|
|
ya.Set(i,CMath::RandomReal());
|
|
}
|
|
CStudentTests::UnequalVarianceTest(xa,n,ya,n,tailb,taill,tailr);
|
|
if(!silent)
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFitSphereUnit
|
|
{
|
|
public:
|
|
static bool TestFitSphere(bool silent);
|
|
|
|
private:
|
|
static void TestSphereFittingLS(bool &err);
|
|
static void TestSphereFittingNS(bool &err);
|
|
static void TestSphereFittingVosswinkel2(bool &err);
|
|
static void CalcRadII(CMatrixDouble &xy,int npoints,int nx,CRowDouble &cx,double &rlo,double &rhi);
|
|
static void CalcLSError(CMatrixDouble &xy,int npoints,int nx,CRowDouble &cx,double &err);
|
|
static void AddValue(CMatrixDouble &xy,int &cnt,double v);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFitSphereUnit::TestFitSphere(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nserrors;
|
|
bool lserrors;
|
|
bool waserrors;
|
|
nserrors=false;
|
|
lserrors=false;
|
|
//--- Sphere fitting, several different test suites
|
|
TestSphereFittingLS(lserrors);
|
|
TestSphereFittingNS(nserrors);
|
|
TestSphereFittingVosswinkel2(nserrors);
|
|
//--- report
|
|
waserrors=nserrors||lserrors;
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FITSPHERE");
|
|
PrintResult("* LEAST SQUARES CIRCLE FITTING",!lserrors);
|
|
PrintResult("* NON-SMOOTH FITTING (MC,MI,MZ)",!nserrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests least squares (LS) sphere fitting using |
|
|
//| generic synthetic datasets |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFitSphereUnit::TestSphereFittingLS(bool &err)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
CMatrixDouble xy;
|
|
double xtol=0;
|
|
int npoints=0;
|
|
int nx=0;
|
|
CRowDouble cx;
|
|
CRowDouble cy;
|
|
double rlo=0;
|
|
double rhi=0;
|
|
double ftol=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
int problemtype=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
xtol=1.0E-5;
|
|
//--- Generate random problem
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
//--- Generate synthetic dataset, at least 5 points
|
|
npoints=5+2*nx+CHighQualityRand::HQRndUniformI(rs,50+(int)MathRound(MathPow(4,nx)));
|
|
xy=matrix<double>::Zeros(npoints,nx);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
vv=CHighQualityRand::HQRndNormal(rs);
|
|
v+=CMath::Sqr(vv);
|
|
xy.Set(i,j,vv);
|
|
}
|
|
if(!CAp::Assert(v>0.0))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
v=(1+0.1*CHighQualityRand::HQRndUniformR(rs))/MathSqrt(v);
|
|
for(j=0; j<nx ; j++)
|
|
xy.Mul(i,j,v);
|
|
}
|
|
//--- Solve with generic solver, check
|
|
rlo=0;
|
|
rhi=0;
|
|
problemtype=0;
|
|
CFitSphere::FitSphereX(xy,npoints,nx,problemtype,0.0,0,0.0,cx,rlo,rhi);
|
|
CAp::SetErrorFlag(err,rlo!=rhi,"testfitsphereunit.ap:109");
|
|
vv=0.0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
v=CAblasF::RDotVR(nx,cx,xy,i);
|
|
vv+=MathSqrt(v)/npoints;
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(vv-rlo)>xtol,"testfitsphereunit.ap:118");
|
|
CalcLSError(xy,npoints,nx,cx,v0);
|
|
//--- Check that small perturbations to center position increase target function
|
|
//--- NOTE: in fact, we do allow small increase in target function - but no more
|
|
//--- than FTol=1E-6*XTol. It helps to avoid spurious error reports in
|
|
//--- degenerate cases.
|
|
ftol=1.0E-6*xtol;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
cy=cx;
|
|
cy.Add(j,xtol);
|
|
CalcLSError(xy,npoints,nx,cy,v1);
|
|
CAp::SetErrorFlag(err,v1<(v0-ftol),"testfitsphereunit.ap:137");
|
|
cy=cx;
|
|
cy.Add(j,-xtol);
|
|
CalcLSError(xy,npoints,nx,cy,v1);
|
|
CAp::SetErrorFlag(err,v1<(v0-ftol),"testfitsphereunit.ap:144");
|
|
}
|
|
//--- Compare against results returned by specific solver
|
|
CFitSphere::FitSphereLS(xy,npoints,nx,cy,v);
|
|
CAp::SetErrorFlag(err,v!=rlo,"testfitsphereunit.ap:151");
|
|
for(j=0; j<nx; j++)
|
|
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:153");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests sphere fitting using generic synthetic |
|
|
//| datasets and non - smooth target functions(MC, MI, MZ fitting) |
|
|
//| On failure sets Err to True(leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFitSphereUnit::TestSphereFittingNS(bool &err)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
CMatrixDouble xy;
|
|
int npoints=0;
|
|
int nx=0;
|
|
CRowDouble cx;
|
|
CRowDouble cy;
|
|
double rlo=0;
|
|
double rhi=0;
|
|
double rlo2=0;
|
|
double rhi2=0;
|
|
double xtol=0;
|
|
double ftol=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double vv=0;
|
|
int problemtype=0;
|
|
double vlo=0;
|
|
double vhi=0;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
xtol=1.0E-5;
|
|
//--- Generate random problem
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
//--- Generate synthetic dataset
|
|
npoints=50+(int)MathRound(MathPow(4,nx));
|
|
xy=matrix<double>::Zeros(npoints,nx);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
vv=CHighQualityRand::HQRndNormal(rs);
|
|
v+=CMath::Sqr(vv);
|
|
xy.Set(i,j,vv);
|
|
}
|
|
CAp::Assert(v>0.0);
|
|
v=(1+0.1*CHighQualityRand::HQRndUniformR(rs))/MathSqrt(v);
|
|
for(j=0; j<nx ; j++)
|
|
xy.Mul(i,j,v);
|
|
}
|
|
//--- Perform various kinds of fit, NLC solver is used
|
|
for(problemtype=1; problemtype<=3; problemtype++)
|
|
{
|
|
//--- Solve with generic solver
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereX(xy,npoints,nx,problemtype,0.0,0,0.0,cx,rlo,rhi);
|
|
//--- Check that small perturbations to center position increase target function
|
|
//--- NOTE: in fact, we do allow small increase in target function - but no more
|
|
//--- than FTol=1E-6*XTol. It helps to avoid spurious error reports in
|
|
//--- degenerate cases.
|
|
ftol=1.0E-6*xtol;
|
|
cy=vector<double>::Zeros(nx);
|
|
if(problemtype==2 || problemtype==3)
|
|
vlo=1;
|
|
else
|
|
vlo=0;
|
|
if(problemtype==1 || problemtype==3)
|
|
vhi=1;
|
|
else
|
|
vhi=0;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
cy=cx;
|
|
cy.Add(j,xtol);
|
|
CalcRadII(xy,npoints,nx,cy,rlo2,rhi2);
|
|
CAp::SetErrorFlag(err,(rhi2*vhi-rlo2*vlo)<(rhi*vhi-rlo*vlo-ftol),"testfitsphereunit.ap:245");
|
|
cy=cx;
|
|
cy.Add(j,-xtol);
|
|
CalcRadII(xy,npoints,nx,cy,rlo2,rhi2);
|
|
CAp::SetErrorFlag(err,(rhi2*vhi-rlo2*vlo)<(rhi*vhi-rlo*vlo-ftol),"testfitsphereunit.ap:252");
|
|
}
|
|
//--- Compare against results returned by specific solver
|
|
if(problemtype==1)
|
|
{
|
|
CFitSphere::FitSphereMC(xy,npoints,nx,cy,rhi2);
|
|
CAp::SetErrorFlag(err,rhi2!=rhi,"testfitsphereunit.ap:261");
|
|
for(j=0; j<nx ; j++)
|
|
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:263");
|
|
}
|
|
if(problemtype==2)
|
|
{
|
|
CFitSphere::FitSphereMI(xy,npoints,nx,cy,rlo2);
|
|
CAp::SetErrorFlag(err,rlo2!=rlo,"testfitsphereunit.ap:268");
|
|
for(j=0; j<nx ; j++)
|
|
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:270");
|
|
}
|
|
if(problemtype==3)
|
|
{
|
|
CFitSphere::FitSphereMZ(xy,npoints,nx,cy,rlo2,rhi2);
|
|
CAp::SetErrorFlag(err,rlo2!=rlo,"testfitsphereunit.ap:275");
|
|
CAp::SetErrorFlag(err,rhi2!=rhi,"testfitsphereunit.ap:276");
|
|
for(j=0; j<nx ; j++)
|
|
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:278");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests sphere fitting |
|
|
//| On failure sets Err to True(leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFitSphereUnit::TestSphereFittingVosswinkel2(bool &err)
|
|
{
|
|
//--- create variables
|
|
CHighQualityRandState rs;
|
|
CMatrixDouble xy;
|
|
int cnt=0;
|
|
CRowDouble cx;
|
|
double rlo=0;
|
|
double rhi=0;
|
|
double tol=0;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test problem #2 by Vosswinkel GmbH
|
|
xy=matrix<double>::Zeros(40,2);
|
|
cnt=0;
|
|
AddValue(xy,cnt,0.1026);
|
|
AddValue(xy,cnt,0.000036);
|
|
AddValue(xy,cnt,0.101119);
|
|
AddValue(xy,cnt,0.016144);
|
|
AddValue(xy,cnt,0.096754);
|
|
AddValue(xy,cnt,0.031654);
|
|
AddValue(xy,cnt,0.088981);
|
|
AddValue(xy,cnt,0.045634);
|
|
AddValue(xy,cnt,0.082056);
|
|
AddValue(xy,cnt,0.06008);
|
|
AddValue(xy,cnt,0.074966);
|
|
AddValue(xy,cnt,0.075647);
|
|
AddValue(xy,cnt,0.065);
|
|
AddValue(xy,cnt,0.090471);
|
|
AddValue(xy,cnt,0.052411);
|
|
AddValue(xy,cnt,0.104381);
|
|
AddValue(xy,cnt,0.036436);
|
|
AddValue(xy,cnt,0.114859);
|
|
AddValue(xy,cnt,0.019034);
|
|
AddValue(xy,cnt,0.126577);
|
|
AddValue(xy,cnt,-0.001191);
|
|
AddValue(xy,cnt,0.139295);
|
|
AddValue(xy,cnt,-0.024689);
|
|
AddValue(xy,cnt,0.147143);
|
|
AddValue(xy,cnt,-0.049729);
|
|
AddValue(xy,cnt,0.147861);
|
|
AddValue(xy,cnt,-0.076402);
|
|
AddValue(xy,cnt,0.145907);
|
|
AddValue(xy,cnt,-0.103928);
|
|
AddValue(xy,cnt,0.139553);
|
|
AddValue(xy,cnt,-0.133726);
|
|
AddValue(xy,cnt,0.130429);
|
|
AddValue(xy,cnt,-0.159051);
|
|
AddValue(xy,cnt,0.112298);
|
|
AddValue(xy,cnt,-0.179496);
|
|
AddValue(xy,cnt,0.08821);
|
|
AddValue(xy,cnt,-0.194562);
|
|
AddValue(xy,cnt,0.059989);
|
|
AddValue(xy,cnt,-0.204838);
|
|
AddValue(xy,cnt,0.029135);
|
|
AddValue(xy,cnt,-0.206971);
|
|
AddValue(xy,cnt,-0.00349);
|
|
AddValue(xy,cnt,-0.206207);
|
|
AddValue(xy,cnt,-0.036427);
|
|
AddValue(xy,cnt,-0.197079);
|
|
AddValue(xy,cnt,-0.06806);
|
|
AddValue(xy,cnt,-0.180492);
|
|
AddValue(xy,cnt,-0.096353);
|
|
AddValue(xy,cnt,-0.158203);
|
|
AddValue(xy,cnt,-0.119891);
|
|
AddValue(xy,cnt,-0.132669);
|
|
AddValue(xy,cnt,-0.138375);
|
|
AddValue(xy,cnt,-0.105652);
|
|
AddValue(xy,cnt,-0.152229);
|
|
AddValue(xy,cnt,-0.078587);
|
|
AddValue(xy,cnt,-0.16316);
|
|
AddValue(xy,cnt,-0.049984);
|
|
AddValue(xy,cnt,-0.167084);
|
|
AddValue(xy,cnt,-0.022067);
|
|
AddValue(xy,cnt,-0.165233);
|
|
AddValue(xy,cnt,0.004002);
|
|
AddValue(xy,cnt,-0.16075);
|
|
AddValue(xy,cnt,0.028058);
|
|
AddValue(xy,cnt,-0.151829);
|
|
AddValue(xy,cnt,0.050088);
|
|
AddValue(xy,cnt,-0.141178);
|
|
AddValue(xy,cnt,0.067646);
|
|
AddValue(xy,cnt,-0.124169);
|
|
AddValue(xy,cnt,0.081421);
|
|
AddValue(xy,cnt,-0.10567);
|
|
AddValue(xy,cnt,0.087305);
|
|
AddValue(xy,cnt,-0.082618);
|
|
AddValue(xy,cnt,0.094189);
|
|
AddValue(xy,cnt,-0.064399);
|
|
AddValue(xy,cnt,0.099445);
|
|
AddValue(xy,cnt,-0.047018);
|
|
AddValue(xy,cnt,0.09936);
|
|
AddValue(xy,cnt,-0.028981);
|
|
AddValue(xy,cnt,0.101784);
|
|
AddValue(xy,cnt,-0.012918);
|
|
tol=1.0E-7;
|
|
//--- MZ problem, NLC solver
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereMZ(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rlo,rhi);
|
|
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:395");
|
|
if(err)
|
|
return;
|
|
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.050884688)>tol,"testfitsphereunit.ap:398");
|
|
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011472328)>tol,"testfitsphereunit.ap:399");
|
|
CAp::SetErrorFlag(err,MathAbs(rlo-0.150973382)>tol,"testfitsphereunit.ap:400");
|
|
CAp::SetErrorFlag(err,MathAbs(rhi-0.164374709)>tol,"testfitsphereunit.ap:401");
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),3,0.0,0,0.0,cx,rlo,rhi);
|
|
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:406");
|
|
if(err)
|
|
return;
|
|
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.050884688)>tol,"testfitsphereunit.ap:409");
|
|
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011472328)>tol,"testfitsphereunit.ap:410");
|
|
CAp::SetErrorFlag(err,MathAbs(rlo-0.150973382)>tol,"testfitsphereunit.ap:411");
|
|
CAp::SetErrorFlag(err,MathAbs(rhi-0.164374709)>tol,"testfitsphereunit.ap:412");
|
|
//--- MC problem, NLC solver
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereMC(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rhi);
|
|
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:421");
|
|
if(err)
|
|
return;
|
|
|
|
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.051137580)>tol,"testfitsphereunit.ap:424");
|
|
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011680985)>tol,"testfitsphereunit.ap:425");
|
|
CAp::SetErrorFlag(err,rlo!=0.0,"testfitsphereunit.ap:426");
|
|
CAp::SetErrorFlag(err,MathAbs(rhi-0.164365735)>tol,"testfitsphereunit.ap:427");
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),1,0.0,0,0.0,cx,rlo,rhi);
|
|
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:432");
|
|
if(err)
|
|
return;
|
|
|
|
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.051137580)>tol,"testfitsphereunit.ap:435");
|
|
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011680985)>tol,"testfitsphereunit.ap:436");
|
|
CAp::SetErrorFlag(err,rlo!=0.0,"testfitsphereunit.ap:437");
|
|
CAp::SetErrorFlag(err,MathAbs(rhi-0.164365735)>tol,"testfitsphereunit.ap:438");
|
|
//--- MI problem, NLC solver
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereMI(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rlo);
|
|
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:447");
|
|
if(err)
|
|
return;
|
|
|
|
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.054593489)>tol,"testfitsphereunit.ap:450");
|
|
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.007459466)>tol,"testfitsphereunit.ap:451");
|
|
CAp::SetErrorFlag(err,MathAbs(rlo-0.152429205)>tol,"testfitsphereunit.ap:452");
|
|
CAp::SetErrorFlag(err,rhi!=0.0,"testfitsphereunit.ap:453");
|
|
rlo=0;
|
|
rhi=0;
|
|
CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),2,0.0,0,0.0,cx,rlo,rhi);
|
|
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:458");
|
|
if(err)
|
|
return;
|
|
|
|
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.054593489)>tol,"testfitsphereunit.ap:461");
|
|
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.007459466)>tol,"testfitsphereunit.ap:462");
|
|
CAp::SetErrorFlag(err,MathAbs(rlo-0.152429205)>tol,"testfitsphereunit.ap:463");
|
|
CAp::SetErrorFlag(err,rhi!=0.0,"testfitsphereunit.ap:464");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Used to calculate RLo / Rhi given XY and center position |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFitSphereUnit::CalcRadII(CMatrixDouble &xy,
|
|
int npoints,
|
|
int nx,
|
|
CRowDouble &cx,
|
|
double &rlo,
|
|
double &rhi)
|
|
{
|
|
double v=0;
|
|
|
|
rlo=0;
|
|
rhi=0;
|
|
rlo=CMath::m_maxrealnumber;
|
|
rhi=0;
|
|
|
|
for(int i=0; i<npoints ; i++)
|
|
{
|
|
v=0;
|
|
for(int j=0; j<nx ; j++)
|
|
v+=CMath::Sqr(xy.Get(i,j)-cx[j]);
|
|
v=MathSqrt(v);
|
|
rhi=MathMax(rhi,v);
|
|
rlo=MathMin(rlo,v);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Used to calculate least squares error given XY and center |
|
|
//| position |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFitSphereUnit::CalcLSError(CMatrixDouble &xy,
|
|
int npoints,
|
|
int nx,
|
|
CRowDouble &cx,
|
|
double &err)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double rad=0;
|
|
|
|
err=0;
|
|
rad=0.0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<nx ; j++)
|
|
v+=CMath::Sqr(cx[j]-xy.Get(i,j));
|
|
rad+=MathSqrt(v)/npoints;
|
|
}
|
|
|
|
err=0.0;
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<nx ; j++)
|
|
v+=CMath::Sqr(cx[j]-xy.Get(i,j));
|
|
err+=CMath::Sqr(rad-MathSqrt(v));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Used to initialize dynamic array with constant values |
|
|
//+------------------------------------------------------------------+
|
|
void CTestFitSphereUnit::AddValue(CMatrixDouble &xy,
|
|
int &cnt,
|
|
double v)
|
|
{
|
|
xy.Set(cnt/CAp::Cols(xy),cnt%CAp::Cols(xy),v);
|
|
cnt++;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpline3DUnit
|
|
{
|
|
public:
|
|
static bool TestSpline3D(bool silence);
|
|
|
|
private:
|
|
static bool BasicTest(void);
|
|
static bool TestUnpack(void);
|
|
static bool TestLinTrans(void);
|
|
static bool TestTrilinearreSample(void);
|
|
static void BuildRndGrid(bool isvect,bool reorder,int &n,int &m,int &l,int &d,CRowDouble &x,CRowDouble &y,CRowDouble &z,CRowDouble &f);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline3DUnit::TestSpline3D(bool silence)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool basicerr;
|
|
bool unpackerr;
|
|
bool lintransferr;
|
|
bool trilinreserr;
|
|
|
|
basicerr=BasicTest();
|
|
unpackerr=TestUnpack();
|
|
lintransferr=TestLinTrans();
|
|
trilinreserr=TestTrilinearreSample();
|
|
waserrors=((basicerr||unpackerr)||lintransferr)||trilinreserr;
|
|
if(!silence)
|
|
{
|
|
Print("TESTING 3D SPLINE");
|
|
PrintResult("BASIC TEST",!basicerr);
|
|
PrintResult("UNPACK TEST",!unpackerr);
|
|
PrintResult("LIN_TRANSF TEST",!lintransferr);
|
|
PrintResult("TRILINEAR RESAMPLING TEST",!trilinreserr);
|
|
//--- Summary
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The function does test basic functionality. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline3DUnit::BasicTest(void)
|
|
{
|
|
//--- create variables
|
|
double eps=1000*CMath::m_machineepsilon;
|
|
CSpline3DInterpolant c;
|
|
CSpline3DInterpolant cc;
|
|
CRowDouble vvf;
|
|
double vsf=0;
|
|
int d=0;
|
|
int m=0;
|
|
int n=0;
|
|
int l=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble z;
|
|
CRowDouble sf;
|
|
CRowDouble vf;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int offs=0;
|
|
int di=0;
|
|
double ax=0;
|
|
double ay=0;
|
|
double az=0;
|
|
double axy=0;
|
|
double ayz=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double vz=0;
|
|
//--- Test spline ability to reproduce D-dimensional vector function
|
|
//--- f[idx](x,y,z) = idx+AX*x + AY*y + AZ*z + AXY*x*y + AYZ*y*z
|
|
//--- with random AX/AY/...
|
|
//--- We generate random test function, build spline, then evaluate
|
|
//--- it in the random test point.
|
|
for(d=1; d<=3; d++)
|
|
{
|
|
n=2+CMath::RandomInteger(4);
|
|
m=2+CMath::RandomInteger(4);
|
|
l=2+CMath::RandomInteger(4);
|
|
x=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,i);
|
|
y=vector<double>::Zeros(m);
|
|
for(i=0; i<m; i++)
|
|
y.Set(i,i);
|
|
z=vector<double>::Zeros(l);
|
|
for(i=0; i<l; i++)
|
|
z.Set(i,i);
|
|
vf=vector<double>::Zeros(l*m*n*d);
|
|
offs=0;
|
|
ax=2*CMath::RandomReal()-1;
|
|
ay=2*CMath::RandomReal()-1;
|
|
az=2*CMath::RandomReal()-1;
|
|
axy=2*CMath::RandomReal()-1;
|
|
ayz=2*CMath::RandomReal()-1;
|
|
for(k=0; k<l; k++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
for(i=0; i<n; i++)
|
|
for(di=0; di<d; di++)
|
|
{
|
|
vf.Set(offs,di+ax*i+ay*j+az*k+axy*i*j+ayz*j*k);
|
|
offs++;
|
|
}
|
|
}
|
|
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c);
|
|
vx=CMath::RandomReal()*n;
|
|
vy=CMath::RandomReal()*m;
|
|
vz=CMath::RandomReal()*l;
|
|
CSpline3D::Spline3DCalcV(c,vx,vy,vz,vf);
|
|
for(di=0; di<d; di++)
|
|
{
|
|
if(MathAbs(di+ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vf[di])>eps)
|
|
return(true);
|
|
}
|
|
if(d==1)
|
|
{
|
|
vsf=CSpline3D::Spline3DCalc(c,vx,vy,vz);
|
|
if(MathAbs(ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vsf)>eps)
|
|
return(true);
|
|
}
|
|
}
|
|
//--- Generate random grid and test function.
|
|
//--- Test spline ability to reproduce function values at grid nodes.
|
|
passcount=20;
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Prepare a model and check that functions (Spline3DBuildTrilinear,
|
|
//--- Spline3DCalc,Spline3DCalcV) work correctly and
|
|
BuildRndGrid(true,true,n,m,l,d,x,y,z,vf);
|
|
CApServ::RVectorSetLengthAtLeast(sf,n*m*l);
|
|
//--- Check that the model's values are equal to the function's values
|
|
//--- in grid points
|
|
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
for(k=0; k<l; k++)
|
|
{
|
|
CSpline3D::Spline3DCalcV(c,x[i],y[j],z[k],vvf);
|
|
for(di=0; di<d; di++)
|
|
if((double)(MathAbs(vf[d*(n*(m*k+j)+i)+di]-vvf[di]))>eps)
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpack / UnpackV test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline3DUnit::TestUnpack(void)
|
|
{
|
|
//--- create variables
|
|
int passcount=20;
|
|
bool result;
|
|
CSpline3DInterpolant c;
|
|
CMatrixDouble tbl0;
|
|
CMatrixDouble tbl1;
|
|
int n=0;
|
|
int m=0;
|
|
int l=0;
|
|
int d=0;
|
|
int sz=0;
|
|
int un=0;
|
|
int um=0;
|
|
int ul=0;
|
|
int ud=0;
|
|
int ust=0;
|
|
int uvn=0;
|
|
int uvm=0;
|
|
int uvl=0;
|
|
int uvd=0;
|
|
int uvst=0;
|
|
int ci=0;
|
|
int cj=0;
|
|
int ck=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble z;
|
|
CRowDouble sf;
|
|
CRowDouble vf;
|
|
int p0=0;
|
|
int p1=0;
|
|
double tx=0;
|
|
double ty=0;
|
|
double tz=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double err=0;
|
|
int pass=0;
|
|
bool bperr;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int di=0;
|
|
int i0=0;
|
|
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- generate random grid.
|
|
//--- NOTE: for this test we need ordered grid, i.e. grid
|
|
//--- with nodes in ascending order
|
|
BuildRndGrid(true,false,n,m,l,d,x,y,z,vf);
|
|
sz=n*m*l;
|
|
CApServ::RVectorSetLengthAtLeast(sf,sz);
|
|
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c);
|
|
CSpline3D::Spline3DUnpackV(c,uvn,uvm,uvl,uvd,uvst,tbl0);
|
|
for(di=0; di<d; di++)
|
|
{
|
|
//--- DI-th component copy of a vector-function to
|
|
//--- a scalar function
|
|
for(i=0; i<=sz-1; i++)
|
|
sf.Set(i,vf[d*i+di]);
|
|
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,sf,1,c);
|
|
CSpline3D::Spline3DUnpackV(c,un,um,ul,ud,ust,tbl1);
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
for(j=0; j<=m-2; j++)
|
|
{
|
|
for(k=0; k<=l-2; k++)
|
|
{
|
|
p1=(n-1)*((m-1)*k+j)+i;
|
|
p0=d*p1+di;
|
|
//--- Check that all components are correct:
|
|
//--- *first check, that unpacked componets are equal
|
|
//--- to packed components;
|
|
bperr=(un!=n || um!=m || ul!=l || tbl1.Get(p1,0)!=x[i] || tbl1.Get(p1,1)!=x[i+1] || tbl1.Get(p1,2)!=y[j] ||
|
|
tbl1.Get(p1,3)!=y[j+1] || tbl1.Get(p1,4)!=z[k] || tbl1.Get(p1,5)!=z[k+1] || uvn!=n || uvm!=m ||
|
|
uvl!=l || uvd!=d || tbl0.Get(p0,0)!=x[i] || tbl0.Get(p0,1)!=x[i+1] || tbl0.Get(p0,2)!=y[j] ||
|
|
tbl0.Get(p0,3)!=y[j+1] || tbl0.Get(p0,4)!=z[k] || tbl0.Get(p0,5)!=z[k+1]);
|
|
//--- *check, that all components unpacked by Unpack
|
|
//--- function are equal to all components unpacked
|
|
//--- by UnpackV function.
|
|
for(i0=0; i0<=13; i0++)
|
|
bperr=bperr||tbl0.Get(p0,i0)!=tbl1.Get(p1,i0);
|
|
if(bperr)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
tx=(0.001+0.999*CMath::RandomReal())*(tbl1.Get(p1,1)-tbl1.Get(p1,0));
|
|
ty=(0.001+0.999*CMath::RandomReal())*(tbl1.Get(p1,3)-tbl1.Get(p1,2));
|
|
tz=(0.001+0.999*CMath::RandomReal())*(tbl1.Get(p1,5)-tbl1.Get(p1,4));
|
|
//--- Interpolation properties for:
|
|
//--- *scalar function;
|
|
v1=0;
|
|
for(ci=0; ci<=1; ci++)
|
|
{
|
|
for(cj=0; cj<=1; cj++)
|
|
for(ck=0; ck<=1; ck++)
|
|
v1+=tbl1.Get(p1,6+2*(2*ck+cj)+ci)*MathPow(tx,ci)*MathPow(ty,cj)*MathPow(tz,ck);
|
|
}
|
|
v2=CSpline3D::Spline3DCalc(c,tbl1.Get(p1,0)+tx,tbl1.Get(p1,2)+ty,tbl1.Get(p1,4)+tz);
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
//--- *component of vector function.
|
|
v1=0;
|
|
for(ci=0; ci<=1; ci++)
|
|
{
|
|
for(cj=0; cj<=1; cj++)
|
|
for(ck=0; ck<=1; ck++)
|
|
v1+=tbl0.Get(p0,6+2*(2*ck+cj)+ci)*MathPow(tx,ci)*MathPow(ty,cj)*MathPow(tz,ck);
|
|
}
|
|
v2=CSpline3D::Spline3DCalc(c,tbl0.Get(p0,0)+tx,tbl0.Get(p0,2)+ty,tbl0.Get(p0,4)+tz);
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=err>(double)(1.0E+5*CMath::m_machineepsilon);
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LinTrans test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline3DUnit::TestLinTrans(void)
|
|
{
|
|
//--- create variables
|
|
int passcount=15;
|
|
bool result;
|
|
CSpline3DInterpolant c;
|
|
CSpline3DInterpolant c2;
|
|
int m=0;
|
|
int n=0;
|
|
int l=0;
|
|
int d=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble z;
|
|
CRowDouble f;
|
|
double a1=0;
|
|
double a2=0;
|
|
double a3=0;
|
|
double b1=0;
|
|
double b2=0;
|
|
double b3=0;
|
|
double tx=0;
|
|
double ty=0;
|
|
double tz=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double vz=0;
|
|
CRowDouble v1;
|
|
CRowDouble v2;
|
|
int pass=0;
|
|
int xjob=0;
|
|
int yjob=0;
|
|
int zjob=0;
|
|
double err=0;
|
|
int i=0;
|
|
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
BuildRndGrid(true,false,n,m,l,d,x,y,z,f);
|
|
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,f,d,c);
|
|
for(xjob=0; xjob<=1; xjob++)
|
|
{
|
|
for(yjob=0; yjob<=1; yjob++)
|
|
{
|
|
for(zjob=0; zjob<=1; zjob++)
|
|
{
|
|
//--- Prepare
|
|
do
|
|
{
|
|
a1=2.0*CMath::RandomReal()-1.0;
|
|
}
|
|
while(a1==0.0);
|
|
a1=a1*xjob;
|
|
b1=x[0]+CMath::RandomReal()*(x[n-1]-x[0]+2.0)-1.0;
|
|
do
|
|
{
|
|
a2=2.0*CMath::RandomReal()-1.0;
|
|
}
|
|
while(a2==0.0);
|
|
a2=a2*yjob;
|
|
b2=y[0]+CMath::RandomReal()*(y[m-1]-y[0]+2.0)-1.0;
|
|
do
|
|
{
|
|
a3=2.0*CMath::RandomReal()-1.0;
|
|
}
|
|
while(a3==0.0);
|
|
a3=a3*zjob;
|
|
b3=z[0]+CMath::RandomReal()*(z[l-1]-z[0]+2.0)-1.0;
|
|
//--- Test XYZ
|
|
CSpline3D::Spline3DCopy(c,c2);
|
|
CSpline3D::Spline3DLinTransXYZ(c2,a1,b1,a2,b2,a3,b3);
|
|
tx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]);
|
|
ty=y[0]+CMath::RandomReal()*(y[m-1]-y[0]);
|
|
tz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]);
|
|
if(xjob==0)
|
|
{
|
|
tx=b1;
|
|
vx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]);
|
|
}
|
|
else
|
|
vx=(tx-b1)/a1;
|
|
if(yjob==0)
|
|
{
|
|
ty=b2;
|
|
vy=y[0]+CMath::RandomReal()*(y[m-1]-y[0]);
|
|
}
|
|
else
|
|
vy=(ty-b2)/a2;
|
|
if(zjob==0)
|
|
{
|
|
tz=b3;
|
|
vz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]);
|
|
}
|
|
else
|
|
vz=(tz-b3)/a3;
|
|
CSpline3D::Spline3DCalcV(c,tx,ty,tz,v1);
|
|
CSpline3D::Spline3DCalcV(c2,vx,vy,vz,v2);
|
|
for(i=0; i<d; i++)
|
|
err=MathMax(err,MathAbs(v1[i]-v2[i]));
|
|
if(err>(double)(1.0E+4*CMath::m_machineepsilon))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Test F
|
|
CSpline3D::Spline3DCopy(c,c2);
|
|
CSpline3D::Spline3DLinTransF(c2,a1,b1);
|
|
tx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]);
|
|
ty=y[0]+CMath::RandomReal()*(y[m-1]-y[0]);
|
|
tz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]);
|
|
CSpline3D::Spline3DCalcV(c,tx,ty,tz,v1);
|
|
CSpline3D::Spline3DCalcV(c2,tx,ty,tz,v2);
|
|
for(i=0; i<d; i++)
|
|
err=MathMax(err,MathAbs(a1*v1[i]+b1-v2[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=err>(1.0E+4*CMath::m_machineepsilon);
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Resample test |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSpline3DUnit::TestTrilinearreSample(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int passcount=20;
|
|
CSpline3DInterpolant c;
|
|
int n=0;
|
|
int m=0;
|
|
int l=0;
|
|
int n2=0;
|
|
int m2=0;
|
|
int l2=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble z;
|
|
CRowDouble f;
|
|
CRowDouble fr;
|
|
double v1=0;
|
|
double v2=0;
|
|
double err=0;
|
|
double mf=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
n=CMath::RandomInteger(4)+2;
|
|
m=CMath::RandomInteger(4)+2;
|
|
l=CMath::RandomInteger(4)+2;
|
|
n2=CMath::RandomInteger(4)+2;
|
|
m2=CMath::RandomInteger(4)+2;
|
|
l2=CMath::RandomInteger(4)+2;
|
|
CApServ::RVectorSetLengthAtLeast(x,n);
|
|
CApServ::RVectorSetLengthAtLeast(y,m);
|
|
CApServ::RVectorSetLengthAtLeast(z,l);
|
|
CApServ::RVectorSetLengthAtLeast(f,n*m*l);
|
|
for(i=0; i<n; i++)
|
|
x.Set(i,(double)i/(double)(n-1));
|
|
for(i=0; i<m; i++)
|
|
y.Set(i,(double)i/(double)(m-1));
|
|
for(i=0; i<l; i++)
|
|
z.Set(i,(double)i/(double)(l-1));
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
for(k=0; k<l; k++)
|
|
f.Set(n*(m*k+j)+i,2*CMath::RandomReal()-1);
|
|
}
|
|
CSpline3D::Spline3DResampleTrilinear(f,l,m,n,l2,m2,n2,fr);
|
|
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,f,1,c);
|
|
err=0;
|
|
mf=0;
|
|
for(i=0; i<n2 ; i++)
|
|
{
|
|
for(j=0; j<=m2-1; j++)
|
|
for(k=0; k<=l2-1; k++)
|
|
{
|
|
v1=CSpline3D::Spline3DCalc(c,(double)i/(double)(n2-1),(double)j/(double)(m2-1),(double)k/(double)(l2-1));
|
|
v2=fr[n2*(m2*k+j)+i];
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
mf=MathMax(mf,MathAbs(v1));
|
|
}
|
|
}
|
|
result=result||(err/mf)>(1.0E+4*CMath::m_machineepsilon);
|
|
if(result)
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The function does build random function on random grid with |
|
|
//| random number of points: |
|
|
//| * N, M, K - random from 2 to 5 |
|
|
//| * D - 1 in case IsVect=False, 1..3 in case |
|
|
//| IsVect=True |
|
|
//| * X, Y, Z - each variable spans from MinV to MaxV, with |
|
|
//| MinV is random number from [-1.5, 0.5] and |
|
|
//| MaxV is random number from [0.5, 1.5]. All |
|
|
//| nodes are well separated. All nodes are |
|
|
//| randomly reordered in case Reorder=False. |
|
|
//| When Reorder=True, nodes are returned in |
|
|
//| ascending order. |
|
|
//| * F - random values from [-1, +1] |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSpline3DUnit::BuildRndGrid(bool isvect,bool reorder,
|
|
int &n,int &m,int &l,int &d,
|
|
CRowDouble &x,CRowDouble &y,
|
|
CRowDouble &z,CRowDouble &f)
|
|
{
|
|
//--- create variables
|
|
double st=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int di=0;
|
|
double v=0;
|
|
double mx=0;
|
|
double maxv=0;
|
|
double minv=0;
|
|
|
|
n=0;
|
|
m=0;
|
|
l=0;
|
|
d=0;
|
|
st=0.3;
|
|
m=CMath::RandomInteger(4)+2;
|
|
n=CMath::RandomInteger(4)+2;
|
|
l=CMath::RandomInteger(4)+2;
|
|
|
|
if(isvect)
|
|
d=CMath::RandomInteger(3)+1;
|
|
else
|
|
d=1;
|
|
CApServ::RVectorSetLengthAtLeast(x,n);
|
|
CApServ::RVectorSetLengthAtLeast(y,m);
|
|
CApServ::RVectorSetLengthAtLeast(z,l);
|
|
CApServ::RVectorSetLengthAtLeast(f,n*m*l*d);
|
|
//--- Fill X
|
|
x.Set(0,0);
|
|
for(i=1; i<n; i++)
|
|
x.Set(i,x[i-1]+st+CMath::RandomReal());
|
|
minv=-0.5-CMath::RandomReal();
|
|
maxv=0.5+CMath::RandomReal();
|
|
mx=x[n-1];
|
|
x=x/mx*(maxv-minv)+minv;
|
|
if(reorder)
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
x.Swap(i,k);
|
|
}
|
|
//--- Fill Y
|
|
y.Set(0,0);
|
|
for(i=1; i<m; i++)
|
|
y.Set(i,y[i-1]+st+CMath::RandomReal());
|
|
minv=-0.5-CMath::RandomReal();
|
|
maxv=0.5+CMath::RandomReal();
|
|
mx=y[m-1];
|
|
y=y/mx*(maxv-minv)+minv;
|
|
if(reorder)
|
|
for(i=0; i<m; i++)
|
|
{
|
|
k=CMath::RandomInteger(m);
|
|
y.Swap(i,k);
|
|
}
|
|
//--- Fill Z
|
|
z.Set(0,0);
|
|
for(i=1; i<l; i++)
|
|
z.Set(i,z[i-1]+st+CMath::RandomReal());
|
|
minv=-0.5-CMath::RandomReal();
|
|
maxv=0.5+CMath::RandomReal();
|
|
mx=z[l-1];
|
|
z=z/mx*(maxv-minv)+minv;
|
|
if(reorder)
|
|
for(i=0; i<l; i++)
|
|
{
|
|
k=CMath::RandomInteger(l);
|
|
z.Swap(i,k);
|
|
}
|
|
//--- Fill F
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<m; j++)
|
|
for(k=0; k<l; k++)
|
|
for(di=0; di<d; di++)
|
|
f.Set(d*(n*(m*k+j)+i)+di,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestRBFUnit
|
|
{
|
|
public:
|
|
static const int m_algorithmscnt;
|
|
static const int m_xrbfalgocnt;
|
|
static const int m_xrbfc1algocnt;
|
|
static const int m_xrbfsmoothingalgocnt;
|
|
static const double m_tol;
|
|
static const int m_mxits;
|
|
static const double m_heps;
|
|
|
|
static bool TestRBF(bool silent);
|
|
static bool SqrDegMatrixRBFTest(bool silent);
|
|
static bool BasicMultilayErRBF1DTest(void);
|
|
|
|
private:
|
|
static bool SpecialTest(void);
|
|
static bool BasicRBFTest(void);
|
|
static bool IrregularRBFTest(void);
|
|
static bool LinearityModelRBFTest(void);
|
|
static bool SerializationTest(void);
|
|
static bool SearcherR(const CMatrixDouble &y0,const CMatrixDouble &y1,int n,int ny,int errtype,const CRowDouble &b1,const CRowDouble &delta);
|
|
static bool BasicMultilayerRBFTest(void);
|
|
static void GridCalc23Test(bool &errorflag);
|
|
static bool BasicXRBFTest(void);
|
|
static bool ScaledXRBFTest(void);
|
|
static bool GridXRBFTest(void);
|
|
static bool BasicHRBFTest(void);
|
|
static bool ScaleHRBFTtest(void);
|
|
static bool SpecHRBFTest(void);
|
|
static bool GridHRBFTest(void);
|
|
static void GradTest(bool &err);
|
|
static void GenerateNearlyRegularGrid(int n,int nx,int ny,CHighQualityRandState &rs,CMatrixDouble &xy,double &meanseparation);
|
|
static void GenerateGoodRandomGrid(int n,int nx,int ny,CHighQualityRandState &rs,CMatrixDouble &xy,double &meanseparation);
|
|
static void GenerateSeparatedWithInbox(CMatrixDouble &xy,int n,int nx,double mindist,CHighQualityRandState &rs,CRowDouble &x);
|
|
static void SetAlgorithm(CRBFModel &s,int n,int nx,int ny,bool hasscale,int algoidx,double meanpointsseparation,bool &hasgradientatnodes,bool &hashessianatnodes);
|
|
static void SetXRBFAlgorithmExact(CRBFModel &s,int algoidx,double meanpointsseparation);
|
|
static void SetXRBFAlgorithmSmoothing(CRBFModel &s,int algoidx,double lambdav,double meanpointsseparation);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestRBFUnit::m_algorithmscnt=6;
|
|
const int CTestRBFUnit::m_xrbfalgocnt=3;
|
|
const int CTestRBFUnit::m_xrbfc1algocnt=2;
|
|
const int CTestRBFUnit::m_xrbfsmoothingalgocnt=3;
|
|
const double CTestRBFUnit::m_tol=1.0E-10;
|
|
const int CTestRBFUnit::m_mxits=0;
|
|
const double CTestRBFUnit::m_heps=1.0E-12;
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::TestRBF(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool specialerrors;
|
|
bool basicrbferrors;
|
|
bool irregularrbferrors;
|
|
bool linearitymodelrbferr;
|
|
bool sqrdegmatrixrbferr;
|
|
bool sererrors;
|
|
bool multilayerrbf1derrors;
|
|
bool multilayerrbferrors;
|
|
bool gridcalc23errors;
|
|
bool graderrors;
|
|
bool hrbfbasicerrors;
|
|
bool hrbfscaleerrors;
|
|
bool hrbfspecerrors;
|
|
bool hrbfgriderrors;
|
|
bool hrbferrors;
|
|
bool xrbfbasicerrors;
|
|
bool xrbfscaleerrors;
|
|
bool xrbfgriderrors;
|
|
bool xrbferrors;
|
|
bool waserrors;
|
|
|
|
graderrors=false;
|
|
//--- Shared tests
|
|
GradTest(graderrors);
|
|
//--- XRBF tests
|
|
xrbfbasicerrors=BasicXRBFTest();
|
|
xrbfscaleerrors=ScaledXRBFTest();
|
|
xrbfgriderrors=GridXRBFTest();
|
|
xrbferrors=(xrbfbasicerrors||xrbfscaleerrors||xrbfgriderrors);
|
|
//--- HRBF tests
|
|
hrbfbasicerrors=BasicHRBFTest();
|
|
hrbfspecerrors=SpecHRBFTest();
|
|
hrbfscaleerrors=ScaleHRBFTtest();
|
|
hrbfgriderrors=GridHRBFTest();
|
|
hrbferrors=(hrbfbasicerrors||hrbfspecerrors||hrbfscaleerrors||hrbfgriderrors);
|
|
//--- Other tests
|
|
specialerrors=SpecialTest();
|
|
basicrbferrors=BasicRBFTest();
|
|
irregularrbferrors=IrregularRBFTest();
|
|
linearitymodelrbferr=LinearityModelRBFTest();
|
|
sqrdegmatrixrbferr=SqrDegMatrixRBFTest(silent);
|
|
multilayerrbf1derrors=false;
|
|
multilayerrbferrors=BasicMultilayerRBFTest();
|
|
sererrors=SerializationTest();
|
|
gridcalc23errors=false;
|
|
GridCalc23Test(gridcalc23errors);
|
|
//--- report
|
|
waserrors=(graderrors||specialerrors||basicrbferrors||irregularrbferrors||linearitymodelrbferr||sqrdegmatrixrbferr ||
|
|
sererrors||multilayerrbf1derrors||multilayerrbferrors||gridcalc23errors||hrbferrors||xrbferrors);
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RBF");
|
|
Print("GENERAL TESTS:");
|
|
PrintResult("* differentiation test",!graderrors);
|
|
PrintResult("* serialization test",!sererrors);
|
|
PrintResult("* special properties",!specialerrors);
|
|
Print("RBF-V3:");
|
|
PrintResult("* basic XRBF test",!xrbfbasicerrors);
|
|
PrintResult("* scale-related tests",!xrbfscaleerrors);
|
|
PrintResult("* grid calculation tests",!xrbfgriderrors);
|
|
Print("RBF-V2:");
|
|
PrintResult("* basic HRBF test",!hrbfbasicerrors);
|
|
PrintResult("* scale-related tests",!hrbfscaleerrors);
|
|
PrintResult("* grid calculation tests",!hrbfgriderrors);
|
|
PrintResult("* special properties",!hrbfspecerrors);
|
|
Print("RBF-V1:");
|
|
PrintResult("* basicRBFTest",!basicrbferrors);
|
|
PrintResult("* irregularRBFTest",!irregularrbferrors);
|
|
PrintResult("* linearity test",!linearitymodelrbferr);
|
|
PrintResult("* SqrDegMatrixRBFTest",!sqrdegmatrixrbferr);
|
|
PrintResult("* MultiLayerRBFErrors in 1D test",!multilayerrbf1derrors);
|
|
PrintResult("* MultiLayerRBFErrors in 2-3D test",!multilayerrbferrors);
|
|
PrintResult("* GridCalc2/3V",!gridcalc23errors);
|
|
//--- was errors?
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test has to check, that algorithm can solve problems of |
|
|
//| matrix are degenerate. |
|
|
//| * used model with linear term; |
|
|
//| * points locate in a subspace of dimension less than an |
|
|
//| original space. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::SqrDegMatrixRBFTest(bool silent)
|
|
{
|
|
//--- create variables
|
|
double zx=10;
|
|
double px=15;
|
|
double zy=10;
|
|
double py=15;
|
|
double eps=1.0E-6;
|
|
int ny=1;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int nx=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
int np=0;
|
|
double sx=0;
|
|
double sy=0;
|
|
double q=0;
|
|
double z=0;
|
|
CRowDouble point;
|
|
CMatrixDouble a;
|
|
CRowDouble d0;
|
|
CRowDouble d1;
|
|
int gen=0;
|
|
CRowDouble pvd0;
|
|
CRowDouble pvd1;
|
|
double pvdnorm=0;
|
|
double vnorm=0;
|
|
double dd0=0;
|
|
double dd1=0;
|
|
CMatrixDouble gp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int unx=0;
|
|
int uny=0;
|
|
CMatrixDouble xwr;
|
|
CMatrixDouble v;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int modelversion=0;
|
|
int i_=0;
|
|
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
//--- prepare test problem
|
|
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
|
|
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
point=vector<double>::Zeros(nx);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits);
|
|
q=0.25+CMath::RandomReal();
|
|
z=4.5+CMath::RandomReal();
|
|
CRBF::RBFSetAlgoQNN(s,q,z);
|
|
//--- start points for grid
|
|
for(i=0; i<nx ; i++)
|
|
point.Set(i,sx*(2*CMath::RandomReal()-1));
|
|
if(nx==2)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
CMatGen::RMatrixRndOrthogonal(nx,a);
|
|
d0=vector<double>::Zeros(nx);
|
|
for(i_=0; i_<nx ; i_++)
|
|
d0.Set(i_,a.Get(i_,0));
|
|
np=k0;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
gp.Set(i,0,point[0]+sx*i*d0[0]);
|
|
gp.Set(i,1,point[1]+sx*i*d0[1]);
|
|
for(k=0; k<ny; k++)
|
|
gp.Set(i,nx+k,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
for(i=0; i<np; i++)
|
|
{
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(nx==3)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
for(gen=1; gen<=2; gen++)
|
|
{
|
|
CMatGen::RMatrixRndOrthogonal(nx,a);
|
|
d0=vector<double>::Zeros(nx);
|
|
for(i_=0; i_<nx ; i_++)
|
|
d0.Set(i_,a.Get(i_,0));
|
|
//--- create grid
|
|
np=-1;
|
|
if(gen==1)
|
|
{
|
|
np=k0;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
gp.Set(i,0,point[0]+sx*i*d0[0]);
|
|
gp.Set(i,1,point[1]+sx*i*d0[1]);
|
|
gp.Set(i,2,point[2]+sx*i*d0[2]);
|
|
for(k=0; k<ny; k++)
|
|
gp.Set(i,nx+k,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
if(gen==2)
|
|
{
|
|
d1=vector<double>::Zeros(nx);
|
|
for(i_=0; i_<nx ; i_++)
|
|
d1.Set(i_,a.Get(i_,1));
|
|
np=k0*k1;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
{
|
|
gp.Set(i*k1+j,0,sx*i*d0[0]+sx*j*d1[0]);
|
|
gp.Set(i*k1+j,1,sx*i*d0[1]+sx*j*d1[1]);
|
|
gp.Set(i*k1+j,2,sx*i*d0[2]+sx*j*d1[2]);
|
|
for(k=0; k<ny; k++)
|
|
gp.Set(i*k1+j,nx+k,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
}
|
|
if(!CAp::Assert(np>=0,"rbf test: integrity error"))
|
|
return(true);
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
for(i=0; i<np; i++)
|
|
{
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
x.Set(2,gp.Get(i,2));
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
return(true);
|
|
}
|
|
}
|
|
if(gen==2)
|
|
{
|
|
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
|
|
dd0=(d0[0]*v.Get(0,0)+d0[1]*v.Get(0,1)+d0[2]*v.Get(0,2))/(d0[0]*d0[0]+d0[1]*d0[1]+d0[2]*d0[2]);
|
|
dd1=(d1[0]*v.Get(0,0)+d1[1]*v.Get(0,1)+d1[2]*v.Get(0,2))/(d1[0]*d1[0]+d1[1]*d1[1]+d1[2]*d1[2]);
|
|
pvd0=vector<double>::Zeros(nx);
|
|
pvd1=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
{
|
|
pvd0.Set(i,dd0*d0[i]);
|
|
pvd1.Set(i,dd1*d1[i]);
|
|
}
|
|
pvdnorm=MathSqrt(CMath::Sqr(v.Get(0,0)-pvd0[0]-pvd1[0])+CMath::Sqr(v.Get(0,1)-pvd0[1]-pvd1[1])+CMath::Sqr(v.Get(0,2)-pvd0[2]-pvd1[2]));
|
|
vnorm=MathSqrt(CMath::Sqr(v.Get(0,0))+CMath::Sqr(v.Get(0,1))+CMath::Sqr(v.Get(0,2)));
|
|
if(pvdnorm>(vnorm*m_tol))
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functionality of RBF module on regular|
|
|
//| grids with multi-layer algorithm in 1D. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::BasicMultilayErRBF1DTest(void)
|
|
{
|
|
//--- create variables
|
|
double a=1.0;
|
|
double b=1.0/9.0;
|
|
double f1=1.0;
|
|
double f2=10.0;
|
|
int passcount=5;
|
|
int n=100;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int nx=0;
|
|
int ny=0;
|
|
int linterm=0;
|
|
double q=0;
|
|
double r=0;
|
|
int errtype=0;
|
|
CRowDouble delta;
|
|
int nlayers=0;
|
|
CRowDouble a1;
|
|
CRowDouble b1;
|
|
CMatrixDouble gp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CMatrixDouble mody0;
|
|
CMatrixDouble mody1;
|
|
CMatrixDouble gy;
|
|
CRowDouble gpgx0;
|
|
CRowDouble gpgx1;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
|
|
gpgx0=vector<double>::Zeros(n);
|
|
gpgx1=vector<double>::Zeros(n);
|
|
for(i=0; i<n; i++)
|
|
gpgx0.Set(i,(double)i/(double)n);
|
|
r=1;
|
|
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
nx=CMath::RandomInteger(2)+2;
|
|
ny=CMath::RandomInteger(3)+1;
|
|
linterm=CMath::RandomInteger(3)+1;
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
a1=vector<double>::Zeros(ny);
|
|
b1=vector<double>::Zeros(ny);
|
|
delta=vector<double>::Zeros(ny);
|
|
mody0=matrix<double>::Zeros(n,ny);
|
|
mody1=matrix<double>::Zeros(n,ny);
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
a1.Set(i,a+0.01*a*(2*CMath::RandomReal()-1));
|
|
b1.Set(i,b+0.01*b*(2*CMath::RandomReal()-1));
|
|
delta.Set(i,0.35*b1[i]);
|
|
}
|
|
gp=matrix<double>::Zeros(n,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<n; i++)
|
|
{
|
|
gp.Set(i,0,(double)i/(double)n);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
gp.Set(i,nx+j,a1[j]*MathCos(f1*2*M_PI*gp.Get(i,0))+b1[j]*MathCos(f2*2*M_PI*gp.Get(i,0)));
|
|
mody0.Set(i,j,gp.Get(i,nx+j));
|
|
}
|
|
}
|
|
q=1;
|
|
nlayers=1;
|
|
errtype=1;
|
|
//--- test multilayer algorithm with different parameters
|
|
while(q>=(1/(2*f2)))
|
|
{
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoMultilayer(s,r,nlayers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
CRBF::RBFSetPoints(s,gp,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(ny==1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,gp.Get(i,j));
|
|
switch(nx)
|
|
{
|
|
case 2:
|
|
mody1.Set(i,0,CRBF::RBFCalc2(s,x[0],x[1]));
|
|
break;
|
|
case 3:
|
|
mody1.Set(i,0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
|
|
break;
|
|
default:
|
|
CAp::Assert(false,"BasicMultiLayerRBFTest1D: Invalid variable NX(NX neither 2 nor 3)");
|
|
return(true);
|
|
}
|
|
}
|
|
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
|
|
return(true);
|
|
if(nx==2)
|
|
{
|
|
CRBF::RBFGridCalc2(s,gpgx0,n,gpgx1,n,gy);
|
|
for(i=0; i<n; i++)
|
|
mody1.Set(i,0,gy.Get(i,0));
|
|
}
|
|
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
|
|
return(true);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,gp.Get(i,j));
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
mody1.Set(i,j,y[j]);
|
|
}
|
|
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
|
|
return(true);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,gp.Get(i,j));
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
mody1.Set(i,j,y[j]);
|
|
}
|
|
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
|
|
return(true);
|
|
q=q/2;
|
|
nlayers=nlayers+1;
|
|
if(errtype==1 && q<=(1.0/f2))
|
|
errtype=2;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests special cases: |
|
|
//| * uninitialized RBF model will correctly return zero values |
|
|
//| * RBF correctly handles 1 or 2 distinct points |
|
|
//| * when we have many uniformly spaced points and one outlier, |
|
|
//| filter which is applied to radii, makes all radii equal |
|
|
//| (RBF - QNN). |
|
|
//| * RBF-ML with NLayers = 0 gives linear model |
|
|
//| * Hierarchical RBF with NLayers = 0 gives linear model |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::SpecialTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
double errtol=1.0E-9;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int n=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int t=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble vf;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int termtype=0;
|
|
int tmpnx=0;
|
|
int tmpny=0;
|
|
int tmpnc=0;
|
|
CMatrixDouble xwr;
|
|
CMatrixDouble v;
|
|
double sx=0;
|
|
double z=0;
|
|
double va=0;
|
|
double vb=0;
|
|
double vc=0;
|
|
double vd=0;
|
|
int modelversion=0;
|
|
double vv=0;
|
|
//--- Create model in the default state (no parameters/points specified).
|
|
//--- With probability 0.5 we do one of the following:
|
|
//--- * test that default state of the model is a zero model (all Calc()
|
|
//--- functions return zero)
|
|
//--- * call RBFBuildModel() (without specifying anything) and test that
|
|
//--- all Calc() functions return zero.
|
|
//--- NOTE: because NX varies between 1 and 4, both V1 (old) and V2 RBFs
|
|
//--- are tested.
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=3; ny++)
|
|
{
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:251");
|
|
return(result);
|
|
}
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(1);
|
|
for(i=0; i<nx ; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(CAp::Len(y)!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:262");
|
|
return(result);
|
|
}
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
if(y[i]!=0.0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:268");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Create default model with 1 point and different types of linear term.
|
|
//--- Test algorithm on such dataset.
|
|
//--- NOTE: because NX varies between 1 and 4, both V1 (old) and V2 RBFs
|
|
//--- are tested.
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=3; ny++)
|
|
{
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
for(termtype=0; termtype<=1; termtype++)
|
|
{
|
|
if(termtype==0)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(termtype==1)
|
|
CRBF::RBFSetConstTerm(s);
|
|
xy=matrix<double>::Zeros(1,nx+ny);
|
|
for(i=0; i<nx+ny; i++)
|
|
xy.Set(0,i,2*CMath::RandomReal()-1);
|
|
CRBF::RBFSetPoints(s,xy,1);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:297");
|
|
return(result);
|
|
}
|
|
//--- First, test that model exactly reproduces our dataset in the specified point
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
x.Set(i,xy.Get(0,i));
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(CAp::Len(y)!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:310");
|
|
return(result);
|
|
}
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
if(MathAbs(y[i]-xy.Get(0,nx+i))>errtol)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:316");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Create model with 2 points and different types of linear term.
|
|
//--- Test algorithm on such dataset.
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
for(ny=1; ny<=3; ny++)
|
|
{
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
for(termtype=0; termtype<=1; termtype++)
|
|
{
|
|
if(termtype==0)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(termtype==1)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(termtype==2)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
xy=matrix<double>::Zeros(2,nx+ny);
|
|
for(i=0; i<nx+ny; i++)
|
|
xy.Set(0,i,2*CMath::RandomReal()-1);
|
|
xy.Row(1,xy[0]+1.0);
|
|
CRBF::RBFSetPoints(s,xy,2);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<=1; j++)
|
|
{
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
x.Set(i,xy.Get(j,i));
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(CAp::Len(y)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
if(MathAbs(y[i]-xy.Get(j,nx+i))>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Generate a set of points (xi,yi) = (SX*i,0), and one
|
|
//--- outlier (x_far,y_far)=(-1000*SX,0).
|
|
//--- Radii filtering should place a bound on the radius of outlier.
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
for(ny=1; ny<=3; ny++)
|
|
{
|
|
sx=MathExp(-5+10*CMath::RandomReal());
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
xy=matrix<double>::Zeros(20,nx+ny);
|
|
for(i=0; i<=CAp::Rows(xy)-1; i++)
|
|
{
|
|
xy.Set(i,0,sx*i);
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal());
|
|
}
|
|
xy.Set(CAp::Rows(xy)-1,0,-(1000*sx));
|
|
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
|
|
//--- Try random Z from [1,5]
|
|
z=1+CMath::RandomReal()*4;
|
|
CRBF::RBFSetAlgoQNN(s,1.0,z);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CRBF::RBFUnpack(s,tmpnx,tmpny,xwr,tmpnc,v,modelversion);
|
|
if(((((tmpnx!=nx || tmpny!=ny) || tmpnc!=CAp::Rows(xy)) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(xwr)!=tmpnc) || modelversion!=1)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<=tmpnc-2; i++)
|
|
{
|
|
if(MathAbs(xwr.Get(i,nx+ny)-sx)>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(MathAbs(xwr.Get(tmpnc-1,nx+ny)-z*sx)>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- RBF-ML with NLayers=0 gives us linear model.
|
|
//--- In order to perform this test, we use test function which
|
|
//--- is perfectly linear and see whether RBF model is able to
|
|
//--- reproduce such function.
|
|
n=5;
|
|
for(ny=1; ny<=3; ny++)
|
|
{
|
|
va=2*CMath::RandomReal()-1;
|
|
vb=2*CMath::RandomReal()-1;
|
|
vc=2*CMath::RandomReal()-1;
|
|
vd=2*CMath::RandomReal()-1;
|
|
//--- Test NX=2.
|
|
//--- Generate linear function using random coefficients VA/VB/VC.
|
|
//--- Function is K-dimensional vector-valued, each component has slightly
|
|
//--- different coefficients.
|
|
xy=matrix<double>::Zeros(n*n,2+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
xy.Set(n*i+j,0,i);
|
|
xy.Set(n*i+j,1,j);
|
|
for(k=0; k<ny; k++)
|
|
xy.Set(n*i+j,2+k,(va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k));
|
|
}
|
|
}
|
|
CRBF::RBFCreate(2,ny,s);
|
|
CRBF::RBFSetPoints(s,xy,n*n);
|
|
CRBF::RBFSetAlgoMultilayer(s,1.0,0,0.01);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
x=vector<double>::Zeros(2);
|
|
x.Set(0,(n-1)*CMath::RandomReal());
|
|
x.Set(1,(n-1)*CMath::RandomReal());
|
|
if(ny==1 && MathAbs(CRBF::RBFCalc2(s,x[0],x[1])-(va*x[0]+vb*x[1]+vc))>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(k=0; k<ny; k++)
|
|
if(MathAbs(y[k]-((va+0.1*k)*x[0]+(vb+0.2*k)*x[1]+(vc+0.3*k)))>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Test NX=3.
|
|
//--- Generate linear function using random coefficients VA/VB/VC/VC.
|
|
//--- Function is K-dimensional vector-valued, each component has slightly
|
|
//--- different coefficients.
|
|
xy=matrix<double>::Zeros(n*n*n,3+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<n; j++)
|
|
{
|
|
for(t=0; t<n; t++)
|
|
{
|
|
xy.Set(n*n*i+n*j+t,0,i);
|
|
xy.Set(n*n*i+n*j+t,1,j);
|
|
xy.Set(n*n*i+n*j+t,2,t);
|
|
for(k=0; k<ny; k++)
|
|
xy.Set(n*n*i+n*j+t,3+k,(va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k)*t+(vd+0.4*k));
|
|
}
|
|
}
|
|
}
|
|
CRBF::RBFCreate(3,ny,s);
|
|
CRBF::RBFSetPoints(s,xy,n*n*n);
|
|
CRBF::RBFSetAlgoMultilayer(s,1.0,0,0.01);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
x=vector<double>::Zeros(3);
|
|
x.Set(0,(n-1)*CMath::RandomReal());
|
|
x.Set(1,(n-1)*CMath::RandomReal());
|
|
x.Set(2,(n-1)*CMath::RandomReal());
|
|
if(ny==1 && MathAbs(CRBF::RBFCalc3(s,x[0],x[1],x[2])-(va*x[0]+vb*x[1]+vc*x[2]+vd))>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(k=0; k<ny; k++)
|
|
{
|
|
if(MathAbs(y[k]-((va+0.1*k)*x[0]+(vb+0.2*k)*x[1]+(vc+0.3*k)*x[2]+(vd+0.4*k)))>errtol)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- HierarchicalRBF with NLayers=0 gives us linear model.
|
|
//--- In order to perform this test, we use test function which
|
|
//--- is perfectly linear and see whether RBF model is able to
|
|
//--- reproduce such function.
|
|
n=15;
|
|
for(nx=1; nx<=5; nx++)
|
|
{
|
|
for(ny=1; ny<=3; ny++)
|
|
{
|
|
vf=matrix<double>::Zeros(ny,nx+1);
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
vf.Set(i,j,2*CMath::RandomReal()-1);
|
|
}
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,vf.Get(j,nx)+CAblasF::RDotRR(nx,vf,j,xy,i));
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFSetAlgoHierarchical(s,1.0,0,0.0);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:574");
|
|
return(result);
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
x.Set(i,CMath::RandomReal());
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(k=0; k<ny; k++)
|
|
{
|
|
vv=vf.Get(k,nx)+CAblasF::RDotVR(nx,x,vf,k);
|
|
CAp::SetErrorFlag(result,MathAbs(vv-y[k])>errtol,"testrbfunit.ap:586");
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functionality of RBF module on regular|
|
|
//| grids. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::BasicRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
double zx=10;
|
|
double px=15;
|
|
double zy=10;
|
|
double py=15;
|
|
double eps=1.0E-6;
|
|
bool result;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer calcbuf;
|
|
int nx=0;
|
|
int ny=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
int k2=0;
|
|
int linterm=0;
|
|
int np=0;
|
|
double sx=0;
|
|
double sy=0;
|
|
double q=0;
|
|
double z=0;
|
|
CRowDouble point;
|
|
CMatrixDouble gp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CMatrixDouble gy;
|
|
int unx=0;
|
|
int uny=0;
|
|
CMatrixDouble xwr;
|
|
CMatrixDouble v;
|
|
CRowDouble gpgx0;
|
|
CRowDouble gpgx1;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int fidx=0;
|
|
int modelversion=0;
|
|
|
|
//--- Problem types:
|
|
//--- * 2 and 3-dimensional problems
|
|
//--- * problems with zero, constant, linear terms
|
|
//--- * different scalings of X and Y values (1.0, 1E-15, 1E+15)
|
|
//--- * regular grids different grid sizes (from 2 to 4 points for each dimension)
|
|
//--- We check that:
|
|
//--- * RBF model correctly reproduces function value (testes with different Calc() functions)
|
|
//--- * unpacked model containt correct radii
|
|
//--- * linear term has correct form
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- prepare test problem
|
|
linterm=CMath::RandomInteger(3)+1;
|
|
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
|
|
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
point=vector<double>::Zeros(nx);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits);
|
|
q=0.25+CMath::RandomReal();
|
|
z=4.5+CMath::RandomReal();
|
|
CRBF::RBFSetAlgoQNN(s,q,z);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
//--- start points for grid
|
|
for(i=0; i<nx ; i++)
|
|
point.Set(i,sx*(2*CMath::RandomReal()-1));
|
|
//--- 2-dimensional test problem
|
|
if(nx==2)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
np=k0*k1;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
{
|
|
gp.Set(i*k1+j,0,point[0]+sx*i);
|
|
gp.Set(i*k1+j,1,point[1]+sx*j);
|
|
for(k=0; k<ny; k++)
|
|
gp.Set(i*k1+j,nx+k,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
CRBF::RBFCreateCalcBuffer(s,calcbuf);
|
|
if(ny==1)
|
|
{
|
|
gpgx0=vector<double>::Zeros(k0);
|
|
gpgx1=vector<double>::Zeros(k1);
|
|
for(i=0; i<k0 ; i++)
|
|
gpgx0.Set(i,point[0]+sx*i);
|
|
for(i=0; i<k1 ; i++)
|
|
gpgx1.Set(i,point[1]+sx*i);
|
|
CRBF::RBFGridCalc2(s,gpgx0,k0,gpgx1,k1,gy);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
if(MathAbs(gy.Get(i,j)-gp.Get(i*k1+j,nx))>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<np; i++)
|
|
{
|
|
//--- For each row we randomly choose a function to test
|
|
//--- and call it. We do not call multiple functions per
|
|
//--- row because carry-over effects may mask errors in
|
|
//--- some function (say, it is possible that function
|
|
//--- simply returns results from previous call of some
|
|
//--- other function which were stored in the RBF model;
|
|
//--- in this case, previous call with same parameters
|
|
//--- may hide deficiencies in the function).
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
fidx=CMath::RandomInteger(4);
|
|
if(fidx==0 && ny==1)
|
|
{
|
|
y.Set(0,CRBF::RBFCalc2(s,x[0],x[1]));
|
|
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(fidx==1)
|
|
{
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
if(fidx==2)
|
|
{
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
if(fidx==3)
|
|
{
|
|
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test for RBFUnpack
|
|
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
|
|
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:784");
|
|
return(result);
|
|
}
|
|
for(i=0; i<np; i++)
|
|
{
|
|
if(MathAbs(xwr.Get(i,unx+uny)-q*sx)>(sx*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(linterm==2)
|
|
{
|
|
for(i=0; i<=unx-1; i++)
|
|
for(j=0; j<=uny-1; j++)
|
|
if(v.Get(j,i)!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(linterm==3)
|
|
{
|
|
for(i=0; i<=unx; i++)
|
|
for(j=0; j<=uny-1; j++)
|
|
if(v.Get(j,i)!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- 3-dimensional test problems
|
|
if(nx==3)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
for(k2=2; k2<=4; k2++)
|
|
{
|
|
np=k0*k1*k2;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
for(k=0; k<k2 ; k++)
|
|
{
|
|
gp.Set((i*k1+j)*k2+k,0,point[0]+sx*i);
|
|
gp.Set((i*k1+j)*k2+k,1,point[1]+sx*j);
|
|
gp.Set((i*k1+j)*k2+k,2,point[2]+sx*k);
|
|
for(l=0; l<ny; l++)
|
|
gp.Set((i*k1+j)*k2+k,nx+l,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
CRBF::RBFCreateCalcBuffer(s,calcbuf);
|
|
for(i=0; i<np; i++)
|
|
{
|
|
//--- For each row we randomly choose a function to test
|
|
//--- and call it. We do not call multiple functions per
|
|
//--- row because carry-over effects may mask errors in
|
|
//--- some function (say, it is possible that function
|
|
//--- simply returns results from previous call of some
|
|
//--- other function which were stored in the RBF model;
|
|
//--- in this case, previous call with same parameters
|
|
//--- may hide deficiencies in the function).
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
x.Set(2,gp.Get(i,2));
|
|
fidx=CMath::RandomInteger(4);
|
|
if(fidx==0 && ny==1)
|
|
{
|
|
y.Set(0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
|
|
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(fidx==1)
|
|
{
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(fidx==2)
|
|
{
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(fidx==3)
|
|
{
|
|
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- test for RBFUnpack
|
|
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
|
|
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:901");
|
|
return(result);
|
|
}
|
|
for(i=0; i<np; i++)
|
|
{
|
|
if((double)(MathAbs(xwr.Get(i,unx+uny)-q*sx))>(sx*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(linterm==2)
|
|
{
|
|
for(i=0; i<=unx-1; i++)
|
|
for(j=0; j<=uny-1; j++)
|
|
if(v.Get(j,i)!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(linterm==3)
|
|
{
|
|
for(i=0; i<=unx; i++)
|
|
for(j=0; j<=uny-1; j++)
|
|
if(v.Get(j,i)!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing RBF module on irregular grids. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::IrregularRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
double zx=10;
|
|
double px=15;
|
|
double zy=10;
|
|
double py=15;
|
|
double noiselevel=0.1;
|
|
double eps=1.0E-6;
|
|
bool result;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int nx=0;
|
|
int ny=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
int k2=0;
|
|
int linterm=0;
|
|
int np=0;
|
|
double sx=0;
|
|
double sy=0;
|
|
double q=0;
|
|
double z=0;
|
|
CRowDouble point;
|
|
CMatrixDouble gp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CMatrixDouble gy;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
|
|
//--- Problem types:
|
|
//--- * 2 and 3-dimensional problems
|
|
//--- * problems with zero, constant, linear terms
|
|
//--- * different scalings of X and Y values (1.0, 1E-15, 1E+15)
|
|
//--- * noisy grids, which are just regular grids with different grid sizes
|
|
//--- (from 2 to 4 points for each dimension) and moderate amount of random
|
|
//--- noise added to all node positions.
|
|
//--- We check that:
|
|
//--- * RBF model correctly reproduces function value (testes with different Calc() functions)
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
//--- prepare test problem
|
|
linterm=CMath::RandomInteger(3)+1;
|
|
ny=CMath::RandomInteger(2)+1;
|
|
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
|
|
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
point=vector<double>::Zeros(nx);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits);
|
|
q=0.25+CMath::RandomReal();
|
|
z=4.5+CMath::RandomReal();
|
|
CRBF::RBFSetAlgoQNN(s,q,z);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
//--- start points for grid
|
|
for(i=0; i<nx ; i++)
|
|
point.Set(i,sx*(2*CMath::RandomReal()-1));
|
|
//--- 2-dimensional test problems
|
|
if(nx==2)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
np=k0*k1;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
for(j=0; j<k1 ; j++)
|
|
{
|
|
gp.Set(i*k1+j,0,point[0]+sx*i+noiselevel*sx*(2*CMath::RandomReal()-1));
|
|
gp.Set(i*k1+j,1,point[1]+sx*j+noiselevel*sx*(2*CMath::RandomReal()-1));
|
|
for(k=0; k<ny; k++)
|
|
gp.Set(i*k1+j,nx+k,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
for(i=0; i<np; i++)
|
|
{
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
if(ny==1)
|
|
{
|
|
y.Set(0,CRBF::RBFCalc2(s,x[0],x[1]));
|
|
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//---
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//---
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- 3-dimensional test problems
|
|
if(nx==3)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
for(k2=2; k2<=4; k2++)
|
|
{
|
|
np=k0*k1*k2;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
{
|
|
for(k=0; k<k2 ; k++)
|
|
{
|
|
gp.Set((i*k1+j)*k2+k,0,point[0]+sx*i+noiselevel*sx*(2*CMath::RandomReal()-1));
|
|
gp.Set((i*k1+j)*k2+k,1,point[1]+sx*j+noiselevel*sx*(2*CMath::RandomReal()-1));
|
|
gp.Set((i*k1+j)*k2+k,2,point[2]+sx*k+noiselevel*sx*(2*CMath::RandomReal()-1));
|
|
for(l=0; l<ny; l++)
|
|
gp.Set((i*k1+j)*k2+k,nx+l,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
for(i=0; i<np; i++)
|
|
{
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
x.Set(2,gp.Get(i,2));
|
|
if(ny==1)
|
|
{
|
|
y.Set(0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
|
|
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//---
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//---
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The test does check, that algorithm can build linear model for|
|
|
//| the data sets, when Y depends on X linearly. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::LinearityModelRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
double zx=10;
|
|
double px=15;
|
|
double zy=10;
|
|
double py=15;
|
|
int ny=1;
|
|
bool result;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int nx=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
int k2=0;
|
|
int linterm=0;
|
|
int np=0;
|
|
double sx=0;
|
|
double sy=0;
|
|
double q=0;
|
|
double z=0;
|
|
CRowDouble point;
|
|
CRowDouble a;
|
|
CMatrixDouble gp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
int unx=0;
|
|
int uny=0;
|
|
CMatrixDouble xwr;
|
|
CMatrixDouble v;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int modelversion=0;
|
|
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
//--- prepare test problem
|
|
linterm=CMath::RandomInteger(3)+1;
|
|
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
|
|
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
point=vector<double>::Zeros(nx);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
q=0.25+CMath::RandomReal();
|
|
z=4.5+CMath::RandomReal();
|
|
CRBF::RBFSetAlgoQNN(s,q,z);
|
|
a=vector<double>::Zeros(nx+1);
|
|
if(linterm==1)
|
|
{
|
|
CRBF::RBFSetLinTerm(s);
|
|
for(i=0; i<nx ; i++)
|
|
a.Set(i,sy*(2*CMath::RandomReal()-1)/sx);
|
|
a.Set(nx,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
if(linterm==2)
|
|
{
|
|
CRBF::RBFSetConstTerm(s);
|
|
for(i=0; i<nx ; i++)
|
|
a.Set(i,0);
|
|
a.Set(nx,sy*(2*CMath::RandomReal()-1));
|
|
}
|
|
if(linterm==3)
|
|
{
|
|
CRBF::RBFSetZeroTerm(s);
|
|
for(i=0; i<=nx; i++)
|
|
a.Set(i,0);
|
|
}
|
|
//--- start points for grid
|
|
for(i=0; i<nx ; i++)
|
|
point.Set(i,sx*(2*CMath::RandomReal()-1));
|
|
if(nx==2)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
np=k0*k1;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
{
|
|
gp.Set(i*k1+j,0,point[0]+sx*i);
|
|
gp.Set(i*k1+j,1,point[1]+sx*j);
|
|
gp.Set(i*k1+j,nx,a[nx]);
|
|
for(k=0; k<nx ; k++)
|
|
gp.Add(i*k1+j,nx,gp.Get(i*k1+j,k)*a[k]);
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
//--- test for RBFUnpack
|
|
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
|
|
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<nx ; i++)
|
|
{
|
|
if(MathAbs(v.Get(0,i)-a[i])>(sy/sx*m_tol))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(MathAbs(v.Get(0,nx)-a[nx])>(sy*m_tol))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<np; i++)
|
|
if(MathAbs(xwr.Get(i,unx))>(sy*m_tol))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//---
|
|
if(nx==3)
|
|
{
|
|
for(k0=2; k0<=4; k0++)
|
|
{
|
|
for(k1=2; k1<=4; k1++)
|
|
{
|
|
for(k2=2; k2<=4; k2++)
|
|
{
|
|
np=k0*k1*k2;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
for(k=0; k<k2 ; k++)
|
|
{
|
|
gp.Set((i*k1+j)*k2+k,0,point[0]+sx*i);
|
|
gp.Set((i*k1+j)*k2+k,1,point[1]+sx*j);
|
|
gp.Set((i*k1+j)*k2+k,2,point[2]+sx*k);
|
|
gp.Set((i*k1+j)*k2+k,nx,a[nx]);
|
|
for(l=0; l<nx ; l++)
|
|
gp.Add((i*k1+j)*k2+k,nx,gp.Get((i*k1+j)*k2+k,l)*a[l]);
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
//--- test for RBFUnpack
|
|
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
|
|
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<nx ; i++)
|
|
{
|
|
if(MathAbs(v.Get(0,i)-a[i])>(sy/sx*m_tol))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(MathAbs(v.Get(0,nx)-a[nx])>(sy*m_tol))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<np; i++)
|
|
{
|
|
if(MathAbs(xwr.Get(i,unx))>(sy*m_tol))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests serialization |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::SerializationTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFModel s2;
|
|
CRBFReport rep;
|
|
int n=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
int k2=0;
|
|
int i=0;
|
|
int i0=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
int j=0;
|
|
int k=0;
|
|
double rbase=0;
|
|
int nlayers=0;
|
|
int bf=0;
|
|
int gridsize=0;
|
|
CMatrixDouble xy;
|
|
CRowDouble testpoint;
|
|
CRowDouble y0;
|
|
CRowDouble y1;
|
|
CRowDouble scalevec;
|
|
CHighQualityRandState rs;
|
|
double meanseparation=0;
|
|
int algoidx=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- This function generates random 2 or 3 dimensional problem,
|
|
//--- builds RBF model (QNN is used), serializes/unserializes it, then compares
|
|
//--- models by calculating model value at some random point.
|
|
//--- Additionally we test that new model (one which was restored
|
|
//--- after serialization) has lost all model construction settings,
|
|
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
|
|
//--- empty (zero) model.
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- prepare test problem
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoQNN(s,1.0,5.0);
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(nx==2)
|
|
{
|
|
//--- 2-dimensional problem
|
|
k0=2+CMath::RandomInteger(4);
|
|
k1=2+CMath::RandomInteger(4);
|
|
xy=matrix<double>::Zeros(k0*k1,nx+ny);
|
|
for(i0=0; i0<k0 ; i0++)
|
|
{
|
|
for(i1=0; i1<k1 ; i1++)
|
|
{
|
|
xy.Set(i0*k1+i1,0,i0+0.1*(2*CMath::RandomReal()-1));
|
|
xy.Set(i0*k1+i1,1,i1+0.1*(2*CMath::RandomReal()-1));
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i0*k1+i1,nx+j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
testpoint=vector<double>::Zeros(nx);
|
|
testpoint.Set(0,CMath::RandomReal()*(k0-1));
|
|
testpoint.Set(1,CMath::RandomReal()*(k1-1));
|
|
}
|
|
else
|
|
{
|
|
//--- 3-dimensional problem
|
|
k0=2+CMath::RandomInteger(4);
|
|
k1=2+CMath::RandomInteger(4);
|
|
k2=2+CMath::RandomInteger(4);
|
|
xy=matrix<double>::Zeros(k0*k1*k2,nx+ny);
|
|
for(i0=0; i0<k0 ; i0++)
|
|
{
|
|
for(i1=0; i1<k1 ; i1++)
|
|
{
|
|
for(i2=0; i2<k2 ; i2++)
|
|
{
|
|
xy.Set(i0*k1*k2+i1*k2+i2,0,i0+0.1*(2*CMath::RandomReal()-1));
|
|
xy.Set(i0*k1*k2+i1*k2+i2,1,i1+0.1*(2*CMath::RandomReal()-1));
|
|
xy.Set(i0*k1*k2+i1*k2+i2,2,i2+0.1*(2*CMath::RandomReal()-1));
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i0*k1*k2+i1*k2+i2,nx+j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
}
|
|
testpoint=vector<double>::Zeros(nx);
|
|
testpoint.Set(0,CMath::RandomReal()*(k0-1));
|
|
testpoint.Set(1,CMath::RandomReal()*(k1-1));
|
|
testpoint.Set(2,CMath::RandomReal()*(k2-1));
|
|
}
|
|
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
|
|
//--- Build model, serialize, compare
|
|
CRBF::RBFBuildModel(s,rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CRBF::RBFAlloc(_local_serializer,s);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CRBF::RBFSerialize(_local_serializer,s);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CRBF::RBFUnserialize(_local_serializer,s2);
|
|
_local_serializer.Stop();
|
|
}
|
|
CRBF::RBFCalc(s,testpoint,y0);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(y0[j]!=y1[j])
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Check that calling RBFBuildModel() on S2 (new model)
|
|
//--- will result in construction of zero model, i.e. test
|
|
//--- that serialization restores model, but not dataset
|
|
//--- which was used to build model.
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y1)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(y1[j]!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- This function generates random 2 or 3 dimensional problem,
|
|
//--- builds model using RBF-ML algo, serializes/unserializes it,
|
|
//--- then compares models by calculating model value at some
|
|
//--- random point.
|
|
//--- Additionally we test that new model (one which was restored
|
|
//--- after serialization) has lost all model construction settings,
|
|
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
|
|
//--- empty (zero) model.
|
|
for(nx=2; nx<=3; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- prepare test problem
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoMultilayer(s,5.0,5,1.0E-3);
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(nx==2)
|
|
{
|
|
//--- 2-dimensional problem
|
|
k0=2+CMath::RandomInteger(4);
|
|
k1=2+CMath::RandomInteger(4);
|
|
xy=matrix<double>::Zeros(k0*k1,nx+ny);
|
|
for(i0=0; i0<k0 ; i0++)
|
|
{
|
|
for(i1=0; i1<k1 ; i1++)
|
|
{
|
|
xy.Set(i0*k1+i1,0,i0+0.1*(2*CMath::RandomReal()-1));
|
|
xy.Set(i0*k1+i1,1,i1+0.1*(2*CMath::RandomReal()-1));
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i0*k1+i1,nx+j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
testpoint=vector<double>::Zeros(nx);
|
|
testpoint.Set(0,CMath::RandomReal()*(k0-1));
|
|
testpoint.Set(1,CMath::RandomReal()*(k1-1));
|
|
}
|
|
else
|
|
{
|
|
//--- 3-dimensional problem
|
|
k0=2+CMath::RandomInteger(4);
|
|
k1=2+CMath::RandomInteger(4);
|
|
k2=2+CMath::RandomInteger(4);
|
|
xy=matrix<double>::Zeros(k0*k1*k2,nx+ny);
|
|
for(i0=0; i0<k0 ; i0++)
|
|
{
|
|
for(i1=0; i1<k1 ; i1++)
|
|
for(i2=0; i2<k2 ; i2++)
|
|
{
|
|
xy.Set(i0*k1*k2+i1*k2+i2,0,i0+0.1*(2*CMath::RandomReal()-1));
|
|
xy.Set(i0*k1*k2+i1*k2+i2,1,i1+0.1*(2*CMath::RandomReal()-1));
|
|
xy.Set(i0*k1*k2+i1*k2+i2,2,i2+0.1*(2*CMath::RandomReal()-1));
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i0*k1*k2+i1*k2+i2,nx+j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
testpoint=vector<double>::Zeros(nx);
|
|
testpoint.Set(0,CMath::RandomReal()*(k0-1));
|
|
testpoint.Set(1,CMath::RandomReal()*(k1-1));
|
|
testpoint.Set(2,CMath::RandomReal()*(k2-1));
|
|
}
|
|
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
|
|
//--- Build model, serialize, compare
|
|
CRBF::RBFBuildModel(s,rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CRBF::RBFAlloc(_local_serializer,s);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CRBF::RBFSerialize(_local_serializer,s);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CRBF::RBFUnserialize(_local_serializer,s2);
|
|
_local_serializer.Stop();
|
|
}
|
|
CRBF::RBFCalc(s,testpoint,y0);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(y0[j]!=y1[j])
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Check that calling RBFBuildModel() on S2 (new model)
|
|
//--- will result in construction of zero model, i.e. test
|
|
//--- that serialization restores model, but not dataset
|
|
//--- which was used to build model.
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y1)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
if(y1[j]!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- This function generates random 1...4-dimensional problem,
|
|
//--- builds model using HRBF algo, serializes/unserializes it,
|
|
//--- then compares models by calculating model value at some
|
|
//--- random point.
|
|
//--- NOTE: we choose at random whether to use default scaling -
|
|
//--- or user-supplied one.
|
|
//--- Additionally we test that new model (one which was restored
|
|
//--- after serialization) has lost all model construction settings,
|
|
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
|
|
//--- empty (zero) model.
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
rbase=0.33;
|
|
nlayers=5;
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
bf=CMath::RandomInteger(2);
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
CAp::Assert(gridsize>1);
|
|
CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx)));
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
|
|
k=k/gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
testpoint=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
testpoint.Set(j,CMath::RandomReal());
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
|
|
//--- prepare test problem
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(CMath::RandomReal()>0.5)
|
|
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
|
|
else
|
|
CRBF::RBFSetPointsAndScales(s,xy,CAp::Rows(xy),scalevec);
|
|
//--- Build model, serialize, compare
|
|
CRBF::RBFBuildModel(s,rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CRBF::RBFAlloc(_local_serializer,s);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CRBF::RBFSerialize(_local_serializer,s);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CRBF::RBFUnserialize(_local_serializer,s2);
|
|
_local_serializer.Stop();
|
|
}
|
|
CRBF::RBFCalc(s,testpoint,y0);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(y0[j]!=y1[j])
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Check that calling RBFBuildModel() on S2 (new model)
|
|
//--- will result in construction of zero model, i.e. test
|
|
//--- that serialization restores model, but not dataset
|
|
//--- which was used to build model.
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y1)!=ny)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(y1[j]!=0.0)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- This function generates random 1...4-dimensional problem,
|
|
//--- builds model using XRBF algo, serializes/unserializes it,
|
|
//--- then compares models by calculating model value at some
|
|
//--- random point.
|
|
//--- NOTE: we choose at random whether to use default scaling -
|
|
//--- or user-supplied one.
|
|
//--- Additionally we test that new model (one which was restored
|
|
//--- after serialization) has lost all model construction settings,
|
|
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
|
|
//--- empty (zero) model.
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
|
|
testpoint=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
testpoint.Set(j,CHighQualityRand::HQRndNormal(rs));
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
|
|
//--- prepare test problem
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.5)
|
|
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
|
|
else
|
|
CRBF::RBFSetPointsAndScales(s,xy,CAp::Rows(xy),scalevec);
|
|
//--- Clear target model
|
|
CRBF::RBFCreate(1,1,s2);
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y1)!=1)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1913");
|
|
return(result);
|
|
}
|
|
if((double)(y1[0])!=0.0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1918");
|
|
return(result);
|
|
}
|
|
//--- Build model, serialize, compare
|
|
CRBF::RBFBuildModel(s,rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CRBF::RBFAlloc(_local_serializer,s);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CRBF::RBFSerialize(_local_serializer,s);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CRBF::RBFUnserialize(_local_serializer,s2);
|
|
_local_serializer.Stop();
|
|
}
|
|
CRBF::RBFCalc(s,testpoint,y0);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1933");
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(y0[j]!=y1[j])
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1939");
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Check that calling RBFBuildModel() on S2 (new model)
|
|
//--- will result in construction of zero model, i.e. test
|
|
//--- that serialization restores model, but not dataset
|
|
//--- which was used to build model.
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
CRBF::RBFCalc(s2,testpoint,y1);
|
|
if(CAp::Len(y1)!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1953");
|
|
return(result);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
if(y1[j]!=0.0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1959");
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::SearcherR(const CMatrixDouble &Y0,
|
|
const CMatrixDouble &Y1,
|
|
int n,int ny,int errtype,
|
|
const CRowDouble &B1,
|
|
const CRowDouble &Delta)
|
|
{
|
|
//--- create variables
|
|
double oralerr=1.0E-1;
|
|
double iralerr=1.0E-2;
|
|
int lb=25;
|
|
int rb=75;
|
|
CRowDouble irerr;
|
|
CRowDouble orerr;
|
|
int i=0;
|
|
int j=0;
|
|
CMatrixDouble y0=Y0;
|
|
CMatrixDouble y1=Y1;
|
|
CRowDouble b1=B1;
|
|
CRowDouble delta=Delta;
|
|
//--- check parameters
|
|
if(!CAp::Assert(n>0,"SearchErr: invalid parameter N(N<=0)."))
|
|
return(true);
|
|
if(!CAp::Assert(ny>0,"SearchErr: invalid parameter NY(NY<=0)."))
|
|
return(true);
|
|
|
|
orerr=vector<double>::Zeros(ny);
|
|
irerr=vector<double>::Zeros(ny);
|
|
|
|
if(errtype==1)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
if(orerr[j]<(double)(MathAbs(y0.Get(i,j)-y1.Get(i,j))))
|
|
orerr.Set(j,MathAbs(y0.Get(i,j)-y1.Get(i,j)));
|
|
}
|
|
}
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
if(orerr[i]>(b1[i]+delta[i]) || orerr[i]<(b1[i]-delta[i]))
|
|
return(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(errtype==2)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<ny; j++)
|
|
if(i>lb && i<rb)
|
|
{
|
|
if(irerr[j]<MathAbs(y0.Get(i,j)-y1.Get(i,j)))
|
|
irerr.Set(j,MathAbs(y0.Get(i,j)-y1.Get(i,j)));
|
|
}
|
|
else
|
|
{
|
|
if(orerr[j]<MathAbs(y0.Get(i,j)-y1.Get(i,j)))
|
|
orerr.Set(j,MathAbs(y0.Get(i,j)-y1.Get(i,j)));
|
|
}
|
|
}
|
|
for(i=0; i<ny; i++)
|
|
if(orerr[i]>oralerr || irerr[i]>iralerr)
|
|
return(true);
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false,"SearchErr: invalid argument ErrType(ErrType neither 1 nor 2)");
|
|
return(true);
|
|
}
|
|
}
|
|
//--- create variables
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functionality of RBF module on regular|
|
|
//| grids with multi-layer algorithm in 2-3D. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::BasicMultilayerRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int range=10;
|
|
int passcount=10;
|
|
double eps=1.0E-6;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer calcbuf;
|
|
int nx=0;
|
|
int ny=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
int k2=0;
|
|
int linterm=0;
|
|
int np=0;
|
|
double q=0;
|
|
int layers=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double gstep=0;
|
|
CRowDouble point;
|
|
CMatrixDouble gp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CMatrixDouble gy;
|
|
CRowDouble gpgx0;
|
|
CRowDouble gpgx1;
|
|
CRowDouble gpgx2;
|
|
CRowDouble gcy;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int fidx=0;
|
|
double r0=0;
|
|
int margin=0;
|
|
int gridsize=0;
|
|
double threshold=0;
|
|
double v=0;
|
|
|
|
//--- Test that RBF model with sufficient layers will exactly reproduce
|
|
//--- target function.
|
|
for(pass=0; pass<=passcount-1; pass++)
|
|
{
|
|
//--- prepare test problem
|
|
k0=6+CMath::RandomInteger(3);
|
|
k1=6+CMath::RandomInteger(3);
|
|
k2=6+CMath::RandomInteger(3);
|
|
s1=MathPow(range,CMath::RandomInteger(3)-1);
|
|
s2=MathPow(range,CMath::RandomInteger(3)-1);
|
|
nx=CMath::RandomInteger(2)+2;
|
|
ny=CMath::RandomInteger(2)+1;
|
|
linterm=CMath::RandomInteger(3)+1;
|
|
layers=5;
|
|
gstep=s1/6;
|
|
q=s1;
|
|
//--- Create RBF structure and auxiliary structures
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
point=vector<double>::Zeros(nx);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoMultilayer(s,q,layers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
for(i=0; i<nx ; i++)
|
|
point.Set(i,s1*(2*CMath::RandomReal()-1));
|
|
//--- 2-dimensional test problem
|
|
if(nx==2)
|
|
{
|
|
np=k0*k1;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
{
|
|
gp.Set(i*k1+j,0,point[0]+gstep*i);
|
|
gp.Set(i*k1+j,1,point[1]+gstep*j);
|
|
for(k=0; k<ny; k++)
|
|
gp.Set(i*k1+j,nx+k,s2*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
CRBF::RBFCreateCalcBuffer(s,calcbuf);
|
|
if(ny==1)
|
|
{
|
|
gpgx0=vector<double>::Zeros(k0);
|
|
gpgx1=vector<double>::Zeros(k1);
|
|
for(i=0; i<k0 ; i++)
|
|
gpgx0.Set(i,point[0]+gstep*i);
|
|
for(i=0; i<k1 ; i++)
|
|
gpgx1.Set(i,point[1]+gstep*i);
|
|
CRBF::RBFGridCalc2(s,gpgx0,k0,gpgx1,k1,gy);
|
|
for(i=0; i<k0 ; i++)
|
|
for(j=0; j<k1 ; j++)
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(gy.Get(i,j)-gp.Get(i*k1+j,nx)))>(double)(s2*eps),"testrbfunit.ap:2366");
|
|
}
|
|
for(i=0; i<np; i++)
|
|
{
|
|
//--- For each row we randomly choose a function to test
|
|
//--- and call it. We do not call multiple functions per
|
|
//--- row because carry-over effects may mask errors in
|
|
//--- some function (say, it is possible that function
|
|
//--- simply returns results from previous call of some
|
|
//--- other function which were stored in the RBF model;
|
|
//--- in this case, previous call with same parameters
|
|
//--- may hide deficiencies in the function).
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
fidx=CMath::RandomInteger(4);
|
|
if(fidx==0 && ny!=1)
|
|
continue;
|
|
if(fidx==0)
|
|
y.Set(0,CRBF::RBFCalc2(s,x[0],x[1]));
|
|
if(fidx==1)
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(fidx==2)
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
if(fidx==3)
|
|
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(gp.Get(i,nx+j)-y[j])>(s2*eps),"testrbfunit.ap:2394");
|
|
}
|
|
}
|
|
//--- 3-dimensional test problems
|
|
if(nx==3)
|
|
{
|
|
np=k0*k1*k2;
|
|
gp=matrix<double>::Zeros(np,nx+ny);
|
|
//--- create grid, build model
|
|
gpgx0=vector<double>::Zeros(k0);
|
|
gpgx1=vector<double>::Zeros(k1);
|
|
gpgx2=vector<double>::Zeros(k2);
|
|
for(i=0; i<k0 ; i++)
|
|
gpgx0.Set(i,point[0]+gstep*i);
|
|
for(i=0; i<k1 ; i++)
|
|
gpgx1.Set(i,point[1]+gstep*i);
|
|
for(i=0; i<k2 ; i++)
|
|
gpgx2.Set(i,point[2]+gstep*i);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
for(k=0; k<k2 ; k++)
|
|
{
|
|
gp.Set((i*k1+j)*k2+k,0,gpgx0[i]);
|
|
gp.Set((i*k1+j)*k2+k,1,gpgx1[j]);
|
|
gp.Set((i*k1+j)*k2+k,2,gpgx2[k]);
|
|
for(l=0; l<ny; l++)
|
|
gp.Set((i*k1+j)*k2+k,nx+l,s2*(2*CMath::RandomReal()-1));
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,np);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
CRBF::RBFCreateCalcBuffer(s,calcbuf);
|
|
//--- Test RBFCalc3(), RBFCalc() and RBFCalcBuf() vs expected values on the grid (we expect good fit).
|
|
for(i=0; i<np; i++)
|
|
{
|
|
//--- For each row we randomly choose a function to test
|
|
//--- and call it. We do not call multiple functions per
|
|
//--- row because carry-over effects may mask errors in
|
|
//--- some function (say, it is possible that function
|
|
//--- simply returns results from previous call of some
|
|
//--- other function which were stored in the RBF model;
|
|
//--- in this case, previous call with same parameters
|
|
//--- may hide deficiencies in the function).
|
|
x.Set(0,gp.Get(i,0));
|
|
x.Set(1,gp.Get(i,1));
|
|
x.Set(2,gp.Get(i,2));
|
|
fidx=CMath::RandomInteger(4);
|
|
if(fidx==0 && ny!=1)
|
|
continue;
|
|
if(fidx==0)
|
|
y.Set(0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
|
|
if(fidx==1)
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(fidx==2)
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
if(fidx==3)
|
|
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(gp.Get(i,nx+j)-y[j])>(s2*eps),"testrbfunit.ap:2462");
|
|
}
|
|
//--- Test RBFGridCalc3V vs RBFCalc()
|
|
CRBF::RBFGridCalc3V(s,gpgx0,k0,gpgx1,k1,gpgx2,k2,gcy);
|
|
for(i=0; i<k0 ; i++)
|
|
{
|
|
for(j=0; j<k1 ; j++)
|
|
for(k=0; k<k2 ; k++)
|
|
{
|
|
x.Set(0,gpgx0[i]);
|
|
x.Set(1,gpgx1[j]);
|
|
x.Set(2,gpgx2[k]);
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(l=0; l<ny; l++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[l]-gcy[l+ny*(i+j*k0+k*k0*k1)])>(1.0E-9*s2),"testrbfunit.ap:2479");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test smoothing properties of RBF model: model with just one layer
|
|
//--- and large initial radius will produce "average" value of neighbors.
|
|
//--- In order to test it we create regular grid, fill it with regular
|
|
//--- +1/-1 pattern, and test model values in the inner points. Model
|
|
//--- values should be around zero (we use handcrafted threshold to test
|
|
//--- it). Radius is chosen to be several times larger than grid step.
|
|
//--- We perform test for 2D model, because same behavior is expected
|
|
//--- regardless of dimensionality.
|
|
r0=3;
|
|
margin=5;
|
|
gridsize=2*margin+20;
|
|
threshold=0.1;
|
|
nx=2;
|
|
ny=1;
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoMultilayer(s,r0,1,0.0);
|
|
CRBF::RBFSetZeroTerm(s);
|
|
gp=matrix<double>::Zeros(gridsize*gridsize,nx+ny);
|
|
for(i=0; i<gridsize; i++)
|
|
{
|
|
for(j=0; j<gridsize; j++)
|
|
{
|
|
gp.Set(i*gridsize+j,0,i);
|
|
gp.Set(i*gridsize+j,1,j);
|
|
gp.Set(i*gridsize+j,2,0.10*CMath::RandomReal()-0.05+(2*((i+j)%2)-1));
|
|
}
|
|
}
|
|
CRBF::RBFSetPoints(s,gp,gridsize*gridsize);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
v=0.0;
|
|
for(i=margin; i<gridsize-margin ; i++)
|
|
{
|
|
for(j=margin; j<gridsize-margin ; j++)
|
|
v=MathMax(v,MathAbs(CRBF::RBFCalc2(s,i,j)));
|
|
}
|
|
CAp::SetErrorFlag(result,v>threshold,"testrbfunit.ap:2526");
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functionality of RBF module on regular|
|
|
//| grids with multi-layer algorithm in 2-3D. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::GridCalc23Test(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
CHighQualityRandState rs;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
double perturbation=0;
|
|
CRowInt kx;
|
|
double sx=0;
|
|
double sy=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int linterm=0;
|
|
int layers=0;
|
|
int npoints=0;
|
|
CMatrixDouble xy;
|
|
bool gf[];
|
|
bool rf[];
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble gy;
|
|
CRowDouble gy2;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
double sparsity=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
|
|
for(pass=0; pass<=24; pass++)
|
|
{
|
|
//--- prepare test problem
|
|
kx.Resize(3);
|
|
for(i=0; i<=2; i++)
|
|
{
|
|
//--- 66% of cases - large grid
|
|
if(CHighQualityRand::HQRndUniformI(rs,3)==0)
|
|
{
|
|
kx.Set(i,(int)MathRound(10*MathPow(10,CHighQualityRand::HQRndUniformR(rs))));
|
|
continue;
|
|
}
|
|
//--- 33% of cases - small grid
|
|
k=CHighQualityRand::HQRndUniformI(rs,3);
|
|
if(k==0)
|
|
kx.Set(i,1);
|
|
if(k==1)
|
|
kx.Set(i,2);
|
|
if(k==2)
|
|
kx.Set(i,10);
|
|
}
|
|
sx=MathPow(10,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
sy=MathPow(10,CHighQualityRand::HQRndUniformI(rs,3)-1);
|
|
nx=3;
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
linterm=CHighQualityRand::HQRndUniformI(rs,3)+1;
|
|
layers=CHighQualityRand::HQRndUniformI(rs,3)+1;
|
|
npoints=100;
|
|
xy=matrix<double>::Zeros(npoints,nx+ny);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndUniformR(rs)*sx);
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CHighQualityRand::HQRndUniformR(rs)*sy);
|
|
}
|
|
//--- Create RBF model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoMultilayer(s,0.1*sx,layers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
CRBF::RBFSetPoints(s,xy,npoints);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testrbfunit.ap:2623");
|
|
if(errorflag)
|
|
return;
|
|
//--- Prepare test grid
|
|
x0=vector<double>::Zeros(kx[0]);
|
|
for(i=0; i<kx[0] ; i++)
|
|
{
|
|
perturbation=0.5*(CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x0.Set(i,sx*(i+perturbation)/kx[0]);
|
|
}
|
|
x1=vector<double>::Zeros(kx[1]);
|
|
for(i=0; i<kx[1] ; i++)
|
|
{
|
|
perturbation=0.5*(CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x1.Set(i,sx*(i+perturbation)/kx[1]);
|
|
}
|
|
x2=vector<double>::Zeros(kx[2]);
|
|
for(i=0; i<kx[2] ; i++)
|
|
{
|
|
perturbation=0.5*(CHighQualityRand::HQRndUniformR(rs)-0.5);
|
|
x2.Set(i,sx*(i+perturbation)/kx[2]);
|
|
}
|
|
//--- Test calculation on grid
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
gy.Resize(0);
|
|
CRBF::RBFGridCalc3V(s,x0,kx[0],x1,kx[1],x2,kx[2],gy);
|
|
for(i=0; i<kx[0] ; i++)
|
|
{
|
|
for(j=0; j<kx[1] ; j++)
|
|
for(k=0; k<kx[2] ; k++)
|
|
{
|
|
x.Set(0,x0[i]);
|
|
x.Set(1,x1[j]);
|
|
x.Set(2,x2[k]);
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
for(l=0; l<ny; l++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(y[l]-gy[l+ny*(i+j*kx[0]+k*kx[0]*kx[1])]))>(double)(1.0E-9*sy),"testrbfunit.ap:2665");
|
|
}
|
|
}
|
|
//--- Test calculation on subset of regular grid:
|
|
//--- * select sparsity coefficient (from 1.0 to 0.001)
|
|
//--- * fill bitmap array
|
|
//--- * Test 1: compare full and subset versions
|
|
//--- * Test 2: check sparsity. Subset function may perform additional
|
|
//--- evaluations because it processes data micro-row by micro-row.
|
|
//--- So, we can't check that all elements which were not flagged
|
|
//--- are zero - some of them may become non-zero. However, if entire
|
|
//--- row is empty, we can reasonably expect (informal guarantee)
|
|
//--- that it is not processed. So, we check empty (completely
|
|
//--- unflagged) rows
|
|
sparsity=MathPow(10,-CHighQualityRand::HQRndUniformI(rs,4));
|
|
ArrayResize(gf,kx[0]*kx[1]*kx[2]);
|
|
ArrayResize(rf,kx[1]*kx[2]);
|
|
ArrayInitialize(rf,false);
|
|
for(i=0; i<=kx[0]*kx[1]*kx[2]-1; i++)
|
|
{
|
|
gf[i]=CHighQualityRand::HQRndUniformR(rs)<sparsity;
|
|
if(gf[i])
|
|
rf[i/kx[0]]=true;
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
CRBF::RBFGridCalc3VSubset(s,x0,kx[0],x1,kx[1],x2,kx[2],gf,gy);
|
|
CRBF::RBFGridCalc3V(s,x0,kx[0],x1,kx[1],x2,kx[2],gy2);
|
|
for(i=0; i<=ny*kx[0]*kx[1]*kx[2]-1; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,gf[i/ny] && MathAbs(gy[i]-gy2[i])>(1.0E-9*sy),"testrbfunit.ap:2701");
|
|
CAp::SetErrorFlag(errorflag,!rf[i/(ny*kx[0])] && gy[i]!=0.0,"testrbfunit.ap:2702");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functionality of XRBF |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::BasicXRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFModel s2;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer tsbuf;
|
|
int algoidx=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int nduplicate=0;
|
|
double mx=0;
|
|
double errtol=0;
|
|
double meanseparation=0;
|
|
double lambdav=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xytest;
|
|
CMatrixDouble uxwr;
|
|
CMatrixDouble uv;
|
|
CMatrixDouble xy2;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble y2;
|
|
CRowDouble xzero;
|
|
CRowDouble yref;
|
|
CRowDouble scalevec;
|
|
bool bflags[];
|
|
int n=0;
|
|
int ntest=0;
|
|
int gridsize=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double delta=0;
|
|
double refrms=0;
|
|
double refmax=0;
|
|
double v=0;
|
|
int functype=0;
|
|
int densitytype=0;
|
|
double width=0;
|
|
double lowprec=0;
|
|
double highprec=0;
|
|
int shaketype=0;
|
|
double maxerr=0;
|
|
bool fractionalerror;
|
|
int unx=0;
|
|
int uny=0;
|
|
int unc=0;
|
|
int modelversion=0;
|
|
bool hasscale;
|
|
int nondistinctcnt=0;
|
|
int termtype=0;
|
|
int leadingzeros=0;
|
|
double avx2_epsilon=1e-14;
|
|
CHighQualityRandState rs;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- First test - random problem, ability to build model
|
|
//--- which reproduces function value in all points with
|
|
//--- good precision.
|
|
//--- All dataset points are located in unit cube on
|
|
//--- regular grid. We do not use smoothing for this test.
|
|
//--- We use/test following functions:
|
|
//--- * RBFCalc()
|
|
//--- * RBFCalc2()
|
|
//--- * RBFCalc3()
|
|
//--- * RBFCalcBuf()
|
|
//--- * RBFTsCalcBuf()
|
|
errtol=1.0E-5;
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation);
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:2800");
|
|
return(result);
|
|
}
|
|
CRBF::RBFCreateCalcBuffer(s,tsbuf);
|
|
//--- Test ability to reproduce function value
|
|
//--- NOTE: we use RBFCalc(XZero) to guarantee that internal state of
|
|
//--- RBF model is "reset" between subsequent calls of different
|
|
//--- functions. It allows us to make sure that we have no bug
|
|
//--- like function simply returning latest result
|
|
CAblasF::RSetAllocV(nx,0.0,xzero);
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy.Get(i,j));
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
if(CMath::RandomReal()>0.5)
|
|
yref=vector<double>::Zeros(ny+1);
|
|
else
|
|
yref.Resize(0);
|
|
CRBF::RBFCalc(s,x,yref);
|
|
CAp::SetErrorFlag(result,CAp::Len(yref)!=ny,"testrbfunit.ap:2828");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(yref[j]-xy.Get(i,nx+j)))>(double)(errtol*n),"testrbfunit.ap:2832");
|
|
if(nx==1 && ny==1)
|
|
{
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,x[0])!=yref[0],"testrbfunit.ap:2838");
|
|
}
|
|
else
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,1.2)!=0.0,"testrbfunit.ap:2841");
|
|
if(nx==2 && ny==1)
|
|
{
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
CAp::SetErrorFlag(result,MathAbs(CRBF::RBFCalc2(s,x[0],x[1])-yref[0])>avx2_epsilon,"testrbfunit.ap:2847");
|
|
}
|
|
else
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc2(s,1.2,3.4)!=0.0,"testrbfunit.ap:2850");
|
|
if(nx==3 && ny==1)
|
|
{
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
CAp::SetErrorFlag(result,MathAbs(CRBF::RBFCalc3(s,x[0],x[1],x[2])-yref[0])>avx2_epsilon,"testrbfunit.ap:2856");
|
|
}
|
|
else
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc3(s,1.2,3.4,5.6)!=0.0,"testrbfunit.ap:2859");
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
y=vector<double>::Zeros(ny+1);
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:2867");
|
|
}
|
|
else
|
|
{
|
|
y=vector<double>::Zeros(ny-1);
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2873");
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:2876");
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
y=vector<double>::Zeros(ny+1);
|
|
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:2884");
|
|
}
|
|
else
|
|
{
|
|
y=vector<double>::Zeros(ny-1);
|
|
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2890");
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:2893");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test ability to handle surface normal conditions
|
|
algoidx=CHighQualityRand::HQRndUniformI(rs,m_xrbfc1algocnt);
|
|
nx=2;
|
|
ny=CHighQualityRand::HQRndUniformI(rs,4);
|
|
//--- Test ability to handle all dataset sizes from 0 to 10
|
|
errtol=1.0E-5;
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
for(n=0; n<=10; n++)
|
|
{
|
|
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
if(n>0)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CAblasF::RAllocV(nx,scalevec);
|
|
for(i=0; i<nx ; i++)
|
|
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
}
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
}
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:2929");
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAblasF::RAllocV(nx,x);
|
|
CAblasF::RCopyRV(nx,xy,i,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2937");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-xy.Get(i,nx+j))>errtol,"testrbfunit.ap:2941");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test ability to handle datasets with non-distinct points:
|
|
//--- * the model correctly predicts mean value at non-distinct points
|
|
//--- * Rep.RMSError and Rep.MaxError are correctly computed
|
|
errtol=1.0E-4;
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,20);
|
|
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
|
|
CAblasF::RAllocM(2*n,nx+ny,xy2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+0);
|
|
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+1);
|
|
v=0.001+CHighQualityRand::HQRndUniformR(rs);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy2.Add(2*i,nx+j,-v);
|
|
xy2.Add(2*i+1,nx+j,v);
|
|
}
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CAblasF::RAllocV(nx,scalevec);
|
|
for(i=0; i<nx ; i++)
|
|
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
|
|
CRBF::RBFSetPointsAndScales(s,xy2,2*n,scalevec);
|
|
}
|
|
else
|
|
{
|
|
CAblasF::RSetAllocV(nx,1.0,scalevec);
|
|
CRBF::RBFSetPoints(s,xy2,2*n);
|
|
}
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:2985");
|
|
return(result);
|
|
}
|
|
refrms=0;
|
|
refmax=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAblasF::RAllocV(nx,x);
|
|
CAblasF::RCopyRV(nx,xy,i,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2995");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-0.5*(xy2.Get(2*i,nx+j)+xy2.Get(2*i+1,nx+j)))>errtol,"testrbfunit.ap:3000");
|
|
refrms=refrms+CMath::Sqr(y[j]-xy2.Get(2*i,nx+j))+CMath::Sqr(y[j]-xy2.Get(2*i+1,nx+j));
|
|
refmax=CApServ::RMaxAbs3(refmax,y[j]-xy2.Get(2*i,nx+j),y[j]-xy2.Get(2*i+1,nx+j));
|
|
}
|
|
}
|
|
refrms=MathSqrt(refrms/(2*n*ny));
|
|
CAp::SetErrorFlag(result,MathAbs(refrms-rep.m_rmserror)>errtol,"testrbfunit.ap:3006");
|
|
CAp::SetErrorFlag(result,MathAbs(refmax-rep.m_maxerror)>errtol,"testrbfunit.ap:3007");
|
|
}
|
|
}
|
|
}
|
|
//--- Test ability of smoothing RBFs to handle datasets with nearly (but not exactly!) non-distinct points:
|
|
//--- * the model correctly predicts mean value at these points
|
|
//--- * Rep.RMSError and Rep.MaxError are correctly computed
|
|
//--- NOTE: at least NX+1 points are required for this test
|
|
errtol=0.20;
|
|
lambdav=1.0E-4;
|
|
for(algoidx=0; algoidx<=m_xrbfsmoothingalgocnt-1; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
n=1+CHighQualityRand::HQRndUniformI(rs,20);
|
|
n=MathMax(n,(int)MathRound(MathPow(2,nx))+1);
|
|
GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation);
|
|
CAblasF::RAllocM(2*n,nx+ny,xy2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
delta=1.0E-5*MathPow(10,-(4*CHighQualityRand::HQRndUniformR(rs)));
|
|
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+0);
|
|
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+1);
|
|
j=CHighQualityRand::HQRndUniformI(rs,nx);
|
|
xy2.Add(2*i+1,j,CHighQualityRand::HQRndNormal(rs)*delta);
|
|
v=CHighQualityRand::HQRndUniformR(rs)-0.5;
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy2.Add(2*i+0,nx+j,-v);
|
|
xy2.Add(2*i+1,nx+j,v);
|
|
}
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmSmoothing(s,algoidx,lambdav,meanseparation);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CAblasF::RAllocV(nx,scalevec);
|
|
for(i=0; i<nx ; i++)
|
|
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
|
|
CRBF::RBFSetPointsAndScales(s,xy2,2*n,scalevec);
|
|
}
|
|
else
|
|
{
|
|
CAblasF::RSetAllocV(nx,1.0,scalevec);
|
|
CRBF::RBFSetPoints(s,xy2,2*n);
|
|
}
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3058");
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAblasF::RAllocV(nx,x);
|
|
CAblasF::RCopyRV(nx,xy2,2*i+0,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3066");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-0.5*(xy2.Get(2*i,nx+j)+xy2.Get(2*i+1,nx+j)))>errtol,"testrbfunit.ap:3070");
|
|
CAblasF::RCopyRV(nx,xy2,2*i+1,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3073");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-0.5*(xy2.Get(2*i,nx+j)+xy2.Get(2*i+1,nx+j)))>errtol,"testrbfunit.ap:3077");
|
|
}
|
|
refrms=0;
|
|
refmax=0;
|
|
for(i=0; i<=2*n-1; i++)
|
|
{
|
|
CAblasF::RAllocV(nx,x);
|
|
CAblasF::RCopyRV(nx,xy2,i,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3086");
|
|
if(result)
|
|
return(result);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
refrms=refrms+CMath::Sqr(y[j]-xy2.Get(i,nx+j));
|
|
refmax=MathMax(refmax,MathAbs(y[j]-xy2.Get(i,nx+j)));
|
|
}
|
|
}
|
|
refrms=MathSqrt(refrms/(2*n*ny));
|
|
CAp::SetErrorFlag(result,MathAbs(refrms-rep.m_rmserror)>(1.0E-5),"testrbfunit.ap:3096");
|
|
CAp::SetErrorFlag(result,MathAbs(refmax-rep.m_maxerror)>(1.0E-5),"testrbfunit.ap:3097");
|
|
}
|
|
}
|
|
}
|
|
//--- Test ability to handle datasets consisting of several isolated "islands",
|
|
//--- far away from each other
|
|
errtol=1.0E-3;
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
ny=1;
|
|
n=50;
|
|
GenerateGoodRandomGrid(n,nx,1,rs,xy2,meanseparation);
|
|
nduplicate=2*n;
|
|
CAblasF::RAllocM(nduplicate,nx+1,xy);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(2*i+0,j,xy2.Get(i,j));
|
|
xy.Set(2*i+1,j,xy2.Get(i,j)+1000);
|
|
}
|
|
xy.Set(2*i+0,nx,CHighQualityRand::HQRndNormal(rs));
|
|
xy.Set(2*i+1,nx,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
CRBF::RBFSetPoints(s,xy,nduplicate);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3129");
|
|
return(result);
|
|
}
|
|
if(rep.m_rmserror>errtol)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3134");
|
|
return(result);
|
|
}
|
|
v=0;
|
|
for(i=0; i<=nduplicate-1; i++)
|
|
{
|
|
CAblasF::RCopyRV(nx,xy,i,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
v+=CMath::Sqr(y[j]-xy.Get(i,nx+j));
|
|
}
|
|
v=MathSqrt(v/(nduplicate*ny));
|
|
CAp::SetErrorFlag(result,v>errtol,"testrbfunit.ap:3146");
|
|
}
|
|
}
|
|
//--- Test ability to handle degenerate problems:
|
|
//--- * we generate dataset with all points being well separated, except for a few
|
|
//--- that have neighbors that are close enough to ruin the multiquadric algorithm
|
|
//--- but not close enough to be merged
|
|
//--- * we test that the model that was built actually uses Reg-QR
|
|
//--- * we also test that small and easy model without degeneracies does not use Reg-QR
|
|
//--- The idea of this test is to check that the solver signals about bad condition numbers
|
|
//--- by setting DbgRegQRUsedForDDM field.
|
|
for(nx=1; nx<=2; nx++)
|
|
{
|
|
//--- Solve easy problem, test that Reg-QR solver was NOT used
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
n=5;
|
|
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3174");
|
|
return(result);
|
|
}
|
|
CAp::SetErrorFlag(result,s.m_model3.m_dbgregqrusedforddm,"testrbfunit.ap:3177");
|
|
CAp::SetErrorFlag(result,rep.m_rmserror>0.001,"testrbfunit.ap:3178");
|
|
//--- Solve badly conditioned problem, test that Reg-QR solver was used
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
n=100;
|
|
nduplicate=100;
|
|
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
|
|
CApServ::RMatrixGrowRowsTo(xy,n+nduplicate,nx+ny);
|
|
for(i=0; i<=nduplicate-1; i++)
|
|
{
|
|
CAblasF::RCopyRR(nx+ny,xy,CHighQualityRand::HQRndUniformI(rs,n),xy,n+i);
|
|
k=CHighQualityRand::HQRndUniformI(rs,nx);
|
|
xy.Add(n+i,k,0.00001*CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetPoints(s,xy,n+nduplicate);
|
|
CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3200");
|
|
return(result);
|
|
}
|
|
CAp::SetErrorFlag(result,!s.m_model3.m_dbgregqrusedforddm,"testrbfunit.ap:3203");
|
|
}
|
|
//--- Test that various polynomial term types are actually handled:
|
|
//---*we generate "good random" problem
|
|
//--- * try various term types: linear, constant, zero
|
|
//--- * directly access model term and check that corresponding entries are zero
|
|
errtol=1.0E-3;
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
for(termtype=0; termtype<=2; termtype++)
|
|
{
|
|
n=nx+2+CHighQualityRand::HQRndUniformI(rs,10);
|
|
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CAblasF::RAllocV(nx,scalevec);
|
|
for(i=0; i<nx ; i++)
|
|
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
}
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
leadingzeros=0;
|
|
if(termtype==0)
|
|
{
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CRBF::RBFSetLinTerm(s);
|
|
leadingzeros=0;
|
|
}
|
|
if(termtype==1)
|
|
{
|
|
CRBF::RBFSetConstTerm(s);
|
|
leadingzeros=nx;
|
|
}
|
|
if(termtype==2)
|
|
{
|
|
CRBF::RBFSetZeroTerm(s);
|
|
leadingzeros=nx+1;
|
|
}
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3250");
|
|
return(result);
|
|
}
|
|
if((double)(rep.m_maxerror)>errtol)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3255");
|
|
return(result);
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
CAblasF::RAllocV(nx,x);
|
|
CAblasF::RCopyRV(nx,xy,i,x);
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny || (double)(MathAbs(y[j]-xy.Get(i,nx+j)))>errtol,"testrbfunit.ap:3265");
|
|
}
|
|
CAp::SetErrorFlag(result,s.m_modelversion!=3,"testrbfunit.ap:3267");
|
|
CAp::SetErrorFlag(result,CAp::Rows(s.m_model3.m_v)<ny,"testrbfunit.ap:3268");
|
|
CAp::SetErrorFlag(result,CAp::Cols(s.m_model3.m_v)<nx+1,"testrbfunit.ap:3269");
|
|
if(result)
|
|
return(result);
|
|
for(i=0; i<ny; i++)
|
|
{
|
|
for(j=0; j<leadingzeros ; j++)
|
|
CAp::SetErrorFlag(result,s.m_model3.m_v.Get(i,j)!=0.0,"testrbfunit.ap:3275");
|
|
for(j=leadingzeros; j<=nx; j++)
|
|
CAp::SetErrorFlag(result,s.m_model3.m_v.Get(i,j)==0.0,"testrbfunit.ap:3277");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test RBFUnpack()
|
|
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
meanseparation=(double)1/(double)gridsize;
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
CAp::Assert(gridsize>1);
|
|
CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx)));
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
|
|
k/=gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
hasscale=CHighQualityRand::HQRndNormal(rs)>0.0;
|
|
if(hasscale)
|
|
{
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CHighQualityRand::HQRndUniformR(rs)-1));
|
|
}
|
|
else
|
|
CAblasF::RSetAllocV(nx,1.0,scalevec);
|
|
nondistinctcnt=CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndUniformI(rs,10);
|
|
if(nondistinctcnt>0)
|
|
{
|
|
CApServ::RMatrixGrowRowsTo(xy,n+nondistinctcnt,nx+ny);
|
|
for(i=n; i<=n+nondistinctcnt-1; i++)
|
|
CAblasF::RCopyRR(nx+ny,xy,CHighQualityRand::HQRndUniformI(rs,n),xy,i);
|
|
n+=nondistinctcnt;
|
|
}
|
|
//--- Build model: either build directly in S, or build in S2 and serialize/unserialize it to S.
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
//--- Build in S directly
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
}
|
|
else
|
|
{
|
|
//--- Build in S2, copy via serialization/unserialization.
|
|
//--- This branch tests our ability to correctly save the model state.
|
|
CRBF::RBFCreate(nx,ny,s2);
|
|
SetXRBFAlgorithmExact(s2,algoidx,meanseparation);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s2,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s2,xy,n);
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CRBF::RBFAlloc(_local_serializer,s2);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CRBF::RBFSerialize(_local_serializer,s2);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CRBF::RBFUnserialize(_local_serializer,s);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3358");
|
|
return(result);
|
|
}
|
|
//--- Test RBFUnpack()
|
|
CRBF::RBFUnpack(s,unx,uny,uxwr,unc,uv,modelversion);
|
|
if(modelversion!=3)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3368");
|
|
return(result);
|
|
}
|
|
if((unx!=nx || uny!=ny) || unc!=n-nondistinctcnt)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3373");
|
|
return(result);
|
|
}
|
|
if(CAp::Cols(uv)!=nx+1 || CAp::Rows(uv)!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3378");
|
|
return(result);
|
|
}
|
|
if(CAp::Cols(uxwr)!=nx+ny+nx+3 || CAp::Rows(uxwr)!=unc)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3383");
|
|
return(result);
|
|
}
|
|
CAblasF::BSetAllocV(n,false,bflags);
|
|
for(i=0; i<=unc-1; i++)
|
|
{
|
|
//--- Test center information stored in the model:
|
|
//--- * all center indexes are in [0,N)
|
|
//--- * all center indexes are distinct
|
|
//--- * compare centers with dataset points
|
|
CAp::SetErrorFlag(result,uxwr.Get(i,nx+ny+nx+2)<0.0,"testrbfunit.ap:3395");
|
|
CAp::SetErrorFlag(result,uxwr.Get(i,nx+ny+nx+2)>(n-1),"testrbfunit.ap:3396");
|
|
k=(int)MathRound(uxwr.Get(i,nx+ny+nx+2));
|
|
if(k>=0 && k<n)
|
|
{
|
|
CAp::SetErrorFlag(result,bflags[k],"testrbfunit.ap:3400");
|
|
bflags[k]=true;
|
|
for(j=0; j<nx ; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(uxwr.Get(i,j)-xy.Get(k,j))>(1.0E-9),"testrbfunit.ap:3403");
|
|
}
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
for(i=0; i<=9; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,CHighQualityRand::HQRndUniformR(rs));
|
|
CRBF::RBFCalc(s,x,yref);
|
|
mx=1.0;
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
y.Set(j,uv.Get(j,nx));
|
|
for(k=0; k<nx ; k++)
|
|
y.Add(j,x[k]*uv.Get(j,k));
|
|
}
|
|
for(k=0; k<=unc-1; k++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<nx ; j++)
|
|
v+=CMath::Sqr(uxwr.Get(k,j)-x[j])/CMath::Sqr(uxwr.Get(k,nx+ny+j));
|
|
if(algoidx==0)
|
|
{
|
|
//--- Thin plate spline
|
|
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx)!=2.0,"testrbfunit.ap:3435");
|
|
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+1)!=0.0,"testrbfunit.ap:3436");
|
|
if(v!=0.0)
|
|
v*=MathLog(MathSqrt(v));
|
|
else
|
|
v=0;
|
|
}
|
|
if(algoidx==1)
|
|
{
|
|
//--- Multiquadric
|
|
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+0)!=10.0,"testrbfunit.ap:3447");
|
|
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+1)<=0.0,"testrbfunit.ap:3448");
|
|
v=MathSqrt(v+CMath::Sqr(uxwr.Get(k,nx+ny+nx+1)));
|
|
}
|
|
if(algoidx==2)
|
|
{
|
|
//--- Biharmonic
|
|
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx)!=1.0,"testrbfunit.ap:3456");
|
|
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+1)!=0.0,"testrbfunit.ap:3457");
|
|
v=MathSqrt(v);
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
y.Add(j,v*uxwr.Get(k,nx+j));
|
|
mx=MathMax(mx,MathAbs(uxwr.Get(k,nx+j)));
|
|
}
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(y[j]-yref[j]))>(double)(1.0E-9*mx),"testrbfunit.ap:3469");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test that smooth 1D function is reproduced (between nodes)
|
|
//--- with good precision. We test two model types: model with
|
|
//--- three layers and moderate initial radius, and model with
|
|
//--- large initial radius and large number of layers.
|
|
//--- This test:
|
|
//--- * generates test function on [-Width,+Width]. Two sets of
|
|
//--- nodes are generated-"model" ones and "test" ones.
|
|
//---*builds RBF model using "model" dataset
|
|
//---*test model using "test" dataset. Test points are more
|
|
//--- dense and are spread in [-0.9*Width, +0.9*Width] (reduced
|
|
//--- interval is used because RBF models are too bad near the
|
|
//--- boundaries).
|
|
//--- NOTE: we calculate maximum error for given function type
|
|
//--- and grid density over all modifications of the task,
|
|
//--- and only after that we perform comparison with tolerance
|
|
//--- level. It allows easier debugging.
|
|
for(functype=0; functype<=2; functype++)
|
|
{
|
|
for(densitytype=0; densitytype<=1; densitytype++)
|
|
{
|
|
//--- Select tolerance
|
|
lowprec=-999999;
|
|
highprec=-999999;
|
|
if(functype==0)
|
|
{
|
|
lowprec=1.0E-2;
|
|
highprec=1.0E-3;
|
|
}
|
|
else
|
|
{
|
|
if(functype==1)
|
|
{
|
|
lowprec=1.0E-1;
|
|
highprec=1.0E-2;
|
|
}
|
|
else
|
|
{
|
|
if(functype==2)
|
|
{
|
|
lowprec=1.0E-3;
|
|
highprec=1.0E-4;
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
if(densitytype==0)
|
|
errtol=lowprec;
|
|
else
|
|
errtol=highprec;
|
|
//--- Test
|
|
maxerr=0;
|
|
for(shaketype=0; shaketype<=1; shaketype++)
|
|
{
|
|
//--- Generate grid
|
|
width=1;
|
|
fractionalerror=false;
|
|
if(functype==0)
|
|
{
|
|
//--- sin(x) on [-2*pi,+2*pi]
|
|
n=20*(int)MathRound(MathPow(4,densitytype));
|
|
width=M_PI;
|
|
fractionalerror=false;
|
|
}
|
|
else
|
|
{
|
|
if(functype==1)
|
|
{
|
|
//--- exp(x) on [-3,+3]
|
|
n=50*(int)MathRound(MathPow(4,densitytype));
|
|
width=3;
|
|
fractionalerror=true;
|
|
}
|
|
else
|
|
{
|
|
if(functype==2)
|
|
{
|
|
//--- 1/(1+x^2) on [-3,+3]
|
|
n=50*(int)MathRound(MathPow(4,densitytype));
|
|
width=3;
|
|
fractionalerror=false;
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
xy=matrix<double>::Zeros(n,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=shaketype*0.25*(CMath::RandomReal()-0.5);
|
|
v=(i+v)/(n-1);
|
|
v=2*v-1;
|
|
xy.Set(i,0,width*v);
|
|
}
|
|
ntest=n*10;
|
|
xytest=matrix<double>::Zeros(ntest,2);
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,0,0.9*width*((double)(2*i)/(double)(ntest-1)-1));
|
|
//--- Evaluate function
|
|
if(functype==0)
|
|
{
|
|
//--- sin(x)
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,1,MathSin(xy.Get(i,0)));
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,1,MathSin(xytest.Get(i,0)));
|
|
}
|
|
else
|
|
{
|
|
if(functype==1)
|
|
{
|
|
//--- exp(x)
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,1,MathExp(xy.Get(i,0)));
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,1,MathExp(xytest.Get(i,0)));
|
|
}
|
|
else
|
|
{
|
|
if(functype==2)
|
|
{
|
|
//--- 1/(1+x^2)
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,1,1.0/(1.0+CMath::Sqr(xy.Get(i,0))));
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,1,1.0/(1+CMath::Sqr(xytest.Get(i,0))));
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(1,1,s);
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3624");
|
|
return(result);
|
|
}
|
|
//--- Check
|
|
x=vector<double>::Zeros(1);
|
|
for(i=0; i<ntest ; i++)
|
|
{
|
|
x.Set(0,xytest.Get(i,0));
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(fractionalerror)
|
|
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1))/MathAbs(xytest.Get(i,1)));
|
|
else
|
|
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1)));
|
|
}
|
|
}
|
|
//--- Check error
|
|
CAp::SetErrorFlag(result,maxerr>errtol,"testrbfunit.ap:3646");
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing scaling - related functionality of XRBF |
|
|
//| Returns True on failure(error flag is set). |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::ScaledXRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFModel s2;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer tsbuf;
|
|
int nx=0;
|
|
int ny=0;
|
|
double nodetol=0;
|
|
double randtol=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble y2;
|
|
CRowDouble xzero;
|
|
CRowDouble yref;
|
|
CRowDouble scalex;
|
|
CRowDouble scaley;
|
|
CRowDouble c0;
|
|
CRowDouble c1;
|
|
int n=0;
|
|
int gridsize=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
CHighQualityRandState rs;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- First test - random problem, test that using scaling
|
|
//--- does not change model significantly (except for
|
|
//--- rounding-related errors).
|
|
//--- We also apply scaling to Y, in order to test that it
|
|
//--- is correctly handled too.
|
|
for(nx=1; nx<=2; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
nodetol=0.001;
|
|
randtol=0.010;
|
|
scalex=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
scalex.Set(i,MathPow(4,CHighQualityRand::HQRndNormal(rs)));
|
|
scaley=vector<double>::Zeros(ny);
|
|
for(i=0; i<ny; i++)
|
|
scaley.Set(i,MathPow(4,CHighQualityRand::HQRndNormal(rs)));
|
|
n=150;
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
if(!CAp::Assert(gridsize>1))
|
|
return(true);
|
|
if(!CAp::Assert(n==MathPow(gridsize,nx)))
|
|
return(true);
|
|
c0=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
c0.Set(j,CMath::RandomReal()-0.5);
|
|
c1=vector<double>::Zeros(ny);
|
|
for(j=0; j<ny; j++)
|
|
c1.Set(j,CMath::RandomReal()-0.5);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
|
|
k/=gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy.Set(i,nx+j,0);
|
|
for(k=0; k<nx ; k++)
|
|
xy.Add(i,nx+j,c0[k]*MathCos(M_PI*(1+k)*xy.Get(i,k)));
|
|
xy.Mul(i,nx+j,c1[j]);
|
|
}
|
|
}
|
|
xy2=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy2.Set(i,j,xy.Get(i,j)* scalex[j]);
|
|
for(j=0; j<ny; j++)
|
|
xy2.Set(i,nx+j,xy.Get(i,nx+j)* scaley[j]);
|
|
}
|
|
//--- Build models
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3751");
|
|
return(result);
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s2);
|
|
CRBF::RBFSetAlgoThinPlateSpline(s2,0.0);
|
|
CRBF::RBFSetPointsAndScales(s2,xy2,n,scalex);
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3760");
|
|
return(result);
|
|
}
|
|
//--- Compare model values in grid points
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy.Get(i,j));
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy2.Get(i,j));
|
|
CRBF::RBFCalc(s2,x,y2);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>nodetol,"testrbfunit.ap:3777");
|
|
}
|
|
//--- Compare model values in random points
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,CMath::RandomReal());
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<nx ; j++)
|
|
x.Mul(j,scalex[j]);
|
|
CRBF::RBFCalc(s2,x,y2);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>randtol,"testrbfunit.ap:3793");
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test gridded evaluation of XRBFs. |
|
|
//| Returns True on errors. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::GridXRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int nx=0;
|
|
int ny=0;
|
|
bool hasscale;
|
|
double errtol=0;
|
|
int n=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
CMatrixDouble y2;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble x02;
|
|
CRowDouble x12;
|
|
CRowDouble x22;
|
|
CRowDouble scalevec;
|
|
CRowDouble scalevec2;
|
|
bool needy[];
|
|
bool rowflags[];
|
|
int n0=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int nkind=0;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int i0=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble yv;
|
|
CRowDouble yv2;
|
|
//--- Test 2-dimensional grid calculation
|
|
errtol=1.0E-12;
|
|
nx=2;
|
|
for(ny=1; ny<=4; ny++)
|
|
{
|
|
for(nkind=0; nkind<=2; nkind++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
hasscale=CMath::RandomInteger(2)==0;
|
|
if(hasscale)
|
|
{
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3869");
|
|
return(result);
|
|
}
|
|
//--- Prepare grid to test
|
|
n0=1+CMath::RandomInteger(50);
|
|
n1=1+CMath::RandomInteger(50);
|
|
if(nkind==1)
|
|
{
|
|
k=CMath::RandomInteger(2);
|
|
if(k==0)
|
|
n0=1;
|
|
if(k==1)
|
|
n1=1;
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
n0=1;
|
|
n1=1;
|
|
}
|
|
else
|
|
{
|
|
if(!CAp::Assert(nkind==0))
|
|
return(true);
|
|
}
|
|
}
|
|
x0=vector<double>::Zeros(n0);
|
|
x0.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n0 ; i++)
|
|
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
|
|
x1=vector<double>::Zeros(n1);
|
|
x1.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n1; i++)
|
|
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
|
|
ArrayResize(needy,n0*n1);
|
|
v=MathPow(10,-(3*CMath::RandomReal()));
|
|
for(i=0; i<n0*n1 ; i++)
|
|
needy[i]=CMath::RandomReal()<v;
|
|
//--- Test at grid
|
|
x=vector<double>::Zeros(nx);
|
|
CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv);
|
|
for(i0=0; i0<n0 ; i0++)
|
|
{
|
|
for(i1=0; i1<n1; i1++)
|
|
{
|
|
x.Set(0,x0[i0]);
|
|
x.Set(1,x1[i1]);
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(i=0; i<ny; i++)
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(y[i]-yv[i+ny*(i0+i1*n0)]))>errtol,"testrbfunit.ap:3918");
|
|
}
|
|
}
|
|
//--- Test calculation on subset of regular grid:
|
|
//--- * Test 1: compare full and subset versions
|
|
//--- * Test 2: check sparsity. Subset function may perform additional
|
|
//--- evaluations because it processes data micro-row by micro-row.
|
|
//--- So, we can't check that all elements which were not flagged
|
|
//--- are zero - some of them may become non-zero. However, if entire
|
|
//--- row is empty, we can reasonably expect (informal guarantee)
|
|
//--- that it is not processed. So, we check empty (completely
|
|
//--- unflagged) rows
|
|
CRBF::RBFGridCalc2VSubset(s,x0,n0,x1,n1,needy,yv2);
|
|
for(i=0; i<=ny*n0*n1-1; i++)
|
|
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:3937");
|
|
//--- Test legacy function
|
|
CRBF::RBFGridCalc2(s,x0,n0,x1,n1,y2);
|
|
for(i=0; i<n0*n1 ; i++)
|
|
{
|
|
if(ny==1)
|
|
CAp::SetErrorFlag(result,MathAbs(yv[i]-y2.Get(i%n0,i/n0))>errtol,"testrbfunit.ap:3947");
|
|
else
|
|
CAp::SetErrorFlag(result,y2.Get(i%n0,i/n0)!=0.0,"testrbfunit.ap:3949");
|
|
}
|
|
}
|
|
}
|
|
//--- Test 3-dimensional grid calculation
|
|
errtol=1.0E-12;
|
|
nx=3;
|
|
for(ny=1; ny<=4; ny++)
|
|
{
|
|
for(nkind=0; nkind<=2; nkind++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
hasscale=CMath::RandomInteger(2)==0;
|
|
if(hasscale)
|
|
{
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3993");
|
|
return(result);
|
|
}
|
|
//--- Prepare grid to test
|
|
n0=1+CMath::RandomInteger(50);
|
|
n1=1+CMath::RandomInteger(50);
|
|
n2=1+CMath::RandomInteger(50);
|
|
if(nkind==1)
|
|
{
|
|
k=CMath::RandomInteger(3);
|
|
if(k==0)
|
|
n0=1;
|
|
if(k==1)
|
|
n1=1;
|
|
if(k==2)
|
|
n2=1;
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
n0=1;
|
|
n1=1;
|
|
n2=1;
|
|
}
|
|
else
|
|
{
|
|
if(!CAp::Assert(nkind==0))
|
|
return(true);
|
|
}
|
|
}
|
|
x0=vector<double>::Zeros(n0);
|
|
x0.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n0 ; i++)
|
|
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
|
|
x1=vector<double>::Zeros(n1);
|
|
x1.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n1; i++)
|
|
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
|
|
x2=vector<double>::Zeros(n2);
|
|
x2.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n2 ; i++)
|
|
x2.Set(i,x2[i-1]+CMath::RandomReal()/n2);
|
|
ArrayResize(needy,n0*n1*n2);
|
|
v=MathPow(10,-(3*CMath::RandomReal()));
|
|
for(i=0; i<n0*n1*n2; i++)
|
|
needy[i]=CMath::RandomReal()<v;
|
|
//--- Test at grid
|
|
x=vector<double>::Zeros(nx);
|
|
CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv);
|
|
for(i0=0; i0<n0 ; i0++)
|
|
{
|
|
for(i1=0; i1<n1; i1++)
|
|
for(i2=0; i2<n2 ; i2++)
|
|
{
|
|
x.Set(0,x0[i0]);
|
|
x.Set(1,x1[i1]);
|
|
x.Set(2,x2[i2]);
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(i=0; i<ny; i++)
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(y[i]-yv[i+ny*(i0+i1*n0+i2*n0*n1)]))>errtol,"testrbfunit.ap:4052");
|
|
}
|
|
}
|
|
//--- Test calculation on subset of regular grid:
|
|
//--- * Test 1: compare full and subset versions
|
|
//--- * Test 2: check sparsity. Subset function may perform additional
|
|
//--- evaluations because it processes data micro-row by micro-row.
|
|
//--- So, we can't check that all elements which were not flagged
|
|
//--- are zero - some of them may become non-zero. However, if entire
|
|
//--- row is empty, we can reasonably expect (informal guarantee)
|
|
//--- that it is not processed. So, we check empty (completely
|
|
//--- unflagged) rows
|
|
ArrayResize(rowflags,n1*n2);
|
|
for(i=0; i<=n1*n2-1; i++)
|
|
rowflags[i]=false;
|
|
for(i=0; i<=n0*n1*n2-1; i++)
|
|
{
|
|
if(needy[i])
|
|
rowflags[i/n0]=true;
|
|
}
|
|
CRBF::RBFGridCalc3VSubset(s,x0,n0,x1,n1,x2,n2,needy,yv2);
|
|
for(i=0; i<ny*n0*n1*n2; i++)
|
|
{
|
|
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:4077");
|
|
CAp::SetErrorFlag(result,!rowflags[i/(ny*n0)] && yv2[i]!=0.0,"testrbfunit.ap:4078");
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing basic functionality of RBF module with |
|
|
//| hierarchical algorithm. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::BasicHRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFModel s2;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer tsbuf;
|
|
int nx=0;
|
|
int ny=0;
|
|
int linterm=0;
|
|
int bf=0;
|
|
double rbase=0;
|
|
int nlayers=0;
|
|
double errtol=0;
|
|
double scalefactor=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xytest;
|
|
CMatrixDouble uxwr;
|
|
CMatrixDouble uv;
|
|
CMatrixDouble xy2;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble y2;
|
|
CRowDouble xzero;
|
|
CRowDouble yref;
|
|
CRowDouble scalevec;
|
|
int n=0;
|
|
int ntest=0;
|
|
int gridsize=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double r0=0;
|
|
int margin=0;
|
|
double threshold=0;
|
|
double v=0;
|
|
int functype=0;
|
|
int densitytype=0;
|
|
double width=0;
|
|
double lowprec=0;
|
|
double highprec=0;
|
|
int modeltype=0;
|
|
int shaketype=0;
|
|
double maxerr=0;
|
|
bool fractionalerror;
|
|
int unx=0;
|
|
int uny=0;
|
|
int unc=0;
|
|
int modelversion=0;
|
|
bool hasscale;
|
|
CHighQualityRandState rs;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- First test - random problem, ability to build model
|
|
//--- which reproduces function value in all points with
|
|
//--- good precision.
|
|
//--- We also test properties of the linear term - that
|
|
//--- model value in far away points is either constant
|
|
//--- or exactly zero (for corresponding kinds of linear
|
|
//--- term).
|
|
//--- All dataset points are located in unit cube on
|
|
//--- regular grid. We do not use smoothing for this test.
|
|
//--- We use/test following functions:
|
|
//--- * RBFCalc()
|
|
//--- * RBFCalc2()
|
|
//--- * RBFCalc3()
|
|
//--- * RBFCalcBuf()
|
|
//--- * RBFTsCalcBuf()
|
|
errtol=1.0E-6;
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
rbase=3.0;
|
|
nlayers=10;
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
linterm=1+CMath::RandomInteger(3);
|
|
ny=1+CMath::RandomInteger(2);
|
|
bf=CMath::RandomInteger(2);
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
if(!CAp::Assert(gridsize>1))
|
|
return(true);
|
|
if(!CAp::Assert(n==MathPow(gridsize,nx)))
|
|
return(true);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,k%gridsize);
|
|
k/=gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4197");
|
|
return(result);
|
|
}
|
|
CRBF::RBFCreateCalcBuffer(s,tsbuf);
|
|
//--- Test ability to reproduce function value
|
|
//--- NOTE: we use RBFCalc(XZero) to guarantee that internal state of
|
|
//--- RBF model is "reset" between subsequent calls of different
|
|
//--- functions. It allows us to make sure that we have no bug
|
|
//--- like function simply returning latest result
|
|
x=vector<double>::Zeros(nx);
|
|
xzero=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
for(j=0; j<nx ; j++)
|
|
xzero.Set(j,0);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy.Get(i,j));
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
if(CMath::RandomReal()>0.5)
|
|
yref=vector<double>::Zeros(ny+1);
|
|
CRBF::RBFCalc(s,x,yref);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(yref[j]-xy.Get(i,nx+j))>errtol,"testrbfunit.ap:4226");
|
|
CAp::SetErrorFlag(result,CAp::Len(yref)!=ny,"testrbfunit.ap:4227");
|
|
if(nx==1 && ny==1)
|
|
{
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,x[0])!=yref[0],"testrbfunit.ap:4233");
|
|
}
|
|
if(nx==2 && ny==1)
|
|
{
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc2(s,x[0],x[1])!=yref[0],"testrbfunit.ap:4240");
|
|
}
|
|
if(nx==3 && ny==1)
|
|
{
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
CAp::SetErrorFlag(result,CRBF::RBFCalc3(s,x[0],x[1],x[2])!=yref[0],"testrbfunit.ap:4247");
|
|
}
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
y=vector<double>::Zeros(ny+1);
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:4256");
|
|
}
|
|
else
|
|
{
|
|
y=vector<double>::Zeros(ny-1);
|
|
CRBF::RBFCalcBuf(s,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:4262");
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:4265");
|
|
CRBF::RBFCalc(s,xzero,y);
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
y=vector<double>::Zeros(ny+1);
|
|
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:4273");
|
|
}
|
|
else
|
|
{
|
|
y=vector<double>::Zeros(ny-1);
|
|
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
|
|
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:4279");
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:4282");
|
|
}
|
|
//--- Test that:
|
|
//--- a) model with zero linear term is zero far away from dataset
|
|
//--- b) model with constant linear term is constant far away from dataset
|
|
x=vector<double>::Zeros(nx);
|
|
if(linterm==2)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
x.Set(j,gridsize+1000*rbase);
|
|
else
|
|
x.Set(j,0-1000*rbase);
|
|
}
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
x.Set(j,gridsize+1000*rbase);
|
|
else
|
|
x.Set(j,0-1000*rbase);
|
|
}
|
|
CRBF::RBFCalc(s,x,y2);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=(double)(y2[j]),"testrbfunit.ap:4306");
|
|
}
|
|
if(linterm==3)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
x.Set(j,gridsize+1000*rbase);
|
|
else
|
|
x.Set(j,0-1000*rbase);
|
|
}
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=0.0,"testrbfunit.ap:4317");
|
|
}
|
|
}
|
|
//--- Test RBFUnpack()
|
|
for(nx=1; nx<=2; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
rbase=0.33;
|
|
nlayers=5;
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
linterm=1+CMath::RandomInteger(3);
|
|
bf=CMath::RandomInteger(2);
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
if(!CAp::Assert(gridsize>1))
|
|
return(true);
|
|
if(!CAp::Assert(n==MathPow(gridsize,nx)))
|
|
return(true);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
|
|
k/=gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
hasscale=CMath::RandomInteger(2)==0;
|
|
scalevec=vector<double>::Zeros(nx);
|
|
if(hasscale)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4378");
|
|
return(result);
|
|
}
|
|
//--- Test RBFUnpack()
|
|
CRBF::RBFUnpack(s,unx,uny,uxwr,unc,uv,modelversion);
|
|
if(modelversion!=2)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4388");
|
|
return(result);
|
|
}
|
|
if(unx!=nx || uny!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4393");
|
|
return(result);
|
|
}
|
|
if(CAp::Cols(uv)!=nx+1 || CAp::Rows(uv)!=ny)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4398");
|
|
return(result);
|
|
}
|
|
if(linterm==2)
|
|
{
|
|
for(i=0; i<ny; i++)
|
|
for(j=0; j<nx ; j++)
|
|
CAp::SetErrorFlag(result,(double)(uv.Get(i,j))!=0.0,"testrbfunit.ap:4405");
|
|
}
|
|
if(linterm==3)
|
|
{
|
|
for(i=0; i<ny; i++)
|
|
for(j=0; j<=nx; j++)
|
|
CAp::SetErrorFlag(result,(double)(uv.Get(i,j))!=0.0,"testrbfunit.ap:4411");
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
for(i=0; i<=9; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,CMath::RandomReal());
|
|
CRBF::RBFCalc(s,x,yref);
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
y.Set(j,uv.Get(j,nx));
|
|
for(k=0; k<nx ; k++)
|
|
y.Add(j,x[k]*uv.Get(j,k));
|
|
}
|
|
for(k=0; k<=unc-1; k++)
|
|
{
|
|
v=0;
|
|
for(j=0; j<nx ; j++)
|
|
v+=CMath::Sqr(uxwr.Get(k,j)-x[j])/CMath::Sqr(uxwr.Get(k,nx+ny+j));
|
|
if(v<(double)(CRBFV2::RBFV2FarRadius(bf)*CRBFV2::RBFV2FarRadius(bf)))
|
|
v=CRBFV2::RBFV2BasisFunc(bf,v);
|
|
else
|
|
v=0;
|
|
for(j=0; j<ny; j++)
|
|
y.Add(j,v*uxwr.Get(k,nx+j));
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-yref[j])>(1.0E-9),"testrbfunit.ap:4446");
|
|
}
|
|
}
|
|
}
|
|
//--- Test that smooth 1D function is reproduced (between nodes)
|
|
//--- with good precision. We test two model types: model with
|
|
//--- three layers and moderate initial radius, and model with
|
|
//--- large initial radius and large number of layers.
|
|
//--- This test:
|
|
//--- * generates test function on [-Width,+Width]. Two sets of
|
|
//--- nodes are generated-"model" ones and "test" ones.
|
|
//---*builds RBF model using "model" dataset
|
|
//---*test model using "test" dataset. Test points are more
|
|
//--- dense and are spread in [-0.9*Width, +0.9*Width] (reduced
|
|
//--- interval is used because RBF models are too bad near the
|
|
//--- boundaries).
|
|
//--- NOTE: we calculate maximum error for given function type
|
|
//--- and grid density over all modifications of the task,
|
|
//--- and only after that we perform comparison with tolerance
|
|
//--- level. It allows easier debugging.
|
|
for(functype=0; functype<=2; functype++)
|
|
{
|
|
for(densitytype=0; densitytype<=1; densitytype++)
|
|
{
|
|
//--- Select tolerance
|
|
lowprec=-999999;
|
|
highprec=-999999;
|
|
switch(functype)
|
|
{
|
|
case 0:
|
|
lowprec=1.0E-2;
|
|
highprec=1.0E-3;
|
|
break;
|
|
case 1:
|
|
lowprec=1.0E-1;
|
|
highprec=1.0E-2;
|
|
break;
|
|
case 2:
|
|
lowprec=1.0E-3;
|
|
highprec=1.0E-4;
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
if(densitytype==0)
|
|
errtol=lowprec;
|
|
else
|
|
errtol=highprec;
|
|
//--- Test
|
|
maxerr=0;
|
|
for(modeltype=0; modeltype<=1; modeltype++)
|
|
{
|
|
for(shaketype=0; shaketype<=1; shaketype++)
|
|
{
|
|
//--- Generate grid
|
|
width=1;
|
|
fractionalerror=false;
|
|
switch(functype)
|
|
{
|
|
case 0:
|
|
//--- sin(x) on [-2*pi,+2*pi]
|
|
n=17*(int)MathRound(MathPow(4,densitytype));
|
|
width=M_PI;
|
|
fractionalerror=false;
|
|
break;
|
|
case 1:
|
|
//--- exp(x) on [-3,+3]
|
|
n=50*(int)MathRound(MathPow(4,densitytype));
|
|
width=3;
|
|
fractionalerror=true;
|
|
break;
|
|
case 2:
|
|
//--- 1/(1+x^2) on [-3,+3]
|
|
n=20*(int)MathRound(MathPow(4,densitytype));
|
|
width=3;
|
|
fractionalerror=false;
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
xy=matrix<double>::Zeros(n,2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
v=shaketype*0.25*(CMath::RandomReal()-0.5);
|
|
v=(i+v)/(n-1);
|
|
v=2*v-1;
|
|
xy.Set(i,0,width*v);
|
|
}
|
|
ntest=n*10;
|
|
xytest=matrix<double>::Zeros(ntest,2);
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,0,0.9*width*((double)(2*i)/(double)(ntest-1)-1));
|
|
//--- Evaluate function
|
|
switch(functype)
|
|
{
|
|
case 0:
|
|
//--- sin(x)
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,1,MathSin(xy.Get(i,0)));
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,1,MathSin(xytest.Get(i,0)));
|
|
break;
|
|
case 1:
|
|
//--- exp(x)
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,1,MathExp(xy.Get(i,0)));
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,1,MathExp(xytest.Get(i,0)));
|
|
break;
|
|
case 2:
|
|
//--- 1/(1+x^2)
|
|
for(i=0; i<n; i++)
|
|
xy.Set(i,1,1/(1+CMath::Sqr(xy.Get(i,0))));
|
|
for(i=0; i<ntest ; i++)
|
|
xytest.Set(i,1,1/(1+CMath::Sqr(xytest.Get(i,0))));
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
//--- Select model properties and precision
|
|
switch(modeltype)
|
|
{
|
|
case 0:
|
|
rbase=4.0*(2*width/n);
|
|
nlayers=3;
|
|
break;
|
|
case 1:
|
|
rbase=16.0*(2*width/n);
|
|
nlayers=6;
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
return(true);
|
|
}
|
|
//--- Build model
|
|
bf=CMath::RandomInteger(2);
|
|
CRBF::RBFCreate(1,1,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4620");
|
|
return(result);
|
|
}
|
|
//--- Check
|
|
x=vector<double>::Zeros(1);
|
|
for(i=0; i<ntest ; i++)
|
|
{
|
|
x.Set(0,xytest.Get(i,0));
|
|
CRBF::RBFCalc(s,x,y);
|
|
if(fractionalerror)
|
|
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1))/MathAbs(xytest.Get(i,1)));
|
|
else
|
|
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1)));
|
|
}
|
|
}
|
|
}
|
|
//--- Check error
|
|
CAp::SetErrorFlag(result,maxerr>errtol,"testrbfunit.ap:4642");
|
|
}
|
|
}
|
|
//--- Scaling test - random problem, we test that after
|
|
//--- scaling of all variables and radius by 2^K (for some K)
|
|
//--- we will get exactly same results (up to the last bit of
|
|
//--- mantissa).
|
|
//--- It is very strong requirement for algorithm stability,
|
|
//--- but it is satisfiable in most implementations of RBFs,
|
|
//--- because all operations involving spatial values are usually
|
|
//--- followed by division by radius, and using multiplier which
|
|
//--- is exactly power of 2 results in no changes in numbers
|
|
//--- being returned.
|
|
//--- It allows to test different scale-related bugs
|
|
//--- (say, situation when deep in kd-tree search code we compare
|
|
//--- against R instead of R^2).
|
|
//--- All dataset points are located in unit cube on
|
|
//--- regular grid.
|
|
//--- We do not use smoothing for this test.
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
rbase=0.33;
|
|
nlayers=CMath::RandomInteger(4);
|
|
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
ny=1+CMath::RandomInteger(3);
|
|
linterm=1+CMath::RandomInteger(3);
|
|
bf=CMath::RandomInteger(2);
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
xy2=matrix<double>::Zeros(n,nx+ny);
|
|
if(!CAp::Assert(gridsize>1))
|
|
return(true);
|
|
if(!CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx))))
|
|
return(true);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
|
|
xy2.Set(i,j,xy.Get(i,j)*scalefactor);
|
|
k/=gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
xy2.Set(i,nx+j,xy.Get(i,nx+j));
|
|
}
|
|
}
|
|
//--- Build model 1
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4718");
|
|
return(result);
|
|
}
|
|
//--- Build model 2
|
|
CRBF::RBFCreate(nx,ny,s2);
|
|
CRBF::RBFSetV2BF(s2,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s2,rbase*scalefactor,nlayers,0.0);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s2);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s2);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s2);
|
|
CRBF::RBFSetPoints(s2,xy2,n);
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4738");
|
|
return(result);
|
|
}
|
|
//--- Compare models
|
|
x=vector<double>::Zeros(nx);
|
|
y=vector<double>::Zeros(ny);
|
|
y2=vector<double>::Zeros(ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy.Get(i,j));
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy2.Get(i,j));
|
|
CRBF::RBFCalc(s2,x,y2);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,y[j]!=y2[j],"testrbfunit.ap:4757");
|
|
}
|
|
}
|
|
//--- Test smoothing properties of RBF model: model with just one layer
|
|
//--- and large initial radius will produce "average" value of neighbors.
|
|
//--- In order to test it we create regular grid, fill it with regular
|
|
//--- +1/-1 pattern, and test model values in the inner points. Model
|
|
//--- values should be around zero (we use handcrafted threshold to test
|
|
//--- it). Radius is chosen to be several times larger than grid step.
|
|
//--- We perform test for 2D model, because same behavior is expected
|
|
//--- regardless of dimensionality.
|
|
r0=6;
|
|
margin=5;
|
|
threshold=0.05;
|
|
gridsize=2*margin+5;
|
|
nx=2;
|
|
ny=1;
|
|
for(bf=0; bf<=1; bf++)
|
|
{
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,r0,1,1.0E-1);
|
|
CRBF::RBFSetZeroTerm(s);
|
|
xy=matrix<double>::Zeros(gridsize*gridsize,nx+ny);
|
|
for(i=0; i<gridsize; i++)
|
|
for(j=0; j<gridsize; j++)
|
|
{
|
|
xy.Set(i*gridsize+j,0,i);
|
|
xy.Set(i*gridsize+j,1,j);
|
|
xy.Set(i*gridsize+j,2,0.01*(CMath::RandomReal()-0.5)+(2*((i+j)%2)-1));
|
|
}
|
|
CRBF::RBFSetPoints(s,xy,gridsize*gridsize);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4798");
|
|
return(result);
|
|
}
|
|
v=0.0;
|
|
for(i=margin; i<gridsize-margin ; i++)
|
|
{
|
|
for(j=margin; j<gridsize-margin ; j++)
|
|
v=MathMax(v,MathAbs(CRBF::RBFCalc2(s,i,j)));
|
|
}
|
|
CAp::SetErrorFlag(result,v>threshold,"testrbfunit.ap:4805");
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing scaling - related functionality of RBF |
|
|
//| module with hierarchical algorithm. |
|
|
//| Returns True on failure(error flag is set). |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::ScaleHRBFTtest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFModel s2;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer tsbuf;
|
|
int nx=0;
|
|
int ny=0;
|
|
int linterm=0;
|
|
int bf=0;
|
|
double rbase=0;
|
|
int nlayers=0;
|
|
double errtol=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble y2;
|
|
CRowDouble xzero;
|
|
CRowDouble yref;
|
|
CRowDouble scalex;
|
|
CRowDouble scaley;
|
|
CRowDouble c0;
|
|
CRowDouble c1;
|
|
int n=0;
|
|
int gridsize=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int strictness=0;
|
|
double lambdav=0;
|
|
//--- First test - random problem, test that using scaling
|
|
//--- does not change model significantly (except for
|
|
//--- rounding-related errors).
|
|
//--- We test two kinds of scaling:
|
|
//---*"strict", which is scaling by some power of 2, and
|
|
//--- which should result in bit-to-bit equivalence of results
|
|
//---*"non-strict", which is scaling by random number, and
|
|
//--- which should result in approximate equivalence
|
|
//--- We also apply scaling to Y, in order to test that it
|
|
//--- is correctly handled too.
|
|
for(strictness=0; strictness<=1; strictness++)
|
|
{
|
|
for(nx=1; nx<=2; nx++)
|
|
{
|
|
for(ny=1; ny<=2; ny++)
|
|
{
|
|
//--- problem setup
|
|
if(strictness==1)
|
|
{
|
|
errtol=0;
|
|
scalex=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
scalex.Set(i,MathPow(16,CMath::RandomInteger(3)-1));
|
|
scaley=vector<double>::Zeros(ny);
|
|
for(i=0; i<ny; i++)
|
|
scaley.Set(i,MathPow(16,CMath::RandomInteger(3)-1));
|
|
}
|
|
else
|
|
{
|
|
errtol=1.0E-3;
|
|
scalex=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
scalex.Set(i,MathPow(4,2*CMath::RandomReal()-1));
|
|
scaley=vector<double>::Zeros(ny);
|
|
for(i=0; i<ny; i++)
|
|
scaley.Set(i,MathPow(4,2*CMath::RandomReal()-1));
|
|
}
|
|
n=150;
|
|
rbase=0.33;
|
|
nlayers=2;
|
|
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
|
|
linterm=1+CMath::RandomInteger(3);
|
|
bf=CMath::RandomInteger(2);
|
|
lambdav=1.0E-3*CMath::RandomInteger(2);
|
|
n=(int)MathRound(MathPow(gridsize,nx));
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
CAp::Assert(gridsize>1);
|
|
CAp::Assert((double)(n)==MathPow(gridsize,nx));
|
|
c0=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
c0.Set(j,CMath::RandomReal()-0.5);
|
|
c1=vector<double>::Zeros(ny);
|
|
for(j=0; j<ny; j++)
|
|
c1.Set(j,CMath::RandomReal()-0.5);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
|
|
k/=gridsize;
|
|
}
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
xy.Set(i,nx+j,0);
|
|
for(k=0; k<nx ; k++)
|
|
xy.Add(i,nx+j,c0[k]*MathCos(M_PI*(1+k)*xy.Get(i,k)));
|
|
xy.Mul(i,nx+j,c1[j]);
|
|
}
|
|
}
|
|
xy2=matrix<double>::Zeros(n,nx+ny);
|
|
for(j=0; j<nx ; j++)
|
|
xy2.Col(j,xy.Col(j)*scalex[j]);
|
|
for(j=0; j<ny; j++)
|
|
xy2.Col(nx+j,xy.Col(nx+j)*scaley[j]);
|
|
//--- Build models
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,lambdav);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4945");
|
|
return(result);
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s2);
|
|
CRBF::RBFSetV2BF(s2,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s2,rbase,nlayers,lambdav);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s2);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s2);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s2);
|
|
CRBF::RBFSetPointsAndScales(s2,xy2,n,scalex);
|
|
CRBF::RBFBuildModel(s2,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4961");
|
|
return(result);
|
|
}
|
|
//--- Compare model values in grid points
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy.Get(i,j));
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,xy2.Get(i,j));
|
|
CRBF::RBFCalc(s2,x,y2);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>errtol,"testrbfunit.ap:4978"+" j="+string(j)+" y[j]="+string(y[j])+" y2[j]="+string(y2[j])+" scaley[j]="+string(scaley[j])+" errtol="+string(errtol));
|
|
}
|
|
//--- Compare model values in random points
|
|
x=vector<double>::Zeros(nx);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,CMath::RandomReal());
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(j=0; j<nx ; j++)
|
|
x.Mul(j,scalex[j]);
|
|
CRBF::RBFCalc(s2,x,y2);
|
|
for(j=0; j<ny; j++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>errtol,"testrbfunit.ap:4994"+" j="+string(j)+" y[j]="+string(y[j])+" y2[j]="+string(y2[j])+" scaley[j]="+string(scaley[j])+" errtol="+string(errtol));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test special properties of hierarchical RBFs. |
|
|
//| Returns True on errors. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::SpecHRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int n=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
double rbase=0;
|
|
CRBFModel s0;
|
|
CRBFModel s1;
|
|
CRBFReport rep;
|
|
int nlayers=0;
|
|
CMatrixDouble xy;
|
|
int i=0;
|
|
int j=0;
|
|
double vdiff=0;
|
|
double d2=0;
|
|
int v2its=0;
|
|
double vref=0;
|
|
double vfunc=0;
|
|
double maxerr=0;
|
|
CHighQualityRandState rs;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test properties of RBF basis functions - we compare values
|
|
//--- returned by RBFV2BasisFunc() against analytic expressions
|
|
//--- which are approximately modeled by RBFV2BasisFunc().
|
|
d2=0.0;
|
|
while(d2<100.0)
|
|
{
|
|
vref=MathExp(-d2);
|
|
vfunc=CRBFV2::RBFV2BasisFunc(0,d2);
|
|
CAp::SetErrorFlag(result,MathAbs(vref-vfunc)>(1.0E-9*vref),"testrbfunit.ap:5036");
|
|
d2+=1.0/64.0;
|
|
}
|
|
d2=0.0;
|
|
maxerr=0.0;
|
|
while(d2<16.0)
|
|
{
|
|
vref=MathMax(1-d2/9,0)*MathExp(-d2);
|
|
vfunc=CRBFV2::RBFV2BasisFunc(1,d2);
|
|
maxerr=MathMax(maxerr,MathAbs(vref-vfunc));
|
|
CAp::SetErrorFlag(result,MathAbs(vref-vfunc)>0.005,"testrbfunit.ap:5046");
|
|
d2+=1.0/64.0;
|
|
}
|
|
//--- Test that tiny changes in dataset points introduce tiny
|
|
//--- numerical noise. The noise magnitude depends on the
|
|
//--- properties of the linear solver being used. We compare
|
|
//--- noise magnitude against hard-coded values.
|
|
//--- Test sequence:
|
|
//--- * create model #1
|
|
//--- * create model #2 using slightly modified dataset
|
|
nx=2;
|
|
ny=1;
|
|
n=20;
|
|
rbase=2.0;
|
|
nlayers=1;
|
|
v2its=50;
|
|
xy=matrix<double>::Zeros(n*n,nx+ny);
|
|
for(i=0; i<=n*n-1; i++)
|
|
{
|
|
xy.Set(i,0,i%n);
|
|
xy.Set(i,1,i/n);
|
|
xy.Set(i,2,MathSin(i));
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s0);
|
|
CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0);
|
|
CRBF::RBFSetV2Its(s0,v2its);
|
|
CRBF::RBFSetPoints(s0,xy,n*n);
|
|
CRBF::RBFBuildModel(s0,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5081");
|
|
return(result);
|
|
}
|
|
for(i=0; i<=n*n-1; i++)
|
|
{
|
|
xy.Add(i,0,1.0E-14*MathSin(3*i));
|
|
xy.Add(i,1,1.0E-14*MathSin(7*i*i));
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s1);
|
|
CRBF::RBFSetAlgoHierarchical(s1,rbase,nlayers,0.0);
|
|
CRBF::RBFSetV2Its(s1,v2its);
|
|
CRBF::RBFSetPoints(s1,xy,n*n);
|
|
CRBF::RBFBuildModel(s1,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5096");
|
|
return(result);
|
|
}
|
|
vdiff=0;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
for(j=0; j<=n-2; j++)
|
|
vdiff+=MathAbs(CRBF::RBFCalc2(s0,0.5+i,0.5+j)-CRBF::RBFCalc2(s1,0.5+i,0.5+j))/CMath::Sqr(n-1);
|
|
}
|
|
CAp::SetErrorFlag(result,vdiff>0.002 || vdiff<0.00001,"testrbfunit.ap:5103");
|
|
//--- Test progress reports: progress before model construction is
|
|
//--- zero, progress after model is built is 1. More detailed tests
|
|
//--- are performed in the multithreaded TestHRBFProgress().
|
|
nx=2;
|
|
ny=1;
|
|
n=20;
|
|
rbase=1.0;
|
|
nlayers=3;
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CRBF::RBFCreate(nx,ny,s0);
|
|
CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0);
|
|
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=0.0,"testrbfunit.ap:5125");
|
|
CRBF::RBFBuildModel(s0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testrbfunit.ap:5127");
|
|
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=1.0,"testrbfunit.ap:5128");
|
|
CRBF::RBFCreate(nx,ny,s0);
|
|
CRBF::RBFSetPoints(s0,xy,n);
|
|
CRBF::RBFSetAlgoHierarchical(s0,rbase,0,0.0);
|
|
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=0.0,"testrbfunit.ap:5134");
|
|
CRBF::RBFBuildModel(s0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testrbfunit.ap:5136");
|
|
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=1.0,"testrbfunit.ap:5137");
|
|
CRBF::RBFCreate(nx,ny,s0);
|
|
CRBF::RBFSetPoints(s0,xy,n);
|
|
CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0);
|
|
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=0.0,"testrbfunit.ap:5143");
|
|
CRBF::RBFBuildModel(s0,rep);
|
|
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testrbfunit.ap:5145");
|
|
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=1.0,"testrbfunit.ap:5146");
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test gridded evaluation of hierarchical RBFs. |
|
|
//| Returns True on errors. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestRBFUnit::GridHRBFTest(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
int linterm=0;
|
|
int bf=0;
|
|
double rbase=0;
|
|
int nlayers=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
bool hasscale;
|
|
double errtol=0;
|
|
int n=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
CMatrixDouble y2;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble x02;
|
|
CRowDouble x12;
|
|
CRowDouble x22;
|
|
CRowDouble scalevec;
|
|
CRowDouble scalevec2;
|
|
bool needy[];
|
|
bool rowflags[];
|
|
int n0=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int nkind=0;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int i0=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble yv;
|
|
CRowDouble yv2;
|
|
double scalefactor=0;
|
|
double lambdav=0;
|
|
//--- Test 2-dimensional grid calculation
|
|
errtol=1.0E-12;
|
|
nx=2;
|
|
for(ny=1; ny<=4; ny++)
|
|
{
|
|
for(nkind=0; nkind<=2; nkind++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
rbase=0.10;
|
|
nlayers=CMath::RandomInteger(3);
|
|
linterm=1+CMath::RandomInteger(3);
|
|
lambdav=1.0E-3*CMath::RandomInteger(2);
|
|
bf=CMath::RandomInteger(2);
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
hasscale=CMath::RandomInteger(2)==0;
|
|
if(hasscale)
|
|
{
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,lambdav);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5238");
|
|
return(result);
|
|
}
|
|
//--- Prepare grid to test
|
|
n0=1+CMath::RandomInteger(50);
|
|
n1=1+CMath::RandomInteger(50);
|
|
if(nkind==1)
|
|
{
|
|
k=CMath::RandomInteger(2);
|
|
if(k==0)
|
|
n0=1;
|
|
if(k==1)
|
|
n1=1;
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
n0=1;
|
|
n1=1;
|
|
}
|
|
else
|
|
{
|
|
if(!CAp::Assert(nkind==0))
|
|
return(true);
|
|
}
|
|
}
|
|
x0=vector<double>::Zeros(n0);
|
|
x0.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n0 ; i++)
|
|
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
|
|
x1=vector<double>::Zeros(n1);
|
|
x1.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n1; i++)
|
|
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
|
|
ArrayResize(needy,n0*n1);
|
|
v=MathPow(10,-(3*CMath::RandomReal()));
|
|
for(i=0; i<n0*n1 ; i++)
|
|
needy[i]=CMath::RandomReal()<v;
|
|
//--- Test at grid
|
|
x=vector<double>::Zeros(nx);
|
|
CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv);
|
|
for(i0=0; i0<n0 ; i0++)
|
|
{
|
|
for(i1=0; i1<n1; i1++)
|
|
{
|
|
x.Set(0,x0[i0]);
|
|
x.Set(1,x1[i1]);
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(i=0; i<ny; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[i]-yv[i+ny*(i0+i1*n0)])>errtol,"testrbfunit.ap:5287");
|
|
}
|
|
}
|
|
//--- Test calculation on subset of regular grid:
|
|
//--- * Test 1: compare full and subset versions
|
|
//--- * Test 2: check sparsity. Subset function may perform additional
|
|
//--- evaluations because it processes data micro-row by micro-row.
|
|
//--- So, we can't check that all elements which were not flagged
|
|
//--- are zero - some of them may become non-zero. However, if entire
|
|
//--- row is empty, we can reasonably expect (informal guarantee)
|
|
//--- that it is not processed. So, we check empty (completely
|
|
//--- unflagged) rows
|
|
CRBF::RBFGridCalc2VSubset(s,x0,n0,x1,n1,needy,yv2);
|
|
for(i=0; i<ny*n0*n1; i++)
|
|
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:5306");
|
|
//--- Test legacy function
|
|
CRBF::RBFGridCalc2(s,x0,n0,x1,n1,y2);
|
|
for(i=0; i<n0*n1 ; i++)
|
|
{
|
|
if(ny==1)
|
|
CAp::SetErrorFlag(result,MathAbs(yv[i]-y2.Get(i%n0,i/n0))>errtol,"testrbfunit.ap:5316");
|
|
else
|
|
CAp::SetErrorFlag(result,y2.Get(i%n0,i/n0)!=0.0,"testrbfunit.ap:5318");
|
|
}
|
|
//--- Test that scaling RBase, XY, X0, X1 by some power of 2
|
|
//--- does not change values at grid (quite a strict requirement, but
|
|
//--- ALGLIB implementation of RBF may deal with it).
|
|
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
|
|
xy2=matrix<double>::Zeros(n,nx+ny);
|
|
x02=vector<double>::Zeros(n0);
|
|
x12=vector<double>::Zeros(n1);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy2.Set(i,j,xy.Get(i,j)*scalefactor);
|
|
for(j=0; j<ny; j++)
|
|
xy2.Set(i,nx+j,xy.Get(i,nx+j));
|
|
}
|
|
for(i=0; i<n0 ; i++)
|
|
x02.Set(i,x0[i]*scalefactor);
|
|
for(i=0; i<n1; i++)
|
|
x12.Set(i,x1[i]*scalefactor);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy2,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy2,n);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5350");
|
|
return(result);
|
|
}
|
|
CRBF::RBFGridCalc2V(s,x02,n0,x12,n1,yv2);
|
|
for(i=0; i<ny*n0*n1; i++)
|
|
CAp::SetErrorFlag(result,yv[i]!=yv2[i],"testrbfunit.ap:5356");
|
|
//--- Test that scaling RBase and scale vector by some power of 2
|
|
//--- (increase RBase and decreasing scale, or vice versa) does not
|
|
//--- change values at grid (quite a strict requirement, but
|
|
//--- ALGLIB implementation of RBF may deal with it).
|
|
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
|
|
scalevec2=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
{
|
|
if(hasscale)
|
|
scalevec2.Set(i,scalevec[i]);
|
|
else
|
|
scalevec2.Set(i,1.0);
|
|
scalevec2.Mul(i,1.0/scalefactor);
|
|
}
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec2);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5380");
|
|
return(result);
|
|
}
|
|
CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv2);
|
|
for(i=0; i<ny*n0*n1; i++)
|
|
CAp::SetErrorFlag(result,(double)(MathAbs(yv[i]-yv2[i]))>(100.0*CMath::m_machineepsilon*CApServ::RMaxAbs3(yv[i],yv2[i],1.0)),"testrbfunit.ap:5386");
|
|
}
|
|
}
|
|
//--- Test 3-dimensional grid calculation
|
|
errtol=1.0E-12;
|
|
nx=3;
|
|
for(ny=1; ny<=4; ny++)
|
|
{
|
|
for(nkind=0; nkind<=2; nkind++)
|
|
{
|
|
//--- problem setup
|
|
n=150;
|
|
rbase=0.10;
|
|
lambdav=1.0E-3*CMath::RandomInteger(2);
|
|
nlayers=CMath::RandomInteger(3);
|
|
linterm=1+CMath::RandomInteger(3);
|
|
bf=CMath::RandomInteger(2);
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
for(j=0; j<ny; j++)
|
|
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
|
|
}
|
|
hasscale=CMath::RandomInteger(2)==0;
|
|
if(hasscale)
|
|
{
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(j=0; j<nx ; j++)
|
|
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Build model
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,lambdav);
|
|
if(linterm==1)
|
|
CRBF::RBFSetLinTerm(s);
|
|
if(linterm==2)
|
|
CRBF::RBFSetConstTerm(s);
|
|
if(linterm==3)
|
|
CRBF::RBFSetZeroTerm(s);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5441");
|
|
return(result);
|
|
}
|
|
//--- Prepare grid to test
|
|
n0=1+CMath::RandomInteger(50);
|
|
n1=1+CMath::RandomInteger(50);
|
|
n2=1+CMath::RandomInteger(50);
|
|
if(nkind==1)
|
|
{
|
|
k=CMath::RandomInteger(3);
|
|
if(k==0)
|
|
n0=1;
|
|
if(k==1)
|
|
n1=1;
|
|
if(k==2)
|
|
n2=1;
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
n0=1;
|
|
n1=1;
|
|
n2=1;
|
|
}
|
|
else
|
|
{
|
|
if(!CAp::Assert(nkind==0))
|
|
return(true);
|
|
}
|
|
}
|
|
x0=vector<double>::Zeros(n0);
|
|
x0.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n0 ; i++)
|
|
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
|
|
x1=vector<double>::Zeros(n1);
|
|
x1.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n1; i++)
|
|
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
|
|
x2=vector<double>::Zeros(n2);
|
|
x2.Set(0,CMath::RandomReal());
|
|
for(i=1; i<n2 ; i++)
|
|
x2.Set(i,x2[i-1]+CMath::RandomReal()/n2);
|
|
ArrayResize(needy,n0*n1*n2);
|
|
v=MathPow(10,-(3*CMath::RandomReal()));
|
|
for(i=0; i<n0*n1*n2; i++)
|
|
needy[i]=CMath::RandomReal()<v;
|
|
//--- Test at grid
|
|
x=vector<double>::Zeros(nx);
|
|
CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv);
|
|
for(i0=0; i0<n0 ; i0++)
|
|
{
|
|
for(i1=0; i1<n1; i1++)
|
|
for(i2=0; i2<n2 ; i2++)
|
|
{
|
|
x.Set(0,x0[i0]);
|
|
x.Set(1,x1[i1]);
|
|
x.Set(2,x2[i2]);
|
|
CRBF::RBFCalc(s,x,y);
|
|
for(i=0; i<ny; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(y[i]-yv[i+ny*(i0+i1*n0+i2*n0*n1)])>errtol,"testrbfunit.ap:5500");
|
|
}
|
|
}
|
|
//--- Test calculation on subset of regular grid:
|
|
//--- * Test 1: compare full and subset versions
|
|
//--- * Test 2: check sparsity. Subset function may perform additional
|
|
//--- evaluations because it processes data micro-row by micro-row.
|
|
//--- So, we can't check that all elements which were not flagged
|
|
//--- are zero - some of them may become non-zero. However, if entire
|
|
//--- row is empty, we can reasonably expect (informal guarantee)
|
|
//--- that it is not processed. So, we check empty (completely
|
|
//--- unflagged) rows
|
|
ArrayResize(rowflags,n1*n2);
|
|
ArrayInitialize(rowflags,false);
|
|
for(i=0; i<=n0*n1*n2-1; i++)
|
|
{
|
|
if(needy[i])
|
|
rowflags[i/n0]=true;
|
|
}
|
|
CRBF::RBFGridCalc3VSubset(s,x0,n0,x1,n1,x2,n2,needy,yv2);
|
|
for(i=0; i<ny*n0*n1*n2; i++)
|
|
{
|
|
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:5527");
|
|
CAp::SetErrorFlag(result,!rowflags[i/(ny*n0)] && yv2[i]!=0.0,"testrbfunit.ap:5528");
|
|
}
|
|
//--- Test that scaling RBase, XY, X0, X1 and X2 by some power of 2
|
|
//--- does not change values at grid (quite a strict requirement, but
|
|
//--- ALGLIB implementation of RBF may deal with it).
|
|
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
|
|
xy2=matrix<double>::Zeros(n,nx+ny);
|
|
x02=vector<double>::Zeros(n0);
|
|
x12=vector<double>::Zeros(n1);
|
|
x22=vector<double>::Zeros(n2);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
xy2.Set(i,j,xy.Get(i,j)*scalefactor);
|
|
for(j=0; j<ny; j++)
|
|
xy2.Set(i,nx+j,xy.Get(i,nx+j));
|
|
}
|
|
for(i=0; i<n0 ; i++)
|
|
x02.Set(i,x0[i]*scalefactor);
|
|
for(i=0; i<n1; i++)
|
|
x12.Set(i,x1[i]*scalefactor);
|
|
for(i=0; i<n2 ; i++)
|
|
x22.Set(i,x2[i]*scalefactor);
|
|
if(hasscale)
|
|
CRBF::RBFSetPointsAndScales(s,xy2,n,scalevec);
|
|
else
|
|
CRBF::RBFSetPoints(s,xy2,n);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5563");
|
|
return(result);
|
|
}
|
|
CRBF::RBFGridCalc3V(s,x02,n0,x12,n1,x22,n2,yv2);
|
|
for(i=0; i<ny*n0*n1*n2; i++)
|
|
CAp::SetErrorFlag(result,yv[i]!=yv2[i],"testrbfunit.ap:5569");
|
|
//--- Test that scaling RBase and scale vector by some power of 2
|
|
//--- (increase RBase and decreasing scale, or vice versa) does not
|
|
//--- change values at grid (quite a strict requirement, but
|
|
//--- ALGLIB implementation of RBF may deal with it).
|
|
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
|
|
scalevec2=vector<double>::Ones(nx);
|
|
if(hasscale)
|
|
scalevec2=scalevec;
|
|
scalevec2/=scalefactor;
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec2);
|
|
CRBF::RBFSetV2BF(s,bf);
|
|
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5593");
|
|
return(result);
|
|
}
|
|
CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv2);
|
|
for(i=0; i<ny*n0*n1*n2; i++)
|
|
CAp::SetErrorFlag(result,MathAbs(yv[i]-yv2[i])>(100.0*CMath::m_machineepsilon*CApServ::RMaxAbs3(yv[i],yv2[i],1.0)),"testrbfunit.ap:5599");
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Function for testing gradient evaluation |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::GradTest(bool &err)
|
|
{
|
|
//--- create variables
|
|
int algoidx=0;
|
|
int nkind=0;
|
|
int n=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
bool hasscale;
|
|
double errtol=0;
|
|
double errtol2=0;
|
|
double stp=0;
|
|
double stp2=0;
|
|
CMatrixDouble xy;
|
|
CRowDouble scalevec;
|
|
CRowDouble x;
|
|
CRowDouble x1;
|
|
CRowDouble yref;
|
|
CRowDouble y;
|
|
CRowDouble y1;
|
|
CRowDouble dy;
|
|
CRowDouble d2y;
|
|
CRowDouble dyref;
|
|
CRowDouble d2yref;
|
|
CRowDouble dy0;
|
|
CRowDouble dy1;
|
|
CRowDouble dy2;
|
|
CRowDouble dy3;
|
|
CRowDouble dy4;
|
|
CRBFModel s;
|
|
CRBFReport rep;
|
|
CRBFCalcBuffer tsbuf;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int trialidx=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v3=0;
|
|
double v4=0;
|
|
double df=0;
|
|
double d2f=0;
|
|
double vy=0;
|
|
double vdy0=0;
|
|
double vdy1=0;
|
|
double vdy2=0;
|
|
CHighQualityRandState rs;
|
|
bool islonger;
|
|
bool hasgradientatnodes;
|
|
bool hashessianatnodes;
|
|
bool trialatnode;
|
|
double meanseparation=0;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Generate good random grid, build model, check derivatives at random points and at grid nodes
|
|
stp=0.000001;
|
|
stp2=0.000001;
|
|
errtol=0.005;
|
|
errtol2=0.005;
|
|
for(algoidx=0; algoidx<m_algorithmscnt; algoidx++)
|
|
{
|
|
for(nx=1; nx<=4; nx++)
|
|
{
|
|
for(nkind=0; nkind<=1; nkind++)
|
|
{
|
|
//--- problem setup
|
|
if(!CAp::Assert(nkind==0 || nkind==1,"rbf: nkind"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
ny=1+CHighQualityRand::HQRndUniformI(rs,2);
|
|
n=100*nkind;
|
|
hasscale=CHighQualityRand::HQRndNormal(rs)>0.0;
|
|
CRBF::RBFCreate(nx,ny,s);
|
|
if(n!=0)
|
|
{
|
|
GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation);
|
|
if(hasscale)
|
|
{
|
|
scalevec=vector<double>::Zeros(nx);
|
|
for(i=0; i<nx ; i++)
|
|
scalevec.Set(i,MathPow(2,0.5*CHighQualityRand::HQRndNormal(rs)));
|
|
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
|
|
}
|
|
else
|
|
CRBF::RBFSetPoints(s,xy,n);
|
|
}
|
|
else
|
|
meanseparation=1.0;
|
|
SetAlgorithm(s,n,nx,ny,hasscale,algoidx,meanseparation,hasgradientatnodes,hashessianatnodes);
|
|
CRBF::RBFBuildModel(s,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
CAp::SetErrorFlag(err,true,"testrbfunit.ap:5676");
|
|
return;
|
|
}
|
|
CRBF::RBFCreateCalcBuffer(s,tsbuf);
|
|
//--- Check first derivatives - perform N randomized trials
|
|
for(trialidx=0; trialidx<=n; trialidx++)
|
|
{
|
|
//--- Check RBFDiff vs numerical derivatives. RBFCalc() is used to provide reference values.
|
|
//--- After this block we assume that RBFDiff is correct reference implementation.
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
trialatnode=true;
|
|
}
|
|
else
|
|
{
|
|
GenerateSeparatedWithInbox(xy,n,nx,MathMax(100*stp,MathMax(100*stp2,(n>0?0.01/n:0))),rs,x);
|
|
trialatnode=false;
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
yref=vector<double>::Zeros(ny+1);
|
|
y=vector<double>::Zeros(ny+1);
|
|
dy=vector<double>::Zeros(ny*nx+1);
|
|
}
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFCalc(s,x1,yref);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,y,dy);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5716");
|
|
CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testrbfunit.ap:5717");
|
|
CAp::SetErrorFlag(err,CAp::Len(dy)!=ny*nx,"testrbfunit.ap:5718");
|
|
if(hasgradientatnodes || !trialatnode)
|
|
{
|
|
//--- Gradient is well defined, check
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(y[j]-yref[j])>errtol,"testrbfunit.ap:5726");
|
|
for(k=0; k<nx ; k++)
|
|
{
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,- 2*stp);
|
|
CRBF::RBFCalc(s,x1,y1);
|
|
v0=y1[j];
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,-stp);
|
|
CRBF::RBFCalc(s,x1,y1);
|
|
v1=y1[j];
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFCalc(s,x1,y1);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,stp);
|
|
CRBF::RBFCalc(s,x1,y1);
|
|
v3=y1[j];
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,2*stp);
|
|
CRBF::RBFCalc(s,x1,y1);
|
|
v4=y1[j];
|
|
df=(-v4+8*v3-8*v1+v0)/(12*stp);
|
|
CAp::SetErrorFlag(err,(MathAbs(dy[j*nx+k]-df)/(1+MathAbs(df)))>errtol,"testrbfunit.ap:5750");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Gradient is undefined at the trial point, zero must be returned
|
|
CAp::SetErrorFlag(err,CAblasF::RMaxAbsV(ny*nx,dy)!=0.0,"testrbfunit.ap:5760");
|
|
}
|
|
//--- Check RBFDiffBuf vs RBFDiff.
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
//---
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
yref=vector<double>::Zeros(ny+1);
|
|
y=vector<double>::Zeros(ny+1);
|
|
dy=vector<double>::Zeros(ny*nx+1);
|
|
dyref=vector<double>::Zeros(ny*nx+1);
|
|
islonger=true;
|
|
}
|
|
else
|
|
{
|
|
yref.Resize(0);
|
|
y.Resize(0);
|
|
dy.Resize(0);
|
|
dyref.Resize(0);
|
|
islonger=false;
|
|
}
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,yref,dyref);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiffBuf(s,x1,y,dy);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5788");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5789");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:5790");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:5791");
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(y[j]-yref[j])>errtol,"testrbfunit.ap:5794");
|
|
for(k=0; k<nx ; k++)
|
|
CAp::SetErrorFlag(err,MathAbs(dy[j*nx+k]-dyref[j*nx+k])>errtol,"testrbfunit.ap:5796");
|
|
}
|
|
//--- Check RBFTsDiffBuf vs RBFDiff.
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
//---
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
yref=vector<double>::Zeros(ny+1);
|
|
y=vector<double>::Zeros(ny+1);
|
|
dy=vector<double>::Zeros(ny*nx+1);
|
|
dyref=vector<double>::Zeros(ny*nx+1);
|
|
islonger=true;
|
|
}
|
|
else
|
|
{
|
|
yref.Resize(0);
|
|
y.Resize(0);
|
|
dy.Resize(0);
|
|
dyref.Resize(0);
|
|
islonger=false;
|
|
}
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,yref,dyref);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFTSDiffBuf(s,tsbuf,x1,y,dy);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5824");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5825");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:5826");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:5827");
|
|
for(j=0; j<ny; j++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(y[j]-yref[j])>errtol,"testrbfunit.ap:5830");
|
|
for(k=0; k<nx ; k++)
|
|
CAp::SetErrorFlag(err,MathAbs(dy[j*nx+k]-dyref[j*nx+k])>errtol,"testrbfunit.ap:5832");
|
|
}
|
|
//--- Check RBFDiff1/RBFDiff2/RBFDiff3 vs RBFDiff.
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
if(nx==1 && ny==1)
|
|
{
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,yref,dyref);
|
|
CRBF::RBFDiff1(s,x[0],vy,vdy0);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5850");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5851");
|
|
CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5852");
|
|
CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5853");
|
|
}
|
|
else
|
|
{
|
|
CRBF::RBFDiff1(s,1.2,vy,vdy0);
|
|
CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5858");
|
|
CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5859");
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
if(nx==2 && ny==1)
|
|
{
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,yref,dyref);
|
|
CRBF::RBFDiff2(s,x[0],x[1],vy,vdy0,vdy1);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5871");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5872");
|
|
CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5873");
|
|
CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5874");
|
|
CAp::SetErrorFlag(err,MathAbs(vdy1-dyref[1])>errtol,"testrbfunit.ap:5875");
|
|
}
|
|
else
|
|
{
|
|
CRBF::RBFDiff2(s,1.2,3.4,vy,vdy0,vdy1);
|
|
CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5880");
|
|
CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5881");
|
|
CAp::SetErrorFlag(err,vdy1!=0.0,"testrbfunit.ap:5882");
|
|
}
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
if(nx==3 && ny==1)
|
|
{
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,yref,dyref);
|
|
CRBF::RBFDiff3(s,x[0],x[1],x[2],vy,vdy0,vdy1,vdy2);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5894");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5895");
|
|
CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5896");
|
|
CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5897");
|
|
CAp::SetErrorFlag(err,MathAbs(vdy1-dyref[1])>errtol,"testrbfunit.ap:5898");
|
|
CAp::SetErrorFlag(err,MathAbs(vdy2-dyref[2])>errtol,"testrbfunit.ap:5899");
|
|
}
|
|
else
|
|
{
|
|
CRBF::RBFDiff3(s,1.2,3.4,5.6,vy,vdy0,vdy1,vdy2);
|
|
CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5904");
|
|
CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5905");
|
|
CAp::SetErrorFlag(err,vdy1!=0.0,"testrbfunit.ap:5906");
|
|
CAp::SetErrorFlag(err,vdy2!=0.0,"testrbfunit.ap:5907");
|
|
}
|
|
}
|
|
//--- Check second derivatives - perform N randomized trials
|
|
for(trialidx=0; trialidx<=n; trialidx++)
|
|
{
|
|
//--- Check RBFHess vs numerical derivatives.
|
|
//--- RBFDiff() is assumed to be already tested and is used to provide exact first derivatives.
|
|
//--- In this test we have to handle situation when the Hessian is undefined at the trial node.
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
trialatnode=true;
|
|
}
|
|
else
|
|
{
|
|
if(n<1)
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
else
|
|
GenerateSeparatedWithInbox(xy,n,nx,MathMax(100*stp,MathMax(100*stp2,0.01/(double)n)),rs,x);
|
|
trialatnode=false;
|
|
}
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
dyref=vector<double>::Zeros(ny+1);
|
|
yref=vector<double>::Zeros(ny+1);
|
|
y=vector<double>::Zeros(ny+1);
|
|
dy=vector<double>::Zeros(ny*nx+1);
|
|
d2y=vector<double>::Zeros(ny*nx*nx+1);
|
|
}
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,yref,dyref);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFHess(s,x1,y,dy,d2y);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5950");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5951");
|
|
CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testrbfunit.ap:5952");
|
|
CAp::SetErrorFlag(err,CAp::Len(dy)!=ny*nx,"testrbfunit.ap:5953");
|
|
CAp::SetErrorFlag(err,CAp::Len(d2y)!=ny*nx*nx,"testrbfunit.ap:5954");
|
|
for(k=0; k<nx ; k++)
|
|
CAp::SetErrorFlag(err,(MathAbs(dy[k]-dyref[k])/(1+MathAbs(dyref[k])))>errtol,"testrbfunit.ap:5956");
|
|
if(hashessianatnodes || !trialatnode)
|
|
{
|
|
//--- The Hessian is well defined at the trial point.
|
|
for(k=0; k<nx ; k++)
|
|
{
|
|
//--- Test K-th column of the Hessian, using RBFDiff() as a reference function for the first derivatives
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,-2*stp2);
|
|
CRBF::RBFDiff(s,x1,y1,dy0);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,-stp2);
|
|
CRBF::RBFDiff(s,x1,y1,dy1);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFDiff(s,x1,y1,dy2);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,stp2);
|
|
CRBF::RBFDiff(s,x1,y1,dy3);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
x1.Add(k,2*stp2);
|
|
CRBF::RBFDiff(s,x1,y1,dy4);
|
|
for(i=0; i<ny; i++)
|
|
for(j=0; j<nx ; j++)
|
|
{
|
|
double check1=8*dy3[i*nx+j]-8*dy1[i*nx+j];
|
|
double check2=dy0[i*nx+j]-dy4[i*nx+j];
|
|
d2f=(-dy4[i*nx+j]+8*dy3[i*nx+j]-8*dy1[i*nx+j]+dy0[i*nx+j])/(12*stp2);
|
|
CAp::SetErrorFlag(err,(MathAbs(d2y[i*nx*nx+j*nx+k]-d2f)/(1+MathAbs(d2f)))>errtol2,"testrbfunit.ap:5986");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- The Hessian is undefined at the trial point, exact zero must be returned
|
|
CAp::SetErrorFlag(err,CAblasF::RMaxAbsV(ny*nx*nx,d2y)!=0.0,"testrbfunit.ap:5996");
|
|
}
|
|
//--- Check RBFHessBuf vs RBFHess
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
if(n<1)
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
else
|
|
GenerateSeparatedWithInbox(xy,n,nx,1.0/(double)n,rs,x);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
y=vector<double>::Zeros(ny+1);
|
|
yref=vector<double>::Zeros(ny+1);
|
|
dy=vector<double>::Zeros(ny*nx+1);
|
|
dyref=vector<double>::Zeros(ny*nx+1);
|
|
d2y=vector<double>::Zeros(ny*nx*nx+1);
|
|
d2yref=vector<double>::Zeros(ny*nx*nx+1);
|
|
islonger=true;
|
|
}
|
|
else
|
|
{
|
|
yref.Resize(0);
|
|
y.Resize(0);
|
|
dy.Resize(0);
|
|
dyref.Resize(0);
|
|
d2y.Resize(0);
|
|
d2yref.Resize(0);
|
|
islonger=false;
|
|
}
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFHess(s,x1,yref,dyref,d2yref);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFHessBuf(s,x1,y,dy,d2y);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:6029");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:6030");
|
|
CAp::SetErrorFlag(err,CAp::Len(d2yref)!=ny*nx*nx,"testrbfunit.ap:6031");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:6032");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:6033");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(d2y)<=ny*nx*nx) || (!islonger && CAp::Len(d2y)!=ny*nx*nx),"testrbfunit.ap:6034");
|
|
for(k=0; k<ny; k++)
|
|
CAp::SetErrorFlag(err,y[k]!=yref[k],"testrbfunit.ap:6036");
|
|
for(k=0; k<ny*nx; k++)
|
|
CAp::SetErrorFlag(err,dy[k]!=dyref[k],"testrbfunit.ap:6038");
|
|
for(k=0; k<ny*nx*nx; k++)
|
|
CAp::SetErrorFlag(err,d2y[k]!=d2yref[k],"testrbfunit.ap:6040");
|
|
//--- Check RBFTsHessBuf vs RBFHess
|
|
x=vector<double>::Zeros(nx);
|
|
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
|
|
else
|
|
if(n<1)
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
else
|
|
GenerateSeparatedWithInbox(xy,n,nx,1.0/(double)n,rs,x);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
y=vector<double>::Zeros(ny+1);
|
|
yref=vector<double>::Zeros(ny+1);
|
|
dy=vector<double>::Zeros(ny*nx+1);
|
|
dyref=vector<double>::Zeros(ny*nx+1);
|
|
d2y=vector<double>::Zeros(ny*nx*nx+1);
|
|
d2yref=vector<double>::Zeros(ny*nx*nx+1);
|
|
islonger=true;
|
|
}
|
|
else
|
|
{
|
|
islonger=false;
|
|
y.Resize(0);
|
|
yref.Resize(0);
|
|
dy.Resize(0);
|
|
dyref.Resize(0);
|
|
d2y.Resize(0);
|
|
d2yref.Resize(0);
|
|
}
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFHess(s,x1,yref,dyref,d2yref);
|
|
CAblasF::RCopyAllocV(nx,x,x1);
|
|
CRBF::RBFTSHessBuf(s,tsbuf,x1,y,dy,d2y);
|
|
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:6071");
|
|
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:6072");
|
|
CAp::SetErrorFlag(err,CAp::Len(d2yref)!=ny*nx*nx,"testrbfunit.ap:6073");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:6074");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:6075");
|
|
CAp::SetErrorFlag(err,(islonger && CAp::Len(d2y)<=ny*nx*nx) || (!islonger && CAp::Len(d2y)!=ny*nx*nx),"testrbfunit.ap:6076");
|
|
for(k=0; k<ny; k++)
|
|
CAp::SetErrorFlag(err,y[k]!=yref[k],"testrbfunit.ap:6078");
|
|
for(k=0; k<=ny*nx-1; k++)
|
|
CAp::SetErrorFlag(err,dy[k]!=dyref[k],"testrbfunit.ap:6080");
|
|
for(k=0; k<=ny*nx*nx-1; k++)
|
|
CAp::SetErrorFlag(err,d2y[k]!=d2yref[k],"testrbfunit.ap:6082");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates nearly regular grid |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::GenerateNearlyRegularGrid(int n,int nx,int ny,
|
|
CHighQualityRandState &rs,
|
|
CMatrixDouble &xy,
|
|
double &meanseparation)
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
int griddim=0;
|
|
|
|
meanseparation=0;
|
|
if(!CAp::Assert(n>=MathPow(2,nx),"GenerateNearlyRegularGrid: N<2^NX"))
|
|
return;
|
|
|
|
meanseparation=1.0;
|
|
griddim=(int)MathRound(MathPow(n,1.0/(double)nx))+1;
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
k=i;
|
|
for(int j=0; j<nx ; j++)
|
|
{
|
|
xy.Set(i,j,k%griddim+0.05*CHighQualityRand::HQRndNormal(rs));
|
|
k/=griddim;
|
|
}
|
|
for(k=nx; k<nx+ny; k++)
|
|
xy.Set(i,k,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates set of normally distributed points(mean = 0, sigma = 1)|
|
|
//| with good separation between nodes. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::GenerateGoodRandomGrid(int n,int nx,int ny,
|
|
CHighQualityRandState &rs,
|
|
CMatrixDouble &xy,
|
|
double &meanseparation)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double mindist=0;
|
|
double d=0;
|
|
double v=0;
|
|
|
|
meanseparation=0;
|
|
if(!CAp::Assert(n>=0,"GenerateGoodRandomGrid: N<1"))
|
|
return;
|
|
|
|
if(n==0)
|
|
{
|
|
meanseparation=1;
|
|
return;
|
|
}
|
|
xy=matrix<double>::Zeros(n,nx+ny);
|
|
d=1.0/MathPow(n,1.0/(double)nx);
|
|
|
|
for(i=0; i<n; i++)
|
|
{
|
|
do
|
|
{
|
|
for(k=0; k<nx+ny; k++)
|
|
xy.Set(i,k,CHighQualityRand::HQRndNormal(rs));
|
|
mindist=1.0E50;
|
|
for(j=0; j<=i-1; j++)
|
|
{
|
|
v=0;
|
|
for(k=0; k<nx ; k++)
|
|
v+=CMath::Sqr(xy.Get(i,k)-xy.Get(j,k));
|
|
mindist=MathMin(mindist,MathSqrt(v));
|
|
}
|
|
}
|
|
while(mindist<=d);
|
|
}
|
|
if(n>1)
|
|
{
|
|
meanseparation=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
mindist=1.0E50;
|
|
for(j=0; j<n; j++)
|
|
{
|
|
if(j!=i)
|
|
{
|
|
v=0;
|
|
for(k=0; k<nx ; k++)
|
|
v+=CMath::Sqr(xy.Get(i,k)-xy.Get(j,k));
|
|
mindist=MathMin(mindist,MathSqrt(v));
|
|
}
|
|
}
|
|
meanseparation+=mindist/n;
|
|
}
|
|
meanseparation=CApServ::Coalesce(meanseparation,1);
|
|
}
|
|
else
|
|
meanseparation=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates random point within box surrounding dataset, with good |
|
|
//| separation from dataset points. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::GenerateSeparatedWithInbox(CMatrixDouble &xy,int n,
|
|
int nx,double mindist,
|
|
CHighQualityRandState &rs,
|
|
CRowDouble &x)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
CRowDouble minx;
|
|
CRowDouble maxx;
|
|
double d=0;
|
|
double mind=0;
|
|
|
|
if(n<1)
|
|
{
|
|
CHighQualityRand::HQRndNormalV(rs,nx,x);
|
|
return;
|
|
}
|
|
|
|
CAblasF::RAllocV(nx,minx);
|
|
CAblasF::RAllocV(nx,maxx);
|
|
CAblasF::RAllocV(nx,x);
|
|
CAblasF::RCopyRV(nx,xy,0,minx);
|
|
CAblasF::RCopyRV(nx,xy,0,maxx);
|
|
|
|
for(i=1; i<n; i++)
|
|
{
|
|
CAblasF::RMergeMinRV(nx,xy,i,minx);
|
|
CAblasF::RMergeMaxRV(nx,xy,i,maxx);
|
|
}
|
|
for(i=0; i<nx ; i++)
|
|
{
|
|
minx.Add(i,-2*mindist);
|
|
maxx.Add(i,2*mindist);
|
|
}
|
|
|
|
do
|
|
{
|
|
for(j=0; j<nx ; j++)
|
|
x.Set(j,minx[j]+(maxx[j]-minx[j])*CHighQualityRand::HQRndUniformR(rs));
|
|
mind=1.0E50+mindist;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
d=0;
|
|
for(j=0; j<nx ; j++)
|
|
d+=CMath::Sqr(xy.Get(i,j)-x[j]);
|
|
d=MathSqrt(d);
|
|
if(d<mind)
|
|
mind=d;
|
|
}
|
|
}
|
|
while(mind<mindist);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sets algorithm according to its index. If problem has unsupported|
|
|
//| dimensions or other properties(e.m_g. variable scales vector is |
|
|
//| present), this function assigns some other algorithm. |
|
|
//| This function outputs information about properties of the |
|
|
//| algorithm that was actually chosen: |
|
|
//| * whether second derivatives are defined at the nodes |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::SetAlgorithm(CRBFModel &s,int n,int nx,int ny,
|
|
bool hasscale,int algoidx,
|
|
double meanpointsseparation,
|
|
bool &hasgradientatnodes,
|
|
bool &hashessianatnodes)
|
|
{
|
|
hasgradientatnodes=true;
|
|
hashessianatnodes=true;
|
|
|
|
if(algoidx==0)
|
|
{
|
|
if((nx>=2 && nx<=3) && !hasscale)
|
|
CRBF::RBFSetAlgoQNN(s,meanpointsseparation,5.0);
|
|
else
|
|
CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0);
|
|
return;
|
|
}
|
|
if(algoidx==1)
|
|
{
|
|
if((nx>=2 && nx<=3) && !hasscale)
|
|
CRBF::RBFSetAlgoMultilayer(s,16*meanpointsseparation,8,0.0);
|
|
else
|
|
CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0);
|
|
return;
|
|
}
|
|
if(algoidx==2)
|
|
{
|
|
CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0);
|
|
return;
|
|
}
|
|
if(algoidx==3)
|
|
{
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
|
|
hashessianatnodes=false;
|
|
return;
|
|
}
|
|
if(algoidx==4)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
|
|
else
|
|
CRBF::RBFSetAlgoMultiQuadricAuto(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
|
|
return;
|
|
}
|
|
if(algoidx==5)
|
|
{
|
|
CRBF::RBFSetAlgoBiharmonic(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
|
|
hasgradientatnodes=false;
|
|
hashessianatnodes=false;
|
|
return;
|
|
}
|
|
CAp::Assert(false,"SetAlgorithm: unknown AlgoIdx");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sets one of XRBF algorithms (version 3 RBFs introduced in 2022) |
|
|
//| according to its index. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::SetXRBFAlgorithmExact(CRBFModel &s,int algoidx,
|
|
double meanpointsseparation)
|
|
{
|
|
if(algoidx==0)
|
|
{
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
|
|
return;
|
|
}
|
|
if(algoidx==1)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),0.0);
|
|
else
|
|
CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0);
|
|
return;
|
|
}
|
|
if(algoidx==2)
|
|
{
|
|
CRBF::RBFSetAlgoBiharmonic(s,0.0);
|
|
return;
|
|
}
|
|
CAp::Assert(false,"SetXRBFAlgorithm: unknown AlgoIdx");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sets one of smoothing XRBF algorithms(version 3 RBFs introduced |
|
|
//| in 2022) according to its index. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestRBFUnit::SetXRBFAlgorithmSmoothing(CRBFModel &s,int algoidx,
|
|
double lambdav,
|
|
double meanpointsseparation)
|
|
{
|
|
if(algoidx==0)
|
|
{
|
|
CRBF::RBFSetAlgoThinPlateSpline(s,lambdav);
|
|
return;
|
|
}
|
|
if(algoidx==1)
|
|
{
|
|
if(CMath::RandomReal()>0.5)
|
|
CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),lambdav);
|
|
else
|
|
CRBF::RBFSetAlgoMultiQuadricAuto(s,lambdav);
|
|
return;
|
|
}
|
|
if(algoidx==2)
|
|
{
|
|
CRBF::RBFSetAlgoBiharmonic(s,lambdav);
|
|
return;
|
|
}
|
|
CAp::Assert(false,"SetXRBFAlgorithmSmoothing: unknown AlgoIdx");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMLPBaseUnit
|
|
{
|
|
public:
|
|
static bool TestMLPBase(bool silent);
|
|
|
|
private:
|
|
static double VectorDiff(CRowDouble &g0,CRowDouble &g1,int n,double s);
|
|
static void CreateNetwork(CMultilayerPerceptron &network,int nkind,double a1,double a2,int nin,int nhid1,int nhid2,int nout);
|
|
static void UnsetNetwork(CMultilayerPerceptron &network);
|
|
static void TestInformational(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err);
|
|
static void TestProcessing(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err);
|
|
static void TestGradient(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,int sizemin,int sizemax,bool &err);
|
|
static void TestHessian(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err);
|
|
static void TestErr(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,int sizemin,int sizemax,bool &err);
|
|
static void SpecTests(bool &inferrors,bool &procerrors,bool &graderrors,bool &hesserrors,bool &errerrors);
|
|
static bool TestMLPGBSubset(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPBaseUnit::TestMLPBase(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors=false;
|
|
bool inferrors=false;
|
|
bool procerrors=false;
|
|
bool graderrors=false;
|
|
bool hesserrors=false;
|
|
bool errerrors=false;
|
|
int passcount=5;
|
|
int maxn=3;
|
|
int maxhid=3;
|
|
bool result;
|
|
int sizemin=0;
|
|
int sizemax=0;
|
|
int nf=0;
|
|
int nl=0;
|
|
int nhid1=0;
|
|
int nhid2=0;
|
|
int nkind=0;
|
|
CMultilayerPerceptron network;
|
|
CMultilayerPerceptron network2;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble valxy;
|
|
|
|
//--- Special tests
|
|
SpecTests(inferrors,procerrors,graderrors,hesserrors,errerrors);
|
|
//--- General multilayer network tests.
|
|
//--- These tests are performed with small dataset, whose size is in [0,10].
|
|
//--- We test correctness of functions on small sets, but do not test code
|
|
//--- which splits large dataset into smaller chunks.
|
|
sizemin=0;
|
|
sizemax=10;
|
|
for(nf=1; nf<=maxn; nf++)
|
|
{
|
|
for(nl=1; nl<=maxn; nl++)
|
|
{
|
|
for(nhid1=0; nhid1<=maxhid; nhid1++)
|
|
{
|
|
for(nhid2=0; nhid2<=maxhid; nhid2++)
|
|
{
|
|
for(nkind=0; nkind<=3; nkind++)
|
|
{
|
|
//--- Skip meaningless parameters combinations
|
|
if(nkind==1 && nl<2)
|
|
continue;
|
|
if(nhid1==0 && nhid2!=0)
|
|
continue;
|
|
//--- Tests
|
|
TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors);
|
|
TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors);
|
|
TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,sizemin,sizemax,graderrors);
|
|
TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors);
|
|
TestErr(nkind,nf,nhid1,nhid2,nl,passcount,sizemin,sizemax,errerrors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Special tests on large datasets: test ability to correctly split
|
|
//--- work into smaller chunks.
|
|
nf=2;
|
|
nhid1=20;
|
|
nhid2=20;
|
|
nl=2;
|
|
sizemin=50000;
|
|
sizemax=50000;
|
|
TestErr(0,nf,nhid1,nhid2,nl,1,sizemin,sizemax,errerrors);
|
|
TestGradient(0,nf,nhid1,nhid2,nl,1,sizemin,sizemax,graderrors);
|
|
//--- Test for MLPGradBatch____Subset()
|
|
graderrors=graderrors||TestMLPGBSubset();
|
|
//--- Final report
|
|
waserrors=(((inferrors||procerrors)||graderrors)||hesserrors)||errerrors;
|
|
if(!silent)
|
|
{
|
|
Print("MLP TEST");
|
|
PrintResult("INFORMATIONAL FUNCTIONS",!inferrors);
|
|
PrintResult("BASIC PROCESSING",!procerrors);
|
|
PrintResult("GRADIENT CALCULATION",!graderrors);
|
|
PrintResult("HESSIAN CALCULATION",!hesserrors);
|
|
PrintResult("ERROR FUNCTIONS",!errerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function compares vectors G0 and G1 and returns |
|
|
//| ||G0-G1||/max(||G0||,||G1||,S) |
|
|
//| For zero G0, G1 and S (all three quantities are zero) it returns |
|
|
//| zero. |
|
|
//+------------------------------------------------------------------+
|
|
double CTestMLPBaseUnit::VectorDiff(CRowDouble &g0,
|
|
CRowDouble &g1,
|
|
int n,double s)
|
|
{
|
|
//--- create variables
|
|
double norm0=0;
|
|
double norm1=0;
|
|
double diff=0;
|
|
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
norm0+=CMath::Sqr(g0[i]);
|
|
norm1+=CMath::Sqr(g1[i]);
|
|
diff+=CMath::Sqr(g0[i]-g1[i]);
|
|
}
|
|
norm0=MathSqrt(norm0);
|
|
norm1=MathSqrt(norm1);
|
|
diff=MathSqrt(diff);
|
|
if(norm0!=0.0 || norm1!=0.0 || s!=0.0)
|
|
diff/=MathMax(MathMax(norm0,norm1),s);
|
|
else
|
|
diff=0;
|
|
|
|
return(diff);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Network creation |
|
|
//| This function creates network with desired structure. |
|
|
//| Network is created using one of the three methods: |
|
|
//| a) straightforward creation using MLPCreate ?? ? () |
|
|
//| b) MLPCreate ?? ? () for proxy object, which is copied with |
|
|
//| PassThroughSerializer() |
|
|
//| c) MLPCreate ?? ? () for proxy object, which is copied with |
|
|
//| MLPCopy() |
|
|
//| One of these methods is chosen at random. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::CreateNetwork(CMultilayerPerceptron &network,
|
|
int nkind,double a1,double a2,
|
|
int nin,int nhid1,int nhid2,
|
|
int nout)
|
|
{
|
|
//--- create variables
|
|
int mkind=0;
|
|
CMultilayerPerceptron tmp;
|
|
//--- check
|
|
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
|
|
return;
|
|
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
|
|
return;
|
|
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
|
|
return;
|
|
|
|
mkind=CMath::RandomInteger(3);
|
|
if(nhid1==0)
|
|
{
|
|
//--- No hidden layers
|
|
if(nkind==0)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate0(nin,nout,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreate0(nin,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate0(nin,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==1)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC0(nin,nout,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateC0(nin,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC0(nin,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==3)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
return;
|
|
}
|
|
if(nhid2==0)
|
|
{
|
|
//--- One hidden layer
|
|
if(nkind==0)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==1)
|
|
{
|
|
if(mkind==0)
|
|
{
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,network);
|
|
}
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==3)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
return;
|
|
}
|
|
//--- Two hidden layers
|
|
if(nkind==0)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==1)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==2)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nkind==3)
|
|
{
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network);
|
|
if(mkind==1)
|
|
{
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets network (initialize it to smallest network possible |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::UnsetNetwork(CMultilayerPerceptron &network)
|
|
{
|
|
CMLPBase::MLPCreate0(1,1,network);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Informational functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::TestInformational(int nkind,int nin,int nhid1,
|
|
int nhid2,int nout,
|
|
int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron network;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double threshold=0;
|
|
int nlayers=0;
|
|
int nmax=0;
|
|
CMatrixDouble neurons;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
double mean=0;
|
|
double sigma=0;
|
|
int fkind=0;
|
|
double c=0;
|
|
double f=0;
|
|
double df=0;
|
|
double d2f=0;
|
|
double s=0;
|
|
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout);
|
|
//--- test MLPProperties()
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
err=(err||n1!=nin||n2!=nout||wcount<=0);
|
|
err=(err||CMLPBase::MLPGetInputsCount(network)!=nin||CMLPBase::MLPGetOutputsCount(network)!=nout ||
|
|
CMLPBase::MLPGetWeightsCount(network)!=wcount);
|
|
//--- Test network geometry functions
|
|
//--- In order to do this we calculate neural network output using
|
|
//--- informational functions only, and compare results with ones
|
|
//--- obtained with MLPProcess():
|
|
//--- 1. we allocate 2-dimensional array of neurons and fill it by zeros
|
|
//--- 2. we full first layer of neurons by input values
|
|
//--- 3. we move through array, calculating values of subsequent layers
|
|
//--- 4. if we have classification network, we SOFTMAX-normalize output layer
|
|
//--- 5. we apply scaling to the outputs
|
|
//--- 6. we compare results with ones obtained by MLPProcess()
|
|
//--- NOTE: it is important to do (4) before (5), because on SOFTMAX network
|
|
//--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
|
|
//--- to test it implicitly, we apply it to the classifier results
|
|
//--- (already normalized). If one of the coefficients deviates from
|
|
//--- expected values, we will get error during (6).
|
|
nlayers=2;
|
|
nmax=MathMax(nin,nout);
|
|
if(nhid1!=0)
|
|
{
|
|
nlayers=3;
|
|
nmax=MathMax(nmax,nhid1);
|
|
}
|
|
if(nhid2!=0)
|
|
{
|
|
nlayers=4;
|
|
nmax=MathMax(nmax,nhid2);
|
|
}
|
|
neurons=matrix<double>::Zeros(nlayers,nmax);
|
|
x=vector<double>::Zeros(nin);
|
|
for(i=0; i<nin ; i++)
|
|
x.Set(i,2*CMath::RandomReal()-1);
|
|
y=vector<double>::Zeros(nout);
|
|
for(i=0; i<nout; i++)
|
|
y.Set(i,2*CMath::RandomReal()-1);
|
|
for(j=0; j<nin ; j++)
|
|
{
|
|
CMLPBase::MLPGetInputScaling(network,j,mean,sigma);
|
|
neurons.Set(0,j,(x[j]-mean)/sigma);
|
|
}
|
|
for(i=1; i<=nlayers-1; i++)
|
|
{
|
|
for(j=0; j<=CMLPBase::MLPGetLayerSize(network,i)-1; j++)
|
|
{
|
|
for(k=0; k<=CMLPBase::MLPGetLayerSize(network,i-1)-1; k++)
|
|
neurons.Add(i,j,CMLPBase::MLPGetWeight(network,i-1,k,i,j)*neurons.Get(i-1,k));
|
|
CMLPBase::MLPGetNeuronInfo(network,i,j,fkind,c);
|
|
CMLPBase::MLPActivationFunction(neurons.Get(i,j)-c,fkind,f,df,d2f);
|
|
neurons.Set(i,j,f);
|
|
}
|
|
}
|
|
if(nkind==1)
|
|
{
|
|
s=0;
|
|
for(j=0; j<nout; j++)
|
|
s+=MathExp(neurons.Get(nlayers-1,j));
|
|
for(j=0; j<nout; j++)
|
|
neurons.Set(nlayers-1,j,MathExp(neurons.Get(nlayers-1,j))/s);
|
|
}
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
CMLPBase::MLPGetOutputScaling(network,j,mean,sigma);
|
|
neurons.Set(nlayers-1,j,neurons.Get(nlayers-1,j)*sigma+mean);
|
|
}
|
|
CMLPBase::MLPProcess(network,x,y);
|
|
for(j=0; j<nout; j++)
|
|
err=err||MathAbs(neurons.Get(nlayers-1,j)-y[j])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::TestProcessing(int nkind,int nin,int nhid1,
|
|
int nhid2,int nout,
|
|
int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron network;
|
|
CMultilayerPerceptron network2;
|
|
CSparseMatrix sparsexy;
|
|
CMatrixDouble densexy;
|
|
int npoints=0;
|
|
int subnp=0;
|
|
bool iscls;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
bool zeronet;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool allsame;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble y1;
|
|
CRowDouble y2;
|
|
CRowDouble p0;
|
|
CRowDouble p1;
|
|
int pcount=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- Prepare network
|
|
a1=0;
|
|
a2=0;
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
iscls=CMLPBase::MLPIsSoftMax(network);
|
|
//--- Initialize arrays
|
|
x1=vector<double>::Zeros(nin);
|
|
x2=vector<double>::Zeros(nin);
|
|
y1=vector<double>::Zeros(nout);
|
|
y2=vector<double>::Zeros(nout);
|
|
//--- Initialize sets
|
|
npoints=CMath::RandomInteger(11)+10;
|
|
if(iscls)
|
|
{
|
|
densexy=matrix<double>::Zeros(npoints,nin+1);
|
|
CSparse::SparseCreate(npoints,nin+1,npoints,sparsexy);
|
|
}
|
|
else
|
|
{
|
|
densexy=matrix<double>::Zeros(npoints,nin+nout);
|
|
CSparse::SparseCreate(npoints,nin+nout,npoints,sparsexy);
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
//--- Main cycle
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Last run is made on zero network
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
zeronet=false;
|
|
if(pass==passcount)
|
|
{
|
|
network.m_weights.Fill(0);
|
|
zeronet=true;
|
|
}
|
|
//--- Same inputs leads to same outputs
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network,x2,y2);
|
|
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:513");
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created:
|
|
//--- * using MLPCopy
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
UnsetNetwork(network2);
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:534");
|
|
//--- Additionally we tests functions for copying of tunable
|
|
//--- parameters by:
|
|
//--- * copying network using MLPCopy
|
|
//--- * randomizing tunable parameters with MLPRandomizeFull()
|
|
//--- * copying tunable parameters with:
|
|
//--- a) MLPCopyTunableParameters
|
|
//--- b) combination of MLPExportTunableParameters and
|
|
//--- MLPImportTunableParameters - we export parameters
|
|
//--- to P1, copy PCount elements to P2, then test import.
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
UnsetNetwork(network2);
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomizeFull(network2);
|
|
CMLPBase::MLPCopyTunableParameters(network,network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:571");
|
|
for(i=0; i<nout; i++)
|
|
y2.Set(i,2*CMath::RandomReal()-1);
|
|
UnsetNetwork(network2);
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomizeFull(network2);
|
|
CMLPBase::MLPExportTunableParameters(network,p0,pcount);
|
|
p1=p0;
|
|
CMLPBase::MLPImportTunableParameters(network2,p1);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:585");
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPSerialize
|
|
UnsetNetwork(network2);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,network);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network2);
|
|
_local_serializer.Stop();
|
|
}
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||!allsame;
|
|
//--- Different inputs leads to different outputs (non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,y1[i]);
|
|
}
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network,x2,y2);
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||allsame;
|
|
}
|
|
//--- Randomization changes outputs (when inputs are unchanged, non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,y1[i]);
|
|
}
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomize(network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||allsame;
|
|
}
|
|
//--- Full randomization changes outputs (when inputs are unchanged, non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0; i<nin ; i++)
|
|
{
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
x2.Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,2*CMath::RandomReal()-1);
|
|
y2.Set(i,y1[i]);
|
|
}
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomizeFull(network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err||allsame;
|
|
}
|
|
//--- Normalization properties
|
|
if(nkind==1)
|
|
{
|
|
//--- Classifier network outputs are normalized
|
|
for(i=0; i<nin ; i++)
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
v=0;
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
v+=y1[i];
|
|
err=err||y1[i]<0.0;
|
|
}
|
|
err=err||MathAbs(v-1)>(1000.0*CMath::m_machineepsilon);
|
|
}
|
|
if(nkind==2)
|
|
{
|
|
//--- B-type network outputs are bounded from above/below
|
|
for(i=0; i<nin ; i++)
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
if(a2>=0.0)
|
|
err=err||y1[i]<a1;
|
|
else
|
|
err=err||y1[i]>a1;
|
|
}
|
|
}
|
|
if(nkind==3)
|
|
{
|
|
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
|
|
for(i=0; i<nin ; i++)
|
|
x1.Set(i,2*CMath::RandomReal()-1);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
for(i=0; i<nout; i++)
|
|
err=(err||y1[i]<MathMin(a1,a2)||y1[i]>MathMax(a1,a2));
|
|
}
|
|
//--- Comperison MLPInitPreprocessor results with
|
|
//--- MLPInitPreprocessorSparse results
|
|
CSparse::SparseConvertToHash(sparsexy);
|
|
if(iscls)
|
|
{
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
{
|
|
densexy.Set(i,j,2*CMath::RandomReal()-1);
|
|
CSparse::SparseSet(sparsexy,i,j,densexy.Get(i,j));
|
|
}
|
|
densexy.Set(i,nin,CMath::RandomInteger(nout));
|
|
CSparse::SparseSet(sparsexy,i,j,densexy.Get(i,nin));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<npoints ; i++)
|
|
for(j=0; j<nin+nout ; j++)
|
|
{
|
|
densexy.Set(i,j,2*CMath::RandomReal()-1);
|
|
CSparse::SparseSet(sparsexy,i,j,densexy.Get(i,j));
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPInitPreprocessor(network,densexy,npoints);
|
|
CMLPBase::MLPInitPreprocessorSparse(network2,sparsexy,npoints);
|
|
subnp=CMath::RandomInteger(npoints);
|
|
for(i=0; i<=subnp-1; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
x1.Set(j,2*CMath::RandomReal()-1);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
for(j=0; j<nout; j++)
|
|
err=err||(double)(MathAbs(y1[j]-y2[j]))>1.0E-6;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gradient functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::TestGradient(int nkind,int nin,int nhid1,
|
|
int nhid2,int nout,int passcount,
|
|
int sizemin,int sizemax,bool &err)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron network;
|
|
CSparseMatrix sparsexy;
|
|
CSparseMatrix sparsexy2;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double h=0;
|
|
double etol=0;
|
|
double escale=0;
|
|
double gscale=0;
|
|
double nonstricttolerance=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int ssize=0;
|
|
int subsetsize=0;
|
|
int rowsize=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
CRowDouble grad1;
|
|
CRowDouble grad2;
|
|
CRowDouble gradsp;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble y1;
|
|
CRowDouble y2;
|
|
CRowInt idx;
|
|
double v=0;
|
|
double e=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double esp=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double v4=0;
|
|
double wprev=0;
|
|
double referencee=0;
|
|
CRowDouble referenceg;
|
|
int i_=0;
|
|
int i1_=0;
|
|
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
h=0.0001;
|
|
etol=1.0E-2;
|
|
escale=1.0E-2;
|
|
gscale=1.0E-2;
|
|
nonstricttolerance=0.01;
|
|
//--- Initialize
|
|
x=vector<double>::Zeros(nin);
|
|
x1=vector<double>::Zeros(nin);
|
|
x2=vector<double>::Zeros(nin);
|
|
y=vector<double>::Zeros(nout);
|
|
y1=vector<double>::Zeros(nout);
|
|
y2=vector<double>::Zeros(nout);
|
|
referenceg=vector<double>::Zeros(wcount);
|
|
grad1=vector<double>::Zeros(wcount);
|
|
grad2=vector<double>::Zeros(wcount);
|
|
//--- Process
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Randomize network, then re-randomaze weights manually.
|
|
//--- NOTE: weights magnitude is chosen to be small, about 0.1,
|
|
//--- which allows us to avoid oversaturated network.
|
|
//--- In 10% of cases we use zero weights.
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
if(CMath::RandomReal()<=0.1)
|
|
network.m_weights.Fill(0.0);
|
|
else
|
|
{
|
|
for(i=0; i<wcount; i++)
|
|
network.m_weights.Set(i,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
//--- Test MLPError(), MLPErrorSparse(), MLPGrad() for single-element dataset:
|
|
//--- * generate input X, output Y, combine them in dataset XY
|
|
//---*calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
|
|
//---*calculate "reference" gradient by performing numerical differentiation of "reference" error
|
|
//--- using 4-point differentiation formula
|
|
//--- * test error/gradient returned by MLPGrad(), MLPError(), MLPErrorSparse()
|
|
xy=matrix<double>::Zeros(1,nin+nout);
|
|
CSparse::SparseCreate(1,nin+nout,nin+nout,sparsexy);
|
|
for(i=0; i<nin ; i++)
|
|
x.Set(i,4*CMath::RandomReal()-2);
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(0,i_,x[i_]);
|
|
for(i=0; i<nin ; i++)
|
|
CSparse::SparseSet(sparsexy,0,i,x[i]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y.Set(i,0);
|
|
xy.Set(0,nin,CMath::RandomInteger(nout));
|
|
CSparse::SparseSet(sparsexy,0,nin,xy.Get(0,nin));
|
|
y.Set((int)MathRound(xy.Get(0,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y.Set(i,4*CMath::RandomReal()-2);
|
|
CSparse::SparseSet(sparsexy,0,nin+i,y[i]);
|
|
}
|
|
i1_= -nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(0,i_,y[i_+i1_]);
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
y2-=y;
|
|
referencee=y2.Dot(y2)/2.0;
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
wprev=network.m_weights[i];
|
|
network.m_weights.Set(i,wprev-2*h);
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
y1-=y;
|
|
v1=y1.Dot(y1)/2.0;
|
|
network.m_weights.Set(i,wprev-h);
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
y1-=y;
|
|
v2=y1.Dot(y1)/2.0;
|
|
network.m_weights.Set(i,wprev+h);
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
y1-=y;
|
|
v3=y1.Dot(y1)/2.0;
|
|
network.m_weights.Set(i,wprev+2*h);
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
y1-=y;
|
|
v4=y1.Dot(y1)/2.0;
|
|
network.m_weights.Set(i,wprev);
|
|
referenceg.Set(i,(v1-8*v2+8*v3-v4)/(12*h));
|
|
}
|
|
CMLPBase::MLPGrad(network,x,y,e,grad2);
|
|
CAp::SetErrorFlagDiff(err,e,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPError(network,xy,1),referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPErrorSparse(network,sparsexy,1),referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:948");
|
|
//--- Test MLPErrorN(), MLPGradN() for single-element dataset:
|
|
//--- * generate input X, output Y, combine them in dataset XY
|
|
//---*calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
|
|
//---*calculate "reference" gradient by performing numerical differentiation of "reference" error
|
|
//--- * test error/gradient returned by MLPGradN(), MLPErrorN()
|
|
//--- NOTE: because we use inexact 2-point formula, we perform gradient test with NonStrictTolerance
|
|
xy=matrix<double>::Zeros(1,nin+nout);
|
|
for(i=0; i<nin ; i++)
|
|
x.Set(i,4*CMath::RandomReal()-2);
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(0,i_,x[i_]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y.Set(i,0);
|
|
xy.Set(0,nin,CMath::RandomInteger(nout));
|
|
y.Set((int)MathRound(xy.Get(0,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y.Set(i,4*CMath::RandomReal()-2);
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(0,i_,y[i_+i1_]);
|
|
}
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
referencee=0;
|
|
if(nkind!=1)
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
referencee+=0.5*CMath::Sqr(y2[i]-y[i]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
if(y[i]!=0.0)
|
|
{
|
|
if(y2[i]==0.0)
|
|
referencee+=y[i]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
referencee+=y[i]*MathLog(y[i]/y2[i]);
|
|
}
|
|
}
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
wprev=network.m_weights[i];
|
|
network.m_weights.Set(i,wprev+h);
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
network.m_weights.Set(i,wprev-h);
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
network.m_weights.Set(i,wprev);
|
|
v=0;
|
|
if(nkind!=1)
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
v+=0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
if(y[j]!=0.0)
|
|
{
|
|
if((double)(y2[j])==0.0)
|
|
v+=y[j]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v+=y[j]*MathLog(y[j]/y2[j]);
|
|
if(y1[j]==0.0)
|
|
v-=y[j]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v-=y[j]*MathLog(y[j]/y1[j]);
|
|
}
|
|
v=v/(2*h);
|
|
}
|
|
referenceg.Set(i,v);
|
|
}
|
|
CMLPBase::MLPGradN(network,x,y,e,grad2);
|
|
CAp::SetErrorFlagDiff(err,e,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPErrorN(network,xy,1),referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>nonstricttolerance,"testmlpbaseunit.ap:1029");
|
|
//--- Test that gradient calculation functions automatically allocate
|
|
//--- space for gradient, if needed.
|
|
//--- NOTE: we perform test with empty dataset.
|
|
CSparse::SparseCreate(1,nin+nout,0,sparsexy);
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
grad1=vector<double>::Zeros(1);
|
|
CMLPBase::MLPGradBatch(network,xy,0,e1,grad1);
|
|
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1042");
|
|
grad1=vector<double>::Zeros(1);
|
|
CMLPBase::MLPGradBatchSparse(network,sparsexy,0,e1,grad1);
|
|
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1045");
|
|
grad1=vector<double>::Zeros(1);
|
|
CMLPBase::MLPGradBatchSubset(network,xy,0,idx,0,e1,grad1);
|
|
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1048");
|
|
grad1=vector<double>::Zeros(1);
|
|
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,0,idx,0,e1,grad1);
|
|
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1051");
|
|
//--- Test MLPError(), MLPErrorSparse(), MLPGradBatch(), MLPGradBatchSparse() for many-element dataset:
|
|
//--- * generate random dataset XY
|
|
//---*calculate "reference" error/gradient using MLPGrad(), which was tested in previous
|
|
//--- section and is assumed to work correctly
|
|
//--- * test results returned by MLPGradBatch/MLPGradBatchSparse against reference ones
|
|
//--- NOTE: about 10% of tests are performed with zero SSize
|
|
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
|
|
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+nout);
|
|
CSparse::SparseCreate(MathMax(ssize,1),nin+nout,ssize*(nin+nout),sparsexy);
|
|
referenceg.Fill(0);
|
|
referencee=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
{
|
|
x1.Set(j,4*CMath::RandomReal()-2);
|
|
CSparse::SparseSet(sparsexy,i,j,x1[j]);
|
|
}
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
y1.Fill(0);
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
CSparse::SparseSet(sparsexy,i,nin,xy.Get(i,nin));
|
|
y1.Set((int)MathRound(xy.Get(i,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
y1.Set(j,4*CMath::RandomReal()-2);
|
|
CSparse::SparseSet(sparsexy,i,nin+j,y1[j]);
|
|
}
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
referencee+=v;
|
|
for(i_=0; i_<wcount; i_++)
|
|
referenceg.Add(i_,grad2[i_]);
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
e2=CMLPBase::MLPError(network,xy,ssize);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
e2=CMLPBase::MLPErrorSparse(network,sparsexy,ssize);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CMLPBase::MLPGradBatch(network,xy,ssize,e2,grad2);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1104");
|
|
CMLPBase::MLPGradBatchSparse(network,sparsexy,ssize,esp,gradsp);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1107");
|
|
//--- Test MLPErrorSubset(), MLPGradBatchSubset(), MLPErrorSparseSubset(), MLPGradBatchSparseSubset()
|
|
//--- for many-element dataset with different types of subsets:
|
|
//--- * generate random dataset XY
|
|
//---*"reference" error/gradient are calculated with MLPGradBatch(),
|
|
//--- which was tested in previous section and is assumed to work correctly
|
|
//--- * we perform tests for different subsets:
|
|
//--- * SubsetSize<0 - subset is a full dataset
|
|
//--- * SubsetSize=0 - subset is empty
|
|
//--- * SubsetSize>0 - random subset
|
|
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
|
|
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+nout);
|
|
CSparse::SparseCreate(MathMax(ssize,1),nin+nout,ssize*(nin+nout),sparsexy);
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
{
|
|
x1.Set(j,4*CMath::RandomReal()-2);
|
|
CSparse::SparseSet(sparsexy,i,j,x1[j]);
|
|
}
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,0);
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
CSparse::SparseSet(sparsexy,i,nin,xy.Get(i,nin));
|
|
y1.Set((int)MathRound(xy.Get(i,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
y1.Set(j,4*CMath::RandomReal()-2);
|
|
CSparse::SparseSet(sparsexy,i,nin+j,y1[j]);
|
|
}
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
if(ssize>0)
|
|
{
|
|
subsetsize=1+CMath::RandomInteger(10);
|
|
xy2=matrix<double>::Zeros(subsetsize,nin+nout);
|
|
idx.Resize(subsetsize);
|
|
CSparse::SparseCreate(subsetsize,nin+nout,subsetsize*(nin+nout),sparsexy2);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
rowsize=nin+1;
|
|
else
|
|
rowsize=nin+nout;
|
|
for(i=0; i<=subsetsize-1; i++)
|
|
{
|
|
k=CMath::RandomInteger(ssize);
|
|
idx.Set(i,k);
|
|
for(j=0; j<=rowsize-1; j++)
|
|
{
|
|
xy2.Set(i,j,xy.Get(k,j));
|
|
CSparse::SparseSet(sparsexy2,i,j,CSparse::SparseGet(sparsexy,k,j));
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy2);
|
|
}
|
|
else
|
|
{
|
|
subsetsize=0;
|
|
CSparse::SparseCreate(1,nin+nout,0,sparsexy2);
|
|
CSparse::SparseConvertToCRS(sparsexy2);
|
|
}
|
|
CMLPBase::MLPGradBatch(network,xy,ssize,referencee,referenceg);
|
|
e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,-1);
|
|
esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,-1);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,-1,e2,grad2);
|
|
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,-1,esp,gradsp);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1192");
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1193");
|
|
CMLPBase::MLPGradBatch(network,xy,0,referencee,referenceg);
|
|
e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,0);
|
|
esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,0);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,0,e2,grad2);
|
|
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,0,esp,gradsp);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1205");
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1206");
|
|
CMLPBase::MLPGradBatch(network,xy2,subsetsize,referencee,referenceg);
|
|
e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,subsetsize);
|
|
esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,subsetsize);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,subsetsize,e2,grad2);
|
|
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,subsetsize,esp,gradsp);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1218");
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1219");
|
|
//--- Test MLPGradNBatch() for many-element dataset:
|
|
//--- * generate random dataset XY
|
|
//---*calculate "reference" error/gradient using MLPGrad(), which was tested in previous
|
|
//--- section and is assumed to work correctly
|
|
//--- * test results returned by MLPGradNBatch against reference ones
|
|
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
|
|
xy=matrix<double>::Zeros(ssize,nin+nout);
|
|
for(i=0; i<wcount; i++)
|
|
referenceg.Set(i,0);
|
|
referencee=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<nin ; j++)
|
|
x1.Set(j,4*CMath::RandomReal()-2);
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,0);
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
y1.Set((int)MathRound(xy.Get(i,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,4*CMath::RandomReal()-2);
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
referencee+=v;
|
|
for(i_=0; i_<wcount; i_++)
|
|
referenceg.Add(i_,grad2[i_]);
|
|
}
|
|
CMLPBase::MLPGradNBatch(network,xy,ssize,e2,grad2);
|
|
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
|
|
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1258");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Hessian functions test
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::TestHessian(int nkind,int nin,int nhid1,
|
|
int nhid2,int nout,
|
|
int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron network;
|
|
int hkind=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double h=0;
|
|
double etol=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ssize=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble h1;
|
|
CMatrixDouble h2;
|
|
CRowDouble grad1;
|
|
CRowDouble grad2;
|
|
CRowDouble grad3;
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble y1;
|
|
CRowDouble y2;
|
|
double v=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double wprev=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
h=0.00001;
|
|
etol=0.05;
|
|
//--- Initialize
|
|
x=vector<double>::Zeros(nin);
|
|
x1=vector<double>::Zeros(nin);
|
|
x2=vector<double>::Zeros(nin);
|
|
y=vector<double>::Zeros(nout);
|
|
y1=vector<double>::Zeros(nout);
|
|
y2=vector<double>::Zeros(nout);
|
|
grad1=vector<double>::Zeros(wcount);
|
|
grad2=vector<double>::Zeros(wcount);
|
|
grad3=vector<double>::Zeros(wcount);
|
|
h1=matrix<double>::Zeros(wcount,wcount);
|
|
h2=matrix<double>::Zeros(wcount,wcount);
|
|
//--- Process
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
//--- Test hessian calculation .
|
|
//--- E1 contains total error (calculated using MLPGrad/MLPGradN)
|
|
//--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
|
|
//--- H1 contains Hessian calculated using differences of gradients
|
|
//--- E2, Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
|
|
for(hkind=0; hkind<=1; hkind++)
|
|
{
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy=matrix<double>::Zeros(ssize,nin+nout-1+1);
|
|
for(i=0; i<wcount; i++)
|
|
grad1.Set(i,0);
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
for(j=0; j<wcount; j++)
|
|
h1.Set(i,j,0);
|
|
}
|
|
e1=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
//--- X, Y
|
|
for(j=0; j<nin ; j++)
|
|
x1.Set(j,4*CMath::RandomReal()-2);
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,0);
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
y1.Set((int)MathRound(xy.Get(i,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,4*CMath::RandomReal()-2);
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
//--- E1, Grad1
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
e1+=v;
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad1.Add(i_,grad2[i_]);
|
|
//--- H1
|
|
for(j=0; j<wcount; j++)
|
|
{
|
|
wprev=network.m_weights[j];
|
|
network.m_weights.Set(j,wprev-2*h);
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
network.m_weights.Set(j,wprev-h);
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad2.Add(i_,-8*grad3[i_]);
|
|
network.m_weights.Set(j,wprev+h);
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad2.Add(i_,8*grad3[i_]);
|
|
network.m_weights.Set(j,wprev+2*h);
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
for(i_=0; i_<wcount; i_++)
|
|
grad2.Add(i_,-grad3[i_]);
|
|
v=1.0/(12.0*h);
|
|
for(i_=0; i_<wcount; i_++)
|
|
h1.Add(j,i_,v*grad2[i_]);
|
|
network.m_weights.Set(j,wprev);
|
|
}
|
|
}
|
|
if(hkind==0)
|
|
CMLPBase::MLPHessianBatch(network,xy,ssize,e2,grad2,h2);
|
|
else
|
|
CMLPBase::MLPHessianNBatch(network,xy,ssize,e2,grad2,h2);
|
|
CAp::SetErrorFlag(err,(MathAbs(e1-e2)/e1)>etol,"testmlpbaseunit.ap:1440");
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
if(MathAbs(grad1[i])>1.0E-2)
|
|
CAp::SetErrorFlag(err,MathAbs((grad2[i]-grad1[i])/grad1[i])>etol,"testmlpbaseunit.ap:1443");
|
|
else
|
|
CAp::SetErrorFlag(err,MathAbs(grad2[i]-grad1[i])>etol,"testmlpbaseunit.ap:1445");
|
|
}
|
|
for(i=0; i<wcount; i++)
|
|
for(j=0; j<wcount; j++)
|
|
{
|
|
if(MathAbs(h1.Get(i,j))>5.0E-2)
|
|
CAp::SetErrorFlag(err,MathAbs((h1.Get(i,j)-h2.Get(i,j))/h1.Get(i,j))>etol,"testmlpbaseunit.ap:1449");
|
|
else
|
|
CAp::SetErrorFlag(err,MathAbs(h2.Get(i,j)-h1.Get(i,j))>etol,"testmlpbaseunit.ap:1451");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Error functions (other than MLPError and MLPErrorN) test. |
|
|
//| Network of type NKind is created, with NIn inputs, NHid1*NHid2 |
|
|
//| hidden layers (one layer if NHid2 = 0), NOut outputs. PassCount |
|
|
//| random passes is performed. Dataset has random size in |
|
|
//| [SizeMin, SizeMax]. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::TestErr(int nkind,int nin,int nhid1,
|
|
int nhid2,int nout,int passcount,
|
|
int sizemin,int sizemax,bool &err)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron network;
|
|
CSparseMatrix sparsexy;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double etol=0;
|
|
double escale=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int ssize=0;
|
|
int subsetsize=0;
|
|
CMatrixDouble xy;
|
|
CRowDouble y;
|
|
CRowDouble x1;
|
|
CRowDouble y1;
|
|
CRowInt idx;
|
|
CRowInt dummy;
|
|
double refrmserror=0;
|
|
double refclserror=0;
|
|
double refrelclserror=0;
|
|
double refavgce=0;
|
|
double refavgerror=0;
|
|
double refavgrelerror=0;
|
|
int avgrelcnt=0;
|
|
CModelErrors allerrors;
|
|
int nnmax=0;
|
|
int dsmax=0;
|
|
double relclstolerance=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
etol=1.0E-4;
|
|
escale=1.0E-2;
|
|
//--- Initialize
|
|
x1=vector<double>::Zeros(nin);
|
|
y=vector<double>::Zeros(nout);
|
|
y1=vector<double>::Zeros(nout);
|
|
//--- Process
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Randomize network, then re-randomaze weights manually.
|
|
//--- NOTE: weights magnitude is chosen to be small, about 0.1,
|
|
//--- which allows us to avoid oversaturated network.
|
|
//--- In 10% of cases we use zero weights.
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
if(CMath::RandomReal()<=0.1)
|
|
network.m_weights.Fill(0.0);
|
|
else
|
|
{
|
|
for(i=0; i<wcount; i++)
|
|
network.m_weights.Set(i,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
//--- Generate random dataset.
|
|
//--- Calculate reference errors.
|
|
//--- NOTE: about 10% of tests are performed with zero SSize
|
|
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+1);
|
|
CSparse::SparseCreate(MathMax(ssize,1),nin+1,0,sparsexy);
|
|
}
|
|
else
|
|
{
|
|
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+nout);
|
|
CSparse::SparseCreate(MathMax(ssize,1),nin+nout,0,sparsexy);
|
|
}
|
|
refrmserror=0.0;
|
|
refclserror=0.0;
|
|
refavgce=0.0;
|
|
refavgerror=0.0;
|
|
refavgrelerror=0.0;
|
|
avgrelcnt=0;
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
//--- Fill I-th row
|
|
for(j=0; j<nin ; j++)
|
|
{
|
|
x1.Set(j,4*CMath::RandomReal()-2);
|
|
CSparse::SparseSet(sparsexy,i,j,x1[j]);
|
|
}
|
|
for(i_=0; i_<nin ; i_++)
|
|
xy.Set(i,i_,x1[i_]);
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,0);
|
|
xy.Set(i,nin,CMath::RandomInteger(nout));
|
|
CSparse::SparseSet(sparsexy,i,nin,xy.Get(i,nin));
|
|
y1.Set((int)MathRound(xy.Get(i,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
y1.Set(j,4*CMath::RandomReal()-2);
|
|
if(y1[j]>=0.0)
|
|
y1.Add(j,0.1);
|
|
else
|
|
y1.Add(j,-0.1);
|
|
CSparse::SparseSet(sparsexy,i,nin+j,y1[j]);
|
|
}
|
|
i1_=-nin;
|
|
for(i_=nin; i_<nin+nout ; i_++)
|
|
xy.Set(i,i_,y1[i_+i1_]);
|
|
}
|
|
//--- Process
|
|
CMLPBase::MLPProcess(network,x1,y);
|
|
//--- Update reference errors
|
|
nnmax=0;
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
if(y[(int)MathRound(xy.Get(i,nin))]>0.0)
|
|
refavgce+=MathLog(1.0/y[(int)MathRound(xy.Get(i,nin))]);
|
|
else
|
|
refavgce+=MathLog(CMath::m_maxrealnumber);
|
|
}
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
dsmax=(int)MathRound(xy.Get(i,nin));
|
|
else
|
|
dsmax=0;
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
refrmserror+=CMath::Sqr(y[j]-y1[j]);
|
|
refavgerror+=MathAbs(y[j]-y1[j]);
|
|
if(y1[j]!=0.0)
|
|
{
|
|
refavgrelerror+=MathAbs(y[j]-y1[j])/MathAbs(y1[j]);
|
|
avgrelcnt++;
|
|
}
|
|
if(y[j]>y[nnmax])
|
|
nnmax=j;
|
|
if(!CMLPBase::MLPIsSoftMax(network) && y1[j]>y1[dsmax])
|
|
dsmax=j;
|
|
}
|
|
if(nnmax!=dsmax)
|
|
refclserror++;
|
|
}
|
|
CSparse::SparseConvertToCRS(sparsexy);
|
|
if(ssize>0)
|
|
{
|
|
refrmserror=MathSqrt(refrmserror/(ssize*nout));
|
|
refavgerror/=(double)(ssize*nout);
|
|
refrelclserror=refclserror/ssize;
|
|
refavgce/=(ssize*MathLog(2.0));
|
|
}
|
|
else
|
|
refrelclserror=0.0;
|
|
if(avgrelcnt>0)
|
|
refavgrelerror/=(double)avgrelcnt;
|
|
//--- Test "continuous" errors on full dataset
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPRMSError(network,xy,ssize),refrmserror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgCE(network,xy,ssize),refavgce,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgError(network,xy,ssize),refavgerror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgRelError(network,xy,ssize),refavgrelerror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPRMSErrorSparse(network,sparsexy,ssize),refrmserror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgCESparse(network,sparsexy,ssize),refavgce,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgErrorSparse(network,sparsexy,ssize),refavgerror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgRelErrorSparse(network,sparsexy,ssize),refavgrelerror,etol,escale);
|
|
CMLPBase::MLPAllErrorsSubset(network,xy,ssize,dummy,-1,allerrors);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_AvgCE,refavgce,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_AvgError,refavgerror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_AvgRelError,refavgrelerror,etol,escale);
|
|
CMLPBase::MLPAllErrorsSparseSubset(network,sparsexy,ssize,dummy,-1,allerrors);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_AvgCE,refavgce,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_AvgError,refavgerror,etol,escale);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_AvgRelError,refavgrelerror,etol,escale);
|
|
//--- Test errors on dataset given by subset.
|
|
//--- We perform only limited test for RMS error, assuming that either all errors
|
|
//--- are calculated correctly (subject to subset given by Idx) - or none of them.
|
|
if(ssize>0)
|
|
subsetsize=CMath::RandomInteger(10);
|
|
else
|
|
subsetsize=0;
|
|
idx.Resize(subsetsize);
|
|
refrmserror=0.0;
|
|
for(i=0; i<=subsetsize-1; i++)
|
|
{
|
|
k=CMath::RandomInteger(ssize);
|
|
idx.Set(i,k);
|
|
for(i_=0; i_<nin ; i_++)
|
|
x1.Set(i_,xy.Get(k,i_));
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,0);
|
|
y1.Set((int)MathRound(xy.Get(k,nin)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
y1.Set(j,xy.Get(k,nin+j));
|
|
}
|
|
CMLPBase::MLPProcess(network,x1,y);
|
|
for(j=0; j<nout; j++)
|
|
refrmserror+=CMath::Sqr(y[j]-y1[j]);
|
|
}
|
|
if(subsetsize>0)
|
|
refrmserror=MathSqrt(refrmserror/(subsetsize*nout));
|
|
CMLPBase::MLPAllErrorsSubset(network,xy,ssize,idx,subsetsize,allerrors);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
|
|
CMLPBase::MLPAllErrorsSparseSubset(network,sparsexy,ssize,idx,subsetsize,allerrors);
|
|
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
|
|
//--- Test "discontinuous" error function.
|
|
//--- Even slight changes in the network output may force these functions
|
|
//--- to change by 1. So, we test them with relaxed criteria, corresponding to
|
|
//--- difference in classification of two samples.
|
|
if(ssize>0)
|
|
{
|
|
relclstolerance=2.5/ssize;
|
|
CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPClsError(network,xy,ssize)-refclserror)>(ssize*relclstolerance),"testmlpbaseunit.ap:1732");
|
|
CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPRelClsError(network,xy,ssize)-refrelclserror)>relclstolerance,"testmlpbaseunit.ap:1733");
|
|
CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPRelClsErrorSparse(network,sparsexy,ssize)-refrelclserror)>relclstolerance,"testmlpbaseunit.ap:1734");
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Special tests |
|
|
//+------------------------------------------------------------------+
|
|
void CTestMLPBaseUnit::SpecTests(bool &inferrors,bool &procerrors,
|
|
bool &graderrors,bool &hesserrors,
|
|
bool &errerrors)
|
|
{
|
|
//--- create variables
|
|
CMultilayerPerceptron net;
|
|
CMatrixDouble xy;
|
|
double f=0;
|
|
CRowDouble g;
|
|
int i=0;
|
|
//--- Special test for overflow in TanH:
|
|
//--- * create 1x1x1 linear network
|
|
//--- * create dataset with 1 item: [x, y] = .Get(0, 1)
|
|
//--- * set network weights to [10000000, 10000000, 10000000, 10000000]
|
|
//--- * check that error function is finite
|
|
//--- * check that gradient is finite
|
|
CMLPBase::MLPCreate1(1,1,1,net);
|
|
xy=matrix<double>::Zeros(1,2);
|
|
xy.Set(0,1,1.0);
|
|
for(i=0; i<=CMLPBase::MLPGetWeightsCount(net)-1; i++)
|
|
net.m_weights.Set(i,10000000.0);
|
|
CMLPBase::MLPGradBatch(net,xy,1,f,g);
|
|
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(f),"testmlpbaseunit.ap:1907");
|
|
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(CMLPBase::MLPError(net,xy,1)),"testmlpbaseunit.ap:1908");
|
|
for(i=0; i<CMLPBase::MLPGetWeightsCount(net); i++)
|
|
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(g[i]),"testmlpbaseunit.ap:1910");
|
|
//--- Special test for overflow in SOFTMAX layer:
|
|
//--- * create 1x1x2 classifier network
|
|
//--- * create dataset with 1 item: [x, y] = .Get(0, 1)
|
|
//--- * set network weights to [10000000, 10000000, 10000000, 10000000]
|
|
//--- * check that error function is finite
|
|
//--- * check that gradient is finite
|
|
CMLPBase::MLPCreateC1(1,1,2,net);
|
|
xy=matrix<double>::Zeros(1,2);
|
|
xy.Set(0,1,1.0);
|
|
for(i=0; i<CMLPBase::MLPGetWeightsCount(net); i++)
|
|
net.m_weights.Set(i,10000000.0);
|
|
CMLPBase::MLPGradBatch(net,xy,1,f,g);
|
|
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(f),"testmlpbaseunit.ap:1928");
|
|
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(CMLPBase::MLPError(net,xy,1)),"testmlpbaseunit.ap:1929");
|
|
for(i=0; i<=CMLPBase::MLPGetWeightsCount(net)-1; i++)
|
|
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(g[i]),"testmlpbaseunit.ap:1931");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The function test functions MLPGradBatchMasked and |
|
|
//| MLPGradBatchSparseMasked. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestMLPBaseUnit::TestMLPGBSubset(void)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CMultilayerPerceptron net;
|
|
CMatrixDouble a;
|
|
CMatrixDouble parta;
|
|
CSparseMatrix sa;
|
|
CSparseMatrix partsa;
|
|
CRowInt idx;
|
|
double e1=0;
|
|
double e2=0;
|
|
CRowDouble grad1;
|
|
CRowDouble grad2;
|
|
int nin=0;
|
|
int nout=0;
|
|
int w=0;
|
|
int wcount=0;
|
|
int nhid1=0;
|
|
int nhid2=0;
|
|
int nkind=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int ssize=0;
|
|
int maxssize=0;
|
|
int sbsize=0;
|
|
int nvar=0;
|
|
int variant=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
//--- Variant:
|
|
//--- * 1 - there are all rows;
|
|
//--- * 2 - there are no one rows;
|
|
//--- * 3 - there are some random rows.
|
|
nvar=3;
|
|
maxssize=96;
|
|
for(ssize=0; ssize<=maxssize; ssize++)
|
|
{
|
|
idx.Resize(ssize);
|
|
nkind=CMath::RandomInteger(4);
|
|
a1=0;
|
|
a2=0;
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
nin=CMath::RandomInteger(20)+1;
|
|
nhid1=CMath::RandomInteger(5);
|
|
if(nhid1==0)
|
|
nhid2=0;
|
|
else
|
|
nhid2=CMath::RandomInteger(5);
|
|
nout=CMath::RandomInteger(20)+2;
|
|
CreateNetwork(net,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(net,n1,n2,wcount);
|
|
if(CMLPBase::MLPIsSoftMax(net))
|
|
{
|
|
w=nin+1;
|
|
if(ssize>0)
|
|
{
|
|
a=matrix<double>::Zeros(ssize,w);
|
|
CSparse::SparseCreate(ssize,w,ssize*w,sa);
|
|
}
|
|
else
|
|
{
|
|
a.Resize(0,0);
|
|
CSparse::SparseCreate(1,1,0,sa);
|
|
}
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<w ; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
a.Set(i,nin,CMath::RandomInteger(nout));
|
|
CSparse::SparseSet(sa,i,nin,a.Get(i,nin));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
w=nin+nout;
|
|
if(ssize>0)
|
|
{
|
|
a=matrix<double>::Zeros(ssize,w);
|
|
CSparse::SparseCreate(ssize,w,ssize*w,sa);
|
|
}
|
|
else
|
|
{
|
|
a.Resize(0,0);
|
|
CSparse::SparseCreate(1,1,0,sa);
|
|
}
|
|
for(i=0; i<ssize; i++)
|
|
{
|
|
for(j=0; j<w ; j++)
|
|
{
|
|
a.Set(i,j,2*CMath::RandomReal()-1);
|
|
CSparse::SparseSet(sa,i,j,a.Get(i,j));
|
|
}
|
|
}
|
|
}
|
|
CSparse::SparseConvertToCRS(sa);
|
|
for(variant=1; variant<=nvar; variant++)
|
|
{
|
|
sbsize=-1;
|
|
if(variant==1)
|
|
{
|
|
sbsize=ssize;
|
|
for(i=0; i<sbsize ; i++)
|
|
idx.Set(i,i);
|
|
}
|
|
if(variant==2)
|
|
sbsize=0;
|
|
if(variant==3)
|
|
{
|
|
if(ssize==0)
|
|
sbsize=0;
|
|
else
|
|
sbsize=CMath::RandomInteger(ssize);
|
|
for(i=0; i<sbsize ; i++)
|
|
idx.Set(i,CMath::RandomInteger(ssize));
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(sbsize>=0,"mlpbase test: integrity check failed"))
|
|
return(true);
|
|
if(sbsize!=0)
|
|
{
|
|
parta=matrix<double>::Zeros(sbsize,w);
|
|
CSparse::SparseCreate(sbsize,w,sbsize*w,partsa);
|
|
}
|
|
else
|
|
{
|
|
parta.Resize(0,0);
|
|
CSparse::SparseCreate(1,1,0,partsa);
|
|
}
|
|
for(i=0; i<sbsize ; i++)
|
|
{
|
|
for(i_=0; i_<w ; i_++)
|
|
parta.Set(i,i_,a.Get(idx[i],i_));
|
|
for(j=0; j<w ; j++)
|
|
CSparse::SparseSet(partsa,i,j,parta.Get(i,j));
|
|
}
|
|
CSparse::SparseConvertToCRS(partsa);
|
|
CMLPBase::MLPGradBatch(net,parta,sbsize,e1,grad1);
|
|
CMLPBase::MLPGradBatchSubset(net,a,ssize,idx,sbsize,e2,grad2);
|
|
//--- Test for dense matrix
|
|
if(MathAbs(e1-e2)>1.0E-6)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
if(MathAbs(grad1[i]-grad2[i])>1.0E-6)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Test for sparse matrix
|
|
CMLPBase::MLPGradBatchSparse(net,partsa,sbsize,e1,grad1);
|
|
CMLPBase::MLPGradBatchSparseSubset(net,sa,ssize,idx,sbsize,e2,grad2);
|
|
if(MathAbs(e1-e2)>1.0E-6)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<wcount; i++)
|
|
{
|
|
if(MathAbs(grad1[i]-grad2[i])>1.0E-6)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFiltersUnit
|
|
{
|
|
public:
|
|
static bool CTestFiltersUnit::TestFilters(bool silent);
|
|
|
|
private:
|
|
static bool TestSMA(bool issilent);
|
|
static bool TestEMA(bool issilent);
|
|
static bool TestLRMA(bool issilent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFiltersUnit::TestFilters(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool smaerrors;
|
|
bool emaerrors;
|
|
bool lrmaerrors;
|
|
|
|
smaerrors=TestSMA(silent);
|
|
emaerrors=TestEMA(silent);
|
|
lrmaerrors=TestLRMA(silent);
|
|
//--- Final report
|
|
waserrors=(smaerrors||emaerrors)||lrmaerrors;
|
|
if(!silent)
|
|
{
|
|
Print("FILTERS TEST");
|
|
PrintResult("* SMA",!smaerrors);
|
|
PrintResult("* EMA",!emaerrors);
|
|
PrintResult("* LRMA",!lrmaerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests SMA(k) filter. It returns True on error. |
|
|
//| Additional IsSilent parameter controls detailed error reporting. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFiltersUnit::TestSMA(bool issilent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CRowDouble x;
|
|
bool precomputederrors;
|
|
bool zerohandlingerrors;
|
|
double threshold=1000*CMath::m_machineepsilon;
|
|
|
|
if(!issilent)
|
|
Print("SMA(K) TEST");
|
|
//--- Test several pre-computed problems.
|
|
//--- NOTE: tests below rely on the fact that floating point
|
|
//--- additions and subtractions are exact when dealing
|
|
//--- with integer values.
|
|
precomputederrors=false;
|
|
x=vector<double>::Full(1,7);
|
|
CFilters::FilterSMA(x,1,1);
|
|
precomputederrors=precomputederrors||x[0]!=7.0;
|
|
x=vector<double>::Zeros(3);
|
|
x.Set(0,7);
|
|
x.Set(1,8);
|
|
x.Set(2,9);
|
|
CFilters::FilterSMA(x,3,1);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0);
|
|
CFilters::FilterSMA(x,3,2);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.5);
|
|
x=vector<double>::Zeros(3);
|
|
x.Set(0,7);
|
|
x.Set(1,8);
|
|
x.Set(2,9);
|
|
CFilters::FilterSMA(x,3,4);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.0);
|
|
//--- Test zero-handling:
|
|
//--- a) when we have non-zero sequence (N1 elements) followed by zero sequence
|
|
//--- (N2 elements), then first N1+K-1 elements of the processed sequence are
|
|
//--- non-zero, but elements since (N1+K)th must be exactly zero.
|
|
//--- b) similar property holds for zero sequence followed by non-zero one
|
|
//--- Naive implementation of SMA does not have such property.
|
|
//--- NOTE: it is important to initialize X with non-integer elements with long
|
|
//--- binary mantissas, because this test tries to test behaviour in the presence
|
|
//--- of roundoff errors, and it will be useless when used with integer inputs.
|
|
zerohandlingerrors=false;
|
|
x=vector<double>::Zeros(10);
|
|
x.Set(0,MathSqrt(2));
|
|
x.Set(1,MathSqrt(3));
|
|
x.Set(2,MathSqrt(5));
|
|
x.Set(3,MathSqrt(6));
|
|
x.Set(4,MathSqrt(7));
|
|
CFilters::FilterSMA(x,10,3);
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[0]-MathSqrt(2))>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[1]-(MathSqrt(2)+MathSqrt(3))/2)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[2]-(MathSqrt(2)+MathSqrt(3)+MathSqrt(5))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[3]-(MathSqrt(3)+MathSqrt(5)+MathSqrt(6))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[4]-(MathSqrt(5)+MathSqrt(6)+MathSqrt(7))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[5]-(MathSqrt(6)+MathSqrt(7))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[6]-MathSqrt(7)/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||x[7]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||x[8]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||x[9]!=0.0;
|
|
x.Set(0,0);
|
|
x.Set(1,0);
|
|
x.Set(2,0);
|
|
x.Set(3,0);
|
|
x.Set(4,0);
|
|
x.Set(5,MathSqrt(2));
|
|
x.Set(6,MathSqrt(3));
|
|
x.Set(7,MathSqrt(5));
|
|
x.Set(8,MathSqrt(6));
|
|
x.Set(9,MathSqrt(7));
|
|
CFilters::FilterSMA(x,10,3);
|
|
zerohandlingerrors=zerohandlingerrors||x[0]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||x[1]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||x[2]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||x[3]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||x[4]!=0.0;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[5]-MathSqrt(2)/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[6]-(MathSqrt(2)+MathSqrt(3))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[7]-(MathSqrt(2)+MathSqrt(3)+MathSqrt(5))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[8]-(MathSqrt(3)+MathSqrt(5)+MathSqrt(6))/3)>threshold;
|
|
zerohandlingerrors=zerohandlingerrors||MathAbs(x[9]-(MathSqrt(5)+MathSqrt(6)+MathSqrt(7))/3)>threshold;
|
|
//--- Final result
|
|
result=precomputederrors||zerohandlingerrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests EMA(alpha) filter. It returns True on error. |
|
|
//| Additional IsSilent parameter controls detailed error reporting. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFiltersUnit::TestEMA(bool issilent)
|
|
{
|
|
//--- create variables
|
|
CRowDouble x;
|
|
bool precomputederrors;
|
|
|
|
if(!issilent)
|
|
Print("EMA(alpha) TEST");
|
|
//--- Test several pre-computed problems.
|
|
//--- NOTE: tests below rely on the fact that floating point
|
|
//--- additions and subtractions are exact when dealing
|
|
//--- with integer values.
|
|
precomputederrors=false;
|
|
x=vector<double>::Full(1,7);
|
|
CFilters::FilterEMA(x,1,1.0);
|
|
precomputederrors=precomputederrors||x[0]!=7.0;
|
|
CFilters::FilterEMA(x,1,0.5);
|
|
precomputederrors=precomputederrors||x[0]!=7.0;
|
|
x=vector<double>::Zeros(3);
|
|
x.Set(0,7);
|
|
x.Set(1,8);
|
|
x.Set(2,9);
|
|
CFilters::FilterEMA(x,3,1.0);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0);
|
|
CFilters::FilterEMA(x,3,0.5);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.25);
|
|
//--- Final result
|
|
return(precomputederrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests LRMA(k) filter. It returns True on error. |
|
|
//| Additional IsSilent parameter controls detailed error reporting. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestFiltersUnit::TestLRMA(bool issilent)
|
|
{
|
|
//--- create variables
|
|
CRowDouble x;
|
|
bool precomputederrors=false;
|
|
double threshold=1000*CMath::m_machineepsilon;
|
|
|
|
if(!issilent)
|
|
Print("LRMA(K) TEST");
|
|
//--- First, check that filter does not changes points for K=1 or K=2
|
|
x=vector<double>::Full(1,7);
|
|
CFilters::FilterLRMA(x,1,1);
|
|
precomputederrors=precomputederrors||x[0]!=7.0;
|
|
x=vector<double>::Zeros(6);
|
|
x.Set(0,7);
|
|
x.Set(1,8);
|
|
x.Set(2,9);
|
|
x.Set(3,10);
|
|
x.Set(4,11);
|
|
x.Set(5,12);
|
|
CFilters::FilterLRMA(x,6,1);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0||x[3]!=10.0||x[4]!=11.0||x[5]!=12.0);
|
|
CFilters::FilterLRMA(x,6,2);
|
|
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0||x[3]!=10.0||x[4]!=11.0||x[5]!=12.0);
|
|
//--- Check several precomputed problems
|
|
x=vector<double>::Zeros(6);
|
|
x.Set(0,7);
|
|
x.Set(1,8);
|
|
x.Set(2,9);
|
|
x.Set(3,10);
|
|
x.Set(4,11);
|
|
x.Set(5,12);
|
|
CFilters::FilterLRMA(x,6,3);
|
|
precomputederrors=precomputederrors||MathAbs(x[0]-7)>threshold;
|
|
precomputederrors=precomputederrors||MathAbs(x[1]-8)>threshold;
|
|
precomputederrors=precomputederrors||MathAbs(x[2]-9)>threshold;
|
|
precomputederrors=precomputederrors||MathAbs(x[3]-10)>threshold;
|
|
precomputederrors=precomputederrors||MathAbs(x[4]-11)>threshold;
|
|
precomputederrors=precomputederrors||MathAbs(x[5]-12)>threshold;
|
|
x=vector<double>::Zeros(6);
|
|
x.Set(0,7);
|
|
x.Set(1,8);
|
|
x.Set(2,8);
|
|
x.Set(3,9);
|
|
x.Set(4,12);
|
|
x.Set(5,12);
|
|
CFilters::FilterLRMA(x,6,3);
|
|
precomputederrors=precomputederrors||MathAbs(x[0]-7.0000000000)>(1.0E-5);
|
|
precomputederrors=precomputederrors||MathAbs(x[1]-8.0000000000)>(1.0E-5);
|
|
precomputederrors=precomputederrors||MathAbs(x[2]-8.1666666667)>(1.0E-5);
|
|
precomputederrors=precomputederrors||MathAbs(x[3]-8.8333333333)>(1.0E-5);
|
|
precomputederrors=precomputederrors||MathAbs(x[4]-11.6666666667)>(1.0E-5);
|
|
precomputederrors=precomputederrors||MathAbs(x[5]-12.5000000000)>(1.0E-5);
|
|
//--- Final result
|
|
return(precomputederrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSSAUnit
|
|
{
|
|
public:
|
|
static bool CTestSSAUnit::TestSSA(bool silent);
|
|
|
|
private:
|
|
static void TestGeneral(bool &errorflag);
|
|
static void TestSpecial(bool&errorflag);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestSSAUnit::TestSSA(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool specerrors;
|
|
bool generrors;
|
|
bool waserrors;
|
|
|
|
specerrors=false;
|
|
generrors=false;
|
|
TestSpecial(specerrors);
|
|
TestGeneral(generrors);
|
|
//--- Final report
|
|
waserrors=specerrors||generrors;
|
|
if(!silent)
|
|
{
|
|
Print("SSA TEST");
|
|
PrintResult("* GENERAL TEST SUITE",!generrors)
|
|
PrintResult("* SPECIAL CASES",!specerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests SSA on several general purpose analysis / |
|
|
//| prediction problems. |
|
|
//| On failure sets ErrorFlag, on success it is untouched. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSSAUnit::TestGeneral(bool &errorflag)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double vv=0;
|
|
int pass=0;
|
|
CSSAModel state;
|
|
CSSAModel state2;
|
|
int ntracks=0;
|
|
int windowwidth=0;
|
|
int nlasttracklen=0;
|
|
int nzeros=0;
|
|
int nlinear=0;
|
|
double sinefreq=0;
|
|
double sineoffs=0;
|
|
double sineamp=0;
|
|
int windowwidth2=0;
|
|
int nbasis=0;
|
|
int nbasis2=0;
|
|
int ninitial=0;
|
|
CRowDouble x;
|
|
CRowDouble x2;
|
|
CRowDouble trend;
|
|
CRowDouble noise;
|
|
CRowDouble sv;
|
|
CRowDouble sv2;
|
|
CRowDouble tmp0;
|
|
CRowDouble trend2;
|
|
CRowDouble noise2;
|
|
int algotype=0;
|
|
int nticks=0;
|
|
int nnoise=0;
|
|
int navg=0;
|
|
CMatrixDouble a;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble b;
|
|
double tol=0;
|
|
int datalen=0;
|
|
int forecastlen=0;
|
|
int mlimit=0;
|
|
int passcount=0;
|
|
double skipprob=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
CHighQualityRandState rs;
|
|
//--- Initialize RNG, test pass count and skip probability.
|
|
//--- When we perform several sequential tests on the same model, we may
|
|
//--- skip some of them with probability SkipProb in order to make sure
|
|
//--- that no carry-over effect is observed between tests.
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
passcount=500;
|
|
skipprob=0.50;
|
|
//--- Iterate over several algorithm types.
|
|
//--- Algorithms unsupported by tests are skipped within tests.
|
|
for(algotype=1; algotype<=3; algotype++)
|
|
{
|
|
//--- Test that on perfectly constant dataset SSA correctly predicts
|
|
//--- perfectly constant trend. Additionally test that analysis phase
|
|
//--- correctly returns nearly-zero noise and nearly-constant trend.
|
|
//--- Dataset is a one/few sequences with different constants; top-1
|
|
//--- algorithm is used (or precomputed unit-normalized vector of 1's).
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
nlasttracklen=-9999999;
|
|
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
windowwidth=2+CHighQualityRand::HQRndUniformI(rs,3);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
nlasttracklen=windowwidth+CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(i=0; i<nlasttracklen; i++)
|
|
x.Set(i,v);
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
b=matrix<double>::Zeros(windowwidth,1);
|
|
for(i=0; i<windowwidth ; i++)
|
|
b.Set(i,0,1.0/MathSqrt(windowwidth));
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,1);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,1);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,1);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
tol=1.0E-6;
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks);
|
|
CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:143");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:144");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:145");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen+i-windowwidth])>tol,"testssaunit.ap:150");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:151");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAAnalyzeLast(state,nlasttracklen,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nlasttracklen,"testssaunit.ap:157");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nlasttracklen,"testssaunit.ap:158");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nlasttracklen; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i])>tol,"testssaunit.ap:163");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:164");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:172");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<CAp::Len(trend) ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen-1])>tol,"testssaunit.ap:176");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+3);
|
|
CSSA::SSAForecastAvgLast(state,navg,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:184");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<CAp::Len(trend) ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen-1])>tol,"testssaunit.ap:188");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
x.Set(0,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=1; i<datalen ; i++)
|
|
x.Set(i,x[i-1]);
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:200");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[datalen-1])>tol,"testssaunit.ap:204");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
|
|
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+3);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
x.Set(0,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=1; i<datalen ; i++)
|
|
x.Set(i,x[i-1]);
|
|
CSSA::SSAForecastAvgSequence(state,x,datalen,navg,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:217");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x[datalen-1]))>tol,"testssaunit.ap:221");
|
|
}
|
|
}
|
|
//--- Test that on specially designed linear dataset SSA correctly
|
|
//--- predicts perfectly linear trend. Additionally test that analysis
|
|
//--- phase correctly returns nearly-zero noise and nearly-linear trend.
|
|
//--- Also test that correct basis vectors are returned.
|
|
//--- Dataset consists of many WindowWidth-sized (exactly) sequences with
|
|
//--- linear trend. Trend coefficients vary accross sequences. Top-2
|
|
//--- algorithm is used (or precomputed unit-normalized linear trend basis).
|
|
//--- NOTE: this test requires NTracks>=2, WindowWidth>=3 and TopK=2 to work;
|
|
//--- however, in order to improve numerical properties (diversity among
|
|
//--- samples, different slopes and offsets) we set NTracks to be
|
|
//--- at least 5.
|
|
//--- NOTE: one more version of this test verifies scaling properties
|
|
//--- by solving larger task (WindowWidth=100, NTracks=5,
|
|
//--- TrackLen=2*WindowWidth). This version makes just 5 checks because
|
|
//--- of higher cost.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
ntracks=5+CHighQualityRand::HQRndUniformI(rs,10);
|
|
windowwidth=3+CHighQualityRand::HQRndUniformI(rs,3);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
x=vector<double>::Zeros(windowwidth);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
x.Set(0,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=1; i<windowwidth ; i++)
|
|
x.Set(i,x[i-1]+v);
|
|
CSSA::SSAAddSequence(state,x,windowwidth);
|
|
}
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
b=matrix<double>::Zeros(windowwidth,2);
|
|
v=0.0;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
b.Set(i,0,1.0/MathSqrt(windowwidth));
|
|
b.Set(i,1,i);
|
|
v+=(double)i/(double)windowwidth;
|
|
}
|
|
vv=0.0;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
b.Add(i,1,-v);
|
|
vv+=CMath::Sqr(b.Get(i,1));
|
|
}
|
|
for(i=0; i<windowwidth ; i++)
|
|
b.Mul(i,1,1.0/MathSqrt(vv));
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,2);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,2);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,2);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
tol=1.0E-6;
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
//--- Basis vectors must be linear/constant functions
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:293");
|
|
CAp::SetErrorFlag(errorflag,nbasis!=2,"testssaunit.ap:294");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:295");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:296");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:297");
|
|
if(errorflag)
|
|
return;
|
|
for(j=0; j<nbasis ; j++)
|
|
{
|
|
v=a.Get(1,j)-a.Get(0,j);
|
|
for(i=2; i<windowwidth ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(a.Get(i,j)-a.Get(i-1,j)-v)>tol,"testssaunit.ap:304");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks);
|
|
CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:310");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:311");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:312");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i])>tol,"testssaunit.ap:317");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:318");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAAnalyzeLast(state,windowwidth,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:324");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:325");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i])>tol,"testssaunit.ap:330");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:331");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:338");
|
|
if(errorflag)
|
|
return;
|
|
v=x[windowwidth-1]-x[windowwidth-2];
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[windowwidth-1]-v)>tol,"testssaunit.ap:342");
|
|
for(i=1; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:344");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+5);
|
|
CSSA::SSAForecastAvgLast(state,navg,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:352");
|
|
if(errorflag)
|
|
return;
|
|
v=x[windowwidth-1]-x[windowwidth-2];
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[windowwidth-1]-v)>tol,"testssaunit.ap:356");
|
|
for(i=1; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:358");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
x.Set(0,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=1; i<datalen ; i++)
|
|
x.Set(i,x[i-1]+v);
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:371");
|
|
if(errorflag)
|
|
return;
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[datalen-1]-v)>tol,"testssaunit.ap:374");
|
|
for(i=1; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:376");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
|
|
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+5);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
x.Set(0,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=1; i<datalen ; i++)
|
|
x.Set(i,x[i-1]+v);
|
|
CSSA::SSAForecastAvgSequence(state,x,datalen,navg,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:390");
|
|
if(errorflag)
|
|
return;
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[datalen-1]-v)>tol,"testssaunit.ap:393");
|
|
for(i=1; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:395");
|
|
}
|
|
}
|
|
for(pass=1; pass<=5; pass++)
|
|
{
|
|
ntracks=5+CHighQualityRand::HQRndUniformI(rs,10);
|
|
windowwidth=100;
|
|
nlasttracklen=2*windowwidth;
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
x.Set(0,CHighQualityRand::HQRndNormal(rs));
|
|
for(i=1; i<nlasttracklen; i++)
|
|
x.Set(i,x[i-1]+v);
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
b=matrix<double>::Zeros(windowwidth,2);
|
|
v=0.0;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
b.Set(i,0,1/MathSqrt(windowwidth));
|
|
b.Set(i,1,i);
|
|
v+=(double)i/(double)windowwidth;
|
|
}
|
|
vv=0.0;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
b.Add(i,1,-v);
|
|
vv+=CMath::Sqr(b.Get(i,1));
|
|
}
|
|
for(i=0; i<windowwidth ; i++)
|
|
b.Mul(i,1,1.0/MathSqrt(vv));
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,2);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,2);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,2);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
tol=1.0E-6;
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks);
|
|
CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:444");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:445");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:446");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen+i-windowwidth])>tol,"testssaunit.ap:451");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:452");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAAnalyzeLast(state,nlasttracklen,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nlasttracklen,"testssaunit.ap:458");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nlasttracklen,"testssaunit.ap:459");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nlasttracklen; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x[i]))>tol,"testssaunit.ap:464");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:465");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:472");
|
|
if(errorflag)
|
|
return;
|
|
v=x[nlasttracklen-1]-x[nlasttracklen-2];
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[nlasttracklen-1]-v)>tol,"testssaunit.ap:476");
|
|
for(i=1; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:478");
|
|
}
|
|
}
|
|
//--- Test that on specially designed dataset with two sinusoidal components
|
|
//--- with significantly different amplitudes, whose periods are integer
|
|
//--- divisors of window width, SSA correctly separates leading sine from
|
|
//--- its smaller counterpart. Also test that we can correctly predict future
|
|
//--- values of the sequence.
|
|
//--- Dataset consists of many sequences, each of them featuring either
|
|
//--- sine with period=WindowWidth or sine with period=WindowWidth/2. Such
|
|
//--- dataset is necessary because sum of two sines is not well separated by
|
|
//--- SVD (it performs only approximate, asymptotic separation). But when
|
|
//--- every sequence is either one of the sines, but not two together, we
|
|
//--- can easily separate them.
|
|
//--- Sine coefficients are changed from sequence to sequence.
|
|
//--- NOTE: this test requires large WindowWidth and TopK=2 to work.
|
|
//--- NOTE: this test uses reduced number of passes because of higher computational complexity
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Skip "precomputed basis" algorithm
|
|
if(algotype==1)
|
|
continue;
|
|
//--- Generate dataset
|
|
ntracks=100+CHighQualityRand::HQRndUniformI(rs,2);
|
|
windowwidth=64;
|
|
nlasttracklen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSACreate(state);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
if(k%2==0)
|
|
{
|
|
sineamp=1+CHighQualityRand::HQRndUniformR(rs);
|
|
sinefreq=1;
|
|
}
|
|
else
|
|
{
|
|
sineamp=0.1*(1+CHighQualityRand::HQRndUniformR(rs));
|
|
sinefreq=2;
|
|
}
|
|
for(i=0; i<nlasttracklen; i++)
|
|
x.Set(i,sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq));
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
if(algotype==2)
|
|
CSSA::SSASetAlgoTopKDirect(state,2);
|
|
else
|
|
{
|
|
if(algotype==3)
|
|
CSSA::SSASetAlgoTopKRealtime(state,2);
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
tol=1.0E-6;
|
|
//--- Test analysis with WindowWidth=SinePeriod:
|
|
//--- * analyze sine with frequency=1, it must be recognized as trend
|
|
//--- * analyze sine with frequency=2, with smoothing enabled
|
|
//--- it must be discarded as noise;
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
nticks=windowwidth+1+CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
sineamp=1+CHighQualityRand::HQRndUniformR(rs);
|
|
sinefreq=1;
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq));
|
|
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:558");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:559");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
v=sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-v)>tol,"testssaunit.ap:565");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:566");
|
|
}
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,nticks-windowwidth);
|
|
datalen=nticks-forecastlen;
|
|
x2=x;
|
|
x2.Resize(datalen);
|
|
CSSA::SSAForecastSequence(state,x2,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[datalen+i])>tol,"testssaunit.ap:575");
|
|
windowwidth2=-1;
|
|
CSSA::SSAGetLRR(state,tmp0,windowwidth2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:579");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(tmp0)!=windowwidth-1,"testssaunit.ap:580");
|
|
if(errorflag)
|
|
return;
|
|
for(i=windowwidth-1; i<nticks ; i++)
|
|
{
|
|
i1_= -(i-(windowwidth-1));
|
|
v=0.0;
|
|
for(i_=i-(windowwidth-1); i_<=i-1; i_++)
|
|
v+=x[i_]*tmp0[i_+i1_];
|
|
CAp::SetErrorFlag(errorflag,MathAbs(v-x[i])>tol,"testssaunit.ap:586");
|
|
}
|
|
nticks=windowwidth+1+CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
sineamp=1+CHighQualityRand::HQRndUniformR(rs);
|
|
sinefreq=2;
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq));
|
|
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:597");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:598");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
v=sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq);
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>tol,"testssaunit.ap:604");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-v)>tol,"testssaunit.ap:605");
|
|
}
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,nticks-windowwidth);
|
|
datalen=nticks-forecastlen;
|
|
x2=x;
|
|
x2.Resize(datalen);
|
|
CSSA::SSAForecastSequence(state,x2,datalen,forecastlen,true,trend);
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>tol,"testssaunit.ap:614");
|
|
}
|
|
//--- Test appendPoint() functionality.
|
|
//--- We have specially designed dataset:
|
|
//--- * NZeros ticks of exactly zero values
|
|
//--- * NLinear ticks of series which linearly grow from 0 to 1
|
|
//--- * NLinear ticks of series which linearly decrease from 1 to 0
|
|
//--- * NZeros ticks of exactly zero values
|
|
//--- * NZeros=100
|
|
//--- * NLinear=20
|
|
//--- SSA settings have following values:
|
|
//--- * WindowWidth=4 or 25 (large widths are more problematic for
|
|
//--- iterative solvers, so they help to debug incremental updates)
|
|
//--- * NBasis=2
|
|
//--- We choose number of initial values to start with NInitial
|
|
//--- in [1..2*WindowWidth], then add points one
|
|
//--- by one with SSAAppendPointAndUpdate(). In the end we compare
|
|
//--- results returned by SSAGetBasis() with that returned by model
|
|
//--- which was created from full dataset.
|
|
//--- NOTE: we perform limited amount of passes because this test
|
|
//--- has high cost.
|
|
for(pass=1; pass<=25; pass++)
|
|
{
|
|
//--- Generate dataset
|
|
tol=1.0E-6;
|
|
nzeros=100;
|
|
nlinear=50;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
windowwidth=25;
|
|
else
|
|
windowwidth=4;
|
|
nbasis=2;
|
|
ninitial=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
x=vector<double>::Zeros(2*nzeros+2*nlinear);
|
|
for(i=0; i<=nlinear-1; i++)
|
|
{
|
|
x.Set(nzeros+i,(double)i/(double)nlinear);
|
|
x.Set(nzeros+nlinear+i,1-(double)i/(double)nlinear);
|
|
}
|
|
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
|
|
//--- Build model using many sequential appends
|
|
//--- NOTE: for NInitial>=WindowWidth with probability 50%
|
|
//--- we enforce basis calculation before first
|
|
//--- append() call. It helps to debug different
|
|
//--- branches of algorithms.
|
|
//--- NOTE: we may also request delayed power-up of the
|
|
//--- algorithm. It also checks various branches of
|
|
//--- the algo, although with such settings delayed
|
|
//--- power-up is hard to check (initial dataset is
|
|
//--- just zeros, zeros, zeros..).
|
|
x2=x;
|
|
x2.Resize(ninitial);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
CSSA::SSAAddSequence(state,x2,CAp::Len(x2));
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,nbasis);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,nbasis);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.5)
|
|
CSSA::SSASetPowerUpLength(state,10);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
if(ninitial>=windowwidth && CHighQualityRand::HQRndUniformR(rs)>0.5)
|
|
{
|
|
a.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:705");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:706");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:707");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:708");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:709");
|
|
if(errorflag)
|
|
return;
|
|
}
|
|
for(i=CAp::Len(x2); i<CAp::Len(x); i++)
|
|
CSSA::SSAAppendPointAndUpdate(state,x[i],1.0);
|
|
a.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:720");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:721");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:722");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:723");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:724");
|
|
if(errorflag)
|
|
return;
|
|
//--- Build model using one big sequence
|
|
CSSA::SSACreate(state2);
|
|
CSSA::SSASetWindow(state2,windowwidth);
|
|
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
CSSA::SSASetAlgoPrecomputed(state2,b,windowwidth,nbasis);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state2,nbasis);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
a2.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:747");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:748");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:749");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:750");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:751");
|
|
if(errorflag)
|
|
return;
|
|
//--- Compare results
|
|
for(i=0; i<nbasis ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(sv[i]-sv2[i])>tol,"testssaunit.ap:759");
|
|
for(j=0; j<nbasis ; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<windowwidth ; i_++)
|
|
v+=a.Get(i_,j)*a2.Get(i_,j);
|
|
for(i=0; i<windowwidth ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j))>tol,"testssaunit.ap:764");
|
|
}
|
|
}
|
|
//--- Test appendSequence() functionality.
|
|
//--- We have specially designed dataset:
|
|
//--- * NNoise sequences of Gaussian noise
|
|
//--- * NLinear linear sequences partially corrupted by noise
|
|
//--- * NZeros exactly zero sequences (used to let incremental algo converge)
|
|
//--- * all sequences have random size in [WindowWidth-2,WindowWidth+2]
|
|
//--- * NNoise=20
|
|
//--- * NLinear=20
|
|
//--- SSA settings have following values:
|
|
//--- * WindowWidth=4 or 25 (large widths are more problematic for
|
|
//--- iterative solvers, so they help to debug incremental updates)
|
|
//--- * NBasis=2
|
|
//--- We have two solvers:
|
|
//--- * one is trained on complete dataset
|
|
//--- * another one starts from NNoise noisy sequences, linear/zero sequenes
|
|
//--- are incrementally appended with AppendSequence
|
|
//--- NOTE: we perform limited amount of passes because this test
|
|
//--- has high cost.
|
|
for(pass=1; pass<=25; pass++)
|
|
{
|
|
//--- Problem metrics
|
|
tol=1.0E-5;
|
|
nnoise=20;
|
|
nlinear=20;
|
|
nzeros=50;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
windowwidth=25;
|
|
else
|
|
windowwidth=4;
|
|
nbasis=2;
|
|
//--- Initialize solvers
|
|
//--- NOTE: we set State.DefaultSubspaceIts to large value in order
|
|
//--- to ensure convergence to same basis.
|
|
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSACreate(state2);
|
|
state.m_DefaultSubspaceits=50;
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
CSSA::SSASetWindow(state2,windowwidth);
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,nbasis);
|
|
CSSA::SSASetAlgoPrecomputed(state2,b,windowwidth,nbasis);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,nbasis);
|
|
CSSA::SSASetAlgoTopKDirect(state2,nbasis);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
|
|
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Feed noisy sequences
|
|
for(i=0; i<nnoise ; i++)
|
|
{
|
|
k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2);
|
|
x=vector<double>::Zeros(k);
|
|
for(j=0; j<k; j++)
|
|
x.Set(j,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,CAp::Len(x));
|
|
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
|
|
}
|
|
//--- Feed linear and zero sequences.
|
|
//--- NOTE: with probability 50% we call SSAGetBasis(State2).
|
|
//--- Ideally, SSA should be able to handle appends correctly
|
|
//--- with or without preceeding call which requires basis
|
|
//--- to be evaluated.
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.5)
|
|
{
|
|
a2.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:865");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:866");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:867");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:868");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:869");
|
|
if(errorflag)
|
|
return;
|
|
}
|
|
for(i=0; i<=nlinear-1; i++)
|
|
{
|
|
k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2);
|
|
v=CHighQualityRand::HQRndNormal(rs);
|
|
vv=CHighQualityRand::HQRndNormal(rs);
|
|
x=vector<double>::Zeros(k);
|
|
x.Set(0,v);
|
|
for(j=1; j<k; j++)
|
|
x.Set(j,x[j-1]+vv+0.1*CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,CAp::Len(x));
|
|
CSSA::SSAAppendSequenceAndUpdate(state2,x,CAp::Len(x),1.0);
|
|
}
|
|
for(i=0; i<=nzeros-1; i++)
|
|
{
|
|
k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2);
|
|
x=vector<double>::Zeros(k);
|
|
CSSA::SSAAddSequence(state,x,CAp::Len(x));
|
|
CSSA::SSAAppendSequenceAndUpdate(state2,x,CAp::Len(x),1.0);
|
|
}
|
|
//--- Compare results
|
|
a.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:903");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:904");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:905");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:906");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:907");
|
|
if(errorflag)
|
|
return;
|
|
a2.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:915");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:916");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:917");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:918");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:919");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nbasis ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(sv[i]-sv2[i])>tol,"testssaunit.ap:923");
|
|
for(j=0; j<nbasis ; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<windowwidth ; i_++)
|
|
v+=a.Get(i_,j)*a2.Get(i_,j);
|
|
for(i=0; i<windowwidth ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j))>tol,"testssaunit.ap:928");
|
|
}
|
|
}
|
|
//--- Test memory limit functionality.
|
|
//--- Compare results obtained with HUGE limit vs ones obtained with small limit.
|
|
for(windowwidth=1; windowwidth<=20; windowwidth++)
|
|
{
|
|
for(mlimit=-1; mlimit<=16; mlimit++)
|
|
{
|
|
//--- Not tested
|
|
if(algotype==1)
|
|
continue;
|
|
//--- Problem metrics
|
|
tol=1.0E-5;
|
|
nticks=1000+CHighQualityRand::HQRndUniformI(rs,1000);
|
|
if(windowwidth>5)
|
|
nbasis=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
else
|
|
nbasis=1;
|
|
//--- Create solvers, dataset, set limits
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSACreate(state2);
|
|
if(mlimit>=0)
|
|
CSSA::SSASetMemoryLimit(state,(int)MathRound(MathPow(2,mlimit)));
|
|
else
|
|
CSSA::SSASetMemoryLimit(state,0);
|
|
CSSA::SSASetMemoryLimit(state2,999999999);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
CSSA::SSASetWindow(state2,windowwidth);
|
|
if(algotype==2)
|
|
{
|
|
CSSA::SSASetAlgoTopKDirect(state,nbasis);
|
|
CSSA::SSASetAlgoTopKDirect(state2,nbasis);
|
|
}
|
|
else
|
|
{
|
|
if(algotype==3)
|
|
{
|
|
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
|
|
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
|
|
}
|
|
else
|
|
{
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
}
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,CAp::Len(x));
|
|
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
|
|
//--- Reset internal temporaries for this test.
|
|
//--- Implementation-dependent, may fail due to future changes in the core.
|
|
state.m_UxBatch.Resize(0,0);
|
|
state.m_AseqTrajectory.Resize(0,0);
|
|
state.m_AseqTbProduct.Resize(0,0);
|
|
//--- Test
|
|
CSSA::SSAAnalyzeLast(state,nticks,trend,noise);
|
|
CSSA::SSAAnalyzeLast(state2,nticks,trend2,noise2);
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>tol,"testssaunit.ap:1001");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-noise2[i])>tol,"testssaunit.ap:1002");
|
|
}
|
|
//--- Additional tests for sizes of internal arrays.
|
|
//--- Implementation-dependent, may fail due to future changes in the core.
|
|
if(mlimit>=0)
|
|
{
|
|
k=MathMax(4*windowwidth*windowwidth,(int)MathRound(MathPow(2,mlimit)));
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_UxBatch)*CAp::Rows(state.m_UxBatch)>k,"testssaunit.ap:1012");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_AseqTrajectory)*CAp::Rows(state.m_AseqTrajectory)>k,"testssaunit.ap:1013");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_AseqTbProduct)*CAp::Rows(state.m_AseqTbProduct)>k,"testssaunit.ap:1014");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_UxBatch)!=nticks-windowwidth+1,"testssaunit.ap:1018");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_AseqTrajectory)!=nticks-windowwidth+1,"testssaunit.ap:1019");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_AseqTbProduct)!=nticks-windowwidth+1,"testssaunit.ap:1020");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test power-up ability of the real-time algorithm.
|
|
//--- We have specially designed dataset:
|
|
//--- * NTicks ticks of linearly descending from 1 to 0 linear trend,
|
|
//--- corrupted by random Gaussian noise
|
|
//--- * NZeros ticks of exactly zero values
|
|
//--- * NTicks=100
|
|
//--- * NZeros=100
|
|
//--- SSA settings have following values:
|
|
//--- * WindowWidth=4 or 25 (large widths are more problematic for
|
|
//--- iterative solvers, so they help to debug incremental updates)
|
|
//--- * NBasis=1..2 (only top vectors converge stable enough for unit testing)
|
|
//--- * powerup length is 10
|
|
//--- We perform two SSAs:
|
|
//--- * one with full dataset and no powerup
|
|
//--- * one with powerup, NTicks+WindowWidth first elements, followed by appending of NZeros zeros
|
|
//--- We check that:
|
|
//--- * basis found by second model (right after initialization) is different
|
|
//--- from basis of the first one
|
|
//--- * basis found by second model after last append is same as the first one
|
|
//--- NOTE: we perform limited amount of passes because this test
|
|
//--- has high cost.
|
|
for(pass=1; pass<=25; pass++)
|
|
{
|
|
//--- Generate dataset
|
|
tol=1.0E-3;
|
|
nticks=100;
|
|
nzeros=50;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
windowwidth=25;
|
|
else
|
|
windowwidth=4;
|
|
nbasis=1+CHighQualityRand::HQRndUniformI(rs,2);
|
|
x=vector<double>::Zeros(nticks+nzeros);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,1-(double)i/(double)nticks+0.05*CHighQualityRand::HQRndNormal(rs));
|
|
//--- Build complete model
|
|
//--- NOTE: we tweak S.DefaultSubspaceIts in order to enforce convergence to
|
|
//--- same basis; for this test we need extra-precise convergence.
|
|
CSSA::SSACreate(state2);
|
|
CSSA::SSASetWindow(state2,windowwidth);
|
|
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
|
|
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
|
|
a2.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1088");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1089");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:1090");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:1091");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:1092");
|
|
if(errorflag)
|
|
return;
|
|
//--- Build model with power-up cycle
|
|
//--- NOTE: with probability 50% we enforce basis calculation
|
|
//--- before first append() call and compare basis with
|
|
//--- one returned by full analysis. We do it only in 50%
|
|
//--- of the cases because randomness helps to debug different
|
|
//--- branches of algorithms.
|
|
x2=x;
|
|
x2.Resize(nticks+windowwidth);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
CSSA::SSAAddSequence(state,x2,CAp::Len(x2));
|
|
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
|
|
CSSA::SSASetPowerUpLength(state,10);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>0.5)
|
|
{
|
|
a.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1120");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1121");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1122");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:1123");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:1124");
|
|
if(errorflag)
|
|
return;
|
|
vv=0.0;
|
|
for(i=0; i<nbasis ; i++)
|
|
vv=MathMax(vv,MathAbs(sv[i]-sv2[i]));
|
|
for(j=0; j<nbasis ; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<windowwidth ; i_++)
|
|
v+=a.Get(i_,j)*a2.Get(i_,j);
|
|
for(i=0; i<windowwidth ; i++)
|
|
vv=MathMax(vv,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j)));
|
|
}
|
|
CAp::SetErrorFlag(errorflag,MathAbs(vv)<tol,"testssaunit.ap:1136");
|
|
}
|
|
for(i=CAp::Len(x2); i<CAp::Len(x) ; i++)
|
|
CSSA::SSAAppendPointAndUpdate(state,x[i],1.0);
|
|
a.Resize(0,0);
|
|
windowwidth2=-1;
|
|
nbasis2=-1;
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1145");
|
|
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1146");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1147");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:1148");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:1149");
|
|
if(errorflag)
|
|
return;
|
|
vv=0.0;
|
|
for(i=0; i<nbasis ; i++)
|
|
vv=MathMax(vv,MathAbs(sv[i]-sv2[i]));
|
|
for(j=0; j<nbasis ; j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0; i_<windowwidth ; i_++)
|
|
v+=a.Get(i_,j)*a2.Get(i_,j);
|
|
for(i=0; i<windowwidth ; i++)
|
|
vv=MathMax(vv,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j)));
|
|
}
|
|
CAp::SetErrorFlag(errorflag,MathAbs(vv)>tol,"testssaunit.ap:1161");
|
|
}
|
|
//--- Test that SSAForecastAvgLast/Sequence() actually performs averaging.
|
|
//--- We test it by comparing its results vs manually averaged predictions.
|
|
//--- Dataset is a small linear trend + Gaussian noise. We are not interested
|
|
//--- in getting meaningful components, we just want to check correctness of CMath::
|
|
for(pass=1; pass<=100; pass++)
|
|
{
|
|
nticks=75+CHighQualityRand::HQRndUniformI(rs,75);
|
|
windowwidth=5+CHighQualityRand::HQRndUniformI(rs,5);
|
|
navg=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,0.1*i+CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,nticks);
|
|
CSSA::SSASetAlgoTopKDirect(state,1+CHighQualityRand::HQRndUniformI(rs,3));
|
|
tol=1.0E-9;
|
|
CSSA::SSAForecastAvgLast(state,navg,forecastlen,trend);
|
|
trend2=vector<double>::Zeros(forecastlen);
|
|
for(i=0; i<forecastlen ; i++)
|
|
trend2.Set(i,0.0);
|
|
for(i=0; i<navg ; i++)
|
|
{
|
|
CSSA::SSAForecastSequence(state,x,nticks-i,forecastlen+i,true,tmp0);
|
|
for(j=0; j<forecastlen ; j++)
|
|
trend2.Add(j,tmp0[i+j]/navg);
|
|
}
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1198");
|
|
nticks=75+CHighQualityRand::HQRndUniformI(rs,75);
|
|
j=CHighQualityRand::HQRndUniformI(rs,150)-75;
|
|
x2=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x2.Set(i,0.1*(i+j)+CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastAvgSequence(state,x2,nticks,navg,forecastlen,true,trend);
|
|
trend2=vector<double>::Zeros(forecastlen);
|
|
for(i=0; i<forecastlen ; i++)
|
|
trend2.Set(i,0.0);
|
|
for(i=0; i<navg ; i++)
|
|
{
|
|
CSSA::SSAForecastSequence(state,x2,nticks-i,forecastlen+i,true,tmp0);
|
|
for(j=0; j<forecastlen ; j++)
|
|
trend2.Add(j,tmp0[i+j]/navg);
|
|
}
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1217");
|
|
nticks=75+CHighQualityRand::HQRndUniformI(rs,75);
|
|
j=CHighQualityRand::HQRndUniformI(rs,150)-75;
|
|
x2=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x2.Set(i,0.1*(i+j)+CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastAvgSequence(state,x2,nticks,navg,forecastlen,false,trend);
|
|
trend2=vector<double>::Zeros(forecastlen);
|
|
for(i=0; i<navg ; i++)
|
|
{
|
|
CSSA::SSAForecastSequence(state,x2,nticks-i,forecastlen+i,false,tmp0);
|
|
for(j=0; j<forecastlen ; j++)
|
|
trend2.Add(j,tmp0[i+j]/navg);
|
|
}
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1236");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests different special cases(mostly - degenerate |
|
|
//| ones). |
|
|
//| On failure sets ErrorFlag, on success it is untouched. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestSSAUnit::TestSpecial(bool&errorflag)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int pass=0;
|
|
CSSAModel state;
|
|
CSSAModel state2;
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CRowDouble x;
|
|
CRowDouble x2;
|
|
CRowDouble sv;
|
|
CRowDouble trend;
|
|
CRowDouble noise;
|
|
CRowDouble trend2;
|
|
CRowDouble noise2;
|
|
int algotype=0;
|
|
int nticks=0;
|
|
int nanalyzed=0;
|
|
int nlasttracklen=0;
|
|
int ntracks=0;
|
|
int maxtracklen=0;
|
|
int mintracklen=0;
|
|
int datalen=0;
|
|
int forecastlen=0;
|
|
int windowwidth=0;
|
|
int nbasis=0;
|
|
int windowwidth2=0;
|
|
CMatrixDouble tracksmatrix;
|
|
CRowInt trackssizes;
|
|
int passcount=0;
|
|
double skipprob=0;
|
|
int i_=0;
|
|
CHighQualityRandState rs;
|
|
//--- Initialize RNG, test pass count and skip probability.
|
|
//--- When we perform several sequential tests on the same model, we may
|
|
//--- skip some of them with probability SkipProb in order to make sure
|
|
//--- that no carry-over effect is observed between tests.
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
passcount=500;
|
|
skipprob=0.50;
|
|
//--- Test that for empty model in default state:
|
|
//--- * SSAGetBasis() returns zero 1x1 basis
|
|
//--- * SSAAnalyzeLastWindow() returns zeros as trend/noise
|
|
//--- * SSAAnalyzeLast() returns zeros as trend/noise
|
|
//--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
|
|
//--- * SSAForecastLast() returns zero trend
|
|
//--- * SSAForecastSequence() returns zero trend
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
CSSA::SSACreate(state);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1299");
|
|
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1300");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=1,"testssaunit.ap:1301");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1302");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1303");
|
|
for(i=0; i<CAp::Rows(a) ; i++)
|
|
{
|
|
for(j=0; j<CAp::Cols(a) ; j++)
|
|
CAp::SetErrorFlag(errorflag,a.Get(i,j)!=0.0,"testssaunit.ap:1306");
|
|
}
|
|
for(i=0; i<CAp::Len(sv) ; i++)
|
|
CAp::SetErrorFlag(errorflag,sv[i]!=0.0,"testssaunit.ap:1308");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
windowwidth=-1;
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1316");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=1 || trend[0]!=0.0,"testssaunit.ap:1317");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=1 || noise[0]!=0.0,"testssaunit.ap:1318");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAAnalyzeLast(state,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1326");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1327");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1332");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1333");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1345");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1346");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1351");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=x[i],"testssaunit.ap:1352");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1360");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1364");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
for(i=0; i<datalen ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1375");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1379");
|
|
}
|
|
}
|
|
//--- Test that for empty model with non-default window, but default
|
|
//--- algorithm and no data:
|
|
//--- * SSAGetBasis() returns zero WINDOWx1 basis
|
|
//--- * SSAAnalyzeLastWindow() returns zeros as trend/noise
|
|
//--- * SSAAnalyzeLast() returns zeros as trend/noise
|
|
//--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
|
|
//--- * SSAForecastLast() returns zero trend
|
|
//--- * SSAForecastSequence() returns zero trend
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
windowwidth2=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth2);
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1401");
|
|
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1402");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1403");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1404");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1405");
|
|
for(i=0; i<CAp::Rows(a) ; i++)
|
|
{
|
|
for(j=0; j<CAp::Cols(a) ; j++)
|
|
CAp::SetErrorFlag(errorflag,a.Get(i,j)!=0.0,"testssaunit.ap:1408");
|
|
}
|
|
for(i=0; i<CAp::Len(sv) ; i++)
|
|
CAp::SetErrorFlag(errorflag,sv[i]!=0.0,"testssaunit.ap:1410");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
windowwidth=-1;
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1418");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth2,"testssaunit.ap:1419");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth2,"testssaunit.ap:1420");
|
|
for(i=0; i<CAp::Len(trend) ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1422");
|
|
for(i=0; i<=CAp::Len(noise)-1; i++)
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1424");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAAnalyzeLast(state,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1432");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1433");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1438");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1439");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1451");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1452");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1457");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=x[i],"testssaunit.ap:1458");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1466");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1470");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
for(i=0; i<datalen ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1481");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1485");
|
|
}
|
|
}
|
|
//--- Test that for empty model with default algorithm and one/few tracks
|
|
//--- (which are sometimes shorter than window, sometimes longer than window)
|
|
//--- * SSAGetBasis() returns zero WINDOWx1 basis
|
|
//--- * SSAAnalyzeLastWindow() returns zeros as trend and correctly aligned
|
|
//--- cropped/padded copy of X in the noise
|
|
//--- * SSAAnalyzeLast() returns zeros as trend and correctly aligned
|
|
//--- cropped/padded copy of X in the noise
|
|
//--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
|
|
//--- * SSAForecastLast() returns zero trend
|
|
//--- * SSAForecastSequence() returns zero trend
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate task; last track is stored in X, its length in NLastTrackLen
|
|
nlasttracklen=-999999;
|
|
windowwidth2=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth2);
|
|
tracksmatrix=matrix<double>::Zeros(ntracks,2*windowwidth2);
|
|
trackssizes.Resize(ntracks);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
nlasttracklen=CHighQualityRand::HQRndUniformI(rs,2*windowwidth2);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(i=0; i<nlasttracklen; i++)
|
|
{
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
tracksmatrix.Set(k,i,x[i]);
|
|
}
|
|
trackssizes.Set(k,nlasttracklen);
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
//--- Test
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1532");
|
|
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1533");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1534");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1535");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1536");
|
|
for(i=0; i<CAp::Rows(a) ; i++)
|
|
{
|
|
for(j=0; j<CAp::Cols(a) ; j++)
|
|
CAp::SetErrorFlag(errorflag,a.Get(i,j)!=0.0,"testssaunit.ap:1539");
|
|
}
|
|
for(i=0; i<CAp::Len(sv) ; i++)
|
|
CAp::SetErrorFlag(errorflag,sv[i]!=0.0,"testssaunit.ap:1541");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
windowwidth=-1;
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1549");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth2,"testssaunit.ap:1550");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth2,"testssaunit.ap:1551");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1555");
|
|
for(i=0; i<=MathMax(windowwidth-nlasttracklen,0)-1; i++)
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1557");
|
|
for(i=MathMax(windowwidth-nlasttracklen,0); i<windowwidth ; i++)
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=x[nlasttracklen+(i-windowwidth)],"testssaunit.ap:1559");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1567");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1568");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nanalyzed ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1572");
|
|
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1574");
|
|
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=x[nlasttracklen+(i-nanalyzed)],"testssaunit.ap:1576");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1587");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1588");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1593");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=x[i],"testssaunit.ap:1594");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1602");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1606");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
for(i=0; i<datalen ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1617");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1621");
|
|
}
|
|
}
|
|
//--- Tests below are performed for all algorithms supported.
|
|
for(algotype=1; algotype<=3; algotype++)
|
|
{
|
|
//--- Test that SSAClearData() actually clears data
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
if(algotype==1)
|
|
continue;
|
|
//--- Create two models, one is created after cleardata() call
|
|
//--- which should erase all traces of the previous dataset.
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSACreate(state2);
|
|
CSSA::SSASetWindow(state,5);
|
|
CSSA::SSASetWindow(state2,5);
|
|
switch(algotype)
|
|
{
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,2);
|
|
CSSA::SSASetAlgoTopKDirect(state2,2);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,2);
|
|
CSSA::SSASetAlgoTopKRealtime(state2,2);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
x=vector<double>::Zeros(10);
|
|
for(i=0; i<CAp::Len(x) ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,CAp::Len(x));
|
|
CSSA::SSAClearData(state);
|
|
for(i=0; i<CAp::Len(x) ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,CAp::Len(x));
|
|
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
|
|
//--- Test
|
|
CSSA::SSAAnalyzeLast(state,CAp::Len(x),trend,noise);
|
|
CSSA::SSAAnalyzeLast(state2,CAp::Len(x),trend2,noise2);
|
|
for(i=0; i<CAp::Len(x) ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(1.0E-5),"testssaunit.ap:1675");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-noise2[i])>(1.0E-5),"testssaunit.ap:1676");
|
|
}
|
|
}
|
|
//--- Test that for model with some algo being set, one/few tracks and unit window length:
|
|
//--- * SSAGetBasis() returns 1x1 basis (unit matrix)
|
|
//--- * SSAAnalyzeLastWindow() returns last element of track as trend and zeros
|
|
//--- as noise (with minor rounding error)
|
|
//--- * SSAAnalyzeLast() returns tracks as trend and zeros as noise (with
|
|
//--- minor rounding error possible). For NTicks>TrackLength result is correctly
|
|
//--- prepended with zeros.
|
|
//--- * SSAAnalyzeSequence() returns sequence as trend, zeros as noise (up to
|
|
//--- machine precision)
|
|
//--- * SSAForecastLast() returns just copies of the last element
|
|
//--- * SSAForecastSequence() returns just copies of the last element
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate task; last track is stored in X, its length in NLastTrackLen
|
|
nlasttracklen=-999999;
|
|
maxtracklen=10;
|
|
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,1);
|
|
tracksmatrix=matrix<double>::Zeros(ntracks,maxtracklen);
|
|
trackssizes.Resize(ntracks);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
nlasttracklen=1+CHighQualityRand::HQRndUniformI(rs,maxtracklen);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(i=0; i<nlasttracklen; i++)
|
|
{
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
tracksmatrix.Set(k,i,x[i]);
|
|
}
|
|
trackssizes.Set(k,nlasttracklen);
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
b=matrix<double>::Zeros(1,1);
|
|
b.Set(0,0,2*CHighQualityRand::HQRndUniformI(rs,2)-1);
|
|
CSSA::SSASetAlgoPrecomputed(state,b,1,1);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,1);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,1);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Test
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1736");
|
|
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1737");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=1,"testssaunit.ap:1738");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1739");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1740");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(MathAbs(a.Get(0,0))-1)>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1741");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
windowwidth=-1;
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
|
|
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1749");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=1,"testssaunit.ap:1750");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=1,"testssaunit.ap:1751");
|
|
if(errorflag)
|
|
return;
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[nlasttracklen-1])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1754");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[0])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1755");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1763");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1764");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1769");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1770");
|
|
}
|
|
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i-nanalyzed+nlasttracklen])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1774");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1775");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x2=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x2.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAnalyzeSequence(state,x2,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1787");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1788");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1793");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1794");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1802");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=x[nlasttracklen-1],"testssaunit.ap:1806");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
for(i=0; i<datalen ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1817");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=x[datalen-1],"testssaunit.ap:1821");
|
|
}
|
|
}
|
|
//--- Test that for model with non-unit window, some algo being set to calculate ALL
|
|
//--- components, and one/few tracks which have at least window length ticks:
|
|
//--- * SSAGetBasis() returns full orthonormal basis
|
|
//--- * SSAAnalyzeLastWindow() returns last elements of track as trend and zeros
|
|
//--- as noise (with minor rounding error)
|
|
//--- * SSAAnalyzeLast() returns track as trend and zeros as noise (with
|
|
//--- minor rounding error possible). For NTicks>TrackLength result is correctly
|
|
//--- prepended with zeros.
|
|
//--- * SSAAnalyzeSequence() returns:
|
|
//--- * for sequences with length at least window width - sequence as trend,
|
|
//--- zeros as noise (up to machine precision)
|
|
//--- * for sequences shorter than window width - zeros as trend,
|
|
//--- sequence as noise (exactly)
|
|
//--- * SSAForecastLast() returns just copies of the last element
|
|
//--- * SSAForecastSequence() returns just copies of the last element for long enough
|
|
//--- sequences, zeros for shorter sequences
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate task; last track is stored in X, its length in NLastTrackLen
|
|
windowwidth=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
mintracklen=windowwidth;
|
|
maxtracklen=mintracklen+2*windowwidth;
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
tracksmatrix=matrix<double>::Zeros(ntracks,maxtracklen);
|
|
trackssizes.Resize(ntracks);
|
|
nlasttracklen=0;
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
nlasttracklen=mintracklen+CHighQualityRand::HQRndUniformI(rs,maxtracklen-mintracklen+1);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(i=0; i<nlasttracklen; i++)
|
|
{
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
tracksmatrix.Set(k,i,x[i]);
|
|
}
|
|
trackssizes.Set(k,nlasttracklen);
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,windowwidth);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,windowwidth);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,windowwidth);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Test
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1888");
|
|
CAp::SetErrorFlag(errorflag,nbasis!=windowwidth,"testssaunit.ap:1889");
|
|
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1890");
|
|
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=windowwidth,"testssaunit.ap:1891");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=windowwidth,"testssaunit.ap:1892");
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
for(j=0; j<windowwidth ; j++)
|
|
{
|
|
v=CAblasF::RDotRR(windowwidth,a,i,a,j);
|
|
if(i==j)
|
|
v-=1;
|
|
CAp::SetErrorFlag(errorflag,MathAbs(v)>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1899");
|
|
}
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
windowwidth2=-1;
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1908");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:1909");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:1910");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x[i-windowwidth+nlasttracklen]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1915");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1916");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,2*maxtracklen);
|
|
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1925");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1926");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1931");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1932");
|
|
}
|
|
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i-nanalyzed+nlasttracklen])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1936");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1937");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
x2=vector<double>::Zeros(nticks);
|
|
for(i=0; i<nticks ; i++)
|
|
x2.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAnalyzeSequence(state,x2,nticks,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1949");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1950");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
{
|
|
if(nticks>=windowwidth)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1957");
|
|
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1958");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1962");
|
|
CAp::SetErrorFlag(errorflag,(double)(MathAbs(noise[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1963");
|
|
}
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1972");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=x[nlasttracklen-1],"testssaunit.ap:1976");
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
if(windowwidth>2)
|
|
{
|
|
datalen=1+CHighQualityRand::HQRndUniformI(rs,windowwidth-1);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
for(i=0; i<datalen ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1990");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1994");
|
|
}
|
|
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,windowwidth);
|
|
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
x=vector<double>::Zeros(datalen);
|
|
for(i=0; i<datalen ; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:2005");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<forecastlen ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=x[datalen-1],"testssaunit.ap:2009");
|
|
}
|
|
}
|
|
//--- Test that for model with
|
|
//--- * strictly non-unit window
|
|
//--- * some algo being set to calculate one leading component
|
|
//--- * a few tracks which have at least window length ticks
|
|
//--- * and last track which has LESS than window length minus 1 ticks
|
|
//--- we have:
|
|
//--- * SSAAnalyzeLastWindow() returns zero trend and sequence as noise,
|
|
//--- correctly padded by zeros
|
|
//--- * SSAAnalyzeLast() returns zero trend and sequence as noise,
|
|
//--- correctly padded by zeros
|
|
//--- * SSAForecastLast() returns zero trend
|
|
//--- This test checks correct handling of the situatuon when last sequence
|
|
//--- stored in the dataset is too short.
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate task; last track is stored in X, its length in NLastTrackLen
|
|
windowwidth=3+CHighQualityRand::HQRndUniformI(rs,5);
|
|
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
mintracklen=windowwidth;
|
|
maxtracklen=mintracklen+2*windowwidth;
|
|
CSSA::SSACreate(state);
|
|
CSSA::SSASetWindow(state,windowwidth);
|
|
for(k=0; k<ntracks ; k++)
|
|
{
|
|
nlasttracklen=mintracklen+CHighQualityRand::HQRndUniformI(rs,maxtracklen-mintracklen+1);
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(i=0; i<nlasttracklen; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
}
|
|
nlasttracklen=windowwidth-2;
|
|
x=vector<double>::Zeros(nlasttracklen);
|
|
for(i=0; i<nlasttracklen; i++)
|
|
x.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CSSA::SSAAddSequence(state,x,nlasttracklen);
|
|
switch(algotype)
|
|
{
|
|
case 1:
|
|
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
|
|
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,1);
|
|
break;
|
|
case 2:
|
|
CSSA::SSASetAlgoTopKDirect(state,1);
|
|
break;
|
|
case 3:
|
|
CSSA::SSASetAlgoTopKRealtime(state,1);
|
|
break;
|
|
default:
|
|
CAp::Assert(false);
|
|
errorflag=true;
|
|
return;
|
|
}
|
|
//--- Test
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
windowwidth2=-1;
|
|
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth2);
|
|
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:2078");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:2079");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:2080");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<windowwidth ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2085");
|
|
j=nlasttracklen+(i-windowwidth);
|
|
if(j>=0)
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=x[j],"testssaunit.ap:2088");
|
|
else
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:2090");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
|
|
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:2099");
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:2100");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2105");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:2106");
|
|
}
|
|
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
|
|
{
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2110");
|
|
CAp::SetErrorFlag(errorflag,noise[i]!=(double)(x[nlasttracklen+(i-nanalyzed)]),"testssaunit.ap:2111");
|
|
}
|
|
}
|
|
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
|
|
{
|
|
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
CSSA::SSAForecastLast(state,nticks,trend);
|
|
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:2119");
|
|
if(errorflag)
|
|
return;
|
|
for(i=0; i<nticks ; i++)
|
|
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2123");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CTestKNNUnit
|
|
{
|
|
public:
|
|
static bool TestKNN(bool silent);
|
|
|
|
private:
|
|
static void TestKNNAlgo(bool&err);
|
|
static void UnsetKNN(CKNNModel &model);
|
|
static void CTestKNNUnit::TestSetErrors(CKNNModel &model,int nvars,int nout,bool iscls,CMatrixDouble &xy,int npoints,double &avgce,double &relcls0,double &relcls1,double &rms,double &avg,double &avgrel);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestKNNUnit::TestKNN(bool silent)
|
|
{
|
|
bool result;
|
|
bool knnerrors=false;
|
|
bool waserrors=false;
|
|
//--- Tests
|
|
TestKNNAlgo(knnerrors);
|
|
//--- Final report
|
|
waserrors=knnerrors;
|
|
if(!silent)
|
|
{
|
|
Print("KNN CLASSIFIER/REGRESSION TEST");
|
|
PrintResult("TOTAL RESULTS",!waserrors);
|
|
PrintResult("* KNN (AKNN) ALGORITHM",!knnerrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
void CTestKNNUnit::TestKNNAlgo(bool&err)
|
|
{
|
|
//--- create variables
|
|
int nvars=0;
|
|
int nout=0;
|
|
bool iscls;
|
|
int ny=0;
|
|
int k=0;
|
|
double eps=0;
|
|
CKNNModel model1;
|
|
CKNNModel model2;
|
|
CKNNModel modelus;
|
|
CKNNBuilder builder;
|
|
CKNNReport rep;
|
|
CKNNReport rep2;
|
|
CKNNReport tstrep;
|
|
CKNNBuffer buf;
|
|
CMatrixDouble xy;
|
|
int npoints=0;
|
|
CMatrixDouble testxy;
|
|
int testnpoints=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool allsame;
|
|
CRowDouble x1;
|
|
CRowDouble x2;
|
|
CRowDouble y1;
|
|
CRowDouble y2;
|
|
CRowDouble y3;
|
|
double v=0;
|
|
double refavgce=0;
|
|
double refcls0=0;
|
|
double refcls1=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double maxerr=0;
|
|
double avgerr=0;
|
|
int cnt=0;
|
|
CHighQualityRandState rs;
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Test on randomly generated sets with randomly generated algo settings
|
|
//--- We test:
|
|
//--- * process
|
|
//--- * processi
|
|
//--- * process0
|
|
//--- * classify
|
|
//--- * serialization/unserialization
|
|
//--- * reports and test set error evaluation
|
|
//--- * KNNRewriteKEps()
|
|
for(pass=1; pass<=100; pass++)
|
|
{
|
|
//--- initialize parameters
|
|
nvars=1+CHighQualityRand::HQRndUniformI(rs,5);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
iscls=true;
|
|
nout=2+CHighQualityRand::HQRndUniformI(rs,3);
|
|
ny=1;
|
|
}
|
|
else
|
|
{
|
|
iscls=false;
|
|
nout=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
ny=nout;
|
|
}
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
eps=CHighQualityRand::HQRndUniformI(rs,2)*0.1*CHighQualityRand::HQRndUniformR(rs);
|
|
//--- Initialize arrays and data: training and test sets
|
|
npoints=10+CHighQualityRand::HQRndUniformI(rs,100);
|
|
xy=matrix<double>::Zeros(npoints,nvars+ny);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
{
|
|
if(j%2==0)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
else
|
|
xy.Set(i,j,CHighQualityRand::HQRndUniformI(rs,2));
|
|
}
|
|
if(iscls)
|
|
xy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
|
|
else
|
|
for(j=0; j<nout; j++)
|
|
xy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
testnpoints=10+CHighQualityRand::HQRndUniformI(rs,100);
|
|
testxy=matrix<double>::Zeros(testnpoints,nvars+ny);
|
|
for(i=0; i<testnpoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
{
|
|
if(j%2==0)
|
|
testxy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
else
|
|
testxy.Set(i,j,CHighQualityRand::HQRndUniformI(rs,2));
|
|
}
|
|
if(iscls)
|
|
testxy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
|
|
else
|
|
for(j=0; j<nout; j++)
|
|
testxy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- create model
|
|
UnsetKNN(model1);
|
|
CKNN::KNNBuilderCreate(builder);
|
|
if(iscls)
|
|
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,nvars,nout);
|
|
else
|
|
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,nvars,nout);
|
|
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
|
|
CKNN::KNNBuilderBuildKNNModel(builder,k,eps,model1,rep);
|
|
CKNN::KNNCreateBuffer(model1,buf);
|
|
//--- Check that:
|
|
//--- * KNNProcess() does not shrink output array, only increases
|
|
//--- * KNNTsProcess() does not shrink output array, only increases
|
|
//--- * KNNProcessI() fits length exactly to NOut
|
|
x1=vector<double>::Zeros(nvars);
|
|
for(i=0; i<nvars ; i++)
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
y1=vector<double>::Zeros(nout+1);
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout+1,"testknnunit.ap:200");
|
|
y1.Resize(nout-1);
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:203");
|
|
y1=vector<double>::Zeros(nout+1);
|
|
CKNN::KNNTsProcess(model1,buf,x1,y1);
|
|
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout+1,"testknnunit.ap:207");
|
|
y1.Resize(nout-1);
|
|
CKNN::KNNTsProcess(model1,buf,x1,y1);
|
|
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:210");
|
|
y1=vector<double>::Zeros(nout+1);
|
|
CKNN::KNNProcessI(model1,x1,y1);
|
|
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:214");
|
|
y1.Resize(nout-1);
|
|
CKNN::KNNProcessI(model1,x1,y1);
|
|
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:217");
|
|
if(err)
|
|
return;
|
|
//--- KNNProcess(), KNNProcessI() and KNNTsProcess() return same results
|
|
x1=vector<double>::Zeros(nvars);
|
|
for(i=0; i<nvars ; i++)
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CKNN::KNNProcessI(model1,x1,y2);
|
|
CKNN::KNNTsProcess(model1,buf,x1,y3);
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(y2[i]-y1[i])>(100.0*CMath::m_machineepsilon),"testknnunit.ap:232");
|
|
CAp::SetErrorFlag(err,MathAbs(y3[i]-y1[i])>(100.0*CMath::m_machineepsilon),"testknnunit.ap:233");
|
|
}
|
|
//--- Same inputs leads to same outputs
|
|
x1=vector<double>::Zeros(nvars);
|
|
x2=vector<double>::Zeros(nvars);
|
|
y1=vector<double>::Zeros(nout);
|
|
y2=vector<double>::Zeros(nout);
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
y2.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CKNN::KNNProcess(model1,x2,y2);
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
CAp::SetErrorFlag(err,!allsame,"testknnunit.ap:259");
|
|
//--- Same inputs on original forest leads to same outputs
|
|
//--- on copy created using KNNSerialize
|
|
x1=vector<double>::Zeros(nvars);
|
|
x2=vector<double>::Zeros(nvars);
|
|
y1=vector<double>::Zeros(nout);
|
|
y2=vector<double>::Zeros(nout);
|
|
UnsetKNN(modelus);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
_local_serializer.Alloc_Start();
|
|
CKNN::KNNAlloc(_local_serializer,model1);
|
|
_local_serializer.Alloc_Entry();
|
|
_local_serializer.SStart_Str();
|
|
CKNN::KNNSerialize(_local_serializer,model1);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//---
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CKNN::KNNUnserialize(_local_serializer,modelus);
|
|
_local_serializer.Stop();
|
|
}
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
y2.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CKNN::KNNProcess(modelus,x2,y2);
|
|
allsame=true;
|
|
for(i=0; i<nout; i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
CAp::SetErrorFlag(err,!allsame,"testknnunit.ap:287");
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNProcess0(model1,x1)-CKNN::KNNProcess0(modelus,x2))>(100.0*CMath::m_machineepsilon),"testknnunit.ap:293");
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
CAp::SetErrorFlag(err,CKNN::KNNClassify(model1,x1)!=CKNN::KNNClassify(modelus,x2),"testknnunit.ap:299");
|
|
//--- KNNProcess0 works as expected
|
|
x1=vector<double>::Zeros(nvars);
|
|
x2=vector<double>::Zeros(nvars);
|
|
y1=vector<double>::Zeros(nout);
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
for(i=0; i<nout; i++)
|
|
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CAp::SetErrorFlag(err,MathAbs(y1[0]-CKNN::KNNProcess0(model1,x2))>(100.0*CMath::m_machineepsilon),"testknnunit.ap:318");
|
|
//--- KNNClassify works as expected
|
|
x1=vector<double>::Zeros(nvars);
|
|
x2=vector<double>::Zeros(nvars);
|
|
y1=vector<double>::Zeros(nout);
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
if(iscls)
|
|
{
|
|
for(i=0; i<nout; i++)
|
|
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
j=CKNN::KNNClassify(model1,x2);
|
|
for(i=0; i<nout; i++)
|
|
CAp::SetErrorFlag(err,y1[i]>y1[j],"testknnunit.ap:341");
|
|
}
|
|
else
|
|
CAp::SetErrorFlag(err,CKNN::KNNClassify(model1,x2)!=-1,"testknnunit.ap:344");
|
|
//--- Normalization properties
|
|
if(iscls)
|
|
{
|
|
for(i=0; i<nvars ; i++)
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
v=0;
|
|
for(i=0; i<nout; i++)
|
|
{
|
|
v+=y1[i];
|
|
CAp::SetErrorFlag(err,y1[i]<0.0,"testknnunit.ap:359");
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(v-1)>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:361");
|
|
}
|
|
//--- Test various error metrics reported by algorithm:
|
|
//--- * training set errors included in the report
|
|
//--- * errors on the test set
|
|
//--- NOTE: cross-entropy metric is tested only for training set.
|
|
//--- because model can produce exactly zero probabilities
|
|
//--- for completely unknown values
|
|
TestSetErrors(model1,nvars,nout,iscls,xy,npoints,refavgce,refcls0,refcls1,refrms,refavg,refavgrel);
|
|
if(iscls)
|
|
{
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_AvgCE-refavgce)>1.0E-6,"testknnunit.ap:377");
|
|
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgCE(model1,xy,npoints)-refavgce)>1.0E-6,"testknnunit.ap:378");
|
|
CAp::SetErrorFlag(err,rep.m_RelCLSError<(refcls0-1.0E-6),"testknnunit.ap:381");
|
|
CAp::SetErrorFlag(err,rep.m_RelCLSError>(refcls1+1.0E-6),"testknnunit.ap:382");
|
|
CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)<(refcls0-1.0E-6),"testknnunit.ap:383");
|
|
CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)>(refcls1+1.0E-6),"testknnunit.ap:384");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(err,rep.m_AvgCE!=0.0,"testknnunit.ap:389");
|
|
CAp::SetErrorFlag(err,CKNN::KNNAvgCE(model1,xy,npoints)!=0.0,"testknnunit.ap:390");
|
|
CAp::SetErrorFlag(err,rep.m_RelCLSError!=0.0,"testknnunit.ap:393");
|
|
CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)!=0.0,"testknnunit.ap:394");
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_RMSError-refrms)>1.0E-6,"testknnunit.ap:396");
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_AvgError-refavg)>1.0E-6,"testknnunit.ap:397");
|
|
CAp::SetErrorFlag(err,MathAbs(rep.m_AvgRelError-refavgrel)>1.0E-6,"testknnunit.ap:398");
|
|
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNRMSError(model1,xy,npoints)-refrms)>1.0E-6,"testknnunit.ap:399");
|
|
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgError(model1,xy,npoints)-refavg)>1.0E-6,"testknnunit.ap:400");
|
|
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgRelError(model1,xy,npoints)-refavgrel)>1.0E-6,"testknnunit.ap:401");
|
|
TestSetErrors(model1,nvars,nout,iscls,testxy,testnpoints,refavgce,refcls0,refcls1,refrms,refavg,refavgrel);
|
|
CKNN::KNNAllErrors(model1,testxy,testnpoints,tstrep);
|
|
if(iscls)
|
|
{
|
|
CAp::SetErrorFlag(err,tstrep.m_RelCLSError<(refcls0-1.0E-6),"testknnunit.ap:406");
|
|
CAp::SetErrorFlag(err,tstrep.m_RelCLSError>(refcls1+1.0E-6),"testknnunit.ap:407");
|
|
}
|
|
else
|
|
{
|
|
CAp::SetErrorFlag(err,tstrep.m_AvgCE!=0.0,"testknnunit.ap:411");
|
|
CAp::SetErrorFlag(err,tstrep.m_RelCLSError!=0.0,"testknnunit.ap:412");
|
|
}
|
|
CAp::SetErrorFlag(err,MathAbs(tstrep.m_RMSError-refrms)>1.0E-6,"testknnunit.ap:414");
|
|
CAp::SetErrorFlag(err,MathAbs(tstrep.m_AvgError-refavg)>1.0E-6,"testknnunit.ap:415");
|
|
CAp::SetErrorFlag(err,MathAbs(tstrep.m_AvgRelError-refavgrel)>1.0E-6,"testknnunit.ap:416");
|
|
}
|
|
//--- Test that model built with K=1 and Eps=0 remembers all training cases
|
|
for(nvars=1; nvars<=4; nvars++)
|
|
{
|
|
for(pass=0; pass<=10; pass++)
|
|
{
|
|
//--- Prepare task
|
|
npoints=1+CHighQualityRand::HQRndUniformI(rs,20);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
iscls=true;
|
|
nout=2+CHighQualityRand::HQRndUniformI(rs,3);
|
|
ny=1;
|
|
}
|
|
else
|
|
{
|
|
iscls=false;
|
|
nout=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
ny=nout;
|
|
}
|
|
xy=matrix<double>::Zeros(npoints,nvars+ny);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
xy.Set(i,0,i*0.000001);
|
|
for(j=1; j<nvars ; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
if(iscls)
|
|
xy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
|
|
else
|
|
for(j=0; j<nout; j++)
|
|
xy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Create model
|
|
UnsetKNN(model1);
|
|
CKNN::KNNBuilderCreate(builder);
|
|
if(iscls)
|
|
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,nvars,nout);
|
|
else
|
|
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,nvars,nout);
|
|
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
|
|
CKNN::KNNBuilderBuildKNNModel(builder,1,0.0,model1,rep);
|
|
CAp::SetErrorFlag(err,rep.m_AvgCE>0.0,"testknnunit.ap:467");
|
|
CAp::SetErrorFlag(err,rep.m_RelCLSError>0.0,"testknnunit.ap:468");
|
|
CAp::SetErrorFlag(err,rep.m_RMSError>1.0E-6,"testknnunit.ap:469");
|
|
CAp::SetErrorFlag(err,rep.m_AvgError>1.0E-6,"testknnunit.ap:470");
|
|
CAp::SetErrorFlag(err,rep.m_AvgRelError>1.0E-6,"testknnunit.ap:471");
|
|
x1=vector<double>::Zeros(nvars);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
x1.Set(j,xy.Get(i,j));
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
if(iscls)
|
|
CAp::SetErrorFlag(err,MathAbs(y1[(int)MathRound(xy.Get(i,nvars))]-1.0)>(10.0*CMath::m_machineepsilon),"testknnunit.ap:479");
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
CAp::SetErrorFlag(err,MathAbs(y1[j]-xy.Get(i,nvars+j))>(10.0*CMath::m_machineepsilon),"testknnunit.ap:483");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test that model rewrite works as expected
|
|
for(nvars=1; nvars<=20; nvars++)
|
|
{
|
|
//--- Prepare task
|
|
npoints=50+5*nvars;
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
iscls=true;
|
|
nout=2+CHighQualityRand::HQRndUniformI(rs,3);
|
|
ny=1;
|
|
}
|
|
else
|
|
{
|
|
iscls=false;
|
|
nout=1+CHighQualityRand::HQRndUniformI(rs,3);
|
|
ny=nout;
|
|
}
|
|
xy=matrix<double>::Zeros(npoints,nvars+ny);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
if(iscls)
|
|
xy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
|
|
else
|
|
for(j=0; j<nout; j++)
|
|
xy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Select K, Eps
|
|
k=1+CHighQualityRand::HQRndUniformI(rs,10);
|
|
eps=CHighQualityRand::HQRndUniformI(rs,2)*(1+3*CHighQualityRand::HQRndUniformR(rs));
|
|
//--- Create model 1 using K, EPS.
|
|
//--- Create model 2 using other (K,Eps), then rewrite search settings
|
|
CKNN::KNNBuilderCreate(builder);
|
|
if(iscls)
|
|
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,nvars,nout);
|
|
else
|
|
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,nvars,nout);
|
|
UnsetKNN(model1);
|
|
CKNN::KNNBuilderBuildKNNModel(builder,k,eps,model1,rep);
|
|
UnsetKNN(model2);
|
|
CKNN::KNNBuilderBuildKNNModel(builder,1+CHighQualityRand::HQRndUniformI(rs,10),CHighQualityRand::HQRndUniformI(rs,2)*(1+3*CHighQualityRand::HQRndUniformR(rs)),model2,rep2);
|
|
CKNN::KNNRewriteKEps(model2,k,eps);
|
|
x1=vector<double>::Zeros(nvars);
|
|
x2=vector<double>::Zeros(nvars);
|
|
for(i=0; i<nvars ; i++)
|
|
{
|
|
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
|
|
x2.Set(i,x1[i]);
|
|
}
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CKNN::KNNProcess(model2,x2,y2);
|
|
for(i=0; i<nout; i++)
|
|
CAp::SetErrorFlag(err,MathAbs(y1[i]-y2[i])>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:553");
|
|
}
|
|
//--- Test generalization ability on a simple noisy classification task:
|
|
//--- * 0<x<1 - P(class=0)=1
|
|
//--- * 1<x<2 - P(class=0)=2-x
|
|
//--- * 2<x<3 - P(class=0)=0
|
|
//--- An algorithm with K=100 and Eps={0,0.1} is tested
|
|
for(pass=0; pass<=1; pass++)
|
|
{
|
|
//--- Prepare task
|
|
npoints=3000;
|
|
xy=matrix<double>::Zeros(npoints,2);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
xy.Set(i,0,3*CHighQualityRand::HQRndUniformR(rs));
|
|
if(xy.Get(i,0)<=1.0)
|
|
xy.Set(i,1,0);
|
|
else
|
|
{
|
|
if(xy.Get(i,0)<=2.0)
|
|
{
|
|
if(CHighQualityRand::HQRndUniformR(rs)<(xy.Get(i,0)-1.0))
|
|
xy.Set(i,1,1.0);
|
|
else
|
|
xy.Set(i,1,0);
|
|
}
|
|
else
|
|
xy.Set(i,1,1);
|
|
}
|
|
}
|
|
//--- Select K and Eps, create model
|
|
k=100;
|
|
eps=0.1*(pass%2);
|
|
UnsetKNN(model1);
|
|
CKNN::KNNBuilderCreate(builder);
|
|
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,1,2);
|
|
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
|
|
CKNN::KNNBuilderBuildKNNModel(builder,k,eps,model1,rep);
|
|
//--- Check model quality
|
|
x1=vector<double>::Zeros(1);
|
|
while(x1[0]<=3.0)
|
|
{
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
CAp::SetErrorFlag(err,y1[0]<0.0,"testknnunit.ap:606");
|
|
CAp::SetErrorFlag(err,y1[1]<0.0,"testknnunit.ap:607");
|
|
CAp::SetErrorFlag(err,MathAbs(y1[0]+y1[1]-1)>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:608");
|
|
if(x1[0]<1.0)
|
|
CAp::SetErrorFlag(err,y1[0]<0.8,"testknnunit.ap:610");
|
|
if(x1[0]>=1.0 && x1[0]<=2.0)
|
|
CAp::SetErrorFlag(err,MathAbs(y1[1]-(x1[0]-1))>0.5,"testknnunit.ap:612");
|
|
if(x1[0]>2.0)
|
|
CAp::SetErrorFlag(err,y1[1]<0.8,"testknnunit.ap:614");
|
|
x1.Add(0,0.01);
|
|
}
|
|
}
|
|
//--- Test simple regression task without noise:
|
|
//--- * |x|<1, |y|<1
|
|
//--- * F(x,y) = x^2+y
|
|
//--- A model with K=50 is used, we check that
|
|
//--- * Eps=0 is good enough
|
|
//--- * having Eps>0 results in worse error metrics, but not too bad
|
|
//--- Prepare task
|
|
npoints=5000;
|
|
xy=matrix<double>::Zeros(npoints,3);
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
xy.Set(i,0,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
xy.Set(i,1,2*CHighQualityRand::HQRndUniformR(rs)-1);
|
|
xy.Set(i,2,CMath::Sqr(xy.Get(i,0))+xy.Get(i,1));
|
|
}
|
|
//--- Build model with Eps=0, check model quality
|
|
k=50;
|
|
UnsetKNN(model1);
|
|
CKNN::KNNBuilderCreate(builder);
|
|
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,2,1);
|
|
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
|
|
CKNN::KNNBuilderBuildKNNModel(builder,k,0.0,model1,rep);
|
|
x1=vector<double>::Zeros(2);
|
|
maxerr=0;
|
|
avgerr=0;
|
|
cnt=0;
|
|
for(i=0; i<=1000; i++)
|
|
{
|
|
//--- NOTE: we test model in the inner points, it deteriorates near the bounds
|
|
x1.Set(0,0.8*(2*CHighQualityRand::HQRndUniformR(rs)-1));
|
|
x1.Set(1,0.8*(2*CHighQualityRand::HQRndUniformR(rs)-1));
|
|
v=CMath::Sqr(x1[0])+x1[1];
|
|
CKNN::KNNProcess(model1,x1,y1);
|
|
v=MathAbs(y1[0]-v);
|
|
maxerr=MathMax(maxerr,v);
|
|
avgerr+=v;
|
|
cnt++;
|
|
}
|
|
avgerr/=cnt;
|
|
CAp::SetErrorFlag(err,maxerr>0.15,"testknnunit.ap:669");
|
|
CAp::SetErrorFlag(err,avgerr>0.05,"testknnunit.ap:670");
|
|
//--- Build model with Eps=1.0, compare error metrics with Eps=0
|
|
CKNN::KNNBuilderBuildKNNModel(builder,k,1.0,model2,rep2);
|
|
CAp::SetErrorFlag(err,rep2.m_RMSError<=rep.m_RMSError,"testknnunit.ap:676");
|
|
CAp::SetErrorFlag(err,rep2.m_RMSError<=(rep.m_RMSError+0.001),"testknnunit.ap:677");
|
|
CAp::SetErrorFlag(err,rep2.m_RMSError>=(rep.m_RMSError+0.020),"testknnunit.ap:678");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets model |
|
|
//+------------------------------------------------------------------+
|
|
void CTestKNNUnit::UnsetKNN(CKNNModel &model)
|
|
{
|
|
//--- create variables
|
|
CMatrixDouble xy;
|
|
CKNNBuilder builder;
|
|
CKNNReport rep;
|
|
|
|
xy=matrix<double>::Zeros(1,2);
|
|
xy.Set(0,1,CMath::RandomReal()-0.5);
|
|
CKNN::KNNBuilderCreate(builder);
|
|
CKNN::KNNBuilderSetDatasetReg(builder,xy,1,1,1);
|
|
CKNN::KNNBuilderBuildKNNModel(builder,1,0,model,rep);
|
|
model.m_nvars=-1;
|
|
model.m_nout=-1;
|
|
model.m_k=-1;
|
|
model.m_eps=0;
|
|
model.m_isdummy=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test set errors. |
|
|
//| Computes test set errors: |
|
|
//| * average cross-entropy |
|
|
//| * relative classification error (a range [RelCls0,RelCls1] is |
|
|
//| returned because classification error can be computed |
|
|
//| differently in the presence of the ties). All possible values|
|
|
//| of the classification error, no matter how ties are resolved,|
|
|
//| are guaranteed to be within [RelCls0,RelCls1]. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestKNNUnit::TestSetErrors(CKNNModel &model,int nvars,
|
|
int nout,bool iscls,
|
|
CMatrixDouble &xy,int npoints,
|
|
double &avgce,double &relcls0,
|
|
double &relcls1,double &rms,
|
|
double &avg,double &avgrel)
|
|
{
|
|
//--- create variables
|
|
CRowDouble x;
|
|
CRowDouble y;
|
|
CRowDouble ey;
|
|
int relcnt=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double mxy=0;
|
|
|
|
avgce=0;
|
|
relcls0=0;
|
|
relcls1=0;
|
|
rms=0;
|
|
avg=0;
|
|
avgrel=0;
|
|
x=vector<double>::Zeros(nvars);
|
|
y=vector<double>::Zeros(nout);
|
|
ey=vector<double>::Zeros(nout);
|
|
avgce=0;
|
|
relcls0=0;
|
|
relcls1=0;
|
|
rms=0;
|
|
avg=0;
|
|
avgrel=0;
|
|
|
|
for(i=0; i<npoints ; i++)
|
|
{
|
|
for(j=0; j<nvars ; j++)
|
|
x.Set(j,xy.Get(i,j));
|
|
CKNN::KNNProcess(model,x,y);
|
|
if(iscls)
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
ey.Set(j,0);
|
|
ey.Set((int)MathRound(xy.Get(i,nvars)),1);
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<nout; j++)
|
|
ey.Set(j,xy.Get(i,nvars+j));
|
|
}
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
v=y[j]-ey[j];
|
|
if(iscls)
|
|
avgce-=ey[j]*MathLog(y[j]+CMath::m_minrealnumber);
|
|
rms+=CMath::Sqr(v);
|
|
avg+=MathAbs(v);
|
|
if(ey[j]!=0.0)
|
|
{
|
|
avgrel+=MathAbs(v/ey[j]);
|
|
relcnt ++;
|
|
}
|
|
}
|
|
if(iscls)
|
|
{
|
|
mxy=0;
|
|
for(j=0; j<nout; j++)
|
|
mxy=MathMax(mxy,y[j]);
|
|
if(y[(int)MathRound(xy.Get(i,nvars))]==mxy)
|
|
{
|
|
//--- RelCls0 is NOT increased because correct value achieves maximum.
|
|
//--- However, if ties are present, we have to increase RelCls1, upper bound
|
|
//--- of the uncertainty interval for the classification error.
|
|
for(j=0; j<nout; j++)
|
|
{
|
|
if(j!=(int)MathRound(xy.Get(i,nvars)) && y[j]==mxy)
|
|
{
|
|
relcls1=relcls1+1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Both bounds of the error range are increased by 1
|
|
relcls0++;
|
|
relcls1++;
|
|
}
|
|
}
|
|
}
|
|
relcls0/=npoints;
|
|
relcls1/=npoints;
|
|
avgce/=npoints;
|
|
rms=MathSqrt(rms/(npoints*nout));
|
|
avg/=(npoints*nout);
|
|
avgrel/=CApServ::Coalesce(relcnt,1.0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing clustering |
|
|
//+------------------------------------------------------------------+
|
|
class CTestClusteringUnit
|
|
{
|
|
public:
|
|
static bool TestClustering(bool silent);
|
|
|
|
private:
|
|
static bool BasicAHCTests(void);
|
|
static bool AdvancedAHCTests(void);
|
|
static void KMeansSimpleTest1(int nvars,int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors);
|
|
static void KMeansSpecialTests(bool &othererrors);
|
|
static void KMeansInfiniteLoopTest(bool &othererrors);
|
|
static void KMeansRestartsTest(bool &converrors,bool &restartserrors);
|
|
static double RNormal(void);
|
|
static void RSphere(CMatrixDouble &xy,int n,int i);
|
|
static double DistFunc(CRowDouble &x0,CRowDouble &x1,int d,int disttype);
|
|
static bool ErrorsInMerges(CMatrixDouble &d,CMatrixDouble &xy,int npoints,int nf,CAHCReport &rep,int ahcalgo);
|
|
static void KMeansReferenceUpdateDistances(CMatrixDouble &xy,int npoints,int nvars,CMatrixDouble &ct,int k,CRowInt &xyc,CRowDouble &xydist2);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing clustering |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestClusteringUnit::TestClustering(bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool waserrors;
|
|
bool basicahcerrors;
|
|
bool ahcerrors;
|
|
bool kmeansconverrors;
|
|
bool kmeanssimpleerrors;
|
|
bool kmeansothererrors;
|
|
bool kmeansrestartserrors;
|
|
int passcount=0;
|
|
int nf=0;
|
|
int nc=0;
|
|
//--- AHC tests
|
|
basicahcerrors=BasicAHCTests();
|
|
ahcerrors=AdvancedAHCTests();
|
|
//
|
|
//--- k-means tests
|
|
//
|
|
passcount=10;
|
|
kmeansconverrors=false;
|
|
kmeansothererrors=false;
|
|
kmeanssimpleerrors=false;
|
|
kmeansrestartserrors=false;
|
|
KMeansSpecialTests(kmeansothererrors);
|
|
KMeansInfiniteLoopTest(kmeansothererrors);
|
|
KMeansRestartsTest(kmeansconverrors,kmeansrestartserrors);
|
|
for(nf=1; nf<=5; nf++)
|
|
for(nc=1; nc<=5; nc++)
|
|
KMeansSimpleTest1(nf,nc,passcount,kmeansconverrors,kmeansothererrors,kmeanssimpleerrors);
|
|
//--- Results
|
|
waserrors=false;
|
|
waserrors=waserrors||(basicahcerrors||ahcerrors);
|
|
waserrors=waserrors||(kmeansconverrors||kmeansothererrors||kmeanssimpleerrors||kmeansrestartserrors);
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CLUSTERING");
|
|
Print("AHC:");
|
|
PrintResult("* BASIC TESTS",!basicahcerrors);
|
|
PrintResult("* GENERAL TESTS",!ahcerrors);
|
|
Print("K-MEANS:");
|
|
PrintResult("* CONVERGENCE",!kmeansconverrors);
|
|
PrintResult("* SIMPLE TASKS",!kmeanssimpleerrors);
|
|
PrintResult("* OTHER PROPERTIES",!kmeansothererrors);
|
|
PrintResult("* RESTARTS PROPERTIES",!kmeansrestartserrors);
|
|
PrintResult("TEST SUMMARY",!waserrors);
|
|
Print("\n");
|
|
}
|
|
//--- return result
|
|
result=!waserrors;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic agglomerative hierarchical clustering tests: |
|
|
//| returns True on failure, False on success. |
|
|
//| Basic tests study algorithm behavior on simple, hand-made |
|
|
//| datasets with small number of points (1..10). |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestClusteringUnit::BasicAHCTests(void)
|
|
{
|
|
//--- create variables
|
|
bool result=true;
|
|
CClusterizerState s;
|
|
CAHCReport rep;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble d;
|
|
CMatrixDouble c;
|
|
bool berr;
|
|
int ahcalgo=0;
|
|
int i=0;
|
|
int j=0;
|
|
int npoints=0;
|
|
int k=0;
|
|
CRowInt cidx;
|
|
CRowInt cz;
|
|
CRowInt cidx2;
|
|
CRowInt cz2;
|
|
//--- Test on empty problem
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=0)
|
|
return(result);
|
|
//--- Test on problem with one point
|
|
xy=matrix<double>::Zeros(1,2);
|
|
xy.Set(0,0,CMath::RandomReal());
|
|
xy.Set(0,1,CMath::RandomReal());
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,1,2,0);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=1)
|
|
return(result);
|
|
//--- Test on problem with two points
|
|
xy=matrix<double>::Zeros(2,2);
|
|
xy.Set(0,0,CMath::RandomReal());
|
|
xy.Set(0,1,CMath::RandomReal());
|
|
xy.Set(1,0,CMath::RandomReal());
|
|
xy.Set(1,1,CMath::RandomReal());
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,2,2,0);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if((rep.m_NPoints!=2 || CAp::Rows(rep.m_z)!=1) || CAp::Cols(rep.m_z)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1)
|
|
return(result);
|
|
//--- Test on specially designed problem which should have
|
|
//--- following dendrogram:
|
|
//--- ------
|
|
//--- | |
|
|
//--- ---- ----
|
|
//--- | | | |
|
|
//--- 0 1 2 3
|
|
//--- ...with first merge performed on 0 and 1, second merge
|
|
//--- performed on 2 and 3. Complete linkage is used.
|
|
//--- Additionally we test ClusterizerSeparatedByDist() on this
|
|
//--- problem for different distances. Test is performed by
|
|
//--- comparing function result with ClusterizerGetKClusters()
|
|
//--- for known K.
|
|
xy=matrix<double>::Zeros(4,1);
|
|
xy.Set(1,0,1.0);
|
|
xy.Set(2,0,3.0);
|
|
xy.Set(3,0,4.1);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,4,1,0);
|
|
CClustering::ClusterizerSetAHCAlgo(s,0);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=4 || CAp::Rows(rep.m_z)!=3 || CAp::Cols(rep.m_z)!=2 || CAp::Rows(rep.m_pz)!=3 || CAp::Cols(rep.m_pz)!=2)
|
|
return(result);
|
|
|
|
berr=false;
|
|
berr=(berr||rep.m_z.Get(0,0)!=0||rep.m_z.Get(0,1)!=1);
|
|
berr=(berr||rep.m_z.Get(1,0)!=2||rep.m_z.Get(1,1)!=3);
|
|
berr=(berr||rep.m_z.Get(2,0)!=4||rep.m_z.Get(2,1)!=5);
|
|
berr=(berr||rep.m_p[0]!=0||rep.m_p[1]!=1||rep.m_p[2]!=2||rep.m_p[3]!=3);
|
|
berr=(berr||rep.m_pz.Get(0,0)!=0||rep.m_pz.Get(0,1)!=1);
|
|
berr=(berr||rep.m_pz.Get(1,0)!=2||rep.m_pz.Get(1,1)!=3);
|
|
berr=(berr||rep.m_pz.Get(2,0)!=4||rep.m_pz.Get(2,1)!=5);
|
|
berr=(berr||rep.m_pm.Get(0,0)!=0||rep.m_pm.Get(0,1)!=0||rep.m_pm.Get(0,2)!=1||rep.m_pm.Get(0,3)!=1);
|
|
berr=(berr||rep.m_pm.Get(1,0)!=2||rep.m_pm.Get(1,1)!=2||rep.m_pm.Get(1,2)!=3||rep.m_pm.Get(1,3)!=3);
|
|
berr=(berr||rep.m_pm.Get(2,0)!=0||rep.m_pm.Get(2,1)!=1||rep.m_pm.Get(2,2)!=2||rep.m_pm.Get(2,3)!=3);
|
|
if(berr)
|
|
return(result);
|
|
|
|
CClustering::ClusterizerSeparatedByDist(rep,0.5,k,cidx,cz);
|
|
CClustering::ClusterizerGetKClusters(rep,4,cidx2,cz2);
|
|
if(k!=4)
|
|
return(result);
|
|
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
|
|
return(result);
|
|
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2] || cz[3]!=cz2[3])
|
|
return(result);
|
|
CClustering::ClusterizerSeparatedByDist(rep,1.05,k,cidx,cz);
|
|
CClustering::ClusterizerGetKClusters(rep,3,cidx2,cz2);
|
|
if(k!=3)
|
|
return(result);
|
|
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
|
|
return(result);
|
|
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2])
|
|
return(result);
|
|
CClustering::ClusterizerSeparatedByDist(rep,1.15,k,cidx,cz);
|
|
CClustering::ClusterizerGetKClusters(rep,2,cidx2,cz2);
|
|
if(k!=2)
|
|
return(result);
|
|
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
|
|
return(result);
|
|
if(cz[0]!=cz2[0] || cz[1]!=cz2[1])
|
|
return(result);
|
|
//--- Test on specially designed problem with Pearson distance
|
|
//--- which should have following dendrogram:
|
|
//--- ------
|
|
//--- | |
|
|
//--- ---- ----
|
|
//--- | | | |
|
|
//--- 0 1 2 3
|
|
//--- This problem is used to test ClusterizerSeparatedByDist().
|
|
//--- The test is performed by comparing function result with
|
|
//--- ClusterizerGetKClusters() for known K.
|
|
//--- NOTE:
|
|
//--- * corr(a0,a1) = 0.866
|
|
//--- * corr(a2,a3) = 0.990
|
|
//--- * corr(a0/a1, a2/a3)<=0.5
|
|
xy=matrix<double>::Zeros(4,3);
|
|
xy.Set(0,0,0.3);
|
|
xy.Set(0,1,0.5);
|
|
xy.Set(0,2,0.3);
|
|
xy.Set(1,0,0.3);
|
|
xy.Set(1,1,0.5);
|
|
xy.Set(1,2,0.4);
|
|
xy.Set(2,0,0.1);
|
|
xy.Set(2,1,0.5);
|
|
xy.Set(2,2,0.9);
|
|
xy.Set(3,0,0.1);
|
|
xy.Set(3,1,0.4);
|
|
xy.Set(3,2,0.9);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,4,3,10);
|
|
CClustering::ClusterizerSetAHCAlgo(s,1);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
CClustering::ClusterizerSeparatedByCorr(rep,0.999,k,cidx,cz);
|
|
CClustering::ClusterizerGetKClusters(rep,4,cidx2,cz2);
|
|
if(k!=4)
|
|
return(result);
|
|
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
|
|
return(result);
|
|
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2] || cz[3]!=cz2[3])
|
|
return(result);
|
|
CClustering::ClusterizerSeparatedByCorr(rep,0.900,k,cidx,cz);
|
|
CClustering::ClusterizerGetKClusters(rep,3,cidx2,cz2);
|
|
if(k!=3)
|
|
return(result);
|
|
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
|
|
return(result);
|
|
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2])
|
|
return(result);
|
|
CClustering::ClusterizerSeparatedByCorr(rep,0.600,k,cidx,cz);
|
|
CClustering::ClusterizerGetKClusters(rep,2,cidx2,cz2);
|
|
if(k!=2)
|
|
return(result);
|
|
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
|
|
return(result);
|
|
if(cz[0]!=cz2[0] || cz[1]!=cz2[1])
|
|
return(result);
|
|
//--- Single linkage vs. complete linkage
|
|
xy=matrix<double>::Zeros(6,1);
|
|
xy.Set(0,0,0.0);
|
|
xy.Set(1,0,1.0);
|
|
xy.Set(2,0,2.1);
|
|
xy.Set(3,0,3.3);
|
|
xy.Set(4,0,6.0);
|
|
xy.Set(5,0,4.6);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,6,1,0);
|
|
CClustering::ClusterizerSetAHCAlgo(s,0);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=6 || CAp::Len(rep.m_p)!=6)
|
|
return(result);
|
|
if(CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2 || CAp::Rows(rep.m_pz)!=5 || CAp::Cols(rep.m_pz)!=2)
|
|
return(result);
|
|
berr=false;
|
|
berr=berr||rep.m_p[0]!=2;
|
|
berr=berr||rep.m_p[1]!=3;
|
|
berr=berr||rep.m_p[2]!=4;
|
|
berr=berr||rep.m_p[3]!=5;
|
|
berr=berr||rep.m_p[4]!=0;
|
|
berr=berr||rep.m_p[5]!=1;
|
|
berr=(berr||rep.m_z.Get(0,0)!=0)||rep.m_z.Get(0,1)!=1;
|
|
berr=(berr||rep.m_z.Get(1,0)!=2)||rep.m_z.Get(1,1)!=3;
|
|
berr=(berr||rep.m_z.Get(2,0)!=4)||rep.m_z.Get(2,1)!=5;
|
|
berr=(berr||rep.m_z.Get(3,0)!=6)||rep.m_z.Get(3,1)!=7;
|
|
berr=(berr||rep.m_z.Get(4,0)!=8)||rep.m_z.Get(4,1)!=9;
|
|
berr=(berr||rep.m_pz.Get(0,0)!=2)||rep.m_pz.Get(0,1)!=3;
|
|
berr=(berr||rep.m_pz.Get(1,0)!=4)||rep.m_pz.Get(1,1)!=5;
|
|
berr=(berr||rep.m_pz.Get(2,0)!=0)||rep.m_pz.Get(2,1)!=1;
|
|
berr=(berr||rep.m_pz.Get(3,0)!=6)||rep.m_pz.Get(3,1)!=7;
|
|
berr=(berr||rep.m_pz.Get(4,0)!=8)||rep.m_pz.Get(4,1)!=9;
|
|
if(berr)
|
|
return(result);
|
|
CClustering::ClusterizerSetAHCAlgo(s,1);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=6 || CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2)
|
|
return(result);
|
|
berr=false;
|
|
berr=(berr||rep.m_z.Get(0,0)!=0)||rep.m_z.Get(0,1)!=1;
|
|
berr=(berr||rep.m_z.Get(1,0)!=2)||rep.m_z.Get(1,1)!=6;
|
|
berr=(berr||rep.m_z.Get(2,0)!=3)||rep.m_z.Get(2,1)!=7;
|
|
berr=(berr||rep.m_z.Get(3,0)!=5)||rep.m_z.Get(3,1)!=8;
|
|
berr=(berr||rep.m_z.Get(4,0)!=4)||rep.m_z.Get(4,1)!=9;
|
|
if(berr)
|
|
return(result);
|
|
//--- Test which differentiates complete linkage and average linkage from
|
|
//--- single linkage:
|
|
//--- * we have cluster C0={(-0.5), (0)},
|
|
//--- cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
|
|
//--- and point P between them - (10.0)
|
|
//--- * we try three different strategies - single linkage, complete
|
|
//--- linkage, average linkage.
|
|
//--- * any strategy will merge C0 first, then merge points of C1,
|
|
//--- and then merge P with C0 or C1 (depending on linkage type)
|
|
//--- * we test that:
|
|
//--- a) C0 is merged first
|
|
//--- b) after 5 merges (including merge of C0), P is merged with C0 or C1
|
|
//--- c) P is merged with C1 when we have single linkage, with C0 otherwise
|
|
xy=matrix<double>::Zeros(8,1);
|
|
xy.Set(0,0,-0.5);
|
|
xy.Set(1,0,0.0);
|
|
xy.Set(2,0,10.0);
|
|
xy.Set(3,0,19.0);
|
|
xy.Set(4,0,20.0);
|
|
xy.Set(5,0,21.0);
|
|
xy.Set(6,0,22.0);
|
|
xy.Set(7,0,23.0);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,8,1,0);
|
|
for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
|
|
{
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=8 || CAp::Rows(rep.m_z)!=7 || CAp::Cols(rep.m_z)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1)
|
|
return(result);
|
|
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
|
|
return(result);
|
|
if((ahcalgo==0 || ahcalgo==2) && rep.m_z.Get(5,0)!=8 && rep.m_z.Get(5,1)!=8)
|
|
return(result);
|
|
if(ahcalgo==1 && (rep.m_z.Get(5,0)==8 || rep.m_z.Get(5,1)==8))
|
|
return(result);
|
|
}
|
|
//--- Test which differentiates single linkage and average linkage from
|
|
//--- complete linkage:
|
|
//--- * we have cluster C0={(-2.5), (-2.0)},
|
|
//--- cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
|
|
//--- and point P between them - (10.0)
|
|
//--- * we try three different strategies - single linkage, complete
|
|
//--- linkage, average linkage.
|
|
//--- * any strategy will merge C0 first, then merge points of C1,
|
|
//--- and then merge P with C0 or C1 (depending on linkage type)
|
|
//--- * we test that:
|
|
//--- a) C0 is merged first
|
|
//--- b) after 5 merges (including merge of C0), P is merged with C0 or C1
|
|
//--- c) P is merged with C0 when we have complete linkage, with C1 otherwise
|
|
xy=matrix<double>::Zeros(8,1);
|
|
xy.Set(0,0,-2.5);
|
|
xy.Set(1,0,-2.0);
|
|
xy.Set(2,0,10.0);
|
|
xy.Set(3,0,19.0);
|
|
xy.Set(4,0,20.0);
|
|
xy.Set(5,0,21.0);
|
|
xy.Set(6,0,22.0);
|
|
xy.Set(7,0,23.0);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,8,1,0);
|
|
for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
|
|
{
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=8 || CAp::Rows(rep.m_z)!=7 || CAp::Cols(rep.m_z)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1)
|
|
return(result);
|
|
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
|
|
return(result);
|
|
if(ahcalgo==0 && rep.m_z.Get(5,0)!=8 && rep.m_z.Get(5,1)!=8)
|
|
return(result);
|
|
if((ahcalgo==1 || ahcalgo==2) && (rep.m_z.Get(5,0)==8 || rep.m_z.Get(5,1)==8))
|
|
return(result);
|
|
}
|
|
//--- Test which differentiates weighred average linkage from unweighted average linkage:
|
|
//--- * we have cluster C0={(0.0), (1.5), (2.5)},
|
|
//--- cluster C1={(7.5), (7.99)},
|
|
//--- and point P between them - (4.5)
|
|
//--- * we try two different strategies - weighted average linkage and unweighted average linkage
|
|
//--- * any strategy will merge C1 first, then merge points of C0,
|
|
//--- and then merge P with C0 or C1 (depending on linkage type)
|
|
//--- * we test that:
|
|
//--- a) C1 is merged first, C0 is merged after that
|
|
//--- b) after first 3 merges P is merged with C0 or C1
|
|
//--- c) P is merged with C1 when we have weighted average linkage, with C0 otherwise
|
|
xy=matrix<double>::Zeros(6,1);
|
|
xy.Set(0,0,0.0);
|
|
xy.Set(1,0,1.5);
|
|
xy.Set(2,0,2.5);
|
|
xy.Set(3,0,4.5);
|
|
xy.Set(4,0,7.5);
|
|
xy.Set(5,0,7.99);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,6,1,0);
|
|
for(ahcalgo=2; ahcalgo<=3; ahcalgo++)
|
|
{
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=6 || CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(0,0)!=4 || rep.m_z.Get(0,1)!=5)
|
|
return(result);
|
|
if(rep.m_z.Get(1,0)!=1 || rep.m_z.Get(1,1)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(2,0)!=0 || rep.m_z.Get(2,1)!=7)
|
|
return(result);
|
|
if(rep.m_z.Get(3,0)!=3)
|
|
return(result);
|
|
if(ahcalgo==2 && rep.m_z.Get(3,1)!=8)
|
|
return(result);
|
|
if(ahcalgo==3 && rep.m_z.Get(3,1)!=6)
|
|
return(result);
|
|
}
|
|
//--- Test which checks correctness of Ward's method on very basic problem
|
|
xy=matrix<double>::Zeros(4,1);
|
|
xy.Set(0,0,0.0);
|
|
xy.Set(1,0,1.0);
|
|
xy.Set(2,0,3.1);
|
|
xy.Set(3,0,4.0);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,CAp::Rows(xy),CAp::Cols(xy),2);
|
|
CClustering::ClusterizerGetDistances(xy,CAp::Rows(xy),CAp::Cols(xy),2,d);
|
|
CClustering::ClusterizerSetAHCAlgo(s,4);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(ErrorsInMerges(d,xy,CAp::Rows(xy),CAp::Cols(xy),rep,4))
|
|
return(result);
|
|
//--- One more Ward's test
|
|
xy=matrix<double>::Zeros(8,2);
|
|
xy.Set(0,0,0.4700566262);
|
|
xy.Set(0,1,0.4565938448);
|
|
xy.Set(1,0,0.2394499506);
|
|
xy.Set(1,1,0.1750209592);
|
|
xy.Set(2,0,0.6518417019);
|
|
xy.Set(2,1,0.6151370746);
|
|
xy.Set(3,0,0.9863942841);
|
|
xy.Set(3,1,0.7855012189);
|
|
xy.Set(4,0,0.1517812919);
|
|
xy.Set(4,1,0.2600174758);
|
|
xy.Set(5,0,0.7840203638);
|
|
xy.Set(5,1,0.9023597604);
|
|
xy.Set(6,0,0.2604194835);
|
|
xy.Set(6,1,0.9792704661);
|
|
xy.Set(7,0,0.6353096042);
|
|
xy.Set(7,1,0.8252606906);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,CAp::Rows(xy),CAp::Cols(xy),2);
|
|
CClustering::ClusterizerGetDistances(xy,CAp::Rows(xy),CAp::Cols(xy),2,d);
|
|
CClustering::ClusterizerSetAHCAlgo(s,4);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_z.Get(0,0)!=1 || rep.m_z.Get(0,1)!=4)
|
|
return(result);
|
|
if(rep.m_z.Get(1,0)!=5 || rep.m_z.Get(1,1)!=7)
|
|
return(result);
|
|
if(rep.m_z.Get(2,0)!=0 || rep.m_z.Get(2,1)!=2)
|
|
return(result);
|
|
if(rep.m_z.Get(3,0)!=3 || rep.m_z.Get(3,1)!=9)
|
|
return(result);
|
|
if(rep.m_z.Get(4,0)!=10 || rep.m_z.Get(4,1)!=11)
|
|
return(result);
|
|
if(rep.m_z.Get(5,0)!=6 || rep.m_z.Get(5,1)!=12)
|
|
return(result);
|
|
if(rep.m_z.Get(6,0)!=8 || rep.m_z.Get(6,1)!=13)
|
|
return(result);
|
|
if(ErrorsInMerges(d,xy,CAp::Rows(xy),CAp::Cols(xy),rep,4))
|
|
return(result);
|
|
//--- Ability to solve problems with zero distance matrix
|
|
npoints=20;
|
|
d=matrix<double>::Zeros(npoints,npoints);
|
|
for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
|
|
{
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetDistances(s,d,npoints,true);
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
if(rep.m_NPoints!=npoints || CAp::Rows(rep.m_z)!=(npoints-1) || CAp::Cols(rep.m_z)!=2)
|
|
return(result);
|
|
}
|
|
//--- Test GetKClusters()
|
|
xy=matrix<double>::Zeros(8,1);
|
|
xy.Set(0,0,-2.5);
|
|
xy.Set(1,0,-2.0);
|
|
xy.Set(2,0,10.0);
|
|
xy.Set(3,0,19.0);
|
|
xy.Set(4,0,20.0);
|
|
xy.Set(5,0,21.0);
|
|
xy.Set(6,0,22.0);
|
|
xy.Set(7,0,23.0);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,8,1,0);
|
|
CClustering::ClusterizerSetAHCAlgo(s,0);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
CClustering::ClusterizerGetKClusters(rep,3,cidx,cz);
|
|
if(cidx[0]!=1 || cidx[1]!=1 || cidx[2]!=0 || cidx[3]!=2 || cidx[4]!=2 || cidx[5]!=2 || cidx[6]!=2 || cidx[7]!=2)
|
|
return(result);
|
|
if(cz[0]!=2 || cz[1]!=8 || cz[2]!=12)
|
|
return(result);
|
|
//--- Test is done
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Advanced agglomerative hierarchical clustering tests: returns |
|
|
//| True on failure, False on success. |
|
|
//| Advanced testing subroutine perform several automatically |
|
|
//| generated tests. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestClusteringUnit::AdvancedAHCTests(void)
|
|
{
|
|
//--- create variables
|
|
bool result=false;
|
|
int euclidean=2;
|
|
CClusterizerState s;
|
|
CAHCReport rep;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble dm;
|
|
CMatrixDouble dm2;
|
|
CRowInt idx;
|
|
CRowInt disttypes;
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
int d=0;
|
|
int n=0;
|
|
int npoints=0;
|
|
int ahcalgo=0;
|
|
int disttype=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int t=0;
|
|
int i_=0;
|
|
//--- Test on D-dimensional problem:
|
|
//--- * D = 2...5
|
|
//--- * D clusters, each has N points;
|
|
//--- centers are located at x=(0 ... 1 ... 0);
|
|
//--- cluster radii are approximately 0.1
|
|
//--- * single/complete/unweighted_average/weighted_average linkage/Ward's method are tested
|
|
//--- * Euclidean distance is used, either:
|
|
//--- a) one given by distance matrix (ClusterizerSetDistances)
|
|
//--- b) one calculated from dataset (ClusterizerSetPoints)
|
|
//--- * we have N*D points, and N*D-1 merges in total
|
|
//--- * points are randomly rearranged after generation
|
|
//--- For all kinds of linkage we perform following test:
|
|
//--- * for each point we remember index of its cluster
|
|
//--- (one which is determined during dataset generation)
|
|
//--- * we clusterize points with ALGLIB capabilities
|
|
//--- * we scan Rep.Z and perform first D*(N-1) merges
|
|
//--- * for each merge we check that it merges points
|
|
//--- from same cluster;
|
|
//--- Additonally, we call ErrorsInMerges(). See function comments
|
|
//--- for more information about specific tests performed. This function
|
|
//--- allows us to check that clusters are built exactly as specified by
|
|
//--- definition of the clustering algorithm.
|
|
for(d=2; d<=5; d++)
|
|
{
|
|
for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
|
|
{
|
|
n=(int)MathRound(MathPow(3,CMath::RandomInteger(3)));
|
|
npoints=d*n;
|
|
//--- 1. generate dataset.
|
|
//--- 2. fill Idx (array of cluster indexes):
|
|
//--- * first N*D elements store cluster indexes
|
|
//--- * next D*(N-1) elements are filled during merges
|
|
//--- 3. build distance matrix DM
|
|
xy=matrix<double>::Zeros(n*d,d);
|
|
idx.Resize(n*d+d*(n-1));
|
|
for(i=0; i<n*d; i++)
|
|
{
|
|
for(j=0; j<d; j++)
|
|
xy.Set(i,j,0.2*CMath::RandomReal()-0.1);
|
|
xy.Add(i,i%d,1.0);
|
|
idx.Set(i,i%d);
|
|
}
|
|
for(i=0; i<n*d; i++)
|
|
{
|
|
k=CMath::RandomInteger(n*d);
|
|
if(k!=i)
|
|
{
|
|
xy.SwapRows(i,k);
|
|
idx.Swap(k,i);
|
|
}
|
|
}
|
|
dm=matrix<double>::Zeros(npoints,npoints);
|
|
x0=vector<double>::Zeros(d);
|
|
x1=vector<double>::Zeros(d);
|
|
for(i=0; i<npoints; i++)
|
|
for(j=0; j<npoints; j++)
|
|
{
|
|
x0=xy[i]+0;
|
|
x1=xy[j]+0;
|
|
dm.Set(i,j,DistFunc(x0,x1,d,euclidean));
|
|
}
|
|
//--- Clusterize with SetPoints()
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,n*d,d,euclidean);
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
//--- Tests:
|
|
//--- * replay first D*(N-1) merges; these merges should take place
|
|
//--- within clusters, intercluster merges will be performed at the
|
|
//--- last stages of the processing.
|
|
//--- * test with ErrorsInMerges()
|
|
if(rep.m_NPoints!=npoints)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<d*(n-1); i++)
|
|
{
|
|
//--- Check correctness of I-th row of Z
|
|
if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=(d*n+i))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check that merge is performed within cluster
|
|
if(idx[rep.m_z.Get(i,0)]!=idx[rep.m_z.Get(i,1)])
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Write new entry of Idx.
|
|
//--- Both points from the same cluster, so result of the merge
|
|
//--- belongs to the same cluster
|
|
idx.Set(n*d+i,idx[rep.m_z.Get(i,1)]);
|
|
}
|
|
if(((ahcalgo==0 || ahcalgo==1) || ahcalgo==2) || ahcalgo==4)
|
|
{
|
|
if(ErrorsInMerges(dm,xy,d*n,d,rep,ahcalgo))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Clusterize one more time, now with distance matrix
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetDistances(s,dm,n*d,CMath::RandomReal()>0.5);
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
//--- Tests:
|
|
//--- * replay first D*(N-1) merges; these merges should take place
|
|
//--- within clusters, intercluster merges will be performed at the
|
|
//--- last stages of the processing.
|
|
//--- * test with ErrorsInMerges()
|
|
if(rep.m_NPoints!=npoints)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<(d*(n-1)); i++)
|
|
{
|
|
//--- Check correctness of I-th row of Z
|
|
if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=(d*n+i))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check that merge is performed within cluster
|
|
if(idx[rep.m_z.Get(i,0)]!=idx[rep.m_z.Get(i,1)])
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Write new entry of Idx.
|
|
//--- Both points from the same cluster, so result of the merge
|
|
//--- belongs to the same cluster
|
|
idx.Set(n*d+i,idx[rep.m_z.Get(i,1)]);
|
|
}
|
|
if(ahcalgo==0 || ahcalgo==1 || ahcalgo==2 || ahcalgo==4)
|
|
if(ErrorsInMerges(dm,xy,d*n,d,rep,ahcalgo))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- Test on random D-dimensional problem:
|
|
//--- * D = 2...5
|
|
//--- * N=1..16 random points from unit hypercube
|
|
//--- * single/complete/unweighted_average linkage/Ward's method are tested
|
|
//--- * different distance functions are tested
|
|
//--- * we call ErrorsInMerges() and we check distance matrix
|
|
//--- calculated by unit test against one returned by GetDistances()
|
|
disttypes.Resize(9);
|
|
disttypes.Set(0,0);
|
|
disttypes.Set(1,1);
|
|
disttypes.Set(2,2);
|
|
disttypes.Set(3,10);
|
|
disttypes.Set(4,11);
|
|
disttypes.Set(5,12);
|
|
disttypes.Set(6,13);
|
|
disttypes.Set(7,20);
|
|
disttypes.Set(8,21);
|
|
for(disttype=0; disttype<CAp::Len(disttypes); disttype++)
|
|
{
|
|
for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
|
|
{
|
|
if(ahcalgo==3)
|
|
continue;
|
|
if(ahcalgo==4 && disttype!=2)
|
|
continue;
|
|
npoints=(int)MathRound(MathPow(2,CMath::RandomInteger(5)));
|
|
d=2+CMath::RandomInteger(4);
|
|
//--- Generate dataset and distance matrix
|
|
xy=matrix<double>::Zeros(npoints,d);
|
|
for(i=0; i<npoints; i++)
|
|
for(j=0; j<d; j++)
|
|
xy.Set(i,j,CMath::RandomReal());
|
|
dm=matrix<double>::Zeros(npoints,npoints);
|
|
x0=vector<double>::Zeros(d);
|
|
x1=vector<double>::Zeros(d);
|
|
for(i=0; i<npoints; i++)
|
|
for(j=0; j<npoints; j++)
|
|
{
|
|
x0=xy[i]+0;
|
|
x1=xy[j]+0;
|
|
dm.Set(i,j,DistFunc(x0,x1,d,disttypes[disttype]));
|
|
}
|
|
//--- Clusterize
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,d,disttypes[disttype]);
|
|
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
|
|
CClustering::ClusterizerRunAHC(s,rep);
|
|
//--- Test with ErrorsInMerges()
|
|
if(ErrorsInMerges(dm,xy,npoints,d,rep,ahcalgo))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Test distance matrix
|
|
CClustering::ClusterizerGetDistances(xy,npoints,d,disttypes[disttype],dm2);
|
|
for(i=0; i<npoints; i++)
|
|
for(j=0; j<npoints; j++)
|
|
if(!MathIsValidNumber(dm2.Get(i,j)) || MathAbs(dm.Get(i,j)-dm2.Get(i,j))>(1.0E5*CMath::m_machineepsilon))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Simple test 1: ellipsoid in NF-dimensional space. |
|
|
//| compare k-means centers with random centers |
|
|
//+------------------------------------------------------------------+
|
|
void CTestClusteringUnit::KMeansSimpleTest1(int nvars,int nc,
|
|
int passcount,
|
|
bool &converrors,
|
|
bool &othererrors,
|
|
bool &simpleerrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=nc*100;
|
|
int restarts=5;
|
|
int majoraxis=0;
|
|
CMatrixDouble xy;
|
|
CRowDouble tmp;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
double ekmeans=0;
|
|
double erandom=0;
|
|
double dclosest=0;
|
|
int cclosest=0;
|
|
CClusterizerState s;
|
|
CKmeansReport rep;
|
|
int i_=0;
|
|
|
|
passcount=10;
|
|
tmp=vector<double>::Zeros(nvars);
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Fill
|
|
xy=matrix<double>::Zeros(npoints,nvars);
|
|
majoraxis=CMath::RandomInteger(nvars);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
RSphere(xy,nvars,i);
|
|
xy.Mul(i,majoraxis,nc);
|
|
}
|
|
//--- Test
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nvars,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,restarts,0);
|
|
CClustering::ClusterizerRunKMeans(s,nc,rep);
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- Test that XYC is correct mapping to cluster centers
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
cclosest=-1;
|
|
dclosest=CMath::m_maxrealnumber;
|
|
for(j=0; j<nc; j++)
|
|
{
|
|
for(i_=0; i_<nvars; i_++)
|
|
tmp.Set(i_,xy.Get(i,i_)-rep.m_c.Get(j,i_));
|
|
v=CAblasF::RDotV2(nvars,tmp);
|
|
if(v<dclosest)
|
|
{
|
|
cclosest=j;
|
|
dclosest=v;
|
|
}
|
|
}
|
|
if(cclosest!=rep.m_cidx[i])
|
|
{
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Use first NC rows of XY as random centers
|
|
//--- (XY is totally random, so it is as good as any other choice).
|
|
//--- Compare potential functions.
|
|
ekmeans=0;
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(i_=0; i_<nvars; i_++)
|
|
tmp.Set(i_,xy.Get(i,i_)-rep.m_c.Get(rep.m_cidx[i],i_));
|
|
v=CAblasF::RDotV2(nvars,tmp);
|
|
ekmeans+=v;
|
|
}
|
|
erandom=0;
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
dclosest=CMath::m_maxrealnumber;
|
|
v=0;
|
|
for(j=0; j<nc; j++)
|
|
{
|
|
for(i_=0; i_<nvars; i_++)
|
|
tmp.Set(i_,xy.Get(i,i_)-xy.Get(j,i_));
|
|
v=CAblasF::RDotV2(nvars,tmp);
|
|
if(v<dclosest)
|
|
dclosest=v;
|
|
}
|
|
erandom+=v;
|
|
}
|
|
if(erandom<ekmeans)
|
|
{
|
|
simpleerrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This test perform several checks for special properties |
|
|
//| On failure sets error flag, on success leaves it unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestClusteringUnit::KMeansSpecialTests(bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=0;
|
|
int nfeatures=0;
|
|
int nclusters=0;
|
|
int initalgo=0;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble c;
|
|
int idx0=0;
|
|
int idx1=0;
|
|
int idx2=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int separation=0;
|
|
CRowInt xyc;
|
|
CRowInt xycref;
|
|
CRowDouble xydist2;
|
|
CRowDouble xydist2ref;
|
|
CRowDouble energies;
|
|
CHighQualityRandState rs;
|
|
CClusterizerState s;
|
|
CKmeansReport rep;
|
|
CRowInt pointslist;
|
|
CRowInt featureslist;
|
|
CRowInt clusterslist;
|
|
bool allsame;
|
|
|
|
CHighQualityRand::HQRndRandomize(rs);
|
|
//--- Compare different initialization algorithms:
|
|
//--- * dataset is K balls, chosen at random gaussian points, with
|
|
//--- radius equal to 2^(-Separation).
|
|
//--- * we generate random sample, run k-means initialization algorithm
|
|
//--- and calculate mean energy for each initialization algorithm.
|
|
//--- In order to suppress Lloyd's iteration we use KmeansDbgNoIts
|
|
//--- debug flag.
|
|
//--- * then, we compare mean energies; kmeans++ must be best one,
|
|
//--- random initialization must be worst one.
|
|
energies=vector<double>::Zeros(4);
|
|
passcount=1000;
|
|
npoints=100;
|
|
nfeatures=3;
|
|
nclusters=6;
|
|
xy=matrix<double>::Zeros(npoints,nfeatures);
|
|
c=matrix<double>::Zeros(nclusters,nfeatures);
|
|
CClustering::ClusterizerCreate(s);
|
|
s.m_kmeansdbgnoits=true;
|
|
for(separation=2; separation<=5; separation++)
|
|
{
|
|
//--- Try different init algorithms
|
|
for(initalgo=1; initalgo<=3; initalgo++)
|
|
{
|
|
energies.Set(initalgo,0.0);
|
|
CClustering::ClusterizerSetKMeansInit(s,initalgo);
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Generate centers of balls
|
|
for(i=0; i<nclusters; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Generate points
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs)*MathPow(2,-separation)+c.Get(i%nclusters,j));
|
|
}
|
|
//--- Run clusterization
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1069");
|
|
energies.Add(initalgo,rep.m_energy/passcount);
|
|
}
|
|
}
|
|
//--- Compare
|
|
CAp::SetErrorFlag(othererrors,!(energies[2]<energies[1]),"testclusteringunit.ap:1077");
|
|
CAp::SetErrorFlag(othererrors,!(energies[3]<energies[1]),"testclusteringunit.ap:1078");
|
|
}
|
|
//--- Test distance calculation algorithm
|
|
pointslist.Resize(6);
|
|
pointslist.Set(0,1);
|
|
pointslist.Set(1,10);
|
|
pointslist.Set(2,32);
|
|
pointslist.Set(3,100);
|
|
pointslist.Set(4,512);
|
|
pointslist.Set(5,8000);
|
|
featureslist.Resize(5);
|
|
featureslist.Set(0,1);
|
|
featureslist.Set(1,5);
|
|
featureslist.Set(2,32);
|
|
featureslist.Set(3,50);
|
|
featureslist.Set(4,96);
|
|
clusterslist.Resize(5);
|
|
clusterslist.Set(0,1);
|
|
clusterslist.Set(1,5);
|
|
clusterslist.Set(2,32);
|
|
clusterslist.Set(3,50);
|
|
clusterslist.Set(4,96);
|
|
for(idx0=0; idx0<CAp::Len(pointslist); idx0++)
|
|
{
|
|
for(idx1=0; idx1<CAp::Len(featureslist); idx1++)
|
|
{
|
|
for(idx2=0; idx2<CAp::Len(clusterslist); idx2++)
|
|
{
|
|
npoints=pointslist[idx0];
|
|
nfeatures=featureslist[idx1];
|
|
nclusters=clusterslist[idx2];
|
|
xy=matrix<double>::Zeros(npoints,nfeatures);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(j=0; j<=nfeatures-1; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
c=matrix<double>::Zeros(nclusters,nfeatures);
|
|
for(i=0; i<nclusters; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
xyc.Resize(npoints);
|
|
xycref.Resize(npoints);
|
|
xydist2=vector<double>::Zeros(npoints);
|
|
xydist2ref=vector<double>::Zeros(npoints);
|
|
//--- Test
|
|
CClustering::KMeansUpdateDistances(xy,0,npoints,nfeatures,c,0,nclusters,xyc,xydist2);
|
|
KMeansReferenceUpdateDistances(xy,npoints,nfeatures,c,nclusters,xycref,xydist2ref);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
CAp::SetErrorFlag(othererrors,xyc[i]!=xycref[i],"testclusteringunit.ap:1137");
|
|
CAp::SetErrorFlag(othererrors,MathAbs(xydist2[i]-xydist2ref[i])>(1.0E-6),"testclusteringunit.ap:1138");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test degenerate dataset (less than NClusters distinct points)
|
|
for(nclusters=2; nclusters<=10; nclusters++)
|
|
{
|
|
for(initalgo=0; initalgo<=3; initalgo++)
|
|
{
|
|
for(pass=1; pass<=10; pass++)
|
|
{
|
|
//--- Initialize points. Two algorithms are used:
|
|
//--- * initialization by small integers (no rounding problems)
|
|
//---*initialization by "long" fraction
|
|
npoints=100;
|
|
nfeatures=10;
|
|
xy=matrix<double>::Zeros(npoints,nfeatures);
|
|
if(CHighQualityRand::HQRndNormal(rs)>0.0)
|
|
{
|
|
for(i=0; i<=nclusters-2; i++)
|
|
for(j=0; j<=nfeatures-1; j++)
|
|
xy.Set(i,j,MathSin(CHighQualityRand::HQRndNormal(rs)));
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<nclusters-1; i++)
|
|
for(j=0; j<nfeatures; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndUniformI(rs,50));
|
|
}
|
|
for(i=nclusters-1; i<npoints; i++)
|
|
{
|
|
idx0=CHighQualityRand::HQRndUniformI(rs,nclusters-1);
|
|
for(j=0; j<nfeatures; j++)
|
|
xy.Set(i,j,xy.Get(idx0,j));
|
|
}
|
|
//--- Clusterize with unlimited number of iterations.
|
|
//--- Correct error code must be returned.
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,1,0);
|
|
CClustering::ClusterizerSetKMeansInit(s,initalgo);
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
CAp::SetErrorFlag(othererrors,rep.m_terminationtype!=-3,"testclusteringunit.ap:1185");
|
|
}
|
|
}
|
|
}
|
|
//--- Test deterministic seed:
|
|
//--- * specyfying zero seed, or not specyfind seed at all = nondeterministic algo
|
|
//--- * nonzero zeed = deterministic algo
|
|
for(initalgo=0; initalgo<=3; initalgo++)
|
|
{
|
|
//--- Initialize points.
|
|
npoints=100;
|
|
nfeatures=3;
|
|
xy=matrix<double>::Zeros(npoints,nfeatures);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
|
|
}
|
|
//--- Clusterize with negative seed.
|
|
//--- Perform multiple runs, compare results with
|
|
//--- first one returned - at least one result must
|
|
//--- be different.
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,1,1);
|
|
CClustering::ClusterizerSetKMeansInit(s,initalgo);
|
|
CClustering::ClusterizerSetSeed(s,-CMath::RandomInteger(3));
|
|
allsame=true;
|
|
for(pass=0; pass<=10; pass++)
|
|
{
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1221");
|
|
if(othererrors)
|
|
return;
|
|
if(pass==0)
|
|
{
|
|
//--- Save clusters
|
|
c=rep.m_c;
|
|
}
|
|
else
|
|
{
|
|
//--- Compare clusters with ones returned from first run.
|
|
for(i=0; i<CAp::Rows(rep.m_c); i++)
|
|
for(j=0; j<CAp::Cols(rep.m_c); j++)
|
|
allsame=allsame && (c.Get(i,j)==rep.m_c.Get(i,j));
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(othererrors,allsame,"testclusteringunit.ap:1244");
|
|
//--- Clusterize with positive seed.
|
|
//--- Perform multiple runs, compare results with
|
|
//--- first one returned - all results must be same.
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,1,1);
|
|
CClustering::ClusterizerSetKMeansInit(s,initalgo);
|
|
CClustering::ClusterizerSetSeed(s,1+CMath::RandomInteger(3));
|
|
allsame=true;
|
|
for(pass=0; pass<=10; pass++)
|
|
{
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1261");
|
|
if(othererrors)
|
|
return;
|
|
if(pass==0)
|
|
{
|
|
//--- Save clusters
|
|
c=rep.m_c;
|
|
}
|
|
else
|
|
{
|
|
//--- Compare clusters with ones returned from first run.
|
|
for(i=0; i<CAp::Rows(rep.m_c); i++)
|
|
for(j=0; j<CAp::Cols(rep.m_c); j++)
|
|
allsame=allsame && (c.Get(i,j)==rep.m_c.Get(i,j));
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(othererrors,!allsame,"testclusteringunit.ap:1284");
|
|
//--- Clusterize with default seed.
|
|
//--- Perform multiple runs, compare results with
|
|
//--- first one returned - all results must be same.
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,1,1);
|
|
CClustering::ClusterizerSetKMeansInit(s,initalgo);
|
|
allsame=true;
|
|
for(pass=0; pass<=10; pass++)
|
|
{
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1301");
|
|
if(othererrors)
|
|
return;
|
|
if(pass==0)
|
|
{
|
|
//--- Save clusters
|
|
c=rep.m_c;
|
|
}
|
|
else
|
|
{
|
|
//--- Compare clusters with ones returned from first run.
|
|
for(i=0; i<CAp::Rows(rep.m_c); i++)
|
|
for(j=0; j<CAp::Cols(rep.m_c); j++)
|
|
allsame=allsame && (c.Get(i,j)==rep.m_c.Get(i,j));
|
|
}
|
|
}
|
|
CAp::SetErrorFlag(othererrors,!allsame,"testclusteringunit.ap:1324");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This test checks algorithm ability to handle degenerate problems |
|
|
//| without causing infinite loop. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestClusteringUnit::KMeansInfiniteLoopTest(bool &othererrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=0;
|
|
int nfeatures=0;
|
|
int nclusters=0;
|
|
int restarts=0;
|
|
CMatrixDouble xy;
|
|
int i=0;
|
|
int j=0;
|
|
CClusterizerState s;
|
|
CKmeansReport rep;
|
|
//--- Problem 1: all points are same.
|
|
//--- For NClusters=1 we must get correct solution, for NClusters>1 we must get failure.
|
|
npoints=100;
|
|
nfeatures=1;
|
|
restarts=5;
|
|
xy=matrix<double>::Zeros(npoints,nfeatures);
|
|
for(j=0; j<nfeatures; j++)
|
|
xy.Set(0,j,CMath::RandomReal());
|
|
for(i=1; i<npoints; i++)
|
|
{
|
|
for(j=0; j<nfeatures; j++)
|
|
xy.Set(i,j,xy.Get(0,j));
|
|
}
|
|
nclusters=1;
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,restarts,0);
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
othererrors=othererrors||rep.m_terminationtype<=0;
|
|
for(i=0; i<nfeatures; i++)
|
|
othererrors=othererrors||(MathAbs(rep.m_c.Get(0,i)-xy.Get(0,i))>(1000*CMath::m_machineepsilon));
|
|
for(i=0; i<npoints; i++)
|
|
othererrors=othererrors||rep.m_cidx[i]!=0;
|
|
nclusters=5;
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
othererrors=othererrors||rep.m_terminationtype>0;
|
|
//--- Problem 2: degenerate dataset (report by Andreas).
|
|
npoints=57;
|
|
nfeatures=1;
|
|
restarts=1;
|
|
nclusters=4;
|
|
xy=matrix<double>::Zeros(npoints,nfeatures);
|
|
xy.Set(0,0,12.244689632138986);
|
|
xy.Set(1,0,12.244689632138982);
|
|
xy.Set(2,0,12.244689632138986);
|
|
xy.Set(3,0,12.244689632138982);
|
|
xy.Set(4,0,12.244689632138986);
|
|
xy.Set(5,0,12.244689632138986);
|
|
xy.Set(6,0,12.244689632138986);
|
|
xy.Set(7,0,12.244689632138986);
|
|
xy.Set(8,0,12.244689632138986);
|
|
xy.Set(9,0,12.244689632138986);
|
|
xy.Set(10,0,12.244689632138989);
|
|
xy.Set(11,0,12.244689632138984);
|
|
xy.Set(12,0,12.244689632138986);
|
|
xy.Set(13,0,12.244689632138986);
|
|
xy.Set(14,0,12.244689632138989);
|
|
xy.Set(15,0,12.244689632138986);
|
|
xy.Set(16,0,12.244689632138986);
|
|
xy.Set(17,0,12.244689632138986);
|
|
xy.Set(18,0,12.244689632138986);
|
|
xy.Set(19,0,12.244689632138989);
|
|
xy.Set(20,0,12.244689632138972);
|
|
xy.Set(21,0,12.244689632138986);
|
|
xy.Set(22,0,12.244689632138986);
|
|
xy.Set(23,0,12.244689632138986);
|
|
xy.Set(24,0,12.244689632138984);
|
|
xy.Set(25,0,12.244689632138982);
|
|
xy.Set(26,0,12.244689632138986);
|
|
xy.Set(27,0,12.244689632138986);
|
|
xy.Set(28,0,12.244689632138986);
|
|
xy.Set(29,0,12.244689632138986);
|
|
xy.Set(30,0,12.244689632138986);
|
|
xy.Set(31,0,12.244689632138986);
|
|
xy.Set(32,0,12.244689632138986);
|
|
xy.Set(33,0,12.244689632138986);
|
|
xy.Set(34,0,12.244689632138986);
|
|
xy.Set(35,0,12.244689632138982);
|
|
xy.Set(36,0,12.244689632138989);
|
|
xy.Set(37,0,12.244689632138986);
|
|
xy.Set(38,0,12.244689632138986);
|
|
xy.Set(39,0,12.244689632138986);
|
|
xy.Set(40,0,12.244689632138986);
|
|
xy.Set(41,0,12.244689632138986);
|
|
xy.Set(42,0,12.244689632138986);
|
|
xy.Set(43,0,12.244689632138986);
|
|
xy.Set(44,0,12.244689632138986);
|
|
xy.Set(45,0,12.244689632138986);
|
|
xy.Set(46,0,12.244689632138986);
|
|
xy.Set(47,0,12.244689632138986);
|
|
xy.Set(48,0,12.244689632138986);
|
|
xy.Set(49,0,12.244689632138986);
|
|
xy.Set(50,0,12.244689632138984);
|
|
xy.Set(51,0,12.244689632138986);
|
|
xy.Set(52,0,12.244689632138986);
|
|
xy.Set(53,0,12.244689632138986);
|
|
xy.Set(54,0,12.244689632138986);
|
|
xy.Set(55,0,12.244689632138986);
|
|
xy.Set(56,0,12.244689632138986);
|
|
CClustering::ClusterizerCreate(s);
|
|
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
|
|
CClustering::ClusterizerSetKMeansLimits(s,restarts,0);
|
|
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
|
|
othererrors=othererrors||rep.m_terminationtype<=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This non-deterministic test checks that Restarts>1 significantly |
|
|
//| improves quality of results. |
|
|
//| Subroutine generates random task 3 unit balls in 2D, each with 20|
|
|
//| points, separated by 5 units wide gaps, and solves it with |
|
|
//| Restarts=1 and with Restarts=5. Potential functions are compared,|
|
|
//| outcome of the trial is either 0 or 1 (depending on what is |
|
|
//| better). |
|
|
//| Sequence of 1000 such tasks is solved. If Restarts>1 actually |
|
|
//| improve quality of solution, sum of outcome will be non-binomial.|
|
|
//| If it doesn't matter, it will be binomially distributed. |
|
|
//| P.S. This test was added after report from Gianluca Borello who |
|
|
//| noticed error in the handling of multiple restarts. |
|
|
//+------------------------------------------------------------------+
|
|
void CTestClusteringUnit::KMeansRestartsTest(bool &converrors,
|
|
bool &restartserrors)
|
|
{
|
|
//--- create variables
|
|
int restarts=5;
|
|
int passcount=1000;
|
|
int clustersize=20;
|
|
int nclusters=3;
|
|
int nvars=2;
|
|
int npoints=nclusters*clustersize;
|
|
double sigmathreshold=5;
|
|
double p=0;
|
|
double s=0;
|
|
CMatrixDouble xy;
|
|
CRowDouble tmp;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
double ea=0;
|
|
double eb=0;
|
|
double v=0;
|
|
CClusterizerState state;
|
|
CKmeansReport rep1;
|
|
CKmeansReport rep2;
|
|
int i_=0;
|
|
|
|
xy=matrix<double>::Zeros(npoints,nvars);
|
|
tmp=vector<double>::Zeros(nvars);
|
|
for(pass=1; pass<=passcount; pass++)
|
|
{
|
|
//--- Fill
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
RSphere(xy,nvars,i);
|
|
for(j=0; j<nvars; j++)
|
|
xy.Add(i,j,(double)i/(double)clustersize*5.0);
|
|
}
|
|
CClustering::ClusterizerCreate(state);
|
|
CClustering::ClusterizerSetPoints(state,xy,npoints,nvars,2);
|
|
//--- Test: Restarts=1
|
|
CClustering::ClusterizerSetKMeansLimits(state,1,0);
|
|
CClustering::ClusterizerRunKMeans(state,nclusters,rep1);
|
|
if(rep1.m_terminationtype<=0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
ea=0;
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(i_=0; i_<nvars; i_++)
|
|
tmp.Set(i_,xy.Get(i,i_)-rep1.m_c.Get(rep1.m_cidx[i],i_));
|
|
v=CAblasF::RDotV2(nvars,tmp);
|
|
ea+=v;
|
|
}
|
|
//--- Test: Restarts>1
|
|
CClustering::ClusterizerSetKMeansLimits(state,restarts,0);
|
|
CClustering::ClusterizerRunKMeans(state,nclusters,rep2);
|
|
if(rep2.m_terminationtype<=0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
eb=0;
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
for(i_=0; i_<nvars; i_++)
|
|
tmp.Set(i_,xy.Get(i,i_)-rep2.m_c.Get(rep2.m_cidx[i],i_));
|
|
v=CAblasF::RDotV2(nvars,tmp);
|
|
eb+=v;
|
|
}
|
|
//--- Calculate statistic.
|
|
if(ea<eb)
|
|
p++;
|
|
if(ea==eb)
|
|
p+=0.5;
|
|
}
|
|
//--- If Restarts doesn't influence quality of centers found, P must be
|
|
//--- binomially distributed random value with mean 0.5*PassCount and
|
|
//--- standard deviation Sqrt(PassCount/4).
|
|
//--- If Restarts do influence quality of solution, P must be significantly
|
|
//--- lower than 0.5*PassCount.
|
|
s=(p-0.5*passcount)/MathSqrt((double)passcount/4.0);
|
|
restartserrors=restartserrors||(s>(-sigmathreshold));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal number |
|
|
//+------------------------------------------------------------------+
|
|
double CTestClusteringUnit::RNormal(void)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double s=0;
|
|
double x1=0;
|
|
|
|
while(true)
|
|
{
|
|
u=2*CMath::RandomReal()-1;
|
|
v=2*CMath::RandomReal()-1;
|
|
s=CMath::Sqr(u)+CMath::Sqr(v);
|
|
if(s>0.0 && s<1.0)
|
|
{
|
|
s=MathSqrt(-(2*MathLog(s)/s));
|
|
x1=u*s;
|
|
break;
|
|
}
|
|
}
|
|
//--- return result
|
|
result=x1;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random point from sphere |
|
|
//+------------------------------------------------------------------+
|
|
void CTestClusteringUnit::RSphere(CMatrixDouble &xy,int n,int i)
|
|
{
|
|
double v=0;
|
|
|
|
for(int j=0; j<n; j++)
|
|
xy.Set(i,j,RNormal());
|
|
v=CAblasF::RDotRR(n,xy,i,xy,i);
|
|
v=CMath::RandomReal()/MathSqrt(v);
|
|
for(int i_=0; i_<n; i_++)
|
|
xy.Mul(i,i_,v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Distance function: distance between X0 and X1 |
|
|
//| X0, X1 - array[D], points |
|
|
//| DistType - distance type |
|
|
//+------------------------------------------------------------------+
|
|
double CTestClusteringUnit::DistFunc(CRowDouble &x0,CRowDouble &x1,
|
|
int d,int disttype)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
//--- check
|
|
if(!CAp::Assert(disttype==0 || disttype==1 || disttype==2 || disttype==10 ||
|
|
disttype==11 || disttype==12 || disttype==13 || disttype==20 ||
|
|
disttype==21))
|
|
return(result);
|
|
|
|
switch(disttype)
|
|
{
|
|
case 0:
|
|
for(i=0; i<d; i++)
|
|
result=MathMax(result,MathAbs(x0[i]-x1[i]));
|
|
break;
|
|
case 1:
|
|
for(i=0; i<d; i++)
|
|
result+=MathAbs(x0[i]-x1[i]);
|
|
break;
|
|
case 2:
|
|
for(i=0; i<d; i++)
|
|
result+=CMath::Sqr(x0[i]-x1[i]);
|
|
result=MathSqrt(result);
|
|
break;
|
|
case 10:
|
|
result=MathMax(1-CBaseStat::PearsonCorr2(x0,x1,d),0.0);
|
|
break;
|
|
case 11:
|
|
result=MathMax(1-MathAbs(CBaseStat::PearsonCorr2(x0,x1,d)),0.0);
|
|
break;;
|
|
case 12:
|
|
case 13:
|
|
s0=0.0;
|
|
s1=0.0;
|
|
for(i=0; i<d; i++)
|
|
{
|
|
s0+=CMath::Sqr(x0[i])/d;
|
|
s1+=CMath::Sqr(x1[i])/d;
|
|
}
|
|
s0=MathSqrt(s0);
|
|
s1=MathSqrt(s1);
|
|
result=0;
|
|
for(i=0; i<d; i++)
|
|
result+=x0[i]/s0*(x1[i]/s1)/d;
|
|
if(disttype==12)
|
|
result=MathMax(1-result,0.0);
|
|
else
|
|
result=MathMax(1-MathAbs(result),0.0);
|
|
break;
|
|
case 20:
|
|
result=MathMax(1-CBaseStat::SpearmanCorr2(x0,x1,d),0.0);
|
|
break;
|
|
case 21:
|
|
result=MathMax(1-MathAbs(CBaseStat::SpearmanCorr2(x0,x1,d)),0.0);
|
|
break;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function replays merges and checks that: |
|
|
//| * Rep.NPoints, Rep.Z, Rep.PZ and Rep.PM are consistent and |
|
|
//| correct |
|
|
//| * Rep.MergeDist is consistent with distance between clusters |
|
|
//| being merged |
|
|
//| * clusters with minimal distance are merged at each step |
|
|
//| * GetKClusters() correctly unpacks clusters for each K |
|
|
//| NOTE: this algorithm correctly handle ties, i.e. situations where|
|
|
//| several pairs of clusters have same intercluster distance, |
|
|
//| and we can't unambiguously choose clusters to merge. |
|
|
//| INPUT PARAMETERS |
|
|
//| D - distance matrix, array[NPoints,NPoints], full |
|
|
//| matrix is given (including both triangles and |
|
|
//| zeros on the main diagonal) |
|
|
//| XY - dataset matrix, array[NPoints,NF] |
|
|
//| NPoints - dataset size |
|
|
//| NF - number of features |
|
|
//| Rep - clusterizer report |
|
|
//| AHCAlgo - AHC algorithm: |
|
|
//| * 0 - complete linkage |
|
|
//| * 1 - single linkage |
|
|
//| * 2 - unweighted average linkage |
|
|
//| This function returns True on failure, False on success. |
|
|
//+------------------------------------------------------------------+
|
|
bool CTestClusteringUnit::ErrorsInMerges(CMatrixDouble &d,
|
|
CMatrixDouble &xy,
|
|
int npoints,int nf,
|
|
CAHCReport &rep,
|
|
int ahcalgo)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
CMatrixDouble dm;
|
|
CMatrixInt cm;
|
|
CRowInt clustersizes;
|
|
CRowInt clusterheights;
|
|
bool b[];
|
|
CRowDouble x0;
|
|
CRowDouble x1;
|
|
bool bflag;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int i0=0;
|
|
int i1=0;
|
|
int c0=0;
|
|
int c1=0;
|
|
int s0=0;
|
|
int s1=0;
|
|
double v=0;
|
|
int t=0;
|
|
int mergeidx=0;
|
|
CRowInt kidx;
|
|
CRowInt kidxz;
|
|
int currentelement=0;
|
|
//--- check
|
|
if(!CAp::Assert(ahcalgo!=3,"integrity error"))
|
|
return(true);
|
|
|
|
result=false;
|
|
x0=vector<double>::Zeros(nf);
|
|
x1=vector<double>::Zeros(nf);
|
|
//--- Basic checks:
|
|
//--- * positive completion code
|
|
//--- * sizes of arrays
|
|
//--- * Rep.P is correct permutation
|
|
//--- * Rep.Z contains correct cluster indexes
|
|
//--- * Rep.PZ is consistent with Rep.P/Rep.Z
|
|
//--- * Rep.PM contains consistent indexes
|
|
//--- * GetKClusters() for K=NPoints
|
|
bflag=false;
|
|
bflag=bflag||rep.m_terminationtype<=0;
|
|
if(bflag)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
bflag=bflag||rep.m_NPoints!=npoints;
|
|
bflag=(bflag||CAp::Rows(rep.m_z)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_z)!=2);
|
|
bflag=(bflag||CAp::Rows(rep.m_pz)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_pz)!=2);
|
|
bflag=(bflag||CAp::Rows(rep.m_pm)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_pm)!=6);
|
|
bflag=bflag||CAp::Len(rep.m_mergedist)!=npoints-1;
|
|
bflag=bflag||CAp::Len(rep.m_p)!=npoints;
|
|
if(bflag)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
CAblasF::BSetAllocV(npoints,false,b);
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
if(rep.m_p[i]<0 || rep.m_p[i]>=npoints || b[rep.m_p[i]])
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
b[rep.m_p[i]]=true;
|
|
}
|
|
for(i=0; i<npoints-1; i++)
|
|
{
|
|
if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=npoints+i)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
if(rep.m_pz.Get(i,0)<0 || rep.m_pz.Get(i,0)>=rep.m_pz.Get(i,1) || rep.m_pz.Get(i,1)>=npoints+i)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
for(i=0; i<npoints-1; i++)
|
|
{
|
|
c0=rep.m_z.Get(i,0);
|
|
c1=rep.m_z.Get(i,1);
|
|
s0=rep.m_pz.Get(i,0);
|
|
s1=rep.m_pz.Get(i,1);
|
|
if(c0<npoints)
|
|
c0=rep.m_p[c0];
|
|
if(c1<npoints)
|
|
c1=rep.m_p[c1];
|
|
if(c0!=s0 || c1!=s1)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
CClustering::ClusterizerGetKClusters(rep,npoints,kidx,kidxz);
|
|
if(CAp::Len(kidx)!=npoints || CAp::Len(kidxz)!=npoints)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
if(kidxz[i]!=i || kidx[i]!=i)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Test description:
|
|
//--- * we generate (2*NPoints-1)x(2*NPoints-1) matrix of distances DM and
|
|
//--- (2*NPoints-1)xNPoints matrix of clusters CM (I-th row contains indexes
|
|
//--- of elements which belong to I-th cluster, negative indexes denote
|
|
//--- empty cells). Leading N*N square of DM is just a distance matrix,
|
|
//--- other elements are filled by some large number M (used to mark empty
|
|
//--- elements).
|
|
//--- * we replay all merges
|
|
//--- * every time we merge clusters I and J into K, we:
|
|
//--- * check that distance between I and J is equal to the smallest
|
|
//--- element of DM (note: we account for rounding errors when we
|
|
//--- decide on that)
|
|
//--- * check that distance is consistent with Rep.MergeDist
|
|
//--- * then, we enumerate all elements in clusters being merged,
|
|
//--- and check that after permutation their indexes fall into range
|
|
//--- prescribed by Rep.PM
|
|
//--- * fill K-th column/row of D by distances to cluster K
|
|
//--- * merge I-th and J-th rows of CM and store result into K-th row
|
|
//--- * clear DM and CM: fill I-th and J-th column/row of DM by large
|
|
//--- number M, fill I-th and J-th row of CM by -1.
|
|
//--- NOTE: DM is initialized by distance metric specific to AHC algorithm
|
|
//--- being used. CLINK, SLINK and average linkage use user-provided
|
|
//--- distance measure, say Euclidean one, without any modifications.
|
|
//--- Ward's method uses (and reports) squared and scaled Euclidean
|
|
//--- distances.
|
|
dm=matrix<double>::Zeros(2*npoints-1,2*npoints-1);
|
|
cm.Resize(2*npoints-1,npoints);
|
|
clustersizes.Resize(2*npoints-1);
|
|
for(i=0; i<2*npoints-1; i++)
|
|
{
|
|
for(j=0; j<2*npoints-1; j++)
|
|
if(i<npoints && j<npoints)
|
|
{
|
|
dm.Set(i,j,d.Get(i,j));
|
|
if(ahcalgo==4)
|
|
dm.Set(i,j,0.5*CMath::Sqr(dm.Get(i,j)));
|
|
}
|
|
else
|
|
dm.Set(i,j,CMath::m_maxrealnumber);
|
|
}
|
|
for(i=0; i<2*npoints-1; i++)
|
|
{
|
|
for(j=0; j<npoints; j++)
|
|
cm.Set(i,j,-1);
|
|
}
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
cm.Set(i,0,i);
|
|
clustersizes.Set(i,1);
|
|
}
|
|
for(i=npoints; i<2*npoints-1; i++)
|
|
clustersizes.Set(i,0);
|
|
clusterheights.Resize(2*npoints-1);
|
|
for(i=0; i<npoints; i++)
|
|
clusterheights.Set(i,0);
|
|
for(mergeidx=0; mergeidx<npoints-1; mergeidx++)
|
|
{
|
|
//--- Check that clusters with minimum distance are merged,
|
|
//--- and that MergeDist is consistent with results.
|
|
//--- NOTE: we do not check for specific cluster indexes,
|
|
//--- because it is possible to have a tie. We just
|
|
//--- check that distance between clusters is a true
|
|
//--- minimum over all possible clusters.
|
|
v=CMath::m_maxrealnumber;
|
|
for(i=0; i<2*npoints-1; i++)
|
|
{
|
|
for(j=0; j<2*npoints-1; j++)
|
|
if(i!=j)
|
|
v=MathMin(v,dm.Get(i,j));
|
|
}
|
|
c0=rep.m_z.Get(mergeidx,0);
|
|
c1=rep.m_z.Get(mergeidx,1);
|
|
if(dm.Get(c0,c1)>(v+10000*CMath::m_machineepsilon))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
if(rep.m_mergedist[mergeidx]>(v+10000*CMath::m_machineepsilon))
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Check that indexes of elements fall into range prescribed by Rep.PM,
|
|
//--- and Rep.PM correctly described merge operation
|
|
s0=clustersizes[c0];
|
|
s1=clustersizes[c1];
|
|
for(j=0; j<clustersizes[c0]; j++)
|
|
{
|
|
if(rep.m_p[cm.Get(c0,j)]<rep.m_pm.Get(mergeidx,0) || rep.m_p[cm.Get(c0,j)]>rep.m_pm.Get(mergeidx,1))
|
|
{
|
|
//--- Element falls outside of range described by PM
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
for(j=0; j<clustersizes[c1]; j++)
|
|
{
|
|
if(rep.m_p[cm.Get(c1,j)]<rep.m_pm.Get(mergeidx,2) || rep.m_p[cm.Get(c1,j)]>rep.m_pm.Get(mergeidx,3))
|
|
{
|
|
//--- Element falls outside of range described by PM
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
if(rep.m_pm.Get(mergeidx,1)-rep.m_pm.Get(mergeidx,0)!=s0-1 || rep.m_pm.Get(mergeidx,3)-rep.m_pm.Get(mergeidx,2)!=s1-1 ||
|
|
rep.m_pm.Get(mergeidx,2)!=rep.m_pm.Get(mergeidx,1)+1)
|
|
{
|
|
//--- Cluster size (as given by PM) is inconsistent with its actual size.
|
|
result=true;
|
|
return(result);
|
|
}
|
|
if(rep.m_pm.Get(mergeidx,4)!=clusterheights[rep.m_z.Get(mergeidx,0)] || rep.m_pm.Get(mergeidx,5)!=clusterheights[rep.m_z.Get(mergeidx,1)])
|
|
{
|
|
//--- Heights of subdendrograms as returned by PM are inconsistent with heights
|
|
//--- calculated by us.
|
|
result=true;
|
|
return(result);
|
|
}
|
|
//--- Update cluster heights
|
|
clusterheights.Set(mergeidx+npoints,MathMax(clusterheights[rep.m_z.Get(mergeidx,0)],clusterheights[rep.m_z.Get(mergeidx,1)])+1);
|
|
//--- Update CM
|
|
t=0;
|
|
for(j=0; j<clustersizes[rep.m_z.Get(mergeidx,0)]; j++)
|
|
{
|
|
cm.Set(npoints+mergeidx,t,cm.Get(rep.m_z.Get(mergeidx,0),j));
|
|
t++;
|
|
}
|
|
for(j=0; j<clustersizes[rep.m_z.Get(mergeidx,1)]; j++)
|
|
{
|
|
cm.Set(npoints+mergeidx,t,cm.Get(rep.m_z.Get(mergeidx,1),j));
|
|
t++;
|
|
}
|
|
clustersizes.Set(npoints+mergeidx,t);
|
|
clustersizes.Set(rep.m_z.Get(mergeidx,0),0);
|
|
clustersizes.Set(rep.m_z.Get(mergeidx,1),0);
|
|
//--- Update distance matrix D
|
|
for(i=0; i<2*npoints-1; i++)
|
|
{
|
|
//--- "Remove" columns/rows corresponding to clusters being merged
|
|
dm.Set(i,rep.m_z.Get(mergeidx,0),CMath::m_maxrealnumber);
|
|
dm.Set(i,rep.m_z.Get(mergeidx,1),CMath::m_maxrealnumber);
|
|
dm.Set(rep.m_z.Get(mergeidx,0),i,CMath::m_maxrealnumber);
|
|
dm.Set(rep.m_z.Get(mergeidx,1),i,CMath::m_maxrealnumber);
|
|
}
|
|
for(i=0; i<npoints+mergeidx; i++)
|
|
{
|
|
if(clustersizes[i]>0)
|
|
{
|
|
//--- Calculate column/row corresponding to new cluster
|
|
switch(ahcalgo)
|
|
{
|
|
case 0:
|
|
//--- Calculate distance between clusters I and NPoints+MergeIdx for CLINK
|
|
v=0.0;
|
|
for(i0=0; i0<clustersizes[i]; i0++)
|
|
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
|
|
v=MathMax(v,d.Get(cm.Get(i,i0),cm.Get(npoints+mergeidx,i1)));
|
|
break;
|
|
case 1:
|
|
//--- Calculate distance between clusters I and NPoints+MergeIdx for SLINK
|
|
v=CMath::m_maxrealnumber;
|
|
for(i0=0; i0<clustersizes[i]; i0++)
|
|
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
|
|
v=MathMin(v,d.Get(cm.Get(i,i0),cm.Get(npoints+mergeidx,i1)));
|
|
break;
|
|
case 2:
|
|
//--- Calculate distance between clusters I and NPoints+MergeIdx for unweighted average
|
|
v=0.0;
|
|
t=0;
|
|
for(i0=0; i0<clustersizes[i]; i0++)
|
|
{
|
|
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
|
|
{
|
|
v+=d.Get(cm.Get(i,i0),cm.Get(npoints+mergeidx,i1));
|
|
t++;
|
|
}
|
|
}
|
|
v=v/(double)t;
|
|
break;
|
|
case 3:
|
|
CAp::Assert(false);
|
|
return(true);
|
|
case 4:
|
|
//--- Calculate distance between clusters I and NPoints+MergeIdx for Ward's method:
|
|
//--- * X0 = center of mass for cluster I
|
|
//--- * X1 = center of mass for cluster NPoints+MergeIdx
|
|
//--- * S0 = size of cluster I
|
|
//--- * S1 = size of cluster NPoints+MergeIdx
|
|
//--- * distance between clusters is S0*S1/(S0+S1)*|X0-X1|^2
|
|
for(j=0; j<nf; j++)
|
|
{
|
|
x0.Set(j,0.0);
|
|
x1.Set(j,0.0);
|
|
}
|
|
for(i0=0; i0<clustersizes[i]; i0++)
|
|
{
|
|
for(j=0; j<nf; j++)
|
|
x0.Add(j,xy.Get(cm.Get(i,i0),j)/clustersizes[i]);
|
|
}
|
|
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
|
|
{
|
|
for(j=0; j<nf; j++)
|
|
x1.Add(j,xy.Get(cm.Get(npoints+mergeidx,i1),j)/clustersizes[npoints+mergeidx]);
|
|
}
|
|
v=0.0;
|
|
for(j=0; j<nf; j++)
|
|
v+=CMath::Sqr(x0[j]-x1[j]);
|
|
v=v*clustersizes[i]*clustersizes[npoints+mergeidx]/(clustersizes[i]+clustersizes[npoints+mergeidx]);
|
|
}
|
|
dm.Set(i,npoints+mergeidx,v);
|
|
dm.Set(npoints+mergeidx,i,v);
|
|
}
|
|
}
|
|
//--- Check that GetKClusters() correctly unpacks clusters for K=NPoints-(MergeIdx+1):
|
|
//--- * check lengths of arays
|
|
//--- * check consistency of CIdx/CZ parameters
|
|
//--- * scan clusters (CZ parameter), for each cluster scan CM matrix which stores
|
|
//--- cluster elements (according to our replay of merges), for each element of
|
|
//--- the current cluster check that CIdx array correctly reflects its status.
|
|
k=npoints-(mergeidx+1);
|
|
CClustering::ClusterizerGetKClusters(rep,k,kidx,kidxz);
|
|
if(CAp::Len(kidx)!=npoints || CAp::Len(kidxz)!=k)
|
|
{
|
|
result=true;
|
|
return(result);
|
|
}
|
|
for(i=0; i<=k-2; i++)
|
|
{
|
|
if(kidxz[i]<0 || kidxz[i]>=kidxz[i+1] || kidxz[i+1]>2*npoints-2)
|
|
{
|
|
//--- CZ is inconsistent
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
for(i=0; i<npoints; i++)
|
|
{
|
|
if(kidx[i]<0 || kidx[i]>=k)
|
|
{
|
|
//--- CIdx is inconsistent
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=0; j<clustersizes[kidxz[i]]; j++)
|
|
{
|
|
currentelement=cm.Get(kidxz[i],j);
|
|
if(kidx[currentelement]!=i)
|
|
{
|
|
//--- We've found element which belongs to I-th cluster (according to CM
|
|
//--- matrix, which reflects current status of agglomerative clustering),
|
|
//--- but this element does not belongs to I-th cluster according to
|
|
//--- results of ClusterizerGetKClusters()
|
|
result=true;
|
|
return(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This procedure is a reference version of KMeansUpdateDistances().|
|
|
//| INPUT PARAMETERS: |
|
|
//| XY - dataset, array [0..NPoints-1,0..NVars-1]. |
|
|
//| NPoints - dataset size, NPoints>=K |
|
|
//| NVars - number of variables, NVars>=1 |
|
|
//| CT - matrix of centers, centers are stored in rows |
|
|
//| K - number of centers, K>=1 |
|
|
//| XYC - preallocated output buffer |
|
|
//| XYDist2 - preallocated output buffer |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| XYC - new assignment of points to centers |
|
|
//| XYDist2 - squared distances |
|
|
//+------------------------------------------------------------------+
|
|
void CTestClusteringUnit::KMeansReferenceUpdateDistances(CMatrixDouble &xy,
|
|
int npoints,
|
|
int nvars,
|
|
CMatrixDouble &ct,
|
|
int k,
|
|
CRowInt &xyc,
|
|
CRowDouble &xydist2)
|
|
{
|
|
//--- create variables
|
|
int cclosest=0;
|
|
double dclosest=0;
|
|
double v=0;
|
|
CRowDouble tmp;
|
|
|
|
tmp=vector<double>::Zeros(nvars);
|
|
for(int i=0; i<npoints; i++)
|
|
{
|
|
cclosest=-1;
|
|
dclosest=CMath::m_maxrealnumber;
|
|
for(int j=0; j<k; j++)
|
|
{
|
|
for(int i_=0; i_<nvars; i_++)
|
|
tmp.Set(i_,xy.Get(i,i_)-ct.Get(j,i_));
|
|
v=CAblasF::RDotV2(nvars,tmp);
|
|
if(v<dclosest)
|
|
{
|
|
cclosest=j;
|
|
dclosest=v;
|
|
}
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(cclosest>=0,"KMeansUpdateDistances: internal error"))
|
|
return;
|
|
|
|
xyc.Set(i,cclosest);
|
|
xydist2.Set(i,dclosest);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|