use crate::function::Function;
use crate::solution::Solution;
use genetic_algorithm_traits::{Individual, Population};
use rand::distributions::uniform::SampleRange;
use std::fmt;
use crossbeam_utils::thread;
use std::collections::HashSet;
use std::convert::From;
use std::time::Instant;
#[derive(Debug, Clone, PartialEq)]
pub struct Solutions {
solutions: HashSet<Solution>,
}
impl From<Vec<Solution>> for Solutions {
fn from(solution: Vec<Solution>) -> Self {
Solutions {
solutions: solution.into_iter().collect(),
}
}
}
impl fmt::Display for Solutions {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"Solutions([\n\t{}\n])",
self.solutions
.iter()
.map(|solution| solution.to_string())
.collect::<Vec<String>>()
.join(",\n\t")
)
}
}
impl Solutions {
pub fn random<R>(n_solutions: usize, range: R, length: usize) -> Self
where
R: SampleRange<f64> + Clone,
{
let mut routes = HashSet::new();
while routes.len() < n_solutions {
routes.insert(Solution::random(range.clone(), length));
}
Solutions { solutions: routes }
}
}
impl<'a> Population<'a> for Solutions {
type Individual = Solution;
type IndividualCollection = std::collections::hash_set::Iter<'a, Solution>;
fn get_fittest_population(&self, n: usize, function: &Function) -> Solutions {
Solutions::from(self.get_n_fittest(n, function))
}
fn evolve(&self, mutate_prob: f32) -> Solutions {
Solutions {
solutions: HashSet::from_iter(self.evolve_individuals(mutate_prob).into_iter()),
}
}
fn iter(&'a self) -> std::collections::hash_set::Iter<Solution> {
self.solutions.iter()
}
}
pub fn evolve_population(
initial_population: Solutions,
n_generations: usize,
size_generation: usize,
function: &Function,
n_jobs: usize,
) -> Solutions {
if n_jobs == 0 {
(0..n_generations).fold(initial_population, |pop, _| {
pop.evolve(0.5)
.get_fittest_population(size_generation, function)
})
} else {
thread::scope(|s| {
let mut result = Vec::new();
for _ in 0..n_jobs {
let this_population = initial_population.clone();
result.push(s.spawn(move |_| -> Vec<Solution> {
(0..((n_generations / n_jobs) + 1))
.fold(this_population, |pop, _| {
pop.evolve(0.5)
.get_fittest_population(size_generation, function)
})
.get_n_fittest(size_generation, function)
}))
}
Solutions::from(
result
.into_iter()
.map(|thread| thread.join().unwrap())
.flatten()
.collect::<Vec<Solution>>(),
)
})
.unwrap()
}
}
pub fn benchmark_population<R>(
n_generations: usize,
size_generation: usize,
function: &Function,
n_jobs: usize,
sample_range: R,
) -> (u64, f64)
where
R: SampleRange<f64> + Clone,
{
let before = Instant::now();
let final_population = evolve_population(
Solutions::random(size_generation, sample_range, 3),
n_generations,
size_generation,
function,
n_jobs,
);
let duration = before.elapsed();
let nanos = duration.subsec_nanos() as u64;
(
(1000 * 1000 * 1000 * duration.as_secs() + nanos) / (1000 * 1000),
final_population.get_n_fittest(1, function)[0].fitness(function),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::solution;
#[test]
fn test_formatter() {
assert_eq!(
format!(
"{}",
Solutions::from(vec![solution::Solution::new(vec![1.1, 2.2, 3.3]),])
),
"Solutions([\n\tSolution([1.1, 2.2, 3.3])\n])"
)
}
}