ht(x) | \(\mathbf{x}^{*}=\overline{\mathbf{x}^{\scriptscriptstyle T}}\) | Conj(t(x)) | ht(x) |
cprod(x,y) | \(\mathbf{x}^{*}\mathbf{y}\) | ht(x) %*% y | cp() |
tcprod(x,y) | \(\mathbf{x}\mathbf{y}^{*}\) | x %*% ht(y) | tcp() |
quad.form(M,x) | \(\mathbf{x}^{*}M\mathbf{x}\) | ht(x) %*% M %*% x | qf() |
quad.form.inv(M,x) | \(\mathbf{x}^{*}M^{-1}\mathbf{x}\) | ht(x) %*% solve(M) %*% x | qfi() |
quad.tform(M,x) | \(\mathbf{x}M\mathbf{x}^{*}\) | x %*% A %*% ht(x) | qt() |
quad.tform.inv(M,x) | \(\mathbf{x}M^{-1}\mathbf{x}^{*}\) | x %*% solve(M) %*% ht(x) | qti() |
quad3.form(M,l,r) | \(\mathbf{l}^{*}M\mathbf{r}\) | t(l) %*% M %*% r | q3() |
quad3.form.inv(M,l,r) | \(\mathbf{l}^{*}M^{-1}\mathbf{r}\) | t(l) %*% solve(M) %*% r | q3i() |
quad3.tform(M,l,r) | \(\mathbf{l}M\mathbf{r}^{*}\) | l %*% M %*% t(r) | q3t() |
quad.diag(M,x) | \(\operatorname{diag}(\mathbf{x}^{*}M\mathbf{x})\) | diag(quad.form(M,x)) | qd() |
quad.tdiag(M,x) | \(\operatorname{diag}(\mathbf{x}M\mathbf{x}^{*})\) | diag(quad.tform(M,x)) | qtd() |
quad3.diag(M,l,r) | \(\operatorname{diag}(\mathbf{l}^{*}M\mathbf{r})\) | diag(quad3.form(M,l,r)) | q3d() |
quad3.tdiag(M,l,r) | \(\operatorname{diag}(\mathbf{l}M\mathbf{r}^{*})\) | diag(quad3.tform(M,l,r)) | q3td() |
quad.trace(M,x) | \(\operatorname{tr}(\mathbf{x}^{*}M\mathbf{x})\) | tr(quad.form(M,x)) | qt() |
quad.ttrace(M,x) | \(\operatorname{tr}(\mathbf{x}M\mathbf{x}^{*})\) | tr(quad.tform(M,x)) | qtt() |
In the above, \(\mathbf{x}^{*}\) denotes the
complex conjugate of the transpose, also known as the Hermitian
transpose (this only matters when considering complex numbers).
These various functions generally avoid taking needless expensive
transposes in favour of using nested crossprod() and
tcrossprod() calls. For example, the “meat” of
quad.form() is just crossprod(crossprod(M,Conj(x)),x).
Functions such as quad.form.inv() avoid taking a matrix
inverse. The meat of quad.form.inv(), for example, is
cprod(x, solve(M, x)). Many people have stated things like
“Never invert a matrix unless absolutely necessary”. But I
have never seen a case where quad.form.inv(M,x) is
faster than quad.form(solve(M),x).
One motivation for the package is to return consistent results with
complex arguments. Note, for example, that
base::crossprod(x,y) evaluates t(x) %*% y and not, as
one would almost always want, Conj(t(x)) %*% y. Function
cprod(), unlike crossprod(), is consistent and returns
Conj(t(x)) %*% y [or ht(x) %*% y]; internally it is
essentially crossprod(Conj(x), y).
Function quad.form.chol() interprets argument chol as
the lower triangular Cholesky decomposition of the quadratic form.
Remember that M.lower %*% M.upper == M, and chol()
returns the upper triangular matrix, so one needs to use the transpose
t(chol(M)) in calls. If the Cholesky decomposition of M
is available, then using quad.form.chol() and supplying
chol(M) should generally be faster (for large matrices) than
calling quad.form() and using M directly. The time
saving is negligible for matrices smaller than about \(50\times
50\), even if the overhead of computing the decomposition is
ignored.
Functions quad3.foo() take three arguments: a matrix M
and two other vectors l and r [or left and
right]. For these functions, M is not necessarily
square although of course the matrices have to be compatible.
Functions quad3.form_ab() and quad3.form_bc() are helper
functions not really intended for the end-user. They return
mathematically identical results but differ in the bracketing order of
their operations: quad3.form_ab(M,l,r) returns
\(\left(\mathbf{l}^*M\right)\mathbf{r}\) and
quad3.form_bc(M,l,r) returns
\(\mathbf{l}^{*}\left(M\mathbf{r}\right)\). The mnemonic
for their names is derived from the first multiplication when
calculating \((ab)c\) and \(a(bc)\). Note that
quad3.form_ab(M,l,r) returns
crossprod(crossprod(M,Conj(l)),r) rather than the
mathematically equivalent cprod(cprod(M,l),r) on efficiency
grounds (only a single conjugate is taken).
Function quad3.form() dispatches to either
quad3.form_ab() or quad3.form_bc() depending on the
dimensions of its argument as per the efficiency discussion at
inst/quadform3test.Rmd. Similar considerations apply to
quad3.tform(), quad3.tform_ab(), and
quad3.tform_bc().
Terse forms [qf() for quad.form(), qti() for
quad.tform.inv(), etc] are provided for the perl
golfers among us.