use rand::Rng;
use super::chromosome::{
insert_mutation, invert_mutation, jox_crossover, lox_crossover, mav_mutation, pox_crossover,
swap_mutation, ScheduleChromosome,
};
use super::problem::ActivityInfo;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossoverType {
POX,
LOX,
JOX,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MutationType {
Swap,
Insert,
Invert,
}
#[derive(Debug, Clone)]
pub struct GeneticOperators {
pub crossover_type: CrossoverType,
pub mutation_type: MutationType,
}
impl Default for GeneticOperators {
fn default() -> Self {
Self {
crossover_type: CrossoverType::POX,
mutation_type: MutationType::Swap,
}
}
}
impl GeneticOperators {
pub fn crossover<R: Rng>(
&self,
p1: &ScheduleChromosome,
p2: &ScheduleChromosome,
activities: &[ActivityInfo],
rng: &mut R,
) -> (ScheduleChromosome, ScheduleChromosome) {
match self.crossover_type {
CrossoverType::POX => pox_crossover(p1, p2, activities, rng),
CrossoverType::LOX => lox_crossover(p1, p2, activities, rng),
CrossoverType::JOX => jox_crossover(p1, p2, activities, rng),
}
}
pub fn mutate<R: Rng>(
&self,
chromosome: &mut ScheduleChromosome,
activities: &[ActivityInfo],
rng: &mut R,
) {
match self.mutation_type {
MutationType::Swap => swap_mutation(chromosome, rng),
MutationType::Insert => insert_mutation(chromosome, rng),
MutationType::Invert => invert_mutation(chromosome, rng),
}
mav_mutation(chromosome, activities, rng);
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::SmallRng;
use rand::SeedableRng;
fn sample_activities() -> Vec<ActivityInfo> {
vec![
ActivityInfo {
task_id: "T1".into(),
sequence: 1,
process_ms: 1000,
candidates: vec!["M1".into(), "M2".into()],
},
ActivityInfo {
task_id: "T1".into(),
sequence: 2,
process_ms: 2000,
candidates: vec!["M2".into()],
},
ActivityInfo {
task_id: "T2".into(),
sequence: 1,
process_ms: 1500,
candidates: vec!["M1".into(), "M3".into()],
},
]
}
#[test]
fn test_default_operators() {
let ops = GeneticOperators::default();
assert_eq!(ops.crossover_type, CrossoverType::POX);
assert_eq!(ops.mutation_type, MutationType::Swap);
}
#[test]
fn test_crossover_pox() {
let acts = sample_activities();
let ops = GeneticOperators::default();
let mut rng = SmallRng::seed_from_u64(42);
let p1 = ScheduleChromosome::random(&acts, &mut rng);
let p2 = ScheduleChromosome::random(&acts, &mut rng);
let (c1, c2) = ops.crossover(&p1, &p2, &acts, &mut rng);
assert_eq!(c1.osv.len(), 3);
assert_eq!(c2.osv.len(), 3);
}
#[test]
fn test_crossover_lox() {
let acts = sample_activities();
let ops = GeneticOperators {
crossover_type: CrossoverType::LOX,
mutation_type: MutationType::Swap,
};
let mut rng = SmallRng::seed_from_u64(42);
let p1 = ScheduleChromosome::random(&acts, &mut rng);
let p2 = ScheduleChromosome::random(&acts, &mut rng);
let (c1, c2) = ops.crossover(&p1, &p2, &acts, &mut rng);
assert_eq!(c1.osv.len(), 3);
assert_eq!(c2.osv.len(), 3);
}
#[test]
fn test_crossover_jox() {
let acts = sample_activities();
let ops = GeneticOperators {
crossover_type: CrossoverType::JOX,
mutation_type: MutationType::Swap,
};
let mut rng = SmallRng::seed_from_u64(42);
let p1 = ScheduleChromosome::random(&acts, &mut rng);
let p2 = ScheduleChromosome::random(&acts, &mut rng);
let (c1, c2) = ops.crossover(&p1, &p2, &acts, &mut rng);
assert_eq!(c1.osv.len(), 3);
assert_eq!(c2.osv.len(), 3);
}
#[test]
fn test_mutation_swap() {
let acts = sample_activities();
let ops = GeneticOperators::default();
let mut rng = SmallRng::seed_from_u64(42);
let mut ch = ScheduleChromosome::random(&acts, &mut rng);
ops.mutate(&mut ch, &acts, &mut rng);
assert_eq!(ch.osv.len(), 3);
}
#[test]
fn test_mutation_insert() {
let acts = sample_activities();
let ops = GeneticOperators {
crossover_type: CrossoverType::POX,
mutation_type: MutationType::Insert,
};
let mut rng = SmallRng::seed_from_u64(42);
let mut ch = ScheduleChromosome::random(&acts, &mut rng);
ops.mutate(&mut ch, &acts, &mut rng);
assert_eq!(ch.osv.len(), 3);
}
#[test]
fn test_mutation_invert() {
let acts = sample_activities();
let ops = GeneticOperators {
crossover_type: CrossoverType::POX,
mutation_type: MutationType::Invert,
};
let mut rng = SmallRng::seed_from_u64(42);
let mut ch = ScheduleChromosome::random(&acts, &mut rng);
ops.mutate(&mut ch, &acts, &mut rng);
assert_eq!(ch.osv.len(), 3);
}
#[test]
fn test_mutate_always_applies_mav() {
let acts = sample_activities();
let ops = GeneticOperators::default();
let mut rng = SmallRng::seed_from_u64(42);
let ch = ScheduleChromosome::random(&acts, &mut rng);
let original_mav = ch.mav.clone();
let mut mav_changed = false;
for _ in 0..50 {
let mut ch2 = ch.clone();
ops.mutate(&mut ch2, &acts, &mut rng);
if ch2.mav != original_mav {
mav_changed = true;
break;
}
}
assert!(
mav_changed,
"MAV mutation should occur alongside OSV mutation"
);
}
}