~ruther/ctu-fee-eoa

41b0eb7ee7ae129303c1c0f7d818c52904edc001 — Rutherther a month ago ef07dac
refactor: Use OVector instead of SVector in library
M env/src/binary_string.rs => env/src/binary_string.rs +41 -30
@@ 1,4 1,5 @@
use std::str::FromStr;
use nalgebra::{allocator::Allocator, Const, DefaultAllocator, Dim, DimName, Dynamic, OVector, SVector, U1};
use rand::{Rng, RngCore};

#[derive(Debug, Clone, PartialEq)]


@@ 73,20 74,20 @@ impl BinaryString {
        BinaryString::to_real_internal(self.vec.iter(), self.vec.len(), min, max)
    }

    pub fn to_real(self: &Self, bounds: &Vec<Bounds>) -> Result<Vec<f64>, BinaryStringConversionError> {
        if bounds.len() == 0 {
            return Err(BinaryStringConversionError::NoBounds);
        }

        let chunk_size = self.vec.len() / bounds.len();
        if self.vec.len() % bounds.len() != 0 {
    pub fn to_real<TDim>(self: &Self, min: &OVector<f64, TDim>, max: &OVector<f64, TDim>) -> Result<OVector<f64, TDim>, BinaryStringConversionError>
    where
        TDim: Dim,
        DefaultAllocator: Allocator<TDim>,
    {
        let chunk_size = self.vec.len() / min.len();
        if self.vec.len() % min.len() != 0 {
            return Err(BinaryStringConversionError::DimensionMismatch);
        }

        Ok(self.vec.chunks(chunk_size)
            .zip(bounds)
            .map(|(chunk, bound)| BinaryString::to_real_internal(chunk.iter(), chunk_size, bound.min(), bound.max()))
            .collect::<Vec<f64>>())
        let iter = self.vec.chunks(chunk_size)
                .zip(min.iter().zip(max))
            .map(|(chunk, (&min, &max))| BinaryString::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))
    }
}



