quad.form {emulator} | R Documentation |
Evaluate a quadratic form efficiently
Description
Given a square matrix M
of size n\times n
, and a
matrix x
of size n\times p
(or a vector of length
n
), evaluate various quadratic forms.
(in the following, x^T
denotes the complex conjugate of
the transpose, also known as the Hermitian transpose. This only
matters when considering complex numbers).
Function
quad.form(M,x)
evaluatesx^TMx
in an efficient mannerFunction
quad.form.inv(M,x)
returnsx^TM^{-1}x
using an efficient method that avoids invertingM
Function
quad.tform(M,x)
returnsxMx^T
usingtcrossprod()
without taking a transposeFunction
quad.tform.inv(M,x)
returnsxM^{-1}x^T
, although a single transpose is neededFunction
quad.3form(M,l,r)
returnsl^TMr
using nested calls tocrossprod()
. It's no faster than callingcrossprod()
directly, but makes code neater and less error-prone (IMHO)Function
quad.3form.inv(M,l,r)
returnsl^TM^{-1}r
Function
quad.3tform(M,l,r)
returnslMr^T
using nested calls totcrossprod()
. Again, this is to make for neater codeFunction
quad.diag(M,x)
returns the diagonal of the (potentially very large) square matrixquad.form(M,x)
without calculating the off diagonal elementsFunction
quad.tdiag(M,x)
similarly returns the diagonal ofquad.tform(M,x)
Function
quad.3diag(M,l,r)
returns the diagonal ofquad.3form(M,l,r)
Function
quad.3tdiag(M,l,r)
returns the diagonal ofquad.3tform(M,l,r)
These functions invoke the following lower-level calls:
Function
ht(x)
returns the Hermitian transpose, that is, the complex conjugate of the transpose, sometimes writtenx^*
Function
cprod(x,y)
returnsx^T y
, equivalent tocrossprod(Conj(x),y)
Function
tcprod(x,y)
returnsx y^T
, equivalent tocrossprod(x,Conj(y))
Note again that in the calls above, “transpose” [that is,
x^T
] means “Conjugate transpose”, or the Hermitian
transpose.
Usage
quad.form(M, x, chol=FALSE)
quad.form.inv(M, x)
quad.tform(M, x)
quad.3form(M,left,right)
quad.3tform(M,left,right)
quad.tform.inv(M,x)
quad.diag(M,x)
quad.tdiag(M,x)
quad.3diag(M,left,right)
quad.3tdiag(M,left,right)
cprod(x,y)
tcprod(x,y)
ht(x)
Arguments
M |
Square matrix of size |
x , y |
Matrix of size |
chol |
Boolean, with |
left , right |
In function |
Details
The “meat” of quad.form()
for chol=FALSE
is just
crossprod(crossprod(M, x), x)
, and that of
quad.form.inv()
is crossprod(x, solve(M, x))
.
If the Cholesky decomposition of M
is available, then calling
with chol=TRUE
and supplying M.upper
should generally be
faster (for large matrices) than calling with chol=FALSE
and
using M
directly. The time saving is negligible for matrices
smaller than about 50\times 50
, even if the overhead of
computing M.upper
is ignored.
Note
These functions are used extensively in the emulator and
calibrator packages' R code, primarily in the interests of elegant
code, but also speed. For the problems I usually consider, the
speedup (of quad.form(M,x)
over t(x) %*% M %*% x
,
say) is marginal at best.
Author(s)
Robin K. S. Hankin
See Also
Examples
jj <- matrix(rnorm(80),20,4)
M <- crossprod(jj,jj)
M.lower <- t(chol(M))
x <- matrix(rnorm(8),4,2)
jj.1 <- t(x) %*% M %*% x
jj.2 <- quad.form(M,x)
jj.3 <- quad.form(M.lower,x,chol=TRUE)
print(jj.1)
print(jj.2)
print(jj.3)
## Make two Hermitian positive-definite matrices:
L <- matrix(c(1,0.1i,-0.1i,1),2,2)
LL <- diag(11)
LL[2,1] <- -(LL[1,2] <- 0.1i)
z <- t(latin.hypercube(11,2,complex=TRUE))
quad.diag(L,z) # elements real because L is HPD
quad.tdiag(LL,z) # ditto
## Now consider accuracy:
quad.form(solve(M),x) - quad.form.inv(M,x) # should be zero
quad.form(M,x) - quad.tform(M,t(x)) # should be zero
quad.diag(M,x) - diag(quad.form(M,x)) # should be zero
diag(quad.form(L,z)) - quad.diag(L,z) # should be zero
diag(quad.tform(LL,z)) - quad.tdiag(LL,z) # should be zero