From 7a341c8dc6d18a2d802e132c68884a99ba847ad3 Mon Sep 17 00:00:00 2001 From: yannrichet Date: Sun, 12 Jul 2026 15:15:13 +0200 Subject: [PATCH 1/6] fix: restore LICENSE stub and install funz-fz in R-CMD-check CI R-CMD-check has been failing on main since 653914f, unrelated to any functional change: - DESCRIPTION declares `License: BSD_3_clause + file LICENSE`, but the LICENSE stub file (the templated YEAR/ORGANIZATION/COPYRIGHT HOLDER file required by R's packaging convention) was deleted in 653914f, leaving only LICENSE.md (the full license text, which is a separate, optional file). Restore LICENSE with the same content it had before deletion. - The R-CMD-check workflow never installs the funz-fz Python package, unlike test-with-python.yaml (which passes): it relies solely on reticulate's automatic ephemeral-environment provisioning, which resolves to a Python 'fz' module missing attributes like fzc, breaking --run-donttest examples. Add the same explicit actions/setup-python + `pip install funz-fz` steps already used successfully in test-with-python.yaml. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/R-CMD-check.yaml | 7 +++++++ LICENSE | 3 +++ 2 files changed, 10 insertions(+) create mode 100644 LICENSE diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 2c6e490..c3bbdd3 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -41,6 +41,13 @@ jobs: extra-packages: any::rcmdcheck needs: check + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install fz Python package + run: pip install funz-fz + - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f9943bf --- /dev/null +++ b/LICENSE @@ -0,0 +1,3 @@ +YEAR: 2025 +ORGANIZATION: Funz +COPYRIGHT HOLDER: Funz From c14f6789b45affa6a948807656f592c116b48d89 Mon Sep 17 00:00:00 2001 From: yannrichet Date: Sun, 12 Jul 2026 15:19:33 +0200 Subject: [PATCH 2/6] debug: print reticulate python resolution in R-CMD-check CI --- .github/workflows/R-CMD-check.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index c3bbdd3..0991840 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -48,6 +48,18 @@ jobs: - name: Install fz Python package run: pip install funz-fz + - name: Debug reticulate Python resolution + run: | + Rscript -e ' + reticulate::py_config() + cat("py_module_available(fz):", reticulate::py_module_available("fz"), "\n") + if (reticulate::py_module_available("fz")) { + m <- reticulate::import("fz") + cat("fz module file:", m[["__file__"]], "\n") + cat("fz module names:", paste(names(m), collapse = ", "), "\n") + } + ' + - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true From 3d196a11eb91ff59b2250e6a2580796e0ae2df15 Mon Sep 17 00:00:00 2001 From: yannrichet Date: Sun, 12 Jul 2026 15:26:16 +0200 Subject: [PATCH 3/6] fix: force reticulate to use the CI Python via RETICULATE_PYTHON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause found via debug logging: reticulate 1.46's automatic py_require()/uv-based ephemeral environment provisioning was resolving to an unrelated PyPI package literally named "fz" (0.1.1, distinct from "funz-fz"), which naturally lacks fzc and the rest of funz-fz's API. Neither actions/setup-python nor `pip install funz-fz` had any effect on this, since reticulate's ephemeral provisioning ignores the Python already on PATH unless explicitly told otherwise. Set RETICULATE_PYTHON to the interpreter we just installed funz-fz into, which takes priority over reticulate's own environment auto-provisioning and forces it to use exactly that Python — sidestepping the ephemeral uv-managed environment (and its package-name mismatch) entirely. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/R-CMD-check.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0991840..2cb2040 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -48,17 +48,8 @@ jobs: - name: Install fz Python package run: pip install funz-fz - - name: Debug reticulate Python resolution - run: | - Rscript -e ' - reticulate::py_config() - cat("py_module_available(fz):", reticulate::py_module_available("fz"), "\n") - if (reticulate::py_module_available("fz")) { - m <- reticulate::import("fz") - cat("fz module file:", m[["__file__"]], "\n") - cat("fz module names:", paste(names(m), collapse = ", "), "\n") - } - ' + - name: Point reticulate at the CI Python + run: echo "RETICULATE_PYTHON=$(python3 -c 'import sys; print(sys.executable)')" >> "$GITHUB_ENV" - uses: r-lib/actions/check-r-package@v2 with: From 9b9f657039477959e90decd37c3ae6ac122d7db4 Mon Sep 17 00:00:00 2001 From: yannrichet Date: Sun, 12 Jul 2026 15:54:15 +0200 Subject: [PATCH 4/6] fix: fzd() algorithm_options example used a format funz-fz never accepted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI now correctly resolves the real funz-fz package (see previous commit), which surfaced a latent bug hidden until now: fzd()'s own roxygen example and the modelica-examples vignette passed algorithm_options as a "key=val;key2=val2" string, but funz-fz's _resolve_algorithm_options only ever accepted a dict, a JSON string, or a path to a .json file — never semicolon-separated key=value pairs. Replaced with a named list (which reticulate converts to a Python dict) in the executed fzd() example, the vignette source, and its purled/rendered copies in inst/doc. Co-Authored-By: Claude Sonnet 5 --- NEWS.md | 11 +++++++++++ R/core-functions.R | 7 +++---- inst/doc/modelica-examples.R | 2 +- inst/doc/modelica-examples.Rmd | 2 +- inst/doc/modelica-examples.html | 2 +- man/fzd.Rd | 7 +++---- vignettes/modelica-examples.R | 2 +- vignettes/modelica-examples.Rmd | 2 +- 8 files changed, 22 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index d2cae55..a78edd3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,6 +14,17 @@ 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. +* Fixed `fzd()`'s own `algorithm_options` example/documentation, which used + a `"key=val;key2=val2"` string that `funz-fz` has never actually accepted + (only a named list, JSON string, or path to a JSON file) — replaced with + a named list. CI never caught this because it was silently exercising the + wrong Python module (see CI fixes below). +* Restored the `LICENSE` file (the templated file R's packaging convention + requires alongside `License: BSD_3_clause + file LICENSE`), which had been + mistakenly deleted, and fixed `R-CMD-check` CI to install `funz-fz` and + point `reticulate` at it via `RETICULATE_PYTHON` — without this, + `reticulate`'s automatic environment provisioning was silently resolving + to an unrelated PyPI package literally named `fz` (not `funz-fz`). # fz 1.1 diff --git a/R/core-functions.R b/R/core-functions.R index f5aeea4..e53412c 100644 --- a/R/core-functions.R +++ b/R/core-functions.R @@ -202,9 +202,8 @@ fzl <- function(models = "*", calculators = "*", check = FALSE) { #' \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 algorithm_options Algorithm options as a named list, a JSON string, +#' or a path to a JSON file. Default \code{NULL}. #' @param analysis_dir Analysis directory. Default \code{"analysis"}. #' #' @section Direct function model: @@ -259,7 +258,7 @@ fzl <- function(models = "*", calculators = "*", check = FALSE) { #' model, #' output_expression = "z", #' algorithm = "algorithms/montecarlo_uniform.py", -#' algorithm_options = "batch_sample_size=10;max_iterations=3" +#' algorithm_options = list(batch_sample_size = 10, max_iterations = 3) #' ) #' } #' } diff --git a/inst/doc/modelica-examples.R b/inst/doc/modelica-examples.R index 1e878ec..2b1230e 100644 --- a/inst/doc/modelica-examples.R +++ b/inst/doc/modelica-examples.R @@ -76,7 +76,7 @@ knitr::opts_chunk$set( # model, # output_expression = "pressure", # algorithm = "algorithms/montecarlo_uniform.py", -# algorithm_options = "batch_sample_size=10;max_iterations=5;seed=42" +# algorithm_options = list(batch_sample_size = 10, max_iterations = 5, seed = 42) # ) ## ----fzl---------------------------------------------------------------------- diff --git a/inst/doc/modelica-examples.Rmd b/inst/doc/modelica-examples.Rmd index 49759ff..290e3dd 100644 --- a/inst/doc/modelica-examples.Rmd +++ b/inst/doc/modelica-examples.Rmd @@ -173,7 +173,7 @@ result <- fzd( model, output_expression = "pressure", algorithm = "algorithms/montecarlo_uniform.py", - algorithm_options = "batch_sample_size=10;max_iterations=5;seed=42" + algorithm_options = list(batch_sample_size = 10, max_iterations = 5, seed = 42) ) ``` diff --git a/inst/doc/modelica-examples.html b/inst/doc/modelica-examples.html index 985c634..a2608f0 100644 --- a/inst/doc/modelica-examples.html +++ b/inst/doc/modelica-examples.html @@ -484,7 +484,7 @@

