# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- **`heuropt-plot` companion crate (v0.1.0)** at `heuropt-plot/`,
published independently. Lightweight SVG-only plotter for Pareto
fronts (`pareto_front_svg`) and convergence traces
(`convergence_svg`) — hand-rolled SVG output, no `plotters` /
`tiny-skia` dep so the crate stays a tiny optional addition.
- `examples/visualize.rs` — runs NSGA-II on Schaffer N.1 with a
closure observer that records hypervolume per generation, then
emits `pareto_front.svg` + `convergence.svg` via `heuropt-plot`.
## [0.7.0] — 2026-05-05
Theme: async evaluation. heuropt now supports problems where each
evaluation is a `.await`-able operation — HTTP services, RPC clients,
spawned subprocesses. This is the differentiating capability vs.
pymoo / hyperopt / MOEA Framework, none of which ship first-class
async support.
No public-API breaks for synchronous users. The new surface is
gated behind a new `async` feature flag.
### Added
- New optional feature `async`, gated on
[`futures`](https://crates.io/crates/futures).
- `core::async_problem::AsyncProblem` trait — mirrors `Problem` but
with `async fn evaluate_async(&self, decision)`. Adapt an
existing sync `Problem` with a one-line wrapper.
- Per-algorithm `run_async(&problem, concurrency).await` methods on
`RandomSearch` and `DifferentialEvolution` — drives evaluations
through whichever async runtime the caller is using (typically
tokio). `concurrency` bounds in-flight evaluations.
- Internal `algorithms::parallel_eval_async::evaluate_batch_async`
helper — uses `futures::stream::FuturesOrdered` with concurrency-
bounded chunks, preserves input order so seeded determinism is
preserved when evaluations are themselves deterministic.
- `examples/async_eval.rs` — worked example with a simulated 20 ms
remote service. At concurrency = 1 it's serial; at concurrency = 4
it's 2× faster; demonstrates DifferentialEvolution under tokio.
[0.7.0]: https://github.com/swaits/heuropt/releases/tag/v0.7.0
## [0.6.0] — 2026-05-05
Theme: production lifecycle. heuropt becomes deployable for long-
running, real-world optimization workloads — callbacks, stop
conditions, tracing, and two new performance indicators.
No breaking changes to the public API. Existing `Optimizer
` impls
keep compiling — `run_with` is added as a default-impl method that
falls back to `run` plus a single final notification.
### Added
#### Observer + stop-conditions API
A new module `heuropt::observer` introduces:
- `Snapshot<'a, D>` — per-generation observation payload with
`iteration`, `evaluations`, `elapsed`, `population`,
`pareto_front`, `best`, and `objectives`.
- `Observer` trait — single method `observe(&Snapshot) ->
ControlFlow<()>`. Closures of the right shape implement it
automatically. `()` is the no-op observer.
- `Optimizer::run_with(problem, observer)` — new method on the
`Optimizer` trait with a default impl that falls back to `run`.
Algorithms that override `run_with` (so far: `Nsga2`,
`RandomSearch`, `DifferentialEvolution`) call the observer once
per generation; others call it once at the end. Returning
`ControlFlow::Break` halts the optimizer and returns the partial
result.
#### Built-in observers (`observer::builtin`)
- `MaxTime(Duration)` — wall-clock cap.
- `MaxIterations(usize)` — generation cap.
- `TargetFitness(f64)` — direction-aware single-objective target.
- `Stagnation { window, tolerance }` — halt when the best fitness
hasn't improved by `tolerance` over `window` generations.
- `Periodic::new(every, |snap| { … })` — call a user closure every
`every` generations.
- `AnyOf` / `AllOf` plus `Observer::or` / `Observer::and` for
composition.
- `TracingObserver` (behind the new `tracing` feature) — emits
structured `debug!` events per generation.
#### Tracing feature
New optional feature `tracing`, gated on the
[`tracing`](https://crates.io/crates/tracing) crate. Adds
`TracingObserver` to the prelude when enabled.
#### Performance indicators
- `metrics::igd::igd` — Inverted Generational Distance against a
reference set (typically the true Pareto front).
- `metrics::igd::igd_plus` — Pareto-compliant IGD+ variant; adding
a dominated point never improves the score.
- `metrics::r2::r2` — R2 indicator using the weighted Tchebycheff
utility. Pair with `pareto::das_dennis` for the canonical weight
set.
#### Constrained example
`examples/constrained.rs` — solves the BNH constrained 2-objective
problem (Binh & Korn 1996) with NSGA-II + the new observer API,
demonstrating `Periodic` progress logging and `MaxTime` /
composition.
### Changed
- `Population::as_slice()` — new convenience accessor.
[0.6.0]: https://github.com/swaits/heuropt/releases/tag/v0.6.0
## [0.5.0] — 2026-05-05
Theme: comprehensive documentation and project polish. No public-API
changes — bumping `heuropt = "0.5"` in your `Cargo.toml` is enough.
### Added
#### User guide (mdbook)
A new mdbook user guide at `docs/book/`, deployed to
via a CI workflow on tag pushes.
Chapters:
- **Introduction** — what heuropt is, who it's for, what's in the box.
- **Five-minute walkthrough** — install, define a problem, run an
optimizer, look at the result.
- **Defining a problem** — the `Problem` trait in depth: single- vs
multi-objective, constraints, custom decision types
(`Vec`, `Vec`, `Vec`, custom structs).
- **Choosing an algorithm** — the README's decision tree, expanded
to a full chapter with the reasoning behind every branch.
- **Cookbook** — seven recipes covering parallelism, expensive
evaluations, comparison harnesses, permutation problems,
constraint repair, picking one answer off a Pareto front, and
writing your own optimizer.
- **Comparison with other libraries** — heuropt vs pymoo, hyperopt,
optuna, MOEA Framework, metaheuristics-rs, argmin. Honest about
when *not* to pick heuropt.
- **Stability and SemVer** — explicit guarantees about which surfaces
are stable; what's likely to change before 1.0; bit-identical
determinism contract.
- **Migration guides** — per-release upgrade notes.
#### Runnable rustdoc examples
Every algorithm now has a runnable ` ```rust ` example block in its
rustdoc — 35 algorithms, all exercised by `cargo test --doc`. Plus
the existing crate-level example in `lib.rs` and the
`CompositeVariation` operator example.
#### Real-world examples
Three new polished examples covering distinct domains:
- `examples/portfolio.rs` — multi-objective portfolio optimization
with budget constraint via `ProjectToSimplex`. Pareto front of
return-vs-risk trade-offs, plus a-posteriori weighted decision.
- `examples/hyperparam_tuning.rs` — sample-efficient hyperparameter
tuning with `BayesianOpt` and `Tpe`, demonstrating mixed-scale
decoding (log-uniform learning rate, integer depth) and a 60-eval
budget.
- `examples/scheduling.rs` — single-machine weighted-completion-time
scheduling: permutation decisions optimized via
`SimulatedAnnealing` + `SwapMutation`, comparing against the
Smith's-rule oracle.
#### Governance docs
- `CONTRIBUTING.md` — local-test checklist, conventional-commits
requirement, contribution areas that land easily vs. those that
need prior discussion.
- `SECURITY.md` — disclosure policy, supported versions, what counts
as a security issue.
- `CODE_OF_CONDUCT.md` — adopts the
[Builder's Code of Conduct](https://builderscode.org/) (CC0).
- `.github/ISSUE_TEMPLATE/` — bug, feature, docs templates plus a
`config.yml` that points security reports to the private
vulnerability-disclosure flow.
- `.github/PULL_REQUEST_TEMPLATE.md` — short, opinionated PR
template.
#### CI / tooling
- `.github/workflows/docs.yml` — builds the mdbook user guide and
deploys it to GitHub Pages on `main` pushes and tag pushes.
### Changed
- README hero block expanded with badges and a punchier opening;
added explicit links to the user guide, the docs.rs API reference,
and the testing-coverage breakdown.
- `lib.rs` crate-level docs polished — better intro, points readers
at the user guide and the design spec.
[0.5.0]: https://github.com/swaits/heuropt/releases/tag/v0.5.0
## [0.4.0] — 2026-05-05
Theme: testing infrastructure, two real bug fixes surfaced by that
infrastructure, and a CPU-time optimization pass that made the
comparison harness 3.27× faster end-to-end. No breaking changes to
the v0.3.0 public API.
### Performance
A focused, measure-and-iterate optimization pass on the Pareto-based
multi-objective hot paths. Every change verified bit-identical against
the v0.3.0 comparison-harness snapshot — quality metrics
(hypervolume, spacing, mean L2, mean dist, front size) match to the
last decimal in every benchmark.
**Cumulative wall-clock impact (compare harness, 10-seed mean):**
| Algorithm / Problem | v0.3.0 | v0.4.0 | Speedup |
|----------------------|--------:|--------:|--------:|
| AGE-MOEA / DTLZ1 | 2299 ms | 229 ms | 10× |
| SPEA2 / DTLZ2 | 4304 ms | 513 ms | 8.4× |
| AGE-MOEA / ZDT3 | 932 ms | 193 ms | 4.8× |
| NSGA-II / ZDT1 | 268 ms | 65 ms | 4.1× |
| NSGA-II / ZDT3 | 267 ms | 65 ms | 4.1× |
| SMS-EMOA / DTLZ2 | 5643 ms | 1369 ms | 4.1× |
| NSGA-II / Rastrigin | 260 ms | 71 ms | 3.7× |
| NSGA-II / DTLZ2 | 344 ms | 106 ms | 3.2× |
| NSGA-III / DTLZ2 | 318 ms | 122 ms | 2.6× |
| NSGA-III / DTLZ1 | 303 ms | 122 ms | 2.5× |
| HypE / DTLZ2 | 80 ms | 44 ms | 1.8× |
| **Total compare** | **18 629 ms** | **5688 ms** | **3.27×** |
**Hot-path instruction counts (gungraun):**
| Benchmark | v0.3.0 | v0.4.0 | Speedup |
|-------------------------|------------:|---------:|--------:|
| `hypervolume_nd_3d` n=100 | 13 523 760 | 367 767 | 37× |
| `hypervolume_nd_3d` n=30 | 676 902 | 70 334 | 9.6× |
| `non_dominated_sort_2d` n=200 | 13 513 271 | 2 601 813 | 5.2× |
| `non_dominated_sort_2d` n=50 | 852 317 | 198 574 | 4.3× |
| `spea2_short` | 179 113 | 133 783 | 1.34× |
**Changes (in commit order):**
- `perf(hypervolume)` — Rewrote the M≥3 HSO recursion in
`hypervolume_nd`. The original cloned the active set into a fresh
Vec> at the top of every recursive call, used a linear-scan
`position` lookup to remove the just-processed point each band, and
re-projected onto M-1 axes inside every band. Now: sort-by-index,
pre-project once, slice prefixes for the active set, and skip
`non_dominated_projection` when recursing into the M=2 base case
(whose sweep already filters dominated points internally).
- `perf(non_dominated_sort)` — Cache `as_minimization` /
feasibility / violation per individual once at the top of the
Deb fast-non-dominated-sort, then inline the dominance test against
those arrays. The naïve formulation called `pareto_compare` twice
per pair, each call allocating two fresh Vecs — 4N(N-1)
allocations per sort. Propagates to every Pareto-based MOEA.
- `perf(age_moea)` — Cache `lp_norm(translated[i], p)` once per
candidate at function entry; maintain a `nearest[]` array updated
incrementally on each pick (single `min` per remaining instead of
a fresh full scan over the keep list). Cuts the splitting-front
scoring loop from O(R · K · M) per iteration to O(R · M).
- `perf(spea2)` — Two wins. (1) `compute_fitness` (called twice per
generation): inline dominance against cached oriented arrays,
symmetric distance matrix built once. (2) `build_archive` truncation:
compute pairwise distances + sorted neighbor vectors once, then on
victim removal use binary-search-remove on every survivor's
still-sorted vector — total truncation cost O(K³ log K) → O(K² log K).
- `perf(hypervolume)` — Index-sort instead of cloning point vectors
in the M≥3 recursion. The N inner-Vec clones per HV call were
redundant once we'd already sorted by last-axis. Big bench win
(32×→37× cumulative on n=100/3D), modest wall-clock impact because
SMS-EMOA's worst-front HV calls operate on small fronts.
- `build(release)` — Enable thin LTO + codegen-units=1 in the
release profile. Worth ~150 ms across the harness; only applies
when heuropt is the workspace root, so downstream consumers see
whatever profile their own Cargo.toml configures.
- `perf(pareto_archive)` — Cache the candidate's oriented +
feasibility once per `insert`, build each member's oriented vector
once, and inline the two-pass dominance checks. Used by PESA-II
(most impact), PAES, ε-MOEA, and any user code working through the
archive directly.
### Added
- **Decision tree update** in README to cover all v0.3.0 algorithms,
with a new top-level branch on "is each evaluation expensive?" so
`BayesianOpt` / `Tpe` / `Hyperband` have a clear home.
- **Comparison results snapshot** at `examples/compare-results.md` —
reference output of the harness across 7 benchmark problems and ~20
algorithms, captured after v0.3.0 landed.
- **Instruction-count benchmarks** via `gungraun` (the Rust 2026
rename of `iai-callgrind`) at `benches/hot_paths.rs`. Covers
`non_dominated_sort`, `crowding_distance`, `hypervolume_2d`,
`hypervolume_nd` (HSO), and one-generation costs of NSGA-II and
CMA-ES, plus a short-run bench for every algorithm. Stable across
machines via callgrind.
- **Property-based test suite expansion**: `tests/properties.rs`
(Pareto-comparison antisymmetry, partitioning, operator bounds),
`tests/algorithm_properties.rs` (per-algorithm determinism +
population-size invariants — 32 tests, one per algorithm),
`tests/operator_properties.rs` (every `Variation` / `Initializer` /
`Repair` impl), `tests/metric_properties.rs` (HV / spacing
invariants), and `tests/numerical_stability.rs` (empty / singleton /
duplicate / flat-fitness / zero-width-bounds populations).
- **Coverage-guided fuzz harness** at `fuzz/` (cargo-fuzz +
libFuzzer). Eight targets covering `pareto_compare`,
`non_dominated_sort`, `hypervolume_2d`, `ParetoArchive`,
`crowding_distance`, `spacing`, SBX/PolyMut, and the `Repair`
operators. Runs in CI for a short soak per PR; longer runs locally
via `cargo +nightly fuzz run `.
- **cargo-mutants config** at `.cargo/mutants.toml` for advisory
mutation testing. Not gated in CI; run with `cargo mutants` to
surface tests that don't actually check the behavior they look like
they do.
- **GitHub Actions CI** at `.github/workflows/ci.yml` with fmt /
clippy / test (4-feature matrix) / doc / MSRV / fuzz-smoke jobs,
all gated on `-D warnings`.
### Fixed
- `pareto::sort::non_dominated_sort` previously dropped indices when
the dominance graph contained a cycle (which arises when objectives
contain NaN — `pareto_compare` becomes intransitive). Fuzzing the
partition invariant surfaced the bug; orphans now go into a final
residual front.
- `operators::repair::ProjectToSimplex` could silently return the
all-zero vector when the input vector's magnitude dwarfed `total`
(the standard Duchi/Held-Wolfe τ computation lost precision and
τ ≈ max(x), so `max(x_i - τ, 0)` rounded to zero everywhere).
Detected by the `clamp_to_bounds` fuzzer; now falls through to a
degenerate "all mass on argmax" projection above a 1e15 magnitude
ratio, and is robust to floating-point precision loss in the
algorithm's inner loop.
[0.4.0]: https://github.com/swaits/heuropt/releases/tag/v0.4.0
## [0.3.0] — 2026-05-05
Theme: filling heuropt's expensive-evaluation, gradient-free, and
constraint-handling gaps. No breaking changes to the v0.2.0 public API.
### Added
#### New algorithms (9)
**Sample-efficient / surrogate-based:**
- `BayesianOpt` — Gaussian-process Bayesian Optimization with Expected
Improvement acquisition. heuropt's first sample-efficient algorithm:
targets the 50–500 evaluation regime.
- `Tpe` — Bergstra et al. 2011 Tree-structured Parzen Estimator
(workhorse of Hyperopt and Optuna). KDE-based surrogate; cheaper
per-step than BO and more robust without hyperparameter tuning.
**Classical and modern evolution strategies:**
- `OnePlusOneEs` — Rechenberg 1973 (1+1)-ES with the one-fifth success
rule. Smallest possible self-adapting evolution strategy.
- `IpopCmaEs` — Auger & Hansen 2005 increasing-population CMA-ES with
restart. Specifically fixes vanilla CMA-ES's known weakness on
multimodal problems.
- `SeparableNes` — Wierstra et al. 2008/2014 Natural Evolution Strategy
with diagonal covariance (sNES). Different theoretical foundation
than CMA-ES; cheaper per-step at the cost of being unable to model
rotated landscapes.
**Direct search:**
- `NelderMead` — Nelder & Mead 1965 simplex method. Classical gradient-
free local optimizer; superb on low-dim smooth problems
(Rosenbrock 5-D: f = 0 exactly).
**Multi-fidelity:**
- `Hyperband` — Li et al. 2017 multi-fidelity hyperparameter optimizer
built on Successive Halving. Operates on a new `PartialProblem`
trait so configurations can be evaluated at adjustable fidelity
budgets.
#### New operators
- `LevyMutation` — heavy-tailed Lévy-flight mutation via Mantegna's
algorithm. The actual algorithmic contribution from Cuckoo Search
packaged as a reusable `Variation` operator.
#### New traits + impls
- `PartialProblem` — multi-fidelity problem contract:
`evaluate_at_budget(decision, budget) -> Evaluation`. Used by
`Hyperband`. Intentionally not a sub-trait of `Problem`.
- `Repair` — in-place projection trait for restoring decisions to
feasibility. Pair with `Variation` operators to get bounds-aware
variants. Provided impls:
- `ClampToBounds` for `Vec` per-axis clamping
- `ProjectToSimplex` for L1-budget / probability-simplex projection
#### New selection helpers
- `stochastic_ranking_select` — Runarsson & Yao 2000 stochastic
ranking. Better than strict feasibility-first tournament selection
on heavily-constrained problems.
#### Internal helpers
- `internal::cholesky` — Cholesky factorization + triangular solves
for SPD matrices, used by the GP posterior in `BayesianOpt`.
### Changed
- `CmaEsConfig` gained `initial_mean: Option>`. `None`
preserves the existing midpoint-of-bounds default; `IpopCmaEs` sets
it to inject restart diversity without shrinking the search box.
[0.3.0]: https://github.com/swaits/heuropt/releases/tag/v0.3.0
## [0.2.0] — 2026-05-05
A substantial expansion of the algorithm catalog (21 new algorithms),
five new operators, an n-D hypervolume utility, an algorithm-selection
guide in the README, and a multi-seed comparison harness covering seven
benchmark problems. No breaking changes to the v0.1.0 public API.
### Added
#### New algorithms
**Single-objective:**
- `HillClimber` — simplest greedy local search.
- `SimulatedAnnealing` — Kirkpatrick et al. 1983, generic over decision type.
- `GeneticAlgorithm` — generational SO GA with tournament selection + elitism.
- `ParticleSwarm` — Eberhart & Kennedy 1995 PSO for `Vec`.
- `CmaEs` — Hansen & Ostermeier 2001 covariance-matrix adaptation.
- `TabuSearch` — Glover 1986, with a user-supplied neighbor generator.
- `AntColonyTsp` — Dorigo Ant System for permutation problems.
- `Umda` — Mühlenbein 1997 univariate marginal-distribution EDA for
`Vec`.
- `Tlbo` — Rao 2011 Teaching-Learning-Based Optimization (parameter-free).
**Multi-objective:**
- `Mopso` — Coello, Pulido & Lechuga 2004 multi-objective PSO.
- `Ibea` — Zitzler & Künzli 2004 indicator-based EA.
- `SmsEmoa` — Beume, Naujoks & Emmerich 2007 S-metric selection EMOA.
- `Hype` — Bader & Zitzler 2011 Hypervolume Estimation Algorithm.
- `Rvea` — Cheng et al. 2016 Reference Vector-guided EA.
- `PesaII` — Corne et al. 2001 Pareto Envelope-based Selection II.
- `EpsilonMoea` — Deb, Mohan & Mishra 2003 ε-dominance MOEA.
- `AgeMoea` — Panichella 2019 Adaptive Geometry Estimation MOEA.
- `Grea` — Yang et al. 2013 Grid-based EA.
- `Knea` — Zhang, Tian & Jin 2015 Knee point-driven EA.
#### New operators
- `BoundedGaussianMutation` — Gaussian noise + per-axis clamping.
- `SimulatedBinaryCrossover` (SBX) — Deb & Agrawal 1995 canonical
real-valued crossover.
- `PolynomialMutation` — Deb's polynomial mutation, the standard NSGA-II
pair to SBX.
- `CompositeVariation` — pipeline two `Variation` operators
(typically crossover → mutation).
- `LevyMutation` — heavy-tailed Lévy-flight mutation via Mantegna's
algorithm.
#### New metrics / utilities
- `hypervolume_nd` — exact N-dimensional dominated hypervolume via the
Hypervolume-by-Slicing-Objectives (HSO) algorithm, plus an internal
Jacobi symmetric eigendecomposition helper used by CMA-ES.
#### New examples
- `compare` — multi-seed comparison harness running every applicable
algorithm across ZDT1, ZDT3, DTLZ1, DTLZ2 (multi/many-objective) and
Rastrigin, Rosenbrock, Ackley (single-objective). Reports
hypervolume, spacing, mean L2/dist, front size, and wall-clock ms.
- `benchmarks` — canonical reference runs of NSGA-II on ZDT1 and DE on
Rastrigin.
- `jiggly_tuning` — real-world 4-objective NSGA-III firmware tuning
for the [`jiggly`](https://github.com/swaits/jiggly) USB-mouse-jiggler,
with an a-posteriori weighted-decision step that picks one
recommendation off the Pareto front.
#### New optional feature
- `parallel` — rayon-backed parallel population evaluation in
`RandomSearch`, `Nsga2`, `DifferentialEvolution`, `Spea2`, `Ibea`,
`Mopso`, and most other algorithms with batchable inner loops.
Seeded runs stay bit-identical to serial mode.
#### Documentation
- README gained an explanatory algorithm-selection decision tree that
walks newcomers through choosing an optimizer, defining the
terminology (multi-objective, Pareto front, dominance, multimodality,
evaluation cost) as it goes.
### Changed
- Minimum supported Rust version remains 1.85 (edition 2024).
- Algorithm impls now require `P: Sync` and `P::Decision: Send` so the
same impl serves both `parallel` and serial feature builds. Any
`Problem` / decision type without exotic interior mutability already
satisfies these.
[0.2.0]: https://github.com/swaits/heuropt/releases/tag/v0.2.0
## [0.1.0] — 2026-05-04
Initial release.
### Core types and traits
- `Direction`, `Objective`, `ObjectiveSpace` (with `as_minimization` direction
conversion).
- `Evaluation` with feasibility (`constraint_violation <= 0.0`).
- `Candidate`, `Population`, `OptimizationResult`.
- `type Rng = rand::rngs::StdRng` and `rng_from_seed` so no public trait is
generic over the RNG.
- `Problem`, `Optimizer`, `Initializer`, `Variation`.
### Pareto utilities
- `pareto_compare`, `pareto_front`, `best_candidate`,
`non_dominated_sort` (Deb fast non-dominated sort), `crowding_distance`,
`ParetoArchive`, `das_dennis` (structured reference points for NSGA-III
and MOEA/D).
### Operators
- Real: `RealBounds`, `GaussianMutation`, `BoundedGaussianMutation`,
`SimulatedBinaryCrossover` (SBX), `PolynomialMutation`.
- Binary: `BitFlipMutation`.
- Permutation: `SwapMutation`.
- `CompositeVariation` pipeline (typically crossover → mutation).
### Selection helpers
- `select_random`, `tournament_select_single_objective`.
### Reference algorithms
- `RandomSearch` — sample-evaluate-keep baseline.
- `Paes` — small (1+1) Pareto Archived Evolution Strategy.
- `Nsga2` — canonical Pareto-based EA with crowding distance.
- `Nsga3` — many-objective NSGA-III with reference-point niching.
- `Spea2` — Strength Pareto Evolutionary Algorithm 2.
- `Moead` — decomposition-based MOEA/D with the Tchebycheff scalar.
- `DifferentialEvolution` — single-objective DE/rand/1/bin.
### Metrics
- `spacing` (Schott), `hypervolume_2d` (exact 2-D dominated hypervolume).
### Examples
- `random_search`, `toy_nsga2`, `custom_optimizer` — minimum-viable
walkthroughs.
- `benchmarks` — ZDT1 and Rastrigin reference runs.
- `compare` — multi-seed comparison harness running every applicable
algorithm on ZDT1 (2-obj), DTLZ2 (3-obj), and Rastrigin (single-obj),
reporting hypervolume, spacing, mean L2, front size, and wall time.
- `jiggly_tuning` — 4-objective NSGA-III tuning of the
[`jiggly`](https://github.com/swaits/jiggly) USB-mouse-jiggler firmware
with an a-posteriori weighted-decision step that picks one
recommendation off the Pareto front.
### Optional features
- `serde` — `Serialize` / `Deserialize` derives on the core data types.
- `parallel` — rayon-backed parallel population evaluation in
`RandomSearch`, `Nsga2`, and `DifferentialEvolution`. Seeded runs stay
bit-identical to serial mode.
[Unreleased]: https://github.com/swaits/heuropt/compare/v0.7.0...HEAD
[0.1.0]: https://github.com/swaits/heuropt/releases/tag/v0.1.0