use itertools::Itertools;
use rand::prelude::ThreadRng;
use rand::seq::SliceRandom;
use rand::{thread_rng, Rng};
use std::fmt::Debug;
use std::ops::{Range, RangeInclusive};
use super::{individual::Chromosome, Individual};
pub trait PopulationGenerator<T: Chromosome> {
fn generate(&mut self, count: usize) -> Vec<Individual<T>>;
}
pub struct RandomPoints<R: Rng> {
dim: usize,
constraints: Vec<(f64, f64)>,
rng: R,
}
impl RandomPoints<ThreadRng> {
pub fn with_constraints(dim: usize, constraints: Vec<Range<f64>>) -> Self {
Self::with_constraints_and_rng(dim, constraints, thread_rng())
}
pub fn with_constraints_inclusive(dim: usize, constraints: Vec<RangeInclusive<f64>>) -> Self {
let noninclusive = constraints
.into_iter()
.map(|r| (*r.start()..*r.end()))
.collect_vec();
Self::with_constraints_and_rng(dim, noninclusive, thread_rng())
}
pub fn new(dim: usize) -> Self {
Self::with_rng(dim, thread_rng())
}
}
impl<R: Rng> RandomPoints<R> {
pub fn with_constraints_and_rng(dim: usize, constraints: Vec<Range<f64>>, rng: R) -> Self {
assert!(dim > 0, "Space dimension must be > 0");
assert_eq!(
dim,
constraints.len(),
"Number of constraints must match dimension of sampled space"
);
RandomPoints {
dim,
constraints: constraints
.into_iter()
.map(|range| (range.end - range.start, range.start))
.collect_vec(),
rng,
}
}
pub fn with_rng(dim: usize, rng: R) -> Self {
assert!(dim > 0, "Space dimension must be > 0");
RandomPoints {
dim,
constraints: Vec::<(f64, f64)>::with_capacity(0),
rng,
}
}
}
impl<R: Rng> PopulationGenerator<Vec<f64>> for RandomPoints<R> {
fn generate(&mut self, count: usize) -> Vec<Individual<Vec<f64>>> {
let distribution = rand::distributions::Uniform::from(0.0..1.0);
let mut population: Vec<Individual<Vec<f64>>> = Vec::with_capacity(count);
if self.constraints.is_empty() {
for _ in 0..count {
let mut point = Vec::<f64>::with_capacity(self.dim);
for _ in 0..self.dim {
point.push(self.rng.sample(distribution));
}
population.push(Individual::from(point));
}
} else {
for _ in 0..count {
let mut point: Vec<f64> = Vec::with_capacity(self.dim);
for restriction in &self.constraints {
point.push(restriction.0 * self.rng.sample(distribution) + restriction.1);
}
population.push(Individual::from(point));
}
}
population
}
}
pub struct BitStrings<R: Rng> {
dim: usize,
rng: R,
}
impl BitStrings<ThreadRng> {
pub fn new(dim: usize) -> Self {
Self::with_rng(dim, thread_rng())
}
}
impl<R: Rng> BitStrings<R> {
pub fn with_rng(dim: usize, rng: R) -> Self {
assert!(dim > 0, "Space dimension must be > 0");
BitStrings { dim, rng }
}
}
impl<R: Rng> PopulationGenerator<Vec<bool>> for BitStrings<R> {
fn generate(&mut self, count: usize) -> Vec<Individual<Vec<bool>>> {
let mut population: Vec<Individual<Vec<bool>>> = Vec::with_capacity(count);
let distr = rand::distributions::Standard;
for _ in 0..count {
population.push(Individual::from(
(&mut self.rng).sample_iter(distr).take(self.dim).collect_vec(),
));
}
population
}
}
pub struct RandomPermutations<GeneT: Copy, R: Rng> {
genes: Vec<GeneT>,
rng: R,
}
impl<GeneT: Copy> RandomPermutations<GeneT, ThreadRng> {
pub fn new(genes: Vec<GeneT>) -> Self {
Self::with_rng(genes, thread_rng())
}
}
impl<GeneT: Copy, R: Rng> RandomPermutations<GeneT, R> {
pub fn with_rng(genes: Vec<GeneT>, rng: R) -> Self {
RandomPermutations { genes, rng }
}
}
impl<GeneT, R> PopulationGenerator<Vec<GeneT>> for RandomPermutations<GeneT, R>
where
GeneT: Copy + Debug + Sync + Send,
R: Rng,
{
fn generate(&mut self, count: usize) -> Vec<Individual<Vec<GeneT>>> {
let mut population: Vec<Individual<Vec<GeneT>>> = Vec::with_capacity(count);
for _ in 0..count {
let mut genome = self.genes.clone();
genome.shuffle(&mut self.rng);
population.push(Individual::from(genome))
}
population
}
}
#[cfg(test)]
mod tests {
use super::{BitStrings, PopulationGenerator, RandomPoints};
use crate::ga::population::RandomPermutations;
use itertools::Itertools;
#[test]
fn points_have_appropriate_len() {
let dim = 4;
let mut gen =
RandomPoints::with_constraints(dim, vec![(0.0..2.0), (-1.0..1.0), (3.0..10.0), (-5.0..-4.0)]);
let points: Vec<crate::ga::Individual<Vec<f64>>> = gen.generate(30);
for p in points {
assert_eq!(p.chromosome.len(), dim)
}
}
#[test]
fn points_follow_explicit_constraints() {
let dim = 4;
let constraints = vec![(0.0..2.0), (-1.0..1.0), (3.0..10.0), (-5.0..-4.0)];
let mut gen = RandomPoints::with_constraints(dim, constraints.clone());
let points: Vec<crate::ga::Individual<Vec<f64>>> = gen.generate(30);
for p in points {
for (v, res) in std::iter::zip(p.chromosome_ref(), &constraints) {
assert!(res.contains(v));
}
}
}
#[test]
fn points_follow_implicit_constraints() {
let dim = 30;
let count = 100;
let mut gen = RandomPoints::new(dim);
let points: Vec<crate::ga::Individual<Vec<f64>>> = gen.generate(count);
for p in points {
for v in p.chromosome_ref() {
assert!((0.0..1.0).contains(v));
}
}
}
#[test]
fn bistrings_have_appropriate_len() {
let dim = 30;
let mut gen = BitStrings::new(dim);
let points: Vec<crate::ga::Individual<Vec<bool>>> = gen.generate(30);
for p in points {
assert_eq!(p.chromosome_ref().len(), dim)
}
}
#[test]
fn permutations_have_appropriate_len() {
let dim = 30;
let mut gen = RandomPermutations::new((0..dim).collect_vec());
let points = gen.generate(30);
for p in points {
assert_eq!(p.chromosome_ref().len(), dim)
}
}
#[test]
fn permutations_have_every_gene() {
let dim: usize = 30;
let mut gen = RandomPermutations::new((1..=dim).collect_vec());
let points = gen.generate(10);
for p in points {
let sum: usize = p.chromosome_ref().iter().sum();
assert_eq!(sum, ((dim + 1) * dim) / 2)
}
}
}