//+------------------------------------------------------------------+ //| TestClasses.mqh | //| Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) | //| Copyright 2012-2026, MetaQuotes Ltd. | //| www.mql5.com | //+------------------------------------------------------------------+ //| Implementation of ALGLIB library in MetaQuotes Language 5 | //| | //| The features of the library include: | //| - Linear algebra (direct algorithms, EVD, SVD) | //| - Solving systems of linear and non-linear equations | //| - Interpolation | //| - Optimization | //| - FFT (Fast Fourier Transform) | //| - Numerical integration | //| - Linear and nonlinear least-squares fitting | //| - Ordinary differential equations | //| - Computation of special functions | //| - Descriptive statistics and hypothesis testing | //| - Data analysis - classification, regression | //| - Implementing linear algebra algorithms, interpolation, etc. | //| in high-precision arithmetic (using MPFR) | //| | //| This file is free software; you can redistribute it and/or | //| modify it under the terms of the GNU General Public License as | //| published by the Free Software Foundation (www.fsf.org); either | //| version 2 of the License, or (at your option) any later version. | //| | //| This program is distributed in the hope that it will be useful, | //| but WITHOUT ANY WARRANTY; without even the implied warranty of | //| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | //| GNU General Public License for more details. | //+------------------------------------------------------------------+ #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define CheckErrorDiff(val,refval,tol,s) (MathAbs(val-refval)>tol*MathMax(MathAbs(refval),s)) #define PrintResult(function,check) PrintFormat("%-40s: %10s",function,(check ? "PASSED " : "- FAILED -")); //+------------------------------------------------------------------+ //| Testing class CHighQualityRand | //+------------------------------------------------------------------+ class CTestHQRndUnit { public: static bool TestHQRnd(const bool silent); private: static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs); static void UnsetState(CHighQualityRandState &state); }; //+------------------------------------------------------------------+ //| Testing class CHighQualityRand | //+------------------------------------------------------------------+ bool CTestHQRndUnit::TestHQRnd(const bool silent) { bool waserrors; int samplesize=0; double sigmathreshold=0; int passcount=0; int n=0; int i=0; int pass=0; int s1=0; int s2=0; int i1=0; int i2=0; double r1=0; double r2=0; double mean=0; double means=0; double stddev=0; double stddevs=0; double lambdav=0; bool seederrors; bool urerrors; double ursigmaerr=0; bool uierrors; double uisigmaerr=0; bool normerrors; double normsigmaerr=0; bool experrors; double expsigmaerr=0; //--- create array double x[]; //--- object of class CHighQualityRandState state; //--- initialization waserrors=false; sigmathreshold=7; samplesize=100000; passcount=50; seederrors=false; urerrors=false; uierrors=false; normerrors=false; experrors=false; //--- allocation ArrayResize(x,samplesize); //--- Test seed errors for(pass=1; pass<=passcount; pass++) { //--- change values s1=1+CMath::RandomInteger(32000); s2=1+CMath::RandomInteger(32000); //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change value i1=CHighQualityRand::HQRndUniformI(state,100); //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change values i2=CHighQualityRand::HQRndUniformI(state,100); seederrors=seederrors||i1!=i2; //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change value r1=CHighQualityRand::HQRndUniformR(state); //--- function calls UnsetState(state); CHighQualityRand::HQRndSeed(s1,s2,state); //--- change values r2=CHighQualityRand::HQRndUniformR(state); seederrors=seederrors||r1!=r2; } //--- Test HQRNDRandomize() and real uniform generator UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values ursigmaerr=0; for(i=0; i<=samplesize-1; i++) x[i]=CHighQualityRand::HQRndUniformR(state); for(i=0; i<=samplesize-1; i++) urerrors=(urerrors||x[i]<=0.0)||x[i]>=1.0; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) ursigmaerr=MathMax(ursigmaerr,MathAbs((mean-0.5)/means)); else urerrors=true; //--- check if(stddevs!=0.0) ursigmaerr=MathMax(ursigmaerr,MathAbs((stddev-MathSqrt(1.0/12.0))/stddevs)); else urerrors=true; //--- change value urerrors=urerrors||ursigmaerr>sigmathreshold; //--- Test HQRNDRandomize() and integer uniform UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- calculation uisigmaerr=0; for(n=2; n<=10; n++) { for(i=0; i<=samplesize-1; i++) x[i]=CHighQualityRand::HQRndUniformI(state,n); for(i=0; i<=samplesize-1; i++) uierrors=(uierrors||x[i]<0.0)||x[i]>=n; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means)); else uierrors=true; //--- check if(stddevs!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs)); else uierrors=true; } //--- change values uierrors=uierrors||uisigmaerr>sigmathreshold; //--- Special 'close-to-limit' test on uniformity of integers //--- (straightforward implementation like 'RND mod N' will return //--- non-uniform numbers for N=2/3*LIMIT) UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values uisigmaerr=0; n=1431655708; //--- calculation for(i=0; i<=samplesize-1; i++) x[i]=CHighQualityRand::HQRndUniformI(state,n); for(i=0; i<=samplesize-1; i++) uierrors=(uierrors||x[i]<0.0)||x[i]>=n; //--- function call CalculateMV(x,samplesize,mean,means,stddev,stddevs); //--- check if(means!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means)); else uierrors=true; //--- check if(stddevs!=0.0) uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs)); else uierrors=true; uierrors=uierrors||uisigmaerr>sigmathreshold; //--- Test normal UnsetState(state); CHighQualityRand::HQRndRandomize(state); //--- change values normsigmaerr=0; i=0; //--- cycle while(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 PrintResult("SEED TEST",!seederrors); //--- check PrintResult("UNIFORM CONTINUOUS",!urerrors); //--- check PrintResult("UNIFORM INTEGER",!uierrors); //--- check PrintResult("NORMAL",!normerrors); //--- check PrintResult("EXPONENTIAL",!experrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestHQRndUnit::CalculateMV(double &x[],const int n,double &mean, double &means,double &stddev, double &stddevs) { //--- create variables int i=0; double v1=0; double v2=0; double variance=0; //--- initialization mean=0; means=1; stddev=0; stddevs=1; variance=0; //--- check if(n<=1) return; //--- Mean for(i=0; iasorted[i+1]; //--- P1 correctness for(i=0; i=ntotal) { //--- return result return(true); } //--- search errors for(int j=0; j=' comparisons) errtol=100000*CMath::m_machineepsilon; //--- fill tags ArrayResize(tags,n); for(i=0; i0.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_qr[i+1]; } for(i=0; ierrtol; } //--- 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=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_qr[i+1]; } //--- calculation for(i=0; ierrtol; } } //--- 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_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; ithreshold) { err=true; return; } } } //--- function call CNearestNeighbor::KDTreeQueryResultsTags(tree0,tags0); //--- function call CNearestNeighbor::KDTreeQueryResultsTags(tree1,tags1); for(i=0; i0.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=aoffsi && i=aoffsj) && j=xoffsi && j>=xoffsj) { cxr1.Set(i,j,refcxr.Get(i,j)); cxr2.Set(i,j,refcxr.Get(i,j)); rxr1.Set(i,j,refrxr.Get(i,j)); rxr2.Set(i,j,refrxr.Get(i,j)); } else { cxr1.Set(i,j,CMath::RandomReal()); cxr2.Set(i,j,cxr1.Get(i,j)); rxr1.Set(i,j,CMath::RandomReal()); rxr2.Set(i,j,rxr1.Get(i,j)); } } } //--- change values for(i=0; i=xoffsi && j>=xoffsj) { cxl1.Set(i,j,refcxl.Get(i,j)); cxl2.Set(i,j,refcxl.Get(i,j)); rxl1.Set(i,j,refrxl.Get(i,j)); rxl2.Set(i,j,refrxl.Get(i,j)); } else { cxl1.Set(i,j,CMath::RandomReal()); cxl2.Set(i,j,cxl1.Get(i,j)); rxl1.Set(i,j,CMath::RandomReal()); rxl2.Set(i,j,rxl1.Get(i,j)); } } } //--- Test CXR CAblas::CMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr1,xoffsi,xoffsj); RefCMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr2,xoffsi,xoffsj); //--- search errors for(i=0; ithreshold; } //--- 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; ithreshold; } //--- 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; ithreshold; } //--- 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; ithreshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| SYRK tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ bool CTestAblasUnit::TestSyrk(const int minn,const int maxn) { //--- create variables bool result; int n=0; int k=0; int mx=0; int i=0; int j=0; int uppertype=0; int xoffsi=0; int xoffsj=0; int aoffsitype=0; int aoffsjtype=0; int aoffsi=0; int aoffsj=0; int alphatype=0; int betatype=0; double alpha=0; double beta=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrc; CMatrixComplex refca; CMatrixComplex refcc; CMatrixDouble ra1; CMatrixDouble ra2; CMatrixComplex ca1; CMatrixComplex ca2; CMatrixDouble rc; CMatrixDouble rct; CMatrixComplex cc; CMatrixComplex cct; //--- initialization threshold=maxn*100*CMath::m_machineepsilon; result=false; for(mx=minn; mx<=maxn; mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX k=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomReal()>0.5) k=mx; else n=mx; //--- Initialize RefRA/RefCA by random Hermitian matrices, //--- RefRC/RefCC by random matrices //--- RA/CA size is 2Nx2N (four copies of same NxN matrix //--- to test different offsets) refra.Resize(2*n,2*n); refca.Resize(2*n,2*n); for(i=0; i=aoffsi && i=aoffsj) && j=xoffsi && j>=xoffsj) { rc.Set(i,j,refrc.Get(i,j)); rct.Set(j,i,refrc.Get(i,j)); cc.Set(i,j,refcc.Get(i,j)); cct.Set(j,i,refcc.Get(i,j)); } else { rc.Set(i,j,CMath::RandomReal()); rct.Set(j,i,rc.Get(i,j)); cc.Set(i,j,CMath::RandomReal()); cct.Set(j,i,cct[j][i]); } } } //--- Test complex //--- Only one of transform types is selected and tested if(CMath::RandomReal()>0.5) { CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca1,aoffsi,aoffsj,uppertype==0); RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca2,aoffsi,aoffsj,uppertype==0); } else { CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca1,aoffsi,aoffsj,uppertype==0); RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca2,aoffsi,aoffsj,uppertype==0); } //--- search errors for(i=0; ithreshold; } //--- 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; ithreshold; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| GEMM tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ bool CTestAblasUnit::TestGemm(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int k=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int aoptype=0; int aoptyper=0; int boffsi=0; int boffsj=0; int boptype=0; int boptyper=0; int coffsi=0; int coffsj=0; double alphar=0; double betar=0; complex alphac=0; complex betac=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrb; CMatrixDouble refrc; CMatrixComplex refca; CMatrixComplex refcb; CMatrixComplex refcc; CMatrixDouble rc1; CMatrixDouble rc2; CMatrixComplex cc1; CMatrixComplex cc2; //--- initialization threshold=maxn*100*CMath::m_machineepsilon; result=false; //--- calculation for(mx=minn; mx<=maxn; mx++) { //--- Select random M/N/K in [1,MX] such that max(M,N,K)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); k=1+CMath::RandomInteger(mx); i=CMath::RandomInteger(3); //--- check if(i==0) m=mx; //--- check if(i==1) n=mx; //--- check if(i==2) k=mx; //--- Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1) refra.Resize(maxn+1,maxn+1); refrb.Resize(maxn+1,maxn+1); refrc.Resize(maxn+1,maxn+1); refca.Resize(maxn+1,maxn+1); refcb.Resize(maxn+1,maxn+1); refcc.Resize(maxn+1,maxn+1); //--- change values for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) { refra.Set(i,j,2*CMath::RandomReal()-1); refrb.Set(i,j,2*CMath::RandomReal()-1); refrc.Set(i,j,2*CMath::RandomReal()-1); refca.SetRe(i,j,2*CMath::RandomReal()-1); refca.SetIm(i,j,2*CMath::RandomReal()-1); refcb.SetRe(i,j,2*CMath::RandomReal()-1); refcb.SetIm(i,j,2*CMath::RandomReal()-1); refcc.SetRe(i,j,2*CMath::RandomReal()-1); refcc.SetIm(i,j,2*CMath::RandomReal()-1); } } //--- test different types of operations,offsets,and so on... //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. rc1.Resize(maxn+1,maxn+1); rc2.Resize(maxn+1,maxn+1); cc1.Resize(maxn+1,maxn+1); cc2.Resize(maxn+1,maxn+1); //--- initialization aoffsi=CMath::RandomInteger(2); aoffsj=CMath::RandomInteger(2); aoptype=CMath::RandomInteger(3); aoptyper=CMath::RandomInteger(2); boffsi=CMath::RandomInteger(2); boffsj=CMath::RandomInteger(2); boptype=CMath::RandomInteger(3); boptyper=CMath::RandomInteger(2); coffsi=CMath::RandomInteger(2); coffsj=CMath::RandomInteger(2); alphar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1); betar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1); //--- check if(CMath::RandomReal()>0.5) { alphac.real=2*CMath::RandomReal()-1; alphac.imag=2*CMath::RandomReal()-1; } else alphac=0; //--- check if(CMath::RandomReal()>0.5) { betac.real=2*CMath::RandomReal()-1; betac.imag=2*CMath::RandomReal()-1; } else betac=0; //--- copy C for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) { rc1.Set(i,j,refrc.Get(i,j)); rc2.Set(i,j,refrc.Get(i,j)); cc1.Set(i,j,refcc.Get(i,j)); cc2.Set(i,j,refcc.Get(i,j)); } } //--- Test complex CAblas::CMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc1,coffsi,coffsj); RefCMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc2,coffsi,coffsj); //--- search errors for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) result=result||CMath::AbsComplex(cc1.Get(i,j)-cc2.Get(i,j))>threshold; } //--- Test real CAblas::RMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc1,coffsi,coffsj); RefRMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc2,coffsi,coffsj); //--- search errors for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) result=result||MathAbs(rc1.Get(i,j)-rc2.Get(i,j))>threshold; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| transpose tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ bool CTestAblasUnit::TestTrans(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int boffsi=0; int boffsj=0; double v1=0; double v2=0; double threshold=0; //--- create matrix CMatrixDouble refra; CMatrixDouble refrb; CMatrixComplex refca; CMatrixComplex refcb; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn; mx<=maxn; mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX //--- Generate random V1 and V2 which are used to fill //--- RefRB/RefCB with control values. m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- change values v1=CMath::RandomReal(); v2=CMath::RandomReal(); //--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1) //--- Fill B with control values refra.Resize(maxn+1,maxn+1); refrb.Resize(maxn+1,maxn+1); refca.Resize(maxn+1,maxn+1); refcb.Resize(maxn+1,maxn+1); //--- change values for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) { refra.Set(i,j,2*CMath::RandomReal()-1); refca.SetRe(i,j,2*CMath::RandomReal()-1); refca.SetIm(i,j,2*CMath::RandomReal()-1); refrb.Set(i,j,i*v1+j*v2); refcb.Set(i,j,i*v1+j*v2); } } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(2); aoffsj=CMath::RandomInteger(2); boffsi=CMath::RandomInteger(2); boffsj=CMath::RandomInteger(2); //--- function call CAblas::RMatrixTranspose(m,n,refra,aoffsi,aoffsj,refrb,boffsi,boffsj); //--- search errors for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) { //--- check if(((i=boffsi+n) || j=boffsj+m) result=result||MathAbs(refrb.Get(i,j)-(v1*i+v2*j))>threshold; else result=result||MathAbs(refrb.Get(i,j)-refra[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold; } } //--- function call CAblas::CMatrixTranspose(m,n,refca,aoffsi,aoffsj,refcb,boffsi,boffsj); //--- search errors for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) { //--- check if(((i=boffsi+n) || j=boffsj+m) result=result||CMath::AbsComplex(refcb.Get(i,j)-(v1*i+v2*j))>threshold; else result=result||CMath::AbsComplex(refcb.Get(i,j)-refca[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| rank-1tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ bool CTestAblasUnit::TestRank1(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int uoffs=0; int voffs=0; double threshold=0; //--- create arrays double ru[]; double rv[]; complex cu[]; complex cv[]; //--- create matrix CMatrixDouble refra; CMatrixDouble refrb; CMatrixComplex refca; CMatrixComplex refcb; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn; mx<=maxn; mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1) //--- Fill B with control values refra.Resize(maxn+maxn,maxn+maxn); refrb.Resize(maxn+maxn,maxn+maxn); refca.Resize(maxn+maxn,maxn+maxn); refcb.Resize(maxn+maxn,maxn+maxn); //--- change values for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { refra.Set(i,j,2*CMath::RandomReal()-1); refca.SetRe(i,j,2*CMath::RandomReal()-1); refca.SetIm(i,j,2*CMath::RandomReal()-1); refrb.Set(i,j,refra.Get(i,j)); refcb.Set(i,j,refca.Get(i,j)); } } //--- allocation ArrayResize(ru,2*m); ArrayResize(cu,2*m); //--- change values for(i=0; i<=2*m-1; i++) { ru[i]=2*CMath::RandomReal()-1; cu[i].real=2*CMath::RandomReal()-1; cu[i].imag=2*CMath::RandomReal()-1; } //--- allocation ArrayResize(rv,2*n); ArrayResize(cv,2*n); //--- change values for(i=0; i<=2*n-1; i++) { rv[i]=2*CMath::RandomReal()-1; cv[i].real=2*CMath::RandomReal()-1; cv[i].imag=2*CMath::RandomReal()-1; } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(maxn); aoffsj=CMath::RandomInteger(maxn); uoffs=CMath::RandomInteger(m); voffs=CMath::RandomInteger(n); //--- function call CAblas::CMatrixRank1(m,n,refca,aoffsi,aoffsj,cu,uoffs,cv,voffs); //--- search errors for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { //--- check if(((i=aoffsi+m) || j=aoffsj+n) result=result||CMath::AbsComplex(refca.Get(i,j)-refcb.Get(i,j))>threshold; else result=result||CMath::AbsComplex(refca.Get(i,j)-(refcb.Get(i,j)+cu[i-aoffsi+uoffs]*cv[j-aoffsj+voffs]))>threshold; } } //--- function call CAblas::RMatrixRank1(m,n,refra,aoffsi,aoffsj,ru,uoffs,rv,voffs); //--- search errors for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { //--- check if(((i=aoffsi+m) || j=aoffsj+n) result=result||MathAbs(refra.Get(i,j)-refrb.Get(i,j))>threshold; else result=result||MathAbs(refra.Get(i,j)-(refrb.Get(i,j)+ru[i-aoffsi+uoffs]*rv[j-aoffsj+voffs]))>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| MV tests | //| Returns False for passed test,True - for failed | //+------------------------------------------------------------------+ bool CTestAblasUnit::TestMV(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int xoffs=0; int yoffs=0; int opca=0; int opra=0; double threshold=0; double rv1=0; double rv2=0; complex cv1=0; complex cv2=0; int i_=0; int i1_=0; //--- create arrays double rx[]; double ry[]; complex cx[]; complex cy[]; //--- create matrix CMatrixDouble refra; CMatrixComplex refca; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn; mx<=maxn; mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN) //--- Initialize X by random vector with size (MaxN+MaxN) //--- Fill Y by control values refra.Resize(maxn+maxn,maxn+maxn); refca.Resize(maxn+maxn,maxn+maxn); //--- change values for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { refra.Set(i,j,2*CMath::RandomReal()-1); refca.SetRe(i,j,2*CMath::RandomReal()-1); refca.SetIm(i,j,2*CMath::RandomReal()-1); } } //--- allocation ArrayResize(rx,2*maxn); ArrayResize(cx,2*maxn); ArrayResize(ry,2*maxn); ArrayResize(cy,2*maxn); //--- change values for(i=0; i<=2*maxn-1; i++) { rx[i]=2*CMath::RandomReal()-1; cx[i].real=2*CMath::RandomReal()-1; cx[i].imag=2*CMath::RandomReal()-1; ry[i]=i; cy[i]=i; } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(maxn); aoffsj=CMath::RandomInteger(maxn); xoffs=CMath::RandomInteger(maxn); yoffs=CMath::RandomInteger(maxn); opca=CMath::RandomInteger(3); opra=CMath::RandomInteger(2); //--- function call CAblas::CMatrixMVect(m,n,refca,aoffsi,aoffsj,opca,cx,xoffs,cy,yoffs); //--- search errors for(i=0; i<=2*maxn-1; i++) { //--- check if(i=yoffs+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 | //+------------------------------------------------------------------+ bool CTestAblasUnit::TestCopy(const int minn,const int maxn) { //--- create variables bool result; int m=0; int n=0; int mx=0; int i=0; int j=0; int aoffsi=0; int aoffsj=0; int boffsi=0; int boffsj=0; double threshold=0; //--- create matrix CMatrixDouble ra; CMatrixDouble rb; CMatrixComplex ca; CMatrixComplex cb; //--- initialization result=false; threshold=1000*CMath::m_machineepsilon; //--- calculation for(mx=minn; mx<=maxn; mx++) { //--- Select random M/N in [1,MX] such that max(M,N)=MX m=1+CMath::RandomInteger(mx); n=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomInteger(2)==0) m=mx; else n=mx; //--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN) //--- Initialize X by random vector with size (MaxN+MaxN) //--- Fill Y by control values ra.Resize(maxn+maxn,maxn+maxn); ca.Resize(maxn+maxn,maxn+maxn); rb.Resize(maxn+maxn,maxn+maxn); cb.Resize(maxn+maxn,maxn+maxn); //--- change values for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { ra.Set(i,j,2*CMath::RandomReal()-1); ca.SetRe(i,j,2*CMath::RandomReal()-1); ca.SetIm(i,j,2*CMath::RandomReal()-1); rb.Set(i,j,1+2*i+3*j); cb.Set(i,j,1+2*i+3*j); } } //--- test different offsets (zero or one) //--- to avoid unnecessary slowdown we don't test ALL possible //--- combinations of operation types. We just generate one random //--- set of parameters and test it. aoffsi=CMath::RandomInteger(maxn); aoffsj=CMath::RandomInteger(maxn); boffsi=CMath::RandomInteger(maxn); boffsj=CMath::RandomInteger(maxn); //--- function call CAblas::CMatrixCopy(m,n,ca,aoffsi,aoffsj,cb,boffsi,boffsj); //--- search errors for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { //--- check if(((i=boffsi+m) || j=boffsj+n) result=result||cb.Get(i,j)!=1+2*i+3*j; else result=result||CMath::AbsComplex(ca[aoffsi+i-boffsi][aoffsj+j-boffsj]-cb.Get(i,j))>threshold; } } //--- function call CAblas::RMatrixCopy(m,n,ra,aoffsi,aoffsj,rb,boffsi,boffsj); //--- search errors for(i=0; i<=2*maxn-1; i++) { for(j=0; j<=2*maxn-1; j++) { //--- check if(((i=boffsi+m) || j=boffsj+n) result=result||rb.Get(i,j)!=1+2*i+3*j; else result=result||MathAbs(ra[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb.Get(i,j))>threshold; } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ void CTestAblasUnit::RefCMatrixRightTrsM(const int m,const int n, CMatrixComplex &a, const int i1,const int j1, const bool isupper, const bool isunit, const int optype, CMatrixComplex &x, const int i2,const int j2) { //--- create variables int i=0; int j=0; complex vc=0; bool rupper; int i_=0; int i1_=0; //--- create array complex tx[]; //--- create matrix CMatrixComplex a1; CMatrixComplex a2; //--- check if(n*m==0) return; //--- allocation a1.Resize(n,n); for(i=0; i0) { for(i_=0; i_=0; j--) { //--- check if(nounit) { //--- check if(a[j][j]==0) { //--- return result return(false); } //--- change values a.Set(j,j,one/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(j+1j+1) { v=0.0; for(i_=j+1; i_0) { for(i_=0; i_=0; j--) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } //--- change values a.Set(j,j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { v=0.0; for(i_=j+1; i_=i) || (!isupper && j<=i)) { //--- check if(beta==0.0) c.Set(i+ic,j+jc,0); else c.Set(i+ic,j+jc,c[i+ic][j+jc]*beta); } } } //--- check if(alpha==0.0) return; //--- check if(n*k>0) ae.Resize(n,k); //--- change values for(i=0; i0) { vc=0.0; for(i_=0; i_=i) c.Set(i+ic,jc+j,vc+c[ic+i][jc+j]); //--- check if(!isupper && j<=i) c.Set(i+ic,jc+j,vc+c[ic+i][jc+j]); } } } //+------------------------------------------------------------------+ //| Reference SYRK subroutine. | //+------------------------------------------------------------------+ void CTestAblasUnit::RefRMatrixSyrk(const int n,const int k, const double alpha,CMatrixDouble &a, const int ia,const int ja, const int optypea,const double beta, CMatrixDouble &c,const int ic, const int jc,const bool isupper) { //--- create variables int i=0; int j=0; double vr=0; int i_=0; //--- create matrix CMatrixDouble ae; //--- calculation for(i=0; i=i) || (!isupper && j<=i)) { //--- check if(beta==0.0) c.Set(i+ic,j+jc,0); else c.Set(i+ic,j+jc,c[i+ic][j+jc]*beta); } } } //--- check if(alpha==0.0) return; //--- check if(n*k>0) ae.Resize(n,k); //--- change values for(i=0; i0) { vr=0.0; for(i_=0; i_=i) c.Set(i+ic,jc+j,vr+c[ic+i][jc+j]); //--- check if(!isupper && j<=i) c.Set(i+ic,jc+j,vr+c[ic+i][jc+j]); } } } //+------------------------------------------------------------------+ //| Reference GEMM, | //| ALGLIB subroutine | //+------------------------------------------------------------------+ void CTestAblasUnit::RefCMatrixGemm(const int m,const int n, const int k,complex &alpha, CMatrixComplex &a,const int ia, const int ja,const int optypea, CMatrixComplex &b,const int ib, const int jb,const int optypeb, complex &beta,CMatrixComplex &c, const int ic,const int jc) { //--- create variables int i=0; int j=0; complex vc=0; int i_=0; //--- create matrix CMatrixComplex ae; CMatrixComplex be; //--- allocation ae.Resize(m,k); //--- change values for(i=0; i0.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; i0.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; i0.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; i0.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; i0) { for(i_=0; i_threshold; covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp.Get(i,j))>threshold; covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs.Get(i,j))>threshold; } } //--- check if(ctype!=0 && n>0) { for(i=0; i<=kx-1; i++) { //--- search errors covcorrerrors=covcorrerrors||cc.Get(i,cidxx)!=0.0; covcorrerrors=covcorrerrors||cc.Get(cidxx,i)!=0.0; covcorrerrors=covcorrerrors||cp.Get(i,cidxx)!=0.0; covcorrerrors=covcorrerrors||cp.Get(cidxx,i)!=0.0; covcorrerrors=covcorrerrors||cs.Get(i,cidxx)!=0.0; covcorrerrors=covcorrerrors||cs.Get(cidxx,i)!=0.0; } } //--- test cross-covariance/cross-correlation matrix using //--- 2-sample functions as reference point. //--- We also test that coefficients for constant variables //--- are exactly zero. CBaseStat::CovM2(mx,my,n,kx,ky,cc); CBaseStat::PearsonCorrM2(mx,my,n,kx,ky,cp); CBaseStat::SpearmanCorrM2(mx,my,n,kx,ky,cs); for(i=0; i<=kx-1; i++) { for(j=0; j<=ky-1; j++) { //--- check if(n>0) { for(i_=0; i_threshold; covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp.Get(i,j))>threshold; covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs.Get(i,j))>threshold; } } //--- check if(ctype!=0 && n>0) { for(i=0; i<=kx-1; i++) { //--- search errors covcorrerrors=covcorrerrors||cc.Get(i,cidxy)!=0.0; covcorrerrors=covcorrerrors||cp.Get(i,cidxy)!=0.0; covcorrerrors=covcorrerrors||cs.Get(i,cidxy)!=0.0; } for(j=0; j<=ky-1; j++) { //--- search errors covcorrerrors=covcorrerrors||cc.Get(cidxx,j)!=0.0; covcorrerrors=covcorrerrors||cp.Get(cidxx,j)!=0.0; covcorrerrors=covcorrerrors||cs.Get(cidxx,j)!=0.0; } } } } } } //--- Final report waserrors=s1errors||covcorrerrors; //--- check if(!silent) { Print("DESC.STAT TEST"); PrintResult("TOTAL RESULTS",!waserrors); PrintResult("* 1-SAMPLE FUNCTIONALITY",!s1errors); PrintResult("* CORRELATION/COVARIATION",!covcorrerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CBdSS | //+------------------------------------------------------------------+ class CTestBdSSUnit { public: static bool TestBdSS(const bool silent); private: static void Unset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static void Unset1DI(int &a[]); static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors); }; //+------------------------------------------------------------------+ //| Testing class CBdSS | //+------------------------------------------------------------------+ bool CTestBdSSUnit::TestBdSS(const bool silent) { //--- create variables int n=0; int i=0; int j=0; int pass=0; int passcount=0; int maxn=0; int maxnq=0; int tiecount=0; int c1=0; int c0=0; int ni=0; int nc=0; double pal=0; double pbl=0; double par=0; double pbr=0; double cve=0; double cvr=0; int info=0; double threshold=0; double rms=0; double cvrms=0; bool waserrors; bool tieserrors; bool split2errors; bool optimalsplitkerrors; bool splitkerrors; //--- create arrays double a[]; double a0[]; double at[]; double thresholds[]; int c[]; int p1[]; int p2[]; int ties[]; int pt1[]; int pt2[]; double tmp[]; double sortrbuf[]; double sortrbuf2[]; int sortibuf[]; int tiebuf[]; int cntbuf[]; //--- create matrix CMatrixDouble p; //--- initialization waserrors=false; tieserrors=false; split2errors=false; splitkerrors=false; optimalsplitkerrors=false; maxn=100; maxnq=49; passcount=10; //--- Test ties for(n=1; n<=maxn; n++) { for(pass=1; pass<=passcount; pass++) { //--- untied data,test DSTie Unset1DI(p1); Unset1DI(p2); Unset1DI(pt1); Unset1DI(pt2); //--- allocation ArrayResize(a,n); ArrayResize(a0,n); ArrayResize(at,n); ArrayResize(tmp,n); //--- change values a[0]=2*CMath::RandomReal()-1; tmp[0]=CMath::RandomReal(); for(i=1; i1) { for(i=0; i100*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; i1) { for(i=0; i100*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; i100*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; i1) { c0=0; c1=0; for(i=0; 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; } //--- test #2 if(n>2) { c0=1+CMath::RandomInteger(n-1); c1=n-c0; for(i=0; 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); j100*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; i1) { c0=0; c1=0; for(i=0; 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; } } //--- test #2 if(n>2) { c0=1+CMath::RandomInteger(n-1); c1=n-c0; for(i=0; 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; i100*CMath::m_machineepsilon; } cvr=-(nc*c0*MathLog((double)c0/(double)(c0+nc-1))); splitkerrors=splitkerrors||MathAbs(cve-cvr)>100*CMath::m_machineepsilon; } } } } //--- report waserrors=((tieserrors||split2errors)||optimalsplitkerrors)||splitkerrors; //--- check if(!silent) { Print("TESTING BASIC DATASET SUBROUTINES"); PrintResult("TIES",!tieserrors); PrintResult("SPLIT-2",!split2errors); PrintResult("OPTIMAL SPLIT-K",!optimalsplitkerrors); PrintResult("SPLIT-K",!splitkerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets 2D array. | //+------------------------------------------------------------------+ void CTestBdSSUnit::Unset2D(CMatrixComplex &a) { //--- allocation a.Resize(1,1); //--- change value a.Set(0,0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ void CTestBdSSUnit::Unset1D(double &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets 1D array. | //+------------------------------------------------------------------+ void CTestBdSSUnit::Unset1DI(int &a[]) { //--- allocation ArrayResize(a,1); //--- change value a[0]=CMath::RandomInteger(3)-1; } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestBdSSUnit::TestSortResults(double &asorted[],int &p1[], int &p2[],double &aoriginal[], const int n,bool &waserrors) { //--- create variables int i=0; double t=0; //--- create arrays double a2[]; int f[]; //--- allocation ArrayResize(a2,n); ArrayResize(f,n); //--- is set ordered? for(i=0; i<=n-2; i++) waserrors=waserrors||asorted[i]>asorted[i+1]; //--- P1 correctness for(i=0; i0.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; i1) { for(i=0; i1000*CMath::m_machineepsilon; } } } //+------------------------------------------------------------------+ //| Basic test: one-tree forest built using full sample must | //| remember all the training cases | //+------------------------------------------------------------------+ void CTestDForestUnit::BasicTest1(const int nvars,const int nclasses, const int passcount,bool &err) { //--- create variables int pass=0; int npoints=0; int i=0; int j=0; int k=0; double s=0; int info=0; bool hassame; int i_=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDFReport rep; //--- check if(nclasses==1) { //--- only classification tasks return; } //--- calculation for(pass=1; pass<=passcount; pass++) { //--- select number of points if(pass<=3 && passcount>3) npoints=pass; else npoints=100+CMath::RandomInteger(100); //--- Prepare task xy.Resize(npoints,nvars+1); ArrayResize(x,nvars); ArrayResize(y,nclasses); //--- change values for(i=0; i1000*CMath::m_machineepsilon) { err=true; return; } //--- check if(MathAbs(y[(int)MathRound(xy[i][nvars])]-1)>1000*CMath::m_machineepsilon) { //--- not an error if there exists such K,J that XY.Set(k,j,XY[I,J] //--- (may be we just can't distinguish two tied values). //--- definitely error otherwise. hassame=false; for(k=0; k1000*CMath::m_machineepsilon) { err=true; return; } //--- test for good correlation with results if(x[0]<1.0) err=err||y[0]<0.8; //--- check if(x[0]>=1.0 && x[0]<=2.0) err=err||MathAbs(y[1]-(x[0]-1))>0.5; //--- check if(x[0]>2.0) err=err||y[1]<0.8; x[0]=x[0]+0.01; } } } //+------------------------------------------------------------------+ //| Basic test: tests generalization ability on a simple | //| classification task (no noise): | //| * ||x||<1,||y||<1 | //| * x^2+y^2<=0.25 - P(class=0)=1 | //| * x^2+y^2>0.25 - P(class=0)=0 | //+------------------------------------------------------------------+ void CTestDForestUnit::BasicTest3(bool &err) { //--- create variables int pass=0; int passcount=0; int npoints=0; int ntrees=0; int i=0; int j=0; int k=0; double s=0; int info=0; int testgridsize=0; double r=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDFReport rep; //--- initialization passcount=1; testgridsize=50; //--- calculation for(pass=1; pass<=passcount; pass++) { //--- select npoints and ntrees npoints=2000; ntrees=100; //--- Prepare task xy.Resize(npoints,3); ArrayResize(x,2); ArrayResize(y,2); //--- change values for(i=0; i1000*CMath::m_machineepsilon) { err=true; return; } //--- test for good correlation with results r=MathSqrt(CMath::Sqr(x[0])+CMath::Sqr(x[1])); //--- check if(r<0.5*0.5) err=err||y[0]<0.6; //--- check if(r>0.5*1.5) err=err||y[1]<0.6; } } } } //+------------------------------------------------------------------+ //| Basic test: simple regression task without noise: | //| * ||x||<1,||y||<1 | //| * F(x,y)=x^2+y | //+------------------------------------------------------------------+ void CTestDForestUnit::BasicTest4(bool &err) { //--- create variables int pass=0; int passcount=0; int npoints=0; int ntrees=0; int ns=0; int strongc=0; int i=0; int j=0; int info=0; int testgridsize=0; double maxerr=0; double maxerr2=0; double avgerr=0; double avgerr2=0; int cnt=0; double ey=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDecisionForest df2; CDFReport rep; CDFReport rep2; //--- initialization passcount=1; testgridsize=50; //--- calculation for(pass=1; pass<=passcount; pass++) { //--- select npoints and ntrees npoints=5000; ntrees=100; ns=(int)MathRound(0.1*npoints); strongc=1; //--- Prepare task xy.Resize(npoints,3); ArrayResize(x,2); ArrayResize(y,1); //--- change values for(i=0; i0.2; err=err||maxerr2>0.2; err=err||avgerr>0.1; err=err||avgerr2>0.1; } } //+------------------------------------------------------------------+ //| Basic test: extended variable selection leads to better results. | //| Next task CAN be solved without EVS but it is very unlikely. | //| With EVS it can be easily and exactly solved. | //| Task matrix: | //| 1 0 0 0 ... 0 0 | //| 0 1 0 0 ... 0 1 | //| 0 0 1 0 ... 0 2 | //| 0 0 0 1 ... 0 3 | //| 0 0 0 0 ... 1 N-1 | //+------------------------------------------------------------------+ void CTestDForestUnit::BasicTest5(bool &err) { //--- create variables int nvars=0; int npoints=0; int nfeatures=0; int nsample=0; int ntrees=0; int evs=0; int i=0; int j=0; bool eflag; int info=0; int i_=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble xy; //--- objects of classes CDecisionForest df; CDFReport rep; //--- select npoints and ntrees npoints=50; nvars=npoints; ntrees=1; nsample=npoints; evs=2; nfeatures=1; //--- Prepare task xy.Resize(npoints,nvars+1); ArrayResize(x,nvars); ArrayResize(y,1); for(i=0; i1000*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; i1000*CMath::m_machineepsilon) { eflag=true; } } //--- check if(eflag) { err=true; return; } } //+------------------------------------------------------------------+ //| Random normal number | //+------------------------------------------------------------------+ double CTestDForestUnit::RNormal(void) { //--- create variables double result=0; double u=0; double v=0; double s=0; double x1=0; double x2=0; //--- calculation while(true) { u=2*CMath::RandomReal()-1; v=2*CMath::RandomReal()-1; s=CMath::Sqr(u)+CMath::Sqr(v); //--- check if(s>0.0 && s<1.0) { s=MathSqrt(-(2*MathLog(s)/s)); x1=u*s; x2=v*s; //--- break the cycle break; } } //--- check if(x1!=0) result=x1; else result=x2; //--- return result return(result); } //+------------------------------------------------------------------+ //| Random point from sphere | //+------------------------------------------------------------------+ void CTestDForestUnit::RSphere(CMatrixDouble &xy,const int n, const int i) { //--- create variables int j=0; double v=0; int i_=0; //--- change values for(j=0; j=threshold || e2>=threshold) || e3>=threshold; //--- Testing VectorAbsMax,Column/Row AbsMax ArrayResize(x1,6); x1[1]=2.0; x1[2]=0.2; x1[3]=-1.3; x1[4]=0.7; x1[5]=-3.0; //--- search errors amaxerrors=(CBlas::VectorIdxAbsMax(x1,1,5)!=5 || CBlas::VectorIdxAbsMax(x1,1,4)!=1) || CBlas::VectorIdxAbsMax(x1,2,4)!=3; n=30; //--- allocation ArrayResize(x1,n+1); a.Resize(n+1,n+1); for(i=1; i<=n; i++) { for(j=1; j<=n; j++) a.Set(i,j,2*CMath::RandomReal()-1); } //--- calculation was1=false; was2=false; for(pass=1; pass<=1000; pass++) { //--- change values j=1+CMath::RandomInteger(n); i1=1+CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n+1-i1); for(i_=i1; i_<=i2; i_++) x1[i_]=a.Get(i_,j); //--- check if(CBlas::VectorIdxAbsMax(x1,i1,i2)!=CBlas::ColumnIdxAbsMax(a,i1,i2,j)) was1=true; //--- change values i=1+CMath::RandomInteger(n); j1=1+CMath::RandomInteger(n); j2=j1+CMath::RandomInteger(n+1-j1); for(i_=j1; i_<=j2; i_++) x1[i_]=a.Get(i,i_); //--- check if(CBlas::VectorIdxAbsMax(x1,j1,j2)!=CBlas::RowIdxAbsMax(a,j1,j2,i)) was2=true; } //--- search errors amaxerrors=(amaxerrors||was1)||was2; //--- Testing upper Hessenberg 1-norm a.Resize(4,4); ArrayResize(x1,4); a.Set(1,1,2); a.Set(1,2,3); a.Set(1,3,1); a.Set(2,1,4); a.Set(2,2,-5); a.Set(2,3,8); a.Set(3,1,99); a.Set(3,2,3); a.Set(3,3,1); //--- search errors hsnerrors=MathAbs(CBlas::UpperHessenberg1Norm(a,1,3,1,3,x1)-11)>threshold; //--- Testing MatrixVectorMultiply a.Resize(4,6); ArrayResize(x1,4); ArrayResize(x2,3); a.Set(2,3,2); a.Set(2,4,-1); a.Set(2,5,-1); a.Set(3,3,1); a.Set(3,4,-2); a.Set(3,5,2); x1[1]=1; x1[2]=2; x1[3]=1; x2[1]=-1; x2[2]=-1; //--- function calls CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,1.0); CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,1.0); //--- calculation e1=MathAbs(x1[1]+5)+MathAbs(x1[2]-8)+MathAbs(x1[3]+1)+MathAbs(x2[1]+2)+MathAbs(x2[2]+2); x1[1]=1; x1[2]=2; x1[3]=1; x2[1]=-1; x2[2]=-1; //--- function calls CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,0.0); CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,0.0); //--- calculation e2=MathAbs(x1[1]+3)+MathAbs(x1[2]-3)+MathAbs(x1[3]+1)+MathAbs(x2[1]+1)+MathAbs(x2[2]+1); mverrors=e1+e2>=threshold; //--- testing inplace transpose n=10; a.Resize(n+1,n+1); b.Resize(n+1,n+1); ArrayResize(x1,n); for(i=1; i<=n; i++) { for(j=1; j<=n; j++) a.Set(i,j,CMath::RandomReal()); } //--- calculation passcount=10000; was1=false; for(pass=1; pass<=passcount; pass++) { //--- change values i1=1+CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1+1); j1=1+CMath::RandomInteger(n-(i2-i1)); j2=j1+(i2-i1); //--- function calls CBlas::CopyMatrix(a,i1,i2,j1,j2,b,i1,i2,j1,j2); CBlas::InplaceTranspose(b,i1,i2,j1,j2,x1); for(i=i1; i<=i2; i++) { for(j=j1; j<=j2; j++) { //--- check if(a.Get(i,j)!=b[i1+(j-j1)][j1+(i-i1)]) was1=true; } } } //--- change value iterrors=was1; //--- testing copy and transpose n=10; a.Resize(n+1,n+1); b.Resize(n+1,n+1); for(i=1; i<=n; i++) { for(j=1; j<=n; j++) a.Set(i,j,CMath::RandomReal()); } //--- calculation passcount=10000; was1=false; for(pass=1; pass<=passcount; pass++) { //--- change values i1=1+CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1+1); j1=1+CMath::RandomInteger(n); j2=j1+CMath::RandomInteger(n-j1+1); //--- function call CBlas::CopyAndTranspose(a,i1,i2,j1,j2,b,j1,j2,i1,i2); for(i=i1; i<=i2; i++) { for(j=j1; j<=j2; j++) { //--- check if(a.Get(i,j)!=b[j][i]) was1=true; } } } //--- change values cterrors=was1; //--- Testing MatrixMatrixMultiply n=10; a.Resize(2*n+1,2*n+1); b.Resize(2*n+1,2*n+1); c1.Resize(2*n+1,2*n+1); c2.Resize(2*n+1,2*n+1); ArrayResize(x1,n+1); ArrayResize(x2,n+1); for(i=1; i<=2*n; i++) { for(j=1; j<=2*n; j++) { a.Set(i,j,CMath::RandomReal()); b.Set(i,j,CMath::RandomReal()); } } //--- calculation passcount=1000; was1=false; for(pass=1; pass<=passcount; pass++) { for(i=1; i<=2*n; i++) { for(j=1; j<=2*n; j++) { c1.Set(i,j,2.1*i+3.1*j); c2.Set(i,j,c1.Get(i,j)); } } //--- change values l=1+CMath::RandomInteger(n); k=1+CMath::RandomInteger(n); r=1+CMath::RandomInteger(n); i1=1+CMath::RandomInteger(n); j1=1+CMath::RandomInteger(n); i2=1+CMath::RandomInteger(n); j2=1+CMath::RandomInteger(n); i3=1+CMath::RandomInteger(n); j3=1+CMath::RandomInteger(n); trans1=CMath::RandomReal()>0.5; trans2=CMath::RandomReal()>0.5; //--- check if(trans1) { col1=l; row1=k; } else { col1=k; row1=l; } //--- check if(trans2) { col2=k; row2=r; } else { col2=r; row2=k; } //--- change values scl1=CMath::RandomReal(); scl2=CMath::RandomReal(); //--- function calls CBlas::MatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c1,i3,i3+l-1,j3,j3+r-1,scl2,x1); NaiveMatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c2,i3,i3+l-1,j3,j3+r-1,scl2); //--- search errors err=0; for(i=1; i<=l; i++) { for(j=1; j<=r; j++) err=MathMax(err,MathAbs(c1[i3+i-1][j3+j-1]-c2[i3+i-1][j3+j-1])); } //--- check if(err>threshold) { was1=true; break; } } //--- change value mmerrors=was1; //--- report waserrors=(((((n2errors||amaxerrors)||hsnerrors)||mverrors)||iterrors)||cterrors)||mmerrors; //--- check if(!silent) { Print("TESTING BLAS"); PrintResult("VectorNorm2",!n2errors); PrintResult("AbsMax (vector/row/column)",!amaxerrors); PrintResult("UpperHessenberg1Norm",!hsnerrors); PrintResult("MatrixVectorMultiply",!mverrors); PrintResult("InplaceTranspose",!iterrors); PrintResult("CopyAndTranspose",!cterrors); PrintResult("MatrixMatrixMultiply",!mmerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestBlasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a, const int ai1, const int ai2, const int aj1, const int aj2, const bool transa, CMatrixDouble &b, const int bi1, const int bi2, const int bj1, const int bj2, const bool transb, const double alpha, CMatrixDouble &c, const int ci1, const int ci2, const int cj1, const int cj2, const double beta) { //--- create variables int arows=0; int acols=0; int brows=0; int bcols=0; int i=0; int j=0; int k=0; int l=0; int r=0; double v=0; int i_=0; int i1_=0; //--- create arrays double x1[]; double x2[]; //--- Setup if(!transa) { arows=ai2-ai1+1; acols=aj2-aj1+1; } else { arows=aj2-aj1+1; acols=ai2-ai1+1; } //--- check if(!transb) { brows=bi2-bi1+1; bcols=bj2-bj1+1; } else { brows=bj2-bj1+1; bcols=bi2-bi1+1; } //--- check if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!")) return; //--- check if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0) return; //--- change values l=arows; r=bcols; k=acols; //--- allocation ArrayResize(x1,k+1); ArrayResize(x2,k+1); //--- calculation for(i=1; i<=l; i++) { for(j=1; j<=r; j++) { //--- check if(!transa) { //--- check if(!transb) { //--- change values i1_=aj1-bi1; v=0.0; for(i_=bi1; i_<=bi2; i_++) v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_]; } else { //--- change values i1_=aj1-bj1; v=0.0; for(i_=bj1; i_<=bj2; i_++) v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_]; } } else { //--- check if(!transb) { //--- change values i1_=ai1-bi1; v=0.0; for(i_=bi1; i_<=bi2; i_++) v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1]; } else { //--- change values i1_=ai1-bj1; v=0.0; for(i_=bj1; i_<=bj2; i_++) v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1]; } } //--- check if(beta==0.0) c.Set(ci1+i-1,cj1+j-1,alpha*v); else c.Set(ci1+i-1,cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v); } } } //+------------------------------------------------------------------+ //| Testing class CKMeans | //+------------------------------------------------------------------+ class CTestKMeansUnit { public: static bool TestKMeans(const bool silent); private: static void SimpleTest1(const int nvars,const int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors); static void RestartsTest(bool &converrors,bool &restartserrors); static double RNormal(void); static void RSphere(CMatrixDouble &xy,const int n,const int i); }; //+------------------------------------------------------------------+ //| Testing class CKMeans | //+------------------------------------------------------------------+ bool CTestKMeansUnit::TestKMeans(const bool silent) { //--- create variables int nf=0; int maxnf=0; int nc=0; int maxnc=0; int passcount=0; bool waserrors; bool converrors; bool simpleerrors; bool complexerrors; bool othererrors; bool restartserrors; //--- Primary settings maxnf=5; maxnc=5; passcount=10; waserrors=false; converrors=false; othererrors=false; simpleerrors=false; complexerrors=false; restartserrors=false; //--- calculation for(nf=1; nf<=maxnf; nf++) { for(nc=1; nc<=maxnc; nc++) SimpleTest1(nf,nc,passcount,converrors,othererrors,simpleerrors); } RestartsTest(converrors,restartserrors); //--- Final report waserrors=(((converrors||othererrors)||simpleerrors)||complexerrors)||restartserrors; //--- check if(!silent) { Print("K-MEANS TEST"); PrintResult("* CONVERGENCE",!converrors); PrintResult("* SIMPLE TASKS",!simpleerrors); PrintResult("* COMPLEX TASKS",!complexerrors); PrintResult("* OTHER PROPERTIES",!othererrors); PrintResult("* RESTARTS PROPERTIES",!restartserrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Simple test 1: ellipsoid in NF-dimensional space. | //| compare k-means centers with random centers | //+------------------------------------------------------------------+ void CTestKMeansUnit::SimpleTest1(const int nvars,const int nc, int passcount,bool &converrors, bool &othererrors,bool &simpleerrors) { //--- create variables int npoints=0; int majoraxis=0; double v=0; int i=0; int j=0; int info=0; int pass=0; int restarts=0; double ekmeans=0; double erandom=0; double dclosest=0; int cclosest=0; int i_=0; //--- create arrays double tmp[]; int xyc[]; //--- create matrix CMatrixDouble xy; CMatrixDouble c; //--- initialization npoints=nc*100; restarts=5; passcount=10; //--- allocation ArrayResize(tmp,nvars); //--- calculation for(pass=1; pass<=passcount; pass++) { //--- Fill xy.Resize(npoints,nvars); majoraxis=CMath::RandomInteger(nvars); for(i=0; i1 significantly | //| improves quality of results. | //| Subroutine generates random task 3 unit balls in 2D,each with 20 | //| points, separated by 5 units wide gaps,and solves it with | //| Restarts=1 and with Restarts=5. Potential functions are compared,| //| outcome of the trial is either 0 or 1 (depending on what is | //| better). | //| Sequence of 1000 such tasks is olved. If Restarts>1 actually | //| improve quality of solution,sum of outcome will be non-binomial. | //| If it doesn't matter,it will be binomially distributed. | //| P.S. This test was added after report from Gianluca Borello who | //| noticed error in the handling of multiple restarts. | //+------------------------------------------------------------------+ void CTestKMeansUnit::RestartsTest(bool &converrors,bool &restartserrors) { //--- create variables int npoints=0; int nvars=0; int nclusters=0; int clustersize=0; int restarts=0; int passcount=0; double sigmathreshold=0; double p=0; double s=0; int i=0; int j=0; int info=0; int pass=0; double ea=0; double eb=0; double v=0; int i_=0; //--- create matrix CMatrixDouble xy; CMatrixDouble ca; CMatrixDouble cb; //--- create arrays int xyca[]; int xycb[]; double tmp[]; //--- initialization restarts=5; passcount=1000; clustersize=20; nclusters=3; nvars=2; npoints=nclusters*clustersize; sigmathreshold=5; //--- allocation xy.Resize(npoints,nvars); ArrayResize(tmp,nvars); p=0; //--- calculation for(pass=1; pass<=passcount; pass++) { //--- Fill for(i=0; i1 CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,restarts,info,cb,xycb); //--- check if(info<0) { converrors=true; return; } //--- calculation eb=0; for(i=0; i(double)(-sigmathreshold); } //+------------------------------------------------------------------+ //| Random normal number | //+------------------------------------------------------------------+ double CTestKMeansUnit::RNormal(void) { //--- create variables double result=0; double u=0; double v=0; double s=0; double x1=0; double x2=0; //--- calculation while(true) { //--- change values u=2*CMath::RandomReal()-1; v=2*CMath::RandomReal()-1; s=CMath::Sqr(u)+CMath::Sqr(v); //--- check if(s>0.0 && s<1.0) { s=MathSqrt(-(2*MathLog(s)/s)); x1=u*s; x2=v*s; //--- break the cycle break; } } //--- check if(x1==0) result=x2; else result=x1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Random point from sphere | //+------------------------------------------------------------------+ void CTestKMeansUnit::RSphere(CMatrixDouble &xy,const int n,const int i) { //--- create variables int j=0; double v=0; int i_=0; //--- change values for(j=0; jthreshold; //--- check if(!silent) { Print("TESTING HERMITIAN BLAS"); PrintFormat("MV error: %5.3E",mverr); PrintFormat("Threshold: %5.3E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CReflections | //+------------------------------------------------------------------+ class CTestReflectionsUnit { public: static bool TestReflections(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CReflections | //+------------------------------------------------------------------+ bool CTestReflectionsUnit::TestReflections(const bool silent) { //--- create variables bool result; int i=0; int j=0; int n=0; int m=0; int maxmn=0; double tmp=0; double beta=0; double tau=0; double err=0; double mer=0; double mel=0; double meg=0; int pass=0; int passcount=0; double threshold=0; int tasktype=0; double xscale=0; int i_=0; //--- create arrays double x[]; double v[]; double work[]; //--- create matrix CMatrixDouble h; CMatrixDouble a; CMatrixDouble b; CMatrixDouble c; //--- initialization passcount=10; threshold=100*CMath::m_machineepsilon; mer=0; mel=0; meg=0; //--- calculation for(pass=1; pass<=passcount; pass++) { for(n=1; n<=10; n++) { for(m=1; m<=10; m++) { //--- Task n=1+CMath::RandomInteger(10); m=1+CMath::RandomInteger(10); maxmn=MathMax(m,n); //--- Initialize ArrayResize(x,maxmn+1); ArrayResize(v,maxmn+1); ArrayResize(work,maxmn+1); h.Resize(maxmn+1,maxmn+1); a.Resize(maxmn+1,maxmn+1); b.Resize(maxmn+1,maxmn+1); c.Resize(maxmn+1,maxmn+1); //--- GenerateReflection,three tasks are possible: //--- * random X //--- * zero X //--- * non-zero X[1],all other are zeros //--- * random X,near underflow scale //--- * random X,near overflow scale for(tasktype=0; tasktype<=4; tasktype++) { xscale=1; //--- check if(tasktype==0) { for(i=1; i<=n; i++) x[i]=2*CMath::RandomReal()-1; } //--- check if(tasktype==1) { for(i=1; i<=n; i++) x[i]=0; } //--- check if(tasktype==2) { x[1]=2*CMath::RandomReal()-1; for(i=2; i<=n; i++) x[i]=0; } //--- check if(tasktype==3) { for(i=1; i<=n; i++) x[i]=(CMath::RandomInteger(21)-10)*CMath::m_minrealnumber; xscale=10*CMath::m_minrealnumber; } //--- check if(tasktype==4) { for(i=1; i<=n; i++) x[i]=(2*CMath::RandomReal()-1)*CMath::m_maxrealnumber; xscale=CMath::m_maxrealnumber; } for(i_=1; i_<=n; i_++) { v[i_]=x[i_]; } //--- function call CReflections::GenerateReflection(v,n,tau); //--- change values beta=v[1]; v[1]=1; for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { //--- check if(i==j) h.Set(i,j,1-tau*v[i]*v[j]); else h.Set(i,j,-(tau*v[i]*v[j])); } } //--- calculation err=0; for(i=1; i<=n; i++) { tmp=0.0; for(i_=1; i_<=n; i_++) tmp+=h.Get(i,i_)*x[i_]; //--- check if(i==1) err=MathMax(err,MathAbs(tmp-beta)); else err=MathMax(err,MathAbs(tmp)); } meg=MathMax(meg,err/xscale); } //--- ApplyReflectionFromTheLeft for(i=1; i<=m; i++) { x[i]=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { a.Set(i,j,2*CMath::RandomReal()-1); b.Set(i,j,a.Get(i,j)); } } //--- function call CReflections::GenerateReflection(v,m,tau); beta=v[1]; v[1]=1; //--- function call CReflections::ApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work); //--- change values for(i=1; i<=m; i++) { for(j=1; j<=m; j++) { //--- check if(i==j) h.Set(i,j,1-tau*v[i]*v[j]); else h.Set(i,j,-(tau*v[i]*v[j])); } } //--- calculation for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { tmp=0.0; for(i_=1; i_<=m; i_++) tmp+=h.Get(i,i_)*a.Get(i_,j); c.Set(i,j,tmp); } } //--- change value err=0; for(i=1; i<=m; i++) { for(j=1; j<=n; j++) err=MathMax(err,MathAbs(b.Get(i,j)-c.Get(i,j))); } mel=MathMax(mel,err); //--- ApplyReflectionFromTheRight for(i=1; i<=n; i++) { x[i]=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { a.Set(i,j,2*CMath::RandomReal()-1); b.Set(i,j,a.Get(i,j)); } } //--- function call CReflections::GenerateReflection(v,n,tau); beta=v[1]; v[1]=1; //--- function call CReflections::ApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work); //--- change value for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { //--- check if(i==j) h.Set(i,j,1-tau*v[i]*v[j]); else h.Set(i,j,-(tau*v[i]*v[j])); } } //--- calculation for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { tmp=0.0; for(i_=1; i_<=n; i_++) tmp+=a.Get(i,i_)*h.Get(i_,j); c.Set(i,j,tmp); } } err=0; for(i=1; i<=m; i++) { for(j=1; j<=n; j++) err=MathMax(err,MathAbs(b.Get(i,j)-c.Get(i,j))); } mer=MathMax(mer,err); } } } //--- Overflow crash test ArrayResize(x,11); ArrayResize(v,11); for(i=1; i<=10; i++) v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1); //--- function call CReflections::GenerateReflection(v,10,tau); result=(meg<=threshold && mel<=threshold) && mer<=threshold; //--- check if(!silent) { Print("TESTING REFLECTIONS"); PrintFormat("Pass count is %d",passcount); PrintFormat("Generate absolute error is %5.3E",meg); PrintFormat("Apply(Left) absolute error is %5.3E",mel); PrintFormat("Apply(Right) absolute error is %5.3E",mer); Print("Overflow crash test passed"); //--- check PrintResult("TEST SUMMARY",result); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CComplexReflections | //+------------------------------------------------------------------+ class CTestCReflectionsUnit { public: static bool TestCReflections(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CComplexReflections | //+------------------------------------------------------------------+ bool CTestCReflectionsUnit::TestCReflections(const bool silent) { //--- create variables int i=0; int j=0; int n=0; int m=0; int maxmn=0; complex tmp=0; complex beta=0; complex tau=0; double err=0; double mer=0; double mel=0; double meg=0; int pass=0; int passcount=0; bool waserrors; double threshold=0; int i_=0; //--- create arrays complex x[]; complex v[]; complex work[]; //--- create matrix CMatrixComplex h; CMatrixComplex a; CMatrixComplex b; CMatrixComplex c; //--- initialization threshold=1000*CMath::m_machineepsilon; passcount=1000; mer=0; mel=0; meg=0; //--- calculation for(pass=1; pass<=passcount; pass++) { //--- Task n=1+CMath::RandomInteger(10); m=1+CMath::RandomInteger(10); maxmn=MathMax(m,n); //--- Initialize ArrayResize(x,maxmn+1); ArrayResize(v,maxmn+1); ArrayResize(work,maxmn+1); h.Resize(maxmn+1,maxmn+1); a.Resize(maxmn+1,maxmn+1); b.Resize(maxmn+1,maxmn+1); c.Resize(maxmn+1,maxmn+1); //--- GenerateReflection for(i=1; i<=n; i++) { x[i].real=2*CMath::RandomReal()-1; x[i].imag=2*CMath::RandomReal()-1; v[i]=x[i]; } //--- function call CComplexReflections::ComplexGenerateReflection(v,n,tau); //--- change values beta=v[1]; v[1]=1; for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { //--- check if(i==j) h.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1); else h.Set(i,j,-(tau*v[i]*CMath::Conj(v[j]))); } } //--- calculation err=0; for(i=1; i<=n; i++) { tmp=0.0; for(i_=1; i_<=n; i_++) tmp+=CMath::Conj(h.Get(i_,i))*x[i_]; //--- check if(i==1) err=MathMax(err,CMath::AbsComplex(tmp-beta)); else err=MathMax(err,CMath::AbsComplex(tmp)); } err=MathMax(err,MathAbs(beta.imag)); meg=MathMax(meg,err); //--- ApplyReflectionFromTheLeft for(i=1; i<=m; i++) { x[i].real=2*CMath::RandomReal()-1; x[i].imag=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { a.SetRe(i,j,2*CMath::RandomReal()-1); a.SetIm(i,j,2*CMath::RandomReal()-1); b.Set(i,j,a.Get(i,j)); } } //--- function call CComplexReflections::ComplexGenerateReflection(v,m,tau); beta=v[1]; v[1]=1; //--- function call CComplexReflections::ComplexApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work); //--- change values for(i=1; i<=m; i++) { for(j=1; j<=m; j++) { //--- check if(i==j) h.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1); else h.Set(i,j,-(tau*v[i]*CMath::Conj(v[j]))); } } //--- calculation for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { tmp=0.0; for(i_=1; i_<=m; i_++) tmp+=h.Get(i,i_)*a.Get(i_,j); c.Set(i,j,tmp); } } err=0; for(i=1; i<=m; i++) { for(j=1; j<=n; j++) err=MathMax(err,CMath::AbsComplex(b.Get(i,j)-c.Get(i,j))); } mel=MathMax(mel,err); //--- ApplyReflectionFromTheRight for(i=1; i<=n; i++) { x[i]=2*CMath::RandomReal()-1; v[i]=x[i]; } for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { a.Set(i,j,2*CMath::RandomReal()-1); b.Set(i,j,a.Get(i,j)); } } //--- function call CComplexReflections::ComplexGenerateReflection(v,n,tau); beta=v[1]; v[1]=1; //--- function call CComplexReflections::ComplexApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work); //--- change values for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { //--- check if(i==j) h.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1); else h.Set(i,j,-(tau*v[i]*CMath::Conj(v[j]))); } } //--- calculation for(i=1; i<=m; i++) { for(j=1; j<=n; j++) { tmp=0.0; for(i_=1; i_<=n; i_++) tmp+=a.Get(i,i_)*h.Get(i_,j); c.Set(i,j,tmp); } } err=0; for(i=1; i<=m; i++) { for(j=1; j<=n; j++) err=MathMax(err,CMath::AbsComplex(b.Get(i,j)-c.Get(i,j))); } mer=MathMax(mer,err); } //--- Overflow crash test ArrayResize(x,11); ArrayResize(v,11); for(i=1; i<=10; i++) v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1); //--- function call CComplexReflections::ComplexGenerateReflection(v,10,tau); //--- report waserrors=(meg>threshold || mel>threshold) || mer>threshold; //--- check if(!silent) { Print("TESTING COMPLEX REFLECTIONS"); PrintFormat("Generate error: %5.3E",meg); PrintFormat("Apply(L) error: %5.3E",mel); PrintFormat("Apply(R) error: %5.3E",mer); PrintFormat("Threshold: %5.3E",threshold); PrintResult("Overflow crash test",true); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CSblas | //+------------------------------------------------------------------+ class CTestSblasUnit { public: static bool TestSblas(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CSblas | //+------------------------------------------------------------------+ bool CTestSblasUnit::TestSblas(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int j=0; int i1=0; int i2=0; bool waserrors; double mverr=0; double threshold=0; double alpha=0; double v=0; int i_=0; int i1_=0; //--- create arrays double x[]; double y1[]; double y2[]; double y3[]; //--- create matrix CMatrixDouble a; CMatrixDouble ua; CMatrixDouble la; //--- initialization mverr=0; waserrors=false; maxn=10; threshold=1000*CMath::m_machineepsilon; //--- Test MV for(n=2; n<=maxn; n++) { //--- allocation a.Resize(n+1,n+1); ua.Resize(n+1,n+1); la.Resize(n+1,n+1); ArrayResize(x,n+1); ArrayResize(y1,n+1); ArrayResize(y2,n+1); ArrayResize(y3,n+1); //--- fill A,UA,LA for(i=1; i<=n; i++) { a.Set(i,i,2*CMath::RandomReal()-1); for(j=i+1; j<=n; j++) { a.Set(i,j,2*CMath::RandomReal()-1); a.Set(j,i,a.Get(i,j)); } } //--- change values for(i=1; i<=n; i++) { for(j=1; j<=n; j++) ua.Set(i,j,0); } //--- change values for(i=1; i<=n; i++) { for(j=i; j<=n; j++) ua.Set(i,j,a.Get(i,j)); } //--- change values for(i=1; i<=n; i++) { for(j=1; j<=n; j++) la.Set(i,j,0); } //--- change values for(i=1; i<=n; i++) { for(j=1; j<=i; j++) la.Set(i,j,a.Get(i,j)); } //--- test on different I1,I2 for(i1=1; i1<=n; i1++) { for(i2=i1; i2<=n; i2++) { //--- Fill X,choose Alpha for(i=1; i<=i2-i1+1; i++) x[i]=2*CMath::RandomReal()-1; alpha=2*CMath::RandomReal()-1; //--- calculate A*x,UA*x,LA*x for(i=i1; i<=i2; i++) { i1_=1-i1; v=0.0; for(i_=i1; i_<=i2; i_++) v+=a.Get(i,i_)*x[i_+i1_]; y1[i-i1+1]=alpha*v; } //--- function call CSblas::SymmetricMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2); //--- function call CSblas::SymmetricMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3); //--- Calculate error for(i_=1; i_<=i2-i1+1; i_++) y2[i_]=y2[i_]-y1[i_]; v=0.0; for(i_=1; i_<=i2-i1+1; i_++) v+=y2[i_]*y2[i_]; //--- search errors mverr=MathMax(mverr,MathSqrt(v)); for(i_=1; i_<=i2-i1+1; i_++) y3[i_]=y3[i_]-y1[i_]; v=0.0; for(i_=1; i_<=i2-i1+1; i_++) v+=y3[i_]*y3[i_]; //--- search errors mverr=MathMax(mverr,MathSqrt(v)); } } } //--- report waserrors=mverr>threshold; //--- check if(!silent) { Print("TESTING SYMMETRIC BLAS"); PrintFormat("MV error: %5.3E",mverr); PrintFormat("Threshold: %5.3E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class COrtFac | //+------------------------------------------------------------------+ class CTestOrtFacUnit { public: static bool TestOrtFac(const bool silent); private: static double RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n); static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity); static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity); static void InternalMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2); static void TestRQRProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &qrerrors); static void TestCQRProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &qrerrors); static void TestRLQProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &lqerrors); static void TestCLQProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &lqerrors); static void TestRBdProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &bderrors); static void TestRHessProblem(CMatrixDouble &a,const int n,const double threshold,bool &hesserrors); static void TestRTdProblem(CMatrixDouble &a,const int n,const double threshold,bool &tderrors); static void TestCTdProblem(CMatrixComplex &a,const int n,const double threshold,bool &tderrors); }; //+------------------------------------------------------------------+ //| Main unittest subroutine | //+------------------------------------------------------------------+ bool CTestOrtFacUnit::TestOrtFac(const bool silent) { int maxmn=0; double threshold=0; int passcount=0; int mx=0; int m=0; int n=0; int pass=0; int i=0; int j=0; bool rqrerrors; bool rlqerrors; bool cqrerrors; bool clqerrors; bool rbderrors; bool rhesserrors; bool rtderrors; bool ctderrors; bool waserrors; //--- create matrix CMatrixDouble ra; CMatrixComplex ca; //--- initialization waserrors=false; rqrerrors=false; rlqerrors=false; cqrerrors=false; clqerrors=false; rbderrors=false; rhesserrors=false; rtderrors=false; ctderrors=false; maxmn=3*CAblas::AblasBlockSize()+1; passcount=1; threshold=5*1000*CMath::m_machineepsilon; //--- Different problems for(mx=1; mx<=maxmn; mx++) { for(pass=1; pass<=passcount; pass++) { //--- Rectangular factorizations: QR,LQ,bidiagonal //--- Matrix types: zero,dense,sparse n=1+CMath::RandomInteger(mx); m=1+CMath::RandomInteger(mx); //--- check if(CMath::RandomReal()>0.5) n=mx; else m=mx; //--- allocation ra.Resize(m,n); ca.Resize(m,n); //--- change values for(i=0; i::Zeros(mx,mx); ca=matrix::Zeros(mx,mx); //--- function calls TestRTdProblem(ra,mx,threshold,rtderrors); TestCTdProblem(ca,mx,threshold,ctderrors); //--- change values for(i=0; i=sparcity) a.Set(i,j,2*CMath::RandomReal()-1); else a.Set(i,j,0); } } } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ void CTestOrtFacUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- calculation for(i=0; i=sparcity) { a.SetRe(i,j,2*CMath::RandomReal()-1); a.SetIm(i,j,2*CMath::RandomReal()-1); } else a.Set(i,j,0); } } } //+------------------------------------------------------------------+ //| Matrix multiplication | //+------------------------------------------------------------------+ void CTestOrtFacUnit::InternalMatrixMatrixMultiply(CMatrixDouble &a, const int ai1, const int ai2, const int aj1, const int aj2, const bool transa, CMatrixDouble &b, const int bi1, const int bi2, const int bj1, const int bj2, const bool transb, CMatrixDouble &c, const int ci1, const int ci2, const int cj1, const int cj2) { //--- create variables int arows=0; int acols=0; int brows=0; int bcols=0; int crows=0; int ccols=0; int i=0; int j=0; int k=0; int l=0; int r=0; double v=0; double beta=0; double alpha=0; int i_=0; int i1_=0; //--- create array double work[]; //--- Pre-setup k=MathMax(ai2-ai1+1,aj2-aj1+1); k=MathMax(k,bi2-bi1+1); k=MathMax(k,bj2-bj1+1); //--- allocation ArrayResize(work,k+1); //--- initialization beta=0; alpha=1; //--- Setup if(!transa) { arows=ai2-ai1+1; acols=aj2-aj1+1; } else { arows=aj2-aj1+1; acols=ai2-ai1+1; } //--- check if(!transb) { brows=bi2-bi1+1; bcols=bj2-bj1+1; } else { brows=bj2-bj1+1; bcols=bi2-bi1+1; } //--- check if(!CAp::Assert(acols==brows,"MatrixMatrixMultiply: incorrect matrix sizes!")) return; //--- check if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0) return; //--- change values crows=arows; ccols=bcols; //--- Test WORK i=MathMax(arows,acols); i=MathMax(brows,i); i=MathMax(i,bcols); work[1]=0; work[i]=i; //--- Prepare C if(beta==0.0) { for(i=ci1; i<=ci2; i++) { for(j=cj1; j<=cj2; j++) c.Set(i,j,0); } } else { for(i=ci1; i<=ci2; i++) { for(i_=cj1; i_<=cj2; i_++) c.Set(i,i_,beta*c.Get(i,i_)); } } //--- A*B if(!transa && !transb) { for(l=ai1; l<=ai2; l++) { for(r=bi1; r<=bi2; r++) { //--- change values v=alpha*a[l][aj1+r-bi1]; k=ci1+l-ai1; i1_=bj1-cj1; //--- calculation for(i_=cj1; i_<=cj2; i_++) c.Set(k,i_,c[k][i_]+v*b[r][i_+i1_]); } } return; } //--- A*B' if(!transa && transb) { //--- check if(arows*acolsthreshold; } } //--- search errors for(i=0; i=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; i10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestCQRProblem(CMatrixComplex &a,const int m, const int n,const double threshold, bool &qrerrors) { //--- create variables int i=0; int j=0; int k=0; complex v=0; int i_=0; //--- create array complex taub[]; //--- create matrix CMatrixComplex b; CMatrixComplex q; CMatrixComplex r; CMatrixComplex q2; //--- Test decompose-and-unpack error CMatrixMakeACopy(a,m,n,b); //--- function calls COrtFac::CMatrixQR(b,m,n,taub); COrtFac::CMatrixQRUnpackQ(b,m,n,taub,m,q); COrtFac::CMatrixQRUnpackR(b,m,n,r); //--- search errors for(i=0; ithreshold; } } //--- search errors for(i=0; i=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; i10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestRLQProblem(CMatrixDouble &a,const int m, const int n,const double threshold, bool &lqerrors) { //--- create variables int i=0; int j=0; int k=0; double v=0; int i_=0; //--- create array double taub[]; //--- create matrix CMatrixDouble b; CMatrixDouble q; CMatrixDouble l; CMatrixDouble q2; //--- Test decompose-and-unpack error RMatrixMakeACopy(a,m,n,b); //--- function calls COrtFac::RMatrixLQ(b,m,n,taub); COrtFac::RMatrixLQUnpackQ(b,m,n,taub,n,q); COrtFac::RMatrixLQUnpackL(b,m,n,l); //--- search errors for(i=0; i=threshold; } } //--- search errors for(i=0; i=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; i10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestCLQProblem(CMatrixComplex &a,const int m, const int n,const double threshold, bool &lqerrors) { //--- create variables int i=0; int j=0; int k=0; complex v=0; int i_=0; //--- create array complex taub[]; //--- create matrix CMatrixComplex b; CMatrixComplex q; CMatrixComplex l; CMatrixComplex q2; //--- Test decompose-and-unpack error CMatrixMakeACopy(a,m,n,b); //--- function calls COrtFac::CMatrixLQ(b,m,n,taub); COrtFac::CMatrixLQUnpackQ(b,m,n,taub,n,q); COrtFac::CMatrixLQUnpackL(b,m,n,l); //--- search errors for(i=0; i=threshold; } } //--- search errors for(i=0; i=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; i10*CMath::m_machineepsilon; } } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestRBdProblem(CMatrixDouble &a,const int m, const int n,const double threshold, bool &bderrors) { //--- create variables int i=0; int j=0; int k=0; bool up; double v=0; int mtsize=0; int i_=0; //--- create arrays double taup[]; double tauq[]; double d[]; double e[]; //--- create matrix CMatrixDouble t; CMatrixDouble pt; CMatrixDouble q; CMatrixDouble r; CMatrixDouble bd; CMatrixDouble x; CMatrixDouble r1; CMatrixDouble r2; //--- Bidiagonal decomposition error RMatrixMakeACopy(a,m,n,t); //--- function calls COrtFac::RMatrixBD(t,m,n,tauq,taup); COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,m,q); COrtFac::RMatrixBDUnpackPT(t,m,n,taup,n,pt); COrtFac::RMatrixBDUnpackDiagonals(t,m,n,up,d,e); //--- allocation bd.Resize(m,n); //--- change values for(i=0; ithreshold; } } //--- Orthogonality test for Q/PT for(i=0; ithreshold; else bderrors=bderrors||MathAbs(v)>threshold; } } //--- search errors for(i=0; ithreshold; 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; i10*CMath::m_machineepsilon; } k=1+CMath::RandomInteger(n); //--- function call COrtFac::RMatrixBDUnpackPT(t,m,n,taup,k,r); //--- search errors for(i=0; ithreshold; //--- function calls RMatrixMakeACopy(x,mtsize,m,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,true,r1,0,mtsize-1,0,m-1); RMatrixMakeACopy(x,mtsize,m,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,true); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,m)>threshold; //--- function calls RMatrixMakeACopy(x,m,mtsize,r); InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,false,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1); RMatrixMakeACopy(x,m,mtsize,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,false); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,m,mtsize)>threshold; //--- function calls RMatrixMakeACopy(x,m,mtsize,r); InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,true,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1); RMatrixMakeACopy(x,m,mtsize,r2); COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,true); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,m,mtsize)>threshold; //--- function calls RMatrixMakeACopy(x,mtsize,n,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,true,r1,0,mtsize-1,0,n-1); RMatrixMakeACopy(x,mtsize,n,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,false); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,n)>threshold; //--- function calls RMatrixMakeACopy(x,mtsize,n,r); InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,false,r1,0,mtsize-1,0,n-1); RMatrixMakeACopy(x,mtsize,n,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,true); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,mtsize,n)>threshold; //--- function calls RMatrixMakeACopy(x,n,mtsize,r); InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,true,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1); RMatrixMakeACopy(x,n,mtsize,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,false); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,n,mtsize)>threshold; //--- function calls RMatrixMakeACopy(x,n,mtsize,r); InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,false,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1); RMatrixMakeACopy(x,n,mtsize,r2); COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,true); //--- search errors bderrors=bderrors||RMatrixDiff(r1,r2,n,mtsize)>threshold; } //+------------------------------------------------------------------+ //| Problem testing | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestRHessProblem(CMatrixDouble &a,const int n, const double threshold,bool &hesserrors) { //--- create variables int i=0; int j=0; double v=0; int i_=0; //--- create array double tau[]; //--- create matrix CMatrixDouble b; CMatrixDouble h; CMatrixDouble q; CMatrixDouble t1; CMatrixDouble t2; //--- function call RMatrixMakeACopy(a,n,n,b); //--- Decomposition COrtFac::RMatrixHessenberg(b,n,tau); COrtFac::RMatrixHessenbergUnpackQ(b,n,tau,q); COrtFac::RMatrixHessenbergUnpackH(b,n,h); //--- Matrix properties for(i=0; ithreshold; } } //--- search errors for(i=0; ithreshold; } //+------------------------------------------------------------------+ //| Tridiagonal tester | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestRTdProblem(CMatrixDouble &a,const int n, const double threshold,bool &tderrors) { //--- create variables int i=0; int j=0; double v=0; int i_=0; //--- create arrays CRowDouble tau; CRowDouble d; CRowDouble e; //--- create matrix CMatrixDouble ua; CMatrixDouble la; CMatrixDouble t; CMatrixDouble q; CMatrixDouble t2; CMatrixDouble t3; //--- allocation ua=a.TriU()+0; la=a.TriL()+0; t=matrix::Zeros(n,n); q=matrix::Zeros(n,n); t2=matrix::Zeros(n,n); t3=matrix::Zeros(n,n); //--- Test 2tridiagonal: upper COrtFac::SMatrixTD(ua,n,true,tau,d,e); COrtFac::SMatrixTDUnpackQ(ua,n,true,tau,q); //--- change values t.Diag(d); t.Diag(e,1); t.Diag(e,-1); //--- calculation t2=q.Transpose()+0; t2=t2.MatMul(a)+0; t3=t2.MatMul(q)+0; //--- search errors for(i=0; ithreshold; } //--- search errors for(i=0; ithreshold; } } //--- Test 2tridiagonal: lower COrtFac::SMatrixTD(la,n,false,tau,d,e); COrtFac::SMatrixTDUnpackQ(la,n,false,tau,q); //--- change values t.Diag(d); t.Diag(e,1); t.Diag(e,-1); //--- calculation t2=q.Transpose()+0; t2=t2.MatMul(a)+0; t3=t2.MatMul(q)+0; //--- search errors for(i=0; ithreshold; } //--- search errors for(i=0; ithreshold; } } } //+------------------------------------------------------------------+ //| Hermitian problem tester | //+------------------------------------------------------------------+ void CTestOrtFacUnit::TestCTdProblem(CMatrixComplex &a,const int n, const double threshold,bool &tderrors) { //--- create variables int i=0; int j=0; complex v=0; int i_=0; //--- create arrays CRowComplex tau; CRowDouble d; CRowDouble e; //--- create matrix CMatrixComplex ua; CMatrixComplex la; CMatrixComplex t; CMatrixComplex q; CMatrixComplex t2; CMatrixComplex t3; //--- allocation ua=a.TriU()+0; la=a.TriL()+0; t=matrix::Full(n,n,0); q=matrix::Full(n,n,0); t2=matrix::Full(n,n,0); t3=matrix::Full(n,n,0); //--- Test 2tridiagonal: upper COrtFac::HMatrixTD(ua,n,true,tau,d,e); COrtFac::HMatrixTDUnpackQ(ua,n,true,tau,q); //--- change values for(i=0; ithreshold; } //--- search errors for(i=0; ithreshold; } } //--- Test 2tridiagonal: lower COrtFac::HMatrixTD(la,n,false,tau,d,e); COrtFac::HMatrixTDUnpackQ(la,n,false,tau,q); //--- change values for(i=0; ithreshold; } //--- search errors for(i=0; ithreshold; } } } //+------------------------------------------------------------------+ //| Testing class CEigenVDetect | //+------------------------------------------------------------------+ class CTestEVDUnit { public: static bool TestEVD(const bool silent); private: static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity); static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity); static void RMatrixSymmetricSplit(CMatrixDouble &a,const int n,CMatrixDouble &al,CMatrixDouble &au); static void CMatrixHermitianSplit(CMatrixComplex &a,const int n,CMatrixComplex &al,CMatrixComplex &au); static void Unset2D(CMatrixDouble &a); static void CUnset2D(CMatrixComplex &a); static void Unset1D(double &a[]); static void CUnset1D(complex &a[]); static double TdTestProduct(double &d[],double &e[],const int n,CMatrixDouble &z,double &lambdav[]); static double TestProduct(CMatrixDouble &a,const int n,CMatrixDouble &z,double &lambdav[]); static double TestOrt(CMatrixDouble &z,const int n); static double TestCProduct(CMatrixComplex &a,const int n,CMatrixComplex &z,double &lambdav[]); static double TestCOrt(CMatrixComplex &z,const int n); static void TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,CMatrixDouble &au,const int n,const double threshold,bool &serrors,int &failc,int &runs); static void TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,CMatrixComplex &au,const int n,const double threshold,bool &herrors,int &failc,int &runs); static void TestSEVDBiProblem(CMatrixDouble &afull,CMatrixDouble &al,CMatrixDouble &au,const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs); static void TestHEVDBiProblem(CMatrixComplex &afull,CMatrixComplex &al,CMatrixComplex &au,const int n,const bool distvals,const double threshold,bool &herrors,int &failc,int &runs); static void TestTdEVDProblem(double &d[],double &e[],const int n,const double threshold,bool &tderrors,int &failc,int &runs); static void TestTdEVDBiProblem(double &d[],double &e[],const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs); static void TestNSEVDProblem(CMatrixDouble &a,const int n,const double threshold,bool &nserrors,int &failc,int &runs); static void TestEVDSet(const int n,const double threshold,double bithreshold,int &failc,int &runs,bool &nserrors,bool &serrors,bool &herrors,bool &tderrors,bool &sbierrors,bool &hbierrors,bool &tdbierrors); }; //+------------------------------------------------------------------+ //| Testing symmetric EVD subroutine | //+------------------------------------------------------------------+ bool CTestEVDUnit::TestEVD(const bool silent) { //--- create variables int n=0; int j=0; int failc=0; int runs=0; double failthreshold=0; double threshold=0; double bithreshold=0; bool waserrors; bool nserrors; bool serrors; bool herrors; bool tderrors; bool sbierrors; bool hbierrors; bool tdbierrors; bool wfailed; //--- create matrix CMatrixDouble ra; //--- initialization failthreshold=0.005; threshold=100000*CMath::m_machineepsilon; bithreshold=1.0E-6; nserrors=false; serrors=false; herrors=false; tderrors=false; sbierrors=false; hbierrors=false; tdbierrors=false; failc=0; runs=0; //--- Test problems for(n=1; n<=CAblas::AblasBlockSize(); n++) TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors); for(j=2; j<=3; j++) { for(n=j*CAblas::AblasBlockSize()-1; n<=j*CAblas::AblasBlockSize()+1; n++) TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors); } //--- report wfailed=(double)failc/(double)runs>failthreshold; waserrors=((((((nserrors||serrors)||herrors)||tderrors)||sbierrors)||hbierrors)||tdbierrors)||wfailed; //--- check if(!silent) { Print("TESTING EVD UNIT"); PrintResult("NS ERRORS",!nserrors); PrintResult("S ERRORS",!serrors); PrintResult("H ERRORS",!herrors); PrintResult("TD ERRORS",!tderrors); PrintResult("SBI ERRORS",!sbierrors); PrintResult("HBI ERRORS",!hbierrors); PrintResult("TDBI ERRORS",!tdbierrors); PrintResult("FAILURE THRESHOLD",!wfailed); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ void CTestEVDUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- change values for(i=0; i=sparcity) a.Set(i,j,2*CMath::RandomReal()-1); else a.Set(i,j,0); } } } //+------------------------------------------------------------------+ //| Sparse fill | //+------------------------------------------------------------------+ void CTestEVDUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m, const int n,const double sparcity) { //--- create variables int i=0; int j=0; //--- change values for(i=0; i=sparcity) { a.SetRe(i,j,2*CMath::RandomReal()-1); a.SetIm(i,j,2*CMath::RandomReal()-1); } else a.Set(i,j,0); } } } //+------------------------------------------------------------------+ //| Copies A to AL (lower half) and AU (upper half),filling unused | //| parts by random garbage. | //+------------------------------------------------------------------+ void CTestEVDUnit::RMatrixSymmetricSplit(CMatrixDouble &a,const int n, CMatrixDouble &al,CMatrixDouble &au) { //--- create variables int i=0; int j=0; //--- change values for(i=0; i1) result=MathMax(result,MathAbs(v)); } } //--- change value mx=0; for(i=0; ithreshold; 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; ithreshold; } //+------------------------------------------------------------------+ //| Tests SEVD problem | //+------------------------------------------------------------------+ void CTestEVDUnit::TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al, CMatrixComplex &au,const int n, const double threshold,bool &herrors, int &failc,int &runs) { //--- create a variable int i=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixComplex z; //--- Test simple EVD: values and full vectors,lower A Unset1D(lambdaref); CUnset2D(z); runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVD(al,n,1,false,lambdaref,z)) { failc=failc+1; return; } //--- search errors herrors=herrors||TestCProduct(a,n,z,lambdaref)>threshold; herrors=herrors||TestCOrt(z,n)>threshold; for(i=0; i<=n-2; i++) { //--- check if(lambdaref[i+1]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; ithreshold; } //+------------------------------------------------------------------+ //| Tests EVD problem | //| DistVals - is True,when eigenvalues are distinct. Is False, | //| when we are solving sparse task with lots of zero| //| eigenvalues. In such cases some tests related to | //| the eigenvectors are not performed. | //+------------------------------------------------------------------+ void CTestEVDUnit::TestSEVDBiProblem(CMatrixDouble &afull, CMatrixDouble &al,CMatrixDouble &au, const int n,const bool distvals, const double threshold,bool &serrors, int &failc,int &runs) { //--- create variables int i=0; int j=0; int k=0; int m=0; int i1=0; int i2=0; double v=0; double a=0; double b=0; int i_=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixDouble z; CMatrixDouble zref; CMatrixDouble a1; CMatrixDouble a2; CMatrixDouble ar; //--- allocation ArrayResize(lambdaref,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- Reference EVD runs=runs+1; //--- check if(!CEigenVDetect::SMatrixEVD(afull,n,1,true,lambdaref,zref)) { failc=failc+1; return; } //--- Select random interval boundaries. //--- If there are non-distinct eigenvalues at the boundaries, //--- we move indexes further until values splits. It is done to //--- avoid situations where we can't get definite answer. i1=CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1); //--- calculation while(i1>0) { //--- check if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold) break; i1=i1-1; } while(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; kthreshold; //--- 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; kthreshold; //--- 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; kthreshold; //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } } //+------------------------------------------------------------------+ //| Tests EVD problem | //| DistVals - is True,when eigenvalues are distinct. Is False, | //| when we are solving sparse task with lots of zero| //| eigenvalues. In such cases some tests related to | //| the eigenvectors are not performed. | //+------------------------------------------------------------------+ void CTestEVDUnit::TestHEVDBiProblem(CMatrixComplex &afull, CMatrixComplex &al, CMatrixComplex &au,const int n, const bool distvals,const double threshold, bool &herrors,int &failc,int &runs) { //--- create variables int i=0; int j=0; int k=0; int m=0; int i1=0; int i2=0; complex v=0; double a=0; double b=0; int i_=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixComplex z; CMatrixComplex zref; CMatrixComplex a1; CMatrixComplex a2; CMatrixComplex ar; //--- allocation ArrayResize(lambdaref,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- Reference EVD runs=runs+1; //--- check if(!CEigenVDetect::HMatrixEVD(afull,n,1,true,lambdaref,zref)) { failc=failc+1; return; } //--- Select random interval boundaries. //--- If there are non-distinct eigenvalues at the boundaries, //--- we move indexes further until values splits. It is done to //--- avoid situations where we can't get definite answer. i1=CMath::RandomInteger(n); i2=i1+CMath::RandomInteger(n-i1); //--- calculation while(i1>0) { //--- check if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold) break; i1=i1-1; } while(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; kthreshold; //--- 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; kthreshold; //--- 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; kthreshold; //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } //--- 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; kthreshold; //--- check if(distvals) { //--- Distinct eigenvalues,test vectors for(j=0; jthreshold; } } } //+------------------------------------------------------------------+ //| Tests EVD problem | //+------------------------------------------------------------------+ void CTestEVDUnit::TestTdEVDProblem(double &d[],double &e[], const int n,const double threshold, bool &tderrors,int &failc,int &runs) { //--- create variables bool wsucc; int i=0; int j=0; double v=0; int i_=0; //--- create arrays double lambdav[]; double ee[]; double lambda2[]; //--- create matrix CMatrixDouble z; CMatrixDouble zref; CMatrixDouble a1; CMatrixDouble a2; //--- allocation ArrayResize(lambdav,n); ArrayResize(lambda2,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- check if(n>1) { //--- allocation ArrayResize(ee,n-1); } //--- Test simple EVD: values and full vectors for(i=0; ithreshold; 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; ithreshold; for(i=0; ithreshold && MathAbs(v+a1.Get(i,j))>threshold); } } //--- Test first row variant for(i=0; ithreshold; //--- next line is a bit complicated because //--- depending on algorithm used we can get either //--- z or -z as eigenvector. so we compare result //--- with both z and -z tderrors=tderrors||(MathAbs(z[0][i]-zref[0][i])>threshold && MathAbs(z[0][i]+zref[0][i])>threshold); } } //+------------------------------------------------------------------+ //| Tests EVD problem | //| DistVals - is True,when eigenvalues are distinct. Is False, | //| when we are solving sparse task with lots of zero| //| eigenvalues. In such cases some tests related to | //| the eigenvectors are not performed. | //+------------------------------------------------------------------+ void CTestEVDUnit::TestTdEVDBiProblem(double &d[],double &e[], const int n,const bool distvals, const double threshold,bool &serrors, int &failc,int &runs) { //--- create variables int i=0; int j=0; int k=0; int m=0; int i1=0; int i2=0; double v=0; double a=0; double b=0; int i_=0; //--- create arrays double lambdav[]; double lambdaref[]; //--- create matrix CMatrixDouble z; CMatrixDouble zref; CMatrixDouble a1; CMatrixDouble a2; CMatrixDouble ar; //--- allocation ArrayResize(lambdaref,n); zref.Resize(n,n); a1.Resize(n,n); a2.Resize(n,n); //--- Reference EVD ArrayResize(lambdaref,n); for(i_=0; i_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; ithreshold; //--- Test interval,transform vectors ArrayResize(lambdav,n); for(i=0; ithreshold; //--- check if(distvals) { //--- allocation ar.Resize(n,m); for(i=0; ithreshold; } } //--- Test indexes,transform vectors ArrayResize(lambdav,n); for(i=0; ithreshold; //--- check if(distvals) { //--- allocation ar.Resize(n,m); //--- calculation for(i=0; ithreshold; } } //--- Test interval,do not transform vectors ArrayResize(lambdav,n); for(i=0; ithreshold; //--- check if(distvals) { for(j=0; jthreshold; } } //--- Test indexes,do not transform vectors ArrayResize(lambdav,n); for(i=0; ithreshold; //--- check if(distvals) { for(j=0; jthreshold; } } } //+------------------------------------------------------------------+ //| Non-symmetric problem | //+------------------------------------------------------------------+ void CTestEVDUnit::TestNSEVDProblem(CMatrixDouble &a,const int n, const double threshold, bool &nserrors,int &failc, int &runs) { //--- create variables double mx=0; int i=0; int j=0; int k=0; int vjob=0; bool needl; bool needr; double curwr=0; double curwi=0; double vt=0; double tmp=0; int i_=0; //--- create arrays double wr0[]; double wi0[]; double wr1[]; double wi1[]; double wr0s[]; double wi0s[]; double wr1s[]; double wi1s[]; double vec1r[]; double vec1i[]; double vec2r[]; double vec2i[]; double vec3r[]; double vec3i[]; //--- create matrix CMatrixDouble vl; CMatrixDouble vr; //--- allocation ArrayResize(vec1r,n); ArrayResize(vec2r,n); ArrayResize(vec3r,n); ArrayResize(vec1i,n); ArrayResize(vec2i,n); ArrayResize(vec3i,n); ArrayResize(wr0s,n); ArrayResize(wr1s,n); ArrayResize(wi0s,n); ArrayResize(wi1s,n); //--- initialization mx=0; for(i=0; imx) mx=MathAbs(a.Get(i,j)); } } //--- check if(mx==0.0) mx=1; //--- Load values-only runs=runs+1; //--- check if(!CEigenVDetect::RMatrixEVD(a,n,0,wr0,wi0,vl,vr)) { failc=failc+1; return; } //--- Test different jobs for(vjob=1; vjob<=3; vjob++) { needr=vjob==1||vjob==3; needl=vjob==2||vjob==3; runs=runs+1; //--- check if(!CEigenVDetect::RMatrixEVD(a,n,vjob,wr1,wi1,vl,vr)) { failc=failc+1; return; } //--- Test values: //--- 1. sort by real part //--- 2. test for(i_=0; i_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_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; ithreshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));; CAp::SetErrorFlag(nserrors,MathAbs(wi0s[i]-wi1s[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));; } //--- Test right vectors if(needr) { k=0; //--- calculation while(k<=n-1) { //--- check if(wi1[k]==0.0) { for(i_=0; i_0.0) { for(i_=0; i_threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob)); CAp::SetErrorFlag(nserrors,MathAbs(vec2i[i]-vec3i[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob)); } k++; } } //--- Test left vectors if(needl) { k=0; //--- calculation while(k<=n-1) { //--- check if(wi1[k]==0.0) { for(i_=0; i_0.0) { for(i_=0; i_threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob)); CAp::SetErrorFlag(nserrors,MathAbs(vec2i[i]-vec3i[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob)); } k++; } } } } //+------------------------------------------------------------------+ //| Testing EVD subroutines for one N | //| NOTES: | //| * BIThreshold is a threshold for bisection-and-inverse-iteration | //| subroutines. special threshold is needed because these | //| subroutines may have much more larger error than QR-based | //| algorithms. | //+------------------------------------------------------------------+ void CTestEVDUnit::TestEVDSet(const int n,const double threshold, double bithreshold,int &failc, int &runs,bool &nserrors,bool &serrors, bool &herrors,bool &tderrors,bool &sbierrors, bool &hbierrors,bool &tdbierrors) { //--- create variables int i=0; int j=0; int mkind=0; //--- create arrays double d[]; double e[]; //--- create matrix CMatrixDouble ra; CMatrixDouble ral; CMatrixDouble rau; CMatrixComplex ca; CMatrixComplex cal; CMatrixComplex cau; //--- Test symmetric problems ra.Resize(n,n); ral.Resize(n,n); rau.Resize(n,n); ca.Resize(n,n); cal.Resize(n,n); cau.Resize(n,n); //--- Zero matrices for(i=0; i1) ArrayResize(e,n-1); //--- check if(mkind==0) { //--- Zero matrix for(i=0; ithreshold; else rerr=rerr||MathAbs(vt)>threshold; //--- change value vt=0.0; for(i_=0; i_threshold; else rerr=rerr||MathAbs(vt)>threshold; //--- test for difference in A and B if(n>=2) rerr=rerr||a.Get(i,j)==b.Get(i,j); } } //--- Random orthogonal,complex Unset2DC(ca); Unset2DC(cb); //--- function call CMatGen::CMatrixRndOrthogonal(n,ca); //--- function call CMatGen::CMatrixRndOrthogonal(n,cb); //--- search errors for(i=0; ithreshold; else cerr=cerr||CMath::AbsComplex(ct)>threshold; //--- change value ct=0.0; for(i_=0; i_threshold; else cerr=cerr||CMath::AbsComplex(ct)>threshold; //--- test for difference in A and B if(n>=2) cerr=cerr||ca.Get(i,j)==cb.Get(i,j); } } //--- From the right real tests: //--- 1. E*Q is orthogonal //--- 2. Q1<>Q2 (routine result is changing) //--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices) Unset2D(a); Unset2D(b); //--- allocation a.Resize(n,n); b.Resize(n,n); //--- change values for(i=0; ithreshold; else rerr=rerr||MathAbs(vt)>threshold; //--- change value vt=0.0; for(i_=0; i_threshold; else rerr=rerr||MathAbs(vt)>threshold; //--- test for difference in A and B if(n>=2) rerr=rerr||a.Get(i,j)==b.Get(i,j); } } //--- change values for(i=0; ithreshold; } //--- 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; ithreshold; else rerr=rerr||MathAbs(vt)>threshold; //--- change value vt=0.0; for(i_=0; i_threshold; else rerr=rerr||MathAbs(vt)>threshold; //--- test for difference in A and B if(n>=2) rerr=rerr||a.Get(i,j)==b.Get(i,j); } } //--- change values for(i=0; ithreshold; } //--- 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; ithreshold; else cerr=cerr||CMath::AbsComplex(ct)>threshold; //--- change value ct=0.0; for(i_=0; i_threshold; else cerr=cerr||CMath::AbsComplex(ct)>threshold; //--- test for difference in A and B cerr=cerr||ca.Get(i,j)==cb.Get(i,j); } } //--- change values for(i=0; ithreshold; } //--- 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; ithreshold; else cerr=cerr||CMath::AbsComplex(ct)>threshold; //--- change value ct=0.0; for(i_=0; i_threshold; else cerr=cerr||CMath::AbsComplex(ct)>threshold; //--- test for difference in A and B cerr=cerr||ca.Get(i,j)==cb.Get(i,j); } } //--- change values for(i=0; ithreshold; } } } //--- Testing GCond for(n=2; n<=maxn; n++) { for(pass=1; pass<=passcount; pass++) { //--- real test Unset2D(a); cond=MathExp(MathLog(1000)*CMath::RandomReal()); //--- function call CMatGen::RMatrixRndCond(n,cond,a); //--- allocation b.Resize(n+1,n+1); //--- change values for(i=1; i<=n; i++) { for(j=1; j<=n; j++) b.Set(i,j,a[i-1][j-1]); } //--- check if(ObsoleteSVDDecomposition(b,n,n,w,v)) { maxw=w[1]; minw=w[1]; for(i=2; i<=n; i++) { //--- check if(w[i]>maxw) maxw=w[i]; //--- check if(w[i]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; ithreshold; } //--- 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; i2) 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; ithreshold; } //--- 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; ithreshold; //--- test for difference between A and B Unset2D(b); //--- function call CMatGen::SMatrixRndCond(n,cond,b); //--- check if(n>=2) { for(i=0; ithreshold; } //--- 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=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; jmaxw) maxw=w[i]; } //--- return result return(maxw/minw); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ bool CTestMatGenUnit::ObsoleteSVDDecomposition(CMatrixDouble &a, const int m, const int n, double &w[], CMatrixDouble &v) { //--- create variables bool result; int nm=0; int minmn=0; int l=0; int k=0; int j=0; int jj=0; int its=0; int i=0; double z=0; double y=0; double x=0; double vscale=0; double s=0; double h=0; double g=0; double f=0; double c=0; double anorm=0; bool flag; //--- create array double rv1[]; //--- allocation ArrayResize(rv1,n+1); ArrayResize(w,n+1); v.Resize(n+1,n+1); //--- initialization result=true; //--- check if(m=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.Set(j,nm,y*c+z*s); a.Set(j,i,-(y*s)+z*c); } } } } z=w[k]; //--- check if(l==k) { //--- check if(z<0.0) { w[k]=-z; for(j=1; j<=n; j++) v.Set(j,k,-v[j][k]); } break; } //--- check if(its==m_maxsvditerations) { //--- return result return(false); } //--- change values x=w[l]; nm=k-1; y=w[nm]; g=rv1[nm]; h=rv1[k]; f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y); g=PyThag(f,1); f=((x-z)*(x+z)+h*(y/(f+ExtSign(g,f))-h))/x; c=1.0; s=1.0; //--- calculation for(j=l; j<=nm; j++) { i=j+1; g=rv1[i]; y=w[i]; h=s*g; g=c*g; z=PyThag(f,h); rv1[j]=z; c=f/z; s=h/z; f=x*c+g*s; g=-(x*s)+g*c; h=y*s; y=y*c; for(jj=1; jj<=n; jj++) { x=v.Get(jj,j); z=v.Get(jj,i); v.Set(jj,j,x*c+z*s); v.Set(jj,i,-(x*s)+z*c); } z=PyThag(f,h); w[j]=z; //--- check if(z!=0.0) { z=1.0/z; c=f*z; s=h*z; } //--- calculation f=c*g+s*y; x=-(s*g)+c*y; for(jj=1; jj<=m; jj++) { y=a.Get(jj,j); z=a.Get(jj,i); a.Set(jj,j,y*c+z*s); a.Set(jj,i,-(y*s)+z*c); } } //--- change values rv1[l]=0.0; rv1[k]=f; w[k]=x; } } //--- return result return(result); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ double CTestMatGenUnit::ExtSign(const double a,const double b) { //--- create a variable double result=0; //--- check if(b>=0.0) result=MathAbs(a); else result=-MathAbs(a); //--- return result return(result); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ double CTestMatGenUnit::MyMax(const double a,const double b) { //--- create a variable double result=0; //--- check if(a>b) result=a; else result=b; //--- return result return(result); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ double CTestMatGenUnit::PyThag(const double a,const double b) { //--- create a variable double result=0; //--- check if(MathAbs(a)0.5) n=mx; else m=mx; //--- First,test on zero matrix ra.Resize(m,n); ca.Resize(m,n); for(i=0; ii) hpderr=hpderr||cal.Get(i,j)!=i; else { vc=0.0; for(i_=0; i_<=j; i_++) vc+=cal.Get(i,i_)*CMath::Conj(cal.Get(j,i_)); //--- search errors hpderr=hpderr||CMath::AbsComplex(ca.Get(i,j)-vc)>threshold; } } } } else hpderr=true; //--- check if(CTrFac::HPDMatrixCholesky(cau,n,true)) { for(i=0; ithreshold; } } } } 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; ii) spderr=spderr||ral.Get(i,j)!=i; else { vr=0.0; for(i_=0; i_<=j; i_++) vr+=ral.Get(i,i_)*ral.Get(j,i_); //--- search errors spderr=spderr||MathAbs(ra.Get(i,j)-vr)>threshold; } } } } else spderr=true; //--- check if(CTrFac::SPDMatrixCholesky(rau,n,true)) { for(i=0; ithreshold; } } } } else spderr=true; } //--- report waserrors=(((rerr||spderr)||cerr)||hpderr)||properr; //--- check if(!silent) { Print("TESTING TRIANGULAR FACTORIZATIONS"); PrintResult("* REAL",!rerr); PrintResult("* SPD",!spderr); PrintResult("* COMPLEX",!cerr); PrintResult("* HPD",!hpderr); PrintResult("* OTHER PROPERTIES",!properr); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestTrFacUnit::TestCLUProblem(CMatrixComplex &a,const int m, const int n,const double threshold, bool &err,bool &properr) { //--- create variables int i=0; int j=0; int minmn=0; complex v=0; int i_=0; //--- create arrays complex ct[]; int p[]; //--- create matrix CMatrixComplex ca; CMatrixComplex cl; CMatrixComplex cu; CMatrixComplex ca2; //--- initialization minmn=MathMin(m,n); //--- PLU test ca.Resize(m,n); for(i=0; i=m) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); for(j=0; j<=minmn-1; j++) { for(i=0; i=0; i--) { //--- check if(i!=p[i]) { for(i_=0; i_threshold; } //--- LUP test ca.Resize(m,n); for(i=0; i=n) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); //--- change values for(j=0; j<=minmn-1; j++) { for(i=0; i=0; i--) { //--- check if(i!=p[i]) { for(i_=0; i_threshold; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestTrFacUnit::TestRLUProblem(CMatrixDouble &a,const int m, const int n,const double threshold, bool &err,bool &properr) { //--- create variables int i=0; int j=0; int minmn=0; double v=0; int i_=0; //--- create arrays double ct[]; int p[]; //--- create matrix CMatrixDouble ca=a; CMatrixDouble cl; CMatrixDouble cu; CMatrixDouble ca2; //--- initialization minmn=MathMin(m,n); //--- PLU test ca.Resize(m,n); //--- function call CTrFac::RMatrixPLU(ca,m,n,p); for(i=0; i<=minmn-1; i++) { //--- check if(p[i]=m) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); //--- change values for(j=0; j<=minmn-1; j++) { for(i=0; i=0; i--) { //--- check if(i!=p[i]) { //--- change values for(i_=0; i_threshold; } //--- LUP test ca.Resize(m,n); for(i=0; i=n) { properr=false; return; } } //--- allocation cl.Resize(m,minmn); //--- change values for(j=0; j<=minmn-1; j++) { for(i=0; i=0; i--) { //--- check if(i!=p[i]) { //--- change values for(i_=0; i_threshold; } } //+------------------------------------------------------------------+ //| Testing class CTrLinSolve | //+------------------------------------------------------------------+ class CTestTrLinSolveUnit { public: static bool TestTrLinSolve(const bool silent); private: static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); }; //+------------------------------------------------------------------+ //| Main unittest subroutine | //+------------------------------------------------------------------+ bool CTestTrLinSolveUnit::TestTrLinSolve(const bool silent) { //--- create variables int maxmn=0; int passcount=0; double threshold=0; int n=0; int pass=0; int i=0; int j=0; int cnts=0; int cntu=0; int cntt=0; int cntm=0; bool waserrors; bool isupper; bool istrans; bool isunit; double v=0; double s=0; int i_=0; //--- create arrays double xe[]; double b[]; //--- create matrix CMatrixDouble aeffective; CMatrixDouble aparam; //--- initialization waserrors=false; maxmn=15; passcount=15; threshold=1000*CMath::m_machineepsilon; //--- Different problems for(n=1; n<=maxmn; n++) { //--- allocation aeffective.Resize(n,n); aparam.Resize(n,n); ArrayResize(xe,n); ArrayResize(b,n); //--- calculation for(pass=1; pass<=passcount; pass++) { for(cnts=0; cnts<=1; cnts++) { for(cntu=0; cntu<=1; cntu++) { for(cntt=0; cntt<=1; cntt++) { for(cntm=0; cntm<=2; cntm++) { isupper=cnts==0; isunit=cntu==0; istrans=cntt==0; //--- Skip meaningless combinations of parameters: //--- (matrix is singular) AND (matrix is unit diagonal) if(cntm==2 && isunit) continue; //--- Clear matrices for(i=0; ithreshold; } } } } } } //--- report if(!silent) { Print("TESTING RMatrixTrSafeSolve"); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ void CTestTrLinSolveUnit::MakeACopy(CMatrixDouble &a,const int m, const int n,CMatrixDouble &b) { //--- create variables int i=0; int j=0; //--- allocation b.Resize(m,n); //--- copy for(i=0; i0.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; ithreshold; } 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; ithreshold; } else rerrors=true; } //--- Special test with diagonal ill-conditioned matrix: //--- * ability to solve it when resulting growth is less than threshold //--- * ability to Stop solve when resulting growth is greater than threshold //--- A=diag(1,1/growth) //--- b=(1,0.5) n=2; growth=10; //--- allocation ca.Resize(n,n); //--- change values ca.Set(0,0,1); ca.Set(0,1,0); ca.Set(1,0,0); ca.Set(1,1,1/growth); //--- allocation ArrayResize(cxs,n); //--- change values cxs[0]=1.0; cxs[1]=0.5; //--- search errors cerrors=cerrors||!CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,1.05*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0)); cerrors=cerrors||!CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,0.95*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0)); //--- allocation ra.Resize(n,n); //--- change values ra.Set(0,0,1); ra.Set(0,1,0); ra.Set(1,0,0); ra.Set(1,1,1/growth); //--- allocation ArrayResize(rxs,n); //--- change values rxs[0]=1.0; rxs[1]=0.5; //--- search errors rerrors=rerrors||!CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,1.05*MathMax(MathAbs(rxs[1])*growth,1.0)); rerrors=rerrors||!CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,0.95*MathMax(MathAbs(rxs[1])*growth,1.0)); //--- Special test with diagonal degenerate matrix: //--- * ability to solve it when resulting growth is less than threshold //--- * ability to Stop solve when resulting growth is greater than threshold //--- A=diag(1,0) //--- b=(1,0.5) n=2; ca.Resize(n,n); //--- change values ca.Set(0,0,1); ca.Set(0,1,0); ca.Set(1,0,0); ca.Set(1,1,0); //--- allocation ArrayResize(cxs,n); //--- change values cxs[0]=1.0; cxs[1]=0.5; //--- search errors cerrors=cerrors||CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,MathSqrt(CMath::m_maxrealnumber)); //--- allocation ra.Resize(n,n); //--- change values ra.Set(0,0,1); ra.Set(0,1,0); ra.Set(1,0,0); ra.Set(1,1,0); //--- allocation ArrayResize(rxs,n); //--- change values rxs[0]=1.0; rxs[1]=0.5; //--- search errors rerrors=rerrors||CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,MathSqrt(CMath::m_maxrealnumber)); //--- report waserrors=rerrors||cerrors; //--- check if(!silent) { Print("TESTING SAFE TR SOLVER"); PrintResult("REAL",!rerrors); PrintResult("COMPLEX",!cerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ void CTestSafeSolveUnit::RMatrixMakeACopy(CMatrixDouble &a, const int m,const int n, CMatrixDouble &b) { //--- allocation b.Resize(m,n); //--- copy for(int i=0; ij) || (!droplower && ij) || (!droplower && i0) { for(i_=0; i_=0; j--) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } a.Set(j,j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { //--- change value v=0.0; for(i_=j+1; i_=0; j--) { //--- Copy current column of L to WORK and replace with zeros. for(i=j+1; i=0; j--) { jp=pivots[j]; //--- check if(jp!=j) { for(i_=0; i_0) { for(i_=0; i_=0; j--) { //--- check if(nounit) { //--- check if(a[j][j]==0) { //--- return result return(false); } a.Set(j,j,one/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { //--- change value v=0.0; for(i_=j+1; i_=0; j--) { //--- Copy current column of L to WORK and replace with zeros. for(i=j+1; i=0; j--) { jp=pivots[j]; //--- check if(jp!=j) { //--- change values for(i_=0; i_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=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; i0.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; i0.5,false)!=0.0; errspec=errspec||CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; } } //--- report result=!(((err50||err90)||errless)||errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ bool CTestRCondUnit::TestCMatrixTrRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int j1=0; int j2=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; double erc1=0; double ercinf=0; double v=0; bool isupper; bool isunit; //--- create arrays int p[]; double q50[]; double q90[]; //--- create matrix CMatrixComplex a; CMatrixComplex ea; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,2); ArrayResize(q90,2); //--- calculation for(n=1; n<=maxn; n++) { //--- special test for zero matrix CMatrixGenZero(a,n); //--- search errors errspec=errspec||CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0; errspec=errspec||CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; //--- general test a.Resize(n,n); for(i=0; i<=1; i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1; pass<=passcount; pass++) { //--- change values isupper=CMath::RandomReal()>0.5; isunit=CMath::RandomReal()>0.5; for(i=0; i=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; i0.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; i0.5,false)!=0.0; errspec=errspec||CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0; } } //--- report result=!(((err50||err90)||errless)||errspec); //--- return result return(result); } //+------------------------------------------------------------------+ //| Returns True for successful test,False - for failed test | //+------------------------------------------------------------------+ bool CTestRCondUnit::TestRMatrixRCond(const int maxn,const int passcount) { //--- create variables bool result; int n=0; int i=0; int j=0; int pass=0; bool err50; bool err90; bool errspec; bool errless; double erc1=0; double ercinf=0; double v=0; //--- create array int p[]; double q50[]; double q90[]; //--- create matrix CMatrixDouble a; CMatrixDouble lua; //--- initialization err50=false; err90=false; errless=false; errspec=false; //--- allocation ArrayResize(q50,4); ArrayResize(q90,4); //--- calculation for(n=1; n<=maxn; n++) { //--- special test for zero matrix RMatrixGenZero(a,n); RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); //--- search errors errspec=errspec||CRCond::RMatrixRCond1(a,n)!=0.0; errspec=errspec||CRCond::RMatrixRCondInf(a,n)!=0.0; errspec=errspec||CRCond::RMatrixLURCond1(lua,n)!=0.0; errspec=errspec||CRCond::RMatrixLURCondInf(lua,n)!=0.0; //--- general test a.Resize(n,n); for(i=0; i<=3; i++) { q50[i]=0; q90[i]=0; } //--- calculation for(pass=1; pass<=passcount; pass++) { //--- function call CMatGen::RMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a); RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); RMatrixRefRCond(a,n,erc1,ercinf); //--- 1-norm,normal v=1/CRCond::RMatrixRCond1(a,n); //--- check if(v>=m_threshold50*erc1) q50[0]=q50[0]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[0]=q90[0]+1.0/(double)passcount; //--- search errors errless=errless||v>(double)(erc1*1.001); //--- 1-norm,LU v=1/CRCond::RMatrixLURCond1(lua,n); //--- check if(v>=m_threshold50*erc1) q50[1]=q50[1]+1.0/(double)passcount; //--- check if(v>=m_threshold90*erc1) q90[1]=q90[1]+1.0/(double)passcount; //--- search errors errless=errless||v>erc1*1.001; //--- Inf-norm,normal v=1/CRCond::RMatrixRCondInf(a,n); //--- check if(v>=m_threshold50*ercinf) q50[2]=q50[2]+1.0/(double)passcount; //--- check if(v>=m_threshold90*ercinf) q90[2]=q90[2]+1.0/(double)passcount; //--- search errors errless=errless||v>ercinf*1.001; //--- Inf-norm,LU v=1/CRCond::RMatrixLURCondInf(lua,n); //--- check if(v>=m_threshold50*ercinf) q50[3]=q50[3]+1.0/(double)passcount; //--- check if(v>=m_threshold90*ercinf) q90[3]=q90[3]+1.0/(double)passcount; //--- search errors errless=errless||v>ercinf*1.001; } //--- search errors for(i=0; i<=3; i++) { err50=err50||q50[i]<0.5; err90=err90||q90[i]<0.9; } //--- degenerate matrix test if(n>=3) { //--- allocation a.Resize(n,n); for(i=0; i=2) { //--- allocation a.Resize(n,n); for(i=0; i0.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=2) { //--- allocation a.Resize(n,n); for(i=0; i=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=2) { //--- allocation a.Resize(n,n); for(i=0; i0.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=2) { //--- allocation a.Resize(n,n); for(i=0; i1+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; i1+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; i1+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; i1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(int i=0; i1+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; i1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(int i=0; ij) || (!droplower && ij) || (!droplower && ii && !isupper)) { a.Set(i,j,0); b.Set(i,j,0); } } } //--- check if(isunit) { for(i=0; ithreshold; else rtrerrors=rtrerrors||MathAbs(v-1)>threshold; } } } } } } //+------------------------------------------------------------------+ //| Complex TR inverse | //+------------------------------------------------------------------+ void CTestMatInvUnit::TestCTRInv(const int maxn,const int passcount, const double threshold,bool &ctrerrors) { //--- create variables int n=0; int pass=0; int i=0; int j=0; int task=0; bool isupper; bool isunit; complex v=0; bool waserrors; int info=0; int i_=0; //--- create arrays CMatrixComplex a; CMatrixComplex b; //--- object of class CMatInvReport rep; //--- initialization waserrors=false; //--- Test for(n=1; n<=maxn; n++) { //--- allocation a.Resize(n,n); b.Resize(n,n); //--- calculation for(task=0; task<=3; task++) { for(pass=1; pass<=passcount; pass++) { //--- Determine task isupper=task%2==0; isunit=task/2%2==0; //--- Generate matrix for(i=0; ii && !isupper)) { a.Set(i,j,0); b.Set(i,j,0); } } } //--- check if(isunit) { for(i=0; ithreshold; else ctrerrors=ctrerrors||CMath::AbsComplex(v-1)>threshold; } } } } } } //+------------------------------------------------------------------+ //| Real test | //+------------------------------------------------------------------+ void CTestMatInvUnit::TesTrInv(const int maxn,const int passcount, const double threshold,bool &rerrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int pass=0; int taskkind=0; int info=0; int i_=0; //--- create array int p[]; //--- create matrix CMatrixDouble a; CMatrixDouble lua; CMatrixDouble inva; CMatrixDouble invlua; //--- object of class CMatInvReport rep; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::RMatrixRndCond(n,1000,a); RMatrixMakeACopy(a,n,n,lua); CTrFac::RMatrixLU(lua,n,n,p); RMatrixMakeACopy(a,n,n,inva); RMatrixMakeACopy(lua,n,n,invlua); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::RMatrixInverse(inva,n,info,rep); //--- search errors rerrors=rerrors||!RMatrixCheckInverse(a,inva,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::RMatrixLUInverse(invlua,p,n,info,rep); //--- search errors rerrors=rerrors||!RMatrixCheckInverse(a,invlua,n,threshold,info,rep); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- * with equal rows/columns //--- 2. test different methods for(taskkind=0; taskkind<=4; taskkind++) { Unset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0; i0.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; i1000*CMath::m_machineepsilon; spderrors=(spderrors||rep.m_rinf<0.0)||rep.m_rinf>1000*CMath::m_machineepsilon; } } } } } //+------------------------------------------------------------------+ //| HPD test | //+------------------------------------------------------------------+ void CTestMatInvUnit::TestHPDInv(const int maxn,const int passcount, const double threshold,bool &hpderrors) { //--- create variables bool isupper; int i=0; int j=0; int k=0; int n=0; int pass=0; int taskkind=0; int info=0; int i_=0; //--- create matrix CMatrixComplex a; CMatrixComplex cha; CMatrixComplex inva; CMatrixComplex invcha; //--- object of class CMatInvReport rep; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { isupper=CMath::RandomReal()>0.5; //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A CMatGen::HPDMatrixRndCond(n,1000,a); CMatrixDropHalf(a,n,isupper); CMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::HPDMatrixCholesky(cha,n,isupper)) continue; //--- function calls CMatrixMakeACopy(a,n,n,inva); CMatrixMakeACopy(cha,n,n,invcha); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::HPDMatrixInverse(inva,n,isupper,info,rep); //--- search errors hpderrors=hpderrors||!HPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep); //--- change value info=0; //--- function calls UnsetRep(rep); CMatInv::HPDMatrixCholeskyInverse(invcha,n,isupper,info,rep); //--- search errors hpderrors=hpderrors||!HPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep); //--- ******************************************************** //--- EXACTLY SINGULAR MATRICES //--- ability to detect singularity is tested //--- ******************************************************** //--- 1. generate different types of singular matrices: //--- * zero //--- * with zero columns //--- * with zero rows //--- 2. test different methods for(taskkind=0; taskkind<=2; taskkind++) { CUnset2D(a); //--- check if(taskkind==0) { //--- all zeros a.Resize(n,n); for(i=0; i1000*CMath::m_machineepsilon; hpderrors=(hpderrors||rep.m_rinf<0.0)||rep.m_rinf>1000*CMath::m_machineepsilon; } } } } } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ void CTestMatInvUnit::Unset2D(CMatrixDouble &x) { //--- allocation x.Resize(1,1); //--- change value x.Set(0,0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ void CTestMatInvUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ void CTestMatInvUnit::CUnset2D(CMatrixComplex &x) { //--- allocation x.Resize(1,1); //--- change value x.Set(0,0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ void CTestMatInvUnit::CUnset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets report | //+------------------------------------------------------------------+ void CTestMatInvUnit::UnsetRep(CMatInvReport &r) { //--- change values r.m_r1=-1; r.m_rinf=-1; } //+------------------------------------------------------------------+ //| Testing class CLDA | //+------------------------------------------------------------------+ class CTestLDAUnit { public: static bool TestLDA(const bool silent); private: static void GenSimpleSet(const int nfeatures,const int nclasses,const int nsamples,const int axis,CMatrixDouble &xy); static void GenDeg1Set(const int nfeatures,const int nclasses,const int nsamples,int axis,CMatrixDouble &xy); static double GenerateNormal(const double mean,const double sigma); static bool TestWN(CMatrixDouble &xy,CMatrixDouble &wn,const int ns,const int nf,const int nc,const int ndeg); static double CalcJ(const int nf,CMatrixDouble &st,CMatrixDouble &sw,double &w[],double &p,double &q); static void Fishers(CMatrixDouble &xy,int npoints,const int nfeatures,const int nclasses,CMatrixDouble &st,CMatrixDouble &sw); }; //+------------------------------------------------------------------+ //| Testing class CLDA | //+------------------------------------------------------------------+ bool CTestLDAUnit::TestLDA(const bool silent) { //--- create variables int maxnf=0; int maxns=0; int maxnc=0; int passcount=0; bool ldanerrors; bool lda1errors; bool waserrors; int nf=0; int nc=0; int ns=0; int i=0; int info=0; int pass=0; int axis=0; //--- create array double w1[]; //--- create matrix CMatrixDouble xy; CMatrixDouble wn; //--- Primary settings maxnf=10; maxns=1000; maxnc=5; passcount=1; waserrors=false; ldanerrors=false; lda1errors=false; //--- General tests for(nf=1; nf<=maxnf; nf++) { for(nc=2; nc<=maxnc; nc++) { for(pass=1; pass<=passcount; pass++) { //--- Simple test for LDA-N/LDA-1 axis=CMath::RandomInteger(nf); ns=maxns/2+CMath::RandomInteger(maxns/2); //--- function calls GenSimpleSet(nf,nc,ns,axis,xy); CLDA::FisherLDAN(xy,ns,nf,nc,info,wn); //--- check if(info!=1) { ldanerrors=true; continue; } //--- search errors ldanerrors=ldanerrors||!TestWN(xy,wn,ns,nf,nc,0); ldanerrors=ldanerrors||MathAbs(wn[axis][0])<=0.75; //--- function call CLDA::FisherLDA(xy,ns,nf,nc,info,w1); //--- search errors for(i=0; i=3) { ns=maxns/2+CMath::RandomInteger(maxns/2); //--- there are two duplicate features, //--- axis is oriented along non-duplicate feature axis=CMath::RandomInteger(nf-2); GenDeg1Set(nf,nc,ns,axis,xy); CLDA::FisherLDAN(xy,ns,nf,nc,info,wn); //--- check if(info!=2) { ldanerrors=true; continue; } //--- function calls ldanerrors=ldanerrors||wn.Get(axis,0)<=0.75; CLDA::FisherLDA(xy,ns,nf,nc,info,w1); //--- search errors for(i=0; i=0 && axis=3. | //+------------------------------------------------------------------+ void CTestLDAUnit::GenDeg1Set(const int nfeatures,const int nclasses, const int nsamples,int axis, CMatrixDouble &xy) { //--- 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(int i=0; i<=nsamples-1; i++) { for(int j=0; j<=nfeatures-2; j++) xy.Set(i,j,GenerateNormal(0.0,1.0)); //--- change values xy.Set(i,nfeatures-1,xy[i][nfeatures-2]); int c=i%nclasses; xy.Set(i,axis,xy[i][axis]+c); xy.Set(i,nfeatures,c); } } //+------------------------------------------------------------------+ //| Normal random number | //+------------------------------------------------------------------+ double CTestLDAUnit::GenerateNormal(const double mean,const double sigma) { //--- create variables double result=0; double u=0; double v=0; double sum=0; //--- initialization result=mean; //--- calculation while(true) { //--- change values u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal(); v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal(); sum=u*u+v*v; //--- check if(sum<1.0 && sum>0.0) { sum=MathSqrt(-(2*MathLog(sum)/sum)); result=sigma*u*sum+mean; //--- return result return(result); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests WN for correctness | //+------------------------------------------------------------------+ bool CTestLDAUnit::TestWN(CMatrixDouble &xy,CMatrixDouble &wn, const int ns,const int nf, const int nc,const int ndeg) { //--- create variables bool result; int i=0; int j=0; double v=0; double wprev=0; double tol=0; double p=0; double q=0; int i_=0; //--- create arrays double tx[]; double jp[]; double jq[]; double work[]; //--- create matrix CMatrixDouble st; CMatrixDouble sw; CMatrixDouble a; CMatrixDouble z; //--- initialization tol=10000; result=true; Fishers(xy,ns,nf,nc,st,sw); //--- Test for decreasing of J ArrayResize(tx,nf); ArrayResize(jp,nf); ArrayResize(jq,nf); //--- calculation for(j=0; j=(1-tol*CMath::m_machineepsilon)*jp[i]/jq[i]; for(i=nf-1-ndeg+1; i=(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=0.0; } //--- return result return(result); } //+------------------------------------------------------------------+ //| Calculates J | //+------------------------------------------------------------------+ double CTestLDAUnit::CalcJ(const int nf,CMatrixDouble &st, CMatrixDouble &sw,double &w[], double &p,double &q) { //--- create variables double result=0; int i=0; double v=0; int i_=0; //--- create array double tx[]; //--- initialization p=0; q=0; //--- allocation ArrayResize(tx,nf); //--- calculation for(i=0; ithreshold; gammaerrors=gammaerrors||MathAbs(CGammaFunc::GammaFunc(1.5)-0.5*MathSqrt(M_PI))>threshold; //--- function call v=CGammaFunc::LnGamma(0.5,s); //--- search errors lngammaerrors=(lngammaerrors||MathAbs(v-MathLog(MathSqrt(M_PI)))>threshold)||s!=1.0; //--- function call v=CGammaFunc::LnGamma(1.5,s); //--- search errors lngammaerrors=(lngammaerrors||MathAbs(v-MathLog(0.5*MathSqrt(M_PI)))>threshold)||s!=1.0; //--- report waserrors=gammaerrors||lngammaerrors; //--- check if(!silent) { Print("TESTING GAMMA FUNCTION"); PrintResult("GAMMA",!gammaerrors); PrintResult("LN GAMMA",!lngammaerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- end //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CBdSingValueDecompose | //+------------------------------------------------------------------+ class CTestBdSVDUnit { public: static bool TestBdSVD(const bool silent); private: static void FillIdentity(CMatrixDouble &a,const int n); static void FillSparseDE(double &d[],double &e[],const int n,const double sparcity); static void GetBdSVDError(double &d[],double &e[],const int n,const bool isupper,CMatrixDouble &u,CMatrixDouble &c,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted); static void TestBdSVDProblem(double &d[],double &e[],const int n,double &materr,double &orterr,bool &wsorted,bool &wfailed,int &failcount,int &succcount); }; //+------------------------------------------------------------------+ //| Testing bidiagonal SVD decomposition subroutine | //+------------------------------------------------------------------+ bool CTestBdSVDUnit::TestBdSVD(const bool silent) { //--- create variables int n=0; int maxn=15; int i=0; int pass=0; bool waserrors=false; bool wsorted=true; bool wfailed=false; bool failcase; double materr=0; double orterr=0; double threshold=5*100*CMath::m_machineepsilon; double failthreshold=1.0E-2; double failr=0; int failcount=0; int succcount=0; //--- create arrays double d[]; double e[]; //--- create matrix CMatrixDouble mempty; //--- allocation ArrayResize(d,maxn); ArrayResize(e,maxn-1); //--- special case: fail matrix n=5; d[0]=-8.27448347422711894000e-01; d[1]=-8.16705832087160854600e-01; d[2]=-2.53974358904729382800e-17; d[3]=-1.24626684881972815700e+00; d[4]=-4.64744131545637651000e-01; e[0]=-3.25785088656270038800e-01; e[1]=-1.03732413708914436580e-01; e[2]=-9.57365642262031357700e-02; e[3]=-2.71564153973817390400e-01; failcase=CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0); //--- special case: zero divide matrix //--- unfixed LAPACK routine should fail on this problem n=7; d[0]=-6.96462904751731892700e-01; d[1]=0.00000000000000000000e+00; d[2]=-5.73827770385971991400e-01; d[3]=-6.62562624399371191700e-01; d[4]=5.82737148001782223600e-01; d[5]=3.84825263580925003300e-01; d[6]=9.84087420830525472200e-01; e[0]=-7.30307931760612871800e-02; e[1]=-2.30079042939542843800e-01; e[2]=-6.87824621739351216300e-01; e[3]=-1.77306437707837570600e-02; e[4]=1.78285126526551632000e-15; e[5]=-4.89434737751289969400e-02; CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0); //--- zero matrix,several cases for(i=0; i<=maxn-1; i++) d[i]=0; for(i=0; i<=maxn-2; i++) e[i]=0; for(n=1; n<=maxn; n++) TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); //--- Dense matrix for(n=1; n<=maxn; n++) { for(pass=1; pass<=10; pass++) { for(i=0; i<=maxn-1; i++) d[i]=2*CMath::RandomReal()-1; for(i=0; i<=maxn-2; i++) e[i]=2*CMath::RandomReal()-1; //--- function call TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); } } //--- Sparse matrices,very sparse matrices,incredible sparse matrices for(n=1; n<=maxn; n++) { for(pass=1; pass<=10; pass++) { //--- function calls FillSparseDE(d,e,n,0.5); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); FillSparseDE(d,e,n,0.8); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); FillSparseDE(d,e,n,0.9); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); FillSparseDE(d,e,n,0.95); TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount); } } //--- report failr=(double)failcount/(double)(succcount+failcount); waserrors=((materr>threshold||orterr>threshold)||!wsorted)||failr>failthreshold; //--- check if(!silent) { Print("TESTING BIDIAGONAL SVD DECOMPOSITION"); PrintFormat("SVD decomposition error: %5.3E",materr); PrintFormat("SVD orthogonality error: %5.3E}",orterr); PrintResult("Singular values order",wsorted); PrintFormat("Always converged: %s",(!wfailed ? "YES" : "NO")); if(wfailed) PrintFormat("Fail ratio: %5.3f",failr); PrintFormat("Fail matrix test: %s",(!failcase ? "AS EXPECTED" : "CONVERGED (UNEXPECTED)")); PrintFormat("Threshold: %5.3E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestBdSVDUnit::FillIdentity(CMatrixDouble &a,const int n) { //--- allocation a.Resize(n,n); //--- change values for(int i=0; i=sparcity) d[i]=2*CMath::RandomReal()-1; else d[i]=0; } //--- change values for(i=0; i<=n-2; i++) { //--- check if(CMath::RandomReal()>=sparcity) e[i]=2*CMath::RandomReal()-1; else e[i]=0; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestBdSVDUnit::GetBdSVDError(double &d[],double &e[], const int n,const bool isupper, CMatrixDouble &u,CMatrixDouble &c, double &w[],CMatrixDouble &vt, double &materr,double &orterr, bool &wsorted) { //--- create variables int i=0; int j=0; int k=0; double locerr=0; double sm=0; int i_=0; //--- decomposition error locerr=0; for(i=0; iw[i-1]) wsorted=false; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestBdSVDUnit::TestBdSVDProblem(double &d[],double &e[], const int n,double &materr, double &orterr,bool &wsorted, bool &wfailed,int &failcount, int &succcount) { //--- create variables int i=0; double mx=0; //--- create array double w[]; //--- create matrix CMatrixDouble u; CMatrixDouble vt; CMatrixDouble c; //--- change value mx=0; for(i=0; imx) 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; ithreshold||orterr>threshold)||othererr>threshold)||!wsorted)||failr>failthreshold; //--- check if(!silent) { Print("TESTING SVD DECOMPOSITION"); PrintFormat("SVD decomposition error: %5.3E",materr); PrintFormat("SVD orthogonality error: %5.3E",orterr); PrintFormat("SVD with different parameters error: %5.3E",othererr); PrintResult("Singular values order",wsorted); PrintFormat("Always converged: %s",(!wfailed ? "YES" : "NO")); if(wfailed) PrintFormat("Fail ratio: %5.3f",failr); PrintFormat("Threshold: %5.3E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestSVDUnit::FillsParseA(CMatrixDouble &a,const int m, const int n,const double sparcity) { //--- change values for(int i=0; i=sparcity) a.Set(i,j,2*CMath::RandomReal()-1); else a.Set(i,j,0); } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestSVDUnit::GetSVDError(CMatrixDouble &a,const int m, const int n,CMatrixDouble &u, double &w[],CMatrixDouble &vt, double &materr,double &orterr, bool &wsorted) { //--- create variables int i=0; int j=0; int k=0; int minmn=0; double locerr=0; double sm=0; int i_=0; //--- initialization minmn=MathMin(m,n); //--- decomposition error locerr=0; for(i=0; iw[i-1]) wsorted=false; } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestSVDUnit::TestSVDProblem(CMatrixDouble &a,const int m, const int n,double &materr, double &orterr,double &othererr, bool &wsorted,bool &wfailed, int &failcount,int &succcount) { //--- create variables int i=0; int j=0; int ujob=0; int vtjob=0; int memjob=0; int ucheck=0; int vtcheck=0; //--- create arrays double w[]; double w2[]; //--- create matrix CMatrixDouble u; CMatrixDouble vt; CMatrixDouble u2; CMatrixDouble vt2; //--- Main SVD test if(!CSingValueDecompose::RMatrixSVD(a,m,n,2,2,2,w,u,vt)) { failcount=failcount+1; wfailed=true; //--- exit the function return; } //--- function call GetSVDError(a,m,n,u,w,vt,materr,orterr,wsorted); //--- Additional SVD tests for(ujob=0; ujob<=2; ujob++) { for(vtjob=0; vtjob<=2; vtjob++) { for(memjob=0; memjob<=2; memjob++) { //--- check if(!CSingValueDecompose::RMatrixSVD(a,m,n,ujob,vtjob,memjob,w2,u2,vt2)) { failcount=failcount+1; wfailed=true; //--- exit the function return; } //--- change value ucheck=0; //--- check if(ujob==1) ucheck=MathMin(m,n); //--- check if(ujob==2) ucheck=m; //--- change value vtcheck=0; //--- check if(vtjob==1) vtcheck=MathMin(m,n); //--- check if(vtjob==2) vtcheck=n; //--- search errors for(i=0; i0.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; i0.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; i0.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; ithreshold; 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; iMathLog(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; ithreshold; //--- LRPack test CLinReg::LRPack(tmpweights,m,wt2); ArrayResize(x,m); for(i=0; ithreshold; //--- 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; ifp)||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=sigmathreshold; //--- Test for the fast CV error: //--- calculate CV error by definition (leaving out N //--- points and recalculating solution). //--- Test for the training set error cvrmserror=0; cvavgerror=0; cvavgrelerror=0; rmserror=0; avgerror=0; avgrelerror=0; xy2.Resize(n-1,m+1); ArrayResize(s2,n-1); //--- change values for(i=0; i<=n-2; i++) { for(i_=0; i_<=m; i_++) xy2.Set(i,i_,xy[i+1][i_]); s2[i]=s[i+1]; } //--- calculation for(i=0; iMathLog(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; ithreshold; //--- 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; ithreshold; } 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; i0.0) { sum=MathSqrt(-(2*MathLog(sum)/sum)); result=sigma*u*sum+mean; //--- return result return(result); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Moments estimates and their errors | //+------------------------------------------------------------------+ void CTestLinRegUnit::CalculateMV(double &x[],const int n,double &mean, double &means,double &stddev, double &stddevs) { //--- create variables int i=0; double v1=0; double v2=0; double variance=0; //--- initialization mean=0; means=1; stddev=0; stddevs=1; variance=0; //--- check if(n<=1) return; //--- Mean for(i=0; i0)) return; } //+------------------------------------------------------------------+ //| Testing class CXblas | //+------------------------------------------------------------------+ class CTestXBlasUnit { public: static bool TestXBlas(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CXblas | //+------------------------------------------------------------------+ bool CTestXBlasUnit::TestXBlas(const bool silent) { //--- create variables bool approxerrors; bool exactnesserrors; bool waserrors; double approxthreshold=0; int maxn=0; int passcount=0; int n=0; int i=0; int pass=0; double rv1=0; double rv2=0; double rv2err=0; complex cv1=0; complex cv2=0; double cv2err=0; double s=0; int i_=0; //--- create arrays double rx[]; double ry[]; complex cx[]; complex cy[]; double temp[]; //--- initialization approxerrors=false; exactnesserrors=false; waserrors=false; approxthreshold=1000*CMath::m_machineepsilon; maxn=1000; passcount=10; //--- tests: //--- 1. ability to calculate dot product //--- 2. higher precision for(n=1; n<=maxn; n++) { for(pass=1; pass<=passcount; pass++) { //--- ability to approximately calculate real dot product ArrayResize(rx,n); ArrayResize(ry,n); ArrayResize(temp,n); //--- change values for(i=0; i0.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_approxthreshold; //--- ability to approximately calculate complex dot product ArrayResize(cx,n); ArrayResize(cy,n); ArrayResize(temp,2*n); //--- change values for(i=0; i0.2) { cx[i].real=2*CMath::RandomReal()-1; cx[i].imag=2*CMath::RandomReal()-1; } else cx[i]=0; //--- check if(CMath::RandomReal()>0.2) { cy[i].real=2*CMath::RandomReal()-1; cy[i].imag=2*CMath::RandomReal()-1; } else cy[i]=0; } //--- change value cv1=0.0; for(i_=0; i_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; i4*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; i4*CMath::m_machineepsilon*MathAbs(ry[0])*n; exactnesserrors=exactnesserrors||MathAbs(rv2-n*ry[0])>rv2err; } //--- test of precision: complex n=50000; ArrayResize(cx,n); ArrayResize(cy,n); ArrayResize(temp,2*n); //--- calculation for(pass=0; pass<=passcount-1; pass++) { //--- check if(!CAp::Assert(n%2==0)) return(false); //--- First test: X + X + ... + X - X - X - ... - X=1*X s=MathExp(MathMax(pass,50)); //--- check if(pass==passcount-1 && pass>1) s=1E300; //--- change values cy[0].real=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); cy[0].imag=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal()); for(i=1; i4*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; i4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0])*n; exactnesserrors=exactnesserrors||CMath::AbsComplex(cv2-cy[0]*n)>cv2err; } //--- report waserrors=approxerrors||exactnesserrors; //--- check if(!silent) { Print("TESTING XBLAS"); PrintResult("APPROX.TESTS",!approxerrors); PrintResult("EXACT TESTS",!exactnesserrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- end //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CDenseSolver | //+------------------------------------------------------------------+ class CTestDenseSolverUnit { public: static bool TestDenseSolver(const bool silent); private: static bool RMatrixCheckSolutionM(CMatrixDouble &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixDouble &xs); static bool RMatrixCheckSolution(CMatrixDouble &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,double &xs[]); static bool RMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixDouble &xs); static bool RMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,double &xs[]); static bool CMatrixCheckSolutionM(CMatrixComplex &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixComplex &xs); static bool CMatrixCheckSolution(CMatrixComplex &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,complex &xs[]); static bool CMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixComplex &xs); static bool CMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,complex &xs[]); static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b); static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower); static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower); static void TestRSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &rerrors,bool &rfserrors); static void TestSPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &spderrors,bool &rfserrors); static void TestCSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &cerrors,bool &rfserrors); static void TestHPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &hpderrors,bool &rfserrors); static void Unset2D(CMatrixDouble &x); static void Unset1D(double &x[]); static void CUnset2D(CMatrixComplex &x); static void CUnset1D(complex &x[]); static void UnsetRep(CDenseSolverReport &r); static void UnsetLSRep(CDenseSolverLSReport &r); }; //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ bool CTestDenseSolverUnit::TestDenseSolver(const bool silent) { //--- create variables int maxn=0; int maxm=0; int passcount=0; double threshold=0; bool rerrors; bool cerrors; bool spderrors; bool hpderrors; bool rfserrors; bool waserrors; //--- initialization maxn=10; maxm=5; passcount=5; threshold=10000*CMath::m_machineepsilon; rfserrors=false; rerrors=false; cerrors=false; spderrors=false; hpderrors=false; //--- function calls TestRSolver(maxn,maxm,passcount,threshold,rerrors,rfserrors); TestSPDSolver(maxn,maxm,passcount,threshold,spderrors,rfserrors); TestCSolver(maxn,maxm,passcount,threshold,cerrors,rfserrors); TestHPDSolver(maxn,maxm,passcount,threshold,hpderrors,rfserrors); //--- search errors waserrors=(((rerrors||cerrors)||spderrors)||hpderrors)||rfserrors; //--- check if(!silent) { Print("TESTING DENSE SOLVER"); PrintResult("* REAL",!rerrors); PrintResult("* COMPLEX",!cerrors); PrintResult("* SPD",!spderrors); PrintResult("* HPD",!hpderrors); PrintResult("* ITERATIVE IMPROVEMENT",!rfserrors); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Checks whether solver results are correct solution. | //| Returns True on success. | //+------------------------------------------------------------------+ bool CTestDenseSolverUnit::RMatrixCheckSolutionM(CMatrixDouble &xe, const int n,const int m, const double threshold, const int info, CDenseSolverReport &rep, CMatrixDouble &xs) { bool result=true; //--- check if(info<=0) result=false; else { //--- calculation result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); for(int i=0; i1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(int i=0; i1+1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon); for(int i=0; i1000*CMath::m_machineepsilon); result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon); //--- check if(info==-3) { for(int i=0; ij) || (!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_1+1000*CMath::m_machineepsilon; rerrors=(rerrors||repls.m_n!=n)||repls.m_k!=0; for(i=0; ithreshold; } //--- change value info=0; //--- function calls UnsetLSRep(repls); Unset1D(xv); //--- allocation ArrayResize(bv,2*n); for(i_=0; i_1+1000*CMath::m_machineepsilon; rerrors=(rerrors||repls.m_n!=n)||repls.m_k!=0; for(i=0; ithreshold; } //--- change value info=0; //--- function calls UnsetLSRep(repls); Unset1D(xv); //--- allocation ArrayResize(bv,n); //--- change values for(i_=0; 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; i0.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_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; i8*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; i8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i])); } } } } //+------------------------------------------------------------------+ //| SPD test | //+------------------------------------------------------------------+ void CTestDenseSolverUnit::TestSPDSolver(const int maxn,const int maxm, const int passcount, const double threshold, bool &spderrors,bool &rfserrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int m=0; int pass=0; int taskkind=0; double v=0; bool isupper; int info=0; int i_=0; //--- create arrays int p[]; double bv[]; double xv[]; double y[]; double tx[]; //--- create matrix CMatrixDouble a; CMatrixDouble cha; CMatrixDouble atmp; CMatrixDouble xe; CMatrixDouble b; CMatrixDouble x; //--- objects of class CDenseSolverReport rep; CDenseSolverLSReport repls; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { for(m=1; m<=maxm; m++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A isupper=CMath::RandomReal()>0.5; CMatGen::SPDMatrixRndCond(n,1000,a); RMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::SPDMatrixCholesky(cha,n,isupper)) { spderrors=true; return; } //--- allocation xe.Resize(n,m); for(i=0; i0.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_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_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; i8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(bv[i])); } } //--- TODO: Test LS-solver on the same matrix } } //+------------------------------------------------------------------+ //| HPD test | //+------------------------------------------------------------------+ void CTestDenseSolverUnit::TestHPDSolver(const int maxn,const int maxm, const int passcount, const double threshold, bool &hpderrors,bool &rfserrors) { //--- create variables int i=0; int j=0; int k=0; int n=0; int m=0; int pass=0; int taskkind=0; complex v=0; bool isupper; int info=0; int i_=0; //--- create arrays int p[]; complex bv[]; complex xv[]; complex y[]; complex tx[]; //--- create matrix CMatrixComplex a; CMatrixComplex cha; CMatrixComplex atmp; CMatrixComplex xe; CMatrixComplex b; CMatrixComplex x; //--- objects of classes CDenseSolverReport rep; CDenseSolverLSReport repls; //--- General square matrices: //--- * test general solvers //--- * test least squares solver for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { for(m=1; m<=maxm; m++) { //--- ******************************************************** //--- WELL CONDITIONED TASKS //--- ability to find correct solution is tested //--- ******************************************************** //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe //--- 4. test different methods on original A isupper=CMath::RandomReal()>0.5; CMatGen::HPDMatrixRndCond(n,1000,a); CMatrixMakeACopy(a,n,n,cha); //--- check if(!CTrFac::HPDMatrixCholesky(cha,n,isupper)) { hpderrors=true; return; } //--- allocation xe.Resize(n,m); for(i=0; i0.001)||MathAbs(x[1])>0.001)||MathAbs(x[2]-2)>0.001; //--- F2 problem with restarts: //--- * make several iterations and restart BEFORE termination //--- * iterate and restart AFTER termination //--- NOTE: step is bounded from above to avoid premature convergence ArrayResize(x,3); //--- change values n=3; diffstep=1.0E-6; x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function calls CMinCG::MinCGSetCGType(state,cgtype); CMinCG::MinCGSetStpMax(state,0.1); CMinCG::MinCGSetCond(state,0.0000001,0.0,0.0,0); //--- calculation for(i=0; i<=10; i++) { //--- check if(!CMinCG::MinCGIteration(state)) break; //--- function call TestFunc2(state); } //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc2(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01; //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinCG::MinCGRestartFrom(state,x); //--- cycle while(CMinCG::MinCGIteration(state)) TestFunc2(state); //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01; //--- 1D problem #1 ArrayResize(x,1); //--- change values n=1; diffstep=1.0E-6; x[0]=100*CMath::RandomReal()-50; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=-MathCos(state.m_x[0]); //--- check if(state.m_needfg) state.m_g.Set(0,MathSin(state.m_x[0])); } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors linerror1=(linerror1||rep.m_terminationtype<=0)||MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001; //--- 1D problem #2 ArrayResize(x,1); //--- change values n=1; diffstep=1.0E-6; x[0]=100*CMath::RandomReal()-50; //--- check if(difftype==0) CMinCG::MinCGCreate(n,x,state); //--- check if(difftype==1) CMinCG::MinCGCreateF(n,x,diffstep,state); //--- function call CMinCG::MinCGSetCGType(state,cgtype); //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0])); //--- check if(state.m_needfg) state.m_g.Set(0,(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0]))); } //--- function call CMinCG::MinCGResults(state,x,rep); //--- search errors linerror2=(linerror2||rep.m_terminationtype<=0)||MathAbs(x[0])>0.001; //--- Linear equations diffstep=1.0E-6; for(n=1; n<=10; n++) { //--- Prepare task a.Resize(n,n); ArrayResize(x,n); ArrayResize(xe,n); ArrayResize(b,n); for(i=0; i0.001; } //--- Testing convergence properties diffstep=1.0E-6; n=3; //--- allocation ArrayResize(x,n); for(i=0; ifprev; //--- check if(fprev==CMath::m_maxrealnumber) { for(i=0; iMathLog(1+1000*CMath::m_machineepsilon); //--- Test maximum step n=1; ArrayResize(x,n); x[0]=100; stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinCG::MinCGCreate(n,x,state); CMinCG::MinCGSetCond(state,1.0E-9,0,0,0); CMinCG::MinCGSetStpMax(state,stpmax); CMinCG::MinCGSetXRep(state,true); xprev=x[0]; //--- cycle while(CMinCG::MinCGIteration(state)) { //--- check if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0])); //--- search errors err=err||MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax; } //--- check if(state.m_xupdated) { //--- search errors err=err||MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax; xprev=state.m_x[0]; } } //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100] //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set stringent stopping conditions (we try EpsG and EpsX) //--- * and we test that in the extremum stopping conditions are //--- satisfied subject to the current scaling coefficients. tmpeps=1.0E-10; for(n=1; n<=10; n++) { for(pkind=0; pkind<=2; pkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); ArrayResize(a,n); ArrayResize(s,n); ArrayResize(h,n); //--- change values for(i=0; itmpeps; //--- Test step-based stopping condition for(i=0; itmpeps; } } //--- 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 | //+------------------------------------------------------------------+ void CTestMinCGUnit::TestFunc1(CMinCGState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber)); state.m_g.Set(1,0); state.m_g.Set(2,0); } } } //+------------------------------------------------------------------+ //| Calculate test function #2 | //| Simple variation of #1,much more nonlinear,which makes unlikely | //| premature convergence of algorithm . | //+------------------------------------------------------------------+ void CTestMinCGUnit::TestFunc2(CMinCGState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,4*state.m_x[1]*CMath::Sqr(state.m_x[1])); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber)); state.m_g.Set(1,0); state.m_g.Set(2,0); } } } //+------------------------------------------------------------------+ //| Calculate test function #3 | //| Simple variation of #1,much more nonlinear,with non-zero value at| //| minimum. | //| It achieve two goals: | //| * makes unlikely premature convergence of algorithm. | //| * solves some issues with EpsF stopping condition which arise | //| when F(minimum) is zero | //+------------------------------------------------------------------+ void CTestMinCGUnit::TestFunc3(CMinCGState &state) { double s=0.001; //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber)); state.m_g.Set(1,0); state.m_g.Set(2,0); } } } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ void CTestMinCGUnit::CalcIIP2(CMinCGState &state,const int n) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- calculation for(int i=0; i0) { //--- allocation v.Resize(vs,n); ArrayResize(vd,vs); //--- change values for(i=0; ibndu[i]) err=true; } } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ void CTestMinBLEICUnit::CalcIIP2(CMinBLEICState &state,const int n, const int fk) { //--- check if(state.m_needfg) state.m_f=0; //--- calculation for(int i=0; i::Zeros(n); g=vector::Zeros(n); c=matrix::Zeros(1,n+1); ct.Resize(1); for(i=0; iepsfeas; //--- if C is nonzero, test that result is //--- a stationary point of constrained F. //--- NOTE: this check is done only if C is nonzero vv=CAblasF::RDotRR(n,c,0,c,0); if(vv!=0.0) { //--- Calculate gradient at the result //--- Project gradient into C //--- Check projected norm g=x.Pow(p-1)*p; v2=CAblasF::RDotRR(n,c,0,c,0); v=CAblasF::RDotVR(n,g,c,0); vv=v/v2; for(i_=0; i_weakepsg; } } } } } //--- Test problem 2 (multiple equality constraints): //--- * 1<=N<=NMax, 1<=K<=N //--- * no boundary constraints //--- * N-dimensional space //--- * randomly generated point xs //--- * K randomly generated hyperplanes which all pass through xs //--- define K equality constraints: (a[k],x)=b[k] //--- * equality constraints are checked for being well conditioned //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * f(x) = |x-x0|^2, x0 = xs+a[0] //--- * either analytic gradient or numerical differentiation are used //--- * extremum of f(x) is exactly xs because: //--- * xs is the closest point in the plane defined by (a[0],x)=b[0] //--- * xs is feasible by definition diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { for(preckind=0; preckind<=2; preckind++) { for(n=2; n<=nmax; n++) { for(k=1; k<=n; k++) { //--- Generate X, X0, XS, BL, BU, CT and left part of C. //--- Right part of C is generated using somewhat complex algo: //--- * we generate random vector and multiply it by C. //--- * result is used as the right part. //--- * calculations are done on the fly, vector itself is not stored //--- We use such algo to be sure that our system is consistent. x=vector::Zeros(n); x0=vector::Zeros(n); xs=vector::Zeros(n); g=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.0 && svdw[k-1]>(0.001*svdw[0]))); for(i_=0; i_epsx; } //--- Compare with XS v=MathPow(x.ToVector()-xs.ToVector(),2.0).Sum(); v=MathSqrt(v); converr=converr||MathAbs(v)>0.001; } } } } //--- Another simple problem: //--- * bound constraints 0 <= x[i] <= 1 //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple boundaries and function it is easy to find //--- analytic form of solution: S[i] = bound(x0[i], 0, 1) //--- * we also check that both final solution and subsequent iterates //--- are strictly feasible diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { //--- Generate X, BL, BU. p=2*pkind; bl=vector::Zeros(n); bu=vector::Ones(n); x=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i1.0; } CMinBLEIC::MinBLEICResults(state,x,rep); if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility v=0.0; for(i=0; i0.0 && x[i]<1.0) v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1)); feaserr=feaserr||x[i]<0.0; feaserr=feaserr||x[i]>1.0; } converr=converr||MathSqrt(v)>weakepsg; } } } } //--- Same as previous problem, but with minor modifications: //--- * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple boundaries and function it is easy to find //--- analytic form of solution: S[i] = bound(x0[i], 0, 1) //--- * we also check that both final solution and subsequent iterates //--- are strictly feasible diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { //--- Generate X, BL, BU. p=2*pkind; bl=vector::Zeros(n); bu=vector::Zeros(n); x=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.5) { bl.Set(i,0); bu.Set(i,1); } else { bl.Set(i,CMath::RandomReal()); bu.Set(i,bl[i]); } x.Set(i,CMath::RandomReal()); x0.Set(i,3*CMath::RandomReal()-1); } //--- Create and optimize if(dkind==0) CMinBLEIC::MinBLEICCreate(n,x,state); if(dkind==1) CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0); SetRandomPreconditioner(state,n,preckind); while(CMinBLEIC::MinBLEICIteration(state)) { if(state.m_needf || state.m_needfg) state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum(); if(state.m_needfg) state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p; for(i=0; ibu[i]; } } CMinBLEIC::MinBLEICResults(state,x,rep); if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility v=0.0; for(i=0; ibl[i] && x[i]bu[i]; } converr=converr||MathSqrt(v)>weakepsg; } } } } //--- Same as previous one, but with bound constraints posed //--- as general linear ones: //--- * no bound constraints //--- * 2*N linear constraints 0 <= x[i] <= 1 //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple constraints and function it is easy to find //--- analytic form of solution: S[i] = bound(x0[i], 0, 1). //--- * however, we can't guarantee that solution is strictly feasible //--- with respect to nonlinearity constraint, so we check //--- for approximate feasibility. for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { //--- Generate X, BL, BU. p=2*pkind; x=vector::Zeros(n); x0=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); ct.Resize(2*n); for(i=0; i::Zeros(n+1)); c.Row(2*i+1,vector::Zeros(n+1)); c.Set(2*i,i,1); c.Set(2*i+0,n,0); ct.Set(2*i,1); c.Set(2*i+1,i,1); c.Set(2*i+1,n,1); ct.Set(2*i+1,-1); } //--- Create and optimize CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetLC(state,c,ct,2*n); CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0); SetRandomPreconditioner(state,n,preckind); while(CMinBLEIC::MinBLEICIteration(state)) { if(state.m_needfg) { state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum(); state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p; continue; } //--- Unknown protocol specified interr=true; return; } CMinBLEIC::MinBLEICResults(state,x,rep); if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility v=0.0; for(i=0; i0.02 && x[i]<0.98) v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1)); feaserr=feaserr||x[i]<(0.0-epsfeas); feaserr=feaserr||x[i]>(1.0+epsfeas); } converr=converr||MathSqrt(v)>weakepsg; } } } //--- Feasibility problem: //--- * bound constraints 0<=x[i]<=1 //--- * starting point xs with xs[i] in [-1,+2] //--- * random point xc from [0,1] is used to generate K<=N //--- random linear equality/inequality constraints of the form //--- (c,x-xc)=0.0 (or, alternatively, >= or <=), where //--- c is a random vector. //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * we do not know analytic form of the solution, and, if fact, we do not //--- check for solution correctness. We just check that algorithm converges //--- to the feasible points. for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { for(k=1; k<=n; k++) { //--- Generate X, BL, BU. p=2*pkind; x0=vector::Zeros(n); xc=vector::Zeros(n); xs=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); bl=vector::Zeros(n); bu=vector::Ones(n); for(i=0; i1.0; } for(i=0; iepsfeas; if(ct[i]<0) feaserr=feaserr||v>epsfeas; if(ct[i]>0) feaserr=feaserr||v<(-epsfeas); } } } } } //--- Infeasible problem: //--- * all bound constraints are 0 <= x[i] <= 1 except for one //--- * that one is 0 >= x[i] >= 1 //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from detecting //--- infeasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * algorithm must return correct error code on such problem for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { //--- Generate X, BL, BU. p=2*pkind; bl=vector::Zeros(n); bu=vector::Ones(n); x=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(n); c=matrix::Zeros(k+1,n+1); ct.Resize(k+1); ct.Fill(0); for(i=0; iMathLog(1+1000*CMath::m_machineepsilon); //--- Test stpmax n=1; ArrayResize(x,n); ArrayResize(bl,n); ArrayResize(bu,n); //--- change values x[0]=100; bl[0]=2*CMath::RandomReal()-1; bu[0]=CInfOrNaN::PositiveInfinity(); stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); CMinBLEIC::MinBLEICSetXRep(state,true); CMinBLEIC::MinBLEICSetStpMax(state,stpmax); xprev=x[0]; //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0])); err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); } //--- check if(state.m_xupdated) { err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); xprev=state.m_x[0]; } } //--- Ability to solve problems with function which is unbounded from below n=1; ArrayResize(x,n); ArrayResize(bl,n); ArrayResize(bu,n); bl[0]=4*CMath::RandomReal()+1; bu[0]=bl[0]+1; x[0]=0.5*(bl[0]+bu[0]); //--- function calls CMinBLEIC::MinBLEICCreate(n,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0])); state.m_g.Set(0,-(2.0E8*state.m_x[0])); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- search errors err=err||MathAbs(x[0]-bu[0])>epsc; //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100] //--- * function is EFFECTIVELY unconstrained;it has formal constraints, //--- but they are inactive at the solution;we try different variants //--- in order to explore different control paths of the optimizer: //--- 0) absense of constraints //--- 1) bound constraints -100000<=x[i]<=100000 //--- 2) one linear constraint 0*x=0 //--- 3) combination of (1) and (2) //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set very mild outer stopping conditions - OuterEpsX=1.0,but //--- inner conditions are very stringent //--- * and we test that in the extremum inner stopping conditions are //--- satisfied subject to the current scaling coefficients. tmpeps=1.0E-10; for(n=1; n<=10; n++) { for(ckind=0; ckind<=3; ckind++) { for(pkind=0; pkind<=2; pkind++) { //--- allocation ArrayResize(x,n); ArrayResize(a,n); ArrayResize(s,n); ArrayResize(h,n); ArrayResize(bl,n); ArrayResize(bu,n); c.Resize(1,n+1); ArrayResize(ct,1); ct[0]=0; c.Set(0,n,0); //--- change values for(i=0; itmpeps; } } } //--- 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) | //+------------------------------------------------------------------+ void CTestMinBLEICUnit::TestConv(bool &err) { //--- create variables int passcount=10; int pass=0; double epsc=1.0E-4; double epsg=1.0E-8; double tol=0.001; //--- create arrays double bl[]; double bu[]; double x[]; int ct[]; //--- create matrix CMatrixDouble c; //--- objects of classes CMinBLEICState state; CMinBLEICReport rep; //--- Three closely connected problems: //--- * 2-dimensional space //--- * octagonal area bounded by: //--- * -1<=x<=+1 //--- * -1<=y<=+1 //--- * x+y<=1.5 //--- * x-y<=1.5 //--- * -x+y<=1.5 //--- * -x-y<=1.5 //--- * several target functions: //--- * f0=x+0.001*y,minimum at x=-1,y=-0.5 //--- * f1=(x+10)^2+y^2,minimum at x=-1,y=0 //--- * f2=(x+10)^2+(y-0.6)^2,minimum at x=-1,y=0.5 ArrayResize(x,2); ArrayResize(bl,2); ArrayResize(bu,2); c.Resize(4,3); ArrayResize(ct,4); //--- change values bl[0]=-1; bl[1]=-1; bu[0]=1; bu[1]=1; c.Set(0,0,1); c.Set(0,1,1); c.Set(0,2,1.5); ct[0]=-1; c.Set(1,0,1); c.Set(1,1,-1); c.Set(1,2,1.5); ct[1]=-1; c.Set(2,0,-1); c.Set(2,1,1); c.Set(2,2,1.5); ct[2]=-1; c.Set(3,0,-1); c.Set(3,1,-1); c.Set(3,2,1.5); ct[3]=-1; //--- calculation for(pass=1; pass<=passcount; pass++) { //--- f0 x[0]=0.2*CMath::RandomReal()-0.1; x[1]=0.2*CMath::RandomReal()-0.1; //--- function call CMinBLEIC::MinBLEICCreate(2,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetLC(state,c,ct,4); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=state.m_x[0]+0.001*state.m_x[1]; state.m_g.Set(0,1); state.m_g.Set(1,0.001); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { //--- search errors err=err||MathAbs(x[0]+1)>tol; err=err||MathAbs(x[1]+0.5)>tol; } else err=true; //--- f1 x[0]=0.2*CMath::RandomReal()-0.1; x[1]=0.2*CMath::RandomReal()-0.1; //--- function calls CMinBLEIC::MinBLEICCreate(2,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetLC(state,c,ct,4); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]); state.m_g.Set(0,2*(state.m_x[0]+10)); state.m_g.Set(1,2*state.m_x[1]); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { //--- search errors err=err||MathAbs(x[0]+1)>tol; err=err||MathAbs(x[1])>tol; } else err=true; //--- f2 x[0]=0.2*CMath::RandomReal()-0.1; x[1]=0.2*CMath::RandomReal()-0.1; //--- function calls CMinBLEIC::MinBLEICCreate(2,x,state); CMinBLEIC::MinBLEICSetBC(state,bl,bu); CMinBLEIC::MinBLEICSetLC(state,c,ct,4); CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0); CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc); //--- cycle while(CMinBLEIC::MinBLEICIteration(state)) { //--- check if(state.m_needfg) { state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]-0.6); state.m_g.Set(0,2*(state.m_x[0]+10)); state.m_g.Set(1,2*(state.m_x[1]-0.6)); } } //--- function call CMinBLEIC::MinBLEICResults(state,x,rep); //--- check if(rep.m_terminationtype>0) { //--- search errors err=err||MathAbs(x[0]+1)>tol; err=err||MathAbs(x[1]-0.5)>tol; } else err=true; } } //+------------------------------------------------------------------+ //| This function tests preconditioning | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ void CTestMinBLEICUnit::TestPreconditioning(bool &err) { //--- create variables int pass=0; int n=0; int i=0; int k=0; int cntb1=0; int cntb2=0; int cntg1=0; int cntg2=0; double epsg=0; int fkind=0; int ckind=0; int fk=0; //--- create arrays double x[]; double x0[]; int ct[]; double bl[]; double bu[]; double vd[]; double d[]; double units[]; double s[]; double diagh[]; //--- create matrix CMatrixDouble v; CMatrixDouble c; //--- objects of classes CMinBLEICState state; CMinBLEICReport rep; //--- Preconditioner test 1. //--- If //--- * B1 is default preconditioner with unit scale //--- * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix //--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i]) //--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i]) //--- then B1 is worse than G1,B2 is worse than G2. //--- "Worse" means more iterations to converge. //--- Test problem setup: //--- * f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) //--- * FK is either +1 or -1 (we try both to test different aspects of preconditioning) //--- * constraints: //--- 0) absent //--- 1) boundary only //--- 2) linear equality only //--- 3) combination of boundary and linear equality constraints //--- N - problem size //--- K - number of repeated passes (should be large enough to average out random factors) k=30; epsg=1.0E-10; for(n=5; n<=8; n++) { for(fkind=0; fkind<=1; fkind++) { for(ckind=0; ckind<=3; ckind++) { fk=1-2*fkind; //--- allocation ArrayResize(x,n); ArrayResize(units,n); for(i=0; i0) { for(i=0; ithreshold; } } 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; i0) { xy.Set(0,i-1,offdiagonal); for(j=0; j0) { for(i=0; ithreshold; } } 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; i0) { xy.Set(0,i-1,v0*offdiagonal); for(j=0; j0) { for(i=0; ithreshold; } } else err=true; } } //+------------------------------------------------------------------+ //| Test for different combinations of "entry"/"exit" models | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ void CTestMCPDUnit::TestEntryExit(bool &err) { //--- create variables int n=0; double threshold=1.0E-3; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int popkind=0; int i=0; int j=0; int k=0; double v=0; int i_=0; //--- create matrix CMatrixDouble p; CMatrixDouble pexact; CMatrixDouble xy; //--- objects of classes CMCPDState s; CMCPDReport rep; //--- calculation for(n=2; n<=5; n++) { for(entrykind=0; entrykind<=1; entrykind++) { for(exitkind=0; exitkind<=1; exitkind++) { for(popkind=0; popkind<=1; popkind++) { //--- Generate EntryState/ExitState such that one of the following is True: //--- * EntryState<>ExitState //--- * EntryState=-1 or ExitState=-1 do { //--- check if(entrykind==0) entrystate=-1; else entrystate=CMath::RandomInteger(n); //--- check if(exitkind==0) exitstate=-1; else exitstate=CMath::RandomInteger(n); } while(!((entrystate==-1 || exitstate==-1) || entrystate!=exitstate)); //--- Generate transition matrix P such that: //--- * columns corresponding to non-exit states sums to 1.0 //--- * columns corresponding to exit states sums to 0.0 //--- * rows corresponding to entry states are zero pexact.Resize(n,n); for(i=0; i=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; k0.0) { for(j=0; j0) { for(i=0; ithreshold; } } else err=true; } } } } } //+------------------------------------------------------------------+ //| Test equality constraints. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ void CTestMCPDUnit::TestEC(bool &err) { //--- create variables int n=0; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int i=0; int j=0; int ic=0; int jc=0; double vc=0; //--- create matrix CMatrixDouble p; CMatrixDouble ec; CMatrixDouble xy; //--- objects of classes CMCPDState s; CMCPDReport rep; //--- We try different problems with following properties: //--- * N is large enough - we won't have problems with inconsistent constraints //---*first state is either "entry" or "normal" //---*last state is either "exit" or "normal" //--- * we have one long random track //--- We test several properties which are described in comments below for(n=4; n<=6; n++) { for(entrykind=0; entrykind<=1; entrykind++) { for(exitkind=0; exitkind<=1; exitkind++) { //--- Prepare problem if(entrykind==0) entrystate=-1; else entrystate=0; //--- check if(exitkind==0) exitstate=-1; else exitstate=n-1; //--- allocation xy.Resize(2*n,n); for(i=0; i<=CAp::Rows(xy)-1; i++) { for(j=0; j<=CAp::Cols(xy)-1; j++) xy.Set(i,j,CMath::RandomReal()); } //--- Test that single equality constraint on non-entry //--- non-exit elements of P is satisfied. //--- NOTE: this test needs N>=4 because smaller values //--- can give us inconsistent constraints if(!CAp::Assert(n>=4,"TestEC: expectation failed")) return; ic=1+CMath::RandomInteger(n-2); jc=1+CMath::RandomInteger(n-2); vc=CMath::RandomReal(); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDAddEC(s,ic,jc,vc); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) err=err||p[ic][jc]!=vc; else err=true; //--- Test interaction with default "sum-to-one" constraint //--- on columns of P. //--- We set N-1 equality constraints on random non-exit column //--- of P,which are inconsistent with this default constraint //--- (sum will be greater that 1.0). //--- Algorithm must detect inconsistency. //--- NOTE: //--- 1. we do not set constraints for the first element of //--- the column,because this element may be constrained by //--- "exit state" constraint. //--- 2. this test needs N>=3 if(!CAp::Assert(n>=3,"TestEC: expectation failed")) return; jc=CMath::RandomInteger(n-1); vc=0.95; //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); for(i=1; i=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; i0) { for(i=0; i=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=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; i0) { for(i=0; ibndu.Get(i,j); } } } else err=true; } } } } //+------------------------------------------------------------------+ //| Test bound constraints. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ void CTestMCPDUnit::TestLC(bool &err) { //--- create variables int n=0; int entrystate=0; int exitstate=0; int entrykind=0; int exitkind=0; int i=0; int j=0; int k=0; int t=0; int jc=0; double v=0; double threshold=0; //--- create array int ct[]; //--- create matrix CMatrixDouble p; CMatrixDouble c; CMatrixDouble xy; //--- objects of classes CMCPDState s; CMCPDReport rep; //--- initialization threshold=1.0E5*CMath::m_machineepsilon; //--- We try different problems with following properties: //--- * N is large enough - we won't have problems with inconsistent constraints //---*first state is either "entry" or "normal" //---*last state is either "exit" or "normal" //--- * we have one long random track //--- We test several properties which are described in comments below for(n=4; n<=6; n++) { for(entrykind=0; entrykind<=1; entrykind++) { for(exitkind=0; exitkind<=1; exitkind++) { //--- Prepare problem if(entrykind==0) entrystate=-1; else entrystate=0; //--- check if(exitkind==0) exitstate=-1; else exitstate=n-1; //--- allocation xy.Resize(2*n,n); for(i=0; i<=CAp::Rows(xy)-1; i++) { for(j=0; j<=CAp::Cols(xy)-1; j++) xy.Set(i,j,CMath::RandomReal()); } //--- Test that single linear equality/inequality constraint //--- on non-entry non-exit elements of P is satisfied. //--- NOTE 1: this test needs N>=4 because smaller values //--- can give us inconsistent constraints //--- NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N) //--- is always feasible. It guarantees that there always exists //--- at least one feasible point //--- NOTE 3: If we have inequality constraint,we "shift" right part //--- in order to make feasible some neighborhood of P=(1/N ... 1/N). if(!CAp::Assert(n>=4,"TestLC: expectation failed")) return; //--- allocation c.Resize(1,n*n+1); ArrayResize(ct,1); //--- calculation v=0; for(i=0; i0) c.Set(0,n*n,c[0][n*n]-0.1); //--- function calls CreateEE(n,entrystate,exitstate,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,1); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { v=0; for(i=0; i=c[0][n*n]+threshold; //--- check if(ct[0]==0) err=err||MathAbs(v-c[0][n*n])>=threshold; //--- check if(ct[0]>0) err=err||v<=c[0][n*n]-threshold; } else err=true; //--- Test interaction with default "sum-to-one" constraint //--- on columns of P. //--- We set linear constraint which has for "sum-to-X" on //--- on random non-exit column of P. This constraint can be //--- either consistent (X=1.0) or inconsistent (X<>1.0) with //--- this default constraint. //--- Algorithm must detect inconsistency. //--- NOTE: //--- 1. this test needs N>=2 if(!CAp::Assert(n>=2,"TestLC: expectation failed")) return; jc=CMath::RandomInteger(n-1); //--- allocation c.Resize(1,n*n+1); ArrayResize(ct,1); //--- change values for(i=0; i<=n*n-1; i++) c.Set(0,i,0.0); for(i=0; i=0) { c.Resize(1,n*n+1); ArrayResize(ct,1); for(i=0; i<=n*n-1; i++) c.Set(0,i,0.0); for(j=0; j=0) { c.Resize(1,n*n+1); ArrayResize(ct,1); //--- change values for(i=0; i<=n*n-1; i++) c.Set(0,i,0.0); for(i=0; i0) c.Set(i,n*n,c[i][n*n]-0.1); } //--- Test CreateEE(n,-1,-1,s); CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy)); CMarkovCPD::MCPDSetLC(s,c,ct,k); CMarkovCPD::MCPDSolve(s); CMarkovCPD::MCPDResults(s,p,rep); //--- check if(rep.m_terminationtype>0) { for(t=0; t=c[t][n*n]+threshold; //--- check if(ct[t]==0) err=err||MathAbs(v-c[t][n*n])>=threshold; //--- check if(ct[t]>0) err=err||v<=c[t][n*n]-threshold; } } else err=true; } } } //+------------------------------------------------------------------+ //| This function is used to create MCPD object with arbitrary | //| combination of entry and exit states | //+------------------------------------------------------------------+ void CTestMCPDUnit::CreateEE(const int n,const int entrystate, const int exitstate,CMCPDState &s) { //--- check if(entrystate<0 && exitstate<0) CMarkovCPD::MCPDCreate(n,s); //--- check if(entrystate>=0 && exitstate<0) CMarkovCPD::MCPDCreateEntry(n,entrystate,s); //--- check if(entrystate<0 && exitstate>=0) CMarkovCPD::MCPDCreateExit(n,exitstate,s); //--- check if(entrystate>=0 && exitstate>=0) CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s); } //+------------------------------------------------------------------+ //| Testing class CFbls | //+------------------------------------------------------------------+ class CTestFblsUnit { public: static bool TestFbls(const bool silent); }; //+------------------------------------------------------------------+ //| Testing | //+------------------------------------------------------------------+ bool CTestFblsUnit::TestFbls(const bool silent) { //--- create variables int n=0; int m=0; int mx=0; int i=0; int j=0; bool waserrors; bool cgerrors; double v=0; double v1=0; double v2=0; double alpha=0; double e1=0; double e2=0; int i_=0; //--- create arrays CRowDouble tmp1; CRowDouble tmp2; double b[]; CRowDouble x; double xe[]; double buf[]; //--- create matrix CMatrixDouble a; //--- object of class CFblsLinCgState cgstate; //--- initialization mx=10; waserrors=false; cgerrors=false; //--- Test CG solver: //--- * generate problem (A,B,Alpha,XE - exact solution) and initial approximation X //--- * E1=||A'A*x-b|| //--- * solve //--- * E2=||A'A*x-b|| //--- * test that E2<0.001*E1 for(n=1; n<=mx; n++) { for(m=1; m<=mx; m++) { //--- allocation a.Resize(m,n); ArrayResize(b,n); x.Resize(n); ArrayResize(xe,n); tmp1.Resize(m); tmp2.Resize(n); //--- init A,alpha,B,X (initial approximation),XE (exact solution) //--- X is initialized in such way that is has no chances to be equal to XE. for(i=0; i0.001*e1; //--- Test sparse CG (which relies on reverse communication) for(i=0; i100*CMath::m_machineepsilon*e1; cgerrors=cgerrors||MathAbs(e2-cgstate.m_e2)>100*CMath::m_machineepsilon*e1; cgerrors=cgerrors||e2>0.001*e1; } } //--- report waserrors=cgerrors; //--- check if(!silent) { Print("TESTING FBLS"); PrintResult("CG ERRORS",!cgerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CMinLBFGS | //+------------------------------------------------------------------+ class CTestMinLBFGSUnit { public: static bool TestMinLBFGS(const bool silent); private: static void TestFunc1(CMinLBFGSState &state); static void TestFunc2(CMinLBFGSState &state); static void TestFunc3(CMinLBFGSState &state); static void CalcIIP2(CMinLBFGSState &state,const int n); static void TestPreconditioning(bool &err); static void TestOther(bool &err); }; //+------------------------------------------------------------------+ //| Testing class CMinLBFGS | //+------------------------------------------------------------------+ bool CTestMinLBFGSUnit::TestMinLBFGS(const bool silent) { //--- create variables bool waserrors; bool referror; bool nonconverror; bool eqerror; bool converror; bool crashtest; bool othererrors; bool restartserror; bool precerror; int n=0; int m=0; int i=0; int j=0; double v=0; int maxits=0; double diffstep=0; int dkind=0; int i_=0; //--- create arrays double x[]; double xe[]; double b[]; double xlast[]; double diagh[]; //--- create matrix CMatrixDouble a; //--- objects of classes CMinLBFGSState state; CMinLBFGSReport rep; //--- initialization waserrors=false; precerror=false; nonconverror=false; restartserror=false; eqerror=false; converror=false; crashtest=false; othererrors=false; referror=false; //--- function calls TestPreconditioning(precerror); TestOther(othererrors); //--- Reference problem diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { //--- allocation ArrayResize(x,3); n=3; m=2; x[0]=100*CMath::RandomReal()-50; x[1]=100*CMath::RandomReal()-50; x[2]=100*CMath::RandomReal()-50; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { state.m_g.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors referror=((rep.m_terminationtype<=0||MathAbs(x[0]-2)>0.001)||MathAbs(x[1])>0.001)||MathAbs(x[2]-2)>0.001; } //--- nonconvex problems with complex surface: we start from point with very small //--- gradient,but we need ever smaller gradient in the next step due to //--- Wolfe conditions. diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { //--- allocation ArrayResize(x,1); n=1; m=1; v=-100; //--- calculation while(v<0.1) { x[0]=v; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0])); //--- check if(state.m_needfg) state.m_g.Set(0,(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0]))); } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors nonconverror=(nonconverror||rep.m_terminationtype<=0)||MathAbs(x[0])>0.001; v+=0.1; } } //--- F2 problem with restarts: //--- * make several iterations and restart BEFORE termination //--- * iterate and restart AFTER termination //--- NOTE: step is bounded from above to avoid premature convergence diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { //--- allocation ArrayResize(x,3); //--- change values n=3; m=2; x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function calls CMinLBFGS::MinLBFGSSetStpMax(state,0.1); CMinLBFGS::MinLBFGSSetCond(state,0.0000001,0.0,0.0,0); //--- calculation for(i=0; i<=10; i++) { //--- check if(!CMinLBFGS::MinLBFGSIteration(state)) break; TestFunc2(state); } //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc2(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01; //--- change values x[0]=10+10*CMath::RandomReal(); x[1]=10+10*CMath::RandomReal(); x[2]=10+10*CMath::RandomReal(); //--- function call CMinLBFGS::MinLBFGSRestartFrom(state,x); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc2(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors restartserror=(((restartserror||rep.m_terminationtype<=0)||MathAbs(x[0]-MathLog(2))>0.01)||MathAbs(x[1])>0.01)||MathAbs(x[2]-MathLog(2))>0.01; } //--- Linear equations diffstep=1.0E-6; for(n=1; n<=10; n++) { //--- Prepare task a.Resize(n,n); ArrayResize(x,n); ArrayResize(xe,n); ArrayResize(b,n); //--- change values for(i=0; i0.001; } } } //--- Testing convergence properties diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { //--- allocation ArrayResize(x,3); //--- change values n=3; m=2; for(i=0; i<=2; i++) x[i]=6*CMath::RandomReal()-3; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0.001,0,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=converror||rep.m_terminationtype!=4; //--- change values for(i=0; i<=2; i++) x[i]=6*CMath::RandomReal()-3; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0.001,0,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=converror||rep.m_terminationtype!=1; //--- change values for(i=0; i<=2; i++) x[i]=6*CMath::RandomReal()-3; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0.001,0); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=converror||rep.m_terminationtype!=2; //--- change values for(i=0; i<=2; i++) x[i]=2*CMath::RandomReal()-1; //--- check if(dkind==0) CMinLBFGS::MinLBFGSCreate(n,m,x,state); //--- check if(dkind==1) CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state); //--- function call CMinLBFGS::MinLBFGSSetCond(state,0,0,0,10); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) TestFunc3(state); //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors converror=(converror||rep.m_terminationtype!=5)||rep.m_iterationscount!=10; } //--- Crash test: too many iterations on a simple tasks //--- May fail when encounter zero step,underflow or something like that ArrayResize(x,3); n=3; m=2; maxits=10000; for(i=0; i<=2; i++) x[i]=6*CMath::RandomReal()-3; //--- function calls CMinLBFGS::MinLBFGSCreate(n,m,x,state); CMinLBFGS::MinLBFGSSetCond(state,0,0,0,maxits); //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } //--- function call CMinLBFGS::MinLBFGSResults(state,x,rep); //--- search errors crashtest=crashtest||rep.m_terminationtype<=0; //--- end waserrors=((((((referror||nonconverror)||eqerror)||converror)||crashtest)||othererrors)||restartserror)||precerror; //--- check if(!silent) { Print("TESTING L-BFGS OPTIMIZATION"); PrintResult("REFERENCE PROBLEM",!referror); PrintResult("NON-CONVEX PROBLEM",!nonconverror); PrintResult("LINEAR EQUATIONS",!eqerror); PrintResult("RESTARTS",!restartserror); PrintResult("PRECONDITIONER",!precerror); PrintResult("CONVERGENCE PROPERTIES",!converror); PrintResult("CRASH TEST",!crashtest); PrintResult("OTHER PROPERTIES",!othererrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Calculate test function #1 | //| It may show very interesting behavior when optimized with | //| 'x[0]>=ln(2)' constraint. | //+------------------------------------------------------------------+ void CTestMinLBFGSUnit::TestFunc1(CMinLBFGSState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { //--- calculation state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { //--- calculation state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber)); state.m_g.Set(1,0); state.m_g.Set(2,0); } } } //+------------------------------------------------------------------+ //| Calculate test function #2 | //| Simple variation of #1,much more nonlinear,which makes unlikely | //| premature convergence of algorithm. | //+------------------------------------------------------------------+ void CTestMinLBFGSUnit::TestFunc2(CMinLBFGSState &state) { //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { //--- calculation state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,4*state.m_x[1]*CMath::Sqr(state.m_x[1])); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { //--- calculation state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber)); state.m_g.Set(1,0); state.m_g.Set(2,0); } } } //+------------------------------------------------------------------+ //| Calculate test function #3 | //| Simple variation of #1, much more nonlinear, with non-zero value | //| at minimum. It achieve two goals: | //| * makes unlikely premature convergence of algorithm . | //| * solves some issues with EpsF stopping condition which arise | //| when F(minimum) is zero | //+------------------------------------------------------------------+ void CTestMinLBFGSUnit::TestFunc3(CMinLBFGSState &state) { double s=0.001; //--- check if(state.m_x[0]<100.0) { //--- check if(state.m_needf || state.m_needfg) state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]); //--- check if(state.m_needfg) { //--- calculation state.m_g.Set(0,2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } } else { //--- check if(state.m_needf || state.m_needfg) state.m_f=MathSqrt(CMath::m_maxrealnumber); //--- check if(state.m_needfg) { //--- calculation state.m_g.Set(0,MathSqrt(CMath::m_maxrealnumber)); state.m_g.Set(1,0); state.m_g.Set(2,0); } } } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ void CTestMinLBFGSUnit::CalcIIP2(CMinLBFGSState &state,const int n) { //--- check if(state.m_needf || state.m_needfg) state.m_f=0; //--- calculation for(int i=0; ii) || (pkind==1 && jfprev; //--- check if(fprev==CMath::m_maxrealnumber) { for(i=0; iMathLog(1+1000*CMath::m_machineepsilon); //--- test maximum step n=1; m=1; //--- allocation ArrayResize(x,n); x[0]=100; stpmax=0.05+0.05*CMath::RandomReal(); //--- function calls CMinLBFGS::MinLBFGSCreate(n,m,x,state); CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0); CMinLBFGS::MinLBFGSSetStpMax(state,stpmax); CMinLBFGS::MinLBFGSSetXRep(state,true); xprev=x[0]; //--- cycle while(CMinLBFGS::MinLBFGSIteration(state)) { //--- check if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0])); //--- search errors err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); } //--- check if(state.m_xupdated) { //--- search errors err=err||MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax); xprev=state.m_x[0]; } } //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100] //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set stringent stopping conditions (we try EpsG and EpsX) //--- * and we test that in the extremum stopping conditions are //--- satisfied subject to the current scaling coefficients. tmpeps=1.0E-10; m=1; for(n=1; n<=10; n++) { for(pkind=0; pkind<=2; pkind++) { //--- allocation ArrayResize(x,n); ArrayResize(xlast,n); ArrayResize(a,n); ArrayResize(s,n); ArrayResize(h,n); //--- change values for(i=0; itmpeps; //--- Test step-based stopping condition for(i=0; itmpeps; } } //--- 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 and CMLPTrain | //+------------------------------------------------------------------+ class CTestMLPTrainUnit { public: static bool TestMLPTrain(const bool silent); private: static void CreateNetwork(CMultilayerPerceptron &network,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout); static void UnsetNetwork(CMultilayerPerceptron &network); static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); static void TestGradient(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); static void TestHessian(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err); }; //+------------------------------------------------------------------+ //| Testing class CMLPBase and CMLPTrain | //+------------------------------------------------------------------+ bool CTestMLPTrainUnit::TestMLPTrain(const bool silent) { //--- create variables bool waserrors; int passcount=0; int maxn=0; int maxhid=0; int info=0; int nf=0; int nl=0; int nhid1=0; int nhid2=0; int nkind=0; int i=0; int ncount=0; bool inferrors; bool procerrors; bool graderrors; bool hesserrors; bool trnerrors; //--- objects of classes CMultilayerPerceptron network; CMultilayerPerceptron network2; CMLPReport rep; CMLPCVReport cvrep; CMatrixDouble xy; CMatrixDouble valxy; //--- initialization waserrors=false; inferrors=false; procerrors=false; graderrors=false; hesserrors=false; trnerrors=false; passcount=10; maxn=4; maxhid=4; //--- General multilayer network tests for(nf=1; nf<=maxn; nf++) { for(nl=1; nl<=maxn; nl++) { for(nhid1=0; nhid1<=maxhid; nhid1++) { for(nhid2=0; nhid2<=0; nhid2++) { for(nkind=0; nkind<=3; nkind++) { //--- Skip meaningless parameters combinations if(nkind==1 && nl<2) continue; //--- check if(nhid1==0 && nhid2!=0) continue; //--- Tests TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors); TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors); TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,graderrors); TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors); } } } } } //--- Test network training on simple XOR problem xy.Resize(4,3); xy.Set(0,0,-1); xy.Set(0,1,-1); xy.Set(0,2,-1); xy.Set(1,0,1); xy.Set(1,1,-1); xy.Set(1,2,1); xy.Set(2,0,-1); xy.Set(2,1,1); xy.Set(2,2,1); xy.Set(3,0,1); xy.Set(3,1,1); xy.Set(3,2,-1); //--- function calls CMLPBase::MLPCreate1(2,2,1,network); CMLPTrain::MLPTrainLM(network,xy,4,0.001,10,info,rep); //--- search errors trnerrors=trnerrors||CMLPBase::MLPRMSError(network,xy,4)>0.1; //--- Test CV on random noisy problem ncount=100; xy.Resize(ncount,2); //--- change values for(i=0; i<=ncount-1; i++) { xy.Set(i,0,2*CMath::RandomReal()-1); xy.Set(i,1,CMath::RandomInteger(4)); } //--- function calls CMLPBase::MLPCreateC0(1,4,network); CMLPTrain::MLPKFoldCVLM(network,xy,ncount,0.001,5,10,info,rep,cvrep); //--- Final report waserrors=(((inferrors||procerrors)||graderrors)||hesserrors)||trnerrors; //--- check if(!silent) { Print("MLP TEST"); PrintResult("INFORMATIONAL FUNCTIONS",!inferrors); PrintResult("BASIC PROCESSING",!procerrors); PrintResult("GRADIENT CALCULATION",!graderrors); PrintResult("HESSIAN CALCULATION",!hesserrors); PrintResult("TRAINING",!trnerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Network creation | //| This function creates network with desired structure. Network is | //| created using one of the three methods: | //| a) straighforward creation using MLPCreate???() | //| b) MLPCreate???() for proxy object,which is copied with | //| PassThroughSerializer() | //| c) MLPCreate???() for proxy object,which is copied with MLPCopy()| //| One of these methods is chosen with probability 1/3. | //+------------------------------------------------------------------+ void CTestMLPTrainUnit::CreateNetwork(CMultilayerPerceptron &network, const int nkind,const double a1, const double a2,const int nin, const int nhid1,const int nhid2, const int nout) { //--- create a variable int mkind=0; //--- object of class CMultilayerPerceptron tmp; //--- check if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error")) return; //--- check if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error")) return; //--- check if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error")) return; //--- change value mkind=CMath::RandomInteger(3); //--- check if(nhid1==0) { //--- No hidden layers if(nkind==0) { //--- check if(mkind==0) CMLPBase::MLPCreate0(nin,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreate0(nin,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreate0(nin,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==1) { //--- check if(mkind==0) CMLPBase::MLPCreateC0(nin,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateC0(nin,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateC0(nin,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==2) { //--- check if(mkind==0) CMLPBase::MLPCreateB0(nin,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==3) { //--- check if(mkind==0) CMLPBase::MLPCreateR0(nin,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } //--- function call CMLPBase::MLPRandomizeFull(network); return; } //--- check if(nhid2==0) { //--- One hidden layer if(nkind==0) { //--- check if(mkind==0) CMLPBase::MLPCreate1(nin,nhid1,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreate1(nin,nhid1,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreate1(nin,nhid1,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==1) { //--- check if(mkind==0) CMLPBase::MLPCreateC1(nin,nhid1,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==2) { //--- check if(mkind==0) CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==3) { //--- check if(mkind==0) CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } //--- function call CMLPBase::MLPRandomizeFull(network); return; } //--- Two hidden layers if(nkind==0) { //--- check if(mkind==0) CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==1) { //--- check if(mkind==0) CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==2) { //--- check if(mkind==0) CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { //--- check if(nkind==3) { //--- check if(mkind==0) CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network); //--- check if(mkind==1) { //--- function call CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } //--- check if(mkind==2) { CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } //--- function call CMLPBase::MLPRandomizeFull(network); } //+------------------------------------------------------------------+ //| Unsets network (initialize it to smallest network possible | //+------------------------------------------------------------------+ void CTestMLPTrainUnit::UnsetNetwork(CMultilayerPerceptron &network) { CMLPBase::MLPCreate0(1,1,network); } //+------------------------------------------------------------------+ //| Informational functions test | //+------------------------------------------------------------------+ void CTestMLPTrainUnit::TestInformational(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int n1=0; int n2=0; int wcount=0; int i=0; int j=0; int k=0; double threshold=0; int nlayers=0; int nmax=0; bool issoftmax; double mean=0; double sigma=0; int fkind=0; double c=0; double f=0; double df=0; double d2f=0; double s=0; //--- create arrays double x[]; double y[]; //--- create matrix CMatrixDouble neurons; //--- object of class CMultilayerPerceptron network; //--- initialization threshold=100000*CMath::m_machineepsilon; //--- function call CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout); //--- test MLPProperties() CMLPBase::MLPProperties(network,n1,n2,wcount); //--- search errors err=((err||n1!=nin)||n2!=nout)||wcount<=0; //--- Test network geometry functions //--- In order to do this we calculate neural network output using //--- informational functions only,and compare results with ones //--- obtained with MLPProcess(): //--- 1. we allocate 2-dimensional array of neurons and fill it by zeros //--- 2. we full first layer of neurons by input values //--- 3. we move through array,calculating values of subsequent layers //--- 4. if we have classification network,we SOFTMAX-normalize output layer //--- 5. we apply scaling to the outputs //--- 6. we compare results with ones obtained by MLPProcess() //--- NOTE: it is important to do (4) before (5),because on SOFTMAX network //--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order //--- to test it implicitly,we apply it to the classifier results //--- (already normalized). If one of the coefficients deviates from //--- expected values,we will get error during (6). nlayers=2; nmax=MathMax(nin,nout); issoftmax=nkind==1; //--- check if(nhid1!=0) { nlayers=3; nmax=MathMax(nmax,nhid1); } //--- check if(nhid2!=0) { nlayers=4; nmax=MathMax(nmax,nhid2); } //--- allocation neurons.Resize(nlayers,nmax); for(i=0; i<=nlayers-1; i++) { for(j=0; j<=nmax-1; j++) neurons.Set(i,j,0); } //--- allocation ArrayResize(x,nin); //--- change values for(i=0; ithreshold; } //+------------------------------------------------------------------+ //| Processing functions test | //+------------------------------------------------------------------+ void CTestMLPTrainUnit::TestProcessing(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int n1=0; int n2=0; int wcount=0; bool zeronet; double a1=0; double a2=0; int pass=0; int i=0; bool allsame; double v=0; int i_=0; //--- create arrays double x1[]; double x2[]; double y1[]; double y2[]; //--- objects of classes CMultilayerPerceptron network; CMultilayerPerceptron network2; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) return; //--- Prepare network a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- function calls CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); //--- Initialize arrays ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y1,nout); ArrayResize(y2,nout); //--- Main cycle for(pass=1; pass<=passcount; pass++) { //--- Last run is made on zero network CMLPBase::MLPRandomizeFull(network); zeronet=false; //--- check if(pass==passcount) { for(i_=0; i_1000*CMath::m_machineepsilon; } //--- check if(nkind==2) { //--- B-type network outputs are bounded from above/below for(i=0; i=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; iMathMax(a1,a2); } } } //+------------------------------------------------------------------+ //| Gradient functions test | //+------------------------------------------------------------------+ void CTestMLPTrainUnit::TestGradient(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int n1=0; int n2=0; int wcount=0; double h=0; double etol=0; double a1=0; double a2=0; int pass=0; int i=0; int j=0; int ssize=0; double v=0; double e=0; double e1=0; double e2=0; double v1=0; double v2=0; double v3=0; double v4=0; double wprev=0; int i_=0; int i1_=0; //--- create arrays double grad1[]; double grad2[]; double x[]; double y[]; double x1[]; double x2[]; double y1[]; double y2[]; //--- create matrix CMatrixDouble xy; //--- object of class CMultilayerPerceptron network; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) return; //--- change values a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- function calls CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); //--- change values h=0.0001; etol=0.01; //--- Initialize ArrayResize(x,nin); ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y,nout); ArrayResize(y1,nout); ArrayResize(y2,nout); ArrayResize(grad1,wcount); ArrayResize(grad2,wcount); //--- Process for(pass=1; pass<=passcount; pass++) { //--- function call CMLPBase::MLPRandomizeFull(network); //--- Test error/gradient calculation (least squares) xy.Resize(1,nin+nout); for(i=0; ietol; err=err||MathAbs((CMLPBase::MLPError(network,xy,1)-v)/v)>etol; for(i=0; i1.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; ietol; err=err||MathAbs((CMLPBase::MLPErrorN(network,xy,1)-v)/v)>etol; for(i=0; i1.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; i0.01; for(i=0; ietol; 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; ietol; for(i=0; ietol; else err=err||grad2[i]!=grad1[i]; } } } //+------------------------------------------------------------------+ //| Hessian functions test | //+------------------------------------------------------------------+ void CTestMLPTrainUnit::TestHessian(const int nkind,const int nin, const int nhid1,const int nhid2, const int nout,const int passcount, bool &err) { //--- create variables int hkind=0; int n1=0; int n2=0; int wcount=0; double h=0; double etol=0; int pass=0; int i=0; int j=0; int ssize=0; double a1=0; double a2=0; double v=0; double e1=0; double e2=0; double wprev=0; int i_=0; int i1_=0; //--- create arrays double grad1[]; double grad2[]; double grad3[]; double x[]; double y[]; double x1[]; double x2[]; double y1[]; double y2[]; //--- create matrix CMatrixDouble xy; CMatrixDouble h1; CMatrixDouble h2; //--- object of class CMultilayerPerceptron network; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) return; //--- change values a1=0; a2=0; //--- check if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } //--- check if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } //--- function calls CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); //--- change values h=0.0001; etol=0.05; //--- Initialize ArrayResize(x,nin); ArrayResize(x1,nin); ArrayResize(x2,nin); ArrayResize(y,nout); ArrayResize(y1,nout); ArrayResize(y2,nout); ArrayResize(grad1,wcount); ArrayResize(grad2,wcount); ArrayResize(grad3,wcount); h1.Resize(wcount,wcount); h2.Resize(wcount,wcount); //--- Process for(pass=1; pass<=passcount; pass++) { //--- function call CMLPBase::MLPRandomizeFull(network); //--- Test hessian calculation . //--- E1 contains total error (calculated using MLPGrad/MLPGradN) //--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN) //--- H1 contains Hessian calculated using differences of gradients //--- E2,Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch for(hkind=0; hkind<=1; hkind++) { ssize=1+CMath::RandomInteger(10); xy.Resize(ssize,nin+nout); for(i=0; ietol; for(i=0; i1.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; i5.0E-2) err=err||MathAbs((h1.Get(i,j)-h2.Get(i,j))/h1.Get(i,j))>etol; else err=err||MathAbs(h2.Get(i,j)-h1.Get(i,j))>etol; } } } } } //+------------------------------------------------------------------+ //| Testing class CMLPE | //+------------------------------------------------------------------+ class CTestMLPEUnit { public: static bool TestMLPE(const bool silent); private: static bool TestMLPTrainES(void); static bool TestMLPTrainRegr(void); static bool TestMLPXORRegr(void); static bool TestMLPTrainClass(void); static bool TestMLPXORCls(void); static bool TestMLPZeroWeights(void); static bool TestMLPRestarts(void); static bool TestMLPTrainEns(void); static bool TestMLPTrainEnsRegr(void); static bool TestMLPTrainEnsCls(void); }; //+------------------------------------------------------------------+ //| Testing class CMLPE | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPE(const bool silent) { //--- create variables bool waserrors=false; int info=0; CMultilayerPerceptronShell network; CMultilayerPerceptronShell network2; CMLPReportShell rep; CMLPCVReportShell cvrep; CMatrixDouble xy; CMatrixDouble valxy; bool trnerrors=false; bool mlptrainregrerr=false; bool mlptrainclasserr=false; bool mlprestartserr=false; bool mlpxorregrerr=false; bool mlpxorclserr=false; bool mlptrainenserrors=false; //--- Test network training on simple XOR problem xy.Resize(4,3); xy.Set(0,0,-1); xy.Set(0,1,-1); xy.Set(0,2,-1); xy.Set(1,0,1); xy.Set(1,1,-1); xy.Set(1,2,1); xy.Set(2,0,-1); xy.Set(2,1,1); xy.Set(2,2,1); xy.Set(3,0,1); xy.Set(3,1,1); xy.Set(3,2,-1); CMLPBase::MLPCreate1(2,2,1,network.GetInnerObj()); CMLPTrain::MLPTrainLM(network.GetInnerObj(),xy,4,0.0001,20,info,rep.GetInnerObj()); trnerrors=trnerrors||CMLPBase::MLPRMSError(network.GetInnerObj(),xy,4)>0.1; //--- Test early stopping trnerrors=trnerrors||TestMLPTrainES(); //--- Test for training functions mlptrainregrerr=TestMLPTrainRegr()||TestMLPZeroWeights(); mlptrainclasserr=TestMLPTrainClass(); mlprestartserr=TestMLPRestarts(); mlpxorregrerr=TestMLPXORRegr(); mlpxorclserr=TestMLPXORCls(); //--- Training for ensembles mlptrainenserrors=(TestMLPTrainEns()||TestMLPTrainEnsRegr()||TestMLPTrainEnsCls()); //--- Final report waserrors=(trnerrors||mlptrainregrerr||mlptrainclasserr||mlprestartserr||mlpxorregrerr||mlpxorclserr||mlptrainenserrors); if(!silent) { Print("MLPE TEST"); PrintResult("TRAINING",!trnerrors); PrintResult("TRAIN -LM -LBFGS FOR REGRESSION",!mlptrainregrerr); PrintResult("TRAIN -LM -LBFGS FOR CLASSIFIER",!mlptrainclasserr); PrintResult("PARAMETER RESTARTS IN TRAIN -LBFGS",!mlprestartserr); PrintResult("TRAINIG WITH TRAINER FOR REGRESSION",!mlpxorregrerr); PrintResult("TRAINIG WITH TRAINER FOR CLASSIFIER",!mlpxorclserr); PrintResult("TRAINING ENSEMBLES",!mlptrainenserrors); PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| This function tests MLPTrainES(). | //| It returns True in case of errors, False when no errors were | //| detected | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPTrainES(void) { //--- create variables int pass=0; int passcount=0; CMultilayerPerceptron network; CMatrixDouble trnxy; CMatrixDouble valxy; CRowDouble x; CRowDouble y; int n=0; int i=0; int j=0; int nrestarts=0; int info=0; CMLPReportShell rep; //--- First test checks that MLPTrainES() - when training set is equal to the validation //--- set, MLPTrainES() behaves just like a "normal" training algorithm. //--- Test sequence: //--- * generate training set - 100 random points from 2D square [-1,+1]*[-1,+1] //--- * generate network with 2 inputs, no hidden layers, nonlinear output layer, //--- use its outputs as target values for the test set //--- * randomize network //--- * train with MLPTrainES(), using original set as both training and validation set //--- * trained network must reproduce training set with good precision //--- NOTE: it is important to test algorithm on nonlinear network because linear //--- problems converge too fast. Slow convergence is important to detect //--- some kinds of bugs. //--- NOTE: it is important to have NRestarts at least equal to 5, because with just //--- one restart algorithm fails test about once in several thousands of passes. passcount=10; nrestarts=5; for(pass=1; pass<=passcount; pass++) { //--- Create network, generate training/validation sets CMLPBase::MLPCreateR0(2,1,-2.0,2.0,network); CMLPBase::MLPRandomize(network); n=100; trnxy.Resize(n,3); valxy.Resize(n,3); x.Resize(2); y.Resize(1); for(i=0; i0.01) return(true); } return(false); } //+------------------------------------------------------------------+ //| This function tests MLPTrainLM, MLPTrainLBFGS and | //| MLPTrainNetwork functions for regression. It check that train | //| functions work correctly. | //| Test use Create1 with 10 neurons. | //| Test function is f(x,y)=X^2+cos(3*Pi*y). | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPTrainRegr(void) { //--- create variables CMultilayerPerceptronShell net; CMLPTrainer trainer; CMLPReport rep; int info=0; CMatrixDouble xy; CSparseMatrix sm; CRowDouble x; CRowDouble y; int n=0; int sn=0; int nneurons=0; double vdecay=0; double averr=0; double st=0; double eps=0; double traineps=0; int nneedrest=0; int trainits=0; int shift=0; int i=0; int j=0; int vtrain=0; //--- initialization eps=0.02; vdecay=0.001; nneurons=10; nneedrest=5; traineps=1.0E-3; trainits=0; sn=5; n=sn*sn; st=(double)2/(double)(sn-1); CSparse::SparseCreate(n,3,n*3,sm); xy.Resize(n,3); x.Resize(2); for(vtrain=0; vtrain<=3; vtrain++) { averr=0; //--- Create a train set(uniformly distributed set of points). for(i=0; ieps) return(true); } //--- return result return(false); } //+------------------------------------------------------------------+ //| This function tests MLPTrainNetwork / MLPStartTraining / | //| MLPContinueTraining functions for regression. It check that train| //| functions work correctly. | //| Test use Create1 with 2 neurons. | //| Test function is XOR(x,y). | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPXORRegr(void) { //--- create variables CMultilayerPerceptronShell net; CMLPTrainer trainer; CMLPReportShell rep; CMatrixDouble xy; CSparseMatrix sm; CRowDouble x; CRowDouble y; int n=0; int sn=0; int nneurons=0; double vdecay=0; double averr=0; double eps=0; int numxp=0; double traineps=0; int nneedrest=0; int trainits=0; int shift=0; int i=0; int j=0; int vtrain=0; int xp=0; int nfailures=0; eps=0.01; numxp=15; vdecay=0.0001; nneurons=4; nneedrest=1; traineps=1.0E-4; trainits=0; sn=2; n=sn*sn; CSparse::SparseCreate(n,3,n*3,sm); xy.Resize(n,3); x.Resize(2); nfailures=0; for(xp=1; xp<=numxp; xp++) { for(vtrain=0; vtrain<=3; vtrain++) { //--- Create a train set(uniformly distributed set of points). for(i=0; ieps) nfailures++; } } if(nfailures>=2) { Print("testmlptrainunit.ap:580"); return(true); } //--- return result return(false); } //+------------------------------------------------------------------+ //| This function tests MLPTrainLM, MLPTrainLBFGS and | //| MLPTrainNetwork functions for classification problems. It check | //| that train functions work correctly when is used CreateC1 | //| function. Here the network tries to distinguish positive from| //| negative numbers. | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPTrainClass(void) { //--- create variables CMultilayerPerceptron net; CMLPTrainer trainer; CMLPReportShell rep; int info=0; CMatrixDouble xy; CSparseMatrix sm; CRowDouble x; CRowDouble y; int n=0; double vdecay=0; double traineps=0; int nneedrest=0; int trainits=0; double tmp=0; double mnc=0; double mxc=0; int nxp=0; int i=0; int rndind=0; int vtrain=0; int xp=0; //--- initialization mnc=10; mxc=11; nxp=15; vdecay=0.0001; nneedrest=10; traineps=1.0E-4; trainits=0; n=100; CSparse::SparseCreate(n,2,n*2,sm); x.Resize(1); xy.Resize(n,2); for(xp=1; xp<=nxp; xp++) { for(vtrain=0; vtrain<=3; vtrain++) { //--- Initialization: //--- * create negative part of the set; for(i=0; i<=n/2-1; i++) { xy.Set(i,0,-(1*((mxc-mnc)*CMath::RandomReal()+mnc))); xy.Set(i,1,0); } //--- * create positive part of the set; for(i=n/2; i0.05) || (x[0]>=0.0 && y[0]>0.05 && y[1]<0.95)) { Print("testmlptrainunit.ap:730"); return(true); } } //--- Test on random set for(i=0; i0.05) || (x[0]>=0.0 && y[0]>0.05 && y[1]<0.95)) { Print("testmlptrainunit.ap:745"); return(true); } } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| This function tests MLPTrainNetwork / MLPStartTraining / | //| MLPContinueTraining functions for classification problems. It | //| check that train functions work correctly when is used CreateC1 | //| function. Here the network tries to distinguish positive from | //| negative numbers. | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPXORCls(void) { //--- create variables CMultilayerPerceptron net; CMLPTrainer trainer; CMLPReportShell rep; CMatrixDouble xy; CSparseMatrix sm; CRowDouble x; CRowDouble y; int n=0; int nin=0; int nout=0; int wcount=0; double e=0; double ebest=0; double v=0; CRowDouble wbest; double vdecay=0; double traineps=0; int nneurons=0; int nneedrest=0; int trainits=0; int nxp=0; int i=0; int vtrain=0; int xp=0; int i_=0; //--- initialization nxp=15; nneurons=4; vdecay=0.001; nneedrest=3; traineps=1.0E-4; trainits=0; n=4; CSparse::SparseCreate(n,3,n*3,sm); x.Resize(2); xy.Resize(n,3); //--- Initialization: xy.Set(0,0,0); xy.Set(0,1,0); xy.Set(0,2,0); xy.Set(1,0,0); xy.Set(1,1,1); xy.Set(1,2,1); xy.Set(2,0,1); xy.Set(2,1,0); xy.Set(2,2,1); xy.Set(3,0,1); xy.Set(3,1,1); xy.Set(3,2,0); //--- Create a neural network CMLPBase::MLPCreateC1(2,nneurons,2,net); CMLPBase::MLPProperties(net,nin,nout,wcount); wbest.Resize(wcount); //--- Test for(xp=1; xp<=nxp; xp++) { for(vtrain=0; vtrain<=3; vtrain++) { //--- Train with trainer, using: //--- * dense matrix; switch(vtrain) { case 0: CMLPTrain::MLPCreateTrainerCls(2,2,trainer); CMLPTrain::MLPSetDataset(trainer,xy,n); CMLPTrain::MLPSetDecay(trainer,vdecay); CMLPTrain::MLPSetCond(trainer,traineps,trainits); CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj()); break; case 1: CMLPTrain::MLPCreateTrainerCls(2,2,trainer); CMLPTrain::MLPSetDataset(trainer,xy,n); CMLPTrain::MLPSetDecay(trainer,vdecay); CMLPTrain::MLPSetCond(trainer,traineps,trainits); ebest=CMath::m_maxrealnumber; for(i=1; i<=nneedrest; i++) { CMLPTrain::MLPStartTraining(trainer,net,true); while(CMLPTrain::MLPContinueTraining(trainer,net)) { } v=CAblasF::RDotV2(wcount,net.m_weights); e=CMLPBase::MLPError(net,xy,n)+0.5*vdecay*v; //--- Compare with the best answer. if(e0.05) || (x[0]!=x[1] && y[0]>0.05 && y[1]<0.95)) return(true); } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| The test check, that all weights are zero after training with| //| trainer using empty dataset(either zero size or is't used | //| MLPSetDataSet function). Test on regression and classification | //| problems given by dense or sparse matrix. | //| NOTE: Result of the function is written in MLPTrainRegrErr | //| variable in unit test. | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPZeroWeights(void) { //--- create variables CMLPTrainer trainer; CMultilayerPerceptron net; CMLPReportShell rep; int nin=0; int nout=0; int wcount=0; int mxnin=0; int mxnout=0; double vdecay=0; double traineps=0; int trainits=0; int nneedrest=0; CMatrixDouble dds; CSparseMatrix sds; bool iscls; bool issparse; int c=0; int n=0; int xp=0; int nxp=0; //--- initialization mxnin=10; mxnout=10; vdecay=1.0E-3; nneedrest=1; traineps=1.0E-3; trainits=0; CSparse::SparseCreate(1,1,0,sds); CSparse::SparseConvertToCRS(sds); nxp=10; for(xp=1; xp<=nxp; xp++) { c=CMath::RandomInteger(2); iscls=c==1; c=CMath::RandomInteger(2); issparse=c==1; //--- Create trainer and network if(!iscls) { //--- Regression nin=CMath::RandomInteger(mxnin)+1; nout=CMath::RandomInteger(mxnout)+1; CMLPTrain::MLPCreateTrainer(nin,nout,trainer); CMLPBase::MLPCreate0(nin,nout,net); } else { //--- Classification nin=CMath::RandomInteger(mxnin)+1; nout=CMath::RandomInteger(mxnout)+2; CMLPTrain::MLPCreateTrainerCls(nin,nout,trainer); CMLPBase::MLPCreateC0(nin,nout,net); } n=CMath::RandomInteger(2)-1; if(n==0) { if(!issparse) CMLPTrain::MLPSetDataset(trainer,dds,n); else CMLPTrain::MLPSetSparseDataset(trainer,sds,n); } CMLPTrain::MLPSetDecay(trainer,vdecay); CMLPTrain::MLPSetCond(trainer,traineps,trainits); c=CMath::RandomInteger(2); if(c==0) { CMLPTrain::MLPStartTraining(trainer,net,true); while(CMLPTrain::MLPContinueTraining(trainer,net)) { } } if(c==1) { CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj()); } //--- Check weights CMLPBase::MLPProperties(net,nin,nout,wcount); for(c=0; c1 restarts. CMLPBase::MLPProperties(net1,nin,nout,wcount1); e1=CAblasF::RDotV2(wcount1,net1.m_weights); e1=CMLPBase::MLPErrorN(net1,xy,n)+0.5*vdecay*e1; if(e0<=e1) avval++; } if((double)(mean-numsigma)<(double)(avval)) return(true); } //--- return result return(false); } //+------------------------------------------------------------------+ //| The function tests functions for training ensembles : | //| MLPEBaggingLM, MLPEBaggingLBFGS. | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPTrainEns(void) { //--- create variables CMLPEnsembleShell ensemble; CMLPReportShell rep; CMLPCVReportShell oobrep; int info=0; CMatrixDouble xy; int nin=0; int nout=0; int npoints=0; CMatrixDouble hf; int nhid=0; int algtype=0; int tasktype=0; int pass=0; double e=0; int nless=0; int nall=0; int nclasses=0; int i=0; int j=0; int k=0; CHighQualityRandStateShell rs; CHighQualityRand::HQRndRandomize(rs.GetInnerObj()); //--- network training must reduce error //--- test on random regression task nin=3; nout=2; nhid=5; npoints=100; nless=0; nall=0; for(pass=1; pass<=10; pass++) { for(algtype=0; algtype<=1; algtype++) { for(tasktype=0; tasktype<=1; tasktype++) { if(tasktype==0) { hf.Resize(nout,nin+1); for(i=0; ixy.Get(i,k)) k=j; } if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0) xy.Set(i,nin,k); else xy.Set(i,nin,CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),nclasses)); } CMLPE::MLPECreateC1(nin,nhid,nclasses,1+CMath::RandomInteger(3),ensemble.GetInnerObj()); } e=CMLPE::MLPERMSError(ensemble.GetInnerObj(),xy,npoints); if(algtype==0) CMLPTrain::MLPEBaggingLM(ensemble.GetInnerObj(),xy,npoints,0.001,1,info,rep.GetInnerObj(),oobrep.GetInnerObj()); else CMLPTrain::MLPEBaggingLBFGS(ensemble.GetInnerObj(),xy,npoints,0.001,1,0.01,0,info,rep.GetInnerObj(),oobrep.GetInnerObj()); if(info<0) return(true); if(CMLPE::MLPERMSError(ensemble.GetInnerObj(),xy,npoints)(double)(0.3*nall)) { Print("testmlptrainunit.ap:1636"); return(true); } //--- return result return(false); } //+------------------------------------------------------------------+ //| Testing for functions MLPETrainES and MLPTrainEnsembleES on | //| regression problems. Returns TRUE for errors, FALSE for success. | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPTrainEnsRegr(void) { //--- return result CMLPTrainer trainer; CMLPEnsembleShell netens; CMLPReportShell rep; CModelErrors repx; int info=0; CSparseMatrix xytrainsp; CMatrixDouble xytrain; CMatrixDouble xytest; int nin=0; int nout=0; int nneurons=0; CRowDouble x; CRowDouble y; double decay=0; double wstep=0; int maxits=0; int nneedrest=0; int enssize=0; double mnval=0; double mxval=0; int ntrain=0; int ntest=0; double avgerr=0; int issparse=0; int withtrainer=0; double eps=0; int xp=0; int i=0; int j=0; int i_=0; //--- This test checks ability to train ensemble on simple regression //--- problem "f(x0,x1,x2,...) = x0 + x1 + x2 + ...". eps=5.0E-2; mnval=-1; mxval=1; ntrain=40; ntest=20; decay=1.0E-3; wstep=1.0E-3; maxits=20; nneedrest=1; nneurons=20; nout=1; enssize=100; for(xp=1; xp<=2; xp++) { nin=CMath::RandomInteger(3)+1; CApServ::RVectorSetLengthAtLeast(x,nin); CMLPTrain::MLPCreateTrainer(nin,nout,trainer); CMLPTrain::MLPSetDecay(trainer,decay); CMLPTrain::MLPSetCond(trainer,wstep,maxits); CApServ::RMatrixSetLengthAtLeast(xytrain,ntrain,nin+nout); CApServ::RMatrixSetLengthAtLeast(xytest,ntest,nin+nout); withtrainer=CMath::RandomInteger(2); issparse=0; if(withtrainer==0) issparse=0; if(withtrainer==1) issparse=CMath::RandomInteger(2); //--- Training set for(i=0; ieps) { Print("testmlptrainunit.ap:1817"); return(true); } avgerr=0; for(i=0; i(double)(2*eps)) { Print("testmlptrainunit.ap:1826"); return(true); } } //--- Catch bug in implementation of MLPTrainEnsembleX: //--- test ensemble training on empty dataset. //--- Unfixed version should crash with violation of array //--- bounds (at least in C#). nin=2; nout=2; nneurons=3; enssize=3; nneedrest=2; wstep=0.001; maxits=2; CMLPTrain::MLPCreateTrainer(nin,nout,trainer); CMLPTrain::MLPSetCond(trainer,wstep,maxits); CMLPE::MLPECreate1(nin,nneurons,nout,enssize,netens.GetInnerObj()); CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj()); return(false); } //+------------------------------------------------------------------+ //| Testing for functions MLPETrainES and MLPTrainEnsembleES on | //| classification problems. | //+------------------------------------------------------------------+ bool CTestMLPEUnit::TestMLPTrainEnsCls(void) { //--- create variables CMLPTrainer trainer; CMLPEnsembleShell netens; CMLPReportShell rep; int info=0; CSparseMatrix xytrainsp; CMatrixDouble xytrain; CMatrixDouble xytest; int nin=0; int nout=0; CRowDouble x; CRowDouble y; double decay=0; double wstep=0; int maxits=0; int nneedrest=0; int enssize=0; int val=0; int ntrain=0; int ntest=0; double avgerr=0; double eps=0; double delta=0; int issparse=0; int withtrainer=0; int xp=0; int nxp=0; int i=0; int j=0; int i_=0; eps=5.0E-2; delta=0.1; ntrain=90; ntest=90; nin=3; nout=3; CApServ::RVectorSetLengthAtLeast(x,nin); CApServ::RMatrixSetLengthAtLeast(xytrain,ntrain,nin+1); CApServ::RMatrixSetLengthAtLeast(xytest,ntest,nin+1); decay=1.0E-3; wstep=1.0E-3; maxits=100; nneedrest=1; CMLPTrain::MLPCreateTrainerCls(nin,nout,trainer); CMLPTrain::MLPSetDecay(trainer,decay); CMLPTrain::MLPSetCond(trainer,wstep,maxits); nxp=5; for(xp=1; xp<=nxp; xp++) { enssize=(int)MathRound(MathPow(10,CMath::RandomInteger(2)+1)); withtrainer=CMath::RandomInteger(2); issparse=0; if(withtrainer==0) issparse=0; if(withtrainer==1) issparse=CMath::RandomInteger(2); for(i=0; ieps) return(true); //--- * on test dataset. avgerr=0; for(i=0; ieps) return(true); } //--- return result return(false); } //+------------------------------------------------------------------+ //| Testing class CPCAnalysis | //+------------------------------------------------------------------+ class CTestPCAUnit { public: static bool TestPCA(const bool silent); private: static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs); }; //+------------------------------------------------------------------+ //| Testing class CPCAnalysis | //+------------------------------------------------------------------+ bool CTestPCAUnit::TestPCA(const bool silent) { //--- create variables int passcount=0; int maxn=0; int maxm=0; double threshold=0; int m=0; int n=0; int i=0; int j=0; int k=0; int info=0; double t=0; double h=0; double tmean=0; double tmeans=0; double tstddev=0; double tstddevs=0; double tmean2=0; double tmeans2=0; double tstddev2=0; double tstddevs2=0; bool pcaconverrors; bool pcaorterrors; bool pcavarerrors; bool pcaopterrors; bool waserrors; int i_=0; //--- create arrays double means[]; double s[]; double t2[]; double t3[]; //--- create matrix CMatrixDouble v; CMatrixDouble x; //--- Primary settings maxm=10; maxn=100; passcount=1; threshold=1000*CMath::m_machineepsilon; waserrors=false; pcaconverrors=false; pcaorterrors=false; pcavarerrors=false; pcaopterrors=false; //--- Test 1: N random points in M-dimensional space for(m=1; m<=maxm; m++) { for(n=1; n<=maxn; n++) { //--- Generate task x.Resize(n,m); ArrayResize(means,m); //--- change values for(j=0; jthreshold; } } //--- Variance test ArrayResize(t2,n); for(k=0; kthreshold; } //--- 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; ithreshold; } } } //--- Final report waserrors=((pcaconverrors||pcaorterrors)||pcavarerrors)||pcaopterrors; //--- check if(!silent) { Print("PCA TEST"); PrintResult("TOTAL RESULTS",!waserrors); PrintResult("* CONVERGENCE",!pcaconverrors); PrintResult("* ORTOGONALITY",!pcaorterrors); PrintResult("* VARIANCE REPORT",!pcavarerrors); PrintResult("* OPTIMALITY",!pcaopterrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Moments estimates and their errors | //+------------------------------------------------------------------+ void CTestPCAUnit::CalculateMV(double &x[],const int n,double &mean, double &means,double &stddev,double &stddevs) { //--- create variables int i=0; double v1=0; double v2=0; double variance=0; //--- initialization mean=0; means=1; stddev=0; stddevs=1; variance=0; //--- check if(n<=1) return; //--- Mean for(i=0; i=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; i0.5) { for(i_=0; i_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; iMathAbs(eps); rkckerrors=rkckerrors||mynfev!=rep.m_nfev; } } //--- end waserrors=rkckerrors; //--- check if(!silent) { Print("TESTING ODE SOLVER"); PrintResult("* RK CASH-KARP",!rkckerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Unsets real matrix | //+------------------------------------------------------------------+ void CTestODESolverUnit::Unset2D(CMatrixDouble &x) { //--- allocation x.Resize(1,1); //--- change value x.Set(0,0,2*CMath::RandomReal()-1); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ void CTestODESolverUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Unsets report | //+------------------------------------------------------------------+ void CTestODESolverUnit::UnsetRep(CODESolverReport &rep) { rep.m_nfev=0; } //+------------------------------------------------------------------+ //| Testing class CFastFourierTransform | //+------------------------------------------------------------------+ class CTestFFTUnit { public: static bool TestFFT(const bool silent); private: static void RefFFTC1D(complex &a[],const int n); static void RefFFTC1DInv(complex &a[],const int n); static void RefInternalCFFT(double &a[],const int nn,const bool inversefft); static void RefInternalRFFT(double &a[],const int nn,complex &f[]); }; //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ bool CTestFFTUnit::TestFFT(const bool silent) { //--- create variables int n=0; int i=0; int k=0; int maxn=0; double bidierr=0; double bidirerr=0; double referr=0; double refrerr=0; double reinterr=0; double errtol=0; bool referrors; bool bidierrors; bool refrerrors; bool bidirerrors; bool reinterrors; bool waserrors; int i_=0; //--- create arrays complex a1[]; complex a2[]; complex a3[]; double r1[]; double r2[]; double buf[]; //--- object of class CFtPlan plan; //--- initialization maxn=128; errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon; bidierrors=false; referrors=false; bidirerrors=false; refrerrors=false; reinterrors=false; waserrors=false; //--- Test bi-directional error: norm(x-invFFT(FFT(x))) bidierr=0; bidirerr=0; for(n=1; n<=maxn; n++) { //--- Complex FFT/invFFT ArrayResize(a1,n); ArrayResize(a2,n); ArrayResize(a3,n); for(i=0; ierrtol; 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; ierrtol; 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; ierrtol; //--- end waserrors=(((bidierrors||bidirerrors)||referrors)||refrerrors)||reinterrors; //--- check if(!silent) { Print("TESTING FFT"); PrintResult("FINAL RESULT",!waserrors); PrintResult("* BI-DIRECTIONAL COMPLEX TEST",!bidierrors); PrintResult("* AGAINST REFERENCE COMPLEX FFT",!referrors); PrintResult("* BI-DIRECTIONAL REAL TEST",!bidirerrors); PrintResult("* AGAINST REFERENCE REAL FFT",!refrerrors); PrintResult("* INTERNAL EVEN FFT",!reinterrors); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference FFT | //+------------------------------------------------------------------+ void CTestFFTUnit::RefFFTC1D(complex &a[],const int n) { //--- create variables int i=0; //--- create array double buf[]; //--- check if(!CAp::Assert(n>0,"FFTC1D: incorrect N!")) return; //--- allocation ArrayResize(buf,2*n); //--- copy for(i=0; i0,"FFTC1DInv: incorrect N!")) return; //--- allocation ArrayResize(buf,2*n); //--- copy for(i=0; i-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=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; ierrtol; 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; ierrtol; invrerrors=invrerrors||invrerr>errtol; //--- end waserrors=((referrors||refrerrors)||inverrors)||invrerrors; //--- check if(!silent) { Print("TESTING CONVOLUTION"); PrintResult("FINAL RESULT",!waserrors); PrintResult("* AGAINST REFERENCE COMPLEX CONV",!referrors); PrintResult("* AGAINST REFERENCE REAL CONV",!refrerrors); PrintResult("* COMPLEX INVERSE",!inverrors); PrintResult("* REAL INVERSE",!invrerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ void CTestConvUnit::RefConvC1D(complex &a[],const int m,complex &b[], const int n,complex &r[]) { //--- create variables int i=0; complex v=0; int i_=0; int i1_=0; //--- allocation ArrayResize(r,m+n-1); //--- initialization for(i=0; i<=m+n-2; i++) r[i]=0; //--- calculation for(i=0; ierrtol; refrerrors=refrerrors||refrerr>errtol; //--- end waserrors=referrors||refrerrors; //--- check if(!silent) { Print("TESTING CORRELATION"); PrintResult("FINAL RESULT",!waserrors); PrintResult("* AGAINST REFERENCE COMPLEX CORR",!referrors); PrintResult("* AGAINST REFERENCE REAL CORR",!refrerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference implementation | //+------------------------------------------------------------------+ void CTestCorrUnit::RefCorrC1D(complex &signal[],const int n, complex &pattern[],const int m, complex &r[]) { //--- create variables int i=0; int j=0; complex v=0; int i_=0; //--- create array complex s[]; //--- allocation ArrayResize(s,m+n-1); //--- change values for(i_=0; i_=n) break; v+=CMath::Conj(pattern[j])*s[i+j]; } r[i]=v; } //--- calculation for(i=1; i=n) break; v+=pattern[j]*s[i+j]; } r[i]=v; } //--- calculation for(i=1; ierrtol; //--- 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; ierrtol; //--- end waserrors=bidierrors||referrors; //--- check if(!silent) { Print("TESTING FHT"); PrintResult("FINAL RESULT",!waserrors); PrintResult("* BI-DIRECTIONAL TEST",!bidierrors); PrintResult("* AGAINST REFERENCE FHT",!referrors); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Reference FHT | //+------------------------------------------------------------------+ void CTestFHTUnit::RefFHTR1D(double &a[],const int n) { //--- create variables int i=0; int j=0; double v=0; //--- create array double buf[]; //--- check if(!CAp::Assert(n>0,"RefFHTR1D: incorrect N!")) return; //--- allocation ArrayResize(buf,n); //--- calculation for(i=0; i0,"RefFHTR1DInv: incorrect N!")) return; //--- function call RefFHTR1D(a,n); //--- change values for(int i=0; i0) { //--- 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; i1) 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=x[i+1]; } else recerrors=true; n=n*2; } //--- search errors recerrors=recerrors||err>errtol; //--- Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes: //--- 1. Gauss-Lobatto with N=3 //--- 2. Gauss-Lobatto with N=4 //--- 3. Gauss-Lobatto with N=6 err=0; ArrayResize(alpha,2); ArrayResize(beta,2); alpha[0]=0; alpha[1]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); //--- function call CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,3,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1])); err=MathMax(err,MathAbs(x[2]-1)); err=MathMax(err,MathAbs(w[0]-1.0/3.0)); err=MathMax(err,MathAbs(w[1]-4.0/3.0)); err=MathMax(err,MathAbs(w[2]-1.0/3.0)); for(i=0; i<=1; i++) recerrors=recerrors||x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,3); ArrayResize(beta,3); //--- change values alpha[0]=0; alpha[1]=0; alpha[2]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); beta[2]=(double)(2*2)/(double)(4*2*2-1); //--- function call CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,4,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]+MathSqrt(5)/5)); err=MathMax(err,MathAbs(x[2]-MathSqrt(5)/5)); err=MathMax(err,MathAbs(x[3]-1)); err=MathMax(err,MathAbs(w[0]-1.0/6.0)); err=MathMax(err,MathAbs(w[1]-5.0/6.0)); err=MathMax(err,MathAbs(w[2]-5.0/6.0)); err=MathMax(err,MathAbs(w[3]-1.0/6.0)); for(i=0; i<=2; i++) recerrors=recerrors||x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,5); ArrayResize(beta,5); //--- change values alpha[0]=0; alpha[1]=0; alpha[2]=0; alpha[3]=0; alpha[4]=0; beta[0]=0; beta[1]=(double)(1*1)/(double)(4*1*1-1); beta[2]=(double)(2*2)/(double)(4*2*2-1); beta[3]=(double)(3*3)/(double)(4*3*3-1); beta[4]=(double)(4*4)/(double)(4*4*4-1); //--- function call CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,6,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]+MathSqrt((7+2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[2]+MathSqrt((7-2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[3]-MathSqrt((7-2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[4]-MathSqrt((7+2*MathSqrt(7))/21))); err=MathMax(err,MathAbs(x[5]-1)); err=MathMax(err,MathAbs(w[0]-1.0/(double)15)); err=MathMax(err,MathAbs(w[1]-(14-MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[2]-(14+MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[3]-(14+MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[4]-(14-MathSqrt(7))/30)); err=MathMax(err,MathAbs(w[5]-1.0/(double)15)); for(i=0; i<=4; i++) recerrors=recerrors||x[i]>=x[i+1]; } else recerrors=true; recerrors=recerrors||err>errtol; //--- Three tests for rec-based Gauss-Radau quadratures with known weights/nodes: //--- 1. Gauss-Radau with N=2 //--- 2. Gauss-Radau with N=3 //--- 3. Gauss-Radau with N=3 (another case) err=0; ArrayResize(alpha,1); ArrayResize(beta,2); alpha[0]=0; beta[0]=0; beta[1]=1.0/3.0; //--- function call CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,2,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]-1.0/3.0)); err=MathMax(err,MathAbs(w[0]-0.5)); err=MathMax(err,MathAbs(w[1]-1.5)); for(i=0; i<=0; i++) recerrors=recerrors||x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,2); ArrayResize(beta,3); //--- change values alpha[0]=0; alpha[1]=0; for(i=0; i<=2; i++) beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1); //--- function call CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,3,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[0]+1)); err=MathMax(err,MathAbs(x[1]-(1-MathSqrt(6))/5)); err=MathMax(err,MathAbs(x[2]-(1+MathSqrt(6))/5)); err=MathMax(err,MathAbs(w[0]-2.0/9.0)); err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18)); err=MathMax(err,MathAbs(w[2]-(16-MathSqrt(6))/18)); for(i=0; i<=1; i++) recerrors=recerrors||x[i]>=x[i+1]; } else recerrors=true; //--- allocation ArrayResize(alpha,2); ArrayResize(beta,3); alpha[0]=0; alpha[1]=0; for(i=0; i<=2; i++) beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1); //--- function call CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,1,3,info,x,w); //--- check if(info>0) { //--- search errors err=MathMax(err,MathAbs(x[2]-1)); err=MathMax(err,MathAbs(x[1]+(1-MathSqrt(6))/5)); err=MathMax(err,MathAbs(x[0]+(1+MathSqrt(6))/5)); err=MathMax(err,MathAbs(w[2]-2.0/9.0)); err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18)); err=MathMax(err,MathAbs(w[0]-(16-MathSqrt(6))/18)); for(i=0; i<=1; i++) recerrors=recerrors||x[i]>=x[i+1]; } else recerrors=true; //--- search errors recerrors=recerrors||err>errtol; //--- test recurrence-based special cases (Legendre,Jacobi,Hermite,...) //--- against another implementation (polynomial root-finder) for(n=1; n<=20; n++) { //--- test gauss-legendre err=0; CGaussQ::GQGenerateGaussLegendre(n,info,x,w); //--- check if(info>0) { BuildGaussLegendreQuadrature(n,x2,w2); //--- search errors for(i=0; ierrtol; //--- 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; inonstricterrtol; //--- 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; istricterrtol; //--- 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; inonstricterrtol; //--- Test Gauss-Hermite err=0; CGaussQ::GQGenerateGaussHermite(n,info,x,w); //--- check if(info>0) { BuildGaussHermiteQuadrature(n,x2,w2); //--- search errors for(i=0; inonstricterrtol; } //--- end waserrors=recerrors||specerrors; //--- check if(!silent) { Print("TESTING GAUSS QUADRATURES"); PrintResult("FINAL RESULT",!waserrors); PrintResult("* SPECIAL CASES (LEGENDRE/JACOBI/..)",!specerrors); PrintResult("* RECURRENCE-BASED",!recerrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Maps: | //| 0=> -0.9 | //| 1=> -0.5 | //| 2=> -0.1 | //| 3=> 0.0 | //| 4=> +0.1 | //| 5=> +0.5 | //| 6=> +0.9 | //| 7=> +1.0 | //| 8=> +1.5 | //| 9=> +2.0 | //+------------------------------------------------------------------+ double CTestGQUnit::MapKind(const int k) { //--- create a variable double result=0; //--- check if(k==0) result=-0.9; //--- check if(k==1) result=-0.5; //--- check if(k==2) result=-0.1; //--- check if(k==3) result=0.0; //--- check if(k==4) result=0.1; //--- check if(k==5) result=0.5; //--- check if(k==6) result=0.9; //--- check if(k==7) result=1.0; //--- check if(k==8) result=1.5; //--- check if(k==9) result=2.0; //--- return result return(result); } //+------------------------------------------------------------------+ //| Gauss-Legendre, another variant | //+------------------------------------------------------------------+ void CTestGQUnit::BuildGaussLegendreQuadrature(const int n,double &x[], double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double p1=0; double p2=0; double p3=0; double dp3=0; double tmp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); //--- calculation for(i=0; i<=(n+1)/2-1; i++) { r=MathCos(M_PI*(4*i+3)/(4*n+2)); //--- cycle do { //--- change values p2=0; p3=1; //--- calculation for(j=0; j=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=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Gauss-Jacobi, another variant | //+------------------------------------------------------------------+ void CTestGQUnit::BuildGaussJacobiQuadrature(const int n,const double alpha, const double beta,double &x[], double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double t1=0; double t2=0; double t3=0; double p1=0; double p2=0; double p3=0; double pp=0; double an=0; double bn=0; double a=0; double b=0; double c=0; double tmpsgn=0; double tmp=0; double alfbet=0; double temp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); for(i=0; i=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=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Gauss-Laguerre, another variant | //+------------------------------------------------------------------+ void CTestGQUnit::BuildGaussLaguerreQuadrature(const int n,const double alpha, double &x[],double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double p1=0; double p2=0; double p3=0; double dp3=0; double tsg=0; double tmp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); //--- calculation for(i=0; i=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=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Gauss-Hermite, another variant | //+------------------------------------------------------------------+ void CTestGQUnit::BuildGaussHermiteQuadrature(const int n,double &x[], double &w[]) { //--- create variables int i=0; int j=0; double r=0; double r1=0; double p1=0; double p2=0; double p3=0; double dp3=0; double pipm4=0; double tmp=0; //--- allocation ArrayResize(x,n); ArrayResize(w,n); //--- calculation pipm4=MathPow(M_PI,-0.25); for(i=0; i<=(n+1)/2-1; i++) { //--- check if(i==0) r=MathSqrt(2*n+1)-1.85575*MathPow(2*n+1,-(1.0/6.0)); else { //--- check if(i==1) r=r-1.14*MathPow(n,0.426)/r; else { //--- check if(i==2) r=1.86*r-0.86*x[0]; else { //--- check if(i==3) r=1.91*r-0.91*x[1]; else r=2*r-x[i-2]; } } } //--- cycle do { //--- change values p2=0; p3=pipm4; //--- calculation for(j=0; j=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=x[j+1]) { tmp=x[j]; x[j]=x[j+1]; x[j+1]=tmp; tmp=w[j]; w[j]=w[j+1]; w[j+1]=tmp; } } } } //+------------------------------------------------------------------+ //| Testing class CGaussKronrodQ | //+------------------------------------------------------------------+ class CTestGKQUnit { public: static bool TestGKQ(const bool silent); private: static double MapKind(const int k); }; //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ bool CTestGKQUnit::TestGKQ(const bool silent) { //--- create variables int pkind=0; double errtol=0; double eps=0; double nonstricterrtol=0; int n=0; int i=0; int k=0; int info=0; double err=0; int akind=0; int bkind=0; double alphac=0; double betac=0; int info1=0; int info2=0; bool successatleastonce; bool intblerrors; bool vstblerrors; bool generrors; bool waserrors; //--- create arrays double x1[]; double wg1[]; double wk1[]; double x2[]; double wg2[]; double wk2[]; //--- initialization intblerrors=false; vstblerrors=false; generrors=false; waserrors=false; errtol=10000*CMath::m_machineepsilon; nonstricterrtol=1000*errtol; //--- test recurrence-based Legendre nodes against the precalculated table for(pkind=0; pkind<=5; pkind++) { n=0; //--- check if(pkind==0) n=15; //--- check if(pkind==1) n=21; //--- check if(pkind==2) n=31; //--- check if(pkind==3) n=41; //--- check if(pkind==4) n=51; //--- check if(pkind==5) n=61; //--- function calls CGaussKronrodQ::GKQLegendreCalc(n,info,x1,wk1,wg1); CGaussKronrodQ::GKQLegendreTbl(n,x2,wk2,wg2,eps); //--- check if(info<=0) { generrors=true; break; } //--- search errors for(i=0; ierrtol; 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; ierrtol; } //--- 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; ierrtol)||!successatleastonce; } //--- end waserrors=(intblerrors||vstblerrors)||generrors; //--- check if(!silent) { Print("TESTING GAUSS-KRONROD QUADRATURES"); PrintResult("FINAL RESULT",!waserrors); PrintResult("* PRE-CALCULATED TABLE",!intblerrors); PrintResult("* CALCULATED AGAINST THE TABLE",!vstblerrors); PrintResult("* GENERAL PROPERTIES",!generrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Maps: | //| 0=> -0.9 | //| 1=> -0.5 | //| 2=> -0.1 | //| 3=> 0.0 | //| 4=> +0.1 | //| 5=> +0.5 | //| 6=> +0.9 | //| 7=> +1.0 | //| 8=> +1.5 | //| 9=> +2.0 | //+------------------------------------------------------------------+ double CTestGKQUnit::MapKind(const int k) { //--- create variables double result=0; //--- check if(k==0) result=-0.9; //--- check if(k==1) result=-0.5; //--- check if(k==2) result=-0.1; //--- check if(k==3) result=0.0; //--- check if(k==4) result=0.1; //--- check if(k==5) result=0.5; //--- check if(k==6) result=0.9; //--- check if(k==7) result=1.0; //--- check if(k==8) result=1.5; //--- check if(k==9) result=2.0; //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CAutoGK | //+------------------------------------------------------------------+ class CTestAutoGKUnit { public: static bool TestAutoGK(const bool silent); }; //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ bool CTestAutoGKUnit::TestAutoGK(const bool silent) { //--- create variables double a=0; double b=0; double v=0; double exact=0; double eabs=0; double alpha=0; int pkind=0; double errtol=0; bool simpleerrors; bool sngenderrors; bool waserrors; //--- objects of classes CAutoGKState state; CAutoGKReport rep; //--- initialization simpleerrors=false; sngenderrors=false; waserrors=false; errtol=10000*CMath::m_machineepsilon; //--- Simple test: integral(exp(x),+-1,+-2),no maximum width requirements a=(2*CMath::RandomInteger(2)-1)*1.0; b=(2*CMath::RandomInteger(2)-1)*2.0; //--- function call CAutoGK::AutoGKSmooth(a,b,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathExp(state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=MathExp(b)-MathExp(a); eabs=MathAbs(MathExp(b)-MathExp(a)); //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs; //--- Simple test: integral(exp(x),+-1,+-2),XWidth=0.1 a=(2*CMath::RandomInteger(2)-1)*1.0; b=(2*CMath::RandomInteger(2)-1)*2.0; //--- function call CAutoGK::AutoGKSmoothW(a,b,0.1,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathExp(state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=MathExp(b)-MathExp(a); eabs=MathAbs(MathExp(b)-MathExp(a)); //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs; //--- Simple test: integral(cos(100*x),0,2*pi),no maximum width requirements a=0; b=2*M_PI; //--- function call CAutoGK::AutoGKSmooth(a,b,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathCos(100*state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=0; eabs=4; //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs; //--- Simple test: integral(cos(100*x),0,2*pi),XWidth=0.3 a=0; b=2*M_PI; //--- function call CAutoGK::AutoGKSmoothW(a,b,0.3,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) state.m_f=MathCos(100*state.m_x); //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- change values exact=0; eabs=4; //--- check if(rep.m_terminationtype<=0) simpleerrors=true; else simpleerrors=simpleerrors||MathAbs(exact-v)>errtol*eabs; //--- singular problem on [a,b]=[0.1,0.5] //--- f2(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1 for(pkind=0; pkind<=6; pkind++) { a=0.1; b=0.5; //--- check if(pkind==0) alpha=-0.9; //--- check if(pkind==1) alpha=-0.5; //--- check if(pkind==2) alpha=-0.1; //--- check if(pkind==3) alpha=0.0; //--- check if(pkind==4) alpha=0.1; //--- check if(pkind==5) alpha=0.5; //--- check if(pkind==6) alpha=0.9; //--- f1(x)=(1+x)*(x-a)^alpha,-1 < alpha < 1 //--- 1. use singular integrator for [a,b] //--- 2. use singular integrator for [b,a] exact=MathPow(b-a,alpha+2)/(alpha+2)+(1+a)*MathPow(b-a,alpha+1)/(alpha+1); eabs=MathAbs(exact); //--- function call CAutoGK::AutoGKSingular(a,b,alpha,0.0,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_xminusa<0.01) state.m_f=MathPow(state.m_xminusa,alpha)*(1+state.m_x); else state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors||MathAbs(v-exact)>errtol*eabs; //--- function call CAutoGK::AutoGKSingular(b,a,0.0,alpha,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_bminusx>-0.01) state.m_f=MathPow(-state.m_bminusx,alpha)*(1+state.m_x); else state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors||MathAbs(-v-exact)>errtol*eabs; //--- f1(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1 //--- 1. use singular integrator for [a,b] //--- 2. use singular integrator for [b,a] exact=(1+b)*MathPow(b-a,alpha+1)/(alpha+1)-MathPow(b-a,alpha+2)/(alpha+2); eabs=MathAbs(exact); //--- function call CAutoGK::AutoGKSingular(a,b,0.0,alpha,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_bminusx<0.01) state.m_f=MathPow(state.m_bminusx,alpha)*(1+state.m_x); else state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors||MathAbs(v-exact)>errtol*eabs; //--- function call CAutoGK::AutoGKSingular(b,a,alpha,0.0,state); //--- cycle while(CAutoGK::AutoGKIteration(state)) { //--- check if(state.m_xminusa>-0.01) state.m_f=MathPow(-state.m_xminusa,alpha)*(1+state.m_x); else state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x); } //--- function call CAutoGK::AutoGKResults(state,v,rep); //--- check if(rep.m_terminationtype<=0) sngenderrors=true; else sngenderrors=sngenderrors||MathAbs(-v-exact)>errtol*eabs; } //--- end waserrors=simpleerrors||sngenderrors; //--- check if(!silent) { Print("TESTING AUTOGK"); PrintResult("INTEGRATION WITH GIVEN ACCURACY",!(simpleerrors || sngenderrors)); PrintResult("* SIMPLE PROBLEMS",!simpleerrors); PrintResult("* SINGULAR PROBLEMS (ENDS OF INTERVAL)",!sngenderrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CIDWInt | //+------------------------------------------------------------------+ class CTestIDWIntUnit { public: static bool TestIDWInt(const bool silent); private: static void TestContinuity(CIDWModel &model,int nx,int ny,CRowDouble &x0,CRowDouble &x1,int nsteps,int d,bool &err); static void TestCommon(bool &err); static void TestMSTAB(bool &err); }; //+------------------------------------------------------------------+ //| Testing IDW interpolation | //+------------------------------------------------------------------+ bool CTestIDWIntUnit::TestIDWInt(const bool silent) { bool commonerrors=false; bool mstaberrors=false; TestCommon(commonerrors); TestMSTAB(mstaberrors); bool waserrors=commonerrors||mstaberrors; if(!silent) { Print("TESTING INVERSE DISTANCE WEIGHTING"); PrintResult("* common properties",!commonerrors); PrintResult("* MSTAB-specific tests",!mstaberrors); PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing continuity properties: C0 (D=0) or C1 (D=1) continuity. | //| Error flag is modified on failure, unchanged on success. | //+------------------------------------------------------------------+ void CTestIDWIntUnit::TestContinuity(CIDWModel &model,int nx, int ny,CRowDouble &x0,CRowDouble &x1, int nsteps, int d,bool &err) { //--- create variables int i=0; int j=0; int cidx=0; double t=0; CRowDouble xc; CRowDouble yc; CMatrixDouble yy; double lc1=0; double lc2=0; //--- check if(!CAp::Assert(nsteps>=10,"TestContinuity: NSteps is too small")) { err=true; return; } if(!CAp::Assert(d==0 || d==1,"TestContinuity: incorrect D")) { err=true; return; } //--- Compute sequence of function values xc.Resize(nx); yy.Resize(nsteps,ny); for(i=0; i1.0E-4 && lc1>(1.750*lc2)) { Print("testidwunit.ap:67"); err=true; return; } //--- Differentiate function, repeat one more time for(i=0; i<=nsteps-2; i++) { for(j=0; j0.0) { //--- Fit and store result directly into the variable CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); } else { //--- Fit, store result to temporary, pass through the CSerializer CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj()); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj()); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj()); _local_serializer.Stop(); } } CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer); //--- Test report if(rep.m_rmserror!=0.0) { err=true; Print("testidwunit.ap:169"); return; } if(rep.m_avgerror!=0.0) { err=true; Print("testidwunit.ap:170"); return; } if(rep.m_maxerror!=0.0) { err=true; Print("testidwunit.ap:171"); return; } if(rep.m_r2!=1.0) { err=true; Print("testidwunit.ap:172"); return; } //--- Test simplified evaluation x0=CHighQualityRand::HQRndNormal(rs.GetInnerObj()); x1=CHighQualityRand::HQRndNormal(rs.GetInnerObj()); x2=CHighQualityRand::HQRndNormal(rs.GetInnerObj()); if(nx==1 && ny==1 && CIDWInt::IDWCalc1(model.GetInnerObj(),x0)!=0.0) { err=true; Print("testidwunit.ap:181"); return; } if(nx==2 && ny==1 && CIDWInt::IDWCalc2(model.GetInnerObj(),x0,x1)!=0.0) { err=true; Print("testidwunit.ap:183"); return; } if(nx==3 && ny==1 && CIDWInt::IDWCalc3(model.GetInnerObj(),x0,x1,x2)!=0.0) { err=true; Print("testidwunit.ap:185"); return; } //--- Test generic evaluation x.Resize(nx); for(i=0; i::Zeros(ny); i=0; while(i0.0) { //--- Fit and store result directly into the variable CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); } else { //--- Fit, store result to temporary, pass through the CSerializer CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj()); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj()); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj()); _local_serializer.Stop(); } } CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer); //--- Test error metrics //--- NOTE: we expect that dataset is reproduced exactly if(CAp::SetErrorFlag(err,MathAbs(rep.m_rmserror)>tol,"testidwunit.ap:320") || CAp::SetErrorFlag(err,MathAbs(rep.m_avgerror)>tol,"testidwunit.ap:321") || CAp::SetErrorFlag(err,MathAbs(rep.m_maxerror)>tol,"testidwunit.ap:322") || CAp::SetErrorFlag(err,MathAbs(rep.m_r2)<(1.0-tol),"testidwunit.ap:323")) return; //--- Test that dataset is actually exactly reproduced for(i=0; itol,"testidwunit.ap:339")) return; k=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2*ny+1); y.Resize(k); CIDWInt::IDWCalcBuf(model.GetInnerObj(),x,y); if(CAp::SetErrorFlag(err,CAp::Len(y)!=MathMax(ny,k),"testidwunit.ap:346")) return; for(j=0; jtol,"testidwunit.ap:348")) return; k=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2*ny+1); y.Resize(k); CIDWInt::IDWTsCalcBuf(model.GetInnerObj(),buffer,x,y); if(CAp::SetErrorFlag(err,CAp::Len(y)!=MathMax(ny,k),"testidwunit.ap:355")) return; for(j=0; jtol,"testidwunit.ap:357")) return; //--- Specialized 1, 2, 3-dimensional cases if(ny==1) { if(nx==1 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc1(model.GetInnerObj(),x[0])-xy.Get(i,nx))>tol,"testidwunit.ap:366")) return; if(nx==2 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),x[0],x[1])-xy.Get(i,nx))>tol,"testidwunit.ap:368")) return; if(nx==3 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc3(model.GetInnerObj(),x[0],x[1],x[2])-xy.Get(i,nx))>tol,"testidwunit.ap:370")) return; } } //--- Test continuity properties: //--- * continuity is guaranteed for original Shepard's method, MSTAB and MSMOOTH //--- * modified Shepard method does not guarantee continuity of the model, but //--- we can be sure that model is continuous along line connecting two nearest //--- points for(k=0; k<=1; k++) { i0=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n); x=xy[i0]+0; i1=-1; v=CMath::m_maxrealnumber; for(i=0; i0.0) { i1=i; xx=xy[i]+0; v=vv; } } if(i1<0) { i1=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n); xx=xy[i1]+0; } continuitytesting=1; if(algotype==0) continuitytesting=0; if(algotype==1) continuitytesting=-1; if(continuitytesting>=0) TestContinuity(model.GetInnerObj(),nx,ny,x,xx,10000,continuitytesting,err); if(err) return; } //--- Test evaluation at remote points x.Resize(nx); for(j=0; jtol,"testidwunit.ap:424")) return; } //--- Generate random dataset with NONDISTINCT points, test approximation //--- properties and error reports. for(pass=1; pass<=20; pass++) { n=2*(1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),10)); nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4); ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4); x.Resize(nx); xx.Resize(nx); y.Resize(ny); meany=vector::Zeros(ny); //--- Generate dataset with nondistinct points, each point is repeated; //--- compute reference values of the error metrics xy.Resize(n,nx+ny); refrms=0; refavg=0; refmax=0; refrss=0; reftss=0; i=0; while(i<=n/2-1) { //--- Generate two copies of the same point for(j=0; j0.0) { //--- Fit and store result directly into the variable CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); } else { //--- Fit, store result to temporary, pass through the CSerializer CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj()); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj()); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj()); _local_serializer.Stop(); } } CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer); //--- Test error metrics if(CAp::SetErrorFlag(err,MathAbs(rep.m_rmserror-refrms)>tol,"testidwunit.ap:562") || CAp::SetErrorFlag(err,MathAbs(rep.m_avgerror-refavg)>tol,"testidwunit.ap:563") || CAp::SetErrorFlag(err,MathAbs(rep.m_maxerror-refmax)>tol,"testidwunit.ap:564") || CAp::SetErrorFlag(err,MathAbs(rep.m_r2-refr2)>tol,"testidwunit.ap:565")) return; //--- Test ability to reproduce mean over non-distinct points //--- NOTE: we do not test all evaluation functions, just IDWCalc() for(i=0; itol,"testidwunit.ap:578")) return; } //--- Test continuity properties: //--- * continuity is guaranteed for original Shepard's method, MSTAB and MSMOOTH //--- * modified Shepard method does not guarantee continuity of the model, but //--- we can be sure that model is continuous along line connecting two nearest //--- points for(k=0; k<=1; k++) { i0=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n); x=xy[i0]+0; i1=-1; v=CMath::m_maxrealnumber; for(i=0; i0.0) { i1=i; xx=xy[i]+0; v=vv; } } if(i1<0) { i1=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n); xx=xy[i1]+0; } continuitytesting=1; if(algotype==0) continuitytesting=0; if(algotype==1) continuitytesting=-1; if(continuitytesting>=0) TestContinuity(model.GetInnerObj(),nx,ny,x,xx,10000,continuitytesting,err); if(err) return; } } //--- Test correct handling of the prior term n=10; for(pass=1; pass<=20; pass++) { nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4); ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4); x.Resize(nx); y.Resize(ny); xy.Resize(n,nx+ny); meany=vector::Zeros(ny); for(i=0; itol,"testidwunit.ap:684")) return; } //--- Mean prior CIDWInt::IDWBuilderSetConstTerm(builder); CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); for(j=0; jtol,"testidwunit.ap:696")) return; //--- User-specified prior (not tested with textbook Shepard method) if(algotype!=0) { v=CHighQualityRand::HQRndNormal(rs.GetInnerObj()); CIDWInt::IDWBuilderSetUserTerm(builder,v); CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); for(j=0; jtol,"testidwunit.ap:710")) return; } } } } //+------------------------------------------------------------------+ //| Test MSTAB; | //| Err is set to True on failure, unchanged otherwise. | //+------------------------------------------------------------------+ void CTestIDWIntUnit::TestMSTAB(bool &err) { CMatrixDouble xy; int i=0; int n=0; double v=0; double vv=0; double x0=0; double x1=0; double rbase=0; CIDWModelShell model; CIDWBuilder builder; CIDWReport rep; CHighQualityRandStateShell rs; CHighQualityRand::HQRndRandomize(rs.GetInnerObj()); //--- Basic test #1: nonzero derivative //--- * XY = [[-1,-1],[0,0,],[1,1]] //--- * RBase>=2 //--- * derivative at x=0 must be positive and bigger than 0.1 xy.Resize(3,2); for(i=0; i<=2; i++) { xy.Set(i,0,i-1); xy.Set(i,1,i-1); } CIDWInt::IDWBuilderCreate(1,1,builder); rbase=MathPow(2.0,1.0+CHighQualityRand::HQRndUniformR(rs.GetInnerObj())); CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase); CIDWInt::IDWBuilderSetPoints(builder,xy,3); CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); v=0.01; if(CAp::SetErrorFlag(err,((CIDWInt::IDWCalc1(model.GetInnerObj(),v)-CIDWInt::IDWCalc1(model.GetInnerObj(),-v))/(2*v))<0.1,"testidwunit.ap:758")) return; //--- Basic test #2: good smoothness //--- * 2D task, dataset is composed from 3 parallel lines //--- along y=-0.1, y=0, y=+0.1, with outer lines having //--- constant zero target value, inner line having constant //--- target equal to 1 //--- * RBase=1 is used //--- * we test that function value does not change significantly //--- along the line n=100; xy.Resize(3*n,3); for(i=0; i0.001,"testidwunit.ap:796"); //--- Continuity when moving away from the dataset xy.Resize(1,2); xy.Set(0,0,0); xy.Set(0,1,1); rbase=1.0; CIDWInt::IDWBuilderCreate(1,1,builder); CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase); CIDWInt::IDWBuilderSetPoints(builder,xy,1); CIDWInt::IDWBuilderSetZeroTerm(builder); CIDWInt::IDWFit(builder,model.GetInnerObj(),rep); CAp::SetErrorFlag(err,CIDWInt::IDWCalc1(model.GetInnerObj(),100000)!=0.0,"testidwunit.ap:810"); v=0; for(i=0; i<=500; i++) { x0=1.2*rbase*((double)i/(double)500); x1=1.2*rbase*((double)(i+1)/(double)500); v=MathMax(v,MathAbs((CIDWInt::IDWCalc1(model.GetInnerObj(),x1)-CIDWInt::IDWCalc1(model.GetInnerObj(),x0))/(x1-x0))); } vv=0; for(i=0; i<=1000; i++) { x0=1.2*rbase*((double)i/1000.0); x1=1.2*rbase*((double)(i+1)/1000.0); vv=MathMax(vv,MathAbs((CIDWInt::IDWCalc1(model.GetInnerObj(),x1)-CIDWInt::IDWCalc1(model.GetInnerObj(),x0))/(x1-x0))); } CAp::SetErrorFlag(err,(vv/v)>1.333,"testidwunit.ap:825"); } //+------------------------------------------------------------------+ //| Testing class CRatInt | //+------------------------------------------------------------------+ class CTestRatIntUnit { public: static bool TestRatInt(const bool silent); private: static void PolDiff2(double &x[],double &cf[],int n,double t,double &p,double &dp,double &d2p); static void BRCunSet(CBarycentricInterpolant &b); static bool Is1DSolution(const int n,double &y[],double &w[],double c); }; //+------------------------------------------------------------------+ //| Testing class CRatInt | //+------------------------------------------------------------------+ bool CTestRatIntUnit::TestRatInt(const bool silent) { //--- create variables bool waserrors; bool bcerrors; bool nperrors; double threshold=0; double lipschitztol=0; int maxn=0; int passcount=0; double h=0; double s1=0; double s2=0; int n=0; int n2=0; int i=0; int j=0; int k=0; int d=0; int pass=0; double maxerr=0; double t=0; double a=0; double b=0; double s=0; double v0=0; double v1=0; double v2=0; double v3=0; double d0=0; double d1=0; double d2=0; //--- create arrays double x[]; double x2[]; double y[]; double y2[]; double w[]; double w2[]; double xc[]; double yc[]; int dc[]; //--- objects of classes CBarycentricInterpolant b1; CBarycentricInterpolant b2; //--- initialization nperrors=false; bcerrors=false; waserrors=false; //--- PassCount number of repeated passes //--- Threshold error tolerance //--- LipschitzTol Lipschitz constant increase allowed //--- when calculating constant on a twice denser grid passcount=5; maxn=15; threshold=1000000*CMath::m_machineepsilon; lipschitztol=1.3; //--- Basic barycentric functions for(n=1; n<=10; n++) { //--- randomized tests for(pass=1; pass<=passcount; pass++) { //--- generate weights from polynomial interpolation v0=1+0.4*CMath::RandomReal()-0.2; v1=2*CMath::RandomReal()-1; v2=2*CMath::RandomReal()-1; v3=2*CMath::RandomReal()-1; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); //--- calculation for(i=0; ithreshold; //--- 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; ithreshold*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; jlipschitztol*s1 && s1>threshold*k); } //--- test differentiation properties for(i=0; ithreshold*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; ithreshold) nperrors=true; //--- report waserrors=bcerrors||nperrors; //--- check if(!silent) { Print("TESTING RATIONAL INTERPOLATION"); PrintResult("BASIC BARYCENTRIC FUNCTIONS",!bcerrors); PrintResult("FLOATER-HORMANN",!nperrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestRatIntUnit::PolDiff2(double &x[],double &cf[],int n, double t,double &p,double &dp, double &d2p) { //--- create variables int m=0; int i=0; //--- create arrays double df[]; double d2f[]; double f[]; //--- copy ArrayCopy(f,cf); //--- change values p=0; dp=0; d2p=0; n=n-1; //--- allocation ArrayResize(df,n+1); ArrayResize(d2f,n+1); for(i=0; i<=n; i++) { d2f[i]=0; df[i]=0; } //--- calculation for(m=1; m<=n; m++) { for(i=0; i<=n-m; i++) { d2f[i]=((t-x[i+m])*d2f[i]+(x[i]-t)*d2f[i+1]+2*df[i]-2*df[i+1])/(x[i]-x[i+m]); df[i]=((t-x[i+m])*df[i]+f[i]+(x[i]-t)*df[i+1]-f[i+1])/(x[i]-x[i+m]); f[i]=((t-x[i+m])*f[i]+(x[i]-t)*f[i+1])/(x[i]-x[i+m]); } } //--- change values p=f[0]; dp=df[0]; d2p=d2f[0]; } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestRatIntUnit::BRCunSet(CBarycentricInterpolant &b) { //--- create arrays double x[]; double y[]; double w[]; //--- allocation ArrayResize(x,1); ArrayResize(y,1); ArrayResize(w,1); //--- change values x[0]=0; y[0]=0; w[0]=1; //--- function call CRatInt::BarycentricBuildXYW(x,y,w,1,b); } //+------------------------------------------------------------------+ //| Tests whether constant C is solution of 1D LLS problem | //+------------------------------------------------------------------+ bool CTestRatIntUnit::Is1DSolution(const int n,double &y[], double &w[],double c) { //--- create variables bool result; int i=0; double s1=0; double s2=0; double s3=0; double delta=0; //--- initialization delta=0.001; //--- Test result s1=0; for(i=0; i=s1 && s3>=s1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CPolInt | //+------------------------------------------------------------------+ class CTestPolIntUnit { public: static bool TestPolInt(const bool silent); private: static double InternalPolInt(double &x[],double &cf[],int n,const double t); static void BRCunSet(CBarycentricInterpolant &b); }; //+------------------------------------------------------------------+ //| Unit test | //+------------------------------------------------------------------+ bool CTestPolIntUnit::TestPolInt(const bool silent) { //--- create variables bool waserrors; bool interrors; double threshold=0; double a=0; double b=0; double t=0; int i=0; int k=0; double v=0; double v0=0; double v1=0; double v2=0; double v3=0; double v4=0; double pscale=0; double poffset=0; int n=0; int maxn=0; int pass=0; int passcount=0; //--- create arrays double x[]; double y[]; double w[]; double c[]; double x2[]; double y2[]; double w2[]; double xfull[]; double yfull[]; double xc[]; double yc[]; int dc[]; //--- objects of classes CBarycentricInterpolant p; CBarycentricInterpolant p1; CBarycentricInterpolant p2; //--- initialization waserrors=false; interrors=false; maxn=5; passcount=20; threshold=1.0E8*CMath::m_machineepsilon; //--- Test equidistant interpolation for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { //--- prepare task: //--- * equidistant points //--- * random Y //--- * T in [A,B] or near (within 10% of its width) do { a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; } while(MathAbs(a-b)<=0.2); //--- change value t=a+(1.2*CMath::RandomReal()-0.1)*(b-a); //--- function call CApServ::TaskGenInt1DEquidist(a,b,n,x,y); //--- test "fast" equidistant interpolation (no barycentric model) interrors=interrors||MathAbs(CPolInt::PolynomialCalcEqDist(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "slow" equidistant interpolation (create barycentric model) BRCunSet(p); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "fast" interpolation (create "fast" barycentric model) BRCunSet(p); CPolInt::PolynomialBuildEqDist(a,b,y,n,p); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; } } //--- Test Chebyshev-1 interpolation for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { //--- prepare task: //--- * equidistant points //--- * random Y //--- * T in [A,B] or near (within 10% of its width) do { a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; } while(MathAbs(a-b)<=0.2); //--- change value t=a+(1.2*CMath::RandomReal()-0.1)*(b-a); //--- function call CApServ::TaskGenInt1DCheb1(a,b,n,x,y); //--- test "fast" interpolation (no barycentric model) interrors=interrors||MathAbs(CPolInt::PolynomialCalcCheb1(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "slow" interpolation (create barycentric model) BRCunSet(p); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "fast" interpolation (create "fast" barycentric model) BRCunSet(p); CPolInt::PolynomialBuildCheb1(a,b,y,n,p); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; } } //--- Test Chebyshev-2 interpolation for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { //--- prepare task: //--- * equidistant points //--- * random Y //--- * T in [A,B] or near (within 10% of its width) do { a=2*CMath::RandomReal()-1; b=2*CMath::RandomReal()-1; } while(MathAbs(a-b)<=0.2); //--- change value t=a+(1.2*CMath::RandomReal()-0.1)*(b-a); //--- function call CApServ::TaskGenInt1DCheb2(a,b,n,x,y); //--- test "fast" interpolation (no barycentric model) interrors=interrors||MathAbs(CPolInt::PolynomialCalcCheb2(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "slow" interpolation (create barycentric model) BRCunSet(p); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; //--- test "fast" interpolation (create "fast" barycentric model) BRCunSet(p); CPolInt::PolynomialBuildCheb2(a,b,y,n,p); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold; } } //--- Testing conversion Barycentric<->Chebyshev for(pass=1; pass<=passcount; pass++) { for(k=1; k<=3; k++) { //--- Allocate ArrayResize(x,k); ArrayResize(y,k); //--- Generate problem a=2*CMath::RandomReal()-1; b=a+(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1); v0=2*CMath::RandomReal()-1; v1=2*CMath::RandomReal()-1; v2=2*CMath::RandomReal()-1; //--- check if(k==1) { x[0]=0.5*(a+b); y[0]=v0; } //--- check if(k==2) { x[0]=a; y[0]=v0-v1; x[1]=b; y[1]=v0+v1; } //--- check if(k==3) { x[0]=a; y[0]=v0-v1+v2; x[1]=0.5*(a+b); y[1]=v0-v2; x[2]=b; y[2]=v0+v1+v2; } //--- Test forward conversion CPolInt::PolynomialBuild(x,y,k,p); ArrayResize(c,1); CPolInt::PolynomialBar2Cheb(p,a,b,c); //--- search errors interrors=interrors||CAp::Len(c)!=k; //--- check if(k>=1) interrors=interrors||MathAbs(c[0]-v0)>threshold; //--- check if(k>=2) interrors=interrors||MathAbs(c[1]-v1)>threshold; //--- check if(k>=3) interrors=interrors||MathAbs(c[2]-v2)>threshold; //--- Test backward conversion CPolInt::PolynomialCheb2Bar(c,k,a,b,p2); v=a+CMath::RandomReal()*(b-a); //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold; } } //--- Testing conversion Barycentric<->Power for(pass=1; pass<=passcount; pass++) { for(k=1; k<=5; k++) { //--- Allocate ArrayResize(x,k); ArrayResize(y,k); //--- Generate problem poffset=2*CMath::RandomReal()-1; pscale=(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1); v0=2*CMath::RandomReal()-1; v1=2*CMath::RandomReal()-1; v2=2*CMath::RandomReal()-1; v3=2*CMath::RandomReal()-1; v4=2*CMath::RandomReal()-1; //--- check if(k==1) { x[0]=poffset; y[0]=v0; } //--- check if(k==2) { x[0]=poffset-pscale; y[0]=v0-v1; x[1]=poffset+pscale; y[1]=v0+v1; } //--- check if(k==3) { x[0]=poffset-pscale; y[0]=v0-v1+v2; x[1]=poffset; y[1]=v0; x[2]=poffset+pscale; y[2]=v0+v1+v2; } //--- check if(k==4) { x[0]=poffset-pscale; y[0]=v0-v1+v2-v3; x[1]=poffset-0.5*pscale; y[1]=v0-0.5*v1+0.25*v2-0.125*v3; x[2]=poffset+0.5*pscale; y[2]=v0+0.5*v1+0.25*v2+0.125*v3; x[3]=poffset+pscale; y[3]=v0+v1+v2+v3; } //--- check if(k==5) { x[0]=poffset-pscale; y[0]=v0-v1+v2-v3+v4; x[1]=poffset-0.5*pscale; y[1]=v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4; x[2]=poffset; y[2]=v0; x[3]=poffset+0.5*pscale; y[3]=v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4; x[4]=poffset+pscale; y[4]=v0+v1+v2+v3+v4; } //--- Test forward conversion CPolInt::PolynomialBuild(x,y,k,p); ArrayResize(c,1); CPolInt::PolynomialBar2Pow(p,poffset,pscale,c); //--- search errors interrors=interrors||CAp::Len(c)!=k; //--- check if(k>=1) interrors=interrors||MathAbs(c[0]-v0)>threshold; //--- check if(k>=2) interrors=interrors||MathAbs(c[1]-v1)>threshold; //--- check if(k>=3) interrors=interrors||MathAbs(c[2]-v2)>threshold; //--- check if(k>=4) interrors=interrors||MathAbs(c[3]-v3)>threshold; //--- check if(k>=5) interrors=interrors||MathAbs(c[4]-v4)>threshold; //--- Test backward conversion CPolInt::PolynomialPow2Bar(c,k,poffset,pscale,p2); v=poffset+(2*CMath::RandomReal()-1)*pscale; //--- search errors interrors=interrors||MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold; } } //--- crash-test: ability to solve tasks which will overflow/underflow //--- weights with straightforward implementation for(n=1; n<=20; n++) { a=-(0.1*CMath::m_maxrealnumber); b=0.1*CMath::m_maxrealnumber; CApServ::TaskGenInt1DEquidist(a,b,n,x,y); CPolInt::PolynomialBuild(x,y,n,p); //--- search errors for(i=0; ithreshold; 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; ithreshold; //--- 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; ithreshold; //--- compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results n2=2+CMath::RandomInteger(2*n); ArrayResize(tmpx,n2); //--- calculation for(i=0; ithreshold; } } //--- 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; ithreshold; //--- 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; ithreshold; err=0; for(i=0; i1.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; ithreshold; 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; ithreshold; } //--- 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=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; i0.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; i0.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; i0.001; for(i=-10; i<=10; i++) { ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i+v)-(i*intab+vr))>0.001; ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i-1000*CMath::m_machineepsilon)-i*intab)>0.001; ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i)-i*intab)>0.001; ierrors=ierrors||MathAbs(CSpline1D::Spline1DIntegrate(c,i+1000*CMath::m_machineepsilon)-i*intab)>0.001; } //--- report waserrors=((((((((lserrors||cserrors)||crserrors)||hserrors)||aserrors)||dserrors)||cperrors)||uperrors)||lterrors)||ierrors; //--- check if(!silent) { Print("TESTING SPLINE INTERPOLATION"); //--- Normal tests PrintResult("LINEAR SPLINE TEST",!lserrors); PrintResult("CUBIC SPLINE TEST",!cserrors); PrintResult("CATMULL-ROM SPLINE TEST",!crserrors); PrintResult("HERMITE SPLINE TEST",!hserrors); PrintResult("AKIMA SPLINE TEST",!aserrors); PrintResult("DIFFERENTIATION TEST",!dserrors); PrintResult("COPY/SERIALIZATION TEST",!cperrors); PrintResult("UNPACK TEST",!uperrors); PrintResult("LIN.TRANS. TEST",!lterrors); PrintResult("INTEGRATION TEST",!ierrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Lipschitz constants for spline inself,first and second | //| derivatives. | //+------------------------------------------------------------------+ void CTestSpline1DUnit::LConst(const double a,const double b, CSpline1DInterpolant &c, const double lstep,double &l0, double &l1,double &l2) { //--- create variables double t=0; double vl=0; double vm=0; double vr=0; double prevf=0; double prevd=0; double prevd2=0; double f=0; double d=0; double d2=0; //--- change values l0=0; l1=0; l2=0; t=a-0.1; vl=CSpline1D::Spline1DCalc(c,t-2*lstep); vm=CSpline1D::Spline1DCalc(c,t-lstep); vr=CSpline1D::Spline1DCalc(c,t); f=vm; d=(vr-vl)/(2*lstep); d2=(vr-2*vm+vl)/CMath::Sqr(lstep); //--- calculation while(t<=b+0.1) { //--- change values prevf=f; prevd=d; prevd2=d2; vl=vm; vm=vr; vr=CSpline1D::Spline1DCalc(c,t+lstep); f=vm; d=(vr-vl)/(2*lstep); d2=(vr-2*vm+vl)/CMath::Sqr(lstep); l0=MathMax(l0,MathAbs((f-prevf)/lstep)); l1=MathMax(l1,MathAbs((d-prevd)/lstep)); l2=MathMax(l2,MathAbs((d2-prevd2)/lstep)); t=t+lstep; } } //+------------------------------------------------------------------+ //| Unpack testing | //+------------------------------------------------------------------+ bool CTestSpline1DUnit::TestUnpack(CSpline1DInterpolant &c,double &x[]) { //--- create variables bool result; int i=0; int n=0; double err=0; double t=0; double v1=0; double v2=0; int pass=0; int passcount=0; //--- create matrix CMatrixDouble tbl; //--- initialization passcount=20; err=0; //--- function call CSpline1D::Spline1DUnpack(c,n,tbl); //--- calculation for(i=0; i<=n-2; i++) { for(pass=1; pass<=passcount; pass++) { t=CMath::RandomReal()*(tbl[i][1]-tbl[i][0]); v1=tbl[i][2]+t*tbl[i][3]+CMath::Sqr(t)*tbl[i][4]+t*CMath::Sqr(t)*tbl[i][5]; v2=CSpline1D::Spline1DCalc(c,tbl[i][0]+t); //--- search errors err=MathMax(err,MathAbs(v1-v2)); } } //--- search errors for(i=0; i<=n-2; i++) err=MathMax(err,MathAbs(x[i]-tbl[i][0])); //--- search errors for(i=0; i<=n-2; i++) err=MathMax(err,MathAbs(x[i+1]-tbl[i][1])); //--- get result result=err<100*CMath::m_machineepsilon; //--- return result return(result); } //+------------------------------------------------------------------+ //| Unset spline,i.e. initialize it with random garbage | //+------------------------------------------------------------------+ void CTestSpline1DUnit::UnsetSpline1D(CSpline1DInterpolant &c) { //--- create arrays double x[]; double y[]; double d[]; //--- allocation ArrayResize(x,2); ArrayResize(y,2); ArrayResize(d,2); //--- change values x[0]=-1; y[0]=CMath::RandomReal(); d[0]=CMath::RandomReal(); x[1]=1; y[1]=CMath::RandomReal(); d[1]=CMath::RandomReal(); //--- function call CSpline1D::Spline1DBuildHermite(x,y,d,2,c); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ void CTestSpline1DUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Tests whether constant C is solution of 1D LLS problem | //+------------------------------------------------------------------+ bool CTestSpline1DUnit::Is1DSolution(const int n,double &y[], double &w[],const double c) { //--- create variables bool result; int i=0; double s1=0; double s2=0; double s3=0; double delta=0; //--- initialization delta=0.001; //--- Test result s1=0; for(i=0; i=s1 && s3>=s1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Testing class CMinLM | //+------------------------------------------------------------------+ class CTestMinLMUnit { public: static bool TestMinLM(const bool silent); private: static void TestU(bool &errorflag,bool &statefieldsconsistencyflag); static void TestBC(bool &errorflag); static void TestLC(bool &errorflag); static void TestOther(bool &errorflag,bool &statefieldsconsistencyflag); static void TestOptGuard(bool &waserrors); static bool RKindVSStateCheck(int rkind,CMinLMState &state); static void AXMB(CMinLMState &state,CMatrixDouble &a,CRowDouble &b,int n); static void TryReproduceFixedBugs(bool &err); static void TestFunc1(int n,int m,CMatrixDouble &c,CRowDouble &x,double &f,bool needf,CRowDouble &fi,bool needfi,CMatrixDouble &jac,bool needjac); }; //+------------------------------------------------------------------+ //| Testing class CMinLM | //+------------------------------------------------------------------+ bool CTestMinLMUnit::TestMinLM(const bool silent) { //--- create variables bool result=false; bool waserrors=false; bool uerrors=false; bool bcerrors=false; bool lcerrors=false; bool scerror=false; bool othererrors=false; bool optguarderr=false; //--- Various tests TestOther(othererrors,scerror); //--- Tests sorted by constraint types TestU(uerrors,scerror); TestBC(bcerrors); TestLC(lcerrors); //--- Try to reproduce previously fixed bugs TryReproduceFixedBugs(othererrors); //--- Test for MinLMGradientCheck optguarderr=false; TestOptGuard(optguarderr); //--- end waserrors=(uerrors||bcerrors||lcerrors||scerror||othererrors||optguarderr); if(!silent) { Print("TESTING LEVENBERG-MARQUARDT OPTIMIZATION"); Print("PROBLEM TYPES:"); PrintResult("* UNCONSTRAINED",!uerrors); PrintResult("* BOX CONSTRAINED",!bcerrors); PrintResult("* LINEARLY CONSTRAINED",!lcerrors); PrintResult("STATE FIELDS CONSISTENCY",!scerror); PrintResult("OTHER PROPERTIES",!othererrors); PrintResult("OPTGUARD",!optguarderr); PrintResult("TEST SUMMARY",!waserrors); Print(""); } result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| Test for unconstrained problems. | //| On failure sets error flag, leaves it unchanged on success. | //+------------------------------------------------------------------+ void CTestMinLMUnit::TestU(bool &errorflag, bool &statefieldsconsistencyflag) { int n=0; int m=0; int i=0; CRowDouble x; CRowDouble xe; CRowDouble b; double h=0; int rkind=0; CMatrixDouble a; double v=0; double s=0; double eps=0; double epsx=0; int maxits=0; int ckind=0; CMinLMState state; CMinLMReport rep; int i_=0; //--- Reference problem. //--- See comments for RKindVsStateCheck() for more info about RKind. //--- NOTES: we also test negative RKind's corresponding to "inexact" schemes //--- which use approximate finite difference Jacobian. x=vector::Zeros(3); n=3; m=3; h=0.0001; for(rkind=-2; rkind<=5; rkind++) { x.Set(0,100*CMath::RandomReal()-50); x.Set(1,100*CMath::RandomReal()-50); x.Set(2,100*CMath::RandomReal()-50); switch(rkind) { case -2: CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,1); break; case -1: CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,0); break; case 0: CMinLM::MinLMCreateFJ(n,m,x,state); break; case 1: CMinLM::MinLMCreateFGJ(n,m,x,state); break; case 2: CMinLM::MinLMCreateFGH(n,x,state); break; case 3: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); break; case 4: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); break; case 5: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); break; } while(CMinLM::MinLMIteration(state)) { //--- (x-2)^2 + y^2 + (z-x)^2 if(state.m_needfi) { state.m_fi.Set(0,state.m_x[0]-2); state.m_fi.Set(1,state.m_x[1]); state.m_fi.Set(2,state.m_x[2]-state.m_x[0]); } if(state.m_needfij) { state.m_fi.Set(0,state.m_x[0]-2); state.m_fi.Set(1,state.m_x[1]); state.m_fi.Set(2,state.m_x[2]-state.m_x[0]); state.m_j.Set(0,0,1); state.m_j.Set(0,1,0); state.m_j.Set(0,2,0); state.m_j.Set(1,0,0); state.m_j.Set(1,1,1); state.m_j.Set(1,2,0); state.m_j.Set(2,0,-1); state.m_j.Set(2,1,0); state.m_j.Set(2,2,1); } if(state.m_needf || state.m_needfg || state.m_needfgh) state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); if(state.m_needfg || state.m_needfgh) { state.m_g.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } if(state.m_needfgh) { state.m_h.Set(0,0,4); state.m_h.Set(0,1,0); state.m_h.Set(0,2,-2); state.m_h.Set(1,0,0); state.m_h.Set(1,1,2); state.m_h.Set(1,2,0); state.m_h.Set(2,0,-2); state.m_h.Set(2,1,0); state.m_h.Set(2,2,2); } CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:212"); } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:215"); CAp::SetErrorFlag(errorflag,MathAbs(x[0]-2)>0.001,"testminlmunit.ap:216"); CAp::SetErrorFlag(errorflag,MathAbs(x[1])>0.001,"testminlmunit.ap:217"); CAp::SetErrorFlag(errorflag,MathAbs(x[2]-2)>0.001,"testminlmunit.ap:218"); } //--- 1D problem #1 //--- NOTES: we also test negative RKind's corresponding to "inexact" schemes //--- which use approximate finite difference Jacobian. for(rkind=-2; rkind<=5; rkind++) { x.Resize(1); n=1; m=1; h=0.00001; x.Set(0,100*CMath::RandomReal()-50); switch(rkind) { case -2: CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,1); break; case -1: CMinLM::MinLMCreateV(n,m,x,h,state); CMinLM::MinLMSetAccType(state,0); break; case 0: CMinLM::MinLMCreateFJ(n,m,x,state); break; case 1: CMinLM::MinLMCreateFGJ(n,m,x,state); break; case 2: CMinLM::MinLMCreateFGH(n,x,state); break; case 3: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); break; case 4: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); break; case 5: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); break; } while(CMinLM::MinLMIteration(state)) { if(state.m_needfi) state.m_fi.Set(0,MathSin(state.m_x[0])); if(state.m_needfij) { state.m_fi.Set(0,MathSin(state.m_x[0])); state.m_j.Set(0,0,MathCos(state.m_x[0])); } if(state.m_needf || state.m_needfg || state.m_needfgh) state.m_f=CMath::Sqr(MathSin(state.m_x[0])); if(state.m_needfg || state.m_needfgh) state.m_g.Set(0,2*MathSin(state.m_x[0])*MathCos(state.m_x[0])); if(state.m_needfgh) state.m_h.Set(0,0,2*(MathCos(state.m_x[0])*MathCos(state.m_x[0])-MathSin(state.m_x[0])*MathSin(state.m_x[0]))); CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:281"); } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:284"); CAp::SetErrorFlag(errorflag,MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001,"testminlmunit.ap:285"); } //--- Linear equations: test normal optimization and optimization with restarts for(n=1; n<=10; n++) { //--- Prepare task h=0.00001; CMatGen::RMatrixRndCond(n,100,a); x.Resize(n); xe.Resize(n); b.Resize(n); for(i=0; i0.001,"testminlmunit.ap:364"); //--- Now we try to restart algorithm from new point for(i=0; i0.001,"testminlmunit.ap:382"); } } //--- Testing convergence properties using //--- different optimizer types and different conditions. //--- Only limited subset of optimizers is tested because some //--- optimizers converge too quickly. s=100; for(rkind=0; rkind<=5; rkind++) { //--- Skip FGH optimizer - it converges too quickly if(rkind==2) continue; //--- Test for(ckind=0; ckind<=1; ckind++) { eps=0; epsx=0; maxits=0; if(ckind==0) { epsx=1.0E-6; eps=1.0E-4; } if(ckind==1) { maxits=2; eps=0.05; } x.Resize(3); n=3; m=3; for(i=0; i<=2; i++) x.Set(i,6); if(!CAp::Assert(rkind!=2)) return; switch(rkind) { case 0: CMinLM::MinLMCreateFJ(n,m,x,state); break; case 1: CMinLM::MinLMCreateFGJ(n,m,x,state); break; case 3: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,0); break; case 4: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,1); break; case 5: CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetAccType(state,2); break; } CMinLM::MinLMSetCond(state,epsx,maxits); while(CMinLM::MinLMIteration(state)) { if(state.m_needfi || state.m_needfij) { state.m_fi.Set(0,s*(MathExp(state.m_x[0])-2)); state.m_fi.Set(1,CMath::Sqr(state.m_x[1])+1); state.m_fi.Set(2,state.m_x[2]-state.m_x[0]); } if(state.m_needfij) { state.m_j.Set(0,0,s*MathExp(state.m_x[0])); state.m_j.Set(0,1,0); state.m_j.Set(0,2,0); state.m_j.Set(1,0,0); state.m_j.Set(1,1,2*state.m_x[1]); state.m_j.Set(1,2,0); state.m_j.Set(2,0,-1); state.m_j.Set(2,1,0); state.m_j.Set(2,2,1); } if((state.m_needf || state.m_needfg) || state.m_needfgh) { state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+1)+CMath::Sqr(state.m_x[2]-state.m_x[0]); } if(state.m_needfg || state.m_needfgh) { state.m_g.Set(0,s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2])); state.m_g.Set(1,2*(CMath::Sqr(state.m_x[1])+1)*2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); } if(state.m_needfgh) { state.m_h.Set(0,0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2); state.m_h.Set(0,1,0); state.m_h.Set(0,2,-2); state.m_h.Set(1,0,0); state.m_h.Set(1,1,12*CMath::Sqr(state.m_x[1])+4); state.m_h.Set(1,2,0); state.m_h.Set(2,0,-2); state.m_h.Set(2,1,0); state.m_h.Set(2,2,2); } CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:486"); } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:489"); if(errorflag) return; //--- if(ckind==0) { CAp::SetErrorFlag(errorflag,MathAbs(x[0]-MathLog(2))>eps,"testminlmunit.ap:494"); CAp::SetErrorFlag(errorflag,MathAbs(x[1])>eps,"testminlmunit.ap:495"); CAp::SetErrorFlag(errorflag,MathAbs(x[2]-MathLog(2))>eps,"testminlmunit.ap:496"); CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=2,"testminlmunit.ap:497"); } if(ckind==1) { CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=5,"testminlmunit.ap:501"); CAp::SetErrorFlag(errorflag,rep.m_iterationscount!=maxits,"testminlmunit.ap:502"); } } } } //+------------------------------------------------------------------+ //| Test for box constrained problems. | //| On failure sets error flag, leaves it unchanged on success. | //+------------------------------------------------------------------+ void CTestMinLMUnit::TestBC(bool &errorflag) { CMinLMState state; CMinLMReport rep; CRowDouble bl; CRowDouble bu; int n=0; int m=0; CRowDouble x0; CRowDouble x; CRowDouble xe; CRowDouble x1; CRowDouble d; CMatrixDouble c; int i=0; int j=0; double v=0; double h=0; int tmpkind=0; CHighQualityRandState rs; double epsx=0; double tolf=0; double f0=0; double f1=0; CHighQualityRand::HQRndRandomize(rs); //--- Reference box constrained problem: //--- min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1 //--- NOTES: //--- 1. we test only two optimization modes - V and FGH, //--- because from algorithm internals we can assume that actual //--- mode being used doesn't matter for bound constrained optimization //--- process. for(tmpkind=0; tmpkind<=1; tmpkind++) { for(n=1; n<=5; n++) { bl.Resize(n); bu.Resize(n); xe.Resize(n); x.Resize(n); for(i=0; i::Zeros(n,n); state.m_h.Diag(MathPow(state.m_x.ToVector()-xe.ToVector(),2.0)*12); } } CMinLM::MinLMResults(state,x,rep); if(rep.m_terminationtype>0) { for(i=0; i(double)(5.0E-2),"testminlmunit.ap:594"); } else CAp::SetErrorFlag(errorflag,true,"testminlmunit.ap:597"); } } //--- Minimize //--- [ [ ]2 ] //--- SUM_i[ SUM_j[ power(x_j,3)*c_ij ] ] //--- [ [ ] ] //--- subject to non-negativity constraints on x_j epsx=1.0E-9; tolf=1.0E-10; for(tmpkind=0; tmpkind<=1; tmpkind++) { for(n=1; n<=20; n++) { m=n+CHighQualityRand::HQRndUniformI(rs,n); bl.Resize(n); bu.Resize(n); x0.Resize(n); for(i=0; ibu[i],"testminlmunit.ap:640"); } if(state.m_needfi) { TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false); continue; } if(state.m_needfij) { TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true); continue; } //--- check if(!CAp::Assert(false,"MinLM test: integrity check failed")) return; } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:655"); if(errorflag) return; //--- TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false); x1.Resize(n); h=0.001; for(i=0; ibu[i],"testminlmunit.ap:665"); if((x[i]+h)>=bl[i]) { x1=x; x1.Set(i,x[i]+h); TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false); CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:674"); } if((x[i]-h)>=bl[i]) { x1=x; x1.Set(i,x[i]-h); TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false); CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:684"); } } } } //--- Minimize //--- [ [ ]2 ] //--- SUM_i[ SUM_j[ power(x_j,3)*c_ij ] ] //--- [ [ ] ] //--- subject to random box constraints on x_j epsx=1.0E-9; tolf=1.0E-10; for(tmpkind=0; tmpkind<=1; tmpkind++) { for(n=1; n<=20; n++) { m=n+CHighQualityRand::HQRndUniformI(rs,n); bl.Resize(n); bu.Resize(n); x0.Resize(n); for(i=0; ibu[i],"testminlmunit.ap:729"); } if(state.m_needfi) { TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false); continue; } if(state.m_needfij) { TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true); continue; } //--- check if(!CAp::Assert(false,"minlm test: integrity check failed")) return; } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:744"); if(errorflag) return; //--- TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false); x1.Resize(n); h=0.001; for(i=0; ibu[i],"testminlmunit.ap:754"); if((x[i]+h)>=bl[i] && (x[i]+h)<=bu[i]) { x1=x; x1.Set(i,x[i]+h); TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false); CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:763"); } if((x[i]-h)>=bl[i] && (x[i]-h)<=bu[i]) { x1=x; x1.Set(i,x[i]-h); TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false); CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:773"); } } } } } //+------------------------------------------------------------------+ //| Test for linearly constrained problems. | //| On failure sets error flag, leaves it unchanged on success. | //+------------------------------------------------------------------+ void CTestMinLMUnit::TestLC(bool &errorflag) { CMinLMState state; CMinLMReport rep; CRowDouble bl; CRowDouble bu; int n=0; int m=0; int m1=0; int m2=0; CRowDouble x0; CRowDouble x; CRowDouble xe; CRowDouble x1; CRowDouble x12; CRowDouble d; CMatrixDouble rawc; CRowInt rawct; int rawccnt=0; CMatrixDouble c; CMatrixDouble c12; CMatrixDouble z; int trialidx=0; int i=0; int j=0; double v=0; double h=0; int optkind=0; CHighQualityRandState rs; double epsx=0; double xtol=0; double tolf=0; double f0=0; double f1=0; bool bflag=false; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Minimize //--- [ [ ]2 ] //--- SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ] ] //--- [ [ ] ] //--- subject to mix of box and linear inequality constraints on x_j //--- We check correctness of solution by sampling a few random points //--- around one returned by optimizer, and comparing function value //--- with target. Sampling is performed with respect to inequality //--- constraints. epsx=1.0E-12; xtol=1.0E-8; tolf=1.0E-10; for(optkind=0; optkind<=1; optkind++) { for(n=5; n<=20; n++) { //--- Generate problem m=n+CHighQualityRand::HQRndUniformI(rs,n); bl.Resize(n); bu.Resize(n); x0.Resize(n); for(i=0; i=5)) return; rawccnt=3; rawc.Resize(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; ibu[i],"testminlmunit.ap:879"); } if(state.m_needfi) { TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false); continue; } if(state.m_needfij) { TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true); continue; } //--- check if(!CAp::Assert(false,"MinLM test: integrity check failed")) return; } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:894"); if(errorflag) return; //--- Test feasibility w.r.t. box and linear inequality constraints for(i=0; ibu[i],"testminlmunit.ap:904"); } for(i=0; i<=rawccnt-1; i++) { v=CAblasF::RDotVR(n,x,rawc,i); v-=rawc.Get(i,n); if(rawct[i]>0) CAp::SetErrorFlag(errorflag,v<(-xtol),"testminlmunit.ap:911"); if(rawct[i]<0) CAp::SetErrorFlag(errorflag,v>xtol,"testminlmunit.ap:913"); } //--- Make several random trial steps and: //--- 0) generate small random trial step //--- 1) if step is infeasible, skip to next trial //--- 2) compare function value in the trial point against one in other points TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false); x1.Resize(n); for(trialidx=0; trialidx<=10*n; trialidx++) { h=0.001; for(i=0; i=bl[i] && x1[i]<=bu[i])); } bflag=false; for(i=0; i0 && v<0.0); bflag=bflag||(rawct[i]<0 && v>0.0); } if(bflag) continue; //--- TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false); CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:950"); } } } //--- Minimize //--- [ [ ]2 ] //--- SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ] ] //--- [ [ ] ] //--- subject to linear EQUALITY constraints on x_j. //--- We check correctness of solution by sampling a few random points //--- around one returned by optimizer, and comparing function value //--- with target. Sampling is performed with respect to equality //--- constraints. In order to simplify algorithm we use orthogonal //--- equality constraints. //--- NOTE: we solve problem using VJ mode (analytic Jacobian) because //--- roundoff errors from numerical differentiation sometimes //--- prevent us from converging with good precision. epsx=1.0E-12; xtol=1.0E-8; tolf=1.0E-10; optkind=1; for(n=10; n<=20; n++) { //--- Generate problem m=n+CHighQualityRand::HQRndUniformI(rs,n); x0.Resize(n); for(i=0; i=5)) return; rawccnt=1+CHighQualityRand::HQRndUniformI(rs,5); CMatGen::RMatrixRndOrthogonal(n,z); rawc.Resize(rawccnt,n+1); rawct.Resize(rawccnt); rawct.Fill(0); for(i=0; ixtol,"testminlmunit.ap:1042"); } //--- Make several random trial steps and: //--- 0) generate small random trial step //--- 1) project it onto equality constrained subspace //--- 2) compare function value in the trial point against one in other points TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false); x1.Resize(n); for(trialidx=0; trialidx<=10*n; trialidx++) { h=0.001; for(i=0; i0.0)) return; v=h/MathSqrt(v); x1*=v; x1+=x; TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false); CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:1072"); } } //--- Minimize //--- [ [ ]2 ] [ [ ]2 ] //--- SUM_i[ SUM_j[ (0.1*x_j+power(x0_j,3))*c0_ij ] ] + SUM_i[ SUM_j[ (0.1*x_j+power(x1_j,3))*c1_ij ] ] //--- [ [ ] ] [ [ ] ] //--- for two sets of unknowns (x0_j and x1_j) and two sets of //--- coefficients (c0_ij and c1_ij, M1*N and M2*N matrices) subject //--- to equality constraint //--- x0_j=x1_j for all j //--- Such optimization problem arises when we fit same model to //--- two distinct datasets and want to share SOME of coefficients //--- between fits. If we share ALL coefficients, it is equal to //--- fitting one model to combination of two datasets. //--- Our test checks that such "combined" 2N-dimensional problem //--- solved with general linear constraints which "glue" two datasets //--- together returns same answer as N-dimensional problem on (M1+M2)-point //--- dataset. //--- NOTE: we solve problem using VJ mode (analytic Jacobian) because //--- roundoff errors from numerical differentiation prevent us //--- from converging with good precision. epsx=1.0E-12; for(n=5; n<=20; n++) { //--- Generate problems m1=n+CHighQualityRand::HQRndUniformI(rs,n); m2=n+CHighQualityRand::HQRndUniformI(rs,n); x0.Resize(n); x12.Resize(2*n); for(i=0; i::Zeros(m1+m2,2*n+1); for(i=0; i<=m1+m2-1; i++) { for(j=0; j::Identity(rawccnt,2*n+1); rawct.Resize(rawccnt); rawct.Fill(0); rawc.Diag(vector::Full(n,-1),n); //--- Solve N-dimensional "combined" problem, store result to X1 CMinLM::MinLMCreateVJ(n,m1+m2,x0,state); CMinLM::MinLMSetCond(state,epsx,0); while(CMinLM::MinLMIteration(state)) { if(state.m_needfi) { TestFunc1(n,m1+m2,c,state.m_x,v,false,state.m_fi,true,state.m_j,false); continue; } if(state.m_needfij) { TestFunc1(n,m1+m2,c,state.m_x,v,false,state.m_fi,true,state.m_j,true); continue; } //--- check if(!CAp::Assert(false,"MinLM Test: integrity check failed")) return; } CMinLM::MinLMResults(state,x,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1170"); if(errorflag) return; //--- //--- Solve N-dimensional "glued" problem, store result to X12 CMinLM::MinLMCreateVJ(2*n,m1+m2,x12,state); CMinLM::MinLMSetCond(state,epsx,0); CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt); while(CMinLM::MinLMIteration(state)) { if(state.m_needfi) { TestFunc1(2*n,m1+m2,c12,state.m_x,v,false,state.m_fi,true,state.m_j,false); continue; } if(state.m_needfij) { TestFunc1(2*n,m1+m2,c12,state.m_x,v,false,state.m_fi,true,state.m_j,true); continue; } //--- check if(!CAp::Assert(false,"minlm test: integrity check failed")) return; } CMinLM::MinLMResults(state,x12,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1195"); if(errorflag) return; //--- //--- Compare solutions for(i=0; i1.0E-3,"testminlmunit.ap:1204"); CAp::SetErrorFlag(errorflag,MathAbs(x[i]-x12[n+i])>1.0E-3,"testminlmunit.ap:1205"); CAp::SetErrorFlag(errorflag,MathAbs(x12[i]-x12[n+i])>1.0E-6,"testminlmunit.ap:1206"); } } } //+------------------------------------------------------------------+ //| Test other properties | //| On failure sets error flag, leaves it unchanged on success. | //+------------------------------------------------------------------+ void CTestMinLMUnit::TestOther(bool &errorflag,bool &statefieldsconsistencyflag) { CMinLMState state; CMinLMReport rep; int n=0; int m=0; double v=0; double s=0; CRowDouble x; CRowDouble xlast; int i=0; int j=0; int rkind=0; double fprev=0; double xprev=0; double stpmax=0; int stopcallidx=0; int callidx=0; int maxits=0; bool terminationrequested=false; int pass=0; int spoilcnt=0; double mx=0; int i_=0; //--- Other properties: //--- 1. test reports (F should form monotone sequence) //--- 2. test maximum step for(rkind=0; rkind<=5; rkind++) { //--- reports: //--- * check that first report is initial point //--- * check that F is monotone decreasing //--- * check that last report is final result n=3; m=3; s=100; x.Resize(n); xlast.Resize(n); for(i=0; ifprev,"testminlmunit.ap:1323"); if(fprev==CMath::m_maxrealnumber) { for(i=0; i::Zeros(n); x.Set(0,100); stpmax=0.05+0.05*CMath::RandomReal(); CMinLM::MinLMCreateFGH(n,x,state); CMinLM::MinLMSetCond(state,1.0E-12,0); CMinLM::MinLMSetStpMax(state,stpmax); CMinLM::MinLMSetXRep(state,true); xprev=x[0]; while(CMinLM::MinLMIteration(state)) { if((state.m_needf || state.m_needfg) || state.m_needfgh) state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); if(state.m_needfg || state.m_needfgh) state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0])); if(state.m_needfgh) state.m_h.Set(0,0,MathExp(state.m_x[0])+MathExp(-state.m_x[0])); CAp::SetErrorFlag(errorflag,MathAbs(state.m_x[0]-xprev)>((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminlmunit.ap:1355"); if(state.m_xupdated) xprev=state.m_x[0]; } //--- Check algorithm ability to handle request for termination: //--- * to terminate with correct return code = 8 //---*to return point which was "current" at the moment of termination for(pass=1; pass<=50; pass++) { n=3; m=3; s=100; x=vector::Zeros(n); xlast=vector::Zeros(n); for(i=0; i::Zeros(n); CMinLM::MinLMCreateVJ(n,m,x,state); CMinLM::MinLMSetCond(state,1.0E-12,0); spoilcnt=0; while(CMinLM::MinLMIteration(state)) { if(state.m_needfij) { for(i=0; i0.5) state.m_fi.Set(CMath::RandomInteger(m),v); else state.m_j.Set(CMath::RandomInteger(m),CMath::RandomInteger(n),v); spoilcnt++; } continue; } if(state.m_needfi) { for(i=0; i::Zeros(n)); continue; } //--- check if(!CAp::Assert(false)) return; } CMinLM::MinLMResults(state,x1,rep); CMinLM::MinLMOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminlmunit.ap:1575"); CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminlmunit.ap:1576"); if(waserrors) return; CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminlmunit.ap:1579"); CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminlmunit.ap:1580"); CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminlmunit.ap:1581"); CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminlmunit.ap:1582"); CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminlmunit.ap:1583"); if(waserrors) return; CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminlmunit.ap:1586"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminlmunit.ap:1587"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminlmunit.ap:1588"); //--- Test gradient checking functionality, try various //--- defect types: //--- * accidental zeroing of some gradient component //--- * accidental addition of 1.0 to some component //--- * accidental multiplication by 2.0 //--- Try distorting both target and constraints. diffstep=0.001; n=10; for(skind=0; skind<=1; skind++) { for(funcidx=0; funcidx<=1; funcidx++) { for(defecttype=-1; defecttype<=2; defecttype++) { varidx=CHighQualityRand::HQRndUniformI(rs,n); x0.Resize(n); s.Resize(n); bndl.Resize(n); bndu.Resize(n); for(i=0; ibndu[i],"testminlmunit.ap:1638"); } state.m_fi.Set(0,0); vector xs=state.m_x/s; for(i=0; i bs=ogrep.m_badgradxbase/s; for(i=0; i=0) { CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminlmunit.ap:1747"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=funcidx,"testminlmunit.ap:1748"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminlmunit.ap:1749"); } else { CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminlmunit.ap:1753"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminlmunit.ap:1754"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminlmunit.ap:1755"); } for(i=0; i<=1; i++) { for(j=0; j(0.01/s[j]),"testminlmunit.ap:1760"); CAp::SetErrorFlag(waserrors,MathAbs(jacdefect.Get(i,j)-ogrep.m_badgraduser.Get(i,j))>(0.01/s[j]),"testminlmunit.ap:1761"); } } } } } //+------------------------------------------------------------------+ //| Asserts that State fields are consistent with RKind. | //| Returns False otherwise. | //| RKind is an algorithm selector: | //| * -2 = V, AccType = 1 | //| * -1 = V, AccType = 0 | //| * 0 = FJ | //| * 1 = FGJ | //| * 2 = FGH | //| * 3 = VJ, AccType = 0 | //| * 4 = VJ, AccType = 1 | //| * 5 = VJ, AccType = 2 | //+------------------------------------------------------------------+ bool CTestMinLMUnit::RKindVSStateCheck(int rkind,CMinLMState &state) { int nset=0; //--- check if(state.m_needfi) nset++; if(state.m_needf) nset++; if(state.m_needfg) nset++; if(state.m_needfij) nset++; if(state.m_needfgh) nset++; if(state.m_xupdated) nset++; //--- check if(nset!=1) return(false); switch(rkind) { case -2: return (state.m_needfi || state.m_xupdated); case -1: return (state.m_needfi || state.m_xupdated); case 0: return (state.m_needf || state.m_needfij || state.m_xupdated); case 1: return (state.m_needf || state.m_needfij || state.m_needfg || state.m_xupdated); case 2: return (state.m_needf || state.m_needfg || state.m_needfgh || state.m_xupdated); case 3: return (state.m_needfi || state.m_needfij || state.m_xupdated); case 4: return (state.m_needfi || state.m_needfij || state.m_xupdated); case 5: return (state.m_needfi || state.m_needfij || state.m_xupdated); } return(false); } //+------------------------------------------------------------------+ //| Calculates FI / F / G / H for problem min( || Ax - b ||) | //+------------------------------------------------------------------+ void CTestMinLMUnit::AXMB(CMinLMState &state,CMatrixDouble &a, CRowDouble &b,int n) { //--- create variables double v=0; if(state.m_needf || state.m_needfg || state.m_needfgh) state.m_f=0; if(state.m_needfg || state.m_needfgh) state.m_g=vector::Zeros(n); if(state.m_needfgh) state.m_h=matrix::Zeros(n,n); for(int i=0; i::Full(2,-1); CRowDouble bu=vector::Full(2,1);; CRowDouble x=vector::Full(2,2); //--- Reproduce bug reported by ISS: //--- when solving bound constrained problem with numerical differentiation //--- and starting from infeasible point, we won't Stop at the feasible point CMinLM::MinLMCreateV(2,2,x,0.001,s); CMinLM::MinLMSetBC(s,bl,bu); while(CMinLM::MinLMIteration(s)) { if(s.m_needfi) s.m_fi=s.m_x.Pow(2)+0; } CMinLM::MinLMResults(s,x,rep); CAp::SetErrorFlag(err,(x[0]bu[0] || x[1]bu[1]),"testminlmunit.ap:1923"); } //+------------------------------------------------------------------+ //| Test function 1: F(N, M, C, X) = SUM(f_i ^ 2) | //| f_i = SUM((power(x_j, 3) + alpha*x_j) * c_ij) | //+------------------------------------------------------------------+ void CTestMinLMUnit::TestFunc1(int n,int m,CMatrixDouble &c, CRowDouble &x,double &f, bool needf,CRowDouble &fi, bool needfi,CMatrixDouble &jac, bool needjac) { //--- create variables double v=0; double alpha=0.01; if(needf) f=0; for(int i=0; i0) { //--- 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; ithreshold; for(i=0; ithreshold; } } //--- 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; ithreshold; //--- 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; iv1; 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=2,"PassCount should be 2 or greater!")) return; //--- solve simple task (all X[] are the same,Y[] are specially //--- calculated to ensure simple form of all types of errors) //--- and check correctness of the errors calculated by subroutines //--- First pass is done with zero Y[],other passes - with random Y[]. //--- It should test both ability to correctly calculate errors and //--- ability to not fail while working with zeros :) n=4*maxn; //--- check if(pass==1) { v1=0; v2=0; v=0; } else { v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); } //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); //--- change values for(i=0; i<=maxn-1; i++) { x[4*i+0]=i; y[4*i+0]=v-v2; w[4*i+0]=1; x[4*i+1]=i; y[4*i+1]=v-v1; w[4*i+1]=1; x[4*i+2]=i; y[4*i+2]=v+v1; w[4*i+2]=1; x[4*i+3]=i; y[4*i+3]=v+v2; w[4*i+3]=1; } //--- change values refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; //--- check if(pass==1) refavgrel=0; else refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test errors correctness CLSFit::PolynomialFit(x,y,n,m,info,p,rep); //--- check if(info<=0) fiterrors=true; else { s=CRatInt::BarycentricCalc(p,0); //--- search errors fiterrors=fiterrors||MathAbs(s-v)>threshold; fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold; } } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestLSFitUnit::TestRationalFitting(bool &fiterrors) { //--- create variables double threshold=0; int maxn=0; int passcount=0; int n=0; int m=0; int i=0; int k=0; int pass=0; double t=0; double s=0; double v=0; double v0=0; double v1=0; double v2=0; int info=0; int info2=0; double xmin=0; double xmax=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; //--- create arrays double x[]; double x2[]; double y[]; double y2[]; double w[]; double w2[]; double xc[]; double yc[]; int dc[]; //--- objects of classes CBarycentricInterpolant b1; CBarycentricInterpolant b2; CBarycentricFitReport rep; CBarycentricFitReport rep2; //--- initialization fiterrors=false; //--- PassCount number of repeated passes //--- Threshold error tolerance //--- LipschitzTol Lipschitz constant increase allowed //--- when calculating constant on a twice denser grid passcount=5; maxn=15; threshold=1000000*CMath::m_machineepsilon; //--- Test rational fitting: for(pass=1; pass<=passcount; pass++) { for(n=2; n<=maxn; n++) { //--- N=M+K fitting (i.e. interpolation) for(k=0; k0) { //--- 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; ithreshold; for(i=0; ithreshold; } } //--- 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; ithreshold; } } } } } } //--- 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; iv1; 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=2,"PassCount should be 2 or greater!")) return; //--- solve simple task (all X[] are the same,Y[] are specially //--- calculated to ensure simple form of all types of errors) //--- and check correctness of the errors calculated by subroutines //--- First pass is done with zero Y[],other passes - with random Y[]. //--- It should test both ability to correctly calculate errors and //--- ability to not fail while working with zeros :) n=4; if(pass==1) { v1=0; v2=0; v=0; } else { v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); } //--- allocation ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); //--- change values x[0]=0; y[0]=v-v2; w[0]=1; x[1]=0; y[1]=v-v1; w[1]=1; x[2]=0; y[2]=v+v1; w[2]=1; x[3]=0; y[3]=v+v2; w[3]=1; refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; //--- check if(pass==1) refavgrel=0; else refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test errors correctness CLSFit::BarycentricFitFloaterHormann(x,y,4,2,info,b1,rep); //--- check if(info<=0) fiterrors=true; else { s=CRatInt::BarycentricCalc(b1,0); //--- search errors fiterrors=fiterrors||MathAbs(s-v)>threshold; fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold; } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestLSFitUnit::TestSplineFitting(bool &fiterrors) { //--- create variables double threshold=0; double nonstrictthreshold=0; int passcount=0; int n=0; int m=0; int i=0; int k=0; int pass=0; double sa=0; double sb=0; int info=0; int info1=0; int info2=0; double s=0; double ds=0; double d2s=0; int stype=0; double t=0; double v=0; double v1=0; double v2=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; double rho=0; //--- create arrays double x[]; double y[]; double w[]; double w2[]; double xc[]; double yc[]; double d[]; int dc[]; //--- objects of classes CSpline1DInterpolant c; CSpline1DInterpolant c2; CSpline1DFitReport rep; CSpline1DFitReport rep2; //--- Valyes: //--- * pass count //--- * threshold - for tests which must be satisfied exactly //--- * nonstrictthreshold - for approximate tests passcount=20; threshold=10000*CMath::m_machineepsilon; nonstrictthreshold=1.0E-4; fiterrors=false; //--- Test fitting by Cubic and Hermite splines (obsolete,but still supported) for(pass=1; pass<=passcount; pass++) { //--- Cubic splines //--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx). for(m=4; m<=8; m++) { for(k=1; k<=4; k++) { //--- check if(k>=m) continue; n=100; //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(xc,4); ArrayResize(yc,4); ArrayResize(dc,4); //--- change values sa=1+CMath::RandomReal(); sb=2*CMath::RandomReal()-1; for(i=0; ithreshold; //--- 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; ithreshold; //--- 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; ithreshold; //--- 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; ithreshold; //--- 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; ithreshold; } } } } } //--- 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=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=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; i0) { v=2*CMath::RandomReal()-1; v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0]; //--- search errors fiterrors=fiterrors||MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold; } else fiterrors=true; //--- function call CIntComp::Spline1DFitPenalizedW(x,y,w,2,m,rho,info,c,rep); //--- check if(info>0) { v=2*CMath::RandomReal()-1; v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0]; //--- search errors fiterrors=fiterrors||MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold; } else fiterrors=true; //--- spline fitting is invariant with respect to //--- scaling of weights (of course,ANY fitting algorithm //--- must be invariant,but we want to test this property //--- just to be sure that it is correctly implemented) for(n=2; n<=2*m; n++) { //--- allocation ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); ArrayResize(w2,n); //--- change values s=1+MathExp(10*CMath::RandomReal()); for(i=0; i0 && 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; i0) { //--- search errors for(i=0; inonstrictthreshold; } else fiterrors=true; //--- function call CIntComp::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep); //--- check if(info>0) { //--- search errors for(i=0; inonstrictthreshold; } else fiterrors=true; } //--- Check correctness of error reports for(pass=1; pass<=passcount; pass++) { //--- check if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!")) return; //--- solve simple task (all X[] are the same,Y[] are specially //--- calculated to ensure simple form of all types of errors) //--- and check correctness of the errors calculated by subroutines //--- First pass is done with zero Y[],other passes - with random Y[]. //--- It should test both ability to correctly calculate errors and //--- ability to not fail while working with zeros :) n=4; if(pass==1) { v1=0; v2=0; v=0; } else { v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); } //--- allocation ArrayResize(x,4); ArrayResize(y,4); ArrayResize(w,4); //--- change values x[0]=0; y[0]=v-v2; w[0]=1; x[1]=0; y[1]=v-v1; w[1]=1; x[2]=0; y[2]=v+v1; w[2]=1; x[3]=0; y[3]=v+v2; w[3]=1; refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; //--- check if(pass==1) refavgrel=0; else refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test penalized spline CIntComp::Spline1DFitPenalizedW(x,y,w,4,4,0.0,info,c,rep); //--- check if(info<=0) fiterrors=true; else { s=CSpline1D::Spline1DCalc(c,0); //--- search errors fiterrors=fiterrors||MathAbs(s-v)>threshold; fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold; } //--- Test cubic fitting CLSFit::Spline1DFitCubic(x,y,4,4,info,c,rep); //--- check if(info<=0) fiterrors=true; else { s=CSpline1D::Spline1DCalc(c,0); //--- search errors fiterrors=fiterrors||MathAbs(s-v)>threshold; fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold; } //--- Test Hermite fitting CLSFit::Spline1DFitHermite(x,y,4,4,info,c,rep); //--- check if(info<=0) fiterrors=true; else { s=CSpline1D::Spline1DCalc(c,0); //--- search errors fiterrors=fiterrors||MathAbs(s-v)>threshold; fiterrors=fiterrors||MathAbs(rep.m_rmserror-refrms)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgerror-refavg)>threshold; fiterrors=fiterrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; fiterrors=fiterrors||MathAbs(rep.m_maxerror-refmax)>threshold; } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestLSFitUnit::TestGeneralFitting(bool &llserrors,bool &nlserrors) { //--- create variables double threshold=0; double nlthreshold=0; int maxn=0; int maxm=0; int passcount=0; int n=0; int m=0; int i=0; int j=0; int k=0; int pass=0; double xscale=0; double diffstep=0; double v=0; double v1=0; double v2=0; int info=0; int info2=0; double refrms=0; double refavg=0; double refavgrel=0; double refmax=0; //--- create arrays double x[]; double y[]; double w[]; double w2[]; double c[]; double c2[]; //--- create matrix CMatrixDouble a; CMatrixDouble a2; CMatrixDouble cm; //--- objects of classes CLSFitReport rep; CLSFitReport rep2; CLSFitState state; //--- initialization llserrors=false; nlserrors=false; threshold=10000*CMath::m_machineepsilon; nlthreshold=0.00001; diffstep=0.0001; maxn=6; maxm=6; passcount=4; //--- Testing unconstrained least squares (linear/nonlinear) for(n=1; n<=maxn; n++) { for(m=1; m<=maxm; m++) { for(pass=1; pass<=passcount; pass++) { //--- Solve non-degenerate linear least squares task //--- Use Chebyshev basis. Its condition number is very good. a.Resize(n,m); ArrayResize(x,n); ArrayResize(y,n); ArrayResize(w,n); xscale=0.9+0.1*CMath::RandomReal(); for(i=0; i1) a.Set(i,1,x[i]); for(j=2; jthreshold; } //--- test non-weighted fitting ArrayResize(w2,n); for(i=0; ithreshold; //--- 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; i100*nlthreshold; } //--- change values for(i=0; i0.5,state); CLSFit::LSFitSetCond(state,nlthreshold,0); FitLinearNonlinear(m,1,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0; i100*nlthreshold; } //--- change values for(i=0; i100*nlthreshold; } //--- test gradient-only or Hessian-based fitting without weights CLSFit::LSFitLinear(y,a,n,m,info,c,rep); for(i=0; i100*nlthreshold; } //--- change values for(i=0; i0.5,state); CLSFit::LSFitSetCond(state,nlthreshold,0); FitLinearNonlinear(m,1,a,state,nlserrors); CLSFit::LSFitResults(state,info,c2,rep2); //--- check if(info<=0) nlserrors=true; else { //--- search errors for(i=0; i100*nlthreshold; } //--- change values for(i=0; i100*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; ithreshold; } //--- 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; k1) a.Set(i,1,x[i]); for(j=2; jthreshold; 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; i100*nlthreshold; //--- solve simple task (fitting by constant function) and check //--- correctness of the errors calculated by subroutines for(pass=1; pass<=passcount; pass++) { //--- test on task with non-zero Yi n=4; v1=CMath::RandomReal(); v2=CMath::RandomReal(); v=1+CMath::RandomReal(); //--- allocation ArrayResize(c,1); c[0]=1+2*CMath::RandomReal(); //--- allocation a.Resize(4,1); ArrayResize(y,4); //--- change values a.Set(0,0,1); y[0]=v-v2; a.Set(1,0,1); y[1]=v-v1; a.Set(2,0,1); y[2]=v+v1; a.Set(3,0,1); y[3]=v+v2; refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2); refavg=(MathAbs(v1)+MathAbs(v2))/2; refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2)); refmax=MathMax(v1,v2); //--- Test LLS CLSFit::LSFitLinear(y,a,4,1,info,c,rep); //--- check if(info<=0) llserrors=true; else { //--- search errors llserrors=llserrors||MathAbs(c[0]-v)>threshold; llserrors=llserrors||MathAbs(rep.m_rmserror-refrms)>threshold; llserrors=llserrors||MathAbs(rep.m_avgerror-refavg)>threshold; llserrors=llserrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; llserrors=llserrors||MathAbs(rep.m_maxerror-refmax)>threshold; } //--- Test NLS CLSFit::LSFitCreateFG(a,y,c,4,1,1,true,state); CLSFit::LSFitSetCond(state,nlthreshold,0); //--- cycle while(CLSFit::LSFitIteration(state)) { //--- check if(state.m_needf) state.m_f=state.m_c[0]; //--- check if(state.m_needfg) { state.m_f=state.m_c[0]; state.m_g.Set(0,1); } } //--- function call CLSFit::LSFitResults(state,info,c,rep); //--- check if(info<=0) nlserrors=true; else { //--- search errors nlserrors=nlserrors||MathAbs(c[0]-v)>threshold; nlserrors=nlserrors||MathAbs(rep.m_rmserror-refrms)>threshold; nlserrors=nlserrors||MathAbs(rep.m_avgerror-refavg)>threshold; nlserrors=nlserrors||MathAbs(rep.m_avgrelerror-refavgrel)>threshold; nlserrors=nlserrors||MathAbs(rep.m_maxerror-refmax)>threshold; } } } //+------------------------------------------------------------------+ //| Tests whether C is solution of (possibly) constrained LLS problem| //+------------------------------------------------------------------+ bool CTestLSFitUnit::IsGLSSolution(const int n,const int m,const int k, double &y[],double &w[],CMatrixDouble &fmatrix, CMatrixDouble &cmatrix,double &cc[]) { //--- create variables bool result; int i=0; int j=0; double v=0; double s1=0; double s2=0; double s3=0; double delta=0; double threshold=0; int i_=0; //--- create arrays double c[]; double c2[]; double sv[]; double deltac[]; double deltaproj[]; //--- create matrix CMatrixDouble u; CMatrixDouble vt; //--- copy array ArrayCopy(c,cc); //--- Setup. //--- Threshold is small because CMatrix may be ill-conditioned delta=0.001; threshold=MathSqrt(CMath::m_machineepsilon); //--- allocation ArrayResize(c2,m); ArrayResize(deltac,m); ArrayResize(deltaproj,m); //--- test whether C is feasible point or not (projC must be close to C) for(i=0; ithreshold) { //--- 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=(double)(s1/(1+threshold))) && s3>=(double)(s1/(1+threshold)); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests whether C is solution of LLS problem | //+------------------------------------------------------------------+ double CTestLSFitUnit::GetGLSError(const int n,const int m,double &y[], double &w[],CMatrixDouble &fmatrix,double &c[]) { //--- create variables double result=0; double v=0; //--- calculation for(int i=0; i=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; ithreshold; 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; ithreshold; 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_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; inonstrictthreshold; p3errors=p3errors||MathAbs(CPSpline::PSpline3ArcLength(p3,a,b)-(b-a)*MathSqrt(3)*(n-1))>nonstrictthreshold; } //--- report waserrors=p2errors||p3errors; //--- check if(!silent) { Print("TESTING SPLINE INTERPOLATION"); //--- Normal tests PrintResult("2D TEST",!p2errors); PrintResult("3D TEST",!p3errors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Unset spline, i.e. initialize it with random garbage | //+------------------------------------------------------------------+ void CTestPSplineUnit::UnsetP2(CPSpline2Interpolant &p) { //--- create matrix CMatrixDouble xy; //--- allocation xy.Resize(2,2); //--- initialization xy.Set(0,0,-1); xy.Set(0,1,-1); xy.Set(1,0,1); xy.Set(1,1,1); //--- function call CPSpline::PSpline2Build(xy,2,1,0,p); } //+------------------------------------------------------------------+ //| Unset spline, i.e. initialize it with random garbage | //+------------------------------------------------------------------+ void CTestPSplineUnit::UnsetP3(CPSpline3Interpolant &p) { //--- create matrix CMatrixDouble xy; //--- allocation xy.Resize(2,3); //--- initialization xy.Set(0,0,-1); xy.Set(0,1,-1); xy.Set(0,2,-1); xy.Set(1,0,1); xy.Set(1,1,1); xy.Set(1,2,1); //--- function call CPSpline::PSpline3Build(xy,2,1,0,p); } //+------------------------------------------------------------------+ //| Unsets real vector | //+------------------------------------------------------------------+ void CTestPSplineUnit::Unset1D(double &x[]) { //--- allocation ArrayResize(x,1); //--- change value x[0]=2*CMath::RandomReal()-1; } //+------------------------------------------------------------------+ //| Testing class CSpline2D | //+------------------------------------------------------------------+ class CTestSpline2DUnit { public: static bool TestSpline2D(const bool silent); static void LConst(CSpline2DInterpolant &c,double &lx[],double &ly[],const int m,const int n,const double lstep,double &lc,double &lcx,double &lcy,double &lcxy); static void TwodNumder(CSpline2DInterpolant &c,const double x,const double y,const double h,double &f,double &fx,double &fy,double &fxy); static bool TestUnpack(CSpline2DInterpolant &c,double &lx[],double &ly[]); static bool TestLinTrans(CSpline2DInterpolant &c,const double ax,const double bx,const double ay,const double by); static void UnsetSpline2D(CSpline2DInterpolant &c); }; //+------------------------------------------------------------------+ //| Testing class CSpline2D | //+------------------------------------------------------------------+ bool CTestSpline2DUnit::TestSpline2D(const bool silent) { //--- create variables bool waserrors; bool blerrors; bool bcerrors; bool dserrors; bool cperrors; bool uperrors; bool lterrors; bool syerrors; bool rlerrors; bool rcerrors; int pass=0; int passcount=0; int jobtype=0; double lstep=0; double h=0; double ax=0; double ay=0; double bx=0; double by=0; int i=0; int j=0; int k=0; int n=0; int m=0; int n2=0; int m2=0; double err=0; double t=0; double t1=0; double t2=0; double l1=0; double l1x=0; double l1y=0; double l1xy=0; double l2=0; double l2x=0; double l2y=0; double l2xy=0; double fm=0; double f1=0; double f2=0; double f3=0; double f4=0; double v1=0; double v1x=0; double v1y=0; double v1xy=0; double v2=0; double v2x=0; double v2y=0; double v2xy=0; double mf=0; //--- create arrays double x[]; double y[]; double lx[]; double ly[]; //--- create matrix CMatrixDouble f; CMatrixDouble fr; CMatrixDouble ft; //--- objects of classes CSpline2DInterpolant c; CSpline2DInterpolant c2; //--- initialization waserrors=false; passcount=10; h=0.00001; lstep=0.001; blerrors=false; bcerrors=false; dserrors=false; cperrors=false; uperrors=false; lterrors=false; syerrors=false; rlerrors=false; rcerrors=false; //--- Test: bilinear,bicubic for(n=2; n<=7; n++) { for(m=2; m<=7; m++) { //--- allocation ArrayResize(x,n); ArrayResize(y,m); ArrayResize(lx,2*n-2+1); ArrayResize(ly,2*m-2+1); f.Resize(m,n); //--- calculation ft.Resize(n,m); for(pass=1; pass<=passcount; pass++) { //--- Prepare task: //--- * X and Y stores grid //--- * F stores function values //--- * LX and LY stores twice dense grid (for Lipschitz testing) ax=-1-CMath::RandomReal(); bx=1+CMath::RandomReal(); ay=-1-CMath::RandomReal(); by=1+CMath::RandomReal(); for(j=0; j0) lx[2*j-1]=0.5*(x[j]+x[j-1]); } //--- swap for(j=0; j0) ly[2*i-1]=0.5*(y[i]+y[i-1]); } //--- swap for(i=0; i10000*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; i10000*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; i10000*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; j10000*CMath::m_machineepsilon; //--- check if(jobtype==1) rcerrors=rcerrors||err/mf>10000*CMath::m_machineepsilon; } } } } } } //--- report waserrors=(((((((blerrors||bcerrors)||dserrors)||cperrors)||uperrors)||lterrors)||syerrors)||rlerrors)||rcerrors; //--- check if(!silent) { Print("TESTING 2D INTERPOLATION"); //--- Normal tests PrintResult("BILINEAR TEST",!blerrors); PrintResult("BICUBIC TEST",!bcerrors); PrintResult("DIFFERENTIATION TEST",!dserrors); PrintResult("COPY/SERIALIZE TEST",!cperrors); PrintResult("UNPACK TEST",!uperrors); PrintResult("LIN.TRANS. TEST",!lterrors); PrintResult("SPECIAL SYMMETRY TEST",!syerrors); PrintResult("BILINEAR RESAMPLING TEST",!rlerrors); PrintResult("BICUBIC RESAMPLING TEST",!rcerrors); //--- Summary PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- end return(!waserrors); } //+------------------------------------------------------------------+ //| Lipschitz constants for spline inself,first and second | //| derivatives. | //+------------------------------------------------------------------+ void CTestSpline2DUnit::LConst(CSpline2DInterpolant &c,double &lx[], double &ly[],const int m,const int n, const double lstep,double &lc, double &lcx,double &lcy,double &lcxy) { //--- create variables int i=0; int j=0; double f1=0; double f2=0; double f3=0; double f4=0; double fx1=0; double fx2=0; double fx3=0; double fx4=0; double fy1=0; double fy2=0; double fy3=0; double fy4=0; double fxy1=0; double fxy2=0; double fxy3=0; double fxy4=0; double s2lstep=0; //--- initialization lc=0; lcx=0; lcy=0; lcxy=0; s2lstep=MathSqrt(2)*lstep; //--- calculation for(i=0; ithreshold||wfailed)||wnsorted; //--- check if(!silent) { Print("TESTING SYMMETRIC GEVD"); PrintFormat("Av-lambdav error (generalized): %5.3E",valerr); PrintResult("Eigen values order",!wnsorted); PrintFormat("Always converged: " + (!wfailed ? "YES" : "NO")); PrintFormat("Threshold: %5.3E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CInverseUpdate | //+------------------------------------------------------------------+ class CTestInverseUpdateUnit { public: static bool TestInverseUpdate(const bool silent); private: static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b); static void MatLU(CMatrixDouble &a,const int m,const int n,int &pivots[]); static void GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,const int n); static void GenerateRandomMatrixCond(CMatrixDouble &a0,const int n,const double c); static bool InvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular); static bool InvMatLU(CMatrixDouble &a,int &pivots[],const int n); static bool InvMat(CMatrixDouble &a,const int n); static double MatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n); static bool UpdAndInv(CMatrixDouble &a,double &u[],double &v[],const int n); }; //+------------------------------------------------------------------+ //| Testing class CInverseUpdate | //+------------------------------------------------------------------+ bool CTestInverseUpdateUnit::TestInverseUpdate(const bool silent) { //--- create variables int n=0; int maxn=0; int i=0; int updrow=0; int updcol=0; double val=0; int pass=0; int passcount=0; bool waserrors; double threshold=0; double c=0; //--- create arrays double u[]; double v[]; //--- create matrix CMatrixDouble a; CMatrixDouble inva; CMatrixDouble b1; CMatrixDouble b2; //--- initialization waserrors=false; maxn=10; passcount=100; threshold=1.0E-6; //--- process for(n=1; n<=maxn; n++) { //--- allocation a.Resize(n,n); b1.Resize(n,n); b2.Resize(n,n); ArrayResize(u,n); ArrayResize(v,n); //--- calculation for(pass=1; pass<=passcount; pass++) { c=MathExp(CMath::RandomReal()*MathLog(10)); GenerateRandomMatrixCond(a,n,c); MakeACopy(a,n,n,inva); //--- check if(!InvMat(inva,n)) { waserrors=true; break; } //--- Test simple update updrow=CMath::RandomInteger(n); updcol=CMath::RandomInteger(n); val=0.1*(2*CMath::RandomReal()-1); //--- change values for(i=0; ithreshold; //--- Test row update updrow=CMath::RandomInteger(n); for(i=0; ithreshold; //--- Test column update updcol=CMath::RandomInteger(n); for(i=0; ithreshold; //--- Test full update for(i=0; ithreshold; } } //--- report if(!silent) { Print("TESTING INVERSE UPDATE (REAL)"); //--- check PrintResult("TEST",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ void CTestInverseUpdateUnit::MakeACopy(CMatrixDouble &a,const int m, const int n,CMatrixDouble &b) { //--- allocation b.Resize(m,n); //--- copy for(int i=0; i=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; iMathAbs(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_1.0) continue; sm=MathSqrt(-(2*MathLog(sm)/sm)); v[i]=u1*sm; //--- check if(i+1<=s) v[i+1]=u2*sm; i=i+2; } //--- change value lambdav=0.0; for(i_=1; i_<=s; i_++) lambdav+=v[i_]*v[i_]; } while((double)(lambdav)==0.0); lambdav=2/lambdav; //--- A * (I - 2 vv'/v'v )= //--- =A - (2/v'v) * A * v * v'= //--- =A - (2/v'v) * w * v' //--- where w=Av for(i=1; i<=s; i++) { t=0.0; for(i_=1; i_<=s; i_++) t+=a.Get(i,i_)*v[i_]; w[i]=t; } //--- calculation for(i=1; i<=s; i++) { t=w[i]*lambdav; for(i_=1; i_<=s; i_++) a.Set(i,i_,a.Get(i,i_)-t*v[i_]); } } //--- copy for(i=1; i<=n; i++) { for(j=1; j<=n; j++) a0.Set(i-1,j-1,a.Get(i,j)); } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestInverseUpdateUnit::GenerateRandomMatrixCond(CMatrixDouble &a0, const int n, const double c) { //--- create variables double l1=0; double l2=0; int i=0; int j=0; int k=0; //--- create array double cc[]; //--- create matrix CMatrixDouble q1; CMatrixDouble q2; //--- function calls GenerateRandomOrthogonalMatrix(q1,n); GenerateRandomOrthogonalMatrix(q2,n); //--- allocation ArrayResize(cc,n); //--- change values l1=0; l2=MathLog(1/c); cc[0]=MathExp(l1); for(i=1; i<=n-2; i++) cc[i]=MathExp(CMath::RandomReal()*(l2-l1)+l1); cc[n-1]=MathExp(l2); //--- allocation a0.Resize(n,n); //--- calculation for(i=0; i0) { for(i_=0; i_=0; j--) { //--- check if(nounit) { //--- check if(a[j][j]==0.0) { //--- return result return(false); } //--- change values a.Set(j,j,1/a[j][j]); ajj=-a[j][j]; } else ajj=-1; //--- check if(jj+1) { //--- change value v=0.0; for(i_=j+1; i_=0; j--) { //--- Copy current column of L to WORK and replace with zeros. for(i=j+1; i=0; j--) { jp=pivots[j]; //--- check if(jp!=j) { //--- copy for(i_=0; i_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; i30 && n%3!=0) continue; //--- function calls FillsParseA(a,n,0.8); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); FillsParseA(a,n,0.9); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); FillsParseA(a,n,0.95); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); FillsParseA(a,n,0.997); TestSchurProblem(a,n,materr,orterr,errstruct,wfailed); } } //--- report waserrors=((materr>threshold||orterr>threshold)||errstruct)||wfailed; //--- check if(!silent) { Print("TESTING SCHUR DECOMPOSITION"); PrintFormat("Schur decomposition error: %5.3E",materr); PrintFormat("Schur orthogonality error: %5.3E",orterr); PrintResult("T matrix structure",!errstruct); PrintFormat("Always converged: %s",(!wfailed ? "OK" : "FAILED")); PrintFormat("Threshold: %5.3E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestSchurUnit::FillsParseA(CMatrixDouble &a,const int n, const double sparcity) { //--- change values for(int i=0; i=sparcity) a.Set(i,j,2*CMath::RandomReal()-1); else a.Set(i,j,0); } } } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestSchurUnit::TestSchurProblem(CMatrixDouble &a,const int n, double &materr,double &orterr, bool &errstruct,bool &wfailed) { //--- create variables int i=0; int j=0; int k=0; double v=0; double locerr=0; int i_=0; //--- create arrays double sr[]; double astc[]; double sastc[]; //--- create matrix CMatrixDouble s; CMatrixDouble t; //--- allocation ArrayResize(sr,n); ArrayResize(astc,n); ArrayResize(sastc,n); //--- Schur decomposition,convergence test t.Resize(n,n); for(i=0; i0) 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; i0) 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; iflast; //--- 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_0) { othererrors=(othererrors||xlast[0]!=x[0])||xlast[1]!=x[1]; v=CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7); othererrors=othererrors||MathAbs(flast-v)/MathMax(v,1)>100*CMath::m_machineepsilon; } else converror=true; //--- search errors othererrors=othererrors||rep.m_nfunc!=nfunc; othererrors=othererrors||rep.m_njac!=njac; othererrors=othererrors||rep.m_iterationscount!=itcnt-1; //--- Test ability to set limit on algorithm steps ArrayResize(x,2); ArrayResize(xlast,2); //--- change values x[0]=20*CMath::RandomReal()+20; x[1]=20*CMath::RandomReal()+20; xlast[0]=x[0]; xlast[1]=x[1]; stpmax=0.1+0.1*CMath::RandomReal(); epsf=1.0E-9; //--- function calls CNlEq::NlEqCreateLM(2,3,x,state); CNlEq::NlEqSetStpMax(state,stpmax); CNlEq::NlEqSetCond(state,epsf,0); CNlEq::NlEqSetXRep(state,true); //--- cycle while(CNlEq::NlEqIteration(state)) { //--- check if(state.m_needf || state.m_needfij) TestFuncHBM(state); //--- check if((state.m_needf || state.m_needfij) || state.m_xupdated) othererrors=othererrors||MathSqrt(CMath::Sqr(state.m_x[0]-xlast[0])+CMath::Sqr(state.m_x[1]-xlast[1]))>1.00001*stpmax; //--- check if(state.m_xupdated) { xlast[0]=state.m_x[0]; xlast[1]=state.m_x[1]; } } //--- end waserrors=(basicserrors||converror)||othererrors; //--- check if(!silent) { Print("TESTING NLEQ SOLVER"); PrintResult("BASIC FUNCTIONALITY",!basicserrors); PrintResult("CONVERGENCE",!converror); PrintResult("OTHER PROPERTIES",!othererrors); //--- check PrintResult("TEST SUMMARY",!waserrors); Print(""); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Himmelblau's function | //| F=(x^2+y-11)^2 + (x+y^2-7)^2 | //| posed as system of M functions: | //| f0=x^2+y-11 | //| f1=x+y^2-7 | //+------------------------------------------------------------------+ void CTestNlEqUnit::TestFuncHBM(CNlEqState &state) { //--- create variables double x=0; double y=0; //--- check if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!")) return; //--- change values x=state.m_x[0]; y=state.m_x[1]; //--- check if(state.m_needf) { state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7); return; } //--- check if(state.m_needfij) { state.m_fi[0]=x*x+y-11; state.m_fi[1]=x+y*y-7; state.m_j.Set(0,0,2*x); state.m_j.Set(0,1,1); state.m_j.Set(1,0,1); state.m_j.Set(1,1,2*y); //--- exit the function return; } } //+------------------------------------------------------------------+ //| Himmelblau's function | //| F=(x^2+y-11)^2 + (x+y^2-7)^2 | //| posed as system of 1 function | //+------------------------------------------------------------------+ void CTestNlEqUnit::TestFuncHB1(CNlEqState &state) { //--- create variables double x=0; double y=0; //--- check if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!")) return; //--- change values x=state.m_x[0]; y=state.m_x[1]; //--- check if(state.m_needf) { state.m_f=CMath::Sqr(CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)); return; } //--- check if(state.m_needfij) { state.m_fi[0]=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7); state.m_j.Set(0,0,2*(x*x+y-11)*2*x+2*(x+y*y-7)); state.m_j.Set(0,1,2*(x*x+y-11)+2*(x+y*y-7)*2*y); //--- exit the function return; } } //+------------------------------------------------------------------+ //| Shifted Himmelblau's function | //| F=(x^2+y-11)^2 + (x+y^2-7)^2 + 1 | //| posed as system of M functions: | //| f0=x^2+y-11 | //| f1=x+y^2-7 | //| f2=1 | //| This function is used to test algorithm on problem which has no | //| solution. | //+------------------------------------------------------------------+ void CTestNlEqUnit::TestFuncSHBM(CNlEqState &state) { //--- create variables double x=0; double y=0; //--- check if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!")) return; //--- change values x=state.m_x[0]; y=state.m_x[1]; //--- check if(state.m_needf) { state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)+1; return; } //--- check if(state.m_needfij) { state.m_fi[0]=x*x+y-11; state.m_fi[1]=x+y*y-7; state.m_fi[2]=1; state.m_j.Set(0,0,2*x); state.m_j.Set(0,1,1); state.m_j.Set(1,0,1); state.m_j.Set(1,1,2*y); state.m_j.Set(2,0,0); state.m_j.Set(2,1,0); //--- exit the function return; } } //+------------------------------------------------------------------+ //| Testing class CChebyshev | //+------------------------------------------------------------------+ class CTestChebyshevUnit { public: static bool TestChebyshev(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CChebyshev | //+------------------------------------------------------------------+ bool CTestChebyshevUnit::TestChebyshev(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double ferr=0; double threshold=0; double x=0; double v=0; int pass=0; int i=0; int j=0; int k=0; int n=0; int maxn=0; bool waserrors; int i_=0; //--- create arrays double c[]; double p1[]; double p2[]; //--- create matrix CMatrixDouble a; //--- initialization err=0; sumerr=0; cerr=0; ferr=0; threshold=1.0E-9; waserrors=false; //--- Testing Chebyshev polynomials of the first kind err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.00)-1)); err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.33)-1)); err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,-0.42)-1)); x=0.2; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.2)); x=0.4; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.4)); x=0.6; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.6)); x=0.8; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.8)); x=1.0; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-1.0)); x=0.2; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.92)); x=0.4; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.68)); x=0.6; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.28)); x=0.8; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-0.28)); x=1.0; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-1.00)); n=10; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)-0.4284556288)); n=11; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7996160205)); n=12; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7483020370)); //--- Testing Chebyshev polynomials of the second kind n=0; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-1.0000000000)); n=1; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.4000000000)); n=2; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8400000000)); n=3; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.7360000000)); n=4; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.5456000000)); n=10; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.6128946176)); n=11; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.6770370970)); n=12; err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8837094564)); //--- Testing Clenshaw summation maxn=20; ArrayResize(c,maxn+1); for(k=1; k<=2; k++) { for(pass=1; pass<=10; pass++) { x=2*CMath::RandomReal()-1; v=0; //--- calculation for(n=0; n<=maxn; n++) { c[n]=2*CMath::RandomReal()-1; v+=CChebyshev::ChebyshevCalculate(k,n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CChebyshev::ChebyshevSum(c,k,n,x))); } } } //--- Testing coefficients CChebyshev::ChebyshevCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- function call CChebyshev::ChebyshevCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-1)); //--- function call CChebyshev::ChebyshevCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+1)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-2)); //--- function call CChebyshev::ChebyshevCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]+3)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]-4)); //--- function call CChebyshev::ChebyshevCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]+8)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]-8)); //--- function call CChebyshev::ChebyshevCoefficients(9,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-9)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]+120)); cerr=MathMax(cerr,MathAbs(c[4]-0)); cerr=MathMax(cerr,MathAbs(c[5]-432)); cerr=MathMax(cerr,MathAbs(c[6]-0)); cerr=MathMax(cerr,MathAbs(c[7]+576)); cerr=MathMax(cerr,MathAbs(c[8]-0)); cerr=MathMax(cerr,MathAbs(c[9]-256)); //--- Testing FromChebyshev maxn=10; a.Resize(maxn+1,maxn+1); for(i=0; i<=maxn; i++) { for(j=0; j<=maxn; j++) a.Set(i,j,0); //--- function call CChebyshev::ChebyshevCoefficients(i,c); for(i_=0; i_<=i; i_++) a.Set(i,i_,c[i_]); } //--- allocation ArrayResize(c,maxn+1); ArrayResize(p1,maxn+1); //--- calculation for(n=0; n<=maxn; n++) { for(pass=1; pass<=10; pass++) { for(i=0; i<=n; i++) p1[i]=0; for(i=0; i<=n; i++) { //--- change values c[i]=2*CMath::RandomReal()-1; v=c[i]; for(i_=0; i_<=i; i_++) p1[i_]=p1[i_]+v*a.Get(i,i_); } //--- function call CChebyshev::FromChebyshev(c,n,p2); for(i=0; i<=n; i++) ferr=MathMax(ferr,MathAbs(p1[i]-p2[i])); } } //--- Reporting waserrors=((err>threshold||sumerr>threshold)||cerr>threshold)||ferr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS"); PrintFormat("Max error against table %5.2E",err); PrintFormat("Summation error %5.2E",sumerr); PrintFormat("Coefficients error %5.2E",cerr); PrintFormat("FrobChebyshev error %5.2E",ferr); PrintFormat("Threshold %5.2E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CHermite | //+------------------------------------------------------------------+ class CTestHermiteUnit { public: static bool TestHermite(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CHermite | //+------------------------------------------------------------------+ bool CTestHermiteUnit::TestHermite(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double threshold=1.0E-9; int n=0; int maxn=0; int pass=0; double x=0; double v=0; bool waserrors=false; //--- create array double c[]; //--- Testing Hermite polynomials n=0; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-1)); n=1; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2)); n=2; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2)); n=3; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+4)); n=4; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+20)); n=5; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+8)); n=6; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-184)); n=7; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-464)); n=11; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-230848)); n=12; err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-280768)); //--- Testing Clenshaw summation maxn=10; ArrayResize(c,maxn+1); for(pass=1; pass<=10; pass++) { x=2*CMath::RandomReal()-1; v=0; //--- calculation for(n=0; n<=maxn; n++) { c[n]=2*CMath::RandomReal()-1; v+=CHermite::HermiteCalculate(n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CHermite::HermiteSum(c,n,x))); } } //--- Testing coefficients CHermite::HermiteCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- function call CHermite::HermiteCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-2)); //--- function call CHermite::HermiteCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+2)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-4)); //--- function call CHermite::HermiteCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]+12)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]-8)); //--- function call CHermite::HermiteCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-12)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]+48)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]-16)); //--- function call CHermite::HermiteCoefficients(5,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-120)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]+160)); cerr=MathMax(cerr,MathAbs(c[4]-0)); cerr=MathMax(cerr,MathAbs(c[5]-32)); //--- function call CHermite::HermiteCoefficients(6,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+120)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-720)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]+480)); cerr=MathMax(cerr,MathAbs(c[5]-0)); cerr=MathMax(cerr,MathAbs(c[6]-64)); //--- Reporting waserrors=(err>threshold||sumerr>threshold)||cerr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE HERMITE POLYNOMIALS"); PrintFormat("Max error %5.2E}",err); PrintFormat("Summation error %5.2E",sumerr); PrintFormat("Coefficients error %5.2E",cerr); PrintFormat("Threshold %5.2E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CLaguerre | //+------------------------------------------------------------------+ class CTestLaguerreUnit { public: static bool TestLaguerre(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CLaguerre | //+------------------------------------------------------------------+ bool CTestLaguerreUnit::TestLaguerre(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double threshold=1.0E-9; int n=0; int maxn=0; int pass=0; double x=0; double v=0; bool waserrors=false; //--- create array double c[]; //--- Testing Laguerre polynomials n=0; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-1.0000000000)); n=1; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.5000000000)); n=2; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.1250000000)); n=3; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.1458333333)); n=4; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3307291667)); n=5; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4455729167)); n=6; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5041449653)); n=7; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5183392237)); n=8; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4983629984)); n=9; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4529195204)); n=10; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3893744141)); n=11; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3139072988)); n=12; err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.2316496389)); //--- Testing Clenshaw summation maxn=20; ArrayResize(c,maxn+1); for(pass=1; pass<=10; pass++) { x=2*CMath::RandomReal()-1; v=0; //--- calculation for(n=0; n<=maxn; n++) { c[n]=2*CMath::RandomReal()-1; v+=CLaguerre::LaguerreCalculate(n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CLaguerre::LaguerreSum(c,n,x))); } } //--- Testing coefficients CLaguerre::LaguerreCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- function call CLaguerre::LaguerreCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); cerr=MathMax(cerr,MathAbs(c[1]+1)); //--- function call CLaguerre::LaguerreCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-2.0/2.0)); cerr=MathMax(cerr,MathAbs(c[1]+4.0/2.0)); cerr=MathMax(cerr,MathAbs(c[2]-1.0/2.0)); //--- function call CLaguerre::LaguerreCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-6.0/6.0)); cerr=MathMax(cerr,MathAbs(c[1]+18.0/6.0)); cerr=MathMax(cerr,MathAbs(c[2]-9.0/6.0)); cerr=MathMax(cerr,MathAbs(c[3]+1.0/6.0)); //--- function call CLaguerre::LaguerreCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-24.0/24.0)); cerr=MathMax(cerr,MathAbs(c[1]+96.0/24.0)); cerr=MathMax(cerr,MathAbs(c[2]-72.0/24.0)); cerr=MathMax(cerr,MathAbs(c[3]+16.0/24.0)); cerr=MathMax(cerr,MathAbs(c[4]-1.0/24.0)); //--- function call CLaguerre::LaguerreCoefficients(5,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-120.0/120.0)); cerr=MathMax(cerr,MathAbs(c[1]+600.0/120.0)); cerr=MathMax(cerr,MathAbs(c[2]-600.0/120.0)); cerr=MathMax(cerr,MathAbs(c[3]+200.0/120.0)); cerr=MathMax(cerr,MathAbs(c[4]-25.0/120.0)); cerr=MathMax(cerr,MathAbs(c[5]+1.0/120.0)); //--- function call CLaguerre::LaguerreCoefficients(6,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-720.0/720.0)); cerr=MathMax(cerr,MathAbs(c[1]+4320.0/720.0)); cerr=MathMax(cerr,MathAbs(c[2]-5400.0/720.0)); cerr=MathMax(cerr,MathAbs(c[3]+2400.0/720.0)); cerr=MathMax(cerr,MathAbs(c[4]-450.0/720.0)); cerr=MathMax(cerr,MathAbs(c[5]+36.0/720.0)); cerr=MathMax(cerr,MathAbs(c[6]-1.0/720.0)); //--- Reporting waserrors=(err>threshold||sumerr>threshold)||cerr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS"); PrintFormat("Max error %5.2E",err); PrintFormat("Summation error %5.2E",sumerr); PrintFormat("Coefficients error %5.2E",cerr); PrintFormat("Threshold %5.2E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| Testing class CLegendre | //+------------------------------------------------------------------+ class CTestLegendreUnit { public: static bool TestLegendre(const bool silent); }; //+------------------------------------------------------------------+ //| Testing class CLegendre | //+------------------------------------------------------------------+ bool CTestLegendreUnit::TestLegendre(const bool silent) { //--- create variables double err=0; double sumerr=0; double cerr=0; double threshold=1.0E-9; int n=0; int maxn=0; int i=0; int pass=0; double x=0; double v=0; double t=0; bool waserrors=false; //--- create array double c[]; //--- Testing Legendre polynomials values for(n=0; n<=10; n++) { //--- function call CLegendre::LegendreCoefficients(n,c); for(pass=1; pass<=10; pass++) { //--- calculation x=2*CMath::RandomReal()-1; v=CLegendre::LegendreCalculate(n,x); t=1; for(i=0; i<=n; i++) { v=v-c[i]*t; t=t*x; } //--- search errors err=MathMax(err,MathAbs(v)); } } //--- Testing Clenshaw summation maxn=20; ArrayResize(c,maxn+1); for(pass=1; pass<=10; pass++) { //--- change values x=2*CMath::RandomReal()-1; v=0; for(n=0; n<=maxn; n++) { c[n]=2*CMath::RandomReal()-1; v+=CLegendre::LegendreCalculate(n,x)*c[n]; //--- search errors sumerr=MathMax(sumerr,MathAbs(v-CLegendre::LegendreSum(c,n,x))); } } //--- Testing coefficients CLegendre::LegendreCoefficients(0,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-1)); //--- calculation CLegendre::LegendreCoefficients(1,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-1)); //--- calculation CLegendre::LegendreCoefficients(2,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]+1.0/2.0)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]-3.0/2.0)); //--- calculation CLegendre::LegendreCoefficients(3,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]+3.0/2.0)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]-5.0/2.0)); //--- calculation CLegendre::LegendreCoefficients(4,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-3.0/8.0)); cerr=MathMax(cerr,MathAbs(c[1]-0)); cerr=MathMax(cerr,MathAbs(c[2]+30.0/8.0)); cerr=MathMax(cerr,MathAbs(c[3]-0)); cerr=MathMax(cerr,MathAbs(c[4]-35.0/8.0)); //--- calculation CLegendre::LegendreCoefficients(9,c); //--- search errors cerr=MathMax(cerr,MathAbs(c[0]-0)); cerr=MathMax(cerr,MathAbs(c[1]-315.0/128.0)); cerr=MathMax(cerr,MathAbs(c[2]-0)); cerr=MathMax(cerr,MathAbs(c[3]+4620.0/128.0)); cerr=MathMax(cerr,MathAbs(c[4]-0)); cerr=MathMax(cerr,MathAbs(c[5]-18018.0/128.0)); cerr=MathMax(cerr,MathAbs(c[6]-0)); cerr=MathMax(cerr,MathAbs(c[7]+25740.0/128.0)); cerr=MathMax(cerr,MathAbs(c[8]-0)); cerr=MathMax(cerr,MathAbs(c[9]-12155.0/128.0)); //--- Reporting waserrors=(err>threshold||sumerr>threshold)||cerr>threshold; //--- check if(!silent) { Print("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS"); PrintFormat("Max error %5.2E",err); PrintFormat("Summation error %5.2E",sumerr); PrintFormat("Coefficients error %5.2E",cerr); PrintFormat("Threshold %5.2E",threshold); //--- check PrintResult("TEST SUMMARY",!waserrors); } //--- return result return(!waserrors); } //+------------------------------------------------------------------+ //| The auxiliary class | //+------------------------------------------------------------------+ class CRec1 { public: //--- class variables bool m_bfield; double m_rfield; int m_ifield; complex m_cfield; //--- arrays bool m_b1field[]; double m_r1field[]; int m_i1field[]; complex m_c1field[]; //--- matrix CMatrixInt m_b2field; CMatrixDouble m_r2field; CMatrixInt m_i2field; CMatrixComplex m_c2field; //--- constructor, destructor CRec1(void) {} ~CRec1(void) {} }; //+------------------------------------------------------------------+ //| The auxiliary class | //+------------------------------------------------------------------+ class CRec4Serialization { public: //--- arrays bool m_b[]; int m_i[]; double m_r[]; //--- constructor, destructor CRec4Serialization(void) {} ~CRec4Serialization(void) {} }; //+------------------------------------------------------------------+ //| Testing the basic functions | //+------------------------------------------------------------------+ class CTestAlglibBasicsUnit { public: static void Rec4SerializationAlloc(CSerializer &s,CRec4Serialization &v); static void Rec4SerializationSerialize(CSerializer &s,CRec4Serialization &v); static void Rec4SerializationUnserialize(CSerializer &s,CRec4Serialization &v); static bool TestAlglibBasics(const bool silent); private: static bool TestComplexArithmetics(const bool silent); static bool TestIEEESpecial(const bool silent); static bool TestSwapFunctions(const bool silent); static bool TestSerializationFunctions(const bool silent); }; //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestAlglibBasicsUnit::Rec4SerializationAlloc(CSerializer &s, CRec4Serialization &v) { //--- create a variable int i=0; //--- boolean fields s.Alloc_Entry(); for(i=0; i<=CAp::Len(v.m_b)-1; i++) s.Alloc_Entry(); //--- integer fields s.Alloc_Entry(); for(i=0; i<=CAp::Len(v.m_i)-1; i++) s.Alloc_Entry(); //--- real fields s.Alloc_Entry(); for(i=0; i<=CAp::Len(v.m_r)-1; i++) s.Alloc_Entry(); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestAlglibBasicsUnit::Rec4SerializationSerialize(CSerializer &s, CRec4Serialization &v) { //--- create a variable int i=0; //--- boolean fields s.Serialize_Int(CAp::Len(v.m_b)); for(i=0; i<=CAp::Len(v.m_b)-1; i++) s.Serialize_Bool(v.m_b[i]); //--- integer fields s.Serialize_Int(CAp::Len(v.m_i)); for(i=0; i<=CAp::Len(v.m_i)-1; i++) s.Serialize_Int(v.m_i[i]); //--- real fields s.Serialize_Int(CAp::Len(v.m_r)); for(i=0; i<=CAp::Len(v.m_r)-1; i++) s.Serialize_Double(v.m_r[i]); } //+------------------------------------------------------------------+ //| The auxiliary function | //+------------------------------------------------------------------+ void CTestAlglibBasicsUnit::Rec4SerializationUnserialize(CSerializer &s, CRec4Serialization &v) { //--- create variables int i=0; int k=0; bool bv; int iv=0; double rv=0; //--- boolean fields k=s.Unserialize_Int(); //--- check if(k>0) { //--- allocation ArrayResize(v.m_b,k); for(i=0; i0) { //--- allocation ArrayResize(v.m_i,k); for(i=0; i0) { //--- allocation ArrayResize(v.m_r,k); for(i=0; iCInfOrNaN::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) { PrintResult("IEEE SPECIAL VALUES",true); } else { PrintResult("IEEE SPECIAL VALUES",false); PrintResult("* NAN",oknan); PrintResult("* INF",okinf); PrintResult("* FUNCTIONS",okother); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests for swapping functions | //+------------------------------------------------------------------+ bool CTestAlglibBasicsUnit::TestSwapFunctions(const bool silent) { //--- create variables bool result; bool okb1; bool okb2; bool oki1; bool oki2; bool okr1; bool okr2; bool okc1; bool okc2; //--- create arrays bool b11[]; bool b12[]; int i11[]; int i12[]; double r11[]; double r12[]; complex c11[]; complex c12[]; //--- create matrix CMatrixInt b21; CMatrixInt b22; CMatrixInt i21; CMatrixInt i22; CMatrixDouble r21; CMatrixDouble r22; CMatrixComplex c21; CMatrixComplex c22; //--- initialization result=true; okb1=true; okb2=true; oki1=true; oki2=true; okr1=true; okr2=true; okc1=true; okc2=true; //--- Test B1 swaps ArrayResize(b11,1); ArrayResize(b12,2); //--- change values b11[0]=true; b12[0]=false; b12[1]=true; //--- function call CAp::Swap(b11,b12); //--- check if(CAp::Len(b11)==2 && CAp::Len(b12)==1) { okb1=okb1 && !b11[0]; okb1=okb1 && b11[1]; okb1=okb1 && b12[0]; } else okb1=false; //--- Test I1 swaps ArrayResize(i11,1); ArrayResize(i12,2); //--- change values i11[0]=1; i12[0]=2; i12[1]=3; //--- function call CAp::Swap(i11,i12); //--- check if(CAp::Len(i11)==2 && CAp::Len(i12)==1) { oki1=oki1 && i11[0]==2; oki1=oki1 && i11[1]==3; oki1=oki1 && i12[0]==1; } else oki1=false; //--- Test R1 swaps ArrayResize(r11,1); ArrayResize(r12,2); //--- change values r11[0]=1.5; r12[0]=2.5; r12[1]=3.5; //--- function call CAp::Swap(r11,r12); //--- check if(CAp::Len(r11)==2 && CAp::Len(r12)==1) { okr1=okr1 && r11[0]==2.5; okr1=okr1 && r11[1]==3.5; okr1=okr1 && r12[0]==1.5; } else okr1=false; //--- Test C1 swaps ArrayResize(c11,1); ArrayResize(c12,2); //--- change values c11[0]=1; c12[0]=2; c12[1]=3; //--- function call CAp::Swap(c11,c12); //--- check if(CAp::Len(c11)==2 && CAp::Len(c12)==1) { okc1=okc1 && c11[0]==2; okc1=okc1 && c11[1]==3; okc1=okc1 && c12[0]==1; } else okc1=false; //--- Test B2 swaps b21.Resize(1,2); b22.Resize(2,1); //--- change values b21.Set(0,0,true); b21.Set(0,1,false); b22.Set(0,0,false); b22.Set(1,0,true); //--- function call CAp::Swap(b21,b22); //--- check if(((CAp::Rows(b21)==2 && CAp::Cols(b21)==1) && CAp::Rows(b22)==1) && CAp::Cols(b22)==2) { okb2=okb2 && !b21[0][0]; okb2=okb2 && b21[1][0]; okb2=okb2 && b22[0][0]; okb2=okb2 && !b22[0][1]; } else okb2=false; //--- Test I2 swaps i21.Resize(1,2); i22.Resize(2,1); //--- change values i21.Set(0,0,1); i21.Set(0,1,2); i22.Set(0,0,3); i22.Set(1,0,4); //--- function call CAp::Swap(i21,i22); //--- check if(((CAp::Rows(i21)==2 && CAp::Cols(i21)==1) && CAp::Rows(i22)==1) && CAp::Cols(i22)==2) { oki2=oki2 && i21[0][0]==3; oki2=oki2 && i21[1][0]==4; oki2=oki2 && i22[0][0]==1; oki2=oki2 && i22[0][1]==2; } else oki2=false; //--- Test R2 swaps r21.Resize(1,2); r22.Resize(2,1); //--- change values r21.Set(0,0,1); r21.Set(0,1,2); r22.Set(0,0,3); r22.Set(1,0,4); //--- function call CAp::Swap(r21,r22); //--- check if(((CAp::Rows(r21)==2 && CAp::Cols(r21)==1) && CAp::Rows(r22)==1) && CAp::Cols(r22)==2) { okr2=okr2 && r21[0][0]==3.0; okr2=okr2 && r21[1][0]==4.0; okr2=okr2 && r22[0][0]==1.0; okr2=okr2 && r22[0][1]==2.0; } else okr2=false; //--- Test C2 swaps c21.Resize(1,2); c22.Resize(2,1); //--- change values c21.Set(0,0,1); c21.Set(0,1,2); c22.Set(0,0,3); c22.Set(1,0,4); //--- function call CAp::Swap(c21,c22); //--- check if(((CAp::Rows(c21)==2 && CAp::Cols(c21)==1) && CAp::Rows(c22)==1) && CAp::Cols(c22)==2) { okc2=okc2 && c21[0][0]==3; okc2=okc2 && c21[1][0]==4; okc2=okc2 && c22[0][0]==1; okc2=okc2 && c22[0][1]==2; } else okc2=false; //--- summary result=result && okb1; result=result && okb2; result=result && oki1; result=result && oki2; result=result && okr1; result=result && okr2; result=result && okc1; result=result && okc2; //--- check if(!silent) { //--- check PrintResult("SWAPPING FUNCTIONS",result); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Tests for swapping functions | //+------------------------------------------------------------------+ bool CTestAlglibBasicsUnit::TestSerializationFunctions(const bool silent) { //--- create variables bool result; bool okb; bool oki; bool okr; int nb=0; int ni=0; int nr=0; int i=0; //--- objects of classes CRec4Serialization r0; CRec4Serialization r1; //--- initialization result=true; okb=true; oki=true; okr=true; //--- calculation for(nb=1; nb<=4; nb++) { for(ni=1; ni<=4; ni++) { for(nr=1; nr<=4; nr++) { //--- allocation ArrayResize(r0.m_b,nb); for(i=0; i<=nb-1; i++) r0.m_b[i]=CMath::RandomInteger(2)!=0; //--- allocation ArrayResize(r0.m_i,ni); for(i=0; i<=ni-1; i++) r0.m_i[i]=CMath::RandomInteger(10)-5; //--- allocation ArrayResize(r0.m_r,nr); for(i=0; i<=nr-1; i++) r0.m_r[i]=2*CMath::RandomReal()-1; { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; //--- serialization _local_serializer.Reset(); _local_serializer.Alloc_Start(); CTestAlglibBasicsUnit::Rec4SerializationAlloc(_local_serializer,r0); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CTestAlglibBasicsUnit::Rec4SerializationSerialize(_local_serializer,r0); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- unserialization _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CTestAlglibBasicsUnit::Rec4SerializationUnserialize(_local_serializer,r1); _local_serializer.Stop(); } //--- check if((CAp::Len(r0.m_b)==CAp::Len(r1.m_b) && CAp::Len(r0.m_i)==CAp::Len(r1.m_i)) && CAp::Len(r0.m_r)==CAp::Len(r1.m_r)) { //--- change value for(i=0; i<=nb-1; i++) okb=okb && ((r0.m_b[i] && r1.m_b[i])||(!r0.m_b[i] && !r1.m_b[i])); for(i=0; i<=ni-1; i++) oki=oki && r0.m_i[i]==r1.m_i[i]; for(i=0; i<=nr-1; i++) okr=okr && r0.m_r[i]==r1.m_r[i]; } else oki=false; } } } //--- summary result=result && okb; result=result && oki; result=result && okr; //--- check if(!silent) { //--- check if(result) { PrintResult("SERIALIZATION FUNCTIONS",true); } else { PrintResult("SERIALIZATION FUNCTIONS",false); PrintResult("* BOOLEAN ",okb); PrintResult("* INTEGER ",oki); PrintResult("* REAL ",okr); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Test sparse matrix | //+------------------------------------------------------------------+ class CSparseGenerator { public: int m_m; int m_matkind; int m_n; int m_triangle; RCommState m_rcs; CMatrixDouble m_bufa; CHighQualityRandState m_rs; CSparseGenerator(void) {} ~CSparseGenerator(void) {} void Copy(const CSparseGenerator &obj); //--- overloading void operator=(const CSparseGenerator &obj) { Copy(obj); } }; //+------------------------------------------------------------------+ //| Copy | //+------------------------------------------------------------------+ void CSparseGenerator::Copy(const CSparseGenerator &obj) { m_m=obj.m_m; m_matkind=obj.m_matkind; m_n=obj.m_n; m_triangle=obj.m_triangle; m_rcs=obj.m_rcs; m_bufa=obj.m_bufa; m_rs=obj.m_rs; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestSparseUnit { public: static const int m_maxtype; static bool TestSparse(bool silent); static bool SKSTest(void); static void CRSTest(bool errorflag); static void TestSerialize(bool errorflag); static bool BasicFuncTest(void); static bool TestLevel2Unsymmetric(void); static bool TestLevel2Triangular(void); static bool TestLevel3Unsymmetric(void); static bool TestLevel2Symmetric(void); static bool TestLevel3Symmetric(void); static bool TestSymmetricPerm(void); static bool BasicFuncRandomTest(void); static bool LinearFunctionsTest(void); static bool LinearFunctionSSTest(void); static bool LinearFunctionSMMTest(void); static bool LinearFunctionsSMMTest(void); static bool BasicCopyFuncTest(bool silent); static bool CopyFuncTest(bool silent); private: static void InitGenerator(int m,int n,int matkind,int triangle,CSparseGenerator &g); static bool GenerateNext(CSparseGenerator &g,CMatrixDouble &da,CSparseMatrix &sa); static void CreateRandom(int m,int n,int pkind,int ckind,int p0,int p1,CMatrixDouble &da,CSparseMatrix &sa); static bool EnumerateTest(void); static bool RewriteExistingTest(void); static void TestGetRow(bool &err); static bool TestConvertSM(void); static bool TestGCMatrixType(void); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ const int CTestSparseUnit::m_maxtype=2; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestSparseUnit::TestSparse(bool silent) { bool result=false; bool waserrors=false; bool basicerrors=false; bool linearerrors=false; bool basicrnderrors=false; bool level2unsymmetricerrors=false; bool level2symmetricerrors=false; bool level2triangularerrors=false; bool level3unsymmetricerrors=false; bool level3symmetricerrors=false; bool symmetricpermerrors=false; bool linearserrors=false; bool linearmmerrors=false; bool linearsmmerrors=false; bool getrowerrors=false; bool serializeerrors=false; bool copyerrors=false; bool basiccopyerrors=false; bool enumerateerrors=false; bool rewriteexistingerr=false; bool skserrors=false; bool crserrors=false; skserrors=SKSTest(); CRSTest(crserrors); basicerrors=BasicFuncTest()||TestGCMatrixType(); basicrnderrors=BasicFuncRandomTest(); linearerrors=LinearFunctionsTest(); level2unsymmetricerrors=TestLevel2Unsymmetric(); level2symmetricerrors=TestLevel2Symmetric(); level2triangularerrors=TestLevel2Triangular(); level3unsymmetricerrors=TestLevel3Unsymmetric(); level3symmetricerrors=TestLevel3Symmetric(); symmetricpermerrors=TestSymmetricPerm(); linearserrors=LinearFunctionSSTest(); linearmmerrors=LinearFunctionSMMTest(); linearsmmerrors=LinearFunctionsSMMTest(); copyerrors=CopyFuncTest(true)||TestConvertSM(); basiccopyerrors=BasicCopyFuncTest(true); enumerateerrors=EnumerateTest(); rewriteexistingerr=RewriteExistingTest(); TestGetRow(getrowerrors); TestSerialize(serializeerrors); //--- report waserrors=(skserrors||crserrors||getrowerrors||serializeerrors || basicerrors||linearerrors||basicrnderrors||level2unsymmetricerrors || level2symmetricerrors||level2triangularerrors||level3unsymmetricerrors || level3symmetricerrors||symmetricpermerrors||linearserrors||linearmmerrors || linearsmmerrors||copyerrors||basiccopyerrors||enumerateerrors || rewriteexistingerr); if(!silent) { Print("TESTING SPARSE"); Print("STORAGE FORMAT SPECIFICS:"); PrintResult("* SKS",!skserrors); PrintResult("* CRS",!crserrors); Print("OPERATIONS:"); PrintResult("* GETROW",!getrowerrors); PrintResult("* SERIALIZE",!serializeerrors); Print("BLAS:"); PrintResult("* LEVEL 2 GENERAL",!level2unsymmetricerrors); PrintResult("* LEVEL 2 SYMMETRIC",!level2symmetricerrors); PrintResult("* LEVEL 2 TRIANGULAR",!level2triangularerrors); PrintResult("* LEVEL 3 GENERAL",!level3unsymmetricerrors); PrintResult("* LEVEL 3 SYMMETRIC",!level3symmetricerrors); PrintResult("* PERMUTATIONS (SYMMETRIC)",!symmetricpermerrors); PrintResult("BASIC TEST",!basicerrors); PrintResult("COPY TEST",!copyerrors); PrintResult("BASIC_COPY TEST",!basiccopyerrors); PrintResult("BASIC_RND TEST",!basicrnderrors); PrintResult("LINEAR TEST",!linearerrors); PrintResult("LINEAR TEST FOR SYMMETRIC MATRICES",!linearserrors); PrintResult("LINEAR MxM TEST",!linearmmerrors); PrintResult("LINEAR MxM TEST FOR SYMMETRIC MATRICES",!linearsmmerrors); PrintResult("ENUMERATE TEST",!enumerateerrors); PrintResult("REWRITE EXISTING TEST",!rewriteexistingerr); //--- PrintResult("TEST SUMMARY",!waserrors); Print("\n\n"); } result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| Function for testing basic SKS functional. | //| Returns True on errors, False on success. | //+------------------------------------------------------------------+ bool CTestSparseUnit::SKSTest(void) { bool result=false; CSparseMatrix s0; CSparseMatrix s1; CSparseMatrix s2; CSparseMatrix s3; CSparseMatrix s4; CSparseMatrix s5; CSparseMatrix s6; int n=0; int nz=0; double pnz=0; int i=0; int j=0; int t0=0; int t1=0; CMatrixDouble a; CMatrixInt wasenumerated; CRowInt d; CRowInt u; CHighQualityRandState rs; double v0=0; double v1=0; int uppercnt=0; int lowercnt=0; int bw=0; result=false; CHighQualityRand::HQRndRandomize(rs); for(n=1; n<=20; n++) { nz=n*n-n; while(true) { //--- Generate N*N matrix where probability of non-diagonal element //--- being non-zero is PNZ. We also generate D and U - subdiagonal //--- and superdiagonal profile sizes. //--- Create matrix with either general SKS or banded SKS constructor function if(n>1) pnz=(double)nz/(double)(n*n-n); else pnz=1.0; d.Resize(n); u.Resize(n); d.Fill(0); u.Fill(0); a=matrix::Zeros(n,n); if(CHighQualityRand::HQRndNormal(rs)>0.0) { //--- Test SparseCreateSKS() functionality for(i=0; i0.0) CAp::SetErrorFlag(result,!CSparse::SparseRewriteExisting(s0,i,j,a.Get(i,j)),"testsparseunit.ap:456"); else CSparse::SparseSet(s0,i,j,a.Get(i,j)); } uppercnt=0; lowercnt=0; for(i=0; i=j && i-j>d[i]) CAp::SetErrorFlag(result,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:475"); if(i>=j && i-j<=d[i]) CAp::SetErrorFlag(result,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:477"); if(i<=j && j-i>u[j]) CAp::SetErrorFlag(result,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:479"); if(i<=j && j-i<=u[j]) CAp::SetErrorFlag(result,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:481"); } //--- Try to call SparseRewriteExisting() for out-of-band elements, make sure that it returns False. for(i=0; i=j && i-j>d[i]) || (i<=j && j-i>u[j])) CAp::SetErrorFlag(result,CSparse::SparseRewriteExisting(s0,i,j,1.0),"testsparseunit.ap:490"); //--- Convert to several different formats, check their contents with SparseGet(). CSparse::SparseCopy(s0,s1); CSparse::SparseConvertToCRS(s1); CSparse::SparseCopyToCRS(s0,s2); CSparse::SparseCopyToCRSBuf(s0,s3); CSparse::SparseCopyToHash(s0,s4); CSparse::SparseCopyToHashBuf(s0,s5); CSparse::SparseCopy(s0,s6); CSparse::SparseConvertToHash(s6); CAp::SetErrorFlag(result,CSparse::SparseGetNRows(s0)!=n,"testsparseunit.ap:503"); CAp::SetErrorFlag(result,CSparse::SparseGetNCols(s0)!=n,"testsparseunit.ap:504"); CAp::SetErrorFlag(result,CSparse::SparseGetMatrixType(s0)!=2,"testsparseunit.ap:505"); CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s0),"testsparseunit.ap:506"); CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s0),"testsparseunit.ap:507"); CAp::SetErrorFlag(result,CSparse::SparseIsHash(s0),"testsparseunit.ap:508"); CAp::SetErrorFlag(result,CSparse::SparseIsSKS(s1),"testsparseunit.ap:509"); CAp::SetErrorFlag(result,!CSparse::SparseIsCRS(s1),"testsparseunit.ap:510"); CAp::SetErrorFlag(result,CSparse::SparseIsHash(s1),"testsparseunit.ap:511"); for(i=0; i1) pnz=(double)nz/(double)(m*n); else pnz=1.0; //--- Generate random matrix in HASH format (testing SparseExists() //--- during process), copy it to CRS format, compare with original CSparse::SparseCreate(m,n,(int)MathRound(nz*CHighQualityRand::HQRndUniformR(rs)),s0); for(i=0; i1) pnz=(double)nz/(double)(m*n); else pnz=0.5; //--- Generate random matrix in HASH format (testing SparseExists() //--- during process), convert it to HASH/CRS/SKS format CSparse::SparseCreate(m,n,(int)MathRound(nz*CHighQualityRand::HQRndUniformR(rs)),s0); for(i=0; i::Zeros(i,j); //--- Checking for Matrix with hash table type uppercnt=0; lowercnt=0; for(i1=0; i1i1) uppercnt++; if(j1N //--- * with 50% probability to CRS or SKS, if M=N if(m!=n || CHighQualityRand::HQRndUniformR(rs)<0.5) CSparse::SparseCopyToCRS(sa,s0); else CSparse::SparseCopyToSKS(sa,s0); //--- Test SparseGet() for SA and S0 against matrix returned in A for(i=0; ieps,"testsparseunit.ap:966"); CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:967"); } //--- Test SparseMV x0=vector::Zeros(n); x1=vector::Zeros(n); for(j=0; jeps,"testsparseunit.ap:987"); } //--- Test SparseMTV x0=vector::Zeros(m); for(j=0; jeps,"testsparseunit.ap:1007"); } //--- Sparse GEMV for(ops=0; ops<=1; ops++) { for(alphakind=0; alphakind<=1; alphakind++) { for(betakind=0; betakind<=1; betakind++) { //--- Prepare inputs for testing ix=CHighQualityRand::HQRndUniformI(rs,50); iy=CHighQualityRand::HQRndUniformI(rs,50); alpha=alphakind*CHighQualityRand::HQRndNormal(rs); beta=betakind*CHighQualityRand::HQRndNormal(rs); if(ops==0) { opm=m; opn=n; } else { opm=n; opn=m; } x0=vector::Zeros(ix+opn); for(j=0; j::Zeros(iy+opm); for(j=0; jeps,"testsparseunit.ap:1068"); } } } //--- Test SparseMV2 if(m==n) { x0=vector::Zeros(n); for(j=0; jeps,"testsparseunit.ap:1091"); v=x1.DotC(a,j); CAp::SetErrorFlag(result,MathAbs(v-y1[j])>eps,"testsparseunit.ap:1093"); } } } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing Level 3 unsymmetric linear algebra functions| //| Additionally it tests SparseGet() for several matrix formats. | //| Returns True on failure. | //+------------------------------------------------------------------+ bool CTestSparseUnit::TestLevel3Unsymmetric(void) { //--- create variables bool result=false; int m=0; int n=0; int k=0; CMatrixDouble x0; CMatrixDouble x1; CMatrixDouble y0; CMatrixDouble y1; int i=0; int j=0; CMatrixDouble a; CSparseMatrix s0; CSparseMatrix sa; double eps=0; double v=0; CSparseGenerator g; CHighQualityRandState rs; int i_=0; eps=10000*CMath::m_machineepsilon; CHighQualityRand::HQRndRandomize(rs); //--- Test linear algebra functions for(m=1; m<=20; m++) { for(n=1; n<=20; n++) { InitGenerator(m,n,0,0,g); while(GenerateNext(g,a,sa)) { //--- Choose matrix width K k=1+CHighQualityRand::HQRndUniformI(rs,20); //--- Convert SA to desired storage format: //--- * to CRS if M<>N //--- * with 50% probability to CRS or SKS, if M=N if(m!=n || CHighQualityRand::HQRndUniformR(rs)<0.5) CSparse::SparseCopyToCRS(sa,s0); else CSparse::SparseCopyToSKS(sa,s0); //--- Test SparseGet() for SA and S0 against matrix returned in A for(i=0; i::Zeros(n,k); for(i=0; ieps,"testsparseunit.ap:1180"); } //--- Test SparseMTM x0=matrix::Zeros(m,k); x1=matrix::Zeros(m,k); for(i=0; ieps,"testsparseunit.ap:1203"); } //--- Test SparseMM2 if(m==n) { x0=matrix::Zeros(n,k); for(i=0; ieps,"testsparseunit.ap:1230"); v=0.0; for(i_=0; i_eps,"testsparseunit.ap:1232"); } } } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing Level 2 symmetric linear algebra functions. | //| Additionally it tests SparseGet() for several matrix formats. | //| Returns True on failure. | //+------------------------------------------------------------------+ bool CTestSparseUnit::TestLevel2Symmetric(void) { //--- create variables bool result=false; int n=0; CRowDouble x0; CRowDouble x1; CRowDouble y0; CRowDouble y1; int i=0; int j=0; CMatrixDouble a; CSparseMatrix s0; CSparseMatrix s1; CSparseMatrix sa; double eps=0; double v=0; double va=0; double vb=0; CSparseGenerator g; CHighQualityRandState rs; bool isupper=false; int triangletype=0; int i_=0; eps=10000*CMath::m_machineepsilon; result=false; CHighQualityRand::HQRndRandomize(rs); //--- Test linear algebra functions for(n=1; n<=20; n++) { for(triangletype=-1; triangletype<=1; triangletype++) { isupper=(CHighQualityRand::HQRndUniformR(rs)>0.5); if(triangletype<0) isupper=false; if(triangletype>0) isupper=true; InitGenerator(n,n,0,triangletype,g); while(GenerateNext(g,a,sa)) { //--- Convert SA to desired storage format: //--- * S0 stores unmodified copy //--- * S1 stores copy with unmodified triangle corresponding //--- to IsUpper and another triangle being spoiled by random //--- trash CSparse::SparseCopyToHash(sa,s1); for(i=0; ii && !isupper)) CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs)); if(CHighQualityRand::HQRndUniformR(rs)<0.5) { CSparse::SparseCopyToCRS(sa,s0); CSparse::SparseConvertToCRS(s1); } else { CSparse::SparseCopyToSKS(sa,s0); CSparse::SparseConvertToSKS(s1); } //--- Test SparseGet() for SA and S0 against matrix returned in A for(i=0; ieps,"testsparseunit.ap:1311"); CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:1312"); CAp::SetErrorFlag(result,(ji && triangletype==-1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1314"); } //--- Before we proceed with testing, update empty triangle of A //--- with its copy from another part of the matrix. for(i=0; ii && !isupper)) a.Set(i,j,a.Get(j,i)); //--- Test SparseSMV x0=vector::Zeros(n); for(j=0; jeps,"testsparseunit.ap:1343"); } CSparse::SparseSMV(s1,isupper,x0,y1); CAp::SetErrorFlag(result,CAp::Len(y1)eps,"testsparseunit.ap:1352"); } //--- Test SparseVSMV x0=vector::Zeros(n); for(j=0; jeps,"testsparseunit.ap:1370"); va=CSparse::SparseVSMV(s1,isupper,x0); CAp::SetErrorFlag(result,MathAbs(va-vb)>eps,"testsparseunit.ap:1372"); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing Level 2 symmetric linear algebra functions. | //| Additionally it tests SparseGet() for several matrix formats. | //| Returns True on failure. | //+------------------------------------------------------------------+ bool CTestSparseUnit::TestLevel3Symmetric(void) { //--- create variables bool result=false; int n=0; int k=0; CMatrixDouble x0; CMatrixDouble x1; CMatrixDouble y0; CMatrixDouble y1; int i=0; int j=0; CMatrixDouble a; CSparseMatrix s0; CSparseMatrix s1; CSparseMatrix sa; double eps=0; double v=0; CSparseGenerator g; CHighQualityRandState rs; bool isupper=false; int triangletype=0; int i_=0; eps=10000*CMath::m_machineepsilon; CHighQualityRand::HQRndRandomize(rs); //--- Test linear algebra functions for(n=1; n<=20; n++) { for(triangletype=-1; triangletype<=1; triangletype++) { isupper=CHighQualityRand::HQRndUniformR(rs)>0.5; if(triangletype<0) isupper=false; if(triangletype>0) isupper=true; InitGenerator(n,n,0,triangletype,g); while(GenerateNext(g,a,sa)) { //--- Choose matrix width K k=1+CHighQualityRand::HQRndUniformI(rs,20); //--- Convert SA to desired storage format: //--- * S0 stores unmodified copy //--- * S1 stores copy with unmodified triangle corresponding //--- to IsUpper and another triangle being spoiled by random //--- trash CSparse::SparseCopyToHash(sa,s1); for(i=0; ii && !isupper)) CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs)); if(CHighQualityRand::HQRndUniformR(rs)<0.5) { CSparse::SparseCopyToCRS(sa,s0); CSparse::SparseConvertToCRS(s1); } else { CSparse::SparseCopyToSKS(sa,s0); CSparse::SparseConvertToSKS(s1); } //--- Test SparseGet() for SA and S0 against matrix returned in A for(i=0; ieps,"testsparseunit.ap:1453"); CAp::SetErrorFlag(result,(double)(MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j)))>eps,"testsparseunit.ap:1454"); CAp::SetErrorFlag(result,(ji && triangletype==-1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1456"); } //--- Before we proceed with testing, update empty triangle of A //--- with its copy from another part of the matrix. for(i=0; ii && !isupper)) a.Set(i,j,a.Get(j,i)); //--- Test SparseSMM x0=matrix::Zeros(n,k); for(i=0; ieps,"testsparseunit.ap:1488"); } } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing sparse symmetric permutations | //+------------------------------------------------------------------+ bool CTestSparseUnit::TestSymmetricPerm(void) { //--- create variables bool result=false; int n=0; int functype=0; int i=0; int j=0; CHighQualityRandState rs; bool isupper=false; double eps=0; double nzprob=0; CMatrixDouble da; CMatrixDouble db; CSparseMatrix sa; CSparseMatrix sb; CRowInt ptbl; CRowInt pprod; eps=10*CMath::m_machineepsilon; CHighQualityRand::HQRndRandomize(rs); //--- Try various N and fill factors for(n=1; n<=20; n++) { nzprob=1.0; while((double)(nzprob)>=(double)(0.1/(n*n))) { //--- Generate matrix with desired fill factor, randomly select one triangle, //--- generate random permutation (in table and product form) CSparse::SparseCreate(n,n,0,sa); da=matrix::Zeros(n,n); for(i=0; i0.0; for(i=0; i::Zeros(n,n); for(i=0; i=i) && MathAbs(db.Get(i,j)-CSparse::SparseGet(sb,i,j))>eps,"testsparseunit.ap:1590"); CAp::SetErrorFlag(result,(isupper && j0.5; if(triangletype<0) isupper=false; if(triangletype>0) isupper=true; InitGenerator(n,n,0,triangletype,g); while(GenerateNext(g,a,sa)) { //--- Settings (IsUpper was already set, handle the rest) isunit=CHighQualityRand::HQRndUniformR(rs)<0.5; optype=CHighQualityRand::HQRndUniformI(rs,2); //--- Convert SA to desired storage format: //--- * S0 stores unmodified copy //--- * S1 stores copy with unmodified triangle corresponding //--- to IsUpper and another triangle being spoiled by random //--- trash CSparse::SparseCopyToHash(sa,s1); for(i=0; ii && !isupper)) CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs)); if(CHighQualityRand::HQRndUniformR(rs)<0.5) { CSparse::SparseCopyToCRS(sa,s0); CSparse::SparseConvertToCRS(s1); } else { CSparse::SparseCopyToSKS(sa,s0); CSparse::SparseConvertToSKS(s1); } //--- Generate "effective A" ea=matrix::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) { i1=i; j1=j; if(optype==1) CApServ::Swap(i1,j1); ea.Set(i1,j1,a.Get(i,j)); if(isunit && i1==j1) ea.Set(i1,j1,1.0); } //--- Test SparseTRMV x0=vector::Zeros(n); for(j=0; jeps,"testsparseunit.ap:1712"); } //--- CSparse::SparseTRMV(s0,isupper,isunit,optype,x0,y1); CAp::SetErrorFlag(result,CAp::Len(y1)eps,"testsparseunit.ap:1721"); } } } } //--- Test sparseTRSV for(n=1; n<=20; n++) { for(triangletype=-1; triangletype<=1; triangletype++) { isupper=CHighQualityRand::HQRndUniformR(rs)>0.5; if(triangletype==-1) isupper=false; if(triangletype==1) isupper=true; InitGenerator(n,n,1,triangletype,g); while(GenerateNext(g,a,sa)) { //--- Settings (IsUpper was already set, handle the rest) isunit=CHighQualityRand::HQRndUniformR(rs)<0.5; optype=CHighQualityRand::HQRndUniformI(rs,2); //--- Convert SA to desired storage format: //--- * S0 stores unmodified copy //--- * S1 stores copy with unmodified triangle corresponding //--- to IsUpper and another triangle being spoiled by random //--- trash CSparse::SparseCopyToHash(sa,s1); for(i=0; ii && !isupper)) CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs)); if(CHighQualityRand::HQRndUniformR(rs)<0.5) { CSparse::SparseCopyToCRS(sa,s0); CSparse::SparseConvertToCRS(s1); } else { CSparse::SparseCopyToSKS(sa,s0); CSparse::SparseConvertToSKS(s1); } //--- Generate "effective A" and EY = inv(EA)*x0 ea=matrix::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) { i1=i; j1=j; if(optype==1) CApServ::Swap(i1,j1); ea.Set(i1,j1,a.Get(i,j)); if(isunit && i1==j1) ea.Set(i1,j1,1.0); } ey=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; ieps,"testsparseunit.ap:1809"); CSparse::SparseTRSV(s1,isupper,isunit,optype,x1); CAp::SetErrorFlag(result,CAp::Len(x1)eps,"testsparseunit.ap:1815"); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing basic functional | //+------------------------------------------------------------------+ bool CTestSparseUnit::BasicFuncRandomTest(void) { //--- create variables bool result=false; CSparseMatrix s; int n=0; int m=0; int i=0; int j=0; int i1=0; int j1=0; CMatrixDouble a; int mfigure=0; int temp=0; n=20; m=20; mfigure=10; for(i=1; i::Zeros(i,j); //--- Checking for Matrix with hash table type for(i1=0; i1::Zeros(i); tyt=vector::Zeros(j); x0=vector::Zeros(j); x1=vector::Zeros(i); for(i1=0; i1=eps) { result=true; return(result); } //--- Check for MTV-result for(i1=0; i1=eps) { result=true; return(result); } } else { //--- Searching true result for(i1=0; i1=eps || MathAbs(yt0[i1]-tyt[i1])>=eps) { result=true; return(result); } //--- Check for MV- and MTV-result by help MV2 for(i1=0; i1eps || MathAbs(yt0[i1]-yt[i1])>eps) { result=true; return(result); } } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| Function for testing multyplication for simmetric matrix with | //| vector | //+------------------------------------------------------------------+ bool CTestSparseUnit::LinearFunctionSSTest(void) { //--- create variables bool result=false; CSparseMatrix s; int m=0; int i=0; int i1=0; int j1=0; double lb=0; double rb=0; CMatrixDouble a; CRowDouble x0; CRowDouble x1; CRowDouble ty; CRowDouble tyt; CRowDouble y; CRowDouble yt; double eps=0; //--- Accuracy eps=1000*CMath::m_machineepsilon; //--- Size of the matrix (m*m) m=10; //--- Left and right borders, limiting matrix values lb=-10; rb=10; //--- Test linear algebra functions for: //--- a) sparse matrix converted to CRS from Hash-Table //--- b) sparse matrix initially created as CRS for(i=1; i::Zeros(i); tyt=vector::Zeros(i); x0=vector::Zeros(i); x1=vector::Zeros(i); for(i1=0; i1=eps || (double)(MathAbs(yt[i1]-tyt[i1]))>=eps) { result=true; return(result); } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| Function for testing multyplication sparse matrix with nerrow | //| dense matrix | //+------------------------------------------------------------------+ bool CTestSparseUnit::LinearFunctionSMMTest(void) { //--- create variables bool result=false; CSparseMatrix s; int n=0; int m=0; int kmax=0; int i=0; int j=0; int k=0; int i1=0; int j1=0; int k1=0; double lb=0; double rb=0; CMatrixDouble a; CMatrixDouble x0; CMatrixDouble x1; CMatrixDouble ty; CMatrixDouble tyt; CMatrixDouble y; CMatrixDouble yt; CMatrixDouble y0; CMatrixDouble yt0; double eps=0; //--- Accuracy eps=1000*CMath::m_machineepsilon; //--- Size of the matrix (m*n) n=32; m=32; kmax=32; //--- Left and right borders, limiting matrix values lb=-10; rb=10; //--- Test linear algebra functions for: //--- a) sparse matrix converted to CRS from Hash-Table //--- b) sparse matrix initially created as CRS for(i=1; i::Zeros(j,kmax); x1=matrix::Zeros(i,kmax); for(i1=0; i1::Zeros(i,kmax); tyt=matrix::Zeros(j,kmax); if(i!=j) { for(i1=0; i1=eps) { result=true; return(result); } //--- Check for MTM-result for(i1=0; i1=eps) { result=true; return(result); } } else { CSparse::SparseMM(s,x0,k,y); CSparse::SparseMTM(s,x0,k,yt); CSparse::SparseMM2(s,x0,k,y0,yt0); //--- Check for MM2-result for(i1=0; i1=eps || MathAbs(yt0.Get(i1,j1)-tyt.Get(i1,j1))>=eps) { result=true; return(result); } //--- Check for MV- and MTM-result by help MV2 for(i1=0; i1eps || MathAbs(yt0.Get(i1,j1)-yt.Get(i1,j1))>eps) { result=true; return(result); } } } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| Function for testing multyplication for simmetric sparse matrix | //| with narrow dense matrix | //+------------------------------------------------------------------+ bool CTestSparseUnit::LinearFunctionsSMMTest(void) { bool result=false; CSparseMatrix s; int m=0; int k=0; int i=0; int j=0; int i1=0; int j1=0; int k1=0; double lb=0; double rb=0; CMatrixDouble a; CMatrixDouble x0; CMatrixDouble x1; CMatrixDouble ty; CMatrixDouble tyt; CMatrixDouble y; CMatrixDouble yt; double eps=0; //--- Accuracy eps=1000*CMath::m_machineepsilon; //--- Size of the matrix (m*m) m=32; k=32; //--- Left and right borders, limiting matrix values lb=-10; rb=10; //--- Test linear algebra functions for: //--- a) sparse matrix converted to CRS from Hash-Table //--- b) sparse matrix initially created as CRS for(i=1; i::Zeros(i,j); tyt=matrix::Zeros(i,j); x0=matrix::Zeros(i,j); x1=matrix::Zeros(i,j); for(i1=0; i1=eps || MathAbs(yt.Get(i1,k1)-tyt.Get(i1,k1))>=eps) { result=true; return(result); } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| Function for basic test SparseCopy | //+------------------------------------------------------------------+ bool CTestSparseUnit::BasicCopyFuncTest(bool silent) { //--- create variables bool result=false; CSparseMatrix s; CSparseMatrix ss; CSparseMatrix sss; int n=0; int m=0; CRowInt ner; int i=0; int j=0; int i1=0; int j1=0; CMatrixDouble a; double a0=0; double a1=0; n=30; m=30; for(i=1; i::Zeros(i,j); ner.Resize(i); for(i1=0; i1i1 && j1<=i1+2) { a.Set(i1,j1,i1+j1+1); CSparse::SparseSet(s,i1,j1,a.Get(i1,j1)); CSparse::SparseAdd(s,i1,j1,0); CSparse::SparseSet(sss,i1,j1,a.Get(i1,j1)); } else { a.Set(i1,j1,0); CSparse::SparseSet(s,i1,j1,a.Get(i1,j1)); CSparse::SparseAdd(s,i1,j1,0); } //--- Check for SparseCreate CSparse::SparseCopy(s,ss); a0=CSparse::SparseGet(s,i1,j1); a1=CSparse::SparseGet(ss,i1,j1); if(a0!=a1) { if(!silent) { Print("BasicCopyFuncTest::Report::SparseGet"); PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0); PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1); Print(" TEST FAILED"); } result=true; return(result); } } } //--- Check for SparseCreateCRS for(i1=0; i1::Zeros(i); tyt=vector::Zeros(j); x0=vector::Zeros(j); x1=vector::Zeros(i); for(i1=0; i1=eps || MathAbs(cpy[i1]-ty[i1])>=eps || (cpy[i1]-y[i1])!=0.0) { if(!silent) { Print("CopyFuncTest::Report::RES_MV"); PrintFormat("Y[%d]=%.5f; tY[%d]=%.5f",i1,y[i1],i1,ty[i1]); PrintFormat("cpY[%d]=%.5f;",i1,cpy[i1]); Print(" TEST FAILED.\n"); } result=true; return(result); } } //--- Check for MTV-result for(i1=0; i1=eps || (double)(MathAbs(cpyt[i1]-tyt[i1]))>=eps) || (double)(cpyt[i1]-yt[i1])!=0.0) { if(!silent) { Print("CopyFuncTest::Report::RES_MTV"); PrintFormat("Yt[%d]=%.5f; tYt[%d]=%.5f",i1,yt[i1],i1,tyt[i1]); PrintFormat("cpYt[%d]=%.5f;",i1,cpyt[i1]); Print(" TEST FAILED."); } result=true; return(result); } CSparse::SparseCopy(s,ss); for(i1=0; i1=eps || MathAbs(yt0[i1]-tyt[i1])>=eps || MathAbs(cpy0[i1]-ty[i1])>=eps || MathAbs(cpyt0[i1]-tyt[i1])>=eps || (cpy0[i1]-y0[i1])!=0.0 || (cpyt0[i1]-yt0[i1])!=0.0) { if(!silent) { Print("CopyFuncTest::Report::RES_MV2"); PrintFormat("Y0[%d]=%.5f; tY[%d]=%.5f",i1,y0[i1],i1,ty[i1]); PrintFormat("Yt0[%d]=%.5f; tYt[%d]=%.5f",i1,yt0[i1],i1,tyt[i1]); PrintFormat("cpY0[%d]=%.5f;",i1,cpy0[i1]); PrintFormat("cpYt0[%d]=%.5f;",i1,cpyt0[i1]); Print(" TEST FAILED."); } result=true; return(result); } //--- Check for MV- and MTV-result by help MV2 for(i1=0; i1eps || MathAbs(yt0[i1]-yt[i1])>eps || MathAbs(cpy0[i1]-cpy[i1])>eps || MathAbs(cpyt0[i1]-cpyt[i1])>eps) { if(!silent) { Print("CopyFuncTest::Report::RES_MV_MVT"); PrintFormat("Y0[%d]=%.5f; Y[%d]=%.5f",i1,y0[i1],i1,y[i1]); PrintFormat("Yt0[%d]=%.5f; Yt[%d]=%.5f",i1,yt0[i1],i1,yt[i1]); PrintFormat("cpY0[%d]=%.5f; cpY[%d]=%.5f",i1,cpy0[i1],i1,cpy[i1]); PrintFormat("cpYt0[%d]=%.5f; cpYt[%d]=%.5f",i1,cpyt0[i1],i1,cpyt[i1]); Print(" TEST FAILED."); } result=true; return(result); } CSparse::SparseCopy(s,ss); for(i1=0; i1::Zeros(1+1); g.m_rcs.stage=-1; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestSparseUnit::GenerateNext(CSparseGenerator &g, CMatrixDouble &da, CSparseMatrix &sa) { //--- create variables bool result=false; int n=0; int m=0; int nz=0; int nzd=0; double pnz=0; int i=0; int j=0; double v=0; int label=-1; da.Resize(0,0); //--- Reverse communication preparations //--- I know it looks ugly, but it works the same way //--- anywhere from C++ to Python. //--- This code initializes locals by: //--- * random values determined during code //--- generation - on first subroutine call //--- * values from previous call - on subsequent calls if(g.m_rcs.stage>=0) { n=g.m_rcs.ia[0]; m=g.m_rcs.ia[1]; nz=g.m_rcs.ia[2]; nzd=g.m_rcs.ia[3]; i=g.m_rcs.ia[4]; j=g.m_rcs.ia[5]; pnz=g.m_rcs.ra[0]; v=g.m_rcs.ra[1]; } else { n=359; m=-58; nz=-919; nzd=-909; i=81; j=255; pnz=74; v=-788; } switch(g.m_rcs.stage) { case 0: label=0; break; case 1: label=1; break; default: //--- Routine body n=g.m_n; if(g.m_m==0) m=n; else m=g.m_m; if(!CAp::Assert(m>0 && n>0,__FUNCTION__+": incorrect N/M")) return(false); //--- Generate general sparse matrix if(g.m_matkind!=0) { label=2; break; } nz=n*m; label=4; break; } while(label>=0) switch(label) { case 4: //--- Generate dense N*N matrix where probability of element //--- being non-zero is PNZ. pnz=(double)nz/(double)(n*m); g.m_bufa=matrix::Zeros(m,n); for(i=0; i::Zeros(m,n); CSparse::SparseCreate(m,n,(int)MathRound(pnz*m*n),sa); for(i=0; i=i && g.m_triangle>=0)) { da.Set(i,j,g.m_bufa.Get(i,j)); CSparse::SparseSet(sa,i,j,g.m_bufa.Get(i,j)); } g.m_rcs.stage=0; label=-1; break; case 0: //--- Increase problem sparcity and try one more time. //--- Stop after testing NZ=0. if(nz==0) { label=5; break; } nz=nz/2; label=4; break; case 5: result=false; return(result); case 2: //--- Generate general sparse matrix with non-zero diagonal if(g.m_matkind!=1) { label=6; break; } if(!CAp::Assert(n==m,__FUNCTION__+": non-square matrix for MatKind=1")) return(false); nz=n*n-n; case 8: //--- Generate dense N*N matrix where probability of non-diagonal element //--- being non-zero is PNZ. if(n>1) pnz=(double)nz/(double)(n*n-n); else pnz=1; g.m_bufa=matrix::Zeros(n,n); for(i=0; i::Zeros(n,n); CSparse::SparseCreate(n,n,(int)MathRound(pnz*(n*n-n)+n),sa); for(i=0; i=i && g.m_triangle>=0)) { da.Set(i,j,g.m_bufa.Get(i,j)); CSparse::SparseSet(sa,i,j,g.m_bufa.Get(i,j)); } g.m_rcs.stage=1; label=-1; break; case 1: //--- Increase problem sparcity and try one more time. //--- Stop after testing NZ=0. if(nz==0) { label=9; break; } nz=nz/2; label=8; break; case 9: result=false; return(result); case 6: result=false; return(result); } //--- Saving state result=true; g.m_rcs.ia.Set(0,n); g.m_rcs.ia.Set(1,m); g.m_rcs.ia.Set(2,nz); g.m_rcs.ia.Set(3,nzd); g.m_rcs.ia.Set(4,i); g.m_rcs.ia.Set(5,j); g.m_rcs.ra.Set(0,pnz); g.m_rcs.ra.Set(1,v); return(result); } //+------------------------------------------------------------------+ //| This function creates random sparse matrix with some prescribed | //| pattern. | //| INPUT PARAMETERS: | //| M - number of rows | //| N - number of columns | //| PKind - sparsity pattern: | //| *-1 = pattern is chosen at random as well as P0/P1 | //| * 0 = matrix with up to P0 non-zero elements at | //| random locations (however, actual number of | //| non-zero elements can be less than P0, and in| //| fact can be zero) | //| * 1 = band matrix with P0 non-zero elements below | //| diagonal and P1 non-zero element above | //| diagonal | //| * 2 = matrix with random number of contiguous | //| non-zero elements in the each row | //| CKind - creation type: | //| *-1 = CKind is chosen at random | //| * 0 = matrix is created in Hash-Table format and | //| converted to CRS representation | //| * 1 = matrix is created in CRS format | //| * 2 = matrix is created in Hash-Table format and | //| converted to SKS representation | //| OUTPUT PARAMETERS: | //| DA - dense representation of A, array[M,N] | //| SA - sparse representation of A, in CRS format | //+------------------------------------------------------------------+ void CTestSparseUnit::CreateRandom(int m,int n,int pkind,int ckind, int p0,int p1,CMatrixDouble &da, CSparseMatrix &sa) { //--- create variables int maxpkind=0; int maxckind=0; int i=0; int j=0; int k=0; double v=0; CRowInt c0; CRowInt c1; CRowInt rowsizes; maxpkind=2; maxckind=2; if(!CAp::Assert(m>=1,__FUNCTION__+": incorrect parameters")) return; if(!CAp::Assert(n>=1,__FUNCTION__+": incorrect parameters")) return; if(!CAp::Assert(pkind>=-1 && pkind<=maxpkind,__FUNCTION__+": incorrect parameters")) return; if(!CAp::Assert(ckind>=-1 && ckind<=maxckind,__FUNCTION__+": incorrect parameters")) return; if(!CAp::Assert(!(ckind==2 && m!=n),__FUNCTION__+": incorrect parameters")) return; if(pkind==-1) { pkind=CMath::RandomInteger(maxpkind+1); if(pkind==0) p0=CMath::RandomInteger(m*n); if(pkind==1) { p0=CMath::RandomInteger(MathMin(m,n)); p1=CMath::RandomInteger(MathMin(m,n)); } } if(ckind==-1) { do { ckind=CMath::RandomInteger(maxckind+1); } while(ckind==2 && m!=n); } if(pkind==0) { //--- Matrix with elements at random locations da=matrix::Zeros(m,n); if(ckind==0 || ckind==2) { //--- Create matrix in Hash format, convert to CRS or SKS CSparse::SparseCreate(m,n,1,sa); for(k=0; k<=p0-1; k++) { i=CMath::RandomInteger(m); j=CMath::RandomInteger(n); v=(double)(CMath::RandomInteger(17)-8)/8.0; if(CMath::RandomReal()>0.5) { da.Set(i,j,v); CSparse::SparseSet(sa,i,j,v); } else { da.Add(i,j,v); CSparse::SparseAdd(sa,i,j,v); } } if(ckind!=2) CSparse::SparseConvertToCRS(sa); else CSparse::SparseConvertToSKS(sa); return; } if(ckind==1) { //--- Create matrix in CRS format for(k=0; k<=p0-1; k++) { i=CMath::RandomInteger(m); j=CMath::RandomInteger(n); v=(double)(CMath::RandomInteger(17)-8)/8.0; da.Set(i,j,v); } rowsizes.Resize(m); for(i=0; i::Zeros(m,n); rowsizes.Resize(m); for(i=0; i::Zeros(m,n); rowsizes.Resize(m); c0.Resize(m); c1.Resize(m); for(i=0; i::Zeros(m,n); ta.Resize(m,n); ta.Fill((int)false); //--- for(i=0; i::Zeros(m,n); ta.Fill((int)false); v=r*(2*CMath::RandomReal()-1); //--- Creating and filling of the matrix ne=0; CSparse::SparseCreate(m,n,m*n,spa); for(i=0; i::Zeros(m,n); CSparse::SparseCreate(m,n,1,s); for(i=0; in) { CAp::SetErrorFlag(err,true,"testsparseunit.ap:3369"); return; } wasreturned.Fill((int)false); for(j=0; j<=nz-1; j++) { if(colidx[j]<0 || colidx[j]>n) { CAp::SetErrorFlag(err,true,"testsparseunit.ap:3378"); return; } CAp::SetErrorFlag(err,j>0 && colidx[j]<=colidx[j-1],"testsparseunit.ap:3381"); CAp::SetErrorFlag(err,vals[j]!=a.Get(i,colidx[j]) || vals[j]!=CSparse::SparseGet(s,i,colidx[j]),"testsparseunit.ap:3382"); wasreturned.Set(colidx[j],true); } for(j=0; j::Zeros(m,n); ner.Resize(m); ner.Fill(0); for(i=0; iresult) result=v; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CTestAblasFUnit::RefRMaxAbsV(int n,CRowDouble &x) { //--- create variables double result=0; double v=0; for(int i=0; iresult) result=v; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CTestAblasFUnit::RefRMaxR(int n,CMatrixDouble &x,int rowidx) { //--- create variables double result=0; double v=0; //--- check if(n<=0) { result=0; return(result); } result=x.Get(rowidx,0); for(int i=0; iresult) result=v; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CTestAblasFUnit::RefRMaxAbsR(int n,CMatrixDouble &x,int rowidx) { //--- create variables double result=0; double v=0; for(int i=0; iresult) result=v; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTestAblasFUnit::RefRGemVX(int m,int n,double alpha, CMatrixDouble &a,int ia, int ja,int opa, CRowDouble &x,int ix,double beta, CRowDouble &y,int iy) { //--- create variables int i=0; double v=0; int i_=0; int i1_=0; //--- Quick exit for M=0, N=0 or Alpha=0. //--- After this block we have M>0, N>0, Alpha<>0. if(m<=0) return; if(n<=0 || alpha==0.0) { if(beta!=0.0) { for(i=0; i=0; i--) { v=x[ix+i]; for(j=i+1; j=0; i--) { v=x[ix+i]; if(!isunit) v/=a.Get(ia+i,ja+i); x.Set(ix+i,v); if(v==0) continue; for(j=0; jtol,"testablasfunit.ap:156"); CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotVR(n,s0.m_x0,s0.m_a0,ridx)-RefRDotVR(n,s1.m_x0,s1.m_a0,ridx)))>tol,"testablasfunit.ap:157"); CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotRR(n,s0.m_a0,ridx,s0.m_a1,ridx2)-RefRDotRR(n,s1.m_a0,ridx,s1.m_a1,ridx2)))>tol,"testablasfunit.ap:158"); CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotV2(n,s0.m_x0)-RefRDotV2(n,s1.m_x0)))>tol,"testablasfunit.ap:159"); } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXSet(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); double alpha=0; int ialpha=0; bool balpha; int ridx=0; int cidx=0; int m0=0; int m1=0; int offsx=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xSetXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. m0=CMath::RandomInteger(MathMax(n,1)); m1=CMath::RandomInteger(MathMax(n,1)); ridx=CMath::RandomInteger(MathMax(n,1)); cidx=CMath::RandomInteger(MathMax(n,1)); offsx=CMath::RandomInteger(n/2+1); alpha=2*CMath::RandomReal()-1; ialpha=CMath::RandomInteger(21)-10; balpha=CMath::RandomReal()>0.5; //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSetV(n,alpha,s0.m_x0); RefRSetV(n,alpha,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:197"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSetR(n,alpha,s0.m_a0,ridx); RefRSetR(n,alpha,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:203"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSetC(n,alpha,s0.m_a0,cidx); RefRSetC(n,alpha,s1.m_a0,cidx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:209"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSetM(m0,m1,alpha,s0.m_a0); RefRSetM(m0,m1,alpha,s1.m_a0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:215"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::ISetV(n,ialpha,s0.m_ix0); RefISetV(n,ialpha,s1.m_ix0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:221"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::BSetV(n,balpha,s0.m_bx0); RefBSetV(n,balpha,s1.m_bx0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:227"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSetVX(n/2,alpha,s0.m_x0,offsx); RefRSetVX(n/2,alpha,s1.m_x0,offsx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:233"); //--- Increment seed iseed ++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXAdd(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); double alpha=0; int ridx=0; int ridx2=0; int cidx=0; int offsx=0; int offsy=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. ridx=CMath::RandomInteger(MathMax(n,1)); ridx2=CMath::RandomInteger(MathMax(n,1)); cidx=CMath::RandomInteger(MathMax(n,1)); offsx=CMath::RandomInteger(n/2+1); offsy=CMath::RandomInteger(n/2+1); alpha=2*CMath::RandomReal()-1; //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RAddV(n,alpha,s0.m_x0,s0.m_x1); RefRAddV(n,alpha,s1.m_x0,s1.m_x1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:271"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RAddVR(n,alpha,s0.m_x0,s0.m_a0,cidx); RefRAddVR(n,alpha,s1.m_x0,s1.m_a0,cidx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:277"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RAddVC(n,alpha,s0.m_x0,s0.m_a0,cidx); RefRAddVC(n,alpha,s1.m_x0,s1.m_a0,cidx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:283"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RAddRV(n,alpha,s0.m_a0,ridx,s0.m_x0); RefRAddRV(n,alpha,s1.m_a0,ridx,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:289"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RAddRR(n,alpha,s0.m_a0,ridx,s0.m_a1,ridx2); RefRAddRR(n,alpha,s1.m_a0,ridx,s1.m_a1,ridx2); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:295"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RAddVX(n/2,alpha,s0.m_x0,offsx,s0.m_x1,offsy); RefRAddVX(n/2,alpha,s1.m_x0,offsx,s1.m_x1,offsy); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:301"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXMulAdd(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xMulAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2); RefRMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:329"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RNegMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2); RefRNegMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:335"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2,s0.m_x3); RefRCopyMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2,s1.m_x3); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:341"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyNegMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2,s0.m_x3); RefRCopyNegMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2,s1.m_x3); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:347"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXMul(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); double alpha=0; int ridx=0; int offsx=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. ridx=CMath::RandomInteger(MathMax(n,1)); offsx=CMath::RandomInteger(n/2+1); alpha=2*CMath::RandomReal()-1; //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMulV(n,alpha,s0.m_x0); RefRMulV(n,alpha,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:382"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMulR(n,alpha,s0.m_a0,ridx); RefRMulR(n,alpha,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:388"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMulVX(n/2,alpha,s0.m_x0,offsx); RefRMulVX(n/2,alpha,s1.m_x0,offsx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:394"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXSqrt(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); int ridx=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. ridx=CMath::RandomInteger(MathMax(n,1)); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSqrtV(n,s0.m_x0); RefRSqrtV(n,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:425"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RSqrtR(n,s0.m_a0,ridx); RefRSqrtR(n,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:431"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXMax(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); int ridx=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. ridx=CMath::RandomInteger(MathMax(n,1)); InitPlayground(n,iseed,s0); s1=s0; s0.m_v0=CAblasF::RMaxV(n,s0.m_x0); s1.m_v0=RefRMaxV(n,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:462"); //--- InitPlayground(n,iseed,s0); s1=s0; s0.m_v0=CAblasF::RMaxAbsV(n,s0.m_x0); s1.m_v0=RefRMaxAbsV(n,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:468"); //--- InitPlayground(n,iseed,s0); s1=s0; s0.m_v0=CAblasF::RMaxR(n,s0.m_a0,ridx); s1.m_v0=RefRMaxR(n,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:474"); //--- InitPlayground(n,iseed,s0); s1=s0; s0.m_v0=CAblasF::RMaxAbsR(n,s0.m_a0,ridx); s1.m_v0=RefRMaxAbsR(n,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:480"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXMerge(int maxn,double tol) { //--- create variables bool result=false; int n=0; int i=0; int iseed=CMath::RandomInteger(10000); int ridx=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. ridx=CMath::RandomInteger(MathMax(n,1)); InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMulV(n,s0.m_x0,s0.m_x1); RefRMergeMulV(n,s1.m_x0,s1.m_x1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:511"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMulVR(n,s0.m_x0,s0.m_a0,ridx); RefRMergeMulVR(n,s1.m_x0,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:517"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMulRV(n,s0.m_a0,ridx,s0.m_x0); RefRMergeMulRV(n,s1.m_a0,ridx,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:523"); //--- InitPlayground(n,iseed,s0); s1=s0; for(i=0; itol,"testablasfunit.ap:534"); //--- InitPlayground(n,iseed,s0); s1=s0; for(i=0; itol,"testablasfunit.ap:545"); //--- InitPlayground(n,iseed,s0); s1=s0; for(i=0; itol,"testablasfunit.ap:556"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMaxV(n,s0.m_x0,s0.m_x1); RefRMergeMaxV(n,s1.m_x0,s1.m_x1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:562"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMaxVR(n,s0.m_x0,s0.m_a0,ridx); RefRMergeMaxVR(n,s1.m_x0,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:568"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMaxRV(n,s0.m_a0,ridx,s0.m_x0); RefRMergeMaxRV(n,s1.m_a0,ridx,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:574"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMinV(n,s0.m_x0,s0.m_x1); RefRMergeMinV(n,s1.m_x0,s1.m_x1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:580"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMinVR(n,s0.m_x0,s0.m_a0,ridx); RefRMergeMinVR(n,s1.m_x0,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:586"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RMergeMinRV(n,s0.m_a0,ridx,s0.m_x0); RefRMergeMinRV(n,s1.m_a0,ridx,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:592"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXCopy(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); double alpha=0; int ridx=0; int ridx2=0; int cidx=0; int offsx=0; int offsy=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each xAddXX version twice - reference vs library. //--- Compare playground snapshots - should be identical. ridx=CMath::RandomInteger(MathMax(n,1)); ridx2=CMath::RandomInteger(MathMax(n,1)); cidx=CMath::RandomInteger(MathMax(n,1)); offsx=CMath::RandomInteger(n/2+1); offsy=CMath::RandomInteger(n/2+1); alpha=2*CMath::RandomReal()-1; //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyV(n,s0.m_x0,s0.m_x1); RefRCopyV(n,s1.m_x0,s1.m_x1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:630"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::ICopyV(n,s0.m_ix0,s0.m_ix1); RefICopyV(n,s1.m_ix0,s1.m_ix1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:636"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::BCopyV(n,s0.m_bx0,s0.m_bx1); RefBCopyV(n,s1.m_bx0,s1.m_bx1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:642"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyVR(n,s0.m_x0,s0.m_a0,ridx); RefRCopyVR(n,s1.m_x0,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:648"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyRV(n,s0.m_a0,ridx,s0.m_x0); RefRCopyRV(n,s1.m_a0,ridx,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:654"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyRR(n,s0.m_a0,ridx,s0.m_a1,ridx2); RefRCopyRR(n,s1.m_a0,ridx,s1.m_a1,ridx2); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:660"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyVC(n,s0.m_x0,s0.m_a0,cidx); RefRCopyVC(n,s1.m_x0,s1.m_a0,cidx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:666"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyCV(n,s0.m_a0,cidx,s0.m_x0); RefRCopyCV(n,s1.m_a0,cidx,s1.m_x0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:672"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyVX(n/2,s0.m_x0,offsx,s0.m_x1,offsy); RefRCopyVX(n/2,s1.m_x0,offsx,s1.m_x1,offsy); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:678"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::ICopyVX(n/2,s0.m_ix0,offsx,s0.m_ix1,offsy); RefICopyVX(n/2,s1.m_ix0,offsx,s1.m_ix1,offsy); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:684"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyMulV(n,alpha,s0.m_x0,s0.m_x1); RefRCopyMulV(n,alpha,s1.m_x0,s1.m_x1); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:690"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyMulVR(n,alpha,s0.m_x0,s0.m_a0,ridx); RefRCopyMulVR(n,alpha,s1.m_x0,s1.m_a0,ridx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:696"); //--- InitPlayground(n,iseed,s0); s1=s0; CAblasF::RCopyMulVC(n,alpha,s0.m_x0,s0.m_a0,cidx); RefRCopyMulVC(n,alpha,s1.m_x0,s1.m_a0,cidx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:702"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXGemV(int maxn,double tol) { //--- create variables bool result=false; int n=0; int m=0; int iseed=CMath::RandomInteger(10000); double alpha=0; double beta=0; int offs0=0; int offs1=0; int offsx=0; int offsy=0; int padding=0; int opa=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each GEMV version twice - reference vs library. //--- Compare playground snapshots - should be identical. padding=CMath::RandomInteger(10); m=CMath::RandomInteger(n+1); offs0=CMath::RandomInteger(MathMax(n,1)); offs1=CMath::RandomInteger(MathMax(n,1)); offsx=CMath::RandomInteger(MathMax(n,1)); offsy=CMath::RandomInteger(MathMax(n,1)); alpha=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2); beta=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2); opa=CMath::RandomInteger(2); //--- InitPlayground(2*n+padding,iseed,s0); InitPlayground(2*n+padding,iseed,s1); CAblasF::RGemV(m,n,alpha,s0.m_a0,opa,s0.m_x0,beta,s0.m_x1); RefRGemVX(m,n,alpha,s1.m_a0,0,0,opa,s1.m_x0,0,beta,s1.m_x1,0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:745"); //--- InitPlayground(2*n+padding,iseed,s0); InitPlayground(2*n+padding,iseed,s1); CAblasF::RGemVX(m,n,alpha,s0.m_a0,offs0,offs1,opa,s0.m_x0,offsx,beta,s0.m_x1,offsy); RefRGemVX(m,n,alpha,s1.m_a0,offs0,offs1,opa,s1.m_x0,offsx,beta,s1.m_x1,offsy); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:751"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXGer(int maxn,double tol) { //--- create variables bool result=false; int n=0; int m=0; int iseed=CMath::RandomInteger(10000); double alpha=0; int padding=0; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each GER version twice - reference vs library. //--- Compare playground snapshots - should be identical. padding=CMath::RandomInteger(10); m=CMath::RandomInteger(n+1); alpha=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2); //--- InitPlayground(2*n+padding,iseed,s0); InitPlayground(2*n+padding,iseed,s1); CAblasF::RGer(m,n,alpha,s0.m_x0,s0.m_x1,s0.m_a0); RefGerX(m,n,s1.m_a0,0,0,alpha,s1.m_x0,0,s1.m_x1,0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:786"); //--- InitPlayground(2*n+padding,iseed,s0); InitPlayground(2*n+padding,iseed,s1); CAblasF::RGer(n,m,alpha,s0.m_x0,s0.m_x1,s0.m_a0); RefGerX(n,m,s1.m_a0,0,0,alpha,s1.m_x0,0,s1.m_x1,0); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:792"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestAblasFUnit::TestXTrsV(int maxn,double tol) { //--- create variables bool result=false; int n=0; int iseed=CMath::RandomInteger(10000); int offs0=0; int offs1=0; int offsx=0; int padding=0; int opa=0; bool isupper; bool isunit; CAblasFPlayground s0; CAblasFPlayground s1; for(n=0; n<=maxn; n++) { //--- Prepare two identical playground structures //--- Compute each GEMV version twice - reference vs library. //--- Compare playground snapshots - should be identical. padding=CMath::RandomInteger(10); offs0=CMath::RandomInteger(MathMax(n,1)); offs1=CMath::RandomInteger(MathMax(n,1)); offsx=CMath::RandomInteger(MathMax(n,1)); opa=CMath::RandomInteger(2); isupper=CMath::RandomReal()>0.5; isunit=CMath::RandomReal()>0.5; InitPlayground(2*n+padding,iseed,s0); InitPlayground(2*n+padding,iseed,s1); CAblasF::RTrsVX(n,s0.m_a0,offs0,offs1,isupper,isunit,opa,s0.m_x0,offsx); RefTrsVX(n,s1.m_a0,offs0,offs1,isupper,isunit,opa,s1.m_x0,offsx); CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:833"); //--- Increment seed iseed++; } //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTestAblasFUnit::InitPlayground(int minlen,int iseed,CAblasFPlayground &s) { int maxpad=10; int k=iseed+iseed*iseed*iseed; minlen=MathMax(minlen,1); s.m_x0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; s.m_x1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; s.m_x2.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; s.m_x3.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; s.m_ix0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; s.m_ix1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; ArrayResize(s.m_bx0,minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; ArrayResize(s.m_bx1,minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k)))); k++; s.m_a0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))),minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k+1)))); k+=2; s.m_a1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))),minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k+1)))); k+=2; s.m_v0=0; k=PseudoRandomInit1(s.m_x0,k); k=PseudoRandomInit1(s.m_x1,k); k=PseudoRandomInit1(s.m_x2,k); k=PseudoRandomInit1(s.m_x3,k); k=PseudoRandomInit1I(s.m_ix0,k); k=PseudoRandomInit1I(s.m_ix1,k); k=PseudoRandomInit1B(s.m_bx0,k); k=PseudoRandomInit1B(s.m_bx1,k); k=PseudoRandomInit2(s.m_a0,k); k=PseudoRandomInit2(s.m_a1,k); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CTestAblasFUnit::ComparePlaygrounds(CAblasFPlayground &s0, CAblasFPlayground &s1) { double result=0; result=MathMax(result,MathAbs(s0.m_v0-s1.m_v0)/rmx3(MathAbs(s0.m_v0),MathAbs(s1.m_v0),1)); result=MathMax(result,rcmp1(s0.m_x0,s1.m_x0)); result=MathMax(result,rcmp1(s0.m_x1,s1.m_x1)); result=MathMax(result,icmp1(s0.m_ix0,s1.m_ix0)); result=MathMax(result,icmp1(s0.m_ix1,s1.m_ix1)); result=MathMax(result,bcmp1(s0.m_bx0,s1.m_bx0)); result=MathMax(result,bcmp1(s0.m_bx1,s1.m_bx1)); result=MathMax(result,rcmp1(s0.m_x2,s1.m_x2)); result=MathMax(result,rcmp1(s0.m_x3,s1.m_x3)); result=MathMax(result,rcmp2(s0.m_a0,s1.m_a0)); result=MathMax(result,rcmp2(s0.m_a1,s1.m_a1)); return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CTestAblasFUnit::PseudoRandomInit1(CRowDouble &x,int iseed) { for(int i=0; i0.0; iseed++; } //--- return result return(iseed); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CTestAblasFUnit::PseudoRandomInit2(CMatrixDouble &x,int iseed) { //--- create variables int i=0; int j=0; CRowDouble xr; CRowDouble xc; xr=vector::Zeros(CAp::Rows(x)); for(i=0; i::Zeros(CAp::Cols(x)); for(j=0; j1.0) x.Add(i,j,- 1); while(x.Get(i,j)<(-1.0)) x.Add(i,j,1); } } //--- return result return(iseed); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CTestAblasFUnit::rmx3(double r0,double r1,double r2) { double result=r0; if(r1>result) result=r1; if(r2>result) result=r2; //--- return result return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CTestAblasFUnit::rcmp1(CRowDouble &x,CRowDouble &y) { double result=0; int i=0; double mx=0; //--- check if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match")) return(EMPTY_VALUE); result=0; mx=1; for(i=0; i::Zeros(n+1); a.Set(0,2); a.Set(1,3); CPolynomialSolver::PolynomialSolve(a,n,x,rep); CAp::SetErrorFlag(waserrors,(double)(MathAbs(x[0].real+2.0/3.0))>eps,"testpolynomialsolverunit.ap:33"); CAp::SetErrorFlag(waserrors,(double)(x[0].imag)!=0.0,"testpolynomialsolverunit.ap:34"); CAp::SetErrorFlag(waserrors,(double)(rep.m_maxerr)>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:35"); n=2; a=vector::Zeros(n+1); a.Set(0,1); a.Set(1,-2); a.Set(2,1); CPolynomialSolver::PolynomialSolve(a,n,x,rep); CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[0]-1)>eps,"testpolynomialsolverunit.ap:43"); CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[1]-1)>eps,"testpolynomialsolverunit.ap:44"); CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:45"); n=2; a=vector::Zeros(n+1); a.Set(0,2); a.Set(1,-3); a.Set(2,1); CPolynomialSolver::PolynomialSolve(a,n,x,rep); if(x[0].realeps,"testpolynomialsolverunit.ap:55"); CAp::SetErrorFlag(waserrors,MathAbs(x[1].real-2)>eps,"testpolynomialsolverunit.ap:56"); } else { CAp::SetErrorFlag(waserrors,MathAbs(x[0].real-2)>eps,"testpolynomialsolverunit.ap:60"); CAp::SetErrorFlag(waserrors,MathAbs(x[1].real-1)>eps,"testpolynomialsolverunit.ap:61"); } CAp::SetErrorFlag(waserrors,x[0].imag!=0.0,"testpolynomialsolverunit.ap:63"); CAp::SetErrorFlag(waserrors,x[1].imag!=0.0,"testpolynomialsolverunit.ap:64"); CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:65"); n=2; a=vector::Zeros(n+1); a.Set(0,1); a.Set(1,0); a.Set(2,1); CPolynomialSolver::PolynomialSolve(a,n,x,rep); CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[0]*x[0]+1)>eps,"testpolynomialsolverunit.ap:73"); CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:74"); n=4; a=vector::Zeros(n+1); a.Set(4,1); CPolynomialSolver::PolynomialSolve(a,n,x,rep); CAp::SetErrorFlag(waserrors,x[0]!=0,"testpolynomialsolverunit.ap:84"); CAp::SetErrorFlag(waserrors,x[1]!=0,"testpolynomialsolverunit.ap:85"); CAp::SetErrorFlag(waserrors,x[2]!=0,"testpolynomialsolverunit.ap:86"); CAp::SetErrorFlag(waserrors,x[3]!=0,"testpolynomialsolverunit.ap:87"); CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:88"); n=2; a=vector::Zeros(n+1); a.Set(1,3); a.Set(2,2); CPolynomialSolver::PolynomialSolve(a,n,x,rep); if(x[0].real>x[1].real) { CAp::SetErrorFlag(waserrors,x[0]!=0,"testpolynomialsolverunit.ap:98"); CAp::SetErrorFlag(waserrors,MathAbs(x[1].real+3.0/2.0)>eps,"testpolynomialsolverunit.ap:99"); CAp::SetErrorFlag(waserrors,x[1].imag!=0.0,"testpolynomialsolverunit.ap:100"); } else { CAp::SetErrorFlag(waserrors,x[1]!=0,"testpolynomialsolverunit.ap:104"); CAp::SetErrorFlag(waserrors,MathAbs(x[0].real+3.0/2.0)>eps,"testpolynomialsolverunit.ap:105"); CAp::SetErrorFlag(waserrors,x[0].imag!=0.0,"testpolynomialsolverunit.ap:106"); } CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:108"); if(!silent) PrintResult("TESTING POLYNOMIAL SOLVER",!waserrors); result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestDirectSparseSolversUnit { public: static bool TestDirectSparseSolvers(bool silent); private: static void TestSKS(bool &errorflag); static void TestCholesky(bool &errorflag); static void TestGen(bool &errorflag); }; //+------------------------------------------------------------------+ //| Test | //+------------------------------------------------------------------+ bool CTestDirectSparseSolversUnit::TestDirectSparseSolvers(bool silent) { //--- create variables bool result; bool rskserrors; bool rcholerrors; bool rgenerrors; bool waserrors; //--- initialization rskserrors=false; rcholerrors=false; rgenerrors=false; TestSKS(rskserrors); TestCholesky(rcholerrors); TestGen(rgenerrors); waserrors=(rskserrors||rcholerrors)||rgenerrors; if(!silent) { Print("TESTING DIRECT SPARSE SOLVERS:"); PrintResult("* SPD-SKS (real)",!rskserrors); PrintResult("* SPD-CRS (real)",!rcholerrors); PrintResult("* GENERAL (real)",!rgenerrors); PrintResult("TEST SUMMARY",!waserrors); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| SPD SKS test | //+------------------------------------------------------------------+ void CTestDirectSparseSolversUnit::TestSKS(bool &errorflag) { //--- create variables int pass=0; int passcount=0; int maxn=0; double threshold=0; int bw=0; bool isupper; int n=0; int i=0; int j=0; CMatrixDouble a; CRowDouble xe; CRowDouble b; CRowDouble xs; CSparseMatrix sa; CSparseMatrix sa2; CSparseSolverReport rep; CHighQualityRandState rs; //--- initialization passcount=10; maxn=30; threshold=1.0E-6; CHighQualityRand::HQRndRandomize(rs); //--- Well conditioned SPD problems solved with SKS solver for(n=1; n<=maxn; n++) { for(pass=1; pass<=passcount; pass++) { //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe isupper=CHighQualityRand::HQRndNormal(rs)>0.5; bw=CHighQualityRand::HQRndUniformI(rs,n+1); a=matrix::Zeros(n,n); CSparse::SparseCreate(n,n,0,sa); for(i=0; ii && !isupper)) if(CHighQualityRand::HQRndUniformR(rs)<0.25) CSparse::SparseSet(sa,i,j,CHighQualityRand::HQRndNormal(rs)); } CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3)); xe.Resize(n); for(i=0; i::Zeros(n); CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0); //--- Test SKS solver CSparse::SparseCopyToSKS(sa,sa2); CDirectSparseSolvers::SparseSPDSolveSKS(sa2,isupper,b,xs,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:88"); CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:89"); if(errorflag) return; for(i=0; ithreshold,"testdirectsparsesolversunit.ap:93"); //--- Test solver #2 CSparse::SparseCopyToSKS(sa,sa2); if(!CTrFac::SparseCholeskySkyLine(sa2,n,isupper)) { CAp::SetErrorFlag(errorflag,true,"testdirectsparsesolversunit.ap:102"); return; } CDirectSparseSolvers::SparseSPDCholeskySolve(sa2,isupper,b,xs,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:107"); CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:108"); if(errorflag) return; for(i=0; ithreshold,"testdirectsparsesolversunit.ap:112"); } } } //+------------------------------------------------------------------+ //| SPD SKS test | //+------------------------------------------------------------------+ void CTestDirectSparseSolversUnit::TestCholesky(bool &errorflag) { //--- create variables int pass=0; int passcount=0; int maxn=0; double threshold=0; int bw=0; bool isupper; int n=0; int i=0; int j=0; CMatrixDouble a; CRowDouble xe; CRowDouble b; CRowDouble xs; CSparseMatrix sa; CSparseMatrix sa2; CSparseSolverReport rep; CHighQualityRandState rs; //--- initialization passcount=10; maxn=30; threshold=1.0E-6; CHighQualityRand::HQRndRandomize(rs); //--- Well conditioned SPD problems solved with Cholesky solver for(n=1; n<=maxn; n++) { for(pass=1; pass<=passcount; pass++) { //--- 1. generate random well conditioned matrix A. //--- 2. generate random solution vector xe //--- 3. generate right part b=A*xe isupper=CHighQualityRand::HQRndNormal(rs)>0.5; bw=CHighQualityRand::HQRndUniformI(rs,n+1); a=matrix::Zeros(n,n); CSparse::SparseCreate(n,n,0,sa); for(i=0; ii && !isupper)) if(CHighQualityRand::HQRndUniformR(rs)<0.25) CSparse::SparseSet(sa,i,j,CHighQualityRand::HQRndNormal(rs)); } CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3)); xe=vector::Zeros(n); for(i=0; i::Zeros(n); CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0); //--- Test CRS/AMD solver CSparse::SparseCopy(sa,sa2); CDirectSparseSolvers::SparseSPDSolve(sa2,isupper,b,xs,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:196"); CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:197"); if(errorflag) return; for(i=0; ithreshold,"testdirectsparsesolversunit.ap:201")) return; } } } //+------------------------------------------------------------------+ //| General linear test | //+------------------------------------------------------------------+ void CTestDirectSparseSolversUnit::TestGen(bool &errorflag) { //--- create variables int pass=0; int passcount=0; int maxn=0; double threshold=0; int n=0; int noffdiag=0; int i=0; int j=0; int k=0; CMatrixDouble a; CRowDouble xe; CRowDouble b; CRowDouble xs; CRowDouble xs2; CRowInt pivp; CRowInt pivq; CSparseMatrix sa; CSparseMatrix sa2; CSparseSolverReport rep; CHighQualityRandState rs; //--- initialization passcount=10; maxn=30; threshold=1.0E-6; CHighQualityRand::HQRndRandomize(rs); //--- Well conditioned general linear problems solved with LU solver for(n=1; n<=maxn; n++) { for(pass=1; pass<=passcount; pass++) { //--- Select number of off-diagonal entries, we want to try matrices //--- from dense to sparse noffdiag=n*(n-1); while(true) { //--- 1. generate random well conditioned matrix A. //--- 2. apply row/col permutation //--- 3. generate random solution vector xe //--- 4. generate right part b=A*xe a=matrix::Zeros(n,n); for(k=0; k::Zeros(n); for(i=0; i::Zeros(n); CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0); //--- Test solver #1 CDirectSparseSolvers::SparseSolve(sa,b,xs,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:285"); CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:286"); if(errorflag) return; for(i=0; ithreshold,"testdirectsparsesolversunit.ap:290"); //--- Test solver #2 CSparse::SparseCopyToCRS(sa,sa2); CTrFac::SparseLU(sa2,0,pivp,pivq); CDirectSparseSolvers::SparseLUSolve(sa2,pivp,pivq,b,xs2,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:300"); CAp::SetErrorFlag(errorflag,CAp::Len(xs2)!=n,"testdirectsparsesolversunit.ap:301"); if(errorflag) return; for(i=0; ithreshold,"testdirectsparsesolversunit.ap:305")) return; //--- Update fill factor if(noffdiag==0) break; noffdiag=noffdiag/2; } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestIterativeSparseUnit { public: static bool TestIterativeSparse(bool silent); private: static void TestGMRES(int maxn,bool &err); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestIterativeSparseUnit::TestIterativeSparse(bool silent) { //--- create variables bool result; int maxn=50; bool gmreserrors; bool waserrors; //--- Prepare error flags gmreserrors=false; //--- Run tests TestGMRES(maxn,gmreserrors); //--- report waserrors=gmreserrors; if(!silent) { Print("TESTING ITERATIVE SPARSE SOLVERS:"); PrintResult("* GMRES",!gmreserrors); //--- Were errors? PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| Test GMRES solver. | //| on failure sets error flag, on success does not touch it | //+------------------------------------------------------------------+ void CTestIterativeSparseUnit::TestGMRES(int maxn,bool &err) { //--- create variables CSparseMatrix a; CSparseMatrix crsa; CRowDouble xref; CRowDouble x; CRowDouble b; CRowDouble b2; CRowDouble ax; CRowDouble xr; CRowDouble x0; CRowDouble repfirst; CRowDouble replast; CRowDouble tmp; CMatrixDouble z; int nnz=0; int i=0; int j=0; int k=0; int n=0; int gmresk=0; CSparseSolverState solver; CHighQualityRandStateShell rs; double epsf=0; bool isupper; CSparseSolverReport rep; double v=0; double tol=0; double rprev=0; int requesttype=0; int nmv=0; int nreports=0; CAlglib::HQRndRandomize(rs); epsf=MathPow(10,-4-CAlglib::HQRndUniformI(rs,3)); //--- Test that GMRES is a Krylov subspace method, i.e. its iterate belongs to span(b,A*b,A*A*b,...) for(n=1; n<=maxn; n++) { //--- Generate sparse matrix and RHS. CSparse::SparseCreate(n,n,0,a); for(i=0; i::Zeros(gmresk+1,n); CAblasF::RCopyAllocV(n,b,xr); for(k=0; k1.0E-6,"testiterativesparseunit.ap:125"); //--- Additional test - R2 field is correct CSparse::SparseMV(crsa,x,ax); CAblasF::RAddV(n,-1.0,b,ax); if(CAp::SetErrorFlag(err,MathAbs(rep.m_r2-CAblasF::RDotV2(n,ax))>1.0E-6,"testiterativesparseunit.ap:132")) return; } } //--- Randomly generated sparse problem, possibly degenerate. //--- Iterate until convergence. //--- Try various sparsity patterns. for(n=1; n<=maxn; n++) { nnz=n*n; while(true) { //--- Generate sparse matrix and RHS. //--- The matrix is regularized by placing large elements on its diagonal CSparse::SparseCreate(n,n,0,a); for(k=0; k0.0; CSparse::SparseSMV(crsa,isupper,xref,b); CIterativeSparse::SparseSolverCreate(n,solver); CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk); CIterativeSparse::SparseSolverSetCond(solver,epsf,0); CIterativeSparse::SparseSolverSolveSymmetric(solver,a,isupper,b); CIterativeSparse::SparseSolverResults(solver,x,rep); CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:180"); CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:181"); if(err) { int r=CSparse::SparseGetNRows(a); int c=CSparse::SparseGetNCols(a); matrix m=matrix::Zeros(r,c); CRowDouble sr; for(int row=0; row(1.1*epsf),"testiterativesparseunit.ap:189"); CIterativeSparse::SparseSolveSymmetricGMRES(a,isupper,b,gmresk,epsf,0,x,rep); CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:193"); CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:194"); if(err) return; CSparse::SparseSMV(crsa,isupper,x,b2); v=0; for(i=0; i(1.1*epsf),"testiterativesparseunit.ap:202"); //--- Nonsymmetric solve gmresk=5+CAlglib::HQRndUniformI(rs,2*n+1); CSparse::SparseMV(crsa,xref,b); CIterativeSparse::SparseSolverCreate(n,solver); CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk); CIterativeSparse::SparseSolverSetCond(solver,epsf,0); CIterativeSparse::SparseSolverSolve(solver,a,b); CIterativeSparse::SparseSolverResults(solver,x,rep); CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:215"); CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:216"); if(err) return; CSparse::SparseMV(crsa,x,b2); v=0; for(i=0; i(1.1*epsf),"testiterativesparseunit.ap:224"); CIterativeSparse::SparseSolveGMRES(a,b,gmresk,epsf,0,x,rep); CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:228"); CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:229"); if(err) return; CSparse::SparseMV(crsa,x,b2); v=0; for(i=0; i(1.1*epsf),"testiterativesparseunit.ap:237"); //--- Increase sparsity if(nnz==0) break; nnz/=2; } } //--- Check that initial point is actually used and improves the situation: //--- * generate big random problem //--- * solve it with GMRES(1), EpsF=0.1 //--- * restart using previous solution as initial point //--- Restarted solution must be achieved in 1 iteration //--- To be exact, we perform three tests: //--- * initial solution needs more than 1 iteration //--- * attempt to solve second time without starting point again needs more than 1 iteration //--- * solution with starting point needs 1 iteration n=100; nnz=10*n; epsf=0.001; gmresk=10; CSparse::SparseCreate(n,n,0,a); for(k=0; k1,"testiterativesparseunit.ap:294"); if(err) return; //--- Check termination request handling by the out-of-core API. //--- Solve long-running problem, at each OOC call randomly decide whether //--- we want to terminate the process or not. n=100; nnz=10*n; epsf=1.0E-50; gmresk=1; CSparse::SparseCreate(n,n,0,a); for(k=0; k(tol*(1+CAblasF::RDotV2(n,ax))),"testiterativesparseunit.ap:376"); CAp::SetErrorFlag(err,v>(rprev+tol),"testiterativesparseunit.ap:377"); rprev=v; nreports++; continue; } //--- check if(!CAp::Assert(requesttype==0,"SparseSolverSolve: integrity check 5364 failed")) return; CIterativeSparse::SparseSolverOOCGetRequestData(solver,xr); CSparse::SparseMV(crsa,xr,ax); nmv++; CIterativeSparse::SparseSolverOOCSendResult(solver,ax); } CIterativeSparse::SparseSolverOOCStop(solver,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testiterativesparseunit.ap:389"); CAp::SetErrorFlag(err,rep.m_nmv!=nmv,"testiterativesparseunit.ap:390"); CAp::SetErrorFlag(err,rep.m_iterationscount<(nreports-2)*gmresk,"testiterativesparseunit.ap:391"); CAp::SetErrorFlag(err,CAp::Len(repfirst)!=n,"testiterativesparseunit.ap:392"); CAp::SetErrorFlag(err,CAp::Len(replast)!=n,"testiterativesparseunit.ap:393"); if(err) return; for(i=0; i(100.0*CMath::m_machineepsilon),"testiterativesparseunit.ap:398"); CAp::SetErrorFlag(err,MathAbs(replast[i]-x[i])>(double)(100*CMath::m_machineepsilon),"testiterativesparseunit.ap:399"); } CIterativeSparse::SparseSolverSolve(solver,a,b); CIterativeSparse::SparseSolverResults(solver,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:403"); CIterativeSparse::SparseSolverSolveSymmetric(solver,a,CAlglib::HQRndNormal(rs)>0.0,b); CIterativeSparse::SparseSolverResults(solver,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:406"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestLinCGUnit { public: static const double m_e0; static const double m_maxcond; static bool TestLinCG(bool silent); private: static bool ComplexTest(bool silent); static bool ComplexRes(bool silent); static bool BasicTestX(bool silent); static bool TestRCorrectness(bool silent); static bool BasicTestIters(bool silent); static bool KrylovSubspaceTest(bool silent); static bool SparseTest(bool silent); static bool PrecondTest(bool silent); static void GramShmidtOrtnorm(CMatrixDouble &a,int n,int k,double eps,CMatrixDouble &b,int &k2); static bool FromBasis(CRowDouble &x,CMatrixDouble &basis,int n,int k,double eps); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ const double CTestLinCGUnit::m_e0=1.0E-6; const double CTestLinCGUnit::m_maxcond=30; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestLinCGUnit::TestLinCG(bool silent) { //--- create variables bool result; bool basictestxerrors; bool basictestiterserr; bool complexreserrors; bool complexerrors; bool rcorrectness; bool krylovsubspaceerr; bool sparseerrors; bool preconderrors; bool waserrors; //--- Tests basictestxerrors=BasicTestX(silent); basictestiterserr=BasicTestIters(silent); complexreserrors=ComplexRes(silent); complexerrors=ComplexTest(silent); rcorrectness=TestRCorrectness(silent); krylovsubspaceerr=KrylovSubspaceTest(silent); sparseerrors=SparseTest(silent); preconderrors=PrecondTest(silent); //--- report waserrors=((((((basictestxerrors||complexreserrors)||complexerrors)||rcorrectness)||basictestiterserr)||krylovsubspaceerr)||sparseerrors)||preconderrors; if(!silent) { Print("TESTING LinCG"); PrintResult("BasicTestX",!basictestxerrors); PrintResult("BasicTestIters",!basictestiterserr); PrintResult("ComplexResTest",!complexreserrors); PrintResult("ComplexTest",!complexerrors); PrintResult("R2 correctness",!rcorrectness); PrintResult("KrylovSubSpaceTest",!krylovsubspaceerr); PrintResult("SparseTest",!sparseerrors); PrintResult("PrecondTest",!preconderrors); //--- was errors? PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| Function for testing LinCGIteration function(custom option), | //| which to solve Ax=b (here A is random positive definite matrix | //| NxN, b is random vector). It uses the default stopping criterion | //| (RNormk; | //| 2. (rk,rm)=0 for any m<>k; | //| 3. (rk,pm)=0 for any m<>k; | //| INPUT: | //| Silent - if true then function output report | //+------------------------------------------------------------------+ bool CTestLinCGUnit::ComplexTest(bool silent) { //--- create variables bool result=false; double mx=100; int n=5; CLinCGState state; CLinCGReport rep; CMatrixDouble a; CRowDouble b; double c=0; CRowDouble x0; CRowDouble residual; double normofresidual=0; double sclr=0; double na=0; double nv0=0; double nv1=0; int sz=0; int i=0; int j=0; int k=0; int l=0; double tmp=0; CMatrixDouble mtx; CMatrixDouble mtp; CMatrixDouble mtr; double getrnorm=0; int numofit=0; for(sz=1; sz<=n; sz++) { //--- Generate: //--- * random A with norm NA (equal to 1.0), //--- * random right part B whose elements are uniformly distributed in [-MX,+MX] //--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX] c=15+15*CMath::RandomReal(); CMatGen::SPDMatrixRndCond(sz,c,a); na=1; b=vector::Zeros(sz); x0=vector::Zeros(sz); for(i=0; i::Zeros(sz+1,sz); //--- Start optimization, record its progress for further analysis //--- NOTE: we set update frequency of R to 2 in order to test that R is updated correctly CLinCG::LinCGCreate(sz,state); CLinCG::LinCGSetXRep(state,true); CLinCG::LinCGSetB(state,b); CLinCG::LinCGSetStartingPoint(state,x0); CLinCG::LinCGSetCond(state,0,sz); CLinCG::LinCGSetRUpdateFreq(state,2); numofit=0; getrnorm=CMath::m_maxrealnumber; while(CLinCG::LinCGIteration(state)) { if(state.m_needmv) { for(i=0; i=CAp::Rows(mtx)) { CAp::SetErrorFlag(result,true,"testlincgunit.ap:208"); return(result); } mtx.Row(numofit,state.m_x); getrnorm=state.m_r2; numofit++; } } CLinCG::LinCGResult(state,x0,rep); if(getrnorm!=rep.m_r2) { if(!silent) { PrintFormat("IterationsCount=%d;\nNMV=&d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype); PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::GetRNorm<>Rep.R2!(%0.2E<>%0.2E)\n",sz,c,getrnorm,rep.m_r2); } CAp::SetErrorFlag(result,true,"testlincgunit.ap:227"); return(result); } //--- Calculate residual, check result residual=vector::Zeros(sz); for(i=0; im_e0) { if(!silent) { PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype); PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::Discripancy[%d]>E0!(%0.2E>%0.2E)\n",sz,c,i,residual[i],m_e0); } CAp::SetErrorFlag(result,true,"testlincgunit.ap:254"); return(result); } normofresidual+=residual[i]*residual[i]; } if(MathAbs(normofresidual-rep.m_r2)>m_e0) { if(!silent) { PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype); PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::||NormOfResidual-Rep.R2||>E0!(%0.2E>%0.2E)\n",sz,c,MathAbs(normofresidual - rep.m_r2),m_e0); PrintFormat("NormOfResidual=%0.2E; Rep.R2=%0.2E\n",normofresidual,rep.m_r2); } CAp::SetErrorFlag(result,true,"testlincgunit.ap:270"); return(result); } //--- Check algorithm properties (conjugacy/orthogonality). //--- Here we use MtX which was filled during algorithm progress towards solution. //--- NOTE: this test is skipped when algorithm converged in less than SZ iterations. if(sz>1 && rep.m_iterationscount==sz) { mtp=matrix::Zeros(sz,sz); mtr=matrix::Zeros(sz,sz); for(i=0; i(m_e0*na*MathMax(nv0,1)*MathMax(nv1,1))) { if(!silent) { PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype); PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(Ap%d,p%d)!=0\n{{Sclr=%0.15f; NA=%0.15f NV0=%0.15f NV1=%0.15f;}}\n",sz,c,i,j,sclr,na,nv0,nv1); } CAp::SetErrorFlag(result,true,"testlincgunit.ap:327"); return(result); } } } //--- (ri,pj)=0? for(i=1; i(m_e0*MathMax(nv0,1)*MathMax(nv1,1))) { if(!silent) { PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype); PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(r%d,p%d)!=0\n{{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}}\n",sz,c,i,j,sclr,nv0,nv1); } CAp::SetErrorFlag(result,true,"testlincgunit.ap:360"); return(result); } } } //--- (ri,rj)=0? for(i=0; i(m_e0*MathMax(nv0,1)*MathMax(nv1,1))) { if(!silent) { PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype); PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(rm,rk)!=0\n{{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}}\n",sz,c,sclr,nv0,nv1); } CAp::SetErrorFlag(result,true,"testlincgunit.ap:392"); return(result); } } } } } if(!silent) PrintResult("ComplexTest",true); //--- return result return(result); } //+------------------------------------------------------------------+ //| This function prepare problem with a known solution 'Xs' | //| (A*Xs-b=0). There b is A*Xs. After, function check algorithm | //| result and 'Xs'. | //| There used two stopping criterions: | //| 1. achieved the required precision(StCrit=0); | //| 2. execution of the required number of iterations(StCrit=1). | //+------------------------------------------------------------------+ bool CTestLinCGUnit::ComplexRes(bool silent) { //--- create variables int sz=5; double mx=100; int nxp=100; CLinCGState s; CLinCGReport rep; CMatrixDouble a; CRowDouble b; CRowDouble xs; CRowDouble x0; double err=0; int n=0; double c=0; int i=0; int j=0; int stcrit=0; double tmp=0; double eps=0; int xp=0; for(xp=0; xp::Zeros(n); x0=vector::Zeros(n); xs=vector::Zeros(n); for(i=0; ieps) { if(!silent) { PrintResult("ComplexRes",false); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); Print("X and Xs...\n"); for(j=0; j::Zeros(n); x0=vector::Zeros(n); x00=vector::Zeros(n); x01=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; irtol) return(true); } } CLinCG::LinCGResult(s,b,rep); if(rep.m_iterationscount!=maxits) return(true); //--- return result return(false); } //+------------------------------------------------------------------+ //| This function check, that number of iterations are't more than | //| MaxIts. | //+------------------------------------------------------------------+ bool CTestLinCGUnit::BasicTestIters(bool silent) { //--- create variables int sz=5; double mx=100; CLinCGState s; CLinCGReport rep; CMatrixDouble a; CRowDouble b; CRowDouble x0; int n=0; double c=0; int i=0; int j=0; int iters=0; for(n=1; n<=sz; n++) { //--- Generate: //--- * random A with norm NA (equal to 1.0), //--- * random right part B whose elements are uniformly distributed in [-MX,+MX] //--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX] c=(m_maxcond-1)*CMath::RandomReal()+1; CMatGen::SPDMatrixRndCond(n,c,a); b=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; in+1) { if(!silent) { PrintResult("BasicTestIters",false); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); PrintFormat("Iters=%d\n",iters); } return(true); } //--- Restart problem c=(m_maxcond-1)*CMath::RandomReal()+1; CMatGen::SPDMatrixRndCond(n,c,a); for(i=0; in+1) { if(!silent) { PrintResult("BasicTestIters",false); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); PrintFormat("Iters=%d\n",iters); } return(true); } } //--- test has been passed if(!silent) PrintResult("BasicTestIters",true); //--- return result return(false); } //+------------------------------------------------------------------+ //| This function check, that programmed method is Krylov subspace | //| methed. | //+------------------------------------------------------------------+ bool CTestLinCGUnit::KrylovSubspaceTest(bool silent) { //--- create variables bool result=false; double eps=1.0E-6; int maxits=3; int sz=5; double mx=100; CLinCGState s; CLinCGReport rep; CMatrixDouble a; CRowDouble b; CRowDouble x0; CMatrixDouble ksr; CRowDouble r0; CRowDouble tarray; CMatrixDouble mtx; int n=0; double c=0; int i=0; int j=0; int l=0; int m=0; double tmp=0; double normr0=0; int numofit=0; int k2=0; //--- initialization for(n=1; n<=sz; n++) { //--- Generate: //--- * random A with unit norm //--- * cond(A) in [0.5*MaxCond, 1.0*MaxCond] //--- * random x0 and b such that |A*x0-b| is large enough for algorithm to make at least one iteration. //--- IMPORTANT: it is very important to have cond(A) both (1) not very large and //--- (2) not very small. Large cond(A) will make our problem ill-conditioned, //--- thus analytic properties won't hold. Small cond(A), from the other side, //--- will give us rapid convergence of the algorithm - in fact, too rapid. //--- Krylov basis will be dominated by numerical noise and test may fail. c=m_maxcond*(0.5*CMath::RandomReal()+0.5); CMatGen::SPDMatrixRndCond(n,c,a); mtx=matrix::Zeros(n+1,n); ksr=matrix::Zeros(n,n); r0=vector::Zeros(n); tarray=vector::Zeros(n); b=vector::Zeros(n); x0=vector::Zeros(n); //--- do { for(i=0; i::Zeros(n); xs=vector::Zeros(n); for(i=0; i=i) CSparse::SparseSet(uppera,i,j,a.Get(i,j)); if(j<=i) CSparse::SparseSet(lowera,i,j,a.Get(i,j)); } } CSparse::SparseConvertToCRS(uppera); CSparse::SparseConvertToCRS(lowera); //--- Test upper triangle CLinCG::LinCGCreate(n,s); CLinCG::LinCGSetCond(s,0,n); CLinCG::LinCGSolveSparse(s,uppera,true,b); CLinCG::LinCGResult(s,x0,rep); err=0; for(i=0; ieps) return(true); //--- Test lower triangle CLinCG::LinCGCreate(n,s); CLinCG::LinCGSetCond(s,0,n); CLinCG::LinCGSolveSparse(s,lowera,false,b); CLinCG::LinCGResult(s,x1,rep); err=0; for(i=0; ieps) return(true); } //--- return result return(false); } //+------------------------------------------------------------------+ //| Function for testing the preconditioned conjugate gradient method| //+------------------------------------------------------------------+ bool CTestLinCGUnit::PrecondTest(bool silent) { //--- create variables bool result; CLinCGState s; CLinCGReport rep; CMatrixDouble a; CMatrixDouble ta; CSparseMatrix sa; CRowDouble m; CMatrixDouble mtx; CMatrixDouble mtprex; CRowDouble de; CRowDouble rde; CRowDouble b; CRowDouble tb; CRowDouble d; CRowDouble xe; CRowDouble x0; CRowDouble tx0; CRowDouble err; //--- initialization int n=0; int sz=5; int numofit=0; double c=0; int i=0; int j=0; int k=0; double eps=0; bool bflag; //--- Test 1. //--- Preconditioned CG for A*x=b with preconditioner M=E*E' is algebraically //--- equivalent to non-preconditioned CG for (inv(E)*A*inv(E'))*z = inv(E)*b //--- with z=E'*x. //--- We test it by generating random preconditioner, running algorithm twice - //--- one time for original problem with preconditioner , another one for //--- modified problem without preconditioner. for(n=1; n<=sz; n++) { //--- Generate: //--- * random A with unit norm //--- * random positive definite diagonal preconditioner M //--- * dE=sqrt(M) //--- * rdE=dE^(-1) //--- * tA = rdE*A*rdE //--- * random x0 and b - for original preconditioned problem //--- * tx0 and tb - for modified problem c=(m_maxcond-1)*CMath::RandomReal()+1; CMatGen::SPDMatrixRndCond(n,c,a); ta=matrix::Zeros(n,n); mtx=matrix::Zeros(n+1,n); mtprex=matrix::Zeros(n+1,n); m=vector::Zeros(n); de=vector::Zeros(n); rde=vector::Zeros(n); for(i=0; i::Zeros(n); tb=vector::Zeros(n); x0=vector::Zeros(n); tx0=vector::Zeros(n); err=vector::Zeros(n); for(i=0; i=CAp::Rows(mtx)) { result=true; return(result); } mtx.Row(numofit,s.m_x); numofit=numofit+1; } } CLinCG::LinCGSetStartingPoint(s,tx0); CLinCG::LinCGSetB(s,tb); CLinCG::LinCGRestart(s); numofit=0; //--- while(CLinCG::LinCGIteration(s)) { if(s.m_needmv) { for(i=0; i=CAp::Rows(mtprex)) { result=true; return(result); } mtprex.Row(numofit,s.m_x); numofit=numofit+1; } } //--- Compare results - sequence of points generated when solving original problem with //--- points generated by modified problem. for(i=0; ieps) { if(!silent) { PrintResult("PrecondTest",false); PrintFormat("Size=%d\n",n); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); Print("X and X^...\n"); for(k=0; k::Zeros(n); for(i=0; i::Zeros(n); d=vector::Zeros(n); for(i=0; i::Zeros(n,n); CSparse::SparseCreate(n,n,n*n,sa); for(i=0; i(5.0E-2/d[i])) { result=true; return(result); } } CLinCG::LinCGSetPrecUnit(s); CLinCG::LinCGSolveSparse(s,sa,true,b); CLinCG::LinCGResult(s,x0,rep); if(rep.m_terminationtype>0) { bflag=false; for(i=0; i(5.0E-2/d[i]); if(!bflag) { result=true; return(result); } } CLinCG::LinCGSetPrecDiag(s); CLinCG::LinCGSolveSparse(s,sa,true,b); CLinCG::LinCGResult(s,x0,rep); if(rep.m_terminationtype<=0) { result=true; return(result); } for(i=0; i(5.0E-2/d[i])) { result=true; return(result); } } //--- test has been passed if(!silent) PrintResult("PrecondTest",true); //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| Orthogonalization by Gram - Shmidt method. | //+------------------------------------------------------------------+ void CTestLinCGUnit::GramShmidtOrtnorm(CMatrixDouble &a, int n, int k, double eps, CMatrixDouble &b, int &k2) { //--- create variables double scaling=0; double tmp=0; double e=0; int i=0; int j=0; int l=0; int m=0; double sc=0; //--- initialization k2=0; k2=0; scaling=0; b=matrix::Zeros(k,n); for(i=0; iscaling) scaling=tmp; } scaling=MathSqrt(scaling); e=eps*scaling; for(i=0; i::Zeros(k); //--- calculating NORM for X normx=CAblasF::RDotV2(n,x); normx=MathSqrt(normx); //--- Gram-Shmidt method GramShmidtOrtnorm(basis,n,k,MathPow(CMath::m_machineepsilon,0.75),ortnormbasis,k2); for(i=0; i(normx*eps)) return(false); } //--- return result return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestNormEstimatorUnit { public: static bool TestNormEstimator(bool silent); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestNormEstimatorUnit::TestNormEstimator(bool silent) { //--- create variables bool result; double tol=0.01; int maxmn=5; bool waserrors=false; int m=0; int n=0; int pass=0; int passcount=0; CMatrixDouble a; CRowInt rowsizes; CSparseMatrix s; double snorm=0; double enorm=0; double enorm2=0; int nbetter=0; double sigma=0; int i=0; int j=0; CNormEstimatorState e; CNormEstimatorState e2; //--- First test: algorithm must correctly determine matrix norm for(m=1; m<=maxmn; m++) { for(n=1; n<=maxmn; n++) { //--- Create estimator with quite large NStart and NIts. //--- It should guarantee that we converge to the correct solution. CNormEstimator::NormEstimatorCreate(m,n,15,15,e); //--- Try with zero A first CSparse::SparseCreate(m,n,1,s); CSparse::SparseConvertToCRS(s); CNormEstimator::NormEstimatorEstimateSparse(e,s); CNormEstimator::NormEstimatorResults(e,enorm); waserrors=waserrors||enorm!=0.0; //--- Choose random norm, try with non-zero matrix //--- with specified norm. snorm=MathExp(10*CMath::RandomReal()-5); CSparse::SparseCreate(m,n,1,s); if(m>=n) { //--- Generate random orthogonal M*M matrix, //--- use N leading columns as columns of A CMatGen::RMatrixRndOrthogonal(m,a); for(i=0; i(snorm*(1+tol)))||enorm<(snorm*(1-tol)); } } //--- NStart=10 should give statistically better results than NStart=1. //--- In order to test it we perform PassCount attempts to solve random //--- problem by means of two estimators: one with NStart=10 and another //--- one with NStart=1. Every time we compare two estimates and choose //--- better one. //--- Random variable NBetter is a number of cases when NStart=10 was better. //--- Under null hypothesis (no difference) it is binomially distributed //--- with mean PassCount/2 and variance PassCount/4. However, we expect //--- to have significant deviation to the right, in the area of larger //--- values. //--- NOTE: we use fixed N because this test is independent of problem size. n=3; CNormEstimator::NormEstimatorCreate(n,n,1,1,e); CNormEstimator::NormEstimatorCreate(n,n,10,1,e2); CNormEstimator::NormEstimatorSetSeed(e,0); CNormEstimator::NormEstimatorSetSeed(e2,0); nbetter=0; passcount=2000; sigma=5.0; for(pass=1; pass<=passcount; pass++) { snorm=MathPow(10.0,2*CMath::RandomReal()-1); CSparse::SparseCreate(n,n,1,s); CMatGen::RMatrixRndCond(n,2.0,a); for(i=0; i::Zeros(m,n); for(i=0; i::Zeros(m); for(i=0; i::Zeros(m); do { bnorm=0; for(i=0; i::Zeros(m,n); for(i=0; i::Zeros(m); do { bnorm=0; for(i=0; i::Zeros(m,n); for(i=0; i=pz) a.Set(i,i,2*CMath::RandomReal()-1); } for(i=1; i=pz) { if(diag==0) a.Set(i-1,i,2*CMath::RandomReal()-1); if(diag==1) a.Set(i,i-1,2*CMath::RandomReal()-1); } } a*=s0; b=vector::Zeros(m); do { bnorm=0; for(i=0; i0; | //| 2. A<>0, B=0; | //| 3. A=0, B=0. | //| If some part is not zero then it filled with ones. | //| INPUT: | //| Silent - if true then function output report | //+------------------------------------------------------------------+ bool CTestLinLSQRUnit::ZeroMatrixTest(bool silent) { //--- create variables CLinLSQRState s; CLinLSQRReport rep; CMatrixDouble a; CRowDouble b; CRowDouble x0; int sz=5; int n=0; int m=0; int i=0; int j=0; int nzeropart=0; for(m=1; m<=sz; m++) { for(n=1; n<=sz; n++) { for(nzeropart=0; nzeropart<=2; nzeropart++) { //--- Initialize A, b if(nzeropart==0 || nzeropart==2) a=matrix::Zeros(m,n); else a=matrix::Ones(m,n); if(nzeropart==1 || nzeropart==2) b=vector::Zeros(m); else b=vector::Ones(m); //--- LSQR CLinLSQR::LinLSQRCreate(m,n,s); CLinLSQR::LinLSQRSetB(s,b); CLinLSQR::LinLSQRSetCond(s,0,0,n); while(CLinLSQR::LinLSQRIteration(s)) { if(s.m_needmv) for(i=0; i::Zeros(m+n); rnorm=0; for(i=0; i::Zeros(n); lastx=vector::Zeros(n); //--- calculating by LSQR method CLinLSQR::LinLSQRCreate(m,n,s); CLinLSQR::LinLSQRSetB(s,b); CLinLSQR::LinLSQRSetCond(s,0,0,n); CLinLSQR::LinLSQRSetLambdaI(s,lambdai); CLinLSQR::LinLSQRSetXRep(s,true); CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=0,"testlinlsqrunit.ap:811"); while(CLinLSQR::LinLSQRIteration(s)) { if(s.m_needmv) { for(i=0; i(rnorm+1000*CMath::m_machineepsilon*MathMax(rnorm,1))) { CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:853"); return(result); } if(MathAbs(s.m_r2-tnorm)>eps) { CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:858"); return(result); } rnorm=s.m_r2; its++; //get X value from first iteration //--- and from last iteration. if(its==0) { for(i=0; i::Zeros(n); bnorm=0; for(i=0; imaxits || rep.m_terminationtype<=0) { if(!silent) { PrintResult("StoppingCriteriaTestn",false); PrintFormat("N=%d\n",n); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); } result=true; return(result); } //--- Test EpsB. //--- Set EpsB=eps, check that |r|::Zeros(n); CLinLSQR::LinLSQRCreate(n,n,s); CLinLSQR::LinLSQRSetB(s,b); CLinLSQR::LinLSQRSetCond(s,0,eps,0); while(CLinLSQR::LinLSQRIteration(s)) { if(s.m_needmv) { for(i=0; i(epsmod*bnorm) || rep.m_terminationtype<=0) { if(!silent) { PrintResult("StoppingCriteriaTest",false); PrintFormat("rkNorm=%0.2E\n",rknorm); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); } result=true; return(result); } } //--- Test EpsA. //--- Generate well conditioned underdetermined system with nonzero residual //--- at the solution. Such system can be generated by generating random //--- orthogonal matrix (N>=2) and using first N-1 columns as rectangular //--- system matrix, and sum of all columns with random non-zero coefficients //--- as right part. for(n=2; n<=sz; n++) { for(k0=-1; k0<=1; k0++) { for(k1=-1; k1<=1; k1++) { //--- Initialize A with non-unit norm 10^(10*K0), b with non-unit norm 10^(10*K1) anorm=MathPow(10,10*k0); CMatGen::RMatrixRndOrthogonal(n,a); a*=anorm; b=vector::Zeros(n); for(i=0; i::Zeros(n-1); rknorm=0; for(i=0; i(epsmod) || rep.m_terminationtype!=4) { if(!silent) { PrintResult("StoppingCriteriaTestn",false); PrintFormat("N=%d\n",n); PrintFormat("IterationsCount=%d\n",rep.m_iterationscount); PrintFormat("NMV=%d\n",rep.m_nmv); PrintFormat("TerminationType=%d\n",rep.m_terminationtype); } result=true; return(result); } } } } if(!silent) PrintResult("StoppingCriteriaTest",true); //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| This test compares LSQR for original system A*x=b against | //| CG for a modified system (A'*A)x = A*b. Both algorithms should | //| give same sequences of trial points (under exact arithmetics, or| //| for very good conditioned systems). | //| INPUT: | //| Silent - if true then function does not output report | //+------------------------------------------------------------------+ bool CTestLinLSQRUnit::AnalyticTest(bool silent) { //--- create variables bool result=false; CLinLSQRState s; CLinLSQRReport rep; CMatrixDouble a; CMatrixDouble xk; CMatrixDouble ap; CMatrixDouble r; CRowDouble b; CRowDouble tmp; int n=0; int m=0; int i=0; int j=0; int k=0; int smallk=4; int pointsstored=0; double v=0; double tol=1.0E-7; int i_=0; //--- Set: //--- * SmallK - number of steps to check, must be small number in order //--- to reduce influence of the rounding errors //--- * Tol - error tolerance for orthogonality/conjugacy criteria for(m=smallk; m<=smallk+5; m++) { for(n=smallk; n<=m; n++) { //--- Prepare problem: //--- * MxN matrix A, Mx1 vector B //--- * A is filled with random values from [-1,+1] //--- * diagonal elements are filled with large positive values //--- (should make system better conditioned) a=matrix::Zeros(m,n); b=vector::Zeros(m); for(i=0; i::Zeros(smallk+1,n); CLinLSQR::LinLSQRCreate(m,n,s); CLinLSQR::LinLSQRSetB(s,b); CLinLSQR::LinLSQRSetCond(s,0,0,smallk); CLinLSQR::LinLSQRSetXRep(s,true); pointsstored=0; while(CLinLSQR::LinLSQRIteration(s)) { if(s.m_needmv) { for(i=0; ij //--- * (p[i]^T)*A'*A*p[j]=0 for i<>j //--- where r[i]=(A'*A)*x[i]-A'*b is I-th residual , p[i] is I-th step. //--- In order to test these criteria we generate two matrices: //--- * (PointsStored-1)*M matrix AP (matrix of A*p products) //--- * (PointsStored-1)*N matrix R (matrix of residuals) //--- Then, we check that each matrix has orthogonal rows. ap=matrix::Zeros(pointsstored-1,m); r=matrix::Zeros(pointsstored-1,n); tmp=vector::Zeros(m); for(k=0; ktol; v=CAblasF::RDotRR(n,r,i,r,j); result=result||MathAbs(v)>tol; } } } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| This test checks behavior of the termination requests. Sets error| //| flag on failure, leaves it unchanged on success. | //+------------------------------------------------------------------+ void CTestLinLSQRUnit::TestTerminationRequests(bool &err) { //--- create variables CLinLSQRState s; CLinLSQRReport rep; CHighQualityRandState rs; int pass=0; CMatrixDouble a; CRowDouble b; CRowDouble x1; int n=0; int m=0; int callsleft=0; int reportsafterrequest=0; bool firstpointreported; int i=0; int j=0; CHighQualityRand::HQRndRandomize(rs); for(pass=1; pass<=50; pass++) { //--- Prepare problem callsleft=1+CHighQualityRand::HQRndUniformI(rs,10); n=callsleft+100+CHighQualityRand::HQRndUniformI(rs,50); m=n+CHighQualityRand::HQRndUniformI(rs,50); a=matrix::Zeros(m,n); b=vector::Zeros(m); for(i=0; i=0 | //| X - array[N], solution found by LSQR | //| EpsErr - tolerance for |A*x-b| | //| EpsOrt - tolerance for |A^T*Rk|/(|A|*|Rk|) | //| RESULT | //| True, for solution which passess all the tests | //+------------------------------------------------------------------+ bool CTestLinLSQRUnit::IsItGoodSolution(CMatrixDouble &a, CRowDouble &b,int m,int n, double lambdav, CRowDouble &x, double epserr,double epsort) { //--- create variables bool result; CMatrixDouble svda; CMatrixDouble u; CMatrixDouble vt; CRowDouble w; CRowDouble svdx; CRowDouble tmparr; CRowDouble r; int i=0; int j=0; int minmn=0; bool svdresult; double v=0; double rnorm=0; double bnorm=0; double anorm=0; double atrnorm=0; double xnorm=0; double svdxnorm=0; bool clause1holds; bool clause2holds; int i_=0; //--- Solve regularized problem with SVD solver svda=a; svda.Resize(m+n,n); for(i=m; i<=m+n-1; i++) { for(j=0; j::Zeros(n); tmparr=vector::Zeros(minmn); for(i=0; i::Zeros(m+n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); CSparse::SparseCreate(n,n,n*n,sa); for(i=0; i(5.0E-2/d[i])) { result=true; return(result); } } CLinLSQR::LinLSQRSetPrecUnit(s); CLinLSQR::LinLSQRSolveSparse(s,sa,b); CLinLSQR::LinLSQRResults(s,x0,rep); if(rep.m_terminationtype>0) { bflag=false; for(i=0; i(5.0E-2/d[i]); if(!bflag) { result=true; return(result); } } CLinLSQR::LinLSQRSetPrecDiag(s); CLinLSQR::LinLSQRSolveSparse(s,sa,b); CLinLSQR::LinLSQRResults(s,x0,rep); if(rep.m_terminationtype<=0) { result=true; return(result); } for(i=0; i(5.0E-2/d[i])) { result=true; return(result); } } //--- test has been passed result=false; return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestOptservUnit { public: static bool TestOptserv(bool silent); private: static void TestPrec(bool &waserrors); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestOptservUnit::TestOptserv(bool silent) { //--- create variables bool result; bool precerrors; bool waserrors; //--- initialization precerrors=false; TestPrec(precerrors); //--- report waserrors=precerrors; if(!silent) { Print("TESTING OPTSERV"); Print("TESTS:"); PrintResult("* PRECONDITIONERS",!precerrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| This function checks preconditioning functions | //| On failure sets error flag. | //+------------------------------------------------------------------+ void CTestOptservUnit::TestPrec(bool &waserrors) { //--- create variables int n=0; int k=0; int i=0; int j=0; int i0=0; int j0=0; int j1=0; double v=0; double rho=0; double theta=0; double tolg=0; CMatrixDouble va; CRowDouble vc; CRowDouble vd; CRowDouble vb; CRowDouble s0; CRowDouble s1; CRowDouble s2; CRowDouble g; CPrecBufLBFGS buf; CPrecBufLowRank lowrankbuf; CRowDouble norms; CMatrixDouble sk; CMatrixDouble yk; CMatrixDouble bk; CRowDouble bksk; CRowDouble tmp; CMatInvReport rep; CHighQualityRandState rs; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Test for inexact L-BFGS preconditioner. //--- We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V. //--- Different K's, from 0 to N, are tried. We test preconditioner //--- code which uses compact L-BFGS update against reference implementation //--- which uses non-compact BFGS scheme. //--- For each K we perform two tests: first for KxN non-zero matrix V, //--- second one for NxN matrix V with last N-K rows set to zero. Last test //--- checks algorithm's ability to handle zero updates. tolg=1.0E-9; for(n=1; n<=10; n++) { for(k=0; k<=n; k++) { //--- Prepare problem: //--- * VD, VC, VA, with VC/VA reordered by ascending of VC[i]*norm(VA[i,...])^2 //--- * trial vector S (copies are stored to S0,S1,S2) vd=vector::Zeros(n); s0=vector::Zeros(n); s1=vector::Zeros(n); s2=vector::Zeros(n); for(i=0; i::Zeros(k); for(i=0; inorms[j+1]) { //--- Swap elements J and J+1 norms.Swap(j,j+1); vc.Swap(j,j+1); va.SwapRows(j,j+1); } } } //--- Generate reference model and apply it to S2: //--- * generate approximate Hessian Bk //--- * calculate inv(Bk) //--- * calculate inv(Bk)*S2, store to S2 CApServ::RMatrixSetLengthAtLeast(sk,k,n); CApServ::RMatrixSetLengthAtLeast(yk,k,n); bk=matrix::Zeros(n,n); bksk=vector::Zeros(n); tmp=vector::Zeros(n); for(i=0; itolg,"testoptservunit.ap:236"); //--- Second test - N-K zero rows appended to V and rows are //--- randomly reordered. Doing so should not change result, //--- algorithm must be able to order rows according to second derivative //--- and skip zero updates. for(i=0; itolg,"testoptservunit.ap:259"); } } //--- Test for exact low-rank preconditioner. //--- We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V. //--- Different K's, from 0 to N, are tried. We test preconditioner //--- code which uses Woodbury update against reference implementation //--- which performs straightforward matrix inversion. //--- For each K we perform two tests: first for KxN non-zero matrix V, //--- second one for NxN matrix V with randomly appended N-K zero rows. //--- Last test checks algorithm's ability to handle zero updates. tolg=1.0E-9; for(n=1; n<=10; n++) { for(k=0; k<=n; k++) { //--- Prepare problem: //--- * VD, VC, VA //--- * trial vector S (copies are stored to S0,S1,S2) vd=vector::Zeros(n); s0=vector::Zeros(n); s1=vector::Zeros(n); s2=vector::Zeros(n); for(i=0; i::Zeros(n,n); tmp=vector::Zeros(n); for(i=0; i0)) { waserrors=true; return; } for(i=0; itolg,"testoptservunit.ap:341"); //--- Second test - N-K zero rows appended to V and rows are //--- randomly reordered. Doing so should not change result, //--- algorithm must be able to order rows according to second derivative //--- and skip zero updates. // for(i=0; itolg,"testoptservunit.ap:365"); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestCQModelsUnit { public: static bool TestCQModels(bool silent); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestCQModelsUnit::TestCQModels(bool silent) { //--- create variables bool result; bool eval0errors; bool eval1errors; bool eval2errors; bool newton0errors; bool newton1errors; bool newton2errors; bool waserrors; CConvexQuadraticModel s; int nkind=0; int kmax=0; int n=0; int k=0; int i=0; int pass=0; int j=0; double alpha=0; double theta=0; double tau=0; double v=0; double v2=0; double h=0; double f0=0; double mkind=0; double xtadx2=0; double noise=0; CMatrixDouble a; CMatrixDouble q; CRowDouble b; CRowDouble r; CRowDouble x; CRowDouble x0; CRowDouble xc; CRowDouble d; CRowDouble ge; CRowDouble gt; CRowDouble tmp0; CRowDouble adx; CRowDouble adxe; bool activeset[]; int i_=0; waserrors=false; //--- Eval0 test: unconstrained model evaluation eval0errors=false; for(n=1; n<=5; n++) { for(k=0; k<=2*n; k++) { //--- Allocate place a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); d=vector::Zeros(n); ge=vector::Zeros(n); gt=vector::Zeros(n); tmp0=vector::Zeros(n); if(k>0) { q=matrix::Zeros(k,n); r=vector::Zeros(k); } //--- Generate problem alpha=CMath::RandomReal()+1.0; theta=CMath::RandomReal()+1.0; tau=CMath::RandomReal()*CMath::RandomInteger(2); for(i=0; i0.5,alpha); CCQModels::CQMSetB(s,b); CCQModels::CQMSetQ(s,q,r,k,theta); CCQModels::CQMSetD(s,d,tau); //--- Evaluate and compare: //--- * X - random point //---*GE - "exact" gradient //--- * XTADX2 - x'*(alpha*A+tau*D)*x/2 //--- * ADXE - (alpha*A+tau*D)*x //--- * V - model value at X for(i=0; i::Zeros(n); for(i=0; i(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); CCQModels::CQMEvalX(s,x,v2,noise); CAp::SetErrorFlag(eval0errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); CAp::SetErrorFlag(eval0errors,(noise<0.0 || noise>(10000.0*CMath::m_machineepsilon)),StringFormat("%s: %d",__FUNCTION__,__LINE__)); v2=CCQModels::CQMXTADX2(s,x,tmp0); CAp::SetErrorFlag(eval0errors,MathAbs(xtadx2-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); CCQModels::CQMGradUnconstrained(s,x,gt); for(i=0; i(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); CCQModels::CQMADX(s,x,adx); for(i=0; i(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); } } waserrors=waserrors||eval0errors; //--- Eval1 test: constrained model evaluation eval1errors=false; for(n=1; n<=5; n++) { for(k=0; k<=2*n; k++) { //--- Allocate place a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); xc=vector::Zeros(n); ArrayResize(activeset,n); if(k>0) { q=matrix::Zeros(k,n); r=vector::Zeros(k); } //--- Generate problem alpha=CMath::RandomReal()+1.0; theta=CMath::RandomReal()+1.0; for(i=0; i0.5; for(j=i+1; j0.5,alpha); CCQModels::CQMSetB(s,b); CCQModels::CQMSetQ(s,q,r,k,theta); CCQModels::CQMSetActiveSet(s,xc,activeset); for(i=0; i(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMDebugConstrainedEvalT(s,x))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMDebugConstrainedEvalE(s,x))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); } } waserrors=waserrors||eval1errors; //--- Eval2 test: we generate empty problem and apply sequence of random transformations, //--- re-evaluating and re-checking model after each modification. //--- The purpose of such test is to ensure that our caching strategy works correctly. eval2errors=false; for(n=1; n<=5; n++) { kmax=2*n; a=matrix::Zeros(n,n); b=vector::Zeros(n); d=vector::Ones(n); x=vector::Zeros(n); xc=vector::Zeros(n); q=matrix< double>::Zeros(kmax,n); r=vector< double>::Zeros(kmax); ArrayResize(activeset,n); tmp0=vector::Zeros(n); alpha=0.0; theta=0.0; k=0; tau=1.0+CMath::RandomReal(); for(i=0; i0.5,alpha); } tau=0.0; d.Fill(0.0); CCQModels::CQMSetD(s,d,0.0); } else { if(mkind==2.0) { //--- Set non-zero A alpha=1.0+CMath::RandomReal(); for(i=0; i0.5,alpha); } else { if(mkind==3.0) { //--- Set zero A. //--- In case Tau=0, set non-zero D. if(tau==0.0) { tau=1.0+CMath::RandomReal(); for(i=0; i::Zeros(n,n); CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha); } else { if(mkind==4.0) { //--- Set B. for(i=0; i0.5; xc.Set(i,2*CMath::RandomReal()-1); } CCQModels::CQMSetActiveSet(s,xc,activeset); } else { if(mkind==7.0) { //--- Rewrite main diagonal if(alpha==0.0) alpha=1.0; for(i=0; i0.0) { for(i=0; i0.0) { for(i=0; i0.0) { for(i=0; i(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); v2=CCQModels::CQMDebugConstrainedEvalT(s,x); CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); v2=CCQModels::CQMDebugConstrainedEvalE(s,x); CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__)); } } waserrors=waserrors||eval2errors; //--- Newton0 test: unconstrained optimization newton0errors=false; for(n=1; n<=5; n++) { for(k=0; k<=2*n; k++) { //--- Allocate place a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); x0=vector::Zeros(n); if(k>0) { q=matrix::Zeros(k,n); r=vector::Zeros(k); } //--- Generate problem with known solution x0: //--- min f(x), //--- f(x) = 0.5*(x-x0)'*A*(x-x0) //--- = 0.5*x'*A*x + (-x0'*A)*x + 0.5*x0'*A*x0' alpha=CMath::RandomReal()+1.0; for(i=0; i0.5,alpha); CCQModels::CQMSetB(s,b); CCQModels::CQMSetQ(s,q,r,k,theta); CCQModels::CQMConstrainedOptimum(s,x); for(i=0; i(1.0E6*CMath::m_machineepsilon); } } waserrors=waserrors||newton0errors; //--- Newton1 test: constrained optimization newton1errors=false; h=1.0E-3; for(n=1; n<=5; n++) { for(k=0; k<=2*n; k++) { //--- Allocate place a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); xc=vector::Zeros(n); ArrayResize(activeset,n); if(k>0) { q=matrix::Zeros(k,n); r=vector::Zeros(k); } //--- Generate test problem with unknown solution. alpha=CMath::RandomReal()+1.0; for(i=0; i0.5; for(j=i+1; j0.5,alpha); CCQModels::CQMSetB(s,b); CCQModels::CQMSetQ(s,q,r,k,theta); CCQModels::CQMSetActiveSet(s,xc,activeset); if(CCQModels::CQMConstrainedOptimum(s,x)) { //--- Check that constraints are satisfied, //--- and that solution is true optimum f0=CCQModels::CQMEval(s,x); for(i=0; i::Zeros(n); b=vector::Zeros(n); x=vector::Zeros(n); x0=vector::Zeros(n); ArrayResize(activeset,n); if(k>0) { q=matrix::Zeros(k,n); r=vector::Zeros(k); } tau=1+CMath::RandomReal(); theta=1+CMath::RandomReal(); for(i=0; i0.5; } for(i=0; i(1000.0*CMath::m_machineepsilon)); } //--- Check that constrained evaluation at some point gives correct results for(i=0; i(10000.0*CMath::m_machineepsilon); v2=CCQModels::CQMDebugConstrainedEvalT(s,x); newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon); v2=CCQModels::CQMDebugConstrainedEvalE(s,x); newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon); } else newton2errors=true; } } waserrors=waserrors||newton2errors; //--- report if(!silent) { Print("TESTING CONVEX QUADRATIC MODELS"); PrintResult("Eval0 test",!eval0errors); PrintResult("Eval1 test",!eval1errors); PrintResult("Eval2 test",!eval2errors); PrintResult("Newton0 test",!newton0errors); PrintResult("Newton1 test",!newton1errors); PrintResult("Newton2 test",!newton2errors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestSNNLSUnit { public: static bool TestSNNLS(bool silent); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestSNNLSUnit::TestSNNLS(bool silent) { //--- create variables bool result; bool test0errors=false; bool test1errors=false; bool test2errors=false; bool testnewtonerrors=false; bool waserrors=false; double eps=0; int i=0; int j=0; int k=0; double v=0; int ns=0; int nd=0; int nr=0; CMatrixDouble densea; CMatrixDouble effectivea; bool isconstrained[]; CRowDouble g; CRowDouble b; CRowDouble x; CRowDouble xs; CSNNLSSolver s; CHighQualityRandState rs; double rho=0; double xtol=0; int nmax=10; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Test 2 (comes first because it is very basic): //--- * NS=0 //--- * ND in [1,NMAX] //--- * NR=ND //--- * DenseA is diagonal with positive entries //--- * B is random //--- * random constraints //--- Exact solution is known and can be tested eps=1.0E-12; for(nd=1; nd<=nmax; nd++) { //--- Generate problem ns=0; nr=nd; densea=matrix::Zeros(nd,nd); b=vector::Zeros(nd); ArrayResize(isconstrained,nd); for(i=0; i0.5; } //--- Solve with SNNLS solver CSNNLS::SNNLSInit(0,0,0,s); CSNNLS::SNNLSSetProblem(s,densea,b,0,nd,nd); for(i=0; ieps,"testsnnlsunit.ap:86"); CAp::SetErrorFlag(test2errors,x[i]<0.0,"testsnnlsunit.ap:87"); } else CAp::SetErrorFlag(test2errors,MathAbs(x[i]-b[i]/densea.Get(i,i))>eps,"testsnnlsunit.ap:90"); } } //--- Test 0: //--- * NS in [0,NMAX] //--- * ND in [0,NMAX] //--- * NR in [NS,NS+ND+NMAX] //--- * NS+ND>0, NR>0 //--- * about 50% of variables are constrained //--- * we check that constrained gradient is small at the solution eps=1.0E-5; for(ns=0; ns<=nmax; ns++) { for(nd=0; nd<=nmax; nd++) { for(nr=ns; nr<=ns+nd+nmax; nr++) { //--- Skip NS+ND=0, NR=0 if(ns+nd==0) continue; if(nr==0) continue; //--- Generate problem: //--- * DenseA, array[NR,ND] //--- * EffectiveA, array[NR,NS+ND] //--- * B, array[NR] //--- * IsConstrained, array[NS+ND] if(nd>0) { densea=matrix::Zeros(nr,nd); for(i=0; i::Zeros(nr,ns+nd); for(i=0; i<=ns-1; i++) effectivea.Set(i,i,1.0); for(i=0; i::Zeros(nr); for(i=0; i0.5; //--- Solve with SNNLS solver CSNNLS::SNNLSInit(0,0,0,s); CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr); for(i=0; i::Zeros(ns+nd); for(i=0; i0.0) CAp::SetErrorFlag(test0errors,MathAbs(g[i])>eps,"testsnnlsunit.ap:179"); else CAp::SetErrorFlag(test0errors,g[i]<(-eps),"testsnnlsunit.ap:181"); } } } } //--- Test 1: ability of the solver to take very short steps. //--- We solve problem similar to one solver in test 0, but with //--- progressively decreased magnitude of variables. We generate //--- problem with already-known solution and compare results against it. xtol=1.0E-7; for(ns=0; ns<=nmax; ns++) { for(nd=0; nd<=nmax; nd++) { for(nr=ns; nr<=ns+nd+nmax; nr++) { for(k=0; k<=20; k++) { //--- Skip NS+ND=0, NR=0 //--- Skip degenerate problems (NR0) { densea=matrix::Zeros(nr,nd); for(i=0; i::Zeros(nr,ns+nd); for(i=0; i<=ns-1; i++) effectivea.Set(i,i,1.0); for(i=0; i::Zeros(ns+nd); ArrayResize(isconstrained,ns+nd); for(i=0; i0.0; } b=vector::Zeros(nr); for(i=0; i(double)(rho*xtol),"testsnnlsunit.ap:269"); } } } } //--- Test for Newton phase: //--- * NS in [0,NMAX] //--- * ND in [0,NMAX] //--- * NR in [NS,NS+ND+NMAX] //--- * NS+ND>0, NR>0 //--- * all variables are unconstrained //--- * S.DebugMaxNewton is set to 1, S.RefinementIts is set to 1, //--- i.e. algorithm is terminated after one Newton iteration, and no //--- iterative refinement is used. //--- * we test that gradient is small at solution, i.e. one Newton iteration //--- on unconstrained problem is enough to find solution. In case of buggy //--- Newton solver one iteration won't move us to the solution - it may //--- decrease function value, but won't find exact solution. //--- This test is intended to catch subtle bugs in the Newton solver which //--- do NOT prevent algorithm from converging to the solution, but slow it //--- down (convergence becomes linear or even slower). eps=1.0E-4; for(ns=0; ns<=nmax; ns++) { for(nd=0; nd<=nmax; nd++) { for(nr=ns; nr<=ns+nd+nmax; nr++) { //--- Skip NS+ND=0, NR=0 if(ns+nd==0) continue; if(nr==0) continue; //--- Generate problem: //--- * DenseA, array[NR,ND] //--- * EffectiveA, array[NR,NS+ND] //--- * B, array[NR] //--- * IsConstrained, array[NS+ND] if(nd>0) { densea=matrix::Zeros(nr,nd); for(i=0; i::Zeros(nr,ns+nd); for(i=0; i<=ns-1; i++) effectivea.Set(i,i,1.0); for(i=0; i::Zeros(nr); for(i=0; i::Zeros(ns+nd); for(i=0; ieps,"testsnnlsunit.ap:358"); } } } //--- report waserrors=((test0errors||test1errors)||test2errors)||testnewtonerrors; if(!silent) { Print("TESTING SPECIAL NNLS SOLVER"); PrintResult("TEST 0",!test0errors); PrintResult("TEST 1",!test1errors); PrintResult("TEST 2",!test2errors); PrintResult("NEWTON PHASE",!testnewtonerrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestSActiveSetsUnit { public: static bool TestSActiveSets(bool silent); private: static void CTestSActiveSetsUnit::TestSpecProperties(bool &err); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestSActiveSetsUnit::TestSActiveSets(bool silent) { //--- create variables bool result; bool waserrors; bool specerr=false; //--- initialization TestSpecProperties(specerr); //--- report waserrors=specerr; if(!silent) { Print("TESTING ACTIVE SETS"); PrintResult("* SPECIAL PROPERTIES",!specerr); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| This function tests special properties. | //| On failure sets Err to True (leaves it unchanged otherwise) | //+------------------------------------------------------------------+ void CTestSActiveSetsUnit::TestSpecProperties(bool &err) { //--- create variables int i=0; int j=0; int n=0; int nec=0; int nic=0; double v=0; double vv=0; CSActiveSet state; CHighQualityRandState rs; CRowDouble bl; CRowDouble bu; CRowDouble x; CRowDouble s; CMatrixDouble c; CRowInt ct; int scaletype=0; int pass=0; int distortidx=0; double distortmag=0; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- N-dimensional problem with Ne equality and Ni inequality constraints. //--- Check that SActiveSet object uses efficient algorithm //--- to determine initial point: it avoids expensive (N+Ni)-dimensional //--- QP subproblem when initial point is feasible w.r.t. constraints. //--- In order to do so we try to find initial point for a problem with //--- 2 equality constraints and 1000000 inequality constraints (+box //--- constraints). Inefficient algorithm will simply fail to allocate //--- enough memory, so we do not have to perform any checks here. n=5; nec=2; nic=1000000; x=vector::Zeros(n); bl=vector::Zeros(n); bu=vector::Zeros(n); for(scaletype=0; scaletype<=1; scaletype++) { //--- Generate problem for(i=0; i::Zeros(nec+nic,n+1); ct.Resize(nec+nic); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(scaletype=0; scaletype<=1; scaletype++) { //--- Generate problem, prepare distortion which introduces infeasibility for(i=0; i::Zeros(nec+nic,n+1); ct.Resize(nec+nic); for(i=0; i0.0)) { err=true; return; } distortmag=1.0E7*CMath::m_machineepsilon/distortmag; //--- Apply scaling (if needed), then randomly multiply C by //--- some large/small value in order to distort magnitude of //--- linear constraints. //--- Correct SActiveSet implementation must be able to renormalize //--- constraints prior to checking feasibility of initial point. s=vector::Zeros(n); for(i=0; i0 (random) | //| b = 0 random bounds (either no bounds, one bound, two bounds | //| a::Zeros(sn); xori=vector::Zeros(sn); stx=vector::Zeros(sn); db=vector::Zeros(sn); ub=vector::Zeros(sn); a=matrix::Zeros(sn,sn); for(i=0; i<=nexp; i++) { //--- create diagonal matrix for(k=0; keps || x[j]ub[j]) return(true); } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', | //| 'MinQPSetQuadraticTerm','MinQPSetOrigin','MinQPSetStartingPoint',| //| 'MinQPOptimize', 'MinQPResults'. | //| Test problem: | //| A = positive-definite matrix, obtained by 'SPDMatrixRndCond' | //| function | //| b <> 0 without bounds random start point dimension-from 1 to 5.| //+------------------------------------------------------------------+ bool CTestMinQPUnit::FuncTest1(void) { //--- create variables double eps=0.001; int msn=5; int c2=1000; int nexp=1000; CMinQPState state; int sn=0; int i=0; int j=0; int k=0; CMatrixDouble a; CRowDouble ub; CRowDouble db; CRowDouble x; CRowDouble tx; CRowDouble stx; CRowDouble xori; CRowDouble xoric; CMinQPReport rep; CRowDouble b; for(sn=1; sn<=msn; sn++) { b=vector::Zeros(sn); tx=vector::Zeros(sn); xori=vector::Zeros(sn); xoric=vector::Zeros(sn); stx=vector::Zeros(sn); for(i=0; i<=nexp; i++) { //--- create simmetric matrix 'A' CMatGen::SPDMatrixRndCond(sn,MathExp(CMath::RandomReal()*MathLog(c2)),a); CMinQP::MinQPCreate(sn,state); SetRandomAlgoBC(state); CMinQP::MinQPSetQuadraticTerm(state,a,false); for(j=0; jeps) return(true); } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', | //| 'MinQPSetQuadraticTerm', 'MinQPSetBC', 'MinQPSetOrigin', | //| 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'. | //| Test problem: | //| A = positive-definite matrix, obtained by 'SPDMatrixRndCond' | //| function | //| b <> 0 boundary constraints random start point dimension - | //| from 1 to 5. | //+------------------------------------------------------------------+ bool CTestMinQPUnit::FuncTest2(void) { //--- create variables CMinQPState state; double eps=0.001; int msn=5; int nexp=1000; int c2=1000; double maxstb=10; double maxnb=1000; double minnb=-1000; int sn=0; int i=0; int j=0; int k=0; int infd=0; double anti=0; CMatrixDouble a; CRowDouble ub; CRowDouble db; CRowDouble x; CRowDouble tmpx; CRowDouble stx; CRowDouble xori; CRowDouble xoric; CMinQPReport rep; CRowDouble b; CRowDouble g; CRowDouble c; CRowDouble y0; CRowDouble y1; for(sn=1; sn<=msn; sn++) { tmpx=vector::Zeros(sn); b=vector::Zeros(sn); c=vector::Zeros(sn); g=vector::Zeros(sn); xori=vector::Zeros(sn); xoric=vector::Zeros(sn); stx=vector::Zeros(sn); db=vector::Zeros(sn); ub=vector::Zeros(sn); y0=vector::Zeros(sn); y1=vector::Zeros(sn); for(i=0; i<=nexp; i++) { //--- create simmetric matrix 'A' CMatGen::SPDMatrixRndCond(sn,MathExp(CMath::RandomReal()*MathLog(c2)),a); CMinQP::MinQPCreate(sn,state); SetRandomAlgoBC(state); CMinQP::MinQPSetQuadraticTerm(state,a,false); for(j=0; jeps) return(true); } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| ConsoleTest. | //+------------------------------------------------------------------+ bool CTestMinQPUnit::ConsoleTest(void) { //--- create variables double eps=0.001; int msn=2; CMinQPState state; int nexp=0; int sn=0; int i=0; int j=0; int k=0; CMatrixDouble a; CRowDouble ub; CRowDouble db; CRowDouble x; CRowDouble stx; CRowDouble xori; CRowDouble xoric; CMinQPReport rep; CRowDouble b; CRowDouble g; CRowDouble y0; CRowDouble y1; double c=0; double anti=0; for(sn=2; sn<=msn; sn++) { b=vector::Zeros(sn); g=vector::Zeros(sn); xori=vector::Zeros(sn); xoric=vector::Zeros(sn); stx=vector::Zeros(sn); db=vector::Zeros(sn); ub=vector::Zeros(sn); y0=vector::Zeros(sn); y1=vector::Zeros(sn); for(i=0; i<=nexp; i++) { //--- create simmetric matrix 'A' a=matrix::Identity(sn,sn); CMinQP::MinQPCreate(sn,state); SetRandomAlgoBC(state); CMinQP::MinQPSetQuadraticTerm(state,a,false); for(j=0; jeps) return(true); } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| This function performs tests specific for QuickQP solver | //| Returns True on failure. | //+------------------------------------------------------------------+ bool CTestMinQPUnit::QuickQPTests(void) { //--- create variables bool result=false; CMinQPState state; CMinQPReport rep; int n=0; int pass=0; int i=0; int j=0; int k=0; double v=0; double g=0; double gnorm=0; bool flag; int origintype=0; int scaletype=0; bool isupper; bool issparse; int itscnt=0; CRowInt nlist; int nidx=0; CMatrixDouble a; CMatrixDouble za; CMatrixDouble fulla; CMatrixDouble halfa; CMatrixDouble c; CSparseMatrix sa; CRowInt ct; CRowDouble b; CRowDouble zb; CRowDouble bndl; CRowDouble bndu; CRowDouble x0; CRowDouble x1; CRowDouble xend0; CRowDouble xend1; CRowDouble xori; CRowDouble xz; CRowDouble s; double eps=0; CHighQualityRandState rs; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Convex unconstrained test: //--- * N dimensions //--- * positive-definite A //--- * algorithm randomly choose dense or sparse A, and for //--- sparse matrix it randomly choose format. //--- * random B with normal entries //--- * initial point is random, feasible //--- * random origin (zero or non-zero) and scale (unit or //--- non-unit) are generated eps=1.0E-5; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem origintype=CHighQualityRand::HQRndUniformI(rs,2); scaletype=CHighQualityRand::HQRndUniformI(rs,2); isupper=CHighQualityRand::HQRndUniformR(rs)<0.5; issparse=CHighQualityRand::HQRndUniformR(rs)<0.5; CMatGen::SPDMatrixRndCond(n,1.0E3,fulla); halfa=matrix::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); x0=vector::Zeros(n); xori=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i0.5); CMinQP::MinQPSetLinearTerm(state,b); if(issparse) CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper); else CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper); if(origintype!=0) CMinQP::MinQPSetOrigin(state,xori); if(scaletype!=0) CMinQP::MinQPSetScale(state,s); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5703"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; ieps,"testminqpunit.ap:5721"); } } //--- Convex test: //--- * N dimensions //--- * random number (0..N) of random boundary constraints //--- * positive-definite A //--- * algorithm randomly choose dense or sparse A, and for //--- sparse matrix it randomly choose format. //--- * random B with normal entries //--- * initial point is random, feasible //--- * random origin (zero or non-zero) and scale (unit or //--- non-unit) are generated eps=1.0E-5; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem origintype=CHighQualityRand::HQRndUniformI(rs,2); scaletype=CHighQualityRand::HQRndUniformI(rs,2); isupper=CHighQualityRand::HQRndUniformR(rs)<0.5; issparse=CHighQualityRand::HQRndUniformR(rs)<0.5; CMatGen::SPDMatrixRndCond(n,1.0E3,fulla); halfa=matrix::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xori=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i0.5); CMinQP::MinQPSetLinearTerm(state,b); if(issparse) CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper); else CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper); if(origintype!=0) CMinQP::MinQPSetOrigin(state,xori); if(scaletype!=0) CMinQP::MinQPSetScale(state,s); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5818"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if(x1[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(result,x1[i]bndu[i],"testminqpunit.ap:5839"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5842"); } } //--- Strongly non-convex test: //--- * N dimensions, N>=2 //--- * box constraints, x[i] in [-1,+1] //--- * A = A0-0.5*I, where A0 is SPD with unit norm and smallest //--- singular value equal to 1.0E-3, I is identity matrix //--- * random B with normal entries //--- * initial point is random, feasible //--- We perform two tests: //--- * unconstrained problem must be recognized as unbounded //--- * constrained problem can be successfully solved //--- NOTE: it is important to have N>=2, because formula for A //--- can be applied only to matrix with at least two //--- singular values eps=1.0E-5; for(n=2; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem CMatGen::SPDMatrixRndCond(n,1.0E3,fulla); for(i=0; i::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.5); CMinQP::MinQPSetLinearTerm(state,b); if(CHighQualityRand::HQRndNormal(rs)>0.0) CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper); else CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:5907"); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5911"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if(x1[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(result,x1[i]bndu[i],"testminqpunit.ap:5931"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5934"); } } //--- Basic semi-definite test: //--- * N dimensions, N>=2 //--- * box constraints, x[i] in [-1,+1] //--- [ 1 1 ... 1 1 ] //--- * A = [ ... ... ... ], with one (random) diagonal entry set to -1 //--- [ 1 1 ... 1 1 ] //--- * random B with normal entries //--- * initial point is random, feasible //--- We perform two tests: //--- * unconstrained problem must be recognized as unbounded //--- * constrained problem must be recognized as bounded and //--- successfully solved //--- Both problems require subtle programming when we work //--- with semidefinite QP. //--- NOTE: unlike BLEIC-QP algorthm, QQP may detect unboundedness //--- of the problem when started from any x0, with any b. //--- BLEIC-based solver requires carefully chosen x0 and b //--- to find direction of zero curvature, but this solver //--- can find it from any point. nlist.Resize(12); nlist.Set(0,2); nlist.Set(1,3); nlist.Set(2,4); nlist.Set(3,5); nlist.Set(4,6); nlist.Set(5,7); nlist.Set(6,8); nlist.Set(7,9); nlist.Set(8,10); nlist.Set(9,20); nlist.Set(10,40); nlist.Set(11,80); eps=1.0E-5; for(nidx=0; nidx::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Ones(n,n); j=CHighQualityRand::HQRndUniformI(rs,n); a.Set(j,j,-1.0); DenseToSparse(a,n,sa); //--- Solve problem: //--- * without constraints we expect failure //--- * with constraints algorithm must succeed CMinQP::MinQPCreate(n,state); CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5); CMinQP::MinQPSetLinearTerm(state,b); if(CHighQualityRand::HQRndNormal(rs)>0.0) CMinQP::MinQPSetQuadraticTerm(state,a,true); else CMinQP::MinQPSetQuadraticTermSparse(state,sa,true); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6017"); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6021"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if(x1[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(result,x1[i]bndu[i],"testminqpunit.ap:6043"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6046"); } } //--- Linear (zero-quadratic) test: //--- * N dimensions, N>=1 //--- * box constraints, x[i] in [-1,+1] //--- * A = 0 //--- * random B with normal entries //--- * initial point is random, feasible //--- We perform two tests: //--- * unconstrained problem must be recognized as unbounded //--- * constrained problem can be successfully solved //--- NOTE: we may explicitly set zero A, or assume that by //--- default it is zero. During test we will try both //--- ways. eps=1.0E-5; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.5); CMinQP::MinQPSetLinearTerm(state,b); if(CHighQualityRand::HQRndNormal(rs)>0.0) { a=matrix::Zeros(n,n); CMinQP::MinQPSetQuadraticTerm(state,a,true); } CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6104"); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6108"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. for(i=0; i0.0 && x1[i]>bndl[i],"testminqpunit.ap:6118"); CAp::SetErrorFlag(result,b[i]<0.0 && x1[i]::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.0) { bndl.Set(i,AL_NEGINF); bndu.Set(i,AL_POSINF); } else { bndl.Set(i,CHighQualityRand::HQRndNormal(rs)); bndu.Set(i,bndl[i]); } } //--- Solve problem //--- NOTE: we modify internal structures of QQP solver in order //--- to deactivate CG phase CMinQP::MinQPCreate(n,state); CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,1,true); state.m_qqpsettingsuser.m_cgphase=false; CMinQP::MinQPSetLinearTerm(state,b); if(CHighQualityRand::HQRndNormal(rs)>0.0) CMinQP::MinQPSetQuadraticTerm(state,a,CHighQualityRand::HQRndNormal(rs)>0.0); else CMinQP::MinQPSetQuadraticTermSparse(state,sa,CHighQualityRand::HQRndNormal(rs)>0.0); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6179"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if(x1[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(result,x1[i]bndu[i],"testminqpunit.ap:6200"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6203"); } //--- Test for Newton phase of QQP algorithm - we test that Newton //--- updates work correctly, i.e. that CNewtonUpdate() internal //--- function correctly updates inverse Hessian matrix. //--- To test it we: //--- * solve ill conditioned convex QP problem //--- * with unconstrained solution XZ whose components are within [-0.5,+0.5] //--- * with one inequality constraint X[k]>=5 //--- * with initial point such that: //--- * X0[i] = 100 for i<>k //--- * X0[k] = 5+1.0E-5 //--- * with number of outer iterations limited to just 1 //--- * and with CG phase turned off (we modify internal structures //--- of the QQP solver in order to make it) //--- The idea is that single Newton step is not enough to find solution, //--- but with just one update we can move exactly to the solution. //--- We perform two tests: //--- * first one with State.QQP.NewtMaxIts set to 1, in order to //--- make sure that algorithm fails with just one iteration //--- * second one with State.QQP.NewtMaxIts set to 2, in order to //--- make sure that algorithm converges when it can perform update eps=1.0E-5; for(pass=1; pass<=10; pass++) { //--- Generate problem n=20+CHighQualityRand::HQRndUniformI(rs,20); CMatGen::SPDMatrixRndCond(n,1.0E5,a); DenseToSparse(a,n,sa); CSparse::SparseConvertToCRS(sa); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xz=vector::Zeros(n); for(i=0; i0.0); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetStartingPoint(state,x0); //--- Solve problem. First time, with no Newton updates. //--- It must fail. //--- NOTE: we modify internal structures of QQP solver in order //--- to deactivate CG phase and turn off Newton updates. state.m_qqpsettingsuser.m_cgphase=false; state.m_qqpsettingsuser.m_cnphase=true; state.m_qqpsettingsuser.m_cnmaxupdates=0; CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6282"); if(result) return(result); flag=false; TestBCGradAndFeasibility(a,b,bndl,bndu,n,x1,eps,flag); CAp::SetErrorFlag(result,!flag,"testminqpunit.ap:6287"); //--- Now with Newton updates - it must succeeed. state.m_qqpsettingsuser.m_cgphase=false; state.m_qqpsettingsuser.m_cnmaxupdates=n; CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6296"); if(result) return(result); flag=false; TestBCGradAndFeasibility(a,b,bndl,bndu,n,x1,eps,flag); CAp::SetErrorFlag(result,flag,"testminqpunit.ap:6301"); } //--- Check that problem with general constraints results in //--- correct error code (-5 should be returned). c=matrix::Ones(1,3); ct.Resize(1); c.Set(0,2,2.0); ct.Set(0,0); CMinQP::MinQPCreate(2,state); CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5); CMinQP::MinQPSetLC(state,c,ct,1); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype!=-5,"testminqpunit.ap:6319"); //--- Test sparse functionality. QQP solver must perform //--- same steps independently of matrix type (dense or sparse). //--- We generate random unconstrained test problem and solve it //--- twice - first time we solve dense version, second time - //--- sparse version is solved. //--- During this test we: //--- * use stringent stopping criteria (one outer iteration) //--- * turn off Newton phase of the algorithm to slow down //--- convergence eps=1.0E-3; itscnt=1; n=20; isupper=CMath::RandomReal()>0.5; CMatGen::SPDMatrixRndCond(n,1.0E3,za); CSparse::SparseCreate(n,n,0,sa); a=matrix::Zeros(n,n); for(i=0; i=i && isupper) { CSparse::SparseSet(sa,i,j,za.Get(i,j)); a.Set(i,j,za.Get(i,j)); } if(j<=i && !isupper) { CSparse::SparseSet(sa,i,j,za.Get(i,j)); a.Set(i,j,za.Get(i,j)); } } } b=vector::Zeros(n); s=vector::Zeros(n); for(i=0; ieps,"testminqpunit.ap:6437"); //--- Test scale-invariance. QQP performs same steps on scaled and //--- unscaled problems (assuming that scale of the variables is known). //--- We generate random scale matrix S and random well-conditioned and //--- well scaled matrix A. Then we solve two problems: //--- (1) f = 0.5*x'*A*x+b'*x //--- (identity scale matrix is used) //--- and //--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y //--- (scale matrix S is used) //--- Solution process is started from X=0, we perform ItsCnt=1 outer //--- iterations with Newton phase turned off (to slow down convergence; //--- we want to prevent algorithm from converging to exact solution which //--- is exactly same for both problems; the idea is to test that same //--- intermediate tests are taken). //--- As result, we must get S*x=y eps=1.0E-3; itscnt=1; n=100; s=vector::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i::Zeros(n); zb=vector::Zeros(n); for(i=0; ieps,"testminqpunit.ap:6494"); //--- Test that QQP can efficiently use sparse matrices (i.e. it is //--- not disguised version of some dense QP solver). In order to test //--- it we create very large and very sparse problem (diagonal matrix //--- with N=40.000) and perform 10 iterations of QQP solver. //--- In case QP solver uses some form of dense linear algebra to solve //--- this problem, it will take TOO much time to solve it. And we will //--- notice it by EXTREME slowdown during testing. n=40000; CSparse::SparseCreate(n,n,0,sa); for(i=0; i::Zeros(n); for(i=0; i0.5); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetQuadraticTermSparse(state,sa,true); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend0,rep); //--- return result return(result); } //+------------------------------------------------------------------+ //| This function performs tests specific for BLEIC solver | //| Returns True on error, False on success. | //+------------------------------------------------------------------+ bool CTestMinQPUnit::BLEICTests(void) { //--- create variables bool result=false; CMinQPState state; CMinQPReport rep; CRowInt nlist; int nidx=0; CMatrixDouble a; CMatrixDouble za; CMatrixDouble c; CRowDouble b; CRowDouble zb; CRowDouble bndl; CRowDouble bndu; CRowDouble s; CRowDouble x; CRowInt ct; CSparseMatrix sa; int n=0; CRowDouble x0; CRowDouble x1; CHighQualityRandState rs; int i=0; int j=0; int pass=0; CRowDouble xend0; CRowDouble xend1; double eps=0; double v=0; double g=0; double gnorm=0; int itscnt=0; bool isupper; CHighQualityRand::HQRndRandomize(rs); //--- Test sparse functionality. BLEIC-based solver must perform //--- same steps independently of matrix type (dense or sparse). //--- We generate random unconstrained test problem and solve it //--- twice - first time we solve dense version, second time - //--- sparse version is solved. eps=1.0E-3; itscnt=5; n=20; isupper=CMath::RandomReal()>0.5; CMatGen::SPDMatrixRndCond(n,1.0E3,za); CSparse::SparseCreate(n,n,0,sa); a=matrix::Zeros(n,n); for(i=0; i=i && isupper) { CSparse::SparseSet(sa,i,j,za.Get(i,j)); a.Set(i,j,za.Get(i,j)); } if(j<=i && !isupper) { CSparse::SparseSet(sa,i,j,za.Get(i,j)); a.Set(i,j,za.Get(i,j)); } } } b=vector::Zeros(n); s=vector::Zeros(n); for(i=0; ieps,"testminqpunit.ap:6609"); //--- Test scale-invariance. BLEIC performs same steps on scaled and //--- unscaled problems (assuming that scale of the variables is known). //--- We generate random scale matrix S and random well-conditioned and //--- well scaled matrix A. Then we solve two problems: //--- (1) f = 0.5*x'*A*x+b'*x //--- (identity scale matrix is used) //--- and //--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y //--- (scale matrix S is used) //--- Solution process is started from X=0, we perform ItsCnt=5 steps. //--- As result, we must get S*x=y eps=1.0E-3; itscnt=5; n=20; s=vector::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i::Zeros(n); zb=vector::Zeros(n); for(i=0; ieps,"testminqpunit.ap:6662"); //--- Test that BLEIC can efficiently use sparse matrices (i.e. it is //--- not disguised version of some dense QP solver). In order to test //--- it we create very large and very sparse problem (diagonal matrix //--- with N=20.000) and perform 10 iterations of BLEIC-based QP solver. //--- In case QP solver uses some form of dense linear algebra to solve //--- this problem, it will take TOO much time to solve it. And we will //--- notice it by EXTREME slowdown during testing. n=20000; CSparse::SparseCreate(n,n,0,sa); for(i=0; i::Zeros(n); for(i=0; i=2 (important!) //--- * box constraints, x[i] in [-1,+1] //--- [ 1 1 ... 1 1 ] //--- * A = [ ... ... ... ] //--- [ 1 1 ... 1 1 ] //--- * random B such that SUM(b[i])=0.0 (important!) //--- * initial point x0 is chosen in such way that SUM(x[i])=0.0 //--- (important!) //--- We perform two tests: //--- * unconstrained problem must be recognized as unbounded //--- (when starting from x0!) //--- * constrained problem must be recognized as bounded //--- and successfully solved //--- Both problems require subtle programming when we work //--- with semidefinite QP. //--- NOTE: it is very important to have N>=2 (otherwise problem //--- will be bounded from below even without boundary //--- constraints) and to have x0/b0 such that sum of //--- components is zero (such x0 is exact minimum of x'*A*x, //--- which allows algorithm to find direction of zero curvature //--- at the very first step). If x0/b are chosen in other way, //--- algorithm may be unable to find direction of zero //--- curvature and will cycle forever, slowly decreasing //--- function value at each iteration. //--- This is major difference from similar test for QQP solver - //--- QQP can find direction of zero curvature from almost any //--- point due to internal CG solver which favors such directions. //--- BLEIC uses LBFGS, which is less able to find direction of //--- zero curvature. nlist.Resize(12); nlist.Set(0,2); nlist.Set(1,3); nlist.Set(2,4); nlist.Set(3,5); nlist.Set(4,6); nlist.Set(5,7); nlist.Set(6,8); nlist.Set(7,9); nlist.Set(8,10); nlist.Set(9,20); nlist.Set(10,40); nlist.Set(11,80); eps=1.0E-5; for(nidx=0; nidx::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Ones(n,n); DenseToSparse(a,n,sa); //--- Solve problem: //--- * without constraints we expect failure //--- * with constraints algorithm must succeed CMinQP::MinQPCreate(n,state); CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,0); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetStartingPoint(state,x0); if(CHighQualityRand::HQRndNormal(rs)>0.0) CMinQP::MinQPSetQuadraticTerm(state,a,true); else CMinQP::MinQPSetQuadraticTermSparse(state,sa,true); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6790"); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6794"); if(rep.m_terminationtype<=0) return(result); //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if(x1[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(result,x1[i]bndu[i],"testminqpunit.ap:6815"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6818"); } } //--- Test that BLEIC-based QP solver can solve non-convex problems //--- which are bounded from below on the feasible set: //--- min -||x||^2 s.t. x[i] in [-1,+1] //--- We also test ability of the solver to detect unbounded problems //--- (we remove one of the constraints and repeat solution process). n=20; eps=1.0E-14; CSparse::SparseCreate(n,n,0,sa); for(i=0; i::Zeros(n); bndu=vector::Zeros(n); x=vector::Zeros(n); for(i=0; i0) { for(i=0; i::Zeros(2*n,n+1); ct.Resize(2*n); for(i=0; i::Zeros(n); for(i=0; i0) { for(i=0; i(100.0*CMath::m_machineepsilon) && MathAbs(xend0[i]-1)>(100.0*CMath::m_machineepsilon),"testminqpunit.ap:6909"); } CMinQP::MinQPSetLC(state,c,ct,2*(n-1)); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend0,rep); CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6913"); //--- Test that BLEIC-based QP solver can solve QP problems with //--- zero quadratic term: //--- min b'*x s.t. x[i] in [-1,+1] //--- It means that QP solver can be used as linear programming solver //--- (altough performance of such solver is worse than that of specialized //--- LP solver). //--- NOTE: we perform this test twice - first time without explicitly setting //--- quadratic term (we test that default quadratic term is zero), and //--- second time - with explicitly set quadratic term. n=20; CSparse::SparseCreate(n,n,0,sa); bndl=vector::Zeros(n); bndu=vector::Zeros(n); b=vector::Zeros(n); for(i=0; i0) { for(i=0; i0.0 && xend0[i]!=bndl[i],"testminqpunit.ap:6951"); CAp::SetErrorFlag(result,b[i]<0.0 && xend0[i]!=bndu[i],"testminqpunit.ap:6952"); } } CMinQP::MinQPCreate(n,state); CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetQuadraticTermSparse(state,sa,true); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend0,rep); CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6961"); if(rep.m_terminationtype>0) { for(i=0; i0.0 && xend0[i]!=bndl[i],"testminqpunit.ap:6965"); CAp::SetErrorFlag(result,b[i]<0.0 && xend0[i]!=bndu[i],"testminqpunit.ap:6966"); } } //--- Test specific problem sent by V.Semenenko, which resulted in //--- the initinite loop in FindFeasiblePoint (before fix). We do //--- not test results returned by solver - simply being able to //--- Stop is enough for this test. //--- NOTE: it is important that modifications to problem are applied //--- sequentially. Test fails after 100-5000 such modifications. //--- One modification is not enough to cause failure. n=3; a=matrix::Zeros(n,n); a.Set(0,0,1.222990); a.Set(1,1,1.934900); a.Set(2,2,0.603924); b=vector::Zeros(n); b.Set(0,-4.97245); b.Set(1,-9.09039); b.Set(2,-4.63856); c=matrix::Zeros(8,n+1); c.Set(0,0,1); c.Set(0,n,4.94298); c.Set(1,0,1); c.Set(1,n,4.79981); c.Set(2,1,1); c.Set(2,n,-0.4848); c.Set(3,1,1); c.Set(3,n,-0.73804); c.Set(4,2,1); c.Set(4,n,0.575729); c.Set(5,2,1); c.Set(5,n,0.458645); c.Set(6,0,1); c.Set(6,2,-1); c.Set(6,n,-0.0546574); c.Set(7,0,1); c.Set(7,2,-1); c.Set(7,n,-0.5900440); ct.Resize(8); ct.Set(0,-1); ct.Set(1,1); ct.Set(2,-1); ct.Set(3,1); ct.Set(4,-1); ct.Set(5,1); ct.Set(6,-1); ct.Set(7,1); s=vector::Zeros(n); s.Set(0,0.143171); s.Set(1,0.253240); s.Set(2,0.117084); x0=vector::Zeros(n); x0.Set(0,3.51126); x0.Set(1,4.05731); x0.Set(2,6.63307); for(pass=1; pass<=10000; pass++) { //--- Apply random distortion for(j=0; j::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xori=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i0.0) CMinQP::MinQPSetBC(state,bndl,bndu); else { for(i=0; i0.0) g=0; if(x1[i]>=(bndu[i]-bctol) && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(waserrors,x1[i]bndu[i],"testminqpunit.ap:905"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:908"); //--- Test Lagrange multipliers returned by the solver (we skip //--- BLEIC solver because it does not return Lagrange vals). if(solvertype!=-1 && solvertype!=0) { gtrial=vector::Zeros(n); for(i=0; i1.0E-3,"testminqpunit.ap:928"); } } } //--- Convex test, same box constraints for all variables: //--- * N dimensions //--- * box constraints set with MinQPSetBCAll() //--- * positive-definite A //--- * algorithm randomly choose dense or sparse A, and for //--- sparse matrix it randomly choose format. //--- * random B with normal entries //--- * initial point is random, feasible //--- * random origin (zero or non-zero) and scale (unit or //--- non-unit) are generated eps=1.0E-3; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem origintype=CHighQualityRand::HQRndUniformI(rs,2); scaletype=CHighQualityRand::HQRndUniformI(rs,2); isupper=CHighQualityRand::HQRndUniformR(rs)<0.5; issparse=CHighQualityRand::HQRndUniformR(rs)<0.5; CMatGen::SPDMatrixRndCond(n,1.0E3,fulla); halfa=matrix::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); x0=vector::Zeros(n); xori=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i0.0) g=0; if(x1[i]>=(vu-bctol) && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(waserrors,x1[i]vu,"testminqpunit.ap:1038"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1041"); } } //--- Semidefinite test: //--- * N dimensions //--- * nonnegativity constraints //--- * A = [ 1 1 ... 1 1 ; 1 1 ... 1 1 ; .... ; 1 1 ... 1 1 ] //--- * algorithm randomly choose dense or sparse A, and for //--- sparse matrix it randomly choose format. //--- * random B with normal entries //--- * initial point is random, feasible eps=1.0E-4; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem isupper=CHighQualityRand::HQRndUniformR(rs)<0.5; issparse=CHighQualityRand::HQRndUniformR(rs)<0.5; fulla=matrix::Zeros(n,n); halfa=matrix::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.0) g=0; if(x1[i]>=(bndu[i]-bctol) && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(waserrors,x1[i]bndu[i],"testminqpunit.ap:1122"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1125"); } } //--- Non-convex test: //--- * N dimensions, N>=2 //--- * box constraints, x[i] in [-1,+1] //--- * A = A0-0.5*I, where A0 is SPD with unit norm and smallest //--- singular value equal to 1.0E-3, I is identity matrix //--- * random B with normal entries //--- * initial point is random, feasible //--- We perform two tests: //--- * unconstrained problem must be recognized as unbounded //--- * constrained problem can be successfully solved //--- NOTE: it is important to have N>=2, because formula for A //--- can be applied only to matrix with at least two //--- singular values eps=1.0E-4; for(n=2; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem CMatGen::SPDMatrixRndCond(n,1.0E3,fulla); for(i=0; i::Zeros(n,n); for(i=0; i=i && isupper) || (j<=i && !isupper)) halfa.Set(i,j,fulla.Get(i,j)); else halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5); } } DenseToSparse(halfa,n,sa); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.0) CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper); else CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-4,"testminqpunit.ap:1192"); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1196"); if(rep.m_terminationtype<=0) return; //--- Test - calculate constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if(x1[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); CAp::SetErrorFlag(waserrors,x1[i]bndu[i],"testminqpunit.ap:1216"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1219"); } } //--- Linear (zero-quadratic) test: //--- * N dimensions, N>=1 //--- * box constraints, x[i] in [-1,+1] //--- * A = 0 //--- * random B with normal entries //--- * initial point is random, feasible //--- We perform two tests: //--- * unconstrained problem must be recognized as unbounded //--- * constrained problem can be successfully solved //--- NOTE: we may explicitly set zero A, or assume that by //--- default it is zero. During test we will try both //--- ways. eps=1.0E-4; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate problem b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.0) { a=matrix::Zeros(n,n); CMinQP::MinQPSetQuadraticTerm(state,a,true); } CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-4 && rep.m_terminationtype!=-2,"testminqpunit.ap:1278"); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1283"); if(rep.m_terminationtype<=0) return; //--- Test - calculate constrained gradient at solution, //--- check its norm. for(i=0; i0.0 && x1[i]>(bndl[i]+bctol),"testminqpunit.ap:1293"); CAp::SetErrorFlag(waserrors,b[i]<0.0 && x1[i]<(bndu[i]-bctol),"testminqpunit.ap:1294"); } } } } //+------------------------------------------------------------------+ //| This function tests equality constrained quadratic programming | //| algorithm. | //| Returns True on errors. | //+------------------------------------------------------------------+ bool CTestMinQPUnit::ECQPTest(void) { //--- create variables bool result; int n=0; int k=0; CMatrixDouble a; CMatrixDouble q; CMatrixDouble c; CMatrixDouble a2; CRowDouble b; CRowDouble b2; CRowDouble xstart; CRowDouble xstart2; CRowDouble xend; CRowDouble xend2; CRowDouble x0; CRowDouble x1; CRowDouble xd; CRowDouble xs; CRowDouble tmp; CRowDouble g; CRowDouble bndl; CRowDouble bndu; CRowDouble xorigin; CRowInt ct; double eps=0; double theta=0; double f0=0; double f1=0; CMinQPState state; CMinQPState state2; CMinQPReport rep; int i=0; int j=0; int pass=0; int rk=0; double v=0; int aulits=0; bool waserrors=false; int i_=0; //--- First test: //--- * N*N identity A //--- * K::Identity(n,n); b=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); //--- Compare with analytic solution if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1400"); continue; } for(i=0; ieps,"testminqpunit.ap:1404"); } } //--- Second test: //--- * N*N SPD A //--- * K::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); //--- Calculate gradient, check projection if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1472"); continue; } for(i=0; ieps,"testminqpunit.ap:1478"); } g=b; for(i=0; ieps,"testminqpunit.ap:1493"); } } //--- Boundary and linear equality constrained QP problem: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1580"); continue; } //--- Solve problem using barrier functions (quadrative objective, boundary constraints, //--- explicit penalty term added to the main quadratic matrix. Lagrangian terms improve //--- solution quality): //--- * A2 := A+C'*C //--- * b2 := b-r'*C //--- * b2 is iteratively updated using augmented Lagrangian update //--- NOTE: we may need many outer iterations to converge to the optimal values //--- of Lagrange multipliers. Convergence is slowed down by the presense //--- of boundary constraints, whose activation/deactivation slows down //--- process. a2=matrix::Zeros(n,n); CAblas::RMatrixCopy(n,n,a,0,0,a2,0,0); CAblas::RMatrixSyrk(n,k,theta,c,0,0,2,1.0,a2,0,0,true); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state2,xstart); CMinQP::MinQPSetBC(state2,bndl,bndu); for(i=1; i<=aulits; i++) { //--- Solve, update B2 according to augmented Lagrangian algorithm CMinQP::MinQPSetLinearTerm(state2,b2); CMinQP::MinQPOptimize(state2); CMinQP::MinQPResults(state2,xend2,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1627"); continue; } for(j=0; jeps,"testminqpunit.ap:1657"); for(i=0; i(1.0E6*CMath::m_machineepsilon),"testminqpunit.ap:1661"); } for(i=0; i1.0,"testminqpunit.ap:1666"); } } } //--- Boundary and linear equality constrained QP problem, //--- test for correct handling of non-zero XOrigin: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K::Zeros(n); b2=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xorigin=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetOrigin(state,xorigin); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1750"); continue; } //--- Solve problem using explicit origin CMinQP::MinQPCreate(n,state2); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state2,b2); CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5); CMinQP::MinQPSetStartingPoint(state2,xstart); CMinQP::MinQPSetBC(state2,bndl,bndu); CMinQP::MinQPSetLC(state2,c,ct,k); CMinQP::MinQPOptimize(state2); CMinQP::MinQPResults(state2,xend2,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1768"); continue; } //--- Check feasibility properties and compare solutions for(i=0; ieps,"testminqpunit.ap:1778"); } for(i=0; ieps,"testminqpunit.ap:1782"); CAp::SetErrorFlag(waserrors,xend[i]<0.0,"testminqpunit.ap:1783"); CAp::SetErrorFlag(waserrors,xend[i]>1.0,"testminqpunit.ap:1784"); } } } //--- Boundary and linear equality constrained QP problem with excessive //--- equality constraints: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix, //--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9) //--- * optimization problem has form 0.5*x'*A*x-b*x, //--- where b is some random vector //--- * because constraints are excessive, the main problem is to find //--- feasible point; the only existing feasible point is solution, //--- so we have to check only feasibility eps=1.0E-4; for(n=1; n<=6; n++) { //--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart k=2*n; CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); //--- Check feasibility properties of the solution //--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code if(CAp::Len(xend)!=n) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1858"); continue; } for(i=0; ieps,"testminqpunit.ap:1864"); } } //--- Boundary and linear equality constrained QP problem, //--- test checks that different starting points yield same final point: //--- * random N from [1..6], random K from [1..2*N] //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K::Zeros(n); b2=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); xstart2=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1938"); continue; } //--- Solve with XStart2 CMinQP::MinQPSetStartingPoint(state,xstart2); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend2,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1950"); continue; } //--- Calculate function value and XEnd and XEnd2, compare solutions f0=0.0; f1=0.0; for(i=0; ieps,"testminqpunit.ap:1969"); } //--- Test ability to correctly handle situation where algorithm //--- either: //--- (1) starts from point with gradient whose projection to //--- active set is almost zero (but not exactly zero) //--- (2) performs step to such point //--- In order to do this we solve problem //--- * min 0.5*x'*x - (x0+c)'*x //--- * subject to c'*x = c'*x0, with c and x0 random unit vectors //--- * with initial point xs = x0+r*xd, where r is scalar, //--- xd is vector which is orthogonal to c. //--- * we try different r=power(2,-rk) for rk=0...70. The idea //--- is that as we approach closer and closer to x0, which is //--- a solution of the constrained problem, constrained gradient //--- of the function rapidly vanishes. eps=1.0E-6; for(rk=0; rk<=70; rk++) { n=10; //--- Generate x0, c, xd, xs, generate unit A CApServ::RandomUnit(n,x0); CApServ::RandomUnit(n,xd); CApServ::RandomUnit(n,tmp); c=matrix::Zeros(1,n+1); ct.Resize(1); xs=vector::Zeros(n); b=vector::Zeros(n); ct.Set(0,0); v=0; for(i=0; i::Identity(n,n); //--- Create and solve optimization problem CMinQP::MinQPCreate(n,state); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetQuadraticTerm(state,a,true); CMinQP::MinQPSetStartingPoint(state,xs); CMinQP::MinQPSetLC(state,c,ct,1); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:2040"); continue; } for(i=0; ieps,"testminqpunit.ap:2044"); v=CAblasF::RDotVR(n,xend,c,0)-c.Get(0,n); CAp::SetErrorFlag(waserrors,MathAbs(v)>(1.0E5*CMath::m_machineepsilon),"testminqpunit.ap:2048"); } //--- return result result=waserrors; return(result); } //+------------------------------------------------------------------+ //| This function tests inequality constrained quadratic programming | //| algorithm. | //| On failure sets Err to True; on success leaves it unchanged. | //+------------------------------------------------------------------+ void CTestMinQPUnit::ICQPTest(bool &err) { //--- create variables int n=0; int k=0; CMatrixDouble a; CMatrixDouble q; CMatrixDouble c; CMatrixDouble a2; CMatrixDouble t2; CMatrixDouble t3; CMatrixDouble ce; CRowDouble xs0; CRowDouble bl; CRowDouble bu; CRowDouble tmp; CRowDouble x; CRowDouble xstart; CRowDouble xstart2; CRowDouble xend; CRowDouble xend2; CRowDouble x0; CRowDouble x1; CRowDouble b; CRowDouble b2; CRowDouble g; CRowDouble bndl; CRowDouble bndu; CRowDouble xorigin; CRowDouble tmp0; CRowDouble tmp1; CRowDouble da; CRowInt ct; bool nonnegative[]; double eps=0; CMinQPState state; CMinQPState state2; CMinQPReport rep; CMinQPReport rep2; int i=0; int j=0; int pass=0; double v=0; double vv=0; double f0=0; double f1=0; double tolconstr=0; int bscale=0; int akind=0; int shiftkind=0; int ccnt=0; CHighQualityRandState rs; CSNNLSSolver nnls; bool isnonconvex; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Inequality constrained problem: //--- * N*N diagonal A //--- * one inequality constraint q'*x>=0, where q is random unit vector //--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x, //--- where x1 is some random vector //--- * either: //--- a) x1 is feasible => we must Stop at x1 //--- b) x1 is infeasible => we must Stop at the boundary q'*x=0 and //--- projection of gradient onto q*x=0 must be zero //--- NOTE: we make several passes because some specific kind of errors is rarely //--- caught by this test, so we need several repetitions. eps=1.0E-4; for(n=2; n<=6; n++) { for(pass=0; pass<=4; pass++) { //--- Generate problem: A, b, CMatrix, x0, XStart CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(1,n+1); ct.Resize(1); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetLC(state,c,ct,1); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); //--- Test if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2160"); continue; } v=CAblasF::RDotVR(n,x1,c,0); if(v>=0.0) { //--- X1 is feasible, compare target function values at XEnd and X1 for(i=0; ieps,"testminqpunit.ap:2173"); } else { //--- X1 is infeasible: //--- * XEnd must be approximately feasible //--- * gradient projection onto c'*x=0 must be zero v=CAblasF::RDotVR(n,xend,c,0); CAp::SetErrorFlag(err,v<(-eps),"testminqpunit.ap:2183"); g=b; for(i=0; ieps,"testminqpunit.ap:2194"); } } } //--- Boundary and linear equality/inequality constrained QP problem, //--- test for correct handling of non-zero XOrigin: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K::Zeros(n); b2=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xorigin=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetOrigin(state,xorigin); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2278"); continue; } //--- Solve problem using explicit origin CMinQP::MinQPCreate(n,state2); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state2,b2); CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5); CMinQP::MinQPSetStartingPoint(state2,xstart); CMinQP::MinQPSetBC(state2,bndl,bndu); CMinQP::MinQPSetLC(state2,c,ct,k); CMinQP::MinQPOptimize(state2); CMinQP::MinQPResults(state2,xend2,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2296"); continue; } //--- Calculate function value and XEnd and XEnd2 f0=0.0; f1=0.0; for(i=0; ieps,"testminqpunit.ap:2323"); if(ct[i]>0) CAp::SetErrorFlag(err,v<(c.Get(i,n)-eps),"testminqpunit.ap:2325"); if(ct[i]<0) CAp::SetErrorFlag(err,v>(c.Get(i,n)+eps),"testminqpunit.ap:2327"); } for(i=0; ieps,"testminqpunit.ap:2331"); CAp::SetErrorFlag(err,xend[i]<0.0,"testminqpunit.ap:2332"); CAp::SetErrorFlag(err,xend[i]>1.0,"testminqpunit.ap:2333"); } } } //--- Boundary constraints vs linear ones: //--- * N*N SPD A //--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x, //--- where x1 is some random vector from [-1,+1] //--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i], //--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0] //--- * initial point xstart is from [-1,+2] //--- * we solve two related QP problems: //--- a) one with constraints posed as boundary ones //--- b) another one with same constraints posed as general linear ones //--- both problems must have same solution. //--- Here we test that boundary constrained and linear inequality constrained //--- solvers give same results. eps=1.0E-3; for(n=1; n<=6; n++) { //--- Generate problem: A, b, x0, XStart, C, CT CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); ct.Resize(2*n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetLC(state,c,ct,2*n); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); //--- Solve boundary constrained problem CMinQP::MinQPCreate(n,state2); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state2,b); CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5); CMinQP::MinQPSetStartingPoint(state2,xstart); CMinQP::MinQPSetBC(state2,bndl,bndu); CMinQP::MinQPOptimize(state2); CMinQP::MinQPResults(state2,xend2,rep2); //--- Calculate gradient, check projection if(rep.m_terminationtype<=0 || rep2.m_terminationtype<=0) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2421"); continue; } for(i=0; i(bndu[i]+eps),"testminqpunit.ap:2427"); CAp::SetErrorFlag(err,MathAbs(xend[i]-xend2[i])>eps,"testminqpunit.ap:2428"); } } //--- Boundary constraints posed as general linear ones: //--- * no bound constraints //--- * 2*N linear constraints 0 <= x[i] <= 1 //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple constraints and function it is easy to find //--- analytic form of solution: S[i] = bound(x0[i], 0, 1). //--- * however, we can't guarantee that solution is strictly feasible //--- with respect to nonlinearity constraint, so we check //--- for approximate feasibility. for(n=1; n<=5; n++) { //--- Generate X, BL, BU. a=matrix::Identity(n,n); b=vector::Zeros(n); xstart=vector::Zeros(n); x0=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); ct.Resize(2*n); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2497"); continue; } //--- * compare solution with analytic one //--- * check feasibility for(i=0; i0.05,"testminqpunit.ap:2507"); CAp::SetErrorFlag(err,xend[i]<(-1.0E-6),"testminqpunit.ap:2508"); CAp::SetErrorFlag(err,xend[i]>(1.0+1.0E-6),"testminqpunit.ap:2509"); } } //--- Boundary and linear equality/inequality constrained QP problem with //--- excessive constraints: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix, //--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9) //--- * optimization problem has form 0.5*x'*A*x-b*x, //--- where b is some random vector //--- * because constraints are excessive, the main problem is to find //--- feasible point; usually, the only existing feasible point is solution, //--- so we have to check only feasibility eps=1.0E-4; for(n=1; n<=6; n++) { //--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart k=2*n; CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0) c.Set(i,n,v-1.0E-3); if(ct[i]<0) c.Set(i,n,v+1.0E-3); } for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); //--- Check feasibility properties of the solution //--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code if(CAp::Len(xend)!=n) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2588"); continue; } for(i=0; ieps,"testminqpunit.ap:2595"); if(ct[i]>0) CAp::SetErrorFlag(err,v<(c.Get(i,n)-eps),"testminqpunit.ap:2597"); if(ct[i]<0) CAp::SetErrorFlag(err,v>(c.Get(i,n)+eps),"testminqpunit.ap:2599"); } } //--- General inequality constrained problem: //--- * N*N SPD diagonal A with moderate condtion number //--- * no boundary constraints //--- * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned //--- matrix, x0 is some random vector [-1,+1] //--- * optimization problem has form 0.5*x'*A*x-b'*x, //--- where b is random vector from [-1,+1] //--- * using duality, we can obtain solution of QP problem as follows: //--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0 //--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0) //--- b) after we got dual solution ys, we calculate primal solution //--- xs = inv(A)*(C'*ys-b) eps=1.0E-3; for(n=1; n<=6; n++) { //--- Generate problem da=vector::Zeros(n); a=matrix::Zeros(n,n); CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2); b=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(n,n+1); ct.Resize(n); for(i=0; i0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetLC(state,c,ct,n); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2665"); continue; } for(i=0; i::Zeros(n,n); a2=matrix::Zeros(n,n); CAblas::RMatrixTranspose(n,n,c,0,0,t3,0,0); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); bndu=vector::Zeros(n); for(i=0; i(eps*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:2739"); } //--- Boundary and linear equality/inequality constrained QP problem, //--- test checks that different starting points yield same final point: //--- * random N from [1..6], random K from [1..2*N] //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K<2*N linear inequality constraints Q*x <= Q*x0, where //--- Q is random K*N matrix, x0 is some random vector from the //--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9) //--- * optimization problem has form 0.5*x'*A*x+b*x, //--- where b is some random vector with -5<=b[i]<=+5 //--- * every component of the initial point XStart is random from [-2,+2] //--- * we perform two starts from random different XStart and compare values //--- of the target function (although final points may be slightly different, //--- function values should match each other) eps=1.0E-4; for(pass=1; pass<=50; pass++) { //--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart. n=CMath::RandomInteger(5)+2; k=CMath::RandomInteger(2*n)+1; CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a); b=vector::Zeros(n); b2=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); xstart2=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i=(0.5*n) && ct[i]==0) ct.Set(i,1); if(ct[i]<0) c.Set(i,n,c.Get(i,n)+0.1); if(ct[i]>0) c.Set(i,n,c.Get(i,n)-0.1); } //--- Solve with XStart //--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code CMinQP::MinQPCreate(n,state); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,k); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(CAp::Len(xend)!=n || !CApServ::IsFiniteVector(xend,n)) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2821"); continue; } //--- Solve with XStart2 CMinQP::MinQPSetStartingPoint(state,xstart2); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend2,rep); if(CAp::Len(xend2)!=n || !CApServ::IsFiniteVector(xend2,n)) { CAp::SetErrorFlag(err,true,"testminqpunit.ap:2834"); continue; } //--- Calculate function value and XEnd and XEnd2, compare solutions f0=0.0; f1=0.0; for(i=0; ieps,"testminqpunit.ap:2853"); } //--- Convex/nonconvex optimization problem with excessive constraints: //--- * N=2..5 //--- * f = 0.5*x'*A*x+b'*x //--- * b has normally distributed entries with scale 10^BScale //--- * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank SPD, low rank indefinite //--- * box constraints: x[i] in [-1,+1] //---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift, //--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is //--- a shift parameter: //--- * with zero v_shift such constraints are degenerate (each vertex has //--- N box constraints and one "redundant" linear constraint) //--- * with positive v_shift linear constraint is always inactive //--- * with small (about machine epsilon) but negative v_shift, //--- constraint is close to degenerate - but not exactly //--- We check that constrained gradient is close to zero at solution. //--- Box constraint is considered active if distance to boundary is less //--- than TolConstr. //--- NOTE: TolConstr must be large enough so it won't conflict with //--- perturbation introduced by v_shift tolconstr=1.0E-3; for(n=2; n<=5; n++) { for(akind=0; akind<=4; akind++) { for(shiftkind=-5; shiftkind<=1; shiftkind++) { for(bscale=0; bscale>=-2; bscale--) { //--- Generate A, B and initial point a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); for(i=0; i::Zeros(n); for(k=1; k<=MathMin(3,n-1); k++) { for(i=0; i::Zeros(n); for(k=1; k<=MathMin(3,n-1); k++) { for(i=0; i::Full(n,-1.0); bu=vector::Full(n,1.0); ccnt=(int)MathRound(MathPow(2,n)); c=matrix::Zeros(ccnt,n+1); ct.Resize(ccnt); for(i=0; i0.5); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xs0,rep); CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminqpunit.ap:3000"); if(err) return; //--- Evaluate gradient at solution and test vv=0.0; for(i=0; i0.0) v=0.0; if(xs0[i]>=(bu[i]-tolconstr) && v<0.0) v=0.0; vv+=CMath::Sqr(v); } vv=MathSqrt(vv); CAp::SetErrorFlag(err,vv>(1.0E-3),"testminqpunit.ap:3024"); } } } } //--- Convex/nonconvex optimization problem with combination of //--- box and linear constraints: //--- * N=2..8 //--- * f = 0.5*x'*A*x+b'*x //--- * b has normally distributed entries with scale 10^BScale //--- * several kinds of A are tried: zero, well conditioned SPD, //--- well conditioned indefinite, low rank semidefinite, low rank indefinite //--- * box constraints: x[i] in [-1,+1] //--- * initial point x0 = [0 0 ... 0 0] //--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0. //--- random mix of equality/inequality constraints is tried. //--- x0 is guaranteed to be feasible. //--- We check that constrained gradient is close to zero at solution. //--- Inequality constraint is considered active if distance to boundary //--- is less than TolConstr. We use nonnegative least squares solver //--- in order to compute constrained gradient. tolconstr=1.0E-3; for(n=2; n<=8; n++) { for(akind=0; akind<=4; akind++) { for(bscale=0; bscale>=-2; bscale--) { //--- Generate A, B and initial point a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); for(i=0; i::Zeros(n); for(k=1; k<=MathMin(3,n-1); k++) { for(i=0; i::Zeros(n); for(k=1; k<=MathMin(3,n-1); k++) { for(i=0; i::Full(n,-1.0); bu=vector::Full(n,1.0); ccnt=MathMin(3,n-1); c=matrix::Zeros(ccnt,n+1); ct.Resize(ccnt); for(i=0; i0.5); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xs0,rep); CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminqpunit.ap:3159"); if(err) return; //--- 1. evaluate unconstrained gradient at solution //--- 2. calculate constrained gradient (NNLS solver is used //--- to evaluate gradient subject to active constraints). //--- In order to do this we form CE matrix, matrix of active //--- constraints (columns store constraint vectors). Then //--- we try to approximate gradient vector by columns of CE, //--- subject to non-negativity restriction placed on variables //--- corresponding to inequality constraints. //--- Residual from such regression is a constrained gradient vector. g=vector::Zeros(n); for(i=0; i::Zeros(n,n+ccnt); ArrayResize(nonnegative,n+ccnt); k=0; for(i=0; i(double)(bu[i]),"testminqpunit.ap:3188"); if(xs0[i]<=(double)(bl[i]+tolconstr)) { for(j=0; j=(double)(bu[i]-tolconstr)) { for(j=0; jtolconstr,"testminqpunit.ap:3212"); CAp::SetErrorFlag(err,ct[i]>0 && v<(-tolconstr),"testminqpunit.ap:3213"); CAp::SetErrorFlag(err,ct[i]<0 && v>tolconstr,"testminqpunit.ap:3214"); if(ct[i]==0) { for(j=0; j0 && v<=tolconstr) || (ct[i]<0 && v>=(-tolconstr))) { for(j=0; j(1.0E-2),"testminqpunit.ap:3244"); else CAp::SetErrorFlag(err,vv>(1.0E-3),"testminqpunit.ap:3246"); } } } } //+------------------------------------------------------------------+ //| This function tests linearly constrained QP solvers. | //| On failure sets Err to True; on success leaves it unchanged. | //+------------------------------------------------------------------+ void CTestMinQPUnit::GeneralLCQPTest(bool &errorflag) { //--- create variables CHighQualityRandState rs; int n=0; int i=0; int j=0; int k=0; int solvertype=0; double v=0; double vv=0; bool bflag; int pass=0; CMatrixDouble rawa; CMatrixDouble a; CSparseMatrix sa; CRowDouble b; CRowDouble b2; CRowDouble xs; CRowDouble x0; CRowDouble x1; CRowDouble x2; CRowDouble xf; CRowDouble xorigin; CRowDouble s; CRowDouble da; CRowDouble xstart; CRowDouble g; CRowDouble gtrial; CRowDouble gs; CRowDouble tmp; CRowDouble tmp0; CRowDouble tmp1; CRowDouble tmp2; CRowDouble lagrange; CRowDouble bndl; CRowDouble bndu; CMatrixDouble rawc; CRowDouble rawcl; CRowDouble rawcu; CRowInt rawct; int rawccnt=0; CMatrixDouble densec; CRowInt densect; int denseccnt=0; CSparseMatrix sparsec; CRowInt sparsect; int sparseccnt=0; CMatrixDouble activeset; bool activeeq[]; int nactive=0; CSNNLSSolver nnls; double constraintsrcond=0; CRowDouble svdw; CMatrixDouble svdu; CMatrixDouble svdvt; CMinQPState state; CMinQPReport rep; CMinQPState state2; CMinQPReport rep2; double f0=0; double f1=0; double xtol=0; double ftol=0; double gtol=0; double tolconstr=0; int bscale=0; int akind=0; CMatrixDouble ce; CMatrixDouble q; CMatrixDouble t2; CMatrixDouble t3; CMatrixDouble a2; CRowDouble lagbc; CRowDouble laglc; bool nonnegative[]; double mx=0; int shiftkind=0; int nnz=0; bool issemidefinite; bool skiptest; double bleicepsx=0; double aulepsx=0; double aulrho=0; int aulits=0; double ipmeps=0; CMatrixDouble kkt; CRowDouble kktright; double minushuge=0; double plushuge=0; int i_=0; CHighQualityRand::HQRndRandomize(rs); bleicepsx=1.0E-9; ipmeps=1.0E-12; aulepsx=1.0E-12; aulrho=5.0E3; aulits=15; //--- SMALL-SCALE TESTS: many tests for small N's for(solvertype=0; solvertype<=3; solvertype++) { //--- Test random linearly constrained convex QP problem with known answer: //--- * generate random A and b //--- * generate random solution XS //--- * calculate unconstrained gradient GS at XS //--- * generate random box/linear constraints C, with some of them being //--- active at XS, and some being inactive. Calculate residual gradient //--- GP after projection of GS onto active set, add one more constraint //--- equal to +-(GP-GS). //--- We test here BLEIC and Dense-AUL solvers, with A being passed //--- in dense or sparse format, and linear constraints C being passed //--- as dense, sparse or mixed ones. for(n=1; n<=10; n++) { //--- Generate random A, b and xs CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),rawa); b=vector::Zeros(n); xs=vector::Zeros(n); for(i=0; i::Zeros(n); tmp=vector::Zeros(n); CAblas::RMatrixMVect(n,n,rawa,0,0,0,xs,0,gs,0); for(i=0; i::Zeros(n); bndu=vector::Zeros(n); activeset=matrix::Zeros(n,n+rawccnt); ArrayResize(activeeq,n+rawccnt); rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); do { nactive=0; for(i=0; i0.50) { //--- I-th box constraint is equality one bndl.Set(i,xs[i]); bndu.Set(i,xs[i]); activeset.Col(nactive,vector::Zeros(n)); activeset.Set(i,nactive,1); activeeq[nactive]=true; nactive++; } else { //--- I-th box constraint is inequality one activeset.Col(nactive,vector::Zeros(n)); if(gs[i]>0.0) { bndl.Set(i,xs[i]); activeset.Set(i,nactive,-1); if(CHighQualityRand::HQRndUniformR(rs)>0.50) bndu.Set(i,AL_POSINF); } else { bndu.Set(i,xs[i]); activeset.Set(i,nactive,1); if(CHighQualityRand::HQRndUniformR(rs)>0.50) bndl.Set(i,AL_NEGINF); } activeeq[nactive]=false; nactive++; } } for(i=0; i0.50) { for(i_=0; i_<=n; i_++) rawc.Mul(i,i_,-1); rawct.Mul(i,-1); } continue; } if(CHighQualityRand::HQRndUniformR(rs)>0.50) { //--- I-th box constraint is equality one rawct.Set(i,0); activeset.Col(nactive,rawc[i]+0); activeeq[nactive]=true; nactive++; } else { //--- I-th box constraint is inequality one v=CAblasF::RDotVR(n,gs,rawc,i); if(v>0.0) { rawct.Set(i,1); activeset.Col(nactive,rawc[i]*(-1)); } else { rawct.Set(i,-1); activeset.Col(nactive,rawc[i]+0); } activeeq[nactive]=false; nactive++; } } tmp=gs.ToVector()*(-1); CSNNLS::SNNLSInit(0,0,0,nnls); CSNNLS::SNNLSSetProblem(nnls,activeset,tmp,0,nactive,n); for(i=0; i0) { bflag=CSingValueDecompose::RMatrixSVD(activeset,n,nactive,0,0,0,svdw,svdu,svdvt); //--- check if(!CAp::Assert(bflag,"MinQPTest: integrity failure")) { errorflag=true; return; } constraintsrcond=svdw[MathMin(nactive,n)-1]/svdw[0]; } else constraintsrcond=1; //--- Check RCond } while(!(constraintsrcond>=0.01 && nactive1.0E-3,"testminqpunit.ap:3584"); } //--- Inequality constrained convex problem: //--- * N*N diagonal A //--- * one inequality constraint q'*x>=0, where q is random unit vector //--- * optimization problem has form 0.5*x'*A*x-(xs*A)*x, //--- where xs is some random vector //--- * either: //--- a) xs is feasible => we must Stop at xs //--- b) xs is infeasible => we must Stop at the boundary q'*x=0 and //--- projection of gradient onto q*x=0 must be zero //--- NOTE: we make several passes because some specific kind of errors is rarely //--- caught by this test, so we need several repetitions. xtol=1.0E-4; gtol=1.0E-4; for(n=2; n<=6; n++) { for(pass=0; pass<=4; pass++) { //--- Generate problem: A, b, CMatrix, x0, XStart CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); xs=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(1,n+1); rawct.Resize(1); for(i=0; i=0.0) { //--- XS is feasible for(i=0; ixtol,"testminqpunit.ap:3677"); } else { //--- XS is infeasible: //--- * X1 must be approximately feasible //--- * gradient projection onto c'*x=0 must be zero v=CAblasF::RDotVR(n,x1,rawc,0); CAp::SetErrorFlag(errorflag,v<(-xtol),"testminqpunit.ap:3687"); g=b; for(i=0; igtol,"testminqpunit.ap:3698"); } } } //--- Box constraints vs linear ones: //--- * N*N SPD A //--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x, //--- where x1 is some random vector from [-1,+1] //--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i], //--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0] //--- * initial point xstart is from [-1,+2] //--- * we solve two related QP problems: //--- a) one with constraints posed as boundary ones //--- b) another one with same constraints posed as general linear ones //--- both problems must have same solution. //--- Here we test that boundary constrained and linear inequality constrained //--- solvers give same results. xtol=1.0E-5; for(n=1; n<=6; n++) { //--- Generate problem: A, b, x0, XStart, C, CT CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(2*n,n+1); rawct.Resize(2*n); bndl=vector::Full(n,AL_NEGINF); bndu=vector::Full(n,AL_POSINF); for(i=0; i(bndu[i]+xtol),"testminqpunit.ap:3810"); CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-x2[i])>xtol,"testminqpunit.ap:3811"); } } //--- Convex/nonconvex optimization problem with combination of //--- box and linear constraints: //--- * N=2..8 //--- * f = 0.5*x'*A*x+b'*x //--- * b has normally distributed entries with scale 10^BScale //--- * several kinds of A are tried: zero, well conditioned SPD, //--- well conditioned definite, low rank semidefinity //--- * box constraints: x[i] in [-1,+1] //--- * initial point x0 = [0 0 ... 0 0] //--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0. //--- random mix of equality/inequality constraints is tried, moderate //--- condition number is guaranteed, x0 is guaranteed to be feasible. //--- We check that constrained gradient is close to zero at solution. //--- Inequality constraint is considered active if distance to boundary //--- is less than TolConstr. We use nonnegative least squares solver //--- in order to compute constrained gradient. tolconstr=-99999; for(n=2; n<=8; n++) { for(akind=0; akind<=3; akind++) { for(bscale=1; bscale>=-1; bscale--) { //--- Dense-AUL solver has lower precision on rank-deficient //--- problems, so we skip AKind=3. //--- IPM solvers can not work with indefinite problems. if(solvertype==1 && akind==3) continue; if((solvertype==2 || solvertype==3) && (akind==2 || akind==4)) continue; //--- Set up tolerances if(solvertype==0) tolconstr=1.0E-8; else { if((solvertype==1 || solvertype==2) || solvertype==3) { tolconstr=1.0E-3; if(akind==3) tolconstr=tolconstr*5; } else { CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } } gtol=1.0E-4; //--- Generate A, B and initial point a=matrix::Zeros(n,n); b=vector::Zeros(n); xstart=vector::Zeros(n); for(i=0; i::Zeros(n); for(k=1; k<=MathMin(3,n-1); k++) { for(i=0; i::Full(n,-1.0); bndu=vector::Full(n,1.0); rawccnt=MathMin(3,n-1); rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); do { for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n+rawccnt); ArrayResizeAL(nonnegative,n+rawccnt); k=0; for(i=0; ibndu[i],"testminqpunit.ap:3990"); if(x1[i]<=(bndl[i]+tolconstr)) { for(j=0; j=(double)(bndu[i]-tolconstr)) { for(j=0; jtolconstr,"testminqpunit.ap:4014"); CAp::SetErrorFlag(errorflag,rawct[i]>0 && v<(-tolconstr),"testminqpunit.ap:4015"); CAp::SetErrorFlag(errorflag,rawct[i]<0 && v>tolconstr,"testminqpunit.ap:4016"); if(rawct[i]==0) { for(j=0; j0 && v<=tolconstr) || (rawct[i]<0 && v>=(-tolconstr))) { for(j=0; jgtol,"testminqpunit.ap:4045"); } } } //--- Boundary and linear equality/inequality constrained QP problem, //--- test for correct handling of non-zero XOrigin: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K::Zeros(n); b2=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xorigin=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(k,n+1); rawct.Resize(k); for(i=0; iftol,"testminqpunit.ap:4200"); for(i=0; ixtol,"testminqpunit.ap:4202"); } } //--- Test scale-invariance. Solver performs same steps on scaled and //--- unscaled problems (assuming that scale of the variables is known //--- and is 2^k for some integer k; latter guarantees that DENSE-AUL solver //--- performs EXACTLY same steps even under finite-precision arithmetics). //--- We generate random scale matrix S and random well-conditioned and //--- well scaled matrix A. Then we solve two problems: //--- (1) f = 0.5*x'*A*x+b'*x //--- (identity scale matrix is used) //--- and //--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y //--- (scale matrix S is used) //--- with correspondingly scaled constraints, origin and starting point. //--- As result, we must get S*x=y for(n=2; n<=6; n++) { for(k=1; k<=MathMax(n-2,1); k++) { //--- Set up tolerances: //--- * BLEIC or IPM do not guarantee that we perform steps exactly even //--- when scale is 2^k, so we use non-zero XTol=1E-3 //--- * DENSE-AUL provides such guarantee, so XTol=0 if((solvertype==0 || solvertype==2) || solvertype==3) xtol=1.0E-3; else { if(solvertype==1) xtol=0; else { CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } } //--- Generate "raw" problem: A, b, BndL, BndU, C/CT, XOrigin, XStart. //--- Solve it immediately. //--- NOTE: we make sure that there exists at least one feasible point X0. CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); xorigin=vector::Zeros(n); rawc=matrix::Zeros(k,n+1); rawct.Resize(k); for(i=0; i0) rawc.Add(i,n,-0.1); } RandomlySplitLC(rawc,rawct,k,n,sparsec,sparsect,sparseccnt,densec,densect,denseccnt,rs); CMinQP::MinQPCreate(n,state); state.m_dbgskipconstraintnormalization=true; if(solvertype==0) CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0); else if(solvertype==1) CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits); else if(solvertype==2) CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps); else if(solvertype==3) CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps); else { CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } CMinQP::MinQPSetLinearTerm(state,b); RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetOrigin(state,xorigin); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4306"); continue; } //--- Scale problem and solve one more time. //--- Randomly choose between dense and sparse versions. s=vector::Zeros(n); for(i=0; i0.5); if(akind==1) CMinQP::MinQPSetQuadraticTermSparse(state,sa,CMath::RandomReal()>0.5); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetOrigin(state,xorigin); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt); CMinQP::MinQPSetScale(state,s); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x2,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4371"); continue; } //--- Compare mx=0; for(i=0; i(double)(xtol*mx),"testminqpunit.ap:4383"); } } //--- General inequality constrained problem: //--- * N*N SPD diagonal A with moderate condtion number //--- * no boundary constraints //--- * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned //--- matrix, x0 is some random vector [-1,+1] //--- * optimization problem has form 0.5*x'*A*x-b'*x, //--- where b is random vector from [-1,+1] //--- * using duality, we can obtain solution of QP problem as follows: //--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0 //--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0) //--- b) after we got dual solution ys, we calculate primal solution //--- xs = inv(A)*(C'*ys-b) for(n=1; n<=6; n++) { //--- Set up tolerances xtol=1.0E-3; //--- Generate problem da=vector::Zeros(n); a=matrix::Zeros(n,n); CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2); b=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(n,n+1); rawct.Resize(n); for(i=0; i::Zeros(n,n); a2=matrix::Zeros(n,n); CAblas::RMatrixTranspose(n,n,rawc,0,0,t3,0,0); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); bndu=vector::Full(n,AL_POSINF); CMinQP::MinQPCreate(n,state2); CMinQP::MinQPSetAlgoQuickQP(state2,0.0,0.0,1.0E-9,0,true); CMinQP::MinQPSetLinearTerm(state2,b2); CMinQP::MinQPSetQuadraticTerm(state2,a2,true); CMinQP::MinQPSetBC(state2,bndl,bndu); CMinQP::MinQPOptimize(state2); CMinQP::MinQPResults(state2,x2,rep2); if(rep2.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4523"); continue; } for(i=0; i(double)(xtol*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:4536"); } //--- Boundary and linear equality/inequality constrained QP problem with //--- excessive constraints: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix, //--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9) //--- * optimization problem has form 0.5*x'*A*x-b*x, //--- where b is some random vector //--- * because constraints are excessive, the main problem is to find //--- feasible point; usually, the only existing feasible point is solution, //--- so we have to check only feasibility for(n=1; n<=6; n++) { //--- Set up tolerances if(solvertype==0) xtol=1.0E-5; else { if((solvertype==1 || solvertype==2) || solvertype==3) xtol=5.0E-3; else { CAp::Assert(false,"unexpected solver type"); errorflag=false; return; } } //--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart k=2*n; CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Ones(n); x0=vector::Zeros(n); x1=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(k,n+1); rawct.Resize(k); for(i=0; i0) rawc.Set(i,n,v-50*xtol); if(rawct[i]<0) rawc.Set(i,n,v+50*xtol); } for(i=0; ixtol,"testminqpunit.ap:4644"); if(rawct[i]>0) CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:4646"); if(rawct[i]<0) CAp::SetErrorFlag(errorflag,v>(rawc.Get(i,n)+xtol),"testminqpunit.ap:4648"); } } //--- Boundary constraints posed as general linear ones: //--- * no bound constraints //--- * 2*N linear constraints 0 <= x[i] <= 1 //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple constraints and function it is easy to find //--- analytic form of solution: S[i] = bound(x0[i], 0, 1). //--- * however, we can't guarantee that solution is strictly feasible //--- with respect to nonlinearity constraint, so we check //--- for approximate feasibility. for(n=1; n<=5; n++) { //--- Set up tolerances if(solvertype==0) xtol=1.0E-3; else { if((solvertype==1 || solvertype==2) || solvertype==3) xtol=1.0E-3; else { CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } } //--- Generate X, BL, BU. a=matrix::Identity(n,n); b=vector::Zeros(n); xstart=vector::Zeros(n); x0=vector::Zeros(n); rawc=matrix::Zeros(2*n,n+1); rawct.Resize(2*n); k=2*n; for(i=0; ixtol,"testminqpunit.ap:4745"); CAp::SetErrorFlag(errorflag,x1[i]<(0.0-xtol),"testminqpunit.ap:4746"); CAp::SetErrorFlag(errorflag,x1[i]>(1.0+xtol),"testminqpunit.ap:4747"); } } //--- Convex optimization problem with excessive constraints: //--- * N=2..5 //--- * f = 0.5*x'*A*x+b'*x //--- * b has normally distributed entries //--- * A is diagonal with log-normally distributed entries //--- * box constraints: x[i] in [-1,+1] //---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift, //--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is //--- a shift parameter: //--- * with zero v_shift such constraints are degenerate (each vertex has //--- N box constraints and one "redundant" linear constraint) //--- * with positive v_shift linear constraint is always inactive //--- * with small (about machine epsilon) but negative v_shift, //--- constraint is close to degenerate - but not exactly //--- Because A is diagonal, we can easily find out solution analytically. //--- NOTE: TolConstr must be large enough so it won't conflict with //--- perturbation introduced by v_shift for(n=2; n<=5; n++) { for(shiftkind=-5; shiftkind<=1; shiftkind++) { //--- Set up tolerances if(solvertype==0) { tolconstr=1.0E-6; gtol=1.0E-6; } else { if((solvertype==1 || solvertype==2) || solvertype==3) { tolconstr=1.0E-4; gtol=1.0E-4; } else { CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } } //--- Generate A, B and initial point a=matrix::Zeros(n,n); b=vector::Zeros(n); xstart=vector::Zeros(n); for(i=0; i::Full(n,-1.0); bndu=vector::Full(n,1.0); rawccnt=(int)MathRound(MathPow(2,n)); rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i0.0) v=0.0; if(x1[i]>=(bndu[i]-tolconstr) && v<0.0) v=0.0; vv+=CMath::Sqr(v); } vv=MathSqrt(vv); CAp::SetErrorFlag(errorflag,vv>gtol,"testminqpunit.ap:4882"); } } //--- Convex optimization problem with known answer (we generate constraints //--- and Lagrange coefficients, and then generate quadratic and linear term //--- which satisfy KKT conditions for given Lagrange coefficients): //--- * N=2..8 //--- * f = 0.5*x'*A*x+b'*x //--- * several kinds of A are tried: zero, well conditioned SPD, low rank //--- * initial point x0 = [0 0 ... 0 0] //--- * absent bounds can be set to +-INF or to some huge number (randomly) //--- * first, we generate set of linear constraints (without bounds), //--- solution point X and Lagrange multipliers for linear and box constraints //--- * then we determine bounds on variables and linear constraints which //--- align with values/signs of Lagrange multipliers //--- * then, having quadratic term A and known constraints and Lagrange //--- multipliers we determine linear term B which makes KKT conditions true for(n=1; n<=8; n++) { for(akind=0; akind<=2; akind++) { //--- Select number used for absent bounds: //--- * +- INF //--- * 1e9 //--- * 1e11 i=CHighQualityRand::HQRndUniformI(rs,3); minushuge=AL_NEGINF; plushuge=AL_POSINF; if(i==1) { minushuge=-1.0E9; plushuge=1.0E9; } if(i==2) { minushuge=-1.0E11; plushuge=1.0E11; } //--- Generate quadratic term A, origin and scale a=matrix::Zeros(n,n); issemidefinite=true; if(akind==1) { //--- Dense well conditioned SPD CMatGen::SPDMatrixRndCond(n,50.0,a); issemidefinite=false; } if(akind==2) { //--- Low rank semidefinite tmp=vector::Zeros(n); for(k=1; k<=MathMin(3,n-1); k++) { for(i=0; i::Zeros(n); xorigin=vector::Zeros(n); for(j=0; j::Zeros(rawccnt,n); for(i=0; i::Zeros(n); laglc=vector::Zeros(rawccnt); nactive=CHighQualityRand::HQRndUniformI(rs,n); k=CHighQualityRand::HQRndUniformI(rs,MathMin(nactive,rawccnt)+1); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Full(n,minushuge); bndu=vector::Full(n,plushuge); for(j=0; j0.0 || issemidefinite) bndl.Set(j,xf[j]-MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite) bndu.Set(j,xf[j]+MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(lagbc[j]!=0.0) { if(CHighQualityRand::HQRndUniformR(rs)<0.15) { bndl.Set(j,xf[j]); bndu.Set(j,xf[j]); } else { if(lagbc[j]<0.0) bndl.Set(j,xf[j]); if(lagbc[j]>0.0) bndu.Set(j,xf[j]); } } } rawcl=vector::Zeros(rawccnt); rawcu=vector::Zeros(rawccnt); for(i=0; i0.0) rawcl.Set(i,v-MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(CHighQualityRand::HQRndNormal(rs)>0.0) rawcu.Set(i,v+MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(laglc[i]!=0.0) { if(CHighQualityRand::HQRndUniformR(rs)<0.15) { //--- Active equality constraint rawcl.Set(i,v); rawcu.Set(i,v); } else { //--- Active inequality constraint if(laglc[i]<0.0) rawcl.Set(i,v); if(laglc[i]>0.0) rawcu.Set(i,v); } } if((!MathIsValidNumber(rawcl[i]) || rawcl[i]==minushuge) && (!MathIsValidNumber(rawcu[i]) || rawcu[i]==plushuge)) { //--- At least one bound must be present for linear constraint if(CHighQualityRand::HQRndNormal(rs)>0.0) rawcl.Set(i,v-10); else rawcu.Set(i,v+10); } } //--- Completely skip some solvers depending on their properties: //--- * Dense-AUL solver has lower precision on rank-deficient //--- problems, so we skip AKind=0 and AKind=2. //--- * BLEIC solver is always skipped if(solvertype==1 && akind!=1) continue; if(solvertype==0) continue; //--- Solve CMinQP::MinQPCreate(n,state); switch(solvertype) { case 0: CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0); break; case 1: CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits); break; case 2: CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps); break; case 3: CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps); break; default: CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } CMinQP::MinQPSetBC(state,bndl,bndu); RandomlySplitAndSetLC2(rawc,rawcl,rawcu,rawccnt,n,state,rs); CMinQP::MinQPSetLinearTerm(state,b); RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs); CMinQP::MinQPSetScale(state,s); CMinQP::MinQPSetOrigin(state,xorigin); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,x1,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:5138"); CAp::SetErrorFlag(errorflag,CAp::Len(x1)(CApServ::RMaxAbs3(f0,f1,1.0)*1.0E-3),"testminqpunit.ap:5150"); //--- Test Lagrange multipliers returned by the solver; test is skipped: //--- * for BLEIC solver //--- * for overconstrained DENSE-AUL skiptest=false; skiptest=skiptest||solvertype==0; skiptest=skiptest||(solvertype==1 && (double)(nactive)>(double)(0.5*(n-1))); if(!skiptest) { gtrial=vector::Zeros(n); for(i=0; i1.0E-3,"testminqpunit.ap:5176"); } } } } //--- Large-scale tests: a few selected tests for large N's for(solvertype=0; solvertype<=3; solvertype++) { //--- General equality constrained problem: //--- * N*N SPD (non-diagonal) A with moderate condition number //--- * no box constraints //--- * K::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i::Zeros(n+rawccnt,n+rawccnt); kktright=vector::Zeros(n+rawccnt); CAblas::RMatrixCopy(n,n,a,0,0,kkt,0,0); CAblas::RMatrixCopy(rawccnt,n,rawc,0,0,kkt,n,0); CAblas::RMatrixTranspose(rawccnt,n,rawc,0,0,kkt,0,n); for(i=0; i(xtol*MathMax(MathAbs(kktright[i]),1.0)),"testminqpunit.ap:5280"); //--- General inequality constrained problem: //--- * N*N SPD diagonal A with moderate condtion number //--- * no box constraints //--- * K= C*x0, where C is N*N well conditioned //--- matrix, x0 is some random vector [-1,+1] //--- * optimization problem has form 0.5*x'*A*x-b'*x, //--- where b is random vector from [-1,+1] //--- * using duality, we can obtain solution of QP problem as follows: //--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0 //--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0) //--- b) after we got dual solution ys, we calculate primal solution //--- xs = inv(A)*(C'*ys-b) n=60; rawccnt=40; if(solvertype==0) xtol=1.0E-3; else { if((solvertype==1 || solvertype==2) || solvertype==3) xtol=1.0E-3; else { CAp::Assert(false,"unexpected solver type"); errorflag=true; return; } } da=vector::Zeros(n); a=matrix::Zeros(n,n); CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2); b=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i::Zeros(n,rawccnt); a2=matrix::Zeros(rawccnt,rawccnt); CAblas::RMatrixTranspose(rawccnt,n,rawc,0,0,t3,0,0); for(i=0; i::Zeros(rawccnt); for(i=0; i::Zeros(rawccnt); bndu=vector::Full(rawccnt,AL_POSINF); CMinQP::MinQPCreate(rawccnt,state2); CMinQP::MinQPSetAlgoQuickQP(state2,0.0,0.0,1.0E-9,0,true); CMinQP::MinQPSetLinearTerm(state2,b2); CMinQP::MinQPSetQuadraticTerm(state2,a2,true); CMinQP::MinQPSetBC(state2,bndl,bndu); CMinQP::MinQPOptimize(state2); CMinQP::MinQPResults(state2,x2,rep2); if(rep2.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5421"); continue; } for(i=0; i(double)(xtol*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:5434"); } } //+------------------------------------------------------------------+ //| This function tests special inequality constrained QP problems. | //| Returns True on errors. | //+------------------------------------------------------------------+ bool CTestMinQPUnit::SpecialICQPTests(void) { //--- create variables bool result; CMatrixDouble a; CMatrixDouble c; CRowDouble xstart; CRowDouble xend; CRowDouble xexact; CRowDouble b; CRowDouble bndl; CRowDouble bndu; CRowInt ct; CMinQPState state; CMinQPReport rep; bool waserrors=false; int i=0; int j=0; //--- Test 1: reported by Vanderlande Industries. //--- Tests algorithm ability to handle degenerate constraints. a=matrix::Identity(3,3); b.Resize(3); b.Set(0,-50); b.Set(1,-50); b.Set(2,-75); bndl=vector::Zeros(3); bndu=vector::Full(3,100); bndu.Set(2,150); xstart=vector::Zeros(3); xstart.Set(1,100); xexact=xstart; xexact.Set(2,50); c=matrix::Zeros(3,4); c.Set(0,0,1); c.Set(0,1,-1); c.Set(0,2,0); c.Set(0,3,-100); c.Set(1,0,1); c.Set(1,1,0); c.Set(1,2,-1); c.Set(1,3,0); c.Set(2,0,-1); c.Set(2,1,0); c.Set(2,2,1); c.Set(2,3,50); ct.Resize(3); ct.Set(0,-1); ct.Set(1,-1); ct.Set(2,-1); CMinQP::MinQPCreate(3,state); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetQuadraticTerm(state,a,true); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,3); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype>0) { for(i=0; i<=2; i++) waserrors=waserrors||(double)(MathAbs(xend[i]-xexact[i]))>(1.0E6*CMath::m_machineepsilon); } else waserrors=true; //--- Test 2: reported by Vanderlande Industries. //--- Tests algorithm ability to handle degenerate constraints. a=matrix::Identity(3,3); b=vector::Full(3,-50); b.Set(2,-75); bndl=vector::Zeros(3); bndu=vector::Full(3,100); bndu.Set(2,150); xstart=bndu; xstart.Set(0,0); xexact=xstart; xexact.Set(2,100); c=matrix::Zeros(3,4); c.Set(0,0,1); c.Set(0,1,-1); c.Set(0,3,-100); c.Set(1,1,1); c.Set(1,2,-1); c.Set(2,1,-1); c.Set(2,2,1); c.Set(2,3,50); ct.Resize(3); ct.Fill(-1); CMinQP::MinQPCreate(3,state); SetRandomAlgoConvexLC(state); CMinQP::MinQPSetLinearTerm(state,b); CMinQP::MinQPSetQuadraticTerm(state,a,true); CMinQP::MinQPSetStartingPoint(state,xstart); CMinQP::MinQPSetBC(state,bndl,bndu); CMinQP::MinQPSetLC(state,c,ct,3); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xend,rep); if(rep.m_terminationtype>0) { for(i=0; i<=2; i++) waserrors=waserrors||(double)(MathAbs(xend[i]-xexact[i]))>(1.0E6*CMath::m_machineepsilon); } else waserrors=true; //--- return result result=waserrors; return(result); } //+------------------------------------------------------------------+ //| This function tests linearly constrained DENSE-AUL solver | //| On failure sets Err to True; on success leaves it unchanged. | //+------------------------------------------------------------------+ void CTestMinQPUnit::DenseAULTests(bool &errorflag) { CHighQualityRandState rs; int n=0; int i=0; int j=0; int k=0; double v=0; double vv=0; int scaletype=0; CMatrixDouble rawa; CMatrixDouble z; CRowDouble bndl; CRowDouble bndu; CMatrixDouble rawc; CRowInt rawct; int rawccnt=0; CRowDouble b; CRowDouble x0; CRowDouble xf; CRowDouble r; CRowDouble xsol; CRowDouble s; CMinQPState state; CMinQPReport rep; double epsx=0; double xtol=0; double rho=0; int outerits=0; CDenseSolverReport svrep; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Test that unconstrained problem is solved with high precision, //--- independently of Rho and/or outer iterations count. //--- 50% of problems are rescaled wildly (with scale being passed to //--- the solver). epsx=1.0E-12; xtol=1.0E-7; for(n=1; n<=10; n++) { for(scaletype=0; scaletype<=1; scaletype++) { //--- Generate random A, b, X0 and XSOL CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Ones(n); if(scaletype>0) for(i=0; ixtol,"testminqpunit.ap:7158"); } } //--- Test that problem with zero constraint matrix can be solved //--- (with high precision). We do not perform any additional "tweaks" //--- like scaling of variables, just want to test ability to handle //--- zero matrices. epsx=1.0E-12; xtol=1.0E-7; for(n=1; n<=10; n++) { //--- Generate random A, b, X0 and XSOL CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; ixtol,"testminqpunit.ap:7222"); } //--- Test that box/linearly inequality constrained problem with ALL constraints //--- being inactive at BOTH initial and final points is solved with high precision. //--- 50% of problems are rescaled wildly (with scale being passed to //--- the solver). epsx=1.0E-12; xtol=1.0E-7; for(n=1; n<=10; n++) { for(scaletype=0; scaletype<=1; scaletype++) { //--- Generate random A, b, X0 and XSOL CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(n); bndu=vector::Zeros(n); for(i=0; i::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i0.0) { rawct.Set(i,1); rawc.Set(i,n,MathMin(v,vv)-1-CHighQualityRand::HQRndUniformR(rs)); } else { rawct.Set(i,-1); rawc.Set(i,n,MathMax(v,vv)+1+CHighQualityRand::HQRndUniformR(rs)); } } //--- Generate scale vector, apply it s=vector::Ones(n); if(scaletype>0) for(i=0; ixtol,"testminqpunit.ap:7343"); } } //--- Test that linear equality constrained problem is solved with high precision. //--- 50% of problems are rescaled wildly (variable scaling, with scale being //--- passed to the solver). epsx=1.0E-12; xtol=1.0E-6; for(n=1; n<=10; n++) { for(scaletype=0; scaletype<=1; scaletype++) { //--- Generate random A, b, X0, constraints CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0) { rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i::Ones(n); if(scaletype>0) for(i=0; i::Zeros(n+rawccnt,n+rawccnt); r=vector::Zeros(n+rawccnt); for(i=0; i0,"MinQPTest: integrity check failed")) { errorflag=true; return; } //--- Compare against analytically known solution if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7453"); return; } for(i=0; ixtol,"testminqpunit.ap:7457"); } } //--- MEDIUM-SCALE VERSION OF PREVIOUS TEST. //--- Test that linear equality constrained problem is solved with high precision. //--- 50% of problems are rescaled wildly (variable scaling, with scale being //--- passed to the solver). epsx=1.0E-12; xtol=1.0E-6; for(n=99; n<=101; n++) { for(scaletype=0; scaletype<=1; scaletype++) { //--- Generate random A, b, X0, constraints CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0) { rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i::Ones(n); if(scaletype>0) for(i=0; i::Zeros(n+rawccnt,n+rawccnt); r=vector::Zeros(n+rawccnt); for(i=0; i0,"MinQPTest: integrity check failed")) { errorflag=true; return; } //--- Compare against analytically known solution if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7569"); return; } for(i=0; ixtol,"testminqpunit.ap:7573"); } } //--- Test that constraints are automatically scaled to adapt to problem curvature //--- (that multiplication of A and b by some large/small number does not affect //--- solver). //--- We generate random well-scaled problem, and multiply A/b by some large/small number, //--- and test that problem is still solved with high precision. //--- NOTE: just to make things worse, we rescale variables randomly, but primary //--- idea of this test is to check for multiplication of A/B epsx=1.0E-12; xtol=1.0E-6; for(n=1; n<=10; n++) { for(scaletype=-1; scaletype<=1; scaletype++) { //--- Generate random A, b, X0, constraints CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0) { rawc=matrix::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i::Zeros(n+rawccnt,n+rawccnt); r=vector::Zeros(n+rawccnt); for(i=0; i0) xsol.Resize(n); if(!CAp::Assert(k>0,"MinQPTest: integrity check failed")) { errorflag=true; return; } //--- Generate scale vector, apply it s=vector::Ones(n); for(i=0; ixtol,"testminqpunit.ap:7700"); } } //--- Test that problem with slight negative curvature f = -0.001*|x|^2 //--- subject to general linear (!) constraints -1 <= x[i] <= +1 is //--- correctly solved. Initial point is in [-0.001,+0.001] range. //--- NOTE: this test is mostly intended for working set selection algorithm; //--- it must correctly detect non-SPD problems and select full working //--- set; starting with zero working set will result in recognition of //--- problem as unbounded one and premature termination of algorithm. epsx=1.0E-12; xtol=1.0E-6; for(n=1; n<=10; n++) { //--- Generate A, b, X0, constraints rawa=matrix::Zeros(n,n); b=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(2*n,n+1); rawct.Resize(2*n); for(i=0; ixtol,"testminqpunit.ap:7769"); } } //+------------------------------------------------------------------+ //| This function tests IPM QP solver | //| On failure sets Err to True; on success leaves it unchanged. | //+------------------------------------------------------------------+ void CTestMinQPUnit::IPMTests(bool &errorflag) { //--- create variables CHighQualityRandState rs; int n=0; int nmain=0; int nslack=0; int i=0; int j=0; int k=0; double v=0; int nnz=0; int nactive=0; double f0=0; double f1=0; CMatrixDouble maina; CMatrixDouble fulla; CRowDouble s; CRowDouble xorigin; CRowDouble xf; CRowDouble x1; CRowDouble g; CRowDouble b; CRowDouble gtrial; CRowDouble bndl; CRowDouble bndu; int akind=0; CMatrixDouble rawc; CRowDouble rawcl; CRowDouble rawcu; int rawccnt=0; CSparseMatrix sparsec; int sparseccnt=0; CMatrixDouble densec; int denseccnt=0; bool issemidefinite; CRowDouble tmp; CRowDouble lagbc; CRowDouble laglc; CSparseMatrix dummysparse; CVIPMState vsolver; CRowDouble replagbc; CRowDouble replaglc; int repterminationtype=0; double epsx=0; double xtol=0; CRowDouble xsol; CRowDouble x0; CRowInt rawct; CMinQPState state; CMinQPReport rep; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Test handling of slack variables by IPM solver. We use IPM directly, //--- without wrapping it with MinQP interface layer. //--- Convex optimization problem with known answer (we generate constraints //--- and Lagrange coefficients, and then generate quadratic and linear term //--- which satisfy KKT conditions for given Lagrange coefficients): //--- * N=2..8 //--- * f = 0.5*x'*A*x+b'*x //--- * several kinds of A are tried: zero, well conditioned SPD, low rank //--- * initial point x0 = [0 0 ... 0 0] //--- * first, we generate set of linear constraints (without bounds), //--- solution point X and Lagrange multipliers for linear and box constraints //--- * then we determine bounds on variables and linear constraints which //--- align with values/signs of Lagrange multipliers //--- * then, having quadratic term A and known constraints and Lagrange //--- multipliers we determine linear term B which makes KKT conditions true epsx=1.0E-12; for(nmain=1; nmain<=8; nmain++) { for(nslack=0; nslack<=8; nslack++) { for(akind=0; akind<=2; akind++) { n=nmain+nslack; //--- Generate quadratic term A, origin and scale maina=matrix::Zeros(nmain,nmain); issemidefinite=true; if(akind==1) { //--- Dense well conditioned SPD CMatGen::SPDMatrixRndCond(nmain,50.0,maina); issemidefinite=false; } if(akind==2) { //--- Low rank semidefinite tmp=vector::Zeros(nmain); for(k=1; k<=MathMin(3,nmain-1); k++) { for(i=0; i0; fulla=matrix::Zeros(n,n); for(i=0; i::Zeros(n); xorigin=vector::Zeros(n); for(j=0; j0) { rawc=matrix::Zeros(rawccnt,n); for(i=0; i0.0) { v=CHighQualityRand::HQRndNormal(rs); v+=0.1*CApServ::PosSign(v); rawc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),j,v); } } //--- Generate Lagrange multipliers, with 0<=NActive<=N-1 being non-zero lagbc=vector::Zeros(n); laglc=vector::Zeros(rawccnt); nactive=CHighQualityRand::HQRndUniformI(rs,n); k=CHighQualityRand::HQRndUniformI(rs,MathMin(nactive,rawccnt)+1); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Full(n,AL_NEGINF); bndu=vector::Full(n,AL_POSINF); for(j=0; j0.0 || issemidefinite) || j>=nmain) bndl.Set(j,xf[j]-MathPow(2,CHighQualityRand::HQRndNormal(rs))); if((CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite) || j>=nmain) bndu.Set(j,xf[j]+MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(lagbc[j]!=0.0) { if(CHighQualityRand::HQRndUniformR(rs)<0.15) { bndl.Set(j,xf[j]); bndu.Set(j,xf[j]); } else { if(lagbc[j]<0.0) bndl.Set(j,xf[j]); if(lagbc[j]>0.0) bndu.Set(j,xf[j]); } } } rawcl=vector::Full(rawccnt,AL_NEGINF); rawcu=vector::Full(rawccnt,AL_POSINF); for(i=0; i0.0) rawcl.Set(i,v-MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(CHighQualityRand::HQRndNormal(rs)>0.0) rawcu.Set(i,v+MathPow(2,CHighQualityRand::HQRndNormal(rs))); if(laglc[i]!=0.0) { if(CHighQualityRand::HQRndUniformR(rs)<0.15) { //--- Active equality constraint rawcl.Set(i,v); rawcu.Set(i,v); } else { //--- Active inequality constraint if(laglc[i]<0.0) rawcl.Set(i,v); if(laglc[i]>0.0) rawcu.Set(i,v); } } if(!MathIsValidNumber(rawcl[i]) && !MathIsValidNumber(rawcu[i])) { //--- At least one bound must be present for linear constraint if(CHighQualityRand::HQRndNormal(rs)>0.0) rawcl.Set(i,v-10); else rawcu.Set(i,v+10); } } //--- Randomly split constraints into dense and sparse parts sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1); denseccnt=rawccnt-sparseccnt; if(sparseccnt>0) { CSparse::SparseCreate(sparseccnt,n,0,sparsec); for(i=0; i0) { densec=matrix::Zeros(denseccnt,n); for(i=0; i0.0,b); CVIPMSolver::VIPMSetConstraints(vsolver,bndl,bndu,sparsec,sparseccnt,densec,denseccnt,rawcl,rawcu); CVIPMSolver::VIPMSetCond(vsolver,epsx,epsx,epsx); CVIPMSolver::VIPMOptimize(vsolver,false,x1,replagbc,replaglc,repterminationtype); CAp::SetErrorFlag(errorflag,repterminationtype<=0,"testminqpunit.ap:8095"); CAp::SetErrorFlag(errorflag,CAp::Len(x1)(CApServ::RMaxAbs3(f0,f1,1.0)*1.0E-3),"testminqpunit.ap:8107"); //--- Test Lagrange multipliers returned by the solver gtrial=vector::Zeros(n); for(i=0; i1.0E-3,"testminqpunit.ap:8126"); } } } //--- Test that problem with zero constraint matrix can be solved //--- (with high precision). We do not perform any additional "tweaks" //--- like scaling of variables, just want to test ability to handle //--- zero matrices. epsx=1.0E-8; xtol=1.0E-5; for(n=1; n<=10; n++) { //--- Generate random A, b, X0 and XSOL CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),fulla); b=vector::Zeros(n); xsol=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(rawccnt,n+1); rawct.Resize(rawccnt); for(i=0; i0.0) CMinQP::MinQPSetAlgoDenseIPM(state,epsx); else CMinQP::MinQPSetAlgoSparseIPM(state,epsx); CMinQP::MinQPSetLinearTerm(state,b); RandomlySelectConvertAndSetQuadraticTerm(fulla,n,state,rs); CMinQP::MinQPSetLC(state,rawc,rawct,rawccnt); CMinQP::MinQPSetStartingPoint(state,x0); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xf,rep); //--- Compare against analytically known solution if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:8187"); return; } for(i=0; ixtol,"testminqpunit.ap:8191"); } } //+------------------------------------------------------------------+ //| This function tests various special properties | //| On failure sets Err to True; on success leaves it unchanged. | //+------------------------------------------------------------------+ void CTestMinQPUnit::SpecTests(bool &errorflag) { //--- create variables CHighQualityRandState rs; CMinQPState state; CMinQPReport rep; CMatrixDouble rawa; CRowDouble b; CRowDouble s; CRowDouble x0; CRowDouble xs; CRowDouble xs2; CRowDouble bndl; CRowDouble bndu; int n=0; int i=0; int j=0; double xtol=0; CHighQualityRand::HQRndRandomize(rs); //--- Test correctness of MinQPSetScaleDiagAuto(): //--- * that it correctly handles matrices with positive diagonals //--- * that it correctly handles non-positive elements //--- First test is performed by comparing one step of automatically //--- scaled QP-BLEIC with one step of manually scaled QP-BLEIC. xtol=1.0E-6; for(n=1; n<=10; n++) { //--- Generate random box constrained QP problem with specially //--- crafted diagonal. rawa=matrix::Zeros(n,n); b=vector::Zeros(n); x0=vector::Zeros(n); bndl=vector::Full(n,-1.0); bndu=vector::Full(n,1.0); for(i=0; ixtol,"testminqpunit.ap:8281"); //--- Check that automatic scaling fails for matrices with zero or negative elements i=CHighQualityRand::HQRndUniformI(rs,n); j=CHighQualityRand::HQRndUniformI(rs,2)-1; rawa.Set(i,i,j); RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs); CMinQP::MinQPSetScaleAutoDiag(state); CMinQP::MinQPOptimize(state); CMinQP::MinQPResults(state,xs,rep); CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=-9,"testminqpunit.ap:8294"); } } //+------------------------------------------------------------------+ //| Function normal | //+------------------------------------------------------------------+ double CTestMinQPUnit::ProjectedAntiGradNorm(int n,CRowDouble &x, CRowDouble &g, CRowDouble &bndl, CRowDouble &bndu) { //--- create variables double result=0; int i=0; double r=0; for(i=0; i=bndl[i] && x[i]<=bndu[i],"ProjectedAntiGradNormal: boundary constraints violation")) return(EMPTY_VALUE); if(((x[i]>bndl[i] && x[i]0.0)) || (x[i]==bndu[i] && (double)(-g[i])<0.0)) r+=g[i]*g[i]; } //--- return result result=MathSqrt(r); return(result); } //+------------------------------------------------------------------+ //| This function tests that norm of bound-constrained gradient at | //| point X is less than Eps: | //| * unconstrained gradient is A*x+b | //| * if I-th component is at the boundary, and antigradient points| //| outside of the feasible area, I-th component of constrained | //| gradient is zero | //| This function accepts QP terms A and B, bound constraints, | //| current point, and performs test. Additionally, it checks that | //| point is feasible w.r.t. boundary constraints. | //| In case of failure, error flag is set. Otherwise, it is not | //| modified. | //| IMPORTANT: this function does NOT use SetErrorFlag() to modify | //| flag. If you want to use SetErrorFlag() for easier | //| tracking of errors, you should store flag returned by | //| this function into separate variable TmpFlag and call | //| SetErrorFlag(ErrorFlag, TmpFlag) yourself. | //+------------------------------------------------------------------+ void CTestMinQPUnit::TestBCGradAndFeasibility(CMatrixDouble &a, CRowDouble &b, CRowDouble &bndl, CRowDouble &bndu, int n,CRowDouble &x, double eps, bool &errorflag) { //--- create variables double g=0; double gnorm=0; for(int i=0; i0.0) g=0; if(x[i]==bndu[i] && g<0.0) g=0; gnorm+=CMath::Sqr(g); if(x[i]bndu[i]) errorflag=true; } gnorm=MathSqrt(gnorm); if(gnorm>eps) errorflag=true; } //+------------------------------------------------------------------+ //| set random type of the QP solver. | //| All "modern" solvers can be chosen. | //| OUTPUT PARAMETERS: | //| BCTol - expected precision of box constraints handling | //| assuming unit scale of variables. | //| LCTol - expected precinion of linear constraints | //| handling assuming unit scale of variables. | //| RESULT: | //| Solver type: | //| *-1 for QuickQP | //| * 0 for BLEIC-QP | //| * 1 for DENSE-AUL | //| * 2 for DENSE-IPM | //| * 3 for SPARSE-IPM | //| BCTol and LCTol have following meaning - if some constraint is | //| active, it means that we should be at most TOL units away from | //| boundary. It is possible that zero value is returned. | //| From definition it follows that if we stopped at more than TOL | //| units away from the boundary, gradient in corresponding direction| //| is nearly zero. | //+------------------------------------------------------------------+ int CTestMinQPUnit::SetRandomAlgoAllModern(CMinQPState &s, double &bctol, double &lctol) { //--- create variables int result=0; bctol=0; lctol=0; result=CMath::RandomInteger(5)-1; switch(result) { case -1: CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5); bctol=0; lctol=0; break; case 0: CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0); bctol=0; lctol=1.0E-8; break; case 1: CMinQP::MinQPSetAlgoDenseAUL(s,1.0E-12,1000.0,10); bctol=1.0E-3; lctol=1.0E-3; break; case 2: CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12); bctol=1.0E-3; lctol=1.0E-3; break; case 3: CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12); bctol=1.0E-3; lctol=1.0E-3; break; } //--- return result return(result); } //+------------------------------------------------------------------+ //| set random type of theQP solver | //+------------------------------------------------------------------+ void CTestMinQPUnit::SetRandomAlgoNonConvex(CMinQPState &s) { //--- create variables int i=0; i=1+CMath::RandomInteger(2); if(i==1) CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0); if(i==2) CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5); } //+------------------------------------------------------------------+ //| set random type of theQP solver | //+------------------------------------------------------------------+ void CTestMinQPUnit::SetRandomAlgoSemiDefinite(CMinQPState &s, double &bctol, double &lctol) { bctol=0; lctol=0; int i=1+CMath::RandomInteger(4); switch(i) { case 1: CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0); bctol=0; lctol=1.0E-8; break; case 2: CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5); bctol=0; lctol=0; break; case 3: CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12); bctol=1.0E-3; lctol=1.0E-3; break; case 4: CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12); bctol=1.0E-3; lctol=1.0E-3; break; } } //+------------------------------------------------------------------+ //| set random type of the QP solver, must support boundary | //| constraints | //+------------------------------------------------------------------+ void CTestMinQPUnit::SetRandomAlgoBC(CMinQPState &s) { int i=CMath::RandomInteger(2); if(i==0) CMinQP::MinQPSetAlgoQuickQP(s,0.0,0.0,1.0E-13,0,true); if(i==1) CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0); } //+------------------------------------------------------------------+ //| set random type of the QP solver, must support convex problems | //| with boundary/linear constraints | //+------------------------------------------------------------------+ void CTestMinQPUnit::SetRandomAlgoConvexLC(CMinQPState &s) { int i=CMath::RandomInteger(4); switch(i) { case 0: CMinQP::MinQPSetAlgoDenseAUL(s,1.0E-12,10000,15); break; case 1: CMinQP::MinQPSetAlgoBLEIC(s,0.0,0.0,1.0E-12,0); break; case 2: CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12); break; case 3: CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12); break; } } //+------------------------------------------------------------------+ //| set random type of the QP solver, | //| must support nonconvex problems with boundary/linear constraints | //+------------------------------------------------------------------+ void CTestMinQPUnit::SetRandomAlgoNonConvexLC(CMinQPState &s) { int i=CMath::RandomInteger(1); if(i==0) CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0); } //+------------------------------------------------------------------+ //| Convert dense matrix to sparse matrix using random format | //+------------------------------------------------------------------+ void CTestMinQPUnit::DenseToSparse(CMatrixDouble &a,int n, CSparseMatrix &s) { //--- create variables CSparseMatrix s0; CSparse::SparseCreate(n,n,n*n,s0); for(int i=0; i0) { CSparse::SparseCreate(sparseccnt,n+1,0,sparsec); sparsect.Resize(sparseccnt); for(i=0; i0) { densec=matrix::Zeros(denseccnt,n+1); densect.Resize(denseccnt); for(i=0; i0.5 || denseccnt*sparseccnt>0) CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt); else { if(denseccnt>0) CMinQP::MinQPSetLC(state,densec,densect,denseccnt); if(sparseccnt>0) CMinQP::MinQPSetLCSparse(state,sparsec,sparsect,sparseccnt); } } //+------------------------------------------------------------------+ //| Randomly split 2-sided constraints into dense and sparse parts | //| and set them; | //+------------------------------------------------------------------+ void CTestMinQPUnit::RandomlySplitAndSetLC2(CMatrixDouble &rawc, CRowDouble &rawcl, CRowDouble &rawcu, int rawccnt,int n, CMinQPState &state, CHighQualityRandState &rs) { //--- create variables CMatrixDouble densec; CRowDouble densecl; CRowDouble densecu; CSparseMatrix sparsec; CRowDouble sparsecl; CRowDouble sparsecu; CRowDouble wrkcl; CRowDouble wrkcu; int denseccnt=0; int sparseccnt=0; int i=0; int j=0; int appenddense=0; int appendsparse=0; CRowDouble cv; CRowInt ci; int nnz=0; //--- reset constraints CMinQP::MinQPSetLC2Dense(state,densec,densecl,densecu,0); //--- Split "raw" constraints into dense and sparse parts sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1); denseccnt=rawccnt-sparseccnt; appenddense=CHighQualityRand::HQRndUniformI(rs,denseccnt+1); denseccnt=denseccnt-appenddense; appendsparse=CHighQualityRand::HQRndUniformI(rs,sparseccnt+1); sparseccnt=sparseccnt-appendsparse; wrkcl=vector::Zeros(sparseccnt+denseccnt); wrkcu=vector::Zeros(sparseccnt+denseccnt); if(sparseccnt>0) { CSparse::SparseCreate(sparseccnt,n,0,sparsec); sparsecl=vector::Zeros(sparseccnt); sparsecu=vector::Zeros(sparseccnt); for(i=0; i0) { densec=matrix::Zeros(denseccnt,n); densecl=vector::Zeros(denseccnt); densecu=vector::Zeros(denseccnt); for(i=0; i0.5 || denseccnt*sparseccnt>0) CMinQP::MinQPSetLC2Mixed(state,sparsec,sparseccnt,densec,denseccnt,wrkcl,wrkcu); else { if(denseccnt>0) CMinQP::MinQPSetLC2Dense(state,densec,densecl,densecu,denseccnt); if(sparseccnt>0) CMinQP::MinQPSetLC2(state,sparsec,sparsecl,sparsecu,sparseccnt); } cv=vector::Zeros(2*n); ci.Resize(2*n); for(i=sparseccnt; i0.0) { //--- Add sparse constraint using AddLC2() //--- First, generate sparse representation nnz=0; for(j=0; j0 && nnz0.5; isdense=CHighQualityRand::HQRndUniformR(rs)>0.5; if(isupper && isdense) { densea=a.TriU()+0; CMinQP::MinQPSetQuadraticTerm(state,densea,isupper); return; } if(!isupper && isdense) { densea=a.TriL()+0; CMinQP::MinQPSetQuadraticTerm(state,densea,isupper); return; } if(isupper && !isdense) { CSparse::SparseCreate(n,n,0,sparsea); for(i=0; i0.0) result=svdw[MathMin(k,n)-1]/svdw[0]; else result=1; //--- return result return(result); } //+------------------------------------------------------------------+ //| Computes target function 0.5*x'*H*x+c'*x | //+------------------------------------------------------------------+ double CTestMinQPUnit::QuadraticTarget(CMatrixDouble &a, CRowDouble &b,int n, CRowDouble &x) { double result=0; for(int i=0; i::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); //--- Feasible bounded problems for(i=0; i0.0 && (x[i](bndl[i]+primtol)),"testminlpunit.ap:141"); CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:142"); CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:143"); CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:144"); } for(i=0; i0.0) bndu.Set(i,AL_POSINF); if(c[i]<0.0) bndl.Set(i,AL_NEGINF); } ShiftFromZero(c,n,0.01); CMinLP::MinLPCreate(n,state); SelectSolver(state,solvertype); CMinLP::MinLPSetCost(state,c); CMinLP::MinLPSetBC(state,bndl,bndu); CMinLP::MinLPOptimize(state); CMinLP::MinLPResults(state,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:166"); CAp::SetErrorFlag(err,CAp::Len(x)0.0 && (x[i](bndl[i]+primtol)),"testminlpunit.ap:172"); CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:173"); CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:174"); CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:175"); } //--- Feasible bounded problems with zeros in cost vector for(i=0; i0.0 && (x[i](bndl[i]+primtol)),"testminlpunit.ap:205"); CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:206"); CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:207"); CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:208"); } CAp::SetErrorFlag(err,x[k]!=0.0,"testminlpunit.ap:210"); bndl.Set(k,CHighQualityRand::HQRndNormal(rs)); bndu.Set(k,AL_POSINF); CMinLP::MinLPSetBC(state,bndl,bndu); CMinLP::MinLPOptimize(state); CMinLP::MinLPResults(state,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:217"); CAp::SetErrorFlag(err,CAp::Len(x)0.0 && (x[i](bndl[i]+primtol)),"testminlpunit.ap:223"); CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:224"); CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:225"); CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:226"); } CAp::SetErrorFlag(err,x[k]0.0 && (x[i](bndl[i]+primtol)),"testminlpunit.ap:242"); CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:243"); CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:244"); CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:245"); } CAp::SetErrorFlag(err,x[k]>bndu[k],"testminlpunit.ap:247"); CAp::SetErrorFlag(err,solvertype==0 && x[k]!=bndu[k],"testminlpunit.ap:248"); //--- Infeasible problems for(i=0; i0.0) bndl.Set(k,AL_NEGINF); else bndu.Set(k,AL_POSINF); CMinLP::MinLPCreate(n,state); SelectSolver(state,solvertype); CMinLP::MinLPSetCost(state,c); CMinLP::MinLPSetBC(state,bndl,bndu); CMinLP::MinLPOptimize(state); CMinLP::MinLPResults(state,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype!=-4,"testminlpunit.ap:300"); CAp::SetErrorFlag(err,CAp::Len(x)::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); a=matrix::Zeros(ccnt,n); al=vector::Zeros(ccnt); au=vector::Zeros(ccnt); for(i=0; i0.0 && (x[i](bndl[i]+primtol)),"testminlpunit.ap:349"); CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:350"); CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:351"); CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:352"); } } //--- Test linearly constrained problems with all variables being boxed ones //--- and with random linear constraints, with at least one feasible point //--- in the box internals. for(n=1; n<=10; n++) { solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount); ccnt=1+CHighQualityRand::HQRndUniformI(rs,10); c=vector::Zeros(n); x0=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); a=matrix::Zeros(ccnt,n); al=vector::Zeros(ccnt); au=vector::Zeros(ccnt); for(i=0; iprimtol,"testminlpunit.ap:404"); CAp::SetErrorFlag(err,MathAbs(v1)>dualtol,"testminlpunit.ap:405"); CAp::SetErrorFlag(err,MathAbs(errslack)>slacktol,"testminlpunit.ap:406"); } } //+------------------------------------------------------------------+ //| Single - call tests; | //| sets error flag on failure, does not touch it on success. | //+------------------------------------------------------------------+ void CTestMinLPUnit::SingleCallTests(bool &err) { //--- create variables double primtol=1.0E-3; double dualtol=1.0E-3; double slacktol=1.0E-3; double etol=1.0E-8; double ftol=1.0E-3; int n=0; int pass=0; CMinLPState state0; CMinLPReport rep0; CMinLPState state1; CMinLPReport rep1; int i=0; int j=0; CRowDouble c; CRowDouble bndl; CRowDouble bndu; CRowDouble al; CRowDouble au; CRowDouble s; CRowDouble bndls; CRowDouble bndus; CRowDouble cs; CMatrixDouble a; CMatrixDouble a1; CRowDouble x0; CRowDouble x1; int m=0; int n0=0; double v0=0; double v1=0; double f=0; double f1=0; double errp=0; double errd=0; double errs=0; double alpha=0; CHighQualityRandState rs; int solvertype=0; CHighQualityRand::HQRndRandomize(rs); //--- Try different feasible problems for(n=1; n<=50; n++) { for(pass=1; pass<=30; pass++) { solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount); //--- Generate random feasible problem and solve it using basic MinLPSetLC2Dense() API GenerateLPProblem(rs,n,c,bndl,bndu,a,al,au,m); CMinLP::MinLPCreate(n,state0); SelectSolver(state0,solvertype); CMinLP::MinLPSetCost(state0,c); CMinLP::MinLPSetBC(state0,bndl,bndu); CMinLP::MinLPSetLC2Dense(state0,a,al,au,m); CMinLP::MinLPOptimize(state0); CMinLP::MinLPResults(state0,x0,rep0); CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:470"); CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:471"); if(err) return; ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs); CAp::SetErrorFlag(err,MathAbs(errp)>primtol,"testminlpunit.ap:475"); CAp::SetErrorFlag(err,MathAbs(errd)>dualtol,"testminlpunit.ap:476"); CAp::SetErrorFlag(err,MathAbs(errs)>slacktol,"testminlpunit.ap:477"); CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,1)),"testminlpunit.ap:478"); CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,1)),"testminlpunit.ap:479"); CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,1)),"testminlpunit.ap:480"); //--- Apply random modification to the problem (and to the way //--- we pass constraints to the solver), try solving one more time //--- and compare with the original solution. //--- NOTE: because our test suite has degenerate problems, we do //--- not compare points returned - only target values are //--- compared. CMinLP::MinLPCreate(n,state1); SelectSolver(state1,solvertype); CMinLP::MinLPSetCost(state1,c); CMinLP::MinLPSetBC(state1,bndl,bndu); ModifyAndSendConstraintsTo(n,a,al,au,m,rs,state1); CMinLP::MinLPOptimize(state1); CMinLP::MinLPResults(state1,x1,rep1); CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:500"); CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:501"); if(err) return; f=CAblasF::RDotV(n,x0,c); f1=CAblasF::RDotV(n,x1,c); CAp::SetErrorFlag(err,MathAbs(f1-f)>ftol,"testminlpunit.ap:511"); //--- Test scaling (random scale is applied, target values for //--- original and scaled problems are compared). //--- NOTE: because our test suite has degenerate problems, we do //--- not compare points returned - only target values. s=vector::Zeros(n); cs=vector::Zeros(n); bndls=vector::Zeros(n); bndus=vector::Zeros(n); for(i=0; i::Zeros(m,n); for(i=0; i(1000.0*CMath::m_machineepsilon),"testminlpunit.ap:557"); } } //--- Test infeasible/unbounded problems for(n=1; n<=50; n++) { for(pass=1; pass<=30; pass++) { solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount); //--- Generate random primal unbounded //--- NOTE: because we use constraint validation code, we can not //--- use ModifyAndSendConstraintsTo() function - it permutes //--- constraints order and prevents correct validation. GenerateUnboundedLPProblem(rs,n,c,bndl,bndu,a,al,au,m); CMinLP::MinLPCreate(n,state0); SelectSolver(state0,solvertype); CMinLP::MinLPSetCost(state0,c); CMinLP::MinLPSetBC(state0,bndl,bndu); CMinLP::MinLPSetLC2Dense(state0,a,al,au,m); CMinLP::MinLPOptimize(state0); CMinLP::MinLPResults(state0,x0,rep0); CAp::SetErrorFlag(err,rep0.m_terminationtype!=-4 && rep0.m_terminationtype!=-2,"testminlpunit.ap:584"); CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:585"); if(err) return; ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs); CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:589"); CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:590"); CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:591"); //--- Generate random primal infeasible //--- NOTE: because we use constraint validation code, we can not //--- use ModifyAndSendConstraintsTo() function - it permutes //--- constraints order and prevents correct validation. GenerateInfeasibleLPProblem(rs,n,c,bndl,bndu,a,al,au,m); CMinLP::MinLPCreate(n,state0); SelectSolver(state0,solvertype); CMinLP::MinLPSetCost(state0,c); CMinLP::MinLPSetBC(state0,bndl,bndu); CMinLP::MinLPSetLC2Dense(state0,a,al,au,m); CMinLP::MinLPOptimize(state0); CMinLP::MinLPResults(state0,x0,rep0); CAp::SetErrorFlag(err,rep0.m_terminationtype!=-3 && rep0.m_terminationtype!=-2,"testminlpunit.ap:610"); CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:611"); if(err) return; ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs); CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:615"); CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:616"); CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:617"); } } for(n=2; n<=50; n++) { for(pass=1; pass<=30; pass++) { //--- Special test for simplex solver. //--- Generate carefully crafted primal infeasible - the problem is ALMOST feasible. //--- So, we expect simplex solver to Stop at the best point possible. We also test //--- that X and Y are correctly unscaled on return. //--- check if(!CAp::Assert(n>=2,"LPTEST: integrity check failed")) { err=true; return; } n0=n/2; //--- check if(!CAp::Assert(n0::Zeros(n); s=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); for(i=0; i::Zeros(m,n); al=vector::Zeros(m); au=vector::Zeros(m); for(i=0; i<=m-2; i++) { v0=0; for(j=n0; j0.0) CMinLP::MinLPSetScale(state0,s); CMinLP::MinLPSetBC(state0,bndl,bndu); CMinLP::MinLPSetLC2Dense(state0,a,al,au,m); CMinLP::MinLPOptimize(state0); CMinLP::MinLPResults(state0,x0,rep0); CAp::SetErrorFlag(err,rep0.m_terminationtype!=-3 && rep0.m_terminationtype!=-2,"testminlpunit.ap:686"); CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:687"); if(err) return; for(i=0; i0.001,"testminlpunit.ap:693"); CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,1)),"testminlpunit.ap:694"); CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,1)),"testminlpunit.ap:695"); CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,1)),"testminlpunit.ap:696"); } } //--- Check SetBCAll() and SetBCi() //--- We generate random problem with box constraints //--- L<=x[i]<=U, with L<0::Zeros(n); bndl=vector::Full(n,v0); bndu=vector::Full(n,v1); a=matrix::Zeros(m,n); al=vector::Zeros(m); au=vector::Zeros(m); for(i=0; i::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n)); x1=vector::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n)); CMinLP::MinLPCreate(n,state0); SelectSolver(state0,solvertype); CMinLP::MinLPSetCost(state0,c); CMinLP::MinLPSetBC(state0,bndl,bndu); CMinLP::MinLPSetLC2Dense(state0,a,al,au,m); CMinLP::MinLPOptimize(state0); CMinLP::MinLPResults(state0,x0,rep0); CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:754"); CAp::SetErrorFlag(err,CAp::Len(x0)!=n,"testminlpunit.ap:755"); if(err) return; CMinLP::MinLPCreate(n,state1); SelectSolver(state1,solvertype); CMinLP::MinLPSetCost(state1,c); CMinLP::MinLPSetBCAll(state1,v0,v1); CMinLP::MinLPSetLC2Dense(state1,a,al,au,m); CMinLP::MinLPOptimize(state1); CMinLP::MinLPResults(state1,x1,rep1); CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:765"); CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:766"); if(err) return; for(i=0; i::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n)); x1=vector::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n)); for(i=0; i::Zeros(n); c=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); //--- Choose problem type pcount=4; ptype=CHighQualityRand::HQRndUniformI(rs,pcount); mincoeff=0.001; //--- A linearly constrained LP problem with all variables/linear constraints //--- being boxed (easy start). if(ptype==0) { q=5.0; m=1+CHighQualityRand::HQRndUniformI(rs,2*n); a=matrix::Zeros(m,n); al=vector::Zeros(m); au=vector::Zeros(m); for(i=0; i::Zeros(m,n); al=vector::Zeros(m); au=vector::Zeros(m); for(i=0; i0.0) bndl.Set(j,xx[j]); else bndu.Set(j,xx[j]); } else { if(CHighQualityRand::HQRndNormal(rs)>0.0) al.Set(j-n,xx[j]); else au.Set(j-n,xx[j]); } } return; } CAp::Assert(false,"GenerateLPProblem failed"); } //+------------------------------------------------------------------+ //| This function generates random primal unbounded LP problem. | //+------------------------------------------------------------------+ void CTestMinLPUnit::GenerateUnboundedLPProblem(CHighQualityRandState &rs, int n,CRowDouble &c, CRowDouble &bndl, CRowDouble &bndu, CMatrixDouble &a, CRowDouble &al, CRowDouble &au,int &m) { //--- create variables int i=0; int j=0; int ptype=0; int pcount=0; double v=0; double vv=0; double q=0; CRowDouble x0; m=0; c=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); //--- Choose problem type pcount=2; ptype=CHighQualityRand::HQRndUniformI(rs,pcount); //--- Boundary-only constraints, can be easily made primal unbounded if(ptype==0) { q=5.0; m=0; for(i=0; i0.0) { bndl.Set(i,v); bndu.Set(i,AL_POSINF); } else { bndl.Set(i,AL_NEGINF); bndu.Set(i,v); } } i=CHighQualityRand::HQRndUniformI(rs,n); do { c.Set(i,CHighQualityRand::HQRndNormal(rs)); } while(c[i]==0.0); c.Add(i,0.1*MathSign(c[i])); v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs)); if(c[i]<0.0) { bndl.Set(i,v); bndu.Set(i,AL_POSINF); } else { bndl.Set(i,AL_NEGINF); bndu.Set(i,v); } return; } //--- Boundary and single-sided linear constraints; a bit more tricky to make it primal unbounded if(ptype==1) { q=5.0; m=1+CHighQualityRand::HQRndUniformI(rs,2*n); a=matrix::Zeros(m,n); al=vector::Zeros(m); au=vector::Zeros(m); x0=vector::Zeros(n); for(i=0; i=0.0) { bndl.Set(i,AL_NEGINF); bndu.Set(i,x0[i]); } else { bndl.Set(i,x0[i]); bndu.Set(i,AL_POSINF); } } for(i=0; i=0.0) { al.Set(i,AL_NEGINF); au.Set(i,v); } else { al.Set(i,v); au.Set(i,AL_POSINF); } } return; } CAp::Assert(false,"GenerateUnboundedLPProblem failed"); } //+------------------------------------------------------------------+ //| This function generates random primal infeasible LP problem | //+------------------------------------------------------------------+ void CTestMinLPUnit::GenerateInfeasibleLPProblem(CHighQualityRandState &rs, int n,CRowDouble &c, CRowDouble &bndl, CRowDouble &bndu, CMatrixDouble &a, CRowDouble &al, CRowDouble &au,int &m) { //--- create variables int i=0; int ptype=0; int pcount=0; double v=0; double q=0; double minerr=0; CRowDouble xx; CRowDouble d; CRowDouble x0; CRowInt tmpi; m=0; c=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); //--- Choose problem type pcount=3; ptype=CHighQualityRand::HQRndUniformI(rs,pcount); //--- Boundary-only constraints, can be easily made primal infeasible if(ptype==0) { q=5.0; m=0; for(i=0; i::Zeros(m,n); ax=matrix::Zeros(m,n+m); for(i=0; i::Zeros(n+m); for(i=0; i::Zeros(m); for(i=0; i=n) y.Set(basicnonbasic[i]-n,0); //--- Generate D and C d=vector::Zeros(n+m); CAblas::RMatrixGemVect(n+m,m,-1.0,ax,0,0,1,y,0,0.0,d,0); cx=vector::Zeros(n+m); for(i=0; i::Zeros(n); CAblasF::RCopyV(n,cx,c); } //+------------------------------------------------------------------+ //| This function generates random non-degenerate bounds for given XX| //| and D. | //+------------------------------------------------------------------+ void CTestMinLPUnit::GenerateBounds(CHighQualityRandState &rs, int n,int m, CRowDouble &xx, CRowDouble &d, CRowInt &basicnonbasic, CRowDouble &bndl, CRowDouble &bndu, CRowDouble &al, CRowDouble &au) { //--- create variables double v0=0; double v1=0; double q=0; int i=0; int kk=0; bndl=vector::Zeros(n); bndu=vector::Zeros(n); al=vector::Zeros(m); au=vector::Zeros(m); //--- Choose bounds in such a way that non-basic variables are //--- strictly at the bounds and dual feasible, and all basic //--- variables are strictly within bounds. q=5; for(kk=0; kk<=n+m-1; kk++) { i=basicnonbasic[kk]; v0=xx[i]-MathPow(q,CHighQualityRand::HQRndNormal(rs)); v1=xx[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs)); if(CHighQualityRand::HQRndNormal(rs)>0.0) v0=AL_NEGINF; if(CHighQualityRand::HQRndNormal(rs)>0.0) v1=AL_POSINF; if(kk>=m) { if((double)(d[i])>=0.0) { v0=xx[i]; if(CHighQualityRand::HQRndUniformR(rs)<0.05) v1=v0; } else { v1=xx[i]; if(CHighQualityRand::HQRndUniformR(rs)<0.05) v0=v1; } } if(i::Zeros(2*m,n+1); ct.Resize(2*m); ccnt=0; for(i=0; i0.0) CSparse::SparseConvertToCRS(sa); CMinLP::MinLPSetLC2(state,sa,al,au,m); return; } //--- A few rows are added with SetLC2Dense() call, the rest //--- is processed with random mix of AddLC2Dense()/AddLC2() if(stype==3) { if(m==0) { CMinLP::MinLPSetLC2(state,sa,al,au,0); return; } minit=CHighQualityRand::HQRndUniformI(rs,m); CMinLP::MinLPSetLC2Dense(state,a,al,au,minit); for(i=minit; i::Zeros(n); for(j=0; j::Zeros(n); idxi.Resize(n); nz=0; for(j=0; j::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetCond(state,1.0E-7,0); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:245"); } if(waserrors) return; //--- Check constraint violation reports CAp::SetErrorFlag(waserrors,rep.m_bcerr>tolx,"testminnlcunit.ap:253"); CAp::SetErrorFlag(waserrors,rep.m_bcidx>=n,"testminnlcunit.ap:254"); CAp::SetErrorFlag(waserrors,rep.m_lcerr>0.0,"testminnlcunit.ap:255"); CAp::SetErrorFlag(waserrors,rep.m_lcidx>=0,"testminnlcunit.ap:256"); //--- Check feasibility properties for(i=0; i=(double)(bndu[i]+tolx*s[i]),"testminnlcunit.ap:264"); } //--- Test - calculate scaled constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<(double)(tolx*s[i])) && g<0.0) g=0; gnorm+=CMath::Sqr(g); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(waserrors,gnorm>tolg,"testminnlcunit.ap:286"); } } } //--- Non-convex test: //--- * N dimensions, N>=2 //--- * box constraints, x[i] in [-1,+1] //--- * A is symmetric indefinite with condition number 50.0 //--- * random B with normal entries //--- * initial point is random, feasible //--- * scale is always unit //--- We check that constrained problem can be successfully solved. //--- We do not check ability to detect unboundedness of unconstrained //--- problem because there is such functionality in MinNLC. aulits=50; rho=200.0; tolx=0.0005; tolg=0.01; for(n=2; n<=10; n++) { for(pass=1; pass<=10; pass++) { for(prectype=0; prectype<=2; prectype++) { //--- Generate problem fulla.Fill(0.0); for(i=0; i::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetCond(state,1.0E-7,0); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:383"); } if(waserrors) return; //--- Check feasibility properties for(i=0; i=(double)(bndu[i]+tolx),"testminnlcunit.ap:394"); } //--- Test - calculate scaled constrained gradient at solution, //--- check its norm. gnorm=0.0; for(i=0; i0.0) g=0; if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])tolg,"testminnlcunit.ap:415"); } } } } } //+------------------------------------------------------------------+ //| This function tests linearly constrained quadratic programming | //| algorithm. | //| Sets error flag on failure. | //+------------------------------------------------------------------+ void CTestMinNLCUnit::TestLC(bool &waserrors) { //--- create variables int n=0; int k=0; int i=0; int j=0; int pass=0; CMatrixDouble q; CMatrixDouble fulla; double v=0; double vv=0; CRowDouble tmp; CRowDouble bl; CRowDouble bu; CRowDouble b; CRowDouble xs0; CRowDouble xstart; CRowDouble x; CRowDouble x0; CRowDouble x1; CRowDouble x2; CRowDouble xm; CRowDouble s; CRowDouble g; CRowDouble bndl; CRowDouble bndu; CMatrixDouble a; CMatrixDouble c; CMatrixDouble ce; CRowInt ct; bool nonnegative[]; double tolx=0; double tolg=0; double tolf=0; int aulits=0; double rho=0; CMinNLCState state; CMinNLCReport rep; COptGuardReport ogrep; int scaletype=0; double f0=0; double f1=0; double tolconstr=0; int bscale=0; int akind=0; int ccnt=0; int shiftkind=0; int prectype=0; int solvertype=0; double gnrm2=0; CHighQualityRandState rs; CSNNLSSolver nnls; int i_=0; CHighQualityRand::HQRndRandomize(rs); for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++) { //--- First test: //--- * K::Zeros(n); x0=vector::Zeros(n); xm=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i::Zeros(n); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:585"); } if(waserrors) return; //--- Check constraint violation reports CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:593"); CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:594"); CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:595"); CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:596"); //--- Compare with analytic solution f0=0; f1=0; for(i=0; i(double)(tolf),"testminnlcunit.ap:608"); } } } //--- Inequality constrained problem: //--- * N*N diagonal A //--- * one inequality constraint q'*x>=0, where q is random unit vector //--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x, //--- where x1 is some random vector //--- * either: //--- a) x1 is feasible => we must Stop at x1 //--- b) x1 is infeasible => we must Stop at the boundary q'*x=0 and //--- projection of gradient onto q*x=0 must be zero //--- NOTE: we make several passes because some specific kind of errors is rarely //--- caught by this test, so we need several repetitions. rho=200.0; tolx=0.0005; tolg=0.01; aulits=50; for(n=2; n<=6; n++) { for(pass=0; pass<=4; pass++) { //--- Generate problem: A, b, CMatrix, x0, XStart CMatGen::SPDMatrixRndCond(n,1.0E2,fulla); b=vector::Zeros(n); xm=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(1,n+1); ct.Resize(1); for(i=0; i::Zeros(n); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:735"); } if(waserrors) return; //--- Check constraint violation reports CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:743"); CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:744"); CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:745"); CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:746"); //--- Test solution g=b; for(i=0; itolg,"testminnlcunit.ap:779"); } } //--- Equality-constrained test: //--- * N*N SPD A //--- * K::Zeros(n); x0=vector::Zeros(n); xm=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:879"); } if(waserrors) return; //--- Check constraint violation reports CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:887"); CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:888"); CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:889"); CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:890"); //--- Check feasibility properties and gradient projection for(i=0; itolx,"testminnlcunit.ap:898"); } g=b; for(i=0; itolg,"testminnlcunit.ap:913"); } } //--- Boundary constraints vs linear ones: //--- * N*N SPD A //--- * optimization problem has form 0.5*x'*A*x-(xm*A)*x, //--- where xm is some random vector from [-1,+1] //--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i], //--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0] //--- * initial point xstart is from [-1,+2] //--- * we solve two related QP problems: //--- a) one with constraints posed as boundary ones //--- b) another one with same constraints posed as general linear ones //--- both problems must have same solution. //--- Here we test that boundary constrained and linear inequality constrained //--- solvers give same results. rho=200.0; tolx=0.0005; tolf=0.00001; aulits=50; for(n=1; n<=6; n++) { //--- Generate problem: A, b, x0, XStart, C, CT CMatGen::SPDMatrixRndCond(n,1.0E2,fulla); b=vector::Zeros(n); xm=vector::Zeros(n); x0=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); ct.Resize(2*n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1018"); } if(waserrors) return; //--- Solve boundary constrained problem CMinNLC::MinNLCCreate(n,x0,state); if(solvertype==0) CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); else { if(solvertype==1) CMinNLC::MinNLCSetAlgoSLP(state); else { if(solvertype==2) CMinNLC::MinNLCSetAlgoSQP(state); else { CAp::Assert(false); waserrors=true; return; } } } CMinNLC::MinNLCSetBC(state,bndl,bndu); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i(double)(tolf),"testminnlcunit.ap:1080"); } //--- Boundary and linear equality constrained QP problem with excessive //--- equality constraints: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix, //--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9) //--- * optimization problem has form 0.5*x'*A*x-b*x, //--- where b is some random vector //--- * because constraints are excessive, the main problem is to find //--- feasible point; the only existing feasible point is solution, //--- so we have to check only feasibility rho=1000.0; tolx=0.0005; aulits=10; for(n=1; n<=6; n++) { //--- Generate problem: A, b, BndL, BndU, CMatrix, x0, xm, XStart k=2*n; CMatGen::SPDMatrixRndCond(n,1.0E2,fulla); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xm=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1182"); } if(waserrors) return; for(i=0; itolx,"testminnlcunit.ap:1189"); } } //--- Boundary and linear equality/inequality constrained QP problem with //--- excessive constraints: //--- * N*N SPD A with moderate condtion number (up to 100) //--- * boundary constraints 0<=x[i]<=1 //--- * K=2*N equality/inequality constraints: //--- * N/2 equality ones q'*x = q'*xm for random vector q //--- * the rest are inequality ones, feasible at xm (xm is an inner point for these constraints) //--- where xm is some random vector from the feasible hypercube (0.1<=xm[i]<=0.9) //--- * optimization problem has form 0.5*x'*A*x-b*x, //--- where b is some random vector //--- * because constraints are excessive, the main problem is to find //--- feasible point; we do not check that algorithm found a solution, //--- we just check that it found feasible point. //--- NOTE: this problem is difficult one (estimates of Lagrange multipliers converge //--- slowly), so we use relaxed tolerances - 0.010 for AUL solver rho=1000.0; aulits=30; for(n=1; n<=6; n++) { //--- Generate problem: A, b, BndL, BndU, CMatrix, xm, x1, XStart k=2*n; CMatGen::SPDMatrixRndCond(n,1.0E2,fulla); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); xm=vector::Zeros(n); x0=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0) c.Add(i,n,-0.1); else c.Add(i,n,0.1); } //--- Create optimizer, solve CMinNLC::MinNLCCreate(n,x0,state); if(solvertype==0) { CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); tolx=0.0050; } else { if(solvertype==1) { CMinNLC::MinNLCSetAlgoSLP(state); tolx=0.0010; } else { if(solvertype==2) { CMinNLC::MinNLCSetAlgoSQP(state); tolx=0.0010; } else { CAp::Assert(false); waserrors=true; return; } } } CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetLC(state,c,ct,k); CMinNLC::MinNLCSetCond(state,1.0E-7,0); if(CHighQualityRand::HQRndNormal(rs)>0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1314"); } if(waserrors) return; for(i=0; itolx,"testminnlcunit.ap:1322"); if(ct[i]>0) CAp::SetErrorFlag(waserrors,v<(c.Get(i,n)-tolx),"testminnlcunit.ap:1324"); if(ct[i]<0) CAp::SetErrorFlag(waserrors,v>(c.Get(i,n)+tolx),"testminnlcunit.ap:1326"); } } //--- Boundary and linear equality constrained QP problem, //--- test checks that different starting points yield same final point: //--- * random N from [1..6], random K from [1..N-1] //--- * N*N SPD A with moderate condtion number (important!) //--- * boundary constraints 0<=x[i]<=1 //--- * K::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); xstart=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1435"); } if(waserrors) return; //--- Start from another point for(i=0; i(double)(tolf),"testminnlcunit.ap:1486"); } //--- Convex/nonconvex optimization problem with excessive //--- (degenerate constraints): //--- * N=2..7 //--- * f = 0.5*x'*A*x+b'*x //--- * b has normally distributed entries with scale 10^BScale //--- * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank //--- * box constraints: x[i] in [-1,+1] //---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift, //--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is //--- a shift parameter: //--- * with zero v_shift such constraints are degenerate (each vertex has //--- N box constraints and one "redundant" linear constraint) //--- * with positive v_shift linear constraint is always inactive //--- * with small (about machine epsilon) but negative v_shift, //--- constraint is close to degenerate - but not exactly //--- We check that constrained gradient is close to zero at solution. //--- Box constraint is considered active if distance to boundary is less //--- than TolConstr. //--- NOTE: because AUL algorithm is less exact than its active set counterparts, //--- VERY loose tolerances are used for this test. tolconstr=1.0E-2; for(n=2; n<=6; n++) { for(akind=0; akind<=3; akind++) { //--- Choose random parameters shiftkind=CHighQualityRand::HQRndUniformI(rs,7)-5; bscale=CHighQualityRand::HQRndUniformI(rs,3)-2; //--- Generate A, B and initial point a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); bu=vector::Zeros(n); for(i=0; i::Zeros(ccnt,n+1); ct.Resize(ccnt); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { CAp::Assert(state.m_needfij); state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1645"); } if(waserrors) return; //--- Evaluate gradient at solution and test vv=0.0; gnrm2=0; for(i=0; i0.0) v=0.0; if(xs0[i]>=(bu[i]-tolconstr) && v<0.0) v=0.0; vv+=CMath::Sqr(v); } vv=MathSqrt(vv/(gnrm2+1.0)); CAp::SetErrorFlag(waserrors,vv>(1.0E-2),"testminnlcunit.ap:1672"); } } //--- Linear/convex optimization problem with combination of //--- box and linear constraints: //--- * N=2..8 //--- * f = 0.5*x'*A*x+b'*x //--- * b has normally distributed entries with scale 10^BScale //--- * several kinds of A are tried: zero, well conditioned SPD //--- * box constraints: x[i] in [-1,+1] //--- * initial point x0 = [0 0 ... 0 0] //--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0. //--- random mix of equality/inequality constraints is tried. //--- x0 is guaranteed to be feasible. //--- We check that constrained gradient is close to zero at solution. //--- Inequality constraint is considered active if distance to boundary //--- is less than TolConstr. We use nonnegative least squares solver //--- in order to compute constrained gradient. tolconstr=1.0E-2; for(n=2; n<=8; n++) { for(akind=0; akind<=1; akind++) { for(bscale=0; bscale>=-2; bscale--) { //--- Generate A, B and initial point a=matrix::Zeros(n,n); b=vector::Zeros(n); x=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); bu=vector::Zeros(n); for(i=0; i::Zeros(ccnt,n+1); ct.Resize(ccnt); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { CAp::Assert(state.m_needfij); state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1821"); } if(waserrors) return; //--- 1. evaluate unconstrained gradient at solution //--- 2. calculate constrained gradient (NNLS solver is used //--- to evaluate gradient subject to active constraints). //--- In order to do this we form CE matrix, matrix of active //--- constraints (columns store constraint vectors). Then //--- we try to approximate gradient vector by columns of CE, //--- subject to non-negativity restriction placed on variables //--- corresponding to inequality constraints. //--- Residual from such regression is a constrained gradient vector. g=vector::Zeros(n); for(i=0; i::Zeros(n,n+ccnt); ArrayResize(nonnegative,n+ccnt); k=0; for(i=0; i(bu[i]+tolconstr),"testminnlcunit.ap:1851"); if(xs0[i]<=(bl[i]+tolconstr)) { for(j=0; j=(bu[i]-tolconstr)) { for(j=0; jtolconstr,"testminnlcunit.ap:1875"); CAp::SetErrorFlag(waserrors,ct[i]>0 && v<(-tolconstr),"testminnlcunit.ap:1876"); CAp::SetErrorFlag(waserrors,ct[i]<0 && v>tolconstr,"testminnlcunit.ap:1877"); if(ct[i]==0) { for(j=0; j0 && v<=tolconstr) || (ct[i]<0 && v>=(-tolconstr))) { for(j=0; j(1.0E-3),"testminnlcunit.ap:1906"); } } } } } //+------------------------------------------------------------------+ //| This function tests nonlinearly constrained quadratic programming| //| algorithm. | //| Sets error flag on failure. | //+------------------------------------------------------------------+ void CTestMinNLCUnit::TestNLC(bool &waserrors) { //--- create variables int n=0; int n2=0; double tolx=0; double tolf=0; double tolg=0; int aulits=0; double rho=0; CMinNLCState state; CMinNLCReport rep; COptGuardReport ogrep; int scaletype=0; CRowDouble x0; CRowDouble x1; CRowDouble b; CRowDouble bndl; CRowDouble bndu; CRowDouble s; CRowDouble g; CRowDouble rnlc; CRowDouble xref; CRowDouble lagmult; CRowInt ckind; CMatrixDouble fulla; CMatrixDouble c; CRowInt ct; int cntbc=0; int cntlc=0; int cntnlec=0; int cntnlic=0; int cntactive=0; int i=0; int j=0; int k=0; int pass=0; int klc=0; int knlec=0; int knlic=0; double v=0; double vv=0; double vx=0; double vy=0; double gnorm2=0; double rawgnorm2=0; double fref=0; double diffstep=0; CHighQualityRandState rs; int prectype=0; int solvertype=0; CHighQualityRand::HQRndRandomize(rs); for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++) { //--- Basic test: //--- * 2-dimensional problem //--- * target function F(x0,x1) = (x0-1)^2 + (x1-1)^2 //--- * one nonlinear constraint Z(x0,x1) = x0^2+x1^2-1, //--- which is tried as equality and inequality one rho=200.0; tolx=0.0005; aulits=50; n=2; x0=vector::Zeros(n); for(i=0; itolx,"testminnlcunit.ap:2000"); CAp::SetErrorFlag(waserrors,(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2001"); CMinNLC::MinNLCSetNLC(state,1,0); CMinNLC::MinNLCRestartFrom(state,x0); if(CHighQualityRand::HQRndNormal(rs)>0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1)); state.m_j.Set(0,0,2*(state.m_x[0]-1)); state.m_j.Set(0,1,2*(state.m_x[1]-1)); state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1); state.m_j.Set(1,0,2*state.m_x[0]); state.m_j.Set(1,1,2*state.m_x[1]); continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2021"); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2022"); if(CHighQualityRand::HQRndNormal(rs)>0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2026"); } if(waserrors) return; CAp::SetErrorFlag(waserrors,(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2030"); CAp::SetErrorFlag(waserrors,(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2031"); //--- This test checks correctness of scaling being applied to nonlinear //--- constraints. We solve bound constrained scaled problem and check //--- that solution is correct. aulits=50; rho=200.0; tolx=0.0005; tolg=0.01; for(n=1; n<=10; n++) { for(pass=1; pass<=10; pass++) { //--- Generate well-conditioned problem with unit scale CMatGen::SPDMatrixRndCond(n,1.0E2,fulla); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { for(i=0; i<=cntnlec+2*cntnlic; i++) { state.m_fi.Set(i,0); for(j=0; j0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2170"); } if(waserrors) return; //--- Check feasibility properties for(i=0; i=(double)(bndu[i]+tolx*s[i]),"testminnlcunit.ap:2181"); } //--- Test - calculate scaled constrained gradient at solution, //--- check its norm. g=vector::Zeros(n); gnorm2=0.0; for(i=0; i0.0) g.Set(i,0); if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<(double)(tolx*s[i])) && (double)(g[i])<0.0) g.Set(i,0); gnorm2+=CMath::Sqr(g[i]); } CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)),"testminnlcunit.ap:2203"); } } //--- Complex problem with mix of boundary, linear and nonlinear constraints: //--- * quadratic target function f(x) = 0.5*x'*A*x + b'*x //--- * unit scaling is used //--- * problem size N is even //--- * all variables are divided into pairs: x[0] and x[1], x[2] and x[3], ... //--- * constraints are set for pairs of variables, i.e. each constraint involves //--- only pair of adjacent variables (x0/x1, x2/x3, x4/x5 and so on), and each //--- pair of variables has at most one constraint which binds them //--- * for variables u and v following kinds of constraints can be randomly set: //--- * CKind=0 no constraint //--- * CKind=1 boundary equality constraint: u=a, v=b //--- * CKind=2 boundary inequality constraint: a0<=u<=b0, a1<=v<=b1 //--- * CKind=3 linear equality constraint: a*u+b*v = c //--- * CKind=4 linear inequality constraint: a*u+b*v <= c //--- * CKind=5 nonlinear equality constraint: u^2+v^2 = 1 //--- * CKind=6 nonlinear inequality constraint: u^2+v^2 <= 1 //--- * it is relatively easy to calculated projected gradient for such problem aulits=50; rho=200.0; tolx=0.0005; tolg=0.005; n=20; n2=n/2; for(pass=1; pass<=5; pass++) { for(prectype=1; prectype<=2; prectype++) { //--- Generate well-conditioned problem with unit scale CMatGen::SPDMatrixRndCond(n,1.0E2,fulla); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); c=matrix::Zeros(n,n+1); ct.Resize(n); x0=vector::Zeros(n); ckind.Resize(n2); rnlc=vector::Zeros(n2); cntbc=0; cntlc=0; cntnlec=0; cntnlic=0; for(i=0; i0.0) CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1)); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { //--- Evaluate target function state.m_fi.Set(0,0); for(i=0; i0.0) { CMinNLC::MinNLCOptGuardResults(state,ogrep); CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2439"); } if(waserrors) return; //--- Check feasibility properties klc=0; for(i=0; itolx,"testminnlcunit.ap:2462"); CAp::SetErrorFlag(waserrors,MathAbs(x1[2*i+1]-bndl[2*i+1])>tolx,"testminnlcunit.ap:2463"); continue; } if(ckind[i]==2) { //--- Bound inequality constrained CAp::SetErrorFlag(waserrors,x1[2*i ]<(bndl[2*i+0]-tolx),"testminnlcunit.ap:2471"); CAp::SetErrorFlag(waserrors,x1[2*i ]>(bndu[2*i+0]+tolx),"testminnlcunit.ap:2472"); CAp::SetErrorFlag(waserrors,x1[2*i+1]<(bndl[2*i+1]-tolx),"testminnlcunit.ap:2473"); CAp::SetErrorFlag(waserrors,x1[2*i+1]>(bndu[2*i+1]+tolx),"testminnlcunit.ap:2474"); continue; } if(ckind[i]==3) { //--- Linear equality constrained v=x1[2*i+0]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][n]; CAp::SetErrorFlag(waserrors,MathAbs(v)>tolx,"testminnlcunit.ap:2483"); klc++; continue; } if(ckind[i]==4) { //--- Linear inequality constrained v=x1[2*i+0]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][n]; CAp::SetErrorFlag(waserrors,v>tolx,"testminnlcunit.ap:2493"); klc++; continue; } if(ckind[i]==5) { //--- Nonlinear equality constrained v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i]; CAp::SetErrorFlag(waserrors,MathAbs(v)>tolx,"testminnlcunit.ap:2503"); continue; } if(ckind[i]==6) { //--- Nonlinear inequality constrained v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i]; CAp::SetErrorFlag(waserrors,v>tolx,"testminnlcunit.ap:2512"); continue; } CAp::Assert(false); waserrors=true; return; } //--- Test - calculate scaled constrained gradient at solution, //--- check its norm. gnorm2=0.0; rawgnorm2=0; g=vector::Zeros(n); for(i=0; i(double)(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2542"); continue; } if(ckind[i]==1) { //--- Bound equality constrained, unconditionally set gradient to zero g.Set(2*i+0,0.0); g.Set(2*i+1,0.0); gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]); CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2553"); continue; } if(ckind[i]==2) { //--- Bound inequality constrained, conditionally set gradient to zero //--- (when constraint is active) if(x1[2*i ]<(bndl[2*i]+tolx) || x1[2*i ]>(bndu[2*i]-tolx)) g.Set(2*i,0.0); if(x1[2*i+1]<(bndl[2*i+1]+tolx) || x1[2*i+1]>(bndu[2*i+1]-tolx)) g.Set(2*i+1,0.0); gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]); CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2567"); continue; } if(ckind[i]==3) { //--- Linear equality constrained, unconditionally project gradient into //--- equality constrained subspace v=g[2*i+0]*c[klc][ 2*i]+g[2*i+1]*c[klc][ 2*i+1]; vv=CMath::Sqr(c[klc][ 2*i])+CMath::Sqr(c[klc][ 2*i+1]); g.Add(2*i,c[klc][ 2*i]*(-v/vv)); g.Add(2*i+1,c[klc][ 2*i+1]*(-v/vv)); klc++; gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]); CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2582"); continue; } if(ckind[i]==4) { //--- Linear inequality constrained, conditionally project gradient //--- (when constraint is active) v=x1[2*i]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][ n]; if(v>(-tolx)) { v=g[2*i+0]*c[klc][ 2*i]+g[2*i+1]*c[klc][ 2*i+1]; vv=CMath::Sqr(c[klc][ 2*i])+CMath::Sqr(c[klc][ 2*i+1]); g.Add(2*i,c[klc][ 2*i]*(-v/vv)); g.Add(2*i+1,c[klc][ 2*i+1]*(-v/vv)); } klc++; gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]); CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2601"); continue; } if(ckind[i]==5) { //--- Nonlinear equality constrained, unconditionally project gradient //--- NOTE: here we rely on the fact that corresponding components of X //--- sum to one. v=CApServ::Coalesce(MathSqrt(CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])),1.0); vx=x1[2*i]/v; vy=x1[2*i+1]/v; v=g[2*i]*vx+g[2*i+1]*vy; g.Add(2*i,- vx*v); g.Add(2*i+1,- vy*v); gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]); CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2619"); continue; } if(ckind[i]==6) { //--- Nonlinear inequality constrained, conditionally project gradient //--- (when constraint is active) //--- NOTE: here we rely on the fact that corresponding components of X //--- sum to one. v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i]; if(v>(double)(-tolx)) { v=CApServ::Coalesce(MathSqrt(CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])),1.0); vx=x1[2*i ]/v; vy=x1[2*i+1]/v; v=g[2*i]*vx+g[2*i+1]*vy; g.Add(2*i,- vx*v); g.Add(2*i+1,- vy*v); } gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]); CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2642"); continue; } CAp::Assert(false); waserrors=true; return; } } } } //--- Test a sequence of inequality constrained problems with varying N/NLIC and //--- known answer. The problem formulation is prepared as follows: //--- * first, we generate a solution point and active constraints with Lagrange //--- multipliers of our choice; at least one constraint is active at the solution. //--- * then we select the target function to match our constraints //--- * finally, we add inactive constraints rho=1000.0; tolf=0.002; aulits=10; diffstep=0.0001; for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++) { for(n=1; n<=10; n++) { for(cntnlic=1; cntnlic<=10; cntnlic++) { //--- Create constraint matrix C that stores coefficients of quadratic //--- constraints: first N columns store linear terms, next N columns //--- store diagonal of the quadratic term, next column stores constant term. CHighQualityRand::HQRndNormalV(rs,n,xref); cntactive=1+CHighQualityRand::HQRndUniformI(rs,n); c=matrix::Zeros(cntnlic,2*n+1); lagmult=vector::Zeros(cntnlic); CAblasF::RSetAllocV(n,0.0,g); for(i=0; i0.0) CMinNLC::MinNLCCreate(n,x0,state); else CMinNLC::MinNLCCreateF(n,x0,diffstep,state); switch(solvertype) { case 0: CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); CMinNLC::MinNLCSetCond(state,1.0E-7,0); prectype=CHighQualityRand::HQRndUniformI(rs,3); if(prectype==0) CMinNLC::MinNLCSetPrecInexact(state); if(prectype==1) CMinNLC::MinNLCSetPrecExactLowRank(state,0); if(prectype==2) CMinNLC::MinNLCSetPrecExactRobust(state,0); break; case 1: CMinNLC::MinNLCSetAlgoSLP(state); CMinNLC::MinNLCSetCond(state,1.0E-6,0); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); CMinNLC::MinNLCSetCond(state,1.0E-6,0); break; default: CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCSetNLC(state,0,cntnlic); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfi) { //--- Evaluate target function state.m_fi.Set(0,0); for(i=0; i(tolf*(CAblasF::RMaxAbsV(n,b)+1)),"testminnlcunit.ap:2824"); } } } } //+------------------------------------------------------------------+ //| This function performs additional tests | //| On failure sets error flag. | //+------------------------------------------------------------------+ void CTestMinNLCUnit::TestOther(bool &waserrors) { //--- create variables CHighQualityRandState rs; double v=0; double h=0; double fl=0; double fr=0; double fl2=0; double fr2=0; double dfl=0; double dfr=0; double dfl2=0; double dfr2=0; double d2fl=0; double d2fr=0; double d2fl2=0; double d2fr2=0; double f0=0; double df=0; double d2f=0; double ndf=0; double nd2f=0; double dtol=0; double diffstep=0; CMinNLCState state; CMinNLCReport rep; COptGuardReport ogrep; double rho=0; int aulits=0; double tolx=0; int i=0; int j=0; int k=0; int n=0; CRowDouble s; CRowDouble b; CRowDouble x0; CRowDouble x1; CRowDouble x2; CRowDouble x3; CRowDouble xlast; CRowDouble bndl; CRowDouble bndu; CRowDouble xu; double condv=0; CMatrixDouble a; CMatrixDouble c; CMatrixDouble fulla; CRowInt ct; int nlbfgs=0; int nexactlowrank=0; int nexactrobust=0; int nnone=0; int ctype=0; int trialidx=0; int blocksize=0; int blockcnt=0; int maxits=0; int spoiliteration=0; int stopiteration=0; int spoilvar=0; double spoilval=0; int pass=0; int solvertype=0; int badidx0=0; int badidx1=0; int nlec=0; int nlic=0; double ss=0; int stopcallidx=0; int callidx=0; bool terminationrequested; bool firstrep; int i_=0; CHighQualityRand::HQRndRandomize(rs); //--- Test equality penalty function (correctly calculated and smooth) h=1.0E-4; v=-0.98; dtol=1.0E-3; while(v<=0.98) { //--- Test numerical derivative; this test also checks continuity of the //--- function CMinNLC::MinNLCEqualityPenaltyFunction(v-2*h,fl2,dfl2,d2fl2); CMinNLC::MinNLCEqualityPenaltyFunction(v-h,fl,dfl,d2fl); CMinNLC::MinNLCEqualityPenaltyFunction(v+h,fr,dfr,d2fr); CMinNLC::MinNLCEqualityPenaltyFunction(v+2*h,fr2,dfr2,d2fr2); CMinNLC::MinNLCEqualityPenaltyFunction(v,f0,df,d2f); ndf=(-fr2+8*fr-8*fl+fl2)/(12*h); CAp::SetErrorFlag(waserrors,MathAbs(ndf-df)>(dtol*MathMax(MathAbs(ndf),1)),"testminnlcunit.ap:2903"); nd2f=(-dfr2+8*dfr-8*dfl+dfl2)/(12*h); CAp::SetErrorFlag(waserrors,(MathAbs(nd2f-d2f))>(dtol*MathMax(MathAbs(nd2f),1)),"testminnlcunit.ap:2905"); //--- Next point v+=h; } CMinNLC::MinNLCEqualityPenaltyFunction(0.0,f0,df,d2f); CAp::SetErrorFlag(waserrors,f0!=0.0,"testminnlcunit.ap:2913"); CAp::SetErrorFlag(waserrors,(double)(df)!=0.0,"testminnlcunit.ap:2914"); //--- Test inequality penalty function (correctly calculated and smooth) h=1.0E-4; v=0.02; dtol=1.0E-3; while(v<=2.0) { //--- Test numerical derivative; this test also checks continuity of the //--- function CMinNLC::MinNLCInequalityShiftFunction(v-2*h,fl2,dfl2,d2fl2); CMinNLC::MinNLCInequalityShiftFunction(v-h,fl,dfl,d2fl); CMinNLC::MinNLCInequalityShiftFunction(v+h,fr,dfr,d2fr); CMinNLC::MinNLCInequalityShiftFunction(v+2*h,fr2,dfr2,d2fr2); CMinNLC::MinNLCInequalityShiftFunction(v,f0,df,d2f); ndf=(-fr2+8*fr-8*fl+fl2)/(12*h); CAp::SetErrorFlag(waserrors,MathAbs(ndf-df)>(dtol*MathMax(MathAbs(ndf),1)),"testminnlcunit.ap:2938"); nd2f=(-dfr2+8*dfr-8*dfl+dfl2)/(12*h); CAp::SetErrorFlag(waserrors,MathAbs(nd2f-d2f)>(dtol*MathMax(MathAbs(nd2f),1)),"testminnlcunit.ap:2940"); //--- Next point v+=h; } CMinNLC::MinNLCInequalityShiftFunction(1.0,f0,df,d2f); CAp::SetErrorFlag(waserrors,MathAbs(f0)>(1.0E-6),"testminnlcunit.ap:2948"); CAp::SetErrorFlag(waserrors,MathAbs(df+1)>1.0E-6,"testminnlcunit.ap:2949"); //--- Test different properties shared by all solvers for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++) { //--- Test location reports aulits=50; rho=200.0; n=2; x0=vector::Zeros(n); xlast=vector::Zeros(n); x0.Set(0,0.1+0.1*CHighQualityRand::HQRndUniformR(rs)); x0.Set(1,0.2+0.1*CHighQualityRand::HQRndUniformR(rs)); xlast.Set(0,0); xlast.Set(1,0); firstrep=true; CMinNLC::MinNLCCreate(n,x0,state); switch(solvertype) { case 0: CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); case 1: CMinNLC::MinNLCSetAlgoSLP(state); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); break; default: CAp::Assert(false); waserrors=true; break; } CMinNLC::MinNLCSetCond(state,1.0E-7,0); CMinNLC::MinNLCSetNLC(state,0,1); CMinNLC::MinNLCSetXRep(state,true); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1)); state.m_j.Set(0,0,2*(state.m_x[0]-1)); state.m_j.Set(0,1,2*(state.m_x[1]-1)); state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1); state.m_j.Set(1,0,2*state.m_x[0]); state.m_j.Set(1,1,2*state.m_x[1]); continue; } if(state.m_xupdated) { //--- If first point reported, compare with initial one if(firstrep) { CAp::SetErrorFlag(waserrors,MathAbs(state.m_x[0]-x0[0])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3002"); CAp::SetErrorFlag(waserrors,MathAbs(state.m_x[1]-x0[1])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3003"); } firstrep=false; //--- Save last point xlast.Set(0,state.m_x[0]); xlast.Set(1,state.m_x[1]); //--- Done continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3021"); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3022"); if(waserrors) return; CAp::SetErrorFlag(waserrors,MathAbs(x1[0]-xlast[0])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3025"); CAp::SetErrorFlag(waserrors,MathAbs(x1[1]-xlast[1])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3026"); //--- Test numerical differentiation aulits=50; rho=200.0; tolx=0.001; diffstep=0.0001; n=2; x0=vector::Zeros(n); x0.Set(0,0.1); x0.Set(1,0.2); CMinNLC::MinNLCCreateF(n,x0,diffstep,state); switch(solvertype) { case 0: CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); break; case 1: CMinNLC::MinNLCSetAlgoSLP(state); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); break; default: CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCSetCond(state,1.0E-7,0); CMinNLC::MinNLCSetNLC(state,0,1); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfi) { state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1)); state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1); continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3062"); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3063"); if(waserrors) return; CAp::SetErrorFlag(waserrors,(double)(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:3066"); CAp::SetErrorFlag(waserrors,(double)(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:3067"); //--- Test integrity checks for NAN/INF: //--- * algorithm solves optimization problem, which is normal for some time (quadratic) //--- * after 5-th step we choose random component of gradient and consistently spoil //--- it by NAN or INF. //--- * we check that correct termination code is returned (-8) n=100; for(pass=1; pass<=10; pass++) { spoiliteration=5; stopiteration=8; if(CHighQualityRand::HQRndNormal(rs)>0.0) { //--- Gradient can be spoiled by +INF, -INF, NAN spoilvar=CHighQualityRand::HQRndUniformI(rs,n); i=CHighQualityRand::HQRndUniformI(rs,3); spoilval=AL_NaN; if(i==0) spoilval=AL_NEGINF; if(i==1) spoilval=AL_POSINF; } else { //--- Function value can be spoiled only by NAN //--- (+INF can be recognized as legitimate value during optimization) spoilvar=-1; spoilval=AL_NaN; } CMatGen::SPDMatrixRndCond(n,1.0E5,fulla); b=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i=spoiliteration) { if(spoilvar<0) state.m_fi.Set(0,spoilval); else state.m_j.Set(0,spoilvar,spoilval); } continue; } if(state.m_xupdated) { k++; continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-8,"testminnlcunit.ap:3160"); } //--- Test that optimizer respects box constraints in all //--- intermediate points: //--- * test that analytic Jacobian respects them //--- * test that numerical Jacobian respects them //--- NOTE: we skip SolverType=0 (AUL) because AUL optimizer //--- does not provide such guarantee if(solvertype!=0) { n=10; CMatGen::SPDMatrixRndCond(n,1.0E3,fulla); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.0) { bndl.Set(i,0); bndu.Set(i,AL_POSINF); } else { bndl.Set(i,AL_NEGINF); bndu.Set(i,0); } } //--- Check analytic Jacobian CMinNLC::MinNLCCreate(n,x0,state); switch(solvertype) { case 1: CMinNLC::MinNLCSetAlgoSLP(state); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); break; default: CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetCond(state,1.0E-8,0); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; ibndu[i],"testminnlcunit.ap:3216"); state.m_fi.Add(0,b[i]*state.m_x[i]); state.m_j.Set(0,i,b[i]); for(j=0; jbndu[i],"testminnlcunit.ap:3252"); state.m_fi.Add(0,b[i]*state.m_x[i]); for(j=0; j::Zeros(n); for(i=0; itolx,"testminnlcunit.ap:3310"); //--- Check constraint violation reports CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3315"); CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3316"); CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3317"); CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3318"); CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3319"); CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3320"); } for(pass=1; pass<=10; pass++) { n=1+CHighQualityRand::HQRndUniformI(rs,5); k=1+CHighQualityRand::HQRndUniformI(rs,5); x0=vector::Zeros(n); xu=vector::Zeros(n); bndl=vector::Full(n,-1000.0); bndu=vector::Full(n,1000.0); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.0) { c.Set(i,n,1000); ct.Set(i,-1); } else { c.Set(i,n,-1000); ct.Set(i,1); } } CMinNLC::MinNLCCreate(n,x0,state); switch(solvertype) { case 0: CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20); break; case 1: CMinNLC::MinNLCSetAlgoSLP(state); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); break; default: CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetLC(state,c,ct,k); CMinNLC::MinNLCSetCond(state,1.0E-7,0); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i::Zeros(n); x0=vector::Zeros(n); xu=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); c=matrix::Zeros(k,n+1); ct.Resize(k); for(i=0; i0.0) { c.Set(i,n,1000); ct.Set(i,-1); } else { c.Set(i,n,-1000); ct.Set(i,1); } } CAp::Assert(n>=2,"NLCTest: integrity check failed"); badidx0=CHighQualityRand::HQRndUniformI(rs,k); badidx1=CHighQualityRand::HQRndUniformI(rs,n); for(j=0; j0.0) ct.Set(badidx0,0); else ct.Set(badidx0,(int)MathSign(c.Get(badidx0,n))); //--- Create and try to solve CMinNLC::MinNLCCreate(n,x0,state); switch(solvertype) { case 0: CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20); break; case 1: CMinNLC::MinNLCSetAlgoSLP(state); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); break; default: CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetLC(state,c,ct,k); CMinNLC::MinNLCSetScale(state,s); CMinNLC::MinNLCSetCond(state,1.0E-7,0); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { v=MathMax(bndl[badidx1]-x1[badidx1],x1[badidx1]-bndu[badidx1]); v=v/s[badidx1]; CAp::SetErrorFlag(waserrors,rep.m_bcidx!=badidx1,"testminnlcunit.ap:3505"); CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_bcerr-v))>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3507"); } else { CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3512"); CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3513"); CAp::SetErrorFlag(waserrors,x1[badidx1]bndu[badidx1],"testminnlcunit.ap:3514"); } CAp::SetErrorFlag(waserrors,rep.m_lcidx!=badidx0,"testminnlcunit.ap:3516"); CAp::SetErrorFlag(waserrors,MathAbs(rep.m_lcerr-MathAbs(x1[badidx1]-c.Get(badidx0,n))/s[badidx1])>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3517"); CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3518"); CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3519"); } //--- Test constraint violation reports for box/nonlinearly constrained //--- problems. We generate a problem which can not satisfy one (and //--- just one) general linear constraint. //--- NOTE: it is important to have N>=NLEC+NLIC tolx=0.001; for(pass=1; pass<=10; pass++) { //--- Formulate problem with inconsistent constraints nlec=1+CHighQualityRand::HQRndUniformI(rs,5); nlic=1+CHighQualityRand::HQRndUniformI(rs,5); k=nlec+nlic; n=k+CHighQualityRand::HQRndUniformI(rs,5); s=vector::Zeros(n); x0=vector::Zeros(n); xu=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); c=matrix::Zeros(k,n+1); for(i=0; i0.0) c.Set(badidx0,n,bndu[badidx0]+10); else c.Set(badidx0,n,bndl[badidx0]-10); } else c.Set(badidx0,n,-1000); //--- Create and try to solve CMinNLC::MinNLCCreate(n,x0,state); switch(solvertype) { case 0: CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20); break; case 1: CMinNLC::MinNLCSetAlgoSLP(state); break; case 2: CMinNLC::MinNLCSetAlgoSQP(state); break; default: CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCSetBC(state,bndl,bndu); CMinNLC::MinNLCSetNLC(state,nlec,nlic); CMinNLC::MinNLCSetScale(state,s); CMinNLC::MinNLCSetCond(state,1.0E-7,0); while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0); for(i=0; i0.0) { v=MathMax(bndl[badidx0]-x1[badidx0],x1[badidx0]-bndu[badidx0]); v=v/s[badidx0]; CAp::SetErrorFlag(waserrors,rep.m_bcidx!=badidx0,"testminnlcunit.ap:3636"); CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_bcerr-v))>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3638"); } else { CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3643"); CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3644"); CAp::SetErrorFlag(waserrors,(double)(x1[badidx0])<(double)(bndl[badidx0]) || (double)(x1[badidx0])>(double)(bndu[badidx0]),"testminnlcunit.ap:3645"); } } CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3648"); CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3649"); CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=badidx0,"testminnlcunit.ap:3650"); CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_nlcerr-MathAbs(x1[badidx0]-c.Get(badidx0,n))))>(double)(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3651"); } //--- Test support for termination requests: //--- * to terminate with correct return code = 8 //---*to return point which was "current" at the moment of termination for(pass=1; pass<=50; pass++) { n=3; ss=100; x0=vector::Zeros(n); xlast=vector::Zeros(n); for(i=0; i::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); c=matrix::Zeros(blocksize*blockcnt,n+1); ct.Resize(blocksize*blockcnt); for(ctype=0; ctype<=1; ctype++) { //--- First, initialize iteration counters nlbfgs=0; nexactlowrank=0; nexactrobust=0; nnone=0; //--- Perform trials for(trialidx=0; trialidx<=99; trialidx++) { //--- Generate: //--- * boundary constraints BndL/BndU and initial point X0 //--- * block-diagonal matrix of linear constraints C such //--- that X0 is feasible w.r.t. constraints given by C for(i=0; i0.0) { bndl.Set(i,0); bndu.Set(i,AL_POSINF); x0.Set(i,CHighQualityRand::HQRndUniformR(rs)); } else { bndl.Set(i,0); bndu.Set(i,0); x0.Set(i,0); } } for(i=0; i<=blocksize*blockcnt-1; i++) for(j=0; j::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminnlcunit.ap:4153"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>=0,"testminnlcunit.ap:4154"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test0positive,"testminnlcunit.ap:4155"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test1positive,"testminnlcunit.ap:4156"); //--- Test gradient checking functionality, try various //--- defect types: //--- * accidental zeroing of some gradient component //--- * accidental addition of 1.0 to some component //--- * accidental multiplication by 2.0 //--- Try distorting both target and constraints. diffstep=0.001; n=10; for(skind=0; skind<=1; skind++) { for(funcidx=0; funcidx<=1; funcidx++) { for(defecttype=-1; defecttype<=2; defecttype++) { varidx=CHighQualityRand::HQRndUniformI(rs,n); x0=vector::Zeros(n); s=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); for(i=0; ibndu[i],"testminnlcunit.ap:4217"); } state.m_fi.Set(0,0); for(i=0; i::Zeros(2,n); jacdefect=matrix::Zeros(2,n); for(i=0; i=0) { CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminnlcunit.ap:4319"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=funcidx,"testminnlcunit.ap:4320"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminnlcunit.ap:4321"); } else { CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminnlcunit.ap:4325"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminnlcunit.ap:4326"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminnlcunit.ap:4327"); } for(i=0; i<=1; i++) for(j=0; j(0.01/s[j]),"testminnlcunit.ap:4332"); CAp::SetErrorFlag(waserrors,(MathAbs(jacdefect.Get(i,j)-ogrep.m_badgraduser.Get(i,j)))>(0.01/s[j]),"testminnlcunit.ap:4333"); } } } } //--- Make sure than no false positives are reported for larger //--- problems where numerical noise can be an issue: //--- * N=100 dimensions //--- * nonnegativity constraints //--- * positive-definite quadratic programming problem //--- * upper limit on iterations count, MaxIts=25 //--- We simply test that OptGuard does not return error code. n=100; CMatGen::SPDMatrixRndCond(n,1.0E2,a); b=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(n); xlast=vector::Zeros(n); x0=vector::Zeros(n); xu=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); do { v=0; for(i=0; i=2 && cntabove>=2) && (double)(stplen)>=(double)(shortstplen)); wasgoodlinesearch1=wasgoodlinesearch1||((cntbelow>=2 && cntabove>=2) && (double)(stplen)>=(double)(shortstplen)); } //--- Start new line search linesearchstarted=true; cntbelow=0; cntabove=0; xlast=state.m_x; v=CAblasF::RDotV(n,state.m_x,xu); if(vmaxshortsessions,"testminnlcunit.ap:4610"); } //--- One more test for detection of C1 continuity violations in the target. //--- Target function is a sum of |(x,c_i)| for i=1..N. //--- No constraints is present. //--- Analytic gradient is provided. //--- OptGuard should be able to detect violations in more than //--- 99.9% of runs; it means that 100 runs should have no more than 4 //--- failures in all cases (even after multiple repeated tests; according //--- to the binomial distribution quantiles). //--- We select some N and perform exhaustive search for this N. //--- NOTE: we skip SQP for this test passcount=100; maxfails=4; maxc1test0fails=10; maxc1test1fails=10; n=1+CHighQualityRand::HQRndUniformI(rs,10); failurecounter=0; c1test0fails=0; c1test1fails=0; avgstr0len=0; avglng0len=0; avgstr1len=0; avglng1len=0; for(pass=1; pass<=passcount; pass++) { //--- Formulate problem x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminnlcunit.ap:4713"); failed=false; failed=failed||COptGuardApi::OptGuardAllClear(ogrep); failed=failed||!ogrep.m_nonc1suspected; failed=failed||ogrep.m_nonc1fidx!=0; if(failed) failurecounter++; //--- Check C1 continuity test #0 CMinNLC::MinNLCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep); CMinNLC::MinNLCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep); if(ogrep.m_nonc1test0positive) { CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4728"); CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminnlcunit.ap:4729"); CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4730"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4731"); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n); avgstr0len+=(double)ognonc1test0strrep.m_cnt/(double)passcount; avglng0len+=(double)ognonc1test0lngrep.m_cnt/(double)passcount; } else { CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminnlcunit.ap:4739"); CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4740"); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n); c1test0fails++; } if(ogrep.m_nonc1test1positive) { CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4747"); CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminnlcunit.ap:4748"); CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminnlcunit.ap:4749"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4750"); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n); avgstr1len+=(double)ognonc1test1strrep.m_cnt/(double)passcount; avglng1len+=(double)ognonc1test1lngrep.m_cnt/(double)passcount; } else { CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminnlcunit.ap:4758"); CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminnlcunit.ap:4759"); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n); c1test1fails++; } } CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:4766"); CAp::SetErrorFlag(waserrors,c1test0fails>maxc1test0fails,"testminnlcunit.ap:4767"); CAp::SetErrorFlag(waserrors,c1test1fails>maxc1test1fails,"testminnlcunit.ap:4768"); CAp::SetErrorFlag(waserrors,(double)(avglng0len)<=(double)(avgstr0len),"testminnlcunit.ap:4769"); CAp::SetErrorFlag(waserrors,(double)(avglng1len)<=(double)(avgstr1len),"testminnlcunit.ap:4770"); //--- Detection of C1 continuity violations in the target under numerical differentiation: //--- * target function is a sum of |(x,c_i)| for i=1..N. //--- * no constraints is present. //--- * analytic gradient is provided. //--- OptGuard should always be able to detect violations in more than //--- 99% of runs (note: reduced strength when compared with analytic gradient); //--- it means that 100 runs should have no more than 10 failures in all cases //--- (even after multiple repeated tests; according to the binomial distribution //--- quantiles). //--- We select some N and perform exhaustive search for this N. //--- NOTE: we skip SQP solver for this test diffstep=0.0001; passcount=100; maxfails=10; n=1+CHighQualityRand::HQRndUniformI(rs,10); failurecounter=0; for(pass=1; pass<=passcount; pass++) { //--- Formulate problem x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminnlcunit.ap:4858"); failed=false; failed=failed||COptGuardApi::OptGuardAllClear(ogrep); failed=failed||!ogrep.m_nonc1suspected; failed=failed||ogrep.m_nonc1fidx!=0; if(failed) failurecounter++; } CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:4866"); //--- Detection of C1 continuity violations in the nonlinear constraints. //--- This test is a bit tricky because optimizers are less sensitive to //--- continuity violations in constraints, so we may have hard time collecting //--- enough statistics. In order to do so we solve carefully designed hard //--- problem with MULTIPLE bad constraints and only one good constraint. //--- Optimizer may report any of bad constraints, but not good one. //--- We select some N and perform exhaustive search for this N. passcount=100; maxfails=20; n=5; failurecounter=0; c1test0fails=0; c1test1fails=0; for(pass=1; pass<=passcount; pass++) { //--- Formulate problem, select constraint index to perturb with //--- nonsmoothness; make sure that this constraint is active at //--- the solution x0=vector::Zeros(n); s=vector::Zeros(n); b=vector::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminnlcunit.ap:4975"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:4976"); failed=false; failed=failed||COptGuardApi::OptGuardAllClear(ogrep); failed=failed||!ogrep.m_nonc1suspected; if(failed) failurecounter++; //--- Check C1 continuity tests #0 and #1 CMinNLC::MinNLCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep); CMinNLC::MinNLCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep); if(ogrep.m_nonc1test0positive) { CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4990"); CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminnlcunit.ap:4991"); CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4992"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx<1,"testminnlcunit.ap:4993"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>n,"testminnlcunit.ap:4994"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:4995"); TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0strrep,a,n,goodidx); TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0lngrep,a,n,goodidx); } else { CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminnlcunit.ap:5001"); CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminnlcunit.ap:5002"); TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0strrep,a,n,goodidx); TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0lngrep,a,n,goodidx); c1test0fails++; } if(ogrep.m_nonc1test1positive) { CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:5009"); CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminnlcunit.ap:5010"); CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminnlcunit.ap:5011"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx<1,"testminnlcunit.ap:5012"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>n,"testminnlcunit.ap:5013"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:5014"); TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1strrep,a,n,goodidx); TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1lngrep,a,n,goodidx); } else { CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminnlcunit.ap:5020"); CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminnlcunit.ap:5021"); TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1strrep,a,n,goodidx); TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1lngrep,a,n,goodidx); c1test1fails++; } } CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:5027"); } } //+------------------------------------------------------------------+ //| This function performs tests for fixed bugs | //| On failure sets error flag. | //+------------------------------------------------------------------+ void CTestMinNLCUnit::TestBugs(bool &waserrors) { //--- create variables CHighQualityRandState rs; int n=0; int aulits=0; int maxits=0; double rho=0; int ckind=0; int i=0; int j=0; int k=0; int solvertype=0; CMinNLCState state; CMinNLCReport rep; CRowDouble x0; CRowDouble x1; CRowDouble x2; CRowDouble bndl; CRowDouble bndu; CRowDouble d; CRowDouble b; CRowInt ct; CMatrixDouble c; CHighQualityRand::HQRndRandomize(rs); //--- Bug description (fixed): sometimes on non-convex problems, when //--- Lagrange coefficient for inequality constraint becomes small, //--- algorithm performs VERY deep step into infeasible area (step is 1E50), //--- which de-stabilizes it and prevents from converging back to feasible area. //--- Very rare situation, but must be fixed with additional "convexifying" term. //--- This test reproduces situation with convexified term turned off, then //--- checks that introduction of term solves issue. //--- We perform three kinds of tests: //--- * with box inequality constraint //--- * with linear inequality constraint //--- * with nonlinear inequality constraint //--- In all three cases we: //--- * first time solve non-convex problem with artificially moved stabilizing //--- point and decreased initial value of Lagrange multiplier. //--- * second time we solve problem with good stabilizing point, but zero Lagrange multiplier //--- * last time solve same problem, but with default settings aulits=1; maxits=1; rho=100.0; n=1; x0=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Full(n,AL_POSINF); c=matrix::Zeros(1,2); ct.Resize(1); c.Set(0,0,1.0); ct.Set(0,1); for(ckind=0; ckind<=2; ckind++) { CMinNLC::MinNLCCreate(n,x0,state); state.m_stabilizingpoint=-1.0E300; state.m_initialinequalitymultiplier=1.0E-12; CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); CMinNLC::MinNLCSetCond(state,0.0,maxits); switch(ckind) { case 0: CMinNLC::MinNLCSetBC(state,bndl,bndu); break; case 1: CMinNLC::MinNLCSetLC(state,c,ct,1); break; case 2: CMinNLC::MinNLCSetNLC(state,0,1); break; } while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0])); state.m_j.Set(0,0,1-2*state.m_x[0]); if(ckind==2) { state.m_fi.Set(1,-state.m_x[0]); state.m_j.Set(1,0,-1); } continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5125"); if(waserrors) return; CAp::SetErrorFlag(waserrors,x1[0]>(-1.0E6),"testminnlcunit.ap:5128"); CMinNLC::MinNLCCreate(n,x0,state); state.m_stabilizingpoint=-1.0E2; state.m_initialinequalitymultiplier=1.0E-12; CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); CMinNLC::MinNLCSetCond(state,0.0,maxits); switch(ckind) { case 0: CMinNLC::MinNLCSetBC(state,bndl,bndu); break; case 1: CMinNLC::MinNLCSetLC(state,c,ct,1); break; case 2: CMinNLC::MinNLCSetNLC(state,0,1); break; } while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0])); state.m_j.Set(0,0,1-2*state.m_x[0]); if(ckind==2) { state.m_fi.Set(1,-state.m_x[0]); state.m_j.Set(1,0,-1); } continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5157"); if(waserrors) return; CAp::SetErrorFlag(waserrors,x1[0]<(double)(3*state.m_stabilizingpoint),"testminnlcunit.ap:5160"); CMinNLC::MinNLCCreate(n,x0,state); CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits); CMinNLC::MinNLCSetCond(state,0.0,maxits); switch(ckind) { case 0: CMinNLC::MinNLCSetBC(state,bndl,bndu); break; case 1: CMinNLC::MinNLCSetLC(state,c,ct,1); break; case 2: CMinNLC::MinNLCSetNLC(state,0,1); break; } while(CMinNLC::MinNLCIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0])); state.m_j.Set(0,0,1-2*state.m_x[0]); if(ckind==2) { state.m_fi.Set(1,-state.m_x[0]); state.m_j.Set(1,0,-1); } continue; } CAp::Assert(false); waserrors=true; return; } CMinNLC::MinNLCResults(state,x1,rep); CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5187"); if(waserrors) return; CAp::SetErrorFlag(waserrors,x1[0]<(double)(3*state.m_stabilizingpoint),"testminnlcunit.ap:5190"); } //--- This test checks report by E. Pozamantir. Relevant for SLP //--- and related methods, but we test it for all algorithms. //--- Description: //--- The sequential linear programming solver performs warm-start //--- at each iteration, i.e. it reuses previously found LP basis. //--- However, when some initially non-zero Jacobian entries become //--- exactly zero (possible with constraints which are nonsmooth //--- at some distance from the boundary), our warm-start strategy //--- may fail because warm-start basis becomes exactly degenerate. //--- In order to test that this bug was fixed we solve carefully //--- designed noisy test problem with Jacobian entries being randomly //--- turned on and off. Simply being able to return from the solver //--- without triggering critical exception is enough. for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++) { for(n=6; n<=15; n++) { //--- Setup problem x0=vector::Zeros(n); d=vector::Zeros(n); for(i=0; i::Zeros(2*k,n+1); for(i=0; i<2*k; i++) for(j=0; j=rep.m_stp[k+1],"testminnlcunit.ap:5327"); //--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity hasc1discontinuities=false; for(int i=0; in,"testminnlcunit.ap:5389"); CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5390"); CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5391"); CAp::SetErrorFlag(err,!(rep.m_stpidxa=rep.m_stp[k+1],"testminnlcunit.ap:5409"); //--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity tooclose=false; hasc1discontinuities=false; for(int i=0; in,"testminnlcunit.ap:5523"); CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5524"); CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5525"); CAp::SetErrorFlag(err,!(rep.m_stpidxa::Zeros(n); d=vector::Zeros(n); for(pass=1; pass<=10; pass++) { for(i=0; i0.001,"testminnsunit.ap:147"); sumits+=(double)rep.m_iterationscount/(double)passcount; sumnfev+=(double)rep.m_nfev/(double)passcount; } } //+--------------------------------------------------------------------+ //| Basic unconstrained test: nonsmooth Rosenbrock posed as | //| unconstrained problem. | //| [ ]| //|min[10*|x0^2-x1|+(1-x0)^2+100*max(sqrt(2)*x0-1,0)+100*max(2*x1-1,0)]| //| [ ]| //| It's exact solution is x0=1/sqrt(2), x1=1/2 | //+--------------------------------------------------------------------+ void CTestMinNSUnit::BasicTest1UC(bool &errors) { //--- create variables int n=2; double v0=0; double v1=0; CRowDouble x0; CRowDouble x1; CMinNSState s; CMinNSReport rep; x0=vector::Zeros(n); CMinNS::MinNSCreate(n,x0,s); CMinNS::MinNSSetAlgoAGS(s,0.1,0.0); while(CMinNS::MinNSIteration(s)) { if(s.m_needfij) { v0=s.m_x[0]; v1=s.m_x[1]; s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1)); s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1)); s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1); if((MathSqrt(2.0)*v0-1.0)>0.0) { s.m_fi.Add(0,100*(MathSqrt(2)*v0-1)); s.m_j.Add(0,0,100*MathSqrt(2)); } if((2.0*v1-1.0)>0.0) { s.m_fi.Add(0,100.0*(2.0*v1-1.0)); s.m_j.Add(0,1,100.0*2.0); } continue; } CAp::Assert(false); errors=true; return; } CMinNS::MinNSResults(s,x1,rep); CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:204"); if(errors) return; CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (MathAbs(x1[0]-1.0/MathSqrt(2.0)))>0.001,"testminnsunit.ap:207"); CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:208"); } //+------------------------------------------------------------------+ //| Basic box constrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::BasicTest0BC(bool &errors) { //--- create variables int n=5; int passcount=10; int pass=0; int i=0; CRowDouble x0; CRowDouble x1; CRowDouble d; CRowDouble bl; CRowDouble bu; CMinNSState s; CMinNSReport rep; double sumits=0; double sumnfev=0; double v0=0; double v1=0; x0=vector::Zeros(n); bl=vector::Zeros(n); bu=vector::Zeros(n); d=vector::Zeros(n); for(pass=1; pass<=10; pass++) { for(i=0; i0.001,"testminnsunit.ap:267"); sumits+=(double)rep.m_iterationscount/(double)passcount; sumnfev+=(double)rep.m_nfev/(double)passcount; } } //+------------------------------------------------------------------+ //| Basic constrained test: nonsmooth Rosenbrock posed as box | //| constrained problem. | //| [ ] | //| minimize [ 10*|x0^2-x1| + (1-x0)^2 ] | //| [ ] | //| s.t. x0<=1/sqrt(2), x1<=0.5 | //| It's exact solution is x0=1/sqrt(2), x1=1/2 | //+------------------------------------------------------------------+ void CTestMinNSUnit::BasicTest1BC(bool &errors) { //--- create variables int n=2; double v0=0; double v1=0; CRowDouble x0; CRowDouble x1; CRowDouble bndl; CRowDouble bndu; CMinNSState s; CMinNSReport rep; x0=vector::Zeros(n); bndl=vector::Full(n,AL_NEGINF); bndu=vector::Zeros(n); bndu.Set(0,1.0/MathSqrt(2)); bndu.Set(1,0.5); CMinNS::MinNSCreate(n,x0,s); CMinNS::MinNSSetBC(s,bndl,bndu); CMinNS::MinNSSetAlgoAGS(s,0.1,0.0); while(CMinNS::MinNSIteration(s)) { if(s.m_needfij) { v0=s.m_x[0]; v1=s.m_x[1]; s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1)); s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1)); s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1); continue; } CAp::Assert(false); errors=true; return; } CMinNS::MinNSResults(s,x1,rep); CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:323"); if(errors) return; CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (double)(MathAbs(x1[0]-1/MathSqrt(2)))>0.001,"testminnsunit.ap:326"); CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (double)(MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:327"); } //+------------------------------------------------------------------+ //| Basic linearly constrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::BasicTest0LC(bool &errors) { //--- create variables double d=-10.0; int n=5; int passcount=10; int pass=0; int i=0; int j=0; CRowDouble x0; CRowDouble x1; CMatrixDouble c; CRowInt ct; CMinNSState s; CMinNSReport rep; double sumits=0; double sumnfev=0; int nc=0; x0=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); ct.Resize(2*n); for(pass=1; pass<=10; pass++) { nc=0; for(i=0; i0.001,"testminnsunit.ap:408"); sumits+=(double)rep.m_iterationscount/(double)passcount; sumnfev+=(double)rep.m_nfev/(double)passcount; } } //+------------------------------------------------------------------+ //| Basic constrained test: nonsmooth Rosenbrock posed as linearly | //| constrained problem. | //| [ ] | //| minimize [ 10*|x0^2-x1| + (1-x0)^2 ] | //| [ ] | //| s.t. x0<=1/sqrt(2), x1<=0.5 | //| It's exact solution is x0=1/sqrt(2), x1=1/2 | //+------------------------------------------------------------------+ void CTestMinNSUnit::BasicTest1LC(bool &errors) { //--- create variables int n=2; double v0=0; double v1=0; CRowDouble x0; CRowDouble x1; CMatrixDouble c; CRowInt ct; CMinNSState s; CMinNSReport rep; x0=vector::Zeros(n); c=matrix::Zeros(2,n+1); ct.Resize(2); c.Set(0,0,1.0); c.Set(0,2,1/MathSqrt(2)); c.Set(1,1,1.0); c.Set(1,2,0.5); ct.Set(0,-1); ct.Set(1,-1); CMinNS::MinNSCreate(n,x0,s); CMinNS::MinNSSetLC(s,c,ct,2); CMinNS::MinNSSetAlgoAGS(s,0.1,0.0); while(CMinNS::MinNSIteration(s)) { if(s.m_needfij) { v0=s.m_x[0]; v1=s.m_x[1]; s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1)); s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1)); s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1); continue; } CAp::Assert(false); errors=true; return; } CMinNS::MinNSResults(s,x1,rep); CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:470"); if(errors) return; CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (double)(MathAbs(x1[0]-1/MathSqrt(2)))>0.001,"testminnsunit.ap:473"); CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (double)(MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:474"); } //+------------------------------------------------------------------+ //| Basic nonlinearly constrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::BasicTest0NLC(bool &errors) { //--- create variables double d=-10.0; int n=5; int passcount=10; int pass=0; int i=0; int j=0; CRowDouble x0; CRowDouble x1; CMatrixDouble ec; CMatrixDouble ic; int nec=0; int nic=0; CMinNSState s; CMinNSReport rep; double sumits=0; double sumnfev=0; x0=vector::Zeros(n); ec=matrix::Zeros(2*n,n+1); ic=matrix::Zeros(2*n,n+1); for(pass=1; pass<=10; pass++) { nec=0; nic=0; for(i=0; i0.001,"testminnsunit.ap:571"); sumits+=(double)rep.m_iterationscount/(double)passcount; sumnfev+=(double)rep.m_nfev/(double)passcount; } } //+------------------------------------------------------------------+ //| Unconstrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::TestUC(bool &primaryerrors,bool &othererrors) { //--- create variables int n=0; int i=0; CRowDouble x0; CRowDouble x0s; CRowDouble x1; CRowDouble x1s; CRowDouble d; CRowDouble xc; CRowDouble s; CRowDouble xrfirst; CRowDouble xrlast; CMinNSState state; CMinNSReport rep; double v=0; int pass=0; bool requirexrep; double epsrad=0; bool werexreports; double repferr=0; double xtol=0; int i_=0; for(pass=1; pass<=10; pass++) { for(n=1; n<=5; n++) { //--- First test: //--- * test that problem is successfully solved //--- * test that X-reports are performed correctly - present //--- when requested, return first and last points correctly, //--- not present by default, function value is reported //--- correctly. //--- * we use non-unit scale, randomly chosen one, which results //--- in badly conditioned problems (to check robustness) x0=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrfirst=vector::Zeros(n); xrlast=vector::Zeros(n); for(i=0; i0.5; xtol=0.01*MathPow(10,-(1*CMath::RandomReal())); epsrad=xtol/100; CMinNS::MinNSCreate(n,x0,state); CMinNS::MinNSSetAlgoAGS(state,0.1,0.0); CMinNS::MinNSSetCond(state,epsrad,0); CMinNS::MinNSSetScale(state,s); if(requirexrep) CMinNS::MinNSSetXRep(state,true); werexreports=false; repferr=0.0; while(CMinNS::MinNSIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0.0); for(i=0; i(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:671"); if(primaryerrors||othererrors) return; //--- for(i=0; ixtol,"testminnsunit.ap:676"); if(requirexrep) { CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrfirst[i]) || (double)(MathAbs(x0[i]-xrfirst[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:679"); CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1[i]-xrlast[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:680"); } } //--- Test numerical differentiation: //--- * test that problem is successfully solved //--- * test that correct function value is reported x0=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrlast=vector::Zeros(n); for(i=0; i(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:732"); if(primaryerrors || othererrors) return; for(i=0; ixtol,"testminnsunit.ap:736"); //--- Test scaling: we perform several steps on unit-scale problem, //--- then we perform same amount of steps on re-scaled problem, //--- starting from same point (but scaled according to chosen scale). //--- Correctly written optimizer should perform essentially same steps //--- (up to scale) on both problems. At least, it holds within first //--- several steps, before rounding errors start to accumulate. //--- NOTE: we also check that correctly scaled points are reported. //--- And, as side effect, we check MinNSRestartFrom(). //--- NOTE: we use moderate scale and diagonal coefficients in order //--- to have well-conditioned system. We test correctness of //--- formulae here, not robustness of algorithm. x0=vector::Zeros(n); xc=vector::Zeros(n); x0s=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrlast=vector::Zeros(n); for(i=0; i(double)(1.0E-4)),"testminnsunit.ap:823"); CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1s[i]-xrlast[i]))>m_scalingtesttol,"testminnsunit.ap:827"); } } } } //+------------------------------------------------------------------+ //| Box constrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::TestBC(bool &primaryerrors,bool &othererrors) { //--- create variables int passcount=10; int maxn=5; int n=0; int i=0; int j=0; int k=0; CRowDouble x0; CRowDouble x0s; CRowDouble x1; CRowDouble x1s; CRowDouble b; CRowDouble d; CRowDouble xc; CRowDouble s; CRowDouble bndl; CRowDouble bndu; CRowDouble scaledbndl; CRowDouble scaledbndu; CRowDouble xrfirst; CRowDouble xrlast; CMatrixDouble a; CMinNSState state; CMinNSReport rep; double v=0; double v0=0; double v1=0; int pass=0; bool requirexrep; double epsrad=0; bool werexreports; double repferr=0; double xtol=0; double conda=0; double gnorm=0; int i_=0; //--- First test: //--- * sparse function //--- * test that problem is successfully solved //--- * non-unit scale is used, which results in badly conditioned problem //--- * check that all iterates are feasible (box-constrained) for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { x0=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrfirst=vector::Zeros(n); xrlast=vector::Zeros(n); for(i=0; i0.5; xtol=0.01*MathPow(10,-(1*CMath::RandomReal())); epsrad=xtol/100; CMinNS::MinNSCreate(n,x0,state); CMinNS::MinNSSetAlgoAGS(state,0.1,0.0); CMinNS::MinNSSetCond(state,epsrad,0); CMinNS::MinNSSetBC(state,bndl,bndu); CMinNS::MinNSSetScale(state,s); if(requirexrep) CMinNS::MinNSSetXRep(state,true); werexreports=false; repferr=0.0; while(CMinNS::MinNSIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0.0); for(i=0; ibndu[i],"testminnsunit.ap:945"); } continue; } CAp::Assert(false); othererrors=true; return; } CMinNS::MinNSResults(state,x1,rep); CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:953"); CAp::SetErrorFlag(othererrors,werexreports && !requirexrep,"testminnsunit.ap:954"); CAp::SetErrorFlag(othererrors,requirexrep && !werexreports,"testminnsunit.ap:955"); CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:956"); if(primaryerrors || othererrors) return; for(i=0; ixtol,"testminnsunit.ap:961"); CAp::SetErrorFlag(primaryerrors,x1[i]bndu[i],"testminnsunit.ap:963"); if(requirexrep) { CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrfirst[i]) || (double)(MathAbs(CApServ::BoundVal(x0[i],bndl[i],bndu[i])-xrfirst[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:966"); CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1[i]-xrlast[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:967"); } } } } //--- A bit harder test: //--- * dense quadratic function (smooth), may be prone to different //--- rounding-related issues //--- * non-negativity box constraints //--- * unit scale is used //--- * extreme stopping criteria (EpsX=1.0E-12) //--- * single pass for each problem size //--- * check that constrained gradient at solution is small conda=1.0E3; epsrad=1.0E-12; for(n=1; n<=10; n++) { x0=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); b=vector::Zeros(n); for(i=0; i0.0) v=0; if(x1[i]==bndu[i] && v<0.0) v=0; gnorm+=CMath::Sqr(v); CAp::SetErrorFlag(primaryerrors,x1[i]bndu[i],"testminnsunit.ap:1046"); } gnorm=MathSqrt(gnorm); CAp::SetErrorFlag(primaryerrors,gnorm>(1.0E-4),"testminnsunit.ap:1049"); } //--- Test on HIGHLY nonconvex bound constrained problem. //--- Algorithm should be able to Stop. //--- NOTE: because algorithm can be attracted to saddle points, //--- x[i] may be -1, +1 or approximately zero. for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { x0=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); for(i=0; ixtol,"testminnsunit.ap:1100"); } } } //--- Test numerical differentiation: //--- * test that problem is successfully solved //--- * test that correct function value is reported //--- * test that all iterates are within bound-constrained area for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { x0=vector::Zeros(n); xc=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrlast=vector::Zeros(n); for(i=0; ibndu[i],"testminnsunit.ap:1165"); } continue; } if(state.m_xupdated) { v=0.0; for(i=0; ibndu[i],"testminnsunit.ap:1177"); } repferr=MathMax(repferr,MathAbs(v-state.m_f)); continue; } CAp::Assert(false); othererrors=true; return; } CMinNS::MinNSResults(state,x1,rep); CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1186"); CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:1187"); if(primaryerrors || othererrors) return; for(i=0; ixtol,"testminnsunit.ap:1191"); } } //--- Test scaling: we perform several steps on unit-scale problem, //--- then we perform same amount of steps on re-scaled problem, //--- starting from same point (but scaled according to chosen scale). //--- Correctly written optimizer should perform essentially same steps //--- (up to scale) on both problems. At least, it holds within first //--- several steps, before rounding errors start to accumulate. //--- NOTE: we also check that correctly scaled points are reported. //--- And, as side effect, we check MinNSRestartFrom(). //--- NOTE: we use very low scale and diagonal coefficients in order //--- to have well-conditioned system. We test correctness of //--- formulae here, not robustness of algorithm. for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { x0=vector::Zeros(n); xc=vector::Zeros(n); x0s=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrlast=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); scaledbndl=vector::Zeros(n); scaledbndu=vector::Zeros(n); for(i=0; i(double)(m_scalingtesttol),"testminnsunit.ap:1297"); } } } //+------------------------------------------------------------------+ //| Linearly constrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::TestLC(bool &primaryerrors,bool &othererrors) { //--- create variables int n=0; int i=0; int j=0; int k=0; int nc=0; CRowDouble x0; CRowDouble x0s; CRowDouble x1; CRowDouble x2; CRowDouble x1s; CRowDouble d; CRowDouble xc; CRowDouble s; CRowDouble bndl; CRowDouble bndu; CMatrixDouble c; CMatrixDouble scaledc; CRowInt ct; CRowDouble scaledbndl; CRowDouble scaledbndu; CRowDouble xrfirst; CRowDouble xrlast; CMinNSState state; CMinNSReport rep; double v=0; double v0=0; double v1=0; double vv=0; double flast0=0; double flast1=0; int pass=0; double epsrad=0; double repferr=0; double xtol=0; double ftol=0; double rho=0; int i_=0; for(pass=1; pass<=10; pass++) { for(n=1; n<=5; n++) { //--- First test: //--- * smooth problem //--- * subject to random linear constraints //--- * with non-unit scale //--- We: //--- * compare function value at constrained solution with function //--- value for penalized unconstrained problem. We do not compare //--- actual X-values returned, because they are highly unstable - //--- function values at minimum show better stability. //--- * check that correct function values are reported x0=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i0) { c=matrix::Zeros(nc,n+1); ct.Resize(nc); for(i=0; i(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:1404"); if(primaryerrors || othererrors) return; CMinNS::MinNSSetLC(state,c,ct,0); CMinNS::MinNSRestartFrom(state,x0); rho=10000.0; repferr=0.0; flast1=AL_NaN; while(CMinNS::MinNSIteration(state)) { if(state.m_needfij) { state.m_fi.Set(0,0.0); for(i=0; i0) { vv=-MathSign(MathMax(-v,0.0)); v=MathMax(-v,0.0); } state.m_fi.Add(0,rho*v); for(j=0; jftol,"testminnsunit.ap:1466"); //--- Test on HIGHLY nonconvex linearly constrained problem. //--- Algorithm should be able to Stop at the bounds. x0=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); ct.Resize(2*n); for(i=0; ixtol && MathAbs(x1[i])>xtol) && (double)(MathAbs(x1[i]+1))>xtol,"testminnsunit.ap:1520"); } //--- Test scaling: we perform several steps on unit-scale problem, //--- then we perform same amount of steps on re-scaled problem, //--- starting from same point (but scaled according to chosen scale). //--- Correctly written optimizer should perform essentially same steps //--- (up to scale) on both problems. At least, it holds within first //--- several steps, before rounding errors start to accumulate. //--- NOTE: we also check that correctly scaled points are reported. //--- And, as side effect, we check MinNSRestartFrom(). //--- NOTE: we use moderate scale and diagonal coefficients in order //--- to have well-conditioned system. We test correctness of //--- formulae here, not robustness of algorithm. x0=vector::Zeros(n); xc=vector::Zeros(n); x0s=vector::Zeros(n); d=vector::Zeros(n); s=vector::Zeros(n); xrlast=vector::Zeros(n); c=matrix::Zeros(2*n,n+1); scaledc=matrix::Zeros(2*n,n+1); ct.Resize(2*n); ct.Fill(0); for(i=0; i(double)(m_scalingtesttol),"testminnsunit.ap:1647"); } } } //+------------------------------------------------------------------+ //| Nonlinearly constrained test | //+------------------------------------------------------------------+ void CTestMinNSUnit::TestNLC(bool &primaryerrors,bool &othererrors) { //--- create variables int passcount=10; int maxn=5; double rho=100.0; int n=0; int i=0; int j=0; int nc=0; int nec=0; CRowDouble x0; CRowDouble x0s; CRowDouble x1; CRowDouble x2; CRowDouble x1s; CRowDouble d; CRowDouble xc; CRowDouble s; CRowDouble bndl; CRowDouble bndu; CRowDouble b; CRowDouble r; CMatrixDouble c; CMatrixDouble scaledc; CRowInt ct; CRowDouble scaledbndl; CRowDouble scaledbndu; CRowDouble xrfirst; CRowDouble xrlast; CMinNSState state; CMinNSReport rep; double v=0; int pass=0; double epsrad=0; double xtol=0; double diffstep=0; //--- First test: //--- * simple problem //--- * subject to random nonlinear constraints of form r[i]*x[i] OPERATION 0.0, //--- where OPERATION is <= or = //--- * with non-unit scale //--- We: //--- * compare numerical solution with analytic one, which can be //--- easily calculated for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { for(nc=1; nc<=n; nc++) { for(nec=0; nec<=nc; nec++) { x0=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); r=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i=nec && i0.0) v=MathMin(v,0.0); if(r[i]<0.0) v=MathMax(v,0.0); } CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1762"); CAp::SetErrorFlag(primaryerrors,MathAbs(x1[i]-v)>(double)(xtol*s[i]),"testminnsunit.ap:1763"); } } } } } //--- Numerical differentiation test. for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { for(nc=1; nc<=n; nc++) { for(nec=0; nec<=nc; nec++) { x0=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); r=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i=nec && i0.0) v=MathMin(v,0.0); if(r[i]<0.0) v=MathMax(v,0.0); } CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1832"); CAp::SetErrorFlag(primaryerrors,MathAbs(x1[i]-v)>(xtol*s[i]),"testminnsunit.ap:1833"); } } } } } //--- Test scaling: we perform several steps on unit-scale problem, //--- then we perform same amount of steps on re-scaled problem, //--- starting from same point (but scaled according to chosen scale). //--- Correctly written optimizer should perform essentially same steps //--- (up to scale) on both problems. At least, it holds within first //--- several steps, before rounding errors start to accumulate. //--- NOTE: we use moderate scale and diagonal coefficients in order //--- to have well-conditioned system. We test correctness of //--- formulae here, not robustness of algorithm. //--- NOTE: we have to use very relaxed thresholds for this test because //--- each AGS iteration involves solution of several nested QP //--- subproblems, so rounding errors accumulate too quickly. //--- It does not mean that algorithm is inexact, just that two //--- almost identical optimization sessions diverge too fast to //--- compare them. for(pass=1; pass<=passcount; pass++) { for(n=1; n<=maxn; n++) { for(nc=1; nc<=n; nc++) { for(nec=0; nec<=nc; nec++) { x0=vector::Zeros(n); x0s=vector::Zeros(n); xc=vector::Zeros(n); d=vector::Zeros(n); r=vector::Zeros(n); s=vector::Zeros(n); for(i=0; i(double)(m_scalingtesttol),"testminnsunit.ap:1937"); } } } } } //+------------------------------------------------------------------+ //| Special tests | //+------------------------------------------------------------------+ void CTestMinNSUnit::TestOther(bool &othererrors) { //--- create variables int n=0; int k=0; CRowDouble x0; CRowDouble x1; CMinNSState state; CMinNSReport rep; double v0=0; double v1=0; double v=0; double xtol=0; double epsrad=0; double rho=0; //--- First test: //--- * 2D problem, minimization of F(x0,x1)=x1 //--- * two constraints, with wildly different magnitudes //--- * G0(x0,x1)=Rho*Abs(x0+x1)=0 //--- * H0(x0,x1)=Rho*(x0-1000)<=0 //--- where Rho is some large value //--- Optimizer should be able to deal with situation when //--- magnitude of Jacobian components is so wildly different. n=2; x0=vector::Zeros(n); x0.Set(0,0.1); x0.Set(1,1.0); epsrad=0.00001; xtol=0.01; for(k=0; k<=6; k++) { rho=MathPow(10,k); CMinNS::MinNSCreate(n,x0,state); CMinNS::MinNSSetAlgoAGS(state,0.1,10.0); CMinNS::MinNSSetCond(state,epsrad,0); CMinNS::MinNSSetNLC(state,1,1); v=1000.0; while(CMinNS::MinNSIteration(state)) { if(state.m_needfij) { v0=state.m_x[0]; v1=state.m_x[1]; state.m_fi.Set(0,v1); state.m_j.Set(0,0,0.0); state.m_j.Set(0,1,1.0); state.m_fi.Set(1,rho*MathAbs(v0+v1)); state.m_j.Set(1,0,rho*MathSign(v0+v1)); state.m_j.Set(1,1,rho*MathSign(v0+v1)); state.m_fi.Set(2,rho*(v0-v)); state.m_j.Set(2,0,rho); state.m_j.Set(2,1,0.0); continue; } CAp::Assert(false); othererrors=true; return; } CMinNS::MinNSResults(state,x1,rep); CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testminnsunit.ap:2003"); CAp::SetErrorFlag(othererrors,!MathIsValidNumber(x1[0]),"testminnsunit.ap:2004"); CAp::SetErrorFlag(othererrors,!MathIsValidNumber(x1[1]),"testminnsunit.ap:2005"); CAp::SetErrorFlag(othererrors,MathAbs(x1[0]-v)>xtol,"testminnsunit.ap:2006"); CAp::SetErrorFlag(othererrors,MathAbs(x1[1]+v)>xtol,"testminnsunit.ap:2007"); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestMinBCUnit { public: static const int m_maxoptguardlevel; static bool TestMinBC(bool silent); private: static void CalcIIP2(CMinBCState &state,int n,int fk); static void TestFeasibility(bool &feaserr,bool &converr,bool &interr); static void TestOther(bool &err); static void TestPreconditioning(bool &err); static void TestOptGuard(bool &waserrors); static void SetRandomPreconditioner(CMinBCState &state,int n,int preckind); static void TestOptGuardC1Test0ReportForTask0(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n); static void TestOptGuardC1Test1ReportForTask0(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ const int CTestMinBCUnit::m_maxoptguardlevel=1; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestMinBCUnit::TestMinBC(bool silent) { //--- create variables bool result; bool waserrors; bool feasibilityerrors; bool othererrors; bool precerrors; bool interrors; bool converrors; bool optguarderrors; waserrors=false; feasibilityerrors=false; othererrors=false; precerrors=false; interrors=false; converrors=false; optguarderrors=false; //--- tests TestFeasibility(feasibilityerrors,converrors,interrors); TestOther(othererrors); TestPreconditioning(precerrors); TestOptGuard(optguarderrors); //--- end waserrors=((((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors)||optguarderrors; if(!silent) { Print("TESTING BC OPTIMIZATION"); PrintResult("FEASIBILITY PROPERTIES",!feasibilityerrors); PrintResult("PRECONDITIONING",!precerrors); PrintResult("OTHER PROPERTIES",!othererrors); PrintResult("CONVERGENCE PROPERTIES",!converrors); PrintResult("INTERNAL ERRORS",!interrors); PrintResult("OPTGUARD",!optguarderrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| Calculate test function IIP2 | //| f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1) | //| It has high condition number which makes fast convergence | //| unlikely without good preconditioner. | //+------------------------------------------------------------------+ void CTestMinBCUnit::CalcIIP2(CMinBCState &state,int n,int fk) { if(!state.m_needfg) return; for(int i=0; i::Zeros(n); bu=vector::Zeros(n); x=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i1.0; } } CMinBC::MinBCResults(state,x,rep); if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility v=0.0; for(i=0; i0.0 && x[i]<1.0) v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1)); feaserr=feaserr||x[i]<0.0; feaserr=feaserr||x[i]>1.0; } converr=converr||MathSqrt(v)>(double)(weakepsg); } } } } //--- Same as previous problem, but with minor modifications: //--- * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from converging //--- to the feasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * with such simple boundaries and function it is easy to find //--- analytic form of solution: S[i] = bound(x0[i], 0, 1) //--- * we also check that both final solution and subsequent iterates //--- are strictly feasible diffstep=1.0E-6; for(dkind=0; dkind<=1; dkind++) { for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { //--- Generate X, BL, BU. p=2*pkind; bl=vector::Zeros(n); bu=vector::Zeros(n); x=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i0.5) { bl.Set(i,0); bu.Set(i,1); } else { bl.Set(i,CMath::RandomReal()); bu.Set(i,bl[i]); } x.Set(i,CMath::RandomReal()); x0.Set(i,3*CMath::RandomReal()-1); } //--- Create and optimize if(dkind==0) CMinBC::MinBCCreate(n,x,state); if(dkind==1) CMinBC::MinBCCreateF(n,x,diffstep,state); CMinBC::MinBCSetBC(state,bl,bu); CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0); SetRandomPreconditioner(state,n,preckind); while(CMinBC::MinBCIteration(state)) { if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0; i(double)(bu[i]); } } CMinBC::MinBCResults(state,x,rep); if(rep.m_terminationtype<=0) { converr=true; return; } //--- * compare solution with analytic one //--- * check feasibility v=0.0; for(i=0; i(double)(bl[i]) && x[i]<(double)(bu[i])) v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1)); feaserr=feaserr||x[i]<(double)(bl[i]); feaserr=feaserr||x[i]>(double)(bu[i]); } converr=converr||MathSqrt(v)>(double)(weakepsg); } } } } //--- Infeasible problem: //--- * all bound constraints are 0 <= x[i] <= 1 except for one //--- * that one is 0 >= x[i] >= 1 //--- * no linear constraints //--- * preconditioner is chosen at random (we just want to be //--- sure that preconditioning won't prevent us from detecting //--- infeasible point): //--- * unit preconditioner //--- * random diagonal-based preconditioner //--- * random scale-based preconditioner //--- * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N //--- * algorithm must return correct error code on such problem for(preckind=0; preckind<=2; preckind++) { for(pkind=1; pkind<=2; pkind++) { for(n=1; n<=nmax; n++) { //--- Generate X, BL, BU. p=2*pkind; bl=vector::Zeros(n); bu=vector::Zeros(n); x=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i=0, for i=0..m_n-1 //--- with initial point //--- x[0] = 1.0E-100, x[1]=x[2]=...=0.5 //--- We try to reproduce this problem in different settings: //--- * boundary-only constraints - we test that completion code is positive, //--- and all x[] are EXACTLY zero //--- * boundary constraints posed as general linear ones - we test that //--- completion code is positive, and all x[] are APPROXIMATELY zero. n=10; x=vector::Full(n,0.5); bl=vector::Zeros(n); bu=vector::Full(n,AL_POSINF); x.Set(0,1.0E-100); CMinBC::MinBCCreate(n,x,state); CMinBC::MinBCSetBC(state,bl,bu); CMinBC::MinBCSetCond(state,0,0,0,2*n); while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { state.m_f=0; for(i=0; i0) { for(i=0; i::Full(n,10); xlast=vector::Zeros(n); bl=vector::Zeros(n); bu=vector::Full(n,AL_POSINF); for(i=0; i::Ones(n); xlast=vector::Zeros(n); if(dkind==0) CMinBC::MinBCCreate(n,x,state); if(dkind==1) CMinBC::MinBCCreateF(n,x,diffstep,state); CMinBC::MinBCSetCond(state,1.0E-6,0,epsx,0); wasf=false; wasfg=false; while(CMinBC::MinBCIteration(state)) { if(state.m_needf || state.m_needfg) state.m_f=0; for(i=0; i::Zeros(1); s=vector::Zeros(1); diffstep=CMath::RandomReal()*1.0E-6; s.Set(0,MathExp(CMath::RandomReal()*4-2)); x.Set(0,0); CMinBC::MinBCCreateF(1,x,diffstep,state); CMinBC::MinBCSetCond(state,1.0E-6,0,epsx,0); CMinBC::MinBCSetScale(state,s); v=0; while(CMinBC::MinBCIteration(state)) { state.m_f=CMath::Sqr(state.m_x[0]); v=MathMax(v,MathAbs(state.m_x[0])); } CMinBC::MinBCResults(state,x,rep); r=v/(s[0]*diffstep); CAp::SetErrorFlag(err,MathAbs(MathLog(r))>(MathLog(1+1000*CMath::m_machineepsilon)),"testminbcunit.ap:618"); } //--- Test stpmax for(pass=1; pass<=passcount; pass++) { n=1; x=vector::Zeros(n); bl=vector::Zeros(n); bu=vector::Zeros(n); x.Set(0,100); bl.Set(0,2*CMath::RandomReal()-1); bu.Set(0,AL_POSINF); stpmax=0.05+0.05*CMath::RandomReal(); CMinBC::MinBCCreate(n,x,state); CMinBC::MinBCSetBC(state,bl,bu); CMinBC::MinBCSetCond(state,epsg,0,epsx,0); CMinBC::MinBCSetXRep(state,true); CMinBC::MinBCSetStpMax(state,stpmax); xprev=x[0]; while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]); state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0])); CAp::SetErrorFlag(err,MathAbs(state.m_x[0]-xprev)>((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminbcunit.ap:646"); } if(state.m_xupdated) { CAp::SetErrorFlag(err,(double)(MathAbs(state.m_x[0]-xprev))>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminbcunit.ap:650"); xprev=state.m_x[0]; } } } //--- Ability to solve problems with function which is unbounded from below for(pass=1; pass<=passcount; pass++) { n=1; x=vector::Zeros(n); bl=vector::Zeros(n); bu=vector::Zeros(n); bl.Set(0,4*CMath::RandomReal()+1); bu.Set(0,bl[0]+1); x.Set(0,0.5*(bl[0]+bu[0])); CMinBC::MinBCCreate(n,x,state); CMinBC::MinBCSetBC(state,bl,bu); CMinBC::MinBCSetCond(state,epsg,0,epsx,0); while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0])); state.m_g.Set(0,-(2.0E8*state.m_x[0])); } } CMinBC::MinBCResults(state,x,rep); CAp::SetErrorFlag(err,MathAbs(x[0]-bu[0])>epsx,"testminbcunit.ap:680"); } //--- Test correctness of the scaling: //--- * initial point is random point from [+1,+2]^N //--- * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100] //--- * function is EFFECTIVELY unconstrained; it has formal constraints, //--- but they are inactive at the solution; we try different variants //--- in order to explore different control paths of the optimizer: //--- 0) absense of constraints //--- 1) bound constraints -100000<=x[i]<=100000 //--- * we use random scaling matrix //--- * we test different variants of the preconditioning: //--- 0) unit preconditioner //--- 1) random diagonal from [0.01,100] //--- 2) scale preconditioner //--- * we set very stringent stopping conditions //--- * and we test that in the extremum stopping conditions are //--- satisfied subject to the current scaling coefficients. for(pass=1; pass<=passcount; pass++) { tmpeps=1.0E-5; for(n=1; n<=10; n++) { for(ckind=0; ckind<=1; ckind++) { for(pkind=0; pkind<=2; pkind++) { x=vector::Zeros(n); a=vector::Zeros(n); s=vector::Zeros(n); h=vector::Zeros(n); bl=vector::Full(n,-100000); bu=vector::Full(n,100000); for(i=0; i(double)(tmpeps),"testminbcunit.ap:754"); } } } } //--- Check correctness of the "trimming". //--- Trimming is a technique which is used to help algorithm //--- cope with unbounded functions. In order to check this //--- technique we will try to solve following optimization //--- problem: //--- min f(x) subject to no constraints on X //--- { 1/(1-x) + 1/(1+x) + c*x, if -0.999999=0.999999 //--- where c is either 1.0 or 1.0E+4, M is either 1.0E8, 1.0E20 or +INF //--- (we try different combinations) for(pass=1; pass<=passcount; pass++) { for(ckind=0; ckind<=1; ckind++) { for(mkind=0; mkind<=2; mkind++) { //--- Choose c and M vc=1; vm=1; if(ckind==0) vc=1.0; if(ckind==1) vc=1.0E+4; if(mkind==0) vm=1.0E+8; if(mkind==1) vm=1.0E+20; if(mkind==2) vm=AL_POSINF; //--- Create optimizer, solve optimization problem epsg=1.0E-6*vc; x=vector::Zeros(1); CMinBC::MinBCCreate(1,x,state); CMinBC::MinBCSetCond(state,epsg,0,0,0); while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { if((-0.999999)(double)(epsg),"testminbcunit.ap:825"); } } } //--- Test behaviour on noisy functions. //--- Consider following problem: //--- * f(x,y) = (x+1)^2 + (y+1)^2 + 10000*MachineEpsilon*RandomReal() //--- * boundary constraints x>=0, y>=0 //--- * starting point (x0,y0)=(10*MachineEpsilon,1.0) //--- Such problem contains small numerical noise. Without noise its //--- solution is (xs,ys)=(0,0), which is easy to find. However, presence //--- of the noise makes it hard to solve: //--- * noisy f(x,y) is monotonically decreasing only when we perform //--- steps orders of magnitude larger than 10000*MachineEpsilon //--- * at small scales f(x,y) is non-monotonic and non-convex //--- * however, our first step must be done towards //--- (x1,y1) = (0,1-some_small_value), and length of such step is //--- many times SMALLER than 10000*MachineEpsilon //--- * second step, from (x1,y1) to (xs,ys), will be large enough to //--- ignore numerical noise, so the only problem is to perform //--- first step //--- Naive implementation of BC should fail sometimes (sometimes - //--- due to non-deterministic nature of noise) on such problem. However, //--- our improved implementation should solve it correctly. We test //--- several variations of inner stopping criteria. for(pass=1; pass<=passcount; pass++) { eps=1.0E-9; x.Resize(2); bl=vector::Zeros(2); bu=vector::Zeros(2); x.Set(0,10*CMath::m_machineepsilon); x.Set(1,1.0); bu.Set(0,AL_POSINF); bu.Set(1,AL_POSINF); for(ckind=0; ckind<=2; ckind++) { CMinBC::MinBCCreate(2,x,state); CMinBC::MinBCSetBC(state,bl,bu); if(ckind==0) CMinBC::MinBCSetCond(state,eps,0,0,0); if(ckind==1) CMinBC::MinBCSetCond(state,0,eps,0,0); if(ckind==2) CMinBC::MinBCSetCond(state,0,0,eps,0); while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { state.m_f=CMath::Sqr(state.m_x[0]+1)+CMath::Sqr(state.m_x[1]+1)+10000*CMath::m_machineepsilon*CMath::RandomReal(); state.m_g.Set(0,2*(state.m_x[0]+1)); state.m_g.Set(1,2*(state.m_x[1]+1)); } } CMinBC::MinBCResults(state,xf,rep); if((rep.m_terminationtype<=0 || xf[0]!=0.0) || xf[1]!=0.0) { CAp::SetErrorFlag(err,true,"testminbcunit.ap:889"); return; } } } //--- Deterministic variation of the previous problem. //--- Consider following problem: //--- * boundary constraints x>=0, y>=0 //--- * starting point (x0,y0)=(10*MachineEpsilon,1.0) //--- / (x+1)^2 + (y+1)^2, for (x,y)<>(x0,y0) //--- * f(x,y) = | //--- \ (x+1)^2 + (y+1)^2 - 0.1, for (x,y)=(x0,y0) //--- Such problem contains deterministic numerical noise (-0.1 at //--- starting point). Without noise its solution is easy to find. //--- However, presence of the noise makes it hard to solve: //--- * our first step must be done towards (x1,y1) = (0,1-some_small_value), //--- but such step will increase function valye by approximately 0.1 - //--- instead of decreasing it. //--- Naive implementation of BC should fail on such problem. However, //--- our improved implementation should solve it correctly. We test //--- several variations of inner stopping criteria. for(pass=1; pass<=passcount; pass++) { eps=1.0E-9; x=vector::Ones(2); bl=vector::Zeros(2); bu=vector::Zeros(2); x.Set(0,10*CMath::m_machineepsilon); bu.Set(0,AL_POSINF); bu.Set(1,AL_POSINF); for(ckind=0; ckind<=2; ckind++) { CMinBC::MinBCCreate(2,x,state); CMinBC::MinBCSetBC(state,bl,bu); if(ckind==0) CMinBC::MinBCSetCond(state,eps,0,0,0); if(ckind==1) CMinBC::MinBCSetCond(state,0,eps,0,0); if(ckind==2) CMinBC::MinBCSetCond(state,0,0,eps,0); while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { state.m_f=CMath::Sqr(state.m_x[0]+1)+CMath::Sqr(state.m_x[1]+1); if(state.m_x[0]==x[0] && (double)(state.m_x[1])==x[1]) state.m_f-=0.1; state.m_g.Set(0,2*(state.m_x[0]+1)); state.m_g.Set(1,2*(state.m_x[1]+1)); } } CMinBC::MinBCResults(state,xf,rep); if((rep.m_terminationtype<=0 || xf[0]!=0.0) || xf[1]!=0.0) { CAp::SetErrorFlag(err,true,"testminbcunit.ap:952"); return; } } } //--- Test integrity checks for NAN/INF: //--- * algorithm solves optimization problem, which is normal for some time (quadratic) //--- * after 5-th step we choose random component of gradient and consistently spoil //--- it by NAN or INF. //--- * we check that correct termination code is returned (-8) n=100; for(pass=1; pass<=10; pass++) { spoiliteration=5; stopiteration=8; if(CHighQualityRand::HQRndNormal(rs)>0.0) { //--- Gradient can be spoiled by +INF, -INF, NAN spoilvar=CHighQualityRand::HQRndUniformI(rs,n); i=CHighQualityRand::HQRndUniformI(rs,3); spoilval=AL_NaN; if(i==0) spoilval=AL_NEGINF; if(i==1) spoilval=AL_POSINF; } else { //--- Function value can be spoiled only by NAN //--- (+INF can be recognized as legitimate value during optimization) spoilvar=-1; spoilval=AL_NaN; } CMatGen::SPDMatrixRndCond(n,1.0E5,fulla); b=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i=spoiliteration) { if(spoilvar<0) state.m_f=spoilval; else state.m_g.Set(spoilvar,spoilval); } continue; } if(state.m_xupdated) { k++; continue; } CAp::Assert(false); err=true; return; } CMinBC::MinBCResults(state,x1,rep); CAp::SetErrorFlag(err,rep.m_terminationtype!=-8,"testminbcunit.ap:1037"); } //--- Check algorithm ability to handle request for termination: //--- * to terminate with correct return code = 8 //---*to return point which was "current" at the moment of termination //--- NOTE: we solve problem with "corrupted" preconditioner which makes it hard //--- to converge in less than StopCallIdx iterations for(pass=1; pass<=50; pass++) { n=3; ss=100; x=vector::Zeros(n); xlast=vector::Zeros(n); for(i=0; i::Full(3,0.00001); s.Set(2,10000.0); stopcallidx=CMath::RandomInteger(20); maxits=25; CMinBC::MinBCCreate(n,x,state); CMinBC::MinBCSetCond(state,0,0,0,maxits); CMinBC::MinBCSetXRep(state,true); CMinBC::MinBCSetPrecDiag(state,s); callidx=0; terminationrequested=false; xlast=x; while(CMinBC::MinBCIteration(state)) { if(state.m_needfg) { state.m_f=ss*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]); state.m_g.Set(0,2*ss*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[2]-state.m_x[0])*-1); state.m_g.Set(1,2*state.m_x[1]); state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0])); if(callidx==stopcallidx) { CMinBC::MinBCRequestTermination(state); terminationrequested=true; } callidx++; continue; } if(state.m_xupdated) { if(!terminationrequested) xlast=state.m_x; continue; } CAp::Assert(false); err=true; return; } CMinBC::MinBCResults(state,x,rep); CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testminbcunit.ap:1095"); for(i=0; i::Zeros(n); units=vector::Ones(n); CMinBC::MinBCCreate(n,x,state); CMinBC::MinBCSetCond(state,epsg,0.0,0.0,0); if(ckind==1) { bl=vector::Full(n,-1.0); bu=vector::Full(n,1.0); CMinBC::MinBCSetBC(state,bl,bu); } //--- Test it with default preconditioner VS. perturbed diagonal preconditioner CMinBC::MinBCSetPrecDefault(state); CMinBC::MinBCSetScale(state,units); cntb1=0; for(pass=0; pass::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminbcunit.ap:1381"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>=0,"testminbcunit.ap:1382"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test0positive,"testminbcunit.ap:1383"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test1positive,"testminbcunit.ap:1384"); //--- Test gradient checking functionality, try various //--- defect types: //--- * accidental zeroing of some gradient component //--- * accidental addition of 1.0 to some component //--- * accidental multiplication by 2.0 //--- Try distorting both target and constraints. diffstep=0.001; n=10; for(skind=0; skind<=1; skind++) { for(defecttype=-1; defecttype<=2; defecttype++) { varidx=CHighQualityRand::HQRndUniformI(rs,n); x0=vector::Zeros(n); s=vector::Zeros(n); bndl=vector::Zeros(n); bndu=vector::Zeros(n); for(i=0; ibndu[i],"testminbcunit.ap:1433"); } state.m_f=0; for(i=0; i::Zeros(1,n); jacdefect=matrix::Zeros(1,n); for(i=0; i=0) { CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminbcunit.ap:1508"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=0,"testminbcunit.ap:1509"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminbcunit.ap:1510"); } else { CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminbcunit.ap:1514"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminbcunit.ap:1515"); CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminbcunit.ap:1516"); } for(j=0; j(0.01/s[j]),"testminbcunit.ap:1520"); CAp::SetErrorFlag(waserrors,(MathAbs(jacdefect.Get(0,j)-ogrep.m_badgraduser.Get(0,j)))>(0.01/s[j]),"testminbcunit.ap:1521"); } } } //--- A test for detection of C1 continuity violations in the target. //--- Target function is a sum of |(x,c_i)| for i=1..N. //--- No constraints is present. //--- Analytic gradient is provided. //--- OptGuard should be able to detect violations in more than //--- 99.9% of runs; it means that 100 runs should have no more than 4 //--- failures in all cases (even after multiple repeated tests; according //--- to the binomial distribution quantiles). //--- We select some N and perform exhaustive search for this N. passcount=100; maxfails=4; maxc1test0fails=10; maxc1test1fails=10; n=1+CHighQualityRand::HQRndUniformI(rs,10); failurecounter=0; c1test0fails=0; c1test1fails=0; avgstr0len=0; avglng0len=0; avgstr1len=0; avglng1len=0; for(pass=1; pass<=passcount; pass++) { //--- Formulate problem x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminbcunit.ap:1615"); failed=false; failed=failed||COptGuardApi::OptGuardAllClear(ogrep); failed=failed||!ogrep.m_nonc1suspected; failed=failed||ogrep.m_nonc1fidx!=0; if(failed) failurecounter++; //--- Check C1 continuity test #0 CMinBC::MinBCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep); CMinBC::MinBCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep); if(ogrep.m_nonc1test0positive) { CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminbcunit.ap:1630"); CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminbcunit.ap:1631"); CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminbcunit.ap:1632"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminbcunit.ap:1633"); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n); avgstr0len+=(double)ognonc1test0strrep.m_cnt/(double)passcount; avglng0len+=(double)ognonc1test0lngrep.m_cnt/(double)passcount; } else { CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminbcunit.ap:1641"); CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminbcunit.ap:1642"); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n); TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n); c1test0fails++; } if(ogrep.m_nonc1test1positive) { CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminbcunit.ap:1649"); CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminbcunit.ap:1650"); CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminbcunit.ap:1651"); CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminbcunit.ap:1652"); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n); avgstr1len+=(double)ognonc1test1strrep.m_cnt/(double)passcount; avglng1len+=(double)ognonc1test1lngrep.m_cnt/(double)passcount; } else { CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminbcunit.ap:1660"); CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminbcunit.ap:1661"); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n); TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n); c1test1fails++; } } CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminbcunit.ap:1668"); CAp::SetErrorFlag(waserrors,c1test0fails>maxc1test0fails,"testminbcunit.ap:1669"); CAp::SetErrorFlag(waserrors,c1test1fails>maxc1test1fails,"testminbcunit.ap:1670"); CAp::SetErrorFlag(waserrors,(double)(avglng0len)<=(double)(avgstr0len),"testminbcunit.ap:1671"); CAp::SetErrorFlag(waserrors,(double)(avglng1len)<=(double)(avgstr1len),"testminbcunit.ap:1672"); //--- Detection of C1 continuity violations in the target under numerical differentiation: //--- * target function is a sum of |(x,c_i)| for i=1..N. //--- * no constraints is present. //--- * analytic gradient is provided. //--- OptGuard should always be able to detect violations in more than //--- 99% of runs (note: reduced strength when compared with analytic gradient); //--- it means that 100 runs should have no more than 10 failures in all cases //--- (even after multiple repeated tests; according to the binomial distribution //--- quantiles). //--- We select some N and perform exhaustive search for this N. diffstep=0.0001; passcount=100; maxfails=10; n=1+CHighQualityRand::HQRndUniformI(rs,10); failurecounter=0; for(pass=1; pass<=passcount; pass++) { //--- Formulate problem x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(n,n); for(i=0; i=0,"testminbcunit.ap:1751"); failed=false; failed=failed||COptGuardApi::OptGuardAllClear(ogrep); failed=failed||!ogrep.m_nonc1suspected; failed=failed||ogrep.m_nonc1fidx!=0; if(failed) failurecounter++; } CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminbcunit.ap:1759"); //--- Make sure than no false positives are reported for larger //--- problems where numerical noise can be an issue: //--- * N=100 dimensions //--- * positive-definite quadratic programming problem //--- * upper limit on iterations count, MaxIts=25 //--- We simply test that OptGuard does not return error code. n=100; CMatGen::SPDMatrixRndCond(n,1.0E2,a); b=vector::Zeros(n); x0=vector::Zeros(n); for(i=0; i::Zeros(n); for(int i=0; i=rep.m_stp[k+1],"testminbcunit.ap:1878"); for(k=0; k(1.0E-6*MathMax(MathAbs(v),1)),"testminbcunit.ap:1891"); } //--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity hasc1discontinuities=false; for(i=0; in,"testminbcunit.ap:1954"); CAp::SetErrorFlag(err,rep.m_n!=n,"testminbcunit.ap:1955"); CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminbcunit.ap:1956"); CAp::SetErrorFlag(err,!(rep.m_stpidxa=rep.m_stp[k+1],"testminbcunit.ap:1974"); for(k=0; k(1.0E-6*MathMax(MathAbs(v),1)),"testminbcunit.ap:1988"); } //--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity tooclose=false; hasc1discontinuities=false; for(i=0; i(1.0E-4),"testnormaldistrunit.ap:75"); } } //+------------------------------------------------------------------+ //| Bivariate normal test | //+------------------------------------------------------------------+ void CTestNormalDistrUnit::TestBVN(bool &errorflag) { //--- create variables double v0=0; double v1=0; double err=0; double x=0; double y=0; double rho=0; double h=0; CHighQualityRandState rs; CHighQualityRand::HQRndRandomize(rs); //--- Test bivariate normal CDF values (small Rho and hard Rho) err=0; err=MathMax(err,MathAbs(0.142221121370770000-CAlglib::BivariateNormalCDF(-1.060937077356340000,1.523763953950230000,0.344134938671007000))); err=MathMax(err,MathAbs(0.001090824383322160-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,0.633583566571867000,0.196737644948391000))); err=MathMax(err,MathAbs(0.000063428666799461-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-2.584892928002350000,0.603284544092224000))); err=MathMax(err,MathAbs(0.000000019144648137-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-2.987017621703680000,-0.659498170394145000))); err=MathMax(err,MathAbs(0.972551066792029000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,1.919693671247900000,0.261346800934746000))); err=MathMax(err,MathAbs(0.000000608605272790-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,2.398320119295830000,0.662485812586403000))); err=MathMax(err,MathAbs(0.576135824931800000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,4.695461487450740000,-0.110028699320946000))); err=MathMax(err,MathAbs(0.876357061322732000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,1.211768206209080000,0.397007061864995000))); err=MathMax(err,MathAbs(0.002969212104812400-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,1.340051767837440000,-0.233916559318503000))); err=MathMax(err,MathAbs(0.000377048151161364-CNormalDistr::BivariateNormalCDF(1.128363097510700000,-2.781006578069910000,-0.642877478801918000))); err=MathMax(err,MathAbs(0.995657061734790000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,2.626841258388710000,0.291409185863929000))); err=MathMax(err,MathAbs(0.003282876365551870-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,3.217920162027340000,-0.101773464540366000))); err=MathMax(err,MathAbs(0.002099371685469470-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-2.262911120125770000,0.361735313431128000))); err=MathMax(err,MathAbs(0.648944114852307000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,0.432740073549983000,0.092845182466246300))); err=MathMax(err,MathAbs(0.000000000094851728-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-3.491204153631050000,-0.010492822687090300))); err=MathMax(err,MathAbs(0.000416778593465223-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,1.862802982022170000,0.398642687655347000))); err=MathMax(err,MathAbs(0.741640376816388000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,0.648415139929991000,-0.692925257444683000))); err=MathMax(err,MathAbs(0.868844042717264000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,1.148153167494120000,0.297877516862745000))); err=MathMax(err,MathAbs(0.999356215351682000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,3.221749932900420000,-0.257163446680127000))); err=MathMax(err,MathAbs(0.998760969813713000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.111433954663520000,0.198012195099628000))); CAp::SetErrorFlag(errorflag,err>(1.0E-12),"testnormaldistrunit.ap:121"); err=0; err=MathMax(err,MathAbs(0.080575405379940000-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,1.523763953950230000,-0.999999999999000000))); err=MathMax(err,MathAbs(0.000000000000000007-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,-1.060937077356340000,-0.999999999999000000))); err=MathMax(err,MathAbs(0.000000000000000007-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,-1.060937077354570000,-0.999999999999000000))); err=MathMax(err,MathAbs(0.000000000000000016-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,0.633583566571867000,-0.999999999990000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,-3.034280037001620000,-0.999999999990000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,-3.034280036974940000,-0.999999999990000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-2.584892928002350000,-0.999999999900000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-3.577198111015300000,-0.999999999900000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-3.577198111015280000,-0.999999999900000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-2.987017621703680000,-0.999999999000000000))); err=MathMax(err,MathAbs(0.000000000000000021-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-1.053171728048290000,-0.999999999000000000))); err=MathMax(err,MathAbs(0.000000000000000014-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-1.052982935276290000,-0.999999999000000000))); err=MathMax(err,MathAbs(0.972550843757766000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,1.919693671247900000,-0.999999990000000000))); err=MathMax(err,MathAbs(0.999998291644901000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,4.785178416287600000,-0.999999990000000000))); err=MathMax(err,MathAbs(0.999998291644901000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,4.785178416295720000,-0.999999990000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,2.398320119295830000,-0.999999900000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,-4.852815283224590000,-0.999999900000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,-4.852815283224590000,-0.999999900000000000))); err=MathMax(err,MathAbs(0.576135517318684000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,4.695461487450740000,-0.999999000000000000))); err=MathMax(err,MathAbs(0.152273694692214000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,0.192020298597275000,-0.999999000000000000))); err=MathMax(err,MathAbs(0.152273697664791000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,0.192020306187062000,-0.999999000000000000))); err=MathMax(err,MathAbs(0.869148589647661000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,1.211768206209080000,-0.999990000000000000))); err=MathMax(err,MathAbs(0.963898301217005000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,2.095780141251770000,-0.999990000000000000))); err=MathMax(err,MathAbs(0.963898301217035000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,2.095780141252440000,-0.999990000000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,1.340051767837440000,-0.999900000000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,-2.654007687508430000,-0.999900000000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,-2.654007613149620000,-0.999900000000000000))); err=MathMax(err,MathAbs(0.000000000000000019-CNormalDistr::BivariateNormalCDF(1.128363097510700000,-2.781006578069910000,-0.999000000000000000))); err=MathMax(err,MathAbs(0.740833394117601000-CNormalDistr::BivariateNormalCDF(1.128363097510700000,1.128363097510700000,-0.999000000000000000))); err=MathMax(err,MathAbs(0.740862731628071000-CNormalDistr::BivariateNormalCDF(1.128363097510700000,1.128502099120660000,-0.999000000000000000))); err=MathMax(err,MathAbs(0.995654456762395000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,2.626841258388710000,-0.990000000000000000))); err=MathMax(err,MathAbs(0.999927066319845000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,3.966506850977000000,-0.990000000000000000))); err=MathMax(err,MathAbs(0.999927066319846000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,3.966506850981670000,-0.990000000000000000))); err=MathMax(err,MathAbs(0.002769246133985220-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,3.217920162027340000,-0.900000000000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,-2.717530641700190000,-0.900000000000000000))); err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,-2.717530635181090000,-0.900000000000000000))); err=MathMax(err,MathAbs(0.010079636581695100-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-2.262911120125770000,0.900000000000000000))); err=MathMax(err,MathAbs(0.021493462833838000-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-1.811681729272450000,0.900000000000000000))); err=MathMax(err,MathAbs(0.021493462833871500-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-1.811681729271170000,0.900000000000000000))); err=MathMax(err,MathAbs(0.667398193291227000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,0.432740073549983000,0.990000000000000000))); err=MathMax(err,MathAbs(0.964688949692122000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,1.861468373436860000,0.990000000000000000))); err=MathMax(err,MathAbs(0.964688949699170000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,1.861468373617680000,0.990000000000000000))); err=MathMax(err,MathAbs(0.000000482786767864-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-3.491204153631050000,0.999000000000000000))); err=MathMax(err,MathAbs(0.000000439041574222-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-4.898527851968480000,0.999000000000000000))); err=MathMax(err,MathAbs(0.000000439041575582-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-4.898527850755250000,0.999000000000000000))); err=MathMax(err,MathAbs(0.000416850246666766-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,1.862802982022170000,0.999900000000000000))); err=MathMax(err,MathAbs(0.000408379488679640-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,-3.341356669094100000,0.999900000000000000))); err=MathMax(err,MathAbs(0.000408379488680111-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,-3.341356669093450000,0.999900000000000000))); err=MathMax(err,MathAbs(0.741641759669778000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,0.648415139929991000,0.999990000000000000))); err=MathMax(err,MathAbs(0.999998605095178000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,4.687494092358740000,0.999990000000000000))); err=MathMax(err,MathAbs(0.999998606247630000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,4.687843556049730000,0.999990000000000000))); err=MathMax(err,MathAbs(0.874547330614982000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,1.148153167494120000,0.999999000000000000))); err=MathMax(err,MathAbs(0.991070530231016000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,2.369093655782270000,0.999999000000000000))); err=MathMax(err,MathAbs(0.991070530231065000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,2.369093655786410000,0.999999000000000000))); err=MathMax(err,MathAbs(0.999362948580782000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,3.221749932900420000,0.999999900000000000))); err=MathMax(err,MathAbs(0.999993261271150000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,4.352384277131720000,0.999999900000000000))); err=MathMax(err,MathAbs(0.999993261272904000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,4.352384391237480000,0.999999900000000000))); err=MathMax(err,MathAbs(0.999069094420465000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.111433954663520000,0.999999990000000000))); err=MathMax(err,MathAbs(0.999689455134645000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.422289814750960000,0.999999990000000000))); err=MathMax(err,MathAbs(0.999689455134658000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.422289814777010000,0.999999990000000000))); err=MathMax(err,MathAbs(0.294719708527162000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,2.177679562057720000,0.999999999000000000))); err=MathMax(err,MathAbs(0.294713555379044000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,-0.539648565868907000,0.999999999000000000))); err=MathMax(err,MathAbs(0.294718518285085000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,-0.539602058260055000,0.999999999000000000))); err=MathMax(err,MathAbs(0.000002811467986141-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,2.436946780486250000,0.999999999900000000))); err=MathMax(err,MathAbs(0.000002811392754616-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,-4.540093229575050000,0.999999999900000000))); err=MathMax(err,MathAbs(0.000002811467986126-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,-4.540001786779430000,0.999999999900000000))); err=MathMax(err,MathAbs(0.565106870340374000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,3.995097146641120000,0.999999999990000000))); err=MathMax(err,MathAbs(0.565106168077456000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,0.163929988133744000,0.999999999990000000))); err=MathMax(err,MathAbs(0.565106168096292000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,0.163929988229317000,0.999999999990000000))); err=MathMax(err,MathAbs(0.000064751025417698-CNormalDistr::BivariateNormalCDF(3.421155338081630000,-3.827403648909790000,0.999999999999000000))); err=MathMax(err,MathAbs(0.999688220825439000-CNormalDistr::BivariateNormalCDF(3.421155338081630000,3.421155338081630000,0.999999999999000000))); err=MathMax(err,MathAbs(0.999688221472430000-CNormalDistr::BivariateNormalCDF(3.421155338081630000,3.421174755498880000,0.999999999999000000))); CAp::SetErrorFlag(errorflag,err>(1.0E-12),"testnormaldistrunit.ap:195"); //--- Test that BVN PDF is roughly equal to derivative of BVN CDF for(int k=0; k<=999; k++) { //--- Generate trial point x=CHighQualityRand::HQRndNormal(rs); y=CHighQualityRand::HQRndNormal(rs); rho=0; //--- Compare two values: normal PDF and differentiation of normal CDF with step H h=1.0E-5; v0=CNormalDistr::BivariateNormalPDF(x,y,rho); v1=(CNormalDistr::BivariateNormalCDF(x+h,y+h,rho)+CNormalDistr::BivariateNormalCDF(x-h,y-h,rho)-CNormalDistr::BivariateNormalCDF(x+h,y-h,rho)-CNormalDistr::BivariateNormalCDF(x-h,y+h,rho))/CMath::Sqr(2*h); CAp::SetErrorFlag(errorflag,MathAbs(v0-v1)>(1.0E-4),"testnormaldistrunit.ap:215"); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestWSRUnit { public: CTestWSRUnit(void) {}; ~CTestWSRUnit(void) {}; //--- static bool TestWSR(bool silent); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestWSRUnit::TestWSR(bool silent) { //--- create variables bool result; bool waserrors; CRowDouble xa; int n=0; int i=0; double taill=0; double tailr=0; double tailb=0; double taillprev=0; double tailrprev=0; double ebase=0; double eshift=0; waserrors=false; //--- Test monotonicity of tail values for moving value of E for(n=5; n<=50; n++) { //--- Generate uniform and sorted X spanning [0,1] xa=vector::Zeros(n); for(i=0; itailr,"testwsrunit.ap:43"); CAp::SetErrorFlag(waserrors,taillprev::Zeros(n); for(i=0; i::Zeros(n); for(i=0; i::Zeros(m); for(i=0; itailr,"testmannwhitneyuunit.ap:58"); CAp::SetErrorFlag(waserrors,taillprev::Zeros(n); y=vector::Zeros(m); //--- Generate two uniformly distributed values, calculate p-value for both-tails, repeat k=0; for(pass=0; pass<=testcnt-1; pass++) { for(i=0; i0.035,"testmannwhitneyuunit.ap:101"); else { if(n<15) CAp::SetErrorFlag(waserrors,v>0.025,"testmannwhitneyuunit.ap:103"); else { if(n<30) CAp::SetErrorFlag(waserrors,v>0.020,"testmannwhitneyuunit.ap:105"); else CAp::SetErrorFlag(waserrors,v>0.015,"testmannwhitneyuunit.ap:107"); } } } //--- Test symmetry properties for(n=5; n<=50; n++) { for(m=5; m<=50; m++) { x=vector::Zeros(n); y=vector::Zeros(m); for(i=0; i(1.0E-12),"testmannwhitneyuunit.ap:126"); CAp::SetErrorFlag(waserrors,MathAbs(taill-tailr1)>(1.0E-12),"testmannwhitneyuunit.ap:127"); CAp::SetErrorFlag(waserrors,MathAbs(tailr-taill1)>(1.0E-12),"testmannwhitneyuunit.ap:128"); } } //--- Test for integer overflow in the function: if one crucial //--- calculation step is performed in 32-bit integer arithmetics, //--- it will return incorrect results. //--- We use special handcrafted N, such that in 32-bit integer //--- arithmetics int32(N*N)<0. Such negative N leads to domain //--- error in the sqrt() function. n=50000; x=vector::Zeros(n); y=vector::Zeros(n); for(i=0; i::Zeros(6); x.Set(0,-3); x.Set(1,-2); x.Set(2,-1); x.Set(3,1); x.Set(4,2); x.Set(5,3); CSignTest::OneSampleSignTest(x,6,0.0,tailb,taill,tailr); waserrors=waserrors||MathAbs(taill-0.65625)>eps; waserrors=waserrors||MathAbs(tailr-0.65625)>eps; waserrors=waserrors||MathAbs(tailb-1.00000)>eps; CSignTest::OneSampleSignTest(x,6,-1.0,tailb,taill,tailr); waserrors=waserrors||MathAbs(taill-0.81250)>eps; waserrors=waserrors||MathAbs(tailr-0.50000)>eps; waserrors=waserrors||MathAbs(tailb-1.00000)>eps; CSignTest::OneSampleSignTest(x,6,-1.5,tailb,taill,tailr); waserrors=waserrors||MathAbs(taill-0.89062)>eps; waserrors=waserrors||MathAbs(tailr-0.34375)>eps; waserrors=waserrors||MathAbs(tailb-0.68750)>eps; CSignTest::OneSampleSignTest(x,6,-3.0,tailb,taill,tailr); waserrors=waserrors||MathAbs(taill-1.00000)>eps; waserrors=waserrors||MathAbs(tailr-0.03125)>eps; waserrors=waserrors||MathAbs(tailb-0.06250)>eps; //--- Test 2 x=vector::Full(3,2); CSignTest::OneSampleSignTest(x,3,2.0,tailb,taill,tailr); waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; waserrors=waserrors||tailb!=1.0; //--- Final report if(!silent) { PrintResult("SIGN TEST",!waserrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestStudentTestsUnit { public: static bool TestStudentTests(bool silent); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestStudentTestsUnit::TestStudentTests(bool silent) { //--- create variables bool waserrors=false; double eps=0.001; bool result; CRowDouble x; CRowDouble y; CRowDouble xa; CRowDouble ya; CRowDouble xb; CRowDouble yb; int n=0; int i=0; double taill=0; double tailr=0; double tailb=0; double taill1=0; double tailr1=0; double tailb1=0; //--- 1-sample test n=8; x.Resize(8); x.Set(0,-3.0); x.Set(1,-1.5); x.Set(2,-1.0); x.Set(3,-0.5); x.Set(4,0.5); x.Set(5,1.0); x.Set(6,1.5); x.Set(7,3.0); CStudentTests::StudentTest1(x,n,0.0,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-1.00000)>eps; waserrors=waserrors||MathAbs(taill-0.50000)>eps; waserrors=waserrors||MathAbs(tailr-0.50000)>eps; CStudentTests::StudentTest1(x,n,1.0,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-0.17816)>eps; waserrors=waserrors||MathAbs(taill-0.08908)>eps; waserrors=waserrors||MathAbs(tailr-0.91092)>eps; CStudentTests::StudentTest1(x,n,-1.0,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-0.17816)>eps; waserrors=waserrors||MathAbs(taill-0.91092)>eps; waserrors=waserrors||MathAbs(tailr-0.08908)>eps; x=vector::Full(8,1.1); CStudentTests::StudentTest1(x,n,1.1,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest1(x,n,0.0,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=0.0; x.Set(7,1.1); CStudentTests::StudentTest1(x,1,1.1,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest1(x,1,0.0,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=0.0; //--- 2-sample pooled (equal variance) test n=8; x.Resize(8); y.Resize(8); x.Set(0,-3.0); x.Set(1,-1.5); x.Set(2,-1.0); x.Set(3,-0.5); x.Set(4,0.5); x.Set(5,1.0); x.Set(6,1.5); x.Set(7,3.0); y.Set(0,-2.0); y.Set(1,-0.5); y.Set(2,0.0); y.Set(3,0.5); y.Set(4,1.5); y.Set(5,2.0); y.Set(6,2.5); y.Set(7,4.0); CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-0.30780)>eps; waserrors=waserrors||MathAbs(taill-0.15390)>eps; waserrors=waserrors||MathAbs(tailr-0.84610)>eps; CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-0.53853)>eps; waserrors=waserrors||MathAbs(taill-0.26927)>eps; waserrors=waserrors||MathAbs(tailr-0.73074)>eps; CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-0.13829)>eps; waserrors=waserrors||MathAbs(taill-0.06915)>eps; waserrors=waserrors||MathAbs(tailr-0.93086)>eps; x=vector::Full(8,-1.0); y=vector::Ones(8); CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,n,y,1,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,1,y,n,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,1,y,1,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(y,1,x,1,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=0.0; x=vector::Full(8,1.1); y=vector::Full(8,1.1); CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,n,y,1,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,1,y,n,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; CStudentTests::StudentTest2(x,1,y,1,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; //--- 2-sample unpooled (unequal variance) test: //--- * test on two non-constant samples //--- * tests on different combinations of non-constant and constant samples n=8; xa.Resize(n); ya.Resize(n); xb.Resize(n); yb.Resize(n); xa.Set(0,-3.0); xa.Set(1,-1.5); xa.Set(2,-1.0); xa.Set(3,-0.5); xa.Set(4,0.5); xa.Set(5,1.0); xa.Set(6,1.5); xa.Set(7,3.0); ya.Set(0,-1.0); ya.Set(1,-0.5); ya.Set(2,0.0); ya.Set(3,0.5); ya.Set(4,1.5); ya.Set(5,2.0); ya.Set(6,2.5); ya.Set(7,3.0); xb.Set(0,-1.1); xb.Set(1,-1.1); xb.Set(2,-1.1); xb.Set(3,-1.1); xb.Set(4,-1.1); xb.Set(5,-1.1); xb.Set(6,-1.1); xb.Set(7,-1.1); yb.Set(0,1.1); yb.Set(1,1.1); yb.Set(2,1.1); yb.Set(3,1.1); yb.Set(4,1.1); yb.Set(5,1.1); yb.Set(6,1.1); yb.Set(7,1.1); CStudentTests::UnequalVarianceTest(xa,n,ya,n,tailb,taill,tailr); waserrors=waserrors||MathAbs(tailb-0.25791)>eps; waserrors=waserrors||MathAbs(taill-0.12896)>eps; waserrors=waserrors||MathAbs(tailr-0.87105)>eps; CStudentTests::UnequalVarianceTest(xa,n,yb,n,tailb,taill,tailr); CStudentTests::StudentTest1(xa,n,1.1,tailb1,taill1,tailr1); waserrors=waserrors||MathAbs(tailb-tailb1)>eps; waserrors=waserrors||MathAbs(taill-taill1)>eps; waserrors=waserrors||MathAbs(tailr-tailr1)>eps; CStudentTests::UnequalVarianceTest(xa,n,yb,1,tailb,taill,tailr); CStudentTests::StudentTest1(xa,n,1.1,tailb1,taill1,tailr1); waserrors=waserrors||MathAbs(tailb-tailb1)>eps; waserrors=waserrors||MathAbs(taill-taill1)>eps; waserrors=waserrors||MathAbs(tailr-tailr1)>eps; CStudentTests::UnequalVarianceTest(xb,n,ya,n,tailb,taill,tailr); CStudentTests::StudentTest1(ya,n,-1.1,tailb1,taill1,tailr1); waserrors=waserrors||MathAbs(tailb-tailb1)>eps; waserrors=waserrors||MathAbs(taill-tailr1)>eps; waserrors=waserrors||MathAbs(tailr-taill1)>eps; CStudentTests::UnequalVarianceTest(xb,1,ya,n,tailb,taill,tailr); CStudentTests::StudentTest1(ya,n,-1.1,tailb1,taill1,tailr1); waserrors=waserrors||MathAbs(tailb-tailb1)>eps; waserrors=waserrors||MathAbs(taill-tailr1)>eps; waserrors=waserrors||MathAbs(tailr-taill1)>eps; CStudentTests::UnequalVarianceTest(xb,1,yb,1,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=0.0; waserrors=waserrors||tailr!=1.0; CStudentTests::UnequalVarianceTest(yb,1,xb,1,tailb,taill,tailr); waserrors=waserrors||tailb!=0.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=0.0; CStudentTests::UnequalVarianceTest(xb,1,xb,1,tailb,taill,tailr); waserrors=waserrors||tailb!=1.0; waserrors=waserrors||taill!=1.0; waserrors=waserrors||tailr!=1.0; //--- Test for integer overflow in the function: if one crucial //--- calculation step is performed in 32-bit integer arithmetics, //--- it will return incorrect results. //--- We use special handcrafted N, such that in 32-bit integer //--- arithmetics int32(N*N)<0. Such negative N leads to domain //--- error in the incomplete beta function. n=50000; xa=vector::Zeros(n); ya=vector::Zeros(n); for(i=0; i::Zeros(npoints,nx); for(i=0; i0.0)) { err=true; return; } v=(1+0.1*CHighQualityRand::HQRndUniformR(rs))/MathSqrt(v); for(j=0; jxtol,"testfitsphereunit.ap:118"); CalcLSError(xy,npoints,nx,cx,v0); //--- Check that small perturbations to center position increase target function //--- NOTE: in fact, we do allow small increase in target function - but no more //--- than FTol=1E-6*XTol. It helps to avoid spurious error reports in //--- degenerate cases. ftol=1.0E-6*xtol; for(j=0; j::Zeros(npoints,nx); for(i=0; i0.0); v=(1+0.1*CHighQualityRand::HQRndUniformR(rs))/MathSqrt(v); for(j=0; j::Zeros(nx); if(problemtype==2 || problemtype==3) vlo=1; else vlo=0; if(problemtype==1 || problemtype==3) vhi=1; else vhi=0; for(j=0; j::Zeros(40,2); cnt=0; AddValue(xy,cnt,0.1026); AddValue(xy,cnt,0.000036); AddValue(xy,cnt,0.101119); AddValue(xy,cnt,0.016144); AddValue(xy,cnt,0.096754); AddValue(xy,cnt,0.031654); AddValue(xy,cnt,0.088981); AddValue(xy,cnt,0.045634); AddValue(xy,cnt,0.082056); AddValue(xy,cnt,0.06008); AddValue(xy,cnt,0.074966); AddValue(xy,cnt,0.075647); AddValue(xy,cnt,0.065); AddValue(xy,cnt,0.090471); AddValue(xy,cnt,0.052411); AddValue(xy,cnt,0.104381); AddValue(xy,cnt,0.036436); AddValue(xy,cnt,0.114859); AddValue(xy,cnt,0.019034); AddValue(xy,cnt,0.126577); AddValue(xy,cnt,-0.001191); AddValue(xy,cnt,0.139295); AddValue(xy,cnt,-0.024689); AddValue(xy,cnt,0.147143); AddValue(xy,cnt,-0.049729); AddValue(xy,cnt,0.147861); AddValue(xy,cnt,-0.076402); AddValue(xy,cnt,0.145907); AddValue(xy,cnt,-0.103928); AddValue(xy,cnt,0.139553); AddValue(xy,cnt,-0.133726); AddValue(xy,cnt,0.130429); AddValue(xy,cnt,-0.159051); AddValue(xy,cnt,0.112298); AddValue(xy,cnt,-0.179496); AddValue(xy,cnt,0.08821); AddValue(xy,cnt,-0.194562); AddValue(xy,cnt,0.059989); AddValue(xy,cnt,-0.204838); AddValue(xy,cnt,0.029135); AddValue(xy,cnt,-0.206971); AddValue(xy,cnt,-0.00349); AddValue(xy,cnt,-0.206207); AddValue(xy,cnt,-0.036427); AddValue(xy,cnt,-0.197079); AddValue(xy,cnt,-0.06806); AddValue(xy,cnt,-0.180492); AddValue(xy,cnt,-0.096353); AddValue(xy,cnt,-0.158203); AddValue(xy,cnt,-0.119891); AddValue(xy,cnt,-0.132669); AddValue(xy,cnt,-0.138375); AddValue(xy,cnt,-0.105652); AddValue(xy,cnt,-0.152229); AddValue(xy,cnt,-0.078587); AddValue(xy,cnt,-0.16316); AddValue(xy,cnt,-0.049984); AddValue(xy,cnt,-0.167084); AddValue(xy,cnt,-0.022067); AddValue(xy,cnt,-0.165233); AddValue(xy,cnt,0.004002); AddValue(xy,cnt,-0.16075); AddValue(xy,cnt,0.028058); AddValue(xy,cnt,-0.151829); AddValue(xy,cnt,0.050088); AddValue(xy,cnt,-0.141178); AddValue(xy,cnt,0.067646); AddValue(xy,cnt,-0.124169); AddValue(xy,cnt,0.081421); AddValue(xy,cnt,-0.10567); AddValue(xy,cnt,0.087305); AddValue(xy,cnt,-0.082618); AddValue(xy,cnt,0.094189); AddValue(xy,cnt,-0.064399); AddValue(xy,cnt,0.099445); AddValue(xy,cnt,-0.047018); AddValue(xy,cnt,0.09936); AddValue(xy,cnt,-0.028981); AddValue(xy,cnt,0.101784); AddValue(xy,cnt,-0.012918); tol=1.0E-7; //--- MZ problem, NLC solver rlo=0; rhi=0; CFitSphere::FitSphereMZ(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rlo,rhi); CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:395"); if(err) return; CAp::SetErrorFlag(err,MathAbs(cx[0]+0.050884688)>tol,"testfitsphereunit.ap:398"); CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011472328)>tol,"testfitsphereunit.ap:399"); CAp::SetErrorFlag(err,MathAbs(rlo-0.150973382)>tol,"testfitsphereunit.ap:400"); CAp::SetErrorFlag(err,MathAbs(rhi-0.164374709)>tol,"testfitsphereunit.ap:401"); rlo=0; rhi=0; CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),3,0.0,0,0.0,cx,rlo,rhi); CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:406"); if(err) return; CAp::SetErrorFlag(err,MathAbs(cx[0]+0.050884688)>tol,"testfitsphereunit.ap:409"); CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011472328)>tol,"testfitsphereunit.ap:410"); CAp::SetErrorFlag(err,MathAbs(rlo-0.150973382)>tol,"testfitsphereunit.ap:411"); CAp::SetErrorFlag(err,MathAbs(rhi-0.164374709)>tol,"testfitsphereunit.ap:412"); //--- MC problem, NLC solver rlo=0; rhi=0; CFitSphere::FitSphereMC(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rhi); CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:421"); if(err) return; CAp::SetErrorFlag(err,MathAbs(cx[0]+0.051137580)>tol,"testfitsphereunit.ap:424"); CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011680985)>tol,"testfitsphereunit.ap:425"); CAp::SetErrorFlag(err,rlo!=0.0,"testfitsphereunit.ap:426"); CAp::SetErrorFlag(err,MathAbs(rhi-0.164365735)>tol,"testfitsphereunit.ap:427"); rlo=0; rhi=0; CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),1,0.0,0,0.0,cx,rlo,rhi); CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:432"); if(err) return; CAp::SetErrorFlag(err,MathAbs(cx[0]+0.051137580)>tol,"testfitsphereunit.ap:435"); CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011680985)>tol,"testfitsphereunit.ap:436"); CAp::SetErrorFlag(err,rlo!=0.0,"testfitsphereunit.ap:437"); CAp::SetErrorFlag(err,MathAbs(rhi-0.164365735)>tol,"testfitsphereunit.ap:438"); //--- MI problem, NLC solver rlo=0; rhi=0; CFitSphere::FitSphereMI(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rlo); CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:447"); if(err) return; CAp::SetErrorFlag(err,MathAbs(cx[0]+0.054593489)>tol,"testfitsphereunit.ap:450"); CAp::SetErrorFlag(err,MathAbs(cx[1]+0.007459466)>tol,"testfitsphereunit.ap:451"); CAp::SetErrorFlag(err,MathAbs(rlo-0.152429205)>tol,"testfitsphereunit.ap:452"); CAp::SetErrorFlag(err,rhi!=0.0,"testfitsphereunit.ap:453"); rlo=0; rhi=0; CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),2,0.0,0,0.0,cx,rlo,rhi); CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:458"); if(err) return; CAp::SetErrorFlag(err,MathAbs(cx[0]+0.054593489)>tol,"testfitsphereunit.ap:461"); CAp::SetErrorFlag(err,MathAbs(cx[1]+0.007459466)>tol,"testfitsphereunit.ap:462"); CAp::SetErrorFlag(err,MathAbs(rlo-0.152429205)>tol,"testfitsphereunit.ap:463"); CAp::SetErrorFlag(err,rhi!=0.0,"testfitsphereunit.ap:464"); } //+------------------------------------------------------------------+ //| Used to calculate RLo / Rhi given XY and center position | //+------------------------------------------------------------------+ void CTestFitSphereUnit::CalcRadII(CMatrixDouble &xy, int npoints, int nx, CRowDouble &cx, double &rlo, double &rhi) { double v=0; rlo=0; rhi=0; rlo=CMath::m_maxrealnumber; rhi=0; for(int i=0; i::Zeros(n); for(i=0; i::Zeros(m); for(i=0; i::Zeros(l); for(i=0; i::Zeros(l*m*n*d); offs=0; ax=2*CMath::RandomReal()-1; ay=2*CMath::RandomReal()-1; az=2*CMath::RandomReal()-1; axy=2*CMath::RandomReal()-1; ayz=2*CMath::RandomReal()-1; for(k=0; keps) return(true); } if(d==1) { vsf=CSpline3D::Spline3DCalc(c,vx,vy,vz); if(MathAbs(ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vsf)>eps) return(true); } } //--- Generate random grid and test function. //--- Test spline ability to reproduce function values at grid nodes. passcount=20; for(pass=1; pass<=passcount; pass++) { //--- Prepare a model and check that functions (Spline3DBuildTrilinear, //--- Spline3DCalc,Spline3DCalcV) work correctly and BuildRndGrid(true,true,n,m,l,d,x,y,z,vf); CApServ::RVectorSetLengthAtLeast(sf,n*m*l); //--- Check that the model's values are equal to the function's values //--- in grid points CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c); for(i=0; ieps) return(true); } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| Unpack / UnpackV test | //+------------------------------------------------------------------+ bool CTestSpline3DUnit::TestUnpack(void) { //--- create variables int passcount=20; bool result; CSpline3DInterpolant c; CMatrixDouble tbl0; CMatrixDouble tbl1; int n=0; int m=0; int l=0; int d=0; int sz=0; int un=0; int um=0; int ul=0; int ud=0; int ust=0; int uvn=0; int uvm=0; int uvl=0; int uvd=0; int uvst=0; int ci=0; int cj=0; int ck=0; CRowDouble x; CRowDouble y; CRowDouble z; CRowDouble sf; CRowDouble vf; int p0=0; int p1=0; double tx=0; double ty=0; double tz=0; double v1=0; double v2=0; double err=0; int pass=0; bool bperr; int i=0; int j=0; int k=0; int di=0; int i0=0; for(pass=1; pass<=passcount; pass++) { //--- generate random grid. //--- NOTE: for this test we need ordered grid, i.e. grid //--- with nodes in ascending order BuildRndGrid(true,false,n,m,l,d,x,y,z,vf); sz=n*m*l; CApServ::RVectorSetLengthAtLeast(sf,sz); CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c); CSpline3D::Spline3DUnpackV(c,uvn,uvm,uvl,uvd,uvst,tbl0); for(di=0; di(double)(1.0E+5*CMath::m_machineepsilon); return(result); } //+------------------------------------------------------------------+ //| LinTrans test | //+------------------------------------------------------------------+ bool CTestSpline3DUnit::TestLinTrans(void) { //--- create variables int passcount=15; bool result; CSpline3DInterpolant c; CSpline3DInterpolant c2; int m=0; int n=0; int l=0; int d=0; CRowDouble x; CRowDouble y; CRowDouble z; CRowDouble f; double a1=0; double a2=0; double a3=0; double b1=0; double b2=0; double b3=0; double tx=0; double ty=0; double tz=0; double vx=0; double vy=0; double vz=0; CRowDouble v1; CRowDouble v2; int pass=0; int xjob=0; int yjob=0; int zjob=0; double err=0; int i=0; for(pass=1; pass<=passcount; pass++) { BuildRndGrid(true,false,n,m,l,d,x,y,z,f); CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,f,d,c); for(xjob=0; xjob<=1; xjob++) { for(yjob=0; yjob<=1; yjob++) { for(zjob=0; zjob<=1; zjob++) { //--- Prepare do { a1=2.0*CMath::RandomReal()-1.0; } while(a1==0.0); a1=a1*xjob; b1=x[0]+CMath::RandomReal()*(x[n-1]-x[0]+2.0)-1.0; do { a2=2.0*CMath::RandomReal()-1.0; } while(a2==0.0); a2=a2*yjob; b2=y[0]+CMath::RandomReal()*(y[m-1]-y[0]+2.0)-1.0; do { a3=2.0*CMath::RandomReal()-1.0; } while(a3==0.0); a3=a3*zjob; b3=z[0]+CMath::RandomReal()*(z[l-1]-z[0]+2.0)-1.0; //--- Test XYZ CSpline3D::Spline3DCopy(c,c2); CSpline3D::Spline3DLinTransXYZ(c2,a1,b1,a2,b2,a3,b3); tx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]); ty=y[0]+CMath::RandomReal()*(y[m-1]-y[0]); tz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]); if(xjob==0) { tx=b1; vx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]); } else vx=(tx-b1)/a1; if(yjob==0) { ty=b2; vy=y[0]+CMath::RandomReal()*(y[m-1]-y[0]); } else vy=(ty-b2)/a2; if(zjob==0) { tz=b3; vz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]); } else vz=(tz-b3)/a3; CSpline3D::Spline3DCalcV(c,tx,ty,tz,v1); CSpline3D::Spline3DCalcV(c2,vx,vy,vz,v2); for(i=0; i(double)(1.0E+4*CMath::m_machineepsilon)) { result=true; return(result); } //--- Test F CSpline3D::Spline3DCopy(c,c2); CSpline3D::Spline3DLinTransF(c2,a1,b1); tx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]); ty=y[0]+CMath::RandomReal()*(y[m-1]-y[0]); tz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]); CSpline3D::Spline3DCalcV(c,tx,ty,tz,v1); CSpline3D::Spline3DCalcV(c2,tx,ty,tz,v2); for(i=0; i(1.0E+4*CMath::m_machineepsilon); return(result); } //+------------------------------------------------------------------+ //| Resample test | //+------------------------------------------------------------------+ bool CTestSpline3DUnit::TestTrilinearreSample(void) { //--- create variables bool result=false; int passcount=20; CSpline3DInterpolant c; int n=0; int m=0; int l=0; int n2=0; int m2=0; int l2=0; CRowDouble x; CRowDouble y; CRowDouble z; CRowDouble f; CRowDouble fr; double v1=0; double v2=0; double err=0; double mf=0; int pass=0; int i=0; int j=0; int k=0; for(pass=1; pass<=passcount; pass++) { n=CMath::RandomInteger(4)+2; m=CMath::RandomInteger(4)+2; l=CMath::RandomInteger(4)+2; n2=CMath::RandomInteger(4)+2; m2=CMath::RandomInteger(4)+2; l2=CMath::RandomInteger(4)+2; CApServ::RVectorSetLengthAtLeast(x,n); CApServ::RVectorSetLengthAtLeast(y,m); CApServ::RVectorSetLengthAtLeast(z,l); CApServ::RVectorSetLengthAtLeast(f,n*m*l); for(i=0; i(1.0E+4*CMath::m_machineepsilon); if(result) return(result); } //--- return result return(result); } //+------------------------------------------------------------------+ //| The function does build random function on random grid with | //| random number of points: | //| * N, M, K - random from 2 to 5 | //| * D - 1 in case IsVect=False, 1..3 in case | //| IsVect=True | //| * X, Y, Z - each variable spans from MinV to MaxV, with | //| MinV is random number from [-1.5, 0.5] and | //| MaxV is random number from [0.5, 1.5]. All | //| nodes are well separated. All nodes are | //| randomly reordered in case Reorder=False. | //| When Reorder=True, nodes are returned in | //| ascending order. | //| * F - random values from [-1, +1] | //+------------------------------------------------------------------+ void CTestSpline3DUnit::BuildRndGrid(bool isvect,bool reorder, int &n,int &m,int &l,int &d, CRowDouble &x,CRowDouble &y, CRowDouble &z,CRowDouble &f) { //--- create variables double st=0; int i=0; int j=0; int k=0; int di=0; double v=0; double mx=0; double maxv=0; double minv=0; n=0; m=0; l=0; d=0; st=0.3; m=CMath::RandomInteger(4)+2; n=CMath::RandomInteger(4)+2; l=CMath::RandomInteger(4)+2; if(isvect) d=CMath::RandomInteger(3)+1; else d=1; CApServ::RVectorSetLengthAtLeast(x,n); CApServ::RVectorSetLengthAtLeast(y,m); CApServ::RVectorSetLengthAtLeast(z,l); CApServ::RVectorSetLengthAtLeast(f,n*m*l*d); //--- Fill X x.Set(0,0); for(i=1; i::Zeros(nx); y=vector::Zeros(ny); point=vector::Zeros(nx); CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits); q=0.25+CMath::RandomReal(); z=4.5+CMath::RandomReal(); CRBF::RBFSetAlgoQNN(s,q,z); //--- start points for grid for(i=0; i::Zeros(nx); for(i_=0; i_::Zeros(np,nx+ny); //--- create grid for(i=0; i(sy*eps)) return(true); } } } } if(nx==3) { for(k0=2; k0<=4; k0++) { for(k1=2; k1<=4; k1++) { for(gen=1; gen<=2; gen++) { CMatGen::RMatrixRndOrthogonal(nx,a); d0=vector::Zeros(nx); for(i_=0; i_::Zeros(np,nx+ny); for(i=0; i::Zeros(nx); for(i_=0; i_::Zeros(np,nx+ny); for(i=0; i=0,"rbf test: integrity error")) return(true); CRBF::RBFSetPoints(s,gp,np); CRBF::RBFBuildModel(s,rep); for(i=0; i(sy*eps)) return(true); } } if(gen==2) { CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion); dd0=(d0[0]*v.Get(0,0)+d0[1]*v.Get(0,1)+d0[2]*v.Get(0,2))/(d0[0]*d0[0]+d0[1]*d0[1]+d0[2]*d0[2]); dd1=(d1[0]*v.Get(0,0)+d1[1]*v.Get(0,1)+d1[2]*v.Get(0,2))/(d1[0]*d1[0]+d1[1]*d1[1]+d1[2]*d1[2]); pvd0=vector::Zeros(nx); pvd1=vector::Zeros(nx); for(i=0; i(vnorm*m_tol)) return(true); } } } } } } //--- return result return(false); } //+------------------------------------------------------------------+ //| Function for testing basic functionality of RBF module on regular| //| grids with multi-layer algorithm in 1D. | //+------------------------------------------------------------------+ bool CTestRBFUnit::BasicMultilayErRBF1DTest(void) { //--- create variables double a=1.0; double b=1.0/9.0; double f1=1.0; double f2=10.0; int passcount=5; int n=100; CRBFModel s; CRBFReport rep; int nx=0; int ny=0; int linterm=0; double q=0; double r=0; int errtype=0; CRowDouble delta; int nlayers=0; CRowDouble a1; CRowDouble b1; CMatrixDouble gp; CRowDouble x; CRowDouble y; CMatrixDouble mody0; CMatrixDouble mody1; CMatrixDouble gy; CRowDouble gpgx0; CRowDouble gpgx1; int pass=0; int i=0; int j=0; gpgx0=vector::Zeros(n); gpgx1=vector::Zeros(n); for(i=0; i::Zeros(nx); y=vector::Zeros(ny); a1=vector::Zeros(ny); b1=vector::Zeros(ny); delta=vector::Zeros(ny); mody0=matrix::Zeros(n,ny); mody1=matrix::Zeros(n,ny); for(i=0; i::Zeros(n,nx+ny); //--- create grid for(i=0; i=(1/(2*f2))) { CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetAlgoMultilayer(s,r,nlayers,0.0); if(linterm==1) CRBF::RBFSetLinTerm(s); if(linterm==2) CRBF::RBFSetConstTerm(s); if(linterm==3) CRBF::RBFSetZeroTerm(s); CRBF::RBFSetPoints(s,gp,n); CRBF::RBFBuildModel(s,rep); if(ny==1) { for(i=0; i0.5) { CRBF::RBFBuildModel(s,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:251"); return(result); } } x=vector::Zeros(nx); y=vector::Zeros(1); for(i=0; i::Zeros(1,nx+ny); for(i=0; i::Zeros(nx); for(i=0; ierrtol) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:316"); return(result); } } } } } //--- Create model with 2 points and different types of linear term. //--- Test algorithm on such dataset. for(nx=2; nx<=3; nx++) { for(ny=1; ny<=3; ny++) { CRBF::RBFCreate(nx,ny,s); for(termtype=0; termtype<=1; termtype++) { if(termtype==0) CRBF::RBFSetLinTerm(s); if(termtype==1) CRBF::RBFSetConstTerm(s); if(termtype==2) CRBF::RBFSetZeroTerm(s); xy=matrix::Zeros(2,nx+ny); for(i=0; i::Zeros(nx); for(i=0; ierrtol) { result=true; return(result); } } } } } } //--- Generate a set of points (xi,yi) = (SX*i,0), and one //--- outlier (x_far,y_far)=(-1000*SX,0). //--- Radii filtering should place a bound on the radius of outlier. for(nx=2; nx<=3; nx++) { for(ny=1; ny<=3; ny++) { sx=MathExp(-5+10*CMath::RandomReal()); CRBF::RBFCreate(nx,ny,s); xy=matrix::Zeros(20,nx+ny); for(i=0; i<=CAp::Rows(xy)-1; i++) { xy.Set(i,0,sx*i); for(j=0; jerrtol) { result=true; return(result); } } if(MathAbs(xwr.Get(tmpnc-1,nx+ny)-z*sx)>errtol) { result=true; return(result); } } } //--- RBF-ML with NLayers=0 gives us linear model. //--- In order to perform this test, we use test function which //--- is perfectly linear and see whether RBF model is able to //--- reproduce such function. n=5; for(ny=1; ny<=3; ny++) { va=2*CMath::RandomReal()-1; vb=2*CMath::RandomReal()-1; vc=2*CMath::RandomReal()-1; vd=2*CMath::RandomReal()-1; //--- Test NX=2. //--- Generate linear function using random coefficients VA/VB/VC. //--- Function is K-dimensional vector-valued, each component has slightly //--- different coefficients. xy=matrix::Zeros(n*n,2+ny); for(i=0; i::Zeros(2); x.Set(0,(n-1)*CMath::RandomReal()); x.Set(1,(n-1)*CMath::RandomReal()); if(ny==1 && MathAbs(CRBF::RBFCalc2(s,x[0],x[1])-(va*x[0]+vb*x[1]+vc))>errtol) { result=true; return(result); } CRBF::RBFCalc(s,x,y); for(k=0; kerrtol) { result=true; return(result); } //--- Test NX=3. //--- Generate linear function using random coefficients VA/VB/VC/VC. //--- Function is K-dimensional vector-valued, each component has slightly //--- different coefficients. xy=matrix::Zeros(n*n*n,3+ny); for(i=0; i::Zeros(3); x.Set(0,(n-1)*CMath::RandomReal()); x.Set(1,(n-1)*CMath::RandomReal()); x.Set(2,(n-1)*CMath::RandomReal()); if(ny==1 && MathAbs(CRBF::RBFCalc3(s,x[0],x[1],x[2])-(va*x[0]+vb*x[1]+vc*x[2]+vd))>errtol) { result=true; return(result); } CRBF::RBFCalc(s,x,y); for(k=0; kerrtol) { result=true; return(result); } } } //--- HierarchicalRBF with NLayers=0 gives us linear model. //--- In order to perform this test, we use test function which //--- is perfectly linear and see whether RBF model is able to //--- reproduce such function. n=15; for(nx=1; nx<=5; nx++) { for(ny=1; ny<=3; ny++) { vf=matrix::Zeros(ny,nx+1); for(i=0; i::Zeros(n,nx+ny); for(i=0; i::Zeros(nx); for(i=0; ierrtol,"testrbfunit.ap:586"); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing basic functionality of RBF module on regular| //| grids. | //+------------------------------------------------------------------+ bool CTestRBFUnit::BasicRBFTest(void) { //--- create variables double zx=10; double px=15; double zy=10; double py=15; double eps=1.0E-6; bool result; CRBFModel s; CRBFReport rep; CRBFCalcBuffer calcbuf; int nx=0; int ny=0; int k0=0; int k1=0; int k2=0; int linterm=0; int np=0; double sx=0; double sy=0; double q=0; double z=0; CRowDouble point; CMatrixDouble gp; CRowDouble x; CRowDouble y; CMatrixDouble gy; int unx=0; int uny=0; CMatrixDouble xwr; CMatrixDouble v; CRowDouble gpgx0; CRowDouble gpgx1; int i=0; int j=0; int k=0; int l=0; int fidx=0; int modelversion=0; //--- Problem types: //--- * 2 and 3-dimensional problems //--- * problems with zero, constant, linear terms //--- * different scalings of X and Y values (1.0, 1E-15, 1E+15) //--- * regular grids different grid sizes (from 2 to 4 points for each dimension) //--- We check that: //--- * RBF model correctly reproduces function value (testes with different Calc() functions) //--- * unpacked model containt correct radii //--- * linear term has correct form for(nx=2; nx<=3; nx++) { for(ny=1; ny<=2; ny++) { //--- prepare test problem linterm=CMath::RandomInteger(3)+1; sx=MathPow(zx,px*(CMath::RandomInteger(3)-1)); sy=MathPow(zy,py*(CMath::RandomInteger(3)-1)); x=vector::Zeros(nx); y=vector::Zeros(ny); point=vector::Zeros(nx); CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits); q=0.25+CMath::RandomReal(); z=4.5+CMath::RandomReal(); CRBF::RBFSetAlgoQNN(s,q,z); if(linterm==1) CRBF::RBFSetLinTerm(s); if(linterm==2) CRBF::RBFSetConstTerm(s); if(linterm==3) CRBF::RBFSetZeroTerm(s); //--- start points for grid for(i=0; i::Zeros(np,nx+ny); //--- create grid for(i=0; i::Zeros(k0); gpgx1=vector::Zeros(k1); for(i=0; i(sy*eps)) { result=true; return(result); } } } for(i=0; i(sy*eps)) { result=true; return(result); } } if(fidx==1) { CRBF::RBFCalc(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } } if(fidx==2) { CRBF::RBFCalcBuf(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } } if(fidx==3) { CRBF::RBFTSCalcBuf(s,calcbuf,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } } } //--- test for RBFUnpack CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion); if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:784"); return(result); } for(i=0; i(sx*eps)) { result=true; return(result); } } if(linterm==2) { for(i=0; i<=unx-1; i++) for(j=0; j<=uny-1; j++) if(v.Get(j,i)!=0.0) { result=true; return(result); } } if(linterm==3) { for(i=0; i<=unx; i++) for(j=0; j<=uny-1; j++) if(v.Get(j,i)!=0.0) { result=true; return(result); } } } } } //--- 3-dimensional test problems if(nx==3) { for(k0=2; k0<=4; k0++) { for(k1=2; k1<=4; k1++) { for(k2=2; k2<=4; k2++) { np=k0*k1*k2; gp=matrix::Zeros(np,nx+ny); //--- create grid for(i=0; i(sy*eps)) { result=true; return(result); } } if(fidx==1) { CRBF::RBFCalc(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } if(fidx==2) { CRBF::RBFCalcBuf(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } if(fidx==3) { CRBF::RBFTSCalcBuf(s,calcbuf,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } } //--- test for RBFUnpack CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion); if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:901"); return(result); } for(i=0; i(sx*eps)) { result=true; return(result); } } if(linterm==2) { for(i=0; i<=unx-1; i++) for(j=0; j<=uny-1; j++) if(v.Get(j,i)!=0.0) { result=true; return(result); } } if(linterm==3) { for(i=0; i<=unx; i++) for(j=0; j<=uny-1; j++) if(v.Get(j,i)!=0.0) { result=true; return(result); } } } } } } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| Function for testing RBF module on irregular grids. | //+------------------------------------------------------------------+ bool CTestRBFUnit::IrregularRBFTest(void) { //--- create variables double zx=10; double px=15; double zy=10; double py=15; double noiselevel=0.1; double eps=1.0E-6; bool result; CRBFModel s; CRBFReport rep; int nx=0; int ny=0; int k0=0; int k1=0; int k2=0; int linterm=0; int np=0; double sx=0; double sy=0; double q=0; double z=0; CRowDouble point; CMatrixDouble gp; CRowDouble x; CRowDouble y; CMatrixDouble gy; int i=0; int j=0; int k=0; int l=0; //--- Problem types: //--- * 2 and 3-dimensional problems //--- * problems with zero, constant, linear terms //--- * different scalings of X and Y values (1.0, 1E-15, 1E+15) //--- * noisy grids, which are just regular grids with different grid sizes //--- (from 2 to 4 points for each dimension) and moderate amount of random //--- noise added to all node positions. //--- We check that: //--- * RBF model correctly reproduces function value (testes with different Calc() functions) for(nx=2; nx<=3; nx++) { //--- prepare test problem linterm=CMath::RandomInteger(3)+1; ny=CMath::RandomInteger(2)+1; sx=MathPow(zx,px*(CMath::RandomInteger(3)-1)); sy=MathPow(zy,py*(CMath::RandomInteger(3)-1)); x=vector::Zeros(nx); y=vector::Zeros(ny); point=vector::Zeros(nx); CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits); q=0.25+CMath::RandomReal(); z=4.5+CMath::RandomReal(); CRBF::RBFSetAlgoQNN(s,q,z); if(linterm==1) CRBF::RBFSetLinTerm(s); if(linterm==2) CRBF::RBFSetConstTerm(s); if(linterm==3) CRBF::RBFSetZeroTerm(s); //--- start points for grid for(i=0; i::Zeros(np,nx+ny); //--- create grid for(i=0; i(sy*eps)) { result=true; return(result); } } //--- CRBF::RBFCalc(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } //--- CRBF::RBFCalcBuf(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } } } } //--- 3-dimensional test problems if(nx==3) { for(k0=2; k0<=4; k0++) { for(k1=2; k1<=4; k1++) { for(k2=2; k2<=4; k2++) { np=k0*k1*k2; gp=matrix::Zeros(np,nx+ny); //--- create grid for(i=0; i(sy*eps)) { result=true; return(result); } } //--- CRBF::RBFCalc(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } //--- CRBF::RBFCalcBuf(s,x,y); for(j=0; j(sy*eps)) { result=true; return(result); } } } } } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| The test does check, that algorithm can build linear model for| //| the data sets, when Y depends on X linearly. | //+------------------------------------------------------------------+ bool CTestRBFUnit::LinearityModelRBFTest(void) { //--- create variables double zx=10; double px=15; double zy=10; double py=15; int ny=1; bool result; CRBFModel s; CRBFReport rep; int nx=0; int k0=0; int k1=0; int k2=0; int linterm=0; int np=0; double sx=0; double sy=0; double q=0; double z=0; CRowDouble point; CRowDouble a; CMatrixDouble gp; CRowDouble x; CRowDouble y; int unx=0; int uny=0; CMatrixDouble xwr; CMatrixDouble v; int i=0; int j=0; int k=0; int l=0; int modelversion=0; for(nx=2; nx<=3; nx++) { //--- prepare test problem linterm=CMath::RandomInteger(3)+1; sx=MathPow(zx,px*(CMath::RandomInteger(3)-1)); sy=MathPow(zy,py*(CMath::RandomInteger(3)-1)); x=vector::Zeros(nx); y=vector::Zeros(ny); point=vector::Zeros(nx); CRBF::RBFCreate(nx,ny,s); q=0.25+CMath::RandomReal(); z=4.5+CMath::RandomReal(); CRBF::RBFSetAlgoQNN(s,q,z); a=vector::Zeros(nx+1); if(linterm==1) { CRBF::RBFSetLinTerm(s); for(i=0; i::Zeros(np,nx+ny); //--- create grid for(i=0; i(sy/sx*m_tol)) { result=true; return(result); } } if(MathAbs(v.Get(0,nx)-a[nx])>(sy*m_tol)) { result=true; return(result); } for(i=0; i(sy*m_tol)) { result=true; return(result); } } } } //--- if(nx==3) { for(k0=2; k0<=4; k0++) { for(k1=2; k1<=4; k1++) { for(k2=2; k2<=4; k2++) { np=k0*k1*k2; gp=matrix::Zeros(np,nx+ny); //--- create grid for(i=0; i(sy/sx*m_tol)) { result=true; return(result); } } if(MathAbs(v.Get(0,nx)-a[nx])>(sy*m_tol)) { result=true; return(result); } for(i=0; i(sy*m_tol)) { result=true; return(result); } } } } } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| This function tests serialization | //+------------------------------------------------------------------+ bool CTestRBFUnit::SerializationTest(void) { //--- create variables bool result=false; CRBFModel s; CRBFModel s2; CRBFReport rep; int n=0; int nx=0; int ny=0; int k0=0; int k1=0; int k2=0; int i=0; int i0=0; int i1=0; int i2=0; int j=0; int k=0; double rbase=0; int nlayers=0; int bf=0; int gridsize=0; CMatrixDouble xy; CRowDouble testpoint; CRowDouble y0; CRowDouble y1; CRowDouble scalevec; CHighQualityRandState rs; double meanseparation=0; int algoidx=0; CHighQualityRand::HQRndRandomize(rs); //--- This function generates random 2 or 3 dimensional problem, //--- builds RBF model (QNN is used), serializes/unserializes it, then compares //--- models by calculating model value at some random point. //--- Additionally we test that new model (one which was restored //--- after serialization) has lost all model construction settings, //--- i.e. if we call RBFBuildModel() on a NEW model, we will get //--- empty (zero) model. for(nx=2; nx<=3; nx++) { for(ny=1; ny<=2; ny++) { //--- prepare test problem CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetAlgoQNN(s,1.0,5.0); CRBF::RBFSetLinTerm(s); if(nx==2) { //--- 2-dimensional problem k0=2+CMath::RandomInteger(4); k1=2+CMath::RandomInteger(4); xy=matrix::Zeros(k0*k1,nx+ny); for(i0=0; i0::Zeros(nx); testpoint.Set(0,CMath::RandomReal()*(k0-1)); testpoint.Set(1,CMath::RandomReal()*(k1-1)); } else { //--- 3-dimensional problem k0=2+CMath::RandomInteger(4); k1=2+CMath::RandomInteger(4); k2=2+CMath::RandomInteger(4); xy=matrix::Zeros(k0*k1*k2,nx+ny); for(i0=0; i0::Zeros(nx); testpoint.Set(0,CMath::RandomReal()*(k0-1)); testpoint.Set(1,CMath::RandomReal()*(k1-1)); testpoint.Set(2,CMath::RandomReal()*(k2-1)); } CRBF::RBFSetPoints(s,xy,CAp::Rows(xy)); //--- Build model, serialize, compare CRBF::RBFBuildModel(s,rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CRBF::RBFAlloc(_local_serializer,s); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CRBF::RBFSerialize(_local_serializer,s); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CRBF::RBFUnserialize(_local_serializer,s2); _local_serializer.Stop(); } CRBF::RBFCalc(s,testpoint,y0); CRBF::RBFCalc(s2,testpoint,y1); if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny) { result=true; return(result); } for(j=0; j::Zeros(k0*k1,nx+ny); for(i0=0; i0::Zeros(nx); testpoint.Set(0,CMath::RandomReal()*(k0-1)); testpoint.Set(1,CMath::RandomReal()*(k1-1)); } else { //--- 3-dimensional problem k0=2+CMath::RandomInteger(4); k1=2+CMath::RandomInteger(4); k2=2+CMath::RandomInteger(4); xy=matrix::Zeros(k0*k1*k2,nx+ny); for(i0=0; i0::Zeros(nx); testpoint.Set(0,CMath::RandomReal()*(k0-1)); testpoint.Set(1,CMath::RandomReal()*(k1-1)); testpoint.Set(2,CMath::RandomReal()*(k2-1)); } CRBF::RBFSetPoints(s,xy,CAp::Rows(xy)); //--- Build model, serialize, compare CRBF::RBFBuildModel(s,rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CRBF::RBFAlloc(_local_serializer,s); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CRBF::RBFSerialize(_local_serializer,s); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CRBF::RBFUnserialize(_local_serializer,s2); _local_serializer.Stop(); } CRBF::RBFCalc(s,testpoint,y0); CRBF::RBFCalc(s2,testpoint,y1); if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny) { result=true; return(result); } for(j=0; j::Zeros(n,nx+ny); CAp::Assert(gridsize>1); CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx))); for(i=0; i::Zeros(nx); for(j=0; j::Zeros(nx); for(j=0; j0.5) CRBF::RBFSetPoints(s,xy,CAp::Rows(xy)); else CRBF::RBFSetPointsAndScales(s,xy,CAp::Rows(xy),scalevec); //--- Build model, serialize, compare CRBF::RBFBuildModel(s,rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Reset(); _local_serializer.Alloc_Start(); CRBF::RBFAlloc(_local_serializer,s); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CRBF::RBFSerialize(_local_serializer,s); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CRBF::RBFUnserialize(_local_serializer,s2); _local_serializer.Stop(); } CRBF::RBFCalc(s,testpoint,y0); CRBF::RBFCalc(s2,testpoint,y1); if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny) { result=true; return(result); } for(j=0; j::Zeros(nx); for(j=0; j::Zeros(nx); for(j=0; j0.5) CRBF::RBFSetPoints(s,xy,CAp::Rows(xy)); else CRBF::RBFSetPointsAndScales(s,xy,CAp::Rows(xy),scalevec); //--- Clear target model CRBF::RBFCreate(1,1,s2); CRBF::RBFBuildModel(s2,rep); CRBF::RBFCalc(s2,testpoint,y1); if(CAp::Len(y1)!=1) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:1913"); return(result); } if((double)(y1[0])!=0.0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:1918"); return(result); } //--- Build model, serialize, compare CRBF::RBFBuildModel(s,rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Reset(); _local_serializer.Alloc_Start(); CRBF::RBFAlloc(_local_serializer,s); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CRBF::RBFSerialize(_local_serializer,s); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CRBF::RBFUnserialize(_local_serializer,s2); _local_serializer.Stop(); } CRBF::RBFCalc(s,testpoint,y0); CRBF::RBFCalc(s2,testpoint,y1); if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:1933"); return(result); } for(j=0; j0,"SearchErr: invalid parameter N(N<=0).")) return(true); if(!CAp::Assert(ny>0,"SearchErr: invalid parameter NY(NY<=0).")) return(true); orerr=vector::Zeros(ny); irerr=vector::Zeros(ny); if(errtype==1) { for(i=0; i(b1[i]+delta[i]) || orerr[i]<(b1[i]-delta[i])) return(true); } } else { if(errtype==2) { for(i=0; ilb && ioralerr || irerr[i]>iralerr) return(true); } else { CAp::Assert(false,"SearchErr: invalid argument ErrType(ErrType neither 1 nor 2)"); return(true); } } //--- create variables return(false); } //+------------------------------------------------------------------+ //| Function for testing basic functionality of RBF module on regular| //| grids with multi-layer algorithm in 2-3D. | //+------------------------------------------------------------------+ bool CTestRBFUnit::BasicMultilayerRBFTest(void) { //--- create variables bool result=false; int range=10; int passcount=10; double eps=1.0E-6; CRBFModel s; CRBFReport rep; CRBFCalcBuffer calcbuf; int nx=0; int ny=0; int k0=0; int k1=0; int k2=0; int linterm=0; int np=0; double q=0; int layers=0; double s1=0; double s2=0; double gstep=0; CRowDouble point; CMatrixDouble gp; CRowDouble x; CRowDouble y; CMatrixDouble gy; CRowDouble gpgx0; CRowDouble gpgx1; CRowDouble gpgx2; CRowDouble gcy; int pass=0; int i=0; int j=0; int k=0; int l=0; int fidx=0; double r0=0; int margin=0; int gridsize=0; double threshold=0; double v=0; //--- Test that RBF model with sufficient layers will exactly reproduce //--- target function. for(pass=0; pass<=passcount-1; pass++) { //--- prepare test problem k0=6+CMath::RandomInteger(3); k1=6+CMath::RandomInteger(3); k2=6+CMath::RandomInteger(3); s1=MathPow(range,CMath::RandomInteger(3)-1); s2=MathPow(range,CMath::RandomInteger(3)-1); nx=CMath::RandomInteger(2)+2; ny=CMath::RandomInteger(2)+1; linterm=CMath::RandomInteger(3)+1; layers=5; gstep=s1/6; q=s1; //--- Create RBF structure and auxiliary structures x=vector::Zeros(nx); y=vector::Zeros(ny); point=vector::Zeros(nx); CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetAlgoMultilayer(s,q,layers,0.0); if(linterm==1) CRBF::RBFSetLinTerm(s); if(linterm==2) CRBF::RBFSetConstTerm(s); if(linterm==3) CRBF::RBFSetZeroTerm(s); for(i=0; i::Zeros(np,nx+ny); //--- create grid for(i=0; i::Zeros(k0); gpgx1=vector::Zeros(k1); for(i=0; i(double)(s2*eps),"testrbfunit.ap:2366"); } for(i=0; i(s2*eps),"testrbfunit.ap:2394"); } } //--- 3-dimensional test problems if(nx==3) { np=k0*k1*k2; gp=matrix::Zeros(np,nx+ny); //--- create grid, build model gpgx0=vector::Zeros(k0); gpgx1=vector::Zeros(k1); gpgx2=vector::Zeros(k2); for(i=0; i(s2*eps),"testrbfunit.ap:2462"); } //--- Test RBFGridCalc3V vs RBFCalc() CRBF::RBFGridCalc3V(s,gpgx0,k0,gpgx1,k1,gpgx2,k2,gcy); for(i=0; i(1.0E-9*s2),"testrbfunit.ap:2479"); } } } } //--- Test smoothing properties of RBF model: model with just one layer //--- and large initial radius will produce "average" value of neighbors. //--- In order to test it we create regular grid, fill it with regular //--- +1/-1 pattern, and test model values in the inner points. Model //--- values should be around zero (we use handcrafted threshold to test //--- it). Radius is chosen to be several times larger than grid step. //--- We perform test for 2D model, because same behavior is expected //--- regardless of dimensionality. r0=3; margin=5; gridsize=2*margin+20; threshold=0.1; nx=2; ny=1; CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetAlgoMultilayer(s,r0,1,0.0); CRBF::RBFSetZeroTerm(s); gp=matrix::Zeros(gridsize*gridsize,nx+ny); for(i=0; ithreshold,"testrbfunit.ap:2526"); //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing basic functionality of RBF module on regular| //| grids with multi-layer algorithm in 2-3D. | //+------------------------------------------------------------------+ void CTestRBFUnit::GridCalc23Test(bool &errorflag) { //--- create variables CRBFModel s; CRBFReport rep; CHighQualityRandState rs; int pass=0; int i=0; int j=0; int k=0; int l=0; double perturbation=0; CRowInt kx; double sx=0; double sy=0; int nx=0; int ny=0; int linterm=0; int layers=0; int npoints=0; CMatrixDouble xy; bool gf[]; bool rf[]; CRowDouble x0; CRowDouble x1; CRowDouble x2; CRowDouble gy; CRowDouble gy2; CRowDouble x; CRowDouble y; double sparsity=0; CHighQualityRand::HQRndRandomize(rs); for(pass=0; pass<=24; pass++) { //--- prepare test problem kx.Resize(3); for(i=0; i<=2; i++) { //--- 66% of cases - large grid if(CHighQualityRand::HQRndUniformI(rs,3)==0) { kx.Set(i,(int)MathRound(10*MathPow(10,CHighQualityRand::HQRndUniformR(rs)))); continue; } //--- 33% of cases - small grid k=CHighQualityRand::HQRndUniformI(rs,3); if(k==0) kx.Set(i,1); if(k==1) kx.Set(i,2); if(k==2) kx.Set(i,10); } sx=MathPow(10,CHighQualityRand::HQRndUniformI(rs,3)-1); sy=MathPow(10,CHighQualityRand::HQRndUniformI(rs,3)-1); nx=3; ny=1+CHighQualityRand::HQRndUniformI(rs,5); linterm=CHighQualityRand::HQRndUniformI(rs,3)+1; layers=CHighQualityRand::HQRndUniformI(rs,3)+1; npoints=100; xy=matrix::Zeros(npoints,nx+ny); for(i=0; i::Zeros(kx[0]); for(i=0; i::Zeros(kx[1]); for(i=0; i::Zeros(kx[2]); for(i=0; i::Zeros(nx); y=vector::Zeros(ny); gy.Resize(0); CRBF::RBFGridCalc3V(s,x0,kx[0],x1,kx[1],x2,kx[2],gy); for(i=0; i(double)(1.0E-9*sy),"testrbfunit.ap:2665"); } } //--- Test calculation on subset of regular grid: //--- * select sparsity coefficient (from 1.0 to 0.001) //--- * fill bitmap array //--- * Test 1: compare full and subset versions //--- * Test 2: check sparsity. Subset function may perform additional //--- evaluations because it processes data micro-row by micro-row. //--- So, we can't check that all elements which were not flagged //--- are zero - some of them may become non-zero. However, if entire //--- row is empty, we can reasonably expect (informal guarantee) //--- that it is not processed. So, we check empty (completely //--- unflagged) rows sparsity=MathPow(10,-CHighQualityRand::HQRndUniformI(rs,4)); ArrayResize(gf,kx[0]*kx[1]*kx[2]); ArrayResize(rf,kx[1]*kx[2]); ArrayInitialize(rf,false); for(i=0; i<=kx[0]*kx[1]*kx[2]-1; i++) { gf[i]=CHighQualityRand::HQRndUniformR(rs)::Zeros(nx); y=vector::Zeros(ny); CRBF::RBFGridCalc3VSubset(s,x0,kx[0],x1,kx[1],x2,kx[2],gf,gy); CRBF::RBFGridCalc3V(s,x0,kx[0],x1,kx[1],x2,kx[2],gy2); for(i=0; i<=ny*kx[0]*kx[1]*kx[2]-1; i++) { CAp::SetErrorFlag(errorflag,gf[i/ny] && MathAbs(gy[i]-gy2[i])>(1.0E-9*sy),"testrbfunit.ap:2701"); CAp::SetErrorFlag(errorflag,!rf[i/(ny*kx[0])] && gy[i]!=0.0,"testrbfunit.ap:2702"); } } } //+------------------------------------------------------------------+ //| Function for testing basic functionality of XRBF | //+------------------------------------------------------------------+ bool CTestRBFUnit::BasicXRBFTest(void) { //--- create variables bool result=false; CRBFModel s; CRBFModel s2; CRBFReport rep; CRBFCalcBuffer tsbuf; int algoidx=0; int nx=0; int ny=0; int nduplicate=0; double mx=0; double errtol=0; double meanseparation=0; double lambdav=0; CMatrixDouble xy; CMatrixDouble xytest; CMatrixDouble uxwr; CMatrixDouble uv; CMatrixDouble xy2; CRowDouble x; CRowDouble y; CRowDouble y2; CRowDouble xzero; CRowDouble yref; CRowDouble scalevec; bool bflags[]; int n=0; int ntest=0; int gridsize=0; int i=0; int j=0; int k=0; double delta=0; double refrms=0; double refmax=0; double v=0; int functype=0; int densitytype=0; double width=0; double lowprec=0; double highprec=0; int shaketype=0; double maxerr=0; bool fractionalerror; int unx=0; int uny=0; int unc=0; int modelversion=0; bool hasscale; int nondistinctcnt=0; int termtype=0; int leadingzeros=0; double avx2_epsilon=1e-14; CHighQualityRandState rs; CHighQualityRand::HQRndRandomize(rs); //--- First test - random problem, ability to build model //--- which reproduces function value in all points with //--- good precision. //--- All dataset points are located in unit cube on //--- regular grid. We do not use smoothing for this test. //--- We use/test following functions: //--- * RBFCalc() //--- * RBFCalc2() //--- * RBFCalc3() //--- * RBFCalcBuf() //--- * RBFTsCalcBuf() errtol=1.0E-5; for(algoidx=0; algoidx::Zeros(nx); y=vector::Zeros(ny); for(i=0; i0.5) yref=vector::Zeros(ny+1); else yref.Resize(0); CRBF::RBFCalc(s,x,yref); CAp::SetErrorFlag(result,CAp::Len(yref)!=ny,"testrbfunit.ap:2828"); if(result) return(result); for(j=0; j(double)(errtol*n),"testrbfunit.ap:2832"); if(nx==1 && ny==1) { CRBF::RBFCalc(s,xzero,y); CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,x[0])!=yref[0],"testrbfunit.ap:2838"); } else CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,1.2)!=0.0,"testrbfunit.ap:2841"); if(nx==2 && ny==1) { CRBF::RBFCalc(s,xzero,y); CAp::SetErrorFlag(result,MathAbs(CRBF::RBFCalc2(s,x[0],x[1])-yref[0])>avx2_epsilon,"testrbfunit.ap:2847"); } else CAp::SetErrorFlag(result,CRBF::RBFCalc2(s,1.2,3.4)!=0.0,"testrbfunit.ap:2850"); if(nx==3 && ny==1) { CRBF::RBFCalc(s,xzero,y); CAp::SetErrorFlag(result,MathAbs(CRBF::RBFCalc3(s,x[0],x[1],x[2])-yref[0])>avx2_epsilon,"testrbfunit.ap:2856"); } else CAp::SetErrorFlag(result,CRBF::RBFCalc3(s,1.2,3.4,5.6)!=0.0,"testrbfunit.ap:2859"); CRBF::RBFCalc(s,xzero,y); if(CMath::RandomReal()>0.5) { y=vector::Zeros(ny+1); CRBF::RBFCalcBuf(s,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:2867"); } else { y=vector::Zeros(ny-1); CRBF::RBFCalcBuf(s,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2873"); } for(j=0; j0.5) { y=vector::Zeros(ny+1); CRBF::RBFTSCalcBuf(s,tsbuf,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:2884"); } else { y=vector::Zeros(ny-1); CRBF::RBFTSCalcBuf(s,tsbuf,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2890"); } for(j=0; j0) { if(CHighQualityRand::HQRndNormal(rs)>0.0) { CAblasF::RAllocV(nx,scalevec); for(i=0; ierrtol,"testrbfunit.ap:2941"); } } } } } //--- Test ability to handle datasets with non-distinct points: //--- * the model correctly predicts mean value at non-distinct points //--- * Rep.RMSError and Rep.MaxError are correctly computed errtol=1.0E-4; for(algoidx=0; algoidx0.0) { CAblasF::RAllocV(nx,scalevec); for(i=0; ierrtol,"testrbfunit.ap:3000"); refrms=refrms+CMath::Sqr(y[j]-xy2.Get(2*i,nx+j))+CMath::Sqr(y[j]-xy2.Get(2*i+1,nx+j)); refmax=CApServ::RMaxAbs3(refmax,y[j]-xy2.Get(2*i,nx+j),y[j]-xy2.Get(2*i+1,nx+j)); } } refrms=MathSqrt(refrms/(2*n*ny)); CAp::SetErrorFlag(result,MathAbs(refrms-rep.m_rmserror)>errtol,"testrbfunit.ap:3006"); CAp::SetErrorFlag(result,MathAbs(refmax-rep.m_maxerror)>errtol,"testrbfunit.ap:3007"); } } } //--- Test ability of smoothing RBFs to handle datasets with nearly (but not exactly!) non-distinct points: //--- * the model correctly predicts mean value at these points //--- * Rep.RMSError and Rep.MaxError are correctly computed //--- NOTE: at least NX+1 points are required for this test errtol=0.20; lambdav=1.0E-4; for(algoidx=0; algoidx<=m_xrbfsmoothingalgocnt-1; algoidx++) { for(nx=1; nx<=4; nx++) { for(ny=1; ny<=2; ny++) { n=1+CHighQualityRand::HQRndUniformI(rs,20); n=MathMax(n,(int)MathRound(MathPow(2,nx))+1); GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation); CAblasF::RAllocM(2*n,nx+ny,xy2); for(i=0; i0.0) { CAblasF::RAllocV(nx,scalevec); for(i=0; ierrtol,"testrbfunit.ap:3070"); CAblasF::RCopyRV(nx,xy2,2*i+1,x); CRBF::RBFCalc(s,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3073"); if(result) return(result); for(j=0; jerrtol,"testrbfunit.ap:3077"); } refrms=0; refmax=0; for(i=0; i<=2*n-1; i++) { CAblasF::RAllocV(nx,x); CAblasF::RCopyRV(nx,xy2,i,x); CRBF::RBFCalc(s,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3086"); if(result) return(result); for(j=0; j(1.0E-5),"testrbfunit.ap:3096"); CAp::SetErrorFlag(result,MathAbs(refmax-rep.m_maxerror)>(1.0E-5),"testrbfunit.ap:3097"); } } } //--- Test ability to handle datasets consisting of several isolated "islands", //--- far away from each other errtol=1.0E-3; for(algoidx=0; algoidxerrtol) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3134"); return(result); } v=0; for(i=0; i<=nduplicate-1; i++) { CAblasF::RCopyRV(nx,xy,i,x); CRBF::RBFCalc(s,x,y); for(j=0; jerrtol,"testrbfunit.ap:3146"); } } //--- Test ability to handle degenerate problems: //--- * we generate dataset with all points being well separated, except for a few //--- that have neighbors that are close enough to ruin the multiquadric algorithm //--- but not close enough to be merged //--- * we test that the model that was built actually uses Reg-QR //--- * we also test that small and easy model without degeneracies does not use Reg-QR //--- The idea of this test is to check that the solver signals about bad condition numbers //--- by setting DbgRegQRUsedForDDM field. for(nx=1; nx<=2; nx++) { //--- Solve easy problem, test that Reg-QR solver was NOT used ny=1+CHighQualityRand::HQRndUniformI(rs,3); n=5; GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation); CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetPoints(s,xy,n); CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0); CRBF::RBFBuildModel(s,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3174"); return(result); } CAp::SetErrorFlag(result,s.m_model3.m_dbgregqrusedforddm,"testrbfunit.ap:3177"); CAp::SetErrorFlag(result,rep.m_rmserror>0.001,"testrbfunit.ap:3178"); //--- Solve badly conditioned problem, test that Reg-QR solver was used ny=1+CHighQualityRand::HQRndUniformI(rs,3); n=100; nduplicate=100; GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation); CApServ::RMatrixGrowRowsTo(xy,n+nduplicate,nx+ny); for(i=0; i<=nduplicate-1; i++) { CAblasF::RCopyRR(nx+ny,xy,CHighQualityRand::HQRndUniformI(rs,n),xy,n+i); k=CHighQualityRand::HQRndUniformI(rs,nx); xy.Add(n+i,k,0.00001*CHighQualityRand::HQRndNormal(rs)); } CRBF::RBFCreate(nx,ny,s); CRBF::RBFSetPoints(s,xy,n+nduplicate); CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0); CRBF::RBFBuildModel(s,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3200"); return(result); } CAp::SetErrorFlag(result,!s.m_model3.m_dbgregqrusedforddm,"testrbfunit.ap:3203"); } //--- Test that various polynomial term types are actually handled: //---*we generate "good random" problem //--- * try various term types: linear, constant, zero //--- * directly access model term and check that corresponding entries are zero errtol=1.0E-3; for(algoidx=0; algoidx0.0) { CAblasF::RAllocV(nx,scalevec); for(i=0; i0.0) CRBF::RBFSetLinTerm(s); leadingzeros=0; } if(termtype==1) { CRBF::RBFSetConstTerm(s); leadingzeros=nx; } if(termtype==2) { CRBF::RBFSetZeroTerm(s); leadingzeros=nx+1; } CRBF::RBFBuildModel(s,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3250"); return(result); } if((double)(rep.m_maxerror)>errtol) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3255"); return(result); } for(i=0; ierrtol,"testrbfunit.ap:3265"); } CAp::SetErrorFlag(result,s.m_modelversion!=3,"testrbfunit.ap:3267"); CAp::SetErrorFlag(result,CAp::Rows(s.m_model3.m_v)::Zeros(n,nx+ny); CAp::Assert(gridsize>1); CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx))); for(i=0; i0.0; if(hasscale) { scalevec=vector::Zeros(nx); for(j=0; j0) { CApServ::RMatrixGrowRowsTo(xy,n+nondistinctcnt,nx+ny); for(i=n; i<=n+nondistinctcnt-1; i++) CAblasF::RCopyRR(nx+ny,xy,CHighQualityRand::HQRndUniformI(rs,n),xy,i); n+=nondistinctcnt; } //--- Build model: either build directly in S, or build in S2 and serialize/unserialize it to S. if(CHighQualityRand::HQRndNormal(rs)>0.0) { //--- Build in S directly CRBF::RBFCreate(nx,ny,s); SetXRBFAlgorithmExact(s,algoidx,meanseparation); if(hasscale) CRBF::RBFSetPointsAndScales(s,xy,n,scalevec); else CRBF::RBFSetPoints(s,xy,n); CRBF::RBFBuildModel(s,rep); } else { //--- Build in S2, copy via serialization/unserialization. //--- This branch tests our ability to correctly save the model state. CRBF::RBFCreate(nx,ny,s2); SetXRBFAlgorithmExact(s2,algoidx,meanseparation); if(hasscale) CRBF::RBFSetPointsAndScales(s2,xy,n,scalevec); else CRBF::RBFSetPoints(s2,xy,n); CRBF::RBFBuildModel(s2,rep); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CRBF::RBFAlloc(_local_serializer,s2); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CRBF::RBFSerialize(_local_serializer,s2); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CRBF::RBFUnserialize(_local_serializer,s); _local_serializer.Stop(); } } if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3358"); return(result); } //--- Test RBFUnpack() CRBF::RBFUnpack(s,unx,uny,uxwr,unc,uv,modelversion); if(modelversion!=3) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3368"); return(result); } if((unx!=nx || uny!=ny) || unc!=n-nondistinctcnt) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3373"); return(result); } if(CAp::Cols(uv)!=nx+1 || CAp::Rows(uv)!=ny) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3378"); return(result); } if(CAp::Cols(uxwr)!=nx+ny+nx+3 || CAp::Rows(uxwr)!=unc) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:3383"); return(result); } CAblasF::BSetAllocV(n,false,bflags); for(i=0; i<=unc-1; i++) { //--- Test center information stored in the model: //--- * all center indexes are in [0,N) //--- * all center indexes are distinct //--- * compare centers with dataset points CAp::SetErrorFlag(result,uxwr.Get(i,nx+ny+nx+2)<0.0,"testrbfunit.ap:3395"); CAp::SetErrorFlag(result,uxwr.Get(i,nx+ny+nx+2)>(n-1),"testrbfunit.ap:3396"); k=(int)MathRound(uxwr.Get(i,nx+ny+nx+2)); if(k>=0 && k(1.0E-9),"testrbfunit.ap:3403"); } } x=vector::Zeros(nx); y=vector::Zeros(ny); for(i=0; i<=9; i++) { for(j=0; j(double)(1.0E-9*mx),"testrbfunit.ap:3469"); } } } } //--- Test that smooth 1D function is reproduced (between nodes) //--- with good precision. We test two model types: model with //--- three layers and moderate initial radius, and model with //--- large initial radius and large number of layers. //--- This test: //--- * generates test function on [-Width,+Width]. Two sets of //--- nodes are generated-"model" ones and "test" ones. //---*builds RBF model using "model" dataset //---*test model using "test" dataset. Test points are more //--- dense and are spread in [-0.9*Width, +0.9*Width] (reduced //--- interval is used because RBF models are too bad near the //--- boundaries). //--- NOTE: we calculate maximum error for given function type //--- and grid density over all modifications of the task, //--- and only after that we perform comparison with tolerance //--- level. It allows easier debugging. for(functype=0; functype<=2; functype++) { for(densitytype=0; densitytype<=1; densitytype++) { //--- Select tolerance lowprec=-999999; highprec=-999999; if(functype==0) { lowprec=1.0E-2; highprec=1.0E-3; } else { if(functype==1) { lowprec=1.0E-1; highprec=1.0E-2; } else { if(functype==2) { lowprec=1.0E-3; highprec=1.0E-4; } else { CAp::Assert(false); return(true); } } } if(densitytype==0) errtol=lowprec; else errtol=highprec; //--- Test maxerr=0; for(shaketype=0; shaketype<=1; shaketype++) { //--- Generate grid width=1; fractionalerror=false; if(functype==0) { //--- sin(x) on [-2*pi,+2*pi] n=20*(int)MathRound(MathPow(4,densitytype)); width=M_PI; fractionalerror=false; } else { if(functype==1) { //--- exp(x) on [-3,+3] n=50*(int)MathRound(MathPow(4,densitytype)); width=3; fractionalerror=true; } else { if(functype==2) { //--- 1/(1+x^2) on [-3,+3] n=50*(int)MathRound(MathPow(4,densitytype)); width=3; fractionalerror=false; } else { CAp::Assert(false); return(true); } } } xy=matrix::Zeros(n,2); for(i=0; i::Zeros(ntest,2); for(i=0; i::Zeros(1); for(i=0; ierrtol,"testrbfunit.ap:3646"); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing scaling - related functionality of XRBF | //| Returns True on failure(error flag is set). | //+------------------------------------------------------------------+ bool CTestRBFUnit::ScaledXRBFTest(void) { //--- create variables bool result=false; CRBFModel s; CRBFModel s2; CRBFReport rep; CRBFCalcBuffer tsbuf; int nx=0; int ny=0; double nodetol=0; double randtol=0; CMatrixDouble xy; CMatrixDouble xy2; CRowDouble x; CRowDouble y; CRowDouble y2; CRowDouble xzero; CRowDouble yref; CRowDouble scalex; CRowDouble scaley; CRowDouble c0; CRowDouble c1; int n=0; int gridsize=0; int i=0; int j=0; int k=0; CHighQualityRandState rs; CHighQualityRand::HQRndRandomize(rs); //--- First test - random problem, test that using scaling //--- does not change model significantly (except for //--- rounding-related errors). //--- We also apply scaling to Y, in order to test that it //--- is correctly handled too. for(nx=1; nx<=2; nx++) { for(ny=1; ny<=2; ny++) { //--- problem setup nodetol=0.001; randtol=0.010; scalex=vector::Zeros(nx); for(i=0; i::Zeros(ny); for(i=0; i::Zeros(n,nx+ny); if(!CAp::Assert(gridsize>1)) return(true); if(!CAp::Assert(n==MathPow(gridsize,nx))) return(true); c0=vector::Zeros(nx); for(j=0; j::Zeros(ny); for(j=0; j::Zeros(n,nx+ny); for(i=0; i::Zeros(nx); for(i=0; inodetol,"testrbfunit.ap:3777"); } //--- Compare model values in random points x=vector::Zeros(nx); for(i=0; irandtol,"testrbfunit.ap:3793"); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Test gridded evaluation of XRBFs. | //| Returns True on errors. | //+------------------------------------------------------------------+ bool CTestRBFUnit::GridXRBFTest(void) { //--- create variables bool result=false; CRBFModel s; CRBFReport rep; int nx=0; int ny=0; bool hasscale; double errtol=0; int n=0; CMatrixDouble xy; CMatrixDouble xy2; CMatrixDouble y2; CRowDouble x0; CRowDouble x1; CRowDouble x2; CRowDouble x02; CRowDouble x12; CRowDouble x22; CRowDouble scalevec; CRowDouble scalevec2; bool needy[]; bool rowflags[]; int n0=0; int n1=0; int n2=0; int nkind=0; double v=0; int i=0; int j=0; int k=0; int i0=0; int i1=0; int i2=0; CRowDouble x; CRowDouble y; CRowDouble yv; CRowDouble yv2; //--- Test 2-dimensional grid calculation errtol=1.0E-12; nx=2; for(ny=1; ny<=4; ny++) { for(nkind=0; nkind<=2; nkind++) { //--- problem setup n=150; xy=matrix::Zeros(n,nx+ny); for(i=0; i::Zeros(nx); for(j=0; j::Zeros(n0); x0.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(n1); x1.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(nx); CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv); for(i0=0; i0errtol,"testrbfunit.ap:3918"); } } //--- Test calculation on subset of regular grid: //--- * Test 1: compare full and subset versions //--- * Test 2: check sparsity. Subset function may perform additional //--- evaluations because it processes data micro-row by micro-row. //--- So, we can't check that all elements which were not flagged //--- are zero - some of them may become non-zero. However, if entire //--- row is empty, we can reasonably expect (informal guarantee) //--- that it is not processed. So, we check empty (completely //--- unflagged) rows CRBF::RBFGridCalc2VSubset(s,x0,n0,x1,n1,needy,yv2); for(i=0; i<=ny*n0*n1-1; i++) CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:3937"); //--- Test legacy function CRBF::RBFGridCalc2(s,x0,n0,x1,n1,y2); for(i=0; ierrtol,"testrbfunit.ap:3947"); else CAp::SetErrorFlag(result,y2.Get(i%n0,i/n0)!=0.0,"testrbfunit.ap:3949"); } } } //--- Test 3-dimensional grid calculation errtol=1.0E-12; nx=3; for(ny=1; ny<=4; ny++) { for(nkind=0; nkind<=2; nkind++) { //--- problem setup n=150; xy=matrix::Zeros(n,nx+ny); for(i=0; i::Zeros(nx); for(j=0; j::Zeros(n0); x0.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(n1); x1.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(n2); x2.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(nx); CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv); for(i0=0; i0errtol,"testrbfunit.ap:4052"); } } //--- Test calculation on subset of regular grid: //--- * Test 1: compare full and subset versions //--- * Test 2: check sparsity. Subset function may perform additional //--- evaluations because it processes data micro-row by micro-row. //--- So, we can't check that all elements which were not flagged //--- are zero - some of them may become non-zero. However, if entire //--- row is empty, we can reasonably expect (informal guarantee) //--- that it is not processed. So, we check empty (completely //--- unflagged) rows ArrayResize(rowflags,n1*n2); for(i=0; i<=n1*n2-1; i++) rowflags[i]=false; for(i=0; i<=n0*n1*n2-1; i++) { if(needy[i]) rowflags[i/n0]=true; } CRBF::RBFGridCalc3VSubset(s,x0,n0,x1,n1,x2,n2,needy,yv2); for(i=0; ierrtol,"testrbfunit.ap:4077"); CAp::SetErrorFlag(result,!rowflags[i/(ny*n0)] && yv2[i]!=0.0,"testrbfunit.ap:4078"); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing basic functionality of RBF module with | //| hierarchical algorithm. | //+------------------------------------------------------------------+ bool CTestRBFUnit::BasicHRBFTest(void) { //--- create variables bool result=false; CRBFModel s; CRBFModel s2; CRBFReport rep; CRBFCalcBuffer tsbuf; int nx=0; int ny=0; int linterm=0; int bf=0; double rbase=0; int nlayers=0; double errtol=0; double scalefactor=0; CMatrixDouble xy; CMatrixDouble xytest; CMatrixDouble uxwr; CMatrixDouble uv; CMatrixDouble xy2; CRowDouble x; CRowDouble y; CRowDouble y2; CRowDouble xzero; CRowDouble yref; CRowDouble scalevec; int n=0; int ntest=0; int gridsize=0; int i=0; int j=0; int k=0; double r0=0; int margin=0; double threshold=0; double v=0; int functype=0; int densitytype=0; double width=0; double lowprec=0; double highprec=0; int modeltype=0; int shaketype=0; double maxerr=0; bool fractionalerror; int unx=0; int uny=0; int unc=0; int modelversion=0; bool hasscale; CHighQualityRandState rs; CHighQualityRand::HQRndRandomize(rs); //--- First test - random problem, ability to build model //--- which reproduces function value in all points with //--- good precision. //--- We also test properties of the linear term - that //--- model value in far away points is either constant //--- or exactly zero (for corresponding kinds of linear //--- term). //--- All dataset points are located in unit cube on //--- regular grid. We do not use smoothing for this test. //--- We use/test following functions: //--- * RBFCalc() //--- * RBFCalc2() //--- * RBFCalc3() //--- * RBFCalcBuf() //--- * RBFTsCalcBuf() errtol=1.0E-6; for(nx=1; nx<=4; nx++) { //--- problem setup n=150; rbase=3.0; nlayers=10; gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1; linterm=1+CMath::RandomInteger(3); ny=1+CMath::RandomInteger(2); bf=CMath::RandomInteger(2); n=(int)MathRound(MathPow(gridsize,nx)); xy=matrix::Zeros(n,nx+ny); if(!CAp::Assert(gridsize>1)) return(true); if(!CAp::Assert(n==MathPow(gridsize,nx))) return(true); for(i=0; i::Zeros(nx); xzero=vector::Zeros(nx); y=vector::Zeros(ny); for(j=0; j0.5) yref=vector::Zeros(ny+1); CRBF::RBFCalc(s,x,yref); for(j=0; jerrtol,"testrbfunit.ap:4226"); CAp::SetErrorFlag(result,CAp::Len(yref)!=ny,"testrbfunit.ap:4227"); if(nx==1 && ny==1) { CRBF::RBFCalc(s,xzero,y); CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,x[0])!=yref[0],"testrbfunit.ap:4233"); } if(nx==2 && ny==1) { CRBF::RBFCalc(s,xzero,y); CAp::SetErrorFlag(result,CRBF::RBFCalc2(s,x[0],x[1])!=yref[0],"testrbfunit.ap:4240"); } if(nx==3 && ny==1) { CRBF::RBFCalc(s,xzero,y); CAp::SetErrorFlag(result,CRBF::RBFCalc3(s,x[0],x[1],x[2])!=yref[0],"testrbfunit.ap:4247"); } CRBF::RBFCalc(s,xzero,y); if(CMath::RandomReal()>0.5) { y=vector::Zeros(ny+1); CRBF::RBFCalcBuf(s,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:4256"); } else { y=vector::Zeros(ny-1); CRBF::RBFCalcBuf(s,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:4262"); } for(j=0; j0.5) { y=vector::Zeros(ny+1); CRBF::RBFTSCalcBuf(s,tsbuf,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:4273"); } else { y=vector::Zeros(ny-1); CRBF::RBFTSCalcBuf(s,tsbuf,x,y); CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:4279"); } for(j=0; j::Zeros(nx); if(linterm==2) { for(j=0; j0.5) x.Set(j,gridsize+1000*rbase); else x.Set(j,0-1000*rbase); } CRBF::RBFCalc(s,x,y); for(j=0; j0.5) x.Set(j,gridsize+1000*rbase); else x.Set(j,0-1000*rbase); } CRBF::RBFCalc(s,x,y2); for(j=0; j0.5) x.Set(j,gridsize+1000*rbase); else x.Set(j,0-1000*rbase); } CRBF::RBFCalc(s,x,y); for(j=0; j::Zeros(n,nx+ny); if(!CAp::Assert(gridsize>1)) return(true); if(!CAp::Assert(n==MathPow(gridsize,nx))) return(true); for(i=0; i::Zeros(nx); if(hasscale) { for(j=0; j::Zeros(nx); y=vector::Zeros(ny); for(i=0; i<=9; i++) { for(j=0; j(1.0E-9),"testrbfunit.ap:4446"); } } } //--- Test that smooth 1D function is reproduced (between nodes) //--- with good precision. We test two model types: model with //--- three layers and moderate initial radius, and model with //--- large initial radius and large number of layers. //--- This test: //--- * generates test function on [-Width,+Width]. Two sets of //--- nodes are generated-"model" ones and "test" ones. //---*builds RBF model using "model" dataset //---*test model using "test" dataset. Test points are more //--- dense and are spread in [-0.9*Width, +0.9*Width] (reduced //--- interval is used because RBF models are too bad near the //--- boundaries). //--- NOTE: we calculate maximum error for given function type //--- and grid density over all modifications of the task, //--- and only after that we perform comparison with tolerance //--- level. It allows easier debugging. for(functype=0; functype<=2; functype++) { for(densitytype=0; densitytype<=1; densitytype++) { //--- Select tolerance lowprec=-999999; highprec=-999999; switch(functype) { case 0: lowprec=1.0E-2; highprec=1.0E-3; break; case 1: lowprec=1.0E-1; highprec=1.0E-2; break; case 2: lowprec=1.0E-3; highprec=1.0E-4; break; default: CAp::Assert(false); return(true); } if(densitytype==0) errtol=lowprec; else errtol=highprec; //--- Test maxerr=0; for(modeltype=0; modeltype<=1; modeltype++) { for(shaketype=0; shaketype<=1; shaketype++) { //--- Generate grid width=1; fractionalerror=false; switch(functype) { case 0: //--- sin(x) on [-2*pi,+2*pi] n=17*(int)MathRound(MathPow(4,densitytype)); width=M_PI; fractionalerror=false; break; case 1: //--- exp(x) on [-3,+3] n=50*(int)MathRound(MathPow(4,densitytype)); width=3; fractionalerror=true; break; case 2: //--- 1/(1+x^2) on [-3,+3] n=20*(int)MathRound(MathPow(4,densitytype)); width=3; fractionalerror=false; break; default: CAp::Assert(false); return(true); } xy=matrix::Zeros(n,2); for(i=0; i::Zeros(ntest,2); for(i=0; i::Zeros(1); for(i=0; ierrtol,"testrbfunit.ap:4642"); } } //--- Scaling test - random problem, we test that after //--- scaling of all variables and radius by 2^K (for some K) //--- we will get exactly same results (up to the last bit of //--- mantissa). //--- It is very strong requirement for algorithm stability, //--- but it is satisfiable in most implementations of RBFs, //--- because all operations involving spatial values are usually //--- followed by division by radius, and using multiplier which //--- is exactly power of 2 results in no changes in numbers //--- being returned. //--- It allows to test different scale-related bugs //--- (say, situation when deep in kd-tree search code we compare //--- against R instead of R^2). //--- All dataset points are located in unit cube on //--- regular grid. //--- We do not use smoothing for this test. for(nx=1; nx<=4; nx++) { //--- problem setup n=150; rbase=0.33; nlayers=CMath::RandomInteger(4); scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1); gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1; ny=1+CMath::RandomInteger(3); linterm=1+CMath::RandomInteger(3); bf=CMath::RandomInteger(2); n=(int)MathRound(MathPow(gridsize,nx)); xy=matrix::Zeros(n,nx+ny); xy2=matrix::Zeros(n,nx+ny); if(!CAp::Assert(gridsize>1)) return(true); if(!CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx)))) return(true); for(i=0; i::Zeros(nx); y=vector::Zeros(ny); y2=vector::Zeros(ny); for(i=0; i::Zeros(gridsize*gridsize,nx+ny); for(i=0; ithreshold,"testrbfunit.ap:4805"); } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing scaling - related functionality of RBF | //| module with hierarchical algorithm. | //| Returns True on failure(error flag is set). | //+------------------------------------------------------------------+ bool CTestRBFUnit::ScaleHRBFTtest(void) { //--- create variables bool result=false; CRBFModel s; CRBFModel s2; CRBFReport rep; CRBFCalcBuffer tsbuf; int nx=0; int ny=0; int linterm=0; int bf=0; double rbase=0; int nlayers=0; double errtol=0; CMatrixDouble xy; CMatrixDouble xy2; CRowDouble x; CRowDouble y; CRowDouble y2; CRowDouble xzero; CRowDouble yref; CRowDouble scalex; CRowDouble scaley; CRowDouble c0; CRowDouble c1; int n=0; int gridsize=0; int i=0; int j=0; int k=0; int strictness=0; double lambdav=0; //--- First test - random problem, test that using scaling //--- does not change model significantly (except for //--- rounding-related errors). //--- We test two kinds of scaling: //---*"strict", which is scaling by some power of 2, and //--- which should result in bit-to-bit equivalence of results //---*"non-strict", which is scaling by random number, and //--- which should result in approximate equivalence //--- We also apply scaling to Y, in order to test that it //--- is correctly handled too. for(strictness=0; strictness<=1; strictness++) { for(nx=1; nx<=2; nx++) { for(ny=1; ny<=2; ny++) { //--- problem setup if(strictness==1) { errtol=0; scalex=vector::Zeros(nx); for(i=0; i::Zeros(ny); for(i=0; i::Zeros(nx); for(i=0; i::Zeros(ny); for(i=0; i::Zeros(n,nx+ny); CAp::Assert(gridsize>1); CAp::Assert((double)(n)==MathPow(gridsize,nx)); c0=vector::Zeros(nx); for(j=0; j::Zeros(ny); for(j=0; j::Zeros(n,nx+ny); for(j=0; j::Zeros(nx); for(i=0; ierrtol,"testrbfunit.ap:4978"+" j="+string(j)+" y[j]="+string(y[j])+" y2[j]="+string(y2[j])+" scaley[j]="+string(scaley[j])+" errtol="+string(errtol)); } //--- Compare model values in random points x=vector::Zeros(nx); for(i=0; ierrtol,"testrbfunit.ap:4994"+" j="+string(j)+" y[j]="+string(y[j])+" y2[j]="+string(y2[j])+" scaley[j]="+string(scaley[j])+" errtol="+string(errtol)); } } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Test special properties of hierarchical RBFs. | //| Returns True on errors. | //+------------------------------------------------------------------+ bool CTestRBFUnit::SpecHRBFTest(void) { //--- create variables bool result=false; int n=0; int nx=0; int ny=0; double rbase=0; CRBFModel s0; CRBFModel s1; CRBFReport rep; int nlayers=0; CMatrixDouble xy; int i=0; int j=0; double vdiff=0; double d2=0; int v2its=0; double vref=0; double vfunc=0; double maxerr=0; CHighQualityRandState rs; CHighQualityRand::HQRndRandomize(rs); //--- Test properties of RBF basis functions - we compare values //--- returned by RBFV2BasisFunc() against analytic expressions //--- which are approximately modeled by RBFV2BasisFunc(). d2=0.0; while(d2<100.0) { vref=MathExp(-d2); vfunc=CRBFV2::RBFV2BasisFunc(0,d2); CAp::SetErrorFlag(result,MathAbs(vref-vfunc)>(1.0E-9*vref),"testrbfunit.ap:5036"); d2+=1.0/64.0; } d2=0.0; maxerr=0.0; while(d2<16.0) { vref=MathMax(1-d2/9,0)*MathExp(-d2); vfunc=CRBFV2::RBFV2BasisFunc(1,d2); maxerr=MathMax(maxerr,MathAbs(vref-vfunc)); CAp::SetErrorFlag(result,MathAbs(vref-vfunc)>0.005,"testrbfunit.ap:5046"); d2+=1.0/64.0; } //--- Test that tiny changes in dataset points introduce tiny //--- numerical noise. The noise magnitude depends on the //--- properties of the linear solver being used. We compare //--- noise magnitude against hard-coded values. //--- Test sequence: //--- * create model #1 //--- * create model #2 using slightly modified dataset nx=2; ny=1; n=20; rbase=2.0; nlayers=1; v2its=50; xy=matrix::Zeros(n*n,nx+ny); for(i=0; i<=n*n-1; i++) { xy.Set(i,0,i%n); xy.Set(i,1,i/n); xy.Set(i,2,MathSin(i)); } CRBF::RBFCreate(nx,ny,s0); CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0); CRBF::RBFSetV2Its(s0,v2its); CRBF::RBFSetPoints(s0,xy,n*n); CRBF::RBFBuildModel(s0,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:5081"); return(result); } for(i=0; i<=n*n-1; i++) { xy.Add(i,0,1.0E-14*MathSin(3*i)); xy.Add(i,1,1.0E-14*MathSin(7*i*i)); } CRBF::RBFCreate(nx,ny,s1); CRBF::RBFSetAlgoHierarchical(s1,rbase,nlayers,0.0); CRBF::RBFSetV2Its(s1,v2its); CRBF::RBFSetPoints(s1,xy,n*n); CRBF::RBFBuildModel(s1,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:5096"); return(result); } vdiff=0; for(i=0; i<=n-2; i++) { for(j=0; j<=n-2; j++) vdiff+=MathAbs(CRBF::RBFCalc2(s0,0.5+i,0.5+j)-CRBF::RBFCalc2(s1,0.5+i,0.5+j))/CMath::Sqr(n-1); } CAp::SetErrorFlag(result,vdiff>0.002 || vdiff<0.00001,"testrbfunit.ap:5103"); //--- Test progress reports: progress before model construction is //--- zero, progress after model is built is 1. More detailed tests //--- are performed in the multithreaded TestHRBFProgress(). nx=2; ny=1; n=20; rbase=1.0; nlayers=3; xy=matrix::Zeros(n,nx+ny); for(i=0; i::Zeros(n,nx+ny); for(i=0; i::Zeros(nx); for(j=0; j::Zeros(n0); x0.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(n1); x1.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(nx); CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv); for(i0=0; i0errtol,"testrbfunit.ap:5287"); } } //--- Test calculation on subset of regular grid: //--- * Test 1: compare full and subset versions //--- * Test 2: check sparsity. Subset function may perform additional //--- evaluations because it processes data micro-row by micro-row. //--- So, we can't check that all elements which were not flagged //--- are zero - some of them may become non-zero. However, if entire //--- row is empty, we can reasonably expect (informal guarantee) //--- that it is not processed. So, we check empty (completely //--- unflagged) rows CRBF::RBFGridCalc2VSubset(s,x0,n0,x1,n1,needy,yv2); for(i=0; ierrtol,"testrbfunit.ap:5306"); //--- Test legacy function CRBF::RBFGridCalc2(s,x0,n0,x1,n1,y2); for(i=0; ierrtol,"testrbfunit.ap:5316"); else CAp::SetErrorFlag(result,y2.Get(i%n0,i/n0)!=0.0,"testrbfunit.ap:5318"); } //--- Test that scaling RBase, XY, X0, X1 by some power of 2 //--- does not change values at grid (quite a strict requirement, but //--- ALGLIB implementation of RBF may deal with it). scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1); xy2=matrix::Zeros(n,nx+ny); x02=vector::Zeros(n0); x12=vector::Zeros(n1); for(i=0; i::Zeros(nx); for(i=0; i(100.0*CMath::m_machineepsilon*CApServ::RMaxAbs3(yv[i],yv2[i],1.0)),"testrbfunit.ap:5386"); } } //--- Test 3-dimensional grid calculation errtol=1.0E-12; nx=3; for(ny=1; ny<=4; ny++) { for(nkind=0; nkind<=2; nkind++) { //--- problem setup n=150; rbase=0.10; lambdav=1.0E-3*CMath::RandomInteger(2); nlayers=CMath::RandomInteger(3); linterm=1+CMath::RandomInteger(3); bf=CMath::RandomInteger(2); xy=matrix::Zeros(n,nx+ny); for(i=0; i::Zeros(nx); for(j=0; j::Zeros(n0); x0.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(n1); x1.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(n2); x2.Set(0,CMath::RandomReal()); for(i=1; i::Zeros(nx); CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv); for(i0=0; i0errtol,"testrbfunit.ap:5500"); } } //--- Test calculation on subset of regular grid: //--- * Test 1: compare full and subset versions //--- * Test 2: check sparsity. Subset function may perform additional //--- evaluations because it processes data micro-row by micro-row. //--- So, we can't check that all elements which were not flagged //--- are zero - some of them may become non-zero. However, if entire //--- row is empty, we can reasonably expect (informal guarantee) //--- that it is not processed. So, we check empty (completely //--- unflagged) rows ArrayResize(rowflags,n1*n2); ArrayInitialize(rowflags,false); for(i=0; i<=n0*n1*n2-1; i++) { if(needy[i]) rowflags[i/n0]=true; } CRBF::RBFGridCalc3VSubset(s,x0,n0,x1,n1,x2,n2,needy,yv2); for(i=0; ierrtol,"testrbfunit.ap:5527"); CAp::SetErrorFlag(result,!rowflags[i/(ny*n0)] && yv2[i]!=0.0,"testrbfunit.ap:5528"); } //--- Test that scaling RBase, XY, X0, X1 and X2 by some power of 2 //--- does not change values at grid (quite a strict requirement, but //--- ALGLIB implementation of RBF may deal with it). scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1); xy2=matrix::Zeros(n,nx+ny); x02=vector::Zeros(n0); x12=vector::Zeros(n1); x22=vector::Zeros(n2); for(i=0; i::Ones(nx); if(hasscale) scalevec2=scalevec; scalevec2/=scalefactor; CRBF::RBFSetPointsAndScales(s,xy,n,scalevec2); CRBF::RBFSetV2BF(s,bf); CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav); CRBF::RBFBuildModel(s,rep); if(rep.m_terminationtype<=0) { CAp::SetErrorFlag(result,true,"testrbfunit.ap:5593"); return(result); } CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv2); for(i=0; i(100.0*CMath::m_machineepsilon*CApServ::RMaxAbs3(yv[i],yv2[i],1.0)),"testrbfunit.ap:5599"); } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Function for testing gradient evaluation | //+------------------------------------------------------------------+ void CTestRBFUnit::GradTest(bool &err) { //--- create variables int algoidx=0; int nkind=0; int n=0; int nx=0; int ny=0; bool hasscale; double errtol=0; double errtol2=0; double stp=0; double stp2=0; CMatrixDouble xy; CRowDouble scalevec; CRowDouble x; CRowDouble x1; CRowDouble yref; CRowDouble y; CRowDouble y1; CRowDouble dy; CRowDouble d2y; CRowDouble dyref; CRowDouble d2yref; CRowDouble dy0; CRowDouble dy1; CRowDouble dy2; CRowDouble dy3; CRowDouble dy4; CRBFModel s; CRBFReport rep; CRBFCalcBuffer tsbuf; int i=0; int j=0; int k=0; int trialidx=0; double v0=0; double v1=0; double v3=0; double v4=0; double df=0; double d2f=0; double vy=0; double vdy0=0; double vdy1=0; double vdy2=0; CHighQualityRandState rs; bool islonger; bool hasgradientatnodes; bool hashessianatnodes; bool trialatnode; double meanseparation=0; CHighQualityRand::HQRndRandomize(rs); //--- Generate good random grid, build model, check derivatives at random points and at grid nodes stp=0.000001; stp2=0.000001; errtol=0.005; errtol2=0.005; for(algoidx=0; algoidx0.0; CRBF::RBFCreate(nx,ny,s); if(n!=0) { GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation); if(hasscale) { scalevec=vector::Zeros(nx); for(i=0; i::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) { CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); trialatnode=true; } else { GenerateSeparatedWithInbox(xy,n,nx,MathMax(100*stp,MathMax(100*stp2,(n>0?0.01/n:0))),rs,x); trialatnode=false; } if(CHighQualityRand::HQRndNormal(rs)>0.0) { yref=vector::Zeros(ny+1); y=vector::Zeros(ny+1); dy=vector::Zeros(ny*nx+1); } CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFCalc(s,x1,yref); CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,y,dy); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5716"); CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testrbfunit.ap:5717"); CAp::SetErrorFlag(err,CAp::Len(dy)!=ny*nx,"testrbfunit.ap:5718"); if(hasgradientatnodes || !trialatnode) { //--- Gradient is well defined, check for(j=0; jerrtol,"testrbfunit.ap:5726"); for(k=0; kerrtol,"testrbfunit.ap:5750"); } } } else { //--- Gradient is undefined at the trial point, zero must be returned CAp::SetErrorFlag(err,CAblasF::RMaxAbsV(ny*nx,dy)!=0.0,"testrbfunit.ap:5760"); } //--- Check RBFDiffBuf vs RBFDiff. x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else CHighQualityRand::HQRndNormalV(rs,nx,x); //--- if(CHighQualityRand::HQRndNormal(rs)>0.0) { yref=vector::Zeros(ny+1); y=vector::Zeros(ny+1); dy=vector::Zeros(ny*nx+1); dyref=vector::Zeros(ny*nx+1); islonger=true; } else { yref.Resize(0); y.Resize(0); dy.Resize(0); dyref.Resize(0); islonger=false; } CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,yref,dyref); CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiffBuf(s,x1,y,dy); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5788"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5789"); CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:5790"); CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:5791"); for(j=0; jerrtol,"testrbfunit.ap:5794"); for(k=0; kerrtol,"testrbfunit.ap:5796"); } //--- Check RBFTsDiffBuf vs RBFDiff. x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else CHighQualityRand::HQRndNormalV(rs,nx,x); //--- if(CHighQualityRand::HQRndNormal(rs)>0.0) { yref=vector::Zeros(ny+1); y=vector::Zeros(ny+1); dy=vector::Zeros(ny*nx+1); dyref=vector::Zeros(ny*nx+1); islonger=true; } else { yref.Resize(0); y.Resize(0); dy.Resize(0); dyref.Resize(0); islonger=false; } CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,yref,dyref); CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFTSDiffBuf(s,tsbuf,x1,y,dy); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5824"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5825"); CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:5826"); CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:5827"); for(j=0; jerrtol,"testrbfunit.ap:5830"); for(k=0; kerrtol,"testrbfunit.ap:5832"); } //--- Check RBFDiff1/RBFDiff2/RBFDiff3 vs RBFDiff. x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else CHighQualityRand::HQRndNormalV(rs,nx,x); if(nx==1 && ny==1) { CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,yref,dyref); CRBF::RBFDiff1(s,x[0],vy,vdy0); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5850"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5851"); CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5852"); CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5853"); } else { CRBF::RBFDiff1(s,1.2,vy,vdy0); CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5858"); CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5859"); } x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else CHighQualityRand::HQRndNormalV(rs,nx,x); if(nx==2 && ny==1) { CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,yref,dyref); CRBF::RBFDiff2(s,x[0],x[1],vy,vdy0,vdy1); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5871"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5872"); CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5873"); CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5874"); CAp::SetErrorFlag(err,MathAbs(vdy1-dyref[1])>errtol,"testrbfunit.ap:5875"); } else { CRBF::RBFDiff2(s,1.2,3.4,vy,vdy0,vdy1); CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5880"); CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5881"); CAp::SetErrorFlag(err,vdy1!=0.0,"testrbfunit.ap:5882"); } x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else CHighQualityRand::HQRndNormalV(rs,nx,x); if(nx==3 && ny==1) { CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,yref,dyref); CRBF::RBFDiff3(s,x[0],x[1],x[2],vy,vdy0,vdy1,vdy2); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5894"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5895"); CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5896"); CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5897"); CAp::SetErrorFlag(err,MathAbs(vdy1-dyref[1])>errtol,"testrbfunit.ap:5898"); CAp::SetErrorFlag(err,MathAbs(vdy2-dyref[2])>errtol,"testrbfunit.ap:5899"); } else { CRBF::RBFDiff3(s,1.2,3.4,5.6,vy,vdy0,vdy1,vdy2); CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5904"); CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5905"); CAp::SetErrorFlag(err,vdy1!=0.0,"testrbfunit.ap:5906"); CAp::SetErrorFlag(err,vdy2!=0.0,"testrbfunit.ap:5907"); } } //--- Check second derivatives - perform N randomized trials for(trialidx=0; trialidx<=n; trialidx++) { //--- Check RBFHess vs numerical derivatives. //--- RBFDiff() is assumed to be already tested and is used to provide exact first derivatives. //--- In this test we have to handle situation when the Hessian is undefined at the trial node. x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) { CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); trialatnode=true; } else { if(n<1) CHighQualityRand::HQRndNormalV(rs,nx,x); else GenerateSeparatedWithInbox(xy,n,nx,MathMax(100*stp,MathMax(100*stp2,0.01/(double)n)),rs,x); trialatnode=false; } if(CHighQualityRand::HQRndNormal(rs)>0.0) { dyref=vector::Zeros(ny+1); yref=vector::Zeros(ny+1); y=vector::Zeros(ny+1); dy=vector::Zeros(ny*nx+1); d2y=vector::Zeros(ny*nx*nx+1); } CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFDiff(s,x1,yref,dyref); CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFHess(s,x1,y,dy,d2y); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5950"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5951"); CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testrbfunit.ap:5952"); CAp::SetErrorFlag(err,CAp::Len(dy)!=ny*nx,"testrbfunit.ap:5953"); CAp::SetErrorFlag(err,CAp::Len(d2y)!=ny*nx*nx,"testrbfunit.ap:5954"); for(k=0; kerrtol,"testrbfunit.ap:5956"); if(hashessianatnodes || !trialatnode) { //--- The Hessian is well defined at the trial point. for(k=0; kerrtol2,"testrbfunit.ap:5986"); } } } else { //--- The Hessian is undefined at the trial point, exact zero must be returned CAp::SetErrorFlag(err,CAblasF::RMaxAbsV(ny*nx*nx,d2y)!=0.0,"testrbfunit.ap:5996"); } //--- Check RBFHessBuf vs RBFHess x=vector::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else if(n<1) CHighQualityRand::HQRndNormalV(rs,nx,x); else GenerateSeparatedWithInbox(xy,n,nx,1.0/(double)n,rs,x); if(CHighQualityRand::HQRndNormal(rs)>0.0) { y=vector::Zeros(ny+1); yref=vector::Zeros(ny+1); dy=vector::Zeros(ny*nx+1); dyref=vector::Zeros(ny*nx+1); d2y=vector::Zeros(ny*nx*nx+1); d2yref=vector::Zeros(ny*nx*nx+1); islonger=true; } else { yref.Resize(0); y.Resize(0); dy.Resize(0); dyref.Resize(0); d2y.Resize(0); d2yref.Resize(0); islonger=false; } CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFHess(s,x1,yref,dyref,d2yref); CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFHessBuf(s,x1,y,dy,d2y); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:6029"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:6030"); CAp::SetErrorFlag(err,CAp::Len(d2yref)!=ny*nx*nx,"testrbfunit.ap:6031"); CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:6032"); CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:6033"); CAp::SetErrorFlag(err,(islonger && CAp::Len(d2y)<=ny*nx*nx) || (!islonger && CAp::Len(d2y)!=ny*nx*nx),"testrbfunit.ap:6034"); for(k=0; k::Zeros(nx); if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0) CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x); else if(n<1) CHighQualityRand::HQRndNormalV(rs,nx,x); else GenerateSeparatedWithInbox(xy,n,nx,1.0/(double)n,rs,x); if(CHighQualityRand::HQRndNormal(rs)>0.0) { y=vector::Zeros(ny+1); yref=vector::Zeros(ny+1); dy=vector::Zeros(ny*nx+1); dyref=vector::Zeros(ny*nx+1); d2y=vector::Zeros(ny*nx*nx+1); d2yref=vector::Zeros(ny*nx*nx+1); islonger=true; } else { islonger=false; y.Resize(0); yref.Resize(0); dy.Resize(0); dyref.Resize(0); d2y.Resize(0); d2yref.Resize(0); } CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFHess(s,x1,yref,dyref,d2yref); CAblasF::RCopyAllocV(nx,x,x1); CRBF::RBFTSHessBuf(s,tsbuf,x1,y,dy,d2y); CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:6071"); CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:6072"); CAp::SetErrorFlag(err,CAp::Len(d2yref)!=ny*nx*nx,"testrbfunit.ap:6073"); CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:6074"); CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:6075"); CAp::SetErrorFlag(err,(islonger && CAp::Len(d2y)<=ny*nx*nx) || (!islonger && CAp::Len(d2y)!=ny*nx*nx),"testrbfunit.ap:6076"); for(k=0; k=MathPow(2,nx),"GenerateNearlyRegularGrid: N<2^NX")) return; meanseparation=1.0; griddim=(int)MathRound(MathPow(n,1.0/(double)nx))+1; xy=matrix::Zeros(n,nx+ny); for(int i=0; i=0,"GenerateGoodRandomGrid: N<1")) return; if(n==0) { meanseparation=1; return; } xy=matrix::Zeros(n,nx+ny); d=1.0/MathPow(n,1.0/(double)nx); for(i=0; i1) { meanseparation=0; for(i=0; i=2 && nx<=3) && !hasscale) CRBF::RBFSetAlgoQNN(s,meanpointsseparation,5.0); else CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0); return; } if(algoidx==1) { if((nx>=2 && nx<=3) && !hasscale) CRBF::RBFSetAlgoMultilayer(s,16*meanpointsseparation,8,0.0); else CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0); return; } if(algoidx==2) { CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0); return; } if(algoidx==3) { CRBF::RBFSetAlgoThinPlateSpline(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal()))); hashessianatnodes=false; return; } if(algoidx==4) { if(CMath::RandomReal()>0.5) CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal()))); else CRBF::RBFSetAlgoMultiQuadricAuto(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal()))); return; } if(algoidx==5) { CRBF::RBFSetAlgoBiharmonic(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal()))); hasgradientatnodes=false; hashessianatnodes=false; return; } CAp::Assert(false,"SetAlgorithm: unknown AlgoIdx"); } //+------------------------------------------------------------------+ //| Sets one of XRBF algorithms (version 3 RBFs introduced in 2022) | //| according to its index. | //+------------------------------------------------------------------+ void CTestRBFUnit::SetXRBFAlgorithmExact(CRBFModel &s,int algoidx, double meanpointsseparation) { if(algoidx==0) { CRBF::RBFSetAlgoThinPlateSpline(s,0.0); return; } if(algoidx==1) { if(CMath::RandomReal()>0.5) CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),0.0); else CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0); return; } if(algoidx==2) { CRBF::RBFSetAlgoBiharmonic(s,0.0); return; } CAp::Assert(false,"SetXRBFAlgorithm: unknown AlgoIdx"); } //+------------------------------------------------------------------+ //| Sets one of smoothing XRBF algorithms(version 3 RBFs introduced | //| in 2022) according to its index. | //+------------------------------------------------------------------+ void CTestRBFUnit::SetXRBFAlgorithmSmoothing(CRBFModel &s,int algoidx, double lambdav, double meanpointsseparation) { if(algoidx==0) { CRBF::RBFSetAlgoThinPlateSpline(s,lambdav); return; } if(algoidx==1) { if(CMath::RandomReal()>0.5) CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),lambdav); else CRBF::RBFSetAlgoMultiQuadricAuto(s,lambdav); return; } if(algoidx==2) { CRBF::RBFSetAlgoBiharmonic(s,lambdav); return; } CAp::Assert(false,"SetXRBFAlgorithmSmoothing: unknown AlgoIdx"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestMLPBaseUnit { public: static bool TestMLPBase(bool silent); private: static double VectorDiff(CRowDouble &g0,CRowDouble &g1,int n,double s); static void CreateNetwork(CMultilayerPerceptron &network,int nkind,double a1,double a2,int nin,int nhid1,int nhid2,int nout); static void UnsetNetwork(CMultilayerPerceptron &network); static void TestInformational(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err); static void TestProcessing(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err); static void TestGradient(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,int sizemin,int sizemax,bool &err); static void TestHessian(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err); static void TestErr(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,int sizemin,int sizemax,bool &err); static void SpecTests(bool &inferrors,bool &procerrors,bool &graderrors,bool &hesserrors,bool &errerrors); static bool TestMLPGBSubset(void); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestMLPBaseUnit::TestMLPBase(bool silent) { //--- create variables bool waserrors=false; bool inferrors=false; bool procerrors=false; bool graderrors=false; bool hesserrors=false; bool errerrors=false; int passcount=5; int maxn=3; int maxhid=3; bool result; int sizemin=0; int sizemax=0; int nf=0; int nl=0; int nhid1=0; int nhid2=0; int nkind=0; CMultilayerPerceptron network; CMultilayerPerceptron network2; CMatrixDouble xy; CMatrixDouble valxy; //--- Special tests SpecTests(inferrors,procerrors,graderrors,hesserrors,errerrors); //--- General multilayer network tests. //--- These tests are performed with small dataset, whose size is in [0,10]. //--- We test correctness of functions on small sets, but do not test code //--- which splits large dataset into smaller chunks. sizemin=0; sizemax=10; for(nf=1; nf<=maxn; nf++) { for(nl=1; nl<=maxn; nl++) { for(nhid1=0; nhid1<=maxhid; nhid1++) { for(nhid2=0; nhid2<=maxhid; nhid2++) { for(nkind=0; nkind<=3; nkind++) { //--- Skip meaningless parameters combinations if(nkind==1 && nl<2) continue; if(nhid1==0 && nhid2!=0) continue; //--- Tests TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors); TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors); TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,sizemin,sizemax,graderrors); TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors); TestErr(nkind,nf,nhid1,nhid2,nl,passcount,sizemin,sizemax,errerrors); } } } } } //--- Special tests on large datasets: test ability to correctly split //--- work into smaller chunks. nf=2; nhid1=20; nhid2=20; nl=2; sizemin=50000; sizemax=50000; TestErr(0,nf,nhid1,nhid2,nl,1,sizemin,sizemax,errerrors); TestGradient(0,nf,nhid1,nhid2,nl,1,sizemin,sizemax,graderrors); //--- Test for MLPGradBatch____Subset() graderrors=graderrors||TestMLPGBSubset(); //--- Final report waserrors=(((inferrors||procerrors)||graderrors)||hesserrors)||errerrors; if(!silent) { Print("MLP TEST"); PrintResult("INFORMATIONAL FUNCTIONS",!inferrors); PrintResult("BASIC PROCESSING",!procerrors); PrintResult("GRADIENT CALCULATION",!graderrors); PrintResult("HESSIAN CALCULATION",!hesserrors); PrintResult("ERROR FUNCTIONS",!errerrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| This function compares vectors G0 and G1 and returns | //| ||G0-G1||/max(||G0||,||G1||,S) | //| For zero G0, G1 and S (all three quantities are zero) it returns | //| zero. | //+------------------------------------------------------------------+ double CTestMLPBaseUnit::VectorDiff(CRowDouble &g0, CRowDouble &g1, int n,double s) { //--- create variables double norm0=0; double norm1=0; double diff=0; for(int i=0; i0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error")) return; if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error")) return; if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error")) return; mkind=CMath::RandomInteger(3); if(nhid1==0) { //--- No hidden layers if(nkind==0) { if(mkind==0) CMLPBase::MLPCreate0(nin,nout,network); if(mkind==1) { CMLPBase::MLPCreate0(nin,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreate0(nin,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==1) { if(mkind==0) CMLPBase::MLPCreateC0(nin,nout,network); if(mkind==1) { CMLPBase::MLPCreateC0(nin,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateC0(nin,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==2) { if(mkind==0) CMLPBase::MLPCreateB0(nin,nout,a1,a2,network); if(mkind==1) { CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==3) { if(mkind==0) CMLPBase::MLPCreateR0(nin,nout,a1,a2,network); if(mkind==1) { CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } CMLPBase::MLPRandomizeFull(network); return; } if(nhid2==0) { //--- One hidden layer if(nkind==0) { if(mkind==0) CMLPBase::MLPCreate1(nin,nhid1,nout,network); if(mkind==1) { CMLPBase::MLPCreate1(nin,nhid1,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreate1(nin,nhid1,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==1) { if(mkind==0) { CMLPBase::MLPCreateC1(nin,nhid1,nout,network); } if(mkind==1) { CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==2) { if(mkind==0) CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network); if(mkind==1) { CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==3) { if(mkind==0) CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network); if(mkind==1) { CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } CMLPBase::MLPRandomizeFull(network); return; } //--- Two hidden layers if(nkind==0) { if(mkind==0) CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network); if(mkind==1) { CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==1) { if(mkind==0) CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network); if(mkind==1) { CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==2) { if(mkind==0) CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network); if(mkind==1) { CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } else { if(nkind==3) { if(mkind==0) CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network); if(mkind==1) { CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CMLPBase::MLPAlloc(_local_serializer,tmp); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CMLPBase::MLPSerialize(_local_serializer,tmp); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CMLPBase::MLPUnserialize(_local_serializer,network); _local_serializer.Stop(); } } if(mkind==2) { CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp); CMLPBase::MLPCopy(tmp,network); } } } } } CMLPBase::MLPRandomizeFull(network); } //+------------------------------------------------------------------+ //| Unsets network (initialize it to smallest network possible | //+------------------------------------------------------------------+ void CTestMLPBaseUnit::UnsetNetwork(CMultilayerPerceptron &network) { CMLPBase::MLPCreate0(1,1,network); } //+------------------------------------------------------------------+ //| Informational functions test | //+------------------------------------------------------------------+ void CTestMLPBaseUnit::TestInformational(int nkind,int nin,int nhid1, int nhid2,int nout, int passcount,bool &err) { //--- create variables CMultilayerPerceptron network; int n1=0; int n2=0; int wcount=0; int i=0; int j=0; int k=0; double threshold=0; int nlayers=0; int nmax=0; CMatrixDouble neurons; CRowDouble x; CRowDouble y; double mean=0; double sigma=0; int fkind=0; double c=0; double f=0; double df=0; double d2f=0; double s=0; threshold=100000*CMath::m_machineepsilon; CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout); //--- test MLPProperties() CMLPBase::MLPProperties(network,n1,n2,wcount); err=(err||n1!=nin||n2!=nout||wcount<=0); err=(err||CMLPBase::MLPGetInputsCount(network)!=nin||CMLPBase::MLPGetOutputsCount(network)!=nout || CMLPBase::MLPGetWeightsCount(network)!=wcount); //--- Test network geometry functions //--- In order to do this we calculate neural network output using //--- informational functions only, and compare results with ones //--- obtained with MLPProcess(): //--- 1. we allocate 2-dimensional array of neurons and fill it by zeros //--- 2. we full first layer of neurons by input values //--- 3. we move through array, calculating values of subsequent layers //--- 4. if we have classification network, we SOFTMAX-normalize output layer //--- 5. we apply scaling to the outputs //--- 6. we compare results with ones obtained by MLPProcess() //--- NOTE: it is important to do (4) before (5), because on SOFTMAX network //--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order //--- to test it implicitly, we apply it to the classifier results //--- (already normalized). If one of the coefficients deviates from //--- expected values, we will get error during (6). nlayers=2; nmax=MathMax(nin,nout); if(nhid1!=0) { nlayers=3; nmax=MathMax(nmax,nhid1); } if(nhid2!=0) { nlayers=4; nmax=MathMax(nmax,nhid2); } neurons=matrix::Zeros(nlayers,nmax); x=vector::Zeros(nin); for(i=0; i::Zeros(nout); for(i=0; ithreshold; } //+------------------------------------------------------------------+ //| Processing functions test | //+------------------------------------------------------------------+ void CTestMLPBaseUnit::TestProcessing(int nkind,int nin,int nhid1, int nhid2,int nout, int passcount,bool &err) { //--- create variables CMultilayerPerceptron network; CMultilayerPerceptron network2; CSparseMatrix sparsexy; CMatrixDouble densexy; int npoints=0; int subnp=0; bool iscls; int n1=0; int n2=0; int wcount=0; bool zeronet; double a1=0; double a2=0; int pass=0; int i=0; int j=0; bool allsame; CRowDouble x1; CRowDouble x2; CRowDouble y1; CRowDouble y2; CRowDouble p0; CRowDouble p1; int pcount=0; double v=0; int i_=0; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) { err=true; return; } //--- Prepare network a1=0; a2=0; if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); iscls=CMLPBase::MLPIsSoftMax(network); //--- Initialize arrays x1=vector::Zeros(nin); x2=vector::Zeros(nin); y1=vector::Zeros(nout); y2=vector::Zeros(nout); //--- Initialize sets npoints=CMath::RandomInteger(11)+10; if(iscls) { densexy=matrix::Zeros(npoints,nin+1); CSparse::SparseCreate(npoints,nin+1,npoints,sparsexy); } else { densexy=matrix::Zeros(npoints,nin+nout); CSparse::SparseCreate(npoints,nin+nout,npoints,sparsexy); } CSparse::SparseConvertToCRS(sparsexy); //--- Main cycle for(pass=1; pass<=passcount; pass++) { //--- Last run is made on zero network CMLPBase::MLPRandomizeFull(network); zeronet=false; if(pass==passcount) { network.m_weights.Fill(0); zeronet=true; } //--- Same inputs leads to same outputs for(i=0; i(1000.0*CMath::m_machineepsilon); } if(nkind==2) { //--- B-type network outputs are bounded from above/below for(i=0; i=0.0) err=err||y1[i]a1; } } if(nkind==3) { //--- R-type network outputs are within [A1,A2] (or [A2,A1]) for(i=0; iMathMax(a1,a2)); } //--- Comperison MLPInitPreprocessor results with //--- MLPInitPreprocessorSparse results CSparse::SparseConvertToHash(sparsexy); if(iscls) { for(i=0; i1.0E-6; } } } //+------------------------------------------------------------------+ //| Gradient functions test | //+------------------------------------------------------------------+ void CTestMLPBaseUnit::TestGradient(int nkind,int nin,int nhid1, int nhid2,int nout,int passcount, int sizemin,int sizemax,bool &err) { //--- create variables CMultilayerPerceptron network; CSparseMatrix sparsexy; CSparseMatrix sparsexy2; int n1=0; int n2=0; int wcount=0; double h=0; double etol=0; double escale=0; double gscale=0; double nonstricttolerance=0; double a1=0; double a2=0; int pass=0; int i=0; int j=0; int k=0; int ssize=0; int subsetsize=0; int rowsize=0; CMatrixDouble xy; CMatrixDouble xy2; CRowDouble grad1; CRowDouble grad2; CRowDouble gradsp; CRowDouble x; CRowDouble y; CRowDouble x1; CRowDouble x2; CRowDouble y1; CRowDouble y2; CRowInt idx; double v=0; double e=0; double e1=0; double e2=0; double esp=0; double v1=0; double v2=0; double v3=0; double v4=0; double wprev=0; double referencee=0; CRowDouble referenceg; int i_=0; int i1_=0; if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); h=0.0001; etol=1.0E-2; escale=1.0E-2; gscale=1.0E-2; nonstricttolerance=0.01; //--- Initialize x=vector::Zeros(nin); x1=vector::Zeros(nin); x2=vector::Zeros(nin); y=vector::Zeros(nout); y1=vector::Zeros(nout); y2=vector::Zeros(nout); referenceg=vector::Zeros(wcount); grad1=vector::Zeros(wcount); grad2=vector::Zeros(wcount); //--- Process for(pass=1; pass<=passcount; pass++) { //--- Randomize network, then re-randomaze weights manually. //--- NOTE: weights magnitude is chosen to be small, about 0.1, //--- which allows us to avoid oversaturated network. //--- In 10% of cases we use zero weights. CMLPBase::MLPRandomizeFull(network); if(CMath::RandomReal()<=0.1) network.m_weights.Fill(0.0); else { for(i=0; i::Zeros(1,nin+nout); CSparse::SparseCreate(1,nin+nout,nin+nout,sparsexy); for(i=0; ietol,"testmlpbaseunit.ap:948"); //--- Test MLPErrorN(), MLPGradN() for single-element dataset: //--- * generate input X, output Y, combine them in dataset XY //---*calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors) //---*calculate "reference" gradient by performing numerical differentiation of "reference" error //--- * test error/gradient returned by MLPGradN(), MLPErrorN() //--- NOTE: because we use inexact 2-point formula, we perform gradient test with NonStrictTolerance xy=matrix::Zeros(1,nin+nout); for(i=0; inonstricttolerance,"testmlpbaseunit.ap:1029"); //--- Test that gradient calculation functions automatically allocate //--- space for gradient, if needed. //--- NOTE: we perform test with empty dataset. CSparse::SparseCreate(1,nin+nout,0,sparsexy); CSparse::SparseConvertToCRS(sparsexy); grad1=vector::Zeros(1); CMLPBase::MLPGradBatch(network,xy,0,e1,grad1); CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1042"); grad1=vector::Zeros(1); CMLPBase::MLPGradBatchSparse(network,sparsexy,0,e1,grad1); CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1045"); grad1=vector::Zeros(1); CMLPBase::MLPGradBatchSubset(network,xy,0,idx,0,e1,grad1); CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1048"); grad1=vector::Zeros(1); CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,0,idx,0,e1,grad1); CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1051"); //--- Test MLPError(), MLPErrorSparse(), MLPGradBatch(), MLPGradBatchSparse() for many-element dataset: //--- * generate random dataset XY //---*calculate "reference" error/gradient using MLPGrad(), which was tested in previous //--- section and is assumed to work correctly //--- * test results returned by MLPGradBatch/MLPGradBatchSparse against reference ones //--- NOTE: about 10% of tests are performed with zero SSize ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1); xy=matrix::Zeros(MathMax(ssize,1),nin+nout); CSparse::SparseCreate(MathMax(ssize,1),nin+nout,ssize*(nin+nout),sparsexy); referenceg.Fill(0); referencee=0; for(i=0; ietol,"testmlpbaseunit.ap:1104"); CMLPBase::MLPGradBatchSparse(network,sparsexy,ssize,esp,gradsp); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1107"); //--- Test MLPErrorSubset(), MLPGradBatchSubset(), MLPErrorSparseSubset(), MLPGradBatchSparseSubset() //--- for many-element dataset with different types of subsets: //--- * generate random dataset XY //---*"reference" error/gradient are calculated with MLPGradBatch(), //--- which was tested in previous section and is assumed to work correctly //--- * we perform tests for different subsets: //--- * SubsetSize<0 - subset is a full dataset //--- * SubsetSize=0 - subset is empty //--- * SubsetSize>0 - random subset ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1); xy=matrix::Zeros(MathMax(ssize,1),nin+nout); CSparse::SparseCreate(MathMax(ssize,1),nin+nout,ssize*(nin+nout),sparsexy); for(i=0; i0) { subsetsize=1+CMath::RandomInteger(10); xy2=matrix::Zeros(subsetsize,nin+nout); idx.Resize(subsetsize); CSparse::SparseCreate(subsetsize,nin+nout,subsetsize*(nin+nout),sparsexy2); if(CMLPBase::MLPIsSoftMax(network)) rowsize=nin+1; else rowsize=nin+nout; for(i=0; i<=subsetsize-1; i++) { k=CMath::RandomInteger(ssize); idx.Set(i,k); for(j=0; j<=rowsize-1; j++) { xy2.Set(i,j,xy.Get(k,j)); CSparse::SparseSet(sparsexy2,i,j,CSparse::SparseGet(sparsexy,k,j)); } } CSparse::SparseConvertToCRS(sparsexy2); } else { subsetsize=0; CSparse::SparseCreate(1,nin+nout,0,sparsexy2); CSparse::SparseConvertToCRS(sparsexy2); } CMLPBase::MLPGradBatch(network,xy,ssize,referencee,referenceg); e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,-1); esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,-1); CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,-1,e2,grad2); CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,-1,esp,gradsp); CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1192"); CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1193"); CMLPBase::MLPGradBatch(network,xy,0,referencee,referenceg); e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,0); esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,0); CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,0,e2,grad2); CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,0,esp,gradsp); CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1205"); CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1206"); CMLPBase::MLPGradBatch(network,xy2,subsetsize,referencee,referenceg); e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,subsetsize); esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,subsetsize); CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,subsetsize,e2,grad2); CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,subsetsize,esp,gradsp); CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale); CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale); CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1218"); CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1219"); //--- Test MLPGradNBatch() for many-element dataset: //--- * generate random dataset XY //---*calculate "reference" error/gradient using MLPGrad(), which was tested in previous //--- section and is assumed to work correctly //--- * test results returned by MLPGradNBatch against reference ones ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1); xy=matrix::Zeros(ssize,nin+nout); for(i=0; ietol,"testmlpbaseunit.ap:1258"); } } //+------------------------------------------------------------------+ //| Hessian functions test //+------------------------------------------------------------------+ void CTestMLPBaseUnit::TestHessian(int nkind,int nin,int nhid1, int nhid2,int nout, int passcount,bool &err) { //--- create variables CMultilayerPerceptron network; int hkind=0; int n1=0; int n2=0; int wcount=0; double h=0; double etol=0; int pass=0; int i=0; int j=0; int ssize=0; double a1=0; double a2=0; CMatrixDouble xy; CMatrixDouble h1; CMatrixDouble h2; CRowDouble grad1; CRowDouble grad2; CRowDouble grad3; CRowDouble x; CRowDouble y; CRowDouble x1; CRowDouble x2; CRowDouble y1; CRowDouble y2; double v=0; double e1=0; double e2=0; double wprev=0; int i_=0; int i1_=0; //--- check if(!CAp::Assert(passcount>=2,"PassCount<2!")) { err=true; return; } if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); h=0.00001; etol=0.05; //--- Initialize x=vector::Zeros(nin); x1=vector::Zeros(nin); x2=vector::Zeros(nin); y=vector::Zeros(nout); y1=vector::Zeros(nout); y2=vector::Zeros(nout); grad1=vector::Zeros(wcount); grad2=vector::Zeros(wcount); grad3=vector::Zeros(wcount); h1=matrix::Zeros(wcount,wcount); h2=matrix::Zeros(wcount,wcount); //--- Process for(pass=1; pass<=passcount; pass++) { CMLPBase::MLPRandomizeFull(network); //--- Test hessian calculation . //--- E1 contains total error (calculated using MLPGrad/MLPGradN) //--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN) //--- H1 contains Hessian calculated using differences of gradients //--- E2, Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch for(hkind=0; hkind<=1; hkind++) { ssize=1+CMath::RandomInteger(10); xy=matrix::Zeros(ssize,nin+nout-1+1); for(i=0; ietol,"testmlpbaseunit.ap:1440"); for(i=0; i1.0E-2) CAp::SetErrorFlag(err,MathAbs((grad2[i]-grad1[i])/grad1[i])>etol,"testmlpbaseunit.ap:1443"); else CAp::SetErrorFlag(err,MathAbs(grad2[i]-grad1[i])>etol,"testmlpbaseunit.ap:1445"); } for(i=0; i5.0E-2) CAp::SetErrorFlag(err,MathAbs((h1.Get(i,j)-h2.Get(i,j))/h1.Get(i,j))>etol,"testmlpbaseunit.ap:1449"); else CAp::SetErrorFlag(err,MathAbs(h2.Get(i,j)-h1.Get(i,j))>etol,"testmlpbaseunit.ap:1451"); } } } } //+------------------------------------------------------------------+ //| Error functions (other than MLPError and MLPErrorN) test. | //| Network of type NKind is created, with NIn inputs, NHid1*NHid2 | //| hidden layers (one layer if NHid2 = 0), NOut outputs. PassCount | //| random passes is performed. Dataset has random size in | //| [SizeMin, SizeMax]. | //+------------------------------------------------------------------+ void CTestMLPBaseUnit::TestErr(int nkind,int nin,int nhid1, int nhid2,int nout,int passcount, int sizemin,int sizemax,bool &err) { //--- create variables CMultilayerPerceptron network; CSparseMatrix sparsexy; int n1=0; int n2=0; int wcount=0; double etol=0; double escale=0; double a1=0; double a2=0; int pass=0; int i=0; int j=0; int k=0; int ssize=0; int subsetsize=0; CMatrixDouble xy; CRowDouble y; CRowDouble x1; CRowDouble y1; CRowInt idx; CRowInt dummy; double refrmserror=0; double refclserror=0; double refrelclserror=0; double refavgce=0; double refavgerror=0; double refavgrelerror=0; int avgrelcnt=0; CModelErrors allerrors; int nnmax=0; int dsmax=0; double relclstolerance=0; int i_=0; int i1_=0; if(nkind==2) { a1=1000*CMath::RandomReal()-500; a2=2*CMath::RandomReal()-1; } if(nkind==3) { a1=1000*CMath::RandomReal()-500; a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal()); } CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout); CMLPBase::MLPProperties(network,n1,n2,wcount); etol=1.0E-4; escale=1.0E-2; //--- Initialize x1=vector::Zeros(nin); y=vector::Zeros(nout); y1=vector::Zeros(nout); //--- Process for(pass=1; pass<=passcount; pass++) { //--- Randomize network, then re-randomaze weights manually. //--- NOTE: weights magnitude is chosen to be small, about 0.1, //--- which allows us to avoid oversaturated network. //--- In 10% of cases we use zero weights. CMLPBase::MLPRandomizeFull(network); if(CMath::RandomReal()<=0.1) network.m_weights.Fill(0.0); else { for(i=0; i::Zeros(MathMax(ssize,1),nin+1); CSparse::SparseCreate(MathMax(ssize,1),nin+1,0,sparsexy); } else { xy=matrix::Zeros(MathMax(ssize,1),nin+nout); CSparse::SparseCreate(MathMax(ssize,1),nin+nout,0,sparsexy); } refrmserror=0.0; refclserror=0.0; refavgce=0.0; refavgerror=0.0; refavgrelerror=0.0; avgrelcnt=0; for(i=0; i=0.0) y1.Add(j,0.1); else y1.Add(j,-0.1); CSparse::SparseSet(sparsexy,i,nin+j,y1[j]); } i1_=-nin; for(i_=nin; i_0.0) refavgce+=MathLog(1.0/y[(int)MathRound(xy.Get(i,nin))]); else refavgce+=MathLog(CMath::m_maxrealnumber); } if(CMLPBase::MLPIsSoftMax(network)) dsmax=(int)MathRound(xy.Get(i,nin)); else dsmax=0; for(j=0; jy[nnmax]) nnmax=j; if(!CMLPBase::MLPIsSoftMax(network) && y1[j]>y1[dsmax]) dsmax=j; } if(nnmax!=dsmax) refclserror++; } CSparse::SparseConvertToCRS(sparsexy); if(ssize>0) { refrmserror=MathSqrt(refrmserror/(ssize*nout)); refavgerror/=(double)(ssize*nout); refrelclserror=refclserror/ssize; refavgce/=(ssize*MathLog(2.0)); } else refrelclserror=0.0; if(avgrelcnt>0) refavgrelerror/=(double)avgrelcnt; //--- Test "continuous" errors on full dataset CAp::SetErrorFlagDiff(err,CMLPBase::MLPRMSError(network,xy,ssize),refrmserror,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgCE(network,xy,ssize),refavgce,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgError(network,xy,ssize),refavgerror,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgRelError(network,xy,ssize),refavgrelerror,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPRMSErrorSparse(network,sparsexy,ssize),refrmserror,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgCESparse(network,sparsexy,ssize),refavgce,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgErrorSparse(network,sparsexy,ssize),refavgerror,etol,escale); CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgRelErrorSparse(network,sparsexy,ssize),refavgrelerror,etol,escale); CMLPBase::MLPAllErrorsSubset(network,xy,ssize,dummy,-1,allerrors); CAp::SetErrorFlagDiff(err,allerrors.m_AvgCE,refavgce,etol,escale); CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale); CAp::SetErrorFlagDiff(err,allerrors.m_AvgError,refavgerror,etol,escale); CAp::SetErrorFlagDiff(err,allerrors.m_AvgRelError,refavgrelerror,etol,escale); CMLPBase::MLPAllErrorsSparseSubset(network,sparsexy,ssize,dummy,-1,allerrors); CAp::SetErrorFlagDiff(err,allerrors.m_AvgCE,refavgce,etol,escale); CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale); CAp::SetErrorFlagDiff(err,allerrors.m_AvgError,refavgerror,etol,escale); CAp::SetErrorFlagDiff(err,allerrors.m_AvgRelError,refavgrelerror,etol,escale); //--- Test errors on dataset given by subset. //--- We perform only limited test for RMS error, assuming that either all errors //--- are calculated correctly (subject to subset given by Idx) - or none of them. if(ssize>0) subsetsize=CMath::RandomInteger(10); else subsetsize=0; idx.Resize(subsetsize); refrmserror=0.0; for(i=0; i<=subsetsize-1; i++) { k=CMath::RandomInteger(ssize); idx.Set(i,k); for(i_=0; i_0) refrmserror=MathSqrt(refrmserror/(subsetsize*nout)); CMLPBase::MLPAllErrorsSubset(network,xy,ssize,idx,subsetsize,allerrors); CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale); CMLPBase::MLPAllErrorsSparseSubset(network,sparsexy,ssize,idx,subsetsize,allerrors); CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale); //--- Test "discontinuous" error function. //--- Even slight changes in the network output may force these functions //--- to change by 1. So, we test them with relaxed criteria, corresponding to //--- difference in classification of two samples. if(ssize>0) { relclstolerance=2.5/ssize; CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPClsError(network,xy,ssize)-refclserror)>(ssize*relclstolerance),"testmlpbaseunit.ap:1732"); CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPRelClsError(network,xy,ssize)-refrelclserror)>relclstolerance,"testmlpbaseunit.ap:1733"); CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPRelClsErrorSparse(network,sparsexy,ssize)-refrelclserror)>relclstolerance,"testmlpbaseunit.ap:1734"); } } } //+------------------------------------------------------------------+ //| Special tests | //+------------------------------------------------------------------+ void CTestMLPBaseUnit::SpecTests(bool &inferrors,bool &procerrors, bool &graderrors,bool &hesserrors, bool &errerrors) { //--- create variables CMultilayerPerceptron net; CMatrixDouble xy; double f=0; CRowDouble g; int i=0; //--- Special test for overflow in TanH: //--- * create 1x1x1 linear network //--- * create dataset with 1 item: [x, y] = .Get(0, 1) //--- * set network weights to [10000000, 10000000, 10000000, 10000000] //--- * check that error function is finite //--- * check that gradient is finite CMLPBase::MLPCreate1(1,1,1,net); xy=matrix::Zeros(1,2); xy.Set(0,1,1.0); for(i=0; i<=CMLPBase::MLPGetWeightsCount(net)-1; i++) net.m_weights.Set(i,10000000.0); CMLPBase::MLPGradBatch(net,xy,1,f,g); CAp::SetErrorFlag(graderrors,!MathIsValidNumber(f),"testmlpbaseunit.ap:1907"); CAp::SetErrorFlag(graderrors,!MathIsValidNumber(CMLPBase::MLPError(net,xy,1)),"testmlpbaseunit.ap:1908"); for(i=0; i::Zeros(1,2); xy.Set(0,1,1.0); for(i=0; i0) { a=matrix::Zeros(ssize,w); CSparse::SparseCreate(ssize,w,ssize*w,sa); } else { a.Resize(0,0); CSparse::SparseCreate(1,1,0,sa); } for(i=0; i0) { a=matrix::Zeros(ssize,w); CSparse::SparseCreate(ssize,w,ssize*w,sa); } else { a.Resize(0,0); CSparse::SparseCreate(1,1,0,sa); } for(i=0; i=0,"mlpbase test: integrity check failed")) return(true); if(sbsize!=0) { parta=matrix::Zeros(sbsize,w); CSparse::SparseCreate(sbsize,w,sbsize*w,partsa); } else { parta.Resize(0,0); CSparse::SparseCreate(1,1,0,partsa); } for(i=0; i1.0E-6) { result=true; return(result); } for(i=0; i1.0E-6) { result=true; return(result); } } //--- Test for sparse matrix CMLPBase::MLPGradBatchSparse(net,partsa,sbsize,e1,grad1); CMLPBase::MLPGradBatchSparseSubset(net,sa,ssize,idx,sbsize,e2,grad2); if(MathAbs(e1-e2)>1.0E-6) { result=true; return(result); } for(i=0; i1.0E-6) { result=true; return(result); } } } } //--- return result result=false; return(result); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestFiltersUnit { public: static bool CTestFiltersUnit::TestFilters(bool silent); private: static bool TestSMA(bool issilent); static bool TestEMA(bool issilent); static bool TestLRMA(bool issilent); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestFiltersUnit::TestFilters(bool silent) { //--- create variables bool result; bool waserrors; bool smaerrors; bool emaerrors; bool lrmaerrors; smaerrors=TestSMA(silent); emaerrors=TestEMA(silent); lrmaerrors=TestLRMA(silent); //--- Final report waserrors=(smaerrors||emaerrors)||lrmaerrors; if(!silent) { Print("FILTERS TEST"); PrintResult("* SMA",!smaerrors); PrintResult("* EMA",!emaerrors); PrintResult("* LRMA",!lrmaerrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| This function tests SMA(k) filter. It returns True on error. | //| Additional IsSilent parameter controls detailed error reporting. | //+------------------------------------------------------------------+ bool CTestFiltersUnit::TestSMA(bool issilent) { //--- create variables bool result; CRowDouble x; bool precomputederrors; bool zerohandlingerrors; double threshold=1000*CMath::m_machineepsilon; if(!issilent) Print("SMA(K) TEST"); //--- Test several pre-computed problems. //--- NOTE: tests below rely on the fact that floating point //--- additions and subtractions are exact when dealing //--- with integer values. precomputederrors=false; x=vector::Full(1,7); CFilters::FilterSMA(x,1,1); precomputederrors=precomputederrors||x[0]!=7.0; x=vector::Zeros(3); x.Set(0,7); x.Set(1,8); x.Set(2,9); CFilters::FilterSMA(x,3,1); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0); CFilters::FilterSMA(x,3,2); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.5); x=vector::Zeros(3); x.Set(0,7); x.Set(1,8); x.Set(2,9); CFilters::FilterSMA(x,3,4); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.0); //--- Test zero-handling: //--- a) when we have non-zero sequence (N1 elements) followed by zero sequence //--- (N2 elements), then first N1+K-1 elements of the processed sequence are //--- non-zero, but elements since (N1+K)th must be exactly zero. //--- b) similar property holds for zero sequence followed by non-zero one //--- Naive implementation of SMA does not have such property. //--- NOTE: it is important to initialize X with non-integer elements with long //--- binary mantissas, because this test tries to test behaviour in the presence //--- of roundoff errors, and it will be useless when used with integer inputs. zerohandlingerrors=false; x=vector::Zeros(10); x.Set(0,MathSqrt(2)); x.Set(1,MathSqrt(3)); x.Set(2,MathSqrt(5)); x.Set(3,MathSqrt(6)); x.Set(4,MathSqrt(7)); CFilters::FilterSMA(x,10,3); zerohandlingerrors=zerohandlingerrors||MathAbs(x[0]-MathSqrt(2))>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[1]-(MathSqrt(2)+MathSqrt(3))/2)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[2]-(MathSqrt(2)+MathSqrt(3)+MathSqrt(5))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[3]-(MathSqrt(3)+MathSqrt(5)+MathSqrt(6))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[4]-(MathSqrt(5)+MathSqrt(6)+MathSqrt(7))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[5]-(MathSqrt(6)+MathSqrt(7))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[6]-MathSqrt(7)/3)>threshold; zerohandlingerrors=zerohandlingerrors||x[7]!=0.0; zerohandlingerrors=zerohandlingerrors||x[8]!=0.0; zerohandlingerrors=zerohandlingerrors||x[9]!=0.0; x.Set(0,0); x.Set(1,0); x.Set(2,0); x.Set(3,0); x.Set(4,0); x.Set(5,MathSqrt(2)); x.Set(6,MathSqrt(3)); x.Set(7,MathSqrt(5)); x.Set(8,MathSqrt(6)); x.Set(9,MathSqrt(7)); CFilters::FilterSMA(x,10,3); zerohandlingerrors=zerohandlingerrors||x[0]!=0.0; zerohandlingerrors=zerohandlingerrors||x[1]!=0.0; zerohandlingerrors=zerohandlingerrors||x[2]!=0.0; zerohandlingerrors=zerohandlingerrors||x[3]!=0.0; zerohandlingerrors=zerohandlingerrors||x[4]!=0.0; zerohandlingerrors=zerohandlingerrors||MathAbs(x[5]-MathSqrt(2)/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[6]-(MathSqrt(2)+MathSqrt(3))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[7]-(MathSqrt(2)+MathSqrt(3)+MathSqrt(5))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[8]-(MathSqrt(3)+MathSqrt(5)+MathSqrt(6))/3)>threshold; zerohandlingerrors=zerohandlingerrors||MathAbs(x[9]-(MathSqrt(5)+MathSqrt(6)+MathSqrt(7))/3)>threshold; //--- Final result result=precomputederrors||zerohandlingerrors; return(result); } //+------------------------------------------------------------------+ //| This function tests EMA(alpha) filter. It returns True on error. | //| Additional IsSilent parameter controls detailed error reporting. | //+------------------------------------------------------------------+ bool CTestFiltersUnit::TestEMA(bool issilent) { //--- create variables CRowDouble x; bool precomputederrors; if(!issilent) Print("EMA(alpha) TEST"); //--- Test several pre-computed problems. //--- NOTE: tests below rely on the fact that floating point //--- additions and subtractions are exact when dealing //--- with integer values. precomputederrors=false; x=vector::Full(1,7); CFilters::FilterEMA(x,1,1.0); precomputederrors=precomputederrors||x[0]!=7.0; CFilters::FilterEMA(x,1,0.5); precomputederrors=precomputederrors||x[0]!=7.0; x=vector::Zeros(3); x.Set(0,7); x.Set(1,8); x.Set(2,9); CFilters::FilterEMA(x,3,1.0); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0); CFilters::FilterEMA(x,3,0.5); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.25); //--- Final result return(precomputederrors); } //+------------------------------------------------------------------+ //| This function tests LRMA(k) filter. It returns True on error. | //| Additional IsSilent parameter controls detailed error reporting. | //+------------------------------------------------------------------+ bool CTestFiltersUnit::TestLRMA(bool issilent) { //--- create variables CRowDouble x; bool precomputederrors=false; double threshold=1000*CMath::m_machineepsilon; if(!issilent) Print("LRMA(K) TEST"); //--- First, check that filter does not changes points for K=1 or K=2 x=vector::Full(1,7); CFilters::FilterLRMA(x,1,1); precomputederrors=precomputederrors||x[0]!=7.0; x=vector::Zeros(6); x.Set(0,7); x.Set(1,8); x.Set(2,9); x.Set(3,10); x.Set(4,11); x.Set(5,12); CFilters::FilterLRMA(x,6,1); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0||x[3]!=10.0||x[4]!=11.0||x[5]!=12.0); CFilters::FilterLRMA(x,6,2); precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0||x[3]!=10.0||x[4]!=11.0||x[5]!=12.0); //--- Check several precomputed problems x=vector::Zeros(6); x.Set(0,7); x.Set(1,8); x.Set(2,9); x.Set(3,10); x.Set(4,11); x.Set(5,12); CFilters::FilterLRMA(x,6,3); precomputederrors=precomputederrors||MathAbs(x[0]-7)>threshold; precomputederrors=precomputederrors||MathAbs(x[1]-8)>threshold; precomputederrors=precomputederrors||MathAbs(x[2]-9)>threshold; precomputederrors=precomputederrors||MathAbs(x[3]-10)>threshold; precomputederrors=precomputederrors||MathAbs(x[4]-11)>threshold; precomputederrors=precomputederrors||MathAbs(x[5]-12)>threshold; x=vector::Zeros(6); x.Set(0,7); x.Set(1,8); x.Set(2,8); x.Set(3,9); x.Set(4,12); x.Set(5,12); CFilters::FilterLRMA(x,6,3); precomputederrors=precomputederrors||MathAbs(x[0]-7.0000000000)>(1.0E-5); precomputederrors=precomputederrors||MathAbs(x[1]-8.0000000000)>(1.0E-5); precomputederrors=precomputederrors||MathAbs(x[2]-8.1666666667)>(1.0E-5); precomputederrors=precomputederrors||MathAbs(x[3]-8.8333333333)>(1.0E-5); precomputederrors=precomputederrors||MathAbs(x[4]-11.6666666667)>(1.0E-5); precomputederrors=precomputederrors||MathAbs(x[5]-12.5000000000)>(1.0E-5); //--- Final result return(precomputederrors); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTestSSAUnit { public: static bool CTestSSAUnit::TestSSA(bool silent); private: static void TestGeneral(bool &errorflag); static void TestSpecial(bool&errorflag); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTestSSAUnit::TestSSA(bool silent) { //--- create variables bool result; bool specerrors; bool generrors; bool waserrors; specerrors=false; generrors=false; TestSpecial(specerrors); TestGeneral(generrors); //--- Final report waserrors=specerrors||generrors; if(!silent) { Print("SSA TEST"); PrintResult("* GENERAL TEST SUITE",!generrors) PrintResult("* SPECIAL CASES",!specerrors); PrintResult("TEST SUMMARY",!waserrors); Print("\n"); } //--- return result result=!waserrors; return(result); } //+------------------------------------------------------------------+ //| This function tests SSA on several general purpose analysis / | //| prediction problems. | //| On failure sets ErrorFlag, on success it is untouched. | //+------------------------------------------------------------------+ void CTestSSAUnit::TestGeneral(bool &errorflag) { //--- create variables int i=0; int j=0; int k=0; double v=0; double vv=0; int pass=0; CSSAModel state; CSSAModel state2; int ntracks=0; int windowwidth=0; int nlasttracklen=0; int nzeros=0; int nlinear=0; double sinefreq=0; double sineoffs=0; double sineamp=0; int windowwidth2=0; int nbasis=0; int nbasis2=0; int ninitial=0; CRowDouble x; CRowDouble x2; CRowDouble trend; CRowDouble noise; CRowDouble sv; CRowDouble sv2; CRowDouble tmp0; CRowDouble trend2; CRowDouble noise2; int algotype=0; int nticks=0; int nnoise=0; int navg=0; CMatrixDouble a; CMatrixDouble a2; CMatrixDouble b; double tol=0; int datalen=0; int forecastlen=0; int mlimit=0; int passcount=0; double skipprob=0; int i_=0; int i1_=0; CHighQualityRandState rs; //--- Initialize RNG, test pass count and skip probability. //--- When we perform several sequential tests on the same model, we may //--- skip some of them with probability SkipProb in order to make sure //--- that no carry-over effect is observed between tests. CHighQualityRand::HQRndRandomize(rs); passcount=500; skipprob=0.50; //--- Iterate over several algorithm types. //--- Algorithms unsupported by tests are skipped within tests. for(algotype=1; algotype<=3; algotype++) { //--- Test that on perfectly constant dataset SSA correctly predicts //--- perfectly constant trend. Additionally test that analysis phase //--- correctly returns nearly-zero noise and nearly-constant trend. //--- Dataset is a one/few sequences with different constants; top-1 //--- algorithm is used (or precomputed unit-normalized vector of 1's). for(pass=1; pass<=passcount; pass++) { nlasttracklen=-9999999; ntracks=1+CHighQualityRand::HQRndUniformI(rs,3); windowwidth=2+CHighQualityRand::HQRndUniformI(rs,3); CSSA::SSACreate(state); CSSA::SSASetWindow(state,windowwidth); for(k=0; k::Zeros(nlasttracklen); for(i=0; i::Zeros(windowwidth,1); for(i=0; iskipprob) { CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks); CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:143"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:144"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:145"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:150"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:151"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { CSSA::SSAAnalyzeLast(state,nlasttracklen,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nlasttracklen,"testssaunit.ap:157"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nlasttracklen,"testssaunit.ap:158"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:163"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:164"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:172"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:176"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+3); CSSA::SSAForecastAvgLast(state,navg,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:184"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:188"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); x.Set(0,CHighQualityRand::HQRndNormal(rs)); for(i=1; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:200"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:204"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10); navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+3); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); x.Set(0,CHighQualityRand::HQRndNormal(rs)); for(i=1; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:217"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:221"); } } //--- Test that on specially designed linear dataset SSA correctly //--- predicts perfectly linear trend. Additionally test that analysis //--- phase correctly returns nearly-zero noise and nearly-linear trend. //--- Also test that correct basis vectors are returned. //--- Dataset consists of many WindowWidth-sized (exactly) sequences with //--- linear trend. Trend coefficients vary accross sequences. Top-2 //--- algorithm is used (or precomputed unit-normalized linear trend basis). //--- NOTE: this test requires NTracks>=2, WindowWidth>=3 and TopK=2 to work; //--- however, in order to improve numerical properties (diversity among //--- samples, different slopes and offsets) we set NTracks to be //--- at least 5. //--- NOTE: one more version of this test verifies scaling properties //--- by solving larger task (WindowWidth=100, NTracks=5, //--- TrackLen=2*WindowWidth). This version makes just 5 checks because //--- of higher cost. for(pass=1; pass<=passcount; pass++) { ntracks=5+CHighQualityRand::HQRndUniformI(rs,10); windowwidth=3+CHighQualityRand::HQRndUniformI(rs,3); CSSA::SSACreate(state); CSSA::SSASetWindow(state,windowwidth); x=vector::Zeros(windowwidth); for(k=0; k::Zeros(windowwidth,2); v=0.0; for(i=0; iskipprob) { //--- Basis vectors must be linear/constant functions CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:293"); CAp::SetErrorFlag(errorflag,nbasis!=2,"testssaunit.ap:294"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:295"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:296"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:297"); if(errorflag) return; for(j=0; jtol,"testssaunit.ap:304"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks); CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:310"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:311"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:312"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:317"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:318"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { CSSA::SSAAnalyzeLast(state,windowwidth,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:324"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:325"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:330"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:331"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:338"); if(errorflag) return; v=x[windowwidth-1]-x[windowwidth-2]; CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[windowwidth-1]-v)>tol,"testssaunit.ap:342"); for(i=1; itol,"testssaunit.ap:344"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+5); CSSA::SSAForecastAvgLast(state,navg,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:352"); if(errorflag) return; v=x[windowwidth-1]-x[windowwidth-2]; CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[windowwidth-1]-v)>tol,"testssaunit.ap:356"); for(i=1; itol,"testssaunit.ap:358"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); v=CHighQualityRand::HQRndNormal(rs); x.Set(0,CHighQualityRand::HQRndNormal(rs)); for(i=1; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:371"); if(errorflag) return; CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[datalen-1]-v)>tol,"testssaunit.ap:374"); for(i=1; itol,"testssaunit.ap:376"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10); navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+5); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); v=CHighQualityRand::HQRndNormal(rs); x.Set(0,CHighQualityRand::HQRndNormal(rs)); for(i=1; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:390"); if(errorflag) return; CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[datalen-1]-v)>tol,"testssaunit.ap:393"); for(i=1; itol,"testssaunit.ap:395"); } } for(pass=1; pass<=5; pass++) { ntracks=5+CHighQualityRand::HQRndUniformI(rs,10); windowwidth=100; nlasttracklen=2*windowwidth; CSSA::SSACreate(state); CSSA::SSASetWindow(state,windowwidth); x=vector::Zeros(nlasttracklen); for(k=0; k::Zeros(windowwidth,2); v=0.0; for(i=0; iskipprob) { CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks); CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:444"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:445"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:446"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:451"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:452"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { CSSA::SSAAnalyzeLast(state,nlasttracklen,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nlasttracklen,"testssaunit.ap:458"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nlasttracklen,"testssaunit.ap:459"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:464"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:465"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:472"); if(errorflag) return; v=x[nlasttracklen-1]-x[nlasttracklen-2]; CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[nlasttracklen-1]-v)>tol,"testssaunit.ap:476"); for(i=1; itol,"testssaunit.ap:478"); } } //--- Test that on specially designed dataset with two sinusoidal components //--- with significantly different amplitudes, whose periods are integer //--- divisors of window width, SSA correctly separates leading sine from //--- its smaller counterpart. Also test that we can correctly predict future //--- values of the sequence. //--- Dataset consists of many sequences, each of them featuring either //--- sine with period=WindowWidth or sine with period=WindowWidth/2. Such //--- dataset is necessary because sum of two sines is not well separated by //--- SVD (it performs only approximate, asymptotic separation). But when //--- every sequence is either one of the sines, but not two together, we //--- can easily separate them. //--- Sine coefficients are changed from sequence to sequence. //--- NOTE: this test requires large WindowWidth and TopK=2 to work. //--- NOTE: this test uses reduced number of passes because of higher computational complexity for(pass=1; pass<=10; pass++) { //--- Skip "precomputed basis" algorithm if(algotype==1) continue; //--- Generate dataset ntracks=100+CHighQualityRand::HQRndUniformI(rs,2); windowwidth=64; nlasttracklen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSACreate(state); x=vector::Zeros(nlasttracklen); for(k=0; k::Zeros(nticks); for(i=0; itol,"testssaunit.ap:565"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:566"); } forecastlen=1+CHighQualityRand::HQRndUniformI(rs,nticks-windowwidth); datalen=nticks-forecastlen; x2=x; x2.Resize(datalen); CSSA::SSAForecastSequence(state,x2,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend); for(i=0; itol,"testssaunit.ap:575"); windowwidth2=-1; CSSA::SSAGetLRR(state,tmp0,windowwidth2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:579"); CAp::SetErrorFlag(errorflag,CAp::Len(tmp0)!=windowwidth-1,"testssaunit.ap:580"); if(errorflag) return; for(i=windowwidth-1; itol,"testssaunit.ap:586"); } nticks=windowwidth+1+CHighQualityRand::HQRndUniformI(rs,windowwidth); sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth); sineamp=1+CHighQualityRand::HQRndUniformR(rs); sinefreq=2; x=vector::Zeros(nticks); for(i=0; itol,"testssaunit.ap:604"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-v)>tol,"testssaunit.ap:605"); } forecastlen=1+CHighQualityRand::HQRndUniformI(rs,nticks-windowwidth); datalen=nticks-forecastlen; x2=x; x2.Resize(datalen); CSSA::SSAForecastSequence(state,x2,datalen,forecastlen,true,trend); for(i=0; itol,"testssaunit.ap:614"); } //--- Test appendPoint() functionality. //--- We have specially designed dataset: //--- * NZeros ticks of exactly zero values //--- * NLinear ticks of series which linearly grow from 0 to 1 //--- * NLinear ticks of series which linearly decrease from 1 to 0 //--- * NZeros ticks of exactly zero values //--- * NZeros=100 //--- * NLinear=20 //--- SSA settings have following values: //--- * WindowWidth=4 or 25 (large widths are more problematic for //--- iterative solvers, so they help to debug incremental updates) //--- * NBasis=2 //--- We choose number of initial values to start with NInitial //--- in [1..2*WindowWidth], then add points one //--- by one with SSAAppendPointAndUpdate(). In the end we compare //--- results returned by SSAGetBasis() with that returned by model //--- which was created from full dataset. //--- NOTE: we perform limited amount of passes because this test //--- has high cost. for(pass=1; pass<=25; pass++) { //--- Generate dataset tol=1.0E-6; nzeros=100; nlinear=50; if(CHighQualityRand::HQRndNormal(rs)>0.0) windowwidth=25; else windowwidth=4; nbasis=2; ninitial=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); x=vector::Zeros(2*nzeros+2*nlinear); for(i=0; i<=nlinear-1; i++) { x.Set(nzeros+i,(double)i/(double)nlinear); x.Set(nzeros+nlinear+i,1-(double)i/(double)nlinear); } CMatGen::RMatrixRndOrthogonal(windowwidth,b); //--- Build model using many sequential appends //--- NOTE: for NInitial>=WindowWidth with probability 50% //--- we enforce basis calculation before first //--- append() call. It helps to debug different //--- branches of algorithms. //--- NOTE: we may also request delayed power-up of the //--- algorithm. It also checks various branches of //--- the algo, although with such settings delayed //--- power-up is hard to check (initial dataset is //--- just zeros, zeros, zeros..). x2=x; x2.Resize(ninitial); CSSA::SSACreate(state); CSSA::SSASetWindow(state,windowwidth); CSSA::SSAAddSequence(state,x2,CAp::Len(x2)); switch(algotype) { case 1: CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,nbasis); break; case 2: CSSA::SSASetAlgoTopKDirect(state,nbasis); break; case 3: CSSA::SSASetAlgoTopKRealtime(state,nbasis); if(CHighQualityRand::HQRndUniformR(rs)>0.5) CSSA::SSASetPowerUpLength(state,10); break; default: CAp::Assert(false); errorflag=true; return; } if(ninitial>=windowwidth && CHighQualityRand::HQRndUniformR(rs)>0.5) { a.Resize(0,0); windowwidth2=-1; nbasis2=-1; CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:705"); CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:706"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:707"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:708"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:709"); if(errorflag) return; } for(i=CAp::Len(x2); itol,"testssaunit.ap:759"); for(j=0; jtol,"testssaunit.ap:764"); } } //--- Test appendSequence() functionality. //--- We have specially designed dataset: //--- * NNoise sequences of Gaussian noise //--- * NLinear linear sequences partially corrupted by noise //--- * NZeros exactly zero sequences (used to let incremental algo converge) //--- * all sequences have random size in [WindowWidth-2,WindowWidth+2] //--- * NNoise=20 //--- * NLinear=20 //--- SSA settings have following values: //--- * WindowWidth=4 or 25 (large widths are more problematic for //--- iterative solvers, so they help to debug incremental updates) //--- * NBasis=2 //--- We have two solvers: //--- * one is trained on complete dataset //--- * another one starts from NNoise noisy sequences, linear/zero sequenes //--- are incrementally appended with AppendSequence //--- NOTE: we perform limited amount of passes because this test //--- has high cost. for(pass=1; pass<=25; pass++) { //--- Problem metrics tol=1.0E-5; nnoise=20; nlinear=20; nzeros=50; if(CHighQualityRand::HQRndNormal(rs)>0.0) windowwidth=25; else windowwidth=4; nbasis=2; //--- Initialize solvers //--- NOTE: we set State.DefaultSubspaceIts to large value in order //--- to ensure convergence to same basis. CMatGen::RMatrixRndOrthogonal(windowwidth,b); CSSA::SSACreate(state); CSSA::SSACreate(state2); state.m_DefaultSubspaceits=50; CSSA::SSASetWindow(state,windowwidth); CSSA::SSASetWindow(state2,windowwidth); switch(algotype) { case 1: CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,nbasis); CSSA::SSASetAlgoPrecomputed(state2,b,windowwidth,nbasis); break; case 2: CSSA::SSASetAlgoTopKDirect(state,nbasis); CSSA::SSASetAlgoTopKDirect(state2,nbasis); break; case 3: CSSA::SSASetAlgoTopKRealtime(state,nbasis); CSSA::SSASetAlgoTopKRealtime(state2,nbasis); break; default: CAp::Assert(false); errorflag=true; return; } //--- Feed noisy sequences for(i=0; i::Zeros(k); for(j=0; j0.5) { a2.Resize(0,0); windowwidth2=-1; nbasis2=-1; CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:865"); CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:866"); CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:867"); CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:868"); CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:869"); if(errorflag) return; } for(i=0; i<=nlinear-1; i++) { k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2); v=CHighQualityRand::HQRndNormal(rs); vv=CHighQualityRand::HQRndNormal(rs); x=vector::Zeros(k); x.Set(0,v); for(j=1; j::Zeros(k); CSSA::SSAAddSequence(state,x,CAp::Len(x)); CSSA::SSAAppendSequenceAndUpdate(state2,x,CAp::Len(x),1.0); } //--- Compare results a.Resize(0,0); windowwidth2=-1; nbasis2=-1; CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:903"); CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:904"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:905"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:906"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:907"); if(errorflag) return; a2.Resize(0,0); windowwidth2=-1; nbasis2=-1; CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:915"); CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:916"); CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:917"); CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:918"); CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:919"); if(errorflag) return; for(i=0; itol,"testssaunit.ap:923"); for(j=0; jtol,"testssaunit.ap:928"); } } //--- Test memory limit functionality. //--- Compare results obtained with HUGE limit vs ones obtained with small limit. for(windowwidth=1; windowwidth<=20; windowwidth++) { for(mlimit=-1; mlimit<=16; mlimit++) { //--- Not tested if(algotype==1) continue; //--- Problem metrics tol=1.0E-5; nticks=1000+CHighQualityRand::HQRndUniformI(rs,1000); if(windowwidth>5) nbasis=1+CHighQualityRand::HQRndUniformI(rs,5); else nbasis=1; //--- Create solvers, dataset, set limits CSSA::SSACreate(state); CSSA::SSACreate(state2); if(mlimit>=0) CSSA::SSASetMemoryLimit(state,(int)MathRound(MathPow(2,mlimit))); else CSSA::SSASetMemoryLimit(state,0); CSSA::SSASetMemoryLimit(state2,999999999); CSSA::SSASetWindow(state,windowwidth); CSSA::SSASetWindow(state2,windowwidth); if(algotype==2) { CSSA::SSASetAlgoTopKDirect(state,nbasis); CSSA::SSASetAlgoTopKDirect(state2,nbasis); } else { if(algotype==3) { CSSA::SSASetAlgoTopKRealtime(state,nbasis); CSSA::SSASetAlgoTopKRealtime(state2,nbasis); } else { CAp::Assert(false); errorflag=true; return; } } x=vector::Zeros(nticks); for(i=0; itol,"testssaunit.ap:1001"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-noise2[i])>tol,"testssaunit.ap:1002"); } //--- Additional tests for sizes of internal arrays. //--- Implementation-dependent, may fail due to future changes in the core. if(mlimit>=0) { k=MathMax(4*windowwidth*windowwidth,(int)MathRound(MathPow(2,mlimit))); CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_UxBatch)*CAp::Rows(state.m_UxBatch)>k,"testssaunit.ap:1012"); CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_AseqTrajectory)*CAp::Rows(state.m_AseqTrajectory)>k,"testssaunit.ap:1013"); CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_AseqTbProduct)*CAp::Rows(state.m_AseqTbProduct)>k,"testssaunit.ap:1014"); } else { CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_UxBatch)!=nticks-windowwidth+1,"testssaunit.ap:1018"); CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_AseqTrajectory)!=nticks-windowwidth+1,"testssaunit.ap:1019"); CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_AseqTbProduct)!=nticks-windowwidth+1,"testssaunit.ap:1020"); } } } } //--- Test power-up ability of the real-time algorithm. //--- We have specially designed dataset: //--- * NTicks ticks of linearly descending from 1 to 0 linear trend, //--- corrupted by random Gaussian noise //--- * NZeros ticks of exactly zero values //--- * NTicks=100 //--- * NZeros=100 //--- SSA settings have following values: //--- * WindowWidth=4 or 25 (large widths are more problematic for //--- iterative solvers, so they help to debug incremental updates) //--- * NBasis=1..2 (only top vectors converge stable enough for unit testing) //--- * powerup length is 10 //--- We perform two SSAs: //--- * one with full dataset and no powerup //--- * one with powerup, NTicks+WindowWidth first elements, followed by appending of NZeros zeros //--- We check that: //--- * basis found by second model (right after initialization) is different //--- from basis of the first one //--- * basis found by second model after last append is same as the first one //--- NOTE: we perform limited amount of passes because this test //--- has high cost. for(pass=1; pass<=25; pass++) { //--- Generate dataset tol=1.0E-3; nticks=100; nzeros=50; if(CHighQualityRand::HQRndNormal(rs)>0.0) windowwidth=25; else windowwidth=4; nbasis=1+CHighQualityRand::HQRndUniformI(rs,2); x=vector::Zeros(nticks+nzeros); for(i=0; i0.5) { a.Resize(0,0); windowwidth2=-1; nbasis2=-1; CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1120"); CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1121"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1122"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:1123"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:1124"); if(errorflag) return; vv=0.0; for(i=0; itol,"testssaunit.ap:1161"); } //--- Test that SSAForecastAvgLast/Sequence() actually performs averaging. //--- We test it by comparing its results vs manually averaged predictions. //--- Dataset is a small linear trend + Gaussian noise. We are not interested //--- in getting meaningful components, we just want to check correctness of CMath:: for(pass=1; pass<=100; pass++) { nticks=75+CHighQualityRand::HQRndUniformI(rs,75); windowwidth=5+CHighQualityRand::HQRndUniformI(rs,5); navg=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); CSSA::SSACreate(state); CSSA::SSASetWindow(state,windowwidth); x=vector::Zeros(nticks); for(i=0; i::Zeros(forecastlen); for(i=0; i(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1198"); nticks=75+CHighQualityRand::HQRndUniformI(rs,75); j=CHighQualityRand::HQRndUniformI(rs,150)-75; x2=vector::Zeros(nticks); for(i=0; i::Zeros(forecastlen); for(i=0; i(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1217"); nticks=75+CHighQualityRand::HQRndUniformI(rs,75); j=CHighQualityRand::HQRndUniformI(rs,150)-75; x2=vector::Zeros(nticks); for(i=0; i::Zeros(forecastlen); for(i=0; i(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1236"); } } //+------------------------------------------------------------------+ //| This function tests different special cases(mostly - degenerate | //| ones). | //| On failure sets ErrorFlag, on success it is untouched. | //+------------------------------------------------------------------+ void CTestSSAUnit::TestSpecial(bool&errorflag) { //--- create variables int i=0; int j=0; int k=0; double v=0; int pass=0; CSSAModel state; CSSAModel state2; CMatrixDouble a; CMatrixDouble b; CRowDouble x; CRowDouble x2; CRowDouble sv; CRowDouble trend; CRowDouble noise; CRowDouble trend2; CRowDouble noise2; int algotype=0; int nticks=0; int nanalyzed=0; int nlasttracklen=0; int ntracks=0; int maxtracklen=0; int mintracklen=0; int datalen=0; int forecastlen=0; int windowwidth=0; int nbasis=0; int windowwidth2=0; CMatrixDouble tracksmatrix; CRowInt trackssizes; int passcount=0; double skipprob=0; int i_=0; CHighQualityRandState rs; //--- Initialize RNG, test pass count and skip probability. //--- When we perform several sequential tests on the same model, we may //--- skip some of them with probability SkipProb in order to make sure //--- that no carry-over effect is observed between tests. CHighQualityRand::HQRndRandomize(rs); passcount=500; skipprob=0.50; //--- Test that for empty model in default state: //--- * SSAGetBasis() returns zero 1x1 basis //--- * SSAAnalyzeLastWindow() returns zeros as trend/noise //--- * SSAAnalyzeLast() returns zeros as trend/noise //--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise //--- * SSAForecastLast() returns zero trend //--- * SSAForecastSequence() returns zero trend for(pass=1; pass<=passcount; pass++) { CSSA::SSACreate(state); if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis); CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1299"); CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1300"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=1,"testssaunit.ap:1301"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1302"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1303"); for(i=0; iskipprob) { windowwidth=-1; CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth); CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1316"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=1 || trend[0]!=0.0,"testssaunit.ap:1317"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=1 || noise[0]!=0.0,"testssaunit.ap:1318"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAAnalyzeLast(state,nticks,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1326"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1327"); if(errorflag) return; for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(nticks); for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1360"); if(errorflag) return; for(i=0; iskipprob) { datalen=1+CHighQualityRand::HQRndUniformI(rs,10); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); for(i=0; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1375"); if(errorflag) return; for(i=0; iskipprob) { CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis); CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1401"); CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1402"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1403"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1404"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1405"); for(i=0; iskipprob) { windowwidth=-1; CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth); CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1418"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth2,"testssaunit.ap:1419"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth2,"testssaunit.ap:1420"); for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAAnalyzeLast(state,nticks,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1432"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1433"); if(errorflag) return; for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(nticks); for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1466"); if(errorflag) return; for(i=0; iskipprob) { datalen=1+CHighQualityRand::HQRndUniformI(rs,10); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); for(i=0; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1481"); if(errorflag) return; for(i=0; i::Zeros(ntracks,2*windowwidth2); trackssizes.Resize(ntracks); for(k=0; k::Zeros(nlasttracklen); for(i=0; iskipprob) { CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis); CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1532"); CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1533"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1534"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1535"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1536"); for(i=0; iskipprob) { windowwidth=-1; CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth); CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1549"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth2,"testssaunit.ap:1550"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth2,"testssaunit.ap:1551"); if(errorflag) return; for(i=0; iskipprob) { nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1567"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1568"); if(errorflag) return; for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(nticks); for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1602"); if(errorflag) return; for(i=0; iskipprob) { datalen=1+CHighQualityRand::HQRndUniformI(rs,10); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); for(i=0; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1617"); if(errorflag) return; for(i=0; i::Zeros(10); for(i=0; i(1.0E-5),"testssaunit.ap:1675"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-noise2[i])>(1.0E-5),"testssaunit.ap:1676"); } } //--- Test that for model with some algo being set, one/few tracks and unit window length: //--- * SSAGetBasis() returns 1x1 basis (unit matrix) //--- * SSAAnalyzeLastWindow() returns last element of track as trend and zeros //--- as noise (with minor rounding error) //--- * SSAAnalyzeLast() returns tracks as trend and zeros as noise (with //--- minor rounding error possible). For NTicks>TrackLength result is correctly //--- prepended with zeros. //--- * SSAAnalyzeSequence() returns sequence as trend, zeros as noise (up to //--- machine precision) //--- * SSAForecastLast() returns just copies of the last element //--- * SSAForecastSequence() returns just copies of the last element for(pass=1; pass<=passcount; pass++) { //--- Generate task; last track is stored in X, its length in NLastTrackLen nlasttracklen=-999999; maxtracklen=10; ntracks=1+CHighQualityRand::HQRndUniformI(rs,3); CSSA::SSACreate(state); CSSA::SSASetWindow(state,1); tracksmatrix=matrix::Zeros(ntracks,maxtracklen); trackssizes.Resize(ntracks); for(k=0; k::Zeros(nlasttracklen); for(i=0; i::Zeros(1,1); b.Set(0,0,2*CHighQualityRand::HQRndUniformI(rs,2)-1); CSSA::SSASetAlgoPrecomputed(state,b,1,1); break; case 2: CSSA::SSASetAlgoTopKDirect(state,1); break; case 3: CSSA::SSASetAlgoTopKRealtime(state,1); break; default: CAp::Assert(false); errorflag=true; return; } //--- Test if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis); CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1736"); CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1737"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=1,"testssaunit.ap:1738"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1739"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1740"); CAp::SetErrorFlag(errorflag,MathAbs(MathAbs(a.Get(0,0))-1)>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1741"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { windowwidth=-1; CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth); CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1749"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=1,"testssaunit.ap:1750"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=1,"testssaunit.ap:1751"); if(errorflag) return; CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[nlasttracklen-1])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1754"); CAp::SetErrorFlag(errorflag,MathAbs(noise[0])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1755"); } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1763"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1764"); if(errorflag) return; for(i=0; i(100.0*CMath::m_machineepsilon),"testssaunit.ap:1774"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1775"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); x2=vector::Zeros(nticks); for(i=0; i(100.0*CMath::m_machineepsilon),"testssaunit.ap:1793"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1794"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1802"); if(errorflag) return; for(i=0; iskipprob) { datalen=1+CHighQualityRand::HQRndUniformI(rs,10); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); for(i=0; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1817"); if(errorflag) return; for(i=0; iTrackLength result is correctly //--- prepended with zeros. //--- * SSAAnalyzeSequence() returns: //--- * for sequences with length at least window width - sequence as trend, //--- zeros as noise (up to machine precision) //--- * for sequences shorter than window width - zeros as trend, //--- sequence as noise (exactly) //--- * SSAForecastLast() returns just copies of the last element //--- * SSAForecastSequence() returns just copies of the last element for long enough //--- sequences, zeros for shorter sequences for(pass=1; pass<=passcount; pass++) { //--- Generate task; last track is stored in X, its length in NLastTrackLen windowwidth=1+CHighQualityRand::HQRndUniformI(rs,5); ntracks=1+CHighQualityRand::HQRndUniformI(rs,3); mintracklen=windowwidth; maxtracklen=mintracklen+2*windowwidth; CSSA::SSACreate(state); CSSA::SSASetWindow(state,windowwidth); tracksmatrix=matrix::Zeros(ntracks,maxtracklen); trackssizes.Resize(ntracks); nlasttracklen=0; for(k=0; k::Zeros(nlasttracklen); for(i=0; iskipprob) { CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1888"); CAp::SetErrorFlag(errorflag,nbasis!=windowwidth,"testssaunit.ap:1889"); CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1890"); CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=windowwidth,"testssaunit.ap:1891"); CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=windowwidth,"testssaunit.ap:1892"); for(i=0; i(100.0*CMath::m_machineepsilon),"testssaunit.ap:1899"); } } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { windowwidth2=-1; CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1908"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:1909"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:1910"); if(errorflag) return; for(i=0; i(100.0*CMath::m_machineepsilon),"testssaunit.ap:1915"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1916"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,2*maxtracklen); CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1925"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1926"); if(errorflag) return; for(i=0; i(100.0*CMath::m_machineepsilon),"testssaunit.ap:1936"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1937"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); x2=vector::Zeros(nticks); for(i=0; i=windowwidth) { CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1957"); CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1958"); } else { CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1962"); CAp::SetErrorFlag(errorflag,(double)(MathAbs(noise[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1963"); } } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1972"); if(errorflag) return; for(i=0; iskipprob) { if(windowwidth>2) { datalen=1+CHighQualityRand::HQRndUniformI(rs,windowwidth-1); forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10); x=vector::Zeros(datalen); for(i=0; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1990"); if(errorflag) return; for(i=0; i::Zeros(datalen); for(i=0; i0.0,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:2005"); if(errorflag) return; for(i=0; i::Zeros(nlasttracklen); for(i=0; i::Zeros(nlasttracklen); for(i=0; iskipprob) { windowwidth2=-1; CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth2); CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:2078"); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:2079"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:2080"); if(errorflag) return; for(i=0; i=0) CAp::SetErrorFlag(errorflag,noise[i]!=x[j],"testssaunit.ap:2088"); else CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:2090"); } } if(CHighQualityRand::HQRndUniformR(rs)>skipprob) { nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth); CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:2099"); CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:2100"); if(errorflag) return; for(i=0; iskipprob) { nticks=1+CHighQualityRand::HQRndUniformI(rs,10); CSSA::SSAForecastLast(state,nticks,trend); CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:2119"); if(errorflag) return; for(i=0; i0.0) { iscls=true; nout=2+CHighQualityRand::HQRndUniformI(rs,3); ny=1; } else { iscls=false; nout=1+CHighQualityRand::HQRndUniformI(rs,3); ny=nout; } k=1+CHighQualityRand::HQRndUniformI(rs,3); eps=CHighQualityRand::HQRndUniformI(rs,2)*0.1*CHighQualityRand::HQRndUniformR(rs); //--- Initialize arrays and data: training and test sets npoints=10+CHighQualityRand::HQRndUniformI(rs,100); xy=matrix::Zeros(npoints,nvars+ny); for(i=0; i::Zeros(testnpoints,nvars+ny); for(i=0; i::Zeros(nvars); for(i=0; i::Zeros(nout+1); CKNN::KNNProcess(model1,x1,y1); CAp::SetErrorFlag(err,CAp::Len(y1)!=nout+1,"testknnunit.ap:200"); y1.Resize(nout-1); CKNN::KNNProcess(model1,x1,y1); CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:203"); y1=vector::Zeros(nout+1); CKNN::KNNTsProcess(model1,buf,x1,y1); CAp::SetErrorFlag(err,CAp::Len(y1)!=nout+1,"testknnunit.ap:207"); y1.Resize(nout-1); CKNN::KNNTsProcess(model1,buf,x1,y1); CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:210"); y1=vector::Zeros(nout+1); CKNN::KNNProcessI(model1,x1,y1); CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:214"); y1.Resize(nout-1); CKNN::KNNProcessI(model1,x1,y1); CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:217"); if(err) return; //--- KNNProcess(), KNNProcessI() and KNNTsProcess() return same results x1=vector::Zeros(nvars); for(i=0; i(100.0*CMath::m_machineepsilon),"testknnunit.ap:232"); CAp::SetErrorFlag(err,MathAbs(y3[i]-y1[i])>(100.0*CMath::m_machineepsilon),"testknnunit.ap:233"); } //--- Same inputs leads to same outputs x1=vector::Zeros(nvars); x2=vector::Zeros(nvars); y1=vector::Zeros(nout); y2=vector::Zeros(nout); for(i=0; i::Zeros(nvars); x2=vector::Zeros(nvars); y1=vector::Zeros(nout); y2=vector::Zeros(nout); UnsetKNN(modelus); { //--- This code passes data structure through serializers //--- (serializes it to string and loads back) CSerializer _local_serializer; string _local_str; _local_serializer.Alloc_Start(); CKNN::KNNAlloc(_local_serializer,model1); _local_serializer.Alloc_Entry(); _local_serializer.SStart_Str(); CKNN::KNNSerialize(_local_serializer,model1); _local_serializer.Stop(); _local_str=_local_serializer.Get_String(); //--- _local_serializer.Reset(); _local_serializer.UStart_Str(_local_str); CKNN::KNNUnserialize(_local_serializer,modelus); _local_serializer.Stop(); } for(i=0; i(100.0*CMath::m_machineepsilon),"testknnunit.ap:293"); for(i=0; i::Zeros(nvars); x2=vector::Zeros(nvars); y1=vector::Zeros(nout); for(i=0; i(100.0*CMath::m_machineepsilon),"testknnunit.ap:318"); //--- KNNClassify works as expected x1=vector::Zeros(nvars); x2=vector::Zeros(nvars); y1=vector::Zeros(nout); for(i=0; iy1[j],"testknnunit.ap:341"); } else CAp::SetErrorFlag(err,CKNN::KNNClassify(model1,x2)!=-1,"testknnunit.ap:344"); //--- Normalization properties if(iscls) { for(i=0; i(1000.0*CMath::m_machineepsilon),"testknnunit.ap:361"); } //--- Test various error metrics reported by algorithm: //--- * training set errors included in the report //--- * errors on the test set //--- NOTE: cross-entropy metric is tested only for training set. //--- because model can produce exactly zero probabilities //--- for completely unknown values TestSetErrors(model1,nvars,nout,iscls,xy,npoints,refavgce,refcls0,refcls1,refrms,refavg,refavgrel); if(iscls) { CAp::SetErrorFlag(err,MathAbs(rep.m_AvgCE-refavgce)>1.0E-6,"testknnunit.ap:377"); CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgCE(model1,xy,npoints)-refavgce)>1.0E-6,"testknnunit.ap:378"); CAp::SetErrorFlag(err,rep.m_RelCLSError<(refcls0-1.0E-6),"testknnunit.ap:381"); CAp::SetErrorFlag(err,rep.m_RelCLSError>(refcls1+1.0E-6),"testknnunit.ap:382"); CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)<(refcls0-1.0E-6),"testknnunit.ap:383"); CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)>(refcls1+1.0E-6),"testknnunit.ap:384"); } else { CAp::SetErrorFlag(err,rep.m_AvgCE!=0.0,"testknnunit.ap:389"); CAp::SetErrorFlag(err,CKNN::KNNAvgCE(model1,xy,npoints)!=0.0,"testknnunit.ap:390"); CAp::SetErrorFlag(err,rep.m_RelCLSError!=0.0,"testknnunit.ap:393"); CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)!=0.0,"testknnunit.ap:394"); } CAp::SetErrorFlag(err,MathAbs(rep.m_RMSError-refrms)>1.0E-6,"testknnunit.ap:396"); CAp::SetErrorFlag(err,MathAbs(rep.m_AvgError-refavg)>1.0E-6,"testknnunit.ap:397"); CAp::SetErrorFlag(err,MathAbs(rep.m_AvgRelError-refavgrel)>1.0E-6,"testknnunit.ap:398"); CAp::SetErrorFlag(err,MathAbs(CKNN::KNNRMSError(model1,xy,npoints)-refrms)>1.0E-6,"testknnunit.ap:399"); CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgError(model1,xy,npoints)-refavg)>1.0E-6,"testknnunit.ap:400"); CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgRelError(model1,xy,npoints)-refavgrel)>1.0E-6,"testknnunit.ap:401"); TestSetErrors(model1,nvars,nout,iscls,testxy,testnpoints,refavgce,refcls0,refcls1,refrms,refavg,refavgrel); CKNN::KNNAllErrors(model1,testxy,testnpoints,tstrep); if(iscls) { CAp::SetErrorFlag(err,tstrep.m_RelCLSError<(refcls0-1.0E-6),"testknnunit.ap:406"); CAp::SetErrorFlag(err,tstrep.m_RelCLSError>(refcls1+1.0E-6),"testknnunit.ap:407"); } else { CAp::SetErrorFlag(err,tstrep.m_AvgCE!=0.0,"testknnunit.ap:411"); CAp::SetErrorFlag(err,tstrep.m_RelCLSError!=0.0,"testknnunit.ap:412"); } CAp::SetErrorFlag(err,MathAbs(tstrep.m_RMSError-refrms)>1.0E-6,"testknnunit.ap:414"); CAp::SetErrorFlag(err,MathAbs(tstrep.m_AvgError-refavg)>1.0E-6,"testknnunit.ap:415"); CAp::SetErrorFlag(err,MathAbs(tstrep.m_AvgRelError-refavgrel)>1.0E-6,"testknnunit.ap:416"); } //--- Test that model built with K=1 and Eps=0 remembers all training cases for(nvars=1; nvars<=4; nvars++) { for(pass=0; pass<=10; pass++) { //--- Prepare task npoints=1+CHighQualityRand::HQRndUniformI(rs,20); if(CHighQualityRand::HQRndNormal(rs)>0.0) { iscls=true; nout=2+CHighQualityRand::HQRndUniformI(rs,3); ny=1; } else { iscls=false; nout=1+CHighQualityRand::HQRndUniformI(rs,3); ny=nout; } xy=matrix::Zeros(npoints,nvars+ny); for(i=0; i0.0,"testknnunit.ap:467"); CAp::SetErrorFlag(err,rep.m_RelCLSError>0.0,"testknnunit.ap:468"); CAp::SetErrorFlag(err,rep.m_RMSError>1.0E-6,"testknnunit.ap:469"); CAp::SetErrorFlag(err,rep.m_AvgError>1.0E-6,"testknnunit.ap:470"); CAp::SetErrorFlag(err,rep.m_AvgRelError>1.0E-6,"testknnunit.ap:471"); x1=vector::Zeros(nvars); for(i=0; i(10.0*CMath::m_machineepsilon),"testknnunit.ap:479"); else { for(j=0; j(10.0*CMath::m_machineepsilon),"testknnunit.ap:483"); } } } } //--- Test that model rewrite works as expected for(nvars=1; nvars<=20; nvars++) { //--- Prepare task npoints=50+5*nvars; if(CHighQualityRand::HQRndNormal(rs)>0.0) { iscls=true; nout=2+CHighQualityRand::HQRndUniformI(rs,3); ny=1; } else { iscls=false; nout=1+CHighQualityRand::HQRndUniformI(rs,3); ny=nout; } xy=matrix::Zeros(npoints,nvars+ny); for(i=0; i::Zeros(nvars); x2=vector::Zeros(nvars); for(i=0; i(1000.0*CMath::m_machineepsilon),"testknnunit.ap:553"); } //--- Test generalization ability on a simple noisy classification task: //--- * 0::Zeros(npoints,2); for(i=0; i::Zeros(1); while(x1[0]<=3.0) { CKNN::KNNProcess(model1,x1,y1); CAp::SetErrorFlag(err,y1[0]<0.0,"testknnunit.ap:606"); CAp::SetErrorFlag(err,y1[1]<0.0,"testknnunit.ap:607"); CAp::SetErrorFlag(err,MathAbs(y1[0]+y1[1]-1)>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:608"); if(x1[0]<1.0) CAp::SetErrorFlag(err,y1[0]<0.8,"testknnunit.ap:610"); if(x1[0]>=1.0 && x1[0]<=2.0) CAp::SetErrorFlag(err,MathAbs(y1[1]-(x1[0]-1))>0.5,"testknnunit.ap:612"); if(x1[0]>2.0) CAp::SetErrorFlag(err,y1[1]<0.8,"testknnunit.ap:614"); x1.Add(0,0.01); } } //--- Test simple regression task without noise: //--- * |x|<1, |y|<1 //--- * F(x,y) = x^2+y //--- A model with K=50 is used, we check that //--- * Eps=0 is good enough //--- * having Eps>0 results in worse error metrics, but not too bad //--- Prepare task npoints=5000; xy=matrix::Zeros(npoints,3); for(i=0; i::Zeros(2); maxerr=0; avgerr=0; cnt=0; for(i=0; i<=1000; i++) { //--- NOTE: we test model in the inner points, it deteriorates near the bounds x1.Set(0,0.8*(2*CHighQualityRand::HQRndUniformR(rs)-1)); x1.Set(1,0.8*(2*CHighQualityRand::HQRndUniformR(rs)-1)); v=CMath::Sqr(x1[0])+x1[1]; CKNN::KNNProcess(model1,x1,y1); v=MathAbs(y1[0]-v); maxerr=MathMax(maxerr,v); avgerr+=v; cnt++; } avgerr/=cnt; CAp::SetErrorFlag(err,maxerr>0.15,"testknnunit.ap:669"); CAp::SetErrorFlag(err,avgerr>0.05,"testknnunit.ap:670"); //--- Build model with Eps=1.0, compare error metrics with Eps=0 CKNN::KNNBuilderBuildKNNModel(builder,k,1.0,model2,rep2); CAp::SetErrorFlag(err,rep2.m_RMSError<=rep.m_RMSError,"testknnunit.ap:676"); CAp::SetErrorFlag(err,rep2.m_RMSError<=(rep.m_RMSError+0.001),"testknnunit.ap:677"); CAp::SetErrorFlag(err,rep2.m_RMSError>=(rep.m_RMSError+0.020),"testknnunit.ap:678"); } //+------------------------------------------------------------------+ //| Unsets model | //+------------------------------------------------------------------+ void CTestKNNUnit::UnsetKNN(CKNNModel &model) { //--- create variables CMatrixDouble xy; CKNNBuilder builder; CKNNReport rep; xy=matrix::Zeros(1,2); xy.Set(0,1,CMath::RandomReal()-0.5); CKNN::KNNBuilderCreate(builder); CKNN::KNNBuilderSetDatasetReg(builder,xy,1,1,1); CKNN::KNNBuilderBuildKNNModel(builder,1,0,model,rep); model.m_nvars=-1; model.m_nout=-1; model.m_k=-1; model.m_eps=0; model.m_isdummy=true; } //+------------------------------------------------------------------+ //| Test set errors. | //| Computes test set errors: | //| * average cross-entropy | //| * relative classification error (a range [RelCls0,RelCls1] is | //| returned because classification error can be computed | //| differently in the presence of the ties). All possible values| //| of the classification error, no matter how ties are resolved,| //| are guaranteed to be within [RelCls0,RelCls1]. | //+------------------------------------------------------------------+ void CTestKNNUnit::TestSetErrors(CKNNModel &model,int nvars, int nout,bool iscls, CMatrixDouble &xy,int npoints, double &avgce,double &relcls0, double &relcls1,double &rms, double &avg,double &avgrel) { //--- create variables CRowDouble x; CRowDouble y; CRowDouble ey; int relcnt=0; int i=0; int j=0; double v=0; double mxy=0; avgce=0; relcls0=0; relcls1=0; rms=0; avg=0; avgrel=0; x=vector::Zeros(nvars); y=vector::Zeros(nout); ey=vector::Zeros(nout); avgce=0; relcls0=0; relcls1=0; rms=0; avg=0; avgrel=0; for(i=0; i::Zeros(1,2); xy.Set(0,0,CMath::RandomReal()); xy.Set(0,1,CMath::RandomReal()); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,1,2,0); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=1) return(result); //--- Test on problem with two points xy=matrix::Zeros(2,2); xy.Set(0,0,CMath::RandomReal()); xy.Set(0,1,CMath::RandomReal()); xy.Set(1,0,CMath::RandomReal()); xy.Set(1,1,CMath::RandomReal()); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,2,2,0); CClustering::ClusterizerRunAHC(s,rep); if((rep.m_NPoints!=2 || CAp::Rows(rep.m_z)!=1) || CAp::Cols(rep.m_z)!=2) return(result); if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1) return(result); //--- Test on specially designed problem which should have //--- following dendrogram: //--- ------ //--- | | //--- ---- ---- //--- | | | | //--- 0 1 2 3 //--- ...with first merge performed on 0 and 1, second merge //--- performed on 2 and 3. Complete linkage is used. //--- Additionally we test ClusterizerSeparatedByDist() on this //--- problem for different distances. Test is performed by //--- comparing function result with ClusterizerGetKClusters() //--- for known K. xy=matrix::Zeros(4,1); xy.Set(1,0,1.0); xy.Set(2,0,3.0); xy.Set(3,0,4.1); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,4,1,0); CClustering::ClusterizerSetAHCAlgo(s,0); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=4 || CAp::Rows(rep.m_z)!=3 || CAp::Cols(rep.m_z)!=2 || CAp::Rows(rep.m_pz)!=3 || CAp::Cols(rep.m_pz)!=2) return(result); berr=false; berr=(berr||rep.m_z.Get(0,0)!=0||rep.m_z.Get(0,1)!=1); berr=(berr||rep.m_z.Get(1,0)!=2||rep.m_z.Get(1,1)!=3); berr=(berr||rep.m_z.Get(2,0)!=4||rep.m_z.Get(2,1)!=5); berr=(berr||rep.m_p[0]!=0||rep.m_p[1]!=1||rep.m_p[2]!=2||rep.m_p[3]!=3); berr=(berr||rep.m_pz.Get(0,0)!=0||rep.m_pz.Get(0,1)!=1); berr=(berr||rep.m_pz.Get(1,0)!=2||rep.m_pz.Get(1,1)!=3); berr=(berr||rep.m_pz.Get(2,0)!=4||rep.m_pz.Get(2,1)!=5); berr=(berr||rep.m_pm.Get(0,0)!=0||rep.m_pm.Get(0,1)!=0||rep.m_pm.Get(0,2)!=1||rep.m_pm.Get(0,3)!=1); berr=(berr||rep.m_pm.Get(1,0)!=2||rep.m_pm.Get(1,1)!=2||rep.m_pm.Get(1,2)!=3||rep.m_pm.Get(1,3)!=3); berr=(berr||rep.m_pm.Get(2,0)!=0||rep.m_pm.Get(2,1)!=1||rep.m_pm.Get(2,2)!=2||rep.m_pm.Get(2,3)!=3); if(berr) return(result); CClustering::ClusterizerSeparatedByDist(rep,0.5,k,cidx,cz); CClustering::ClusterizerGetKClusters(rep,4,cidx2,cz2); if(k!=4) return(result); if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3]) return(result); if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2] || cz[3]!=cz2[3]) return(result); CClustering::ClusterizerSeparatedByDist(rep,1.05,k,cidx,cz); CClustering::ClusterizerGetKClusters(rep,3,cidx2,cz2); if(k!=3) return(result); if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3]) return(result); if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2]) return(result); CClustering::ClusterizerSeparatedByDist(rep,1.15,k,cidx,cz); CClustering::ClusterizerGetKClusters(rep,2,cidx2,cz2); if(k!=2) return(result); if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3]) return(result); if(cz[0]!=cz2[0] || cz[1]!=cz2[1]) return(result); //--- Test on specially designed problem with Pearson distance //--- which should have following dendrogram: //--- ------ //--- | | //--- ---- ---- //--- | | | | //--- 0 1 2 3 //--- This problem is used to test ClusterizerSeparatedByDist(). //--- The test is performed by comparing function result with //--- ClusterizerGetKClusters() for known K. //--- NOTE: //--- * corr(a0,a1) = 0.866 //--- * corr(a2,a3) = 0.990 //--- * corr(a0/a1, a2/a3)<=0.5 xy=matrix::Zeros(4,3); xy.Set(0,0,0.3); xy.Set(0,1,0.5); xy.Set(0,2,0.3); xy.Set(1,0,0.3); xy.Set(1,1,0.5); xy.Set(1,2,0.4); xy.Set(2,0,0.1); xy.Set(2,1,0.5); xy.Set(2,2,0.9); xy.Set(3,0,0.1); xy.Set(3,1,0.4); xy.Set(3,2,0.9); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,4,3,10); CClustering::ClusterizerSetAHCAlgo(s,1); CClustering::ClusterizerRunAHC(s,rep); CClustering::ClusterizerSeparatedByCorr(rep,0.999,k,cidx,cz); CClustering::ClusterizerGetKClusters(rep,4,cidx2,cz2); if(k!=4) return(result); if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3]) return(result); if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2] || cz[3]!=cz2[3]) return(result); CClustering::ClusterizerSeparatedByCorr(rep,0.900,k,cidx,cz); CClustering::ClusterizerGetKClusters(rep,3,cidx2,cz2); if(k!=3) return(result); if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3]) return(result); if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2]) return(result); CClustering::ClusterizerSeparatedByCorr(rep,0.600,k,cidx,cz); CClustering::ClusterizerGetKClusters(rep,2,cidx2,cz2); if(k!=2) return(result); if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3]) return(result); if(cz[0]!=cz2[0] || cz[1]!=cz2[1]) return(result); //--- Single linkage vs. complete linkage xy=matrix::Zeros(6,1); xy.Set(0,0,0.0); xy.Set(1,0,1.0); xy.Set(2,0,2.1); xy.Set(3,0,3.3); xy.Set(4,0,6.0); xy.Set(5,0,4.6); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,6,1,0); CClustering::ClusterizerSetAHCAlgo(s,0); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=6 || CAp::Len(rep.m_p)!=6) return(result); if(CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2 || CAp::Rows(rep.m_pz)!=5 || CAp::Cols(rep.m_pz)!=2) return(result); berr=false; berr=berr||rep.m_p[0]!=2; berr=berr||rep.m_p[1]!=3; berr=berr||rep.m_p[2]!=4; berr=berr||rep.m_p[3]!=5; berr=berr||rep.m_p[4]!=0; berr=berr||rep.m_p[5]!=1; berr=(berr||rep.m_z.Get(0,0)!=0)||rep.m_z.Get(0,1)!=1; berr=(berr||rep.m_z.Get(1,0)!=2)||rep.m_z.Get(1,1)!=3; berr=(berr||rep.m_z.Get(2,0)!=4)||rep.m_z.Get(2,1)!=5; berr=(berr||rep.m_z.Get(3,0)!=6)||rep.m_z.Get(3,1)!=7; berr=(berr||rep.m_z.Get(4,0)!=8)||rep.m_z.Get(4,1)!=9; berr=(berr||rep.m_pz.Get(0,0)!=2)||rep.m_pz.Get(0,1)!=3; berr=(berr||rep.m_pz.Get(1,0)!=4)||rep.m_pz.Get(1,1)!=5; berr=(berr||rep.m_pz.Get(2,0)!=0)||rep.m_pz.Get(2,1)!=1; berr=(berr||rep.m_pz.Get(3,0)!=6)||rep.m_pz.Get(3,1)!=7; berr=(berr||rep.m_pz.Get(4,0)!=8)||rep.m_pz.Get(4,1)!=9; if(berr) return(result); CClustering::ClusterizerSetAHCAlgo(s,1); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=6 || CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2) return(result); berr=false; berr=(berr||rep.m_z.Get(0,0)!=0)||rep.m_z.Get(0,1)!=1; berr=(berr||rep.m_z.Get(1,0)!=2)||rep.m_z.Get(1,1)!=6; berr=(berr||rep.m_z.Get(2,0)!=3)||rep.m_z.Get(2,1)!=7; berr=(berr||rep.m_z.Get(3,0)!=5)||rep.m_z.Get(3,1)!=8; berr=(berr||rep.m_z.Get(4,0)!=4)||rep.m_z.Get(4,1)!=9; if(berr) return(result); //--- Test which differentiates complete linkage and average linkage from //--- single linkage: //--- * we have cluster C0={(-0.5), (0)}, //--- cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)}, //--- and point P between them - (10.0) //--- * we try three different strategies - single linkage, complete //--- linkage, average linkage. //--- * any strategy will merge C0 first, then merge points of C1, //--- and then merge P with C0 or C1 (depending on linkage type) //--- * we test that: //--- a) C0 is merged first //--- b) after 5 merges (including merge of C0), P is merged with C0 or C1 //--- c) P is merged with C1 when we have single linkage, with C0 otherwise xy=matrix::Zeros(8,1); xy.Set(0,0,-0.5); xy.Set(1,0,0.0); xy.Set(2,0,10.0); xy.Set(3,0,19.0); xy.Set(4,0,20.0); xy.Set(5,0,21.0); xy.Set(6,0,22.0); xy.Set(7,0,23.0); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,8,1,0); for(ahcalgo=0; ahcalgo<=2; ahcalgo++) { CClustering::ClusterizerSetAHCAlgo(s,ahcalgo); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=8 || CAp::Rows(rep.m_z)!=7 || CAp::Cols(rep.m_z)!=2) return(result); if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1) return(result); if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2) return(result); if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2) return(result); if((ahcalgo==0 || ahcalgo==2) && rep.m_z.Get(5,0)!=8 && rep.m_z.Get(5,1)!=8) return(result); if(ahcalgo==1 && (rep.m_z.Get(5,0)==8 || rep.m_z.Get(5,1)==8)) return(result); } //--- Test which differentiates single linkage and average linkage from //--- complete linkage: //--- * we have cluster C0={(-2.5), (-2.0)}, //--- cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)}, //--- and point P between them - (10.0) //--- * we try three different strategies - single linkage, complete //--- linkage, average linkage. //--- * any strategy will merge C0 first, then merge points of C1, //--- and then merge P with C0 or C1 (depending on linkage type) //--- * we test that: //--- a) C0 is merged first //--- b) after 5 merges (including merge of C0), P is merged with C0 or C1 //--- c) P is merged with C0 when we have complete linkage, with C1 otherwise xy=matrix::Zeros(8,1); xy.Set(0,0,-2.5); xy.Set(1,0,-2.0); xy.Set(2,0,10.0); xy.Set(3,0,19.0); xy.Set(4,0,20.0); xy.Set(5,0,21.0); xy.Set(6,0,22.0); xy.Set(7,0,23.0); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,8,1,0); for(ahcalgo=0; ahcalgo<=2; ahcalgo++) { CClustering::ClusterizerSetAHCAlgo(s,ahcalgo); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=8 || CAp::Rows(rep.m_z)!=7 || CAp::Cols(rep.m_z)!=2) return(result); if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1) return(result); if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2) return(result); if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2) return(result); if(ahcalgo==0 && rep.m_z.Get(5,0)!=8 && rep.m_z.Get(5,1)!=8) return(result); if((ahcalgo==1 || ahcalgo==2) && (rep.m_z.Get(5,0)==8 || rep.m_z.Get(5,1)==8)) return(result); } //--- Test which differentiates weighred average linkage from unweighted average linkage: //--- * we have cluster C0={(0.0), (1.5), (2.5)}, //--- cluster C1={(7.5), (7.99)}, //--- and point P between them - (4.5) //--- * we try two different strategies - weighted average linkage and unweighted average linkage //--- * any strategy will merge C1 first, then merge points of C0, //--- and then merge P with C0 or C1 (depending on linkage type) //--- * we test that: //--- a) C1 is merged first, C0 is merged after that //--- b) after first 3 merges P is merged with C0 or C1 //--- c) P is merged with C1 when we have weighted average linkage, with C0 otherwise xy=matrix::Zeros(6,1); xy.Set(0,0,0.0); xy.Set(1,0,1.5); xy.Set(2,0,2.5); xy.Set(3,0,4.5); xy.Set(4,0,7.5); xy.Set(5,0,7.99); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,6,1,0); for(ahcalgo=2; ahcalgo<=3; ahcalgo++) { CClustering::ClusterizerSetAHCAlgo(s,ahcalgo); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=6 || CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2) return(result); if(rep.m_z.Get(0,0)!=4 || rep.m_z.Get(0,1)!=5) return(result); if(rep.m_z.Get(1,0)!=1 || rep.m_z.Get(1,1)!=2) return(result); if(rep.m_z.Get(2,0)!=0 || rep.m_z.Get(2,1)!=7) return(result); if(rep.m_z.Get(3,0)!=3) return(result); if(ahcalgo==2 && rep.m_z.Get(3,1)!=8) return(result); if(ahcalgo==3 && rep.m_z.Get(3,1)!=6) return(result); } //--- Test which checks correctness of Ward's method on very basic problem xy=matrix::Zeros(4,1); xy.Set(0,0,0.0); xy.Set(1,0,1.0); xy.Set(2,0,3.1); xy.Set(3,0,4.0); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,CAp::Rows(xy),CAp::Cols(xy),2); CClustering::ClusterizerGetDistances(xy,CAp::Rows(xy),CAp::Cols(xy),2,d); CClustering::ClusterizerSetAHCAlgo(s,4); CClustering::ClusterizerRunAHC(s,rep); if(ErrorsInMerges(d,xy,CAp::Rows(xy),CAp::Cols(xy),rep,4)) return(result); //--- One more Ward's test xy=matrix::Zeros(8,2); xy.Set(0,0,0.4700566262); xy.Set(0,1,0.4565938448); xy.Set(1,0,0.2394499506); xy.Set(1,1,0.1750209592); xy.Set(2,0,0.6518417019); xy.Set(2,1,0.6151370746); xy.Set(3,0,0.9863942841); xy.Set(3,1,0.7855012189); xy.Set(4,0,0.1517812919); xy.Set(4,1,0.2600174758); xy.Set(5,0,0.7840203638); xy.Set(5,1,0.9023597604); xy.Set(6,0,0.2604194835); xy.Set(6,1,0.9792704661); xy.Set(7,0,0.6353096042); xy.Set(7,1,0.8252606906); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,CAp::Rows(xy),CAp::Cols(xy),2); CClustering::ClusterizerGetDistances(xy,CAp::Rows(xy),CAp::Cols(xy),2,d); CClustering::ClusterizerSetAHCAlgo(s,4); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_z.Get(0,0)!=1 || rep.m_z.Get(0,1)!=4) return(result); if(rep.m_z.Get(1,0)!=5 || rep.m_z.Get(1,1)!=7) return(result); if(rep.m_z.Get(2,0)!=0 || rep.m_z.Get(2,1)!=2) return(result); if(rep.m_z.Get(3,0)!=3 || rep.m_z.Get(3,1)!=9) return(result); if(rep.m_z.Get(4,0)!=10 || rep.m_z.Get(4,1)!=11) return(result); if(rep.m_z.Get(5,0)!=6 || rep.m_z.Get(5,1)!=12) return(result); if(rep.m_z.Get(6,0)!=8 || rep.m_z.Get(6,1)!=13) return(result); if(ErrorsInMerges(d,xy,CAp::Rows(xy),CAp::Cols(xy),rep,4)) return(result); //--- Ability to solve problems with zero distance matrix npoints=20; d=matrix::Zeros(npoints,npoints); for(ahcalgo=0; ahcalgo<=4; ahcalgo++) { CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetDistances(s,d,npoints,true); CClustering::ClusterizerSetAHCAlgo(s,ahcalgo); CClustering::ClusterizerRunAHC(s,rep); if(rep.m_NPoints!=npoints || CAp::Rows(rep.m_z)!=(npoints-1) || CAp::Cols(rep.m_z)!=2) return(result); } //--- Test GetKClusters() xy=matrix::Zeros(8,1); xy.Set(0,0,-2.5); xy.Set(1,0,-2.0); xy.Set(2,0,10.0); xy.Set(3,0,19.0); xy.Set(4,0,20.0); xy.Set(5,0,21.0); xy.Set(6,0,22.0); xy.Set(7,0,23.0); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,8,1,0); CClustering::ClusterizerSetAHCAlgo(s,0); CClustering::ClusterizerRunAHC(s,rep); CClustering::ClusterizerGetKClusters(rep,3,cidx,cz); if(cidx[0]!=1 || cidx[1]!=1 || cidx[2]!=0 || cidx[3]!=2 || cidx[4]!=2 || cidx[5]!=2 || cidx[6]!=2 || cidx[7]!=2) return(result); if(cz[0]!=2 || cz[1]!=8 || cz[2]!=12) return(result); //--- Test is done return(false); } //+------------------------------------------------------------------+ //| Advanced agglomerative hierarchical clustering tests: returns | //| True on failure, False on success. | //| Advanced testing subroutine perform several automatically | //| generated tests. | //+------------------------------------------------------------------+ bool CTestClusteringUnit::AdvancedAHCTests(void) { //--- create variables bool result=false; int euclidean=2; CClusterizerState s; CAHCReport rep; CMatrixDouble xy; CMatrixDouble dm; CMatrixDouble dm2; CRowInt idx; CRowInt disttypes; CRowDouble x0; CRowDouble x1; int d=0; int n=0; int npoints=0; int ahcalgo=0; int disttype=0; int i=0; int j=0; int k=0; double v=0; int t=0; int i_=0; //--- Test on D-dimensional problem: //--- * D = 2...5 //--- * D clusters, each has N points; //--- centers are located at x=(0 ... 1 ... 0); //--- cluster radii are approximately 0.1 //--- * single/complete/unweighted_average/weighted_average linkage/Ward's method are tested //--- * Euclidean distance is used, either: //--- a) one given by distance matrix (ClusterizerSetDistances) //--- b) one calculated from dataset (ClusterizerSetPoints) //--- * we have N*D points, and N*D-1 merges in total //--- * points are randomly rearranged after generation //--- For all kinds of linkage we perform following test: //--- * for each point we remember index of its cluster //--- (one which is determined during dataset generation) //--- * we clusterize points with ALGLIB capabilities //--- * we scan Rep.Z and perform first D*(N-1) merges //--- * for each merge we check that it merges points //--- from same cluster; //--- Additonally, we call ErrorsInMerges(). See function comments //--- for more information about specific tests performed. This function //--- allows us to check that clusters are built exactly as specified by //--- definition of the clustering algorithm. for(d=2; d<=5; d++) { for(ahcalgo=0; ahcalgo<=4; ahcalgo++) { n=(int)MathRound(MathPow(3,CMath::RandomInteger(3))); npoints=d*n; //--- 1. generate dataset. //--- 2. fill Idx (array of cluster indexes): //--- * first N*D elements store cluster indexes //--- * next D*(N-1) elements are filled during merges //--- 3. build distance matrix DM xy=matrix::Zeros(n*d,d); idx.Resize(n*d+d*(n-1)); for(i=0; i::Zeros(npoints,npoints); x0=vector::Zeros(d); x1=vector::Zeros(d); for(i=0; i=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=(d*n+i)) { result=true; return(result); } //--- Check that merge is performed within cluster if(idx[rep.m_z.Get(i,0)]!=idx[rep.m_z.Get(i,1)]) { result=true; return(result); } //--- Write new entry of Idx. //--- Both points from the same cluster, so result of the merge //--- belongs to the same cluster idx.Set(n*d+i,idx[rep.m_z.Get(i,1)]); } if(((ahcalgo==0 || ahcalgo==1) || ahcalgo==2) || ahcalgo==4) { if(ErrorsInMerges(dm,xy,d*n,d,rep,ahcalgo)) { result=true; return(result); } } //--- Clusterize one more time, now with distance matrix CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetDistances(s,dm,n*d,CMath::RandomReal()>0.5); CClustering::ClusterizerSetAHCAlgo(s,ahcalgo); CClustering::ClusterizerRunAHC(s,rep); //--- Tests: //--- * replay first D*(N-1) merges; these merges should take place //--- within clusters, intercluster merges will be performed at the //--- last stages of the processing. //--- * test with ErrorsInMerges() if(rep.m_NPoints!=npoints) { result=true; return(result); } for(i=0; i<(d*(n-1)); i++) { //--- Check correctness of I-th row of Z if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=(d*n+i)) { result=true; return(result); } //--- Check that merge is performed within cluster if(idx[rep.m_z.Get(i,0)]!=idx[rep.m_z.Get(i,1)]) { result=true; return(result); } //--- Write new entry of Idx. //--- Both points from the same cluster, so result of the merge //--- belongs to the same cluster idx.Set(n*d+i,idx[rep.m_z.Get(i,1)]); } if(ahcalgo==0 || ahcalgo==1 || ahcalgo==2 || ahcalgo==4) if(ErrorsInMerges(dm,xy,d*n,d,rep,ahcalgo)) { result=true; return(result); } } } //--- Test on random D-dimensional problem: //--- * D = 2...5 //--- * N=1..16 random points from unit hypercube //--- * single/complete/unweighted_average linkage/Ward's method are tested //--- * different distance functions are tested //--- * we call ErrorsInMerges() and we check distance matrix //--- calculated by unit test against one returned by GetDistances() disttypes.Resize(9); disttypes.Set(0,0); disttypes.Set(1,1); disttypes.Set(2,2); disttypes.Set(3,10); disttypes.Set(4,11); disttypes.Set(5,12); disttypes.Set(6,13); disttypes.Set(7,20); disttypes.Set(8,21); for(disttype=0; disttype::Zeros(npoints,d); for(i=0; i::Zeros(npoints,npoints); x0=vector::Zeros(d); x1=vector::Zeros(d); for(i=0; i(1.0E5*CMath::m_machineepsilon)) { result=true; return(result); } } } //--- return result return(result); } //+------------------------------------------------------------------+ //| Simple test 1: ellipsoid in NF-dimensional space. | //| compare k-means centers with random centers | //+------------------------------------------------------------------+ void CTestClusteringUnit::KMeansSimpleTest1(int nvars,int nc, int passcount, bool &converrors, bool &othererrors, bool &simpleerrors) { //--- create variables int npoints=nc*100; int restarts=5; int majoraxis=0; CMatrixDouble xy; CRowDouble tmp; double v=0; int i=0; int j=0; int pass=0; double ekmeans=0; double erandom=0; double dclosest=0; int cclosest=0; CClusterizerState s; CKmeansReport rep; int i_=0; passcount=10; tmp=vector::Zeros(nvars); for(pass=1; pass<=passcount; pass++) { //--- Fill xy=matrix::Zeros(npoints,nvars); majoraxis=CMath::RandomInteger(nvars); for(i=0; i::Zeros(4); passcount=1000; npoints=100; nfeatures=3; nclusters=6; xy=matrix::Zeros(npoints,nfeatures); c=matrix::Zeros(nclusters,nfeatures); CClustering::ClusterizerCreate(s); s.m_kmeansdbgnoits=true; for(separation=2; separation<=5; separation++) { //--- Try different init algorithms for(initalgo=1; initalgo<=3; initalgo++) { energies.Set(initalgo,0.0); CClustering::ClusterizerSetKMeansInit(s,initalgo); for(pass=1; pass<=passcount; pass++) { //--- Generate centers of balls for(i=0; i::Zeros(npoints,nfeatures); for(i=0; i::Zeros(nclusters,nfeatures); for(i=0; i::Zeros(npoints); xydist2ref=vector::Zeros(npoints); //--- Test CClustering::KMeansUpdateDistances(xy,0,npoints,nfeatures,c,0,nclusters,xyc,xydist2); KMeansReferenceUpdateDistances(xy,npoints,nfeatures,c,nclusters,xycref,xydist2ref); for(i=0; i(1.0E-6),"testclusteringunit.ap:1138"); } } } } //--- Test degenerate dataset (less than NClusters distinct points) for(nclusters=2; nclusters<=10; nclusters++) { for(initalgo=0; initalgo<=3; initalgo++) { for(pass=1; pass<=10; pass++) { //--- Initialize points. Two algorithms are used: //--- * initialization by small integers (no rounding problems) //---*initialization by "long" fraction npoints=100; nfeatures=10; xy=matrix::Zeros(npoints,nfeatures); if(CHighQualityRand::HQRndNormal(rs)>0.0) { for(i=0; i<=nclusters-2; i++) for(j=0; j<=nfeatures-1; j++) xy.Set(i,j,MathSin(CHighQualityRand::HQRndNormal(rs))); } else { for(i=0; i::Zeros(npoints,nfeatures); for(i=0; i1 we must get failure. npoints=100; nfeatures=1; restarts=5; xy=matrix::Zeros(npoints,nfeatures); for(j=0; j(1000*CMath::m_machineepsilon)); for(i=0; i0; //--- Problem 2: degenerate dataset (report by Andreas). npoints=57; nfeatures=1; restarts=1; nclusters=4; xy=matrix::Zeros(npoints,nfeatures); xy.Set(0,0,12.244689632138986); xy.Set(1,0,12.244689632138982); xy.Set(2,0,12.244689632138986); xy.Set(3,0,12.244689632138982); xy.Set(4,0,12.244689632138986); xy.Set(5,0,12.244689632138986); xy.Set(6,0,12.244689632138986); xy.Set(7,0,12.244689632138986); xy.Set(8,0,12.244689632138986); xy.Set(9,0,12.244689632138986); xy.Set(10,0,12.244689632138989); xy.Set(11,0,12.244689632138984); xy.Set(12,0,12.244689632138986); xy.Set(13,0,12.244689632138986); xy.Set(14,0,12.244689632138989); xy.Set(15,0,12.244689632138986); xy.Set(16,0,12.244689632138986); xy.Set(17,0,12.244689632138986); xy.Set(18,0,12.244689632138986); xy.Set(19,0,12.244689632138989); xy.Set(20,0,12.244689632138972); xy.Set(21,0,12.244689632138986); xy.Set(22,0,12.244689632138986); xy.Set(23,0,12.244689632138986); xy.Set(24,0,12.244689632138984); xy.Set(25,0,12.244689632138982); xy.Set(26,0,12.244689632138986); xy.Set(27,0,12.244689632138986); xy.Set(28,0,12.244689632138986); xy.Set(29,0,12.244689632138986); xy.Set(30,0,12.244689632138986); xy.Set(31,0,12.244689632138986); xy.Set(32,0,12.244689632138986); xy.Set(33,0,12.244689632138986); xy.Set(34,0,12.244689632138986); xy.Set(35,0,12.244689632138982); xy.Set(36,0,12.244689632138989); xy.Set(37,0,12.244689632138986); xy.Set(38,0,12.244689632138986); xy.Set(39,0,12.244689632138986); xy.Set(40,0,12.244689632138986); xy.Set(41,0,12.244689632138986); xy.Set(42,0,12.244689632138986); xy.Set(43,0,12.244689632138986); xy.Set(44,0,12.244689632138986); xy.Set(45,0,12.244689632138986); xy.Set(46,0,12.244689632138986); xy.Set(47,0,12.244689632138986); xy.Set(48,0,12.244689632138986); xy.Set(49,0,12.244689632138986); xy.Set(50,0,12.244689632138984); xy.Set(51,0,12.244689632138986); xy.Set(52,0,12.244689632138986); xy.Set(53,0,12.244689632138986); xy.Set(54,0,12.244689632138986); xy.Set(55,0,12.244689632138986); xy.Set(56,0,12.244689632138986); CClustering::ClusterizerCreate(s); CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2); CClustering::ClusterizerSetKMeansLimits(s,restarts,0); CClustering::ClusterizerRunKMeans(s,nclusters,rep); othererrors=othererrors||rep.m_terminationtype<=0; } //+------------------------------------------------------------------+ //| This non-deterministic test checks that Restarts>1 significantly | //| improves quality of results. | //| Subroutine generates random task 3 unit balls in 2D, each with 20| //| points, separated by 5 units wide gaps, and solves it with | //| Restarts=1 and with Restarts=5. Potential functions are compared,| //| outcome of the trial is either 0 or 1 (depending on what is | //| better). | //| Sequence of 1000 such tasks is solved. If Restarts>1 actually | //| improve quality of solution, sum of outcome will be non-binomial.| //| If it doesn't matter, it will be binomially distributed. | //| P.S. This test was added after report from Gianluca Borello who | //| noticed error in the handling of multiple restarts. | //+------------------------------------------------------------------+ void CTestClusteringUnit::KMeansRestartsTest(bool &converrors, bool &restartserrors) { //--- create variables int restarts=5; int passcount=1000; int clustersize=20; int nclusters=3; int nvars=2; int npoints=nclusters*clustersize; double sigmathreshold=5; double p=0; double s=0; CMatrixDouble xy; CRowDouble tmp; int i=0; int j=0; int pass=0; double ea=0; double eb=0; double v=0; CClusterizerState state; CKmeansReport rep1; CKmeansReport rep2; int i_=0; xy=matrix::Zeros(npoints,nvars); tmp=vector::Zeros(nvars); for(pass=1; pass<=passcount; pass++) { //--- Fill for(i=0; i1 CClustering::ClusterizerSetKMeansLimits(state,restarts,0); CClustering::ClusterizerRunKMeans(state,nclusters,rep2); if(rep2.m_terminationtype<=0) { converrors=true; return; } eb=0; for(i=0; i(-sigmathreshold)); } //+------------------------------------------------------------------+ //| Random normal number | //+------------------------------------------------------------------+ double CTestClusteringUnit::RNormal(void) { //--- create variables double result=0; double u=0; double v=0; double s=0; double x1=0; while(true) { u=2*CMath::RandomReal()-1; v=2*CMath::RandomReal()-1; s=CMath::Sqr(u)+CMath::Sqr(v); if(s>0.0 && s<1.0) { s=MathSqrt(-(2*MathLog(s)/s)); x1=u*s; break; } } //--- return result result=x1; return(result); } //+------------------------------------------------------------------+ //| Random point from sphere | //+------------------------------------------------------------------+ void CTestClusteringUnit::RSphere(CMatrixDouble &xy,int n,int i) { double v=0; for(int j=0; j::Zeros(nf); x1=vector::Zeros(nf); //--- Basic checks: //--- * positive completion code //--- * sizes of arrays //--- * Rep.P is correct permutation //--- * Rep.Z contains correct cluster indexes //--- * Rep.PZ is consistent with Rep.P/Rep.Z //--- * Rep.PM contains consistent indexes //--- * GetKClusters() for K=NPoints bflag=false; bflag=bflag||rep.m_terminationtype<=0; if(bflag) { result=true; return(result); } bflag=bflag||rep.m_NPoints!=npoints; bflag=(bflag||CAp::Rows(rep.m_z)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_z)!=2); bflag=(bflag||CAp::Rows(rep.m_pz)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_pz)!=2); bflag=(bflag||CAp::Rows(rep.m_pm)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_pm)!=6); bflag=bflag||CAp::Len(rep.m_mergedist)!=npoints-1; bflag=bflag||CAp::Len(rep.m_p)!=npoints; if(bflag) { result=true; return(result); } CAblasF::BSetAllocV(npoints,false,b); for(i=0; i=npoints || b[rep.m_p[i]]) { result=true; return(result); } b[rep.m_p[i]]=true; } for(i=0; i=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=npoints+i) { result=true; return(result); } if(rep.m_pz.Get(i,0)<0 || rep.m_pz.Get(i,0)>=rep.m_pz.Get(i,1) || rep.m_pz.Get(i,1)>=npoints+i) { result=true; return(result); } } for(i=0; i::Zeros(2*npoints-1,2*npoints-1); cm.Resize(2*npoints-1,npoints); clustersizes.Resize(2*npoints-1); for(i=0; i<2*npoints-1; i++) { for(j=0; j<2*npoints-1; j++) if(i(v+10000*CMath::m_machineepsilon)) { result=true; return(result); } if(rep.m_mergedist[mergeidx]>(v+10000*CMath::m_machineepsilon)) { result=true; return(result); } //--- Check that indexes of elements fall into range prescribed by Rep.PM, //--- and Rep.PM correctly described merge operation s0=clustersizes[c0]; s1=clustersizes[c1]; for(j=0; jrep.m_pm.Get(mergeidx,1)) { //--- Element falls outside of range described by PM result=true; return(result); } } for(j=0; jrep.m_pm.Get(mergeidx,3)) { //--- Element falls outside of range described by PM result=true; return(result); } } if(rep.m_pm.Get(mergeidx,1)-rep.m_pm.Get(mergeidx,0)!=s0-1 || rep.m_pm.Get(mergeidx,3)-rep.m_pm.Get(mergeidx,2)!=s1-1 || rep.m_pm.Get(mergeidx,2)!=rep.m_pm.Get(mergeidx,1)+1) { //--- Cluster size (as given by PM) is inconsistent with its actual size. result=true; return(result); } if(rep.m_pm.Get(mergeidx,4)!=clusterheights[rep.m_z.Get(mergeidx,0)] || rep.m_pm.Get(mergeidx,5)!=clusterheights[rep.m_z.Get(mergeidx,1)]) { //--- Heights of subdendrograms as returned by PM are inconsistent with heights //--- calculated by us. result=true; return(result); } //--- Update cluster heights clusterheights.Set(mergeidx+npoints,MathMax(clusterheights[rep.m_z.Get(mergeidx,0)],clusterheights[rep.m_z.Get(mergeidx,1)])+1); //--- Update CM t=0; for(j=0; j0) { //--- Calculate column/row corresponding to new cluster switch(ahcalgo) { case 0: //--- Calculate distance between clusters I and NPoints+MergeIdx for CLINK v=0.0; for(i0=0; i0=kidxz[i+1] || kidxz[i+1]>2*npoints-2) { //--- CZ is inconsistent result=true; return(result); } } for(i=0; i=k) { //--- CIdx is inconsistent result=true; return(result); } } for(i=0; i<=k-1; i++) { for(j=0; j=K | //| NVars - number of variables, NVars>=1 | //| CT - matrix of centers, centers are stored in rows | //| K - number of centers, K>=1 | //| XYC - preallocated output buffer | //| XYDist2 - preallocated output buffer | //| OUTPUT PARAMETERS: | //| XYC - new assignment of points to centers | //| XYDist2 - squared distances | //+------------------------------------------------------------------+ void CTestClusteringUnit::KMeansReferenceUpdateDistances(CMatrixDouble &xy, int npoints, int nvars, CMatrixDouble &ct, int k, CRowInt &xyc, CRowDouble &xydist2) { //--- create variables int cclosest=0; double dclosest=0; double v=0; CRowDouble tmp; tmp=vector::Zeros(nvars); for(int i=0; i=0,"KMeansUpdateDistances: internal error")) return; xyc.Set(i,cclosest); xydist2.Set(i,dclosest); } } //+------------------------------------------------------------------+