moonlander_gp/
random_pop.rsuse std::cmp::max;
use super::{AstNode, Population, Fitness, Mutatable, Number};
use rand::Rng;
use rustc_serialize::Encodable;
pub trait RandNode: Sized {
fn rand(weights: NodeWeights, rng: &mut Rng) -> Self;
}
impl <T: RandNode+AstNode> Mutatable for T {
fn mutate(&self, max_height: i32, rng: &mut Rng) -> Box<AstNode> {
Box::new(T::rand(NodeWeights::fixed(max_height), rng))
}
}
pub fn random_population<P: RandNode+Clone+Sync, F: Fitness+Sized+Send, R: Rng>(n: usize, max_depth: usize, rng: &mut R) -> Population<P, F> {
let mut ret = Population::new(n, 0);
for i in 0..n {
let height = 1 + i / (n / max_depth);
ret.add(P::rand(NodeWeights::fixed(height as i32), rng));
}
ret
}
pub fn retain_best<P, F, R>(frac: Number, pop: Population<P, F>, max_depth: usize, rng: &mut R) -> Population<P, F>
where P: RandNode+Clone+Sync+Encodable,
F: Fitness+Sized+Send+Encodable,
R: Rng
{
let n = (pop.n() as Number * frac) as usize;
let filler = pop.n() - n;
let mut ret = Population::new(n, 0);
ret.generation = pop.generation + 1;
for c in pop.best_n(n) {
ret.add(c);
}
for i in 0..filler {
let height = 1 + i / (filler / max_depth);
ret.add(P::rand(NodeWeights::fixed(height as i32), rng));
}
ret
}
#[derive(Copy,Clone)]
pub struct NodeWeights {
current_level: i32,
per_level: i32
}
impl NodeWeights {
pub fn fixed(target_height: i32) -> NodeWeights {
NodeWeights {
current_level: 0,
per_level: 100 / max(target_height - 1, 1)
}
}
pub fn randomized(max_height: i32, rng: &mut Rng) -> NodeWeights {
NodeWeights::fixed(1 + rng.next_u32() as i32 % (max_height - 1))
}
pub fn internal(&self) -> u32 {
max(100 - self.per_level * self.current_level, MIN_WEIGHT) as u32
}
pub fn leaf(&self) -> u32 {
max(self.per_level * self.current_level, MIN_WEIGHT) as u32
}
pub fn next_level(&self) -> NodeWeights {
NodeWeights { current_level: self.current_level + 1, per_level: self.per_level }
}
pub fn gen_child<P: RandNode>(&self, rng: &mut Rng) -> Box<P> {
Box::new(P::rand(self.next_level(), rng))
}
}
const MIN_WEIGHT : i32 = 1;
#[cfg(test)]
mod tests {
use super::*;
use super::super::{num, Number, depth};
use super::super::genetic::mutate_tree;
#[derive(Clone)]
enum List {
Cons(Box<List>),
Nil
}
impl_astnode!(List, 0,
int Cons(next),
leaf Nil());
#[test]
fn test_node_heights_on_generation() {
let target_height = 8;
let n = 1000;
let mut rng = ::rand::StdRng::new().unwrap();
let weights = NodeWeights::fixed(target_height);
let programs : Vec<List> = (0..n).map(|_| RandNode::rand(weights, &mut rng)).collect();
let avg_height = num::sum(programs.iter().map(|p| depth(p) as Number)) / n as Number;
assert!(avg_height <= 0.6 * target_height as Number);
}
#[test]
fn test_node_heights_during_mutation() {
let n = 1000;
let target_height = 8;
let mut rng = ::rand::StdRng::new().unwrap();
let weights = NodeWeights::fixed(target_height);
let mut program : List = RandNode::rand(weights, &mut rng);
for _ in 0..n {
program = *mutate_tree(&program, target_height, &mut rng);
let depth = depth(&program);
println!("Depth: {}", depth);
assert!((depth as Number) < target_height as Number * 1.5);
}
}
}