symbios-genetics
A battle-hardened evolutionary computation engine for Rust.
symbios-genetics is a trait-based library designed for Morphogenetic Engineering, Artificial Life, and Creative AI. Unlike general-purpose genetic libraries, it prioritizes correctness, reproducibility, and serialization above all else.
Algorithms
The library implements three distinct evolutionary strategies covering the spectrum of optimization needs:
| Algorithm | Type | Best Use Case |
|---|---|---|
| SimpleGA | Single-Objective | Converging on a specific optimal solution (e.g., maximizing speed). Features Elitism and Tournament Selection. |
| NSGA-II | Multi-Objective | Finding the Pareto Front of trade-offs between conflicting goals (e.g., maximize strength AND minimize weight). |
| MAP-Elites | Quality-Diversity | Illuminating the search space. Finds the best solution for every possible niche (e.g., "fastest robot for every possible height"). |
| CVT-MAP-Elites | Quality-Diversity | MAP-Elites for high-dimensional or non-uniform descriptor spaces. Decouples archive size from descriptor dimensionality via a Voronoi tessellation. |
| Novelty Search | Open-ended exploration | Replaces fitness with kNN behavioural distance. Escapes deceptive local optima where pure fitness search gets stuck. Configurable novelty/fitness blend. |
Quick Start
Add to your Cargo.toml:
[]
= "0.2"
= { = "1.0", = ["derive"] }
= "0.9"
Defining a Genome
Implement the Genotype trait for your data structure.
use Rng;
use ;
use Genotype;
Defining an Evaluator
Implement Evaluator to bridge your genome to the engine. Returns a tuple of (Fitness, Objectives, Descriptor).
use Evaluator;
;
Running Evolution
use ;
Quality-Diversity Metrics & Export
MapElites exposes the standard QD metrics and a CSV export of the archive:
use File;
let coverage = engine.coverage; // fraction of cells occupied (0.0..=1.0)
let qd_score = engine.qd_score; // sum of fitness across cells
// CSV export requires the `export` feature.
let mut out = create?;
engine.export_csv?;
Enable export with:
= { = "0.2", = ["export"] }
The CSV has columns key,descriptor,fitness,objectives,genotype_hash — one row per occupied cell, in deterministic iteration order. The genotype hash is a 16-hex-char seahash of the bincode-serialised genotype, suitable for joining against a separate genotype dump.
Note:
qd_scoreis the raw sum of fitness. If your fitness can be negative (e.g.-distance), shift it to non-negative before relying on the score for cross-run comparison.
Pre-made Scorers
The scorers module ships composable building blocks for locomotion / robotics fitness:
- Concrete scorers over a [
Trajectory] struct:Displacement,UpAlignment,Height,EnergyEfficiency— andConstfor literals. - Combinators:
Multiply,Sum,Penalize,Normalizefor assembling multi-term objectives. CompositeEvaluatorto bridge a scorer composition into theEvaluator<G>trait used by every algorithm. The simulator function runs once per genotype; the result is fanned out to fitness,objectives(NSGA-II), anddescriptor(MAP-Elites) signals.
use ;
// locomotion = displacement * up_alignment * (height + 0.5)
let locomotion = Multiply;
let evaluator = new
.with_objectives
.with_descriptors;
Behavioural diversity is intentionally not provided as a scorer — it's a population-level signal. Use NoveltySearch for that.
Speciation
The speciation module is a standalone NEAT-style speciation primitive that operates on a &mut [Phenotype<G>]. You supply a CompatibilityDistance<G> metric; Speciation clusters the population by that distance, applies Stanley & Miikkulainen explicit fitness sharing (each phenotype's fitness is divided by its species size), and adapts the compatibility threshold each generation toward a configured target species count.
It is genotype-agnostic and not coupled to a specific [Evolver] — drive it from your own selection loop, or against a population you maintain alongside an engine.
use ;
;
let mut spec = new;
let mut pop: = /* evaluated population you own */ vec!;
// Each generation, after evaluation:
spec.assign;
spec.share_fitness;
spec.adjust_threshold;
Species IDs are stable across generations as long as a representative survives, making it straightforward to track lineage. See the module docs for the algorithm sketch and tuning knobs (with_threshold_step, with_min_threshold).
Architecture
The Evolver Trait
All algorithms implement the Evolver<G> trait. This allows you to write simulation harnesses (e.g., a Bevy plugin) that are agnostic to the specific evolutionary strategy. You can hot-swap SimpleGA for MapElites without rewriting your game loop.
Parallelism
To enable parallel evaluation, ensure the parallel feature is enabled (default) and your Evaluator implements Send + Sync. The engine automatically dispatches evaluation tasks via rayon::par_iter.
= { = "0.2", = ["parallel"] }
License
This project is licensed under the MIT License - see the LICENSE file for details.