R/matrixparametrizations.R

Defines functions .par.inv.transform.orth.matrix.cayley .par.transform.orth.matrix.cayley .num.inv.transform.positive .num.transform.positive .par.inv.transform.orth.matrix.givens .par.transform.orth.matrix.givens .par.inv.transform.decomp.matrix .par.transform.decomp.matrix .par.inv.transform.decomp.real.matrix .par.transform.decomp.real.matrix .par.inv.transform.decomp.neg.real.matrix .par.transform.decomp.neg.real.matrix .par.inv.transform.decomp.pos.real.matrix .par.transform.decomp.pos.real.matrix .par.inv.transform.decomp.sym.matrix .par.transform.decomp.sym.matrix .par.inv.transform.invert.qr.matrix .par.transform.invert.qr.matrix .par.inv.transform.twobytwo.matrix .par.transform.twobytwo.matrix .par.inv.transform.invert.svd.matrix .par.transform.invert.svd.matrix .par.inv.transform.invert.matrix .par.transform.invert.matrix .par.inv.transform.lowertri.matrix .par.transform.lowertri.matrix .par.inv.transform.uppertri.matrix .par.transform.uppertri.matrix .M.mult.givens .givens.mult.M .col.rot .row.rot .inv.rho.givens.real .givens.real .par.inv.transform.symmetric.matrix .par.transform.symmetric.matrix

Documented in .col.rot .givens.mult.M .givens.real .inv.rho.givens.real .M.mult.givens .num.inv.transform.positive .num.transform.positive .par.inv.transform.decomp.matrix .par.inv.transform.decomp.neg.real.matrix .par.inv.transform.decomp.pos.real.matrix .par.inv.transform.decomp.real.matrix .par.inv.transform.decomp.sym.matrix .par.inv.transform.invert.matrix .par.inv.transform.invert.qr.matrix .par.inv.transform.invert.svd.matrix .par.inv.transform.lowertri.matrix .par.inv.transform.orth.matrix.cayley .par.inv.transform.orth.matrix.givens .par.inv.transform.symmetric.matrix .par.inv.transform.twobytwo.matrix .par.inv.transform.uppertri.matrix .par.transform.decomp.matrix .par.transform.decomp.neg.real.matrix .par.transform.decomp.pos.real.matrix .par.transform.decomp.real.matrix .par.transform.decomp.sym.matrix .par.transform.invert.matrix .par.transform.invert.qr.matrix .par.transform.invert.svd.matrix .par.transform.lowertri.matrix .par.transform.orth.matrix.cayley .par.transform.orth.matrix.givens .par.transform.symmetric.matrix .par.transform.twobytwo.matrix .par.transform.uppertri.matrix .row.rot

## This file is part of mvSLOUCH

## This software comes AS IS in the hope that it will be useful WITHOUT ANY WARRANTY, 
## NOT even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
## Please understand that there may still be bugs and errors. Use it at your own risk. 
## We take no responsibility for any errors or omissions in this package or for any misfortune 
## that may befall you or others as a result of its use. Please send comments and report 
## bugs to Krzysztof Bartoszek at krzbar@protonmail.ch .


## ------------- parametrization of symmetric positive definite matrix ------------------------------
.sym.par <- function (x,nchar) {
## function taken from ouch package
    mSym<-NA
    nchar <- floor(sqrt(2*length(x)))
    if (nchar*(nchar+1)!=2*length(x)) {
        .my_warning("a symmetric matrix is parameterized by a triangular number of parameters \n",FALSE,FALSE)
    }
    y <- matrix(0,nchar,nchar)
    y[lower.tri(y,diag=TRUE)] <- x
    mSym<-y%*%t(y)
    mSym
}

.sym.unpar <- function (x) {
## function taken from ouch package
  y <- t(.my_chol(x))
  y[lower.tri(y,diag=TRUE)]
}

.par.transform.symmetric.matrix<-function(vParams,n){
    A<-matrix(NA,n,n)
    A[upper.tri(A,diag=TRUE)]<-vParams
    A[lower.tri(A)]<-t(A)[lower.tri(A)]
    A
}

.par.inv.transform.symmetric.matrix<-function(A){
    A[upper.tri(A,diag=TRUE)]
}
## -------------------------------------------------------------------------------------------------





