use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::ops::RangeBounds;
use std::sync::Arc;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use crate::core::{DataValue, OError, Problem, VariableValue};
use crate::utils::hasmap_eq_with_nans;
#[cfg(not(feature = "python"))]
#[derive(Debug, Clone)]
pub struct Individual {
problem: Arc<Problem>,
variable_values: HashMap<String, VariableValue>,
constraint_values: HashMap<String, f64>,
objective_values: HashMap<String, f64>,
evaluated: bool,
data: HashMap<String, DataValue>,
}
#[cfg(feature = "python")]
#[derive(Debug, Clone)]
#[pyclass]
pub struct Individual {
problem: Arc<Problem>,
#[pyo3(get)]
variable_values: HashMap<String, VariableValue>,
#[pyo3(get)]
constraint_values: HashMap<String, f64>,
#[pyo3(get)]
objective_values: HashMap<String, f64>,
#[pyo3(get)]
evaluated: bool,
#[pyo3(get)]
data: HashMap<String, DataValue>,
}
impl PartialEq for Individual {
fn eq(&self, other: &Self) -> bool {
self.variable_values == other.variable_values
&& hasmap_eq_with_nans(&self.constraint_values, &other.constraint_values)
&& hasmap_eq_with_nans(&self.objective_values, &other.objective_values)
&& self.data == other.data
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IndividualExport {
pub constraint_values: HashMap<String, f64>,
pub objective_values: HashMap<String, f64>,
pub constraint_violation: f64,
pub variable_values: HashMap<String, VariableValue>,
pub is_feasible: bool,
pub evaluated: bool,
pub data: HashMap<String, DataValue>,
}
impl Display for Individual {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Individual(variables={:?}, objectives={:?},constraints={:?})",
self.variable_values, self.objective_values, self.constraint_values,
)
}
}
impl Individual {
pub fn new(problem: Arc<Problem>) -> Self {
let mut variable_values: HashMap<String, VariableValue> = HashMap::new();
for (variable_name, var_type) in problem.variables() {
variable_values.insert(variable_name, var_type.generate_random_value());
}
let mut objective_values: HashMap<String, f64> = HashMap::new();
for objective_name in problem.objective_names() {
objective_values.insert(objective_name, f64::NAN);
}
let mut constraint_values: HashMap<String, f64> = HashMap::new();
for constraint_name in problem.constraint_names() {
constraint_values.insert(constraint_name, f64::NAN);
}
Self {
problem,
variable_values,
constraint_values,
objective_values,
evaluated: false,
data: HashMap::new(),
}
}
pub fn problem(&self) -> Arc<Problem> {
self.problem.clone()
}
pub fn get_real_value(&self, name: &str) -> Result<f64, OError> {
self.get_variable_value(name)?
.as_real()
.map_err(|_| OError::WrongVariableTypeWithName(name.to_string(), "real".to_string()))
}
pub fn get_integer_value(&self, name: &str) -> Result<i64, OError> {
self.get_variable_value(name)?
.as_integer()
.map_err(|_| OError::WrongVariableTypeWithName(name.to_string(), "integer".to_string()))
}
pub(crate) fn clone_variables(&self) -> Self {
let mut i = Self::new(self.problem.clone());
for (var_name, var_value) in self.variable_values.iter() {
i.update_variable(var_name, var_value.clone()).unwrap()
}
i
}
pub fn update_variable(&mut self, name: &str, value: VariableValue) -> Result<(), OError> {
if !self.variable_values.contains_key(name) {
return Err(OError::NonExistingName(
"variable".to_string(),
name.to_string(),
));
}
if !value.match_type(name, self.problem.clone())? {
return Err(OError::NonMatchingVariableType(name.to_string()));
}
if let Some(x) = self.variable_values.get_mut(name) {
*x = value;
}
Ok(())
}
pub fn update_objective(&mut self, name: &str, value: f64) -> Result<(), OError> {
if !self.objective_values.contains_key(name) {
return Err(OError::NonExistingName(
"objective".to_string(),
name.to_string(),
));
}
if value.is_nan() {
return Err(OError::NaN("objective".to_string(), name.to_string()));
}
let sign = match self.problem.is_objective_minimised(name)? {
true => 1.0,
false => -1.0,
};
if let Some(x) = self.objective_values.get_mut(name) {
*x = sign * value;
}
Ok(())
}
pub(crate) fn update_constraint(&mut self, name: &str, value: f64) -> Result<(), OError> {
if !self.constraint_values.contains_key(name) {
return Err(OError::NonExistingName(
"constrain".to_string(),
name.to_string(),
));
}
if value.is_nan() {
return Err(OError::NaN("constraint".to_string(), name.to_string()));
}
if let Some(x) = self.constraint_values.get_mut(name) {
*x = value;
}
Ok(())
}
pub fn transform_objective_values<F: Fn(f64, String) -> Result<f64, OError>>(
&self,
transform: F,
) -> Result<Vec<f64>, OError> {
self.problem
.objective_names()
.iter()
.map(|obj_name| {
let val = self.get_objective_value(obj_name)?;
transform(val, obj_name.clone())
})
.collect()
}
pub fn is_evaluated(&self) -> bool {
self.evaluated
}
pub fn set_evaluated(&mut self) {
self.evaluated = true;
}
pub fn set_data(&mut self, name: &str, value: DataValue) {
self.data.insert(name.to_string(), value);
}
pub fn get_data(&self, name: &str) -> Result<DataValue, OError> {
self.data
.get(name)
.cloned()
.ok_or(OError::WrongDataName(name.to_string()))
}
pub fn serialise(&self) -> IndividualExport {
let mut objective_values = self.objective_values.clone();
for name in self.problem.objective_names() {
match self.problem.is_objective_minimised(&name) {
Ok(is_minimised) => {
if !is_minimised {
*objective_values.get_mut(&name).unwrap() *= -1.0;
}
}
Err(_) => continue,
}
}
IndividualExport {
constraint_values: self.constraint_values.clone(),
objective_values,
constraint_violation: self.constraint_violation(),
variable_values: self.variable_values.clone(),
is_feasible: self.is_feasible(),
evaluated: self.evaluated,
data: self.data.clone(),
}
}
pub fn deserialise(data: &IndividualExport, problem: Arc<Problem>) -> Result<Self, OError> {
let mut ind = Individual::new(problem.clone());
for (var_name, var_value) in data.variable_values.iter() {
ind.update_variable(var_name, var_value.clone())?;
}
for (obj_name, obj_value) in data.objective_values.iter() {
ind.update_objective(obj_name, *obj_value)?;
}
for (const_name, const_value) in data.constraint_values.iter() {
ind.update_constraint(const_name, *const_value)?;
}
ind.set_evaluated();
Ok(ind)
}
}
#[cfg_attr(feature = "python", pymethods)]
impl Individual {
pub fn constraint_violation(&self) -> f64 {
self.problem
.constraints()
.iter()
.map(|(name, c)| c.constraint_violation(self.constraint_values[name]))
.sum()
}
pub fn is_feasible(&self) -> bool {
for (name, constraint_value) in self.constraint_values.iter() {
if !self.problem.constraint_names().contains(name) {
continue;
}
if !self
.problem
.get_constraint(name)
.unwrap()
.is_met(*constraint_value)
{
return false;
}
}
true
}
pub fn variables(&self) -> HashMap<String, VariableValue> {
self.variable_values.clone()
}
pub fn constraints(&self) -> HashMap<String, f64> {
self.constraint_values.clone()
}
pub fn objectives(&self) -> HashMap<String, f64> {
self.objective_values.clone()
}
pub fn get_variable_value(&self, name: &str) -> Result<&VariableValue, OError> {
if !self.variable_values.contains_key(name) {
return Err(OError::NonExistingName(
"variable".to_string(),
name.to_string(),
));
}
Ok(&self.variable_values[name])
}
pub fn get_variable_values(&self) -> Result<Vec<&VariableValue>, OError> {
self.problem
.variable_names()
.iter()
.map(|var_name| self.get_variable_value(var_name))
.collect()
}
pub fn get_constraint_value(&self, name: &str) -> Result<f64, OError> {
if !self.constraint_values.contains_key(name) {
return Err(OError::NonExistingName(
"constraint".to_string(),
name.to_string(),
));
}
Ok(self.constraint_values[name])
}
pub fn get_objective_value(&self, name: &str) -> Result<f64, OError> {
if !self.objective_values.contains_key(name) {
return Err(OError::NonExistingName(
"objective".to_string(),
name.to_string(),
));
}
Ok(self.objective_values[name])
}
pub fn get_objective_values(&self) -> Result<Vec<f64>, OError> {
self.problem
.objective_names()
.iter()
.map(|obj_name| self.get_objective_value(obj_name))
.collect()
}
pub fn data(&self) -> HashMap<String, DataValue> {
self.data.clone()
}
}
#[derive(Clone, Default, Debug)]
pub struct Population(Vec<Individual>);
impl Population {
pub fn new() -> Self {
Self::default()
}
pub fn new_with(individuals: Vec<Individual>) -> Self {
Self(individuals)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn individuals(&self) -> &[Individual] {
self.0.as_ref()
}
pub fn individual(&self, index: usize) -> Option<&Individual> {
self.0.get(index)
}
pub fn individuals_as_mut(&mut self) -> &mut [Individual] {
self.0.as_mut()
}
pub fn add_new_individuals(&mut self, individuals: Vec<Individual>) {
self.0.extend(individuals);
}
pub fn add_individual(&mut self, individual: Individual) {
self.0.push(individual);
}
pub fn drain<R>(&mut self, range_to_remove: R) -> Vec<Individual>
where
R: RangeBounds<usize>,
{
self.0.drain(range_to_remove).collect()
}
pub fn init(problem: Arc<Problem>, number_of_individuals: usize) -> Self {
let mut population: Vec<Individual> = vec![];
for _ in 0..number_of_individuals {
population.push(Individual::new(problem.clone()));
}
Self(population)
}
pub fn serialise(&self) -> Vec<IndividualExport> {
self.0.iter().map(|i| i.serialise()).collect()
}
pub fn deserialise(
data: &[IndividualExport],
problem: Arc<Problem>,
) -> Result<Population, OError> {
let individuals = data
.iter()
.map(|d| Individual::deserialise(d, problem.clone()))
.collect::<Result<Vec<Individual>, OError>>()?;
Ok(Population::new_with(individuals))
}
}
pub trait Individuals {
fn individual(&self, index: usize) -> Result<&Individual, OError>;
fn objective_values(&self, name: &str) -> Result<Vec<f64>, OError>;
fn to_real_vec(&self, name: &str) -> Result<Vec<f64>, OError>;
}
pub trait IndividualsMut {
fn individual_as_mut(&mut self, index: usize) -> Result<&mut Individual, OError>;
}
macro_rules! impl_individuals {
( $($type:ty),* $(,)? ) => {
$(
impl Individuals for $type {
fn individual(&self, index: usize) -> Result<&Individual, OError> {
self.get(index)
.ok_or(OError::NonExistingIndex("individual".to_string(), index))
}
fn objective_values(&self, name: &str) -> Result<Vec<f64>, OError> {
self.iter().map(|i| i.get_objective_value(name)).collect()
}
fn to_real_vec(&self, name: &str) -> Result<Vec<f64>, OError> {
self.iter().map(|i| i.get_real_value(name)).collect()
}
}
)*
};
}
impl IndividualsMut for &mut [Individual] {
fn individual_as_mut(&mut self, index: usize) -> Result<&mut Individual, OError> {
self.get_mut(index)
.ok_or(OError::NonExistingIndex("individual".to_string(), index))
}
}
impl_individuals!(&[Individual]);
impl_individuals!(&mut [Individual]);
impl_individuals!(Vec<Individual>);
#[cfg(test)]
mod test {
use std::sync::Arc;
use crate::core::utils::dummy_evaluator;
use crate::core::{
BoundedNumber, Constraint, Individual, Objective, ObjectiveDirection, Problem,
RelationalOperator, VariableType,
};
#[test]
fn test_non_existing_data() {
let objectives = vec![Objective::new("objX", ObjectiveDirection::Minimise)];
let var_types = vec![VariableType::Real(
BoundedNumber::new("X1", 0.0, 2.0).unwrap(),
)];
let e = dummy_evaluator();
let problem = Arc::new(Problem::new(objectives, var_types, None, e).unwrap());
let mut solution1 = Individual::new(problem);
assert!(solution1.update_objective("obj1", 5.0).is_err());
assert!(solution1.get_objective_value("obj1").is_err());
}
#[test]
fn test_feasibility() {
let objectives = vec![Objective::new("obj1", ObjectiveDirection::Minimise)];
let variables = vec![VariableType::Real(
BoundedNumber::new("X1", 0.0, 2.0).unwrap(),
)];
let constraints = vec![
Constraint::new("c1", RelationalOperator::EqualTo, 1.0),
Constraint::new("c2", RelationalOperator::EqualTo, 599.0),
];
let e = dummy_evaluator();
let problem = Arc::new(Problem::new(objectives, variables, Some(constraints), e).unwrap());
let mut solution1 = Individual::new(problem);
solution1.update_objective("obj1", 5.0).unwrap();
solution1.update_constraint("c1", 5.0).unwrap();
assert!(!solution1.is_feasible());
solution1.update_constraint("c1", 1.0).unwrap();
solution1.update_constraint("c2", 599.0).unwrap();
assert!(solution1.is_feasible());
solution1.update_constraint("c1", 2.0).unwrap();
solution1.update_constraint("c2", 600.0).unwrap();
assert_eq!(solution1.constraint_violation(), 2.0);
}
}