diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml
index f87860b..0148659 100644
--- a/.github/workflows/R-CMD-check.yaml
+++ b/.github/workflows/R-CMD-check.yaml
@@ -53,7 +53,6 @@ jobs:
- name: setup r-reticulate venv
shell: Rscript {0}
run: |
-
path_to_python <- reticulate::virtualenv_create(
envname = "r-reticulate",
python = Sys.which("python"), # placed on PATH by the setup-python action
@@ -61,9 +60,12 @@ jobs:
"POT==0.9.1", "scikit-learn==1.3.1", "gudhi==3.8.0"
)
)
-
writeLines(sprintf("RETICULATE_PYTHON=%s", path_to_python),
Sys.getenv("GITHUB_ENV"))
+ install.packages("tensorflow")
+ tensorflow::install_tensorflow(
+ envname = "r-reticulate", version = "2.14-cpu"
+ )
- uses: r-lib/actions/check-r-package@v2
with:
diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml
index ed7650c..147f920 100644
--- a/.github/workflows/pkgdown.yaml
+++ b/.github/workflows/pkgdown.yaml
@@ -30,9 +30,34 @@ jobs:
with:
use-public-rspm: true
+ - name: Install reticulate
+ shell: Rscript {0}
+ run: |
+ install.packages("reticulate")
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: "3.9"
+
+ - name: setup r-reticulate venv
+ shell: Rscript {0}
+ run: |
+ path_to_python <- reticulate::virtualenv_create(
+ envname = "r-reticulate",
+ python = Sys.which("python"), # placed on PATH by the setup-python action
+ packages = c(
+ "POT==0.9.1", "scikit-learn==1.3.1",
+ "tensorflow==2.14.0", "gudhi==3.8.0"
+ )
+ )
+ writeLines(sprintf("RETICULATE_PYTHON=%s", path_to_python),
+ Sys.getenv("GITHUB_ENV"))
+
- uses: r-lib/actions/setup-r-dependencies@v2
with:
- extra-packages: any::pkgdown, local::.
+ extra-packages: |
+ any::pkgdown
+ local::.
needs: website
- name: Build site
@@ -41,7 +66,7 @@ jobs:
- name: Deploy to GitHub pages 🚀
if: github.event_name != 'pull_request'
- uses: JamesIves/github-pages-deploy-action@v4.4.1
+ uses: JamesIves/github-pages-deploy-action@v4.4.3
with:
clean: false
branch: gh-pages
diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml
index 539d5cb..cfb73ce 100644
--- a/.github/workflows/test-coverage.yaml
+++ b/.github/workflows/test-coverage.yaml
@@ -28,20 +28,19 @@ jobs:
- uses: actions/setup-python@v4
with:
- python-version: "3.x"
+ python-version: "3.9"
- name: setup r-reticulate venv
shell: Rscript {0}
run: |
-
path_to_python <- reticulate::virtualenv_create(
envname = "r-reticulate",
python = Sys.which("python"), # placed on PATH by the setup-python action
packages = c(
- "POT==0.9.1", "scikit-learn==1.3.1", "gudhi==3.8.0"
+ "POT==0.9.1", "scikit-learn==1.3.1",
+ "tensorflow==2.14.0", "gudhi==3.8.0"
)
)
-
writeLines(sprintf("RETICULATE_PYTHON=%s", path_to_python),
Sys.getenv("GITHUB_ENV"))
diff --git a/DESCRIPTION b/DESCRIPTION
index dd358a0..a88297e 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -30,6 +30,7 @@ Config/reticulate:
packages = list(
list(package = "POT", version = "0.9.1"),
list(package = "scikit-learn", version = "1.3.1"),
+ list(package = "tensorflow", version = "2.14.0"),
list(package = "gudhi", version = "3.8.0")
)
)
diff --git a/NAMESPACE b/NAMESPACE
index 4b94eb1..366d731 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -19,6 +19,10 @@ export(DiagramScaler)
export(DiagramSelector)
export(Entropy)
export(FeatureAgglomeration)
+export(FlatPerslayPhi)
+export(GaussianMixturePerslayWeight)
+export(GaussianPerslayPhi)
+export(GridPerslayWeight)
export(KMeans)
export(Landscape)
export(MaxAbsScaler)
@@ -34,6 +38,8 @@ export(PersistenceImage)
export(PersistenceScaleSpaceKernel)
export(PersistenceSlicedWassersteinKernel)
export(PersistenceWeightedGaussianKernel)
+export(Perslay)
+export(PowerPerslayWeight)
export(ProminentPoints)
export(RipsComplex)
export(RobustScaler)
@@ -46,6 +52,7 @@ export(SpectralCoclustering)
export(StandardScaler)
export(StrongWitnessComplex)
export(TangentialComplex)
+export(TentPerslayPhi)
export(Tomato)
export(TopologicalVector)
export(WassersteinDistance)
@@ -59,6 +66,8 @@ export(is_persistence_diagram)
export(is_persistence_diagram_sample)
export(seq_circle)
export(sphere)
+export(tf)
+export(to_ragged_tensor)
export(torus)
importFrom(R6,R6Class)
importFrom(Rdpack,reprompt)
diff --git a/R/perslay.R b/R/perslay.R
new file mode 100644
index 0000000..b00a2ae
--- /dev/null
+++ b/R/perslay.R
@@ -0,0 +1,576 @@
+#' Perslay: Base Class
+#' @keywords internal
+PerslayBaseClass <- R6::R6Class(
+ classname = "PerslayBaseClass",
+ inherit = PythonClass,
+ public = list(
+ #' @description Creates the variables of the layer (optional, for subclass
+ #' implementers).
+ #'
+ #' This is a method that implementers of subclasses of `Layer` or `Model`
+ #' can override if they need a state-creation step in-between layer
+ #' instantiation and layer call. It is invoked automatically before the
+ #' first execution of `call()`.
+ #'
+ #' This is typically used to create the weights of `Layer` subclasses (at
+ #' the discretion of the subclass implementer).
+ #'
+ #' @param input_shape Instance of `TensorShape`, or list of instances of
+ #' `TensorShape` if the layer expects a list of inputs (one instance per
+ #' input).
+ #'
+ #' @return The class itself invisibly.
+ build = function(input_shape) {
+ super$get_python_class()$build(input_shape)
+ invisible(self)
+ },
+
+ #' @description Apply the layer to an input, or list of inputs, and return
+ #' the result.
+ #'
+ #' @param diagrams `r doc_perslay_diagrams_param()`
+ #'
+ #' @return Either a ragged tensor or a tensor storing the result of the
+ #' layer.
+ call = function(diagrams) {
+ super$get_python_class()$call(diagrams)
+ }
+ )
+)
+
+#' Perslay: Main Class
+#'
+#' @description This is a TensorFlow layer for vectorizing persistence diagrams
+#' in a differentiable way within a neural network. This function implements
+#' the PersLay equation \insertCite{carriere2020perslay}{rgudhi}.
+#'
+#' ## References
+#' \insertCited{}
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+Perslay <- R6::R6Class(
+ classname = "Perslay",
+ inherit = PerslayBaseClass,
+ public = list(
+ #' @description The [`Perslay`] constructor.
+ #'
+ #' @param weight A weight function for the persistence diagram points. Can
+ #' be either an object of class [`GridPerslayWeight`],
+ #' [`GaussianMixturePerslayWeight`], [`PowerPerslayWeight`] or a custom
+ #' TensorFlow function that takes persistence diagrams as argument
+ #' (represented as an \eqn{n \times \mathrm{None} \times 2} ragged tensor,
+ #' where \eqn{n} is the number of diagrams).
+ #' @param phi A transformation function for the persistence diagram points.
+ #' Can be either an object of class [`GaussianPerslayPhi`],
+ #' [`TentPerslayPhi`], [`FlatPerslayPhi`] or a custom TensorFlow class
+ #' (that can have trainable parameters) with a method `call()` that takes
+ #' persistence diagrams as argument (represented as an \eqn{n \times
+ #' \mathrm{None} \times 2} ragged tensor, where \eqn{n} is the number of
+ #' diagrams).
+ #' @param perm_op A permutation invariant function, such as
+ #' `tf$math$reduce_sum`, `tf$math$reduce_mean`, `tf$math$reduce_max`,
+ #' `tf$math$reduce_min` or a custom TensorFlow function that takes two
+ #' arguments: a tensor and an axis on which to apply the permutation
+ #' invariant operation. If `perm_op` is the string `"topk"` (where \eqn{k}
+ #' is a number), this function will be computed as `tf$math$top_k` with
+ #' integer parameter `k`.
+ #' @param rho A postprocessing function that is applied after the
+ #' permutation invariant operation. Can be any TensorFlow layer.
+ #' @param ... A named list providing extra arguments for compatibility with
+ #' the TensorFlow API. Not used here.
+ #'
+ #' @return An object of class [`Perslay`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' phi <- FlatPerslayPhi$new(
+ #' samples = c(1:13, 12:1),
+ #' theta = 0.1
+ #' )
+ #' weight <- GaussianMixturePerslayWeight$new(
+ #' gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+ #' )
+ #' perm_op <- tf$math$reduce_sum
+ #' rho <- tf$identity
+ #'
+ #' perslay <- Perslay$new(
+ #' phi = phi,
+ #' weight = weight,
+ #' perm_op = perm_op,
+ #' rho = rho
+ #' )
+ initialize = function(weight,
+ phi,
+ perm_op,
+ rho,
+ ...) {
+ super$set_python_class(
+ tfInterface$perslay$Perslay(
+ weight = weight$get_python_class(),
+ phi = phi$get_python_class(),
+ perm_op = perm_op,
+ rho = rho,
+ ...
+ )
+ )
+ },
+
+ #' @description Applies Perslay on a ragged tensor containing a list of
+ #' persistence diagrams.
+ #'
+ #' @param diagrams `r doc_perslay_diagrams_param()`
+ #'
+ #' @return A tensor storing the vectorizations of the persistence diagrams.
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' phi <- FlatPerslayPhi$new(
+ #' samples = c(1:13, 12:1),
+ #' theta = 0.1
+ #' )
+ #' weight <- GaussianMixturePerslayWeight$new(
+ #' gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+ #' )
+ #' perm_op <- tf$math$reduce_sum
+ #' rho <- tf$identity
+ #'
+ #' perslay <- Perslay$new(
+ #' phi = phi,
+ #' weight = weight,
+ #' perm_op = perm_op,
+ #' rho = rho
+ #' )
+ #'
+ #' vectors <- perslay$call(dgms)
+ call = function(diagrams) {
+ super$call(diagrams)
+ }
+ )
+)
+
+#' Perslay: Base Perslay Weight Class
+#' @description This is a base class for computing a differentiable weight
+#' function for persistence diagram points.
+#' @keywords internal
+BasePerslayWeightClass <- R6::R6Class(
+ classname = "BasePerslayWeightClass",
+ inherit = PerslayBaseClass,
+ public = list(
+ #' @description Apply the layer to an input, or list of inputs, and return
+ #' the result.
+ #'
+ #' @param diagrams `r doc_perslay_diagrams_param()`
+ #'
+ #' @return A ragged tensor storing the weights of the points in the \eqn{n}
+ #' persistence diagrams. The second dimension is ragged since persistence
+ #' diagrams can have different numbers of points.
+ call = function(diagrams) {
+ super$call(diagrams)
+ }
+ )
+)
+
+#' Perslay: Gaussian Mixture Perslay Weight
+#'
+#' @description This is a class for computing a differentiable weight function
+#' for persistence diagram points. This function is defined from a mixture of
+#' Gaussian functions.
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+GaussianMixturePerslayWeight <- R6::R6Class(
+ classname = "GaussianMixturePerslayWeight",
+ inherit = BasePerslayWeightClass,
+ public = list(
+ #' @description The [`GaussianMixturePerslayWeight`] constructor.
+ #'
+ #' @param gaussians A numeric matrix of size \eqn{4 \times n} specifying a
+ #' two-dimensional Gaussian distribution for each of the \eqn{n} diagrams.
+ #' Each column must store the Gaussian parameters in the following order:
+ #' \eqn{\mu_x, \mu_y, \sigma_x, \sigma_y}.
+ #' @param ... A named list providing extra arguments for compatibility with
+ #' the TensorFlow API. Not used here.
+ #'
+ #' @return An object of class [`GaussianMixturePerslayWeight`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' weight <- GaussianMixturePerslayWeight$new(
+ #' gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+ #' )
+ #' weight$call(dgms)
+ initialize = function(gaussians, ...) {
+ gaussians <- gaussians |>
+ as.matrix() |>
+ tf$constant(dtype = tf$float32)
+ super$set_python_class(
+ tfInterface$perslay$GaussianMixturePerslayWeight(
+ gaussians = gaussians,
+ ...
+ )
+ )
+ }
+ )
+)
+
+#' Perslay: Grid Perslay Weight
+#'
+#' @description This is a class for computing a differentiable weight function
+#' for persistence diagram points. This function is defined from an array that
+#' contains its values on a 2D grid.
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+GridPerslayWeight <- R6::R6Class(
+ classname = "GridPerslayWeight",
+ inherit = BasePerslayWeightClass,
+ public = list(
+ #' @description The [`GridPerslayWeight`] constructor.
+ #'
+ #' @param grid A numeric matrix of shape \eqn{n \times n} specifying the
+ #' grid of values.
+ #' @param grid_bnds A numeric matrix of shape \eqn{2 \times 2} specifying
+ #' the boundaries of the grid. It should be of the form
+ #' \eqn{\begin{bmatrix} x_{min} & x_{max} \\ y_{min} & y_{max}
+ #' \end{bmatrix}}.
+ #' @param ... A named list providing extra arguments for compatibility with
+ #' the TensorFlow API. Not used here.
+ #'
+ #' @return An object of class [`GridPerslayWeight`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' weight <- GridPerslayWeight$new(
+ #' grid = matrix(c(1:13, 12:1), 5, 5),
+ #' grid_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5))
+ #' )
+ #' weight$call(dgms)
+ initialize = function(grid, grid_bnds, ...) {
+ grid <- grid |>
+ as.matrix() |>
+ tf$constant(dtype = tf$float32)
+ grid_bnds <- grid_bnds |>
+ as.matrix() |>
+ tf$constant(dtype = tf$float32)
+ super$set_python_class(
+ tfInterface$perslay$GridPerslayWeight(
+ grid = grid,
+ grid_bnds = grid_bnds,
+ ...
+ )
+ )
+ }
+ )
+)
+
+#' Perslay: Power Perslay Weight
+#'
+#' @description This is a class for computing a differentiable weight function
+#' for persistence diagram points. This function is defined as a constant
+#' multiplied by the distance to the diagonal of the persistence diagram point
+#' raised to some power.
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+PowerPerslayWeight <- R6::R6Class(
+ classname = "PowerPerslayWeight",
+ inherit = PerslayBaseClass,
+ public = list(
+ #' @description The [`PowerPerslayWeight`] constructor.
+ #'
+ #' @param constant A numeric value specifying the constant of the
+ #' transformation.
+ #' @param power A numeric value specifying the power of the transformation
+ #' which will be applied to the distance to the diagonal.
+ #' @param ... A named list providing extra arguments for compatibility with
+ #' the TensorFlow API. Not used here.
+ #'
+ #' @return An object of class [`PowerPerslayWeight`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' weight <- PowerPerslayWeight$new(
+ #' constant = 1,
+ #' power = 0
+ #' )
+ #' weight$call(dgms)
+ initialize = function(constant, power, ...) {
+ constant <- as.numeric(constant)
+ power <- as.numeric(power)
+ super$set_python_class(
+ tfInterface$perslay$PowerPerslayWeight(
+ constant = constant,
+ power = power,
+ ...
+ )
+ )
+ }
+ )
+)
+
+#' Perslay: Base Perslay Phi Class
+#' @keywords internal
+BasePerslayPhiClass <- R6::R6Class(
+ classname = "BasePerslayPhiClass",
+ inherit = PerslayBaseClass,
+ public = list(
+ #' @description Apply the layer to an input, or list of inputs, and return
+ #' the result.
+ #'
+ #' @param diagrams `r doc_perslay_diagrams_param()`
+ #'
+ #' @return A ragged tensor storing the evaluations on the 1D grid of the 1D
+ #' phi functions corresponding to the persistence diagram points. The
+ #' second dimension is ragged since persistence diagrams can have
+ #' different numbers of points.
+ call = function(diagrams) {
+ super$call(diagrams)
+ }
+ )
+)
+
+#' Perslay: Flat Perslay Phi Class
+#'
+#' @description
+#' This is a class for computing a transformation function for persistence
+#' diagram points. This function turns persistence diagram points into 1D
+#' constant functions (that evaluate to half of the bar length on the bar
+#' corresponding to the point and zero elsewhere), that are then evaluated on a
+#' regular 1D grid.
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+FlatPerslayPhi <- R6::R6Class(
+ classname = "FlatPerslayPhi",
+ inherit = BasePerslayPhiClass,
+ public = list(
+ #' @description The [`FlatPerslayPhi`] constructor.
+ #'
+ #' @param samples `r doc_perslay_phi_samples_param()`
+ #' @param theta A numeric value specifying the sigmoid parameter used to
+ #' approximate the constant function with a differentiable sigmoid
+ #' function. The bigger `theta`, the closer to a constant function the
+ #' output will be.
+ #' @param ... `r doc_perslay_phi_dots_param()`
+ #'
+ #' @return An object of class [`FlatPerslayPhi`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' phi <- FlatPerslayPhi$new(
+ #' samples = c(1:13, 12:1),
+ #' theta = 0.1
+ #' )
+ #' phi$call(dgms)
+ initialize = function(samples, theta, ...) {
+ samples <- as.numeric(samples)
+ theta <- as.numeric(theta)
+ super$set_python_class(
+ tfInterface$perslay$FlatPerslayPhi(
+ samples = samples,
+ theta = theta,
+ ...
+ )
+ )
+ }
+ )
+)
+
+#' Perslay: Gaussian Perslay Phi Class
+#'
+#' @description This is a class for computing a transformation function for
+#' persistence diagram points. This function turns persistence diagram points
+#' into 2D Gaussian functions centered on the points, that are then evaluated
+#' on a regular 2D grid.
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+GaussianPerslayPhi <- R6::R6Class(
+ classname = "GaussianPerslayPhi",
+ inherit = BasePerslayPhiClass,
+ public = list(
+ #' @description The [`GaussianPerslayPhi`] constructor.
+ #'
+ #' @param image_size A length-2 integer vector specifying the size of the 2D
+ #' grid on which the phi function must be evaluated.
+ #' @param image_bnds An integer matrix of shape \eqn{2 \times 2} specifying
+ #' the boundaries of the grid on which the phi function must be evaluated.
+ #' It must be of the form \eqn{\begin{bmatrix} x_{min} & x_{max} \\
+ #' y_{min} & y_{max} \end{bmatrix}}.
+ #' @param variance A numeric value specifying the variance of the Gaussian
+ #' functions.
+ #' @param ... `r doc_perslay_phi_dots_param()`
+ #'
+ #' @return An object of class [`GaussianPerslayPhi`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' phi <- GaussianPerslayPhi$new(
+ #' image_size = c(5, 5),
+ #' image_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5)),
+ #' variance = .1
+ #' )
+ #' phi$call(dgms)
+ initialize = function(image_size, image_bnds, variance, ...) {
+ tf <- tfInterface$perslay$tf
+ image_size <- as.integer(image_size)
+ image_bnds <- image_bnds |>
+ as.matrix() |>
+ tf$constant(dtype = tf$float32)
+ variance <- as.numeric(variance)
+ super$set_python_class(
+ tfInterface$perslay$GaussianPerslayPhi(
+ image_size = image_size,
+ image_bnds = image_bnds,
+ variance = variance,
+ ...
+ )
+ )
+ }
+ )
+)
+
+#' Perslay: Tent Perslay Phi Class
+#'
+#' @description This is a class for computing a transformation function for
+#' persistence diagram points. This function turns persistence diagram points
+#' into 1D tent functions (linearly increasing on the first half of the bar
+#' corresponding to the point from zero to half of the bar length, linearly
+#' decreasing on the second half and zero elsewhere) centered on the points,
+#' that are then evaluated on a regular 1D grid.
+#'
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#' @export
+TentPerslayPhi <- R6::R6Class(
+ classname = "TentPerslayPhi",
+ inherit = BasePerslayPhiClass,
+ public = list(
+ #' @description The [`TentPerslayPhi`] constructor.
+ #'
+ #' @param samples `r doc_perslay_phi_samples_param()`
+ #' @param ... `r doc_perslay_phi_dots_param()`
+ #'
+ #' @return An object of class [`TentPerslayPhi`].
+ #'
+ #' @examplesIf reticulate::py_module_available("gudhi")
+ #' diagrams <- as_persistence_diagram_sample(list(
+ #' as_persistence_diagram(tibble::tibble(
+ #' birth = c(0, 1, 3, 6),
+ #' death = c(4, 2, 8, 8)
+ #' ))
+ #' ))
+ #' ds <- DiagramScaler$new(
+ #' use = TRUE,
+ #' scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ #' )
+ #' dgms <- diagrams |>
+ #' ds$fit_transform() |>
+ #' to_ragged_tensor()
+ #'
+ #' phi <- TentPerslayPhi$new(
+ #' samples = c(1:13, 12:1)
+ #' )
+ #' phi$call(dgms)
+ initialize = function(samples, ...) {
+ samples <- as.numeric(samples)
+ super$set_python_class(
+ tfInterface$perslay$TentPerslayPhi(
+ samples = samples,
+ ...
+ )
+ )
+ }
+ )
+)
diff --git a/R/representation-preprocessing.R b/R/representation-preprocessing.R
index b88c090..c45c6ec 100644
--- a/R/representation-preprocessing.R
+++ b/R/representation-preprocessing.R
@@ -135,6 +135,13 @@ DiagramScaler <- R6::R6Class(
#' ds$fit_transform(list(dgm))
initialize = function(use = FALSE, scalers = list()) {
private$variable_names <- c("birth", "death")
+ if (length(scalers) > 0) {
+ scalers <- purrr::map(scalers, \(.scaler) {
+ .scaler[[1]] <- as.integer(.scaler[[1]])
+ .scaler[[2]] <- .scaler[[2]]$get_python_class()
+ .scaler
+ })
+ }
super$set_python_class(
gd$representations$DiagramScaler(use = use, scalers = scalers)
)
diff --git a/R/representation-vector-methods.R b/R/representation-vector-methods.R
index 3404920..4020820 100644
--- a/R/representation-vector-methods.R
+++ b/R/representation-vector-methods.R
@@ -6,7 +6,7 @@
#' @param y An integer vector specifying persistence diagram labels (unused for
#' now).
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
#' @keywords internal
VectorRepresentationStep <- R6::R6Class(
classname = "VectorRepresentationStep",
@@ -122,7 +122,7 @@ VectorRepresentationStep <- R6::R6Class(
#' ## References
#' \insertCited{}
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
#'
#' @export
Atol <- R6::R6Class(
@@ -216,7 +216,8 @@ Atol <- R6::R6Class(
#' the Betti curve is obtained by sampling evenly using either the given
#' `sample_range` or based on the persistence diagrams.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
BettiCurve <- R6::R6Class(
classname = "BettiCurve",
@@ -300,7 +301,8 @@ BettiCurve <- R6::R6Class(
#' See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for
#' more details.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
ComplexPolynomial <- R6::R6Class(
classname = "ComplexPolynomial",
@@ -376,7 +378,8 @@ ComplexPolynomial <- R6::R6Class(
#' function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note
#' that a previous implementation was contributed by Manuel Soriano-Trigueros.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
Entropy <- R6::R6Class(
classname = "Entropy",
@@ -468,7 +471,8 @@ Entropy <- R6::R6Class(
#' given range and the corresponding vectors of samples are concatenated and
#' returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
Landscape <- R6::R6Class(
classname = "Landscape",
@@ -548,7 +552,8 @@ Landscape <- R6::R6Class(
#' then discretized into an image with pixels, which is flattened and returned
#' as a vector. See http://jmlr.org/papers/v18/16-337.html for more details.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
PersistenceImage <- R6::R6Class(
classname = "PersistenceImage",
@@ -639,7 +644,8 @@ PersistenceImage <- R6::R6Class(
#' Finally, the corresponding vector of samples is returned. See
#' https://arxiv.org/abs/1312.0308 for more details.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
Silhouette <- R6::R6Class(
classname = "Silhouette",
@@ -722,7 +728,8 @@ Silhouette <- R6::R6Class(
#' the persistence diagram points. See
#' https://diglib.eg.org/handle/10.1111/cgf12692 for more details.
#'
-#' @author Mathieu Carrière
+#' @author Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+#'
#' @export
TopologicalVector <- R6::R6Class(
classname = "TopologicalVector",
diff --git a/R/tensorflow-module.R b/R/tensorflow-module.R
new file mode 100644
index 0000000..145aa23
--- /dev/null
+++ b/R/tensorflow-module.R
@@ -0,0 +1,15 @@
+#' Main TensorFlow module
+#'
+#' Interface to main TensorFlow module. Provides access to top level classes
+#' and functions as well as sub-modules (e.g. \code{tf$nn},
+#' \code{tf$contrib$learn}, etc.).
+#'
+#' @format TensorFlow module
+#'
+#' @examplesIf reticulate::py_module_available("tensorflow")
+#' hello <- tf$constant('Hello, TensorFlow!')
+#' zeros <- tf$Variable(tf$zeros(tf$shape(1L)))
+#' tf$print(hello)
+#' tf$print(zeros)
+#' @export
+tf <- NULL
diff --git a/R/utils.R b/R/utils.R
index 1ed3d20..184cdd4 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -1,12 +1,12 @@
-utils::globalVariables("tf")
+utils::globalVariables("workfile")
read_off_file <- function(off_file) {
if (fs::path_ext(off_file) != "off")
cli::cli_abort("The input file should have extension {.code .off}.")
if (substr(off_file, 1, 5) == "https") {
- withr::with_tempfile("tf", {
- download_file(off_file, tf)
- gd$read_points_from_off_file(tf)
+ withr::with_tempfile("workfile", {
+ download_file(off_file, workfile)
+ gd$read_points_from_off_file(workfile)
})
} else
gd$read_points_from_off_file(off_file)
@@ -45,3 +45,50 @@ capture_extra_params <- function(...) {
cli::cli_abort("All extra-arguments should be named arguments.")
dots
}
+
+#' Convert Persistence Diagrams to Ragged Tensors
+#'
+#' @param diagrams A list of numeric matrices of dimension \eqn{n \times 2}
+#' specifying a sample of \eqn{n} persistence diagrams.
+#'
+#' @return A ragged tensor of shape \eqn{n \times \mathrm{None} \times 2}.
+#'
+#' @export
+#' @examples
+#' dgm <- list(
+#' matrix(c(0, 1, 0, 2), ncol = 2, byrow = TRUE),
+#' matrix(c(0, 1, 0, 2, 0, 3), ncol = 2, byrow = TRUE)
+#' )
+#' dgm
+#' to_ragged_tensor(dgm)
+to_ragged_tensor <- function(diagrams) {
+ diagrams |>
+ purrr::map(as.matrix) |>
+ purrr::map(\(dgm) purrr::array_tree(dgm, margin = 1)) |>
+ tf$ragged$constant(ragged_rank = 1L, dtype = tf$float32)
+}
+
+to_diagrams <- function(ragged_tensor) {
+ ragged_tensor$to_list() |>
+ purrr::map(\(dgm) do.call(rbind, dgm)) |>
+ purrr::map(\(dgm) `colnames<-`(dgm, c("birth", "death"))) |>
+ purrr::map(tibble::as_tibble) |>
+ purrr::map(as_persistence_diagram) |>
+ as_persistence_diagram_sample()
+}
+
+doc_perslay_diagrams_param <- function() {
+ "A ragged tensor of shape \\eqn{n \\times \\mathrm{None} \\times 2} specifying
+ a sample of \\eqn{n} persistence diagrams. The second dimension is ragged
+ because the number of points in each diagram may vary."
+}
+
+doc_perslay_phi_samples_param <- function() {
+ "A numeric vector specifying the grid elements on which to evaluate the phi
+ function."
+}
+
+doc_perslay_phi_dots_param <- function() {
+ "A named list providing extra arguments for compatibility with the TensorFlow
+ API. Not used here."
+}
diff --git a/R/zzz.R b/R/zzz.R
index 4aa7f74..f3829be 100644
--- a/R/zzz.R
+++ b/R/zzz.R
@@ -4,13 +4,34 @@ skl_preprocessing <- NULL
skl_cluster <- NULL
# global reference to gudhi (will be initialized in .onLoad)
gd <- NULL
+# global reference to tensorflow interface (will be initialized in .onLoad)
+tfInterface <- NULL
.onLoad <- function(libname, pkgname) {
reticulate::configure_environment(pkgname)
# use superassignment to update global reference to sklearn.preprocessing
- skl_preprocessing <<- reticulate::import("sklearn.preprocessing", delay_load = TRUE, convert = TRUE)
+ skl_preprocessing <<- reticulate::import(
+ module = "sklearn.preprocessing",
+ delay_load = TRUE,
+ convert = TRUE
+ )
# use superassignment to update global reference to sklearn.preprocessing
- skl_cluster <<- reticulate::import("sklearn.cluster", delay_load = TRUE, convert = TRUE)
+ skl_cluster <<- reticulate::import(
+ module = "sklearn.cluster",
+ delay_load = TRUE,
+ convert = TRUE
+ )
# use superassignment to update global reference to gudhi
- gd <<- reticulate::import("gudhi", delay_load = TRUE, convert = TRUE)
+ gd <<- reticulate::import(
+ module = "gudhi",
+ delay_load = TRUE,
+ convert = TRUE
+ )
+ # use superassignment to update global reference to tf interface
+ tfInterface <<- reticulate::import(
+ module = "gudhi.tensorflow",
+ delay_load = TRUE,
+ convert = TRUE
+ )
+ tf <<- tfInterface$perslay$tf
}
diff --git a/_pkgdown.yml b/_pkgdown.yml
index da0fe70..de77496 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -63,6 +63,22 @@ reference:
- MinMaxScaler
- RobustScaler
- StandardScaler
+ - title: Perslay
+ - subtitle: Tensorflow layer
+ - contents:
+ - tf
+ - to_ragged_tensor
+ - Perslay
+ - subtitle: Weight functions
+ - contents:
+ - GaussianMixturePerslayWeight
+ - GridPerslayWeight
+ - PowerPerslayWeight
+ - subtitle: Phi functions
+ - contents:
+ - FlatPerslayPhi
+ - GaussianPerslayPhi
+ - TentPerslayPhi
- title: Clustering
- contents:
- AffinityPropagation
diff --git a/inst/REFERENCES.bib b/inst/REFERENCES.bib
index e42721a..ba47852 100644
--- a/inst/REFERENCES.bib
+++ b/inst/REFERENCES.bib
@@ -42,3 +42,20 @@ @inproceedings{royer2021atol
year={2021},
organization={PMLR}
}
+
+
+@InProceedings{carriere2020perslay,
+ title = {PersLay: A Neural Network Layer for Persistence Diagrams and New Graph Topological Signatures},
+ author = {Carriere, Mathieu and Chazal, Frederic and Ike, Yuichi and Lacombe, Theo and Royer, Martin and Umeda, Yuhei},
+ booktitle = {Proceedings of the Twenty Third International Conference on Artificial Intelligence and Statistics},
+ pages = {2786--2796},
+ year = {2020},
+ editor = {Chiappa, Silvia and Calandra, Roberto},
+ volume = {108},
+ series = {Proceedings of Machine Learning Research},
+ month = {26--28 Aug},
+ publisher = {PMLR},
+ pdf = {http://proceedings.mlr.press/v108/carriere20a/carriere20a.pdf},
+ url = {https://proceedings.mlr.press/v108/carriere20a.html},
+ abstract = {Persistence diagrams, the most common descriptors of Topological Data Analysis, encode topological properties of data and have already proved pivotal in many different applications of data science. However, since the metric space of persistence diagrams is not Hilbert, they end up being difficult inputs for most Machine Learning techniques. To address this concern, several vectorization methods have been put forward that embed persistence diagrams into either finite-dimensional Euclidean space or implicit infinite dimensional Hilbert space with kernels. In this work, we focus on persistence diagrams built on top of graphs. Relying on extended persistence theory and the so-called heat kernel signature, we show how graphs can be encoded by (extended) persistence diagrams in a provably stable way. We then propose a general and versatile framework for learning vectorizations of persistence diagrams, which encompasses most of the vectorization techniques used in the literature. We finally showcase the experimental strength of our setup by achieving competitive scores on classification tasks on real-life graph datasets.}
+}
diff --git a/man/Atol.Rd b/man/Atol.Rd
index 35cc469..9e6cba0 100644
--- a/man/Atol.Rd
+++ b/man/Atol.Rd
@@ -27,7 +27,7 @@ vr$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{Atol}
diff --git a/man/BasePerslayPhiClass.Rd b/man/BasePerslayPhiClass.Rd
new file mode 100644
index 0000000..ba9d409
--- /dev/null
+++ b/man/BasePerslayPhiClass.Rd
@@ -0,0 +1,74 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{BasePerslayPhiClass}
+\alias{BasePerslayPhiClass}
+\title{Perslay: Base Perslay Phi Class}
+\description{
+Perslay: Base Perslay Phi Class
+
+Perslay: Base Perslay Phi Class
+}
+\keyword{internal}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{BasePerslayPhiClass}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-BasePerslayPhiClass-call}{\code{BasePerslayPhiClass$call()}}
+\item \href{#method-BasePerslayPhiClass-clone}{\code{BasePerslayPhiClass$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-BasePerslayPhiClass-call}{}}}
+\subsection{Method \code{call()}}{
+Apply the layer to an input, or list of inputs, and return
+the result.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{BasePerslayPhiClass$call(diagrams)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{diagrams}}{A ragged tensor of shape \eqn{n \times \mathrm{None} \times 2} specifying
+a sample of \eqn{n} persistence diagrams. The second dimension is ragged
+because the number of points in each diagram may vary.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+A ragged tensor storing the evaluations on the 1D grid of the 1D
+phi functions corresponding to the persistence diagram points. The
+second dimension is ragged since persistence diagrams can have
+different numbers of points.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-BasePerslayPhiClass-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{BasePerslayPhiClass$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/BasePerslayWeightClass.Rd b/man/BasePerslayWeightClass.Rd
new file mode 100644
index 0000000..d536c97
--- /dev/null
+++ b/man/BasePerslayWeightClass.Rd
@@ -0,0 +1,72 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{BasePerslayWeightClass}
+\alias{BasePerslayWeightClass}
+\title{Perslay: Base Perslay Weight Class}
+\description{
+This is a base class for computing a differentiable weight
+function for persistence diagram points.
+}
+\keyword{internal}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{BasePerslayWeightClass}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-BasePerslayWeightClass-call}{\code{BasePerslayWeightClass$call()}}
+\item \href{#method-BasePerslayWeightClass-clone}{\code{BasePerslayWeightClass$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-BasePerslayWeightClass-call}{}}}
+\subsection{Method \code{call()}}{
+Apply the layer to an input, or list of inputs, and return
+the result.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{BasePerslayWeightClass$call(diagrams)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{diagrams}}{A ragged tensor of shape \eqn{n \times \mathrm{None} \times 2} specifying
+a sample of \eqn{n} persistence diagrams. The second dimension is ragged
+because the number of points in each diagram may vary.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+A ragged tensor storing the weights of the points in the \eqn{n}
+persistence diagrams. The second dimension is ragged since persistence
+diagrams can have different numbers of points.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-BasePerslayWeightClass-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{BasePerslayWeightClass$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/BettiCurve.Rd b/man/BettiCurve.Rd
index 26e253f..935248c 100644
--- a/man/BettiCurve.Rd
+++ b/man/BettiCurve.Rd
@@ -31,7 +31,7 @@ bc$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{BettiCurve}
diff --git a/man/ComplexPolynomial.Rd b/man/ComplexPolynomial.Rd
index 1e75d13..6c8ac83 100644
--- a/man/ComplexPolynomial.Rd
+++ b/man/ComplexPolynomial.Rd
@@ -24,7 +24,7 @@ cp$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{ComplexPolynomial}
diff --git a/man/Entropy.Rd b/man/Entropy.Rd
index b7cc65f..edcf794 100644
--- a/man/Entropy.Rd
+++ b/man/Entropy.Rd
@@ -24,7 +24,7 @@ ent$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{Entropy}
diff --git a/man/FlatPerslayPhi.Rd b/man/FlatPerslayPhi.Rd
new file mode 100644
index 0000000..5d8b64a
--- /dev/null
+++ b/man/FlatPerslayPhi.Rd
@@ -0,0 +1,105 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{FlatPerslayPhi}
+\alias{FlatPerslayPhi}
+\title{Perslay: Flat Perslay Phi Class}
+\description{
+This is a class for computing a transformation function for persistence
+diagram points. This function turns persistence diagram points into 1D
+constant functions (that evaluate to half of the bar length on the bar
+corresponding to the point and zero elsewhere), that are then evaluated on a
+regular 1D grid.
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+phi <- FlatPerslayPhi$new(
+ samples = c(1:13, 12:1),
+ theta = 0.1
+)
+phi$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{\link[rgudhi:BasePerslayPhiClass]{rgudhi::BasePerslayPhiClass}} -> \code{FlatPerslayPhi}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-FlatPerslayPhi-new}{\code{FlatPerslayPhi$new()}}
+\item \href{#method-FlatPerslayPhi-clone}{\code{FlatPerslayPhi$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-FlatPerslayPhi-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{FlatPerslayPhi}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{FlatPerslayPhi$new(samples, theta, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{samples}}{A numeric vector specifying the grid elements on which to evaluate the phi
+function.}
+
+\item{\code{theta}}{A numeric value specifying the sigmoid parameter used to
+approximate the constant function with a differentiable sigmoid
+function. The bigger \code{theta}, the closer to a constant function the
+output will be.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with the TensorFlow
+API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{FlatPerslayPhi}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-FlatPerslayPhi-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{FlatPerslayPhi$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/GaussianMixturePerslayWeight.Rd b/man/GaussianMixturePerslayWeight.Rd
new file mode 100644
index 0000000..430ed9b
--- /dev/null
+++ b/man/GaussianMixturePerslayWeight.Rd
@@ -0,0 +1,99 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{GaussianMixturePerslayWeight}
+\alias{GaussianMixturePerslayWeight}
+\title{Perslay: Gaussian Mixture Perslay Weight}
+\description{
+This is a class for computing a differentiable weight function
+for persistence diagram points. This function is defined from a mixture of
+Gaussian functions.
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+weight <- GaussianMixturePerslayWeight$new(
+ gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+)
+weight$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{\link[rgudhi:BasePerslayWeightClass]{rgudhi::BasePerslayWeightClass}} -> \code{GaussianMixturePerslayWeight}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-GaussianMixturePerslayWeight-new}{\code{GaussianMixturePerslayWeight$new()}}
+\item \href{#method-GaussianMixturePerslayWeight-clone}{\code{GaussianMixturePerslayWeight$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-GaussianMixturePerslayWeight-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{GaussianMixturePerslayWeight}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{GaussianMixturePerslayWeight$new(gaussians, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{gaussians}}{A numeric matrix of size \eqn{4 \times n} specifying a
+two-dimensional Gaussian distribution for each of the \eqn{n} diagrams.
+Each column must store the Gaussian parameters in the following order:
+\eqn{\mu_x, \mu_y, \sigma_x, \sigma_y}.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with
+the TensorFlow API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{GaussianMixturePerslayWeight}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-GaussianMixturePerslayWeight-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{GaussianMixturePerslayWeight$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/GaussianPerslayPhi.Rd b/man/GaussianPerslayPhi.Rd
new file mode 100644
index 0000000..6e51483
--- /dev/null
+++ b/man/GaussianPerslayPhi.Rd
@@ -0,0 +1,108 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{GaussianPerslayPhi}
+\alias{GaussianPerslayPhi}
+\title{Perslay: Gaussian Perslay Phi Class}
+\description{
+This is a class for computing a transformation function for
+persistence diagram points. This function turns persistence diagram points
+into 2D Gaussian functions centered on the points, that are then evaluated
+on a regular 2D grid.
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+phi <- GaussianPerslayPhi$new(
+ image_size = c(5, 5),
+ image_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5)),
+ variance = .1
+)
+phi$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{\link[rgudhi:BasePerslayPhiClass]{rgudhi::BasePerslayPhiClass}} -> \code{GaussianPerslayPhi}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-GaussianPerslayPhi-new}{\code{GaussianPerslayPhi$new()}}
+\item \href{#method-GaussianPerslayPhi-clone}{\code{GaussianPerslayPhi$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-GaussianPerslayPhi-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{GaussianPerslayPhi}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{GaussianPerslayPhi$new(image_size, image_bnds, variance, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{image_size}}{A length-2 integer vector specifying the size of the 2D
+grid on which the phi function must be evaluated.}
+
+\item{\code{image_bnds}}{An integer matrix of shape \eqn{2 \times 2} specifying
+the boundaries of the grid on which the phi function must be evaluated.
+It must be of the form \eqn{\begin{bmatrix} x_{min} & x_{max} \\
+ y_{min} & y_{max} \end{bmatrix}}.}
+
+\item{\code{variance}}{A numeric value specifying the variance of the Gaussian
+functions.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with the TensorFlow
+API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{GaussianPerslayPhi}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-GaussianPerslayPhi-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{GaussianPerslayPhi$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/GridPerslayWeight.Rd b/man/GridPerslayWeight.Rd
new file mode 100644
index 0000000..d2ab590
--- /dev/null
+++ b/man/GridPerslayWeight.Rd
@@ -0,0 +1,103 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{GridPerslayWeight}
+\alias{GridPerslayWeight}
+\title{Perslay: Grid Perslay Weight}
+\description{
+This is a class for computing a differentiable weight function
+for persistence diagram points. This function is defined from an array that
+contains its values on a 2D grid.
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+weight <- GridPerslayWeight$new(
+ grid = matrix(c(1:13, 12:1), 5, 5),
+ grid_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5))
+)
+weight$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{\link[rgudhi:BasePerslayWeightClass]{rgudhi::BasePerslayWeightClass}} -> \code{GridPerslayWeight}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-GridPerslayWeight-new}{\code{GridPerslayWeight$new()}}
+\item \href{#method-GridPerslayWeight-clone}{\code{GridPerslayWeight$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-GridPerslayWeight-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{GridPerslayWeight}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{GridPerslayWeight$new(grid, grid_bnds, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{grid}}{A numeric matrix of shape \eqn{n \times n} specifying the
+grid of values.}
+
+\item{\code{grid_bnds}}{A numeric matrix of shape \eqn{2 \times 2} specifying
+the boundaries of the grid. It should be of the form
+\eqn{\begin{bmatrix} x_{min} & x_{max} \\ y_{min} & y_{max}
+ \end{bmatrix}}.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with
+the TensorFlow API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{GridPerslayWeight}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-GridPerslayWeight-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{GridPerslayWeight$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/Landscape.Rd b/man/Landscape.Rd
index 66f1e65..72f45db 100644
--- a/man/Landscape.Rd
+++ b/man/Landscape.Rd
@@ -25,7 +25,7 @@ lds$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{Landscape}
diff --git a/man/PersistenceImage.Rd b/man/PersistenceImage.Rd
index ddb0419..d9e64cc 100644
--- a/man/PersistenceImage.Rd
+++ b/man/PersistenceImage.Rd
@@ -24,7 +24,7 @@ pei$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{PersistenceImage}
diff --git a/man/Perslay.Rd b/man/Perslay.Rd
new file mode 100644
index 0000000..8562e3e
--- /dev/null
+++ b/man/Perslay.Rd
@@ -0,0 +1,193 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{Perslay}
+\alias{Perslay}
+\title{Perslay: Main Class}
+\description{
+This is a TensorFlow layer for vectorizing persistence diagrams
+in a differentiable way within a neural network. This function implements
+the PersLay equation \insertCite{carriere2020perslay}{rgudhi}.
+\subsection{References}{
+
+\insertCited{}
+}
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+phi <- FlatPerslayPhi$new(
+ samples = c(1:13, 12:1),
+ theta = 0.1
+)
+weight <- GaussianMixturePerslayWeight$new(
+ gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+)
+perm_op <- tf$math$reduce_sum
+rho <- tf$identity
+
+perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+)
+\dontshow{\}) # examplesIf}
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+phi <- FlatPerslayPhi$new(
+ samples = c(1:13, 12:1),
+ theta = 0.1
+)
+weight <- GaussianMixturePerslayWeight$new(
+ gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+)
+perm_op <- tf$math$reduce_sum
+rho <- tf$identity
+
+perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+)
+
+vectors <- perslay$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{Perslay}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-Perslay-new}{\code{Perslay$new()}}
+\item \href{#method-Perslay-call}{\code{Perslay$call()}}
+\item \href{#method-Perslay-clone}{\code{Perslay$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-Perslay-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{Perslay}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{Perslay$new(weight, phi, perm_op, rho, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{weight}}{A weight function for the persistence diagram points. Can
+be either an object of class \code{\link{GridPerslayWeight}},
+\code{\link{GaussianMixturePerslayWeight}}, \code{\link{PowerPerslayWeight}} or a custom
+TensorFlow function that takes persistence diagrams as argument
+(represented as an \eqn{n \times \mathrm{None} \times 2} ragged tensor,
+where \eqn{n} is the number of diagrams).}
+
+\item{\code{phi}}{A transformation function for the persistence diagram points.
+Can be either an object of class \code{\link{GaussianPerslayPhi}},
+\code{\link{TentPerslayPhi}}, \code{\link{FlatPerslayPhi}} or a custom TensorFlow class
+(that can have trainable parameters) with a method \code{call()} that takes
+persistence diagrams as argument (represented as an \eqn{n \times
+ \mathrm{None} \times 2} ragged tensor, where \eqn{n} is the number of
+diagrams).}
+
+\item{\code{perm_op}}{A permutation invariant function, such as
+\code{tf$math$reduce_sum}, \code{tf$math$reduce_mean}, \code{tf$math$reduce_max},
+\code{tf$math$reduce_min} or a custom TensorFlow function that takes two
+arguments: a tensor and an axis on which to apply the permutation
+invariant operation. If \code{perm_op} is the string \code{"topk"} (where \eqn{k}
+is a number), this function will be computed as \code{tf$math$top_k} with
+integer parameter \code{k}.}
+
+\item{\code{rho}}{A postprocessing function that is applied after the
+permutation invariant operation. Can be any TensorFlow layer.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with
+the TensorFlow API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{Perslay}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-Perslay-call}{}}}
+\subsection{Method \code{call()}}{
+Applies Perslay on a ragged tensor containing a list of
+persistence diagrams.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{Perslay$call(diagrams)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{diagrams}}{A ragged tensor of shape \eqn{n \times \mathrm{None} \times 2} specifying
+a sample of \eqn{n} persistence diagrams. The second dimension is ragged
+because the number of points in each diagram may vary.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+A tensor storing the vectorizations of the persistence diagrams.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-Perslay-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{Perslay$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/PerslayBaseClass.Rd b/man/PerslayBaseClass.Rd
new file mode 100644
index 0000000..f2dbdb8
--- /dev/null
+++ b/man/PerslayBaseClass.Rd
@@ -0,0 +1,103 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{PerslayBaseClass}
+\alias{PerslayBaseClass}
+\title{Perslay: Base Class}
+\description{
+Perslay: Base Class
+
+Perslay: Base Class
+}
+\keyword{internal}
+\section{Super class}{
+\code{rgudhi::PythonClass} -> \code{PerslayBaseClass}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-PerslayBaseClass-build}{\code{PerslayBaseClass$build()}}
+\item \href{#method-PerslayBaseClass-call}{\code{PerslayBaseClass$call()}}
+\item \href{#method-PerslayBaseClass-clone}{\code{PerslayBaseClass$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-PerslayBaseClass-build}{}}}
+\subsection{Method \code{build()}}{
+Creates the variables of the layer (optional, for subclass
+implementers).
+
+This is a method that implementers of subclasses of \code{Layer} or \code{Model}
+can override if they need a state-creation step in-between layer
+instantiation and layer call. It is invoked automatically before the
+first execution of \code{call()}.
+
+This is typically used to create the weights of \code{Layer} subclasses (at
+the discretion of the subclass implementer).
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{PerslayBaseClass$build(input_shape)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{input_shape}}{Instance of \code{TensorShape}, or list of instances of
+\code{TensorShape} if the layer expects a list of inputs (one instance per
+input).}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+The class itself invisibly.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-PerslayBaseClass-call}{}}}
+\subsection{Method \code{call()}}{
+Apply the layer to an input, or list of inputs, and return
+the result.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{PerslayBaseClass$call(diagrams)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{diagrams}}{A ragged tensor of shape \eqn{n \times \mathrm{None} \times 2} specifying
+a sample of \eqn{n} persistence diagrams. The second dimension is ragged
+because the number of points in each diagram may vary.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+Either a ragged tensor or a tensor storing the result of the
+layer.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-PerslayBaseClass-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{PerslayBaseClass$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/PowerPerslayWeight.Rd b/man/PowerPerslayWeight.Rd
new file mode 100644
index 0000000..f55aa85
--- /dev/null
+++ b/man/PowerPerslayWeight.Rd
@@ -0,0 +1,102 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{PowerPerslayWeight}
+\alias{PowerPerslayWeight}
+\title{Perslay: Power Perslay Weight}
+\description{
+This is a class for computing a differentiable weight function
+for persistence diagram points. This function is defined as a constant
+multiplied by the distance to the diagonal of the persistence diagram point
+raised to some power.
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+weight <- PowerPerslayWeight$new(
+ constant = 1,
+ power = 0
+)
+weight$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{PowerPerslayWeight}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-PowerPerslayWeight-new}{\code{PowerPerslayWeight$new()}}
+\item \href{#method-PowerPerslayWeight-clone}{\code{PowerPerslayWeight$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-PowerPerslayWeight-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{PowerPerslayWeight}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{PowerPerslayWeight$new(constant, power, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{constant}}{A numeric value specifying the constant of the
+transformation.}
+
+\item{\code{power}}{A numeric value specifying the power of the transformation
+which will be applied to the distance to the diagonal.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with
+the TensorFlow API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{PowerPerslayWeight}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-PowerPerslayWeight-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{PowerPerslayWeight$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/Silhouette.Rd b/man/Silhouette.Rd
index a897f33..8b56996 100644
--- a/man/Silhouette.Rd
+++ b/man/Silhouette.Rd
@@ -25,7 +25,7 @@ sil$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{Silhouette}
diff --git a/man/TentPerslayPhi.Rd b/man/TentPerslayPhi.Rd
new file mode 100644
index 0000000..704a046
--- /dev/null
+++ b/man/TentPerslayPhi.Rd
@@ -0,0 +1,100 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/perslay.R
+\name{TentPerslayPhi}
+\alias{TentPerslayPhi}
+\title{Perslay: Tent Perslay Phi Class}
+\description{
+This is a class for computing a transformation function for
+persistence diagram points. This function turns persistence diagram points
+into 1D tent functions (linearly increasing on the first half of the bar
+corresponding to the point from zero to half of the bar length, linearly
+decreasing on the second half and zero elsewhere) centered on the points,
+that are then evaluated on a regular 1D grid.
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("gudhi")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+))
+ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+)
+dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+phi <- TentPerslayPhi$new(
+ samples = c(1:13, 12:1)
+)
+phi$call(dgms)
+\dontshow{\}) # examplesIf}
+}
+\author{
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
+}
+\section{Super classes}{
+\code{rgudhi::PythonClass} -> \code{\link[rgudhi:PerslayBaseClass]{rgudhi::PerslayBaseClass}} -> \code{\link[rgudhi:BasePerslayPhiClass]{rgudhi::BasePerslayPhiClass}} -> \code{TentPerslayPhi}
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-TentPerslayPhi-new}{\code{TentPerslayPhi$new()}}
+\item \href{#method-TentPerslayPhi-clone}{\code{TentPerslayPhi$clone()}}
+}
+}
+\if{html}{\out{
+Inherited methods
+
+
+}}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-TentPerslayPhi-new}{}}}
+\subsection{Method \code{new()}}{
+The \code{\link{TentPerslayPhi}} constructor.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{TentPerslayPhi$new(samples, ...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{samples}}{A numeric vector specifying the grid elements on which to evaluate the phi
+function.}
+
+\item{\code{...}}{A named list providing extra arguments for compatibility with the TensorFlow
+API. Not used here.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+An object of class \code{\link{TentPerslayPhi}}.
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-TentPerslayPhi-clone}{}}}
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{TentPerslayPhi$clone(deep = FALSE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/TopologicalVector.Rd b/man/TopologicalVector.Rd
index c8148fc..571d224 100644
--- a/man/TopologicalVector.Rd
+++ b/man/TopologicalVector.Rd
@@ -24,7 +24,7 @@ tv$fit_transform(list(dgm))
\dontshow{\}) # examplesIf}
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\section{Super classes}{
\code{rgudhi::PythonClass} -> \code{rgudhi::SKLearnClass} -> \code{\link[rgudhi:VectorRepresentationStep]{rgudhi::VectorRepresentationStep}} -> \code{TopologicalVector}
diff --git a/man/VectorRepresentationStep.Rd b/man/VectorRepresentationStep.Rd
index 7bc31ab..be7b9e3 100644
--- a/man/VectorRepresentationStep.Rd
+++ b/man/VectorRepresentationStep.Rd
@@ -9,7 +9,7 @@ Vector Representation Step
Vector Representation Step
}
\author{
-Mathieu Carrière
+Mathieu Carrière, Martin Royer, Gard Spreemann, Wojciech Reise
}
\keyword{internal}
\section{Super classes}{
diff --git a/man/tf.Rd b/man/tf.Rd
new file mode 100644
index 0000000..a0e7834
--- /dev/null
+++ b/man/tf.Rd
@@ -0,0 +1,26 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/tensorflow-module.R
+\docType{data}
+\name{tf}
+\alias{tf}
+\title{Main TensorFlow module}
+\format{
+TensorFlow module
+}
+\usage{
+tf
+}
+\description{
+Interface to main TensorFlow module. Provides access to top level classes
+and functions as well as sub-modules (e.g. \code{tf$nn},
+\code{tf$contrib$learn}, etc.).
+}
+\examples{
+\dontshow{if (reticulate::py_module_available("tensorflow")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
+hello <- tf$constant('Hello, TensorFlow!')
+zeros <- tf$Variable(tf$zeros(tf$shape(1L)))
+tf$print(hello)
+tf$print(zeros)
+\dontshow{\}) # examplesIf}
+}
+\keyword{datasets}
diff --git a/man/to_ragged_tensor.Rd b/man/to_ragged_tensor.Rd
new file mode 100644
index 0000000..9082d58
--- /dev/null
+++ b/man/to_ragged_tensor.Rd
@@ -0,0 +1,26 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils.R
+\name{to_ragged_tensor}
+\alias{to_ragged_tensor}
+\title{Convert Persistence Diagrams to Ragged Tensors}
+\usage{
+to_ragged_tensor(diagrams)
+}
+\arguments{
+\item{diagrams}{A list of numeric matrices of dimension \eqn{n \times 2}
+specifying a sample of \eqn{n} persistence diagrams.}
+}
+\value{
+A ragged tensor of shape \eqn{n \times \mathrm{None} \times 2}.
+}
+\description{
+Convert Persistence Diagrams to Ragged Tensors
+}
+\examples{
+dgm <- list(
+ matrix(c(0, 1, 0, 2), ncol = 2, byrow = TRUE),
+ matrix(c(0, 1, 0, 2, 0, 3), ncol = 2, byrow = TRUE)
+)
+dgm
+to_ragged_tensor(dgm)
+}
diff --git a/tests/testthat/_snaps/perslay.md b/tests/testthat/_snaps/perslay.md
new file mode 100644
index 0000000..e19afd5
--- /dev/null
+++ b/tests/testthat/_snaps/perslay.md
@@ -0,0 +1,173 @@
+# perslay works with Gaussian mixture weight and flat phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with grid weight and flat phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with power weight and flat phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with Gaussian mixture weight and Gaussian phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with grid weight and Gaussian phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with power weight and Gaussian phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with Gaussian mixture weight and tent phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with grid weight and tent phi
+
+ Code
+ vectors
+ Output
+
+
+# perslay works with power weight and tent phi
+
+ Code
+ vectors
+ Output
+
+
diff --git a/tests/testthat/test-perslay.R b/tests/testthat/test-perslay.R
new file mode 100644
index 0000000..f1efb14
--- /dev/null
+++ b/tests/testthat/test-perslay.R
@@ -0,0 +1,330 @@
+test_that("perslay works with Gaussian mixture weight and flat phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- FlatPerslayPhi$new(
+ samples = c(1:13, 12:1),
+ theta = 0.1
+ )
+ weight <- GaussianMixturePerslayWeight$new(
+ gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1)
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with grid weight and flat phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- FlatPerslayPhi$new(
+ samples = c(1:13, 12:1),
+ theta = 0.1
+ )
+ weight <- GridPerslayWeight$new(
+ grid = matrix(c(1:13, 12:1), 5, 5),
+ grid_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5))
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with power weight and flat phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- FlatPerslayPhi$new(
+ samples = c(1:13, 12:1),
+ theta = 0.1
+ )
+ weight <- PowerPerslayWeight$new(
+ constant = 1,
+ power = 0
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+
+test_that("perslay works with Gaussian mixture weight and Gaussian phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- GaussianPerslayPhi$new(
+ image_size = c(5, 5),
+ image_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5)),
+ variance = .1
+ )
+ weight <- GaussianMixturePerslayWeight$new(
+ gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1),
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with grid weight and Gaussian phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- GaussianPerslayPhi$new(
+ image_size = c(5, 5),
+ image_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5)),
+ variance = .1
+ )
+ weight <- GridPerslayWeight$new(
+ grid = matrix(c(1:13, 12:1), 5, 5),
+ grid_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5))
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with power weight and Gaussian phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- GaussianPerslayPhi$new(
+ image_size = c(5, 5),
+ image_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5)),
+ variance = .1
+ )
+ weight <- PowerPerslayWeight$new(
+ constant = 1,
+ power = 0
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with Gaussian mixture weight and tent phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- TentPerslayPhi$new(
+ samples = c(1:13, 12:1)
+ )
+ weight <- GaussianMixturePerslayWeight$new(
+ gaussians = matrix(c(0, 0, 1, 1), nrow = 4, ncol = 1),
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with grid weight and tent phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- TentPerslayPhi$new(
+ samples = c(1:13, 12:1)
+ )
+ weight <- GridPerslayWeight$new(
+ grid = matrix(c(1:13, 12:1), 5, 5),
+ grid_bnds = rbind(c(-.5, 1.5), c(-.5, 1.5))
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})
+
+test_that("perslay works with power weight and tent phi", {
+ diagrams <- as_persistence_diagram_sample(list(
+ as_persistence_diagram(tibble::tibble(
+ birth = c(0, 1, 3, 6),
+ death = c(4, 2, 8, 8)
+ ))
+ ))
+ ds <- DiagramScaler$new(
+ use = TRUE,
+ scalers = list(list(c(0, 1), MinMaxScaler$new()))
+ )
+ dgms <- diagrams |>
+ ds$fit_transform() |>
+ to_ragged_tensor()
+
+ phi <- TentPerslayPhi$new(
+ samples = c(1:13, 12:1)
+ )
+ weight <- PowerPerslayWeight$new(
+ constant = 1,
+ power = 0
+ )
+ perm_op <- tf$math$reduce_sum
+ rho <- tf$identity
+
+ perslay <- Perslay$new(
+ phi = phi,
+ weight = weight,
+ perm_op = perm_op,
+ rho = rho
+ )
+
+ vectors <- perslay$call(dgms)
+ expect_snapshot(vectors)
+})