use burn::backend::Flex;
use rlevo_core::bounds::Bounds;
use rlevo_core::fitness::Landscape;
use rlevo_core::objective::ObjectiveSense;
use rlevo_core::rate::NonNegativeRate;
use rlevo_evolution::algorithms::de::{DeConfig, DeVariant, DifferentialEvolution};
use rlevo_evolution::algorithms::ep::{EpConfig, EvolutionaryProgramming};
use rlevo_evolution::algorithms::es_classical::{EsConfig, EsKind, EvolutionStrategy};
use rlevo_evolution::algorithms::ga::{
GaConfig, GaCrossover, GaReplacement, GaSelection, GeneticAlgorithm,
};
use rlevo_evolution::fitness::{BatchFitnessFn, FromLandscape};
use rlevo_evolution::strategy::{EvolutionaryHarness, Strategy};
type B = Flex;
pub const GENS: usize = 500;
pub const SEED: u64 = 42;
fn run<S, F>(label: &str, strategy: S, params: S::Params, fitness_fn: F)
where
S: Strategy<B>,
S::Params: std::fmt::Debug + rlevo_core::config::Validate,
F: BatchFitnessFn<B, S::Genome>,
B: burn::tensor::backend::Backend,
<B as burn::tensor::backend::BackendTypes>::Device: Clone + Default,
{
let device = Default::default();
let mut harness =
EvolutionaryHarness::<B, S, F>::new(strategy, params, fitness_fn, SEED, device, GENS)
.expect("valid params");
harness.reset();
loop {
if harness.step(()).done {
break;
}
}
let m = harness
.latest_metrics()
.expect("at least one generation ran");
println!(
"{label:>30} | gens={:>4} | best={:>.6e} | mean={:>.6e}",
m.generation(),
m.best_fitness_ever(),
m.mean_fitness(),
);
}
pub fn showcase<L>(title: &str, dim: usize, bounds: (f64, f64), mutation_sigma: f32, landscape: L)
where
L: Landscape + Copy,
{
#[allow(clippy::cast_possible_truncation)]
let bounds: Bounds = Bounds::new(bounds.0 as f32, bounds.1 as f32);
println!(
"{title}-D{dim} showcase — {GENS} generations, Flex backend, seed={SEED}\n{:-<80}",
"",
);
run(
"GA/real/tournament-blx-elite",
GeneticAlgorithm::<B>::new(),
GaConfig {
pop_size: 64,
genome_dim: dim,
bounds,
mutation_sigma: NonNegativeRate::new(mutation_sigma),
selection: GaSelection::Tournament { size: 2 },
crossover: GaCrossover::BlxAlpha {
alpha: NonNegativeRate::new(0.5),
},
replacement: GaReplacement::Elitist { elitism_k: 2 },
},
FromLandscape::with_sense(landscape, ObjectiveSense::Minimize),
);
for kind in [
EsKind::OnePlusOne,
EsKind::OnePlusLambda { lambda: 8 },
EsKind::MuPlusLambda { mu: 5, lambda: 20 },
EsKind::MuCommaLambda { mu: 5, lambda: 20 },
] {
let mut params = EsConfig::default_for(kind, dim);
params.bounds = bounds;
let label = format!("ES/{kind:?}");
run(
&label,
EvolutionStrategy::<B>::new(),
params,
FromLandscape::with_sense(landscape, ObjectiveSense::Minimize),
);
}
let mut ep_params = EpConfig::default_for(20, dim);
ep_params.bounds = bounds;
run(
"EP/fogel/μ=20/q=10",
EvolutionaryProgramming::<B>::new(),
ep_params,
FromLandscape::with_sense(landscape, ObjectiveSense::Minimize),
);
for variant in [
DeVariant::Rand1Bin,
DeVariant::Best1Bin,
DeVariant::CurrentToBest1Bin,
DeVariant::Rand2Bin,
DeVariant::Rand1Exp,
] {
let mut params = DeConfig::default_for(30, dim);
params.variant = variant;
params.bounds = bounds;
let label = format!("DE/{variant:?}");
run(
&label,
DifferentialEvolution::<B>::new(),
params,
FromLandscape::with_sense(landscape, ObjectiveSense::Minimize),
);
}
}