## ------------- Givens rotation functions ---------------------------------------------------------
## these functions are taken nearly as is from :
## for the real case :
## G. Golub, C. Van Loan; Matrix Computations; The Johns Hopkins University Press; 1989; Baltimore
## for the complex case : ## this still has to be done and worked on
## D. Bindel, J. Demmel, W. Kahan; On Computing Givens Rotations Reliably and Efficiently;ACM Transactions on Mathematical Software 28(2):206-238

.givens.real<-function(a,b,getrho=TRUE,cs=NULL){## algorithm 5.1.5 p202 and eq 5.1.9 p204
    if (is.null(cs)){
	if (b==0){cG<-1;sG<-0}
	else{
	    if (abs(b)>abs(a)){tau<-(-1)*a/b;sG<-1/sqrt(1+tau^2);cG<-sG*tau}
	    else{tau<-(-1)*b/a;cG<-1/sqrt(1+tau^2);sG<-cG*tau}
	}
    }else{cG<-cs[1];sG<-cs[2];}
    if (getrho){
	if (cG==0){rho<-1}
	else{
	    if(abs(sG)<abs(cG)){rho<-sign(cG)*sG/2}
	    else{rho<-2*sign(sG)/cG}	    
	}	
    }else{rho<-c(cG,sG)}
    rho
}

.inv.rho.givens.real<-function(rho){## eq 5.1.10 p205 -> to get a and b back do .row.rot or .col.rot
    if (rho==1){cG<-0;sG<-1}
    else{
	if (abs(rho)<1){sG<-2*rho;cG<-sqrt(1-sG^2)}
	else{cG<-2/rho;sG<-sqrt(1-cG^2)}
    }
    c(cG,sG)
}

.row.rot<-function(M,cG,sG){## algorithm 5.1.6 p203
    if (nrow(M)!=2){.my_message("Cannot perform .row.rot when M does not have 2 rows \n",FALSE);rotM<-NA}
    else{rotM<-apply(M,2,function(x){c(cG*x[1]-sG*x[2],sG*x[1]+cG*x[2])})}
    rotM
}

.col.rot<-function(M,cG,sG){## algorithm 5.1.7 p203
    if (ncol(M)!=2){.my_message("Cannot perform .col.rot when M does not have 2 columns \n",FALSE);rotM<-NA}
    else{rotM<-apply(M,1,function(x){c(cG*x[1]-sG*x[2],sG*x[1]+cG*x[2])})}
    rotM
}

.givens.mult.M<-function(M,cG,sG,i1,i2){
    M[c(i1,i2),]<-.row.rot(M[c(i1,i2),],cG,sG)
    M
}

.M.mult.givens<-function(M,cG,sG,j1,j2){
    M[,c(j1,j2)]<-.col.rot(M[,c(j1,j2)],cG,sG)
    M
}
## -------------------------------------------------------------------------------------------------

## ------------ parametrization of traingular matrix -----------------------------------------------
.par.transform.uppertri.matrix<-function(vParams,k){
    A<-matrix(0,k,k)
    A[upper.tri(A,diag=TRUE)]<-vParams
    A
}

.par.inv.transform.uppertri.matrix<-function(A){
    A[upper.tri(A,diag=TRUE)]
}

.par.transform.lowertri.matrix<-function(vParams,k){
    A<-matrix(0,k,k)
    A[lower.tri(A,diag=TRUE)]<-vParams
    A
}

.par.inv.transform.lowertri.matrix<-function(A){
    A[lower.tri(A,diag=TRUE)]
}
## -------------------------------------------------------------------------------------------------



## ------------ parametrization of invertible matrix -----------------------------------------------
## ------------ needed for A's 2x2  parametrization ------------------------------------------------
.par.transform.invert.matrix<-function(vParams,n,method="qr",minRdiag=0.01){
    lA<-NA
    if (method=="qr"){lA<-.par.transform.invert.qr.matrix(vParams,n,NULL,NULL,minRdiag)}
    if (method=="svd"){lA<-.par.transform.invert.svd.matrix(vParams,n)}
    lA
}

.par.inv.transform.invert.matrix<-function(A,tol=1e-15,method="qr",mCsigns=NULL,minRdiag=0.01){
    lAparams<-NA
    if (method=="qr"){lAparams<-.par.inv.transform.invert.qr.matrix(A,mCsigns,tol,minRdiag)}
    if (method=="svd"){lAparams<-.par.inv.transform.invert.svd.matrix(A,tol)}
    lAparams
}

