# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.17.1] - 2024-10-10 ### Changed * Make `MutateMultiGene` and `MutateMultiGeneDynamic` mutate up to the provided `number_of_mutations` (instead of always the exact amount of mutations) ## [0.17.0] - 2024-10-04 ### Added * Add `on_exit()` event to `StrategyReporter` * Add `StrategyVariant` info in `on_enter()` and `on_exit()` events * Add `fitness_duration_rate()` to `StrategyState` and use in `StrategyReporter` ### Changed * Permutation with `seed_genes_list` now only permutates over the seeded genes (useful to calculate only a specific predefined set) * Add cleanup step in strategies. As the repeated/speciated calls keep the runs around, it seems better to cleanup the population and genotype storage * Rename `StrategyAction::Init` to `StrategyAction::SetupAndCleanup` * Rename `on_init()` event to `on_enter()` in `StrategyReporter` * Move duration reporting to `on_exit()` in reporters to include the cleanup duration ## [0.16.0] - 2024-09-18 ### Changed * Add a buffered option to all Reporters (through `new_with_buffer()`) * When the buffer is off (default), the reporter will print to stdout * When the buffer is on, the reporter will print to the internal buffer, which can be read out through `flush_reporter()` on the strategy later * Change `call_repeatedly()`, `call_par_repeatedly()`, `call_speciated()` and `call_par_speciated()` to return a tuple `(best_run, other_runs)`. This way the reporter buffers of the other runs can be read out as well afterwards ### Dropped * Drop `StrategyReporterBuffer`, as all reporters now have a buffered option ## [0.15.1] - 2024-09-17 ### Added * Add `StrategyReporterBuffer` implementation with an internal buffer instead of stdout * Add `flush_reporter()` in `Strategy` as the strategy can be boxed and we need a way to get to the buffer of the `Reporter` ## [0.15.0] - 2024-09-17 ### Changed * Implement `StrategyReporter` trait * It was held off in previous releases, because of the generics in the API, but it is needed for the superset `StrategyBuilder` * Provide `StrategyReporterNoop`, `StrategyReporterDuration` and `StrategyReporterSimple` reporters, usable by all strategies (but less informative) * Re-export `StrategyReporterNoop` as `EvolveReporterNoop,` `HillClimbReporterNoop` and `PermutateReporterNoop` * Re-export `StrategyReporterDuration` as `EvolveReporterDuration,` `HillClimbReporterDuration` and `PermutateReporterDuration` * Re-implement `EvolveReporterSimple,` `HillClimbReporterSimple,` `PermutateReporterSimple` as strategy specialized versions * Implement `EvolveGenotype` for all genotypes (as it was implicit) and move crossover functions over from `Genotype` * Rename `IncrementalGenotype` to `HillClimbGenotype` * Rename `PermutableGenotype` to `PermutateGenotype` * Move `Extension` step to follow after `Select` step, so the `fitness_score_cardinality` of the selected population can be taken as a trigger * Move reporter event `on_new_generation` to after selection for `Evolve`, as it is a more informative location in the loop ### Added * Add `StrategyBuilder,` a superset builder which delays specialization to `Evolve`, `HillClimb` or `Permutate`. Only usable by genotypes that implement all strategies (i.e. not for `RangeGenotype` and friends) * Add `call()`, `call_repeatedly()`, `call_par_repeatedly()`, `call_speciated()` and `call_par_speciated()` to `StrategyBuilder` (fallback to equivalent of not available on specialized strategy) * Add `EvolveVariant` (Standard only) and `PermutateVariant` (Standard only) for symmetry next to `HillClimbVariant` * Implement `mutation_type()` for all genotypes. Use it to trigger scaling logic in `Evolve` and `HillClimb.` * Implement `MutationType::Random` in `fill_neighbouring_population()` for `RangeGenotype`, `MultiRangeGenotype`, `DynamicMatrixGenotype` and `StaticMatrixGenotype`. It is not advised to use in `HillClimb` context, but a panic is worse. ### Dropped * Drop `EvolveReporterLog`, `HillClimbReporterLog` and `PermutateReporterLog` ## [0.14.0] - 2024-09-12 ### Design choices and internal impact (API changes listed separately below) * In order to support future GPU acceleration, the possibilty for the Genotype to store the genes of the whole population in a single contiguous memory location has been added. This has the following effects: * The Genotype has to be mutable and passed along for most operations. Affects a lot of interal function paramters * Chromosomes no longer always own their own genes, but can also just point to a section of the genotype's data storage. New triats `GenesOwner` (has genes) and `GenesPointer` (has row_id) for chromosomes * Each Genotype now has its own type of Chromosome, which implements the Genotype's genes storage model. Genotypes get an associated Chromosome type, all with their own alias. This leads to three core chromosome implementations: * `VectorChromosome`, stores `Vec` in genes field (the original chromosome) * `BitChromosome`, stores `FixedBitSet` in genes field * `RowChromosome`, stores genotype data row in row_id fields * Chromosomes can't just be created, cloned and dropped, as the genotype needs to keep track of which sections of the data storage are in use. Therefore Genotype now is the constructor and destructor of chromosomes. Added `ChromosomeManager` trait to implement on the Genotoype. The strategies and plugins now need to properly call population reduction and regrow methods through this ChromosomeManager trait * The `Fitness` now has two implementation points, the normal `calculate_for_chromosome(...)` and a population level `calculate_for_population(...)`. The latter is optional and can be used to calculate the genotype data strucure as a whole. * Because the chromosomes no longer always hold the genes, returning a `best_chromosome()` as end result from the strategies is not really suitable anymore. The method still is available when using standard chromosomes, but the new `best_genes_and_fitness_score()` is the preferred method as it works for all chromosome types. * The best_genes are now stored on the Genotype, instead of in a chromosome clone on the StrategyState. The latter would require the Genotype internal storage to keep a row reserved for the best_chromosome clone, which isn't nice. * Because the Genotype now constructs and destructs chromosomes, this feature can be leveraged to recycle chromosome allocations for all Genotypes. This leads to an overal performance improvement, especially noticable for large genes sizes and low survival rates. ### Changed (API) * The `calculate_for_chromosome(...)` client implementation now gets a reference to `Genotype`, which can subsequently be ignored for standard use. * The `EvolveReporter`, `HillClimbReporter` and `PermutateReporter` traits now also get a reference to `Genotype` on all functions, for the same reason * Add `FitnessGenotype`, `FitnessChromosome` & `FitnessPopulation` type aliases for better fitness API ### Added (API) * Add `DynamicMatrixGenotype`, storing the population's genes in a single contiguous memory `Vec` on the heap. All other features are like `RangeGenotype`. * Add `StaticMatrixGenotype`, storing the population's genes with a single contiguous memory `Box<[[T; N]; M]>` on the heap. All other features are like `RangeGenotype`. `N` is the genes size and `M` is the population size: * For `Evolve`, `M` would be the `target_population_size` * For `HillClimbVariant::SteepesAscent`, `M` would be the `neighbouring_population_size` * Add `calculate_for_population(...)` client implementation option in `Fitness`, only usable for the new matrix genotypes above. * Add `best_genes_and_fitness_score()` function on Evolve, HillClimb and Permutate. Prefer this use over `best_chromosome()`. * Add utility methods `genes_slice(chromosome)` and `best_genes_slice()` to `Genotype`, returning the genes as a slice for all chromosome types (`GenesOwned` or `GenesPointer`) * Add `Fitness` placeholder `SumDynamicMatrix` for `DynamicMatrixGenotype` (with optional precision) * Add `Fitness` placeholder `SumStaticMatrix` for `StaticMatrixGenotype` (with optional precision) * Add `EvolveReporterDuration`, `HillClimbReporterDuration` and `PermutateReporterDuration` to report only on the duration of the different phases of the strategies ### Removed (API) * Drop `HillClimbVariant::StochasticSecondary` and `HillClimbVariant::SteepestAscentSecondary` as `call_repeatedly(...)` on the basic variant is much more efficient * Drop `CrossoverParMultiPoint` as it conflicts with storage owning `Genotypes`. You would have to provide a mutable Genotype in a Mutex, which is not worth the effort ## [0.13.0] - 2024-09-11 ### Changed * Rename `Compete` to `Select` * Redo `Select`/`Crossover` selection & survival rates: * Remove `Crossover` `parent_survival_rate` and add `Select` `selection_rate` * `Select` now reduces the population and `Crossover` restores the population with the best parents after creating new offspring * Simulate the old behaviour of keeping all the parents by setting the `selection_rate = 0.5` and doubling the target_population_size * Implement `Allele` for `()` and set `BitGenotype::Allele = ()` as it is not used ### Removed * Drop `BinaryAllele` type alias for `bool`. It is the only of it's kind and never used ### Fixed * Fix `SelectElite` sorting (should be best first, was best last). Effect was that the best part of population was dropped instead of kept, resulting in slow to no solution in `Evolve` ## [0.12.1] - 2024-09-07 ### Fixed * Fix `CompeteElite` sorting (should be best first, was best last). Effect was that the best part of population was dropped instead of kept, resulting in slow to no solution in `Evolve` ## [0.12.0] - 2024-09-03 This is a major breaking release (back to pre-v0.9.0 API), see Changed: ### Changed * Add formal `Genes` trait: `Genes: Clone + Send + Sync + Debug` * Change associated type from `Allele` to `Genotype` for: `Fitness`, `EvolveReporter`, `HillClimbReporter` and `PermutateReporter` * Change generic type `Allele` to `Genotype` for: `Chromosome`, `Population` and other structs/functions using these types * Store `Genotype::Genes` instead of `Vec` in the `Chromosome` `genes` field ### Addded * Allow for non-`Vec` based genes in `Genotype.` Most existing `Genotype` implementations use `Vec` as genes, but now alternatives are possible * Add `BitGenotype` using `FixedBitSet` for genes storage. Functionally the same as `BinaryGenotype,` but better for large genes sizes as storage is much more efficient than `Vec`. * Add `Fitness` placeholder `CountOnes` for `BitGenotype` ## [0.11.1] - 2024-09-07 ### Fixed * Fix `CompeteElite` sorting (should be best first, was best last). Effect was that the best part of population was dropped instead of kept, resulting in slow to no solution in `Evolve` ## [0.11.0] - 2024-09-02 ### Changed * Change `Crossover`'s `keep_parent` parameter to `parent_survival_rate`. This keeps a fraction of the top parents, instead of the previous all or nothing boolean. * Add duration tracking of interal actions for `Evolve`, `HillClimb` & `Permutate` * Add duration tracking results in `EvolveReporterSimple`, `HillClimbReporterSimple` and `PermutateReporterSimple` * Remove `Genotype` parameter from `EvolveReporter`, `HillClimbReporter` and `PermutateReporter` `on_start` event, use `on_init` event for reporting about `Genotype` ### Removed * Drop `ExtensionMassInvasion` as reseeding the population conflicts with scaling. And reseeding can better be done by `call_repeatedly` or `call_speciated` anyway ### Fixed * Remove yanked package warning for `bytemuck v1.16.1` in Cargo.lock by updating all dependencies to latest versions ## [0.10.3] - 2024-08-29 ### Added * Add performance considerations in documentation * Add `CrossoverMultiGene`, `CrossoverMultiPoint` and `CrossoverParMultiPoint` * Add `Fitness` placeholders `Countdown` and `CountdownNoisy` ### Changed * Move all crossover and mutation logic to `Genotype`. The goal is to limit the knowledge of the internal genes structure to the Genotype only. Lots of internal changes, not relevant for API. * Improve `CrossoverSinglePoint` and `CrossoverMultiPoint` performance by avoiding cloning of genes * Reimplement `CrossoverUniform` as `CrossoverMultiGene` with `number_of_crossovers = genes_size / 2` and `allow_duplicates = true`, no API change ### Removed * Drop `CrossoverParUniform` in favor of `CrossoverParMultiPoint` as a more useful example implementation, although parallel execution of crossovers have no performance benefits for most situations ## [0.10.2] - 2024-08-26 ### Added * Add `with_rng_seed_from_u64_option()` function to `EvolveBuilder` and `HillClimbBuider` for more flexible API ## [0.10.1] - 2024-08-26 ### Changed * Move `PartialEq` requirement from general `Allele` to `ListGenotype` and `MultiListGenotype` specific allele requirements ### Added * Implement `Allele` trait for tuple sizes 1 to 12, as 12 is the limit for `PartialEq` in tuples ### Removed * Remove `Default` requirement on `Allele` for `RangeGenotype` and `MultiRangeGenotype` (no longer used) * Remove `Zero` requirement on `Allele` for `RangeGenotype` and `MultiRangeGenotype` (no longer used) ## [0.10.0] - 2024-08-26 ### Changed * Add Copy to Allele trait, this drops support for String Alleles, but is fine for primitives, enum and structs * Make the randomness provider internal to the API. You no longer need to provide it in the `call()` methods * Add `with_rng_seed_from_u64()` functions to `EvolveBuilder` and `HillClimbBuider` for reproducible runs (e.g. testing) * Align all multithreading approaches of `Evolve`, `HillClimb` & `Permutate` using [rayon::iter](https://docs.rs/rayon/latest/rayon/iter/index.html) and [std::sync::mpsc](https://doc.rust-lang.org/1.78.0/std/sync/mpsc/index.html) * Distinguish between internal and external multithreading: * Internal multithreading means: parallel execution within an `Evolve`, `HillClimb` or `Permutate` run (mainly `Fitness` calculations) * External multithreading means: parallel execution of multiple independent `Evolve` or `HillClimb` runs. * Note that `Permutate` only has internal multithreading as repeated calls make no sense * Note that internal and external multithreading can be combined * Note that internal multithreading has been explored for `Compete`, `Crossover` and `Mutate`. But the overhead of parallel execution was too high, generally resulting in degradation of performance. The breakeven point was found only for huge populations or genes_sizes, and only where each gene was part of the calculation (e.g. `CrossoverUniform`). Since `Fitness` is a client implementation which could be very heavy depending on the domain, an explicit `with_par_fitness()` is used for enabling internal multithreading of the fitness calculation only. Adding `with_par_crossover()` and friends has been considered, but due to the little expected gain, separate implementations where possibly beneficial are added instead (e.g. `CrossoverParUniform`). * Rename `with_multithreading` to `with_par_fitness()`, as to properly reflect it's effects only in the fitness calculations (internal multithreading) * Require `Send + Sync` to Compete, Crossover, Extension and Mutate * Change `chromosome_permutations_into_iter()` return type from `Box` to `impl Iterator` * Rename `with_multithreading()` to explicit `with_par_fitness()` for clarity of the effect ### Added * Add `call_par_repeatedly()` and `call_par_speciated()` to `EvolveBuilder` (external multithreading) * Add `call_par_repeatedly()` to `HillClimbBuilder` (external multithreading) * Add short-circuit for `call_speciated()` and `call_par_speciated()` when target_fitness_score is reached during speciation * Add `CountTrueWithSleep` fitness placeholder for use in multithreading examples and benchmarking * Add `CrossoverParUniform` for a multithreaded implemenation of `CrossoverUniform` * Add `reference_id: usize` to `Chromosome` as user controlled alternative to `genes_key()` for the GPU calculation use case described in [issue 5](https://github.com/basvanwesting/genetic-algorithm/issues/5) ## [0.9.0] - 2024-08-20 This is a major breaking release, see Changed: ### Changed * Add formal `Allele` trait: `Allele: Clone + Send + Sync + PartialEq + Debug` * Change associated type from `Genotype` to `Allele` for: `Fitness`, `EvolveReporter`, `HillClimbReporter` and `PermutateReporter` * Change generic type `Genotype` to `Allele` for: `Chromosome`, `Population` and other structs/functions using these types * Rename `DiscreteGenotype` to `ListGenotype` (incl. Multi) * Rename `ContinuousGenotype` to `RangeGenotype` (incl Multi) * Generalize `RangeGenotype` for numeric types (incl. Multi, default still f32, but other float and integer types are now supported) * Replace `Range` with `RangeInclusive` for all ranges in `RangeGenotype` in order to handle integer ranges more intuitively (incl. Multi) * Change `Fitness` placeholders `SumContinuousAllele` and `SumDiscreteAllele` to generalized `SumGenes` (with optional precision) * Reimplement scaling completely now `RangeGenotype` is generalized. * Drop f32 `Scaling` logic * Set `allele_mutation_scaled_range` on `RangeGenotype` to define scaling (incl. Multi) instead of `with_scaling()` in `HillClimb` 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 new `stale_generations` counter to zero * Only trigger `max_stale_generations` ending condition when already reached the smallest scale * 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` to `MutateSingleGene` as it just calls `mutate_chromosome()` on `Genotype` * Rename `MutateSingleGeneRandomDynamic` to `MutateSingleGeneDynamic` as it just calls `mutate_chromosome()` on `Genotype` * Rename `MutateMultiGeneRandom` to `MutateMultiGene` as it just calls `mutate_chromosome()` on `Genotype` * Rename `MutateMultiGeneRandomDynamic` to `MutateMultiGeneDynamic`as it just calls `mutate_chromosome()` on `Genotype` * Rename `allele_neighbour_range` to `allele_mutation_range` in `RangeGenoype` (incl. Multi) to define relative mutation * Add `allele_mutation_scaled_range` to `RangeGenotype` (incl. Multi) to define scaled mutation * All changes to `RangeGenotype` are reflected in `MultiRangeGenotype` as well ### Added * Allow relative mutations for `Evolve` as well, as it is a `Genotype` responsibility now * Allow scaled mutations for `Evolve` as well, as it is a `Genotype` responsibility now * Scale down after `max_stale_generations` is reached and reset `stale_generations` to zero * Only trigger `max_stale_generations` ending condition when already reached the smallest scale * Add `replace_on_equal_fitness` to builders to allow for lateral moves in search space * `Evolve`: defaults to false, maybe useful to avoid repeatedly seeding with the same best chromosomes after mass extinction events * `HillClimb`: defaults to true, crucial for some type of problems with discrete fitness steps like nqueens * `Permutate`: defaults to false, makes no sense to use in this strategy ### Removed * Drop `MutateSingleGeneDistance` as random, relative or scaled mutations are now handled by `Genotype` and not the caller ## [0.8.2] - 2024-08-09 ### Added * Improve bootstrapped reporter outputs `EvolveReporterSimple`, `HillClimbReporterSimple` and `PermutateReporterSimple` * Implement `ExtensionNoop` default for `EvolveBuilder` and remove now optional `with_extension()` steps in examples * Implement `EvolveReporterNoop` default for `EvolveBuilder` and remove now optional `with_reporter()` steps in examples * Implement `HillClimbReporterNoop` default for `HillClimbBuilder` and remove now optional `with_reporter()` steps in examples * Implement `PermutateReporterNoop` default for `PermutateBuilder` and remove now optional `with_reporter()` steps in examples ### Changed * Align `EvolveReporter`, `HillClimbReporter` and `PermutateReporter` traits to take `EvolveState` and `EvolveConfig` as parameters in further aligment with `Mutate`, `Compete`, `Crossover` and `Extension` traits. * Add `Sync` trait everywhere where `Send` trait was required. ### Fixed * 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. ## [0.8.1] - 2024-08-08 ### Added * Always implement `new()` next to `default()`. Use `new()` in public API examples ### Changed * Rename `new()` to `new_with_flags()` for more verbose reporting in `EvolveReporterSimple`, `HillClimbReporterSimple` and `PermutateReporterSimple` * Add simpler `new()` to only take `period: usize` and set all flags to false (as this is the sensible less noisy default) in `EvolveReporterSimple`, `HillClimbReporterSimple` and `PermutateReporterSimple` ## [0.8.0] - 2024-08-07 ### Added * Add `PermutateConfig` and `PermutateState` to align structure with `Evolve` and `HillClimb` * Extract `StrategyConfig` trait and use for `EvolveConfig`, `HillClimbConfig` and `PermutateConfig` * Extract `StrategyState` trait and use for `EvolveState`, `HillClimbState` and `PermutateState` * Add pluggable `EvolveReporter` to `Evolve` strategy * Set in builder using `with_reporter()` * Custom implementations by client are encouraged, the API resembles the Fitness API * Add bootstrap implementations `EvolveReporterNoop`, `EvolveReporterSimple` and `EvolveReporterLog` * Add pluggable `HillClimbReporter` to `HillClimb` strategy * Set in builder using `with_reporter()` * Custom implementations by client are encouraged, the API resembles the Fitness API * Add bootstrap implementations `HillClimbReporterNoop`, `HillClimbReporterSimple` and `HillClimbReporterLog` * Add pluggable `PermutateReporter` to `Permutate` strategy * Set in builder using `with_reporter()` * Custom implementations by client are encouraged, the API resembles the Fitness API * Add bootstrap implementations `PermutateReporterNoop`, `PermutateReporterSimple` and `PermutateReporterLog` * Add `fitness_score_cardinality()` to `Population` * Add `MutateMultiGeneRandomDynamic` (generalize to any number of mutations) * Add `MutateSingleGeneDistance` (only for `ContinuousGenotype`) ### Removed * Drop `fitness_score_uniformity()` and `fitness_score_prevalence()` from `Population` * Drop `MutateDynamicRounds` ### Changed * Align `Mutate`, `Compete`, `Crossover` and `Extension` traits to take `EvolveState`, `EvolveConfig`, `EvolveReporter` as parameters * Reimplement `MutateOnce` as `MutateSingleGeneRandom` * Reimplement `MutateTwice` as `MutateMultiGeneRandom` (generalize to any number of mutations) * Reimplement `MutateDynamicOnce` as `MutateSingleGeneRandomDynamic` (also fix InvalidProbabilty issue) * Replace `target_uniformity` with `target_cardinality` in `MutateSingleGeneRandomDynamic` and `MutateMultiGeneRandomDynamic` as uniformity is ill defined * Replace `uniformity_threshold` with `cardinality_threshold` in `Extension` implementations, as uniformity is ill defined * Move permutation `total_population_size` from `PermutateConfig` to `PermutateState`, so progress can be reported on in `PermutateReporterSimple` * Move `env_logger` dependency to dev-dependencies as this crate is a library, not an executable ### Note * Note that `HillClimb` scaling needs review as it doesn't feel right in its design approach. Possibly align with `MutateSingleGeneDistance` approach? * Extract `StrategyReporter` trait, but don't use because of error E0658: associated type defaults are unstable. So for `EvolveReporter`, `HillClimbReporter` and `PermutateReporter` the trait is shadowed as if it is implemented ## [0.7.2] - 2024-07-27 ### Added * Add `Wrapper`s instead of `Dispatcher`s as they keep state, behaviour is the same using `into()` (e.g. `MutateOnce::new(0.2).into()`) ### Removed * Extract Meta logic to separate crate [genetic_algorithm_meta](https://docs.rs/genetic_algorithm_meta/latest/genetic_algorithm_meta) * Phase out the `Dispatcher`s as they are replaced by `Wrapper`s ## [0.7.1] - 2024-07-23 ### Changed * MSRV bumped to 1.71.1 * Solve [RUSTSEC-2021-0145](https://rustsec.org/advisories/RUSTSEC-2021-0145) ## [0.7.0] - 2023-05-25 ### Added * Add `Mutate` implementations: * `MutateTwice`, support some form of swap-like behaviour where `UniqueGenotype` doesn't match with the problem space * `MutateDynamicOnce`, increase mutation probability when population uniformity is above threshold and vice versa * `MutateDynamicRounds`, increase mutation rounds when population uniformity is above threshold and vice versa * Add `HillClimbVariant::StochasticSecondary` and `HillClimbVariant::SteepestAscentSecondary` as well for the same reasons as `MutateTwice` * Add `call_speciated` next to the existing `call_repeatedly` in `EvolveBuilder`. 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 optional `with_max_chromosome_age` to `EvolveBuilder`. Filtering chromosomes past the maximum age from the next generation * Add `best_generation()` and `best_fitness_score()` to `Strategy`, so client implementation can report and switch more easily over different strategies. Return zero for `Permutate::best_generation()` as there is no concept of best generation there * Add `Extension` step to `Evolve`, adding `with_extension` to `EvolveBuilder`, with several implementations: * `ExtensionNoop`, for no extension * `ExtensionMassExtinction`, trigger mass extinction to allow for cambrian explosion (no competition for a while, which allows for more diversity) * `ExtensionMassGenesis`, like `ExtensionMassExtinction`, but only a pair of best chromosomes (adam and eve) are taken as the start for the next generations * `ExtensionMassInvasion`, like `ExtensionMassExtinction`, 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 for `MutateDynamic*` and `Extension` * Add dispatch `From` to `Evolve` plugins for use in `MetaConfigBuilder`, instead of manual wrapping (e.g. `MutateOnce::new(0.2).into()` instead of `MutateDispatch(MutateOnce::new(0.2)`) ### Changed * Refactor `Compete`, `Crossover` and `Mutate` 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` and `HillClimb`, leave `Permutate` untouched weighing overkill v. symmetry different there * Split off internal plugins for `Evolve` (i.e. `Mutate`/`Crossover`/`Compete`/`Extension`) * Change `seed_genes` to `seed_genes_list` to allow for multiple seed genes taken randomly (used in `call_speciated`) * Only mutate children in the `Mutate` step, in earlier versions parents and children were mutated equally * Refactor `Evolve` `population_size` property to `target_population_size`, thus also replacing `with_population_size` with `with_target_population_size` * Add `env_logger::init()` to all examples, so the `RUST_LOG` environment variable works as expected * Change `HillClimbBuilder::with_scaling` parameter from tuple to struct `Scaling` ### Removed * Phase out the `with_mass_degeneration` in `EvolveBuilder` as it is replaced by `ExtensionMassDegeneration` ## [0.6.0] - 2022-10-14 ### Changed * Calculate initial chromosome fitness in `HillClimb` to lock in on original seed if present ### Removed * Remove `random_chromosome_probability` to `HillClimb` as it was hackish ## [0.5.4] - 2022-10-14 ### Added * Add `valid_fitness_score` to block ending conditions until met for `Evolve` and `HillClimb` strategies ## [0.5.3] - 2022-10-14 ### Changed * Tweak TRACE logging ## [0.5.2] - 2022-10-14 ### Added * Add env_logger and some INFO/DEBUG/TRACE logging ### Changed * Count generation zero based ## [0.5.1] - 2022-09-10 ### Fixed * Solve lock-in to single best chromosome in stale `HillClimbVariant::SteepestAscent` by shuffling chromosomes before taking best ## [0.5.0] - 2022-07-07 ### Added * Add `IncrementalGenotype` Trait with neighbouring chromosome implementations * Implement `IncrementalGenotype` for all `Genotype`s * Add `allele_neighbour_range` to `ContinuousGenotype` * Add `allele_neighbour_ranges` for `MultiContinuousGenotype` * Add `HillClimbVariant::Stochastic` and `HillClimbVariant::SteepestAscent` * Add `HillClimb` scaling (for `ContinuousGenotype` & `MultiContinuousGenotype`) to scale down neighbours on each round and use as ending condition * Add `random_chromosome_probability` to `HillClimb` 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 for `HillClimbVariant::SteepestAscent` population only) * Add `call_repeatedly` for `EvolveBuilder` and `HillClimbBuilder` * 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 ### Changed * Require `IncrementalGenotype` for `HillClimb` strategy * Refactor `allele_values` to `allele_list` * Refactor `allele_multi_values` to `allele_lists` * Refactor `allele_multi_range` to `allele_ranges` * Add median/mean/stddev to `report_round` in `Evolve` and `HillClimb` * Add precision to `SumContinuousGenotype` and `SumMultiContinuousGenotype` placeholders for better handling of decimal changes on cast to isize ## [0.4.1] - 2022-06-14 ### Documentation * 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 ## [0.4.0] - 2022-06-14 ### Documentation * Note degeneration_range use as case by case configuration ### Added * Add `Strategy` trait and implement for `Evolve` and `Permutate` * Add `HillClimb` strategy for when crossover is impossible or inefficient * Add `MultiUniqueGenotype` * Add table_seating example (hill_climb and evolve) ### Changed * Move `Evolve` & `Permutate` to `strategy` module * Remove `Genotype::is_unique` and `Crossover::allow_unique_genotype` methods * Replace with `Genotype::crossover_indexes` and `Crossover::require_crossover_indexes` * Replace with `Genotype::crossover_points` and `Crossover::require_crossover_points` * Rename `UniqueDiscreteGenotype` to `UniqueGenotype` as it is discrete by definition * Rename `PermutableGenotype::allele_values` to `PermutableGenotype::allele_values_for_chromosome_permutations` for clarity of purpose * Hide `Evolve` and `Permutate` internal fields (to align with `Strategy` trait) ## [0.3.1] - 2022-05-16 ### Fixed * forgot to update version in `Cargo.toml` ## [0.3.0] - 2022-05-16 ### Documentation * Make proper distinction between gene and allele as in the book "Genetic Algorithms in Elixir" ### Added * Add option to `call()` from `EvolveBuilder` & `PermutateBuilder` directly * Add `Fitness::Zero` placeholder ### Changed * Refactor `Evolve` & `Permutate` to `call(&mut self, ...)` * Refactor `Fitness`, `Crossover`, `Mutate` & `Compete` to take mutable population reference * Improve performance in `Crossover` when not keeping parents * Rename `gene_value*` to `allele_value*` * Rename `gene_ranges` to `allele_multi_range` for symmetry reasons with `allele_multi_values` * Rename `gene_size` to `genes_size` as it is not the size of a gene * Rename `CrossoverSingle` to `CrossoverSingleGene` * Rename `CrossoverRange` to `CrossoverSinglePoint` * Rename `CrossoverAll` to `CrossoverUniform` ### Removed * Drop SetGenotype as it is always better implemented using BinaryGenotype * Cleanup examples ## [0.2.0] - 2022-05-13 ### Added * Add `SetGenotype`, with `examples/evolve_knapsack_set.rs` and `examples/permutate_knapsack_set.rs` ### Changed * Refactor fitness placeholders to placeholders module to emphasize that production use is not intended * Rename `Fitness::call_for_chromosome()` to `Fitness::calculate_for_chromosome()` * Replaced `PermutableGenotype::population_factory()` with `PermutableGenotype::chromosome_permutations_into_iter()` * Use `PermutableGenotype::chromosome_permutations_into_iter()` in `Permutate::call()` instead of fully instantiated population * Rename `PermutableGenotype::population_factory_size()` to `PermutableGenotype::chromosome_permutations_size()` * Use `num::BigUint` for `PermutableGenotype::chromosome_permutations_size()` as it overflows easily * Rename existing `examples/evove_knapsack_set.rs` to `examples/evolve_knapsack_discrete.rs` to note is uses `DiscreteGenotype` ### Documentation - Improve rustdocs, refer to docs.rs documentation from general README.md ## [0.1.1] - 2022-05-11 ### Documentation - Added rustdocs, refer to crate.io documentation from general README.md ## [0.1.0] - 2022-05-10 Initial version