docs(readme): update algorithm-selection decision tree for v0.3.0

The DT was written when v0.2.0 shipped. v0.3.0 added a whole regime
(expensive evaluation, multi-fidelity) plus new entries in existing
regimes (CMA-ES restart variant, smooth SO direct search, parameter-
free SO, etc.) — fold them in.

Specifically:
- New top-level branch on "how expensive is each evaluation?" so the
  sample-efficient algorithms (BayesianOpt, Tpe) and multi-fidelity
  ones (Hyperband) have a clear home.
- Continuous-SO branch gains IPOP-CMA-ES (multimodal), Nelder-Mead
  (smooth, low-dim), (1+1)-ES (cheap baseline), sNES (high-dim
  alternative to CMA-ES), Tlbo (parameter-free).
- Multi-objective branches gain SMS-EMOA, HypE, ε-MOEA, PESA-II,
  AGE-MOEA, GrEA, KnEA, RVEA — placed by their distinguishing
  characteristic (geometry-aware, knee-points, grid-based, etc.)
- Quick-reference table extended to all 35 algorithms and grouped by
  paradigm.
This commit is contained in:
2026-05-05 10:30:45 -06:00
parent 9ae1df68cb
commit 6faff0204d
+197 -90
View File
@@ -180,9 +180,18 @@ money) force you to be sample-efficient: 50500 evaluations total.
This decides whether you can afford a **population-based** algorithm
that throws hundreds of evaluations at each generation, or whether
you need a **sample-efficient** approach. heuropt's current toolkit
is mostly population-based; for budgets under a few hundred
evaluations you may want Bayesian optimization (out of scope today).
you need a **sample-efficient** or **multi-fidelity** approach:
- **Cheap (1k+ evals affordable):** any of the population-based
algorithms — DE, GA, CMA-ES, NSGA-II, etc.
- **Expensive (50500 evals):** `BayesianOpt` (Gaussian-process
surrogate + Expected Improvement) or `Tpe` (Parzen-density
surrogate, cheaper per step, more robust without hyperparameter
tuning).
- **Multi-fidelity (each eval has a tunable budget — epochs, sim
steps, MC samples):** `Hyperband`. Implement the `PartialProblem`
trait on your problem and Hyperband allocates compute aggressively
across promising configs.
The `parallel` feature flag also matters here — if your `evaluate`
function takes more than ~50 µs, enabling rayon-backed parallel
@@ -199,10 +208,19 @@ violation magnitude, so the rule "feasibility comes first" is
enforced automatically.
If your constraints are very tight and the search keeps hitting them,
consider also adding a **repair operator** (clamping, rounding, or a
greedy fix) inside your `Variation` impl so children come out feasible
in the first place. The example `BoundedGaussianMutation` does this for
real-valued bounds.
you have three options:
- **Repair**: implement the `Repair<D>` trait (or use the provided
`ClampToBounds` / `ProjectToSimplex` impls) to in-place project
infeasible decisions back into the feasible region. Pair with a
`Variation` operator to get bounds-aware variants without writing a
custom `Variation` impl.
- **Stochastic ranking**: use `stochastic_ranking_select` instead of
`tournament_select_single_objective`. It probabilistically explores
near-feasibility instead of strict feasibility-first ordering, which
helps when feasible regions are narrow.
- **Penalty-only**: stick with `constraint_violation` — the simplest,
works well when the feasible region is large and convex.
---
@@ -213,113 +231,194 @@ A flow you can run mentally:
```
START
├─ How many objectives?
├─ Is each evaluation EXPENSIVE (>1 sec) or BUDGETED (50500 total)?
│ │
│ ├─ 1 (single-objective)
│ ├─ Yes → sample-efficient regime
│ │ ├─ Standard expensive black-box, single-objective
│ │ │ → BayesianOpt (GP + Expected Improvement; gold standard)
│ │ │ → Tpe (KDE-based; cheaper per-step,
│ │ │ more robust without tuning)
│ │ │
│ │ Decision is Vec<f64> (continuous)
│ │ ├─ Smooth, low-dim, expensive evals
│ │ → CmaEs (sample-efficient,
│ │ │ │ invariant to scale & rotation)
│ │ │ ├─ Multimodal or jagged
│ │ │ │ → DifferentialEvolution
│ │ │ │ → ParticleSwarm
│ │ │ │ → SimulatedAnnealing (cheap & generic)
│ │ │ ├─ Just want a strong default
│ │ │ │ → DifferentialEvolution (rarely beaten on cheap
│ │ │ │ evaluators)
│ │ │ └─ Just want a baseline
│ │ │ → RandomSearch
│ │ │
│ │ ├─ Decision is Vec<bool> (binary)
│ │ │ ├─ Independent bits, smooth fitness
│ │ │ │ → Umda (estimates per-bit marginals)
│ │ │ └─ Bit interactions matter
│ │ │ → GeneticAlgorithm with BitFlipMutation +
│ │ │ a bit-string crossover
│ │ │
│ │ ├─ Decision is Vec<usize> (permutation, e.g., TSP)
│ │ │ → AntColonyTsp (with a distance matrix)
│ │ │ → TabuSearch (with your own neighbor function)
│ │ │ → SimulatedAnnealing with SwapMutation
│ │ │
│ │ └─ Custom decision type (a struct, a tree, …)
│ │ → SimulatedAnnealing or HillClimber
│ │ with your own Variation impl
│ │ (the trait is generic over D)
│ │ Each eval has a tunable fidelity (epochs, sim steps, …)
│ │ → Hyperband (implement PartialProblem; allocates
│ │ compute across configs adaptively)
│ │
2 or 3 (multi-objective)
│ │ │
│ │ ├─ Just want a strong default
│ │ │ → Nsga2 (canonical, fast, well-understood)
│ │ │
│ │ ├─ Convergence quality matters more than speed
│ │ │ → Spea2 (slower, comparable quality)
│ │ │ → Ibea (often beats Nsga2 on tough fronts)
│ │ │
│ │ ├─ Want decomposition / weight-vector style
│ │ │ → Moead (very fast per generation, scales well)
│ │ │
│ │ ├─ Real-valued and want swarm style
│ │ │ → Mopso (good on simple 2-obj fronts)
│ │ │
│ │ └─ Just one starting decision (no population budget)
│ │ → Paes (1+1 ES with a Pareto archive)
│ │
│ └─ 4+ (many-objective)
│ │
│ ├─ Just want a strong default
│ │ → Nsga3 (reference-point niching, the canonical
│ │ many-obj choice)
│ │ → Moead (also scales naturally past 3 objectives)
│ │
│ └─ Convergence vs diversity tradeoff matters
│ → Ibea (indicator-based, doesn't lose discrimination
│ at high obj count)
No → continue to the population-based branches below
└─ Don't forget:
- Set a seed for reproducibility (every Config has one).
- Enable the `parallel` feature if your evaluate is expensive.
- Use `examples/compare.rs` as a template for benchmarking
multiple algorithms on your own problem.
└─ How many objectives?
├─ 1 (single-objective)
│ │
│ ├─ Decision is Vec<f64> (continuous)
│ │ ├─ Smooth landscape (well-conditioned)
│ │ │ → CmaEs (full-cov adaptive Gaussian)
│ │ │ → SeparableNes (cheaper diag-cov; high-dim)
│ │ │ → NelderMead (low-dim, deterministic, simple)
│ │ ├─ Multimodal landscape
│ │ │ → IpopCmaEs (CMA-ES with restart;
│ │ │ fixes vanilla CMA-ES's
│ │ │ multimodal failure)
│ │ │ → DifferentialEvolution (rarely beaten on cheap
│ │ │ multimodal continuous)
│ │ │ → SimulatedAnnealing (cheap & generic)
│ │ ├─ Want parameter-free (no F, CR, w, σ to tune)
│ │ │ → Tlbo
│ │ ├─ Want minimum self-adapting baseline
│ │ │ → OnePlusOneEs (one-fifth rule,
│ │ │ smallest possible ES)
│ │ ├─ Just want a strong default for cheap continuous
│ │ │ → DifferentialEvolution
│ │ └─ Just want a baseline
│ │ → RandomSearch
│ │
│ ├─ Decision is Vec<bool> (binary)
│ │ ├─ Independent bits, smooth fitness
│ │ │ → Umda (per-bit marginal EDA)
│ │ └─ Bit interactions matter
│ │ → GeneticAlgorithm with BitFlipMutation +
│ │ a bit-string crossover
│ │
│ ├─ Decision is Vec<usize> (permutation, e.g., TSP)
│ │ → AntColonyTsp (with a distance matrix)
│ │ → TabuSearch (with your own neighbor function)
│ │ → SimulatedAnnealing with SwapMutation
│ │
│ └─ Custom decision type (a struct, a tree, …)
│ → SimulatedAnnealing or HillClimber
│ with your own Variation impl
├─ 2 or 3 (multi-objective)
│ │
│ ├─ Strong default, fast, well-understood
│ │ → Nsga2
│ │
│ ├─ Want better front quality than NSGA-II
│ │ → Ibea (indicator-based; often best of the
│ │ dominance-based methods)
│ │ → SmsEmoa (hypervolume-contribution selection;
│ │ great on 23 obj at higher per-step cost)
│ │ → Spea2 (strength + density)
│ │
│ ├─ Want decomposition / weight-vector style
│ │ → Moead (very fast per generation, scales well)
│ │
│ ├─ Disconnected or non-convex front
│ │ → AgeMoea (estimates front geometry adaptively)
│ │ → Knea (favors knee points)
│ │ → Ibea
│ │
│ ├─ Want region-based diversity
│ │ → PesaII (grid hyperboxes drive selection)
│ │ → EpsilonMoea (ε-grid archive,
│ │ archive size auto-limits)
│ │
│ ├─ Real-valued and want swarm style
│ │ → Mopso
│ │
│ └─ Just one starting decision (no population budget)
│ → Paes (1+1 ES with a Pareto archive)
└─ 4+ (many-objective)
├─ Strong default
│ → Nsga3 (reference-point niching, canonical)
│ → Moead (decomposition; scales naturally)
├─ Want geometric structure inferred (vs assumed)
│ → AgeMoea (estimates L_p geometry per generation)
│ → Rvea (reference vectors with adaptive penalty)
├─ Want indicator-based selection
│ → Ibea (additive ε-indicator; doesn't degrade
│ at high obj count)
│ → Hype (Monte Carlo HV estimation; scales
│ to arbitrary M)
└─ Want grid-based diversity
→ Grea (grid coords drive ranking; particularly
good on linear/simplex fronts)
```
### Quick reference
| Algorithm | Objectives | Decision type | Strengths |
**Sample-efficient / expensive evaluation (50500 evals):**
| Algorithm | Objectives | Decision | Strengths |
|---|---|---|---|
| `RandomSearch` | any | any | sanity baseline |
| `HillClimber` | 1 | any | simplest greedy local search |
| `SimulatedAnnealing` | 1 | any | escapes local optima, decision-type-agnostic |
| `TabuSearch` | 1 | any | combinatorial / discrete, you supply neighbors |
| `GeneticAlgorithm` | 1 | any | classic SO GA with elitism |
| `ParticleSwarm` | 1 | `Vec<f64>` | simple swarm, good baseline |
| `DifferentialEvolution` | 1 | `Vec<f64>` | strong default for cheap continuous problems |
| `CmaEs` | 1 | `Vec<f64>` | sample-efficient, smooth landscapes |
| `Umda` | 1 | `Vec<bool>` | independent-bit binary problems |
| `AntColonyTsp` | 1 | `Vec<usize>` | TSP / permutation problems |
| `Paes` | 23 | any (variation defines) | 1+1 ES with archive |
| `Nsga2` | 23 | any | canonical multi-objective EA |
| `Spea2` | 23 | any | strength + density-based MOEA |
| `Moead` | 2+ | any | decomposition-based, fast per gen |
| `Mopso` | 23 | `Vec<f64>` | multi-objective PSO with archive |
| `Ibea` | 2+ | any | indicator-based, scales to many obj |
| `Nsga3` | 4+ | any | reference-point niching for many-obj |
| `BayesianOpt` | 1 | `Vec<f64>` | GP surrogate + Expected Improvement; the gold standard |
| `Tpe` | 1 | `Vec<f64>` | KDE surrogate; robust without hyperparameter tuning |
| `Hyperband` | 1 | any | multi-fidelity; needs `PartialProblem` |
**Single-objective continuous (`Vec<f64>`):**
| Algorithm | Strengths |
|---|---|
| `RandomSearch` | sanity baseline |
| `HillClimber` | simplest greedy local search |
| `OnePlusOneEs` | one-fifth-rule self-adapting baseline |
| `SimulatedAnnealing` | escapes local optima |
| `GeneticAlgorithm` | classic SO GA with elitism |
| `ParticleSwarm` | simple swarm baseline |
| `DifferentialEvolution` | strong default for cheap continuous |
| `Tlbo` | parameter-free (no F, CR, w, σ) |
| `CmaEs` | smooth landscapes; full covariance |
| `IpopCmaEs` | CMA-ES + restart for multimodal |
| `SeparableNes` | diagonal-cov NES; cheap per-step |
| `NelderMead` | classical simplex; deterministic |
**Single-objective other decision types:**
| Algorithm | Decision | Strengths |
|---|---|---|
| `Umda` | `Vec<bool>` | independent-bit EDA |
| `TabuSearch` | any | discrete, you supply neighbors |
| `AntColonyTsp` | `Vec<usize>` | TSP / permutation |
**Multi-objective (23) and many-objective (4+):**
| Algorithm | Objectives | Strengths |
|---|---|---|
| `Paes` | 23 | 1+1 ES with Pareto archive |
| `Nsga2` | 23 | canonical Pareto-based EA |
| `Spea2` | 23 | strength + density |
| `Mopso` | 23 | multi-objective PSO with archive |
| `Ibea` | 2+ | indicator-based; scales to many-obj |
| `SmsEmoa` | 2+ | hypervolume-contribution selection |
| `Hype` | 2+ | Monte Carlo HV estimation |
| `EpsilonMoea` | 2+ | ε-grid archive; auto-sized |
| `PesaII` | 2+ | grid-based region selection |
| `AgeMoea` | 2+ | adaptive front-geometry estimation |
| `Knea` | 2+ | knee-point favored survival |
| `Moead` | 2+ | decomposition; fast per-gen |
| `Nsga3` | 4+ | reference-point niching |
| `Rvea` | 4+ | reference vectors with penalty |
| `Grea` | 4+ | grid coords drive selection |
## Current algorithms
The full list with one-line descriptions:
**Sample-efficient / multi-fidelity:**
- `BayesianOpt` — Gaussian-process surrogate + Expected Improvement.
- `Tpe` — Bergstra et al. 2011 Tree-structured Parzen Estimator.
- `Hyperband` — Li et al. 2017 multi-fidelity (uses `PartialProblem`).
**Single-objective:**
- `RandomSearch` — sample-evaluate-keep baseline.
- `HillClimber` — greedy single-step local search.
- `OnePlusOneEs` — Rechenberg 1973 (1+1)-ES with one-fifth rule.
- `SimulatedAnnealing` — Kirkpatrick et al. 1983, generic over decision type.
- `TabuSearch` — Glover 1986, with a user-supplied neighbor generator.
- `GeneticAlgorithm` — generational GA with tournament selection + elitism.
- `ParticleSwarm` — Eberhart & Kennedy 1995 PSO for `Vec<f64>`.
- `DifferentialEvolution` — Storn & Price DE/rand/1/bin for `Vec<f64>`.
- `Tlbo` — Rao 2011 Teaching-Learning-Based Optimization (parameter-free).
- `CmaEs` — Hansen & Ostermeier 2001 covariance-matrix adaptation.
- `IpopCmaEs` — Auger & Hansen 2005 CMA-ES with restart, for multimodal.
- `SeparableNes` — Wierstra et al. 2008/2014 diagonal-cov NES.
- `NelderMead` — Nelder & Mead 1965 simplex direct search.
- `Umda` — Mühlenbein 1997 univariate marginal-distribution EDA for `Vec<bool>`.
- `AntColonyTsp` — Dorigo Ant System for permutation problems.
@@ -331,10 +430,18 @@ The full list with one-line descriptions:
- `Moead` — Zhang & Li 2007 decomposition-based MOEA with Tchebycheff scalarization.
- `Mopso` — Coello, Pulido & Lechuga 2004 multi-objective PSO.
- `Ibea` — Zitzler & Künzli 2004 indicator-based EA.
- `SmsEmoa` — Beume, Naujoks & Emmerich 2007 hypervolume-selection EMOA.
- `Hype` — Bader & Zitzler 2011 Hypervolume Estimation Algorithm.
- `EpsilonMoea` — Deb, Mohan & Mishra 2003 ε-dominance MOEA.
- `PesaII` — Corne et al. 2001 Pareto Envelope Selection II.
- `AgeMoea` — Panichella 2019 Adaptive Geometry Estimation MOEA.
- `Knea` — Zhang, Tian & Jin 2015 Knee point-driven EA.
**Many-objective (4+):**
- `Nsga3` — Deb & Jain 2014 reference-point NSGA-III.
- `Rvea` — Cheng et al. 2016 Reference Vector-guided EA.
- `Grea` — Yang et al. 2013 Grid-based EA.
**Reusable utilities:** `pareto_compare`, `pareto_front`, `best_candidate`,
`non_dominated_sort`, `crowding_distance`, `ParetoArchive`, `das_dennis`,