use std::str::FromStr; use crate::test_infra::{load_test_file, DataArrOfReals}; use rand::Rng; #[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 } #[derive(Debug, Clone, PartialEq)] pub enum BinaryStringConversionError { DimensionMismatch, NoBounds } impl BinaryString { pub fn new(vec: Vec) -> BinaryString { BinaryString { vec } } pub fn vec(&self) -> &Vec { &self.vec } pub fn perturb(self: &Self, rng: &mut TRng, p: f64) -> Self where TRng : Rng { BinaryString::new( self.into_iter() .map(|c| if rng.random::() <= p { 1 - *c } else { *c }) .collect::>() ) } fn to_real_internal<'a, T: DoubleEndedIterator>(vec: T, len: usize, min: f64, max: f64) -> f64 { let diff = max - min; let len = len as i32; let max_represent_num = 2f64.powi(len) - 1.0; let represented_num = vec .rev() .enumerate() .map(|(bit, c)| diff * (*c as f64) * 2f64.powi(bit as i32)) .sum::(); min + (represented_num / max_represent_num) } pub fn to_real_single(self: &Self, min: f64, max: f64) -> f64 { BinaryString::to_real_internal(self.vec.iter(), self.vec.len(), min, max) } pub fn to_real(self: &Self, bounds: &Vec) -> Result, BinaryStringConversionError> { if bounds.len() == 0 { return Err(BinaryStringConversionError::NoBounds); } let chunk_size = self.vec.len() / bounds.len(); if self.vec.len() % bounds.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::>()) } } impl FromStr for BinaryString { type Err = String; fn from_str(s: &str) -> Result { let binary_vec: Vec = s .chars() // skip spaces .filter(|c| *c != ' ') // Map ones and zeros .map(|c| match c { '0' => Ok(0), '1' => Ok(1), _ => Err(format!("Invalid binary character: {}", c)), }) .collect::, Self::Err>>()?; Ok(BinaryString::new(binary_vec)) } } impl<'a> IntoIterator for &'a BinaryString { type Item = &'a i8; type IntoIter = std::slice::Iter<'a, i8>; fn into_iter(self) -> Self::IntoIter { self.vec.iter() } } #[test] fn test_binary_string_to_real_single() { assert_eq!( BinaryString::new(vec![1]) .to_real_single(0.0, 32.0), 32.0 ); assert_eq!( BinaryString::new(vec![1, 1]) .to_real_single(0.0, 32.0), 32.0 ); assert_eq!( BinaryString::new(vec![0, 1]) .to_real_single(0.0, 32.0), 32.0 / 3.0 ); assert_eq!( BinaryString::new(vec![0, 0]) .to_real_single(-16.0, 16.0), -16.0 ); assert_eq!( 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]) .to_real_single(0.0, 31.0), 31.0 ); assert_eq!( BinaryString::new(vec![0, 0, 0, 1, 0]) .to_real_single(0.0, 31.0), 2.0 ); assert_eq!( BinaryString::new(vec![1; 512]) .to_real_single(0.0, 31.0), 31.0 ); } fn test_binary_string_to_real(file_name: &str, bounds: Vec) { let data = load_test_file::(file_name); for test in data { let res = BinaryString::new(test.inp) .to_real(&bounds); if !test.out.valid { assert_eq!( res, Err(BinaryStringConversionError::DimensionMismatch) ); } else { assert_eq!( res.unwrap(), test.out.vec ); } } } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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)] ); } #[test] 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::>() ); }