From 96e498675423c4bad7fa358efb37fe26a6dc1563 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 18 Oct 2025 18:07:02 +0200 Subject: [PATCH] feat: add initializers, ZeroInitializer, RandomInitializer --- env/src/binary_string.rs | 25 +++++++- env/src/bounded.rs | 126 +++++++++++++++++++++++++++++++++++++ env/src/initializer/mod.rs | 93 +++++++++++++++++++++++++++ env/src/main.rs | 3 + 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 env/src/bounded.rs create mode 100644 env/src/initializer/mod.rs diff --git a/env/src/binary_string.rs b/env/src/binary_string.rs index 727e8c6b0d68d945a4d4292289c4eb3a185c82ac..649e5821fc83c487c6b87b2bb790b17b21edd53a 100644 --- a/env/src/binary_string.rs +++ b/env/src/binary_string.rs @@ -3,7 +3,7 @@ use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, DimName, Dyn, OVecto use rand::{Rng, RngCore}; use thiserror::Error; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct BinaryString where D: Dim, @@ -12,12 +12,35 @@ where pub vec: OVector } +impl PartialOrd for BinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + fn partial_cmp(&self, other: &Self) -> Option { + // TODO: implement more efficient ordering by iteration... + self.to_real_single(0.0, 100.0).partial_cmp(&other.to_real_single(0.0, 100.0)) + } +} + #[derive(Error, Debug, Clone, PartialEq)] pub enum BinaryStringConversionError { #[error("The dimension of the bounds does not divide the length of the binary string.")] DimensionMismatch, } +impl BinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + pub fn from_ovector(vec: OVector) -> Self { + Self { + vec + } + } +} + impl BinaryString where D: DimName, diff --git a/env/src/bounded.rs b/env/src/bounded.rs new file mode 100644 index 0000000000000000000000000000000000000000..598aa1a839781f89f74f37f73ee8a4fd63ff1d0f --- /dev/null +++ b/env/src/bounded.rs @@ -0,0 +1,126 @@ +use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, U1}; +use rand::{Rng, RngCore}; + +use crate::binary_string::BinaryString; + +pub trait Bounded { + type Item; + + fn is_in_bounds(&self, obj: &Self::Item) -> bool; + fn bound(&self, obj: Self::Item) -> Self::Item; + fn next_random(&self, size: D, rand: &mut dyn RngCore) -> Self::Item; +} + +pub struct BoundedBinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + min: Option>, + max: Option>, +} + +impl BoundedBinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + pub fn unbounded() -> Self { + Self { + min: None, + max: None, + } + } +} + +impl Bounded for BoundedBinaryString +where + D: Dim, + DefaultAllocator: Allocator +{ + type Item = BinaryString; + + fn is_in_bounds(&self, obj: &Self::Item) -> bool { + if let Some(min) = &self.min { + if obj < min { + return false; + } + } + + if let Some(max) = &self.max { + if obj > max { + return false; + } + } + + return true; + } + + fn bound(&self, obj: Self::Item) -> Self::Item { + if let Some(min) = &self.min { + if &obj < min { + return min.clone(); + } + } + + if let Some(max) = &self.max { + if &obj > max { + return max.clone(); + } + } + + obj + } + + fn next_random(&self, size: D, rand: &mut dyn RngCore) -> Self::Item { + // TODO... this is very suboptimal, favoring the max, min. + self.bound( + BinaryString::from_ovector( + OVector::::from_fn_generic(size, U1, |_, _| rand.random_range(0..=1)) + )) + } +} + +pub struct BoundedOVector +where + D: Dim, + DefaultAllocator: Allocator +{ + min_max: OVector<(f64, f64), D> +} + +impl BoundedOVector +where + D: Dim, + DefaultAllocator: Allocator +{ + pub fn new(min: OVector, max: OVector) -> Self { + Self { + min_max: min.zip_map(&max, |min, max| (min, max)) + } + } +} + +impl Bounded for BoundedOVector +where + D: Dim, + DefaultAllocator: Allocator +{ + type Item = OVector; + + fn is_in_bounds(&self, obj: &Self::Item) -> bool { + obj.iter() + .zip(self.min_max.iter()) + .all(|(&c, &(min, max))| c <= max && c >= min) + } + + fn bound(&self, mut obj: Self::Item) -> Self::Item { + obj + .zip_apply(&self.min_max, |c, (min, max)| *c = c.clamp(min, max)); + obj + } + + fn next_random(&self, _: D, rand: &mut dyn RngCore) -> Self::Item { + self.min_max.map(|(min, max)| rand.random_range(min..=max)) + } +} diff --git a/env/src/initializer/mod.rs b/env/src/initializer/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..09aa72cd8cd162b544ea417760a032884ddbd97a --- /dev/null +++ b/env/src/initializer/mod.rs @@ -0,0 +1,93 @@ +use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, Scalar, U1}; +use rand::{rng, RngCore}; + +use crate::{binary_string::BinaryString, bounded::{Bounded, BoundedBinaryString}}; + +pub trait Initializer { + fn initialize_single(&mut self, size: D) -> T; + fn initialize(&mut self, size: D, count: usize) -> impl Iterator { + let size = size; + (0..count).map(move |_| self.initialize_single(size)) + } +} + +// Always initializes with zeros +pub struct ZeroInitializer; + +impl ZeroInitializer { + pub fn new() -> Self { + Self { + } + } +} + +impl Initializer> for ZeroInitializer +where + D: Dim, + DefaultAllocator: Allocator +{ + fn initialize_single(&mut self, size: D) -> BinaryString { + BinaryString::::from_ovector( + >>::initialize_single(self, size) + ) + } +} + +impl Initializer> for ZeroInitializer +where + T: Scalar + Default, + D: Dim, + DefaultAllocator: Allocator +{ + fn initialize_single(&mut self, size: D) -> OVector { + OVector::::from_element_generic(size, U1, Default::default()) + } +} + +pub struct RandomInitializer { + rng: Box, + bounded: Box> +} + +impl RandomInitializer { + pub fn new(bounded: Box>) -> Self { + Self { + rng: Box::new(rand::rng()), + bounded + } + } +} + +impl RandomInitializer> +where + D: Dim, + DefaultAllocator: Allocator +{ + pub fn new_binary() -> Self { + Self { + rng: Box::new(rand::rng()), + bounded: Box::new(BoundedBinaryString::unbounded()) + } + } +} + +impl Initializer> for RandomInitializer> +where + D: Dim, + DefaultAllocator: Allocator +{ + fn initialize_single(&mut self, size: D) -> BinaryString { + self.bounded.next_random(size, &mut self.rng) + } +} + +impl Initializer> for RandomInitializer> +where + T: Scalar + Default, + D: Dim, + DefaultAllocator: Allocator +{ + fn initialize_single(&mut self, size: D) -> OVector { + self.bounded.next_random(size, &mut self.rng) + } +} diff --git a/env/src/main.rs b/env/src/main.rs index 5f502741379018bae7842ff6a07973e64d2f12bb..09b95fcacdf4a39a36c980236230a3954e05548d 100644 --- a/env/src/main.rs +++ b/env/src/main.rs @@ -1,4 +1,7 @@ pub mod fitness; +pub mod crossover; +pub mod bounded; +pub mod initializer; pub mod terminating; pub mod perturbation; pub mod comparison;