Skip to content

Release Notes

v1.3.2 - py 0.0.16

A multi-objective quality release. Three operator bugs were fixed: polynomial mutation, simulated binary crossover, and NSGA-II crowding distance. Together with better-tuned examples and a documented recommended configuration, they take radiate from clearly trailing pymoo and DEAP on multi-objective hypervolume (DTLZ2 0.30 vs ~0.70) to on par with them: within ~0.01 of the best library on ZDT1, ZDT3, and DTLZ2, ahead of pymoo on both ZDT problems, while running roughly 7–35× faster. A new exact hypervolume indicator lets you measure front quality directly, and a new benchmarks page compares radiate against DEAP and pymoo across single- and multi-objective problems.

Changed

  • Python: plotting moved from Matplotlib to Plotly. The plot extra now installs plotly instead of matplotlib (uv add "radiate[plot]"), and MetricCollector.plot(...) renders an interactive Plotly figure. All Python examples and docs snippets were converted as well.
  • Gene no longer requires Clone. The bound moved down to NumericGene, and the GP graph types (GraphNode, GraphChromosome, GraphMutator) no longer require PartialEq on their value type. Generic code that relied on G: Gene implying Clone needs an explicit G: Gene + Clone bound.
  • BitFlipMutator is implemented for BitChromosome only (breaking). It was implemented for any ContiguousChromosome with Gene = BitGene. A custom chromosome holding BitGenes no longer gets BitFlipMutator and needs its own Mutate impl. The rate is documented as per bit: a chromosome of n bits sees n * rate flips per generation on average.
  • Multi-objective examples and docs use a tuned NSGA-II/III configuration, and a new recommended configuration section explains it:
  • Parent selection: tournament NSGA-II.
  • Survivor selection: NSGA-II for 2 objectives, NSGA-III for 3 or more.
  • Offspring fraction: 0.5.
  • Crossover: SBX, distribution index 20.
  • Mutation: polynomial, distribution index 20.

The ZDT3 example's hypervolume goes from ~0.85 to ~1.32. - random_provider::sample_indices only generates the indices it returns instead of shuffling the whole range, so sampling a few points from a long range is much cheaper (2 points from 1,000 indices: ~2,250 ns → ~33 ns). This speeds up MultiPointCrossover on long chromosomes. - PMXCrossover is much faster. It now builds each child in place by swapping genes, using position lookup tables. This is O(n) instead of the previous quadratic search, and it doesn't clone genes. Each crossover is roughly 7× faster at 20 genes, 23× at 100, and 260× at 2,000. The output is unchanged (still standard PMX), and seeded runs produce the same results as before. - Seeded runs produce different results than before. The new sampling in bernoulli_indices and sample_indices draws random numbers in a different order, so a given seed now gives a different (but statistically equivalent) run for operators that use them.

Added

  • Hypervolume indicator. pareto::hypervolume(scores, reference, objective) and Front::hypervolume(&reference) compute the exact hypervolume of a set of scores or of the Pareto front against a reference point. They handle any mix of minimized/maximized objectives and any number of objectives: an O(n log n) sweep for 2, O(n²) slicing for 3, and recursive slicing for 4+.
  • random_provider::bernoulli_indices(p, input, f) calls f for each index in input (a length n for 0..n, or a Range<usize>) selected independently with probability p, the same as a per-index bool(p) check, but at low p it jumps straight to the next selected index, so the cost scales with the number of selections rather than the range length. BitFlipMutator and BlendCrossover now use it. Rates <= 0 or NaN select nothing and rates >= 1 select everything, instead of panicking.
  • pareto::front_crowding_distance and pareto::fronts_from_ranks are now public, so you can compute per-front crowding distance and group indices by Pareto rank directly.
  • Benchmarks page in the docs comparing radiate against DEAP and pymoo on continuous, combinatorial, and multi-objective problems.

