Skip to content

Example

Let's continue with our example from the previous section - 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 use the same codec and fitness_function as before, but now we'll incorporate a selector to choose individuals for the next generation.

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


# Use Boltzmann selection for offspring - individuals which
# will be used to create new individuals through mutation and crossover
offspring_selector = rd.Select.boltzmann(temp=4)

# Use tournament selection for survivors - individuals which will
# be passed down unchanged to the next generation
survivor_selector = rd.Select.tournament(k=3)

# Define the offspring fraction. This is the % of the population that
# will be created through mutation and crossover (offspring) vs passed down unchanged (survivors).
# The default is 80% offspring and 20% survivors, but here we'll use 50% for both.
fraction = 0.5

# Create the engine that will evolve a population of genomes with 2 genes in 1 chromosome, a
# fitness function, and selectors.
engine = (
    rd.Engine.float(2, init_range=(-1.0, 1.0), bounds=(-10.0, 10.0), dtype=rd.Float32)
    .fitness(fit)
    .select(offspring=offspring_selector, survivor=survivor_selector, frac=fraction)
    .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);

// Use Boltzmann selection for offspring - individuals which
// will be used to create new individuals through mutation and crossover
let offspring_selector = BoltzmannSelector::new(4.0);

// Use tournament selection for survivors - individuals which will
// be passed down unchanged to the next generation
let survivor_selector = TournamentSelector::new(3);

let engine = GeneticEngine::builder()
    .codec(codec)
    .offspring_selector(offspring_selector)
    .survivor_selector(survivor_selector)
    .fitness_fn(fit)
    // ... other parameters ...
    .build();

// Run the engine: stop after 1000 generations
// `.run()` is equal to `.last().unwrap()`
let result = engine.iter().take(1000).run();