.par.transform.invert.svd.matrix<-function(vParams,n){
## parametrization is based on SVD but not exactly : D is allowed to be negative
## the reason is for the parametrization of U,V on the diagonal after Givens zeroing
## we can have negative values, ie rho does not hold information on the sign
    A<-NA
    if (length(vParams)!=n^2){.my_message(paste("Cannot calculate A, vParams should be of length : ",n^2," \n"),FALSE)}
    else{
	k<-(length(vParams)-n)/2
	D<-diag(vParams[c(1:n)],n,n)
	U<-.par.transform.orth.matrix.givens(vParams[(n+1):(n+k)],n)
	V<-.par.transform.orth.matrix.givens(vParams[(n+k+1):(n^2)],n)
	A<-U%*%D%*%t(V)	
    }
    A
}

.par.inv.transform.invert.svd.matrix<-function(A,tol=1e-15){
    n<-nrow(A)
    vParams<-rep(NA,n^2)
    svdA<-svd(A)    
    vParamsU<-.par.inv.transform.orth.matrix.givens(svdA$u,tol,TRUE)
    vParamsV<-.par.inv.transform.orth.matrix.givens(svdA$v,tol,TRUE)
    vParams[1:n]<-svdA$d*vParamsU[1:n]*vParamsV[1:n] ## this allows the ``singular values'' to be negative but we don't care
    vParamsU<-vParamsU[-(1:n)]
    vParamsV<-vParamsV[-(1:n)]
    vParams[(n+1):(n^2)]<-c(vParamsU,vParamsV)
    vParams    
}

.par.transform.twobytwo.matrix<-function(vParams){
    matrix(vParams,2,2)
}

.par.inv.transform.twobytwo.matrix<-function(A){
    c(A)
}

.par.transform.invert.qr.matrix<-function(vParams,n,mCsigns=NULL,vDiagQ=NULL,minRdiag=0.01){
## the parametrization is done via QR decomposition assuming Rs diagonal is meant to be positive
    A<-NA
    mQcSigns<-NULL
    if(!is.null(mCsigns)){mQcSigns<-mCsigns}
    if (length(vParams)!=n^2){.my_message(paste("Number of parameters describing A must be ",n,"^2=",n^2," \n"),FALSE)}
    else{
	if (n==1){A<-vParams[1]}
	else{
	    k<-n*(n-1)/2
	    ## first n(n-1)/2 parameters are the Givens rotions of Q
	    lQparam<-.par.transform.orth.matrix.givens(vParams[1:k],n,mQcSigns,vDiagQ)
	    Q<-lQparam$Q
	    mQcSigns<-lQparam$cSigns
	    ## next n parameters are R's diagonal, they must be positive
	    R<-matrix(0,n,n)
	    diag(R)<-exp(vParams[(k+1):(k+n)])+minRdiag
	    ## last n(n-1)/2 parameters are R's upper triangle
	    R[upper.tri(R)]<-vParams[(k+n+1):(n^2)]	
	    A<-Q%*%R*(-1) ## turns out in R's qr need to multiply by (-1) to get back original
	}
    }
    list(A=A,QcSigns=mQcSigns)
}

.par.inv.transform.invert.qr.matrix<-function(A,mCSigns=NULL,tol=1e-15,minRdiag=0.01){
    n<-nrow(A)    
    vParams<-rep(NA,n^2)
    if (ncol(A)!=n){.my_message("A must be a square matrix.\n",FALSE)}
    else{
	if (n==1){vParams[1]<-A[1,1];mCSigns=1}
	else{
    	    k<-n*(n-1)/2
	    qrA<-qr(A)   		
	    QqrA<-qr.Q(qrA)
	    RqrA<-qr.R(qrA)
    	    minRdiag<-min(minRdiag,min(diag(RqrA)/2))
	    vNegR<-which(diag(RqrA<0))
    	    RqrA[vNegR,]<-RqrA[vNegR,]*(-1)
	    QqrA[,vNegR]<-QqrA[,vNegR]*(-1)
	    lQparams<-.par.inv.transform.orth.matrix.givens(QqrA,mCSigns,tol)
	    vParams[1:k]<-lQparams$vParams
	    mCSigns<-lQparams$cSigns
	    vDiagQ<-lQparams$vDiagQ
	    vParams[(k+1):(k+n)]<-log(diag(RqrA)-minRdiag)
	    vParams[(k+n+1):(n^2)]<-RqrA[upper.tri(RqrA)]
	}	    
    }
    list(vParams=vParams,cSigns=mCSigns,vDiagQ=vDiagQ)
}
## -------------------------------------------------------------------------------------------------



