Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create svd_wrapper and copy ginv to avoid LAPACK errors #83

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Imports:
ggrepel,
graphics,
gridExtra,
MASS,
matrixStats,
methods,
parallel,
Expand All @@ -45,6 +44,7 @@ Suggests:
devtools,
FactoMineR,
knitr,
MASS,
pander,
rmarkdown,
rticles,
Expand Down
1 change: 0 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export(rgcca_predict)
export(rgcca_stability)
export(rgcca_transform)
importFrom(Deriv,Deriv)
importFrom(MASS,ginv)
importFrom(caret,confusionMatrix)
importFrom(caret,multiClassSummary)
importFrom(caret,postResample)
Expand Down
2 changes: 0 additions & 2 deletions R/block_init.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#' @importFrom MASS ginv

block_init <- function(x, init = "svd") {
UseMethod("block_init")
}
Expand Down
23 changes: 18 additions & 5 deletions R/block_project.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ block_project.block <- function(x) {
#' @export
block_project.dual_block <- function(x) {
if (any(x$alpha != 0)) {
x$alpha <- x$alpha / drop(sqrt(t(x$alpha) %*% x$K %*% x$alpha))
alpha_norm <- t(x$alpha) %*% x$K %*% x$alpha
if (alpha_norm > 0) {
x$alpha <- x$alpha / drop(sqrt(alpha_norm))
} else {
x$alpha <- x$alpha * 0
}
}
x$a <- pm(t(x$x), x$alpha, na.rm = x$na.rm)

Expand All @@ -26,7 +31,12 @@ block_project.dual_block <- function(x) {
#' @export
block_project.primal_regularized_block <- function(x) {
if (any(x$a != 0)) {
x$a <- x$M %*% x$a / drop(sqrt(t(x$a) %*% x$M %*% x$a))
a_norm <- t(x$a) %*% x$M %*% x$a
if (a_norm > 0) {
x$a <- x$M %*% x$a / drop(sqrt(a_norm))
} else {
x$a <- x$a * 0
}
}

x$Y <- pm(x$x, x$a, na.rm = x$na.rm)
Expand All @@ -36,9 +46,12 @@ block_project.primal_regularized_block <- function(x) {
#' @export
block_project.dual_regularized_block <- function(x) {
if (any(x$alpha != 0)) {
x$alpha <- x$M %*% x$alpha / drop(sqrt(
t(x$alpha) %*% x$M %*% x$K %*% x$alpha
))
alpha_norm <- t(x$alpha) %*% x$M %*% x$K %*% x$alpha
if (alpha_norm > 0) {
x$alpha <- x$M %*% x$alpha / drop(sqrt(alpha_norm))
} else {
x$alpha <- x$alpha * 0
}
}
x$a <- pm(t(x$x), x$alpha, na.rm = x$na.rm)

Expand Down
8 changes: 6 additions & 2 deletions R/deflation.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
deflation <- function(X, y, na.rm = TRUE, left = TRUE) {
# Computation of the residual matrix R
# Computation of the vector p.
y_norm <- drop(crossprod(y))
if (y_norm == 0) {
y_norm <- 1
}
if (left) {
p <- pm(t(X), y, na.rm = na.rm) / as.vector(crossprod(y))
p <- pm(t(X), y, na.rm = na.rm) / y_norm
R <- X - tcrossprod(y, p)
} else {
p <- pm(X, y, na.rm = na.rm) / as.vector(crossprod(y))
p <- pm(X, y, na.rm = na.rm) / y_norm
R <- X - tcrossprod(p, y)
}
return(list(p = p, R = R))
Expand Down
20 changes: 20 additions & 0 deletions R/ginv.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copy of the ginv function from the MASS package.
# We replace calls to svd by our wrapper to avoid LAPACK errors.
ginv <- function (X, tol = sqrt(.Machine$double.eps))
{
if (length(dim(X)) > 2L || !(is.numeric(X) || is.complex(X)))
stop("'X' must be a numeric or complex matrix")
if (!is.matrix(X))
X <- as.matrix(X)
Xsvd <- svd_wrapper(X)
if (is.complex(X))
Xsvd$u <- Conj(Xsvd$u)
Positive <- Xsvd$d > max(tol * Xsvd$d[1L], 0)
if (all(Positive))
Xsvd$v %*% (1/Xsvd$d * t(Xsvd$u))
else if (!any(Positive))
array(0, dim(X)[2L:1L])
else Xsvd$v[, Positive, drop = FALSE] %*% (
(1/Xsvd$d[Positive]) * t(Xsvd$u[, Positive, drop = FALSE])
)
}
6 changes: 3 additions & 3 deletions R/initsvd.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ initsvd <- function(X, dual = TRUE) {

if (dual) {
ifelse(n >= p,
return(svd(X, nu = 0, nv = 1)$v),
return(svd(X, nu = 1, nv = 0)$u)
return(svd_wrapper(X, nu = 0, nv = 1)$v),
return(svd_wrapper(X, nu = 1, nv = 0)$u)
)
} else {
return(svd(X, nu = 0, nv = 1)$v)
return(svd_wrapper(X, nu = 0, nv = 1)$v)
}
}
4 changes: 3 additions & 1 deletion R/rgcca_predict.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ rgcca_predict <- function(rgcca_res,

### Get projected train and test data
projection <- rgcca_transform(rgcca_res, blocks_test[-test_idx])
X_train <- rgcca_res$Y[names(projection)]
X_train <- rgcca_transform(
rgcca_res, rgcca_res$call$blocks[names(projection)]
)
X_train <- reformat_projection(X_train)
X_test <- reformat_projection(projection)

Expand Down
4 changes: 4 additions & 0 deletions R/rgcca_transform.R
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ rgcca_transform <- function(rgcca_res, blocks_test = rgcca_res$call$blocks) {
# Otherwise we directly use astar to project the individual blocks
} else {
astar <- rgcca_res$astar[names(X_train)]
# Remove zero columns of astar
astar <- lapply(astar, function(x) {
x[, which(apply(x, 2, function(y) sum(abs(y)) > 0))]
})
projection <- lapply(seq_along(blocks_test), function(j) {
x <- pm(as.matrix(blocks_test[[j]]), astar[[j]])
rownames(x) <- rownames(blocks_test[[j]])
Expand Down
25 changes: 25 additions & 0 deletions R/svd_wrapper.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# workaround by Art Owen to avoid LAPACK errors
# See https://stat.ethz.ch/pipermail/r-help/2007-October/143508.html
svd_wrapper <- function(x, nu = min(n, p), nv = min(n, p), ...) {
success <- FALSE
n <- NROW(x)
p <- NCOL(x)
try({
svd_x <- base::svd(x, nu, nv, ...)
success <- TRUE
}, silent = TRUE)
if( success ) {
return(svd_x)
}
try( {
svd_tx <- base::svd(t(x), nv, nu, ...)
success <- TRUE
}, silent = TRUE )
if( !success ) {
stop("Error: svd(x) and svd(t(x)) both failed to converge.")
}
temp <- svd_tx$u
svd_tx$u <- svd_tx$v
svd_tx$v <- temp
return(svd_tx)
}
17 changes: 5 additions & 12 deletions codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@
},
"sameAs": "https://CRAN.R-project.org/package=knitr"
},
{
"@type": "SoftwareApplication",
"identifier": "MASS",
"name": "MASS"
},
{
"@type": "SoftwareApplication",
"identifier": "pander",
Expand Down Expand Up @@ -247,18 +252,6 @@
"sameAs": "https://CRAN.R-project.org/package=gridExtra"
},
"8": {
"@type": "SoftwareApplication",
"identifier": "MASS",
"name": "MASS",
"provider": {
"@id": "https://cran.r-project.org",
"@type": "Organization",
"name": "Comprehensive R Archive Network (CRAN)",
"url": "https://cran.r-project.org"
},
"sameAs": "https://CRAN.R-project.org/package=MASS"
},
"9": {
"@type": "SoftwareApplication",
"identifier": "matrixStats",
"name": "matrixStats",
Expand Down
11 changes: 10 additions & 1 deletion tests/testthat/test_defl_select.r
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,17 @@ test_that("defl_select does not deflate block which reached ncomp", {
expect_equal(res$resdefl[[1]], A[[1]])
})

test_that("defl_select does not deflate block when y is zero", {
yy0 <- yy
yy0[[1]] <- yy0[[1]] * 0
res <- defl_select(
yy = yy0, rr = A, nncomp = c(2, 2, 2), nn = 1, nbloc = 3
)
expect_equal(res$resdefl[[1]], A[[1]])
})

test_that("defl_select outputs coherent residuals and projections", {
res <- defl_select(yy = yy, rr = A, nncomp = c(1, 1, 1), nn = 1, nbloc = 3)
res <- defl_select(yy = yy, rr = A, nncomp = c(2, 2, 2), nn = 1, nbloc = 3)
for (j in seq_along(A)) {
expect_equal(A[[j]] - yy[[j]] %*% t(res$pdefl[[j]]), res$resdefl[[j]])
}
Expand Down
14 changes: 14 additions & 0 deletions tests/testthat/test_rgcca_transform.R
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,17 @@ test_that("rgcca_transform creates projection with the right number of
expect_true(all(dim(projection[[j]]) == c(nrow(projection[[j]]), ncomp[j])))
}
})

#-------------------------------------------------------------------------
# Checking rgcca_transform removes zero columns
#-------------------------------------------------------------------------
fit.rgcca_with_zeros <- fit.rgcca
fit.rgcca_with_zeros$astar[[1]][, 3] <- 0
fit.rgcca_with_zeros$astar[[3]][, c(3, 4)] <- 0

projection <- rgcca_transform(fit.rgcca_with_zeros, A_test)

test_that("rgcca_transform creates projection without zero columns", {
expect_equal(ncol(projection[[1]]), 2)
expect_equal(ncol(projection[[3]]), 2)
})
Loading