use fugue_evo::prelude::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
#[derive(Clone)]
struct Quadratic {
center: f64,
}
impl Fitness for Quadratic {
type Genome = RealVector;
type Value = f64;
fn evaluate(&self, genome: &RealVector) -> f64 {
-0.5 * genome
.genes()
.iter()
.map(|x| (x - self.center).powi(2))
.sum::<f64>()
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Evolution as Bayesian Inference ===\n");
let mut rng = StdRng::seed_from_u64(20260710);
const DIM: usize = 2;
let center = 3.0;
let sigma0 = 2.0;
let fitness = Quadratic { center };
let model = EvolutionModel::new(GaussianPrior::new(0.0, sigma0, DIM), fitness.clone());
println!("-- Tempered SMC (fugue adaptive_smc_with_kernel) --");
let result = EvolutionSMC::run(
&mut rng,
&model,
EvoSmcConfig {
num_particles: 3000,
rejuvenation_steps: 5,
crossover: Some(CrossoverConfig {
n_pairs: 500,
swap_probability: 0.5,
}),
..Default::default()
},
);
let tau = 1.0 / (sigma0 * sigma0) + 1.0;
let analytic_mean = center / tau;
let analytic_var = 1.0 / tau;
for coord in 0..DIM {
let mean = result.weighted_mean(coord);
let var = result.weighted_variance(coord);
println!(
" coord {coord}: posterior mean {mean:7.4} (analytic {analytic_mean:7.4}) \
variance {var:6.4} (analytic {analytic_var:6.4})"
);
}
println!(
" log evidence: {:.4} (Bayesian model score, free from the tempering ladder)",
result.log_evidence
);
let model_fn = model.smc_model();
if let Some((best, best_f)) = result.best(&fitness, &model_fn) {
println!(
" best genome (decode-replay): {:?} with fitness {best_f:.4}\n",
best.genes()
);
}
println!("-- MH chain (fugue adaptive_single_site_mh) --");
let chain_model = EvolutionModel::new(GaussianPrior::new(0.0, sigma0, 1), Quadratic { center })
.with_beta(1.0);
let mut chain = EvolutionChain::new(chain_model);
let mut current = chain.init(&mut rng);
let mut samples = Vec::new();
for i in 0..20_000 {
let (g, t) = chain.step(&mut rng, ¤t);
current = t;
if i >= 2_000 {
samples.push(g.genes()[0]);
}
}
let mh_mean = samples.iter().sum::<f64>() / samples.len() as f64;
println!(" MH posterior mean {mh_mean:7.4} (analytic {analytic_mean:7.4})\n");
println!("-- Bayesian adaptive GA (conjugate posteriors + Thompson sampling) --");
let sphere = Sphere::new(4);
let bounds = MultiBounds::symmetric(5.12, 4);
let mut ga = BayesianAdaptiveGA::new(UniformBoxPrior::new(bounds), sphere, 60, 80);
let result = ga.run(&mut rng);
println!(" best fitness: {:.6}", result.best_fitness);
for (i, arm) in result.operator_posteriors.iter().enumerate() {
println!(
" arm {i}: σ = {:5.2} posterior mean success = {:.3} (selected {}×)",
arm.sigma,
arm.posterior.mean(),
arm.times_selected
);
}
println!(
" improvement-rate posterior mean: {:.2} events/generation",
result.improvement_rate.mean()
);
Ok(())
}