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.
This is a major breaking release, see Changed:
- Add formal
Allele
trait:Allele: Clone + Send + Sync + PartialEq + Debug
- Change associated type from
Genotype
toAllele
for:Fitness
,EvolveReporter
,HillClimbReporter
andPermutateReporter
- Change generic type
Genotype
toAllele
for:Chromosome
,Population
and other structs/functions using these types - Rename
DiscreteGenotype
toListGenotype
(incl. Multi) - Rename
ContinuousGenotype
toRangeGenotype
(incl Multi) - Generalize
RangeGenotype
for numeric types (incl. Multi, default still f32, but other float and integer types are now supported) - Replace
Range
withRangeInclusive
for all ranges inRangeGenotype
in order to handle integer ranges more intuitively (incl. Multi) - Change
Fitness
placeholdersSumContinuousAllele
andSumDiscreteAllele
to generalizedSumGenes
(with optional precision) - Reimplement scaling completely now
RangeGenotype
is generalized.- Drop f32
Scaling
logic - Set
allele_mutation_scaled_range
onRangeGenotype
to define scaling (incl. Multi) instead ofwith_scaling()
inHillClimb
build step - Mutation distance only on edges of current scale (e.g. -1 and +1 for -1..-1 scale)
- Scale down after
max_stale_generations
is reached and reset newstale_generations
counter to zero - Only trigger
max_stale_generations
ending condition when already reached the smallest scale
- Drop f32
- How to mutate now fully controlled by
Genotype
with random, relative or scaled mutations options (relative and scaled only possible for RangeGenotype, incl. Multi)- Rename
MutateSingleGeneRandom
toMutateSingleGene
as it just callsmutate_chromosome()
onGenotype
- Rename
MutateSingleGeneRandomDynamic
toMutateSingleGeneDynamic
as it just callsmutate_chromosome()
onGenotype
- Rename
MutateMultiGeneRandom
toMutateMultiGene
as it just callsmutate_chromosome()
onGenotype
- Rename
MutateMultiGeneRandomDynamic
toMutateMultiGeneDynamic
as it just callsmutate_chromosome()
onGenotype
- Rename
allele_neighbour_range
toallele_mutation_range
inRangeGenoype
(incl. Multi) to define relative mutation - Add
allele_mutation_scaled_range
toRangeGenotype
(incl. Multi) to define scaled mutation
- Rename
- All changes to
RangeGenotype
are reflected inMultiRangeGenotype
as well
- Allow relative mutations for
Evolve
as well, as it is aGenotype
responsibility now - Allow scaled mutations for
Evolve
as well, as it is aGenotype
responsibility now- Scale down after
max_stale_generations
is reached and resetstale_generations
to zero - Only trigger
max_stale_generations
ending condition when already reached the smallest scale
- Scale down after
- Add
replace_on_equal_fitness
to builders to allow for lateral moves in search spaceEvolve
: defaults to false, maybe useful to avoid repeatedly seeding with the same best chromosomes after mass extinction eventsHillClimb
: defaults to true, crucial for some type of problems with discrete fitness steps like nqueensPermutate
: defaults to false, makes no sense to use in this strategy
- Drop
MutateSingleGeneDistance
as random, relative or scaled mutations are now handled byGenotype
and not the caller
- Improve bootstrapped reporter outputs
EvolveReporterSimple
,HillClimbReporterSimple
andPermutateReporterSimple
- Implement
ExtensionNoop
default forEvolveBuilder
and remove now optionalwith_extension()
steps in examples - Implement
EvolveReporterNoop
default forEvolveBuilder
and remove now optionalwith_reporter()
steps in examples - Implement
HillClimbReporterNoop
default forHillClimbBuilder
and remove now optionalwith_reporter()
steps in examples - Implement
PermutateReporterNoop
default forPermutateBuilder
and remove now optionalwith_reporter()
steps in examples
- Align
EvolveReporter
,HillClimbReporter
andPermutateReporter
traits to takeEvolveState
andEvolveConfig
as parameters in further aligment withMutate
,Compete
,Crossover
andExtension
traits. - Add
Sync
trait everywhere whereSend
trait was required.
- Fix major issue where cardinality starts out as 0 as there are no fitness
calculations yet. This triggers the optional extension event, if set, at the
start of the evolve loop (killing seed population and diversity). Issue was
introduced in v0.8.0 with
fitness_score_cardinality()
. Solve by adding None fitness counts to cardinality.
- Always implement
new()
next todefault()
. Usenew()
in public API examples
- Rename
new()
tonew_with_flags()
for more verbose reporting inEvolveReporterSimple
,HillClimbReporterSimple
andPermutateReporterSimple
- Add simpler
new()
to only takeperiod: usize
and set all flags to false (as this is the sensible less noisy default) inEvolveReporterSimple
,HillClimbReporterSimple
andPermutateReporterSimple
- Add
PermutateConfig
andPermutateState
to align structure withEvolve
andHillClimb
- Extract
StrategyConfig
trait and use forEvolveConfig
,HillClimbConfig
andPermutateConfig
- Extract
StrategyState
trait and use forEvolveState
,HillClimbState
andPermutateState
- Add pluggable
EvolveReporter
toEvolve
strategy- Set in builder using
with_reporter()
- Custom implementations by client are encouraged, the API resembles the Fitness API
- Add bootstrap implementations
EvolveReporterNoop
,EvolveReporterSimple
andEvolveReporterLog
- Set in builder using
- Add pluggable
HillClimbReporter
toHillClimb
strategy- Set in builder using
with_reporter()
- Custom implementations by client are encouraged, the API resembles the Fitness API
- Add bootstrap implementations
HillClimbReporterNoop
,HillClimbReporterSimple
andHillClimbReporterLog
- Set in builder using
- Add pluggable
PermutateReporter
toPermutate
strategy- Set in builder using
with_reporter()
- Custom implementations by client are encouraged, the API resembles the Fitness API
- Add bootstrap implementations
PermutateReporterNoop
,PermutateReporterSimple
andPermutateReporterLog
- Set in builder using
- Add
fitness_score_cardinality()
toPopulation
- Add
MutateMultiGeneRandomDynamic
(generalize to any number of mutations) - Add
MutateSingleGeneDistance
(only forContinuousGenotype
)
- Drop
fitness_score_uniformity()
andfitness_score_prevalence()
fromPopulation
- Drop
MutateDynamicRounds
- Align
Mutate
,Compete
,Crossover
andExtension
traits to takeEvolveState
,EvolveConfig
,EvolveReporter
as parameters - Reimplement
MutateOnce
asMutateSingleGeneRandom
- Reimplement
MutateTwice
asMutateMultiGeneRandom
(generalize to any number of mutations) - Reimplement
MutateDynamicOnce
asMutateSingleGeneRandomDynamic
(also fix InvalidProbabilty issue) - Replace
target_uniformity
withtarget_cardinality
inMutateSingleGeneRandomDynamic
andMutateMultiGeneRandomDynamic
as uniformity is ill defined - Replace
uniformity_threshold
withcardinality_threshold
inExtension
implementations, as uniformity is ill defined - Move permutation
total_population_size
fromPermutateConfig
toPermutateState
, so progress can be reported on inPermutateReporterSimple
- Move
env_logger
dependency to dev-dependencies as this crate is a library, not an executable
- Note that
HillClimb
scaling needs review as it doesn't feel right in its design approach. Possibly align withMutateSingleGeneDistance
approach? - Extract
StrategyReporter
trait, but don't use because of error E0658: associated type defaults are unstable. So forEvolveReporter
,HillClimbReporter
andPermutateReporter
the trait is shadowed as if it is implemented
- Add
Wrapper
s instead ofDispatcher
s as they keep state, behaviour is the same usinginto()
(e.g.MutateOnce::new(0.2).into()
)
- Extract Meta logic to separate crate genetic_algorithm_meta
- Phase out the
Dispatcher
s as they are replaced byWrapper
s
- MSRV bumped to 1.71.1
- Solve RUSTSEC-2021-0145
- Add
Mutate
implementations:MutateTwice
, support some form of swap-like behaviour whereUniqueGenotype
doesn't match with the problem spaceMutateDynamicOnce
, increase mutation probability when population uniformity is above threshold and vice versaMutateDynamicRounds
, increase mutation rounds when population uniformity is above threshold and vice versa
- Add
HillClimbVariant::StochasticSecondary
andHillClimbVariant::SteepestAscentSecondary
as well for the same reasons asMutateTwice
- Add
call_speciated
next to the existingcall_repeatedly
inEvolveBuilder
. This runs multiple independent evolve strategies and then competes their best chromosomes as starting population against each other in one final evolve strategy - Add
Chromosome
age and optionalwith_max_chromosome_age
toEvolveBuilder
. Filtering chromosomes past the maximum age from the next generation - Add
best_generation()
andbest_fitness_score()
toStrategy
, so client implementation can report and switch more easily over different strategies. Return zero forPermutate::best_generation()
as there is no concept of best generation there - Add
Extension
step toEvolve
, addingwith_extension
toEvolveBuilder
, with several implementations:ExtensionNoop
, for no extensionExtensionMassExtinction
, trigger mass extinction to allow for cambrian explosion (no competition for a while, which allows for more diversity)ExtensionMassGenesis
, likeExtensionMassExtinction
, but only a pair of best chromosomes (adam and eve) are taken as the start for the next generationsExtensionMassInvasion
, likeExtensionMassExtinction
, but replace extinct population with random population (respecting seed_genes if present)ExtensionMassDegeneration
, simulate cambrian explosion by apply several rounds of uncontrolled mutation directly
- Add
Population::fitness_score_unformity()
as measure for uniformity (fraction between 0 and 1). Use as triggers forMutateDynamic*
andExtension
- Add dispatch
From
toEvolve
plugins for use inMetaConfigBuilder
, instead of manual wrapping (e.g.MutateOnce::new(0.2).into()
instead ofMutateDispatch(MutateOnce::new(0.2)
)
- Refactor
Compete
,Crossover
andMutate
from tuple structs to struct, initialize with::new()
, because the structs now have some mutable internal properties (e.g.MutateDynamicOnce
). Make all plugins mutable for consistency - Split off internal config and state structs for
Evolve
andHillClimb
, leavePermutate
untouched weighing overkill v. symmetry different there - Split off internal plugins for
Evolve
(i.e.Mutate
/Crossover
/Compete
/Extension
) - Change
seed_genes
toseed_genes_list
to allow for multiple seed genes taken randomly (used incall_speciated
) - Only mutate children in the
Mutate
step, in earlier versions parents and children were mutated equally - Refactor
Evolve
population_size
property totarget_population_size
, thus also replacingwith_population_size
withwith_target_population_size
- Add
env_logger::init()
to all examples, so theRUST_LOG
environment variable works as expected - Change
HillClimbBuilder::with_scaling
parameter from tuple to structScaling
- Phase out the
with_mass_degeneration
inEvolveBuilder
as it is replaced byExtensionMassDegeneration
- Calculate initial chromosome fitness in
HillClimb
to lock in on original seed if present
- Remove
random_chromosome_probability
toHillClimb
as it was hackish
- Add
valid_fitness_score
to block ending conditions until met forEvolve
andHillClimb
strategies
- Tweak TRACE logging
- Add env_logger and some INFO/DEBUG/TRACE logging
- Count generation zero based
- Solve lock-in to single best chromosome in stale
HillClimbVariant::SteepestAscent
by shuffling chromosomes before taking best
- Add
IncrementalGenotype
Trait with neighbouring chromosome implementations - Implement
IncrementalGenotype
for allGenotype
s - Add
allele_neighbour_range
toContinuousGenotype
- Add
allele_neighbour_ranges
forMultiContinuousGenotype
- Add
HillClimbVariant::Stochastic
andHillClimbVariant::SteepestAscent
- Add
HillClimb
scaling (forContinuousGenotype
&MultiContinuousGenotype
) to scale down neighbours on each round and use as ending condition - Add
random_chromosome_probability
toHillClimb
to avoid local optima - Add multithreading to
Permutate
(parallel processing of chromosome generator) - Add multithreading to
Evolve
(fitness execution for population) - Add multithreading to
HillClimb
(fitness execution forHillClimbVariant::SteepestAscent
population only) - Add
call_repeatedly
forEvolveBuilder
andHillClimbBuilder
- Add examples/evolve_milp.rs
- Add examples/evolve_scrabble.rs
- Add examples/hill_climb_scrabble.rs
- Add examples/hill_climb_milp.rs
- Add examples/permutate_scrabble.rs
- Require
IncrementalGenotype
forHillClimb
strategy - Refactor
allele_values
toallele_list
- Refactor
allele_multi_values
toallele_lists
- Refactor
allele_multi_range
toallele_ranges
- Add median/mean/stddev to
report_round
inEvolve
andHillClimb
- Add precision to
SumContinuousGenotype
andSumMultiContinuousGenotype
placeholders for better handling of decimal changes on cast to isize
- Use SPDX license in Cargo.toml as the existing LICENSE file (MIT) was marked as non-standard by crates.io
- Add Apache 2.0 license
- Note degeneration_range use as case by case configuration
- Add
Strategy
trait and implement forEvolve
andPermutate
- Add
HillClimb
strategy for when crossover is impossible or inefficient - Add
MultiUniqueGenotype
- Add table_seating example (hill_climb and evolve)
- Move
Evolve
&Permutate
tostrategy
module - Remove
Genotype::is_unique
andCrossover::allow_unique_genotype
methods- Replace with
Genotype::crossover_indexes
andCrossover::require_crossover_indexes
- Replace with
Genotype::crossover_points
andCrossover::require_crossover_points
- Replace with
- Rename
UniqueDiscreteGenotype
toUniqueGenotype
as it is discrete by definition - Rename
PermutableGenotype::allele_values
toPermutableGenotype::allele_values_for_chromosome_permutations
for clarity of purpose - Hide
Evolve
andPermutate
internal fields (to align withStrategy
trait)
- forgot to update version in
Cargo.toml
- Make proper distinction between gene and allele as in the book "Genetic Algorithms in Elixir"
- Add option to
call()
fromEvolveBuilder
&PermutateBuilder
directly - Add
Fitness::Zero
placeholder
- Refactor
Evolve
&Permutate
tocall(&mut self, ...)
- Refactor
Fitness
,Crossover
,Mutate
&Compete
to take mutable population reference - Improve performance in
Crossover
when not keeping parents - Rename
gene_value*
toallele_value*
- Rename
gene_ranges
toallele_multi_range
for symmetry reasons withallele_multi_values
- Rename
gene_size
togenes_size
as it is not the size of a gene - Rename
CrossoverSingle
toCrossoverSingleGene
- Rename
CrossoverRange
toCrossoverSinglePoint
- Rename
CrossoverAll
toCrossoverUniform
- Drop SetGenotype as it is always better implemented using BinaryGenotype
- Cleanup examples
- Add
SetGenotype<T>
, withexamples/evolve_knapsack_set.rs
andexamples/permutate_knapsack_set.rs
- Refactor fitness placeholders to placeholders module to emphasize that production use is not intended
- Rename
Fitness::call_for_chromosome()
toFitness::calculate_for_chromosome()
- Replaced
PermutableGenotype::population_factory()
withPermutableGenotype::chromosome_permutations_into_iter()
- Use
PermutableGenotype::chromosome_permutations_into_iter()
inPermutate::call()
instead of fully instantiated population - Rename
PermutableGenotype::population_factory_size()
toPermutableGenotype::chromosome_permutations_size()
- Use
num::BigUint
forPermutableGenotype::chromosome_permutations_size()
as it overflows easily - Rename existing
examples/evove_knapsack_set.rs
toexamples/evolve_knapsack_discrete.rs
to note is usesDiscreteGenotype<T>
- Improve rustdocs, refer to docs.rs documentation from general README.md
- Added rustdocs, refer to crate.io documentation from general README.md
Initial version