diff --git a/.gitignore b/.gitignore index 04908fc..617b8b9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ results*/ output/ # notebook work directories (generated by examples/) examples/work_*/ +# fz run scratch under examples/ (re-excluded: the !examples/** rule above +# would otherwise un-ignore the global .fz rule for this path) +examples/.fz/ diff --git a/CLAUDE.md b/CLAUDE.md index 2be9d7f..d3c7be8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ codes to run parameter studies locally, over SSH, or on SLURM. Python rewrite of `cli.py` (entry points `fz`, `fzi`, ... defined in pyproject.toml), `runners.py` (sh/ssh/slurm/funz/cache calculators), `interpreter.py` (variable/formula parsing, Python and R evaluation), `io.py`, `config.py` (env vars), `installer.py` (`fz install`). -- `tests/` — pytest suite. `context/` — modular user docs. `skills/fz/` — Agent Skill +- `tests/` — pytest suite. `doc/` — modular user docs. `skills/fz/` — Agent Skill (workflow + condensed reference). `examples/` — example models, algorithms, notebooks. - `fz/_version.py` is stamped by CI (`scripts/stamp_version.py`) — never edit manually. @@ -68,7 +68,7 @@ R-dependent code or tests. Python signatures consistent — every core function has a CLI twin with the same semantics, and CLI output must stay parseable with `--format json`. - When changing the public API or CLI flags, update **all three** doc surfaces: - `README.md`, `context/`, and `skills/fz/reference.md` (the agent skill ships to users). + `README.md`, `doc/`, and `skills/fz/reference.md` (the agent skill ships to users). - Default values live in `fz/config.py` and are env-overridable (`FZ_LOG_LEVEL`, `FZ_MAX_WORKERS`, `FZ_MAX_RETRIES` (default 5), `FZ_SSH_*`, `FZ_SHELL_PATH`). - User-facing release notes go in `NEWS.md`. diff --git a/README.md b/README.md index 32c7f03..e1629ed 100644 --- a/README.md +++ b/README.md @@ -1550,7 +1550,7 @@ results = fz.fzr("input.txt", input_variables, model, calculators=calculators) - Broadcasts UDP message on port 19001 - Servers respond with their host, port, and supported codes - Useful for dynamic calculator allocation in cluster environments -- See `context/funz-protocol.md` for detailed protocol documentation +- See `doc/funz-protocol.md` for detailed protocol documentation **Protocol**: - Text-based TCP socket communication @@ -2430,7 +2430,7 @@ model = { # C:\msys64\usr\bin\grep.exe 'pressure' output.txt | C:\msys64\usr\bin\awk.exe '{print $2}' ``` -See `context/shell-path.md` and `examples/shell_path_example.md` for detailed documentation. +See `doc/shell-path.md` and `examples/shell_path_example.md` for detailed documentation. ### Timeout Configuration @@ -3185,7 +3185,7 @@ The skill contains: - **skills/fz/SKILL.md** - Step-by-step workflow for wrapping a simulation code - **skills/fz/reference.md** - Condensed API/CLI reference, JSON schemas, environment variables -- **skills/fz/algorithms.md** - Interface for writing custom fzd algorithms +- **skills/fz/algorithm-wrapper.md** - Interface for writing custom fzd algorithms See **[skills/howto.md](skills/howto.md)** for a complete walkthrough with example prompts (parametric studies, SSH execution, cache reuse, optimization, headless usage). @@ -3196,22 +3196,23 @@ See **[skills/howto.md](skills/howto.md)** for a complete walkthrough with examp - **README.md** (this file) - Complete user guide with examples - **NEWS.md** - Release notes and changelog (version 0.9.1 and later) -- **context/funz-protocol.md** - Funz protocol and UDP discovery documentation -- **context/shell-path.md** - FZ_SHELL_PATH configuration details +- **doc/funz-protocol.md** - Funz protocol and UDP discovery documentation +- **doc/shell-path.md** - FZ_SHELL_PATH configuration details ### Context Documentation -Modular documentation in the `context/` directory: - -- **context/INDEX.md** - Documentation overview and navigation -- **context/overview.md** - High-level FZ concepts and design -- **context/core-functions.md** - API reference for fzi, fzc, fzo, fzr, fzl, fzd -- **context/calculators.md** - Calculator types, URIs, and configuration -- **context/model-definition.md** - Model structure, aliases, and output parsing -- **context/formulas-and-interpreters.md** - Formula evaluation (Python/R) -- **context/syntax-guide.md** - Input template syntax reference -- **context/parallel-and-caching.md** - Performance optimization strategies -- **context/quick-examples.md** - Common usage patterns and snippets +Modular documentation in the `doc/` directory: + +- **doc/INDEX.md** - Documentation overview and navigation +- **doc/overview.md** - High-level FZ concepts and design +- **doc/core-functions.md** - API reference for fzi, fzc, fzo, fzr, fzl, fzd +- **doc/installing-models.md** - Installing models and algorithms (`fz install`) +- **doc/calculators.md** - Calculator types, URIs, and configuration +- **doc/model-definition.md** - Model structure, aliases, and output parsing +- **doc/formulas-and-interpreters.md** - Formula evaluation (Python/R) +- **doc/syntax-guide.md** - Input template syntax reference +- **doc/parallel-and-caching.md** - Performance optimization strategies +- **doc/quick-examples.md** - Common usage patterns and snippets ### Examples diff --git a/context/INDEX.md b/doc/INDEX.md similarity index 93% rename from context/INDEX.md rename to doc/INDEX.md index f498494..af75120 100644 --- a/context/INDEX.md +++ b/doc/INDEX.md @@ -5,6 +5,7 @@ Quick reference index for finding specific topics in the FZ context documentatio ## Table of Contents - [Getting Started](#getting-started) +- [Installing Models & Algorithms](#installing-models--algorithms) - [Variable Substitution](#variable-substitution) - [Formula Evaluation](#formula-evaluation) - [Core Functions](#core-functions) @@ -23,10 +24,21 @@ Quick reference index for finding specific topics in the FZ context documentatio | What is FZ? | overview.md | "What is FZ?" | | When to use FZ | overview.md | "When to Use FZ" | | Quick example | overview.md | "Quick Example" | -| Four core functions | overview.md | "Four Core Functions" | +| Core functions | overview.md | "Core Functions" | | Typical workflow | overview.md | "Typical Workflow" | | Installation | *(see README.md)* | - | +## Installing Models & Algorithms + +| Topic | File | Section | +|-------|------|---------| +| Install a model | installing-models.md | "CLI" / "Python API" | +| Install an fzd algorithm | installing-models.md | "CLI" / "Python API" | +| Uninstall a model/algorithm | installing-models.md | "CLI" | +| Source resolution (name/URL/zip) | installing-models.md | "Source resolution" | +| `fz-` repository convention | installing-models.md | "Source resolution" | +| Project-local vs global install | installing-models.md | "Install location and discovery" | + ## Variable Substitution | Topic | File | Section | @@ -186,6 +198,7 @@ Quick reference index for finding specific topics in the FZ context documentatio | Task | Start Here | |------|-----------| | Get started with FZ | overview.md | +| Install a ready-made model or algorithm | installing-models.md | | Write an input template | syntax-guide.md | | Use formulas to calculate values | formulas-and-interpreters.md | | Parse input variables | core-functions.md → "fzi" | @@ -221,6 +234,9 @@ Quick reference index for finding specific topics in the FZ context documentatio Quick keyword search: - **Variables**: syntax-guide.md +- **Install**: installing-models.md +- **fz install**: installing-models.md → "CLI" +- **Algorithm install**: installing-models.md - **Formulas**: syntax-guide.md, formulas-and-interpreters.md - **Python**: formulas-and-interpreters.md - **R**: formulas-and-interpreters.md diff --git a/context/README.md b/doc/README.md similarity index 82% rename from context/README.md rename to doc/README.md index 779e17e..39ae843 100644 --- a/context/README.md +++ b/doc/README.md @@ -16,7 +16,7 @@ These context files are designed to: ### 1. `overview.md` - Framework Introduction High-level overview of FZ including: - What FZ is and when to use it -- The four core functions (fzi, fzc, fzo, fzr) +- The six core functions (fzi, fzc, fzo, fzr, fzl, fzd) - Key concepts and typical workflows - Common patterns for different scenarios @@ -32,16 +32,27 @@ Detailed syntax reference for: **Use when**: Writing input templates or working with formulas ### 3. `core-functions.md` - API Reference -Comprehensive guide to the four main functions: +Comprehensive guide to the six core functions: - `fzi()` - Parse input variables - `fzc()` - Compile input files - `fzo()` - Read output files - `fzr()` - Run parametric calculations +- `fzl()` - List and validate installed models / calculators +- `fzd()` - Adaptive design of experiments - Function signatures, parameters, and return values - Examples for each function **Use when**: Using FZ API functions in Python code +### 3b. `installing-models.md` - Installing Models and Algorithms +How to obtain ready-made wrappers and `fzd` algorithms: +- `fz install model|algorithm` and `fz uninstall` (CLI) +- `install_model`/`install_algorithm`/`list_installed_*` (Python API) +- Source resolution (short name, GitHub URL, local zip) and the `fz-` convention +- Project-local vs `--global` install locations + +**Use when**: Wrapping a known code, or adding an optimization/sampling algorithm + ### 4. `model-definition.md` - Model Configuration Complete model definition guide covering: - Model structure and all fields @@ -56,6 +67,8 @@ Complete model definition guide covering: Guide to all calculator types: - `sh://` - Local shell execution - `ssh://` - Remote SSH execution +- `slurm://` - SLURM workload manager +- `funz://` - Legacy Funz server - `cache://` - Cached result reuse - Calculator aliases and configuration - Parallel execution and fallback chains @@ -138,17 +151,21 @@ Task: How do I speed up my parametric study with 1000 cases? ## File Organization ``` -context/ +doc/ ├── README.md # This file ├── INDEX.md # Table of contents with sections ├── overview.md # High-level framework introduction ├── syntax-guide.md # Variable and formula syntax -├── core-functions.md # API reference (fzi, fzc, fzo, fzr) +├── core-functions.md # API reference (fzi, fzc, fzo, fzr, fzl, fzd) +├── installing-models.md # Installing models and algorithms ├── model-definition.md # Model configuration guide ├── calculators.md # Calculator types and configuration ├── formulas-and-interpreters.md # Formula evaluation (Python/R) -├── parallel-and-caching.md # Parallel execution and caching -└── quick-examples.md # Common patterns and examples +├── parallel-and-caching.md # Parallel execution and caching +├── fzd_content_format.md # fzd analysis content formats +├── quick-examples.md # Common patterns and examples +├── funz-protocol.md # Legacy Funz server protocol +└── shell-path.md # FZ_SHELL_PATH configuration ``` ## Updating This Documentation @@ -178,6 +195,6 @@ To improve this documentation: ## Version -These context files are for **FZ version 0.9.0+** +These docs are for **fz version 1.0+** -Last updated: 2025-01-XX +Last updated: 2026-06-13 diff --git a/context/calculators.md b/doc/calculators.md similarity index 100% rename from context/calculators.md rename to doc/calculators.md diff --git a/context/core-functions.md b/doc/core-functions.md similarity index 100% rename from context/core-functions.md rename to doc/core-functions.md diff --git a/context/formulas-and-interpreters.md b/doc/formulas-and-interpreters.md similarity index 100% rename from context/formulas-and-interpreters.md rename to doc/formulas-and-interpreters.md diff --git a/context/funz-protocol.md b/doc/funz-protocol.md similarity index 99% rename from context/funz-protocol.md rename to doc/funz-protocol.md index d1c59d9..87506d7 100644 --- a/context/funz-protocol.md +++ b/doc/funz-protocol.md @@ -663,6 +663,6 @@ Client Funz Server - **Funz protocol tests**: `tests/test_funz_protocol.py` - **Integration tests**: `tests/test_funz_integration.py` -- **Calculator types**: `context/calculators.md` +- **Calculator types**: `doc/calculators.md` - **Implementation**: `fz/runners.py` (run_funz_calculation function) - **Java Funz**: https://github.com/Funz/funz-calculator diff --git a/context/fzd_content_format.md b/doc/fzd_content_format.md similarity index 100% rename from context/fzd_content_format.md rename to doc/fzd_content_format.md diff --git a/doc/installing-models.md b/doc/installing-models.md new file mode 100644 index 0000000..8e3240c --- /dev/null +++ b/doc/installing-models.md @@ -0,0 +1,83 @@ +# Installing Models and Algorithms + +fz can install ready-made **models** (simulation-code wrappers) and **fzd algorithms** +(design-of-experiments / optimization strategies) from GitHub or local zip files, so you +don't have to author them by hand. This is the first thing to try when wrapping a known +code: an official wrapper may give you the parameterization syntax and output parsers for +free. + +## CLI + +```bash +# Install a model into the current project (./.fz/models/) +fz install model perfectgas +fz install model https://github.com/Funz/fz-modelica +fz install model ./fz-mycode.zip + +# Install globally (~/.fz/models/) so every project can use it +fz install model perfectgas --global + +# Install an fzd algorithm (into ./.fz/algorithms/ or, with --global, ~/.fz/algorithms/) +fz install algorithm brent +fz install algorithm https://github.com/Funz/fz-montecarlo + +# Remove an installed resource (by name) +fz uninstall model perfectgas +fz uninstall algorithm brent +fz uninstall model perfectgas --global + +# See what is installed (models and calculators), optionally validating each +fz list +fz list --check +``` + +`fz install` requires fz 1.0+. + +## Python API + +```python +import fz + +fz.install_model("perfectgas") # → ./.fz/models/ +fz.install_model("perfectgas", global_install=True) # → ~/.fz/models/ +fz.install_algorithm("brent") +fz.uninstall_model("perfectgas") +fz.uninstall_algorithm("brent") + +fz.list_installed_models() # dict of installed models +fz.list_installed_algorithms() # dict of installed algorithms +``` + +## Source resolution + +The `source` argument accepts three forms (resolved by `normalize_github_url`): + +| Form | Example | Resolves to | +|------|---------|-------------| +| Short name | `perfectgas` | `https://github.com/Funz/fz-perfectgas` (the `fz-` prefix and `Funz` org are assumed) | +| Full GitHub URL | `https://github.com/you/fz-mycode` | that repository's `main` archive | +| Local zip / path | `./fz-mycode.zip` | the local file, unpacked directly | + +By convention an installable repository is named `fz-` and ships a `.fz/` directory: + +``` +fz-mycode/ +└── .fz/ + ├── models/MyCode.json # the model definition (id, varprefix, output parsers) + ├── calculators/*.json|*.sh # optional calculator aliases wired to the model + └── algorithms/*.py|*.R # for algorithm repositories +``` + +On install, fz copies the model JSON into `.fz/models/` (or `~/.fz/models/` with +`--global`) and any accompanying `.fz/` subdirectories (calculators, algorithms, …) +alongside it. + +## Install location and discovery + +- **Project-local** (default): `./.fz/` — visible only inside the current project. +- **Global** (`--global`): `~/.fz/` — visible from every project for the current user. + +Installed models are then referenced by id (`--model MyCode`), and installed calculator +aliases are auto-discovered, so a single-case run needs no `--calculators` argument. See +[Calculators](calculators.md) for calculator aliases and [Model definition](model-definition.md) +for the model JSON structure, and [Core functions](core-functions.md) for `fzl` / `fz list`. diff --git a/context/model-definition.md b/doc/model-definition.md similarity index 100% rename from context/model-definition.md rename to doc/model-definition.md diff --git a/context/overview.md b/doc/overview.md similarity index 91% rename from context/overview.md rename to doc/overview.md index 77d0bf2..8a9b848 100644 --- a/context/overview.md +++ b/doc/overview.md @@ -32,7 +32,9 @@ Use FZ when you need to: 4. **Scale calculations**: Execute on local machines, HPC clusters, or both 5. **Resume interrupted runs**: Gracefully handle Ctrl+C and resume using cache -## Four Core Functions +## Core Functions + +fz exposes six core functions, each with a CLI twin of the same name: ```python import fz @@ -57,8 +59,19 @@ results = fz.fzr( results_dir="results" ) # Returns: DataFrame with all results + +# 5. fzl - List and validate installed models / calculators +fz.fzl() + +# 6. fzd - Adaptive design of experiments (optimization, sampling, inverse problems) +results = fz.fzd("input.txt", {"x": "[0;10]"}, model, "result", + algorithm="brent", calculators="sh://bash calc.sh") ``` +See [Core functions](core-functions.md) for full signatures, and +[Installing models and algorithms](installing-models.md) to obtain ready-made wrappers +and `fzd` algorithms. + ## Quick Example **Input template** (`input.txt`): diff --git a/context/parallel-and-caching.md b/doc/parallel-and-caching.md similarity index 100% rename from context/parallel-and-caching.md rename to doc/parallel-and-caching.md diff --git a/context/quick-examples.md b/doc/quick-examples.md similarity index 100% rename from context/quick-examples.md rename to doc/quick-examples.md diff --git a/context/shell-path.md b/doc/shell-path.md similarity index 98% rename from context/shell-path.md rename to doc/shell-path.md index 4653f5c..a67a162 100644 --- a/context/shell-path.md +++ b/doc/shell-path.md @@ -421,7 +421,7 @@ jobs: ## See Also - **Shell path example**: `examples/shell_path_example.md` -- **Configuration guide**: `context/overview.md` -- **Calculator types**: `context/calculators.md` +- **Configuration guide**: `doc/overview.md` +- **Calculator types**: `doc/calculators.md` - **Source code**: `fz/shell_path.py` - **Tests**: `tests/test_shell_path.py` diff --git a/context/syntax-guide.md b/doc/syntax-guide.md similarity index 100% rename from context/syntax-guide.md rename to doc/syntax-guide.md diff --git a/llms.txt b/llms.txt index bc60ee6..2f0ebfc 100644 --- a/llms.txt +++ b/llms.txt @@ -24,13 +24,14 @@ extract outputs; *calculators* (URIs like `sh://cmd`, `ssh://user@host/cmd`, ## Documentation - [README](README.md): complete user guide with examples -- [Core functions](context/core-functions.md): fzi, fzc, fzo, fzr, fzl, fzd in detail -- [Input syntax guide](context/syntax-guide.md): variables, formulas, defaults -- [Model definition](context/model-definition.md): model structure, aliases, output parsing -- [Calculators](context/calculators.md): calculator types, URIs, configuration -- [Formulas and interpreters](context/formulas-and-interpreters.md): Python/R formula evaluation -- [Parallelism and caching](context/parallel-and-caching.md): performance strategies -- [Quick examples](context/quick-examples.md): common usage patterns +- [Core functions](doc/core-functions.md): fzi, fzc, fzo, fzr, fzl, fzd in detail +- [Installing models and algorithms](doc/installing-models.md): fz install model|algorithm, the fz- convention +- [Input syntax guide](doc/syntax-guide.md): variables, formulas, defaults +- [Model definition](doc/model-definition.md): model structure, aliases, output parsing +- [Calculators](doc/calculators.md): calculator types, URIs, configuration +- [Formulas and interpreters](doc/formulas-and-interpreters.md): Python/R formula evaluation +- [Parallelism and caching](doc/parallel-and-caching.md): performance strategies +- [Quick examples](doc/quick-examples.md): common usage patterns ## Examples @@ -41,5 +42,5 @@ extract outputs; *calculators* (URIs like `sh://cmd`, `ssh://user@host/cmd`, ## Optional - [Release notes](NEWS.md) -- [Funz protocol](context/funz-protocol.md): legacy Java Funz server protocol -- [Shell path configuration](context/shell-path.md): bash on Windows (FZ_SHELL_PATH) +- [Funz protocol](doc/funz-protocol.md): legacy Java Funz server protocol +- [Shell path configuration](doc/shell-path.md): bash on Windows (FZ_SHELL_PATH) diff --git a/skills/fz/SKILL.md b/skills/fz/SKILL.md index afb1533..4452771 100644 --- a/skills/fz/SKILL.md +++ b/skills/fz/SKILL.md @@ -69,7 +69,7 @@ This drops into the project's `.fz/` directory (add `--global` for `~/.fz/`): With a wrapper installed, skip to step 1 just to add `$var` markers to your input file, then verify with steps 3–6 as usual. Write a custom model (step 2) only when no wrapper exists for your code — and if it should be reusable or published as `fz-`, follow -[wrapper.md](wrapper.md). +[code-wrapper.md](code-wrapper.md). ### 1. Parameterize input files @@ -248,7 +248,7 @@ by bare name (or glob): `algorithm="montecarlo_uniform"`. Check the `#options:` Points already evaluated are cached across iterations and across re-runs. To write a custom algorithm (a Python class with `get_initial_design` / `get_next_design` / `get_analysis`), -read [algorithms.md](algorithms.md). +read [algorithm-wrapper.md](algorithm-wrapper.md). ## Tips for agents diff --git a/skills/fz/algorithm-wrapper.md b/skills/fz/algorithm-wrapper.md new file mode 100644 index 0000000..bb06058 --- /dev/null +++ b/skills/fz/algorithm-wrapper.md @@ -0,0 +1,126 @@ +# Writing and packaging fzd algorithms + +An fzd algorithm is a Python (or R) file containing one class that proposes points to +evaluate, receives results, and decides when to stop. Pass its path as `algorithm=` to +`fz.fzd` (or `--algorithm` on the CLI). Built-in examples live in `examples/algorithms/` +(`randomsampling.py`, `montecarlo_uniform.py`, `bfgs.py`, `brent.py`). + +This is the algorithm analog of [code-wrapper.md](code-wrapper.md): the two kinds of +reusable, installable `fz-` packages are **code wrappers** (bind a simulation code) +and **algorithm packages** (a `fzd` design-of-experiments / optimization strategy). This +guide covers both writing one (below) and publishing it (last two sections). + +## File format + +```python +#title: My Algorithm +#author: Name +#options: max_iter=100;tol=1e-6 +#require: numpy;scipy + +class MyAlgorithm: + def __init__(self, **options): + # options arrive as strings from #options defaults or user-provided + # algorithm_options — cast them yourself + self.max_iter = int(options.get("max_iter", 100)) + self.iteration = 0 + + def get_initial_design(self, input_vars, output_vars): + """First batch of points to evaluate. + + input_vars: Dict[str, Tuple[float, float]] — {"x": (min, max), ...} + output_vars: List[str] — output names, e.g. ["pressure"] + returns: List[Dict[str, float]] — points to evaluate + """ + self.bounds = input_vars + return [{v: (lo + hi) / 2 for v, (lo, hi) in input_vars.items()}] + + def get_next_design(self, previous_input_vars, previous_output_values): + """Next batch, given all results so far. + + previous_input_vars: List[Dict[str, float]] — all evaluated points + previous_output_values: List[float] — corresponding outputs; None = failed case + returns: List[Dict[str, float]], or [] to stop + """ + self.iteration += 1 + if self.iteration >= self.max_iter: + return [] + ... + return [next_point] + + def get_analysis(self, input_vars, output_values): + """Final analysis once finished. + + returns: dict with at least "text" (human summary) and "data" (dict of values); + may include HTML/plot file paths. + """ + best = min((y, x) for x, y in zip(input_vars, output_values) if y is not None) + return {"text": f"best: {best}", "data": {"best_output": best[0], + "best_input": best[1]}} +``` + +## Metadata headers + +The leading `#key: value` comment lines (before the first code line) are parsed by fz: + +- **`#title`** / **`#author`** — descriptive metadata, shown when listing algorithms. +- **`#options: key=value;key2=value2`** — default constructor options. They arrive in + `__init__(**options)` as **strings** (cast them yourself). A user overrides any of them + via `algorithm_options=` on `fz.fzd` (`--options '{"max_iter": 50}'` / `-o` on the CLI, + taking inline JSON or a JSON file path). +- **`#require: pkg1;pkg2`** — Python packages the algorithm imports. On load, fz imports + each and, **if missing, pip-installs it automatically** into the current environment. + List only what you actually need, and prefer widely available packages. + +## Rules + +- The interface uses plain lists/dicts of floats, not numpy arrays — convert internally + if needed: `X = np.array([[p[v] for v in var_names] for p in points])`. +- `previous_output_values` may contain `None` for failed cases — always filter: + `valid = [(x, y) for x, y in zip(xs, ys) if y is not None]`. +- Returning `[]` from `get_next_design` ends the loop. +- Batches may contain several points: fz evaluates them in parallel on the available + calculators and deduplicates repeated points automatically. +- fzd caches all evaluated points across iterations and re-runs, so proposing a + previously seen point costs nothing. + +## Packaging as an installable `fz-` algorithm + +To make an algorithm installable with `fz install algorithm `, ship it in a GitHub +repo named `fz-` (lowercase) with the file under `.fz/algorithms/`: + +``` +fz-myalgo/ # GitHub repo named fz- +├── .fz/ +│ └── algorithms/ +│ └── myalgo.py # the algorithm file (or .R) +├── tests/ # recommended: a tiny end-to-end fzd case +└── README.md +``` + +- A bare name resolves to `https://github.com/Funz/fz-` (official algorithms live + under the Funz organization); any git URL or local zip also works. +- On install, the file lands in the project's `./.fz/algorithms/` (or, with `--global`, + `~/.fz/algorithms/`). `fzd` then accepts `--algorithm myalgo` without a full path. +- Both `.py` and `.R` algorithm files are supported (the installer globs + `.fz/algorithms/*.py` and `*.R`). + +## Test it like a user would + +From a scratch directory, install and run a known problem whose answer you can check: + +```bash +fz install algorithm ./fz-myalgo.zip # or the repo path / URL +fz list # algorithm available? + +# drive it through fzd on a simple input (see code-wrapper.md for wrapping the code) +fzd --input_path tests/input.txt --model MyCode \ + --input_variables '{"x": "[0;10]"}' --output_expression "result" \ + --algorithm myalgo --options '{"max_iter": 20}' --format json +``` + +Ship that as `tests/test.sh` in the repo. Publish conventions match code wrappers: repo +named `fz-`, default branch `main` (the installer fetches +`archive/refs/heads/main.zip`), and a README stating the options, what the algorithm +optimizes/samples, and a copy-paste quickstart. See [code-wrapper.md](code-wrapper.md) +for the shared `fz-` packaging conventions. diff --git a/skills/fz/algorithms.md b/skills/fz/algorithms.md deleted file mode 100644 index 96c6260..0000000 --- a/skills/fz/algorithms.md +++ /dev/null @@ -1,67 +0,0 @@ -# Writing custom fzd algorithms - -An fzd algorithm is a Python file containing one class that proposes points to evaluate, -receives results, and decides when to stop. Pass its path as `algorithm=` to `fz.fzd` -(or `--algorithm` on the CLI). Built-in examples live in `examples/algorithms/` -(`randomsampling.py`, `montecarlo_uniform.py`, `bfgs.py`, `brent.py`). - -## File format - -```python -#title: My Algorithm -#author: Name -#options: max_iter=100;tol=1e-6 -#require: numpy;scipy - -class MyAlgorithm: - def __init__(self, **options): - # options arrive as strings from #options defaults or user-provided - # algorithm_options — cast them yourself - self.max_iter = int(options.get("max_iter", 100)) - self.iteration = 0 - - def get_initial_design(self, input_vars, output_vars): - """First batch of points to evaluate. - - input_vars: Dict[str, Tuple[float, float]] — {"x": (min, max), ...} - output_vars: List[str] — output names, e.g. ["pressure"] - returns: List[Dict[str, float]] — points to evaluate - """ - self.bounds = input_vars - return [{v: (lo + hi) / 2 for v, (lo, hi) in input_vars.items()}] - - def get_next_design(self, previous_input_vars, previous_output_values): - """Next batch, given all results so far. - - previous_input_vars: List[Dict[str, float]] — all evaluated points - previous_output_values: List[float] — corresponding outputs; None = failed case - returns: List[Dict[str, float]], or [] to stop - """ - self.iteration += 1 - if self.iteration >= self.max_iter: - return [] - ... - return [next_point] - - def get_analysis(self, input_vars, output_values): - """Final analysis once finished. - - returns: dict with at least "text" (human summary) and "data" (dict of values); - may include HTML/plot file paths. - """ - best = min((y, x) for x, y in zip(input_vars, output_values) if y is not None) - return {"text": f"best: {best}", "data": {"best_output": best[0], - "best_input": best[1]}} -``` - -## Rules - -- The interface uses plain lists/dicts of floats, not numpy arrays — convert internally - if needed: `X = np.array([[p[v] for v in var_names] for p in points])`. -- `previous_output_values` may contain `None` for failed cases — always filter: - `valid = [(x, y) for x, y in zip(xs, ys) if y is not None]`. -- Returning `[]` from `get_next_design` ends the loop. -- Batches may contain several points: fz evaluates them in parallel on the available - calculators and deduplicates repeated points automatically. -- fzd caches all evaluated points across iterations and re-runs, so proposing a - previously seen point costs nothing. diff --git a/skills/fz/wrapper.md b/skills/fz/code-wrapper.md similarity index 98% rename from skills/fz/wrapper.md rename to skills/fz/code-wrapper.md index d9dde42..3344a2b 100644 --- a/skills/fz/wrapper.md +++ b/skills/fz/code-wrapper.md @@ -153,8 +153,8 @@ fz, bash, and the simulation code). - README: what the wrapper expects installed (the code itself, env vars like `MYCODE_BIN`), the variables syntax chosen, the outputs provided, and a copy-paste quickstart (`fz install model ` + one `fzr` line). -- Same convention for algorithm packages: `fz-` with `.fz/algorithms/.py`, - installed via `fz install algorithm `. +- fzd algorithms follow the same `fz-` packaging convention — see + [algorithm-wrapper.md](algorithm-wrapper.md). ## Existing wrappers to imitate diff --git a/skills/howto.md b/skills/howto.md index 7fcc569..325eacf 100644 --- a/skills/howto.md +++ b/skills/howto.md @@ -104,4 +104,4 @@ same test as explained, copy-pasteable shell commands. - [fz/SKILL.md](fz/SKILL.md) — the workflow guide loaded by the agent when relevant - [fz/reference.md](fz/reference.md) — condensed API/CLI reference and JSON schemas -- [fz/algorithms.md](fz/algorithms.md) — how to write custom `fzd` algorithms +- [fz/algorithm-wrapper.md](fz/algorithm-wrapper.md) — how to write custom `fzd` algorithms diff --git a/skills/test_skill_e2e.md b/skills/test_skill_e2e.md index 17e5803..fc3f38a 100644 --- a/skills/test_skill_e2e.md +++ b/skills/test_skill_e2e.md @@ -45,7 +45,7 @@ cp -r "$FZ_REPO/skills/fz" .claude/skills/ The simulation: an input file parameterized with fz syntax (`$var` variables, `@{...}` formulas) and a solver script. Deliberately **no fz model is provided** — defining it (variable syntax, output parsing command) is part of what the agent must do, guided by -the skill (SKILL.md step 2 / wrapper.md), and part of what this test verifies. +the skill (SKILL.md step 2 / code-wrapper.md), and part of what this test verifies. ```bash # input file: 3 variables, 2 compile-time formulas @@ -90,7 +90,7 @@ echo "PASS: all variables identified" ## 3. Level 2 — implement the wrapper -The real test of the skill's wrapper guide (`wrapper.md`): the agent must read `calc.sh` +The real test of the skill's wrapper guide (`code-wrapper.md`): the agent must read `calc.sh` and implement a reusable fz wrapper — model under `.fz/models/`, calculator alias under `.fz/calculators/` wired to it — and verify it per the guide's definition of done. No study yet: the wrapper gets certified first (§4), and only then do the actual studies diff --git a/tests/test_skill_e2e.py b/tests/test_skill_e2e.py index e3028a8..0087dd3 100644 --- a/tests/test_skill_e2e.py +++ b/tests/test_skill_e2e.py @@ -77,7 +77,7 @@ def setup_sandbox(sandbox: Path): Deliberately NO fz model is provided: defining it (variable syntax, output parsing command) is part of what the agent must do, guided by the skill - (SKILL.md step 2 / wrapper.md). + (SKILL.md step 2 / code-wrapper.md). """ shutil.copytree(SKILL_SRC, sandbox / ".claude/skills/fz") @@ -159,7 +159,7 @@ def test_skill_activation(tmp_path, claude_ready): def test_skill_implement_wrapper(tmp_path, claude_ready, monkeypatch): """Level 2: the agent implements a reusable fz wrapper and we certify it. - The agent must follow the skill's wrapper guide (wrapper.md): create the + The agent must follow the skill's wrapper guide (code-wrapper.md): create the model under .fz/models/ and a calculator alias under .fz/calculators/, then verify them per the guide's definition of done. We assert the wrapper structure AND run fzr ourselves through it on a parameter point the agent diff --git a/tests/test_skill_static.py b/tests/test_skill_static.py index 00a0080..941d28f 100644 --- a/tests/test_skill_static.py +++ b/tests/test_skill_static.py @@ -51,7 +51,7 @@ class TestSkillFormat: def test_skill_files_exist(self): assert SKILL_MD.exists() assert (SKILL_DIR / "reference.md").exists() - assert (SKILL_DIR / "algorithms.md").exists() + assert (SKILL_DIR / "algorithm-wrapper.md").exists() def test_name_matches_directory(self): name = re.search(r"^name:\s*(\S+)", _frontmatter(), re.M)