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 @@
/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)
)
```