M env/src/binary_string.rs => env/src/binary_string.rs +54 -52
@@ 1,34 1,15 @@
use std::str::FromStr;
-use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector};
+use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, DimName, Dyn, OVector, U1};
use rand::{Rng, RngCore};
use thiserror::Error;
#[derive(Debug, Clone, PartialEq)]
-pub struct Bounds {
- min: f64,
- max: f64,
-}
-
-impl Bounds {
- pub fn new(min: f64, max: f64) -> Self {
- Bounds {
- min,
- max
- }
- }
-
- pub fn min(&self) -> f64 {
- self.min
- }
-
- pub fn max(&self) -> f64 {
- self.max
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct BinaryString {
- pub vec: Vec<i8>
+pub struct BinaryString<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D>
+{
+ pub vec: OVector<i8, D>
}
#[derive(Error, Debug, Clone, PartialEq)]
@@ 37,24 18,40 @@ pub enum BinaryStringConversionError {
DimensionMismatch,
}
-impl BinaryString {
- pub fn new(vec: Vec<i8>) -> BinaryString {
+impl<D> BinaryString<D>
+where
+ D: DimName,
+ DefaultAllocator: Allocator<D>
+{
+ pub fn new(vec: Vec<i8>) -> Self {
+ Self {
+ vec: OVector::from_vec(vec)
+ }
+ }
+}
+
+impl BinaryString<Dyn>
+{
+ pub fn new_dyn(vec: Vec<i8>) -> Self {
BinaryString {
- vec
+ vec: OVector::from_vec_generic(Dyn(vec.len()), U1, vec)
}
}
+}
- pub fn vec(&self) -> &Vec<i8> {
+impl<D> BinaryString<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D>
+{
+ pub fn vec(&self) -> &OVector<i8, D> {
&self.vec
}
- pub fn perturb(self: &Self, rng: &mut dyn RngCore, p: f64) -> Self
+ pub fn perturb(mut self, rng: &mut dyn RngCore, p: f64) -> Self
{
- BinaryString::new(
- self.into_iter()
- .map(|c| if rng.random::<f64>() <= p { 1 - *c } else { *c })
- .collect::<Vec<i8>>()
- )
+ self.vec.apply(|c| *c = if rng.random::<f64>() <= p { 1 - *c } else { *c });
+ self
}
fn to_real_internal<'a, T: DoubleEndedIterator<Item = &'a i8>>(vec: T, len: usize, min: f64, max: f64) -> f64
@@ 85,14 82,15 @@ impl BinaryString {
return Err(BinaryStringConversionError::DimensionMismatch);
}
- let iter = self.vec.chunks(chunk_size)
+ let iter = self.vec.as_slice().chunks(chunk_size)
.zip(min.iter().zip(max))
- .map(|(chunk, (&min, &max))| BinaryString::to_real_internal(chunk.iter(), chunk_size, min, max));
+ .map(|(chunk, (&min, &max))| BinaryString::<D>::to_real_internal(chunk.iter(), chunk_size, min, max));
Ok(OVector::<f64, TDim>::from_iterator_generic(min.shape_generic().0, min.shape_generic().1, iter))
}
}
-impl FromStr for BinaryString {
+impl FromStr for BinaryString<Dyn>
+{
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ 108,13 106,17 @@ impl FromStr for BinaryString {
})
.collect::<Result<Vec<i8>, Self::Err>>()?;
- Ok(BinaryString::new(binary_vec))
+ Ok(BinaryString::new_dyn(binary_vec))
}
}
-impl<'a> IntoIterator for &'a BinaryString {
+impl<'a, D> IntoIterator for &'a BinaryString<D>
+where
+ D: Dim,
+ DefaultAllocator: nalgebra::allocator::Allocator<D>
+{
type Item = &'a i8;
- type IntoIter = std::slice::Iter<'a, i8>;
+ type IntoIter = nalgebra::iter::MatrixIter<'a, i8, D, U1, <DefaultAllocator as nalgebra::allocator::Allocator<D>>::Buffer<i8>>;
fn into_iter(self) -> Self::IntoIter {
self.vec.iter()
@@ 124,48 126,48 @@ impl<'a> IntoIterator for &'a BinaryString {
#[cfg(test)]
pub mod tests {
use crate::{binary_string::{BinaryString, BinaryStringConversionError}, test_infra::{load_test_file, DataArrOfReals}};
- use nalgebra::SVector;
+ use nalgebra::{SVector, U1, U2, U5};
#[test]
fn test_binary_string_to_real_single() {
assert_eq!(
- BinaryString::new(vec![1])
+ BinaryString::<U1>::new(vec![1])
.to_real_single(0.0, 32.0),
32.0
);
assert_eq!(
- BinaryString::new(vec![1, 1])
+ BinaryString::<U2>::new(vec![1, 1])
.to_real_single(0.0, 32.0),
32.0
);
assert_eq!(
- BinaryString::new(vec![0, 1])
+ BinaryString::<U2>::new(vec![0, 1])
.to_real_single(0.0, 32.0),
32.0 / 3.0
);
assert_eq!(
- BinaryString::new(vec![0, 0])
+ BinaryString::<U2>::new(vec![0, 0])
.to_real_single(-16.0, 16.0),
-16.0
);
assert_eq!(
- BinaryString::new(vec![0, 0, 0, 0, 1])
+ BinaryString::<U5>::new(vec![0, 0, 0, 0, 1])
.to_real_single(0.0, 31.0),
1.0
);
assert_eq!(
- BinaryString::new(vec![1, 1, 1, 1, 1])
+ BinaryString::<U5>::new(vec![1, 1, 1, 1, 1])
.to_real_single(0.0, 31.0),
31.0
);
assert_eq!(
- BinaryString::new(vec![0, 0, 0, 1, 0])
+ BinaryString::<U5>::new(vec![0, 0, 0, 1, 0])
.to_real_single(0.0, 31.0),
2.0
);
assert_eq!(
- BinaryString::new(vec![1; 512])
+ BinaryString::<U2>::new(vec![1; 512])
.to_real_single(0.0, 31.0),
31.0
);
@@ 175,7 177,7 @@ pub mod tests {
let data = load_test_file::<i8, DataArrOfReals>(file_name);
for test in data {
- let res = BinaryString::new(test.inp)
+ let res = BinaryString::new_dyn(test.inp)
.to_real(&min, &max);
if !test.out.valid {
assert_eq!(
M env/src/fitness/labs.rs => env/src/fitness/labs.rs +30 -17
@@ 1,30 1,41 @@
use std::convert::Infallible;
+use nalgebra::{allocator::Allocator, DefaultAllocator, Dim};
+
use crate::binary_string::BinaryString;
use super::FitnessFunction;
+use std::marker::PhantomData;
-pub struct LABS;
+pub struct LABS<D> {
+ _phantom: PhantomData<D>
+}
-impl LABS {
+impl<D> LABS<D> {
pub fn new() -> Self {
- LABS
- }
-
- fn ck(k: usize, s: &Vec<i32>) -> i32 {
- let d = s.len();
- s.iter()
- .take(d - k)
- .zip(s.iter().skip(k))
- .map(|(x, y)| x * y)
- .sum()
+ Self {
+ _phantom: PhantomData
+ }
}
}
-impl FitnessFunction for LABS {
- type In = BinaryString;
+impl<D> FitnessFunction for LABS<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D>
+{
+ type In = BinaryString<D>;
type Out = i32;
type Err = Infallible;
- fn fit(self: &Self, chromosome: &BinaryString) -> Result<i32, Infallible> {
+ fn fit(self: &Self, chromosome: &BinaryString<D>) -> Result<i32, Infallible> {
+ fn ck(k: usize, s: &Vec<i32>) -> i32 {
+ let d = s.len();
+ s.iter()
+ .take(d - k)
+ .zip(s.iter().skip(k))
+ .map(|(x, y)| x * y)
+ .sum()
+ }
+
let s: Vec<i32> = chromosome
.into_iter()
.map(|c| (*c as i32) * 2 - 1)
@@ 32,13 43,15 @@ impl FitnessFunction for LABS {
let d = s.len();
Ok((1..=d-1)
- .map(|k| LABS::ck(k, &s).pow(2))
+ .map(|k| ck(k, &s).pow(2))
.sum())
}
}
#[cfg(test)]
pub mod tests {
+ use nalgebra::Dyn;
+
use crate::{binary_string::BinaryString, fitness::{labs::LABS, FitnessFunction}, test_infra::load_test_file};
#[test]
@@ 51,7 64,7 @@ pub mod tests {
.collect::<Vec<String>>()
.join(", "));
assert_eq!(
- LABS::new().fit(&BinaryString::new(test.inp)).unwrap(),
+ LABS::<Dyn>::new().fit(&BinaryString::new_dyn(test.inp)).unwrap(),
test.out
)
}
M env/src/fitness/mod.rs => env/src/fitness/mod.rs +14 -8
@@ 1,4 1,4 @@
-use std::{convert::Infallible, error::Error};
+use std::{convert::Infallible, error::Error, marker::PhantomData};
use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector};
@@ 18,7 18,7 @@ pub trait FitnessFunction {
fn fit(self: &Self, inp: &Self::In) -> Result<Self::Out, Self::Err>;
}
-pub struct BinaryFitnessWrapper<D, TFitness>
+pub struct BinaryFitnessWrapper<D, DString, TFitness>
where
D: Dim,
DefaultAllocator: Allocator<D>
@@ 26,33 26,39 @@ where
min: OVector<f64, D>,
max: OVector<f64, D>,
fitting_function: TFitness,
+ _phantom: PhantomData<DString>
}
-impl<D, TFitness> BinaryFitnessWrapper<D, TFitness>
+impl<D, DString, TFitness> BinaryFitnessWrapper<D, DString, TFitness>
where
+ DString: Dim,
+ DefaultAllocator: Allocator<DString>,
D: Dim,
DefaultAllocator: Allocator<D>
{
pub fn new(fitting_function: TFitness, min: OVector<f64, D>, max: OVector<f64, D>) -> Self {
- BinaryFitnessWrapper {
+ Self {
fitting_function,
min,
- max
+ max,
+ _phantom: PhantomData
}
}
}
-impl<D, TFitness> FitnessFunction for BinaryFitnessWrapper<D, TFitness>
+impl<D, DString, TFitness> FitnessFunction for BinaryFitnessWrapper<D, DString, TFitness>
where
+ DString: Dim,
+ DefaultAllocator: Allocator<DString>,
D: Dim,
DefaultAllocator: Allocator<D>,
TFitness: FitnessFunction<In = OVector<f64, D>, Out = f64, Err = Infallible>
{
- type In = BinaryString;
+ type In = BinaryString<DString>;
type Out = f64;
type Err = BinaryStringConversionError;
- fn fit(self: &Self, inp: &BinaryString) -> Result<f64, BinaryStringConversionError> {
+ fn fit(self: &Self, inp: &BinaryString<DString>) -> Result<f64, BinaryStringConversionError> {
Ok(self.fitting_function.fit(&inp.to_real(&self.min, &self.max)?).unwrap())
}
}
M env/src/fitness/one_max.rs => env/src/fitness/one_max.rs +19 -8
@@ 1,20 1,31 @@
-use std::convert::Infallible;
+use std::{convert::Infallible, marker::PhantomData};
+use nalgebra::{allocator::Allocator, DefaultAllocator, Dim};
+
use crate::binary_string::BinaryString;
use super::FitnessFunction;
-pub struct OneMax;
-impl OneMax {
+pub struct OneMax<D> {
+ _phantom: PhantomData<D>
+}
+
+impl<D> OneMax<D> {
pub fn new() -> Self {
- OneMax
+ Self {
+ _phantom: PhantomData
+ }
}
}
-impl FitnessFunction for OneMax {
- type In = BinaryString;
+impl<D> FitnessFunction for OneMax<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D>
+{
+ type In = BinaryString<D>;
type Out = i32;
type Err = Infallible;
- fn fit(self: &Self, chromosome: &BinaryString) -> Result<i32, Infallible> {
+ fn fit(self: &Self, chromosome: &BinaryString<D>) -> Result<i32, Infallible> {
Ok(chromosome.into_iter()
.map(|x| *x as i32)
.sum())
@@ 33,7 44,7 @@ pub mod tests {
for test in data {
assert_eq!(
- OneMax::new().fit(&BinaryString::new(test.inp)).unwrap(),
+ OneMax::new().fit(&BinaryString::new_dyn(test.inp)).unwrap(),
test.out
);
}
M env/src/local_search/mod.rs => env/src/local_search/mod.rs +14 -9
@@ 7,7 7,8 @@ use crate::terminating::TerminatingCondition;
use crate::perturbation::PerturbationOperator;
use crate::comparison::BetterThanOperator;
use full_palette::BROWN;
-use nalgebra::SVector;
+use nalgebra::allocator::Allocator;
+use nalgebra::{DefaultAllocator, Dim, SVector, U2};
use plotters::prelude::*;
// Functions
@@ 24,7 25,11 @@ pub struct LocalSearchStats<TInput, TResult> {
stats: Vec<LocalSearchCandidate<TInput, TResult>>
}
-impl<TResult> LocalSearchStats<BinaryString, TResult> {
+impl<D, TResult> LocalSearchStats<BinaryString<D>, TResult>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D>
+{
pub fn to_real(self, min: &SVector<f64, 2>, max: &SVector<f64, 2>) -> Result<LocalSearchStats<SVector<f64, 2>, TResult>, BinaryStringConversionError> {
Ok(LocalSearchStats::<SVector<f64, 2>, TResult>::from_vec(
self.stats
@@ 32,7 37,7 @@ impl<TResult> LocalSearchStats<BinaryString, TResult> {
.map(|candidate|
Ok(LocalSearchCandidate {
fit: candidate.fit,
- pos: candidate.pos.to_real(&min, &max)?,
+ pos: candidate.pos.to_real::<U2>(&min, &max)?,
cycle: candidate.cycle,
}))
.collect::<Result<Vec<_>, _>>()?))
@@ 273,13 278,13 @@ pub fn plot_fitness_evolution(file: &str, stats: LocalSearchStats<SVector<f64, 2
#[cfg(test)]
pub mod tests {
- use nalgebra::SVector;
+ use nalgebra::{SVector, U10};
use crate::{binary_string::BinaryString, comparison::MinimizingOperator, evolutionary_strategy::{IdentityStrategy, OneToFiveStrategy}, fitness::{one_max::OneMax, real::Linear, rosenbrock::Rosenbrock, sphere::Sphere, BinaryFitnessWrapper}, local_search::{local_search_first_improving, local_search_first_improving_evolving, plot_fitness_evolution}, perturbation::{BinaryStringBitPerturbation, BoundedPerturbation, BoundedPerturbationStrategy, PatternPerturbation, RandomDistributionPerturbation}, terminating::{AndTerminatingConditions, CloserThanTerminatingCondition, EqualTerminatingCondition, NoBetterForCyclesTerminatingCondition}};
#[test]
fn test_local_search_sphere_binary() {
- let optimum = BinaryString::new(vec![0, 0, 1, 0, 0,
+ let optimum = BinaryString::<U10>::new(vec![0, 0, 1, 0, 0,
0, 0, 1, 0, 0]);
let min = SVector::<f64, 2>::from_element(0.0);
let max = SVector::<f64, 2>::from_element(31.0);
@@ 351,8 356,8 @@ pub mod tests {
#[test]
fn test_local_search_one_max() {
- let one_max = OneMax::new();
- let optimum = BinaryString::new(vec![0; 10]);
+ let one_max = OneMax::<U10>::new();
+ let optimum = BinaryString::<U10>::new(vec![0; 10]);
let result = local_search_first_improving(
&one_max,
@@ 365,7 370,7 @@ pub mod tests {
),
&mut BinaryStringBitPerturbation::new(0.3),
&MinimizingOperator::new(),
- &BinaryString::new(vec![1; 10]),
+ &BinaryString::<U10>::new(vec![1; 10]),
).unwrap();
println!("{:?}", result);
@@ 416,7 421,7 @@ pub mod tests {
#[test]
fn test_local_search_rosenbrock() {
let rosenbrock = Rosenbrock::new();
- let optimum = BinaryString::new(vec![1, 0, 0, 0, 1, 1, 0, 0, 0, 1]);
+ let optimum = BinaryString::<U10>::new(vec![1, 0, 0, 0, 1, 1, 0, 0, 0, 1]);
let min = SVector::<f64, 2>::from_element(-16.0);
let max = SVector::<f64, 2>::from_element(15.0);
M env/src/perturbation/mod.rs => env/src/perturbation/mod.rs +26 -11
@@ 1,4 1,6 @@
-use nalgebra::SVector;
+use std::marker::PhantomData;
+
+use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, SVector};
use rand::{distr::Distribution, Rng, RngCore};
use rand_distr::{uniform, Normal, NormalError, Uniform};
@@ 10,25 12,31 @@ pub trait PerturbationOperator {
fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome;
}
-pub struct BinaryStringBitPerturbation {
+pub struct BinaryStringBitPerturbation<D> {
rng: Box<dyn RngCore>,
p: f64,
+ _phantom: PhantomData<D>
}
-impl BinaryStringBitPerturbation {
+impl<D> BinaryStringBitPerturbation<D> {
pub fn new(p: f64) -> Self {
Self {
rng: Box::new(rand::rng()),
- p
+ p,
+ _phantom: PhantomData
}
}
}
-impl PerturbationOperator for BinaryStringBitPerturbation {
- type Chromosome = BinaryString;
+impl<D> PerturbationOperator for BinaryStringBitPerturbation<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D>
+{
+ type Chromosome = BinaryString<D>;
fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
- chromosome.perturb(&mut self.rng, self.p)
+ chromosome.clone().perturb(&mut self.rng, self.p)
}
}
@@ 211,16 219,23 @@ pub mod tests {
let mut rng = rand::rng();
assert_eq!(
- *BinaryString::new(vec![1, 1, 0, 0])
+ *BinaryString::new_dyn(vec![1, 1, 0, 0])
.perturb(&mut rng, 1.0)
- .vec(),
+ .vec()
+ .iter()
+ .map(|&x| x)
+ .collect::<Vec<_>>(),
vec![0, 0, 1, 1]
);
+
assert_eq!(
- *BinaryString::new(vec![1, 1, 0, 0])
+ *BinaryString::new_dyn(vec![1, 1, 0, 0])
.perturb(&mut rng, 0.0)
- .vec(),
+ .vec()
+ .iter()
+ .map(|&x| x)
+ .collect::<Vec<_>>(),
vec![1, 1, 0, 0]
);
}