All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Package metadata now includes
documentation(docs.rs/fugue-evo) andhomepage(evo.fugue.run), so crates.io shows the documentation link. - The island model (
algorithms::island) is now available without theparallelfeature: islands evolve sequentially when rayon is absent (e.g. wasm32 builds), with identical seeded results thanks to the per-island RNGs (EV-12). fugue-evo-wasmgains incremental explorable engines (ExploreGa,ExploreCma,ExploreNsga2,ExploreIsland,ExploreUmda, plusexplore_landscape_grid/explore_landscape_info): seeded, generation-by-generationstep()APIs streaming population, fitness, CMA-ES covariance/eigenstructure, Pareto ranks, and migration events as JSON — the compute layer behind the interactive docs at evo.fugue.run.- Interactive explorable documentation (evo.fugue.run): the fugue-viz foundation (navy theme, seeded canvas widgets, lazy init) with evo-specific explorables — a live CMA-ES covariance ellipse, NSGA-II Pareto front formation, island-model migration, GA operator anatomy, UMDA model contraction — plus a WASM playground page running the real crate in the browser.
Remediation of the full 2026-07 audit (AUDIT-2026-07.md, findings EV-01
through EV-106: correctness, math, completeness, usability, elegance, and
docs issues across CMA-ES, hyperparameter learning, interactive/Bradley-Terry
ranking, genome traces, population/operators, algorithms, Fugue integration,
checkpointing, the WASM bindings, and package metadata/dependencies).
Individual::set_fitnessnow panics on a NaN fitness value, andPopulation::best/worst/sort_by_fitnesstreat NaN as strictly worst, so a NaN-fitness individual can no longer be silently reported as the best/worst (EV-07).Population::best/worst/sort_by_fitnessnow rank viaFitnessValue::is_better_than(newcmp_by_qualitytotal order) instead of ato_f64()scalar, returning the correct result forParetoFitnesswith infinite crowding distances (EV-08).- NSGA-II now recomputes crowding distance per non-dominated front (Deb 2002) rather than over the whole mixed-rank population, correcting binary-tournament parent-selection diversity pressure and the reported
crowding_distance(EV-13). Individual::genome_mutnow clears the cached fitness, and a newIndividual::set_genomedoes the same, so a mutated genome is always re-evaluated (EV-28).- NSGA-II binary tournament now draws two distinct competitors (sampling without replacement) (EV-84).
- SubtreeMutation (GP) no longer violates a genome's max_depth; the replacement subtree is generated within the depth budget max_depth - depth(mutation point), preventing bloat-control overruns (EV-27).
- Bounded Simulated Binary Crossover now uses Deb & Agrawal's bounds-aware spread factor so offspring fall inside [min,max] by construction, eliminating the probability mass previously piled onto the bounds by clamping (EV-71).
- SwapMutation, PermutationSwapMutation, and InsertMutation
Default::default()now perform one operation instead of being a silent no-op (EV-101). - Composite genome trace round-trip now delegates to each component's own to_trace/from_trace under a 'first/'/'second/' namespace, fixing silent data loss for Permutation and Tree components (EV-03).
- TreeGenome trace encode/decode is now lossless: function nodes serialize as their index in the stable ArithmeticFunction::functions() ordering and terminals as a (discriminant, payload) pair, so from_trace(to_trace(g)) reproduces g exactly (EV-04).
- DynamicRealVector::generate no longer panics on empty bounds; added try_generate -> Result for the degenerate case (EV-58).
- from_trace on RealVector/BitString/Permutation now returns GenomeError::TypeMismatch for a present-but-wrong-typed choice instead of silently truncating, distinguishing it from a genuinely missing address (EV-59).
- Deep GP trees: eval/depth/size are now iterative (explicit stack) and an iterative teardown (TreeGenome::dismantle / drop_node_iteratively) is provided so pathologically deep trees no longer overflow the stack (EV-60).
- DynamicRealVector trace I/O now derives its gene address from trace_prefix(), so the advertised and actual prefixes can no longer diverge (EV-91).
- Interactive/Bradley-Terry: the MLE is now re-fit inside the live pairwise loop (via process_pairwise), so pairwise user feedback actually orders candidates (EV-06).
- Interactive/Bradley-Terry: Newton-Raphson uses a Gaussian log-strength prior and MM a Gamma pseudo-count prior, keeping all-win/all-loss candidates finite; the
regularizationfield is renamedprior_lambda(default 0.1, serde alias retained) (EV-67). - Interactive/Bradley-Terry: uncertainty is reported on the strength scale for both optimizers via a delta-method, sum-to-zero-constrained Fisher pseudo-inverse (the ridge-inflation bug is gone) (EV-25, EV-66).
- Interactive/Bradley-Terry: the backtracking line search now enforces the correct Armijo sufficient-increase condition (EV-65).
- Interactive/Aggregation: Elo and ImplicitRanking uncertainties are now on the same scale as their means (Elo gains a steady-state floor; ImplicitRanking uses the score-scale binomial variance) (EV-98).
- Interactive/Selection: CoverageAware pairing never returns a self-pair; exploration/coverage bonuses are normalized to a model-agnostic scale by mean population variance, and zero-variance (already-known) pairs score ~0 instead of the max-uncertainty sentinel (EV-68, EV-69, EV-70).
- License metadata reconciled:
fugue-evoandfugue-evo-wasmnow declare a singlelicense = "MIT"(matchingfugue-ppl'slicense = "MIT"), and a rootLICENSEfile (MIT text, copyright Alex Nodeland 2025-2026) is now shipped; README/crate docs no longer claim a dual MIT-OR-Apache-2.0 license with no accompanying license texts (EV-29). fugue-pplnow resolves to the co-developed sibling crate viafugue-ppl = { path = "../fugue", version = "0.1.0" }instead of the publishedfugue-ppl = "0.1.0"crates.io release, sofugue-evo's Fugue integration is finally built and tested against the actual co-developed../fuguesource rather than a registry release the two crates were never exercised against together — the gap this finding was originally about. This became safe oncefugue's own 2026-07 audit remediation landed with a green full-test gate; adapting to that post-remediation API required migratinggenome::composite's trace namespacing to the newAddressstruct (Address::new(..)/addr.as_str()in place of the former tuple-structAddress(..)constructor and.0field), with no behavior change. Theversion = "0.1.0"field is retained so the dependency still resolves from crates.io if the sibling checkout is absent, and the README "Development" section documents how to pin back to the published release (EV-30).crates/fugue-evo-wasm/Cargo.toml's[profile.release](opt-level "s", LTO) has moved to the workspace-rootCargo.toml, where Cargo actually honors it; the member manifest previously declared it in a location Cargo silently ignores, leaving the WASM release build unshrunk and non-LTO (EV-31).- Checkpoint resume is now a first-class library API (EV-02):
SimpleGA::checkpoint_runsnapshots an in-progress incremental run — population, best, evaluations, statistics, and a capturedSnapshotRng(ChaCha family) — into aCheckpoint, andSimpleGA::resume/SimpleGA::run_from_checkpointrestore it (RNG included) so a resumed run is bit-identical to an uninterrupted one, instead of forcing users to re-implement the generation loop.resumerejects a checkpoint with no captured RNG rather than silently diverging. The incremental stepping API (SimpleGaRun+init_run/step_generation/finish_run) is now available in all builds (previouslyparallel-gated), andexamples/checkpointing.rswas rewritten to drive the resume purely through this API. - Every remaining WASM optimizer now exposes a per-generation progress/cancel callback (EV-34), extending the incremental support beyond the RealVector
SteppedRealOptimizer:BitStringOptimizer,PermutationOptimizer,Nsga2Optimizer, andSymbolicRegressionOptimizergainoptimizeWithProgress/optimizeCustomWithProgressmethods (driven throughSimpleGA::init_run/step_generationandNsga2::step), andEvolutionStrategyOptimizer/UmdaOptimizergainoptimizeWithProgressbacked by new nativeEvolutionStrategy::run_with_callbackandUMDA::run_with_callbackhooks. The callback receives(generation, bestFitness)(NSGA-II reports the Pareto-front size) and returningfalsecancels the run, so a Web Worker canpostMessageprogress or honor a cancel button instead of blocking on one opaque call. - Reworded the misleading "we negate because fugue-evo maximizes" comment in
examples/sphere_optimization.rs(the built-inSpherefitness already negates internally; no user negation is needed) and fixed the printed "Best fitness" to report the un-negated sum-of-squares objective so it reads as the expected near-zero, non-negative value at the optimum (EV-78).
- The closure
MultiObjectiveFitnessblanket impl (hardcoded 2 objectives) is replaced byClosureMultiObjective::new(num_objectives, closure), which reports the true objective count (EV-85). - SbxCrossover now exposes two separate probabilities — per-pair
crossover_probability(default 0.9) and per-geneexchange_probability(default 0.5, canonical) — via distinct fields and builders (with_probability,with_exchange_probability) (EV-72). - Unbounded PolynomialMutation now applies a local Gaussian perturbation (sigma default 0.1*(1+|x|), configurable via
with_unbounded_sigma) instead of fabricating +/-1e10 bounds (EV-102). - MutationOperator::mutation_probability now returns Option, reporting None for the length-dependent 1/n default instead of an untruthful 1.0 (EV-103) (breaking).
- TournamentSelection samples with replacement by default (canonical selection pressure; no longer deterministic when tournament_size >= population size); use
TournamentSelection::without_replacementfor the distinct-competitor variant (EV-104). - Added length-aware variation operators for DynamicRealVector (cut_and_splice crossover and DynamicGaussianMutation) in the new genome::dynamic_ops module (EV-57).
- Documented the MultiBounds-as-length/depth convention on EvolutionaryGenome::generate and added honest per-type constructors: BitString/Permutation/DynamicRealVector::generate_with_len and TreeGenome::generate_with_depth (EV-94).
- Bounds gained a fallible try_new constructor (rejects min > max); normalize()/denormalize() now handle degenerate min==max bounds (0.5 / min) instead of producing NaN via divide-by-zero (EV-56).
- README/SPEC updated to precisely describe post-remediation behavior: the Bayesian hyperparameter learner is a wired, opt-in
ThompsonSamplingTuner(SimpleGABuilder::adaptive_operators+run_adaptive); the Fugue integration runs a genuine tempered-SMC/Boltzmann pipeline with a flagshipexamples/bayesian_evolution.rs; and checkpointing supports bit-identical resume for the ChaCha RNG family (EV-29 through EV-78 doc sweep). - The duplicate
randmajor in dev/test builds is eliminated by pinningproptest = ">=1.5, <1.7". proptest migrated its internal RNG stack to rand 0.9 in 1.7.0; the 1.5.x/1.6.x line still uses rand 0.8, so pinning below 1.7 collapses the graph back to a single rand major (0.8.5). Verified empirically (cargo update -p proptest --precise 1.6.0drops rand 0.9.2/rand_chacha 0.9.0/rand_core 0.9.3, after whichcargo tree -dshows one rand major andcargo check --all-targets+ the property-test suite pass). Amake deps-checktarget (cargo tree -dguard, wired intomake ci) now fails the build if a duplicate rand major reappears (EV-74).
EvolutionaryGenome::distanceis now a required method (no silent 0.0 default) and panics on structural mismatch; a new requiredtry_distance -> Resultprovides the fallible path. RealVector/BitString/Permutation distance no longer silently truncate or report 0.0 on length mismatch (EV-19, EV-20, EV-55, EV-93).Permutation::new_uncheckedrenamed tofrom_vec_unchecked, with documented invariants and a debug-build validity assertion (EV-92).
-
Core Genetic Algorithm Framework
SimpleGAbuilder pattern for easy algorithm configuration- Generational evolution with configurable operators
- Elitism support for preserving best individuals
-
Genome Types
RealVectorfor continuous optimizationBitStringfor binary/combinatorial problemsPermutationfor ordering problems (TSP, scheduling)TreeGenomefor genetic programming- Unified
EvolutionaryGenometrait abstraction
-
Selection Operators
TournamentSelectionwith configurable tournament sizeRouletteWheelSelection(fitness-proportionate)TruncationSelectionfor steady-state evolutionRankSelectionfor rank-based selectionBoltzmannSelectionwith temperature parameter
-
Crossover Operators
SbxCrossover(Simulated Binary Crossover) for real-valued genomesUniformCrossoverfor bit stringsSinglePointCrossoverandTwoPointCrossoverOrderCrossover(OX) for permutationsSubtreeCrossoverfor tree genomes
-
Mutation Operators
PolynomialMutationfor real-valued genomesGaussianMutationwith adaptive step sizesBitFlipMutationfor bit stringsSwapMutationandInsertMutationfor permutationsPointMutationandSubtreeMutationfor trees
-
Advanced Algorithms
CmaEs(Covariance Matrix Adaptation Evolution Strategy)NSGA2for multi-objective optimization with Pareto frontsIslandModelfor parallel evolution with migration
-
Fugue PPL Integration
to_trace()andfrom_trace()for probabilistic programming interop- Trace-based evolutionary operators
- Bayesian hyperparameter learning with
BetaPosterior
-
Production Features
- Checkpointing with
CheckpointManager(JSON, Binary, Compressed) - Convergence detection with configurable criteria
- Evolution statistics tracking
- Termination conditions (max generations, target fitness, stagnation)
- Checkpointing with
-
Benchmark Functions
Sphere,Rastrigin,Rosenbrock,Ackley,GriewankOneMax,LeadingOnesfor bit stringsSymbolicRegressionfor GP benchmarks
-
Examples
sphere_optimization.rs- Basic continuous optimizationrastrigin_benchmark.rs- Multimodal function optimizationcma_es_example.rs- CMA-ES for Rosenbrockisland_model.rs- Parallel island modelcheckpointing.rs- Save/restore evolution statesymbolic_regression.rs- Genetic programminghyperparameter_learning.rs- Bayesian adaptation
-
Testing
- Comprehensive unit tests (370+ tests)
- Property-based tests with proptest (21 tests)