pub trait Fitness:
Clone
+ Send
+ Sync
+ Debug {
type Genotype: Genotype;
// Provided methods
fn call_for_state_population<S: StrategyState<Self::Genotype>>(
&mut self,
state: &mut S,
genotype: &Self::Genotype,
thread_local: Option<&ThreadLocal<RefCell<Self>>>,
) { ... }
fn call_for_state_chromosome<S: StrategyState<Self::Genotype>>(
&mut self,
state: &mut S,
genotype: &Self::Genotype,
) { ... }
fn call_for_population(
&mut self,
population: &mut FitnessPopulation<Self>,
genotype: &Self::Genotype,
thread_local: Option<&ThreadLocal<RefCell<Self>>>,
) { ... }
fn call_for_chromosome(
&mut self,
chromosome: &mut FitnessChromosome<Self>,
genotype: &Self::Genotype,
) { ... }
fn calculate_for_population(
&mut self,
_population: &FitnessPopulation<Self>,
_genotype: &Self::Genotype,
) -> Vec<Option<FitnessValue>> { ... }
fn calculate_for_chromosome(
&mut self,
_chromosome: &FitnessChromosome<Self>,
_genotype: &Self::Genotype,
) -> Option<FitnessValue> { ... }
}
Expand description
The fitness function, is implemented as a fitness method object.
Normally the fitness returns Some(FitnessValue)
for the chromosome, which can be minimized or
maximized in the search strategy (e.g. Evolve) by providing the FitnessOrdering.
If the fitness returns None
, the chromosome is assumed invalid and taken last in the selection phase (also when minimizing).
§User implementation
There are two possible levels to implement. At least one level needs to be implemented:
calculate_for_chromosome(...) -> Option<FitnessValue>
- The standard situation, suits all strategies. Implementable with all Genotypes.
- Standard Genotypes have GenesOwner chromosomes. These
chromosomes have a
genes
field, which can be read for the calculations. - non-standard Genotypes with GenesPointer chromosomes.
These chromosomes have don’t have a
genes
field, so you need to retrieve the genes using genotype.genes_slice(&chromosome), which can then be read for the calculations. But for these types you usually don’t want to reach this call level, see other level below
calculate_for_population(...) -> Vec<Option<FitnessValue>>
- Only overwrite for matrix Genotypes (designed for possible GPU acceleration)
- If not overwritten, results in calling calculate_for_chromosome for each chromosome in the population. So it doesn’t have to be implemented by default, but it is a possible point to intercept with a custom implementation where the whole population data is available.
- Only for Genotype with GenesPointer chromosomes. These
chromosomes don’t have a
genes
field to read, but arow_id
. The matrix Genotype has a contiguous memorydata
field with all the data, which can be calculated in one go. - The order and length of the rows in the genotype data matrix needs to be preserved in the returned vector as it matches the row_id on the chromosome
- The order and length of the population does not matter at all and will most likely not align.
The population is provided, because the full genotype matrix data also contains untainted
chromosomes which already have a fitness_score (and will not be updated). The results for
these chromosomes will be ignored, thus these do not have to be recalculated, so knowing
which ones might be relevant (or not). The order of the results still need to align, so if the
calculation is skipped, a
None
value should be inserted in the results to keep the order and length aligned.
The strategies use different levels of calls in Fitness. So you cannot always just intercept at calculate_for_population and be sure calculate_for_chromosome will not be called:
- Population level calculations (calling calculate_for_chromosome indirectly through calculate_for_population, if not overwritten)
- Chromosome level calculations (calling
calculate_for_chromosome directly, bypassing
calculate_for_population entirely)
- Permutate
- HillClimb with Stochastic
Therefore, additionally, you might need to implement calculate_for_chromosome for GenesPointer chromosomes. This is sometimes needed when testing out different strategies with different call levels. Problably no longer needed once settled on a strategy.
§Panics
calculate_for_chromosome has a default implementation which panics, because it doesn’t need to be implemented for genotypes which implement calculate_for_population. Will panic if reached and not implemented.
§Example (calculate_for_chromosome, standard GenesOwner chromosome):
use genetic_algorithm::fitness::prelude::*;
#[derive(Clone, Debug)]
pub struct CountTrue;
impl Fitness for CountTrue {
type Genotype = BinaryGenotype;
fn calculate_for_chromosome(
&mut self,
chromosome: &FitnessChromosome<Self>,
_genotype: &FitnessGenotype<Self>
) -> Option<FitnessValue> {
Some(chromosome.genes.iter().filter(|&value| *value).count() as FitnessValue)
}
}
§Example (calculate_for_population, static matrix calculation, GenesPointer chromosome):
use genetic_algorithm::fitness::prelude::*;
use genetic_algorithm::strategy::evolve::prelude::*;
#[derive(Clone, Debug)]
pub struct SumStaticMatrixGenes;
impl Fitness for SumStaticMatrixGenes {
type Genotype = StaticMatrixGenotype<u16, 10, 100>;
fn calculate_for_population(
&mut self,
population: &Population<StaticMatrixChromosome>,
genotype: &FitnessGenotype<Self>,
) -> Vec<Option<FitnessValue>> {
// pure matrix data calculation on [[T; N] M]
// the order of the rows needs to be preserved as it matches the row_id on the chromosome
// the order of the population does not matter at all and will most likely not align
genotype
.data
.iter()
.map(|genes| {
genes
.iter()
.sum::<u16>() as FitnessValue
})
.map(Some)
.collect()
}
}
§Example (calculate_for_population, dynamic matrix calculation, GenesPointer chromosome):
use genetic_algorithm::fitness::prelude::*;
use genetic_algorithm::strategy::evolve::prelude::*;
#[derive(Clone, Debug)]
pub struct SumDynamicMatrixGenes;
impl Fitness for SumDynamicMatrixGenes {
type Genotype = DynamicMatrixGenotype<u16>;
fn calculate_for_population(
&mut self,
population: &Population<DynamicMatrixChromosome>,
genotype: &FitnessGenotype<Self>,
) -> Vec<Option<FitnessValue>> {
// pure matrix data calculation on Vec<T> with genes_size step
// the order of the rows needs to be preserved as it matches the row_id on the chromosome
// the order of the population does not matter at all and will most likely not align
genotype
.data
.chunks(genotype.genes_size())
.map(|genes| {
genes
.iter()
.sum::<u16>() as FitnessValue
})
.map(Some)
.collect()
}
}
§Example (calculate_for_chromosome, matrix fall back, GenesPointer chromosome):
Note: For exploration purposes when switching stratgies a lot, not really used in final implementation
use genetic_algorithm::fitness::prelude::*;
use genetic_algorithm::strategy::hill_climb::prelude::*;
#[derive(Clone, Debug)]
pub struct SumStaticMatrixGenes;
impl Fitness for SumStaticMatrixGenes {
type Genotype = StaticMatrixGenotype<u16, 10, 100>;
fn calculate_for_chromosome(
&mut self,
chromosome: &FitnessChromosome<Self>,
genotype: &Self::Genotype,
) -> Option<FitnessValue> {
let score = genotype.genes_slice(chromosome).iter().sum::<u16>();
Some(score as FitnessValue)
}
}
Required Associated Types§
Provided Methods§
fn call_for_state_population<S: StrategyState<Self::Genotype>>( &mut self, state: &mut S, genotype: &Self::Genotype, thread_local: Option<&ThreadLocal<RefCell<Self>>>, )
fn call_for_state_chromosome<S: StrategyState<Self::Genotype>>( &mut self, state: &mut S, genotype: &Self::Genotype, )
Sourcefn call_for_population(
&mut self,
population: &mut FitnessPopulation<Self>,
genotype: &Self::Genotype,
thread_local: Option<&ThreadLocal<RefCell<Self>>>,
)
fn call_for_population( &mut self, population: &mut FitnessPopulation<Self>, genotype: &Self::Genotype, thread_local: Option<&ThreadLocal<RefCell<Self>>>, )
Pass thread_local for external control of fitness caching in multithreading
fn call_for_chromosome( &mut self, chromosome: &mut FitnessChromosome<Self>, genotype: &Self::Genotype, )
Sourcefn calculate_for_population(
&mut self,
_population: &FitnessPopulation<Self>,
_genotype: &Self::Genotype,
) -> Vec<Option<FitnessValue>>
fn calculate_for_population( &mut self, _population: &FitnessPopulation<Self>, _genotype: &Self::Genotype, ) -> Vec<Option<FitnessValue>>
Optional interception point for client implementation.
The order and length of the results need to align with the order and length of the genotype data matrix. The order and length of the population does not matter at all and will most likely not align.
Sourcefn calculate_for_chromosome(
&mut self,
_chromosome: &FitnessChromosome<Self>,
_genotype: &Self::Genotype,
) -> Option<FitnessValue>
fn calculate_for_chromosome( &mut self, _chromosome: &FitnessChromosome<Self>, _genotype: &Self::Genotype, ) -> Option<FitnessValue>
Optional interception point for client implementation
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.