Fixed

  • PolynomialMutator returned values anchored at the lower bound instead of the current value. It computed min + q·(max − min) instead of Deb's x + δq·(max − min), so mutated genes landed near a bound or near the reflection of their current value. Higher eta made it worse, not more local. On DTLZ problems, where the optimum sits mid-range, this collapsed hypervolume (DTLZ1 went to 0). It now matches the reference NSGA-II / pymoo operator, with tests for locality, bounds, centering, and eta behavior.
  • SimulatedBinaryCrossover produced the wrong children. The child was centered on half the parents' difference instead of their midpoint, and only the first parent was updated. It now writes both children symmetrically around the parents' midpoint, per the standard SBX definition.
  • MultiPointCrossover could lose a cut point. Cut points were sampled from 0..length, and a cut at index 0 swaps nothing, so a 2-point crossover sometimes behaved as a 1-point one. Cut points are now sampled from 1..length.
  • NSGA-II crowding distance is now computed per Pareto front instead of across the whole population. This affects NSGA2Selector and TournamentNSGA2Selector. Before, a truncated front could lose its own boundary points because they weren't extreme relative to other fronts.

For example and details please refer to the user guide and API docs.

v1.3.1 - py 0.0.15

Rate is fully replaced by the expression DSL, events/checkpointing/stopping move onto the engine builder, and the Python operator API is reorganized into namespaces (Select.*/Cross.*/Mutate.*/Dist.*/Limit.*/Filter.*/Fitness.*). Also new: a population-filter stage for stagnation recovery, adaptive species-count targeting, a BitFlipMutator, f64 support for GP graphs/trees, and three new TUI dashboard tabs. Pareto front calculation's should be much faster now; buffers are cached & reused whenever possible, efficent sorting, and in-place crowding distance calculation.

Breaking

  • Rate is replaced by float or Expr. (impl Into<Expr> is now used for conversion - minimal friction expected.) Every crossover/mutator now takes a plain float or Expr instead of a Rate. Python: rd.Rate is deleted. This was implemented in order to support full dynamic rates for anything in radiate's ecosystem.
  • Events rewritten as typed pub/sub. Implement Handler<E> for one event type (EpochComplete, Improvement, EngineStart/Stop, LimitTriggered, Warning, CheckpointSaved, ...) and register with GeneticEngine::subscribe::<E>(handler). Python: new rd.on_limit_triggered/on_log/on_checkpoint_saved decorators; event.index/event.event_type are now attributes, not methods.
  • Checkpointing moved onto the builder. Use GeneticEngineBuilder::checkpoint(interval, path) instead of run()-time checkpointing. Python: Engine.write_checkpoint(path, interval, file_type="pkl") replaces run(checkpoint=...). Checkpoint pickle format also changed — checkpoints written by 1.3.0 may not load.
  • Metric-predicate stopping removed — no more Limit::Metric/Limit.metric(...). Use Limit::Expr/Limit.expr(...) instead. Expressions read directly from the MetricSet so this is functionally equivalent.
  • Custom Chromosome/Gene implementors need updates. Trait methods were re-split (bounds vs. init range, contiguous-storage methods moved to a new ContiguousChromosome sub-trait). No impact if you only use the built-in gene types.

Changed

  • GP ops/regression are generic over f32/f64 now. Calls like Op::sigmoid() may need Op::<f32>::sigmoid() if the type can't be inferred. Python GraphCodec/TreeCodec now take a dtype param.
  • Python: operators collapsed into namespaces. TournamentSelector(k=3) → Select.tournament(k=3), BlendCrossover(...) → Cross.blend(...), UniformMutator(...) → Mutate.uniform(...), HammingDistance() → Dist.hamming(), ScoreLimit(...) → Limit.score(...). Old class names are no longer exported.
  • Python: Engine.alters(...) renamed Engine.alter(...).
  • Python: Engine.run() no longer takes limits= See limits (set limits on the builder) and step_next() is gone — iterate the engine directly (for epoch in engine:).
  • Python: EngineConfig.max_species_age default changed 20 → 25 to match Rust.
  • Python: install extras regrouped. [polars]/[pandas]/[numpy]/[matplotlib] → [data] (numpy+pandas+polars) and [plot] (matplotlib); [all] unchanged.
  • Metric names renamed see default metrics. Consistency across the engine and radiate-gp (e.g. age.replace → replace.age, count.species → species.count). See docs/source/engine/metrics.md for the full mapping.
  • Python: free-threaded wheels now actually target 3.14t — CI had been building against 3.13t. This is out of radiate's control and is a result of underlying maturin/pyo3 support.

