From 08609e6f3638e7bccd08bf54a3d0f6109951226b Mon Sep 17 00:00:00 2001 From: david-cortes Date: Sat, 22 May 2021 21:56:40 +0300 Subject: [PATCH] switch to MatrixExtra (#68) --- DESCRIPTION | 4 +- NAMESPACE | 7 - R/RcppExports.R | 24 -- R/methods.R | 489 ------------------------------ R/model_WRMF.R | 28 +- README.md | 8 +- man/WRMF.Rd | 2 +- man/casting.Rd | 66 ---- man/matmult.Rd | 66 ---- man/slice.Rd | 43 --- src/RcppExports.cpp | 87 ------ src/matrix_csr.cpp | 241 --------------- src/matrix_product.cpp | 49 --- tests/testthat/test-conversions.R | 162 ---------- tests/testthat/test-csr-subset.R | 114 ------- tests/testthat/test-ftrl.R | 2 +- tests/testthat/test-matmul.R | 23 -- 17 files changed, 25 insertions(+), 1390 deletions(-) delete mode 100644 R/methods.R delete mode 100644 man/casting.Rd delete mode 100644 man/matmult.Rd delete mode 100644 man/slice.Rd delete mode 100644 src/matrix_csr.cpp delete mode 100644 src/matrix_product.cpp delete mode 100644 tests/testthat/test-conversions.R delete mode 100644 tests/testthat/test-csr-subset.R delete mode 100644 tests/testthat/test-matmul.R diff --git a/DESCRIPTION b/DESCRIPTION index c3486e8..ef361af 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,9 +40,9 @@ License: GPL (>= 2) Encoding: UTF-8 LazyData: true ByteCompile: true -Depends: R (>= 3.6.0), methods +Depends: R (>= 3.6.0), methods, MatrixExtra Imports: - Matrix (>= 1.3), + Matrix (>= 1.3), Rcpp (>= 0.11), data.table (>= 1.10.0), float (>= 0.2-2), diff --git a/NAMESPACE b/NAMESPACE index c1957f3..256e1f9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,17 +8,10 @@ export(PureSVD) export(ScaleNormalize) export(WRMF) export(ap_k) -export(as.coo.matrix) -export(as.csc.matrix) -export(as.csr.matrix) export(detect_number_omp_threads) export(ndcg_k) export(soft_impute) export(soft_svd) -exportMethods("%*%") -exportMethods("[") -exportMethods(crossprod) -exportMethods(tcrossprod) import(Matrix) import(Rcpp) import(data.table) diff --git a/R/RcppExports.R b/R/RcppExports.R index ffa5d30..566673c 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -53,30 +53,6 @@ arma_kmeans <- function(x, k, seed_mode, n_iter, verbose, result) { .Call(`_rsparse_arma_kmeans`, x, k, seed_mode, n_iter, verbose, result) } -check_is_seq <- function(indices) { - .Call(`_rsparse_check_is_seq`, indices) -} - -copy_csr_rows <- function(indptr, indices, values, rows_take) { - .Call(`_rsparse_copy_csr_rows`, indptr, indices, values, rows_take) -} - -copy_csr_rows_col_seq <- function(indptr, indices, values, rows_take, cols_take) { - .Call(`_rsparse_copy_csr_rows_col_seq`, indptr, indices, values, rows_take, cols_take) -} - -copy_csr_arbitrary <- function(indptr, indices, values, rows_take, cols_take) { - .Call(`_rsparse_copy_csr_arbitrary`, indptr, indices, values, rows_take, cols_take) -} - -csr_dense_tcrossprod <- function(x_csr_r, y_transposed, num_threads = 1L) { - .Call(`_rsparse_csr_dense_tcrossprod`, x_csr_r, y_transposed, num_threads) -} - -dense_csc_prod <- function(x_r, y_csc_r, num_threads = 1L) { - .Call(`_rsparse_dense_csc_prod`, x_r, y_csc_r, num_threads) -} - top_product <- function(x, y, k, n_threads, not_recommend_r, exclude, glob_mean = 0.) { .Call(`_rsparse_top_product`, x, y, k, n_threads, not_recommend_r, exclude, glob_mean) } diff --git a/R/methods.R b/R/methods.R deleted file mode 100644 index 1d66d54..0000000 --- a/R/methods.R +++ /dev/null @@ -1,489 +0,0 @@ -#' Multithreaded Sparse-Dense Matrix Multiplication -#' -#' @description Multithreaded \code{\%*\%}, \code{crossprod}, \code{tcrossprod} -#' for sparse-dense matrix multiplication -#' -#' @details -#' Accelerates sparse-dense matrix multiplications using openmp. Applicable to the following pairs: -#' (\code{dgRMatrix}, \code{matrix}), (\code{matrix}, \code{dgRMatrix}), -#' (\code{dgCMatrix}, \code{matrix}), (\code{matrix}, \code{dgCMatrix}) combinations -#' -#' @param x,y -#' dense \code{matrix} and sparse -#' \code{Matrix::RsparseMatrix} / \code{Matrix::CsparseMatrix} matrices. -#' -#' @return -#' A dense \code{matrix} -#' -#' @name matmult -#' @rdname matmult -#' @examples -#' library(Matrix) -#' data("movielens100k") -#' k = 10 -#' nc = ncol(movielens100k) -#' nr = nrow(movielens100k) -#' x_nc = matrix(rep(1:k, nc), nrow = nc) -#' x_nr = t(matrix(rep(1:k, nr), nrow = nr)) -#' csc = movielens100k -#' csr = as(movielens100k, "RsparseMatrix") -#' dense = as.matrix(movielens100k) -#' identical(csr %*% x_nc, dense %*% x_nc) -#' identical(x_nr %*% csc, x_nr %*% dense) -NULL - -#' @rdname matmult -#' @export -setMethod("%*%", signature(x="dgRMatrix", y="matrix"), function(x, y) { - # restore on exit - n_thread = RhpcBLASctl::blas_get_num_procs() - on.exit(RhpcBLASctl::blas_set_num_threads(n_thread)) - - # set num threads to 1 in order to avoid thread contention between BLAS and openmp threads - RhpcBLASctl::blas_set_num_threads(1L) - - - - check_dimensions_match(x, y) - res = csr_dense_tcrossprod(x, t(y), getOption("rsparse_omp_threads", 1L)) - set_dimnames(res, rownames(x), colnames(y)) -}) - -#' @rdname matmult -#' @export -setMethod("%*%", signature(x="dgRMatrix", y="float32"), function(x, y) { - x %*% float::dbl(y) -}) - -#' @rdname matmult -#' @export -setMethod("%*%", signature(x="float32", y="dgRMatrix"), function(x, y) { - float::dbl(x) %*% y -}) - -#' @rdname matmult -#' @export -setMethod("tcrossprod", signature(x="dgRMatrix", y="matrix"), function(x, y) { - # restore on exit - n_thread = RhpcBLASctl::blas_get_num_procs() - on.exit(RhpcBLASctl::blas_set_num_threads(n_thread)) - - # set num threads to 1 in order to avoid thread contention between BLAS and openmp threads - RhpcBLASctl::blas_set_num_threads(1L) - - check_dimensions_match(x, y, y_transposed = TRUE) - res = csr_dense_tcrossprod(x, y, getOption("rsparse_omp_threads", 1L)) - set_dimnames(res, rownames(x), rownames(y)) -}) - -#' @rdname matmult -#' @export -setMethod("tcrossprod", signature(x="dgRMatrix", y="float32"), function(x, y) { - tcrossprod(x, float::dbl(y)) -}) - -#' @rdname matmult -#' @export -setMethod("%*%", signature(x="matrix", y="dgCMatrix"), function(x, y) { - # restore on exit - n_thread = RhpcBLASctl::blas_get_num_procs() - on.exit(RhpcBLASctl::blas_set_num_threads(n_thread)) - - # set num threads to 1 in order to avoid thread contention between BLAS and openmp threads - RhpcBLASctl::blas_set_num_threads(1L) - - - check_dimensions_match(x, y) - res = dense_csc_prod(x, y, getOption("rsparse_omp_threads", 1L)) - set_dimnames(res, rownames(x), colnames(y)) -}) - -#' @rdname matmult -#' @export -setMethod("%*%", signature(x="float32", y="dgCMatrix"), function(x, y) { - float::dbl(x) %*% y -}) - -#' @rdname matmult -#' @export -setMethod("%*%", signature(x="dgCMatrix", y="float32"), function(x, y) { - x %*% float::dbl(y) -}) - -#' @rdname matmult -#' @export -setMethod("crossprod", signature(x="matrix", y="dgCMatrix"), function(x, y) { - # restore on exit - n_thread = RhpcBLASctl::blas_get_num_procs() - on.exit(RhpcBLASctl::blas_set_num_threads(n_thread)) - - # set num threads to 1 in order to avoid thread contention between BLAS and openmp threads - RhpcBLASctl::blas_set_num_threads(1L) - - - x = t(x) - check_dimensions_match(x, y) - res = dense_csc_prod(x, y, getOption("rsparse_omp_threads", 1L)) - set_dimnames(res, rownames(x), colnames(y)) -}) - -#' @rdname matmult -#' @export -setMethod("crossprod", signature(x="float32", y="dgCMatrix"), function(x, y) { - crossprod(float::dbl(x), y) -}) - -get_indices_integer = function(i, max_i, index_names) { - if(is.numeric(i)) i = as.integer(i) - if(is.character(i)) i = match(i, index_names) - if(is.logical(i)) { - if (length(i) != max_i) { - i = seq(1L, max_i)[i] - } else { - i = which(i) - } - } - if (any(i < 0)) - i = seq(1L, max_i)[i] - if(anyNA(i) || any(i > max_i, na.rm = TRUE)) - stop("some of row subset indices are not present in matrix") - as.integer(i) -} - -#' CSR Matrices Slicing -#' -#' @description natively slice CSR matrices without converting them to triplet/CSC -#' -#' @param x input \code{RsparseMatrix} -#' @param i row indices to subset -#' @param j column indices to subset -#' @param drop whether to simplify 1d matrix to a vector -#' -#' @return -#' A \code{RsparseMatrix} -#' -#' @name slice -#' @examples -#' library(Matrix) -#' library(rsparse) -#' # dgCMatrix - CSC -#' m = rsparsematrix(20, 20, 0.1) -#' # make CSR -#' m = as(m, "RsparseMatrix") -#' inherits(m[1:2, ], "RsparseMatrix") -#' inherits(m[1:2, 3:4], "RsparseMatrix") -NULL - -subset_csr = function(x, i, j, drop = TRUE) { - - if(missing(i) && missing(j)) return(x) - - row_names = rownames(x) - col_names = colnames(x) - - all_i = FALSE - all_j = FALSE - i_is_seq = FALSE - j_is_seq = FALSE - if (missing(j)) { - all_j = TRUE - j = seq_len(ncol(x)) - n_col = ncol(x) - } else { - j = get_indices_integer(j, ncol(x), col_names) - if (length(j) == ncol(x) && j[1L] == 1L && j[length(j)] == ncol(x)) { - if (all(j == seq(1L, ncol(x)))) - all_j = TRUE - } else { - j_is_seq = check_is_seq(j) - } - n_col = length(j) - } - if (missing(i)) { - i = seq_len(nrow(x)) - all_i = TRUE - i_is_seq = TRUE - n_row = nrow(x) - } else { - # convert integer/numeric/logical/character indices to integer indices - # also takes care of negative indices - i = get_indices_integer(i, nrow(x), row_names) - i_is_seq = check_is_seq(i) - n_row = length(i) - - if (all_j && i_is_seq && length(i) == nrow(x) && i[1L] == 1L && i[length(i)] == nrow(x)) { - if (all(i == seq(1L, nrow(x)))) - all_i = TRUE - } - } - - if (!NROW(i) || !NROW(j)) { - res = new("dgRMatrix") - res@p = integer(NROW(i) + 1L) - res@Dim = c(NROW(i), NROW(j)) - - row_names = if(is.null(row_names) || !NROW(row_names)) NULL else row_names[i] - col_names = if(is.null(col_names) || !NROW(col_names)) NULL else col_names[j] - res@Dimnames = list(row_names, col_names) - return(res) - } - - if (all_i && all_j) { - return(x) - } else if (length(x@x) == 0L) { - indptr = integer(n_row + 1) - col_indices = integer() - x_values = numeric() - } else if (i_is_seq && all_j) { - first = x@p[i[1L]] + 1L - last = x@p[i[n_row] + 1L] - indptr = x@p[seq(i[1L], i[n_row]+1L)] - x@p[i[1L]] - col_indices = x@j[first:last] - x_values = x@x[first:last] - } else if (!i_is_seq && all_j) { - temp = copy_csr_rows(x@p, x@j, x@x, i-1L) - indptr = temp$indptr - col_indices = temp$indices - x_values = temp$values - } else if (j_is_seq) { - temp = copy_csr_rows_col_seq(x@p, x@j, x@x, i-1L, j-1L) - indptr = temp$indptr - col_indices = temp$indices - x_values = temp$values - } else { - temp = copy_csr_arbitrary(x@p, x@j, x@x, i-1L, j-1L) - indptr = temp$indptr - col_indices = temp$indices - x_values = temp$values - } - - res = new("dgRMatrix") - res@p = indptr - res@j = col_indices - res@x = x_values - res@Dim = c(n_row, n_col) - - row_names = if(is.null(row_names) || !NROW(row_names)) NULL else row_names[i] - col_names = if(is.null(col_names) || !NCOL(col_names)) NULL else col_names[j] - res@Dimnames = list(row_names, col_names) - - if(isTRUE(drop) && (n_row == 1L || n_col == 1L)) - res = as.vector(res) - res -} - -#' @rdname slice -#' @export -setMethod(`[`, signature(x = "RsparseMatrix", i = "index", j = "index", drop="logical"), subset_csr) -#' @rdname slice -#' @export -setMethod(`[`, signature(x = "RsparseMatrix", i = "missing", j = "index", drop="logical"), subset_csr) -#' @rdname slice -#' @export -setMethod(`[`, signature(x = "RsparseMatrix", i = "index", j = "missing", drop="logical"), subset_csr) -#' @rdname slice -#' @export -setMethod(`[`, signature(x = "RsparseMatrix", i = "missing", j = "missing", drop="logical"), subset_csr) - - -# nocov start -set_dimnames = function(target, new_rownames, new_colnames) { - data.table::setattr(target, 'dimnames', list(new_rownames, new_colnames)) - invisible(target) -} - -check_dimensions_match = function(x, y, y_transposed = FALSE) { - test_non_conformable = if(y_transposed) { - ncol(x) != ncol(y) - } else { - ncol(x) != nrow(y) - } - if(test_non_conformable) stop("non-conformable arguments") -} - -# nocov end - -#' Conversions between matrix types -#' -#' @description Convenience functions for converting to different sparse matrix formats. -#' -#' @details The functions internally use as(x, "?sparseMatrix"), so they might work -#' with other object classes if they register a conversion method for `Matrix` base -#' types. -#' -#' When passed a vector, the functions `as.csr.matrix` and `as.coo.matrix` will -#' assume that it is a row vector, while `as.csc.matrix` will assume it's a column vector. -#' -#' @param x A matrix which is to be converted to a different format. -#' @param binary Whether the result should be a binary-only matrix (inheriting from -#' class `nsparseMatrix` - these don't have slot `x`). -#' Supported input types are:\itemize{ -#' \item Sparse matrices from `Matrix` package, in any format. -#' \item Sparse vector from `Matrix` (class `dsparseVector`). -#' \item Dense matrix from base R. -#' \item Dense vector from base R (classes `numeric` and `integer`). -#' \item Dense matrix or vector from package `float` (class `float32`). -#' \item `data.frame` and `data.table`. -#' } -#' -#' @return A sparse matrix, with format:\itemize{ -#' \item CSR (a.k.a. `RsparseMatrix`) when calling `as.csr.matrix` -#' (class `dgRMatrix` with `binary=FALSE`, class `ngRMatrix` with `binary=TRUE`). -#' \item CSC (a.k.a. `CsparseMatrix`) when calling `as.csc.matrix` -#' (class `dgCMatrix` with `binary=FALSE`, class `ngCMatrix` with `binary=TRUE`). -#' \item COO (a.k.a. `TsparseMatrix`) when calling `as.coo.matrix` -#' (class `dgTMatrix` with `binary=FALSE`, class `ngTMatrix` with `binary=TRUE`). -#' } -#' -#' @name casting -#' @examples -#' library(Matrix) -#' library(rsparse) -#' -#' m.coo = as(matrix(1:3), "TsparseMatrix") -#' as.csr.matrix(m.coo) -#' as.csr.matrix(1:3) # <- assumes it's a row vector -#' as.csc.matrix(1:3) # <- assumes it's a column vector -#' -#' library(float) -#' m.f32 = float::fl(matrix(1:10, nrow=5)) -#' as.csr.matrix(m.f32) -#' -#' library(data.table) -#' as.coo.matrix(data.table(col1=1:3)) -NULL - -#' @rdname casting -#' @export -as.csr.matrix = function(x, binary=FALSE) { - if ((inherits(x, "dgRMatrix") && !binary) || (inherits(x, "ngRMatrix") && binary)) - return(x) - - if (inherits(x, "float32")) - x = float::dbl(x) - - if (inherits(x, c("numeric", "integer"))) - x = matrix(x, nrow=1L) - - if (inherits(x, c("data.frame", "tibble", "data.table"))) - x = as.matrix(x) - - if (inherits(x, "dsparseVector")) { - X.csr = new("dgRMatrix") - X.csr@Dim = c(1L, x@length) - X.csr@p = c(0L, length(x@i)) - X.csr@j = x@i - 1L - X.csr@x = x@x - x = X.csr - } - - if (!inherits(x, "RsparseMatrix")) - x = as(x, "RsparseMatrix") - - if (!binary && !inherits(x, "dgRMatrix")) { - X.csr = new("dgRMatrix") - X.csr@Dim = x@Dim - X.csr@Dimnames = x@Dimnames - X.csr@p = x@p - X.csr@j = x@j - if (.hasSlot(x, "x")) - X.csr@x = as.numeric(x@x) - else - X.csr@x = rep(1., length(x@j)) - x = X.csr - } - - if (binary && !inherits(x, "ngRMatrix")) { - X.csr = new("ngRMatrix") - X.csr@Dim = x@Dim - X.csr@Dimnames = x@Dimnames - X.csr@p = x@p - X.csr@j = x@j - x = X.csr - } - return(x) -} - -#' @rdname casting -#' @export -as.csc.matrix = function(x, binary=FALSE) { - if ((inherits(x, "dgCMatrix") && !binary) || (inherits(x, "ngCMatrix") && binary)) - return(x) - - if (inherits(x, "float32")) - x = float::dbl(x) - - if (inherits(x, c("numeric", "integer", "data.frame", "tibble", "data.table"))) - x = as.matrix(x) - - if (!inherits(x, "CsparseMatrix")) - x = as(x, "CsparseMatrix") - - if (!binary && !inherits(x, "dgCMatrix")) { - X.csc = new("dgCMatrix") - X.csc@Dim = x@Dim - X.csc@Dimnames = x@Dimnames - X.csc@p = x@p - X.csc@i = x@i - if (.hasSlot(x, "x")) - X.csc@x = as.numeric(x@x) - else - X.csc@x = rep(1., length(x@i)) - x = X.csc - } - - if (binary && !inherits(x, "ngCMatrix")) { - X.csc = new("ngCMatrix") - X.csc@Dim = x@Dim - X.csc@Dimnames = x@Dimnames - X.csc@p = x@p - X.csc@i = x@i - x = X.csc - } - return(x) -} - -#' @rdname casting -#' @export -as.coo.matrix = function(x, binary=FALSE) { - if ((inherits(x, "dgTMatrix") && !binary) || (inherits(x, "ngTMatrix") && binary)) - return(x) - - if (inherits(x, "float32")) - x = float::dbl(x) - - if (inherits(x, c("numeric", "integer"))) - x = matrix(x, nrow=1L) - - if (inherits(x, c("data.frame", "tibble", "data.table"))) - x = as.matrix(x) - - if (inherits(x, "dsparseVector")) - x = as.csr.matrix(x) - - if (!inherits(x, "TsparseMatrix")) - x = as(x, "TsparseMatrix") - - if (!binary && !inherits(x, "dgTMatrix")) { - X.coo = new("dgTMatrix") - X.coo@Dim = x@Dim - X.coo@Dimnames = x@Dimnames - X.coo@i = x@i - X.coo@j = x@j - if (.hasSlot(x, "x")) - X.coo@x = as.numeric(x@x) - else - X.coo@x = rep(1., length(x@j)) - x = X.coo - } - - if (binary && !inherits(x, "ngTMatrix")) { - X.coo = new("ngTMatrix") - X.coo@Dim = x@Dim - X.coo@Dimnames = x@Dimnames - X.coo@i = x@i - X.coo@j = x@j - x = X.coo - } - return(x) -} diff --git a/R/model_WRMF.R b/R/model_WRMF.R index bdee07e..0a2e7b2 100644 --- a/R/model_WRMF.R +++ b/R/model_WRMF.R @@ -184,9 +184,9 @@ WRMF = R6::R6Class( }) } - c_ui = as(x, "CsparseMatrix") + c_ui = MatrixExtra::as.csc.matrix(x) c_ui = private$preprocess(c_ui) - c_iu = t(c_ui) + c_iu = MatrixExtra::t_shallow(MatrixExtra::as.csr.matrix(x)) # store item_ids in order to use them in predict method private$item_ids = colnames(c_ui) @@ -353,11 +353,22 @@ WRMF = R6::R6Class( }, # project new users into latent user space - just make ALS step given fixed items matrix #' @description create user embeddings for new input - #' @param x user-item iteraction matrix + #' @param x user-item iteraction matrix (preferrably as `dgRMatrix`) #' @param ... not used at the moment transform = function(x, ...) { stopifnot(ncol(x) == ncol(self$components)) + if (inherits(x, "RsparseMatrix")) { + x = MatrixExtra::t_shallow(x) + x = MatrixExtra::as.csc.matrix(x) + } else if (inherits(x, "CsparseMatrix")) { + x = MatrixExtra::t_deep(x) + x = MatrixExtra::as.csc.matrix(x) + } else { + x = MatrixExtra::as.csr.matrix(x) + x = MatrixExtra::t_shallow(x) + } + if (private$feedback == "implicit" ) { logger$trace("WRMF$transform(): calling `RhpcBLASctl::blas_set_num_threads(1)` (to avoid thread contention)") blas_threads_keep = RhpcBLASctl::blas_get_num_procs() @@ -368,15 +379,14 @@ WRMF = R6::R6Class( }) } - x = as(x, "CsparseMatrix") x = private$preprocess(x) if (self$global_bias != 0. && private$feedback == "explicit") x@x = x@x - self$global_bias if (private$precision == "double") { - res = matrix(0, nrow = private$rank, ncol = nrow(x)) + res = matrix(0, nrow = private$rank, ncol = ncol(x)) } else { - res = float(0, nrow = private$rank, ncol = nrow(x)) + res = float(0, nrow = private$rank, ncol = ncol(x)) } if (private$with_user_item_bias) { @@ -384,7 +394,7 @@ WRMF = R6::R6Class( } loss = private$solver( - t(x), + x, self$components, res, is_bias_last_row = FALSE, @@ -396,9 +406,9 @@ WRMF = R6::R6Class( res = t(res) if (private$precision == "double") - setattr(res, "dimnames", list(rownames(x), NULL)) + setattr(res, "dimnames", list(colnames(x), NULL)) else - setattr(res@Data, "dimnames", list(rownames(x), NULL)) + setattr(res@Data, "dimnames", list(colnames(x), NULL)) res } diff --git a/README.md b/README.md index 903df8f..ad9f913 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ `rsparse` is an R package for statistical learning primarily on **sparse matrices** - **matrix factorizations, factorization machines, out-of-core regression**. Many of the implemented algorithms are particularly useful for **recommender systems** and **NLP**. -On top of that we provide some optimized routines to work on sparse matrices - multithreaded matrix multiplications and improved support for sparse matrices in CSR format (`Matrix::RsparseMatrix`), as well as convenience functions to convert between matrix types. - We've paid some attention to the implementation details - we try to avoid data copies, utilize multiple threads via OpenMP and use SIMD where appropriate. Package **allows to work on datasets with millions of rows and millions of columns**. @@ -50,11 +48,9 @@ See details in [Applications of the Conjugate Gradient Method for Implicit Feedb * This is usually used to train word embeddings, but actually also very useful for recommender systems. 1. Matrix scaling as descibed in [EigenRec: Generalizing PureSVD for Effective and Efficient Top-N Recommendations](https://arxiv.org/pdf/1511.06033.pdf) -### Optimized matrix operations +********************* -1. multithreaded `%*%` and `tcrossprod()` for `` -1. multithreaded `%*%` and `crossprod()` for `` -1. natively slice `CSR` matrices (`Matrix::RsparseMatrix`) without converting them to triplet / CSC +_Note: the optimized matrix operations which `rparse` used to offer have been moved to a [separate package](https://github.com/david-cortes/MatrixExtra)_ # Installation diff --git a/man/WRMF.Rd b/man/WRMF.Rd index 268c018..1a791c7 100644 --- a/man/WRMF.Rd +++ b/man/WRMF.Rd @@ -170,7 +170,7 @@ create user embeddings for new input \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{x}}{user-item iteraction matrix} +\item{\code{x}}{user-item iteraction matrix (preferrably as `dgRMatrix`)} \item{\code{...}}{not used at the moment} } diff --git a/man/casting.Rd b/man/casting.Rd deleted file mode 100644 index b4787cb..0000000 --- a/man/casting.Rd +++ /dev/null @@ -1,66 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/methods.R -\name{casting} -\alias{casting} -\alias{as.csr.matrix} -\alias{as.csc.matrix} -\alias{as.coo.matrix} -\title{Conversions between matrix types} -\usage{ -as.csr.matrix(x, binary = FALSE) - -as.csc.matrix(x, binary = FALSE) - -as.coo.matrix(x, binary = FALSE) -} -\arguments{ -\item{x}{A matrix which is to be converted to a different format.} - -\item{binary}{Whether the result should be a binary-only matrix (inheriting from -class `nsparseMatrix` - these don't have slot `x`). -Supported input types are:\itemize{ -\item Sparse matrices from `Matrix` package, in any format. -\item Sparse vector from `Matrix` (class `dsparseVector`). -\item Dense matrix from base R. -\item Dense vector from base R (classes `numeric` and `integer`). -\item Dense matrix or vector from package `float` (class `float32`). -\item `data.frame` and `data.table`. -}} -} -\value{ -A sparse matrix, with format:\itemize{ -\item CSR (a.k.a. `RsparseMatrix`) when calling `as.csr.matrix` -(class `dgRMatrix` with `binary=FALSE`, class `ngRMatrix` with `binary=TRUE`). -\item CSC (a.k.a. `CsparseMatrix`) when calling `as.csc.matrix` -(class `dgCMatrix` with `binary=FALSE`, class `ngCMatrix` with `binary=TRUE`). -\item COO (a.k.a. `TsparseMatrix`) when calling `as.coo.matrix` -(class `dgTMatrix` with `binary=FALSE`, class `ngTMatrix` with `binary=TRUE`). -} -} -\description{ -Convenience functions for converting to different sparse matrix formats. -} -\details{ -The functions internally use as(x, "?sparseMatrix"), so they might work -with other object classes if they register a conversion method for `Matrix` base -types. - -When passed a vector, the functions `as.csr.matrix` and `as.coo.matrix` will -assume that it is a row vector, while `as.csc.matrix` will assume it's a column vector. -} -\examples{ -library(Matrix) -library(rsparse) - -m.coo = as(matrix(1:3), "TsparseMatrix") -as.csr.matrix(m.coo) -as.csr.matrix(1:3) # <- assumes it's a row vector -as.csc.matrix(1:3) # <- assumes it's a column vector - -library(float) -m.f32 = float::fl(matrix(1:10, nrow=5)) -as.csr.matrix(m.f32) - -library(data.table) -as.coo.matrix(data.table(col1=1:3)) -} diff --git a/man/matmult.Rd b/man/matmult.Rd deleted file mode 100644 index 92d98d9..0000000 --- a/man/matmult.Rd +++ /dev/null @@ -1,66 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/methods.R -\name{matmult} -\alias{matmult} -\alias{\%*\%,dgRMatrix,matrix-method} -\alias{\%*\%,dgRMatrix,float32-method} -\alias{\%*\%,float32,dgRMatrix-method} -\alias{tcrossprod,dgRMatrix,matrix-method} -\alias{tcrossprod,dgRMatrix,float32-method} -\alias{\%*\%,matrix,dgCMatrix-method} -\alias{\%*\%,float32,dgCMatrix-method} -\alias{\%*\%,dgCMatrix,float32-method} -\alias{crossprod,matrix,dgCMatrix-method} -\alias{crossprod,float32,dgCMatrix-method} -\title{Multithreaded Sparse-Dense Matrix Multiplication} -\usage{ -\S4method{\%*\%}{dgRMatrix,matrix}(x, y) - -\S4method{\%*\%}{dgRMatrix,float32}(x, y) - -\S4method{\%*\%}{float32,dgRMatrix}(x, y) - -\S4method{tcrossprod}{dgRMatrix,matrix}(x, y) - -\S4method{tcrossprod}{dgRMatrix,float32}(x, y) - -\S4method{\%*\%}{matrix,dgCMatrix}(x, y) - -\S4method{\%*\%}{float32,dgCMatrix}(x, y) - -\S4method{\%*\%}{dgCMatrix,float32}(x, y) - -\S4method{crossprod}{matrix,dgCMatrix}(x, y) - -\S4method{crossprod}{float32,dgCMatrix}(x, y) -} -\arguments{ -\item{x, y}{dense \code{matrix} and sparse -\code{Matrix::RsparseMatrix} / \code{Matrix::CsparseMatrix} matrices.} -} -\value{ -A dense \code{matrix} -} -\description{ -Multithreaded \code{\%*\%}, \code{crossprod}, \code{tcrossprod} -for sparse-dense matrix multiplication -} -\details{ -Accelerates sparse-dense matrix multiplications using openmp. Applicable to the following pairs: -(\code{dgRMatrix}, \code{matrix}), (\code{matrix}, \code{dgRMatrix}), -(\code{dgCMatrix}, \code{matrix}), (\code{matrix}, \code{dgCMatrix}) combinations -} -\examples{ -library(Matrix) -data("movielens100k") -k = 10 -nc = ncol(movielens100k) -nr = nrow(movielens100k) -x_nc = matrix(rep(1:k, nc), nrow = nc) -x_nr = t(matrix(rep(1:k, nr), nrow = nr)) -csc = movielens100k -csr = as(movielens100k, "RsparseMatrix") -dense = as.matrix(movielens100k) -identical(csr \%*\% x_nc, dense \%*\% x_nc) -identical(x_nr \%*\% csc, x_nr \%*\% dense) -} diff --git a/man/slice.Rd b/man/slice.Rd deleted file mode 100644 index 0da4289..0000000 --- a/man/slice.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/methods.R -\name{slice} -\alias{slice} -\alias{[,RsparseMatrix,index,index,logical-method} -\alias{[,RsparseMatrix,missing,index,logical-method} -\alias{[,RsparseMatrix,index,missing,logical-method} -\alias{[,RsparseMatrix,missing,missing,logical-method} -\title{CSR Matrices Slicing} -\usage{ -\S4method{[}{RsparseMatrix,index,index,logical}(x, i, j, drop = TRUE) - -\S4method{[}{RsparseMatrix,missing,index,logical}(x, i, j, drop = TRUE) - -\S4method{[}{RsparseMatrix,index,missing,logical}(x, i, j, drop = TRUE) - -\S4method{[}{RsparseMatrix,missing,missing,logical}(x, i, j, drop = TRUE) -} -\arguments{ -\item{x}{input \code{RsparseMatrix}} - -\item{i}{row indices to subset} - -\item{j}{column indices to subset} - -\item{drop}{whether to simplify 1d matrix to a vector} -} -\value{ -A \code{RsparseMatrix} -} -\description{ -natively slice CSR matrices without converting them to triplet/CSC -} -\examples{ -library(Matrix) -library(rsparse) -# dgCMatrix - CSC -m = rsparsematrix(20, 20, 0.1) -# make CSR -m = as(m, "RsparseMatrix") -inherits(m[1:2, ], "RsparseMatrix") -inherits(m[1:2, 3:4], "RsparseMatrix") -} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index ae09a0a..9376905 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -180,87 +180,6 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } -// check_is_seq -bool check_is_seq(Rcpp::IntegerVector indices); -RcppExport SEXP _rsparse_check_is_seq(SEXP indicesSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indices(indicesSEXP); - rcpp_result_gen = Rcpp::wrap(check_is_seq(indices)); - return rcpp_result_gen; -END_RCPP -} -// copy_csr_rows -Rcpp::List copy_csr_rows(Rcpp::IntegerVector indptr, Rcpp::IntegerVector indices, Rcpp::NumericVector values, Rcpp::IntegerVector rows_take); -RcppExport SEXP _rsparse_copy_csr_rows(SEXP indptrSEXP, SEXP indicesSEXP, SEXP valuesSEXP, SEXP rows_takeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indptr(indptrSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indices(indicesSEXP); - Rcpp::traits::input_parameter< Rcpp::NumericVector >::type values(valuesSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type rows_take(rows_takeSEXP); - rcpp_result_gen = Rcpp::wrap(copy_csr_rows(indptr, indices, values, rows_take)); - return rcpp_result_gen; -END_RCPP -} -// copy_csr_rows_col_seq -Rcpp::List copy_csr_rows_col_seq(Rcpp::IntegerVector indptr, Rcpp::IntegerVector indices, Rcpp::NumericVector values, Rcpp::IntegerVector rows_take, Rcpp::IntegerVector cols_take); -RcppExport SEXP _rsparse_copy_csr_rows_col_seq(SEXP indptrSEXP, SEXP indicesSEXP, SEXP valuesSEXP, SEXP rows_takeSEXP, SEXP cols_takeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indptr(indptrSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indices(indicesSEXP); - Rcpp::traits::input_parameter< Rcpp::NumericVector >::type values(valuesSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type rows_take(rows_takeSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type cols_take(cols_takeSEXP); - rcpp_result_gen = Rcpp::wrap(copy_csr_rows_col_seq(indptr, indices, values, rows_take, cols_take)); - return rcpp_result_gen; -END_RCPP -} -// copy_csr_arbitrary -Rcpp::List copy_csr_arbitrary(Rcpp::IntegerVector indptr, Rcpp::IntegerVector indices, Rcpp::NumericVector values, Rcpp::IntegerVector rows_take, Rcpp::IntegerVector cols_take); -RcppExport SEXP _rsparse_copy_csr_arbitrary(SEXP indptrSEXP, SEXP indicesSEXP, SEXP valuesSEXP, SEXP rows_takeSEXP, SEXP cols_takeSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indptr(indptrSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type indices(indicesSEXP); - Rcpp::traits::input_parameter< Rcpp::NumericVector >::type values(valuesSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type rows_take(rows_takeSEXP); - Rcpp::traits::input_parameter< Rcpp::IntegerVector >::type cols_take(cols_takeSEXP); - rcpp_result_gen = Rcpp::wrap(copy_csr_arbitrary(indptr, indices, values, rows_take, cols_take)); - return rcpp_result_gen; -END_RCPP -} -// csr_dense_tcrossprod -Rcpp::NumericMatrix csr_dense_tcrossprod(const Rcpp::S4& x_csr_r, const arma::Mat& y_transposed, int num_threads); -RcppExport SEXP _rsparse_csr_dense_tcrossprod(SEXP x_csr_rSEXP, SEXP y_transposedSEXP, SEXP num_threadsSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::S4& >::type x_csr_r(x_csr_rSEXP); - Rcpp::traits::input_parameter< const arma::Mat& >::type y_transposed(y_transposedSEXP); - Rcpp::traits::input_parameter< int >::type num_threads(num_threadsSEXP); - rcpp_result_gen = Rcpp::wrap(csr_dense_tcrossprod(x_csr_r, y_transposed, num_threads)); - return rcpp_result_gen; -END_RCPP -} -// dense_csc_prod -Rcpp::NumericMatrix dense_csc_prod(const Rcpp::NumericMatrix& x_r, const Rcpp::S4& y_csc_r, int num_threads); -RcppExport SEXP _rsparse_dense_csc_prod(SEXP x_rSEXP, SEXP y_csc_rSEXP, SEXP num_threadsSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< const Rcpp::NumericMatrix& >::type x_r(x_rSEXP); - Rcpp::traits::input_parameter< const Rcpp::S4& >::type y_csc_r(y_csc_rSEXP); - Rcpp::traits::input_parameter< int >::type num_threads(num_threadsSEXP); - rcpp_result_gen = Rcpp::wrap(dense_csc_prod(x_r, y_csc_r, num_threads)); - return rcpp_result_gen; -END_RCPP -} // top_product Rcpp::IntegerMatrix top_product(const arma::mat& x, const arma::mat& y, unsigned k, unsigned n_threads, const Rcpp::S4& not_recommend_r, const Rcpp::IntegerVector& exclude, const double glob_mean); RcppExport SEXP _rsparse_top_product(SEXP xSEXP, SEXP ySEXP, SEXP kSEXP, SEXP n_threadsSEXP, SEXP not_recommend_rSEXP, SEXP excludeSEXP, SEXP glob_meanSEXP) { @@ -545,12 +464,6 @@ static const R_CallMethodDef CallEntries[] = { {"_rsparse_fm_partial_fit", (DL_FUNC) &_rsparse_fm_partial_fit, 6}, {"_rsparse_is_invalid_ptr", (DL_FUNC) &_rsparse_is_invalid_ptr, 1}, {"_rsparse_arma_kmeans", (DL_FUNC) &_rsparse_arma_kmeans, 6}, - {"_rsparse_check_is_seq", (DL_FUNC) &_rsparse_check_is_seq, 1}, - {"_rsparse_copy_csr_rows", (DL_FUNC) &_rsparse_copy_csr_rows, 4}, - {"_rsparse_copy_csr_rows_col_seq", (DL_FUNC) &_rsparse_copy_csr_rows_col_seq, 5}, - {"_rsparse_copy_csr_arbitrary", (DL_FUNC) &_rsparse_copy_csr_arbitrary, 5}, - {"_rsparse_csr_dense_tcrossprod", (DL_FUNC) &_rsparse_csr_dense_tcrossprod, 3}, - {"_rsparse_dense_csc_prod", (DL_FUNC) &_rsparse_dense_csc_prod, 3}, {"_rsparse_top_product", (DL_FUNC) &_rsparse_top_product, 7}, {"_rsparse_c_nnls_double", (DL_FUNC) &_rsparse_c_nnls_double, 4}, {"_rsparse_rankmf_solver_double", (DL_FUNC) &_rsparse_rankmf_solver_double, 22}, diff --git a/src/matrix_csr.cpp b/src/matrix_csr.cpp deleted file mode 100644 index cfd758d..0000000 --- a/src/matrix_csr.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include -#include -#include "rsparse.h" -#include -#include -// [[Rcpp::plugins(unwindProtect)]] - -// [[Rcpp::export]] -bool check_is_seq(Rcpp::IntegerVector indices) { - if (indices.size() < 2) return true; - int n_els = indices.size(); - if ((indices[n_els - 1] - indices[0]) != n_els - 1) return false; - for (int ix = 1; ix < n_els; ix++) { - if (indices[ix] != indices[ix - 1] + 1) return false; - } - return true; -} - -// [[Rcpp::export]] -Rcpp::List copy_csr_rows(Rcpp::IntegerVector indptr, Rcpp::IntegerVector indices, - Rcpp::NumericVector values, Rcpp::IntegerVector rows_take) { - size_t total_size = 0; - for (const int row : rows_take) total_size += indptr[row + 1] - indptr[row]; - if (total_size == 0) { - return Rcpp::List::create(Rcpp::_["indptr"] = Rcpp::IntegerVector(), - Rcpp::_["indices"] = Rcpp::IntegerVector(), - Rcpp::_["values"] = Rcpp::NumericVector()); - } - Rcpp::IntegerVector new_indptr = Rcpp::IntegerVector(rows_take.size() + 1); - Rcpp::IntegerVector new_indices = Rcpp::IntegerVector(total_size); - Rcpp::NumericVector new_values = Rcpp::NumericVector(total_size); - - size_t n_copy; - int row; - int* ptr_indptr = indptr.begin(); - int* ptr_indices = indices.begin(); - double* prt_values = values.begin(); - int* ptr_new_indptr = new_indptr.begin(); - int* ptr_new_indices = new_indices.begin(); - double* ptr_new_values = new_values.begin(); - - size_t curr = 0; - for (int ix = 0; ix < (int)rows_take.size(); ix++) { - row = rows_take[ix]; - n_copy = ptr_indptr[row + 1] - ptr_indptr[row]; - ptr_new_indptr[ix + 1] = ptr_new_indptr[ix] + n_copy; - if (n_copy) { - std::copy(ptr_indices + ptr_indptr[row], ptr_indices + ptr_indptr[row + 1], - ptr_new_indices + curr); - std::copy(prt_values + ptr_indptr[row], prt_values + ptr_indptr[row + 1], - ptr_new_values + curr); - } - curr += n_copy; - } - return Rcpp::List::create(Rcpp::_["indptr"] = new_indptr, - Rcpp::_["indices"] = new_indices, - Rcpp::_["values"] = new_values); -} - -// [[Rcpp::export]] -Rcpp::List copy_csr_rows_col_seq(Rcpp::IntegerVector indptr, Rcpp::IntegerVector indices, - Rcpp::NumericVector values, - Rcpp::IntegerVector rows_take, - Rcpp::IntegerVector cols_take) { - int min_col = *std::min_element(cols_take.begin(), cols_take.end()); - int max_col = *std::max_element(cols_take.begin(), cols_take.end()); - Rcpp::IntegerVector new_indptr(rows_take.size() + 1); - - int* ptr_indptr = indptr.begin(); - int* ptr_indices = indices.begin(); - double* ptr_values = values.begin(); - int* ptr_new_indptr = new_indptr.begin(); - - size_t total_size = 0; - for (int row = 0; row < (int)rows_take.size(); row++) { - for (int ix = ptr_indptr[rows_take[row]]; ix < ptr_indptr[rows_take[row] + 1]; ix++) { - total_size += (ptr_indices[ix] >= min_col) && (ptr_indices[ix] <= max_col); - } - ptr_new_indptr[row + 1] = total_size; - } - - if (total_size == 0) { - return Rcpp::List::create(Rcpp::_["indptr"] = new_indptr, - Rcpp::_["indices"] = Rcpp::IntegerVector(), - Rcpp::_["values"] = Rcpp::NumericVector()); - } - - Rcpp::IntegerVector new_indices = Rcpp::IntegerVector(total_size); - Rcpp::NumericVector new_values = Rcpp::NumericVector(total_size); - int* ptr_new_indices = new_indices.begin(); - double* ptr_new_values = new_values.begin(); - - int curr = 0; - for (int row = 0; row < (int)rows_take.size(); row++) { - for (int ix = ptr_indptr[rows_take[row]]; ix < ptr_indptr[rows_take[row] + 1]; ix++) { - if ((ptr_indices[ix] >= min_col) && (ptr_indices[ix] <= max_col)) { - ptr_new_indices[curr] = ptr_indices[ix] - min_col; - ptr_new_values[curr] = ptr_values[ix]; - curr++; - } - } - } - return Rcpp::List::create(Rcpp::_["indptr"] = new_indptr, - Rcpp::_["indices"] = new_indices, - Rcpp::_["values"] = new_values); -} - -struct VectorConstructorArgs { - bool as_integer = false; - bool from_cpp_vec = false; - size_t size = 0; - std::vector *int_vec_from = NULL; - std::vector *num_vec_from = NULL; -}; - -SEXP SafeRcppVector(void *args_) -{ - VectorConstructorArgs *args = (VectorConstructorArgs*)args_; - if (args->as_integer) { - if (args->from_cpp_vec) { - return Rcpp::IntegerVector(args->int_vec_from->begin(), args->int_vec_from->end()); - } - - else { - return Rcpp::IntegerVector(args->size); - } - } - - else { - if (args->from_cpp_vec) { - return Rcpp::NumericVector(args->num_vec_from->begin(), args->num_vec_from->end()); - } - - else { - return Rcpp::NumericVector(args->size); - } - } -} - -// [[Rcpp::export]] -Rcpp::List copy_csr_arbitrary(Rcpp::IntegerVector indptr, Rcpp::IntegerVector indices, - Rcpp::NumericVector values, Rcpp::IntegerVector rows_take, - Rcpp::IntegerVector cols_take) { - std::unordered_map new_mapping; - for (int col = 0; col < (int)cols_take.size(); col++) new_mapping[cols_take[col]] = col; - std::unordered_map n_repeats; - for (auto el : cols_take) n_repeats[el]++; - bool has_duplicates = false; - for (auto& el : n_repeats) { - if (el.second > 1) { - has_duplicates = true; - break; - } - } - std::unordered_map> indices_rep; - if (has_duplicates) { - for (int col = 0; col < (int)cols_take.size(); col++) { - if (n_repeats[cols_take[col]] > 1) { - indices_rep[cols_take[col]].push_back(col); - } - } - } - - bool cols_are_sorted = true; - for (int ix = 1; ix < (int)cols_take.size(); ix++) { - if (cols_take[ix] < cols_take[ix - 1]) { - cols_are_sorted = false; - break; - } - } - - Rcpp::IntegerVector new_indptr; - VectorConstructorArgs args; - args.as_integer = true; args.from_cpp_vec = false; args.size = rows_take.size() + 1; - new_indptr = Rcpp::unwindProtect(SafeRcppVector, (void*)&args); - const char *oom_err_msg = "Could not allocate sufficient memory.\n"; - if (!new_indptr.size()) - Rcpp::stop(oom_err_msg); - std::vector new_indices; - std::vector new_values; - - std::vector argsort_cols; - std::vector temp_int; - std::vector temp_double; - - int size_this = 0; - int row = 0; - for (int row_ix = 0; row_ix < (int)rows_take.size(); row_ix++) { - row = rows_take[row_ix]; - for (int ix = indptr[row]; ix < indptr[row + 1]; ix++) { - auto match = new_mapping.find(indices[ix]); - if (match != new_mapping.end()) { - if (has_duplicates && n_repeats[indices[ix]] > 1) { - for (const auto& el : indices_rep[indices[ix]]) { - new_indices.push_back(el); - new_values.push_back(values[ix]); - } - } else { - new_indices.push_back(match->second); - new_values.push_back(values[ix]); - } - } - } - new_indptr[row_ix + 1] = new_indices.size(); - if (!cols_are_sorted && new_indptr[row_ix + 1] > new_indptr[row_ix]) { - size_this = new_indptr[row_ix + 1] - new_indptr[row_ix]; - if (argsort_cols.size() < (size_t)size_this) { - argsort_cols.resize(size_this); - temp_int.resize(size_this); - temp_double.resize(size_this); - } - std::iota(argsort_cols.begin(), argsort_cols.begin() + size_this, - new_indptr[row_ix]); - std::sort(argsort_cols.begin(), argsort_cols.begin() + size_this, - [&new_indices](const int a, const int b) { - return new_indices[a] < new_indices[b]; - }); - for (int col = 0; col < size_this; col++) { - temp_int[col] = new_indices[argsort_cols[col]]; - temp_double[col] = new_values[argsort_cols[col]]; - } - std::copy(temp_int.begin(), temp_int.begin() + size_this, - new_indices.begin() + new_indptr[row_ix]); - std::copy(temp_double.begin(), temp_double.begin() + size_this, - new_values.begin() + new_indptr[row_ix]); - } - } - - Rcpp::List out; - out["indptr"] = new_indptr; - args.as_integer = true; args.from_cpp_vec = true; args.int_vec_from = &new_indices; - out["indices"] = Rcpp::unwindProtect(SafeRcppVector, (void*)&args); - if (Rf_xlength(out["indices"]) != new_indices.size()) - Rcpp::stop(oom_err_msg); - new_indices.clear(); - args.as_integer = false; args.from_cpp_vec = true; args.num_vec_from = &new_values; - out["values"] = Rcpp::unwindProtect(SafeRcppVector, (void*)&args); - if (Rf_xlength(out["values"]) != new_values.size()) - Rcpp::stop(oom_err_msg); - return out; -} diff --git a/src/matrix_product.cpp b/src/matrix_product.cpp deleted file mode 100644 index d76f96e..0000000 --- a/src/matrix_product.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "rsparse.h" - -// implements x * t(y) multiplication where x = sparse CSR matrix and y = dense matrix -// it doesn't implement just x * y because it will be slow due to the fact that -// our main pattern for access elements in y is by row which is cache inefficient and -// problematic to vectorize because matrices are stored by column so essentially it -// implements R's `tcrossprod()` -// FIXME for now works with just double but can be templated in future - -// [[Rcpp::export]] -Rcpp::NumericMatrix csr_dense_tcrossprod(const Rcpp::S4& x_csr_r, - const arma::Mat& y_transposed, - int num_threads = 1) { - const dMappedCSR x_csr = extract_mapped_csr(x_csr_r); - Rcpp::NumericMatrix res(x_csr.n_rows, - y_transposed.n_rows); // y_transposed.n_rows = y_dense.n_cols - arma::dmat res_arma_map = arma::dmat(res.begin(), res.nrow(), res.ncol(), false, false); -#ifdef _OPENMP -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, GRAIN_SIZE) -#endif - for (arma::uword i = 0; i < x_csr.n_rows; i++) { - const arma::uword p1 = x_csr.row_ptrs[i]; - const arma::uword p2 = x_csr.row_ptrs[i + 1]; - const arma::uvec idx = arma::uvec(&x_csr.col_indices[p1], p2 - p1, false, true); - const arma::colvec x_csr_row = arma::colvec(&x_csr.values[p1], p2 - p1, false, false); - res_arma_map.row(i) = (y_transposed.cols(idx) * x_csr_row).t(); - } - return res; -} - -// [[Rcpp::export]] -Rcpp::NumericMatrix dense_csc_prod(const Rcpp::NumericMatrix& x_r, - const Rcpp::S4& y_csc_r, int num_threads = 1) { - const arma::dmat x = arma::dmat((double*)&x_r[0], x_r.nrow(), x_r.ncol(), false, false); - const dMappedCSC y_csc = extract_mapped_csc(y_csc_r); - Rcpp::NumericMatrix res(x.n_rows, y_csc.n_cols); - arma::dmat res_arma_map = arma::dmat(res.begin(), res.nrow(), res.ncol(), false, false); -#ifdef _OPENMP -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, GRAIN_SIZE) -#endif - for (arma::uword i = 0; i < y_csc.n_cols; i++) { - const arma::uword p1 = y_csc.col_ptrs[i]; - const arma::uword p2 = y_csc.col_ptrs[i + 1]; - const arma::uvec idx = arma::uvec(&y_csc.row_indices[p1], p2 - p1, false, true); - const arma::colvec y_csc_col = arma::colvec(&y_csc.values[p1], p2 - p1, false, false); - res_arma_map.col(i) = x.cols(idx) * y_csc_col; - } - return res; -} diff --git a/tests/testthat/test-conversions.R b/tests/testthat/test-conversions.R deleted file mode 100644 index ceba183..0000000 --- a/tests/testthat/test-conversions.R +++ /dev/null @@ -1,162 +0,0 @@ -library("testthat") -library("rsparse") -context("Conversion functions") - -m.base = matrix(1:10, nrow=5) -m.coo = as(m.base, "TsparseMatrix") -m.coo.b = as(m.coo, "nsparseMatrix") -m.csr = as(m.base, "RsparseMatrix") -m.csr.b = as(m.csr, "nsparseMatrix") -m.csc = as(m.base, "CsparseMatrix") -m.csc.b = as(m.csc, "nsparseMatrix") -m.f32 = float::fl(m.base) -df = as.data.frame(m.base) -dt = data.table::as.data.table(df) -v.num = as.numeric(1:3) -v.int = as.integer(v.num) -v.f32 = float::fl(v.num) -v.sp = as(v.num, "dsparseVector") - -test_that("Conversion to CSR", { - expect_true(inherits(as.csr.matrix(m.base), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.coo), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.coo.b), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.csr), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.csr.b), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.csc), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.csc.b), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(m.f32), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(df), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(dt), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(v.num), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(v.int), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(v.f32), "dgRMatrix")) - expect_true(inherits(as.csr.matrix(v.sp), "dgRMatrix")) - - expect_true(inherits(as.csr.matrix(m.base, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.coo, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.coo.b, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.csr, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.csr.b, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.csc, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.csc.b, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(m.f32, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(df, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(dt, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(v.num, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(v.int, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(v.f32, binary=TRUE), "ngRMatrix")) - expect_true(inherits(as.csr.matrix(v.sp, binary=TRUE), "ngRMatrix")) - - expect_equal(nrow(as.csr.matrix(v.num)), 1L) - expect_equal(nrow(as.csr.matrix(v.int)), 1L) - expect_equal(nrow(as.csr.matrix(v.f32)), 1L) - expect_equal(nrow(as.csr.matrix(v.sp)), 1L) - - expect_equal(dim(as.csr.matrix(m.base)), dim(m.base)) - expect_equal(dim(as.csr.matrix(m.coo)), dim(m.coo)) - expect_equal(dim(as.csr.matrix(m.coo.b)), dim(m.coo.b)) - expect_equal(dim(as.csr.matrix(m.csr)), dim(m.csr)) - expect_equal(dim(as.csr.matrix(m.csr.b)), dim(m.csr.b)) - expect_equal(dim(as.csr.matrix(m.csc)), dim(m.csc)) - expect_equal(dim(as.csr.matrix(m.csc.b)), dim(m.csc.b)) - expect_equal(dim(as.csr.matrix(m.f32)), dim(m.f32)) - expect_equal(dim(as.csr.matrix(df)), dim(df)) - expect_equal(dim(as.csr.matrix(dt)), dim(dt)) -}) - -test_that("Conversion to CSC", { - expect_true(inherits(as.csc.matrix(m.base), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.coo), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.coo.b), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.csr), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.csr.b), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.csc), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.csc.b), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(m.f32), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(df), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(dt), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(v.num), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(v.int), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(v.f32), "dgCMatrix")) - expect_true(inherits(as.csc.matrix(v.sp), "dgCMatrix")) - - expect_true(inherits(as.csc.matrix(m.base, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.coo, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.coo.b, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.csr, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.csr.b, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.csc, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.csc.b, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(m.f32, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(df, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(dt, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(v.num, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(v.int, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(v.f32, binary=TRUE), "ngCMatrix")) - expect_true(inherits(as.csc.matrix(v.sp, binary=TRUE), "ngCMatrix")) - - expect_equal(ncol(as.csc.matrix(v.num)), 1L) - expect_equal(ncol(as.csc.matrix(v.int)), 1L) - expect_equal(ncol(as.csc.matrix(v.f32)), 1L) - expect_equal(ncol(as.csc.matrix(v.sp)), 1L) - - expect_equal(dim(as.csc.matrix(m.base)), dim(m.base)) - expect_equal(dim(as.csc.matrix(m.coo)), dim(m.coo)) - expect_equal(dim(as.csc.matrix(m.coo.b)), dim(m.coo.b)) - expect_equal(dim(as.csc.matrix(m.csr)), dim(m.csr)) - expect_equal(dim(as.csc.matrix(m.csr.b)), dim(m.csr.b)) - expect_equal(dim(as.csc.matrix(m.csc)), dim(m.csc)) - expect_equal(dim(as.csc.matrix(m.csc.b)), dim(m.csc.b)) - expect_equal(dim(as.csc.matrix(m.f32)), dim(m.f32)) - expect_equal(dim(as.csc.matrix(df)), dim(df)) - expect_equal(dim(as.csc.matrix(dt)), dim(dt)) -}) - -test_that("Conversion to COO", { - expect_true(inherits(as.coo.matrix(m.base), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.coo), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.coo.b), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.csr), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.csr.b), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.csc), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.csc.b), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(m.f32), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(df), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(dt), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(v.num), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(v.int), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(v.f32), "dgTMatrix")) - expect_true(inherits(as.coo.matrix(v.sp), "dgTMatrix")) - - expect_true(inherits(as.coo.matrix(m.base, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.coo, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.coo.b, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.csr, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.csr.b, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.csc, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.csc.b, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(m.f32, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(df, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(dt, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(v.num, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(v.int, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(v.f32, binary=TRUE), "ngTMatrix")) - expect_true(inherits(as.coo.matrix(v.sp, binary=TRUE), "ngTMatrix")) - - expect_equal(nrow(as.coo.matrix(v.num)), 1L) - expect_equal(nrow(as.coo.matrix(v.int)), 1L) - expect_equal(nrow(as.coo.matrix(v.f32)), 1L) - expect_equal(nrow(as.coo.matrix(v.sp)), 1L) - - expect_equal(dim(as.coo.matrix(m.base)), dim(m.base)) - expect_equal(dim(as.coo.matrix(m.coo)), dim(m.coo)) - expect_equal(dim(as.coo.matrix(m.coo.b)), dim(m.coo.b)) - expect_equal(dim(as.coo.matrix(m.csr)), dim(m.csr)) - expect_equal(dim(as.coo.matrix(m.csr.b)), dim(m.csr.b)) - expect_equal(dim(as.coo.matrix(m.csc)), dim(m.csc)) - expect_equal(dim(as.coo.matrix(m.csc.b)), dim(m.csc.b)) - expect_equal(dim(as.coo.matrix(m.f32)), dim(m.f32)) - expect_equal(dim(as.coo.matrix(df)), dim(df)) - expect_equal(dim(as.coo.matrix(dt)), dim(dt)) -}) diff --git a/tests/testthat/test-csr-subset.R b/tests/testthat/test-csr-subset.R deleted file mode 100644 index 9779b5f..0000000 --- a/tests/testthat/test-csr-subset.R +++ /dev/null @@ -1,114 +0,0 @@ -library("testthat") -library("rsparse") -context("RsparseMatrix subsets") - -nc = 500L -nr = 1000L -set.seed(123) -m = Matrix::rsparsematrix(nrow = nr, ncol = nc, density = 0.1) -colnames(m) = as.character(seq_len(nc)) -rownames(m) = as.character(seq_len(nr)) -m = as(m, "RsparseMatrix") -m_csc = as(m, "CsparseMatrix") -m_base = as.matrix(m) - -test_that("RsparseMatrix subset cols and rows", { - expect_equal(m, m[, ]) - expect_equal(m, m[]) - expect_equal(m, m[, , ]) - expect_equal(m[1:10, 1:100], as(m_csc[1:10, 1:100], "RsparseMatrix")) - expect_equal(m[as.character(1:10), 1:100], as(m_csc[as.character(1:10), 1:100], "RsparseMatrix")) - expect_equal(m["10", "20", drop = FALSE], as(m_csc["10", "20", drop = FALSE], "RsparseMatrix")) - expect_equal(m["10", "20", drop = TRUE], m_csc["10", "20", drop = TRUE]) -}) - -test_that("RsparseMatrix subset non sequential", { - expect_equal(m, m[, ]) - expect_equal(m, m[]) - expect_equal(m, m[, , ]) - expect_equal(m[c(5,2,1,7,4), c(5,2,1,7,4,10,100)], - as(m_base[c(5,2,1,7,4), c(5,2,1,7,4,10,100)], "RsparseMatrix")) - expect_equal(m[as.character(c(5,2,1,7,4)), as.character(c(5,2,1,7,4,10,100))], - as(m_base[c(5,2,1,7,4), c(5,2,1,7,4,10,100)], "RsparseMatrix")) -}) - -test_that("RsparseMatrix subset repeated", { - expect_equal(m, m[, ]) - expect_equal(m, m[]) - expect_equal(m, m[, , ]) - expect_equal(m[c(2,2,2,1,1,3), c(3,3,4,4,1,1,1)], - as(m_base[c(2,2,2,1,1,3), c(3,3,4,4,1,1,1)], "RsparseMatrix")) - expect_equal(m[as.character(c(2,2,2,1,1,3)), as.character(c(3,3,4,4,1,1,1))], - as(m_base[c(2,2,2,1,1,3), c(3,3,4,4,1,1,1)], "RsparseMatrix")) - expect_equal(m[c(5,2,1,7,4,1,5), c(5,2,1,7,4,1,10,100,5)], - as(m_base[c(5,2,1,7,4,1,5), c(5,2,1,7,4,1,10,100,5)], "RsparseMatrix")) - expect_equal(m[as.character(c(5,2,1,7,4,1,5)), as.character( c(5,2,1,7,4,1,10,100,5))], - as(m_base[c(5,2,1,7,4,1,5), c(5,2,1,7,4,1,10,100,5)], "RsparseMatrix")) -}) - -test_that("RsparseMatrix subset empty", { - expect_equal(m[3:10, integer()], - as(m_csc[3:10, integer()], "RsparseMatrix")) - expect_equal(m[c(2,2,2,1,1,3), integer()], - as(m_csc[c(2,2,2,1,1,3), integer()], "RsparseMatrix")) - expect_equal(m[, integer()], - as(m_csc[, integer()], "RsparseMatrix")) - - expect_equal(m[character(), ], - as(m_csc[integer(), ], "RsparseMatrix")) - expect_equal(m[character(), as.character(c(3,3,4,4,1,1,1))], - as(m_csc[integer(), c(3,3,4,4,1,1,1)], "RsparseMatrix")) - expect_equal(m[character(), 3:10], - as(m_csc[integer(), 3:10], "RsparseMatrix")) - - expect_equal(m[integer(), integer()], - as(m_csc[integer(), integer()], "RsparseMatrix")) - expect_equal(m[character(), character()], - as(m_csc[character(), character()], "RsparseMatrix")) -}) - -test_that("RsparseMatrix subset cols", { - expect_true(inherits(m[, 2L], 'numeric')) - expect_true(inherits(m[, 2L, drop = FALSE], 'RsparseMatrix')) - expect_true(inherits(m[, 1L:2L], 'RsparseMatrix')) - expect_equal(rownames(m[, 2L:4L]), rownames(m)) - expect_equal(colnames(m[, 2L:4L]), as.character(2L:4L) ) - expect_equal(m[, as.character(2L:4L)], m[, 2L:4L]) - expect_error(m[, 501L]) - expect_error(m[, 500L:501L]) - expect_equal(m[, -1, drop = FALSE], as(m_csc[, -1, drop = FALSE], "RsparseMatrix")) - expect_equal(m[, -1, drop = TRUE], as(m_csc[, -1, drop = TRUE], "RsparseMatrix")) - expect_equal(m[, -10:-1 ], as(m_csc[, -10:-1 ], "RsparseMatrix")) -}) - -test_that("RsparseMatrix subset rows", { - expect_true(inherits(m[2L, ], 'numeric')) - expect_true(inherits(m[2L, , drop = FALSE], 'RsparseMatrix')) - expect_true(inherits(m[1L:2L, ], 'RsparseMatrix')) - expect_equal(colnames(m[2L:4L, ]), colnames(m)) - expect_equal(rownames(m[2L:4L, ]), as.character(2L:4L) ) - expect_equal(m[as.character(2L:4L), ], m[2L:4L, ] ) - expect_error(m[1001L, ]) - expect_error(m[900L:1001L, ]) - expect_equal(m[-1, , drop = TRUE], as(m_csc[-1, , drop = TRUE], "RsparseMatrix")) - expect_equal(m[-1, , drop = TRUE], as(m_csc[-1, , drop = TRUE], "RsparseMatrix")) - expect_equal(m[-10:-1, ], as(m_csc[-10:-1, ], "RsparseMatrix")) -}) - -test_that("RsparseMatrix subset with boolean", { - long_vec_rows = rep(FALSE, nrow(m)) - long_vec_cols = rep(FALSE, ncol(m)) - long_vec_rows[1L] = TRUE - long_vec_rows[2L] = TRUE - long_vec_cols[1L] = TRUE - long_vec_cols[2L] = TRUE - expect_equal(m[long_vec_rows, ], as(m_csc[long_vec_rows, ], "RsparseMatrix")) - expect_equal(m[, long_vec_cols], as(m_csc[, long_vec_cols], "RsparseMatrix")) - expect_equal(m[c(TRUE, FALSE, TRUE), ], as(m_csc[c(TRUE, FALSE, TRUE), ], "RsparseMatrix")) - expect_equal(m[, c(TRUE, FALSE, TRUE)], as(m_csc[, c(TRUE, FALSE, TRUE)], "RsparseMatrix")) - - expect_equal(m[FALSE, ], as(m_csc[FALSE, ], "RsparseMatrix")) - expect_equal(m[, FALSE], as(m_csc[, FALSE], "RsparseMatrix")) - expect_equal(m[FALSE, FALSE], as(m_csc[FALSE, FALSE], "RsparseMatrix")) - expect_equal(m[TRUE, TRUE], as(m_csc[TRUE, TRUE], "RsparseMatrix")) -}) diff --git a/tests/testthat/test-ftrl.R b/tests/testthat/test-ftrl.R index 769e48f..631e48b 100644 --- a/tests/testthat/test-ftrl.R +++ b/tests/testthat/test-ftrl.R @@ -11,7 +11,7 @@ y = sample(c(0, 1), N_SMPL, TRUE) x = sample(c(-1, 1), NNZ, TRUE) odd = seq(1, 99, 2) x[i %in% which(y == 1) & j %in% odd] = 1 -m = sparseMatrix(i = i, j = j, x = x, dims = c(N_SMPL, N_FEAT), giveCsparse = FALSE) +m = sparseMatrix(i = i, j = j, x = x, dims = c(N_SMPL, N_FEAT), repr="T") x = as(m, "RsparseMatrix") diff --git a/tests/testthat/test-matmul.R b/tests/testthat/test-matmul.R deleted file mode 100644 index ba36c5c..0000000 --- a/tests/testthat/test-matmul.R +++ /dev/null @@ -1,23 +0,0 @@ -context("matmul") - -k = 10 -nc = ncol(movielens100k) -nr = nrow(movielens100k) -x_nc = matrix(rep(1:10, nc), nrow = nc) -x_nr = t(matrix(rep(1:10, nr), nrow = nr)) - -csc = movielens100k -csr = as(movielens100k, "RsparseMatrix") -dense = as.matrix(movielens100k) - -test_that("matmul CSR", { - expect_equal(csr %*% x_nc, dense %*% x_nc) - expect_equal(tcrossprod(csr, t(x_nc)), tcrossprod(dense, t(x_nc))) - expect_error(csr %*% cbind(x_nc, rep(1, ncrow(x_nc)))) -}) - -test_that("matmul CSC", { - expect_equal(x_nr %*% csc, x_nr %*% dense) - expect_equal(crossprod(t(x_nr), csc), crossprod(t(x_nr), csc)) - expect_error(cbind(x_nr, rep(1, nrow(x_nr))) %*% csc) -})