From 77d691e7e2f31c0c7d0604f5043b50bff0de3ec9 Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 14:09:34 -0800 Subject: [PATCH 1/8] chore: Update docs for R and fix binding --- r-phylo2vec/README.md | 56 +++++++++++++++++++++++++++++++-- r-phylo2vec/src/rust/src/lib.rs | 4 +++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/r-phylo2vec/README.md b/r-phylo2vec/README.md index 7fc9f6a..713455b 100644 --- a/r-phylo2vec/README.md +++ b/r-phylo2vec/README.md @@ -12,7 +12,51 @@ Open up `R` command line interactive mode within the pixi environment: pixi run -e r-phylo2vec R --interactive ``` -Once in R, you can run the following to start using the package: +## Development Guide + +The core Rust code for phylo2vec is located in [phylo2vec](../phylo2vec) +directory. To add additional binding for the Rust code, you can add more define +these in [`lib.rs`](./src/rust/src/lib.rs). + +It uses [extendr](https://github.com/extendr/extendr), a safe and user friendly +R extension interface using Rust. You should be able to directly import the +necessary functions from `phylo2vec` Rust crate. + +**Note: You will need to run the steps below from the root of the repository for +it to work** + +### 1. Modify `src/rust/src/lib.rs` + +Add the function with @export, so it will get exported from the generated R +package. (Without this tag, the function would be available internally for +package programming but not externally to users of the package.) + +```Rust +/// Recover a rooted tree (in Newick format) from a Phylo2Vec v +/// @export +#[extendr] +fn to_newick(input_integers: Vec) -> String { + let input_vector = input_integers.iter().map(|&x| x as usize).collect(); + let newick = ops::to_newick(&input_vector); + newick +} +``` + +Don’t forget to add the function to `extendr_module!`: + +```Rust +extendr_module! { + mod phylo2vec; + ... + fn to_newick; + ... +} +``` + +### 2. Run `rextendr::document` for building docs and binding R code + +To rebuild the docs and binding code for R, you can simply run +`rextendr::document`: ```R # Install rextendr if not already installed @@ -20,9 +64,15 @@ install.packages("rextendr") # Install phylo2vec package rextendr::document('./r-phylo2vec') +``` + +### 3. Run `devtools::load_all` and test the function -# Import the library -library('phylo2vec') +Once in R, you can run the following to load and use the package: + +```R +# Compile, install, and load phylo2vec package +devtools::load_all('./r-phylo2vec') # A small demo v = sample(5, FALSE) diff --git a/r-phylo2vec/src/rust/src/lib.rs b/r-phylo2vec/src/rust/src/lib.rs index 1028fe1..f1a01a7 100644 --- a/r-phylo2vec/src/rust/src/lib.rs +++ b/r-phylo2vec/src/rust/src/lib.rs @@ -4,6 +4,7 @@ use phylo2vec::tree_vec::ops; use phylo2vec::utils; /// Sample a random tree via Phylo2Vec +/// @export #[extendr] fn sample(n_leaves: usize, ordered: bool) -> Vec { let v = utils::sample(n_leaves, ordered); @@ -11,6 +12,7 @@ fn sample(n_leaves: usize, ordered: bool) -> Vec { } /// Recover a rooted tree (in Newick format) from a Phylo2Vec v +/// @export #[extendr] fn to_newick(input_integers: Vec) -> String { let input_vector = input_integers.iter().map(|&x| x as usize).collect(); @@ -19,6 +21,7 @@ fn to_newick(input_integers: Vec) -> String { } /// Convert a newick string to a Phylo2Vec vector +/// @export #[extendr] fn to_vector(newick: &str) -> Vec { let v = ops::to_vector(&newick); @@ -26,6 +29,7 @@ fn to_vector(newick: &str) -> Vec { } /// Validate a Phylo2Vec vector +/// @export #[extendr] fn check_v(input_integers: Vec) { let input_vector = input_integers.iter().map(|&x| x as usize).collect(); From df4edeb08c445f54143bd7de3c97e1b84e5019ef Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 14:10:48 -0800 Subject: [PATCH 2/8] chore: Run build --- r-phylo2vec/NAMESPACE | 4 ++++ r-phylo2vec/R/extendr-wrappers.R | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/r-phylo2vec/NAMESPACE b/r-phylo2vec/NAMESPACE index f8caf78..72a5fb5 100644 --- a/r-phylo2vec/NAMESPACE +++ b/r-phylo2vec/NAMESPACE @@ -1,3 +1,7 @@ # Generated by roxygen2: do not edit by hand +export(check_v) +export(sample) +export(to_newick) +export(to_vector) useDynLib(phylo2vec, .registration = TRUE) diff --git a/r-phylo2vec/R/extendr-wrappers.R b/r-phylo2vec/R/extendr-wrappers.R index ea47b10..a9c3293 100644 --- a/r-phylo2vec/R/extendr-wrappers.R +++ b/r-phylo2vec/R/extendr-wrappers.R @@ -11,15 +11,19 @@ NULL #' Sample a random tree via Phylo2Vec +#' @export sample <- function(n_leaves, ordered) .Call(wrap__sample, n_leaves, ordered) #' Recover a rooted tree (in Newick format) from a Phylo2Vec v +#' @export to_newick <- function(input_integers) .Call(wrap__to_newick, input_integers) #' Convert a newick string to a Phylo2Vec vector +#' @export to_vector <- function(newick) .Call(wrap__to_vector, newick) #' Validate a Phylo2Vec vector +#' @export check_v <- function(input_integers) invisible(.Call(wrap__check_v, input_integers)) From b712f91ad19b628ccfc0cf889d97eae1b28c3ec3 Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 14:40:32 -0800 Subject: [PATCH 3/8] test: Add R tests with testthat --- r-phylo2vec/DESCRIPTION | 3 +++ r-phylo2vec/tests/testthat.R | 12 ++++++++++++ r-phylo2vec/tests/testthat/test_to_newick.R | 12 ++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 r-phylo2vec/tests/testthat.R create mode 100644 r-phylo2vec/tests/testthat/test_to_newick.R diff --git a/r-phylo2vec/DESCRIPTION b/r-phylo2vec/DESCRIPTION index 9496ea7..5ba7321 100644 --- a/r-phylo2vec/DESCRIPTION +++ b/r-phylo2vec/DESCRIPTION @@ -9,3 +9,6 @@ Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 Config/rextendr/version: 0.3.1 +Suggests: + testthat (>= 3.0.0) +Config/testthat/edition: 3 diff --git a/r-phylo2vec/tests/testthat.R b/r-phylo2vec/tests/testthat.R new file mode 100644 index 0000000..b3f172b --- /dev/null +++ b/r-phylo2vec/tests/testthat.R @@ -0,0 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + +library(testthat) +library(phylo2vec) + +test_check("phylo2vec") diff --git a/r-phylo2vec/tests/testthat/test_to_newick.R b/r-phylo2vec/tests/testthat/test_to_newick.R new file mode 100644 index 0000000..4ec5e96 --- /dev/null +++ b/r-phylo2vec/tests/testthat/test_to_newick.R @@ -0,0 +1,12 @@ +library(testthat) +library(phylo2vec) + +test_that(desc = "Vector to newick", code = { + + vec <- c(0L, 0L, 0L, 1L, 3L) + + newick <- to_newick(vec) + + # Test that the result is the correct value + expect_equal( newick, "(((0,(3,5)6)8,2)9,(1,4)7)10;"); +}) From ad74fc5bdbc034161ea6c1501dadf985da3b1f03 Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 15:21:41 -0800 Subject: [PATCH 4/8] chore: Add easy R install --- pixi.toml | 3 +++ r-phylo2vec/README.md | 26 +++++++++++++++++++++++++- r-phylo2vec/scripts/install-package.R | 9 +++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 r-phylo2vec/scripts/install-package.R diff --git a/pixi.toml b/pixi.toml index 7718c0f..3a7ec7c 100644 --- a/pixi.toml +++ b/pixi.toml @@ -107,3 +107,6 @@ depends-on = ["install-python"] [feature.r.dependencies] r = "4.*" r-devtools = ">=2.4.5,<3" + +[feature.r.tasks.install-r] +cmd = "Rscript ./r-phylo2vec/scripts/install-package.R" diff --git a/r-phylo2vec/README.md b/r-phylo2vec/README.md index 713455b..71d6dfb 100644 --- a/r-phylo2vec/README.md +++ b/r-phylo2vec/README.md @@ -6,12 +6,30 @@ your own risk.** This directory contains the pylo2vec R codebase, which includes Rust binding setup. -Open up `R` command line interactive mode within the pixi environment: +## Quick install and run + +To quickly install the package and run it, simply run the following + +```console +pixi run -e r-phylo2vec install-r +``` + +Once the package is installed you can open up the R terminal: ```console pixi run -e r-phylo2vec R --interactive ``` +In the R terminal, you can then import the `phylo2vec` library: + +```R +library(phylo2vec) + +# A small demo +v = sample(10, FALSE) +to_newick(v) +``` + ## Development Guide The core Rust code for phylo2vec is located in [phylo2vec](../phylo2vec) @@ -25,6 +43,12 @@ necessary functions from `phylo2vec` Rust crate. **Note: You will need to run the steps below from the root of the repository for it to work** +Open up `R` command line interactive mode within the pixi environment: + +```console +pixi run -e r-phylo2vec R --interactive +``` + ### 1. Modify `src/rust/src/lib.rs` Add the function with @export, so it will get exported from the generated R diff --git a/r-phylo2vec/scripts/install-package.R b/r-phylo2vec/scripts/install-package.R new file mode 100644 index 0000000..e13e18e --- /dev/null +++ b/r-phylo2vec/scripts/install-package.R @@ -0,0 +1,9 @@ +pkg_path <- "./r-phylo2vec" + +fn = devtools::build(pkg_path, binary = TRUE, args = c('--preclean')) + +devtools::install_local(fn, force = TRUE) + +if (file.exists(fn)) { + file.remove(fn) +} From a8cdfcdec3e6409fdb597e1ccb47cfbcc7ae177f Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 15:30:12 -0800 Subject: [PATCH 5/8] ci: Add R ci test --- .github/workflows/ci-R.yml | 26 ++++++++++++++++++++++++++ pixi.toml | 3 +++ r-phylo2vec/scripts/run-tests.R | 3 +++ 3 files changed, 32 insertions(+) create mode 100644 .github/workflows/ci-R.yml create mode 100644 r-phylo2vec/scripts/run-tests.R diff --git a/.github/workflows/ci-R.yml b/.github/workflows/ci-R.yml new file mode 100644 index 0000000..ec33685 --- /dev/null +++ b/.github/workflows/ci-R.yml @@ -0,0 +1,26 @@ +name: CI R + +on: + push: + branches: + - main + - dev + pull_request: + +permissions: + contents: read + +env: + PIXI_COLOR: always + R_ENV: r-phylo2vec + +jobs: + build_and_test: + name: R project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: prefix-dev/setup-pixi@v0.8.1 + with: + environments: ${{ env.R_ENV }} + - run: pixi run -e ${{ env.R_ENV }} test diff --git a/pixi.toml b/pixi.toml index 3a7ec7c..9160f2b 100644 --- a/pixi.toml +++ b/pixi.toml @@ -110,3 +110,6 @@ r-devtools = ">=2.4.5,<3" [feature.r.tasks.install-r] cmd = "Rscript ./r-phylo2vec/scripts/install-package.R" + +[feature.r.tasks.test] +cmd = "Rscript ./r-phylo2vec/scripts/run-tests.R" diff --git a/r-phylo2vec/scripts/run-tests.R b/r-phylo2vec/scripts/run-tests.R new file mode 100644 index 0000000..ad0737d --- /dev/null +++ b/r-phylo2vec/scripts/run-tests.R @@ -0,0 +1,3 @@ +pkg_path <- "./r-phylo2vec" + +devtools::test(pkg_path, stop_on_failure = TRUE) From bde4546e4b79389b537d37ff1a6a63c7ce04ba98 Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 15:45:06 -0800 Subject: [PATCH 6/8] ci: Update to have args input for r tasks --- .github/workflows/ci-R.yml | 2 +- r-phylo2vec/scripts/install-package.R | 12 ++++++++++-- r-phylo2vec/scripts/run-tests.R | 12 ++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-R.yml b/.github/workflows/ci-R.yml index ec33685..7a3a52c 100644 --- a/.github/workflows/ci-R.yml +++ b/.github/workflows/ci-R.yml @@ -23,4 +23,4 @@ jobs: - uses: prefix-dev/setup-pixi@v0.8.1 with: environments: ${{ env.R_ENV }} - - run: pixi run -e ${{ env.R_ENV }} test + - run: pixi run -e ${{ env.R_ENV }} test "./${{ env.R_ENV}}" diff --git a/r-phylo2vec/scripts/install-package.R b/r-phylo2vec/scripts/install-package.R index e13e18e..a98df76 100644 --- a/r-phylo2vec/scripts/install-package.R +++ b/r-phylo2vec/scripts/install-package.R @@ -1,6 +1,14 @@ -pkg_path <- "./r-phylo2vec" +# File to run binary build and install for the package +# Usage: Rscript install-package.R -fn = devtools::build(pkg_path, binary = TRUE, args = c('--preclean')) +args = commandArgs(trailingOnly=TRUE) + +if (length(args)==0) { + # default path from root + args[1] = "./r-phylo2vec" +} + +fn = devtools::build(args[1], binary = TRUE, args = c('--preclean')) devtools::install_local(fn, force = TRUE) diff --git a/r-phylo2vec/scripts/run-tests.R b/r-phylo2vec/scripts/run-tests.R index ad0737d..e69e1bd 100644 --- a/r-phylo2vec/scripts/run-tests.R +++ b/r-phylo2vec/scripts/run-tests.R @@ -1,3 +1,11 @@ -pkg_path <- "./r-phylo2vec" +# File to run tests for the package +# Usage: Rscript run-tests.R -devtools::test(pkg_path, stop_on_failure = TRUE) +args = commandArgs(trailingOnly=TRUE) + +if (length(args)==0) { + # default path from root + args[1] = "./r-phylo2vec" +} + +devtools::test(args[1], stop_on_failure = TRUE) From 5f323c85435daa4629dcac7709ab4c63ecc96a48 Mon Sep 17 00:00:00 2001 From: Landung 'Don' Setiawan Date: Thu, 2 Jan 2025 15:49:40 -0800 Subject: [PATCH 7/8] docs: Update phylo2vec misspelling --- py-phylo2vec/README.md | 2 +- r-phylo2vec/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/py-phylo2vec/README.md b/py-phylo2vec/README.md index e50e12c..3e6fd06 100644 --- a/py-phylo2vec/README.md +++ b/py-phylo2vec/README.md @@ -3,7 +3,7 @@ **NOTE: This is currently in active development and APIs will change. Use at your own risk.** -This directory contains the pylo2vec Python codebase, which includes Rust +This directory contains the phylo2vec Python codebase, which includes Rust binding setup. To install this python package, simply run the following within the root diff --git a/r-phylo2vec/README.md b/r-phylo2vec/README.md index 71d6dfb..f306f3c 100644 --- a/r-phylo2vec/README.md +++ b/r-phylo2vec/README.md @@ -3,7 +3,7 @@ **NOTE: This is currently in active development and APIs will change. Use at your own risk.** -This directory contains the pylo2vec R codebase, which includes Rust binding +This directory contains the phylo2vec R codebase, which includes Rust binding setup. ## Quick install and run From ef4860eadd60e753fdef97f33a9cdcfaf8d89e8d Mon Sep 17 00:00:00 2001 From: Don Setiawan Date: Mon, 6 Jan 2025 09:39:54 -0800 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Maddie Gordon <50681653+madelinegordon@users.noreply.github.com> --- r-phylo2vec/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/r-phylo2vec/README.md b/r-phylo2vec/README.md index f306f3c..5bd63d4 100644 --- a/r-phylo2vec/README.md +++ b/r-phylo2vec/README.md @@ -33,10 +33,10 @@ to_newick(v) ## Development Guide The core Rust code for phylo2vec is located in [phylo2vec](../phylo2vec) -directory. To add additional binding for the Rust code, you can add more define +directory. To add additional bindings for the Rust code, you can add more define these in [`lib.rs`](./src/rust/src/lib.rs). -It uses [extendr](https://github.com/extendr/extendr), a safe and user friendly +It uses [extendr](https://github.com/extendr/extendr), a safe and user-friendly R extension interface using Rust. You should be able to directly import the necessary functions from `phylo2vec` Rust crate.