Example
Lets add on to our example - evolving a simple function: finding the best values for y = ax + b where we want to find optimal values for a and b. We'll keep the previous inputs the same as before, but now we add diversity to the GeneticEngine.
Speciation
In this example we'll include speciation, but for all of the following examples we'll keep the same problem but leave speciation off (not adding the diversity input). This is also a good time to again highlight that speciation is opt-in and not free - it has a computational cost.
import radiate as rd
# Define a fitness function that uses the decoded values
def fit(individual: list[float]) -> float:
a = individual[0]
b = individual[1]
return calculate_error(a, b) # Your error calculation here
# Define the diversity measure - we'll use Euclidean distance to
# measure how different, or structurally distant, two individuals are.
# This is the function used to determine species membership and manage diversity.
diversity = rd.Dist.euclidean()
engine = (
rd.Engine.float(2, init_range=(-1.0, 1.0), bounds=(-10.0, 10.0), dtype=rd.Float32)
.fitness(fit)
.select(
offspring=rd.Select.boltzmann(temp=4),
survivor=rd.Select.tournament(k=3),
frac=0.5,
)
.alters(
rd.Mutate.gaussian(rate=0.1),
rd.Cross.blend(rate=0.8, alpha=0.5),
)
.diversity(
diversity, species_threshold=0.5
) # <- Add the diversity measure and species threshold
.age(max_species_age=25) # <- Add the max species age
.limit(rd.Limit.score(0.01), rd.Limit.generations(1000))
# ... other parameters ...
)
# Run the engine
result = engine.run()
// Define a fitness function that uses the decoded values
fn fit(individual: Vec<f32>) -> f32 {
let a = individual[0];
let b = individual[1];
calculate_error(a, b) // Your error calculation here
}
// This will produce a Genotype<FloatChromosome> with 1 FloatChromosome which
// holds 2 FloatGenes (a and b), each with a value between -1.0 and 1.0 and a bound between -10.0 and 10.0
let codec = FloatCodec::vector(2, -1.0..1.0).with_bounds(-10.0..10.0);
// Define the diversity measure
let diversity = EuclideanDistance; // or HammingDistance for discrete problems
let engine = GeneticEngine::builder()
.codec(codec)
.offspring_selector(BoltzmannSelector::new(4.0))
.survivor_selector(TournamentSelector::new(3))
.fitness_fn(fit)
.alter(alters!(
GaussianMutator::new(0.1),
BlendCrossover::new(0.8, 0.5)
))
.diversity(diversity) // Add the diversity measure
.species_threshold(0.5) // Default value
.max_species_age(25) // Default value
// ... other parameters ...
.build();
// Run the engine: stop after 100 generations.
// Now that we've added diversity, each generation's ecosystem includes species:
let result = engine
.iter()
.inspect(|generation| {
let species = generation.species().unwrap();
println!("Species count: {}", species.len());
})
.take(100)
.last()
.unwrap();