use std::{
cmp::Ordering,
ops::{Deref, DerefMut},
};
use getset::CopyGetters;
use nalgebra::{
allocator::Allocator, convert, storage::StorageMut, ComplexField, DefaultAllocator, Dim,
DimName, OVector, Vector, U1,
};
use num_traits::Zero;
use rand::Rng;
use rand_distr::{uniform::SampleUniform, Distribution, Uniform};
use crate::core::{Domain, Function, Problem, ProblemError};
#[allow(clippy::len_without_is_empty)]
pub struct Population<F: Problem>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
individuals: Vec<OVector<F::Scalar, F::Dim>>,
errors: Vec<F::Scalar>,
sorted: Vec<usize>,
fx: OVector<F::Scalar, F::Dim>,
sorted_dirty: bool,
}
impl<F: Problem> Population<F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
pub fn new<R: Rng + ?Sized, I: PopulationInit<F>>(
f: &F,
dom: &Domain<F::Scalar>,
rng: &mut R,
initializer: &I,
size: PopulationSize,
) -> Self
where
F: Function,
{
let size = size.get(f);
let mut individuals = vec![OVector::zeros_generic(f.dim(), U1::name()); size];
initializer.init_all(f, dom, rng, individuals.iter_mut());
let errors = vec![F::Scalar::zero(); size];
let sorted = (0..size).collect();
let fx = OVector::zeros_generic(f.dim(), U1::name());
let mut this = Self {
individuals,
errors,
sorted,
fx,
sorted_dirty: true,
};
this.eval(f);
this.sort();
this
}
pub fn zeros(f: &F, size: PopulationSize) -> Self {
let size = size.get(f);
let individuals = vec![OVector::zeros_generic(f.dim(), U1::name()); size];
let errors = vec![F::Scalar::zero(); size];
let sorted = (0..size).collect();
let fx = OVector::zeros_generic(f.dim(), U1::name());
Self {
individuals,
errors,
sorted,
fx,
sorted_dirty: false,
}
}
pub fn reinit<R: Rng + ?Sized, I: PopulationInit<F>>(
&mut self,
f: &F,
dom: &Domain<F::Scalar>,
rng: &mut R,
initializer: &I,
) where
F: Function,
{
initializer.init_all(f, dom, rng, self.individuals.iter_mut());
self.eval(f);
self.sort();
}
pub fn len(&self) -> usize {
self.individuals.len()
}
pub fn iter_sorted(&self) -> IterSorted<'_, F> {
debug_assert!(
!self.sorted_dirty,
"population is supposedly not sorted - this is a bug in the solving algorithm used"
);
IterSorted {
individuals: &self.individuals,
errors: &self.errors,
sorted: self.sorted.iter(),
}
}
pub fn iter(&self) -> Iter<'_, F> {
Iter {
inner: self.individuals.iter().zip(self.errors.iter()),
}
}
pub fn iter_mut(&mut self) -> IterMut<'_, F> {
self.sorted_dirty = true;
IterMut {
inner: self.individuals.iter_mut().zip(self.errors.iter_mut()),
}
}
pub fn get(&self, index: usize) -> Option<Individual<'_, F>> {
match (self.individuals.get(index), self.errors.get(index)) {
(Some(x), Some(&error)) => Some(Individual { x, error }),
_ => None,
}
}
pub fn get_mut(&mut self, index: usize) -> Option<IndividualMut<'_, F>> {
self.sorted_dirty = true;
match (self.individuals.get_mut(index), self.errors.get_mut(index)) {
(Some(x), Some(error)) => Some(IndividualMut {
x,
error,
dirty: false,
}),
_ => None,
}
}
pub fn eval(&mut self, f: &F)
where
F: Function,
{
for (x, error) in self.individuals.iter().zip(self.errors.iter_mut()) {
*error = f
.apply_eval(x, &mut self.fx)
.unwrap_or_else(|_| convert(f64::INFINITY));
}
}
pub fn sort(&mut self) {
let errors = &mut self.errors;
self.sorted.sort_unstable_by(|lhs, rhs| {
let lhs = errors[*lhs];
let rhs = errors[*rhs];
if lhs.is_finite() && rhs.is_finite() {
lhs.partial_cmp(&rhs).unwrap()
} else if lhs.is_finite() {
Ordering::Less
} else if rhs.is_finite() {
Ordering::Greater
} else {
Ordering::Equal
}
});
self.sorted_dirty = false;
}
pub fn report(&self) -> PopulationReport<F> {
PopulationReport::new(self)
}
}
pub struct Individual<'a, F: Problem>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
x: &'a OVector<F::Scalar, F::Dim>,
error: F::Scalar,
}
impl<'a, F: Problem> Individual<'a, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
pub fn error(&self) -> F::Scalar {
self.error
}
}
impl<F: Problem> Deref for Individual<'_, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
type Target = OVector<F::Scalar, F::Dim>;
fn deref(&self) -> &Self::Target {
self.x
}
}
pub struct Iter<'a, F: Problem>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
inner: std::iter::Zip<
std::slice::Iter<'a, OVector<F::Scalar, F::Dim>>,
std::slice::Iter<'a, F::Scalar>,
>,
}
impl<'a, F: Problem> Iterator for Iter<'a, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
type Item = Individual<'a, F>;
fn next(&mut self) -> Option<Self::Item> {
let (x, error) = self.inner.next()?;
Some(Individual { x, error: *error })
}
}
pub struct IterSorted<'a, F: Problem>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
individuals: &'a [OVector<F::Scalar, F::Dim>],
errors: &'a [F::Scalar],
sorted: std::slice::Iter<'a, usize>,
}
impl<'a, F: Problem> Iterator for IterSorted<'a, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
type Item = Individual<'a, F>;
fn next(&mut self) -> Option<Self::Item> {
let index = *self.sorted.next()?;
Some(Individual {
x: &self.individuals[index],
error: self.errors[index],
})
}
}
pub struct IndividualMut<'a, F: Problem>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
x: &'a mut OVector<F::Scalar, F::Dim>,
error: &'a mut F::Scalar,
dirty: bool,
}
impl<'a, F: Problem> IndividualMut<'a, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
pub fn error(&self) -> F::Scalar {
*self.error
}
pub fn set_error(&mut self, error: F::Scalar) {
*self.error = error;
self.dirty = false;
}
pub fn eval<Sfx>(
&self,
f: &F,
fx: &mut Vector<F::Scalar, F::Dim, Sfx>,
) -> Result<F::Scalar, ProblemError>
where
Sfx: StorageMut<F::Scalar, F::Dim>,
F: Function,
{
f.apply_eval(self.x, fx)
}
pub fn clamp(&mut self, dom: &Domain<F::Scalar>) {
self.x
.iter_mut()
.zip(dom.vars().iter())
.for_each(|(xi, vi)| *xi = vi.clamp(*xi));
self.dirty = true;
}
}
impl<F: Problem> Deref for IndividualMut<'_, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
type Target = OVector<F::Scalar, F::Dim>;
fn deref(&self) -> &Self::Target {
self.x
}
}
impl<F: Problem> DerefMut for IndividualMut<'_, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.dirty = true;
self.x
}
}
impl<F: Problem> Drop for IndividualMut<'_, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
fn drop(&mut self) {
debug_assert!(!self.dirty, "individual has supposedly obsolete error - this is a bug in the solving algorithm used");
}
}
pub struct IterMut<'a, F: Problem>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
inner: std::iter::Zip<
std::slice::IterMut<'a, OVector<F::Scalar, F::Dim>>,
std::slice::IterMut<'a, F::Scalar>,
>,
}
impl<'a, F: Problem> Iterator for IterMut<'a, F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
type Item = IndividualMut<'a, F>;
fn next(&mut self) -> Option<Self::Item> {
let (x, error) = self.inner.next()?;
Some(IndividualMut {
x,
error,
dirty: false,
})
}
}
#[derive(Debug, Clone, CopyGetters)]
#[get_copy = "pub"]
pub struct PopulationReport<F: Problem> {
best: F::Scalar,
avg: F::Scalar,
valid: usize,
invalid: usize,
}
impl<F: Problem> PopulationReport<F>
where
DefaultAllocator: Allocator<F::Scalar, F::Dim>,
{
fn new(population: &Population<F>) -> Self {
let mut best = convert(f64::INFINITY);
let mut sum = F::Scalar::zero();
let mut valid = 0;
let mut invalid = 0;
for error in population.errors.iter().copied() {
if error < best {
best = error;
}
if error.is_finite() {
sum += error;
valid += 1;
} else {
invalid += 1;
}
}
Self {
best,
avg: sum / convert(valid as f64),
valid,
invalid,
}
}
}
pub trait PopulationInit<F: Problem> {
fn init<R: Rng + ?Sized, S>(
&self,
f: &F,
dom: &Domain<F::Scalar>,
rng: &mut R,
x: &mut Vector<F::Scalar, F::Dim, S>,
) where
S: StorageMut<F::Scalar, F::Dim>;
fn init_all<'pop, R: Rng + ?Sized, I, S>(
&self,
f: &F,
dom: &Domain<F::Scalar>,
rng: &mut R,
population: I,
) where
I: Iterator<Item = &'pop mut Vector<F::Scalar, F::Dim, S>>,
S: StorageMut<F::Scalar, F::Dim> + 'pop,
{
for x in population {
self.init(f, dom, rng, x);
}
}
}
pub struct UniformInit<F: Problem> {
factor: F::Scalar,
}
impl<F: Problem> UniformInit<F> {
pub fn with_factor(factor: F::Scalar) -> Self {
Self { factor }
}
pub fn new() -> Self {
Self::with_factor(convert(10.0))
}
pub fn factor(&self) -> F::Scalar {
self.factor
}
}
impl<F: Problem> Default for UniformInit<F> {
fn default() -> Self {
Self::new()
}
}
impl<F: Problem> PopulationInit<F> for UniformInit<F>
where
F::Scalar: SampleUniform,
{
fn init<R: Rng + ?Sized, S>(
&self,
_f: &F,
dom: &Domain<F::Scalar>,
rng: &mut R,
x: &mut Vector<F::Scalar, F::Dim, S>,
) where
S: StorageMut<F::Scalar, F::Dim>,
{
x.iter_mut().zip(dom.vars().iter()).for_each(|(xi, vi)| {
let lower = if vi.lower().is_finite() {
vi.lower()
} else {
-vi.magnitude() * self.factor
};
let upper = if vi.upper().is_finite() {
vi.upper()
} else {
vi.magnitude() * self.factor
};
*xi = Uniform::new_inclusive(lower, upper).sample(rng)
});
}
}
#[derive(Debug, Clone, Copy)]
pub enum PopulationSize {
Fixed(usize),
Adaptive,
}
impl PopulationSize {
pub fn get<F: Problem>(&self, f: &F) -> usize {
let size = match self {
PopulationSize::Fixed(size) => *size,
PopulationSize::Adaptive => {
let size = 10.0 + 5.0 * (f.dim().value() as f64).sqrt();
let size = (size / 5.0).ceil() * 5.0;
size as usize
}
};
size.max(2)
}
}