From 79c83af97c81934e0a00b46126f6cea215dc77dc Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 5 Oct 2025 17:15:26 +0200 Subject: [PATCH] feat: add real fitness functions Add linear, step, rastrigin, griewank and schwefel fitness functions along with the tests for them. Try using static vector where appropriate, but use OVector for those where filling with zeros won't work. This is mostly a constrain for the tests... rather than a real constrain. In the end the dimensions will be given for each problem. --- env/src/fitness/mod.rs | 1 + env/src/fitness/real.rs | 278 ++++++++++++++++++++++++++++++++++++++++ env/tests/griewank.txt | 32 +++++ env/tests/linear_1.txt | 32 +++++ env/tests/linear_2.txt | 32 +++++ env/tests/rastrigin.txt | 32 +++++ env/tests/schwefel.txt | 32 +++++ env/tests/step_1.txt | 32 +++++ env/tests/step_2.txt | 32 +++++ 9 files changed, 503 insertions(+) create mode 100644 env/src/fitness/real.rs create mode 100644 env/tests/griewank.txt create mode 100644 env/tests/linear_1.txt create mode 100644 env/tests/linear_2.txt create mode 100644 env/tests/rastrigin.txt create mode 100644 env/tests/schwefel.txt create mode 100644 env/tests/step_1.txt create mode 100644 env/tests/step_2.txt diff --git a/env/src/fitness/mod.rs b/env/src/fitness/mod.rs index 271aa73751ec3bcb6f50910c73091963e3d3a07c..8effb0e3e61d47abc1510a7fef22245af1cc8aa6 100644 --- a/env/src/fitness/mod.rs +++ b/env/src/fitness/mod.rs @@ -6,6 +6,7 @@ pub mod labs; pub mod one_max; pub mod rosenbrock; pub mod sphere; +pub mod real; pub trait FitnessFunction { type In; diff --git a/env/src/fitness/real.rs b/env/src/fitness/real.rs new file mode 100644 index 0000000000000000000000000000000000000000..2600e65fa0e138af34d20028fa4bad3f60cf212f --- /dev/null +++ b/env/src/fitness/real.rs @@ -0,0 +1,278 @@ +use std::{convert::Infallible, f64::consts::PI, marker::PhantomData}; + +use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, SVector}; + +use super::FitnessFunction; + +pub struct Linear { + a0: f64, + a: SVector, +} + +impl Linear { + pub fn new(a0: f64, a: SVector) -> Self { + Self { + a0, + a + } + } +} + +impl FitnessFunction for Linear { + type In = SVector; + type Out = f64; + type Err = Infallible; + + fn fit(self: &Self, inp: &Self::In) -> Result { + Ok(self.a0 + (self.a.transpose() * inp).x) + } +} + +pub struct Step { + a0: f64, + a: SVector, +} + +impl Step { + pub fn new(a0: f64, a: SVector) -> Self { + Self { + a0, + a + } + } +} + +impl FitnessFunction for Step { + type In = SVector; + type Out = f64; + type Err = Infallible; + + fn fit(self: &Self, inp: &Self::In) -> Result { + Ok(self.a0 + inp.component_mul(&self.a).map(|x| x.floor()).sum()) + } +} + +pub struct Rastrigin { + _phantom: PhantomData +} + +impl Rastrigin { + pub fn new() -> Self { + Self { + _phantom: PhantomData + } + } +} + +impl FitnessFunction for Rastrigin +where + DefaultAllocator: Allocator +{ + type In = OVector; + type Out = f64; + type Err = Infallible; + + fn fit(self: &Self, inp: &Self::In) -> Result { + let dim = inp.len() as f64; + Ok(10.0 * dim + inp.iter() + .map(|x| x.powi(2) - 10.0 * (2.0 * PI * x).cos()) + .sum::()) + } +} + +pub struct Griewank { + _phantom: PhantomData +} + +impl Griewank { + pub fn new() -> Self { + Self { + _phantom: PhantomData + } + } +} + +impl FitnessFunction for Griewank +where + DefaultAllocator: Allocator +{ + type In = OVector; + type Out = f64; + type Err = Infallible; + + fn fit(self: &Self, inp: &Self::In) -> Result { + Ok(1.0 + inp.map(|x| x.powi(2)).sum() / 4000.0 - inp + .iter() + .enumerate() + .map(|(i, x)| (x / ((i + 1) as f64).sqrt()).cos()) + .product::()) + } +} + +pub struct Schwefel; + +impl Schwefel { + pub fn new() -> Self { + Self + } +} + +impl FitnessFunction for Schwefel { + type In = SVector; + type Out = f64; + type Err = Infallible; + + fn fit(self: &Self, inp: &Self::In) -> Result { + Ok(-inp + .map(|x| x * x.abs().sqrt().sin()) + .sum()) + } +} + +#[cfg(test)] +pub mod tests { + use nalgebra::{Dyn, OVector, SVector}; + + use crate::{fitness::{real::{Linear, Rastrigin, Schwefel, Step}, FitnessFunction}, test_infra::load_test_file}; + + use super::Griewank; + + #[test] + fn test_linear_1() { + const MAX_LEN: usize = 10; + + let data = load_test_file::("tests/linear_1.txt"); + let offset = SVector::repeat(1.0); + let linear = Linear::new(1.0, offset); + + for test in data { + let filled = test.inp.iter() + .chain(vec![0f64; MAX_LEN - test.inp.len()].iter()) + .map(|x| *x) + .collect::>(); + let inp = SVector::::from_vec(filled); + + assert_eq!( + linear.fit(&inp).unwrap(), + test.out + ); + } + } + + #[test] + fn test_linear_2() { + const MAX_LEN: usize = 10; + + let data = load_test_file::("tests/linear_2.txt"); + let offset = SVector::from_vec((2..=11).map(|x| x as f64).collect()); + let linear = Linear::new(1.0, offset); + + for test in data { + let filled = test.inp.iter() + .chain(vec![0f64; MAX_LEN - test.inp.len()].iter()) + .map(|x| *x) + .collect::>(); + let inp = SVector::::from_vec(filled); + + assert_eq!( + linear.fit(&inp).unwrap(), + test.out + ); + } + } + + #[test] + fn test_step_1() { + const MAX_LEN: usize = 10; + + let data = load_test_file::("tests/step_1.txt"); + let offset = SVector::repeat(1.0); + let linear = Step::new(1.0, offset); + + for test in data { + let filled = test.inp.iter() + .chain(vec![0f64; MAX_LEN - test.inp.len()].iter()) + .map(|x| *x) + .collect::>(); + let inp = SVector::::from_vec(filled); + + assert_eq!( + linear.fit(&inp).unwrap(), + test.out + ); + } + } + + #[test] + fn test_step_2() { + const MAX_LEN: usize = 10; + + let data = load_test_file::("tests/step_2.txt"); + let offset = SVector::from_vec((2..=11).map(|x| x as f64).collect()); + let linear = Step::new(1.0, offset); + + for test in data { + let filled = test.inp.iter() + .chain(vec![0f64; MAX_LEN - test.inp.len()].iter()) + .map(|x| *x) + .collect::>(); + let inp = SVector::::from_vec(filled); + + assert_eq!( + linear.fit(&inp).unwrap(), + test.out + ); + } + } + + #[test] + fn test_rastrigin() { + let data = load_test_file::("tests/rastrigin.txt"); + let linear = Rastrigin::::new(); + + for test in data { + let inp = OVector::::from_vec(test.inp); + + assert_eq!( + linear.fit(&inp).unwrap(), + test.out + ); + } + } + + #[test] + fn test_griewank() { + let data = load_test_file::("tests/griewank.txt"); + let griewank = Griewank::::new(); + + for test in data { + let inp = OVector::::from_vec(test.inp); + + assert_eq!( + griewank.fit(&inp).unwrap(), + test.out + ); + } + } + + #[test] + fn test_schwefel() { + const MAX_LEN: usize = 10; + + let data = load_test_file::("tests/schwefel.txt"); + let schwefel = Schwefel::new(); + + for test in data { + let filled = test.inp.iter() + .chain(vec![0f64; MAX_LEN - test.inp.len()].iter()) + .map(|x| *x) + .collect::>(); + let inp = SVector::::from_vec(filled); + + assert_eq!( + schwefel.fit(&inp).unwrap(), + test.out + ); + } + } +} diff --git a/env/tests/griewank.txt b/env/tests/griewank.txt new file mode 100644 index 0000000000000000000000000000000000000000..75abde71fa24a30d32718da2a82f104cb2d5750e --- /dev/null +++ b/env/tests/griewank.txt @@ -0,0 +1,32 @@ +# Test data for Griewank function. +0 : 0.0 +0.5 : 0.12247993810962732 +1 : 0.4599476941318603 +-0.5 : 0.12247993810962732 +-5 : 0.7225878145367739 +5 : 0.7225878145367739 +0 0 : 0.0 +-1 -1 : 0.5897380911762422 +-1 1 : 0.5897380911762422 +1 -1 : 0.5897380911762422 +1 1 : 0.5897380911762422 +-0.1 -0.2 : 0.014941804023654082 +0.1 0.2 : 0.014941804023654082 +-5 -5 : 1.2744346440216008 +-5 5 : 1.2744346440216008 +5 -5 : 1.2744346440216008 +5 5 : 1.2744346440216008 +0 0 0 : 0.0 +1 1 1 : 0.656567738230001 +-5 -5 -5 : 0.765274977211063 +-5 -5 5 : 0.765274977211063 +-5 5 -5 : 0.765274977211063 +-5 5 5 : 0.765274977211063 +5 -5 -5 : 0.765274977211063 +5 -5 5 : 0.765274977211063 +5 5 -5 : 0.765274977211063 +5 5 5 : 0.765274977211063 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : 1.088546150836955 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : 1.088546150836955 +0 0 0 0 0 0 0 0 0 0 : 0.0 +1 1 1 1 1 1 1 1 1 1 : 0.8067591547236139 diff --git a/env/tests/linear_1.txt b/env/tests/linear_1.txt new file mode 100644 index 0000000000000000000000000000000000000000..ae6f90b82ed9261e22f56cd590a35a80f9ae9b57 --- /dev/null +++ b/env/tests/linear_1.txt @@ -0,0 +1,32 @@ +# Test data for Linear function, with coeffs a_i= (1,1,...,1) and bias a_0 = 1 +0 : 1 +0.5 : 1.5 +1 : 2 +-0.5 : 0.5 +-5 : -4 +5 : 6 +0 0 : 1 +-1 -1 : -1 +-1 1 : 1 +1 -1 : 1 +1 1 : 3 +-0.1 -0.2 : 0.7 +0.1 0.2 : 1.3 +-5 -5 : -9 +-5 5 : 1 +5 -5 : 1 +5 5 : 11 +0 0 0 : 1 +1 1 1 : 4 +-5 -5 -5 : -14 +-5 -5 5 : -4 +-5 5 -5 : -4 +-5 5 5 : 6 +5 -5 -5 : -4 +5 -5 5 : 6 +5 5 -5 : 6 +5 5 5 : 16 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : -48.6 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : 50.6 +0 0 0 0 0 0 0 0 0 0 : 1 +1 1 1 1 1 1 1 1 1 1 : 11 diff --git a/env/tests/linear_2.txt b/env/tests/linear_2.txt new file mode 100644 index 0000000000000000000000000000000000000000..ec1f865babce251da685ae08457cdc8f178a3f0e --- /dev/null +++ b/env/tests/linear_2.txt @@ -0,0 +1,32 @@ +# Test data for Linear function, with coeffs a_i = (2,3,...,D+1) and bias a_0 = 1 +0 : 1 +0.5 : 2.0 +1 : 3 +-0.5 : 0.0 +-5 : -9 +5 : 11 +0 0 : 1 +-1 -1 : -4 +-1 1 : 2 +1 -1 : 0 +1 1 : 6 +-0.1 -0.2 : 0.19999999999999996 +0.1 0.2 : 1.8 +-5 -5 : -24 +-5 5 : 6 +5 -5 : -4 +5 5 : 26 +0 0 0 : 1 +1 1 1 : 10 +-5 -5 -5 : -44 +-5 -5 5 : -4 +-5 5 -5 : -14 +-5 5 5 : 26 +5 -5 -5 : -24 +5 -5 5 : 16 +5 5 -5 : 6 +5 5 5 : 46 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : -408.1 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : 410.1 +0 0 0 0 0 0 0 0 0 0 : 1 +1 1 1 1 1 1 1 1 1 1 : 66 diff --git a/env/tests/rastrigin.txt b/env/tests/rastrigin.txt new file mode 100644 index 0000000000000000000000000000000000000000..76a2645acc50bb997908e7904cc1de0674ac3692 --- /dev/null +++ b/env/tests/rastrigin.txt @@ -0,0 +1,32 @@ +# Test data for Rastringin function. +0 : 0.0 +0.5 : 20.25 +1 : 1.0 +-0.5 : 20.25 +-5 : 25.0 +5 : 25.0 +0 0 : 0.0 +-1 -1 : 2.0 +-1 1 : 2.0 +1 -1 : 2.0 +1 1 : 2.0 +-0.1 -0.2 : 8.869660112501052 +0.1 0.2 : 8.869660112501052 +-5 -5 : 50.0 +-5 5 : 50.0 +5 -5 : 50.0 +5 5 : 50.0 +0 0 0 : 0.0 +1 1 1 : 3.0 +-5 -5 -5 : 75.0 +-5 -5 5 : 75.0 +-5 5 -5 : 75.0 +-5 5 5 : 75.0 +5 -5 -5 : 75.0 +5 -5 5 : 75.0 +5 5 -5 : 75.0 +5 5 5 : 75.0 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : 439.5698300562505 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : 439.5698300562505 +0 0 0 0 0 0 0 0 0 0 : 0.0 +1 1 1 1 1 1 1 1 1 1 : 10.0 diff --git a/env/tests/schwefel.txt b/env/tests/schwefel.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f3ff0fb8869be903801f5b5fc1300afeeefa816 --- /dev/null +++ b/env/tests/schwefel.txt @@ -0,0 +1,32 @@ +# Test data for Schwefel function. +0 : -0.0 +0.5 : -0.32481846954003124 +1 : -0.8414709848078965 +-0.5 : 0.32481846954003124 +-5 : 3.93374565773607 +5 : -3.93374565773607 +0 0 : -0.0 +-1 -1 : 1.682941969615793 +-1 1 : -0.0 +1 -1 : -0.0 +1 1 : -1.682941969615793 +-0.1 -0.2 : 0.11758932708149258 +0.1 0.2 : -0.11758932708149258 +-5 -5 : 7.86749131547214 +-5 5 : -0.0 +5 -5 : -0.0 +5 5 : -7.86749131547214 +0 0 0 : -0.0 +1 1 1 : -2.5244129544236893 +-5 -5 -5 : 11.80123697320821 +-5 -5 5 : 3.93374565773607 +-5 5 -5 : 3.93374565773607 +-5 5 5 : -3.93374565773607 +5 -5 -5 : 3.93374565773607 +5 -5 5 : -3.93374565773607 +5 5 -5 : -3.93374565773607 +5 5 5 : -11.80123697320821 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : 23.145593064008178 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : -23.145593064008178 +0 0 0 0 0 0 0 0 0 0 : -0.0 +1 1 1 1 1 1 1 1 1 1 : -8.414709848078965 diff --git a/env/tests/step_1.txt b/env/tests/step_1.txt new file mode 100644 index 0000000000000000000000000000000000000000..935549ecfd5202cd6c3e2bf3ec9c0fee07fe15f0 --- /dev/null +++ b/env/tests/step_1.txt @@ -0,0 +1,32 @@ +# Test data for Step function, with coeffs a_i= (1,1,...,1) and bias a_0 = 1 +0 : 1 +0.5 : 1 +1 : 2 +-0.5 : 0 +-5 : -4 +5 : 6 +0 0 : 1 +-1 -1 : -1 +-1 1 : 1 +1 -1 : 1 +1 1 : 3 +-0.1 -0.2 : -1 +0.1 0.2 : 1 +-5 -5 : -9 +-5 5 : 1 +5 -5 : 1 +5 5 : 11 +0 0 0 : 1 +1 1 1 : 4 +-5 -5 -5 : -14 +-5 -5 5 : -4 +-5 5 -5 : -4 +-5 5 5 : 6 +5 -5 -5 : -4 +5 -5 5 : 6 +5 5 -5 : 6 +5 5 5 : 16 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : -54 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : 46 +0 0 0 0 0 0 0 0 0 0 : 1 +1 1 1 1 1 1 1 1 1 1 : 11 diff --git a/env/tests/step_2.txt b/env/tests/step_2.txt new file mode 100644 index 0000000000000000000000000000000000000000..c309b5bff4b0be0a5d7d3e3df63d3f25450398c1 --- /dev/null +++ b/env/tests/step_2.txt @@ -0,0 +1,32 @@ +# Test data for Step function, with coeffs a_i = (2,3,...,D+1) and bias a_0 = 1 +0 : 1 +0.5 : 2 +1 : 3 +-0.5 : 0 +-5 : -9 +5 : 11 +0 0 : 1 +-1 -1 : -4 +-1 1 : 2 +1 -1 : 0 +1 1 : 6 +-0.1 -0.2 : -1 +0.1 0.2 : 1 +-5 -5 : -24 +-5 5 : 6 +5 -5 : -4 +5 5 : 26 +0 0 0 : 1 +1 1 1 : 10 +-5 -5 -5 : -44 +-5 -5 5 : -4 +-5 5 -5 : -14 +-5 5 5 : 26 +5 -5 -5 : -24 +5 -5 5 : 16 +5 5 -5 : 6 +5 5 5 : 46 +-0.1 -1.2 -2.3 -3.4 -4.5 -5.6 -6.7 -7.8 -8.9 -9.1 : -413 +0.1 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 9.1 : 408 +0 0 0 0 0 0 0 0 0 0 : 1 +1 1 1 1 1 1 1 1 1 1 : 66