20340 lines
692 KiB
Plaintext
20340 lines
692 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| linalg.mqh |
|
|
//| Copyright 2003-2012 Sergey Bochkanov (ALGLIB project) |
|
|
//| Copyright 2012-2017, MetaQuotes Software Corp. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
//| Implementation of ALGLIB library in MetaQuotes Language 5 |
|
|
//| |
|
|
//| The features of the library include: |
|
|
//| - Linear algebra (direct algorithms, EVD, SVD) |
|
|
//| - Solving systems of linear and non-linear equations |
|
|
//| - Interpolation |
|
|
//| - Optimization |
|
|
//| - FFT (Fast Fourier Transform) |
|
|
//| - Numerical integration |
|
|
//| - Linear and nonlinear least-squares fitting |
|
|
//| - Ordinary differential equations |
|
|
//| - Computation of special functions |
|
|
//| - Descriptive statistics and hypothesis testing |
|
|
//| - Data analysis - classification, regression |
|
|
//| - Implementing linear algebra algorithms, interpolation, etc. |
|
|
//| in high-precision arithmetic (using MPFR) |
|
|
//| |
|
|
//| This file is free software; you can redistribute it and/or |
|
|
//| modify it under the terms of the GNU General Public License as |
|
|
//| published by the Free Software Foundation (www.fsf.org); either |
|
|
//| version 2 of the License, or (at your option) any later version. |
|
|
//| |
|
|
//| This program is distributed in the hope that it will be useful, |
|
|
//| but WITHOUT ANY WARRANTY;without even the implied warranty of |
|
|
//| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
//| GNU General Public License for more details. |
|
|
//+------------------------------------------------------------------+
|
|
#include "alglibinternal.mqh"
|
|
#include "alglibmisc.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Work with matrix forms |
|
|
//+------------------------------------------------------------------+
|
|
class CAblas
|
|
{
|
|
private:
|
|
//--- split lenght
|
|
static void AblasInternalSplitLength(const int n,const int nb,int &n1,int &n2);
|
|
//--- real numbers
|
|
static void RMatrixSyrk2(const int n,const int k,const double alpha,const CMatrixDouble &a,const int ia,const int ja,const int optypea,const double beta,CMatrixDouble &c,const int ic,const int jc,const bool isUpper);
|
|
static void RMatrixGemmK(const int m,const int n,const int k,const double alpha,const CMatrixDouble &a,const int ia,const int ja,const int optypea,const CMatrixDouble &b,const int ib,const int jb,const int optypeb,const double beta,CMatrixDouble &c,const int ic,const int jc);
|
|
static void RMatrixRightTrsM2(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isUpper,const bool isUnit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
static void RMatrixLeftTrsM2(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isUpper,const bool isUnit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
//--- complex numbers
|
|
static void CMatrixSyrk2(const int n,const int k,const double alpha,const CMatrixComplex &a,const int ia,const int ja,const int optypea,const double beta,CMatrixComplex &c,const int ic,const int jc,const bool isUpper);
|
|
static void CMatrixGemmk(const int m,const int n,const int k,complex &alpha,const CMatrixComplex &a,const int ia,const int ja,const int optypea,const CMatrixComplex &b,const int ib,const int jb,const int optypeb,complex &beta,CMatrixComplex &c,const int ic,const int jc);
|
|
static void CMatrixRightTrsM2(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isUpper,const bool isUnit,const int optype,CMatrixComplex &x,const int i2,const int j2);
|
|
static void CMatrixLeftTrsM2(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);
|
|
public:
|
|
CAblas(void);
|
|
~CAblas(void);
|
|
//--- size
|
|
static int AblasBlockSize(void) { return(32);}
|
|
static int AblasMicroBlockSize(void) { return(8); }
|
|
static int AblasComplexBlockSize(void) { return(24);}
|
|
//--- split lenght
|
|
static void AblasSplitLength(const CMatrixDouble &a,const int n,int &n1,int &n2);
|
|
static void AblasComplexSplitLength(const CMatrixComplex &a,const int n,int &n1,int &n2);
|
|
//--- real numbers
|
|
static void RMatrixSyrk(const int n,const int k,const double alpha,const CMatrixDouble &a,const int ia,const int ja,const int optypea,const double beta,CMatrixDouble &c,const int ic,const int jc,const bool isUpper);
|
|
static void RMatrixGemm(const int m,const int n,const int k,const double alpha,const CMatrixDouble &a,const int ia,const int ja,const int optypea,const CMatrixDouble &b,const int ib,const int jb,const int optypeb,const double beta,CMatrixDouble &c,const int ic,const int jc);
|
|
static void RMatrixTranspose(const int m,const int n,const CMatrixDouble &a,const int ia,const int ja,CMatrixDouble &b,const int ib,const int jb);
|
|
static void RMatrixCopy(const int m,const int n,const CMatrixDouble &a,const int ia,const int ja,CMatrixDouble &b,const int ib,const int jb);
|
|
static void RMatrixRank1(const int m,const int n,CMatrixDouble &a,const int ia,const int ja,const double &u[],const int iu,const double &v[],const int iv);
|
|
static void RMatrixMVect(const int m,const int n,const CMatrixDouble &a,const int ia,const int ja,const int opa,const double &x[],const int ix,double &y[],const int iy);
|
|
static void RMatrixRightTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isUpper,const bool isUnit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
static void RMatrixLeftTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isUpper,const bool isUnit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
//--- complex numbers
|
|
static void CMatrixSyrk(const int n,const int k,const double alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,const double beta,CMatrixComplex &c,const int ic,const int jc,const bool isUpper);
|
|
static void CMatrixGemm(const int m,const int n,const int k,complex &alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,CMatrixComplex &b,const int ib,const int jb,const int optypeb,complex &beta,CMatrixComplex &c,const int ic,const int jc);
|
|
static void CMatrixTranspose(const int m,const int n,const CMatrixComplex &a,const int ia,const int ja,CMatrixComplex &b,const int ib,const int jb);
|
|
static void CMatrixCopy(const int m,const int n,const CMatrixComplex &a,const int ia,const int ja,CMatrixComplex &b,const int ib,const int jb);
|
|
static void CMatrixRank1(const int m,const int n,CMatrixComplex &a,const int ia,const int ja,const complex &u[],const int iu,const complex &v[],const int iv);
|
|
static void CMatrixMVect(const int m,const int n,const CMatrixComplex &a,const int ia,const int ja,const int opa,const complex &x[],const int ix,complex &y[],const int iy);
|
|
static void CMatrixRightTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isUpper,const bool isUnit,const int optype,CMatrixComplex &x,const int i2,const int j2);
|
|
static void CMatrixLeftTrsM(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);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CAblas::CAblas(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CAblas::~CAblas(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Same as CMatrixSYRK, but for real matrices |
|
|
//| OpType may be only 0 or 1. |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixSyrk(const int n,const int k,const double alpha,
|
|
const 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 s1=0;
|
|
int s2=0;
|
|
int bs=0;
|
|
//--- calculation size
|
|
bs=AblasBlockSize();
|
|
//--- check
|
|
if(n<=bs && k<=bs)
|
|
{
|
|
RMatrixSyrk2(n,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
return;
|
|
}
|
|
//--- check
|
|
if(k>=n)
|
|
{
|
|
//--- Split K
|
|
AblasSplitLength(a,k,s1,s2);
|
|
//--- check
|
|
if(optypea==0)
|
|
{
|
|
RMatrixSyrk(n,s1,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
RMatrixSyrk(n,s2,alpha,a,ia,ja+s1,optypea,1.0,c,ic,jc,isUpper);
|
|
}
|
|
else
|
|
{
|
|
RMatrixSyrk(n,s1,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
RMatrixSyrk(n,s2,alpha,a,ia+s1,ja,optypea,1.0,c,ic,jc,isUpper);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Split N
|
|
AblasSplitLength(a,n,s1,s2);
|
|
//--- check
|
|
if(optypea==0 && isUpper)
|
|
{
|
|
RMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
RMatrixGemm(s1,s2,k,alpha,a,ia,ja,0,a,ia+s1,ja,1,beta,c,ic,jc+s1);
|
|
RMatrixSyrk(s2,k,alpha,a,ia+s1,ja,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea==0 && !isUpper)
|
|
{
|
|
RMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
RMatrixGemm(s2,s1,k,alpha,a,ia+s1,ja,0,a,ia,ja,1,beta,c,ic+s1,jc);
|
|
RMatrixSyrk(s2,k,alpha,a,ia+s1,ja,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && isUpper)
|
|
{
|
|
RMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
RMatrixGemm(s1,s2,k,alpha,a,ia,ja,1,a,ia,ja+s1,0,beta,c,ic,jc+s1);
|
|
RMatrixSyrk(s2,k,alpha,a,ia,ja+s1,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && !isUpper)
|
|
{
|
|
RMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
RMatrixGemm(s2,s1,k,alpha,a,ia,ja+s1,1,a,ia,ja,0,beta,c,ic+s1,jc);
|
|
RMatrixSyrk(s2,k,alpha,a,ia,ja+s1,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Splits matrix length in two parts, left part should match ABLAS |
|
|
//| block size |
|
|
//| INPUT PARAMETERS |
|
|
//| A - real matrix, is passed to ensure that we didn't split|
|
|
//| complex matrix using real splitting subroutine. |
|
|
//| matrix itself is not changed. |
|
|
//| N - length, N>0 |
|
|
//| OUTPUT PARAMETERS |
|
|
//| N1 - length |
|
|
//| N2 - length |
|
|
//| N1+N2=N, N1>=N2, N2 may be zero |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::AblasSplitLength(const CMatrixDouble &a,const int n,
|
|
int &n1,int &n2)
|
|
{
|
|
//--- initialization
|
|
n1=0;
|
|
n2=0;
|
|
//--- check
|
|
if(n>AblasBlockSize())
|
|
AblasInternalSplitLength(n,AblasBlockSize(),n1,n2);
|
|
else
|
|
AblasInternalSplitLength(n,AblasMicroBlockSize(),n1,n2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex ABLASSplitLength |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::AblasComplexSplitLength(const CMatrixComplex &a,const int n,
|
|
int &n1,int &n2)
|
|
{
|
|
//--- check
|
|
if(n>AblasComplexBlockSize())
|
|
AblasInternalSplitLength(n,AblasComplexBlockSize(),n1,n2);
|
|
else
|
|
AblasInternalSplitLength(n,AblasMicroBlockSize(),n1,n2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Same as CMatrixGEMM, but for real numbers. |
|
|
//| OpType may be only 0 or 1. |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixGemm(const int m,const int n,const int k,const double alpha,
|
|
const CMatrixDouble &a,const int ia,const int ja,
|
|
const int optypea,const CMatrixDouble &b,
|
|
const int ib,const int jb,const int optypeb,
|
|
const double beta,CMatrixDouble &c,const int ic,
|
|
const int jc)
|
|
{
|
|
//--- create variables
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs;
|
|
//--- calculation size
|
|
bs=AblasBlockSize();
|
|
//--- check
|
|
if(m<=bs && n<=bs && k<=bs)
|
|
{
|
|
RMatrixGemmK(m,n,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m>=n && m>=k)
|
|
{
|
|
//--- A*B = (A1 A2)^T*B
|
|
AblasSplitLength(a,m,s1,s2);
|
|
//--- check
|
|
if(optypea==0)
|
|
{
|
|
RMatrixGemm(s1,n,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(s2,n,k,alpha,a,ia+s1,ja,optypea,b,ib,jb,optypeb,beta,c,ic+s1,jc);
|
|
}
|
|
else
|
|
{
|
|
RMatrixGemm(s1,n,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(s2,n,k,alpha,a,ia,ja+s1,optypea,b,ib,jb,optypeb,beta,c,ic+s1,jc);
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
if(n>=m && n>=k)
|
|
{
|
|
//--- A*B = A*(B1 B2)
|
|
AblasSplitLength(a,n,s1,s2);
|
|
//--- check
|
|
if(optypeb==0)
|
|
{
|
|
RMatrixGemm(m,s1,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(m,s2,k,alpha,a,ia,ja,optypea,b,ib,jb+s1,optypeb,beta,c,ic,jc+s1);
|
|
}
|
|
else
|
|
{
|
|
RMatrixGemm(m,s1,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(m,s2,k,alpha,a,ia,ja,optypea,b,ib+s1,jb,optypeb,beta,c,ic,jc+s1);
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
if(k>=m && k>=n)
|
|
{
|
|
//--- A*B = (A1 A2)*(B1 B2)^T
|
|
AblasSplitLength(a,k,s1,s2);
|
|
//--- check
|
|
if(optypea==0 && optypeb==0)
|
|
{
|
|
RMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(m,n,s2,alpha,a,ia,ja+s1,optypea,b,ib+s1,jb,optypeb,1.0,c,ic,jc);
|
|
}
|
|
//--- check
|
|
if(optypea==0 && optypeb!=0)
|
|
{
|
|
RMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(m,n,s2,alpha,a,ia,ja+s1,optypea,b,ib,jb+s1,optypeb,1.0,c,ic,jc);
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb==0)
|
|
{
|
|
RMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(m,n,s2,alpha,a,ia+s1,ja,optypea,b,ib+s1,jb,optypeb,1.0,c,ic,jc);
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb!=0)
|
|
{
|
|
RMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
RMatrixGemm(m,n,s2,alpha,a,ia+s1,ja,optypea,b,ib,jb+s1,optypeb,1.0,c,ic,jc);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level 2 subrotuine |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixSyrk2(const int n,const int k,const double alpha,
|
|
const 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)
|
|
{
|
|
//--- check
|
|
if((alpha==0.0 || k==0.0) && beta==1.0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(optypea==0)
|
|
{
|
|
//--- C=alpha*A*A^H+beta*C
|
|
for(i=0;i<n;i++)
|
|
{
|
|
if(isUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
//--- check
|
|
if(alpha!=0 && k>0)
|
|
{
|
|
v=0.0;
|
|
for(i_=ja;i_<ja+k;i_++)
|
|
v+=a[ia+i][i_]*a[ia+j][i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(beta==0)
|
|
c[ic+i].Set(jc+j,alpha*v);
|
|
else
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- C=alpha*A^H*A+beta*C
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- check
|
|
if(beta==0)
|
|
{
|
|
for(j=j1;j<=j2;j++)
|
|
c[ic+i].Set(jc+j,0);
|
|
}
|
|
else
|
|
{
|
|
for(i_=jc+j1;i_<=jc+j2;i_++)
|
|
c[ic+i].Set(i_,beta*c[ic+i][i_]);
|
|
}
|
|
}
|
|
for(i=0;i<k;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
j1=j;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=j;
|
|
}
|
|
//--- change values
|
|
v=alpha*a[ia+i][ja+j];
|
|
i1_=(ja+j1)-(jc+j1);
|
|
for(i_=jc+j1;i_<=jc+j2;i_++)
|
|
c[ic+j].Set(i_,c[ic+j][i_]+v*a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex ABLASSplitLength |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::AblasInternalSplitLength(const int n,const int nb,
|
|
int &n1,int &n2)
|
|
{
|
|
//--- initialization
|
|
int r=0;
|
|
n1=0;
|
|
n2=0;
|
|
//--- check
|
|
if(n<=nb)
|
|
{
|
|
//--- Block size, no further splitting
|
|
n1=n;
|
|
n2=0;
|
|
}
|
|
else
|
|
{
|
|
//--- Greater than block size
|
|
if(n%nb!=0)
|
|
{
|
|
//--- Split remainder
|
|
n2=n%nb;
|
|
n1=n-n2;
|
|
}
|
|
else
|
|
{
|
|
//--- Split on block boundaries
|
|
n2=n/2;
|
|
n1=n-n2;
|
|
//--- check
|
|
if(n1%nb==0)
|
|
return;
|
|
r=nb-n1%nb;
|
|
n1+=r;
|
|
n2-=r;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| GEMM kernel |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixGemmK(const int m,const int n,const int k,const double alpha,
|
|
const CMatrixDouble &a,const int ia,const int ja,
|
|
const int optypea,const CMatrixDouble &b,const int ib,
|
|
const int jb,const int optypeb,const double beta,
|
|
CMatrixDouble &c,const int ic,const int jc)
|
|
{
|
|
//--- check
|
|
if(m*n==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- if K=0, then C=Beta*C
|
|
if(k==0)
|
|
{
|
|
//--- check
|
|
if(beta!=1)
|
|
{
|
|
//--- check
|
|
if(beta!=0)
|
|
{
|
|
for(i=0;i<m;i++)
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<m;i++)
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,0);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea==0 && optypeb!=0)
|
|
{
|
|
//--- a*b'
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(k==0 || alpha==0)
|
|
v=0;
|
|
else
|
|
{
|
|
i1_=jb-ja;
|
|
v=0.0;
|
|
for(i_=ja;i_<ja+k;i_++)
|
|
v+=a[ia+i][i_]*b[ib+j][i_+i1_];
|
|
}
|
|
//--- check
|
|
if(beta==0)
|
|
c[ic+i].Set(jc+j,alpha*v);
|
|
else
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea==0 && optypeb==0)
|
|
{
|
|
//--- a*b
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(beta!=0)
|
|
{
|
|
for(i_=jc;i_<jc+n;i_++)
|
|
c[ic+i].Set(i_,beta*c[ic+i][i_]);
|
|
}
|
|
else
|
|
{
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,0);
|
|
}
|
|
//--- check
|
|
if(alpha!=0)
|
|
{
|
|
for(j=0;j<k;j++)
|
|
{
|
|
v=alpha*a[ia+i][ja+j];
|
|
i1_=jb-jc;
|
|
for(i_=jc;i_<jc+n;i_++)
|
|
c[ic+i].Set(i_,c[ic+i][i_]+v*b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb!=0)
|
|
{
|
|
//--- a`*b`
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(alpha==0)
|
|
v=0;
|
|
else
|
|
{
|
|
i1_=(jb)-(ia);
|
|
v=0.0;
|
|
for(i_=ia;i_<ia+k;i_++)
|
|
v+=a[i_][ja+i]*b[ib+j][i_+i1_];
|
|
}
|
|
//--- check
|
|
if(beta==0)
|
|
c[ic+i].Set(jc+j,alpha*v);
|
|
else
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb==0)
|
|
{
|
|
//--- a`*b
|
|
if(beta==0)
|
|
{
|
|
for(i=0;i<m;i++)
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,0);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<m;i++)
|
|
for(i_=jc;i_<jc+n;i_++)
|
|
c[ic+i].Set(i_,beta*c[ic+i][i_]);
|
|
}
|
|
//--- check
|
|
if(alpha!=0)
|
|
{
|
|
for(j=0;j<k;j++)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
v=alpha*a[ia+j][ja+i];
|
|
i1_=jb-jc;
|
|
for(i_=jc;i_<jc+n;i_++)
|
|
c[ic+i].Set(i_,c[ic+i][i_]+v*b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Cache-oblivous complex "copy-and-transpose" |
|
|
//| Input parameters: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| A - source matrix, MxN submatrix is copied and transposed|
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| A - destination matrix |
|
|
//| IB - submatrix offset (row index) |
|
|
//| JB - submatrix offset (column index) |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixTranspose(const int m,const int n,const CMatrixComplex &a,
|
|
const int ia,const int ja,CMatrixComplex &b,
|
|
const int ib,const int jb)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int s1=0;
|
|
int s2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m<=2*AblasComplexBlockSize() && n<=2*AblasComplexBlockSize())
|
|
{
|
|
//--- base case
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=ja-ib;
|
|
for(i_=ib;i_<ib+n;i_++)
|
|
b[i_].Set(jb+i,a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Cache-oblivious recursion
|
|
if(m>n)
|
|
{
|
|
//--- split
|
|
AblasComplexSplitLength(a,m,s1,s2);
|
|
//--- function call
|
|
CMatrixTranspose(s1,n,a,ia,ja,b,ib,jb);
|
|
CMatrixTranspose(s2,n,a,ia+s1,ja,b,ib,jb+s1);
|
|
}
|
|
else
|
|
{
|
|
//--- split
|
|
AblasComplexSplitLength(a,n,s1,s2);
|
|
//--- function call
|
|
CMatrixTranspose(m,s1,a,ia,ja,b,ib,jb);
|
|
CMatrixTranspose(m,s2,a,ia,ja+s1,b,ib+s1,jb);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Cache-oblivous real "copy-and-transpose" |
|
|
//| Input parameters: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| A - source matrix, MxN submatrix is copied and transposed|
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| A - destination matrix |
|
|
//| IB - submatrix offset (row index) |
|
|
//| JB - submatrix offset (column index) |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixTranspose(const int m,const int n,const CMatrixDouble &a,
|
|
const int ia,const int ja,CMatrixDouble &b,
|
|
const int ib,const int jb)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int s1=0;
|
|
int s2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m<=2*AblasBlockSize() && n<=2*AblasBlockSize())
|
|
{
|
|
//--- base case
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=ja-ib;
|
|
for(i_=ib;i_<ib+n;i_++)
|
|
b[i_].Set(jb+i,a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Cache-oblivious recursion
|
|
if(m>n)
|
|
{
|
|
//--- split
|
|
AblasSplitLength(a,m,s1,s2);
|
|
//--- function call
|
|
RMatrixTranspose(s1,n,a,ia,ja,b,ib,jb);
|
|
RMatrixTranspose(s2,n,a,ia+s1,ja,b,ib,jb+s1);
|
|
}
|
|
else
|
|
{
|
|
//--- split
|
|
AblasSplitLength(a,n,s1,s2);
|
|
//--- function call
|
|
RMatrixTranspose(m,s1,a,ia,ja,b,ib,jb);
|
|
RMatrixTranspose(m,s2,a,ia,ja+s1,b,ib+s1,jb);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//| Input parameters: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| A - source matrix, MxN submatrix is copied and transposed|
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| B - destination matrix |
|
|
//| IB - submatrix offset (row index) |
|
|
//| JB - submatrix offset (column index) |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixCopy(const int m,const int n,const CMatrixComplex &a,
|
|
const int ia,const int ja,CMatrixComplex &b,
|
|
const int ib,const int jb)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- copy
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=ja-jb;
|
|
for(i_=jb;i_<jb+n;i_++)
|
|
b[ib+i].Set(i_,a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//| Input parameters: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| A - source matrix, MxN submatrix is copied and transposed|
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| B - destination matrix |
|
|
//| IB - submatrix offset (row index) |
|
|
//| JB - submatrix offset (column index) |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixCopy(const int m,const int n,const CMatrixDouble &a,
|
|
const int ia,const int ja,CMatrixDouble &b,
|
|
const int ib,const int jb)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- copy
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=ja-jb;
|
|
for(i_=jb;i_<jb+n;i_++)
|
|
b[ib+i].Set(i_,a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rank-1 correction: A := A + u*v' |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| A - target matrix, MxN submatrix is updated |
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| U - vector #1 |
|
|
//| IU - subvector offset |
|
|
//| V - vector #2 |
|
|
//| IV - subvector offset |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixRank1(const int m,const int n,CMatrixComplex &a,
|
|
const int ia,const int ja,const complex &u[],
|
|
const int iu,const complex &v[],const int iv)
|
|
{
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
complex s=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- correction
|
|
for(i=0;i<m;i++)
|
|
{
|
|
s=u[iu+i];
|
|
i1_=iv-ja;
|
|
for(i_=ja;i_<ja+n;i_++)
|
|
a[ia+i].Set(i_,a[ia+i][i_]+s*v[i_+i1_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rank-1 correction: A := A + u*v' |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows |
|
|
//| N - number of columns |
|
|
//| A - target matrix, MxN submatrix is updated |
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| U - vector #1 |
|
|
//| IU - subvector offset |
|
|
//| V - vector #2 |
|
|
//| IV - subvector offset |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixRank1(const int m,const int n,CMatrixDouble &a,
|
|
const int ia,const int ja,const double &u[],
|
|
const int iu,const double &v[],const int iv)
|
|
{
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
double s=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- correction
|
|
for(i=0;i<m;i++)
|
|
{
|
|
s=u[iu+i];
|
|
i1_=iv-ja;
|
|
for(i_=ja;i_<ja+n;i_++)
|
|
a[ia+i].Set(i_,a[ia+i][i_]+s*v[i_+i1_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix-vector product: y := op(A)*x |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows of op(A) |
|
|
//| M>=0 |
|
|
//| N - number of columns of op(A) |
|
|
//| N>=0 |
|
|
//| A - target matrix |
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| OpA - operation type: |
|
|
//| * OpA=0 => op(A) = A |
|
|
//| * OpA=1 => op(A) = A^T |
|
|
//| * OpA=2 => op(A) = A^H |
|
|
//| X - input vector |
|
|
//| IX - subvector offset |
|
|
//| IY - subvector offset |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Y - vector which stores result |
|
|
//| if M=0, then subroutine does nothing. |
|
|
//| if N=0, Y is filled by zeros. |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixMVect(const int m,const int n,const CMatrixComplex &a,
|
|
const int ia,const int ja,const int opa,
|
|
const complex &x[],const int ix,complex &y[],
|
|
const int iy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m==0)
|
|
return;
|
|
//--- check
|
|
if(n==0)
|
|
{
|
|
for(i=0;i<m;i++)
|
|
y[iy+i]=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(opa==0)
|
|
{
|
|
//--- y = A*x
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=ix-ja;
|
|
v=0.0;
|
|
for(i_=ja;i_<ja+n;i_++)
|
|
v+=a[ia+i][i_]*x[i_+i1_];
|
|
//--- get y
|
|
y[iy+i]=v;
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(opa==1)
|
|
{
|
|
//--- y = A^T*x
|
|
for(i=0;i<m;i++)
|
|
y[iy+i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=x[ix+i];
|
|
i1_=ja-iy;
|
|
for(i_=iy;i_<=iy+m-1;i_++)
|
|
y[i_]+=v*a[ia+i][i_+i1_];
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(opa==2)
|
|
{
|
|
//--- y = A^H*x
|
|
for(i=0;i<m;i++)
|
|
y[iy+i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=x[ix+i];
|
|
i1_=ja-iy;
|
|
for(i_=iy;i_<=iy+m-1;i_++)
|
|
y[i_]+=v*CMath::Conj(a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix-vector product: y := op(A)*x |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows of op(A) |
|
|
//| N - number of columns of op(A) |
|
|
//| A - target matrix |
|
|
//| IA - submatrix offset (row index) |
|
|
//| JA - submatrix offset (column index) |
|
|
//| OpA - operation type: |
|
|
//| * OpA=0 => op(A) = A |
|
|
//| * OpA=1 => op(A) = A^T |
|
|
//| X - input vector |
|
|
//| IX - subvector offset |
|
|
//| IY - subvector offset |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Y - vector which stores result |
|
|
//| if M=0, then subroutine does nothing. |
|
|
//| if N=0, Y is filled by zeros. |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixMVect(const int m,const int n,const CMatrixDouble &a,
|
|
const int ia,const int ja,const int opa,
|
|
const double &x[],const int ix,double &y[],
|
|
const int iy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m==0)
|
|
return;
|
|
//--- check
|
|
if(n==0)
|
|
{
|
|
for(i=0;i<m;i++)
|
|
y[iy+i]=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(opa==0)
|
|
{
|
|
//--- y = A*x
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=ix-ja;
|
|
v=0.0;
|
|
for(i_=ja;i_<ja+n;i_++)
|
|
v+=a[ia+i][i_]*x[i_+i1_];
|
|
y[iy+i]=v;
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(opa==1)
|
|
{
|
|
//--- y = A^T*x
|
|
for(i=0;i<m;i++)
|
|
y[iy+i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=x[ix+i];
|
|
i1_=ja-iy;
|
|
for(i_=iy;i_<iy+m;i_++)
|
|
y[i_]+=v*a[ia+i][i_+i1_];
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates X*op(A^-1) where: |
|
|
//| * X is MxN general matrix |
|
|
//| * A is NxN upper/lower triangular/unitriangular matrix |
|
|
//| * "op" may be identity transformation, transposition, conjugate |
|
|
//| transposition |
|
|
//| Multiplication result replaces X. |
|
|
//| Cache-oblivious algorithm is used. |
|
|
//| INPUT PARAMETERS |
|
|
//| N - matrix size, N>=0 |
|
|
//| M - matrix size, N>=0 |
|
|
//| A - matrix, actial matrix is stored in |
|
|
//| A[I1:I1+N-1,J1:J1+N-1] |
|
|
//| I1 - submatrix offset |
|
|
//| J1 - submatrix offset |
|
|
//| IsUpper - whether matrix is upper triangular |
|
|
//| IsUnit - whether matrix is unitriangular |
|
|
//| OpType - transformation type: |
|
|
//| * 0 - no transformation |
|
|
//| * 1 - transposition |
|
|
//| * 2 - conjugate transposition |
|
|
//| C - matrix, actial matrix is stored in |
|
|
//| C[I2:I2+M-1,J2:J2+N-1] |
|
|
//| I2 - submatrix offset |
|
|
//| J2 - submatrix offset |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixRightTrsM(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
|
|
complex Alpha(-1,0);
|
|
complex Beta(1,0);
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs=AblasComplexBlockSize();
|
|
//--- check
|
|
if(m<=bs && n<=bs)
|
|
{
|
|
//--- basic algorithm
|
|
CMatrixRightTrsM2(m,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- Split X: X*A = (X1 X2)^T*A
|
|
AblasComplexSplitLength(a,m,s1,s2);
|
|
CMatrixRightTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
CMatrixRightTrsM(s2,n,a,i1,j1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
}
|
|
else
|
|
{
|
|
//--- Split A:
|
|
//--- (A1 A12)
|
|
//--- X*op(A) = X*op( )
|
|
//--- ( A2)
|
|
//---
|
|
//--- Different variants depending on
|
|
//--- IsUpper/OpType combinations
|
|
AblasComplexSplitLength(a,n,s1,s2);
|
|
//--- check
|
|
if(isUpper && optype==0)
|
|
{
|
|
//--- (A1 A12)-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- ( A2)
|
|
CMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
CMatrixGemm(m,s2,s1,Alpha,x,i2,j2,0,a,i1,j1+s1,0,Beta,x,i2,j2+s1);
|
|
CMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(isUpper && optype!=0)
|
|
{
|
|
//--- (A1' )-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- (A12' A2')
|
|
CMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
CMatrixGemm(m,s1,s2,Alpha,x,i2,j2+s1,0,a,i1,j1+s1,optype,Beta,x,i2,j2);
|
|
CMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype==0)
|
|
{
|
|
//--- (A1 )-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- (A21 A2)
|
|
CMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
CMatrixGemm(m,s1,s2,Alpha,x,i2,j2+s1,0,a,i1+s1,j1,0,Beta,x,i2,j2);
|
|
CMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype!=0)
|
|
{
|
|
//--- (A1' A21')-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- ( A2')
|
|
CMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
CMatrixGemm(m,s2,s1,Alpha,x,i2,j2,0,a,i1+s1,j1,optype,Beta,x,i2,j2+s1);
|
|
CMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates op(A^-1)*X where: |
|
|
//| * X is MxN general matrix |
|
|
//| * A is MxM upper/lower triangular/unitriangular matrix |
|
|
//| * "op" may be identity transformation, transposition, conjugate |
|
|
//| transposition |
|
|
//| Multiplication result replaces X. |
|
|
//| Cache-oblivious algorithm is used. |
|
|
//| INPUT PARAMETERS |
|
|
//| N - matrix size, N>=0 |
|
|
//| M - matrix size, N>=0 |
|
|
//| A - matrix, actial matrix is stored in |
|
|
//| A[I1:I1+M-1,J1:J1+M-1] |
|
|
//| I1 - submatrix offset |
|
|
//| J1 - submatrix offset |
|
|
//| IsUpper - whether matrix is upper triangular |
|
|
//| IsUnit - whether matrix is unitriangular |
|
|
//| OpType - transformation type: |
|
|
//| * 0 - no transformation |
|
|
//| * 1 - transposition |
|
|
//| * 2 - conjugate transposition |
|
|
//| C - matrix, actial matrix is stored in |
|
|
//| C[I2:I2+M-1,J2:J2+N-1] |
|
|
//| I2 - submatrix offset |
|
|
//| J2 - submatrix offset |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixLeftTrsM(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
|
|
complex Alpha(-1,0);
|
|
complex Beta(1,0);
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs=AblasComplexBlockSize();
|
|
//--- check
|
|
if(m<=bs && n<=bs)
|
|
{
|
|
//--- basic algorithm
|
|
CMatrixLeftTrsM2(m,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(n>=m)
|
|
{
|
|
//--- Split X: op(A)^-1*X = op(A)^-1*(X1 X2)
|
|
AblasComplexSplitLength(x,n,s1,s2);
|
|
CMatrixLeftTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
CMatrixLeftTrsM(m,s2,a,i1,j1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
}
|
|
else
|
|
{
|
|
//--- Split A
|
|
AblasComplexSplitLength(a,m,s1,s2);
|
|
//--- check
|
|
if(isUpper && optype==0)
|
|
{
|
|
//--- (A1 A12)-1 ( X1 )
|
|
//--- A^-1*X* = ( ) *( )
|
|
//--- ( A2) ( X2 )
|
|
CMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
CMatrixGemm(s1,n,s2,Alpha,a,i1,j1+s1,0,x,i2+s1,j2,0,Beta,x,i2,j2);
|
|
CMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(isUpper && optype!=0)
|
|
{
|
|
//--- (A1' )-1 ( X1 )
|
|
//--- A^-1*X = ( ) *( )
|
|
//--- (A12' A2') ( X2 )
|
|
CMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
CMatrixGemm(s2,n,s1,Alpha,a,i1,j1+s1,optype,x,i2,j2,0,Beta,x,i2+s1,j2);
|
|
CMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype==0)
|
|
{
|
|
//--- (A1 )-1 ( X1 )
|
|
//--- A^-1*X = ( ) *( )
|
|
//--- (A21 A2) ( X2 )
|
|
CMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
CMatrixGemm(s2,n,s1,Alpha,a,i1+s1,j1,0,x,i2,j2,0,Beta,x,i2+s1,j2);
|
|
CMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype!=0)
|
|
{
|
|
//--- (A1' A21')-1 ( X1 )
|
|
//--- A^-1*X = ( ) *( )
|
|
//--- ( A2') ( X2 )
|
|
CMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
CMatrixGemm(s1,n,s2,Alpha,a,i1+s1,j1,optype,x,i2+s1,j2,0,Beta,x,i2,j2);
|
|
CMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Same as CMatrixRightTRSM, but for real matrices |
|
|
//| OpType may be only 0 or 1. |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixRightTrsM(const int m,const int n,CMatrixDouble &a,
|
|
const int i1,const int j1,const bool isUpper,
|
|
const bool isUnit,const int optype,
|
|
CMatrixDouble &x,const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs=AblasBlockSize();
|
|
//--- check
|
|
if(m<=bs && n<=bs)
|
|
{
|
|
//--- basic algorithm
|
|
RMatrixRightTrsM2(m,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- Split X: X*A = (X1 X2)^T*A
|
|
AblasSplitLength(a,m,s1,s2);
|
|
RMatrixRightTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
RMatrixRightTrsM(s2,n,a,i1,j1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
}
|
|
else
|
|
{
|
|
//--- Split A:
|
|
//--- (A1 A12)
|
|
//--- X*op(A) = X*op( )
|
|
//--- ( A2)
|
|
//--- Different variants depending on
|
|
//--- IsUpper/OpType combinations
|
|
AblasSplitLength(a,n,s1,s2);
|
|
//--- check
|
|
if(isUpper && optype==0)
|
|
{
|
|
//--- (A1 A12)-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- ( A2)
|
|
RMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
RMatrixGemm(m,s2,s1,-1.0,x,i2,j2,0,a,i1,j1+s1,0,1.0,x,i2,j2+s1);
|
|
RMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(isUpper && optype!=0)
|
|
{
|
|
//--- (A1' )-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- (A12' A2')
|
|
RMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
RMatrixGemm(m,s1,s2,-1.0,x,i2,j2+s1,0,a,i1,j1+s1,optype,1.0,x,i2,j2);
|
|
RMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype==0)
|
|
{
|
|
//--- (A1 )-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- (A21 A2)
|
|
RMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
RMatrixGemm(m,s1,s2,-1.0,x,i2,j2+s1,0,a,i1+s1,j1,0,1.0,x,i2,j2);
|
|
RMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype!=0)
|
|
{
|
|
//--- (A1' A21')-1
|
|
//--- X*A^-1 = (X1 X2)*( )
|
|
//--- ( A2')
|
|
RMatrixRightTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
RMatrixGemm(m,s2,s1,-1.0,x,i2,j2,0,a,i1+s1,j1,optype,1.0,x,i2,j2+s1);
|
|
RMatrixRightTrsM(m,s2,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Same as CMatrixLeftTRSM, but for real matrices |
|
|
//| OpType may be only 0 or 1. |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixLeftTrsM(const int m,const int n,CMatrixDouble &a,
|
|
const int i1,const int j1,const bool isUpper,
|
|
const bool isUnit,const int optype,
|
|
CMatrixDouble &x,const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs=AblasBlockSize();
|
|
//--- check
|
|
if(m<=bs && n<=bs)
|
|
{
|
|
//--- basic algorithm
|
|
RMatrixLeftTrsM2(m,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(n>=m)
|
|
{
|
|
//--- Split X: op(A)^-1*X = op(A)^-1*(X1 X2)
|
|
AblasSplitLength(x,n,s1,s2);
|
|
RMatrixLeftTrsM(m,s1,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
RMatrixLeftTrsM(m,s2,a,i1,j1,isUpper,isUnit,optype,x,i2,j2+s1);
|
|
}
|
|
else
|
|
{
|
|
//--- Split A
|
|
AblasSplitLength(a,m,s1,s2);
|
|
//--- check
|
|
if(isUpper && optype==0)
|
|
{
|
|
//--- (A1 A12)-1 ( X1 )
|
|
//--- A^-1*X* = ( ) *( )
|
|
//--- ( A2) ( X2 )
|
|
RMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
RMatrixGemm(s1,n,s2,-1.0,a,i1,j1+s1,0,x,i2+s1,j2,0,1.0,x,i2,j2);
|
|
RMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(isUpper && optype!=0)
|
|
{
|
|
//--- (A1' )-1 ( X1 )
|
|
//--- A^-1*X = ( ) *( )
|
|
//--- (A12' A2') ( X2 )
|
|
RMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
RMatrixGemm(s2,n,s1,-1.0,a,i1,j1+s1,optype,x,i2,j2,0,1.0,x,i2+s1,j2);
|
|
RMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype==0)
|
|
{
|
|
//--- (A1 )-1 ( X1 )
|
|
//--- A^-1*X = ( ) *( )
|
|
//--- (A21 A2) ( X2 )
|
|
RMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
RMatrixGemm(s2,n,s1,-1.0,a,i1+s1,j1,0,x,i2,j2,0,1.0,x,i2+s1,j2);
|
|
RMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!isUpper && optype!=0)
|
|
{
|
|
//--- (A1' A21')-1 ( X1 )
|
|
//--- A^-1*X = ( ) *( )
|
|
//--- ( A2') ( X2 )
|
|
RMatrixLeftTrsM(s2,n,a,i1+s1,j1+s1,isUpper,isUnit,optype,x,i2+s1,j2);
|
|
RMatrixGemm(s1,n,s2,-1.0,a,i1+s1,j1,optype,x,i2+s1,j2,0,1.0,x,i2,j2);
|
|
RMatrixLeftTrsM(s1,n,a,i1,j1,isUpper,isUnit,optype,x,i2,j2);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates C=alpha*A*A^H+beta*C or |
|
|
//| C=alpha*A^H*A+beta*C where: |
|
|
//| * C is NxN Hermitian matrix given by its upper/lower triangle |
|
|
//| * A is NxK matrix when A*A^H is calculated, KxN matrix otherwise |
|
|
//| Additional info: |
|
|
//| * cache-oblivious algorithm is used. |
|
|
//| * multiplication result replaces C. If Beta=0, C elements are not|
|
|
//| used in calculations (not multiplied by zero - just not |
|
|
//| referenced) |
|
|
//| * if Alpha=0, A is not used (not multiplied by zero - just not |
|
|
//| referenced) |
|
|
//| * if both Beta and Alpha are zero, C is filled by zeros. |
|
|
//| INPUT PARAMETERS |
|
|
//| N - matrix size, N>=0 |
|
|
//| K - matrix size, K>=0 |
|
|
//| Alpha - coefficient |
|
|
//| A - matrix |
|
|
//| IA - submatrix offset |
|
|
//| JA - submatrix offset |
|
|
//| OpTypeA - multiplication type: |
|
|
//| * 0 - A*A^H is calculated |
|
|
//| * 2 - A^H*A is calculated |
|
|
//| Beta - coefficient |
|
|
//| C - matrix |
|
|
//| IC - submatrix offset |
|
|
//| JC - submatrix offset |
|
|
//| IsUpper - whether C is upper triangular or lower triangular|
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixSyrk(const int n,const int k,const double alpha,
|
|
CMatrixComplex &a,const int ia,const int ja,
|
|
const int optypea,const double beta,CMatrixComplex &c,
|
|
const int ic,const int jc,const bool isUpper)
|
|
{
|
|
//--- create variables
|
|
complex Alpha(alpha,0);
|
|
complex Beta(beta,0);
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs=AblasComplexBlockSize();
|
|
//--- check
|
|
if(n<=bs && k<=bs)
|
|
{
|
|
//--- basic algorithm
|
|
CMatrixSyrk2(n,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(k>=n)
|
|
{
|
|
//--- Split K
|
|
AblasComplexSplitLength(a,k,s1,s2);
|
|
//--- check
|
|
if(optypea==0)
|
|
{
|
|
CMatrixSyrk(n,s1,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
CMatrixSyrk(n,s2,alpha,a,ia,ja+s1,optypea,1.0,c,ic,jc,isUpper);
|
|
}
|
|
else
|
|
{
|
|
CMatrixSyrk(n,s1,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
CMatrixSyrk(n,s2,alpha,a,ia+s1,ja,optypea,1.0,c,ic,jc,isUpper);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Split N
|
|
AblasComplexSplitLength(a,n,s1,s2);
|
|
//--- check
|
|
if(optypea==0 && isUpper)
|
|
{
|
|
CMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
CMatrixGemm(s1,s2,k,Alpha,a,ia,ja,0,a,ia+s1,ja,2,Beta,c,ic,jc+s1);
|
|
CMatrixSyrk(s2,k,alpha,a,ia+s1,ja,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea==0 && !isUpper)
|
|
{
|
|
CMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
CMatrixGemm(s2,s1,k,Alpha,a,ia+s1,ja,0,a,ia,ja,2,Beta,c,ic+s1,jc);
|
|
CMatrixSyrk(s2,k,alpha,a,ia+s1,ja,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && isUpper)
|
|
{
|
|
CMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
CMatrixGemm(s1,s2,k,Alpha,a,ia,ja,2,a,ia,ja+s1,0,Beta,c,ic,jc+s1);
|
|
CMatrixSyrk(s2,k,alpha,a,ia,ja+s1,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && !isUpper)
|
|
{
|
|
CMatrixSyrk(s1,k,alpha,a,ia,ja,optypea,beta,c,ic,jc,isUpper);
|
|
CMatrixGemm(s2,s1,k,Alpha,a,ia,ja+s1,2,a,ia,ja,0,Beta,c,ic+s1,jc);
|
|
CMatrixSyrk(s2,k,alpha,a,ia,ja+s1,optypea,beta,c,ic+s1,jc+s1,isUpper);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates C = alpha*op1(A)*op2(B) +beta*C where:|
|
|
//| * C is MxN general matrix |
|
|
//| * op1(A) is MxK matrix |
|
|
//| * op2(B) is KxN matrix |
|
|
//| * "op" may be identity transformation, transposition, conjugate |
|
|
//| transposition |
|
|
//| Additional info: |
|
|
//| * cache-oblivious algorithm is used. |
|
|
//| * multiplication result replaces C. If Beta=0, C elements are not|
|
|
//| used in calculations (not multiplied by zero - just not |
|
|
//| referenced) |
|
|
//| * if Alpha=0, A is not used (not multiplied by zero - just not |
|
|
//| referenced) |
|
|
//| * if both Beta and Alpha are zero, C is filled by zeros. |
|
|
//| INPUT PARAMETERS |
|
|
//| N - matrix size, N>0 |
|
|
//| M - matrix size, N>0 |
|
|
//| K - matrix size, K>0 |
|
|
//| Alpha - coefficient |
|
|
//| A - matrix |
|
|
//| IA - submatrix offset |
|
|
//| JA - submatrix offset |
|
|
//| OpTypeA - transformation type: |
|
|
//| * 0 - no transformation |
|
|
//| * 1 - transposition |
|
|
//| * 2 - conjugate transposition |
|
|
//| B - matrix |
|
|
//| IB - submatrix offset |
|
|
//| JB - submatrix offset |
|
|
//| OpTypeB - transformation type: |
|
|
//| * 0 - no transformation |
|
|
//| * 1 - transposition |
|
|
//| * 2 - conjugate transposition |
|
|
//| Beta - coefficient |
|
|
//| C - matrix |
|
|
//| IC - submatrix offset |
|
|
//| JC - submatrix offset |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixGemm(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
|
|
complex Beta(1,0);
|
|
int s1=0;
|
|
int s2=0;
|
|
int bs=AblasComplexBlockSize();
|
|
//--- check
|
|
if(m<=bs && n<=bs && k<=bs)
|
|
{
|
|
//--- basic algorithm
|
|
CMatrixGemmk(m,n,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m>=n && m>=k)
|
|
{
|
|
//--- A*B = (A1 A2)^T*B
|
|
AblasComplexSplitLength(a,m,s1,s2);
|
|
CMatrixGemm(s1,n,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
//--- check
|
|
if(optypea==0)
|
|
CMatrixGemm(s2,n,k,alpha,a,ia+s1,ja,optypea,b,ib,jb,optypeb,beta,c,ic+s1,jc);
|
|
else
|
|
CMatrixGemm(s2,n,k,alpha,a,ia,ja+s1,optypea,b,ib,jb,optypeb,beta,c,ic+s1,jc);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(n>=m && n>=k)
|
|
{
|
|
//---A*B = A*(B1 B2)
|
|
AblasComplexSplitLength(a,n,s1,s2);
|
|
//--- check
|
|
if(optypeb==0)
|
|
{
|
|
CMatrixGemm(m,s1,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
CMatrixGemm(m,s2,k,alpha,a,ia,ja,optypea,b,ib,jb+s1,optypeb,beta,c,ic,jc+s1);
|
|
}
|
|
else
|
|
{
|
|
CMatrixGemm(m,s1,k,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
CMatrixGemm(m,s2,k,alpha,a,ia,ja,optypea,b,ib+s1,jb,optypeb,beta,c,ic,jc+s1);
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(k>=m && k>=n)
|
|
{
|
|
//--- A*B = (A1 A2)*(B1 B2)^T
|
|
AblasComplexSplitLength(a,k,s1,s2);
|
|
//--- check
|
|
if(optypea==0 && optypeb==0)
|
|
{
|
|
CMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
CMatrixGemm(m,n,s2,alpha,a,ia,ja+s1,optypea,b,ib+s1,jb,optypeb,Beta,c,ic,jc);
|
|
}
|
|
//--- check
|
|
if(optypea==0 && optypeb!=0)
|
|
{
|
|
CMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
CMatrixGemm(m,n,s2,alpha,a,ia,ja+s1,optypea,b,ib,jb+s1,optypeb,Beta,c,ic,jc);
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb==0)
|
|
{
|
|
CMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
CMatrixGemm(m,n,s2,alpha,a,ia+s1,ja,optypea,b,ib+s1,jb,optypeb,Beta,c,ic,jc);
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb!=0)
|
|
{
|
|
CMatrixGemm(m,n,s1,alpha,a,ia,ja,optypea,b,ib,jb,optypeb,beta,c,ic,jc);
|
|
CMatrixGemm(m,n,s2,alpha,a,ia+s1,ja,optypea,b,ib,jb+s1,optypeb,Beta,c,ic,jc);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level 2 variant of CMatrixRightTRSM |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixRightTrsM2(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)
|
|
{
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
complex vd=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- General case
|
|
if(isUpper)
|
|
{
|
|
//--- Upper triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- X*A^(-1)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,x[i2+i][j2+j]/vd);
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
vc=x[i2+i][j2+j];
|
|
i1_=j1-j2;
|
|
for(i_=j2+j+1;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vc*a[i1+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
//--- X*A^(-T)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
vc=0;
|
|
vd=1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
i1_=j1-j2;
|
|
vc=0.0;
|
|
for(i_=j2+j+1;i_<j2+n;i_++)
|
|
vc+=x[i2+i][i_]*a[i1+j][i_+i1_];
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,(x[i2+i][j2+j]-vc)/vd);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==2)
|
|
{
|
|
//--- X*A^(-H)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
vc=0;
|
|
vd=1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
i1_=j1-j2;
|
|
vc=0.0;
|
|
for(i_=j2+j+1;i_<j2+n;i_++)
|
|
vc+=x[i2+i][i_]*CMath::Conj(a[i1+j][i_+i1_]);
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
vd=CMath::Conj(a[i1+j][j1+j]);
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,(x[i2+i][j2+j]-vc)/vd);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Lower triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- X*A^(-1)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,x[i2+i][j2+j]/vd);
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
vc=x[i2+i][j2+j];
|
|
i1_=j1-j2;
|
|
for(i_=j2;i_<j2+j;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vc*a[i1+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
if(optype==1)
|
|
{
|
|
//--- X*A^(-T)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
vc=0;
|
|
vd=1;
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=j1-j2;
|
|
vc=0.0;
|
|
for(i_=j2;i_<j2+j;i_++)
|
|
vc+=x[i2+i][i_]*a[i1+j][i_+i1_];
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,(x[i2+i][j2+j]-vc)/vd);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
if(optype==2)
|
|
{
|
|
//--- X*A^(-H)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
vc=0;
|
|
vd=1;
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=j1-j2;
|
|
vc=0.0;
|
|
for(i_=j2;i_<j2+j;i_++)
|
|
vc+=x[i2+i][i_]*CMath::Conj(a[i1+j][i_+i1_]);
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
vd=CMath::Conj(a[i1+j][j1+j]);
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,(x[i2+i][j2+j]-vc)/vd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level-2 subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixLeftTrsM2(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)
|
|
{
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- create variables
|
|
complex Beta(1,0);
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
complex vd=0;
|
|
int i_=0;
|
|
//--- General case
|
|
if(isUpper)
|
|
{
|
|
//--- Upper triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- A^(-1)*X
|
|
for(i=m-1;i>=0;i--)
|
|
{
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
vc=a[i1+i][j1+j];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vc*x[i2+j][i_]);
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
{
|
|
vd=Beta/a[i1+i][j1+i];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
//--- A^(-T)*X
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=Beta/a[i1+i][j1+i];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
vc=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+j].Set(i_,x[i2+j][i_]-vc*x[i2+i][i_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==2)
|
|
{
|
|
//--- A^(-H)*X
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=Beta/CMath::Conj(a[i1+i][j1+i]);
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
vc=CMath::Conj(a[i1+i][j1+j]);
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+j].Set(i_,x[i2+j][i_]-vc*x[i2+i][i_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Lower triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- A^(-1)*X
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- change x
|
|
for(j=0;j<i;j++)
|
|
{
|
|
vc=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vc*x[i2+j][i_]);
|
|
}
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=Beta/a[i1+j][j1+j];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
if(optype==1)
|
|
{
|
|
//--- A^(-T)*X
|
|
for(i=m-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=Beta/a[i1+i][j1+i];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
for(j=i-1;j>=0;j--)
|
|
{
|
|
vc=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+j].Set(i_,x[i2+j][i_]-vc*x[i2+i][i_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
if(optype==2)
|
|
{
|
|
//--- A^(-H)*X
|
|
for(i=m-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=Beta/CMath::Conj(a[i1+i][j1+i]);
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
for(j=i-1;j>=0;j--)
|
|
{
|
|
vc=CMath::Conj(a[i1+i][j1+j]);
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+j].Set(i_,x[i2+j][i_]-vc*x[i2+i][i_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level 2 subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixRightTrsM2(const int m,const int n,CMatrixDouble &a,
|
|
const int i1,const int j1,const bool isUpper,
|
|
const bool isUnit,const int optype,
|
|
CMatrixDouble &x,const int i2,const int j2)
|
|
{
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
double vd=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
//--- Upper triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- X*A^(-1)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,x[i2+i][j2+j]/vd);
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
vr=x[i2+i][j2+j];
|
|
i1_=j1-j2;
|
|
//--- change x
|
|
for(i_=j2+j+1;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vr*a[i1+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
//--- X*A^(-T)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
vr=0;
|
|
vd=1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
i1_=j1-j2;
|
|
vr=0.0;
|
|
for(i_=j2+j+1;i_<j2+n;i_++)
|
|
vr+=x[i2+i][i_]*a[i1+j][i_+i1_];
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,(x[i2+i][j2+j]-vr)/vd);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Lower triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- X*A^(-1)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,x[i2+i][j2+j]/vd);
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
vr=x[i2+i][j2+j];
|
|
i1_=j1-j2;
|
|
//--- change x
|
|
for(i_=j2;i_<j2+j;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vr*a[i1+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
//--- X*A^(-T)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
vr=0;
|
|
vd=1;
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=j1-j2;
|
|
vr=0.0;
|
|
for(i_=j2;i_<j2+j;i_++)
|
|
vr+=x[i2+i][i_]*a[i1+j][i_+i1_];
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
vd=a[i1+j][j1+j];
|
|
//--- change x
|
|
x[i2+i].Set(j2+j,(x[i2+i][j2+j]-vr)/vd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level 2 subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::RMatrixLeftTrsM2(const int m,const int n,CMatrixDouble &a,
|
|
const int i1,const int j1,const bool isUpper,
|
|
const bool isUnit,const int optype,
|
|
CMatrixDouble &x,const int i2,const int j2)
|
|
{
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
double vd=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
//--- Upper triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- A^(-1)*X
|
|
for(i=m-1;i>=0;i--)
|
|
{
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
vr=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vr*x[i2+j][i_]);
|
|
}
|
|
//--- check
|
|
if(!isUnit)
|
|
{
|
|
vd=1/a[i1+i][j1+i];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
//--- A^(-T)*X
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=1/a[i1+i][j1+i];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
vr=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+j].Set(i_,x[i2+j][i_]-vr*x[i2+i][i_]);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Lower triangular matrix
|
|
if(optype==0)
|
|
{
|
|
//--- A^(-1)*X
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
{
|
|
vr=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,x[i2+i][i_]-vr*x[i2+j][i_]);
|
|
}
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=1/a[i1+j][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
//--- A^(-T)*X
|
|
for(i=m-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(isUnit)
|
|
vd=1;
|
|
else
|
|
vd=1/a[i1+i][j1+i];
|
|
//--- change x
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+i].Set(i_,vd*x[i2+i][i_]);
|
|
for(j=i-1;j>=0;j--)
|
|
{
|
|
vr=a[i1+i][j1+j];
|
|
for(i_=j2;i_<j2+n;i_++)
|
|
x[i2+j].Set(i_,x[i2+j][i_]-vr*x[i2+i][i_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level 2 subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixSyrk2(const int n,const int k,const double alpha,
|
|
const CMatrixComplex &a,const int ia,const int ja,
|
|
const int optypea,const double beta,CMatrixComplex &c,
|
|
const int ic,const int jc,const bool isUpper)
|
|
{
|
|
//--- check
|
|
if((alpha==0 || k==0) && beta==1)
|
|
return;
|
|
//--- create variables
|
|
complex Alpha(alpha,0);
|
|
complex Beta(beta,0);
|
|
complex Zero(0,0);
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(optypea==0)
|
|
{
|
|
//--- C = alpha*A*A^H+beta*C
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- cycle
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
//--- check
|
|
if(alpha!=0 && k>0)
|
|
{
|
|
v=0.0;
|
|
for(i_=ja;i_<=ja+k-1;i_++)
|
|
v+=a[ia+i][i_]*CMath::Conj(a[ia+j][i_]);
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(beta==0)
|
|
c[ic+i].Set(jc+j,Alpha*v);
|
|
else
|
|
c[ic+i].Set(jc+j,Beta*c[ic+i][jc+j]+Alpha*v);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- C = alpha*A^H*A+beta*C
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- check
|
|
if(beta==0)
|
|
{
|
|
for(j=j1;j<=j2;j++)
|
|
c[ic+i].Set(jc+j,Zero);
|
|
}
|
|
else
|
|
{
|
|
for(i_=jc+j1;i_<=jc+j2;i_++)
|
|
c[ic+i].Set(i_,Beta*c[ic+i][i_]);
|
|
}
|
|
}
|
|
//--- cycle
|
|
for(i=0;i<k;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(isUpper)
|
|
{
|
|
j1=j;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=j;
|
|
}
|
|
v=Alpha*CMath::Conj(a[ia+i][ja+j]);
|
|
i1_=(ja+j1)-(jc+j1);
|
|
for(i_=jc+j1;i_<=jc+j2;i_++)
|
|
c[ic+j].Set(i_,c[ic+j][i_]+v*a[ia+i][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| GEMM kernel |
|
|
//+------------------------------------------------------------------+
|
|
static void CAblas::CMatrixGemmk(const int m,const int n,const int k,complex &alpha,
|
|
const CMatrixComplex &a,const int ia,const int ja,
|
|
const int optypea,const CMatrixComplex &b,const int ib,
|
|
const int jb,const int optypeb,complex &beta,
|
|
CMatrixComplex &c,const int ic,const int jc)
|
|
{
|
|
//--- check
|
|
if(m*n==0)
|
|
return;
|
|
//--- create variables
|
|
complex Zero(0,0);
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- special case
|
|
if(k==0)
|
|
{
|
|
//--- check
|
|
if(beta!=Zero)
|
|
{
|
|
//--- get c
|
|
for(i=0;i<m;i++)
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]);
|
|
}
|
|
else
|
|
{
|
|
//--- get c
|
|
for(i=0;i<m;i++)
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,Zero);
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General case
|
|
if(optypea==0 && optypeb!=0)
|
|
{
|
|
//--- A*B'
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(k==0 || alpha==Zero)
|
|
v=0;
|
|
else
|
|
{
|
|
//--- check
|
|
if(optypeb==1)
|
|
{
|
|
i1_=(jb)-(ja);
|
|
v=0.0;
|
|
for(i_=ja;i_<=ja+k-1;i_++)
|
|
v+=a[ia+i][i_]*b[ib+j][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
i1_=(jb)-(ja);
|
|
v=0.0;
|
|
for(i_=ja;i_<=ja+k-1;i_++)
|
|
v+=a[ia+i][i_]*CMath::Conj(b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(beta==Zero)
|
|
c[ic+i].Set(jc+j,alpha*v);
|
|
else
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]+alpha*v);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
if(optypea==0 && optypeb==0)
|
|
{
|
|
//--- A*B
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(beta!=Zero)
|
|
{
|
|
for(i_=jc;i_<=jc+n-1;i_++)
|
|
c[ic+i].Set(i_,beta*c[ic+i][i_]);
|
|
}
|
|
else
|
|
{
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,Zero);
|
|
}
|
|
//--- check
|
|
if(alpha!=Zero)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
v=alpha*a[ia+i][ja+j];
|
|
i1_=(jb)-(jc);
|
|
for(i_=jc;i_<=jc+n-1;i_++)
|
|
c[ic+i].Set(i_,c[ic+i][i_]+v*b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb!=0)
|
|
{
|
|
//--- A'*B'
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(alpha==Zero)
|
|
v=0;
|
|
else
|
|
{
|
|
//--- check
|
|
if(optypea==1)
|
|
{
|
|
//--- check
|
|
if(optypeb==1)
|
|
{
|
|
i1_=(jb)-(ia);
|
|
v=0.0;
|
|
for(i_=ia;i_<=ia+k-1;i_++)
|
|
v+=a[i_][ja+i]*b[ib+j][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
i1_=(jb)-(ia);
|
|
v=0.0;
|
|
for(i_=ia;i_<=ia+k-1;i_++)
|
|
v+=a[i_][ja+i]*CMath::Conj(b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(optypeb==1)
|
|
{
|
|
i1_=(jb)-(ia);
|
|
v=0.0;
|
|
for(i_=ia;i_<=ia+k-1;i_++)
|
|
v+=CMath::Conj(a[i_][ja+i])*b[ib+j][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
i1_=(jb)-(ia);
|
|
v=0.0;
|
|
for(i_=ia;i_<=ia+k-1;i_++)
|
|
v+=CMath::Conj(a[i_][ja+i])*CMath::Conj(b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(beta==Zero)
|
|
c[ic+i].Set(jc+j,alpha*v);
|
|
else
|
|
c[ic+i].Set(jc+j,beta*c[ic+i][jc+j]+alpha*v);
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(optypea!=0 && optypeb==0)
|
|
{
|
|
//--- A'*B
|
|
if(beta==Zero)
|
|
{
|
|
for(i=0;i<m;i++)
|
|
for(j=0;j<n;j++)
|
|
c[ic+i].Set(jc+j,Zero);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<m;i++)
|
|
for(i_=jc;i_<=jc+n-1;i_++)
|
|
c[ic+i].Set(i_,beta*c[ic+i][i_]);
|
|
}
|
|
//--- check
|
|
if(alpha!=Zero)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(optypea==1)
|
|
v=alpha*a[ia+j][ja+i];
|
|
else
|
|
v=alpha*CMath::Conj(a[ia+j][ja+i]);
|
|
i1_=(jb)-(jc);
|
|
for(i_=jc;i_<=jc+n-1;i_++)
|
|
c[ic+i].Set(i_,c[ic+i][i_]+v*b[ib+j][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Orthogonal factorizations |
|
|
//+------------------------------------------------------------------+
|
|
class COrtFac
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixQRBaseCase(CMatrixDouble &a,const int m,const int n,double &work[],double &t[],double &tau[]);
|
|
static void RMatrixLQBaseCase(CMatrixDouble &a,const int m,const int n,double &work[],double &t[],double &tau[]);
|
|
static void CMatrixQRBaseCase(CMatrixComplex &a,const int m,const int n,complex &work[],complex &t[],complex &tau[]);
|
|
static void CMatrixLQBaseCase(CMatrixComplex &a,const int m,const int n,complex &work[],complex &t[],complex &tau[]);
|
|
static void RMatrixBlockReflector(CMatrixDouble &a,double &tau[],const bool columnwisea,const int lengtha,const int blocksize,CMatrixDouble &t,double &work[]);
|
|
static void CMatrixBlockReflector(CMatrixComplex &a,complex &tau[],const bool columnwisea,const int lengtha,const int blocksize,CMatrixComplex &t,complex &work[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
COrtFac(void);
|
|
~COrtFac(void);
|
|
//--- real matrix
|
|
static void RMatrixQR(CMatrixDouble &a,const int m,const int n,double &tau[]);
|
|
static void RMatrixLQ(CMatrixDouble &a,const int m,const int n,double &tau[]);
|
|
static void RMatrixQRUnpackQ(CMatrixDouble &a,const int m,const int n,double &tau[],const int qcolumns,CMatrixDouble &q);
|
|
static void RMatrixQRUnpackR(CMatrixDouble &a,const int m,const int n,CMatrixDouble &r);
|
|
static void RMatrixLQUnpackQ(CMatrixDouble &a,const int m,const int n,double &tau[],const int qrows,CMatrixDouble &q);
|
|
static void RMatrixLQUnpackL(CMatrixDouble &a,const int m,const int n,CMatrixDouble &l);
|
|
static void RMatrixBD(CMatrixDouble &a,const int m,const int n,double &tauq[],double &taup[]);
|
|
static void RMatrixBDUnpackQ(CMatrixDouble &qp,const int m,const int n,double &tauq[],const int qcolumns,CMatrixDouble &q);
|
|
static void RMatrixBDMultiplyByQ(CMatrixDouble &qp,const int m,const int n,double &tauq[],CMatrixDouble &z,const int zrows,const int zcolumns,const bool fromtheright,const bool dotranspose);
|
|
static void RMatrixBDUnpackPT(CMatrixDouble &qp,const int m,const int n,double &taup[],const int ptrows,CMatrixDouble &pt);
|
|
static void RMatrixBDMultiplyByP(CMatrixDouble &qp,const int m,const int n,double &taup[],CMatrixDouble &z,const int zrows,const int zcolumns,const bool fromtheright,const bool dotranspose);
|
|
static void RMatrixBDUnpackDiagonals(CMatrixDouble &b,const int m,const int n,bool &isupper,double &d[],double &e[]);
|
|
static void RMatrixHessenberg(CMatrixDouble &a,const int n,double &tau[]);
|
|
static void RMatrixHessenbergUnpackQ(CMatrixDouble &a,const int n,double &tau[],CMatrixDouble &q);
|
|
static void RMatrixHessenbergUnpackH(CMatrixDouble &a,const int n,CMatrixDouble &h);
|
|
static void SMatrixTD(CMatrixDouble &a,const int n,const bool isupper,double &tau[],double &d[],double &e[]);
|
|
static void SMatrixTDUnpackQ(CMatrixDouble &a,const int n,const bool isupper,double &tau[],CMatrixDouble &q);
|
|
//--- complex matrix
|
|
static void CMatrixQR(CMatrixComplex &a,const int m,const int n,complex &tau[]);
|
|
static void CMatrixLQ(CMatrixComplex &a,const int m,const int n,complex &tau[]);
|
|
static void CMatrixQRUnpackQ(CMatrixComplex &a,const int m,const int n,complex &tau[],const int qcolumns,CMatrixComplex &q);
|
|
static void CMatrixQRUnpackR(CMatrixComplex &a,const int m,const int n,CMatrixComplex &r);
|
|
static void CMatrixLQUnpackQ(CMatrixComplex &a,const int m,const int n,complex &tau[],const int qrows,CMatrixComplex &q);
|
|
static void CMatrixLQUnpackL(CMatrixComplex &a,const int m,const int n,CMatrixComplex &l);
|
|
static void HMatrixTD(CMatrixComplex &a,const int n,const bool isupper,complex &tau[],double &d[],double &e[]);
|
|
static void HMatrixTDUnpackQ(CMatrixComplex &a,const int n,const bool isupper,complex &tau[],CMatrixComplex &q);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
COrtFac::COrtFac(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
COrtFac::~COrtFac(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| QR decomposition of a rectangular matrix of size MxN |
|
|
//| Input parameters: |
|
|
//| A - matrix A whose indexes range within [0..M-1, 0..N-1].|
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| Output parameters: |
|
|
//| A - matrices Q and R in compact form (see below). |
|
|
//| Tau - array of scalar factors which are used to form |
|
|
//| matrix Q. Array whose index ranges within |
|
|
//| [0.. Min(M-1,N-1)]. |
|
|
//| Matrix A is represented as A = QR, where Q is an orthogonal |
|
|
//| matrix of size MxM, R - upper triangular (or upper trapezoid) |
|
|
//| matrix of size M x N. |
|
|
//| The elements of matrix R are located on and above the main |
|
|
//| diagonal of matrix A. The elements which are located in Tau |
|
|
//| array and below the main diagonal of matrix A are used to form |
|
|
//| matrix Q as follows: |
|
|
//| Matrix Q is represented as a product of elementary reflections |
|
|
//| Q = H(0)*H(2)*...*H(k-1), |
|
|
//| where k = min(m,n), and each H(i) is in the form |
|
|
//| H(i) = 1 - tau * v * (v^T) |
|
|
//| where tau is a scalar stored in Tau[I]; v - real vector, |
|
|
//| so that v(0:i-1) = 0, v(i) = 1, v(i+1:m-1) stored in |
|
|
//| A(i+1:m-1,i). |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixQR(CMatrixDouble &a,const int m,const int n,double &tau[])
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int blockstart=0;
|
|
int blocksize=0;
|
|
int rowscount=0;
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double work[];
|
|
double t[];
|
|
double taubuf[];
|
|
//--- create matrix
|
|
CMatrixDouble tmpa;
|
|
CMatrixDouble tmpt;
|
|
CMatrixDouble tmpr;
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(tau,minmn);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(m,CAblas::AblasBlockSize());
|
|
tmpt.Resize(CAblas::AblasBlockSize(),2*CAblas::AblasBlockSize());
|
|
tmpr.Resize(2*CAblas::AblasBlockSize(),n);
|
|
//--- Blocked code
|
|
while(blockstart!=minmn)
|
|
{
|
|
//--- Determine block size
|
|
blocksize=minmn-blockstart;
|
|
if(blocksize>CAblas::AblasBlockSize())
|
|
blocksize=CAblas::AblasBlockSize();
|
|
//--- change
|
|
rowscount=m-blockstart;
|
|
//--- QR decomposition of submatrix.
|
|
//--- Matrix is copied to temporary storage to solve
|
|
//--- some TLB issues arising from non-contiguous memory
|
|
//--- access pattern.
|
|
CAblas::RMatrixCopy(rowscount,blocksize,a,blockstart,blockstart,tmpa,0,0);
|
|
RMatrixQRBaseCase(tmpa,rowscount,blocksize,work,t,taubuf);
|
|
CAblas::RMatrixCopy(rowscount,blocksize,tmpa,0,0,a,blockstart,blockstart);
|
|
i1_=-blockstart;
|
|
for(i_=blockstart;i_<=blockstart+blocksize-1;i_++)
|
|
tau[i_]=taubuf[i_+i1_];
|
|
//--- check
|
|
if(blockstart+blocksize<=n-1)
|
|
{
|
|
//--- Update the rest, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(n-blockstart-blocksize>=2*CAblas::AblasBlockSize() || rowscount>=4*CAblas::AblasBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
RMatrixBlockReflector(tmpa,taubuf,true,rowscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q'.
|
|
//--- Q = E + Y*T*Y' = E + TmpA*TmpT*TmpA'
|
|
//--- Q' = E + Y*T'*Y' = E + TmpA*TmpT'*TmpA'
|
|
CAblas::RMatrixGemm(blocksize,n-blockstart-blocksize,rowscount,1.0,tmpa,0,0,1,a,blockstart,blockstart+blocksize,0,0.0,tmpr,0,0);
|
|
CAblas::RMatrixGemm(blocksize,n-blockstart-blocksize,blocksize,1.0,tmpt,0,0,1,tmpr,0,0,0,0.0,tmpr,blocksize,0);
|
|
CAblas::RMatrixGemm(rowscount,n-blockstart-blocksize,blocksize,1.0,tmpa,0,0,0,tmpr,blocksize,0,0,1.0,a,blockstart,blockstart+blocksize);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=0;i<blocksize;i++)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=rowscount-i;i_++)
|
|
t[i_]=tmpa[i_+i1_][i];
|
|
t[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(a,taubuf[i],t,blockstart+i,m-1,blockstart+blocksize,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart+blocksize;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LQ decomposition of a rectangular matrix of size MxN |
|
|
//| Input parameters: |
|
|
//| A - matrix A whose indexes range within [0..M-1, 0..N-1].|
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| Output parameters: |
|
|
//| A - matrices L and Q in compact form (see below) |
|
|
//| Tau - array of scalar factors which are used to form |
|
|
//| matrix Q. Array whose index ranges within |
|
|
//| [0..Min(M,N)-1]. |
|
|
//| Matrix A is represented as A = LQ, where Q is an orthogonal |
|
|
//| matrix of size MxM, L - lower triangular (or lower trapezoid) |
|
|
//| matrix of size M x N. |
|
|
//| The elements of matrix L are located on and below the main |
|
|
//| diagonal of matrix A. The elements which are located in Tau |
|
|
//| array and above the main diagonal of matrix A are used to form |
|
|
//| matrix Q as follows: |
|
|
//| Matrix Q is represented as a product of elementary reflections |
|
|
//| Q = H(k-1)*H(k-2)*...*H(1)*H(0), |
|
|
//| where k = min(m,n), and each H(i) is of the form |
|
|
//| H(i) = 1 - tau * v * (v^T) |
|
|
//| where tau is a scalar stored in Tau[I]; v - real vector, so that |
|
|
//| v(0:i-1)=0, v(i) = 1, v(i+1:n-1) stored in A(i,i+1:n-1). |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixLQ(CMatrixDouble &a,const int m,const int n,double &tau[])
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int blockstart=0;
|
|
int blocksize=0;
|
|
int columnscount=0;
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double work[];
|
|
double t[];
|
|
double taubuf[];
|
|
//--- create matrix
|
|
CMatrixDouble tmpa;
|
|
CMatrixDouble tmpt;
|
|
CMatrixDouble tmpr;
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(tau,minmn);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(CAblas::AblasBlockSize(),n);
|
|
tmpt.Resize(CAblas::AblasBlockSize(),2*CAblas::AblasBlockSize());
|
|
tmpr.Resize(m,2*CAblas::AblasBlockSize());
|
|
//--- Blocked code
|
|
while(blockstart!=minmn)
|
|
{
|
|
//--- Determine block size
|
|
blocksize=minmn-blockstart;
|
|
if(blocksize>CAblas::AblasBlockSize())
|
|
blocksize=CAblas::AblasBlockSize();
|
|
//--- change
|
|
columnscount=n-blockstart;
|
|
//--- LQ decomposition of submatrix.
|
|
//--- Matrix is copied to temporary storage to solve
|
|
//--- some TLB issues arising from non-contiguous memory
|
|
//--- access pattern.
|
|
CAblas::RMatrixCopy(blocksize,columnscount,a,blockstart,blockstart,tmpa,0,0);
|
|
RMatrixLQBaseCase(tmpa,blocksize,columnscount,work,t,taubuf);
|
|
CAblas::RMatrixCopy(blocksize,columnscount,tmpa,0,0,a,blockstart,blockstart);
|
|
i1_=-blockstart;
|
|
for(i_=blockstart;i_<=blockstart+blocksize-1;i_++)
|
|
{
|
|
tau[i_]=taubuf[i_+i1_];
|
|
}
|
|
//--- Update the rest, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(blockstart+blocksize<=m-1)
|
|
{
|
|
//--- check
|
|
if(m-blockstart-blocksize>=2*CAblas::AblasBlockSize())
|
|
{
|
|
//--- prepare
|
|
RMatrixBlockReflector(tmpa,taubuf,false,columnscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q.
|
|
//--- Q = E + Y*T*Y' = E + TmpA'*TmpT*TmpA
|
|
CAblas::RMatrixGemm(m-blockstart-blocksize,blocksize,columnscount,1.0,a,blockstart+blocksize,blockstart,0,tmpa,0,0,1,0.0,tmpr,0,0);
|
|
CAblas::RMatrixGemm(m-blockstart-blocksize,blocksize,blocksize,1.0,tmpr,0,0,0,tmpt,0,0,0,0.0,tmpr,0,blocksize);
|
|
CAblas::RMatrixGemm(m-blockstart-blocksize,columnscount,blocksize,1.0,tmpr,0,blocksize,0,tmpa,0,0,0,1.0,a,blockstart+blocksize,blockstart);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=0;i<blocksize;i++)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=columnscount-i;i_++)
|
|
t[i_]=tmpa[i][i_+i1_];
|
|
t[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(a,taubuf[i],t,blockstart+blocksize,m-1,blockstart+i,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart+blocksize;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| QR decomposition of a rectangular complex matrix of size MxN |
|
|
//| Input parameters: |
|
|
//| A - matrix A whose indexes range within [0..M-1, 0..N-1] |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| Output parameters: |
|
|
//| A - matrices Q and R in compact form |
|
|
//| Tau - array of scalar factors which are used to form |
|
|
//| matrix Q. Array whose indexes range within |
|
|
//| [0.. Min(M,N)-1] |
|
|
//| Matrix A is represented as A = QR, where Q is an orthogonal |
|
|
//| matrix of size MxM, R - upper triangular (or upper trapezoid) |
|
|
//| matrix of size MxN. |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| September 30, 1994 |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixQR(CMatrixComplex &a,const int m,const int n,complex &tau[])
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create arrays
|
|
complex work[];
|
|
complex t[];
|
|
complex taubuf[];
|
|
//--- create matrix
|
|
CMatrixComplex tmpa;
|
|
CMatrixComplex tmpt;
|
|
CMatrixComplex tmpr;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);;
|
|
int blockstart=0;
|
|
int blocksize=0;
|
|
int rowscount=0;
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex One(1,0);
|
|
complex Alpha(1,0);
|
|
complex Beta(0,0);
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(tau,minmn);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(m,CAblas::AblasComplexBlockSize());
|
|
tmpt.Resize(CAblas::AblasComplexBlockSize(),CAblas::AblasComplexBlockSize());
|
|
tmpr.Resize(2*CAblas::AblasComplexBlockSize(),n);
|
|
//--- Blocked code
|
|
while(blockstart!=minmn)
|
|
{
|
|
//--- Determine block size
|
|
blocksize=minmn-blockstart;
|
|
if(blocksize>CAblas::AblasComplexBlockSize())
|
|
blocksize=CAblas::AblasComplexBlockSize();
|
|
rowscount=m-blockstart;
|
|
//--- QR decomposition of submatrix.
|
|
//--- Matrix is copied to temporary storage to solve
|
|
//--- some TLB issues arising from non-contiguous memory
|
|
//--- access pattern.
|
|
CAblas::CMatrixCopy(rowscount,blocksize,a,blockstart,blockstart,tmpa,0,0);
|
|
CMatrixQRBaseCase(tmpa,rowscount,blocksize,work,t,taubuf);
|
|
CAblas::CMatrixCopy(rowscount,blocksize,tmpa,0,0,a,blockstart,blockstart);
|
|
i1_=-blockstart;
|
|
for(i_=blockstart;i_<=blockstart+blocksize-1;i_++)
|
|
tau[i_]=taubuf[i_+i1_];
|
|
//--- Update the rest, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(blockstart+blocksize<=n-1)
|
|
{
|
|
//--- check
|
|
if(n-blockstart-blocksize>=2*CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
CMatrixBlockReflector(tmpa,taubuf,true,rowscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q'.
|
|
//--- Q = E + Y*T*Y' = E + TmpA*TmpT*TmpA'
|
|
//--- Q' = E + Y*T'*Y' = E + TmpA*TmpT'*TmpA'
|
|
CAblas::CMatrixGemm(blocksize,n-blockstart-blocksize,rowscount,Alpha,tmpa,0,0,2,a,blockstart,blockstart+blocksize,0,Beta,tmpr,0,0);
|
|
CAblas::CMatrixGemm(blocksize,n-blockstart-blocksize,blocksize,Alpha,tmpt,0,0,2,tmpr,0,0,0,Beta,tmpr,blocksize,0);
|
|
CAblas::CMatrixGemm(rowscount,n-blockstart-blocksize,blocksize,Alpha,tmpa,0,0,0,tmpr,blocksize,0,0,Alpha,a,blockstart,blockstart+blocksize);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=0;i<blocksize;i++)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=rowscount-i;i_++)
|
|
t[i_]=tmpa[i_+i1_][i];
|
|
t[1]=One;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(a,CMath::Conj(taubuf[i]),t,blockstart+i,m-1,blockstart+blocksize,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart+blocksize;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LQ decomposition of a rectangular complex matrix of size MxN |
|
|
//| Input parameters: |
|
|
//| A - matrix A whose indexes range within [0..M-1, 0..N-1] |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| Output parameters: |
|
|
//| A - matrices Q and L in compact form |
|
|
//| Tau - array of scalar factors which are used to form |
|
|
//| matrix Q. Array whose indexes range within |
|
|
//| [0.. Min(M,N)-1] |
|
|
//| Matrix A is represented as A = LQ, where Q is an orthogonal |
|
|
//| matrix of size MxM, L - lower triangular (or lower trapezoid) |
|
|
//| matrix of size MxN. |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| September 30, 1994 |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixLQ(CMatrixComplex &a,const int m,const int n,complex &tau[])
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create arrays
|
|
complex work[];
|
|
complex t[];
|
|
complex taubuf[];
|
|
//--- create matrix
|
|
CMatrixComplex tmpa;
|
|
CMatrixComplex tmpt;
|
|
CMatrixComplex tmpr;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int blockstart=0;
|
|
int blocksize=0;
|
|
int columnscount=0;
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex One(1,0);
|
|
complex Alpha(1,0);
|
|
complex Beta(0,0);
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(tau,minmn);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(CAblas::AblasComplexBlockSize(),n);
|
|
tmpt.Resize(CAblas::AblasComplexBlockSize(),CAblas::AblasComplexBlockSize());
|
|
tmpr.Resize(m,2*CAblas::AblasComplexBlockSize());
|
|
//--- Blocked code
|
|
while(blockstart!=minmn)
|
|
{
|
|
//--- Determine block size
|
|
blocksize=minmn-blockstart;
|
|
if(blocksize>CAblas::AblasComplexBlockSize())
|
|
blocksize=CAblas::AblasComplexBlockSize();
|
|
columnscount=n-blockstart;
|
|
//--- LQ decomposition of submatrix.
|
|
//--- Matrix is copied to temporary storage to solve
|
|
//--- some TLB issues arising from non-contiguous memory
|
|
//--- access pattern.
|
|
CAblas::CMatrixCopy(blocksize,columnscount,a,blockstart,blockstart,tmpa,0,0);
|
|
CMatrixLQBaseCase(tmpa,blocksize,columnscount,work,t,taubuf);
|
|
CAblas::CMatrixCopy(blocksize,columnscount,tmpa,0,0,a,blockstart,blockstart);
|
|
i1_=-blockstart;
|
|
for(i_=blockstart;i_<=blockstart+blocksize-1;i_++)
|
|
tau[i_]=taubuf[i_+i1_];
|
|
//--- Update the rest, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(blockstart+blocksize<=m-1)
|
|
{
|
|
//--- check
|
|
if(m-blockstart-blocksize>=2*CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
CMatrixBlockReflector(tmpa,taubuf,false,columnscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q.
|
|
//--- Q = E + Y*T*Y' = E + TmpA'*TmpT*TmpA
|
|
CAblas::CMatrixGemm(m-blockstart-blocksize,blocksize,columnscount,Alpha,a,blockstart+blocksize,blockstart,0,tmpa,0,0,2,Beta,tmpr,0,0);
|
|
CAblas::CMatrixGemm(m-blockstart-blocksize,blocksize,blocksize,Alpha,tmpr,0,0,0,tmpt,0,0,0,Beta,tmpr,0,blocksize);
|
|
CAblas::CMatrixGemm(m-blockstart-blocksize,columnscount,blocksize,Alpha,tmpr,0,blocksize,0,tmpa,0,0,0,Alpha,a,blockstart+blocksize,blockstart);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=0;i<blocksize;i++)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=columnscount-i;i_++)
|
|
t[i_]=CMath::Conj(tmpa[i][i_+i1_]);
|
|
t[1]=One;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(a,taubuf[i],t,blockstart+blocksize,m-1,blockstart+i,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart+blocksize;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Partial unpacking of matrix Q from the QR decomposition of a |
|
|
//| matrix A |
|
|
//| Input parameters: |
|
|
//| A - matrices Q and R in compact form. |
|
|
//| Output of RMatrixQR subroutine. |
|
|
//| M - number of rows in given matrix A. M>=0. |
|
|
//| N - number of columns in given matrix A. N>=0. |
|
|
//| Tau - scalar factors which are used to form Q. |
|
|
//| Output of the RMatrixQR subroutine. |
|
|
//| QColumns - required number of columns of matrix Q. |
|
|
//| M>=QColumns>=0. |
|
|
//| Output parameters: |
|
|
//| Q - first QColumns columns of matrix Q. |
|
|
//| Array whose indexes range within |
|
|
//| [0..M-1, 0..QColumns-1]. |
|
|
//| If QColumns=0, the array remains unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixQRUnpackQ(CMatrixDouble &a,const int m,const int n,
|
|
double &tau[],const int qcolumns,CMatrixDouble &q)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(qcolumns<=m,__FUNCTION__+": QColumns>M!"))
|
|
return;
|
|
//--- check
|
|
if(m<=0 || n<=0 || qcolumns<=0)
|
|
return;
|
|
//--- create arrays
|
|
double work[];
|
|
double t[];
|
|
double taubuf[];
|
|
//--- create matrix
|
|
CMatrixDouble tmpa;
|
|
CMatrixDouble tmpt;
|
|
CMatrixDouble tmpr;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int refcnt=MathMin(minmn,qcolumns);
|
|
int blockstart=CAblas::AblasBlockSize()*(refcnt/CAblas::AblasBlockSize());
|
|
int blocksize=refcnt-blockstart;
|
|
int rowscount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
q.Resize(m,qcolumns);
|
|
//--- identity matrix
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<qcolumns;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,qcolumns)+1);
|
|
ArrayResizeAL(t,MathMax(m,qcolumns)+1);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(m,CAblas::AblasBlockSize());
|
|
tmpt.Resize(CAblas::AblasBlockSize(),2*CAblas::AblasBlockSize());
|
|
tmpr.Resize(2*CAblas::AblasBlockSize(),qcolumns);
|
|
//--- Blocked code
|
|
while(blockstart>=0)
|
|
{
|
|
rowscount=m-blockstart;
|
|
//--- Copy current block
|
|
CAblas::RMatrixCopy(rowscount,blocksize,a,blockstart,blockstart,tmpa,0,0);
|
|
i1_=blockstart;
|
|
for(i_=0;i_<blocksize;i_++)
|
|
taubuf[i_]=tau[i_+i1_];
|
|
//--- Update, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(qcolumns>=2*CAblas::AblasBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
RMatrixBlockReflector(tmpa,taubuf,true,rowscount,blocksize,tmpt,work);
|
|
//--- Multiply matrix by Q.
|
|
//--- Q = E + Y*T*Y' = E + TmpA*TmpT*TmpA'
|
|
CAblas::RMatrixGemm(blocksize,qcolumns,rowscount,1.0,tmpa,0,0,1,q,blockstart,0,0,0.0,tmpr,0,0);
|
|
CAblas::RMatrixGemm(blocksize,qcolumns,blocksize,1.0,tmpt,0,0,0,tmpr,0,0,0,0.0,tmpr,blocksize,0);
|
|
CAblas::RMatrixGemm(rowscount,qcolumns,blocksize,1.0,tmpa,0,0,0,tmpr,blocksize,0,0,1.0,q,blockstart,0);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=blocksize-1;i>=0;i--)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=rowscount-i;i_++)
|
|
{
|
|
t[i_]=tmpa[i_+i1_][i];
|
|
}
|
|
t[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(q,taubuf[i],t,blockstart+i,m-1,0,qcolumns-1,work);
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart-CAblas::AblasBlockSize();
|
|
blocksize=CAblas::AblasBlockSize();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking of matrix R from the QR decomposition of a matrix A |
|
|
//| Input parameters: |
|
|
//| A - matrices Q and R in compact form. |
|
|
//| Output of RMatrixQR subroutine. |
|
|
//| M - number of rows in given matrix A. M>=0. |
|
|
//| N - number of columns in given matrix A. N>=0. |
|
|
//| Output parameters: |
|
|
//| R - matrix R, array[0..M-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixQRUnpackR(CMatrixDouble &a,const int m,const int n,CMatrixDouble &r)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int k=MathMin(m,n);
|
|
int i_=0;
|
|
//--- allocation
|
|
r.Resize(m,n);
|
|
//--- Prepare matrix
|
|
for(i=0;i<n;i++)
|
|
r[0].Set(i,0);
|
|
for(i=1;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
r[i].Set(i_,r[0][i_]);
|
|
}
|
|
//--- get result
|
|
for(i=0;i<k;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
r[i].Set(i_,a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Partial unpacking of matrix Q from the LQ decomposition of a |
|
|
//| matrix A |
|
|
//| Input parameters: |
|
|
//| A - matrices L and Q in compact form. |
|
|
//| Output of RMatrixLQ subroutine. |
|
|
//| M - number of rows in given matrix A. M>=0. |
|
|
//| N - number of columns in given matrix A. N>=0. |
|
|
//| Tau - scalar factors which are used to form Q. |
|
|
//| Output of the RMatrixLQ subroutine. |
|
|
//| QRows - required number of rows in matrix Q. N>=QRows>=0.|
|
|
//| Output parameters: |
|
|
//| Q - first QRows rows of matrix Q. Array whose indexes|
|
|
//| range within [0..QRows-1, 0..N-1]. If QRows=0, |
|
|
//| the array remains unchanged. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixLQUnpackQ(CMatrixDouble &a,const int m,const int n,
|
|
double &tau[],const int qrows,CMatrixDouble &q)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(qrows<=n,__FUNCTION__+": QRows>N!"))
|
|
return;
|
|
//--- check
|
|
if(m<=0 || n<=0 || qrows<=0)
|
|
return;
|
|
//--- create arrays
|
|
double work[];
|
|
double t[];
|
|
double taubuf[];
|
|
//--- create matrix
|
|
CMatrixDouble tmpa;
|
|
CMatrixDouble tmpt;
|
|
CMatrixDouble tmpr;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int refcnt=MathMin(minmn,qrows);
|
|
int blockstart=CAblas::AblasBlockSize()*(refcnt/CAblas::AblasBlockSize());
|
|
int blocksize=refcnt-blockstart;
|
|
int columnscount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(CAblas::AblasBlockSize(),n);
|
|
tmpt.Resize(CAblas::AblasBlockSize(),2*CAblas::AblasBlockSize());
|
|
tmpr.Resize(qrows,2*CAblas::AblasBlockSize());
|
|
q.Resize(qrows,n);
|
|
//--- identity matrix
|
|
for(i=0;i<=qrows-1;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- Blocked code
|
|
while(blockstart>=0)
|
|
{
|
|
columnscount=n-blockstart;
|
|
//--- Copy submatrix
|
|
CAblas::RMatrixCopy(blocksize,columnscount,a,blockstart,blockstart,tmpa,0,0);
|
|
i1_=blockstart;
|
|
for(i_=0;i_<blocksize;i_++)
|
|
taubuf[i_]=tau[i_+i1_];
|
|
//--- Update matrix, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(qrows>=2*CAblas::AblasBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
RMatrixBlockReflector(tmpa,taubuf,false,columnscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q'.
|
|
//--- Q' = E + Y*T'*Y' = E + TmpA'*TmpT'*TmpA
|
|
CAblas::RMatrixGemm(qrows,blocksize,columnscount,1.0,q,0,blockstart,0,tmpa,0,0,1,0.0,tmpr,0,0);
|
|
CAblas::RMatrixGemm(qrows,blocksize,blocksize,1.0,tmpr,0,0,0,tmpt,0,0,1,0.0,tmpr,0,blocksize);
|
|
CAblas::RMatrixGemm(qrows,columnscount,blocksize,1.0,tmpr,0,blocksize,0,tmpa,0,0,0,1.0,q,0,blockstart);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=blocksize-1;i>=0;i--)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=columnscount-i;i_++)
|
|
t[i_]=tmpa[i][i_+i1_];
|
|
t[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(q,taubuf[i],t,0,qrows-1,blockstart+i,n-1,work);
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart-CAblas::AblasBlockSize();
|
|
blocksize=CAblas::AblasBlockSize();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking of matrix L from the LQ decomposition of a matrix A |
|
|
//| Input parameters: |
|
|
//| A -matrices Q and L in compact form. |
|
|
//| Output of RMatrixLQ subroutine. |
|
|
//| M -number of rows in given matrix A. M>=0. |
|
|
//| N -number of columns in given matrix A. N>=0. |
|
|
//| Output parameters: |
|
|
//| L -matrix L, array[0..M-1,0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixLQUnpackL(CMatrixDouble &a,const int m,const int n,CMatrixDouble &l)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
int i_=0;
|
|
//--- allocation
|
|
l.Resize(m,n);
|
|
//--- Prepare matrix
|
|
for(i=0;i<n;i++)
|
|
l[0].Set(i,0);
|
|
for(i=1;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
l[i].Set(i_,l[0][i_]);
|
|
}
|
|
//--- get result
|
|
for(i=0;i<m;i++)
|
|
{
|
|
k=MathMin(i,n-1);
|
|
for(i_=0;i_<=k;i_++)
|
|
l[i].Set(i_,a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Partial unpacking of matrix Q from QR decomposition of a complex |
|
|
//| matrix A. |
|
|
//| Input parameters: |
|
|
//| A - matrices Q and R in compact form. |
|
|
//| Output of CMatrixQR subroutine . |
|
|
//| M - number of rows in matrix A. M>=0. |
|
|
//| N - number of columns in matrix A. N>=0. |
|
|
//| Tau - scalar factors which are used to form Q. |
|
|
//| Output of CMatrixQR subroutine . |
|
|
//| QColumns - required number of columns in matrix Q. |
|
|
//| M>=QColumns>=0. |
|
|
//| Output parameters: |
|
|
//| Q - first QColumns columns of matrix Q. |
|
|
//| Array whose index ranges within [0..M-1, |
|
|
//| 0..QColumns-1]. |
|
|
//| If QColumns=0, array isn't changed. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixQRUnpackQ(CMatrixComplex &a,const int m,const int n,
|
|
complex &tau[],const int qcolumns,CMatrixComplex &q)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(qcolumns<=m,__FUNCTION__+": QColumns>M!"))
|
|
return;
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create arrays
|
|
complex work[];
|
|
complex t[];
|
|
complex taubuf[];
|
|
//--- create matrix
|
|
CMatrixComplex tmpa;
|
|
CMatrixComplex tmpt;
|
|
CMatrixComplex tmpr;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int refcnt=MathMin(minmn,qcolumns);
|
|
int blockstart=CAblas::AblasComplexBlockSize()*(refcnt/CAblas::AblasComplexBlockSize());
|
|
int blocksize=refcnt-blockstart;
|
|
int rowscount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex One(1,0);
|
|
complex Zero(0,0);
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(m,CAblas::AblasComplexBlockSize());
|
|
tmpt.Resize(CAblas::AblasComplexBlockSize(),CAblas::AblasComplexBlockSize());
|
|
tmpr.Resize(2*CAblas::AblasComplexBlockSize(),qcolumns);
|
|
q.Resize(m,qcolumns);
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<qcolumns;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,One);
|
|
else
|
|
q[i].Set(j,Zero);
|
|
}
|
|
}
|
|
//--- Blocked code
|
|
while(blockstart>=0)
|
|
{
|
|
rowscount=m-blockstart;
|
|
//--- QR decomposition of submatrix.
|
|
//--- Matrix is copied to temporary storage to solve
|
|
//--- some TLB issues arising from non-contiguous memory
|
|
//--- access pattern.
|
|
CAblas::CMatrixCopy(rowscount,blocksize,a,blockstart,blockstart,tmpa,0,0);
|
|
i1_=blockstart;
|
|
for(i_=0;i_<blocksize;i_++)
|
|
taubuf[i_]=tau[i_+i1_];
|
|
//--- Update matrix, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(qcolumns>=2*CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
CMatrixBlockReflector(tmpa,taubuf,true,rowscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q.
|
|
//--- Q = E + Y*T*Y' = E + TmpA*TmpT*TmpA'
|
|
CAblas::CMatrixGemm(blocksize,qcolumns,rowscount,One,tmpa,0,0,2,q,blockstart,0,0,Zero,tmpr,0,0);
|
|
CAblas::CMatrixGemm(blocksize,qcolumns,blocksize,One,tmpt,0,0,0,tmpr,0,0,0,Zero,tmpr,blocksize,0);
|
|
CAblas::CMatrixGemm(rowscount,qcolumns,blocksize,One,tmpa,0,0,0,tmpr,blocksize,0,0,One,q,blockstart,0);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=blocksize-1;i>=0;i--)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=rowscount-i;i_++)
|
|
t[i_]=tmpa[i_+i1_][i];
|
|
t[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(q,taubuf[i],t,blockstart+i,m-1,0,qcolumns-1,work);
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart-CAblas::AblasComplexBlockSize();
|
|
blocksize=CAblas::AblasComplexBlockSize();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking of matrix R from the QR decomposition of a matrix A |
|
|
//| Input parameters: |
|
|
//| A - matrices Q and R in compact form. |
|
|
//| Output of CMatrixQR subroutine. |
|
|
//| M - number of rows in given matrix A. M>=0. |
|
|
//| N - number of columns in given matrix A. N>=0. |
|
|
//| Output parameters: |
|
|
//| R - matrix R, array[0..M-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixQRUnpackR(CMatrixComplex &a,const int m,const int n,CMatrixComplex &r)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
complex Zero(0,0);
|
|
int i=0;
|
|
int k=MathMin(m,n);
|
|
int i_=0;
|
|
//--- allocation
|
|
r.Resize(m,n);
|
|
//--- Prepare matrix
|
|
for(i=0;i<n;i++)
|
|
r[0].Set(i,Zero);
|
|
for(i=1;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
r[i].Set(i_,r[0][i_]);
|
|
}
|
|
//--- get result
|
|
for(i=0;i<k;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
r[i].Set(i_,a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Partial unpacking of matrix Q from LQ decomposition of a complex |
|
|
//| matrix A. |
|
|
//| Input parameters: |
|
|
//| A - matrices Q and R in compact form. |
|
|
//| Output of CMatrixLQ subroutine. |
|
|
//| M - number of rows in matrix A. M>=0. |
|
|
//| N - number of columns in matrix A. N>=0. |
|
|
//| Tau - scalar factors which are used to form Q. |
|
|
//| Output of CMatrixLQ subroutine . |
|
|
//| QRows - required number of rows in matrix Q. |
|
|
//| N>=QColumns>=0. |
|
|
//| Output parameters: |
|
|
//| Q - first QRows rows of matrix Q. |
|
|
//| Array whose index ranges within [0..QRows-1, |
|
|
//| 0..N-1]. |
|
|
//| If QRows=0, array isn't changed. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixLQUnpackQ(CMatrixComplex &a,const int m,const int n,
|
|
complex &tau[],const int qrows,CMatrixComplex &q)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create arrays
|
|
complex work[];
|
|
complex t[];
|
|
complex taubuf[];
|
|
//--- create matrix
|
|
CMatrixComplex tmpa;
|
|
CMatrixComplex tmpt;
|
|
CMatrixComplex tmpr;
|
|
//--- create variables
|
|
int minmn=MathMin(m,n);
|
|
int refcnt=MathMin(minmn,qrows);
|
|
int blockstart=CAblas::AblasComplexBlockSize()*(refcnt/CAblas::AblasComplexBlockSize());
|
|
int blocksize=refcnt-blockstart;
|
|
int columnscount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex One(1,0);
|
|
complex Zero(0,0);
|
|
//--- allocation
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
ArrayResizeAL(t,MathMax(m,n)+1);
|
|
ArrayResizeAL(taubuf,minmn);
|
|
//--- allocation
|
|
tmpa.Resize(CAblas::AblasComplexBlockSize(),n);
|
|
tmpt.Resize(CAblas::AblasComplexBlockSize(),CAblas::AblasComplexBlockSize());
|
|
tmpr.Resize(qrows,2*CAblas::AblasComplexBlockSize());
|
|
q.Resize(qrows,n);
|
|
for(i=0;i<=qrows-1;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,One);
|
|
else
|
|
q[i].Set(j,Zero);
|
|
}
|
|
}
|
|
//--- Blocked code
|
|
while(blockstart>=0)
|
|
{
|
|
columnscount=n-blockstart;
|
|
//--- LQ decomposition of submatrix.
|
|
//--- Matrix is copied to temporary storage to solve
|
|
//--- some TLB issues arising from non-contiguous memory
|
|
//--- access pattern.
|
|
CAblas::CMatrixCopy(blocksize,columnscount,a,blockstart,blockstart,tmpa,0,0);
|
|
i1_=blockstart;
|
|
for(i_=0;i_<blocksize;i_++)
|
|
taubuf[i_]=tau[i_+i1_];
|
|
//--- Update matrix, choose between:
|
|
//--- a) Level 2 algorithm (when the rest of the matrix is small enough)
|
|
//--- b) blocked algorithm, see algorithm 5 from 'A storage efficient WY
|
|
//--- representation for products of Householder transformations',
|
|
//--- by R. Schreiber and C. Van Loan.
|
|
if(qrows>=2*CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- Prepare block reflector
|
|
CMatrixBlockReflector(tmpa,taubuf,false,columnscount,blocksize,tmpt,work);
|
|
//--- Multiply the rest of A by Q'.
|
|
//--- Q' = E + Y*T'*Y' = E + TmpA'*TmpT'*TmpA
|
|
CAblas::CMatrixGemm(qrows,blocksize,columnscount,One,q,0,blockstart,0,tmpa,0,0,2,Zero,tmpr,0,0);
|
|
CAblas::CMatrixGemm(qrows,blocksize,blocksize,One,tmpr,0,0,0,tmpt,0,0,2,Zero,tmpr,0,blocksize);
|
|
CAblas::CMatrixGemm(qrows,columnscount,blocksize,One,tmpr,0,blocksize,0,tmpa,0,0,0,One,q,0,blockstart);
|
|
}
|
|
else
|
|
{
|
|
//--- Level 2 algorithm
|
|
for(i=blocksize-1;i>=0;i--)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=columnscount-i;i_++)
|
|
t[i_]=CMath::Conj(tmpa[i][i_+i1_]);
|
|
t[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(q,CMath::Conj(taubuf[i]),t,0,qrows-1,blockstart+i,n-1,work);
|
|
}
|
|
}
|
|
//--- change value
|
|
blockstart=blockstart-CAblas::AblasComplexBlockSize();
|
|
blocksize=CAblas::AblasComplexBlockSize();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking of matrix L from the LQ decomposition of a matrix A |
|
|
//| Input parameters: |
|
|
//| A - matrices Q and L in compact form. |
|
|
//| Output of CMatrixLQ subroutine. |
|
|
//| M - number of rows in given matrix A. M>=0. |
|
|
//| N - number of columns in given matrix A. N>=0. |
|
|
//| Output parameters: |
|
|
//| L - matrix L, array[0..M-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixLQUnpackL(CMatrixComplex &a,const int m,const int n,CMatrixComplex &l)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
complex Zero(0,0);
|
|
int i=0;
|
|
int k=0;
|
|
int i_=0;
|
|
//--- allocation
|
|
l.Resize(m,n);
|
|
//--- Prepare matrix
|
|
for(i=0;i<n;i++)
|
|
l[0].Set(i,Zero);
|
|
for(i=1;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
l[i].Set(i_,l[0][i_]);
|
|
}
|
|
//--- get result
|
|
for(i=0;i<m;i++)
|
|
{
|
|
k=MathMin(i,n-1);
|
|
for(i_=0;i_<=k;i_++)
|
|
l[i].Set(i_,a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reduction of a rectangular matrix to bidiagonal form |
|
|
//| The algorithm reduces the rectangular matrix A to bidiagonal |
|
|
//| form by orthogonal transformations P and Q: A = Q*B*P. |
|
|
//| Input parameters: |
|
|
//| A - source matrix. array[0..M-1, 0..N-1] |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| Output parameters: |
|
|
//| A - matrices Q, B, P in compact form (see below). |
|
|
//| TauQ - scalar factors which are used to form matrix Q. |
|
|
//| TauP - scalar factors which are used to form matrix P. |
|
|
//| The main diagonal and one of the secondary diagonals of matrix A |
|
|
//| are replaced with bidiagonal matrix B. Other elements contain |
|
|
//| elementary reflections which form MxM matrix Q and NxN matrix P, |
|
|
//| respectively. |
|
|
//| If M>=N, B is the upper bidiagonal MxN matrix and is stored in |
|
|
//| the corresponding elements of matrix A. Matrix Q is represented |
|
|
//| as a product of elementary reflections Q = H(0)*H(1)*...*H(n-1), |
|
|
//| where H(i) = 1-tau*v*v'. Here tau is a scalar which is stored in |
|
|
//| TauQ[i], and vector v has the following structure: v(0:i-1)=0, |
|
|
//| v(i)=1, v(i+1:m-1) is stored in elements A(i+1:m-1,i).Matrix P is|
|
|
//| as follows: P = G(0)*G(1)*...*G(n-2), where G(i) = 1 - tau*u*u'. |
|
|
//| Tau is stored in TauP[i], u(0:i)=0, u(i+1)=1, u(i+2:n-1) is |
|
|
//| stored in elements A(i,i+2:n-1). |
|
|
//| If M<N, B is the lower bidiagonal MxN matrix and is stored in the|
|
|
//| corresponding elements of matrix A. Q = H(0)*H(1)*...*H(m-2), |
|
|
//| where H(i) = 1 - tau*v*v', tau is stored in TauQ, v(0:i)=0, |
|
|
//| v(i+1)=1, v(i+2:m-1) is stored in elements A(i+2:m-1,i). |
|
|
//| P = G(0)*G(1)*...*G(m-1), G(i) = 1-tau*u*u', tau is stored in |
|
|
//| TauP, u(0:i-1)=0, u(i)=1, u(i+1:n-1) is stored in A(i,i+1:n-1). |
|
|
//| EXAMPLE: |
|
|
//| m=6, n=5 (m > n): m=5, n=6 (m < n): |
|
|
//| ( d e u1 u1 u1 ) ( d u1 u1 u1 u1 u1 ) |
|
|
//| ( v1 d e u2 u2 ) ( e d u2 u2 u2 u2 ) |
|
|
//| ( v1 v2 d e u3 ) ( v1 e d u3 u3 u3 ) |
|
|
//| ( v1 v2 v3 d e ) ( v1 v2 e d u4 u4 ) |
|
|
//| ( v1 v2 v3 v4 d ) ( v1 v2 v3 e d u5 ) |
|
|
//| ( v1 v2 v3 v4 v5 ) |
|
|
//| Here vi and ui are vectors which form H(i) and G(i), and d and |
|
|
//| e - are the diagonal and off-diagonal elements of matrix B. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBD(CMatrixDouble &a,const int m,const int n,double &tauq[],double &taup[])
|
|
{
|
|
//--- check
|
|
if(n<=0 || m<=0)
|
|
return;
|
|
//--- create arrays
|
|
double work[];
|
|
double t[];
|
|
//--- create variables
|
|
int minmn=0;
|
|
int maxmn=MathMax(m,n);
|
|
int i=0;
|
|
double ltau=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(work,maxmn+1);
|
|
ArrayResizeAL(t,maxmn+1);
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
ArrayResizeAL(tauq,n);
|
|
ArrayResizeAL(taup,n);
|
|
}
|
|
else
|
|
{
|
|
ArrayResizeAL(tauq,m);
|
|
ArrayResizeAL(taup,m);
|
|
}
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- Reduce to upper bidiagonal form
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- Generate elementary reflector H(i) to annihilate A(i+1:m-1,i)
|
|
i1_=i-1;
|
|
for(i_=1;i_<=m-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CReflections::GenerateReflection(t,m-i,ltau);
|
|
tauq[i]=ltau;
|
|
i1_=1-i;
|
|
for(i_=i;i_<m;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- Apply H(i) to A(i:m-1,i+1:n-1) from the left
|
|
CReflections::ApplyReflectionFromTheLeft(a,ltau,t,i,m-1,i+1,n-1,work);
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
//--- Generate elementary reflector G(i) to annihilate
|
|
//--- A(i,i+2:n-1)
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i][i_+i1_];
|
|
CReflections::GenerateReflection(t,n-1-i,ltau);
|
|
taup[i]=ltau;
|
|
i1_=-i;
|
|
for(i_=i+1;i_<n;i_++)
|
|
a[i].Set(i_,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- Apply G(i) to A(i+1:m-1,i+1:n-1) from the right
|
|
CReflections::ApplyReflectionFromTheRight(a,ltau,t,i+1,m-1,i+1,n-1,work);
|
|
}
|
|
else
|
|
taup[i]=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Reduce to lower bidiagonal form
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- Generate elementary reflector G(i) to annihilate A(i,i+1:n-1)
|
|
i1_=i-1;
|
|
for(i_=1;i_<=n-i;i_++)
|
|
t[i_]=a[i][i_+i1_];
|
|
CReflections::GenerateReflection(t,n-i,ltau);
|
|
taup[i]=ltau;
|
|
i1_=1-i;
|
|
for(i_=i;i_<n;i_++)
|
|
a[i].Set(i_,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- Apply G(i) to A(i+1:m-1,i:n-1) from the right
|
|
CReflections::ApplyReflectionFromTheRight(a,ltau,t,i+1,m-1,i,n-1,work);
|
|
//--- check
|
|
if(i<m-1)
|
|
{
|
|
//--- Generate elementary reflector H(i) to annihilate
|
|
//--- A(i+2:m-1,i)
|
|
i1_=i;
|
|
for(i_=1;i_<m-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CReflections::GenerateReflection(t,m-1-i,ltau);
|
|
tauq[i]=ltau;
|
|
i1_=-i;
|
|
for(i_=i+1;i_<m;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- Apply H(i) to A(i+1:m-1,i+1:n-1) from the left
|
|
CReflections::ApplyReflectionFromTheLeft(a,ltau,t,i+1,m-1,i+1,n-1,work);
|
|
}
|
|
else
|
|
tauq[i]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking matrix Q which reduces a matrix to bidiagonal form. |
|
|
//| Input parameters: |
|
|
//| QP - matrices Q and P in compact form. |
|
|
//| Output of ToBidiagonal subroutine. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| TAUQ - scalar factors which are used to form Q. |
|
|
//| Output of ToBidiagonal subroutine. |
|
|
//| QColumns - required number of columns in matrix Q. |
|
|
//| M>=QColumns>=0. |
|
|
//| Output parameters: |
|
|
//| Q - first QColumns columns of matrix Q. |
|
|
//| Array[0..M-1, 0..QColumns-1] |
|
|
//| If QColumns=0, the array is not modified. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBDUnpackQ(CMatrixDouble &qp,const int m,const int n,
|
|
double &tauq[],const int qcolumns,CMatrixDouble &q)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(qcolumns<=m,__FUNCTION__+": QColumns>M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(qcolumns>=0,__FUNCTION__+": QColumns<0!"))
|
|
return;
|
|
//--- check
|
|
if(m==0 || n==0 || qcolumns==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
q.Resize(m,qcolumns);
|
|
//--- identity matrix
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<qcolumns;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- get result
|
|
RMatrixBDMultiplyByQ(qp,m,n,tauq,q,m,qcolumns,false,false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Multiplication by matrix Q which reduces matrix A to bidiagonal |
|
|
//| form. |
|
|
//| The algorithm allows pre- or post-multiply by Q or Q'. |
|
|
//| Input parameters: |
|
|
//| QP - matrices Q and P in compact form. |
|
|
//| Output of ToBidiagonal subroutine. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| TAUQ - scalar factors which are used to form Q. |
|
|
//| Output of ToBidiagonal subroutine. |
|
|
//| Z - multiplied matrix. |
|
|
//| array[0..ZRows-1,0..ZColumns-1] |
|
|
//| ZRows - number of rows in matrix Z. If FromTheRight= |
|
|
//| =False, ZRows=M, otherwise ZRows can be |
|
|
//| arbitrary. |
|
|
//| ZColumns - number of columns in matrix Z. If |
|
|
//| FromTheRight=True, ZColumns=M, otherwise |
|
|
//| ZColumns can be arbitrary. |
|
|
//| FromTheRight - pre- or post-multiply. |
|
|
//| DoTranspose - multiply by Q or Q'. |
|
|
//| Output parameters: |
|
|
//| Z - product of Z and Q. |
|
|
//| Array[0..ZRows-1,0..ZColumns-1] |
|
|
//| If ZRows=0 or ZColumns=0, the array is not |
|
|
//| modified. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBDMultiplyByQ(CMatrixDouble &qp,const int m,const int n,
|
|
double &tauq[],CMatrixDouble &z,const int zrows,
|
|
const int zcolumns,const bool fromtheright,
|
|
const bool dotranspose)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0 || zrows<=0 || zcolumns<=0)
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((fromtheright && zcolumns==m) || (!fromtheright && zrows==m),__FUNCTION__+": incorrect Z size!"))
|
|
return;
|
|
//--- create variables
|
|
int mx=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
int i=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
int istep=0;
|
|
//--- create arrays
|
|
double v[];
|
|
double work[];
|
|
//--- initialization
|
|
mx=MathMax(m,n);
|
|
mx=MathMax(mx,zrows);
|
|
mx=MathMax(mx,zcolumns);
|
|
//--- allocation
|
|
ArrayResizeAL(v,mx+1);
|
|
ArrayResizeAL(work,mx+1);
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- setup
|
|
if(fromtheright)
|
|
{
|
|
i1=0;
|
|
i2=n-1;
|
|
istep=1;
|
|
}
|
|
else
|
|
{
|
|
i1=n-1;
|
|
i2=0;
|
|
istep=-1;
|
|
}
|
|
//--- check
|
|
if(dotranspose)
|
|
{
|
|
i=i1;
|
|
i1=i2;
|
|
i2=i;
|
|
istep=-istep;
|
|
}
|
|
//--- Process
|
|
i=i1;
|
|
do
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=m-i;i_++)
|
|
v[i_]=qp[i_+i1_][i];
|
|
v[1]=1;
|
|
//--- check
|
|
if(fromtheright)
|
|
CReflections::ApplyReflectionFromTheRight(z,tauq[i],v,0,zrows-1,i,m-1,work);
|
|
else
|
|
CReflections::ApplyReflectionFromTheLeft(z,tauq[i],v,i,m-1,0,zcolumns-1,work);
|
|
i=i+istep;
|
|
}
|
|
while(i!=i2+istep);
|
|
}
|
|
else
|
|
{
|
|
//--- setup
|
|
if(fromtheright)
|
|
{
|
|
i1=0;
|
|
i2=m-2;
|
|
istep=1;
|
|
}
|
|
else
|
|
{
|
|
i1=m-2;
|
|
i2=0;
|
|
istep=-1;
|
|
}
|
|
//--- check
|
|
if(dotranspose)
|
|
{
|
|
i=i1;
|
|
i1=i2;
|
|
i2=i;
|
|
istep=-istep;
|
|
}
|
|
//--- Process
|
|
if(m-1>0)
|
|
{
|
|
i=i1;
|
|
do
|
|
{
|
|
i1_=i;
|
|
for(i_=1;i_<=m-i-1;i_++)
|
|
v[i_]=qp[i_+i1_][i];
|
|
v[1]=1;
|
|
//--- check
|
|
if(fromtheright)
|
|
CReflections::ApplyReflectionFromTheRight(z,tauq[i],v,0,zrows-1,i+1,m-1,work);
|
|
else
|
|
CReflections::ApplyReflectionFromTheLeft(z,tauq[i],v,i+1,m-1,0,zcolumns-1,work);
|
|
i=i+istep;
|
|
}
|
|
while(i!=i2+istep);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking matrix P which reduces matrix A to bidiagonal form. |
|
|
//| The subroutine returns transposed matrix P. |
|
|
//| Input parameters: |
|
|
//| QP - matrices Q and P in compact form. |
|
|
//| Output of ToBidiagonal subroutine. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| TAUP - scalar factors which are used to form P. |
|
|
//| Output of ToBidiagonal subroutine. |
|
|
//| PTRows - required number of rows of matrix P^T. |
|
|
//| N >= PTRows >= 0. |
|
|
//| Output parameters: |
|
|
//| PT - first PTRows columns of matrix P^T |
|
|
//| Array[0..PTRows-1, 0..N-1] |
|
|
//| If PTRows=0, the array is not modified. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBDUnpackPT(CMatrixDouble &qp,const int m,const int n,
|
|
double &taup[],const int ptrows,CMatrixDouble &pt)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(ptrows<=n,__FUNCTION__+": PTRows>N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(ptrows>=0,__FUNCTION__+": PTRows<0!"))
|
|
return;
|
|
//--- check
|
|
if(m==0 || n==0 || ptrows==0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
pt.Resize(ptrows,n);
|
|
//--- prepare
|
|
for(i=0;i<=ptrows-1;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
if(i==j)
|
|
pt[i].Set(j,1);
|
|
else
|
|
pt[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- get result
|
|
RMatrixBDMultiplyByP(qp,m,n,taup,pt,ptrows,n,true,true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Multiplication by matrix P which reduces matrix A to bidiagonal |
|
|
//| form. |
|
|
//| The algorithm allows pre- or post-multiply by P or P'. |
|
|
//| Input parameters: |
|
|
//| QP - matrices Q and P in compact form. |
|
|
//| Output of RMatrixBD subroutine. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| TAUP - scalar factors which are used to form P. |
|
|
//| Output of RMatrixBD subroutine. |
|
|
//| Z - multiplied matrix. |
|
|
//| Array whose indexes range within |
|
|
//| [0..ZRows-1,0..ZColumns-1]. |
|
|
//| ZRows - number of rows in matrix Z. If |
|
|
//| FromTheRight=False, ZRows=N, otherwise ZRows |
|
|
//| can be arbitrary. |
|
|
//| ZColumns - number of columns in matrix Z. If |
|
|
//| FromTheRight=True, ZColumns=N, otherwise |
|
|
//| ZColumns can be arbitrary. |
|
|
//| FromTheRight - pre- or post-multiply. |
|
|
//| DoTranspose - multiply by P or P'. |
|
|
//| Output parameters: |
|
|
//| Z - product of Z and P. |
|
|
//| Array whose indexes range within |
|
|
//| [0..ZRows-1,0..ZColumns-1]. If ZRows=0 or |
|
|
//| ZColumns=0, the array is not modified. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBDMultiplyByP(CMatrixDouble &qp,const int m,const int n,
|
|
double &taup[],CMatrixDouble &z,const int zrows,
|
|
const int zcolumns,const bool fromtheright,
|
|
const bool dotranspose)
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0 || zrows<=0 || zcolumns<=0)
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((fromtheright && zcolumns==n) || (!fromtheright && zrows==n),__FUNCTION__+": incorrect Z size!"))
|
|
return;
|
|
//--- create arrays
|
|
double v[];
|
|
double work[];
|
|
//--- create variables
|
|
int i=0;
|
|
int mx=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
int istep=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- initialization
|
|
mx=MathMax(m,n);
|
|
mx=MathMax(mx,zrows);
|
|
mx=MathMax(mx,zcolumns);
|
|
//--- allocation
|
|
ArrayResizeAL(v,mx+1);
|
|
ArrayResizeAL(work,mx+1);
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- setup
|
|
if(fromtheright)
|
|
{
|
|
i1=n-2;
|
|
i2=0;
|
|
istep=-1;
|
|
}
|
|
else
|
|
{
|
|
i1=0;
|
|
i2=n-2;
|
|
istep=1;
|
|
}
|
|
//--- check
|
|
if(!dotranspose)
|
|
{
|
|
i=i1;
|
|
i1=i2;
|
|
i2=i;
|
|
istep=-istep;
|
|
}
|
|
//--- Process
|
|
if(n-1>0)
|
|
{
|
|
i=i1;
|
|
do
|
|
{
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
v[i_]=qp[i][i_+i1_];
|
|
v[1]=1;
|
|
//--- check
|
|
if(fromtheright)
|
|
CReflections::ApplyReflectionFromTheRight(z,taup[i],v,0,zrows-1,i+1,n-1,work);
|
|
else
|
|
CReflections::ApplyReflectionFromTheLeft(z,taup[i],v,i+1,n-1,0,zcolumns-1,work);
|
|
i=i+istep;
|
|
}
|
|
while(i!=i2+istep);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- setup
|
|
if(fromtheright)
|
|
{
|
|
i1=m-1;
|
|
i2=0;
|
|
istep=-1;
|
|
}
|
|
else
|
|
{
|
|
i1=0;
|
|
i2=m-1;
|
|
istep=1;
|
|
}
|
|
//--- check
|
|
if(!dotranspose)
|
|
{
|
|
i=i1;
|
|
i1=i2;
|
|
i2=i;
|
|
istep=-istep;
|
|
}
|
|
//--- Process
|
|
i=i1;
|
|
do
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=n-i;i_++)
|
|
v[i_]=qp[i][i_+i1_];
|
|
v[1]=1;
|
|
//--- check
|
|
if(fromtheright)
|
|
CReflections::ApplyReflectionFromTheRight(z,taup[i],v,0,zrows-1,i,n-1,work);
|
|
else
|
|
CReflections::ApplyReflectionFromTheLeft(z,taup[i],v,i,n-1,0,zcolumns-1,work);
|
|
i=i+istep;
|
|
}
|
|
while(i!=i2+istep);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking of the main and secondary diagonals of bidiagonal |
|
|
//| decomposition of matrix A. |
|
|
//| Input parameters: |
|
|
//| B - output of RMatrixBD subroutine. |
|
|
//| M - number of rows in matrix B. |
|
|
//| N - number of columns in matrix B. |
|
|
//| Output parameters: |
|
|
//| IsUpper - True, if the matrix is upper bidiagonal. |
|
|
//| otherwise IsUpper is False. |
|
|
//| D - the main diagonal. |
|
|
//| Array whose index ranges within [0..Min(M,N)-1]. |
|
|
//| E - the secondary diagonal (upper or lower, depending|
|
|
//| on the value of IsUpper). |
|
|
//| Array index ranges within [0..Min(M,N)-1], the |
|
|
//| last element is not used. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBDUnpackDiagonals(CMatrixDouble &b,const int m,const int n,
|
|
bool &isupper,double &d[],double &e[])
|
|
{
|
|
//--- check
|
|
if(m<=0 || n<=0)
|
|
return;
|
|
//--- create variables
|
|
int i=0;
|
|
//--- check
|
|
if(m>=n)
|
|
isupper=true;
|
|
else
|
|
isupper=false;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(d,n);
|
|
ArrayResizeAL(e,n);
|
|
//--- get result
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
d[i]=b[i][i];
|
|
e[i]=b[i][i+1];
|
|
}
|
|
d[n-1]=b[n-1][n-1];
|
|
}
|
|
else
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(d,m);
|
|
ArrayResizeAL(e,m);
|
|
//--- get result
|
|
for(i=0;i<m-1;i++)
|
|
{
|
|
d[i]=b[i][i];
|
|
e[i]=b[i+1][i];
|
|
}
|
|
d[m-1]=b[m-1][m-1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reduction of a square matrix to upper Hessenberg form: |
|
|
//| Q'*A*Q = H, where Q is an orthogonal matrix, H - Hessenberg |
|
|
//| matrix. |
|
|
//| Input parameters: |
|
|
//| A - matrix A with elements [0..N-1, 0..N-1] |
|
|
//| N - size of matrix A. |
|
|
//| Output parameters: |
|
|
//| A - matrices Q and P in compact form (see below). |
|
|
//| Tau - array of scalar factors which are used to form |
|
|
//| matrix Q. |
|
|
//| Array whose index ranges within [0..N-2] |
|
|
//| Matrix H is located on the main diagonal, on the lower secondary |
|
|
//| diagonal and above the main diagonal of matrix A. The elements |
|
|
//| which are used to form matrix Q are situated in array Tau and |
|
|
//| below the lower secondary diagonal of matrix A as follows: |
|
|
//| Matrix Q is represented as a product of elementary reflections |
|
|
//| Q = H(0)*H(2)*...*H(n-2), |
|
|
//| where each H(i) is given by |
|
|
//| H(i) = 1 - tau * v * (v^T) |
|
|
//| where tau is a scalar stored in Tau[I]; v - is a real vector, |
|
|
//| so that v(0:i) = 0, v(i+1) = 1, v(i+2:n-1) stored in |
|
|
//| A(i+2:n-1,i). |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixHessenberg(CMatrixDouble &a,const int n,double &tau[])
|
|
{
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>=0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- create arrays
|
|
double t[];
|
|
double work[];
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(tau,n-1);
|
|
ArrayResizeAL(t,n+1);
|
|
ArrayResizeAL(work,n);
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
//--- Compute elementary reflector H(i) to annihilate A(i+2:ihi,i)
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CReflections::GenerateReflection(t,n-i-1,v);
|
|
i1_=-i;
|
|
for(i_=i+1;i_<n;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
tau[i]=v;
|
|
t[1]=1;
|
|
//--- Apply H(i) to A(1:ihi,i+1:ihi) from the right
|
|
CReflections::ApplyReflectionFromTheRight(a,v,t,0,n-1,i+1,n-1,work);
|
|
//--- Apply H(i) to A(i+1:ihi,i+1:n) from the left
|
|
CReflections::ApplyReflectionFromTheLeft(a,v,t,i+1,n-1,i+1,n-1,work);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking matrix Q which reduces matrix A to upper Hessenberg |
|
|
//| form |
|
|
//| Input parameters: |
|
|
//| A - output of RMatrixHessenberg subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Tau - scalar factors which are used to form Q. |
|
|
//| Output of RMatrixHessenberg subroutine. |
|
|
//| Output parameters: |
|
|
//| Q - matrix Q. |
|
|
//| Array whose indexes range within [0..N-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixHessenbergUnpackQ(CMatrixDouble &a,const int n,
|
|
double &tau[],CMatrixDouble &q)
|
|
{
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- create arrays
|
|
double v[];
|
|
double work[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
q.Resize(n,n);
|
|
//--- allocation
|
|
ArrayResizeAL(v,n);
|
|
ArrayResizeAL(work,n);
|
|
//--- identity matrix
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- unpack Q
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
//--- Apply H(i)
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
v[i_]=a[i_+i1_][i];
|
|
v[1]=1;
|
|
CReflections::ApplyReflectionFromTheRight(q,tau[i],v,0,n-1,i+1,n-1,work);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking matrix H (the result of matrix A reduction to upper |
|
|
//| Hessenberg form) |
|
|
//| Input parameters: |
|
|
//| A - output of RMatrixHessenberg subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Output parameters: |
|
|
//| H - matrix H. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixHessenbergUnpackH(CMatrixDouble &a,const int n,CMatrixDouble &h)
|
|
{
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- create arrays
|
|
double v[];
|
|
double work[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
//--- allocation
|
|
h.Resize(n,n);
|
|
//--- get result
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<=i-2;j++)
|
|
h[i].Set(j,0);
|
|
j=(int)MathMax(0,i-1);
|
|
for(i_=j;i_<n;i_++)
|
|
h[i].Set(i_,a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reduction of a symmetric matrix which is given by its higher or |
|
|
//| lower triangular part to a tridiagonal matrix using orthogonal |
|
|
//| similarity transformation: Q'*A*Q=T. |
|
|
//| Input parameters: |
|
|
//| A - matrix to be transformed |
|
|
//| array with elements [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format. If IsUpper = True, then matrix A |
|
|
//| is given by its upper triangle, and the lower |
|
|
//| triangle is not used and not modified by the |
|
|
//| algorithm, and vice versa if IsUpper = False. |
|
|
//| Output parameters: |
|
|
//| A - matrices T and Q in compact form (see lower) |
|
|
//| Tau - array of factors which are forming matrices H(i) |
|
|
//| array with elements [0..N-2]. |
|
|
//| D - main diagonal of symmetric matrix T. |
|
|
//| array with elements [0..N-1]. |
|
|
//| E - secondary diagonal of symmetric matrix T. |
|
|
//| array with elements [0..N-2]. |
|
|
//| If IsUpper=True, the matrix Q is represented as a product of |
|
|
//| elementary reflectors |
|
|
//| Q = H(n-2) . . . H(2) H(0). |
|
|
//| Each H(i) has the form |
|
|
//| H(i) = I - tau * v * v' |
|
|
//| where tau is a real scalar, and v is a real vector with |
|
|
//| v(i+1:n-1) = 0, v(i) = 1, v(0:i-1) is stored on exit in |
|
|
//| A(0:i-1,i+1), and tau in TAU(i). |
|
|
//| If IsUpper=False, the matrix Q is represented as a product of |
|
|
//| elementary reflectors |
|
|
//| Q = H(0) H(2) . . . H(n-2). |
|
|
//| Each H(i) has the form |
|
|
//| H(i) = I - tau * v * v' |
|
|
//| where tau is a real scalar, and v is a real vector with |
|
|
//| v(0:i) = 0, v(i+1) = 1, v(i+2:n-1) is stored on exit in |
|
|
//| A(i+2:n-1,i), and tau in TAU(i). |
|
|
//| The contents of A on exit are illustrated by the following |
|
|
//| examples with n = 5: |
|
|
//| if UPLO = 'U': if UPLO = 'L': |
|
|
//| ( d e v1 v2 v3 ) ( d ) |
|
|
//| ( d e v2 v3 ) ( e d ) |
|
|
//| ( d e v3 ) ( v0 e d ) |
|
|
//| ( d e ) ( v0 v1 e d ) |
|
|
//| ( d ) ( v0 v1 v2 e d ) |
|
|
//| where d and e denote diagonal and off-diagonal elements of T, |
|
|
//| and vi denotes an element of the vector defining H(i). |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::SMatrixTD(CMatrixDouble &a,const int n,const bool isupper,
|
|
double &tau[],double &d[],double &e[])
|
|
{
|
|
//--- check
|
|
if(n<=0)
|
|
return;
|
|
//--- create arrays
|
|
double t[];
|
|
double t2[];
|
|
double t3[];
|
|
//--- create variables
|
|
int i=0;
|
|
double alpha=0;
|
|
double taui=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(t,n+1);
|
|
ArrayResizeAL(t2,n+1);
|
|
ArrayResizeAL(t3,n+1);
|
|
//--- check
|
|
if(n>1)
|
|
ArrayResizeAL(tau,n-1);
|
|
ArrayResizeAL(d,n);
|
|
//--- check
|
|
if(n>1)
|
|
ArrayResizeAL(e,n-1);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Reduce the upper triangle of A
|
|
for(i=n-2;i>=0;i--)
|
|
{
|
|
//--- Generate elementary reflector H() = E - tau * v * v'
|
|
if(i>=1)
|
|
{
|
|
i1_=-2;
|
|
for(i_=2;i_<=i+1;i_++)
|
|
t[i_]=a[i_+i1_][i+1];
|
|
}
|
|
t[1]=a[i][i+1];
|
|
CReflections::GenerateReflection(t,i+1,taui);
|
|
//--- check
|
|
if(i>=1)
|
|
{
|
|
i1_=2;
|
|
for(i_=0;i_<i;i_++)
|
|
a[i_].Set(i+1,t[i_+i1_]);
|
|
}
|
|
a[i].Set(i+1,t[1]);
|
|
e[i]=a[i][i+1];
|
|
//--- check
|
|
if(taui!=0)
|
|
{
|
|
//--- Apply H from both sides to A
|
|
a[i].Set(i+1,1);
|
|
//--- Compute x := tau * A * v storing x in TAU
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
t[i_]=a[i_+i1_][i+1];
|
|
CSblas::SymmetricMatrixVectorMultiply(a,isupper,0,i,t,taui,t3);
|
|
i1_=1;
|
|
for(i_=0;i_<=i;i_++)
|
|
tau[i_]=t3[i_+i1_];
|
|
//--- Compute w := x - 1/2 * tau * (x'*v) * v
|
|
v=0.0;
|
|
for(i_=0;i_<=i;i_++)
|
|
v+=tau[i_]*a[i_][i+1];
|
|
alpha=-(0.5*taui*v);
|
|
for(i_=0;i_<=i;i_++)
|
|
tau[i_]=tau[i_]+alpha*a[i_][i+1];
|
|
//--- Apply the transformation as a rank-2 update:
|
|
//--- A := A - v * w' - w * v'
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
t[i_]=a[i_+i1_][i+1];
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
t3[i_]=tau[i_+i1_];
|
|
CSblas::SymmetricRank2Update(a,isupper,0,i,t,t3,t2,-1);
|
|
a[i].Set(i+1,e[i]);
|
|
}
|
|
d[i+1]=a[i+1][i+1];
|
|
tau[i]=taui;
|
|
}
|
|
d[0]=a[0][0];
|
|
}
|
|
else
|
|
{
|
|
//--- Reduce the lower triangle of A
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
//--- Generate elementary reflector H = E - tau * v * v'
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CReflections::GenerateReflection(t,n-i-1,taui);
|
|
i1_=-i;
|
|
for(i_=i+1;i_<n;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
e[i]=a[i+1][i];
|
|
if(taui!=0)
|
|
{
|
|
//--- Apply H from both sides to A
|
|
a[i+1].Set(i,1);
|
|
//--- Compute x := tau * A * v storing y in TAU
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CSblas::SymmetricMatrixVectorMultiply(a,isupper,i+1,n-1,t,taui,t2);
|
|
i1_=1-i;
|
|
for(i_=i;i_<n-1;i_++)
|
|
tau[i_]=t2[i_+i1_];
|
|
//--- Compute w := x - 1/2 * tau * (x'*v) * v
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=i;i_<=n-2;i_++)
|
|
v+=tau[i_]*a[i_+i1_][i];
|
|
alpha=-(0.5*taui*v);
|
|
i1_=1;
|
|
for(i_=i;i_<n-1;i_++)
|
|
tau[i_]=tau[i_]+alpha*a[i_+i1_][i];
|
|
//--- Apply the transformation as a rank-2 update:
|
|
//--- A := A - v * w' - w * v'
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
i1_=i-1;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t2[i_]=tau[i_+i1_];
|
|
CSblas::SymmetricRank2Update(a,isupper,i+1,n-1,t,t2,t3,-1);
|
|
a[i+1].Set(i,e[i]);
|
|
}
|
|
d[i]=a[i][i];
|
|
tau[i]=taui;
|
|
}
|
|
d[n-1]=a[n-1][n-1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking matrix Q which reduces symmetric matrix to a |
|
|
//| tridiagonal form. |
|
|
//| Input parameters: |
|
|
//| A - the result of a SMatrixTD subroutine |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format (a parameter of SMatrixTD |
|
|
//| subroutine) |
|
|
//| Tau - the result of a SMatrixTD subroutine |
|
|
//| Output parameters: |
|
|
//| Q - transformation matrix. |
|
|
//| array with elements [0..N-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::SMatrixTDUnpackQ(CMatrixDouble &a,const int n,const bool isupper,
|
|
double &tau[],CMatrixDouble &q)
|
|
{
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- create arrays
|
|
double v[];
|
|
double work[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
q.Resize(n,n);
|
|
//--- allocation
|
|
ArrayResizeAL(v,n+1);
|
|
ArrayResizeAL(work,n);
|
|
//--- identity matrix
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- unpack Q
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
//--- Apply H(i)
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
v[i_]=a[i_+i1_][i+1];
|
|
v[i+1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(q,tau[i],v,0,i,0,n-1,work);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=n-2;i>=0;i--)
|
|
{
|
|
//--- Apply H(i)
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
v[i_]=a[i_+i1_][i];
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(q,tau[i],v,i+1,n-1,0,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reduction of a Hermitian matrix which is given by its higher or |
|
|
//| lower triangular part to a real tridiagonal matrix using unitary |
|
|
//| similarity transformation: Q'*A*Q = T. |
|
|
//| Input parameters: |
|
|
//| A - matrix to be transformed |
|
|
//| array with elements [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format. If IsUpper = True, then matrix A |
|
|
//| is given by its upper triangle, and the lower |
|
|
//| triangle is not used and not modified by the |
|
|
//| algorithm, and vice versa if IsUpper = False. |
|
|
//| Output parameters: |
|
|
//| A - matrices T and Q in compact form (see lower) |
|
|
//| Tau - array of factors which are forming matrices H(i) |
|
|
//| array with elements [0..N-2]. |
|
|
//| D - main diagonal of real symmetric matrix T. |
|
|
//| array with elements [0..N-1]. |
|
|
//| E - secondary diagonal of real symmetric matrix T. |
|
|
//| array with elements [0..N-2]. |
|
|
//| If IsUpper=True, the matrix Q is represented as a product of |
|
|
//| elementary reflectors |
|
|
//| Q = H(n-2) . . . H(2) H(0). |
|
|
//| Each H(i) has the form |
|
|
//| H(i) = I - tau * v * v' |
|
|
//| where tau is a complex scalar, and v is a complex vector with |
|
|
//| v(i+1:n-1) = 0, v(i) = 1, v(0:i-1) is stored on exit in |
|
|
//| A(0:i-1,i+1), and tau in TAU(i). |
|
|
//| If IsUpper=False, the matrix Q is represented as a product of |
|
|
//| elementary reflectors |
|
|
//| Q = H(0) H(2) . . . H(n-2). |
|
|
//| Each H(i) has the form |
|
|
//| H(i) = I - tau * v * v' |
|
|
//| where tau is a complex scalar, and v is a complex vector with |
|
|
//| v(0:i) = 0, v(i+1) = 1, v(i+2:n-1) is stored on exit in |
|
|
//| A(i+2:n-1,i), and tau in TAU(i). |
|
|
//| The contents of A on exit are illustrated by the following |
|
|
//| examples with n = 5: |
|
|
//| if UPLO = 'U': if UPLO = 'L': |
|
|
//| ( d e v1 v2 v3 ) ( d ) |
|
|
//| ( d e v2 v3 ) ( e d ) |
|
|
//| ( d e v3 ) ( v0 e d ) |
|
|
//| ( d e ) ( v0 v1 e d ) |
|
|
//| ( d ) ( v0 v1 v2 e d ) |
|
|
//| where d and e denote diagonal and off-diagonal elements of T, and|
|
|
//| vi denotes an element of the vector defining H(i). |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::HMatrixTD(CMatrixComplex &a,const int n,const bool isupper,
|
|
complex &tau[],double &d[],double &e[])
|
|
{
|
|
//--- check
|
|
if(n<=0)
|
|
return;
|
|
//--- create arrays
|
|
complex t[];
|
|
complex t2[];
|
|
complex t3[];
|
|
//--- create variables
|
|
complex Half(0.5,0);
|
|
complex Zero(0,0);
|
|
complex _One(-1,0);
|
|
complex alpha=0;
|
|
complex taui=0;
|
|
complex v=0;
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(a[i][i].im==0))
|
|
return;
|
|
}
|
|
//--- allocation
|
|
if(n>1)
|
|
{
|
|
ArrayResizeAL(tau,n-1);
|
|
ArrayResizeAL(e,n-1);
|
|
}
|
|
ArrayResizeAL(d,n);
|
|
ArrayResizeAL(t,n);
|
|
ArrayResizeAL(t2,n);
|
|
ArrayResizeAL(t3,n);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Reduce the upper triangle of A
|
|
a[n-1].Set(n-1,a[n-1][n-1].re);
|
|
for(i=n-2;i>=0;i--)
|
|
{
|
|
//--- Generate elementary reflector H = I+1 - tau * v * v'
|
|
alpha=a[i][i+1];
|
|
t[1]=alpha;
|
|
//--- check
|
|
if(i>=1)
|
|
{
|
|
i1_=-2;
|
|
for(i_=2;i_<=i+1;i_++)
|
|
t[i_]=a[i_+i1_][i+1];
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(t,i+1,taui);
|
|
//--- check
|
|
if(i>=1)
|
|
{
|
|
i1_=2;
|
|
for(i_=0;i_<i;i_++)
|
|
a[i_].Set(i+1,t[i_+i1_]);
|
|
}
|
|
//--- change values
|
|
alpha=t[1];
|
|
e[i]=alpha.re;
|
|
//--- check
|
|
if(taui!=Zero)
|
|
{
|
|
//--- Apply H(I+1) from both sides to A
|
|
a[i].Set(i+1,1);
|
|
//--- Compute x := tau * A * v storing x in TAU
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
t[i_]=a[i_+i1_][i+1];
|
|
CHblas::HermitianMatrixVectorMultiply(a,isupper,0,i,t,taui,t2);
|
|
i1_=1;
|
|
for(i_=0;i_<=i;i_++)
|
|
tau[i_]=t2[i_+i1_];
|
|
//--- Compute w := x - 1/2 * tau * (x'*v) * v
|
|
v=0.0;
|
|
for(i_=0;i_<=i;i_++)
|
|
v+=CMath::Conj(tau[i_])*a[i_][i+1];
|
|
//--- calculation
|
|
alpha=Half*taui*v;
|
|
alpha.re=-alpha.re;
|
|
alpha.im=-alpha.im;
|
|
for(i_=0;i_<=i;i_++)
|
|
tau[i_]=tau[i_]+alpha*a[i_][i+1];
|
|
//--- Apply the transformation as a rank-2 update:
|
|
//--- A := A - v * w' - w * v'
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
t[i_]=a[i_+i1_][i+1];
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
t3[i_]=tau[i_+i1_];
|
|
CHblas::HermitianRank2Update(a,isupper,0,i,t,t3,t2,_One);
|
|
}
|
|
else
|
|
a[i].Set(i,a[i][i].re);
|
|
//--- change values
|
|
a[i].Set(i+1,e[i]);
|
|
d[i+1]=a[i+1][i+1].re;
|
|
tau[i]=taui;
|
|
}
|
|
d[0]=a[0][0].re;
|
|
}
|
|
else
|
|
{
|
|
//--- Reduce the lower triangle of A
|
|
a[0].Set(0,a[0][0].re);
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
//--- Generate elementary reflector H = I - tau * v * v'
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(t,n-i-1,taui);
|
|
i1_=-i;
|
|
for(i_=i+1;i_<n;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
e[i]=a[i+1][i].re;
|
|
//--- check
|
|
if(taui!=Zero)
|
|
{
|
|
//--- Apply H(i) from both sides to A(i+1:n,i+1:n)
|
|
a[i+1].Set(i,1);
|
|
//--- Compute x := tau * A * v storing y in TAU
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CHblas::HermitianMatrixVectorMultiply(a,isupper,i+1,n-1,t,taui,t2);
|
|
i1_=1-i;
|
|
for(i_=i;i_<n-1;i_++)
|
|
tau[i_]=t2[i_+i1_];
|
|
//--- Compute w := x - 1/2 * tau * (x'*v) * v
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=i;i_<n-1;i_++)
|
|
v+=CMath::Conj(tau[i_])*a[i_+i1_][i];
|
|
//--- calculation
|
|
alpha=Half*taui*v;
|
|
alpha.re=-alpha.re;
|
|
alpha.im=-alpha.im;
|
|
i1_=1;
|
|
for(i_=i;i_<n-1;i_++)
|
|
tau[i_]=tau[i_]+alpha*a[i_+i1_][i];
|
|
//--- Apply the transformation as a rank-2 update:
|
|
//--- A := A - v * w' - w * v'
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
i1_=i-1;
|
|
for(i_=1;i_<n-i;i_++)
|
|
t2[i_]=tau[i_+i1_];
|
|
CHblas::HermitianRank2Update(a,isupper,i+1,n-1,t,t2,t3,_One);
|
|
}
|
|
else
|
|
a[i+1].Set(i+1,a[i+1][i+1].re);
|
|
//--- change values
|
|
a[i+1].Set(i,e[i]);
|
|
d[i]=a[i][i].re;
|
|
tau[i]=taui;
|
|
}
|
|
d[n-1]=a[n-1][n-1].re;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpacking matrix Q which reduces a Hermitian matrix to a real |
|
|
//| tridiagonal form. |
|
|
//| Input parameters: |
|
|
//| A - the result of a HMatrixTD subroutine |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format (a parameter of HMatrixTD |
|
|
//| subroutine) |
|
|
//| Tau - the result of a HMatrixTD subroutine |
|
|
//| Output parameters: |
|
|
//| Q - transformation matrix. |
|
|
//| array with elements [0..N-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::HMatrixTDUnpackQ(CMatrixComplex &a,const int n,const bool isupper,
|
|
complex &tau[],CMatrixComplex &q)
|
|
{
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- create arrays
|
|
complex v[];
|
|
complex work[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
q.Resize(n,n);
|
|
//--- allocation
|
|
ArrayResizeAL(v,n+1);
|
|
ArrayResizeAL(work,n+1);
|
|
//--- identity matrix
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- unpack Q
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n-1;i++)
|
|
{
|
|
//--- Apply H(i)
|
|
i1_=-1;
|
|
for(i_=1;i_<=i+1;i_++)
|
|
v[i_]=a[i_+i1_][i+1];
|
|
v[i+1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(q,tau[i],v,0,i,0,n-1,work);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=n-2;i>=0;i--)
|
|
{
|
|
//--- Apply H(i)
|
|
i1_=i;
|
|
for(i_=1;i_<n-i;i_++)
|
|
v[i_]=a[i_+i1_][i];
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(q,tau[i],v,i+1,n-1,0,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Base case for real QR |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixQRBaseCase(CMatrixDouble &a,const int m,const int n,
|
|
double &work[],double &t[],double &tau[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=MathMin(m,n);
|
|
int minmn=MathMin(m,n);
|
|
double tmp=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- calculations
|
|
for(i=0;i<k;i++)
|
|
{
|
|
//--- Generate elementary reflector H(i) to annihilate A(i+1:m,i)
|
|
i1_=i-1;
|
|
for(i_=1;i_<=m-i;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
CReflections::GenerateReflection(t,m-i,tmp);
|
|
tau[i]=tmp;
|
|
i1_=1-i;
|
|
for(i_=i;i_<m;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- check
|
|
if(i<n)
|
|
{
|
|
//--- Apply H(i) to A(i:m-1,i+1:n-1) from the left
|
|
CReflections::ApplyReflectionFromTheLeft(a,tau[i],t,i,m-1,i+1,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Base case for real LQ |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixLQBaseCase(CMatrixDouble &a,const int m,const int n,
|
|
double &work[],double &t[],double &tau[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=MathMin(m,n);
|
|
int minmn=MathMin(m,n);
|
|
double tmp=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- calculation
|
|
for(i=0;i<k;i++)
|
|
{
|
|
//--- Generate elementary reflector H(i) to annihilate A(i,i+1:n-1)
|
|
i1_=i-1;
|
|
for(i_=1;i_<=n-i;i_++)
|
|
t[i_]=a[i][i_+i1_];
|
|
CReflections::GenerateReflection(t,n-i,tmp);
|
|
tau[i]=tmp;
|
|
i1_=1-i;
|
|
for(i_=i;i_<n;i_++)
|
|
a[i].Set(i_,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- check
|
|
if(i<n)
|
|
{
|
|
//--- Apply H(i) to A(i+1:m,i:n) from the right
|
|
CReflections::ApplyReflectionFromTheRight(a,tau[i],t,i+1,m-1,i,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Base case for complex QR |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixQRBaseCase(CMatrixComplex &a,const int m,const int n,
|
|
complex &work[],complex &t[],complex &tau[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=MathMin(m,n);
|
|
int mmi=0;
|
|
int minmn=MathMin(m,n);
|
|
complex tmp=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(minmn<=0)
|
|
return;
|
|
//--- calculation
|
|
for(i=0;i<k;i++)
|
|
{
|
|
//--- Generate elementary reflector H(i) to annihilate A(i+1:m,i)
|
|
mmi=m-i;
|
|
i1_=i-1;
|
|
for(i_=1;i_<=mmi;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(t,mmi,tmp);
|
|
tau[i]=tmp;
|
|
i1_=1-i;
|
|
for(i_=i;i_<m;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
t[1]=1;
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
//--- Apply H'(i) to A(i:m,i+1:n) from the left
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(a,CMath::Conj(tau[i]),t,i,m-1,i+1,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Base case for complex LQ |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixLQBaseCase(CMatrixComplex &a,const int m,const int n,
|
|
complex &work[],complex &t[],complex &tau[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int minmn=MathMin(m,n);
|
|
complex tmp=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(minmn<=0)
|
|
return;
|
|
//--- calculation
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
//--- Generate elementary reflector H(i)
|
|
//--- NOTE: ComplexGenerateReflection() generates left reflector,
|
|
//--- i.e. H which reduces x by applyiong from the left, but we
|
|
//--- need RIGHT reflector. So we replace H=E-tau*v*v' by H^H,
|
|
//--- which changes v to conj(v).
|
|
i1_=i-1;
|
|
for(i_=1;i_<=n-i;i_++)
|
|
t[i_]=CMath::Conj(a[i][i_+i1_]);
|
|
CComplexReflections::ComplexGenerateReflection(t,n-i,tmp);
|
|
tau[i]=tmp;
|
|
i1_=1-i;
|
|
for(i_=i;i_<n;i_++)
|
|
a[i].Set(i_,CMath::Conj(t[i_+i1_]));
|
|
t[1]=1;
|
|
//--- check
|
|
if(i<m-1)
|
|
{
|
|
//--- Apply H'(i)
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(a,tau[i],t,i+1,m-1,i,n-1,work);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate block reflector: |
|
|
//| * fill unused parts of reflectors matrix by zeros |
|
|
//| * fill diagonal of reflectors matrix by ones |
|
|
//| * generate triangular factor T |
|
|
//| PARAMETERS: |
|
|
//| A - either LengthA*BlockSize (if ColumnwiseA) or |
|
|
//| BlockSize*LengthA (if not ColumnwiseA) matrix|
|
|
//| of elementary reflectors. |
|
|
//| Modified on exit. |
|
|
//| Tau - scalar factors |
|
|
//| ColumnwiseA - reflectors are stored in rows or in columns |
|
|
//| LengthA - length of largest reflector |
|
|
//| BlockSize - number of reflectors |
|
|
//| T - array[BlockSize,2*BlockSize]. Left |
|
|
//| BlockSize*BlockSize submatrix stores |
|
|
//| triangular factor on exit. |
|
|
//| WORK - array[BlockSize] |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::RMatrixBlockReflector(CMatrixDouble &a,double &tau[],
|
|
const bool columnwisea,const int lengtha,
|
|
const int blocksize,CMatrixDouble &t,double &work[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- fill beginning of new column with zeros,
|
|
//--- load 1.0 in the first non-zero element
|
|
for(k=0;k<blocksize;k++)
|
|
{
|
|
//--- check
|
|
if(columnwisea)
|
|
{
|
|
for(i=0;i<k;i++)
|
|
a[i].Set(k,0);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<k;i++)
|
|
a[k].Set(i,0);
|
|
}
|
|
a[k].Set(k,1);
|
|
}
|
|
//--- Calculate Gram matrix of A
|
|
for(i=0;i<blocksize;i++)
|
|
{
|
|
for(j=0;j<blocksize;j++)
|
|
t[i].Set(blocksize+j,0);
|
|
}
|
|
for(k=0;k<=lengtha-1;k++)
|
|
{
|
|
for(j=1;j<blocksize;j++)
|
|
{
|
|
//--- check
|
|
if(columnwisea)
|
|
{
|
|
v=a[k][j];
|
|
//--- check
|
|
if(v!=0)
|
|
{
|
|
i1_=-blocksize;
|
|
for(i_=blocksize;i_<=blocksize+j-1;i_++)
|
|
t[j].Set(i_,t[j][i_]+v*a[k][i_+i1_]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
v=a[j][k];
|
|
//--- check
|
|
if(v!=0)
|
|
{
|
|
i1_=-blocksize;
|
|
for(i_=blocksize;i_<=blocksize+j-1;i_++)
|
|
t[j].Set(i_,t[j][i_]+v*a[i_+i1_][k]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Prepare Y (stored in TmpA) and T (stored in TmpT)
|
|
for(k=0;k<blocksize;k++)
|
|
{
|
|
//--- fill non-zero part of T, use pre-calculated Gram matrix
|
|
i1_=blocksize;
|
|
for(i_=0;i_<k;i_++)
|
|
work[i_]=t[k][i_+i1_];
|
|
for(i=0;i<k;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=i;i_<k;i_++)
|
|
v+=t[i][i_]*work[i_];
|
|
t[i].Set(k,-(tau[k]*v));
|
|
}
|
|
t[k].Set(k,-tau[k]);
|
|
//--- Rest of T is filled by zeros
|
|
for(i=k+1;i<blocksize;i++)
|
|
t[i].Set(k,0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate block reflector (complex): |
|
|
//| * fill unused parts of reflectors matrix by zeros |
|
|
//| * fill diagonal of reflectors matrix by ones |
|
|
//| * generate triangular factor T |
|
|
//+------------------------------------------------------------------+
|
|
static void COrtFac::CMatrixBlockReflector(CMatrixComplex &a,complex &tau[],
|
|
const bool columnwisea,const int lengtha,
|
|
const int blocksize,CMatrixComplex &t,complex &work[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
complex v=0;
|
|
complex tauv=0;
|
|
int i_=0;
|
|
//--- Prepare Y (stored in TmpA) and T (stored in TmpT)
|
|
for(k=0;k<blocksize;k++)
|
|
{
|
|
//--- fill beginning of new column with zeros,
|
|
//--- load 1.0 in the first non-zero element
|
|
if(columnwisea)
|
|
{
|
|
for(i=0;i<k;i++)
|
|
a[i].Set(k,0);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<k;i++)
|
|
a[k].Set(i,0);
|
|
}
|
|
a[k].Set(k,1);
|
|
//--- fill non-zero part of T
|
|
for(i=0;i<k;i++)
|
|
{
|
|
//--- check
|
|
if(columnwisea)
|
|
{
|
|
v=0.0;
|
|
for(i_=k;i_<=lengtha-1;i_++)
|
|
v+=CMath::Conj(a[i_][i])*a[i_][k];
|
|
}
|
|
else
|
|
{
|
|
v=0.0;
|
|
for(i_=k;i_<=lengtha-1;i_++)
|
|
v+=a[i][i_]*CMath::Conj(a[k][i_]);
|
|
}
|
|
work[i]=v;
|
|
}
|
|
for(i=0;i<k;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=i;i_<k;i_++)
|
|
v+=t[i][i_]*work[i_];
|
|
//--- change
|
|
tauv=tau[k]*v;
|
|
tauv.re=-tauv.re;
|
|
tauv.im=-tauv.im;
|
|
t[i].Set(k,tauv);
|
|
}
|
|
//--- change
|
|
tauv=tau[k];
|
|
tauv.re=-tauv.re;
|
|
tauv.im=-tauv.im;
|
|
t[k].Set(k,tauv);
|
|
//--- Rest of T is filled by zeros
|
|
for(i=k+1;i<blocksize;i++)
|
|
t[i].Set(k,0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Eigenvalues and eigenvectors |
|
|
//+------------------------------------------------------------------+
|
|
class CEigenVDetect
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static bool TtidiagonalEVD(double &d[],double &ce[],const int n,const int zneeded,CMatrixDouble &z);
|
|
static void TdEVDE2(const double a,const double b,const double c,double &rt1,double &rt2);
|
|
static void TdEVDEv2(const double a,const double b,const double c,double &rt1,double &rt2,double &cs1,double &sn1);
|
|
static double TdEVDPythag(const double a,const double b);
|
|
static double TdEVDExtSign(const double a,const double b);
|
|
static bool InternalBisectionEigenValues(double &cd[],double &ce[],const int n,int irange,const int iorder,const double vl,const double vu,const int il,const int iu,const double abstol,double &w[],int &m,int &nsplit,int &iblock[],int &isplit[],int &errorcode);
|
|
static void InternalDStein(const int n,double &d[],double &ce[],const int m,double &cw[],int &iblock[],int &isplit[],CMatrixDouble &z,int &ifail[],int &info);
|
|
static void TdIninternalDLAGTF(const int n,double &a[],const double lambdav,double &b[],double &c[],double tol,double &d[],int &iin[],int &info);
|
|
static void TdIninternalDLAGTS(const int n,double &a[],double &b[],double &c[],double &d[],int &iin[],double &y[],double &tol,int &info);
|
|
static void InternalDLAEBZ(const int ijob,const int nitmax,const int n,const int mmax,const int minp,const double abstol,const double reltol,const double pivmin,double &d[],double &e[],double &e2[],int &nval[],CMatrixDouble &ab,double &c[],int &mout,CMatrixInt &nab,double &work[],int &iwork[],int &info);
|
|
static void InternalTREVC(CMatrixDouble &t,const int n,const int side,const int howmny,bool &cvselect[],CMatrixDouble &vl,CMatrixDouble &vr,int &m,int &info);
|
|
static void InternalHsEVDLALN2(const bool ltrans,const int na,const int nw,const double smin,const double ca,CMatrixDouble &a,const double d1,const double d2,CMatrixDouble &b,const double wr,const double wi,bool &rswap4[],bool &zswap4[],CMatrixInt &ipivot44,double &civ4[],double &crv4[],CMatrixDouble &x,double &scl,double &xnorm,int &info);
|
|
static void InternalHsEVDLADIV(const double a,const double b,const double c,const double d,double &p,double &q);
|
|
static bool NonSymmetricEVD(CMatrixDouble &ca,const int n,const int vneeded,double &wr[],double &wi[],CMatrixDouble &vl,CMatrixDouble &vr);
|
|
static void ToUpperHessenberg(CMatrixDouble &a,const int n,double &tau[]);
|
|
static void UnpackQFromUpperHessenberg(CMatrixDouble &a,const int n,double &tau[],CMatrixDouble &q);
|
|
public:
|
|
CEigenVDetect(void);
|
|
~CEigenVDetect(void);
|
|
//--- public methods
|
|
static bool SMatrixEVD(CMatrixDouble &ca,const int n,const int zneeded,const bool isupper,double &d[],CMatrixDouble &z);
|
|
static bool SMatrixEVDR(CMatrixDouble &ca,const int n,const int zneeded,const bool isupper,const double b1,const double b2,int &m,double &w[],CMatrixDouble &z);
|
|
static bool SMatrixEVDI(CMatrixDouble &ca,const int n,const int zneeded,const bool isupper,const int i1,const int i2,double &w[],CMatrixDouble &z);
|
|
static bool HMatrixEVD(CMatrixComplex &ca,const int n,int zneeded,const bool isupper,double &d[],CMatrixComplex &z);
|
|
static bool HMatrixEVDR(CMatrixComplex &ca,const int n,int zneeded,bool isupper,const double b1,const double b2,int &m,double &w[],CMatrixComplex &z);
|
|
static bool HMatrixEVDI(CMatrixComplex &ca,const int n,int zneeded,const bool isupper,const int i1,const int i2,double &w[],CMatrixComplex &z);
|
|
static bool SMatrixTdEVD(double &d[],double &ce[],const int n,const int zneeded,CMatrixDouble &z);
|
|
static bool SMatrixTdEVDR(double &d[],double &e[],const int n,const int zneeded,const double a,const double b,int &m,CMatrixDouble &z);
|
|
static bool SMatrixTdEVDI(double &d[],double &e[],const int n,const int zneeded,const int i1,const int i2,CMatrixDouble &z);
|
|
static bool RMatrixEVD(CMatrixDouble &ca,const int n,const int vneeded,double &wr[],double &wi[],CMatrixDouble &vl,CMatrixDouble &vr);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CEigenVDetect::CEigenVDetect(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CEigenVDetect::~CEigenVDetect(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Finding the eigenvalues and eigenvectors of a symmetric matrix |
|
|
//| The algorithm finds eigen pairs of a symmetric matrix by reducing|
|
|
//| it to tridiagonal form and using the QL/QR algorithm. |
|
|
//| Input parameters: |
|
|
//| A - symmetric matrix which is given by its upper or |
|
|
//| lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. |
|
|
//| If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| IsUpper - storage format. |
|
|
//| Output parameters: |
|
|
//| D - eigenvalues in ascending order. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains the eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| The eigenvectors are stored in the matrix |
|
|
//| columns. |
|
|
//| Result: |
|
|
//| True, if the algorithm has converged. |
|
|
//| False, if the algorithm hasn't converged (rare case). |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::SMatrixEVD(CMatrixDouble &ca,const int n,const int zneeded,
|
|
const bool isupper,double &d[],CMatrixDouble &z)
|
|
{
|
|
//--- create arrays
|
|
double tau[];
|
|
double e[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded==0 || zneeded==1,__FUNCTION__+": incorrect ZNeeded"))
|
|
return(false);
|
|
//--- function call
|
|
COrtFac::SMatrixTD(a,n,isupper,tau,d,e);
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
//--- function call
|
|
COrtFac::SMatrixTDUnpackQ(a,n,isupper,tau,z);
|
|
}
|
|
//--- return result
|
|
return(SMatrixTdEVD(d,e,n,zneeded,z));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for finding the eigenvalues (and eigenvectors) of a |
|
|
//| symmetric matrix in a given half open interval (A, B] by using a |
|
|
//| bisection and inverse iteration |
|
|
//| Input parameters: |
|
|
//| A - symmetric matrix which is given by its upper or |
|
|
//| lower triangular part. Array [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. |
|
|
//| If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| IsUpperA - storage format of matrix A. |
|
|
//| B1, B2 - half open interval (B1, B2] to search |
|
|
//| eigenvalues in. |
|
|
//| Output parameters: |
|
|
//| M - number of eigenvalues found in a given |
|
|
//| half-interval (M>=0). |
|
|
//| W - array of the eigenvalues found. |
|
|
//| Array whose index ranges within [0..M-1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..M-1]. |
|
|
//| The eigenvectors are stored in the matrix |
|
|
//| columns. |
|
|
//| Result: |
|
|
//| True, if successful. M contains the number of eigenvalues in |
|
|
//| the given half-interval (could be equal to 0), W contains the|
|
|
//| eigenvalues, Z contains the eigenvectors (if needed). |
|
|
//| False, if the bisection method subroutine wasn't able to find|
|
|
//| the eigenvalues in the given interval or if the inverse |
|
|
//| iteration subroutine wasn't able to find all the |
|
|
//| corresponding eigenvectors. In that case, the eigenvalues |
|
|
//| and eigenvectors are not returned, M is equal to 0. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::SMatrixEVDR(CMatrixDouble &ca,const int n,const int zneeded,
|
|
const bool isupper,const double b1,const double b2,
|
|
int &m,double &w[],CMatrixDouble &z)
|
|
{
|
|
//--- create arrays
|
|
double tau[];
|
|
double e[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- initialization
|
|
m=0;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded==0 || zneeded==1,__FUNCTION__+": incorrect ZNeeded"))
|
|
return(false);
|
|
//--- function call
|
|
COrtFac::SMatrixTD(a,n,isupper,tau,w,e);
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
//--- function call
|
|
COrtFac::SMatrixTDUnpackQ(a,n,isupper,tau,z);
|
|
}
|
|
//--- return result
|
|
return(SMatrixTdEVDR(w,e,n,zneeded,b1,b2,m,z));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for finding the eigenvalues and eigenvectors of a |
|
|
//| symmetric matrix with given indexes by using bisection and |
|
|
//| inverse iteration methods. |
|
|
//| Input parameters: |
|
|
//| A - symmetric matrix which is given by its upper or |
|
|
//| lower triangular part. Array whose indexes range |
|
|
//| within [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. |
|
|
//| If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| IsUpperA - storage format of matrix A. |
|
|
//| I1, I2 - index interval for searching (from I1 to I2). |
|
|
//| 0 <= I1 <= I2 <= N-1. |
|
|
//| Output parameters: |
|
|
//| W - array of the eigenvalues found. |
|
|
//| Array whose index ranges within [0..I2-I1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..I2-I1]. |
|
|
//| In that case, the eigenvectors are stored in the |
|
|
//| matrix columns. |
|
|
//| Result: |
|
|
//| True, if successful. W contains the eigenvalues, Z contains |
|
|
//| the eigenvectors (if needed). |
|
|
//| False, if the bisection method subroutine wasn't able to find|
|
|
//| the eigenvalues in the given interval or if the inverse |
|
|
//| iteration subroutine wasn't able to find all the |
|
|
//| corresponding eigenvectors. In that case, the eigenvalues |
|
|
//| and eigenvectors are not returned. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::SMatrixEVDI(CMatrixDouble &ca,const int n,const int zneeded,
|
|
const bool isupper,const int i1,const int i2,
|
|
double &w[],CMatrixDouble &z)
|
|
{
|
|
//--- create arrays
|
|
double tau[];
|
|
double e[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded==0 || zneeded==1,__FUNCTION__+": incorrect ZNeeded"))
|
|
return(false);
|
|
//--- function call
|
|
COrtFac::SMatrixTD(a,n,isupper,tau,w,e);
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
//--- function call
|
|
COrtFac::SMatrixTDUnpackQ(a,n,isupper,tau,z);
|
|
}
|
|
//--- return result
|
|
return(SMatrixTdEVDI(w,e,n,zneeded,i1,i2,z));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Finding the eigenvalues and eigenvectors of a Hermitian matrix |
|
|
//| The algorithm finds eigen pairs of a Hermitian matrix by reducing|
|
|
//| it to real tridiagonal form and using the QL/QR algorithm. |
|
|
//| Input parameters: |
|
|
//| A - Hermitian matrix which is given by its upper or |
|
|
//| lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| Output parameters: |
|
|
//| D - eigenvalues in ascending order. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains the eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| The eigenvectors are stored in the matrix |
|
|
//| columns. |
|
|
//| Result: |
|
|
//| True, if the algorithm has converged. |
|
|
//| False, if the algorithm hasn't converged (rare case). |
|
|
//| Note: |
|
|
//| eigenvectors of Hermitian matrix are defined up to |
|
|
//| multiplication by a complex number L, such that |L|=1. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::HMatrixEVD(CMatrixComplex &ca,const int n,int zneeded,
|
|
const bool isupper,double &d[],CMatrixComplex &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
bool result;
|
|
//--- create arrays
|
|
complex tau[];
|
|
double e[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble t;
|
|
CMatrixComplex q;
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded==0 || zneeded==1,__FUNCTION__+": incorrect ZNeeded"))
|
|
return(false);
|
|
//--- Reduce to tridiagonal form
|
|
COrtFac::HMatrixTD(a,n,isupper,tau,d,e);
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
//--- function call
|
|
COrtFac::HMatrixTDUnpackQ(a,n,isupper,tau,q);
|
|
zneeded=2;
|
|
}
|
|
//--- get result
|
|
result=SMatrixTdEVD(d,e,n,zneeded,t);
|
|
//--- Eigenvectors are needed
|
|
//--- Calculate Z = Q*T = Re(Q)*T + i*Im(Q)*T
|
|
if(result && zneeded!=0)
|
|
{
|
|
ArrayResizeAL(work,n);
|
|
z.Resize(n,n);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- Calculate real part
|
|
for(k=0;k<n;k++)
|
|
work[k]=0;
|
|
for(k=0;k<n;k++)
|
|
{
|
|
v=q[i][k].re;
|
|
for(i_=0;i_<n;i_++)
|
|
work[i_]=work[i_]+v*t[k][i_];
|
|
}
|
|
//--- get real part
|
|
for(k=0;k<n;k++)
|
|
z[i].SetRe(k,work[k]);
|
|
//--- Calculate imaginary part
|
|
for(k=0;k<n;k++)
|
|
work[k]=0;
|
|
for(k=0;k<n;k++)
|
|
{
|
|
v=q[i][k].im;
|
|
for(i_=0;i_<n;i_++)
|
|
work[i_]=work[i_]+v*t[k][i_];
|
|
}
|
|
//--- get imaginary part
|
|
for(k=0;k<n;k++)
|
|
z[i].SetIm(k,work[k]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for finding the eigenvalues (and eigenvectors) of a |
|
|
//| Hermitian matrix in a given half-interval (A, B] by using a |
|
|
//| bisection and inverse iteration |
|
|
//| Input parameters: |
|
|
//| A - Hermitian matrix which is given by its upper or |
|
|
//| lower triangular part. Array whose indexes range |
|
|
//| within [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| IsUpperA - storage format of matrix A. |
|
|
//| B1, B2 - half-interval (B1, B2] to search eigenvalues in. |
|
|
//| Output parameters: |
|
|
//| M - number of eigenvalues found in a given |
|
|
//| half-interval, M>=0 |
|
|
//| W - array of the eigenvalues found. |
|
|
//| Array whose index ranges within [0..M-1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..M-1]. |
|
|
//| The eigenvectors are stored in the matrix |
|
|
//| columns. |
|
|
//| Result: |
|
|
//| True, if successful. M contains the number of eigenvalues |
|
|
//| in the given half-interval (could be equal to 0), W contains |
|
|
//| the eigenvalues, Z contains the eigenvectors (if needed). |
|
|
//| False, if the bisection method subroutine wasn't able to find|
|
|
//| the eigenvalues in the given interval or if the inverse |
|
|
//| iteration subroutine wasn't able to find all the |
|
|
//| corresponding eigenvectors. In that case, the eigenvalues and|
|
|
//| eigenvectors are not returned, M is equal to 0. |
|
|
//| Note: |
|
|
//| eigen vectors of Hermitian matrix are defined up to |
|
|
//| multiplication by a complex number L, such as |L|=1. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::HMatrixEVDR(CMatrixComplex &ca,const int n,int zneeded,
|
|
bool isupper,const double b1,const double b2,
|
|
int &m,double &w[],CMatrixComplex &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
bool result;
|
|
//--- create arrays
|
|
complex tau[];
|
|
double e[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixComplex q;
|
|
CMatrixDouble t;
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- initialization
|
|
m=0;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded==0 || zneeded==1,__FUNCTION__+": incorrect ZNeeded"))
|
|
return(false);
|
|
//--- Reduce to tridiagonal form
|
|
COrtFac::HMatrixTD(a,n,isupper,tau,w,e);
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
//--- function call
|
|
COrtFac::HMatrixTDUnpackQ(a,n,isupper,tau,q);
|
|
zneeded=2;
|
|
}
|
|
//--- Bisection and inverse iteration
|
|
result=SMatrixTdEVDR(w,e,n,zneeded,b1,b2,m,t);
|
|
//--- Eigenvectors are needed
|
|
//--- Calculate Z = Q*T = Re(Q)*T + i*Im(Q)*T
|
|
if((result && zneeded!=0) && m!=0)
|
|
{
|
|
ArrayResizeAL(work,m);
|
|
z.Resize(n,m);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- Calculate real part
|
|
for(k=0;k<=m-1;k++)
|
|
work[k]=0;
|
|
for(k=0;k<n;k++)
|
|
{
|
|
v=q[i][k].re;
|
|
for(i_=0;i_<m;i_++)
|
|
work[i_]=work[i_]+v*t[k][i_];
|
|
}
|
|
//--- get real part
|
|
for(k=0;k<=m-1;k++)
|
|
z[i].SetRe(k,work[k]);
|
|
//--- Calculate imaginary part
|
|
for(k=0;k<=m-1;k++)
|
|
work[k]=0;
|
|
for(k=0;k<n;k++)
|
|
{
|
|
v=q[i][k].im;
|
|
for(i_=0;i_<m;i_++)
|
|
work[i_]=work[i_]+v*t[k][i_];
|
|
}
|
|
//--- get imaginary part
|
|
for(k=0;k<=m-1;k++)
|
|
z[i].SetIm(k,work[k]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for finding the eigenvalues and eigenvectors of a |
|
|
//| Hermitian matrix with given indexes by using bisection and |
|
|
//| inverse iteration methods |
|
|
//| Input parameters: |
|
|
//| A - Hermitian matrix which is given by its upper or |
|
|
//| lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| IsUpperA - storage format of matrix A. |
|
|
//| I1, I2 - index interval for searching (from I1 to I2). |
|
|
//| 0 <= I1 <= I2 <= N-1. |
|
|
//| Output parameters: |
|
|
//| W - array of the eigenvalues found. |
|
|
//| Array whose index ranges within [0..I2-I1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..I2-I1]. |
|
|
//| In that case, the eigenvectors are stored in |
|
|
//| the matrix columns. |
|
|
//| Result: |
|
|
//| True, if successful. W contains the eigenvalues, Z contains |
|
|
//| the eigenvectors (if needed). |
|
|
//| False, if the bisection method subroutine wasn't able to find|
|
|
//| the eigenvalues in the given interval or if the inverse |
|
|
//| corresponding eigenvectors. iteration subroutine wasn't able |
|
|
//| to find all the corresponding eigenvectors. In that case, |
|
|
//| the eigenvalues and eigenvectors are not returned. |
|
|
//| Note: |
|
|
//| eigen vectors of Hermitian matrix are defined up to |
|
|
//| multiplication by a complex number L, such as |L|=1. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::HMatrixEVDI(CMatrixComplex &ca,const int n,int zneeded,
|
|
const bool isupper,const int i1,const int i2,
|
|
double &w[],CMatrixComplex &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
int m=0;
|
|
int i_=0;
|
|
bool result;
|
|
//--- create arrays
|
|
complex tau[];
|
|
double e[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixComplex q;
|
|
CMatrixDouble t;
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded==0 || zneeded==1,__FUNCTION__+": incorrect ZNeeded"))
|
|
return(false);
|
|
//--- Reduce to tridiagonal form
|
|
COrtFac::HMatrixTD(a,n,isupper,tau,w,e);
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
//--- function call
|
|
COrtFac::HMatrixTDUnpackQ(a,n,isupper,tau,q);
|
|
zneeded=2;
|
|
}
|
|
//--- Bisection and inverse iteration
|
|
result=SMatrixTdEVDI(w,e,n,zneeded,i1,i2,t);
|
|
//--- Eigenvectors are needed
|
|
//--- Calculate Z = Q*T = Re(Q)*T + i*Im(Q)*T
|
|
m=i2-i1+1;
|
|
//--- check
|
|
if(result && zneeded!=0)
|
|
{
|
|
ArrayResizeAL(work,m);
|
|
z.Resize(n,m);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- Calculate real part
|
|
for(k=0;k<=m-1;k++)
|
|
work[k]=0;
|
|
for(k=0;k<n;k++)
|
|
{
|
|
v=q[i][k].re;
|
|
for(i_=0;i_<m;i_++)
|
|
work[i_]=work[i_]+v*t[k][i_];
|
|
}
|
|
//--- get real part
|
|
for(k=0;k<=m-1;k++)
|
|
z[i].SetRe(k,work[k]);
|
|
//--- Calculate imaginary part
|
|
for(k=0;k<=m-1;k++)
|
|
work[k]=0;
|
|
for(k=0;k<n;k++)
|
|
{
|
|
v=q[i][k].im;
|
|
for(i_=0;i_<m;i_++)
|
|
work[i_]=work[i_]+v*t[k][i_];
|
|
}
|
|
//--- get imaginary part
|
|
for(k=0;k<=m-1;k++)
|
|
z[i].SetIm(k,work[k]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Finding the eigenvalues and eigenvectors of a tridiagonal |
|
|
//| symmetric matrix |
|
|
//| The algorithm finds the eigen pairs of a tridiagonal symmetric |
|
|
//| matrix by using an QL/QR algorithm with implicit shifts. |
|
|
//| Input parameters: |
|
|
//| D - the main diagonal of a tridiagonal matrix. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| E - the secondary diagonal of a tridiagonal matrix. |
|
|
//| Array whose index ranges within [0..N-2]. |
|
|
//| N - size of matrix A. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. |
|
|
//| If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not needed; |
|
|
//| * 1, the eigenvectors of a tridiagonal matrix |
|
|
//| are multiplied by the square matrix Z. It is |
|
|
//| used if the tridiagonal matrix is obtained by |
|
|
//| the similarity transformation of a symmetric |
|
|
//| matrix; |
|
|
//| * 2, the eigenvectors of a tridiagonal matrix |
|
|
//| replace the square matrix Z; |
|
|
//| * 3, matrix Z contains the first row of the |
|
|
//| eigenvectors matrix. |
|
|
//| Z - if ZNeeded=1, Z contains the square matrix by |
|
|
//| which the eigenvectors are multiplied. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| Output parameters: |
|
|
//| D - eigenvalues in ascending order. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains the product of a given matrix |
|
|
//| (from the left) and the eigenvectors matrix |
|
|
//| (from the right); |
|
|
//| * 2, Z contains the eigenvectors. |
|
|
//| * 3, Z contains the first row of the |
|
|
//| eigenvectors matrix. |
|
|
//| If ZNeeded<3, Z is the array whose indexes range |
|
|
//| within [0..N-1, 0..N-1]. |
|
|
//| In that case, the eigenvectors are stored in the |
|
|
//| matrix columns. |
|
|
//| If ZNeeded=3, Z is the array whose indexes range |
|
|
//| within [0..0, 0..N-1]. |
|
|
//| Result: |
|
|
//| True, if the algorithm has converged. |
|
|
//| False, if the algorithm hasn't converged. |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| September 30, 1994 |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::SMatrixTdEVD(double &d[],double &ce[],const int n,const int zneeded,CMatrixDouble &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
bool result;
|
|
//--- create arrays
|
|
double d1[];
|
|
double e1[];
|
|
//--- create matrix
|
|
CMatrixDouble z1;
|
|
//--- create copy
|
|
double e[];
|
|
ArrayResizeAL(e,ArraySize(ce));
|
|
ArrayCopy(e,ce);
|
|
//--- Prepare 1-based task
|
|
ArrayResizeAL(d1,n+1);
|
|
ArrayResizeAL(e1,n+1);
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
d1[i_]=d[i_+i1_];
|
|
//--- check
|
|
if(n>1)
|
|
{
|
|
i1_=-1;
|
|
for(i_=1;i_<n;i_++)
|
|
e1[i_]=e[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
z1.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
z1[i].Set(i_,z[i-1][i_+i1_]);
|
|
}
|
|
}
|
|
//--- Solve 1-based task
|
|
result=TtidiagonalEVD(d1,e1,n,zneeded,z1);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- Convert back to 0-based result
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
d[i_]=d1[i_+i1_];
|
|
//--- check
|
|
if(zneeded!=0)
|
|
{
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[i-1].Set(i_,z1[i][i_+i1_]);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(zneeded==2)
|
|
{
|
|
z.Resize(n,n);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[i-1].Set(i_,z1[i][i_+i1_]);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(zneeded==3)
|
|
{
|
|
z.Resize(1,n);
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[0].Set(i_,z1[1][i_+i1_]);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(false,__FUNCTION__+": Incorrect ZNeeded!"))
|
|
return(false);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for finding the tridiagonal matrix eigenvalues/vectors|
|
|
//| in a given half-interval (A, B] by using bisection and inverse |
|
|
//| iteration. |
|
|
//| Input parameters: |
|
|
//| D - the main diagonal of a tridiagonal matrix. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| E - the secondary diagonal of a tridiagonal matrix. |
|
|
//| Array whose index ranges within [0..N-2]. |
|
|
//| N - size of matrix, N>=0. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not needed; |
|
|
//| * 1, the eigenvectors of a tridiagonal matrix |
|
|
//| are multiplied by the square matrix Z. It is |
|
|
//| used if the tridiagonal matrix is obtained by |
|
|
//| the similarity transformation of a symmetric |
|
|
//| matrix. |
|
|
//| * 2, the eigenvectors of a tridiagonal matrix |
|
|
//| replace matrix Z. |
|
|
//| A, B - half-interval (A, B] to search eigenvalues in. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z isn't used and remains unchanged; |
|
|
//| * 1, Z contains the square matrix (array whose |
|
|
//| indexes range within [0..N-1, 0..N-1]) which |
|
|
//| reduces the given symmetric matrix to |
|
|
//| tridiagonal form; |
|
|
//| * 2, Z isn't used (but changed on the exit). |
|
|
//| Output parameters: |
|
|
//| D - array of the eigenvalues found. |
|
|
//| Array whose index ranges within [0..M-1]. |
|
|
//| M - number of eigenvalues found in the given |
|
|
//| half-interval (M>=0). |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, doesn't contain any information; |
|
|
//| * 1, contains the product of a given NxN matrix |
|
|
//| Z (from the left) and NxM matrix of the |
|
|
//| eigenvectors found (from the right). Array |
|
|
//| whose indexes range within [0..N-1, 0..M-1]. |
|
|
//| * 2, contains the matrix of the eigenvectors |
|
|
//| found. Array whose indexes range within |
|
|
//| [0..N-1, 0..M-1]. |
|
|
//| Result: |
|
|
//| True, if successful. In that case, M contains the number of |
|
|
//| eigenvalues in the given half-interval (could be equal to 0),|
|
|
//| D contains the eigenvalues, Z contains the eigenvectors (if |
|
|
//| needed). It should be noted that the subroutine changes the |
|
|
//| size of arrays D and Z. |
|
|
//| False, if the bisection method subroutine wasn't able to find|
|
|
//| the eigenvalues in the given interval or if the inverse |
|
|
//| iteration subroutine wasn't able to find all the |
|
|
//| corresponding eigenvectors. In that case, the eigenvalues and|
|
|
//| eigenvectors are not returned, M is equal to 0. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::SMatrixTdEVDR(double &d[],double &e[],const int n,
|
|
const int zneeded,const double a,
|
|
const double b,int &m,CMatrixDouble &z)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int errorcode=0;
|
|
int nsplit=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int cr=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
int iblock[];
|
|
int isplit[];
|
|
int ifail[];
|
|
double d1[];
|
|
double e1[];
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble z2;
|
|
CMatrixDouble z3;
|
|
//--- initialization
|
|
m=0;
|
|
//--- check
|
|
if(!CAp::Assert(zneeded>=0 && zneeded<=2,__FUNCTION__+": incorrect ZNeeded!"))
|
|
return(false);
|
|
//--- check
|
|
if(b<=a)
|
|
{
|
|
m=0;
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
m=0;
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- Copy D,E to D1, E1
|
|
ArrayResizeAL(d1,n+1);
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
d1[i_]=d[i_+i1_];
|
|
//--- check
|
|
if(n>1)
|
|
{
|
|
ArrayResizeAL(e1,n);
|
|
i1_=-1;
|
|
for(i_=1;i_<n;i_++)
|
|
e1[i_]=e[i_+i1_];
|
|
}
|
|
//--- No eigen vectors
|
|
if(zneeded==0)
|
|
{
|
|
//--- get result
|
|
result=InternalBisectionEigenValues(d1,e1,n,2,1,a,b,0,0,-1,w,m,nsplit,iblock,isplit,errorcode);
|
|
//--- check
|
|
if(!result || m==0)
|
|
{
|
|
m=0;
|
|
return(result);
|
|
}
|
|
ArrayResizeAL(d,m);
|
|
i1_=1;
|
|
for(i_=0;i_<m;i_++)
|
|
{
|
|
d[i_]=w[i_+i1_];
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Eigen vectors are multiplied by Z
|
|
if(zneeded==1)
|
|
{
|
|
//--- Find eigen pairs
|
|
result=InternalBisectionEigenValues(d1,e1,n,2,2,a,b,0,0,-1,w,m,nsplit,iblock,isplit,errorcode);
|
|
//--- check
|
|
if(!result || m==0)
|
|
{
|
|
m=0;
|
|
return(result);
|
|
}
|
|
//--- function call
|
|
InternalDStein(n,d1,e1,m,w,iblock,isplit,z2,ifail,cr);
|
|
//--- check
|
|
if(cr!=0)
|
|
{
|
|
m=0;
|
|
//--- get result
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- Sort eigen values and vectors
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
k=i;
|
|
for(j=i;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(w[j]<w[k])
|
|
k=j;
|
|
}
|
|
//--- swap
|
|
v=w[i];
|
|
w[i]=w[k];
|
|
w[k]=v;
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- swap
|
|
v=z2[j][i];
|
|
z2[j].Set(i,z2[j][k]);
|
|
z2[j].Set(k,v);
|
|
}
|
|
}
|
|
//--- Transform Z2 and overwrite Z
|
|
z3.Resize(m+1,n+1);
|
|
for(i=1;i<=m;i++)
|
|
for(i_=1;i_<=n;i_++)
|
|
z3[i].Set(i_,z2[i_][i]);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=m;j++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
v+=z[i-1][i_]*z3[j][i_+i1_];
|
|
z2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- rewrite
|
|
z.Resize(n,m);
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[i_].Set(i-1,z2[i_+i1_][i]);
|
|
}
|
|
//--- Store W
|
|
ArrayResizeAL(d,m);
|
|
for(i=1;i<=m;i++)
|
|
d[i-1]=w[i];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Eigen vectors are stored in Z
|
|
if(zneeded==2)
|
|
{
|
|
//--- Find eigen pairs
|
|
result=InternalBisectionEigenValues(d1,e1,n,2,2,a,b,0,0,-1,w,m,nsplit,iblock,isplit,errorcode);
|
|
//--- check
|
|
if(!result || m==0)
|
|
{
|
|
m=0;
|
|
return(result);
|
|
}
|
|
//--- function call
|
|
InternalDStein(n,d1,e1,m,w,iblock,isplit,z2,ifail,cr);
|
|
//--- check
|
|
if(cr!=0)
|
|
{
|
|
m=0;
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- Sort eigen values and vectors
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
k=i;
|
|
for(j=i;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(w[j]<w[k])
|
|
k=j;
|
|
}
|
|
//--- swap
|
|
v=w[i];
|
|
w[i]=w[k];
|
|
w[k]=v;
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- swap
|
|
v=z2[j][i];
|
|
z2[j].Set(i,z2[j][k]);
|
|
z2[j].Set(k,v);
|
|
}
|
|
}
|
|
//--- Store W
|
|
ArrayResizeAL(d,m);
|
|
for(i=1;i<=m;i++)
|
|
d[i-1]=w[i];
|
|
z.Resize(n,m);
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[i_].Set(i-1,z2[i_+i1_][i]);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for finding tridiagonal matrix eigenvalues/vectors |
|
|
//| with given indexes (in ascending order) by using the bisection |
|
|
//| and inverse iteraion. |
|
|
//| Input parameters: |
|
|
//| D - the main diagonal of a tridiagonal matrix. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| E - the secondary diagonal of a tridiagonal matrix. |
|
|
//| Array whose index ranges within [0..N-2]. |
|
|
//| N - size of matrix. N>=0. |
|
|
//| ZNeeded - flag controlling whether the eigenvectors are |
|
|
//| needed or not. If ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not needed; |
|
|
//| * 1, the eigenvectors of a tridiagonal matrix |
|
|
//| are multiplied by the square matrix Z. It is |
|
|
//| used if the tridiagonal matrix is obtained by |
|
|
//| the similarity transformation of a symmetric |
|
|
//| matrix. |
|
|
//| * 2, the eigenvectors of a tridiagonal matrix |
|
|
//| replace matrix Z. |
|
|
//| I1, I2 - index interval for searching (from I1 to I2). |
|
|
//| 0 <= I1 <= I2 <= N-1. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z isn't used and remains unchanged; |
|
|
//| * 1, Z contains the square matrix (array whose |
|
|
//| indexes range within [0..N-1, 0..N-1]) which |
|
|
//| reduces the given symmetric matrix to |
|
|
//| tridiagonal form; |
|
|
//| * 2, Z isn't used (but changed on the exit). |
|
|
//| Output parameters: |
|
|
//| D - array of the eigenvalues found. |
|
|
//| Array whose index ranges within [0..I2-I1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, doesn't contain any information; |
|
|
//| * 1, contains the product of a given NxN matrix |
|
|
//| Z (from the left) and Nx(I2-I1) matrix of the |
|
|
//| eigenvectors found (from the right). Array |
|
|
//| whose indexes range within [0..N-1, 0..I2-I1].|
|
|
//| * 2, contains the matrix of the eigenvalues |
|
|
//| found. Array whose indexes range within |
|
|
//| [0..N-1, 0..I2-I1]. |
|
|
//| Result: |
|
|
//| True, if successful. In that case, D contains the |
|
|
//| eigenvalues, Z contains the eigenvectors (if needed). |
|
|
//| It should be noted that the subroutine changes the size of |
|
|
//| arrays D and Z. |
|
|
//| False, if the bisection method subroutine wasn't able to find|
|
|
//| the eigenvalues in the given interval or if the inverse |
|
|
//| iteration subroutine wasn't able to find all the |
|
|
//| corresponding eigenvectors. In that case, the eigenvalues and|
|
|
//| eigenvectors are not returned. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::SMatrixTdEVDI(double &d[],double &e[],const int n,
|
|
const int zneeded,const int i1,
|
|
const int i2,CMatrixDouble &z)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int errorcode=0;
|
|
int nsplit=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int cr=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
int iblock[];
|
|
int isplit[];
|
|
int ifail[];
|
|
double w[];
|
|
double d1[];
|
|
double e1[];
|
|
//--- create matrix
|
|
CMatrixDouble z2;
|
|
CMatrixDouble z3;
|
|
//--- check
|
|
if(!CAp::Assert((0<=i1 && i1<=i2) && i2<n,__FUNCTION__+": incorrect I1/I2!"))
|
|
return(false);
|
|
//--- Copy D,E to D1, E1
|
|
ArrayResizeAL(d1,n+1);
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
d1[i_]=d[i_+i1_];
|
|
//--- check
|
|
if(n>1)
|
|
{
|
|
ArrayResizeAL(e1,n);
|
|
i1_=-1;
|
|
for(i_=1;i_<n;i_++)
|
|
e1[i_]=e[i_+i1_];
|
|
}
|
|
//--- No eigen vectors
|
|
if(zneeded==0)
|
|
{
|
|
result=InternalBisectionEigenValues(d1,e1,n,3,1,0,0,i1+1,i2+1,-1,w,m,nsplit,iblock,isplit,errorcode);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
result=false;
|
|
return(result);
|
|
}
|
|
ArrayResizeAL(d,m);
|
|
for(i=1;i<=m;i++)
|
|
d[i-1]=w[i];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Eigen vectors are multiplied by Z
|
|
if(zneeded==1)
|
|
{
|
|
//--- Find eigen pairs
|
|
result=InternalBisectionEigenValues(d1,e1,n,3,2,0,0,i1+1,i2+1,-1,w,m,nsplit,iblock,isplit,errorcode);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- function call
|
|
InternalDStein(n,d1,e1,m,w,iblock,isplit,z2,ifail,cr);
|
|
//--- check
|
|
if(cr!=0)
|
|
{
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- Sort eigen values and vectors
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
k=i;
|
|
for(j=i;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(w[j]<w[k])
|
|
k=j;
|
|
}
|
|
//--- swap
|
|
v=w[i];
|
|
w[i]=w[k];
|
|
w[k]=v;
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- swap
|
|
v=z2[j][i];
|
|
z2[j].Set(i,z2[j][k]);
|
|
z2[j].Set(k,v);
|
|
}
|
|
}
|
|
//--- Transform Z2 and overwrite Z
|
|
z3.Resize(m+1,n+1);
|
|
for(i=1;i<=m;i++)
|
|
for(i_=1;i_<=n;i_++)
|
|
z3[i].Set(i_,z2[i_][i]);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=m;j++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
v+=z[i-1][i_]*z3[j][i_+i1_];
|
|
z2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- rewrite z
|
|
z.Resize(n,m);
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[i_].Set(i-1,z2[i_+i1_][i]);
|
|
}
|
|
//--- Store W
|
|
ArrayResizeAL(d,m);
|
|
for(i=1;i<=m;i++)
|
|
d[i-1]=w[i];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Eigen vectors are stored in Z
|
|
if(zneeded==2)
|
|
{
|
|
//--- Find eigen pairs
|
|
result=InternalBisectionEigenValues(d1,e1,n,3,2,0,0,i1+1,i2+1,-1,w,m,nsplit,iblock,isplit,errorcode);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- function call
|
|
InternalDStein(n,d1,e1,m,w,iblock,isplit,z2,ifail,cr);
|
|
//--- check
|
|
if(cr!=0)
|
|
{
|
|
result=false;
|
|
return(result);
|
|
}
|
|
//--- Sort eigen values and vectors
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
k=i;
|
|
for(j=i;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(w[j]<w[k])
|
|
k=j;
|
|
}
|
|
//--- swap
|
|
v=w[i];
|
|
w[i]=w[k];
|
|
w[k]=v;
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- swap
|
|
v=z2[j][i];
|
|
z2[j].Set(i,z2[j][k]);
|
|
z2[j].Set(k,v);
|
|
}
|
|
}
|
|
//--- Store Z
|
|
z.Resize(n,m);
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
z[i_].Set(i-1,z2[i_+i1_][i]);
|
|
}
|
|
//--- Store W
|
|
ArrayResizeAL(d,m);
|
|
for(i=1;i<=m;i++)
|
|
d[i-1]=w[i];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Finding eigenvalues and eigenvectors of a general matrix |
|
|
//| The algorithm finds eigenvalues and eigenvectors of a general |
|
|
//| matrix by using the QR algorithm with multiple shifts. The |
|
|
//| algorithm can find eigenvalues and both left and right |
|
|
//| eigenvectors. |
|
|
//| The right eigenvector is a vector x such that A*x = w*x, and the |
|
|
//| left eigenvector is a vector y such that y'*A = w*y' (here y' |
|
|
//| implies a complex conjugate transposition of vector y). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| VNeeded - flag controlling whether eigenvectors are needed |
|
|
//| or not. If VNeeded is equal to: |
|
|
//| * 0, eigenvectors are not returned; |
|
|
//| * 1, right eigenvectors are returned; |
|
|
//| * 2, left eigenvectors are returned; |
|
|
//| * 3, both left and right eigenvectors are |
|
|
//| returned. |
|
|
//| Output parameters: |
|
|
//| WR - real parts of eigenvalues. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| WR - imaginary parts of eigenvalues. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| VL, VR - arrays of left and right eigenvectors (if they |
|
|
//| are needed). If WI[i]=0, the respective |
|
|
//| eigenvalue is a real number, and it corresponds |
|
|
//| to the column number I of matrices VL/VR. If |
|
|
//| WI[i]>0, we have a pair of complex conjugate |
|
|
//| numbers with positive and negative imaginary |
|
|
//| parts: the first eigenvalue WR[i] + |
|
|
//| + sqrt(-1)*WI[i]; the second eigenvalue |
|
|
//| WR[i+1] + sqrt(-1)*WI[i+1]; |
|
|
//| WI[i]>0 |
|
|
//| WI[i+1] = -WI[i] < 0 |
|
|
//| In that case, the eigenvector corresponding to |
|
|
//| the first eigenvalue is located in i and i+1 |
|
|
//| columns of matrices VL/VR (the column number i |
|
|
//| contains the real part, and the column number |
|
|
//| i+1 contains the imaginary part), and the vector |
|
|
//| corresponding to the second eigenvalue is a |
|
|
//| complex conjugate to the first vector. |
|
|
//| Arrays whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| Result: |
|
|
//| True, if the algorithm has converged. |
|
|
//| False, if the algorithm has not converged. |
|
|
//| Note 1: |
|
|
//| Some users may ask the following question: what if WI[N-1]>0?|
|
|
//| WI[N] must contain an eigenvalue which is complex conjugate |
|
|
//| to the N-th eigenvalue, but the array has only size N? |
|
|
//| The answer is as follows: such a situation cannot occur |
|
|
//| because the algorithm finds a pairs of eigenvalues, |
|
|
//| therefore, if WI[i]>0, I is strictly less than N-1. |
|
|
//| Note 2: |
|
|
//| The algorithm performance depends on the value of the |
|
|
//| internal parameter NS of the InternalSchurDecomposition |
|
|
//| subroutine which defines the number of shifts in the QR |
|
|
//| algorithm (similarly to the block width in block-matrix |
|
|
//| algorithms of linear algebra). If you require maximum |
|
|
//| performance on your machine, it is recommended to adjust |
|
|
//| this parameter manually. |
|
|
//| See also the InternalTREVC subroutine. |
|
|
//| The algorithm is based on the LAPACK 3.0 library. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::RMatrixEVD(CMatrixDouble &ca,const int n,const int vneeded,
|
|
double &wr[],double &wi[],
|
|
CMatrixDouble &vl,CMatrixDouble &vr)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
bool result;
|
|
//--- create arrays
|
|
double wr1[];
|
|
double wi1[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble vl1;
|
|
CMatrixDouble vr1;
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(vneeded>=0 && vneeded<=3,__FUNCTION__+": incorrect VNeeded!"))
|
|
return(false);
|
|
a1.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
a1[i].Set(i_,a[i-1][i_+i1_]);
|
|
}
|
|
//--- get result
|
|
result=NonSymmetricEVD(a1,n,vneeded,wr1,wi1,vl1,vr1);
|
|
//--- check
|
|
if(result)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(wr,n);
|
|
ArrayResizeAL(wi,n);
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
wr[i_]=wr1[i_+i1_];
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
wi[i_]=wi1[i_+i1_];
|
|
//--- check
|
|
if(vneeded==2 || vneeded==3)
|
|
{
|
|
vl.Resize(n,n);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
vl[i].Set(i_,vl1[i+1][i_+i1_]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(vneeded==1 || vneeded==3)
|
|
{
|
|
vr.Resize(n,n);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
vr[i].Set(i_,vr1[i+1][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Eigenvalues and eigenvectors in tridiagonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::TtidiagonalEVD(double &d[],double &ce[],const int n,
|
|
const int zneeded,CMatrixDouble &z)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int maxit=0;
|
|
int i=0;
|
|
int ii=0;
|
|
int iscale=0;
|
|
int j=0;
|
|
int jtot=0;
|
|
int k=0;
|
|
int t=0;
|
|
int l=0;
|
|
int l1=0;
|
|
int lend=0;
|
|
int lendm1=0;
|
|
int lendp1=0;
|
|
int lendsv=0;
|
|
int lm1=0;
|
|
int lsv=0;
|
|
int m=0;
|
|
int mm=0;
|
|
int mm1=0;
|
|
int nm1=0;
|
|
int nmaxit=0;
|
|
int tmpint=0;
|
|
double anorm=0;
|
|
double b=0;
|
|
double c=0;
|
|
double eps=0;
|
|
double eps2=0;
|
|
double f=0;
|
|
double g=0;
|
|
double p=0;
|
|
double r=0;
|
|
double rt1=0;
|
|
double rt2=0;
|
|
double s=0;
|
|
double safmax=0;
|
|
double safmin=0;
|
|
double ssfmax=0;
|
|
double ssfmin=0;
|
|
double tst=0;
|
|
double tmp=0;
|
|
bool gotoflag;
|
|
int zrows=0;
|
|
bool wastranspose;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double work1[];
|
|
double work2[];
|
|
double workc[];
|
|
double works[];
|
|
double wtemp[];
|
|
//--- create copy
|
|
double e[];
|
|
ArrayResizeAL(e,ArraySize(ce));
|
|
ArrayCopy(e,ce);
|
|
//--- check
|
|
if(!CAp::Assert(zneeded>=0 && zneeded<=3,__FUNCTION__+": Incorrent ZNeeded"))
|
|
return(false);
|
|
//--- check
|
|
if(zneeded<0 || zneeded>3)
|
|
return(false);
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(n==0)
|
|
return(result);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
//--- check
|
|
if(zneeded==2 || zneeded==3)
|
|
{
|
|
z.Resize(2,2);
|
|
z[1].Set(1,1);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- initialization
|
|
maxit=30;
|
|
//--- allocation
|
|
ArrayResizeAL(wtemp,n+1);
|
|
ArrayResizeAL(work1,n);
|
|
ArrayResizeAL(work2,n);
|
|
ArrayResizeAL(workc,n+1);
|
|
ArrayResizeAL(works,n+1);
|
|
//--- Determine the unit roundoff and over/underflow thresholds.
|
|
eps=CMath::m_machineepsilon;
|
|
eps2=CMath::Sqr(eps);
|
|
safmin=CMath::m_minrealnumber;
|
|
safmax=CMath::m_maxrealnumber;
|
|
ssfmax=MathSqrt(safmax)/3;
|
|
ssfmin=MathSqrt(safmin)/eps2;
|
|
//--- Here we are using transposition to get rid of column operations
|
|
wastranspose=false;
|
|
zrows=0;
|
|
//--- check
|
|
if(zneeded==1)
|
|
zrows=n;
|
|
//--- check
|
|
if(zneeded==2)
|
|
zrows=n;
|
|
//--- check
|
|
if(zneeded==3)
|
|
zrows=1;
|
|
//--- check
|
|
if(zneeded==1)
|
|
{
|
|
wastranspose=true;
|
|
//--- function call
|
|
CBlas::InplaceTranspose(z,1,n,1,n,wtemp);
|
|
}
|
|
//--- check
|
|
if(zneeded==2)
|
|
{
|
|
wastranspose=true;
|
|
z.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
z[i].Set(j,1);
|
|
else
|
|
z[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(zneeded==3)
|
|
{
|
|
wastranspose=false;
|
|
z.Resize(2,n+1);
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(j==1)
|
|
z[1].Set(j,1);
|
|
else
|
|
z[1].Set(j,0);
|
|
}
|
|
}
|
|
//--- initialization
|
|
nmaxit=n*maxit;
|
|
jtot=0;
|
|
//--- Determine where the matrix splits and choose QL or QR iteration
|
|
//--- for each block, according to whether top or bottom diagonal
|
|
//--- element is smaller.
|
|
l1=1;
|
|
nm1=n-1;
|
|
while(true)
|
|
{
|
|
//--- check
|
|
if(l1>n)
|
|
break;
|
|
//--- check
|
|
if(l1>1)
|
|
e[l1-1]=0;
|
|
gotoflag=false;
|
|
m=l1;
|
|
//--- check
|
|
if(l1<=nm1)
|
|
{
|
|
for(m=l1;m<=nm1;m++)
|
|
{
|
|
tst=MathAbs(e[m]);
|
|
//--- check
|
|
if(tst==0.0)
|
|
{
|
|
gotoflag=true;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
//--- check
|
|
if(tst<=MathSqrt(MathAbs(d[m]))*MathSqrt(MathAbs(d[m+1]))*eps)
|
|
{
|
|
e[m]=0;
|
|
gotoflag=true;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(!gotoflag)
|
|
m=n;
|
|
//--- change values
|
|
l=l1;
|
|
lsv=l;
|
|
lend=m;
|
|
lendsv=lend;
|
|
l1=m+1;
|
|
//--- check
|
|
if(lend==l)
|
|
continue;
|
|
//--- Scale submatrix in rows and columns L to LEND
|
|
if(l==lend)
|
|
anorm=MathAbs(d[l]);
|
|
else
|
|
{
|
|
anorm=MathMax(MathAbs(d[l])+MathAbs(e[l]),MathAbs(e[lend-1])+MathAbs(d[lend]));
|
|
for(i=l+1;i<=lend-1;i++)
|
|
anorm=MathMax(anorm,MathAbs(d[i])+MathAbs(e[i])+MathAbs(e[i-1]));
|
|
}
|
|
iscale=0;
|
|
//--- check
|
|
if(anorm==0.0)
|
|
continue;
|
|
//--- check
|
|
if(anorm>(double)(ssfmax))
|
|
{
|
|
iscale=1;
|
|
tmp=ssfmax/anorm;
|
|
tmpint=lend-1;
|
|
for(i_=l;i_<=lend;i_++)
|
|
d[i_]=tmp*d[i_];
|
|
for(i_=l;i_<=tmpint;i_++)
|
|
e[i_]=tmp*e[i_];
|
|
}
|
|
//--- check
|
|
if(anorm<ssfmin)
|
|
{
|
|
iscale=2;
|
|
tmp=ssfmin/anorm;
|
|
tmpint=lend-1;
|
|
for(i_=l;i_<=lend;i_++)
|
|
d[i_]=tmp*d[i_];
|
|
for(i_=l;i_<=tmpint;i_++)
|
|
e[i_]=tmp*e[i_];
|
|
}
|
|
//--- Choose between QL and QR iteration
|
|
if(MathAbs(d[lend])<MathAbs(d[l]))
|
|
{
|
|
lend=lsv;
|
|
l=lendsv;
|
|
}
|
|
//--- check
|
|
if(lend>l)
|
|
{
|
|
//--- QL Iteration
|
|
//--- Look for small subdiagonal element.
|
|
while(true)
|
|
{
|
|
gotoflag=false;
|
|
//--- check
|
|
if(l!=lend)
|
|
{
|
|
lendm1=lend-1;
|
|
for(m=l;m<=lendm1;m++)
|
|
{
|
|
tst=CMath::Sqr(MathAbs(e[m]));
|
|
//--- check
|
|
if(tst<=eps2*MathAbs(d[m])*MathAbs(d[m+1])+safmin)
|
|
{
|
|
gotoflag=true;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(!gotoflag)
|
|
m=lend;
|
|
//--- check
|
|
if(m<lend)
|
|
e[m]=0;
|
|
p=d[l];
|
|
//--- check
|
|
if(m!=l)
|
|
{
|
|
//--- If remaining matrix is 2-by-2, use DLAE2 or SLAEV2
|
|
//--- to compute its eigensystem.
|
|
if(m==l+1)
|
|
{
|
|
//--- check
|
|
if(zneeded>0)
|
|
{
|
|
//--- function call
|
|
TdEVDEv2(d[l],e[l],d[l+1],rt1,rt2,c,s);
|
|
//--- change values
|
|
work1[l]=c;
|
|
work2[l]=s;
|
|
workc[1]=work1[l];
|
|
works[1]=work2[l];
|
|
//--- check
|
|
if(!wastranspose)
|
|
CRotations::ApplyRotationsFromTheRight(false,1,zrows,l,l+1,workc,works,z,wtemp);
|
|
else
|
|
CRotations::ApplyRotationsFromTheLeft(false,l,l+1,1,zrows,workc,works,z,wtemp);
|
|
}
|
|
else
|
|
//--- function call
|
|
TdEVDE2(d[l],e[l],d[l+1],rt1,rt2);
|
|
//--- change values
|
|
d[l]=rt1;
|
|
d[l+1]=rt2;
|
|
e[l]=0;
|
|
l=l+2;
|
|
//--- check
|
|
if(l<=lend)
|
|
continue;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
//--- check
|
|
if(jtot==nmaxit)
|
|
break;
|
|
jtot=jtot+1;
|
|
//--- Form shift.
|
|
g=(d[l+1]-p)/(2*e[l]);
|
|
//--- function call
|
|
r=TdEVDPythag(g,1);
|
|
g=d[m]-p+e[l]/(g+TdEVDExtSign(r,g));
|
|
s=1;
|
|
c=1;
|
|
p=0;
|
|
//--- Inner loop
|
|
mm1=m-1;
|
|
for(i=mm1;i>=l;i--)
|
|
{
|
|
f=s*e[i];
|
|
b=c*e[i];
|
|
//--- function call
|
|
CRotations::GenerateRotation(g,f,c,s,r);
|
|
//--- check
|
|
if(i!=m-1)
|
|
e[i+1]=r;
|
|
g=d[i+1]-p;
|
|
r=(d[i]-g)*s+2*c*b;
|
|
p=s*r;
|
|
d[i+1]=g+p;
|
|
g=c*r-b;
|
|
//--- If eigenvectors are desired, then save rotations.
|
|
if(zneeded>0)
|
|
{
|
|
work1[i]=c;
|
|
work2[i]=-s;
|
|
}
|
|
}
|
|
//--- If eigenvectors are desired, then apply saved rotations.
|
|
if(zneeded>0)
|
|
{
|
|
for(i=l;i<m;i++)
|
|
{
|
|
workc[i-l+1]=work1[i];
|
|
works[i-l+1]=work2[i];
|
|
}
|
|
//--- check
|
|
if(!wastranspose)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(false,1,zrows,l,m,workc,works,z,wtemp);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(false,l,m,1,zrows,workc,works,z,wtemp);
|
|
}
|
|
}
|
|
d[l]=d[l]-p;
|
|
e[l]=g;
|
|
continue;
|
|
}
|
|
//--- Eigenvalue found.
|
|
d[l]=p;
|
|
l=l+1;
|
|
//--- check
|
|
if(l<=lend)
|
|
continue;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- QR Iteration
|
|
//--- Look for small superdiagonal element.
|
|
while(true)
|
|
{
|
|
gotoflag=false;
|
|
//--- check
|
|
if(l!=lend)
|
|
{
|
|
lendp1=lend+1;
|
|
for(m=l;m>=lendp1;m--)
|
|
{
|
|
tst=CMath::Sqr(MathAbs(e[m-1]));
|
|
//--- check
|
|
if(tst<=(double)(eps2*MathAbs(d[m])*MathAbs(d[m-1])+safmin))
|
|
{
|
|
gotoflag=true;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(!gotoflag)
|
|
m=lend;
|
|
//--- check
|
|
if(m>lend)
|
|
e[m-1]=0;
|
|
p=d[l];
|
|
//--- check
|
|
if(m!=l)
|
|
{
|
|
//--- If remaining matrix is 2-by-2, use DLAE2 or SLAEV2
|
|
//--- to compute its eigensystem.
|
|
if(m==l-1)
|
|
{
|
|
//--- check
|
|
if(zneeded>0)
|
|
{
|
|
//--- function call
|
|
TdEVDEv2(d[l-1],e[l-1],d[l],rt1,rt2,c,s);
|
|
work1[m]=c;
|
|
work2[m]=s;
|
|
workc[1]=c;
|
|
works[1]=s;
|
|
//--- check
|
|
if(!wastranspose)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(true,1,zrows,l-1,l,workc,works,z,wtemp);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(true,l-1,l,1,zrows,workc,works,z,wtemp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
TdEVDE2(d[l-1],e[l-1],d[l],rt1,rt2);
|
|
}
|
|
d[l-1]=rt1;
|
|
d[l]=rt2;
|
|
e[l-1]=0;
|
|
l=l-2;
|
|
//--- check
|
|
if(l>=lend)
|
|
continue;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
//--- check
|
|
if(jtot==nmaxit)
|
|
break;
|
|
jtot=jtot+1;
|
|
//--- Form shift.
|
|
g=(d[l-1]-p)/(2*e[l-1]);
|
|
//--- function call
|
|
r=TdEVDPythag(g,1);
|
|
g=d[m]-p+e[l-1]/(g+TdEVDExtSign(r,g));
|
|
s=1;
|
|
c=1;
|
|
p=0;
|
|
//--- Inner loop
|
|
lm1=l-1;
|
|
for(i=m;i<=lm1;i++)
|
|
{
|
|
f=s*e[i];
|
|
b=c*e[i];
|
|
//--- function call
|
|
CRotations::GenerateRotation(g,f,c,s,r);
|
|
//--- check
|
|
if(i!=m)
|
|
e[i-1]=r;
|
|
//--- change values
|
|
g=d[i]-p;
|
|
r=(d[i+1]-g)*s+2*c*b;
|
|
p=s*r;
|
|
d[i]=g+p;
|
|
g=c*r-b;
|
|
//--- If eigenvectors are desired, then save rotations.
|
|
if(zneeded>0)
|
|
{
|
|
work1[i]=c;
|
|
work2[i]=s;
|
|
}
|
|
}
|
|
//--- If eigenvectors are desired, then apply saved rotations.
|
|
if(zneeded>0)
|
|
{
|
|
mm=l-m+1;
|
|
for(i=m;i<=l-1;i++)
|
|
{
|
|
workc[i-m+1]=work1[i];
|
|
works[i-m+1]=work2[i];
|
|
}
|
|
//--- check
|
|
if(!wastranspose)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(true,1,zrows,m,l,workc,works,z,wtemp);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(true,m,l,1,zrows,workc,works,z,wtemp);
|
|
}
|
|
}
|
|
d[l]=d[l]-p;
|
|
e[lm1]=g;
|
|
continue;
|
|
}
|
|
//--- Eigenvalue found.
|
|
d[l]=p;
|
|
l=l-1;
|
|
//--- check
|
|
if(l>=lend)
|
|
continue;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
//--- Undo scaling if necessary
|
|
if(iscale==1)
|
|
{
|
|
tmp=anorm/ssfmax;
|
|
tmpint=lendsv-1;
|
|
for(i_=lsv;i_<=lendsv;i_++)
|
|
d[i_]=tmp*d[i_];
|
|
for(i_=lsv;i_<=tmpint;i_++)
|
|
e[i_]=tmp*e[i_];
|
|
}
|
|
//--- check
|
|
if(iscale==2)
|
|
{
|
|
tmp=anorm/ssfmin;
|
|
tmpint=lendsv-1;
|
|
for(i_=lsv;i_<=lendsv;i_++)
|
|
d[i_]=tmp*d[i_];
|
|
for(i_=lsv;i_<=tmpint;i_++)
|
|
e[i_]=tmp*e[i_];
|
|
}
|
|
//--- Check for no convergence to an eigenvalue after a total
|
|
//--- of N*MAXIT iterations.
|
|
if(jtot>=nmaxit)
|
|
{
|
|
result=false;
|
|
//--- check
|
|
if(wastranspose)
|
|
{
|
|
//--- function call
|
|
CBlas::InplaceTranspose(z,1,n,1,n,wtemp);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- Order eigenvalues and eigenvectors.
|
|
if(zneeded==0)
|
|
{
|
|
//--- Sort
|
|
if(n==1)
|
|
return(result);
|
|
//--- check
|
|
if(n==2)
|
|
{
|
|
//--- check
|
|
if(d[1]>d[2])
|
|
{
|
|
tmp=d[1];
|
|
d[1]=d[2];
|
|
d[2]=tmp;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
i=2;
|
|
do
|
|
{
|
|
t=i;
|
|
while(t!=1)
|
|
{
|
|
k=t/2;
|
|
//--- check
|
|
if(d[k]>=d[t])
|
|
t=1;
|
|
else
|
|
{
|
|
//--- change values
|
|
tmp=d[k];
|
|
d[k]=d[t];
|
|
d[t]=tmp;
|
|
t=k;
|
|
}
|
|
}
|
|
i=i+1;
|
|
}
|
|
//--- cycle
|
|
while(i<=n);
|
|
i=n-1;
|
|
do
|
|
{
|
|
tmp=d[i+1];
|
|
d[i+1]=d[1];
|
|
d[1]=tmp;
|
|
t=1;
|
|
while(t!=0)
|
|
{
|
|
k=2*t;
|
|
//--- check
|
|
if(k>i)
|
|
t=0;
|
|
else
|
|
{
|
|
//--- check
|
|
if(k<i)
|
|
{
|
|
//--- check
|
|
if(d[k+1]>d[k])
|
|
k=k+1;
|
|
}
|
|
//--- check
|
|
if(d[t]>=d[k])
|
|
t=0;
|
|
else
|
|
{
|
|
//--- change values
|
|
tmp=d[k];
|
|
d[k]=d[t];
|
|
d[t]=tmp;
|
|
t=k;
|
|
}
|
|
}
|
|
}
|
|
i=i-1;
|
|
}
|
|
while(i>=1);
|
|
}
|
|
else
|
|
{
|
|
//--- Use Selection Sort to minimize swaps of eigenvectors
|
|
for(ii=2;ii<=n;ii++)
|
|
{
|
|
i=ii-1;
|
|
k=i;
|
|
p=d[i];
|
|
for(j=ii;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(d[j]<p)
|
|
{
|
|
k=j;
|
|
p=d[j];
|
|
}
|
|
}
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
d[k]=d[i];
|
|
d[i]=p;
|
|
//--- check
|
|
if(wastranspose)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
wtemp[i_]=z[i][i_];
|
|
for(i_=1;i_<=n;i_++)
|
|
z[i].Set(i_,z[k][i_]);
|
|
for(i_=1;i_<=n;i_++)
|
|
z[k].Set(i_,wtemp[i_]);
|
|
}
|
|
else
|
|
{
|
|
for(i_=1;i_<=zrows;i_++)
|
|
wtemp[i_]=z[i_][i];
|
|
for(i_=1;i_<=zrows;i_++)
|
|
z[i_].Set(i,z[i_][k]);
|
|
for(i_=1;i_<=zrows;i_++)
|
|
z[i_].Set(k,wtemp[i_]);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(wastranspose)
|
|
{
|
|
//--- function call
|
|
CBlas::InplaceTranspose(z,1,n,1,n,wtemp);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| DLAE2 computes the eigenvalues of a 2-by-2 symmetric matrix |
|
|
//| [ A B ] |
|
|
//| [ B C ]. |
|
|
//| On return, RT1 is the eigenvalue of larger absolute value, and |
|
|
//| RT2 is the eigenvalue of smaller absolute value. |
|
|
//| -- LAPACK auxiliary routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::TdEVDE2(const double a,const double b,const double c,
|
|
double &rt1,double &rt2)
|
|
{
|
|
//--- create variables
|
|
double ab=0;
|
|
double acmn=0;
|
|
double acmx=0;
|
|
double adf=0;
|
|
double df=0;
|
|
double rt=0;
|
|
double sm=0;
|
|
double tb=0;
|
|
//--- initialization
|
|
rt1=0;
|
|
rt2=0;
|
|
sm=a+c;
|
|
df=a-c;
|
|
adf=MathAbs(df);
|
|
tb=b+b;
|
|
ab=MathAbs(tb);
|
|
//--- check
|
|
if(MathAbs(a)>MathAbs(c))
|
|
{
|
|
acmx=a;
|
|
acmn=c;
|
|
}
|
|
else
|
|
{
|
|
acmx=c;
|
|
acmn=a;
|
|
}
|
|
//--- check
|
|
if(adf>ab)
|
|
{
|
|
rt=adf*MathSqrt(1+CMath::Sqr(ab/adf));
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(adf<ab)
|
|
rt=ab*MathSqrt(1+CMath::Sqr(adf/ab));
|
|
else
|
|
{
|
|
//--- Includes case AB=ADF=0
|
|
rt=ab*MathSqrt(2);
|
|
}
|
|
}
|
|
//--- check
|
|
if(sm<0.0)
|
|
{
|
|
rt1=0.5*(sm-rt);
|
|
//--- Order of execution important.
|
|
//--- To get fully accurate smaller eigenvalue,
|
|
//--- next line needs to be executed in higher precision.
|
|
rt2=acmx/rt1*acmn-b/rt1*b;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(sm>0.0)
|
|
{
|
|
rt1=0.5*(sm+rt);
|
|
//--- Order of execution important.
|
|
//--- To get fully accurate smaller eigenvalue,
|
|
//--- next line needs to be executed in higher precision.
|
|
rt2=acmx/rt1*acmn-b/rt1*b;
|
|
}
|
|
else
|
|
{
|
|
//--- Includes case RT1 = RT2 = 0
|
|
rt1=0.5*rt;
|
|
rt2=-(0.5*rt);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| DLAEV2 computes the eigendecomposition of a 2-by-2 symmetric |
|
|
//| matrix |
|
|
//| [ A B ] |
|
|
//| [ B C ]. |
|
|
//| On return, RT1 is the eigenvalue of larger absolute value, RT2 is|
|
|
//| the eigenvalue of smaller absolute value, and (CS1,SN1) is the |
|
|
//| unit right eigenvector for RT1, giving the decomposition |
|
|
//| [ CS1 SN1 ] [ A B ] [ CS1 -SN1 ] = [ RT1 0 ] |
|
|
//| [-SN1 CS1 ] [ B C ] [ SN1 CS1 ] [ 0 RT2 ]. |
|
|
//| -- LAPACK auxiliary routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::TdEVDEv2(const double a,const double b,const double c,
|
|
double &rt1,double &rt2,double &cs1,double &sn1)
|
|
{
|
|
//--- create variables
|
|
int sgn1=0;
|
|
int sgn2=0;
|
|
double ab=0;
|
|
double acmn=0;
|
|
double acmx=0;
|
|
double acs=0;
|
|
double adf=0;
|
|
double cs=0;
|
|
double ct=0;
|
|
double df=0;
|
|
double rt=0;
|
|
double sm=0;
|
|
double tb=0;
|
|
double tn=0;
|
|
//--- initialization
|
|
rt1=0;
|
|
rt2=0;
|
|
cs1=0;
|
|
sn1=0;
|
|
//--- Compute the eigenvalues
|
|
sm=a+c;
|
|
df=a-c;
|
|
adf=MathAbs(df);
|
|
tb=b+b;
|
|
ab=MathAbs(tb);
|
|
//--- check
|
|
if(MathAbs(a)>MathAbs(c))
|
|
{
|
|
acmx=a;
|
|
acmn=c;
|
|
}
|
|
else
|
|
{
|
|
acmx=c;
|
|
acmn=a;
|
|
}
|
|
//--- check
|
|
if(adf>ab)
|
|
rt=adf*MathSqrt(1+CMath::Sqr(ab/adf));
|
|
else
|
|
{
|
|
//--- check
|
|
if(adf<ab)
|
|
rt=ab*MathSqrt(1+CMath::Sqr(adf/ab));
|
|
else
|
|
{
|
|
//--- Includes case AB=ADF=0
|
|
rt=ab*MathSqrt(2);
|
|
}
|
|
}
|
|
//--- check
|
|
if(sm<0.0)
|
|
{
|
|
rt1=0.5*(sm-rt);
|
|
sgn1=-1;
|
|
//--- Order of execution important.
|
|
//--- To get fully accurate smaller eigenvalue,
|
|
//--- next line needs to be executed in higher precision.
|
|
rt2=acmx/rt1*acmn-b/rt1*b;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(sm>0.0)
|
|
{
|
|
rt1=0.5*(sm+rt);
|
|
sgn1=1;
|
|
//--- Order of execution important.
|
|
//--- To get fully accurate smaller eigenvalue,
|
|
//--- next line needs to be executed in higher precision.
|
|
rt2=acmx/rt1*acmn-b/rt1*b;
|
|
}
|
|
else
|
|
{
|
|
//--- Includes case RT1 = RT2 = 0
|
|
rt1=0.5*rt;
|
|
rt2=-(0.5*rt);
|
|
sgn1=1;
|
|
}
|
|
}
|
|
//--- Compute the eigenvector
|
|
if(df>=0.0)
|
|
{
|
|
cs=df+rt;
|
|
sgn2=1;
|
|
}
|
|
else
|
|
{
|
|
cs=df-rt;
|
|
sgn2=-1;
|
|
}
|
|
acs=MathAbs(cs);
|
|
//--- check
|
|
if(acs>ab)
|
|
{
|
|
ct=-(tb/cs);
|
|
sn1=1/MathSqrt(1+ct*ct);
|
|
cs1=ct*sn1;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(ab==0.0)
|
|
{
|
|
cs1=1;
|
|
sn1=0;
|
|
}
|
|
else
|
|
{
|
|
tn=-(cs/tb);
|
|
cs1=1/MathSqrt(1+tn*tn);
|
|
sn1=tn*cs1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(sgn1==sgn2)
|
|
{
|
|
tn=cs1;
|
|
cs1=-sn1;
|
|
sn1=tn;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal routine |
|
|
//+------------------------------------------------------------------+
|
|
static double CEigenVDetect::TdEVDPythag(const double a,const double b)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
//--- check
|
|
if(MathAbs(a)<MathAbs(b))
|
|
result=MathAbs(b)*MathSqrt(1+CMath::Sqr(a/b));
|
|
else
|
|
result=MathAbs(a)*MathSqrt(1+CMath::Sqr(b/a));
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal routine |
|
|
//+------------------------------------------------------------------+
|
|
static double CEigenVDetect::TdEVDExtSign(const double a,const double b)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
//--- check
|
|
if(b>=0.0)
|
|
result=MathAbs(a);
|
|
else
|
|
result=-MathAbs(a);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::InternalBisectionEigenValues(double &cd[],double &ce[],
|
|
const int n,int irange,
|
|
const int iorder,const double vl,
|
|
const double vu,const int il,
|
|
const int iu,const double abstol,
|
|
double &w[],int &m,
|
|
int &nsplit,int &iblock[],
|
|
int &isplit[],int &errorcode)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
double fudge=0;
|
|
double relfac=0;
|
|
bool ncnvrg;
|
|
bool toofew;
|
|
int ib=0;
|
|
int ibegin=0;
|
|
int idiscl=0;
|
|
int idiscu=0;
|
|
int ie=0;
|
|
int iend=0;
|
|
int iinfo=0;
|
|
int im=0;
|
|
int iin=0;
|
|
int ioff=0;
|
|
int iout=0;
|
|
int itmax=0;
|
|
int iw=0;
|
|
int iwoff=0;
|
|
int j=0;
|
|
int itmp1=0;
|
|
int jb=0;
|
|
int jdisc=0;
|
|
int je=0;
|
|
int nwl=0;
|
|
int nwu=0;
|
|
int tmpi=0;
|
|
double atoli=0;
|
|
double bnorm=0;
|
|
double gl=0;
|
|
double gu=0;
|
|
double pivmin=0;
|
|
double rtoli=0;
|
|
double safemn=0;
|
|
double tmp1=0;
|
|
double tmp2=0;
|
|
double tnorm=0;
|
|
double ulp=0;
|
|
double wkill=0;
|
|
double wl=0;
|
|
double wlu=0;
|
|
double wu=0;
|
|
double wul=0;
|
|
double scalefactor=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
int idumma[];
|
|
double work[];
|
|
int iwork[];
|
|
int ia1s2[];
|
|
double ra1s2[];
|
|
double ra1siin[];
|
|
double ra2siin[];
|
|
double ra3siin[];
|
|
double ra4siin[];
|
|
int iworkspace[];
|
|
double rworkspace[];
|
|
//--- create matrix
|
|
CMatrixDouble ra1s2x2;
|
|
CMatrixInt ia1s2x2;
|
|
CMatrixDouble ra1siinx2;
|
|
CMatrixInt ia1siinx2;
|
|
//--- create copy
|
|
double d[];
|
|
ArrayResizeAL(d,ArraySize(cd));
|
|
ArrayCopy(d,cd);
|
|
//--- create copy
|
|
double e[];
|
|
ArrayResizeAL(e,ArraySize(ce));
|
|
ArrayCopy(e,ce);
|
|
//--- initialization
|
|
m=0;
|
|
nsplit=0;
|
|
errorcode=0;
|
|
//--- check
|
|
if(n==0)
|
|
return(true);
|
|
//--- Get machine constants
|
|
//--- NB is the minimum vector length for vector bisection, or 0
|
|
//--- if only scalar is to be done.
|
|
fudge=2;
|
|
relfac=2;
|
|
safemn=CMath::m_minrealnumber;
|
|
ulp=2*CMath::m_machineepsilon;
|
|
rtoli=ulp*relfac;
|
|
//--- allocation
|
|
ArrayResizeAL(idumma,2);
|
|
ArrayResizeAL(work,4*n+1);
|
|
ArrayResizeAL(iwork,3*n+1);
|
|
ArrayResizeAL(w,n+1);
|
|
ArrayResizeAL(iblock,n+1);
|
|
ArrayResizeAL(isplit,n+1);
|
|
ArrayResizeAL(ia1s2,3);
|
|
ArrayResizeAL(ra1s2,3);
|
|
ArrayResizeAL(ra1siin,n+1);
|
|
ArrayResizeAL(ra2siin,n+1);
|
|
ArrayResizeAL(ra3siin,n+1);
|
|
ArrayResizeAL(ra4siin,n+1);
|
|
ArrayResizeAL(iworkspace,n+1);
|
|
ArrayResizeAL(rworkspace,n+1);
|
|
ra1siinx2.Resize(n+1,3);
|
|
ia1siinx2.Resize(n+1,3);
|
|
ra1s2x2.Resize(3,3);
|
|
ia1s2x2.Resize(3,3);
|
|
//--- initialization
|
|
wlu=0;
|
|
wul=0;
|
|
//--- Check for Errors
|
|
result=false;
|
|
errorcode=0;
|
|
//--- check
|
|
if(irange<=0 || irange>=4)
|
|
errorcode=-4;
|
|
//--- check
|
|
if(iorder<=0 || iorder>=3)
|
|
errorcode=-5;
|
|
//--- check
|
|
if(n<0)
|
|
errorcode=-3;
|
|
//--- check
|
|
if(irange==2 && vl>=vu)
|
|
errorcode=-6;
|
|
//--- check
|
|
if(irange==3 && (il<1 || il>MathMax(1,n)))
|
|
errorcode=-8;
|
|
//--- check
|
|
if(irange==3 && (iu<MathMin(n,il) || iu>n))
|
|
errorcode=-9;
|
|
//--- check
|
|
if(errorcode!=0)
|
|
return(result);
|
|
//--- Initialize error flags
|
|
ncnvrg=false;
|
|
toofew=false;
|
|
//--- Simplifications:
|
|
if(irange==3 && il==1 && iu==n)
|
|
irange=1;
|
|
//--- Special Case when N=1
|
|
if(n==1)
|
|
{
|
|
nsplit=1;
|
|
isplit[1]=1;
|
|
//--- check
|
|
if((irange==2 && vl>=d[1]) || vu<d[1])
|
|
m=0;
|
|
else
|
|
{
|
|
w[1]=d[1];
|
|
iblock[1]=1;
|
|
m=1;
|
|
}
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- Scaling
|
|
t=MathAbs(d[n]);
|
|
for(j=1;j<n;j++)
|
|
{
|
|
t=MathMax(t,MathAbs(d[j]));
|
|
t=MathMax(t,MathAbs(e[j]));
|
|
}
|
|
scalefactor=1;
|
|
//--- check
|
|
if(t!=0.0)
|
|
{
|
|
//--- check
|
|
if(t>MathSqrt(MathSqrt(CMath::m_minrealnumber))*MathSqrt(CMath::m_maxrealnumber))
|
|
scalefactor=t;
|
|
//--- check
|
|
if(t<MathSqrt(MathSqrt(CMath::m_maxrealnumber))*MathSqrt(CMath::m_minrealnumber))
|
|
scalefactor=t;
|
|
for(j=1;j<n;j++)
|
|
{
|
|
d[j]=d[j]/scalefactor;
|
|
e[j]=e[j]/scalefactor;
|
|
}
|
|
d[n]=d[n]/scalefactor;
|
|
}
|
|
//--- Compute Splitting Points
|
|
nsplit=1;
|
|
work[n]=0;
|
|
pivmin=1;
|
|
for(j=2;j<=n;j++)
|
|
{
|
|
tmp1=CMath::Sqr(e[j-1]);
|
|
//--- check
|
|
if(MathAbs(d[j]*d[j-1])*CMath::Sqr(ulp)+safemn>tmp1)
|
|
{
|
|
isplit[nsplit]=j-1;
|
|
nsplit=nsplit+1;
|
|
work[j-1]=0;
|
|
}
|
|
else
|
|
{
|
|
work[j-1]=tmp1;
|
|
pivmin=MathMax(pivmin,tmp1);
|
|
}
|
|
}
|
|
isplit[nsplit]=n;
|
|
pivmin=pivmin*safemn;
|
|
//--- Compute Interval and ATOLI
|
|
if(irange==3)
|
|
{
|
|
//--- RANGE='I': Compute the interval containing eigenvalues
|
|
//--- IL through IU.
|
|
//--- Compute Gershgorin interval for entire (split) matrix
|
|
//--- and use it as the initial interval
|
|
gu=d[1];
|
|
gl=d[1];
|
|
tmp1=0;
|
|
for(j=1;j<n;j++)
|
|
{
|
|
//--- change values
|
|
tmp2=MathSqrt(work[j]);
|
|
gu=MathMax(gu,d[j]+tmp1+tmp2);
|
|
gl=MathMin(gl,d[j]-tmp1-tmp2);
|
|
tmp1=tmp2;
|
|
}
|
|
//--- change values
|
|
gu=MathMax(gu,d[n]+tmp1);
|
|
gl=MathMin(gl,d[n]-tmp1);
|
|
tnorm=MathMax(MathAbs(gl),MathAbs(gu));
|
|
gl=gl-fudge*tnorm*ulp*n-fudge*2*pivmin;
|
|
gu=gu+fudge*tnorm*ulp*n+fudge*pivmin;
|
|
//--- Compute Iteration parameters
|
|
itmax=(int)MathCeil((MathLog(tnorm+pivmin)-MathLog(pivmin))/MathLog(2))+2;
|
|
//--- check
|
|
if(abstol<=0.0)
|
|
atoli=ulp*tnorm;
|
|
else
|
|
atoli=abstol;
|
|
//--- change values
|
|
work[n+1]=gl;
|
|
work[n+2]=gl;
|
|
work[n+3]=gu;
|
|
work[n+4]=gu;
|
|
work[n+5]=gl;
|
|
work[n+6]=gu;
|
|
iwork[1]=-1;
|
|
iwork[2]=-1;
|
|
iwork[3]=n+1;
|
|
iwork[4]=n+1;
|
|
iwork[5]=il-1;
|
|
iwork[6]=iu;
|
|
//--- Calling DLAEBZ
|
|
//--- DLAEBZ( 3, ITMAX, N, 2, 2, NB, ATOLI, RTOLI, PIVMIN, D, E,
|
|
//--- WORK, IWORK( 5 ), WORK( N+1 ), WORK( N+5 ), IOUT,
|
|
//--- IWORK, W, IBLOCK, IINFO )
|
|
ia1s2[1]=iwork[5];
|
|
ia1s2[2]=iwork[6];
|
|
ra1s2[1]=work[n+5];
|
|
ra1s2[2]=work[n+6];
|
|
ra1s2x2[1].Set(1,work[n+1]);
|
|
ra1s2x2[2].Set(1,work[n+2]);
|
|
ra1s2x2[1].Set(2,work[n+3]);
|
|
ra1s2x2[2].Set(2,work[n+4]);
|
|
ia1s2x2[1].Set(1,iwork[1]);
|
|
ia1s2x2[2].Set(1,iwork[2]);
|
|
ia1s2x2[1].Set(2,iwork[3]);
|
|
ia1s2x2[2].Set(2,iwork[4]);
|
|
//--- function call
|
|
InternalDLAEBZ(3,itmax,n,2,2,atoli,rtoli,pivmin,d,e,work,ia1s2,ra1s2x2,ra1s2,iout,ia1s2x2,w,iblock,iinfo);
|
|
iwork[5]=ia1s2[1];
|
|
iwork[6]=ia1s2[2];
|
|
work[n+5]=ra1s2[1];
|
|
work[n+6]=ra1s2[2];
|
|
work[n+1]=ra1s2x2[1][1];
|
|
work[n+2]=ra1s2x2[2][1];
|
|
work[n+3]=ra1s2x2[1][2];
|
|
work[n+4]=ra1s2x2[2][2];
|
|
iwork[1]=ia1s2x2[1][1];
|
|
iwork[2]=ia1s2x2[2][1];
|
|
iwork[3]=ia1s2x2[1][2];
|
|
iwork[4]=ia1s2x2[2][2];
|
|
//--- check
|
|
if(iwork[6]==iu)
|
|
{
|
|
//--- change values
|
|
wl=work[n+1];
|
|
wlu=work[n+3];
|
|
nwl=iwork[1];
|
|
wu=work[n+4];
|
|
wul=work[n+2];
|
|
nwu=iwork[4];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
wl=work[n+2];
|
|
wlu=work[n+4];
|
|
nwl=iwork[2];
|
|
wu=work[n+3];
|
|
wul=work[n+1];
|
|
nwu=iwork[3];
|
|
}
|
|
//--- check
|
|
if(nwl<0 || nwl>=n || nwu<1 || nwu>n)
|
|
{
|
|
errorcode=4;
|
|
return(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- RANGE='A' or 'V' -- Set ATOLI
|
|
tnorm=MathMax(MathAbs(d[1])+MathAbs(e[1]),MathAbs(d[n])+MathAbs(e[n-1]));
|
|
for(j=2;j<n;j++)
|
|
tnorm=MathMax(tnorm,MathAbs(d[j])+MathAbs(e[j-1])+MathAbs(e[j]));
|
|
//--- check
|
|
if(abstol<=0.0)
|
|
atoli=ulp*tnorm;
|
|
else
|
|
atoli=abstol;
|
|
//--- check
|
|
if(irange==2)
|
|
{
|
|
wl=vl;
|
|
wu=vu;
|
|
}
|
|
else
|
|
{
|
|
wl=0;
|
|
wu=0;
|
|
}
|
|
}
|
|
//--- Find Eigenvalues -- Loop Over Blocks and recompute NWL and NWU.
|
|
//--- NWL accumulates the number of eigenvalues .le. WL,
|
|
//--- NWU accumulates the number of eigenvalues .le. WU
|
|
m=0;
|
|
iend=0;
|
|
errorcode=0;
|
|
nwl=0;
|
|
nwu=0;
|
|
for(jb=1;jb<=nsplit;jb++)
|
|
{
|
|
ioff=iend;
|
|
ibegin=ioff+1;
|
|
iend=isplit[jb];
|
|
iin=iend-ioff;
|
|
//--- check
|
|
if(iin==1)
|
|
{
|
|
//--- check
|
|
if(irange==1 || wl>=d[ibegin]-pivmin)
|
|
nwl=nwl+1;
|
|
//--- check
|
|
if(irange==1 || wu>=d[ibegin]-pivmin)
|
|
nwu=nwu+1;
|
|
//--- check
|
|
if((irange==1 || wl<d[ibegin]-pivmin) && wu>=d[ibegin]-pivmin)
|
|
{
|
|
m=m+1;
|
|
w[m]=d[ibegin];
|
|
iblock[m]=jb;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- General Case -- IIN > 1
|
|
//--- Compute Gershgorin Interval
|
|
//--- and use it as the initial interval
|
|
gu=d[ibegin];
|
|
gl=d[ibegin];
|
|
tmp1=0;
|
|
for(j=ibegin;j<=iend-1;j++)
|
|
{
|
|
//--- change values
|
|
tmp2=MathAbs(e[j]);
|
|
gu=MathMax(gu,d[j]+tmp1+tmp2);
|
|
gl=MathMin(gl,d[j]-tmp1-tmp2);
|
|
tmp1=tmp2;
|
|
}
|
|
//--- change values
|
|
gu=MathMax(gu,d[iend]+tmp1);
|
|
gl=MathMin(gl,d[iend]-tmp1);
|
|
bnorm=MathMax(MathAbs(gl),MathAbs(gu));
|
|
gl=gl-fudge*bnorm*ulp*iin-fudge*pivmin;
|
|
gu=gu+fudge*bnorm*ulp*iin+fudge*pivmin;
|
|
//--- Compute ATOLI for the current submatrix
|
|
if(abstol<=0.0)
|
|
atoli=ulp*MathMax(MathAbs(gl),MathAbs(gu));
|
|
else
|
|
atoli=abstol;
|
|
//--- check
|
|
if(irange>1)
|
|
{
|
|
//--- check
|
|
if(gu<wl)
|
|
{
|
|
nwl=nwl+iin;
|
|
nwu=nwu+iin;
|
|
continue;
|
|
}
|
|
gl=MathMax(gl,wl);
|
|
gu=MathMin(gu,wu);
|
|
//--- check
|
|
if(gl>=gu)
|
|
continue;
|
|
}
|
|
//--- Set Up Initial Interval
|
|
work[n+1]=gl;
|
|
work[n+iin+1]=gu;
|
|
//--- Calling DLAEBZ
|
|
//--- CALL DLAEBZ( 1, 0, IN, IN, 1, NB, ATOLI, RTOLI, PIVMIN,
|
|
//--- D( IBEGIN ), E( IBEGIN ), WORK( IBEGIN ),
|
|
//--- IDUMMA, WORK( N+1 ), WORK( N+2*IN+1 ), IM,
|
|
//--- IWORK, W( M+1 ), IBLOCK( M+1 ), IINFO )
|
|
for(tmpi=1;tmpi<=iin;tmpi++)
|
|
{
|
|
ra1siin[tmpi]=d[ibegin-1+tmpi];
|
|
//--- check
|
|
if(ibegin-1+tmpi<n)
|
|
ra2siin[tmpi]=e[ibegin-1+tmpi];
|
|
//--- change values
|
|
ra3siin[tmpi]=work[ibegin-1+tmpi];
|
|
ra1siinx2[tmpi].Set(1,work[n+tmpi]);
|
|
ra1siinx2[tmpi].Set(2,work[n+tmpi+iin]);
|
|
ra4siin[tmpi]=work[n+2*iin+tmpi];
|
|
rworkspace[tmpi]=w[m+tmpi];
|
|
iworkspace[tmpi]=iblock[m+tmpi];
|
|
ia1siinx2[tmpi].Set(1,iwork[tmpi]);
|
|
ia1siinx2[tmpi].Set(2,iwork[tmpi+iin]);
|
|
}
|
|
//--- function call
|
|
InternalDLAEBZ(1,0,iin,iin,1,atoli,rtoli,pivmin,ra1siin,ra2siin,ra3siin,idumma,ra1siinx2,ra4siin,im,ia1siinx2,rworkspace,iworkspace,iinfo);
|
|
for(tmpi=1;tmpi<=iin;tmpi++)
|
|
{
|
|
//--- change values
|
|
work[n+tmpi]=ra1siinx2[tmpi][1];
|
|
work[n+tmpi+iin]=ra1siinx2[tmpi][2];
|
|
work[n+2*iin+tmpi]=ra4siin[tmpi];
|
|
w[m+tmpi]=rworkspace[tmpi];
|
|
iblock[m+tmpi]=iworkspace[tmpi];
|
|
iwork[tmpi]=ia1siinx2[tmpi][1];
|
|
iwork[tmpi+iin]=ia1siinx2[tmpi][2];
|
|
}
|
|
nwl=nwl+iwork[1];
|
|
nwu=nwu+iwork[iin+1];
|
|
iwoff=m-iwork[1];
|
|
//--- Compute Eigenvalues
|
|
itmax=(int)MathCeil((MathLog(gu-gl+pivmin)-MathLog(pivmin))/MathLog(2))+2;
|
|
//--- Calling DLAEBZ
|
|
//--- CALL DLAEBZ( 2, ITMAX, IN, IN, 1, NB, ATOLI, RTOLI, PIVMIN,
|
|
//--- D( IBEGIN ), E( IBEGIN ), WORK( IBEGIN ),
|
|
//--- IDUMMA, WORK( N+1 ), WORK( N+2*IN+1 ), IOUT,
|
|
//--- IWORK, W( M+1 ), IBLOCK( M+1 ), IINFO )
|
|
for(tmpi=1;tmpi<=iin;tmpi++)
|
|
{
|
|
ra1siin[tmpi]=d[ibegin-1+tmpi];
|
|
//--- check
|
|
if(ibegin-1+tmpi<n)
|
|
ra2siin[tmpi]=e[ibegin-1+tmpi];
|
|
//--- change values
|
|
ra3siin[tmpi]=work[ibegin-1+tmpi];
|
|
ra1siinx2[tmpi].Set(1,work[n+tmpi]);
|
|
ra1siinx2[tmpi].Set(2,work[n+tmpi+iin]);
|
|
ra4siin[tmpi]=work[n+2*iin+tmpi];
|
|
rworkspace[tmpi]=w[m+tmpi];
|
|
iworkspace[tmpi]=iblock[m+tmpi];
|
|
ia1siinx2[tmpi].Set(1,iwork[tmpi]);
|
|
ia1siinx2[tmpi].Set(2,iwork[tmpi+iin]);
|
|
}
|
|
//--- function call
|
|
InternalDLAEBZ(2,itmax,iin,iin,1,atoli,rtoli,pivmin,ra1siin,ra2siin,ra3siin,idumma,ra1siinx2,ra4siin,iout,ia1siinx2,rworkspace,iworkspace,iinfo);
|
|
for(tmpi=1;tmpi<=iin;tmpi++)
|
|
{
|
|
//--- change values
|
|
work[n+tmpi]=ra1siinx2[tmpi][1];
|
|
work[n+tmpi+iin]=ra1siinx2[tmpi][2];
|
|
work[n+2*iin+tmpi]=ra4siin[tmpi];
|
|
w[m+tmpi]=rworkspace[tmpi];
|
|
iblock[m+tmpi]=iworkspace[tmpi];
|
|
iwork[tmpi]=ia1siinx2[tmpi][1];
|
|
iwork[tmpi+iin]=ia1siinx2[tmpi][2];
|
|
}
|
|
//--- Copy Eigenvalues Into W and IBLOCK
|
|
//--- Use -JB for block number for unconverged eigenvalues.
|
|
for(j=1;j<=iout;j++)
|
|
{
|
|
tmp1=0.5*(work[j+n]+work[j+iin+n]);
|
|
//--- Flag non-convergence.
|
|
if(j>iout-iinfo)
|
|
{
|
|
ncnvrg=true;
|
|
ib=-jb;
|
|
}
|
|
else
|
|
ib=jb;
|
|
for(je=iwork[j]+1+iwoff;je<=iwork[j+iin]+iwoff;je++)
|
|
{
|
|
w[je]=tmp1;
|
|
iblock[je]=ib;
|
|
}
|
|
}
|
|
m=m+im;
|
|
}
|
|
}
|
|
//--- If RANGE='I', then (WL,WU) contains eigenvalues NWL+1,...,NWU
|
|
//--- If NWL+1 < IL or NWU > IU, discard extra eigenvalues.
|
|
if(irange==3)
|
|
{
|
|
im=0;
|
|
idiscl=il-1-nwl;
|
|
idiscu=nwu-iu;
|
|
//--- check
|
|
if(idiscl>0 || idiscu>0)
|
|
{
|
|
for(je=1;je<=m;je++)
|
|
{
|
|
//--- check
|
|
if(w[je]<=wlu && idiscl>0)
|
|
{
|
|
idiscl=idiscl-1;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(w[je]>=wul && idiscu>0)
|
|
idiscu=idiscu-1;
|
|
else
|
|
{
|
|
im=im+1;
|
|
w[im]=w[je];
|
|
iblock[im]=iblock[je];
|
|
}
|
|
}
|
|
}
|
|
m=im;
|
|
}
|
|
//--- check
|
|
if(idiscl>0 || idiscu>0)
|
|
{
|
|
//--- Code to deal with effects of bad arithmetic:
|
|
//--- Some low eigenvalues to be discarded are not in (WL,WLU],
|
|
//--- or high eigenvalues to be discarded are not in (WUL,WU]
|
|
//--- so just kill off the smallest IDISCL/largest IDISCU
|
|
//--- eigenvalues, by simply finding the smallest/largest
|
|
//--- eigenvalue(s).
|
|
//--- (If N(w) is monotone non-decreasing, this should never
|
|
//--- happen.)
|
|
if(idiscl>0)
|
|
{
|
|
wkill=wu;
|
|
for(jdisc=1;jdisc<=idiscl;jdisc++)
|
|
{
|
|
iw=0;
|
|
for(je=1;je<=m;je++)
|
|
{
|
|
//--- check
|
|
if(iblock[je]!=0 && (w[je]<(double)(wkill) || iw==0))
|
|
{
|
|
iw=je;
|
|
wkill=w[je];
|
|
}
|
|
}
|
|
iblock[iw]=0;
|
|
}
|
|
}
|
|
//--- check
|
|
if(idiscu>0)
|
|
{
|
|
wkill=wl;
|
|
for(jdisc=1;jdisc<=idiscu;jdisc++)
|
|
{
|
|
iw=0;
|
|
for(je=1;je<=m;je++)
|
|
{
|
|
//--- check
|
|
if(iblock[je]!=0 && (w[je]>(double)(wkill) || iw==0))
|
|
{
|
|
iw=je;
|
|
wkill=w[je];
|
|
}
|
|
}
|
|
iblock[iw]=0;
|
|
}
|
|
}
|
|
im=0;
|
|
for(je=1;je<=m;je++)
|
|
{
|
|
//--- check
|
|
if(iblock[je]!=0)
|
|
{
|
|
im=im+1;
|
|
w[im]=w[je];
|
|
iblock[im]=iblock[je];
|
|
}
|
|
}
|
|
m=im;
|
|
}
|
|
//--- check
|
|
if(idiscl<0 || idiscu<0)
|
|
toofew=true;
|
|
}
|
|
//--- If ORDER='B', do nothing -- the eigenvalues are already sorted
|
|
//--- by block.
|
|
//--- If ORDER='E', sort the eigenvalues from smallest to largest
|
|
if(iorder==1 && nsplit>1)
|
|
{
|
|
for(je=1;je<=m-1;je++)
|
|
{
|
|
ie=0;
|
|
tmp1=w[je];
|
|
for(j=je+1;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(w[j]<tmp1)
|
|
{
|
|
ie=j;
|
|
tmp1=w[j];
|
|
}
|
|
}
|
|
//--- check
|
|
if(ie!=0)
|
|
{
|
|
//--- change values
|
|
itmp1=iblock[ie];
|
|
w[ie]=w[je];
|
|
iblock[ie]=iblock[je];
|
|
w[je]=tmp1;
|
|
iblock[je]=itmp1;
|
|
}
|
|
}
|
|
}
|
|
for(j=1;j<=m;j++)
|
|
w[j]=w[j]*scalefactor;
|
|
errorcode=0;
|
|
//--- check
|
|
if(ncnvrg)
|
|
errorcode=errorcode+1;
|
|
//--- check
|
|
if(toofew)
|
|
errorcode=errorcode+2;
|
|
result=errorcode==0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::InternalDStein(const int n,double &d[],double &ce[],
|
|
const int m,double &cw[],int &iblock[],
|
|
int &isplit[],CMatrixDouble &z,
|
|
int &ifail[],int &info)
|
|
{
|
|
//--- create variables
|
|
int maxits=0;
|
|
int extra=0;
|
|
int b1=0;
|
|
int blksiz=0;
|
|
int bn=0;
|
|
int gpind=0;
|
|
int i=0;
|
|
int iinfo=0;
|
|
int its=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int jblk=0;
|
|
int jmax=0;
|
|
int nblk=0;
|
|
int nrmchk=0;
|
|
double dtpcrt=0;
|
|
double eps=0;
|
|
double eps1=0;
|
|
double nrm=0;
|
|
double onenrm=0;
|
|
double ortol=0;
|
|
double pertol=0;
|
|
double scl=0;
|
|
double sep=0;
|
|
double tol=0;
|
|
double xj=0;
|
|
double xjm=0;
|
|
double ztr=0;
|
|
bool tmpcriterion;
|
|
int ti=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double work1[];
|
|
double work2[];
|
|
double work3[];
|
|
double work4[];
|
|
double work5[];
|
|
int iwork[];
|
|
//--- create copy
|
|
double e[];
|
|
ArrayResizeAL(e,ArraySize(ce));
|
|
ArrayCopy(e,ce);
|
|
//--- create copy
|
|
double w[];
|
|
ArrayResizeAL(w,ArraySize(cw));
|
|
ArrayCopy(w,cw);
|
|
//--- initialization
|
|
info=0;
|
|
maxits=5;
|
|
extra=2;
|
|
//--- allocation
|
|
ArrayResizeAL(work1,(int)MathMax(n,1)+1);
|
|
ArrayResizeAL(work2,(int)MathMax(n-1,1)+1);
|
|
ArrayResizeAL(work3,(int)MathMax(n,1)+1);
|
|
ArrayResizeAL(work4,(int)MathMax(n,1)+1);
|
|
ArrayResizeAL(work5,(int)MathMax(n,1)+1);
|
|
ArrayResizeAL(iwork,(int)MathMax(n,1)+1);
|
|
ArrayResizeAL(ifail,(int)MathMax(m,1)+1);
|
|
z.Resize((int)MathMax(n,1)+1,(int)MathMax(m,1)+1);
|
|
//--- initialization
|
|
gpind=0;
|
|
onenrm=0;
|
|
ortol=0;
|
|
dtpcrt=0;
|
|
xjm=0;
|
|
//--- check input parameters
|
|
info=0;
|
|
for(i=1;i<=m;i++)
|
|
ifail[i]=0;
|
|
//--- check
|
|
if(n<0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m<0 || m>n)
|
|
{
|
|
info=-4;
|
|
return;
|
|
}
|
|
for(j=2;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(iblock[j]<iblock[j-1])
|
|
{
|
|
info=-6;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
//--- check
|
|
if(iblock[j]==iblock[j-1] && w[j]<w[j-1])
|
|
{
|
|
info=-5;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(info!=0)
|
|
return;
|
|
//--- check
|
|
if(n==0 || m==0)
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
z[1].Set(1,1);
|
|
return;
|
|
}
|
|
//--- Some preparations
|
|
ti=n-1;
|
|
for(i_=1;i_<=ti;i_++)
|
|
work1[i_]=e[i_];
|
|
ArrayResizeAL(e,n+1);
|
|
for(i_=1;i_<=ti;i_++)
|
|
e[i_]=work1[i_];
|
|
for(i_=1;i_<=m;i_++)
|
|
work1[i_]=w[i_];
|
|
ArrayResizeAL(w,n+1);
|
|
for(i_=1;i_<=m;i_++)
|
|
w[i_]=work1[i_];
|
|
//--- Get machine constants.
|
|
eps=CMath::m_machineepsilon;
|
|
//--- Compute eigenvectors of matrix blocks.
|
|
j1=1;
|
|
for(nblk=1;nblk<=iblock[m];nblk++)
|
|
{
|
|
//--- Find starting and ending indices of block nblk.
|
|
if(nblk==1)
|
|
b1=1;
|
|
else
|
|
b1=isplit[nblk-1]+1;
|
|
bn=isplit[nblk];
|
|
blksiz=bn-b1+1;
|
|
//--- check
|
|
if(blksiz!=1)
|
|
{
|
|
//--- Compute reorthogonalization criterion and stopping criterion.
|
|
gpind=b1;
|
|
onenrm=MathAbs(d[b1])+MathAbs(e[b1]);
|
|
onenrm=MathMax(onenrm,MathAbs(d[bn])+MathAbs(e[bn-1]));
|
|
for(i=b1+1;i<=bn-1;i++)
|
|
onenrm=MathMax(onenrm,MathAbs(d[i])+MathAbs(e[i-1])+MathAbs(e[i]));
|
|
ortol=0.001*onenrm;
|
|
dtpcrt=MathSqrt(0.1/blksiz);
|
|
}
|
|
//--- Loop through eigenvalues of block nblk.
|
|
jblk=0;
|
|
for(j=j1;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(iblock[j]!=nblk)
|
|
{
|
|
j1=j;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
jblk=jblk+1;
|
|
xj=w[j];
|
|
//--- check
|
|
if(blksiz==1)
|
|
{
|
|
//--- Skip all the work if the block size is one.
|
|
work1[1]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- If eigenvalues j and j-1 are too close, add a relatively
|
|
//--- small perturbation.
|
|
if(jblk>1)
|
|
{
|
|
eps1=MathAbs(eps*xj);
|
|
pertol=10*eps1;
|
|
sep=xj-xjm;
|
|
//--- check
|
|
if(sep<pertol)
|
|
xj=xjm+pertol;
|
|
}
|
|
its=0;
|
|
nrmchk=0;
|
|
//--- Get random starting vector.
|
|
for(ti=1;ti<=blksiz;ti++)
|
|
work1[ti]=2*CMath::RandomReal()-1;
|
|
//--- Copy the matrix T so it won't be destroyed in factorization.
|
|
for(ti=1;ti<=blksiz-1;ti++)
|
|
{
|
|
work2[ti]=e[b1+ti-1];
|
|
work3[ti]=e[b1+ti-1];
|
|
work4[ti]=d[b1+ti-1];
|
|
}
|
|
work4[blksiz]=d[b1+blksiz-1];
|
|
//--- Compute LU factors with partial pivoting ( PT = LU )
|
|
tol=0;
|
|
TdIninternalDLAGTF(blksiz,work4,xj,work2,work3,tol,work5,iwork,iinfo);
|
|
//--- Update iteration count.
|
|
do
|
|
{
|
|
its=its+1;
|
|
//--- check
|
|
if(its>maxits)
|
|
{
|
|
//--- If stopping criterion was not satisfied, update info and
|
|
//--- store eigenvector number in array ifail.
|
|
info=info+1;
|
|
ifail[info]=j;
|
|
break;
|
|
}
|
|
//--- Normalize and scale the righthand side vector Pb.
|
|
v=0;
|
|
for(ti=1;ti<=blksiz;ti++)
|
|
v=v+MathAbs(work1[ti]);
|
|
scl=blksiz*onenrm*MathMax(eps,MathAbs(work4[blksiz]))/v;
|
|
for(i_=1;i_<=blksiz;i_++)
|
|
work1[i_]=scl*work1[i_];
|
|
//--- Solve the system LU = Pb.
|
|
TdIninternalDLAGTS(blksiz,work4,work2,work3,work5,iwork,work1,tol,iinfo);
|
|
//--- Reorthogonalize by modified Gram-Schmidt if eigenvalues are
|
|
//--- close enough.
|
|
if(jblk!=1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(xj-xjm)>ortol)
|
|
gpind=j;
|
|
//--- check
|
|
if(gpind!=j)
|
|
{
|
|
for(i=gpind;i<j;i++)
|
|
{
|
|
i1=b1;
|
|
i2=b1+blksiz-1;
|
|
i1_=i1-1;
|
|
ztr=0.0;
|
|
for(i_=1;i_<=blksiz;i_++)
|
|
ztr+=work1[i_]*z[i_+i1_][i];
|
|
i1_=i1-1;
|
|
for(i_=1;i_<=blksiz;i_++)
|
|
work1[i_]=work1[i_]-ztr*z[i_+i1_][i];
|
|
}
|
|
}
|
|
}
|
|
//--- Check the infinity norm of the iterate.
|
|
jmax=CBlas::VectorIdxAbsMax(work1,1,blksiz);
|
|
nrm=MathAbs(work1[jmax]);
|
|
//--- Continue for additional iterations after norm reaches
|
|
//--- stopping criterion.
|
|
tmpcriterion=false;
|
|
//--- check
|
|
if(nrm<dtpcrt)
|
|
tmpcriterion=true;
|
|
else
|
|
{
|
|
nrmchk=nrmchk+1;
|
|
//--- check
|
|
if(nrmchk<extra+1)
|
|
tmpcriterion=true;
|
|
}
|
|
}
|
|
while(tmpcriterion);
|
|
//--- Accept iterate as jth eigenvector.
|
|
scl=1/CBlas::VectorNorm2(work1,1,blksiz);
|
|
jmax=CBlas::VectorIdxAbsMax(work1,1,blksiz);
|
|
//--- check
|
|
if(work1[jmax]<0.0)
|
|
scl=-scl;
|
|
for(i_=1;i_<=blksiz;i_++)
|
|
work1[i_]=scl*work1[i_];
|
|
}
|
|
for(i=1;i<=n;i++)
|
|
z[i].Set(j,0);
|
|
for(i=1;i<=blksiz;i++)
|
|
z[b1+i-1].Set(j,work1[i]);
|
|
//--- Save the shift to check eigenvalue spacing at next iteration.
|
|
xjm=xj;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::TdIninternalDLAGTF(const int n,double &a[],const double lambdav,
|
|
double &b[],double &c[],double tol,
|
|
double &d[],int &iin[],int &info)
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
double eps=0;
|
|
double mult=0;
|
|
double piv1=0;
|
|
double piv2=0;
|
|
double scale1=0;
|
|
double scale2=0;
|
|
double temp=0;
|
|
double tl=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
a[1]=a[1]-lambdav;
|
|
iin[n]=0;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
//--- check
|
|
if(a[1]==0.0)
|
|
iin[1]=1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- initialization
|
|
eps=CMath::m_machineepsilon;
|
|
tl=MathMax(tol,eps);
|
|
scale1=MathAbs(a[1])+MathAbs(b[1]);
|
|
for(k=1;k<n;k++)
|
|
{
|
|
a[k+1]=a[k+1]-lambdav;
|
|
scale2=MathAbs(c[k])+MathAbs(a[k+1]);
|
|
//--- check
|
|
if(k<n-1)
|
|
scale2=scale2+MathAbs(b[k+1]);
|
|
//--- check
|
|
if(a[k]==0.0)
|
|
piv1=0;
|
|
else
|
|
piv1=MathAbs(a[k])/scale1;
|
|
//--- check
|
|
if(c[k]==0.0)
|
|
{
|
|
iin[k]=0;
|
|
piv2=0;
|
|
scale1=scale2;
|
|
//--- check
|
|
if(k<n-1)
|
|
d[k]=0;
|
|
}
|
|
else
|
|
{
|
|
piv2=MathAbs(c[k])/scale2;
|
|
//--- check
|
|
if(piv2<=piv1)
|
|
{
|
|
//--- change values
|
|
iin[k]=0;
|
|
scale1=scale2;
|
|
c[k]=c[k]/a[k];
|
|
a[k+1]=a[k+1]-c[k]*b[k];
|
|
//--- check
|
|
if(k<n-1)
|
|
d[k]=0;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
iin[k]=1;
|
|
mult=a[k]/c[k];
|
|
a[k]=c[k];
|
|
temp=a[k+1];
|
|
a[k+1]=b[k]-mult*temp;
|
|
if(k<n-1)
|
|
{
|
|
d[k]=b[k+1];
|
|
b[k+1]=-(mult*d[k]);
|
|
}
|
|
b[k]=temp;
|
|
c[k]=mult;
|
|
}
|
|
}
|
|
//--- check
|
|
if(MathMax(piv1,piv2)<=tl && iin[n]==0)
|
|
iin[n]=k;
|
|
}
|
|
//--- check
|
|
if(MathAbs(a[n])<=scale1*tl && iin[n]==0)
|
|
iin[n]=n;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::TdIninternalDLAGTS(const int n,double &a[],double &b[],
|
|
double &c[],double &d[],int &iin[],
|
|
double &y[],double &tol,int &info)
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
double absak=0;
|
|
double ak=0;
|
|
double bignum=0;
|
|
double eps=0;
|
|
double pert=0;
|
|
double sfmin=0;
|
|
double temp=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- initialization
|
|
eps=CMath::m_machineepsilon;
|
|
sfmin=CMath::m_minrealnumber;
|
|
bignum=1/sfmin;
|
|
//--- check
|
|
if(tol<=0.0)
|
|
{
|
|
tol=MathAbs(a[1]);
|
|
//--- check
|
|
if(n>1)
|
|
tol=MathMax(tol,MathMax(MathAbs(a[2]),MathAbs(b[1])));
|
|
for(k=3;k<=n;k++)
|
|
tol=MathMax(tol,MathMax(MathAbs(a[k]),MathMax(MathAbs(b[k-1]),MathAbs(d[k-2]))));
|
|
tol=tol*eps;
|
|
//--- check
|
|
if(tol==0.0)
|
|
tol=eps;
|
|
}
|
|
for(k=2;k<=n;k++)
|
|
{
|
|
//--- check
|
|
if(iin[k-1]==0)
|
|
y[k]=y[k]-c[k-1]*y[k-1];
|
|
else
|
|
{
|
|
temp=y[k-1];
|
|
y[k-1]=y[k];
|
|
y[k]=temp-c[k-1]*y[k];
|
|
}
|
|
}
|
|
for(k=n;k>=1;k--)
|
|
{
|
|
//--- check
|
|
if(k<=n-2)
|
|
temp=y[k]-b[k]*y[k+1]-d[k]*y[k+2];
|
|
else
|
|
{
|
|
//--- check
|
|
if(k==n-1)
|
|
temp=y[k]-b[k]*y[k+1];
|
|
else
|
|
temp=y[k];
|
|
}
|
|
ak=a[k];
|
|
pert=MathAbs(tol);
|
|
//--- check
|
|
if(ak<0.0)
|
|
pert=-pert;
|
|
while(true)
|
|
{
|
|
absak=MathAbs(ak);
|
|
//--- check
|
|
if(absak<1.0)
|
|
{
|
|
//--- check
|
|
if(absak<sfmin)
|
|
{
|
|
//--- check
|
|
if(absak==0.0 || MathAbs(temp)*sfmin>absak)
|
|
{
|
|
ak=ak+pert;
|
|
pert=2*pert;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
temp=temp*bignum;
|
|
ak=ak*bignum;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(MathAbs(temp)>absak*bignum)
|
|
{
|
|
ak=ak+pert;
|
|
pert=2*pert;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
y[k]=temp/ak;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::InternalDLAEBZ(const int ijob,const int nitmax,
|
|
const int n,const int mmax,const int minp,
|
|
const double abstol,const double reltol,
|
|
const double pivmin,double &d[],
|
|
double &e[],double &e2[],int &nval[],
|
|
CMatrixDouble &ab,double &c[],int &mout,
|
|
CMatrixInt &nab,double &work[],
|
|
int &iwork[],int &info)
|
|
{
|
|
//--- create variables
|
|
int itmp1=0;
|
|
int itmp2=0;
|
|
int j=0;
|
|
int ji=0;
|
|
int jit=0;
|
|
int jp=0;
|
|
int kf=0;
|
|
int kfnew=0;
|
|
int kl=0;
|
|
int klnew=0;
|
|
double tmp1=0;
|
|
double tmp2=0;
|
|
//--- initialization
|
|
mout=0;
|
|
info=0;
|
|
//--- check
|
|
if(ijob<1 || ijob>3)
|
|
{
|
|
info=-1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Initialize NAB
|
|
if(ijob==1)
|
|
{
|
|
//--- Compute the number of eigenvalues in the initial intervals.
|
|
mout=0;
|
|
//--- DIR$ NOVECTOR
|
|
for(ji=1;ji<=minp;ji++)
|
|
{
|
|
for(jp=1;jp<=2;jp++)
|
|
{
|
|
tmp1=d[1]-ab[ji][jp];
|
|
//--- check
|
|
if(MathAbs(tmp1)<pivmin)
|
|
tmp1=-pivmin;
|
|
nab[ji].Set(jp,0);
|
|
//--- check
|
|
if(tmp1<=0.0)
|
|
nab[ji].Set(jp,1);
|
|
for(j=2;j<=n;j++)
|
|
{
|
|
tmp1=d[j]-e2[j-1]/tmp1-ab[ji][jp];
|
|
//--- check
|
|
if(MathAbs(tmp1)<pivmin)
|
|
tmp1=-pivmin;
|
|
//--- check
|
|
if(tmp1<=0.0)
|
|
nab[ji].Set(jp,nab[ji][jp]+1);
|
|
}
|
|
}
|
|
mout=mout+nab[ji][2]-nab[ji][1];
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Initialize for loop
|
|
//--- KF and KL have the following meaning:
|
|
//--- Intervals 1,...,KF-1 have converged.
|
|
//--- Intervals KF,...,KL still need to be refined.
|
|
kf=1;
|
|
kl=minp;
|
|
//--- If IJOB=2, initialize C.
|
|
//--- If IJOB=3, use the user-supplied starting point.
|
|
if(ijob==2)
|
|
{
|
|
for(ji=1;ji<=minp;ji++)
|
|
c[ji]=0.5*(ab[ji][1]+ab[ji][2]);
|
|
}
|
|
//--- Iteration loop
|
|
for(jit=1;jit<=nitmax;jit++)
|
|
{
|
|
//--- Loop over intervals
|
|
//--- Serial Version of the loop
|
|
klnew=kl;
|
|
for(ji=kf;ji<=kl;ji++)
|
|
{
|
|
//--- Compute N(w), the number of eigenvalues less than w
|
|
tmp1=c[ji];
|
|
tmp2=d[1]-tmp1;
|
|
itmp1=0;
|
|
//--- check
|
|
if(tmp2<=pivmin)
|
|
{
|
|
itmp1=1;
|
|
tmp2=MathMin(tmp2,-pivmin);
|
|
}
|
|
//--- A series of compiler directives to defeat vectorization
|
|
//--- for the next loop
|
|
//--- *$PL$ CMCHAR=' '
|
|
//--- CDIR$ NEXTSCALAR
|
|
//--- C$DIR SCALAR
|
|
//--- CDIR$ NEXT SCALAR
|
|
//--- CVD$L NOVECTOR
|
|
//--- CDEC$ NOVECTOR
|
|
//--- CVD$ NOVECTOR
|
|
//--- *VDIR NOVECTOR
|
|
//--- *VOCL LOOP,SCALAR
|
|
//--- CIBM PREFER SCALAR
|
|
//--- *$PL$ CMCHAR='*'
|
|
for(j=2;j<=n;j++)
|
|
{
|
|
tmp2=d[j]-e2[j-1]/tmp2-tmp1;
|
|
//--- check
|
|
if(tmp2<=pivmin)
|
|
{
|
|
itmp1=itmp1+1;
|
|
tmp2=MathMin(tmp2,-pivmin);
|
|
}
|
|
}
|
|
//--- check
|
|
if(ijob<=2)
|
|
{
|
|
//--- IJOB=2: Choose all intervals containing eigenvalues.
|
|
//--- Insure that N(w) is monotone
|
|
itmp1=MathMin(nab[ji][2],MathMax(nab[ji][1],itmp1));
|
|
//--- Update the Queue -- add intervals if both halves
|
|
//--- contain eigenvalues.
|
|
if(itmp1==nab[ji][2])
|
|
{
|
|
//--- No eigenvalue in the upper interval:
|
|
//--- just use the lower interval.
|
|
ab[ji].Set(2,tmp1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(itmp1==nab[ji][1])
|
|
{
|
|
//--- No eigenvalue in the lower interval:
|
|
//--- just use the upper interval.
|
|
ab[ji].Set(1,tmp1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(klnew<mmax)
|
|
{
|
|
//--- Eigenvalue in both intervals -- add upper to queue.
|
|
klnew=klnew+1;
|
|
ab[klnew].Set(2,ab[ji][2]);
|
|
nab[klnew].Set(2,nab[ji][2]);
|
|
ab[klnew].Set(1,tmp1);
|
|
nab[klnew].Set(1,itmp1);
|
|
ab[ji].Set(2,tmp1);
|
|
nab[ji].Set(2,itmp1);
|
|
}
|
|
else
|
|
{
|
|
info=mmax+1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- IJOB=3: Binary search. Keep only the interval
|
|
//--- containing w s.t. N(w) = NVAL
|
|
if(itmp1<=nval[ji])
|
|
{
|
|
ab[ji].Set(1,tmp1);
|
|
nab[ji].Set(1,itmp1);
|
|
}
|
|
//--- check
|
|
if(itmp1>=nval[ji])
|
|
{
|
|
ab[ji].Set(2,tmp1);
|
|
nab[ji].Set(2,itmp1);
|
|
}
|
|
}
|
|
}
|
|
kl=klnew;
|
|
//--- Check for convergence
|
|
kfnew=kf;
|
|
for(ji=kf;ji<=kl;ji++)
|
|
{
|
|
tmp1=MathAbs(ab[ji][2]-ab[ji][1]);
|
|
tmp2=MathMax(MathAbs(ab[ji][2]),MathAbs(ab[ji][1]));
|
|
//--- check
|
|
if(tmp1<(double)(MathMax(abstol,MathMax(pivmin,reltol*tmp2))) || nab[ji][1]>=nab[ji][2])
|
|
{
|
|
//--- Converged -- Swap with position KFNEW,
|
|
//--- then increment KFNEW
|
|
if(ji>kfnew)
|
|
{
|
|
tmp1=ab[ji][1];
|
|
tmp2=ab[ji][2];
|
|
itmp1=nab[ji][1];
|
|
itmp2=nab[ji][2];
|
|
//--- change values
|
|
ab[ji].Set(1,ab[kfnew][1]);
|
|
ab[ji].Set(2,ab[kfnew][2]);
|
|
nab[ji].Set(1,nab[kfnew][1]);
|
|
nab[ji].Set(2,nab[kfnew][2]);
|
|
ab[kfnew].Set(1,tmp1);
|
|
ab[kfnew].Set(2,tmp2);
|
|
nab[kfnew].Set(1,itmp1);
|
|
nab[kfnew].Set(2,itmp2);
|
|
//--- check
|
|
if(ijob==3)
|
|
{
|
|
itmp1=nval[ji];
|
|
nval[ji]=nval[kfnew];
|
|
nval[kfnew]=itmp1;
|
|
}
|
|
}
|
|
kfnew=kfnew+1;
|
|
}
|
|
}
|
|
kf=kfnew;
|
|
//--- Choose Midpoints
|
|
for(ji=kf;ji<=kl;ji++)
|
|
c[ji]=0.5*(ab[ji][1]+ab[ji][2]);
|
|
//--- If no more intervals to refine, quit.
|
|
if(kf>kl)
|
|
break;
|
|
}
|
|
//--- Converged
|
|
info=(int)MathMax(kl+1-kf,0);
|
|
mout=kl;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab, and Rice University |
|
|
//| June 30,1999 |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::InternalTREVC(CMatrixDouble &t,const int n,const int side,
|
|
const int howmny,bool &cvselect[],CMatrixDouble &vl,
|
|
CMatrixDouble &vr,int &m,int &info)
|
|
{
|
|
//--- create variables
|
|
bool allv;
|
|
bool bothv;
|
|
bool leftv;
|
|
bool over;
|
|
bool pair;
|
|
bool rightv;
|
|
bool somev;
|
|
int i=0;
|
|
int ierr=0;
|
|
int ii=0;
|
|
int ip=0;
|
|
int iis=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int jnxt=0;
|
|
int k=0;
|
|
int ki=0;
|
|
int n2=0;
|
|
double beta=0;
|
|
double bignum=0;
|
|
double emax=0;
|
|
double ovfl=0;
|
|
double rec=0;
|
|
double remax=0;
|
|
double scl=0;
|
|
double smin=0;
|
|
double smlnum=0;
|
|
double ulp=0;
|
|
double unfl=0;
|
|
double vcrit=0;
|
|
double vmax=0;
|
|
double wi=0;
|
|
double wr=0;
|
|
double xnorm=0;
|
|
bool skipflag;
|
|
int k1=0;
|
|
int k2=0;
|
|
int k3=0;
|
|
int k4=0;
|
|
double vt=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double work[];
|
|
double temp[];
|
|
bool rswap4[];
|
|
bool zswap4[];
|
|
double civ4[];
|
|
double crv4[];
|
|
//--- create matrix
|
|
CMatrixDouble x;
|
|
CMatrixDouble temp11;
|
|
CMatrixDouble temp22;
|
|
CMatrixDouble temp11b;
|
|
CMatrixDouble temp21b;
|
|
CMatrixDouble temp12b;
|
|
CMatrixDouble temp22b;
|
|
CMatrixInt ipivot44;
|
|
//--- create copy
|
|
double vselect[];
|
|
ArrayResizeAL(vselect,ArraySize(cvselect));
|
|
ArrayCopy(vselect,cvselect);
|
|
//--- initialization
|
|
m=0;
|
|
info=0;
|
|
//--- allocation
|
|
x.Resize(3,3);
|
|
temp11.Resize(2,2);
|
|
temp11b.Resize(2,2);
|
|
temp21b.Resize(3,2);
|
|
temp12b.Resize(2,3);
|
|
temp22b.Resize(3,3);
|
|
temp22.Resize(3,3);
|
|
ArrayResizeAL(work,3*n+1);
|
|
ArrayResizeAL(temp,n+1);
|
|
ArrayResizeAL(rswap4,5);
|
|
ArrayResizeAL(zswap4,5);
|
|
ArrayResizeAL(civ4,5);
|
|
ArrayResizeAL(crv4,5);
|
|
ipivot44.Resize(5,5);
|
|
//--- check
|
|
if(howmny!=1)
|
|
{
|
|
//--- check
|
|
if(side==1 || side==3)
|
|
vr.Resize(n+1,n+1);
|
|
//--- check
|
|
if(side==2 || side==3)
|
|
vl.Resize(n+1,n+1);
|
|
}
|
|
//--- Decode and test the input parameters
|
|
bothv=side==3;
|
|
rightv=side==1 || bothv;
|
|
leftv=side==2 || bothv;
|
|
allv=howmny==2;
|
|
over=howmny==1;
|
|
somev=howmny==3;
|
|
info=0;
|
|
//--- check
|
|
if(n<0)
|
|
{
|
|
info=-2;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!rightv && !leftv)
|
|
{
|
|
info=-3;
|
|
return;
|
|
}
|
|
//--- check
|
|
if((!allv && !over) && !somev)
|
|
{
|
|
info=-4;
|
|
return;
|
|
}
|
|
//--- Set M to the number of columns required to store the selected
|
|
//--- eigenvectors, standardize the array SELECT if necessary, and
|
|
//--- test MM.
|
|
if(somev)
|
|
{
|
|
m=0;
|
|
pair=false;
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(pair)
|
|
{
|
|
pair=false;
|
|
vselect[j]=false;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(j<n)
|
|
{
|
|
//--- check
|
|
if(t[j+1][j]==0.0)
|
|
{
|
|
//--- check
|
|
if(vselect[j])
|
|
m=m+1;
|
|
}
|
|
else
|
|
{
|
|
pair=true;
|
|
//--- check
|
|
if(vselect[j] || vselect[j+1])
|
|
{
|
|
vselect[j]=true;
|
|
m=m+2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(vselect[n])
|
|
m=m+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m=n;
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- Set the constants to control overflow.
|
|
unfl=CMath::m_minrealnumber;
|
|
ovfl=1/unfl;
|
|
ulp=CMath::m_machineepsilon;
|
|
smlnum=unfl*(n/ulp);
|
|
bignum=(1-ulp)/smlnum;
|
|
//--- Compute 1-norm of each column of strictly upper triangular
|
|
//--- part of T to control overflow in triangular solver.
|
|
work[1]=0;
|
|
for(j=2;j<=n;j++)
|
|
{
|
|
work[j]=0;
|
|
for(i=1;i<j;i++)
|
|
work[j]=work[j]+MathAbs(t[i][j]);
|
|
}
|
|
//--- Index IP is used to specify the real or complex eigenvalue:
|
|
//--- IP = 0, real eigenvalue,
|
|
//--- 1, first of conjugate complex pair: (wr,wi)
|
|
//--- -1, second of conjugate complex pair: (wr,wi)
|
|
n2=2*n;
|
|
//--- check
|
|
if(rightv)
|
|
{
|
|
//--- Compute right eigenvectors.
|
|
ip=0;
|
|
iis=m;
|
|
for(ki=n;ki>=1;ki--)
|
|
{
|
|
skipflag=false;
|
|
//--- check
|
|
if(ip==1)
|
|
skipflag=true;
|
|
else
|
|
{
|
|
//--- check
|
|
if(ki!=1)
|
|
{
|
|
//--- check
|
|
if(t[ki][ki-1]!=0.0)
|
|
ip=-1;
|
|
}
|
|
//--- check
|
|
if(somev)
|
|
{
|
|
//--- check
|
|
if(ip==0)
|
|
{
|
|
//--- check
|
|
if(!vselect[ki])
|
|
skipflag=true;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!vselect[ki-1])
|
|
skipflag=true;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(!skipflag)
|
|
{
|
|
//--- Compute the KI-th eigenvalue (WR,WI).
|
|
wr=t[ki][ki];
|
|
wi=0;
|
|
//--- check
|
|
if(ip!=0)
|
|
wi=MathSqrt(MathAbs(t[ki][ki-1]))*MathSqrt(MathAbs(t[ki-1][ki]));
|
|
smin=MathMax(ulp*(MathAbs(wr)+MathAbs(wi)),smlnum);
|
|
//--- check
|
|
if(ip==0)
|
|
{
|
|
//--- Real right eigenvector
|
|
work[ki+n]=1;
|
|
//--- Form right-hand side
|
|
for(k=1;k<=ki-1;k++)
|
|
work[k+n]=-t[k][ki];
|
|
//--- Solve the upper quasi-triangular system:
|
|
//--- (T(1:KI-1,1:KI-1) - WR)*X = SCALE*WORK.
|
|
jnxt=ki-1;
|
|
for(j=ki-1;j>=1;j--)
|
|
{
|
|
//--- check
|
|
if(j>jnxt)
|
|
continue;
|
|
j1=j;
|
|
j2=j;
|
|
jnxt=j-1;
|
|
//--- check
|
|
if(j>1)
|
|
{
|
|
//--- check
|
|
if(t[j][j-1]!=0.0)
|
|
{
|
|
j1=j-1;
|
|
jnxt=j-2;
|
|
}
|
|
}
|
|
//--- check
|
|
if(j1==j2)
|
|
{
|
|
//--- 1-by-1 diagonal block
|
|
temp11[1].Set(1,t[j][j]);
|
|
temp11b[1].Set(1,work[j+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(false,1,1,smin,1,temp11,1.0,1.0,temp11b,wr,0.0,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale X(1,1) to avoid overflow when updating
|
|
//--- the right-hand side.
|
|
if(xnorm>1.0)
|
|
{
|
|
//--- check
|
|
if(work[j]>bignum/xnorm)
|
|
{
|
|
x[1].Set(1,x[1][1]/xnorm);
|
|
scl=scl/xnorm;
|
|
}
|
|
}
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
k1=n+1;
|
|
k2=n+ki;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
work[j+n]=x[1][1];
|
|
//--- Update right-hand side
|
|
k1=1+n;
|
|
k2=j-1+n;
|
|
k3=j-1;
|
|
vt=-x[1][1];
|
|
i1_=1-k1;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
{
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- 2-by-2 diagonal block
|
|
temp22[1].Set(1,t[j-1][j-1]);
|
|
temp22[1].Set(2,t[j-1][j]);
|
|
temp22[2].Set(1,t[j][j-1]);
|
|
temp22[2].Set(2,t[j][j]);
|
|
temp21b[1].Set(1,work[j-1+n]);
|
|
temp21b[2].Set(1,work[j+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(false,2,1,smin,1.0,temp22,1.0,1.0,temp21b,wr,0,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale X(1,1) and X(2,1) to avoid overflow when
|
|
//--- updating the right-hand side.
|
|
if(xnorm>1.0)
|
|
{
|
|
beta=MathMax(work[j-1],work[j]);
|
|
//--- check
|
|
if(beta>bignum/xnorm)
|
|
{
|
|
x[1].Set(1,x[1][1]/xnorm);
|
|
x[2].Set(1,x[2][1]/xnorm);
|
|
scl=scl/xnorm;
|
|
}
|
|
}
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
k1=1+n;
|
|
k2=ki+n;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
work[j-1+n]=x[1][1];
|
|
work[j+n]=x[2][1];
|
|
//--- Update right-hand side
|
|
k1=1+n;
|
|
k2=j-2+n;
|
|
k3=j-2;
|
|
k4=j-1;
|
|
vt=-x[1][1];
|
|
i1_=1-k1;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][k4];
|
|
vt=-x[2][1];
|
|
i1_=1-k1;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j];
|
|
}
|
|
}
|
|
//--- Copy the vector x or Q*x to VR and normalize.
|
|
if(!over)
|
|
{
|
|
k1=1+n;
|
|
k2=ki+n;
|
|
i1_=k1-1;
|
|
for(i_=1;i_<=ki;i_++)
|
|
vr[i_].Set(iis,work[i_+i1_]);
|
|
//--- function call
|
|
ii=CBlas::ColumnIdxAbsMax(vr,1,ki,iis);
|
|
remax=1/MathAbs(vr[ii][iis]);
|
|
for(i_=1;i_<=ki;i_++)
|
|
vr[i_].Set(iis,remax*vr[i_][iis]);
|
|
for(k=ki+1;k<=n;k++)
|
|
vr[k].Set(iis,0);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(ki>1)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
temp[i_]=vr[i_][ki];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(vr,1,n,1,ki-1,false,work,1+n,ki-1+n,1.0,temp,1,n,work[ki+n]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki,temp[i_]);
|
|
}
|
|
//--- function call
|
|
ii=CBlas::ColumnIdxAbsMax(vr,1,n,ki);
|
|
remax=1/MathAbs(vr[ii][ki]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki,remax*vr[i_][ki]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Complex right eigenvector.
|
|
//--- Initial solve
|
|
//--- [ (T(KI-1,KI-1) T(KI-1,KI) ) - (WR + I* WI)]*X = 0.
|
|
//--- [ (T(KI,KI-1) T(KI,KI) ) ]
|
|
if(MathAbs(t[ki-1][ki])>=MathAbs(t[ki][ki-1]))
|
|
{
|
|
work[ki-1+n]=1;
|
|
work[ki+n2]=wi/t[ki-1][ki];
|
|
}
|
|
else
|
|
{
|
|
work[ki-1+n]=-(wi/t[ki][ki-1]);
|
|
work[ki+n2]=1;
|
|
}
|
|
work[ki+n]=0;
|
|
work[ki-1+n2]=0;
|
|
//--- Form right-hand side
|
|
for(k=1;k<=ki-2;k++)
|
|
{
|
|
work[k+n]=-(work[ki-1+n]*t[k][ki-1]);
|
|
work[k+n2]=-(work[ki+n2]*t[k][ki]);
|
|
}
|
|
//--- Solve upper quasi-triangular system:
|
|
//--- (T(1:KI-2,1:KI-2) - (WR+i*WI))*X = SCALE*(WORK+i*WORK2)
|
|
jnxt=ki-2;
|
|
for(j=ki-2;j>=1;j--)
|
|
{
|
|
//--- check
|
|
if(j>jnxt)
|
|
continue;
|
|
j1=j;
|
|
j2=j;
|
|
jnxt=j-1;
|
|
//--- check
|
|
if(j>1)
|
|
{
|
|
//--- check
|
|
if(t[j][j-1]!=0.0)
|
|
{
|
|
j1=j-1;
|
|
jnxt=j-2;
|
|
}
|
|
}
|
|
//--- check
|
|
if(j1==j2)
|
|
{
|
|
//--- 1-by-1 diagonal block
|
|
temp11[1].Set(1,t[j][j]);
|
|
temp12b[1].Set(1,work[j+n]);
|
|
temp12b[1].Set(2,work[j+n+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(false,1,2,smin,1.0,temp11,1.0,1.0,temp12b,wr,wi,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale X(1,1) and X(1,2) to avoid overflow when
|
|
//--- updating the right-hand side.
|
|
if(xnorm>1.0)
|
|
{
|
|
//--- check
|
|
if(work[j]>bignum/xnorm)
|
|
{
|
|
x[1].Set(1,x[1][1]/xnorm);
|
|
x[1].Set(2,x[1][2]/xnorm);
|
|
scl=scl/xnorm;
|
|
}
|
|
}
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
k1=1+n;
|
|
k2=ki+n;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
k1=1+n2;
|
|
k2=ki+n2;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
work[j+n]=x[1][1];
|
|
work[j+n2]=x[1][2];
|
|
//--- Update the right-hand side
|
|
k1=1+n;
|
|
k2=j-1+n;
|
|
k3=1;
|
|
k4=j-1;
|
|
vt=-x[1][1];
|
|
i1_=k3-k1;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j];
|
|
//--- change values
|
|
k1=1+n2;
|
|
k2=j-1+n2;
|
|
k3=1;
|
|
k4=j-1;
|
|
vt=-x[1][2];
|
|
i1_=k3-k1;
|
|
for(i_=k1;i_<=k2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j];
|
|
}
|
|
else
|
|
{
|
|
//--- 2-by-2 diagonal block
|
|
temp22[1].Set(1,t[j-1][j-1]);
|
|
temp22[1].Set(2,t[j-1][j]);
|
|
temp22[2].Set(1,t[j][j-1]);
|
|
temp22[2].Set(2,t[j][j]);
|
|
temp22b[1].Set(1,work[j-1+n]);
|
|
temp22b[1].Set(2,work[j-1+n+n]);
|
|
temp22b[2].Set(1,work[j+n]);
|
|
temp22b[2].Set(2,work[j+n+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(false,2,2,smin,1.0,temp22,1.0,1.0,temp22b,wr,wi,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale X to avoid overflow when updating
|
|
//--- the right-hand side.
|
|
if(xnorm>1.0)
|
|
{
|
|
beta=MathMax(work[j-1],work[j]);
|
|
//--- check
|
|
if(beta>bignum/xnorm)
|
|
{
|
|
rec=1/xnorm;
|
|
x[1].Set(1,x[1][1]*rec);
|
|
x[1].Set(2,x[1][2]*rec);
|
|
x[2].Set(1,x[2][1]*rec);
|
|
x[2].Set(2,x[2][2]*rec);
|
|
scl=scl*rec;
|
|
}
|
|
}
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
for(i_=1+n;i_<=ki+n;i_++)
|
|
work[i_]=scl*work[i_];
|
|
for(i_=1+n2;i_<=ki+n2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
//--- change values
|
|
work[j-1+n]=x[1][1];
|
|
work[j+n]=x[2][1];
|
|
work[j-1+n2]=x[1][2];
|
|
work[j+n2]=x[2][2];
|
|
//--- Update the right-hand side
|
|
vt=-x[1][1];
|
|
i1_=-n;
|
|
for(i_=n+1;i_<=n+j-2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j-1];
|
|
vt=-x[2][1];
|
|
i1_=-n;
|
|
for(i_=n+1;i_<=n+j-2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j];
|
|
vt=-x[1][2];
|
|
i1_=-n2;
|
|
for(i_=n2+1;i_<=n2+j-2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j-1];
|
|
vt=-x[2][2];
|
|
i1_=-n2;
|
|
for(i_=n2+1;i_<=n2+j-2;i_++)
|
|
work[i_]=work[i_]+vt*t[i_+i1_][j];
|
|
}
|
|
}
|
|
//--- Copy the vector x or Q*x to VR and normalize.
|
|
if(!over)
|
|
{
|
|
i1_=n;
|
|
for(i_=1;i_<=ki;i_++)
|
|
vr[i_].Set(iis-1,work[i_+i1_]);
|
|
i1_=n2;
|
|
for(i_=1;i_<=ki;i_++)
|
|
vr[i_].Set(iis,work[i_+i1_]);
|
|
emax=0;
|
|
for(k=1;k<=ki;k++)
|
|
emax=MathMax(emax,MathAbs(vr[k][iis-1])+MathAbs(vr[k][iis]));
|
|
remax=1/emax;
|
|
//--- copy
|
|
for(i_=1;i_<=ki;i_++)
|
|
vr[i_].Set(iis-1,remax*vr[i_][iis-1]);
|
|
for(i_=1;i_<=ki;i_++)
|
|
vr[i_].Set(iis,remax*vr[i_][iis]);
|
|
for(k=ki+1;k<=n;k++)
|
|
vr[k].Set(iis-1,0);
|
|
vr[k].Set(iis,0);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(ki>2)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
temp[i_]=vr[i_][ki-1];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(vr,1,n,1,ki-2,false,work,1+n,ki-2+n,1.0,temp,1,n,work[ki-1+n]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki-1,temp[i_]);
|
|
for(i_=1;i_<=n;i_++)
|
|
temp[i_]=vr[i_][ki];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(vr,1,n,1,ki-2,false,work,1+n2,ki-2+n2,1.0,temp,1,n,work[ki+n2]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki,temp[i_]);
|
|
}
|
|
else
|
|
{
|
|
vt=work[ki-1+n];
|
|
//--- copy
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki-1,vt*vr[i_][ki-1]);
|
|
vt=work[ki+n2];
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki,vt*vr[i_][ki]);
|
|
}
|
|
emax=0;
|
|
for(k=1;k<=n;k++)
|
|
emax=MathMax(emax,MathAbs(vr[k][ki-1])+MathAbs(vr[k][ki]));
|
|
remax=1/emax;
|
|
//--- copy
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki-1,remax*vr[i_][ki-1]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i_].Set(ki,remax*vr[i_][ki]);
|
|
}
|
|
}
|
|
iis=iis-1;
|
|
//--- check
|
|
if(ip!=0)
|
|
iis=iis-1;
|
|
}
|
|
//--- check
|
|
if(ip==1)
|
|
ip=0;
|
|
//--- check
|
|
if(ip==-1)
|
|
ip=1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(leftv)
|
|
{
|
|
//--- Compute left eigenvectors.
|
|
ip=0;
|
|
iis=1;
|
|
for(ki=1;ki<=n;ki++)
|
|
{
|
|
skipflag=false;
|
|
//--- check
|
|
if(ip==-1)
|
|
skipflag=true;
|
|
else
|
|
{
|
|
//--- check
|
|
if(ki!=n)
|
|
{
|
|
//--- check
|
|
if(t[ki+1][ki]!=0.0)
|
|
ip=1;
|
|
}
|
|
//--- check
|
|
if(somev)
|
|
{
|
|
//--- check
|
|
if(!vselect[ki])
|
|
skipflag=true;
|
|
}
|
|
}
|
|
//--- check
|
|
if(!skipflag)
|
|
{
|
|
//--- Compute the KI-th eigenvalue (WR,WI).
|
|
wr=t[ki][ki];
|
|
wi=0;
|
|
//--- check
|
|
if(ip!=0)
|
|
wi=MathSqrt(MathAbs(t[ki][ki+1]))*MathSqrt(MathAbs(t[ki+1][ki]));
|
|
smin=MathMax(ulp*(MathAbs(wr)+MathAbs(wi)),smlnum);
|
|
//--- check
|
|
if(ip==0)
|
|
{
|
|
//--- Real left eigenvector.
|
|
work[ki+n]=1;
|
|
//--- Form right-hand side
|
|
for(k=ki+1;k<=n;k++)
|
|
work[k+n]=-t[ki][k];
|
|
//--- Solve the quasi-triangular system:
|
|
//--- (T(KI+1:N,KI+1:N) - WR)'*X = SCALE*WORK
|
|
vmax=1;
|
|
vcrit=bignum;
|
|
jnxt=ki+1;
|
|
for(j=ki+1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(j<jnxt)
|
|
continue;
|
|
j1=j;
|
|
j2=j;
|
|
jnxt=j+1;
|
|
//--- check
|
|
if(j<n)
|
|
{
|
|
//--- check
|
|
if(t[j+1][j]!=0.0)
|
|
{
|
|
j2=j+1;
|
|
jnxt=j+2;
|
|
}
|
|
}
|
|
//--- check
|
|
if(j1==j2)
|
|
{
|
|
//--- 1-by-1 diagonal block
|
|
//--- Scale if necessary to avoid overflow when forming
|
|
//--- the right-hand side.
|
|
if(work[j]>vcrit)
|
|
{
|
|
rec=1/vmax;
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
{
|
|
work[i_]=rec*work[i_];
|
|
}
|
|
vmax=1;
|
|
vcrit=bignum;
|
|
}
|
|
i1_=n;
|
|
vt=0.0;
|
|
for(i_=ki+1;i_<j;i_++)
|
|
vt+=t[i_][j]*work[i_+i1_];
|
|
work[j+n]=work[j+n]-vt;
|
|
//--- Solve (T(J,J)-WR)'*X = WORK
|
|
temp11[1].Set(1,t[j][j]);
|
|
temp11b[1].Set(1,work[j+n]);
|
|
InternalHsEVDLALN2(false,1,1,smin,1.0,temp11,1.0,1.0,temp11b,wr,0,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
work[j+n]=x[1][1];
|
|
vmax=MathMax(MathAbs(work[j+n]),vmax);
|
|
vcrit=bignum/vmax;
|
|
}
|
|
else
|
|
{
|
|
//--- 2-by-2 diagonal block
|
|
//--- Scale if necessary to avoid overflow when forming
|
|
//--- the right-hand side.
|
|
beta=MathMax(work[j],work[j+1]);
|
|
//--- check
|
|
if(beta>vcrit)
|
|
{
|
|
rec=1/vmax;
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=rec*work[i_];
|
|
vmax=1;
|
|
vcrit=bignum;
|
|
}
|
|
i1_=n;
|
|
vt=0.0;
|
|
for(i_=ki+1;i_<j;i_++)
|
|
vt+=t[i_][j]*work[i_+i1_];
|
|
//--- change values
|
|
work[j+n]=work[j+n]-vt;
|
|
i1_=n;
|
|
vt=0.0;
|
|
for(i_=ki+1;i_<j;i_++)
|
|
vt+=t[i_][j+1]*work[i_+i1_];
|
|
work[j+1+n]=work[j+1+n]-vt;
|
|
//--- Solve
|
|
//--- [T(J,J)-WR T(J,J+1) ]'* X = SCALE*( WORK1 )
|
|
//--- [T(J+1,J) T(J+1,J+1)-WR] ( WORK2 )
|
|
temp22[1].Set(1,t[j][j]);
|
|
temp22[1].Set(2,t[j][j+1]);
|
|
temp22[2].Set(1,t[j+1][j]);
|
|
temp22[2].Set(2,t[j+1][j+1]);
|
|
temp21b[1].Set(1,work[j+n]);
|
|
temp21b[2].Set(1,work[j+1+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(true,2,1,smin,1.0,temp22,1.0,1.0,temp21b,wr,0,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
//--- change values
|
|
work[j+n]=x[1][1];
|
|
work[j+1+n]=x[2][1];
|
|
vmax=MathMax(MathAbs(work[j+n]),MathMax(MathAbs(work[j+1+n]),vmax));
|
|
vcrit=bignum/vmax;
|
|
}
|
|
}
|
|
//--- Copy the vector x or Q*x to VL and normalize.
|
|
if(!over)
|
|
{
|
|
i1_=n;;
|
|
for(i_=ki;i_<=n;i_++)
|
|
vl[i_].Set(iis,work[i_+i1_]);
|
|
//--- function call
|
|
ii=CBlas::ColumnIdxAbsMax(vl,ki,n,iis);
|
|
remax=1/MathAbs(vl[ii][iis]);
|
|
for(i_=ki;i_<=n;i_++)
|
|
vl[i_].Set(iis,remax*vl[i_][iis]);
|
|
for(k=1;k<=ki-1;k++)
|
|
vl[k].Set(iis,0);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(ki<n)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
temp[i_]=vl[i_][ki];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(vl,1,n,ki+1,n,false,work,ki+1+n,n+n,1.0,temp,1,n,work[ki+n]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki,temp[i_]);
|
|
}
|
|
//--- function call
|
|
ii=CBlas::ColumnIdxAbsMax(vl,1,n,ki);
|
|
remax=1/MathAbs(vl[ii][ki]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki,remax*vl[i_][ki]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Complex left eigenvector.
|
|
//--- Initial solve:
|
|
//--- ((T(KI,KI) T(KI,KI+1) )' - (WR - I* WI))*X = 0.
|
|
//--- ((T(KI+1,KI) T(KI+1,KI+1)) )
|
|
if(MathAbs(t[ki][ki+1])>=MathAbs(t[ki+1][ki]))
|
|
{
|
|
work[ki+n]=wi/t[ki][ki+1];
|
|
work[ki+1+n2]=1;
|
|
}
|
|
else
|
|
{
|
|
work[ki+n]=1;
|
|
work[ki+1+n2]=-(wi/t[ki+1][ki]);
|
|
}
|
|
work[ki+1+n]=0;
|
|
work[ki+n2]=0;
|
|
//--- Form right-hand side
|
|
for(k=ki+2;k<=n;k++)
|
|
{
|
|
work[k+n]=-(work[ki+n]*t[ki][k]);
|
|
work[k+n2]=-(work[ki+1+n2]*t[ki+1][k]);
|
|
}
|
|
//--- Solve complex quasi-triangular system:
|
|
//--- ( T(KI+2,N:KI+2,N) - (WR-i*WI) )*X = WORK1+i*WORK2
|
|
vmax=1;
|
|
vcrit=bignum;
|
|
jnxt=ki+2;
|
|
for(j=ki+2;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(j<jnxt)
|
|
continue;
|
|
j1=j;
|
|
j2=j;
|
|
jnxt=j+1;
|
|
//--- check
|
|
if(j<n)
|
|
{
|
|
//--- check
|
|
if(t[j+1][j]!=0.0)
|
|
{
|
|
j2=j+1;
|
|
jnxt=j+2;
|
|
}
|
|
}
|
|
//--- check
|
|
if(j1==j2)
|
|
{
|
|
//--- 1-by-1 diagonal block
|
|
//--- Scale if necessary to avoid overflow when
|
|
//--- forming the right-hand side elements.
|
|
if(work[j]>vcrit)
|
|
{
|
|
rec=1/vmax;
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=rec*work[i_];
|
|
for(i_=ki+n2;i_<=n+n2;i_++)
|
|
work[i_]=rec*work[i_];
|
|
vmax=1;
|
|
vcrit=bignum;
|
|
}
|
|
i1_=n;
|
|
vt=0.0;
|
|
for(i_=ki+2;i_<j;i_++)
|
|
vt+=t[i_][j]*work[i_+i1_];
|
|
//--- calculation
|
|
work[j+n]=work[j+n]-vt;
|
|
i1_=n2;
|
|
vt=0.0;
|
|
for(i_=ki+2;i_<j;i_++)
|
|
vt+=t[i_][j]*work[i_+i1_];
|
|
work[j+n2]=work[j+n2]-vt;
|
|
//--- Solve (T(J,J)-(WR-i*WI))*(X11+i*X12)= WK+I*WK2
|
|
temp11[1].Set(1,t[j][j]);
|
|
temp12b[1].Set(1,work[j+n]);
|
|
temp12b[1].Set(2,work[j+n+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(false,1,2,smin,1.0,temp11,1.0,1.0,temp12b,wr,-wi,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=scl*work[i_];
|
|
for(i_=ki+n2;i_<=n+n2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
//--- change values
|
|
work[j+n]=x[1][1];
|
|
work[j+n2]=x[1][2];
|
|
vmax=MathMax(MathAbs(work[j+n]),MathMax(MathAbs(work[j+n2]),vmax));
|
|
vcrit=bignum/vmax;
|
|
}
|
|
else
|
|
{
|
|
//--- 2-by-2 diagonal block
|
|
//--- Scale if necessary to avoid overflow when forming
|
|
//--- the right-hand side elements.
|
|
beta=MathMax(work[j],work[j+1]);
|
|
//--- check
|
|
if(beta>vcrit)
|
|
{
|
|
rec=1/vmax;
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=rec*work[i_];
|
|
for(i_=ki+n2;i_<=n+n2;i_++)
|
|
work[i_]=rec*work[i_];
|
|
vmax=1;
|
|
vcrit=bignum;
|
|
}
|
|
i1_=n;
|
|
vt=0.0;
|
|
for(i_=ki+2;i_<j;i_++)
|
|
vt+=t[i_][j]*work[i_+i1_];
|
|
//--- calculation
|
|
work[j+n]=work[j+n]-vt;
|
|
i1_=n2;
|
|
vt=0.0;
|
|
for(i_=ki+2;i_<j;i_++)
|
|
vt+=t[i_][j]*work[i_+i1_];
|
|
//--- calculation
|
|
work[j+n2]=work[j+n2]-vt;
|
|
i1_=n;
|
|
vt=0.0;
|
|
for(i_=ki+2;i_<j;i_++)
|
|
vt+=t[i_][j+1]*work[i_+i1_];
|
|
//--- calculation
|
|
work[j+1+n]=work[j+1+n]-vt;
|
|
i1_=n2;
|
|
vt=0.0;
|
|
for(i_=ki+2;i_<j;i_++)
|
|
vt+=t[i_][j+1]*work[i_+i1_];
|
|
work[j+1+n2]=work[j+1+n2]-vt;
|
|
//--- Solve 2-by-2 complex linear equation
|
|
//--- ([T(j,j) T(j,j+1) ]'-(wr-i*wi)*I)*X = SCALE*B
|
|
//--- ([T(j+1,j) T(j+1,j+1)] )
|
|
temp22[1].Set(1,t[j][j]);
|
|
temp22[1].Set(2,t[j][j+1]);
|
|
temp22[2].Set(1,t[j+1][j]);
|
|
temp22[2].Set(2,t[j+1][j+1]);
|
|
temp22b[1].Set(1,work[j+n]);
|
|
temp22b[1].Set(2,work[j+n+n]);
|
|
temp22b[2].Set(1,work[j+1+n]);
|
|
temp22b[2].Set(2,work[j+1+n+n]);
|
|
//--- function call
|
|
InternalHsEVDLALN2(true,2,2,smin,1.0,temp22,1.0,1.0,temp22b,wr,-wi,rswap4,zswap4,ipivot44,civ4,crv4,x,scl,xnorm,ierr);
|
|
//--- Scale if necessary
|
|
if(scl!=1.0)
|
|
{
|
|
for(i_=ki+n;i_<=n+n;i_++)
|
|
work[i_]=scl*work[i_];
|
|
for(i_=ki+n2;i_<=n+n2;i_++)
|
|
work[i_]=scl*work[i_];
|
|
}
|
|
//--- change values
|
|
work[j+n]=x[1][1];
|
|
work[j+n2]=x[1][2];
|
|
work[j+1+n]=x[2][1];
|
|
work[j+1+n2]=x[2][2];
|
|
vmax=MathMax(MathAbs(x[1][1]),vmax);
|
|
vmax=MathMax(MathAbs(x[1][2]),vmax);
|
|
vmax=MathMax(MathAbs(x[2][1]),vmax);
|
|
vmax=MathMax(MathAbs(x[2][2]),vmax);
|
|
vcrit=bignum/vmax;
|
|
}
|
|
}
|
|
//--- Copy the vector x or Q*x to VL and normalize.
|
|
if(!over)
|
|
{
|
|
i1_=n;
|
|
for(i_=ki;i_<=n;i_++)
|
|
vl[i_].Set(iis,work[i_+i1_]);
|
|
i1_=n2;
|
|
for(i_=ki;i_<=n;i_++)
|
|
vl[i_].Set(iis+1,work[i_+i1_]);
|
|
emax=0;
|
|
for(k=ki;k<=n;k++)
|
|
emax=MathMax(emax,MathAbs(vl[k][iis])+MathAbs(vl[k][iis+1]));
|
|
remax=1/emax;
|
|
//--- copy
|
|
for(i_=ki;i_<=n;i_++)
|
|
vl[i_].Set(iis,remax*vl[i_][iis]);
|
|
for(i_=ki;i_<=n;i_++)
|
|
vl[i_].Set(iis+1,remax*vl[i_][iis+1]);
|
|
for(k=1;k<=ki-1;k++)
|
|
{
|
|
vl[k].Set(iis,0);
|
|
vl[k].Set(iis+1,0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(ki<n-1)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
temp[i_]=vl[i_][ki];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(vl,1,n,ki+2,n,false,work,ki+2+n,n+n,1.0,temp,1,n,work[ki+n]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki,temp[i_]);
|
|
for(i_=1;i_<=n;i_++)
|
|
temp[i_]=vl[i_][ki+1];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(vl,1,n,ki+2,n,false,work,ki+2+n2,n+n2,1.0,temp,1,n,work[ki+1+n2]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki+1,temp[i_]);
|
|
}
|
|
else
|
|
{
|
|
//--- copy
|
|
vt=work[ki+n];
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki,vt*vl[i_][ki]);
|
|
vt=work[ki+1+n2];
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki+1,vt*vl[i_][ki+1]);
|
|
}
|
|
emax=0;
|
|
for(k=1;k<=n;k++)
|
|
emax=MathMax(emax,MathAbs(vl[k][ki])+MathAbs(vl[k][ki+1]));
|
|
remax=1/emax;
|
|
//--- copy
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki,remax*vl[i_][ki]);
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i_].Set(ki+1,remax*vl[i_][ki+1]);
|
|
}
|
|
}
|
|
iis=iis+1;
|
|
//--- check
|
|
if(ip!=0)
|
|
iis=iis+1;
|
|
}
|
|
//--- check
|
|
if(ip==-1)
|
|
ip=0;
|
|
//--- check
|
|
if(ip==1)
|
|
ip=-1;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| DLALN2 solves a system of the form (ca A - w D ) X = s B |
|
|
//| or (ca A' - w D) X = s B with possible scaling ("s") and |
|
|
//| perturbation of A. (A' means A-transpose.) |
|
|
//| A is an NA x NA real matrix, ca is a real scalar, D is an NA x |
|
|
//| NA real diagonal matrix, w is a real or complex value, and X and |
|
|
//| B are NA x 1 matrices -- real if w is real, complex if w is |
|
|
//| complex. NA may be 1 or 2. |
|
|
//| If w is complex, X and B are represented as NA x 2 matrices, |
|
|
//| the first column of each being the real part and the second |
|
|
//| being the imaginary part. |
|
|
//| "s" is a scaling factor (.LE. 1), computed by DLALN2, which is |
|
|
//| so chosen that X can be computed without overflow. X is further |
|
|
//| scaled if necessary to assure that norm(ca A - w D)*norm(X) is |
|
|
//| less than overflow. |
|
|
//| If both singular values of (ca A - w D) are less than SMIN, |
|
|
//| SMIN*identity will be used instead of (ca A - w D). If only one |
|
|
//| singular value is less than SMIN, one element of (ca A - w D) |
|
|
//| will be perturbed enough to make the smallest singular value |
|
|
//| roughly SMIN. If both singular values are at least SMIN, |
|
|
//| (ca A - w D) will not be perturbed. In any case, the |
|
|
//| perturbation will be at most some small multiple of max( SMIN, |
|
|
//| ulp*norm(ca A - w D) ). The singular values are computed by |
|
|
//| infinity-norm approximations, and thus will only be correct to a |
|
|
//| factor of 2 or so. |
|
|
//| Note: all input quantities are assumed to be smaller than |
|
|
//| overflow by a reasonable factor. (See BIGNUM.) |
|
|
//| -- LAPACK auxiliary routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::InternalHsEVDLALN2(const bool ltrans,const int na,
|
|
const int nw,const double smin,
|
|
const double ca,CMatrixDouble &a,
|
|
const double d1,const double d2,
|
|
CMatrixDouble &b,const double wr,
|
|
const double wi,bool &rswap4[],
|
|
bool &zswap4[],CMatrixInt &ipivot44,
|
|
double &civ4[],double &crv4[],
|
|
CMatrixDouble &x,double &scl,
|
|
double &xnorm,int &info)
|
|
{
|
|
//--- create variables
|
|
int icmax=0;
|
|
int j=0;
|
|
double bbnd=0;
|
|
double bi1=0;
|
|
double bi2=0;
|
|
double bignum=0;
|
|
double bnorm=0;
|
|
double br1=0;
|
|
double br2=0;
|
|
double ci21=0;
|
|
double ci22=0;
|
|
double cmax=0;
|
|
double cnorm=0;
|
|
double cr21=0;
|
|
double cr22=0;
|
|
double csi=0;
|
|
double csr=0;
|
|
double li21=0;
|
|
double lr21=0;
|
|
double smini=0;
|
|
double smlnum=0;
|
|
double temp=0;
|
|
double u22abs=0;
|
|
double ui11=0;
|
|
double ui11r=0;
|
|
double ui12=0;
|
|
double ui12s=0;
|
|
double ui22=0;
|
|
double ur11=0;
|
|
double ur11r=0;
|
|
double ur12=0;
|
|
double ur12s=0;
|
|
double ur22=0;
|
|
double xi1=0;
|
|
double xi2=0;
|
|
double xr1=0;
|
|
double xr2=0;
|
|
double tmp1=0;
|
|
double tmp2=0;
|
|
//--- initialization
|
|
scl=0;
|
|
xnorm=0;
|
|
info=0;
|
|
zswap4[1]=false;
|
|
zswap4[2]=false;
|
|
zswap4[3]=true;
|
|
zswap4[4]=true;
|
|
rswap4[1]=false;
|
|
rswap4[2]=true;
|
|
rswap4[3]=false;
|
|
rswap4[4]=true;
|
|
ipivot44[1].Set(1,1);
|
|
ipivot44[2].Set(1,2);
|
|
ipivot44[3].Set(1,3);
|
|
ipivot44[4].Set(1,4);
|
|
ipivot44[1].Set(2,2);
|
|
ipivot44[2].Set(2,1);
|
|
ipivot44[3].Set(2,4);
|
|
ipivot44[4].Set(2,3);
|
|
ipivot44[1].Set(3,3);
|
|
ipivot44[2].Set(3,4);
|
|
ipivot44[3].Set(3,1);
|
|
ipivot44[4].Set(3,2);
|
|
ipivot44[1].Set(4,4);
|
|
ipivot44[2].Set(4,3);
|
|
ipivot44[3].Set(4,2);
|
|
ipivot44[4].Set(4,1);
|
|
smlnum=2*CMath::m_minrealnumber;
|
|
bignum=1/smlnum;
|
|
smini=MathMax(smin,smlnum);
|
|
//--- initialization
|
|
info=0;
|
|
scl=1;
|
|
//--- check
|
|
if(na==1)
|
|
{
|
|
//--- 1 x 1 (i.e., scalar) system C X = B
|
|
if(nw==1)
|
|
{
|
|
//--- Real 1x1 system.
|
|
//--- C = ca A - w D
|
|
csr=ca*a[1][1]-wr*d1;
|
|
cnorm=MathAbs(csr);
|
|
//--- If | C | < SMINI, use C = SMINI
|
|
if(cnorm<smini)
|
|
{
|
|
csr=smini;
|
|
cnorm=smini;
|
|
info=1;
|
|
}
|
|
//--- Check scaling for X = B / C
|
|
bnorm=MathAbs(b[1][1]);
|
|
//--- check
|
|
if(cnorm<1.0 && bnorm>1.0)
|
|
{
|
|
//--- check
|
|
if(bnorm>bignum*cnorm)
|
|
scl=1/bnorm;
|
|
}
|
|
//--- Compute X
|
|
x[1].Set(1,b[1][1]*scl/csr);
|
|
xnorm=MathAbs(x[1][1]);
|
|
}
|
|
else
|
|
{
|
|
//--- Complex 1x1 system (w is complex)
|
|
//--- C = ca A - w D
|
|
csr=ca*a[1][1]-wr*d1;
|
|
csi=-(wi*d1);
|
|
cnorm=MathAbs(csr)+MathAbs(csi);
|
|
//--- If | C | < SMINI, use C = SMINI
|
|
if(cnorm<smini)
|
|
{
|
|
csr=smini;
|
|
csi=0;
|
|
cnorm=smini;
|
|
info=1;
|
|
}
|
|
//--- Check scaling for X = B / C
|
|
bnorm=MathAbs(b[1][1])+MathAbs(b[1][2]);
|
|
//--- check
|
|
if(cnorm<1.0 && bnorm>1.0)
|
|
{
|
|
//--- check
|
|
if(bnorm>bignum*cnorm)
|
|
scl=1/bnorm;
|
|
}
|
|
//--- Compute X
|
|
InternalHsEVDLADIV(scl*b[1][1],scl*b[1][2],csr,csi,tmp1,tmp2);
|
|
x[1].Set(1,tmp1);
|
|
x[1].Set(2,tmp2);
|
|
xnorm=MathAbs(x[1][1])+MathAbs(x[1][2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- 2x2 System
|
|
//--- Compute the real part of C = ca A - w D (or ca A' - w D )
|
|
crv4[1+0]=ca*a[1][1]-wr*d1;
|
|
crv4[2+2]=ca*a[2][2]-wr*d2;
|
|
//--- check
|
|
if(ltrans)
|
|
{
|
|
crv4[1+2]=ca*a[2][1];
|
|
crv4[2+0]=ca*a[1][2];
|
|
}
|
|
else
|
|
{
|
|
crv4[2+0]=ca*a[2][1];
|
|
crv4[1+2]=ca*a[1][2];
|
|
}
|
|
//--- check
|
|
if(nw==1)
|
|
{
|
|
//--- Real 2x2 system (w is real)
|
|
//--- Find the largest element in C
|
|
cmax=0;
|
|
icmax=0;
|
|
for(j=1;j<=4;j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(crv4[j])>cmax)
|
|
{
|
|
cmax=MathAbs(crv4[j]);
|
|
icmax=j;
|
|
}
|
|
}
|
|
//--- If norm(C) < SMINI, use SMINI*identity.
|
|
if(cmax<smini)
|
|
{
|
|
bnorm=MathMax(MathAbs(b[1][1]),MathAbs(b[2][1]));
|
|
//--- check
|
|
if(smini<1.0 && bnorm>1.0)
|
|
{
|
|
//--- check
|
|
if(bnorm>bignum*smini)
|
|
scl=1/bnorm;
|
|
}
|
|
//--- change values
|
|
temp=scl/smini;
|
|
x[1].Set(1,temp*b[1][1]);
|
|
x[2].Set(1,temp*b[2][1]);
|
|
xnorm=temp*bnorm;
|
|
info=1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Gaussian elimination with complete pivoting.
|
|
ur11=crv4[icmax];
|
|
cr21=crv4[ipivot44[2][icmax]];
|
|
ur12=crv4[ipivot44[3][icmax]];
|
|
cr22=crv4[ipivot44[4][icmax]];
|
|
ur11r=1/ur11;
|
|
lr21=ur11r*cr21;
|
|
ur22=cr22-ur12*lr21;
|
|
//--- If smaller pivot < SMINI, use SMINI
|
|
if(MathAbs(ur22)<smini)
|
|
{
|
|
ur22=smini;
|
|
info=1;
|
|
}
|
|
//--- check
|
|
if(rswap4[icmax])
|
|
{
|
|
br1=b[2][1];
|
|
br2=b[1][1];
|
|
}
|
|
else
|
|
{
|
|
br1=b[1][1];
|
|
br2=b[2][1];
|
|
}
|
|
br2=br2-lr21*br1;
|
|
bbnd=MathMax(MathAbs(br1*(ur22*ur11r)),MathAbs(br2));
|
|
//--- check
|
|
if(bbnd>1.0 && MathAbs(ur22)<1.0)
|
|
{
|
|
//--- check
|
|
if(bbnd>=bignum*MathAbs(ur22))
|
|
scl=1/bbnd;
|
|
}
|
|
xr2=br2*scl/ur22;
|
|
xr1=scl*br1*ur11r-xr2*(ur11r*ur12);
|
|
//--- check
|
|
if(zswap4[icmax])
|
|
{
|
|
x[1].Set(1,xr2);
|
|
x[2].Set(1,xr1);
|
|
}
|
|
else
|
|
{
|
|
x[1].Set(1,xr1);
|
|
x[2].Set(1,xr2);
|
|
}
|
|
xnorm=MathMax(MathAbs(xr1),MathAbs(xr2));
|
|
//--- Further scaling if norm(A) norm(X) > overflow
|
|
if(xnorm>1.0 && cmax>1.0)
|
|
{
|
|
//--- check
|
|
if(xnorm>bignum/cmax)
|
|
{
|
|
temp=cmax/bignum;
|
|
x[1].Set(1,temp*x[1][1]);
|
|
x[2].Set(1,temp*x[2][1]);
|
|
xnorm=temp*xnorm;
|
|
scl=temp*scl;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Complex 2x2 system (w is complex)
|
|
//--- Find the largest element in C
|
|
civ4[1+0]=-(wi*d1);
|
|
civ4[2+0]=0;
|
|
civ4[1+2]=0;
|
|
civ4[2+2]=-(wi*d2);
|
|
cmax=0;
|
|
icmax=0;
|
|
for(j=1;j<=4;j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(crv4[j])+MathAbs(civ4[j])>cmax)
|
|
{
|
|
cmax=MathAbs(crv4[j])+MathAbs(civ4[j]);
|
|
icmax=j;
|
|
}
|
|
}
|
|
//--- If norm(C) < SMINI, use SMINI*identity.
|
|
if(cmax<smini)
|
|
{
|
|
bnorm=MathMax(MathAbs(b[1][1])+MathAbs(b[1][2]),MathAbs(b[2][1])+MathAbs(b[2][2]));
|
|
//--- check
|
|
if(smini<1.0 && bnorm>1.0)
|
|
{
|
|
//--- check
|
|
if(bnorm>bignum*smini)
|
|
scl=1/bnorm;
|
|
}
|
|
//--- change values
|
|
temp=scl/smini;
|
|
x[1].Set(1,temp*b[1][1]);
|
|
x[2].Set(1,temp*b[2][1]);
|
|
x[1].Set(2,temp*b[1][2]);
|
|
x[2].Set(2,temp*b[2][2]);
|
|
xnorm=temp*bnorm;
|
|
info=1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Gaussian elimination with complete pivoting.
|
|
ur11=crv4[icmax];
|
|
ui11=civ4[icmax];
|
|
cr21=crv4[ipivot44[2][icmax]];
|
|
ci21=civ4[ipivot44[2][icmax]];
|
|
ur12=crv4[ipivot44[3][icmax]];
|
|
ui12=civ4[ipivot44[3][icmax]];
|
|
cr22=crv4[ipivot44[4][icmax]];
|
|
ci22=civ4[ipivot44[4][icmax]];
|
|
//--- check
|
|
if(icmax==1 || icmax==4)
|
|
{
|
|
//--- Code when off-diagonals of pivoted C are real
|
|
if(MathAbs(ur11)>MathAbs(ui11))
|
|
{
|
|
temp=ui11/ur11;
|
|
ur11r=1/(ur11*(1+CMath::Sqr(temp)));
|
|
ui11r=-(temp*ur11r);
|
|
}
|
|
else
|
|
{
|
|
temp=ur11/ui11;
|
|
ui11r=-(1/(ui11*(1+CMath::Sqr(temp))));
|
|
ur11r=-(temp*ui11r);
|
|
}
|
|
//--- change values
|
|
lr21=cr21*ur11r;
|
|
li21=cr21*ui11r;
|
|
ur12s=ur12*ur11r;
|
|
ui12s=ur12*ui11r;
|
|
ur22=cr22-ur12*lr21;
|
|
ui22=ci22-ur12*li21;
|
|
}
|
|
else
|
|
{
|
|
//--- Code when diagonals of pivoted C are real
|
|
ur11r=1/ur11;
|
|
ui11r=0;
|
|
lr21=cr21*ur11r;
|
|
li21=ci21*ur11r;
|
|
ur12s=ur12*ur11r;
|
|
ui12s=ui12*ur11r;
|
|
ur22=cr22-ur12*lr21+ui12*li21;
|
|
ui22=-(ur12*li21)-ui12*lr21;
|
|
}
|
|
u22abs=MathAbs(ur22)+MathAbs(ui22);
|
|
//--- If smaller pivot < SMINI, use SMINI
|
|
if(u22abs<smini)
|
|
{
|
|
ur22=smini;
|
|
ui22=0;
|
|
info=1;
|
|
}
|
|
//--- check
|
|
if(rswap4[icmax])
|
|
{
|
|
br2=b[1][1];
|
|
br1=b[2][1];
|
|
bi2=b[1][2];
|
|
bi1=b[2][2];
|
|
}
|
|
else
|
|
{
|
|
br1=b[1][1];
|
|
br2=b[2][1];
|
|
bi1=b[1][2];
|
|
bi2=b[2][2];
|
|
}
|
|
br2=br2-lr21*br1+li21*bi1;
|
|
bi2=bi2-li21*br1-lr21*bi1;
|
|
bbnd=MathMax((MathAbs(br1)+MathAbs(bi1))*(u22abs*(MathAbs(ur11r)+MathAbs(ui11r))),MathAbs(br2)+MathAbs(bi2));
|
|
//--- check
|
|
if(bbnd>1.0 && u22abs<1.0)
|
|
{
|
|
//--- check
|
|
if(bbnd>=bignum*u22abs)
|
|
{
|
|
//--- change values
|
|
scl=1/bbnd;
|
|
br1=scl*br1;
|
|
bi1=scl*bi1;
|
|
br2=scl*br2;
|
|
bi2=scl*bi2;
|
|
}
|
|
}
|
|
//--- function call
|
|
InternalHsEVDLADIV(br2,bi2,ur22,ui22,xr2,xi2);
|
|
xr1=ur11r*br1-ui11r*bi1-ur12s*xr2+ui12s*xi2;
|
|
xi1=ui11r*br1+ur11r*bi1-ui12s*xr2-ur12s*xi2;
|
|
//--- check
|
|
if(zswap4[icmax])
|
|
{
|
|
x[1].Set(1,xr2);
|
|
x[2].Set(1,xr1);
|
|
x[1].Set(2,xi2);
|
|
x[2].Set(2,xi1);
|
|
}
|
|
else
|
|
{
|
|
x[1].Set(1,xr1);
|
|
x[2].Set(1,xr2);
|
|
x[1].Set(2,xi1);
|
|
x[2].Set(2,xi2);
|
|
}
|
|
xnorm=MathMax(MathAbs(xr1)+MathAbs(xi1),MathAbs(xr2)+MathAbs(xi2));
|
|
//--- Further scaling if norm(A) norm(X) > overflow
|
|
if(xnorm>1.0 && cmax>1.0)
|
|
{
|
|
//--- check
|
|
if(xnorm>bignum/cmax)
|
|
{
|
|
//--- change values
|
|
temp=cmax/bignum;
|
|
x[1].Set(1,temp*x[1][1]);
|
|
x[2].Set(1,temp*x[2][1]);
|
|
x[1].Set(2,temp*x[1][2]);
|
|
x[2].Set(2,temp*x[2][2]);
|
|
xnorm=temp*xnorm;
|
|
scl=temp*scl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| performs complex division in real arithmetic |
|
|
//| a + i*b |
|
|
//| p + i*q = --------- |
|
|
//| c + i*d |
|
|
//| The algorithm is due to Robert L. Smith and can be found |
|
|
//| in D. Knuth, The art of Computer Programming, Vol.2, p.195 |
|
|
//| -- LAPACK auxiliary routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::InternalHsEVDLADIV(const double a,const double b,
|
|
const double c,const double d,
|
|
double &p,double &q)
|
|
{
|
|
//--- create variables
|
|
double e=0;
|
|
double f=0;
|
|
//--- initialization
|
|
p=0;
|
|
q=0;
|
|
//--- check
|
|
if(MathAbs(d)<MathAbs(c))
|
|
{
|
|
//--- get result
|
|
e=d/c;
|
|
f=c+d*e;
|
|
p=(a+b*e)/f;
|
|
q=(b-a*e)/f;
|
|
}
|
|
else
|
|
{
|
|
//--- get result
|
|
e=c/d;
|
|
f=d+c*e;
|
|
p=(b+a*e)/f;
|
|
q=(-a+b*e)/f;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CEigenVDetect::NonSymmetricEVD(CMatrixDouble &ca,const int n,
|
|
const int vneeded,double &wr[],
|
|
double &wi[],CMatrixDouble &vl,
|
|
CMatrixDouble &vr)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int info=0;
|
|
int m=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tau[];
|
|
bool sel[];
|
|
//--- create matrix
|
|
CMatrixDouble s;
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(vneeded>=0 && vneeded<=3,__FUNCTION__+": incorrect VNeeded!"))
|
|
return(false);
|
|
//--- check
|
|
if(vneeded==0)
|
|
{
|
|
//--- Eigen values only
|
|
ToUpperHessenberg(a,n,tau);
|
|
//--- function call
|
|
CHsSchur::InternalSchurDecomposition(a,n,0,0,wr,wi,s,info);
|
|
//--- get result
|
|
result=info==0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Eigen values and vectors
|
|
ToUpperHessenberg(a,n,tau);
|
|
//--- function call
|
|
UnpackQFromUpperHessenberg(a,n,tau,s);
|
|
//--- function call
|
|
CHsSchur::InternalSchurDecomposition(a,n,1,1,wr,wi,s,info);
|
|
//--- get result
|
|
result=info==0;
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- check
|
|
if(vneeded==1 || vneeded==3)
|
|
{
|
|
vr.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
vr[i].Set(i_,s[i][i_]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(vneeded==2 || vneeded==3)
|
|
{
|
|
vl.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
vl[i].Set(i_,s[i][i_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
InternalTREVC(a,n,vneeded,1,sel,vl,vr,m,info);
|
|
//--- get result
|
|
result=info==0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Upper Hessenberg form |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::ToUpperHessenberg(CMatrixDouble &a,const int n,double &tau[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ip1=0;
|
|
int nmi=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double t[];
|
|
double work[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(tau,n);
|
|
ArrayResizeAL(t,n+1);
|
|
ArrayResizeAL(work,n+1);
|
|
//--- calculations
|
|
for(i=1;i<n;i++)
|
|
{
|
|
//--- Compute elementary reflector H(i) to annihilate A(i+2:ihi,i)
|
|
ip1=i+1;
|
|
nmi=n-i;
|
|
i1_=ip1-1;
|
|
for(i_=1;i_<=nmi;i_++)
|
|
t[i_]=a[i_+i1_][i];
|
|
//--- function call
|
|
CReflections::GenerateReflection(t,nmi,v);
|
|
i1_=1-ip1;
|
|
for(i_=ip1;i_<=n;i_++)
|
|
a[i_].Set(i,t[i_+i1_]);
|
|
tau[i]=v;
|
|
t[1]=1;
|
|
//--- Apply H(i) to A(1:ihi,i+1:ihi) from the right
|
|
CReflections::ApplyReflectionFromTheRight(a,v,t,1,n,i+1,n,work);
|
|
//--- Apply H(i) to A(i+1:ihi,i+1:n) from the left
|
|
CReflections::ApplyReflectionFromTheLeft(a,v,t,i+1,n,i+1,n,work);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpack Q from the matrix of the upper Hessenberg form |
|
|
//+------------------------------------------------------------------+
|
|
static void CEigenVDetect::UnpackQFromUpperHessenberg(CMatrixDouble &a,const int n,
|
|
double &tau[],CMatrixDouble &q)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int ip1=0;
|
|
int nmi=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double v[];
|
|
double work[];
|
|
//--- check
|
|
if(n==0)
|
|
return;
|
|
//--- allocation
|
|
q.Resize(n+1,n+1);
|
|
ArrayResizeAL(v,n+1);
|
|
ArrayResizeAL(work,n+1);
|
|
//--- identity matrix
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
q[i].Set(j,1);
|
|
else
|
|
q[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- unpack Q
|
|
for(i=1;i<n;i++)
|
|
{
|
|
//--- Apply H(i)
|
|
ip1=i+1;
|
|
nmi=n-i;
|
|
i1_=ip1-1;
|
|
for(i_=1;i_<=nmi;i_++)
|
|
v[i_]=a[i_+i1_][i];
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(q,tau[i],v,1,n,i+1,n,work);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random matrix generation |
|
|
//+------------------------------------------------------------------+
|
|
class CMatGen
|
|
{
|
|
public:
|
|
CMatGen(void);
|
|
~CMatGen(void);
|
|
//--- public methods
|
|
static void RMatrixRndOrthogonal(const int n,CMatrixDouble &a);
|
|
static void RMatrixRndCond(const int n,const double c,CMatrixDouble &a);
|
|
static void CMatrixRndOrthogonal(const int n,CMatrixComplex &a);
|
|
static void CMatrixRndCond(const int n,const double c,CMatrixComplex &a);
|
|
static void SMatrixRndCond(const int n,const double c,CMatrixDouble &a);
|
|
static void SPDMatrixRndCond(const int n,const double c,CMatrixDouble &a);
|
|
static void HMatrixRndCond(const int n,const double c,CMatrixComplex &a);
|
|
static void HPDMatrixRndCond(const int n,const double c,CMatrixComplex &a);
|
|
static void RMatrixRndOrthogonalFromTheRight(CMatrixDouble &a,const int m,const int n);
|
|
static void RMatrixRndOrthogonalFromTheLeft(CMatrixDouble &a,const int m,const int n);
|
|
static void CMatrixRndOrthogonalFromTheRight(CMatrixComplex &a,const int m,const int n);
|
|
static void CMatrixRndOrthogonalFromTheLeft(CMatrixComplex &a,const int m,const int n);
|
|
static void SMatrixRndMultiply(CMatrixDouble &a,const int n);
|
|
static void HMatrixRndMultiply(CMatrixComplex &a,const int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMatGen::CMatGen(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMatGen::~CMatGen(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of a random uniformly distributed (Haar) orthogonal |
|
|
//| matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size, N>=1 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - orthogonal NxN matrix, array[0..N-1,0..N-1] |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::RMatrixRndOrthogonal(const int n,CMatrixDouble &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(j,1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- get result
|
|
RMatrixRndOrthogonalFromTheRight(a,n,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of random NxN matrix with given condition number and |
|
|
//| norm2(A)=1 |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size |
|
|
//| C - condition number (in 2-norm) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - random matrix with norm2(A)=1 and cond(A)=C |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::RMatrixRndCond(const int n,const double c,CMatrixDouble &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1&&c>=1.0,__FUNCTION__+": N<1 or C<1!"))
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
a[0].Set(0,2*CMath::RandomInteger(2)-1);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- initialization
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change a
|
|
a[0].Set(0,MathExp(l1));
|
|
for(i=1;i<=n-2;i++)
|
|
a[i].Set(i,MathExp(CMath::RandomReal()*(l2-l1)+l1));
|
|
a[n-1].Set(n-1,MathExp(l2));
|
|
//--- function call
|
|
RMatrixRndOrthogonalFromTheLeft(a,n,n);
|
|
//--- function call
|
|
RMatrixRndOrthogonalFromTheRight(a,n,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of a random Haar distributed orthogonal complex matrix|
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size, N>=1 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - orthogonal NxN matrix, array[0..N-1,0..N-1] |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::CMatrixRndOrthogonal(const int n,CMatrixComplex &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(j,1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- get result
|
|
CMatrixRndOrthogonalFromTheRight(a,n,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of random NxN complex matrix with given condition |
|
|
//| number C and norm2(A)=1 |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size |
|
|
//| C - condition number (in 2-norm) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - random matrix with norm2(A)=1 and cond(A)=C |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::CMatrixRndCond(const int n,const double c,CMatrixComplex &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
complex v=0;
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1&&c>=1.0,__FUNCTION__+": N<1 or C<1!"))
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- function call
|
|
CHighQualityRand::HQRndUnit2(state,v.re,v.im);
|
|
a[0].Set(0,v);
|
|
return;
|
|
}
|
|
//--- initialization
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
a[0].Set(0,MathExp(l1));
|
|
for(i=1;i<=n-2;i++)
|
|
a[i].Set(i,MathExp(CMath::RandomReal()*(l2-l1)+l1));
|
|
a[n-1].Set(n-1,MathExp(l2));
|
|
//--- function call
|
|
CMatrixRndOrthogonalFromTheLeft(a,n,n);
|
|
//--- function call
|
|
CMatrixRndOrthogonalFromTheRight(a,n,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of random NxN symmetric matrix with given condition |
|
|
//| number and norm2(A)=1 |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size |
|
|
//| C - condition number (in 2-norm) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - random matrix with norm2(A)=1 and cond(A)=C |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::SMatrixRndCond(const int n,const double c,CMatrixDouble &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1&&c>=1.0,__FUNCTION__+": N<1 or C<1!"))
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
a[0].Set(0,2*CMath::RandomInteger(2)-1);
|
|
return;
|
|
}
|
|
//--- Prepare matrix
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- initialization
|
|
a[0].Set(0,MathExp(l1));
|
|
for(i=1;i<=n-2;i++)
|
|
a[i].Set(i,(2*CMath::RandomInteger(2)-1)*MathExp(CMath::RandomReal()*(l2-l1)+l1));
|
|
a[n-1].Set(n-1,MathExp(l2));
|
|
//--- Multiply
|
|
SMatrixRndMultiply(a,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of random NxN symmetric positive definite matrix with |
|
|
//| given condition number and norm2(A)=1 |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size |
|
|
//| C - condition number (in 2-norm) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - random SPD matrix with norm2(A)=1 and cond(A)=C |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::SPDMatrixRndCond(const int n,const double c,CMatrixDouble &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
//--- check
|
|
if(n<=0 || c<1.0)
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
a[0].Set(0,1);
|
|
return;
|
|
}
|
|
//--- Prepare matrix
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- initialization
|
|
a[0].Set(0,MathExp(l1));
|
|
for(i=1;i<=n-2;i++)
|
|
a[i].Set(i,MathExp(CMath::RandomReal()*(l2-l1)+l1));
|
|
a[n-1].Set(n-1,MathExp(l2));
|
|
//--- Multiply
|
|
SMatrixRndMultiply(a,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of random NxN Hermitian matrix with given condition |
|
|
//| number and norm2(A)=1 |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size |
|
|
//| C - condition number (in 2-norm) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - random matrix with norm2(A)=1 and cond(A)=C |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::HMatrixRndCond(const int n,const double c,CMatrixComplex &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1&&c>=1.0,__FUNCTION__+": N<1 or C<1!"))
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
a[0].Set(0,2*CMath::RandomInteger(2)-1);
|
|
return;
|
|
}
|
|
//--- Prepare matrix
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- initialization
|
|
a[0].Set(0,MathExp(l1));
|
|
for(i=1;i<=n-2;i++)
|
|
a[i].Set(i,(2*CMath::RandomInteger(2)-1)*MathExp(CMath::RandomReal()*(l2-l1)+l1));
|
|
a[n-1].Set(n-1,MathExp(l2));
|
|
//--- Multiply
|
|
HMatrixRndMultiply(a,n);
|
|
//--- post-process to ensure that matrix diagonal is real
|
|
for(i=0;i<n;i++)
|
|
a[i].SetIm(i,0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generation of random NxN Hermitian positive definite matrix with |
|
|
//| given condition number and norm2(A)=1 |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - matrix size |
|
|
//| C - condition number (in 2-norm) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - random HPD matrix with norm2(A)=1 and cond(A)=C |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::HPDMatrixRndCond(const int n,const double c,CMatrixComplex &a)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
//--- check
|
|
if(n<=0 || c<1.0)
|
|
return;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
a[0].Set(0,1);
|
|
return;
|
|
}
|
|
//--- Prepare matrix
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- initialization
|
|
a[0].Set(0,MathExp(l1));
|
|
for(i=1;i<=n-2;i++)
|
|
a[i].Set(i,MathExp(CMath::RandomReal()*(l2-l1)+l1));
|
|
a[n-1].Set(n-1,MathExp(l2));
|
|
//--- Multiply
|
|
HMatrixRndMultiply(a,n);
|
|
//--- post-process to ensure that matrix diagonal is real
|
|
for(i=0;i<n;i++)
|
|
a[i].SetIm(i,0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Multiplication of MxN matrix by NxN random Haar distributed |
|
|
//| orthogonal matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix, array[0..M-1, 0..N-1] |
|
|
//| M, N- matrix size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - A*Q, where Q is random NxN orthogonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::RMatrixRndOrthogonalFromTheRight(CMatrixDouble &a,const int m,
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
double tau=0;
|
|
double lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
double u1=0;
|
|
double u2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double v[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1 && m>=1,__FUNCTION__+": N<1 or M<1!"))
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
tau=2*CMath::RandomInteger(2)-1;
|
|
for(i=0;i<m;i++)
|
|
a[i].Set(0,a[i][0]*tau);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General case.
|
|
//--- First pass.
|
|
ArrayResizeAL(w,m);
|
|
ArrayResizeAL(v,n+1);
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
for(s=2;s<=n;s++)
|
|
{
|
|
//--- Prepare random normal v
|
|
do
|
|
{
|
|
i=1;
|
|
while(i<=s)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,u1,u2);
|
|
v[i]=u1;
|
|
//--- check
|
|
if(i+1<=s)
|
|
v[i+1]=u2;
|
|
i=i+2;
|
|
}
|
|
//--- change values
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*v[i_];
|
|
}
|
|
while(lambdav==0.0);
|
|
//--- Prepare and apply reflection
|
|
CReflections::GenerateReflection(v,s,tau);
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(a,tau,v,0,m-1,n-s,n-1,w);
|
|
}
|
|
//--- Second pass.
|
|
for(i=0;i<n;i++)
|
|
{
|
|
tau=2*CMath::RandomInteger(2)-1;
|
|
for(i_=0;i_<m;i_++)
|
|
a[i_].Set(i,tau*a[i_][i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Multiplication of MxN matrix by MxM random Haar distributed |
|
|
//| orthogonal matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix, array[0..M-1, 0..N-1] |
|
|
//| M, N- matrix size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - Q*A, where Q is random MxM orthogonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::RMatrixRndOrthogonalFromTheLeft(CMatrixDouble &a,const int m,
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
double tau=0;
|
|
double lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
int j=0;
|
|
double u1=0;
|
|
double u2=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double v[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1 && m>=1,__FUNCTION__+": N<1 or M<1!"))
|
|
return;
|
|
//--- check
|
|
if(m==1)
|
|
{
|
|
tau=2*CMath::RandomInteger(2)-1;
|
|
for(j=0;j<n;j++)
|
|
a[0].Set(j,a[0][j]*tau);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General case.
|
|
//--- First pass.
|
|
ArrayResizeAL(w,n);
|
|
ArrayResizeAL(v,m+1);
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
for(s=2;s<=m;s++)
|
|
{
|
|
//--- Prepare random normal v
|
|
do
|
|
{
|
|
i=1;
|
|
while(i<=s)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,u1,u2);
|
|
v[i]=u1;
|
|
//--- check
|
|
if(i+1<=s)
|
|
v[i+1]=u2;
|
|
i=i+2;
|
|
}
|
|
//--- change values
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*v[i_];
|
|
}
|
|
while(lambdav==0.0);
|
|
//--- Prepare random normal v
|
|
CReflections::GenerateReflection(v,s,tau);
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(a,tau,v,m-s,m-1,0,n-1,w);
|
|
}
|
|
//--- Second pass.
|
|
for(i=0;i<m;i++)
|
|
{
|
|
tau=2*CMath::RandomInteger(2)-1;
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,tau*a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Multiplication of MxN complex matrix by NxN random Haar |
|
|
//| distributed complex orthogonal matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix, array[0..M-1, 0..N-1] |
|
|
//| M, N- matrix size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - A*Q, where Q is random NxN orthogonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::CMatrixRndOrthogonalFromTheRight(CMatrixComplex &a,const int m,const int n)
|
|
{
|
|
//--- create variables
|
|
complex zero=0;
|
|
complex lambdav=0;
|
|
complex tau=0;
|
|
int s=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex w[];
|
|
complex v[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1 && m>=1,__FUNCTION__+": N<1 or M<1!"))
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- function call
|
|
CHighQualityRand::HQRndUnit2(state,tau.re,tau.im);
|
|
for(i=0;i<m;i++)
|
|
a[i].Set(0,a[i][0]*tau);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General case.
|
|
//--- First pass.
|
|
ArrayResizeAL(w,m);
|
|
ArrayResizeAL(v,n+1);
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
for(s=2;s<=n;s++)
|
|
{
|
|
//--- Prepare random normal v
|
|
do
|
|
{
|
|
for(i=1;i<=s;i++)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,tau.re,tau.im);
|
|
v[i]=tau;
|
|
}
|
|
//--- change values
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*CMath::Conj(v[i_]);
|
|
}
|
|
while(lambdav==zero);
|
|
//--- Prepare and apply reflection
|
|
CComplexReflections::ComplexGenerateReflection(v,s,tau);
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(a,tau,v,0,m-1,n-s,n-1,w);
|
|
}
|
|
//--- Second pass.
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndUnit2(state,tau.re,tau.im);
|
|
for(i_=0;i_<m;i_++)
|
|
a[i_].Set(i,tau*a[i_][i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Multiplication of MxN complex matrix by MxM random Haar |
|
|
//| distributed complex orthogonal matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix, array[0..M-1, 0..N-1] |
|
|
//| M, N- matrix size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - Q*A, where Q is random MxM orthogonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::CMatrixRndOrthogonalFromTheLeft(CMatrixComplex &a,const int m,
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
complex zero=0;
|
|
complex tau=0;
|
|
complex lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex w[];
|
|
complex v[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1 && m>=1,__FUNCTION__+": N<1 or M<1!"))
|
|
return;
|
|
//--- check
|
|
if(m==1)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- function call
|
|
CHighQualityRand::HQRndUnit2(state,tau.re,tau.im);
|
|
for(j=0;j<n;j++)
|
|
a[0].Set(j,a[0][j]*tau);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General case.
|
|
//--- First pass.
|
|
ArrayResizeAL(w,n);
|
|
ArrayResizeAL(v,m+1);
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
for(s=2;s<=m;s++)
|
|
{
|
|
//--- Prepare random normal v
|
|
do
|
|
{
|
|
for(i=1;i<=s;i++)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,tau.re,tau.im);
|
|
v[i]=tau;
|
|
}
|
|
//--- change values
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*CMath::Conj(v[i_]);
|
|
}
|
|
while(lambdav==zero);
|
|
//--- Prepare and apply reflection
|
|
CComplexReflections::ComplexGenerateReflection(v,s,tau);
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(a,tau,v,m-s,m-1,0,n-1,w);
|
|
}
|
|
//--- Second pass.
|
|
for(i=0;i<m;i++)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndUnit2(state,tau.re,tau.im);
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,tau*a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Symmetric multiplication of NxN matrix by random Haar |
|
|
//| distributed orthogonal matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix, array[0..N-1, 0..N-1] |
|
|
//| N - matrix size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - Q'*A*Q, where Q is random NxN orthogonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::SMatrixRndMultiply(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create variables
|
|
double tau=0;
|
|
double lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
double u1=0;
|
|
double u2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double v[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- General case.
|
|
ArrayResizeAL(w,n);
|
|
ArrayResizeAL(v,n+1);
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
for(s=2;s<=n;s++)
|
|
{
|
|
//--- Prepare random normal v
|
|
do
|
|
{
|
|
i=1;
|
|
while(i<=s)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,u1,u2);
|
|
v[i]=u1;
|
|
//--- check
|
|
if(i+1<=s)
|
|
v[i+1]=u2;
|
|
i=i+2;
|
|
}
|
|
//--- change values
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*v[i_];
|
|
}
|
|
while(lambdav==0.0);
|
|
//--- Prepare and apply reflection
|
|
CReflections::GenerateReflection(v,s,tau);
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(a,tau,v,0,n-1,n-s,n-1,w);
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(a,tau,v,n-s,n-1,0,n-1,w);
|
|
}
|
|
//--- Second pass.
|
|
for(i=0;i<n;i++)
|
|
{
|
|
tau=2*CMath::RandomInteger(2)-1;
|
|
for(i_=0;i_<n;i_++)
|
|
a[i_].Set(i,tau*a[i_][i]);
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,tau*a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Hermitian multiplication of NxN matrix by random Haar distributed|
|
|
//| complex orthogonal matrix |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix, array[0..N-1, 0..N-1] |
|
|
//| N - matrix size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - Q^H*A*Q, where Q is random NxN orthogonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatGen::HMatrixRndMultiply(CMatrixComplex &a,const int n)
|
|
{
|
|
//--- create variables
|
|
complex zero=0;
|
|
complex tau=0;
|
|
complex lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex w[];
|
|
complex v[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- General case.
|
|
ArrayResizeAL(w,n);
|
|
ArrayResizeAL(v,n+1);
|
|
//--- function call
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
for(s=2;s<=n;s++)
|
|
{
|
|
//--- Prepare random normal v
|
|
do
|
|
{
|
|
for(i=1;i<=s;i++)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,tau.re,tau.im);
|
|
v[i]=tau;
|
|
}
|
|
//--- change values
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*CMath::Conj(v[i_]);
|
|
}
|
|
while(lambdav==zero);
|
|
//--- Prepare and apply reflection
|
|
CComplexReflections::ComplexGenerateReflection(v,s,tau);
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(a,tau,v,0,n-1,n-s,n-1,w);
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(a,CMath::Conj(tau),v,n-s,n-1,0,n-1,w);
|
|
}
|
|
//--- Second pass.
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndUnit2(state,tau.re,tau.im);
|
|
for(i_=0;i_<n;i_++)
|
|
a[i_].Set(i,tau*a[i_][i]);
|
|
tau=CMath::Conj(tau);
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,tau*a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular factorizations |
|
|
//+------------------------------------------------------------------+
|
|
class CTrFac
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void CMatrixLUPRec(CMatrixComplex &a,const int offs,const int m,const int n,int &pivots[],complex &tmp[]);
|
|
static void RMatrixLUPRec(CMatrixDouble &a,const int offs,const int m,const int n,int &pivots[],double &tmp[]);
|
|
static void CMatrixPLURec(CMatrixComplex &a,const int offs,const int m,const int n,int &pivots[],complex &tmp[]);
|
|
static void RMatrixPLURec(CMatrixDouble &a,const int offs,const int m,const int n,int &pivots[],double &tmp[]);
|
|
static void CMatrixLUP2(CMatrixComplex &a,const int offs,const int m,const int n,int &pivots[],complex &tmp[]);
|
|
static void RMatrixLUP2(CMatrixDouble &a,const int offs,const int m,const int n,int &pivots[],double &tmp[]);
|
|
static void CMatrixPLU2(CMatrixComplex &a,const int offs,const int m,const int n,int &pivots[],complex &tmp[]);
|
|
static void RMatrixPLU2(CMatrixDouble &a,const int offs,const int m,const int n,int &pivots[],double &tmp[]);
|
|
static bool HPDMatrixCholeskyRec(CMatrixComplex &a,const int offs,const int n,const bool isupper,complex &tmp[]);
|
|
static bool HPDMatrixCholesky2(CMatrixComplex &aaa,const int offs,const int n,const bool isupper,complex &tmp[]);
|
|
static bool SPDMatrixCholesky2(CMatrixDouble &aaa,const int offs,const int n,const bool isupper,double &tmp[]);
|
|
public:
|
|
CTrFac(void);
|
|
~CTrFac(void);
|
|
//--- public methods
|
|
static void RMatrixLU(CMatrixDouble &a,const int m,const int n,int &pivots[]);
|
|
static void CMatrixLU(CMatrixComplex &a,const int m,const int n,int &pivots[]);
|
|
static bool HPDMatrixCholesky(CMatrixComplex &a,const int n,const bool isupper);
|
|
static bool SPDMatrixCholesky(CMatrixDouble &a,const int n,const bool isupper);
|
|
static void RMatrixLUP(CMatrixDouble &a,const int m,const int n,int &pivots[]);
|
|
static void CMatrixLUP(CMatrixComplex &a,const int m,const int n,int &pivots[]);
|
|
static void RMatrixPLU(CMatrixDouble &a,const int m,const int n,int &pivots[]);
|
|
static void CMatrixPLU(CMatrixComplex &a,const int m,const int n,int &pivots[]);
|
|
static bool SPDMatrixCholeskyRec(CMatrixDouble &a,const int offs,const int n,const bool isupper,double &tmp[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTrFac::CTrFac(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTrFac::~CTrFac(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU decomposition of a general real matrix with row pivoting |
|
|
//| A is represented as A = P*L*U, where: |
|
|
//| * L is lower unitriangular matrix |
|
|
//| * U is upper triangular matrix |
|
|
//| * P = P0*P1*...*PK, K=min(M,N)-1, |
|
|
//| Pi - permutation matrix for I and Pivots[I] |
|
|
//| This is cache-oblivous implementation of LU decomposition. |
|
|
//| It is optimized for square matrices. As for rectangular matrices:|
|
|
//| * best case - M>>N |
|
|
//| * worst case - N>>M, small M, large N, matrix does not fit in CPU|
|
|
//| cache |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - array[0..M-1, 0..N-1]. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - matrices L and U in compact form: |
|
|
//| * L is stored under main diagonal |
|
|
//| * U is stored on and above main diagonal |
|
|
//| Pivots - permutation matrix in compact form. |
|
|
//| array[0..Min(M-1,N-1)]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixLU(CMatrixDouble &a,const int m,const int n,int &pivots[])
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": incorrect M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- function call
|
|
RMatrixPLU(a,m,n,pivots);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU decomposition of a general complex matrix with row pivoting |
|
|
//| A is represented as A = P*L*U, where: |
|
|
//| * L is lower unitriangular matrix |
|
|
//| * U is upper triangular matrix |
|
|
//| * P = P0*P1*...*PK, K=min(M,N)-1, |
|
|
//| Pi - permutation matrix for I and Pivots[I] |
|
|
//| This is cache-oblivous implementation of LU decomposition. It is |
|
|
//| optimized for square matrices. As for rectangular matrices: |
|
|
//| * best case - M>>N |
|
|
//| * worst case - N>>M, small M, large N, matrix does not fit in CPU|
|
|
//| cache |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - array[0..M-1, 0..N-1]. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - matrices L and U in compact form: |
|
|
//| * L is stored under main diagonal |
|
|
//| * U is stored on and above main diagonal |
|
|
//| Pivots - permutation matrix in compact form. |
|
|
//| array[0..Min(M-1,N-1)]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixLU(CMatrixComplex &a,const int m,const int n,int &pivots[])
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": incorrect M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- function call
|
|
CMatrixPLU(a,m,n,pivots);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Cache-oblivious Cholesky decomposition |
|
|
//| The algorithm computes Cholesky decomposition of a Hermitian |
|
|
//| positive - definite matrix. The result of an algorithm is a |
|
|
//| representation of A as A=U'*U or A=L*L' (here X' detones |
|
|
//| conj(X^T)). |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - upper or lower triangle of a factorized matrix. |
|
|
//| array with elements [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - if IsUpper=True, then A contains an upper |
|
|
//| triangle of a symmetric matrix, otherwise A |
|
|
//| contains a lower one. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - the result of factorization. If IsUpper=True, |
|
|
//| then the upper triangle contains matrix U, so |
|
|
//| that A = U'*U, and the elements below the main |
|
|
//| diagonal are not modified. Similarly, if |
|
|
//| IsUpper = False. |
|
|
//| RESULT: |
|
|
//| If the matrix is positive-definite, the function returns |
|
|
//| True. Otherwise, the function returns False. Contents of A is|
|
|
//| not determined in such case. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTrFac::HPDMatrixCholesky(CMatrixComplex &a,const int n,const bool isupper)
|
|
{
|
|
//--- create array
|
|
complex tmp[];
|
|
//--- check
|
|
if(n<1)
|
|
return(false);
|
|
//--- return result
|
|
return(HPDMatrixCholeskyRec(a,0,n,isupper,tmp));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Cache-oblivious Cholesky decomposition |
|
|
//| The algorithm computes Cholesky decomposition of a symmetric |
|
|
//| positive - definite matrix. The result of an algorithm is a |
|
|
//| representation of A as A=U^T*U or A=L*L^T |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - upper or lower triangle of a factorized matrix. |
|
|
//| array with elements [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - if IsUpper=True, then A contains an upper |
|
|
//| triangle of a symmetric matrix, otherwise A |
|
|
//| contains a lower one. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - the result of factorization. If IsUpper=True, |
|
|
//| then the upper triangle contains matrix U, so |
|
|
//| that A = U^T*U, and the elements below the main |
|
|
//| diagonal are not modified. Similarly, if |
|
|
//| IsUpper = False. |
|
|
//| RESULT: |
|
|
//| If the matrix is positive-definite, the function returns |
|
|
//| True. Otherwise, the function returns False. Contents of A is|
|
|
//| not determined in such case. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTrFac::SPDMatrixCholesky(CMatrixDouble &a,const int n,const bool isupper)
|
|
{
|
|
//--- create array
|
|
double tmp[];
|
|
//--- check
|
|
if(n<1)
|
|
return(false);
|
|
//--- return result
|
|
return(SPDMatrixCholeskyRec(a,0,n,isupper,tmp));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LUP decomposition of general real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixLUP(CMatrixDouble &a,const int m,const int n,int &pivots[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double mx=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- Internal LU decomposition subroutine.
|
|
//--- Never call it directly.
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": incorrect M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- Scale matrix to avoid overflows,
|
|
//--- decompose it, then scale back.
|
|
mx=0;
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
mx=MathMax(mx,MathAbs(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=1/mx;
|
|
//--- change matrix
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,v*a[i][i_]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(pivots,MathMin(m,n));
|
|
ArrayResizeAL(tmp,2*MathMax(m,n));
|
|
//--- function call
|
|
RMatrixLUPRec(a,0,m,n,pivots,tmp);
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=mx;
|
|
//--- get result
|
|
for(i=0;i<m;i++)
|
|
for(i_=0;i_<=MathMin(i,n-1);i_++)
|
|
a[i].Set(i_,v*a[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LUP decomposition of general complex matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixLUP(CMatrixComplex &a,const int m,const int n,int &pivots[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double mx=0;
|
|
double v=0;
|
|
complex cV;
|
|
int i_=0;
|
|
//--- create array
|
|
complex tmp[];
|
|
//--- Internal LU decomposition subroutine.
|
|
//--- Never call it directly.
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": incorrect M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- Scale matrix to avoid overflows,
|
|
//--- decompose it, then scale back.
|
|
mx=0;
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
mx=MathMax(mx,CMath::AbsComplex(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=1/mx;
|
|
cV=v;
|
|
//--- change matrix
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,cV*a[i][i_]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(pivots,MathMin(m,n));
|
|
ArrayResizeAL(tmp,2*MathMax(m,n));
|
|
//--- function call
|
|
CMatrixLUPRec(a,0,m,n,pivots,tmp);
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=mx;
|
|
cV=v;
|
|
//--- get result
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(i_=0;i_<=MathMin(i,n-1);i_++)
|
|
a[i].Set(i_,cV*a[i][i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| PLU decomposition of general real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixPLU(CMatrixDouble &a,const int m,const int n,int &pivots[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double mx=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- Internal LU decomposition subroutine.
|
|
//--- Never call it directly.
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": incorrect M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,2*MathMax(m,n));
|
|
ArrayResizeAL(pivots,MathMin(m,n));
|
|
//--- Scale matrix to avoid overflows,
|
|
//--- decompose it, then scale back.
|
|
mx=0;
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
mx=MathMax(mx,MathAbs(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=1/mx;
|
|
//--- change matrix
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,v*a[i][i_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
RMatrixPLURec(a,0,m,n,pivots,tmp);
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=mx;
|
|
//--- get result
|
|
for(i=0;i<=MathMin(m,n)-1;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
a[i].Set(i_,v*a[i][i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| PLU decomposition of general complex matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixPLU(CMatrixComplex &a,const int m,const int n,int &pivots[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double mx=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex tmp[];
|
|
//--- Internal LU decomposition subroutine.
|
|
//--- Never call it directly.
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": incorrect M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,2*MathMax(m,n));
|
|
ArrayResizeAL(pivots,MathMin(m,n));
|
|
//--- Scale matrix to avoid overflows,
|
|
//--- decompose it, then scale back.
|
|
mx=0;
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
mx=MathMax(mx,CMath::AbsComplex(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=1/mx;
|
|
//--- change matrix
|
|
for(i=0;i<m;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,v*a[i][i_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatrixPLURec(a,0,m,n,pivots,tmp);
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
v=mx;
|
|
//--- get result
|
|
for(i=0;i<=MathMin(m,n)-1;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
a[i].Set(i_,v*a[i][i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recursive computational subroutine for SPDMatrixCholesky. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - matrix given by upper or lower triangle |
|
|
//| Offs - offset of diagonal block to decompose |
|
|
//| N - diagonal block size |
|
|
//| IsUpper - what half is given |
|
|
//| Tmp - temporary array; allocated by function, if its |
|
|
//| size is too small; can be reused on subsequent |
|
|
//| calls. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| A - upper (or lower) triangle contains Cholesky |
|
|
//| decomposition |
|
|
//| RESULT: |
|
|
//| True, on success |
|
|
//| False, on failure |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTrFac::SPDMatrixCholeskyRec(CMatrixDouble &a,const int offs,const int n,
|
|
const bool isupper,double &tmp[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n1=0;
|
|
int n2=0;
|
|
//--- check
|
|
if(n<1)
|
|
return(false);
|
|
//--- prepare bufer
|
|
if(CAp::Len(tmp)<2*n)
|
|
ArrayResizeAL(tmp,2*n);
|
|
//--- special cases
|
|
if(n==1)
|
|
{
|
|
//--- check
|
|
if(a[offs][offs]>0.0)
|
|
{
|
|
a[offs].Set(offs,MathSqrt(a[offs][offs]));
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
else
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
}
|
|
//--- check
|
|
if(n<=CAblas::AblasBlockSize())
|
|
{
|
|
//--- return result
|
|
return(SPDMatrixCholesky2(a,offs,n,isupper,tmp));
|
|
}
|
|
//--- general case: split task in cache-oblivious manner
|
|
result=true;
|
|
CAblas::AblasSplitLength(a,n,n1,n2);
|
|
result=SPDMatrixCholeskyRec(a,offs,n1,isupper,tmp);
|
|
//--- check
|
|
if(!result)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(n2>0)
|
|
{
|
|
if(isupper)
|
|
{
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n1,n2,a,offs,offs,isupper,false,1,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::RMatrixSyrk(n2,n1,-1.0,a,offs,offs+n1,1,1.0,a,offs+n1,offs+n1,isupper);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(n2,n1,a,offs,offs,isupper,false,1,a,offs+n1,offs);
|
|
//--- function call
|
|
CAblas::RMatrixSyrk(n2,n1,-1.0,a,offs+n1,offs,0,1.0,a,offs+n1,offs+n1,isupper);
|
|
}
|
|
result=SPDMatrixCholeskyRec(a,offs+n1,n2,isupper,tmp);
|
|
//--- check
|
|
if(!result)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recurrent complex LU subroutine. |
|
|
//| Never call it directly. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixLUPRec(CMatrixComplex &a,const int offs,const int m,
|
|
const int n,int &pivots[],complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int m1=0;
|
|
int m2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex One(1.0,0.0);
|
|
complex _One(-1.0,0.0);
|
|
//--- Kernel case
|
|
if(MathMin(m,n)<=CAblas::AblasComplexBlockSize())
|
|
{
|
|
CMatrixLUP2(a,offs,m,n,pivots,tmp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Preliminary step, make N>=M
|
|
//--- ( A1 )
|
|
//--- A = ( ), where A1 is square
|
|
//--- ( A2 )
|
|
//--- Factorize A1, update A2
|
|
if(m>n)
|
|
{
|
|
//--- function call
|
|
CMatrixLUPRec(a,offs,n,n,pivots,tmp);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
i1_=offs+n;
|
|
for(i_=0;i_<m-n;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+i];
|
|
//--- change matrix
|
|
for(i_=offs+n;i_<offs+m;i_++)
|
|
a[i_].Set(offs+i,a[i_][pivots[offs+i]]);
|
|
i1_=-(offs+n);
|
|
for(i_=offs+n;i_<offs+m;i_++)
|
|
a[i_].Set(pivots[offs+i],tmp[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(m-n,n,a,offs,offs,true,true,0,a,offs+n,offs);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Non-kernel case
|
|
CAblas::AblasComplexSplitLength(a,m,m1,m2);
|
|
//--- function call
|
|
CMatrixLUPRec(a,offs,m1,n,pivots,tmp);
|
|
//--- check
|
|
if(m2>0)
|
|
{
|
|
for(i=0;i<m1;i++)
|
|
{
|
|
//--- check
|
|
if(offs+i!=pivots[offs+i])
|
|
{
|
|
i1_=offs+m1;
|
|
for(i_=0;i_<m2;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+i];
|
|
//--- change matrix
|
|
for(i_=offs+m1;i_<offs+m;i_++)
|
|
a[i_].Set(offs+i,a[i_][pivots[offs+i]]);
|
|
i1_=-(offs+m1);
|
|
for(i_=offs+m1;i_<offs+m;i_++)
|
|
a[i_].Set(pivots[offs+i],tmp[i_+i1_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(m2,m1,a,offs,offs,true,true,0,a,offs+m1,offs);
|
|
//--- function call
|
|
CAblas::CMatrixGemm(m-m1,n-m1,m1,_One,a,offs+m1,offs,0,a,offs,offs+m1,0,One,a,offs+m1,offs+m1);
|
|
//--- function call
|
|
CMatrixLUPRec(a,offs+m1,m-m1,n-m1,pivots,tmp);
|
|
for(i=0;i<m2;i++)
|
|
{
|
|
//--- check
|
|
if(offs+m1+i!=pivots[offs+m1+i])
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<m1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+m1+i];
|
|
//--- change matrix
|
|
for(i_=offs;i_<offs+m1;i_++)
|
|
a[i_].Set(offs+m1+i,a[i_][pivots[offs+m1+i]]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<offs+m1;i_++)
|
|
a[i_].Set(pivots[offs+m1+i],tmp[i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recurrent real LU subroutine. |
|
|
//| Never call it directly. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixLUPRec(CMatrixDouble &a,const int offs,const int m,
|
|
const int n,int &pivots[],double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int m1=0;
|
|
int m2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- Kernel case
|
|
if(MathMin(m,n)<=CAblas::AblasBlockSize())
|
|
{
|
|
RMatrixLUP2(a,offs,m,n,pivots,tmp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Preliminary step, make N>=M
|
|
//--- ( A1 )
|
|
//--- A = ( ), where A1 is square
|
|
//--- ( A2 )
|
|
//--- Factorize A1, update A2
|
|
if(m>n)
|
|
{
|
|
//--- function call
|
|
RMatrixLUPRec(a,offs,n,n,pivots,tmp);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(offs+i!=pivots[offs+i])
|
|
{
|
|
i1_=offs+n;
|
|
for(i_=0;i_<m-n;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+i];
|
|
//--- change matrix
|
|
for(i_=offs+n;i_<offs+m;i_++)
|
|
a[i_].Set(offs+i,a[i_][pivots[offs+i]]);
|
|
i1_=-(offs+n);
|
|
for(i_=offs+n;i_<offs+m;i_++)
|
|
a[i_].Set(pivots[offs+i],tmp[i_+i1_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(m-n,n,a,offs,offs,true,true,0,a,offs+n,offs);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Non-kernel case
|
|
CAblas::AblasSplitLength(a,m,m1,m2);
|
|
//--- function call
|
|
RMatrixLUPRec(a,offs,m1,n,pivots,tmp);
|
|
//--- check
|
|
if(m2>0)
|
|
{
|
|
for(i=0;i<m1;i++)
|
|
{
|
|
//--- check
|
|
if(offs+i!=pivots[offs+i])
|
|
{
|
|
i1_=offs+m1;
|
|
for(i_=0;i_<m2;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+i];
|
|
//--- change matrix
|
|
for(i_=offs+m1;i_<offs+m;i_++)
|
|
a[i_].Set(offs+i,a[i_][pivots[offs+i]]);
|
|
i1_=-(offs+m1);
|
|
for(i_=offs+m1;i_<offs+m;i_++)
|
|
a[i_].Set(pivots[offs+i],tmp[i_+i1_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(m2,m1,a,offs,offs,true,true,0,a,offs+m1,offs);
|
|
//--- function call
|
|
CAblas::RMatrixGemm(m-m1,n-m1,m1,-1.0,a,offs+m1,offs,0,a,offs,offs+m1,0,1.0,a,offs+m1,offs+m1);
|
|
//--- function call
|
|
RMatrixLUPRec(a,offs+m1,m-m1,n-m1,pivots,tmp);
|
|
for(i=0;i<m2;i++)
|
|
{
|
|
//--- check
|
|
if(offs+m1+i!=pivots[offs+m1+i])
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<m1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+m1+i];
|
|
//--- change matrix
|
|
for(i_=offs;i_<offs+m1;i_++)
|
|
a[i_].Set(offs+m1+i,a[i_][pivots[offs+m1+i]]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<offs+m1;i_++)
|
|
a[i_].Set(pivots[offs+m1+i],tmp[i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recurrent complex LU subroutine. |
|
|
//| Never call it directly. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixPLURec(CMatrixComplex &a,const int offs,const int m,const int n,int &pivots[],complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex One(1.0,0.0);
|
|
complex _One(-1.0,0.0);
|
|
//--- Kernel case
|
|
if(MathMin(m,n)<=CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- function call
|
|
CMatrixPLU2(a,offs,m,n,pivots,tmp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Preliminary step, make M>=N.
|
|
//--- A = (A1 A2), where A1 is square
|
|
//--- Factorize A1, update A2
|
|
if(n>m)
|
|
{
|
|
//--- function call
|
|
CMatrixPLURec(a,offs,m,m,pivots,tmp);
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=offs+m;
|
|
for(i_=0;i_<n-m;i_++)
|
|
tmp[i_]=a[offs+i][i_+i1_];
|
|
//--- change matrix
|
|
for(i_=offs+m;i_<offs+n;i_++)
|
|
a[offs+i].Set(i_,a[pivots[offs+i]][i_]);
|
|
i1_=-(offs+m);
|
|
for(i_=offs+m;i_<offs+n;i_++)
|
|
a[pivots[offs+i]].Set(i_,tmp[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(m,n-m,a,offs,offs,false,true,0,a,offs,offs+m);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Non-kernel case
|
|
CAblas::AblasComplexSplitLength(a,n,n1,n2);
|
|
//--- function call
|
|
CMatrixPLURec(a,offs,m,n1,pivots,tmp);
|
|
//--- check
|
|
if(n2>0)
|
|
{
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
//--- check
|
|
if(offs+i!=pivots[offs+i])
|
|
{
|
|
i1_=offs+n1;
|
|
for(i_=0;i_<n2;i_++)
|
|
tmp[i_]=a[offs+i][i_+i1_];
|
|
//--- change matrix
|
|
for(i_=offs+n1;i_<offs+n;i_++)
|
|
a[offs+i].Set(i_,a[pivots[offs+i]][i_]);
|
|
i1_=-(offs+n1);
|
|
for(i_=offs+n1;i_<offs+n;i_++)
|
|
a[pivots[offs+i]].Set(i_,tmp[i_+i1_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n1,n2,a,offs,offs,false,true,0,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::CMatrixGemm(m-n1,n-n1,n1,_One,a,offs+n1,offs,0,a,offs,offs+n1,0,One,a,offs+n1,offs+n1);
|
|
//--- function call
|
|
CMatrixPLURec(a,offs+n1,m-n1,n-n1,pivots,tmp);
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
//--- check
|
|
if(offs+n1+i!=pivots[offs+n1+i])
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<n1;i_++)
|
|
tmp[i_]=a[offs+n1+i][i_+i1_];
|
|
//--- change matrix
|
|
for(i_=offs;i_<offs+n1;i_++)
|
|
a[offs+n1+i].Set(i_,a[pivots[offs+n1+i]][i_]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<offs+n1;i_++)
|
|
|
|
a[pivots[offs+n1+i]].Set(i_,tmp[i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recurrent real LU subroutine. |
|
|
//| Never call it directly. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixPLURec(CMatrixDouble &a,const int offs,const int m,
|
|
const int n,int &pivots[],double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- Kernel case
|
|
if(MathMin(m,n)<=CAblas::AblasBlockSize())
|
|
{
|
|
//--- function call
|
|
RMatrixPLU2(a,offs,m,n,pivots,tmp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Preliminary step, make M>=N.
|
|
//--- A = (A1 A2), where A1 is square
|
|
//--- Factorize A1, update A2
|
|
if(n>m)
|
|
{
|
|
//--- function call
|
|
RMatrixPLURec(a,offs,m,m,pivots,tmp);
|
|
for(i=0;i<m;i++)
|
|
{
|
|
i1_=offs+m;
|
|
for(i_=0;i_<n-m;i_++)
|
|
tmp[i_]=a[offs+i][i_+i1_];
|
|
//--- change matrix
|
|
for(i_=offs+m;i_<offs+n;i_++)
|
|
a[offs+i].Set(i_,a[pivots[offs+i]][i_]);
|
|
i1_=-(offs+m);
|
|
for(i_=offs+m;i_<offs+n;i_++)
|
|
a[pivots[offs+i]].Set(i_,tmp[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(m,n-m,a,offs,offs,false,true,0,a,offs,offs+m);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Non-kernel case
|
|
CAblas::AblasSplitLength(a,n,n1,n2);
|
|
//--- function call
|
|
RMatrixPLURec(a,offs,m,n1,pivots,tmp);
|
|
//--- check
|
|
if(n2>0)
|
|
{
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
//--- check
|
|
if(offs+i!=pivots[offs+i])
|
|
{
|
|
i1_=offs+n1;
|
|
for(i_=0;i_<n2;i_++)
|
|
tmp[i_]=a[offs+i][i_+i1_];
|
|
//--- change matrix
|
|
for(i_=offs+n1;i_<offs+n;i_++)
|
|
a[offs+i].Set(i_,a[pivots[offs+i]][i_]);
|
|
i1_=-(offs+n1);
|
|
for(i_=offs+n1;i_<offs+n;i_++)
|
|
a[pivots[offs+i]].Set(i_,tmp[i_+i1_]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n1,n2,a,offs,offs,false,true,0,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::RMatrixGemm(m-n1,n-n1,n1,-1.0,a,offs+n1,offs,0,a,offs,offs+n1,0,1.0,a,offs+n1,offs+n1);
|
|
//--- function call
|
|
RMatrixPLURec(a,offs+n1,m-n1,n-n1,pivots,tmp);
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
//--- check
|
|
if(offs+n1+i!=pivots[offs+n1+i])
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<n1;i_++)
|
|
tmp[i_]=a[offs+n1+i][i_+i1_];
|
|
//--- change matrix
|
|
for(i_=offs;i_<offs+n1;i_++)
|
|
a[offs+n1+i].Set(i_,a[pivots[offs+n1+i]][i_]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<offs+n1;i_++)
|
|
a[pivots[offs+n1+i]].Set(i_,tmp[i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex LUP kernel |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixLUP2(CMatrixComplex &a,const int offs,const int m,const int n,int &pivots[],complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
complex s=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
complex zero=0;
|
|
complex One(1.0,0.0);
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return;
|
|
//--- main cycle
|
|
for(j=0;j<=MathMin(m-1,n-1);j++)
|
|
{
|
|
//--- Find pivot, swap columns
|
|
jp=j;
|
|
for(i=j+1;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::AbsComplex(a[offs+j][offs+i])>CMath::AbsComplex(a[offs+j][offs+jp]))
|
|
jp=i;
|
|
}
|
|
pivots[offs+j]=offs+jp;
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<m;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
//--- change matrix
|
|
for(i_=offs;i_<offs+m;i_++)
|
|
a[i_].Set(offs+j,a[i_][offs+jp]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<offs+m;i_++)
|
|
a[i_].Set(offs+jp,tmp[i_+i1_]);
|
|
}
|
|
//--- LU decomposition of 1x(N-J) matrix
|
|
if(a[offs+j][offs+j]!=zero && j+1<=n-1)
|
|
{
|
|
s=One/a[offs+j][offs+j];
|
|
for(i_=offs+j+1;i_<offs+n;i_++)
|
|
a[offs+j].Set(i_,s*a[offs+j][i_]);
|
|
}
|
|
//--- Update trailing (M-J-1)x(N-J-1) matrix
|
|
if(j<MathMin(m-1,n-1))
|
|
{
|
|
i1_=offs+j+1;
|
|
for(i_=0;i_<m-j-1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
i1_=(offs+j+1)-(m);
|
|
for(i_=m;i_<m+n-j-1;i_++)
|
|
{
|
|
tmp[i_]=a[offs+j][i_+i1_];
|
|
tmp[i_].re=-tmp[i_].re;
|
|
tmp[i_].im=-tmp[i_].im;
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixRank1(m-j-1,n-j-1,a,offs+j+1,offs+j+1,tmp,0,tmp,m);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real LUP kernel |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixLUP2(CMatrixDouble &a,const int offs,const int m,
|
|
const int n,int &pivots[],double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double s=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return;
|
|
//--- main cycle
|
|
for(j=0;j<=MathMin(m-1,n-1);j++)
|
|
{
|
|
//--- Find pivot, swap columns
|
|
jp=j;
|
|
for(i=j+1;i<n;i++)
|
|
{
|
|
if(MathAbs(a[offs+j][offs+i])>MathAbs(a[offs+j][offs+jp]))
|
|
jp=i;
|
|
}
|
|
pivots[offs+j]=offs+jp;
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<m;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
//--- change matrix
|
|
for(i_=offs;i_<offs+m;i_++)
|
|
a[i_].Set(offs+j,a[i_][offs+jp]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<offs+m;i_++)
|
|
a[i_].Set(offs+jp,tmp[i_+i1_]);
|
|
}
|
|
//--- LU decomposition of 1x(N-J) matrix
|
|
if(a[offs+j][offs+j]!=0.0 && j+1<=n-1)
|
|
{
|
|
s=1/a[offs+j][offs+j];
|
|
for(i_=offs+j+1;i_<offs+n;i_++)
|
|
a[offs+j].Set(i_,s*a[offs+j][i_]);
|
|
}
|
|
//--- Update trailing (M-J-1)x(N-J-1) matrix
|
|
if(j<MathMin(m-1,n-1))
|
|
{
|
|
i1_=offs+j+1;
|
|
for(i_=0;i_<m-j-1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
i1_=(offs+j+1)-(m);
|
|
for(i_=m;i_<m+n-j-1;i_++)
|
|
tmp[i_]=-a[offs+j][i_+i1_];
|
|
//--- function call
|
|
CAblas::RMatrixRank1(m-j-1,n-j-1,a,offs+j+1,offs+j+1,tmp,0,tmp,m);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex PLU kernel |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| June 30,1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::CMatrixPLU2(CMatrixComplex &a,const int offs,const int m,
|
|
const int n,int &pivots[],complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
complex s=0;
|
|
complex zero=0;
|
|
complex One(1.0,0.0);
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return;
|
|
for(j=0;j<=MathMin(m-1,n-1);j++)
|
|
{
|
|
//--- Find pivot and test for singularity.
|
|
jp=j;
|
|
for(i=j+1;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::AbsComplex(a[offs+i][offs+j])>CMath::AbsComplex(a[offs+jp][offs+j]))
|
|
jp=i;
|
|
}
|
|
pivots[offs+j]=offs+jp;
|
|
if(a[offs+jp][offs+j]!=zero)
|
|
{
|
|
//--- Apply the interchange to rows
|
|
if(jp!=j)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
s=a[offs+j][offs+i];
|
|
a[offs+j].Set(offs+i,a[offs+jp][offs+i]);
|
|
a[offs+jp].Set(offs+i,s);
|
|
}
|
|
}
|
|
//--- Compute elements J+1:M of J-th column.
|
|
if(j+1<=m-1)
|
|
{
|
|
s=One/a[offs+j][offs+j];
|
|
for(i_=offs+j+1;i_<offs+m;i_++)
|
|
a[i_].Set(offs+j,s*a[i_][offs+j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(j<MathMin(m,n)-1)
|
|
{
|
|
//--- Update trailing submatrix.
|
|
i1_=offs+j+1;
|
|
for(i_=0;i_<m-j-1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
i1_=(offs+j+1)-(m);
|
|
for(i_=m;i_<m+n-j-1;i_++)
|
|
{
|
|
tmp[i_]=a[offs+j][i_+i1_];
|
|
tmp[i_].re=-tmp[i_].re;
|
|
tmp[i_].im=-tmp[i_].im;
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixRank1(m-j-1,n-j-1,a,offs+j+1,offs+j+1,tmp,0,tmp,m);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real PLU kernel |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| June 30,1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTrFac::RMatrixPLU2(CMatrixDouble &a,const int offs,const int m,
|
|
const int n,int &pivots[],double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double s=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return;
|
|
for(j=0;j<=MathMin(m-1,n-1);j++)
|
|
{
|
|
//--- Find pivot and test for singularity.
|
|
jp=j;
|
|
for(i=j+1;i<m;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(a[offs+i][offs+j])>MathAbs(a[offs+jp][offs+j]))
|
|
jp=i;
|
|
}
|
|
pivots[offs+j]=offs+jp;
|
|
//--- check
|
|
if(a[offs+jp][offs+j]!=0.0)
|
|
{
|
|
//--- Apply the interchange to rows
|
|
if(jp!=j)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
s=a[offs+j][offs+i];
|
|
a[offs+j].Set(offs+i,a[offs+jp][offs+i]);
|
|
a[offs+jp].Set(offs+i,s);
|
|
}
|
|
}
|
|
//--- Compute elements J+1:M of J-th column.
|
|
if(j+1<=m-1)
|
|
{
|
|
s=1/a[offs+j][offs+j];
|
|
for(i_=offs+j+1;i_<offs+m;i_++)
|
|
a[i_].Set(offs+j,s*a[i_][offs+j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(j<MathMin(m,n)-1)
|
|
{
|
|
//--- Update trailing submatrix.
|
|
i1_=offs+j+1;
|
|
for(i_=0;i_<m-j-1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
i1_=(offs+j+1)-(m);
|
|
for(i_=m;i_<m+n-j-1;i_++)
|
|
tmp[i_]=-a[offs+j][i_+i1_];
|
|
//--- function call
|
|
CAblas::RMatrixRank1(m-j-1,n-j-1,a,offs+j+1,offs+j+1,tmp,0,tmp,m);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recursive computational subroutine for HPDMatrixCholesky |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTrFac::HPDMatrixCholeskyRec(CMatrixComplex &a,const int offs,const int n,
|
|
const bool isupper,complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n1=0;
|
|
int n2=0;
|
|
//--- check
|
|
if(n<1)
|
|
return(false);
|
|
//--- prepare bufer
|
|
if(CAp::Len(tmp)<2*n)
|
|
ArrayResizeAL(tmp,2*n);
|
|
//--- special cases
|
|
if(n==1)
|
|
{
|
|
//--- check
|
|
if(a[offs][offs].re>0.0)
|
|
{
|
|
a[offs].Set(offs,MathSqrt(a[offs][offs].re));
|
|
result=true;
|
|
}
|
|
else
|
|
{
|
|
result=false;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(n<=CAblas::AblasComplexBlockSize())
|
|
{
|
|
result=HPDMatrixCholesky2(a,offs,n,isupper,tmp);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- general case: split task in cache-oblivious manner
|
|
result=true;
|
|
//--- function call
|
|
CAblas::AblasComplexSplitLength(a,n,n1,n2);
|
|
result=HPDMatrixCholeskyRec(a,offs,n1,isupper,tmp);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- check
|
|
if(n2>0)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n1,n2,a,offs,offs,isupper,false,2,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::CMatrixSyrk(n2,n1,-1.0,a,offs,offs+n1,2,1.0,a,offs+n1,offs+n1,isupper);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(n2,n1,a,offs,offs,isupper,false,2,a,offs+n1,offs);
|
|
//--- function call
|
|
CAblas::CMatrixSyrk(n2,n1,-1.0,a,offs+n1,offs,0,1.0,a,offs+n1,offs+n1,isupper);
|
|
}
|
|
result=HPDMatrixCholeskyRec(a,offs+n1,n2,isupper,tmp);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level-2 Hermitian Cholesky subroutine. |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| February 29,1992 |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTrFac::HPDMatrixCholesky2(CMatrixComplex &aaa,const int offs,
|
|
const int n,const bool isupper,complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double ajj=0;
|
|
complex v=0;
|
|
complex cR;
|
|
double r=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(n<0)
|
|
return(false);
|
|
//--- check
|
|
if(n==0)
|
|
return(result);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute the Cholesky factorization A = U'*U.
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- Compute U(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=offs;i_<offs+j;i_++)
|
|
v+=CMath::Conj(aaa[i_][offs+j])*aaa[i_][offs+j];
|
|
ajj=(aaa[offs+j][offs+j]-v).re;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- Compute elements J+1:N-1 of row J.
|
|
if(j<n-1)
|
|
{
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<j;i_++)
|
|
{
|
|
tmp[i_]=CMath::Conj(aaa[i_+i1_][offs+j]);
|
|
tmp[i_].re=-tmp[i_].re;
|
|
tmp[i_].im=-tmp[i_].im;
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixMVect(n-j-1,j,aaa,offs,offs+j+1,1,tmp,0,tmp,n);
|
|
i1_=(n)-(offs+j+1);
|
|
for(i_=offs+j+1;i_<offs+n;i_++)
|
|
aaa[offs+j].Set(i_,aaa[offs+j][i_]+tmp[i_+i1_]);
|
|
}
|
|
r=1/ajj;
|
|
cR=r;
|
|
//--- change matrix
|
|
for(i_=offs+j+1;i_<offs+n;i_++)
|
|
aaa[offs+j].Set(i_,cR*aaa[offs+j][i_]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the Cholesky factorization A = L*L'.
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- Compute L(J+1,J+1) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=offs;i_<offs+j;i_++)
|
|
v+=CMath::Conj(aaa[offs+j][i_])*aaa[offs+j][i_];
|
|
ajj=(aaa[offs+j][offs+j]-v).re;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- Compute elements J+1:N of column J.
|
|
if(j<n-1)
|
|
{
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<j;i_++)
|
|
tmp[i_]=CMath::Conj(aaa[offs+j][i_+i1_]);
|
|
//--- function call
|
|
CAblas::CMatrixMVect(n-j-1,j,aaa,offs+j+1,offs,0,tmp,0,tmp,n);
|
|
for(i=0;i<n-j-1;i++)
|
|
aaa[offs+j+1+i].Set(offs+j,(aaa[offs+j+1+i][offs+j]-tmp[n+i])/ajj);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n-j-1;i++)
|
|
aaa[offs+j+1+i].Set(offs+j,aaa[offs+j+1+i][offs+j]/ajj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Level-2 Cholesky subroutine |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| February 29, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTrFac::SPDMatrixCholesky2(CMatrixDouble &aaa,const int offs,const int n,
|
|
const bool isupper,double &tmp[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double ajj=0;
|
|
double v=0;
|
|
double r=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(n<0)
|
|
return(false);
|
|
//--- check
|
|
if(n==0)
|
|
return(result);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute the Cholesky factorization A = U'*U.
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- Compute U(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=offs;i_<offs+j;i_++)
|
|
v+=aaa[i_][offs+j]*aaa[i_][offs+j];
|
|
ajj=aaa[offs+j][offs+j]-v;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
result=false;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- Compute elements J+1:N-1 of row J.
|
|
if(j<n-1)
|
|
{
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<j;i_++)
|
|
tmp[i_]=-aaa[i_+i1_][offs+j];
|
|
//--- function call
|
|
CAblas::RMatrixMVect(n-j-1,j,aaa,offs,offs+j+1,1,tmp,0,tmp,n);
|
|
i1_=(n)-(offs+j+1);
|
|
for(i_=offs+j+1;i_<offs+n;i_++)
|
|
aaa[offs+j].Set(i_,aaa[offs+j][i_]+tmp[i_+i1_]);
|
|
}
|
|
r=1/ajj;
|
|
//--- change matrix
|
|
for(i_=offs+j+1;i_<offs+n;i_++)
|
|
aaa[offs+j].Set(i_,r*aaa[offs+j][i_]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the Cholesky factorization A = L*L'.
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- Compute L(J+1,J+1) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=offs;i_<offs+j;i_++)
|
|
v+=aaa[offs+j][i_]*aaa[offs+j][i_];
|
|
ajj=aaa[offs+j][offs+j]-v;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
aaa[offs+j].Set(offs+j,ajj);
|
|
//--- Compute elements J+1:N of column J.
|
|
if(j<n-1)
|
|
{
|
|
//--- check
|
|
if(j>0)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<j;i_++)
|
|
tmp[i_]=aaa[offs+j][i_+i1_];
|
|
//--- function call
|
|
CAblas::RMatrixMVect(n-j-1,j,aaa,offs+j+1,offs,0,tmp,0,tmp,n);
|
|
for(i=0;i<n-j-1;i++)
|
|
aaa[offs+j+1+i].Set(offs+j,(aaa[offs+j+1+i][offs+j]-tmp[n+i])/ajj);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n-j-1;i++)
|
|
aaa[offs+j+1+i].Set(offs+j,aaa[offs+j+1+i][offs+j]/ajj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of a matrix condition number |
|
|
//+------------------------------------------------------------------+
|
|
class CRCond
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixRCondTrInternal(CMatrixDouble &a,const int n,const bool isupper,const bool isunit,const bool onenorm,double anorm,double &rc);
|
|
static void CMatrixRCondTrInternal(CMatrixComplex &a,const int n,const bool isupper,const bool isunit,const bool onenorm,double anorm,double &rc);
|
|
static void SPDMatrixRCondCholeskyInternal(CMatrixDouble &cha,const int n,const bool isupper,const bool isnormprovided,double anorm,double &rc);
|
|
static void HPDMatrixRCondCholeskyInternal(CMatrixComplex &cha,const int n,const bool isupper,const bool isnormprovided,double anorm,double &rc);
|
|
static void RMatrixRCondLUInternal(CMatrixDouble &lua,const int n,const bool onenorm,const bool isanormprovided,double anorm,double &rc);
|
|
static void CMatrixRCondLUInternal(CMatrixComplex &lua,const int n,const bool onenorm,const bool isanormprovided,double anorm,double &rc);
|
|
static void RMatrixEstimateNorm(const int n,double &v[],double &x[],int &isgn[],double &est,int &kase);
|
|
static void CMatrixEstimateNorm(const int n,complex &v[],complex &x[],double &est,int &kase,int &isave[],double &rsave[]);
|
|
static double InternalComplexRCondScSum1(complex &x[],const int n);
|
|
static int InternalComplexRCondIcMax1(complex &x[],const int n);
|
|
static void InternalComplexRCondSaveAll(int &isave[],double &rsave[],int &i,int &iter,int &j,int &jlast,int &jump,double &absxi,double &altsgn,double &estold,double &temp);
|
|
static void InternalComplexRCondLoadAll(int &isave[],double &rsave[],int &i,int &iter,int &j,int &jlast,int &jump,double &absxi,double &altsgn,double &estold,double &temp);
|
|
public:
|
|
//--- public methods
|
|
static double RMatrixRCond1(CMatrixDouble &ca,const int n);
|
|
static double RMatrixRCondInf(CMatrixDouble &ca,const int n);
|
|
static double SPDMatrixRCond(CMatrixDouble &ca,const int n,const bool isupper);
|
|
static double RMatrixTrRCond1(CMatrixDouble &a,const int n,const bool isupper,const bool isunit);
|
|
static double RMatrixTrRCondInf(CMatrixDouble &a,const int n,const bool isupper,const bool isunit);
|
|
static double RMatrixLURCond1(CMatrixDouble &lua,const int n);
|
|
static double RMatrixLURCondInf(CMatrixDouble &lua,const int n);
|
|
static double SPDMatrixCholeskyRCond(CMatrixDouble &a,const int n,const bool isupper);
|
|
static double HPDMatrixRCond(CMatrixComplex &ca,const int n,const bool isupper);
|
|
static double CMatrixRCond1(CMatrixComplex &ca,const int n);
|
|
static double CMatrixRCondInf(CMatrixComplex &ca,const int n);
|
|
static double HPDMatrixCholeskyRCond(CMatrixComplex &a,const int n,const bool isupper);
|
|
static double CMatrixLURCond1(CMatrixComplex &lua,const int n);
|
|
static double CMatrixLURCondInf(CMatrixComplex &lua,const int n);
|
|
static double CMatrixTrRCond1(CMatrixComplex &a,const int n,const bool isupper,const bool isunit);
|
|
static double CMatrixTrRCondInf(CMatrixComplex &a,const int n,const bool isupper,const bool isunit);
|
|
static double RCondThreshold(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of a matrix condition number (1-norm) |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::RMatrixRCond1(CMatrixDouble &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
//--- create arrays
|
|
int pivots[];
|
|
double t[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- fiiling array
|
|
for(i=0;i<n;i++)
|
|
t[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
t[j]=t[j]+MathAbs(a[i][j]);
|
|
}
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
nrm=MathMax(nrm,t[i]);
|
|
//--- function call
|
|
CTrFac::RMatrixLU(a,n,n,pivots);
|
|
//--- function call
|
|
RMatrixRCondLUInternal(a,n,true,true,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of a matrix condition number (infinity-norm). |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::RMatrixRCondInf(CMatrixDouble &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
//--- create array
|
|
int pivots[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=0;
|
|
for(j=0;j<n;j++)
|
|
v=v+MathAbs(a[i][j]);
|
|
nrm=MathMax(nrm,v);
|
|
}
|
|
//--- function call
|
|
CTrFac::RMatrixLU(a,n,n,pivots);
|
|
//--- function call
|
|
RMatrixRCondLUInternal(a,n,false,true,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Condition number estimate of a symmetric positive definite |
|
|
//| matrix. |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| It should be noted that 1-norm and inf-norm of condition numbers |
|
|
//| of symmetric matrices are equal, so the algorithm doesn't take |
|
|
//| into account the differences between these types of norms. |
|
|
//| Input parameters: |
|
|
//| A - symmetric positive definite matrix which is given|
|
|
//| by its upper or lower triangle depending on the |
|
|
//| value of IsUpper. Array with elements |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format. |
|
|
//| Result: |
|
|
//| 1/LowerBound(cond(A)), if matrix A is positive definite, |
|
|
//| -1, if matrix A is not positive definite, and its condition |
|
|
//| number could not be found by this algorithm. |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::SPDMatrixRCond(CMatrixDouble &ca,const int n,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- fiiling array
|
|
for(i=0;i<n;i++)
|
|
t[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- change t
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
t[i]=t[i]+MathAbs(a[i][i]);
|
|
else
|
|
{
|
|
t[i]=t[i]+MathAbs(a[i][j]);
|
|
t[j]=t[j]+MathAbs(a[i][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
nrm=MathMax(nrm,t[i]);
|
|
//--- check
|
|
if(CTrFac::SPDMatrixCholesky(a,n,isupper))
|
|
{
|
|
//--- function call
|
|
SPDMatrixRCondCholeskyInternal(a,n,isupper,true,nrm,v);
|
|
//--- get result
|
|
result=v;
|
|
}
|
|
else
|
|
result=-1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix: estimate of a condition number (1-norm) |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array[0..N-1, 0..N-1]. |
|
|
//| N - size of A. |
|
|
//| IsUpper - True, if the matrix is upper triangular. |
|
|
//| IsUnit - True, if the matrix has a unit diagonal. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::RMatrixTrRCond1(CMatrixDouble &a,const int n,
|
|
const bool isupper,const bool isunit)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
//--- create arrays
|
|
int pivots[];
|
|
double t[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- fiiling array
|
|
for(i=0;i<n;i++)
|
|
t[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
//--- change t
|
|
for(j=j1;j<=j2;j++)
|
|
t[j]=t[j]+MathAbs(a[i][j]);
|
|
//--- check
|
|
if(isunit)
|
|
t[i]=t[i]+1;
|
|
else
|
|
t[i]=t[i]+MathAbs(a[i][i]);
|
|
}
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
nrm=MathMax(nrm,t[i]);
|
|
//--- function call
|
|
RMatrixRCondTrInternal(a,n,isupper,isunit,true,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+-------------------------------------------------------------------+
|
|
//| Triangular matrix: estimate of a matrix condition number |
|
|
//| (infinity-norm). |
|
|
//| The algorithm calculates a lower bound of the condition number. In|
|
|
//| this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - True, if the matrix is upper triangular. |
|
|
//| IsUnit - True, if the matrix has a unit diagonal. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+-------------------------------------------------------------------+
|
|
static double CRCond::RMatrixTrRCondInf(CMatrixDouble &a,const int n,
|
|
const bool isupper,const bool isunit)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
//--- create array
|
|
int pivots[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
//--- change v
|
|
v=0;
|
|
for(j=j1;j<=j2;j++)
|
|
v=v+MathAbs(a[i][j]);
|
|
//--- check
|
|
if(isunit)
|
|
v=v+1;
|
|
else
|
|
v=v+MathAbs(a[i][i]);
|
|
nrm=MathMax(nrm,v);
|
|
}
|
|
//--- function call
|
|
RMatrixRCondTrInternal(a,n,isupper,isunit,false,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Condition number estimate of a Hermitian positive definite |
|
|
//| matrix. |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| It should be noted that 1-norm and inf-norm of condition numbers |
|
|
//| of symmetric matrices are equal, so the algorithm doesn't take |
|
|
//| into account the differences between these types of norms. |
|
|
//| Input parameters: |
|
|
//| A - Hermitian positive definite matrix which is given|
|
|
//| by its upper or lower triangle depending on the |
|
|
//| value of IsUpper. Array with elements |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - storage format. |
|
|
//| Result: |
|
|
//| 1/LowerBound(cond(A)), if matrix A is positive definite, |
|
|
//| -1, if matrix A is not positive definite, and its condition |
|
|
//| number could not be found by this algorithm. |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::HPDMatrixRCond(CMatrixComplex &ca,const int n,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- fiiling array
|
|
for(i=0;i<n;i++)
|
|
t[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- change t
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
t[i]=t[i]+CMath::AbsComplex(a[i][i]);
|
|
else
|
|
{
|
|
t[i]=t[i]+CMath::AbsComplex(a[i][j]);
|
|
t[j]=t[j]+CMath::AbsComplex(a[i][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
nrm=MathMax(nrm,t[i]);
|
|
//--- check
|
|
if(CTrFac::HPDMatrixCholesky(a,n,isupper))
|
|
{
|
|
//--- function call
|
|
HPDMatrixRCondCholeskyInternal(a,n,isupper,true,nrm,v);
|
|
//--- get result
|
|
result=v;
|
|
}
|
|
else
|
|
result=-1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of a matrix condition number (1-norm) |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::CMatrixRCond1(CMatrixComplex &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
//--- create arrays
|
|
double t[];
|
|
int pivots[];
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- fiiling array
|
|
for(i=0;i<n;i++)
|
|
t[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
t[j]=t[j]+CMath::AbsComplex(a[i][j]);
|
|
}
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
nrm=MathMax(nrm,t[i]);
|
|
//--- function call
|
|
CTrFac::CMatrixLU(a,n,n,pivots);
|
|
//--- function call
|
|
CMatrixRCondLUInternal(a,n,true,true,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of a matrix condition number (infinity-norm). |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::CMatrixRCondInf(CMatrixComplex &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
//--- create array
|
|
int pivots[];
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=0;
|
|
for(j=0;j<n;j++)
|
|
v=v+CMath::AbsComplex(a[i][j]);
|
|
nrm=MathMax(nrm,v);
|
|
}
|
|
//--- function call
|
|
CTrFac::CMatrixLU(a,n,n,pivots);
|
|
//--- function call
|
|
CMatrixRCondLUInternal(a,n,false,true,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of the condition number of a matrix given by its LU |
|
|
//| decomposition (1-norm) |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| LUA - LU decomposition of a matrix in compact form.|
|
|
//| Output of the RMatrixLU subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::RMatrixLURCond1(CMatrixDouble &lua,const int n)
|
|
{
|
|
//--- create a variable
|
|
double v=0;
|
|
//--- function call
|
|
RMatrixRCondLUInternal(lua,n,true,false,0,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of the condition number of a matrix given by its LU |
|
|
//| decomposition (infinity norm). |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| LUA - LU decomposition of a matrix in compact form. |
|
|
//| Output of the RMatrixLU subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::RMatrixLURCondInf(CMatrixDouble &lua,const int n)
|
|
{
|
|
//--- create a variable
|
|
double v=0;
|
|
//--- function call
|
|
RMatrixRCondLUInternal(lua,n,false,false,0,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Condition number estimate of a symmetric positive definite matrix|
|
|
//| given by Cholesky decomposition. |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| It should be noted that 1-norm and inf-norm condition numbers of |
|
|
//| symmetric matrices are equal, so the algorithm doesn't take into |
|
|
//| account the differences between these types of norms. |
|
|
//| Input parameters: |
|
|
//| CD - Cholesky decomposition of matrix A, |
|
|
//| output of SMatrixCholesky subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::SPDMatrixCholeskyRCond(CMatrixDouble &a,const int n,
|
|
const bool isupper)
|
|
{
|
|
//--- create a variable
|
|
double v=0;
|
|
//--- function call
|
|
SPDMatrixRCondCholeskyInternal(a,n,isupper,false,0,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Condition number estimate of a Hermitian positive definite matrix|
|
|
//| given by Cholesky decomposition. |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| It should be noted that 1-norm and inf-norm condition numbers of |
|
|
//| symmetric matrices are equal, so the algorithm doesn't take into |
|
|
//| account the differences between these types of norms. |
|
|
//| Input parameters: |
|
|
//| CD - Cholesky decomposition of matrix A, |
|
|
//| output of SMatrixCholesky subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::HPDMatrixCholeskyRCond(CMatrixComplex &a,const int n,
|
|
const bool isupper)
|
|
{
|
|
//--- create a variable
|
|
double v=0;
|
|
//--- function call
|
|
HPDMatrixRCondCholeskyInternal(a,n,isupper,false,0,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of the condition number of a matrix given by its LU |
|
|
//| decomposition (1-norm) |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| LUA - LU decomposition of a matrix in compact form.|
|
|
//| Output of the CMatrixLU subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::CMatrixLURCond1(CMatrixComplex &lua,const int n)
|
|
{
|
|
//--- create a variable
|
|
double v=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- function call
|
|
CMatrixRCondLUInternal(lua,n,true,false,0.0,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Estimate of the condition number of a matrix given by its LU |
|
|
//| decomposition (infinity norm). |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| LUA - LU decomposition of a matrix in compact form. |
|
|
//| Output of the CMatrixLU subroutine. |
|
|
//| N - size of matrix A. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::CMatrixLURCondInf(CMatrixComplex &lua,const int n)
|
|
{
|
|
//--- create a variable
|
|
double v=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- function call
|
|
CMatrixRCondLUInternal(lua,n,false,false,0.0,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix: estimate of a condition number (1-norm) |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array[0..N-1, 0..N-1]. |
|
|
//| N - size of A. |
|
|
//| IsUpper - True, if the matrix is upper triangular. |
|
|
//| IsUnit - True, if the matrix has a unit diagonal. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::CMatrixTrRCond1(CMatrixComplex &a,const int n,
|
|
const bool isupper,const bool isunit)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
//--- create arrays
|
|
int pivots[];
|
|
double t[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- fiiling array
|
|
for(i=0;i<n;i++)
|
|
t[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
//--- change t
|
|
for(j=j1;j<=j2;j++)
|
|
t[j]=t[j]+CMath::AbsComplex(a[i][j]);
|
|
//--- check
|
|
if(isunit)
|
|
t[i]=t[i]+1;
|
|
else
|
|
t[i]=t[i]+CMath::AbsComplex(a[i][i]);
|
|
}
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
nrm=MathMax(nrm,t[i]);
|
|
//--- function call
|
|
CMatrixRCondTrInternal(a,n,isupper,isunit,true,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix: estimate of a matrix condition number |
|
|
//| (infinity-norm). |
|
|
//| The algorithm calculates a lower bound of the condition number. |
|
|
//| In this case, the algorithm does not return a lower bound of the |
|
|
//| condition number, but an inverse number (to avoid an overflow in |
|
|
//| case of a singular matrix). |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| IsUpper - True, if the matrix is upper triangular. |
|
|
//| IsUnit - True, if the matrix has a unit diagonal. |
|
|
//| Result: 1/LowerBound(cond(A)) |
|
|
//| NOTE: |
|
|
//| if k(A) is very large, then matrix is assumed degenerate, |
|
|
//| k(A)=INF, 0.0 is returned in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::CMatrixTrRCondInf(CMatrixComplex &a,const int n,
|
|
const bool isupper,const bool isunit)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double nrm=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
//--- create array
|
|
int pivots[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- change values
|
|
nrm=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
//--- change v
|
|
v=0;
|
|
for(j=j1;j<=j2;j++)
|
|
v=v+CMath::AbsComplex(a[i][j]);
|
|
//--- check
|
|
if(isunit)
|
|
v=v+1;
|
|
else
|
|
v=v+CMath::AbsComplex(a[i][i]);
|
|
nrm=MathMax(nrm,v);
|
|
}
|
|
//--- function call
|
|
CMatrixRCondTrInternal(a,n,isupper,isunit,false,nrm,v);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Threshold for rcond: matrices with condition number beyond this |
|
|
//| threshold are considered singular. |
|
|
//| Threshold must be far enough from underflow, at least |
|
|
//| Sqr(Threshold) must be greater than underflow. |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::RCondThreshold(void)
|
|
{
|
|
//--- return result
|
|
return(MathSqrt(MathSqrt(CMath::m_minrealnumber)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine for condition number estimation |
|
|
//| -- LAPACK routine (version 3.0) |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| February 29,1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::RMatrixRCondTrInternal(CMatrixDouble &a,const int n,
|
|
const bool isupper,const bool isunit,
|
|
const bool onenorm,double anorm,
|
|
double &rc)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int kase=0;
|
|
int kase1=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
double ainvnm=0;
|
|
double maxgrowth=0;
|
|
double s=0;
|
|
bool mupper;
|
|
bool mtrans;
|
|
bool munit;
|
|
//--- create arrays
|
|
double ex[];
|
|
double ev[];
|
|
int iwork[];
|
|
double tmp[];
|
|
//--- check
|
|
if(onenorm)
|
|
kase1=1;
|
|
else
|
|
kase1=2;
|
|
//--- initialization
|
|
rc=0;
|
|
mupper=true;
|
|
mtrans=true;
|
|
munit=true;
|
|
//--- allocation
|
|
ArrayResizeAL(iwork,n+1);
|
|
ArrayResizeAL(tmp,n);
|
|
//--- prepare parameters for triangular solver
|
|
maxgrowth=1/RCondThreshold();
|
|
s=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
//--- change s
|
|
for(j=j1;j<=j2;j++)
|
|
s=MathMax(s,MathAbs(a[i][j]));
|
|
//--- check
|
|
if(isunit)
|
|
s=MathMax(s,1);
|
|
else
|
|
s=MathMax(s,MathAbs(a[i][i]));
|
|
}
|
|
//--- check
|
|
if(s==0.0)
|
|
s=1;
|
|
s=1/s;
|
|
//--- Scale according to S
|
|
anorm=anorm*s;
|
|
//--- Quick return if possible
|
|
//--- We assume that ANORM<>0 after this block
|
|
if(anorm==0.0)
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
rc=1;
|
|
return;
|
|
}
|
|
//--- Estimate the norm of inv(A).
|
|
ainvnm=0;
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
RMatrixEstimateNorm(n,ev,ex,iwork,ainvnm,kase);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- from 1-based array to 0-based
|
|
for(i=0;i<n;i++)
|
|
ex[i]=ex[i+1];
|
|
//--- multiply by inv(A) or inv(A')
|
|
if(kase==kase1)
|
|
{
|
|
//--- multiply by inv(A)
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(a,s,n,ex,isupper,0,isunit,maxgrowth))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- multiply by inv(A')
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(a,s,n,ex,isupper,1,isunit,maxgrowth))
|
|
return;
|
|
}
|
|
//--- from 0-based array to 1-based
|
|
for(i=n-1;i>=0;i--)
|
|
ex[i+1]=ex[i];
|
|
}
|
|
//--- Compute the estimate of the reciprocal condition number.
|
|
if(ainvnm!=0.0)
|
|
{
|
|
rc=1/ainvnm;
|
|
rc=rc/anorm;
|
|
//--- check
|
|
if(rc<RCondThreshold())
|
|
rc=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Condition number estimation |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| March 31, 1993 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::CMatrixRCondTrInternal(CMatrixComplex &a,const int n,
|
|
const bool isupper,const bool isunit,
|
|
const bool onenorm,double anorm,
|
|
double &rc)
|
|
{
|
|
//--- create variables
|
|
int kase=0;
|
|
int kase1=0;
|
|
double ainvnm=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
double s=0;
|
|
double maxgrowth=0;
|
|
//--- create arrays
|
|
complex ex[];
|
|
complex cwork2[];
|
|
complex cwork3[];
|
|
complex cwork4[];
|
|
int isave[];
|
|
double rsave[];
|
|
//--- initialization
|
|
rc=0;
|
|
//--- check
|
|
if(n<=0)
|
|
return;
|
|
//--- check
|
|
if(n==0)
|
|
{
|
|
rc=1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(cwork2,n+1);
|
|
//--- prepare parameters for triangular solver
|
|
maxgrowth=1/RCondThreshold();
|
|
s=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
//--- change s
|
|
for(j=j1;j<=j2;j++)
|
|
s=MathMax(s,CMath::AbsComplex(a[i][j]));
|
|
//--- check
|
|
if(isunit)
|
|
s=MathMax(s,1);
|
|
else
|
|
s=MathMax(s,CMath::AbsComplex(a[i][i]));
|
|
}
|
|
//--- check
|
|
if(s==0.0)
|
|
s=1;
|
|
s=1/s;
|
|
//--- Scale according to S
|
|
anorm=anorm*s;
|
|
if(anorm==0.0)
|
|
return;
|
|
//--- Estimate the norm of inv(A).
|
|
ainvnm=0;
|
|
//--- check
|
|
if(onenorm)
|
|
kase1=1;
|
|
else
|
|
kase1=2;
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
CMatrixEstimateNorm(n,cwork4,ex,ainvnm,kase,isave,rsave);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- from 1-based array to 0-based
|
|
for(i=0;i<n;i++)
|
|
ex[i]=ex[i+1];
|
|
//--- multiply by inv(A) or inv(A')
|
|
if(kase==kase1)
|
|
{
|
|
//--- multiply by inv(A)
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(a,s,n,ex,isupper,0,isunit,maxgrowth))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- multiply by inv(A')
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(a,s,n,ex,isupper,2,isunit,maxgrowth))
|
|
return;
|
|
}
|
|
//--- from 0-based array to 1-based
|
|
for(i=n-1;i>=0;i--)
|
|
ex[i+1]=ex[i];
|
|
}
|
|
//--- Compute the estimate of the reciprocal condition number.
|
|
if(ainvnm!=0.0)
|
|
{
|
|
rc=1/ainvnm;
|
|
rc=rc/anorm;
|
|
//--- check
|
|
if(rc<RCondThreshold())
|
|
rc=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine for condition number estimation |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| February 29, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::SPDMatrixRCondCholeskyInternal(CMatrixDouble &cha,const int n,
|
|
const bool isupper,
|
|
const bool isnormprovided,
|
|
double anorm,double &rc)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int kase=0;
|
|
double ainvnm=0;
|
|
double sa=0;
|
|
double v=0;
|
|
double maxgrowth=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double ex[];
|
|
double ev[];
|
|
double tmp[];
|
|
int iwork[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,n);
|
|
//--- initialization
|
|
rc=0;
|
|
//--- prepare parameters for triangular solver
|
|
maxgrowth=1/RCondThreshold();
|
|
sa=0;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=i;j<n;j++)
|
|
sa=MathMax(sa,CMath::AbsComplex(cha[i][j]));
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=0;j<=i;j++)
|
|
sa=MathMax(sa,CMath::AbsComplex(cha[i][j]));
|
|
}
|
|
//--- check
|
|
if(sa==0.0)
|
|
sa=1;
|
|
sa=1/sa;
|
|
//--- Estimate the norm of A
|
|
if(!isnormprovided)
|
|
{
|
|
kase=0;
|
|
anorm=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
RMatrixEstimateNorm(n,ev,ex,iwork,anorm,kase);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Multiply by U
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=i-1;i_<n;i_++)
|
|
v+=cha[i-1][i_]*ex[i_+i1_];
|
|
ex[i]=v;
|
|
}
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=sa*ex[i_];
|
|
//--- Multiply by U'
|
|
for(i=0;i<n;i++)
|
|
tmp[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=ex[i+1];
|
|
for(i_=i;i_<n;i_++)
|
|
tmp[i_]=tmp[i_]+v*cha[i][i_];
|
|
}
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=tmp[i_+i1_];
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=sa*ex[i_];
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by L''
|
|
for(i=0;i<n;i++)
|
|
tmp[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=ex[i+1];
|
|
for(i_=0;i_<=i;i_++)
|
|
tmp[i_]=tmp[i_]+v*cha[i][i_];
|
|
}
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=tmp[i_+i1_];
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=sa*ex[i_];
|
|
//--- Multiply by L'
|
|
for(i=n;i>=1;i--)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<i;i_++)
|
|
v+=cha[i-1][i_]*ex[i_+i1_];
|
|
ex[i]=v;
|
|
}
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=sa*ex[i_];
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(anorm==0.0)
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
rc=1;
|
|
return;
|
|
}
|
|
//--- Estimate the 1-norm of inv(A).
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
RMatrixEstimateNorm(n,ev,ex,iwork,ainvnm,kase);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
for(i=0;i<n;i++)
|
|
ex[i]=ex[i+1];
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Multiply by inv(U')
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,1,false,maxgrowth))
|
|
return;
|
|
//--- Multiply by inv(U)
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,0,false,maxgrowth))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by inv(L)
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,0,false,maxgrowth))
|
|
return;
|
|
//--- Multiply by inv(L')
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,1,false,maxgrowth))
|
|
return;
|
|
}
|
|
for(i=n-1;i>=0;i--)
|
|
ex[i+1]=ex[i];
|
|
}
|
|
//--- Compute the estimate of the reciprocal condition number.
|
|
if(ainvnm!=0.0)
|
|
{
|
|
v=1/ainvnm;
|
|
rc=v/anorm;
|
|
//--- check
|
|
if(rc<RCondThreshold())
|
|
rc=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine for condition number estimation |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| February 29, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::HPDMatrixRCondCholeskyInternal(CMatrixComplex &cha,const int n,
|
|
const bool isupper,
|
|
const bool isnormprovided,
|
|
double anorm,double &rc)
|
|
{
|
|
//--- create variables
|
|
complex Csa;
|
|
int kase=0;
|
|
double ainvnm=0;
|
|
complex v=0;
|
|
int i=0;
|
|
int j=0;
|
|
double sa=0;
|
|
double maxgrowth=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
int isave[];
|
|
double rsave[];
|
|
complex ex[];
|
|
complex ev[];
|
|
complex tmp[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,n);
|
|
//--- initialization
|
|
rc=0;
|
|
//--- prepare parameters for triangular solver
|
|
maxgrowth=1/RCondThreshold();
|
|
sa=0;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=i;j<n;j++)
|
|
sa=MathMax(sa,CMath::AbsComplex(cha[i][j]));
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=0;j<=i;j++)
|
|
sa=MathMax(sa,CMath::AbsComplex(cha[i][j]));
|
|
}
|
|
//--- check
|
|
if(sa==0.0)
|
|
sa=1;
|
|
sa=1/sa;
|
|
//--- Estimate the norm of A
|
|
if(!isnormprovided)
|
|
{
|
|
anorm=0;
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
CMatrixEstimateNorm(n,ev,ex,anorm,kase,isave,rsave);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Multiply by U
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=i-1;i_<n;i_++)
|
|
v+=cha[i-1][i_]*ex[i_+i1_];
|
|
ex[i]=v;
|
|
}
|
|
for(i_=1;i_<=n;i_++)
|
|
{
|
|
Csa=sa;
|
|
ex[i_]=Csa*ex[i_];
|
|
}
|
|
//--- Multiply by U'
|
|
for(i=0;i<n;i++)
|
|
tmp[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=ex[i+1];
|
|
for(i_=i;i_<n;i_++)
|
|
tmp[i_]=tmp[i_]+v*CMath::Conj(cha[i][i_]);
|
|
}
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=tmp[i_+i1_];
|
|
for(i_=1;i_<=n;i_++)
|
|
{
|
|
Csa=sa;
|
|
ex[i_]=Csa*ex[i_];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by L''
|
|
for(i=0;i<n;i++)
|
|
tmp[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=ex[i+1];
|
|
for(i_=0;i_<=i;i_++)
|
|
tmp[i_]=tmp[i_]+v*CMath::Conj(cha[i][i_]);
|
|
}
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=tmp[i_+i1_];
|
|
for(i_=1;i_<=n;i_++)
|
|
{
|
|
Csa=sa;
|
|
ex[i_]=Csa*ex[i_];
|
|
}
|
|
//--- Multiply by L'
|
|
for(i=n;i>=1;i--)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<i;i_++)
|
|
v+=cha[i-1][i_]*ex[i_+i1_];
|
|
ex[i]=v;
|
|
}
|
|
for(i_=1;i_<=n;i_++)
|
|
{
|
|
Csa=sa;
|
|
ex[i_]=Csa*ex[i_];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Quick return if possible
|
|
//--- After this block we assume that ANORM<>0
|
|
if(anorm==0.0)
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
rc=1;
|
|
return;
|
|
}
|
|
//--- Estimate the norm of inv(A).
|
|
ainvnm=0;
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
CMatrixEstimateNorm(n,ev,ex,ainvnm,kase,isave,rsave);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
for(i=0;i<n;i++)
|
|
ex[i]=ex[i+1];
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Multiply by inv(U')
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,2,false,maxgrowth))
|
|
return;
|
|
//--- Multiply by inv(U)
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,0,false,maxgrowth))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by inv(L)
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,0,false,maxgrowth))
|
|
return;
|
|
//--- Multiply by inv(L')
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(cha,sa,n,ex,isupper,2,false,maxgrowth))
|
|
return;
|
|
}
|
|
for(i=n-1;i>=0;i--)
|
|
ex[i+1]=ex[i];
|
|
}
|
|
//--- Compute the estimate of the reciprocal condition number.
|
|
if(ainvnm!=0.0)
|
|
{
|
|
rc=1/ainvnm;
|
|
rc=rc/anorm;
|
|
//--- check
|
|
if(rc<RCondThreshold())
|
|
rc=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine for condition number estimation |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| February 29, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::RMatrixRCondLUInternal(CMatrixDouble &lua,const int n,
|
|
const bool onenorm,
|
|
const bool isanormprovided,
|
|
double anorm,double &rc)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int kase=0;
|
|
int kase1=0;
|
|
double ainvnm=0;
|
|
double maxgrowth=0;
|
|
double su=0;
|
|
double sl=0;
|
|
bool mupper;
|
|
bool mtrans;
|
|
bool munit;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double ex[];
|
|
double ev[];
|
|
int iwork[];
|
|
double tmp[];
|
|
//--- check
|
|
if(onenorm)
|
|
kase1=1;
|
|
else
|
|
kase1=2;
|
|
//--- initialization
|
|
rc=0;
|
|
mupper=true;
|
|
mtrans=true;
|
|
munit=true;
|
|
//--- allocation
|
|
ArrayResizeAL(iwork,n+1);
|
|
ArrayResizeAL(tmp,n);
|
|
//--- prepare parameters for triangular solver
|
|
maxgrowth=1/RCondThreshold();
|
|
su=0;
|
|
sl=1;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
sl=MathMax(sl,MathAbs(lua[i][j]));
|
|
for(j=i;j<n;j++)
|
|
su=MathMax(su,MathAbs(lua[i][j]));
|
|
}
|
|
//--- check
|
|
if(su==0.0)
|
|
su=1;
|
|
su=1/su;
|
|
sl=1/sl;
|
|
//--- Estimate the norm of A
|
|
if(!isanormprovided)
|
|
{
|
|
kase=0;
|
|
anorm=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
RMatrixEstimateNorm(n,ev,ex,iwork,anorm,kase);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- check
|
|
if(kase==kase1)
|
|
{
|
|
//--- Multiply by U
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=i-1;i_<n;i_++)
|
|
v+=lua[i-1][i_]*ex[i_+i1_];
|
|
ex[i]=v;
|
|
}
|
|
//--- Multiply by L
|
|
for(i=n;i>=1;i--)
|
|
{
|
|
//--- check
|
|
if(i>1)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<=i-2;i_++)
|
|
v+=lua[i-1][i_]*ex[i_+i1_];
|
|
}
|
|
else
|
|
v=0;
|
|
ex[i]=ex[i]+v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by L'
|
|
for(i=0;i<n;i++)
|
|
tmp[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=ex[i+1];
|
|
//--- check
|
|
if(i>=1)
|
|
{
|
|
for(i_=0;i_<i;i_++)
|
|
tmp[i_]=tmp[i_]+v*lua[i][i_];
|
|
}
|
|
tmp[i]=tmp[i]+v;
|
|
}
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=tmp[i_+i1_];
|
|
//--- Multiply by U'
|
|
for(i=0;i<n;i++)
|
|
tmp[i]=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
v=ex[i+1];
|
|
for(i_=i;i_<n;i_++)
|
|
tmp[i_]=tmp[i_]+v*lua[i][i_];
|
|
}
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
ex[i_]=tmp[i_+i1_];
|
|
}
|
|
}
|
|
}
|
|
//--- Scale according to SU/SL
|
|
anorm=anorm*su*sl;
|
|
//--- Quick return if possible
|
|
//--- We assume that ANORM<>0 after this block
|
|
if(anorm==0.0)
|
|
return;
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
rc=1;
|
|
return;
|
|
}
|
|
//--- Estimate the norm of inv(A).
|
|
ainvnm=0;
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
RMatrixEstimateNorm(n,ev,ex,iwork,ainvnm,kase);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- from 1-based array to 0-based
|
|
for(i=0;i<n;i++)
|
|
ex[i]=ex[i+1];
|
|
//--- multiply by inv(A) or inv(A')
|
|
if(kase==kase1)
|
|
{
|
|
//--- Multiply by inv(L)
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(lua,sl,n,ex,!mupper,0,munit,maxgrowth))
|
|
return;
|
|
//--- Multiply by inv(U)
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(lua,su,n,ex,mupper,0,!munit,maxgrowth))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by inv(U')
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(lua,su,n,ex,mupper,1,!munit,maxgrowth))
|
|
return;
|
|
//--- Multiply by inv(L')
|
|
if(!CSafeSolve::RMatrixScaledTrSafeSolve(lua,sl,n,ex,!mupper,1,munit,maxgrowth))
|
|
return;
|
|
}
|
|
//--- from 0-based array to 1-based
|
|
for(i=n-1;i>=0;i--)
|
|
ex[i+1]=ex[i];
|
|
}
|
|
//--- Compute the estimate of the reciprocal condition number.
|
|
if(ainvnm!=0.0)
|
|
{
|
|
rc=1/ainvnm;
|
|
rc=rc/anorm;
|
|
//--- check
|
|
if(rc<RCondThreshold())
|
|
rc=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Condition number estimation |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| March 31, 1993 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::CMatrixRCondLUInternal(CMatrixComplex &lua,const int n,
|
|
const bool onenorm,
|
|
const bool isanormprovided,
|
|
double anorm,double &rc)
|
|
{
|
|
//--- create variables
|
|
int kase=0;
|
|
int kase1=0;
|
|
double ainvnm=0;
|
|
complex v=0;
|
|
int i=0;
|
|
int j=0;
|
|
double su=0;
|
|
double sl=0;
|
|
double maxgrowth=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
complex ex[];
|
|
complex cwork2[];
|
|
complex cwork3[];
|
|
complex cwork4[];
|
|
int isave[];
|
|
double rsave[];
|
|
//--- check
|
|
if(n<=0)
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(cwork2,n+1);
|
|
//--- initialization
|
|
rc=0;
|
|
//--- check
|
|
if(n==0)
|
|
{
|
|
rc=1;
|
|
return;
|
|
}
|
|
//--- prepare parameters for triangular solver
|
|
maxgrowth=1/RCondThreshold();
|
|
su=0;
|
|
sl=1;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
sl=MathMax(sl,CMath::AbsComplex(lua[i][j]));
|
|
for(j=i;j<n;j++)
|
|
su=MathMax(su,CMath::AbsComplex(lua[i][j]));
|
|
}
|
|
//--- check
|
|
if(su==0.0)
|
|
su=1;
|
|
su=1/su;
|
|
sl=1/sl;
|
|
//--- Estimate the norm of SU*SL*A
|
|
if(!isanormprovided)
|
|
{
|
|
anorm=0;
|
|
//--- check
|
|
if(onenorm)
|
|
kase1=1;
|
|
else
|
|
kase1=2;
|
|
kase=0;
|
|
do
|
|
{
|
|
//--- function call
|
|
CMatrixEstimateNorm(n,cwork4,ex,anorm,kase,isave,rsave);
|
|
//--- check
|
|
if(kase!=0)
|
|
{
|
|
//--- check
|
|
if(kase==kase1)
|
|
{
|
|
//--- Multiply by U
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=i-1;i_<n;i_++)
|
|
v+=lua[i-1][i_]*ex[i_+i1_];
|
|
ex[i]=v;
|
|
}
|
|
//--- Multiply by L
|
|
for(i=n;i>=1;i--)
|
|
{
|
|
v=0;
|
|
//--- check
|
|
if(i>1)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<=i-2;i_++)
|
|
v+=lua[i-1][i_]*ex[i_+i1_];
|
|
}
|
|
ex[i]=v+ex[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by L'
|
|
for(i=1;i<=n;i++)
|
|
cwork2[i]=0;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
v=ex[i];
|
|
//--- check
|
|
if(i>1)
|
|
{
|
|
i1_=-1;
|
|
for(i_=1;i_<i;i_++)
|
|
cwork2[i_]=cwork2[i_]+v*CMath::Conj(lua[i-1][i_+i1_]);
|
|
}
|
|
cwork2[i]=cwork2[i]+v;
|
|
}
|
|
//--- Multiply by U'
|
|
for(i=1;i<=n;i++)
|
|
ex[i]=0;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
v=cwork2[i];
|
|
i1_=-1;
|
|
for(i_=i;i_<=n;i_++)
|
|
ex[i_]=ex[i_]+v*CMath::Conj(lua[i-1][i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(kase!=0);
|
|
}
|
|
//--- Scale according to SU/SL
|
|
anorm=anorm*su*sl;
|
|
//--- check
|
|
if(anorm==0.0)
|
|
return;
|
|
//--- Estimate the norm of inv(A).
|
|
ainvnm=0;
|
|
//--- check
|
|
if(onenorm)
|
|
kase1=1;
|
|
else
|
|
kase1=2;
|
|
kase=0;
|
|
while(true)
|
|
{
|
|
//--- function call
|
|
CMatrixEstimateNorm(n,cwork4,ex,ainvnm,kase,isave,rsave);
|
|
//--- check
|
|
if(kase==0)
|
|
break;
|
|
//--- from 1-based array to 0-based
|
|
for(i=0;i<n;i++)
|
|
ex[i]=ex[i+1];
|
|
//--- multiply by inv(A) or inv(A')
|
|
if(kase==kase1)
|
|
{
|
|
//--- Multiply by inv(L)
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(lua,sl,n,ex,false,0,true,maxgrowth))
|
|
{
|
|
rc=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Multiply by inv(U)
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(lua,su,n,ex,true,0,false,maxgrowth))
|
|
{
|
|
rc=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Multiply by inv(U')
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(lua,su,n,ex,true,2,false,maxgrowth))
|
|
{
|
|
rc=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Multiply by inv(L')
|
|
if(!CSafeSolve::CMatrixScaledTrSafeSolve(lua,sl,n,ex,false,2,true,maxgrowth))
|
|
{
|
|
rc=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//--- from 0-based array to 1-based
|
|
for(i=n-1;i>=0;i--)
|
|
ex[i+1]=ex[i];
|
|
}
|
|
//--- Compute the estimate of the reciprocal condition number.
|
|
if(ainvnm!=0.0)
|
|
{
|
|
rc=1/ainvnm;
|
|
rc=rc/anorm;
|
|
//--- check
|
|
if(rc<RCondThreshold())
|
|
rc=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine for matrix norm estimation |
|
|
//| -- LAPACK auxiliary routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| February 29, 1992 |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::RMatrixEstimateNorm(const int n,double &v[],double &x[],
|
|
int &isgn[],double &est,int &kase)
|
|
{
|
|
//--- create variables
|
|
int itmax=0;
|
|
int i=0;
|
|
double t=0;
|
|
bool flg;
|
|
int positer=0;
|
|
int posj=0;
|
|
int posjlast=0;
|
|
int posjump=0;
|
|
int posaltsgn=0;
|
|
int posestold=0;
|
|
int postemp=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
itmax=5;
|
|
posaltsgn=n+1;
|
|
posestold=n+2;
|
|
postemp=n+3;
|
|
positer=n+1;
|
|
posj=n+2;
|
|
posjlast=n+3;
|
|
posjump=n+4;
|
|
//--- check
|
|
if(kase==0)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(v,n+4);
|
|
ArrayResizeAL(x,n+1);
|
|
ArrayResizeAL(isgn,n+5);
|
|
//--- change values
|
|
t=1.0/(double)n;
|
|
for(i=1;i<=n;i++)
|
|
x[i]=t;
|
|
kase=1;
|
|
isgn[posjump]=1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- ................ entry (jump = 1)
|
|
//--- first iteration. x has been overwritten by a*x.
|
|
if(isgn[posjump]==1)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
v[1]=x[1];
|
|
est=MathAbs(v[1]);
|
|
kase=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- change value
|
|
est=0;
|
|
for(i=1;i<=n;i++)
|
|
est=est+MathAbs(x[i]);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(x[i]>=0.0)
|
|
x[i]=1;
|
|
else
|
|
x[i]=-1;
|
|
//--- check
|
|
if(x[i]>0)
|
|
isgn[i]=1;
|
|
//--- check
|
|
if(x[i]<0)
|
|
isgn[i]=-1;
|
|
//--- check
|
|
if(x[i]==0)
|
|
isgn[i]=0;
|
|
}
|
|
kase=2;
|
|
isgn[posjump]=2;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- ................ entry (jump = 2)
|
|
//--- first iteration. x has been overwritten by trandpose(a)*x.
|
|
if(isgn[posjump]==2)
|
|
{
|
|
isgn[posj]=1;
|
|
for(i=2;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(x[i])>MathAbs(x[isgn[posj]]))
|
|
isgn[posj]=i;
|
|
}
|
|
isgn[positer]=2;
|
|
//--- main loop - iterations 2,3,...,itmax.
|
|
for(i=1;i<=n;i++)
|
|
x[i]=0;
|
|
x[isgn[posj]]=1;
|
|
kase=1;
|
|
isgn[posjump]=3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- ................ entry (jump = 3)
|
|
//--- x has been overwritten by a*x.
|
|
if(isgn[posjump]==3)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
v[i_]=x[i_];
|
|
v[posestold]=est;
|
|
//--- change value
|
|
est=0;
|
|
for(i=1;i<=n;i++)
|
|
est=est+MathAbs(v[i]);
|
|
flg=false;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
if(((x[i]>=0.0)&&(isgn[i]<0))||((x[i]<0.0)&&(isgn[i]>=0)))
|
|
flg=true;
|
|
}
|
|
//--- repeated sign vector detected, hence algorithm has converged.
|
|
//--- or may be cycling.
|
|
if(!flg || est<=v[posestold])
|
|
{
|
|
v[posaltsgn]=1;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i]=v[posaltsgn]*(1+(double)(i-1)/(double)(n-1));
|
|
v[posaltsgn]=-v[posaltsgn];
|
|
}
|
|
kase=1;
|
|
isgn[posjump]=5;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(x[i]>=0.0)
|
|
{
|
|
x[i]=1;
|
|
isgn[i]=1;
|
|
}
|
|
else
|
|
{
|
|
x[i]=-1;
|
|
isgn[i]=-1;
|
|
}
|
|
}
|
|
kase=2;
|
|
isgn[posjump]=4;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- ................ entry (jump = 4)
|
|
//--- x has been overwritten by trandpose(a)*x.
|
|
if(isgn[posjump]==4)
|
|
{
|
|
isgn[posjlast]=isgn[posj];
|
|
isgn[posj]=1;
|
|
for(i=2;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(x[i])>MathAbs(x[isgn[posj]]))
|
|
isgn[posj]=i;
|
|
}
|
|
//--- check
|
|
if(x[isgn[posjlast]]!=MathAbs(x[isgn[posj]]) && isgn[positer]<itmax)
|
|
{
|
|
isgn[positer]=isgn[positer]+1;
|
|
for(i=1;i<=n;i++)
|
|
x[i]=0;
|
|
x[isgn[posj]]=1;
|
|
kase=1;
|
|
isgn[posjump]=3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- iteration complete. final stage.
|
|
v[posaltsgn]=1;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i]=v[posaltsgn]*(1+(double)(i-1)/(double)(n-1));
|
|
v[posaltsgn]=-v[posaltsgn];
|
|
}
|
|
kase=1;
|
|
isgn[posjump]=5;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- ................ entry (jump = 5)
|
|
//--- x has been overwritten by a*x.
|
|
if(isgn[posjump]==5)
|
|
{
|
|
v[postemp]=0;
|
|
for(i=1;i<=n;i++)
|
|
v[postemp]=v[postemp]+MathAbs(x[i]);
|
|
v[postemp]=2*v[postemp]/(3*n);
|
|
//--- check
|
|
if(v[postemp]>est)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
v[i_]=x[i_];
|
|
est=v[postemp];
|
|
}
|
|
kase=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::CMatrixEstimateNorm(const int n,complex &v[],complex &x[],
|
|
double &est,int &kase,int &isave[],
|
|
double &rsave[])
|
|
{
|
|
//--- create variables
|
|
int itmax=0;
|
|
int i=0;
|
|
int iter=0;
|
|
int j=0;
|
|
int jlast=0;
|
|
int jump=0;
|
|
double absxi=0;
|
|
double altsgn=0;
|
|
double estold=0;
|
|
double safmin=0;
|
|
double temp=0;
|
|
int i_=0;
|
|
//--- Executable Statements
|
|
itmax=5;
|
|
safmin=CMath::m_minrealnumber;
|
|
//--- check
|
|
if(kase==0)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(v,n+1);
|
|
ArrayResizeAL(x,n+1);
|
|
ArrayResizeAL(isave,5);
|
|
ArrayResizeAL(rsave,4);
|
|
for(i=1;i<=n;i++)
|
|
x[i]=1.0/(double)n;
|
|
kase=1;
|
|
jump=1;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- function call
|
|
InternalComplexRCondLoadAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- entry (jump = 1)
|
|
//--- first iteration. x has been overwritten by a*x.
|
|
if(jump==1)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
v[1]=x[1];
|
|
//--- function call
|
|
est=CMath::AbsComplex(v[1]);
|
|
kase=0;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- function call
|
|
est=InternalComplexRCondScSum1(x,n);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
//--- function call
|
|
absxi=CMath::AbsComplex(x[i]);
|
|
//--- check
|
|
if(absxi>safmin)
|
|
x[i]=x[i]/absxi;
|
|
else
|
|
x[i]=1;
|
|
}
|
|
kase=2;
|
|
jump=2;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- entry (jump = 2)
|
|
//--- first iteration. x has been overwritten by ctrans(a)*x.
|
|
if(jump==2)
|
|
{
|
|
j=InternalComplexRCondIcMax1(x,n);
|
|
iter=2;
|
|
//--- main loop - iterations 2,3,...,itmax.
|
|
for(i=1;i<=n;i++)
|
|
x[i]=0;
|
|
x[j]=1;
|
|
kase=1;
|
|
jump=3;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- entry (jump = 3)
|
|
//--- x has been overwritten by a*x.
|
|
if(jump==3)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
v[i_]=x[i_];
|
|
estold=est;
|
|
//--- function call
|
|
est=InternalComplexRCondScSum1(v,n);
|
|
//--- test for cycling.
|
|
if(est<=estold)
|
|
{
|
|
//--- iteration complete. final stage.
|
|
altsgn=1;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i]=altsgn*(1+(double)(i-1)/(double)(n-1));
|
|
altsgn=-altsgn;
|
|
}
|
|
kase=1;
|
|
jump=5;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
absxi=CMath::AbsComplex(x[i]);
|
|
//--- check
|
|
if(absxi>safmin)
|
|
x[i]=x[i]/absxi;
|
|
else
|
|
x[i]=1;
|
|
}
|
|
kase=2;
|
|
jump=4;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- entry (jump = 4)
|
|
//--- x has been overwritten by ctrans(a)*x.
|
|
if(jump==4)
|
|
{
|
|
jlast=j;
|
|
j=InternalComplexRCondIcMax1(x,n);
|
|
//--- check
|
|
if(CMath::AbsComplex(x[jlast])!=CMath::AbsComplex(x[j]) && iter<itmax)
|
|
{
|
|
iter=iter+1;
|
|
//--- main loop - iterations 2,3,...,itmax.
|
|
for(i=1;i<=n;i++)
|
|
x[i]=0;
|
|
x[j]=1;
|
|
kase=1;
|
|
jump=3;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- iteration complete. final stage.
|
|
altsgn=1;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i]=altsgn*(1+(double)(i-1)/(double)(n-1));
|
|
altsgn=-altsgn;
|
|
}
|
|
kase=1;
|
|
jump=5;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- entry (jump = 5)
|
|
//--- x has been overwritten by a*x.
|
|
if(jump==5)
|
|
{
|
|
temp=2*(InternalComplexRCondScSum1(x,n)/(3*n));
|
|
//--- check
|
|
if(temp>est)
|
|
{
|
|
for(i_=1;i_<=n;i_++)
|
|
v[i_]=x[i_];
|
|
est=temp;
|
|
}
|
|
kase=0;
|
|
//--- function call
|
|
InternalComplexRCondSaveAll(isave,rsave,i,iter,j,jlast,jump,absxi,altsgn,estold,temp);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static double CRCond::InternalComplexRCondScSum1(complex &x[],const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
//--- get result
|
|
for(i=1;i<=n;i++)
|
|
result=result+CMath::AbsComplex(x[i]);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static int CRCond::InternalComplexRCondIcMax1(complex &x[],const int n)
|
|
{
|
|
//--- create variables
|
|
int result=0;
|
|
int i=0;
|
|
double m=0;
|
|
//--- get result
|
|
result=1;
|
|
m=CMath::AbsComplex(x[1]);
|
|
for(i=2;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::AbsComplex(x[i])>m)
|
|
{
|
|
result=i;
|
|
m=CMath::AbsComplex(x[i]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::InternalComplexRCondSaveAll(int &isave[],double &rsave[],
|
|
int &i,int &iter,int &j,
|
|
int &jlast,int &jump,
|
|
double &absxi,double &altsgn,
|
|
double &estold,double &temp)
|
|
{
|
|
//--- copy
|
|
isave[0]=i;
|
|
isave[1]=iter;
|
|
isave[2]=j;
|
|
isave[3]=jlast;
|
|
isave[4]=jump;
|
|
//--- copy
|
|
rsave[0]=absxi;
|
|
rsave[1]=altsgn;
|
|
rsave[2]=estold;
|
|
rsave[3]=temp;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CRCond::InternalComplexRCondLoadAll(int &isave[],double &rsave[],
|
|
int &i,int &iter,int &j,
|
|
int &jlast,int &jump,
|
|
double &absxi,double &altsgn,
|
|
double &estold,double &temp)
|
|
{
|
|
//--- get
|
|
i=isave[0];
|
|
iter=isave[1];
|
|
j=isave[2];
|
|
jlast=isave[3];
|
|
jump=isave[4];
|
|
//--- get
|
|
absxi=rsave[0];
|
|
altsgn=rsave[1];
|
|
estold=rsave[2];
|
|
temp=rsave[3];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse report: |
|
|
//| * R1 reciprocal of condition number in 1-norm |
|
|
//| * RInf reciprocal of condition number in inf-norm |
|
|
//+------------------------------------------------------------------+
|
|
class CMatInvReport
|
|
{
|
|
public:
|
|
double m_r1;
|
|
double m_rinf;
|
|
//--- constructor, destructor
|
|
CMatInvReport(void);
|
|
~CMatInvReport(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMatInvReport::CMatInvReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMatInvReport::~CMatInvReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse report: |
|
|
//| * R1 reciprocal of condition number in 1-norm |
|
|
//| * RInf reciprocal of condition number in inf-norm |
|
|
//+------------------------------------------------------------------+
|
|
class CMatInvReportShell
|
|
{
|
|
private:
|
|
CMatInvReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMatInvReportShell(void);
|
|
CMatInvReportShell(CMatInvReport &obj);
|
|
~CMatInvReportShell(void);
|
|
//--- methods
|
|
double GetR1(void);
|
|
void SetR1(double r);
|
|
double GetRInf(void);
|
|
void SetRInf(double r);
|
|
CMatInvReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMatInvReportShell::CMatInvReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
CMatInvReportShell::CMatInvReportShell(CMatInvReport &obj)
|
|
{
|
|
m_innerobj.m_r1=obj.m_r1;
|
|
m_innerobj.m_rinf=obj.m_rinf;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMatInvReportShell::~CMatInvReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable r1 |
|
|
//+------------------------------------------------------------------+
|
|
double CMatInvReportShell::GetR1(void)
|
|
{
|
|
return(m_innerobj.m_r1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable r1 |
|
|
//+------------------------------------------------------------------+
|
|
void CMatInvReportShell::SetR1(double r)
|
|
{
|
|
m_innerobj.m_r1=r;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable rinf |
|
|
//+------------------------------------------------------------------+
|
|
double CMatInvReportShell::GetRInf(void)
|
|
{
|
|
return(m_innerobj.m_rinf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable rint |
|
|
//+------------------------------------------------------------------+
|
|
void CMatInvReportShell::SetRInf(double r)
|
|
{
|
|
m_innerobj.m_rinf=r;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMatInvReport *CMatInvReportShell::GetInnerObj(void)
|
|
{
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse matrix |
|
|
//+------------------------------------------------------------------+
|
|
class CMatInv
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixTrInverseRec(CMatrixDouble &a,const int offs,const int n,const bool isupper,const bool isunit,double &tmp[],int &info,CMatInvReport &rep);
|
|
static void CMatrixTrInverseRec(CMatrixComplex &a,const int offs,const int n,const bool isupper,const bool isunit,complex &tmp[],int &info,CMatInvReport &rep);
|
|
static void RMatrixLUInverseRec(CMatrixDouble &a,const int offs,const int n,double &work[],int &info,CMatInvReport &rep);
|
|
static void CMatrixLUInverseRec(CMatrixComplex &a,const int offs,const int n,complex &work[],int &info,CMatInvReport &rep);
|
|
static void SPDMatrixCholeskyInverseRec(CMatrixDouble &a,const int offs,const int n,const bool isupper,double &tmp[]);
|
|
static void HPDMatrixCholeskyInverseRec(CMatrixComplex &a,const int offs,const int n,const bool isupper,complex &tmp[]);
|
|
public:
|
|
CMatInv(void);
|
|
~CMatInv(void);
|
|
//--- public methods
|
|
static void RMatrixLUInverse(CMatrixDouble &a,int &pivots[],const int n,int &info,CMatInvReport &rep);
|
|
static void RMatrixInverse(CMatrixDouble &a,const int n,int &info,CMatInvReport &rep);
|
|
static void SPDMatrixCholeskyInverse(CMatrixDouble &a,const int n,const bool isupper,int &info,CMatInvReport &rep);
|
|
static void SPDMatrixInverse(CMatrixDouble &a,const int n,const bool isupper,int &info,CMatInvReport &rep);
|
|
static void RMatrixTrInverse(CMatrixDouble &a,const int n,const bool isupper,const bool isunit,int &info,CMatInvReport &rep);
|
|
static void CMatrixLUInverse(CMatrixComplex &a,int &pivots[],const int n,int &info,CMatInvReport &rep);
|
|
static void CMatrixInverse(CMatrixComplex &a,const int n,int &info,CMatInvReport &rep);
|
|
static void HPDMatrixCholeskyInverse(CMatrixComplex &a,const int n,const bool isupper,int &info,CMatInvReport &rep);
|
|
static void HPDMatrixInverse(CMatrixComplex &a,const int n,const bool isupper,int &info,CMatInvReport &rep);
|
|
static void CMatrixTrInverse(CMatrixComplex &a,const int n,const bool isupper,const bool isunit,int &info,CMatInvReport &rep);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMatInv::CMatInv(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMatInv::~CMatInv(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a matrix given by its LU decomposition. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - LU decomposition of the matrix |
|
|
//| (output of RMatrixLU subroutine). |
|
|
//| Pivots - table of permutations |
|
|
//| (the output of RMatrixLU subroutine). |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - return code: |
|
|
//| * -3 A is singular, or VERY close to singular.|
|
|
//| it is filled by zeros in such cases. |
|
|
//| * 1 task is solved (but matrix A may be |
|
|
//| ill-conditioned, check R1/RInf parameters|
|
|
//| for condition numbers). |
|
|
//| Rep - solver report, see below for more info |
|
|
//| A - inverse of matrix A. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| SOLVER REPORT |
|
|
//| Subroutine sets following fields of the Rep structure: |
|
|
//| * R1 reciprocal of condition number: 1/cond(A), 1-norm. |
|
|
//| * RInf reciprocal of condition number: 1/cond(A), inf-norm. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::RMatrixLUInverse(CMatrixDouble &a,int &pivots[],
|
|
const int n,int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(pivots)>=n,__FUNCTION__+": len(Pivots)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(pivots[i]>n-1 || pivots[i]<i)
|
|
info=-1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(info>0,__FUNCTION__+": incorrect Pivots array!"))
|
|
return;
|
|
//--- calculate condition numbers
|
|
rep.m_r1=CRCond::RMatrixLURCond1(a,n);
|
|
rep.m_rinf=CRCond::RMatrixLURCondInf(a,n);
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Call cache-oblivious code
|
|
ArrayResizeAL(work,n);
|
|
RMatrixLUInverseRec(a,0,n,work,info,rep);
|
|
//--- apply permutations
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=n-2;j>=0;j--)
|
|
{
|
|
k=pivots[j];
|
|
v=a[i][j];
|
|
a[i].Set(j,a[i][k]);
|
|
a[i].Set(k,v);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a general matrix. |
|
|
//| Input parameters: |
|
|
//| A - matrix. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| Output parameters: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//| Result: |
|
|
//| True, if the matrix is not singular. |
|
|
//| False, if the matrix is singular. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::RMatrixInverse(CMatrixDouble &a,const int n,int &info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
CTrFac::RMatrixLU(a,n,n,pivots);
|
|
//--- function call
|
|
RMatrixLUInverse(a,pivots,n,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a matrix given by its LU decomposition. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - LU decomposition of the matrix |
|
|
//| (output of CMatrixLU subroutine). |
|
|
//| Pivots - table of permutations |
|
|
//| (the output of CMatrixLU subroutine). |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::CMatrixLUInverse(CMatrixComplex &a,int &pivots[],
|
|
const int n,int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
//--- create array
|
|
complex work[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(pivots)>=n,__FUNCTION__+": len(Pivots)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteComplexMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(pivots[i]>n-1 || pivots[i]<i)
|
|
info=-1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(info>0,__FUNCTION__+": incorrect Pivots array!"))
|
|
return;
|
|
//--- calculate condition numbers
|
|
rep.m_r1=CRCond::CMatrixLURCond1(a,n);
|
|
rep.m_rinf=CRCond::CMatrixLURCondInf(a,n);
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Call cache-oblivious code
|
|
ArrayResizeAL(work,n);
|
|
CMatrixLUInverseRec(a,0,n,work,info,rep);
|
|
//--- apply permutations
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=n-2;j>=0;j--)
|
|
{
|
|
k=pivots[j];
|
|
v=a[i][j];
|
|
a[i].Set(j,a[i][k]);
|
|
a[i].Set(k,v);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a general matrix. |
|
|
//| Input parameters: |
|
|
//| A - matrix |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| Output parameters: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::CMatrixInverse(CMatrixComplex &a,const int n,int &info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteComplexMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
CTrFac::CMatrixLU(a,n,n,pivots);
|
|
//--- function call
|
|
CMatrixLUInverse(a,pivots,n,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a symmetric positive definite matrix which is given |
|
|
//| by Cholesky decomposition. |
|
|
//| Input parameters: |
|
|
//| A - Cholesky decomposition of the matrix to be |
|
|
//| inverted: A=U?*U or A = L*L'. |
|
|
//| Output of SPDMatrixCholesky subroutine. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| IsUpper - storage type (optional): |
|
|
//| * if True, symmetric matrix A is given by its |
|
|
//| upper triangle, and the lower triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if False, symmetric matrix A is given by its |
|
|
//| lower triangle, and the upper triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if not given, lower half is used. |
|
|
//| Output parameters: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::SPDMatrixCholeskyInverse(CMatrixDouble &a,const int n,
|
|
const bool isupper,int &info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
bool f;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- object of class
|
|
CMatInvReport rep2;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
f=true;
|
|
for(i=0;i<n;i++)
|
|
f=f && CMath::IsFinite(a[i][i]);
|
|
//--- check
|
|
if(!CAp::Assert(f,__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- calculate condition numbers
|
|
rep.m_r1=CRCond::SPDMatrixCholeskyRCond(a,n,isupper);
|
|
rep.m_rinf=rep.m_r1;
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=i;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=0;j<=i;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Inverse
|
|
ArrayResizeAL(tmp,n);
|
|
SPDMatrixCholeskyInverseRec(a,0,n,isupper,tmp);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a symmetric positive definite matrix. |
|
|
//| Given an upper or lower triangle of a symmetric positive definite|
|
|
//| matrix, the algorithm generates matrix A^-1 and saves the upper |
|
|
//| or lower triangle depending on the input. |
|
|
//| Input parameters: |
|
|
//| A - matrix to be inverted (upper or lower triangle). |
|
|
//| Array with elements [0..N-1,0..N-1]. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| IsUpper - storage type (optional): |
|
|
//| * if True, symmetric matrix A is given by its |
|
|
//| upper triangle, and the lower triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if False, symmetric matrix A is given by its |
|
|
//| lower triangle, and the upper triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if not given, both lower and upper triangles |
|
|
//| must be filled. |
|
|
//| Output parameters: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::SPDMatrixInverse(CMatrixDouble &a,const int n,
|
|
const bool isupper,int &info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteRTrMatrix(a,n,isupper),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
//--- check
|
|
if(CTrFac::SPDMatrixCholesky(a,n,isupper))
|
|
SPDMatrixCholeskyInverse(a,n,isupper,info,rep);
|
|
else
|
|
info=-3;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a Hermitian positive definite matrix which is given |
|
|
//| by Cholesky decomposition. |
|
|
//| Input parameters: |
|
|
//| A - Cholesky decomposition of the matrix to be |
|
|
//| inverted: A=U?*U or A = L*L'. |
|
|
//| Output of HPDMatrixCholesky subroutine. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| IsUpper - storage type (optional): |
|
|
//| * if True, symmetric matrix A is given by its |
|
|
//| upper triangle, and the lower triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if False, symmetric matrix A is given by its |
|
|
//| lower triangle, and the upper triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if not given, lower half is used. |
|
|
//| Output parameters: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::HPDMatrixCholeskyInverse(CMatrixComplex &a,const int n,
|
|
const bool isupper,int &info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
bool f;
|
|
//--- create array
|
|
complex tmp[];
|
|
//--- object of class
|
|
CMatInvReport rep2;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- initialization
|
|
f=true;
|
|
for(i=0;i<n;i++)
|
|
f=(f && CMath::IsFinite(a[i][i].re)) && CMath::IsFinite(a[i][i].im);
|
|
//--- check
|
|
if(!CAp::Assert(f,__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
info=1;
|
|
//--- calculate condition numbers
|
|
rep.m_r1=CRCond::HPDMatrixCholeskyRCond(a,n,isupper);
|
|
rep.m_rinf=rep.m_r1;
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=i;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=0;j<=i;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Inverse
|
|
ArrayResizeAL(tmp,n);
|
|
HPDMatrixCholeskyInverseRec(a,0,n,isupper,tmp);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inversion of a Hermitian positive definite matrix. |
|
|
//| Given an upper or lower triangle of a Hermitian positive definite|
|
|
//| matrix, the algorithm generates matrix A^-1 and saves the upper |
|
|
//| or lower triangle depending on the input. |
|
|
//| Input parameters: |
|
|
//| A - matrix to be inverted (upper or lower triangle). |
|
|
//| Array with elements [0..N-1,0..N-1]. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| IsUpper - storage type (optional): |
|
|
//| * if True, symmetric matrix A is given by its |
|
|
//| upper triangle, and the lower triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if False, symmetric matrix A is given by its |
|
|
//| lower triangle, and the upper triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if not given, both lower and upper triangles |
|
|
//| must be filled. |
|
|
//| Output parameters: |
|
|
//| Info - return code, same as in RMatrixLUInverse |
|
|
//| Rep - solver report, same as in RMatrixLUInverse |
|
|
//| A - inverse of matrix A, same as in RMatrixLUInverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::HPDMatrixInverse(CMatrixComplex &a,const int n,
|
|
const bool isupper,int &info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteCTrMatrix(a,n,isupper),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
//--- check
|
|
if(CTrFac::HPDMatrixCholesky(a,n,isupper))
|
|
HPDMatrixCholeskyInverse(a,n,isupper,info,rep);
|
|
else
|
|
info=-3;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix inverse (real) |
|
|
//| The subroutine inverts the following types of matrices: |
|
|
//| * upper triangular |
|
|
//| * upper triangular with unit diagonal |
|
|
//| * lower triangular |
|
|
//| * lower triangular with unit diagonal |
|
|
//| In case of an upper (lower) triangular matrix, the inverse matrix|
|
|
//| will also be upper (lower) triangular, and after the end of the |
|
|
//| algorithm, the inverse matrix replaces the source matrix. The |
|
|
//| elements below (above) the main diagonal are not changed by the |
|
|
//| algorithm. |
|
|
//| If the matrix has a unit diagonal, the inverse matrix also has a |
|
|
//| unit diagonal, and the diagonal elements are not passed to the |
|
|
//| algorithm. |
|
|
//| Input parameters: |
|
|
//| A - matrix, array[0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| IsUpper - True, if the matrix is upper triangular. |
|
|
//| IsUnit - diagonal type (optional): |
|
|
//| * if True, matrix has unit diagonal (a[i,i] are |
|
|
//| NOT used) |
|
|
//| * if False, matrix diagonal is arbitrary |
|
|
//| * if not given, False is assumed |
|
|
//| Output parameters: |
|
|
//| Info - same as for RMatrixLUInverse |
|
|
//| Rep - same as for RMatrixLUInverse |
|
|
//| A - same as for RMatrixLUInverse. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::RMatrixTrInverse(CMatrixDouble &a,const int n,
|
|
const bool isupper,const bool isunit,
|
|
int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteRTrMatrix(a,n,isupper),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
//--- calculate condition numbers
|
|
rep.m_r1=CRCond::RMatrixTrRCond1(a,n,isupper,isunit);
|
|
rep.m_rinf=CRCond::RMatrixTrRCondInf(a,n,isupper,isunit);
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Inverse
|
|
ArrayResizeAL(tmp,n);
|
|
RMatrixTrInverseRec(a,0,n,isupper,isunit,tmp,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix inverse (complex) |
|
|
//| The subroutine inverts the following types of matrices: |
|
|
//| * upper triangular |
|
|
//| * upper triangular with unit diagonal |
|
|
//| * lower triangular |
|
|
//| * lower triangular with unit diagonal |
|
|
//| In case of an upper (lower) triangular matrix, the inverse matrix|
|
|
//| will also be upper (lower) triangular, and after the end of the |
|
|
//| algorithm, the inverse matrix replaces the source matrix. The |
|
|
//| elements below (above) the main diagonal are not changed by the |
|
|
//| algorithm. |
|
|
//| If the matrix has a unit diagonal, the inverse matrix also has a |
|
|
//| unit diagonal, and the diagonal elements are not passed to the |
|
|
//| algorithm. |
|
|
//| Input parameters: |
|
|
//| A - matrix, array[0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A (optional) : |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, size is automatically determined |
|
|
//| from matrix size (A must be square matrix) |
|
|
//| IsUpper - True, if the matrix is upper triangular. |
|
|
//| IsUnit - diagonal type (optional): |
|
|
//| * if True, matrix has unit diagonal (a[i,i] are |
|
|
//| NOT used) |
|
|
//| * if False, matrix diagonal is arbitrary |
|
|
//| * if not given, False is assumed |
|
|
//| Output parameters: |
|
|
//| Info - same as for RMatrixLUInverse |
|
|
//| Rep - same as for RMatrixLUInverse |
|
|
//| A - same as for RMatrixLUInverse. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::CMatrixTrInverse(CMatrixComplex &a,const int n,
|
|
const bool isupper,const bool isunit,
|
|
int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- create array
|
|
complex tmp[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteCTrMatrix(a,n,isupper),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
info=1;
|
|
//--- calculate condition numbers
|
|
rep.m_r1=CRCond::CMatrixTrRCond1(a,n,isupper,isunit);
|
|
rep.m_rinf=CRCond::CMatrixTrRCondInf(a,n,isupper,isunit);
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<n;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Inverse
|
|
ArrayResizeAL(tmp,n);
|
|
CMatrixTrInverseRec(a,0,n,isupper,isunit,tmp,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix inversion, recursive subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::RMatrixTrInverseRec(CMatrixDouble &a,const int offs,
|
|
const int n,const bool isupper,
|
|
const bool isunit,double &tmp[],
|
|
int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(n<1)
|
|
{
|
|
info=-1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- base case
|
|
if(n<=CAblas::AblasBlockSize())
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(!isunit)
|
|
{
|
|
//--- check
|
|
if(a[offs+j][offs+j]==0.0)
|
|
{
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
a[offs+j].Set(offs+j,1/a[offs+j][offs+j]);
|
|
ajj=-a[offs+j][offs+j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<j;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
for(i=0;i<j;i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
i1_=(i+1)-(offs+i+1);
|
|
v=0.0;
|
|
for(i_=offs+i+1;i_<=offs+j-1;i_++)
|
|
v+=a[offs+i][i_]*tmp[i_+i1_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(!isunit)
|
|
a[offs+i].Set(offs+j,v+a[offs+i][offs+i]*tmp[i]);
|
|
else
|
|
a[offs+i].Set(offs+j,v+tmp[i]);
|
|
}
|
|
for(i_=offs+0;i_<=offs+j-1;i_++)
|
|
a[i_].Set(offs+j,ajj*a[i_][offs+j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(!isunit)
|
|
{
|
|
//--- check
|
|
if(a[offs+j][offs+j]==0.0)
|
|
{
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
a[offs+j].Set(offs+j,1/a[offs+j][offs+j]);
|
|
ajj=-a[offs+j][offs+j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
i1_=offs;
|
|
for(i_=j+1;i_<n;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
for(i=j+1;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
i1_=-offs;
|
|
v=0.0;
|
|
for(i_=offs+j+1;i_<offs+i;i_++)
|
|
v+=a[offs+i][i_]*tmp[i_+i1_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(!isunit)
|
|
a[offs+i].Set(offs+j,v+a[offs+i][offs+i]*tmp[i]);
|
|
else
|
|
a[offs+i].Set(offs+j,v+tmp[i]);
|
|
}
|
|
for(i_=offs+j+1;i_<=offs+n-1;i_++)
|
|
a[i_].Set(offs+j,ajj*a[i_][offs+j]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Recursive case
|
|
CAblas::AblasSplitLength(a,n,n1,n2);
|
|
//--- check
|
|
if(n2>0)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n1;i++)
|
|
for(i_=offs+n1;i_<=offs+n-1;i_++)
|
|
a[offs+i].Set(i_,-1*a[offs+i][i_]);
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n1,n2,a,offs,offs,isupper,isunit,0,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,isupper,isunit,0,a,offs,offs+n1);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n2;i++)
|
|
for(i_=offs;i_<=offs+n1-1;i_++)
|
|
a[offs+n1+i].Set(i_,-1*a[offs+n1+i][i_]);
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(n2,n1,a,offs,offs,isupper,isunit,0,a,offs+n1,offs);
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,isupper,isunit,0,a,offs+n1,offs);
|
|
}
|
|
//--- function call
|
|
RMatrixTrInverseRec(a,offs+n1,n2,isupper,isunit,tmp,info,rep);
|
|
}
|
|
//--- function call
|
|
RMatrixTrInverseRec(a,offs,n1,isupper,isunit,tmp,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular matrix inversion, recursive subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::CMatrixTrInverseRec(CMatrixComplex &a,const int offs,
|
|
const int n,const bool isupper,
|
|
const bool isunit,complex &tmp[],
|
|
int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
complex One=1.0;
|
|
complex _One=-1.0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
complex ajj=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(n<1)
|
|
{
|
|
info=-1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- base case
|
|
if(n<=CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<n;j++)
|
|
{
|
|
//--- check
|
|
if(!isunit)
|
|
{
|
|
//--- check
|
|
if(a[offs+j][offs+j]==0)
|
|
{
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
a[offs+j].Set(offs+j,One/a[offs+j][offs+j]);
|
|
ajj=-a[offs+j][offs+j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
i1_=offs;
|
|
for(i_=0;i_<j;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
for(i=0;i<j;i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
i1_=(i+1)-(offs+i+1);
|
|
v=0.0;
|
|
for(i_=offs+i+1;i_<=offs+j-1;i_++)
|
|
v+=a[offs+i][i_]*tmp[i_+i1_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(!isunit)
|
|
a[offs+i].Set(offs+j,v+a[offs+i][offs+i]*tmp[i]);
|
|
else
|
|
a[offs+i].Set(offs+j,v+tmp[i]);
|
|
}
|
|
for(i_=offs+0;i_<=offs+j-1;i_++)
|
|
a[i_].Set(offs+j,ajj*a[i_][offs+j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(!isunit)
|
|
{
|
|
//--- check
|
|
if(a[offs+j][offs+j]==0)
|
|
{
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
a[offs+j].Set(offs+j,One/a[offs+j][offs+j]);
|
|
ajj=-a[offs+j][offs+j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
i1_=offs;
|
|
for(i_=j+1;i_<n;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+j];
|
|
for(i=j+1;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
i1_=-offs;
|
|
v=0.0;
|
|
for(i_=offs+j+1;i_<offs+i;i_++)
|
|
v+=a[offs+i][i_]*tmp[i_+i1_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(!isunit)
|
|
a[offs+i].Set(offs+j,v+a[offs+i][offs+i]*tmp[i]);
|
|
else
|
|
a[offs+i].Set(offs+j,v+tmp[i]);
|
|
}
|
|
for(i_=offs+j+1;i_<=offs+n-1;i_++)
|
|
a[i_].Set(offs+j,ajj*a[i_][offs+j]);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Recursive case
|
|
CAblas::AblasComplexSplitLength(a,n,n1,n2);
|
|
//--- check
|
|
if(n2>0)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
for(i_=offs+n1;i_<=offs+n-1;i_++)
|
|
a[offs+i].Set(i_,_One*a[offs+i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n1,n2,a,offs,offs,isupper,isunit,0,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,isupper,isunit,0,a,offs,offs+n1);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
for(i_=offs;i_<=offs+n1-1;i_++)
|
|
a[offs+n1+i].Set(i_,_One*a[offs+n1+i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(n2,n1,a,offs,offs,isupper,isunit,0,a,offs+n1,offs);
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,isupper,isunit,0,a,offs+n1,offs);
|
|
}
|
|
//--- function call
|
|
CMatrixTrInverseRec(a,offs+n1,n2,isupper,isunit,tmp,info,rep);
|
|
}
|
|
//--- function call
|
|
CMatrixTrInverseRec(a,offs,n1,isupper,isunit,tmp,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::RMatrixLUInverseRec(CMatrixDouble &a,const int offs,
|
|
const int n,double &work[],
|
|
int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(n<1)
|
|
{
|
|
info=-1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- base case
|
|
if(n<=CAblas::AblasBlockSize())
|
|
{
|
|
//--- Form inv(U)
|
|
RMatrixTrInverseRec(a,offs,n,true,false,work,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return;
|
|
//--- Solve the equation inv(A)*L = inv(U) for inv(A).
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1;i<n;i++)
|
|
{
|
|
work[i]=a[offs+i][offs+j];
|
|
a[offs+i].Set(offs+j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
i1_=-offs;
|
|
v=0.0;
|
|
for(i_=offs+j+1;i_<=offs+n-1;i_++)
|
|
v+=a[offs+i][i_]*work[i_+i1_];
|
|
a[offs+i].Set(offs+j,a[offs+i][offs+j]-v);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Recursive code:
|
|
//--- ( L1 ) ( U1 U12 )
|
|
//--- A = ( ) * ( )
|
|
//--- ( L12 L2 ) ( U2 )
|
|
//--- ( W X )
|
|
//--- A^-1 = ( )
|
|
//--- ( Y Z )
|
|
CAblas::AblasSplitLength(a,n,n1,n2);
|
|
//--- check
|
|
if(!CAp::Assert(n2>0,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- X :=inv(U1)*U12*inv(U2)
|
|
CAblas::RMatrixLeftTrsM(n1,n2,a,offs,offs,true,false,0,a,offs,offs+n1);
|
|
CAblas::RMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,true,false,0,a,offs,offs+n1);
|
|
//--- Y :=inv(L2)*L12*inv(L1)
|
|
CAblas::RMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,false,true,0,a,offs+n1,offs);
|
|
CAblas::RMatrixRightTrsM(n2,n1,a,offs,offs,false,true,0,a,offs+n1,offs);
|
|
//--- W :=inv(L1*U1)+X*Y
|
|
RMatrixLUInverseRec(a,offs,n1,work,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return;
|
|
//--- function call
|
|
CAblas::RMatrixGemm(n1,n1,n2,1.0,a,offs,offs+n1,0,a,offs+n1,offs,0,1.0,a,offs,offs);
|
|
//--- X :=-X*inv(L2)
|
|
//--- Y :=-inv(U2)*Y
|
|
CAblas::RMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,false,true,0,a,offs,offs+n1);
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
for(i_=offs+n1;i_<=offs+n-1;i_++)
|
|
a[offs+i].Set(i_,-1*a[offs+i][i_]);
|
|
}
|
|
CAblas::RMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,true,false,0,a,offs+n1,offs);
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
for(i_=offs;i_<=offs+n1-1;i_++)
|
|
a[offs+n1+i].Set(i_,-1*a[offs+n1+i][i_]);
|
|
}
|
|
//--- Z :=inv(L2*U2)
|
|
RMatrixLUInverseRec(a,offs+n1,n2,work,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::CMatrixLUInverseRec(CMatrixComplex &a,const int offs,
|
|
const int n,complex &work[],
|
|
int &info,CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
complex One=1.0;
|
|
complex _One=-1.0;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- check
|
|
if(n<1)
|
|
{
|
|
info=-1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- base case
|
|
if(n<=CAblas::AblasComplexBlockSize())
|
|
{
|
|
//--- Form inv(U)
|
|
CMatrixTrInverseRec(a,offs,n,true,false,work,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return;
|
|
//--- Solve the equation inv(A)*L = inv(U) for inv(A).
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1;i<n;i++)
|
|
{
|
|
work[i]=a[offs+i][offs+j];
|
|
a[offs+i].Set(offs+j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
{
|
|
i1_=-offs;
|
|
v=0.0;
|
|
for(i_=offs+j+1;i_<=offs+n-1;i_++)
|
|
v+=a[offs+i][i_]*work[i_+i1_];
|
|
a[offs+i].Set(offs+j,a[offs+i][offs+j]-v);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Recursive code:
|
|
//--- ( L1 ) ( U1 U12 )
|
|
//--- A = ( ) * ( )
|
|
//--- ( L12 L2 ) ( U2 )
|
|
//--- ( W X )
|
|
//--- A^-1 = ( )
|
|
//--- ( Y Z )
|
|
CAblas::AblasComplexSplitLength(a,n,n1,n2);
|
|
//--- check
|
|
if(!CAp::Assert(n2>0,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- X :=inv(U1)*U12*inv(U2)
|
|
CAblas::CMatrixLeftTrsM(n1,n2,a,offs,offs,true,false,0,a,offs,offs+n1);
|
|
CAblas::CMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,true,false,0,a,offs,offs+n1);
|
|
//--- Y :=inv(L2)*L12*inv(L1)
|
|
CAblas::CMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,false,true,0,a,offs+n1,offs);
|
|
CAblas::CMatrixRightTrsM(n2,n1,a,offs,offs,false,true,0,a,offs+n1,offs);
|
|
//--- W :=inv(L1*U1)+X*Y
|
|
CMatrixLUInverseRec(a,offs,n1,work,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return;
|
|
CAblas::CMatrixGemm(n1,n1,n2,One,a,offs,offs+n1,0,a,offs+n1,offs,0,One,a,offs,offs);
|
|
//--- X :=-X*inv(L2)
|
|
//--- Y :=-inv(U2)*Y
|
|
CAblas::CMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,false,true,0,a,offs,offs+n1);
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
for(i_=offs+n1;i_<=offs+n-1;i_++)
|
|
a[offs+i].Set(i_,_One*a[offs+i][i_]);
|
|
}
|
|
CAblas::CMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,true,false,0,a,offs+n1,offs);
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
for(i_=offs;i_<=offs+n1-1;i_++)
|
|
a[offs+n1+i].Set(i_,_One*a[offs+n1+i][i_]);
|
|
}
|
|
//--- Z :=inv(L2*U2)
|
|
CMatrixLUInverseRec(a,offs+n1,n2,work,info,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recursive subroutine for SPD inversion. | |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::SPDMatrixCholeskyInverseRec(CMatrixDouble &a,const int offs,
|
|
const int n,const bool isupper,
|
|
double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int info2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- object of class
|
|
CMatInvReport rep2;
|
|
//--- check
|
|
if(n<1)
|
|
return;
|
|
//--- base case
|
|
if(n<=CAblas::AblasBlockSize())
|
|
{
|
|
RMatrixTrInverseRec(a,offs,n,isupper,false,tmp,info2,rep2);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute the product U * U'.
|
|
//--- NOTE: we never assume that diagonal of U is real
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
{
|
|
//--- 1x1 matrix
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i]));
|
|
}
|
|
else
|
|
{
|
|
//--- (I+1)x(I+1) matrix,
|
|
//--- ( A11 A12 ) ( A11^H ) ( A11*A11^H+A12*A12^H A12*A22^H )
|
|
//--- ( ) * ( ) = ( )
|
|
//--- ( A22 ) ( A12^H A22^H ) ( A22*A12^H A22*A22^H )
|
|
//--- A11 is IxI, A22 is 1x1.
|
|
i1_=offs;
|
|
for(i_=0;i_<=i-1;i_++)
|
|
tmp[i_]=a[i_+i1_][offs+i];
|
|
for(j=0;j<i;j++)
|
|
{
|
|
v=a[offs+j][offs+i];
|
|
i1_=-offs;
|
|
for(i_=offs+j;i_<offs+i;i_++)
|
|
a[offs+j].Set(i_,a[offs+j][i_]+v*tmp[i_+i1_]);
|
|
}
|
|
//--- change values
|
|
v=a[offs+i][offs+i];
|
|
for(i_=offs;i_<offs+i;i_++)
|
|
a[i_].Set(offs+i,v*a[i_][offs+i]);
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i]));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the product L' * L
|
|
//--- NOTE: we never assume that diagonal of L is reall
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
{
|
|
//--- 1x1 matrix
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i]));
|
|
}
|
|
else
|
|
{
|
|
//--- (I+1)x(I+1) matrix,
|
|
//--- ( A11^H A21^H ) ( A11 ) ( A11^H*A11+A21^H*A21 A21^H*A22 )
|
|
//--- ( ) * ( ) = ( )
|
|
//--- ( A22^H ) ( A21 A22 ) ( A22^H*A21 A22^H*A22 )
|
|
//--- A11 is IxI, A22 is 1x1.
|
|
i1_=offs;
|
|
for(i_=0;i_<=i-1;i_++)
|
|
tmp[i_]=a[offs+i][i_+i1_];
|
|
for(j=0;j<i;j++)
|
|
{
|
|
v=a[offs+i][offs+j];
|
|
i1_=-offs;
|
|
for(i_=offs;i_<=offs+j;i_++)
|
|
a[offs+j].Set(i_,a[offs+j][i_]+v*tmp[i_+i1_]);
|
|
}
|
|
//--- change values
|
|
v=a[offs+i][offs+i];
|
|
for(i_=offs;i_<offs+i;i_++)
|
|
a[offs+i].Set(i_,v*a[offs+i][i_]);
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i]));
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Recursive code: triangular factor inversion merged with
|
|
//--- UU' or L'L multiplication
|
|
CAblas::AblasSplitLength(a,n,n1,n2);
|
|
//--- form off-diagonal block of trangular inverse
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
for(i_=offs+n1;i_<=offs+n-1;i_++)
|
|
a[offs+i].Set(i_,-1*a[offs+i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n1,n2,a,offs,offs,isupper,false,0,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,isupper,false,0,a,offs,offs+n1);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
for(i_=offs;i_<=offs+n1-1;i_++)
|
|
a[offs+n1+i].Set(i_,-1*a[offs+n1+i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(n2,n1,a,offs,offs,isupper,false,0,a,offs+n1,offs);
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,isupper,false,0,a,offs+n1,offs);
|
|
}
|
|
//--- invert first diagonal block
|
|
SPDMatrixCholeskyInverseRec(a,offs,n1,isupper,tmp);
|
|
//--- update first diagonal block with off-diagonal block,
|
|
//--- update off-diagonal block
|
|
if(isupper)
|
|
{
|
|
//--- function call
|
|
CAblas::RMatrixSyrk(n1,n2,1.0,a,offs,offs+n1,0,1.0,a,offs,offs,isupper);
|
|
//--- function call
|
|
CAblas::RMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,isupper,false,1,a,offs,offs+n1);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CAblas::RMatrixSyrk(n1,n2,1.0,a,offs+n1,offs,1,1.0,a,offs,offs,isupper);
|
|
//--- function call
|
|
CAblas::RMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,isupper,false,1,a,offs+n1,offs);
|
|
}
|
|
//--- invert second diagonal block
|
|
SPDMatrixCholeskyInverseRec(a,offs+n1,n2,isupper,tmp);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Recursive subroutine for HPD inversion. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMatInv::HPDMatrixCholeskyInverseRec(CMatrixComplex &a,const int offs,
|
|
const int n,const bool isupper,
|
|
complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
complex _One=-1.0;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int info2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- object of class
|
|
CMatInvReport rep2;
|
|
//--- check
|
|
if(n<1)
|
|
return;
|
|
//--- base case
|
|
if(n<=CAblas::AblasComplexBlockSize())
|
|
{
|
|
CMatrixTrInverseRec(a,offs,n,isupper,false,tmp,info2,rep2);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute the product U * U'.
|
|
//--- NOTE: we never assume that diagonal of U is real
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
{
|
|
//--- 1x1 matrix
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i].re)+CMath::Sqr(a[offs+i][offs+i].im));
|
|
}
|
|
else
|
|
{
|
|
//--- (I+1)x(I+1) matrix,
|
|
//--- ( A11 A12 ) ( A11^H ) ( A11*A11^H+A12*A12^H A12*A22^H )
|
|
//--- ( ) * ( ) = ( )
|
|
//--- ( A22 ) ( A12^H A22^H ) ( A22*A12^H A22*A22^H )
|
|
//--- A11 is IxI, A22 is 1x1.
|
|
i1_=offs;
|
|
for(i_=0;i_<=i-1;i_++)
|
|
tmp[i_]=CMath::Conj(a[i_+i1_][offs+i]);
|
|
for(j=0;j<i;j++)
|
|
{
|
|
v=a[offs+j][offs+i];
|
|
i1_=-offs;
|
|
for(i_=offs+j;i_<offs+i;i_++)
|
|
a[offs+j].Set(i_,a[offs+j][i_]+v*tmp[i_+i1_]);
|
|
}
|
|
//--- change values
|
|
v=CMath::Conj(a[offs+i][offs+i]);
|
|
for(i_=offs;i_<offs+i;i_++)
|
|
a[i_].Set(offs+i,v*a[i_][offs+i]);
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i].re)+CMath::Sqr(a[offs+i][offs+i].im));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the product L' * L
|
|
//--- NOTE: we never assume that diagonal of L is reall
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
{
|
|
//--- 1x1 matrix
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i].re)+CMath::Sqr(a[offs+i][offs+i].im));
|
|
}
|
|
else
|
|
{
|
|
//--- (I+1)x(I+1) matrix,
|
|
//--- ( A11^H A21^H ) ( A11 ) ( A11^H*A11+A21^H*A21 A21^H*A22 )
|
|
//--- ( ) * ( ) = ( )
|
|
//--- ( A22^H ) ( A21 A22 ) ( A22^H*A21 A22^H*A22 )
|
|
//--- A11 is IxI, A22 is 1x1.
|
|
i1_=offs;
|
|
for(i_=0;i_<=i-1;i_++)
|
|
tmp[i_]=a[offs+i][i_+i1_];
|
|
for(j=0;j<i;j++)
|
|
{
|
|
v=CMath::Conj(a[offs+i][offs+j]);
|
|
i1_=-offs;
|
|
for(i_=offs;i_<=offs+j;i_++)
|
|
a[offs+j].Set(i_,a[offs+j][i_]+v*tmp[i_+i1_]);
|
|
}
|
|
v=CMath::Conj(a[offs+i][offs+i]);
|
|
for(i_=offs;i_<offs+i;i_++)
|
|
a[offs+i].Set(i_,v*a[offs+i][i_]);
|
|
a[offs+i].Set(offs+i,CMath::Sqr(a[offs+i][offs+i].re)+CMath::Sqr(a[offs+i][offs+i].im));
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Recursive code: triangular factor inversion merged with
|
|
//--- UU' or L'L multiplication
|
|
CAblas::AblasComplexSplitLength(a,n,n1,n2);
|
|
//--- form off-diagonal block of trangular inverse
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<n1;i++)
|
|
{
|
|
for(i_=offs+n1;i_<=offs+n-1;i_++)
|
|
a[offs+i].Set(i_,_One*a[offs+i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n1,n2,a,offs,offs,isupper,false,0,a,offs,offs+n1);
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,isupper,false,0,a,offs,offs+n1);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n2;i++)
|
|
{
|
|
for(i_=offs;i_<=offs+n1-1;i_++)
|
|
a[offs+n1+i].Set(i_,_One*a[offs+n1+i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(n2,n1,a,offs,offs,isupper,false,0,a,offs+n1,offs);
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,isupper,false,0,a,offs+n1,offs);
|
|
}
|
|
//--- invert first diagonal block
|
|
HPDMatrixCholeskyInverseRec(a,offs,n1,isupper,tmp);
|
|
//--- update first diagonal block with off-diagonal block,
|
|
//--- update off-diagonal block
|
|
if(isupper)
|
|
{
|
|
//--- function call
|
|
CAblas::CMatrixSyrk(n1,n2,1.0,a,offs,offs+n1,0,1.0,a,offs,offs,isupper);
|
|
//--- function call
|
|
CAblas::CMatrixRightTrsM(n1,n2,a,offs+n1,offs+n1,isupper,false,2,a,offs,offs+n1);
|
|
}
|
|
else
|
|
{
|
|
//--- function call
|
|
CAblas::CMatrixSyrk(n1,n2,1.0,a,offs+n1,offs,2,1.0,a,offs,offs,isupper);
|
|
//--- function call
|
|
CAblas::CMatrixLeftTrsM(n2,n1,a,offs+n1,offs+n1,isupper,false,2,a,offs+n1,offs);
|
|
}
|
|
//--- invert second diagonal block
|
|
HPDMatrixCholeskyInverseRec(a,offs+n1,n2,isupper,tmp);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Singular value decomposition of a bidiagonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
class CBdSingValueDecompose
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static bool BidiagonalSVDDecompositionInternal(double &d[],double &ce[],const int n,const bool isupper,const bool isfractionalaccuracyrequired,CMatrixDouble &u,const int ustart,const int nru,CMatrixDouble &c,const int cstart,const int ncc,CMatrixDouble &vt,const int vstart,const int ncvt);
|
|
static double ExtSignBdSQR(const double a,const double b);
|
|
static void SVD2x2(const double f,const double g,const double h,double &ssmin,double &ssmax);
|
|
static void SVDV2x2(const double f,const double g,const double h,double &ssmin,double &ssmax,double &snr,double &csr,double &snl,double &csl);
|
|
public:
|
|
CBdSingValueDecompose(void);
|
|
~CBdSingValueDecompose(void);
|
|
//--- public methods
|
|
static bool RMatrixBdSVD(double &d[],double &ce[],const int n,const bool isupper,const bool isfractionalaccuracyrequired,CMatrixDouble &u,const int nru,CMatrixDouble &c,const int ncc,CMatrixDouble &vt,const int ncvt);
|
|
static bool BidiagonalSVDDecomposition(double &d[],double &ce[],const int n,const bool isupper,const bool isfractionalaccuracyrequired,CMatrixDouble &u,const int nru,CMatrixDouble &c,const int ncc,CMatrixDouble &vt,const int ncvt);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CBdSingValueDecompose::CBdSingValueDecompose(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CBdSingValueDecompose::~CBdSingValueDecompose(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Singular value decomposition of a bidiagonal matrix (extended |
|
|
//| algorithm) |
|
|
//| The algorithm performs the singular value decomposition of a |
|
|
//| bidiagonal matrix B (upper or lower) representing it as |
|
|
//| B = Q*S*P^T, where Q and P - orthogonal matrices, S - diagonal |
|
|
//| matrix with non-negative elements on the main diagonal, in |
|
|
//| descending order. |
|
|
//| The algorithm finds singular values. In addition, the algorithm |
|
|
//| can calculate matrices Q and P (more precisely, not the matrices,|
|
|
//| but their product with given matrices U and VT - U*Q and |
|
|
//| (P^T)*VT)). Of course, matrices U and VT can be of any type, |
|
|
//| including identity. Furthermore, the algorithm can calculate Q'*C|
|
|
//| (this product is calculated more effectively than U*Q, because |
|
|
//| this calculation operates with rows instead of matrix columns). |
|
|
//| The feature of the algorithm is its ability to find all singular |
|
|
//| values including those which are arbitrarily close to 0 with |
|
|
//| relative accuracy close to machine precision. If the parameter |
|
|
//| IsFractionalAccuracyRequired is set to True, all singular values |
|
|
//| will have high relative accuracy close to machine precision. If |
|
|
//| the parameter is set to False, only the biggest singular value |
|
|
//| will have relative accuracy close to machine precision. The |
|
|
//| absolute error of other singular values is equal to the absolute |
|
|
//| error of the biggest singular value. |
|
|
//| Input parameters: |
|
|
//| D - main diagonal of matrix B. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| E - superdiagonal (or subdiagonal) of matrix B. |
|
|
//| Array whose index ranges within [0..N-2]. |
|
|
//| N - size of matrix B. |
|
|
//| IsUpper - True, if the matrix is upper bidiagonal. |
|
|
//| IsFractionalAccuracyRequired - |
|
|
//| accuracy to search singular values with. |
|
|
//| U - matrix to be multiplied by Q. |
|
|
//| Array whose indexes range within |
|
|
//| [0..NRU-1, 0..N-1]. |
|
|
//| The matrix can be bigger, in that case only the |
|
|
//| submatrix [0..NRU-1, 0..N-1] will be multiplied |
|
|
//| by Q. |
|
|
//| NRU - number of rows in matrix U. |
|
|
//| C - matrix to be multiplied by Q'. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..NCC-1]. |
|
|
//| The matrix can be bigger, in that case only the |
|
|
//| submatrix [0..N-1, 0..NCC-1] will be multiplied |
|
|
//| by Q'. |
|
|
//| NCC - number of columns in matrix C. |
|
|
//| VT - matrix to be multiplied by P^T. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..NCVT-1]. |
|
|
//| The matrix can be bigger, in that case only the |
|
|
//| submatrix [0..N-1, 0..NCVT-1] will be multiplied |
|
|
//| by P^T. |
|
|
//| NCVT - number of columns in matrix VT. |
|
|
//| Output parameters: |
|
|
//| D - singular values of matrix B in descending order. |
|
|
//| U - if NRU>0, contains matrix U*Q. |
|
|
//| VT - if NCVT>0, contains matrix (P^T)*VT. |
|
|
//| C - if NCC>0, contains matrix Q'*C. |
|
|
//| Result: |
|
|
//| True, if the algorithm has converged. |
|
|
//| False, if the algorithm hasn't converged (rare case). |
|
|
//| Additional information: |
|
|
//| The type of convergence is controlled by the internal |
|
|
//| parameter TOL. If the parameter is greater than 0, the |
|
|
//| singular values will have relative accuracy TOL. If TOL<0, |
|
|
//| the singular values will have absolute accuracy |
|
|
//| ABS(TOL)*norm(B). By default, |TOL| falls within the range of|
|
|
//| 10*Epsilon and 100*Epsilon, where Epsilon is the machine |
|
|
//| precision. It is not recommended to use TOL less than |
|
|
//| 10*Epsilon since this will considerably slow down the |
|
|
//| algorithm and may not lead to error decreasing. |
|
|
//| History: |
|
|
//| * 31 March, 2007. |
|
|
//| changed MAXITR from 6 to 12. |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., |
|
|
//| Courant Institute, Argonne National Lab, and Rice University|
|
|
//| October 31, 1999. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CBdSingValueDecompose::RMatrixBdSVD(double &d[],double &ce[],
|
|
const int n,const bool isupper,
|
|
const bool isfractionalaccuracyrequired,
|
|
CMatrixDouble &u,const int nru,
|
|
CMatrixDouble &c,const int ncc,
|
|
CMatrixDouble &vt,const int ncvt)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
//--- create arrays
|
|
double d1[];
|
|
double e1[];
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create copy
|
|
double e[];
|
|
ArrayCopy(e,ce);
|
|
//--- allocation
|
|
ArrayResizeAL(d1,n+1);
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<=n;i_++)
|
|
d1[i_]=d[i_+i1_];
|
|
//--- check
|
|
if(n>1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(e1,n);
|
|
//--- change values
|
|
i1_=-1;
|
|
for(i_=1;i_<n;i_++)
|
|
e1[i_]=e[i_+i1_];
|
|
}
|
|
//--- get result
|
|
result=BidiagonalSVDDecompositionInternal(d1,e1,n,isupper,isfractionalaccuracyrequired,u,0,nru,c,0,ncc,vt,0,ncvt);
|
|
//--- change values
|
|
i1_=1;
|
|
for(i_=0;i_<n;i_++)
|
|
d[i_]=d1[i_+i1_];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Singular value decomposition of a bidiagonal matrix |
|
|
//+------------------------------------------------------------------+
|
|
static bool CBdSingValueDecompose::BidiagonalSVDDecomposition(double &d[],double &ce[],
|
|
const int n,const bool isupper,
|
|
const bool isfractionalaccuracyrequired,
|
|
CMatrixDouble &u,const int nru,
|
|
CMatrixDouble &c,const int ncc,
|
|
CMatrixDouble &vt,const int ncvt)
|
|
{
|
|
//--- create copy
|
|
double e[];
|
|
ArrayCopy(e,ce);
|
|
//--- return result
|
|
return(BidiagonalSVDDecompositionInternal(d,e,n,isupper,isfractionalaccuracyrequired,u,1,nru,c,1,ncc,vt,1,ncvt));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal working subroutine for bidiagonal decomposition |
|
|
//+------------------------------------------------------------------+
|
|
static bool CBdSingValueDecompose::BidiagonalSVDDecompositionInternal(double &d[],double &ce[],
|
|
const int n,const bool isupper,
|
|
const bool isfractionalaccuracyrequired,
|
|
CMatrixDouble &u,const int ustart,
|
|
const int nru,CMatrixDouble &c,
|
|
const int cstart,const int ncc,
|
|
CMatrixDouble &vt,const int vstart,
|
|
const int ncvt)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int idir=0;
|
|
int isub=0;
|
|
int iter=0;
|
|
int j=0;
|
|
int ll=0;
|
|
int lll=0;
|
|
int m=0;
|
|
int maxit=0;
|
|
int oldll=0;
|
|
int oldm=0;
|
|
double abse=0;
|
|
double abss=0;
|
|
double cosl=0;
|
|
double cosr=0;
|
|
double cs=0;
|
|
double eps=0;
|
|
double f=0;
|
|
double g=0;
|
|
double h=0;
|
|
double mu=0;
|
|
double oldcs=0;
|
|
double oldsn=0;
|
|
double r=0;
|
|
double shift=0;
|
|
double sigmn=0;
|
|
double sigmx=0;
|
|
double sinl=0;
|
|
double sinr=0;
|
|
double sll=0;
|
|
double smax=0;
|
|
double smin=0;
|
|
double sminl=0;
|
|
double sminlo=0;
|
|
double sminoa=0;
|
|
double sn=0;
|
|
double thresh=0;
|
|
double tol=0;
|
|
double tolmul=0;
|
|
double unfl=0;
|
|
int maxitr=0;
|
|
bool matrixsplitflag;
|
|
bool iterflag;
|
|
bool rightside;
|
|
bool fwddir;
|
|
double tmp=0;
|
|
int mm1=0;
|
|
int mm0=0;
|
|
bool bchangedir;
|
|
int uend=0;
|
|
int cend=0;
|
|
int vend=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double work0[];
|
|
double work1[];
|
|
double work2[];
|
|
double work3[];
|
|
double utemp[];
|
|
double vttemp[];
|
|
double ctemp[];
|
|
double etemp[];
|
|
//--- create copy
|
|
double e[];
|
|
ArrayCopy(e,ce);
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(n==0)
|
|
return(true);
|
|
//--- check
|
|
if(n==1)
|
|
{
|
|
//--- check
|
|
if(d[1]<0.0)
|
|
{
|
|
d[1]=-d[1];
|
|
//--- check
|
|
if(ncvt>0)
|
|
for(i_=vstart;i_<=vstart+ncvt-1;i_++)
|
|
vt[vstart].Set(i_,-1*vt[vstart][i_]);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- initialization
|
|
ll=0;
|
|
oldsn=0;
|
|
//--- allocation
|
|
ArrayResizeAL(work0,n);
|
|
ArrayResizeAL(work1,n);
|
|
ArrayResizeAL(work2,n);
|
|
ArrayResizeAL(work3,n);
|
|
uend=ustart+(int)MathMax(nru-1,0);
|
|
vend=vstart+(int)MathMax(ncvt-1,0);
|
|
cend=cstart+(int)MathMax(ncc-1,0);
|
|
ArrayResizeAL(utemp,uend+1);
|
|
ArrayResizeAL(vttemp,vend+1);
|
|
ArrayResizeAL(ctemp,cend+1);
|
|
//--- initialization
|
|
maxitr=12;
|
|
rightside=true;
|
|
fwddir=true;
|
|
//--- resize E from N-1 to N
|
|
ArrayResizeAL(etemp,n+1);
|
|
for(i=1;i<n;i++)
|
|
etemp[i]=e[i];
|
|
ArrayResizeAL(e,n+1);
|
|
for(i=1;i<n;i++)
|
|
e[i]=etemp[i];
|
|
e[n]=0;
|
|
idir=0;
|
|
//--- initialization
|
|
eps=CMath::m_machineepsilon;
|
|
unfl=CMath::m_minrealnumber;
|
|
//--- If matrix lower bidiagonal, rotate to be upper bidiagonal
|
|
//--- by applying Givens rotations on the left
|
|
if(!isupper)
|
|
{
|
|
for(i=1;i<n;i++)
|
|
{
|
|
//--- function call
|
|
CRotations::GenerateRotation(d[i],e[i],cs,sn,r);
|
|
d[i]=r;
|
|
e[i]=sn*d[i+1];
|
|
d[i+1]=cs*d[i+1];
|
|
work0[i]=cs;
|
|
work1[i]=sn;
|
|
}
|
|
//--- Update singular vectors if desired
|
|
if(nru>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(fwddir,ustart,uend,1+ustart-1,n+ustart-1,work0,work1,u,utemp);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(fwddir,1+cstart-1,n+cstart-1,cstart,cend,work0,work1,c,ctemp);
|
|
}
|
|
}
|
|
//--- Compute singular values to relative accuracy TOL
|
|
//--- (By setting TOL to be negative, algorithm will compute
|
|
//--- singular values to absolute accuracy ABS(TOL)*norm(input matrix))
|
|
tolmul=MathMax(10,MathMin(100,MathPow(eps,-0.125)));
|
|
tol=tolmul*eps;
|
|
//--- check
|
|
if(!isfractionalaccuracyrequired)
|
|
tol=-tol;
|
|
//--- Compute approximate maximum, minimum singular values
|
|
smax=0;
|
|
for(i=1;i<=n;i++)
|
|
smax=MathMax(smax,MathAbs(d[i]));
|
|
for(i=1;i<n;i++)
|
|
smax=MathMax(smax,MathAbs(e[i]));
|
|
sminl=0;
|
|
//--- check
|
|
if(tol>=0.0)
|
|
{
|
|
//--- Relative accuracy desired
|
|
sminoa=MathAbs(d[1]);
|
|
//--- check
|
|
if(sminoa!=0.0)
|
|
{
|
|
mu=sminoa;
|
|
for(i=2;i<=n;i++)
|
|
{
|
|
mu=MathAbs(d[i])*(mu/(mu+MathAbs(e[i-1])));
|
|
sminoa=MathMin(sminoa,mu);
|
|
//--- check
|
|
if(sminoa==0.0)
|
|
break;
|
|
}
|
|
}
|
|
//--- change values
|
|
sminoa=sminoa/MathSqrt(n);
|
|
thresh=MathMax(tol*sminoa,maxitr*n*n*unfl);
|
|
}
|
|
else
|
|
{
|
|
//--- Absolute accuracy desired
|
|
thresh=MathMax(MathAbs(tol)*smax,maxitr*n*n*unfl);
|
|
}
|
|
//--- Prepare for main iteration loop for the singular values
|
|
//--- (MAXIT is the maximum number of passes through the inner
|
|
//--- loop permitted before nonconvergence signalled.)
|
|
maxit=maxitr*n*n;
|
|
iter=0;
|
|
oldll=-1;
|
|
oldm=-1;
|
|
//--- M points to last element of unconverged part of matrix
|
|
m=n;
|
|
//--- Begin main iteration loop
|
|
while(true)
|
|
{
|
|
//--- Check for convergence or exceeding iteration count
|
|
if(m<=1)
|
|
break;
|
|
//--- check
|
|
if(iter>maxit)
|
|
return(false);
|
|
//--- Find diagonal block of matrix to work on
|
|
if(tol<0.0 && MathAbs(d[m])<=thresh)
|
|
d[m]=0;
|
|
//--- change values
|
|
smax=MathAbs(d[m]);
|
|
smin=smax;
|
|
matrixsplitflag=false;
|
|
for(lll=1;lll<=m-1;lll++)
|
|
{
|
|
ll=m-lll;
|
|
abss=MathAbs(d[ll]);
|
|
abse=MathAbs(e[ll]);
|
|
//--- check
|
|
if(tol<0.0 && abss<=thresh)
|
|
d[ll]=0;
|
|
//--- check
|
|
if(abse<=thresh)
|
|
{
|
|
matrixsplitflag=true;
|
|
break;
|
|
}
|
|
//--- change values
|
|
smin=MathMin(smin,abss);
|
|
smax=MathMax(smax,MathMax(abss,abse));
|
|
}
|
|
//--- check
|
|
if(!matrixsplitflag)
|
|
ll=0;
|
|
else
|
|
{
|
|
//--- Matrix splits since E(LL) = 0
|
|
e[ll]=0;
|
|
//--- check
|
|
if(ll==m-1)
|
|
{
|
|
//--- Convergence of bottom singular value, return to top of loop
|
|
m=m-1;
|
|
continue;
|
|
}
|
|
}
|
|
ll=ll+1;
|
|
//--- E(LL) through E(M-1) are nonzero, E(LL-1) is zero
|
|
if(ll==m-1)
|
|
{
|
|
//--- 2 by 2 block, handle separately
|
|
SVDV2x2(d[m-1],e[m-1],d[m],sigmn,sigmx,sinr,cosr,sinl,cosl);
|
|
d[m-1]=sigmx;
|
|
e[m-1]=0;
|
|
d[m]=sigmn;
|
|
//--- Compute singular vectors, if desired
|
|
if(ncvt>0)
|
|
{
|
|
mm0=m+(vstart-1);
|
|
mm1=m-1+(vstart-1);
|
|
//--- swap
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vttemp[i_]=cosr*vt[mm1][i_];
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vttemp[i_]=vttemp[i_]+sinr*vt[mm0][i_];
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vt[mm0].Set(i_,cosr*vt[mm0][i_]);
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vt[mm0].Set(i_,vt[mm0][i_]-sinr*vt[mm1][i_]);
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vt[mm1].Set(i_,vttemp[i_]);
|
|
}
|
|
//--- check
|
|
if(nru>0)
|
|
{
|
|
mm0=m+ustart-1;
|
|
mm1=m-1+ustart-1;
|
|
//--- swap
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
utemp[i_]=cosl*u[i_][mm1];
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
utemp[i_]=utemp[i_]+sinl*u[i_][mm0];
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
u[i_].Set(mm0,cosl*u[i_][mm0]);
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
u[i_].Set(mm0,u[i_][mm0]-sinl*u[i_][mm1]);
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
u[i_].Set(mm1,utemp[i_]);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
mm0=m+cstart-1;
|
|
mm1=m-1+cstart-1;
|
|
//--- swap
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
ctemp[i_]=cosl*c[mm1][i_];
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
ctemp[i_]=ctemp[i_]+sinl*c[mm0][i_];
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
c[mm0].Set(i_,cosl*c[mm0][i_]);
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
c[mm0].Set(i_,c[mm0][i_]-sinl*c[mm1][i_]);
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
c[mm1].Set(i_,ctemp[i_]);
|
|
}
|
|
m=m-2;
|
|
continue;
|
|
}
|
|
//--- If working on new submatrix, choose shift direction
|
|
//--- (from larger end diagonal element towards smaller)
|
|
//--- Previously was
|
|
//--- "if (LL>OLDM) or (M<OLDLL) then"
|
|
//--- fixed thanks to Michael Rolle < m@rolle.name >
|
|
//--- Very strange that LAPACK still contains it.
|
|
bchangedir=false;
|
|
//--- check
|
|
if(idir==1 && MathAbs(d[ll])<1.0E-3*MathAbs(d[m]))
|
|
bchangedir=true;
|
|
//--- check
|
|
if(idir==2 && MathAbs(d[m])<1.0E-3*MathAbs(d[ll]))
|
|
bchangedir=true;
|
|
//--- check
|
|
if(ll!=oldll || m!=oldm || bchangedir)
|
|
{
|
|
//--- check
|
|
if(MathAbs(d[ll])>=MathAbs(d[m]))
|
|
{
|
|
//--- Chase bulge from top(big end) to bottom(small end)
|
|
idir=1;
|
|
}
|
|
else
|
|
{
|
|
//--- Chase bulge from bottom (big end) to top (small end)
|
|
idir=2;
|
|
}
|
|
}
|
|
//--- Apply convergence tests
|
|
if(idir==1)
|
|
{
|
|
//--- Run convergence test in forward direction
|
|
//--- First apply standard test to bottom of matrix
|
|
if(MathAbs(e[m-1])<=MathAbs(tol)*MathAbs(d[m]) || (tol<0.0 && MathAbs(e[m-1])<=thresh))
|
|
{
|
|
e[m-1]=0;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(tol>=0.0)
|
|
{
|
|
//--- If relative accuracy desired,
|
|
//--- apply convergence criterion forward
|
|
mu=MathAbs(d[ll]);
|
|
sminl=mu;
|
|
iterflag=false;
|
|
for(lll=ll;lll<=m-1;lll++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(e[lll])<=tol*mu)
|
|
{
|
|
e[lll]=0;
|
|
iterflag=true;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
//--- change values
|
|
sminlo=sminl;
|
|
mu=MathAbs(d[lll+1])*(mu/(mu+MathAbs(e[lll])));
|
|
sminl=MathMin(sminl,mu);
|
|
}
|
|
//--- check
|
|
if(iterflag)
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Run convergence test in backward direction
|
|
//--- First apply standard test to top of matrix
|
|
if(MathAbs(e[ll])<=MathAbs(tol)*MathAbs(d[ll]) || (tol<0.0 && MathAbs(e[ll])<=thresh))
|
|
{
|
|
e[ll]=0;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(tol>=0.0)
|
|
{
|
|
//--- If relative accuracy desired,
|
|
//--- apply convergence criterion backward
|
|
mu=MathAbs(d[m]);
|
|
sminl=mu;
|
|
iterflag=false;
|
|
for(lll=m-1;lll>=ll;lll--)
|
|
{
|
|
//--- check
|
|
if(MathAbs(e[lll])<=(double)(tol*mu))
|
|
{
|
|
e[lll]=0;
|
|
iterflag=true;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
sminlo=sminl;
|
|
mu=MathAbs(d[lll])*(mu/(mu+MathAbs(e[lll])));
|
|
sminl=MathMin(sminl,mu);
|
|
}
|
|
//--- check
|
|
if(iterflag)
|
|
continue;
|
|
}
|
|
}
|
|
//--- change values
|
|
oldll=ll;
|
|
oldm=m;
|
|
//--- Compute shift. First, test if shifting would ruin relative
|
|
//--- accuracy, and if so set the shift to zero.
|
|
if(tol>=0.0 && n*tol*(sminl/smax)<=MathMax(eps,0.01*tol))
|
|
{
|
|
//--- Use a zero shift to avoid loss of relative accuracy
|
|
shift=0;
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the shift from 2-by-2 block at end of matrix
|
|
if(idir==1)
|
|
{
|
|
sll=MathAbs(d[ll]);
|
|
SVD2x2(d[m-1],e[m-1],d[m],shift,r);
|
|
}
|
|
else
|
|
{
|
|
sll=MathAbs(d[m]);
|
|
SVD2x2(d[ll],e[ll],d[ll+1],shift,r);
|
|
}
|
|
//--- Test if shift negligible, and if so set to zero
|
|
if(sll>0.0)
|
|
{
|
|
//--- check
|
|
if(CMath::Sqr(shift/sll)<eps)
|
|
shift=0;
|
|
}
|
|
}
|
|
//--- Increment iteration count
|
|
iter=iter+m-ll;
|
|
//--- If SHIFT = 0, do simplified QR iteration
|
|
if(shift==0.0)
|
|
{
|
|
//--- check
|
|
if(idir==1)
|
|
{
|
|
//--- Chase bulge from top to bottom
|
|
//--- Save cosines and sines for later singular vector updates
|
|
cs=1;
|
|
oldcs=1;
|
|
for(i=ll;i<m;i++)
|
|
{
|
|
//--- function call
|
|
CRotations::GenerateRotation(d[i]*cs,e[i],cs,sn,r);
|
|
//--- check
|
|
if(i>ll)
|
|
e[i-1]=oldsn*r;
|
|
//--- function call
|
|
CRotations::GenerateRotation(oldcs*r,d[i+1]*sn,oldcs,oldsn,tmp);
|
|
//--- change values
|
|
d[i]=tmp;
|
|
work0[i-ll+1]=cs;
|
|
work1[i-ll+1]=sn;
|
|
work2[i-ll+1]=oldcs;
|
|
work3[i-ll+1]=oldsn;
|
|
}
|
|
//--- change values
|
|
h=d[m]*cs;
|
|
d[m]=h*oldcs;
|
|
e[m-1]=h*oldsn;
|
|
//--- Update singular vectors
|
|
if(ncvt>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(fwddir,ll+vstart-1,m+vstart-1,vstart,vend,work0,work1,vt,vttemp);
|
|
}
|
|
//--- check
|
|
if(nru>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(fwddir,ustart,uend,ll+ustart-1,m+ustart-1,work2,work3,u,utemp);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(fwddir,ll+cstart-1,m+cstart-1,cstart,cend,work2,work3,c,ctemp);
|
|
}
|
|
//--- Check for convergence
|
|
if(MathAbs(e[m-1])<=thresh)
|
|
e[m-1]=0;
|
|
}
|
|
else
|
|
{
|
|
//--- Chase bulge from bottom to top
|
|
//--- Save cosines and sines for later singular vector updates
|
|
cs=1;
|
|
oldcs=1;
|
|
for(i=m;i>=ll+1;i--)
|
|
{
|
|
//--- function call
|
|
CRotations::GenerateRotation(d[i]*cs,e[i-1],cs,sn,r);
|
|
//--- check
|
|
if(i<m)
|
|
e[i]=oldsn*r;
|
|
//--- function call
|
|
CRotations::GenerateRotation(oldcs*r,d[i-1]*sn,oldcs,oldsn,tmp);
|
|
//--- change values
|
|
d[i]=tmp;
|
|
work0[i-ll]=cs;
|
|
work1[i-ll]=-sn;
|
|
work2[i-ll]=oldcs;
|
|
work3[i-ll]=-oldsn;
|
|
}
|
|
//--- change values
|
|
h=d[ll]*cs;
|
|
d[ll]=h*oldcs;
|
|
e[ll]=h*oldsn;
|
|
//--- Update singular vectors
|
|
if(ncvt>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(!fwddir,ll+vstart-1,m+vstart-1,vstart,vend,work2,work3,vt,vttemp);
|
|
}
|
|
//--- check
|
|
if(nru>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(!fwddir,ustart,uend,ll+ustart-1,m+ustart-1,work0,work1,u,utemp);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(!fwddir,ll+cstart-1,m+cstart-1,cstart,cend,work0,work1,c,ctemp);
|
|
}
|
|
//--- Check for convergence
|
|
if(MathAbs(e[ll])<=thresh)
|
|
e[ll]=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Use nonzero shift
|
|
if(idir==1)
|
|
{
|
|
//--- Chase bulge from top to bottom
|
|
//--- Save cosines and sines for later singular vector updates
|
|
f=(MathAbs(d[ll])-shift)*(ExtSignBdSQR(1,d[ll])+shift/d[ll]);
|
|
g=e[ll];
|
|
for(i=ll;i<m;i++)
|
|
{
|
|
//--- function call
|
|
CRotations::GenerateRotation(f,g,cosr,sinr,r);
|
|
//--- check
|
|
if(i>ll)
|
|
e[i-1]=r;
|
|
//--- change values
|
|
f=cosr*d[i]+sinr*e[i];
|
|
e[i]=cosr*e[i]-sinr*d[i];
|
|
g=sinr*d[i+1];
|
|
d[i+1]=cosr*d[i+1];
|
|
//--- function call
|
|
CRotations::GenerateRotation(f,g,cosl,sinl,r);
|
|
//--- change values
|
|
d[i]=r;
|
|
f=cosl*e[i]+sinl*d[i+1];
|
|
d[i+1]=cosl*d[i+1]-sinl*e[i];
|
|
//--- check
|
|
if(i<m-1)
|
|
{
|
|
g=sinl*e[i+1];
|
|
e[i+1]=cosl*e[i+1];
|
|
}
|
|
//--- change values
|
|
work0[i-ll+1]=cosr;
|
|
work1[i-ll+1]=sinr;
|
|
work2[i-ll+1]=cosl;
|
|
work3[i-ll+1]=sinl;
|
|
}
|
|
e[m-1]=f;
|
|
//--- Update singular vectors
|
|
if(ncvt>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(fwddir,ll+vstart-1,m+vstart-1,vstart,vend,work0,work1,vt,vttemp);
|
|
}
|
|
//--- check
|
|
if(nru>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(fwddir,ustart,uend,ll+ustart-1,m+ustart-1,work2,work3,u,utemp);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(fwddir,ll+cstart-1,m+cstart-1,cstart,cend,work2,work3,c,ctemp);
|
|
}
|
|
//--- Check for convergence
|
|
if(MathAbs(e[m-1])<=thresh)
|
|
e[m-1]=0;
|
|
}
|
|
else
|
|
{
|
|
//--- Chase bulge from bottom to top
|
|
//--- Save cosines and sines for later singular vector updates
|
|
f=(MathAbs(d[m])-shift)*(ExtSignBdSQR(1,d[m])+shift/d[m]);
|
|
g=e[m-1];
|
|
for(i=m;i>=ll+1;i--)
|
|
{
|
|
//--- function call
|
|
CRotations::GenerateRotation(f,g,cosr,sinr,r);
|
|
//--- check
|
|
if(i<m)
|
|
e[i]=r;
|
|
//--- change values
|
|
f=cosr*d[i]+sinr*e[i-1];
|
|
e[i-1]=cosr*e[i-1]-sinr*d[i];
|
|
g=sinr*d[i-1];
|
|
d[i-1]=cosr*d[i-1];
|
|
//--- function call
|
|
CRotations::GenerateRotation(f,g,cosl,sinl,r);
|
|
//--- change values
|
|
d[i]=r;
|
|
f=cosl*e[i-1]+sinl*d[i-1];
|
|
d[i-1]=cosl*d[i-1]-sinl*e[i-1];
|
|
//--- check
|
|
if(i>ll+1)
|
|
{
|
|
g=sinl*e[i-2];
|
|
e[i-2]=cosl*e[i-2];
|
|
}
|
|
//--- change values
|
|
work0[i-ll]=cosr;
|
|
work1[i-ll]=-sinr;
|
|
work2[i-ll]=cosl;
|
|
work3[i-ll]=-sinl;
|
|
}
|
|
e[ll]=f;
|
|
//--- Check for convergence
|
|
if(MathAbs(e[ll])<=thresh)
|
|
e[ll]=0;
|
|
//--- Update singular vectors if desired
|
|
if(ncvt>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(!fwddir,ll+vstart-1,m+vstart-1,vstart,vend,work2,work3,vt,vttemp);
|
|
}
|
|
//--- check
|
|
if(nru>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheRight(!fwddir,ustart,uend,ll+ustart-1,m+ustart-1,work0,work1,u,utemp);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
//--- function call
|
|
CRotations::ApplyRotationsFromTheLeft(!fwddir,ll+cstart-1,m+cstart-1,cstart,cend,work0,work1,c,ctemp);
|
|
}
|
|
}
|
|
}
|
|
//--- QR iteration finished, go back and check convergence
|
|
continue;
|
|
}
|
|
//--- All singular values converged, so make them positive
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(d[i]<0.0)
|
|
{
|
|
d[i]=-d[i];
|
|
//--- Change sign of singular vectors, if desired
|
|
if(ncvt>0)
|
|
{
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vt[i+vstart-1].Set(i_,-1*vt[i+vstart-1][i_]);
|
|
}
|
|
}
|
|
}
|
|
//--- Sort the singular values into decreasing order (insertion sort on
|
|
//--- singular values, but only one transposition per singular vector)
|
|
for(i=1;i<n;i++)
|
|
{
|
|
//--- Scan for smallest D(I)
|
|
isub=1;
|
|
smin=d[1];
|
|
for(j=2;j<=n+1-i;j++)
|
|
{
|
|
//--- check
|
|
if(d[j]<=smin)
|
|
{
|
|
isub=j;
|
|
smin=d[j];
|
|
}
|
|
}
|
|
//--- check
|
|
if(isub!=n+1-i)
|
|
{
|
|
//--- Swap singular values and vectors
|
|
d[isub]=d[n+1-i];
|
|
d[n+1-i]=smin;
|
|
//--- check
|
|
if(ncvt>0)
|
|
{
|
|
j=n+1-i;
|
|
//--- swap
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vttemp[i_]=vt[isub+vstart-1][i_];
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vt[isub+vstart-1].Set(i_,vt[j+vstart-1][i_]);
|
|
for(i_=vstart;i_<=vend;i_++)
|
|
vt[j+vstart-1].Set(i_,vttemp[i_]);
|
|
}
|
|
if(nru>0)
|
|
{
|
|
j=n+1-i;
|
|
//--- swap
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
utemp[i_]=u[i_][isub+ustart-1];
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
u[i_].Set(isub+ustart-1,u[i_][j+ustart-1]);
|
|
for(i_=ustart;i_<=uend;i_++)
|
|
u[i_].Set(j+ustart-1,utemp[i_]);
|
|
}
|
|
//--- check
|
|
if(ncc>0)
|
|
{
|
|
j=n+1-i;
|
|
//--- swap
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
ctemp[i_]=c[isub+cstart-1][i_];
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
c[isub+cstart-1].Set(i_,c[j+cstart-1][i_]);
|
|
for(i_=cstart;i_<=cend;i_++)
|
|
c[j+cstart-1].Set(i_,ctemp[i_]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static double CBdSingValueDecompose::ExtSignBdSQR(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);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CBdSingValueDecompose::SVD2x2(const double f,const double g,
|
|
const double h,double &ssmin,
|
|
double &ssmax)
|
|
{
|
|
//--- create variables
|
|
double aas=0;
|
|
double at=0;
|
|
double au=0;
|
|
double c=0;
|
|
double fa=0;
|
|
double fhmn=0;
|
|
double fhmx=0;
|
|
double ga=0;
|
|
double ha=0;
|
|
//--- initialization
|
|
ssmin=0;
|
|
ssmax=0;
|
|
fa=MathAbs(f);
|
|
ga=MathAbs(g);
|
|
ha=MathAbs(h);
|
|
fhmn=MathMin(fa,ha);
|
|
fhmx=MathMax(fa,ha);
|
|
//--- check
|
|
if(fhmn==0.0)
|
|
{
|
|
ssmin=0;
|
|
//--- check
|
|
if(fhmx==0.0)
|
|
ssmax=ga;
|
|
else
|
|
ssmax=MathMax(fhmx,ga)*MathSqrt(1+CMath::Sqr(MathMin(fhmx,ga)/MathMax(fhmx,ga)));
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(ga<fhmx)
|
|
{
|
|
//--- change values
|
|
aas=1+fhmn/fhmx;
|
|
at=(fhmx-fhmn)/fhmx;
|
|
au=CMath::Sqr(ga/fhmx);
|
|
c=2/(MathSqrt(aas*aas+au)+MathSqrt(at*at+au));
|
|
ssmin=fhmn*c;
|
|
ssmax=fhmx/c;
|
|
}
|
|
else
|
|
{
|
|
au=fhmx/ga;
|
|
//--- check
|
|
if(au==0.0)
|
|
{
|
|
//--- Avoid possible harmful underflow if exponent range
|
|
//--- asymmetric (true SSMIN may not underflow even if
|
|
//--- AU underflows)
|
|
ssmin=fhmn*fhmx/ga;
|
|
ssmax=ga;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
aas=1+fhmn/fhmx;
|
|
at=(fhmx-fhmn)/fhmx;
|
|
c=1/(MathSqrt(1+CMath::Sqr(aas*au))+MathSqrt(1+CMath::Sqr(at*au)));
|
|
ssmin=fhmn*c*au;
|
|
ssmin=ssmin+ssmin;
|
|
ssmax=ga/(c+c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CBdSingValueDecompose::SVDV2x2(const double f,const double g,
|
|
const double h,double &ssmin,
|
|
double &ssmax,double &snr,
|
|
double &csr,double &snl,
|
|
double &csl)
|
|
{
|
|
//--- create variables
|
|
bool gasmal;
|
|
bool swp;
|
|
int pmax=0;
|
|
double a=0;
|
|
double clt=0;
|
|
double crt=0;
|
|
double d=0;
|
|
double fa=0;
|
|
double ft=0;
|
|
double ga=0;
|
|
double gt=0;
|
|
double ha=0;
|
|
double ht=0;
|
|
double l=0;
|
|
double m=0;
|
|
double mm=0;
|
|
double r=0;
|
|
double s=0;
|
|
double slt=0;
|
|
double srt=0;
|
|
double t=0;
|
|
double temp=0;
|
|
double tsign=0;
|
|
double tt=0;
|
|
double v=0;
|
|
//--- initialization
|
|
ssmin=0;
|
|
ssmax=0;
|
|
snr=0;
|
|
csr=0;
|
|
snl=0;
|
|
csl=0;
|
|
ft=f;
|
|
fa=MathAbs(ft);
|
|
ht=h;
|
|
ha=MathAbs(h);
|
|
clt=0;
|
|
crt=0;
|
|
slt=0;
|
|
srt=0;
|
|
tsign=0;
|
|
//--- PMAX points to the maximum absolute element of matrix
|
|
//--- PMAX = 1 if F largest in absolute values
|
|
//--- PMAX = 2 if G largest in absolute values
|
|
//--- PMAX = 3 if H largest in absolute values
|
|
pmax=1;
|
|
swp=ha>fa;
|
|
//--- check
|
|
if(swp)
|
|
{
|
|
//--- Now FA .ge. HA
|
|
pmax=3;
|
|
temp=ft;
|
|
ft=ht;
|
|
ht=temp;
|
|
temp=fa;
|
|
fa=ha;
|
|
ha=temp;
|
|
}
|
|
gt=g;
|
|
ga=MathAbs(gt);
|
|
//--- check
|
|
if(ga==0.0)
|
|
{
|
|
//--- Diagonal matrix
|
|
ssmin=ha;
|
|
ssmax=fa;
|
|
clt=1;
|
|
crt=1;
|
|
slt=0;
|
|
srt=0;
|
|
}
|
|
else
|
|
{
|
|
gasmal=true;
|
|
//--- check
|
|
if(ga>fa)
|
|
{
|
|
pmax=2;
|
|
//--- check
|
|
if(fa/ga<CMath::m_machineepsilon)
|
|
{
|
|
//--- Case of very large GA
|
|
gasmal=false;
|
|
ssmax=ga;
|
|
//--- check
|
|
if(ha>1.0)
|
|
{
|
|
v=ga/ha;
|
|
ssmin=fa/v;
|
|
}
|
|
else
|
|
{
|
|
v=fa/ga;
|
|
ssmin=v*ha;
|
|
}
|
|
//--- change values
|
|
clt=1;
|
|
slt=ht/gt;
|
|
srt=1;
|
|
crt=ft/gt;
|
|
}
|
|
}
|
|
//--- check
|
|
if(gasmal)
|
|
{
|
|
//--- Normal case
|
|
d=fa-ha;
|
|
//--- check
|
|
if(d==fa)
|
|
l=1;
|
|
else
|
|
l=d/fa;
|
|
//--- change values
|
|
m=gt/ft;
|
|
t=2-l;
|
|
mm=m*m;
|
|
tt=t*t;
|
|
s=MathSqrt(tt+mm);
|
|
//--- check
|
|
if(l==0.0)
|
|
r=MathAbs(m);
|
|
else
|
|
r=MathSqrt(l*l+mm);
|
|
//--- change values
|
|
a=0.5*(s+r);
|
|
ssmin=ha/a;
|
|
ssmax=fa*a;
|
|
//--- check
|
|
if(mm==0.0)
|
|
{
|
|
//--- Note that M is very tiny
|
|
if(l==0.0)
|
|
t=ExtSignBdSQR(2,ft)*ExtSignBdSQR(1,gt);
|
|
else
|
|
t=gt/ExtSignBdSQR(d,ft)+m/t;
|
|
}
|
|
else
|
|
t=(m/(s+t)+m/(r+l))*(1+a);
|
|
//--- change values
|
|
l=MathSqrt(t*t+4);
|
|
crt=2/l;
|
|
srt=t/l;
|
|
clt=(crt+srt*m)/a;
|
|
v=ht/ft;
|
|
slt=v*srt/a;
|
|
}
|
|
}
|
|
//--- check
|
|
if(swp)
|
|
{
|
|
csl=srt;
|
|
snl=crt;
|
|
csr=slt;
|
|
snr=clt;
|
|
}
|
|
else
|
|
{
|
|
csl=clt;
|
|
snl=slt;
|
|
csr=crt;
|
|
snr=srt;
|
|
}
|
|
//--- Correct signs of SSMAX and SSMIN
|
|
if(pmax==1)
|
|
tsign=ExtSignBdSQR(1,csr)*ExtSignBdSQR(1,csl)*ExtSignBdSQR(1,f);
|
|
//--- check
|
|
if(pmax==2)
|
|
tsign=ExtSignBdSQR(1,snr)*ExtSignBdSQR(1,csl)*ExtSignBdSQR(1,g);
|
|
//--- check
|
|
if(pmax==3)
|
|
tsign=ExtSignBdSQR(1,snr)*ExtSignBdSQR(1,snl)*ExtSignBdSQR(1,h);
|
|
//--- get result
|
|
ssmax=ExtSignBdSQR(ssmax,tsign);
|
|
ssmin=ExtSignBdSQR(ssmin,tsign*ExtSignBdSQR(1,f)*ExtSignBdSQR(1,h));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Singular value decomposition |
|
|
//+------------------------------------------------------------------+
|
|
class CSingValueDecompose
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CSingValueDecompose(void);
|
|
~CSingValueDecompose(void);
|
|
//--- method
|
|
static bool RMatrixSVD(CMatrixDouble &ca,const int m,const int n,const int uneeded,const int vtneeded,const int additionalmemory,double &w[],CMatrixDouble &u,CMatrixDouble &vt);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSingValueDecompose::CSingValueDecompose(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSingValueDecompose::~CSingValueDecompose(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Singular value decomposition of a rectangular matrix. |
|
|
//| The algorithm calculates the singular value decomposition of a |
|
|
//| matrix of size MxN: A = U * S * V^T |
|
|
//| The algorithm finds the singular values and, optionally, matrices|
|
|
//| U and V^T. The algorithm can find both first min(M,N) columns of |
|
|
//| matrix U and rows of matrix V^T (singular vectors), and matrices |
|
|
//| U and V^T wholly (of sizes MxM and NxN respectively). |
|
|
//| Take into account that the subroutine does not return matrix V |
|
|
//| but V^T. |
|
|
//| Input parameters: |
|
|
//| A - matrix to be decomposed. |
|
|
//| Array whose indexes range within |
|
|
//| [0..M-1, 0..N-1]. |
|
|
//| M - number of rows in matrix A. |
|
|
//| N - number of columns in matrix A. |
|
|
//| UNeeded - 0, 1 or 2. See the description of the |
|
|
//| parameter U. |
|
|
//| VTNeeded - 0, 1 or 2. See the description of the |
|
|
//| parameter VT. |
|
|
//| AdditionalMemory - |
|
|
//| If the parameter: |
|
|
//| * equals 0, the algorithm doesn?t use |
|
|
//| additional memory (lower requirements, |
|
|
//| lower performance). |
|
|
//| * equals 1, the algorithm uses additional |
|
|
//| memory of size min(M,N)*min(M,N) of real |
|
|
//| numbers. It often speeds up the algorithm.|
|
|
//| * equals 2, the algorithm uses additional |
|
|
//| memory of size M*min(M,N) of real numbers.|
|
|
//| It allows to get a maximum performance. |
|
|
//| The recommended value of the parameter is 2. |
|
|
//| Output parameters: |
|
|
//| W - contains singular values in descending order.|
|
|
//| U - if UNeeded=0, U isn't changed, the left |
|
|
//| singular vectors are not calculated. |
|
|
//| if Uneeded=1, U contains left singular |
|
|
//| vectors (first min(M,N) columns of matrix U).|
|
|
//| Array whose indexes range within |
|
|
//| [0..M-1, 0..Min(M,N)-1]. if UNeeded=2, U |
|
|
//| contains matrix U wholly. Array whose indexes|
|
|
//| range within [0..M-1, 0..M-1]. |
|
|
//| VT - if VTNeeded=0, VT isn?t changed, the right |
|
|
//| singular vectors are not calculated. |
|
|
//| if VTNeeded=1, VT contains right singular |
|
|
//| vectors (first min(M,N) rows of matrix V^T). |
|
|
//| Array whose indexes range within |
|
|
//| [0..min(M,N)-1, 0..N-1]. if VTNeeded=2, VT |
|
|
//| contains matrix V^T wholly. Array whose |
|
|
//| indexes range within [0..N-1, 0..N-1]. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CSingValueDecompose::RMatrixSVD(CMatrixDouble &ca,const int m,
|
|
const int n,const int uneeded,
|
|
const int vtneeded,
|
|
const int additionalmemory,
|
|
double &w[],CMatrixDouble &u,
|
|
CMatrixDouble &vt)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool isupper;
|
|
int minmn=0;
|
|
int ncu=0;
|
|
int nrvt=0;
|
|
int nru=0;
|
|
int ncvt=0;
|
|
int i=0;
|
|
int j=0;
|
|
//--- create arrays
|
|
double tauq[];
|
|
double taup[];
|
|
double tau[];
|
|
double e[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble t2;
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(m==0 || n==0)
|
|
return(true);
|
|
//--- check
|
|
if(!CAp::Assert(uneeded>=0 && uneeded<=2,__FUNCTION__+": wrong parameters!"))
|
|
return(false);
|
|
//--- check
|
|
if(!CAp::Assert(vtneeded>=0 && vtneeded<=2,__FUNCTION__+": wrong parameters!"))
|
|
return(false);
|
|
//--- check
|
|
if(!CAp::Assert(additionalmemory>=0 && additionalmemory<=2,__FUNCTION__+": wrong parameters!"))
|
|
return(false);
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
ArrayResizeAL(w,minmn+1);
|
|
ncu=0;
|
|
nru=0;
|
|
//--- check
|
|
if(uneeded==1)
|
|
{
|
|
nru=m;
|
|
ncu=minmn;
|
|
u.Resize(nru,ncu);
|
|
}
|
|
//--- check
|
|
if(uneeded==2)
|
|
{
|
|
nru=m;
|
|
ncu=m;
|
|
u.Resize(nru,ncu);
|
|
}
|
|
nrvt=0;
|
|
ncvt=0;
|
|
//--- check
|
|
if(vtneeded==1)
|
|
{
|
|
nrvt=minmn;
|
|
ncvt=n;
|
|
vt.Resize(nrvt,ncvt);
|
|
}
|
|
//--- check
|
|
if(vtneeded==2)
|
|
{
|
|
nrvt=n;
|
|
ncvt=n;
|
|
vt.Resize(nrvt,ncvt);
|
|
}
|
|
//--- M much larger than N
|
|
//--- Use bidiagonal reduction with QR-decomposition
|
|
if((double)(m)>(double)(1.6*n))
|
|
{
|
|
//--- check
|
|
if(uneeded==0)
|
|
{
|
|
//-- No left singular vectors to be computed
|
|
COrtFac::RMatrixQR(a,m,n,tau);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- function call
|
|
COrtFac::RMatrixBD(a,n,n,tauq,taup);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackPT(a,n,n,taup,nrvt,vt);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackDiagonals(a,n,n,isupper,w,e);
|
|
//--- return result
|
|
return(CBdSingValueDecompose::RMatrixBdSVD(w,e,n,isupper,false,u,0,a,0,vt,ncvt));
|
|
}
|
|
else
|
|
{
|
|
//--- Left singular vectors (may be full matrix U) to be computed
|
|
COrtFac::RMatrixQR(a,m,n,tau);
|
|
//--- function call
|
|
COrtFac::RMatrixQRUnpackQ(a,m,n,tau,ncu,u);
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- function call
|
|
COrtFac::RMatrixBD(a,n,n,tauq,taup);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackPT(a,n,n,taup,nrvt,vt);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackDiagonals(a,n,n,isupper,w,e);
|
|
//--- check
|
|
if(additionalmemory<1)
|
|
{
|
|
//--- No additional memory can be used
|
|
COrtFac::RMatrixBDMultiplyByQ(a,n,n,tauq,u,m,n,true,false);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,n,isupper,false,u,m,a,0,vt,ncvt);
|
|
}
|
|
else
|
|
{
|
|
//--- Large U. Transforming intermediate matrix T2
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(a,n,n,tauq,n,t2);
|
|
//--- function call
|
|
CBlas::CopyMatrix(u,0,m-1,0,n-1,a,0,m-1,0,n-1);
|
|
//--- function call
|
|
CBlas::InplaceTranspose(t2,0,n-1,0,n-1,work);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,n,isupper,false,u,0,t2,n,vt,ncvt);
|
|
//--- function call
|
|
CBlas::MatrixMatrixMultiply(a,0,m-1,0,n-1,false,t2,0,n-1,0,n-1,true,1.0,u,0,m-1,0,n-1,0.0,work);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- N much larger than M
|
|
//--- Use bidiagonal reduction with LQ-decomposition
|
|
if((double)(n)>(double)(1.6*m))
|
|
{
|
|
//--- check
|
|
if(vtneeded==0)
|
|
{
|
|
//--- No right singular vectors to be computed
|
|
COrtFac::RMatrixLQ(a,m,n,tau);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=i+1;j<=m-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- function call
|
|
COrtFac::RMatrixBD(a,m,m,tauq,taup);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(a,m,m,tauq,ncu,u);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackDiagonals(a,m,m,isupper,w,e);
|
|
ArrayResizeAL(work,m+1);
|
|
//--- function call
|
|
CBlas::InplaceTranspose(u,0,nru-1,0,ncu-1,work);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,m,isupper,false,a,0,u,nru,vt,0);
|
|
//--- function call
|
|
CBlas::InplaceTranspose(u,0,nru-1,0,ncu-1,work);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
else
|
|
{
|
|
//--- Right singular vectors (may be full matrix VT) to be computed
|
|
COrtFac::RMatrixLQ(a,m,n,tau);
|
|
//--- function call
|
|
COrtFac::RMatrixLQUnpackQ(a,m,n,tau,nrvt,vt);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=i+1;j<=m-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- function call
|
|
COrtFac::RMatrixBD(a,m,m,tauq,taup);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(a,m,m,tauq,ncu,u);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackDiagonals(a,m,m,isupper,w,e);
|
|
ArrayResizeAL(work,MathMax(m,n)+1);
|
|
//--- function call
|
|
CBlas::InplaceTranspose(u,0,nru-1,0,ncu-1,work);
|
|
//--- check
|
|
if(additionalmemory<1)
|
|
{
|
|
//--- No additional memory available
|
|
COrtFac::RMatrixBDMultiplyByP(a,m,m,taup,vt,m,n,false,true);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,m,isupper,false,a,0,u,nru,vt,n);
|
|
}
|
|
else
|
|
{
|
|
//--- Large VT. Transforming intermediate matrix T2
|
|
COrtFac::RMatrixBDUnpackPT(a,m,m,taup,m,t2);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,m,isupper,false,a,0,u,nru,t2,m);
|
|
//--- function call
|
|
CBlas::CopyMatrix(vt,0,m-1,0,n-1,a,0,m-1,0,n-1);
|
|
//--- function call
|
|
CBlas::MatrixMatrixMultiply(t2,0,m-1,0,m-1,false,a,0,m-1,0,n-1,false,1.0,vt,0,m-1,0,n-1,0.0,work);
|
|
}
|
|
//--- function call
|
|
CBlas::InplaceTranspose(u,0,nru-1,0,ncu-1,work);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- M<=N
|
|
//--- We can use inplace transposition of U to get rid of columnwise operations
|
|
if(m<=n)
|
|
{
|
|
//--- function call
|
|
COrtFac::RMatrixBD(a,m,n,tauq,taup);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(a,m,n,tauq,ncu,u);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackPT(a,m,n,taup,nrvt,vt);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackDiagonals(a,m,n,isupper,w,e);
|
|
ArrayResizeAL(work,m+1);
|
|
//--- function call
|
|
CBlas::InplaceTranspose(u,0,nru-1,0,ncu-1,work);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,minmn,isupper,false,a,0,u,nru,vt,ncvt);
|
|
//--- function call
|
|
CBlas::InplaceTranspose(u,0,nru-1,0,ncu-1,work);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Simple bidiagonal reduction
|
|
COrtFac::RMatrixBD(a,m,n,tauq,taup);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(a,m,n,tauq,ncu,u);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackPT(a,m,n,taup,nrvt,vt);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackDiagonals(a,m,n,isupper,w,e);
|
|
//--- check
|
|
if(additionalmemory<2 || uneeded==0)
|
|
{
|
|
//--- We cant use additional memory or there is no need in such operations
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,minmn,isupper,false,u,nru,a,0,vt,ncvt);
|
|
}
|
|
else
|
|
{
|
|
//--- We can use additional memory
|
|
t2.Resize(minmn,m);
|
|
//--- function call
|
|
CBlas::CopyAndTranspose(u,0,m-1,0,minmn-1,t2,0,minmn-1,0,m-1);
|
|
//--- get result
|
|
result=CBdSingValueDecompose::RMatrixBdSVD(w,e,minmn,isupper,false,u,0,t2,m,vt,ncvt);
|
|
//--- function call
|
|
CBlas::CopyAndTranspose(t2,0,minmn-1,0,m-1,u,0,m-1,0,minmn-1);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Structure which stores state of linear CG solver between |
|
|
//| subsequent calls of FBLSCgIteration(). Initialized with |
|
|
//| FBLSCGCreate(). |
|
|
//| USAGE: |
|
|
//| 1. call to FBLSCGCreate() |
|
|
//| 2. F:=FBLSCgIteration(State) |
|
|
//| 3. if F is False, iterations are over |
|
|
//| 4. otherwise, fill State.AX with A*x, State.XAX with x'*A*x |
|
|
//| 5. goto 2 |
|
|
//| If you want to rerminate iterations, pass zero or negative value |
|
|
//| to XAX. |
|
|
//| FIELDS: |
|
|
//| E1 - 2-norm of residual at the start |
|
|
//| E2 - 2-norm of residual at the end |
|
|
//| X - on return from FBLSCgIteration() it contains |
|
|
//| vector for matrix-vector product |
|
|
//| AX - must be filled with A*x if FBLSCgIteration() |
|
|
//| returned True |
|
|
//| XAX - must be filled with x'*A*x |
|
|
//| XK - contains result (if FBLSCgIteration() returned |
|
|
//| False) |
|
|
//| Other fields are private and should not be used by outsiders. |
|
|
//+------------------------------------------------------------------+
|
|
class CFblsLinCgState
|
|
{
|
|
public:
|
|
//--- variables
|
|
double m_e1;
|
|
double m_e2;
|
|
double m_x[];
|
|
double m_ax[];
|
|
double m_xax;
|
|
double m_xk[];
|
|
int m_n;
|
|
double m_rk[];
|
|
double m_rk1[];
|
|
double m_xk1[];
|
|
double m_pk[];
|
|
double m_pk1[];
|
|
double m_b[];
|
|
RCommState m_rstate;
|
|
double m_tmp2[];
|
|
//--- constructor, destructor
|
|
CFblsLinCgState(void);
|
|
~CFblsLinCgState(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CFblsLinCgState::CFblsLinCgState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CFblsLinCgState::~CFblsLinCgState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast basic linear solutions |
|
|
//+------------------------------------------------------------------+
|
|
class CFbls
|
|
{
|
|
private:
|
|
//--- auxiliary functions for FblsCGiteration
|
|
static void Func_lbl_rcomm(CFblsLinCgState &state,int n,int k,double rk2,double rk12,double pap,double s,double betak,double v1,double v2);
|
|
static bool Func_lbl_3(CFblsLinCgState &state,int &n,int &k,double &rk2,double &rk12,double &pap,double &s,double &betak,double &v1,double &v2);
|
|
static bool Func_lbl_5(CFblsLinCgState &state,int &n,int &k,double &rk2,double &rk12,double &pap,double &s,double &betak,double &v1,double &v2);
|
|
public:
|
|
//--- constructor, destructor
|
|
CFbls(void);
|
|
~CFbls(void);
|
|
//--- methods
|
|
static void FblsCholeskySolve(CMatrixDouble &cha,const double sqrtscalea,const int n,const bool isupper,double &xb[],double &tmp[]);
|
|
static void FblsSolveCGx(CMatrixDouble &a,const int m,const int n,const double alpha,const double &b[],double &x[],double &buf[]);
|
|
static void FblsCGCreate(double &x[],double &b[],const int n,CFblsLinCgState &state);
|
|
static bool FblsCGIteration(CFblsLinCgState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CFbls::CFbls(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CFbls::~CFbls(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic Cholesky solver for ScaleA*Cholesky(A)'*x = y. |
|
|
//| This subroutine assumes that: |
|
|
//| * A*ScaleA is well scaled |
|
|
//| * A is well-conditioned, so no zero divisions or overflow may |
|
|
//| occur |
|
|
//| INPUT PARAMETERS: |
|
|
//| CHA - Cholesky decomposition of A |
|
|
//| SqrtScaleA- square root of scale factor ScaleA |
|
|
//| N - matrix size |
|
|
//| IsUpper - storage type |
|
|
//| XB - right part |
|
|
//| Tmp - buffer; function automatically allocates it, if |
|
|
//| it is too small. It can be reused if function is |
|
|
//| called several times. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| XB - solution |
|
|
//| NOTES: no assertion or tests are done during algorithm operation |
|
|
//+------------------------------------------------------------------+
|
|
static void CFbls::FblsCholeskySolve(CMatrixDouble &cha,const double sqrtscalea,
|
|
const int n,const bool isupper,double &xb[],
|
|
double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(tmp)<n)
|
|
ArrayResizeAL(tmp,n);
|
|
//--- A = L*L' or A=U'*U
|
|
if(isupper)
|
|
{
|
|
//--- Solve U'*y=b first.
|
|
for(i=0;i<n;i++)
|
|
{
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
v=xb[i];
|
|
for(i_=i+1;i_<n;i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
for(i_=i+1;i_<n;i_++)
|
|
xb[i_]=xb[i_]-v*tmp[i_];
|
|
}
|
|
}
|
|
//--- Solve U*x=y then.
|
|
for(i=n-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
for(i_=i+1;i_<n;i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
v=0.0;
|
|
for(i_=i+1;i_<n;i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Solve L*y=b first
|
|
for(i=0;i<n;i++)
|
|
{
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
for(i_=0;i_<i;i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
v=0.0;
|
|
for(i_=0;i_<i;i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
}
|
|
//--- Solve L'*x=y then.
|
|
for(i=n-1;i>=0;i--)
|
|
{
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
v=xb[i];
|
|
for(i_=0;i_<i;i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
for(i_=0;i_<i;i_++)
|
|
xb[i_]=xb[i_]-v*tmp[i_];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast basic linear solver: linear SPD CG |
|
|
//| Solves (A^T*A + alpha*I)*x = b where: |
|
|
//| * A is MxN matrix |
|
|
//| * alpha>0 is a scalar |
|
|
//| * I is NxN identity matrix |
|
|
//| * b is Nx1 vector |
|
|
//| * X is Nx1 unknown vector. |
|
|
//| N iterations of linear conjugate gradient are used to solve |
|
|
//| problem. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - array[M,N], matrix |
|
|
//| M - number of rows |
|
|
//| N - number of unknowns |
|
|
//| B - array[N], right part |
|
|
//| X - initial approxumation, array[N] |
|
|
//| Buf - buffer; function automatically allocates it, if it |
|
|
//| is too small. It can be reused if function is called |
|
|
//| several times with same M and N. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - improved solution |
|
|
//| NOTES: |
|
|
//| * solver checks quality of improved solution. If (because of |
|
|
//| problem condition number, numerical noise, etc.) new solution|
|
|
//| is WORSE than original approximation, then original |
|
|
//| approximation is returned. |
|
|
//| * solver assumes that both A, B, Alpha are well scaled (i.e. |
|
|
//| they are less than sqrt(overflow) and greater than |
|
|
//| sqrt(underflow)). |
|
|
//+------------------------------------------------------------------+
|
|
static void CFbls::FblsSolveCGx(CMatrixDouble &a,const int m,const int n,
|
|
const double alpha,const double &b[],
|
|
double &x[],double &buf[])
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
int offsrk=0;
|
|
int offsrk1=0;
|
|
int offsxk=0;
|
|
int offsxk1=0;
|
|
int offspk=0;
|
|
int offspk1=0;
|
|
int offstmp1=0;
|
|
int offstmp2=0;
|
|
int bs=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double rk2=0;
|
|
double rk12=0;
|
|
double pap=0;
|
|
double s=0;
|
|
double betak=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- Test for special case: B=0
|
|
v1=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
v1+=b[i_]*b[i_];
|
|
//--- check
|
|
if(v1==0.0)
|
|
{
|
|
for(k=0;k<n;k++)
|
|
x[k]=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Offsets inside Buf for:
|
|
//--- * R[K], R[K+1]
|
|
//--- * X[K], X[K+1]
|
|
//--- * P[K], P[K+1]
|
|
//--- * Tmp1 - array[M], Tmp2 - array[N]
|
|
offsrk=0;
|
|
offsrk1=offsrk+n;
|
|
offsxk=offsrk1+n;
|
|
offsxk1=offsxk+n;
|
|
offspk=offsxk1+n;
|
|
offspk1=offspk+n;
|
|
offstmp1=offspk1+n;
|
|
offstmp2=offstmp1+m;
|
|
bs=offstmp2+n;
|
|
//--- check
|
|
if(CAp::Len(buf)<bs)
|
|
ArrayResizeAL(buf,bs);
|
|
//--- x(0)=x
|
|
i1_=-offsxk;
|
|
for(i_=offsxk;i_<=offsxk+n-1;i_++)
|
|
buf[i_]=x[i_+i1_];
|
|
//--- r(0)=b-A*x(0)
|
|
//--- RK2=r(0)'*r(0)
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,buf,offsxk,buf,offstmp1);
|
|
//--- function call
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,buf,offstmp1,buf,offstmp2);
|
|
//--- change array
|
|
i1_=offsxk-offstmp2;
|
|
for(i_=offstmp2;i_<offstmp2+n;i_++)
|
|
buf[i_]=buf[i_]+alpha*buf[i_+i1_];
|
|
//--- change array
|
|
i1_=-offsrk;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
buf[i_]=b[i_+i1_];
|
|
//--- change array
|
|
i1_=offstmp2-offsrk;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
buf[i_]=buf[i_]-buf[i_+i1_];
|
|
rk2=0.0;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
rk2+=buf[i_]*buf[i_];
|
|
//--- change array
|
|
i1_=offsrk-offspk;
|
|
for(i_=offspk;i_<=offspk+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
e1=MathSqrt(rk2);
|
|
//--- cycle
|
|
for(k=0;k<n;k++)
|
|
{
|
|
//--- Calculate A*p(k) - store in Buf[OffsTmp2:OffsTmp2+N-1]
|
|
//--- and p(k)'*A*p(k) - store in PAP
|
|
//--- If PAP=0, break (iteration is over)
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,buf,offspk,buf,offstmp1);
|
|
v1=0.0;
|
|
for(i_=offstmp1;i_<=offstmp1+m-1;i_++)
|
|
v1+=buf[i_]*buf[i_];
|
|
v2=0.0;
|
|
for(i_=offspk;i_<=offspk+n-1;i_++)
|
|
v2+=buf[i_]*buf[i_];
|
|
pap=v1+alpha*v2;
|
|
//--- function call
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,buf,offstmp1,buf,offstmp2);
|
|
i1_=offspk-offstmp2;
|
|
for(i_=offstmp2;i_<offstmp2+n;i_++)
|
|
buf[i_]=buf[i_]+alpha*buf[i_+i1_];
|
|
//--- check
|
|
if(pap==0.0)
|
|
break;
|
|
//--- S=(r(k)'*r(k))/(p(k)'*A*p(k))
|
|
s=rk2/pap;
|
|
//--- x(k+1)=x(k) + S*p(k)
|
|
i1_=offsxk-offsxk1;
|
|
for(i_=offsxk1;i_<=offsxk1+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
i1_=offspk-offsxk1;
|
|
for(i_=offsxk1;i_<=offsxk1+n-1;i_++)
|
|
buf[i_]=buf[i_]+s*buf[i_+i1_];
|
|
//--- r(k+1)=r(k) - S*A*p(k)
|
|
//--- RK12=r(k+1)'*r(k+1)
|
|
//--- Break if r(k+1) small enough (when compared to r(k))
|
|
i1_=offsrk-offsrk1;
|
|
for(i_=offsrk1;i_<=offsrk1+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
i1_=offstmp2-offsrk1;
|
|
for(i_=offsrk1;i_<=offsrk1+n-1;i_++)
|
|
buf[i_]=buf[i_]-s*buf[i_+i1_];
|
|
rk12=0.0;
|
|
for(i_=offsrk1;i_<=offsrk1+n-1;i_++)
|
|
rk12+=buf[i_]*buf[i_];
|
|
//--- check
|
|
if(MathSqrt(rk12)<=100*CMath::m_machineepsilon*MathSqrt(rk2))
|
|
{
|
|
//--- X(k) = x(k+1) before exit -
|
|
//--- - because we expect to find solution at x(k)
|
|
i1_=offsxk1-offsxk;
|
|
for(i_=offsxk;i_<=offsxk+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
break;
|
|
}
|
|
//--- BetaK=RK12/RK2
|
|
//--- p(k+1)=r(k+1)+betak*p(k)
|
|
betak=rk12/rk2;
|
|
i1_=offsrk1-offspk1;
|
|
for(i_=offspk1;i_<=offspk1+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
i1_=offspk-offspk1;
|
|
for(i_=offspk1;i_<=offspk1+n-1;i_++)
|
|
buf[i_]=buf[i_]+betak*buf[i_+i1_];
|
|
//--- r(k) :=r(k+1)
|
|
//--- x(k) :=x(k+1)
|
|
//--- p(k) :=p(k+1)
|
|
i1_=offsrk1-offsrk;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
i1_=offsxk1-offsxk;
|
|
for(i_=offsxk;i_<=offsxk+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
i1_=offspk1-offspk;
|
|
for(i_=offspk;i_<=offspk+n-1;i_++)
|
|
buf[i_]=buf[i_+i1_];
|
|
rk2=rk12;
|
|
}
|
|
//--- Calculate E2
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,buf,offsxk,buf,offstmp1);
|
|
//--- function call
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,buf,offstmp1,buf,offstmp2);
|
|
i1_=offsxk-offstmp2;
|
|
for(i_=offstmp2;i_<offstmp2+n;i_++)
|
|
buf[i_]=buf[i_]+alpha*buf[i_+i1_];
|
|
i1_=-offsrk;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
buf[i_]=b[i_+i1_];
|
|
i1_=offstmp2-offsrk;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
buf[i_]=buf[i_]-buf[i_+i1_];
|
|
v1=0.0;
|
|
for(i_=offsrk;i_<offsrk+n;i_++)
|
|
v1+=buf[i_]*buf[i_];
|
|
e2=MathSqrt(v1);
|
|
//--- Output result (if it was improved)
|
|
if(e2<e1)
|
|
{
|
|
i1_=offsxk;
|
|
for(i_=0;i_<n;i_++)
|
|
x[i_]=buf[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Construction of linear conjugate gradient solver. |
|
|
//| State parameter passed using "var" semantics (i.e. previous state|
|
|
//| is NOT erased). When it is already initialized, we can reause |
|
|
//| prevously allocated memory. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - initial solution |
|
|
//| B - right part |
|
|
//| N - system size |
|
|
//| State - structure; may be preallocated, if we want to |
|
|
//| reuse memory |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which is used by FBLSCGIteration() to |
|
|
//| store algorithm state between subsequent calls. |
|
|
//| NOTE: no error checking is done; caller must check all |
|
|
//| parameters, prevent overflows, and so on. |
|
|
//+------------------------------------------------------------------+
|
|
static void CFbls::FblsCGCreate(double &x[],double &b[],const int n,CFblsLinCgState &state)
|
|
{
|
|
//--- create variables
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(state.m_b)<n)
|
|
ArrayResizeAL(state.m_b,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_rk)<n)
|
|
ArrayResizeAL(state.m_rk,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_rk1)<n)
|
|
ArrayResizeAL(state.m_rk1,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_xk)<n)
|
|
ArrayResizeAL(state.m_xk,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_xk1)<n)
|
|
ArrayResizeAL(state.m_xk1,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_pk)<n)
|
|
ArrayResizeAL(state.m_pk,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_pk1)<n)
|
|
ArrayResizeAL(state.m_pk1,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_tmp2)<n)
|
|
ArrayResizeAL(state.m_tmp2,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_x)<n)
|
|
ArrayResizeAL(state.m_x,n);
|
|
//--- check
|
|
if(CAp::Len(state.m_ax)<n)
|
|
ArrayResizeAL(state.m_ax,n);
|
|
//--- check
|
|
state.m_n=n;
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_xk[i_]=x[i_];
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_b[i_]=b[i_];
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,2);
|
|
ArrayResizeAL(state.m_rstate.ra,7);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Linear CG solver, function relying on reverse communication to |
|
|
//| calculate matrix-vector products. |
|
|
//| See comments for FBLSLinCGState structure for more info. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CFbls::FblsCGIteration(CFblsLinCgState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int k=0;
|
|
double rk2=0;
|
|
double rk12=0;
|
|
double pap=0;
|
|
double s=0;
|
|
double betak=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int i_=0;
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
n=state.m_rstate.ia[0];
|
|
k=state.m_rstate.ia[1];
|
|
rk2=state.m_rstate.ra[0];
|
|
rk12=state.m_rstate.ra[1];
|
|
pap=state.m_rstate.ra[2];
|
|
s=state.m_rstate.ra[3];
|
|
betak=state.m_rstate.ra[4];
|
|
v1=state.m_rstate.ra[5];
|
|
v2=state.m_rstate.ra[6];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
k=-989;
|
|
rk2=-834;
|
|
rk12=900;
|
|
pap=-287;
|
|
s=364;
|
|
betak=214;
|
|
v1=-338;
|
|
v2=-686;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk[i_]=state.m_b[i_];
|
|
//--- calculation
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk[i_]=state.m_rk[i_]-state.m_ax[i_];
|
|
//--- change value
|
|
rk2=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
rk2+=state.m_rk[i_]*state.m_rk[i_];
|
|
//--- copy
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_pk[i_]=state.m_rk[i_];
|
|
state.m_e1=MathSqrt(rk2);
|
|
//--- cycle
|
|
k=0;
|
|
//--- function call
|
|
return(Func_lbl_3(state,n,k,rk2,rk12,pap,s,betak,v1,v2));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_tmp2[i_]=state.m_ax[i_];
|
|
pap=state.m_xax;
|
|
//--- check
|
|
if(!CMath::IsFinite(pap))
|
|
return(Func_lbl_5(state,n,k,rk2,rk12,pap,s,betak,v1,v2));
|
|
//--- check
|
|
if(pap<=0.0)
|
|
return(Func_lbl_5(state,n,k,rk2,rk12,pap,s,betak,v1,v2));
|
|
//--- S=(r(k)'*r(k))/(p(k)'*A*p(k))
|
|
s=rk2/pap;
|
|
//--- x(k+1)=x(k) + S*p(k)
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_xk1[i_]=state.m_xk[i_];
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_xk1[i_]=state.m_xk1[i_]+s*state.m_pk[i_];
|
|
//--- r(k+1)=r(k) - S*A*p(k)
|
|
//--- RK12=r(k+1)'*r(k+1)
|
|
//--- Break if r(k+1) small enough (when compared to r(k))
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk1[i_]=state.m_rk[i_];
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk1[i_]=state.m_rk1[i_]-s*state.m_tmp2[i_];
|
|
rk12=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
rk12+=state.m_rk1[i_]*state.m_rk1[i_];
|
|
//--- check
|
|
if(MathSqrt(rk12)<=100*CMath::m_machineepsilon*state.m_e1)
|
|
{
|
|
//--- X(k) = x(k+1) before exit -
|
|
//--- - because we expect to find solution at x(k)
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_xk[i_]=state.m_xk1[i_];
|
|
//--- function call
|
|
return(Func_lbl_5(state,n,k,rk2,rk12,pap,s,betak,v1,v2));
|
|
}
|
|
//--- BetaK=RK12/RK2
|
|
//--- p(k+1)=r(k+1)+betak*p(k)
|
|
//--- NOTE: we expect that BetaK won't overflow because of
|
|
//--- "Sqrt(RK12)<=100*MachineEpsilon*E1" test above.
|
|
betak=rk12/rk2;
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_pk1[i_]=state.m_rk1[i_];
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_pk1[i_]=state.m_pk1[i_]+betak*state.m_pk[i_];
|
|
//--- r(k) :=r(k+1)
|
|
//--- x(k) :=x(k+1)
|
|
//--- p(k) :=p(k+1)
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk[i_]=state.m_rk1[i_];
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_xk[i_]=state.m_xk1[i_];
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_pk[i_]=state.m_pk1[i_];
|
|
rk2=rk12;
|
|
k=k+1;
|
|
//--- function call
|
|
return(Func_lbl_3(state,n,k,rk2,rk12,pap,s,betak,v1,v2));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk[i_]=state.m_b[i_];
|
|
//--- calculation
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_rk[i_]=state.m_rk[i_]-state.m_ax[i_];
|
|
//--- change value
|
|
v1=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
v1+=state.m_rk[i_]*state.m_rk[i_];
|
|
state.m_e2=MathSqrt(v1);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Routine body
|
|
//--- prepare locals
|
|
n=state.m_n;
|
|
//--- Test for special case: B=0
|
|
v1=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
v1+=state.m_b[i_]*state.m_b[i_];
|
|
//--- check
|
|
if(v1==0.0)
|
|
{
|
|
for(k=0;k<n;k++)
|
|
state.m_xk[k]=0;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- r(0)=b-A*x(0)
|
|
//--- RK2=r(0)'*r(0)
|
|
for(i_=0;i_<n;i_++)
|
|
state.m_x[i_]=state.m_xk[i_];
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,k,rk2,rk12,pap,s,betak,v1,v2);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for FblsCGiteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CFbls::Func_lbl_rcomm(CFblsLinCgState &state,int n,int k,
|
|
double rk2,double rk12,double pap,
|
|
double s,double betak,double v1,double v2)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia[0]=n;
|
|
state.m_rstate.ia[1]=k;
|
|
state.m_rstate.ra[0]=rk2;
|
|
state.m_rstate.ra[1]=rk12;
|
|
state.m_rstate.ra[2]=pap;
|
|
state.m_rstate.ra[3]=s;
|
|
state.m_rstate.ra[4]=betak;
|
|
state.m_rstate.ra[5]=v1;
|
|
state.m_rstate.ra[6]=v2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for FblsCGiteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CFbls::Func_lbl_3(CFblsLinCgState &state,int &n,int &k,
|
|
double &rk2,double &rk12,double &pap,
|
|
double &s,double &betak,double &v1,double &v2)
|
|
{
|
|
//--- check
|
|
if(k>n-1)
|
|
return(Func_lbl_5(state,n,k,rk2,rk12,pap,s,betak,v1,v2));
|
|
//--- Calculate A*p(k) - store in State.Tmp2
|
|
//--- and p(k)'*A*p(k) - store in PAP
|
|
//--- If PAP=0,break (iteration is over)
|
|
for(int i_=0;i_<n;i_++)
|
|
state.m_x[i_]=state.m_pk[i_];
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,k,rk2,rk12,pap,s,betak,v1,v2);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for FblsCGiteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CFbls::Func_lbl_5(CFblsLinCgState &state,int &n,int &k,
|
|
double &rk2,double &rk12,double &pap,
|
|
double &s,double &betak,double &v1,double &v2)
|
|
{
|
|
//--- calculation E2
|
|
for(int i_=0;i_<n;i_++)
|
|
state.m_x[i_]=state.m_xk[i_];
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,k,rk2,rk12,pap,s,betak,v1,v2);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Determinant calculation |
|
|
//+------------------------------------------------------------------+
|
|
class CMatDet
|
|
{
|
|
public:
|
|
CMatDet(void);
|
|
~CMatDet(void);
|
|
//--- public methods
|
|
static double RMatrixLUDet(CMatrixDouble &a,int &pivots[],const int n);
|
|
static double RMatrixDet(CMatrixDouble &ca,const int n);
|
|
static double SPDMatrixCholeskyDet(CMatrixDouble &a,const int n);
|
|
static double SPDMatrixDet(CMatrixDouble &ca,const int n,const bool isupper);
|
|
static complex CMatrixLUDet(CMatrixComplex &a,int &pivots[],const int n);
|
|
static complex CMatrixDet(CMatrixComplex &ca,const int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMatDet::CMatDet(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMatDet::~CMatDet(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Determinant calculation of the matrix given by its LU |
|
|
//| decomposition. |
|
|
//| Input parameters: |
|
|
//| A - LU decomposition of the matrix (output of |
|
|
//| RMatrixLU subroutine). |
|
|
//| Pivots - table of permutations which were made during |
|
|
//| the LU decomposition. |
|
|
//| Output of RMatrixLU subroutine. |
|
|
//| N - (optional) size of matrix A: |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, automatically determined from |
|
|
//| matrix size (A must be square matrix) |
|
|
//| Result: matrix determinant. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMatDet::RMatrixLUDet(CMatrixDouble &a,int &pivots[],const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int s=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(pivots)>=n,__FUNCTION__+": Pivots array is too short!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- initialization
|
|
result=1;
|
|
s=1;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
result=result*a[i][i];
|
|
//--- check
|
|
if(pivots[i]!=i)
|
|
s=-s;
|
|
}
|
|
//--- return result
|
|
return(result*s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculation of the determinant of a general matrix |
|
|
//| Input parameters: |
|
|
//| A - matrix, array[0..N-1, 0..N-1] |
|
|
//| N - (optional) size of matrix A: |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, automatically determined from |
|
|
//| matrix size (A must be square matrix) |
|
|
//| Result: determinant of matrix A. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMatDet::RMatrixDet(CMatrixDouble &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int pivots[];
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- function call
|
|
CTrFac::RMatrixLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(RMatrixLUDet(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Determinant calculation of the matrix given by its LU |
|
|
//| decomposition. |
|
|
//| Input parameters: |
|
|
//| A - LU decomposition of the matrix (output of |
|
|
//| RMatrixLU subroutine). |
|
|
//| Pivots - table of permutations which were made during |
|
|
//| the LU decomposition. |
|
|
//| Output of RMatrixLU subroutine. |
|
|
//| N - (optional) size of matrix A: |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, automatically determined from |
|
|
//| matrix size (A must be square matrix) |
|
|
//| Result: matrix determinant. |
|
|
//+------------------------------------------------------------------+
|
|
static complex CMatDet::CMatrixLUDet(CMatrixComplex &a,int &pivots[],const int n)
|
|
{
|
|
//--- create variables
|
|
complex result=0;
|
|
int i=0;
|
|
int s=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(pivots)>=n,__FUNCTION__+": Pivots array is too short!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteComplexMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- initialization
|
|
result=1;
|
|
s=1;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
result=result*a[i][i];
|
|
//--- check
|
|
if(pivots[i]!=i)
|
|
s=-s;
|
|
}
|
|
//--- return result
|
|
return(result*s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculation of the determinant of a general matrix |
|
|
//| Input parameters: |
|
|
//| A - matrix, array[0..N-1, 0..N-1] |
|
|
//| N - (optional) size of matrix A: |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, automatically determined from |
|
|
//| matrix size (A must be square matrix) |
|
|
//| Result: determinant of matrix A. |
|
|
//+------------------------------------------------------------------+
|
|
static complex CMatDet::CMatrixDet(CMatrixComplex &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
complex result=0;
|
|
int pivots[];
|
|
//--- create copy
|
|
CMatrixComplex a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteComplexMatrix(a,n,n),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- function call
|
|
CTrFac::CMatrixLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(CMatrixLUDet(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Determinant calculation of the matrix given by the Cholesky |
|
|
//| decomposition. |
|
|
//| Input parameters: |
|
|
//| A - Cholesky decomposition, |
|
|
//| output of SMatrixCholesky subroutine. |
|
|
//| N - (optional) size of matrix A: |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, automatically determined from |
|
|
//| matrix size (A must be square matrix) |
|
|
//| As the determinant is equal to the product of squares of diagonal|
|
|
//| elements, it?s not necessary to specify which triangle - lower |
|
|
//| or upper - the matrix is stored in. |
|
|
//| Result: |
|
|
//| matrix determinant. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMatDet::SPDMatrixCholeskyDet(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
bool f;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- initialization
|
|
f=true;
|
|
for(i=0;i<n;i++)
|
|
f=f && CMath::IsFinite(a[i][i]);
|
|
//--- check
|
|
if(!CAp::Assert(f,__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
result=1;
|
|
for(i=0;i<n;i++)
|
|
result=result*CMath::Sqr(a[i][i]);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Determinant calculation of the symmetric positive definite |
|
|
//| matrix. |
|
|
//| Input parameters: |
|
|
//| A - matrix. Array with elements [0..N-1, 0..N-1]. |
|
|
//| N - (optional) size of matrix A: |
|
|
//| * if given, only principal NxN submatrix is |
|
|
//| processed and overwritten. other elements are |
|
|
//| unchanged. |
|
|
//| * if not given, automatically determined from |
|
|
//| matrix size (A must be square matrix) |
|
|
//| IsUpper - (optional) storage type: |
|
|
//| * if True, symmetric matrix A is given by its |
|
|
//| upper triangle, and the lower triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if False, symmetric matrix A is given by its |
|
|
//| lower triangle, and the upper triangle isn?t |
|
|
//| used/changed by function |
|
|
//| * if not given, both lower and upper triangles |
|
|
//| must be filled. |
|
|
//| Result: |
|
|
//| determinant of matrix A. |
|
|
//| If matrix A is not positive definite, exception is thrown. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMatDet::SPDMatrixDet(CMatrixDouble &ca,const int n,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
bool b;
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": rows(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": cols(A)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteRTrMatrix(a,n,isupper),__FUNCTION__+": A contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- function call
|
|
b=CTrFac::SPDMatrixCholesky(a,n,isupper);
|
|
//--- check
|
|
if(!CAp::Assert(b,__FUNCTION__+": A is not SPD!"))
|
|
return(EMPTY_VALUE);
|
|
//--- return result
|
|
return(SPDMatrixCholeskyDet(a,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generalized symmetric eigensolver |
|
|
//+------------------------------------------------------------------+
|
|
class CSpdGEVD
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CSpdGEVD(void);
|
|
~CSpdGEVD(void);
|
|
//--- methods
|
|
static bool SMatrixGEVD(CMatrixDouble &ca,const int n,const bool isuppera,CMatrixDouble &b,const bool isupperb,const int zneeded,const int problemtype,double &d[],CMatrixDouble &z);
|
|
static bool SMatrixGEVDReduce(CMatrixDouble &a,const int n,const bool isuppera,CMatrixDouble &b,const bool isupperb,const int problemtype,CMatrixDouble &r,bool &isupperr);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpdGEVD::CSpdGEVD(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpdGEVD::~CSpdGEVD(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Algorithm for solving the following generalized symmetric |
|
|
//| positive-definite eigenproblem: |
|
|
//| A*x = lambda*B*x (1) or |
|
|
//| A*B*x = lambda*x (2) or |
|
|
//| B*A*x = lambda*x (3). |
|
|
//| where A is a symmetric matrix, B - symmetric positive-definite |
|
|
//| matrix. The problem is solved by reducing it to an ordinary |
|
|
//| symmetric eigenvalue problem. |
|
|
//| Input parameters: |
|
|
//| A - symmetric matrix which is given by its upper |
|
|
//| or lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrices A and B. |
|
|
//| IsUpperA - storage format of matrix A. |
|
|
//| B - symmetric positive-definite matrix which is |
|
|
//| given by its upper or lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| IsUpperB - storage format of matrix B. |
|
|
//| ZNeeded - if ZNeeded is equal to: |
|
|
//| * 0, the eigenvectors are not returned; |
|
|
//| * 1, the eigenvectors are returned. |
|
|
//| ProblemType - if ProblemType is equal to: |
|
|
//| * 1, the following problem is solved: |
|
|
//| A*x = lambda*B*x; |
|
|
//| * 2, the following problem is solved: |
|
|
//| A*B*x = lambda*x; |
|
|
//| * 3, the following problem is solved: |
|
|
//| B*A*x = lambda*x. |
|
|
//| Output parameters: |
|
|
//| D - eigenvalues in ascending order. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Z - if ZNeeded is equal to: |
|
|
//| * 0, Z hasn?t changed; |
|
|
//| * 1, Z contains eigenvectors. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| The eigenvectors are stored in matrix |
|
|
//| columns. It should be noted that the |
|
|
//| eigenvectors in such problems do not form an |
|
|
//| orthogonal system. |
|
|
//| Result: |
|
|
//| True, if the problem was solved successfully. |
|
|
//| False, if the error occurred during the Cholesky |
|
|
//| decomposition of matrix B (the matrix isn?t |
|
|
//| positive-definite) or during the work of the iterative |
|
|
//| algorithm for solving the symmetric eigenproblem. |
|
|
//| See also the GeneralizedSymmetricDefiniteEVDReduce subroutine. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CSpdGEVD::SMatrixGEVD(CMatrixDouble &ca,const int n,const bool isuppera,
|
|
CMatrixDouble &b,const bool isupperb,const int zneeded,
|
|
const int problemtype,double &d[],CMatrixDouble &z)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool isupperr;
|
|
int j1=0;
|
|
int j2=0;
|
|
int j1inc=0;
|
|
int j2inc=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble r;
|
|
CMatrixDouble t;
|
|
//--- create copy
|
|
CMatrixDouble a;
|
|
a=ca;
|
|
//--- Reduce and solve
|
|
result=SMatrixGEVDReduce(a,n,isuppera,b,isupperb,problemtype,r,isupperr);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- get result
|
|
result=CEigenVDetect::SMatrixEVD(a,n,zneeded,isuppera,d,t);
|
|
//--- check
|
|
if(!result)
|
|
return(result);
|
|
//--- Transform eigenvectors if needed
|
|
if(zneeded!=0)
|
|
{
|
|
//--- fill Z with zeros
|
|
z.Resize(n,n);
|
|
for(j=0;j<n;j++)
|
|
z[0].Set(j,0.0);
|
|
for(i=1;i<n;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
z[i].Set(i_,z[0][i_]);
|
|
}
|
|
//--- Setup R properties
|
|
if(isupperr)
|
|
{
|
|
j1=0;
|
|
j2=n-1;
|
|
j1inc=1;
|
|
j2inc=0;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=0;
|
|
j1inc=0;
|
|
j2inc=1;
|
|
}
|
|
//--- Calculate R*Z
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
v=r[i][j];
|
|
for(i_=0;i_<n;i_++)
|
|
z[i].Set(i_,z[i][i_]+v*t[j][i_]);
|
|
}
|
|
j1=j1+j1inc;
|
|
j2=j2+j2inc;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Algorithm for reduction of the following generalized symmetric |
|
|
//| positive- definite eigenvalue problem: |
|
|
//| A*x = lambda*B*x (1) or |
|
|
//| A*B*x = lambda*x (2) or |
|
|
//| B*A*x = lambda*x (3) |
|
|
//| to the symmetric eigenvalues problem C*y = lambda*y (eigenvalues |
|
|
//| of this and the given problems are the same, and the eigenvectors|
|
|
//| of the given problem could be obtained by multiplying the |
|
|
//| obtained eigenvectors by the transformation matrix x = R*y). |
|
|
//| Here A is a symmetric matrix, B - symmetric positive-definite |
|
|
//| matrix. |
|
|
//| Input parameters: |
|
|
//| A - symmetric matrix which is given by its upper |
|
|
//| or lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrices A and B. |
|
|
//| IsUpperA - storage format of matrix A. |
|
|
//| B - symmetric positive-definite matrix which is |
|
|
//| given by its upper or lower triangular part. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| IsUpperB - storage format of matrix B. |
|
|
//| ProblemType - if ProblemType is equal to: |
|
|
//| * 1, the following problem is solved: |
|
|
//| A*x = lambda*B*x; |
|
|
//| * 2, the following problem is solved: |
|
|
//| A*B*x = lambda*x; |
|
|
//| * 3, the following problem is solved: |
|
|
//| B*A*x = lambda*x. |
|
|
//| Output parameters: |
|
|
//| A - symmetric matrix which is given by its upper |
|
|
//| or lower triangle depending on IsUpperA. |
|
|
//| Contains matrix C. Array whose indexes range |
|
|
//| within [0..N-1, 0..N-1]. |
|
|
//| R - upper triangular or low triangular |
|
|
//| transformation matrix which is used to obtain|
|
|
//| the eigenvectors of a given problem as the |
|
|
//| product of eigenvectors of C (from the right)|
|
|
//| and matrix R (from the left). If the matrix |
|
|
//| is upper triangular, the elements below the |
|
|
//| main diagonal are equal to 0 (and vice versa)|
|
|
//| Thus, we can perform the multiplication |
|
|
//| without taking into account the internal |
|
|
//| structure (which is an easier though less |
|
|
//| effective way). Array whose indexes range |
|
|
//| within [0..N-1, 0..N-1]. |
|
|
//| IsUpperR - type of matrix R (upper or lower triangular).|
|
|
//| Result: |
|
|
//| True, if the problem was reduced successfully. |
|
|
//| False, if the error occurred during the Cholesky |
|
|
//| decomposition of matrix B (the matrix is not |
|
|
//| positive-definite). |
|
|
//+------------------------------------------------------------------+
|
|
static bool CSpdGEVD::SMatrixGEVDReduce(CMatrixDouble &a,const int n,const bool isuppera,
|
|
CMatrixDouble &b,const bool isupperb,const int problemtype,
|
|
CMatrixDouble &r,bool &isupperr)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int info=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create matrix
|
|
CMatrixDouble t;
|
|
//--- create arrays
|
|
double w1[];
|
|
double w2[];
|
|
double w3[];
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return(false);
|
|
//--- check
|
|
if(!CAp::Assert(problemtype==1 || problemtype==2 || problemtype==3,__FUNCTION__+": incorrect ProblemType!"))
|
|
return(false);
|
|
//--- initialization
|
|
result=true;
|
|
//--- Problem 1: A*x = lambda*B*x
|
|
//--- Reducing to:
|
|
//--- C*y = lambda*y
|
|
//--- C = L^(-1) * A * L^(-T)
|
|
//--- x = L^(-T) * y
|
|
if(problemtype==1)
|
|
{
|
|
//--- Factorize B in T: B = LL'
|
|
t.Resize(n,n);
|
|
//--- check
|
|
if(isupperb)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(i_=i;i_<n;i_++)
|
|
t[i_].Set(i,b[i][i_]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(i_=0;i_<=i;i_++)
|
|
t[i].Set(i_,b[i][i_]);
|
|
}
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(t,n,false))
|
|
return(false);
|
|
//--- Invert L in T
|
|
CMatInv::RMatrixTrInverse(t,n,false,false,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return(false);
|
|
//--- Build L^(-1) * A * L^(-T) in R
|
|
ArrayResizeAL(w1,n+1);
|
|
ArrayResizeAL(w2,n+1);
|
|
r.Resize(n,n);
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- Form w2 = A * l'(j) (here l'(j) is j-th column of L^(-T))
|
|
i1_=-1;
|
|
for(i_=1;i_<=j;i_++)
|
|
w1[i_]=t[j-1][i_+i1_];
|
|
//--- function call
|
|
CSblas::SymmetricMatrixVectorMultiply(a,isuppera,0,j-1,w1,1.0,w2);
|
|
//--- check
|
|
if(isuppera)
|
|
CBlas::MatrixVectorMultiply(a,0,j-1,j,n-1,true,w1,1,j,1.0,w2,j+1,n,0.0);
|
|
else
|
|
CBlas::MatrixVectorMultiply(a,j,n-1,0,j-1,false,w1,1,j,1.0,w2,j+1,n,0.0);
|
|
//--- Form l(i)*w2 (here l(i) is i-th row of L^(-1))
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=1;
|
|
v=0.0;
|
|
for(i_=0;i_<i;i_++)
|
|
v+=t[i-1][i_]*w2[i_+i1_];
|
|
r[i-1].Set(j-1,v);
|
|
}
|
|
}
|
|
// Copy R to A
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,r[i][i_]);
|
|
}
|
|
//--- Copy L^(-1) from T to R and transpose
|
|
isupperr=true;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
r[i].Set(j,0);
|
|
}
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
r[i].Set(i_,t[i_][i]);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- Problem 2: A*B*x = lambda*x
|
|
//--- or
|
|
//--- problem 3: B*A*x = lambda*x
|
|
//--- Reducing to:
|
|
//--- C*y = lambda*y
|
|
//--- C = U * A * U'
|
|
//--- B = U'* U
|
|
if(problemtype==2||problemtype==3)
|
|
{
|
|
//--- Factorize B in T: B = U'*U
|
|
t.Resize(n,n);
|
|
//--- check
|
|
if(isupperb)
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(i_=i;i_<n;i_++)
|
|
t[i].Set(i_,b[i][i_]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<n;i++)
|
|
for(i_=i;i_<n;i_++)
|
|
t[i].Set(i_,b[i_][i]);
|
|
}
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(t,n,true))
|
|
return(false);
|
|
//--- Build U * A * U' in R
|
|
ArrayResizeAL(w1,n+1);
|
|
ArrayResizeAL(w2,n+1);
|
|
ArrayResizeAL(w3,n+1);
|
|
r.Resize(n,n);
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- Form w2 = A * u'(j) (here u'(j) is j-th column of U')
|
|
i1_=(j-1)-(1);
|
|
for(i_=1;i_<=n-j+1;i_++)
|
|
w1[i_]=t[j-1][i_+i1_];
|
|
//--- function call
|
|
CSblas::SymmetricMatrixVectorMultiply(a,isuppera,j-1,n-1,w1,1.0,w3);
|
|
i1_=(1)-(j);
|
|
for(i_=j;i_<=n;i_++)
|
|
w2[i_]=w3[i_+i1_];
|
|
i1_=(j-1)-(j);
|
|
for(i_=j;i_<=n;i_++)
|
|
w1[i_]=t[j-1][i_+i1_];
|
|
//--- check
|
|
if(isuppera)
|
|
CBlas::MatrixVectorMultiply(a,0,j-2,j-1,n-1,false,w1,j,n,1.0,w2,1,j-1,0.0);
|
|
else
|
|
CBlas::MatrixVectorMultiply(a,j-1,n-1,0,j-2,true,w1,j,n,1.0,w2,1,j-1,0.0);
|
|
//--- Form u(i)*w2 (here u(i) is i-th row of U)
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
i1_=(i)-(i-1);
|
|
v=0.0;
|
|
for(i_=i-1;i_<n;i_++)
|
|
v+=t[i-1][i_]*w2[i_+i1_];
|
|
r[i-1].Set(j-1,v);
|
|
}
|
|
}
|
|
//--- Copy R to A
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(i_=0;i_<n;i_++)
|
|
a[i].Set(i_,r[i][i_]);
|
|
}
|
|
//--- check
|
|
if(problemtype==2)
|
|
{
|
|
//--- Invert U in T
|
|
CMatInv::RMatrixTrInverse(t,n,true,false,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return(false);
|
|
//--- Copy U^-1 from T to R
|
|
isupperr=true;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=0;j<i;j++)
|
|
r[i].Set(j,0);
|
|
}
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
r[i].Set(i_,t[i][i_]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Copy U from T to R and transpose
|
|
isupperr=false;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(j=i+1;j<n;j++)
|
|
r[i].Set(j,0);
|
|
}
|
|
for(i=0;i<n;i++)
|
|
{
|
|
for(i_=i;i_<n;i_++)
|
|
r[i_].Set(i,t[i][i_]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse and update |
|
|
//+------------------------------------------------------------------+
|
|
class CInverseUpdate
|
|
{
|
|
public:
|
|
CInverseUpdate(void);
|
|
~CInverseUpdate(void);
|
|
//--- methods
|
|
static void RMatrixInvUpdateSimple(CMatrixDouble &inva,const int n,const int updrow,const int updcolumn,const double updval);
|
|
static void RMatrixInvUpdateRow(CMatrixDouble &inva,const int n,const int updrow,double &v[]);
|
|
static void RMatrixInvUpdateColumn(CMatrixDouble &inva,const int n,const int updcolumn,double &u[]);
|
|
static void RMatrixInvUpdateUV(CMatrixDouble &inva,const int n,double &u[],double &v[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CInverseUpdate::CInverseUpdate(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CInverseUpdate::~CInverseUpdate(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse matrix update by the Sherman-Morrison formula |
|
|
//| The algorithm updates matrix A^-1 when adding a number to an |
|
|
//| element of matrix A. |
|
|
//| Input parameters: |
|
|
//| InvA - inverse of matrix A. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| UpdRow - row where the element to be updated is stored. |
|
|
//| UpdColumn - column where the element to be updated is stored.|
|
|
//| UpdVal - a number to be added to the element. |
|
|
//| Output parameters: |
|
|
//| InvA - inverse of modified matrix A. |
|
|
//+------------------------------------------------------------------+
|
|
static void CInverseUpdate::RMatrixInvUpdateSimple(CMatrixDouble &inva,const int n,
|
|
const int updrow,const int updcolumn,
|
|
const double updval)
|
|
{
|
|
//--- create arrays
|
|
double t1[];
|
|
double t2[];
|
|
//--- create variables
|
|
int i=0;
|
|
double lambdav=0;
|
|
double vt=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(updrow>=0 && updrow<n,"RMatrixInvUpdateSimple: incorrect UpdRow!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(updcolumn>=0 && updcolumn<n,"RMatrixInvUpdateSimple: incorrect UpdColumn!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(t1,n);
|
|
ArrayResizeAL(t2,n);
|
|
//--- T1=InvA * U
|
|
for(i_=0;i_<n;i_++)
|
|
t1[i_]=inva[i_][updrow];
|
|
//--- T2=v*InvA
|
|
for(i_=0;i_<n;i_++)
|
|
t2[i_]=inva[updcolumn][i_];
|
|
//--- Lambda=v * InvA * U
|
|
lambdav=updval*inva[updcolumn][updrow];
|
|
//--- InvA=InvA - correction
|
|
for(i=0;i<n;i++)
|
|
{
|
|
vt=updval*t1[i];
|
|
vt=vt/(1+lambdav);
|
|
for(i_=0;i_<n;i_++)
|
|
inva[i].Set(i_,inva[i][i_]-vt*t2[i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse matrix update by the Sherman-Morrison formula |
|
|
//| The algorithm updates matrix A^-1 when adding a vector to a row |
|
|
//| of matrix A. |
|
|
//| Input parameters: |
|
|
//| InvA - inverse of matrix A. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| UpdRow - the row of A whose vector V was added. |
|
|
//| 0 <= Row <= N-1 |
|
|
//| V - the vector to be added to a row. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Output parameters: |
|
|
//| InvA - inverse of modified matrix A. |
|
|
//+------------------------------------------------------------------+
|
|
static void CInverseUpdate::RMatrixInvUpdateRow(CMatrixDouble &inva,const int n,
|
|
const int updrow,double &v[])
|
|
{
|
|
//--- create arrays
|
|
double t1[];
|
|
double t2[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double lambdav=0;
|
|
double vt=0;
|
|
int i_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(t1,n);
|
|
ArrayResizeAL(t2,n);
|
|
//--- T1=InvA * U
|
|
for(i_=0;i_<n;i_++)
|
|
t1[i_]=inva[i_][updrow];
|
|
//--- T2=v*InvA
|
|
//--- Lambda=v * InvA * U
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
vt+=v[i_]*inva[i_][j];
|
|
t2[j]=vt;
|
|
}
|
|
lambdav=t2[updrow];
|
|
//--- InvA=InvA - correction
|
|
for(i=0;i<n;i++)
|
|
{
|
|
vt=t1[i]/(1+lambdav);
|
|
for(i_=0;i_<n;i_++)
|
|
inva[i].Set(i_,inva[i][i_]-vt*t2[i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse matrix update by the Sherman-Morrison formula |
|
|
//| The algorithm updates matrix A^-1 when adding a vector to a |
|
|
//| column of matrix A. |
|
|
//| Input parameters: |
|
|
//| InvA - inverse of matrix A. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| UpdColumn - the column of A whose vector U was added. |
|
|
//| 0 <= UpdColumn <= N-1 |
|
|
//| U - the vector to be added to a column. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Output parameters: |
|
|
//| InvA - inverse of modified matrix A. |
|
|
//+------------------------------------------------------------------+
|
|
static void CInverseUpdate::RMatrixInvUpdateColumn(CMatrixDouble &inva,const int n,
|
|
const int updcolumn,double &u[])
|
|
{
|
|
//--- create arrays
|
|
double t1[];
|
|
double t2[];
|
|
//--- create variables
|
|
int i=0;
|
|
double lambdav=0;
|
|
double vt=0;
|
|
int i_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(t1,n);
|
|
ArrayResizeAL(t2,n);
|
|
//--- T1=InvA * U
|
|
//--- Lambda=v * InvA * U
|
|
for(i=0;i<n;i++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
vt+=inva[i][i_]*u[i_];
|
|
t1[i]=vt;
|
|
}
|
|
lambdav=t1[updcolumn];
|
|
//--- T2=v*InvA
|
|
for(i_=0;i_<n;i_++)
|
|
t2[i_]=inva[updcolumn][i_];
|
|
//--- InvA=InvA - correction
|
|
for(i=0;i<n;i++)
|
|
{
|
|
vt=t1[i]/(1+lambdav);
|
|
for(i_=0;i_<n;i_++)
|
|
inva[i].Set(i_,inva[i][i_]-vt*t2[i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse matrix update by the Sherman-Morrison formula |
|
|
//| The algorithm computes the inverse of matrix A+u*v? by using the |
|
|
//| given matrix A^-1 and the vectors u and v. |
|
|
//| Input parameters: |
|
|
//| InvA - inverse of matrix A. |
|
|
//| Array whose indexes range within |
|
|
//| [0..N-1, 0..N-1]. |
|
|
//| N - size of matrix A. |
|
|
//| U - the vector modifying the matrix. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| V - the vector modifying the matrix. |
|
|
//| Array whose index ranges within [0..N-1]. |
|
|
//| Output parameters: |
|
|
//| InvA - inverse of matrix A + u*v'. |
|
|
//+------------------------------------------------------------------+
|
|
static void CInverseUpdate::RMatrixInvUpdateUV(CMatrixDouble &inva,const int n,
|
|
double &u[],double &v[])
|
|
{
|
|
//--- create arrays
|
|
double t1[];
|
|
double t2[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double lambdav=0;
|
|
double vt=0;
|
|
int i_=0;
|
|
//--- allocation
|
|
ArrayResizeAL(t1,n);
|
|
ArrayResizeAL(t2,n);
|
|
//--- T1=InvA * U
|
|
//--- Lambda=v * T1
|
|
for(i=0;i<n;i++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
vt+=inva[i][i_]*u[i_];
|
|
t1[i]=vt;
|
|
}
|
|
lambdav=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
lambdav+=v[i_]*t1[i_];
|
|
//--- T2=v*InvA
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0;i_<n;i_++)
|
|
vt+=v[i_]*inva[i_][j];
|
|
t2[j]=vt;
|
|
}
|
|
//--- InvA=InvA - correction
|
|
for(i=0;i<n;i++)
|
|
{
|
|
vt=t1[i]/(1+lambdav);
|
|
for(i_=0;i_<n;i_++)
|
|
inva[i].Set(i_,inva[i][i_]-vt*t2[i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Schur decomposition |
|
|
//+------------------------------------------------------------------+
|
|
class CSchur
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CSchur(void);
|
|
~CSchur(void);
|
|
//--- method
|
|
static bool RMatrixSchur(CMatrixDouble &a,const int n,CMatrixDouble &s);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSchur::CSchur(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSchur::~CSchur(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine performing the Schur decomposition of a general matrix|
|
|
//| by using the QR algorithm with multiple shifts. |
|
|
//| The source matrix A is represented as S'*A*S = T, where S is an |
|
|
//| orthogonal matrix (Schur vectors), T - upper quasi-triangular |
|
|
//| matrix (with blocks of sizes 1x1 and 2x2 on the main diagonal). |
|
|
//| Input parameters: |
|
|
//| A - matrix to be decomposed. |
|
|
//| Array whose indexes range within [0..N-1, 0..N-1]. |
|
|
//| N - size of A, N>=0. |
|
|
//| Output parameters: |
|
|
//| A - contains matrix T. |
|
|
//| Array whose indexes range within [0..N-1, 0..N-1]. |
|
|
//| S - contains Schur vectors. |
|
|
//| Array whose indexes range within [0..N-1, 0..N-1]. |
|
|
//| Note 1: |
|
|
//| The block structure of matrix T can be easily recognized: |
|
|
//| since all the elements below the blocks are zeros, the |
|
|
//| elements a[i+1,i] which are equal to 0 show the block border.|
|
|
//| Note 2: |
|
|
//| The algorithm performance depends on the value of the |
|
|
//| internal parameter NS of the InternalSchurDecomposition |
|
|
//| subroutine which defines the number of shifts in the QR |
|
|
//| algorithm (similarly to the block width in block-matrix |
|
|
//| algorithms in linear algebra). If you require maximum |
|
|
//| performance on your machine, it is recommended to adjust |
|
|
//| this parameter manually. |
|
|
//| Result: |
|
|
//| True, |
|
|
//| if the algorithm has converged and parameters A and S |
|
|
//| contain the result. |
|
|
//| False, |
|
|
//| if the algorithm has not converged. |
|
|
//| Algorithm implemented on the basis of the DHSEQR subroutine |
|
|
//| (LAPACK 3.0 library). |
|
|
//+------------------------------------------------------------------+
|
|
static bool CSchur::RMatrixSchur(CMatrixDouble &a,const int n,CMatrixDouble &s)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int info=0;
|
|
int i=0;
|
|
int j=0;
|
|
//--- create arrays
|
|
double tau[];
|
|
double wi[];
|
|
double wr[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble s1;
|
|
//--- Upper Hessenberg form of the 0-based matrix
|
|
COrtFac::RMatrixHessenberg(a,n,tau);
|
|
COrtFac::RMatrixHessenbergUnpackQ(a,n,tau,s);
|
|
//--- Convert from 0-based arrays to 1-based,
|
|
//--- then call InternalSchurDecomposition
|
|
//--- Awkward, of course, but Schur decompisiton subroutine
|
|
//--- is too complex to fix it.
|
|
a1.Resize(n+1,n+1);
|
|
s1.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
a1[i].Set(j,a[i-1][j-1]);
|
|
s1[i].Set(j,s[i-1][j-1]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CHsSchur::InternalSchurDecomposition(a1,n,1,1,wr,wi,s1,info);
|
|
result=info==0;
|
|
//--- convert from 1-based arrays to -based
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
a[i-1].Set(j-1,a1[i][j]);
|
|
s[i-1].Set(j-1,s1[i][j]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|