//+------------------------------------------------------------------+ //| TestClasses.mqh | //| Copyright 2003-2012 Sergey Bochkanov (ALGLIB project) | //| Copyright 2012-2017, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ //| Implementation of ALGLIB library in MetaQuotes Language 5 | //| | //| The features of the library include: | //| - Linear algebra (direct algorithms, EVD, SVD) | //| - Solving systems of linear and non-linear equations | //| - Interpolation | //| - Optimization | //| - FFT (Fast Fourier Transform) | //| - Numerical integration | //| - Linear and nonlinear least-squares fitting | //| - Ordinary differential equations | //| - Computation of special functions | //| - Descriptive statistics and hypothesis testing | //| - Data analysis - classification, regression | //| - Implementing linear algebra algorithms, interpolation, etc. | //| in high-precision arithmetic (using MPFR) | //| | //| This file is free software; you can redistribute it and/or | //| modify it under the terms of the GNU General Public License as | //| published by the Free Software Foundation (www.fsf.org); either | //| version 2 of the License, or (at your option) any later version. | //| | //| This program is distributed in the hope that it will be useful, | //| but WITHOUT ANY WARRANTY; without even the implied warranty of | //| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | //| GNU General Public License for more details. | //+------------------------------------------------------------------+ #include #include #include #include #include #include //+------------------------------------------------------------------+ //| Testing class CHighQualityRand | //+------------------------------------------------------------------+ class CTestHQRndUnit { public: CTestHQRndUnit(void); ~CTestHQRndUnit(void); static bool TestHQRnd(const bool silent); private: static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs); static void UnsetState(CHighQualityRandState &state); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestHQRndUnit::CTestHQRndUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestHQRndUnit::~CTestHQRndUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CHighQualityRand | //+------------------------------------------------------------------+ static bool CTestHQRndUnit::TestHQRnd(const bool silent) { bool waserrors; int samplesize=0; double sigmathreshold=0; int passcount=0; int n=0; int i=0; int pass=0; int s1=0; int s2=0; int i1=0; int i2=0; double r1=0; double r2=0; double mean=0; double means=0; double stddev=0; double stddevs=0; double lambdav=0; bool seederrors; bool urerrors; double ursigmaerr=0; bool uierrors; double uisigmaerr=0; bool normerrors; double normsigmaerr=0; bool experrors; double expsigmaerr=0; //--- create array double x[]; //--- object of class CHighQualityRandState state; //--- initialization waserrors=false; sigmathreshold=7; samplesize=100000; passcount=50; seederrors=false; urerrors=false; uierrors=false; normerrors=false; experrors=false; //--- allocation ArrayResize(x,samplesize); //--- Test seed errors for(pass=1;pass<=passcount;pass++) { //--- change values s1=1+CMath::RandomInteger(32000); s2=1+CMath::RandomInteger(32000); //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change value i1=CHighQualityRand::HQRndUniformI(state,100); //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change values i2=CHighQualityRand::HQRndUniformI(state,100); seederrors=seederrors || i1!=i2; //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change value r1=CHighQualityRand::HQRndUniformR(state); //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change values r2=CHighQualityRand::HQRndUniformR(state); seederrors=seederrors || r1!=r2; } //--- Test HQRNDRandomize() and real uniform generator UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values ursigmaerr=0; for(i=0;i<=samplesize-1;i++) x[i]=CHighQualityRand::HQRndUniformR(state); for(i=0;i<=samplesize-1;i++) urerrors=(urerrors || x[i]<=0.0) || x[i]>=1.0; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) ursigmaerr=MathMax(ursigmaerr,MathAbs((mean-0.5)/means)); else urerrors=true; //--- check if(stddevs!=0.0) ursigmaerr=MathMax(ursigmaerr,MathAbs((stddev-MathSqrt(1.0/12.0))/stddevs)); else urerrors=true; //--- change value urerrors=urerrors || ursigmaerr>sigmathreshold; //--- Test HQRNDRandomize() and integer uniform UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- calculation uisigmaerr=0; for(n=2;n<=10;n++) { for(i=0;i<=samplesize-1;i++) x[i]=CHighQualityRand::HQRndUniformI(state,n); for(i=0;i<=samplesize-1;i++) uierrors=(uierrors || x[i]<0.0) || x[i]>=n; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means)); else uierrors=true; //--- check if(stddevs!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs)); else uierrors=true; } //--- change values uierrors=uierrors || uisigmaerr>sigmathreshold; //--- Special 'close-to-limit' test on uniformity of integers //--- (straightforward implementation like 'RND mod N' will return //--- non-uniform numbers for N=2/3*LIMIT) UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values uisigmaerr=0; n=1431655708; //--- calculation for(i=0;i<=samplesize-1;i++) x[i]=CHighQualityRand::HQRndUniformI(state,n); for(i=0;i<=samplesize-1;i++) uierrors=(uierrors || x[i]<0.0) || x[i]>=n; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means)); else uierrors=true; //--- check if(stddevs!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs)); else uierrors=true; uierrors=uierrors || uisigmaerr>sigmathreshold; //--- Test normal UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values normsigmaerr=0; i=0; //--- cycle while(isigmathreshold; //--- Test exponential UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values expsigmaerr=0; lambdav=2+5*CMath::RandomReal(); //--- calculation for(i=0;i<=samplesize-1;i++) x[i]=CHighQualityRand::HQRndExponential(state,lambdav); for(i=0;i<=samplesize-1;i++) uierrors=uierrors || x[i]<0.0; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) expsigmaerr=MathMax(expsigmaerr,MathAbs((mean-1.0/lambdav)/means)); else experrors=true; //--- check if(stddevs!=0.0) expsigmaerr=MathMax(expsigmaerr,MathAbs((stddev-1.0/lambdav)/stddevs)); else experrors=true; experrors=experrors || expsigmaerr>sigmathreshold; //--- Final report waserrors=(((seederrors || urerrors) || uierrors) || normerrors) || experrors; //--- check if(!silent) { Print("RNG TEST"); //--- check if(!seederrors) Print("SEED TEST: OK"); else Print("SEED TEST: FAILED"); //--- check if(!urerrors) Print("UNIFORM CONTINUOUS: OK"); else Print("UNIFORM CONTINUOUS: FAILED"); //--- check if(!uierrors) Print("UNIFORM INTEGER: OK"); else Print("UNIFORM INTEGER: FAILED"); //--- check if(!normerrors) Print("NORMAL: OK"); else Print("NORMAL: FAILED"); //--- check if(!experrors) Print("EXPONENTIAL: OK"); else Print("EXPONENTIAL: FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestHQRndUnit::CalculateMV(double &x[],const int n,double &mean, double &means,double &stddev, double &stddevs) { //--- create variables int i=0; double v1=0; double v2=0; double variance=0; //--- initialization mean=0; means=1; stddev=0; stddevs=1; variance=0; //--- check if(n<=1) return; //--- Mean for(i=0;i<=n-1;i++) mean=mean+x[i]; mean=mean/n; //--- Variance (using corrected two-pass algorithm) if(n!=1) { //--- change value v1=0; for(i=0;i<=n-1;i++) v1=v1+CMath::Sqr(x[i]-mean); //--- change value v2=0; for(i=0;i<=n-1;i++) v2=v2+(x[i]-mean); //--- calculation v2=CMath::Sqr(v2)/n; variance=(v1-v2)/(n-1); //--- check if(variance<0.0) variance=0; stddev=MathSqrt(variance); } //--- Errors means=stddev/MathSqrt(n); stddevs=stddev*MathSqrt(2)/MathSqrt(n-1); } //+------------------------------------------------------------------+ //| Unsets HQRNDState structure | //+------------------------------------------------------------------+ static void CTestHQRndUnit::UnsetState(CHighQualityRandState &state) { state.m_s1=0; state.m_s2=0; state.m_v=0; state.m_magicv=0; } //+------------------------------------------------------------------+ //| Testing class CTSort | //+------------------------------------------------------------------+ class CTestTSortUnit { public: CTestTSortUnit(void); ~CTestTSortUnit(void); static bool TestTSort(const bool silent); private: static void Unset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static void Unset1DI(int &a[]); static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestTSortUnit::CTestTSortUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestTSortUnit::~CTestTSortUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CTSort | //+------------------------------------------------------------------+ static bool CTestTSortUnit::TestTSort(const bool silent) { //--- create variables bool waserrors; int n=0; int i=0; int pass=0; int passcount=0; int maxn=0; //--- create arrays double a[]; double a0[]; double a1[]; double a2[]; double a3[]; double ar[]; int ai[]; int p1[]; int p2[]; double bufr1[]; double bufr2[]; int bufi1[]; //--- initialization waserrors=false; maxn=100; passcount=10; //--- Test tagsort for(n=1;n<=maxn;n++) { for(pass=1;pass<=passcount;pass++) { //--- (probably) distinct sort: //--- * first sort A0 using TagSort and test sort results //--- * now we can use A0 as reference point and test other functions Unset1DI(p1); Unset1DI(p2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(a1,n); ArrayResize(a2,n); ArrayResize(a3,n); ArrayResize(ar,n); ArrayResize(ai,n); //--- change values for(i=0;i<=n-1;i++) { a[i]=2*CMath::RandomReal()-1; a0[i]=a[i]; a1[i]=a[i]; a2[i]=a[i]; a3[i]=a[i]; ar[i]=i; ai[i]=i; } //--- function call CTSort::TagSort(a0,n,p1,p2); //--- function call TestSortResults(a0,p1,p2,a,n,waserrors); //--- function call CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i]; //--- function call CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i]; //--- function call CTSort::TagSortFast(a3,bufr1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || a3[i]!=a0[i]; //--- non-distinct sort Unset1DI(p1); Unset1DI(p2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(a1,n); ArrayResize(a2,n); ArrayResize(a3,n); ArrayResize(ar,n); ArrayResize(ai,n); //--- change values for(i=0;i<=n-1;i++) { a[i]=i/2; a0[i]=a[i]; a1[i]=a[i]; a2[i]=a[i]; a3[i]=a[i]; ar[i]=i; ai[i]=i; } //--- function call CTSort::TagSort(a0,n,p1,p2); //--- function call TestSortResults(a0,p1,p2,a,n,waserrors); //--- function call CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i]; //--- function call CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i]; //--- function call CTSort::TagSortFast(a3,bufr1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || a3[i]!=a0[i]; //--- 'All same' sort Unset1DI(p1); Unset1DI(p2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(a1,n); ArrayResize(a2,n); ArrayResize(a3,n); ArrayResize(ar,n); ArrayResize(ai,n); //--- change values for(i=0;i<=n-1;i++) { a[i]=0; a0[i]=a[i]; a1[i]=a[i]; a2[i]=a[i]; a3[i]=a[i]; ar[i]=i; ai[i]=i; } //--- function call CTSort::TagSort(a0,n,p1,p2); //--- function call TestSortResults(a0,p1,p2,a,n,waserrors); //--- function call CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i]; //--- function call CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i]; //--- function call CTSort::TagSortFast(a3,bufr1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || a3[i]!=a0[i]; //--- 0-1 sort Unset1DI(p1); Unset1DI(p2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(a1,n); ArrayResize(a2,n); ArrayResize(a3,n); ArrayResize(ar,n); ArrayResize(ai,n); //--- change values for(i=0;i<=n-1;i++) { a[i]=CMath::RandomInteger(2); a0[i]=a[i]; a1[i]=a[i]; a2[i]=a[i]; a3[i]=a[i]; ar[i]=i; ai[i]=i; } //--- function call CTSort::TagSort(a0,n,p1,p2); //--- function call TestSortResults(a0,p1,p2,a,n,waserrors); //--- function call CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i]; //--- function call CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i]; //--- function call CTSort::TagSortFast(a3,bufr1,n); //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || a3[i]!=a0[i]; } } //--- report if(!silent) { Print("TESTING TAGSORT"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestTSortUnit::Unset2D(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestTSortUnit::Unset1D(double &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestTSortUnit::Unset1DI(int &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=CMath::RandomInteger(3)-1; } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestTSortUnit::TestSortResults(double &asorted[],int &p1[], int &p2[],double &aoriginal[], const int n,bool &waserrors) { //--- create variables int i=0; double t=0; //--- create arrays double a2[]; int f[]; //--- allocation ArrayResize(a2,n); ArrayResize(f,n); //--- is set ordered? for(i=0;i<=n-2;i++) waserrors=waserrors || asorted[i]>asorted[i+1]; //--- P1 correctness for(i=0;i<=n-1;i++) waserrors=waserrors || asorted[i]!=aoriginal[p1[i]]; //--- change values for(i=0;i<=n-1;i++) f[i]=0; for(i=0;i<=n-1;i++) f[p1[i]]=f[p1[i]]+1; //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || f[i]!=1; //--- P2 correctness for(i=0;i<=n-1;i++) a2[i]=aoriginal[i]; for(i=0;i<=n-1;i++) { //--- check if(p2[i]!=i) { t=a2[i]; a2[i]=a2[p2[i]]; a2[p2[i]]=t; } } //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || asorted[i]!=a2[i]; } //+------------------------------------------------------------------+ //| Testing class CNearestNeighbor | //+------------------------------------------------------------------+ class CTestNearestNeighborUnit { public: CTestNearestNeighborUnit(void); ~CTestNearestNeighborUnit(void); static bool TestNearestNeighbor(const bool silent); static void Unset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static bool KDTResultsDifferent(CMatrixDouble &refxy,const int ntotal,CMatrixDouble &qx,CMatrixDouble &qxy,int &qt[],const int n,const int nx,const int ny); static double VNorm(double &x[],const int n,const int normtype); static void TestKDTUniform(CMatrixDouble &xy,const int n,const int nx,const int ny,const int normtype,bool &kdterrors); private: static void TestKDTreeSerialization(bool &err); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestNearestNeighborUnit::CTestNearestNeighborUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestNearestNeighborUnit::~CTestNearestNeighborUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CNearestNeighbor | //+------------------------------------------------------------------+ static bool CTestNearestNeighborUnit::TestNearestNeighbor(const bool silent) { //--- create variables int i=0; int j=0; double v=0; int normtype=0; int nx=0; int ny=0; int n=0; int smalln=0; int largen=0; int passcount=0; int pass=0; bool waserrors; bool kdterrors; //--- create matrix CMatrixDouble xy; //--- initialization kdterrors=false; passcount=2; smalln=256; largen=2048; ny=3; //--- function call TestKDTreeSerialization(kdterrors); //--- calculation for(pass=1;pass<=passcount;pass++) { for(normtype=0;normtype<=2;normtype++) { for(nx=1;nx<=3;nx++) { //--- Test in hypercube xy.Resize(largen,nx+ny); for(i=0;i<=largen-1;i++) { for(j=0;j<=nx+ny-1;j++) xy[i].Set(j,10*CMath::RandomReal()-5); } //--- function calls for(n=1;n<=10;n++) TestKDTUniform(xy,n,nx,CMath::RandomInteger(ny+1),normtype,kdterrors); TestKDTUniform(xy,largen,nx,CMath::RandomInteger(ny+1),normtype,kdterrors); //--- Test clustered (2*N points,pairs of equal points) xy.Resize(2*smalln,nx+ny); for(i=0;i<=smalln-1;i++) { for(j=0;j<=nx+ny-1;j++) { xy[2*i].Set(j,10*CMath::RandomReal()-5); xy[2*i+1].Set(j,xy[2*i][j]); } } //--- function call TestKDTUniform(xy,2*smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors); //--- Test degenerate case: all points are same except for one xy.Resize(smalln,nx+ny); v=CMath::RandomReal(); //--- change values for(i=0;i<=smalln-2;i++) { for(j=0;j<=nx+ny-1;j++) xy[i].Set(j,v); } for(j=0;j<=nx+ny-1;j++) xy[smalln-1].Set(j,10*CMath::RandomReal()-5); //--- function call TestKDTUniform(xy,smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors); } } } //--- report waserrors=kdterrors; //--- check if(!silent) { Print("TESTING NEAREST NEIGHBOR SEARCH"); //--- check if(!kdterrors) Print("KD TREES: OK"); else Print("KD TREES: FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestNearestNeighborUnit::Unset2D(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestNearestNeighborUnit::Unset1D(double &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Compare results from different queries: | //| * X just X-values | //| * XY X-values and Y-values | //| * XT X-values and tag values | //+------------------------------------------------------------------+ static bool CTestNearestNeighborUnit::KDTResultsDifferent(CMatrixDouble &refxy, const int ntotal, CMatrixDouble &qx, CMatrixDouble &qxy, int &qt[],const int n, const int nx,const int ny) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=false; for(i=0;i<=n-1;i++) { //--- check if(qt[i]<0 || qt[i]>=ntotal) { //--- return result return(true); } //--- search errors for(j=0;j<=nx-1;j++) { result=result || qx[i][j]!=refxy[qt[i]][j]; result=result || qxy[i][j]!=refxy[qt[i]][j]; } for(j=0;j<=ny-1;j++) result=result || qxy[i][nx+j]!=refxy[qt[i]][nx+j]; } //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns norm | //+------------------------------------------------------------------+ static double CTestNearestNeighborUnit::VNorm(double &x[],const int n, const int normtype) { //--- create variables double result=0; int i=0; //--- initialization result=CMath::RandomReal(); //--- check if(normtype==0) { //--- calculation result=0; for(i=0;i<=n-1;i++) result=MathMax(result,MathAbs(x[i])); //--- return result return(result); } //--- check if(normtype==1) { //--- calculation result=0; for(i=0;i<=n-1;i++) result=result+MathAbs(x[i]); //--- return result return(result); } //--- check if(normtype==2) { //--- calculation result=0; for(i=0;i<=n-1;i++) result=result+CMath::Sqr(x[i]); result=MathSqrt(result); //--- return result return(result); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing Nearest Neighbor Search on uniformly distributed | //| hypercube | //| NormType: 0,1,2 | //| D: space dimension | //| N: points count | //+------------------------------------------------------------------+ static void CTestNearestNeighborUnit::TestKDTUniform(CMatrixDouble &xy, const int n,const int nx, const int ny,const int normtype, bool &kdterrors) { //--- create variables double errtol=0; int kx=0; int kxy=0; int kt=0; double eps=0; int i=0; int j=0; int k=0; int task=0; bool isequal; double r=0; int q=0; int qcount=0; int i_=0; //--- create arrays int tags[]; double ptx[]; double tmpx[]; bool tmpb[]; int qtags[]; double qr[]; //--- objects of classes CKDTree treex; CKDTree treexy; CKDTree treext; //--- create matrix CMatrixDouble qx; CMatrixDouble qxy; //--- initialization qcount=10; //--- Tol - roundoff error tolerance (for '>=' comparisons) errtol=100000*CMath::m_machineepsilon; //--- fill tags ArrayResize(tags,n); for(i=0;i<=n-1;i++) tags[i]=i; //--- build trees CNearestNeighbor::KDTreeBuild(xy,n,nx,0,normtype,treex); CNearestNeighbor::KDTreeBuild(xy,n,nx,ny,normtype,treexy); CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,0,normtype,treext); //--- allocate arrays ArrayResize(tmpx,nx); ArrayResize(tmpb,n); qx.Resize(n,nx); qxy.Resize(n,nx+ny); ArrayResize(qtags,n); ArrayResize(qr,n); ArrayResize(ptx,nx); //--- test general K-NN queries (with self-matches): //--- * compare results from different trees (must be equal) and //--- check that correct (value,tag) pairs are returned //--- * test results from XT tree - let R be radius of query result. //--- then all points not in result must be not closer than R. for(q=1;q<=qcount;q++) { //--- Select K: 1..N if(CMath::RandomReal()>0.5) k=1+CMath::RandomInteger(n); else k=1; //--- Select point (either one of the points,or random) if(CMath::RandomReal()>0.5) { i=CMath::RandomInteger(n); for(i_=0;i_<=nx-1;i_++) ptx[i_]=xy[i][i_]; } else { for(i=0;i<=nx-1;i++) ptx[i]=2*CMath::RandomReal()-1; } //--- Test: //--- * consistency of results from different queries //--- * points in query are IN the R-sphere (or at the boundary), //--- and points not in query are outside of the R-sphere (or at the boundary) //--- * distances are correct and are ordered kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true); kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true); kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true); //--- check if((kx!=k || kxy!=k) || kt!=k) { kdterrors=true; return; } //--- function calls CNearestNeighbor::KDTreeQueryResultsXI(treex,qx); CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); //--- function calls CNearestNeighbor::KDTreeQueryResultsX(treex,qx); CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); //--- change values for(i=0;i<=n-1;i++) tmpb[i]=true; r=0; //--- calculation for(i=0;i<=k-1;i++) { tmpb[qtags[i]]=false; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-qx[i][i_]; r=MathMax(r,VNorm(tmpx,nx,normtype)); } for(i=0;i<=n-1;i++) { //--- check if(tmpb[i]) { for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-xy[i][i_]; //--- search errors kdterrors=kdterrors || VNorm(tmpx,nx,normtype)qr[i+1]; } for(i=0;i<=k-1;i++) { for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_]; //--- search errors kdterrors=kdterrors || MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol; } //--- Test reallocation properties: buffered functions must automatically //--- resize array which is too small,but leave unchanged array which is //--- too large. if(n>=2) { //--- First step: array is too small,two elements are required k=2; kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true); kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true); kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true); //--- check if((kx!=k || kxy!=k) || kt!=k) { kdterrors=true; return; } //--- allocation qx.Resize(1,1); qxy.Resize(1,1); ArrayResize(qtags,1); ArrayResize(qr,1); //--- function calls CNearestNeighbor::KDTreeQueryResultsX(treex,qx); CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); //--- Second step: array is one row larger than needed,so only first //--- row is overwritten. Test it. k=1; kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true); kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true); kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true); //--- check if((kx!=k || kxy!=k) || kt!=k) { kdterrors=true; return; } //--- change values for(i=0;i<=nx-1;i++) qx[1].Set(i,CInfOrNaN::NaN()); for(i=0;i<=nx+ny-1;i++) qxy[1].Set(i,CInfOrNaN::NaN()); qtags[1]=999; qr[1]=CInfOrNaN::NaN(); //--- function calls CNearestNeighbor::KDTreeQueryResultsX(treex,qx); CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); for(i=0;i<=nx-1;i++) { //--- search errors kdterrors=kdterrors || !CInfOrNaN::IsNaN(qx[1][i]); } for(i=0;i<=nx+ny-1;i++) { //--- search errors kdterrors=kdterrors || !CInfOrNaN::IsNaN(qxy[1][i]); } //--- search errors kdterrors=kdterrors || !(qtags[1]==999); kdterrors=kdterrors || !CInfOrNaN::IsNaN(qr[1]); } //--- Test reallocation properties: 'interactive' functions must allocate //--- new array on each call. if(n>=2) { //--- On input array is either too small or too large for(k=1;k<=2;k++) { //--- check if(!CAp::Assert(k==1 || k==2,"KNN: internal error (unexpected K)!")) return; //--- change values kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true); kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true); kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true); //--- check if((kx!=k || kxy!=k) || kt!=k) { kdterrors=true; return; } //--- allocation qx.Resize(3-k,3-k); qxy.Resize(3-k,3-k); ArrayResize(qtags,3-k); ArrayResize(qr,3-k); //--- function calls CNearestNeighbor::KDTreeQueryResultsXI(treex,qx); CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); kdterrors=(kdterrors || CAp::Rows(qx)!=k) || CAp::Cols(qx)!=nx; kdterrors=(kdterrors || CAp::Rows(qxy)!=k) || CAp::Cols(qxy)!=nx+ny; kdterrors=kdterrors || CAp::Len(qtags)!=k; kdterrors=kdterrors || CAp::Len(qr)!=k; } } } //--- test general approximate K-NN queries (with self-matches): //--- * compare results from different trees (must be equal) and //--- check that correct (value,tag) pairs are returned //--- * test results from XT tree - let R be radius of query result. //--- then all points not in result must be not closer than R/(1+Eps). for(q=1;q<=qcount;q++) { //--- Select K: 1..N if(CMath::RandomReal()>0.5) k=1+CMath::RandomInteger(n); else k=1; //--- Select Eps eps=0.5+CMath::RandomReal(); //--- Select point (either one of the points,or random) if(CMath::RandomReal()>0.5) { i=CMath::RandomInteger(n); for(i_=0;i_<=nx-1;i_++) ptx[i_]=xy[i][i_]; } else { for(i=0;i<=nx-1;i++) ptx[i]=2*CMath::RandomReal()-1; } //--- Test: //--- * consistency of results from different queries //--- * points in query are IN the R-sphere (or at the boundary), //--- and points not in query are outside of the R-sphere (or at the boundary) //--- * distances are correct and are ordered kx=CNearestNeighbor::KDTreeQueryAKNN(treex,ptx,k,true,eps); kxy=CNearestNeighbor::KDTreeQueryAKNN(treexy,ptx,k,true,eps); kt=CNearestNeighbor::KDTreeQueryAKNN(treext,ptx,k,true,eps); //--- check if((kx!=k || kxy!=k) || kt!=k) { kdterrors=true; return; } //--- function calls CNearestNeighbor::KDTreeQueryResultsXI(treex,qx); CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); //--- function calls CNearestNeighbor::KDTreeQueryResultsX(treex,qx); CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny); //--- change values for(i=0;i<=n-1;i++) tmpb[i]=true; r=0; //--- calculation for(i=0;i<=k-1;i++) { tmpb[qtags[i]]=false; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-qx[i][i_]; r=MathMax(r,VNorm(tmpx,nx,normtype)); } //--- calculation for(i=0;i<=n-1;i++) { //--- check if(tmpb[i]) { for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-xy[i][i_]; //--- search errors kdterrors=kdterrors || VNorm(tmpx,nx,normtype)qr[i+1]; } //--- calculation for(i=0;i<=k-1;i++) { for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_]; //--- search errors kdterrors=kdterrors || MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol; } } //--- test general R-NN queries (with self-matches): //--- * compare results from different trees (must be equal) and //--- check that correct (value,tag) pairs are returned //--- * test results from XT tree - let R be radius of query result. //--- then all points not in result must be not closer than R. for(q=1;q<=qcount;q++) { //--- Select R if(CMath::RandomReal()>0.3) r=MathMax(CMath::RandomReal(),CMath::m_machineepsilon); else r=CMath::m_machineepsilon; //--- Select point (either one of the points,or random) if(CMath::RandomReal()>0.5) { i=CMath::RandomInteger(n); for(i_=0;i_<=nx-1;i_++) ptx[i_]=xy[i][i_]; } else { for(i=0;i<=nx-1;i++) ptx[i]=2*CMath::RandomReal()-1; } //--- Test: //--- * consistency of results from different queries //--- * points in query are IN the R-sphere (or at the boundary), //--- and points not in query are outside of the R-sphere (or at the boundary) //--- * distances are correct and are ordered kx=CNearestNeighbor::KDTreeQueryRNN(treex,ptx,r,true); kxy=CNearestNeighbor::KDTreeQueryRNN(treexy,ptx,r,true); kt=CNearestNeighbor::KDTreeQueryRNN(treext,ptx,r,true); //--- check if(kxy!=kx || kt!=kx) { kdterrors=true; return; } //--- function calls CNearestNeighbor::KDTreeQueryResultsXI(treex,qx); CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny); //--- function calls CNearestNeighbor::KDTreeQueryResultsX(treex,qx); CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy); CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags); CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr); //--- search errors kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny); //--- change values for(i=0;i<=n-1;i++) tmpb[i]=true; for(i=0;i<=kx-1;i++) tmpb[qtags[i]]=false; //--- calculation for(i=0;i<=n-1;i++) { for(i_=0;i_<=nx-1;i_++) tmpx[i_]=ptx[i_]; for(i_=0;i_<=nx-1;i_++) tmpx[i_]=tmpx[i_]-xy[i][i_]; //--- check if(tmpb[i]) { //--- search errors kdterrors=kdterrors || VNorm(tmpx,nx,normtype)r*(1+errtol); } } for(i=0;i<=kx-2;i++) { //--- search errors kdterrors=kdterrors || qr[i]>qr[i+1]; } } //--- Test self-matching: //--- * self-match - nearest neighbor of each point in XY is the point itself //--- * no self-match - nearest neighbor is NOT the point itself if(n>1) { //--- test for N=1 have non-general form,but it is not really needed for(task=0;task<=1;task++) { for(i=0;i<=n-1;i++) { for(i_=0;i_<=nx-1;i_++) ptx[i_]=xy[i][i_]; //--- function calls kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,1,task==0); CNearestNeighbor::KDTreeQueryResultsXI(treex,qx); //--- check if(kx!=1) { kdterrors=true; return; } //--- change value isequal=true; for(j=0;j<=nx-1;j++) isequal=isequal && qx[0][j]==ptx[j]; //--- check if(task==0) kdterrors=kdterrors || !isequal; else kdterrors=kdterrors || isequal; } } } } //+------------------------------------------------------------------+ //| Testing serialization of KD trees | //| This function sets Err to True on errors, but leaves it unchanged| //| on success | //+------------------------------------------------------------------+ static void CTestNearestNeighborUnit::TestKDTreeSerialization(bool &err) { //--- create variables int n=0; int nx=0; int ny=0; int normtype=0; int i=0; int j=0; int k=0; int q=0; double threshold=0; int k0=0; int k1=0; //--- objects of classes CKDTree tree0; CKDTree tree1; //--- create matrix CMatrixDouble xy; CMatrixDouble xy0; CMatrixDouble xy1; //--- create arrays double x[]; int tags[]; int qsizes[]; int tags0[]; int tags1[]; int i_=0; //--- initialization threshold=100*CMath::m_machineepsilon; //--- different N,NX,NY,NormType n=1; while(n<=51) { //--- prepare array with query sizes ArrayResize(qsizes,4); qsizes[0]=1; qsizes[1]=(int)(MathMin(2,n)); qsizes[2]=(int)(MathMin(4,n)); qsizes[3]=n; //--- different NX/NY/NormType for(nx=1;nx<=2;nx++) { for(ny=0;ny<=2;ny++) { for(normtype=0;normtype<=2;normtype++) { //--- Prepare data xy.Resize(n,nx+ny); ArrayResize(tags,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=nx+ny-1;j++) xy[i].Set(j,CMath::RandomReal()); tags[i]=CMath::RandomInteger(100); } //--- Build tree,pass it through serializer CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,ny,normtype,tree0); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CNearestNeighbor::KDTreeAlloc(_local_serializer,tree0); _local_serializer.SStart_Str(); CNearestNeighbor::KDTreeSerialize(_local_serializer,tree0); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CNearestNeighbor::KDTreeUnserialize(_local_serializer,tree1); _local_serializer.Stop(); } //--- For each point of XY we make queries with different sizes ArrayResize(x,nx); for(k=0;k<=n-1;k++) { for(q=0;q<=CAp::Len(qsizes)-1;q++) { for(i_=0;i_<=nx-1;i_++) x[i_]=xy[k][i_]; //--- change values k0=CNearestNeighbor::KDTreeQueryKNN(tree0,x,qsizes[q],true); k1=CNearestNeighbor::KDTreeQueryKNN(tree1,x,qsizes[q],true); //--- check if(k0!=k1) { err=true; return; } //--- function call CNearestNeighbor::KDTreeQueryResultsXY(tree0,xy0); //--- function call CNearestNeighbor::KDTreeQueryResultsXY(tree1,xy1); for(i=0;i<=k0-1;i++) { for(j=0;j<=nx+ny-1;j++) { //--- check if(MathAbs(xy0[i][j]-xy1[i][j])>threshold) { err=true; return; } } } //--- function call CNearestNeighbor::KDTreeQueryResultsTags(tree0,tags0); //--- function call CNearestNeighbor::KDTreeQueryResultsTags(tree1,tags1); for(i=0;i<=k0-1;i++) { //--- check if(tags0[i]!=tags1[i]) { err=true; return; } } } } } } } //--- Next N n=n+25; } } //+------------------------------------------------------------------+ //| Testing class CAblas | //+------------------------------------------------------------------+ class CTestAblasUnit { public: CTestAblasUnit(void); ~CTestAblasUnit(void); static bool TestAblas(const bool silent); private: static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta); static bool TestTrsM(const int minn,const int maxn); static bool TestSyrk(const int minn,const int maxn); static bool TestGemm(const int minn,const int maxn); static bool TestTrans(const int minn,const int maxn); static bool TestRank1(const int minn,const int maxn); static bool TestMV(const int minn,const int maxn); static bool TestCopy(const int minn,const int maxn); static void RefCMatrixRightTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2); static void RefCMatrixLeftTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2); static void RefRMatrixRightTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2); static void RefRMatrixLeftTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2); static bool InternalCMatrixTrInverse(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular); static bool InternalRMatrixTrInverse(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular); static void RefCMatrixSyrk(const int n,const int k,const double alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,const double beta,CMatrixComplex &c,const int ic,const int jc,const bool isupper); static void RefRMatrixSyrk(const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,const double beta,CMatrixDouble &c,const int ic,const int jc,const bool isupper); static void RefCMatrixGemm(const int m,const int n,const int k,complex &alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,CMatrixComplex &b,const int ib,const int jb,const int optypeb,complex &beta,CMatrixComplex &c,const int ic,const int jc); static void RefRMatrixGemm(const int m,const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,CMatrixDouble &b,const int ib,const int jb,const int optypeb,const double beta,CMatrixDouble &c,const int ic,const int jc); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestAblasUnit::CTestAblasUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestAblasUnit::~CTestAblasUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CAblas | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestAblas(const bool silent) { //--- create variables double threshold=0; bool trsmerrors; bool syrkerrors; bool gemmerrors; bool transerrors; bool rank1errors; bool mverrors; bool copyerrors; bool waserrors; //--- create array CMatrixDouble ra; //--- initialization trsmerrors=false; syrkerrors=false; gemmerrors=false; transerrors=false; rank1errors=false; mverrors=false; copyerrors=false; waserrors=false; threshold=10000*CMath::m_machineepsilon; //--- search errors trsmerrors=trsmerrors || TestTrsM(1,3*CAblas::AblasBlockSize()+1); syrkerrors=syrkerrors || TestSyrk(1,3*CAblas::AblasBlockSize()+1); gemmerrors=gemmerrors || TestGemm(1,3*CAblas::AblasBlockSize()+1); transerrors=transerrors || TestTrans(1,3*CAblas::AblasBlockSize()+1); rank1errors=rank1errors || TestRank1(1,3*CAblas::AblasBlockSize()+1); mverrors=mverrors || TestMV(1,3*CAblas::AblasBlockSize()+1); copyerrors=copyerrors || TestCopy(1,3*CAblas::AblasBlockSize()+1); //--- report waserrors=(((((trsmerrors || syrkerrors) || gemmerrors) || transerrors) || rank1errors) || mverrors) || copyerrors; //--- check if(!silent) { Print("TESTING ABLAS"); Print("* TRSM: "); //--- check if(trsmerrors) Print("FAILED"); else Print("OK"); Print("* SYRK: "); //--- check if(syrkerrors) Print("FAILED"); else Print("OK"); Print("* GEMM: "); //--- check if(gemmerrors) Print("FAILED"); else Print("OK"); Print("* TRANS: "); //--- check if(transerrors) Print("FAILED"); else Print("OK"); Print("* RANK1: "); //--- check if(rank1errors) Print("FAILED"); else Print("OK"); Print("* MV: "); //--- check if(mverrors) Print("FAILED"); else Print("OK"); Print("* COPY: "); //--- check if(copyerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestAblasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a, const int ai1, const int ai2, const int aj1, const int aj2, const bool transa, CMatrixDouble &b, const int bi1, const int bi2, const int bj1, const int bj2, const bool transb, const double alpha, CMatrixDouble &c, const int ci1, const int ci2, const int cj1, const int cj2, const double beta) { //--- create variables int arows=0; int acols=0; int brows=0; int bcols=0; int i=0; int j=0; int k=0; int l=0; int r=0; double v=0; int i_=0; int i1_=0; //--- create arrays double x1[]; double x2[]; //--- Setup if(!transa) { arows=ai2-ai1+1; acols=aj2-aj1+1; } else { arows=aj2-aj1+1; acols=ai2-ai1+1; } //--- check if(!transb) { brows=bi2-bi1+1; bcols=bj2-bj1+1; } else { brows=bj2-bj1+1; bcols=bi2-bi1+1; } //--- check if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!")) return; //--- check if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0) return; //--- change values l=arows; r=bcols; k=acols; //--- allocation ArrayResize(x1,k+1); ArrayResize(x2,k+1); //--- calculation for(i=1;i<=l;i++) { for(j=1;j<=r;j++) { //--- check if(!transa) { //--- check if(!transb) { //--- change values i1_=aj1-bi1; v=0.0; for(i_=bi1;i_<=bi2;i_++) v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_]; } else { //--- change values i1_=aj1-bj1; v=0.0; for(i_=bj1;i_<=bj2;i_++) v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_]; } } else { //--- check if(!transb) { //--- change values i1_=ai1-bi1; v=0.0; for(i_=bi1;i_<=bi2;i_++) v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1]; } else { //--- change values i1_=ai1-bj1; v=0.0; for(i_=bj1;i_<=bj2;i_++) v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1]; } } //--- check if(beta==0.0) c[ci1+i-1].Set(cj1+j-1,alpha*v); else c[ci1+i-1].Set(cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v); } } } //+------------------------------------------------------------------+ //| ?Matrix????TRSM tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestTrsM(const int minn,const int maxn) { //--- create variables bool result; int n=0; int m=0; int mx=0; int i=0; int j=0; int optype=0; int uppertype=0; int unittype=0; int xoffsi=0; int xoffsj=0; int aoffsitype=0; int aoffsjtype=0; int aoffsi=0; int aoffsj=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrxl; CMatrixDouble refrxr; CMatrixComplex refca; CMatrixComplex refcxl; CMatrixComplex refcxr; CMatrixDouble ra; CMatrixComplex ca; CMatrixDouble rxr1; CMatrixDouble rxl1; CMatrixComplex cxr1; CMatrixComplex cxl1; CMatrixDouble rxr2; CMatrixDouble rxl2; CMatrixComplex cxr2; CMatrixComplex cxl2; //--- initialization threshold=CMath::Sqr(maxn)*100*CMath::m_machineepsilon; result=false; //--- calculation for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomReal()>0.5) m=mx; else n=mx; //--- Initialize RefRA/RefCA by random matrices whose upper //--- and lower triangle submatrices are non-degenerate //--- well-conditioned matrices. //--- Matrix size is 2Mx2M (four copies of same MxM matrix //--- to test different offsets) refra.Resize(2*m,2*m); for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) refra[i].Set(j,0.2*CMath::RandomReal()-0.1); } for(i=0;i<=m-1;i++) refra[i].Set(i,(2*CMath::RandomInteger(1)-1)*(2*m+CMath::RandomReal())); for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { refra[i+m].Set(j,refra[i][j]); refra[i].Set(j+m,refra[i][j]); refra[i+m].Set(j+m,refra[i][j]); } } //--- allocation refca.Resize(2*m,2*m); //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { refca[i].SetRe(j,0.2*CMath::RandomReal()-0.1); refca[i].SetIm(j,0.2*CMath::RandomReal()-0.1); } } //--- change values for(i=0;i<=m-1;i++) { refca[i].SetRe(i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal())); refca[i].SetIm(i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal())); } //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { refca[i+m].Set(j,refca[i][j]); refca[i].Set(j+m,refca[i][j]); refca[i+m].Set(j+m,refca[i][j]); } } //--- Generate random XL/XR. //--- XR is NxM matrix (matrix for 'Right' subroutines) //--- XL is MxN matrix (matrix for 'Left' subroutines) refrxr.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) refrxr[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation refrxl.Resize(m,n); for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) refrxl[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation refcxr.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { refcxr[i].SetRe(j,2*CMath::RandomReal()-1); refcxr[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- allocation refcxl.Resize(m,n); for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { refcxl[i].SetRe(j,2*CMath::RandomReal()-1); refcxl[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- test different types of operations,offsets,and so on... //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. ra.Resize(2*m,2*m); rxr1.Resize(n,m); rxr2.Resize(n,m); rxl1.Resize(m,n); rxl2.Resize(m,n); ca.Resize(2*m,2*m); cxr1.Resize(n,m); cxr2.Resize(n,m); cxl1.Resize(m,n); cxl2.Resize(m,n); //--- initialization optype=CMath::RandomInteger(3); uppertype=CMath::RandomInteger(2); unittype=CMath::RandomInteger(2); xoffsi=CMath::RandomInteger(2); xoffsj=CMath::RandomInteger(2); aoffsitype=CMath::RandomInteger(2); aoffsjtype=CMath::RandomInteger(2); aoffsi=m*aoffsitype; aoffsj=m*aoffsjtype; //--- copy A,XR,XL (fill unused parts with random garbage) for(i=0;i<=2*m-1;i++) { for(j=0;j<=2*m-1;j++) { //--- check if(((i>=aoffsi && i=aoffsj) && j=xoffsi && j>=xoffsj) { cxr1[i].Set(j,refcxr[i][j]); cxr2[i].Set(j,refcxr[i][j]); rxr1[i].Set(j,refrxr[i][j]); rxr2[i].Set(j,refrxr[i][j]); } else { cxr1[i].Set(j,CMath::RandomReal()); cxr2[i].Set(j,cxr1[i][j]); rxr1[i].Set(j,CMath::RandomReal()); rxr2[i].Set(j,rxr1[i][j]); } } } //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i>=xoffsi && j>=xoffsj) { cxl1[i].Set(j,refcxl[i][j]); cxl2[i].Set(j,refcxl[i][j]); rxl1[i].Set(j,refrxl[i][j]); rxl2[i].Set(j,refrxl[i][j]); } else { cxl1[i].Set(j,CMath::RandomReal()); cxl2[i].Set(j,cxl1[i][j]); rxl1[i].Set(j,CMath::RandomReal()); rxl2[i].Set(j,rxl1[i][j]); } } } //--- Test CXR CAblas::CMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr1,xoffsi,xoffsj); RefCMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr2,xoffsi,xoffsj); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) result=result || CMath::AbsComplex(cxr1[i][j]-cxr2[i][j])>threshold; } //--- Test CXL CAblas::CMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl1,xoffsi,xoffsj); RefCMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl2,xoffsi,xoffsj); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) result=result || CMath::AbsComplex(cxl1[i][j]-cxl2[i][j])>threshold; } //--- check if(optype<2) { //--- Test RXR CAblas::RMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr1,xoffsi,xoffsj); RefRMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr2,xoffsi,xoffsj); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) result=result || MathAbs(rxr1[i][j]-rxr2[i][j])>threshold; } //--- Test RXL CAblas::RMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl1,xoffsi,xoffsj); RefRMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl2,xoffsi,xoffsj); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) result=result || MathAbs(rxl1[i][j]-rxl2[i][j])>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| SYRK tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestSyrk(const int minn,const int maxn) { //--- create variables bool result; int n=0; int k=0; int mx=0; int i=0; int j=0; int uppertype=0; int xoffsi=0; int xoffsj=0; int aoffsitype=0; int aoffsjtype=0; int aoffsi=0; int aoffsj=0; int alphatype=0; int betatype=0; double alpha=0; double beta=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrc; CMatrixComplex refca; CMatrixComplex refcc; CMatrixDouble ra1; CMatrixDouble ra2; CMatrixComplex ca1; CMatrixComplex ca2; CMatrixDouble rc; CMatrixDouble rct; CMatrixComplex cc; CMatrixComplex cct; //--- initialization threshold=maxn*100*CMath::m_machineepsilon; result=false; for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX k=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomReal()>0.5) k=mx; else n=mx; //--- Initialize RefRA/RefCA by random Hermitian matrices, //--- RefRC/RefCC by random matrices //--- RA/CA size is 2Nx2N (four copies of same NxN matrix //--- to test different offsets) refra.Resize(2*n,2*n); refca.Resize(2*n,2*n); for(i=0;i<=n-1;i++) { refra[i].Set(i,2*CMath::RandomReal()-1); refca[i].Set(i,2*CMath::RandomReal()-1); for(j=i+1;j<=n-1;j++) { refra[i].Set(j,2*CMath::RandomReal()-1); refca[i].SetRe(j,2*CMath::RandomReal()-1); refca[i].SetIm(j,2*CMath::RandomReal()-1); refra[j].Set(i,refra[i][j]); refca[j].Set(i,CMath::Conj(refca[i][j])); } } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { refra[i+n].Set(j,refra[i][j]); refra[i].Set(j+n,refra[i][j]); refra[i+n].Set(j+n,refra[i][j]); refca[i+n].Set(j,refca[i][j]); refca[i].Set(j+n,refca[i][j]); refca[i+n].Set(j+n,refca[i][j]); } } //--- allocation refrc.Resize(n,k); refcc.Resize(n,k); for(i=0;i<=n-1;i++) { for(j=0;j<=k-1;j++) { refrc[i].Set(j,2*CMath::RandomReal()-1); refcc[i].SetRe(j,2*CMath::RandomReal()-1); refcc[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- test different types of operations,offsets,and so on... //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. ra1.Resize(2*n,2*n); ra2.Resize(2*n,2*n); ca1.Resize(2*n,2*n); ca2.Resize(2*n,2*n); rc.Resize(n,k); rct.Resize(k,n); cc.Resize(n,k); cct.Resize(k,n); //--- initialization uppertype=CMath::RandomInteger(2); xoffsi=CMath::RandomInteger(2); xoffsj=CMath::RandomInteger(2); aoffsitype=CMath::RandomInteger(2); aoffsjtype=CMath::RandomInteger(2); alphatype=CMath::RandomInteger(2); betatype=CMath::RandomInteger(2); aoffsi=n*aoffsitype; aoffsj=n*aoffsjtype; alpha=alphatype*(2*CMath::RandomReal()-1); beta=betatype*(2*CMath::RandomReal()-1); //--- copy A,C (fill unused parts with random garbage) for(i=0;i<=2*n-1;i++) { for(j=0;j<=2*n-1;j++) { //--- check if(((i>=aoffsi && i=aoffsj) && j=xoffsi && j>=xoffsj) { rc[i].Set(j,refrc[i][j]); rct[j].Set(i,refrc[i][j]); cc[i].Set(j,refcc[i][j]); cct[j].Set(i,refcc[i][j]); } else { rc[i].Set(j,CMath::RandomReal()); rct[j].Set(i,rc[i][j]); cc[i].Set(j,CMath::RandomReal()); cct[j].Set(i,cct[j][i]); } } } //--- Test complex //--- Only one of transform types is selected and tested if(CMath::RandomReal()>0.5) { CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca1,aoffsi,aoffsj,uppertype==0); RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca2,aoffsi,aoffsj,uppertype==0); } else { CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca1,aoffsi,aoffsj,uppertype==0); RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca2,aoffsi,aoffsj,uppertype==0); } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) result=result || CMath::AbsComplex(ca1[i][j]-ca2[i][j])>threshold; } //--- Test real //--- Only one of transform types is selected and tested if(CMath::RandomReal()>0.5) { CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra1,aoffsi,aoffsj,uppertype==0); RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra2,aoffsi,aoffsj,uppertype==0); } else { CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra1,aoffsi,aoffsj,uppertype==0); RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra2,aoffsi,aoffsj,uppertype==0); } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) result=result || MathAbs(ra1[i][j]-ra2[i][j])>threshold; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| GEMM tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestGemm(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int k=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int aoptype=0; int aoptyper=0; int boffsi=0; int boffsj=0; int boptype=0; int boptyper=0; int coffsi=0; int coffsj=0; double alphar=0; double betar=0; complex alphac=0; complex betac=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrb; CMatrixDouble refrc; CMatrixComplex refca; CMatrixComplex refcb; CMatrixComplex refcc; CMatrixDouble rc1; CMatrixDouble rc2; CMatrixComplex cc1; CMatrixComplex cc2; //--- initialization threshold=maxn*100*CMath::m_machineepsilon; result=false; //--- calculation for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N/K in [1,MX] such that max(M,N,K)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); k=1+CMath::RandomInteger(mx); i=CMath::RandomInteger(3); //--- check if(i==0) m=mx; //--- check if(i==1) n=mx; //--- check if(i==2) k=mx; //--- Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1) refra.Resize(maxn+1,maxn+1); refrb.Resize(maxn+1,maxn+1); refrc.Resize(maxn+1,maxn+1); refca.Resize(maxn+1,maxn+1); refcb.Resize(maxn+1,maxn+1); refcc.Resize(maxn+1,maxn+1); //--- change values for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) { refra[i].Set(j,2*CMath::RandomReal()-1); refrb[i].Set(j,2*CMath::RandomReal()-1); refrc[i].Set(j,2*CMath::RandomReal()-1); refca[i].SetRe(j,2*CMath::RandomReal()-1); refca[i].SetIm(j,2*CMath::RandomReal()-1); refcb[i].SetRe(j,2*CMath::RandomReal()-1); refcb[i].SetIm(j,2*CMath::RandomReal()-1); refcc[i].SetRe(j,2*CMath::RandomReal()-1); refcc[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- test different types of operations,offsets,and so on... //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. rc1.Resize(maxn+1,maxn+1); rc2.Resize(maxn+1,maxn+1); cc1.Resize(maxn+1,maxn+1); cc2.Resize(maxn+1,maxn+1); //--- initialization aoffsi=CMath::RandomInteger(2); aoffsj=CMath::RandomInteger(2); aoptype=CMath::RandomInteger(3); aoptyper=CMath::RandomInteger(2); boffsi=CMath::RandomInteger(2); boffsj=CMath::RandomInteger(2); boptype=CMath::RandomInteger(3); boptyper=CMath::RandomInteger(2); coffsi=CMath::RandomInteger(2); coffsj=CMath::RandomInteger(2); alphar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1); betar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1); //--- check if(CMath::RandomReal()>0.5) { alphac.re=2*CMath::RandomReal()-1; alphac.im=2*CMath::RandomReal()-1; } else alphac=0; //--- check if(CMath::RandomReal()>0.5) { betac.re=2*CMath::RandomReal()-1; betac.im=2*CMath::RandomReal()-1; } else betac=0; //--- copy C for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) { rc1[i].Set(j,refrc[i][j]); rc2[i].Set(j,refrc[i][j]); cc1[i].Set(j,refcc[i][j]); cc2[i].Set(j,refcc[i][j]); } } //--- Test complex CAblas::CMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc1,coffsi,coffsj); RefCMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc2,coffsi,coffsj); //--- search errors for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) result=result || CMath::AbsComplex(cc1[i][j]-cc2[i][j])>threshold; } //--- Test real CAblas::RMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc1,coffsi,coffsj); RefRMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc2,coffsi,coffsj); //--- search errors for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) result=result || MathAbs(rc1[i][j]-rc2[i][j])>threshold; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| transpose tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestTrans(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int boffsi=0; int boffsj=0; double v1=0; double v2=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrb; CMatrixComplex refca; CMatrixComplex refcb; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX //--- Generate random V1 and V2 which are used to fill //--- RefRB/RefCB with control values. m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- change values v1=CMath::RandomReal(); v2=CMath::RandomReal(); //--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1) //--- Fill B with control values refra.Resize(maxn+1,maxn+1); refrb.Resize(maxn+1,maxn+1); refca.Resize(maxn+1,maxn+1); refcb.Resize(maxn+1,maxn+1); //--- change values for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) { refra[i].Set(j,2*CMath::RandomReal()-1); refca[i].SetRe(j,2*CMath::RandomReal()-1); refca[i].SetIm(j,2*CMath::RandomReal()-1); refrb[i].Set(j,i*v1+j*v2); refcb[i].Set(j,i*v1+j*v2); } } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(2); aoffsj=CMath::RandomInteger(2); boffsi=CMath::RandomInteger(2); boffsj=CMath::RandomInteger(2); //--- function call CAblas::RMatrixTranspose(m,n,refra,aoffsi,aoffsj,refrb,boffsi,boffsj); //--- search errors for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) { //--- check if(((i=boffsi+n) || j=boffsj+m) result=result || MathAbs(refrb[i][j]-(v1*i+v2*j))>threshold; else result=result || MathAbs(refrb[i][j]-refra[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold; } } //--- function call CAblas::CMatrixTranspose(m,n,refca,aoffsi,aoffsj,refcb,boffsi,boffsj); //--- search errors for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) { //--- check if(((i=boffsi+n) || j=boffsj+m) result=result || CMath::AbsComplex(refcb[i][j]-(v1*i+v2*j))>threshold; else result=result || CMath::AbsComplex(refcb[i][j]-refca[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| rank-1tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestRank1(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int uoffs=0; int voffs=0; double threshold=0; //--- create arrays double ru[]; double rv[]; complex cu[]; complex cv[]; //--- create matrix CMatrixDouble refra; CMatrixDouble refrb; CMatrixComplex refca; CMatrixComplex refcb; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1) //--- Fill B with control values refra.Resize(maxn+maxn,maxn+maxn); refrb.Resize(maxn+maxn,maxn+maxn); refca.Resize(maxn+maxn,maxn+maxn); refcb.Resize(maxn+maxn,maxn+maxn); //--- change values for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { refra[i].Set(j,2*CMath::RandomReal()-1); refca[i].SetRe(j,2*CMath::RandomReal()-1); refca[i].SetIm(j,2*CMath::RandomReal()-1); refrb[i].Set(j,refra[i][j]); refcb[i].Set(j,refca[i][j]); } } //--- allocation ArrayResize(ru,2*m); ArrayResize(cu,2*m); //--- change values for(i=0;i<=2*m-1;i++) { ru[i]=2*CMath::RandomReal()-1; cu[i].re=2*CMath::RandomReal()-1; cu[i].im=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(rv,2*n); ArrayResize(cv,2*n); //--- change values for(i=0;i<=2*n-1;i++) { rv[i]=2*CMath::RandomReal()-1; cv[i].re=2*CMath::RandomReal()-1; cv[i].im=2*CMath::RandomReal()-1; } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(maxn); aoffsj=CMath::RandomInteger(maxn); uoffs=CMath::RandomInteger(m); voffs=CMath::RandomInteger(n); //--- function call CAblas::CMatrixRank1(m,n,refca,aoffsi,aoffsj,cu,uoffs,cv,voffs); //--- search errors for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { //--- check if(((i=aoffsi+m) || j=aoffsj+n) result=result || CMath::AbsComplex(refca[i][j]-refcb[i][j])>threshold; else result=result || CMath::AbsComplex(refca[i][j]-(refcb[i][j]+cu[i-aoffsi+uoffs]*cv[j-aoffsj+voffs]))>threshold; } } //--- function call CAblas::RMatrixRank1(m,n,refra,aoffsi,aoffsj,ru,uoffs,rv,voffs); //--- search errors for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { //--- check if(((i=aoffsi+m) || j=aoffsj+n) result=result || MathAbs(refra[i][j]-refrb[i][j])>threshold; else result=result || MathAbs(refra[i][j]-(refrb[i][j]+ru[i-aoffsi+uoffs]*rv[j-aoffsj+voffs]))>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| MV tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestMV(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int xoffs=0; int yoffs=0; int opca=0; int opra=0; double threshold=0; double rv1=0; double rv2=0; complex cv1=0; complex cv2=0; int i_=0; int i1_=0; //--- create arrays double rx[]; double ry[]; complex cx[]; complex cy[]; //--- create matrix CMatrixDouble refra; CMatrixComplex refca; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN) //--- Initialize X by random vector with size (MaxN+MaxN) //--- Fill Y by control values refra.Resize(maxn+maxn,maxn+maxn); refca.Resize(maxn+maxn,maxn+maxn); //--- change values for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { refra[i].Set(j,2*CMath::RandomReal()-1); refca[i].SetRe(j,2*CMath::RandomReal()-1); refca[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- allocation ArrayResize(rx,2*maxn); ArrayResize(cx,2*maxn); ArrayResize(ry,2*maxn); ArrayResize(cy,2*maxn); //--- change values for(i=0;i<=2*maxn-1;i++) { rx[i]=2*CMath::RandomReal()-1; cx[i].re=2*CMath::RandomReal()-1; cx[i].im=2*CMath::RandomReal()-1; ry[i]=i; cy[i]=i; } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(maxn); aoffsj=CMath::RandomInteger(maxn); xoffs=CMath::RandomInteger(maxn); yoffs=CMath::RandomInteger(maxn); opca=CMath::RandomInteger(3); opra=CMath::RandomInteger(2); //--- function call CAblas::CMatrixMVect(m,n,refca,aoffsi,aoffsj,opca,cx,xoffs,cy,yoffs); //--- search errors for(i=0;i<=2*maxn-1;i++) { //--- check if(i=yoffs+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+m) result=result || ry[i]!=i; else { rv1=ry[i]; rv2=0; //--- check if(opra==0) { //--- change values i1_=xoffs-aoffsj; rv2=0.0; for(i_=aoffsj;i_<=aoffsj+n-1;i_++) rv2+=refra[aoffsi+i-yoffs][i_]*rx[i_+i1_]; } //--- check if(opra==1) { //--- change values i1_=xoffs-aoffsi; rv2=0.0; for(i_=aoffsi;i_<=aoffsi+n-1;i_++) rv2+=refra[i_][aoffsj+i-yoffs]*rx[i_+i1_]; } result=result || MathAbs(rv1-rv2)>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| COPY tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ static bool CTestAblasUnit::TestCopy(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int boffsi=0; int boffsj=0; double threshold=0; //--- create matrix CMatrixDouble ra; CMatrixDouble rb; CMatrixComplex ca; CMatrixComplex cb; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn;mx<=maxn;mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN) //--- Initialize X by random vector with size (MaxN+MaxN) //--- Fill Y by control values ra.Resize(maxn+maxn,maxn+maxn); ca.Resize(maxn+maxn,maxn+maxn); rb.Resize(maxn+maxn,maxn+maxn); cb.Resize(maxn+maxn,maxn+maxn); //--- change values for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { ra[i].Set(j,2*CMath::RandomReal()-1); ca[i].SetRe(j,2*CMath::RandomReal()-1); ca[i].SetIm(j,2*CMath::RandomReal()-1); rb[i].Set(j,1+2*i+3*j); cb[i].Set(j,1+2*i+3*j); } } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(maxn); aoffsj=CMath::RandomInteger(maxn); boffsi=CMath::RandomInteger(maxn); boffsj=CMath::RandomInteger(maxn); //--- function call CAblas::CMatrixCopy(m,n,ca,aoffsi,aoffsj,cb,boffsi,boffsj); //--- search errors for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { //--- check if(((i=boffsi+m) || j=boffsj+n) result=result || cb[i][j]!=1+2*i+3*j; else result=result || CMath::AbsComplex(ca[aoffsi+i-boffsi][aoffsj+j-boffsj]-cb[i][j])>threshold; } } //--- function call CAblas::RMatrixCopy(m,n,ra,aoffsi,aoffsj,rb,boffsi,boffsj); //--- search errors for(i=0;i<=2*maxn-1;i++) { for(j=0;j<=2*maxn-1;j++) { //--- check if(((i=boffsi+m) || j=boffsj+n) result=result || rb[i][j]!=1+2*i+3*j; else result=result || MathAbs(ra[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb[i][j])>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefCMatrixRightTrsM(const int m,const int n, CMatrixComplex &a, const int i1,const int j1, const bool isupper, const bool isunit, const int optype, CMatrixComplex &x, const int i2,const int j2) { //--- create variables int i=0; int j=0; complex vc=0; bool rupper; int i_=0; int i1_=0; //--- create array complex tx[]; //--- create matrix CMatrixComplex a1; CMatrixComplex a2; //--- check if(n*m==0) return; //--- allocation a1.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a1[i].Set(j,0); } //--- check if(isupper) { for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) a1[i].Set(j,a[i1+i][j1+j]); } } else { for(i=0;i<=n-1;i++) { for(j=0;j<=i;j++) a1[i].Set(j,a[i1+i][j1+j]); } } //--- change value rupper=isupper; //--- check if(isunit) { for(i=0;i<=n-1;i++) { a1[i].Set(i,1); } } //--- allocation a2.Resize(n,n); //--- check if(optype==0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a2[i].Set(j,a1[i][j]); } } //--- check if(optype==1) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a2[i].Set(j,a1[j][i]); } //--- change value rupper=!rupper; } //--- check if(optype==2) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a2[i].Set(j,CMath::Conj(a1[j][i])); } } //--- change value rupper=!rupper; } //--- function call InternalCMatrixTrInverse(a2,n,rupper,false); //--- allocation ArrayResize(tx,n); //--- calculation for(i=0;i<=m-1;i++) { i1_=j2; for(i_=0;i_<=n-1;i_++) tx[i_]=x[i2+i][i_+i1_]; //--- change values for(j=0;j<=n-1;j++) { vc=0.0; for(i_=0;i_<=n-1;i_++) vc+=tx[i_]*a2[i_][j]; x[i2+i].Set(j2+j,vc); } } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefCMatrixLeftTrsM(const int m,const int n, CMatrixComplex &a, const int i1,const int j1, const bool isupper, const bool isunit, const int optype, CMatrixComplex &x, const int i2,const int j2) { //--- create variables int i=0; int j=0; complex vc=0; bool rupper; int i_=0; int i1_=0; //--- create array complex tx[]; //--- create matrix CMatrixComplex a1; CMatrixComplex a2; //--- check if(n*m==0) return; //--- allocation a1.Resize(m,m); for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a1[i].Set(j,0); } //--- check if(isupper) { for(i=0;i<=m-1;i++) { for(j=i;j<=m-1;j++) a1[i].Set(j,a[i1+i][j1+j]); } } else { for(i=0;i<=m-1;i++) { for(j=0;j<=i;j++) a1[i].Set(j,a[i1+i][j1+j]); } } //--- change value rupper=isupper; //--- check if(isunit) { for(i=0;i<=m-1;i++) a1[i].Set(i,1); } //--- allocation a2.Resize(m,m); //--- check if(optype==0) { for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a2[i].Set(j,a1[i][j]); } } //--- check if(optype==1) { for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a2[i].Set(j,a1[j][i]); } //--- change value rupper=!rupper; } //--- check if(optype==2) { for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a2[i].Set(j,CMath::Conj(a1[j][i])); } //--- change value rupper=!rupper; } //--- function call InternalCMatrixTrInverse(a2,m,rupper,false); //--- allocation ArrayResize(tx,m); for(j=0;j<=n-1;j++) { i1_=i2; for(i_=0;i_<=m-1;i_++) tx[i_]=x[i_+i1_][j2+j]; //--- change values for(i=0;i<=m-1;i++) { vc=0.0; for(i_=0;i_<=m-1;i_++) vc+=a2[i][i_]*tx[i_]; x[i2+i].Set(j2+j,vc); } } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefRMatrixRightTrsM(const int m,const int n, CMatrixDouble &a, const int i1,const int j1, const bool isupper, const bool isunit, const int optype, CMatrixDouble &x, const int i2,const int j2) { //--- create variables int i=0; int j=0; double vr=0; bool rupper; int i_=0; int i1_=0; //--- create array double tx[]; //--- create matrix CMatrixDouble a1; CMatrixDouble a2; //--- check if(n*m==0) return; //--- allocation a1.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a1[i].Set(j,0); } //--- check if(isupper) { for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) a1[i].Set(j,a[i1+i][j1+j]); } } else { for(i=0;i<=n-1;i++) { for(j=0;j<=i;j++) a1[i].Set(j,a[i1+i][j1+j]); } } //--- change value rupper=isupper; //--- check if(isunit) { for(i=0;i<=n-1;i++) a1[i].Set(i,1); } //--- allocation a2.Resize(n,n); //--- check if(optype==0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a2[i].Set(j,a1[i][j]); } } //--- check if(optype==1) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a2[i].Set(j,a1[j][i]); } //--- change value rupper=!rupper; } //--- function call InternalRMatrixTrInverse(a2,n,rupper,false); //--- allocation ArrayResize(tx,n); for(i=0;i<=m-1;i++) { i1_=j2; for(i_=0;i_<=n-1;i_++) tx[i_]=x[i2+i][i_+i1_]; //--- change values for(j=0;j<=n-1;j++) { vr=0.0; for(i_=0;i_<=n-1;i_++) vr+=tx[i_]*a2[i_][j]; x[i2+i].Set(j2+j,vr); } } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefRMatrixLeftTrsM(const int m,const int n, CMatrixDouble &a, const int i1,const int j1, const bool isupper, const bool isunit, const int optype, CMatrixDouble &x, const int i2,const int j2) { //--- create variables int i=0; int j=0; double vr=0; bool rupper; int i_=0; int i1_=0; //--- create array double tx[]; //--- create matrix CMatrixDouble a1; CMatrixDouble a2; //--- check if(n*m==0) return; //--- allocation a1.Resize(m,m); for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a1[i].Set(j,0); } //--- check if(isupper) { for(i=0;i<=m-1;i++) { for(j=i;j<=m-1;j++) a1[i].Set(j,a[i1+i][j1+j]); } } else { for(i=0;i<=m-1;i++) { for(j=0;j<=i;j++) a1[i].Set(j,a[i1+i][j1+j]); } } //--- change value rupper=isupper; //--- check if(isunit) { for(i=0;i<=m-1;i++) a1[i].Set(i,1); } //--- allocation a2.Resize(m,m); //--- check if(optype==0) { for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a2[i].Set(j,a1[i][j]); } } //--- check if(optype==1) { for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) a2[i].Set(j,a1[j][i]); } //--- change value rupper=!rupper; } //--- function call InternalRMatrixTrInverse(a2,m,rupper,false); //--- allocation ArrayResize(tx,m); for(j=0;j<=n-1;j++) { i1_=i2; for(i_=0;i_<=m-1;i_++) tx[i_]=x[i_+i1_][j2+j]; //--- change values for(i=0;i<=m-1;i++) { vr=0.0; for(i_=0;i_<=m-1;i_++) vr+=a2[i][i_]*tx[i_]; x[i2+i].Set(j2+j,vr); } } } //+------------------------------------------------------------------+ //| Internal subroutine. | //| Triangular matrix inversion | //| -- LAPACK routine (version 3.0) -- | //| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., | //| Courant Institute,Argonne National Lab,and Rice University | //| February 29,1992 | //+------------------------------------------------------------------+ static bool CTestAblasUnit::InternalCMatrixTrInverse(CMatrixComplex &a, const int n, const bool isupper, const bool isunittriangular) { //--- create variables bool result; bool nounit; int i=0; int j=0; complex v=0; complex ajj=0; complex one=1; int i_=0; //--- create array complex t[]; //--- initialization result=true; //--- allocation ArrayResize(t,n); //--- Test the input parameters. nounit=!isunittriangular; //--- check if(isupper) { //--- Compute inverse of upper triangular matrix. for(j=0;j<=n-1;j++) { //--- check if(nounit) { //--- check if(a[j][j]==0) { //--- return result return(false); } //--- change values a[j].Set(j,one/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- Compute elements 1:j-1 of j-th column. if(j>0) { for(i_=0;i_<=j-1;i_++) t[i_]=a[i_][j]; for(i=0;i<=j-1;i++) { //--- check if(i+1=0;j--) { //--- check if(nounit) { //--- check if(a[j][j]==0) { //--- return result return(false); } //--- change values a[j].Set(j,one/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(j+1j+1) { v=0.0; for(i_=j+1;i_<=i-1;i_++) v+=a[i][i_]*t[i_]; } else v=0; //--- check if(nounit) a[i].Set(j,v+a[i][i]*t[i]); else a[i].Set(j,v+t[i]); } //--- change values for(i_=j+1;i_<=n-1;i_++) a[i_].Set(j,ajj*a[i_][j]); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Internal subroutine. | //| Triangular matrix inversion | //| -- LAPACK routine (version 3.0) -- | //| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., | //| Courant Institute,Argonne National Lab,and Rice University | //| February 29,1992 | //+------------------------------------------------------------------+ static bool CTestAblasUnit::InternalRMatrixTrInverse(CMatrixDouble &a, const int n, const bool isupper, const bool isunittriangular) { //--- create variables bool result; bool nounit; int i=0; int j=0; double v=0; double ajj=0; int i_=0; //--- create array double t[]; //--- initialization result=true; //--- allocation ArrayResize(t,n); //--- Test the input parameters. nounit=!isunittriangular; //--- check if(isupper) { //--- Compute inverse of upper triangular matrix. for(j=0;j<=n-1;j++) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } //--- change values a[j].Set(j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- Compute elements 1:j-1 of j-th column. if(j>0) { for(i_=0;i_<=j-1;i_++) t[i_]=a[i_][j]; for(i=0;i<=j-1;i++) { //--- check if(i=0;j--) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } //--- change values a[j].Set(j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { v=0.0; for(i_=j+1;i_<=i-1;i_++) v+=a[i][i_]*t[i_]; } else v=0; //--- check if(nounit) a[i].Set(j,v+a[i][i]*t[i]); else a[i].Set(j,v+t[i]); } //--- change values for(i_=j+1;i_<=n-1;i_++) a[i_].Set(j,ajj*a[i_][j]); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Reference SYRK subroutine. | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefCMatrixSyrk(const int n,const int k, const double alpha,CMatrixComplex &a, const int ia,const int ja, const int optypea,const double beta, CMatrixComplex &c,const int ic, const int jc,const bool isupper) { //--- create variables int i=0; int j=0; complex vc=0; int i_=0; //--- create matrix CMatrixComplex ae; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if((isupper && j>=i) || (!isupper && j<=i)) { //--- check if(beta==0.0) c[i+ic].Set(j+jc,0); else c[i+ic].Set(j+jc,c[i+ic][j+jc]*beta); } } } //--- check if(alpha==0.0) return; //--- check if(n*k>0) ae.Resize(n,k); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=k-1;j++) { //--- check if(optypea==0) ae[i].Set(j,a[ia+i][ja+j]); //--- check if(optypea==2) ae[i].Set(j,CMath::Conj(a[ia+j][ja+i])); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { vc=0; //--- check if(k>0) { vc=0.0; for(i_=0;i_<=k-1;i_++) vc+=ae[i][i_]*CMath::Conj(ae[j][i_]); } vc=vc*alpha; //--- check if(isupper && j>=i) c[ic+i].Set(jc+j,vc+c[ic+i][jc+j]); //--- check if(!isupper && j<=i) c[ic+i].Set(jc+j,vc+c[ic+i][jc+j]); } } } //+------------------------------------------------------------------+ //| Reference SYRK subroutine. | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefRMatrixSyrk(const int n,const int k, const double alpha,CMatrixDouble &a, const int ia,const int ja, const int optypea,const double beta, CMatrixDouble &c,const int ic, const int jc,const bool isupper) { //--- create variables int i=0; int j=0; double vr=0; int i_=0; //--- create matrix CMatrixDouble ae; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if((isupper && j>=i) || (!isupper && j<=i)) { //--- check if(beta==0.0) c[i+ic].Set(j+jc,0); else c[i+ic].Set(j+jc,c[i+ic][j+jc]*beta); } } } //--- check if(alpha==0.0) return; //--- check if(n*k>0) ae.Resize(n,k); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=k-1;j++) { //--- check if(optypea==0) ae[i].Set(j,a[ia+i][ja+j]); //--- check if(optypea==1) ae[i].Set(j,a[ia+j][ja+i]); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { vr=0; //--- check if(k>0) { vr=0.0; for(i_=0;i_<=k-1;i_++) vr+=ae[i][i_]*ae[j][i_]; } vr=alpha*vr; //--- check if(isupper && j>=i) c[ic+i].Set(jc+j,vr+c[ic+i][jc+j]); //--- check if(!isupper && j<=i) c[ic+i].Set(jc+j,vr+c[ic+i][jc+j]); } } } //+------------------------------------------------------------------+ //| Reference GEMM, | //| ALGLIB subroutine | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefCMatrixGemm(const int m,const int n, const int k,complex &alpha, CMatrixComplex &a,const int ia, const int ja,const int optypea, CMatrixComplex &b,const int ib, const int jb,const int optypeb, complex &beta,CMatrixComplex &c, const int ic,const int jc) { //--- create variables int i=0; int j=0; complex vc=0; int i_=0; //--- create matrix CMatrixComplex ae; CMatrixComplex be; //--- allocation ae.Resize(m,k); //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=k-1;j++) { //--- check if(optypea==0) ae[i].Set(j,a[ia+i][ja+j]); //--- check if(optypea==1) ae[i].Set(j,a[ia+j][ja+i]); //--- check if(optypea==2) ae[i].Set(j,CMath::Conj(a[ia+j][ja+i])); } } //--- allocation be.Resize(k,n); //--- change values for(i=0;i<=k-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(optypeb==0) be[i].Set(j,b[ib+i][jb+j]); //--- check if(optypeb==1) be[i].Set(j,b[ib+j][jb+i]); //--- check if(optypeb==2) be[i].Set(j,CMath::Conj(b[ib+j][jb+i])); } } //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value vc=0.0; for(i_=0;i_<=k-1;i_++) vc+=ae[i][i_]*be[i_][j]; vc=alpha*vc; //--- check if(beta!=0) vc=vc+beta*c[ic+i][jc+j]; c[ic+i].Set(jc+j,vc); } } } //+------------------------------------------------------------------+ //| Reference GEMM, | //| ALGLIB subroutine | //+------------------------------------------------------------------+ static void CTestAblasUnit::RefRMatrixGemm(const int m,const int n, const int k,const double alpha, CMatrixDouble &a,const int ia, const int ja,const int optypea, CMatrixDouble &b,const int ib, const int jb,const int optypeb, const double beta,CMatrixDouble &c, const int ic,const int jc) { //--- create variables int i=0; int j=0; double vc=0; int i_=0; //--- create matrix CMatrixDouble ae; CMatrixDouble be; //--- allocation ae.Resize(m,k); //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=k-1;j++) { //--- check if(optypea==0) ae[i].Set(j,a[ia+i][ja+j]); //--- check if(optypea==1) ae[i].Set(j,a[ia+j][ja+i]); } } //--- allocation be.Resize(k,n); //--- change values for(i=0;i<=k-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(optypeb==0) be[i].Set(j,b[ib+i][jb+j]); //--- check if(optypeb==1) be[i].Set(j,b[ib+j][jb+i]); } } //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value vc=0.0; for(i_=0;i_<=k-1;i_++) vc+=ae[i][i_]*be[i_][j]; vc=alpha*vc; //--- check if(beta!=0.0) vc=vc+beta*c[ic+i][jc+j]; c[ic+i].Set(jc+j,vc); } } } //+------------------------------------------------------------------+ //| Testing class CBaseStat | //+------------------------------------------------------------------+ class CTestBaseStatUnit { public: //--- constructor, destructor CTestBaseStatUnit(void); ~CTestBaseStatUnit(void); //--- public method static bool TestBaseStat(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestBaseStatUnit::CTestBaseStatUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestBaseStatUnit::~CTestBaseStatUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CBaseStat | //+------------------------------------------------------------------+ static bool CTestBaseStatUnit::TestBaseStat(const bool silent) { //--- create variables bool waserrors; bool s1errors; bool covcorrerrors; double threshold=0; int i=0; int j=0; int n=0; int kx=0; int ky=0; int ctype=0; int cidxx=0; int cidxy=0; double mean=0; double variance=0; double skewness=0; double kurtosis=0; double adev=0; double median=0; double pv=0; double v=0; int i_=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble mx; CMatrixDouble my; CMatrixDouble cc; CMatrixDouble cp; CMatrixDouble cs; //--- Primary settings waserrors=false; s1errors=false; covcorrerrors=false; threshold=1000*CMath::m_machineepsilon; //--- * prepare X and Y - two test samples //--- * test 1-sample coefficients n=10; //--- allocation ArrayResize(x,n); for(i=0;i<=n-1;i++) x[i]=CMath::Sqr(i); //--- function call CBaseStat::SampleMoments(x,n,mean,variance,skewness,kurtosis); //--- search errors s1errors=s1errors || MathAbs(mean-28.5)>0.001; s1errors=s1errors || MathAbs(variance-801.1667)>0.001; s1errors=s1errors || MathAbs(skewness-0.5751)>0.001; s1errors=s1errors || MathAbs(kurtosis+1.2666)>0.001; //--- function call CBaseStat::SampleAdev(x,n,adev); //--- search errors s1errors=s1errors || MathAbs(adev-23.2000)>0.001; //--- function call CBaseStat::SampleMedian(x,n,median); //--- search errors s1errors=s1errors || MathAbs(median-0.5*(16+25))>0.001; for(i=0;i<=n-1;i++) { //--- function call CBaseStat::SamplePercentile(x,n,(double)i/(double)(n-1),pv); //--- search errors s1errors=s1errors || MathAbs(pv-x[i])>0.001; } //--- function call CBaseStat::SamplePercentile(x,n,0.5,pv); //--- search errors s1errors=s1errors || MathAbs(pv-0.5*(16+25))>0.001; //--- test covariance/correlation: //--- * 2-sample coefficients //--- We generate random matrices MX and MY n=10; ArrayResize(x,n); ArrayResize(y,n); for(i=0;i<=n-1;i++) { x[i]=CMath::Sqr(i); y[i]=i; } //--- search errors covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-0.9627)>0.0001; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-1.0000)>0.0001; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-82.5000)>0.0001; for(i=0;i<=n-1;i++) { x[i]=CMath::Sqr(i-0.5*n); y[i]=i; } //--- search errors covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)+0.3676)>0.0001; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)+0.2761)>0.0001; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)+9.1667)>0.0001; //--- test covariance/correlation: //--- * matrix covariance/correlation //--- * matrix cross-covariance/cross-correlation //--- We generate random matrices MX and MY which contain KX (KY) //--- columns,all except one are random,one of them is constant. //--- We test that function (a) do not crash on constant column, //--- and (b) return variances/correlations that are exactly zero //--- for this column. //--- CType control variable controls type of constant: 0 - no constant //--- column,1 - zero column,2 - nonzero column with value whose //--- binary representation contains many non-zero bits. Using such //--- type of constant column we are able to ensure than even in the //--- presense of roundoff error functions correctly detect constant //--- columns. for(n=0;n<=10;n++) { //--- check if(n>0) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); } //--- calculation for(ctype=0;ctype<=2;ctype++) { for(kx=1;kx<=10;kx++) { for(ky=1;ky<=10;ky++) { //--- Fill matrices,add constant column (when CType=1 or=2) cidxx=-1; cidxy=-1; //--- check if(n>0) { //--- allocation mx.Resize(n,kx); my.Resize(n,ky); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=kx-1;j++) mx[i].Set(j,2*CMath::RandomReal()-1); for(j=0;j<=ky-1;j++) my[i].Set(j,2*CMath::RandomReal()-1); } //--- check if(ctype==1) { cidxx=CMath::RandomInteger(kx); cidxy=CMath::RandomInteger(ky); //--- change values for(i=0;i<=n-1;i++) { mx[i].Set(cidxx,0.0); my[i].Set(cidxy,0.0); } } //--- check if(ctype==2) { cidxx=CMath::RandomInteger(kx); cidxy=CMath::RandomInteger(ky); //--- change values v=MathSqrt((CMath::RandomInteger(kx)+1)/(double)kx); for(i=0;i<=n-1;i++) { mx[i].Set(cidxx,v); my[i].Set(cidxy,v); } } } //--- test covariance/correlation matrix using //--- 2-sample functions as reference point. //--- We also test that coefficients for constant variables //--- are exactly zero. CBaseStat::CovM(mx,n,kx,cc); CBaseStat::PearsonCorrM(mx,n,kx,cp); CBaseStat::SpearmanCorrM(mx,n,kx,cs); for(i=0;i<=kx-1;i++) { for(j=0;j<=kx-1;j++) { //--- check if(n>0) { for(i_=0;i_<=n-1;i_++) { x[i_]=mx[i_][i]; } for(i_=0;i_<=n-1;i_++) { y[i_]=mx[i_][j]; } } //--- search errors covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-cc[i][j])>threshold; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp[i][j])>threshold; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs[i][j])>threshold; } } //--- check if(ctype!=0 && n>0) { for(i=0;i<=kx-1;i++) { //--- search errors covcorrerrors=covcorrerrors || cc[i][cidxx]!=0.0; covcorrerrors=covcorrerrors || cc[cidxx][i]!=0.0; covcorrerrors=covcorrerrors || cp[i][cidxx]!=0.0; covcorrerrors=covcorrerrors || cp[cidxx][i]!=0.0; covcorrerrors=covcorrerrors || cs[i][cidxx]!=0.0; covcorrerrors=covcorrerrors || cs[cidxx][i]!=0.0; } } //--- test cross-covariance/cross-correlation matrix using //--- 2-sample functions as reference point. //--- We also test that coefficients for constant variables //--- are exactly zero. CBaseStat::CovM2(mx,my,n,kx,ky,cc); CBaseStat::PearsonCorrM2(mx,my,n,kx,ky,cp); CBaseStat::SpearmanCorrM2(mx,my,n,kx,ky,cs); for(i=0;i<=kx-1;i++) { for(j=0;j<=ky-1;j++) { //--- check if(n>0) { for(i_=0;i_<=n-1;i_++) x[i_]=mx[i_][i]; for(i_=0;i_<=n-1;i_++) y[i_]=my[i_][j]; } //--- search errors covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-cc[i][j])>threshold; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp[i][j])>threshold; covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs[i][j])>threshold; } } //--- check if(ctype!=0 && n>0) { for(i=0;i<=kx-1;i++) { //--- search errors covcorrerrors=covcorrerrors || cc[i][cidxy]!=0.0; covcorrerrors=covcorrerrors || cp[i][cidxy]!=0.0; covcorrerrors=covcorrerrors || cs[i][cidxy]!=0.0; } for(j=0;j<=ky-1;j++) { //--- search errors covcorrerrors=covcorrerrors || cc[cidxx][j]!=0.0; covcorrerrors=covcorrerrors || cp[cidxx][j]!=0.0; covcorrerrors=covcorrerrors || cs[cidxx][j]!=0.0; } } } } } } //--- Final report waserrors=s1errors || covcorrerrors; //--- check if(!silent) { Print("DESC.STAT TEST"); Print("TOTAL RESULTS: "); //--- check if(!waserrors) Print("OK"); else Print("FAILED"); Print("* 1-SAMPLE FUNCTIONALITY: "); //--- check if(!s1errors) Print("OK"); else Print("FAILED"); Print("* CORRELATION/COVARIATION: "); //--- check if(!covcorrerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CBdSS | //+------------------------------------------------------------------+ class CTestBdSSUnit { public: CTestBdSSUnit(void); ~CTestBdSSUnit(void); static bool TestBdSS(const bool silent); private: static void Unset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static void Unset1DI(int &a[]); static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestBdSSUnit::CTestBdSSUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestBdSSUnit::~CTestBdSSUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CBdSS | //+------------------------------------------------------------------+ static bool CTestBdSSUnit::TestBdSS(const bool silent) { //--- create variables int n=0; int i=0; int j=0; int pass=0; int passcount=0; int maxn=0; int maxnq=0; int tiecount=0; int c1=0; int c0=0; int ni=0; int nc=0; double pal=0; double pbl=0; double par=0; double pbr=0; double cve=0; double cvr=0; int info=0; double threshold=0; double rms=0; double cvrms=0; bool waserrors; bool tieserrors; bool split2errors; bool optimalsplitkerrors; bool splitkerrors; //--- create arrays double a[]; double a0[]; double at[]; double thresholds[]; int c[]; int p1[]; int p2[]; int ties[]; int pt1[]; int pt2[]; double tmp[]; double sortrbuf[]; double sortrbuf2[]; int sortibuf[]; int tiebuf[]; int cntbuf[]; //--- create matrix CMatrixDouble p; //--- initialization waserrors=false; tieserrors=false; split2errors=false; splitkerrors=false; optimalsplitkerrors=false; maxn=100; maxnq=49; passcount=10; //--- Test ties for(n=1;n<=maxn;n++) { for(pass=1;pass<=passcount;pass++) { //--- untied data,test DSTie Unset1DI(p1); Unset1DI(p2); Unset1DI(pt1); Unset1DI(pt2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(at,n); ArrayResize(tmp,n); //--- change values a[0]=2*CMath::RandomReal()-1; tmp[0]=CMath::RandomReal(); for(i=1;i<=n-1;i++) { //--- A is randomly permuted a[i]=a[i-1]+0.1*CMath::RandomReal()+0.1; tmp[i]=CMath::RandomReal(); } //--- function call CTSort::TagSortFastR(tmp,a,sortrbuf,sortrbuf2,n); for(i=0;i<=n-1;i++) { a0[i]=a[i]; at[i]=a[i]; } //--- function call CBdSS::DSTie(a0,n,ties,tiecount,p1,p2); //--- function call CTSort::TagSort(at,n,pt1,pt2); //--- search errors for(i=0;i<=n-1;i++) { tieserrors=tieserrors || p1[i]!=pt1[i]; tieserrors=tieserrors || p2[i]!=pt2[i]; } tieserrors=tieserrors || tiecount!=n; //--- check if(tiecount==n) { for(i=0;i<=n;i++) tieserrors=tieserrors || ties[i]!=i; } //--- tied data,test DSTie Unset1DI(p1); Unset1DI(p2); Unset1DI(pt1); Unset1DI(pt2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(at,n); //--- change values c1=0; c0=0; for(i=0;i<=n-1;i++) { a[i]=CMath::RandomInteger(2); //--- check if(a[i]==0.0) c0=c0+1; else c1=c1+1; a0[i]=a[i]; at[i]=a[i]; } //--- function call CBdSS::DSTie(a0,n,ties,tiecount,p1,p2); //--- function call CTSort::TagSort(at,n,pt1,pt2); //--- search errors for(i=0;i<=n-1;i++) { tieserrors=tieserrors || p1[i]!=pt1[i]; tieserrors=tieserrors || p2[i]!=pt2[i]; } //--- check if(c0==0 || c1==0) { //--- search errors tieserrors=tieserrors || tiecount!=1; //--- check if(tiecount==1) { tieserrors=tieserrors || ties[0]!=0; tieserrors=tieserrors || ties[1]!=n; } } else { //--- search errors tieserrors=tieserrors || tiecount!=2; //--- check if(tiecount==2) { tieserrors=tieserrors || ties[0]!=0; tieserrors=tieserrors || ties[1]!=c0; tieserrors=tieserrors || ties[2]!=n; } } } } //--- split-2 //--- General tests for different N's for(n=1;n<=maxn;n++) { //--- allocation ArrayResize(a,n); ArrayResize(c,n); //--- one-tie test if(n%2==0) { for(i=0;i<=n-1;i++) { a[i]=n; c[i]=i%2; } //--- function call CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve); //--- check if(info!=-3) { split2errors=true; continue; } } //--- two-tie test //--- test #1 if(n>1) { for(i=0;i<=n-1;i++) { a[i]=i/((n+1)/2); c[i]=i/((n+1)/2); } //--- function call CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve); //--- check if(info!=1) { split2errors=true; continue; } //--- search errors split2errors=split2errors || MathAbs(threshold-0.5)>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(pal-1)>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(pbl-0)>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(par-0)>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(pbr-1)>100*CMath::m_machineepsilon; } } //--- Special "CREDIT"-test (transparency coefficient) n=110; //--- allocation ArrayResize(a,n); ArrayResize(c,n); //--- initialization a[0]=0.000; c[0]=0; a[1]=0.000; c[1]=0; a[2]=0.000; c[2]=0; a[3]=0.000; c[3]=0; a[4]=0.000; c[4]=0; a[5]=0.000; c[5]=0; a[6]=0.000; c[6]=0; a[7]=0.000; c[7]=1; a[8]=0.000; c[8]=0; a[9]=0.000; c[9]=1; a[10]=0.000; c[10]=0; a[11]=0.000; c[11]=0; a[12]=0.000; c[12]=0; a[13]=0.000; c[13]=0; a[14]=0.000; c[14]=0; a[15]=0.000; c[15]=0; a[16]=0.000; c[16]=0; a[17]=0.000; c[17]=0; a[18]=0.000; c[18]=0; a[19]=0.000; c[19]=0; a[20]=0.000; c[20]=0; a[21]=0.000; c[21]=0; a[22]=0.000; c[22]=1; a[23]=0.000; c[23]=0; a[24]=0.000; c[24]=0; a[25]=0.000; c[25]=0; a[26]=0.000; c[26]=0; a[27]=0.000; c[27]=1; a[28]=0.000; c[28]=0; a[29]=0.000; c[29]=1; a[30]=0.000; c[30]=0; a[31]=0.000; c[31]=1; a[32]=0.000; c[32]=0; a[33]=0.000; c[33]=1; a[34]=0.000; c[34]=0; a[35]=0.030; c[35]=0; a[36]=0.030; c[36]=0; a[37]=0.050; c[37]=0; a[38]=0.070; c[38]=1; a[39]=0.110; c[39]=0; a[40]=0.110; c[40]=1; a[41]=0.120; c[41]=0; a[42]=0.130; c[42]=0; a[43]=0.140; c[43]=0; a[44]=0.140; c[44]=0; a[45]=0.140; c[45]=0; a[46]=0.150; c[46]=0; a[47]=0.150; c[47]=0; a[48]=0.170; c[48]=0; a[49]=0.190; c[49]=1; a[50]=0.200; c[50]=0; a[51]=0.200; c[51]=0; a[52]=0.250; c[52]=0; a[53]=0.250; c[53]=0; a[54]=0.260; c[54]=0; a[55]=0.270; c[55]=0; a[56]=0.280; c[56]=0; a[57]=0.310; c[57]=0; a[58]=0.310; c[58]=0; a[59]=0.330; c[59]=0; a[60]=0.330; c[60]=0; a[61]=0.340; c[61]=0; a[62]=0.340; c[62]=0; a[63]=0.370; c[63]=0; a[64]=0.380; c[64]=1; a[65]=0.380; c[65]=0; a[66]=0.410; c[66]=0; a[67]=0.460; c[67]=0; a[68]=0.520; c[68]=0; a[69]=0.530; c[69]=0; a[70]=0.540; c[70]=0; a[71]=0.560; c[71]=0; a[72]=0.560; c[72]=0; a[73]=0.570; c[73]=0; a[74]=0.600; c[74]=0; a[75]=0.600; c[75]=0; a[76]=0.620; c[76]=0; a[77]=0.650; c[77]=0; a[78]=0.660; c[78]=0; a[79]=0.680; c[79]=0; a[80]=0.700; c[80]=0; a[81]=0.750; c[81]=0; a[82]=0.770; c[82]=0; a[83]=0.770; c[83]=0; a[84]=0.770; c[84]=0; a[85]=0.790; c[85]=0; a[86]=0.810; c[86]=0; a[87]=0.840; c[87]=0; a[88]=0.860; c[88]=0; a[89]=0.870; c[89]=0; a[90]=0.890; c[90]=0; a[91]=0.900; c[91]=1; a[92]=0.900; c[92]=0; a[93]=0.910; c[93]=0; a[94]=0.940; c[94]=0; a[95]=0.950; c[95]=0; a[96]=0.952; c[96]=0; a[97]=0.970; c[97]=0; a[98]=0.970; c[98]=0; a[99]=0.980; c[99]=0; a[100]=1.000; c[100]=0; a[101]=1.000; c[101]=0; a[102]=1.000; c[102]=0; a[103]=1.000; c[103]=0; a[104]=1.000; c[104]=0; a[105]=1.020; c[105]=0; a[106]=1.090; c[106]=0; a[107]=1.130; c[107]=0; a[108]=1.840; c[108]=0; a[109]=2.470; c[109]=0; //--- function call CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve); //--- check if(info!=1) split2errors=true; else { //--- search errors split2errors=split2errors || MathAbs(threshold-0.195)>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(pal-0.80)>0.02; split2errors=split2errors || MathAbs(pbl-0.20)>0.02; split2errors=split2errors || MathAbs(par-0.97)>0.02; split2errors=split2errors || MathAbs(pbr-0.03)>0.02; } //--- split-2 fast //--- General tests for different N's for(n=1;n<=maxn;n++) { //--- allocation ArrayResize(a,n); ArrayResize(c,n); ArrayResize(tiebuf,n+1); ArrayResize(cntbuf,4); //--- one-tie test if(n%2==0) { for(i=0;i<=n-1;i++) { a[i]=n; c[i]=i%2; } //--- function call CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms); //--- check if(info!=-3) { split2errors=true; continue; } } //--- two-tie test //--- test #1 if(n>1) { for(i=0;i<=n-1;i++) { a[i]=i/((n+1)/2); c[i]=i/((n+1)/2); } //--- function call CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms); //--- check if(info!=1) { split2errors=true; continue; } //--- search errors split2errors=split2errors || MathAbs(threshold-0.5)>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(rms-0)>100*CMath::m_machineepsilon; //--- check if(n==2) split2errors=split2errors || MathAbs(cvrms-0.5)>100*CMath::m_machineepsilon; else { //--- check if(n==3) split2errors=split2errors || MathAbs(cvrms-MathSqrt((2*0+2*0+2*0.25)/6))>100*CMath::m_machineepsilon; else split2errors=split2errors || MathAbs(cvrms)>100*CMath::m_machineepsilon; } } } //--- special tests n=10; //--- allocation ArrayResize(a,n); ArrayResize(c,n); ArrayResize(tiebuf,n+1); ArrayResize(cntbuf,2*3); //--- change values for(i=0;i<=n-1;i++) { a[i]=i; //--- check if(i<=n-3) c[i]=0; else c[i]=i-(n-3); } //--- function call CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,3,0.00,info,threshold,rms,cvrms); //--- check if(info!=1) split2errors=true; else { //--- search errors split2errors=split2errors || MathAbs(threshold-(n-2.5))>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(rms-MathSqrt((0.25+0.25+0.25+0.25)/(3*n)))>100*CMath::m_machineepsilon; split2errors=split2errors || MathAbs(cvrms-MathSqrt((double)(1+1+1+1)/(double)(3*n)))>100*CMath::m_machineepsilon; } //--- Optimal split-K //--- General tests for different N's for(n=1;n<=maxnq;n++) { //--- allocation ArrayResize(a,n); ArrayResize(c,n); //--- one-tie test if(n%2==0) { for(i=0;i<=n-1;i++) { a[i]=n; c[i]=i%2; } //--- function call CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve); //--- check if(info!=-3) { optimalsplitkerrors=true; continue; } } //--- two-tie test //--- test #1 if(n>1) { c0=0; c1=0; for(i=0;i<=n-1;i++) { a[i]=i/((n+1)/2); c[i]=i/((n+1)/2); //--- check if(c[i]==0) c0=c0+1; //--- check if(c[i]==1) c1=c1+1; } //--- function call CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve); //--- check if(info!=1) { optimalsplitkerrors=true; continue; } //--- search errors optimalsplitkerrors=optimalsplitkerrors || ni!=2; optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon; optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon; } //--- test #2 if(n>2) { c0=1+CMath::RandomInteger(n-1); c1=n-c0; for(i=0;i<=n-1;i++) { //--- check if(i100*CMath::m_machineepsilon; optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon; } //--- multi-tie test if(n>=16) { //--- Multi-tie test. //--- First NC-1 ties have C0 entries,remaining NC-th tie //--- have C1 entries. nc=(int)MathRound(MathSqrt(n)); c0=n/nc; c1=n-c0*(nc-1); for(i=0;i<=nc-2;i++) { for(j=c0*i;j<=c0*(i+1)-1;j++) { a[j]=j; c[j]=i; } } //--- change values for(j=c0*(nc-1);j<=n-1;j++) { a[j]=j; c[j]=nc-1; } //--- function call CBdSS::DSOptimalSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve); //--- check if(info!=1) { optimalsplitkerrors=true; continue; } //--- search errors optimalsplitkerrors=optimalsplitkerrors || ni!=nc; //--- check if(ni==nc) { for(i=0;i<=nc-2;i++) optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon; cvr=-((nc-1)*c0*MathLog((double)c0/(double)(c0+nc-1))+c1*MathLog((double)c1/(double)(c1+nc-1))); optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-cvr)>100*CMath::m_machineepsilon; } } } //--- Non-optimal split-K //--- General tests for different N's for(n=1;n<=maxnq;n++) { //--- allocation ArrayResize(a,n); ArrayResize(c,n); //--- one-tie test if(n%2==0) { for(i=0;i<=n-1;i++) { a[i]=pass; c[i]=i%2; } //--- function call CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve); //--- check if(info!=-3) { splitkerrors=true; continue; } } //--- two-tie test //--- test #1 if(n>1) { c0=0; c1=0; for(i=0;i<=n-1;i++) { a[i]=i/((n+1)/2); c[i]=i/((n+1)/2); //--- check if(c[i]==0) c0=c0+1; //--- check if(c[i]==1) c1=c1+1; } //--- function call CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve); //--- check if(info!=1) { splitkerrors=true; continue; } //--- search errors splitkerrors=splitkerrors || ni!=2; //--- check if(ni==2) { splitkerrors=splitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon; splitkerrors=splitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon; } } //--- test #2 if(n>2) { c0=1+CMath::RandomInteger(n-1); c1=n-c0; for(i=0;i<=n-1;i++) { //--- check if(i100*CMath::m_machineepsilon; splitkerrors=splitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon; } } //--- multi-tie test for(c0=4;c0<=n;c0++) { //--- check if((n%c0==0 && n/c0<=c0) && n/c0>1) { nc=n/c0; for(i=0;i<=nc-1;i++) { for(j=c0*i;j<=c0*(i+1)-1;j++) { a[j]=j; c[j]=i; } } //--- function call CBdSS::DSSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve); //--- check if(info!=1) { splitkerrors=true; continue; } splitkerrors=splitkerrors || ni!=nc; //--- check if(ni==nc) { for(i=0;i<=nc-2;i++) { splitkerrors=splitkerrors || MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon; } cvr=-(nc*c0*MathLog((double)c0/(double)(c0+nc-1))); splitkerrors=splitkerrors || MathAbs(cve-cvr)>100*CMath::m_machineepsilon; } } } } //--- report waserrors=((tieserrors || split2errors) || optimalsplitkerrors) || splitkerrors; //--- check if(!silent) { Print("TESTING BASIC DATASET SUBROUTINES"); Print("TIES: "); //--- check if(!tieserrors) Print("OK"); else Print("FAILED"); Print("SPLIT-2: "); //--- check if(!split2errors) Print("OK"); else Print("FAILED"); Print("OPTIMAL SPLIT-K: "); //--- check if(!optimalsplitkerrors) Print("OK"); else Print("FAILED"); Print("SPLIT-K: "); //--- check if(!splitkerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestBdSSUnit::Unset2D(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestBdSSUnit::Unset1D(double &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestBdSSUnit::Unset1DI(int &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=CMath::RandomInteger(3)-1; } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestBdSSUnit::TestSortResults(double &asorted[],int &p1[], int &p2[],double &aoriginal[], const int n,bool &waserrors) { //--- create variables int i=0; double t=0; //--- create arrays double a2[]; int f[]; //--- allocation ArrayResize(a2,n); ArrayResize(f,n); //--- is set ordered? for(i=0;i<=n-2;i++) waserrors=waserrors || asorted[i]>asorted[i+1]; //--- P1 correctness for(i=0;i<=n-1;i++) waserrors=waserrors || asorted[i]!=aoriginal[p1[i]]; //--- change values for(i=0;i<=n-1;i++) f[i]=0; for(i=0;i<=n-1;i++) f[p1[i]]=f[p1[i]]+1; //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || f[i]!=1; //--- P2 correctness for(i=0;i<=n-1;i++) a2[i]=aoriginal[i]; for(i=0;i<=n-1;i++) { //--- check if(p2[i]!=i) { t=a2[i]; a2[i]=a2[p2[i]]; a2[p2[i]]=t; } } //--- search errors for(i=0;i<=n-1;i++) waserrors=waserrors || asorted[i]!=a2[i]; } //+------------------------------------------------------------------+ //| Testing class CDForest | //+------------------------------------------------------------------+ class CTestDForestUnit { public: CTestDForestUnit(void); ~CTestDForestUnit(void); static bool TestDForest(const bool silent); private: static void TestProcessing(bool &err); static void BasicTest1(const int nvars,const int nclasses,const int passcount,bool &err); static void BasicTest2(bool &err); static void BasicTest3(bool &err); static void BasicTest4(bool &err); static void BasicTest5(bool &err); static double RNormal(void); static void RSphere(CMatrixDouble &xy,const int n,const int i); static void UnsetDF(CDecisionForest &df); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestDForestUnit::CTestDForestUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestDForestUnit::~CTestDForestUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CDForest | //+------------------------------------------------------------------+ static bool CTestDForestUnit::TestDForest(const bool silent) { //--- create variables int ncmax=0; int nvmax=0; int passcount=0; int nvars=0; int nclasses=0; bool waserrors; bool basicerrors; bool procerrors; //--- Primary settings nvmax=4; ncmax=3; passcount=10; basicerrors=false; procerrors=false; waserrors=false; //--- Tests TestProcessing(procerrors); for(nvars=1;nvars<=nvmax;nvars++) { for(nclasses=1;nclasses<=ncmax;nclasses++) BasicTest1(nvars,nclasses,passcount,basicerrors); } //--- function calls BasicTest2(basicerrors); BasicTest3(basicerrors); BasicTest4(basicerrors); BasicTest5(basicerrors); //--- Final report waserrors=basicerrors || procerrors; //--- check if(!silent) { Print("RANDOM FOREST TEST"); Print(""); Print("TOTAL RESULTS: "); //--- check if(!waserrors) Print("OK"); else Print("FAILED"); Print("* PROCESSING FUNCTIONS: "); //--- check if(!procerrors) Print("OK"); else Print("FAILED"); Print("* BASIC TESTS: "); //--- check if(!basicerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Processing functions test | //+------------------------------------------------------------------+ static void CTestDForestUnit::TestProcessing(bool &err) { //--- create variables int nvars=0; int nclasses=0; int nsample=0; int ntrees=0; int nfeatures=0; int flags=0; int npoints=0; int pass=0; int passcount=0; int i=0; int j=0; bool allsame; int info=0; double v=0; //--- create matrix CMatrixDouble xy; //--- create arrays double x1[]; double x2[]; double y1[]; double y2[]; //--- objects of classes CDecisionForest df1; CDecisionForest df2; CDFReport rep; //--- initialization passcount=100; //--- Main cycle for(pass=1;pass<=passcount;pass++) { //--- initialize parameters nvars=1+CMath::RandomInteger(5); nclasses=1+CMath::RandomInteger(3); ntrees=1+CMath::RandomInteger(4); nfeatures=1+CMath::RandomInteger(nvars); flags=0; //--- check if(CMath::RandomReal()>0.5) flags=flags+2; //--- Initialize arrays and data npoints=10+CMath::RandomInteger(50); nsample=(int)(MathMax(10,CMath::RandomInteger(npoints))); //--- allocation ArrayResize(x1,nvars); ArrayResize(x2,nvars); ArrayResize(y1,nclasses); ArrayResize(y2,nclasses); xy.Resize(npoints,nvars+1); //--- change values for(i=0;i<=npoints-1;i++) { for(j=0;j<=nvars-1;j++) { //--- check if(j%2==0) xy[i].Set(j,2*CMath::RandomReal()-1); else xy[i].Set(j,CMath::RandomInteger(2)); } //--- check if(nclasses==1) xy[i].Set(nvars,2*CMath::RandomReal()-1); else xy[i].Set(nvars,CMath::RandomInteger(nclasses)); } //--- create forest CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,ntrees,nsample,nfeatures,flags,info,df1,rep); //--- check if(info<=0) { err=true; return; } //--- Same inputs leads to same outputs for(i=0;i<=nvars-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nclasses-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function call CDForest::DFProcess(df1,x1,y1); //--- function call CDForest::DFProcess(df1,x2,y2); allsame=true; //--- search errors for(i=0;i<=nclasses-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Same inputs on original forest leads to same outputs //--- on copy created using DFCopy UnsetDF(df2); //--- function call CDForest::DFCopy(df1,df2); for(i=0;i<=nvars-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nclasses-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function call CDForest::DFProcess(df1,x1,y1); //--- function call CDForest::DFProcess(df2,x2,y2); allsame=true; //--- search errors for(i=0;i<=nclasses-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Same inputs on original forest leads to same outputs //--- on copy created using DFSerialize UnsetDF(df2); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CDForest::DFAlloc(_local_serializer,df1); _local_serializer.SStart_Str(); CDForest::DFSerialize(_local_serializer,df1); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CDForest::DFUnserialize(_local_serializer,df2); _local_serializer.Stop(); } for(i=0;i<=nvars-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nclasses-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function call CDForest::DFProcess(df1,x1,y1); //--- function call CDForest::DFProcess(df2,x2,y2); allsame=true; //--- search errors for(i=0;i<=nclasses-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Normalization properties if(nclasses>1) { for(i=0;i<=nvars-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CDForest::DFProcess(df1,x1,y1); v=0; //--- search errors for(i=0;i<=nclasses-1;i++) { v=v+y1[i]; err=err || y1[i]<0.0; } err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon; } } } //+------------------------------------------------------------------+ //| Basic test: one-tree forest built using full sample must | //| remember all the training cases | //+------------------------------------------------------------------+ static void CTestDForestUnit::BasicTest1(const int nvars,const int nclasses, const int passcount,bool &err) { //--- create variables int pass=0; int npoints=0; int i=0; int j=0; int k=0; double s=0; int info=0; bool hassame; int i_=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDFReport rep; //--- check if(nclasses==1) { //--- only classification tasks return; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- select number of points if(pass<=3 && passcount>3) npoints=pass; else npoints=100+CMath::RandomInteger(100); //--- Prepare task xy.Resize(npoints,nvars+1); ArrayResize(x,nvars); ArrayResize(y,nclasses); //--- change values for(i=0;i<=npoints-1;i++) { for(j=0;j<=nvars-1;j++) xy[i].Set(j,2*CMath::RandomReal()-1); xy[i].Set(nvars,CMath::RandomInteger(nclasses)); } //--- Test CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,1,npoints,1,1,info,df,rep); //--- check if(info<=0) { err=true; return; } //--- calculation for(i=0;i<=npoints-1;i++) { for(i_=0;i_<=nvars-1;i_++) x[i_]=xy[i][i_]; //--- function call CDForest::DFProcess(df,x,y); s=0; for(j=0;j<=nclasses-1;j++) { //--- check if(y[j]<0.0) { err=true; return; } s=s+y[j]; } //--- check if(MathAbs(s-1)>1000*CMath::m_machineepsilon) { err=true; return; } //--- check if(MathAbs(y[(int)MathRound(xy[i][nvars])]-1)>1000*CMath::m_machineepsilon) { //--- not an error if there exists such K,J that XY[k].Set(j,XY[I,J] //--- (may be we just can't distinguish two tied values). //--- definitely error otherwise. hassame=false; for(k=0;k<=npoints-1;k++) { //--- check if(k!=i) { for(j=0;j<=nvars-1;j++) { //--- check if(xy[k][j]==xy[i][j]) hassame=true; } } } //--- check if(!hassame) { err=true; return; } } } } } //+------------------------------------------------------------------+ //| Basic test: tests generalization ability on a simple noisy | //| classification task: | //| * 01000*CMath::m_machineepsilon) { err=true; return; } //--- test for good correlation with results if(x[0]<1.0) err=err || y[0]<0.8; //--- check if(x[0]>=1.0 && x[0]<=2.0) err=err || MathAbs(y[1]-(x[0]-1))>0.5; //--- check if(x[0]>2.0) err=err || y[1]<0.8; x[0]=x[0]+0.01; } } } //+------------------------------------------------------------------+ //| Basic test: tests generalization ability on a simple | //| classification task (no noise): | //| * ||x||<1,||y||<1 | //| * x^2+y^2<=0.25 - P(class=0)=1 | //| * x^2+y^2>0.25 - P(class=0)=0 | //+------------------------------------------------------------------+ static void CTestDForestUnit::BasicTest3(bool &err) { //--- create variables int pass=0; int passcount=0; int npoints=0; int ntrees=0; int i=0; int j=0; int k=0; double s=0; int info=0; int testgridsize=0; double r=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDFReport rep; //--- initialization passcount=1; testgridsize=50; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- select npoints and ntrees npoints=2000; ntrees=100; //--- Prepare task xy.Resize(npoints,3); ArrayResize(x,2); ArrayResize(y,2); //--- change values for(i=0;i<=npoints-1;i++) { xy[i].Set(0,2*CMath::RandomReal()-1); xy[i].Set(1,2*CMath::RandomReal()-1); //--- check if(CMath::Sqr(xy[i][0])+CMath::Sqr(xy[i][1])<=0.25) xy[i].Set(2,0); else xy[i].Set(2,1); } //--- Test CDForest::DFBuildInternal(xy,npoints,2,2,ntrees,(int)MathRound(0.1*npoints),1,0,info,df,rep); //--- check if(info<=0) { err=true; return; } //--- calculation for(i=-(testgridsize/2);i<=testgridsize/2;i++) { for(j=-(testgridsize/2);j<=testgridsize/2;j++) { x[0]=(double)i/(double)(testgridsize/2); x[1]=(double)j/(double)(testgridsize/2); //--- function call CDForest::DFProcess(df,x,y); //--- Test for basic properties s=0; for(k=0;k<=1;k++) { //--- check if(y[k]<0.0) { err=true; return; } s=s+y[k]; } //--- check if(MathAbs(s-1)>1000*CMath::m_machineepsilon) { err=true; return; } //--- test for good correlation with results r=MathSqrt(CMath::Sqr(x[0])+CMath::Sqr(x[1])); //--- check if(r<0.5*0.5) err=err || y[0]<0.6; //--- check if(r>0.5*1.5) err=err || y[1]<0.6; } } } } //+------------------------------------------------------------------+ //| Basic test: simple regression task without noise: | //| * ||x||<1,||y||<1 | //| * F(x,y)=x^2+y | //+------------------------------------------------------------------+ static void CTestDForestUnit::BasicTest4(bool &err) { //--- create variables int pass=0; int passcount=0; int npoints=0; int ntrees=0; int ns=0; int strongc=0; int i=0; int j=0; int info=0; int testgridsize=0; double maxerr=0; double maxerr2=0; double avgerr=0; double avgerr2=0; int cnt=0; double ey=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDecisionForest df2; CDFReport rep; CDFReport rep2; //--- initialization passcount=1; testgridsize=50; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- select npoints and ntrees npoints=5000; ntrees=100; ns=(int)MathRound(0.1*npoints); strongc=1; //--- Prepare task xy.Resize(npoints,3); ArrayResize(x,2); ArrayResize(y,1); //--- change values for(i=0;i<=npoints-1;i++) { xy[i].Set(0,2*CMath::RandomReal()-1); xy[i].Set(1,2*CMath::RandomReal()-1); xy[i].Set(2,CMath::Sqr(xy[i][0])+xy[i][1]); } //--- Test CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,0,info,df,rep); //--- check if(info<=0) { err=true; return; } //--- function call CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,strongc,info,df2,rep2); //--- check if(info<=0) { err=true; return; } //--- change values maxerr=0; maxerr2=0; avgerr=0; avgerr2=0; cnt=0; //--- calculation for(i=(int)MathRound(-(0.7*testgridsize/2));i<=(int)MathRound(0.7*testgridsize/2);i++) { for(j=(int)MathRound(-(0.7*testgridsize/2));j<=(int)MathRound(0.7*testgridsize/2);j++) { x[0]=(double)i/(double)(testgridsize/2); x[1]=(double)j/(double)(testgridsize/2); ey=CMath::Sqr(x[0])+x[1]; //--- function call CDForest::DFProcess(df,x,y); maxerr=MathMax(maxerr,MathAbs(y[0]-ey)); avgerr=avgerr+MathAbs(y[0]-ey); //--- function call CDForest::DFProcess(df2,x,y); maxerr2=MathMax(maxerr2,MathAbs(y[0]-ey)); avgerr2=avgerr2+MathAbs(y[0]-ey); cnt=cnt+1; } } //--- search errors avgerr=avgerr/cnt; avgerr2=avgerr2/cnt; err=err || maxerr>0.2; err=err || maxerr2>0.2; err=err || avgerr>0.1; err=err || avgerr2>0.1; } } //+------------------------------------------------------------------+ //| Basic test: extended variable selection leads to better results. | //| Next task CAN be solved without EVS but it is very unlikely. | //| With EVS it can be easily and exactly solved. | //| Task matrix: | //| 1 0 0 0 ... 0 0 | //| 0 1 0 0 ... 0 1 | //| 0 0 1 0 ... 0 2 | //| 0 0 0 1 ... 0 3 | //| 0 0 0 0 ... 1 N-1 | //+------------------------------------------------------------------+ static void CTestDForestUnit::BasicTest5(bool &err) { //--- create variables int nvars=0; int npoints=0; int nfeatures=0; int nsample=0; int ntrees=0; int evs=0; int i=0; int j=0; bool eflag; int info=0; int i_=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDFReport rep; //--- select npoints and ntrees npoints=50; nvars=npoints; ntrees=1; nsample=npoints; evs=2; nfeatures=1; //--- Prepare task xy.Resize(npoints,nvars+1); ArrayResize(x,nvars); ArrayResize(y,1); for(i=0;i<=npoints-1;i++) { for(j=0;j<=nvars-1;j++) { xy[i].Set(j,0); } xy[i].Set(i,1); xy[i].Set(nvars,i); } //--- Without EVS CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,0,info,df,rep); //--- check if(info<=0) { err=true; return; } //--- calculation eflag=false; for(i=0;i<=npoints-1;i++) { for(i_=0;i_<=nvars-1;i_++) x[i_]=xy[i][i_]; //--- function call CDForest::DFProcess(df,x,y); //--- check if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon) eflag=true; } //--- check if(!eflag) { err=true; return; } //--- With EVS CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,evs,info,df,rep); //--- check if(info<=0) { err=true; return; } //--- calculation eflag=false; for(i=0;i<=npoints-1;i++) { for(i_=0;i_<=nvars-1;i_++) { x[i_]=xy[i][i_]; } //--- function call CDForest::DFProcess(df,x,y); //--- check if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon) { eflag=true; } } //--- check if(eflag) { err=true; return; } } //+------------------------------------------------------------------+ //| Random normal number | //+------------------------------------------------------------------+ static double CTestDForestUnit::RNormal(void) { //--- create variables double result=0; double u=0; double v=0; double s=0; double x1=0; double x2=0; //--- calculation while(true) { u=2*CMath::RandomReal()-1; v=2*CMath::RandomReal()-1; s=CMath::Sqr(u)+CMath::Sqr(v); //--- check if(s>0.0 && s<1.0) { s=MathSqrt(-(2*MathLog(s)/s)); x1=u*s; x2=v*s; //--- break the cycle break; } } //--- check if(x1!=0) result=x1; else result=x2; //--- return result return(result); } //+------------------------------------------------------------------+ //| Random point from sphere | //+------------------------------------------------------------------+ static void CTestDForestUnit::RSphere(CMatrixDouble &xy,const int n, const int i) { //--- create variables int j=0; double v=0; int i_=0; //--- change values for(j=0;j<=n-1;j++) xy[i].Set(j,RNormal()); v=0.0; for(i_=0;i_<=n-1;i_++) v+=xy[i][i_]*xy[i][i_]; //--- calculation v=CMath::RandomReal()/MathSqrt(v); for(i_=0;i_<=n-1;i_++) xy[i].Set(i_,v*xy[i][i_]); } //+------------------------------------------------------------------+ //| Unsets DF | //+------------------------------------------------------------------+ static void CTestDForestUnit::UnsetDF(CDecisionForest &df) { //--- create a variable int info=0; //--- create matrix CMatrixDouble xy; //--- object of class CDFReport rep; //--- allocation xy.Resize(1,2); //--- change values xy[0].Set(0,0); xy[0].Set(1,0); //--- function call CDForest::DFBuildInternal(xy,1,1,1,1,1,1,0,info,df,rep); } //+------------------------------------------------------------------+ //| Testing class CBlas | //+------------------------------------------------------------------+ class CTestBlasUnit { private: //--- private method static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta); public: //--- constructor, destructor CTestBlasUnit(void); ~CTestBlasUnit(void); //--- public method static bool TestBlas(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestBlasUnit::CTestBlasUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestBlasUnit::~CTestBlasUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CBlas | //+------------------------------------------------------------------+ static bool CTestBlasUnit::TestBlas(const bool silent) { //--- create variables int pass=0; int passcount=0; int n=0; int i=0; int i1=0; int i2=0; int j=0; int j1=0; int j2=0; int l=0; int k=0; int r=0; int i3=0; int j3=0; int col1=0; int col2=0; int row1=0; int row2=0; double err=0; double e1=0; double e2=0; double e3=0; double v=0; double scl1=0; double scl2=0; double scl3=0; bool was1; bool was2; bool trans1; bool trans2; double threshold=0; bool n2errors; bool hsnerrors; bool amaxerrors; bool mverrors; bool iterrors; bool cterrors; bool mmerrors; bool waserrors; int i_=0; //--- create arrays double x1[]; double x2[]; //--- create matrix CMatrixDouble a; CMatrixDouble b; CMatrixDouble c1; CMatrixDouble c2; //--- initialization n2errors=false; amaxerrors=false; hsnerrors=false; mverrors=false; iterrors=false; cterrors=false; mmerrors=false; waserrors=false; threshold=10000*CMath::m_machineepsilon; //--- Test Norm2 passcount=1000; e1=0; e2=0; e3=0; scl2=0.5*CMath::m_maxrealnumber; scl3=2*CMath::m_minrealnumber; //--- calculation for(pass=1;pass<=passcount;pass++) { n=1+CMath::RandomInteger(1000); i1=CMath::RandomInteger(10); i2=n+i1-1; //--- allocation ArrayResize(x1,i2+1); ArrayResize(x2,i2+1); //--- change values for(i=i1;i<=i2;i++) x1[i]=2*CMath::RandomReal()-1; v=0; for(i=i1;i<=i2;i++) v=v+CMath::Sqr(x1[i]); v=MathSqrt(v); //--- calculation e1=MathMax(e1,MathAbs(v-CBlas::VectorNorm2(x1,i1,i2))); for(i=i1;i<=i2;i++) x2[i]=scl2*x1[i]; //--- calculation e2=MathMax(e2,MathAbs(v*scl2-CBlas::VectorNorm2(x2,i1,i2))); for(i=i1;i<=i2;i++) x2[i]=scl3*x1[i]; //--- calculation e3=MathMax(e3,MathAbs(v*scl3-CBlas::VectorNorm2(x2,i1,i2))); } e2=e2/scl2; e3=e3/scl3; //--- search errors n2errors=(e1>=threshold || e2>=threshold) || e3>=threshold; //--- Testing VectorAbsMax,Column/Row AbsMax ArrayResize(x1,6); x1[1]=2.0; x1[2]=0.2; x1[3]=-1.3; x1[4]=0.7; x1[5]=-3.0; //--- search errors amaxerrors=(CBlas::VectorIdxAbsMax(x1,1,5)!=5 || CBlas::VectorIdxAbsMax(x1,1,4)!=1) || CBlas::VectorIdxAbsMax(x1,2,4)!=3; n=30; //--- allocation ArrayResize(x1,n+1); a.Resize(n+1,n+1); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- calculation was1=false; was2=false; for(pass=1;pass<=1000;pass++) { //--- change values j=1+CMath::RandomInteger(n); i1=1+CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n+1-i1); for(i_=i1;i_<=i2;i_++) x1[i_]=a[i_][j]; //--- check if(CBlas::VectorIdxAbsMax(x1,i1,i2)!=CBlas::ColumnIdxAbsMax(a,i1,i2,j)) was1=true; //--- change values i=1+CMath::RandomInteger(n); j1=1+CMath::RandomInteger(n); j2=j1+CMath::RandomInteger(n+1-j1); for(i_=j1;i_<=j2;i_++) x1[i_]=a[i][i_]; //--- check if(CBlas::VectorIdxAbsMax(x1,j1,j2)!=CBlas::RowIdxAbsMax(a,j1,j2,i)) was2=true; } //--- search errors amaxerrors=(amaxerrors || was1) || was2; //--- Testing upper Hessenberg 1-norm a.Resize(4,4); ArrayResize(x1,4); a[1].Set(1,2); a[1].Set(2,3); a[1].Set(3,1); a[2].Set(1,4); a[2].Set(2,-5); a[2].Set(3,8); a[3].Set(1,99); a[3].Set(2,3); a[3].Set(3,1); //--- search errors hsnerrors=MathAbs(CBlas::UpperHessenberg1Norm(a,1,3,1,3,x1)-11)>threshold; //--- Testing MatrixVectorMultiply a.Resize(4,6); ArrayResize(x1,4); ArrayResize(x2,3); a[2].Set(3,2); a[2].Set(4,-1); a[2].Set(5,-1); a[3].Set(3,1); a[3].Set(4,-2); a[3].Set(5,2); x1[1]=1; x1[2]=2; x1[3]=1; x2[1]=-1; x2[2]=-1; //--- function calls CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,1.0); CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,1.0); //--- calculation e1=MathAbs(x1[1]+5)+MathAbs(x1[2]-8)+MathAbs(x1[3]+1)+MathAbs(x2[1]+2)+MathAbs(x2[2]+2); x1[1]=1; x1[2]=2; x1[3]=1; x2[1]=-1; x2[2]=-1; //--- function calls CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,0.0); CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,0.0); //--- calculation e2=MathAbs(x1[1]+3)+MathAbs(x1[2]-3)+MathAbs(x1[3]+1)+MathAbs(x2[1]+1)+MathAbs(x2[2]+1); mverrors=e1+e2>=threshold; //--- testing inplace transpose n=10; a.Resize(n+1,n+1); b.Resize(n+1,n+1); ArrayResize(x1,n); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) a[i].Set(j,CMath::RandomReal()); } //--- calculation passcount=10000; was1=false; for(pass=1;pass<=passcount;pass++) { //--- change values i1=1+CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1+1); j1=1+CMath::RandomInteger(n-(i2-i1)); j2=j1+(i2-i1); //--- function calls CBlas::CopyMatrix(a,i1,i2,j1,j2,b,i1,i2,j1,j2); CBlas::InplaceTranspose(b,i1,i2,j1,j2,x1); for(i=i1;i<=i2;i++) { for(j=j1;j<=j2;j++) { //--- check if(a[i][j]!=b[i1+(j-j1)][j1+(i-i1)]) was1=true; } } } //--- change value iterrors=was1; //--- testing copy and transpose n=10; a.Resize(n+1,n+1); b.Resize(n+1,n+1); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) a[i].Set(j,CMath::RandomReal()); } //--- calculation passcount=10000; was1=false; for(pass=1;pass<=passcount;pass++) { //--- change values i1=1+CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1+1); j1=1+CMath::RandomInteger(n); j2=j1+CMath::RandomInteger(n-j1+1); //--- function call CBlas::CopyAndTranspose(a,i1,i2,j1,j2,b,j1,j2,i1,i2); for(i=i1;i<=i2;i++) { for(j=j1;j<=j2;j++) { //--- check if(a[i][j]!=b[j][i]) was1=true; } } } //--- change values cterrors=was1; //--- Testing MatrixMatrixMultiply n=10; a.Resize(2*n+1,2*n+1); b.Resize(2*n+1,2*n+1); c1.Resize(2*n+1,2*n+1); c2.Resize(2*n+1,2*n+1); ArrayResize(x1,n+1); ArrayResize(x2,n+1); for(i=1;i<=2*n;i++) { for(j=1;j<=2*n;j++) { a[i].Set(j,CMath::RandomReal()); b[i].Set(j,CMath::RandomReal()); } } //--- calculation passcount=1000; was1=false; for(pass=1;pass<=passcount;pass++) { for(i=1;i<=2*n;i++) { for(j=1;j<=2*n;j++) { c1[i].Set(j,2.1*i+3.1*j); c2[i].Set(j,c1[i][j]); } } //--- change values l=1+CMath::RandomInteger(n); k=1+CMath::RandomInteger(n); r=1+CMath::RandomInteger(n); i1=1+CMath::RandomInteger(n); j1=1+CMath::RandomInteger(n); i2=1+CMath::RandomInteger(n); j2=1+CMath::RandomInteger(n); i3=1+CMath::RandomInteger(n); j3=1+CMath::RandomInteger(n); trans1=CMath::RandomReal()>0.5; trans2=CMath::RandomReal()>0.5; //--- check if(trans1) { col1=l; row1=k; } else { col1=k; row1=l; } //--- check if(trans2) { col2=k; row2=r; } else { col2=r; row2=k; } //--- change values scl1=CMath::RandomReal(); scl2=CMath::RandomReal(); //--- function calls CBlas::MatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c1,i3,i3+l-1,j3,j3+r-1,scl2,x1); NaiveMatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c2,i3,i3+l-1,j3,j3+r-1,scl2); //--- search errors err=0; for(i=1;i<=l;i++) { for(j=1;j<=r;j++) err=MathMax(err,MathAbs(c1[i3+i-1][j3+j-1]-c2[i3+i-1][j3+j-1])); } //--- check if(err>threshold) { was1=true; break; } } //--- change value mmerrors=was1; //--- report waserrors=(((((n2errors || amaxerrors) || hsnerrors) || mverrors) || iterrors) || cterrors) || mmerrors; //--- check if(!silent) { Print("TESTING BLAS"); Print("VectorNorm2: "); //--- check if(n2errors) Print("FAILED"); else Print("OK"); Print("AbsMax (vector/row/column): "); //--- check if(amaxerrors) Print("FAILED"); else Print("OK"); Print("UpperHessenberg1Norm: "); //--- check if(hsnerrors) Print("FAILED"); else Print("OK"); Print("MatrixVectorMultiply: "); //--- check if(mverrors) Print("FAILED"); else Print("OK"); Print("InplaceTranspose: "); //--- check if(iterrors) Print("FAILED"); else Print("OK"); Print("CopyAndTranspose: "); //--- check if(cterrors) Print("FAILED"); else Print("OK"); Print("MatrixMatrixMultiply: "); //--- check if(mmerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestBlasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a, const int ai1, const int ai2, const int aj1, const int aj2, const bool transa, CMatrixDouble &b, const int bi1, const int bi2, const int bj1, const int bj2, const bool transb, const double alpha, CMatrixDouble &c, const int ci1, const int ci2, const int cj1, const int cj2, const double beta) { //--- create variables int arows=0; int acols=0; int brows=0; int bcols=0; int i=0; int j=0; int k=0; int l=0; int r=0; double v=0; int i_=0; int i1_=0; //--- create arrays double x1[]; double x2[]; //--- Setup if(!transa) { arows=ai2-ai1+1; acols=aj2-aj1+1; } else { arows=aj2-aj1+1; acols=ai2-ai1+1; } //--- check if(!transb) { brows=bi2-bi1+1; bcols=bj2-bj1+1; } else { brows=bj2-bj1+1; bcols=bi2-bi1+1; } //--- check if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!")) return; //--- check if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0) return; //--- change values l=arows; r=bcols; k=acols; //--- allocation ArrayResize(x1,k+1); ArrayResize(x2,k+1); //--- calculation for(i=1;i<=l;i++) { for(j=1;j<=r;j++) { //--- check if(!transa) { //--- check if(!transb) { //--- change values i1_=aj1-bi1; v=0.0; for(i_=bi1;i_<=bi2;i_++) v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_]; } else { //--- change values i1_=aj1-bj1; v=0.0; for(i_=bj1;i_<=bj2;i_++) v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_]; } } else { //--- check if(!transb) { //--- change values i1_=ai1-bi1; v=0.0; for(i_=bi1;i_<=bi2;i_++) v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1]; } else { //--- change values i1_=ai1-bj1; v=0.0; for(i_=bj1;i_<=bj2;i_++) v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1]; } } //--- check if(beta==0.0) c[ci1+i-1].Set(cj1+j-1,alpha*v); else c[ci1+i-1].Set(cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v); } } } //+------------------------------------------------------------------+ //| Testing class CKMeans | //+------------------------------------------------------------------+ class CTestKMeansUnit { public: CTestKMeansUnit(void); ~CTestKMeansUnit(void); static bool TestKMeans(const bool silent); private: static void SimpleTest1(const int nvars,const int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors); static void RestartsTest(bool &converrors,bool &restartserrors); static double RNormal(void); static void RSphere(CMatrixDouble &xy,const int n,const int i); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestKMeansUnit::CTestKMeansUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestKMeansUnit::~CTestKMeansUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CKMeans | //+------------------------------------------------------------------+ static bool CTestKMeansUnit::TestKMeans(const bool silent) { //--- create variables int nf=0; int maxnf=0; int nc=0; int maxnc=0; int passcount=0; bool waserrors; bool converrors; bool simpleerrors; bool complexerrors; bool othererrors; bool restartserrors; //--- Primary settings maxnf=5; maxnc=5; passcount=10; waserrors=false; converrors=false; othererrors=false; simpleerrors=false; complexerrors=false; restartserrors=false; //--- calculation for(nf=1;nf<=maxnf;nf++) { for(nc=1;nc<=maxnc;nc++) SimpleTest1(nf,nc,passcount,converrors,othererrors,simpleerrors); } RestartsTest(converrors,restartserrors); //--- Final report waserrors=(((converrors || othererrors) || simpleerrors) || complexerrors) || restartserrors; //--- check if(!silent) { Print("K-MEANS TEST"); Print("TOTAL RESULTS: "); //--- check if(!waserrors) Print("OK"); else Print("FAILED"); Print("* CONVERGENCE: "); //--- check if(!converrors) Print("OK"); else Print("FAILED"); Print("* SIMPLE TASKS: "); //--- check if(!simpleerrors) Print("OK"); else Print("FAILED"); Print("* COMPLEX TASKS: "); //--- check if(!complexerrors) Print("OK"); else Print("FAILED"); Print("* OTHER PROPERTIES: "); //--- check if(!othererrors) Print("OK"); else Print("FAILED"); Print("* RESTARTS PROPERTIES: "); //--- check if(!restartserrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Simple test 1: ellipsoid in NF-dimensional space. | //| compare k-means centers with random centers | //+------------------------------------------------------------------+ static void CTestKMeansUnit::SimpleTest1(const int nvars,const int nc, int passcount,bool &converrors, bool &othererrors,bool &simpleerrors) { //--- create variables int npoints=0; int majoraxis=0; double v=0; int i=0; int j=0; int info=0; int pass=0; int restarts=0; double ekmeans=0; double erandom=0; double dclosest=0; int cclosest=0; int i_=0; //--- create arrays double tmp[]; int xyc[]; //--- create matrix CMatrixDouble xy; CMatrixDouble c; //--- initialization npoints=nc*100; restarts=5; passcount=10; //--- allocation ArrayResize(tmp,nvars); //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Fill xy.Resize(npoints,nvars); majoraxis=CMath::RandomInteger(nvars); for(i=0;i<=npoints-1;i++) { RSphere(xy,nvars,i); xy[i].Set(majoraxis,nc*xy[i][majoraxis]); } //--- Test CKMeans::KMeansGenerate(xy,npoints,nvars,nc,restarts,info,c,xyc); //--- check if(info<0) { converrors=true; return; } //--- Test that XYC is correct mapping to cluster centers for(i=0;i<=npoints-1;i++) { cclosest=-1; dclosest=CMath::m_maxrealnumber; //--- calculation for(j=0;j<=nc-1;j++) { for(i_=0;i_<=nvars-1;i_++) tmp[i_]=xy[i][i_]; for(i_=0;i_<=nvars-1;i_++) tmp[i_]=tmp[i_]-c[i_][j]; v=0.0; for(i_=0;i_<=nvars-1;i_++) v+=tmp[i_]*tmp[i_]; //--- check if(v1 significantly | //| improves quality of results. | //| Subroutine generates random task 3 unit balls in 2D,each with 20 | //| points, separated by 5 units wide gaps,and solves it with | //| Restarts=1 and with Restarts=5. Potential functions are compared,| //| outcome of the trial is either 0 or 1 (depending on what is | //| better). | //| Sequence of 1000 such tasks is olved. If Restarts>1 actually | //| improve quality of solution,sum of outcome will be non-binomial. | //| If it doesn't matter,it will be binomially distributed. | //| P.S. This test was added after report from Gianluca Borello who | //| noticed error in the handling of multiple restarts. | //+------------------------------------------------------------------+ static void CTestKMeansUnit::RestartsTest(bool &converrors,bool &restartserrors) { //--- create variables int npoints=0; int nvars=0; int nclusters=0; int clustersize=0; int restarts=0; int passcount=0; double sigmathreshold=0; double p=0; double s=0; int i=0; int j=0; int info=0; int pass=0; double ea=0; double eb=0; double v=0; int i_=0; //--- create matrix CMatrixDouble xy; CMatrixDouble ca; CMatrixDouble cb; //--- create arrays int xyca[]; int xycb[]; double tmp[]; //--- initialization restarts=5; passcount=1000; clustersize=20; nclusters=3; nvars=2; npoints=nclusters*clustersize; sigmathreshold=5; //--- allocation xy.Resize(npoints,nvars); ArrayResize(tmp,nvars); p=0; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Fill for(i=0;i<=npoints-1;i++) { RSphere(xy,nvars,i); for(j=0;j<=nvars-1;j++) xy[i].Set(j,xy[i][j]+(double)i/(double)clustersize*5); } //--- Test: Restarts=1 CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,1,info,ca,xyca); //--- check if(info<0) { converrors=true; return; } //--- calculation ea=0; for(i=0;i<=npoints-1;i++) { for(i_=0;i_<=nvars-1;i_++) tmp[i_]=xy[i][i_]; for(i_=0;i_<=nvars-1;i_++) tmp[i_]=tmp[i_]-ca[i_][xyca[i]]; //--- change value v=0.0; for(i_=0;i_<=nvars-1;i_++) v+=tmp[i_]*tmp[i_]; ea=ea+v; } //--- Test: Restarts>1 CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,restarts,info,cb,xycb); //--- check if(info<0) { converrors=true; return; } //--- calculation eb=0; for(i=0;i<=npoints-1;i++) { for(i_=0;i_<=nvars-1;i_++) tmp[i_]=xy[i][i_]; for(i_=0;i_<=nvars-1;i_++) tmp[i_]=tmp[i_]-cb[i_][xycb[i]]; //--- change value v=0.0; for(i_=0;i_<=nvars-1;i_++) v+=tmp[i_]*tmp[i_]; eb=eb+v; } //--- Calculate statistic. if(ea(double)(-sigmathreshold); } //+------------------------------------------------------------------+ //| Random normal number | //+------------------------------------------------------------------+ static double CTestKMeansUnit::RNormal(void) { //--- create variables double result=0; double u=0; double v=0; double s=0; double x1=0; double x2=0; //--- calculation while(true) { //--- change values u=2*CMath::RandomReal()-1; v=2*CMath::RandomReal()-1; s=CMath::Sqr(u)+CMath::Sqr(v); //--- check if(s>0.0 && s<1.0) { s=MathSqrt(-(2*MathLog(s)/s)); x1=u*s; x2=v*s; //--- break the cycle break; } } //--- check if(x1==0) result=x2; else result=x1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Random point from sphere | //+------------------------------------------------------------------+ static void CTestKMeansUnit::RSphere(CMatrixDouble &xy,const int n,const int i) { //--- create variables int j=0; double v=0; int i_=0; //--- change values for(j=0;j<=n-1;j++) xy[i].Set(j,RNormal()); v=0.0; for(i_=0;i_<=n-1;i_++) v+=xy[i][i_]*xy[i][i_]; //--- calculation v=CMath::RandomReal()/MathSqrt(v); for(i_=0;i_<=n-1;i_++) xy[i].Set(i_,v*xy[i][i_]); } //+------------------------------------------------------------------+ //| Testing class CHblas | //+------------------------------------------------------------------+ class CTestHblasUnit { public: //--- constructor, destructor CTestHblasUnit(void); ~CTestHblasUnit(void); //--- public method static bool TestHblas(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestHblasUnit::CTestHblasUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestHblasUnit::~CTestHblasUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CHblas | //+------------------------------------------------------------------+ static bool CTestHblasUnit::TestHblas(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int j=0; int i1=0; int i2=0; bool waserrors; double mverr=0; double threshold=0; complex alpha=0; complex v=0; int i_=0; int i1_=0; //--- create arrays complex x[]; complex y1[]; complex y2[]; complex y3[]; //--- create matrix CMatrixComplex a; CMatrixComplex ua; CMatrixComplex la; //--- initialization mverr=0; waserrors=false; maxn=10; threshold=1000*CMath::m_machineepsilon; //--- Test MV for(n=2;n<=maxn;n++) { //--- allocation a.Resize(n+1,n+1); ua.Resize(n+1,n+1); la.Resize(n+1,n+1); ArrayResize(x,n+1); ArrayResize(y1,n+1); ArrayResize(y2,n+1); ArrayResize(y3,n+1); //--- fill A,UA,LA for(i=1;i<=n;i++) { a[i].SetRe(i,2*CMath::RandomReal()-1); a[i].SetIm(i,0); //--- change values for(j=i+1;j<=n;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); a[j].Set(i,CMath::Conj(a[i][j])); } } //--- change values for(i=1;i<=n;i++) { for(j=1;j<=n;j++) ua[i].Set(j,0); } //--- change values for(i=1;i<=n;i++) { for(j=i;j<=n;j++) ua[i].Set(j,a[i][j]); } //--- change values for(i=1;i<=n;i++) { for(j=1;j<=n;j++) la[i].Set(j,0); } //--- change values for(i=1;i<=n;i++) { for(j=1;j<=i;j++) la[i].Set(j,a[i][j]); } //--- test on different I1,I2 for(i1=1;i1<=n;i1++) { for(i2=i1;i2<=n;i2++) { //--- Fill X,choose Alpha for(i=1;i<=i2-i1+1;i++) { x[i].re=2*CMath::RandomReal()-1; x[i].im=2*CMath::RandomReal()-1; } alpha.re=2*CMath::RandomReal()-1; alpha.im=2*CMath::RandomReal()-1; //--- calculate A*x,UA*x,LA*x for(i=i1;i<=i2;i++) { i1_=1-i1; v=0.0; for(i_=i1;i_<=i2;i_++) v+=a[i][i_]*x[i_+i1_]; y1[i-i1+1]=alpha*v; } //--- function call CHblas::HermitianMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2); //--- function call CHblas::HermitianMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3); //--- Calculate error for(i_=1;i_<=i2-i1+1;i_++) y2[i_]=y2[i_]-y1[i_]; //--- change value v=0.0; for(i_=1;i_<=i2-i1+1;i_++) v+=y2[i_]*CMath::Conj(y2[i_]); //--- search errors mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v))); for(i_=1;i_<=i2-i1+1;i_++) y3[i_]=y3[i_]-y1[i_]; //--- change value v=0.0; for(i_=1;i_<=i2-i1+1;i_++) v+=y3[i_]*CMath::Conj(y3[i_]); //--- search errors mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v))); } } } //--- report waserrors=mverr>threshold; //--- check if(!silent) { Print("TESTING HERMITIAN BLAS"); Print("MV error: "); Print("{0,5:E3}",mverr); Print("Threshold: "); Print("{0,5:E3}",threshold); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CReflections | //+------------------------------------------------------------------+ class CTestReflectionsUnit { public: //--- constructor, destructor CTestReflectionsUnit(void); ~CTestReflectionsUnit(void); //--- public method static bool TestReflections(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestReflectionsUnit::CTestReflectionsUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestReflectionsUnit::~CTestReflectionsUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CReflections | //+------------------------------------------------------------------+ static bool CTestReflectionsUnit::TestReflections(const bool silent) { //--- create variables bool result; int i=0; int j=0; int n=0; int m=0; int maxmn=0; double tmp=0; double beta=0; double tau=0; double err=0; double mer=0; double mel=0; double meg=0; int pass=0; int passcount=0; double threshold=0; int tasktype=0; double xscale=0; int i_=0; //--- create arrays double x[]; double v[]; double work[]; //--- create matrix CMatrixDouble h; CMatrixDouble a; CMatrixDouble b; CMatrixDouble c; //--- initialization passcount=10; threshold=100*CMath::m_machineepsilon; mer=0; mel=0; meg=0; //--- calculation for(pass=1;pass<=passcount;pass++) { for(n=1;n<=10;n++) { for(m=1;m<=10;m++) { //--- Task n=1+CMath::RandomInteger(10); m=1+CMath::RandomInteger(10); maxmn=MathMax(m,n); //--- Initialize ArrayResize(x,maxmn+1); ArrayResize(v,maxmn+1); ArrayResize(work,maxmn+1); h.Resize(maxmn+1,maxmn+1); a.Resize(maxmn+1,maxmn+1); b.Resize(maxmn+1,maxmn+1); c.Resize(maxmn+1,maxmn+1); //--- GenerateReflection,three tasks are possible: //--- * random X //--- * zero X //--- * non-zero X[1],all other are zeros //--- * random X,near underflow scale //--- * random X,near overflow scale for(tasktype=0;tasktype<=4;tasktype++) { xscale=1; //--- check if(tasktype==0) { for(i=1;i<=n;i++) x[i]=2*CMath::RandomReal()-1; } //--- check if(tasktype==1) { for(i=1;i<=n;i++) x[i]=0; } //--- check if(tasktype==2) { x[1]=2*CMath::RandomReal()-1; for(i=2;i<=n;i++) x[i]=0; } //--- check if(tasktype==3) { for(i=1;i<=n;i++) x[i]=(CMath::RandomInteger(21)-10)*CMath::m_minrealnumber; xscale=10*CMath::m_minrealnumber; } //--- check if(tasktype==4) { for(i=1;i<=n;i++) x[i]=(2*CMath::RandomReal()-1)*CMath::m_maxrealnumber; xscale=CMath::m_maxrealnumber; } for(i_=1;i_<=n;i_++) { v[i_]=x[i_]; } //--- function call CReflections::GenerateReflection(v,n,tau); //--- change values beta=v[1]; v[1]=1; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { //--- check if(i==j) h[i].Set(j,1-tau*v[i]*v[j]); else h[i].Set(j,-(tau*v[i]*v[j])); } } //--- calculation err=0; for(i=1;i<=n;i++) { tmp=0.0; for(i_=1;i_<=n;i_++) tmp+=h[i][i_]*x[i_]; //--- check if(i==1) err=MathMax(err,MathAbs(tmp-beta)); else err=MathMax(err,MathAbs(tmp)); } meg=MathMax(meg,err/xscale); } //--- ApplyReflectionFromTheLeft for(i=1;i<=m;i++) { x[i]=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { a[i].Set(j,2*CMath::RandomReal()-1); b[i].Set(j,a[i][j]); } } //--- function call CReflections::GenerateReflection(v,m,tau); beta=v[1]; v[1]=1; //--- function call CReflections::ApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work); //--- change values for(i=1;i<=m;i++) { for(j=1;j<=m;j++) { //--- check if(i==j) h[i].Set(j,1-tau*v[i]*v[j]); else h[i].Set(j,-(tau*v[i]*v[j])); } } //--- calculation for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { tmp=0.0; for(i_=1;i_<=m;i_++) tmp+=h[i][i_]*a[i_][j]; c[i].Set(j,tmp); } } //--- change value err=0; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) err=MathMax(err,MathAbs(b[i][j]-c[i][j])); } mel=MathMax(mel,err); //--- ApplyReflectionFromTheRight for(i=1;i<=n;i++) { x[i]=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { a[i].Set(j,2*CMath::RandomReal()-1); b[i].Set(j,a[i][j]); } } //--- function call CReflections::GenerateReflection(v,n,tau); beta=v[1]; v[1]=1; //--- function call CReflections::ApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work); //--- change value for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { //--- check if(i==j) h[i].Set(j,1-tau*v[i]*v[j]); else h[i].Set(j,-(tau*v[i]*v[j])); } } //--- calculation for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { tmp=0.0; for(i_=1;i_<=n;i_++) tmp+=a[i][i_]*h[i_][j]; c[i].Set(j,tmp); } } err=0; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) err=MathMax(err,MathAbs(b[i][j]-c[i][j])); } mer=MathMax(mer,err); } } } //--- Overflow crash test ArrayResize(x,11); ArrayResize(v,11); for(i=1;i<=10;i++) v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1); //--- function call CReflections::GenerateReflection(v,10,tau); result=(meg<=threshold && mel<=threshold) && mer<=threshold; //--- check if(!silent) { Print("TESTING REFLECTIONS"); Print("Pass count is "); Print("{0,0:d}",passcount); Print("Generate absolute error is "); Print("{0,5:E3}",meg); Print("Apply(Left) absolute error is "); Print("{0,5:E3}",mel); Print("Apply(Right) absolute error is "); Print("{0,5:E3}",mer); Print("Overflow crash test passed"); //--- check if(result) Print("TEST PASSED"); else Print("TEST FAILED"); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CComplexReflections | //+------------------------------------------------------------------+ class CTestCReflectionsUnit { public: //--- constructor, destructor CTestCReflectionsUnit(void); ~CTestCReflectionsUnit(void); //--- public method static bool TestCReflections(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestCReflectionsUnit::CTestCReflectionsUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestCReflectionsUnit::~CTestCReflectionsUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CComplexReflections | //+------------------------------------------------------------------+ static bool CTestCReflectionsUnit::TestCReflections(const bool silent) { //--- create variables int i=0; int j=0; int n=0; int m=0; int maxmn=0; complex tmp=0; complex beta=0; complex tau=0; double err=0; double mer=0; double mel=0; double meg=0; int pass=0; int passcount=0; bool waserrors; double threshold=0; int i_=0; //--- create arrays complex x[]; complex v[]; complex work[]; //--- create matrix CMatrixComplex h; CMatrixComplex a; CMatrixComplex b; CMatrixComplex c; //--- initialization threshold=1000*CMath::m_machineepsilon; passcount=1000; mer=0; mel=0; meg=0; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Task n=1+CMath::RandomInteger(10); m=1+CMath::RandomInteger(10); maxmn=MathMax(m,n); //--- Initialize ArrayResize(x,maxmn+1); ArrayResize(v,maxmn+1); ArrayResize(work,maxmn+1); h.Resize(maxmn+1,maxmn+1); a.Resize(maxmn+1,maxmn+1); b.Resize(maxmn+1,maxmn+1); c.Resize(maxmn+1,maxmn+1); //--- GenerateReflection for(i=1;i<=n;i++) { x[i].re=2*CMath::RandomReal()-1; x[i].im=2*CMath::RandomReal()-1; v[i]=x[i]; } //--- function call CComplexReflections::ComplexGenerateReflection(v,n,tau); //--- change values beta=v[1]; v[1]=1; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { //--- check if(i==j) h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1); else h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j]))); } } //--- calculation err=0; for(i=1;i<=n;i++) { tmp=0.0; for(i_=1;i_<=n;i_++) tmp+=CMath::Conj(h[i_][i])*x[i_]; //--- check if(i==1) err=MathMax(err,CMath::AbsComplex(tmp-beta)); else err=MathMax(err,CMath::AbsComplex(tmp)); } err=MathMax(err,MathAbs(beta.im)); meg=MathMax(meg,err); //--- ApplyReflectionFromTheLeft for(i=1;i<=m;i++) { x[i].re=2*CMath::RandomReal()-1; x[i].im=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); b[i].Set(j,a[i][j]); } } //--- function call CComplexReflections::ComplexGenerateReflection(v,m,tau); beta=v[1]; v[1]=1; //--- function call CComplexReflections::ComplexApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work); //--- change values for(i=1;i<=m;i++) { for(j=1;j<=m;j++) { //--- check if(i==j) h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1); else h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j]))); } } //--- calculation for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { tmp=0.0; for(i_=1;i_<=m;i_++) tmp+=h[i][i_]*a[i_][j]; c[i].Set(j,tmp); } } err=0; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) err=MathMax(err,CMath::AbsComplex(b[i][j]-c[i][j])); } mel=MathMax(mel,err); //--- ApplyReflectionFromTheRight for(i=1;i<=n;i++) { x[i]=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { a[i].Set(j,2*CMath::RandomReal()-1); b[i].Set(j,a[i][j]); } } //--- function call CComplexReflections::ComplexGenerateReflection(v,n,tau); beta=v[1]; v[1]=1; //--- function call CComplexReflections::ComplexApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work); //--- change values for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { //--- check if(i==j) h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1); else h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j]))); } } //--- calculation for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { tmp=0.0; for(i_=1;i_<=n;i_++) tmp+=a[i][i_]*h[i_][j]; c[i].Set(j,tmp); } } err=0; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) err=MathMax(err,CMath::AbsComplex(b[i][j]-c[i][j])); } mer=MathMax(mer,err); } //--- Overflow crash test ArrayResize(x,11); ArrayResize(v,11); for(i=1;i<=10;i++) v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1); //--- function call CComplexReflections::ComplexGenerateReflection(v,10,tau); //--- report waserrors=(meg>threshold || mel>threshold) || mer>threshold; //--- check if(!silent) { Print("TESTING COMPLEX REFLECTIONS"); Print("Generate error: "); Print("{0,5:E3}",meg); Print("Apply(L) error: "); Print("{0,5:E3}",mel); Print("Apply(R) error: "); Print("{0,5:E3}",mer); Print("Threshold: "); Print("{0,5:E3}",threshold); Print("Overflow crash test: PASSED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CSblas | //+------------------------------------------------------------------+ class CTestSblasUnit { public: //--- constructor, destructor CTestSblasUnit(void); ~CTestSblasUnit(void); //--- public method static bool TestSblas(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSblasUnit::CTestSblasUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSblasUnit::~CTestSblasUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CSblas | //+------------------------------------------------------------------+ static bool CTestSblasUnit::TestSblas(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int j=0; int i1=0; int i2=0; bool waserrors; double mverr=0; double threshold=0; double alpha=0; double v=0; int i_=0; int i1_=0; //--- create arrays double x[]; double y1[]; double y2[]; double y3[]; //--- create matrix CMatrixDouble a; CMatrixDouble ua; CMatrixDouble la; //--- initialization mverr=0; waserrors=false; maxn=10; threshold=1000*CMath::m_machineepsilon; //--- Test MV for(n=2;n<=maxn;n++) { //--- allocation a.Resize(n+1,n+1); ua.Resize(n+1,n+1); la.Resize(n+1,n+1); ArrayResize(x,n+1); ArrayResize(y1,n+1); ArrayResize(y2,n+1); ArrayResize(y3,n+1); //--- fill A,UA,LA for(i=1;i<=n;i++) { a[i].Set(i,2*CMath::RandomReal()-1); for(j=i+1;j<=n;j++) { a[i].Set(j,2*CMath::RandomReal()-1); a[j].Set(i,a[i][j]); } } //--- change values for(i=1;i<=n;i++) { for(j=1;j<=n;j++) ua[i].Set(j,0); } //--- change values for(i=1;i<=n;i++) { for(j=i;j<=n;j++) ua[i].Set(j,a[i][j]); } //--- change values for(i=1;i<=n;i++) { for(j=1;j<=n;j++) la[i].Set(j,0); } //--- change values for(i=1;i<=n;i++) { for(j=1;j<=i;j++) la[i].Set(j,a[i][j]); } //--- test on different I1,I2 for(i1=1;i1<=n;i1++) { for(i2=i1;i2<=n;i2++) { //--- Fill X,choose Alpha for(i=1;i<=i2-i1+1;i++) x[i]=2*CMath::RandomReal()-1; alpha=2*CMath::RandomReal()-1; //--- calculate A*x,UA*x,LA*x for(i=i1;i<=i2;i++) { i1_=1-i1; v=0.0; for(i_=i1;i_<=i2;i_++) v+=a[i][i_]*x[i_+i1_]; y1[i-i1+1]=alpha*v; } //--- function call CSblas::SymmetricMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2); //--- function call CSblas::SymmetricMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3); //--- Calculate error for(i_=1;i_<=i2-i1+1;i_++) y2[i_]=y2[i_]-y1[i_]; v=0.0; for(i_=1;i_<=i2-i1+1;i_++) v+=y2[i_]*y2[i_]; //--- search errors mverr=MathMax(mverr,MathSqrt(v)); for(i_=1;i_<=i2-i1+1;i_++) y3[i_]=y3[i_]-y1[i_]; v=0.0; for(i_=1;i_<=i2-i1+1;i_++) v+=y3[i_]*y3[i_]; //--- search errors mverr=MathMax(mverr,MathSqrt(v)); } } } //--- report waserrors=mverr>threshold; //--- check if(!silent) { Print("TESTING SYMMETRIC BLAS"); Print("MV error: "); Print("{0,5:E3}",mverr); Print("Threshold: "); Print("{0,5:E3}",threshold); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class COrtFac | //+------------------------------------------------------------------+ class CTestOrtFacUnit { private: //--- private methods static double RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n); static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity); static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity); static void InternalMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2); static void TestRQRProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &qrerrors); static void TestCQRProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &qrerrors); static void TestRLQProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &lqerrors); static void TestCLQProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &lqerrors); static void TestRBdProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &bderrors); static void TestRHessProblem(CMatrixDouble &a,const int n,const double threshold,bool &hesserrors); static void TestRTdProblem(CMatrixDouble &a,const int n,const double threshold,bool &tderrors); static void TestCTdProblem(CMatrixComplex &a,const int n,const double threshold,bool &tderrors); public: //--- constructor, destructor CTestOrtFacUnit(void); ~CTestOrtFacUnit(void); //--- public method static bool TestOrtFac(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestOrtFacUnit::CTestOrtFacUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestOrtFacUnit::~CTestOrtFacUnit(void) { } //+------------------------------------------------------------------+ //| Main unittest subroutine | //+------------------------------------------------------------------+ static bool CTestOrtFacUnit::TestOrtFac(const bool silent) { int maxmn=0; double threshold=0; int passcount=0; int mx=0; int m=0; int n=0; int pass=0; int i=0; int j=0; bool rqrerrors; bool rlqerrors; bool cqrerrors; bool clqerrors; bool rbderrors; bool rhesserrors; bool rtderrors; bool ctderrors; bool waserrors; //--- create matrix CMatrixDouble ra; CMatrixComplex ca; //--- initialization waserrors=false; rqrerrors=false; rlqerrors=false; cqrerrors=false; clqerrors=false; rbderrors=false; rhesserrors=false; rtderrors=false; ctderrors=false; maxmn=3*CAblas::AblasBlockSize()+1; passcount=1; threshold=5*1000*CMath::m_machineepsilon; //--- Different problems for(mx=1;mx<=maxmn;mx++) { for(pass=1;pass<=passcount;pass++) { //--- Rectangular factorizations: QR,LQ,bidiagonal //--- Matrix types: zero,dense,sparse n=1+CMath::RandomInteger(mx); m=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomReal()>0.5) n=mx; else m=mx; //--- allocation ra.Resize(m,n); ca.Resize(m,n); //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- function calls TestRQRProblem(ra,m,n,threshold,rqrerrors); TestRLQProblem(ra,m,n,threshold,rlqerrors); TestCQRProblem(ca,m,n,threshold,cqrerrors); TestCLQProblem(ca,m,n,threshold,clqerrors); TestRBdProblem(ra,m,n,threshold,rbderrors); //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,2*CMath::RandomReal()-1); ca[i].SetRe(j,2*CMath::RandomReal()-1); ca[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- function calls TestRQRProblem(ra,m,n,threshold,rqrerrors); TestRLQProblem(ra,m,n,threshold,rlqerrors); TestCQRProblem(ca,m,n,threshold,cqrerrors); TestCLQProblem(ca,m,n,threshold,clqerrors); TestRBdProblem(ra,m,n,threshold,rbderrors); //--- function calls RMatrixFillSparseA(ra,m,n,0.95); CMatrixFillSparseA(ca,m,n,0.95); //--- function calls TestRQRProblem(ra,m,n,threshold,rqrerrors); TestRLQProblem(ra,m,n,threshold,rlqerrors); TestCQRProblem(ca,m,n,threshold,cqrerrors); TestCLQProblem(ca,m,n,threshold,clqerrors); TestRBdProblem(ra,m,n,threshold,rbderrors); //--- Square factorizations: Hessenberg,tridiagonal //--- Matrix types: zero,dense,sparse ra.Resize(mx,mx); ca.Resize(mx,mx); //--- change values for(i=0;i<=mx-1;i++) { for(j=0;j<=mx-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- function call TestRHessProblem(ra,mx,threshold,rhesserrors); //--- change values for(i=0;i<=mx-1;i++) { for(j=0;j<=mx-1;j++) { ra[i].Set(j,2*CMath::RandomReal()-1); ca[i].SetRe(j,2*CMath::RandomReal()-1); ca[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- function calls TestRHessProblem(ra,mx,threshold,rhesserrors); RMatrixFillSparseA(ra,mx,mx,0.95); CMatrixFillSparseA(ca,mx,mx,0.95); TestRHessProblem(ra,mx,threshold,rhesserrors); //--- Symetric factorizations: tridiagonal //--- Matrix types: zero,dense,sparse ra.Resize(mx,mx); ca.Resize(mx,mx); //--- change values for(i=0;i<=mx-1;i++) { for(j=0;j<=mx-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- function calls TestRTdProblem(ra,mx,threshold,rtderrors); TestCTdProblem(ca,mx,threshold,ctderrors); //--- change values for(i=0;i<=mx-1;i++) { for(j=i;j<=mx-1;j++) { ra[i].Set(j,2*CMath::RandomReal()-1); ca[i].SetRe(j,2*CMath::RandomReal()-1); ca[i].SetIm(j,2*CMath::RandomReal()-1); ra[j].Set(i,ra[i][j]); ca[j].Set(i,CMath::Conj(ca[i][j])); } } for(i=0;i<=mx-1;i++) ca[i].Set(i,2*CMath::RandomReal()-1); //--- function calls TestRTdProblem(ra,mx,threshold,rtderrors); TestCTdProblem(ca,mx,threshold,ctderrors); RMatrixFillSparseA(ra,mx,mx,0.95); CMatrixFillSparseA(ca,mx,mx,0.95); //--- change values for(i=0;i<=mx-1;i++) { for(j=i;j<=mx-1;j++) { ra[j].Set(i,ra[i][j]); ca[j].Set(i,CMath::Conj(ca[i][j])); } } for(i=0;i<=mx-1;i++) ca[i].Set(i,2*CMath::RandomReal()-1); //--- function calls TestRTdProblem(ra,mx,threshold,rtderrors); TestCTdProblem(ca,mx,threshold,ctderrors); } } //--- report waserrors=((((((rqrerrors || rlqerrors) || cqrerrors) || clqerrors) || rbderrors) || rhesserrors) || rtderrors) || ctderrors; //--- check if(!silent) { Print("TESTING ORTFAC UNIT"); Print("RQR ERRORS: "); //--- check if(!rqrerrors) Print("OK"); else Print("FAILED"); Print("RLQ ERRORS: "); //--- check if(!rlqerrors) Print("OK"); else Print("FAILED"); Print("CQR ERRORS: "); //--- check if(!cqrerrors) Print("OK"); else Print("FAILED"); Print("CLQ ERRORS: "); //--- check if(!clqerrors) Print("OK"); else Print("FAILED"); Print("RBD ERRORS: "); //--- check if(!rbderrors) Print("OK"); else Print("FAILED"); Print("RHESS ERRORS: "); //--- check if(!rhesserrors) Print("OK"); else Print("FAILED"); Print("RTD ERRORS: "); //--- check if(!rtderrors) Print("OK"); else Print("FAILED"); Print("CTD ERRORS: "); //--- check if(!ctderrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Diff | //+------------------------------------------------------------------+ static double CTestOrtFacUnit::RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b, const int m,const int n) { //--- create variables double result=0; int i=0; int j=0; //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) result=MathMax(result,MathAbs(b[i][j]-a[i][j])); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m, const int n,CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m, const int n,CMatrixComplex &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::RandomReal()>=sparcity) a[i].Set(j,2*CMath::RandomReal()-1); else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::RandomReal()>=sparcity) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| Matrix multiplication | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::InternalMatrixMatrixMultiply(CMatrixDouble &a, const int ai1, const int ai2, const int aj1, const int aj2, const bool transa, CMatrixDouble &b, const int bi1, const int bi2, const int bj1, const int bj2, const bool transb, CMatrixDouble &c, const int ci1, const int ci2, const int cj1, const int cj2) { //--- create variables int arows=0; int acols=0; int brows=0; int bcols=0; int crows=0; int ccols=0; int i=0; int j=0; int k=0; int l=0; int r=0; double v=0; double beta=0; double alpha=0; int i_=0; int i1_=0; //--- create array double work[]; //--- Pre-setup k=MathMax(ai2-ai1+1,aj2-aj1+1); k=MathMax(k,bi2-bi1+1); k=MathMax(k,bj2-bj1+1); //--- allocation ArrayResize(work,k+1); //--- initialization beta=0; alpha=1; //--- Setup if(!transa) { arows=ai2-ai1+1; acols=aj2-aj1+1; } else { arows=aj2-aj1+1; acols=ai2-ai1+1; } //--- check if(!transb) { brows=bi2-bi1+1; bcols=bj2-bj1+1; } else { brows=bj2-bj1+1; bcols=bi2-bi1+1; } //--- check if(!CAp::Assert(acols==brows,"MatrixMatrixMultiply: incorrect matrix sizes!")) return; //--- check if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0) return; //--- change values crows=arows; ccols=bcols; //--- Test WORK i=MathMax(arows,acols); i=MathMax(brows,i); i=MathMax(i,bcols); work[1]=0; work[i]=0; //--- Prepare C if(beta==0.0) { for(i=ci1;i<=ci2;i++) { for(j=cj1;j<=cj2;j++) c[i].Set(j,0); } } else { for(i=ci1;i<=ci2;i++) { for(i_=cj1;i_<=cj2;i_++) c[i].Set(i_,beta*c[i][i_]); } } //--- A*B if(!transa && !transb) { for(l=ai1;l<=ai2;l++) { for(r=bi1;r<=bi2;r++) { //--- change values v=alpha*a[l][aj1+r-bi1]; k=ci1+l-ai1; i1_=bj1-cj1; //--- calculation for(i_=cj1;i_<=cj2;i_++) c[k].Set(i_,c[k][i_]+v*b[r][i_+i1_]); } } return; } //--- A*B' if(!transa && transb) { //--- check if(arows*acolsthreshold; } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=MathMin(i,n-1)-1;j++) qrerrors=qrerrors || r[i][j]!=0.0; } for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=q[i][i_]*q[j][i_]; //--- check if(i==j) v=v-1; qrerrors=qrerrors || MathAbs(v)>=threshold; } } //--- Test for other errors k=1+CMath::RandomInteger(m); //--- function call COrtFac::RMatrixQRUnpackQ(b,m,n,taub,k,q2); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=k-1;j++) qrerrors=qrerrors || MathAbs(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestCQRProblem(CMatrixComplex &a,const int m, const int n,const double threshold, bool &qrerrors) { //--- create variables int i=0; int j=0; int k=0; complex v=0; int i_=0; //--- create array complex taub[]; //--- create matrix CMatrixComplex b; CMatrixComplex q; CMatrixComplex r; CMatrixComplex q2; //--- Test decompose-and-unpack error CMatrixMakeACopy(a,m,n,b); //--- function calls COrtFac::CMatrixQR(b,m,n,taub); COrtFac::CMatrixQRUnpackQ(b,m,n,taub,m,q); COrtFac::CMatrixQRUnpackR(b,m,n,r); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=q[i][i_]*r[i_][j]; qrerrors=qrerrors || CMath::AbsComplex(v-a[i][j])>threshold; } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=MathMin(i,n-1)-1;j++) qrerrors=qrerrors || r[i][j]!=0; } for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=q[i][i_]*CMath::Conj(q[j][i_]); //--- check if(i==j) v=v-1; qrerrors=qrerrors || CMath::AbsComplex(v)>=threshold; } } //--- Test for other errors k=1+CMath::RandomInteger(m); //--- function call COrtFac::CMatrixQRUnpackQ(b,m,n,taub,k,q2); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=k-1;j++) qrerrors=qrerrors || CMath::AbsComplex(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestRLQProblem(CMatrixDouble &a,const int m, const int n,const double threshold, bool &lqerrors) { //--- create variables int i=0; int j=0; int k=0; double v=0; int i_=0; //--- create array double taub[]; //--- create matrix CMatrixDouble b; CMatrixDouble q; CMatrixDouble l; CMatrixDouble q2; //--- Test decompose-and-unpack error RMatrixMakeACopy(a,m,n,b); //--- function calls COrtFac::RMatrixLQ(b,m,n,taub); COrtFac::RMatrixLQUnpackQ(b,m,n,taub,n,q); COrtFac::RMatrixLQUnpackL(b,m,n,l); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=l[i][i_]*q[i_][j]; lqerrors=lqerrors || MathAbs(v-a[i][j])>=threshold; } } //--- search errors for(i=0;i<=m-1;i++) { for(j=MathMin(i,n-1)+1;j<=n-1;j++) lqerrors=lqerrors || l[i][j]!=0.0; } for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i][i_]*q[j][i_]; //--- check if(i==j) v=v-1; lqerrors=lqerrors || MathAbs(v)>=threshold; } } //--- Test for other errors k=1+CMath::RandomInteger(n); //--- function call COrtFac::RMatrixLQUnpackQ(b,m,n,taub,k,q2); //--- search errors for(i=0;i<=k-1;i++) { for(j=0;j<=n-1;j++) lqerrors=lqerrors || MathAbs(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestCLQProblem(CMatrixComplex &a,const int m, const int n,const double threshold, bool &lqerrors) { //--- create variables int i=0; int j=0; int k=0; complex v=0; int i_=0; //--- create array complex taub[]; //--- create matrix CMatrixComplex b; CMatrixComplex q; CMatrixComplex l; CMatrixComplex q2; //--- Test decompose-and-unpack error CMatrixMakeACopy(a,m,n,b); //--- function calls COrtFac::CMatrixLQ(b,m,n,taub); COrtFac::CMatrixLQUnpackQ(b,m,n,taub,n,q); COrtFac::CMatrixLQUnpackL(b,m,n,l); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=l[i][i_]*q[i_][j]; lqerrors=lqerrors || CMath::AbsComplex(v-a[i][j])>=threshold; } } //--- search errors for(i=0;i<=m-1;i++) { for(j=MathMin(i,n-1)+1;j<=n-1;j++) lqerrors=lqerrors || l[i][j]!=0; } for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i][i_]*CMath::Conj(q[j][i_]); //--- check if(i==j) v=v-1; lqerrors=lqerrors || CMath::AbsComplex(v)>=threshold; } } //--- Test for other errors k=1+CMath::RandomInteger(n); //--- function call COrtFac::CMatrixLQUnpackQ(b,m,n,taub,k,q2); //--- search errors for(i=0;i<=k-1;i++) { for(j=0;j<=n-1;j++) lqerrors=lqerrors || CMath::AbsComplex(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestRBdProblem(CMatrixDouble &a,const int m, const int n,const double threshold, bool &bderrors) { //--- create variables int i=0; int j=0; int k=0; bool up; double v=0; int mtsize=0; int i_=0; //--- create arrays double taup[]; double tauq[]; double d[]; double e[]; //--- create matrix CMatrixDouble t; CMatrixDouble pt; CMatrixDouble q; CMatrixDouble r; CMatrixDouble bd; CMatrixDouble x; CMatrixDouble r1; CMatrixDouble r2; //--- Bidiagonal decomposition error RMatrixMakeACopy(a,m,n,t); //--- function calls COrtFac::RMatrixBD(t,m,n,tauq,taup); COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,m,q); COrtFac::RMatrixBDUnpackPT(t,m,n,taup,n,pt); COrtFac::RMatrixBDUnpackDiagonals(t,m,n,up,d,e); //--- allocation bd.Resize(m,n); //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) bd[i].Set(j,0); } for(i=0;i<=MathMin(m,n)-1;i++) bd[i].Set(i,d[i]); //--- check if(up) { for(i=0;i<=MathMin(m,n)-2;i++) bd[i].Set(i+1,e[i]); } else { for(i=0;i<=MathMin(m,n)-2;i++) bd[i+1].Set(i,e[i]); } //--- allocation r.Resize(m,n); //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=m-1;i_++) v+=q[i][i_]*bd[i_][j]; r[i].Set(j,v); } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=r[i][i_]*pt[i_][j]; bderrors=bderrors || MathAbs(v-a[i][j])>threshold; } } //--- Orthogonality test for Q/PT for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=q[i_][i]*q[i_][j]; //--- check if(i==j) bderrors=bderrors || MathAbs(v-1)>threshold; else bderrors=bderrors || MathAbs(v)>threshold; } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=pt[i][i_]*pt[j][i_]; //--- check if(i==j) bderrors=bderrors || MathAbs(v-1)>threshold; else bderrors=bderrors || MathAbs(v)>threshold; } } //--- Partial unpacking test k=1+CMath::RandomInteger(m); //--- function call COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,k,r); //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=k-1;j++) bderrors=bderrors || MathAbs(r[i][j]-q[i][j])>10*CMath::m_machineepsilon; } k=1+CMath::RandomInteger(n); //--- function call COrtFac::RMatrixBDUnpackPT(t,m,n,taup,k,r); //--- search errors for(i=0;i<=k-1;i++) { for(j=0;j<=n-1;j++) bderrors=bderrors || r[i][j]-pt[i][j]!=0.0; } //--- Multiplication test x.Resize(MathMax(m,n),MathMax(m,n)); r.Resize(MathMax(m,n),MathMax(m,n)); r1.Resize(MathMax(m,n),MathMax(m,n)); r2.Resize(MathMax(m,n),MathMax(m,n)); //--- change values for(i=0;i<=MathMax(m,n)-1;i++) { for(j=0;j<=MathMax(m,n)-1;j++) x[i].Set(j,2*CMath::RandomReal()-1); } mtsize=1+CMath::RandomInteger(MathMax(m,n)); //--- function calls RMatrixMakeACopy(x,mtsize,m,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,false,r1,0,mtsize-1,0,m-1); RMatrixMakeACopy(x,mtsize,m,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,false); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,m)>threshold; //--- function calls RMatrixMakeACopy(x,mtsize,m,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,true,r1,0,mtsize-1,0,m-1); RMatrixMakeACopy(x,mtsize,m,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,true); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,m)>threshold; //--- function calls RMatrixMakeACopy(x,m,mtsize,r); InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,false,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1); RMatrixMakeACopy(x,m,mtsize,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,false); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,m,mtsize)>threshold; //--- function calls RMatrixMakeACopy(x,m,mtsize,r); InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,true,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1); RMatrixMakeACopy(x,m,mtsize,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,true); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,m,mtsize)>threshold; //--- function calls RMatrixMakeACopy(x,mtsize,n,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,true,r1,0,mtsize-1,0,n-1); RMatrixMakeACopy(x,mtsize,n,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,false); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,n)>threshold; //--- function calls RMatrixMakeACopy(x,mtsize,n,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,false,r1,0,mtsize-1,0,n-1); RMatrixMakeACopy(x,mtsize,n,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,true); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,n)>threshold; //--- function calls RMatrixMakeACopy(x,n,mtsize,r); InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,true,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1); RMatrixMakeACopy(x,n,mtsize,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,false); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,n,mtsize)>threshold; //--- function calls RMatrixMakeACopy(x,n,mtsize,r); InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,false,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1); RMatrixMakeACopy(x,n,mtsize,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,true); //--- search errors bderrors=bderrors || RMatrixDiff(r1,r2,n,mtsize)>threshold; } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestRHessProblem(CMatrixDouble &a,const int n, const double threshold,bool &hesserrors) { //--- create variables int i=0; int j=0; double v=0; int i_=0; //--- create array double tau[]; //--- create matrix CMatrixDouble b; CMatrixDouble h; CMatrixDouble q; CMatrixDouble t1; CMatrixDouble t2; //--- function call RMatrixMakeACopy(a,n,n,b); //--- Decomposition COrtFac::RMatrixHessenberg(b,n,tau); COrtFac::RMatrixHessenbergUnpackQ(b,n,tau,q); COrtFac::RMatrixHessenbergUnpackH(b,n,h); //--- Matrix properties for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i_][i]*q[i_][j]; //--- check if(i==j) v=v-1; hesserrors=hesserrors || MathAbs(v)>threshold; } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=i-2;j++) hesserrors=hesserrors || h[i][j]!=0.0; } //--- Decomposition error t1.Resize(n,n); t2.Resize(n,n); //--- function calls InternalMatrixMatrixMultiply(q,0,n-1,0,n-1,false,h,0,n-1,0,n-1,false,t1,0,n-1,0,n-1); InternalMatrixMatrixMultiply(t1,0,n-1,0,n-1,false,q,0,n-1,0,n-1,true,t2,0,n-1,0,n-1); //--- search errors hesserrors=hesserrors || RMatrixDiff(t2,a,n,n)>threshold; } //+------------------------------------------------------------------+ //| Tridiagonal tester | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestRTdProblem(CMatrixDouble &a,const int n, const double threshold,bool &tderrors) { //--- create variables int i=0; int j=0; double v=0; int i_=0; //--- create arrays double tau[]; double d[]; double e[]; //--- create matrix CMatrixDouble ua; CMatrixDouble la; CMatrixDouble t; CMatrixDouble q; CMatrixDouble t2; CMatrixDouble t3; //--- allocation ua.Resize(n,n); la.Resize(n,n); t.Resize(n,n); q.Resize(n,n); t2.Resize(n,n); t3.Resize(n,n); //--- fill for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) ua[i].Set(j,0); } //--- change values for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) ua[i].Set(j,a[i][j]); } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) la[i].Set(j,0); } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=i;j++) la[i].Set(j,a[i][j]); } //--- Test 2tridiagonal: upper COrtFac::SMatrixTD(ua,n,true,tau,d,e); COrtFac::SMatrixTDUnpackQ(ua,n,true,tau,q); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) t[i].Set(j,0); } for(i=0;i<=n-1;i++) t[i].Set(i,d[i]); for(i=0;i<=n-2;i++) { t[i].Set(i+1,e[i]); t[i+1].Set(i,e[i]); } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i_][i]*a[i_][j]; t2[i].Set(j,v); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=t2[i][i_]*q[i_][j]; t3[i].Set(j,v); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) tderrors=tderrors || MathAbs(t3[i][j]-t[i][j])>threshold; } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i][i_]*q[j][i_]; //--- check if(i==j) v=v-1; tderrors=tderrors || MathAbs(v)>threshold; } } //--- Test 2tridiagonal: lower COrtFac::SMatrixTD(la,n,false,tau,d,e); COrtFac::SMatrixTDUnpackQ(la,n,false,tau,q); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) t[i].Set(j,0); } for(i=0;i<=n-1;i++) t[i].Set(i,d[i]); for(i=0;i<=n-2;i++) { t[i].Set(i+1,e[i]); t[i+1].Set(i,e[i]); } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i_][i]*a[i_][j]; t2[i].Set(j,v); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=t2[i][i_]*q[i_][j]; t3[i].Set(j,v); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) tderrors=tderrors || MathAbs(t3[i][j]-t[i][j])>threshold; } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i][i_]*q[j][i_]; //--- check if(i==j) v=v-1; tderrors=tderrors || MathAbs(v)>threshold; } } } //+------------------------------------------------------------------+ //| Hermitian problem tester | //+------------------------------------------------------------------+ static void CTestOrtFacUnit::TestCTdProblem(CMatrixComplex &a,const int n, const double threshold,bool &tderrors) { //--- create variables int i=0; int j=0; complex v=0; int i_=0; //--- create arrays complex tau[]; double d[]; double e[]; //--- create matrix CMatrixComplex ua; CMatrixComplex la; CMatrixComplex t; CMatrixComplex q; CMatrixComplex t2; CMatrixComplex t3; //--- allocation ua.Resize(n,n); la.Resize(n,n); t.Resize(n,n); q.Resize(n,n); t2.Resize(n,n); t3.Resize(n,n); //--- fill for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) ua[i].Set(j,0); } //--- change values for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) ua[i].Set(j,a[i][j]); } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) la[i].Set(j,0); } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=i;j++) la[i].Set(j,a[i][j]); } //--- Test 2tridiagonal: upper COrtFac::HMatrixTD(ua,n,true,tau,d,e); COrtFac::HMatrixTDUnpackQ(ua,n,true,tau,q); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) t[i].Set(j,0); } for(i=0;i<=n-1;i++) t[i].Set(i,d[i]); for(i=0;i<=n-2;i++) { t[i].Set(i+1,e[i]); t[i+1].Set(i,e[i]); } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=CMath::Conj(q[i_][i])*a[i_][j]; t2[i].Set(j,v); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=t2[i][i_]*q[i_][j]; t3[i].Set(j,v); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) tderrors=tderrors || CMath::AbsComplex(t3[i][j]-t[i][j])>threshold; } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i][i_]*CMath::Conj(q[j][i_]); //--- check if(i==j) v=v-1; tderrors=tderrors || CMath::AbsComplex(v)>threshold; } } //--- Test 2tridiagonal: lower COrtFac::HMatrixTD(la,n,false,tau,d,e); COrtFac::HMatrixTDUnpackQ(la,n,false,tau,q); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) t[i].Set(j,0); } for(i=0;i<=n-1;i++) t[i].Set(i,d[i]); for(i=0;i<=n-2;i++) { t[i].Set(i+1,e[i]); t[i+1].Set(i,e[i]); } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=CMath::Conj(q[i_][i])*a[i_][j]; t2[i].Set(j,v); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=t2[i][i_]*q[i_][j]; t3[i].Set(j,v); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) tderrors=tderrors || CMath::AbsComplex(t3[i][j]-t[i][j])>threshold; } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { v=0.0; for(i_=0;i_<=n-1;i_++) v+=q[i][i_]*CMath::Conj(q[j][i_]); //--- check if(i==j) v=v-1; tderrors=tderrors || CMath::AbsComplex(v)>threshold; } } } //+------------------------------------------------------------------+ //| Testing class CEigenVDetect | //+------------------------------------------------------------------+ class CTestEVDUnit { private: //--- private methods static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity); static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity); static void RMatrixSymmetricSplit(CMatrixDouble &a,const int n,CMatrixDouble &al,CMatrixDouble &au); static void CMatrixHermitianSplit(CMatrixComplex &a,const int n,CMatrixComplex &al,CMatrixComplex &au); static void Unset2D(CMatrixDouble &a); static void CUnset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static void CUnset1D(complex &a[]); static double TdTestProduct(double &d[],double &e[],const int n,CMatrixDouble &z,double &lambdav[]); static double TestProduct(CMatrixDouble &a,const int n,CMatrixDouble &z,double &lambdav[]); static double TestOrt(CMatrixDouble &z,const int n); static double TestCProduct(CMatrixComplex &a,const int n,CMatrixComplex &z,double &lambdav[]); static double TestCOrt(CMatrixComplex &z,const int n); static void TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,CMatrixDouble &au,const int n,const double threshold,bool &serrors,int &failc,int &runs); static void TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,CMatrixComplex &au,const int n,const double threshold,bool &herrors,int &failc,int &runs); static void TestSEVDBiProblem(CMatrixDouble &afull,CMatrixDouble &al,CMatrixDouble &au,const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs); static void TestHEVDBiProblem(CMatrixComplex &afull,CMatrixComplex &al,CMatrixComplex &au,const int n,const bool distvals,const double threshold,bool &herrors,int &failc,int &runs); static void TestTdEVDProblem(double &d[],double &e[],const int n,const double threshold,bool &tderrors,int &failc,int &runs); static void TestTdEVDBiProblem(double &d[],double &e[],const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs); static void TestNSEVDProblem(CMatrixDouble &a,const int n,const double threshold,bool &nserrors,int &failc,int &runs); static void TestEVDSet(const int n,const double threshold,double bithreshold,int &failc,int &runs,bool &nserrors,bool &serrors,bool &herrors,bool &tderrors,bool &sbierrors,bool &hbierrors,bool &tdbierrors); public: //--- constructor, destructor CTestEVDUnit(void); ~CTestEVDUnit(void); //--- public method static bool TestEVD(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestEVDUnit::CTestEVDUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestEVDUnit::~CTestEVDUnit(void) { } //+------------------------------------------------------------------+ //| Testing symmetric EVD subroutine | //+------------------------------------------------------------------+ static bool CTestEVDUnit::TestEVD(const bool silent) { //--- create variables int n=0; int j=0; int failc=0; int runs=0; double failthreshold=0; double threshold=0; double bithreshold=0; bool waserrors; bool nserrors; bool serrors; bool herrors; bool tderrors; bool sbierrors; bool hbierrors; bool tdbierrors; bool wfailed; //--- create matrix CMatrixDouble ra; //--- initialization failthreshold=0.005; threshold=100000*CMath::m_machineepsilon; bithreshold=1.0E-6; nserrors=false; serrors=false; herrors=false; tderrors=false; sbierrors=false; hbierrors=false; tdbierrors=false; failc=0; runs=0; //--- Test problems for(n=1;n<=CAblas::AblasBlockSize();n++) TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors); for(j=2;j<=3;j++) { for(n=j*CAblas::AblasBlockSize()-1;n<=j*CAblas::AblasBlockSize()+1;n++) TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors); } //--- report wfailed=(double)failc/(double)runs>failthreshold; waserrors=((((((nserrors || serrors) || herrors) || tderrors) || sbierrors) || hbierrors) || tdbierrors) || wfailed; //--- check if(!silent) { Print("TESTING EVD UNIT"); Print("NS ERRORS: "); //--- check if(!nserrors) Print("OK"); else Print("FAILED"); Print("S ERRORS: "); //--- check if(!serrors) Print("OK"); else Print("FAILED"); Print("H ERRORS: "); //--- check if(!herrors) Print("OK"); else Print("FAILED"); Print("TD ERRORS: "); //--- check if(!tderrors) Print("OK"); else Print("FAILED"); Print("SBI ERRORS: "); //--- check if(!sbierrors) Print("OK"); else Print("FAILED"); Print("HBI ERRORS: "); //--- check if(!hbierrors) Print("OK"); else Print("FAILED"); Print("TDBI ERRORS: "); //--- check if(!tdbierrors) Print("OK"); else Print("FAILED"); Print("FAILURE THRESHOLD: "); //--- check if(!wfailed) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ static void CTestEVDUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::RandomReal()>=sparcity) a[i].Set(j,2*CMath::RandomReal()-1); else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ static void CTestEVDUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::RandomReal()>=sparcity) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| Copies A to AL (lower half) and AU (upper half),filling unused | //| parts by random garbage. | //+------------------------------------------------------------------+ static void CTestEVDUnit::RMatrixSymmetricSplit(CMatrixDouble &a,const int n, CMatrixDouble &al,CMatrixDouble &au) { //--- create variables int i=0; int j=0; //--- change values for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { al[i].Set(j,2*CMath::RandomReal()-1); al[j].Set(i,a[i][j]); au[i].Set(j,a[i][j]); au[j].Set(i,2*CMath::RandomReal()-1); } al[i].Set(i,a[i][i]); au[i].Set(i,a[i][i]); } } //+------------------------------------------------------------------+ //| Copies A to AL (lower half) and AU (upper half),filling unused | //| parts by random garbage. | //+------------------------------------------------------------------+ static void CTestEVDUnit::CMatrixHermitianSplit(CMatrixComplex &a,const int n, CMatrixComplex &al,CMatrixComplex &au) { //--- create variables int i=0; int j=0; //--- change values for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { al[i].Set(j,2*CMath::RandomReal()-1); al[j].Set(i,CMath::Conj(a[i][j])); au[i].Set(j,a[i][j]); au[j].Set(i,2*CMath::RandomReal()-1); } al[i].Set(i,a[i][i]); au[i].Set(i,a[i][i]); } } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestEVDUnit::Unset2D(CMatrixDouble &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestEVDUnit::CUnset2D(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestEVDUnit::Unset1D(double &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestEVDUnit::CUnset1D(complex &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Tests Z*Lambda*Z' against tridiag(D,E). | //| Returns relative error. | //+------------------------------------------------------------------+ static double CTestEVDUnit::TdTestProduct(double &d[],double &e[],const int n, CMatrixDouble &z,double &lambdav[]) { //--- create variables double result=0; int i=0; int j=0; int k=0; double v=0; double mx=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- Calculate V=A[i][j],A=Z*Lambda*Z' v=0; for(k=0;k<=n-1;k++) v=v+z[i][k]*lambdav[k]*z[j][k]; //--- Compare if(MathAbs(i-j)==0) result=MathMax(result,MathAbs(v-d[i])); //--- check if(MathAbs(i-j)==1) result=MathMax(result,MathAbs(v-e[MathMin(i,j)])); //--- check if(MathAbs(i-j)>1) result=MathMax(result,MathAbs(v)); } } //--- change value mx=0; for(i=0;i<=n-1;i++) mx=MathMax(mx,MathAbs(d[i])); for(i=0;i<=n-2;i++) mx=MathMax(mx,MathAbs(e[i])); //--- check if(mx==0.0) mx=1; //--- return result return(result/mx); } //+------------------------------------------------------------------+ //| Tests Z*Lambda*Z' against A | //| Returns relative error. | //+------------------------------------------------------------------+ static double CTestEVDUnit::TestProduct(CMatrixDouble &a,const int n, CMatrixDouble &z,double &lambdav[]) { //--- create variables double result=0; int i=0; int j=0; int k=0; double v=0; double mx=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- Calculate V=A[i][j],A=Z*Lambda*Z' v=0; for(k=0;k<=n-1;k++) v=v+z[i][k]*lambdav[k]*z[j][k]; //--- Compare result=MathMax(result,MathAbs(v-a[i][j])); } } //--- change value mx=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) mx=MathMax(mx,MathAbs(a[i][j])); } //--- check if(mx==0.0) mx=1; //--- return result return(result/mx); } //+------------------------------------------------------------------+ //| Tests Z*Z' against diag(1...1) | //| Returns absolute error. | //+------------------------------------------------------------------+ static double CTestEVDUnit::TestOrt(CMatrixDouble &z,const int n) { //--- create variables double result=0; int i=0; int j=0; double v=0; int i_=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][i]*z[i_][j]; //--- check if(i==j) v=v-1; result=MathMax(result,MathAbs(v)); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests Z*Lambda*Z' against A | //| Returns relative error. | //+------------------------------------------------------------------+ static double CTestEVDUnit::TestCProduct(CMatrixComplex &a,const int n, CMatrixComplex &z,double &lambdav[]) { //--- create variables double result=0; int i=0; int j=0; int k=0; complex v=0; double mx=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- Calculate V=A[i][j],A=Z*Lambda*Z' v=0; for(k=0;k<=n-1;k++) v=v+z[i][k]*lambdav[k]*CMath::Conj(z[j][k]); //--- Compare result=MathMax(result,CMath::AbsComplex(v-a[i][j])); } } //--- change value mx=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) mx=MathMax(mx,CMath::AbsComplex(a[i][j])); } //--- check if(mx==0.0) mx=1; //--- return result return(result/mx); } //+------------------------------------------------------------------+ //| Tests Z*Z' against diag(1...1) | //| Returns absolute error. | //+------------------------------------------------------------------+ static double CTestEVDUnit::TestCOrt(CMatrixComplex &z,const int n) { //--- create variables double result=0; int i=0; int j=0; complex v=0; int i_=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][i]*CMath::Conj(z[i_][j]); //--- check if(i==j) v=v-1; result=MathMax(result,CMath::AbsComplex(v)); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests SEVD problem | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al, CMatrixDouble &au,const int n, const double threshold, bool &serrors,int &failc,int &runs) { //--- create a variable int i=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixDouble z; //--- Test simple EVD: values and full vectors,lower A Unset1D(lambdaref); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVD(al,n,1,false,lambdaref,z)) { failc=failc+1; return; } //--- search errors serrors=serrors || TestProduct(a,n,z,lambdaref)>threshold; serrors=serrors || TestOrt(z,n)>threshold; for(i=0;i<=n-2;i++) { //--- check if(lambdaref[i+1]threshold; serrors=serrors || TestOrt(z,n)>threshold; for(i=0;i<=n-2;i++) { //--- check if(lambdav[i+1]threshold; //--- Test simple EVD: values only,upper A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVD(au,n,0,true,lambdav,z)) { failc=failc+1; return; } //--- search errors for(i=0;i<=n-1;i++) serrors=serrors || MathAbs(lambdav[i]-lambdaref[i])>threshold; } //+------------------------------------------------------------------+ //| Tests SEVD problem | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al, CMatrixComplex &au,const int n, const double threshold,bool &herrors, int &failc,int &runs) { //--- create a variable int i=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixComplex z; //--- Test simple EVD: values and full vectors,lower A Unset1D(lambdaref); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVD(al,n,1,false,lambdaref,z)) { failc=failc+1; return; } //--- search errors herrors=herrors || TestCProduct(a,n,z,lambdaref)>threshold; herrors=herrors || TestCOrt(z,n)>threshold; for(i=0;i<=n-2;i++) { //--- check if(lambdaref[i+1]threshold; herrors=herrors || TestCOrt(z,n)>threshold; for(i=0;i<=n-2;i++) { //--- check if(lambdav[i+1]threshold; //--- Test simple EVD: values only,upper A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVD(au,n,0,true,lambdav,z)) { failc=failc+1; return; } //--- search errors for(i=0;i<=n-1;i++) herrors=herrors || MathAbs(lambdav[i]-lambdaref[i])>threshold; } //+------------------------------------------------------------------+ //| Tests EVD problem | //| DistVals - is True,when eigenvalues are distinct. Is False, | //| when we are solving sparse task with lots of zero| //| eigenvalues. In such cases some tests related to | //| the eigenvectors are not performed. | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestSEVDBiProblem(CMatrixDouble &afull, CMatrixDouble &al,CMatrixDouble &au, const int n,const bool distvals, const double threshold,bool &serrors, int &failc,int &runs) { //--- create variables int i=0; int j=0; int k=0; int m=0; int i1=0; int i2=0; double v=0; double a=0; double b=0; int i_=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixDouble z; CMatrixDouble zref; CMatrixDouble a1; CMatrixDouble a2; CMatrixDouble ar; //--- allocation ArrayResize(lambdaref,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- Reference EVD runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVD(afull,n,1,true,lambdaref,zref)) { failc=failc+1; return; } //--- Select random interval boundaries. //--- If there are non-distinct eigenvalues at the boundaries, //--- we move indexes further until values splits. It is done to //--- avoid situations where we can't get definite answer. i1=CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1); //--- calculation while(i1>0) { //--- check if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold) break; i1=i1-1; } while(i210*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(i2threshold; //--- Test interval,no vectors,upper A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDR(au,n,0,true,a,b,m,lambdav,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test indexes,no vectors,lower A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDI(al,n,0,false,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test indexes,no vectors,upper A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDI(au,n,0,true,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test interval,vectors,lower A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDR(al,n,1,false,a,b,m,lambdav,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*zref[i_][i1+j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) z[i_].Set(j,-1*z[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test interval,vectors,upper A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDR(au,n,1,true,a,b,m,lambdav,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*zref[i_][i1+j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) z[i_].Set(j,-1*z[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test indexes,vectors,lower A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDI(al,n,1,false,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*zref[i_][i1+j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) z[i_].Set(j,-1*z[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test indexes,vectors,upper A Unset1D(lambdav); Unset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVDI(au,n,1,true,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*zref[i_][i1+j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) z[i_].Set(j,-1*z[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold; } } } //+------------------------------------------------------------------+ //| Tests EVD problem | //| DistVals - is True,when eigenvalues are distinct. Is False, | //| when we are solving sparse task with lots of zero| //| eigenvalues. In such cases some tests related to | //| the eigenvectors are not performed. | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestHEVDBiProblem(CMatrixComplex &afull, CMatrixComplex &al, CMatrixComplex &au,const int n, const bool distvals,const double threshold, bool &herrors,int &failc,int &runs) { //--- create variables int i=0; int j=0; int k=0; int m=0; int i1=0; int i2=0; complex v=0; double a=0; double b=0; int i_=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixComplex z; CMatrixComplex zref; CMatrixComplex a1; CMatrixComplex a2; CMatrixComplex ar; //--- allocation ArrayResize(lambdaref,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- Reference EVD runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVD(afull,n,1,true,lambdaref,zref)) { failc=failc+1; return; } //--- Select random interval boundaries. //--- If there are non-distinct eigenvalues at the boundaries, //--- we move indexes further until values splits. It is done to //--- avoid situations where we can't get definite answer. i1=CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1); //--- calculation while(i1>0) { //--- check if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold) break; i1=i1-1; } while(i210*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(i2threshold; //--- Test interval,no vectors,upper A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDR(au,n,0,true,a,b,m,lambdav,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test indexes,no vectors,lower A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDI(al,n,0,false,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test indexes,no vectors,upper A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDI(au,n,0,true,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test interval,vectors,lower A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDR(al,n,1,false,a,b,m,lambdav,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]); v=CMath::Conj(v/CMath::AbsComplex(v)); //--- calculation for(i_=0;i_<=n-1;i_++) z[i_].Set(j,v*z[i_][j]); } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test interval,vectors,upper A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDR(au,n,1,true,a,b,m,lambdav,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]); v=CMath::Conj(v/CMath::AbsComplex(v)); //--- calculation for(i_=0;i_<=n-1;i_++) z[i_].Set(j,v*z[i_][j]); } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test indexes,vectors,lower A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDI(al,n,1,false,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]); v=CMath::Conj(v/CMath::AbsComplex(v)); //--- calculation for(i_=0;i_<=n-1;i_++) z[i_].Set(j,v*z[i_][j]); } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test indexes,vectors,upper A Unset1D(lambdav); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVDI(au,n,1,true,i1,i2,lambdav,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]); v=CMath::Conj(v/CMath::AbsComplex(v)); //--- calculation for(i_=0;i_<=n-1;i_++) z[i_].Set(j,v*z[i_][j]); } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold; } } } //+------------------------------------------------------------------+ //| Tests EVD problem | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestTdEVDProblem(double &d[],double &e[], const int n,const double threshold, bool &tderrors,int &failc,int &runs) { //--- create variables bool wsucc; int i=0; int j=0; double v=0; int i_=0; //--- create arrays double lambdav[]; double ee[]; double lambda2[]; //--- create matrix CMatrixDouble z; CMatrixDouble zref; CMatrixDouble a1; CMatrixDouble a2; //--- allocation ArrayResize(lambdav,n); ArrayResize(lambda2,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- check if(n>1) { //--- allocation ArrayResize(ee,n-1); } //--- Test simple EVD: values and full vectors for(i=0;i<=n-1;i++) lambdav[i]=d[i]; for(i=0;i<=n-2;i++) ee[i]=e[i]; runs=runs+1; wsucc=CEigenVDetect::SMatrixTdEVD(lambdav,ee,n,2,z); //--- check if(!wsucc) { failc=failc+1; return; } //--- search errors tderrors=tderrors || TdTestProduct(d,e,n,z,lambdav)>threshold; tderrors=tderrors || TestOrt(z,n)>threshold; for(i=0;i<=n-2;i++) { //--- check if(lambdav[i+1]threshold; //--- Test multiplication variant for(i=0;i<=n-1;i++) lambda2[i]=d[i]; for(i=0;i<=n-2;i++) ee[i]=e[i]; //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a1[i].Set(j,2*CMath::RandomReal()-1); a2[i].Set(j,a1[i][j]); } } runs=runs+1; wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,1,a1); //--- check if(!wsucc) { failc=failc+1; return; } //--- search errors for(i=0;i<=n-1;i++) tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a2[i][i_]*zref[i_][j]; //--- next line is a bit complicated because //--- depending on algorithm used we can get either //--- z or -z as eigenvector. so we compare result //--- with both A*ZRef and -A*ZRef tderrors=tderrors || (MathAbs(v-a1[i][j])>threshold && MathAbs(v+a1[i][j])>threshold); } } //--- Test first row variant for(i=0;i<=n-1;i++) lambda2[i]=d[i]; for(i=0;i<=n-2;i++) ee[i]=e[i]; runs=runs+1; wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,3,z); //--- check if(!wsucc) { failc=failc+1; return; } //--- search errors for(i=0;i<=n-1;i++) { tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold; //--- next line is a bit complicated because //--- depending on algorithm used we can get either //--- z or -z as eigenvector. so we compare result //--- with both z and -z tderrors=tderrors || (MathAbs(z[0][i]-zref[0][i])>threshold && MathAbs(z[0][i]+zref[0][i])>threshold); } } //+------------------------------------------------------------------+ //| Tests EVD problem | //| DistVals - is True,when eigenvalues are distinct. Is False, | //| when we are solving sparse task with lots of zero| //| eigenvalues. In such cases some tests related to | //| the eigenvectors are not performed. | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestTdEVDBiProblem(double &d[],double &e[], const int n,const bool distvals, const double threshold,bool &serrors, int &failc,int &runs) { //--- create variables int i=0; int j=0; int k=0; int m=0; int i1=0; int i2=0; double v=0; double a=0; double b=0; int i_=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixDouble z; CMatrixDouble zref; CMatrixDouble a1; CMatrixDouble a2; CMatrixDouble ar; //--- allocation ArrayResize(lambdaref,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- Reference EVD ArrayResize(lambdaref,n); for(i_=0;i_<=n-1;i_++) lambdaref[i_]=d[i_]; runs=runs+1; //--- check if(!CEigenVDetect::SMatrixTdEVD(lambdaref,e,n,2,zref)) { failc=failc+1; return; } //--- Select random interval boundaries. //--- If there are non-distinct eigenvalues at the boundaries, //--- we move indexes further until values splits. It is done to //--- avoid situations where we can't get definite answer. i1=CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1); //--- calculation while(i1>0) { //--- check if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold) break; i1=i1-1; } while(i210*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(i2threshold; //--- Test indexes,no vectors ArrayResize(lambdav,n); for(i=0;i<=n-1;i++) lambdav[i]=d[i]; runs=runs+1; //--- check if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,0,i1,i2,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- Test interval,transform vectors ArrayResize(lambdav,n); for(i=0;i<=n-1;i++) lambdav[i]=d[i]; //--- allocation a1.Resize(n,n); a2.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a1[i].Set(j,2*CMath::RandomReal()-1); a2[i].Set(j,a1[i][j]); } } runs=runs+1; //--- check if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,1,a,b,m,a1)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- allocation ar.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a2[i][i_]*zref[i_][i1+j]; ar[i].Set(j,v); } } //--- calculation for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a1[i_][j]*ar[i_][j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) ar[i_].Set(j,-1*ar[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(a1[i][j]-ar[i][j])>threshold; } } //--- Test indexes,transform vectors ArrayResize(lambdav,n); for(i=0;i<=n-1;i++) lambdav[i]=d[i]; //--- allocation a1.Resize(n,n); a2.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a1[i].Set(j,2*CMath::RandomReal()-1); a2[i].Set(j,a1[i][j]); } } runs=runs+1; //--- check if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,1,i1,i2,a1)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { //--- allocation ar.Resize(n,m); //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a2[i][i_]*zref[i_][i1+j]; ar[i].Set(j,v); } } //--- calculation for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a1[i_][j]*ar[i_][j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) ar[i_].Set(j,-1*ar[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(a1[i][j]-ar[i][j])>threshold; } } //--- Test interval,do not transform vectors ArrayResize(lambdav,n); for(i=0;i<=n-1;i++) lambdav[i]=d[i]; //--- allocation z.Resize(1,1); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,2,a,b,m,z)) { failc=failc+1; return; } //--- check if(m!=i2-i1+1) { failc=failc+1; return; } //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*zref[i_][i1+j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) z[i_].Set(j,-1*z[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold; } } //--- Test indexes,do not transform vectors ArrayResize(lambdav,n); for(i=0;i<=n-1;i++) lambdav[i]=d[i]; //--- allocation z.Resize(1,1); runs=runs+1; //--- check if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,2,i1,i2,z)) { failc=failc+1; return; } m=i2-i1+1; //--- search errors for(k=0;k<=m-1;k++) serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold; //--- check if(distvals) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=z[i_][j]*zref[i_][i1+j]; //--- check if(v<0.0) { for(i_=0;i_<=n-1;i_++) z[i_].Set(j,-1*z[i_][j]); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold; } } } //+------------------------------------------------------------------+ //| Non-symmetric problem | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestNSEVDProblem(CMatrixDouble &a,const int n, const double threshold, bool &nserrors,int &failc, int &runs) { //--- create variables double mx=0; int i=0; int j=0; int k=0; int vjob=0; bool needl; bool needr; double curwr=0; double curwi=0; double vt=0; double tmp=0; int i_=0; //--- create arrays double wr0[]; double wi0[]; double wr1[]; double wi1[]; double wr0s[]; double wi0s[]; double wr1s[]; double wi1s[]; double vec1r[]; double vec1i[]; double vec2r[]; double vec2i[]; double vec3r[]; double vec3i[]; //--- create matrix CMatrixDouble vl; CMatrixDouble vr; //--- allocation ArrayResize(vec1r,n); ArrayResize(vec2r,n); ArrayResize(vec3r,n); ArrayResize(vec1i,n); ArrayResize(vec2i,n); ArrayResize(vec3i,n); ArrayResize(wr0s,n); ArrayResize(wr1s,n); ArrayResize(wi0s,n); ArrayResize(wi1s,n); //--- initialization mx=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(MathAbs(a[i][j])>mx) mx=MathAbs(a[i][j]); } } //--- check if(mx==0.0) mx=1; //--- Load values-only runs=runs+1; //--- check if(!CEigenVDetect::RMatrixEVD(a,n,0,wr0,wi0,vl,vr)) { failc=failc+1; return; } //--- Test different jobs for(vjob=1;vjob<=3;vjob++) { needr=vjob==1 || vjob==3; needl=vjob==2 || vjob==3; runs=runs+1; //--- check if(!CEigenVDetect::RMatrixEVD(a,n,vjob,wr1,wi1,vl,vr)) { failc=failc+1; return; } //--- Test values: //--- 1. sort by real part //--- 2. test for(i_=0;i_<=n-1;i_++) wr0s[i_]=wr0[i_]; for(i_=0;i_<=n-1;i_++) wi0s[i_]=wi0[i_]; for(i=0;i<=n-1;i++) { for(j=0;j<=n-2-i;j++) { //--- check if(wr0s[j]>wr0s[j+1]) { tmp=wr0s[j]; wr0s[j]=wr0s[j+1]; wr0s[j+1]=tmp; tmp=wi0s[j]; wi0s[j]=wi0s[j+1]; wi0s[j+1]=tmp; } } } //--- copy for(i_=0;i_<=n-1;i_++) wr1s[i_]=wr1[i_]; for(i_=0;i_<=n-1;i_++) wi1s[i_]=wi1[i_]; //--- swap for(i=0;i<=n-1;i++) { for(j=0;j<=n-2-i;j++) { //--- check if(wr1s[j]>wr1s[j+1]) { tmp=wr1s[j]; wr1s[j]=wr1s[j+1]; wr1s[j+1]=tmp; tmp=wi1s[j]; wi1s[j]=wi1s[j+1]; wi1s[j+1]=tmp; } } } //--- search errors for(i=0;i<=n-1;i++) { nserrors=nserrors || MathAbs(wr0s[i]-wr1s[i])>threshold; nserrors=nserrors || MathAbs(wi0s[i]-wi1s[i])>threshold; } //--- Test right vectors if(needr) { k=0; //--- calculation while(k<=n-1) { //--- check if(wi1[k]==0.0) { for(i_=0;i_<=n-1;i_++) vec1r[i_]=vr[i_][k]; for(i=0;i<=n-1;i++) vec1i[i]=0; curwr=wr1[k]; curwi=0; } //--- check if(wi1[k]>0.0) { for(i_=0;i_<=n-1;i_++) vec1r[i_]=vr[i_][k]; for(i_=0;i_<=n-1;i_++) vec1i[i_]=vr[i_][k+1]; curwr=wr1[k]; curwi=wi1[k]; } //--- check if(wi1[k]<0.0) { for(i_=0;i_<=n-1;i_++) vec1r[i_]=vr[i_][k-1]; for(i_=0;i_<=n-1;i_++) vec1i[i_]=-vr[i_][k]; curwr=wr1[k]; curwi=wi1[k]; } //--- calculation for(i=0;i<=n-1;i++) { vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=a[i][i_]*vec1r[i_]; vec2r[i]=vt; vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=a[i][i_]*vec1i[i_]; vec2i[i]=vt; } //--- change values for(i_=0;i_<=n-1;i_++) vec3r[i_]=curwr*vec1r[i_]; for(i_=0;i_<=n-1;i_++) vec3r[i_]=vec3r[i_]-curwi*vec1i[i_]; for(i_=0;i_<=n-1;i_++) vec3i[i_]=curwi*vec1r[i_]; for(i_=0;i_<=n-1;i_++) vec3i[i_]=vec3i[i_]+curwr*vec1i[i_]; //--- search errors for(i=0;i<=n-1;i++) { nserrors=nserrors || MathAbs(vec2r[i]-vec3r[i])>threshold; nserrors=nserrors || MathAbs(vec2i[i]-vec3i[i])>threshold; } k=k+1; } } //--- Test left vectors if(needl) { k=0; //--- calculation while(k<=n-1) { //--- check if(wi1[k]==0.0) { for(i_=0;i_<=n-1;i_++) vec1r[i_]=vl[i_][k]; for(i=0;i<=n-1;i++) vec1i[i]=0; curwr=wr1[k]; curwi=0; } //--- check if(wi1[k]>0.0) { for(i_=0;i_<=n-1;i_++) vec1r[i_]=vl[i_][k]; for(i_=0;i_<=n-1;i_++) vec1i[i_]=vl[i_][k+1]; curwr=wr1[k]; curwi=wi1[k]; } //--- check if(wi1[k]<0.0) { for(i_=0;i_<=n-1;i_++) vec1r[i_]=vl[i_][k-1]; for(i_=0;i_<=n-1;i_++) vec1i[i_]=-vl[i_][k]; curwr=wr1[k]; curwi=wi1[k]; } //--- calculation for(j=0;j<=n-1;j++) { vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=vec1r[i_]*a[i_][j]; vec2r[j]=vt; vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=vec1i[i_]*a[i_][j]; vec2i[j]=-vt; } //--- change values for(i_=0;i_<=n-1;i_++) vec3r[i_]=curwr*vec1r[i_]; for(i_=0;i_<=n-1;i_++) vec3r[i_]=vec3r[i_]+curwi*vec1i[i_]; for(i_=0;i_<=n-1;i_++) vec3i[i_]=curwi*vec1r[i_]; for(i_=0;i_<=n-1;i_++) vec3i[i_]=vec3i[i_]-curwr*vec1i[i_]; //--- search errors for(i=0;i<=n-1;i++) { nserrors=nserrors || MathAbs(vec2r[i]-vec3r[i])>threshold; nserrors=nserrors || MathAbs(vec2i[i]-vec3i[i])>threshold; } k=k+1; } } } } //+------------------------------------------------------------------+ //| Testing EVD subroutines for one N | //| NOTES: | //| * BIThreshold is a threshold for bisection-and-inverse-iteration | //| subroutines. special threshold is needed because these | //| subroutines may have much more larger error than QR-based | //| algorithms. | //+------------------------------------------------------------------+ static void CTestEVDUnit::TestEVDSet(const int n,const double threshold, double bithreshold,int &failc, int &runs,bool &nserrors,bool &serrors, bool &herrors,bool &tderrors,bool &sbierrors, bool &hbierrors,bool &tdbierrors) { //--- create variables int i=0; int j=0; int mkind=0; //--- create arrays double d[]; double e[]; //--- create matrix CMatrixDouble ra; CMatrixDouble ral; CMatrixDouble rau; CMatrixComplex ca; CMatrixComplex cal; CMatrixComplex cau; //--- Test symmetric problems ra.Resize(n,n); ral.Resize(n,n); rau.Resize(n,n); ca.Resize(n,n); cal.Resize(n,n); cau.Resize(n,n); //--- Zero matrices for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- function calls RMatrixSymmetricSplit(ra,n,ral,rau); CMatrixHermitianSplit(ca,n,cal,cau); TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs); TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs); TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs); TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs); //--- Random matrix for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { ra[i].Set(j,2*CMath::RandomReal()-1); ca[i].SetRe(j,2*CMath::RandomReal()-1); ca[i].SetIm(j,2*CMath::RandomReal()-1); ra[j].Set(i,ra[i][j]); ca[j].Set(i,CMath::Conj(ca[i][j])); } //--- change values ra[i].Set(i,2*CMath::RandomReal()-1); ca[i].Set(i,2*CMath::RandomReal()-1); } //--- function calls RMatrixSymmetricSplit(ra,n,ral,rau); CMatrixHermitianSplit(ca,n,cal,cau); TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs); TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs); //--- Random diagonally dominant matrix with distinct eigenvalues for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { ra[i].Set(j,0.1*(2*CMath::RandomReal()-1)/n); ca[i].SetRe(j,0.1*(2*CMath::RandomReal()-1)/n); ca[i].SetIm(j,0.1*(2*CMath::RandomReal()-1)/n); ra[j].Set(i,ra[i][j]); ca[j].Set(i,CMath::Conj(ca[i][j])); } //--- change values ra[i].Set(i,0.1*(2*CMath::RandomReal()-1)+i); ca[i].Set(i,0.1*(2*CMath::RandomReal()-1)+i); } //--- function calls RMatrixSymmetricSplit(ra,n,ral,rau); CMatrixHermitianSplit(ca,n,cal,cau); TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs); TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs); TestSEVDBiProblem(ra,ral,rau,n,true,bithreshold,sbierrors,failc,runs); TestHEVDBiProblem(ca,cal,cau,n,true,bithreshold,hbierrors,failc,runs); //--- Sparse matrices RMatrixFillSparseA(ra,n,n,0.995); CMatrixFillSparseA(ca,n,n,0.995); for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { ra[j].Set(i,ra[i][j]); ca[j].Set(i,CMath::Conj(ca[i][j])); } ca[i].SetIm(i,0); } //--- function calls RMatrixSymmetricSplit(ra,n,ral,rau); CMatrixHermitianSplit(ca,n,cal,cau); TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs); TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs); TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs); TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs); //--- testing tridiagonal problems for(mkind=0;mkind<=7;mkind++) { //--- allocation ArrayResize(d,n); if(n>1) ArrayResize(e,n-1); //--- check if(mkind==0) { //--- Zero matrix for(i=0;i<=n-1;i++) d[i]=0; for(i=0;i<=n-2;i++) e[i]=0; } //--- check if(mkind==1) { //--- Diagonal matrix for(i=0;i<=n-1;i++) d[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-2;i++) e[i]=0; } //--- check if(mkind==2) { //--- Off-diagonal matrix for(i=0;i<=n-1;i++) d[i]=0; for(i=0;i<=n-2;i++) e[i]=2*CMath::RandomReal()-1; } //--- check if(mkind==3) { //--- Dense matrix with blocks for(i=0;i<=n-1;i++) d[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-2;i++) e[i]=2*CMath::RandomReal()-1; //--- change values j=1; i=2; while(j<=n-2) { e[j]=0; j=j+i; i=i+1; } } //--- check if(mkind==4) { //--- dense matrix for(i=0;i<=n-1;i++) d[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-2;i++) e[i]=2*CMath::RandomReal()-1; } //--- check if(mkind==5) { //--- Diagonal matrix with distinct eigenvalues for(i=0;i<=n-1;i++) d[i]=0.1*(2*CMath::RandomReal()-1)+i; for(i=0;i<=n-2;i++) e[i]=0; } //--- check if(mkind==6) { //--- Off-diagonal matrix with distinct eigenvalues for(i=0;i<=n-1;i++) d[i]=0; for(i=0;i<=n-2;i++) e[i]=0.1*(2*CMath::RandomReal()-1)+i+1; } //--- check if(mkind==7) { //--- dense matrix with distinct eigenvalues for(i=0;i<=n-1;i++) d[i]=0.1*(2*CMath::RandomReal()-1)+i+1; for(i=0;i<=n-2;i++) e[i]=0.1*(2*CMath::RandomReal()-1); } //--- function calls TestTdEVDProblem(d,e,n,threshold,tderrors,failc,runs); TestTdEVDBiProblem(d,e,n,(mkind==5 || mkind==6) || mkind==7,bithreshold,tdbierrors,failc,runs); } //--- Test non-symmetric problems //--- Test non-symmetric problems: zero,random,sparse matrices. ra.Resize(n,n); ca.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- function call TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,2*CMath::RandomReal()-1); ca[i].SetRe(j,2*CMath::RandomReal()-1); ca[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- function calls TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs); RMatrixFillSparseA(ra,n,n,0.995); CMatrixFillSparseA(ca,n,n,0.995); TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs); } //+------------------------------------------------------------------+ //| Testing class CMatGen | //+------------------------------------------------------------------+ class CTestMatGenUnit { private: //--- private methods static void Unset2D(CMatrixDouble &a); static void Unset2DC(CMatrixComplex &a); static bool IsSPD(CMatrixDouble &ca,const int n,const bool isupper); static bool IsHPD(CMatrixComplex &ca,const int n); static double SVDCond(CMatrixDouble &a,const int n); static bool ObsoleteSVDDecomposition(CMatrixDouble &a,const int m,const int n,double &w[],CMatrixDouble &v); static double ExtSign(const double a,const double b); static double MyMax(const double a,const double b); static double PyThag(const double a,const double b); public: //--- class constant static const int m_maxsvditerations; //--- constructor, destructor CTestMatGenUnit(void); ~CTestMatGenUnit(void); //--- public method static bool TestMatGen(const bool silent); }; //+------------------------------------------------------------------+ //| Initialize constant | //+------------------------------------------------------------------+ const int CTestMatGenUnit::m_maxsvditerations=60; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMatGenUnit::CTestMatGenUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMatGenUnit::~CTestMatGenUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CMatGen | //+------------------------------------------------------------------+ static bool CTestMatGenUnit::TestMatGen(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int j=0; int pass=0; int passcount=0; int equal_number=0; bool waserrors; double cond=0; double threshold=0; double vt=0; complex ct=0; double minw=0; double maxw=0; bool serr; bool herr; bool spderr; bool hpderr; bool rerr; bool cerr; int i_=0; //--- create array double w[]; //--- create matrix CMatrixDouble a; CMatrixDouble b; CMatrixDouble u; CMatrixDouble v; CMatrixComplex ca; CMatrixComplex cb; CMatrixDouble r1; CMatrixDouble r2; CMatrixComplex c1; CMatrixComplex c2; //--- initialization rerr=false; cerr=false; serr=false; herr=false; spderr=false; hpderr=false; waserrors=false; maxn=20; passcount=15; threshold=1000*CMath::m_machineepsilon; //--- Testing orthogonal for(n=1;n<=maxn;n++) { for(pass=1;pass<=passcount;pass++) { //--- allocation r1.Resize(n,2*n); r2.Resize(2*n,n); c1.Resize(n,2*n); c2.Resize(2*n,n); //--- Random orthogonal,real Unset2D(a); Unset2D(b); //--- function call CMatGen::RMatrixRndOrthogonal(n,a); //--- function call CMatGen::RMatrixRndOrthogonal(n,b); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- orthogonality test vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=a[i][i_]*a[j][i_]; //--- check if(i==j) rerr=rerr || MathAbs(vt-1)>threshold; else rerr=rerr || MathAbs(vt)>threshold; //--- change value vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=b[i][i_]*b[j][i_]; //--- check if(i==j) rerr=rerr || MathAbs(vt-1)>threshold; else rerr=rerr || MathAbs(vt)>threshold; //--- test for difference in A and B if(n>=2) rerr=rerr || a[i][j]==b[i][j]; } } //--- Random orthogonal,complex Unset2DC(ca); Unset2DC(cb); //--- function call CMatGen::CMatrixRndOrthogonal(n,ca); //--- function call CMatGen::CMatrixRndOrthogonal(n,cb); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- orthogonality test ct=0.0; for(i_=0;i_<=n-1;i_++) ct+=ca[i][i_]*CMath::Conj(ca[j][i_]); //--- check if(i==j) cerr=cerr || CMath::AbsComplex(ct-1)>threshold; else cerr=cerr || CMath::AbsComplex(ct)>threshold; //--- change value ct=0.0; for(i_=0;i_<=n-1;i_++) ct+=cb[i][i_]*CMath::Conj(cb[j][i_]); //--- check if(i==j) cerr=cerr || CMath::AbsComplex(ct-1)>threshold; else cerr=cerr || CMath::AbsComplex(ct)>threshold; //--- test for difference in A and B if(n>=2) cerr=cerr || ca[i][j]==cb[i][j]; } } //--- From the right real tests: //--- 1. E*Q is orthogonal //--- 2. Q1<>Q2 (routine result is changing) //--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices) Unset2D(a); Unset2D(b); //--- allocation a.Resize(n,n); b.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].Set(j,0); b[i].Set(j,0); } a[i].Set(i,1); b[i].Set(i,1); } //--- function call CMatGen::RMatrixRndOrthogonalFromTheRight(a,n,n); //--- function call CMatGen::RMatrixRndOrthogonalFromTheRight(b,n,n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- orthogonality test vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=a[i][i_]*a[j][i_]; //--- check if(i==j) rerr=rerr || MathAbs(vt-1)>threshold; else rerr=rerr || MathAbs(vt)>threshold; //--- change value vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=b[i][i_]*b[j][i_]; //--- check if(i==j) rerr=rerr || MathAbs(vt-1)>threshold; else rerr=rerr || MathAbs(vt)>threshold; //--- test for difference in A and B if(n>=2) rerr=rerr || a[i][j]==b[i][j]; } } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { r2[i].Set(j,2*CMath::RandomReal()-1); r2[i+n].Set(j,r2[i][j]); } } //--- function call CMatGen::RMatrixRndOrthogonalFromTheRight(r2,2*n,n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) rerr=rerr || MathAbs(r2[i+n][j]-r2[i][j])>threshold; } //--- From the left real tests: //--- 1. Q*E is orthogonal //--- 2. Q1<>Q2 (routine result is changing) //--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices) Unset2D(a); Unset2D(b); //--- allocation a.Resize(n,n); b.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].Set(j,0); b[i].Set(j,0); } a[i].Set(i,1); b[i].Set(i,1); } //--- function call CMatGen::RMatrixRndOrthogonalFromTheLeft(a,n,n); //--- function call CMatGen::RMatrixRndOrthogonalFromTheLeft(b,n,n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- orthogonality test vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=a[i][i_]*a[j][i_]; //--- check if(i==j) rerr=rerr || MathAbs(vt-1)>threshold; else rerr=rerr || MathAbs(vt)>threshold; //--- change value vt=0.0; for(i_=0;i_<=n-1;i_++) vt+=b[i][i_]*b[j][i_]; //--- check if(i==j) rerr=rerr || MathAbs(vt-1)>threshold; else rerr=rerr || MathAbs(vt)>threshold; //--- test for difference in A and B if(n>=2) rerr=rerr || a[i][j]==b[i][j]; } } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { r1[i].Set(j,2*CMath::RandomReal()-1); r1[i].Set(j+n,r1[i][j]); } } //--- function call CMatGen::RMatrixRndOrthogonalFromTheLeft(r1,n,2*n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) rerr=rerr || MathAbs(r1[i][j]-r1[i][j+n])>threshold; } //--- From the right complex tests: //--- 1. E*Q is orthogonal //--- 2. Q1<>Q2 (routine result is changing) //--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices) Unset2DC(ca); Unset2DC(cb); //--- allocation ca.Resize(n,n); cb.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { ca[i].Set(j,0); cb[i].Set(j,0); } ca[i].Set(i,1); cb[i].Set(i,1); } //--- function call CMatGen::CMatrixRndOrthogonalFromTheRight(ca,n,n); //--- function call CMatGen::CMatrixRndOrthogonalFromTheRight(cb,n,n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- orthogonality test ct=0.0; for(i_=0;i_<=n-1;i_++) ct+=ca[i][i_]*CMath::Conj(ca[j][i_]); //--- check if(i==j) cerr=cerr || CMath::AbsComplex(ct-1)>threshold; else cerr=cerr || CMath::AbsComplex(ct)>threshold; //--- change value ct=0.0; for(i_=0;i_<=n-1;i_++) ct+=cb[i][i_]*CMath::Conj(cb[j][i_]); //--- check if(i==j) cerr=cerr || CMath::AbsComplex(ct-1)>threshold; else cerr=cerr || CMath::AbsComplex(ct)>threshold; //--- test for difference in A and B cerr=cerr || ca[i][j]==cb[i][j]; } } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { c2[i].Set(j,2*CMath::RandomReal()-1); c2[i+n].Set(j,c2[i][j]); } } //--- function call CMatGen::CMatrixRndOrthogonalFromTheRight(c2,2*n,n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) cerr=cerr || CMath::AbsComplex(c2[i+n][j]-c2[i][j])>threshold; } //--- From the left complex tests: //--- 1. Q*E is orthogonal //--- 2. Q1<>Q2 (routine result is changing) //--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices) Unset2DC(ca); Unset2DC(cb); //--- allocation ca.Resize(n,n); cb.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { ca[i].Set(j,0); cb[i].Set(j,0); } ca[i].Set(i,1); cb[i].Set(i,1); } //--- function call CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,n,n); //--- function call CMatGen::CMatrixRndOrthogonalFromTheLeft(cb,n,n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- orthogonality test ct=0.0; for(i_=0;i_<=n-1;i_++) ct+=ca[i][i_]*CMath::Conj(ca[j][i_]); //--- check if(i==j) cerr=cerr || CMath::AbsComplex(ct-1)>threshold; else cerr=cerr || CMath::AbsComplex(ct)>threshold; //--- change value ct=0.0; for(i_=0;i_<=n-1;i_++) ct+=cb[i][i_]*CMath::Conj(cb[j][i_]); //--- check if(i==j) cerr=cerr || CMath::AbsComplex(ct-1)>threshold; else cerr=cerr || CMath::AbsComplex(ct)>threshold; //--- test for difference in A and B cerr=cerr || ca[i][j]==cb[i][j]; } } //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { c1[i].Set(j,2*CMath::RandomReal()-1); c1[i].Set(j+n,c1[i][j]); } } //--- function call CMatGen::CMatrixRndOrthogonalFromTheLeft(c1,n,2*n); //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) cerr=cerr || CMath::AbsComplex(c1[i][j]-c1[i][j+n])>threshold; } } } //--- Testing GCond for(n=2;n<=maxn;n++) { for(pass=1;pass<=passcount;pass++) { //--- real test Unset2D(a); cond=MathExp(MathLog(1000)*CMath::RandomReal()); //--- function call CMatGen::RMatrixRndCond(n,cond,a); //--- allocation b.Resize(n+1,n+1); //--- change values for(i=1;i<=n;i++) { for(j=1;j<=n;j++) b[i].Set(j,a[i-1][j-1]); } //--- check if(ObsoleteSVDDecomposition(b,n,n,w,v)) { maxw=w[1]; minw=w[1]; for(i=2;i<=n;i++) { //--- check if(w[i]>maxw) maxw=w[i]; //--- check if(w[i]MathLog(1+threshold)) rerr=true; } } } //--- Symmetric/SPD //--- N=2 .. 30 for(n=2;n<=maxn;n++) { //--- SPD matrices for(pass=1;pass<=passcount;pass++) { //--- Generate A Unset2D(a); cond=MathExp(MathLog(1000)*CMath::RandomReal()); //--- function call CMatGen::SPDMatrixRndCond(n,cond,a); //--- test condition number spderr=spderr || SVDCond(a,n)/cond-1>threshold; //--- test SPD spderr=spderr || !IsSPD(a,n,true); //--- test that A is symmetic for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) spderr=spderr || MathAbs(a[i][j]-a[j][i])>threshold; } //--- test for difference between A and B (subsequent matrix) Unset2D(b); //--- function call CMatGen::SPDMatrixRndCond(n,cond,b); //--- check if(n>=2) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { if(a[i][j]==b[i][j]) equal_number++; } } if(equal_number>2) spderr=true; } } //--- HPD matrices for(pass=1;pass<=passcount;pass++) { //--- Generate A Unset2DC(ca); cond=MathExp(MathLog(1000)*CMath::RandomReal()); //--- function call CMatGen::HPDMatrixRndCond(n,cond,ca); //--- test HPD hpderr=hpderr || !IsHPD(ca,n); //--- test that A is Hermitian for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) hpderr=hpderr || CMath::AbsComplex(ca[i][j]-CMath::Conj(ca[j][i]))>threshold; } //--- test for difference between A and B (subsequent matrix) Unset2DC(cb); //--- function call CMatGen::HPDMatrixRndCond(n,cond,cb); //--- check if(n>=2) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { hpderr=hpderr || ca[i][j]==cb[i][j]; } } } } //--- Symmetric matrices for(pass=1;pass<=passcount;pass++) { //--- test condition number Unset2D(a); cond=MathExp(MathLog(1000)*CMath::RandomReal()); //--- function call CMatGen::SMatrixRndCond(n,cond,a); serr=serr || SVDCond(a,n)/cond-1>threshold; //--- test for difference between A and B Unset2D(b); //--- function call CMatGen::SMatrixRndCond(n,cond,b); //--- check if(n>=2) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) serr=serr || a[i][j]==b[i][j]; } } } //--- Hermitian matrices for(pass=1;pass<=passcount;pass++) { //--- Generate A Unset2DC(ca); cond=MathExp(MathLog(1000)*CMath::RandomReal()); //--- function call CMatGen::HMatrixRndCond(n,cond,ca); //--- test that A is Hermitian for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) herr=herr || CMath::AbsComplex(ca[i][j]-CMath::Conj(ca[j][i]))>threshold; } //--- test for difference between A and B (subsequent matrix) Unset2DC(cb); //--- function call CMatGen::HMatrixRndCond(n,cond,cb); //--- check if(n>=2) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) herr=herr || ca[i][j]==cb[i][j]; } } } } //--- report waserrors=((((rerr || cerr) || serr) || spderr) || herr) || hpderr; //--- check if(!silent) { Print("TESTING MATRIX GENERATOR"); Print("REAL TEST: "); //--- check if(!rerr) Print("OK"); else Print("FAILED"); Print("COMPLEX TEST: "); //--- check if(!cerr) Print("OK"); else Print("FAILED"); Print("SYMMETRIC TEST: "); //--- check if(!serr) Print("OK"); else Print("FAILED"); Print("HERMITIAN TEST: "); //--- check if(!herr) Print("OK"); else Print("FAILED"); Print("SPD TEST: "); //--- check if(!spderr) Print("OK"); else Print("FAILED"); Print("HPD TEST: "); //--- check if(!hpderr) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestMatGenUnit::Unset2D(CMatrixDouble &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestMatGenUnit::Unset2DC(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Test whether matrix is SPD | //+------------------------------------------------------------------+ static bool CTestMatGenUnit::IsSPD(CMatrixDouble &ca,const int n,const bool isupper) { //--- create variables bool result; int i=0; int j=0; double ajj=0; double v=0; int i_=0; //--- create matrix CMatrixDouble a; //--- copy a=ca; //--- Test the input parameters. if(!CAp::Assert(n>=0,"Error in SMatrixCholesky: incorrect function arguments")) return(false); //--- Quick return if possible result=true; if(n<=0) { //--- return result return(result); } //--- check if(isupper) { //--- Compute the Cholesky factorization A=U'*U. for(j=0;j<=n-1;j++) { //--- Compute U(J,J) and test for non-positive-definiteness. v=0.0; for(i_=0;i_<=j-1;i_++) v+=a[i_][j]*a[i_][j]; ajj=a[j][j]-v; //--- check if(ajj<=0.0) { //--- return result return(false); } //--- change values ajj=MathSqrt(ajj); a[j].Set(j,ajj); //--- Compute elements J+1:N of row J. if(jmaxw) maxw=w[i]; } //--- return result return(maxw/minw); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static bool CTestMatGenUnit::ObsoleteSVDDecomposition(CMatrixDouble &a, const int m, const int n, double &w[], CMatrixDouble &v) { //--- create variables bool result; int nm=0; int minmn=0; int l=0; int k=0; int j=0; int jj=0; int its=0; int i=0; double z=0; double y=0; double x=0; double vscale=0; double s=0; double h=0; double g=0; double f=0; double c=0; double anorm=0; bool flag; //--- create array double rv1[]; //--- allocation ArrayResize(rv1,n+1); ArrayResize(w,n+1); v.Resize(n+1,n+1); //--- initialization result=true; //--- check if(m=1;i--) { //--- check if(i=1;i--) { l=i+1; g=w[i]; //--- check if(i=1;k--) { for(its=1;its<=m_maxsvditerations;its++) { flag=true; for(l=k;l>=1;l--) { nm=l-1; //--- check if(MathAbs(rv1[l])+anorm==anorm) { flag=false; break; } //--- check if(MathAbs(w[nm])+anorm==anorm) break; } //--- check if(flag) { c=0.0; s=1.0; //--- calculation for(i=l;i<=k;i++) { f=s*rv1[i]; //--- check if(MathAbs(f)+anorm!=anorm) { //--- change values g=w[i]; h=PyThag(f,g); w[i]=h; h=1.0/h; c=g*h; s=-(f*h); for(j=1;j<=m;j++) { y=a[j][nm]; z=a[j][i]; a[j].Set(nm,y*c+z*s); a[j].Set(i,-(y*s)+z*c); } } } } z=w[k]; //--- check if(l==k) { //--- check if(z<0.0) { w[k]=-z; for(j=1;j<=n;j++) v[j].Set(k,-v[j][k]); } break; } //--- check if(its==m_maxsvditerations) { //--- return result return(false); } //--- change values x=w[l]; nm=k-1; y=w[nm]; g=rv1[nm]; h=rv1[k]; f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y); g=PyThag(f,1); f=((x-z)*(x+z)+h*(y/(f+ExtSign(g,f))-h))/x; c=1.0; s=1.0; //--- calculation for(j=l;j<=nm;j++) { i=j+1; g=rv1[i]; y=w[i]; h=s*g; g=c*g; z=PyThag(f,h); rv1[j]=z; c=f/z; s=h/z; f=x*c+g*s; g=-(x*s)+g*c; h=y*s; y=y*c; for(jj=1;jj<=n;jj++) { x=v[jj][j]; z=v[jj][i]; v[jj].Set(j,x*c+z*s); v[jj].Set(i,-(x*s)+z*c); } z=PyThag(f,h); w[j]=z; //--- check if(z!=0.0) { z=1.0/z; c=f*z; s=h*z; } //--- calculation f=c*g+s*y; x=-(s*g)+c*y; for(jj=1;jj<=m;jj++) { y=a[jj][j]; z=a[jj][i]; a[jj].Set(j,y*c+z*s); a[jj].Set(i,-(y*s)+z*c); } } //--- change values rv1[l]=0.0; rv1[k]=f; w[k]=x; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static double CTestMatGenUnit::ExtSign(const double a,const double b) { //--- create a variable double result=0; //--- check if(b>=0.0) result=MathAbs(a); else result=-MathAbs(a); //--- return result return(result); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static double CTestMatGenUnit::MyMax(const double a,const double b) { //--- create a variable double result=0; //--- check if(a>b) result=a; else result=b; //--- return result return(result); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static double CTestMatGenUnit::PyThag(const double a,const double b) { //--- create a variable double result=0; //--- check if(MathAbs(a)0.5) n=mx; else m=mx; //--- First,test on zero matrix ra.Resize(m,n); ca.Resize(m,n); for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- function calls TestCLUProblem(ca,m,n,threshold,cerr,properr); TestRLUProblem(ra,m,n,threshold,rerr,properr); //--- Second,random matrix with moderate condition number ra.Resize(m,n); ca.Resize(m,n); for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { ra[i].Set(j,0); ca[i].Set(j,0); } } //--- change values for(i=0;i<=MathMin(m,n)-1;i++) { ra[i].Set(i,1+10*CMath::RandomReal()); ca[i].Set(i,1+10*CMath::RandomReal()); } //--- function call CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,m,n); //--- function call CMatGen::CMatrixRndOrthogonalFromTheRight(ca,m,n); //--- function call CMatGen::RMatrixRndOrthogonalFromTheLeft(ra,m,n); //--- function call CMatGen::RMatrixRndOrthogonalFromTheRight(ra,m,n); //--- function calls TestCLUProblem(ca,m,n,threshold,cerr,properr); TestRLUProblem(ra,m,n,threshold,rerr,properr); } //--- Test Cholesky for(n=1;n<=maxmn;n++) { //--- Load CA (HPD matrix with low condition number), //--- CAL and CAU - its lower and upper triangles CMatGen::HPDMatrixRndCond(n,1+50*CMath::RandomReal(),ca); //--- allocation cal.Resize(n,n); cau.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { cal[i].Set(j,i); cau[i].Set(j,j); } } //--- change values for(i=0;i<=n-1;i++) { for(i_=0;i_<=i;i_++) cal[i].Set(i_,ca[i][i_]); for(i_=i;i_<=n-1;i_++) cau[i].Set(i_,ca[i][i_]); } //--- Test HPDMatrixCholesky: //--- 1. it must leave upper (lower) part unchanged //--- 2. max(A-L*L^H) must be small if(CTrFac::HPDMatrixCholesky(cal,n,false)) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(j>i) hpderr=hpderr || cal[i][j]!=i; else { vc=0.0; for(i_=0;i_<=j;i_++) vc+=cal[i][i_]*CMath::Conj(cal[j][i_]); //--- search errors hpderr=hpderr || CMath::AbsComplex(ca[i][j]-vc)>threshold; } } } } else hpderr=true; //--- check if(CTrFac::HPDMatrixCholesky(cau,n,true)) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(jthreshold; } } } } else hpderr=true; //--- Load RA (SPD matrix with low condition number), //--- RAL and RAU - its lower and upper triangles CMatGen::SPDMatrixRndCond(n,1+50*CMath::RandomReal(),ra); //--- allocation ral.Resize(n,n); rau.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { ral[i].Set(j,i); rau[i].Set(j,j); } } //--- change values for(i=0;i<=n-1;i++) { for(i_=0;i_<=i;i_++) ral[i].Set(i_,ra[i][i_]); for(i_=i;i_<=n-1;i_++) rau[i].Set(i_,ra[i][i_]); } //--- Test SPDMatrixCholesky: //--- 1. it must leave upper (lower) part unchanged //--- 2. max(A-L*L^H) must be small if(CTrFac::SPDMatrixCholesky(ral,n,false)) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(j>i) spderr=spderr || ral[i][j]!=i; else { vr=0.0; for(i_=0;i_<=j;i_++) vr+=ral[i][i_]*ral[j][i_]; //--- search errors spderr=spderr || MathAbs(ra[i][j]-vr)>threshold; } } } } else spderr=true; //--- check if(CTrFac::SPDMatrixCholesky(rau,n,true)) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(jthreshold; } } } } else spderr=true; } //--- report waserrors=(((rerr || spderr) || cerr) || hpderr) || properr; //--- check if(!silent) { Print("TESTING TRIANGULAR FACTORIZATIONS"); Print("* REAL: "); //--- check if(rerr) Print("FAILED"); else Print("OK"); Print("* SPD: "); //--- check if(spderr) Print("FAILED"); else Print("OK"); Print("* COMPLEX: "); //--- check if(cerr) Print("FAILED"); else Print("OK"); Print("* HPD: "); //--- check if(hpderr) Print("FAILED"); else Print("OK"); Print("* OTHER PROPERTIES: "); //--- check if(properr) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestTrFacUnit::TestCLUProblem(CMatrixComplex &a,const int m, const int n,const double threshold, bool &err,bool &properr) { //--- create variables int i=0; int j=0; int minmn=0; complex v=0; int i_=0; //--- create arrays complex ct[]; int p[]; //--- create matrix CMatrixComplex ca; CMatrixComplex cl; CMatrixComplex cu; CMatrixComplex ca2; //--- initialization minmn=MathMin(m,n); //--- PLU test ca.Resize(m,n); for(i=0;i<=m-1;i++) { for(i_=0;i_<=n-1;i_++) ca[i].Set(i_,a[i][i_]); } //--- function call CTrFac::CMatrixPLU(ca,m,n,p); for(i=0;i<=minmn-1;i++) { //--- check if(p[i]=m) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); for(j=0;j<=minmn-1;j++) { for(i=0;i<=j-1;i++) cl[i].Set(j,0.0); //--- change values cl[j].Set(j,1.0); for(i=j+1;i<=m-1;i++) cl[i].Set(j,ca[i][j]); } //--- allocation cu.Resize(minmn,n); //--- change values for(i=0;i<=minmn-1;i++) { for(j=0;j<=i-1;j++) cu[i].Set(j,0.0); for(j=i;j<=n-1;j++) cu[i].Set(j,ca[i][j]); } //--- allocation ca2.Resize(m,n); //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=minmn-1;i_++) v+=cl[i][i_]*cu[i_][j]; ca2[i].Set(j,v); } } //--- allocation ArrayResize(ct,n); //--- change values for(i=minmn-1;i>=0;i--) { //--- check if(i!=p[i]) { for(i_=0;i_<=n-1;i_++) ct[i_]=ca2[i][i_]; for(i_=0;i_<=n-1;i_++) ca2[i].Set(i_,ca2[p[i]][i_]); for(i_=0;i_<=n-1;i_++) ca2[p[i]].Set(i_,ct[i_]); } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) err=err || CMath::AbsComplex(a[i][j]-ca2[i][j])>threshold; } //--- LUP test ca.Resize(m,n); for(i=0;i<=m-1;i++) { for(i_=0;i_<=n-1;i_++) ca[i].Set(i_,a[i][i_]); } //--- function call CTrFac::CMatrixLUP(ca,m,n,p); for(i=0;i<=minmn-1;i++) { //--- check if(p[i]=n) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); //--- change values for(j=0;j<=minmn-1;j++) { for(i=0;i<=j-1;i++) cl[i].Set(j,0.0); for(i=j;i<=m-1;i++) cl[i].Set(j,ca[i][j]); } //--- allocation cu.Resize(minmn,n); //--- change values for(i=0;i<=minmn-1;i++) { for(j=0;j<=i-1;j++) cu[i].Set(j,0.0); cu[i].Set(i,1.0); for(j=i+1;j<=n-1;j++) cu[i].Set(j,ca[i][j]); } //--- allocation ca2.Resize(m,n); //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=minmn-1;i_++) v+=cl[i][i_]*cu[i_][j]; ca2[i].Set(j,v); } } //--- allocation ArrayResize(ct,m); //--- change values for(i=minmn-1;i>=0;i--) { //--- check if(i!=p[i]) { for(i_=0;i_<=m-1;i_++) ct[i_]=ca2[i_][i]; for(i_=0;i_<=m-1;i_++) ca2[i_].Set(i,ca2[i_][p[i]]); for(i_=0;i_<=m-1;i_++) ca2[i_].Set(p[i],ct[i_]); } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) err=err || CMath::AbsComplex(a[i][j]-ca2[i][j])>threshold; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestTrFacUnit::TestRLUProblem(CMatrixDouble &a,const int m, const int n,const double threshold, bool &err,bool &properr) { //--- create variables int i=0; int j=0; int minmn=0; double v=0; int i_=0; //--- create arrays double ct[]; int p[]; //--- create matrix CMatrixDouble ca; CMatrixDouble cl; CMatrixDouble cu; CMatrixDouble ca2; //--- initialization minmn=MathMin(m,n); //--- PLU test ca.Resize(m,n); for(i=0;i<=m-1;i++) { for(i_=0;i_<=n-1;i_++) ca[i].Set(i_,a[i][i_]); } //--- function call CTrFac::RMatrixPLU(ca,m,n,p); for(i=0;i<=minmn-1;i++) { //--- check if(p[i]=m) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); //--- change values for(j=0;j<=minmn-1;j++) { for(i=0;i<=j-1;i++) cl[i].Set(j,0.0); cl[j].Set(j,1.0); for(i=j+1;i<=m-1;i++) cl[i].Set(j,ca[i][j]); } //--- allocation cu.Resize(minmn,n); //--- change values for(i=0;i<=minmn-1;i++) { for(j=0;j<=i-1;j++) cu[i].Set(j,0.0); for(j=i;j<=n-1;j++) cu[i].Set(j,ca[i][j]); } //--- allocation ca2.Resize(m,n); //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=minmn-1;i_++) v+=cl[i][i_]*cu[i_][j]; ca2[i].Set(j,v); } } //--- allocation ArrayResize(ct,n); for(i=minmn-1;i>=0;i--) { //--- check if(i!=p[i]) { //--- change values for(i_=0;i_<=n-1;i_++) ct[i_]=ca2[i][i_]; for(i_=0;i_<=n-1;i_++) ca2[i].Set(i_,ca2[p[i]][i_]); for(i_=0;i_<=n-1;i_++) ca2[p[i]].Set(i_,ct[i_]); } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) err=err || MathAbs(a[i][j]-ca2[i][j])>threshold; } //--- LUP test ca.Resize(m,n); for(i=0;i<=m-1;i++) { for(i_=0;i_<=n-1;i_++) ca[i].Set(i_,a[i][i_]); } //--- function call CTrFac::RMatrixLUP(ca,m,n,p); for(i=0;i<=minmn-1;i++) { //--- check if(p[i]=n) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); //--- change values for(j=0;j<=minmn-1;j++) { for(i=0;i<=j-1;i++) cl[i].Set(j,0.0); for(i=j;i<=m-1;i++) cl[i].Set(j,ca[i][j]); } //--- allocation cu.Resize(minmn,n); //--- change values for(i=0;i<=minmn-1;i++) { for(j=0;j<=i-1;j++) cu[i].Set(j,0.0); cu[i].Set(i,1.0); for(j=i+1;j<=n-1;j++) cu[i].Set(j,ca[i][j]); } //--- allocation ca2.Resize(m,n); for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=minmn-1;i_++) v+=cl[i][i_]*cu[i_][j]; ca2[i].Set(j,v); } } //--- allocation ArrayResize(ct,m); for(i=minmn-1;i>=0;i--) { //--- check if(i!=p[i]) { //--- change values for(i_=0;i_<=m-1;i_++) ct[i_]=ca2[i_][i]; for(i_=0;i_<=m-1;i_++) ca2[i_].Set(i,ca2[i_][p[i]]); for(i_=0;i_<=m-1;i_++) ca2[i_].Set(p[i],ct[i_]); } } //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) err=err || MathAbs(a[i][j]-ca2[i][j])>threshold; } } //+------------------------------------------------------------------+ //| Testing class CTrLinSolve | //+------------------------------------------------------------------+ class CTestTrLinSolveUnit { private: //--- private method static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); public: //--- constructor, destructor CTestTrLinSolveUnit(void); ~CTestTrLinSolveUnit(void); //--- public method static bool TestTrLinSolve(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestTrLinSolveUnit::CTestTrLinSolveUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestTrLinSolveUnit::~CTestTrLinSolveUnit(void) { } //+------------------------------------------------------------------+ //| Main unittest subroutine | //+------------------------------------------------------------------+ static bool CTestTrLinSolveUnit::TestTrLinSolve(const bool silent) { //--- create variables int maxmn=0; int passcount=0; double threshold=0; int n=0; int pass=0; int i=0; int j=0; int cnts=0; int cntu=0; int cntt=0; int cntm=0; bool waserrors; bool isupper; bool istrans; bool isunit; double v=0; double s=0; int i_=0; //--- create arrays double xe[]; double b[]; //--- create matrix CMatrixDouble aeffective; CMatrixDouble aparam; //--- initialization waserrors=false; maxmn=15; passcount=15; threshold=1000*CMath::m_machineepsilon; //--- Different problems for(n=1;n<=maxmn;n++) { //--- allocation aeffective.Resize(n,n); aparam.Resize(n,n); ArrayResize(xe,n); ArrayResize(b,n); //--- calculation for(pass=1;pass<=passcount;pass++) { for(cnts=0;cnts<=1;cnts++) { for(cntu=0;cntu<=1;cntu++) { for(cntt=0;cntt<=1;cntt++) { for(cntm=0;cntm<=2;cntm++) { isupper=cnts==0; isunit=cntu==0; istrans=cntt==0; //--- Skip meaningless combinations of parameters: //--- (matrix is singular) AND (matrix is unit diagonal) if(cntm==2 && isunit) continue; //--- Clear matrices for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { aeffective[i].Set(j,0); aparam[i].Set(j,0); } } //--- Prepare matrices if(isupper) { for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { aeffective[i].Set(j,0.9*(2*CMath::RandomReal()-1)); aparam[i].Set(j,aeffective[i][j]); } //--- change values aeffective[i].Set(i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal())); aparam[i].Set(i,aeffective[i][i]); } } else { for(i=0;i<=n-1;i++) { for(j=0;j<=i;j++) { aeffective[i].Set(j,0.9*(2*CMath::RandomReal()-1)); aparam[i].Set(j,aeffective[i][j]); } //--- change values aeffective[i].Set(i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal())); aparam[i].Set(i,aeffective[i][i]); } } //--- check if(isunit) { for(i=0;i<=n-1;i++) { aeffective[i].Set(i,1); aparam[i].Set(i,0); } } //--- check if(istrans) { //--- check if(isupper) { for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { aeffective[j].Set(i,aeffective[i][j]); aeffective[i].Set(j,0); } } } else { for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { aeffective[i].Set(j,aeffective[j][i]); aeffective[j].Set(i,0); } } } } //--- Prepare task,solve,compare for(i=0;i<=n-1;i++) xe[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=aeffective[i][i_]*xe[i_]; b[i]=v; } //--- function call CTrLinSolve::RMatrixTrSafeSolve(aparam,n,b,s,isupper,istrans,isunit); //--- calculation for(i_=0;i_<=n-1;i_++) xe[i_]=s*xe[i_]; for(i_=0;i_<=n-1;i_++) xe[i_]=xe[i_]-b[i_]; //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=xe[i_]*xe[i_]; v=MathSqrt(v); //--- search errors waserrors=waserrors || v>threshold; } } } } } } //--- report if(!silent) { Print("TESTING RMatrixTrSafeSolve"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestTrLinSolveUnit::MakeACopy(CMatrixDouble &a,const int m, const int n,CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Testing class CSafeSolve | //+------------------------------------------------------------------+ class CTestSafeSolveUnit { private: //--- private methods static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); public: //--- constructor, destructor CTestSafeSolveUnit(void); ~CTestSafeSolveUnit(void); //--- public method static bool TestSafeSolve(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSafeSolveUnit::CTestSafeSolveUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSafeSolveUnit::~CTestSafeSolveUnit(void) { } //+------------------------------------------------------------------+ //| Main unittest subroutine | //+------------------------------------------------------------------+ static bool CTestSafeSolveUnit::TestSafeSolve(const bool silent) { //--- create variables int maxmn=0; double threshold=0; bool rerrors; bool cerrors; bool waserrors; bool isupper; int trans=0; bool isunit; double scalea=0; double growth=0; int i=0; int j=0; int n=0; int j1=0; int j2=0; complex cv=0; double rv=0; int i_=0; //--- create arrays complex cxs[]; complex cxe[]; double rxs[]; double rxe[]; //--- create matrix CMatrixComplex ca; CMatrixComplex cea; CMatrixComplex ctmpa; CMatrixDouble ra; CMatrixDouble rea; CMatrixDouble rtmpa; //--- initialization maxmn=30; threshold=100000*CMath::m_machineepsilon; rerrors=false; cerrors=false; waserrors=false; //--- Different problems: general tests for(n=1;n<=maxmn;n++) { //--- test complex solver with well-conditioned matrix: //--- 1. generate A: fill off-diagonal elements with small values, //--- diagonal elements are filled with larger values //--- 2. generate 'effective' A //--- 3. prepare task (exact X is stored in CXE,right part - in CXS), //--- solve and compare CXS and CXE isupper=CMath::RandomReal()>0.5; trans=CMath::RandomInteger(3); isunit=CMath::RandomReal()>0.5; scalea=CMath::RandomReal()+0.5; //--- allocation ca.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i==j) { ca[i].SetRe(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal())); ca[i].SetIm(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal())); } else { ca[i].SetRe(j,0.2*CMath::RandomReal()-0.1); ca[i].SetIm(j,0.2*CMath::RandomReal()-0.1); } } } //--- function call CMatrixMakeACopy(ca,n,n,ctmpa); for(i=0;i<=n-1;i++) { //--- check if(isupper) { j1=0; j2=i-1; } else { j1=i+1; j2=n-1; } for(j=j1;j<=j2;j++) ctmpa[i].Set(j,0); //--- check if(isunit) ctmpa[i].Set(i,1); } //--- allocation cea.Resize(n,n); for(i=0;i<=n-1;i++) { //--- check if(trans==0) { for(i_=0;i_<=n-1;i_++) cea[i].Set(i_,ctmpa[i][i_]*scalea); } //--- check if(trans==1) { for(i_=0;i_<=n-1;i_++) cea[i_].Set(i,ctmpa[i][i_]*scalea); } //--- check if(trans==2) { for(i_=0;i_<=n-1;i_++) cea[i_].Set(i,CMath::Conj(ctmpa[i][i_])*scalea); } } //--- allocation ArrayResize(cxe,n); //--- change values for(i=0;i<=n-1;i++) { cxe[i].re=2*CMath::RandomReal()-1; cxe[i].im=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(cxs,n); for(i=0;i<=n-1;i++) { //--- change value cv=0.0; for(i_=0;i_<=n-1;i_++) cv+=cea[i][i_]*cxe[i_]; cxs[i]=cv; } //--- check if(CSafeSolve::CMatrixScaledTrSafeSolve(ca,scalea,n,cxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber))) { for(i=0;i<=n-1;i++) cerrors=cerrors || CMath::AbsComplex(cxs[i]-cxe[i])>threshold; } else cerrors=true; //--- same with real isupper=CMath::RandomReal()>0.5; trans=CMath::RandomInteger(2); isunit=CMath::RandomReal()>0.5; scalea=CMath::RandomReal()+0.5; //--- allocation ra.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i==j) ra[i].Set(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal())); else ra[i].Set(j,0.2*CMath::RandomReal()-0.1); } } //--- function call RMatrixMakeACopy(ra,n,n,rtmpa); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(isupper) { j1=0; j2=i-1; } else { j1=i+1; j2=n-1; } for(j=j1;j<=j2;j++) rtmpa[i].Set(j,0); //--- check if(isunit) rtmpa[i].Set(i,1); } //--- allocation rea.Resize(n,n); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(trans==0) { for(i_=0;i_<=n-1;i_++) rea[i].Set(i_,scalea*rtmpa[i][i_]); } //--- check if(trans==1) { for(i_=0;i_<=n-1;i_++) rea[i_].Set(i,scalea*rtmpa[i][i_]); } } //--- allocation ArrayResize(rxe,n); for(i=0;i<=n-1;i++) rxe[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rxs,n); for(i=0;i<=n-1;i++) { //--- change value rv=0.0; for(i_=0;i_<=n-1;i_++) rv+=rea[i][i_]*rxe[i_]; rxs[i]=rv; } //--- check if(CSafeSolve::RMatrixScaledTrSafeSolve(ra,scalea,n,rxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber))) { for(i=0;i<=n-1;i++) rerrors=rerrors || MathAbs(rxs[i]-rxe[i])>threshold; } else rerrors=true; } //--- Special test with diagonal ill-conditioned matrix: //--- * ability to solve it when resulting growth is less than threshold //--- * ability to stop solve when resulting growth is greater than threshold //--- A=diag(1,1/growth) //--- b=(1,0.5) n=2; growth=10; //--- allocation ca.Resize(n,n); //--- change values ca[0].Set(0,1); ca[0].Set(1,0); ca[1].Set(0,0); ca[1].Set(1,1/growth); //--- allocation ArrayResize(cxs,n); //--- change values cxs[0]=1.0; cxs[1]=0.5; //--- search errors cerrors=cerrors || !CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,1.05*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0)); cerrors=cerrors || !CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,0.95*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0)); //--- allocation ra.Resize(n,n); //--- change values ra[0].Set(0,1); ra[0].Set(1,0); ra[1].Set(0,0); ra[1].Set(1,1/growth); //--- allocation ArrayResize(rxs,n); //--- change values rxs[0]=1.0; rxs[1]=0.5; //--- search errors rerrors=rerrors || !CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,1.05*MathMax(MathAbs(rxs[1])*growth,1.0)); rerrors=rerrors || !CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,0.95*MathMax(MathAbs(rxs[1])*growth,1.0)); //--- Special test with diagonal degenerate matrix: //--- * ability to solve it when resulting growth is less than threshold //--- * ability to stop solve when resulting growth is greater than threshold //--- A=diag(1,0) //--- b=(1,0.5) n=2; ca.Resize(n,n); //--- change values ca[0].Set(0,1); ca[0].Set(1,0); ca[1].Set(0,0); ca[1].Set(1,0); //--- allocation ArrayResize(cxs,n); //--- change values cxs[0]=1.0; cxs[1]=0.5; //--- search errors cerrors=cerrors || CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,MathSqrt(CMath::m_maxrealnumber)); //--- allocation ra.Resize(n,n); //--- change values ra[0].Set(0,1); ra[0].Set(1,0); ra[1].Set(0,0); ra[1].Set(1,0); //--- allocation ArrayResize(rxs,n); //--- change values rxs[0]=1.0; rxs[1]=0.5; //--- search errors rerrors=rerrors || CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,MathSqrt(CMath::m_maxrealnumber)); //--- report waserrors=rerrors || cerrors; //--- check if(!silent) { Print("TESTING SAFE TR SOLVER"); Print("REAL: "); //--- check if(!rerrors) Print("OK"); else Print("FAILED"); Print("COMPLEX: "); //--- check if(!cerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestSafeSolveUnit::RMatrixMakeACopy(CMatrixDouble &a, const int m,const int n, CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestSafeSolveUnit::CMatrixMakeACopy(CMatrixComplex &a, const int m,const int n, CMatrixComplex &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Testing class CRCond | //+------------------------------------------------------------------+ class CTestRCondUnit { private: //--- private methods static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower); static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower); static void RMatrixGenZero(CMatrixDouble &a0,const int n); static bool RMatrixInvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular); static bool RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],const int n); static bool RMatrixInvMat(CMatrixDouble &a,const int n); static void RMatrixRefRCond(CMatrixDouble &a,const int n,double &rc1,double &rcinf); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); static void CMatrixGenZero(CMatrixComplex &a0,const int n); static bool CMatrixInvMatTr(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular); static bool CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],const int n); static bool CMatrixInvMat(CMatrixComplex &a,const int n); static void CMatrixRefRCond(CMatrixComplex &a,const int n,double &rc1,double &rcinf); static bool TestRMatrixTrRCond(const int maxn,const int passcount); static bool TestCMatrixTrRCond(const int maxn,const int passcount); static bool TestRMatrixRCond(const int maxn,const int passcount); static bool TestSPDMatrixRCond(const int maxn,const int passcount); static bool TestCMatrixRCond(const int maxn,const int passcount); static bool TestHPDMatrixRCond(const int maxn,const int passcount); public: //--- class constants static const double m_threshold50; static const double m_threshold90; //--- constructor, destructor CTestRCondUnit(void); ~CTestRCondUnit(void); //--- public method static bool TestRCond(const bool silent); }; //+------------------------------------------------------------------+ //| Initialize constants | //+------------------------------------------------------------------+ const double CTestRCondUnit::m_threshold50=0.25; const double CTestRCondUnit::m_threshold90=0.10; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestRCondUnit::CTestRCondUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestRCondUnit::~CTestRCondUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CRCond | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestRCond(const bool silent) { //--- create variables int maxn=0; int passcount=0; bool waserrors; bool rtrerr; bool ctrerr; bool rerr; bool cerr; bool spderr; bool hpderr; //--- initialization maxn=10; passcount=100; //--- report rtrerr=!TestRMatrixTrRCond(maxn,passcount); ctrerr=!TestCMatrixTrRCond(maxn,passcount); rerr=!TestRMatrixRCond(maxn,passcount); cerr=!TestCMatrixRCond(maxn,passcount); spderr=!TestSPDMatrixRCond(maxn,passcount); hpderr=!TestHPDMatrixRCond(maxn,passcount); waserrors=((((rtrerr || ctrerr) || rerr) || cerr) || spderr) || hpderr; //--- check if(!silent) { Print("TESTING RCOND"); Print("REAL TRIANGULAR: "); //--- check if(!rtrerr) Print("OK"); else Print("FAILED"); Print("COMPLEX TRIANGULAR: "); //--- check if(!ctrerr) Print("OK"); else Print("FAILED"); Print("REAL: "); //--- check if(!rerr) Print("OK"); else Print("FAILED"); Print("SPD: "); //--- check if(!spderr) Print("OK"); else Print("FAILED"); Print("HPD: "); //--- check if(!hpderr) Print("OK"); else Print("FAILED"); Print("COMPLEX: "); //--- check if(!cerr) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestRCondUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m, const int n,CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Drops upper or lower half of the matrix - fills it by special | //| pattern which may be used later to ensure that this part wasn't | //| changed | //+------------------------------------------------------------------+ static void CTestRCondUnit::RMatrixDropHalf(CMatrixDouble &a,const int n, const bool droplower) { //--- create variables int i=0; int j=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if((droplower && i>j) || (!droplower && ij) || (!droplower && i0) { for(i_=0;i_<=j-1;i_++) t[i_]=a[i_][j]; for(i=0;i<=j-1;i++) { //--- check if(i=0;j--) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } a[j].Set(j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { //--- change value v=0.0; for(i_=j+1;i_<=i-1;i_++) v+=a[i][i_]*t[i_]; } else v=0; //--- check if(nounit) a[i].Set(j,v+a[i][i]*t[i]); else a[i].Set(j,v+t[i]); } //--- calculation for(i_=j+1;i_<=n-1;i_++) a[i_].Set(j,ajj*a[i_][j]); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| LU inverse | //+------------------------------------------------------------------+ static bool CTestRCondUnit::RMatrixInvMatLU(CMatrixDouble &a,int &pivots[], const int n) { //--- create variables bool result; int i=0; int j=0; int jp=0; double v=0; int i_=0; //--- create array double work[]; //--- initialization result=true; //--- Quick return if possible if(n==0) { //--- return result return(result); } //--- allocation ArrayResize(work,n); //--- Form inv(U) if(!RMatrixInvMatTr(a,n,true,false)) { //--- return result return(false); } //--- Solve the equation inv(A)*L=inv(U) for inv(A). for(j=n-1;j>=0;j--) { //--- Copy current column of L to WORK and replace with zeros. for(i=j+1;i<=n-1;i++) { work[i]=a[i][j]; a[i].Set(j,0); } //--- Compute current column of inv(A). if(j=0;j--) { jp=pivots[j]; //--- check if(jp!=j) { for(i_=0;i_<=n-1;i_++) work[i_]=a[i_][j]; for(i_=0;i_<=n-1;i_++) a[i_].Set(j,a[i_][jp]); for(i_=0;i_<=n-1;i_++) a[i_].Set(jp,work[i_]); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Matrix inverse | //+------------------------------------------------------------------+ static bool CTestRCondUnit::RMatrixInvMat(CMatrixDouble &a,const int n) { //--- create array int pivots[]; //--- function call CTrFac::RMatrixLU(a,n,n,pivots); //--- return result return(RMatrixInvMatLU(a,pivots,n)); } //+------------------------------------------------------------------+ //| reference RCond | //+------------------------------------------------------------------+ static void CTestRCondUnit::RMatrixRefRCond(CMatrixDouble &a,const int n, double &rc1,double &rcinf) { //--- create variables double nrm1a=0; double nrminfa=0; double nrm1inva=0; double nrminfinva=0; double v=0; int k=0; int i=0; //--- create matrix CMatrixDouble inva; //--- initialization rc1=0; rcinf=0; //--- inv A RMatrixMakeACopy(a,n,n,inva); //--- check if(!RMatrixInvMat(inva,n)) { rc1=0; rcinf=0; //--- exit the function return; } //--- norm A nrm1a=0; nrminfa=0; //--- calculation for(k=0;k<=n-1;k++) { //--- change values v=0; for(i=0;i<=n-1;i++) v=v+MathAbs(a[i][k]); nrm1a=MathMax(nrm1a,v); v=0; for(i=0;i<=n-1;i++) v=v+MathAbs(a[k][i]); nrminfa=MathMax(nrminfa,v); } //--- norm inv A nrm1inva=0; nrminfinva=0; //--- calculation for(k=0;k<=n-1;k++) { //--- change values v=0; for(i=0;i<=n-1;i++) v=v+MathAbs(inva[i][k]); nrm1inva=MathMax(nrm1inva,v); v=0; for(i=0;i<=n-1;i++) v=v+MathAbs(inva[k][i]); nrminfinva=MathMax(nrminfinva,v); } //--- result rc1=nrm1inva*nrm1a; rcinf=nrminfinva*nrminfa; } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestRCondUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m, const int n,CMatrixComplex &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Generate matrix with given condition number C (2-norm) | //+------------------------------------------------------------------+ static void CTestRCondUnit::CMatrixGenZero(CMatrixComplex &a0,const int n) { //--- create variables int i=0; int j=0; //--- allocation a0.Resize(n,n); //--- copy for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a0[i].Set(j,0); } } //+------------------------------------------------------------------+ //| triangular inverse | //+------------------------------------------------------------------+ static bool CTestRCondUnit::CMatrixInvMatTr(CMatrixComplex &a,const int n, const bool isupper, const bool isunittriangular) { //--- create variables bool result; bool nounit; int i=0; int j=0; complex v=0; complex ajj=0; complex one=1; int i_=0; //--- create array complex t[]; //--- initialization result=true; //--- allocation ArrayResize(t,n); //--- Test the input parameters. nounit=!isunittriangular; //--- check if(isupper) { //--- Compute inverse of upper triangular matrix. for(j=0;j<=n-1;j++) { //--- check if(nounit) { //--- check if(a[j][j]==0) { //--- return result return(false); } a[j].Set(j,one/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- Compute elements 1:j-1 of j-th column. if(j>0) { for(i_=0;i_<=j-1;i_++) t[i_]=a[i_][j]; //--- calculation for(i=0;i<=j-1;i++) { //--- check if(i=0;j--) { //--- check if(nounit) { //--- check if(a[j][j]==0) { //--- return result return(false); } a[j].Set(j,one/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { //--- change value v=0.0; for(i_=j+1;i_<=i-1;i_++) v+=a[i][i_]*t[i_]; } else v=0; //--- check if(nounit) a[i].Set(j,v+a[i][i]*t[i]); else a[i].Set(j,v+t[i]); } for(i_=j+1;i_<=n-1;i_++) a[i_].Set(j,ajj*a[i_][j]); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| LU inverse | //+------------------------------------------------------------------+ static bool CTestRCondUnit::CMatrixInvMatLU(CMatrixComplex &a,int &pivots[], const int n) { //--- create variables bool result; int i=0; int j=0; int jp=0; complex v=0; int i_=0; //--- create array complex work[]; //--- initialization result=true; //--- Quick return if possible if(n==0) { //--- return result return(result); } //--- allocation ArrayResize(work,n); //--- Form inv(U) if(!CMatrixInvMatTr(a,n,true,false)) { //--- return result return(false); } //--- Solve the equation inv(A)*L=inv(U) for inv(A). for(j=n-1;j>=0;j--) { //--- Copy current column of L to WORK and replace with zeros. for(i=j+1;i<=n-1;i++) { work[i]=a[i][j]; a[i].Set(j,0); } //--- Compute current column of inv(A). if(j=0;j--) { jp=pivots[j]; //--- check if(jp!=j) { //--- change values for(i_=0;i_<=n-1;i_++) work[i_]=a[i_][j]; for(i_=0;i_<=n-1;i_++) a[i_].Set(j,a[i_][jp]); for(i_=0;i_<=n-1;i_++) a[i_].Set(jp,work[i_]); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Matrix inverse | //+------------------------------------------------------------------+ static bool CTestRCondUnit::CMatrixInvMat(CMatrixComplex &a,const int n) { //--- create array int pivots[]; //--- function call CTrFac::CMatrixLU(a,n,n,pivots); //--- return result return(CMatrixInvMatLU(a,pivots,n)); } //+------------------------------------------------------------------+ //| reference RCond | //+------------------------------------------------------------------+ static void CTestRCondUnit::CMatrixRefRCond(CMatrixComplex &a,const int n, double &rc1,double &rcinf) { //--- create variables double nrm1a=0; double nrminfa=0; double nrm1inva=0; double nrminfinva=0; double v=0; int k=0; int i=0; //--- create matrix CMatrixComplex inva; //--- initialization rc1=0; rcinf=0; //--- inv A CMatrixMakeACopy(a,n,n,inva); //--- check if(!CMatrixInvMat(inva,n)) { rc1=0; rcinf=0; //--- exit the function return; } //--- norm A nrm1a=0; nrminfa=0; //--- calculation for(k=0;k<=n-1;k++) { v=0; for(i=0;i<=n-1;i++) v=v+CMath::AbsComplex(a[i][k]); nrm1a=MathMax(nrm1a,v); //--- change value v=0; for(i=0;i<=n-1;i++) v=v+CMath::AbsComplex(a[k][i]); nrminfa=MathMax(nrminfa,v); } //--- norm inv A nrm1inva=0; nrminfinva=0; //--- calculation for(k=0;k<=n-1;k++) { v=0; for(i=0;i<=n-1;i++) v=v+CMath::AbsComplex(inva[i][k]); nrm1inva=MathMax(nrm1inva,v); //--- change value v=0; for(i=0;i<=n-1;i++) v=v+CMath::AbsComplex(inva[k][i]); nrminfinva=MathMax(nrminfinva,v); } //--- result rc1=nrm1inva*nrm1a; rcinf=nrminfinva*nrminfa; } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestRMatrixTrRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int j1=0; int j2=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; double erc1=0; double ercinf=0; double v=0; bool isupper; bool isunit; //--- create arrays int p[]; double q50[]; double q90[]; //--- create matrix CMatrixDouble a; CMatrixDouble ea; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,2); ArrayResize(q90,2); //--- calculation for(n=1;n<=maxn;n++) { //--- special test for zero matrix RMatrixGenZero(a,n); //--- search errors errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; //--- general test a.Resize(n,n); for(i=0;i<=1;i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- change values isupper=CMath::RandomReal()>0.5; isunit=CMath::RandomReal()>0.5; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,CMath::RandomReal()-0.5); } for(i=0;i<=n-1;i++) a[i].Set(i,1+CMath::RandomReal()); //--- function call RMatrixMakeACopy(a,n,n,ea); for(i=0;i<=n-1;i++) { //--- check if(isupper) { j1=0; j2=i-1; } else { j1=i+1; j2=n-1; } //--- change values for(j=j1;j<=j2;j++) ea[i].Set(j,0); //--- check if(isunit) ea[i].Set(i,1); } //--- function call RMatrixRefRCond(ea,n,erc1,ercinf); //--- 1-norm v=1/CRCond::RMatrixTrRCond1(a,n,isupper,isunit); //--- check if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; //--- Inf-norm v=1/CRCond::RMatrixTrRCondInf(a,n,isupper,isunit); //--- check if(v>=m_threshold50*ercinf) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=(double)(m_threshold90*ercinf)) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless || v>ercinf*1.001; } //--- search errors for(i=0;i<=1;i++) { err50=err50 || q50[i]<0.5; err90=err90 || q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } a[0].Set(0,1); a[n-1].Set(n-1,1); //--- search errors errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; } //--- near-degenerate matrix test if(n>=2) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } //--- change values for(i=0;i<=n-1;i++) a[i].Set(i,1); i=CMath::RandomInteger(n); a[i].Set(i,0.1*CMath::m_maxrealnumber); //--- search errors errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; } } //--- report result=!(((err50 || err90) || errless) || errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestCMatrixTrRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int j1=0; int j2=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; double erc1=0; double ercinf=0; double v=0; bool isupper; bool isunit; //--- create arrays int p[]; double q50[]; double q90[]; //--- create matrix CMatrixComplex a; CMatrixComplex ea; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,2); ArrayResize(q90,2); //--- calculation for(n=1;n<=maxn;n++) { //--- special test for zero matrix CMatrixGenZero(a,n); //--- search errors errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; //--- general test a.Resize(n,n); for(i=0;i<=1;i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- change values isupper=CMath::RandomReal()>0.5; isunit=CMath::RandomReal()>0.5; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,CMath::RandomReal()-0.5); a[i].SetIm(j,CMath::RandomReal()-0.5); } } //--- change values for(i=0;i<=n-1;i++) { a[i].SetRe(i,1+CMath::RandomReal()); a[i].SetIm(i,1+CMath::RandomReal()); } //--- function call CMatrixMakeACopy(a,n,n,ea); //--- change values for(i=0;i<=n-1;i++) { //--- check if(isupper) { j1=0; j2=i-1; } else { j1=i+1; j2=n-1; } for(j=j1;j<=j2;j++) ea[i].Set(j,0); //--- check if(isunit) ea[i].Set(i,1); } //--- function call CMatrixRefRCond(ea,n,erc1,ercinf); //--- 1-norm v=1/CRCond::CMatrixTrRCond1(a,n,isupper,isunit); if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=(double)(m_threshold90*erc1)) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless || v>(double)(erc1*1.001); //--- Inf-norm v=1/CRCond::CMatrixTrRCondInf(a,n,isupper,isunit); //--- check if(v>=m_threshold50*ercinf) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=(double)(m_threshold90*ercinf)) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless || v>(double)(ercinf*1.001); } //--- search errors for(i=0;i<=1;i++) { err50=err50 || q50[i]<0.5; err90=err90 || q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } a[0].Set(0,1); a[n-1].Set(n-1,1); //--- search errors errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; } //--- near-degenerate matrix test if(n>=2) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } //--- change values for(i=0;i<=n-1;i++) a[i].Set(i,1); i=CMath::RandomInteger(n); a[i].Set(i,0.1*CMath::m_maxrealnumber); //--- search errors errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; } } //--- report result=!(((err50 || err90) || errless) || errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestRMatrixRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; double erc1=0; double ercinf=0; double v=0; //--- create array int p[]; double q50[]; double q90[]; //--- create matrix CMatrixDouble a; CMatrixDouble lua; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,4); ArrayResize(q90,4); //--- calculation for(n=1;n<=maxn;n++) { //--- special test for zero matrix RMatrixGenZero(a,n); RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); //--- search errors errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0; errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0; errspec=errspec || CRCond::RMatrixLURCond1(lua,n)!=0.0; errspec=errspec || CRCond::RMatrixLURCondInf(lua,n)!=0.0; //--- general test a.Resize(n,n); for(i=0;i<=3;i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- function call CMatGen::RMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a); RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); RMatrixRefRCond(a,n,erc1,ercinf); //--- 1-norm,normal v=1/CRCond::RMatrixRCond1(a,n); //--- check if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless || v>(double)(erc1*1.001); //--- 1-norm,LU v=1/CRCond::RMatrixLURCond1(lua,n); //--- check if(v>=m_threshold50*erc1) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; //--- Inf-norm,normal v=1/CRCond::RMatrixRCondInf(a,n); //--- check if(v>=m_threshold50*ercinf) q50[2]=q50[2]+1.0/(double)passcount; //--- check if(v>=m_threshold90*ercinf) q90[2]=q90[2]+1.0/(double)passcount; //--- search errors errless=errless || v>ercinf*1.001; //--- Inf-norm,LU v=1/CRCond::RMatrixLURCondInf(lua,n); //--- check if(v>=m_threshold50*ercinf) q50[3]=q50[3]+1.0/(double)passcount; //--- check if(v>=m_threshold90*ercinf) q90[3]=q90[3]+1.0/(double)passcount; //--- search errors errless=errless || v>ercinf*1.001; } //--- search errors for(i=0;i<=3;i++) { err50=err50 || q50[i]<0.5; err90=err90 || q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } a[0].Set(0,1); a[n-1].Set(n-1,1); //--- search errors errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0; errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0; errspec=errspec || CRCond::RMatrixLURCond1(a,n)!=0.0; errspec=errspec || CRCond::RMatrixLURCondInf(a,n)!=0.0; } //--- near-degenerate matrix test if(n>=2) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } //--- change values for(i=0;i<=n-1;i++) a[i].Set(i,1); i=CMath::RandomInteger(n); a[i].Set(i,0.1*CMath::m_maxrealnumber); //--- search errors errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0; errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0; errspec=errspec || CRCond::RMatrixLURCond1(a,n)!=0.0; errspec=errspec || CRCond::RMatrixLURCondInf(a,n)!=0.0; } } //--- report result=!(((err50 || err90) || errless) || errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestSPDMatrixRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; bool isupper; double erc1=0; double ercinf=0; double v=0; //--- create arrays int p[]; double q50[]; double q90[]; //--- create matrix CMatrixDouble a; CMatrixDouble cha; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,2); ArrayResize(q90,2); //--- calculation for(n=1;n<=maxn;n++) { isupper=CMath::RandomReal()>0.5; //--- general test a.Resize(n,n); for(i=0;i<=1;i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- function calls CMatGen::SPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a); RMatrixRefRCond(a,n,erc1,ercinf); RMatrixDropHalf(a,n,isupper); RMatrixMakeACopy(a,n,n,cha); CTrFac::SPDMatrixCholesky(cha,n,isupper); //--- normal v=1/CRCond::SPDMatrixRCond(a,n,isupper); //--- check if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; //--- Cholesky v=1/CRCond::SPDMatrixCholeskyRCond(cha,n,isupper); if(v>=m_threshold50*erc1) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; } //--- search errors for(i=0;i<=1;i++) { err50=err50 || q50[i]<0.5; err90=err90 || q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } a[0].Set(0,1); a[n-1].Set(n-1,1); //--- search errors errspec=errspec || CRCond::SPDMatrixRCond(a,n,isupper)!=-1.0; errspec=errspec || CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0; } //--- near-degenerate matrix test if(n>=2) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } //--- change values for(i=0;i<=n-1;i++) a[i].Set(i,1); i=CMath::RandomInteger(n); a[i].Set(i,0.1*CMath::m_maxrealnumber); //--- search errors errspec=errspec || CRCond::SPDMatrixRCond(a,n,isupper)!=0.0; errspec=errspec || CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0; } } //--- report result=!(((err50 || err90) || errless) || errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestCMatrixRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int pass=0; bool err50; bool err90; bool errless; bool errspec; double erc1=0; double ercinf=0; double v=0; //--- create arrays int p[]; double q50[]; double q90[]; //--- create matrix CMatrixComplex a; CMatrixComplex lua; //--- allocation ArrayResize(q50,4); ArrayResize(q90,4); //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- process for(n=1;n<=maxn;n++) { //--- special test for zero matrix CMatrixGenZero(a,n); CMatrixMakeACopy(a,n,n,lua); CTrFac::CMatrixLU(lua,n,n,p); //--- search errors errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0; errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0; errspec=errspec || CRCond::CMatrixLURCond1(lua,n)!=0.0; errspec=errspec || CRCond::CMatrixLURCondInf(lua,n)!=0.0; //--- general test a.Resize(n,n); for(i=0;i<=3;i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- function calls CMatGen::CMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a); CMatrixMakeACopy(a,n,n,lua); CTrFac::CMatrixLU(lua,n,n,p); CMatrixRefRCond(a,n,erc1,ercinf); //--- 1-norm,normal v=1/CRCond::CMatrixRCond1(a,n); //--- check if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; //--- 1-norm,LU v=1/CRCond::CMatrixLURCond1(lua,n); //--- check if(v>=m_threshold50*erc1) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; //--- Inf-norm,normal v=1/CRCond::CMatrixRCondInf(a,n); //--- check if(v>=m_threshold50*ercinf) q50[2]=q50[2]+1.0/(double)passcount; //--- check if(v>=m_threshold90*ercinf) q90[2]=q90[2]+1.0/(double)passcount; //--- search errors errless=errless || v>ercinf*1.001; //--- Inf-norm,LU v=1/CRCond::CMatrixLURCondInf(lua,n); //--- check if(v>=m_threshold50*ercinf) q50[3]=q50[3]+1.0/(double)passcount; //--- check if(v>=m_threshold90*ercinf) q90[3]=q90[3]+1.0/(double)passcount; //--- search errors errless=errless || v>ercinf*1.001; } //--- search errors for(i=0;i<=3;i++) { err50=err50 || q50[i]<0.5; err90=err90 || q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } a[0].Set(0,1); a[n-1].Set(n-1,1); //--- search errors errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0; errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0; errspec=errspec || CRCond::CMatrixLURCond1(a,n)!=0.0; errspec=errspec || CRCond::CMatrixLURCondInf(a,n)!=0.0; } //--- near-degenerate matrix test if(n>=2) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } //--- change values for(i=0;i<=n-1;i++) a[i].Set(i,1); i=CMath::RandomInteger(n); a[i].Set(i,0.1*CMath::m_maxrealnumber); //--- search errors errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0; errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0; errspec=errspec || CRCond::CMatrixLURCond1(a,n)!=0.0; errspec=errspec || CRCond::CMatrixLURCondInf(a,n)!=0.0; } } //--- report result=!(((err50 || err90) || errless) || errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ static bool CTestRCondUnit::TestHPDMatrixRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; bool isupper; double erc1=0; double ercinf=0; double v=0; //--- create arrays int p[]; double q50[]; double q90[]; //--- create matrix CMatrixComplex a; CMatrixComplex cha; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,2); ArrayResize(q90,2); for(n=1;n<=maxn;n++) { isupper=CMath::RandomReal()>0.5; //--- general test a.Resize(n,n); for(i=0;i<=1;i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- function calls CMatGen::HPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a); CMatrixRefRCond(a,n,erc1,ercinf); CMatrixDropHalf(a,n,isupper); CMatrixMakeACopy(a,n,n,cha); CTrFac::HPDMatrixCholesky(cha,n,isupper); //--- normal v=1/CRCond::HPDMatrixRCond(a,n,isupper); //--- check if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; //--- Cholesky v=1/CRCond::HPDMatrixCholeskyRCond(cha,n,isupper); //--- check if(v>=m_threshold50*erc1) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless || v>erc1*1.001; } //--- search errors for(i=0;i<=1;i++) { err50=err50 || q50[i]<0.5; err90=err90 || q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } a[0].Set(0,1); a[n-1].Set(n-1,1); //--- search errors errspec=errspec || CRCond::HPDMatrixRCond(a,n,isupper)!=-1.0; errspec=errspec || CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0; } //--- near-degenerate matrix test if(n>=2) { //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0.0); } //--- change values for(i=0;i<=n-1;i++) a[i].Set(i,1); i=CMath::RandomInteger(n); a[i].Set(i,0.1*CMath::m_maxrealnumber); //--- search errors errspec=errspec || CRCond::HPDMatrixRCond(a,n,isupper)!=0.0; errspec=errspec || CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0; } } //--- report result=!(((err50 || err90) || errless) || errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CMatInv | //+------------------------------------------------------------------+ class CTestMatInvUnit { private: //--- private methods static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); static bool RMatrixCheckInverse(CMatrixDouble &a,CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep); static bool SPDMatrixCheckInverse(CMatrixDouble &ca,CMatrixDouble &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep); static bool HPDMatrixCheckInverse(CMatrixComplex &ca,CMatrixComplex &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep); static bool RMatrixCheckInverseSingular(CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep); static bool CMatrixCheckInverse(CMatrixComplex &a,CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep); static bool CMatrixCheckInverseSingular(CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep); static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower); static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower); static void TestRTRInv(const int maxn,const int passcount,const double threshold,bool &rtrerrors); static void TestCTRInv(const int maxn,const int passcount,const double threshold,bool &ctrerrors); static void TesTrInv(const int maxn,const int passcount,const double threshold,bool &rerrors); static void TestCInv(const int maxn,const int passcount,const double threshold,bool &cerrors); static void TestSPDInv(const int maxn,const int passcount,const double threshold,bool &spderrors); static void TestHPDInv(const int maxn,const int passcount,const double threshold,bool &hpderrors); static void Unset2D(CMatrixDouble &x); static void Unset1D(double &x[]); static void CUnset2D(CMatrixComplex &x); static void CUnset1D(double &x[]); static void UnsetRep(CMatInvReport &r); public: //--- constructor, destructor CTestMatInvUnit(void); ~CTestMatInvUnit(void); //--- public method static bool TestMatInv(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMatInvUnit::CTestMatInvUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMatInvUnit::~CTestMatInvUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::TestMatInv(const bool silent) { //--- create variables int maxrn=0; int maxcn=0; int passcount=0; double threshold=0; double rcondtol=0; bool rtrerrors; bool ctrerrors; bool rerrors; bool cerrors; bool spderrors; bool hpderrors; bool waserrors; //--- create matrix CMatrixDouble emptyra; CMatrixDouble emptyca; //--- initialization maxrn=3*CAblas::AblasBlockSize()+1; maxcn=3*CAblas::AblasBlockSize()+1; passcount=1; threshold=10000*CMath::m_machineepsilon; rcondtol=0.01; rtrerrors=false; ctrerrors=false; rerrors=false; cerrors=false; spderrors=false; hpderrors=false; //--- function calls TestRTRInv(maxrn,passcount,threshold,rtrerrors); TestCTRInv(maxcn,passcount,threshold,ctrerrors); TesTrInv(maxrn,passcount,threshold,rerrors); TestSPDInv(maxrn,passcount,threshold,spderrors); TestCInv(maxcn,passcount,threshold,cerrors); TestHPDInv(maxcn,passcount,threshold,hpderrors); //--- search errors waserrors=((((rtrerrors || ctrerrors) || rerrors) || cerrors) || spderrors) || hpderrors; //--- check if(!silent) { Print("TESTING MATINV"); Print("* REAL TRIANGULAR: "); //--- check if(rtrerrors) Print("FAILED"); else Print("OK"); Print("* COMPLEX TRIANGULAR: "); //--- check if(ctrerrors) Print("FAILED"); else Print("OK"); Print("* REAL: "); //--- check if(rerrors) Print("FAILED"); else Print("OK"); Print("* COMPLEX: "); //--- check if(cerrors) Print("FAILED"); else Print("OK"); Print("* SPD: "); //--- check if(spderrors) Print("FAILED"); else Print("OK"); Print("* HPD: "); //--- check if(hpderrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestMatInvUnit::RMatrixMakeACopy(CMatrixDouble &a, const int m,const int n, CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestMatInvUnit::CMatrixMakeACopy(CMatrixComplex &a, const int m,const int n, CMatrixComplex &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Checks whether inverse is correct | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::RMatrixCheckInverse(CMatrixDouble &a, CMatrixDouble &inva, const int n, const double threshold, const int info, CMatInvReport &rep) { //--- create variables bool result; int i=0; int j=0; double v=0; int i_=0; //--- initialization result=true; //--- check if(info<=0) result=false; else { result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*inva[i_][j]; //--- check if(i==j) v=v-1; result=result && MathAbs(v)<=threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether inverse is correct | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::SPDMatrixCheckInverse(CMatrixDouble &ca, CMatrixDouble &cinva, const bool isupper, const int n, const double threshold, const int info, CMatInvReport &rep) { //--- create variables bool result; int i=0; int j=0; double v=0; int i_=0; //--- create matrix CMatrixDouble a; CMatrixDouble inva; //--- copy a=ca; inva=cinva; //--- calculation for(i=0;i<=n-2;i++) { //--- check if(isupper) { //--- change values for(i_=i+1;i_<=n-1;i_++) a[i_].Set(i,a[i][i_]); for(i_=i+1;i_<=n-1;i_++) inva[i_].Set(i,inva[i][i_]); } else { //--- change values for(i_=i+1;i_<=n-1;i_++) a[i].Set(i_,a[i_][i]); for(i_=i+1;i_<=n-1;i_++) inva[i].Set(i_,inva[i_][i]); } } //--- change value result=true; //--- check if(info<=0) result=false; else { result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*inva[i_][j]; //--- check if(i==j) v=v-1; result=result && MathAbs(v)<=threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether inverse is correct | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::HPDMatrixCheckInverse(CMatrixComplex &ca, CMatrixComplex &cinva, const bool isupper, const int n, const double threshold, const int info, CMatInvReport &rep) { //--- create variables bool result; int i=0; int j=0; complex v=0; int i_=0; //--- create matrix CMatrixComplex a; CMatrixComplex inva; //--- copy a=ca; inva=cinva; //--- calculation for(i=0;i<=n-2;i++) { //--- check if(isupper) { //--- change values for(i_=i+1;i_<=n-1;i_++) a[i_].Set(i,CMath::Conj(a[i][i_])); for(i_=i+1;i_<=n-1;i_++) inva[i_].Set(i,CMath::Conj(inva[i][i_])); } else { //--- change values for(i_=i+1;i_<=n-1;i_++) a[i].Set(i_,CMath::Conj(a[i_][i])); for(i_=i+1;i_<=n-1;i_++) inva[i].Set(i_,CMath::Conj(inva[i_][i])); } } //--- change value result=true; //--- check if(info<=0) result=false; else { result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*inva[i_][j]; //--- check if(i==j) v=v-1; result=result && CMath::AbsComplex(v)<=threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether inversion result indicate singular matrix | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::RMatrixCheckInverseSingular(CMatrixDouble &inva, const int n, const double threshold, const int info, CMatInvReport &rep) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=true; //--- check if(info!=-3 && info!=1) result=false; else { result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) result=result && inva[i][j]==0.0; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether inverse is correct | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::CMatrixCheckInverse(CMatrixComplex &a, CMatrixComplex &inva, const int n, const double threshold, const int info, CMatInvReport &rep) { //--- create variables bool result; int i=0; int j=0; complex v=0; int i_=0; //--- initialization result=true; //--- check if(info<=0) result=false; else { result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*inva[i_][j]; //--- check if(i==j) v=v-1; result=result && CMath::AbsComplex(v)<=threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether inversion result indicate singular matrix | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestMatInvUnit::CMatrixCheckInverseSingular(CMatrixComplex &inva, const int n, const double threshold, const int info, CMatInvReport &rep) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=true; //--- check if(info!=-3 && info!=1) result=false; else { result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) result=result && inva[i][j]==0; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Drops upper or lower half of the matrix - fills it by special | //| pattern which may be used later to ensure that this part wasn't | //| changed | //+------------------------------------------------------------------+ static void CTestMatInvUnit::RMatrixDropHalf(CMatrixDouble &a,const int n, const bool droplower) { //--- create variables int i=0; int j=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if((droplower && i>j) || (!droplower && ij) || (!droplower && ii && !isupper)) { a[i].Set(j,0); b[i].Set(j,0); } } } //--- check if(isunit) { for(i=0;i<=n-1;i++) { a[i].Set(i,1); b[i].Set(i,1); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*b[i_][j]; //--- check if(j!=i) rtrerrors=rtrerrors || MathAbs(v)>threshold; else rtrerrors=rtrerrors || MathAbs(v-1)>threshold; } } } } } } //+------------------------------------------------------------------+ //| Complex TR inverse | //+------------------------------------------------------------------+ static void CTestMatInvUnit::TestCTRInv(const int maxn,const int passcount, const double threshold,bool &ctrerrors) { //--- create variables int n=0; int pass=0; int i=0; int j=0; int task=0; bool isupper; bool isunit; complex v=0; bool waserrors; int info=0; int i_=0; //--- create arrays CMatrixComplex a; CMatrixComplex b; //--- object of class CMatInvReport rep; //--- initialization waserrors=false; //--- Test for(n=1;n<=maxn;n++) { //--- allocation a.Resize(n,n); b.Resize(n,n); //--- calculation for(task=0;task<=3;task++) { for(pass=1;pass<=passcount;pass++) { //--- Determine task isupper=task%2==0; isunit=task/2%2==0; //--- Generate matrix for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i==j) { a[i].SetRe(i,1+CMath::RandomReal()); a[i].SetIm(i,1+CMath::RandomReal()); } else { a[i].SetRe(j,0.2*CMath::RandomReal()-0.1); a[i].SetIm(j,0.2*CMath::RandomReal()-0.1); } b[i].Set(j,a[i][j]); } } //--- Inverse CMatInv::CMatrixTrInverse(b,n,isupper,isunit,info,rep); //--- check if(info<=0) { ctrerrors=true; return; } //--- Structural test if(isunit) { for(i=0;i<=n-1;i++) ctrerrors=ctrerrors || a[i][i]!=b[i][i]; } //--- check if(isupper) { for(i=0;i<=n-1;i++) { for(j=0;j<=i-1;j++) ctrerrors=ctrerrors || a[i][j]!=b[i][j]; } } else { for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) ctrerrors=ctrerrors || a[i][j]!=b[i][j]; } } //--- Inverse test for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if((ji && !isupper)) { a[i].Set(j,0); b[i].Set(j,0); } } } //--- check if(isunit) { for(i=0;i<=n-1;i++) { a[i].Set(i,1); b[i].Set(i,1); } } //--- search errors for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*b[i_][j]; //--- check if(j!=i) ctrerrors=ctrerrors || CMath::AbsComplex(v)>threshold; else ctrerrors=ctrerrors || CMath::AbsComplex(v-1)>threshold; } } } } } } //+------------------------------------------------------------------+ //| Real test | //+------------------------------------------------------------------+ static void CTestMatInvUnit::TesTrInv(const int maxn,const int passcount, const double threshold,bool &rerrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int pass=0; int taskkind=0; int info=0; int i_=0; //--- create array int p[]; //--- create matrix CMatrixDouble a; CMatrixDouble lua; CMatrixDouble inva; CMatrixDouble invlua; //--- object of class CMatInvReport rep; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::RMatrixRndCond(n,1000,a); RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); RMatrixMakeACopy(a,n,n,inva); RMatrixMakeACopy(lua,n,n,invlua); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::RMatrixInverse(inva,n,info,rep); //--- search errors rerrors=rerrors || !RMatrixCheckInverse(a,inva,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::RMatrixLUInverse(invlua,p,n,info,rep); //--- search errors rerrors=rerrors || !RMatrixCheckInverse(a,invlua,n,threshold,info,rep); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. test different methods for(taskkind=0;taskkind<=4;taskkind++) { Unset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,0*a[i_][k]); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,0*a[k][i_]); } //--- check if(taskkind==3) { //--- equal columns if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[i_].Set(0,a[i_][k]); } //--- check if(taskkind==4) { //--- equal rows if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[0].Set(i_,a[k][i_]); } //--- function calls RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::RMatrixInverse(a,n,info,rep); //--- search errors rerrors=rerrors || !RMatrixCheckInverseSingular(a,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::RMatrixLUInverse(lua,p,n,info,rep); //--- search errors rerrors=rerrors || !RMatrixCheckInverseSingular(lua,n,threshold,info,rep); } } } } //+------------------------------------------------------------------+ //| Complex test | //+------------------------------------------------------------------+ static void CTestMatInvUnit::TestCInv(const int maxn,const int passcount, const double threshold,bool &cerrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int pass=0; int taskkind=0; int info=0; int i_=0; //--- create array int p[]; //--- create matrix CMatrixComplex a; CMatrixComplex lua; CMatrixComplex inva; CMatrixComplex invlua; //--- object of class CMatInvReport rep; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::CMatrixRndCond(n,1000,a); CMatrixMakeACopy(a,n,n,lua); CTrFac::CMatrixLU(lua,n,n,p); CMatrixMakeACopy(a,n,n,inva); CMatrixMakeACopy(lua,n,n,invlua); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::CMatrixInverse(inva,n,info,rep); //--- search errors cerrors=cerrors || !CMatrixCheckInverse(a,inva,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::CMatrixLUInverse(invlua,p,n,info,rep); //--- search errors cerrors=cerrors || !CMatrixCheckInverse(a,invlua,n,threshold,info,rep); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. test different methods for(taskkind=0;taskkind<=4;taskkind++) { CUnset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,a[i_][k]*0); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,a[k][i_]*0); } //--- check if(taskkind==3) { //--- equal columns if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[i_].Set(0,a[i_][k]); } //--- check if(taskkind==4) { //--- equal rows if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change value k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[0].Set(i_,a[k][i_]); } //--- function calls CMatrixMakeACopy(a,n,n,lua); CTrFac::CMatrixLU(lua,n,n,p); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::CMatrixInverse(a,n,info,rep); //--- search errors cerrors=cerrors || !CMatrixCheckInverseSingular(a,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::CMatrixLUInverse(lua,p,n,info,rep); //--- search errors cerrors=cerrors || !CMatrixCheckInverseSingular(lua,n,threshold,info,rep); } } } } //+------------------------------------------------------------------+ //| SPD test | //+------------------------------------------------------------------+ static void CTestMatInvUnit::TestSPDInv(const int maxn,const int passcount, const double threshold,bool &spderrors) { //--- create variables bool isupper; int i=0; int j=0; int k=0; int n=0; int pass=0; int taskkind=0; int info=0; int i_=0; //--- create matrix CMatrixDouble a; CMatrixDouble cha; CMatrixDouble inva; CMatrixDouble invcha; //--- object of class CMatInvReport rep; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { isupper=CMath::RandomReal()>0.5; //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::SPDMatrixRndCond(n,1000,a); RMatrixDropHalf(a,n,isupper); RMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::SPDMatrixCholesky(cha,n,isupper)) continue; //--- function calls RMatrixMakeACopy(a,n,n,inva); RMatrixMakeACopy(cha,n,n,invcha); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::SPDMatrixInverse(inva,n,isupper,info,rep); //--- search errors spderrors=spderrors || !SPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::SPDMatrixCholeskyInverse(invcha,n,isupper,info,rep); //--- search errors spderrors=spderrors || !SPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- 2. test different methods for(taskkind=0;taskkind<=2;taskkind++) { Unset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,0*a[i_][k]); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,0*a[k][i_]); } //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::SPDMatrixCholeskyInverse(a,n,isupper,info,rep); //--- check if(info!=-3 && info!=1) spderrors=true; else { spderrors=(spderrors || rep.m_r1<0.0) || rep.m_r1>1000*CMath::m_machineepsilon; spderrors=(spderrors || rep.m_rinf<0.0) || rep.m_rinf>1000*CMath::m_machineepsilon; } } } } } //+------------------------------------------------------------------+ //| HPD test | //+------------------------------------------------------------------+ static void CTestMatInvUnit::TestHPDInv(const int maxn,const int passcount, const double threshold,bool &hpderrors) { //--- create variables bool isupper; int i=0; int j=0; int k=0; int n=0; int pass=0; int taskkind=0; int info=0; int i_=0; //--- create matrix CMatrixComplex a; CMatrixComplex cha; CMatrixComplex inva; CMatrixComplex invcha; //--- object of class CMatInvReport rep; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { isupper=CMath::RandomReal()>0.5; //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::HPDMatrixRndCond(n,1000,a); CMatrixDropHalf(a,n,isupper); CMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::HPDMatrixCholesky(cha,n,isupper)) continue; //--- function calls CMatrixMakeACopy(a,n,n,inva); CMatrixMakeACopy(cha,n,n,invcha); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::HPDMatrixInverse(inva,n,isupper,info,rep); //--- search errors hpderrors=hpderrors || !HPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::HPDMatrixCholeskyInverse(invcha,n,isupper,info,rep); //--- search errors hpderrors=hpderrors || !HPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- 2. test different methods for(taskkind=0;taskkind<=2;taskkind++) { CUnset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,a[i_][k]*0); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,a[k][i_]*0); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,a[k][i_]*0); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,a[i_][k]*0); } //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::HPDMatrixCholeskyInverse(a,n,isupper,info,rep); //--- check if(info!=-3 && info!=1) hpderrors=true; else { hpderrors=(hpderrors || rep.m_r1<0.0) || rep.m_r1>1000*CMath::m_machineepsilon; hpderrors=(hpderrors || rep.m_rinf<0.0) || rep.m_rinf>1000*CMath::m_machineepsilon; } } } } } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ static void CTestMatInvUnit::Unset2D(CMatrixDouble &x) { //--- allocation x.Resize(1,1); //--- change value x[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ static void CTestMatInvUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ static void CTestMatInvUnit::CUnset2D(CMatrixComplex &x) { //--- allocation x.Resize(1,1); //--- change value x[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ static void CTestMatInvUnit::CUnset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets report | //+------------------------------------------------------------------+ static void CTestMatInvUnit::UnsetRep(CMatInvReport &r) { //--- change values r.m_r1=-1; r.m_rinf=-1; } //+------------------------------------------------------------------+ //| Testing class CLDA | //+------------------------------------------------------------------+ class CTestLDAUnit { private: //--- private methods static void GenSimpleSet(const int nfeatures,const int nclasses,const int nsamples,const int axis,CMatrixDouble &xy); static void GenDeg1Set(const int nfeatures,const int nclasses,const int nsamples,int axis,CMatrixDouble &xy); static double GenerateNormal(const double mean,const double sigma); static bool TestWN(CMatrixDouble &xy,CMatrixDouble &wn,const int ns,const int nf,const int nc,const int ndeg); static double CalcJ(const int nf,CMatrixDouble &st,CMatrixDouble &sw,double &w[],double &p,double &q); static void Fishers(CMatrixDouble &xy,int npoints,const int nfeatures,const int nclasses,CMatrixDouble &st,CMatrixDouble &sw); public: //--- constructor, destructor CTestLDAUnit(void); ~CTestLDAUnit(void); //--- public method static bool TestLDA(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestLDAUnit::CTestLDAUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestLDAUnit::~CTestLDAUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CLDA | //+------------------------------------------------------------------+ static bool CTestLDAUnit::TestLDA(const bool silent) { //--- create variables int maxnf=0; int maxns=0; int maxnc=0; int passcount=0; bool ldanerrors; bool lda1errors; bool waserrors; int nf=0; int nc=0; int ns=0; int i=0; int info=0; int pass=0; int axis=0; //--- create array double w1[]; //--- create matrix CMatrixDouble xy; CMatrixDouble wn; //--- Primary settings maxnf=10; maxns=1000; maxnc=5; passcount=1; waserrors=false; ldanerrors=false; lda1errors=false; //--- General tests for(nf=1;nf<=maxnf;nf++) { for(nc=2;nc<=maxnc;nc++) { for(pass=1;pass<=passcount;pass++) { //--- Simple test for LDA-N/LDA-1 axis=CMath::RandomInteger(nf); ns=maxns/2+CMath::RandomInteger(maxns/2); //--- function calls GenSimpleSet(nf,nc,ns,axis,xy); CLDA::FisherLDAN(xy,ns,nf,nc,info,wn); //--- check if(info!=1) { ldanerrors=true; continue; } //--- search errors ldanerrors=ldanerrors || !TestWN(xy,wn,ns,nf,nc,0); ldanerrors=ldanerrors || MathAbs(wn[axis][0])<=0.75; //--- function call CLDA::FisherLDA(xy,ns,nf,nc,info,w1); //--- search errors for(i=0;i<=nf-1;i++) lda1errors=lda1errors || w1[i]!=wn[i][0]; //--- Degenerate test for LDA-N if(nf>=3) { ns=maxns/2+CMath::RandomInteger(maxns/2); //--- there are two duplicate features, //--- axis is oriented along non-duplicate feature axis=CMath::RandomInteger(nf-2); GenDeg1Set(nf,nc,ns,axis,xy); CLDA::FisherLDAN(xy,ns,nf,nc,info,wn); //--- check if(info!=2) { ldanerrors=true; continue; } //--- function calls ldanerrors=ldanerrors || wn[axis][0]<=0.75; CLDA::FisherLDA(xy,ns,nf,nc,info,w1); //--- search errors for(i=0;i<=nf-1;i++) lda1errors=lda1errors || w1[i]!=wn[i][0]; } } } } //--- Final report waserrors=ldanerrors || lda1errors; //--- check if(!silent) { Print("LDA TEST"); Print("FISHER LDA-N: "); //--- check if(!ldanerrors) Print("OK"); else Print("FAILED"); Print("FISHER LDA-1: "); //--- check if(!lda1errors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Generates 'simple' set - a sequence of unit 'balls' at | //| (0,0),(1,0),(2,0) and so on. | //+------------------------------------------------------------------+ static void CTestLDAUnit::GenSimpleSet(const int nfeatures,const int nclasses, const int nsamples,const int axis, CMatrixDouble &xy) { //--- create variables int i=0; int j=0; int c=0; //--- check if(!CAp::Assert(axis>=0 && axis=3. | //+------------------------------------------------------------------+ static void CTestLDAUnit::GenDeg1Set(const int nfeatures,const int nclasses, const int nsamples,int axis, CMatrixDouble &xy) { //--- create variables int i=0; int j=0; int c=0; //--- check if(!CAp::Assert(axis>=0 && axis=3,"GenDeg1Set: wrong NFeatures!")) return; //--- allocation xy.Resize(nsamples,nfeatures+1); //--- check if(axis>=nfeatures-2) axis=nfeatures-3; //--- calculation for(i=0;i<=nsamples-1;i++) { for(j=0;j<=nfeatures-2;j++) xy[i].Set(j,GenerateNormal(0.0,1.0)); //--- change values xy[i].Set(nfeatures-1,xy[i][nfeatures-2]); c=i%nclasses; xy[i].Set(axis,xy[i][axis]+c); xy[i].Set(nfeatures,c); } } //+------------------------------------------------------------------+ //| Normal random number | //+------------------------------------------------------------------+ static double CTestLDAUnit::GenerateNormal(const double mean,const double sigma) { //--- create variables double result=0; double u=0; double v=0; double sum=0; //--- initialization result=mean; //--- calculation while(true) { //--- change values u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal(); v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal(); sum=u*u+v*v; //--- check if(sum<1.0 && sum>0.0) { sum=MathSqrt(-(2*MathLog(sum)/sum)); result=sigma*u*sum+mean; //--- return result return(result); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests WN for correctness | //+------------------------------------------------------------------+ static bool CTestLDAUnit::TestWN(CMatrixDouble &xy,CMatrixDouble &wn, const int ns,const int nf, const int nc,const int ndeg) { //--- create variables bool result; int i=0; int j=0; double v=0; double wprev=0; double tol=0; double p=0; double q=0; int i_=0; //--- create arrays double tx[]; double jp[]; double jq[]; double work[]; //--- create matrix CMatrixDouble st; CMatrixDouble sw; CMatrixDouble a; CMatrixDouble z; //--- initialization tol=10000; result=true; Fishers(xy,ns,nf,nc,st,sw); //--- Test for decreasing of J ArrayResize(tx,nf); ArrayResize(jp,nf); ArrayResize(jq,nf); //--- calculation for(j=0;j<=nf-1;j++) { for(i_=0;i_<=nf-1;i_++) tx[i_]=wn[i_][j]; v=CalcJ(nf,st,sw,tx,p,q); jp[j]=p; jq[j]=q; } //--- calculation for(i=1;i<=nf-1-ndeg;i++) result=result && jp[i-1]/jq[i-1]>=(1-tol*CMath::m_machineepsilon)*jp[i]/jq[i]; for(i=nf-1-ndeg+1;i<=nf-1;i++) result=result && jp[i]<=tol*CMath::m_machineepsilon*jp[0]; //--- Test for J optimality for(i_=0;i_<=nf-1;i_++) tx[i_]=wn[i_][0]; v=CalcJ(nf,st,sw,tx,p,q); //--- calculation for(i=0;i<=nf-1;i++) { wprev=tx[i]; tx[i]=wprev+0.01; result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q); tx[i]=wprev-0.01; result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q); tx[i]=wprev; } //--- Test for linear independence of W ArrayResize(work,nf+1); a.Resize(nf,nf); //--- function call CBlas::MatrixMatrixMultiply(wn,0,nf-1,0,nf-1,false,wn,0,nf-1,0,nf-1,true,1.0,a,0,nf-1,0,nf-1,0.0,work); //--- check if(CEigenVDetect::SMatrixEVD(a,nf,1,true,tx,z)) result=result && tx[0]>tx[nf-1]*1000*CMath::m_machineepsilon; //--- Test for other properties for(j=0;j<=nf-1;j++) { //--- change value v=0.0; for(i_=0;i_<=nf-1;i_++) v+=wn[i_][j]*wn[i_][j]; //--- change values v=MathSqrt(v); result=result && MathAbs(v-1)<=1000*CMath::m_machineepsilon; v=0; for(i=0;i<=nf-1;i++) v=v+wn[i][j]; result=result && v>=0.0; } //--- return result return(result); } //+------------------------------------------------------------------+ //| Calculates J | //+------------------------------------------------------------------+ static double CTestLDAUnit::CalcJ(const int nf,CMatrixDouble &st, CMatrixDouble &sw,double &w[], double &p,double &q) { //--- create variables double result=0; int i=0; double v=0; int i_=0; //--- create array double tx[]; //--- initialization p=0; q=0; //--- allocation ArrayResize(tx,nf); //--- calculation for(i=0;i<=nf-1;i++) { //--- change value v=0.0; for(i_=0;i_<=nf-1;i_++) v+=st[i][i_]*w[i_]; tx[i]=v; } //--- change value v=0.0; for(i_=0;i_<=nf-1;i_++) v+=w[i_]*tx[i_]; p=v; for(i=0;i<=nf-1;i++) { //--- change value v=0.0; for(i_=0;i_<=nf-1;i_++) v+=sw[i][i_]*w[i_]; tx[i]=v; } //--- change value v=0.0; for(i_=0;i_<=nf-1;i_++) v+=w[i_]*tx[i_]; q=v; result=p/q; //--- return result return(result); } //+------------------------------------------------------------------+ //| Calculates ST/SW | //+------------------------------------------------------------------+ static void CTestLDAUnit::Fishers(CMatrixDouble &xy,int npoints, const int nfeatures,const int nclasses, CMatrixDouble &st,CMatrixDouble &sw) { //--- create variables int i=0; int j=0; int k=0; double v=0; int i_=0; //--- create arrays int c[]; double mu[]; int nc[]; double tf[]; double work[]; //--- create matrix CMatrixDouble muc; //--- Prepare temporaries ArrayResize(tf,nfeatures); ArrayResize(work,nfeatures+1); //--- Convert class labels from reals to integers (just for convenience) ArrayResize(c,npoints); for(i=0;i<=npoints-1;i++) c[i]=(int)MathRound(xy[i][nfeatures]); //--- Calculate class sizes and means ArrayResize(mu,nfeatures); muc.Resize(nclasses,nfeatures); ArrayResize(nc,nclasses); //--- change values for(j=0;j<=nfeatures-1;j++) mu[j]=0; for(i=0;i<=nclasses-1;i++) { nc[i]=0; for(j=0;j<=nfeatures-1;j++) muc[i].Set(j,0); } //--- calculation for(i=0;i<=npoints-1;i++) { for(i_=0;i_<=nfeatures-1;i_++) mu[i_]=mu[i_]+xy[i][i_]; for(i_=0;i_<=nfeatures-1;i_++) muc[c[i]].Set(i_,muc[c[i]][i_]+xy[i][i_]); nc[c[i]]=nc[c[i]]+1; } //--- calculation for(i=0;i<=nclasses-1;i++) { v=1.0/(double)nc[i]; for(i_=0;i_<=nfeatures-1;i_++) muc[i].Set(i_,v*muc[i][i_]); } //--- change values v=1.0/(double)npoints; for(i_=0;i_<=nfeatures-1;i_++) mu[i_]=v*mu[i_]; //--- Create ST matrix st.Resize(nfeatures,nfeatures); //--- change values for(i=0;i<=nfeatures-1;i++) { for(j=0;j<=nfeatures-1;j++) st[i].Set(j,0); } //--- calculation for(k=0;k<=npoints-1;k++) { for(i_=0;i_<=nfeatures-1;i_++) tf[i_]=xy[k][i_]; for(i_=0;i_<=nfeatures-1;i_++) tf[i_]=tf[i_]-mu[i_]; //--- calculation for(i=0;i<=nfeatures-1;i++) { v=tf[i]; for(i_=0;i_<=nfeatures-1;i_++) st[i].Set(i_,st[i][i_]+v*tf[i_]); } } //--- Create SW matrix sw.Resize(nfeatures,nfeatures); for(i=0;i<=nfeatures-1;i++) { for(j=0;j<=nfeatures-1;j++) sw[i].Set(j,0); } //--- calculation for(k=0;k<=npoints-1;k++) { for(i_=0;i_<=nfeatures-1;i_++) tf[i_]=xy[k][i_]; for(i_=0;i_<=nfeatures-1;i_++) tf[i_]=tf[i_]-muc[c[k]][i_]; //--- calculation for(i=0;i<=nfeatures-1;i++) { v=tf[i]; for(i_=0;i_<=nfeatures-1;i_++) sw[i].Set(i_,sw[i][i_]+v*tf[i_]); } } } //+------------------------------------------------------------------+ //| Testing class CGammaFunc | //+------------------------------------------------------------------+ class CTestGammaFuncUnit { public: //--- constructor, destructor CTestGammaFuncUnit(void); ~CTestGammaFuncUnit(void); //--- public method static bool TestGammaFunc(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestGammaFuncUnit::CTestGammaFuncUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestGammaFuncUnit::~CTestGammaFuncUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CGammaFunc | //+------------------------------------------------------------------+ static bool CTestGammaFuncUnit::TestGammaFunc(const bool silent) { //--- create variables double threshold=0; double v=0; double s=0; bool waserrors; bool gammaerrors; bool lngammaerrors; //--- initialization gammaerrors=false; lngammaerrors=false; waserrors=false; threshold=100*CMath::m_machineepsilon; //--- search errors gammaerrors=gammaerrors || MathAbs(CGammaFunc::GammaFunc(0.5)-MathSqrt(M_PI))>threshold; gammaerrors=gammaerrors || MathAbs(CGammaFunc::GammaFunc(1.5)-0.5*MathSqrt(M_PI))>threshold; //--- function call v=CGammaFunc::LnGamma(0.5,s); //--- search errors lngammaerrors=(lngammaerrors || MathAbs(v-MathLog(MathSqrt(M_PI)))>threshold) || s!=1.0; //--- function call v=CGammaFunc::LnGamma(1.5,s); //--- search errors lngammaerrors=(lngammaerrors || MathAbs(v-MathLog(0.5*MathSqrt(M_PI)))>threshold) || s!=1.0; //--- report waserrors=gammaerrors || lngammaerrors; //--- check if(!silent) { Print("TESTING GAMMA FUNCTION"); Print("GAMMA: "); //--- check if(gammaerrors) Print("FAILED"); else Print("OK"); Print("LN GAMMA: "); //--- check if(lngammaerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CBdSingValueDecompose | //+------------------------------------------------------------------+ class CTestBdSVDUnit { private: //--- private methods static void FillIdentity(CMatrixDouble &a,const int n); static void FillSparseDE(double &d[],double &e[],const int n,const double sparcity); static void GetBdSVDError(double &d[],double &e[],const int n,const bool isupper,CMatrixDouble &u,CMatrixDouble &c,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted); static void TestBdSVDProblem(double &d[],double &e[],const int n,double &materr,double &orterr,bool &wsorted,bool &wfailed,int &failcount,int &succcount); public: //--- constructor, destructor CTestBdSVDUnit(void); ~CTestBdSVDUnit(void); //--- public method static bool TestBdSVD(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestBdSVDUnit::CTestBdSVDUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestBdSVDUnit::~CTestBdSVDUnit(void) { } //+------------------------------------------------------------------+ //| Testing bidiagonal SVD decomposition subroutine | //+------------------------------------------------------------------+ static bool CTestBdSVDUnit::TestBdSVD(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int pass=0; bool waserrors; bool wsorted; bool wfailed; bool failcase; double materr=0; double orterr=0; double threshold=0; double failthreshold=0; double failr=0; int failcount=0; int succcount=0; //--- create arrays double d[]; double e[]; //--- create matrix CMatrixDouble mempty; //--- initialization failcount=0; succcount=0; materr=0; orterr=0; wsorted=true; wfailed=false; waserrors=false; maxn=15; threshold=5*100*CMath::m_machineepsilon; failthreshold=1.0E-2; //--- allocation ArrayResize(d,maxn); ArrayResize(e,maxn-1); //--- special case: fail matrix n=5; d[0]=-8.27448347422711894000e-01; d[1]=-8.16705832087160854600e-01; d[2]=-2.53974358904729382800e-17; d[3]=-1.24626684881972815700e+00; d[4]=-4.64744131545637651000e-01; e[0]=-3.25785088656270038800e-01; e[1]=-1.03732413708914436580e-01; e[2]=-9.57365642262031357700e-02; e[3]=-2.71564153973817390400e-01; failcase=CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0); //--- special case: zero divide matrix //--- unfixed LAPACK routine should fail on this problem n=7; d[0]=-6.96462904751731892700e-01; d[1]=0.00000000000000000000e+00; d[2]=-5.73827770385971991400e-01; d[3]=-6.62562624399371191700e-01; d[4]=5.82737148001782223600e-01; d[5]=3.84825263580925003300e-01; d[6]=9.84087420830525472200e-01; e[0]=-7.30307931760612871800e-02; e[1]=-2.30079042939542843800e-01; e[2]=-6.87824621739351216300e-01; e[3]=-1.77306437707837570600e-02; e[4]=1.78285126526551632000e-15; e[5]=-4.89434737751289969400e-02; CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0); //--- zero matrix,several cases for(i=0;i<=maxn-1;i++) d[i]=0; for(i=0;i<=maxn-2;i++) e[i]=0; for(n=1;n<=maxn;n++) TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); //--- Dense matrix for(n=1;n<=maxn;n++) { for(pass=1;pass<=10;pass++) { for(i=0;i<=maxn-1;i++) d[i]=2*CMath::RandomReal()-1; for(i=0;i<=maxn-2;i++) e[i]=2*CMath::RandomReal()-1; //--- function call TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); } } //--- Sparse matrices,very sparse matrices,incredible sparse matrices for(n=1;n<=maxn;n++) { for(pass=1;pass<=10;pass++) { //--- function calls FillSparseDE(d,e,n,0.5); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); FillSparseDE(d,e,n,0.8); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); FillSparseDE(d,e,n,0.9); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); FillSparseDE(d,e,n,0.95); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); } } //--- report failr=(double)failcount/(double)(succcount+failcount); waserrors=((materr>threshold || orterr>threshold) || !wsorted) || failr>failthreshold; //--- check if(!silent) { Print("TESTING BIDIAGONAL SVD DECOMPOSITION"); Print("SVD decomposition error: "); Print("{0,5:E3}",materr); Print("SVD orthogonality error: "); Print("{0,5:E3}",orterr); Print("Singular values order: "); //--- check if(wsorted) Print("OK"); else Print("FAILED"); Print("Always converged: "); //--- check if(!wfailed) Print("YES"); else { Print("NO"); Print("Fail ratio: "); Print("{0,5:F3}",failr); } Print("Fail matrix test: "); //--- check if(!failcase) Print("AS EXPECTED"); else Print("CONVERGED (UNEXPECTED)"); Print("Threshold: "); Print("{0,5:E3}",threshold); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestBdSVDUnit::FillIdentity(CMatrixDouble &a,const int n) { //--- create variables int i=0; int j=0; //--- allocation a.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i==j) a[i].Set(j,1); else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestBdSVDUnit::FillSparseDE(double &d[],double &e[], const int n,const double sparcity) { //--- create a variable int i=0; //--- allocation ArrayResize(d,n); ArrayResize(e,(int)(MathMax(0,n-2))+1); //--- change values for(i=0;i<=n-1;i++) { //--- check if(CMath::RandomReal()>=sparcity) d[i]=2*CMath::RandomReal()-1; else d[i]=0; } //--- change values for(i=0;i<=n-2;i++) { //--- check if(CMath::RandomReal()>=sparcity) e[i]=2*CMath::RandomReal()-1; else e[i]=0; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestBdSVDUnit::GetBdSVDError(double &d[],double &e[], const int n,const bool isupper, CMatrixDouble &u,CMatrixDouble &c, double &w[],CMatrixDouble &vt, double &materr,double &orterr, bool &wsorted) { //--- create variables int i=0; int j=0; int k=0; double locerr=0; double sm=0; int i_=0; //--- decomposition error locerr=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value sm=0; for(k=0;k<=n-1;k++) sm=sm+w[k]*u[i][k]*vt[k][j]; //--- check if(isupper) { //--- check if(i==j) locerr=MathMax(locerr,MathAbs(d[i]-sm)); else { //--- check if(i==j-1) locerr=MathMax(locerr,MathAbs(e[i]-sm)); else locerr=MathMax(locerr,MathAbs(sm)); } } else { //--- check if(i==j) locerr=MathMax(locerr,MathAbs(d[i]-sm)); else { //--- check if(i-1==j) locerr=MathMax(locerr,MathAbs(e[j]-sm)); else locerr=MathMax(locerr,MathAbs(sm)); } } } } //--- change value materr=MathMax(materr,locerr); //--- check for C=U' //--- we consider it as decomposition error locerr=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) locerr=MathMax(locerr,MathAbs(u[i][j]-c[j][i])); } materr=MathMax(materr,locerr); //--- orthogonality error locerr=0; for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { //--- change value sm=0.0; for(i_=0;i_<=n-1;i_++) sm+=u[i_][i]*u[i_][j]; //--- check if(i!=j) locerr=MathMax(locerr,MathAbs(sm)); else locerr=MathMax(locerr,MathAbs(sm-1)); //--- change value sm=0.0; for(i_=0;i_<=n-1;i_++) sm+=vt[i][i_]*vt[j][i_]; //--- check if(i!=j) locerr=MathMax(locerr,MathAbs(sm)); else locerr=MathMax(locerr,MathAbs(sm-1)); } } orterr=MathMax(orterr,locerr); //--- values order error for(i=1;i<=n-1;i++) { //--- check if(w[i]>w[i-1]) wsorted=false; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestBdSVDUnit::TestBdSVDProblem(double &d[],double &e[], const int n,double &materr, double &orterr,bool &wsorted, bool &wfailed,int &failcount, int &succcount) { //--- create variables int i=0; double mx=0; //--- create array double w[]; //--- create matrix CMatrixDouble u; CMatrixDouble vt; CMatrixDouble c; //--- change value mx=0; for(i=0;i<=n-1;i++) { //--- check if(MathAbs(d[i])>mx) mx=MathAbs(d[i]); } for(i=0;i<=n-2;i++) { //--- check if(MathAbs(e[i])>mx) mx=MathAbs(e[i]); } //--- check if(mx==0.0) mx=1; //--- Upper BDSVD tests ArrayResize(w,n); FillIdentity(u,n); FillIdentity(vt,n); FillIdentity(c,n); for(i=0;i<=n-1;i++) w[i]=d[i]; //--- check if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,false,u,n,c,n,vt,n)) { failcount=failcount+1; wfailed=true; return; } //--- function calls GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted); FillIdentity(u,n); FillIdentity(vt,n); FillIdentity(c,n); //--- copy for(i=0;i<=n-1;i++) w[i]=d[i]; //--- check if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,true,u,n,c,n,vt,n)) { failcount=failcount+1; wfailed=true; return; } //--- function call GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted); //--- Lower BDSVD tests ArrayResize(w,n); FillIdentity(u,n); FillIdentity(vt,n); FillIdentity(c,n); //--- copy for(i=0;i<=n-1;i++) w[i]=d[i]; //--- check if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,false,u,n,c,n,vt,n)) { failcount=failcount+1; wfailed=true; return; } //--- function calls GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted); FillIdentity(u,n); FillIdentity(vt,n); FillIdentity(c,n); //--- copy for(i=0;i<=n-1;i++) w[i]=d[i]; //--- check if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,true,u,n,c,n,vt,n)) { failcount=failcount+1; wfailed=true; return; } //--- function call GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted); //--- update counter succcount=succcount+1; } //+------------------------------------------------------------------+ //| Testing class CSingValueDecompose | //+------------------------------------------------------------------+ class CTestSVDUnit { private: //--- private methods static void FillsParseA(CMatrixDouble &a,const int m,const int n,const double sparcity); static void GetSVDError(CMatrixDouble &a,const int m,const int n,CMatrixDouble &u,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted); static void TestSVDProblem(CMatrixDouble &a,const int m,const int n,double &materr,double &orterr,double &othererr,bool &wsorted,bool &wfailed,int &failcount,int &succcount); public: //--- constructor, destructor CTestSVDUnit(void); ~CTestSVDUnit(void); //--- public method static bool TestSVD(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSVDUnit::CTestSVDUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSVDUnit::~CTestSVDUnit(void) { } //+------------------------------------------------------------------+ //| Testing SVD decomposition subroutine | //+------------------------------------------------------------------+ static bool CTestSVDUnit::TestSVD(const bool silent) { //--- create variables int m=0; int n=0; int maxmn=0; int i=0; int j=0; int gpass=0; int pass=0; bool waserrors; bool wsorted; bool wfailed; double materr=0; double orterr=0; double othererr=0; double threshold=0; double failthreshold=0; double failr=0; int failcount=0; int succcount=0; //--- create matrix CMatrixDouble a; //--- initialization failcount=0; succcount=0; materr=0; orterr=0; othererr=0; wsorted=true; wfailed=false; waserrors=false; maxmn=30; threshold=5*100*CMath::m_machineepsilon; failthreshold=5.0E-3; //--- allocation a.Resize(maxmn,maxmn); //--- TODO: div by zero fail,convergence fail for(gpass=1;gpass<=1;gpass++) { //--- zero matrix,several cases for(i=0;i<=maxmn-1;i++) { for(j=0;j<=maxmn-1;j++) a[i].Set(j,0); } //--- function calls for(i=1;i<=MathMin(5,maxmn);i++) { for(j=1;j<=MathMin(5,maxmn);j++) TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); } //--- Long dense matrix for(i=0;i<=maxmn-1;i++) { for(j=0;j<=MathMin(5,maxmn)-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- function calls for(i=1;i<=maxmn;i++) { for(j=1;j<=MathMin(5,maxmn);j++) TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); } //--- change values for(i=0;i<=MathMin(5,maxmn)-1;i++) { for(j=0;j<=maxmn-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- function calls for(i=1;i<=MathMin(5,maxmn);i++) { for(j=1;j<=maxmn;j++) TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); } //--- Dense matrices for(m=1;m<=MathMin(10,maxmn);m++) { for(n=1;n<=MathMin(10,maxmn);n++) { for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- function call TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); } } //--- Sparse matrices,very sparse matrices,incredible sparse matrices for(m=1;m<=10;m++) { for(n=1;n<=10;n++) { for(pass=1;pass<=2;pass++) { //--- function calls FillsParseA(a,m,n,0.8); TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); FillsParseA(a,m,n,0.9); TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); FillsParseA(a,m,n,0.95); TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount); } } } } //--- report failr=(double)failcount/(double)(succcount+failcount); waserrors=(((materr>threshold || orterr>threshold) || othererr>threshold) || !wsorted) || failr>failthreshold; //--- check if(!silent) { Print("TESTING SVD DECOMPOSITION"); Print("SVD decomposition error: "); Print("{0,5:E3}",materr); Print("SVD orthogonality error: "); Print("{0,5:E3}",orterr); Print("SVD with different parameters error: "); Print("{0,5:E3}",othererr); Print("Singular values order: "); //--- check if(wsorted) Print("OK"); else Print("FAILED"); Print("Always converged: "); //--- check if(!wfailed) Print("YES"); else { Print("NO"); Print("Fail ratio: "); Print("{0,5:F3}",failr); } Print("Threshold: "); Print("{0,5:E3}",threshold); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestSVDUnit::FillsParseA(CMatrixDouble &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- change values for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::RandomReal()>=sparcity) a[i].Set(j,2*CMath::RandomReal()-1); else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestSVDUnit::GetSVDError(CMatrixDouble &a,const int m, const int n,CMatrixDouble &u, double &w[],CMatrixDouble &vt, double &materr,double &orterr, bool &wsorted) { //--- create variables int i=0; int j=0; int k=0; int minmn=0; double locerr=0; double sm=0; int i_=0; //--- initialization minmn=MathMin(m,n); //--- decomposition error locerr=0; for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- change value sm=0; for(k=0;k<=minmn-1;k++) sm=sm+w[k]*u[i][k]*vt[k][j]; locerr=MathMax(locerr,MathAbs(a[i][j]-sm)); } } materr=MathMax(materr,locerr); //--- orthogonality error locerr=0; for(i=0;i<=minmn-1;i++) { for(j=i;j<=minmn-1;j++) { //--- change value sm=0.0; for(i_=0;i_<=m-1;i_++) sm+=u[i_][i]*u[i_][j]; //--- check if(i!=j) locerr=MathMax(locerr,MathAbs(sm)); else locerr=MathMax(locerr,MathAbs(sm-1)); //--- change value sm=0.0; for(i_=0;i_<=n-1;i_++) sm+=vt[i][i_]*vt[j][i_]; //--- check if(i!=j) locerr=MathMax(locerr,MathAbs(sm)); else locerr=MathMax(locerr,MathAbs(sm-1)); } } orterr=MathMax(orterr,locerr); //--- values order error for(i=1;i<=minmn-1;i++) { //--- check if(w[i]>w[i-1]) wsorted=false; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestSVDUnit::TestSVDProblem(CMatrixDouble &a,const int m, const int n,double &materr, double &orterr,double &othererr, bool &wsorted,bool &wfailed, int &failcount,int &succcount) { //--- create variables int i=0; int j=0; int ujob=0; int vtjob=0; int memjob=0; int ucheck=0; int vtcheck=0; //--- create arrays double w[]; double w2[]; //--- create matrix CMatrixDouble u; CMatrixDouble vt; CMatrixDouble u2; CMatrixDouble vt2; //--- Main SVD test if(!CSingValueDecompose::RMatrixSVD(a,m,n,2,2,2,w,u,vt)) { failcount=failcount+1; wfailed=true; //--- exit the function return; } //--- function call GetSVDError(a,m,n,u,w,vt,materr,orterr,wsorted); //--- Additional SVD tests for(ujob=0;ujob<=2;ujob++) { for(vtjob=0;vtjob<=2;vtjob++) { for(memjob=0;memjob<=2;memjob++) { //--- check if(!CSingValueDecompose::RMatrixSVD(a,m,n,ujob,vtjob,memjob,w2,u2,vt2)) { failcount=failcount+1; wfailed=true; //--- exit the function return; } //--- change value ucheck=0; //--- check if(ujob==1) ucheck=MathMin(m,n); //--- check if(ujob==2) ucheck=m; //--- change value vtcheck=0; //--- check if(vtjob==1) vtcheck=MathMin(m,n); //--- check if(vtjob==2) vtcheck=n; //--- search errors for(i=0;i<=m-1;i++) { for(j=0;j<=ucheck-1;j++) othererr=MathMax(othererr,MathAbs(u[i][j]-u2[i][j])); } //--- search errors for(i=0;i<=vtcheck-1;i++) { for(j=0;j<=n-1;j++) othererr=MathMax(othererr,MathAbs(vt[i][j]-vt2[i][j])); } //--- search errors for(i=0;i<=MathMin(m,n)-1;i++) othererr=MathMax(othererr,MathAbs(w[i]-w2[i])); } } } //--- update counter succcount=succcount+1; } //+------------------------------------------------------------------+ //| Testing class CLinReg | //+------------------------------------------------------------------+ class CTestLinRegUnit { public: //--- constructor, destructor CTestLinRegUnit(void); ~CTestLinRegUnit(void); //--- public methods static bool TestLinReg(const bool silent); static void GenerateRandomTask(const double xl,const double xr,const bool randomx,const double ymin,const double ymax,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]); static void GenerateTask(const double a,const double b,const double xl,const double xr,const bool randomx,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]); static void FillTaskWithY(const double a,const double b,const int n,CMatrixDouble &xy,double &s[]); static double GenerateNormal(const double mean,const double sigma); static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs); static void UnsetLR(CLinearModel &lr); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestLinRegUnit::CTestLinRegUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestLinRegUnit::~CTestLinRegUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CLinReg | //+------------------------------------------------------------------+ static bool CTestLinRegUnit::TestLinReg(const bool silent) { //--- create variables double sigmathreshold=0; int maxn=0; int maxm=0; int passcount=0; int estpasscount=0; double threshold=0; int n=0; int i=0; int j=0; int k=0; int tmpi=0; int pass=0; int epass=0; int m=0; int tasktype=0; int modeltype=0; int m1=0; int m2=0; int n1=0; int n2=0; int info=0; int info2=0; double y1=0; double y2=0; bool allsame; double ea=0; double eb=0; double varatested=0; double varbtested=0; double a=0; double b=0; double vara=0; double varb=0; double a2=0; double b2=0; double covab=0; double corrab=0; double p=0; int qcnt=0; double f=0; double fp=0; double fm=0; double v=0; double vv=0; double cvrmserror=0; double cvavgerror=0; double cvavgrelerror=0; double rmserror=0; double avgerror=0; double avgrelerror=0; bool nondefect; double sinshift=0; double tasklevel=0; double noiselevel=0; double hstep=0; double sigma=0; double mean=0; double means=0; double stddev=0; double stddevs=0; bool slcerrors; bool slerrors; bool grcoverrors; bool gropterrors; bool gresterrors; bool grothererrors; bool grconverrors; bool waserrors; int i_=0; //--- create arrays double s[]; double s2[]; double w2[]; double x[]; double ta[]; double tb[]; double tc[]; double xy0[]; double tmpweights[]; double x1[]; double x2[]; double qtbl[]; double qvals[]; double qsigma[]; //--- create matrix CMatrixDouble xy; CMatrixDouble xy2; //--- objects of classes CLinearModel w; CLinearModel wt; CLinearModel wt2; CLRReport ar; CLRReport ar2; //--- Primary settings maxn=40; maxm=5; passcount=3; estpasscount=1000; sigmathreshold=7; threshold=1000000*CMath::m_machineepsilon; slerrors=false; slcerrors=false; grcoverrors=false; gropterrors=false; gresterrors=false; grothererrors=false; grconverrors=false; waserrors=false; //--- Quantiles table setup qcnt=5; ArrayResize(qtbl,qcnt); ArrayResize(qvals,qcnt); ArrayResize(qsigma,qcnt); qtbl[0]=0.5; qtbl[1]=0.25; qtbl[2]=0.10; qtbl[3]=0.05; qtbl[4]=0.025; for(i=0;i<=qcnt-1;i++) qsigma[i]=MathSqrt(qtbl[i]*(1-qtbl[i])/estpasscount); //--- Other setup ArrayResize(ta,estpasscount); ArrayResize(tb,estpasscount); //--- Test straight line regression for(n=2;n<=maxn;n++) { //--- Fail/pass test GenerateRandomTask(-1,1,false,-1,1,1,2,n,xy,s); CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- search errors slcerrors=slcerrors || info!=1; //--- function calls GenerateRandomTask(1,1,false,-1,1,1,2,n,xy,s); CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- search errors slcerrors=slcerrors || info!=-3; //--- function calls GenerateRandomTask(-1,1,false,-1,1,-1,-1,n,xy,s); CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- search errors slcerrors=slcerrors || info!=-2; //--- function calls GenerateRandomTask(-1,1,false,-1,1,2,1,2,xy,s); CLinReg::LRLines(xy,s,1,info,a,b,vara,varb,covab,corrab,p); //--- search errors slcerrors=slcerrors || info!=-1; //--- Multipass tests for(pass=1;pass<=passcount;pass++) { //--- Test S variant against non-S variant ea=2*CMath::RandomReal()-1; eb=2*CMath::RandomReal()-1; //--- function calls GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),CMath::RandomReal()>0.5,1,1,n,xy,s); CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); CLinReg::LRLine(xy,n,info2,a2,b2); //--- check if(info!=1 || info2!=1) slcerrors=true; else slerrors=(slerrors || MathAbs(a-a2)>threshold) || MathAbs(b-b2)>threshold; //--- Test for A/B //--- Generate task with exact,non-perturbed y[i], //--- then make non-zero s[i] ea=2*CMath::RandomReal()-1; eb=2*CMath::RandomReal()-1; GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,0.0,0.0,n,xy,s); for(i=0;i<=n-1;i++) s[i]=1+CMath::RandomReal(); //--- function call CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- check if(info!=1) slcerrors=true; else slerrors=(slerrors || MathAbs(a-ea)>0.001) || MathAbs(b-eb)>0.001; //--- Test for VarA,VarB,P (P is being tested only for N>2) for(i=0;i<=qcnt-1;i++) qvals[i]=0; ea=2*CMath::RandomReal()-1; eb=2*CMath::RandomReal()-1; //--- function calls GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,1.0,2.0,n,xy,s); CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- check if(info!=1) { slcerrors=true; continue; } //--- change values varatested=vara; varbtested=varb; //--- calculation for(epass=0;epass<=estpasscount-1;epass++) { //--- Generate FillTaskWithY(ea,eb,n,xy,s); CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- check if(info!=1) { slcerrors=true; continue; } //--- A,B,P //--- (P is being tested for uniformity,additional p-tests are below) ta[epass]=a; tb[epass]=b; for(i=0;i<=qcnt-1;i++) { //--- check if(p<=qtbl[i]) qvals[i]=qvals[i]+1.0/(double)estpasscount; } } //--- function call CalculateMV(ta,estpasscount,mean,means,stddev,stddevs); //--- search errors slerrors=slerrors || MathAbs(mean-ea)/means>=sigmathreshold; slerrors=slerrors || MathAbs(stddev-MathSqrt(varatested))/stddevs>=sigmathreshold; //--- function call CalculateMV(tb,estpasscount,mean,means,stddev,stddevs); //--- search errors slerrors=slerrors || MathAbs(mean-eb)/means>=sigmathreshold; slerrors=slerrors || MathAbs(stddev-MathSqrt(varbtested))/stddevs>=sigmathreshold; //--- check if(n>2) { for(i=0;i<=qcnt-1;i++) { //--- check if(MathAbs(qtbl[i]-qvals[i])/qsigma[i]>sigmathreshold) slerrors=true; } } //--- Additional tests for P: correlation with fit quality if(n>2) { GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,0.0,0.0,n,xy,s); for(i=0;i<=n-1;i++) s[i]=1+CMath::RandomReal(); //--- function call CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- check if(info!=1) { slcerrors=true; continue; } //--- search errors slerrors=slerrors || p<(double)(0.999); //--- function call GenerateTask(0,0,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,1.0,1.0,n,xy,s); for(i=0;i<=n-1;i++) { //--- check if(i%2==0) xy[i].Set(1,5.0); else xy[i].Set(1,-5.0); } //--- check if(n%2!=0) xy[n-1].Set(1,0); //--- function call CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p); //--- check if(info!=1) { slcerrors=true; continue; } //--- search errors slerrors=slerrors || p>0.001; } } } //--- General regression tests: //--- Simple linear tests (small sample,optimum point,covariance) for(n=3;n<=maxn;n++) { ArrayResize(s,n); //--- Linear tests: //--- a. random points,sigmas //--- b. no sigmas xy.Resize(n,2); for(i=0;i<=n-1;i++) { xy[i].Set(0,2*CMath::RandomReal()-1); xy[i].Set(1,2*CMath::RandomReal()-1); s[i]=1+CMath::RandomReal(); } //--- function call CLinReg::LRBuildS(xy,s,n,1,info,wt,ar); //--- check if(info!=1) { grconverrors=true; continue; } //--- function calls CLinReg::LRUnpack(wt,tmpweights,tmpi); //--- search errors CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p); gropterrors=gropterrors || MathAbs(a-tmpweights[1])>threshold; gropterrors=gropterrors || MathAbs(b-tmpweights[0])>threshold; grcoverrors=grcoverrors || MathAbs(vara-ar.m_c[1][1])>threshold; grcoverrors=grcoverrors || MathAbs(varb-ar.m_c[0][0])>threshold; grcoverrors=grcoverrors || MathAbs(covab-ar.m_c[1][0])>threshold; grcoverrors=grcoverrors || MathAbs(covab-ar.m_c[0][1])>threshold; //--- function call CLinReg::LRBuild(xy,n,1,info,wt,ar); //--- check if(info!=1) { grconverrors=true; continue; } //--- function calls CLinReg::LRUnpack(wt,tmpweights,tmpi); CLinReg::LRLine(xy,n,info2,a,b); //--- search errors gropterrors=gropterrors || MathAbs(a-tmpweights[1])>threshold; gropterrors=gropterrors || MathAbs(b-tmpweights[0])>threshold; } //--- S covariance versus S-less covariance. //--- Slightly skewed task,large sample size. //--- Will S-less subroutine estimate covariance matrix good enough? n=1000+CMath::RandomInteger(3000); sigma=0.1+CMath::RandomReal()*1.9; //--- allocation xy.Resize(n,2); ArrayResize(s,n); //--- change values for(i=0;i<=n-1;i++) { xy[i].Set(0,1.5*CMath::RandomReal()-0.5); xy[i].Set(1,1.2*xy[i][0]-0.3+GenerateNormal(0,sigma)); s[i]=sigma; } //--- function calls CLinReg::LRBuild(xy,n,1,info,wt,ar); CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p); //--- check if(info!=1 || info2!=1) grconverrors=true; else { grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[0][0]/varb))>MathLog(1.2); grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[1][1]/vara))>MathLog(1.2); grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[0][1]/covab))>MathLog(1.2); grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[1][0]/covab))>MathLog(1.2); } //--- General tests: //--- * basis functions - up to cubic //--- * task types: //--- * data set is noisy sine half-period with random shift //--- * tests: //--- unpacking/packing //--- optimality //--- error estimates //--- * tasks: //--- 0=noised sine //--- 1=degenerate task with 1-of-n encoded categorical variables //--- 2=random task with large variation (for 1-type models) //--- 3=random task with small variation (for 1-type models) //--- Additional tasks TODO //--- specially designed task with defective vectors which leads to //--- the failure of the fast CV formula. for(modeltype=0;modeltype<=1;modeltype++) { for(tasktype=0;tasktype<=3;tasktype++) { //--- check if(tasktype==0) { m1=1; m2=3; } //--- check if(tasktype==1) { m1=9; m2=9; } //--- check if(tasktype==2 || tasktype==3) { m1=9; m2=9; } //--- calculation for(m=m1;m<=m2;m++) { //--- check if(tasktype==0) { n1=m+3; n2=m+20; } //--- check if(tasktype==1) { n1=70+CMath::RandomInteger(70); n2=n1; } //--- check if(tasktype==2 || tasktype==3) { n1=100; n2=n1; } for(n=n1;n<=n2;n++) { //--- allocation xy.Resize(n,m+1); ArrayResize(xy0,n); ArrayResize(s,n); hstep=0.001; noiselevel=0.2; //--- Prepare task if(tasktype==0) { for(i=0;i<=n-1;i++) xy[i].Set(0,2*CMath::RandomReal()-1); for(i=0;i<=n-1;i++) { for(j=1;j<=m-1;j++) xy[i].Set(j,xy[i][0]*xy[i][j-1]); } sinshift=CMath::RandomReal()*M_PI; //--- calculation for(i=0;i<=n-1;i++) { xy0[i]=MathSin(sinshift+M_PI*0.5*(xy[i][0]+1)); xy[i].Set(m,xy0[i]+noiselevel*GenerateNormal(0,1)); } } //--- check if(tasktype==1) { //--- check if(!CAp::Assert(m==9)) return(false); //--- allocation ArrayResize(ta,9); //--- change values ta[0]=1; ta[1]=2; ta[2]=3; ta[3]=0.25; ta[4]=0.5; ta[5]=0.75; ta[6]=0.06; ta[7]=0.12; ta[8]=0.18; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) xy[i].Set(j,0); xy[i].Set(i%3,1); xy[i].Set(3+i/3%3,1); xy[i].Set(6+i/9%3,1); //--- change value v=0.0; for(i_=0;i_<=8;i_++) v+=xy[i][i_]*ta[i_]; xy0[i]=v; xy[i].Set(m,v+noiselevel*GenerateNormal(0,1)); } } //--- check if(tasktype==2 || tasktype==3) { //--- check if(!CAp::Assert(m==9)) return(false); //--- allocation ArrayResize(ta,9); //--- change values ta[0]=1; ta[1]=-2; ta[2]=3; ta[3]=0.25; ta[4]=-0.5; ta[5]=0.75; ta[6]=-0.06; ta[7]=0.12; ta[8]=-0.18; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- check if(tasktype==2) xy[i].Set(j,1+GenerateNormal(0,3)); else xy[i].Set(j,1+GenerateNormal(0,0.05)); } //--- change value v=0.0; for(i_=0;i_<=8;i_++) v+=xy[i][i_]*ta[i_]; xy0[i]=v; xy[i].Set(m,v+noiselevel*GenerateNormal(0,1)); } } for(i=0;i<=n-1;i++) s[i]=1+CMath::RandomReal(); //--- Solve (using S-variant,non-S-variant is not tested) if(modeltype==0) CLinReg::LRBuildS(xy,s,n,m,info,wt,ar); else CLinReg::LRBuildZS(xy,s,n,m,info,wt,ar); //--- check if(info!=1) { grconverrors=true; continue; } //--- function call CLinReg::LRUnpack(wt,tmpweights,tmpi); //--- LRProcess test ArrayResize(x,m); //--- change values v=tmpweights[m]; for(i=0;i<=m-1;i++) { x[i]=2*CMath::RandomReal()-1; v=v+tmpweights[i]*x[i]; } //--- search errors grothererrors=grothererrors || MathAbs(v-CLinReg::LRProcess(wt,x))/MathMax(MathAbs(v),1)>threshold; //--- LRPack test CLinReg::LRPack(tmpweights,m,wt2); ArrayResize(x,m); for(i=0;i<=m-1;i++) x[i]=2*CMath::RandomReal()-1; v=CLinReg::LRProcess(wt,x); grothererrors=grothererrors || MathAbs(v-CLinReg::LRProcess(wt2,x))/MathAbs(v)>threshold; //--- Optimality test for(k=0;k<=m;k++) { //--- check if(modeltype==1 && k==m) { //--- 0-type models (with non-zero constant term) //--- are tested for optimality of all coefficients. //--- 1-type models (with zero constant term) //--- are tested for optimality of non-constant terms only. continue; } //--- change values f=0; fp=0; fm=0; //--- calculation for(i=0;i<=n-1;i++) { v=tmpweights[m]; for(j=0;j<=m-1;j++) v=v+xy[i][j]*tmpweights[j]; f=f+CMath::Sqr((v-xy[i][m])/s[i]); //--- check if(kfp) || f>fm; } //--- Covariance matrix test: //--- generate random vector,project coefficients on it, //--- compare variance of projection with estimate provided //--- by cov.matrix ArrayResize(ta,estpasscount); ArrayResize(tb,m+1); ArrayResize(tc,m+1); xy2.Resize(n,m+1); for(i=0;i<=m;i++) tb[i]=GenerateNormal(0,1); //--- calculation for(epass=0;epass<=estpasscount-1;epass++) { for(i=0;i<=n-1;i++) { for(i_=0;i_<=m-1;i_++) xy2[i].Set(i_,xy[i][i_]); xy2[i].Set(m,xy0[i]+s[i]*GenerateNormal(0,1)); } //--- check if(modeltype==0) CLinReg::LRBuildS(xy2,s,n,m,info,wt,ar2); else CLinReg::LRBuildZS(xy2,s,n,m,info,wt,ar2); //--- check if(info!=1) { ta[epass]=0; grconverrors=true; continue; } //--- function call CLinReg::LRUnpack(wt,w2,tmpi); //--- change value v=0.0; for(i_=0;i_<=m;i_++) v+=tb[i_]*w2[i_]; ta[epass]=v; } //--- function call CalculateMV(ta,estpasscount,mean,means,stddev,stddevs); for(i=0;i<=m;i++) { //--- change value v=0.0; for(i_=0;i_<=m;i_++) v+=tb[i_]*ar.m_c[i_][i]; tc[i]=v; } //--- change value v=0.0; for(i_=0;i_<=m;i_++) v+=tc[i_]*tb[i_]; //--- search errors grcoverrors=grcoverrors || MathAbs((MathSqrt(v)-stddev)/stddevs)>=sigmathreshold; //--- Test for the fast CV error: //--- calculate CV error by definition (leaving out N //--- points and recalculating solution). //--- Test for the training set error cvrmserror=0; cvavgerror=0; cvavgrelerror=0; rmserror=0; avgerror=0; avgrelerror=0; xy2.Resize(n-1,m+1); ArrayResize(s2,n-1); //--- change values for(i=0;i<=n-2;i++) { for(i_=0;i_<=m;i_++) xy2[i].Set(i_,xy[i+1][i_]); s2[i]=s[i+1]; } //--- calculation for(i=0;i<=n-1;i++) { //--- Trn v=0.0; for(i_=0;i_<=m-1;i_++) v+=xy[i][i_]*tmpweights[i_]; v=v+tmpweights[m]; //--- search errors rmserror=rmserror+CMath::Sqr(v-xy[i][m]); avgerror=avgerror+MathAbs(v-xy[i][m]); avgrelerror=avgrelerror+MathAbs((v-xy[i][m])/xy[i][m]); //--- CV: non-defect vectors only nondefect=true; for(k=0;k<=ar.m_ncvdefects-1;k++) { //--- check if(ar.m_cvdefects[k]==i) nondefect=false; } //--- check if(nondefect) { //--- check if(modeltype==0) CLinReg::LRBuildS(xy2,s2,n-1,m,info2,wt,ar2); else CLinReg::LRBuildZS(xy2,s2,n-1,m,info2,wt,ar2); //--- check if(info2!=1) { grconverrors=true; continue; } //--- function call CLinReg::LRUnpack(wt,w2,tmpi); //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=xy[i][i_]*w2[i_]; v=v+w2[m]; //--- search errors cvrmserror=cvrmserror+CMath::Sqr(v-xy[i][m]); cvavgerror=cvavgerror+MathAbs(v-xy[i][m]); cvavgrelerror=cvavgrelerror+MathAbs((v-xy[i][m])/xy[i][m]); } //--- Next set if(i!=n-1) { for(i_=0;i_<=m;i_++) xy2[i].Set(i_,xy[i][i_]); s2[i]=s[i]; } } //--- search errors cvrmserror=MathSqrt(cvrmserror/(n-ar.m_ncvdefects)); cvavgerror=cvavgerror/(n-ar.m_ncvdefects); cvavgrelerror=cvavgrelerror/(n-ar.m_ncvdefects); rmserror=MathSqrt(rmserror/n); avgerror=avgerror/n; avgrelerror=avgrelerror/n; gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvrmserror/cvrmserror))>MathLog(1+1.0E-5); gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvavgerror/cvavgerror))>MathLog(1+1.0E-5); gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvavgrelerror/cvavgrelerror))>MathLog(1+1.0E-5); gresterrors=gresterrors || MathAbs(MathLog(ar.m_rmserror/rmserror))>MathLog(1+1.0E-5); gresterrors=gresterrors || MathAbs(MathLog(ar.m_avgerror/avgerror))>MathLog(1+1.0E-5); gresterrors=gresterrors || MathAbs(MathLog(ar.m_avgrelerror/avgrelerror))>MathLog(1+1.0E-5); } } } } //--- Additional subroutines for(pass=1;pass<=50;pass++) { n=2; //--- cycle do { noiselevel=CMath::RandomReal()+0.1; tasklevel=2*CMath::RandomReal()-1; } while(MathAbs(noiselevel-tasklevel)<=0.05); //--- allocation xy.Resize(3*n,2); //--- change values for(i=0;i<=n-1;i++) { xy[3*i+0].Set(0,i); xy[3*i+1].Set(0,i); xy[3*i+2].Set(0,i); xy[3*i+0].Set(1,tasklevel-noiselevel); xy[3*i+1].Set(1,tasklevel); xy[3*i+2].Set(1,tasklevel+noiselevel); } //--- function call CLinReg::LRBuild(xy,3*n,1,info,wt,ar); //--- check if(info==1) { //--- function calls CLinReg::LRUnpack(wt,tmpweights,tmpi); v=CLinReg::LRRMSError(wt,xy,3*n); //--- search errors grothererrors=grothererrors || MathAbs(v-noiselevel*MathSqrt(2.0/3.0))>threshold; //--- function call v=CLinReg::LRAvgError(wt,xy,3*n); //--- search errors grothererrors=grothererrors || MathAbs(v-noiselevel*(2.0/3.0))>threshold; //--- function call v=CLinReg::LRAvgRelError(wt,xy,3*n); vv=(MathAbs(noiselevel/(tasklevel-noiselevel))+MathAbs(noiselevel/(tasklevel+noiselevel)))/3; //--- search errors grothererrors=grothererrors || MathAbs(v-vv)>threshold*vv; } else grothererrors=true; //--- change values for(i=0;i<=n-1;i++) { xy[3*i+0].Set(0,i); xy[3*i+1].Set(0,i); xy[3*i+2].Set(0,i); xy[3*i+0].Set(1,-noiselevel); xy[3*i+1].Set(1,0); xy[3*i+2].Set(1,noiselevel); } //--- function call CLinReg::LRBuild(xy,3*n,1,info,wt,ar); //--- check if(info==1) { //--- function calls CLinReg::LRUnpack(wt,tmpweights,tmpi); v=CLinReg::LRAvgRelError(wt,xy,3*n); //--- search errors grothererrors=grothererrors || MathAbs(v-1)>threshold; } else grothererrors=true; } //--- calculation for(pass=1;pass<=10;pass++) { m=1+CMath::RandomInteger(5); n=10+CMath::RandomInteger(10); //--- allocation xy.Resize(n,m+1); for(i=0;i<=n-1;i++) { for(j=0;j<=m;j++) xy[i].Set(j,2*CMath::RandomReal()-1); } //--- function call CLinReg::LRBuild(xy,n,m,info,w,ar); //--- check if(info<0) { grothererrors=true; break; } //--- allocation ArrayResize(x1,m); ArrayResize(x2,m); //--- Same inputs on original leads to same outputs //--- on copy created using LRCopy UnsetLR(wt); CLinReg::LRCopy(w,wt); //--- change values for(i=0;i<=m-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } //--- change values y1=CLinReg::LRProcess(w,x1); y2=CLinReg::LRProcess(wt,x2); allsame=y1==y2; //--- search errors grothererrors=grothererrors || !allsame; } //--- TODO: Degenerate tests (when design matrix and right part are zero) //--- Final report waserrors=(((((slerrors || slcerrors) || gropterrors) || grcoverrors) || gresterrors) || grothererrors) || grconverrors; //--- check if(!silent) { Print("REGRESSION TEST"); Print("STRAIGHT LINE REGRESSION: "); //--- check if(!slerrors) Print("OK"); else Print("FAILED"); Print("STRAIGHT LINE REGRESSION CONVERGENCE: "); //--- check if(!slcerrors) Print("OK"); else Print("FAILED"); Print("GENERAL LINEAR REGRESSION: "); //--- check if(!((((gropterrors || grcoverrors) || gresterrors) || grothererrors) || grconverrors)) Print("OK"); else Print("FAILED"); Print("* OPTIMALITY: "); //--- check if(!gropterrors) Print("OK"); else Print("FAILED"); Print("* COV. MATRIX: "); //--- check if(!grcoverrors) Print("OK"); else Print("FAILED"); Print("* ERROR ESTIMATES: "); //--- check if(!gresterrors) Print("OK"); else Print("FAILED"); Print("* CONVERGENCE: "); //--- check if(!grconverrors) Print("OK"); else Print("FAILED"); Print("* OTHER SUBROUTINES: "); //--- check if(!grothererrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Task generation. Meaningless task,just random numbers. | //+------------------------------------------------------------------+ static void CTestLinRegUnit::GenerateRandomTask(const double xl,const double xr, const bool randomx,const double ymin, const double ymax,const double smin, const double smax,const int n, CMatrixDouble &xy,double &s[]) { //--- create a variable int i=0; //--- allocation xy.Resize(n,2); ArrayResize(s,n); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(randomx) xy[i].Set(0,xl+(xr-xl)*CMath::RandomReal()); else xy[i].Set(0,xl+(xr-xl)*i/(n-1)); //--- calculation xy[i].Set(1,ymin+(ymax-ymin)*CMath::RandomReal()); s[i]=smin+(smax-smin)*CMath::RandomReal(); } } //+------------------------------------------------------------------+ //| Task generation. | //+------------------------------------------------------------------+ static void CTestLinRegUnit::GenerateTask(const double a,const double b, const double xl,const double xr, const bool randomx,const double smin, const double smax,const int n, CMatrixDouble &xy,double &s[]) { //--- create a variable int i=0; //--- allocation xy.Resize(n,2); ArrayResize(s,n); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(randomx) xy[i].Set(0,xl+(xr-xl)*CMath::RandomReal()); else xy[i].Set(0,xl+(xr-xl)*i/(n-1)); //--- change values s[i]=smin+(smax-smin)*CMath::RandomReal(); xy[i].Set(1,a+b*xy[i][0]+GenerateNormal(0,s[i])); } } //+------------------------------------------------------------------+ //| Task generation. | //| y[i] are filled based on A,B,X[I],S[I] | //+------------------------------------------------------------------+ static void CTestLinRegUnit::FillTaskWithY(const double a,const double b, const int n,CMatrixDouble &xy, double &s[]) { //--- create variables int i=0; //--- change values for(i=0;i<=n-1;i++) xy[i].Set(1,a+b*xy[i][0]+GenerateNormal(0,s[i])); } //+------------------------------------------------------------------+ //| Normal random numbers | //+------------------------------------------------------------------+ static double CTestLinRegUnit::GenerateNormal(const double mean,const double sigma) { //--- create variables double result=0; double u=0; double v=0; double sum=0; //--- initialization result=mean; //--- calculation while(true) { //--- change values u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal(); v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal(); sum=u*u+v*v; //--- check if(sum<1.0 && sum>0.0) { sum=MathSqrt(-(2*MathLog(sum)/sum)); result=sigma*u*sum+mean; //--- return result return(result); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Moments estimates and their errors | //+------------------------------------------------------------------+ static void CTestLinRegUnit::CalculateMV(double &x[],const int n,double &mean, double &means,double &stddev, double &stddevs) { //--- create variables int i=0; double v1=0; double v2=0; double variance=0; //--- initialization mean=0; means=1; stddev=0; stddevs=1; variance=0; //--- check if(n<=1) return; //--- Mean for(i=0;i<=n-1;i++) mean=mean+x[i]; mean=mean/n; //--- Variance (using corrected two-pass algorithm) if(n!=1) { //--- change value v1=0; for(i=0;i<=n-1;i++) v1=v1+CMath::Sqr(x[i]-mean); //--- change value v2=0; for(i=0;i<=n-1;i++) v2=v2+(x[i]-mean); v2=CMath::Sqr(v2)/n; //--- calculation variance=(v1-v2)/(n-1); //--- check if(variance<0.0) variance=0; stddev=MathSqrt(variance); } //--- Errors means=stddev/MathSqrt(n); stddevs=stddev*MathSqrt(2)/MathSqrt(n-1); } //+------------------------------------------------------------------+ //| Unsets LR | //+------------------------------------------------------------------+ static void CTestLinRegUnit::UnsetLR(CLinearModel &lr) { //--- create variables int info=0; int i=0; //--- create matrix CMatrixDouble xy; //--- object of class CLRReport rep; //--- allocation xy.Resize(6,2); //--- change values for(i=0;i<=5;i++) { xy[i].Set(0,0); xy[i].Set(1,0); } //--- function call CLinReg::LRBuild(xy,6,1,info,lr,rep); //--- check if(!CAp::Assert(info>0)) return; } //+------------------------------------------------------------------+ //| Testing class CXblas | //+------------------------------------------------------------------+ class CTestXBlasUnit { public: //--- constructor, destructor CTestXBlasUnit(void); ~CTestXBlasUnit(void); //--- public method static bool TestXBlas(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestXBlasUnit::CTestXBlasUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestXBlasUnit::~CTestXBlasUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CXblas | //+------------------------------------------------------------------+ static bool CTestXBlasUnit::TestXBlas(const bool silent) { //--- create variables bool approxerrors; bool exactnesserrors; bool waserrors; double approxthreshold=0; int maxn=0; int passcount=0; int n=0; int i=0; int pass=0; double rv1=0; double rv2=0; double rv2err=0; complex cv1=0; complex cv2=0; double cv2err=0; double s=0; int i_=0; //--- create arrays double rx[]; double ry[]; complex cx[]; complex cy[]; double temp[]; //--- initialization approxerrors=false; exactnesserrors=false; waserrors=false; approxthreshold=1000*CMath::m_machineepsilon; maxn=1000; passcount=10; //--- tests: //--- 1. ability to calculate dot product //--- 2. higher precision for(n=1;n<=maxn;n++) { for(pass=1;pass<=passcount;pass++) { //--- ability to approximately calculate real dot product ArrayResize(rx,n); ArrayResize(ry,n); ArrayResize(temp,n); //--- change values for(i=0;i<=n-1;i++) { //--- check if(CMath::RandomReal()>0.2) rx[i]=2*CMath::RandomReal()-1; else rx[i]=0; //--- check if(CMath::RandomReal()>0.2) ry[i]=2*CMath::RandomReal()-1; else ry[i]=0; } //--- change value rv1=0.0; for(i_=0;i_<=n-1;i_++) rv1+=rx[i_]*ry[i_]; //--- function call CXblas::XDot(rx,ry,n,temp,rv2,rv2err); //--- search errors approxerrors=approxerrors || MathAbs(rv1-rv2)>approxthreshold; //--- ability to approximately calculate complex dot product ArrayResize(cx,n); ArrayResize(cy,n); ArrayResize(temp,2*n); //--- change values for(i=0;i<=n-1;i++) { //--- check if(CMath::RandomReal()>0.2) { cx[i].re=2*CMath::RandomReal()-1; cx[i].im=2*CMath::RandomReal()-1; } else cx[i]=0; //--- check if(CMath::RandomReal()>0.2) { cy[i].re=2*CMath::RandomReal()-1; cy[i].im=2*CMath::RandomReal()-1; } else cy[i]=0; } //--- change value cv1=0.0; for(i_=0;i_<=n-1;i_++) cv1+=cx[i_]*cy[i_]; //--- function call CXblas::XCDot(cx,cy,n,temp,cv2,cv2err); //--- search errors approxerrors=approxerrors || CMath::AbsComplex(cv1-cv2)>approxthreshold; } } //--- test of precision: real n=50000; ArrayResize(rx,n); ArrayResize(ry,n); ArrayResize(temp,n); //--- calculation for(pass=0;pass<=passcount-1;pass++) { //--- check if(!CAp::Assert(n%2==0)) return(false); //--- First test: X + X + ... + X - X - X - ... - X=1*X s=MathExp(MathMax(pass,50)); //--- check if(pass==passcount-1 && pass>1) s=1E300; //--- change values ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); for(i=1;i<=n-1;i++) ry[i]=ry[0]; for(i=0;i<=n/2-1;i++) rx[i]=1; for(i=n/2;i<=n-2;i++) rx[i]=-1; rx[n-1]=0; //--- function call CXblas::XDot(rx,ry,n,temp,rv2,rv2err); //--- search errors exactnesserrors=exactnesserrors || rv2err<0.0; exactnesserrors=exactnesserrors || rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0]); exactnesserrors=exactnesserrors || MathAbs(rv2-ry[0])>rv2err; //--- First test: X + X + ... + X=N*X s=MathExp(MathMax(pass,50)); //--- check if(pass==passcount-1 && pass>1) s=1E300; //--- change values ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); for(i=1;i<=n-1;i++) ry[i]=ry[0]; for(i=0;i<=n-1;i++) rx[i]=1; //--- function call CXblas::XDot(rx,ry,n,temp,rv2,rv2err); //--- search errors exactnesserrors=exactnesserrors || rv2err<0.0; exactnesserrors=exactnesserrors || rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0])*n; exactnesserrors=exactnesserrors || MathAbs(rv2-n*ry[0])>rv2err; } //--- test of precision: complex n=50000; ArrayResize(cx,n); ArrayResize(cy,n); ArrayResize(temp,2*n); //--- calculation for(pass=0;pass<=passcount-1;pass++) { //--- check if(!CAp::Assert(n%2==0)) return(false); //--- First test: X + X + ... + X - X - X - ... - X=1*X s=MathExp(MathMax(pass,50)); //--- check if(pass==passcount-1 && pass>1) s=1E300; //--- change values cy[0].re=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); cy[0].im=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); for(i=1;i<=n-1;i++) cy[i]=cy[0]; for(i=0;i<=n/2-1;i++) cx[i]=1; for(i=n/2;i<=n-2;i++) cx[i]=-1; cx[n-1]=0; //--- function call CXblas::XCDot(cx,cy,n,temp,cv2,cv2err); //--- search errors exactnesserrors=exactnesserrors || cv2err<0.0; exactnesserrors=exactnesserrors || cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0]); exactnesserrors=exactnesserrors || CMath::AbsComplex(cv2-cy[0])>cv2err; //--- First test: X + X + ... + X=N*X s=MathExp(MathMax(pass,50)); //--- check if(pass==passcount-1 && pass>1) s=1E300; //--- change values cy[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); for(i=1;i<=n-1;i++) cy[i]=cy[0]; for(i=0;i<=n-1;i++) cx[i]=1; //--- function call CXblas::XCDot(cx,cy,n,temp,cv2,cv2err); //--- search errors exactnesserrors=exactnesserrors || cv2err<0.0; exactnesserrors=exactnesserrors || cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0])*n; exactnesserrors=exactnesserrors || CMath::AbsComplex(cv2-cy[0]*n)>cv2err; } //--- report waserrors=approxerrors || exactnesserrors; //--- check if(!silent) { Print("TESTING XBLAS"); Print("APPROX.TESTS: "); //--- check if(approxerrors) Print("FAILED"); else Print("OK"); Print("EXACT TESTS: "); //--- check if(exactnesserrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CDenseSolver | //+------------------------------------------------------------------+ class CTestDenseSolverUnit { private: //--- private methods static bool RMatrixCheckSolutionM(CMatrixDouble &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixDouble &xs); static bool RMatrixCheckSolution(CMatrixDouble &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,double &xs[]); static bool RMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixDouble &xs); static bool RMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,double &xs[]); static bool CMatrixCheckSolutionM(CMatrixComplex &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixComplex &xs); static bool CMatrixCheckSolution(CMatrixComplex &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,complex &xs[]); static bool CMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixComplex &xs); static bool CMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,complex &xs[]); static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower); static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower); static void TestRSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &rerrors,bool &rfserrors); static void TestSPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &spderrors,bool &rfserrors); static void TestCSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &cerrors,bool &rfserrors); static void TestHPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &hpderrors,bool &rfserrors); static void Unset2D(CMatrixDouble &x); static void Unset1D(double &x[]); static void CUnset2D(CMatrixComplex &x); static void CUnset1D(complex &x[]); static void UnsetRep(CDenseSolverReport &r); static void UnsetLSRep(CDenseSolverLSReport &r); public: //--- constructor, destructor CTestDenseSolverUnit(void); ~CTestDenseSolverUnit(void); //--- public method static bool TestDenseSolver(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestDenseSolverUnit::CTestDenseSolverUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestDenseSolverUnit::~CTestDenseSolverUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::TestDenseSolver(const bool silent) { //--- create variables int maxn=0; int maxm=0; int passcount=0; double threshold=0; bool rerrors; bool cerrors; bool spderrors; bool hpderrors; bool rfserrors; bool waserrors; //--- initialization maxn=10; maxm=5; passcount=5; threshold=10000*CMath::m_machineepsilon; rfserrors=false; rerrors=false; cerrors=false; spderrors=false; hpderrors=false; //--- function calls TestRSolver(maxn,maxm,passcount,threshold,rerrors,rfserrors); TestSPDSolver(maxn,maxm,passcount,threshold,spderrors,rfserrors); TestCSolver(maxn,maxm,passcount,threshold,cerrors,rfserrors); TestHPDSolver(maxn,maxm,passcount,threshold,hpderrors,rfserrors); //--- search errors waserrors=(((rerrors || cerrors) || spderrors) || hpderrors) || rfserrors; //--- check if(!silent) { Print("TESTING DENSE SOLVER"); Print("* REAL: "); //--- check if(rerrors) Print("FAILED"); else Print("OK"); Print("* COMPLEX: "); //--- check if(cerrors) Print("FAILED"); else Print("OK"); Print("* SPD: "); //--- check if(spderrors) Print("FAILED"); else Print("OK"); Print("* HPD: "); //--- check if(hpderrors) Print("FAILED"); else Print("OK"); Print("* ITERATIVE IMPROVEMENT: "); //--- check if(rfserrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Checks whether solver results are correct solution. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::RMatrixCheckSolutionM(CMatrixDouble &xe, const int n,const int m, const double threshold, const int info, CDenseSolverReport &rep, CMatrixDouble &xs) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=true; //--- check if(info<=0) result=false; else { //--- calculation result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) result=result && MathAbs(xe[i][j]-xs[i][j])<=threshold; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether solver results are correct solution. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::RMatrixCheckSolution(CMatrixDouble &xe, const int n, const double threshold, const int info, CDenseSolverReport &rep, double &xs[]) { //--- create a variable int i_=0; //--- create matrix CMatrixDouble xsm; //--- allocation xsm.Resize(n,1); //--- change values for(i_=0;i_<=n-1;i_++) xsm[i_].Set(0,xs[i_]); //--- return result return(RMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm)); } //+------------------------------------------------------------------+ //| Checks whether solver results indicate singular matrix. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::RMatrixCheckSingularM(const int n, const int m, const int info, CDenseSolverReport &rep, CMatrixDouble &xs) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=true; //--- check if(info!=-3 && info!=1) result=false; else { //--- calculation result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) result=result && xs[i][j]==0.0; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether solver results indicate singular matrix. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::RMatrixCheckSingular(const int n, const int info, CDenseSolverReport &rep, double &xs[]) { //--- create a variable int i_=0; //--- create matrix CMatrixDouble xsm; //--- allocation xsm.Resize(n,1); //--- change values for(i_=0;i_<=n-1;i_++) xsm[i_].Set(0,xs[i_]); //--- return result return(RMatrixCheckSingularM(n,1,info,rep,xsm)); } //+------------------------------------------------------------------+ //| Checks whether solver results are correct solution. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::CMatrixCheckSolutionM(CMatrixComplex &xe, const int n,const int m, const double threshold, const int info, CDenseSolverReport &rep, CMatrixComplex &xs) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=true; //--- check if(info<=0) result=false; else { //--- calculation result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) result=result && CMath::AbsComplex(xe[i][j]-xs[i][j])<=threshold; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether solver results are correct solution. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::CMatrixCheckSolution(CMatrixComplex &xe, const int n, const double threshold, const int info, CDenseSolverReport &rep, complex &xs[]) { //--- create a variable int i_=0; //--- create matrix CMatrixComplex xsm; //--- allocation xsm.Resize(n,1); //--- change values for(i_=0;i_<=n-1;i_++) xsm[i_].Set(0,xs[i_]); //--- return result return(CMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm)); } //+------------------------------------------------------------------+ //| Checks whether solver results indicate singular matrix. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::CMatrixCheckSingularM(const int n, const int m, const int info, CDenseSolverReport &rep, CMatrixComplex &xs) { //--- create variables bool result; int i=0; int j=0; //--- initialization result=true; //--- check if(info!=-3 && info!=1) result=false; else { //--- calculation result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) result=result && xs[i][j]==0; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Checks whether solver results indicate singular matrix. | //| Returns True on success. | //+------------------------------------------------------------------+ static bool CTestDenseSolverUnit::CMatrixCheckSingular(const int n, const int info, CDenseSolverReport &rep, complex &xs[]) { //--- create variables int i_=0; //--- create matrix CMatrixComplex xsm; //--- allocation xsm.Resize(n,1); //--- change values for(i_=0;i_<=n-1;i_++) xsm[i_].Set(0,xs[i_]); //--- return result return(CMatrixCheckSingularM(n,1,info,rep,xsm)); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::RMatrixMakeACopy(CMatrixDouble &a, const int m, const int n, CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::CMatrixMakeACopy(CMatrixComplex &a, const int m, const int n, CMatrixComplex &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| Drops upper or lower half of the matrix - fills it by special | //| pattern which may be used later to ensure that this part wasn't | //| changed | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::RMatrixDropHalf(CMatrixDouble &a, const int n, const bool droplower) { //--- create variables int i=0; int j=0; //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if((droplower && i>j) || (!droplower && ij) || (!droplower && i0.5,info,rep,x); //--- search errors rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change value info=0; //--- function calls UnsetRep(rep); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv); //--- search errors rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- change value info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x); //--- search errors rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change value info=0; //--- function calls UnsetRep(rep); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv); //--- search errors rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- change value info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x); //--- search errors rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change value info=0; //--- function calls UnsetRep(rep); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv); //--- search errors rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- Test DenseSolverRLS(): //--- * test on original system A*x=b //--- * test on overdetermined system with the same solution: (A' A')'*x=(b' b')' //--- * test on underdetermined system with the same solution: (A 0 0 0 ) * z=b info=0; UnsetLSRep(repls); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv); //--- check if(info<=0) rerrors=true; else { //--- search errors rerrors=(rerrors || repls.m_r2<100*CMath::m_machineepsilon) || repls.m_r2>1+1000*CMath::m_machineepsilon; rerrors=(rerrors || repls.m_n!=n) || repls.m_k!=0; for(i=0;i<=n-1;i++) rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold; } //--- change value info=0; //--- function calls UnsetLSRep(repls); Unset1D(xv); //--- allocation ArrayResize(bv,2*n); for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- change value i1_=-n; for(i_=n;i_<=2*n-1;i_++) bv[i_]=b[i_+i1_][0]; //--- allocation atmp.Resize(2*n,n); //--- function calls CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1); CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,n,2*n-1,0,n-1); CDenseSolver::RMatrixSolveLS(atmp,2*n,n,bv,0.0,info,repls,xv); //--- check if(info<=0) rerrors=true; else { //--- search errors rerrors=(rerrors || repls.m_r2<100*CMath::m_machineepsilon) || repls.m_r2>1+1000*CMath::m_machineepsilon; rerrors=(rerrors || repls.m_n!=n) || repls.m_k!=0; for(i=0;i<=n-1;i++) rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold; } //--- change value info=0; //--- function calls UnsetLSRep(repls); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- allocation atmp.Resize(n,2*n); CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1); for(i=0;i<=n-1;i++) { for(j=n;j<=2*n-1;j++) atmp[i].Set(j,0); } //--- function call CDenseSolver::RMatrixSolveLS(atmp,n,2*n,bv,0.0,info,repls,xv); //--- check if(info<=0) rerrors=true; else { //--- search errors rerrors=rerrors || repls.m_r2!=0.0; rerrors=(rerrors || repls.m_n!=2*n) || repls.m_k!=n; for(i=0;i<=n-1;i++) rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold; for(i=n;i<=2*n-1;i++) rerrors=rerrors || MathAbs(xv[i])>threshold; } //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods // for(taskkind=0;taskkind<=4;taskkind++) { Unset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,0*a[i_][k]); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,0*a[k][i_]); } //--- check if(taskkind==3) { //--- equal columns if(n<2) continue; //--- change values a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[i_].Set(0,a[i_][k]); } //--- check if(taskkind==4) { //--- equal rows if(n<2) continue; //--- change values a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[0].Set(i_,a[k][i_]); } //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) xe[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- function calls RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); //--- Test RMatrixSolveM() info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x); //--- search errors rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x); //--- Test RMatrixSolve() info=0; //--- function calls UnsetRep(rep); Unset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv); //--- search errors rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv); //--- Test RMatrixLUSolveM() info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x); //--- search errors rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x); //--- Test RMatrixLUSolve() info=0; //--- function calls UnsetRep(rep); Unset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv); //--- search errors rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv); //--- Test RMatrixMixedSolveM() info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x); //--- search errors rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x); //--- Test RMatrixMixedSolve() info=0; //--- function calls UnsetRep(rep); Unset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv); //--- search errors rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv); } } } } //--- test iterative improvement for(pass=1;pass<=passcount;pass++) { //--- Test iterative improvement matrices //--- A matrix/right part are constructed such that both matrix //--- and solution components are within (-1,+1). Such matrix/right part //--- have nice properties - system can be solved using iterative //--- improvement with ||A*x-b|| about several ulps of max(1,||b||). n=100; a.Resize(n,n); b.Resize(n,1); ArrayResize(bv,n); ArrayResize(tx,n); ArrayResize(xv,n); ArrayResize(y,n); //--- change values for(i=0;i<=n-1;i++) xv[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XDot(y,xv,n,tx,v,verr); bv[i]=v; } //--- change values for(i_=0;i_<=n-1;i_++) b[i_].Set(0,bv[i_]); //--- Test RMatrixSolveM() Unset2D(x); CDenseSolver::RMatrixSolveM(a,n,b,1,true,info,rep,x); //--- check if(info<=0) rfserrors=true; else { //--- allocation ArrayResize(xv,n); for(i_=0;i_<=n-1;i_++) xv[i_]=x[i_][0]; for(i=0;i<=n-1;i++) { for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XDot(y,xv,n,tx,v,verr); //--- search errors rfserrors=rfserrors || MathAbs(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(b[i][0])); } } //--- Test RMatrixSolve() Unset1D(xv); CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv); //--- check if(info<=0) rfserrors=true; else { for(i=0;i<=n-1;i++) { for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XDot(y,xv,n,tx,v,verr); //--- search errors rfserrors=rfserrors || MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i])); } } //--- Test LS-solver on the same matrix CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv); //--- check if(info<=0) rfserrors=true; else { for(i=0;i<=n-1;i++) { for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XDot(y,xv,n,tx,v,verr); //--- search errors rfserrors=rfserrors || MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i])); } } } } //+------------------------------------------------------------------+ //| SPD test | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::TestSPDSolver(const int maxn,const int maxm, const int passcount, const double threshold, bool &spderrors,bool &rfserrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int m=0; int pass=0; int taskkind=0; double v=0; bool isupper; int info=0; int i_=0; //--- create arrays int p[]; double bv[]; double xv[]; double y[]; double tx[]; //--- create matrix CMatrixDouble a; CMatrixDouble cha; CMatrixDouble atmp; CMatrixDouble xe; CMatrixDouble b; CMatrixDouble x; //--- objects of class CDenseSolverReport rep; CDenseSolverLSReport repls; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { for(m=1;m<=maxm;m++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A isupper=CMath::RandomReal()>0.5; CMatGen::SPDMatrixRndCond(n,1000,a); RMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::SPDMatrixCholesky(cha,n,isupper)) { spderrors=true; return; } //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) xe[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- function calls RMatrixDropHalf(a,n,isupper); RMatrixDropHalf(cha,n,isupper); //--- Test solvers info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x); //--- search errors spderrors=spderrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change value info=0; //--- function calls UnsetRep(rep); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv); //--- search errors spderrors=spderrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- change value info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x); //--- search errors spderrors=spderrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change value info=0; //--- function calls UnsetRep(rep); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv); //--- search errors spderrors=spderrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods for(taskkind=0;taskkind<=3;taskkind++) { Unset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { a[i].Set(j,2*CMath::RandomReal()-1); a[j].Set(i,a[i][j]); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,0*a[i_][k]); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,0*a[k][i_]); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { a[i].Set(j,2*CMath::RandomReal()-1); a[j].Set(i,a[i][j]); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,0*a[k][i_]); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,0*a[i_][k]); } //--- check if(taskkind==3) { //--- equal columns/rows if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { a[i].Set(j,2*CMath::RandomReal()-1); a[j].Set(i,a[i][j]); } } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[i_].Set(0,a[i_][k]); for(i_=0;i_<=n-1;i_++) a[0].Set(i_,a[k][i_]); } //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) xe[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- function calls RMatrixMakeACopy(a,n,n,cha); RMatrixDropHalf(a,n,isupper); RMatrixDropHalf(cha,n,isupper); //--- Test SPDMatrixSolveM() info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x); //--- search errors spderrors=spderrors || !RMatrixCheckSingularM(n,m,info,rep,x); //--- Test SPDMatrixSolve() info=0; UnsetRep(rep); Unset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv); //--- search errors spderrors=spderrors || !RMatrixCheckSingular(n,info,rep,xv); //--- 'equal columns/rows' are degenerate,but //--- Cholesky matrix with equal columns/rows IS NOT degenerate, //--- so it is not used for testing purposes. if(taskkind!=3) { //--- Test SPDMatrixLUSolveM() info=0; //--- function calls UnsetRep(rep); Unset2D(x); CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x); //--- search errors spderrors=spderrors || !RMatrixCheckSingularM(n,m,info,rep,x); //--- Test SPDMatrixLUSolve() info=0; UnsetRep(rep); Unset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv); //--- search errors spderrors=spderrors || !RMatrixCheckSingular(n,info,rep,xv); } } } } } } //+------------------------------------------------------------------+ //| Real test | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::TestCSolver(const int maxn,const int maxm, const int passcount, const double threshold, bool &cerrors,bool &rfserrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int m=0; int pass=0; int taskkind=0; double verr=0; complex v=0; int info=0; int i_=0; //--- create arrays int p[]; complex bv[]; complex xv[]; complex y[]; double tx[]; //--- create matrix CMatrixComplex a; CMatrixComplex lua; CMatrixComplex atmp; CMatrixComplex xe; CMatrixComplex b; CMatrixComplex x; //--- objects of classes CDenseSolverReport rep; CDenseSolverLSReport repls; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { for(m=1;m<=maxm;m++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::CMatrixRndCond(n,1000,a); CMatrixMakeACopy(a,n,n,lua); CTrFac::CMatrixLU(lua,n,n,p); //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { xe[i].SetRe(j,2*CMath::RandomReal()-1); xe[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- Test solvers info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x); //--- search errors cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv); //--- search errors cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x); //--- search errors cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv); //--- search errors cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x); //--- search errors cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv); //--- search errors cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods for(taskkind=0;taskkind<=4;taskkind++) { CUnset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,a[i_][k]*0); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,a[k][i_]*0); } //--- check if(taskkind==3) { //--- equal columns if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[i_].Set(0,a[i_][k]); } //--- check if(taskkind==4) { //--- equal rows if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[0].Set(i_,a[k][i_]); } //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) xe[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- function calls CMatrixMakeACopy(a,n,n,lua); CTrFac::CMatrixLU(lua,n,n,p); //--- Test CMatrixSolveM() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x); //--- search errors cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x); //--- Test CMatrixSolve() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv); //--- search errors cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv); //--- Test CMatrixLUSolveM() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x); //--- search errors cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x); //--- Test CMatrixLUSolve() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv); //--- search errors cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv); //--- Test CMatrixMixedSolveM() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x); //--- search errors cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x); //--- Test CMatrixMixedSolve() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv); //--- search errors cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv); } } } } //--- test iterative improvement for(pass=1;pass<=passcount;pass++) { //--- Test iterative improvement matrices //--- A matrix/right part are constructed such that both matrix //--- and solution components magnitudes are within (-1,+1). //--- Such matrix/right part have nice properties - system can //--- be solved using iterative improvement with ||A*x-b|| about //--- several ulps of max(1,||b||). n=100; a.Resize(n,n); b.Resize(n,1); ArrayResize(bv,n); ArrayResize(tx,2*n); ArrayResize(xv,n); ArrayResize(y,n); //--- change values for(i=0;i<=n-1;i++) { xv[i].re=2*CMath::RandomReal()-1; xv[i].im=2*CMath::RandomReal()-1; } for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); } for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XCDot(y,xv,n,tx,v,verr); bv[i]=v; } for(i_=0;i_<=n-1;i_++) b[i_].Set(0,bv[i_]); //--- Test CMatrixSolveM() CUnset2D(x); CDenseSolver::CMatrixSolveM(a,n,b,1,true,info,rep,x); //--- check if(info<=0) rfserrors=true; else { //--- allocation ArrayResize(xv,n); for(i_=0;i_<=n-1;i_++) xv[i_]=x[i_][0]; for(i=0;i<=n-1;i++) { for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XCDot(y,xv,n,tx,v,verr); //--- search errors rfserrors=rfserrors || CMath::AbsComplex(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(b[i][0])); } } //--- Test CMatrixSolve() CUnset1D(xv); CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv); //--- check if(info<=0) rfserrors=true; else { for(i=0;i<=n-1;i++) { for(i_=0;i_<=n-1;i_++) y[i_]=a[i][i_]; //--- function call CXblas::XCDot(y,xv,n,tx,v,verr); //--- search errors rfserrors=rfserrors || CMath::AbsComplex(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(bv[i])); } } //--- TODO: Test LS-solver on the same matrix } } //+------------------------------------------------------------------+ //| HPD test | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::TestHPDSolver(const int maxn,const int maxm, const int passcount, const double threshold, bool &hpderrors,bool &rfserrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int m=0; int pass=0; int taskkind=0; complex v=0; bool isupper; int info=0; int i_=0; //--- create arrays int p[]; complex bv[]; complex xv[]; complex y[]; complex tx[]; //--- create matrix CMatrixComplex a; CMatrixComplex cha; CMatrixComplex atmp; CMatrixComplex xe; CMatrixComplex b; CMatrixComplex x; //--- objects of classes CDenseSolverReport rep; CDenseSolverLSReport repls; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { for(m=1;m<=maxm;m++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A isupper=CMath::RandomReal()>0.5; CMatGen::HPDMatrixRndCond(n,1000,a); CMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::HPDMatrixCholesky(cha,n,isupper)) { hpderrors=true; return; } //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { xe[i].SetRe(j,2*CMath::RandomReal()-1); xe[i].SetIm(j,2*CMath::RandomReal()-1); } } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- function calls CMatrixDropHalf(a,n,isupper); CMatrixDropHalf(cha,n,isupper); //--- Test solvers info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x); //--- search errors hpderrors=hpderrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv); //--- search errors hpderrors=hpderrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x); //--- search errors hpderrors=hpderrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x); //--- change values info=0; //--- function calls UnsetRep(rep); CUnset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv); //--- search errors hpderrors=hpderrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods for(taskkind=0;taskkind<=3;taskkind++) { CUnset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,0); } } //--- check if(taskkind==1) { //--- there is zero column a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); //--- check if(i==j) a[i].SetIm(j,0); a[j].Set(i,a[i][j]); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,a[i_][k]*0); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,a[k][i_]*0); } //--- check if(taskkind==2) { //--- there is zero row a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); //--- check if(i==j) a[i].SetIm(j,0); a[j].Set(i,a[i][j]); } } //--- change values k=CMath::RandomInteger(n); for(i_=0;i_<=n-1;i_++) a[k].Set(i_,a[k][i_]*0); for(i_=0;i_<=n-1;i_++) a[i_].Set(k,a[i_][k]*0); } //--- check if(taskkind==3) { //--- equal columns/rows if(n<2) continue; //--- allocation a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=i;j<=n-1;j++) { a[i].SetRe(j,2*CMath::RandomReal()-1); a[i].SetIm(j,2*CMath::RandomReal()-1); //--- check if(i==j) a[i].SetIm(j,0); a[j].Set(i,a[i][j]); } } //--- change values k=1+CMath::RandomInteger(n-1); for(i_=0;i_<=n-1;i_++) a[i_].Set(0,a[i_][k]); for(i_=0;i_<=n-1;i_++) a[0].Set(i_,a[k][i_]); } //--- allocation xe.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) xe[i].Set(j,2*CMath::RandomReal()-1); } //--- allocation b.Resize(n,m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_][j]; b[i].Set(j,v); } } //--- function calls CMatrixMakeACopy(a,n,n,cha); CMatrixDropHalf(a,n,isupper); CMatrixDropHalf(cha,n,isupper); //--- Test SPDMatrixSolveM() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x); //--- search errors hpderrors=hpderrors || !CMatrixCheckSingularM(n,m,info,rep,x); //--- Test SPDMatrixSolve() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv); //--- search errors hpderrors=hpderrors || !CMatrixCheckSingular(n,info,rep,xv); //--- 'equal columns/rows' are degenerate,but //--- Cholesky matrix with equal columns/rows IS NOT degenerate, //--- so it is not used for testing purposes. if(taskkind!=3) { //--- Test SPDMatrixLUSolveM() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x); //--- search errors hpderrors=hpderrors || !CMatrixCheckSingularM(n,m,info,rep,x); //--- Test SPDMatrixLUSolve() info=0; //--- function calls UnsetRep(rep); CUnset2D(x); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0;i_<=n-1;i_++) bv[i_]=b[i_][0]; //--- function call CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv); //--- search errors hpderrors=hpderrors || !CMatrixCheckSingular(n,info,rep,xv); } } } } } } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::Unset2D(CMatrixDouble &x) { //--- allocation x.Resize(1,1); //--- change value x[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::CUnset2D(CMatrixComplex &x) { //--- allocation x.Resize(1,1); //--- change value x[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::CUnset1D(complex &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets report | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::UnsetRep(CDenseSolverReport &r) { //--- change values r.m_r1=-1; r.m_rinf=-1; } //+------------------------------------------------------------------+ //| Unsets report | //+------------------------------------------------------------------+ static void CTestDenseSolverUnit::UnsetLSRep(CDenseSolverLSReport &r) { //--- change values r.m_r2=-1; r.m_n=-1; r.m_k=-1; //--- function call Unset2D(r.m_cx); } //+------------------------------------------------------------------+ //| Testing class CLinMin | //+------------------------------------------------------------------+ class CTestLinMinUnit { public: static bool TestLinMin(const bool silent) { //--- create variables bool waserrors; //--- initialization waserrors=false; //--- check if(!silent) { Print("TESTING LINMIN"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } }; //+------------------------------------------------------------------+ //| Testing class CMinCG | //+------------------------------------------------------------------+ class CTestMinCGUnit { private: //--- private methods static void TestFunc1(CMinCGState &state); static void TestFunc2(CMinCGState &state); static void TestFunc3(CMinCGState &state); static void CalcIIP2(CMinCGState &state,const int n); static void CalcLowRank(CMinCGState &state,const int n,const int vcnt,double &d[],CMatrixDouble &v,double &vd[],double &x0[]); static void TestPreconditioning(bool &err); public: //--- constructor, destructor CTestMinCGUnit(void); ~CTestMinCGUnit(void); //--- public methods static bool TestMinCG(const bool silent); static void TestOther(bool &err); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMinCGUnit::CTestMinCGUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMinCGUnit::~CTestMinCGUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CMinCG | //+------------------------------------------------------------------+ static bool CTestMinCGUnit::TestMinCG(const bool silent) { //--- create variables bool waserrors; bool referror; bool eqerror; bool linerror1; bool linerror2; bool restartserror; bool precerror; bool converror; bool othererrors; int n=0; int i=0; int j=0; double v=0; int cgtype=0; int difftype=0; double diffstep=0; int i_=0; //--- create arrays double x[]; double xe[]; double b[]; double xlast[]; double diagh[]; //--- create matrix CMatrixDouble a; //--- objects of classes CMinCGState state; CMinCGReport rep; //--- initialization waserrors=false; referror=false; linerror1=false; linerror2=false; eqerror=false; converror=false; restartserror=false; othererrors=false; precerror=false; //--- function calls TestPreconditioning(precerror); TestOther(othererrors); //--- calculation for(difftype=0;difftype<=1;difftype++) { for(cgtype=-1;cgtype<=1;cgtype++) { //--- Reference problem ArrayResize(x,3); //--- change values n=3; diffstep=1.0E-6; x[0]=100*CMath::RandomReal()-50; x[1]=100*CMath::RandomReal()-50; x[2]=100*CMath::RandomReal()-50; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors referror=(((referror || rep.m_terminationtype<=0) || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001; //--- F2 problem with restarts: //--- * make several iterations and restart BEFORE termination //--- * iterate and restart AFTER termination //--- NOTE: step is bounded from above to avoid premature convergence ArrayResize(x,3); //--- change values n=3; diffstep=1.0E-6; x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function calls CMinCG::MinCGSetCGType(state,cgtype); CMinCG::MinCGSetStpMax(state,0.1); CMinCG::MinCGSetCond(state,0.0000001,0.0,0.0,0); //--- calculation for(i=0;i<=10;i++) { //--- check if(!CMinCG::MinCGIteration(state)) break; //--- function call TestFunc2(state); } //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc2(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01; //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc2(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01; //--- 1D problem #1 ArrayResize(x,1); //--- change values n=1; diffstep=1.0E-6; x[0]=100*CMath::RandomReal()-50; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=-MathCos(state.m_x[0]); //--- check if(state.m_needfg) state.m_g[0]=MathSin(state.m_x[0]); } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors linerror1=(linerror1 || rep.m_terminationtype<=0) || MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001; //--- 1D problem #2 ArrayResize(x,1); //--- change values n=1; diffstep=1.0E-6; x[0]=100*CMath::RandomReal()-50; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0])); //--- check if(state.m_needfg) state.m_g[0]=(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0])); } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors linerror2=(linerror2 || rep.m_terminationtype<=0) || MathAbs(x[0])>0.001; //--- Linear equations diffstep=1.0E-6; for(n=1;n<=10;n++) { //--- Prepare task a.Resize(n,n); ArrayResize(x,n); ArrayResize(xe,n); ArrayResize(b,n); for(i=0;i<=n-1;i++) xe[i]=2*CMath::RandomReal()-1; //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); a[i].Set(i,a[i][i]+3*MathSign(a[i][i])); } for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_]; b[i]=v; } //--- Solve task for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- check if(state.m_needfg) { for(i=0;i<=n-1;i++) state.m_g[i]=0; } for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*state.m_x[i_]; //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr(v-b[i]); //--- check if(state.m_needfg) { for(j=0;j<=n-1;j++) state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j]; } } } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors eqerror=eqerror || rep.m_terminationtype<=0; for(i=0;i<=n-1;i++) eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001; } //--- Testing convergence properties diffstep=1.0E-6; n=3; //--- allocation ArrayResize(x,n); for(i=0;i<=n-1;i++) x[i]=6*CMath::RandomReal()-3; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function calls CMinCG::MinCGSetCond(state,0.001,0.0,0.0,0); CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc3(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors converror=converror || rep.m_terminationtype!=4; for(i=0;i<=n-1;i++) x[i]=6*CMath::RandomReal()-3; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function calls CMinCG::MinCGSetCond(state,0.0,0.001,0.0,0); CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc3(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors converror=converror || rep.m_terminationtype!=1; //--- change values for(i=0;i<=n-1;i++) x[i]=6*CMath::RandomReal()-3; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function calls CMinCG::MinCGSetCond(state,0.0,0.0,0.001,0); CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc3(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors converror=converror || rep.m_terminationtype!=2; //--- change values for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function calls CMinCG::MinCGSetCond(state,0.0,0.0,0.0,10); CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc3(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors converror=converror || !((rep.m_terminationtype==5 && rep.m_iterationscount==10) || rep.m_terminationtype==7); } } //--- end waserrors=((((((referror || eqerror) || linerror1) || linerror2) || converror) || othererrors) || restartserror) || precerror; //--- check if(!silent) { Print("TESTING CG OPTIMIZATION"); Print("REFERENCE PROBLEM: "); //--- check if(referror) Print("FAILED"); else Print("OK"); Print("LIN-1 PROBLEM: "); //--- check if(linerror1) Print("FAILED"); else Print("OK"); Print("LIN-2 PROBLEM: "); //--- check if(linerror2) Print("FAILED"); else Print("OK"); Print("LINEAR EQUATIONS: "); //--- check if(eqerror) Print("FAILED"); else Print("OK"); Print("RESTARTS: "); //--- check if(restartserror) Print("FAILED"); else Print("OK"); Print("PRECONDITIONING: "); //--- check if(precerror) Print("FAILED"); else Print("OK"); Print("CONVERGENCE PROPERTIES: "); //--- check if(converror) Print("FAILED"); else Print("OK"); Print("OTHER PROPERTIES: "); //--- check if(othererrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Other properties | //+------------------------------------------------------------------+ static void CTestMinCGUnit::TestOther(bool &err) { //--- create variables int n=0; double fprev=0; double xprev=0; double stpmax=0; int i=0; int cgtype=0; double tmpeps=0; double epsg=0; double v=0; double r=0; bool hasxlast; double lastscaledstep=0; int pkind=0; int ckind=0; int mkind=0; int dkind=0; double diffstep=0; double vc=0; double vm=0; bool wasf; bool wasfg; int i_=0; //--- create arrays double x[]; double s[]; double a[]; double h[]; double xlast[]; //--- objects of classes CMinCGState state; CMinCGReport rep; //--- calculation for(cgtype=-1;cgtype<=1;cgtype++) { //--- Test reports (F should form monotone sequence) n=50; ArrayResize(x,n); ArrayResize(xlast,n); //--- change values for(i=0;i<=n-1;i++) x[i]=1; //--- function calls CMinCG::MinCGCreate(n,x,state); CMinCG::MinCGSetCond(state,0,0,0,100); CMinCG::MinCGSetXRep(state,true); fprev=CMath::m_maxrealnumber; //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]); state.m_g[i]=2*(1+i)*state.m_x[i]; } } //--- check if(state.m_xupdated) { err=err || state.m_f>fprev; //--- check if(fprev==CMath::m_maxrealnumber) { for(i=0;i<=n-1;i++) err=err || state.m_x[i]!=x[i]; } //--- change values fprev=state.m_f; for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; } } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors for(i=0;i<=n-1;i++) err=err || x[i]!=xlast[i]; //--- Test differentiation vs. analytic gradient //--- (first one issues NeedF requests,second one issues NeedFG requests) n=50; diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); for(i=0;i<=n-1;i++) x[i]=1; //--- check if(dkind==0) CMinCG::MinCGCreate(n,x,state); //--- check if(dkind==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCond(state,0,0,0,n/2); //--- change values wasf=false; wasfg=false; //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]); //--- check if(state.m_needfg) state.m_g[i]=2*(1+i)*state.m_x[i]; } //--- search errors wasf=wasf || state.m_needf; wasfg=wasfg || state.m_needfg; } //--- function call CMinCG::MinCGResults(state,x,rep); //--- check if(dkind==0) err=(err || wasf) || !wasfg; //--- check if(dkind==1) err=(err || !wasf) || wasfg; } //--- Test that numerical differentiation uses scaling. //--- In order to test that we solve simple optimization //--- problem: min(x^2) with initial x equal to 0.0. //--- We choose random DiffStep and S,then we check that //--- optimizer evaluates function at +-DiffStep*S only. ArrayResize(x,1); ArrayResize(s,1); diffstep=CMath::RandomReal()*1.0E-6; s[0]=MathExp(CMath::RandomReal()*4-2); x[0]=0; //--- function calls CMinCG::MinCGCreateF(1,x,diffstep,state); CMinCG::MinCGSetCond(state,1.0E-6,0,0,0); CMinCG::MinCGSetScale(state,s); v=0; //--- cycle while(CMinCG::MinCGIteration(state)) { state.m_f=CMath::Sqr(state.m_x[0]); v=MathMax(v,MathAbs(state.m_x[0])); } //--- function call CMinCG::MinCGResults(state,x,rep); r=v/(s[0]*diffstep); //--- search errors err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon); //--- Test maximum step n=1; ArrayResize(x,n); x[0]=100; stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinCG::MinCGCreate(n,x,state); CMinCG::MinCGSetCond(state,1.0E-9,0,0,0); CMinCG::MinCGSetStpMax(state,stpmax); CMinCG::MinCGSetXRep(state,true); xprev=x[0]; //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]); //--- search errors err=err || MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax; } //--- check if(state.m_xupdated) { //--- search errors err=err || MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax; xprev=state.m_x[0]; } } //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100] //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set stringent stopping conditions (we try EpsG and EpsX) //--- * and we test that in the extremum stopping conditions are //--- satisfied subject to the current scaling coefficients. tmpeps=1.0E-10; for(n=1;n<=10;n++) { for(pkind=0;pkind<=2;pkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); ArrayResize(a,n); ArrayResize(s,n); ArrayResize(h,n); //--- change values for(i=0;i<=n-1;i++) { x[i]=CMath::RandomReal()+1; a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); } //--- function calls CMinCG::MinCGCreate(n,x,state); CMinCG::MinCGSetScale(state,s); CMinCG::MinCGSetXRep(state,true); //--- check if(pkind==1) CMinCG::MinCGSetPrecDiag(state,h); //--- check if(pkind==2) CMinCG::MinCGSetPrecScale(state); //--- Test gradient-based stopping condition for(i=0;i<=n-1;i++) x[i]=CMath::RandomReal()+1; //--- function calls CMinCG::MinCGSetCond(state,tmpeps,0,0,0); CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4); state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3); } } } //--- function call CMinCG::MinCGResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { err=true; return; } //--- change value v=0; for(i=0;i<=n-1;i++) v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3)); v=MathSqrt(v); //--- search errors err=err || v>tmpeps; //--- Test step-based stopping condition for(i=0;i<=n-1;i++) x[i]=CMath::RandomReal()+1; hasxlast=false; //--- function calls CMinCG::MinCGSetCond(state,0,0,tmpeps,0); CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4); state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3); } } //--- check if(state.m_xupdated) { //--- check if(hasxlast) { lastscaledstep=0; for(i=0;i<=n-1;i++) lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]); lastscaledstep=MathSqrt(lastscaledstep); } else lastscaledstep=0; for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; hasxlast=true; } } //--- function call CMinCG::MinCGResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { err=true; return; } //--- search errors err=err || lastscaledstep>tmpeps; } } //--- Check correctness of the "trimming". //--- Trimming is a technique which is used to help algorithm //--- cope with unbounded functions. In order to check this //--- technique we will try to solve following optimization //--- problem: //--- min f(x) subject to no constraints on X //--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999=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.999999epsg; } } } } //+------------------------------------------------------------------+ //| Calculate test function #1 | //+------------------------------------------------------------------+ static void CTestMinCGUnit::TestFunc1(CMinCGState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { state.m_g[0]=MathSqrt(CMath::m_maxrealnumber); state.m_g[1]=0; state.m_g[2]=0; } } } //+------------------------------------------------------------------+ //| Calculate test function #2 | //| Simple variation of #1,much more nonlinear,which makes unlikely | //| premature convergence of algorithm . | //+------------------------------------------------------------------+ static void CTestMinCGUnit::TestFunc2(CMinCGState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=4*state.m_x[1]*CMath::Sqr(state.m_x[1]); state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { state.m_g[0]=MathSqrt(CMath::m_maxrealnumber); state.m_g[1]=0; state.m_g[2]=0; } } } //+------------------------------------------------------------------+ //| Calculate test function #3 | //| Simple variation of #1,much more nonlinear,with non-zero value at| //| minimum. | //| It achieve two goals: | //| * makes unlikely premature convergence of algorithm. | //| * solves some issues with EpsF stopping condition which arise | //| when F(minimum) is zero | //+------------------------------------------------------------------+ static void CTestMinCGUnit::TestFunc3(CMinCGState &state) { //--- create a variable double s=0; //--- initialization s=0.001; //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { state.m_g[0]=MathSqrt(CMath::m_maxrealnumber); state.m_g[1]=0; state.m_g[2]=0; } } } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ static void CTestMinCGUnit::CalcIIP2(CMinCGState &state,const int n) { //--- create a variable int i=0; //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- calculation for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]); //--- check if(state.m_needfg) state.m_g[i]=CMath::Sqr(i*i+1)*2*state.m_x[i]; } } //+------------------------------------------------------------------+ //| Calculate test function f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V | //+------------------------------------------------------------------+ static void CTestMinCGUnit::CalcLowRank(CMinCGState &state,const int n, const int vcnt,double &d[], CMatrixDouble &v,double &vd[], double &x0[]) { //--- create variables int i=0; int j=0; double dx=0; double t=0; double t2=0; int i_=0; //--- change values state.m_f=0; for(i=0;i<=n-1;i++) state.m_g[i]=0; //--- calculation for(i=0;i<=n-1;i++) { dx=state.m_x[i]-x0[i]; state.m_f=state.m_f+0.5*dx*d[i]*dx; state.m_g[i]=state.m_g[i]+d[i]*dx; } //--- calculation for(i=0;i<=vcnt-1;i++) { t=0; for(j=0;j<=n-1;j++) t=t+v[i][j]*(state.m_x[j]-x0[j]); //--- change values state.m_f=state.m_f+0.5*t*vd[i]*t; t2=t*vd[i]; for(i_=0;i_<=n-1;i_++) state.m_g[i_]=state.m_g[i_]+t2*v[i][i_]; } } //+------------------------------------------------------------------+ //| This function tests preconditioning | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMinCGUnit::TestPreconditioning(bool &err) { //--- create variables int pass=0; int n=0; int i=0; int j=0; int k=0; int vs=0; int cntb1=0; int cntg1=0; int cntb2=0; int cntg2=0; double epsg=0; int cgtype=0; //--- create arrays double x[]; double x0[]; double vd[]; double d[]; double s[]; double diagh[]; //--- create matrix CMatrixDouble v; //--- objects of classes CMinCGState state; CMinCGReport rep; //--- initialization k=50; epsg=1.0E-10; //--- calculation for(cgtype=-1;cgtype<=1;cgtype++) { //--- Preconditioner test 1. //--- If //--- * B1 is default preconditioner //--- * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix //--- then "bad" preconditioner is worse than "good" one. //--- "Worse" means more iterations to converge. //--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1). //--- N - problem size //--- K - number of repeated passes (should be large enough to average out random factors) for(n=10;n<=15;n++) { //--- allocation ArrayResize(x,n); for(i=0;i<=n-1;i++) x[i]=0; //--- function calls CMinCG::MinCGCreate(n,x,state); CMinCG::MinCGSetCGType(state,cgtype); //--- Test it with default preconditioner CMinCG::MinCGSetPrecDefault(state); //--- change values cntb1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) CalcIIP2(state,n); //--- function call CMinCG::MinCGResults(state,x,rep); cntb1=cntb1+rep.m_iterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- Test it with perturbed diagonal preconditioner ArrayResize(diagh,n); for(i=0;i<=n-1;i++) diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal()); //--- function call CMinCG::MinCGSetPrecDiag(state,diagh); //--- change values cntg1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) CalcIIP2(state,n); //--- function call CMinCG::MinCGResults(state,x,rep); cntg1=cntg1+rep.m_iterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- Compare err=err || cntb10) { //--- allocation v.Resize(vs,n); ArrayResize(vd,vs); //--- change values for(i=0;i<=vs-1;i++) { for(j=0;j<=n-1;j++) v[i].Set(j,2*CMath::RandomReal()-1); vd[i]=MathExp(2*CMath::RandomReal()); } } //--- function calls CMinCG::MinCGCreate(n,x,state); CMinCG::MinCGSetCGType(state,cgtype); //--- Test it with default preconditioner CMinCG::MinCGSetPrecDefault(state); //--- change values cntb1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) CalcLowRank(state,n,vs,d,v,vd,x0); //--- function call CMinCG::MinCGResults(state,x,rep); cntb1=cntb1+rep.m_iterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- Test it with low rank preconditioner CMinCG::MinCGSetPrecLowRankFast(state,d,vd,v,vs); //--- change values cntg1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) CalcLowRank(state,n,vs,d,v,vd,x0); //--- function call CMinCG::MinCGResults(state,x,rep); cntg1=cntg1+rep.m_iterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- Compare err=err || cntb1bndu[i]) err=true; } } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ static void CTestMinBLEICUnit::CalcIIP2(CMinBLEICState &state,const int n, const int fk) { //--- create a variable int i=0; //--- check if(state.m_needfg) state.m_f=0; //--- calculation for(i=0;i<=n-1;i++) { //--- check if(state.m_needfg) { state.m_f=state.m_f+MathPow(i*i+1,2*fk)*CMath::Sqr(state.m_x[i]); state.m_g[i]=MathPow(i*i+1,2*fk)*2*state.m_x[i]; } } } //+------------------------------------------------------------------+ //| This function test feasibility properties. | //| It launches a sequence of problems and examines their solutions. | //| Most of the attention is directed towards feasibility properties,| //| although we make some quick checks to ensure that actual solution| //| is found. | //| On failure sets FeasErr (or ConvErr,depending on failure type) | //| to True, or leaves it unchanged otherwise. | //| IntErr is set to True on internal errors (errors in the control | //| flow). | //+------------------------------------------------------------------+ static void CTestMinBLEICUnit::TestFeasibility(bool &feaserr,bool &converr, bool &interr) { //--- create variables int pkind=0; int preckind=0; int passcount=0; int pass=0; int n=0; int nmax=0; int i=0; int j=0; int k=0; int p=0; double v=0; double v2=0; double v3=0; double vv=0; double epsc=0; double epsg=0; int dkind=0; double diffstep=0; int i_=0; //--- create arrays double bl[]; double bu[]; double x[]; double g[]; double x0[]; double xs[]; int ct[]; //--- create matrix CMatrixDouble c; //--- objects of classes CMinBLEICState state; CMinBLEICReport rep; //--- initialization nmax=5; epsc=1.0E-4; epsg=1.0E-8; passcount=10; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Test problem 1: //--- * no boundary and inequality constraints //--- * randomly generated plane as equality constraint //--- * random point (not necessarily on the plane) //--- * f=||x||^P,P={2,4} is used as target function //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * either analytic gradient or numerical differentiation are used //--- * we check that after work is over we are on the plane and //--- that we are in the stationary point of constrained F diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { for(preckind=0;preckind<=2;preckind++) { for(pkind=1;pkind<=2;pkind++) { for(n=1;n<=nmax;n++) { //--- Generate X,BL,BU,CT and left part of C. //--- Right part of C is generated using somewhat complex algo: //--- * we generate random vector and multiply it by C. //--- * result is used as the right part. //--- * calculations are done on the fly,vector itself is not stored //--- We use such algo to be sure that our system is consistent. p=2*pkind; ArrayResize(x,n); ArrayResize(g,n); c.Resize(1,n+1); ArrayResize(ct,1); c[0].Set(n,0); //--- calculation for(i=0;i<=n-1;i++) { x[i]=2*CMath::RandomReal()-1; c[0].Set(i,2*CMath::RandomReal()-1); v=2*CMath::RandomReal()-1; c[0].Set(n,c[0][n]+c[0][i]*v); } ct[0]=0; //--- Create and optimize if(dkind==0) CMinBLEIC::MinBLEICCreate(n,x,state); //--- check if(dkind==1) CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state); //--- function calls CMinBLEIC::MinBLEICSetLC(state,c,ct,1); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); SetRandomPreconditioner(state,n,preckind); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+MathPow(state.m_x[i],p); //--- check if(state.m_needfg) state.m_g[i]=p*MathPow(state.m_x[i],p-1); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { converr=true; return; } //--- Test feasibility of solution v=0.0; for(i_=0;i_<=n-1;i_++) v+=c[0][i_]*x[i_]; //--- search errors feaserr=feaserr || MathAbs(v-c[0][n])>epsc; //--- if C is nonzero,test that result is //--- a stationary point of constrained F. //--- NOTE: this check is done only if C is nonzero vv=0.0; for(i_=0;i_<=n-1;i_++) vv+=c[0][i_]*c[0][i_]; //--- check if(vv!=0.0) { //--- Calculate gradient at the result //--- Project gradient into C //--- Check projected norm for(i=0;i<=n-1;i++) g[i]=p*MathPow(x[i],p-1); v2=0.0; for(i_=0;i_<=n-1;i_++) v2+=c[0][i_]*c[0][i_]; //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=c[0][i_]*g[i_]; vv=v/v2; for(i_=0;i_<=n-1;i_++) { g[i_]=g[i_]-vv*c[0][i_]; } v3=0.0; for(i_=0;i_<=n-1;i_++) v3+=g[i_]*g[i_]; //--- search errors converr=converr || MathSqrt(v3)>0.001; } } } } } //--- Test problem 2 (multiple equality constraints): //--- * 1<=N<=NMax,1<=K<=N //--- * no boundary constraints //--- * N-dimensional space //--- * randomly generated point xs //--- * K randomly generated hyperplanes which all pass through xs //--- define K equality constraints: (a[k],x)=b[k] //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * f(x)=||x-x0||^2,x0=xs+a[0] //--- * either analytic gradient or numerical differentiation are used //--- * extremum of f(x) is exactly xs because: //--- * xs is the closest point in the plane defined by (a[0],x)=b[0] //--- * xs is feasible by definition diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { for(preckind=0;preckind<=2;preckind++) { for(n=2;n<=nmax;n++) { for(k=1;k<=n;k++) { //--- Generate X,X0,XS,BL,BU,CT and left part of C. //--- Right part of C is generated using somewhat complex algo: //--- * we generate random vector and multiply it by C. //--- * result is used as the right part. //--- * calculations are done on the fly,vector itself is not stored //--- We use such algo to be sure that our system is consistent. p=2*pkind; ArrayResize(x,n); ArrayResize(x0,n); ArrayResize(xs,n); ArrayResize(g,n); c.Resize(k,n+1); ArrayResize(ct,k); c[0].Set(n,0); //--- change values for(i=0;i<=n-1;i++) { x[i]=2*CMath::RandomReal()-1; xs[i]=2*CMath::RandomReal()-1; } for(i=0;i<=k-1;i++) { for(j=0;j<=n-1;j++) c[i].Set(j,2*CMath::RandomReal()-1); //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=c[i][i_]*xs[i_]; c[i].Set(n,v); ct[i]=0; } //--- copy for(i_=0;i_<=n-1;i_++) x0[i_]=xs[i_]; for(i_=0;i_<=n-1;i_++) x0[i_]=x0[i_]+c[0][i_]; //--- Create and optimize // if(dkind==0) CMinBLEIC::MinBLEICCreate(n,x,state); //--- check if(dkind==1) CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state); //--- function calls CMinBLEIC::MinBLEICSetLC(state,c,ct,k); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); SetRandomPreconditioner(state,n,preckind); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr(state.m_x[i]-x0[i]); //--- check if(state.m_needfg) state.m_g[i]=2*(state.m_x[i]-x0[i]); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { converr=true; return; } //--- check feasiblity properties for(i=0;i<=k-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=c[i][i_]*x[i_]; //--- search errors feaserr=feaserr || MathAbs(v-c[i][n])>epsc; } //--- Compare with XS v=0; for(i=0;i<=n-1;i++) v=v+CMath::Sqr(x[i]-xs[i]); v=MathSqrt(v); //--- search errors converr=converr || MathAbs(v)>0.001; } } } } //--- Another simple problem: //--- * bound constraints 0 <=x[i] <=1 //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple boundaries and function it is easy to find //--- analytic form of solution: S[i]=bound(x0[i],0,1) //--- * we also check that both final solution and subsequent iterates //--- are strictly feasible diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { for(preckind=0;preckind<=2;preckind++) { for(pkind=1;pkind<=2;pkind++) { for(n=1;n<=nmax;n++) { //--- Generate X,BL,BU. p=2*pkind; ArrayResize(bl,n); ArrayResize(bu,n); ArrayResize(x,n); ArrayResize(x0,n); //--- change values for(i=0;i<=n-1;i++) { bl[i]=0; bu[i]=1; x[i]=CMath::RandomReal(); x0[i]=3*CMath::RandomReal()-1; } //--- Create and optimize if(dkind==0) CMinBLEIC::MinBLEICCreate(n,x,state); //--- check if(dkind==1) CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state); //--- function calls CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); SetRandomPreconditioner(state,n,preckind); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p); //--- check if(state.m_needfg) state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1); //--- search errors feaserr=feaserr || state.m_x[i]<0.0; feaserr=feaserr || state.m_x[i]>1.0; } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility for(i=0;i<=n-1;i++) { //--- search errors converr=converr || MathAbs(x[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.01; feaserr=feaserr || x[i]<0.0; feaserr=feaserr || x[i]>1.0; } } } } } //--- Same as previous one,but with bound constraints posed //--- as general linear ones: //--- * no bound constraints //--- * 2*N linear constraints 0 <=x[i] <=1 //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple constraints and function it is easy to find //--- analytic form of solution: S[i]=bound(x0[i],0,1). //--- * however,we can't guarantee that solution is strictly feasible //--- with respect to nonlinearity constraint,so we check //--- for approximate feasibility. for(preckind=0;preckind<=2;preckind++) { for(pkind=1;pkind<=2;pkind++) { for(n=1;n<=nmax;n++) { //--- Generate X,BL,BU. p=2*pkind; ArrayResize(x,n); ArrayResize(x0,n); c.Resize(2*n,n+1); ArrayResize(ct,2*n); //--- change values for(i=0;i<=n-1;i++) { x[i]=CMath::RandomReal(); x0[i]=3*CMath::RandomReal()-1; for(j=0;j<=n;j++) { c[2*i].Set(j,0); c[2*i+1].Set(j,0); } c[2*i+0].Set(i,1); c[2*i+0].Set(n,0); ct[2*i+0]=1; c[2*i+1].Set(i,1); c[2*i+1].Set(n,1); ct[2*i+1]=-1; } //--- Create and optimize CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetLC(state,c,ct,2*n); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); SetRandomPreconditioner(state,n,preckind); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p); state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1); } continue; } //--- Unknown protocol specified interr=true; return; } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility for(i=0;i<=n-1;i++) { //--- search errors converr=converr || MathAbs(x[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.05; feaserr=feaserr || x[i]<0.0-epsc; feaserr=feaserr || x[i]>1.0+epsc; } } } } //--- Infeasible problem: //--- * all bound constraints are 0 <=x[i] <=1 except for one //--- * that one is 0 >=x[i] >=1 //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from detecting //--- infeasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * algorithm must return correct error code on such problem for(preckind=0;preckind<=2;preckind++) { for(pkind=1;pkind<=2;pkind++) { for(n=1;n<=nmax;n++) { //--- Generate X,BL,BU. p=2*pkind; ArrayResize(bl,n); ArrayResize(bu,n); ArrayResize(x,n); ArrayResize(x0,n); //--- change values for(i=0;i<=n-1;i++) { bl[i]=0; bu[i]=1; x[i]=CMath::RandomReal(); x0[i]=3*CMath::RandomReal()-1; } i=CMath::RandomInteger(n); bl[i]=1; bu[i]=0; //--- Create and optimize CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); SetRandomPreconditioner(state,n,preckind); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p); state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1); } continue; } //--- Unknown protocol specified interr=true; return; } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- search errors feaserr=feaserr || rep.m_terminationtype!=-3; } } } //--- Infeasible problem (2): //--- * no bound and inequality constraints //--- * 1<=K<=N arbitrary equality constraints //--- * (K+1)th constraint which is equal to the first constraint a*x=c, //--- but with c:=c+1. I.e. we have both a*x=c and a*x=c+1,which can't //--- be true (other constraints may be inconsistent too,but we don't //--- have to check it). //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from detecting //--- infeasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x)=||x||^P,where P={2,4} //--- * algorithm must return correct error code on such problem for(preckind=0;preckind<=2;preckind++) { for(pkind=1;pkind<=2;pkind++) { for(n=1;n<=nmax;n++) { for(k=1;k<=n;k++) { //--- Generate X,BL,BU. p=2*pkind; ArrayResize(x,n); c.Resize(k+1,n+1); ArrayResize(ct,k+1); for(i=0;i<=n-1;i++) x[i]=CMath::RandomReal(); //--- change values for(i=0;i<=k-1;i++) { for(j=0;j<=n;j++) c[i].Set(j,2*CMath::RandomReal()-1); ct[i]=0; } ct[k]=0; for(i_=0;i_<=n-1;i_++) c[k].Set(i_,c[0][i_]); c[k].Set(n,c[0][n]+1); //--- Create and optimize CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetLC(state,c,ct,k+1); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); SetRandomPreconditioner(state,n,preckind); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+MathPow(state.m_x[i],p); state.m_g[i]=p*MathPow(state.m_x[i],p-1); } continue; } //--- Unknown protocol specified interr=true; return; } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- search errors feaserr=feaserr || rep.m_terminationtype!=-3; } } } } } } //+------------------------------------------------------------------+ //| This function additional properties. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMinBLEICUnit::TestOther(bool &err) { //--- create variables int passcount=0; int pass=0; int n=0; int nmax=0; int i=0; double fprev=0; double xprev=0; double stpmax=0; double v=0; int pkind=0; int ckind=0; int mkind=0; double vc=0; double vm=0; double epsc=0; double epsg=0; double tmpeps=0; double diffstep=0; int dkind=0; bool wasf; bool wasfg; double r=0; int i_=0; //--- create arrays double bl[]; double bu[]; double x[]; double xf[]; double xlast[]; double a[]; double s[]; double h[]; int ct[]; //--- create matrix CMatrixDouble c; //--- objects of classes CMinBLEICState state; CMinBLEICReport rep; //--- initialization nmax=5; epsc=1.0E-4; epsg=1.0E-8; passcount=10; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Test reports: //--- * first value must be starting point //--- * last value must be last point n=50; ArrayResize(x,n); ArrayResize(xlast,n); ArrayResize(bl,n); ArrayResize(bu,n); //--- change values for(i=0;i<=n-1;i++) { x[i]=10; bl[i]=2*CMath::RandomReal()-1; bu[i]=CInfOrNaN::PositiveInfinity(); } //--- function calls CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,0,0,0); CMinBLEIC::MinBLEICSetMaxIts(state,10); CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-64,1.0E-64); CMinBLEIC::MinBLEICSetXRep(state,true); fprev=CMath::m_maxrealnumber; //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]); state.m_g[i]=2*(1+i)*state.m_x[i]; } } //--- check if(state.m_xupdated) { //--- check if(fprev==CMath::m_maxrealnumber) { for(i=0;i<=n-1;i++) err=err || state.m_x[i]!=x[i]; } //--- change values fprev=state.m_f; for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- search errors for(i=0;i<=n-1;i++) err=err || x[i]!=xlast[i]; //--- Test differentiation vs. analytic gradient //--- (first one issues NeedF requests,second one issues NeedFG requests) n=50; diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); for(i=0;i<=n-1;i++) x[i]=1; //--- check if(dkind==0) CMinBLEIC::MinBLEICCreate(n,x,state); //--- check if(dkind==1) CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state); //--- function calls CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-10,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6); wasf=false; wasfg=false; //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- calculation for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]); //--- check if(state.m_needfg) state.m_g[i]=2*(1+i)*state.m_x[i]; } //--- search errors wasf=wasf || state.m_needf; wasfg=wasfg || state.m_needfg; } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(dkind==0) err=(err || wasf) || !wasfg; //--- check if(dkind==1) err=(err || !wasf) || wasfg; } //--- Test that numerical differentiation uses scaling. //--- In order to test that we solve simple optimization //--- problem: min(x^2) with initial x equal to 0.0. //--- We choose random DiffStep and S,then we check that //--- optimizer evaluates function at +-DiffStep*S only. ArrayResize(x,1); ArrayResize(s,1); diffstep=CMath::RandomReal()*1.0E-6; s[0]=MathExp(CMath::RandomReal()*4-2); x[0]=0; //--- function calls CMinBLEIC::MinBLEICCreateF(1,x,diffstep,state); CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-6,0,0); CMinBLEIC::MinBLEICSetScale(state,s); v=0; //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { state.m_f=CMath::Sqr(state.m_x[0]); v=MathMax(v,MathAbs(state.m_x[0])); } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); r=v/(s[0]*diffstep); //--- search errors err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon); //--- Test stpmax n=1; ArrayResize(x,n); ArrayResize(bl,n); ArrayResize(bu,n); //--- change values x[0]=100; bl[0]=2*CMath::RandomReal()-1; bu[0]=CInfOrNaN::PositiveInfinity(); stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); CMinBLEIC::MinBLEICSetXRep(state,true); CMinBLEIC::MinBLEICSetStpMax(state,stpmax); xprev=x[0]; //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]); err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); } //--- check if(state.m_xupdated) { err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); xprev=state.m_x[0]; } } //--- Ability to solve problems with function which is unbounded from below n=1; ArrayResize(x,n); ArrayResize(bl,n); ArrayResize(bu,n); bl[0]=4*CMath::RandomReal()+1; bu[0]=bl[0]+1; x[0]=0.5*(bl[0]+bu[0]); //--- function calls CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0])); state.m_g[0]=-(2.0E8*state.m_x[0]); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- search errors err=err || MathAbs(x[0]-bu[0])>epsc; //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100] //--- * function is EFFECTIVELY unconstrained;it has formal constraints, //--- but they are inactive at the solution;we try different variants //--- in order to explore different control paths of the optimizer: //--- 0) absense of constraints //--- 1) bound constraints -100000<=x[i]<=100000 //--- 2) one linear constraint 0*x=0 //--- 3) combination of (1) and (2) //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set very mild outer stopping conditions - OuterEpsX=1.0,but //--- inner conditions are very stringent //--- * and we test that in the extremum inner stopping conditions are //--- satisfied subject to the current scaling coefficients. tmpeps=1.0E-10; for(n=1;n<=10;n++) { for(ckind=0;ckind<=3;ckind++) { for(pkind=0;pkind<=2;pkind++) { //--- allocation ArrayResize(x,n); ArrayResize(a,n); ArrayResize(s,n); ArrayResize(h,n); ArrayResize(bl,n); ArrayResize(bu,n); c.Resize(1,n+1); ArrayResize(ct,1); ct[0]=0; c[0].Set(n,0); //--- change values for(i=0;i<=n-1;i++) { x[i]=CMath::RandomReal()+1; bl[i]=-100000; bu[i]=100000; c[0].Set(i,0); a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); } CMinBLEIC::MinBLEICCreate(n,x,state); //--- check if(ckind==1 || ckind==3) CMinBLEIC::MinBLEICSetBC(state,bl,bu); //--- check if(ckind==2 || ckind==3) CMinBLEIC::MinBLEICSetLC(state,c,ct,1); //--- check if(pkind==1) CMinBLEIC::MinBLEICSetPrecDiag(state,h); //--- check if(pkind==2) CMinBLEIC::MinBLEICSetPrecScale(state); //--- function calls CMinBLEIC::MinBLEICSetInnerCond(state,tmpeps,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,1.0,1.0E-8); CMinBLEIC::MinBLEICSetScale(state,s); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4); state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3); } } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { err=true; return; } //--- change value v=0; for(i=0;i<=n-1;i++) v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3)); v=MathSqrt(v); //--- search errors err=err || v>tmpeps; } } } //--- Check correctness of the "trimming". //--- Trimming is a technique which is used to help algorithm //--- cope with unbounded functions. In order to check this //--- technique we will try to solve following optimization //--- problem: //--- min f(x) subject to no constraints on X //--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999=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.999999epsg; } } } } //+------------------------------------------------------------------+ //| This function tests convergence properties. | //| We solve several simple problems with different combinations of | //| constraints | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMinBLEICUnit::TestConv(bool &err) { //--- create variables int passcount=0; int pass=0; double epsc=0; double epsg=0; double tol=0; //--- create arrays double bl[]; double bu[]; double x[]; int ct[]; //--- create matrix CMatrixDouble c; //--- objects of classes CMinBLEICState state; CMinBLEICReport rep; //--- initialization epsc=1.0E-4; epsg=1.0E-8; tol=0.001; passcount=10; //--- Three closely connected problems: //--- * 2-dimensional space //--- * octagonal area bounded by: //--- * -1<=x<=+1 //--- * -1<=y<=+1 //--- * x+y<=1.5 //--- * x-y<=1.5 //--- * -x+y<=1.5 //--- * -x-y<=1.5 //--- * several target functions: //--- * f0=x+0.001*y,minimum at x=-1,y=-0.5 //--- * f1=(x+10)^2+y^2,minimum at x=-1,y=0 //--- * f2=(x+10)^2+(y-0.6)^2,minimum at x=-1,y=0.5 ArrayResize(x,2); ArrayResize(bl,2); ArrayResize(bu,2); c.Resize(4,3); ArrayResize(ct,4); //--- change values bl[0]=-1; bl[1]=-1; bu[0]=1; bu[1]=1; c[0].Set(0,1); c[0].Set(1,1); c[0].Set(2,1.5); ct[0]=-1; c[1].Set(0,1); c[1].Set(1,-1); c[1].Set(2,1.5); ct[1]=-1; c[2].Set(0,-1); c[2].Set(1,1); c[2].Set(2,1.5); ct[2]=-1; c[3].Set(0,-1); c[3].Set(1,-1); c[3].Set(2,1.5); ct[3]=-1; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- f0 x[0]=0.2*CMath::RandomReal()-0.1; x[1]=0.2*CMath::RandomReal()-0.1; //--- function call CMinBLEIC::MinBLEICCreate(2,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetLC(state,c,ct,4); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=state.m_x[0]+0.001*state.m_x[1]; state.m_g[0]=1; state.m_g[1]=0.001; } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { //--- search errors err=err || MathAbs(x[0]+1)>tol; err=err || MathAbs(x[1]+0.5)>tol; } else err=true; //--- f1 x[0]=0.2*CMath::RandomReal()-0.1; x[1]=0.2*CMath::RandomReal()-0.1; //--- function calls CMinBLEIC::MinBLEICCreate(2,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetLC(state,c,ct,4); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]); state.m_g[0]=2*(state.m_x[0]+10); state.m_g[1]=2*state.m_x[1]; } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { //--- search errors err=err || MathAbs(x[0]+1)>tol; err=err || MathAbs(x[1])>tol; } else err=true; //--- f2 x[0]=0.2*CMath::RandomReal()-0.1; x[1]=0.2*CMath::RandomReal()-0.1; //--- function calls CMinBLEIC::MinBLEICCreate(2,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetLC(state,c,ct,4); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]-0.6); state.m_g[0]=2*(state.m_x[0]+10); state.m_g[1]=2*(state.m_x[1]-0.6); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { //--- search errors err=err || MathAbs(x[0]+1)>tol; err=err || MathAbs(x[1]-0.5)>tol; } else err=true; } } //+------------------------------------------------------------------+ //| This function tests preconditioning | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMinBLEICUnit::TestPreconditioning(bool &err) { //--- create variables int pass=0; int n=0; int i=0; int k=0; int cntb1=0; int cntb2=0; int cntg1=0; int cntg2=0; double epsg=0; int fkind=0; int ckind=0; int fk=0; //--- create arrays double x[]; double x0[]; int ct[]; double bl[]; double bu[]; double vd[]; double d[]; double units[]; double s[]; double diagh[]; //--- create matrix CMatrixDouble v; CMatrixDouble c; //--- objects of classes CMinBLEICState state; CMinBLEICReport rep; //--- Preconditioner test 1. //--- If //--- * B1 is default preconditioner with unit scale //--- * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix //--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i]) //--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i]) //--- then B1 is worse than G1,B2 is worse than G2. //--- "Worse" means more iterations to converge. //--- Test problem setup: //--- * f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) //--- * FK is either +1 or -1 (we try both to test different aspects of preconditioning) //--- * constraints: //--- 0) absent //--- 1) boundary only //--- 2) linear equality only //--- 3) combination of boundary and linear equality constraints //--- N - problem size //--- K - number of repeated passes (should be large enough to average out random factors) k=30; epsg=1.0E-10; for(n=5;n<=8;n++) { for(fkind=0;fkind<=1;fkind++) { for(ckind=0;ckind<=3;ckind++) { fk=1-2*fkind; //--- allocation ArrayResize(x,n); ArrayResize(units,n); for(i=0;i<=n-1;i++) { x[i]=0; units[i]=1; } //--- function call CMinBLEIC::MinBLEICCreate(n,x,state); //--- check if(ckind==1 || ckind==3) { //--- allocation ArrayResize(bl,n); ArrayResize(bu,n); for(i=0;i<=n-1;i++) { bl[i]=-1; bu[i]=1; } //--- function call CMinBLEIC::MinBLEICSetBC(state,bl,bu); } //--- check if(ckind==2 || ckind==3) { //--- allocation c.Resize(1,n+1); ArrayResize(ct,1); //--- change value ct[0]=CMath::RandomInteger(3)-1; for(i=0;i<=n-1;i++) c[0].Set(i,2*CMath::RandomReal()-1); c[0].Set(n,0); //--- function call CMinBLEIC::MinBLEICSetLC(state,c,ct,1); } //--- Test it with default preconditioner VS. perturbed diagonal preconditioner CMinBLEIC::MinBLEICSetPrecDefault(state); CMinBLEIC::MinBLEICSetScale(state,units); //--- calculation cntb1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinBLEIC::MinBLEICRestartFrom(state,x); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) CalcIIP2(state,n,fk); //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); cntb1=cntb1+rep.m_inneriterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- allocation ArrayResize(diagh,n); for(i=0;i<=n-1;i++) diagh[i]=2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal()); //--- function calls CMinBLEIC::MinBLEICSetPrecDiag(state,diagh); CMinBLEIC::MinBLEICSetScale(state,units); //--- calculation cntg1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinBLEIC::MinBLEICRestartFrom(state,x); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) CalcIIP2(state,n,fk); //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); cntg1=cntg1+rep.m_inneriterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- search errors err=err || cntb10) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) err=err || MathAbs(p[i][j]-pexact[i][j])>threshold; } } else err=true; } //--- Second test: //--- * N-dimensional problem //--- * proportional data //--- * no "entry"/"exit" states //--- * N tracks,each includes only two states //--- * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position //--- * all tracks are modelled using randomly generated transition matrix P offdiagonal=0.1; for(n=1;n<=5;n++) { //--- Initialize "exact" P: //--- * fill by random values //--- * make sure that each column sums to non-zero value //--- * normalize pexact.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) pexact[i].Set(j,CMath::RandomReal()); } //--- calculation for(j=0;j<=n-1;j++) { i=CMath::RandomInteger(n); pexact[i].Set(j,pexact[i][j]+0.1); } for(j=0;j<=n-1;j++) { v=0; for(i=0;i<=n-1;i++) v=v+pexact[i][j]; for(i=0;i<=n-1;i++) pexact[i].Set(j,pexact[i][j]/v); } //--- Initialize solver: //--- * create object //--- * add tracks CMarkovCPD::MCPDCreate(n,s); for(i=0;i<=n-1;i++) { //--- allocation xy.Resize(2,n); for(j=0;j<=n-1;j++) xy[0].Set(j,0); //--- "main" element xy[0].Set(i,1.0-2*offdiagonal); for(j=0;j<=n-1;j++) xy[1].Set(j,(1.0-2*offdiagonal)*pexact[j][i]); //--- off-diagonal ones if(i>0) { xy[0].Set(i-1,offdiagonal); for(j=0;j<=n-1;j++) xy[1].Set(j,xy[1][j]+offdiagonal*pexact[j][i-1]); } //--- check if(i0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) err=err || MathAbs(p[i][j]-pexact[i][j])>threshold; } } else err=true; } //--- Third test: //--- * N-dimensional problem //--- * population data //--- * no "entry"/"exit" states //--- * N tracks,each includes only two states //--- * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position,V in [1,10] //--- * all tracks are modelled using randomly generated transition matrix P offdiagonal=0.1; for(n=1;n<=5;n++) { //--- Initialize "exact" P: //--- * fill by random values //--- * make sure that each column sums to non-zero value //--- * normalize pexact.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) pexact[i].Set(j,CMath::RandomReal()); } //--- change values for(j=0;j<=n-1;j++) { i=CMath::RandomInteger(n); pexact[i].Set(j,pexact[i][j]+0.1); } for(j=0;j<=n-1;j++) { v=0; for(i=0;i<=n-1;i++) v=v+pexact[i][j]; for(i=0;i<=n-1;i++) pexact[i].Set(j,pexact[i][j]/v); } //--- Initialize solver: //--- * create object //--- * add tracks CMarkovCPD::MCPDCreate(n,s); for(i=0;i<=n-1;i++) { //--- allocation xy.Resize(2,n); for(j=0;j<=n-1;j++) xy[0].Set(j,0); //--- "main" element v0=9*CMath::RandomReal()+1; xy[0].Set(i,v0*(1.0-2*offdiagonal)); for(j=0;j<=n-1;j++) xy[1].Set(j,v0*(1.0-2*offdiagonal)*pexact[j][i]); //--- off-diagonal ones if(i>0) { xy[0].Set(i-1,v0*offdiagonal); for(j=0;j<=n-1;j++) xy[1].Set(j,xy[1][j]+v0*offdiagonal*pexact[j][i-1]); } //--- check if(i0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) err=err || MathAbs(p[i][j]-pexact[i][j])>threshold; } } else err=true; } } //+------------------------------------------------------------------+ //| Test for different combinations of "entry"/"exit" models | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMCPDUnit::TestEntryExit(bool &err) { //--- create variables int n=0; double threshold=0; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int popkind=0; int i=0; int j=0; int k=0; double v=0; int i_=0; //--- create matrix CMatrixDouble p; CMatrixDouble pexact; CMatrixDouble xy; //--- objects of classes CMCPDState s; CMCPDReport rep; //--- initialization threshold=1.0E-3; //--- calculation for(n=2;n<=5;n++) { for(entrykind=0;entrykind<=1;entrykind++) { for(exitkind=0;exitkind<=1;exitkind++) { for(popkind=0;popkind<=1;popkind++) { //--- Generate EntryState/ExitState such that one of the following is True: //--- * EntryState<>ExitState //--- * EntryState=-1 or ExitState=-1 do { //--- check if(entrykind==0) entrystate=-1; else entrystate=CMath::RandomInteger(n); //--- check if(exitkind==0) exitstate=-1; else exitstate=CMath::RandomInteger(n); } while(!((entrystate==-1 || exitstate==-1) || entrystate!=exitstate)); //--- Generate transition matrix P such that: //--- * columns corresponding to non-exit states sums to 1.0 //--- * columns corresponding to exit states sums to 0.0 //--- * rows corresponding to entry states are zero pexact.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { pexact[i].Set(j,1+CMath::RandomInteger(5)); //--- check if(i==entrystate) pexact[i].Set(j,0.0); //--- check if(j==exitstate) pexact[i].Set(j,0.0); } } //--- calculation for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i=0;i<=n-1;i++) v=v+pexact[i][j]; //--- check if(v!=0.0) { for(i=0;i<=n-1;i++) pexact[i].Set(j,pexact[i][j]/v); } } //--- Create MCPD solver if(entrystate<0 && exitstate<0) CMarkovCPD::MCPDCreate(n,s); //--- check if(entrystate>=0 && exitstate<0) CMarkovCPD::MCPDCreateEntry(n,entrystate,s); //--- check if(entrystate<0 && exitstate>=0) CMarkovCPD::MCPDCreateExit(n,exitstate,s); //--- check if(entrystate>=0 && exitstate>=0) CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s); //--- Add N tracks. //--- K-th track starts from vector with large value of //--- K-th component and small random noise in other components. //--- Track contains from 2 to 4 elements. //--- Tracks contain proportional (normalized) or //--- population data,depending on PopKind variable. for(k=0;k<=n-1;k++) { //--- Generate track whose length is in 2..4 xy.Resize(2+CMath::RandomInteger(3),n); for(j=0;j<=n-1;j++) xy[0].Set(j,0.05*CMath::RandomReal()); xy[0].Set(k,1+CMath::RandomReal()); //--- calculation for(i=1;i<=CAp::Rows(xy)-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(j!=entrystate) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=pexact[j][i_]*xy[i-1][i_]; xy[i].Set(j,v); } else xy[i].Set(j,CMath::RandomReal()); } } //--- Normalize,if needed if(popkind==1) { for(i=0;i<=CAp::Rows(xy)-1;i++) { //--- change value v=0.0; for(j=0;j<=n-1;j++) v=v+xy[i][j]; //--- check if(v>0.0) { for(j=0;j<=n-1;j++) xy[i].Set(j,xy[i][j]/v); } } } //--- Add track CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); } //--- Solve and test CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) err=err || MathAbs(p[i][j]-pexact[i][j])>threshold; } } else err=true; } } } } } //+------------------------------------------------------------------+ //| Test equality constraints. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMCPDUnit::TestEC(bool &err) { //--- create variables int n=0; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int i=0; int j=0; int ic=0; int jc=0; double vc=0; //--- create matrix CMatrixDouble p; CMatrixDouble ec; CMatrixDouble xy; //--- objects of classes CMCPDState s; CMCPDReport rep; //--- We try different problems with following properties: //--- * N is large enough - we won't have problems with inconsistent constraints //--- * first state is either "entry" or "normal" //--- * last state is either "exit" or "normal" //--- * we have one long random track //--- We test several properties which are described in comments below for(n=4;n<=6;n++) { for(entrykind=0;entrykind<=1;entrykind++) { for(exitkind=0;exitkind<=1;exitkind++) { //--- Prepare problem if(entrykind==0) entrystate=-1; else entrystate=0; //--- check if(exitkind==0) exitstate=-1; else exitstate=n-1; //--- allocation xy.Resize(2*n,n); for(i=0;i<=CAp::Rows(xy)-1;i++) { for(j=0;j<=CAp::Cols(xy)-1;j++) xy[i].Set(j,CMath::RandomReal()); } //--- Test that single equality constraint on non-entry //--- non-exit elements of P is satisfied. //--- NOTE: this test needs N>=4 because smaller values //--- can give us inconsistent constraints if(!CAp::Assert(n>=4,"TestEC: expectation failed")) return; ic=1+CMath::RandomInteger(n-2); jc=1+CMath::RandomInteger(n-2); vc=CMath::RandomReal(); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddEC(s,ic,jc,vc); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) err=err || p[ic][jc]!=vc; else err=true; //--- Test interaction with default "sum-to-one" constraint //--- on columns of P. //--- We set N-1 equality constraints on random non-exit column //--- of P,which are inconsistent with this default constraint //--- (sum will be greater that 1.0). //--- Algorithm must detect inconsistency. //--- NOTE: //--- 1. we do not set constraints for the first element of //--- the column,because this element may be constrained by //--- "exit state" constraint. //--- 2. this test needs N>=3 if(!CAp::Assert(n>=3,"TestEC: expectation failed")) return; jc=CMath::RandomInteger(n-1); vc=0.95; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); for(i=1;i<=n-1;i++) CMarkovCPD::MCPDAddEC(s,i,jc,vc); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; //--- Test interaction with constrains on entry states. //--- When model has entry state,corresponding row of P //--- must be zero. We try to set two kinds of constraints //--- on random element of this row: //--- * zero equality constraint,which must be consistent //--- * non-zero equality constraint,which must be inconsistent if(entrystate>=0) { jc=CMath::RandomInteger(n); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.0); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.5); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; } //--- Test interaction with constrains on exit states. //--- When model has exit state,corresponding column of P //--- must be zero. We try to set two kinds of constraints //--- on random element of this column: //--- * zero equality constraint,which must be consistent //--- * non-zero equality constraint,which must be inconsistent if(exitstate>=0) { ic=CMath::RandomInteger(n); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.0); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.5); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; } //--- Test SetEC() call - we constrain subset of non-entry //--- non-exit elements and test it. if(!CAp::Assert(n>=4,"TestEC: expectation failed")) return; //--- allocation ec.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) ec[i].Set(j,CInfOrNaN::NaN()); } for(j=1;j<=n-2;j++) ec[1+CMath::RandomInteger(n-2)].Set(j,0.1+0.1*CMath::RandomReal()); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetEC(s,ec); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::IsFinite(ec[i][j])) err=err || p[i][j]!=ec[i][j]; } } } else err=true; } } } } //+------------------------------------------------------------------+ //| Test bound constraints. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMCPDUnit::TestBC(bool &err) { //--- create variables int n=0; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int i=0; int j=0; int ic=0; int jc=0; double vl=0; double vu=0; //--- create matrix CMatrixDouble p; CMatrixDouble bndl; CMatrixDouble bndu; CMatrixDouble xy; //--- îáúåòû êëàññîâ CMCPDState s; CMCPDReport rep; //--- We try different problems with following properties: //--- * N is large enough - we won't have problems with inconsistent constraints //--- * first state is either "entry" or "normal" //--- * last state is either "exit" or "normal" //--- * we have one long random track //--- We test several properties which are described in comments below for(n=4;n<=6;n++) { for(entrykind=0;entrykind<=1;entrykind++) { for(exitkind=0;exitkind<=1;exitkind++) { //--- Prepare problem if(entrykind==0) entrystate=-1; else entrystate=0; //--- check if(exitkind==0) exitstate=-1; else exitstate=n-1; //--- allocation xy.Resize(2*n,n); for(i=0;i<=CAp::Rows(xy)-1;i++) { for(j=0;j<=CAp::Cols(xy)-1;j++) xy[i].Set(j,CMath::RandomReal()); } //--- Test that single bound constraint on non-entry //--- non-exit elements of P is satisfied. //--- NOTE 1: this test needs N>=4 because smaller values //--- can give us inconsistent constraints if(!CAp::Assert(n>=4,"TestBC: expectation failed")) return; //--- change values ic=1+CMath::RandomInteger(n-2); jc=1+CMath::RandomInteger(n-2); //--- check if(CMath::RandomReal()>0.5) vl=0.3*CMath::RandomReal(); else vl=CInfOrNaN::NegativeInfinity(); //--- check if(CMath::RandomReal()>0.5) vu=0.5+0.3*CMath::RandomReal(); else vu=CInfOrNaN::PositiveInfinity(); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddBC(s,ic,jc,vl,vu); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { err=err || p[ic][jc]vu; } else err=true; //--- Test interaction with default "sum-to-one" constraint //--- on columns of P. //--- We set N-1 bound constraints on random non-exit column //--- of P,which are inconsistent with this default constraint //--- (sum will be greater that 1.0). //--- Algorithm must detect inconsistency. //--- NOTE: //--- 1. we do not set constraints for the first element of //--- the column,because this element may be constrained by //--- "exit state" constraint. //--- 2. this test needs N>=3 if(!CAp::Assert(n>=3,"TestEC: expectation failed")) return; jc=CMath::RandomInteger(n-1); vl=0.85; vu=0.95; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); for(i=1;i<=n-1;i++) CMarkovCPD::MCPDAddBC(s,i,jc,vl,vu); //--- function calls CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; //--- Test interaction with constrains on entry states. //--- When model has entry state,corresponding row of P //--- must be zero. We try to set two kinds of constraints //--- on random element of this row: //--- * bound constraint with zero lower bound,which must be consistent //--- * bound constraint with non-zero lower bound,which must be inconsistent if(entrystate>=0) { jc=CMath::RandomInteger(n); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.0,1.0); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.5,1.0); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; } //--- Test interaction with constrains on exit states. //--- When model has exit state,corresponding column of P //--- must be zero. We try to set two kinds of constraints //--- on random element of this column: //--- * bound constraint with zero lower bound,which must be consistent //--- * bound constraint with non-zero lower bound,which must be inconsistent if(exitstate>=0) { ic=CMath::RandomInteger(n); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.0,1.0); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.5,1.0); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; } //--- Test SetBC() call - we constrain subset of non-entry //--- non-exit elements and test it. if(!CAp::Assert(n>=4,"TestBC: expectation failed")) return; //--- allocation bndl.Resize(n,n); bndu.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { bndl[i].Set(j,CInfOrNaN::NegativeInfinity()); bndu[i].Set(j,CInfOrNaN::PositiveInfinity()); } } //--- change values for(j=1;j<=n-2;j++) { i=1+CMath::RandomInteger(n-2); bndl[i].Set(j,0.5-0.1*CMath::RandomReal()); bndu[i].Set(j,0.5+0.1*CMath::RandomReal()); } //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetBC(s,bndl,bndu); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { err=err || p[i][j]bndu[i][j]; } } } else err=true; } } } } //+------------------------------------------------------------------+ //| Test bound constraints. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMCPDUnit::TestLC(bool &err) { //--- create variables int n=0; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int i=0; int j=0; int k=0; int t=0; int jc=0; double v=0; double threshold=0; //--- create array int ct[]; //--- create matrix CMatrixDouble p; CMatrixDouble c; CMatrixDouble xy; //--- objects of classes CMCPDState s; CMCPDReport rep; //--- initialization threshold=1.0E5*CMath::m_machineepsilon; //--- We try different problems with following properties: //--- * N is large enough - we won't have problems with inconsistent constraints //--- * first state is either "entry" or "normal" //--- * last state is either "exit" or "normal" //--- * we have one long random track //--- We test several properties which are described in comments below for(n=4;n<=6;n++) { for(entrykind=0;entrykind<=1;entrykind++) { for(exitkind=0;exitkind<=1;exitkind++) { //--- Prepare problem if(entrykind==0) entrystate=-1; else entrystate=0; //--- check if(exitkind==0) exitstate=-1; else exitstate=n-1; //--- allocation xy.Resize(2*n,n); for(i=0;i<=CAp::Rows(xy)-1;i++) { for(j=0;j<=CAp::Cols(xy)-1;j++) xy[i].Set(j,CMath::RandomReal()); } //--- Test that single linear equality/inequality constraint //--- on non-entry non-exit elements of P is satisfied. //--- NOTE 1: this test needs N>=4 because smaller values //--- can give us inconsistent constraints //--- NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N) //--- is always feasible. It guarantees that there always exists //--- at least one feasible point //--- NOTE 3: If we have inequality constraint,we "shift" right part //--- in order to make feasible some neighborhood of P=(1/N ... 1/N). if(!CAp::Assert(n>=4,"TestLC: expectation failed")) return; //--- allocation c.Resize(1,n*n+1); ArrayResize(ct,1); //--- calculation v=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(((i==0 || i==n-1) || j==0) || j==n-1) c[0].Set(i*n+j,0); else { c[0].Set(i*n+j,CMath::RandomReal()); v=v+c[0][i*n+j]*(1.0/(double)n); } } } //--- change value c[0].Set(n*n,v); ct[0]=CMath::RandomInteger(3)-1; //--- check if(ct[0]<0) c[0].Set(n*n,c[0][n*n]+0.1); //--- check if(ct[0]>0) c[0].Set(n*n,c[0][n*n]-0.1); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { v=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) v=v+p[i][j]*c[0][i*n+j]; } //--- check if(ct[0]<0) err=err || v>=c[0][n*n]+threshold; //--- check if(ct[0]==0) err=err || MathAbs(v-c[0][n*n])>=threshold; //--- check if(ct[0]>0) err=err || v<=c[0][n*n]-threshold; } else err=true; //--- Test interaction with default "sum-to-one" constraint //--- on columns of P. //--- We set linear constraint which has for "sum-to-X" on //--- on random non-exit column of P. This constraint can be //--- either consistent (X=1.0) or inconsistent (X<>1.0) with //--- this default constraint. //--- Algorithm must detect inconsistency. //--- NOTE: //--- 1. this test needs N>=2 if(!CAp::Assert(n>=2,"TestLC: expectation failed")) return; jc=CMath::RandomInteger(n-1); //--- allocation c.Resize(1,n*n+1); ArrayResize(ct,1); //--- change values for(i=0;i<=n*n-1;i++) c[0].Set(i,0.0); for(i=0;i<=n-1;i++) c[0].Set(n*i+jc,1.0); c[0].Set(n*n,1.0); ct[0]=0; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; c[0].Set(n*n,2.0); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; //--- Test interaction with constrains on entry states. //--- When model has entry state,corresponding row of P //--- must be zero. We try to set two kinds of constraints //--- on elements of this row: //--- * sums-to-zero constraint,which must be consistent //--- * sums-to-one constraint,which must be inconsistent if(entrystate>=0) { c.Resize(1,n*n+1); ArrayResize(ct,1); for(i=0;i<=n*n-1;i++) c[0].Set(i,0.0); for(j=0;j<=n-1;j++) c[0].Set(n*entrystate+j,1.0); ct[0]=0; c[0].Set(n*n,0.0); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; c[0].Set(n*n,1.0); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; } //--- Test interaction with constrains on exit states. //--- When model has exit state,corresponding column of P //--- must be zero. We try to set two kinds of constraints //--- on elements of this column: //--- * sums-to-zero constraint,which must be consistent //--- * sums-to-one constraint,which must be inconsistent if(exitstate>=0) { c.Resize(1,n*n+1); ArrayResize(ct,1); //--- change values for(i=0;i<=n*n-1;i++) c[0].Set(i,0.0); for(i=0;i<=n-1;i++) c[0].Set(n*i+exitstate,1.0); ct[0]=0; c[0].Set(n*n,0.0); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype<=0; c[0].Set(n*n,1.0); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- search errors err=err || rep.m_terminationtype!=-3; } } } } //--- Final test - we generate several random constraints and //--- test SetLC() function. //--- NOTES: //--- 1. Constraints are generated is such a way that P=(1/N ... 1/N) //--- is always feasible. It guarantees that there always exists //--- at least one feasible point //--- 2. For simplicity of the test we do not use entry/exit states //--- in our model for(n=1;n<=4;n++) { for(k=1;k<=2*n;k++) { //--- Generate track xy.Resize(2*n,n); for(i=0;i<=CAp::Rows(xy)-1;i++) { for(j=0;j<=CAp::Cols(xy)-1;j++) xy[i].Set(j,CMath::RandomReal()); } //--- Generate random constraints c.Resize(k,n*n+1); ArrayResize(ct,k); //--- calculation for(i=0;i<=k-1;i++) { //--- Generate constraint and its right part c[i].Set(n*n,0); for(j=0;j<=n*n-1;j++) { c[i].Set(j,2*CMath::RandomReal()-1); c[i].Set(n*n,c[i][n*n]+c[i][j]*(1.0/(double)n)); } ct[i]=CMath::RandomInteger(3)-1; //--- If we have inequality constraint,we "shift" right part //--- in order to make feasible some neighborhood of P=(1/N ... 1/N). if(ct[i]<0) c[i].Set(n*n,c[i][n*n]+0.1); //--- check if(ct[i]>0) c[i].Set(n*n,c[i][n*n]-0.1); } //--- Test CreateEE(n,-1,-1,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,k); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { for(t=0;t<=k-1;t++) { //--- change values v=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) v=v+p[i][j]*c[t][i*n+j]; } //--- check if(ct[t]<0) err=err || v>=c[t][n*n]+threshold; //--- check if(ct[t]==0) err=err || MathAbs(v-c[t][n*n])>=threshold; //--- check if(ct[t]>0) err=err || v<=c[t][n*n]-threshold; } } else err=true; } } } //+------------------------------------------------------------------+ //| This function is used to create MCPD object with arbitrary | //| combination of entry and exit states | //+------------------------------------------------------------------+ static void CTestMCPDUnit::CreateEE(const int n,const int entrystate, const int exitstate,CMCPDState &s) { //--- check if(entrystate<0 && exitstate<0) CMarkovCPD::MCPDCreate(n,s); //--- check if(entrystate>=0 && exitstate<0) CMarkovCPD::MCPDCreateEntry(n,entrystate,s); //--- check if(entrystate<0 && exitstate>=0) CMarkovCPD::MCPDCreateExit(n,exitstate,s); //--- check if(entrystate>=0 && exitstate>=0) CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s); } //+------------------------------------------------------------------+ //| Testing class CFbls | //+------------------------------------------------------------------+ class CTestFblsUnit { public: //--- constructor, destructor CTestFblsUnit(void); ~CTestFblsUnit(void); //--- public method static bool TestFbls(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestFblsUnit::CTestFblsUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestFblsUnit::~CTestFblsUnit(void) { } //+------------------------------------------------------------------+ //| Testing | //+------------------------------------------------------------------+ static bool CTestFblsUnit::TestFbls(const bool silent) { //--- create variables int n=0; int m=0; int mx=0; int i=0; int j=0; bool waserrors; bool cgerrors; double v=0; double v1=0; double v2=0; double alpha=0; double e1=0; double e2=0; int i_=0; //--- create arrays double tmp1[]; double tmp2[]; double b[]; double x[]; double xe[]; double buf[]; //--- create matrix CMatrixDouble a; //--- object of class CFblsLinCgState cgstate; //--- initialization mx=10; waserrors=false; cgerrors=false; //--- Test CG solver: //--- * generate problem (A,B,Alpha,XE - exact solution) and initial approximation X //--- * E1=||A'A*x-b|| //--- * solve //--- * E2=||A'A*x-b|| //--- * test that E2<0.001*E1 for(n=1;n<=mx;n++) { for(m=1;m<=mx;m++) { //--- allocation a.Resize(m,n); ArrayResize(b,n); ArrayResize(x,n); ArrayResize(xe,n); ArrayResize(tmp1,m); ArrayResize(tmp2,n); //--- init A,alpha,B,X (initial approximation),XE (exact solution) //--- X is initialized in such way that is has no chances to be equal to XE. for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- change values alpha=CMath::RandomReal()+0.1; for(i=0;i<=n-1;i++) { b[i]=2*CMath::RandomReal()-1; xe[i]=2*CMath::RandomReal()-1; x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal()); } //--- Test dense CG (which solves A'A*x=b and accepts dense A) for(i=0;i<=n-1;i++) x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal()); //--- function calls CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0); CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0); //--- calculation for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]+alpha*x[i_]; for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]-b[i_]; //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=tmp2[i_]*tmp2[i_]; e1=MathSqrt(v); //--- function calls CFbls::FblsSolveCGx(a,m,n,alpha,b,x,buf); CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0); CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0); //--- calculation for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]+alpha*x[i_]; for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]-b[i_]; //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=tmp2[i_]*tmp2[i_]; e2=MathSqrt(v); //--- search errors cgerrors=cgerrors || e2>0.001*e1; //--- Test sparse CG (which relies on reverse communication) for(i=0;i<=n-1;i++) x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal()); //--- function calls CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0); CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0); //--- calculation for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]+alpha*x[i_]; for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]-b[i_]; //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=tmp2[i_]*tmp2[i_]; e1=MathSqrt(v); //--- function call CFbls::FblsCGCreate(x,b,n,cgstate); //--- cycle while(CFbls::FblsCGIteration(cgstate)) { //--- function calls CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_x,0,tmp1,0); CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,cgstate.m_ax,0); for(i_=0;i_<=n-1;i_++) cgstate.m_ax[i_]=cgstate.m_ax[i_]+alpha*cgstate.m_x[i_]; //--- change values v1=0.0; for(i_=0;i_<=m-1;i_++) v1+=tmp1[i_]*tmp1[i_]; v2=0.0; for(i_=0;i_<=n-1;i_++) v2+=cgstate.m_x[i_]*cgstate.m_x[i_]; cgstate.m_xax=v1+alpha*v2; } //--- function calls CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_xk,0,tmp1,0); CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0); //--- calculation for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]+alpha*cgstate.m_xk[i_]; for(i_=0;i_<=n-1;i_++) tmp2[i_]=tmp2[i_]-b[i_]; //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=tmp2[i_]*tmp2[i_]; e2=MathSqrt(v); //--- search errors cgerrors=cgerrors || MathAbs(e1-cgstate.m_e1)>100*CMath::m_machineepsilon*e1; cgerrors=cgerrors || MathAbs(e2-cgstate.m_e2)>100*CMath::m_machineepsilon*e1; cgerrors=cgerrors || e2>0.001*e1; } } //--- report waserrors=cgerrors; //--- check if(!silent) { Print("TESTING FBLS"); Print("CG ERRORS: "); //--- check if(cgerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CMinLBFGS | //+------------------------------------------------------------------+ class CTestMinLBFGSUnit { private: //--- private methods static void TestFunc1(CMinLBFGSState &state); static void TestFunc2(CMinLBFGSState &state); static void TestFunc3(CMinLBFGSState &state); static void CalcIIP2(CMinLBFGSState &state,const int n); static void TestPreconditioning(bool &err); static void TestOther(bool &err); public: //--- constructor, destructor CTestMinLBFGSUnit(void); ~CTestMinLBFGSUnit(void); //--- public method static bool TestMinLBFGS(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMinLBFGSUnit::CTestMinLBFGSUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMinLBFGSUnit::~CTestMinLBFGSUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CMinLBFGS | //+------------------------------------------------------------------+ static bool CTestMinLBFGSUnit::TestMinLBFGS(const bool silent) { //--- create variables bool waserrors; bool referror; bool nonconverror; bool eqerror; bool converror; bool crashtest; bool othererrors; bool restartserror; bool precerror; int n=0; int m=0; int i=0; int j=0; double v=0; int maxits=0; double diffstep=0; int dkind=0; int i_=0; //--- create arrays double x[]; double xe[]; double b[]; double xlast[]; double diagh[]; //--- create matrix CMatrixDouble a; //--- objects of classes CMinLBFGSState state; CMinLBFGSReport rep; //--- initialization waserrors=false; precerror=false; nonconverror=false; restartserror=false; eqerror=false; converror=false; crashtest=false; othererrors=false; referror=false; //--- function calls TestPreconditioning(precerror); TestOther(othererrors); //--- Reference problem diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,3); n=3; m=2; x[0]=100*CMath::RandomReal()-50; x[1]=100*CMath::RandomReal()-50; x[2]=100*CMath::RandomReal()-50; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors referror=((rep.m_terminationtype<=0 || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001; } //--- nonconvex problems with complex surface: we start from point with very small //--- gradient,but we need ever smaller gradient in the next step due to //--- Wolfe conditions. diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,1); n=1; m=1; v=-100; //--- calculation while(v<0.1) { x[0]=v; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0])); //--- check if(state.m_needfg) state.m_g[0]=(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0])); } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors nonconverror=(nonconverror || rep.m_terminationtype<=0) || MathAbs(x[0])>0.001; v=v+0.1; } } //--- F2 problem with restarts: //--- * make several iterations and restart BEFORE termination //--- * iterate and restart AFTER termination //--- NOTE: step is bounded from above to avoid premature convergence diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,3); //--- change values n=3; m=2; x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function calls CMinLBFGS::MinLBFGSSetStpMax(state,0.1); CMinLBFGS::MinLBFGSSetCond(state,0.0000001,0.0,0.0,0); //--- calculation for(i=0;i<=10;i++) { //--- check if(!CMinLBFGS::MinLBFGSIteration(state)) break; TestFunc2(state); } //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc2(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01; //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc2(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01; } //--- Linear equations diffstep=1.0E-6; for(n=1;n<=10;n++) { //--- Prepare task a.Resize(n,n); ArrayResize(x,n); ArrayResize(xe,n); ArrayResize(b,n); //--- change values for(i=0;i<=n-1;i++) xe[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); a[i].Set(i,a[i][i]+3*MathSign(a[i][i])); } //--- calculation for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_]; b[i]=v; } //--- Test different M/DKind for(m=1;m<=n;m++) { for(dkind=0;dkind<=1;dkind++) { //--- Solve task for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- check if(state.m_needfg) { for(i=0;i<=n-1;i++) state.m_g[i]=0; } for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*state.m_x[i_]; //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr(v-b[i]); //--- check if(state.m_needfg) { for(j=0;j<=n-1;j++) state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j]; } } } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors eqerror=eqerror || rep.m_terminationtype<=0; for(i=0;i<=n-1;i++) eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001; } } } //--- Testing convergence properties diffstep=1.0E-6; for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,3); //--- change values n=3; m=2; for(i=0;i<=2;i++) x[i]=6*CMath::RandomReal()-3; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0.001,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=converror || rep.m_terminationtype!=4; //--- change values for(i=0;i<=2;i++) x[i]=6*CMath::RandomReal()-3; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0.001,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=converror || rep.m_terminationtype!=1; //--- change values for(i=0;i<=2;i++) x[i]=6*CMath::RandomReal()-3; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0.001,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=converror || rep.m_terminationtype!=2; //--- change values for(i=0;i<=2;i++) x[i]=2*CMath::RandomReal()-1; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0,10); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=(converror || rep.m_terminationtype!=5) || rep.m_iterationscount!=10; } //--- Crash test: too many iterations on a simple tasks //--- May fail when encounter zero step,underflow or something like that ArrayResize(x,3); n=3; m=2; maxits=10000; for(i=0;i<=2;i++) x[i]=6*CMath::RandomReal()-3; //--- function calls CMinLBFGS::MinLBFGSCreate(n,m,x,state); CMinLBFGS::MinLBFGSSetCond(state,0,0,0,maxits); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors crashtest=crashtest || rep.m_terminationtype<=0; //--- end waserrors=((((((referror || nonconverror) || eqerror) || converror) || crashtest) || othererrors) || restartserror) || precerror; //--- check if(!silent) { Print("TESTING L-BFGS OPTIMIZATION"); Print("REFERENCE PROBLEM: "); //--- check if(referror) Print("FAILED"); else Print("OK"); Print("NON-CONVEX PROBLEM: "); //--- check if(nonconverror) Print("FAILED"); else Print("OK"); Print("LINEAR EQUATIONS: "); //--- check if(eqerror) Print("FAILED"); else Print("OK"); Print("RESTARTS: "); //--- check if(restartserror) Print("FAILED"); else Print("OK"); Print("PRECONDITIONER: "); //--- check if(precerror) Print("FAILED"); else Print("OK"); Print("CONVERGENCE PROPERTIES: "); //--- check if(converror) Print("FAILED"); else Print("OK"); Print("CRASH TEST: "); //--- check if(crashtest) Print("FAILED"); else Print("OK"); Print("OTHER PROPERTIES: "); //--- check if(othererrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Calculate test function #1 | //| It may show very interesting behavior when optimized with | //| 'x[0]>=ln(2)' constraint. | //+------------------------------------------------------------------+ static void CTestMinLBFGSUnit::TestFunc1(CMinLBFGSState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { //--- calculation state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { //--- calculation state.m_g[0]=MathSqrt(CMath::m_maxrealnumber); state.m_g[1]=0; state.m_g[2]=0; } } } //+------------------------------------------------------------------+ //| Calculate test function #2 | //| Simple variation of #1,much more nonlinear,which makes unlikely | //| premature convergence of algorithm. | //+------------------------------------------------------------------+ static void CTestMinLBFGSUnit::TestFunc2(CMinLBFGSState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { //--- calculation state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=4*state.m_x[1]*CMath::Sqr(state.m_x[1]); state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { //--- calculation state.m_g[0]=MathSqrt(CMath::m_maxrealnumber); state.m_g[1]=0; state.m_g[2]=0; } } } //+------------------------------------------------------------------+ //| Calculate test function #3 | //| Simple variation of #1, much more nonlinear, with non-zero value | //| at minimum. It achieve two goals: | //| * makes unlikely premature convergence of algorithm . | //| * solves some issues with EpsF stopping condition which arise | //| when F(minimum) is zero | //+------------------------------------------------------------------+ static void CTestMinLBFGSUnit::TestFunc3(CMinLBFGSState &state) { //--- create a variable double s=0; //--- initialization s=0.001; //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { //--- calculation state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { //--- calculation state.m_g[0]=MathSqrt(CMath::m_maxrealnumber); state.m_g[1]=0; state.m_g[2]=0; } } } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ static void CTestMinLBFGSUnit::CalcIIP2(CMinLBFGSState &state,const int n) { //--- create variables int i=0; //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- calculation for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]); //--- check if(state.m_needfg) state.m_g[i]=CMath::Sqr(i*i+1)*2*state.m_x[i]; } } //+------------------------------------------------------------------+ //| This function tests preconditioning | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ static void CTestMinLBFGSUnit::TestPreconditioning(bool &err) { //--- create variables int pass=0; int n=0; int m=0; int i=0; int j=0; int k=0; int cntb1=0; int cntb2=0; int cntg1=0; int cntg2=0; double epsg=0; int pkind=0; //--- create arrays double x[]; double s[]; double diagh[]; //--- create matrix CMatrixDouble a; //--- objects of classes CMinLBFGSState state; CMinLBFGSReport rep; //--- initialization m=1; k=50; epsg=1.0E-10; //--- Preconditioner test1. //--- If //--- * B1 is default preconditioner //--- * B2 is Cholesky preconditioner with unit diagonal //--- * G1 is Cholesky preconditioner based on exact Hessian with perturbations //--- * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix //--- then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..). //--- "Worse" means more iterations to converge. //--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) and L-BFGS //--- optimizer with deliberately small M=1. //--- N - problem size //--- PKind - zero for upper triangular preconditioner,one for lower triangular. //--- K - number of repeated passes (should be large enough to average out random factors) for(n=10;n<=15;n++) { pkind=CMath::RandomInteger(2); ArrayResize(x,n); for(i=0;i<=n-1;i++) x[i]=0; //--- function call CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- Test it with default preconditioner CMinLBFGS::MinLBFGSSetPrecDefault(state); cntb1=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) CalcIIP2(state,n); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); cntb1=cntb1+rep.m_iterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- Test it with unit preconditioner a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i==j) a[i].Set(i,1); else a[i].Set(j,0); } } //--- function call CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0); //--- change values cntb2=0; for(pass=0;pass<=k-1;pass++) { for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) CalcIIP2(state,n); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); cntb2=cntb2+rep.m_iterationscount; //--- search errors err=err || rep.m_terminationtype<=0; } //--- Test it with perturbed Hessian preconditioner a.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(i==j) a[i].Set(i,(i*i+1)*(0.8+0.4*CMath::RandomReal())); else { //--- check if((pkind==0 && j>i) || (pkind==1 && jfprev; //--- check if(fprev==CMath::m_maxrealnumber) { for(i=0;i<=n-1;i++) err=err || state.m_x[i]!=x[i]; } //--- change values fprev=state.m_f; for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; } } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors for(i=0;i<=n-1;i++) err=err || x[i]!=xlast[i]; //--- Test differentiation vs. analytic gradient //--- (first one issues NeedF requests,second one issues NeedFG requests) n=50; m=5; diffstep=1.0E-6; //--- calculation for(dkind=0;dkind<=1;dkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); for(i=0;i<=n-1;i++) x[i]=1; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0,n/2); wasf=false; wasfg=false; //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0;i<=n-1;i++) { //--- check if(state.m_needf || state.m_needfg) state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]); //--- check if(state.m_needfg) state.m_g[i]=2*(1+i)*state.m_x[i]; } //--- search errors wasf=wasf || state.m_needf; wasfg=wasfg || state.m_needfg; } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- check if(dkind==0) err=(err || wasf) || !wasfg; //--- check if(dkind==1) err=(err || !wasf) || wasfg; } //--- Test that numerical differentiation uses scaling. //--- In order to test that we solve simple optimization //--- problem: min(x^2) with initial x equal to 0.0. //--- We choose random DiffStep and S,then we check that //--- optimizer evaluates function at +-DiffStep*S only. ArrayResize(x,1); ArrayResize(s,1); //--- change values diffstep=CMath::RandomReal()*1.0E-6; s[0]=MathExp(CMath::RandomReal()*4-2); x[0]=0; //--- function calls CMinLBFGS::MinLBFGSCreateF(1,1,x,diffstep,state); CMinLBFGS::MinLBFGSSetCond(state,1.0E-6,0,0,0); CMinLBFGS::MinLBFGSSetScale(state,s); v=0; //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { state.m_f=CMath::Sqr(state.m_x[0]); v=MathMax(v,MathAbs(state.m_x[0])); } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); r=v/(s[0]*diffstep); //--- search errors err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon); //--- test maximum step n=1; m=1; //--- allocation ArrayResize(x,n); x[0]=100; stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinLBFGS::MinLBFGSCreate(n,m,x,state); CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0); CMinLBFGS::MinLBFGSSetStpMax(state,stpmax); CMinLBFGS::MinLBFGSSetXRep(state,true); xprev=x[0]; //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]); //--- search errors err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); } //--- check if(state.m_xupdated) { //--- search errors err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); xprev=state.m_x[0]; } } //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100] //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set stringent stopping conditions (we try EpsG and EpsX) //--- * and we test that in the extremum stopping conditions are //--- satisfied subject to the current scaling coefficients. tmpeps=1.0E-10; m=1; for(n=1;n<=10;n++) { for(pkind=0;pkind<=2;pkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); ArrayResize(a,n); ArrayResize(s,n); ArrayResize(h,n); //--- change values for(i=0;i<=n-1;i++) { x[i]=CMath::RandomReal()+1; a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1)); } //--- function call CMinLBFGS::MinLBFGSCreate(n,m,x,state); CMinLBFGS::MinLBFGSSetScale(state,s); CMinLBFGS::MinLBFGSSetXRep(state,true); //--- check if(pkind==1) CMinLBFGS::MinLBFGSSetPrecDiag(state,h); //--- check if(pkind==2) CMinLBFGS::MinLBFGSSetPrecScale(state); //--- Test gradient-based stopping condition for(i=0;i<=n-1;i++) x[i]=CMath::RandomReal()+1; //--- function calls CMinLBFGS::MinLBFGSSetCond(state,tmpeps,0,0,0); CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4); state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3); } } } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { err=true; return; } //--- change value v=0; for(i=0;i<=n-1;i++) v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3)); v=MathSqrt(v); //--- search errors err=err || v>tmpeps; //--- Test step-based stopping condition for(i=0;i<=n-1;i++) x[i]=CMath::RandomReal()+1; hasxlast=false; //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,tmpeps,0); CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needfg) { state.m_f=0; for(i=0;i<=n-1;i++) { state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4); state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3); } } //--- check if(state.m_xupdated) { //--- check if(hasxlast) { lastscaledstep=0; for(i=0;i<=n-1;i++) lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]); lastscaledstep=MathSqrt(lastscaledstep); } else lastscaledstep=0; //--- change values for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; hasxlast=true; } } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- check if(rep.m_terminationtype<=0) { err=true; return; } //--- search errors err=err || lastscaledstep>tmpeps; } } //--- Check correctness of the "trimming". //--- Trimming is a technique which is used to help algorithm //--- cope with unbounded functions. In order to check this //--- technique we will try to solve following optimization //--- problem: //--- min f(x) subject to no constraints on X //--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999=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.999999epsg; } } } //+------------------------------------------------------------------+ //| Testing class CMLPBase è CMLPTrain | //+------------------------------------------------------------------+ class CTestMLPTrainUnit { private: //--- private methods static void CreateNetwork(CMultilayerPerceptron &network,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout); static void UnsetNetwork(CMultilayerPerceptron &network); static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); static void TestGradient(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); static void TestHessian(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); public: //--- constructor, destructor CTestMLPTrainUnit(void); ~CTestMLPTrainUnit(void); //--- public method static bool TestMLPTrain(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMLPTrainUnit::CTestMLPTrainUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMLPTrainUnit::~CTestMLPTrainUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CMLPBase è CMLPTrain | //+------------------------------------------------------------------+ static bool CTestMLPTrainUnit::TestMLPTrain(const bool silent) { //--- create variables bool waserrors; int passcount=0; int maxn=0; int maxhid=0; int info=0; int nf=0; int nl=0; int nhid1=0; int nhid2=0; int nkind=0; int i=0; int ncount=0; bool inferrors; bool procerrors; bool graderrors; bool hesserrors; bool trnerrors; //--- objects of classes êëàññîâ CMultilayerPerceptron network; CMultilayerPerceptron network2; CMLPReport rep; CMLPCVReport cvrep; CMatrixDouble xy; CMatrixDouble valxy; //--- initialization waserrors=false; inferrors=false; procerrors=false; graderrors=false; hesserrors=false; trnerrors=false; passcount=10; maxn=4; maxhid=4; //--- General multilayer network tests for(nf=1;nf<=maxn;nf++) { for(nl=1;nl<=maxn;nl++) { for(nhid1=0;nhid1<=maxhid;nhid1++) { for(nhid2=0;nhid2<=0;nhid2++) { for(nkind=0;nkind<=3;nkind++) { //--- Skip meaningless parameters combinations if(nkind==1 && nl<2) continue; //--- check if(nhid1==0 && nhid2!=0) continue; //--- Tests TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors); TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors); TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,graderrors); TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors); } } } } } //--- Test network training on simple XOR problem xy.Resize(4,3); xy[0].Set(0,-1); xy[0].Set(1,-1); xy[0].Set(2,-1); xy[1].Set(0,1); xy[1].Set(1,-1); xy[1].Set(2,1); xy[2].Set(0,-1); xy[2].Set(1,1); xy[2].Set(2,1); xy[3].Set(0,1); xy[3].Set(1,1); xy[3].Set(2,-1); //--- function calls CMLPBase::MLPCreate1(2,2,1,network); CMLPTrain::MLPTrainLM(network,xy,4,0.001,10,info,rep); //--- search errors trnerrors=trnerrors || CMLPBase::MLPRMSError(network,xy,4)>0.1; //--- Test CV on random noisy problem ncount=100; xy.Resize(ncount,2); //--- change values for(i=0;i<=ncount-1;i++) { xy[i].Set(0,2*CMath::RandomReal()-1); xy[i].Set(1,CMath::RandomInteger(4)); } //--- function calls CMLPBase::MLPCreateC0(1,4,network); CMLPTrain::MLPKFoldCVLM(network,xy,ncount,0.001,5,10,info,rep,cvrep); //--- Final report waserrors=(((inferrors || procerrors) || graderrors) || hesserrors) || trnerrors; //--- check if(!silent) { Print("MLP TEST"); Print("INFORMATIONAL FUNCTIONS: "); //--- check if(!inferrors) Print("OK"); else Print("FAILED"); Print("BASIC PROCESSING: "); //--- check if(!procerrors) Print("OK"); else Print("FAILED"); Print("GRADIENT CALCULATION: "); //--- check if(!graderrors) Print("OK"); else Print("FAILED"); Print("HESSIAN CALCULATION: "); //--- check if(!hesserrors) Print("OK"); else Print("FAILED"); Print("TRAINING: "); //--- check if(!trnerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Network creation | //| This function creates network with desired structure. Network is | //| created using one of the three methods: | //| a) straighforward creation using MLPCreate???() | //| b) MLPCreate???() for proxy object,which is copied with | //| PassThroughSerializer() | //| c) MLPCreate???() for proxy object,which is copied with MLPCopy()| //| One of these methods is chosen with probability 1/3. | //+------------------------------------------------------------------+ static void CTestMLPTrainUnit::CreateNetwork(CMultilayerPerceptron &network, const int nkind,const double a1, const double a2,const int nin, const int nhid1,const int nhid2, const int nout) { //--- create a variable int mkind=0; //--- object of class CMultilayerPerceptron tmp; //--- check if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error")) return; //--- check if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error")) return; //--- check if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error")) return; //--- change value mkind=CMath::RandomInteger(3); //--- check if(nhid1==0) { //--- No hidden layers if(nkind==0) { //--- check if(mkind==0) CMLPBase::MLPCreate0(nin,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreate0(nin,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreate0(nin,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==1) { //--- check if(mkind==0) CMLPBase::MLPCreateC0(nin,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateC0(nin,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateC0(nin,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==2) { //--- check if(mkind==0) CMLPBase::MLPCreateB0(nin,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==3) { //--- check if(mkind==0) CMLPBase::MLPCreateR0(nin,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } //--- function call CMLPBase::MLPRandomizeFull(network); return; } //--- check if(nhid2==0) { //--- One hidden layer if(nkind==0) { //--- check if(mkind==0) CMLPBase::MLPCreate1(nin,nhid1,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreate1(nin,nhid1,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreate1(nin,nhid1,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==1) { //--- check if(mkind==0) CMLPBase::MLPCreateC1(nin,nhid1,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==2) { //--- check if(mkind==0) CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==3) { //--- check if(mkind==0) CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } //--- function call CMLPBase::MLPRandomizeFull(network); return; } //--- Two hidden layers if(nkind==0) { //--- check if(mkind==0) CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==1) { //--- check if(mkind==0) CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==2) { //--- check if(mkind==0) CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==3) { //--- check if(mkind==0) CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } //--- function call CMLPBase::MLPRandomizeFull(network); } //+------------------------------------------------------------------+ //| Unsets network (initialize it to smallest network possible | //+------------------------------------------------------------------+ static void CTestMLPTrainUnit::UnsetNetwork(CMultilayerPerceptron &network) { //--- function call CMLPBase::MLPCreate0(1,1,network); } //+------------------------------------------------------------------+ //| Informational functions test | //+------------------------------------------------------------------+ static void CTestMLPTrainUnit::TestInformational(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int n1=0; int n2=0; int wcount=0; int i=0; int j=0; int k=0; double threshold=0; int nlayers=0; int nmax=0; bool issoftmax; double mean=0; double sigma=0; int fkind=0; double c=0; double f=0; double df=0; double d2f=0; double s=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble neurons; //--- object of class CMultilayerPerceptron network; //--- initialization threshold=100000*CMath::m_machineepsilon; //--- function call CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout); //--- test MLPProperties() CMLPBase::MLPProperties(network,n1,n2,wcount); //--- search errors err=((err || n1!=nin) || n2!=nout) || wcount<=0; //--- Test network geometry functions //--- In order to do this we calculate neural network output using //--- informational functions only,and compare results with ones //--- obtained with MLPProcess(): //--- 1. we allocate 2-dimensional array of neurons and fill it by zeros //--- 2. we full first layer of neurons by input values //--- 3. we move through array,calculating values of subsequent layers //--- 4. if we have classification network,we SOFTMAX-normalize output layer //--- 5. we apply scaling to the outputs //--- 6. we compare results with ones obtained by MLPProcess() //--- NOTE: it is important to do (4) before (5),because on SOFTMAX network //--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order //--- to test it implicitly,we apply it to the classifier results //--- (already normalized). If one of the coefficients deviates from //--- expected values,we will get error during (6). nlayers=2; nmax=MathMax(nin,nout); issoftmax=nkind==1; //--- check if(nhid1!=0) { nlayers=3; nmax=MathMax(nmax,nhid1); } //--- check if(nhid2!=0) { nlayers=4; nmax=MathMax(nmax,nhid2); } //--- allocation neurons.Resize(nlayers,nmax); for(i=0;i<=nlayers-1;i++) { for(j=0;j<=nmax-1;j++) neurons[i].Set(j,0); } //--- allocation ArrayResize(x,nin); //--- change values for(i=0;i<=nin-1;i++) x[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(y,nout); //--- change values for(i=0;i<=nout-1;i++) y[i]=2*CMath::RandomReal()-1; for(j=0;j<=nin-1;j++) { //--- function call CMLPBase::MLPGetInputScaling(network,j,mean,sigma); neurons[0].Set(j,(x[j]-mean)/sigma); } //--- calculation for(i=1;i<=nlayers-1;i++) { for(j=0;j<=CMLPBase::MLPGetLayerSize(network,i)-1;j++) { for(k=0;k<=CMLPBase::MLPGetLayerSize(network,i-1)-1;k++) neurons[i].Set(j,neurons[i][j]+CMLPBase::MLPGetWeight(network,i-1,k,i,j)*neurons[i-1][k]); //--- function calls CMLPBase::MLPGetNeuronInfo(network,i,j,fkind,c); CMLPBase::MLPActivationFunction(neurons[i][j]-c,fkind,f,df,d2f); neurons[i].Set(j,f); } } //--- check if(nkind==1) { s=0; for(j=0;j<=nout-1;j++) s=s+MathExp(neurons[nlayers-1][j]); for(j=0;j<=nout-1;j++) neurons[nlayers-1].Set(j,MathExp(neurons[nlayers-1][j])/s); } //--- calculation for(j=0;j<=nout-1;j++) { //--- function call CMLPBase::MLPGetOutputScaling(network,j,mean,sigma); neurons[nlayers-1].Set(j,neurons[nlayers-1][j]*sigma+mean); } //--- function call CMLPBase::MLPProcess(network,x,y); //--- search errors for(j=0;j<=nout-1;j++) err=err || MathAbs(neurons[nlayers-1][j]-y[j])>threshold; } //+------------------------------------------------------------------+ //| Processing functions test | //+------------------------------------------------------------------+ static void CTestMLPTrainUnit::TestProcessing(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int n1=0; int n2=0; int wcount=0; bool zeronet; double a1=0; double a2=0; int pass=0; int i=0; bool allsame; double v=0; int i_=0; //--- create arrays double x1[]; double x2[]; double y1[]; double y2[]; //--- objects of classes CMultilayerPerceptron network; CMultilayerPerceptron network2; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) return; //--- Prepare network a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- function calls CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); //--- Initialize arrays ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y1,nout); ArrayResize(y2,nout); //--- Main cycle for(pass=1;pass<=passcount;pass++) { //--- Last run is made on zero network CMLPBase::MLPRandomizeFull(network); zeronet=false; //--- check if(pass==passcount) { for(i_=0;i_<=wcount-1;i_++) network.m_weights[i_]=0*network.m_weights[i_]; zeronet=true; } //--- Same inputs leads to same outputs for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function calls CMLPBase::MLPProcess(network,x1,y1); CMLPBase::MLPProcess(network,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Same inputs on original network leads to same outputs //--- on copy created using MLPCopy UnsetNetwork(network2); CMLPBase::MLPCopy(network,network2); //--- change values for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function calls CMLPBase::MLPProcess(network,x1,y1); CMLPBase::MLPProcess(network2,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Same inputs on original network leads to same outputs //--- on copy created using MLPSerialize UnsetNetwork(network2); //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,network); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,network); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network2); _local_serializer.Stop(); //--- change values for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function calls CMLPBase::MLPProcess(network,x1,y1); CMLPBase::MLPProcess(network2,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Different inputs leads to different outputs (non-zero network) if(!zeronet) { for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=2*CMath::RandomReal()-1; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=y1[i]; } //--- function calls CMLPBase::MLPProcess(network,x1,y1); CMLPBase::MLPProcess(network,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || allsame; } //--- Randomization changes outputs (when inputs are unchanged,non-zero network) if(!zeronet) { for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=2*CMath::RandomReal()-1; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=y1[i]; } //--- function calls CMLPBase::MLPCopy(network,network2); CMLPBase::MLPRandomize(network2); CMLPBase::MLPProcess(network,x1,y1); CMLPBase::MLPProcess(network2,x1,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || allsame; } //--- Full randomization changes outputs (when inputs are unchanged,non-zero network) if(!zeronet) { for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=2*CMath::RandomReal()-1; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=y1[i]; } //--- function calls CMLPBase::MLPCopy(network,network2); CMLPBase::MLPRandomizeFull(network2); CMLPBase::MLPProcess(network,x1,y1); CMLPBase::MLPProcess(network2,x1,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || allsame; } //--- Normalization properties if(nkind==1) { //--- Classifier network outputs are normalized for(i=0;i<=nin-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CMLPBase::MLPProcess(network,x1,y1); v=0; for(i=0;i<=nout-1;i++) { v=v+y1[i]; //--- search errors err=err || y1[i]<0.0; } //--- search errors err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon; } //--- check if(nkind==2) { //--- B-type network outputs are bounded from above/below for(i=0;i<=nin-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CMLPBase::MLPProcess(network,x1,y1); for(i=0;i<=nout-1;i++) { //--- check if(a2>=0.0) err=err || y1[i]a1; } } //--- check if(nkind==3) { //--- R-type network outputs are within [A1,A2] (or [A2,A1]) for(i=0;i<=nin-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CMLPBase::MLPProcess(network,x1,y1); //--- search errors for(i=0;i<=nout-1;i++) err=(err || y1[i]MathMax(a1,a2); } } } //+------------------------------------------------------------------+ //| Gradient functions test | //+------------------------------------------------------------------+ static void CTestMLPTrainUnit::TestGradient(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int n1=0; int n2=0; int wcount=0; double h=0; double etol=0; double a1=0; double a2=0; int pass=0; int i=0; int j=0; int ssize=0; double v=0; double e=0; double e1=0; double e2=0; double v1=0; double v2=0; double v3=0; double v4=0; double wprev=0; int i_=0; int i1_=0; //--- create arrays double grad1[]; double grad2[]; double x[]; double y[]; double x1[]; double x2[]; double y1[]; double y2[]; //--- create matrix CMatrixDouble xy; //--- object of class CMultilayerPerceptron network; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) return; //--- change values a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- function calls CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); //--- change values h=0.0001; etol=0.01; //--- Initialize ArrayResize(x,nin); ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y,nout); ArrayResize(y1,nout); ArrayResize(y2,nout); ArrayResize(grad1,wcount); ArrayResize(grad2,wcount); //--- Process for(pass=1;pass<=passcount;pass++) { //--- function call CMLPBase::MLPRandomizeFull(network); //--- Test error/gradient calculation (least squares) xy.Resize(1,nin+nout); for(i=0;i<=nin-1;i++) x[i]=4*CMath::RandomReal()-2; for(i_=0;i_<=nin-1;i_++) xy[0].Set(i_,x[i_]); //--- check if(CMLPBase::MLPIsSoftMax(network)) { for(i=0;i<=nout-1;i++) y[i]=0; xy[0].Set(nin,CMath::RandomInteger(nout)); y[(int)MathRound(xy[0][nin])]=1; } else { for(i=0;i<=nout-1;i++) y[i]=4*CMath::RandomReal()-2; i1_=-nin; for(i_=nin;i_<=nin+nout-1;i_++) xy[0].Set(i_,y[i_+i1_]); } //--- function calls CMLPBase::MLPGrad(network,x,y,e,grad2); CMLPBase::MLPProcess(network,x,y2); for(i_=0;i_<=nout-1;i_++) y2[i_]=y2[i_]-y[i_]; //--- change value v=0.0; for(i_=0;i_<=nout-1;i_++) v+=y2[i_]*y2[i_]; v=v/2; //--- search errors err=err || MathAbs((v-e)/v)>etol; err=err || MathAbs((CMLPBase::MLPError(network,xy,1)-v)/v)>etol; for(i=0;i<=wcount-1;i++) { wprev=network.m_weights[i]; network.m_weights[i]=wprev-2*h; //--- function call CMLPBase::MLPProcess(network,x,y1); for(i_=0;i_<=nout-1;i_++) y1[i_]=y1[i_]-y[i_]; //--- change value v1=0.0; for(i_=0;i_<=nout-1;i_++) v1+=y1[i_]*y1[i_]; v1=v1/2; network.m_weights[i]=wprev-h; //--- function call CMLPBase::MLPProcess(network,x,y1); for(i_=0;i_<=nout-1;i_++) y1[i_]=y1[i_]-y[i_]; //--- change value v2=0.0; for(i_=0;i_<=nout-1;i_++) v2+=y1[i_]*y1[i_]; v2=v2/2; network.m_weights[i]=wprev+h; //--- function call CMLPBase::MLPProcess(network,x,y1); for(i_=0;i_<=nout-1;i_++) y1[i_]=y1[i_]-y[i_]; //--- change value v3=0.0; for(i_=0;i_<=nout-1;i_++) v3+=y1[i_]*y1[i_]; v3=v3/2; network.m_weights[i]=wprev+2*h; //--- function call CMLPBase::MLPProcess(network,x,y1); for(i_=0;i_<=nout-1;i_++) y1[i_]=y1[i_]-y[i_]; //--- change value v4=0.0; for(i_=0;i_<=nout-1;i_++) v4+=y1[i_]*y1[i_]; v4=v4/2; network.m_weights[i]=wprev; grad1[i]=(v1-8*v2+8*v3-v4)/(12*h); //--- check if(MathAbs(grad1[i])>1.0E-3) err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol; else err=err || MathAbs(grad2[i]-grad1[i])>etol; } //--- Test error/gradient calculation (natural). //--- Testing on non-random structure networks //--- (because NKind is representative only in that case). xy.Resize(1,nin+nout); for(i=0;i<=nin-1;i++) x[i]=4*CMath::RandomReal()-2; for(i_=0;i_<=nin-1;i_++) xy[0].Set(i_,x[i_]); //--- check if(CMLPBase::MLPIsSoftMax(network)) { for(i=0;i<=nout-1;i++) y[i]=0; xy[0].Set(nin,CMath::RandomInteger(nout)); y[(int)MathRound(xy[0][nin])]=1; } else { for(i=0;i<=nout-1;i++) y[i]=4*CMath::RandomReal()-2; i1_=-nin; for(i_=nin;i_<=nin+nout-1;i_++) xy[0].Set(i_,y[i_+i1_]); } //--- function calls CMLPBase::MLPGradN(network,x,y,e,grad2); CMLPBase::MLPProcess(network,x,y2); v=0; //--- check if(nkind!=1) { for(i=0;i<=nout-1;i++) v=v+0.5*CMath::Sqr(y2[i]-y[i]); } else { for(i=0;i<=nout-1;i++) { //--- check if(y[i]!=0.0) { //--- check if(y2[i]==0.0) v=v+y[i]*MathLog(CMath::m_maxrealnumber); else v=v+y[i]*MathLog(y[i]/y2[i]); } } } //--- search errors err=err || MathAbs((v-e)/v)>etol; err=err || MathAbs((CMLPBase::MLPErrorN(network,xy,1)-v)/v)>etol; for(i=0;i<=wcount-1;i++) { wprev=network.m_weights[i]; network.m_weights[i]=wprev+h; //--- function call CMLPBase::MLPProcess(network,x,y2); network.m_weights[i]=wprev-h; //--- function call CMLPBase::MLPProcess(network,x,y1); network.m_weights[i]=wprev; v=0; //--- check if(nkind!=1) { for(j=0;j<=nout-1;j++) v=v+0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h); } else { for(j=0;j<=nout-1;j++) { //--- check if(y[j]!=0.0) { //--- check if(y2[j]==0.0) v=v+y[j]*MathLog(CMath::m_maxrealnumber); else v=v+y[j]*MathLog(y[j]/y2[j]); //--- check if(y1[j]==0.0) v=v-y[j]*MathLog(CMath::m_maxrealnumber); else v=v-y[j]*MathLog(y[j]/y1[j]); } } v=v/(2*h); } grad1[i]=v; //--- check if(MathAbs(grad1[i])>1.0E-3) err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol; else err=err || MathAbs(grad2[i]-grad1[i])>etol; } //--- Test gradient calculation: batch (least squares) ssize=1+CMath::RandomInteger(10); xy.Resize(ssize,nin+nout); for(i=0;i<=wcount-1;i++) grad1[i]=0; //--- calculation e1=0; for(i=0;i<=ssize-1;i++) { for(j=0;j<=nin-1;j++) x1[j]=4*CMath::RandomReal()-2; for(i_=0;i_<=nin-1;i_++) xy[i].Set(i_,x1[i_]); //--- check if(CMLPBase::MLPIsSoftMax(network)) { for(j=0;j<=nout-1;j++) y1[j]=0; //--- change values xy[i].Set(nin,CMath::RandomInteger(nout)); y1[(int)MathRound(xy[i][nin])]=1; } else { //--- change values for(j=0;j<=nout-1;j++) y1[j]=4*CMath::RandomReal()-2; i1_=-nin; for(i_=nin;i_<=nin+nout-1;i_++) xy[i].Set(i_,y1[i_+i1_]); } //--- function call CMLPBase::MLPGrad(network,x1,y1,v,grad2); //--- change values e1=e1+v; for(i_=0;i_<=wcount-1;i_++) grad1[i_]=grad1[i_]+grad2[i_]; } //--- function call CMLPBase::MLPGradBatch(network,xy,ssize,e2,grad2); //--- search errors err=err || MathAbs(e1-e2)/e1>0.01; for(i=0;i<=wcount-1;i++) { //--- check if(grad1[i]!=0.0) err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol; else err=err || grad2[i]!=grad1[i]; } //--- Test gradient calculation: batch (natural error func) ssize=1+CMath::RandomInteger(10); xy.Resize(ssize,nin+nout); for(i=0;i<=wcount-1;i++) grad1[i]=0; //--- calculation e1=0; for(i=0;i<=ssize-1;i++) { for(j=0;j<=nin-1;j++) x1[j]=4*CMath::RandomReal()-2; for(i_=0;i_<=nin-1;i_++) xy[i].Set(i_,x1[i_]); //--- check if(CMLPBase::MLPIsSoftMax(network)) { for(j=0;j<=nout-1;j++) y1[j]=0; //--- change values xy[i].Set(nin,CMath::RandomInteger(nout)); y1[(int)MathRound(xy[i][nin])]=1; } else { //--- change values for(j=0;j<=nout-1;j++) y1[j]=4*CMath::RandomReal()-2; i1_=-nin; for(i_=nin;i_<=nin+nout-1;i_++) xy[i].Set(i_,y1[i_+i1_]); } //--- function call CMLPBase::MLPGradN(network,x1,y1,v,grad2); //--- change values e1=e1+v; for(i_=0;i_<=wcount-1;i_++) grad1[i_]=grad1[i_]+grad2[i_]; } //--- function call CMLPBase::MLPGradNBatch(network,xy,ssize,e2,grad2); //--- search errors err=err || MathAbs(e1-e2)/e1>etol; for(i=0;i<=wcount-1;i++) { //--- check if(grad1[i]!=0.0) err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol; else err=err || grad2[i]!=grad1[i]; } } } //+------------------------------------------------------------------+ //| Hessian functions test | //+------------------------------------------------------------------+ static void CTestMLPTrainUnit::TestHessian(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int hkind=0; int n1=0; int n2=0; int wcount=0; double h=0; double etol=0; int pass=0; int i=0; int j=0; int ssize=0; double a1=0; double a2=0; double v=0; double e1=0; double e2=0; double wprev=0; int i_=0; int i1_=0; //--- create arrays double grad1[]; double grad2[]; double grad3[]; double x[]; double y[]; double x1[]; double x2[]; double y1[]; double y2[]; //--- create matrix CMatrixDouble xy; CMatrixDouble h1; CMatrixDouble h2; //--- îáúåêò êëàññâ CMultilayerPerceptron network; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) return; //--- change values a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- function calls CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); //--- change values h=0.0001; etol=0.05; //--- Initialize ArrayResize(x,nin); ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y,nout); ArrayResize(y1,nout); ArrayResize(y2,nout); ArrayResize(grad1,wcount); ArrayResize(grad2,wcount); ArrayResize(grad3,wcount); h1.Resize(wcount,wcount); h2.Resize(wcount,wcount); //--- Process for(pass=1;pass<=passcount;pass++) { //--- function call CMLPBase::MLPRandomizeFull(network); //--- Test hessian calculation . //--- E1 contains total error (calculated using MLPGrad/MLPGradN) //--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN) //--- H1 contains Hessian calculated using differences of gradients //--- E2,Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch for(hkind=0;hkind<=1;hkind++) { ssize=1+CMath::RandomInteger(10); xy.Resize(ssize,nin+nout); for(i=0;i<=wcount-1;i++) grad1[i]=0; //--- change values for(i=0;i<=wcount-1;i++) { for(j=0;j<=wcount-1;j++) h1[i].Set(j,0); } //--- calculation e1=0; for(i=0;i<=ssize-1;i++) { //--- X,Y for(j=0;j<=nin-1;j++) x1[j]=4*CMath::RandomReal()-2; for(i_=0;i_<=nin-1;i_++) xy[i].Set(i_,x1[i_]); //--- check if(CMLPBase::MLPIsSoftMax(network)) { for(j=0;j<=nout-1;j++) y1[j]=0; //--- change values xy[i].Set(nin,CMath::RandomInteger(nout)); y1[(int)MathRound(xy[i][nin])]=1; } else { //--- change values for(j=0;j<=nout-1;j++) y1[j]=4*CMath::RandomReal()-2; i1_=-nin; for(i_=nin;i_<=nin+nout-1;i_++) xy[i].Set(i_,y1[i_+i1_]); } //--- E1,Grad1 if(hkind==0) CMLPBase::MLPGrad(network,x1,y1,v,grad2); else CMLPBase::MLPGradN(network,x1,y1,v,grad2); //--- change values e1=e1+v; for(i_=0;i_<=wcount-1;i_++) grad1[i_]=grad1[i_]+grad2[i_]; //--- H1 for(j=0;j<=wcount-1;j++) { wprev=network.m_weights[j]; network.m_weights[j]=wprev-2*h; //--- check if(hkind==0) CMLPBase::MLPGrad(network,x1,y1,v,grad2); else CMLPBase::MLPGradN(network,x1,y1,v,grad2); network.m_weights[j]=wprev-h; //--- check if(hkind==0) CMLPBase::MLPGrad(network,x1,y1,v,grad3); else CMLPBase::MLPGradN(network,x1,y1,v,grad3); //--- change values for(i_=0;i_<=wcount-1;i_++) grad2[i_]=grad2[i_]-8*grad3[i_]; network.m_weights[j]=wprev+h; //--- check if(hkind==0) CMLPBase::MLPGrad(network,x1,y1,v,grad3); else CMLPBase::MLPGradN(network,x1,y1,v,grad3); //--- change values for(i_=0;i_<=wcount-1;i_++) grad2[i_]=grad2[i_]+8*grad3[i_]; network.m_weights[j]=wprev+2*h; //--- check if(hkind==0) CMLPBase::MLPGrad(network,x1,y1,v,grad3); else CMLPBase::MLPGradN(network,x1,y1,v,grad3); //--- change values for(i_=0;i_<=wcount-1;i_++) grad2[i_]=grad2[i_]-grad3[i_]; v=1/(12*h); for(i_=0;i_<=wcount-1;i_++) h1[j].Set(i_,h1[j][i_]+v*grad2[i_]); network.m_weights[j]=wprev; } } //--- check if(hkind==0) CMLPBase::MLPHessianBatch(network,xy,ssize,e2,grad2,h2); else CMLPBase::MLPHessianNBatch(network,xy,ssize,e2,grad2,h2); //--- search errors err=err || MathAbs(e1-e2)/e1>etol; for(i=0;i<=wcount-1;i++) { //--- check if(MathAbs(grad1[i])>1.0E-2) err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol; else err=err || MathAbs(grad2[i]-grad1[i])>etol; } //--- search errors for(i=0;i<=wcount-1;i++) { for(j=0;j<=wcount-1;j++) { //--- check if(MathAbs(h1[i][j])>5.0E-2) err=err || MathAbs((h1[i][j]-h2[i][j])/h1[i][j])>etol; else err=err || MathAbs(h2[i][j]-h1[i][j])>etol; } } } } } //+------------------------------------------------------------------+ //| Testing class CMLPE | //+------------------------------------------------------------------+ class CTestMLPEUnit { private: //--- private methods static void CreateEnsemble(CMLPEnsemble &ensemble,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout,const int ec); static void UnsetEnsemble(CMLPEnsemble &ensemble); static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int ec,const int passcount,bool &err); static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int ec,const int passcount,bool &err); public: //--- constructor, destructor CTestMLPEUnit(void); ~CTestMLPEUnit(void); //--- public method static bool TestMLPE(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMLPEUnit::CTestMLPEUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMLPEUnit::~CTestMLPEUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CMLPE | //+------------------------------------------------------------------+ static bool CTestMLPEUnit::TestMLPE(const bool silent) { //--- create variables bool waserrors; int passcount=0; int maxn=0; int maxhid=0; int nf=0; int nhid=0; int nl=0; int nhid1=0; int nhid2=0; int ec=0; int nkind=0; int algtype=0; int tasktype=0; int pass=0; int i=0; int j=0; int nin=0; int nout=0; int npoints=0; double e=0; int info=0; int nless=0; int nall=0; int nclasses=0; bool inferrors; bool procerrors; bool trnerrors; //--- create matrix CMatrixDouble xy; //--- objects of classes CMLPEnsemble ensemble; CMLPReport rep; CMLPCVReport oobrep; //--- initialization waserrors=false; inferrors=false; procerrors=false; trnerrors=false; passcount=10; maxn=4; maxhid=4; //--- General MLP ensembles tests for(nf=1;nf<=maxn;nf++) { for(nl=1;nl<=maxn;nl++) { for(nhid1=0;nhid1<=maxhid;nhid1++) { for(nhid2=0;nhid2<=0;nhid2++) { for(nkind=0;nkind<=3;nkind++) { for(ec=1;ec<=3;ec++) { //--- Skip meaningless parameters combinations if(nkind==1 && nl<2) continue; //--- check if(nhid1==0 && nhid2!=0) continue; //--- Tests TestInformational(nkind,nf,nhid1,nhid2,nl,ec,passcount,inferrors); TestProcessing(nkind,nf,nhid1,nhid2,nl,ec,passcount,procerrors); } } } } } } //--- network training must reduce error //--- test on random regression task nin=3; nout=2; nhid=5; npoints=100; nless=0; nall=0; //--- calculation for(pass=1;pass<=10;pass++) { for(algtype=0;algtype<=1;algtype++) { for(tasktype=0;tasktype<=1;tasktype++) { //--- check if(tasktype==0) { //--- allocation xy.Resize(npoints,nin+nout); //--- change values for(i=0;i<=npoints-1;i++) { for(j=0;j<=nin+nout-1;j++) xy[i].Set(j,2*CMath::RandomReal()-1); } //--- function call CMLPE::MLPECreate1(nin,nhid,nout,1+CMath::RandomInteger(3),ensemble); } else { //--- allocation xy.Resize(npoints,nin+1); //--- change values nclasses=2+CMath::RandomInteger(2); for(i=0;i<=npoints-1;i++) { for(j=0;j<=nin-1;j++) xy[i].Set(j,2*CMath::RandomReal()-1); xy[i].Set(nin,CMath::RandomInteger(nclasses)); } //--- function call CMLPE::MLPECreateC1(nin,nhid,nclasses,1+CMath::RandomInteger(3),ensemble); } e=CMLPE::MLPERMSError(ensemble,xy,npoints); //--- check if(algtype==0) CMLPE::MLPEBaggingLM(ensemble,xy,npoints,0.001,1,info,rep,oobrep); else CMLPE::MLPEBaggingLBFGS(ensemble,xy,npoints,0.001,1,0.01,0,info,rep,oobrep); //--- check if(info<0) trnerrors=true; else { //--- check if(CMLPE::MLPERMSError(ensemble,xy,npoints)0.3*nall; //--- Final report waserrors=(inferrors || procerrors) || trnerrors; //--- check if(!silent) { Print("MLP ENSEMBLE TEST"); Print("INFORMATIONAL FUNCTIONS: "); //--- check if(!inferrors) Print("OK"); else Print("FAILED"); Print("BASIC PROCESSING: "); //--- check if(!procerrors) Print("OK"); else Print("FAILED"); Print("TRAINING: "); //--- check if(!trnerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Network creation | //+------------------------------------------------------------------+ static void CTestMLPEUnit::CreateEnsemble(CMLPEnsemble &ensemble,const int nkind, const double a1,const double a2, const int nin,const int nhid1, const int nhid2,const int nout, const int ec) { //--- check if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error")) return; //--- check if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error")) return; //--- check if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error")) return; //--- check if(nhid1==0) { //--- No hidden layers if(nkind==0) CMLPE::MLPECreate0(nin,nout,ec,ensemble); else { //--- check if(nkind==1) CMLPE::MLPECreateC0(nin,nout,ec,ensemble); else { //--- check if(nkind==2) CMLPE::MLPECreateB0(nin,nout,a1,a2,ec,ensemble); else { //--- check if(nkind==3) CMLPE::MLPECreateR0(nin,nout,a1,a2,ec,ensemble); } } } //--- exit the function return; } //--- check if(nhid2==0) { //--- One hidden layer if(nkind==0) CMLPE::MLPECreate1(nin,nhid1,nout,ec,ensemble); else { //--- check if(nkind==1) CMLPE::MLPECreateC1(nin,nhid1,nout,ec,ensemble); else { //--- check if(nkind==2) CMLPE::MLPECreateB1(nin,nhid1,nout,a1,a2,ec,ensemble); else { //--- check if(nkind==3) CMLPE::MLPECreateR1(nin,nhid1,nout,a1,a2,ec,ensemble); } } } //--- exit the function return; } //--- Two hidden layers if(nkind==0) CMLPE::MLPECreate2(nin,nhid1,nhid2,nout,ec,ensemble); else { //--- check if(nkind==1) CMLPE::MLPECreateC2(nin,nhid1,nhid2,nout,ec,ensemble); else { //--- check if(nkind==2) CMLPE::MLPECreateB2(nin,nhid1,nhid2,nout,a1,a2,ec,ensemble); else { //--- check if(nkind==3) CMLPE::MLPECreateR2(nin,nhid1,nhid2,nout,a1,a2,ec,ensemble); } } } } //+------------------------------------------------------------------+ //| Unsets network (initialize it to smallest network possible | //+------------------------------------------------------------------+ static void CTestMLPEUnit::UnsetEnsemble(CMLPEnsemble &ensemble) { //--- function call CMLPE::MLPECreate0(1,1,1,ensemble); } //+------------------------------------------------------------------+ //| Iformational functions test | //+------------------------------------------------------------------+ static void CTestMLPEUnit::TestInformational(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int ec, const int passcount,bool &err) { //--- create variables int n1=0; int n2=0; //--- object of class CMLPEnsemble ensemble; //--- function calls CreateEnsemble(ensemble,nkind,-1.0,1.0,nin,nhid1,nhid2,nout,ec); CMLPE::MLPEProperties(ensemble,n1,n2); //--- search errors err=(err || n1!=nin) || n2!=nout; } //+------------------------------------------------------------------+ //| Processing functions test | //+------------------------------------------------------------------+ static void CTestMLPEUnit::TestProcessing(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int ec, const int passcount,bool &err) { //--- create variables double a1=0; double a2=0; int pass=0; int i=0; bool allsame; int rlen=0; double v=0; //--- create arrays double x1[]; double x2[]; double y1[]; double y2[]; double ra[]; double ra2[]; //--- objects of classes êëàññà CMLPEnsemble ensemble; CMLPEnsemble ensemble2; //--- Prepare network a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- Initialize arrays ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y1,nout); ArrayResize(y2,nout); //--- Main cycle for(pass=1;pass<=passcount;pass++) { //--- function call CreateEnsemble(ensemble,nkind,a1,a2,nin,nhid1,nhid2,nout,ec); //--- Same inputs leads to same outputs for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function calls CMLPE::MLPEProcess(ensemble,x1,y1); CMLPE::MLPEProcess(ensemble,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Same inputs on original network leads to same outputs //--- on copy created using MLPCopy UnsetEnsemble(ensemble2); CMLPE::MLPECopy(ensemble,ensemble2); for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function calls CMLPE::MLPEProcess(ensemble,x1,y1); CMLPE::MLPEProcess(ensemble2,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Same inputs on original network leads to same outputs //--- on copy created using MLPSerialize UnsetEnsemble(ensemble2); CMLPE::MLPESerialize(ensemble,ra,rlen); //--- allocation ArrayResize(ra2,rlen); //--- copy for(i=0;i<=rlen-1;i++) ra2[i]=ra[i]; //--- function call CMLPE::MLPEUnserialize(ra2,ensemble2); for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=x1[i]; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=2*CMath::RandomReal()-1; } //--- function calls CMLPE::MLPEProcess(ensemble,x1,y1); CMLPE::MLPEProcess(ensemble2,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || !allsame; //--- Different inputs leads to different outputs (non-zero network) for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=2*CMath::RandomReal()-1; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=y1[i]; } //--- function calls CMLPE::MLPEProcess(ensemble,x1,y1); CMLPE::MLPEProcess(ensemble,x2,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || allsame; //--- Randomization changes outputs (when inputs are unchanged,non-zero network) for(i=0;i<=nin-1;i++) { x1[i]=2*CMath::RandomReal()-1; x2[i]=2*CMath::RandomReal()-1; } for(i=0;i<=nout-1;i++) { y1[i]=2*CMath::RandomReal()-1; y2[i]=y1[i]; } //--- function calls CMLPE::MLPECopy(ensemble,ensemble2); CMLPE::MLPERandomize(ensemble2); CMLPE::MLPEProcess(ensemble,x1,y1); CMLPE::MLPEProcess(ensemble2,x1,y2); //--- search errors allsame=true; for(i=0;i<=nout-1;i++) allsame=allsame && y1[i]==y2[i]; err=err || allsame; //--- Normalization properties if(nkind==1) { //--- Classifier network outputs are normalized for(i=0;i<=nin-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CMLPE::MLPEProcess(ensemble,x1,y1); v=0; //--- search errors for(i=0;i<=nout-1;i++) { v=v+y1[i]; err=err || y1[i]<0.0; } err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon; } //--- check if(nkind==2) { //--- B-type network outputs are bounded from above/below for(i=0;i<=nin-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CMLPE::MLPEProcess(ensemble,x1,y1); for(i=0;i<=nout-1;i++) { //--- check if(a2>=0.0) err=err || y1[i]a1; } } //--- check if(nkind==3) { //--- R-type network outputs are within [A1,A2] (or [A2,A1]) for(i=0;i<=nin-1;i++) x1[i]=2*CMath::RandomReal()-1; //--- function call CMLPE::MLPEProcess(ensemble,x1,y1); //--- search errors for(i=0;i<=nout-1;i++) err=(err || y1[i]MathMax(a1,a2); } } } //+------------------------------------------------------------------+ //| Testing class CPCAnalysis | //+------------------------------------------------------------------+ class CTestPCAUnit { private: //--- private method static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs); public: //--- constructor, destructor CTestPCAUnit(void); ~CTestPCAUnit(void); //--- public method static bool TestPCA(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestPCAUnit::CTestPCAUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestPCAUnit::~CTestPCAUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CPCAnalysis | //+------------------------------------------------------------------+ static bool CTestPCAUnit::TestPCA(const bool silent) { //--- create variables int passcount=0; int maxn=0; int maxm=0; double threshold=0; int m=0; int n=0; int i=0; int j=0; int k=0; int info=0; double t=0; double h=0; double tmean=0; double tmeans=0; double tstddev=0; double tstddevs=0; double tmean2=0; double tmeans2=0; double tstddev2=0; double tstddevs2=0; bool pcaconverrors; bool pcaorterrors; bool pcavarerrors; bool pcaopterrors; bool waserrors; int i_=0; //--- create arrays double means[]; double s[]; double t2[]; double t3[]; //--- create matrix CMatrixDouble v; CMatrixDouble x; //--- Primary settings maxm=10; maxn=100; passcount=1; threshold=1000*CMath::m_machineepsilon; waserrors=false; pcaconverrors=false; pcaorterrors=false; pcavarerrors=false; pcaopterrors=false; //--- Test 1: N random points in M-dimensional space for(m=1;m<=maxm;m++) { for(n=1;n<=maxn;n++) { //--- Generate task x.Resize(n,m); ArrayResize(means,m); //--- change values for(j=0;j<=m-1;j++) means[j]=1.5*CMath::RandomReal()-0.75; for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) x[i].Set(j,means[j]+(2*CMath::RandomReal()-1)); } //--- Solve CPCAnalysis::PCABuildBasis(x,n,m,info,s,v); //--- check if(info!=1) { pcaconverrors=true; continue; } //--- Orthogonality test for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { //--- change value t=0.0; for(i_=0;i_<=m-1;i_++) t+=v[i_][i]*v[i_][j]; //--- check if(i==j) t=t-1; //--- search errors pcaorterrors=pcaorterrors || MathAbs(t)>threshold; } } //--- Variance test ArrayResize(t2,n); for(k=0;k<=m-1;k++) { for(i=0;i<=n-1;i++) { //--- change value t=0.0; for(i_=0;i_<=m-1;i_++) t+=x[i][i_]*v[i_][k]; t2[i]=t; } //--- function call CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs); //--- check if(n!=1) t=CMath::Sqr(tstddev)*n/(n-1); else t=0; //--- search errors pcavarerrors=pcavarerrors || MathAbs(t-s[k])>threshold; } //--- search errors for(k=0;k<=m-2;k++) pcavarerrors=pcavarerrors || s[k]tstddev+threshold; } } } //--- Special test for N=0 for(m=1;m<=maxm;m++) { //--- Solve CPCAnalysis::PCABuildBasis(x,0,m,info,s,v); //--- check if(info!=1) { pcaconverrors=true; continue; } //--- Orthogonality test for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) { //--- change value t=0.0; for(i_=0;i_<=m-1;i_++) t+=v[i_][i]*v[i_][j]; //--- check if(i==j) t=t-1; //--- search errors pcaorterrors=pcaorterrors || MathAbs(t)>threshold; } } } //--- Final report waserrors=((pcaconverrors || pcaorterrors) || pcavarerrors) || pcaopterrors; //--- check if(!silent) { Print("PCA TEST"); Print("TOTAL RESULTS: "); //--- check if(!waserrors) Print("OK"); else Print("FAILED"); Print("* CONVERGENCE "); //--- check if(!pcaconverrors) Print("OK"); else Print("FAILED"); Print("* ORTOGONALITY "); //--- check if(!pcaorterrors) Print("OK"); else Print("FAILED"); Print("* VARIANCE REPORT "); //--- check if(!pcavarerrors) Print("OK"); else Print("FAILED"); Print("* OPTIMALITY "); //--- check if(!pcaopterrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST SUMMARY: FAILED"); else Print("TEST SUMMARY: PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Moments estimates and their errors | //+------------------------------------------------------------------+ static void CTestPCAUnit::CalculateMV(double &x[],const int n,double &mean, double &means,double &stddev,double &stddevs) { //--- create variables int i=0; double v1=0; double v2=0; double variance=0; //--- initialization mean=0; means=1; stddev=0; stddevs=1; variance=0; //--- check if(n<=1) return; //--- Mean for(i=0;i<=n-1;i++) mean=mean+x[i]; mean=mean/n; //--- Variance (using corrected two-pass algorithm) if(n!=1) { //--- change values v1=0; for(i=0;i<=n-1;i++) v1=v1+CMath::Sqr(x[i]-mean); v2=0; for(i=0;i<=n-1;i++) v2=v2+(x[i]-mean); v2=CMath::Sqr(v2)/n; variance=(v1-v2)/n; //--- check if(variance<0.0) variance=0; stddev=MathSqrt(variance); } //--- Errors means=stddev/MathSqrt(n); stddevs=stddev*MathSqrt(2)/MathSqrt(n-1); } //+------------------------------------------------------------------+ //| Testing class CODESolver | //+------------------------------------------------------------------+ class CTestODESolverUnit { private: //--- private methods static void Unset2D(CMatrixDouble &x); static void Unset1D(double &x[]); static void UnsetRep(CODESolverReport &rep); public: //--- constructor, destructor CTestODESolverUnit(void); ~CTestODESolverUnit(void); //--- public method static bool TestODESolver(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestODESolverUnit::CTestODESolverUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestODESolverUnit::~CTestODESolverUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestODESolverUnit::TestODESolver(const bool silent) { //--- create variables int passcount=0; bool curerrors; bool rkckerrors; bool waserrors; double h=0; double eps=0; int solver=0; int pass=0; int mynfev=0; double v=0; int m=0; int m2=0; int i=0; double err=0; int i_=0; //--- create arrays double xtbl[]; double xg[]; double y[]; //--- create matrix CMatrixDouble ytbl; //--- objects of classes CODESolverReport rep; CODESolverState state; //--- initialization rkckerrors=false; waserrors=false; passcount=10; //--- simple test: just A*sin(x)+B*cos(x) if(!CAp::Assert(passcount>=2)) return(false); for(pass=0;pass<=passcount-1;pass++) { for(solver=0;solver<=0;solver++) { //--- prepare h=1.0E-2; eps=1.0E-5; //--- check if(pass%2==0) eps=-eps; //--- allocation ArrayResize(y,2); //--- change values for(i=0;i<=1;i++) y[i]=2*CMath::RandomReal()-1; m=2+CMath::RandomInteger(10); //--- allocation ArrayResize(xg,m); xg[0]=(m-1)*CMath::RandomReal(); for(i=1;i<=m-1;i++) xg[i]=xg[i-1]+CMath::RandomReal(); //--- change values v=2*M_PI/(xg[m-1]-xg[0]); for(i_=0;i_<=m-1;i_++) xg[i_]=v*xg[i_]; //--- check if(CMath::RandomReal()>0.5) { for(i_=0;i_<=m-1;i_++) xg[i_]=-1*xg[i_]; } mynfev=0; //--- choose solver if(solver==0) CODESolver::ODESolverRKCK(y,2,xg,m,eps,h,state); //--- solve while(CODESolver::ODESolverIteration(state)) { state.m_dy[0]=state.m_y[1]; state.m_dy[1]=-state.m_y[0]; mynfev=mynfev+1; } //--- function call CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep); //--- check results curerrors=false; //--- check if(rep.m_terminationtype<=0) curerrors=true; else { //--- search errors curerrors=curerrors || m2!=m; err=0; for(i=0;i<=m-1;i++) { err=MathMax(err,MathAbs(ytbl[i][0]-(y[0]*MathCos(xtbl[i]-xtbl[0])+y[1]*MathSin(xtbl[i]-xtbl[0])))); err=MathMax(err,MathAbs(ytbl[i][1]-(-(y[0]*MathSin(xtbl[i]-xtbl[0]))+y[1]*MathCos(xtbl[i]-xtbl[0])))); } curerrors=curerrors || err>10*MathAbs(eps); curerrors=curerrors || mynfev!=rep.m_nfev; } //--- check if(solver==0) rkckerrors=rkckerrors || curerrors; } } //--- another test: //--- y(0)=0 //--- dy/dx=f(x,y) //--- f(x,y)=0, x<1 //--- x-1,x>=1 //--- with BOTH absolute and fractional tolerances. //--- Starting from zero will be real challenge for //--- fractional tolerance. if(!CAp::Assert(passcount>=2)) return(false); for(pass=0;pass<=passcount-1;pass++) { h=1.0E-4; eps=1.0E-4; //--- check if(pass%2==0) eps=-eps; //--- allocation ArrayResize(y,1); y[0]=0; m=21; //--- allocation ArrayResize(xg,m); for(i=0;i<=m-1;i++) xg[i]=(double)(2*i)/(double)(m-1); mynfev=0; //--- function call CODESolver::ODESolverRKCK(y,1,xg,m,eps,h,state); //--- cycle while(CODESolver::ODESolverIteration(state)) { state.m_dy[0]=MathMax(state.m_x-1,0); mynfev=mynfev+1; } //--- function call CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep); //--- check if(rep.m_terminationtype<=0) rkckerrors=true; else { //--- search errors rkckerrors=rkckerrors || m2!=m; err=0; for(i=0;i<=m-1;i++) err=MathMax(err,MathAbs(ytbl[i][0]-CMath::Sqr(MathMax(xg[i]-1,0))/2)); rkckerrors=rkckerrors || err>MathAbs(eps); rkckerrors=rkckerrors || mynfev!=rep.m_nfev; } } //--- end waserrors=rkckerrors; //--- check if(!silent) { Print("TESTING ODE SOLVER"); Print("* RK CASH-KARP: "); //--- check if(rkckerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ static void CTestODESolverUnit::Unset2D(CMatrixDouble &x) { //--- allocation x.Resize(1,1); //--- change value x[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ static void CTestODESolverUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets report | //+------------------------------------------------------------------+ static void CTestODESolverUnit::UnsetRep(CODESolverReport &rep) { //--- change value rep.m_nfev=0; } //+------------------------------------------------------------------+ //| Testing class CFastFourierTransform | //+------------------------------------------------------------------+ class CTestFFTUnit { private: //--- private methods static void RefFFTC1D(complex &a[],const int n); static void RefFFTC1DInv(complex &a[],const int n); static void RefInternalCFFT(double &a[],const int nn,const bool inversefft); static void RefInternalRFFT(double &a[],const int nn,complex &f[]); public: //--- constructor, destructor CTestFFTUnit(void); ~CTestFFTUnit(void); //--- public method static bool TestFFT(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestFFTUnit::CTestFFTUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestFFTUnit::~CTestFFTUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestFFTUnit::TestFFT(const bool silent) { //--- create variables int n=0; int i=0; int k=0; int maxn=0; double bidierr=0; double bidirerr=0; double referr=0; double refrerr=0; double reinterr=0; double errtol=0; bool referrors; bool bidierrors; bool refrerrors; bool bidirerrors; bool reinterrors; bool waserrors; int i_=0; //--- create arrays complex a1[]; complex a2[]; complex a3[]; double r1[]; double r2[]; double buf[]; //--- object of class CFtPlan plan; //--- initialization maxn=128; errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon; bidierrors=false; referrors=false; bidirerrors=false; refrerrors=false; reinterrors=false; waserrors=false; //--- Test bi-directional error: norm(x-invFFT(FFT(x))) bidierr=0; bidirerr=0; for(n=1;n<=maxn;n++) { //--- Complex FFT/invFFT ArrayResize(a1,n); ArrayResize(a2,n); ArrayResize(a3,n); for(i=0;i<=n-1;i++) { a1[i].re=2*CMath::RandomReal()-1; a1[i].im=2*CMath::RandomReal()-1; a2[i]=a1[i]; a3[i]=a1[i]; } //--- function calls CFastFourierTransform::FFTC1D(a2,n); CFastFourierTransform::FFTC1DInv(a2,n); CFastFourierTransform::FFTC1DInv(a3,n); CFastFourierTransform::FFTC1D(a3,n); //--- search errors for(i=0;i<=n-1;i++) { bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a2[i])); bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a3[i])); } //--- Real ArrayResize(r1,n); ArrayResize(r2,n); //--- change values for(i=0;i<=n-1;i++) { r1[i]=2*CMath::RandomReal()-1; r2[i]=r1[i]; } //--- function call CFastFourierTransform::FFTR1D(r2,n,a1); for(i_=0;i_<=n-1;i_++) r2[i_]=0*r2[i_]; //--- function call CFastFourierTransform::FFTR1DInv(a1,n,r2); //--- search errors for(i=0;i<=n-1;i++) bidirerr=MathMax(bidirerr,CMath::AbsComplex(r1[i]-r2[i])); } //--- search errors bidierrors=bidierrors || bidierr>errtol; bidirerrors=bidirerrors || bidirerr>errtol; //--- Test against reference O(N^2) implementation referr=0; refrerr=0; for(n=1;n<=maxn;n++) { //--- Complex FFT ArrayResize(a1,n); ArrayResize(a2,n); for(i=0;i<=n-1;i++) { a1[i].re=2*CMath::RandomReal()-1; a1[i].im=2*CMath::RandomReal()-1; a2[i]=a1[i]; } //--- function calls CFastFourierTransform::FFTC1D(a1,n); RefFFTC1D(a2,n); //--- search errors for(i=0;i<=n-1;i++) referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i])); //--- Complex inverse FFT ArrayResize(a1,n); ArrayResize(a2,n); for(i=0;i<=n-1;i++) { a1[i].re=2*CMath::RandomReal()-1; a1[i].im=2*CMath::RandomReal()-1; a2[i]=a1[i]; } //--- function calls CFastFourierTransform::FFTC1DInv(a1,n); RefFFTC1DInv(a2,n); //--- search errors for(i=0;i<=n-1;i++) referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i])); //--- Real forward/inverse FFT: //--- * calculate and check forward FFT //--- * use precalculated FFT to check backward FFT //--- fill unused parts of frequencies array with random numbers //--- to ensure that they are not really used ArrayResize(r1,n); ArrayResize(r2,n); for(i=0;i<=n-1;i++) { r1[i]=2*CMath::RandomReal()-1; r2[i]=r1[i]; } //--- function calls CFastFourierTransform::FFTR1D(r1,n,a1); RefInternalRFFT(r2,n,a2); //--- search errors for(i=0;i<=n-1;i++) refrerr=MathMax(refrerr,CMath::AbsComplex(a1[i]-a2[i])); //--- allocation ArrayResize(a3,(int)MathFloor((double)n/2.0)+1); for(i=0;i<=(int)MathFloor((double)n/2.0);i++) a3[i]=a2[i]; a3[0].im=2*CMath::RandomReal()-1; //--- check if(n%2==0) a3[(int)MathFloor((double)n/2.0)].im=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) r1[i]=0; //--- function call CFastFourierTransform::FFTR1DInv(a3,n,r1); //--- search errors for(i=0;i<=n-1;i++) refrerr=MathMax(refrerr,MathAbs(r2[i]-r1[i])); } //--- search errors referrors=referrors || referr>errtol; refrerrors=refrerrors || refrerr>errtol; //--- test internal real even FFT reinterr=0; for(k=1;k<=maxn/2;k++) { n=2*k; //--- Real forward FFT ArrayResize(r1,n); ArrayResize(r2,n); for(i=0;i<=n-1;i++) { r1[i]=2*CMath::RandomReal()-1; r2[i]=r1[i]; } //--- function call CFtBase::FtBaseGenerateComplexFFtPlan(n/2,plan); //--- allocation ArrayResize(buf,n); //--- function calls CFastFourierTransform::FFTR1DInternalEven(r1,n,buf,plan); RefInternalRFFT(r2,n,a2); //--- search errors reinterr=MathMax(reinterr,MathAbs(r1[0]-a2[0].re)); reinterr=MathMax(reinterr,MathAbs(r1[1]-a2[n/2].re)); for(i=1;i<=n/2-1;i++) { reinterr=MathMax(reinterr,MathAbs(r1[2*i+0]-a2[i].re)); reinterr=MathMax(reinterr,MathAbs(r1[2*i+1]-a2[i].im)); } //--- Real backward FFT ArrayResize(r1,n); for(i=0;i<=n-1;i++) r1[i]=2*CMath::RandomReal()-1; ArrayResize(a2,(int)MathFloor((double)n/2.0)+1); a2[0]=r1[0]; for(i=1;i<=(int)MathFloor((double)n/2.0)-1;i++) { a2[i].re=r1[2*i+0]; a2[i].im=r1[2*i+1]; } a2[(int)MathFloor((double)n/2.0)]=r1[1]; //--- function call CFtBase::FtBaseGenerateComplexFFtPlan(n/2,plan); //--- allocation ArrayResize(buf,n); //--- function calls CFastFourierTransform::FFTR1DInvInternalEven(r1,n,buf,plan); CFastFourierTransform::FFTR1DInv(a2,n,r2); //--- search errors for(i=0;i<=n-1;i++) reinterr=MathMax(reinterr,MathAbs(r1[i]-r2[i])); } //--- search errors reinterrors=reinterrors || reinterr>errtol; //--- end waserrors=(((bidierrors || bidirerrors) || referrors) || refrerrors) || reinterrors; //--- check if(!silent) { Print("TESTING FFT"); Print("FINAL RESULT: "); //--- check if(waserrors) Print("FAILED"); else Print("OK"); Print("* BI-DIRECTIONAL COMPLEX TEST: "); //--- check if(bidierrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE COMPLEX FFT: "); //--- check if(referrors) Print("FAILED"); else Print("OK"); Print("* BI-DIRECTIONAL REAL TEST: "); //--- check if(bidirerrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE REAL FFT: "); //--- check if(refrerrors) Print("FAILED"); else Print("OK"); Print("* INTERNAL EVEN FFT: "); //--- check if(reinterrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference FFT | //+------------------------------------------------------------------+ static void CTestFFTUnit::RefFFTC1D(complex &a[],const int n) { //--- create variables int i=0; //--- create array double buf[]; //--- check if(!CAp::Assert(n>0,"FFTC1D: incorrect N!")) return; //--- allocation ArrayResize(buf,2*n); //--- copy for(i=0;i<=n-1;i++) { buf[2*i+0]=a[i].re; buf[2*i+1]=a[i].im; } //--- function call RefInternalCFFT(buf,n,false); //--- copy for(i=0;i<=n-1;i++) { a[i].re=buf[2*i+0]; a[i].im=buf[2*i+1]; } } //+------------------------------------------------------------------+ //| Reference inverse FFT | //+------------------------------------------------------------------+ static void CTestFFTUnit::RefFFTC1DInv(complex &a[],const int n) { //--- create variables int i=0; //--- create array double buf[]; //--- check if(!CAp::Assert(n>0,"FFTC1DInv: incorrect N!")) return; //--- allocation ArrayResize(buf,2*n); //--- copy for(i=0;i<=n-1;i++) { buf[2*i+0]=a[i].re; buf[2*i+1]=a[i].im; } //--- function call RefInternalCFFT(buf,n,true); //--- copy for(i=0;i<=n-1;i++) { a[i].re=buf[2*i+0]; a[i].im=buf[2*i+1]; } } //+------------------------------------------------------------------+ //| Internal complex FFT stub. | //| Uses straightforward formula with O(N^2) complexity. | //+------------------------------------------------------------------+ static void CTestFFTUnit::RefInternalCFFT(double &a[],const int nn, const bool inversefft) { //--- create variables int i=0; int k=0; double hre=0; double him=0; double c=0; double s=0; double re=0; double im=0; //--- create array double tmp[]; //--- allocation ArrayResize(tmp,2*nn); //--- check if(!inversefft) { for(i=0;i<=nn-1;i++) { //--- change values hre=0; him=0; //--- calculation for(k=0;k<=nn-1;k++) { re=a[2*k]; im=a[2*k+1]; c=MathCos(-(2*M_PI*k*i/nn)); s=MathSin(-(2*M_PI*k*i/nn)); hre=hre+c*re-s*im; him=him+c*im+s*re; } //--- change values tmp[2*i]=hre; tmp[2*i+1]=him; } for(i=0;i<=2*nn-1;i++) a[i]=tmp[i]; } else { for(k=0;k<=nn-1;k++) { //--- change values hre=0; him=0; //--- calculation for(i=0;i<=nn-1;i++) { re=a[2*i]; im=a[2*i+1]; c=MathCos(2*M_PI*k*i/nn); s=MathSin(2*M_PI*k*i/nn); hre=hre+c*re-s*im; him=him+c*im+s*re; } //--- change values tmp[2*k]=hre/nn; tmp[2*k+1]=him/nn; } for(i=0;i<=2*nn-1;i++) a[i]=tmp[i]; } } //+------------------------------------------------------------------+ //| Internal real FFT stub. | //| Uses straightforward formula with O(N^2) complexity. | //+------------------------------------------------------------------+ static void CTestFFTUnit::RefInternalRFFT(double &a[],const int nn,complex &f[]) { //--- create a variable int i=0; //--- create array double tmp[]; //--- allocation ArrayResize(tmp,2*nn); //--- copy for(i=0;i<=nn-1;i++) { tmp[2*i]=a[i]; tmp[2*i+1]=0; } //--- function call RefInternalCFFT(tmp,nn,false); //--- allocation ArrayResize(f,nn); //--- copy for(i=0;i<=nn-1;i++) { f[i].re=tmp[2*i+0]; f[i].im=tmp[2*i+1]; } } //+------------------------------------------------------------------+ //| Testing class CConv | //+------------------------------------------------------------------+ class CTestConvUnit { private: //--- private methods static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]); static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]); public: //--- constructor, destructor CTestConvUnit(void); ~CTestConvUnit(void); //--- public method static bool TestConv(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestConvUnit::CTestConvUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestConvUnit::~CTestConvUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestConvUnit::TestConv(const bool silent) { //--- create variables int m=0; int n=0; int i=0; int rkind=0; int circkind=0; int maxn=0; double referr=0; double refrerr=0; double inverr=0; double invrerr=0; double errtol=0; bool referrors; bool refrerrors; bool inverrors; bool invrerrors; bool waserrors; //--- create arrays double ra[]; double rb[]; double rr1[]; double rr2[]; complex ca[]; complex cb[]; complex cr1[]; complex cr2[]; //--- initialization maxn=32; errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon; referrors=false; refrerrors=false; inverrors=false; invrerrors=false; waserrors=false; //--- Test against reference O(N^2) implementation. //--- Automatic ConvC1D() and different algorithms of ConvC1DX() are tested. referr=0; refrerr=0; for(m=1;m<=maxn;m++) { for(n=1;n<=maxn;n++) { for(circkind=0;circkind<=1;circkind++) { for(rkind=-3;rkind<=1;rkind++) { //--- skip impossible combinations of parameters: //--- * circular convolution,M-3 - internal subroutine does not support M=n) { //--- test internal subroutine: //--- * circular/non-circular mode CConv::ConvC1DX(ca,m,cb,n,circkind!=0,rkind,0,cr1); } else { //--- test internal subroutine - circular mode only if(!CAp::Assert(circkind==0,"Convolution test: internal error!")) return(false); //--- function call CConv::ConvC1DX(cb,n,ca,m,false,rkind,0,cr1); } } //--- check if(circkind==0) RefConvC1D(ca,m,cb,n,cr2); else RefConvC1DCircular(ca,m,cb,n,cr2); //--- check if(circkind==0) { for(i=0;i<=m+n-2;i++) referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i])); } else { for(i=0;i<=m-1;i++) referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i])); } //--- Real convolution ArrayResize(ra,m); for(i=0;i<=m-1;i++) ra[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rb,n); for(i=0;i<=n-1;i++) rb[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rr1,1); //--- check if(rkind==-3) { //--- test wrapper subroutine: //--- * circular/non-circular if(circkind==0) CConv::ConvR1D(ra,m,rb,n,rr1); else CConv::ConvR1DCircular(ra,m,rb,n,rr1); } else { //--- check if(m>=n) { //--- test internal subroutine: //--- * circular/non-circular mode CConv::ConvR1DX(ra,m,rb,n,circkind!=0,rkind,0,rr1); } else { //--- test internal subroutine - non-circular mode only CConv::ConvR1DX(rb,n,ra,m,circkind!=0,rkind,0,rr1); } } //--- check if(circkind==0) RefConvR1D(ra,m,rb,n,rr2); else RefConvR1DCircular(ra,m,rb,n,rr2); //--- check if(circkind==0) { for(i=0;i<=m+n-2;i++) refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i])); } else { for(i=0;i<=m-1;i++) refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i])); } } } } } //--- search errors referrors=referrors || referr>errtol; refrerrors=refrerrors || refrerr>errtol; //--- Test inverse convolution inverr=0; invrerr=0; for(m=1;m<=maxn;m++) { for(n=1;n<=maxn;n++) { //--- Complex circilar and non-circular ArrayResize(ca,m); for(i=0;i<=m-1;i++) { ca[i].re=2*CMath::RandomReal()-1; ca[i].im=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(cb,n); for(i=0;i<=n-1;i++) { cb[i].re=2*CMath::RandomReal()-1; cb[i].im=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(cr1,1); ArrayResize(cr2,1); //--- function calls CConv::ConvC1D(ca,m,cb,n,cr2); CConv::ConvC1DInv(cr2,m+n-1,cb,n,cr1); //--- search errors for(i=0;i<=m-1;i++) { inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i])); } //--- allocation ArrayResize(cr1,1); ArrayResize(cr2,1); //--- function calls CConv::ConvC1DCircular(ca,m,cb,n,cr2); CConv::ConvC1DCircularInv(cr2,m,cb,n,cr1); //--- search errors for(i=0;i<=m-1;i++) inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i])); //--- Real circilar and non-circular ArrayResize(ra,m); for(i=0;i<=m-1;i++) ra[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rb,n); for(i=0;i<=n-1;i++) rb[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rr1,1); ArrayResize(rr2,1); //--- function calls CConv::ConvR1D(ra,m,rb,n,rr2); CConv::ConvR1DInv(rr2,m+n-1,rb,n,rr1); //--- search errors for(i=0;i<=m-1;i++) invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i])); //--- allocation ArrayResize(rr1,1); ArrayResize(rr2,1); //--- function calls CConv::ConvR1DCircular(ra,m,rb,n,rr2); CConv::ConvR1DCircularInv(rr2,m,rb,n,rr1); //--- search errors for(i=0;i<=m-1;i++) invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i])); } } //--- search errors inverrors=inverrors || inverr>errtol; invrerrors=invrerrors || invrerr>errtol; //--- end waserrors=((referrors || refrerrors) || inverrors) || invrerrors; //--- check if(!silent) { Print("TESTING CONVOLUTION"); Print("FINAL RESULT: "); //--- check if(waserrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE COMPLEX CONV: "); //--- check if(referrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE REAL CONV: "); //--- check if(refrerrors) Print("FAILED"); else Print("OK"); Print("* COMPLEX INVERSE: "); //--- check if(inverrors) Print("FAILED"); else Print("OK"); Print("* REAL INVERSE: "); //--- check if(invrerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestConvUnit::RefConvC1D(complex &a[],const int m,complex &b[], const int n,complex &r[]) { //--- create variables int i=0; complex v=0; int i_=0; int i1_=0; //--- allocation ArrayResize(r,m+n-1); //--- initialization for(i=0;i<=m+n-2;i++) r[i]=0; //--- calculation for(i=0;i<=m-1;i++) { v=a[i]; i1_=-i; for(i_=i;i_<=i+n-1;i_++) r[i_]=r[i_]+v*b[i_+i1_]; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestConvUnit::RefConvC1DCircular(complex &a[],const int m, complex &b[],const int n, complex &r[]) { //--- create variables int i1=0; int i2=0; int j2=0; int i_=0; int i1_=0; //--- create array complex buf[]; //--- function call RefConvC1D(a,m,b,n,buf); //--- allocation ArrayResize(r,m); //--- copy for(i_=0;i_<=m-1;i_++) r[i_]=buf[i_]; //--- calculation i1=m; while(i1<=m+n-2) { //--- change values i2=MathMin(i1+m-1,m+n-2); j2=i2-i1; i1_=i1; for(i_=0;i_<=j2;i_++) r[i_]=r[i_]+buf[i_+i1_]; i1=i1+m; } } //+------------------------------------------------------------------+ //| Reference FFT | //+------------------------------------------------------------------+ static void CTestConvUnit::RefConvR1D(double &a[],const int m,double &b[], const int n,double &r[]) { //--- create variables int i=0; double v=0; int i_=0; int i1_=0; //--- allocation ArrayResize(r,m+n-1); //--- initialization for(i=0;i<=m+n-2;i++) r[i]=0; //--- calculation for(i=0;i<=m-1;i++) { v=a[i]; i1_=-i; for(i_=i;i_<=i+n-1;i_++) r[i_]=r[i_]+v*b[i_+i1_]; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestConvUnit::RefConvR1DCircular(double &a[],const int m, double &b[],const int n, double &r[]) { //--- create variables int i1=0; int i2=0; int j2=0; int i_=0; int i1_=0; //--- create array double buf[]; //--- function call RefConvR1D(a,m,b,n,buf); //--- allocation ArrayResize(r,m); //--- copy for(i_=0;i_<=m-1;i_++) r[i_]=buf[i_]; //--- calculation i1=m; while(i1<=m+n-2) { //--- change values i2=MathMin(i1+m-1,m+n-2); j2=i2-i1; i1_=i1; for(i_=0;i_<=j2;i_++) r[i_]=r[i_]+buf[i_+i1_]; i1=i1+m; } } //+------------------------------------------------------------------+ //| Testing class CCorr | //+------------------------------------------------------------------+ class CTestCorrUnit { private: //--- private methods static void RefCorrC1D(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]); static void RefCorrC1DCircular(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]); static void RefCorrR1D(double &signal[],const int n,double &pattern[],const int m,double &r[]); static void RefCorrR1DCircular(double &signal[],const int n,double &pattern[],const int m,double &r[]); static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]); static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]); public: //--- constructor, destructor CTestCorrUnit(void); ~CTestCorrUnit(void); //--- public method static bool TestCorr(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestCorrUnit::CTestCorrUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestCorrUnit::~CTestCorrUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CCorr | //+------------------------------------------------------------------+ static bool CTestCorrUnit::TestCorr(const bool silent) { //--- create variables int m=0; int n=0; int i=0; int maxn=0; double referr=0; double refrerr=0; double errtol=0; bool referrors; bool refrerrors; bool inverrors; bool invrerrors; bool waserrors; //--- create arrays double ra[]; double rb[]; double rr1[]; double rr2[]; complex ca[]; complex cb[]; complex cr1[]; complex cr2[]; //--- initialization maxn=32; errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon; referrors=false; refrerrors=false; inverrors=false; invrerrors=false; waserrors=false; //--- Test against reference O(N^2) implementation. referr=0; refrerr=0; for(m=1;m<=maxn;m++) { for(n=1;n<=maxn;n++) { //--- Complex correlation ArrayResize(ca,m); for(i=0;i<=m-1;i++) { ca[i].re=2*CMath::RandomReal()-1; ca[i].im=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(cb,n); for(i=0;i<=n-1;i++) { cb[i].re=2*CMath::RandomReal()-1; cb[i].im=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(cr1,1); //--- function calls CCorr::CorrC1D(ca,m,cb,n,cr1); RefCorrC1D(ca,m,cb,n,cr2); //--- search errors for(i=0;i<=m+n-2;i++) referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i])); //--- allocation ArrayResize(cr1,1); //--- function calls CCorr::CorrC1DCircular(ca,m,cb,n,cr1); RefCorrC1DCircular(ca,m,cb,n,cr2); //--- search errors for(i=0;i<=m-1;i++) referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i])); //--- Real correlation ArrayResize(ra,m); for(i=0;i<=m-1;i++) ra[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rb,n); for(i=0;i<=n-1;i++) rb[i]=2*CMath::RandomReal()-1; //--- allocation ArrayResize(rr1,1); //--- function calls CCorr::CorrR1D(ra,m,rb,n,rr1); RefCorrR1D(ra,m,rb,n,rr2); //--- search errors for(i=0;i<=m+n-2;i++) refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i])); //--- allocation ArrayResize(rr1,1); //--- function calls CCorr::CorrR1DCircular(ra,m,rb,n,rr1); RefCorrR1DCircular(ra,m,rb,n,rr2); //--- search errors for(i=0;i<=m-1;i++) refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i])); } } //--- search errors referrors=referrors || referr>errtol; refrerrors=refrerrors || refrerr>errtol; //--- end waserrors=referrors || refrerrors; //--- check if(!silent) { Print("TESTING CORRELATION"); Print("FINAL RESULT: "); //--- check if(waserrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE COMPLEX CORR: "); //--- check if(referrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE REAL CORR: "); //--- check if(refrerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefCorrC1D(complex &signal[],const int n, complex &pattern[],const int m, complex &r[]) { //--- create variables int i=0; int j=0; complex v=0; int i_=0; //--- create array complex s[]; //--- allocation ArrayResize(s,m+n-1); //--- change values for(i_=0;i_<=n-1;i_++) s[i_]=signal[i_]; for(i=n;i<=m+n-2;i++) s[i]=0; //--- allocation ArrayResize(r,m+n-1); //--- calculation for(i=0;i<=n-1;i++) { v=0; for(j=0;j<=m-1;j++) { //--- check if(i+j>=n) break; v=v+CMath::Conj(pattern[j])*s[i+j]; } r[i]=v; } //--- calculation for(i=1;i<=m-1;i++) { v=0; for(j=i;j<=m-1;j++) v=v+CMath::Conj(pattern[j])*s[j-i]; r[m+n-1-i]=v; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefCorrC1DCircular(complex &signal[],const int n, complex &pattern[],const int m, complex &r[]) { //--- create variables int i=0; int j=0; complex v=0; //--- allocation ArrayResize(r,n); //--- calculation for(i=0;i<=n-1;i++) { //--- change value v=0; for(j=0;j<=m-1;j++) v=v+CMath::Conj(pattern[j])*signal[(i+j)%n]; r[i]=v; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefCorrR1D(double &signal[],const int n, double &pattern[],const int m, double &r[]) { //--- create variables int i=0; int j=0; double v=0; int i_=0; //--- create array double s[]; //--- allocation ArrayResize(s,m+n-1); //--- change values for(i_=0;i_<=n-1;i_++) s[i_]=signal[i_]; for(i=n;i<=m+n-2;i++) s[i]=0; //--- allocation ArrayResize(r,m+n-1); //--- calculation for(i=0;i<=n-1;i++) { v=0; for(j=0;j<=m-1;j++) { //--- check if(i+j>=n) break; v=v+pattern[j]*s[i+j]; } r[i]=v; } //--- calculation for(i=1;i<=m-1;i++) { v=0; for(j=i;j<=m-1;j++) v=v+pattern[j]*s[-i+j]; r[m+n-1-i]=v; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefCorrR1DCircular(double &signal[],const int n, double &pattern[],const int m, double &r[]) { //--- create variables int i=0; int j=0; double v=0; //--- allocation ArrayResize(r,n); //--- calculation for(i=0;i<=n-1;i++) { v=0; for(j=0;j<=m-1;j++) v=v+pattern[j]*signal[(i+j)%n]; r[i]=v; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefConvC1D(complex &a[],const int m,complex &b[], const int n,complex &r[]) { //--- create variables int i=0; complex v=0; int i_=0; int i1_=0; //--- allocation ArrayResize(r,m+n-1); //--- initialization for(i=0;i<=m+n-2;i++) r[i]=0; //--- calculation for(i=0;i<=m-1;i++) { v=a[i]; i1_=-i; for(i_=i;i_<=i+n-1;i_++) r[i_]=r[i_]+v*b[i_+i1_]; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefConvC1DCircular(complex &a[],const int m, complex &b[],const int n, complex &r[]) { //--- create variables int i1=0; int i2=0; int j2=0; int i_=0; int i1_=0; //--- create array complex buf[]; //--- function call RefConvC1D(a,m,b,n,buf); //--- allocation ArrayResize(r,m); //--- copy for(i_=0;i_<=m-1;i_++) r[i_]=buf[i_]; //--- calculation i1=m; while(i1<=m+n-2) { //--- change values i2=MathMin(i1+m-1,m+n-2); j2=i2-i1; i1_=i1; for(i_=0;i_<=j2;i_++) r[i_]=r[i_]+buf[i_+i1_]; i1=i1+m; } } //+------------------------------------------------------------------+ //| Reference FFT | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefConvR1D(double &a[],const int m,double &b[], const int n,double &r[]) { //--- create variables int i=0; double v=0; int i_=0; int i1_=0; //--- allocation ArrayResize(r,m+n-1); //--- initialization for(i=0;i<=m+n-2;i++) r[i]=0; //--- calculation for(i=0;i<=m-1;i++) { v=a[i]; i1_=-i; for(i_=i;i_<=i+n-1;i_++) r[i_]=r[i_]+v*b[i_+i1_]; } } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ static void CTestCorrUnit::RefConvR1DCircular(double &a[],const int m, double &b[],const int n, double &r[]) { //--- create variables int i1=0; int i2=0; int j2=0; int i_=0; int i1_=0; //--- create array double buf[]; //--- function call RefConvR1D(a,m,b,n,buf); //--- allocation ArrayResize(r,m); //--- copy for(i_=0;i_<=m-1;i_++) r[i_]=buf[i_]; //--- calculation i1=m; while(i1<=m+n-2) { //--- change values i2=MathMin(i1+m-1,m+n-2); j2=i2-i1; i1_=i1; for(i_=0;i_<=j2;i_++) r[i_]=r[i_]+buf[i_+i1_]; i1=i1+m; } } //+------------------------------------------------------------------+ //| Testing class CFastHartleyTransform | //+------------------------------------------------------------------+ class CTestFHTUnit { private: //--- private methods static void RefFHTR1D(double &a[],const int n); static void RefFHTR1DInv(double &a[],const int n); public: //--- constructor, destructor CTestFHTUnit(void); ~CTestFHTUnit(void); //--- public method static bool TestFHT(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestFHTUnit::CTestFHTUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestFHTUnit::~CTestFHTUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestFHTUnit::TestFHT(const bool silent) { //--- create variables int n=0; int i=0; int maxn=0; double bidierr=0; double referr=0; double errtol=0; bool referrors; bool bidierrors; bool waserrors; //--- create arrays double r1[]; double r2[]; double r3[]; //--- initialization maxn=128; errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon; bidierrors=false; referrors=false; waserrors=false; //--- Test bi-directional error: norm(x-invFHT(FHT(x))) bidierr=0; for(n=1;n<=maxn;n++) { //--- FHT/invFHT ArrayResize(r1,n); ArrayResize(r2,n); ArrayResize(r3,n); for(i=0;i<=n-1;i++) { r1[i]=2*CMath::RandomReal()-1; r2[i]=r1[i]; r3[i]=r1[i]; } //--- function calls CFastHartleyTransform::FHTR1D(r2,n); CFastHartleyTransform::FHTR1DInv(r2,n); CFastHartleyTransform::FHTR1DInv(r3,n); CFastHartleyTransform::FHTR1D(r3,n); //--- search errors for(i=0;i<=n-1;i++) { bidierr=MathMax(bidierr,MathAbs(r1[i]-r2[i])); bidierr=MathMax(bidierr,MathAbs(r1[i]-r3[i])); } } //--- search errors bidierrors=bidierrors || bidierr>errtol; //--- Test against reference O(N^2) implementation referr=0; for(n=1;n<=maxn;n++) { //--- FHT ArrayResize(r1,n); ArrayResize(r2,n); for(i=0;i<=n-1;i++) { r1[i]=2*CMath::RandomReal()-1; r2[i]=r1[i]; } //--- function calls CFastHartleyTransform::FHTR1D(r1,n); RefFHTR1D(r2,n); //--- search errors for(i=0;i<=n-1;i++) referr=MathMax(referr,MathAbs(r1[i]-r2[i])); //--- inverse FHT ArrayResize(r1,n); ArrayResize(r2,n); for(i=0;i<=n-1;i++) { r1[i]=2*CMath::RandomReal()-1; r2[i]=r1[i]; } //--- function calls CFastHartleyTransform::FHTR1DInv(r1,n); RefFHTR1DInv(r2,n); //--- search errors for(i=0;i<=n-1;i++) referr=MathMax(referr,MathAbs(r1[i]-r2[i])); } //--- search errors referrors=referrors || referr>errtol; //--- end waserrors=bidierrors || referrors; //--- check if(!silent) { Print("TESTING FHT"); Print("FINAL RESULT: "); //--- check if(waserrors) Print("FAILED"); else Print("OK"); Print("* BI-DIRECTIONAL TEST: "); //--- check if(bidierrors) Print("FAILED"); else Print("OK"); Print("* AGAINST REFERENCE FHT: "); //--- check if(referrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference FHT | //+------------------------------------------------------------------+ static void CTestFHTUnit::RefFHTR1D(double &a[],const int n) { //--- create variables int i=0; int j=0; double v=0; //--- create array double buf[]; //--- check if(!CAp::Assert(n>0,"RefFHTR1D: incorrect N!")) return; //--- allocation ArrayResize(buf,n); //--- calculation for(i=0;i<=n-1;i++) { v=0; for(j=0;j<=n-1;j++) v=v+a[j]*(MathCos(2*M_PI*i*j/n)+MathSin(2*M_PI*i*j/n)); buf[i]=v; } //--- copy for(i=0;i<=n-1;i++) a[i]=buf[i]; } //+------------------------------------------------------------------+ //| Reference inverse FHT | //+------------------------------------------------------------------+ static void CTestFHTUnit::RefFHTR1DInv(double &a[],const int n) { //--- create a variable int i=0; //--- check if(!CAp::Assert(n>0,"RefFHTR1DInv: incorrect N!")) return; //--- function call RefFHTR1D(a,n); //--- change values for(i=0;i<=n-1;i++) a[i]=a[i]/n; } //+------------------------------------------------------------------+ //| Testing class CGaussQ | //+------------------------------------------------------------------+ class CTestGQUnit { private: //--- private methods static double MapKind(const int k); static void BuildGaussLegendreQuadrature(const int n,double &x[],double &w[]); static void BuildGaussJacobiQuadrature(const int n,const double alpha,const double beta,double &x[],double &w[]); static void BuildGaussLaguerreQuadrature(const int n,const double alpha,double &x[],double &w[]); static void BuildGaussHermiteQuadrature(const int n,double &x[],double &w[]); public: //--- constructor, destructor CTestGQUnit(void); ~CTestGQUnit(void); //--- public method static bool TestGQ(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestGQUnit::CTestGQUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestGQUnit::~CTestGQUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestGQUnit::TestGQ(const bool silent) { //--- create variables double err=0; int n=0; int i=0; int info=0; int akind=0; int bkind=0; double alphac=0; double betac=0; double errtol=0; double nonstricterrtol=0; double stricterrtol=0; bool recerrors; bool specerrors; bool waserrors; //--- create arrays double alpha[]; double beta[]; double x[]; double w[]; double x2[]; double w2[]; //--- initialization recerrors=false; specerrors=false; waserrors=false; errtol=1.0E-12; nonstricterrtol=1.0E-6; stricterrtol=1000*CMath::m_machineepsilon; //--- Three tests for rec-based Gauss quadratures with known weights/nodes: //--- 1. Gauss-Legendre with N=2 //--- 2. Gauss-Legendre with N=5 //--- 3. Gauss-Chebyshev with N=1,2,4,8,...,512 err=0; ArrayResize(alpha,2); ArrayResize(beta,2); alpha[0]=0; alpha[1]=0; beta[1]=1.0/(double)(4*1*1-1); //--- function call CGaussQ::GQGenerateRec(alpha,beta,2.0,2,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+MathSqrt(3)/3)); err=MathMax(err,MathAbs(x[1]-MathSqrt(3)/3)); err=MathMax(err,MathAbs(w[0]-1)); err=MathMax(err,MathAbs(w[1]-1)); for(i=0;i<=0;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,5); ArrayResize(beta,5); //--- change values alpha[0]=0; for(i=1;i<=4;i++) { alpha[i]=0; beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1); } //--- function call CGaussQ::GQGenerateRec(alpha,beta,2.0,5,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+MathSqrt(245+14*MathSqrt(70))/21)); err=MathMax(err,MathAbs(x[0]+x[4])); err=MathMax(err,MathAbs(x[1]+MathSqrt(245-14*MathSqrt(70))/21)); err=MathMax(err,MathAbs(x[1]+x[3])); err=MathMax(err,MathAbs(x[2])); err=MathMax(err,MathAbs(w[0]-(322-13*MathSqrt(70))/900)); err=MathMax(err,MathAbs(w[0]-w[4])); err=MathMax(err,MathAbs(w[1]-(322+13*MathSqrt(70))/900)); err=MathMax(err,MathAbs(w[1]-w[3])); err=MathMax(err,MathAbs(w[2]-128.0/225.0)); for(i=0;i<=3;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- calculation n=1; while(n<=512) { //--- allocation ArrayResize(alpha,n); ArrayResize(beta,n); for(i=0;i<=n-1;i++) { alpha[i]=0; //--- check if(i==0) beta[i]=0; //--- check if(i==1) beta[i]=1.0/2.0; //--- check if(i>1) beta[i]=1.0/4.0; } //--- function call CGaussQ::GQGenerateRec(alpha,beta,M_PI,n,info,x,w); //--- check if(info>0) { //--- search errors for(i=0;i<=n-1;i++) { err=MathMax(err,MathAbs(x[i]-MathCos(M_PI*(n-i-0.5)/n))); err=MathMax(err,MathAbs(w[i]-M_PI/n)); } for(i=0;i<=n-2;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; n=n*2; } //--- search errors recerrors=recerrors || err>errtol; //--- Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes: //--- 1. Gauss-Lobatto with N=3 //--- 2. Gauss-Lobatto with N=4 //--- 3. Gauss-Lobatto with N=6 err=0; ArrayResize(alpha,2); ArrayResize(beta,2); alpha[0]=0; alpha[1]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); //--- function call CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,3,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1])); err=MathMax(err,MathAbs(x[2]-1)); err=MathMax(err,MathAbs(w[0]-1.0/3.0)); err=MathMax(err,MathAbs(w[1]-4.0/3.0)); err=MathMax(err,MathAbs(w[2]-1.0/3.0)); for(i=0;i<=1;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,3); ArrayResize(beta,3); //--- change values alpha[0]=0; alpha[1]=0; alpha[2]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); beta[2]=(double)(2*2)/(double)(4*2*2-1); //--- function call CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,4,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]+MathSqrt(5)/5)); err=MathMax(err,MathAbs(x[2]-MathSqrt(5)/5)); err=MathMax(err,MathAbs(x[3]-1)); err=MathMax(err,MathAbs(w[0]-1.0/6.0)); err=MathMax(err,MathAbs(w[1]-5.0/6.0)); err=MathMax(err,MathAbs(w[2]-5.0/6.0)); err=MathMax(err,MathAbs(w[3]-1.0/6.0)); for(i=0;i<=2;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,5); ArrayResize(beta,5); //--- change values alpha[0]=0; alpha[1]=0; alpha[2]=0; alpha[3]=0; alpha[4]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); beta[2]=(double)(2*2)/(double)(4*2*2-1); beta[3]=(double)(3*3)/(double)(4*3*3-1); beta[4]=(double)(4*4)/(double)(4*4*4-1); //--- function call CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,6,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]+MathSqrt((7+2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[2]+MathSqrt((7-2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[3]-MathSqrt((7-2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[4]-MathSqrt((7+2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[5]-1)); err=MathMax(err,MathAbs(w[0]-1.0/(double)15)); err=MathMax(err,MathAbs(w[1]-(14-MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[2]-(14+MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[3]-(14+MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[4]-(14-MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[5]-1.0/(double)15)); for(i=0;i<=4;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; recerrors=recerrors || err>errtol; //--- Three tests for rec-based Gauss-Radau quadratures with known weights/nodes: //--- 1. Gauss-Radau with N=2 //--- 2. Gauss-Radau with N=3 //--- 3. Gauss-Radau with N=3 (another case) err=0; ArrayResize(alpha,1); ArrayResize(beta,2); alpha[0]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); //--- function call CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,2,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]-1.0/3.0)); err=MathMax(err,MathAbs(w[0]-0.5)); err=MathMax(err,MathAbs(w[1]-1.5)); for(i=0;i<=0;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,2); ArrayResize(beta,3); //--- change values alpha[0]=0; alpha[1]=0; for(i=0;i<=2;i++) beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1); //--- function call CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,3,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]-(1-MathSqrt(6))/5)); err=MathMax(err,MathAbs(x[2]-(1+MathSqrt(6))/5)); err=MathMax(err,MathAbs(w[0]-2.0/9.0)); err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18)); err=MathMax(err,MathAbs(w[2]-(16-MathSqrt(6))/18)); for(i=0;i<=1;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,2); ArrayResize(beta,3); alpha[0]=0; alpha[1]=0; for(i=0;i<=2;i++) beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1); //--- function call CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,1,3,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[2]-1)); err=MathMax(err,MathAbs(x[1]+(1-MathSqrt(6))/5)); err=MathMax(err,MathAbs(x[0]+(1+MathSqrt(6))/5)); err=MathMax(err,MathAbs(w[2]-2.0/9.0)); err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18)); err=MathMax(err,MathAbs(w[0]-(16-MathSqrt(6))/18)); for(i=0;i<=1;i++) recerrors=recerrors || x[i]>=x[i+1]; } else recerrors=true; //--- search errors recerrors=recerrors || err>errtol; //--- test recurrence-based special cases (Legendre,Jacobi,Hermite,...) //--- against another implementation (polynomial root-finder) for(n=1;n<=20;n++) { //--- test gauss-legendre err=0; CGaussQ::GQGenerateGaussLegendre(n,info,x,w); //--- check if(info>0) { BuildGaussLegendreQuadrature(n,x2,w2); //--- search errors for(i=0;i<=n-1;i++) { err=MathMax(err,MathAbs(x[i]-x2[i])); err=MathMax(err,MathAbs(w[i]-w2[i])); } } else specerrors=true; //--- search errors specerrors=specerrors || err>errtol; //--- Test Gauss-Jacobi. //--- Since task is much more difficult we will use less strict //--- threshold. err=0; for(akind=0;akind<=9;akind++) { for(bkind=0;bkind<=9;bkind++) { alphac=MapKind(akind); betac=MapKind(bkind); //--- function call CGaussQ::GQGenerateGaussJacobi(n,alphac,betac,info,x,w); //--- check if(info>0) { BuildGaussJacobiQuadrature(n,alphac,betac,x2,w2); //--- search errors for(i=0;i<=n-1;i++) { err=MathMax(err,MathAbs(x[i]-x2[i])); err=MathMax(err,MathAbs(w[i]-w2[i])); } } else specerrors=true; } } //--- search errors specerrors=specerrors || err>nonstricterrtol; //--- special test for Gauss-Jacobi (Chebyshev weight //--- function with analytically known nodes/weights) err=0; CGaussQ::GQGenerateGaussJacobi(n,-0.5,-0.5,info,x,w); //--- check if(info>0) { //--- search errors for(i=0;i<=n-1;i++) { err=MathMax(err,MathAbs(x[i]+MathCos(M_PI*(i+0.5)/n))); err=MathMax(err,MathAbs(w[i]-M_PI/n)); } } else specerrors=true; //--- search errors specerrors=specerrors || err>stricterrtol; //--- Test Gauss-Laguerre err=0; for(akind=0;akind<=9;akind++) { alphac=MapKind(akind); //--- function call CGaussQ::GQGenerateGaussLaguerre(n,alphac,info,x,w); //--- check if(info>0) { BuildGaussLaguerreQuadrature(n,alphac,x2,w2); //--- search errors for(i=0;i<=n-1;i++) { err=MathMax(err,MathAbs(x[i]-x2[i])); err=MathMax(err,MathAbs(w[i]-w2[i])); } } else specerrors=true; } //--- search errors specerrors=specerrors || err>nonstricterrtol; //--- Test Gauss-Hermite err=0; CGaussQ::GQGenerateGaussHermite(n,info,x,w); //--- check if(info>0) { BuildGaussHermiteQuadrature(n,x2,w2); //--- search errors for(i=0;i<=n-1;i++) { err=MathMax(err,MathAbs(x[i]-x2[i])); err=MathMax(err,MathAbs(w[i]-w2[i])); } } else specerrors=true; //--- search errors specerrors=specerrors || err>nonstricterrtol; } //--- end waserrors=recerrors || specerrors; //--- check if(!silent) { Print("TESTING GAUSS QUADRATURES"); Print("FINAL RESULT: "); //--- check if(waserrors) Print("FAILED"); else Print("OK"); Print("* SPECIAL CASES (LEGENDRE/JACOBI/..) "); //--- check if(specerrors) Print("FAILED"); else Print("OK"); Print("* RECURRENCE-BASED: "); //--- check if(recerrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Maps: | //| 0=> -0.9 | //| 1=> -0.5 | //| 2=> -0.1 | //| 3=> 0.0 | //| 4=> +0.1 | //| 5=> +0.5 | //| 6=> +0.9 | //| 7=> +1.0 | //| 8=> +1.5 | //| 9=> +2.0 | //+------------------------------------------------------------------+ static double CTestGQUnit::MapKind(const int k) { //--- create a variable double result=0; //--- check if(k==0) result=-0.9; //--- check if(k==1) result=-0.5; //--- check if(k==2) result=-0.1; //--- check if(k==3) result=0.0; //--- check if(k==4) result=0.1; //--- check if(k==5) result=0.5; //--- check if(k==6) result=0.9; //--- check if(k==7) result=1.0; //--- check if(k==8) result=1.5; //--- check if(k==9) result=2.0; //--- return result return(result); } //+------------------------------------------------------------------+ //| Gauss-Legendre, another variant | //+------------------------------------------------------------------+ static void CTestGQUnit::BuildGaussLegendreQuadrature(const int n,double &x[], double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double p1=0; double p2=0; double p3=0; double dp3=0; double tmp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); //--- calculation for(i=0;i<=(n+1)/2-1;i++) { r=MathCos(M_PI*(4*i+3)/(4*n+2)); //--- cycle do { //--- change values p2=0; p3=1; //--- calculation for(j=0;j<=n-1;j++) { p1=p2; p2=p3; p3=((2*j+1)*r*p2-j*p1)/(j+1); } dp3=n*(r*p3-p2)/(r*r-1); r1=r; r=r-p3/dp3; } while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100); //--- calculation x[i]=r; x[n-1-i]=-r; w[i]=2/((1-r*r)*dp3*dp3); w[n-1-i]=2/((1-r*r)*dp3*dp3); } //--- shift for(i=0;i<=n-1;i++) { for(j=0;j<=n-2-i;j++) { //--- check if(x[j]>=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Gauss-Jacobi, another variant | //+------------------------------------------------------------------+ static void CTestGQUnit::BuildGaussJacobiQuadrature(const int n,const double alpha, const double beta,double &x[], double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double t1=0; double t2=0; double t3=0; double p1=0; double p2=0; double p3=0; double pp=0; double an=0; double bn=0; double a=0; double b=0; double c=0; double tmpsgn=0; double tmp=0; double alfbet=0; double temp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); for(i=0;i<=n-1;i++) { //--- check if(i==0) { //--- calculation an=alpha/n; bn=beta/n; t1=(1+alpha)*(2.78/(4+n*n)+0.768*an/n); t2=1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn; r=(t2-t1)/t2; } else { //--- check if(i==1) { //--- calculation t1=(4.1+alpha)/((1+alpha)*(1+0.156*alpha)); t2=1+0.06*(n-8)*(1+0.12*alpha)/n; t3=1+0.012*beta*(1+0.25*MathAbs(alpha))/n; r=r-t1*t2*t3*(1-r); } else { //--- check if(i==2) { //--- calculation t1=(1.67+0.28*alpha)/(1+0.37*alpha); t2=1+0.22*(n-8)/n; t3=1+8*beta/((6.28+beta)*n*n); r=r-t1*t2*t3*(x[0]-r); } else { //--- check if(i=CMath::m_machineepsilon*(1+MathAbs(r))*100); //--- change values x[i]=r; w[i]=MathExp(CGammaFunc::LnGamma(alpha+n,tmpsgn)+CGammaFunc::LnGamma(beta+n,tmpsgn)-CGammaFunc::LnGamma(n+1,tmpsgn)-CGammaFunc::LnGamma(n+alfbet+1,tmpsgn))*temp*MathPow(2,alfbet)/(pp*p2); } //--- shift for(i=0;i<=n-1;i++) { for(j=0;j<=n-2-i;j++) { //--- check if(x[j]>=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Gauss-Laguerre, another variant | //+------------------------------------------------------------------+ static void CTestGQUnit::BuildGaussLaguerreQuadrature(const int n,const double alpha, double &x[],double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double p1=0; double p2=0; double p3=0; double dp3=0; double tsg=0; double tmp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(i==0) r=(1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha); else { //--- check if(i==1) r=r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n); else r=r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x[i-2]); } do { //--- change values p2=0; p3=1; //--- calculation for(j=0;j<=n-1;j++) { p1=p2; p2=p3; p3=((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1); } dp3=(n*p3-(n+alpha)*p2)/r; r1=r; r=r-p3/dp3; } //--- change values while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100); //--- change values x[i]=r; w[i]=-(MathExp(CGammaFunc::LnGamma(alpha+n,tsg)-CGammaFunc::LnGamma(n,tsg))/(dp3*n*p2)); } //--- shift for(i=0;i<=n-1;i++) { for(j=0;j<=n-2-i;j++) { //--- check if(x[j]>=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Gauss-Hermite, another variant | //+------------------------------------------------------------------+ static void CTestGQUnit::BuildGaussHermiteQuadrature(const int n,double &x[], double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double p1=0; double p2=0; double p3=0; double dp3=0; double pipm4=0; double tmp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); //--- calculation pipm4=MathPow(M_PI,-0.25); for(i=0;i<=(n+1)/2-1;i++) { //--- check if(i==0) r=MathSqrt(2*n+1)-1.85575*MathPow(2*n+1,-(1.0/6.0)); else { //--- check if(i==1) r=r-1.14*MathPow(n,0.426)/r; else { //--- check if(i==2) r=1.86*r-0.86*x[0]; else { //--- check if(i==3) r=1.91*r-0.91*x[1]; else r=2*r-x[i-2]; } } } //--- cycle do { //--- change values p2=0; p3=pipm4; //--- calculation for(j=0;j<=n-1;j++) { p1=p2; p2=p3; p3=p2*r*MathSqrt(2.0/(double)(j+1))-p1*MathSqrt((double)j/(double)(j+1)); } dp3=MathSqrt(2*j)*p2; r1=r; r=r-p3/dp3; } while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100); //--- change values x[i]=r; w[i]=2/(dp3*dp3); x[n-1-i]=-x[i]; w[n-1-i]=w[i]; } //--- shift for(i=0;i<=n-1;i++) { for(j=0;j<=n-2-i;j++) { //--- check if(x[j]>=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Testing class CGaussKronrodQ | //+------------------------------------------------------------------+ class CTestGKQUnit { private: //--- private method static double MapKind(const int k); public: //--- constructor, destructor CTestGKQUnit(void); ~CTestGKQUnit(void); //--- public method static bool TestGKQ(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestGKQUnit::CTestGKQUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestGKQUnit::~CTestGKQUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestGKQUnit::TestGKQ(const bool silent) { //--- create variables int pkind=0; double errtol=0; double eps=0; double nonstricterrtol=0; int n=0; int i=0; int k=0; int info=0; double err=0; int akind=0; int bkind=0; double alphac=0; double betac=0; int info1=0; int info2=0; bool successatleastonce; bool intblerrors; bool vstblerrors; bool generrors; bool waserrors; //--- create arrays double x1[]; double wg1[]; double wk1[]; double x2[]; double wg2[]; double wk2[]; //--- initialization intblerrors=false; vstblerrors=false; generrors=false; waserrors=false; errtol=10000*CMath::m_machineepsilon; nonstricterrtol=1000*errtol; //--- test recurrence-based Legendre nodes against the precalculated table for(pkind=0;pkind<=5;pkind++) { n=0; //--- check if(pkind==0) n=15; //--- check if(pkind==1) n=21; //--- check if(pkind==2) n=31; //--- check if(pkind==3) n=41; //--- check if(pkind==4) n=51; //--- check if(pkind==5) n=61; //--- function calls CGaussKronrodQ::GKQLegendreCalc(n,info,x1,wk1,wg1); CGaussKronrodQ::GKQLegendreTbl(n,x2,wk2,wg2,eps); //--- check if(info<=0) { generrors=true; break; } //--- search errors for(i=0;i<=n-1;i++) { vstblerrors=vstblerrors || MathAbs(x1[i]-x2[i])>errtol; vstblerrors=vstblerrors || MathAbs(wk1[i]-wk2[i])>errtol; vstblerrors=vstblerrors || MathAbs(wg1[i]-wg2[i])>errtol; } } //--- Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes //--- calculated with subroutines from GQ unit. for(k=1;k<=30;k++) { n=2*k+1; //--- Gauss-Legendre err=0; CGaussKronrodQ::GKQGenerateGaussLegendre(n,info1,x1,wk1,wg1); CGaussQ::GQGenerateGaussLegendre(k,info2,x2,wg2); //--- check if(info1>0 && info2>0) { //--- search errors for(i=0;i<=k-1;i++) { err=MathMax(err,MathAbs(x1[2*i+1]-x2[i])); err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i])); } } else generrors=true; //--- search errors generrors=generrors || err>errtol; } //--- calculation for(k=1;k<=15;k++) { n=2*k+1; //--- Gauss-Jacobi successatleastonce=false; err=0; for(akind=0;akind<=9;akind++) { for(bkind=0;bkind<=9;bkind++) { //--- change values alphac=MapKind(akind); betac=MapKind(bkind); //--- function calls CGaussKronrodQ::GKQGenerateGaussJacobi(n,alphac,betac,info1,x1,wk1,wg1); CGaussQ::GQGenerateGaussJacobi(k,alphac,betac,info2,x2,wg2); //--- check if(info1>0 && info2>0) { successatleastonce=true; //--- search errors for(i=0;i<=k-1;i++) { err=MathMax(err,MathAbs(x1[2*i+1]-x2[i])); err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i])); } } else generrors=generrors || info1!=-5; } } //--- search errors generrors=(generrors || err>errtol) || !successatleastonce; } //--- end waserrors=(intblerrors || vstblerrors) || generrors; //--- check if(!silent) { Print("TESTING GAUSS-KRONROD QUADRATURES"); Print("FINAL RESULT: "); //--- check if(waserrors) Print("FAILED"); else Print("OK"); Print("* PRE-CALCULATED TABLE: "); //--- check if(intblerrors) Print("FAILED"); else Print("OK"); Print("* CALCULATED AGAINST THE TABLE: "); //--- check if(vstblerrors) Print("FAILED"); else Print("OK"); Print("* GENERAL PROPERTIES: "); //--- check if(generrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Maps: | //| 0=> -0.9 | //| 1=> -0.5 | //| 2=> -0.1 | //| 3=> 0.0 | //| 4=> +0.1 | //| 5=> +0.5 | //| 6=> +0.9 | //| 7=> +1.0 | //| 8=> +1.5 | //| 9=> +2.0 | //+------------------------------------------------------------------+ static double CTestGKQUnit::MapKind(const int k) { //--- create variables double result=0; //--- check if(k==0) result=-0.9; //--- check if(k==1) result=-0.5; //--- check if(k==2) result=-0.1; //--- check if(k==3) result=0.0; //--- check if(k==4) result=0.1; //--- check if(k==5) result=0.5; //--- check if(k==6) result=0.9; //--- check if(k==7) result=1.0; //--- check if(k==8) result=1.5; //--- check if(k==9) result=2.0; //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CAutoGK | //+------------------------------------------------------------------+ class CTestAutoGKUnit { public: //--- constructor, destructor CTestAutoGKUnit(void); ~CTestAutoGKUnit(void); //--- public method static bool TestAutoGK(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestAutoGKUnit::CTestAutoGKUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestAutoGKUnit::~CTestAutoGKUnit(void) { } //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ static bool CTestAutoGKUnit::TestAutoGK(const bool silent) { //--- create variables double a=0; double b=0; double v=0; double exact=0; double eabs=0; double alpha=0; int pkind=0; double errtol=0; bool simpleerrors; bool sngenderrors; bool waserrors; //--- objects of classes CAutoGKState state; CAutoGKReport rep; //--- initialization simpleerrors=false; sngenderrors=false; waserrors=false; errtol=10000*CMath::m_machineepsilon; //--- Simple test: integral(exp(x),+-1,+-2),no maximum width requirements a=(2*CMath::RandomInteger(2)-1)*1.0; b=(2*CMath::RandomInteger(2)-1)*2.0; //--- function call CAutoGK::AutoGKSmooth(a,b,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathExp(state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=MathExp(b)-MathExp(a); eabs=MathAbs(MathExp(b)-MathExp(a)); //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs; //--- Simple test: integral(exp(x),+-1,+-2),XWidth=0.1 a=(2*CMath::RandomInteger(2)-1)*1.0; b=(2*CMath::RandomInteger(2)-1)*2.0; //--- function call CAutoGK::AutoGKSmoothW(a,b,0.1,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathExp(state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=MathExp(b)-MathExp(a); eabs=MathAbs(MathExp(b)-MathExp(a)); //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs; //--- Simple test: integral(cos(100*x),0,2*pi),no maximum width requirements a=0; b=2*M_PI; //--- function call CAutoGK::AutoGKSmooth(a,b,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathCos(100*state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=0; eabs=4; //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs; //--- Simple test: integral(cos(100*x),0,2*pi),XWidth=0.3 a=0; b=2*M_PI; //--- function call CAutoGK::AutoGKSmoothW(a,b,0.3,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathCos(100*state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=0; eabs=4; //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs; //--- singular problem on [a,b]=[0.1,0.5] //--- f2(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1 for(pkind=0;pkind<=6;pkind++) { a=0.1; b=0.5; //--- check if(pkind==0) alpha=-0.9; //--- check if(pkind==1) alpha=-0.5; //--- check if(pkind==2) alpha=-0.1; //--- check if(pkind==3) alpha=0.0; //--- check if(pkind==4) alpha=0.1; //--- check if(pkind==5) alpha=0.5; //--- check if(pkind==6) alpha=0.9; //--- f1(x)=(1+x)*(x-a)^alpha,-1 < alpha < 1 //--- 1. use singular integrator for [a,b] //--- 2. use singular integrator for [b,a] exact=MathPow(b-a,alpha+2)/(alpha+2)+(1+a)*MathPow(b-a,alpha+1)/(alpha+1); eabs=MathAbs(exact); //--- function call CAutoGK::AutoGKSingular(a,b,alpha,0.0,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_xminusa<0.01) state.m_f=MathPow(state.m_xminusa,alpha)*(1+state.m_x); else state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors || MathAbs(v-exact)>errtol*eabs; //--- function call CAutoGK::AutoGKSingular(b,a,0.0,alpha,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_bminusx>-0.01) state.m_f=MathPow(-state.m_bminusx,alpha)*(1+state.m_x); else state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors || MathAbs(-v-exact)>errtol*eabs; //--- f1(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1 //--- 1. use singular integrator for [a,b] //--- 2. use singular integrator for [b,a] exact=(1+b)*MathPow(b-a,alpha+1)/(alpha+1)-MathPow(b-a,alpha+2)/(alpha+2); eabs=MathAbs(exact); //--- function call CAutoGK::AutoGKSingular(a,b,0.0,alpha,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_bminusx<0.01) state.m_f=MathPow(state.m_bminusx,alpha)*(1+state.m_x); else state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors || MathAbs(v-exact)>errtol*eabs; //--- function call CAutoGK::AutoGKSingular(b,a,alpha,0.0,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_xminusa>-0.01) state.m_f=MathPow(-state.m_xminusa,alpha)*(1+state.m_x); else state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors || MathAbs(-v-exact)>errtol*eabs; } //--- end waserrors=simpleerrors || sngenderrors; //--- check if(!silent) { Print("TESTING AUTOGK"); Print("INTEGRATION WITH GIVEN ACCURACY: "); //--- check if(simpleerrors || sngenderrors) Print("FAILED"); else Print("OK"); Print("* SIMPLE PROBLEMS: "); //--- check if(simpleerrors) Print("FAILED"); else Print("OK"); Print("* SINGULAR PROBLEMS (ENDS OF INTERVAL): "); //--- check if(sngenderrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CIDWInt | //+------------------------------------------------------------------+ class CTestIDWIntUnit { private: //--- private methods static void Unset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static void TestXY(CMatrixDouble &xy,const int n,const int nx,const int d,const int nq,const int nw,bool &idwerrors); static void TestRXY(CMatrixDouble &xy,const int n,const int nx,const double r,bool &idwerrors); static void TestDegree(const int n,const int nx,const int d,const int dtask,bool &idwerrors); static void TestNoisy(bool &idwerrors); public: //--- constructor, destructor CTestIDWIntUnit(void); ~CTestIDWIntUnit(void); //--- public method static bool TestIDWInt(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestIDWIntUnit::CTestIDWIntUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestIDWIntUnit::~CTestIDWIntUnit(void) { } //+------------------------------------------------------------------+ //| Testing IDW interpolation | //+------------------------------------------------------------------+ static bool CTestIDWIntUnit::TestIDWInt(const bool silent) { //--- create variables int i=0; int j=0; double vx=0; double vy=0; double vz=0; int d=0; int dtask=0; int nx=0; int nq=0; int nw=0; int smalln=0; int largen=0; bool waserrors; bool idwerrors; //--- create matrix CMatrixDouble xy; //--- initialization idwerrors=false; smalln=256; largen=1024; nq=10; nw=18; //--- Simple test: //--- * F=x^3 + sin(pi*y)*z^2 - (x+y)^2 //--- * space is either R1=[-1,+1] (other dimensions are //--- fixed at 0),R1^2 or R1^3. //* D=-1,0,1,2 for(nx=1;nx<=2;nx++) { //--- allocation xy.Resize(largen,nx+1); for(i=0;i<=largen-1;i++) { for(j=0;j<=nx-1;j++) xy[i].Set(j,2*CMath::RandomReal()-1); //--- check if(nx>=1) vx=xy[i][0]; else vx=0; //--- check if(nx>=2) vy=xy[i][1]; else vy=0; //--- check if(nx>=3) vz=xy[i][2]; else vz=0; //--- calculation xy[i].Set(nx,vx*vx*vx+MathSin(M_PI*vy)*CMath::Sqr(vz)-CMath::Sqr(vx+vy)); } for(d=-1;d<=2;d++) TestXY(xy,largen,nx,d,nq,nw,idwerrors); } //--- Another simple test: //--- * five points in 2D - (0,0),(0,1),(1,0),(-1,0) (0,-1) //--- * F is random //--- * D=-1,0,1,2 nx=2; xy.Resize(5,nx+1); //--- change values xy[0].Set(0,0); xy[0].Set(1,0); xy[0].Set(2,2*CMath::RandomReal()-1); xy[1].Set(0,1); xy[1].Set(1,0); xy[1].Set(2,2*CMath::RandomReal()-1); xy[2].Set(0,0); xy[2].Set(1,1); xy[2].Set(2,2*CMath::RandomReal()-1); xy[3].Set(0,-1); xy[3].Set(1,0); xy[3].Set(2,2*CMath::RandomReal()-1); xy[4].Set(0,0); xy[4].Set(1,-1); xy[4].Set(2,2*CMath::RandomReal()-1); //--- calculation for(d=-1;d<=2;d++) TestXY(xy,5,nx,d,nq,nw,idwerrors); //--- Degree test. //--- F is either: //--- * constant (DTask=0) //--- * linear (DTask=1) //--- * quadratic (DTask=2) //--- Nodal functions are either //--- * constant (D=0) //--- * linear (D=1) //--- * quadratic (D=2) //--- When DTask<=D,we can interpolate without errors. //--- When DTask>D,we MUST have errors. for(nx=1;nx<=3;nx++) { for(d=0;d<=2;d++) for(dtask=0;dtask<=2;dtask++) { TestDegree(smalln,nx,d,dtask,idwerrors); } } //--- Noisy test TestNoisy(idwerrors); //--- report waserrors=idwerrors; //--- check if(!silent) { Print("TESTING INVERSE DISTANCE WEIGHTING"); Print("* IDW: "); //--- check if(!idwerrors) Print("OK"); else Print("FAILED"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ static void CTestIDWIntUnit::Unset2D(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a[0].Set(0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ static void CTestIDWIntUnit::Unset1D(double &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Testing IDW: | //| * generate model using N/NX/D/NQ/NW | //| * test basic properties | //+------------------------------------------------------------------+ static void CTestIDWIntUnit::TestXY(CMatrixDouble &xy,const int n,const int nx, const int d,const int nq,const int nw, bool &idwerrors) { //--- create variables double threshold=0; double lipschitzstep=0; int i=0; int i1=0; int i2=0; double v=0; double v1=0; double v2=0; double t=0; double l1=0; double l2=0; int i_=0; //--- create array double x[]; //--- object of class CIDWInterpolant z1; //--- initialization threshold=1000*CMath::m_machineepsilon; lipschitzstep=0.001; //--- allocation ArrayResize(x,nx); //--- build CIDWInt::IDWBuildModifiedShepard(xy,n,nx,d,nq,nw,z1); //--- first,test interpolation properties at nodes for(i=0;i<=n-1;i++) { for(i_=0;i_<=nx-1;i_++) x[i_]=xy[i][i_]; //--- search errors idwerrors=idwerrors || CIDWInt::IDWCalc(z1,x)!=xy[i][nx]; } //--- test Lipschitz continuity i1=CMath::RandomInteger(n); do { i2=CMath::RandomInteger(n); } while(i2==i1); //--- change values l1=0; t=0; //--- cycle while(t<1.0) { //--- calculation v=1-t; for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v1=CIDWInt::IDWCalc(z1,x); v=1-(t+lipschitzstep); //--- calculation for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t+lipschitzstep; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v2=CIDWInt::IDWCalc(z1,x); l1=MathMax(l1,MathAbs(v2-v1)/lipschitzstep); t=t+lipschitzstep; } //--- change values l2=0; t=0; //--- cycle while(t<1.0) { //--- calculation v=1-t; for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v1=CIDWInt::IDWCalc(z1,x); v=1-(t+lipschitzstep/3); //--- calculation for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t+lipschitzstep/3; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v2=CIDWInt::IDWCalc(z1,x); l2=MathMax(l2,MathAbs(v2-v1)/(lipschitzstep/3)); t=t+lipschitzstep/3; } //--- search errors idwerrors=idwerrors || l2>2.0*l1; } //+------------------------------------------------------------------+ //| Testing IDW: | //| * generate model using R-based model | //| * test basic properties | //+------------------------------------------------------------------+ static void CTestIDWIntUnit::TestRXY(CMatrixDouble &xy,const int n,const int nx, const double r,bool &idwerrors) { //--- create variables double threshold=0; double lipschitzstep=0; int i=0; int i1=0; int i2=0; double v=0; double v1=0; double v2=0; double t=0; double l1=0; double l2=0; int i_=0; //--- create array double x[]; //--- object of class CIDWInterpolant z1; //--- initialization threshold=1000*CMath::m_machineepsilon; lipschitzstep=0.001; //--- allocation ArrayResize(x,nx); //--- build CIDWInt::IDWBuildModifiedShepardR(xy,n,nx,r,z1); //--- first,test interpolation properties at nodes for(i=0;i<=n-1;i++) { for(i_=0;i_<=nx-1;i_++) x[i_]=xy[i][i_]; //--- search errors idwerrors=idwerrors || CIDWInt::IDWCalc(z1,x)!=xy[i][nx]; } //--- test Lipschitz continuity i1=CMath::RandomInteger(n); do { i2=CMath::RandomInteger(n); } while(i2==i1); //--- change values l1=0; t=0; //--- cycle while(t<1.0) { //--- calculation v=1-t; for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v1=CIDWInt::IDWCalc(z1,x); v=1-(t+lipschitzstep); //--- calculation for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t+lipschitzstep; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v2=CIDWInt::IDWCalc(z1,x); l1=MathMax(l1,MathAbs(v2-v1)/lipschitzstep); t=t+lipschitzstep; } //--- change values l2=0; t=0; //--- cycle while(t<1.0) { //--- calculation v=1-t; for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v1=CIDWInt::IDWCalc(z1,x); v=1-(t+lipschitzstep/3); //--- calculation for(i_=0;i_<=nx-1;i_++) x[i_]=v*xy[i1][i_]; v=t+lipschitzstep/3; for(i_=0;i_<=nx-1;i_++) x[i_]=x[i_]+v*xy[i2][i_]; v2=CIDWInt::IDWCalc(z1,x); l2=MathMax(l2,MathAbs(v2-v1)/(lipschitzstep/3)); t=t+lipschitzstep/3; } //--- search errors idwerrors=idwerrors || l2>2.0*l1; } //+------------------------------------------------------------------+ //| Testing degree properties | //| F is either: | //| * constant (DTask=0) | //| * linear (DTask=1) | //| * quadratic (DTask=2) | //| Nodal functions are either | //| * constant (D=0) | //| * linear (D=1) | //| * quadratic (D=2) | //| When DTask<=D,we can interpolate without errors. | //| When DTask>D,we MUST have errors. | //+------------------------------------------------------------------+ static void CTestIDWIntUnit::TestDegree(const int n,const int nx,const int d, const int dtask,bool &idwerrors) { //--- create variables double threshold=0; int nq=0; int nw=0; int i=0; int j=0; double v=0; double c0=0; double v1=0; double v2=0; int i_=0; //--- create arrays double c1[]; double x[]; //--- create matrix CMatrixDouble c2; CMatrixDouble xy; //--- object of class CIDWInterpolant z1; //--- initialization threshold=1.0E6*CMath::m_machineepsilon; nq=2*(nx*nx+nx+1); nw=10; //--- check if(!CAp::Assert(nq<=n,"TestDegree: internal error")) return; //--- prepare model c0=2*CMath::RandomReal()-1; //--- allocation ArrayResize(c1,nx); for(i=0;i<=nx-1;i++) c1[i]=2*CMath::RandomReal()-1; //--- allocation c2.Resize(nx,nx); for(i=0;i<=nx-1;i++) { for(j=i+1;j<=nx-1;j++) { c2[i].Set(j,2*CMath::RandomReal()-1); c2[j].Set(i,c2[i][j]); } //--- cycle do { c2[i].Set(i,2*CMath::RandomReal()-1); } while(MathAbs(c2[i][i])<=0.3); } //--- prepare points xy.Resize(n,nx+1); for(i=0;i<=n-1;i++) { for(j=0;j<=nx-1;j++) xy[i].Set(j,4*CMath::RandomReal()-2); xy[i].Set(nx,c0); //--- check if(dtask>=1) { //--- change value v=0.0; for(i_=0;i_<=nx-1;i_++) v+=c1[i_]*xy[i][i_]; xy[i].Set(nx,xy[i][nx]+v); } //--- check if(dtask==2) { for(j=0;j<=nx-1;j++) { //--- change value v=0.0; for(i_=0;i_<=nx-1;i_++) v+=c2[j][i_]*xy[i][i_]; xy[i].Set(nx,xy[i][nx]+xy[i][j]*v); } } } //--- build interpolant,calculate value at random point CIDWInt::IDWBuildModifiedShepard(xy,n,nx,d,nq,nw,z1); ArrayResize(x,nx); for(i=0;i<=nx-1;i++) x[i]=4*CMath::RandomReal()-2; v1=CIDWInt::IDWCalc(z1,x); //--- calculate model value at the same point v2=c0; //--- check if(dtask>=1) { //--- change value v=0.0; for(i_=0;i_<=nx-1;i_++) v+=c1[i_]*x[i_]; v2=v2+v; } //--- check if(dtask==2) { for(j=0;j<=nx-1;j++) { //--- change value v=0.0; for(i_=0;i_<=nx-1;i_++) v+=c2[j][i_]*x[i_]; v2=v2+x[j]*v; } } //--- Compare if(dtask<=d) idwerrors=idwerrors || MathAbs(v2-v1)>threshold; else idwerrors=idwerrors || MathAbs(v2-v1)rms1; } } } //+------------------------------------------------------------------+ //| Testing class CRatInt | //+------------------------------------------------------------------+ class CTestRatIntUnit { private: //--- private methods static void PolDiff2(double &x[],double &cf[],int n,double t,double &p,double &dp,double &d2p); static void BRCunSet(CBarycentricInterpolant &b); static bool Is1DSolution(const int n,double &y[],double &w[],double c); public: //--- constructor, destructor CTestRatIntUnit(void); ~CTestRatIntUnit(void); //--- public method static bool TestRatInt(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestRatIntUnit::CTestRatIntUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestRatIntUnit::~CTestRatIntUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CRatInt | //+------------------------------------------------------------------+ static bool CTestRatIntUnit::TestRatInt(const bool silent) { //--- create variables bool waserrors; bool bcerrors; bool nperrors; double threshold=0; double lipschitztol=0; int maxn=0; int passcount=0; double h=0; double s1=0; double s2=0; int n=0; int n2=0; int i=0; int j=0; int k=0; int d=0; int pass=0; double maxerr=0; double t=0; double a=0; double b=0; double s=0; double v0=0; double v1=0; double v2=0; double v3=0; double d0=0; double d1=0; double d2=0; //--- create arrays double x[]; double x2[]; double y[]; double y2[]; double w[]; double w2[]; double xc[]; double yc[]; int dc[]; //--- objects of classes CBarycentricInterpolant b1; CBarycentricInterpolant b2; //--- initialization nperrors=false; bcerrors=false; waserrors=false; //--- PassCount number of repeated passes //--- Threshold error tolerance //--- LipschitzTol Lipschitz constant increase allowed //--- when calculating constant on a twice denser grid passcount=5; maxn=15; threshold=1000000*CMath::m_machineepsilon; lipschitztol=1.3; //--- Basic barycentric functions for(n=1;n<=10;n++) { //--- randomized tests for(pass=1;pass<=passcount;pass++) { //--- generate weights from polynomial interpolation v0=1+0.4*CMath::RandomReal()-0.2; v1=2*CMath::RandomReal()-1; v2=2*CMath::RandomReal()-1; v3=2*CMath::RandomReal()-1; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(n==1) x[i]=0; else x[i]=v0*MathCos(i*M_PI/(n-1)); y[i]=MathSin(v1*x[i])+MathCos(v2*x[i])+MathExp(v3*x[i]); } //--- calculation for(j=0;j<=n-1;j++) { w[j]=1; for(k=0;k<=n-1;k++) { //--- check if(k!=j) w[j]=w[j]/(x[j]-x[k]); } } //--- function call CRatInt::BarycentricBuildXYW(x,y,w,n,b1); //--- unpack,then pack again and compare BRCunSet(b2); CRatInt::BarycentricUnpack(b1,n2,x2,y2,w2); //--- search errors bcerrors=bcerrors || n2!=n; //--- function call CRatInt::BarycentricBuildXYW(x2,y2,w2,n2,b2); t=2*CMath::RandomReal()-1; //--- search errors bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold; //--- copy,compare BRCunSet(b2); CRatInt::BarycentricCopy(b1,b2); t=2*CMath::RandomReal()-1; //--- search errors bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold; //--- test interpolation properties for(i=0;i<=n-1;i++) { //--- test interpolation at nodes bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold*MathAbs(y[i]); //--- compare with polynomial interpolation t=2*CMath::RandomReal()-1; PolDiff2(x,y,n,t,v0,v1,v2); //--- search errors bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-v0)>threshold*MathMax(MathAbs(v0),1); //--- test continuity between nodes //--- calculate Lipschitz constant on two grids - //--- dense and even more dense. If Lipschitz constant //--- on a denser grid is significantly increased, //--- continuity test is failed t=3.0; k=100; s1=0; for(j=0;j<=k-1;j++) { v1=x[i]+(t-x[i])*j/k; v2=x[i]+(t-x[i])*(j+1)/k; s1=MathMax(s1,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1)); } //--- change values k=2*k; s2=0; for(j=0;j<=k-1;j++) { v1=x[i]+(t-x[i])*j/k; v2=x[i]+(t-x[i])*(j+1)/k; s2=MathMax(s2,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1)); } //--- search errors bcerrors=bcerrors || (s2>lipschitztol*s1 && s1>threshold*k); } //--- test differentiation properties for(i=0;i<=n-1;i++) { t=2*CMath::RandomReal()-1; PolDiff2(x,y,n,t,v0,v1,v2); d0=0; d1=0; d2=0; //--- function call CRatInt::BarycentricDiff1(b1,t,d0,d1); //--- search errors bcerrors=bcerrors || MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1); bcerrors=bcerrors || MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1); //--- change values d0=0; d1=0; d2=0; //--- function call CRatInt::BarycentricDiff2(b1,t,d0,d1,d2); //--- search errors bcerrors=bcerrors || MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1); bcerrors=bcerrors || MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1); bcerrors=bcerrors || MathAbs(v2-d2)>MathSqrt(threshold)*MathMax(MathAbs(v2),1); } //--- test linear translation t=2*CMath::RandomReal()-1; a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; //--- function calls BRCunSet(b2); CRatInt::BarycentricCopy(b1,b2); CRatInt::BarycentricLinTransX(b2,a,b); //--- search errors bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold; //--- change values a=0; b=2*CMath::RandomReal()-1; //--- function calls BRCunSet(b2); CRatInt::BarycentricCopy(b1,b2); CRatInt::BarycentricLinTransX(b2,a,b); //--- search errors bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold; //--- change values a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; //--- function calls BRCunSet(b2); CRatInt::BarycentricCopy(b1,b2); CRatInt::BarycentricLinTransY(b2,a,b); //--- search errors bcerrors=bcerrors || MathAbs(a*CRatInt::BarycentricCalc(b1,t)+b-CRatInt::BarycentricCalc(b2,t))>threshold; } } //--- calculation for(pass=0;pass<=3;pass++) { //--- Crash-test: small numbers,large numbers ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); h=1; //--- check if(pass%2==0) h=100*CMath::m_minrealnumber; //--- check if(pass%2==1) h=0.01*CMath::m_maxrealnumber; //--- change values x[0]=0*h; x[1]=1*h; x[2]=2*h; x[3]=3*h; y[0]=0*h; y[1]=1*h; y[2]=2*h; y[3]=3*h; w[0]=-(1/(x[1]-x[0])); w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1])); w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2]))); w[3]=1/(x[3]-x[2]); //--- check if(pass/2==0) v0=0; //--- check if(pass/2==1) v0=0.6*h; //--- function calls CRatInt::BarycentricBuildXYW(x,y,w,4,b1); t=CRatInt::BarycentricCalc(b1,v0); //--- change values d0=0; d1=0; d2=0; //--- function call CRatInt::BarycentricDiff1(b1,v0,d0,d1); //--- search errors bcerrors=bcerrors || MathAbs(t-v0)>threshold*v0; bcerrors=bcerrors || MathAbs(d0-v0)>threshold*v0; bcerrors=bcerrors || MathAbs(d1-1)>1000*threshold; } //--- crash test: large abscissas,small argument //--- test for errors in D0 is not very strict //--- because renormalization used in Diff1() //--- destroys part of precision. ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); //--- change values h=0.01*CMath::m_maxrealnumber; x[0]=0*h; x[1]=1*h; x[2]=2*h; x[3]=3*h; y[0]=0*h; y[1]=1*h; y[2]=2*h; y[3]=3*h; w[0]=-(1/(x[1]-x[0])); w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1])); w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2]))); w[3]=1/(x[3]-x[2]); v0=100*CMath::m_minrealnumber; //--- function call CRatInt::BarycentricBuildXYW(x,y,w,4,b1); t=CRatInt::BarycentricCalc(b1,v0); //--- change values d0=0; d1=0; d2=0; //--- function call CRatInt::BarycentricDiff1(b1,v0,d0,d1); //--- search errors bcerrors=bcerrors || MathAbs(t)>v0*(1+threshold); bcerrors=bcerrors || MathAbs(d0)>v0*(1+threshold); bcerrors=bcerrors || MathAbs(d1-1)>1000*threshold; //--- crash test: test safe barycentric formula ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); //--- change values h=2*CMath::m_minrealnumber; x[0]=0*h; x[1]=1*h; x[2]=2*h; x[3]=3*h; y[0]=0*h; y[1]=1*h; y[2]=2*h; y[3]=3*h; w[0]=-(1/(x[1]-x[0])); w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1])); w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2]))); w[3]=1/(x[3]-x[2]); v0=CMath::m_minrealnumber; //--- function calls CRatInt::BarycentricBuildXYW(x,y,w,4,b1); t=CRatInt::BarycentricCalc(b1,v0); //--- search errors bcerrors=bcerrors || MathAbs(t-v0)/v0>threshold; //--- Testing "No Poles" interpolation maxerr=0; for(pass=1;pass<=passcount-1;pass++) { //--- allocation ArrayResize(x,1); ArrayResize(y,1); x[0]=2*CMath::RandomReal()-1; y[0]=2*CMath::RandomReal()-1; //--- function call CRatInt::BarycentricBuildFloaterHormann(x,y,1,1,b1); //--- search errors maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,2*CMath::RandomReal()-1)-y[0])); } //--- calculation for(n=2;n<=10;n++) { //--- compare interpolant built by subroutine //--- with interpolant built by hands ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(w2,n); //--- D=1,non-equidistant nodes for(pass=1;pass<=passcount;pass++) { //--- Initialize X,Y,W a=-1-1*CMath::RandomReal(); b=1+1*CMath::RandomReal(); for(i=0;i<=n-1;i++) x[i]=MathArctan((b-a)*i/(n-1)+a); for(i=0;i<=n-1;i++) y[i]=2*CMath::RandomReal()-1; w[0]=-(1/(x[1]-x[0])); s=1; //--- calculation for(i=1;i<=n-2;i++) { w[i]=s*(1/(x[i]-x[i-1])+1/(x[i+1]-x[i])); s=-s; } w[n-1]=s/(x[n-1]-x[n-2]); //--- calculation for(i=0;i<=n-1;i++) { k=CMath::RandomInteger(n); //--- check if(k!=i) { t=x[i]; x[i]=x[k]; x[k]=t; t=y[i]; y[i]=y[k]; y[k]=t; t=w[i]; w[i]=w[k]; w[k]=t; } } //--- Build and test CRatInt::BarycentricBuildFloaterHormann(x,y,n,1,b1); CRatInt::BarycentricBuildXYW(x,y,w,n,b2); //--- search errors for(i=1;i<=2*n;i++) { t=a+(b-a)*CMath::RandomReal(); maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))); } } //--- D=0,1,2. Equidistant nodes. for(d=0;d<=2;d++) { for(pass=1;pass<=passcount;pass++) { //--- Skip incorrect (N,D) pairs if(n<2*d) continue; //--- Initialize X,Y,W a=-1-1*CMath::RandomReal(); b=1+1*CMath::RandomReal(); for(i=0;i<=n-1;i++) x[i]=(b-a)*i/(n-1)+a; for(i=0;i<=n-1;i++) y[i]=2*CMath::RandomReal()-1; s=1; //--- check if(d==0) { for(i=0;i<=n-1;i++) { w[i]=s; s=-s; } } //--- check if(d==1) { w[0]=-s; for(i=1;i<=n-2;i++) { w[i]=2*s; s=-s; } w[n-1]=s; } //--- check if(d==2) { //--- calculation w[0]=s; w[1]=-(3*s); for(i=2;i<=n-3;i++) { w[i]=4*s; s=-s; } w[n-2]=3*s; w[n-1]=-s; } //--- Mix for(i=0;i<=n-1;i++) { k=CMath::RandomInteger(n); //--- check if(k!=i) { t=x[i]; x[i]=x[k]; x[k]=t; t=y[i]; y[i]=y[k]; y[k]=t; t=w[i]; w[i]=w[k]; w[k]=t; } } //--- Build and test CRatInt::BarycentricBuildFloaterHormann(x,y,n,d,b1); CRatInt::BarycentricBuildXYW(x,y,w,n,b2); //--- search errors for(i=1;i<=2*n;i++) { t=a+(b-a)*CMath::RandomReal(); maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))); } } } } //--- check if(maxerr>threshold) nperrors=true; //--- report waserrors=bcerrors || nperrors; //--- check if(!silent) { Print("TESTING RATIONAL INTERPOLATION"); Print("BASIC BARYCENTRIC FUNCTIONS: "); //--- check if(bcerrors) Print("FAILED"); else Print("OK"); Print("FLOATER-HORMANN: "); //--- check if(nperrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestRatIntUnit::PolDiff2(double &x[],double &cf[],int n, double t,double &p,double &dp, double &d2p) { //--- create variables int m=0; int i=0; //--- create arrays double df[]; double d2f[]; double f[]; //--- copy ArrayCopy(f,cf); //--- change values p=0; dp=0; d2p=0; n=n-1; //--- allocation ArrayResize(df,n+1); ArrayResize(d2f,n+1); for(i=0;i<=n;i++) { d2f[i]=0; df[i]=0; } //--- calculation for(m=1;m<=n;m++) { for(i=0;i<=n-m;i++) { d2f[i]=((t-x[i+m])*d2f[i]+(x[i]-t)*d2f[i+1]+2*df[i]-2*df[i+1])/(x[i]-x[i+m]); df[i]=((t-x[i+m])*df[i]+f[i]+(x[i]-t)*df[i+1]-f[i+1])/(x[i]-x[i+m]); f[i]=((t-x[i+m])*f[i]+(x[i]-t)*f[i+1])/(x[i]-x[i+m]); } } //--- change values p=f[0]; dp=df[0]; d2p=d2f[0]; } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestRatIntUnit::BRCunSet(CBarycentricInterpolant &b) { //--- create arrays double x[]; double y[]; double w[]; //--- allocation ArrayResize(x,1); ArrayResize(y,1); ArrayResize(w,1); //--- change values x[0]=0; y[0]=0; w[0]=1; //--- function call CRatInt::BarycentricBuildXYW(x,y,w,1,b); } //+------------------------------------------------------------------+ //| Tests whether constant C is solution of 1D LLS problem | //+------------------------------------------------------------------+ static bool CTestRatIntUnit::Is1DSolution(const int n,double &y[], double &w[],double c) { //--- create variables bool result; int i=0; double s1=0; double s2=0; double s3=0; double delta=0; //--- initialization delta=0.001; //--- Test result s1=0; for(i=0;i<=n-1;i++) s1=s1+CMath::Sqr(w[i]*(c-y[i])); //--- calculation s2=0; s3=0; for(i=0;i<=n-1;i++) { s2=s2+CMath::Sqr(w[i]*(c+delta-y[i])); s3=s3+CMath::Sqr(w[i]*(c-delta-y[i])); } result=s2>=s1 && s3>=s1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CPolInt | //+------------------------------------------------------------------+ class CTestPolIntUnit { private: //--- private methods static double InternalPolInt(double &x[],double &cf[],int n,const double t); static void BRCunSet(CBarycentricInterpolant &b); public: //--- constructor, destructor CTestPolIntUnit(void); ~CTestPolIntUnit(void); //--- public method static bool TestPolInt(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestPolIntUnit::CTestPolIntUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestPolIntUnit::~CTestPolIntUnit(void) { } //+------------------------------------------------------------------+ //| Unit test | //+------------------------------------------------------------------+ static bool CTestPolIntUnit::TestPolInt(const bool silent) { //--- create variables bool waserrors; bool interrors; double threshold=0; double a=0; double b=0; double t=0; int i=0; int k=0; double v=0; double v0=0; double v1=0; double v2=0; double v3=0; double v4=0; double pscale=0; double poffset=0; int n=0; int maxn=0; int pass=0; int passcount=0; //--- create arrays double x[]; double y[]; double w[]; double c[]; double x2[]; double y2[]; double w2[]; double xfull[]; double yfull[]; double xc[]; double yc[]; int dc[]; //--- objects of classes CBarycentricInterpolant p; CBarycentricInterpolant p1; CBarycentricInterpolant p2; //--- initialization waserrors=false; interrors=false; maxn=5; passcount=20; threshold=1.0E8*CMath::m_machineepsilon; //--- Test equidistant interpolation for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- prepare task: //--- * equidistant points //--- * random Y //--- * T in [A,B] or near (within 10% of its width) do { a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; } while(MathAbs(a-b)<=0.2); //--- change value t=a+(1.2*CMath::RandomReal()-0.1)*(b-a); //--- function call CApServ::TaskGenInt1DEquidist(a,b,n,x,y); //--- test "fast" equidistant interpolation (no barycentric model) interrors=interrors || MathAbs(CPolInt::PolynomialCalcEqDist(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "slow" equidistant interpolation (create barycentric model) BRCunSet(p); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "fast" interpolation (create "fast" barycentric model) BRCunSet(p); CPolInt::PolynomialBuildEqDist(a,b,y,n,p); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; } } //--- Test Chebyshev-1 interpolation for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- prepare task: //--- * equidistant points //--- * random Y //--- * T in [A,B] or near (within 10% of its width) do { a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; } while(MathAbs(a-b)<=0.2); //--- change value t=a+(1.2*CMath::RandomReal()-0.1)*(b-a); //--- function call CApServ::TaskGenInt1DCheb1(a,b,n,x,y); //--- test "fast" interpolation (no barycentric model) interrors=interrors || MathAbs(CPolInt::PolynomialCalcCheb1(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "slow" interpolation (create barycentric model) BRCunSet(p); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "fast" interpolation (create "fast" barycentric model) BRCunSet(p); CPolInt::PolynomialBuildCheb1(a,b,y,n,p); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; } } //--- Test Chebyshev-2 interpolation for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- prepare task: //--- * equidistant points //--- * random Y //--- * T in [A,B] or near (within 10% of its width) do { a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; } while(MathAbs(a-b)<=0.2); //--- change value t=a+(1.2*CMath::RandomReal()-0.1)*(b-a); //--- function call CApServ::TaskGenInt1DCheb2(a,b,n,x,y); //--- test "fast" interpolation (no barycentric model) interrors=interrors || MathAbs(CPolInt::PolynomialCalcCheb2(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "slow" interpolation (create barycentric model) BRCunSet(p); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "fast" interpolation (create "fast" barycentric model) BRCunSet(p); CPolInt::PolynomialBuildCheb2(a,b,y,n,p); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; } } //--- Testing conversion Barycentric<->Chebyshev for(pass=1;pass<=passcount;pass++) { for(k=1;k<=3;k++) { //--- Allocate ArrayResize(x,k); ArrayResize(y,k); //--- Generate problem a=2*CMath::RandomReal()-1; b=a+(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1); v0=2*CMath::RandomReal()-1; v1=2*CMath::RandomReal()-1; v2=2*CMath::RandomReal()-1; //--- check if(k==1) { x[0]=0.5*(a+b); y[0]=v0; } //--- check if(k==2) { x[0]=a; y[0]=v0-v1; x[1]=b; y[1]=v0+v1; } //--- check if(k==3) { x[0]=a; y[0]=v0-v1+v2; x[1]=0.5*(a+b); y[1]=v0-v2; x[2]=b; y[2]=v0+v1+v2; } //--- Test forward conversion CPolInt::PolynomialBuild(x,y,k,p); ArrayResize(c,1); CPolInt::PolynomialBar2Cheb(p,a,b,c); //--- search errors interrors=interrors || CAp::Len(c)!=k; //--- check if(k>=1) interrors=interrors || MathAbs(c[0]-v0)>threshold; //--- check if(k>=2) interrors=interrors || MathAbs(c[1]-v1)>threshold; //--- check if(k>=3) interrors=interrors || MathAbs(c[2]-v2)>threshold; //--- Test backward conversion CPolInt::PolynomialCheb2Bar(c,k,a,b,p2); v=a+CMath::RandomReal()*(b-a); //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold; } } //--- Testing conversion Barycentric<->Power for(pass=1;pass<=passcount;pass++) { for(k=1;k<=5;k++) { //--- Allocate ArrayResize(x,k); ArrayResize(y,k); //--- Generate problem poffset=2*CMath::RandomReal()-1; pscale=(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1); v0=2*CMath::RandomReal()-1; v1=2*CMath::RandomReal()-1; v2=2*CMath::RandomReal()-1; v3=2*CMath::RandomReal()-1; v4=2*CMath::RandomReal()-1; //--- check if(k==1) { x[0]=poffset; y[0]=v0; } //--- check if(k==2) { x[0]=poffset-pscale; y[0]=v0-v1; x[1]=poffset+pscale; y[1]=v0+v1; } //--- check if(k==3) { x[0]=poffset-pscale; y[0]=v0-v1+v2; x[1]=poffset; y[1]=v0; x[2]=poffset+pscale; y[2]=v0+v1+v2; } //--- check if(k==4) { x[0]=poffset-pscale; y[0]=v0-v1+v2-v3; x[1]=poffset-0.5*pscale; y[1]=v0-0.5*v1+0.25*v2-0.125*v3; x[2]=poffset+0.5*pscale; y[2]=v0+0.5*v1+0.25*v2+0.125*v3; x[3]=poffset+pscale; y[3]=v0+v1+v2+v3; } //--- check if(k==5) { x[0]=poffset-pscale; y[0]=v0-v1+v2-v3+v4; x[1]=poffset-0.5*pscale; y[1]=v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4; x[2]=poffset; y[2]=v0; x[3]=poffset+0.5*pscale; y[3]=v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4; x[4]=poffset+pscale; y[4]=v0+v1+v2+v3+v4; } //--- Test forward conversion CPolInt::PolynomialBuild(x,y,k,p); ArrayResize(c,1); CPolInt::PolynomialBar2Pow(p,poffset,pscale,c); //--- search errors interrors=interrors || CAp::Len(c)!=k; //--- check if(k>=1) interrors=interrors || MathAbs(c[0]-v0)>threshold; //--- check if(k>=2) interrors=interrors || MathAbs(c[1]-v1)>threshold; //--- check if(k>=3) interrors=interrors || MathAbs(c[2]-v2)>threshold; //--- check if(k>=4) interrors=interrors || MathAbs(c[3]-v3)>threshold; //--- check if(k>=5) interrors=interrors || MathAbs(c[4]-v4)>threshold; //--- Test backward conversion CPolInt::PolynomialPow2Bar(c,k,poffset,pscale,p2); v=poffset+(2*CMath::RandomReal()-1)*pscale; //--- search errors interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold; } } //--- crash-test: ability to solve tasks which will overflow/underflow //--- weights with straightforward implementation for(n=1;n<=20;n++) { a=-(0.1*CMath::m_maxrealnumber); b=0.1*CMath::m_maxrealnumber; CApServ::TaskGenInt1DEquidist(a,b,n,x,y); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors for(i=0;i<=n-1;i++) interrors=interrors || p.m_w[i]==0.0; } //--- report waserrors=interrors; //--- check if(!silent) { Print("TESTING POLYNOMIAL INTERPOLATION"); //--- Normal tests Print("INTERPOLATION TEST: "); //--- check if(interrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static double CTestPolIntUnit::InternalPolInt(double &x[],double &cf[], int n,const double t) { //--- create variables int i=0; int j=0; //--- create array double f[]; //--- copy ArrayCopy(f,cf); //--- calculation n=n-1; for(j=0;j<=n-1;j++) { for(i=j+1;i<=n;i++) f[i]=((t-x[j])*f[i]-(t-x[i])*f[j])/(x[i]-x[j]); } //--- return result return(f[n]); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestPolIntUnit::BRCunSet(CBarycentricInterpolant &b) { //--- create arrays double x[]; double y[]; double w[]; //--- allocation ArrayResize(x,1); ArrayResize(y,1); ArrayResize(w,1); //--- change values x[0]=0; y[0]=0; w[0]=1; //--- function call CRatInt::BarycentricBuildXYW(x,y,w,1,b); } //+------------------------------------------------------------------+ //| Testing class CSpline1D | //+------------------------------------------------------------------+ class CTestSpline1DUnit { private: //--- private methods static void LConst(const double a,const double b,CSpline1DInterpolant &c,const double lstep,double &l0,double &l1,double &l2); static bool TestUnpack(CSpline1DInterpolant &c,double &x[]); static void UnsetSpline1D(CSpline1DInterpolant &c); static void Unset1D(double &x[]); static bool Is1DSolution(const int n,double &y[],double &w[],const double c); public: //--- constructor, destructor CTestSpline1DUnit(void); ~CTestSpline1DUnit(void); //--- public method static bool TestSpline1D(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSpline1DUnit::CTestSpline1DUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSpline1DUnit::~CTestSpline1DUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CSpline1D | //+------------------------------------------------------------------+ static bool CTestSpline1DUnit::TestSpline1D(const bool silent) { //--- create variables bool waserrors; bool crserrors; bool cserrors; bool hserrors; bool aserrors; bool lserrors; bool dserrors; bool uperrors; bool cperrors; bool lterrors; bool ierrors; double nonstrictthreshold=0; double threshold=0; int passcount=0; double lstep=0; double h=0; int maxn=0; int bltype=0; int brtype=0; bool periodiccond; int n=0; int i=0; int k=0; int pass=0; double a=0; double b=0; double bl=0; double br=0; double t=0; double sa=0; double sb=0; int n2=0; double v=0; double l10=0; double l11=0; double l12=0; double l20=0; double l21=0; double l22=0; double p0=0; double p1=0; double p2=0; double s=0; double ds=0; double d2s=0; double s2=0; double ds2=0; double d2s2=0; double vl=0; double vm=0; double vr=0; double err=0; double tension=0; double intab=0; int i_=0; //--- create arrays double x[]; double y[]; double yp[]; double w[]; double w2[]; double y2[]; double d[]; double xc[]; double yc[]; double tmp0[]; double tmp1[]; double tmp2[]; double tmpx[]; int dc[]; //--- objects of classes CSpline1DInterpolant c; CSpline1DInterpolant c2; //--- initialization waserrors=false; passcount=20; lstep=0.005; h=0.00001; maxn=10; threshold=10000*CMath::m_machineepsilon; nonstrictthreshold=0.00001; lserrors=false; cserrors=false; crserrors=false; hserrors=false; aserrors=false; dserrors=false; cperrors=false; uperrors=false; lterrors=false; ierrors=false; //--- General test: linear,cubic,Hermite,Akima for(n=2;n<=maxn;n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(yp,n); ArrayResize(d,n); //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Prepare task: //--- * X contains abscissas from [A,B] //--- * Y contains function values //--- * YP contains periodic function values a=-1-CMath::RandomReal(); b=1+CMath::RandomReal(); bl=2*CMath::RandomReal()-1; br=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { x[i]=0.5*(b+a)+0.5*(b-a)*MathCos(M_PI*(2*i+1)/(2*n)); //--- check if(i==0) x[i]=a; //--- check if(i==n-1) x[i]=b; //--- change values y[i]=MathCos(1.3*M_PI*x[i]+0.4); yp[i]=y[i]; d[i]=-(1.3*M_PI*MathSin(1.3*M_PI*x[i]+0.4)); } yp[n-1]=yp[0]; //--- swap for(i=0;i<=n-1;i++) { k=CMath::RandomInteger(n); //--- check if(k!=i) { t=x[i]; x[i]=x[k]; x[k]=t; t=y[i]; y[i]=y[k]; y[k]=t; t=yp[i]; yp[i]=yp[k]; yp[k]=t; t=d[i]; d[i]=d[k]; d[k]=t; } } //--- Build linear spline //--- Test for general interpolation scheme properties: //--- * values at nodes //--- * continuous function //--- Test for specific properties is implemented below. CSpline1D::Spline1DBuildLinear(x,y,n,c); //--- search errors err=0; for(i=0;i<=n-1;i++) err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))); //--- search errors lserrors=lserrors || err>threshold; LConst(a,b,c,lstep,l10,l11,l12); LConst(a,b,c,lstep/3,l20,l21,l22); //--- search errors lserrors=lserrors || l20/l10>1.2; //--- Build cubic spline. //--- Test for interpolation scheme properties: //--- * values at nodes //--- * boundary conditions //--- * continuous function //--- * continuous first derivative //--- * continuous second derivative //--- * periodicity properties //--- * Spline1DGridDiff(),Spline1DGridDiff2() and Spline1DDiff() //--- calls must return same results for(bltype=-1;bltype<=2;bltype++) { for(brtype=-1;brtype<=2;brtype++) { //--- skip meaningless combination of boundary conditions //--- (one condition is periodic,another is not) periodiccond=bltype==-1 || brtype==-1; //--- check if(periodiccond && bltype!=brtype) continue; //--- build if(periodiccond) CSpline1D::Spline1DBuildCubic(x,yp,n,bltype,bl,brtype,br,c); else CSpline1D::Spline1DBuildCubic(x,y,n,bltype,bl,brtype,br,c); //--- interpolation properties err=0; //--- check if(periodiccond) { //--- * check values at nodes;spline is periodic so //--- we add random number of periods to nodes //--- * we also test for periodicity of derivatives for(i=0;i<=n-1;i++) { v=x[i]; vm=v+(b-a)*(CMath::RandomInteger(5)-2); t=yp[i]-CSpline1D::Spline1DCalc(c,vm); //--- search errors err=MathMax(err,MathAbs(t)); //--- function calls CSpline1D::Spline1DDiff(c,v,s,ds,d2s); CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2); //--- search errors err=MathMax(err,MathAbs(s-s2)); err=MathMax(err,MathAbs(ds-ds2)); err=MathMax(err,MathAbs(d2s-d2s2)); } //--- periodicity between nodes v=a+(b-a)*CMath::RandomReal(); vm=v+(b-a)*(CMath::RandomInteger(5)-2); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm))); //--- function calls CSpline1D::Spline1DDiff(c,v,s,ds,d2s); CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2); //--- search errors err=MathMax(err,MathAbs(s-s2)); err=MathMax(err,MathAbs(ds-ds2)); err=MathMax(err,MathAbs(d2s-d2s2)); } else { //--- * check values at nodes for(i=0;i<=n-1;i++) err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))); } //--- search errors cserrors=cserrors || err>threshold; //--- check boundary conditions err=0; //--- check if(bltype==0) { //--- function calls CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s); CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2); t=(d2s2-d2s)/(2*h); //--- search errors err=MathMax(err,MathAbs(t)); } //--- check if(bltype==1) { t=(CSpline1D::Spline1DCalc(c,a+h)-CSpline1D::Spline1DCalc(c,a-h))/(2*h); //--- search errors err=MathMax(err,MathAbs(bl-t)); } //--- check if(bltype==2) { t=(CSpline1D::Spline1DCalc(c,a+h)-2*CSpline1D::Spline1DCalc(c,a)+CSpline1D::Spline1DCalc(c,a-h))/CMath::Sqr(h); //--- search errors err=MathMax(err,MathAbs(bl-t)); } //--- check if(brtype==0) { CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s); CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2); t=(d2s2-d2s)/(2*h); //--- search errors err=MathMax(err,MathAbs(t)); } //--- check if(brtype==1) { t=(CSpline1D::Spline1DCalc(c,b+h)-CSpline1D::Spline1DCalc(c,b-h))/(2*h); //--- search errors err=MathMax(err,MathAbs(br-t)); } //--- check if(brtype==2) { t=(CSpline1D::Spline1DCalc(c,b+h)-2*CSpline1D::Spline1DCalc(c,b)+CSpline1D::Spline1DCalc(c,b-h))/CMath::Sqr(h); //--- search errors err=MathMax(err,MathAbs(br-t)); } //--- check if(bltype==-1 || brtype==-1) { //--- function calls CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s); CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2); //--- search errors err=MathMax(err,MathAbs(s-s2)); err=MathMax(err,MathAbs(ds-ds2)); err=MathMax(err,MathAbs(d2s-d2s2)); } //--- search errors cserrors=cserrors || err>1.0E-3; //--- Check Lipschitz continuity LConst(a,b,c,lstep,l10,l11,l12); LConst(a,b,c,lstep/3,l20,l21,l22); //--- check if(l10>1.0E-6) cserrors=cserrors || l20/l10>1.2; //--- check if(l11>1.0E-6) cserrors=cserrors || l21/l11>1.2; //--- check if(l12>1.0E-6) cserrors=cserrors || l22/l12>1.2; //--- compare spline1dgriddiff() and spline1ddiff() results err=0; //--- check if(periodiccond) CSpline1D::Spline1DGridDiffCubic(x,yp,n,bltype,bl,brtype,br,tmp1); else CSpline1D::Spline1DGridDiffCubic(x,y,n,bltype,bl,brtype,br,tmp1); //--- check if(!CAp::Assert(CAp::Len(tmp1)>=n)) return(false); for(i=0;i<=n-1;i++) { CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s); //--- search errors err=MathMax(err,MathAbs(ds-tmp1[i])); } //--- check if(periodiccond) CSpline1D::Spline1DGridDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmp1,tmp2); else CSpline1D::Spline1DGridDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmp1,tmp2); for(i=0;i<=n-1;i++) { CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s); //--- search errors err=MathMax(err,MathAbs(ds-tmp1[i])); err=MathMax(err,MathAbs(d2s-tmp2[i])); } //--- search errors cserrors=cserrors || err>threshold; //--- compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results n2=2+CMath::RandomInteger(2*n); ArrayResize(tmpx,n2); //--- calculation for(i=0;i<=n2-1;i++) tmpx[i]=0.5*(a+b)+(a-b)*(2*CMath::RandomReal()-1); err=0; //--- check if(periodiccond) CSpline1D::Spline1DConvCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0); else CSpline1D::Spline1DConvCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0); for(i=0;i<=n2-1;i++) { CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s); //--- search errors err=MathMax(err,MathAbs(s-tmp0[i])); } //--- check if(periodiccond) CSpline1D::Spline1DConvDiffCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1); else CSpline1D::Spline1DConvDiffCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1); for(i=0;i<=n2-1;i++) { CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s); //--- search errors err=MathMax(err,MathAbs(s-tmp0[i])); err=MathMax(err,MathAbs(ds-tmp1[i])); } //--- check if(periodiccond) CSpline1D::Spline1DConvDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2); else CSpline1D::Spline1DConvDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2); for(i=0;i<=n2-1;i++) { CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s); //--- search errors err=MathMax(err,MathAbs(s-tmp0[i])); err=MathMax(err,MathAbs(ds-tmp1[i])); err=MathMax(err,MathAbs(d2s-tmp2[i])); } //--- search errors cserrors=cserrors || err>threshold; } } //--- Build Catmull-Rom spline. //--- Test for interpolation scheme properties: //--- * values at nodes //--- * boundary conditions //--- * continuous function //--- * continuous first derivative //--- * periodicity properties for(bltype=-1;bltype<=0;bltype++) { periodiccond=bltype==-1; //--- select random tension value,then build if(CMath::RandomReal()>0.5) { //--- check if(CMath::RandomReal()>0.5) tension=0; else tension=1; } else tension=CMath::RandomReal(); //--- check if(periodiccond) CSpline1D::Spline1DBuildCatmullRom(x,yp,n,bltype,tension,c); else CSpline1D::Spline1DBuildCatmullRom(x,y,n,bltype,tension,c); //--- interpolation properties err=0; //--- check if(periodiccond) { //--- * check values at nodes;spline is periodic so //--- we add random number of periods to nodes //--- * we also test for periodicity of first derivative for(i=0;i<=n-1;i++) { v=x[i]; vm=v+(b-a)*(CMath::RandomInteger(5)-2); t=yp[i]-CSpline1D::Spline1DCalc(c,vm); //--- search errors err=MathMax(err,MathAbs(t)); //--- function calls CSpline1D::Spline1DDiff(c,v,s,ds,d2s); CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2); //--- search errors err=MathMax(err,MathAbs(s-s2)); err=MathMax(err,MathAbs(ds-ds2)); } //--- periodicity between nodes v=a+(b-a)*CMath::RandomReal(); vm=v+(b-a)*(CMath::RandomInteger(5)-2); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm))); //--- function calls CSpline1D::Spline1DDiff(c,v,s,ds,d2s); CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2); //--- search errors err=MathMax(err,MathAbs(s-s2)); err=MathMax(err,MathAbs(ds-ds2)); } else { //--- * check values at nodes for(i=0;i<=n-1;i++) err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))); } //--- search errors crserrors=crserrors || err>threshold; //--- check boundary conditions err=0; //--- check if(bltype==0) { //--- function calls CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s); CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2); t=(d2s2-d2s)/(2*h); //--- search errors err=MathMax(err,MathAbs(t)); //--- function calls CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s); CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2); t=(d2s2-d2s)/(2*h); //--- search errors err=MathMax(err,MathAbs(t)); } //--- check if(bltype==-1) { //--- function calls CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s); CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2); //--- search errors err=MathMax(err,MathAbs(s-s2)); err=MathMax(err,MathAbs(ds-ds2)); } //--- search errors crserrors=crserrors || err>1.0E-3; //--- Check Lipschitz continuity LConst(a,b,c,lstep,l10,l11,l12); LConst(a,b,c,lstep/3,l20,l21,l22); //--- check if(l10>1.0E-6) crserrors=crserrors || l20/l10>1.2; //--- check if(l11>1.0E-6) crserrors=crserrors || l21/l11>1.2; } //--- Build Hermite spline. //--- Test for interpolation scheme properties: //--- * values and derivatives at nodes //--- * continuous function //--- * continuous first derivative CSpline1D::Spline1DBuildHermite(x,y,d,n,c); //--- search errors err=0; for(i=0;i<=n-1;i++) err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))); //--- search errors hserrors=hserrors || err>threshold; err=0; for(i=0;i<=n-1;i++) { t=(CSpline1D::Spline1DCalc(c,x[i]+h)-CSpline1D::Spline1DCalc(c,x[i]-h))/(2*h); err=MathMax(err,MathAbs(d[i]-t)); } //--- search errors hserrors=hserrors || err>1.0E-3; LConst(a,b,c,lstep,l10,l11,l12); LConst(a,b,c,lstep/3,l20,l21,l22); //--- search errors hserrors=hserrors || l20/l10>1.2; hserrors=hserrors || l21/l11>1.2; //--- Build Akima spline //--- Test for general interpolation scheme properties: //--- * values at nodes //--- * continuous function //--- * continuous first derivative //--- Test for specific properties is implemented below. if(n>=5) { CSpline1D::Spline1DBuildAkima(x,y,n,c); //--- search errors err=0; for(i=0;i<=n-1;i++) err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))); //--- search errors aserrors=aserrors || err>threshold; LConst(a,b,c,lstep,l10,l11,l12); LConst(a,b,c,lstep/3,l20,l21,l22); //--- search errors hserrors=hserrors || l20/l10>1.2; hserrors=hserrors || l21/l11>1.2; } } } //--- Special linear spline test: //--- test for linearity between x[i] and x[i+1] for(n=2;n<=maxn;n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); //--- Prepare task a=-1; b=1; for(i=0;i<=n-1;i++) { x[i]=a+(b-a)*i/(n-1); y[i]=2*CMath::RandomReal()-1; } //--- function call CSpline1D::Spline1DBuildLinear(x,y,n,c); //--- Test err=0; for(k=0;k<=n-2;k++) { a=x[k]; b=x[k+1]; //--- calculation for(pass=1;pass<=passcount;pass++) { t=a+(b-a)*CMath::RandomReal(); v=y[k]+(t-a)/(b-a)*(y[k+1]-y[k]); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-v)); } } //--- search errors lserrors=lserrors || err>threshold; } //--- Special Akima test: test outlier sensitivity //--- Spline value at (x[i],x[i+1]) should depend from //--- f[i-2],f[i-1],f[i],f[i+1],f[i+2],f[i+3] only. for(n=5;n<=maxn;n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(y2,n); //--- Prepare unperturbed Akima spline a=-1; b=1; for(i=0;i<=n-1;i++) { x[i]=a+(b-a)*i/(n-1); y[i]=MathCos(1.3*M_PI*x[i]+0.4); } //--- function call CSpline1D::Spline1DBuildAkima(x,y,n,c); //--- Process perturbed tasks err=0; for(k=0;k<=n-1;k++) { for(i_=0;i_<=n-1;i_++) y2[i_]=y[i_]; y2[k]=5; //--- function call CSpline1D::Spline1DBuildAkima(x,y2,n,c2); //--- Test left part independence if(k-3>=1) { a=-1; b=x[k-3]; for(pass=1;pass<=passcount;pass++) { t=a+(b-a)*CMath::RandomReal(); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t))); } } //--- Test right part independence if(k+3<=n-2) { a=x[k+3]; b=1; for(pass=1;pass<=passcount;pass++) { t=a+(b-a)*CMath::RandomReal(); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t))); } } } //--- search errors aserrors=aserrors || err>threshold; } //--- Differentiation,copy/unpack test for(n=2;n<=maxn;n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); //--- Prepare cubic spline a=-1-CMath::RandomReal(); b=1+CMath::RandomReal(); for(i=0;i<=n-1;i++) { x[i]=a+(b-a)*i/(n-1); y[i]=MathCos(1.3*M_PI*x[i]+0.4); } //--- function call CSpline1D::Spline1DBuildCubic(x,y,n,2,0.0,2,0.0,c); //--- Test diff err=0; for(pass=1;pass<=passcount;pass++) { t=a+(b-a)*CMath::RandomReal(); //--- function calls CSpline1D::Spline1DDiff(c,t,s,ds,d2s); vl=CSpline1D::Spline1DCalc(c,t-h); vm=CSpline1D::Spline1DCalc(c,t); vr=CSpline1D::Spline1DCalc(c,t+h); //--- search errors err=MathMax(err,MathAbs(s-vm)); err=MathMax(err,MathAbs(ds-(vr-vl)/(2*h))); err=MathMax(err,MathAbs(d2s-(vr-2*vm+vl)/CMath::Sqr(h))); } //--- search errors dserrors=dserrors || err>0.001; //--- Test copy UnsetSpline1D(c2); CSpline1D::Spline1DCopy(c,c2); //--- search errors err=0; for(pass=1;pass<=passcount;pass++) { t=a+(b-a)*CMath::RandomReal(); err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t))); } //--- search errors cperrors=cperrors || err>threshold; //--- Test unpack uperrors=uperrors || !TestUnpack(c,x); //--- Test lin.trans. err=0; for(pass=1;pass<=passcount;pass++) { //--- LinTransX,general A sa=4*CMath::RandomReal()-2; sb=2*CMath::RandomReal()-1; t=a+(b-a)*CMath::RandomReal(); //--- function calls CSpline1D::Spline1DCopy(c,c2); CSpline1D::Spline1DLinTransX(c2,sa,sb); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,(t-sb)/sa))); //--- LinTransX,special case: A=0 sb=2*CMath::RandomReal()-1; t=a+(b-a)*CMath::RandomReal(); //--- function calls CSpline1D::Spline1DCopy(c,c2); CSpline1D::Spline1DLinTransX(c2,0,sb); //--- search errors err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,sb)-CSpline1D::Spline1DCalc(c2,t))); //--- LinTransY sa=2*CMath::RandomReal()-1; sb=2*CMath::RandomReal()-1; t=a+(b-a)*CMath::RandomReal(); //--- function calls CSpline1D::Spline1DCopy(c,c2); CSpline1D::Spline1DLinTransY(c2,sa,sb); //--- search errors err=MathMax(err,MathAbs(sa*CSpline1D::Spline1DCalc(c,t)+sb-CSpline1D::Spline1DCalc(c2,t))); } //--- search errors lterrors=lterrors || err>threshold; } //--- Testing integration. //--- Three tests are performed: //--- * approximate test (well behaved smooth function,many points, //--- integration inside [a,b]),non-periodic spline //--- * exact test (integration of parabola,outside of [a,b],non-periodic spline //--- * approximate test for periodic splines. F(x)=cos(2*pi*x)+1. //--- Period length is equals to 1.0,so all operations with //--- multiples of period are done exactly. For each value of PERIOD //--- we calculate and test integral at four points: //--- - 0 < t0 < PERIOD //--- - t1=PERIOD-eps //--- - t2=PERIOD //--- - t3=PERIOD+eps err=0; for(n=20;n<=35;n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); for(pass=1;pass<=passcount;pass++) { //--- Prepare cubic spline a=-1-0.2*CMath::RandomReal(); b=1+0.2*CMath::RandomReal(); for(i=0;i<=n-1;i++) { x[i]=a+(b-a)*i/(n-1); y[i]=MathSin(M_PI*x[i]+0.4)+MathExp(x[i]); } //--- change values bl=M_PI*MathCos(M_PI*a+0.4)+MathExp(a); br=M_PI*MathCos(M_PI*b+0.4)+MathExp(b); //--- function call CSpline1D::Spline1DBuildCubic(x,y,n,1,bl,1,br,c); //--- Test t=a+(b-a)*CMath::RandomReal(); v=-(MathCos(M_PI*a+0.4)/M_PI)+MathExp(a); v=-(MathCos(M_PI*t+0.4)/M_PI)+MathExp(t)-v; v=v-CSpline1D::Spline1DIntegrate(c,t); //--- search errors err=MathMax(err,MathAbs(v)); } } //--- search errors ierrors=ierrors || err>0.001; p0=2*CMath::RandomReal()-1; p1=2*CMath::RandomReal()-1; p2=2*CMath::RandomReal()-1; a=-CMath::RandomReal()-0.5; b=CMath::RandomReal()+0.5; n=2; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(d,n); x[0]=a; y[0]=p0+p1*a+p2*CMath::Sqr(a); d[0]=p1+2*p2*a; x[1]=b; y[1]=p0+p1*b+p2*CMath::Sqr(b); d[1]=p1+2*p2*b; //--- function call CSpline1D::Spline1DBuildHermite(x,y,d,n,c); bl=MathMin(a,b)-MathAbs(b-a); br=MathMin(a,b)+MathAbs(b-a); err=0; //--- calculation for(pass=1;pass<=100;pass++) { t=bl+(br-bl)*CMath::RandomReal(); v=p0*t+p1*CMath::Sqr(t)/2+p2*CMath::Sqr(t)*t/3-(p0*a+p1*CMath::Sqr(a)/2+p2*CMath::Sqr(a)*a/3); v=v-CSpline1D::Spline1DIntegrate(c,t); //--- search errors err=MathMax(err,MathAbs(v)); } //--- search errors ierrors=ierrors || err>threshold; n=100; //--- allocation ArrayResize(x,n); ArrayResize(y,n); for(i=0;i<=n-1;i++) { x[i]=(double)i/(double)(n-1); y[i]=MathCos(2*M_PI*x[i])+1; } //--- change values y[0]=2; y[n-1]=2; //--- function calls CSpline1D::Spline1DBuildCubic(x,y,n,-1,0.0,-1,0.0,c); intab=CSpline1D::Spline1DIntegrate(c,1.0); v=CMath::RandomReal(); vr=CSpline1D::Spline1DIntegrate(c,v); //--- search errors ierrors=ierrors || MathAbs(intab-1)>0.001; for(i=-10;i<=10;i++) { ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i+v)-(i*intab+vr))>0.001; ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i-1000*CMath::m_machineepsilon)-i*intab)>0.001; ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i)-i*intab)>0.001; ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i+1000*CMath::m_machineepsilon)-i*intab)>0.001; } //--- report waserrors=((((((((lserrors || cserrors) || crserrors) || hserrors) || aserrors) || dserrors) || cperrors) || uperrors) || lterrors) || ierrors; //--- check if(!silent) { Print("TESTING SPLINE INTERPOLATION"); //--- Normal tests Print("LINEAR SPLINE TEST: "); //--- check if(lserrors) Print("FAILED"); else Print("OK"); Print("CUBIC SPLINE TEST: "); //--- check if(cserrors) Print("FAILED"); else Print("OK"); Print("CATMULL-ROM SPLINE TEST: "); //--- check if(crserrors) Print("FAILED"); else Print("OK"); Print("HERMITE SPLINE TEST: "); //--- check if(hserrors) Print("FAILED"); else Print("OK"); Print("AKIMA SPLINE TEST: "); //--- check if(aserrors) Print("FAILED"); else Print("OK"); Print("DIFFERENTIATION TEST: "); //--- check if(dserrors) Print("FAILED"); else Print("OK"); Print("COPY/SERIALIZATION TEST: "); //--- check if(cperrors) Print("FAILED"); else Print("OK"); Print("UNPACK TEST: "); //--- check if(uperrors) Print("FAILED"); else Print("OK"); Print("LIN.TRANS. TEST: "); //--- check if(lterrors) Print("FAILED"); else Print("OK"); Print("INTEGRATION TEST: "); //--- check if(ierrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Lipschitz constants for spline inself,first and second | //| derivatives. | //+------------------------------------------------------------------+ static void CTestSpline1DUnit::LConst(const double a,const double b, CSpline1DInterpolant &c, const double lstep,double &l0, double &l1,double &l2) { //--- create variables double t=0; double vl=0; double vm=0; double vr=0; double prevf=0; double prevd=0; double prevd2=0; double f=0; double d=0; double d2=0; //--- change values l0=0; l1=0; l2=0; t=a-0.1; vl=CSpline1D::Spline1DCalc(c,t-2*lstep); vm=CSpline1D::Spline1DCalc(c,t-lstep); vr=CSpline1D::Spline1DCalc(c,t); f=vm; d=(vr-vl)/(2*lstep); d2=(vr-2*vm+vl)/CMath::Sqr(lstep); //--- calculation while(t<=b+0.1) { //--- change values prevf=f; prevd=d; prevd2=d2; vl=vm; vm=vr; vr=CSpline1D::Spline1DCalc(c,t+lstep); f=vm; d=(vr-vl)/(2*lstep); d2=(vr-2*vm+vl)/CMath::Sqr(lstep); l0=MathMax(l0,MathAbs((f-prevf)/lstep)); l1=MathMax(l1,MathAbs((d-prevd)/lstep)); l2=MathMax(l2,MathAbs((d2-prevd2)/lstep)); t=t+lstep; } } //+------------------------------------------------------------------+ //| Unpack testing | //+------------------------------------------------------------------+ static bool CTestSpline1DUnit::TestUnpack(CSpline1DInterpolant &c,double &x[]) { //--- create variables bool result; int i=0; int n=0; double err=0; double t=0; double v1=0; double v2=0; int pass=0; int passcount=0; //--- create matrix CMatrixDouble tbl; //--- initialization passcount=20; err=0; //--- function call CSpline1D::Spline1DUnpack(c,n,tbl); //--- calculation for(i=0;i<=n-2;i++) { for(pass=1;pass<=passcount;pass++) { t=CMath::RandomReal()*(tbl[i][1]-tbl[i][0]); v1=tbl[i][2]+t*tbl[i][3]+CMath::Sqr(t)*tbl[i][4]+t*CMath::Sqr(t)*tbl[i][5]; v2=CSpline1D::Spline1DCalc(c,tbl[i][0]+t); //--- search errors err=MathMax(err,MathAbs(v1-v2)); } } //--- search errors for(i=0;i<=n-2;i++) err=MathMax(err,MathAbs(x[i]-tbl[i][0])); //--- search errors for(i=0;i<=n-2;i++) err=MathMax(err,MathAbs(x[i+1]-tbl[i][1])); //--- get result result=err<100*CMath::m_machineepsilon; //--- return result return(result); } //+------------------------------------------------------------------+ //| Unset spline,i.e. initialize it with random garbage | //+------------------------------------------------------------------+ static void CTestSpline1DUnit::UnsetSpline1D(CSpline1DInterpolant &c) { //--- create arrays double x[]; double y[]; double d[]; //--- allocation ArrayResize(x,2); ArrayResize(y,2); ArrayResize(d,2); //--- change values x[0]=-1; y[0]=CMath::RandomReal(); d[0]=CMath::RandomReal(); x[1]=1; y[1]=CMath::RandomReal(); d[1]=CMath::RandomReal(); //--- function call CSpline1D::Spline1DBuildHermite(x,y,d,2,c); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ static void CTestSpline1DUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Tests whether constant C is solution of 1D LLS problem | //+------------------------------------------------------------------+ static bool CTestSpline1DUnit::Is1DSolution(const int n,double &y[], double &w[],const double c) { //--- create variables bool result; int i=0; double s1=0; double s2=0; double s3=0; double delta=0; //--- initialization delta=0.001; //--- Test result s1=0; for(i=0;i<=n-1;i++) s1=s1+CMath::Sqr(w[i]*(c-y[i])); s2=0; s3=0; //--- calculation for(i=0;i<=n-1;i++) s2=s2+CMath::Sqr(w[i]*(c+delta-y[i])); s3=s3+CMath::Sqr(w[i]*(c-delta-y[i])); result=s2>=s1 && s3>=s1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CMinLM | //+------------------------------------------------------------------+ class CTestMinLMUnit { public: //--- constructor, destructor CTestMinLMUnit(void); ~CTestMinLMUnit(void); //--- public methods static bool TestMinLM(const bool silent); static bool RKindVSStateCheck(const int rkind,CMinLMState &state); static void AXMB(CMinLMState &state,CMatrixDouble &a,double &b[],const int n); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestMinLMUnit::CTestMinLMUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestMinLMUnit::~CTestMinLMUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CMinLM | //+------------------------------------------------------------------+ static bool CTestMinLMUnit::TestMinLM(const bool silent) { //--- create variables bool waserrors; bool referror; bool lin1error; bool lin2error; bool eqerror; bool converror; bool scerror; bool restartserror; bool othererrors; int rkind=0; int ckind=0; int tmpkind=0; double epsf=0; double epsx=0; double epsg=0; int maxits=0; int n=0; int m=0; int i=0; int j=0; double v=0; double s=0; double stpmax=0; double h=0; double fprev=0; double xprev=0; int i_=0; //--- create arrays double x[]; double xe[]; double b[]; double bl[]; double bu[]; double xlast[]; //--- create matrix CMatrixDouble a; //--- objects of classes CMinLMState state; CMinLMReport rep; //--- initialization waserrors=false; referror=false; lin1error=false; lin2error=false; eqerror=false; converror=false; scerror=false; othererrors=false; restartserror=false; //--- Reference problem. //--- See comments for RKindVsStateCheck() for more info about RKind. //--- NOTES: we also test negative RKind's corresponding to "inexact" schemes //--- which use approximate finite difference Jacobian. ArrayResize(x,3); n=3; m=3; h=0.0001; //--- calculation for(rkind=-2;rkind<=5;rkind++) { //--- change values x[0]=100*CMath::RandomReal()-50; x[1]=100*CMath::RandomReal()-50; x[2]=100*CMath::RandomReal()-50; //--- check if(rkind==-2) { CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==-1) { CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==0) CMinLM::MinLMCreateFJ(n,m,x,state); //--- check if(rkind==1) CMinLM::MinLMCreateFGJ(n,m,x,state); //--- check if(rkind==2) CMinLM::MinLMCreateFGH(n,x,state); //--- check if(rkind==3) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==4) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==5) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); } //--- cycle while(CMinLM::MinLMIteration(state)) { //--- (x-2)^2 + y^2 + (z-x)^2 if(state.m_needfi) { state.m_fi[0]=state.m_x[0]-2; state.m_fi[1]=state.m_x[1]; state.m_fi[2]=state.m_x[2]-state.m_x[0]; } //--- check if(state.m_needfij) { state.m_fi[0]=state.m_x[0]-2; state.m_fi[1]=state.m_x[1]; state.m_fi[2]=state.m_x[2]-state.m_x[0]; state.m_j[0].Set(0,1); state.m_j[0].Set(1,0); state.m_j[0].Set(2,0); state.m_j[1].Set(0,0); state.m_j[1].Set(1,1); state.m_j[1].Set(2,0); state.m_j[2].Set(0,-1); state.m_j[2].Set(1,0); state.m_j[2].Set(2,1); } //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg || state.m_needfgh) { state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } //--- check if(state.m_needfgh) { state.m_h[0].Set(0,4); state.m_h[0].Set(1,0); state.m_h[0].Set(2,-2); state.m_h[1].Set(0,0); state.m_h[1].Set(1,2); state.m_h[1].Set(2,0); state.m_h[2].Set(0,-2); state.m_h[2].Set(1,0); state.m_h[2].Set(2,2); } //--- search errors scerror=scerror || !RKindVSStateCheck(rkind,state); } //--- function call CMinLM::MinLMResults(state,x,rep); //--- search errors referror=(((referror || rep.m_terminationtype<=0) || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001; } //--- Reference bound constrained problem: //--- min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1 //--- NOTES: //--- 1. we test only two optimization modes - V and FGH, //--- because from algorithm internals we can assume that actual //--- mode being used doesn't matter for bound constrained optimization //--- process. for(tmpkind=0;tmpkind<=1;tmpkind++) { for(n=1;n<=5;n++) { //--- allocation ArrayResize(bl,n); ArrayResize(bu,n); ArrayResize(xe,n); ArrayResize(x,n); for(i=0;i<=n-1;i++) { bl[i]=0; bu[i]=1; xe[i]=3*CMath::RandomReal()-1; x[i]=CMath::RandomReal(); } //--- check if(tmpkind==0) CMinLM::MinLMCreateFGH(n,x,state); //--- check if(tmpkind==1) CMinLM::MinLMCreateV(n,n,x,1.0E-3,state); //--- function calls CMinLM::MinLMSetCond(state,1.0E-6,0,0,0); CMinLM::MinLMSetBC(state,bl,bu); //--- cycle while(CMinLM::MinLMIteration(state)) { //--- check if(state.m_needfi) { for(i=0;i<=n-1;i++) state.m_fi[i]=MathPow(state.m_x[i]-xe[i],2); } //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) { //--- change value state.m_f=0; for(i=0;i<=n-1;i++) state.m_f=state.m_f+MathPow(state.m_x[i]-xe[i],4); } //--- check if(state.m_needfg || state.m_needfgh) { for(i=0;i<=n-1;i++) state.m_g[i]=4*MathPow(state.m_x[i]-xe[i],3); } //--- check if(state.m_needfgh) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) state.m_h[i].Set(j,0); } for(i=0;i<=n-1;i++) state.m_h[i].Set(i,12*MathPow(state.m_x[i]-xe[i],2)); } } //--- function call CMinLM::MinLMResults(state,x,rep); //--- check if(rep.m_terminationtype==4) { for(i=0;i<=n-1;i++) referror=referror || MathAbs(x[i]-CApServ::BoundVal(xe[i],bl[i],bu[i]))>(double)(5.0E-2); } else referror=true; } } //--- 1D problem #1 //--- NOTES: we also test negative RKind's corresponding to "inexact" schemes //--- which use approximate finite difference Jacobian. for(rkind=-2;rkind<=5;rkind++) { //--- allocation ArrayResize(x,1); n=1; m=1; h=0.00001; x[0]=100*CMath::RandomReal()-50; //--- check if(rkind==-2) { CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==-1) { CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==0) CMinLM::MinLMCreateFJ(n,m,x,state); //--- check if(rkind==1) CMinLM::MinLMCreateFGJ(n,m,x,state); //--- check if(rkind==2) CMinLM::MinLMCreateFGH(n,x,state); //--- check if(rkind==3) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==4) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==5) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); } //--- cycle while(CMinLM::MinLMIteration(state)) { //--- check if(state.m_needfi) state.m_fi[0]=MathSin(state.m_x[0]); //--- check if(state.m_needfij) { state.m_fi[0]=MathSin(state.m_x[0]); state.m_j[0].Set(0,MathCos(state.m_x[0])); } //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=CMath::Sqr(MathSin(state.m_x[0])); //--- check if(state.m_needfg || state.m_needfgh) state.m_g[0]=2*MathSin(state.m_x[0])*MathCos(state.m_x[0]); //--- check if(state.m_needfgh) state.m_h[0].Set(0,2*(MathCos(state.m_x[0])*MathCos(state.m_x[0])-MathSin(state.m_x[0])*MathSin(state.m_x[0]))); //--- search errors scerror=scerror || !RKindVSStateCheck(rkind,state); } //--- function call CMinLM::MinLMResults(state,x,rep); //--- search errors lin1error=rep.m_terminationtype<=0 || MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001; } //--- Linear equations: test normal optimization and optimization with restarts for(n=1;n<=10;n++) { //--- Prepare task h=0.00001; //--- function call CMatGen::RMatrixRndCond(n,100,a); //--- allocation ArrayResize(x,n); ArrayResize(xe,n); ArrayResize(b,n); //--- change values for(i=0;i<=n-1;i++) xe[i]=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*xe[i_]; b[i]=v; } //--- Test different RKind //--- NOTES: we also test negative RKind's corresponding to "inexact" schemes //--- which use approximate finite difference Jacobian. for(rkind=-2;rkind<=5;rkind++) { //--- Solve task (first attempt) for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- check if(rkind==-2) { CMinLM::MinLMCreateV(n,n,x,h,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==-1) { CMinLM::MinLMCreateV(n,n,x,h,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==0) CMinLM::MinLMCreateFJ(n,n,x,state); //--- check if(rkind==1) CMinLM::MinLMCreateFGJ(n,n,x,state); //--- check if(rkind==2) CMinLM::MinLMCreateFGH(n,x,state); //--- check if(rkind==3) { CMinLM::MinLMCreateVJ(n,n,x,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==4) { CMinLM::MinLMCreateVJ(n,n,x,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==5) { CMinLM::MinLMCreateVJ(n,n,x,state); CMinLM::MinLMSetAccType(state,2); } //--- cycle while(CMinLM::MinLMIteration(state)) { AXMB(state,a,b,n); scerror=scerror || !RKindVSStateCheck(rkind,state); } //--- function call CMinLM::MinLMResults(state,x,rep); //--- search errors eqerror=eqerror || rep.m_terminationtype<=0; for(i=0;i<=n-1;i++) eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001; //--- Now we try to restart algorithm from new point for(i=0;i<=n-1;i++) x[i]=2*CMath::RandomReal()-1; //--- function call CMinLM::MinLMRestartFrom(state,x); //--- cycle while(CMinLM::MinLMIteration(state)) { AXMB(state,a,b,n); scerror=scerror || !RKindVSStateCheck(rkind,state); } //--- function call CMinLM::MinLMResults(state,x,rep); //--- search errors restartserror=restartserror || rep.m_terminationtype<=0; for(i=0;i<=n-1;i++) restartserror=restartserror || MathAbs(x[i]-xe[i])>0.001; } } //--- Testing convergence properties using //--- different optimizer types and different conditions. //--- Only limited subset of optimizers is tested because some //--- optimizers converge too quickly. s=100; for(rkind=0;rkind<=5;rkind++) { //--- Skip FGH optimizer - it converges too quickly if(rkind==2) continue; //--- Test for(ckind=0;ckind<=3;ckind++) { epsg=0; epsf=0; epsx=0; maxits=0; //--- check if(ckind==0) epsf=0.000001; //--- check if(ckind==1) epsx=0.000001; //--- check if(ckind==2) maxits=2; //--- check if(ckind==3) epsg=0.0001; //--- allocation ArrayResize(x,3); //--- change values n=3; m=3; for(i=0;i<=2;i++) x[i]=6; //--- check if(rkind==0) CMinLM::MinLMCreateFJ(n,m,x,state); //--- check if(rkind==1) CMinLM::MinLMCreateFGJ(n,m,x,state); //--- check if(!CAp::Assert(rkind!=2)) return(false); //--- check if(rkind==3) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==4) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==5) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); } //--- function call CMinLM::MinLMSetCond(state,epsg,epsf,epsx,maxits); //--- cycle while(CMinLM::MinLMIteration(state)) { //--- check if(state.m_needfi || state.m_needfij) { state.m_fi[0]=s*(MathExp(state.m_x[0])-2); state.m_fi[1]=CMath::Sqr(state.m_x[1])+1; state.m_fi[2]=state.m_x[2]-state.m_x[0]; } //--- check if(state.m_needfij) { state.m_j[0].Set(0,s*MathExp(state.m_x[0])); state.m_j[0].Set(1,0); state.m_j[0].Set(2,0); state.m_j[1].Set(0,0); state.m_j[1].Set(1,2*state.m_x[1]); state.m_j[1].Set(2,0); state.m_j[2].Set(0,-1); state.m_j[2].Set(1,0); state.m_j[2].Set(2,1); } //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+1)+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg || state.m_needfgh) { state.m_g[0]=s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+1)*2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } //--- check if(state.m_needfgh) { state.m_h[0].Set(0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2); state.m_h[0].Set(1,0); state.m_h[0].Set(2,-2); state.m_h[1].Set(0,0); state.m_h[1].Set(1,12*CMath::Sqr(state.m_x[1])+4); state.m_h[1].Set(2,0); state.m_h[2].Set(0,-2); state.m_h[2].Set(1,0); state.m_h[2].Set(2,2); } //--- search errors scerror=scerror || !RKindVSStateCheck(rkind,state); } //--- function call CMinLM::MinLMResults(state,x,rep); //--- check if(ckind==0) { //--- search errors converror=converror || MathAbs(x[0]-MathLog(2))>0.05; converror=converror || MathAbs(x[1])>0.05; converror=converror || MathAbs(x[2]-MathLog(2))>0.05; converror=converror || rep.m_terminationtype!=1; } //--- check if(ckind==1) { //--- search errors converror=converror || MathAbs(x[0]-MathLog(2))>0.05; converror=converror || MathAbs(x[1])>0.05; converror=converror || MathAbs(x[2]-MathLog(2))>0.05; converror=converror || rep.m_terminationtype!=2; } //--- check if(ckind==2) { //--- search errors converror=(converror || rep.m_terminationtype!=5) || rep.m_iterationscount!=maxits; } //--- check if(ckind==3) { //--- search errors converror=converror || MathAbs(x[0]-MathLog(2))>0.05; converror=converror || MathAbs(x[1])>0.05; converror=converror || MathAbs(x[2]-MathLog(2))>0.05; converror=converror || rep.m_terminationtype!=4; } } } //--- Other properties: //--- 1. test reports (F should form monotone sequence) //--- 2. test maximum step for(rkind=0;rkind<=5;rkind++) { //--- reports: //--- * check that first report is initial point //--- * check that F is monotone decreasing //--- * check that last report is final result n=3; m=3; s=100; //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); for(i=0;i<=n-1;i++) x[i]=6; //--- check if(rkind==0) CMinLM::MinLMCreateFJ(n,m,x,state); //--- check if(rkind==1) CMinLM::MinLMCreateFGJ(n,m,x,state); //--- check if(rkind==2) CMinLM::MinLMCreateFGH(n,x,state); //--- check if(rkind==3) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); } //--- check if(rkind==4) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); } //--- check if(rkind==5) { CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); } //--- function calls CMinLM::MinLMSetCond(state,0,0,0,4); CMinLM::MinLMSetXRep(state,true); //--- change value fprev=CMath::m_maxrealnumber; //--- cycle while(CMinLM::MinLMIteration(state)) { //--- check if(state.m_needfi || state.m_needfij) { state.m_fi[0]=MathSqrt(s)*(MathExp(state.m_x[0])-2); state.m_fi[1]=state.m_x[1]; state.m_fi[2]=state.m_x[2]-state.m_x[0]; } //--- check if(state.m_needfij) { state.m_j[0].Set(0,MathSqrt(s)*MathExp(state.m_x[0])); state.m_j[0].Set(1,0); state.m_j[0].Set(2,0); state.m_j[1].Set(0,0); state.m_j[1].Set(1,1); state.m_j[1].Set(2,0); state.m_j[2].Set(0,-1); state.m_j[2].Set(1,0); state.m_j[2].Set(2,1); } //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg || state.m_needfgh) { state.m_g[0]=s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]); state.m_g[1]=2*state.m_x[1]; state.m_g[2]=2*(state.m_x[2]-state.m_x[0]); } //--- check if(state.m_needfgh) { state.m_h[0].Set(0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2); state.m_h[0].Set(1,0); state.m_h[0].Set(2,-2); state.m_h[1].Set(0,0); state.m_h[1].Set(1,2); state.m_h[1].Set(2,0); state.m_h[2].Set(0,-2); state.m_h[2].Set(1,0); state.m_h[2].Set(2,2); } //--- search errors scerror=scerror || !RKindVSStateCheck(rkind,state); //--- check if(state.m_xupdated) { othererrors=othererrors || state.m_f>fprev; //--- check if(fprev==CMath::m_maxrealnumber) { for(i=0;i<=n-1;i++) othererrors=othererrors || state.m_x[i]!=x[i]; } fprev=state.m_f; for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; } } //--- function call CMinLM::MinLMResults(state,x,rep); //--- search errors for(i=0;i<=n-1;i++) othererrors=othererrors || x[i]!=xlast[i]; } n=1; //--- allocation ArrayResize(x,n); x[0]=100; stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinLM::MinLMCreateFGH(n,x,state); CMinLM::MinLMSetCond(state,1.0E-9,0,0,0); CMinLM::MinLMSetStpMax(state,stpmax); CMinLM::MinLMSetXRep(state,true); xprev=x[0]; //--- cycle while(CMinLM::MinLMIteration(state)) { //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); //--- check if(state.m_needfg || state.m_needfgh) state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]); //--- check if(state.m_needfgh) state.m_h[0].Set(0,MathExp(state.m_x[0])+MathExp(-state.m_x[0])); //--- search errors othererrors=othererrors || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); //--- check if(state.m_xupdated) xprev=state.m_x[0]; } //--- end waserrors=((((((referror || lin1error) || lin2error) || eqerror) || converror) || scerror) || othererrors) || restartserror; //--- check if(!silent) { Print("TESTING LEVENBERG-MARQUARDT OPTIMIZATION"); Print("REFERENCE PROBLEMS: "); //--- check if(referror) Print("FAILED"); else Print("OK"); Print("1-D PROBLEM #1: "); //--- check if(lin1error) Print("FAILED"); else Print("OK"); Print("1-D PROBLEM #2: "); //--- check if(lin2error) Print("FAILED"); else Print("OK"); Print("LINEAR EQUATIONS: "); //--- check if(eqerror) Print("FAILED"); else Print("OK"); Print("RESTARTS: "); //--- check if(restartserror) Print("FAILED"); else Print("OK"); Print("CONVERGENCE PROPERTIES: "); //--- check if(converror) Print("FAILED"); else Print("OK"); Print("STATE FIELDS CONSISTENCY: "); //--- check if(scerror) Print("FAILED"); else Print("OK"); Print("OTHER PROPERTIES: "); //--- check if(othererrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Asserts that State fields are consistent with RKind. | //| Returns False otherwise. | //| RKind is an algorithm selector: | //| * -2=V,AccType=1 | //| * -1=V,AccType=0 | //| * 0=FJ | //| * 1=FGJ | //| * 2=FGH | //| * 3=VJ,AccType=0 | //| * 4=VJ,AccType=1 | //| * 5=VJ,AccType=2 | //+------------------------------------------------------------------+ static bool CTestMinLMUnit::RKindVSStateCheck(const int rkind,CMinLMState &state) { //--- create a variable int nset=0; //--- check if(state.m_needfi) nset=nset+1; //--- check if(state.m_needf) nset=nset+1; //--- check if(state.m_needfg) nset=nset+1; //--- check if(state.m_needfij) nset=nset+1; //--- check if(state.m_needfgh) nset=nset+1; //--- check if(state.m_xupdated) nset=nset+1; //--- check if(nset!=1) return(false); //--- check if(rkind==-2) return(state.m_needfi || state.m_xupdated); //--- check if(rkind==-1) return(state.m_needfi || state.m_xupdated); //--- check if(rkind==0) return(state.m_needf || state.m_needfij || state.m_xupdated); //--- check if(rkind==1) return(state.m_needf || state.m_needfij || state.m_needfg || state.m_xupdated); //--- check if(rkind==2) return(state.m_needf || state.m_needfg || state.m_needfgh || state.m_xupdated); //--- check if(rkind==3) return(state.m_needfi || state.m_needfij || state.m_xupdated); //--- check if(rkind==4) return(state.m_needfi || state.m_needfij || state.m_xupdated); //--- check if(rkind==5) return(state.m_needfi || state.m_needfij || state.m_xupdated); //--- return result return(false); } //+------------------------------------------------------------------+ //| Calculates FI/F/G/H for problem min(||Ax-b||) | //+------------------------------------------------------------------+ static void CTestMinLMUnit::AXMB(CMinLMState &state,CMatrixDouble &a, double &b[],const int n) { //--- create variables int i=0; int j=0; int k=0; double v=0; int i_=0; //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=0; //--- check if(state.m_needfg || state.m_needfgh) { for(i=0;i<=n-1;i++) state.m_g[i]=0; } //--- check if(state.m_needfgh) { for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) state.m_h[i].Set(j,0); } } //--- calculation for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=a[i][i_]*state.m_x[i_]; //--- check if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=state.m_f+CMath::Sqr(v-b[i]); //--- check if(state.m_needfg || state.m_needfgh) { for(j=0;j<=n-1;j++) state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j]; } //--- check if(state.m_needfgh) { for(j=0;j<=n-1;j++) { for(k=0;k<=n-1;k++) state.m_h[j].Set(k,state.m_h[j][k]+2*a[i][j]*a[i][k]); } } //--- check if(state.m_needfi) state.m_fi[i]=v-b[i]; //--- check if(state.m_needfij) { state.m_fi[i]=v-b[i]; for(i_=0;i_<=n-1;i_++) state.m_j[i].Set(i_,a[i][i_]); } } } //+------------------------------------------------------------------+ //| Testing class CLSFit | //+------------------------------------------------------------------+ class CTestLSFitUnit { private: //--- private methods static void TestPolynomialFitting(bool &fiterrors); static void TestRationalFitting(bool &fiterrors); static void TestSplineFitting(bool &fiterrors); static void TestGeneralFitting(bool &llserrors,bool &nlserrors); static bool IsGLSSolution(const int n,const int m,const int k,double &y[],double &w[],CMatrixDouble &fmatrix,CMatrixDouble &cmatrix,double &cc[]); static double GetGLSError(const int n,const int m,double &y[],double &w[],CMatrixDouble &fmatrix,double &c[]); static void FitLinearNonlinear(const int m,const int deravailable,CMatrixDouble &xy,CLSFitState &state,bool &nlserrors); public: //--- constructor, destructor CTestLSFitUnit(void); ~CTestLSFitUnit(void); //--- public method static bool TestLSFit(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestLSFitUnit::CTestLSFitUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestLSFitUnit::~CTestLSFitUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CLSFit | //+------------------------------------------------------------------+ static bool CTestLSFitUnit::TestLSFit(const bool silent) { //--- create variables bool waserrors; bool llserrors; bool nlserrors; bool polfiterrors; bool ratfiterrors; bool splfiterrors; //--- initialization waserrors=false; //--- function calls TestPolynomialFitting(polfiterrors); TestRationalFitting(ratfiterrors); TestSplineFitting(splfiterrors); TestGeneralFitting(llserrors,nlserrors); //--- report waserrors=(((llserrors || nlserrors) || polfiterrors) || ratfiterrors) || splfiterrors; //--- check if(!silent) { Print("TESTING LEAST SQUARES"); Print("POLYNOMIAL LEAST SQUARES: "); //--- check if(polfiterrors) Print("FAILED"); else Print("OK"); Print("RATIONAL LEAST SQUARES: "); //--- check if(ratfiterrors) Print("FAILED"); else Print("OK"); Print("SPLINE LEAST SQUARES: "); //--- check if(splfiterrors) Print("FAILED"); else Print("OK"); Print("LINEAR LEAST SQUARES: "); //--- check if(llserrors) Print("FAILED"); else Print("OK"); Print("NON-LINEAR LEAST SQUARES: "); //--- check if(nlserrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Unit test | //+------------------------------------------------------------------+ static void CTestLSFitUnit::TestPolynomialFitting(bool &fiterrors) { //--- create variables double threshold=0; double t=0; int i=0; int k=0; int info=0; int info2=0; double v=0; double v0=0; double v1=0; double v2=0; double s=0; double xmin=0; double xmax=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; int n=0; int m=0; int maxn=0; int pass=0; int passcount=0; //--- create arrays double x[]; double y[]; double w[]; double x2[]; double y2[]; double w2[]; double xfull[]; double yfull[]; double xc[]; double yc[]; int dc[]; //--- objects of classes êëàññîâ CBarycentricInterpolant p; CBarycentricInterpolant p1; CBarycentricInterpolant p2; CPolynomialFitReport rep; CPolynomialFitReport rep2; //--- initialization fiterrors=false; maxn=5; passcount=20; threshold=1.0E8*CMath::m_machineepsilon; //--- Test polunomial fitting for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- N=M+K fitting (i.e. interpolation) for(k=0;k<=n-1;k++) { CApServ::TaskGenInt1D(-1,1,n,xfull,yfull); //--- allocation ArrayResize(x,n-k); ArrayResize(y,n-k); ArrayResize(w,n-k); //--- check if(k>0) { //--- allocation ArrayResize(xc,k); ArrayResize(yc,k); ArrayResize(dc,k); } //--- change values for(i=0;i<=n-k-1;i++) { x[i]=xfull[i]; y[i]=yfull[i]; w[i]=1+CMath::RandomReal(); } //--- change values for(i=0;i<=k-1;i++) { xc[i]=xfull[n-k+i]; yc[i]=yfull[n-k+i]; dc[i]=0; } //--- function call CLSFit::PolynomialFitWC(x,y,w,n-k,xc,yc,dc,k,n,info,p1,rep); //--- check if(info<=0) fiterrors=true; else { //--- search errors for(i=0;i<=n-k-1;i++) fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(p1,x[i])-y[i])>threshold; for(i=0;i<=k-1;i++) fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(p1,xc[i])-yc[i])>threshold; } } //--- Testing constraints on derivatives. //--- Special tasks which will always have solution: //--- 1. P(0)=YC[0] //--- 2. P(0)=YC[0],P'(0)=YC[1] if(n>1) { for(m=3;m<=5;m++) { for(k=1;k<=2;k++) { CApServ::TaskGenInt1D(-1,1,n,x,y); //--- allocation ArrayResize(w,n); ArrayResize(xc,2); ArrayResize(yc,2); ArrayResize(dc,2); //--- change values for(i=0;i<=n-1;i++) w[i]=1+CMath::RandomReal(); xc[0]=0; yc[0]=2*CMath::RandomReal()-1; dc[0]=0; xc[1]=0; yc[1]=2*CMath::RandomReal()-1; dc[1]=1; //--- function call CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,k,m,info,p1,rep); //--- check if(info<=0) fiterrors=true; else { //--- function call CRatInt::BarycentricDiff1(p1,0.0,v0,v1); fiterrors=fiterrors || MathAbs(v0-yc[0])>threshold; //--- check if(k==2) fiterrors=fiterrors || MathAbs(v1-yc[1])>threshold; } } } } } } //--- calculation for(m=2;m<=8;m++) { for(pass=1;pass<=passcount;pass++) { //--- General fitting //--- interpolating function through M nodes should have //--- greater RMS error than fitting it through the same M nodes n=100; ArrayResize(x2,n); ArrayResize(y2,n); ArrayResize(w2,n); xmin=0; xmax=2*M_PI; //--- change values for(i=0;i<=n-1;i++) { x2[i]=2*M_PI*CMath::RandomReal(); y2[i]=MathSin(x2[i]); w2[i]=1; } //--- allocation ArrayResize(x,m); ArrayResize(y,m); for(i=0;i<=m-1;i++) { x[i]=xmin+(xmax-xmin)*i/(m-1); y[i]=MathSin(x[i]); } //--- function calls CPolInt::PolynomialBuild(x,y,m,p1); CLSFit::PolynomialFitWC(x2,y2,w2,n,xc,yc,dc,0,m,info,p2,rep); //--- check if(info<=0) fiterrors=true; else { //--- calculate P1 (interpolant) RMS error,compare with P2 error v1=0; v2=0; for(i=0;i<=n-1;i++) { v1=v1+CMath::Sqr(CRatInt::BarycentricCalc(p1,x2[i])-y2[i]); v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(p2,x2[i])-y2[i]); } v1=MathSqrt(v1/n); v2=MathSqrt(v2/n); //--- search errors fiterrors=fiterrors || v2>v1; fiterrors=fiterrors || MathAbs(v2-rep.m_rmserror)>threshold; } //--- compare weighted and non-weighted n=20; ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); for(i=0;i<=n-1;i++) { x[i]=2*CMath::RandomReal()-1; y[i]=2*CMath::RandomReal()-1; w[i]=1; } //--- function calls CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,0,m,info,p1,rep); CLSFit::PolynomialFit(x,y,n,m,info2,p2,rep2); //--- check if(info<=0 || info2<=0) fiterrors=true; else { //--- calculate P1 (interpolant),compare with P2 error //--- compare RMS errors t=2*CMath::RandomReal()-1; v1=CRatInt::BarycentricCalc(p1,t); v2=CRatInt::BarycentricCalc(p2,t); //--- search errors fiterrors=fiterrors || v2!=v1; fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror; fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror; fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror; fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror; } } } //--- calculation for(m=1;m<=maxn;m++) { for(pass=1;pass<=passcount;pass++) { //--- check if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!")) return; //--- solve simple task (all X[] are the same,Y[] are specially //--- calculated to ensure simple form of all types of errors) //--- and check correctness of the errors calculated by subroutines //--- First pass is done with zero Y[],other passes - with random Y[]. //--- It should test both ability to correctly calculate errors and //--- ability to not fail while working with zeros :) n=4*maxn; //--- check if(pass==1) { v1=0; v2=0; v=0; } else { v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); } //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); //--- change values for(i=0;i<=maxn-1;i++) { x[4*i+0]=i; y[4*i+0]=v-v2; w[4*i+0]=1; x[4*i+1]=i; y[4*i+1]=v-v1; w[4*i+1]=1; x[4*i+2]=i; y[4*i+2]=v+v1; w[4*i+2]=1; x[4*i+3]=i; y[4*i+3]=v+v2; w[4*i+3]=1; } //--- change values refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; //--- check if(pass==1) refavgrel=0; else refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test errors correctness CLSFit::PolynomialFit(x,y,n,m,info,p,rep); //--- check if(info<=0) fiterrors=true; else { s=CRatInt::BarycentricCalc(p,0); //--- search errors fiterrors=fiterrors || MathAbs(s-v)>threshold; fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold; } } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestLSFitUnit::TestRationalFitting(bool &fiterrors) { //--- create variables double threshold=0; int maxn=0; int passcount=0; int n=0; int m=0; int i=0; int k=0; int pass=0; double t=0; double s=0; double v=0; double v0=0; double v1=0; double v2=0; int info=0; int info2=0; double xmin=0; double xmax=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; //--- create arrays double x[]; double x2[]; double y[]; double y2[]; double w[]; double w2[]; double xc[]; double yc[]; int dc[]; //--- objects of classes CBarycentricInterpolant b1; CBarycentricInterpolant b2; CBarycentricFitReport rep; CBarycentricFitReport rep2; //--- initialization fiterrors=false; //--- PassCount number of repeated passes //--- Threshold error tolerance //--- LipschitzTol Lipschitz constant increase allowed //--- when calculating constant on a twice denser grid passcount=5; maxn=15; threshold=1000000*CMath::m_machineepsilon; //--- Test rational fitting: for(pass=1;pass<=passcount;pass++) { for(n=2;n<=maxn;n++) { //--- N=M+K fitting (i.e. interpolation) for(k=0;k<=n-1;k++) { //--- allocation ArrayResize(x,n-k); ArrayResize(y,n-k); ArrayResize(w,n-k); //--- check if(k>0) { //--- allocation ArrayResize(xc,k); ArrayResize(yc,k); ArrayResize(dc,k); } //--- change values for(i=0;i<=n-k-1;i++) { x[i]=(double)i/(double)(n-1); y[i]=2*CMath::RandomReal()-1; w[i]=1+CMath::RandomReal(); } for(i=0;i<=k-1;i++) { xc[i]=(double)(n-k+i)/(double)(n-1); yc[i]=2*CMath::RandomReal()-1; dc[i]=0; } //--- function call CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n-k,xc,yc,dc,k,n,info,b1,rep); //--- check if(info<=0) fiterrors=true; else { //--- search errors for(i=0;i<=n-k-1;i++) fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold; for(i=0;i<=k-1;i++) fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(b1,xc[i])-yc[i])>threshold; } } //--- Testing constraints on derivatives: //--- * several M's are tried //--- * several K's are tried - 1,2. //--- * constraints at the ends of the interval for(m=3;m<=5;m++) { for(k=1;k<=2;k++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(xc,2); ArrayResize(yc,2); ArrayResize(dc,2); for(i=0;i<=n-1;i++) { x[i]=2*CMath::RandomReal()-1; y[i]=2*CMath::RandomReal()-1; w[i]=1+CMath::RandomReal(); } //--- change values xc[0]=-1; yc[0]=2*CMath::RandomReal()-1; dc[0]=0; xc[1]=1; yc[1]=2*CMath::RandomReal()-1; dc[1]=0; //--- function call CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,k,m,info,b1,rep); //--- check if(info<=0) fiterrors=true; else { for(i=0;i<=k-1;i++) { CRatInt::BarycentricDiff1(b1,xc[i],v0,v1); //--- search errors fiterrors=fiterrors || MathAbs(v0-yc[i])>threshold; } } } } } } //--- calculation for(m=2;m<=8;m++) { for(pass=1;pass<=passcount;pass++) { //--- General fitting //--- interpolating function through M nodes should have //--- greater RMS error than fitting it through the same M nodes n=100; ArrayResize(x2,n); ArrayResize(y2,n); ArrayResize(w2,n); //--- change values xmin=CMath::m_maxrealnumber; xmax=-CMath::m_maxrealnumber; for(i=0;i<=n-1;i++) { x2[i]=2*M_PI*CMath::RandomReal(); y2[i]=MathSin(x2[i]); w2[i]=1; xmin=MathMin(xmin,x2[i]); xmax=MathMax(xmax,x2[i]); } //--- allocation ArrayResize(x,m); ArrayResize(y,m); for(i=0;i<=m-1;i++) { x[i]=xmin+(xmax-xmin)*i/(m-1); y[i]=MathSin(x[i]); } //--- function call CRatInt::BarycentricBuildFloaterHormann(x,y,m,3,b1); CLSFit::BarycentricFitFloaterHormannWC(x2,y2,w2,n,xc,yc,dc,0,m,info,b2,rep); //--- check if(info<=0) fiterrors=true; else { //--- calculate B1 (interpolant) RMS error,compare with B2 error v1=0; v2=0; for(i=0;i<=n-1;i++) { v1=v1+CMath::Sqr(CRatInt::BarycentricCalc(b1,x2[i])-y2[i]); v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(b2,x2[i])-y2[i]); } v1=MathSqrt(v1/n); v2=MathSqrt(v2/n); //--- search errors fiterrors=fiterrors || v2>v1; fiterrors=fiterrors || MathAbs(v2-rep.m_rmserror)>threshold; } //--- compare weighted and non-weighted n=20; ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); //--- change values for(i=0;i<=n-1;i++) { x[i]=2*CMath::RandomReal()-1; y[i]=2*CMath::RandomReal()-1; w[i]=1; } //--- function calls CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,0,m,info,b1,rep); CLSFit::BarycentricFitFloaterHormann(x,y,n,m,info2,b2,rep2); //--- check if(info<=0 || info2<=0) fiterrors=true; else { //--- calculate B1 (interpolant),compare with B2 //--- compare RMS errors t=2*CMath::RandomReal()-1; v1=CRatInt::BarycentricCalc(b1,t); v2=CRatInt::BarycentricCalc(b2,t); //--- search errors fiterrors=fiterrors || v2!=v1; fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror; fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror; fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror; fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror; } } } //--- calculation for(pass=1;pass<=passcount;pass++) { //--- check if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!")) return; //--- solve simple task (all X[] are the same,Y[] are specially //--- calculated to ensure simple form of all types of errors) //--- and check correctness of the errors calculated by subroutines //--- First pass is done with zero Y[],other passes - with random Y[]. //--- It should test both ability to correctly calculate errors and //--- ability to not fail while working with zeros :) n=4; if(pass==1) { v1=0; v2=0; v=0; } else { v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); } //--- allocation ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); //--- change values x[0]=0; y[0]=v-v2; w[0]=1; x[1]=0; y[1]=v-v1; w[1]=1; x[2]=0; y[2]=v+v1; w[2]=1; x[3]=0; y[3]=v+v2; w[3]=1; refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; //--- check if(pass==1) refavgrel=0; else refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test errors correctness CLSFit::BarycentricFitFloaterHormann(x,y,4,2,info,b1,rep); //--- check if(info<=0) fiterrors=true; else { s=CRatInt::BarycentricCalc(b1,0); //--- search errors fiterrors=fiterrors || MathAbs(s-v)>threshold; fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold; } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestLSFitUnit::TestSplineFitting(bool &fiterrors) { //--- create variables double threshold=0; double nonstrictthreshold=0; int passcount=0; int n=0; int m=0; int i=0; int k=0; int pass=0; double sa=0; double sb=0; int info=0; int info1=0; int info2=0; double s=0; double ds=0; double d2s=0; int stype=0; double t=0; double v=0; double v1=0; double v2=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; double rho=0; //--- create arrays double x[]; double y[]; double w[]; double w2[]; double xc[]; double yc[]; double d[]; int dc[]; //--- objects of classes êëàññîâ CSpline1DInterpolant c; CSpline1DInterpolant c2; CSpline1DFitReport rep; CSpline1DFitReport rep2; //--- Valyes: //--- * pass count //--- * threshold - for tests which must be satisfied exactly //--- * nonstrictthreshold - for approximate tests passcount=20; threshold=10000*CMath::m_machineepsilon; nonstrictthreshold=1.0E-4; fiterrors=false; //--- Test fitting by Cubic and Hermite splines (obsolete,but still supported) for(pass=1;pass<=passcount;pass++) { //--- Cubic splines //--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx). for(m=4;m<=8;m++) { for(k=1;k<=4;k++) { //--- check if(k>=m) continue; n=100; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(xc,4); ArrayResize(yc,4); ArrayResize(dc,4); //--- change values sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { x[i]=sa*CMath::RandomReal()+sb; y[i]=2*CMath::RandomReal()-1; w[i]=1+CMath::RandomReal(); } xc[0]=sb; yc[0]=2*CMath::RandomReal()-1; dc[0]=0; xc[1]=sb; yc[1]=2*CMath::RandomReal()-1; dc[1]=1; xc[2]=sa+sb; yc[2]=2*CMath::RandomReal()-1; dc[2]=0; xc[3]=sa+sb; yc[3]=2*CMath::RandomReal()-1; dc[3]=1; //--- function call CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep); //--- check if(info<=0) fiterrors=true; else { //--- Check that constraints are satisfied for(i=0;i<=k-1;i++) { CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s); //--- check if(dc[i]==0) fiterrors=fiterrors || MathAbs(s-yc[i])>threshold; //--- check if(dc[i]==1) fiterrors=fiterrors || MathAbs(ds-yc[i])>threshold; //--- check if(dc[i]==2) fiterrors=fiterrors || MathAbs(d2s-yc[i])>threshold; } } } } //--- Cubic splines //--- Ability to handle one internal constraint for(m=4;m<=8;m++) { n=100; ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(xc,1); ArrayResize(yc,1); ArrayResize(dc,1); //--- change values sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { x[i]=sa*CMath::RandomReal()+sb; y[i]=2*CMath::RandomReal()-1; w[i]=1+CMath::RandomReal(); } xc[0]=sa*CMath::RandomReal()+sb; yc[0]=2*CMath::RandomReal()-1; dc[0]=CMath::RandomInteger(2); //--- function call CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep); //--- check if(info<=0) fiterrors=true; else { //--- Check that constraints are satisfied CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s); //--- check if(dc[0]==0) fiterrors=fiterrors || MathAbs(s-yc[0])>threshold; //--- check if(dc[0]==1) fiterrors=fiterrors || MathAbs(ds-yc[0])>threshold; //--- check if(dc[0]==2) fiterrors=fiterrors || MathAbs(d2s-yc[0])>threshold; } } //--- Hermite splines //--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx). for(m=4;m<=8;m++) { for(k=1;k<=4;k++) { //--- check if(k>=m) continue; //--- check if(m%2!=0) continue; n=100; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(xc,4); ArrayResize(yc,4); ArrayResize(dc,4); //--- change values sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { x[i]=sa*CMath::RandomReal()+sb; y[i]=2*CMath::RandomReal()-1; w[i]=1+CMath::RandomReal(); } xc[0]=sb; yc[0]=2*CMath::RandomReal()-1; dc[0]=0; xc[1]=sb; yc[1]=2*CMath::RandomReal()-1; dc[1]=1; xc[2]=sa+sb; yc[2]=2*CMath::RandomReal()-1; dc[2]=0; xc[3]=sa+sb; yc[3]=2*CMath::RandomReal()-1; dc[3]=1; //--- function call CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep); //--- check if(info<=0) fiterrors=true; else { //--- Check that constraints are satisfied for(i=0;i<=k-1;i++) { CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s); //--- check if(dc[i]==0) fiterrors=fiterrors || MathAbs(s-yc[i])>threshold; //--- check if(dc[i]==1) fiterrors=fiterrors || MathAbs(ds-yc[i])>threshold; //--- check if(dc[i]==2) fiterrors=fiterrors || MathAbs(d2s-yc[i])>threshold; } } } } //--- Hermite splines //--- Ability to handle one internal constraint for(m=4;m<=8;m++) { //--- check if(m%2!=0) continue; n=100; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(xc,1); ArrayResize(yc,1); ArrayResize(dc,1); //--- change values sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; for(i=0;i<=n-1;i++) { x[i]=sa*CMath::RandomReal()+sb; y[i]=2*CMath::RandomReal()-1; w[i]=1+CMath::RandomReal(); } xc[0]=sa*CMath::RandomReal()+sb; yc[0]=2*CMath::RandomReal()-1; dc[0]=CMath::RandomInteger(2); //--- function call CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep); //--- check if(info<=0) fiterrors=true; else { //--- Check that constraints are satisfied CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s); //--- check if(dc[0]==0) fiterrors=fiterrors || MathAbs(s-yc[0])>threshold; //--- check if(dc[0]==1) fiterrors=fiterrors || MathAbs(ds-yc[0])>threshold; //--- check if(dc[0]==2) fiterrors=fiterrors || MathAbs(d2s-yc[0])>threshold; } } } //--- calculation for(m=4;m<=8;m++) { for(stype=0;stype<=1;stype++) { for(pass=1;pass<=passcount;pass++) { //--- check if(stype==1 && m%2!=0) continue; //--- cubic/Hermite spline fitting: //--- * generate "template spline" C2 //--- * generate 2*N points from C2,such that result of //--- ideal fit should be equal to C2 //--- * fit,store in C //--- * compare C and C2 sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; //--- check if(stype==0) { //--- allocation ArrayResize(x,m-2); ArrayResize(y,m-2); //--- change values for(i=0;i<=m-2-1;i++) { x[i]=sa*i/(m-2-1)+sb; y[i]=2*CMath::RandomReal()-1; } //--- function call CSpline1D::Spline1DBuildCubic(x,y,m-2,1,2*CMath::RandomReal()-1,1,2*CMath::RandomReal()-1,c2); } //--- check if(stype==1) { //--- allocation ArrayResize(x,m/2); ArrayResize(y,m/2); ArrayResize(d,m/2); //--- change values for(i=0;i<=m/2-1;i++) { x[i]=sa*i/(m/2-1)+sb; y[i]=2*CMath::RandomReal()-1; d[i]=2*CMath::RandomReal()-1; } //--- function call CSpline1D::Spline1DBuildHermite(x,y,d,m/2,c2); } n=50; //--- allocation ArrayResize(x,2*n); ArrayResize(y,2*n); ArrayResize(w,2*n); //--- calculation for(i=0;i<=n-1;i++) { //--- "if i=0" and "if i=1" are needed to //--- synchronize interval size for C2 and //--- spline being fitted (i.e. C). t=CMath::RandomReal(); x[i]=sa*CMath::RandomReal()+sb; //--- check if(i==0) x[i]=sb; //--- check if(i==1) x[i]=sa+sb; //--- change values v=CSpline1D::Spline1DCalc(c2,x[i]); y[i]=v+t; w[i]=1+CMath::RandomReal(); x[n+i]=x[i]; y[n+i]=v-t; w[n+i]=w[i]; } //--- check if(stype==0) CLSFit::Spline1DFitCubicWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep); //--- check if(stype==1) CLSFit::Spline1DFitHermiteWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep); //--- check if(info<=0) fiterrors=true; else { for(i=0;i<=n-1;i++) { v=sa*CMath::RandomReal()+sb; //--- search errors fiterrors=fiterrors || MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c2,v))>threshold; } } } } } //--- calculation for(m=4;m<=8;m++) { for(pass=1;pass<=passcount;pass++) { //--- prepare points/weights sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; n=10+CMath::RandomInteger(10); //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); for(i=0;i<=n-1;i++) { x[i]=sa*CMath::RandomReal()+sb; y[i]=2*CMath::RandomReal()-1; w[i]=1; } //--- Fit cubic with unity weights,without weights,then compare if(m>=4) { //--- function calls CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep); CLSFit::Spline1DFitCubic(x,y,n,m,info2,c2,rep2); //--- check if(info1<=0 || info2<=0) fiterrors=true; else { for(i=0;i<=n-1;i++) { v=sa*CMath::RandomReal()+sb; //--- search errors fiterrors=fiterrors || CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v); fiterrors=fiterrors || rep.m_taskrcond!=rep2.m_taskrcond; fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror; fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror; fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror; fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror; } } } //--- Fit Hermite with unity weights,without weights,then compare if(m>=4 && m%2==0) { CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep); CLSFit::Spline1DFitHermite(x,y,n,m,info2,c2,rep2); //--- check if(info1<=0 || info2<=0) fiterrors=true; else { for(i=0;i<=n-1;i++) { v=sa*CMath::RandomReal()+sb; //--- search errors fiterrors=fiterrors || CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v); fiterrors=fiterrors || rep.m_taskrcond!=rep2.m_taskrcond; fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror; fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror; fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror; fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror; } } } } } //--- check basic properties of penalized splines which are //--- preserved independently of Rho parameter. for(m=4;m<=10;m++) { for(k=-5;k<=5;k++) { rho=k; //--- when we have two points (even with different weights), //--- resulting spline must be equal to the straight line ArrayResize(x,2); ArrayResize(y,2); ArrayResize(w,2); x[0]=-0.5-CMath::RandomReal(); y[0]=0.5+CMath::RandomReal(); w[0]=1+CMath::RandomReal(); x[1]=0.5+CMath::RandomReal(); y[1]=0.5+CMath::RandomReal(); w[1]=1+CMath::RandomReal(); //--- function call CLSFit::Spline1DFitPenalized(x,y,2,m,rho,info,c,rep); //--- check if(info>0) { v=2*CMath::RandomReal()-1; v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0]; //--- search errors fiterrors=fiterrors || MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold; } else fiterrors=true; //--- function call CLSFit::Spline1DFitPenalizedW(x,y,w,2,m,rho,info,c,rep); //--- check if(info>0) { v=2*CMath::RandomReal()-1; v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0]; //--- search errors fiterrors=fiterrors || MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold; } else fiterrors=true; //--- spline fitting is invariant with respect to //--- scaling of weights (of course,ANY fitting algorithm //--- must be invariant,but we want to test this property //--- just to be sure that it is correctly implemented) for(n=2;n<=2*m;n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(w2,n); //--- change values s=1+MathExp(10*CMath::RandomReal()); for(i=0;i<=n-1;i++) { x[i]=(double)i/(double)(n-1); y[i]=CMath::RandomReal(); w[i]=0.1+CMath::RandomReal(); w2[i]=w[i]*s; } //--- function calls CLSFit::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep); CLSFit::Spline1DFitPenalizedW(x,y,w2,n,m,rho,info2,c2,rep2); //--- check if(info>0 && info2>0) { v=CMath::RandomReal(); v1=CSpline1D::Spline1DCalc(c,v); v2=CSpline1D::Spline1DCalc(c2,v); //--- search errors fiterrors=fiterrors || MathAbs(v1-v2)>nonstrictthreshold; } else fiterrors=true; } } } //--- Advanced proprties: //--- * penalized spline with M about 5*N and sufficiently small Rho //--- must pass through all points on equidistant grid for(n=2;n<=10;n++) { m=5*n; rho=-5; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); for(i=0;i<=n-1;i++) { x[i]=(double)i/(double)(n-1); y[i]=CMath::RandomReal(); w[i]=0.1+CMath::RandomReal(); } //--- function call CLSFit::Spline1DFitPenalized(x,y,n,m,rho,info,c,rep); //--- check if(info>0) { //--- search errors for(i=0;i<=n-1;i++) fiterrors=fiterrors || MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold; } else fiterrors=true; //--- function call CLSFit::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep); //--- check if(info>0) { //--- search errors for(i=0;i<=n-1;i++) fiterrors=fiterrors || MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold; } else fiterrors=true; } //--- Check correctness of error reports for(pass=1;pass<=passcount;pass++) { //--- check if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!")) return; //--- solve simple task (all X[] are the same,Y[] are specially //--- calculated to ensure simple form of all types of errors) //--- and check correctness of the errors calculated by subroutines //--- First pass is done with zero Y[],other passes - with random Y[]. //--- It should test both ability to correctly calculate errors and //--- ability to not fail while working with zeros :) n=4; if(pass==1) { v1=0; v2=0; v=0; } else { v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); } //--- allocation ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); //--- change values x[0]=0; y[0]=v-v2; w[0]=1; x[1]=0; y[1]=v-v1; w[1]=1; x[2]=0; y[2]=v+v1; w[2]=1; x[3]=0; y[3]=v+v2; w[3]=1; refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; //--- check if(pass==1) refavgrel=0; else refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test penalized spline CLSFit::Spline1DFitPenalizedW(x,y,w,4,4,0.0,info,c,rep); //--- check if(info<=0) fiterrors=true; else { s=CSpline1D::Spline1DCalc(c,0); //--- search errors fiterrors=fiterrors || MathAbs(s-v)>threshold; fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold; } //--- Test cubic fitting CLSFit::Spline1DFitCubic(x,y,4,4,info,c,rep); //--- check if(info<=0) fiterrors=true; else { s=CSpline1D::Spline1DCalc(c,0); //--- search errors fiterrors=fiterrors || MathAbs(s-v)>threshold; fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold; } //--- Test Hermite fitting CLSFit::Spline1DFitHermite(x,y,4,4,info,c,rep); //--- check if(info<=0) fiterrors=true; else { s=CSpline1D::Spline1DCalc(c,0); //--- search errors fiterrors=fiterrors || MathAbs(s-v)>threshold; fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold; } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestLSFitUnit::TestGeneralFitting(bool &llserrors,bool &nlserrors) { //--- create variables double threshold=0; double nlthreshold=0; int maxn=0; int maxm=0; int passcount=0; int n=0; int m=0; int i=0; int j=0; int k=0; int pass=0; double xscale=0; double diffstep=0; double v=0; double v1=0; double v2=0; int info=0; int info2=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; //--- create arrays double x[]; double y[]; double w[]; double w2[]; double c[]; double c2[]; //--- create matrix CMatrixDouble a; CMatrixDouble a2; CMatrixDouble cm; //--- objects of classes CLSFitReport rep; CLSFitReport rep2; CLSFitState state; //--- initialization llserrors=false; nlserrors=false; threshold=10000*CMath::m_machineepsilon; nlthreshold=0.00001; diffstep=0.0001; maxn=6; maxm=6; passcount=4; //--- Testing unconstrained least squares (linear/nonlinear) for(n=1;n<=maxn;n++) { for(m=1;m<=maxm;m++) { for(pass=1;pass<=passcount;pass++) { //--- Solve non-degenerate linear least squares task //--- Use Chebyshev basis. Its condition number is very good. a.Resize(n,m); ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); xscale=0.9+0.1*CMath::RandomReal(); for(i=0;i<=n-1;i++) { //--- check if(n==1) x[i]=2*CMath::RandomReal()-1; else x[i]=xscale*((double)(2*i)/(double)(n-1)-1); //--- change values y[i]=3*x[i]+MathExp(x[i]); w[i]=1+CMath::RandomReal(); a[i].Set(0,1); //--- check if(m>1) a[i].Set(1,x[i]); for(j=2;j<=m-1;j++) a[i].Set(j,2*x[i]*a[i][j-1]-a[i][j-2]); } //--- 1. test weighted fitting (optimality) //--- 2. Solve degenerate least squares task built on the basis //--- of previous task CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep); //--- check if(info<=0) llserrors=true; else llserrors=llserrors || !IsGLSSolution(n,m,0,y,w,a,cm,c); //--- allocation a2.Resize(n,2*m); for(i=0;i<=n-1;i++) { for(j=0;j<=m-1;j++) { a2[i].Set(2*j+0,a[i][j]); a2[i].Set(2*j+1,a[i][j]); } } //--- function call CLSFit::LSFitLinearW(y,w,a2,n,2*m,info,c2,rep); //--- check if(info<=0) llserrors=true; else { //--- test answer correctness using design matrix properties //--- and previous task solution for(j=0;j<=m-1;j++) llserrors=llserrors || MathAbs(c2[2*j+0]+c2[2*j+1]-c[j])>threshold; } //--- test non-weighted fitting ArrayResize(w2,n); for(i=0;i<=n-1;i++) w2[i]=1; //--- function calls CLSFit::LSFitLinearW(y,w2,a,n,m,info,c,rep); CLSFit::LSFitLinear(y,a,n,m,info2,c2,rep2); //--- check if(info<=0 || info2<=0) llserrors=true; else { //--- test answer correctness for(j=0;j<=m-1;j++) llserrors=llserrors || MathAbs(c[j]-c2[j])>threshold; //--- search errors llserrors=llserrors || MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold; } //--- test nonlinear fitting on the linear task //--- (only non-degenerate tasks are tested) //--- and compare with answer from linear fitting subroutine if(n>=m) { //--- allocation ArrayResize(c2,m); //--- test function/gradient/Hessian-based weighted fitting CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep); for(i=0;i<=m-1;i++) c2[i]=2*CMath::RandomReal()-1; //--- function calls CLSFit::LSFitCreateWF(a,y,w,c2,n,m,m,diffstep,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); FitLinearNonlinear(m,0,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0;i<=m-1;i++) nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold; } //--- change values for(i=0;i<=m-1;i++) c2[i]=2*CMath::RandomReal()-1; //--- function calls CLSFit::LSFitCreateWFG(a,y,w,c2,n,m,m,CMath::RandomReal()>0.5,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); FitLinearNonlinear(m,1,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0;i<=m-1;i++) nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold; } //--- change values for(i=0;i<=m-1;i++) c2[i]=2*CMath::RandomReal()-1; //--- function calls CLSFit::LSFitCreateWFGH(a,y,w,c2,n,m,m,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); FitLinearNonlinear(m,2,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0;i<=m-1;i++) nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold; } //--- test gradient-only or Hessian-based fitting without weights CLSFit::LSFitLinear(y,a,n,m,info,c,rep); for(i=0;i<=m-1;i++) c2[i]=2*CMath::RandomReal()-1; //--- function calls CLSFit::LSFitCreateF(a,y,c2,n,m,m,diffstep,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); FitLinearNonlinear(m,0,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0;i<=m-1;i++) nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold; } //--- change values for(i=0;i<=m-1;i++) c2[i]=2*CMath::RandomReal()-1; //--- function calls CLSFit::LSFitCreateFG(a,y,c2,n,m,m,CMath::RandomReal()>0.5,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); FitLinearNonlinear(m,1,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0;i<=m-1;i++) nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold; } //--- change values for(i=0;i<=m-1;i++) c2[i]=2*CMath::RandomReal()-1; //--- function calls CLSFit::LSFitCreateFGH(a,y,c2,n,m,m,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); FitLinearNonlinear(m,2,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0;i<=m-1;i++) nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold; } } } } //--- test correctness of the RCond field a.Resize(n,n); ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); //--- change values v1=CMath::m_maxrealnumber; v2=CMath::m_minrealnumber; //--- calculation for(i=0;i<=n-1;i++) { x[i]=0.1+0.9*CMath::RandomReal(); y[i]=0.1+0.9*CMath::RandomReal(); w[i]=1; for(j=0;j<=n-1;j++) { //--- check if(i==j) { a[i].Set(i,0.1+0.9*CMath::RandomReal()); v1=MathMin(v1,a[i][i]); v2=MathMax(v2,a[i][i]); } else a[i].Set(j,0); } } //--- function call CLSFit::LSFitLinearW(y,w,a,n,n,info,c,rep); //--- check if(info<=0) llserrors=true; else llserrors=llserrors || MathAbs(rep.m_taskrcond-v1/v2)>threshold; } //--- Test constrained least squares for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { for(m=1;m<=maxm;m++) { //--- test for K<>0 for(k=1;k<=m-1;k++) { //--- Prepare Chebyshev basis. Its condition number is very good. //--- Prepare constraints (random numbers) a.Resize(n,m); ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); xscale=0.9+0.1*CMath::RandomReal(); //--- calculation for(i=0;i<=n-1;i++) { //--- check if(n==1) x[i]=2*CMath::RandomReal()-1; else x[i]=xscale*((double)(2*i)/(double)(n-1)-1); //--- change values y[i]=3*x[i]+MathExp(x[i]); w[i]=1+CMath::RandomReal(); a[i].Set(0,1); //--- check if(m>1) a[i].Set(1,x[i]); for(j=2;j<=m-1;j++) a[i].Set(j,2*x[i]*a[i][j-1]-a[i][j-2]); } //--- allocation cm.Resize(k,m+1); for(i=0;i<=k-1;i++) { for(j=0;j<=m;j++) cm[i].Set(j,2*CMath::RandomReal()-1); } //--- Solve constrained task CLSFit::LSFitLinearWC(y,w,a,cm,n,m,k,info,c,rep); //--- check if(info<=0) llserrors=true; else llserrors=llserrors || !IsGLSSolution(n,m,k,y,w,a,cm,c); //--- test non-weighted fitting ArrayResize(w2,n); for(i=0;i<=n-1;i++) w2[i]=1; //--- function calls CLSFit::LSFitLinearWC(y,w2,a,cm,n,m,k,info,c,rep); CLSFit::LSFitLinearC(y,a,cm,n,m,k,info2,c2,rep2); //--- check if(info<=0 || info2<=0) llserrors=true; else { //--- test answer correctness for(j=0;j<=m-1;j++) llserrors=llserrors || MathAbs(c[j]-c2[j])>threshold; llserrors=llserrors || MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold; } } } } } //--- nonlinear task for nonlinear fitting: //--- f(X,C)=1/(1+C*X^2), //--- C(true)=2. n=100; ArrayResize(c,1); c[0]=1+2*CMath::RandomReal(); //--- allocation a.Resize(n,1); ArrayResize(y,n); for(i=0;i<=n-1;i++) { a[i].Set(0,4*CMath::RandomReal()-2); y[i]=1/(1+2*CMath::Sqr(a[i][0])); } //--- function call CLSFit::LSFitCreateFG(a,y,c,n,1,1,true,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); //--- cycle while(CLSFit::LSFitIteration(state)) { //--- check if(state.m_needf) state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0])); //--- check if(state.m_needfg) { state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0])); state.m_g[0]=-(CMath::Sqr(state.m_x[0])/CMath::Sqr(1+state.m_c[0]*CMath::Sqr(state.m_x[0]))); } } //--- function call CLSFit::LSFitResults(state,info,c,rep); //--- check if(info<=0) nlserrors=true; else nlserrors=nlserrors || MathAbs(c[0]-2)>100*nlthreshold; //--- solve simple task (fitting by constant function) and check //--- correctness of the errors calculated by subroutines for(pass=1;pass<=passcount;pass++) { //--- test on task with non-zero Yi n=4; v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); //--- allocation ArrayResize(c,1); c[0]=1+2*CMath::RandomReal(); //--- allocation a.Resize(4,1); ArrayResize(y,4); //--- change values a[0].Set(0,1); y[0]=v-v2; a[1].Set(0,1); y[1]=v-v1; a[2].Set(0,1); y[2]=v+v1; a[3].Set(0,1); y[3]=v+v2; refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test LLS CLSFit::LSFitLinear(y,a,4,1,info,c,rep); //--- check if(info<=0) llserrors=true; else { //--- search errors llserrors=llserrors || MathAbs(c[0]-v)>threshold; llserrors=llserrors || MathAbs(rep.m_rmserror-refrms)>threshold; llserrors=llserrors || MathAbs(rep.m_avgerror-refavg)>threshold; llserrors=llserrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; llserrors=llserrors || MathAbs(rep.m_maxerror-refmax)>threshold; } //--- Test NLS CLSFit::LSFitCreateFG(a,y,c,4,1,1,true,state); CLSFit::LSFitSetCond(state,0.0,nlthreshold,0); //--- cycle while(CLSFit::LSFitIteration(state)) { //--- check if(state.m_needf) state.m_f=state.m_c[0]; //--- check if(state.m_needfg) { state.m_f=state.m_c[0]; state.m_g[0]=1; } } //--- function call CLSFit::LSFitResults(state,info,c,rep); //--- check if(info<=0) nlserrors=true; else { //--- search errors nlserrors=nlserrors || MathAbs(c[0]-v)>threshold; nlserrors=nlserrors || MathAbs(rep.m_rmserror-refrms)>threshold; nlserrors=nlserrors || MathAbs(rep.m_avgerror-refavg)>threshold; nlserrors=nlserrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold; nlserrors=nlserrors || MathAbs(rep.m_maxerror-refmax)>threshold; } } } //+------------------------------------------------------------------+ //| Tests whether C is solution of (possibly) constrained LLS problem| //+------------------------------------------------------------------+ static bool CTestLSFitUnit::IsGLSSolution(const int n,const int m,const int k, double &y[],double &w[],CMatrixDouble &fmatrix, CMatrixDouble &cmatrix,double &cc[]) { //--- create variables bool result; int i=0; int j=0; double v=0; double s1=0; double s2=0; double s3=0; double delta=0; double threshold=0; int i_=0; //--- create arrays double c[]; double c2[]; double sv[]; double deltac[]; double deltaproj[]; //--- create matrix CMatrixDouble u; CMatrixDouble vt; //--- copy array ArrayCopy(c,cc); //--- Setup. //--- Threshold is small because CMatrix may be ill-conditioned delta=0.001; threshold=MathSqrt(CMath::m_machineepsilon); //--- allocation ArrayResize(c2,m); ArrayResize(deltac,m); ArrayResize(deltaproj,m); //--- test whether C is feasible point or not (projC must be close to C) for(i=0;i<=k-1;i++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=cmatrix[i][i_]*c[i_]; //--- check if(MathAbs(v-cmatrix[i][m])>threshold) { //--- return result return(false); } } //--- find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1) if(k>0) CSingValueDecompose::RMatrixSVD(cmatrix,k,m,0,2,2,sv,u,vt); //--- Test result result=true; s1=GetGLSError(n,m,y,w,fmatrix,c); //--- calculation for(j=0;j<=m-1;j++) { //--- prepare modification of C which leave us in the feasible set. //--- let deltaC be increment on Jth coordinate,then project //--- deltaC in the Null(CMatrix) and store result in DeltaProj for(i_=0;i_<=m-1;i_++) c2[i_]=c[i_]; for(i=0;i<=m-1;i++) { //--- check if(i==j) deltac[i]=delta; else deltac[i]=0; } //--- check if(k==0) { for(i_=0;i_<=m-1;i_++) deltaproj[i_]=deltac[i_]; } else { for(i=0;i<=m-1;i++) deltaproj[i]=0; for(i=k;i<=m-1;i++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=vt[i][i_]*deltac[i_]; for(i_=0;i_<=m-1;i_++) deltaproj[i_]=deltaproj[i_]+v*vt[i][i_]; } } //--- now we have DeltaProj such that if C is feasible, //--- then C+DeltaProj is feasible too for(i_=0;i_<=m-1;i_++) c2[i_]=c[i_]; for(i_=0;i_<=m-1;i_++) c2[i_]=c2[i_]+deltaproj[i_]; s2=GetGLSError(n,m,y,w,fmatrix,c2); for(i_=0;i_<=m-1;i_++) c2[i_]=c[i_]; for(i_=0;i_<=m-1;i_++) c2[i_]=c2[i_]-deltaproj[i_]; s3=GetGLSError(n,m,y,w,fmatrix,c2); result=(result && s2>=(double)(s1/(1+threshold))) && s3>=(double)(s1/(1+threshold)); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests whether C is solution of LLS problem | //+------------------------------------------------------------------+ static double CTestLSFitUnit::GetGLSError(const int n,const int m,double &y[], double &w[],CMatrixDouble &fmatrix,double &c[]) { //--- create variables double result=0; int i=0; double v=0; int i_=0; //--- calculation for(i=0;i<=n-1;i++) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=fmatrix[i][i_]*c[i_]; result=result+CMath::Sqr(w[i]*(v-y[i])); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Subroutine for nonlinear fitting of linear problem | //| DerAvailable: | //| * 0 when only function value should be used | //| * 1 when we can provide gradient/function | //| * 2 when we can provide Hessian/gradient/function | //| When something which is not permitted by DerAvailable is | //| requested, this function sets NLSErrors to True. | //+------------------------------------------------------------------+ static void CTestLSFitUnit::FitLinearNonlinear(const int m,const int deravailable, CMatrixDouble &xy,CLSFitState &state, bool &nlserrors) { //--- create variables int i=0; int j=0; double v=0; int i_=0; //--- cycle while(CLSFit::LSFitIteration(state)) { //--- assume that one and only one of flags is set //--- test that we didn't request hessian in hessian-free setting if(deravailable<1 && state.m_needfg) nlserrors=true; //--- check if(deravailable<2 && state.m_needfgh) nlserrors=true; i=0; //--- check if(state.m_needf) i=i+1; //--- check if(state.m_needfg) i=i+1; //--- check if(state.m_needfgh) i=i+1; //--- check if(i!=1) nlserrors=true; //--- test that PointIndex is consistent with actual point passed for(i=0;i<=m-1;i++) nlserrors=nlserrors || xy[state.m_pointindex][i]!=state.m_x[i]; //--- calculate if(state.m_needf) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=state.m_x[i_]*state.m_c[i_]; state.m_f=v; continue; } //--- check if(state.m_needfg) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=state.m_x[i_]*state.m_c[i_]; state.m_f=v; //--- copy for(i_=0;i_<=m-1;i_++) state.m_g[i_]=state.m_x[i_]; continue; } //--- check if(state.m_needfgh) { //--- change value v=0.0; for(i_=0;i_<=m-1;i_++) v+=state.m_x[i_]*state.m_c[i_]; state.m_f=v; //--- copy for(i_=0;i_<=m-1;i_++) state.m_g[i_]=state.m_x[i_]; for(i=0;i<=m-1;i++) { for(j=0;j<=m-1;j++) state.m_h[i].Set(j,0); } continue; } } } //+------------------------------------------------------------------+ //| Testing class CPSpline | //+------------------------------------------------------------------+ class CTestPSplineUnit { private: //--- private methods static void UnsetP2(CPSpline2Interpolant &p); static void UnsetP3(CPSpline3Interpolant &p); static void Unset1D(double &x[]); public: //--- constructor, destructor CTestPSplineUnit(void); ~CTestPSplineUnit(void); //--- public method static bool TestPSpline(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestPSplineUnit::CTestPSplineUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestPSplineUnit::~CTestPSplineUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CPSpline | //+------------------------------------------------------------------+ static bool CTestPSplineUnit::TestPSpline(const bool silent) { //--- create variables bool waserrors; bool p2errors; bool p3errors; double nonstrictthreshold=0; double threshold=0; int passcount=0; double lstep=0; double h=0; int maxn=0; int periodicity=0; int skind=0; int pkind=0; bool periodic; double a=0; double b=0; int n=0; int tmpn=0; int i=0; double vx=0; double vy=0; double vz=0; double vx2=0; double vy2=0; double vz2=0; double vdx=0; double vdy=0; double vdz=0; double vdx2=0; double vdy2=0; double vdz2=0; double vd2x=0; double vd2y=0; double vd2z=0; double vd2x2=0; double vd2y2=0; double vd2z2=0; double v0=0; double v1=0; int i_=0; //--- create arrays double x[]; double y[]; double z[]; double t[]; double t2[]; double t3[]; //--- create matrix CMatrixDouble xy; CMatrixDouble xyz; //--- objects of classes CPSpline2Interpolant p2; CPSpline3Interpolant p3; CSpline1DInterpolant s; //--- initialization waserrors=false; passcount=20; lstep=0.005; h=0.00001; maxn=10; threshold=10000*CMath::m_machineepsilon; nonstrictthreshold=0.00001; p2errors=false; p3errors=false; //--- Test basic properties of 2- and 3-dimensional splines: //--- * PSpline2ParameterValues() properties //--- * values at nodes //--- * for periodic splines - periodicity properties //--- Variables used: //--- * N points count //--- * SKind spline //--- * PKind parameterization //--- * Periodicity whether we have periodic spline or not for(n=2;n<=maxn;n++) { for(skind=0;skind<=2;skind++) { for(pkind=0;pkind<=2;pkind++) { for(periodicity=0;periodicity<=1;periodicity++) { periodic=periodicity==1; //--- skip unsupported combinations of parameters if(periodic && n<3) continue; //--- check if(periodic && skind==0) continue; //--- check if(n<5 && skind==0) continue; //--- init xy.Resize(n,2); xyz.Resize(n,3); //--- function call CApServ::TaskGenInt1DEquidist(-1,1,n,t2,x); //--- change values for(i_=0;i_<=n-1;i_++) xy[i_].Set(0,x[i_]); for(i_=0;i_<=n-1;i_++) xyz[i_].Set(0,x[i_]); //--- function call CApServ::TaskGenInt1DEquidist(-1,1,n,t2,y); //--- change values for(i_=0;i_<=n-1;i_++) xy[i_].Set(1,y[i_]); for(i_=0;i_<=n-1;i_++) xyz[i_].Set(1,y[i_]); //--- function call CApServ::TaskGenInt1DEquidist(-1,1,n,t2,z); //--- change values for(i_=0;i_<=n-1;i_++) xyz[i_].Set(2,z[i_]); //--- function calls UnsetP2(p2); UnsetP3(p3); //--- check if(periodic) { CPSpline::PSpline2BuildPeriodic(xy,n,skind,pkind,p2); CPSpline::PSpline3BuildPeriodic(xyz,n,skind,pkind,p3); } else { CPSpline::PSpline2Build(xy,n,skind,pkind,p2); CPSpline::PSpline3Build(xyz,n,skind,pkind,p3); } //--- PSpline2ParameterValues() properties CPSpline::PSpline2ParameterValues(p2,tmpn,t2); //--- check if(tmpn!=n) { p2errors=true; continue; } //--- function call CPSpline::PSpline3ParameterValues(p3,tmpn,t3); //--- check if(tmpn!=n) { p3errors=true; continue; } //--- search errors p2errors=p2errors || t2[0]!=0.0; p3errors=p3errors || t3[0]!=0.0; for(i=1;i<=n-1;i++) { p2errors=p2errors || t2[i]<=t2[i-1]; p3errors=p3errors || t3[i]<=t3[i-1]; } //--- check if(periodic) { p2errors=p2errors || t2[n-1]>=1.0; p3errors=p3errors || t3[n-1]>=1.0; } else { p2errors=p2errors || t2[n-1]!=1.0; p3errors=p3errors || t3[n-1]!=1.0; } //--- Now we have parameter values stored at T, //--- and want to test whether the actully correspond to //--- points for(i=0;i<=n-1;i++) { //--- 2-dimensional test CPSpline::PSpline2Calc(p2,t2[i],vx,vy); p2errors=p2errors || MathAbs(vx-x[i])>threshold; p2errors=p2errors || MathAbs(vy-y[i])>threshold; //--- 3-dimensional test CPSpline::PSpline3Calc(p3,t3[i],vx,vy,vz); p3errors=p3errors || MathAbs(vx-x[i])>threshold; p3errors=p3errors || MathAbs(vy-y[i])>threshold; p3errors=p3errors || MathAbs(vz-z[i])>threshold; } //--- Test periodicity (if needed) if(periodic) { //--- periodicity at nodes for(i=0;i<=n-1;i++) { //--- 2-dimensional test CPSpline::PSpline2Calc(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vy); //--- search errors p2errors=p2errors || MathAbs(vx-x[i])>threshold; p2errors=p2errors || MathAbs(vy-y[i])>threshold; //--- function call CPSpline::PSpline2Diff(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy); //--- search errors p2errors=p2errors || MathAbs(vx-x[i])>threshold; p2errors=p2errors || MathAbs(vy-y[i])>threshold; //--- function call CPSpline::PSpline2Diff2(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y); //--- search errors p2errors=p2errors || MathAbs(vx-x[i])>threshold; p2errors=p2errors || MathAbs(vy-y[i])>threshold; //--- 3-dimensional test CPSpline::PSpline3Calc(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vy,vz); //--- search errors p3errors=p3errors || MathAbs(vx-x[i])>threshold; p3errors=p3errors || MathAbs(vy-y[i])>threshold; p3errors=p3errors || MathAbs(vz-z[i])>threshold; //--- function call CPSpline::PSpline3Diff(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy,vz,vdz); //--- search errors p3errors=p3errors || MathAbs(vx-x[i])>threshold; p3errors=p3errors || MathAbs(vy-y[i])>threshold; p3errors=p3errors || MathAbs(vz-z[i])>threshold; //--- function call CPSpline::PSpline3Diff2(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z); //--- search errors p3errors=p3errors || MathAbs(vx-x[i])>threshold; p3errors=p3errors || MathAbs(vy-y[i])>threshold; p3errors=p3errors || MathAbs(vz-z[i])>threshold; } //--- periodicity between nodes v0=CMath::RandomReal(); CPSpline::PSpline2Calc(p2,v0,vx,vy); CPSpline::PSpline2Calc(p2,v0+CMath::RandomInteger(10)-5,vx2,vy2); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; //--- function calls CPSpline::PSpline3Calc(p3,v0,vx,vy,vz); CPSpline::PSpline3Calc(p3,v0+CMath::RandomInteger(10)-5,vx2,vy2,vz2); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; //--- near-boundary test for continuity of function values and derivatives: //--- 2-dimensional curve if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!")) return(false); //--- change values v0=100*CMath::m_machineepsilon; v1=1-v0; //--- function calls CPSpline::PSpline2Calc(p2,v0,vx,vy); CPSpline::PSpline2Calc(p2,v1,vx2,vy2); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; //--- function calls CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy); CPSpline::PSpline2Diff(p2,v1,vx2,vdx2,vy2,vdy2); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; p2errors=p2errors || MathAbs(vdx-vdx2)>nonstrictthreshold; p2errors=p2errors || MathAbs(vdy-vdy2)>nonstrictthreshold; //--- function calls CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y); CPSpline::PSpline2Diff2(p2,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; p2errors=p2errors || MathAbs(vdx-vdx2)>nonstrictthreshold; p2errors=p2errors || MathAbs(vdy-vdy2)>nonstrictthreshold; //--- check if(skind==2) { //--- second derivative test only for cubic splines p2errors=p2errors || MathAbs(vd2x-vd2x2)>nonstrictthreshold; p2errors=p2errors || MathAbs(vd2y-vd2y2)>nonstrictthreshold; } //--- near-boundary test for continuity of function values and derivatives: //--- 3-dimensional curve if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!")) return(false); //--- change values v0=100*CMath::m_machineepsilon; v1=1-v0; //--- function calls CPSpline::PSpline3Calc(p3,v0,vx,vy,vz); CPSpline::PSpline3Calc(p3,v1,vx2,vy2,vz2); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; //--- function calls CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz); CPSpline::PSpline3Diff(p3,v1,vx2,vdx2,vy2,vdy2,vz2,vdz2); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; p3errors=p3errors || MathAbs(vdx-vdx2)>nonstrictthreshold; p3errors=p3errors || MathAbs(vdy-vdy2)>nonstrictthreshold; p3errors=p3errors || MathAbs(vdz-vdz2)>nonstrictthreshold; //--- function calls CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z); CPSpline::PSpline3Diff2(p3,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2,vz2,vdz2,vd2z2); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; p3errors=p3errors || MathAbs(vdx-vdx2)>nonstrictthreshold; p3errors=p3errors || MathAbs(vdy-vdy2)>nonstrictthreshold; p3errors=p3errors || MathAbs(vdz-vdz2)>nonstrictthreshold; //--- check if(skind==2) { //--- second derivative test only for cubic splines p3errors=p3errors || MathAbs(vd2x-vd2x2)>nonstrictthreshold; p3errors=p3errors || MathAbs(vd2y-vd2y2)>nonstrictthreshold; p3errors=p3errors || MathAbs(vd2z-vd2z2)>nonstrictthreshold; } } } } } } //--- Test differentiation,tangents,calculation between nodes. //--- Because differentiation is done in parameterization/spline/periodicity //--- oblivious manner,we don't have to test all possible combinations //--- of spline types and parameterizations. //--- Actually we test special combination with properties which allow us //--- to easily solve this problem: //--- * 2 (3) variables //--- * first variable is sampled from equidistant grid on [0][1] //--- * other variables are random //--- * uniform parameterization is used //--- * periodicity - none //--- * spline type - any (we use cubic splines) //--- Same problem allows us to test calculation BETWEEN nodes. for(n=2;n<=maxn;n++) { //--- init xy.Resize(n,2); xyz.Resize(n,3); //--- function call CApServ::TaskGenInt1DEquidist(0,1,n,t,x); //--- change values for(i_=0;i_<=n-1;i_++) xy[i_].Set(0,x[i_]); for(i_=0;i_<=n-1;i_++) xyz[i_].Set(0,x[i_]); //--- function call CApServ::TaskGenInt1DEquidist(0,1,n,t,y); //--- change values for(i_=0;i_<=n-1;i_++) xy[i_].Set(1,y[i_]); for(i_=0;i_<=n-1;i_++) xyz[i_].Set(1,y[i_]); //--- function call CApServ::TaskGenInt1DEquidist(0,1,n,t,z); //--- change values for(i_=0;i_<=n-1;i_++) xyz[i_].Set(2,z[i_]); //--- function call UnsetP2(p2); UnsetP3(p3); CPSpline::PSpline2Build(xy,n,2,0,p2); CPSpline::PSpline3Build(xyz,n,2,0,p3); //--- Test 2D/3D spline: //--- * build non-parametric cubic spline from T and X/Y //--- * calculate its value and derivatives at V0 //--- * compare with Spline2Calc/Spline2Diff/Spline2Diff2 //--- Because of task properties both variants should //--- return same answer. v0=CMath::RandomReal(); CSpline1D::Spline1DBuildCubic(t,x,n,0,0.0,0,0.0,s); CSpline1D::Spline1DDiff(s,v0,vx2,vdx2,vd2x2); CSpline1D::Spline1DBuildCubic(t,y,n,0,0.0,0,0.0,s); CSpline1D::Spline1DDiff(s,v0,vy2,vdy2,vd2y2); CSpline1D::Spline1DBuildCubic(t,z,n,0,0.0,0,0.0,s); CSpline1D::Spline1DDiff(s,v0,vz2,vdz2,vd2z2); //--- 2D test CPSpline::PSpline2Calc(p2,v0,vx,vy); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; //--- function call CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; p2errors=p2errors || MathAbs(vdx-vdx2)>threshold; p2errors=p2errors || MathAbs(vdy-vdy2)>threshold; //--- function call CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y); //--- search errors p2errors=p2errors || MathAbs(vx-vx2)>threshold; p2errors=p2errors || MathAbs(vy-vy2)>threshold; p2errors=p2errors || MathAbs(vdx-vdx2)>threshold; p2errors=p2errors || MathAbs(vdy-vdy2)>threshold; p2errors=p2errors || MathAbs(vd2x-vd2x2)>threshold; p2errors=p2errors || MathAbs(vd2y-vd2y2)>threshold; //--- 3D test CPSpline::PSpline3Calc(p3,v0,vx,vy,vz); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; //--- function call CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; p3errors=p3errors || MathAbs(vdx-vdx2)>threshold; p3errors=p3errors || MathAbs(vdy-vdy2)>threshold; p3errors=p3errors || MathAbs(vdz-vdz2)>threshold; //--- function call CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z); //--- search errors p3errors=p3errors || MathAbs(vx-vx2)>threshold; p3errors=p3errors || MathAbs(vy-vy2)>threshold; p3errors=p3errors || MathAbs(vz-vz2)>threshold; p3errors=p3errors || MathAbs(vdx-vdx2)>threshold; p3errors=p3errors || MathAbs(vdy-vdy2)>threshold; p3errors=p3errors || MathAbs(vdz-vdz2)>threshold; p3errors=p3errors || MathAbs(vd2x-vd2x2)>threshold; p3errors=p3errors || MathAbs(vd2y-vd2y2)>threshold; p3errors=p3errors || MathAbs(vd2z-vd2z2)>threshold; //--- Test tangents for 2D/3D CPSpline::PSpline2Tangent(p2,v0,vx,vy); //--- search errors p2errors=p2errors || MathAbs(vx-vdx2/CApServ::SafePythag2(vdx2,vdy2))>threshold; p2errors=p2errors || MathAbs(vy-vdy2/CApServ::SafePythag2(vdx2,vdy2))>threshold; //--- function call CPSpline::PSpline3Tangent(p3,v0,vx,vy,vz); //--- search errors p3errors=p3errors || MathAbs(vx-vdx2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold; p3errors=p3errors || MathAbs(vy-vdy2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold; p3errors=p3errors || MathAbs(vz-vdz2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold; } //--- Arc length test. //--- Simple problem with easy solution (points on a straight line with //--- uniform parameterization). for(n=2;n<=maxn;n++) { //--- allocation xy.Resize(n,2); xyz.Resize(n,3); for(i=0;i<=n-1;i++) { xy[i].Set(0,i); xy[i].Set(1,i); xyz[i].Set(0,i); xyz[i].Set(1,i); xyz[i].Set(2,i); } //--- function calls CPSpline::PSpline2Build(xy,n,1,0,p2); CPSpline::PSpline3Build(xyz,n,1,0,p3); a=CMath::RandomReal(); b=CMath::RandomReal(); //--- search errors p2errors=p2errors || MathAbs(CPSpline::PSpline2ArcLength(p2,a,b)-(b-a)*MathSqrt(2)*(n-1))>nonstrictthreshold; p3errors=p3errors || MathAbs(CPSpline::PSpline3ArcLength(p3,a,b)-(b-a)*MathSqrt(3)*(n-1))>nonstrictthreshold; } //--- report waserrors=p2errors || p3errors; //--- check if(!silent) { Print("TESTING SPLINE INTERPOLATION"); //--- Normal tests Print("2D TEST: "); //--- check if(p2errors) Print("FAILED"); else Print("OK"); Print("3D TEST: "); //--- check if(p3errors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Unset spline, i.e. initialize it with random garbage | //+------------------------------------------------------------------+ static void CTestPSplineUnit::UnsetP2(CPSpline2Interpolant &p) { //--- create matrix CMatrixDouble xy; //--- allocation xy.Resize(2,2); //--- initialization xy[0].Set(0,-1); xy[0].Set(1,-1); xy[1].Set(0,1); xy[1].Set(1,1); //--- function call CPSpline::PSpline2Build(xy,2,1,0,p); } //+------------------------------------------------------------------+ //| Unset spline, i.e. initialize it with random garbage | //+------------------------------------------------------------------+ static void CTestPSplineUnit::UnsetP3(CPSpline3Interpolant &p) { //--- create matrix CMatrixDouble xy; //--- allocation xy.Resize(2,3); //--- initialization xy[0].Set(0,-1); xy[0].Set(1,-1); xy[0].Set(2,-1); xy[1].Set(0,1); xy[1].Set(1,1); xy[1].Set(2,1); //--- function call CPSpline::PSpline3Build(xy,2,1,0,p); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ static void CTestPSplineUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Testing class CSpline2D | //+------------------------------------------------------------------+ class CTestSpline2DUnit { public: //--- constructor, destructor CTestSpline2DUnit(void); ~CTestSpline2DUnit(void); //--- public methods static bool TestSpline2D(const bool silent); static void LConst(CSpline2DInterpolant &c,double &lx[],double &ly[],const int m,const int n,const double lstep,double &lc,double &lcx,double &lcy,double &lcxy); static void TwodNumder(CSpline2DInterpolant &c,const double x,const double y,const double h,double &f,double &fx,double &fy,double &fxy); static bool TestUnpack(CSpline2DInterpolant &c,double &lx[],double &ly[]); static bool TestLinTrans(CSpline2DInterpolant &c,const double ax,const double bx,const double ay,const double by); static void UnsetSpline2D(CSpline2DInterpolant &c); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSpline2DUnit::CTestSpline2DUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSpline2DUnit::~CTestSpline2DUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CSpline2D | //+------------------------------------------------------------------+ static bool CTestSpline2DUnit::TestSpline2D(const bool silent) { //--- create variables bool waserrors; bool blerrors; bool bcerrors; bool dserrors; bool cperrors; bool uperrors; bool lterrors; bool syerrors; bool rlerrors; bool rcerrors; int pass=0; int passcount=0; int jobtype=0; double lstep=0; double h=0; double ax=0; double ay=0; double bx=0; double by=0; int i=0; int j=0; int k=0; int n=0; int m=0; int n2=0; int m2=0; double err=0; double t=0; double t1=0; double t2=0; double l1=0; double l1x=0; double l1y=0; double l1xy=0; double l2=0; double l2x=0; double l2y=0; double l2xy=0; double fm=0; double f1=0; double f2=0; double f3=0; double f4=0; double v1=0; double v1x=0; double v1y=0; double v1xy=0; double v2=0; double v2x=0; double v2y=0; double v2xy=0; double mf=0; //--- create arrays double x[]; double y[]; double lx[]; double ly[]; //--- create matrix CMatrixDouble f; CMatrixDouble fr; CMatrixDouble ft; //--- objects of classes CSpline2DInterpolant c; CSpline2DInterpolant c2; //--- initialization waserrors=false; passcount=10; h=0.00001; lstep=0.001; blerrors=false; bcerrors=false; dserrors=false; cperrors=false; uperrors=false; lterrors=false; syerrors=false; rlerrors=false; rcerrors=false; //--- Test: bilinear,bicubic for(n=2;n<=7;n++) { for(m=2;m<=7;m++) { //--- allocation ArrayResize(x,n); ArrayResize(y,m); ArrayResize(lx,2*n-2+1); ArrayResize(ly,2*m-2+1); f.Resize(m,n); //--- calculation ft.Resize(n,m); for(pass=1;pass<=passcount;pass++) { //--- Prepare task: //--- * X and Y stores grid //--- * F stores function values //--- * LX and LY stores twice dense grid (for Lipschitz testing) ax=-1-CMath::RandomReal(); bx=1+CMath::RandomReal(); ay=-1-CMath::RandomReal(); by=1+CMath::RandomReal(); for(j=0;j<=n-1;j++) { x[j]=0.5*(bx+ax)-0.5*(bx-ax)*MathCos(M_PI*(2*j+1)/(2*n)); //--- check if(j==0) x[j]=ax; //--- check if(j==n-1) x[j]=bx; lx[2*j]=x[j]; //--- check if(j>0) lx[2*j-1]=0.5*(x[j]+x[j-1]); } //--- swap for(j=0;j<=n-1;j++) { k=CMath::RandomInteger(n); //--- check if(k!=j) { t=x[j]; x[j]=x[k]; x[k]=t; } } //--- calculation for(i=0;i<=m-1;i++) { y[i]=0.5*(by+ay)-0.5*(by-ay)*MathCos(M_PI*(2*i+1)/(2*m)); //--- check if(i==0) y[i]=ay; //--- check if(i==m-1) y[i]=by; ly[2*i]=y[i]; //--- check if(i>0) ly[2*i-1]=0.5*(y[i]+y[i-1]); } //--- swap for(i=0;i<=m-1;i++) { k=CMath::RandomInteger(m); //--- check if(k!=i) { t=y[i]; y[i]=y[k]; y[k]=t; } } for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) f[i].Set(j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i])); } //--- Test bilinear interpolation: //--- * interpolation at the nodes //--- * linearity //--- * continuity //--- * differentiation in the inner points CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c); //--- search errors err=0; for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) err=MathMax(err,MathAbs(f[i][j]-CSpline2D::Spline2DCalc(c,x[j],y[i]))); } //--- search errors blerrors=blerrors || err>10000*CMath::m_machineepsilon; err=0; for(i=0;i<=m-2;i++) { for(j=0;j<=n-2;j++) { //--- Test for linearity between grid points //--- (test point - geometric center of the cell) fm=CSpline2D::Spline2DCalc(c,lx[2*j+1],ly[2*i+1]); f1=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i]); f2=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i]); f3=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i+2]); f4=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i+2]); //--- search errors err=MathMax(err,MathAbs(0.25*(f1+f2+f3+f4)-fm)); } } //--- search errors blerrors=blerrors || err>10000*CMath::m_machineepsilon; //--- function calls LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy); LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy); //--- search errors blerrors=blerrors || l2/l1>1.2; err=0; for(i=0;i<=m-2;i++) { for(j=0;j<=n-2;j++) { CSpline2D::Spline2DDiff(c,lx[2*j+1],ly[2*i+1],v1,v1x,v1y,v1xy); TwodNumder(c,lx[2*j+1],ly[2*i+1],h,v2,v2x,v2y,v2xy); //--- search errors err=MathMax(err,MathAbs(v1-v2)); err=MathMax(err,MathAbs(v1x-v2x)); err=MathMax(err,MathAbs(v1y-v2y)); err=MathMax(err,MathAbs(v1xy-v2xy)); } } //--- search errors dserrors=dserrors || err>1.0E-3; uperrors=uperrors || !TestUnpack(c,lx,ly); lterrors=lterrors || !TestLinTrans(c,ax,bx,ay,by); //--- Test bicubic interpolation. //--- * interpolation at the nodes //--- * smoothness //--- * differentiation CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c); //--- search errors err=0; for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) err=MathMax(err,MathAbs(f[i][j]-CSpline2D::Spline2DCalc(c,x[j],y[i]))); } //--- search errors bcerrors=bcerrors || err>10000*CMath::m_machineepsilon; LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy); LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy); //--- search errors bcerrors=bcerrors || l2/l1>1.2; bcerrors=bcerrors || l2x/l1x>1.2; bcerrors=bcerrors || l2y/l1y>1.2; //--- check if(l2xy>0.01 && l1xy>0.01) { //--- Cross-derivative continuity is tested only when //--- bigger than 0.01. When the task size is too //--- small,the d2F/dXdY is nearly zero and Lipschitz //--- constant ratio is ill-conditioned. bcerrors=bcerrors || l2xy/l1xy>1.2; } err=0; for(i=0;i<=2*m-2;i++) { for(j=0;j<=2*n-2;j++) { CSpline2D::Spline2DDiff(c,lx[j],ly[i],v1,v1x,v1y,v1xy); TwodNumder(c,lx[j],ly[i],h,v2,v2x,v2y,v2xy); //--- search errors err=MathMax(err,MathAbs(v1-v2)); err=MathMax(err,MathAbs(v1x-v2x)); err=MathMax(err,MathAbs(v1y-v2y)); err=MathMax(err,MathAbs(v1xy-v2xy)); } } //--- search errors dserrors=dserrors || err>1.0E-3; uperrors=uperrors || !TestUnpack(c,lx,ly); lterrors=lterrors || !TestLinTrans(c,ax,bx,ay,by); //--- Copy/Serialise test if(CMath::RandomReal()>0.5) CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c); else CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c); //--- function calls UnsetSpline2D(c2); CSpline2D::Spline2DCopy(c,c2); //--- calculation err=0; for(i=1;i<=5;i++) { t1=ax+(bx-ax)*CMath::RandomReal(); t2=ay+(by-ay)*CMath::RandomReal(); //--- search errors err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t1,t2))); } //--- search errors cperrors=cperrors || err>10000*CMath::m_machineepsilon; //--- Special symmetry test err=0; for(jobtype=0;jobtype<=1;jobtype++) { //--- Prepare for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) ft[j].Set(i,f[i][j]); } //--- check if(jobtype==0) { CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c); CSpline2D::Spline2DBuildBilinear(y,x,ft,n,m,c2); } else { CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c); CSpline2D::Spline2DBuildBicubic(y,x,ft,n,m,c2); } //--- Test for(i=1;i<=10;i++) { t1=ax+(bx-ax)*CMath::RandomReal(); t2=ay+(by-ay)*CMath::RandomReal(); //--- search errors err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t2,t1))); } } //--- search errors syerrors=syerrors || err>10000*CMath::m_machineepsilon; } } } //--- Test resample for(m=2;m<=6;m++) { for(n=2;n<=6;n++) { //--- allocation f.Resize(m,n); ArrayResize(x,n); ArrayResize(y,m); //--- change values for(j=0;j<=n-1;j++) x[j]=(double)j/(double)(n-1); for(i=0;i<=m-1;i++) y[i]=(double)i/(double)(m-1); for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) f[i].Set(j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i])); } //--- calculation for(m2=2;m2<=6;m2++) { for(n2=2;n2<=6;n2++) { for(pass=1;pass<=passcount;pass++) { for(jobtype=0;jobtype<=1;jobtype++) { //--- check if(jobtype==0) { CSpline2D::Spline2DResampleBilinear(f,m,n,fr,m2,n2); CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c); } //--- check if(jobtype==1) { CSpline2D::Spline2DResampleBicubic(f,m,n,fr,m2,n2); CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c); } //--- change values err=0; mf=0; //--- calculation for(i=0;i<=m2-1;i++) { for(j=0;j<=n2-1;j++) { v1=CSpline2D::Spline2DCalc(c,(double)j/(double)(n2-1),(double)i/(double)(m2-1)); v2=fr[i][j]; //--- search errors err=MathMax(err,MathAbs(v1-v2)); mf=MathMax(mf,MathAbs(v1)); } } //--- check if(jobtype==0) rlerrors=rlerrors || err/mf>10000*CMath::m_machineepsilon; //--- check if(jobtype==1) rcerrors=rcerrors || err/mf>10000*CMath::m_machineepsilon; } } } } } } //--- report waserrors=(((((((blerrors || bcerrors) || dserrors) || cperrors) || uperrors) || lterrors) || syerrors) || rlerrors) || rcerrors; //--- check if(!silent) { Print("TESTING 2D INTERPOLATION"); //--- Normal tests Print("BILINEAR TEST: "); //--- check if(blerrors) Print("FAILED"); else Print("OK"); Print("BICUBIC TEST: "); //--- check if(bcerrors) Print("FAILED"); else Print("OK"); Print("DIFFERENTIATION TEST: "); //--- check if(dserrors) Print("FAILED"); else Print("OK"); Print("COPY/SERIALIZE TEST: "); //--- check if(cperrors) Print("FAILED"); else Print("OK"); Print("UNPACK TEST: "); //--- check if(uperrors) Print("FAILED"); else Print("OK"); Print("LIN.TRANS. TEST: "); //--- check if(lterrors) Print("FAILED"); else Print("OK"); Print("SPECIAL SYMMETRY TEST: "); //--- check if(syerrors) Print("FAILED"); else Print("OK"); Print("BILINEAR RESAMPLING TEST: "); //--- check if(rlerrors) Print("FAILED"); else Print("OK"); Print("BICUBIC RESAMPLING TEST: "); //--- check if(rcerrors) Print("FAILED"); else Print("OK"); //--- Summary if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Lipschitz constants for spline inself,first and second | //| derivatives. | //+------------------------------------------------------------------+ static void CTestSpline2DUnit::LConst(CSpline2DInterpolant &c,double &lx[], double &ly[],const int m,const int n, const double lstep,double &lc, double &lcx,double &lcy,double &lcxy) { //--- create variables int i=0; int j=0; double f1=0; double f2=0; double f3=0; double f4=0; double fx1=0; double fx2=0; double fx3=0; double fx4=0; double fy1=0; double fy2=0; double fy3=0; double fy4=0; double fxy1=0; double fxy2=0; double fxy3=0; double fxy4=0; double s2lstep=0; //--- initialization lc=0; lcx=0; lcy=0; lcxy=0; s2lstep=MathSqrt(2)*lstep; //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) { //--- Calculate TwodNumder(c,lx[j]-lstep/2,ly[i]-lstep/2,lstep/4,f1,fx1,fy1,fxy1); TwodNumder(c,lx[j]+lstep/2,ly[i]-lstep/2,lstep/4,f2,fx2,fy2,fxy2); TwodNumder(c,lx[j]+lstep/2,ly[i]+lstep/2,lstep/4,f3,fx3,fy3,fxy3); TwodNumder(c,lx[j]-lstep/2,ly[i]+lstep/2,lstep/4,f4,fx4,fy4,fxy4); //--- Lipschitz constant for the function itself lc=MathMax(lc,MathAbs((f1-f2)/lstep)); lc=MathMax(lc,MathAbs((f2-f3)/lstep)); lc=MathMax(lc,MathAbs((f3-f4)/lstep)); lc=MathMax(lc,MathAbs((f4-f1)/lstep)); lc=MathMax(lc,MathAbs((f1-f3)/s2lstep)); lc=MathMax(lc,MathAbs((f2-f4)/s2lstep)); //--- Lipschitz constant for the first derivative lcx=MathMax(lcx,MathAbs((fx1-fx2)/lstep)); lcx=MathMax(lcx,MathAbs((fx2-fx3)/lstep)); lcx=MathMax(lcx,MathAbs((fx3-fx4)/lstep)); lcx=MathMax(lcx,MathAbs((fx4-fx1)/lstep)); lcx=MathMax(lcx,MathAbs((fx1-fx3)/s2lstep)); lcx=MathMax(lcx,MathAbs((fx2-fx4)/s2lstep)); //--- Lipschitz constant for the first derivative lcy=MathMax(lcy,MathAbs((fy1-fy2)/lstep)); lcy=MathMax(lcy,MathAbs((fy2-fy3)/lstep)); lcy=MathMax(lcy,MathAbs((fy3-fy4)/lstep)); lcy=MathMax(lcy,MathAbs((fy4-fy1)/lstep)); lcy=MathMax(lcy,MathAbs((fy1-fy3)/s2lstep)); lcy=MathMax(lcy,MathAbs((fy2-fy4)/s2lstep)); //--- Lipschitz constant for the cross-derivative lcxy=MathMax(lcxy,MathAbs((fxy1-fxy2)/lstep)); lcxy=MathMax(lcxy,MathAbs((fxy2-fxy3)/lstep)); lcxy=MathMax(lcxy,MathAbs((fxy3-fxy4)/lstep)); lcxy=MathMax(lcxy,MathAbs((fxy4-fxy1)/lstep)); lcxy=MathMax(lcxy,MathAbs((fxy1-fxy3)/s2lstep)); lcxy=MathMax(lcxy,MathAbs((fxy2-fxy4)/s2lstep)); } } } //+------------------------------------------------------------------+ //| Numerical differentiation. | //+------------------------------------------------------------------+ static void CTestSpline2DUnit::TwodNumder(CSpline2DInterpolant &c,const double x, const double y,const double h, double &f,double &fx,double &fy, double &fxy) { //--- calculation f=CSpline2D::Spline2DCalc(c,x,y); fx=(CSpline2D::Spline2DCalc(c,x+h,y)-CSpline2D::Spline2DCalc(c,x-h,y))/(2*h); fy=(CSpline2D::Spline2DCalc(c,x,y+h)-CSpline2D::Spline2DCalc(c,x,y-h))/(2*h); fxy=(CSpline2D::Spline2DCalc(c,x+h,y+h)-CSpline2D::Spline2DCalc(c,x-h,y+h)-CSpline2D::Spline2DCalc(c,x+h,y-h)+CSpline2D::Spline2DCalc(c,x-h,y-h))/CMath::Sqr(2*h); } //+------------------------------------------------------------------+ //| Unpack test | //+------------------------------------------------------------------+ static bool CTestSpline2DUnit::TestUnpack(CSpline2DInterpolant &c,double &lx[], double &ly[]) { //--- create variables bool result; int i=0; int j=0; int n=0; int m=0; int ci=0; int cj=0; int p=0; double err=0; double tx=0; double ty=0; double v1=0; double v2=0; int pass=0; int passcount=0; //--- create matrix CMatrixDouble tbl; //--- initialization passcount=20; err=0; //--- function call CSpline2D::Spline2DUnpack(c,m,n,tbl); //--- calculation for(i=0;i<=m-2;i++) { for(j=0;j<=n-2;j++) { for(pass=1;pass<=passcount;pass++) { p=(n-1)*i+j; tx=(0.001+0.999*CMath::RandomReal())*(tbl[p][1]-tbl[p][0]); ty=(0.001+0.999*CMath::RandomReal())*(tbl[p][3]-tbl[p][2]); //--- Interpolation properties v1=0; for(ci=0;ci<=3;ci++) { for(cj=0;cj<=3;cj++) v1=v1+tbl[p][4+ci*4+cj]*MathPow(tx,ci)*MathPow(ty,cj); } v2=CSpline2D::Spline2DCalc(c,tbl[p][0]+tx,tbl[p][2]+ty); //--- search errors err=MathMax(err,MathAbs(v1-v2)); //--- Grid correctness err=MathMax(err,MathAbs(lx[2*j]-tbl[p][0])); err=MathMax(err,MathAbs(lx[2*(j+1)]-tbl[p][1])); err=MathMax(err,MathAbs(ly[2*i]-tbl[p][2])); err=MathMax(err,MathAbs(ly[2*(i+1)]-tbl[p][3])); } } } //--- get result result=err<10000*CMath::m_machineepsilon; //--- return result return(result); } //+------------------------------------------------------------------+ //| LinTrans test | //+------------------------------------------------------------------+ static bool CTestSpline2DUnit::TestLinTrans(CSpline2DInterpolant &c, const double ax,const double bx, const double ay,const double by) { //--- create variables bool result; double err=0; double a1=0; double a2=0; double b1=0; double b2=0; double tx=0; double ty=0; double vx=0; double vy=0; double v1=0; double v2=0; int pass=0; int passcount=0; int xjob=0; int yjob=0; //--- object of class CSpline2DInterpolant c2; //--- initialization passcount=5; err=0; //--- calculation for(xjob=0;xjob<=1;xjob++) { for(yjob=0;yjob<=1;yjob++) { for(pass=1;pass<=passcount;pass++) { //--- Prepare do { a1=2*CMath::RandomReal()-1; } while(a1==0.0); //--- change values a1=a1*xjob; b1=2*CMath::RandomReal()-1; do { a2=2*CMath::RandomReal()-1; } while(a2==0.0); //--- change values a2=a2*yjob; b2=2*CMath::RandomReal()-1; //--- Test XY CSpline2D::Spline2DCopy(c,c2); CSpline2D::Spline2DLinTransXY(c2,a1,b1,a2,b2); tx=ax+CMath::RandomReal()*(bx-ax); ty=ay+CMath::RandomReal()*(by-ay); //--- check if(xjob==0) { tx=b1; vx=ax+CMath::RandomReal()*(bx-ax); } else vx=(tx-b1)/a1; //--- check if(yjob==0) { ty=b2; vy=ay+CMath::RandomReal()*(by-ay); } else vy=(ty-b2)/a2; v1=CSpline2D::Spline2DCalc(c,tx,ty); v2=CSpline2D::Spline2DCalc(c2,vx,vy); //--- search errors err=MathMax(err,MathAbs(v1-v2)); //--- Test F CSpline2D::Spline2DCopy(c,c2); CSpline2D::Spline2DLinTransF(c2,a1,b1); //--- change values tx=ax+CMath::RandomReal()*(bx-ax); ty=ay+CMath::RandomReal()*(by-ay); v1=CSpline2D::Spline2DCalc(c,tx,ty); v2=CSpline2D::Spline2DCalc(c2,tx,ty); //--- search errors err=MathMax(err,MathAbs(a1*v1+b1-v2)); } } } //--- get result result=err<10000*CMath::m_machineepsilon; //--- return result return(result); } //+------------------------------------------------------------------+ //| Unset spline,i.e. initialize it with random garbage | //+------------------------------------------------------------------+ static void CTestSpline2DUnit::UnsetSpline2D(CSpline2DInterpolant &c) { //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble f; //--- allocation ArrayResize(x,2); ArrayResize(y,2); f.Resize(2,2); //--- initialization x[0]=-1; x[1]=1; y[0]=-1; y[1]=1; f[0].Set(0,0); f[0].Set(1,0); f[1].Set(0,0); f[1].Set(1,0); //--- function call CSpline2D::Spline2DBuildBilinear(x,y,f,2,2,c); } //+------------------------------------------------------------------+ //| Testing class CSpdGEVD | //+------------------------------------------------------------------+ class CTestSpdGEVDUnit { public: //--- constructor, destructor CTestSpdGEVDUnit(void); ~CTestSpdGEVDUnit(void); //--- public method static bool TestSpdGEVD(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSpdGEVDUnit::CTestSpdGEVDUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSpdGEVDUnit::~CTestSpdGEVDUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CSpdGEVD | //+------------------------------------------------------------------+ static bool CTestSpdGEVDUnit::TestSpdGEVD(const bool silent) { //--- create variables int pass=0; int n=0; int passcount=0; int maxn=0; int atask=0; int btask=0; bool isuppera; bool isupperb; int i=0; int j=0; int minij=0; double v=0; double v1=0; double v2=0; double err=0; double valerr=0; double threshold=0; bool waserrors; bool wfailed; bool wnsorted; int i_=0; //--- create arrays double d[]; double t1[]; //--- create matrix CMatrixDouble a; CMatrixDouble b; CMatrixDouble afull; CMatrixDouble bfull; CMatrixDouble l; CMatrixDouble z; //--- initialization threshold=10000*CMath::m_machineepsilon; valerr=0; wfailed=false; wnsorted=false; maxn=20; passcount=5; //--- Main cycle for(n=1;n<=maxn;n++) { for(pass=1;pass<=passcount;pass++) { for(atask=0;atask<=1;atask++) { for(btask=0;btask<=1;btask++) { isuppera=atask==0; isupperb=btask==0; //--- Initialize A,B,AFull,BFull ArrayResize(t1,n); a.Resize(n,n); b.Resize(n,n); afull.Resize(n,n); bfull.Resize(n,n); l.Resize(n,n); //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a[i].Set(j,2*CMath::RandomReal()-1); a[j].Set(i,a[i][j]); afull[i].Set(j,a[i][j]); afull[j].Set(i,a[i][j]); } } //--- change values for(i=0;i<=n-1;i++) { for(j=i+1;j<=n-1;j++) { l[i].Set(j,CMath::RandomReal()); l[j].Set(i,l[i][j]); } l[i].Set(i,1.5+CMath::RandomReal()); } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { minij=MathMin(i,j); //--- change value v=0.0; for(i_=0;i_<=minij;i_++) { v+=l[i][i_]*l[i_][j]; } b[i].Set(j,v); b[j].Set(i,v); bfull[i].Set(j,v); bfull[j].Set(i,v); } } //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(isuppera) { //--- check if(jthreshold || wfailed) || wnsorted; //--- check if(!silent) { Print("TESTING SYMMETRIC GEVD"); Print("Av-lambdav error (generalized): "); Print("{0,5:E3}",valerr); Print("Eigen values order: "); //--- check if(!wnsorted) Print("OK"); else Print("FAILED"); Print("Always converged: "); //--- check if(!wfailed) Print("YES"); else Print("NO"); Print("Threshold: "); Print("{0,5:E3}",threshold); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CInverseUpdate | //+------------------------------------------------------------------+ class CTestInverseUpdateUnit { private: //--- private methods static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void MatLU(CMatrixDouble &a,const int m,const int n,int &pivots[]); static void GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,const int n); static void GenerateRandomMatrixCond(CMatrixDouble &a0,const int n,const double c); static bool InvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular); static bool InvMatLU(CMatrixDouble &a,int &pivots[],const int n); static bool InvMat(CMatrixDouble &a,const int n); static double MatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n); static bool UpdAndInv(CMatrixDouble &a,double &u[],double &v[],const int n); public: //--- constructor, destructor CTestInverseUpdateUnit(void); ~CTestInverseUpdateUnit(void); //--- public method static bool TestInverseUpdate(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestInverseUpdateUnit::CTestInverseUpdateUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestInverseUpdateUnit::~CTestInverseUpdateUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CInverseUpdate | //+------------------------------------------------------------------+ static bool CTestInverseUpdateUnit::TestInverseUpdate(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int updrow=0; int updcol=0; double val=0; int pass=0; int passcount=0; bool waserrors; double threshold=0; double c=0; //--- create arrays double u[]; double v[]; //--- create matrix CMatrixDouble a; CMatrixDouble inva; CMatrixDouble b1; CMatrixDouble b2; //--- initialization waserrors=false; maxn=10; passcount=100; threshold=1.0E-6; //--- process for(n=1;n<=maxn;n++) { //--- allocation a.Resize(n,n); b1.Resize(n,n); b2.Resize(n,n); ArrayResize(u,n); ArrayResize(v,n); //--- calculation for(pass=1;pass<=passcount;pass++) { c=MathExp(CMath::RandomReal()*MathLog(10)); GenerateRandomMatrixCond(a,n,c); MakeACopy(a,n,n,inva); //--- check if(!InvMat(inva,n)) { waserrors=true; break; } //--- Test simple update updrow=CMath::RandomInteger(n); updcol=CMath::RandomInteger(n); val=0.1*(2*CMath::RandomReal()-1); //--- change values for(i=0;i<=n-1;i++) { //--- check if(i==updrow) u[i]=val; else u[i]=0; //--- check if(i==updcol) v[i]=1; else v[i]=0; } //--- function call MakeACopy(a,n,n,b1); //--- check if(!UpdAndInv(b1,u,v,n)) { waserrors=true; break; } //--- function calls MakeACopy(inva,n,n,b2); CInverseUpdate::RMatrixInvUpdateSimple(b2,n,updrow,updcol,val); //--- search errors waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold; //--- Test row update updrow=CMath::RandomInteger(n); for(i=0;i<=n-1;i++) { //--- check if(i==updrow) u[i]=1; else u[i]=0; v[i]=0.1*(2*CMath::RandomReal()-1); } //--- function call MakeACopy(a,n,n,b1); //--- check if(!UpdAndInv(b1,u,v,n)) { waserrors=true; break; } //--- function calls MakeACopy(inva,n,n,b2); CInverseUpdate::RMatrixInvUpdateRow(b2,n,updrow,v); //--- search errors waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold; //--- Test column update updcol=CMath::RandomInteger(n); for(i=0;i<=n-1;i++) { //--- check if(i==updcol) v[i]=1; else v[i]=0; u[i]=0.1*(2*CMath::RandomReal()-1); } //--- function call MakeACopy(a,n,n,b1); //--- check if(!UpdAndInv(b1,u,v,n)) { waserrors=true; break; } //--- function calls MakeACopy(inva,n,n,b2); CInverseUpdate::RMatrixInvUpdateColumn(b2,n,updcol,u); //--- search errors waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold; //--- Test full update for(i=0;i<=n-1;i++) { v[i]=0.1*(2*CMath::RandomReal()-1); u[i]=0.1*(2*CMath::RandomReal()-1); } //--- function call MakeACopy(a,n,n,b1); //--- check if(!UpdAndInv(b1,u,v,n)) { waserrors=true; break; } //--- function calls MakeACopy(inva,n,n,b2); CInverseUpdate::RMatrixInvUpdateUV(b2,n,u,v); //--- search errors waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold; } } //--- report if(!silent) { Print("TESTING INVERSE UPDATE (REAL)"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ static void CTestInverseUpdateUnit::MakeACopy(CMatrixDouble &a,const int m, const int n,CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) b[i].Set(j,a[i][j]); } } //+------------------------------------------------------------------+ //| LU decomposition | //+------------------------------------------------------------------+ static void CTestInverseUpdateUnit::MatLU(CMatrixDouble &a,const int m, const int n,int &pivots[]) { //--- create variables int i=0; int j=0; int jp=0; double s=0; int i_=0; //--- create array double t1[]; //--- allocation ArrayResize(pivots,MathMin(m-1,n-1)+1); ArrayResize(t1,MathMax(m-1,n-1)+1); //--- check if(!CAp::Assert(m>=0 && n>=0,"Error in LUDecomposition: incorrect function arguments")) return; //--- Quick return if possible if(m==0 || n==0) return; //--- calculation for(j=0;j<=MathMin(m-1,n-1);j++) { //--- Find pivot and test for singularity. jp=j; for(i=j+1;i<=m-1;i++) { //--- check if(MathAbs(a[i][j])>MathAbs(a[jp][j])) jp=i; } pivots[j]=jp; //--- check if(a[jp][j]!=0.0) { //--- Apply the interchange to rows if(jp!=j) { for(i_=0;i_<=n-1;i_++) t1[i_]=a[j][i_]; for(i_=0;i_<=n-1;i_++) a[j].Set(i_,a[jp][i_]); for(i_=0;i_<=n-1;i_++) a[jp].Set(i_,t1[i_]); } //--- Compute elements J+1:M of J-th column. if(j1.0) continue; sm=MathSqrt(-(2*MathLog(sm)/sm)); v[i]=u1*sm; //--- check if(i+1<=s) v[i+1]=u2*sm; i=i+2; } //--- change value lambdav=0.0; for(i_=1;i_<=s;i_++) lambdav+=v[i_]*v[i_]; } while((double)(lambdav)==0.0); lambdav=2/lambdav; //--- A * (I - 2 vv'/v'v )= //--- =A - (2/v'v) * A * v * v'= //--- =A - (2/v'v) * w * v' //--- where w=Av for(i=1;i<=s;i++) { t=0.0; for(i_=1;i_<=s;i_++) t+=a[i][i_]*v[i_]; w[i]=t; } //--- calculation for(i=1;i<=s;i++) { t=w[i]*lambdav; for(i_=1;i_<=s;i_++) a[i].Set(i_,a[i][i_]-t*v[i_]); } } //--- copy for(i=1;i<=n;i++) { for(j=1;j<=n;j++) a0[i-1].Set(j-1,a[i][j]); } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestInverseUpdateUnit::GenerateRandomMatrixCond(CMatrixDouble &a0, const int n, const double c) { //--- create variables double l1=0; double l2=0; int i=0; int j=0; int k=0; //--- create array double cc[]; //--- create matrix CMatrixDouble q1; CMatrixDouble q2; //--- function calls GenerateRandomOrthogonalMatrix(q1,n); GenerateRandomOrthogonalMatrix(q2,n); //--- allocation ArrayResize(cc,n); //--- change values l1=0; l2=MathLog(1/c); cc[0]=MathExp(l1); for(i=1;i<=n-2;i++) cc[i]=MathExp(CMath::RandomReal()*(l2-l1)+l1); cc[n-1]=MathExp(l2); //--- allocation a0.Resize(n,n); //--- calculation for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { a0[i].Set(j,0); for(k=0;k<=n-1;k++) a0[i].Set(j,a0[i][j]+q1[i][k]*cc[k]*q2[j][k]); } } } //+------------------------------------------------------------------+ //| triangular inverse | //+------------------------------------------------------------------+ static bool CTestInverseUpdateUnit::InvMatTr(CMatrixDouble &a,const int n, const bool isupper, const bool isunittriangular) { //--- create variables bool result; bool nounit; int i=0; int j=0; double v=0; double ajj=0; int i_=0; //--- create array double t[]; //--- initialization result=true; //--- allocation ArrayResize(t,n); //--- Test the input parameters. nounit=!isunittriangular; //--- check if(isupper) { //--- Compute inverse of upper triangular matrix. for(j=0;j<=n-1;j++) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } a[j].Set(j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- Compute elements 1:j-1 of j-th column. if(j>0) { for(i_=0;i_<=j-1;i_++) t[i_]=a[i_][j]; for(i=0;i<=j-1;i++) { //--- check if(i=0;j--) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } //--- change values a[j].Set(j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { //--- change value v=0.0; for(i_=j+1;i_<=i-1;i_++) v+=a[i][i_]*t[i_]; } else v=0; //--- check if(nounit) a[i].Set(j,v+a[i][i]*t[i]); else a[i].Set(j,v+t[i]); } //--- change values for(i_=j+1;i_<=n-1;i_++) a[i_].Set(j,ajj*a[i_][j]); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| LU inverse | //+------------------------------------------------------------------+ static bool CTestInverseUpdateUnit::InvMatLU(CMatrixDouble &a,int &pivots[], const int n) { //--- create variables bool result; int i=0; int j=0; int jp=0; double v=0; int i_=0; //--- create array double work[]; //--- initialization result=true; //--- Quick return if possible if(n==0) { //--- return result return(result); } //--- allocation ArrayResize(work,n); //--- Form inv(U) if(!InvMatTr(a,n,true,false)) { //--- return result return(false); } //--- Solve the equation inv(A)*L=inv(U) for inv(A). for(j=n-1;j>=0;j--) { //--- Copy current column of L to WORK and replace with zeros. for(i=j+1;i<=n-1;i++) { work[i]=a[i][j]; a[i].Set(j,0); } //--- Compute current column of inv(A). if(j=0;j--) { jp=pivots[j]; //--- check if(jp!=j) { //--- copy for(i_=0;i_<=n-1;i_++) work[i_]=a[i_][j]; for(i_=0;i_<=n-1;i_++) a[i_].Set(j,a[i_][jp]); for(i_=0;i_<=n-1;i_++) a[i_].Set(jp,work[i_]); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Matrix inverse | //+------------------------------------------------------------------+ static bool CTestInverseUpdateUnit::InvMat(CMatrixDouble &a,const int n) { //--- create array int pivots[]; //--- function call MatLU(a,n,n,pivots); //--- return result return(InvMatLU(a,pivots,n)); } //+------------------------------------------------------------------+ //| Diff | //+------------------------------------------------------------------+ static double CTestInverseUpdateUnit::MatrixDiff(CMatrixDouble &a, CMatrixDouble &b, const int m,const int n) { //--- create variables double result=0; int i=0; int j=0; //--- calculation for(i=0;i<=m-1;i++) { for(j=0;j<=n-1;j++) result=MathMax(result,MathAbs(b[i][j]-a[i][j])); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Update and inverse | //+------------------------------------------------------------------+ static bool CTestInverseUpdateUnit::UpdAndInv(CMatrixDouble &a,double &u[], double &v[],const int n) { //--- create variables int i=0; double r=0; int i_=0; //--- create array int pivots[]; //--- calculation for(i=0;i<=n-1;i++) { r=u[i]; for(i_=0;i_<=n-1;i_++) a[i].Set(i_,a[i][i_]+r*v[i_]); } //--- function call MatLU(a,n,n,pivots); //--- return result return(InvMatLU(a,pivots,n)); } //+------------------------------------------------------------------+ //| Testing class CSchur | //+------------------------------------------------------------------+ class CTestSchurUnit { private: //--- private methods static void FillsParseA(CMatrixDouble &a,const int n,const double sparcity); static void TestSchurProblem(CMatrixDouble &a,const int n,double &materr,double &orterr,bool &errstruct,bool &wfailed); public: //--- constructor, destructor CTestSchurUnit(void); ~CTestSchurUnit(void); //--- public method static bool TestSchur(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestSchurUnit::CTestSchurUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestSchurUnit::~CTestSchurUnit(void) { } //+------------------------------------------------------------------+ //| Testing Schur decomposition subroutine | //+------------------------------------------------------------------+ static bool CTestSchurUnit::TestSchur(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int j=0; int pass=0; int passcount=0; bool waserrors; bool errstruct; bool wfailed; double materr=0; double orterr=0; double threshold=0; //--- create matrix CMatrixDouble a; //--- initialization materr=0; orterr=0; errstruct=false; wfailed=false; waserrors=false; maxn=70; passcount=1; threshold=5*100*CMath::m_machineepsilon; //--- allocation a.Resize(maxn,maxn); //--- zero matrix,several cases for(i=0;i<=maxn-1;i++) { for(j=0;j<=maxn-1;j++) a[i].Set(j,0); } for(n=1;n<=maxn;n++) { //--- check if(n>30 && n%2==0) continue; //--- function call TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); } //--- Dense matrix for(pass=1;pass<=passcount;pass++) { for(n=1;n<=maxn;n++) { //--- check if(n>30 && n%2==0) continue; //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) a[i].Set(j,2*CMath::RandomReal()-1); } //--- function call TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); } } //--- Sparse matrices,very sparse matrices,incredible sparse matrices for(pass=1;pass<=1;pass++) { for(n=1;n<=maxn;n++) { //--- check if(n>30 && n%3!=0) continue; //--- function calls FillsParseA(a,n,0.8); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); FillsParseA(a,n,0.9); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); FillsParseA(a,n,0.95); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); FillsParseA(a,n,0.997); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); } } //--- report waserrors=((materr>threshold || orterr>threshold) || errstruct) || wfailed; //--- check if(!silent) { Print("TESTING SCHUR DECOMPOSITION"); Print("Schur decomposition error: "); Print("{0,5:E3}",materr); Print("Schur orthogonality error: "); Print("{0,5:E3}",orterr); Print("T matrix structure: "); //--- check if(!errstruct) Print("OK"); else Print("FAILED"); Print("Always converged: "); //--- check if(!wfailed) Print("OK"); else Print("FAILED"); Print("Threshold: "); Print("{0,5:E3}",threshold); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestSchurUnit::FillsParseA(CMatrixDouble &a,const int n, const double sparcity) { //--- create variables int i=0; int j=0; //--- change values for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- check if(CMath::RandomReal()>=sparcity) a[i].Set(j,2*CMath::RandomReal()-1); else a[i].Set(j,0); } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestSchurUnit::TestSchurProblem(CMatrixDouble &a,const int n, double &materr,double &orterr, bool &errstruct,bool &wfailed) { //--- create variables int i=0; int j=0; int k=0; double v=0; double locerr=0; int i_=0; //--- create arrays double sr[]; double astc[]; double sastc[]; //--- create matrix CMatrixDouble s; CMatrixDouble t; //--- allocation ArrayResize(sr,n); ArrayResize(astc,n); ArrayResize(sastc,n); //--- Schur decomposition,convergence test t.Resize(n,n); for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) t[i].Set(j,a[i][j]); } //--- check if(!CSchur::RMatrixSchur(t,n,s)) { wfailed=true; return; } //--- decomposition error locerr=0; for(j=0;j<=n-1;j++) { for(i_=0;i_<=n-1;i_++) sr[i_]=s[j][i_]; //--- calculation for(k=0;k<=n-1;k++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=t[k][i_]*sr[i_]; astc[k]=v; } //--- calculation for(k=0;k<=n-1;k++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=s[k][i_]*astc[i_]; sastc[k]=v; } //--- search errors for(k=0;k<=n-1;k++) locerr=MathMax(locerr,MathAbs(sastc[k]-a[k][j])); } //--- search errors materr=MathMax(materr,locerr); //--- orthogonality error locerr=0; for(i=0;i<=n-1;i++) { for(j=0;j<=n-1;j++) { //--- change value v=0.0; for(i_=0;i_<=n-1;i_++) v+=s[i_][i]*s[i_][j]; //--- check if(i!=j) locerr=MathMax(locerr,MathAbs(v)); else locerr=MathMax(locerr,MathAbs(v-1)); } } //--- search errors orterr=MathMax(orterr,locerr); //--- T matrix structure for(j=0;j<=n-1;j++) { for(i=j+2;i<=n-1;i++) { //--- check if(t[i][j]!=0.0) errstruct=true; } } } //+------------------------------------------------------------------+ //| Testing class CNlEq | //+------------------------------------------------------------------+ class CTestNlEqUnit { private: //--- private methods static void TestFuncHBM(CNlEqState &state); static void TestFuncHB1(CNlEqState &state); static void TestFuncSHBM(CNlEqState &state); public: //--- constructor, destructor CTestNlEqUnit(void); ~CTestNlEqUnit(void); //--- public method static bool TestNlEq(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestNlEqUnit::CTestNlEqUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestNlEqUnit::~CTestNlEqUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CNlEq | //+------------------------------------------------------------------+ static bool CTestNlEqUnit::TestNlEq(const bool silent) { //--- create variables bool waserrors; bool basicserrors; bool converror; bool othererrors; int n=0; int i=0; int k=0; double v=0; double flast=0; bool firstrep; int nfunc=0; int njac=0; int itcnt=0; int pass=0; int passcount=0; double epsf=0; double stpmax=0; int i_=0; //--- create arrays double x[]; double xlast[]; //--- objects of classes CNlEqState state; CNlEqReport rep; //--- initialization waserrors=false; basicserrors=false; converror=false; othererrors=false; //--- Basic tests //--- Test with Himmelblau's function (M): //--- * ability to find correct result //--- * ability to work after soft restart (restart after finish) //--- * ability to work after hard restart (restart in the middle of optimization) passcount=100; for(pass=0;pass<=passcount-1;pass++) { //--- Ability to find correct result ArrayResize(x,2); x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; //--- function call CNlEq::NlEqCreateLM(2,2,x,state); epsf=1.0E-9; //--- function call CNlEq::NlEqSetCond(state,epsf,0); //--- cycle while(CNlEq::NlEqIteration(state)) TestFuncHBM(state); //--- function call CNlEq::NlEqResults(state,x,rep); //--- check if(rep.m_terminationtype>0) basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf); else basicserrors=true; //--- Ability to work after soft restart ArrayResize(x,2); x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; //--- function call CNlEq::NlEqCreateLM(2,2,x,state); epsf=1.0E-9; //--- function call CNlEq::NlEqSetCond(state,epsf,0); //--- cycle while(CNlEq::NlEqIteration(state)) TestFuncHBM(state); //--- function call CNlEq::NlEqResults(state,x,rep); //--- allocation ArrayResize(x,2); //--- change values x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; //--- function call CNlEq::NlEqRestartFrom(state,x); //--- cycle while(CNlEq::NlEqIteration(state)) TestFuncHBM(state); //--- function call CNlEq::NlEqResults(state,x,rep); //--- check if(rep.m_terminationtype>0) basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf); else basicserrors=true; //--- Ability to work after hard restart: //--- * stopping condition: small F //--- * StpMax is so small that we need about 10000 iterations to //--- find solution (steps are small) //--- * choose random K significantly less that 9999 //--- * iterate for some time,then break,restart optimization ArrayResize(x,2); x[0]=100; x[1]=100; //--- function call CNlEq::NlEqCreateLM(2,2,x,state); epsf=1.0E-9; //--- function calls CNlEq::NlEqSetCond(state,epsf,0); CNlEq::NlEqSetStpMax(state,0.01); k=1+CMath::RandomInteger(100); //--- calculation for(i=0;i<=k-1;i++) { //--- check if(!CNlEq::NlEqIteration(state)) break; TestFuncHBM(state); } //--- allocation ArrayResize(x,2); //--- change values x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; //--- function call CNlEq::NlEqRestartFrom(state,x); //--- cycle while(CNlEq::NlEqIteration(state)) TestFuncHBM(state); //--- function call CNlEq::NlEqResults(state,x,rep); //--- check if(rep.m_terminationtype>0) basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf); else basicserrors=true; } //--- Basic tests //--- Test with Himmelblau's function (1): //--- * ability to find correct result passcount=100; for(pass=0;pass<=passcount-1;pass++) { //--- Ability to find correct result ArrayResize(x,2); x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; //--- function call CNlEq::NlEqCreateLM(2,1,x,state); epsf=1.0E-9; //--- function call CNlEq::NlEqSetCond(state,epsf,0); //--- cycle while(CNlEq::NlEqIteration(state)) TestFuncHB1(state); //--- function call CNlEq::NlEqResults(state,x,rep); //--- check if(rep.m_terminationtype>0) basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>(double)(epsf); else basicserrors=true; } //--- Basic tests //--- Ability to detect situation when we can't find minimum passcount=100; for(pass=0;pass<=passcount-1;pass++) { //--- allocation ArrayResize(x,2); x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; //--- function call CNlEq::NlEqCreateLM(2,3,x,state); epsf=1.0E-9; //--- function call CNlEq::NlEqSetCond(state,epsf,0); //--- cycle while(CNlEq::NlEqIteration(state)) TestFuncSHBM(state); //--- function call CNlEq::NlEqResults(state,x,rep); //--- search errors basicserrors=basicserrors || rep.m_terminationtype!=-4; } //--- Test correctness of intermediate reports and final report: //--- * first report is starting point //--- * function value decreases on subsequent reports //--- * function value is correctly reported //--- * last report is final point //--- * NFunc and NJac are compared with values counted directly //--- * IterationsCount is compared with value counter directly n=2; ArrayResize(x,n); ArrayResize(xlast,n); //--- change values x[0]=20*CMath::RandomReal()-10; x[1]=20*CMath::RandomReal()-10; xlast[0]=CMath::m_maxrealnumber; xlast[1]=CMath::m_maxrealnumber; //--- function calls CNlEq::NlEqCreateLM(n,2,x,state); CNlEq::NlEqSetCond(state,1.0E-6,0); CNlEq::NlEqSetXRep(state,true); //--- change values firstrep=true; flast=CMath::m_maxrealnumber; nfunc=0; njac=0; itcnt=0; //--- cycle while(CNlEq::NlEqIteration(state)) { //--- check if(state.m_xupdated) { //--- first report must be starting point if(firstrep) { for(i=0;i<=n-1;i++) othererrors=othererrors || state.m_x[i]!=x[i]; firstrep=false; } //--- function value must decrease othererrors=othererrors || state.m_f>flast; //--- check correctness of function value v=CMath::Sqr(state.m_x[0]*state.m_x[0]+state.m_x[1]-11)+CMath::Sqr(state.m_x[0]+state.m_x[1]*state.m_x[1]-7); othererrors=othererrors || MathAbs(v-state.m_f)/MathMax(v,1)>100*CMath::m_machineepsilon; //--- update info and continue for(i_=0;i_<=n-1;i_++) xlast[i_]=state.m_x[i_]; flast=state.m_f; itcnt=itcnt+1; continue; } //--- check if(state.m_needf) nfunc=nfunc+1; //--- check if(state.m_needfij) { nfunc=nfunc+1; njac=njac+1; } //--- function call TestFuncHBM(state); } //--- function call CNlEq::NlEqResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { othererrors=(othererrors || xlast[0]!=x[0]) || xlast[1]!=x[1]; v=CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7); othererrors=othererrors || MathAbs(flast-v)/MathMax(v,1)>100*CMath::m_machineepsilon; } else converror=true; //--- search errors othererrors=othererrors || rep.m_nfunc!=nfunc; othererrors=othererrors || rep.m_njac!=njac; othererrors=othererrors || rep.m_iterationscount!=itcnt-1; //--- Test ability to set limit on algorithm steps ArrayResize(x,2); ArrayResize(xlast,2); //--- change values x[0]=20*CMath::RandomReal()+20; x[1]=20*CMath::RandomReal()+20; xlast[0]=x[0]; xlast[1]=x[1]; stpmax=0.1+0.1*CMath::RandomReal(); epsf=1.0E-9; //--- function calls CNlEq::NlEqCreateLM(2,3,x,state); CNlEq::NlEqSetStpMax(state,stpmax); CNlEq::NlEqSetCond(state,epsf,0); CNlEq::NlEqSetXRep(state,true); //--- cycle while(CNlEq::NlEqIteration(state)) { //--- check if(state.m_needf || state.m_needfij) TestFuncHBM(state); //--- check if((state.m_needf || state.m_needfij) || state.m_xupdated) othererrors=othererrors || MathSqrt(CMath::Sqr(state.m_x[0]-xlast[0])+CMath::Sqr(state.m_x[1]-xlast[1]))>1.00001*stpmax; //--- check if(state.m_xupdated) { xlast[0]=state.m_x[0]; xlast[1]=state.m_x[1]; } } //--- end waserrors=(basicserrors || converror) || othererrors; //--- check if(!silent) { Print("TESTING NLEQ SOLVER"); Print("BASIC FUNCTIONALITY: "); //--- check if(basicserrors) Print("FAILED"); else Print("OK"); Print("CONVERGENCE: "); //--- check if(converror) Print("FAILED"); else Print("OK"); Print("OTHER PROPERTIES: "); //--- check if(othererrors) Print("FAILED"); else Print("OK"); //--- check if(waserrors) Print("TEST FAILED"); else Print("TEST PASSED"); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Himmelblau's function | //| F=(x^2+y-11)^2 + (x+y^2-7)^2 | //| posed as system of M functions: | //| f0=x^2+y-11 | //| f1=x+y^2-7 | //+------------------------------------------------------------------+ static void CTestNlEqUnit::TestFuncHBM(CNlEqState &state) { //--- create variables double x=0; double y=0; //--- check if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!")) return; //--- change values x=state.m_x[0]; y=state.m_x[1]; //--- check if(state.m_needf) { state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7); return; } //--- check if(state.m_needfij) { state.m_fi[0]=x*x+y-11; state.m_fi[1]=x+y*y-7; state.m_j[0].Set(0,2*x); state.m_j[0].Set(1,1); state.m_j[1].Set(0,1); state.m_j[1].Set(1,2*y); //--- exit the function return; } } //+------------------------------------------------------------------+ //| Himmelblau's function | //| F=(x^2+y-11)^2 + (x+y^2-7)^2 | //| posed as system of 1 function | //+------------------------------------------------------------------+ static void CTestNlEqUnit::TestFuncHB1(CNlEqState &state) { //--- create variables double x=0; double y=0; //--- check if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!")) return; //--- change values x=state.m_x[0]; y=state.m_x[1]; //--- check if(state.m_needf) { state.m_f=CMath::Sqr(CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)); return; } //--- check if(state.m_needfij) { state.m_fi[0]=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7); state.m_j[0].Set(0,2*(x*x+y-11)*2*x+2*(x+y*y-7)); state.m_j[0].Set(1,2*(x*x+y-11)+2*(x+y*y-7)*2*y); //--- exit the function return; } } //+------------------------------------------------------------------+ //| Shifted Himmelblau's function | //| F=(x^2+y-11)^2 + (x+y^2-7)^2 + 1 | //| posed as system of M functions: | //| f0=x^2+y-11 | //| f1=x+y^2-7 | //| f2=1 | //| This function is used to test algorithm on problem which has no | //| solution. | //+------------------------------------------------------------------+ static void CTestNlEqUnit::TestFuncSHBM(CNlEqState &state) { //--- create variables double x=0; double y=0; //--- check if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!")) return; //--- change values x=state.m_x[0]; y=state.m_x[1]; //--- check if(state.m_needf) { state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)+1; return; } //--- check if(state.m_needfij) { state.m_fi[0]=x*x+y-11; state.m_fi[1]=x+y*y-7; state.m_fi[2]=1; state.m_j[0].Set(0,2*x); state.m_j[0].Set(1,1); state.m_j[1].Set(0,1); state.m_j[1].Set(1,2*y); state.m_j[2].Set(0,0); state.m_j[2].Set(1,0); //--- exit the function return; } } //+------------------------------------------------------------------+ //| Testing class CChebyshev | //+------------------------------------------------------------------+ class CTestChebyshevUnit { public: //--- constructor, destructor CTestChebyshevUnit(void); ~CTestChebyshevUnit(void); //--- public method static bool TestChebyshev(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestChebyshevUnit::CTestChebyshevUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestChebyshevUnit::~CTestChebyshevUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CChebyshev | //+------------------------------------------------------------------+ static bool CTestChebyshevUnit::TestChebyshev(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double ferr=0; double threshold=0; double x=0; double v=0; int pass=0; int i=0; int j=0; int k=0; int n=0; int maxn=0; bool waserrors; int i_=0; //--- create arrays double c[]; double p1[]; double p2[]; //--- create matrix CMatrixDouble a; //--- initialization err=0; sumerr=0; cerr=0; ferr=0; threshold=1.0E-9; waserrors=false; //--- Testing Chebyshev polynomials of the first kind err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.00)-1)); err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.33)-1)); err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,-0.42)-1)); x=0.2; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.2)); x=0.4; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.4)); x=0.6; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.6)); x=0.8; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.8)); x=1.0; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-1.0)); x=0.2; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.92)); x=0.4; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.68)); x=0.6; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.28)); x=0.8; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-0.28)); x=1.0; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-1.00)); n=10; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)-0.4284556288)); n=11; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7996160205)); n=12; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7483020370)); //--- Testing Chebyshev polynomials of the second kind n=0; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-1.0000000000)); n=1; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.4000000000)); n=2; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8400000000)); n=3; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.7360000000)); n=4; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.5456000000)); n=10; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.6128946176)); n=11; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.6770370970)); n=12; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8837094564)); //--- Testing Clenshaw summation maxn=20; ArrayResize(c,maxn+1); for(k=1;k<=2;k++) { for(pass=1;pass<=10;pass++) { x=2*CMath::RandomReal()-1; v=0; //--- calculation for(n=0;n<=maxn;n++) { c[n]=2*CMath::RandomReal()-1; v=v+CChebyshev::ChebyshevCalculate(k,n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CChebyshev::ChebyshevSum(c,k,n,x))); } } } //--- Testing coefficients CChebyshev::ChebyshevCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- function call CChebyshev::ChebyshevCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-1)); //--- function call CChebyshev::ChebyshevCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+1)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-2)); //--- function call CChebyshev::ChebyshevCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]+3)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]-4)); //--- function call CChebyshev::ChebyshevCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]+8)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]-8)); //--- function call CChebyshev::ChebyshevCoefficients(9,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-9)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]+120)); cerr=MathMax(cerr,MathAbs(c[4]-0)); cerr=MathMax(cerr,MathAbs(c[5]-432)); cerr=MathMax(cerr,MathAbs(c[6]-0)); cerr=MathMax(cerr,MathAbs(c[7]+576)); cerr=MathMax(cerr,MathAbs(c[8]-0)); cerr=MathMax(cerr,MathAbs(c[9]-256)); //--- Testing FromChebyshev maxn=10; a.Resize(maxn+1,maxn+1); for(i=0;i<=maxn;i++) { for(j=0;j<=maxn;j++) a[i].Set(j,0); //--- function call CChebyshev::ChebyshevCoefficients(i,c); for(i_=0;i_<=i;i_++) a[i].Set(i_,c[i_]); } //--- allocation ArrayResize(c,maxn+1); ArrayResize(p1,maxn+1); //--- calculation for(n=0;n<=maxn;n++) { for(pass=1;pass<=10;pass++) { for(i=0;i<=n;i++) p1[i]=0; for(i=0;i<=n;i++) { //--- change values c[i]=2*CMath::RandomReal()-1; v=c[i]; for(i_=0;i_<=i;i_++) p1[i_]=p1[i_]+v*a[i][i_]; } //--- function call CChebyshev::FromChebyshev(c,n,p2); for(i=0;i<=n;i++) ferr=MathMax(ferr,MathAbs(p1[i]-p2[i])); } } //--- Reporting waserrors=((err>threshold || sumerr>threshold) || cerr>threshold) || ferr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS"); Print("Max error against table "); Print("{0,5:E2}",err); Print("Summation error "); Print("{0,5:E2}",sumerr); Print("Coefficients error "); Print("{0,5:E2}",cerr); Print("FrobChebyshev error "); Print("{0,5:E2}",ferr); Print("Threshold "); Print("{0,5:E2}",threshold); //--- check if(!waserrors) Print("TEST PASSED"); else Print("TEST FAILED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CHermite | //+------------------------------------------------------------------+ class CTestHermiteUnit { public: //--- constructor, destructor CTestHermiteUnit(void); ~CTestHermiteUnit(void); //--- method static bool TestHermite(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestHermiteUnit::CTestHermiteUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestHermiteUnit::~CTestHermiteUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CHermite | //+------------------------------------------------------------------+ static bool CTestHermiteUnit::TestHermite(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double threshold=0; int n=0; int maxn=0; int pass=0; double x=0; double v=0; bool waserrors; //--- create array double c[]; //--- initialization err=0; sumerr=0; cerr=0; threshold=1.0E-9; waserrors=false; //--- Testing Hermite polynomials n=0; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-1)); n=1; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2)); n=2; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2)); n=3; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+4)); n=4; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+20)); n=5; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+8)); n=6; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-184)); n=7; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-464)); n=11; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-230848)); n=12; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-280768)); //--- Testing Clenshaw summation maxn=10; ArrayResize(c,maxn+1); for(pass=1;pass<=10;pass++) { x=2*CMath::RandomReal()-1; v=0; //--- calculation for(n=0;n<=maxn;n++) { c[n]=2*CMath::RandomReal()-1; v=v+CHermite::HermiteCalculate(n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CHermite::HermiteSum(c,n,x))); } } //--- Testing coefficients CHermite::HermiteCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- function call CHermite::HermiteCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-2)); //--- function call CHermite::HermiteCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+2)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-4)); //--- function call CHermite::HermiteCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]+12)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]-8)); //--- function call CHermite::HermiteCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-12)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]+48)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]-16)); //--- function call CHermite::HermiteCoefficients(5,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-120)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]+160)); cerr=MathMax(cerr,MathAbs(c[4]-0)); cerr=MathMax(cerr,MathAbs(c[5]-32)); //--- function call CHermite::HermiteCoefficients(6,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+120)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-720)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]+480)); cerr=MathMax(cerr,MathAbs(c[5]-0)); cerr=MathMax(cerr,MathAbs(c[6]-64)); //--- Reporting waserrors=(err>threshold || sumerr>threshold) || cerr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE HERMITE POLYNOMIALS"); Print("Max error "); Print("{0,5:E2}",err); Print("Summation error "); Print("{0,5:E2}",sumerr); Print("Coefficients error "); Print("{0,5:E2}",cerr); Print("Threshold "); Print("{0,5:E2}",threshold); //--- check if(!waserrors) Print("TEST PASSED"); else Print("TEST FAILED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CLaguerre | //+------------------------------------------------------------------+ class CTestLaguerreUnit { public: //--- constructor, destructor CTestLaguerreUnit(void); ~CTestLaguerreUnit(void); //--- public method static bool TestLaguerre(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestLaguerreUnit::CTestLaguerreUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestLaguerreUnit::~CTestLaguerreUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CLaguerre | //+------------------------------------------------------------------+ static bool CTestLaguerreUnit::TestLaguerre(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double threshold=0; int n=0; int maxn=0; int pass=0; double x=0; double v=0; bool waserrors; //--- create array double c[]; //--- initialization err=0; sumerr=0; cerr=0; threshold=1.0E-9; waserrors=false; //--- Testing Laguerre polynomials n=0; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-1.0000000000)); n=1; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.5000000000)); n=2; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.1250000000)); n=3; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.1458333333)); n=4; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3307291667)); n=5; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4455729167)); n=6; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5041449653)); n=7; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5183392237)); n=8; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4983629984)); n=9; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4529195204)); n=10; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3893744141)); n=11; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3139072988)); n=12; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.2316496389)); //--- Testing Clenshaw summation maxn=20; ArrayResize(c,maxn+1); for(pass=1;pass<=10;pass++) { x=2*CMath::RandomReal()-1; v=0; //--- calculation for(n=0;n<=maxn;n++) { c[n]=2*CMath::RandomReal()-1; v=v+CLaguerre::LaguerreCalculate(n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CLaguerre::LaguerreSum(c,n,x))); } } //--- Testing coefficients CLaguerre::LaguerreCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- function call CLaguerre::LaguerreCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); cerr=MathMax(cerr,MathAbs(c[1]+1)); //--- function call CLaguerre::LaguerreCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-2.0/2.0)); cerr=MathMax(cerr,MathAbs(c[1]+4.0/2.0)); cerr=MathMax(cerr,MathAbs(c[2]-1.0/2.0)); //--- function call CLaguerre::LaguerreCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-6.0/6.0)); cerr=MathMax(cerr,MathAbs(c[1]+18.0/6.0)); cerr=MathMax(cerr,MathAbs(c[2]-9.0/6.0)); cerr=MathMax(cerr,MathAbs(c[3]+1.0/6.0)); //--- function call CLaguerre::LaguerreCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-24.0/24.0)); cerr=MathMax(cerr,MathAbs(c[1]+96.0/24.0)); cerr=MathMax(cerr,MathAbs(c[2]-72.0/24.0)); cerr=MathMax(cerr,MathAbs(c[3]+16.0/24.0)); cerr=MathMax(cerr,MathAbs(c[4]-1.0/24.0)); //--- function call CLaguerre::LaguerreCoefficients(5,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-120.0/120.0)); cerr=MathMax(cerr,MathAbs(c[1]+600.0/120.0)); cerr=MathMax(cerr,MathAbs(c[2]-600.0/120.0)); cerr=MathMax(cerr,MathAbs(c[3]+200.0/120.0)); cerr=MathMax(cerr,MathAbs(c[4]-25.0/120.0)); cerr=MathMax(cerr,MathAbs(c[5]+1.0/120.0)); //--- function call CLaguerre::LaguerreCoefficients(6,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-720.0/720.0)); cerr=MathMax(cerr,MathAbs(c[1]+4320.0/720.0)); cerr=MathMax(cerr,MathAbs(c[2]-5400.0/720.0)); cerr=MathMax(cerr,MathAbs(c[3]+2400.0/720.0)); cerr=MathMax(cerr,MathAbs(c[4]-450.0/720.0)); cerr=MathMax(cerr,MathAbs(c[5]+36.0/720.0)); cerr=MathMax(cerr,MathAbs(c[6]-1.0/720.0)); //--- Reporting waserrors=(err>threshold || sumerr>threshold) || cerr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS"); Print("Max error "); Print("{0,5:E2}",err); Print("Summation error "); Print("{0,5:E2}",sumerr); Print("Coefficients error "); Print("{0,5:E2}",cerr); Print("Threshold "); Print("{0,5:E2}",threshold); //--- check if(!waserrors) Print("TEST PASSED"); else Print("TEST FAILED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CLegendre | //+------------------------------------------------------------------+ class CTestLegendreUnit { public: //--- constructor, destructor CTestLegendreUnit(void); ~CTestLegendreUnit(void); //--- public method static bool TestLegendre(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestLegendreUnit::CTestLegendreUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestLegendreUnit::~CTestLegendreUnit(void) { } //+------------------------------------------------------------------+ //| Testing class CLegendre | //+------------------------------------------------------------------+ static bool CTestLegendreUnit::TestLegendre(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double threshold=0; int n=0; int maxn=0; int i=0; int pass=0; double x=0; double v=0; double t=0; bool waserrors; //--- create array double c[]; //--- initialization err=0; sumerr=0; cerr=0; threshold=1.0E-9; waserrors=false; //--- Testing Legendre polynomials values for(n=0;n<=10;n++) { //--- function call CLegendre::LegendreCoefficients(n,c); for(pass=1;pass<=10;pass++) { //--- calculation x=2*CMath::RandomReal()-1; v=CLegendre::LegendreCalculate(n,x); t=1; for(i=0;i<=n;i++) { v=v-c[i]*t; t=t*x; } //--- search errors err=MathMax(err,MathAbs(v)); } } //--- Testing Clenshaw summation maxn=20; ArrayResize(c,maxn+1); for(pass=1;pass<=10;pass++) { //--- change values x=2*CMath::RandomReal()-1; v=0; for(n=0;n<=maxn;n++) { c[n]=2*CMath::RandomReal()-1; v=v+CLegendre::LegendreCalculate(n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CLegendre::LegendreSum(c,n,x))); } } //--- Testing coefficients CLegendre::LegendreCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- calculation CLegendre::LegendreCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-1)); //--- calculation CLegendre::LegendreCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+1.0/2.0)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-3.0/2.0)); //--- calculation CLegendre::LegendreCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]+3.0/2.0)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]-5.0/2.0)); //--- calculation CLegendre::LegendreCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-3.0/8.0)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]+30.0/8.0)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]-35.0/8.0)); //--- calculation CLegendre::LegendreCoefficients(9,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-315.0/128.0)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]+4620.0/128.0)); cerr=MathMax(cerr,MathAbs(c[4]-0)); cerr=MathMax(cerr,MathAbs(c[5]-18018.0/128.0)); cerr=MathMax(cerr,MathAbs(c[6]-0)); cerr=MathMax(cerr,MathAbs(c[7]+25740.0/128.0)); cerr=MathMax(cerr,MathAbs(c[8]-0)); cerr=MathMax(cerr,MathAbs(c[9]-12155.0/128.0)); //--- Reporting waserrors=(err>threshold || sumerr>threshold) || cerr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS"); Print("Max error "); Print("{0,5:E2}",err); Print("Summation error "); Print("{0,5:E2}",sumerr); Print("Coefficients error "); Print("{0,5:E2}",cerr); Print("Threshold "); Print("{0,5:E2}",threshold); //--- check if(!waserrors) Print("TEST PASSED"); else Print("TEST FAILED"); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary class | //+------------------------------------------------------------------+ class CRec1 { public: //--- class variables bool m_bfield; double m_rfield; int m_ifield; complex m_cfield; //--- arrays bool m_b1field[]; double m_r1field[]; int m_i1field[]; complex m_c1field[]; //--- matrix CMatrixInt m_b2field; CMatrixDouble m_r2field; CMatrixInt m_i2field; CMatrixComplex m_c2field; //--- constructor, destructor CRec1(void); ~CRec1(void); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CRec1::CRec1(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CRec1::~CRec1(void) { } //+------------------------------------------------------------------+ //| The auxiliary class | //+------------------------------------------------------------------+ class CRec4Serialization { public: //--- arrays bool m_b[]; int m_i[]; double m_r[]; //--- constructor, destructor CRec4Serialization(void); ~CRec4Serialization(void); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CRec4Serialization::CRec4Serialization(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CRec4Serialization::~CRec4Serialization(void) { } //+------------------------------------------------------------------+ //| Testing the basic functions | //+------------------------------------------------------------------+ class CTestAlglibBasicsUnit { private: //--- private methods static bool TestComplexArithmetics(const bool silent); static bool TestIEEESpecial(const bool silent); static bool TestSwapFunctions(const bool silent); static bool TestSerializationFunctions(const bool silent); public: //--- constructor, destructor CTestAlglibBasicsUnit(void); ~CTestAlglibBasicsUnit(void); //--- public methods static void Rec4SerializationAlloc(CSerializer &s,CRec4Serialization &v); static void Rec4SerializationSerialize(CSerializer &s,CRec4Serialization &v); static void Rec4SerializationUnserialize(CSerializer &s,CRec4Serialization &v); static bool TestAlglibBasics(const bool silent); }; //+------------------------------------------------------------------+ //| Constructor without parameters | //+------------------------------------------------------------------+ CTestAlglibBasicsUnit::CTestAlglibBasicsUnit(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTestAlglibBasicsUnit::~CTestAlglibBasicsUnit(void) { } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestAlglibBasicsUnit::Rec4SerializationAlloc(CSerializer &s, CRec4Serialization &v) { //--- create a variable int i=0; //--- boolean fields s.Alloc_Entry(); for(i=0;i<=CAp::Len(v.m_b)-1;i++) s.Alloc_Entry(); //--- integer fields s.Alloc_Entry(); for(i=0;i<=CAp::Len(v.m_i)-1;i++) s.Alloc_Entry(); //--- real fields s.Alloc_Entry(); for(i=0;i<=CAp::Len(v.m_r)-1;i++) s.Alloc_Entry(); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestAlglibBasicsUnit::Rec4SerializationSerialize(CSerializer &s, CRec4Serialization &v) { //--- create a variable int i=0; //--- boolean fields s.Serialize_Int(CAp::Len(v.m_b)); for(i=0;i<=CAp::Len(v.m_b)-1;i++) s.Serialize_Bool(v.m_b[i]); //--- integer fields s.Serialize_Int(CAp::Len(v.m_i)); for(i=0;i<=CAp::Len(v.m_i)-1;i++) s.Serialize_Int(v.m_i[i]); //--- real fields s.Serialize_Int(CAp::Len(v.m_r)); for(i=0;i<=CAp::Len(v.m_r)-1;i++) s.Serialize_Double(v.m_r[i]); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ static void CTestAlglibBasicsUnit::Rec4SerializationUnserialize(CSerializer &s, CRec4Serialization &v) { //--- create variables int i=0; int k=0; bool bv; int iv=0; double rv=0; //--- boolean fields k=s.Unserialize_Int(); //--- check if(k>0) { //--- allocation ArrayResize(v.m_b,k); for(i=0;i<=k-1;i++) { bv=s.Unserialize_Bool(); v.m_b[i]=bv; } } //--- integer fields k=s.Unserialize_Int(); //--- check if(k>0) { //--- allocation ArrayResize(v.m_i,k); for(i=0;i<=k-1;i++) { iv=s.Unserialize_Int(); v.m_i[i]=iv; } } //--- real fields k=s.Unserialize_Int(); //--- check if(k>0) { //--- allocation ArrayResize(v.m_r,k); for(i=0;i<=k-1;i++) { rv=s.Unserialize_Double(); v.m_r[i]=rv; } } } //+------------------------------------------------------------------+ //| Testing the basic functions | //+------------------------------------------------------------------+ static bool CTestAlglibBasicsUnit::TestAlglibBasics(const bool silent) { //--- create a variable bool result; //--- function calls result=true; result=result && TestComplexArithmetics(silent); result=result && TestIEEESpecial(silent); result=result && TestSwapFunctions(silent); result=result && TestSerializationFunctions(silent); //--- check if(!silent) Print(""); //--- return result return(result); } //+------------------------------------------------------------------+ //| Complex arithmetics test | //+------------------------------------------------------------------+ static bool CTestAlglibBasicsUnit::TestComplexArithmetics(const bool silent) { //--- create variables bool result; bool absc; bool addcc; bool addcr; bool addrc; bool subcc; bool subcr; bool subrc; bool mulcc; bool mulcr; bool mulrc; bool divcc; bool divcr; bool divrc; complex ca=0; complex cb=0; complex res=0; double ra=0; double rb=0; double threshold=0; int pass=0; int passcount=0; //--- initialization threshold=100*CMath::m_machineepsilon; passcount=1000; result=true; absc=true; addcc=true; addcr=true; addrc=true; subcc=true; subcr=true; subrc=true; mulcc=true; mulcr=true; mulrc=true; divcc=true; divcr=true; divrc=true; //--- calculation for(pass=1;pass<=passcount;pass++) { //--- Test AbsC ca.re=2*CMath::RandomReal()-1; ca.im=2*CMath::RandomReal()-1; ra=CMath::AbsComplex(ca); absc=absc && MathAbs(ra-MathSqrt(CMath::Sqr(ca.re)+CMath::Sqr(ca.im)))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(); //--- summary result=result && oknan; result=result && okinf; result=result && okother; //--- check if(!silent) { //--- check if(result) Print("IEEE SPECIAL VALUES: OK"); else { Print("IEEE SPECIAL VALUES: FAILED"); Print("* NAN "); //--- check if(oknan) Print("OK"); else Print("FAILED"); Print("* INF "); //--- check if(okinf) Print("OK"); else Print("FAILED"); Print("* FUNCTIONS "); //--- check if(okother) Print("OK"); else Print("FAILED"); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests for swapping functions | //+------------------------------------------------------------------+ static bool CTestAlglibBasicsUnit::TestSwapFunctions(const bool silent) { //--- create variables bool result; bool okb1; bool okb2; bool oki1; bool oki2; bool okr1; bool okr2; bool okc1; bool okc2; //--- create arrays bool b11[]; bool b12[]; int i11[]; int i12[]; double r11[]; double r12[]; complex c11[]; complex c12[]; //--- create matrix CMatrixInt b21; CMatrixInt b22; CMatrixInt i21; CMatrixInt i22; CMatrixDouble r21; CMatrixDouble r22; CMatrixComplex c21; CMatrixComplex c22; //--- initialization result=true; okb1=true; okb2=true; oki1=true; oki2=true; okr1=true; okr2=true; okc1=true; okc2=true; //--- Test B1 swaps ArrayResize(b11,1); ArrayResize(b12,2); //--- change values b11[0]=true; b12[0]=false; b12[1]=true; //--- function call CAp::Swap(b11,b12); //--- check if(CAp::Len(b11)==2 && CAp::Len(b12)==1) { okb1=okb1 && !b11[0]; okb1=okb1 && b11[1]; okb1=okb1 && b12[0]; } else okb1=false; //--- Test I1 swaps ArrayResize(i11,1); ArrayResize(i12,2); //--- change values i11[0]=1; i12[0]=2; i12[1]=3; //--- function call CAp::Swap(i11,i12); //--- check if(CAp::Len(i11)==2 && CAp::Len(i12)==1) { oki1=oki1 && i11[0]==2; oki1=oki1 && i11[1]==3; oki1=oki1 && i12[0]==1; } else oki1=false; //--- Test R1 swaps ArrayResize(r11,1); ArrayResize(r12,2); //--- change values r11[0]=1.5; r12[0]=2.5; r12[1]=3.5; //--- function call CAp::Swap(r11,r12); //--- check if(CAp::Len(r11)==2 && CAp::Len(r12)==1) { okr1=okr1 && r11[0]==2.5; okr1=okr1 && r11[1]==3.5; okr1=okr1 && r12[0]==1.5; } else okr1=false; //--- Test C1 swaps ArrayResize(c11,1); ArrayResize(c12,2); //--- change values c11[0]=1; c12[0]=2; c12[1]=3; //--- function call CAp::Swap(c11,c12); //--- check if(CAp::Len(c11)==2 && CAp::Len(c12)==1) { okc1=okc1 && c11[0]==2; okc1=okc1 && c11[1]==3; okc1=okc1 && c12[0]==1; } else okc1=false; //--- Test B2 swaps b21.Resize(1,2); b22.Resize(2,1); //--- change values b21[0].Set(0,true); b21[0].Set(1,false); b22[0].Set(0,false); b22[1].Set(0,true); //--- function call CAp::Swap(b21,b22); //--- check if(((CAp::Rows(b21)==2 && CAp::Cols(b21)==1) && CAp::Rows(b22)==1) && CAp::Cols(b22)==2) { okb2=okb2 && !b21[0][0]; okb2=okb2 && b21[1][0]; okb2=okb2 && b22[0][0]; okb2=okb2 && !b22[0][1]; } else okb2=false; //--- Test I2 swaps i21.Resize(1,2); i22.Resize(2,1); //--- change values i21[0].Set(0,1); i21[0].Set(1,2); i22[0].Set(0,3); i22[1].Set(0,4); //--- function call CAp::Swap(i21,i22); //--- check if(((CAp::Rows(i21)==2 && CAp::Cols(i21)==1) && CAp::Rows(i22)==1) && CAp::Cols(i22)==2) { oki2=oki2 && i21[0][0]==3; oki2=oki2 && i21[1][0]==4; oki2=oki2 && i22[0][0]==1; oki2=oki2 && i22[0][1]==2; } else oki2=false; //--- Test R2 swaps r21.Resize(1,2); r22.Resize(2,1); //--- change values r21[0].Set(0,1); r21[0].Set(1,2); r22[0].Set(0,3); r22[1].Set(0,4); //--- function call CAp::Swap(r21,r22); //--- check if(((CAp::Rows(r21)==2 && CAp::Cols(r21)==1) && CAp::Rows(r22)==1) && CAp::Cols(r22)==2) { okr2=okr2 && r21[0][0]==3.0; okr2=okr2 && r21[1][0]==4.0; okr2=okr2 && r22[0][0]==1.0; okr2=okr2 && r22[0][1]==2.0; } else okr2=false; //--- Test C2 swaps c21.Resize(1,2); c22.Resize(2,1); //--- change values c21[0].Set(0,1); c21[0].Set(1,2); c22[0].Set(0,3); c22[1].Set(0,4); //--- function call CAp::Swap(c21,c22); //--- check if(((CAp::Rows(c21)==2 && CAp::Cols(c21)==1) && CAp::Rows(c22)==1) && CAp::Cols(c22)==2) { okc2=okc2 && c21[0][0]==3; okc2=okc2 && c21[1][0]==4; okc2=okc2 && c22[0][0]==1; okc2=okc2 && c22[0][1]==2; } else okc2=false; //--- summary result=result && okb1; result=result && okb2; result=result && oki1; result=result && oki2; result=result && okr1; result=result && okr2; result=result && okc1; result=result && okc2; //--- check if(!silent) { //--- check if(result) Print("SWAPPING FUNCTIONS: OK"); else Print("SWAPPING FUNCTIONS: FAILED"); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests for swapping functions | //+------------------------------------------------------------------+ static bool CTestAlglibBasicsUnit::TestSerializationFunctions(const bool silent) { //--- create variables bool result; bool okb; bool oki; bool okr; int nb=0; int ni=0; int nr=0; int i=0; //--- objects of classes CRec4Serialization r0; CRec4Serialization r1; //--- initialization result=true; okb=true; oki=true; okr=true; //--- calculation for(nb=1;nb<=4;nb++) { for(ni=1;ni<=4;ni++) { for(nr=1;nr<=4;nr++) { //--- allocation ArrayResize(r0.m_b,nb); for(i=0;i<=nb-1;i++) r0.m_b[i]=CMath::RandomInteger(2)!=0; //--- allocation ArrayResize(r0.m_i,ni); for(i=0;i<=ni-1;i++) r0.m_i[i]=CMath::RandomInteger(10)-5; //--- allocation ArrayResize(r0.m_r,nr); for(i=0;i<=nr-1;i++) r0.m_r[i]=2*CMath::RandomReal()-1; { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CTestAlglibBasicsUnit::Rec4SerializationAlloc(_local_serializer,r0); _local_serializer.SStart_Str(); CTestAlglibBasicsUnit::Rec4SerializationSerialize(_local_serializer,r0); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CTestAlglibBasicsUnit::Rec4SerializationUnserialize(_local_serializer,r1); _local_serializer.Stop(); } //--- check if((CAp::Len(r0.m_b)==CAp::Len(r1.m_b) && CAp::Len(r0.m_i)==CAp::Len(r1.m_i)) && CAp::Len(r0.m_r)==CAp::Len(r1.m_r)) { //--- change value for(i=0;i<=nb-1;i++) okb=okb && ((r0.m_b[i] && r1.m_b[i]) || (!r0.m_b[i] && !r1.m_b[i])); for(i=0;i<=ni-1;i++) oki=oki && r0.m_i[i]==r1.m_i[i]; for(i=0;i<=nr-1;i++) okr=okr && r0.m_r[i]==r1.m_r[i]; } else oki=false; } } } //--- summary result=result && okb; result=result && oki; result=result && okr; //--- check if(!silent) { //--- check if(result) Print("SERIALIZATION FUNCTIONS: OK"); else { Print("SERIALIZATION FUNCTIONS: FAILED"); Print("* BOOLEAN "); //--- check if(okb) Print("OK"); else Print("FAILED"); Print("* INTEGER "); //--- check if(oki) Print("OK"); else Print("FAILED"); Print("* REAL "); //--- check if(okr) Print("OK"); else Print("FAILED"); } } //--- return result return(result); } //+------------------------------------------------------------------+