pub mod connection;
pub mod recurrent;
pub use connection::WConnection;
pub use recurrent::Recurrent;
use crate::random::{percent, ConnectionEvent, EventKind, GenomeEvent};
use core::{cmp::Ordering, error::Error, fmt::Debug, hash::Hash, ops::Range};
use fxhash::FxHashMap;
use rand::{Rng, RngCore};
pub struct InnoGen {
pub head: usize,
seen: FxHashMap<(usize, usize), usize>,
}
impl InnoGen {
pub fn new(head: usize) -> Self {
Self {
head,
seen: FxHashMap::default(),
}
}
pub fn path(&mut self, v: (usize, usize)) -> usize {
match self.seen.get(&v) {
Some(n) => *n,
None => {
let n = self.head;
self.head += 1;
self.seen.insert(v, n);
n
}
}
}
}
pub trait Connection: Clone + Hash + PartialEq + Default + Debug {
const PROBABILITIES: [u64; ConnectionEvent::COUNT] = [percent(1), percent(99)];
const PARAM_REPLACE_PROBABILITY: u64 = percent(20);
const PARAM_PERTURB_FAC: f64 = 0.45;
const PARAM_STD: f64 = 3.;
const EXCESS_COEFFICIENT: f64;
const DISJOINT_COEFFICIENT: f64;
const PARAM_COEFFICIENT: f64;
const PROBABILITY_PICK_RL: u64 = percent(50);
const PROBABILITY_KEEP_DISABLED: u64 = percent(75);
fn new(from: usize, to: usize, inno: &mut InnoGen) -> Self;
fn inno(&self) -> usize;
fn enabled(&self) -> bool;
fn enable(&mut self);
fn disable(&mut self);
fn path(&self) -> (usize, usize);
fn from(&self) -> usize {
self.path().0
}
fn to(&self) -> usize {
self.path().1
}
fn weight(&self) -> f64;
fn param_diff(&self, other: &Self) -> f64;
fn mutate_param(&mut self, rng: &mut impl RngCore);
fn mutate(&mut self, rng: &mut impl RngCore) {
if let Some(evt) = ConnectionEvent::pick(rng, Self::PROBABILITIES) {
match evt {
ConnectionEvent::Disable => self.disable(),
ConnectionEvent::MutateParam => self.mutate_param(rng),
}
}
}
fn bisect(&mut self, center: usize, inno: &mut InnoGen) -> (Self, Self);
}
pub trait Genome<C: Connection>: Clone {
const MUTATE_NODE_PROBABILITY: u64 = percent(20);
const MUTATE_CONNECTION_PROBABILITY: u64 = percent(20);
const PROBABILITIES: [u64; GenomeEvent::COUNT] =
[percent(5), percent(5), percent(90), percent(0)];
fn new(sensory: usize, action: usize) -> (Self, usize);
fn sensory(&self) -> Range<usize>;
fn action(&self) -> Range<usize>;
fn node_count(&self) -> usize;
fn push_node(&mut self);
fn connections(&self) -> &[C];
fn connections_mut(&mut self) -> &mut [C];
fn push_connection(&mut self, connection: C);
fn push_2_connections(&mut self, first: C, second: C) {
self.push_connection(first);
self.push_connection(second);
}
fn mutate_connection(&mut self, rng: &mut impl RngCore) {
for c in self.connections_mut() {
if rng.next_u64() < Self::MUTATE_CONNECTION_PROBABILITY {
c.mutate(rng);
}
}
}
fn open_path(&self, rng: &mut impl RngCore) -> Option<(usize, usize)>;
fn new_connection(
&mut self,
rng: &mut impl RngCore,
inno: &mut InnoGen,
) -> Result<(), Box<dyn Error>> {
if let Some((from, to)) = self.open_path(rng) {
self.push_connection(C::new(from, to, inno));
Ok(())
} else {
Err("connections on genome are fully saturated".into())
}
}
fn bisect_connection(
&mut self,
rng: &mut impl RngCore,
inno: &mut InnoGen,
) -> Result<(), Box<dyn Error>> {
if self.connections().is_empty() {
return Err("no connections available to bisect".into());
}
let center = self.node_count();
let source = rng.random_range(0..self.connections().len());
let (lower, upper) = self
.connections_mut()
.get_mut(source)
.unwrap()
.bisect(center, inno);
self.push_node();
self.push_2_connections(lower, upper);
Ok(())
}
fn mutate(
&mut self,
rng: &mut impl RngCore,
innogen: &mut InnoGen,
) -> Result<(), Box<dyn Error>> {
if self.connections().is_empty() {
self.new_connection(rng, innogen)?;
} else if let Some(evt) = GenomeEvent::pick(rng, Self::PROBABILITIES) {
match evt {
GenomeEvent::NewConnection => self.new_connection(rng, innogen)?,
GenomeEvent::BisectConnection => self.bisect_connection(rng, innogen)?,
GenomeEvent::MutateConnection => self.mutate_connection(rng),
GenomeEvent::MutateNode => unreachable!("nodes may not be mutated"),
};
}
Ok(())
}
fn reproduce_with(&self, other: &Self, fitness_cmp: Ordering, rng: &mut impl RngCore) -> Self;
}