Struct Evolve

Source
pub struct Evolve<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select, E: Extension, SR: StrategyReporter<Genotype = G>> {
    pub genotype: G,
    pub fitness: F,
    pub plugins: EvolvePlugins<M, S, C, E>,
    pub config: EvolveConfig,
    pub state: EvolveState<G>,
    pub reporter: SR,
    pub rng: SmallRng,
}
Expand description

The Evolve strategy initializes with a random population of chromosomes (unless the genotype seeds specific genes to sample from), calculates fitness for all chromosomes and sets a first best chromosome (if any).

Then the Evolve strategy runs through generations of chromosomes in a loop:

  • select and pair up chromosomes for crossover
  • extension an optional step triggering on population cardinality after selection (e.g. MassExtinction)
  • crossover to produce new offspring with a mix of parents chromosome.
  • mutate the offspring chromosomes to add some additional diversity
  • calculate fitness for all chromosomes
  • store best chromosome and check ending conditions

The ending conditions are one or more of the following:

  • target_fitness_score: when the ultimate goal in terms of fitness score is known and reached
  • max_stale_generations: when the ultimate goal in terms of fitness score is unknown and one depends on some convergion threshold, or one wants a duration limitation next to the target_fitness_score

There are optional mutation distance limitations for RangeGenotype and MultiRangeGenotype chromosomes. Listed in descending priority:

  • With allele_mutation_scaled_range(s) set on genotype:
    • Mutation distance only on edges of current scale (e.g. -1 and +1 for -1..-1 scale), pick random edge
    • 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
  • With allele_mutation_range(s) set on genotype:
    • Mutation distance taken uniformly from mutation range
    • Standard max_stale_generations ending condition
  • With only allele_range(s) set on genotype:
    • Mutate uniformly over the complete allele range
    • Standard max_stale_generations ending condition

There are reporting hooks in the loop receiving the EvolveState, which can by handled by an StrategyReporter (e.g. EvolveReporterDuration, EvolveReporterSimple). But you are encouraged to roll your own, see StrategyReporter.

For Evolve the reporting on_new_generation hook is called just after selection, because that is a more interesting point in the loop.

From the EvolveBuilder level, there are several calling mechanisms:

  • call: this runs a single evolve strategy
  • call_repeatedly: this runs multiple independent evolve strategies and returns the best one (or short circuits when the target_fitness_score is reached)
  • call_par_repeatedly: this runs multiple independent evolve strategies in parallel and returns the best one (or short circuits when the target_fitness_score is reached). This is separate and independent from the with_par_fitness() flag on the builder, which determines multithreading of the fitness calculation inside the evolve strategy. Both can be combined.
  • call_speciated: this runs multiple independent evolve strategies and then selects their best results against each other in one final evolve strategy (or short circuits when the target_fitness_score is reached)
  • call_par_speciated: this runs multiple independent evolve strategies in parallel and then selects their best results against each other in one final evolve strategy (or short circuits when the target_fitness_score is reached). This is separate and independent from the with_par_fitness() flag on the builder, which determines multithreading of the fitness calculation inside the evolve strategy. Both can be combined.

All multithreading mechanisms are implemented using rayon::iter and std::sync::mpsc.

See EvolveBuilder for initialization options.

Example:

use genetic_algorithm::strategy::evolve::prelude::*;
use genetic_algorithm::fitness::placeholders::CountTrue;

// the search space
let genotype = BinaryGenotype::builder() // boolean alleles
    .with_genes_size(100)                // 100 genes per chromosome
    .with_genes_hashing(true)            // store genes_hash on chromosome (required for fitness_cache, optional for better population cardinality estimation)
    .build()
    .unwrap();

