use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use crate::constraints::{Constraint, ConstraintError, ConstraintViolation};
use crate::phenotype::Phenotype;
use crate::rng::RandomNumberGenerator;
pub type Result<T> = std::result::Result<T, ConstraintError>;
#[derive(Debug, Clone)]
pub struct UniqueElementsConstraint<P, T, F>
where
P: Phenotype,
T: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> Vec<T> + Send + Sync + Debug,
{
name: String,
extractor: F,
_marker: PhantomData<(P, T)>,
}
impl<P, T, F> UniqueElementsConstraint<P, T, F>
where
P: Phenotype,
T: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> Vec<T> + Send + Sync + Debug,
{
pub fn new<S: Into<String>>(name: S, extractor: F) -> Result<Self> {
let name = name.into();
if name.is_empty() {
return Err(ConstraintError::EmptyName);
}
Ok(Self {
name,
extractor,
_marker: PhantomData,
})
}
pub fn name(&self) -> &str {
&self.name
}
}
impl<P, T, F> Constraint<P> for UniqueElementsConstraint<P, T, F>
where
P: Phenotype,
T: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> Vec<T> + Send + Sync + Debug,
{
fn check(&self, phenotype: &P) -> Vec<ConstraintViolation> {
let elements = (self.extractor)(phenotype);
let mut seen = HashSet::new();
let mut violations = Vec::new();
for (idx, element) in elements.iter().enumerate() {
if !seen.insert(element) {
violations.push(ConstraintViolation::new(
&self.name,
format!("Duplicate element {:?} at position {}", element, idx),
));
}
}
violations
}
fn repair_with_rng(&self, phenotype: &mut P, rng: &mut RandomNumberGenerator) -> bool {
let violations = self.check(phenotype);
if violations.is_empty() {
return false; }
phenotype.mutate(rng);
let new_violations = self.check(phenotype);
new_violations.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct CompleteAssignmentConstraint<P, K, V, F>
where
P: Phenotype,
K: Eq + Hash + Debug + Clone + Send + Sync,
V: Debug + Clone + Send + Sync,
F: Fn(&P) -> HashMap<K, V> + Send + Sync + Debug,
{
name: String,
extractor: F,
required_keys: HashSet<K>,
_marker: PhantomData<P>,
}
impl<P, K, V, F> CompleteAssignmentConstraint<P, K, V, F>
where
P: Phenotype,
K: Eq + Hash + Debug + Clone + Send + Sync,
V: Debug + Clone + Send + Sync,
F: Fn(&P) -> HashMap<K, V> + Send + Sync + Debug,
{
pub fn new<S: Into<String>>(name: S, extractor: F, required_keys: HashSet<K>) -> Result<Self> {
let name = name.into();
if name.is_empty() {
return Err(ConstraintError::EmptyName);
}
if required_keys.is_empty() {
return Err(ConstraintError::EmptyCollection(
"Required keys set".to_string(),
));
}
Ok(Self {
name,
extractor,
required_keys,
_marker: PhantomData,
})
}
}
impl<P, K, V, F> Constraint<P> for CompleteAssignmentConstraint<P, K, V, F>
where
P: Phenotype,
K: Eq + Hash + Debug + Clone + Send + Sync,
V: Debug + Clone + Send + Sync,
F: Fn(&P) -> HashMap<K, V> + Send + Sync + Debug,
{
fn check(&self, phenotype: &P) -> Vec<ConstraintViolation> {
let assignments = (self.extractor)(phenotype);
let mut violations = Vec::new();
for key in &self.required_keys {
if !assignments.contains_key(key) {
violations.push(ConstraintViolation::new(
&self.name,
format!("Missing assignment for key {:?}", key),
));
}
}
violations
}
fn repair_with_rng(&self, phenotype: &mut P, rng: &mut RandomNumberGenerator) -> bool {
let violations = self.check(phenotype);
if violations.is_empty() {
return false; }
phenotype.mutate(rng);
let new_violations = self.check(phenotype);
new_violations.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct CapacityConstraint<P, K, V, F, G>
where
P: Phenotype,
K: Eq + Hash + Debug + Clone + Send + Sync,
V: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> HashMap<V, Vec<K>> + Send + Sync + Debug,
G: Fn(&V) -> usize + Send + Sync + Debug,
{
name: String,
extractor: F,
capacity_fn: G,
_marker: PhantomData<P>,
}
impl<P, K, V, F, G> CapacityConstraint<P, K, V, F, G>
where
P: Phenotype,
K: Eq + Hash + Debug + Clone + Send + Sync,
V: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> HashMap<V, Vec<K>> + Send + Sync + Debug,
G: Fn(&V) -> usize + Send + Sync + Debug,
{
pub fn new<S: Into<String>>(name: S, extractor: F, capacity_fn: G) -> Result<Self> {
let name = name.into();
if name.is_empty() {
return Err(ConstraintError::EmptyName);
}
Ok(Self {
name,
extractor,
capacity_fn,
_marker: PhantomData,
})
}
}
impl<P, K, V, F, G> Constraint<P> for CapacityConstraint<P, K, V, F, G>
where
P: Phenotype,
K: Eq + Hash + Debug + Clone + Send + Sync,
V: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> HashMap<V, Vec<K>> + Send + Sync + Debug,
G: Fn(&V) -> usize + Send + Sync + Debug,
{
fn check(&self, phenotype: &P) -> Vec<ConstraintViolation> {
let assignments = (self.extractor)(phenotype);
let mut violations = Vec::new();
for (bin, items) in assignments.iter() {
let capacity = (self.capacity_fn)(bin);
if items.len() > capacity {
violations.push(ConstraintViolation::new(
&self.name,
format!(
"Bin {:?} has {} items but capacity is {}",
bin,
items.len(),
capacity
),
));
}
}
violations
}
fn repair_with_rng(&self, phenotype: &mut P, rng: &mut RandomNumberGenerator) -> bool {
let violations = self.check(phenotype);
if violations.is_empty() {
return false; }
phenotype.mutate(rng);
let new_violations = self.check(phenotype);
new_violations.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct DependencyConstraint<P, T, F>
where
P: Phenotype,
T: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> Vec<T> + Send + Sync + Debug,
{
name: String,
extractor: F,
dependencies: Vec<(T, T)>,
_marker: PhantomData<P>,
}
impl<P, T, F> DependencyConstraint<P, T, F>
where
P: Phenotype,
T: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> Vec<T> + Send + Sync + Debug,
{
pub fn new<S: Into<String>>(name: S, extractor: F, dependencies: Vec<(T, T)>) -> Result<Self> {
let name = name.into();
if name.is_empty() {
return Err(ConstraintError::EmptyName);
}
if dependencies.is_empty() {
return Err(ConstraintError::EmptyCollection(
"Dependencies vector".to_string(),
));
}
Ok(Self {
name,
extractor,
dependencies,
_marker: PhantomData,
})
}
}
impl<P, T, F> Constraint<P> for DependencyConstraint<P, T, F>
where
P: Phenotype,
T: Eq + Hash + Debug + Clone + Send + Sync,
F: Fn(&P) -> Vec<T> + Send + Sync + Debug,
{
fn check(&self, phenotype: &P) -> Vec<ConstraintViolation> {
let sequence = (self.extractor)(phenotype);
let mut violations = Vec::new();
let mut positions = HashMap::new();
for (pos, element) in sequence.iter().enumerate() {
positions.insert(element, pos);
}
for (before, after) in &self.dependencies {
if let (Some(&before_pos), Some(&after_pos)) =
(positions.get(before), positions.get(after))
{
if before_pos >= after_pos {
violations.push(ConstraintViolation::new(
&self.name,
format!(
"Dependency violation: {:?} must come before {:?}",
before, after
),
));
}
}
}
violations
}
fn repair_with_rng(&self, phenotype: &mut P, rng: &mut RandomNumberGenerator) -> bool {
let violations = self.check(phenotype);
if violations.is_empty() {
return false; }
phenotype.mutate(rng);
let new_violations = self.check(phenotype);
new_violations.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constraint_violation() {
let violation = ConstraintViolation::new("TestConstraint", "Test violation");
assert_eq!(violation.constraint_name(), "TestConstraint");
assert_eq!(violation.description(), "Test violation");
assert!(violation.severity().is_none());
let violation_with_severity =
ConstraintViolation::with_severity("TestConstraint", "Test violation", 2.5);
assert_eq!(violation_with_severity.constraint_name(), "TestConstraint");
assert_eq!(violation_with_severity.description(), "Test violation");
assert_eq!(violation_with_severity.severity(), Some(2.5));
}
#[test]
fn test_constraint_documentation_examples() {
let violation = ConstraintViolation::new("UniqueValues", "Duplicate value 2 at position 3");
assert_eq!(violation.constraint_name(), "UniqueValues");
assert_eq!(violation.description(), "Duplicate value 2 at position 3");
let violation = ConstraintViolation::with_severity(
"CapacityConstraint",
"Bin 1 exceeds capacity by 3 items",
3.0,
);
assert_eq!(violation.severity(), Some(3.0));
let violation = ConstraintViolation::new("TestConstraint", "Test violation");
let formatted = format!("{}", violation);
assert!(formatted.contains("TestConstraint"));
assert!(formatted.contains("Test violation"));
}
#[test]
fn test_empty_name_validation() {
let empty_name = "".to_string();
let valid_name = "Test".to_string();
assert!(empty_name.is_empty());
assert!(!valid_name.is_empty());
}
#[test]
fn test_empty_collection_validation() {
let empty_keys: HashSet<i32> = HashSet::new();
let valid_keys: HashSet<i32> = [1, 2, 3].iter().cloned().collect();
let empty_deps: Vec<(i32, i32)> = Vec::new();
let valid_deps: Vec<(i32, i32)> = vec![(1, 2), (2, 3)];
assert!(empty_keys.is_empty());
assert!(!valid_keys.is_empty());
assert!(empty_deps.is_empty());
assert!(!valid_deps.is_empty());
}
}