Commit Graph
4 Commits
Author SHA1 Message Date
swaits c1bc3b0528 docs(rustdoc): add runnable examples across operators, metrics, and Pareto utilities
Completes the rustdoc audit — every public item now has at least one
```rust example block in its docstring, exercised by
`cargo test --doc` (55 doctests, all passing).

- Operators: BitFlipMutation, SwapMutation, RealBounds,
  GaussianMutation, BoundedGaussianMutation,
  SimulatedBinaryCrossover, PolynomialMutation, LevyMutation,
  ClampToBounds, ProjectToSimplex.
- Metrics: hypervolume_2d, hypervolume_nd, spacing.
- Pareto utilities: pareto_compare, pareto_front, best_candidate,
  non_dominated_sort, crowding_distance, das_dennis,
  ParetoArchive.

Each example is short (5-15 lines) and self-contained — copy-paste
into a fresh project and it runs.
2026-05-06 08:16:04 -06:00
swaits 214f07975a perf(non_dominated_sort): cache oriented values + inline pareto_compare
The Deb fast non-dominated sort calls `pareto_compare` twice for
every (i, j) pair, and each `pareto_compare` call invokes
`ObjectiveSpace::as_minimization` twice — so for an N-point
population that's 4·N·(N-1) fresh `Vec<f64>` allocations per sort.
At N=100 with thousands of generations across the compare harness,
this dominated the per-generation cost of every Pareto-based MOEA.

Cache `as_minimization`/feasibility/violation once per individual
up front, then inline the dominance test against those cached
arrays. The output (per-pair dominance outcome and the per-i
`dominates` lists) is bit-identical to `pareto_compare`.

gungraun (instructions):
- non_dominated_sort_2d n=50:    852 317 →   198 574 (-77 %, 4.3×)
- non_dominated_sort_2d n=200: 13 513 271 → 2 601 813 (-81 %, 5.2×)

Wall-clock (compare harness, 10-seed mean):
- NSGA-II / ZDT1:        268 →  65 ms (4.1×)
- NSGA-II / ZDT3:        267 →  65 ms (4.1×)
- NSGA-II / DTLZ2:       344 → 106 ms (3.2×)
- NSGA-II / Rastrigin:   260 →  71 ms (3.7×)
- NSGA-III / DTLZ2:      318 → 122 ms (2.6×)
- NSGA-III / DTLZ1:      303 → 122 ms (2.5×)
- SMS-EMOA / DTLZ2:     1413 → 1369 ms (small additional win on top of HV)
- AGE-MOEA / DTLZ1:      430 → 229 ms (1.9×, on top of the AGE-MOEA caching)
- HypE / DTLZ2:           80 →  44 ms (1.8×)
2026-05-05 13:28:18 -06:00
swaits f67b5c5160 fix(pareto): partition NaN-cycle orphan indices into a residual front 2026-05-05 11:28:24 -06:00
swaits 9d64a0f186 feat(pareto): add non_dominated_sort
Deb's fast non-dominated sort: returns Vec<Vec<usize>> of front indices
into the input population, with fronts[0] being the non-dominated set.
O(N²·M) is acceptable for v1 (spec §9.5).

Tests cover: small known population produces expected fronts; equal
candidates land on the same front; an empty population yields no
fronts.
2026-05-04 19:19:38 -06:00