diff --git a/NAMESPACE b/NAMESPACE index 669ec08..a418ce0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -27,6 +27,7 @@ export(evaluate) export(export) export(get_option) export(make_logo) +export(par_apply) export(par_lapply) export(par_sapply) export(peek) @@ -43,6 +44,7 @@ importFrom(parallel,clusterEvalQ) importFrom(parallel,clusterExport) importFrom(parallel,detectCores) importFrom(parallel,makeCluster) +importFrom(parallel,parApply) importFrom(parallel,parLapply) importFrom(parallel,parSapply) importFrom(parallel,stopCluster) diff --git a/NEWS.md b/NEWS.md index 43dd81a..69d67eb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,12 @@ # Development ## Added +- Update implementations of `Service$apply` operation for `Backend` classes to + validate the provided `margin` argument before running the parallel operation. +- Add helper `Helper$check_array_margins` to validate the margins provided to + the `Service$apply` operation. +- Add exception `Exception$array_margins_not_compatible` for using improper + margins in the `Service$apply` operation. - Add exception `Exception$primitive_as_task_not_allowed` for trying to decorate primitive functions with progress tracking in the `ProgressTrackingContext` class. @@ -8,8 +14,11 @@ - Add optional arguments to the `get_output` operation of `SyncBackend` for consistency. - Add more tests to improve coverage. -- Add `par_lapply` function to the user `API`. The `par_lapply` function can be - used to run tasks in parallel akin to `parallel::parLapply`. +- Add implementation for `Service$lapply` and `Service$apply` operations for all + classes that implement the `Service` interface. +- Add `par_lapply` and `par_apply` functions to the user `API`. These functions + can be used to run tasks in parallel akin to `parallel::parLapply` and + `parallel::parApply`, respectively. - Add `UserApiConsumer` `R6` class that provides an opinionated wrapper around the developer `API` of the `parabar` package. All parallel operations (e.g., `par_sapply` and `par_lapply`) follow more or less the same pattern. The diff --git a/R/AsyncBackend.R b/R/AsyncBackend.R index 71933a5..d72fded 100644 --- a/R/AsyncBackend.R +++ b/R/AsyncBackend.R @@ -240,6 +240,21 @@ AsyncBackend <- R6::R6Class("AsyncBackend", }, args = list(x, fun, dots)) }, + # Run tasks asynchronously via the cluster in the session. + .apply = function(x, margin, fun, ...) { + # Capture the `...`. + dots <- list(...) + + # Perform the evaluation from the `R` session. + private$.cluster$call(function(x, margin, fun, dots) { + # Run the task. + output <- do.call(parallel::parApply, c(list(cluster, x, margin, fun), dots)) + + # Return to the session. + return(output) + }, args = list(x, margin, fun, dots)) + }, + # Clear the current output on the backend. .clear_output = function() { # Clear output. @@ -480,6 +495,39 @@ AsyncBackend <- R6::R6Class("AsyncBackend", private$.lapply(x, fun, ...) }, + + #' @description + #' Run a task on the backend akin to [parallel::parApply()]. + #' + #' @param x An array to pass to the `fun` function. + #' + #' @param margin A numeric vector indicating the dimensions of `x` the + #' `fun` function should be applied over. For example, for a matrix, + #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` + #' indicates applying `fun` columns-wise, and `margin = c(1, 2)` + #' indicates applying `fun` element-wise. Named dimensions are also + #' possible depending on `x`. See [parallel::parApply()] and + #' [base::apply()] for more details. + #' + #' @param fun A function to apply to `x` according to the `margin`. + #' + #' @param ... Additional arguments to pass to the `fun` function. + #' + #' @return + #' This method returns void. The output of the task execution must be + #' stored in the private field `.output` on the [`parabar::Backend`] + #' abstract class, and is accessible via the `get_output()` method. + apply = function(x, margin, fun, ...) { + # Throw if backend is busy. + private$.throw_if_backend_is_busy() + + # Validate provided margins. + Helper$check_array_margins(margin, dim(x)) + + # Deploy the task asynchronously. + private$.apply(x, margin, fun, ...) + }, + #' @description #' Get the output of the task execution. #' diff --git a/R/Context.R b/R/Context.R index 4b0687f..104666e 100644 --- a/R/Context.R +++ b/R/Context.R @@ -223,6 +223,32 @@ Context <- R6::R6Class("Context", private$.backend$lapply(x = x, fun = fun, ...) }, + #' @description + #' Run a task on the backend akin to [parallel::parApply()]. + #' + #' @param x An array to pass to the `fun` function. + #' + #' @param margin A numeric vector indicating the dimensions of `x` the + #' `fun` function should be applied over. For example, for a matrix, + #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` + #' indicates applying `fun` columns-wise, and `margin = c(1, 2)` + #' indicates applying `fun` element-wise. Named dimensions are also + #' possible depending on `x`. See [parallel::parApply()] and + #' [base::apply()] for more details. + #' + #' @param fun A function to apply to `x` according to the `margin`. + #' + #' @param ... Additional arguments to pass to the `fun` function. + #' + #' @return + #' This method returns void. The output of the task execution must be + #' stored in the private field `.output` on the [`parabar::Backend`] + #' abstract class, and is accessible via the `get_output()` method. + apply = function(x, margin, fun, ...) { + # Consume the backend API. + private$.backend$apply(x = x, margin = margin, fun = fun, ...) + }, + #' @description #' Get the output of the task execution. #' diff --git a/R/Exception.R b/R/Exception.R index 1a1fc31..317de72 100644 --- a/R/Exception.R +++ b/R/Exception.R @@ -22,6 +22,7 @@ #' \item{\code{Exception$type_not_assignable()}}{Exception for when providing incorrect object types.} #' \item{\code{Exception$unknown_package_option()}}{Exception for when requesting unknown package options.} #' \item{\code{Exception$primitive_as_task_not_allowed()}}{Exception for when decorating primitive functions with progress tracking.} +#' \item{\code{Exception$array_margins_not_compatible(actual, allowed)}}{Exception for using improper margins in the `Service$apply` operation.} #' } #' #' @export @@ -127,3 +128,19 @@ Exception$primitive_as_task_not_allowed <- function() { stop(message, call. = FALSE) } +# Exception for providing incompatible margins in the `apply` operation. +Exception$array_margins_not_compatible <- function(margins, dimensions) { + # Convert the margins to character. + margins <- paste(margins, collapse = ", ") + + # Convert the dimensions to character. + dimensions <- paste(dimensions, collapse = ", ") + + # Construct exception message. + message = paste0( + "Margins {", margins, "} not compatible with array dimensions {", dimensions, "}." + ) + + # Throw the error. + stop(message, call. = FALSE) +} diff --git a/R/Helper.R b/R/Helper.R index e776993..5f4b441 100644 --- a/R/Helper.R +++ b/R/Helper.R @@ -13,6 +13,7 @@ #' \item{\code{Helper$get_option()}}{Get package option, or corresponding default value.} #' \item{\code{Helper$set_option()}}{Set package option.} #' \item{\code{Helper$check_object_type()}}{Check the type of a given object.} +#' \item{\code{Helper$check_array_margins(margins, dimensions)}}{Helper to check array margins for the `Service$apply` operation.} #' } #' #' @export @@ -74,3 +75,21 @@ Helper$check_object_type <- function(object, expected_type) { Exception$type_not_assignable(type, expected_type) } } + +# Helper for checking the array margins provided for the `apply` operation. +Helper$check_array_margins <- function(margins, dimensions) { + # Conditions to ensure the margins are valid. + violations <- c( + # Ensure all margins are unique. + duplicated(margins), + + # Ensure all margins are within the array dimensions. + margins > length(dimensions) + ) + + # If any violations are found. + if (any(violations)) { + # Throw an error. + Exception$array_margins_not_compatible(margins, dimensions) + } +} diff --git a/R/ProgressTrackingContext.R b/R/ProgressTrackingContext.R index cb84c07..cdc000b 100644 --- a/R/ProgressTrackingContext.R +++ b/R/ProgressTrackingContext.R @@ -353,6 +353,42 @@ ProgressTrackingContext <- R6::R6Class("ProgressTrackingContext", # Execute the task via the `lapply` backend operation. private$.execute(operation = operation, fun = fun, total = length(x)) + }, + + #' @description + #' Run a task on the backend akin to [parallel::parApply()]. + #' + #' @param x An array to pass to the `fun` function. + #' + #' @param margin A numeric vector indicating the dimensions of `x` the + #' `fun` function should be applied over. For example, for a matrix, + #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` + #' indicates applying `fun` columns-wise, and `margin = c(1, 2)` + #' indicates applying `fun` element-wise. Named dimensions are also + #' possible depending on `x`. See [parallel::parApply()] and + #' [base::apply()] for more details. + #' + #' @param fun A function to apply to `x` according to the `margin`. + #' + #' @param ... Additional arguments to pass to the `fun` function. + #' + #' @return + #' This method returns void. The output of the task execution must be + #' stored in the private field `.output` on the [`parabar::Backend`] + #' abstract class, and is accessible via the `get_output()` method. + apply = function(x, margin, fun, ...) { + # Determine the number of task executions. + total <- prod(dim(x)[margin]) + + # Prepare the backend operation with early evaluated `...`. + operation <- bquote( + do.call( + super$apply, c(list(x = .(x), margin = .(margin), fun = fun), .(list(...))) + ) + ) + + # Execute the task via the `lapply` backend operation. + private$.execute(operation = operation, fun = fun, total = total) } ), diff --git a/R/Service.R b/R/Service.R index 1e180da..ba9da4e 100644 --- a/R/Service.R +++ b/R/Service.R @@ -132,6 +132,31 @@ Service <- R6::R6Class("Service", Exception$method_not_implemented() }, + #' @description + #' Run a task on the backend akin to [parallel::parApply()]. + #' + #' @param x An array to pass to the `fun` function. + #' + #' @param margin A numeric vector indicating the dimensions of `x` the + #' `fun` function should be applied over. For example, for a matrix, + #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` + #' indicates applying `fun` columns-wise, and `margin = c(1, 2)` + #' indicates applying `fun` element-wise. Named dimensions are also + #' possible depending on `x`. See [parallel::parApply()] and + #' [base::apply()] for more details. + #' + #' @param fun A function to apply to `x` according to the `margin`. + #' + #' @param ... Additional arguments to pass to the `fun` function. + #' + #' @return + #' This method returns void. The output of the task execution must be + #' stored in the private field `.output` on the [`parabar::Backend`] + #' abstract class, and is accessible via the `get_output()` method. + apply = function(x, margin, fun, ...) { + Exception$method_not_implemented() + }, + #' @description #' Get the output of the task execution. #' diff --git a/R/SyncBackend.R b/R/SyncBackend.R index 7495632..7b12c04 100644 --- a/R/SyncBackend.R +++ b/R/SyncBackend.R @@ -163,6 +163,12 @@ SyncBackend <- R6::R6Class("SyncBackend", parallel::parLapply(private$.cluster, X = x, fun = fun, ...) }, + # A wrapper around `parallel:parApply` to run tasks on the cluster. + .apply = function(x, margin, fun, ...) { + # Run the task and return the results. + parallel::parApply(private$.cluster, X = x, MARGIN = margin, FUN = fun, ...) + }, + # Clear the current output on the backend. .clear_output = function() { # Clear output. @@ -310,6 +316,35 @@ SyncBackend <- R6::R6Class("SyncBackend", private$.output = private$.lapply(x, fun, ...) }, + #' @description + #' Run a task on the backend akin to [parallel::parApply()]. + #' + #' @param x An array to pass to the `fun` function. + #' + #' @param margin A numeric vector indicating the dimensions of `x` the + #' `fun` function should be applied over. For example, for a matrix, + #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` + #' indicates applying `fun` columns-wise, and `margin = c(1, 2)` + #' indicates applying `fun` element-wise. Named dimensions are also + #' possible depending on `x`. See [parallel::parApply()] and + #' [base::apply()] for more details. + #' + #' @param fun A function to apply to `x` according to the `margin`. + #' + #' @param ... Additional arguments to pass to the `fun` function. + #' + #' @return + #' This method returns void. The output of the task execution must be + #' stored in the private field `.output` on the [`parabar::Backend`] + #' abstract class, and is accessible via the `get_output()` method. + apply = function(x, margin, fun, ...) { + # Validate provided margins. + Helper$check_array_margins(margin, dim(x)) + + # Deploy the task synchronously. + private$.output = private$.apply(x, margin, fun, ...) + }, + #' @description #' Get the output of the task execution. #' diff --git a/R/UserApiConsumer.R b/R/UserApiConsumer.R index 39b86c5..b4ddc15 100644 --- a/R/UserApiConsumer.R +++ b/R/UserApiConsumer.R @@ -226,6 +226,48 @@ UserApiConsumer <- R6::R6Class("UserApiConsumer", ) ) + # Execute the `lapply` operation accordingly and return the results. + private$.execute(backend, parallel, sequential) + }, + + #' @description + #' Execute a task in parallel akin to [parallel::parApply()]. + #' + #' @param backend An object of class [`parabar::Backend`] as returned by + #' the [parabar::start_backend()] function. It can also be `NULL` to run + #' the task sequentially via [base::apply()]. + #' + #' @param x An array to pass to the `fun` function. + #' + #' @param margin A numeric vector indicating the dimensions of `x` the + #' `fun` function should be applied over. For example, for a matrix, + #' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` + #' indicates applying `fun` columns-wise, and `margin = c(1, 2)` + #' indicates applying `fun` element-wise. Named dimensions are also + #' possible depending on `x`. See [parallel::parApply()] and + #' [base::apply()] for more details. + #' + #' @param fun A function to apply to `x` according to the `margin`. + #' + #' @return + #' The dimensions of the output vary according to the `margin` argument. + #' Consult the documentation of [base::apply()] for a detailed + #' explanation on how the output is structured. + apply = function(backend, x, margin, fun, ...) { + # Prepare the sequential operation. + sequential <- bquote( + do.call( + base::apply, c(list(X = .(x), MARGIN = .(margin), FUN = .(fun)), .(list(...))) + ) + ) + + # Prepare the parallel operation. + parallel <- bquote( + do.call( + context$apply, c(list(x = .(x), margin = .(margin), fun = .(fun)), .(list(...))) + ) + ) + # Execute the `lapply` operation accordingly and return the results. private$.execute(backend, parallel, sequential) } diff --git a/R/exports.r b/R/exports.r index e8338d7..ac03162 100644 --- a/R/exports.r +++ b/R/exports.r @@ -171,6 +171,16 @@ par_lapply <- function(backend = NULL, x, fun, ...) { # Create an user API consumer. consumer <- UserApiConsumer$new() - # Execute the task using the `sapply` parallel operation. + # Execute the task using the `lapply` parallel operation. consumer$lapply(backend = backend, x = x, fun = fun, ...) } + +#' @template par-apply +#' @export +par_apply <- function(backend = NULL, x, margin, fun, ...) { + # Create an user API consumer. + consumer <- UserApiConsumer$new() + + # Execute the task using the `apply` parallel operation. + consumer$apply(backend = backend, x = x, margin = margin, fun = fun, ...) +} diff --git a/R/parabar-package.R b/R/parabar-package.R index 21a4fb5..f673403 100644 --- a/R/parabar-package.R +++ b/R/parabar-package.R @@ -17,7 +17,7 @@ # Imports. #' @importFrom parallel detectCores makeCluster stopCluster clusterExport -#' @importFrom parallel clusterEvalQ parSapply parLapply clusterCall +#' @importFrom parallel clusterEvalQ parSapply parLapply parApply clusterCall #' @importFrom R6 R6Class #' @importFrom progress progress_bar #' @importFrom callr r_session diff --git a/_pkgdown.yml b/_pkgdown.yml index 1eddfcc..1aec39e 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -14,6 +14,7 @@ reference: - evaluate - par_sapply - par_lapply + - par_apply - configure_bar - matches("get_|set_") - subtitle: Developer Classes diff --git a/man-roxygen/clear.R b/man-roxygen/clear.R index 61d6fa3..3077528 100644 --- a/man-roxygen/clear.R +++ b/man-roxygen/clear.R @@ -23,4 +23,5 @@ #' @seealso #' [parabar::start_backend()], [parabar::peek()], [parabar::export()], #' [parabar::evaluate()], [parabar::configure_bar()], [parabar::par_sapply()], -#' [parabar::par_lapply()], [parabar::stop_backend()], and [`parabar::Service`]. +#' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' and [`parabar::Service`]. diff --git a/man-roxygen/evaluate.R b/man-roxygen/evaluate.R index 88c6e0d..ef00f47 100644 --- a/man-roxygen/evaluate.R +++ b/man-roxygen/evaluate.R @@ -8,7 +8,7 @@ #' @param backend An object of class [`parabar::Backend`] as returned by the #' [parabar::start_backend()] function. #' - #' @param expression An unquoted expression to evaluate on the backend. +#' @param expression An unquoted expression to evaluate on the backend. #' #' @details #' This function is a convenience wrapper around the lower-lever API of @@ -26,4 +26,5 @@ #' @seealso #' [parabar::start_backend()], [parabar::peek()], [parabar::export()], #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()], -#' [parabar::par_lapply()], [parabar::stop_backend()], and [`parabar::Service`]. +#' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' and [`parabar::Service`]. diff --git a/man-roxygen/export.R b/man-roxygen/export.R index e751c45..8fd01ad 100644 --- a/man-roxygen/export.R +++ b/man-roxygen/export.R @@ -29,4 +29,5 @@ #' @seealso #' [parabar::start_backend()], [parabar::peek()], [parabar::evaluate()], #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()], -#' [parabar::par_lapply()], [parabar::stop_backend()], and [`parabar::Service`]. +#' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' and [`parabar::Service`]. diff --git a/man-roxygen/par-apply.R b/man-roxygen/par-apply.R new file mode 100644 index 0000000..805f701 --- /dev/null +++ b/man-roxygen/par-apply.R @@ -0,0 +1,108 @@ +#' @title +#' Run a Task in Parallel +#' +#' @description +#' This function can be used to run a task in parallel. The task is executed in +#' parallel on the specified backend, similar to [parallel::parApply()]. If +#' `backend = NULL`, the task is executed sequentially using [base::apply()]. +#' See the **Details** section for more information on how this function works. +#' +#' @param backend An object of class [`parabar::Backend`] as returned by the +#' [parabar::start_backend()] function. It can also be `NULL` to run the task +#' sequentially via [base::apply()]. The default value is `NULL`. +#' +#' @param x An array to pass to the `fun` function. +#' +#' @param margin A numeric vector indicating the dimensions of `x` the +#' `fun` function should be applied over. For example, for a matrix, +#' `margin = 1` indicates applying `fun` rows-wise, `margin = 2` +#' indicates applying `fun` columns-wise, and `margin = c(1, 2)` +#' indicates applying `fun` element-wise. Named dimensions are also +#' possible depending on `x`. See [parallel::parApply()] and +#' [base::apply()] for more details. +#' +#' @param fun A function to apply to `x` according to the `margin`. +#' +#' @param ... Additional arguments to pass to the `fun` function. +#' +#' @details +#' This function uses the [`parabar::UserApiConsumer`] class that acts like an +#' interface for the developer API of the [`parabar::parabar`] package. +#' +#' @return +#' The dimensions of the output vary according to the `margin` argument. Consult +#' the documentation of [base::apply()] for a detailed explanation on how the +#' output is structured. +#' +#' @examples +#' \donttest{ +#' +#' # Define a simple task. +#' task <- function(x) { +#' # Perform computations. +#' Sys.sleep(0.01) +#' +#' # Return the result. +#' mean(x) +#' } +#' +#' # Define a matrix for the task. +#' x <- matrix(rnorm(100^2, mean = 10, sd = 0.5), nrow = 100, ncol = 100) +#' +#' # Start an asynchronous backend. +#' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "async") +#' +#' # Run a task in parallel over the rows of `x`. +#' results <- par_apply(backend, x = x, margin = 1, fun = task) +#' +#' # Run a task in parallel over the columns of `x`. +#' results <- par_apply(backend, x = x, margin = 2, fun = task) +#' +#' # The task can also be run over all elements of `x` using `margin = c(1, 2)`. +#' # Improper dimensions will throw an error. +#' try(par_apply(backend, x = x, margin = c(1, 2, 3), fun = task)) +#' +#' # Disable progress tracking. +#' set_option("progress_track", FALSE) +#' +#' # Run a task in parallel. +#' results <- par_apply(backend, x = x, margin = 1, fun = task) +#' +#' # Enable progress tracking. +#' set_option("progress_track", TRUE) +#' +#' # Change the progress bar options. +#' configure_bar(type = "modern", format = "[:bar] :percent") +#' +#' # Run a task in parallel. +#' results <- par_apply(backend, x = x, margin = 1, fun = task) +#' +#' # Stop the backend. +#' stop_backend(backend) +#' +#' # Start a synchronous backend. +#' backend <- start_backend(cores = 2, cluster_type = "psock", backend_type = "sync") +#' +#' # Run a task in parallel. +#' results <- par_apply(backend, x = x, margin = 1, fun = task) +#' +#' # Disable progress tracking to remove the warning that progress is not supported. +#' set_option("progress_track", FALSE) +#' +#' # Run a task in parallel. +#' results <- par_apply(backend, x = x, margin = 1, fun = task) +#' +#' # Stop the backend. +#' stop_backend(backend) +#' +#' # Run the task using the `base::lapply` (i.e., non-parallel). +#' results <- par_apply(NULL, x = x, margin = 1, fun = task) +#' +#' } +#' +#' @seealso +#' [parabar::start_backend()], [parabar::peek()], [parabar::export()], +#' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()], +#' [parabar::par_sapply()], [parabar::par_lapply()], [parabar::stop_backend()], +#' [parabar::set_option()], [parabar::get_option()], [`parabar::Options`], +#' [`parabar::UserApiConsumer`], and [`parabar::Service`]. diff --git a/man-roxygen/par-lapply.R b/man-roxygen/par-lapply.R index b103d14..e4a9116 100644 --- a/man-roxygen/par-lapply.R +++ b/man-roxygen/par-lapply.R @@ -84,6 +84,6 @@ #' @seealso #' [parabar::start_backend()], [parabar::peek()], [parabar::export()], #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()], -#' [parabar::par_sapply()], [parabar::stop_backend()], [parabar::set_option()], -#' [parabar::get_option()], [`parabar::Options`], [`parabar::UserApiConsumer`], -#' and [`parabar::Service`]. +#' [parabar::par_sapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' [parabar::set_option()], [parabar::get_option()], [`parabar::Options`], +#' [`parabar::UserApiConsumer`], and [`parabar::Service`]. diff --git a/man-roxygen/par-sapply.R b/man-roxygen/par-sapply.R index 9361a55..8463d0b 100644 --- a/man-roxygen/par-sapply.R +++ b/man-roxygen/par-sapply.R @@ -84,6 +84,6 @@ #' @seealso #' [parabar::start_backend()], [parabar::peek()], [parabar::export()], #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()], -#' [parabar::par_lapply()], [parabar::stop_backend()], [parabar::set_option()], -#' [parabar::get_option()], [`parabar::Options`], [`parabar::UserApiConsumer`], -#' and [`parabar::Service`]. +#' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' [parabar::set_option()], [parabar::get_option()], [`parabar::Options`], +#' [`parabar::UserApiConsumer`], and [`parabar::Service`]. diff --git a/man-roxygen/parabar.R b/man-roxygen/parabar.R index faef123..b5b6f98 100644 --- a/man-roxygen/parabar.R +++ b/man-roxygen/parabar.R @@ -23,6 +23,10 @@ #' [base::lapply()] function when no backend is provided. However, when a #' backend is provided, the function will execute a task in parallel on the #' backend, similar to the built-in function [parallel::parLapply()]. +#' - [parabar::par_apply()]: is a drop-in replacement for the built-in +#' [base::apply()] function when no backend is provided. However, when a +#' backend is provided, the function will execute a task in parallel on the +#' backend, similar to the built-in function [parallel::parApply()]. #' - [parabar::clear()]: removes all variables available on a backend. #' - [parabar::peek()]: returns the names of all variables available on a #' backend. @@ -60,8 +64,8 @@ #' [`start()`][parabar::Service], [`stop()`][parabar::Service], #' [`clear()`][parabar::Service], [`peek()`][parabar::Service], #' [`export()`][parabar::Service], [`evaluate()`][parabar::Service], -#' [`sapply()`][parabar::Service], [`lapply()`][parabar::Service], and -#' [`get_output()`][parabar::Service]. +#' [`sapply()`][parabar::Service], [`lapply()`][parabar::Service], +#' [`apply()`][parabar::Service], and [`get_output()`][parabar::Service]. #' #' Check out the documentation for [`parabar::Service`] for more information on #' each method. @@ -91,9 +95,12 @@ #' - [`parabar::BackendFactory`]: factory for creating backend objects. #' - [`parabar::Context`]: default context for executing backend operations. #' - [`parabar::ProgressTrackingContext`]: context for decorating the -#' [`sapply()`][parabar::Service] and [`lapply()`][parabar::Service] +#' [`sapply()`][parabar::Service], [`lapply()`][parabar::Service], and +#' [`apply()`][parabar::Service] #' operations to track and display the execution progress. #' - [`parabar::ContextFactory`]: factory for creating context objects. +#' - [`parabar::UserApiConsumer`]: opinionated wrapper around the other +#' [`R6::R6`] classes used in by the exported functions for the users. #' #' @section Progress Bars: #' [`parabar::parabar`] also exposes several classes for creating and updating diff --git a/man-roxygen/peek.R b/man-roxygen/peek.R index 88331f9..b47877a 100644 --- a/man-roxygen/peek.R +++ b/man-roxygen/peek.R @@ -25,4 +25,5 @@ #' @seealso #' [parabar::start_backend()], [parabar::export()], [parabar::evaluate()], #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()], -#' [parabar::par_lapply()], [parabar::stop_backend()], and [`parabar::Service`]. +#' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' and [`parabar::Service`]. diff --git a/man-roxygen/start-backend.R b/man-roxygen/start-backend.R index abcbbc8..1d751b0 100644 --- a/man-roxygen/start-backend.R +++ b/man-roxygen/start-backend.R @@ -120,4 +120,5 @@ #' @seealso #' [parabar::peek()], [parabar::export()], [parabar::evaluate()], #' [parabar::clear()], [parabar::configure_bar()], [parabar::par_sapply()], -#' [parabar::par_lapply()], [parabar::stop_backend()], and [`parabar::Service`]. +#' [parabar::par_lapply()], [parabar::par_apply()], [parabar::stop_backend()], +#' and [`parabar::Service`]. diff --git a/man-roxygen/stop-backend.R b/man-roxygen/stop-backend.R index 20563ee..c2daede 100644 --- a/man-roxygen/stop-backend.R +++ b/man-roxygen/stop-backend.R @@ -26,4 +26,5 @@ #' @seealso #' [parabar::start_backend()], [parabar::peek()], [parabar::export()], #' [parabar::evaluate()], [parabar::clear()], [parabar::configure_bar()], -#' [parabar::par_sapply()], [parabar::par_lapply()], and [`parabar::Service`]. +#' [parabar::par_sapply()], [parabar::par_apply()], [parabar::par_lapply()], and +#' [`parabar::Service`]. diff --git a/man/AsyncBackend.Rd b/man/AsyncBackend.Rd index 1f0311a..1506def 100644 --- a/man/AsyncBackend.Rd +++ b/man/AsyncBackend.Rd @@ -125,6 +125,7 @@ has been fetched, the backend is free to deploy another task. \item \href{#method-AsyncBackend-evaluate}{\code{AsyncBackend$evaluate()}} \item \href{#method-AsyncBackend-sapply}{\code{AsyncBackend$sapply()}} \item \href{#method-AsyncBackend-lapply}{\code{AsyncBackend$lapply()}} +\item \href{#method-AsyncBackend-apply}{\code{AsyncBackend$apply()}} \item \href{#method-AsyncBackend-get_output}{\code{AsyncBackend$get_output()}} \item \href{#method-AsyncBackend-clone}{\code{AsyncBackend$clone()}} } @@ -308,6 +309,40 @@ Run a task on the backend akin to \code{\link[parallel:clusterApply]{parallel::p \item{\code{fun}}{A function to apply to each element of \code{x}.} +\item{\code{...}}{Additional arguments to pass to the \code{fun} function.} +} +\if{html}{\out{}} +} +\subsection{Returns}{ +This method returns void. The output of the task execution must be +stored in the private field \code{.output} on the \code{\link{Backend}} +abstract class, and is accessible via the \code{get_output()} method. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-AsyncBackend-apply}{}}} +\subsection{Method \code{apply()}}{ +Run a task on the backend akin to \code{\link[parallel:clusterApply]{parallel::parApply()}}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{AsyncBackend$apply(x, margin, fun, ...)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{x}}{An array to pass to the \code{fun} function.} + +\item{\code{margin}}{A numeric vector indicating the dimensions of \code{x} the +\code{fun} function should be applied over. For example, for a matrix, +\code{margin = 1} indicates applying \code{fun} rows-wise, \code{margin = 2} +indicates applying \code{fun} columns-wise, and \code{margin = c(1, 2)} +indicates applying \code{fun} element-wise. Named dimensions are also +possible depending on \code{x}. See \code{\link[parallel:clusterApply]{parallel::parApply()}} and +\code{\link[base:apply]{base::apply()}} for more details.} + +\item{\code{fun}}{A function to apply to \code{x} according to the \code{margin}.} + \item{\code{...}}{Additional arguments to pass to the \code{fun} function.} } \if{html}{\out{
}} diff --git a/man/Backend.Rd b/man/Backend.Rd index 22d4fe2..a7e83d3 100644 --- a/man/Backend.Rd +++ b/man/Backend.Rd @@ -48,6 +48,7 @@ implementation has an active cluster.} \if{html}{\out{
Inherited methods