# full inverse of a square matrix using Gauss-Jordan elimination
# without pivoting (interchanging rows)
#
# not for real use; only to check the GaussJordanPivot function
# this function will fail if a pivot is (near)zero!
GaussJordanInverseNoPivoting <- function(A.) {
A <- A. # we will change it
p <- (d <- dim(A))[1L]
if (!is.numeric(A) || length(d) != 2L || p != d[2L])
stop("'A' is not a square numeric matrix")
# the code below is based on Matlab code presented in Figure 1
# of the following paper:
#
# "Efficient Matrix Inversion via Gauss-Jordan Elimination and its
# Parralelization" (1998).
# Authors: Quintana, Quintana, Sun & van de Geijn
# retrieved from www.cs.utexas.edu/users/plapack/papers/inverse-tr.ps
for(k in 1:p) {
pivot <- A[k,k]
# column scaling
A[-k,k] <- -A[-k,k]/pivot; A[k,k] <- 0.0
# rank-1 update
A <- A + tcrossprod(A[,k], A[k,])
# row scaling
A[k,-k] <- A[k,-k]/pivot; A[k,k] <- 1.0/pivot
}
A
}
# just for fun, *with* pivoting of rows
GaussJordanInverse <- function(A.) {
A <- A.
p <- (d <- dim(A))[1L]
if (!is.numeric(A) || length(d) != 2L || p != d[2L])
stop("'A' is not a square numeric matrix")
ipiv <- 1:p
for(k in 1:p) {
# look for largest element in the column
tmp <- A[,k]; if(k > 1L) tmp[1:(k-1)] <- 0.0; k.max <- which.max(tmp)
pivot <- A[k.max,k]
# pivoting rows?
if(k != k.max) {
tmp <- A[k,]; A[k,] <- A[k.max,]; A[k.max,] <- tmp
tmp <- ipiv[k]; ipiv[k] <- ipiv[k.max]; ipiv[k.max] <- tmp
}
# column scaling
A[-k,k] <- -A[-k,k]/pivot; A[k,k] <- 0.0
# rank-1 update
A <- A + tcrossprod(A[,k], A[k,])
# row scaling
A[k,-k] <- A[k,-k]/pivot; A[k,k] <- 1.0/pivot
}
# backward permutation
A[,ipiv] <- A
A
}
# perform a single 'Gauss-Jordan' pivot on a
# square matrix 'A' using a diagonal element A[k,k]
# as a pivot element
GaussJordanPivot <- function(A., k=1L) {
A <- A.
p <- (d <- dim(A))[1L]
if (!is.numeric(A) || length(d) != 2L || p != d[2L])
stop("'A' is not a square numeric matrix")
pivot <- A[k,k]
# column scaling
A[-k,k] <- -A[-k,k]/pivot; A[k,k] <- 0.0
# rank-1 update
A <- A + tcrossprod(A[,k], A[k,])
# row scaling
A[k,-k] <- A[k,-k]/pivot; A[k,k] <- 1.0/pivot
A
}
# fabin3 (2sls) for a single factor (that is what Mplus is doing)
# S is a selection of ov.names corresponding with the indicators
# of the (single) factor
# 'Hagglund' version (slow)
fabin3.uni2 <- function(S) {
# Gosta Hagglund (Psychometrika, 1982, 47)
nvar <- ncol(S)
if(nvar < 3) {
return( rep(1, nvar) )
}
out <- numeric( nvar ); out[1L] <- 1.0
for(i in 2:nvar) {
idx3 <- (1:nvar)[-c(i, 1L)]
s23 <- S[i, idx3]
S31 <- S[idx3, 1L]; S13 <- S[1L, idx3]
S33 <- S[idx3,idx3]
out[i] <- ( s23 %*% solve(S33) %*% S31 %*%
solve(S13 %*% solve(S33) %*% S31) )
}
out
}
# 'Jennrich' version (fast)
fabin3.uni <- function(S) {
nvar <- ncol(S)
if(nvar < 3) {
return( rep(1, nvar) )
}
# zero out references
S[1L, 1L] <- 0.0
S.tilde <- try(solve(S), silent = TRUE)
if(inherits(S.tilde, "try-error")) {
return( rep(1, nvar) )
}
out <- numeric( nvar ); out[1L] <- 1.0
for(i in 2:nvar) {
S.bar <- GaussJordanPivot(S.tilde[c(i,1L), c(i,1L)], k=1L)
out[i] <- -S.bar[1,-1] - (S[i,1L] %*% S.bar[-1,-1])
}
out
}
fabin2 <- function(S, ref.idx=NULL) {
# Gosta Hagglund (Psychometrika, 1982, 47)
nvar <- ncol(S); nfac <- length(ref.idx)
stopifnot(nvar >= 3*nfac)
out <- matrix(0, nvar, nfac)
for(i in 1:nvar) {
if(i %in% ref.idx) {
out[i, ref.idx == i] <- 1.0
next
}
idx3 <- (1:nvar)[-c(i, ref.idx)]
s23 <- S[i, idx3]
S31 <- S[idx3, ref.idx]; S13 <- S[ref.idx, idx3]
out[i,] <- s23 %*% S31 %*% solve(S13 %*% S31)
}
out
}
fabin3 <- function(S, ref.idx=NULL) {
# Gosta Hagglund (Psychometrika, 1982, 47)
nvar <- ncol(S); nfac <- length(ref.idx)
stopifnot(nvar >= 3*nfac)
out <- matrix(0, nvar, nfac)
for(i in 1:nvar) {
if(i %in% ref.idx) {
out[i, ref.idx == i] <- 1.0
next
}
idx3 <- (1:nvar)[-c(i, ref.idx)]
s23 <- S[i, idx3]
S31 <- S[idx3, ref.idx]; S13 <- S[ref.idx, idx3]
S33 <- S[idx3,idx3]
out[i,] <- ( s23 %*% solve(S33) %*% S31 %*%
solve(S13 %*% solve(S33) %*% S31) )
}
out
}
# using the 'jennrich' computations (much faster!)
fabin3.jennrich <- function(S, ref.idx=NULL) {
# Robert I. Jennrich (Psychometrika, 1987, 52)
nvar <- ncol(S); nfac <- length(ref.idx)
stopifnot(nvar >= 3*nfac)
# zero out references
S[ref.idx, ref.idx] <- 0.0
S.tilde <- solve(S)
out <- matrix(0, nvar, nfac)
for(i in 1:nvar) {
if(i %in% ref.idx) {
out[i, ref.idx == i] <- 1.0
next
}
S.bar <- GaussJordanPivot(S.tilde[c(i,ref.idx), c(i,ref.idx)], k=1L)
out[i,] <- -S.bar[1,-1] - (S[i,ref.idx] %*% S.bar[-1,-1])
}
out
}
# Hagglund table 1
JoreskogLawley1968.COR <- matrix(c(
1.000, 0.411, 0.479, 0.401, 0.370, 0.393, 0.078, 0.389, 0.411,
0.411, 1.000, 0.463, 0.223, 0.198, 0.244, -0.042, 0.169, 0.324,
0.479, 0.463, 1.000, 0.231, 0.272, 0.357, -0.126, 0.153, 0.307,
0.401, 0.223, 0.231, 1.000, 0.659, 0.688, 0.215, 0.221, 0.256,
0.370, 0.198, 0.272, 0.659, 1.000, 0.649, 0.293, 0.279, 0.324,
0.393, 0.244, 0.357, 0.688, 0.649, 1.000, 0.226, 0.298, 0.294,
0.078, -0.042, -0.126, 0.215, 0.293, 0.226, 1.000, 0.602, 0.446,
0.389, 0.169, 0.153, 0.221, 0.279, 0.298, 0.602, 1.000, 0.630,
0.411, 0.324, 0.307, 0.256, 0.324, 0.294, 0.446, 0.630, 1.000),
9, 9, byrow=TRUE)
# round(fabin2(JoreskogLawley1968.COR, ref.idx=c(1,4,7)), 3)
# round(fabin3(JoreskogLawley1968.COR, ref.idx=c(1,4,7)), 3)
# Jennrich
Emmett.1949 <- matrix(c(
1.0, 0.523, 0.395, 0.471, 0.346, 0.426, 0.576, 0.434, 0.639,
0.523, 1.0, 0.479, 0.506, 0.418, 0.462, 0.547, 0.283, 0.645,
0.395, 0.479, 1.0, 0.355, 0.270, 0.254, 0.452, 0.219, 0.504,
0.471, 0.506, 0.355, 1.0, 0.691, 0.791, 0.443, 0.285, 0.505,
0.346, 0.418, 0.270, 0.691, 1.0, 0.679, 0.383, 0.149, 0.409,
0.426, 0.462, 0.254, 0.791, 0.679, 1.0, 0.372, 0.314, 0.472,
0.576, 0.547, 0.452, 0.443, 0.383, 0.372, 1.0, 0.385, 0.680,
0.434, 0.283, 0.219, 0.285, 0.149, 0.314, 0.385, 1.0, 0.470,
0.639, 0.645, 0.504, 0.505, 0.409, 0.472, 0.680, 0.470, 1.0),
9, 9, byrow=TRUE)
colnames(Emmett.1949) <- rownames(Emmett.1949) <- paste("x",1:9,sep="")
# round( fabin3(Emmett.1949, ref.idx=c(1,4,7)), 3)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.