pub(crate) mod implementations;
pub(crate) mod traits;
use std::fmt::Display;
use std::str::FromStr;
pub use implementations::{
binary_solution::BinarySolutionBuilder,
pareto_crowding_solution::{
MultiObjectiveRealSolutionBuilder, MultiObjectiveVectorRealSolutionBuilder,
},
permutation_solution::PermutationSolutionBuilder,
real_solution::RealSolutionBuilder,
string_solution::StringSolutionBuilder,
};
pub use traits::{Dominance, ParetoCrowdingDistanceQuality};
#[derive(Clone, Debug)]
pub struct Solution<T, Q = f64> {
variables: Vec<T>,
quality: Option<Q>,
}
impl<T: Display, Q: Display> Solution<T, Q> {
pub fn new(variables: Vec<T>) -> Self {
Self {
variables,
quality: None,
}
}
pub fn variables(&self) -> &[T] {
&self.variables
}
pub fn variables_mut(&mut self) -> &mut [T] {
self.invalidate();
&mut self.variables
}
pub fn set_variables(&mut self, variables: Vec<T>) {
self.variables = variables;
self.invalidate();
}
pub fn num_variables(&self) -> usize {
self.variables.len()
}
pub fn copy(&self) -> Self
where
T: Clone,
Q: Clone,
{
self.clone()
}
pub fn get_variable(&self, index: usize) -> Option<&T> {
self.variables.get(index)
}
pub fn get_variable_mut(&mut self, index: usize) -> Option<&mut T> {
self.invalidate();
self.variables.get_mut(index)
}
pub fn set_variable(&mut self, index: usize, value: T) -> bool {
if let Some(variable) = self.variables.get_mut(index) {
*variable = value;
self.invalidate();
true
} else {
false
}
}
pub fn swap_variables(&mut self, i: usize, j: usize) -> bool {
if i < self.variables.len() && j < self.variables.len() {
self.variables.swap(i, j);
self.invalidate();
true
} else {
false
}
}
pub fn quality(&self) -> Option<&Q> {
self.quality.as_ref()
}
pub fn quality_mut(&mut self) -> Option<&mut Q> {
self.quality.as_mut()
}
pub fn set_quality(&mut self, quality: Q) {
self.quality = Some(quality);
}
pub fn has_quality(&self) -> bool {
self.quality.is_some()
}
pub fn invalidate(&mut self) {
self.quality = None;
}
pub fn encode(&self) -> String {
let quality_string = match &self.quality {
Some(q) => q.to_string(),
None => "None".to_string(),
};
let genes: String = self
.variables
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(",");
format!("{}|{}", genes, quality_string)
}
pub fn decode(data: &str) -> Result<Self, String>
where
T: FromStr,
Q: FromStr,
{
let parts: Vec<&str> = data.split('|').collect();
if parts.len() != 2 {
return Err("Invalid format: 'genes|fitness' expected".to_string());
}
let variables: Vec<T> = parts[0]
.split(',')
.filter(|s| !s.is_empty())
.map(|s| {
s.parse::<T>()
.map_err(|_| "Error parsing variable (T)".to_string())
})
.collect::<Result<Vec<T>, String>>()?;
let quality = if parts[1] == "None" || parts[1].is_empty() {
None
} else {
Some(
parts[1]
.parse::<Q>()
.map_err(|_| "Error parsing quality (Q)".to_string())?,
)
};
Ok(Self { variables, quality })
}
}
impl<T, Q> Solution<T, Q>
where
Q: traits::Dominance,
{
pub fn dominates(&self, other: &Self) -> bool {
match (&self.quality, &other.quality) {
(Some(a), Some(b)) => a.dominates(b),
_ => false,
}
}
}
impl<T> Solution<T, f64> {
pub fn try_quality_value(&self) -> Option<f64> {
self.quality
}
pub fn quality_value(&self) -> f64 {
self.quality
.expect("quality_value() called on a solution without evaluated quality")
}
}
fn finalize_scalar_solution<T: Display>(variables: Vec<T>, quality: Option<f64>) -> Solution<T> {
let mut solution = Solution::new(variables);
if let Some(quality) = quality {
solution.set_quality(quality);
}
solution
}
fn apply_bounds(
mut variables: Vec<f64>,
lower_bounds: &Option<Vec<f64>>,
upper_bounds: &Option<Vec<f64>>,
) -> Vec<f64> {
if let (Some(lower), Some(upper)) = (lower_bounds, upper_bounds) {
debug_assert_eq!(
lower.len(),
variables.len(),
"lower_bounds length should match variables length"
);
debug_assert_eq!(
upper.len(),
variables.len(),
"upper_bounds length should match variables length"
);
for ((value, &lo), &up) in variables.iter_mut().zip(lower.iter()).zip(upper.iter()) {
*value = value.clamp(lo, up);
}
}
variables
}
#[cfg(test)]
mod tests {
use super::Solution;
#[test]
fn quality_helpers_work_as_expected() {
let mut s: Solution<i32> = Solution::new(vec![1, 2, 3]);
assert!(!s.has_quality());
assert_eq!(s.quality(), None);
s.set_quality(4.0);
assert!(s.has_quality());
assert_eq!(s.quality().copied(), Some(4.0));
s.invalidate();
assert!(!s.has_quality());
assert_eq!(s.quality(), None);
}
#[test]
fn invalidate_clears_quality() {
let mut s: Solution<bool> = Solution::new(vec![true, false]);
s.set_quality(10.0);
assert_eq!(s.quality().copied(), Some(10.0));
s.invalidate();
assert_eq!(s.quality(), None);
}
#[test]
fn try_quality_value_reflects_presence() {
let mut s: Solution<i32> = Solution::new(vec![1, 2, 3]);
assert_eq!(s.try_quality_value(), None);
s.set_quality(1.25);
assert_eq!(s.try_quality_value(), Some(1.25));
}
#[test]
#[should_panic(expected = "quality_value() called on a solution without evaluated quality")]
fn quality_value_panics_when_missing() {
let s: Solution<i32> = Solution::new(vec![1, 2, 3]);
let _ = s.quality_value();
}
}