## ------------ parametrization of decomposable matrix ---------------------------------------------
## ----------------- symmetric positive definite matrix --------------------------------------------
## this is meant to be an alternative parametrization to the one in sym.par (from ouch)

.par.transform.decomp.sym.matrix<-function(vParams,n){
## the eigenvalue decomposition of A=PJP^(T) P parametrized by givens rotations
## it is assumed that the eigenvectors (P's columns )are of unit length 
    A<-NA
    if (length(vParams)!=n^2){.my_message(paste("Cannot calculate A, vParams should be of length : ",n^2," \n"),FALSE)}
    else{
	if (n==1){A<-matrix(vParams[1],1,1)}
	else{
	    k<-n*(n-1)/2
	    if (is.complex(vParams[1:n])){
	    	.my_message("Thera are complex eigenvalues, calling complex calculations! \n",FALSE)
		A<-.par.transform.decomp.matrix(vParams,n)
	    }else{
		J<-diag(vParams[1:n],n,n) ## the eigenvalues	    
		P<-.par.transform.orth.matrix.givens(vParams[(n+1):(n+k)],n)
		A<-P%*%J%*%t(P)
	    }
	}
    }
    A
}

.par.inv.transform.decomp.sym.matrix<-function(A,tol=1e-15){
    n<-nrow(A)    
    vParams<-rep(NA,n^2)
    if (ncol(A)!=n){.my_message("A must be a square matrix. \n",FALSE)}
    else{
	if (n==1){vParams<-A[1,1]}
	else{
	    eigA<-eigen(A)
	    if(is.complex(eigA$values)){
		.my_message("A has complex eigen values, calling complex parametrization! \n",FALSE)
		vParams<-.par.inv.transform.decomp.matrix(A)
	    }else{
	        k<-n*(n-1)/2
		vParams[1:n]<-eigA$values
		vParams[(n+1):(n+k)]<-.par.inv.transform.orth.matrix.givens(eigA$vectors,tol)		
	    }	    
	}
    }
    vParams
}


## ----------------- real eigenvalues --------------------------------------------------------------

## ---------------- positive definite --------------------------------------------------------------
.par.transform.decomp.pos.real.matrix<-function(vParams,n,mCSigns=NULL,minEigenValue=1.5e-8){
## first n parameters are the logs of the eigenvalues -> we want all to be positive
## next numbers are somehow the eigenvectors
    vParams[1:n]<-exp(rev(sort(vParams[1:n])))+minEigenValue
    .par.transform.decomp.real.matrix(vParams,n,mCSigns)    
}

.par.inv.transform.decomp.pos.real.matrix<-function(A,eigenSigns=NULL,mCSigns=NULL,tol=1e-15,minEigenValue=1.5e-8){
    lAparams<-.par.inv.transform.decomp.real.matrix(A,eigenSigns,mCSigns,tol)
    n<-nrow(A)
    vTooSmall<-which(lAparams$vParams[1:n]<minEigenValue)
    if (length(vTooSmall)>0){
	lAparams$vParams[vTooSmall]<-(1.25)*minEigenValue
    }
    lAparams$vParams[1:n]<-log(lAparams$vParams[1:n]-minEigenValue)
    lAparams
}

.par.transform.decomp.neg.real.matrix<-function(vParams,n,mCSigns=NULL,maxEigenValue=1.5e-8){
## first n parameters are the logs of the eigenvalues -> we want all to be positive
## next numbers are somehow the eigenvectors
    vParams[1:n]<-(-1)*exp(rev(sort(vParams[1:n])))-maxEigenValue
    .par.transform.decomp.real.matrix(vParams,n,mCSigns)    
}

.par.inv.transform.decomp.neg.real.matrix<-function(A,eigenSigns=NULL,mCSigns=NULL,tol=1e-15,maxEigenValue=1.5e-8){
    lAparams<-.par.inv.transform.decomp.real.matrix(A,eigenSigns,mCSigns,tol)
    vTooBig<-which(lAparams$vParams[1:n]>(-1)*maxEigenValue)
    if (length(vTooBig)>0){
	lAparams$vParams[vTooBig]<-(-1.25)*maxEigenValue
    }
    n<-nrow(A)
    lAparams$vParams[1:n]<-log((-1)*lAparams$vParams[1:n]-maxEigenValue)
    lAparams
}

