Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: fz
Type: Package
Title: R Wrapper for the 'funz-fz' Parametric Simulation Framework
Version: 1.1
Version: 1.1.9000
Authors@R: c(
person("Yann", "Richet", email = "yann.richet@asnr.fr",
role = c("aut", "cre"), comment = c(ORCID = "0000-0002-5677-8458"))
Expand Down
17 changes: 17 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# fz (development version)

* `fzd()` now accepts an R function as `model`, letting adaptive
design-of-experiments algorithms drive an R function directly instead of
a file-based model (`input_path = NULL`, `output_expression` optional,
`calculators` accepted as any single integer but currently always run
sequentially, one call at a time — see below). This requires the `main`
branch of `fz` on GitHub — install it with
`fz_install(packages = "git+https://github.com/Funz/fz.git")`, and needs
[Funz/fz#73](https://github.com/Funz/fz/pull/73) merged: R closures are
bridged in via `reticulate`, which is only safe to call from the main
thread, so `fz`'s function-model support was changed to always call the
model sequentially (like `lapply`) instead of via a Python thread pool,
regardless of `calculators`.
* `fz_install()` gains a `packages` argument (default `"funz-fz"`) so the
latest `main` branch can be installed instead of the PyPI release.

# fz 1.1

First release, aligned with funz-fz 1.1 on PyPI.
Expand Down
86 changes: 81 additions & 5 deletions R/core-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,60 @@ fzl <- function(models = "*", calculators = "*", check = FALSE) {
#' algorithm adaptively choose which parameter combinations to evaluate, which
#' is useful for sensitivity analysis, surrogate-model fitting, or optimization.
#'
#' @param input_path Path to input file or directory.
#' @param input_path Path to input file or directory. Must be \code{NULL} when
#' \code{model} is an R function (see "Direct function model" below).
#' @param input_variables Named list of variable range strings of the form
#' \code{"[min;max]"}, e.g. \code{list(x = "[0;1]", y = "[-5;5]")}.
#' @param model Model definition dict or alias string.
#' @param model Model definition dict or alias string, or an R function (see
#' "Direct function model" below).
#' @param output_expression Expression evaluated on the model outputs to
#' produce the scalar quantity the algorithm optimizes or analyses,
#' e.g. \code{"result"} or \code{"out1 + 2 * out2"}.
#' e.g. \code{"result"} or \code{"out1 + 2 * out2"}. May be \code{NULL}
#' only when \code{model} is a function, in which case the first output
#' value is used.
#' @param algorithm Path to the algorithm Python file, e.g.
#' \code{"algorithms/montecarlo_uniform.py"}.
#' @param calculators Calculator specification(s). Default \code{NULL}.
#' @param calculators Calculator specification(s). Default \code{NULL}. When
#' \code{model} is a function, this must be a single integer (default
#' \code{1L}), accepted for API compatibility (see "Direct function model"
#' below) — calls are always run sequentially regardless of its value.
#' @param algorithm_options Algorithm options as a named list or
#' semicolon-separated string, e.g. \code{"batch_sample_size=10;seed=42"}.
#' Default \code{NULL}.
#' @param analysis_dir Analysis directory. Default \code{"analysis"}.
#'
#' @section Direct function model:
#' Instead of a file-based model, \code{model} can be an R function (this
#' requires the \code{main} branch of \code{fz} from GitHub, installed with
#' \code{fz_install(packages = "git+https://github.com/Funz/fz.git")} — this
#' mode is not available in released PyPI versions of \code{funz-fz} yet). In
#' this mode:
#' \itemize{
#' \item \code{input_path} must be \code{NULL} — there are no input files.
#' \item \code{input_variables} names must match the function's arguments.
#' \item \code{output_expression} may be \code{NULL}; the value used is then
#' the first element of the function's return value (its return value
#' directly if scalar, the first element if a vector/list, or the first
#' entry's value if a named list).
#' \item \code{calculators} must be a single integer, accepted for API
#' compatibility but currently without effect: on the \code{fz} side,
#' function-model calls always run sequentially, one at a time in the
#' calling thread — never through a thread pool. This is required
#' because R functions are called back into the R session via
#' \code{reticulate}, which is only safe from the main thread; running
#' a Python-side thread pool (which always dispatches to a worker thread,
#' even with a single worker) would call the function from a thread
#' other than the main one and crash the R session. This safety fix
#' requires \href{https://github.com/Funz/fz/pull/73}{Funz/fz#73} on the
#' \code{fz} \code{main} branch (not yet in a PyPI release as of
#' 2026-07-12); without it, direct function models crash regardless of
#' \code{calculators}. A value other than \code{1} emits a warning
#' noting that it has no effect.
#' \item each iteration's directory (\code{iterNNN/}) only contains a
#' \code{values.csv} of that iteration's function inputs/outputs, since
#' there is no file-based execution.
#' }
#'
#' @return Named list with the analysis results produced by the algorithm.
#' @export
#'
Expand All @@ -224,9 +263,46 @@ fzl <- function(models = "*", calculators = "*", check = FALSE) {
#' )
#' }
#' }
fzd <- function(input_path, input_variables, model, output_expression, algorithm,
#'
#' \dontrun{
#' # Direct function model (requires fz main branch from GitHub)
#' rosenbrock <- function(x, y) {
#' list(result = (1 - x)^2 + 100 * (y - x^2)^2)
#' }
#'
#' result <- fzd(
#' input_path = NULL,
#' input_variables = list(x = "[-2;2]", y = "[-2;2]"),
#' model = rosenbrock,
#' output_expression = "result",
#' algorithm = "examples/algorithms/bfgs.py",
#' calculators = 4L,
#' algorithm_options = list(max_iter = 20, tol = 1e-4)
#' )
#' }
fzd <- function(input_path, input_variables, model, output_expression = NULL, algorithm,
calculators = NULL, algorithm_options = NULL,
analysis_dir = "analysis") {
if (is.function(model)) {
if (is.null(calculators)) {
calculators <- 1L
} else if (!is.numeric(calculators) || length(calculators) != 1) {
stop(
"When 'model' is an R function, 'calculators' must be a single integer.",
call. = FALSE
)
}
calculators <- as.integer(calculators)
if (calculators != 1L) {
warning(
"calculators = ", calculators, " has no effect when 'model' is an R ",
"function: evaluations always run sequentially (one call at a time), ",
"since R functions bridged in via reticulate are only safe to call ",
"from the main thread.",
call. = FALSE
)
}
}
fz_module <- get_fz()
fz_module$fzd(input_path, input_variables, model, output_expression, algorithm,
calculators = calculators,
Expand Down
11 changes: 9 additions & 2 deletions R/install.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#' This function installs the fz Python package into a virtual environment
#' or conda environment managed by reticulate.
#'
#' @param packages Package specification passed to [reticulate::py_install()].
#' Default \code{"funz-fz"} installs the latest release from PyPI. To track
#' unreleased features, install the latest \code{main} branch directly from
#' GitHub with \code{"git+https://github.com/Funz/fz.git"}.
#' @param method Installation method. Either "auto", "virtualenv", or "conda".
#' @param conda Path to conda executable. Only used when method is "conda".
#' @param pip Logical; use pip for installation? Default is TRUE.
Expand All @@ -20,9 +24,12 @@
#'
#' # Install in a conda environment
#' fz_install(method = "conda")
#'
#' # Track the latest main branch on GitHub (unreleased features)
#' fz_install(packages = "git+https://github.com/Funz/fz.git")
#' }
fz_install <- function(method = "auto", conda = "auto", pip = TRUE, ...) {
reticulate::py_install("funz-fz", method = method, conda = conda, pip = pip, ...)
fz_install <- function(packages = "funz-fz", method = "auto", conda = "auto", pip = TRUE, ...) {
reticulate::py_install(packages, method = method, conda = conda, pip = pip, ...)
}

#' Check if fz Python Package is Available
Expand Down
16 changes: 15 additions & 1 deletion man/fz_install.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 63 additions & 5 deletions man/fzd.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading