#![doc = include_str!("../README.md")]
#![feature(random)]
#![feature(mpmc_channel)]
use std::{
fmt,
random::{Random, RandomSource},
sync::{Arc, Condvar, Mutex},
thread::available_parallelism,
};
#[allow(unused_imports)]
use crate::{
continuous::ContinuousTrainer,
inertial::{InertialGenome, InertialTrainer},
stochastic::StochasticTrainer,
};
pub mod continuous;
pub mod inertial;
pub mod stochastic;
pub trait Genome {
fn generate<R>(rng: &mut R) -> Self
where
R: RandomSource;
fn mutate<R>(&mut self, mutation_rate: f32, rng: &mut R)
where
R: RandomSource;
fn crossbreed<R>(&self, other: &Self, rng: &mut R) -> Self
where
R: RandomSource;
fn fitness(&self) -> f32;
}
#[derive(Clone, Copy, Debug)]
pub struct PopulationStats {
pub max_fitness: f32,
pub min_fitness: f32,
pub mean_fitness: f32,
pub median_fitness: f32,
}
impl FromIterator<f32> for PopulationStats {
fn from_iter<T: IntoIterator<Item = f32>>(iter: T) -> Self {
let mut scores: Vec<_> = iter.into_iter().collect();
scores.sort_by(|a, b| a.total_cmp(b));
let &min_fitness = scores.first().unwrap();
let &max_fitness = scores.last().unwrap();
let mean_fitness = scores.iter().sum::<f32>() / scores.len() as f32;
let median_fitness = scores[scores.len() / 2];
PopulationStats {
min_fitness,
max_fitness,
mean_fitness,
median_fitness,
}
}
}
impl fmt::Display for PopulationStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"min={:.4} max={:.4} mean={:.4} median={:.4}",
self.min_fitness, self.max_fitness, self.mean_fitness, self.median_fitness
)
}
}
pub struct TrainingReportStrategy<F, C> {
pub should_report: F,
pub report_callback: C,
}
fn random_f32<R>(rng: &mut R) -> f32
where
R: RandomSource,
{
u32::random(rng) as f32 / u32::MAX as f32
}
fn num_cpus() -> usize {
available_parallelism().map(usize::from).unwrap_or(1)
}
fn random_choice_weighted<'a, T, R>(weights: &'a [(T, f32)], rng: &mut R) -> &'a T
where
R: RandomSource,
{
random_choice_weighted_mapped(weights, rng, |x| x)
}
fn random_choice_weighted_mapped<'a, T, R>(
weights: &'a [(T, f32)],
rng: &mut R,
weight_map: impl Fn(f32) -> f32,
) -> &'a T
where
R: RandomSource,
{
let total: f32 = weights.iter().map(|x| (weight_map)(x.1)).sum();
let mut n = random_f32(rng) * total;
for (value, weight) in weights {
let weight = (weight_map)(*weight);
if n <= weight {
return value;
}
n -= weight;
}
&weights.last().unwrap().0
}
fn random_choice_weighted_mapped_by_key<'a, T, R>(
elems: &'a [T],
rng: &mut R,
weight_map: impl Fn(f32) -> f32,
key: impl Fn(&T) -> f32,
) -> &'a T
where
R: RandomSource,
{
let total: f32 = elems.iter().map(|x| (weight_map)(key(x))).sum();
let mut n = random_f32(rng) * total;
for value in elems {
let weight = (weight_map)(key(value));
if n <= weight {
return value;
}
n -= weight;
}
&elems.last().unwrap()
}
#[derive(Clone)]
struct Gate<S>(Arc<(Mutex<S>, Condvar)>);
impl<S> Gate<S> {
fn new(initial_state: S) -> Self {
Self(Arc::new((Mutex::new(initial_state), Condvar::new())))
}
fn update(&self, updater: impl Fn(&mut S)) {
let mut state = self.0.0.lock().unwrap();
(updater)(&mut state);
self.0.1.notify_all();
}
fn wait_while(&self, condition: impl Fn(&S) -> bool) {
let mut state = self.0.0.lock().unwrap();
while (condition)(&state) {
state = self.0.1.wait(state).unwrap();
}
}
}
fn bounds(iter: impl Iterator<Item = f32>) -> Option<(f32, f32)> {
let mut minmax = None;
for score in iter {
minmax = match (minmax, score) {
(None, score) => Some((score, score)),
(Some((min, max)), score) if score < min => Some((score, max)),
(Some((min, max)), score) if score > max => Some((min, score)),
(minmax, _) => minmax,
};
}
minmax
}