From c66539e12fa23155206cc9655202f90c39a42a42 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 18 Oct 2025 12:42:38 +0200 Subject: [PATCH] refactor: Add dimension generic to BinaryString --- env/src/binary_string.rs | 106 ++++++++++++++++++------------------ env/src/fitness/labs.rs | 47 ++++++++++------ env/src/fitness/mod.rs | 22 +++++--- env/src/fitness/one_max.rs | 27 ++++++--- env/src/local_search/mod.rs | 23 +++++--- env/src/perturbation/mod.rs | 37 +++++++++---- 6 files changed, 157 insertions(+), 105 deletions(-) diff --git a/env/src/binary_string.rs b/env/src/binary_string.rs index 208f48193be1ace9e2225a7034925a0d5dc3ab61..727e8c6b0d68d945a4d4292289c4eb3a185c82ac 100644 --- a/env/src/binary_string.rs +++ b/env/src/binary_string.rs @@ -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 +pub struct BinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + pub vec: OVector } #[derive(Error, Debug, Clone, PartialEq)] @@ -37,24 +18,40 @@ pub enum BinaryStringConversionError { DimensionMismatch, } -impl BinaryString { - pub fn new(vec: Vec) -> BinaryString { +impl BinaryString +where + D: DimName, + DefaultAllocator: Allocator +{ + pub fn new(vec: Vec) -> Self { + Self { + vec: OVector::from_vec(vec) + } + } +} + +impl BinaryString +{ + pub fn new_dyn(vec: Vec) -> Self { BinaryString { - vec + vec: OVector::from_vec_generic(Dyn(vec.len()), U1, vec) } } +} - pub fn vec(&self) -> &Vec { +impl BinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + pub fn vec(&self) -> &OVector { &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::() <= p { 1 - *c } else { *c }) - .collect::>() - ) + self.vec.apply(|c| *c = if rng.random::() <= p { 1 - *c } else { *c }); + self } fn to_real_internal<'a, T: DoubleEndedIterator>(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::::to_real_internal(chunk.iter(), chunk_size, min, max)); Ok(OVector::::from_iterator_generic(min.shape_generic().0, min.shape_generic().1, iter)) } } -impl FromStr for BinaryString { +impl FromStr for BinaryString +{ type Err = String; fn from_str(s: &str) -> Result { @@ -108,13 +106,17 @@ impl FromStr for BinaryString { }) .collect::, 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 +where + D: Dim, + DefaultAllocator: nalgebra::allocator::Allocator +{ type Item = &'a i8; - type IntoIter = std::slice::Iter<'a, i8>; + type IntoIter = nalgebra::iter::MatrixIter<'a, i8, D, U1, >::Buffer>; 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::::new(vec![1]) .to_real_single(0.0, 32.0), 32.0 ); assert_eq!( - BinaryString::new(vec![1, 1]) + BinaryString::::new(vec![1, 1]) .to_real_single(0.0, 32.0), 32.0 ); assert_eq!( - BinaryString::new(vec![0, 1]) + BinaryString::::new(vec![0, 1]) .to_real_single(0.0, 32.0), 32.0 / 3.0 ); assert_eq!( - BinaryString::new(vec![0, 0]) + BinaryString::::new(vec![0, 0]) .to_real_single(-16.0, 16.0), -16.0 ); assert_eq!( - BinaryString::new(vec![0, 0, 0, 0, 1]) + BinaryString::::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::::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::::new(vec![0, 0, 0, 1, 0]) .to_real_single(0.0, 31.0), 2.0 ); assert_eq!( - BinaryString::new(vec![1; 512]) + BinaryString::::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::(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!( diff --git a/env/src/fitness/labs.rs b/env/src/fitness/labs.rs index 67a90557144e2a4cede91dc1e568e0c9f1173383..7ee767a944cb9e39003d8ea4c61dfde700c967e2 100644 --- a/env/src/fitness/labs.rs +++ b/env/src/fitness/labs.rs @@ -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 { + _phantom: PhantomData +} -impl LABS { +impl LABS { pub fn new() -> Self { - LABS - } - - fn ck(k: usize, s: &Vec) -> 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 FitnessFunction for LABS +where + D: Dim, + DefaultAllocator: Allocator +{ + type In = BinaryString; type Out = i32; type Err = Infallible; - fn fit(self: &Self, chromosome: &BinaryString) -> Result { + fn fit(self: &Self, chromosome: &BinaryString) -> Result { + fn ck(k: usize, s: &Vec) -> i32 { + let d = s.len(); + s.iter() + .take(d - k) + .zip(s.iter().skip(k)) + .map(|(x, y)| x * y) + .sum() + } + let s: Vec = 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::>() .join(", ")); assert_eq!( - LABS::new().fit(&BinaryString::new(test.inp)).unwrap(), + LABS::::new().fit(&BinaryString::new_dyn(test.inp)).unwrap(), test.out ) } diff --git a/env/src/fitness/mod.rs b/env/src/fitness/mod.rs index b2bfa6dbe5f6aa5ffec3b3b74c1381aa59afd31a..ef1ede5fa679e822554f2aae9105712dfad63a92 100644 --- a/env/src/fitness/mod.rs +++ b/env/src/fitness/mod.rs @@ -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; } -pub struct BinaryFitnessWrapper +pub struct BinaryFitnessWrapper where D: Dim, DefaultAllocator: Allocator @@ -26,33 +26,39 @@ where min: OVector, max: OVector, fitting_function: TFitness, + _phantom: PhantomData } -impl BinaryFitnessWrapper +impl BinaryFitnessWrapper where + DString: Dim, + DefaultAllocator: Allocator, D: Dim, DefaultAllocator: Allocator { pub fn new(fitting_function: TFitness, min: OVector, max: OVector) -> Self { - BinaryFitnessWrapper { + Self { fitting_function, min, - max + max, + _phantom: PhantomData } } } -impl FitnessFunction for BinaryFitnessWrapper +impl FitnessFunction for BinaryFitnessWrapper where + DString: Dim, + DefaultAllocator: Allocator, D: Dim, DefaultAllocator: Allocator, TFitness: FitnessFunction, Out = f64, Err = Infallible> { - type In = BinaryString; + type In = BinaryString; type Out = f64; type Err = BinaryStringConversionError; - fn fit(self: &Self, inp: &BinaryString) -> Result { + fn fit(self: &Self, inp: &BinaryString) -> Result { Ok(self.fitting_function.fit(&inp.to_real(&self.min, &self.max)?).unwrap()) } } diff --git a/env/src/fitness/one_max.rs b/env/src/fitness/one_max.rs index f1911ae457bdb776317d1059c49f3928f401b547..690ef8080c1ffd71b4e5a7e016b301ea7e8e8d08 100644 --- a/env/src/fitness/one_max.rs +++ b/env/src/fitness/one_max.rs @@ -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 { + _phantom: PhantomData +} + +impl OneMax { pub fn new() -> Self { - OneMax + Self { + _phantom: PhantomData + } } } -impl FitnessFunction for OneMax { - type In = BinaryString; +impl FitnessFunction for OneMax +where + D: Dim, + DefaultAllocator: Allocator +{ + type In = BinaryString; type Out = i32; type Err = Infallible; - fn fit(self: &Self, chromosome: &BinaryString) -> Result { + fn fit(self: &Self, chromosome: &BinaryString) -> Result { 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 ); } diff --git a/env/src/local_search/mod.rs b/env/src/local_search/mod.rs index 33557e1c4691f2bbb79cda6a8d0d2e30ba73873d..1fbd85286f4bf6c4a650ee98414aa42ac225a705 100644 --- a/env/src/local_search/mod.rs +++ b/env/src/local_search/mod.rs @@ -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 { stats: Vec> } -impl LocalSearchStats { +impl LocalSearchStats, TResult> +where + D: Dim, + DefaultAllocator: Allocator +{ pub fn to_real(self, min: &SVector, max: &SVector) -> Result, TResult>, BinaryStringConversionError> { Ok(LocalSearchStats::, TResult>::from_vec( self.stats @@ -32,7 +37,7 @@ impl LocalSearchStats { .map(|candidate| Ok(LocalSearchCandidate { fit: candidate.fit, - pos: candidate.pos.to_real(&min, &max)?, + pos: candidate.pos.to_real::(&min, &max)?, cycle: candidate.cycle, })) .collect::, _>>()?)) @@ -273,13 +278,13 @@ pub fn plot_fitness_evolution(file: &str, stats: LocalSearchStats::new(vec![0, 0, 1, 0, 0, 0, 0, 1, 0, 0]); let min = SVector::::from_element(0.0); let max = SVector::::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::::new(); + let optimum = BinaryString::::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::::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::::new(vec![1, 0, 0, 0, 1, 1, 0, 0, 0, 1]); let min = SVector::::from_element(-16.0); let max = SVector::::from_element(15.0); diff --git a/env/src/perturbation/mod.rs b/env/src/perturbation/mod.rs index 8e3b6a9339988db4eb2cf1b70e634fde8dc81a61..a066faa3b91fee767513bc88f2fbcf0907309f2c 100644 --- a/env/src/perturbation/mod.rs +++ b/env/src/perturbation/mod.rs @@ -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 { rng: Box, p: f64, + _phantom: PhantomData } -impl BinaryStringBitPerturbation { +impl BinaryStringBitPerturbation { pub fn new(p: f64) -> Self { Self { rng: Box::new(rand::rng()), - p + p, + _phantom: PhantomData } } } -impl PerturbationOperator for BinaryStringBitPerturbation { - type Chromosome = BinaryString; +impl PerturbationOperator for BinaryStringBitPerturbation +where + D: Dim, + DefaultAllocator: Allocator +{ + type Chromosome = BinaryString; 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![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![1, 1, 0, 0] ); }