@@ 121,7 122,8 @@ impl<'a> IntoIterator for &'a BinaryString {

#[cfg(test)]
pub mod tests {
    use crate::{binary_string::BinaryString, test_infra::{load_test_file, DataArrOfReals}};
    use crate::{binary_string::{BinaryString, BinaryStringConversionError}, test_infra::{load_test_file, DataArrOfReals}};
    use nalgebra::SVector;

    use super::Bounds;



@@ 170,15 172,12 @@ pub mod tests {
        );
    }

    #[cfg(test)]
    fn test_binary_string_to_real(file_name: &str, bounds: Vec<Bounds>) {
        use crate::binary_string::BinaryStringConversionError;

    fn test_binary_string_to_real<const LEN: usize>(file_name: &str, min: SVector<f64, LEN>, max: SVector<f64, LEN>) {
        let data = load_test_file::<i8, DataArrOfReals>(file_name);

        for test in data {
            let res = BinaryString::new(test.inp)
                .to_real(&bounds);
                .to_real(&min, &max);
            if !test.out.valid {
                assert_eq!(
                    res,


@@ 187,7 186,7 @@ pub mod tests {
            }
            else {
                assert_eq!(
                    res.unwrap(),
                    res.unwrap().iter().map(|&x| x).collect::<Vec<_>>(),
                    test.out.vec
                );
            }


@@ 198,7 197,8 @@ pub mod tests {
    fn test_binary_string_to_real_1d_1() {
        test_binary_string_to_real(
            "tests/Bin2Real_1D_1.txt",
            vec![Bounds::new(0.0, 1.0)]
            SVector::<f64, 1>::new(0.0),
            SVector::<f64, 1>::new(1.0),
        );
    }



@@ 206,7 206,8 @@ pub mod tests {
    fn test_binary_string_to_real_1d_2() {
        test_binary_string_to_real(
            "tests/Bin2Real_1D_2.txt",
            vec![Bounds::new(0.0, 4095.0)]
            SVector::<f64, 1>::new(0.0),
            SVector::<f64, 1>::new(4095.0),
        );
    }



@@ 214,7 215,8 @@ pub mod tests {
    fn test_binary_string_to_real_1d_3() {
        test_binary_string_to_real(
            "tests/Bin2Real_1D_3.txt",
            vec![Bounds::new(-5.0, 5.0)]
            SVector::<f64, 1>::new(-5.0),
            SVector::<f64, 1>::new(5.0),
        );
    }



@@ 222,7 224,8 @@ pub mod tests {
    fn test_binary_string_to_real_2d_1() {
        test_binary_string_to_real(
            "tests/Bin2Real_2D_1.txt",
            vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
            SVector::<f64, 2>::new(0.0, 0.0),
            SVector::<f64, 2>::new(1.0, 1.0),
        );
    }



@@ 230,7 233,8 @@ pub mod tests {
    fn test_binary_string_to_real_2d_2() {
        test_binary_string_to_real(
            "tests/Bin2Real_2D_2.txt",
            vec![Bounds::new(0.0, 63.0), Bounds::new(-32.0, 31.0)]
            SVector::<f64, 2>::new(0.0, -32.0),
            SVector::<f64, 2>::new(63.0, 31.0),
        );
    }



@@ 238,7 242,8 @@ pub mod tests {
    fn test_binary_string_to_real_2d_3() {
        test_binary_string_to_real(
            "tests/Bin2Real_2D_3.txt",
            vec![Bounds::new(-5.0, 5.0), Bounds::new(0.0, 10.0)]
            SVector::<f64, 2>::new(-5.0, 0.0),
            SVector::<f64, 2>::new(5.0, 10.0),
        );
    }



@@ 246,7 251,8 @@ pub mod tests {
    fn test_binary_string_to_real_3d_1() {
        test_binary_string_to_real(
            "tests/Bin2Real_3D_1.txt",
            vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
            SVector::<f64, 3>::new(0.0, 0.0, 0.0),
            SVector::<f64, 3>::new(1.0, 1.0, 1.0),
        );
    }



@@ 254,7 260,8 @@ pub mod tests {
    fn test_binary_string_to_real_3d_2() {
        test_binary_string_to_real(
            "tests/Bin2Real_3D_2.txt",
            vec![Bounds::new(0.0, 15.0), Bounds::new(-8.0, 7.0), Bounds::new(-8.0, 8.0)]
            SVector::<f64, 3>::new(0.0, -8.0, -8.0),
            SVector::<f64, 3>::new(15.0, 7.0, 8.0),
        );
    }



@@ 262,7 269,8 @@ pub mod tests {
    fn test_binary_string_to_real_4d_1() {
        test_binary_string_to_real(
            "tests/Bin2Real_4D_1.txt",
            vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
            SVector::<f64, 4>::new(0.0, 0.0, 0.0, 0.0),
            SVector::<f64, 4>::new(1.0, 1.0, 1.0, 1.0),
        );
    }



@@ 270,7 278,8 @@ pub mod tests {
    fn test_binary_string_to_real_4d_2() {
        test_binary_string_to_real(
            "tests/Bin2Real_4D_2.txt",
            vec![Bounds::new(0.0, 7.0), Bounds::new(-4.0, 3.0), Bounds::new(-4.0, 4.0), Bounds::new(-8.0, 0.0)]
            SVector::<f64, 4>::new(0.0, -4.0, -4.0, -8.0),
            SVector::<f64, 4>::new(7.0, 3.0, 4.0, 0.0),
        );
    }



@@ 278,7 287,8 @@ pub mod tests {
    fn test_binary_string_to_real_6d_1() {
        test_binary_string_to_real(
            "tests/Bin2Real_6D_1.txt",
            vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
            SVector::<f64, 6>::zeros(),
            SVector::<f64, 6>::from_element(1.0),
        );
    }



@@ 286,7 296,8 @@ pub mod tests {
    fn test_binary_string_to_real_6d_2() {
        test_binary_string_to_real(
            "tests/Bin2Real_6D_2.txt",
            (0..=5).map(|x| Bounds::new(x as f64, (2 * (x + 1)) as f64)).collect::<Vec<_>>()
            SVector::<f64, 6>::from_iterator((0..=5).map(|x| x as f64)),
            SVector::<f64, 6>::from_iterator((0..=5).map(|x| (2 * (x + 1)) as f64)),
        );
    }
}

M env/src/fitness/mod.rs => env/src/fitness/mod.rs +22 -8
@@ 1,5 1,7 @@
use std::convert::Infallible;

use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, SVector};

use crate::binary_string::{BinaryString, BinaryStringConversionError, Bounds};

pub mod labs;


@@ 16,29 18,41 @@ pub trait FitnessFunction {
    fn fit(self: &Self, inp: &Self::In) -> Result<Self::Out, Self::Err>;
}

pub struct BinaryFitnessWrapper<TFitness> {
    bounds: Vec<Bounds>,
pub struct BinaryFitnessWrapper<D, TFitness>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    min: OVector<f64, D>,
    max: OVector<f64, D>,
    fitting_function: TFitness,
}

impl<TFitness> BinaryFitnessWrapper<TFitness> {
    pub fn new(fitting_function: TFitness, bounds: Vec<Bounds>) -> Self {
impl<D, TFitness> BinaryFitnessWrapper<D, TFitness>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    pub fn new(fitting_function: TFitness, min: OVector<f64, D>, max: OVector<f64, D>) -> Self {
        BinaryFitnessWrapper {
            fitting_function,
            bounds,
            min,
            max
        }
    }
}

impl<TFitness> FitnessFunction for BinaryFitnessWrapper<TFitness>
impl<D, TFitness> FitnessFunction for BinaryFitnessWrapper<D, TFitness>
where
    TFitness: FitnessFunction<In = Vec<f64>, Out = f64, Err = Infallible>
    D: Dim,
    DefaultAllocator: Allocator<D>,
    TFitness: FitnessFunction<In = OVector<f64, D>, Out = f64, Err = Infallible>
{
    type In = BinaryString;
    type Out = f64;
    type Err = BinaryStringConversionError;

    fn fit(self: &Self, inp: &BinaryString) -> Result<f64, BinaryStringConversionError> {
        Ok(self.fitting_function.fit(&inp.to_real(&self.bounds)?).unwrap())
        Ok(self.fitting_function.fit(&inp.to_real(&self.min, &self.max)?).unwrap())
    }
}

M env/src/fitness/real.rs => env/src/fitness/real.rs +69 -47
@@ 1,16 1,24 @@
use std::{convert::Infallible, f64::consts::PI, marker::PhantomData};

use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, SVector};
use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, SVector, U1};

use super::FitnessFunction;

pub struct Linear<const LEN: usize> {
pub struct Linear<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    a0: f64,
    a: SVector<f64, LEN>,
    a: OVector<f64, D>,
}

impl<const LEN: usize> Linear<LEN> {
    pub fn new(a0: f64, a: SVector<f64, LEN>) -> Self {
impl<D> Linear<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    pub fn new(a0: f64, a: OVector<f64, D>) -> Self {
        Self {
            a0,
            a


@@ 18,8 26,13 @@ impl<const LEN: usize> Linear<LEN> {
    }
}

impl<const LEN: usize> FitnessFunction for Linear<LEN> {
    type In = SVector<f64, LEN>;
impl<D> FitnessFunction for Linear<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>,
    DefaultAllocator: Allocator<U1, D>
{
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;



@@ 28,13 41,21 @@ impl<const LEN: usize> FitnessFunction for Linear<LEN> {
    }
}

pub struct Step<const LEN: usize> {
pub struct Step<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>,
{
    a0: f64,
    a: SVector<f64, LEN>,
    a: OVector<f64, D>,
}

impl<const LEN: usize> Step<LEN> {
    pub fn new(a0: f64, a: SVector<f64, LEN>) -> Self {
impl<D> Step<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>,
{
    pub fn new(a0: f64, a: OVector<f64, D>) -> Self {
        Self {
            a0,
            a


@@ 42,8 63,12 @@ impl<const LEN: usize> Step<LEN> {
    }
}

impl<const LEN: usize> FitnessFunction for Step<LEN> {
    type In = SVector<f64, LEN>;
impl<D> FitnessFunction for Step<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>,
{
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;



@@ 52,11 77,11 @@ impl<const LEN: usize> FitnessFunction for Step<LEN> {
    }
}

pub struct Rastrigin<TDim: Dim> {
    _phantom: PhantomData<TDim>
pub struct Rastrigin<D: Dim> {
    _phantom: PhantomData<D>
}

impl<TDim: Dim> Rastrigin<TDim> {
impl<D: Dim> Rastrigin<D> {
    pub fn new() -> Self {
        Self {
            _phantom: PhantomData


@@ 64,11 89,11 @@ impl<TDim: Dim> Rastrigin<TDim> {
    }
}

impl<TDim: Dim> FitnessFunction for Rastrigin<TDim>
impl<D: Dim> FitnessFunction for Rastrigin<D>
where
    DefaultAllocator: Allocator<TDim>
    DefaultAllocator: Allocator<D>
{
    type In = OVector<f64, TDim>;
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;



@@ 80,8 105,8 @@ where
    }
}

pub struct Griewank<TDim: Dim> {
    _phantom: PhantomData<TDim>
pub struct Griewank<D: Dim> {
    _phantom: PhantomData<D>
}

impl<TDim: Dim> Griewank<TDim> {


@@ 92,11 117,11 @@ impl<TDim: Dim> Griewank<TDim> {
    }
}

impl<TDim: Dim> FitnessFunction for Griewank<TDim>
impl<D: Dim> FitnessFunction for Griewank<D>
where
    DefaultAllocator: Allocator<TDim>
    DefaultAllocator: Allocator<D>
{
    type In = OVector<f64, TDim>;
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;



@@ 109,16 134,24 @@ where
    }
}

pub struct Schwefel<const LEN: usize>;
pub struct Schwefel<D: Dim> {
    _phantom: PhantomData<D>
}

impl<const LEN: usize> Schwefel<LEN> {
impl<D: Dim> Schwefel<D> {
    pub fn new() -> Self {
        Self
        Self {
            _phantom: PhantomData
        }
    }
}

impl<const LEN: usize> FitnessFunction for Schwefel<LEN> {
    type In = SVector<f64, LEN>;
impl<D> FitnessFunction for Schwefel<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;



@@ 139,18 172,13 @@ pub mod tests {

    #[test]
    fn test_linear_1() {
        const MAX_LEN: usize = 10;

        let data = load_test_file::<f64, f64>("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::<Vec<_>>();
            let inp = SVector::<f64, MAX_LEN>::from_vec(filled);
            let offset = OVector::<f64, Dyn>::from_element(test.inp.len(), 1.0);
            let inp = OVector::<f64, Dyn>::from_vec(test.inp);

            let linear = Linear::new(1.0, offset);

            assert_eq!(
                linear.fit(&inp).unwrap(),


@@ 161,18 189,12 @@ pub mod tests {

    #[test]
    fn test_linear_2() {
        const MAX_LEN: usize = 10;

        let data = load_test_file::<f64, f64>("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::<Vec<_>>();
            let inp = SVector::<f64, MAX_LEN>::from_vec(filled);
            let offset = OVector::<f64, Dyn>::from_iterator(test.inp.len(), (2..=test.inp.len()+1).map(|x| x as f64));
            let linear = Linear::new(1.0, offset);
            let inp = OVector::<f64, Dyn>::from_vec(test.inp);

            assert_eq!(
                linear.fit(&inp).unwrap(),

M env/src/fitness/rosenbrock.rs => env/src/fitness/rosenbrock.rs +25 -9
@@ 1,21 1,35 @@
use std::convert::Infallible;
use std::{convert::Infallible, marker::PhantomData};
use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector};

use super::FitnessFunction;

pub struct Rosenbrock;
pub struct Rosenbrock<D> {
    _phantom: PhantomData<D>
}

impl Rosenbrock {
impl<D> Rosenbrock<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    pub fn new() -> Self {
        Rosenbrock
        Rosenbrock {
            _phantom: PhantomData
        }
    }
}

impl FitnessFunction for Rosenbrock {
    type In = Vec<f64>;
impl<D> FitnessFunction for Rosenbrock<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;

    fn fit(self: &Self, inp: &Vec<f64>) -> Result<f64, Infallible> {
        Ok(inp.windows(2)
    fn fit(self: &Self, inp: &OVector<f64, D>) -> Result<f64, Infallible> {
        Ok(inp.as_slice().windows(2)
           .map(|xs| 100.0 * (xs[1] - xs[0].powi(2)).powi(2) + (1.0 - xs[0]).powi(2))
           .sum())
    }


@@ 23,6 37,8 @@ impl FitnessFunction for Rosenbrock {

#[cfg(test)]
pub mod tests {
    use nalgebra::{Dyn, OVector};

    use crate::{fitness::{rosenbrock::Rosenbrock, FitnessFunction}, test_infra::load_test_file};

    #[test]


@@ 31,7 47,7 @@ pub mod tests {

        for test in data {
            assert_eq!(
                Rosenbrock::new().fit(&test.inp).unwrap(),
                Rosenbrock::<Dyn>::new().fit(&OVector::<_, Dyn>::from_vec(test.inp)).unwrap(),
                test.out
            )
        }

M env/src/fitness/sphere.rs => env/src/fitness/sphere.rs +25 -8
@@ 1,24 1,38 @@
use std::convert::Infallible;
use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, SVector};

use super::FitnessFunction;

pub struct Sphere {
    offset: Vec<f64>
pub struct Sphere<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    offset: OVector<f64, D>
}

impl Sphere {
    pub fn new(offset: Vec<f64>) -> Self {
impl<D> Sphere<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    pub fn new(offset: OVector<f64, D>) -> Self {
        Sphere {
            offset
        }
    }
}

impl FitnessFunction for Sphere {
    type In = Vec<f64>;
impl<D> FitnessFunction for Sphere<D>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    type In = OVector<f64, D>;
    type Out = f64;
    type Err = Infallible;

    fn fit(self: &Self, chromosome: &Vec<f64>) -> Result<f64, Infallible> {
    fn fit(self: &Self, chromosome: &OVector<f64, D>) -> Result<f64, Infallible> {
        Ok(chromosome
           .iter()
           .zip(&self.offset)


@@ 29,6 43,8 @@ impl FitnessFunction for Sphere {

#[cfg(test)]
pub mod tests {
    use nalgebra::{Dyn, OVector};

    use crate::{fitness::{sphere::Sphere, FitnessFunction}, test_infra::load_test_file};

    #[test]


@@ 37,7 53,8 @@ pub mod tests {

        for test in data {
            assert_eq!(
                Sphere::new(vec![1.0; 10]).fit(&test.inp).unwrap(),
                Sphere::<Dyn>::new(OVector::<_, Dyn>::from_vec(vec![1.0; test.inp.len()]))
                    .fit(&OVector::<_, Dyn>::from_vec(test.inp)).unwrap(),
                test.out
            )
        }

M env/src/local_search/mod.rs => env/src/local_search/mod.rs +7 -5
@@ 127,10 127,11 @@ pub mod tests {
    fn test_local_search_sphere() {
        let optimum = BinaryString::new(vec![0, 0, 1, 0, 0,
                                             0, 0, 1, 0, 0]);
        let bounds = vec![Bounds::new(0.0, 31.0), Bounds::new(0.0, 31.0)];
        let optimum_real = optimum.to_real(&bounds).unwrap();
        let min = SVector::<f64, 2>::from_element(0.0);
        let max = SVector::<f64, 2>::from_element(31.0);
        let optimum_real = optimum.to_real(&min, &max).unwrap();
        let sphere = Sphere::new(optimum_real);
        let sphere_wrapped = BinaryFitnessWrapper::new(sphere, bounds);
        let sphere_wrapped = BinaryFitnessWrapper::new(sphere, min, max);

        let result = local_search_first_improving(
            &sphere_wrapped,


@@ 228,8 229,9 @@ pub mod tests {
        let rosenbrock = Rosenbrock::new();
        let optimum = BinaryString::new(vec![1, 0, 0, 0, 1, 1, 0, 0, 0, 1]);

        let bounds = vec![Bounds::new(-16.0, 15.0), Bounds::new(-16.0, 15.0)];
        let rosenbrock_wrapped = BinaryFitnessWrapper::new(rosenbrock, bounds);
        let min = SVector::<f64, 2>::from_element(-16.0);
        let max = SVector::<f64, 2>::from_element(15.0);
        let rosenbrock_wrapped = BinaryFitnessWrapper::new(rosenbrock, min, max);

        let result = local_search_first_improving(
            &rosenbrock_wrapped,