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

Update dependencies to latest versions and add some new tensor methods #313

Merged
merged 38 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
137812c
dependencies: udpate dependency versions
brendanlu Jul 24, 2024
6076719
dependencies: update renv
brendanlu Jul 24, 2024
f705f4a
dependencies: explicitly add devtools to renv.lock
brendanlu Jul 24, 2024
e02c86c
update: add dctii transforms consistent with Scipy
brendanlu Jul 25, 2024
a80ec47
documentation: add roxygen2 comments
brendanlu Jul 25, 2024
aa6cd23
documentation: investigate problem
brendanlu Jul 25, 2024
13b05e9
documentation: remove non ascii character
brendanlu Jul 25, 2024
6e2edf6
documentation: remove deprecated @docType
brendanlu Jul 25, 2024
6706cf6
tests: inverse tests passing for dctii
brendanlu Jul 25, 2024
a3339e5
testing: ensure fft dct algorithm is sensible
brendanlu Jul 25, 2024
26d50ac
testing: fix - pass in ortho into dctii/idctii
brendanlu Jul 25, 2024
34e5871
update: add new mtransform utils
brendanlu Jul 29, 2024
357d6e2
minor: progress save off of laptop devel
brendanlu Jul 29, 2024
0715a5c
update: finish m-product utils (+TESTING)
brendanlu Jul 29, 2024
9a2820d
update: add UNTESTED m_product function
brendanlu Jul 29, 2024
0558340
update: m-product passing error message tests
brendanlu Jul 29, 2024
bd91fbb
minor: mprod testing and param validation
brendanlu Jul 29, 2024
6698ede
bedtime: save untested tsvdm
brendanlu Jul 29, 2024
e2970e8
testing: validate tsvdm tests
brendanlu Jul 31, 2024
364b3af
update: add untested tpca
brendanlu Aug 7, 2024
bca4ab7
update: tpca running
brendanlu Aug 7, 2024
c403c27
update: change unravel_index not working
brendanlu Aug 7, 2024
f174132
fix: tsvdm filling dimensions + update lock
brendanlu Aug 28, 2024
135b7be
update: tpca matrix compression working
brendanlu Aug 28, 2024
bdb65ae
update: return loadings in tpca
brendanlu Aug 28, 2024
fc088b9
minor: minor changes
brendanlu Aug 28, 2024
737f4c8
minor: docstrings in tens.tpca.R
brendanlu Aug 30, 2024
3b37083
minor: (untested) progress save
brendanlu Aug 30, 2024
6d8fe21
Merge branch 'master' of https://github.com/brendanlu/mixOmics
brendanlu Aug 30, 2024
787f3b5
update: tpls 1 test failing
brendanlu Sep 1, 2024
1740df8
update: PASSES TESTS tpls basic sensible algorithm
brendanlu Sep 1, 2024
4887c33
update: UNTESTED tpls canonical & regression
brendanlu Sep 11, 2024
935f9dd
minor: (UNTESTED) fix tpls errors
brendanlu Sep 11, 2024
ea02562
update: tpls fix, basic tests pass
brendanlu Sep 24, 2024
0efc4c1
minor: fix stupid linter error
brendanlu Sep 24, 2024
88e0987
minor: suppress annoying warnings in tests
brendanlu Sep 24, 2024
2b40d91
testing: only errors are existing parallel tests
brendanlu Sep 24, 2024
2b47f0f
minor: update renv.lock test results unchanged
brendanlu Sep 24, 2024
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
8 changes: 5 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ Imports: igraph,
stats,
ggrepel,
BiocParallel,
utils
utils,
gsignal
Suggests: BiocStyle,
knitr,
rmarkdown,
testthat,
rgl
rgl,
microbenchmark
Authors@R:
c(person("Kim-Anh", "Le Cao", role = "aut", email = "kimanh.lecao@unimelb.edu.au"),
person("Florian", "Rohart", role = "aut"),
Expand Down Expand Up @@ -64,5 +66,5 @@ biocViews: ImmunoOncology,
MultipleComparison,
Classification,
Regression
RoxygenNote: 7.2.3
RoxygenNote: 7.3.2
Encoding: UTF-8
10 changes: 10 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ S3method(summary,mixo_pls)
S3method(summary,mixo_spls)
S3method(summary,pca)
S3method(summary,rcc)
export("%fp%")
export(auroc)
export(background.predict)
export(block.pls)
Expand All @@ -121,15 +122,21 @@ export(color.GreenRed)
export(color.jet)
export(color.mixo)
export(color.spectral)
export(dctii_m_transforms)
export(explained_variance)
export(facewise_product)
export(facewise_transpose)
export(ft)
export(get.BER)
export(get.confusion_matrix)
export(imgCor)
export(impute.nipals)
export(ipca)
export(logratio.transfo)
export(m_product)
export(map)
export(mat.rank)
export(matrix_to_m_transforms)
export(mint.block.pls)
export(mint.block.plsda)
export(mint.block.spls)
Expand Down Expand Up @@ -160,6 +167,9 @@ export(spca)
export(spls)
export(splsda)
export(study_split)
export(tpca)
export(tpls)
export(tsvdm)
export(tune)
export(tune.block.splsda)
export(tune.mint.splsda)
Expand Down
2 changes: 1 addition & 1 deletion R/mixOmics-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#' Canonical Correlation Analysis and P-integration with variants of multi-group
#' Partial Least Squares.
#'
#' @docType package
#' @docType _PACKAGE
#' @name mixOmics-package
NULL

Expand Down
256 changes: 256 additions & 0 deletions R/tens.mproduct.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
# ==============================================================================
# utilities and functions for Kilmer's m product
# ==============================================================================
# bltodo: use classes for m and minv?

#' @description Apply a function across the last dimension of an input vector,
#' matrix, or tensor. This function defines both a parallel algorithm using
#' BiocParrallel, and also a simple \code{apply} algorithm. Even on Windows
#' Machines, setting \code{bpparam = BiocParallel::SerialParam()} offers a
#' notable speedup for larger 3D array inputs.
#' @param x Numerical array input.
#' @param mat Function which defines the tubal transform.
#' @param bpparam A \linkS4class{BiocParallelParam} object indicating the type
#' of parallelisation.
#' @return A tensor of the same size under the specified tubal transform,
#' denoted \hat{x}.
#' @author Brendan Lu
#' @keywords internal
.apply_mat_transform <- function(x, mat, bpparam) {
if (length(dim(x)) == 1) {
return(mat %*% x)
} else if (length(dim(x)) == 2) {
return(mat %*% x)
} else if (length(dim(x)) == 3) {
n <- dim(x)[1]
p <- dim(x)[2]
t <- dim(x)[3]
if (is.null(bpparam)) {
# apply algorithm --------------------------------------------------------
transformed_slices <- apply(
aperm(x, c(3, 1, 2)), c(2, 3), # permuted tensor with mode 3 in front
function(slice) mat %*% slice # left multiply by transform mat
)
# permute back to original orientation
return(aperm(array(transformed_slices, dim = c(t, n, p)), c(2, 3, 1)))
# ------------------------------------------------------------------------
} else {
# BiocParallel algorithm -------------------------------------------------
transformed_slices <- BiocParallel::bplapply(
lapply(seq_len(p), function(i) t(x[, i, ])), # a list of t x n matrices
function(slice) mat %*% slice, # left multiply by transform mat
BPPARAM = bpparam
)
# unlist and cast into array with p facewise t x n matrices
# then appropriately rotate to get original n x p x t matrix
return(
aperm(array(unlist(transformed_slices), dim = c(t, n, p)), c(2, 3, 1))
)
# ------------------------------------------------------------------------
}
} else {
# error: some array >3D has been inputted
stop(
"Only order 1 (vector), 2 (matrix), 3 (3D tensor) arrays are supported"
)
}
}

#' @description Validate appropriate 'null-ness' of m, minv inputs.
#' @author Brendan Lu
#' @keywords internal
.stop_invalid_transform_input <- function(m, minv) {
if (
xor(is.function(m), is.function(minv)) ||
xor(is.null(m), is.null(minv))
) {
stop(
"If explicitly defined, both m and its inverse must be defined as
functions"
)
}
}

#' @description Returns functions \code{m} and \code{m_inv} which apply tubal
#' transforms defined by the matrix \code{m_mat}.
#' @param mat Function which defines the tubal transform.
#' @param m_inv_mat Function which defines inverse tubal transform
#' @param bpparam A \linkS4class{BiocParallelParam} object indicating the type
#' of parallelisation.
#' @return
#' \item{m}{A function which applies the matrix m_mat along the last dimension
#' of a given numerical input array. For 3D tensors it performs the mode-3
#' product.}
#' \item{minv}{The inverse of m.}
#' @author Brendan Lu
#' @export
matrix_to_m_transforms <- function(
m_mat,
m_inv_mat = NULL,
bpparam = NULL
) {
# error: non-matrix input
stopifnot(length(dim(m_mat)) == 2)
# error: non-square matrix input
stopifnot(dim(m_mat)[1] == dim(m_mat)[2])

# invert m_mat if m_inv_mat not specified
if (is.null(m_inv_mat)) {
# error: non-invertible input
m_inv_mat <- solve(m_mat)
} else {
# error: specified matrices are not the same size
stopifnot(identical(dim(m_mat), dim(m_inv_mat)))
}

return(list(
m = function(x) .apply_mat_transform(x, m_mat, bpparam = bpparam),
minv = function(x) .apply_mat_transform(x, m_inv_mat, bpparam = bpparam)
))
}

#' @description Returns functions \code{m} and \code{m_inv} which apply tubal
#' transforms defined by the Discrete Cosine Transform (DCT-II variant). This
#' is equivalent to Scipys DCTI-ii algorithm with \code{norm='ortho'}.
#' @param t The length of the transform.
#' @param bpparam A \linkS4class{BiocParallelParam} object indicating the type
#' of parallelisation.
#' @return
#' \item{m}{A function which applies the dct-ii along the last dimension of a
#' given numerical input array. For 3D tensors it performs the mode-3 product
#' with the DCT matrix.}
#' \item{minv}{The inverse of m,}
#' @author Brendan Lu
#' @export
dctii_m_transforms <- function(t, bpparam = NULL) {
return(matrix_to_m_transforms(m_mat = gsignal::dctmtx(t), bpparam = bpparam))
}

#' @description Compute Kilmer's facewise product. Note that the for-loop
#' implementation is relatively fast, and very readable. There's also a
#' BiocParralel implementation here, but it lacks significant benchmarking
#' results.
#' bltodo: the parallel algorithm is probably stupid remove sometime?
#' @author Brendan Lu
#' @keywords internal
.binary_facewise <- function(a, b, bpparam) {
na <- dim(a)[1]
pa <- dim(a)[2]
ta <- dim(a)[3]

nb <- dim(b)[1]
pb <- dim(b)[2]
tb <- dim(b)[3]

# error: different t for each input
stopifnot(ta == tb)
t <- ta
# error: non-conforming facewise dimensions
stopifnot(pa == nb)

if (is.null(bpparam)) {
# for-loop algorithm -------------------------------------------------------
fp_ab <- array(0, dim = c(na, pb, t))
for (i in seq_len(t)) {
fp_ab[, , i] <- a[, , i] %*% b[, , i]
}
return(fp_ab)
# --------------------------------------------------------------------------
} else {
# BiocParallel algorithm ---------------------------------------------------
# bltodo: benchmark / investigate preallocation here?
return(
simplify2array(
BiocParallel::bplapply(
array(seq_len(t)),
FUN = function(i) a[, , i] %*% b[, , i],
BPPARAM = bpparam
)
)
)
# --------------------------------------------------------------------------
}
}

#' @description Compute Kilmer's facewise product cumulatively across any
#' arbitrary number of tensor inputs.
#' @param ... Arbitrary number of numerical tensor inputs.
#' @param bpparam A \linkS4class{BiocParallelParam} object indicating the type
#' of parallelisation.
#' @return Cumulative facewise product.
#' @author Brendan Lu
#' @export
facewise_product <- function(..., bpparam = NULL) {
return(
Reduce(
function(a, b) .binary_facewise(a, b, bpparam = bpparam),
list(...)
)
)
}

#' @describeIn facewise_product Custom facewise product operator
#' @export
`%fp%` <- function(a, b) .binary_facewise(a, b, bpparam = NULL)

#' @description Perform a facewise transpose on an order-3 tensor.
#' @param tensor Numerical 3D array input.
#' @return Facewise transpose of \code{tensor}
#' @author Brendan Lu
#' @aliases ft facewise_transpose
#' @export
facewise_transpose <- function(tensor) {
return(aperm(tensor, c(2, 1, 3)))
}

#' @describeIn facewise_transpose Alias for \code{\link{facewise_product}}
#' @export
ft <- function(tensor) {
return(facewise_transpose(tensor))
}

#' @description Compute Kilmer's tensor-tensor m-product cumulatively across any
#' arbitrary number of tensor inputs.
#' @param ... Arbitrary number of numerical tensor inputs.
#' @param m A function which applies an orthogonal tensor tubal transform.
#' @param minv The inverse of m.
#' @param bpparam A \linkS4class{BiocParallelParam} object indicating the type
#' of parallelisation. Does not have any effect if transform functions
#' explicitly set using \code{m}, \code{minv}.
#' @return Cumulative m-product.
#' @author Brendan Lu
#' @export
m_product <- function(
...,
m = NULL,
minv = NULL,
bpparam = NULL
) {
tensors <- list(...)
if (length(tensors) == 0) {
stop("No input tensors provided.")
} else {
t <- dim(tensors[[1]])[3]
}
if (
xor(is.function(m), is.function(minv)) ||
xor(is.null(m), is.null(minv))
) {
stop(
"If explicitly defined, both m and its inverse must be defined as
functions."
)
}
# use dctii as default transform if user does not specify an explicit one
if (is.null(m)) {
transforms <- dctii_m_transforms(t, bpparam = bpparam)
m <- transforms$m
minv <- transforms$minv
}
return(minv(
Reduce(
function(a, b) .binary_facewise(a, b, bpparam = NULL),
lapply(list(...), m)
)
))
}
Loading