diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 2c6e490..2cb2040 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -41,6 +41,16 @@ 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 + + - 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: 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 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..3dfeb2e 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: @@ -253,13 +252,41 @@ 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_options = "batch_sample_size=10;max_iterations=3" +#' algorithm = algo, +#' algorithm_options = list(batch_sample_size = 10, max_iterations = 3) #' ) #' } #' } 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/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..209e27d 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"}.} } @@ -99,13 +98,41 @@ 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_options = "batch_sample_size=10;max_iterations=3" + algorithm = algo, + algorithm_options = list(batch_sample_size = 10, max_iterations = 3) ) } } 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") } } 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) ) ```