From a423651706643f2db5b1f5e2cf6af2ffbb6228db Mon Sep 17 00:00:00 2001 From: Jason Cory Brunson Date: Tue, 19 Aug 2025 15:12:53 -0400 Subject: [PATCH 1/3] store distances as double rather than float --- NEWS.md | 6 ++++++ src/RcppExports.cpp | 4 ++-- src/ripser.cpp | 10 +++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index c13005f..a352c4f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +# next version + +## float to double + +Ripser stores values of the `value_t` type and `ratio` as floats. This is not incompatible with R, but R users are likely to expect numeric values to be handled as doubles. Both values are now stored as doubles. + # ripserr 1.0.0 This major version replaces an outdated version of the Ripser C++ library with its current version. diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index a9b2d3f..43c32f8 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -57,7 +57,7 @@ BEGIN_RCPP END_RCPP } // ripser_cpp_dist -Rcpp::List ripser_cpp_dist(const Rcpp::NumericVector& dataset, int dim, double thresh, float ratio, int p); +Rcpp::List ripser_cpp_dist(const Rcpp::NumericVector& dataset, int dim, double thresh, double ratio, int p); RcppExport SEXP _ripserr_ripser_cpp_dist(SEXP datasetSEXP, SEXP dimSEXP, SEXP threshSEXP, SEXP ratioSEXP, SEXP pSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -65,7 +65,7 @@ BEGIN_RCPP Rcpp::traits::input_parameter< const Rcpp::NumericVector& >::type dataset(datasetSEXP); Rcpp::traits::input_parameter< int >::type dim(dimSEXP); Rcpp::traits::input_parameter< double >::type thresh(threshSEXP); - Rcpp::traits::input_parameter< float >::type ratio(ratioSEXP); + Rcpp::traits::input_parameter< double >::type ratio(ratioSEXP); Rcpp::traits::input_parameter< int >::type p(pSEXP); rcpp_result_gen = Rcpp::wrap(ripser_cpp_dist(dataset, dim, thresh, ratio, p)); return rcpp_result_gen; diff --git a/src/ripser.cpp b/src/ripser.cpp index 0868dfa..355c0ee 100644 --- a/src/ripser.cpp +++ b/src/ripser.cpp @@ -84,7 +84,7 @@ template using hash = std::hash; #endif -typedef float value_t; +typedef double value_t; typedef int64_t index_t; typedef uint16_t coefficient_t; @@ -413,7 +413,7 @@ template class ripser { const DistanceMatrix dist; const index_t n, dim_max; const value_t threshold; - const float ratio; + const double ratio; const coefficient_t modulus; const binomial_coeff_table binomial_coeff; const std::vector multiplicative_inverse; @@ -436,7 +436,7 @@ template class ripser { // ripserq: Accumulate pairs in an object to be returned to the user. std::vector>> persistence_pairs; - ripser(DistanceMatrix&& _dist, index_t _dim_max, value_t _threshold, float _ratio, + ripser(DistanceMatrix&& _dist, index_t _dim_max, value_t _threshold, double _ratio, coefficient_t _modulus) : dist(std::move(_dist)), n(dist.size()), dim_max(std::min(_dim_max, index_t(dist.size() - 2))), threshold(_threshold), @@ -1257,7 +1257,7 @@ int main(int argc, char** argv) { index_t dim_max = 1; value_t threshold = std::numeric_limits::max(); - float ratio = 1; + double ratio = 1; coefficient_t modulus = 2; for (index_t i = 1; i < argc; ++i) { @@ -1382,7 +1382,7 @@ int main(int argc, char** argv) { #endif // [[Rcpp::export()]] -Rcpp::List ripser_cpp_dist(const Rcpp::NumericVector &dataset, int dim, double thresh, float ratio, int p) { +Rcpp::List ripser_cpp_dist(const Rcpp::NumericVector &dataset, int dim, double thresh, double ratio, int p) { std::vector distances(dataset.begin(), dataset.end()); compressed_lower_distance_matrix dist(compressed_upper_distance_matrix(std::move(distances))); From c41c26d7f0798edb2ff16057a30ab2a8be77a61b Mon Sep 17 00:00:00 2001 From: Jason Cory Brunson Date: Tue, 30 Sep 2025 17:44:39 -0400 Subject: [PATCH 2/3] extend cubical to 1-dimensional arrays and vectors --- NAMESPACE | 2 ++ NEWS.md | 19 ++++++++++++++----- R/cubical.R | 22 +++++++++++++++++++++- R/utility.R | 8 ++++++-- man/cubical.Rd | 8 ++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 5e6e424..652e518 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ S3method(cubical,array) S3method(cubical,matrix) +S3method(cubical,numeric) S3method(head,PHom) S3method(print,PHom) S3method(tail,PHom) @@ -17,6 +18,7 @@ export(as.PHom) export(cubical) export(cubical.array) export(cubical.matrix) +export(cubical.numeric) export(is.PHom) export(vietoris_rips) export(vietoris_rips.data.frame) diff --git a/NEWS.md b/NEWS.md index ab6ec3b..1069428 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,17 +1,26 @@ # next version -## permission for deaths before births and superlevel set filtrations of rasters +## Vietoris-Rips PH -A logical argument `sublevel` has been added to `cubical` that, when `FALSE`, will pre- and post-transform raster data in order to obtain superlevel set persistent homology. -Enabling this, an assertion that all `birth < death` has been removed from checks of persistence data. - -## sliding window embeddings of multivariable time series (breaking change) +### sliding window embeddings of multivariable time series (breaking change) Previously only univariable time series could be passed to `vietoris_rips()` via the sliding window embedding (used for quasi-attractor detection). An additional unexported embedding function has been written to handle multivariable time series. (It underperforms the original function on univariable time series, which therefore continue to rely on the original.) Furthermore, whereas `data_dim` previously defaulted to `2`, it now defaults to the number of observations per time unit of a time series as recovered by `tsp()`. (The behavior for unclassed numeric vectors remains unchanged.) +## cubical PH + +### functionality for 1-dimensional arrays + +`cubical()` can now handle 1-dimensional arrays (for which no dedicated source code exists) by treating them as 2-dimensional (with an expanse of 1 in the second dimension). +This enables the new method `cubical.numeric()` to accept vectors. + +### deaths before births and superlevel set filtrations + +A logical argument `sublevel` has been added to `cubical` that, when `FALSE`, will pre- and post-transform raster data in order to obtain superlevel set persistent homology. +Enabling this, an assertion that all `birth < death` has been removed from checks of persistence data. + # ripserr 1.0.0 This major version replaces an outdated version of the Ripser C++ library with its current version. diff --git a/R/cubical.R b/R/cubical.R index 82d0dae..be264ba 100644 --- a/R/cubical.R +++ b/R/cubical.R @@ -24,6 +24,11 @@ #' @return `PHom` object #' @examples #' +#' # 1-dim example +#' dataset <- rnorm(1500) +#' dim(dataset) <- 1500 +#' cubical_hom1 <- cubical(dataset) +#' #' # 2-dim example #' dataset <- rnorm(10 ^ 2) #' dim(dataset) <- rep(10, 2) @@ -74,6 +79,8 @@ cubical.array <- function( method = method) validate_arr_cub(dataset) + # if dataset is 1-dimensional, treat it as 2-dimensional + if (length(dim(dataset)) == 1L) dim(dataset) <- c(dim(dataset), 1L) # transform method parameter for C++ function method_int <- switch(method, @@ -146,4 +153,17 @@ cubical.matrix <- function(dataset, ...) { # return return(ans) -} \ No newline at end of file +} + +#' @rdname cubical +#' @export cubical.numeric +#' @export +cubical.numeric <- function(dataset, ...) { + # convert the numeric vector to a 1-dimensional array + dataset <- as.array(dataset, dim = 1L) + + # calculate persistent homology using cubical.array + ans <- cubical.array(dataset, ...) + + return(ans) +} diff --git a/R/utility.R b/R/utility.R index 30026cb..539e077 100644 --- a/R/utility.R +++ b/R/utility.R @@ -122,7 +122,7 @@ validate_arr_cub <- function(dataset) { error_class(dataset, "dataset", "array") # dataset should have either 2, 3, or 4 dimensions (only ones supported) - if (!(length(dim(dataset)) %in% c(2, 3, 4))) { + if (!(length(dim(dataset)) %in% seq(4))) { stop(paste("dataset parameter must have either 2, 3, or 4 dimensions,", "passed argument has", length(dim(dataset)), "dimensions")) } @@ -139,7 +139,11 @@ validate_arr_cub <- function(dataset) { } # make sure dataset is not too large - if (length(dim(dataset)) == 2) { + if (length(dim(dataset)) == 1L) { + if (dim(dataset) > 2000) { + stop(paste("Max size for dim 1 = 2000; passed size =", dim(dataset))) + } + } else if (length(dim(dataset)) == 2) { if (dim(dataset)[1] > 2000 | dim(dataset)[2] > 1000) { stop(paste("Max size for dim 2 = 2000 x 1000; passed size =", diff --git a/man/cubical.Rd b/man/cubical.Rd index b2f1ea9..8ab3c15 100644 --- a/man/cubical.Rd +++ b/man/cubical.Rd @@ -4,6 +4,7 @@ \alias{cubical} \alias{cubical.array} \alias{cubical.matrix} +\alias{cubical.numeric} \title{Calculating Persistent Homology via a Cubical Complex} \usage{ cubical(dataset, ...) @@ -11,6 +12,8 @@ cubical(dataset, ...) \method{cubical}{array}(dataset, threshold = 9999, method = "lj", sublevel = TRUE, ...) \method{cubical}{matrix}(dataset, ...) + +\method{cubical}{numeric}(dataset, ...) } \arguments{ \item{dataset}{object on which to calculate persistent homology} @@ -50,6 +53,11 @@ persistence diagram in which \code{death} precedes \code{birth}. } \examples{ +# 1-dim example +dataset <- rnorm(1500) +dim(dataset) <- 1500 +cubical_hom1 <- cubical(dataset) + # 2-dim example dataset <- rnorm(10 ^ 2) dim(dataset) <- rep(10, 2) From a3373bf46c8f555ba67e6593ceaf870bd2da69b7 Mon Sep 17 00:00:00 2001 From: Jason Cory Brunson Date: Tue, 30 Sep 2025 17:45:37 -0400 Subject: [PATCH 3/3] increment version number --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index ac13e0d..98e102a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: ripserr Title: Calculate Persistent Homology with Ripser-Based Engines -Version: 1.0.0 +Version: 1.0.0.0003 Authors@R: c(person(given = "Raoul R.", family = "Wadhwa",