Added

  • Population filter pipeline stage — new UniqueScoreFilter detects score-diversity collapse and replaces duplicates. GeneticEngineBuilder::filter(...) / Python rd.Filter.unique_score(...) + Engine.filter(...).
  • Adaptive species-count targeting — see target species. Set target_species_count to drive the speciation threshold toward a target instead of a fixed value. Python: Engine.diversity(dist, threshold, target=...).
  • BitFlipMutator for bit-string genomes. Python: Mutate.bit_flip(rate=0.1).
  • HealthMonitor event handler — auto-emits Warning events for stagnation, diversity collapse, and species collapse.
  • f64 support end-to-end for GP graphs/trees, plus new ops: Op.weight2/sign/reciprocal/gaussian/tooth.
  • NumPy-native fitness & regression I/O — custom Python fitness functions can return numpy arrays directly; regression accepts numpy arrays or lists.
  • GraphMutator::target_size(size) — anti-bloat throttling once a graph reaches a target size.
  • Python: Graph/Tree pickle support (to_pickle/from_pickle) and an unchecked=True fast path on .eval() that skips shape validation. Graph.eval/Tree.eval now also accept numpy arrays directly.
  • Python: Expr.select(...) methods — .mean()/.stddev()/.min()/.max()/.sum()/.slope()/etc., plus Expr.alias(name).
  • TUI: three new dashboard tabs — "Improvements", "Front" (Pareto-front tracking), and "Events", plus a richer status bar and better table navigation keys.
  • New examples — Bevy-based flappy-bird (Rust), CPPN image evolution graph_art.py (Python).

Fixed

  • GraphMutator could pick duplicate source-node indices for multi-arity ops — now deduplicated.
  • Fixed a few panics from misaligned indices in graph/tree crossover.
  • AnyValue's numeric → Duration cast now treats the source f32 as seconds, not milliseconds — durations reported via metrics were previously 1000x off.
  • LARGE BUG WITH CROSSOVER PARENT SELECTION — previously, the engine could over select the same individual multiple times as a parent during crossover, leading to unexpected behavior and reduced genetic diversity. This has now been fixed to ensure a more even distribution of parent selection.

For example and details please refer to the user guide and API docs.

v1.3.0 - py 0.0.14

Radiate has reached 1.3.0! This release includes a major refactor of the engine's iteration model, a new expression DSL pass and operators, a substantial NSGA III simplification and optimization, a NEAT implementation refactor, and various other improvements and cleanups across the codebase. The engine is now much more efficient, and the new expression DSL features should make it easier to implement complex adaptive behaviors without custom code. The user guide has been rebuilt around testable snippets, and a new guide on diversity and speciation has been added.

Check the changelog for a full list of changes.

v1.2.22 - py 0.0.13

Breaking changes:

  • Changed the checkpointing feature in python to use .pkl as a default extension instead of .json. It fits better within the python ecosystem.
  • Changed the metric names to use . as a separator instead of _. This allows for better organization and grouping of metrics. For example, scores.best instead of best_scores.

Other

Speed improvements centered around engine steps.

Additions

Added a new crate radiate-expr which includes expressions (think polars) to extend the metric and rating systems. This greatly improves the flexibility of dynamic rates (mutation/crossover/species thresholds) and allows users to define their own rating systems. Along with the rate improvements, this extends into the engine itself by allowing users to define their own metrics and use them in the aforementioned dynamic rates - or simply just to track the engine.

