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 This decides whether you can afford a **population-based** algorithm
that throws hundreds of evaluations at each generation, or whether that throws hundreds of evaluations at each generation, or whether
you need a **sample-efficient** approach. heuropt's current toolkit you need a **sample-efficient** or **multi-fidelity** approach:
is mostly population-based; for budgets under a few hundred
evaluations you may want Bayesian optimization (out of scope today). - **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` The `parallel` feature flag also matters here — if your `evaluate`
function takes more than ~50 µs, enabling rayon-backed parallel 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. enforced automatically.
If your constraints are very tight and the search keeps hitting them, If your constraints are very tight and the search keeps hitting them,
consider also adding a **repair operator** (clamping, rounding, or a you have three options:
greedy fix) inside your `Variation` impl so children come out feasible
in the first place. The example `BoundedGaussianMutation` does this for - **Repair**: implement the `Repair<D>` trait (or use the provided
real-valued bounds. `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 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) │ │ Each eval has a tunable fidelity (epochs, sim steps, …)
│ │ ├─ Smooth, low-dim, expensive evals │ │ → Hyperband (implement PartialProblem; allocates
│ │ → CmaEs (sample-efficient, │ │ compute across configs adaptively)
│ │ │ │ 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)
│ │ │ │
2 or 3 (multi-objective) No → continue to the population-based branches below
│ │ │
│ │ ├─ 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)
└─ Don't forget: └─ How many objectives?
- Set a seed for reproducibility (every Config has one).
- Enable the `parallel` feature if your evaluate is expensive. ├─ 1 (single-objective)
- Use `examples/compare.rs` as a template for benchmarking │ │
multiple algorithms on your own problem. │ ├─ 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 ### Quick reference
| Algorithm | Objectives | Decision type | Strengths | **Sample-efficient / expensive evaluation (50500 evals):**
| Algorithm | Objectives | Decision | Strengths |
|---|---|---|---| |---|---|---|---|
| `RandomSearch` | any | any | sanity baseline | | `BayesianOpt` | 1 | `Vec<f64>` | GP surrogate + Expected Improvement; the gold standard |
| `HillClimber` | 1 | any | simplest greedy local search | | `Tpe` | 1 | `Vec<f64>` | KDE surrogate; robust without hyperparameter tuning |
| `SimulatedAnnealing` | 1 | any | escapes local optima, decision-type-agnostic | | `Hyperband` | 1 | any | multi-fidelity; needs `PartialProblem` |
| `TabuSearch` | 1 | any | combinatorial / discrete, you supply neighbors |
| `GeneticAlgorithm` | 1 | any | classic SO GA with elitism | **Single-objective continuous (`Vec<f64>`):**
| `ParticleSwarm` | 1 | `Vec<f64>` | simple swarm, good baseline |
| `DifferentialEvolution` | 1 | `Vec<f64>` | strong default for cheap continuous problems | | Algorithm | Strengths |
| `CmaEs` | 1 | `Vec<f64>` | sample-efficient, smooth landscapes | |---|---|
| `Umda` | 1 | `Vec<bool>` | independent-bit binary problems | | `RandomSearch` | sanity baseline |
| `AntColonyTsp` | 1 | `Vec<usize>` | TSP / permutation problems | | `HillClimber` | simplest greedy local search |
| `Paes` | 23 | any (variation defines) | 1+1 ES with archive | | `OnePlusOneEs` | one-fifth-rule self-adapting baseline |
| `Nsga2` | 23 | any | canonical multi-objective EA | | `SimulatedAnnealing` | escapes local optima |
| `Spea2` | 23 | any | strength + density-based MOEA | | `GeneticAlgorithm` | classic SO GA with elitism |
| `Moead` | 2+ | any | decomposition-based, fast per gen | | `ParticleSwarm` | simple swarm baseline |
| `Mopso` | 23 | `Vec<f64>` | multi-objective PSO with archive | | `DifferentialEvolution` | strong default for cheap continuous |
| `Ibea` | 2+ | any | indicator-based, scales to many obj | | `Tlbo` | parameter-free (no F, CR, w, σ) |
| `Nsga3` | 4+ | any | reference-point niching for many-obj | | `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 ## Current algorithms
The full list with one-line descriptions: 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:** **Single-objective:**
- `RandomSearch` — sample-evaluate-keep baseline. - `RandomSearch` — sample-evaluate-keep baseline.
- `HillClimber` — greedy single-step local search. - `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. - `SimulatedAnnealing` — Kirkpatrick et al. 1983, generic over decision type.
- `TabuSearch` — Glover 1986, with a user-supplied neighbor generator. - `TabuSearch` — Glover 1986, with a user-supplied neighbor generator.
- `GeneticAlgorithm` — generational GA with tournament selection + elitism. - `GeneticAlgorithm` — generational GA with tournament selection + elitism.
- `ParticleSwarm` — Eberhart & Kennedy 1995 PSO for `Vec<f64>`. - `ParticleSwarm` — Eberhart & Kennedy 1995 PSO for `Vec<f64>`.
- `DifferentialEvolution` — Storn & Price DE/rand/1/bin 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. - `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>`. - `Umda` — Mühlenbein 1997 univariate marginal-distribution EDA for `Vec<bool>`.
- `AntColonyTsp` — Dorigo Ant System for permutation problems. - `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. - `Moead` — Zhang & Li 2007 decomposition-based MOEA with Tchebycheff scalarization.
- `Mopso` — Coello, Pulido & Lechuga 2004 multi-objective PSO. - `Mopso` — Coello, Pulido & Lechuga 2004 multi-objective PSO.
- `Ibea` — Zitzler & Künzli 2004 indicator-based EA. - `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+):** **Many-objective (4+):**
- `Nsga3` — Deb & Jain 2014 reference-point NSGA-III. - `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`, **Reusable utilities:** `pareto_compare`, `pareto_front`, `best_candidate`,
`non_dominated_sort`, `crowding_distance`, `ParetoArchive`, `das_dennis`, `non_dominated_sort`, `crowding_distance`, `ParetoArchive`, `das_dennis`,