Algorithm-driven design of experiments

model, output_expression = "pressure", algorithm = "algorithms/montecarlo_uniform.py", - algorithm_options = "batch_sample_size=10;max_iterations=5;seed=42" + algorithm_options = list(batch_sample_size = 10, max_iterations = 5, seed = 42) )

/home/richet/Sync/Open/Funz/github/fz.R/vignettes/modelica-examples.R

Algorithms are Python files; fz ships several in diff --git a/man/fzd.Rd b/man/fzd.Rd index 52581d1..2ef1a51 100644 --- a/man/fzd.Rd +++ b/man/fzd.Rd @@ -39,9 +39,8 @@ value is used.} \code{1L}), accepted for API compatibility (see "Direct function model" below) — calls are always run sequentially regardless of its value.} -\item{algorithm_options}{Algorithm options as a named list or -semicolon-separated string, e.g. \code{"batch_sample_size=10;seed=42"}. -Default \code{NULL}.} +\item{algorithm_options}{Algorithm options as a named list, a JSON string, +or a path to a JSON file. Default \code{NULL}.} \item{analysis_dir}{Analysis directory. Default \code{"analysis"}.} } @@ -105,7 +104,7 @@ if (fz_available()) { model, output_expression = "z", algorithm = "algorithms/montecarlo_uniform.py", - algorithm_options = "batch_sample_size=10;max_iterations=3" + algorithm_options = list(batch_sample_size = 10, max_iterations = 3) ) } } diff --git a/vignettes/modelica-examples.R b/vignettes/modelica-examples.R index 1e878ec..2b1230e 100644 --- a/vignettes/modelica-examples.R +++ b/vignettes/modelica-examples.R @@ -76,7 +76,7 @@ knitr::opts_chunk$set( # model, # output_expression = "pressure", # algorithm = "algorithms/montecarlo_uniform.py", -# algorithm_options = "batch_sample_size=10;max_iterations=5;seed=42" +# algorithm_options = list(batch_sample_size = 10, max_iterations = 5, seed = 42) # ) ## ----fzl---------------------------------------------------------------------- diff --git a/vignettes/modelica-examples.Rmd b/vignettes/modelica-examples.Rmd index 49759ff..290e3dd 100644 --- a/vignettes/modelica-examples.Rmd +++ b/vignettes/modelica-examples.Rmd @@ -173,7 +173,7 @@ result <- fzd( model, output_expression = "pressure", algorithm = "algorithms/montecarlo_uniform.py", - algorithm_options = "batch_sample_size=10;max_iterations=5;seed=42" + algorithm_options = list(batch_sample_size = 10, max_iterations = 5, seed = 42) ) ``` From f1ec70e6d0885e8154fab821216e85500b444c65 Mon Sep 17 00:00:00 2001 From: yannrichet Date: Sun, 12 Jul 2026 16:22:09 +0200 Subject: [PATCH 5/6] fix: fzd() example algorithm path never existed outside the fz repo Third latent bug surfaced now that CI actually calls the real funz-fz: the example referenced a relative "algorithms/montecarlo_uniform.py", which only exists inside the Funz/fz GitHub repo's examples/ directory and was never installed or shipped anywhere R CMD check's working directory could find it. Replace it with a minimal, self-contained random-sampling algorithm written to a tempfile inline in the example, matching fz's algorithm plugin interface (get_initial_design/get_next_design/get_analysis), so the executed \donttest example has no external file or network dependency. Co-Authored-By: Claude Sonnet 5 --- R/core-functions.R | 30 +++++++++++++++++++++++++++++- man/fzd.Rd | 30 +++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/R/core-functions.R b/R/core-functions.R index e53412c..3dfeb2e 100644 --- a/R/core-functions.R +++ b/R/core-functions.R @@ -252,12 +252,40 @@ fzl <- function(models = "*", calculators = "*", check = FALSE) { #' output = list(z = "grep z output.txt | cut -d= -f2") #' ) #' +#' # A minimal self-contained random-sampling algorithm (see +#' # https://github.com/Funz/fz for ready-made algorithms to install) +#' algo <- tempfile(fileext = ".py") +#' writeLines(c( +#' "import random", +#' "class RandomSampler:", +#' " def __init__(self, **options):", +#' " self.batch = int(options.get('batch_sample_size', 5))", +#' " self.max_iterations = int(options.get('max_iterations', 3))", +#' " self.iteration = 0", +#' " self.input_vars = {}", +#' " def get_initial_design(self, input_vars, output_vars):", +#' " self.input_vars = input_vars", +#' " self.iteration = 1", +#' " return [{k: random.uniform(*v) for k, v in input_vars.items()}", +#' " for _ in range(self.batch)]", +#' " def get_next_design(self, previous_input_vars, previous_output_values):", +#' " self.iteration += 1", +#' " if self.iteration > self.max_iterations:", +#' " return []", +#' " return [{k: random.uniform(*v) for k, v in self.input_vars.items()}", +#' " for _ in range(self.batch)]", +#' " def get_analysis(self, input_vars, output_values):", +#' " valid = [v for v in output_values if v is not None]", +#' " mean = sum(valid) / len(valid) if valid else None", +#' " return {'text': f'mean={mean}', 'data': {'mean': mean}}" +#' ), algo) +#' #' result <- fzd( #' tf, #' list(x = "[0;1]", y = "[-5;5]"), #' model, #' output_expression = "z", -#' algorithm = "algorithms/montecarlo_uniform.py", +#' algorithm = algo, #' algorithm_options = list(batch_sample_size = 10, max_iterations = 3) #' ) #' } diff --git a/man/fzd.Rd b/man/fzd.Rd index 2ef1a51..209e27d 100644 --- a/man/fzd.Rd +++ b/man/fzd.Rd @@ -98,12 +98,40 @@ if (fz_available()) { output = list(z = "grep z output.txt | cut -d= -f2") ) + # A minimal self-contained random-sampling algorithm (see + # https://github.com/Funz/fz for ready-made algorithms to install) + algo <- tempfile(fileext = ".py") + writeLines(c( + "import random", + "class RandomSampler:", + " def __init__(self, **options):", + " self.batch = int(options.get('batch_sample_size', 5))", + " self.max_iterations = int(options.get('max_iterations', 3))", + " self.iteration = 0", + " self.input_vars = {}", + " def get_initial_design(self, input_vars, output_vars):", + " self.input_vars = input_vars", + " self.iteration = 1", + " return [{k: random.uniform(*v) for k, v in input_vars.items()}", + " for _ in range(self.batch)]", + " def get_next_design(self, previous_input_vars, previous_output_values):", + " self.iteration += 1", + " if self.iteration > self.max_iterations:", + " return []", + " return [{k: random.uniform(*v) for k, v in self.input_vars.items()}", + " for _ in range(self.batch)]", + " def get_analysis(self, input_vars, output_values):", + " valid = [v for v in output_values if v is not None]", + " mean = sum(valid) / len(valid) if valid else None", + " return {'text': f'mean={mean}', 'data': {'mean': mean}}" + ), algo) + result <- fzd( tf, list(x = "[0;1]", y = "[-5;5]"), model, output_expression = "z", - algorithm = "algorithms/montecarlo_uniform.py", + algorithm = algo, algorithm_options = list(batch_sample_size = 10, max_iterations = 3) ) } From d60d70708dc9d69b0583558619fba06033cded8e Mon Sep 17 00:00:00 2001 From: yannrichet Date: Sun, 12 Jul 2026 16:47:59 +0200 Subject: [PATCH 6/6] fix: install_model/install_algorithm examples reference a nonexistent repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fourth latent bug surfaced now that CI actually calls the real funz-fz: install("Funz/Model-PerfectGas") (and install_model/install_algorithm's own examples) always failed with a 404 — no "Funz/Model-PerfectGas" or "Funz/Algorithm-MonteCarlo" repository exists. These examples require an external GitHub repo and network access, neither guaranteed in CI or CRAN build environments, so move them from \donttest (executed by R CMD check --run-donttest) to \dontrun (illustrative only, never executed) rather than depending on install targets that may not exist. Verified locally: a full `R CMD check --no-manual --run-donttest` against a build installed with the real funz-fz package (not the mismatched Python "fz" package that was masking all of these) now reports Status: OK with no errors. Co-Authored-By: Claude Sonnet 5 --- R/install.R | 24 ++++++++++++------------ man/install.Rd | 8 ++++---- man/install_algorithm.Rd | 8 ++++---- man/install_model.Rd | 8 ++++---- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/R/install.R b/R/install.R index a7e6f74..08f40e1 100644 --- a/R/install.R +++ b/R/install.R @@ -72,10 +72,10 @@ fz_available <- function() { #' @export #' #' @examples -#' \donttest{ -#' if (fz_available()) { -#' install_model("Funz/Model-PerfectGas") -#' } +#' \dontrun{ +#' # Requires the named GitHub repository to exist and network access; +#' # not run automatically since neither is guaranteed in all environments. +#' install_model("Funz/Model-PerfectGas") #' } install_model <- function(source, global = FALSE) { fz_module <- get_fz() @@ -97,10 +97,10 @@ install_model <- function(source, global = FALSE) { #' @export #' #' @examples -#' \donttest{ -#' if (fz_available()) { -#' install_algorithm("Funz/Algorithm-MonteCarlo") -#' } +#' \dontrun{ +#' # Requires the named GitHub repository to exist and network access; +#' # not run automatically since neither is guaranteed in all environments. +#' install_algorithm("Funz/Algorithm-MonteCarlo") #' } install_algorithm <- function(source, global = FALSE) { fz_module <- get_fz() @@ -219,10 +219,10 @@ list_models <- function(global = FALSE) { #' @export #' #' @examples -#' \donttest{ -#' if (fz_available()) { -#' install("Funz/Model-PerfectGas") -#' } +#' \dontrun{ +#' # Requires the named GitHub repository to exist and network access; +#' # not run automatically since neither is guaranteed in all environments. +#' install("Funz/Model-PerfectGas") #' } install <- function(source, global = FALSE) { install_model(source, global) diff --git a/man/install.Rd b/man/install.Rd index 2f1268f..d4cff31 100644 --- a/man/install.Rd +++ b/man/install.Rd @@ -21,9 +21,9 @@ Generic alias: installs a model from a GitHub name, URL, or local zip file. Equivalent to \code{\link{install_model}}. } \examples{ -\donttest{ -if (fz_available()) { - install("Funz/Model-PerfectGas") -} +\dontrun{ +# Requires the named GitHub repository to exist and network access; +# not run automatically since neither is guaranteed in all environments. +install("Funz/Model-PerfectGas") } } diff --git a/man/install_algorithm.Rd b/man/install_algorithm.Rd index 97a7d39..229782d 100644 --- a/man/install_algorithm.Rd +++ b/man/install_algorithm.Rd @@ -22,9 +22,9 @@ into the user-level \code{~/.fz/algorithms/} directory (or system-level when \code{global = TRUE}). } \examples{ -\donttest{ -if (fz_available()) { - install_algorithm("Funz/Algorithm-MonteCarlo") -} +\dontrun{ +# Requires the named GitHub repository to exist and network access; +# not run automatically since neither is guaranteed in all environments. +install_algorithm("Funz/Algorithm-MonteCarlo") } } diff --git a/man/install_model.Rd b/man/install_model.Rd index 432ff3d..3680350 100644 --- a/man/install_model.Rd +++ b/man/install_model.Rd @@ -22,9 +22,9 @@ the user-level \code{~/.fz/models/} directory (or system-level when \code{global = TRUE}). } \examples{ -\donttest{ -if (fz_available()) { - install_model("Funz/Model-PerfectGas") -} +\dontrun{ +# Requires the named GitHub repository to exist and network access; +# not run automatically since neither is guaranteed in all environments. +install_model("Funz/Model-PerfectGas") } }