Refactored radiate-ui to give much more insight into the engine and the metrics it produces. Included a new search bar and species panel to quickly find and visualize specific species and their members.

In python, radiate now supports optional features (check the user guide installation section for specific info). This lets users opt in to specific integrations within the python ecosystem (e.g. pandas, polars, matplotlib, torch, numpy) without needing to install a bunch of dependencies they may not need.

For checkpointing, new traits were added: CheckpointWriter & CheckpointReader to let users define their own ways of saving checkpoints.

Full Changelog


v1.2.21 - py 0.0.11

Breaking changes:

Changed the FloatGene<T> to take a generic parameter to take either an f32 or f64 value.

  • The above is a breaking change. Just add the generic type to your FloatGene, FloatChromosome, or FloatCodec if needed.

Massive expansion of python's api - check the docs for usage. We moved towards a builder pattern for the engine and added better type hinting.

Full Changelog


v1.2.20 - py 0.0.10

Adding radiate-ui crate, bug fixes, & speed improvements.

I split up some functionality into a new crate radiate-utils and have added a new feature radiate-ui for a tui user interface through ratitui. Some small bug fixes, code simplifications, and some nice little speed improvements.

Full Changelog


v1.2.19 - py 0.0.9

Adding support for experimental PGM or Probabilistic graphical models through the GP feature (crate).

Note

PGM support was experimental and has since been removed; it is no longer part of Radiate.

Major cleanup or unused code and massive graph performance improvements through the use of smallvec as connections instead of BTreeSets.

Improving eventing system through cleaner code and removing redundant events.

Introducing radiate-error (RadiateError) into the core crates ad requiring its usage in certain traits (Problem mainly). We also use this error type in py-radiate and allow it to bubble up into python's type system too.

Brining metrics to the forefront in python.

Full Changelog


v1.2.18 - py 0.0.8

Fixing subtle bug in recurrent graphs where a random seed wasn't being respected, leading to non-deterministic behavior in some cases. This fix ensures that all random operations within recurrent graphs are consistent and reproducible when a seed is provided.

Added three new types of graphs:

  • LSTM (Long Short-Term Memory) Graphs: These are a type of recurrent neural network (RNN) that can learn long-term dependencies.
  • GRU (Gated Recurrent Unit) Graphs: Similar to LSTMs, GRUs are a type of RNN that are simpler and often more efficient.
  • Mesh Graphs: Graphs structured in a mesh topology.

v1.2.17 - py 0.0.7

In response to github issue #23.

Ensuring that FloatGenes/IntGene's respect their bounds during mutation and crossover. This was a bug where mutated or crossovered genes could exceed their defined bounds, which could lead to invalid individuals in the population. This fix ensures that all FloatGenes/IntGene's remain within their specified bounds after any genetic operation. Also some optimizations and code cleanup for py-radiate. Large additions to tests.

Also adding new mutator: JitterMutator for FloatGenes. This mutator adds a small random value (jitter) to each gene, controlled by a magnitude parameter.


v1.2.16

In response to github issue #22.

Adding support for batch fitness functions and batch engine problems through a new trait (BatchFitnessFn). Some small cleanup on other fitness functions and some chromosome operators.


v1.2.15 - py 0.0.6

Adding Novelty Search to python and refactoring engine building across the rust/python bridge. Improving python's speed. Adding type checking to python and upgrading python package to >= python 3.12 to support new python generics. Improving docs to reference new functionality.

New alters:

  • EdgeRecombinationCrossover for PermutationGenes
  • PolynomialMutator for chromosomes with FloatGenes

Added code path in alters for dynamic mutation/crossover rates. This is in early dev, but an be seen in PolynomialMutator.


v1.2.14 - py 0.0.4

Added support for novelty search, fitness-based novelty, and combined novelty and fitness search. Improved documentation and examples. Improved traits for Engine and introduced one for FitnessFn. Bug fixes for pareto fronts and engine iterators.