From a9d2f83d1234043b270572dd5b0c65f03797f01f Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Wed, 5 Jan 2022 15:51:27 +0000 Subject: [PATCH 1/6] add c_ListofMatrixRows * actually only 3x faster than naive lapply and 2x faster than for loop --- NAMESPACE | 1 + R/RcppExports.R | 22 ++++++++++++++++++ man/c_ListofMatrixRows.Rd | 32 ++++++++++++++++++++++++++ src/RcppExports.cpp | 12 ++++++++++ src/matrix.cpp | 44 ++++++++++++++++++++++++++++++++++++ tests/testthat/test-matrix.R | 11 +++++++++ 6 files changed, 122 insertions(+) create mode 100644 man/c_ListofMatrixRows.Rd create mode 100644 src/matrix.cpp create mode 100644 tests/testthat/test-matrix.R diff --git a/NAMESPACE b/NAMESPACE index dc7c100..d8be76d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(c_EdgeListFromSegList) +export(c_ListofMatrixRows) export(c_listlengths) export(c_seglengths) export(c_topntail) diff --git a/R/RcppExports.R b/R/RcppExports.R index 6620c46..f9ce48a 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -20,6 +20,28 @@ c_total_cable <- function(sl, x, y, z) { .Call(`_natcpp_c_total_cable`, sl, x, y, z) } +#' Convert a matrix into list of row vectors +#' +#' @details Typically this will be for 3D coordinates but there are no limits +#' on row length. +#' @param object An integer or numeric matrix of N rows and M columns +#' @return a list containing N integer or numeric vectors of length M +#' @export +#' @examples +#' \dontrun{ +#' library(nat) +#' xyz=xyzmatrix(Cell07PNs) +#' mat2list = function(m) { +#' um=unname(m) +#' lapply(1:nrow(um), function(i) um[i,]) +#' } +#' bench::mark(rcpp=c_ListofMatrixRows(xyz), r=mat2list(xyz)) +#' } +#' @export +c_ListofMatrixRows <- function(object) { + .Call(`_natcpp_c_ListofMatrixRows`, object) +} + #' A simple function to compute the lengths of the elements of an R list #' #' @details This is equivalent to the \code{base::lengths} however it it much diff --git a/man/c_ListofMatrixRows.Rd b/man/c_ListofMatrixRows.Rd new file mode 100644 index 0000000..2f51b55 --- /dev/null +++ b/man/c_ListofMatrixRows.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{c_ListofMatrixRows} +\alias{c_ListofMatrixRows} +\title{Convert a matrix into list of row vectors} +\usage{ +c_ListofMatrixRows(object) +} +\arguments{ +\item{object}{An integer or numeric matrix of N rows and M columns} +} +\value{ +a list containing N integer or numeric vectors of length M +} +\description{ +Convert a matrix into list of row vectors +} +\details{ +Typically this will be for 3D coordinates but there are no limits + on row length. +} +\examples{ +\dontrun{ +library(nat) +xyz=xyzmatrix(Cell07PNs) +mat2list = function(m) { +um=unname(m) +lapply(1:nrow(um), function(i) um[i,]) +} +bench::mark(rcpp=c_ListofMatrixRows(xyz), r=mat2list(xyz)) +} +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 666003e..aed385b 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -38,6 +38,17 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// c_ListofMatrixRows +List c_ListofMatrixRows(const SEXP& object); +RcppExport SEXP _natcpp_c_ListofMatrixRows(SEXP objectSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const SEXP& >::type object(objectSEXP); + rcpp_result_gen = Rcpp::wrap(c_ListofMatrixRows(object)); + return rcpp_result_gen; +END_RCPP +} // c_listlengths IntegerVector c_listlengths(const List& L); RcppExport SEXP _natcpp_c_listlengths(SEXP LSEXP) { @@ -86,6 +97,7 @@ END_RCPP static const R_CallMethodDef CallEntries[] = { {"_natcpp_c_seglengths", (DL_FUNC) &_natcpp_c_seglengths, 4}, {"_natcpp_c_total_cable", (DL_FUNC) &_natcpp_c_total_cable, 4}, + {"_natcpp_c_ListofMatrixRows", (DL_FUNC) &_natcpp_c_ListofMatrixRows, 1}, {"_natcpp_c_listlengths", (DL_FUNC) &_natcpp_c_listlengths, 1}, {"_natcpp_c_topntail", (DL_FUNC) &_natcpp_c_topntail, 1}, {"_natcpp_c_topntail_list", (DL_FUNC) &_natcpp_c_topntail_list, 1}, diff --git a/src/matrix.cpp b/src/matrix.cpp new file mode 100644 index 0000000..a88fbfe --- /dev/null +++ b/src/matrix.cpp @@ -0,0 +1,44 @@ +#include "natcpp.h" + +template +List c_ListofMatrixRowsT(const T &m) { + List L(m.nrow()); + + for(int i=0; i<(m.nrow()); i++) { + // NumericVector v = m( 0 , _ ); + L[i]=m.row(i); + } + return L; +} + + +//' Convert a matrix into list of row vectors +//' +//' @details Typically this will be for 3D coordinates but there are no limits +//' on row length. +//' @param object An integer or numeric matrix of N rows and M columns +//' @return a list containing N integer or numeric vectors of length M +//' @export +//' @examples +//' \dontrun{ +//' library(nat) +//' xyz=xyzmatrix(Cell07PNs) +//' mat2list = function(m) { +//' um=unname(m) +//' lapply(1:nrow(um), function(i) um[i,]) +//' } +//' bench::mark(rcpp=c_ListofMatrixRows(xyz), r=mat2list(xyz)) +//' } +//' @export +// [[Rcpp::export]] +List c_ListofMatrixRows(const SEXP &object) { + if (!Rf_isMatrix(object)) + stop("Please provide a matrix!"); + + switch (TYPEOF(object)) + { + case INTSXP: return c_ListofMatrixRowsT(object); + case REALSXP: return c_ListofMatrixRowsT(object); + } + stop("Unimpemented matrix type!"); +} diff --git a/tests/testthat/test-matrix.R b/tests/testthat/test-matrix.R new file mode 100644 index 0000000..4970431 --- /dev/null +++ b/tests/testthat/test-matrix.R @@ -0,0 +1,11 @@ +test_that("multiplication works", { + m=matrix(1:6, ncol=3, byrow = T) + l=list(1:3, 4:6) + colnames(m)=c("X","Y","Z") + expect_equal(c_ListofMatrixRows(m), l) + + nm=matrix(rnorm(n = 300), ncol=3) + + expect_equal(c_ListofMatrixRows(nm), + lapply(seq_len(nrow(nm)), function(i) nm[i,])) +}) From bc0a49f64f48c3ede32c193f5d1f30ecbd04e2d3 Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Wed, 5 Jan 2022 15:51:43 +0000 Subject: [PATCH 2/6] Bump version --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 581b7a3..76a598c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: natcpp Title: Fast C++ Primitives for the 'NeuroAnatomy Toolbox' -Version: 0.1.0.9000 +Version: 0.1.1 Authors@R: person(given = "Gregory", family = "Jefferis", diff --git a/NEWS.md b/NEWS.md index 9b956ea..97303df 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # natcpp (development version) +# natcpp 0.1.1 + +* adds `c_ListofMatrixRows()` + # natcpp 0.1.0 * First version of the package with basic functions for manipulating segment From df596504e9e806f0ec0051ce0eda0ae31fecae75 Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Wed, 5 Jan 2022 16:03:18 +0000 Subject: [PATCH 3/6] Support logical and character matrices * + more tests --- R/RcppExports.R | 6 ++++-- man/c_ListofMatrixRows.Rd | 6 ++++-- src/matrix.cpp | 10 +++++++--- tests/testthat/test-matrix.R | 9 +++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index f9ce48a..d29e627 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -24,8 +24,10 @@ c_total_cable <- function(sl, x, y, z) { #' #' @details Typically this will be for 3D coordinates but there are no limits #' on row length. -#' @param object An integer or numeric matrix of N rows and M columns -#' @return a list containing N integer or numeric vectors of length M +#' @param object An integer, numeric, character or logical matrix of N rows and +#' M columns +#' @return a list containing N vectors of length M corresponding to the rows of +#' \code{object}. #' @export #' @examples #' \dontrun{ diff --git a/man/c_ListofMatrixRows.Rd b/man/c_ListofMatrixRows.Rd index 2f51b55..02a4f44 100644 --- a/man/c_ListofMatrixRows.Rd +++ b/man/c_ListofMatrixRows.Rd @@ -7,10 +7,12 @@ c_ListofMatrixRows(object) } \arguments{ -\item{object}{An integer or numeric matrix of N rows and M columns} +\item{object}{An integer, numeric, character or logical matrix of N rows and +M columns} } \value{ -a list containing N integer or numeric vectors of length M +a list containing N vectors of length M corresponding to the rows of + \code{object}. } \description{ Convert a matrix into list of row vectors diff --git a/src/matrix.cpp b/src/matrix.cpp index a88fbfe..e6cac70 100644 --- a/src/matrix.cpp +++ b/src/matrix.cpp @@ -16,8 +16,10 @@ List c_ListofMatrixRowsT(const T &m) { //' //' @details Typically this will be for 3D coordinates but there are no limits //' on row length. -//' @param object An integer or numeric matrix of N rows and M columns -//' @return a list containing N integer or numeric vectors of length M +//' @param object An integer, numeric, character or logical matrix of N rows and +//' M columns +//' @return a list containing N vectors of length M corresponding to the rows of +//' \code{object}. //' @export //' @examples //' \dontrun{ @@ -39,6 +41,8 @@ List c_ListofMatrixRows(const SEXP &object) { { case INTSXP: return c_ListofMatrixRowsT(object); case REALSXP: return c_ListofMatrixRowsT(object); + case CHARSXP: return c_ListofMatrixRowsT(object); + case LGLSXP: return c_ListofMatrixRowsT(object); } - stop("Unimpemented matrix type!"); + stop("Unimplemented matrix type!"); } diff --git a/tests/testthat/test-matrix.R b/tests/testthat/test-matrix.R index 4970431..88b6e3f 100644 --- a/tests/testthat/test-matrix.R +++ b/tests/testthat/test-matrix.R @@ -8,4 +8,13 @@ test_that("multiplication works", { expect_equal(c_ListofMatrixRows(nm), lapply(seq_len(nrow(nm)), function(i) nm[i,])) + + expect_equal(c_ListofMatrixRows(matrix(1L, nrow=2, ncol=0)), + list(integer(), integer())) + expect_equal(c_ListofMatrixRows(matrix(1L, nrow=0, ncol=2)), + list()) + expect_error(c_ListofMatrixRows(l)) + # matrix of lists + ml=matrix(rep(list(1), 4), ncol = 2) + expect_error(c_ListofMatrixRows(ml)) }) From d1a773dbfa1688f92a8bf743a55a3c12541b4d7e Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Wed, 5 Jan 2022 16:37:41 +0000 Subject: [PATCH 4/6] github: install curl requirements on linux * r-devel was broken with Configuration failed because libcurl was not found. Try installing: * deb: libcurl4-openssl-dev (Debian, Ubuntu, etc) * rpm: libcurl-devel (Fedora, CentOS, RHEL) * csw: libcurl_dev (Solaris) If libcurl is already installed, check that 'pkg-config' is in your PATH and PKG_CONFIG_PATH contains a libcurl.pc file. If pkg-config is unavailable you can set INCLUDE_DIR and LIB_DIR manually via: R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...' --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index cea7ca2..68da3f7 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -25,7 +25,7 @@ jobs: - {os: windows-latest, r: 'release'} - {os: macOS-latest, r: 'release'} - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest", http-user-agent: "R/4.1.0 (ubuntu-20.04) R (4.1.0 x86_64-pc-linux-gnu x86_64 linux-gnu) on GitHub Actions" } + # - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest", http-user-agent: "R/4.1.0 (ubuntu-20.04) R (4.1.0 x86_64-pc-linux-gnu x86_64 linux-gnu) on GitHub Actions" } env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true From feac6c15a54944207a625dd57cc3810600affd59 Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Wed, 5 Jan 2022 17:30:44 +0000 Subject: [PATCH 5/6] resinstate ubuntu-dev, actually add curl deps --- .github/workflows/R-CMD-check.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 68da3f7..95c0dd1 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -25,7 +25,7 @@ jobs: - {os: windows-latest, r: 'release'} - {os: macOS-latest, r: 'release'} - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - # - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest", http-user-agent: "R/4.1.0 (ubuntu-20.04) R (4.1.0 x86_64-pc-linux-gnu x86_64 linux-gnu) on GitHub Actions" } + - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest", http-user-agent: "R/4.1.0 (ubuntu-20.04) R (4.1.0 x86_64-pc-linux-gnu x86_64 linux-gnu) on GitHub Actions" } env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true @@ -61,7 +61,7 @@ jobs: while read -r cmd do eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') + done < <(Rscript -e 'writeLines(c(remotes::system_requirements("ubuntu", "20.04"), remotes::system_requirements("ubuntu", "20.04", package="curl"))') - name: Install dependencies run: | From 05c274b4836e75375bb2b849f1c0b9f3b292eb1d Mon Sep 17 00:00:00 2001 From: Gregory Jefferis Date: Wed, 5 Jan 2022 17:40:46 +0000 Subject: [PATCH 6/6] github missing bracket --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 95c0dd1..cb17061 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -61,7 +61,7 @@ jobs: while read -r cmd do eval sudo $cmd - done < <(Rscript -e 'writeLines(c(remotes::system_requirements("ubuntu", "20.04"), remotes::system_requirements("ubuntu", "20.04", package="curl"))') + done < <(Rscript -e 'writeLines(c(remotes::system_requirements("ubuntu", "20.04"), remotes::system_requirements("ubuntu", "20.04", package="curl")))') - name: Install dependencies run: |