.par.transform.decomp.real.matrix<-function(vParams,n,mCSigns=NULL){
## the eigenvalue decomposition of A=PJP^(-1) P parametrized by QR 
## it is assumed that the eigenvectors (P's columns )are of unit length -> this should make
## R's columns also of unit length -> we assume diagonal of R is positive!
## here we do not care for the diagonal of Q after the Givens rotations as this will cancel out
## in PJP^-1
    A<-NA
    eigenSigns<-NA
    if (length(vParams)!=n^2){.my_message(paste("Cannot calculate A, vParams should be of length : ",n^2," \n"),FALSE)}
    else{
	if (n==1){A<-matrix(vParams[1],1,1);eigenSigns<-c(1)}
	else{	    
	    if (is.complex(vParams[1:n])){
	    	.my_message("Thera are complex eigenvalues, calling complex calculations! \n",FALSE)
		A<-.par.transform.decomp.matrix(vParams,n)
	    }else{
		k<-n*(n-1)/2
		J<-diag(vParams[1:n],n,n) ## the eigenvalues	    
		if (length(which(diag(J)==0))>0){.my_warning("WARNING : we have zero eigenvalues, matrix is singular. \n",FALSE,FALSE)}
		lQparam<-.par.transform.orth.matrix.givens(vParams[(n+1):(n+k)],n,mCSigns)
		Q<-lQparam$Q
		mQcSigns<-lQparam$cSigns
		mR<-matrix(0,n,n)
		mR[upper.tri(mR)]<-vParams[(n+k+1):(n^2)]
		mR<-sapply(1:ncol(mR),function(i,mR){
		    k<-nrow(mR)
		    orgcolR<-mR[,i]
		    colR<-c(rep(NA,i-1),rep(0,n-i+1))
		    if (i>1){
		    colR[1]<-atan(orgcolR[1])*2/pi
		    if (i>2){
			for (j in 2:(i-1)){
			    a<-sqrt(1-sum(colR[1:(j-1)]^2))
			    colR[j]<-atan(orgcolR[j])*2*a/pi
			}
		    }
		    }
		    colR
		},mR=mR,simplify=TRUE)
		diag(mR)<-c(1,sapply(2:ncol(mR),function(j){sqrt(1-sum(mR[,j]^2))})) ## should work ok as we previously have zeroed R
		P<-Q%*%mR
		eigenSigns<-sign(P[1,])
		invP<-solve(mR)%*%t(Q) ## inversing using the QR would be quicker and faster but we need to fix the eigenvectors
		A<-P%*%J%*%invP	
	    }
	}
    }
    list(A=A,eigenSigns=eigenSigns,QcSigns=mQcSigns)
}

.par.inv.transform.decomp.real.matrix<-function(A,eigenSigns=NULL,mCSigns=NULL,tol=1e-15){
    n<-nrow(A)    
    vParams<-rep(NA,n^2)
    if (ncol(A)!=n){.my_message("A must be a square matrix. \n",FALSE)}
    else{
	if (n==1){vParams<-A[1,1];mCSigns=1}
	else{
	    eigA<-eigen(A)
	    if (!is.null(eigenSigns)){
		eigA$vectors<-apply(rbind(eigenSigns,eigA$vectors),2,function(x){if (sign(x[2])!=x[1]){x<-(-1)*x};x[-1]}) ## we want to fix the eigenvector matrix
	    }
	    if(is.complex(eigA$values)){
		.my_message("A has complex eigen values, calling complex parametrization! \n",FALSE)
		vParams<-.par.inv.transform.decomp.matrix(A,eigenSigns,mCSigns)
	    }else{
	        k<-n*(n-1)/2
		vParams[1:n]<-eigA$values
		qrP<-qr(eigA$vectors)   		
		QqrP<-qr.Q(qrP)
		RqrP<-qr.R(qrP)
		vNegR<-which(diag(RqrP<0))
		RqrP[vNegR,]<-RqrP[vNegR,]*(-1)
		QqrP[,vNegR]<-QqrP[,vNegR]*(-1)
		lQparams<-.par.inv.transform.orth.matrix.givens(QqrP,mCSigns,tol)
		vParams[(n+1):(n+k)]<-lQparams$vParams
		mCSigns<-lQparams$cSigns
		RqrP<-sapply(1:ncol(RqrP),function(i,mR){
		    k<-nrow(mR)
		    orgcolR<-mR[,i]
		    colR<-c(rep(NA,i-1),rep(0,n-i+1))
		    if (i>1){## This turned out to be slightly faster than a while loop
			if (i>2){colR[2:(i-1)]<-tan(orgcolR[2:(i-1)]*0.5*pi/(sqrt(1-cumsum(orgcolR[1:(i-2)]^2))))}
			colR[1]<-tan(orgcolR[1]*0.5*pi)		    
		    }
		    colR
		},mR=RqrP,simplify=TRUE)
		vParams[(n+k+1):(n^2)]<-RqrP[upper.tri(RqrP)]
	    }	    
	}
    }
    list(vParams=vParams,cSigns=mCSigns)
}
## -------------------------------------------------------------------------------------------------
## ----------------- general case ------------------------------------------------------------------
.par.transform.decomp.matrix<-function(vParams,n){
    .my_message("Cannot do parametrization with general complex eigenvalues yet. \n",FALSE)
    matrix(NA,n,n)
}

