
library("mvtnorm")
library("numDeriv")

options(digits = 3)
tol <- 1e-1

set.seed(29)

EVAL <- function(...) {}

if (require("numDeriv", quietly = TRUE))
    EVAL <- eval

chk <- function(...) stopifnot(isTRUE(all.equal(..., check.attributes = FALSE, tol = sqrt(sqrt(.Machine$double.eps)))))

thischeck <- expression({
J <- 5
p <- sample(1:J)
if (isTRUE(all.equal(p, 1:J)))
    warning("Checks for id permutation meaningless")
P <- matrix(0, nrow = J, ncol = J)
P[cbind(1:J, p)] <- 1

L <- as.invchol(ltMatrices(1 + runif(J * (J + 1) / 2), diag = TRUE, byrow = BYROW))
mL <- as.array(L)[,,1]
S <- invchol2cov(L)
mS <- as.array(S)[,,1]
mSp <- mS[p,p]

chk(P %*% mS %*% t(P), mSp)

O <- invchol2pre(L)
mO <- as.array(O)[,,1]
chk(solve(P %*% mO %*% t(P)), mSp)
chk(solve(P %*% t(mL) %*% mL %*% t(P)), mSp)

C <- invchol2chol(L)
mC <- as.array(C)[,,1]
chk(P %*% mC %*% t(mC) %*% t(P), mSp)

Ct <- t(chol(mS[p,p]))
chk(Ct %*% t(Ct), mSp)

chk(as.array(invchol2cov(aperm(L, perm = p)))[,,1], mSp)
chk(as.array(chol2cov(aperm(C, perm = p)))[,,1], mSp)

N <- 10000
obs <- matrix(rnorm(J * N), ncol = N)
obs <- Mult(C, obs)

ll1 <- ldmvnorm(obs = obs, chol = C)
ll2 <- ldmvnorm(obs = obs[p,], chol = aperm(C, perm = p))
ll3 <- ldmvnorm(obs = obs, invchol = L)
ll4 <- ldmvnorm(obs = obs[p,], invchol = aperm(L, perm = p))
chk(ll1, ll2)
chk(ll1, ll3)
chk(ll1, ll4)

### C
### diag = TRUE w/o stand
ll <- function(x) {
    C <- as.chol(ltMatrices(x, diag = TRUE, byrow = BYROW))
    Ct <- aperm(C, perm = p)
    -ldmvnorm(obs = obs[p,], chol = Ct)
}

s <- function(x) {
    C <- as.chol(ltMatrices(x, diag = TRUE, byrow = BYROW))
    Ct <- aperm(C, perm = p)
    sC <- sldmvnorm(obs = obs[p,], chol = Ct)$chol
    ret <- deperma(chol = C, permuted_chol = Ct, perm = p, score_schol = sC)
    -rowSums(Lower_tri(ret, diag = TRUE))
}

g1 <- grad(ll, c(C))
s1 <- s(c(C))
chk(g1, s1)

op1 <- optim(c(C), fn = ll, gr = s, method = "L-BFGS-B")
max(abs(ltMatrices(op1$par, diag = TRUE, byrow = BYROW) - C))

### check against unpermuted (expect same results)
ll <- function(x) {
    C <- ltMatrices(x, diag = TRUE, byrow = BYROW)
    -ldmvnorm(obs = obs, chol = C)
}

s <- function(x) {
    C <- ltMatrices(x, diag = TRUE, byrow = BYROW)
    ret <- sldmvnorm(obs = obs, chol = C)$chol
    -rowSums(Lower_tri(ret, diag = TRUE))
}

op2 <- optim(c(C), fn = ll, gr = s, method = "L-BFGS-B")
chk(max(abs(ltMatrices(op2$par, diag = TRUE, byrow = BYROW) - C)) < tol, TRUE)

chk(op1, op2)

### diag = FALSE
Cd <- ltMatrices(runif(J * (J - 1) / 2), byrow = BYROW)

### w/ standardisation (1. stand, 2. perm)
ll <- function(x) {
    C <- as.chol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    Cs <- standardize(chol = C)
    Ct <- aperm(Cs, perm = p)
    -ldmvnorm(obs = obs[p,], chol = Ct)
}

s <- function(x) {
    C <- ltMatrices(x, diag = FALSE, byrow = BYROW)
    Cs <- standardize(chol = C)
    Ct <- aperm(Cs, perm = p)
    sC <- sldmvnorm(obs = obs[p,], chol = Ct)$chol
    ret <- deperma(chol = Cs, permuted_chol = Ct, perm = p, score_schol = sC)
    ret <- destandardize(chol = C, score_schol = ret)
    -rowSums(Lower_tri(ret, diag = FALSE))
}

chk(grad(ll, c(Cd)), s(c(Cd)))

### w/o standardisation 
ll <- function(x) {
    C <- as.chol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    Ct <- aperm(C, perm = p)
    -ldmvnorm(obs = obs[p,], chol = Ct)
}

s <- function(x) {
    C <- as.chol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    diagonals(C) <- 1		### deperma expects diagonals
    Ct <- aperm(as.chol(C), perm = p)
    sC <- sldmvnorm(obs = obs[p,], chol = Ct)$chol
    ret <- deperma(chol = C, permuted_chol = Ct, perm = p, score_schol = sC)
    -rowSums(Lower_tri(ret, diag = FALSE))
}

chk(grad(ll, c(Cd)), s(c(Cd)))

### L
### diag = TRUE w/o stand
ll <- function(x) {
    C <- as.invchol(ltMatrices(x, diag = TRUE, byrow = BYROW))
    Ct <- aperm(C, perm = p)
    -ldmvnorm(obs = obs[p,], invchol = Ct)
}

s <- function(x) {
    C <- as.invchol(ltMatrices(x, diag = TRUE, byrow = BYROW))
    Ct <- aperm(C, perm = p)
    sC <- sldmvnorm(obs = obs[p,], invchol = Ct)$invchol
    ret <- deperma(invchol = C, permuted_invchol = Ct, perm = p, score_schol = -vectrick(Ct, sC))
    -rowSums(Lower_tri(ret, diag = TRUE))
}

g2 <- grad(ll, c(L))
chk(g2, s(c(L)))
chk(g2, c(Lower_tri(-vectrick(C, ltMatrices(g1, byrow = BYROW, diag = TRUE)), diag = TRUE)))

op3 <- optim(c(L), fn = ll, gr = s, method = "L-BFGS-B")
chk(max(abs(ltMatrices(op3$par, diag = TRUE, byrow = BYROW) - L)) < tol, TRUE)

### check against unpermuted (expect same results)
ll <- function(x) {
    C <- ltMatrices(x, diag = TRUE, byrow = BYROW)
    -ldmvnorm(obs = obs, invchol = C)
}

s <- function(x) {
    C <- ltMatrices(x, diag = TRUE, byrow = BYROW)
    ret <- sldmvnorm(obs = obs, invchol = C)$invchol
    -rowSums(Lower_tri(ret, diag = TRUE))
}

op4 <- optim(c(L), fn = ll, gr = s, method = "L-BFGS-B")
chk(max(abs(ltMatrices(op4$par, diag = TRUE, byrow = BYROW) - L)) < tol, TRUE)

### diag = FALSE
Ld <- ltMatrices(runif(J * (J - 1) / 2), byrow = BYROW)

### w/ standardisation (1. stand, 2. perm)
ll <- function(x) {
    C <- as.invchol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    Cs <- standardize(invchol = C)
    Ct <- aperm(Cs, perm = p)
    -ldmvnorm(obs = obs[p,], invchol = Ct)
}

s <- function(x) {
    C <- as.invchol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    Cs <- standardize(invchol = C)
    Ct <- aperm(Cs, perm = p)
    sC <- sldmvnorm(obs = obs[p,], invchol = Ct)$invchol
    ret <- deperma(invchol = Cs, permuted_invchol = Ct, perm = p, score_schol = -vectrick(Ct, sC))
    ret <- destandardize(invchol = C, score_schol = -vectrick(Cs, ret))
    -rowSums(Lower_tri(ret, diag = FALSE))
}

chk(grad(ll, c(Ld)), s(c(Ld)))

### w/o standardisation 
ll <- function(x) {
    C <- as.invchol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    Ct <- aperm(C, perm = p)
    -ldmvnorm(obs = obs[p,], invchol = Ct)
}

s <- function(x) {
    C <- as.invchol(ltMatrices(x, diag = FALSE, byrow = BYROW))
    diagonals(C) <- 1		### deperma expects diagonals
    Ct <- aperm(as.invchol(C), perm = p)
    sC <- sldmvnorm(obs = obs[p,], invchol = Ct)$invchol
    ret <- deperma(invchol = C, permuted_invchol = Ct, perm = p, score_schol = -vectrick(Ct, sC))
    -rowSums(Lower_tri(ret, diag = FALSE))
}

chk(grad(ll, c(Ld)), s(c(Ld)))
})

BYROW <- FALSE
EVAL(thischeck)

BYROW <- TRUE
EVAL(thischeck)
