2020-06-10 14:35:27 +00:00
|
|
|
#' Generalized matrix power function for symmetric matrices.
|
|
|
|
#'
|
|
|
|
#' Using the SVD of the matrix \eqn{A = U D V'} where \eqn{D} is the
|
|
|
|
#' diagonal matrix with the singular values of \eqn{A}, the powers are then
|
|
|
|
#' computed as \deqn{A^p = U D^p V'} using the symmetrie of \eqn{A}.
|
|
|
|
#'
|
|
|
|
#' @details
|
|
|
|
#' It is assumed that the argument \code{A} is symmeric and it is not checked
|
|
|
|
#' for symmerie, the result will just be wrong. The actual formula for negative
|
|
|
|
#' powers is \deqn{A^-p = V D^-p U'}. For symmetric matrices \eqn{U = V} which
|
|
|
|
#' gives the formula used by this function.
|
|
|
|
#' The reason is for speed, using the symmetrie propertie as described avoids
|
|
|
|
#' two transpositions in the algorithm (one in \code{svd} using \code{La.svd}).
|
|
|
|
#'
|
|
|
|
#' @param A Matrix.
|
|
|
|
#' @param pow numeric power.
|
|
|
|
#' @param tol relative tolerance to detect zero singular values as well as the
|
|
|
|
#' \code{qr} factorization tolerance.
|
|
|
|
#'
|
|
|
|
#' @return a matrix.
|
|
|
|
#'
|
|
|
|
#' @seealso \code{\link{solve}}, \code{\link{qr}}, \code{\link{svd}}.
|
|
|
|
#'
|
2021-11-04 12:05:15 +00:00
|
|
|
#' @examples \dontrun{
|
2020-06-10 14:35:27 +00:00
|
|
|
#' # General full rank square matrices.
|
|
|
|
#' A <- matrix(rnorm(121), 11, 11)
|
|
|
|
#' all.equal(matpow(A, 1), A)
|
|
|
|
#' all.equal(matpow(A, 0), diag(nrow(A)))
|
|
|
|
#' all.equal(matpow(A, -1), solve(A))
|
|
|
|
#'
|
|
|
|
#' # Roots of full rank symmetric matrices.
|
|
|
|
#' A <- crossprod(A)
|
|
|
|
#' B <- matpow(A, 0.5)
|
|
|
|
#' all.equal(B %*% B, A)
|
|
|
|
#' all.equal(matpow(A, -0.5), solve(B))
|
|
|
|
#' C <- matpow(A, -0.5)
|
|
|
|
#' all.equal(C %*% C %*% A, diag(nrow(A)))
|
|
|
|
#' all.equal(A %*% C %*% C, diag(nrow(A)))
|
|
|
|
#'
|
|
|
|
#' # General singular matrices.
|
|
|
|
#' A <- matrix(rnorm(72), 12, 12) # rank(A) = 6
|
|
|
|
#' B <- matpow(A, -1) # B = A^+
|
|
|
|
#' # Check generalized inverse properties.
|
|
|
|
#' all.equal(A %*% B %*% A, A)
|
|
|
|
#' all.equal(B %*% A %*% B, B)
|
|
|
|
#' all.equal(B %*% A, t(B %*% A))
|
|
|
|
#' all.equal(A %*% B, t(A %*% B))
|
|
|
|
#'
|
|
|
|
#' # Roots of singular symmetric matrices.
|
|
|
|
#' A <- crossprod(matrix(rnorm(72), 12, 12)) # rank(A) = 6
|
|
|
|
#' B <- matpow(A, -0.5) # B = (A^+)^1/2
|
|
|
|
#' # Check generalized inverse properties.
|
|
|
|
#' all.equal(A %*% B %*% B %*% A, A)
|
|
|
|
#' all.equal(B %*% B %*% A %*% B %*% B, B %*% B)
|
|
|
|
#' all.equal(B %*% A, t(B %*% A))
|
|
|
|
#' all.equal(A %*% B, t(A %*% B))
|
2021-11-04 12:05:15 +00:00
|
|
|
#' }
|
2020-06-10 14:35:27 +00:00
|
|
|
#'
|
2021-10-29 16:16:40 +00:00
|
|
|
#' @keywords internal
|
2020-06-10 14:35:27 +00:00
|
|
|
matpow <- function(A, pow, tol = 1e-7) {
|
|
|
|
if (nrow(A) != ncol(A)) {
|
|
|
|
stop("Expected a square matix, but 'A' is ", nrow(A), " by ", ncol(A))
|
|
|
|
}
|
|
|
|
# Case study for negative, zero or positive power.
|
|
|
|
if (pow > 0) {
|
|
|
|
if (pow == 1) { return(A) }
|
|
|
|
# Perform SVD and return power as A^pow = U diag(d^pow) V'.
|
|
|
|
svdA <- La.svd(A)
|
|
|
|
return(svdA$u %*% ((svdA$d^pow) * svdA$vt))
|
|
|
|
} else if (pow == 0) {
|
|
|
|
return(diag(nrow(A)))
|
|
|
|
} else {
|
|
|
|
# make QR decomposition.
|
|
|
|
qrA <- qr(A, tol = tol)
|
|
|
|
# Check rank of A.
|
|
|
|
if (qrA$rank == nrow(A)) {
|
|
|
|
# Full rank, calc inverse the classic way using A's QR decomposition
|
|
|
|
return(matpow(solve(qrA), abs(pow), tol = tol))
|
|
|
|
} else {
|
|
|
|
# For singular matrices use the SVD decomposition for the power
|
|
|
|
svdA <- svd(A)
|
|
|
|
# Get (numerically) positive singular values.
|
|
|
|
positives <- svdA$d > tol * svdA$d[1]
|
|
|
|
# Apply the negative power to positive singular values and augment
|
|
|
|
# the rest with zero.
|
|
|
|
d <- c(svdA$d[positives]^pow, rep(0, sum(!positives)))
|
|
|
|
# The pseudo invers as A^pow = V diag(d^pow) U' for pow < 0.
|
|
|
|
return(svdA$v %*% (d * t(svdA$u)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|