diff --git a/nCompiler/R/Rexecution.R b/nCompiler/R/Rexecution.R index ff7f3234..e13dc874 100644 --- a/nCompiler/R/Rexecution.R +++ b/nCompiler/R/Rexecution.R @@ -196,6 +196,8 @@ makeReturnVector <- function(fillValue, length, recycle) { #' @export #' nEigen <- function(x, symmetric, valuesOnly = FALSE) { + if(inherits(r, 'dCHMsimpl')) + stop("`nEigen` not implemented for sparse Cholesky factor input") res <- eigen(x = x, symmetric = symmetric, only.values = valuesOnly) ans <- EigenDecomp$new() ans$values <- res$values @@ -257,6 +259,8 @@ nEigen <- function(x, symmetric, valuesOnly = FALSE) { #' return(singularValues) #' }) nSvd <- function(x, vectors = 'full') { + if(inherits(r, 'dCHMsimpl')) + stop("`nSvd` not implemented for sparse Cholesky factor input") n <- nrow(x) p <- ncol(x) if(vectors == 'full') { # vectors = 2, when converted to int @@ -284,6 +288,8 @@ nSvd <- function(x, vectors = 'full') { #' @export #' nDiag <- function(x, ...) { + if(inherits(x, 'dCHMsimpl')) + stop("`nDiag` not implemented for sparse Cholesky factor input") diag(x, ...) } @@ -299,21 +305,42 @@ nDiag <- function(x, ...) { #' @export #' nChol <- function(x) { + if(inherits(r, 'dCHMsimpl')) + stop("`nChol` not meaningful for sparse Cholesky factor input") chol(x) } +#' Compute the sparse Cholesky decomposition of a matrix for further operations +#' +#' @details This function finds a sparse Cholesky factor using a fill-reducing +#' `P` (AMD reordering by default), so the factorization is actually: +# \eqn{P A P^\top = L L^\top}. +#' +#' @param x a positive definite matrix +#' +#' @author nCompiler development team +#' +#' @export +#' +#' @details +#' +#' @examples TODO +#' +sparseCholFactor <- function(x) { + Matrix::Cholesky(x, LDL = FALSE) +} + #' Compute the log-determinant of a matrix #' #' In a \code{nFunction}, \code{nLogdet} is identical to \code{logdet} #' -#' @details This function is similar to R's \code{\link{diag}} function, but -#' can be used in a nFunction and compiled using \code{nCompile}. -#' -#' @param x a square matrix +#' @param x a square matrix or a sparse Cholesky factor #' #' @export #' nLogdet <- function(x) { + if(inherits(x, 'dCHMsimpl')) + return(sum(log(diag(expand1(chol, "L"))))) ldet <- determinant(x, logarithm = TRUE) ifelse(ldet$sign >= 0, ldet$modulus, NaN) } @@ -410,20 +437,31 @@ asDense <- function(x) { #' #' @export nMul <- function(x, y) { + if(inherits(x, "dCHMsimpl")) + # P^{top} L* x (see ?Matrix:::solve)) + return(solve(ch, expand1(ch, "L") %*% x, system = "Pt")) x %*% y } +## This would only work if Matrix is loaded. +## setMethod("%*%", c(x = "dCHMsimpl", y = "ANY"), +## function(x,y) +## solve(ch, expand1(ch, "L") %*% x, system = "Pt")) # P^{top} L* x (see ?Matrix:::solve)) + #' Wrapper for solve #' #' @export nSolve <- function(a, b) { - solve(a,b) + solve(a,b) # This works fine if `a` is of type `dCHMsimpl`. } #' Wrapper for forwardsolve #' #' @export nForwardsolve <- function(l, x) { + ## TODO: add error trap for non-sparse Cholesky factor input + if(inherits(r, 'dCHMsimpl')) + stop("`nForwardsolve` not implemented for sparse Cholesky factor input") forwardsolve(l,x) } @@ -431,6 +469,8 @@ nForwardsolve <- function(l, x) { #' #' @export nBacksolve <- function(r, x) { + if(inherits(r, 'dCHMsimpl')) + return(solve(r, solve(ch, r, system = "Lt"), system = "Pt")) # P^{top} U*^{-1} x backsolve(r,x) } diff --git a/nCompiler/R/compile_aaa_operatorLists.R b/nCompiler/R/compile_aaa_operatorLists.R index 0cac8c90..3e0bb5ba 100644 --- a/nCompiler/R/compile_aaa_operatorLists.R +++ b/nCompiler/R/compile_aaa_operatorLists.R @@ -1106,6 +1106,15 @@ assignOperatorDef( ) ) +assignOperatorDef( + 'sparseChol', + list( + labelAbstractTypes = list( + handler = 'sparseChol' + ) + ) +) + assignOperatorDef( c('nChol'), list( @@ -1120,7 +1129,7 @@ assignOperatorDef( c('nLogdet'), list( labelAbstractTypes = list( - handler = 'UnaryReduction' + handler = 'nLogdet' ) ) ) diff --git a/nCompiler/R/compile_labelAbstractTypes.R b/nCompiler/R/compile_labelAbstractTypes.R index 15d69c3b..e15491bb 100644 --- a/nCompiler/R/compile_labelAbstractTypes.R +++ b/nCompiler/R/compile_labelAbstractTypes.R @@ -522,19 +522,38 @@ inLabelAbstractTypesEnv( ) inLabelAbstractTypesEnv( - nChol <- function(code, symTab, auxEnv, handlingInfo) { + sparseChol <- function(code, symTab, auxEnv, handlingInfo) { inserts <- recurse_labelAbstractTypes(code, symTab, auxEnv, handlingInfo) argType <- code$args[[1]]$type if(inherits(argType, 'symbolSparse')) { + # Cholesky factor of a sparse matrix is a collection of information + code$type <- symbolNC$new( + name = code$name, type = 'sparseCholFactor', NCgenerator = sparseCholFactor, isArg = FALSE + ) + } else { # Fill in here when handle dense too. + # Cholesky factor of a dense matrix is a dense matrix (i.e., same type) + type <- setReturnType(handlingInfo, argType$type) + nDim <- setReturn_nDim(handlingInfo, argType$nDim) + code$type <- symbolBasic$new(type = type, nDim = nDim) + } + invisible(inserts) + } +) + +inLabelAbstractTypesEnv( + nChol <- function(code, symTab, auxEnv, handlingInfo) { + inserts <- recurse_labelAbstractTypes(code, symTab, auxEnv, handlingInfo) + argType <- code$args[[1]]$type + #if(inherits(argType, 'symbolSparse')) { # Cholesky factor of a sparse matrix is a collection of matrices # TODO: do we need to specify arguments for the initializer? - code$type <- symbolSparseCholesky$new(name = code$name) - } else { + # code$type <- symbolSparseCholesky$new(name = code$name) + #} else { # Cholesky factor of a dense matrix is a dense matrix (i.e., same type) type <- setReturnType(handlingInfo, argType$type) nDim <- setReturn_nDim(handlingInfo, argType$nDim) code$type <- symbolBasic$new(type = type, nDim = nDim) - } + #} invisible(inserts) } ) @@ -1477,7 +1496,8 @@ inLabelAbstractTypesEnv( # product between two input vectors, which returns a matrix resDim <- 2 # return a dense object if any arguments are dense, o/w return sparse - if(all(sapply(code$args, function(a) inherits(a$type, 'symbolSparse')))) { + if(all(sapply(code$args, function(a) inherits(a$type, 'symbolSparse'))) || + inherits(code$args[[1]]$type, 'symbolSimplicialLLT') && inherits(code$args[[2]]$type, 'symbolSparse')) { code$type <- symbolSparse$new(nDim = resDim, type = returnType) } else { code$type <- symbolBasic$new(nDim = resDim, type = returnType) @@ -1601,6 +1621,41 @@ inLabelAbstractTypesEnv( } ) +inLabelAbstractTypesEnv( + # nChol + chol <- function(code, symTab, auxEnv, handlingInfo) { + insertions <- recurse_labelAbstractTypes(code, symTab, auxEnv, handlingInfo) + argType <- code$args[[1]]$type + if(inherits(argType, 'symbolSparse')) { + code$type <- symbolSimplicialLLT$new(name = code$name) + } else + ## Put dense chol handling here + stop(exprClassProcessingErrorMsg( + code, + 'sparse Cholesky factor only supported for sparse matrices.' + ), call. = FALSE) + invisible(NULL) + } +) + +inLabelAbstractTypesEnv( + nLogdet <- + function(code, symTab, auxEnv, handlingInfo) { + if(length(code$args) != 1) + stop(exprClassProcessingErrorMsg( + code, + 'nLogdet called with argument length != 1.' + ), + call. = FALSE) + + inserts <- recurse_labelAbstractTypes(code, symTab, auxEnv, handlingInfo) + + code$type <- symbolBasic$new(nDim = 0, + type = setReturnType(handlingInfo, "double")) + inserts + } +) + inLabelAbstractTypesEnv( nEigen <- function(code, symTab, auxEnv, handlingInfo) { # determine object's natural type diff --git a/nCompiler/R/symbolTable.R b/nCompiler/R/symbolTable.R index 5d22de7f..329c9ac3 100644 --- a/nCompiler/R/symbolTable.R +++ b/nCompiler/R/symbolTable.R @@ -629,3 +629,25 @@ symbolCppVar <- R6::R6Class( } ) ) + +symbolSimplicialLLT <- R6::R6Class( + classname = "symbolSimplicialLLT", + inherit = symbolBase, + portable = TRUE, + public = list( + interface = FALSE, + initialize = function(...) { + super$initialize(..., interface = self$interface) + self$type <- "simplicialLLT" + self + }, + print = function() { + writeLines(paste0(self$name, ': symbolSimplicialLLT')) + }, + genCppVar = function() { + cppVarFullClass$new(name = self$name, + baseType = "Eigen::SimplicialLLT", # "std::shared_ptr", + templateArgs = list("Eigen::SparseMatrix")) + } + ) +) diff --git a/nCompiler/R/typeDeclarations.R b/nCompiler/R/typeDeclarations.R index 603ac6d4..37ab1657 100644 --- a/nCompiler/R/typeDeclarations.R +++ b/nCompiler/R/typeDeclarations.R @@ -268,6 +268,9 @@ typeDeclarationList <- list( type = "double") { nSparseType(scalarType = type, nDim = 1) }, + simplicialLLT = function() { + symbolSimplicialLLT$new() + }, nList = function(type) { elementSym <- argType2symbol(type) symbolNlist$new(elementSym = elementSym) diff --git a/nCompiler/R/zzz_NC_Predefined.R b/nCompiler/R/zzz_NC_Predefined.R index 4998a11e..eff5389a 100644 --- a/nCompiler/R/zzz_NC_Predefined.R +++ b/nCompiler/R/zzz_NC_Predefined.R @@ -125,3 +125,14 @@ OptimResultList <- nClass( message = 'RcppCharacterVector' ) ) + +#' @export +sparseCholFactor <- nClass( + classname = 'sparseCholFactor', + predefined = quote(system.file(file.path("include","nCompiler", "predef"), package="nCompiler") |> + file.path("sparseCholFactor_nC")), + Cpublic = list( + llt = 'simplicialLLT' + ) +) + diff --git a/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations.h b/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations.h index e4607f03..6b56963a 100644 --- a/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations.h +++ b/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations.h @@ -211,19 +211,37 @@ Eigen::Tensor binaryOp( return binaryOp(xEval, y); } -// forward declaration of nCompiler struct to store Sparse Chol. decompositions -class SparseCholesky; +// forward declaration of class to store sparse Cholesky decompositions +// class SparseCholesky; +class sparseCholFactor; + +// TODO: Not sure if IsSparseCholFactor stuff should be here or in tensorOperations_chol.h. +// I think we need to use `IsSparseCholFactor` in `IsEvaluatedType`. /** - * Template meta programming check to see if Class is an Eigen::SparseCholesky + * Template meta programming check to see if Class is an Eigen::SparseCholFactor * * @tparam Class type to inspect */ template -struct IsSparseCholesky : std::is_base_of< - SparseCholesky, +struct IsSparseCholFactor : std::is_base_of< + std::shared_ptr, Class > { }; + +/** + * Returns true if template Class has N dimensions + * + * Intended to be used as a template metaprogramming aid. + * + * @tparam Class Type to inspect, restricted to std::shared_ptr by SFINAE + * @tparam N Number of dimensions to test for + */ +template +constexpr typename std::enable_if::value, bool>::type + HasNumDimensionsN() { + return N == 2; // matrices are inherently 2-dimensional + } /** * Template meta programming check to see if Class is an Eigen::SparseMatrix @@ -394,7 +412,7 @@ std::conditional< template struct IsEvaluatedType : std::conditional< IsSparseType::value || IsTensor::value || - IsSparseCholesky::value || std::is_arithmetic::value || + IsSparseCholFactor::value || std::is_arithmetic::value || IsMap::value || IsTranspose::value, std::true_type, std::false_type @@ -510,6 +528,7 @@ TENSOR_SPMAT_OP(!=, nCompiler::logical_neq) return N == 1; // vectors are inherently 1-dimensional } + /** * Implicitly convert a Tensor expression input to an Eigen::Tensor object * @@ -747,6 +766,7 @@ Eigen::Tensor asDense(SpMatExpr &x) { return res; } +/* template SparseCholType nChol(const Eigen::SparseMatrix &x) { Eigen::SimplicialLLT> llt(x); @@ -758,6 +778,7 @@ SparseCholType nChol(const Eigen::SparseMatrix &x) { res.P = P; return res; } +*/ /** * Compute the Cholesky decomposition for a symmetric matrix stored as an @@ -965,7 +986,7 @@ Eigen::SparseMatrix nDiagonal(Xpr x, Index nrow, Index ncol) { * DiagIO class that uses conversion operators and overloaded * assignment operators to provide additional functionality (i.e., assignment * and extraction of diagonal entries) in a way that is compatible with - * nCompiler/R-like syntax. Without the added flexibility, nCompiler would + * nCompiler/R-like syntax. Without the added flexibility, nCompiler wonuld * need to perform additional, possibly complex, modification of an nFunction's * AST in order to change R-like syntax into Eigen-like syntax. * @@ -1330,6 +1351,7 @@ Eigen::Tensor nBacksolve( return triangularsolve(U, b); } + /** * Matrix multiplication x %*% y when both inputs are matrix-like objects, i.e., * rank 2 Eigen::Tensor objects, or Tensor expressions @@ -1593,7 +1615,6 @@ Scalar nLogdet(const Xpr & x) { return qrdecomp.logDeterminant(); } -// TODO: implement nLogdet for sparse matrices // This is drafted but not yet used. template diff --git a/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations_chol.h b/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations_chol.h new file mode 100644 index 00000000..7f9c03c1 --- /dev/null +++ b/nCompiler/inst/include/nCompiler/ET_ext/post_Rcpp/tensorOperations_chol.h @@ -0,0 +1,113 @@ +#ifndef TENSOROPERATIONS_CHOL_H +#define TENSOROPERATIONS_CHOL_H + + +std::shared_ptr sparseChol(const Eigen::SparseMatrix& x) { + std::shared_ptr ch = nClass_builder()(); + ch->llt.compute(x); + if (ch->llt.info() != Eigen::Success) + throw std::runtime_error("Cholesky factorization failed"); + return ch; +} + +// This overloads nLogdet, on top of use for standard matrices with calculation +// done via the SVD. +double nLogdet(std::shared_ptr ch) { + Eigen::SparseMatrix L = ch->llt.matrixL(); + return L.diagonal().array().log().sum(); +} + +// Should we implement nLogdet for operation directly on sparse matrix? + +template +Eigen::Tensor nSolve(std::shared_ptr ch, const RHS & b + ) { + // explicit Eigen::Tensor types for inputs + typedef Eigen::Tensor bTensor; + // evaluate arguments, if necessary + const auto & b_eval = eval(b); + // initialize storage for solution, given problem dimensions + auto bdim = b.dimensions(); + bTensor res = Eigen::Tensor(bdim[0], bdim.size() > 1 ? bdim[1]: 1); + // map tensor objects to Eigen::Matrix types + auto bmap = matmap(b_eval); + auto resMap = matmap(res); + // solve linear system + resMap = ch->llt.solve(bmap); + return res; +} + +// In progress +template +Eigen::Tensor nBacksolve(std::shared_ptr ch, const RHS & b + ) { + // explicit Eigen::Tensor types for inputs + typedef Eigen::Tensor bTensor; + // evaluate arguments, if necessary + const auto & b_eval = eval(b); + // initialize storage for solution, given problem dimensions + auto bdim = b.dimensions(); + bTensor res = Eigen::Tensor(bdim[0], bdim.size() > 1 ? bdim[1]: 1); + // map tensor objects to Eigen::Matrix types + auto bmap = matmap(b_eval); + auto resMap = matmap(res); + // solve linear system + resMap = ch->llt.permutationPinv() * ch->llt.matrixU().triangularView().solve(bmap); + return res; +} + +// Multiply L by matrix. +template< + typename Ypr, + typename std::enable_if< + HasNumDimensionsN(), + Ypr + >::type* = nullptr, + typename ResultType = typename std::conditional< + IsSparseType::value, + Eigen::SparseMatrix, + Eigen::Tensor + >::type +> +ResultType nMul(std::shared_ptr ch, const Ypr & y) { + // evaluate arguments, if necessary + const auto & yeval = eval(y); + // map inputs and initialize output + auto ymap = matmap(yeval); + ResultType res(ymap.rows(), ymap.cols()); + // map and multiply! + matmap(res) = ch->llt.permutationPinv() * (ch->llt.matrixL() * ymap); + return res; +} + +// Multiply L by vector. +// TODO: is there any way for us to return a vector instead of a matrix? +template< + typename Ypr, + typename std::enable_if< + HasNumDimensionsN(), + Ypr + >::type* = nullptr, + typename ResultType = typename std::conditional< + IsSparseType::value, + Eigen::SparseMatrix, + Eigen::Tensor + >::type +> +ResultType nMul(std::shared_ptr ch, const Ypr & y) { + // evaluate arguments, if necessary + const auto & yeval = eval(y); + // map inputs and initialize output + auto ymap = matmap(yeval); + // initialize output + ResultType res(ymap.size(), 1); + // map and multiply! + matmap(res) = ch->llt.permutationPinv() * (ch->llt.matrixL() * ymap); + return res; +} + + + +// Add function for dense chol factor too. + +#endif diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_copyFiles.txt b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_copyFiles.txt new file mode 100644 index 00000000..e69de29b diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_cppContent.cpp b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_cppContent.cpp new file mode 100644 index 00000000..1c18aa87 --- /dev/null +++ b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_cppContent.cpp @@ -0,0 +1,43 @@ +/* OPENER (Do not edit this comment) */ +#ifndef __sparseCholFactor_CPP +#define __sparseCholFactor_CPP +/* BODY (Do not edit this comment) */ +#ifndef R_NO_REMAP +#define R_NO_REMAP +#endif +#include +#include "sparseCholFactor_c_.h" +using namespace Rcpp; +// [[Rcpp::plugins(nCompiler_Eigen_plugin)]] +// [[Rcpp::depends(RcppParallel)]] +// [[Rcpp::depends(nCompiler)]] +// [[Rcpp::depends(Rcereal)]] + + sparseCholFactor::sparseCholFactor ( ) { +RESET_EIGEN_ERRORS +} + +// [[Rcpp::export(name = "sparseCholFactor_new")]] + SEXP new_sparseCholFactor ( ) { +RESET_EIGEN_ERRORS +return CREATE_NEW_NCOMP_OBJECT(sparseCholFactor);; +} + +// [[Rcpp::export(name = "set_CnClass_env_sparseCholFactor_new")]] + void set_CnClass_env_sparseCholFactor ( SEXP env ) { +RESET_EIGEN_ERRORS +SET_CNCLASS_ENV(sparseCholFactor, env);; +} + +// [[Rcpp::export(name = "get_CnClass_env_sparseCholFactor_new")]] + Rcpp::Environment get_CnClass_env_sparseCholFactor ( ) { +RESET_EIGEN_ERRORS +return GET_CNCLASS_ENV(sparseCholFactor);; +} + +NCOMPILER_INTERFACE( +sparseCholFactor, +NCOMPILER_FIELDS(), +NCOMPILER_METHODS() +) +#endif diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_filebase.txt b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_filebase.txt new file mode 100644 index 00000000..0f6dfd79 --- /dev/null +++ b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_filebase.txt @@ -0,0 +1 @@ +sparseCholFactor_c_ diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_hContent.h b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_hContent.h new file mode 100644 index 00000000..7c455795 --- /dev/null +++ b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_hContent.h @@ -0,0 +1,24 @@ +/* OPENER (Do not edit this comment) */ +#ifndef __sparseCholFactor_H +#define __sparseCholFactor_H +/* BODY (Do not edit this comment) */ +#ifndef R_NO_REMAP +#define R_NO_REMAP +#endif +#include + +class sparseCholFactor : public interface_resolver< genericInterfaceC >, public loadedObjectHookC { +public: + sparseCholFactor ( ) ; + Eigen::SimplicialLLT> llt; +}; + + SEXP new_sparseCholFactor ( ) ; + + void set_CnClass_env_sparseCholFactor ( SEXP env ) ; + + Rcpp::Environment get_CnClass_env_sparseCholFactor ( ) ; + +#include + +#endif diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_manifest.txt b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_manifest.txt new file mode 100644 index 00000000..ffb07a3d --- /dev/null +++ b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_manifest.txt @@ -0,0 +1,7 @@ +list(saved_at = structure(1778520082.54572, class = c("POSIXct", +"POSIXt")), packet_name = "sparseCholFactor", elements = c("preamble", +"cppContent", "hContent", "filebase", "post_cpp_compiler", "copyFiles" +), files = list(preamble = "sparseCholFactor_preamble.cpp", cppContent = "sparseCholFactor_cppContent.cpp", + hContent = "sparseCholFactor_hContent.h", filebase = "sparseCholFactor_filebase.txt", + post_cpp_compiler = "sparseCholFactor_post_cpp_compiler.txt", + copyFiles = "sparseCholFactor_copyFiles.txt")) diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_post_cpp_compiler.txt b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_post_cpp_compiler.txt new file mode 100644 index 00000000..e69de29b diff --git a/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_preamble.cpp b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_preamble.cpp new file mode 100644 index 00000000..61410333 --- /dev/null +++ b/nCompiler/inst/include/nCompiler/predef/sparseCholFactor_nC/sparseCholFactor_preamble.cpp @@ -0,0 +1,6 @@ +#define NCOMPILER_HANDLE_EIGEN_ERRORS +#define NCOMPILER_USES_EIGEN +// #define NCOMPILER_USES_TBB +#define NCOMPILER_USES_NLIST +#define USES_NCOMPILER +#define NCOMPILER_USES_NCLASS_INTERFACE diff --git a/nCompiler/tests/testthat/tensorOps_tests/test-tensorOperations_sparse.R b/nCompiler/tests/testthat/tensorOps_tests/test-tensorOperations_sparse.R index e9e017e6..43233ea7 100644 --- a/nCompiler/tests/testthat/tensorOps_tests/test-tensorOperations_sparse.R +++ b/nCompiler/tests/testthat/tensorOps_tests/test-tensorOperations_sparse.R @@ -270,3 +270,4 @@ expect_equal(cAdd2_force_dense_mixed(x = M_sparse, y = M2), M + M2) expect_equal(cAsSparse(x = M), M_sparse) expect_equal(cAsDense(x = M_sparse), M) expect_equal(cAdd2_force_dense_unnecessary(x = M, y = M2), M + M2) +