.par.inv.transform.decomp.matrix<-function(A,eigenSigns,mCSigns){
    .my_message("Cannot do deparametrization with general complex eigenvalues yet. \n",FALSE)
    rep(NA,nrow(A)^2)
}
## -------------------------------------------------------------------------------------------------
## -------------------------------------------------------------------------------------------------


## ----------- parametrization of orthogonal matrix ------------------------------------------------
.par.transform.orth.matrix.givens<-function(vParams,n,mCSigns=NULL,vDiagQ=NULL){
    Q<-NA
    k<-n*(n-1)/2
    if (is.null(vDiagQ)){vDiagQ<-rep(1,n)}
    if (is.null(mCSigns)){mCSignsQ<-matrix(1,2,k)}
    else{mCSignsQ<-mCSigns}
    if (length(vParams)!=k){.my_message(paste("Cannot calculate Q, vParams should be of lenght : ",n*(n-1)/2," \n"),FALSE);}
    else{
	if (n==1){Q<-matrix(vParams[1],1,1)}## vParams[1] has to be 1 or -1 for orthogonality
	else{
	    Q<-diag(vDiagQ,n,n)
	    ij<-k
    	    for (j in (n-1):1){
		for (i in n:(j+1)){
		    rho<-vParams[ij]
		    if (abs(rho)<1.5){rho<-rho/3}
		    else{rho<-rho*1.5}
		    cs<-.inv.rho.givens.real(rho)
		    if(!is.null(mCSigns)){
			if (sign(cs[1])!=mCSigns[1,ij]){cs[1]<-(-1)*cs[1] }
			if (sign(cs[2])!=mCSigns[2,ij]){cs[2]<-(-1)*cs[2] }
		    }
		    else{mCSignsQ[,ij]<-sign(cs)}
		    Q<-.givens.mult.M(Q,cs[1],-cs[2],j,i) 
		    ij<-ij-1;
		}
	    }
	}
    }
    list(Q=Q,cSigns=mCSignsQ,vDiagQ=vDiagQ)
}

