Learn R Programming

fz (version 1.2.0)

fzd: fzd Function

Description

Runs an iterative design of experiments driven by an algorithm. Unlike fzr (which evaluates a fixed grid), fzd lets an algorithm adaptively choose which parameter combinations to evaluate, which is useful for sensitivity analysis, surrogate-model fitting, or optimization.

Usage

fzd(
  input_path,
  input_variables,
  model,
  output_expression = NULL,
  algorithm,
  calculators = NULL,
  algorithm_options = NULL,
  analysis_dir = "analysis",
  input_static = NULL
)

Value

Named list with the analysis results produced by the algorithm.

Arguments

input_path

Path to input file or directory. Must be NULL when model is an R function (see "Direct function model" below).

input_variables

Named list of variable range strings of the form "[min;max]", e.g. list(x = "[0;1]", y = "[-5;5]").

model

Model definition dict or alias string, or an R function (see "Direct function model" below).

output_expression

Expression evaluated on the model outputs to produce the quantity the algorithm optimizes or analyses, e.g. "result" or "out1 + 2 * out2". Vector-valued outputs may be reduced with mean(), sum(), len(), median(), stdev(), variance(), indexing/slicing, and zip(). A character vector of length > 1 requests a multi-objective run (funz-fz >= 1.2): each case then yields one scalar per expression, passed as-is to multi-objective algorithms such as NSGA-II. May be NULL only when model is a function, in which case the first output value is used.

algorithm

Path to the algorithm Python file, e.g. "algorithms/montecarlo_uniform.py".

calculators

Calculator specification(s). Default NULL. When model is a function, this must be a single integer (default 1L) and is always forced to 1L (see "Direct function model" below): R functions are only safe to call from the main thread, so a value other than 1 triggers a warning and is overridden.

algorithm_options

Algorithm options as a named list, a JSON string, or a path to a JSON file. Default NULL.

analysis_dir

Analysis directory. Default "analysis".

input_static

Optional character vector of files identical across every case (see fzr's input_static); passed through unchanged to each iteration's internal fzr() call. Default NULL.

Direct function model

Instead of a file-based model, model can be an R function. This requires funz-fz >= 1.2 (earlier releases do not support callable models); a development build can be installed with fz_install(packages = "git+https://github.com/Funz/fz.git"). In this mode:

  • input_path must be NULL -- there are no input files.

  • input_variables names must match the function's arguments.

  • output_expression may be 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).

  • calculators must be a single integer, but is always forced to 1 here -- regardless of the value passed in -- before being forwarded to fz. On the fz (Python) side, calculators > 1 now evaluates a Python-function model concurrently in a worker-thread pool (Funz/fz#73); that is unsafe here because R functions are called back into the R session via reticulate, which is only safe from the main thread -- invoking the function from any other thread crashes the R session. Passing a value other than 1 therefore emits a warning explaining that it is being forced back to 1, and the call always proceeds with calculators = 1 (strictly sequential, one call at a time, in the calling thread).

  • each iteration's directory (iterNNN/) only contains a values.csv of that iteration's function inputs/outputs, since there is no file-based execution.

Examples

Run this code
# \donttest{
if (fz_available()) {
  # run inside a throwaway directory: fz writes analysis/ and .fz/ under cwd
  ex_dir <- file.path(tempdir(), "fz-fzd-example")
  dir.create(ex_dir, showWarnings = FALSE)
  owd <- setwd(ex_dir)

  tf <- tempfile(fileext = ".txt")
  writeLines(c("x = ${x~0}", "y = ${y~0}"), tf)

  model <- list(
    varprefix = "$", delim = "{}", formulaprefix = "@", commentline = "#",
    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        = algo,
    algorithm_options = list(batch_sample_size = 10, max_iterations = 3)
  )

  setwd(owd)
  unlink(ex_dir, recursive = TRUE)
}
# }

if (FALSE) {
# Direct function model (requires funz-fz >= 1.2)
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 = 1L, # forced to 1L anyway for R functions -- see "Direct function model"
  algorithm_options = list(max_iter = 20, tol = 1e-4)
)
}

Run the code above in your browser using DataLab