103 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			R
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			R
		
	
	
	
	
	
 | 
						|
ising_conditional_fit <- function(X, Y, ..., callback = NULL) {
 | 
						|
    # get and check dimensions
 | 
						|
    n <- if (is.null(nrow(Y))) length(Y) else nrow(Y)
 | 
						|
    p <- ncol(X)
 | 
						|
    q <- if (is.null(ncol(Y))) attr(Y, "p") else ncol(Y)
 | 
						|
    # check dimensions
 | 
						|
    stopifnot(nrow(X) == n)
 | 
						|
 | 
						|
    ### Initial value estimate
 | 
						|
    # SVD of the predictor covariance estimate `Sigma = U_Sigma D_Sigma U_Sigma'`
 | 
						|
    SigmaSVD <- La.svd(cov(X), min(p, q), 0)
 | 
						|
 | 
						|
    # Estimate `pi` as the single and two way effect means (approx conditional
 | 
						|
    # probabilities through the marginal probability estimate)
 | 
						|
    pi <- mean.mvbinary(Y, twoway = TRUE)
 | 
						|
 | 
						|
    # convert conditional probabilities into natural parameters (log-odds)
 | 
						|
    theta <- ising_theta_from_cond_prob(pi)
 | 
						|
 | 
						|
    # convert natural parameters `theta` to square matrix form `Theta`
 | 
						|
    Theta <- matrix(NA, q, q)
 | 
						|
    Theta[lower.tri(diag(q), diag = TRUE)] <- theta
 | 
						|
    Theta[upper.tri(diag(q))] <- t(Theta)[upper.tri(diag(q))]
 | 
						|
    Theta <- (0.5 + diag(0.5, q, q)) * Theta
 | 
						|
 | 
						|
    # SVD of `Theta`
 | 
						|
    ThetaSVD <- La.svd(Theta, min(p, q), 0)
 | 
						|
 | 
						|
    # Finally, initial `alpha` parameter estimate
 | 
						|
    #   `alpha_0 = U_Sigma D_Sigma^-1/2 D_Theta^1/2 U_Theta'`
 | 
						|
    alpha <- with(list(S = SigmaSVD, T = ThetaSVD), {
 | 
						|
        S$u %*% diag(sqrt(T$d[seq_len(min(p, q))] / S$d[seq_len(min(p, q))])) %*% t(T$u)
 | 
						|
    })
 | 
						|
 | 
						|
    ### Optimize log-likelihood for `alpha`
 | 
						|
    tensorPredictors::NAGD(
 | 
						|
        fun.loss = function(alpha) -ising_conditional_log_likelihood(alpha, X, Y),
 | 
						|
        fun.grad = function(alpha) -ising_conditional_score(alpha, X, Y),
 | 
						|
        params = alpha,
 | 
						|
        ...,
 | 
						|
        callback = callback
 | 
						|
    )
 | 
						|
}
 | 
						|
 | 
						|
n <- 1000
 | 
						|
p <- 7
 | 
						|
q <- 9
 | 
						|
 | 
						|
alpha.true <- matrix(rnorm(p * q), p)
 | 
						|
X <- matrix(runif(n * p), n)
 | 
						|
theta <- function(alpha, x) {
 | 
						|
    Theta <- crossprod(crossprod(x, alpha))
 | 
						|
    diag(Theta) <- 0.5 * diag(Theta)
 | 
						|
    2 * Theta[lower.tri(diag(ncol(alpha)), diag = TRUE)]
 | 
						|
}
 | 
						|
# sample Y ~ P( . | X = x) for x in X
 | 
						|
Y <- apply(X, 1, function(x) ising_sample(1, theta(alpha.true, x)))
 | 
						|
attr(Y, "p") <- as.integer(q)
 | 
						|
 | 
						|
alpha.est <- ising_conditional_fit(X, Y,
 | 
						|
    callback = function(iter, alpha) {
 | 
						|
        cat(sprintf(
 | 
						|
            "%4d - diff: %12.4f - ll: %12.4f\n",
 | 
						|
            iter,
 | 
						|
            min(norm(alpha - alpha.true, "F"), norm(alpha + alpha.true, "F")),
 | 
						|
            ising_conditional_log_likelihood(alpha, X, Y)
 | 
						|
        ))
 | 
						|
    })
 | 
						|
 | 
						|
 | 
						|
## 
 | 
						|
par(mfrow = c(3, 3), mar = c(2, 2, 1, 1))
 | 
						|
for (i in 1:9) {
 | 
						|
    tensorPredictors::matrixImage(
 | 
						|
        flip(vech.inv(theta(alpha.true, X[i, ]))),
 | 
						|
        main = paste(round(range(theta(alpha.true, X[i, ])), 3), collapse = " ")
 | 
						|
    )
 | 
						|
}
 | 
						|
 | 
						|
par(mfrow = c(3, 3), mar = c(2, 2, 1, 1))
 | 
						|
for (i in 1:9) {
 | 
						|
    P <- flip(vech.inv(ising_cond_probs(theta(alpha.true, X[i, ]))))
 | 
						|
    tensorPredictors::matrixImage(
 | 
						|
        round(P, 5), P < .Machine$double.eps | 1 < P + .Machine$double.eps
 | 
						|
    )
 | 
						|
}
 | 
						|
 | 
						|
par(mfrow = c(3, 3), mar = c(2, 2, 1, 1))
 | 
						|
for (i in 1:3) {
 | 
						|
    tensorPredictors::matrixImage(
 | 
						|
        alpha.true
 | 
						|
    )
 | 
						|
    tensorPredictors::matrixImage(
 | 
						|
        flip(vech.inv(theta(alpha.true, X[i, ]))),
 | 
						|
        main = paste(round(range(theta(alpha.true, X[i, ])), 3), collapse = " ")
 | 
						|
    )
 | 
						|
    P <- flip(vech.inv(ising_cond_probs(theta(alpha.true, X[i, ]))))
 | 
						|
    tensorPredictors::matrixImage(
 | 
						|
        round(P, 5), P < .Machine$double.eps | 1 < P + .Machine$double.eps
 | 
						|
    )
 | 
						|
}
 |