#' Generates weights using mirror algorithm.
#' Fulfills equality constraints while maintaining randomness by using a random
#' walk reflecting at the boundaries. Based on \code{xsample} function in
#' limSolve package. Given a set of constraints: \eqn{Ex = Ex_0, x \ge 0} mirror
#' starts at \eqn{x_0} and repeatedly jumps from the point in a random direction
#' in the k-plane that defines \eqn{Ax=b}. It then checks against \eqn{x\ge 0}.
#' If it has violated this constraint, it projects onto the violating components
#' and projects the resulting vector back into the plane. This final vector is
#' subtracted from the violating jump, with the length scaled by a random number
#' that is calculated to maximally reduce the distance from the walls (helps it
#' converge faster). This process is repeated until there are no components
#' violating the constraints. In practice this process generates points in time
#' that is exponential in $n$, the number of components of $x$.
#'
#' To project a vector u onto vector v we use the operation proj(u)_v = v *
#' frac{u*v}{v*v}. We do this several times with an operation that looks like
#' u * u%*%v/(u%*%u).
#'
#' @param Amat This is the matrix of the equality constraint coefficients
#' @param x0 An original solution to the constraints
#' @param n Number of random solutions to output
#' @param verbose Give verbose output describing the progress of the function
#' @param numjump The number of jumps to scatter around the direction given by
#' the difference from zero
#' @param includeInfeasible TRUE to include all "bad" points in the output
#' @return Gives back a matrix with 'n' columns corresponding to 'n' positive
#' solutions to Ax = b. If includeInfeasible is TRUE then infeasible points will also
#' be included in the output, so there will be some negatives. This is to be able to track
#' the walk of mirror.
#'
#' @author Mike Flynn \email{<mflynn210@@gmail.com>}
#' @export
#'
#' @references Van Den Meershe, Karel, Karline Soetaert, and Dick Van Oevelen.
#' "Xsample(): An R Function for Sampling Linear Inverse Problems." Journal of
#' Statistical Software 30 (2009): 1--15.
#' \url{http://cran.cermin.lipi.go.id/web/packages/limSolve/vignettes/xsample.pdf}
#'
#'
#' @examples
#' Amat <- matrix(1, ncol = 3, nrow = 1)
#' x0 <- c(.3, .3, .4)
#' mirror(Amat, x0, 1)
mirror <- function(Amat, x0, n, verbose = FALSE, numjump= 20, includeInfeasible = FALSE) {
if(any(is.na(Amat)))
stop(paste("'Amat' cannot have NA's in it. It currently has ", sum(is.na(Amat)), sep = ""))
if(any(is.na(x0)))
stop(paste("'x0' cannot have NA's in it. It currently has ", sum(is.na(x0)), sep = ""))
## overdetermined, more constraints than degrees of freedom
dimen = dim(Amat)
if(dimen[1] > dimen[2]) {
stop("Problem is overdetermined, more constraints than degrees of freedom")
}
## set number at which to set components to zero
smallnegnumber = -10e-100
## columns of Z are orthogonal, unit basis of null space of Amat
## a.k.a. vectors in the plane defined by Ax=b
Z <- Null(t(Amat))
## initialize return matrix
ret <- matrix(0, nrow = length(x0), ncol = n + 1)
## initialize return list
retlist <- list()
## number of cols in Z and mean of x0 used to normalize jumps else
## the convergence time grows much faster for higher n
nc <- ncol(Z)
mn <- mean(x0)
## jump from initial point, distance normally distributed
## Z %*% r = r1*v1 + r2*v2 + ... + rn*vn where the v's are orthogonal
## vectors in the solution plane and r's are random vectors
## for example: if n = 3 and v1 = x, v2 = y, v4 = z and we have a random point
## [r1, r2, r3].
index<- 1
ret[, 1] <- x0
retlist[[index]] <- ret[,1]
index <- index + 1
## bestjump will eventually be used to store the optimal length to scale the
## reflection
bestjump <- 0
for (i in 2:(n + 1)) {
## jump
ret[, i] <- ret[, i - 1] + Z %*% rnorm(nc, 0, abs(mn))/sqrt(nc)
retlist[[index]] <- ret[,i]
index <- index + 1
## we will compare olddist to dist, if olddist < dist, then we have
## moved away from feasible space with a jump, and are not converging
olddist <- Inf
## if any of the components is negative, mirror component back
## Steps:
## -Project onto negative components
## -Project resulting vector onto solution plane
## -generate random lengths of the resulting vector to subtract from the current
## -pick the length to subtract that results in a vector closest to the interior
## -repeat until back in interior of solution
while(any(ret[, i] < 0)) {
## intialize the reflection
reflection <- rep(0, ncol(Amat))
## overdist will be a vector of all zeros except for the negative components
## of ret[,i], this is to isolate the "bad" part of the current vector
## We will get rid of this "bad" part by projecting it back on to the solution space
## (It is no longer in the solution space because we set all the "good" components to zero,
## changing it)
overdist <- rep(0, ncol(Amat))
j <- which.min(ret[,i])
overdist[j] <- ret[, i][j]
## measure distance of negative components from x ==0, this is to
## help debug and give verbose output
dist <- overdist[j]
## if the only violators are very small members of ret[,i]
## override this step, prepare to output zeros
if(all(abs(ret[,i]) > smallnegnumber)) overdist[j] == 0
if(verbose) str <- paste("Distance from walls: ", dist, "\n", sep = "" )
if(verbose) cat(str)
## project the bad vector back into feasible space, each column
## of z constitutes a basis vector in the plane we want to project into, to
## project we do the standard projection calculation onto each basis vector (each column) and
## subtract the result from overdist in order to not count twice for a component.
for (j in 1:ncol(Z)) {
## projection = u * (u*v)/(v*V)
proj <- Z[, j] * (overdist %*% Z[, j])/(Z[,j] %*% Z[, j])
## add projection to reflection
reflection <- reflection - proj
## remove component from "overdist"
overdist <- overdist - proj
}
ret[,i] <- ret[,i] + 2*reflection
## if only violators are very small negative numbers, set to zero
if(all(ret[,i] > smallnegnumber)) {
ret[which(ret[,i] < 0),i] = 0
warning("mirror set some components to zero because they were very small. Could cause hitandrun to fail.")
}
newdist <- ret[,i][j]
retlist[[index]] <- ret[,i]
index <- index + 1
if(verbose) for(j in 1:nchar(str)) cat("\b")
}
}
ret <- ret[, 2:(n + 1)]
if(includeInfeasible) {
return(do.call("cbind", retlist))
} else {
return(ret)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.