From 087bf3014c4612a7306aac3cb772f6e9b8aec673 Mon Sep 17 00:00:00 2001 From: RevBayes analysis <1695515+ms609@users.noreply.github.com> Date: Fri, 20 Mar 2026 08:08:42 +0000 Subject: [PATCH] Drop stringi dependency --- .github/copilot-instructions.md | 2 +- .github/workflows/ASan.yml | 2 -- .github/workflows/R-CMD-check.yml | 1 + DESCRIPTION | 3 +- NAMESPACE | 1 - NEWS.md | 12 ++++++++ R/PhyToString.R | 9 +++--- R/RcppExports.R | 4 +++ R/Splits.R | 15 +++++----- codemeta.json | 20 +++---------- man/figures/Stemwardness.png | Bin 7709 -> 3703 bytes src/RcppExports.cpp | 13 ++++++++ src/fast_paste.cpp | 48 ++++++++++++++++++++++++++++++ 13 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 src/fast_paste.cpp diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 069897bb7..2f2a88027 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -18,7 +18,7 @@ Always reference these instructions first and fallback to search or bash command ``` - Install TreeTools dependencies: ```bash - sudo R -e "install.packages(c('ape', 'bit64', 'lifecycle', 'fastmatch', 'RCurl', 'Rdpack', 'stringi', 'PlotTools'), repos='https://cran.r-project.org/', dependencies=TRUE)" + sudo R -e "install.packages(c('ape', 'bit64', 'lifecycle', 'fastmatch', 'RCurl', 'Rdpack', 'PlotTools'), repos='https://cran.r-project.org/', dependencies=TRUE)" ``` ### Building and Checking diff --git a/.github/workflows/ASan.yml b/.github/workflows/ASan.yml index 154e206c4..251a75042 100644 --- a/.github/workflows/ASan.yml +++ b/.github/workflows/ASan.yml @@ -43,8 +43,6 @@ jobs: _R_CHECK_FORCE_SUGGESTS_: false RSPM: https://packagemanager.rstudio.com/cran/__linux__/noble/latest USING_ASAN: true - STRINGI_DISABLE_PKG_CONFIG: true - BIOCONDUCTOR_USE_CONTAINER_REPOSITORY: FALSE # For stringi GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} ASAN_OPTIONS: detect_container_overflow=1:verify_asan_link_order=0 diff --git a/.github/workflows/R-CMD-check.yml b/.github/workflows/R-CMD-check.yml index 85b28879f..230f07950 100644 --- a/.github/workflows/R-CMD-check.yml +++ b/.github/workflows/R-CMD-check.yml @@ -75,6 +75,7 @@ jobs: - name: Set up R dependencies uses: r-lib/actions/setup-r-dependencies@v2 with: + cache-version: 2-${{ runner.arch }} needs: | check diff --git a/DESCRIPTION b/DESCRIPTION index cd750cb39..72d65deb2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: TreeTools Title: Create, Modify and Analyse Phylogenetic Trees -Version: 2.2.0 +Version: 2.2.0.9001 Authors@R: c( person("Martin R.", 'Smith', role = c("aut", "cre", "cph"), email = "martin.smith@durham.ac.uk", @@ -45,7 +45,6 @@ Imports: methods, PlotTools, Rdpack (>= 2.6.6), - stringi, Suggests: RCurl, spelling, diff --git a/NAMESPACE b/NAMESPACE index 120e8e0f9..7f34d68f7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -508,7 +508,6 @@ importFrom(stats,median) importFrom(stats,na.omit) importFrom(stats,runif) importFrom(stats,setNames) -importFrom(stringi,stri_paste) importFrom(utils,combn) importFrom(utils,globalVariables) importFrom(utils,head) diff --git a/NEWS.md b/NEWS.md index 541b04d3e..577f39cc4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,15 @@ +# TreeTools 2.2.0.9001 # + +## New functionality + +- `Paste0()` provides a fast Rcpp-backed drop-in for `paste0()` / `stri_paste()` + with `NA` propagation. Exported for use by downstream packages. + +## Performance + +- Drop `stringi` dependency. +- `as.character.Splits()` reimplemented in C++; ~3× faster on 200-tip trees. + # TreeTools 2.2.0 (2026-03-18) # ## New functionality diff --git a/R/PhyToString.R b/R/PhyToString.R index c0a95a14d..1b6653bac 100644 --- a/R/PhyToString.R +++ b/R/PhyToString.R @@ -64,7 +64,6 @@ StringToPhydat <- StringToPhyDat #' #' @family phylogenetic matrix conversion functions #' @template MRS -#' @importFrom stringi stri_paste #' @export PhyToString <- function(phy, parentheses = "{", collapse = "", ps = "", useIndex = TRUE, byTaxon = TRUE, concatenate = TRUE) { @@ -119,17 +118,17 @@ PhyToString <- function(phy, parentheses = "{", collapse = "", ps = "", ret <- t(ret) } if (isTRUE(concatenate)) { - stri_paste(c(ret, ps), collapse = "") + paste0(c(ret, ps), collapse = "") } else { if (isTRUE(byTaxon)) { - stri_paste(stri_paste(ret, ps)) + paste0(ret, ps) } else { - stri_paste(ret, ps, collapse = "") + paste0(ret, ps, collapse = "") } } } else { if (isTRUE(byTaxon)) ret <- t(ret) - stri_paste(apply(ret, 1, stri_paste, collapse = ""), ps) + paste0(apply(ret, 1, paste0, collapse = ""), ps) } # Return: ret diff --git a/R/RcppExports.R b/R/RcppExports.R index ee96e8d01..ada9065f3 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -45,6 +45,10 @@ descendant_tips <- function(parent, child, postorder) { .Call(`_TreeTools_descendant_tips`, parent, child, postorder) } +splits_to_char <- function(membership, labels) { + .Call(`_TreeTools_splits_to_char`, membership, labels) +} + first_matching_split_pair <- function(x, table) { .Call(`_TreeTools_first_matching_split_pair`, x, table) } diff --git a/R/Splits.R b/R/Splits.R index bd57e1ff4..a6189022e 100644 --- a/R/Splits.R +++ b/R/Splits.R @@ -452,17 +452,16 @@ names.Splits <- function(x) rownames(x) #' @family Splits operations -#' @importFrom stringi stri_paste #' @export as.character.Splits <- function(x, ...) { tipLabels <- attr(x, "tip.label") - nTip <- attr(x, "nTip") - - apply(as.logical(x), 1L, function(inSplit) { - stri_paste(stri_paste(tipLabels[inSplit], collapse = " "), " | ", - stri_paste(tipLabels[!inSplit], collapse = " ")) - }) - + if (is.null(tipLabels)) { + tipLabels <- paste0("t", seq_len(attr(x, "nTip"))) + } + logx <- as.logical(x) + ret <- splits_to_char(logx, tipLabels) + names(ret) <- rownames(logx) + ret } #' @family Splits operations diff --git a/codemeta.json b/codemeta.json index c263a3727..a3a09b551 100644 --- a/codemeta.json +++ b/codemeta.json @@ -8,13 +8,13 @@ "codeRepository": "https://github.com/ms609/TreeTools/", "issueTracker": "https://github.com/ms609/TreeTools/issues/", "license": "https://spdx.org/licenses/GPL-3.0", - "version": "2.2.0", + "version": "2.2.0.9001", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.5.3 (2026-03-11)", + "runtimePlatform": "R version 4.5.3 (2026-03-11 ucrt)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -261,21 +261,9 @@ }, "sameAs": "https://CRAN.R-project.org/package=Rdpack" }, - "8": { - "@type": "SoftwareApplication", - "identifier": "stringi", - "name": "stringi", - "provider": { - "@id": "https://cran.r-project.org", - "@type": "Organization", - "name": "Comprehensive R Archive Network (CRAN)", - "url": "https://cran.r-project.org" - }, - "sameAs": "https://CRAN.R-project.org/package=stringi" - }, "SystemRequirements": "C++17" }, - "fileSize": "1849.432KB", + "fileSize": "1871.905KB", "citation": [ { "@type": "SoftwareSourceCode", @@ -290,7 +278,7 @@ ], "name": "TreeTools: create, modify and analyse phylogenetic trees", "identifier": "10.32614/CRAN.package.TreeTools", - "description": "R package version 2.2.0", + "description": "R package version 2.2.0.9001", "@id": "https://doi.org/10.32614/CRAN.package.TreeTools", "sameAs": "https://doi.org/10.32614/CRAN.package.TreeTools" } diff --git a/man/figures/Stemwardness.png b/man/figures/Stemwardness.png index d2954f60831828860909c98b887e466f0e2c86a1..3b49ff71773cc0ccee27dd6af5abadb16506ad46 100644 GIT binary patch literal 3703 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 literal 7709 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 Z(~RDoX$Dg::type membership(membershipSEXP); + Rcpp::traits::input_parameter< const CharacterVector >::type labels(labelsSEXP); + rcpp_result_gen = Rcpp::wrap(splits_to_char(membership, labels)); + return rcpp_result_gen; +END_RCPP +} // first_matching_split_pair IntegerVector first_matching_split_pair(const RawMatrix x, const RawMatrix table); RcppExport SEXP _TreeTools_first_matching_split_pair(SEXP xSEXP, SEXP tableSEXP) { @@ -534,6 +546,7 @@ static const R_CallMethodDef CallEntries[] = { {"_TreeTools_descendant_edges", (DL_FUNC) &_TreeTools_descendant_edges, 3}, {"_TreeTools_descendant_edges_single", (DL_FUNC) &_TreeTools_descendant_edges_single, 5}, {"_TreeTools_descendant_tips", (DL_FUNC) &_TreeTools_descendant_tips, 3}, + {"_TreeTools_splits_to_char", (DL_FUNC) &_TreeTools_splits_to_char, 2}, {"_TreeTools_first_matching_split_pair", (DL_FUNC) &_TreeTools_first_matching_split_pair, 2}, {"_TreeTools_first_matching_split_index", (DL_FUNC) &_TreeTools_first_matching_split_index, 2}, {"_TreeTools_num_to_parent", (DL_FUNC) &_TreeTools_num_to_parent, 2}, diff --git a/src/fast_paste.cpp b/src/fast_paste.cpp new file mode 100644 index 000000000..4e119965d --- /dev/null +++ b/src/fast_paste.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +using namespace Rcpp; + +// Format each row of a logical split matrix as "A B | C D". +// `membership` is nSplit x nTip (logical); `labels` is length nTip. +// [[Rcpp::export]] +CharacterVector splits_to_char(const LogicalMatrix membership, + const CharacterVector labels) { + const int n_split = membership.nrow(); + const int n_tip = membership.ncol(); + CharacterVector out(n_split); + + // Cache translated label pointers and lengths + std::vector lab(n_tip); + std::vector lab_len(n_tip); + for (int j = 0; j < n_tip; ++j) { + lab[j] = Rf_translateCharUTF8(STRING_ELT(labels, j)); + lab_len[j] = std::strlen(lab[j]); + } + + std::string buf; + for (int i = 0; i < n_split; ++i) { + buf.clear(); + bool first_in = true; + for (int j = 0; j < n_tip; ++j) { + if (membership(i, j)) { + if (!first_in) buf.push_back(' '); + buf.append(lab[j], lab_len[j]); + first_in = false; + } + } + buf.append(" | ", 3); + bool first_out = true; + for (int j = 0; j < n_tip; ++j) { + if (!membership(i, j)) { + if (!first_out) buf.push_back(' '); + buf.append(lab[j], lab_len[j]); + first_out = false; + } + } + SET_STRING_ELT(out, i, Rf_mkCharLenCE(buf.c_str(), buf.size(), CE_UTF8)); + } + return out; +}