// the search strategy
let evolve = Evolve::builder()
    .with_genotype(genotype)
    .with_select(SelectElite::new(0.9))                    // sort the chromosomes by fitness to determine crossover order and select 90% of the population for crossover (drop 10% of population)
    .with_extension(ExtensionMassExtinction::new(10, 0.1)) // optional builder step, simulate cambrian explosion by mass extinction, when fitness score cardinality drops to 10 after the selection, trim to 10% of population
    .with_crossover(CrossoverUniform::new())               // crossover all individual genes between 2 chromosomes for offspring (and restore back to 100% of target population size by keeping the best parents alive)
    .with_mutate(MutateSingleGene::new(0.2))               // mutate offspring for a single gene with a 20% probability per chromosome
    .with_fitness(CountTrue)                               // count the number of true values in the chromosomes
    .with_fitness_ordering(FitnessOrdering::Minimize)      // aim for the least true values
    .with_fitness_cache(1000)                              // enable caching of fitness values (LRU size 1000), only works when genes_hash is stored in chromosome. Only useful for long stale runs, but better to increase population diversity
    .with_par_fitness(true)                                // optional, defaults to false, use parallel fitness calculation
    .with_target_population_size(100)                      // evolve with 100 chromosomes
    .with_target_fitness_score(0)                          // ending condition if 0 times true in the best chromosome
    .with_valid_fitness_score(10)                          // block ending conditions until at most a 10 times true in the best chromosome
    .with_max_stale_generations(1000)                      // stop searching if there is no improvement in fitness score for 1000 generations
    .with_max_chromosome_age(10)                           // kill chromosomes after 10 generations
    .with_reporter(EvolveReporterSimple::new(100))         // optional builder step, report every 100 generations
    .with_replace_on_equal_fitness(true)                   // optional, defaults to false, maybe useful to avoid repeatedly seeding with the same best chromosomes after mass extinction events
    .with_rng_seed_from_u64(0)                             // for testing with deterministic results
    .call()
    .unwrap();

// it's all about the best genes after all
let (best_genes, best_fitness_score) = evolve.best_genes_and_fitness_score().unwrap();
assert_eq!(best_genes, vec![false; 100]);
assert_eq!(best_fitness_score, 0);

Fields§

§genotype: G§fitness: F§plugins: EvolvePlugins<M, S, C, E>§config: EvolveConfig§state: EvolveState<G>§reporter: SR§rng: SmallRng

Implementations§

Source§

impl<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select, E: Extension, SR: StrategyReporter<Genotype = G>> Evolve<G, M, F, S, C, E, SR>
where G::Chromosome: GenesOwner<Genes = G::Genes>,

Source§

impl<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select> Evolve<G, M, F, S, C, ExtensionNoop, StrategyReporterNoop<G>>

Source§

impl<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select, E: Extension, SR: StrategyReporter<Genotype = G>> Evolve<G, M, F, S, C, E, SR>

Source

pub fn setup(&mut self, fitness_thread_local: Option<&ThreadLocal<RefCell<F>>>)

Source

pub fn cleanup( &mut self, fitness_thread_local: Option<&mut ThreadLocal<RefCell<F>>>, )

Trait Implementations§

Source§

impl<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select, E: Extension, SR: StrategyReporter<Genotype = G>> Display for Evolve<G, M, F, S, C, E, SR>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select, E: Extension, SR: StrategyReporter<Genotype = G>> Strategy<G> for Evolve<G, M, F, S, C, E, SR>

Source§

fn call(&mut self)

Source§

fn best_generation(&self) -> usize

Source§

fn best_fitness_score(&self) -> Option<FitnessValue>

Source§

fn best_genes(&self) -> Option<G::Genes>

Source§

fn flush_reporter(&mut self, output: &mut Vec<u8>)

strategy can be boxed, need a way to get to the reporter
Source§

fn best_genes_and_fitness_score(&self) -> Option<(G::Genes, FitnessValue)>

Source§

impl<G: EvolveGenotype, M: Mutate, F: Fitness<Genotype = G>, S: Crossover, C: Select, E: Extension, SR: StrategyReporter<Genotype = G>> TryFrom<Builder<G, M, F, S, C, E, SR>> for Evolve<G, M, F, S, C, E, SR>

Source§

type Error = TryFromStrategyBuilderError

The type returned in the event of a conversion error.
Source§

fn try_from( builder: EvolveBuilder<G, M, F, S, C, E, SR>, ) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

§

impl<G, M, F, S, C, E, SR> Freeze for Evolve<G, M, F, S, C, E, SR>
where G: Freeze, F: Freeze, SR: Freeze, M: Freeze, S: Freeze, C: Freeze, E: Freeze, <G as Genotype>::Chromosome: Freeze,

§

impl<G, M, F, S, C, E, SR> RefUnwindSafe for Evolve<G, M, F, S, C, E, SR>

§

impl<G, M, F, S, C, E, SR> Send for Evolve<G, M, F, S, C, E, SR>

§

impl<G, M, F, S, C, E, SR> Sync for Evolve<G, M, F, S, C, E, SR>
where <G as Genotype>::Chromosome: Sync,

§

impl<G, M, F, S, C, E, SR> Unpin for Evolve<G, M, F, S, C, E, SR>
where G: Unpin, F: Unpin, SR: Unpin, M: Unpin, S: Unpin, C: Unpin, E: Unpin, <G as Genotype>::Chromosome: Unpin,

§

impl<G, M, F, S, C, E, SR> UnwindSafe for Evolve<G, M, F, S, C, E, SR>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V