
context("Cholesky solves")

set.seed(1, kind="Mersenne-Twister", normal.kind="Inversion")

test_that("Cholesky for ddiMatrix works", {
  n <- 100L
  M <- Diagonal(x=runif(n, 0.1, 1))
  y0 <- rnorm(n)
  y1 <- matrix(rnorm(3*n), n, 3)
  y2 <- rsparsematrix(n, 10, 0.1)
  ch <- build_chol(M)
  M <- Diagonal(x = 0.1 + 10*rnorm(n)^2)
  ch$update(M)
  expect_equal(ch$solve(y0), y0/M@x)
  expect_equal(ch$solve(y1), y1/M@x)
  expect_equal(ch$solve(y2), Diagonal(x=1/M@x) %*% y2)
  expect_equal(ch$solve(y0, system="Lt"), y0/sqrt(M@x))
  expect_equal(ch$solve(y0, system="Lt"), y0/sqrt(M@x))
  expect_equal(ch$solve(y0, system="L"), y0/sqrt(M@x))
  expect_equal(ch$solve(y1, system="Lt"), y1/sqrt(M@x))
  expect_equal(ch$solve(y2, system="Lt"), Diagonal(x=1/sqrt(M@x)) %*% y2)
})

test_that("Cholesky for matrix works", {
  n <- 12
  M <- crossprod(matrix(rnorm(n^2), n))
  diag(M) <- diag(M) + runif(n, 0.1, 1)
  ch <- build_chol(M)
  M <- crossprod(matrix(rnorm(n^2), n))
  diag(M) <- diag(M) + runif(n, 0.1, 1)
  ch$update(M)
  y0 <- rnorm(n)
  y1 <- matrix(rnorm(3*n), n, 3)
  expect_equal(ch$solve(y0), solve(M, y0))
  expect_equal(ch$solve(y1), solve(M, y1))
  expect_equal(ch$solve(y0, system="Lt"), solve(chol(M), y0))
  expect_equal(ch$solve(y0, system="L"), solve(t(chol(M)), y0))
  expect_equal(ch$solve(y1, system="Lt"), solve(chol(M), y1))
  x <- rnorm(n)
  expect_equal(ch$Ltimes(x), ch$cholM %m*v% x)
})

test_that("Cholesky for dsCMatrix works", {
  n <- 100
  M <- crossprod(rsparsematrix(n, n, density=0.01)) + Diagonal(n)
  ch <- build_chol(M)
  # in-place update only works for same sparsity pattern!!
  M <- M + Diagonal(n)
  ch$update(M)
  cholM <- Cholesky_dsC(M, perm=FALSE, super=NA)
  y0 <- rnorm(n)
  y1 <- matrix(rnorm(3*n), n, 3)
  y2 <- rsparsematrix(n, 10, 0.1)
  expect_equal(ch$solve(y0), as.vector(solve(cholM, y0)))
  expect_equal(ch$solve(y1), array(solve(cholM, y1)@x, dim(y1)))
  expect_equal(ch$solve(y2), solve(cholM, y2))
  expect_equal(ch$solve(y0, system="Lt"), as.vector(solve(cholM, y0, system="Lt")))
  expect_equal(ch$solve(y1, system="Lt"), array(solve(cholM, y1, system="Lt")@x, dim(y1)))
  expect_equal(ch$solve(y2, system="Lt"), solve(cholM, y2, system="Lt"))
  expect_equal(ch$solve(y0, system="L"), as.vector(solve(cholM, y0, system="L")))
  expect_equal(ch$solve(y1, system="L"), array(solve(cholM, y1, system="L")@x, dim(y1)))
  expect_equal(ch$solve(y2, system="L"), solve(cholM, y2, system="L"))
  # with permutation
  ch <- build_chol(M, control=chol_control(perm=TRUE))
  M <- M + Diagonal(x=runif(n))
  ch$update(M)
  cholM <- Cholesky_dsC(M, perm=TRUE, super=NA)
  expect_equal(ch$solve(y0), as.vector(solve(cholM, y0)))
  expect_equal(ch$solve(y1), array(solve(cholM, y1)@x, dim(y1)))
  expect_equal(ch$solve(y2), solve(cholM, y2))
  expect_equal(ch$solve(y0, system="Lt"), as.vector(solve(cholM, solve(cholM, y0, system="Lt"), system="Pt")))
  expect_equal(ch$solve(y1, system="Lt"), array(solve(cholM, solve(cholM, y1, system="Lt"), system="Pt")@x, dim(y1)))
  expect_equal(ch$solve(y2, system="Lt"), solve(cholM, solve(cholM, y2, system="Lt"), system="Pt"))
  expect_equal(ch$solve(y0, system="L"), as.vector(solve(cholM, solve(cholM, y0, system="P"), system="L")))
  expect_equal(ch$solve(y1, system="L"), array(solve(cholM, solve(cholM, y1, system="P"), system="L")@x, dim(y1)))
  expect_equal(ch$solve(y2, system="L"), solve(cholM, solve(cholM, y2, system="P"), system="L"))
})

test_that("Determinants are computed correctly", {
  n <- 30
  M <- Diagonal(x=runif(n, 0.1, 1))
  ch <- build_chol(M)
  expect_equal(ch$logdet(), 0.5*c(determinant(M, logarithm = TRUE)$modulus))
  M <- as.matrix(crossprod(matrix(rnorm(n^2), n, n)) + 10*CdiagU(n))
  ch <- build_chol(M)
  expect_equal(ch$logdet(), 0.5*c(determinant(M, logarithm = TRUE)$modulus))
  M <- rsparsematrix(n, n, 0.1, symmetric=TRUE) + 10*Diagonal(n)
  ch <- build_chol(M)
  expect_equal(ch$logdet(), 0.5*c(determinant(M, logarithm = TRUE)$modulus))
  # determinant templates
  n <- 999  # for n < 1000 an approach based on eigen decomposition is used
  QA <- Q_RW1(n)
  Q <- 0.5 * QA + 0.5 * CdiagU(n)
  detchol <- make_det(Q)
  L <- runif(1L)
  w1 <- 2*L; w2 <- 1 - 2*L
  expect_equal(detchol(w1, w2), c(determinant(L * Q_RW1(n) + (1 - L) * CdiagU(n), logarithm=TRUE)$modulus))
  n <- 1001  # for n > 1000 (sparse) Cholesky updates are used
  QA <- Q_RW1(n)
  Q <- 0.5 * QA + 0.5 * CdiagU(n)
  detchol <- make_det(Q)
  L <- runif(1L)
  w1 <- 2*L; w2 <- 1 - 2*L
  expect_equal(detchol(w1, w2), c(determinant(L * Q_RW1(n) + (1 - L) * CdiagU(n), logarithm=TRUE)$modulus))
})