.par.inv.transform.orth.matrix.givens<-function(Q,mCSigns=NULL,tol=1e-15,check1=FALSE){
    n<-nrow(Q)
    k<-n*(n-1)/2
    vParams<-rep(NA,k)
    if (is.null(mCSigns)){mCSignsQ<-matrix(1,2,k)}
    else{mCSignsQ<-mCSigns}
    if (ncol(Q)!=n){.my_message("Q must be a square matrix. \n",FALSE)}
    else{
	if (n==1){vParams<-Q[1,1]}
	else{
	    ij<-1
	    GtQ<-Q
	    for (j in 1:(n-1)){
		for (i in (j+1):n){
		    cs<-.givens.real(GtQ[j,j],GtQ[i,j],getrho=FALSE)
		    if (!is.null(mCSigns)){
			if (sign(cs[1])!=mCSigns[1,ij]){cs[1]<-(-1)*cs[1]}
			if (sign(cs[2])!=mCSigns[2,ij]){cs[2]<-(-1)*cs[2]}		
		    }else{mCSignsQ[,ij]<-sign(cs)}
		    GtQ<-.givens.mult.M(GtQ,cs[1],cs[2],j,i) ## j<i so it should be given in this order so that rows not swapped
		    rho<-.givens.real(NULL,NULL,TRUE,cs)				    
		    if (abs(rho)<0.5){rho<-rho*3}
		    else{rho<-rho/1.5}
		    vParams[ij]<-rho
		    ij<-ij+1
		}
	    }
	    vDiagGtQ<-diag(GtQ)
	    ## this is a check for whether Q is orthogonal if it is we should have an identity matrix in GtQ (up to numerical prescision)
	    if (check1){vParams<-c(sign(diag(GtQ))*rep(1,n),vParams)}## add the sign of the diagonal	    
	    if (!is.null(tol)){## check whether we have calculated ok
	        diag(GtQ)<-abs(diag(GtQ)) ## we might have +/- 1 on the diagonal 
		GtQ[which(abs(GtQ)<tol)]<-0
		vCorrectNum<-which(abs(abs(GtQ)-diag(1,n,n))<tol) ## if on the diagonal
		GtQ[vCorrectNum]<-diag(1,n,n)[vCorrectNum]
		invGtQ<-solve(GtQ)
		if (length(GtQ[!GtQ==invGtQ])>0){.my_message(paste("Q matrix was not orthogonal or perhaps numerical errors, errors : ",GtQ[!GtQ==invGtQ]-invGtQ[!GtQ==invGtQ]," \n"),FALSE)}
	    }
	}
    }
    list(vParams=vParams,cSigns=mCSignsQ,vDiagQ=vDiagGtQ)
}
## -------------------------------------------------------------------------------------------------

## ----------------- Transform vector to positive numbers ------------------------------------------
.num.transform.positive<-function(x,method){
    y<-0
    if (method=="exp"){y<-exp(x)}
    if (method=="atan"){y<-atan(x)+pi/2}
    y
}

.num.inv.transform.positive<-function(y,method){
    x<-0
    if (method=="exp"){x<-log(y)}
    if (method=="atan"){x<-tan(y-pi/2)}
    x
}

## -------------------------------------------------------------------------------------------------

## ----------- Some additional transformations -------------------------------------------------
#.par.transform.normal.matrix<-function(vParams,kY){
#    eigenM<-diag(vParams[1:kY],ncol=kY,nrow=kY)
#    P<-.par.transform.orth.matrix(vParams[(kY+1):length(vParams)])
#    P%*%eigenM%*%solve(P)
#}

#.par.inv.transform.normal.matrix<-function(A){
#    eigA<-eigen(A)
#    c(eigA$values,.par.inv.transform.orth.matrix(eigA$vectors))
#}

.par.transform.orth.matrix.cayley<-function(vParams){
## This is Cayley's parametrization of an orthognal matrix as found on PlanetMath.org
## vParams represent a skew-symmetric matrix
## The only restriction on the orthogonal matrix P is that it does not have
## an eigenvalue equal to -1, which should in our case be OK
## as we're estimating so there is a prob of 0 of getting exact value
    kY<-(1+sqrt(1+8*length(vParams)))/2
    CayleyA<-matrix(0,nrow=kY,ncol=kY)
    CayleyA[upper.tri(CayleyA)]<-vParams
    CayleyA[lower.tri(CayleyA)]<-(-1)*vParams
    (diag(1,nrow=kY,ncol=kY)+CayleyA)%*%solve(diag(1,nrow=kY,ncol=kY)+CayleyA)
}

.par.inv.transform.orth.matrix.cayley<-function(A){
## This is Cayley's parametrization of an orthognal matrix as found on PlanetMath.org
## vParams represent a skew-symmetric matrix
## The only restriction on the orthogonal matrix P is that it does not have
## an eigenvalue equal to -1, which should in our case be OK
## as we're estimating so there is a prob of 0 of getting exact value
    CayleyA<-solve(A+diag(1,nrow=nrow(A),ncol=ncol(A)))%*%(A-diag(1,nrow=nrow(A),ncol=ncol(A)))
    CayleyA[upper.tri(CayleyA)] ## this is done bycol, upper.tri does not have option byrow=TRUE ...
}
## -------------------------------------------------------------------------------------------------

Try the mvSLOUCH package in your browser

Any scripts or data that you put into this service are public.

mvSLOUCH documentation built on Nov. 21, 2023, 1:08 a.m.