use crate::binary_string::BinaryString;
use crate::fitness::FitnessFunction;
use crate::terminating::TerminatingCondition;
use crate::perturbation::PerturbationOperator;
use crate::comparison::BetterThanOperator;
// Functions
#[derive(Debug, Clone, PartialEq)]
pub struct LocalSearchCandidate<TInput, TResult>
{
pub fit: TResult,
pub pos: TInput,
pub cycle: usize
}
#[derive(Debug, Clone, PartialEq)]
pub struct LocalSearchResult<TInput, TResult>
where TResult: Clone
{
pub best_candidate: LocalSearchCandidate<TInput, TResult>,
// How many cycles there were
pub cycles: usize,
// statistics
pub best_candidates: Vec<LocalSearchCandidate<TInput, TResult>>
}
fn local_search_first_improving<
TInput, TResult, TErr, TFit, TTerminatingCondition, TPerturbationOperator, TBetterThanOperator>(
fit: &TFit,
terminating_condition: &mut TTerminatingCondition,
perturbation_operator: &mut TPerturbationOperator,
better_than_operator: &TBetterThanOperator,
initial: &TInput
) -> Result<LocalSearchResult<TInput, TResult>, TErr>
where
TResult: Clone,
TInput: Clone,
TFit: FitnessFunction<In = TInput, Out = TResult, Err = TErr>,
TTerminatingCondition: TerminatingCondition<TInput, TResult>,
TPerturbationOperator: PerturbationOperator<Chromosome = TInput>,
TBetterThanOperator: BetterThanOperator<TResult>,
{
let mut best_candidate = LocalSearchCandidate {
pos: initial.clone(),
fit: fit.fit(&initial)?,
cycle: 0
};
let mut stats: Vec<LocalSearchCandidate<TInput, TResult>> = vec![];
let mut cycle: usize = 0;
while !terminating_condition.should_terminate(&best_candidate, &stats, cycle) {
let perturbed = perturbation_operator.perturb(&best_candidate.pos);
let perturbed_fit = fit.fit(&perturbed)?;
// Minimize
if better_than_operator.better_than(&perturbed_fit, &best_candidate.fit) {
best_candidate = LocalSearchCandidate {
pos: perturbed.clone(),
fit: perturbed_fit,
cycle
};
stats.push(best_candidate.clone());
}
cycle += 1;
}
Ok(LocalSearchResult {
best_candidate,
best_candidates: stats,
cycles: cycle
})
}
#[cfg(test)]
pub mod tests {
use crate::{binary_string::{BinaryString, Bounds}, comparison::MinimizingOperator, fitness::{one_max::OneMax, rosenbrock::Rosenbrock, sphere::Sphere, BinaryFitnessWrapper}, local_search::local_search_first_improving, perturbation::BinaryStringBitPerturbation, terminating::{AndTerminatingConditions, EqualTerminatingCondition, NoBetterForCyclesTerminatingCondition}};
#[test]
fn test_local_search_sphere() {
let optimum = BinaryString::new(vec![0, 0, 1, 0, 0,
0, 0, 1, 0, 0]);
let bounds = vec![Bounds::new(0.0, 31.0), Bounds::new(0.0, 31.0)];
let optimum_real = optimum.to_real(&bounds).unwrap();
let sphere = Sphere::new(optimum_real);
let sphere_wrapped = BinaryFitnessWrapper::new(sphere, bounds);
let result = local_search_first_improving(
&sphere_wrapped,
&mut
AndTerminatingConditions::new(
vec![
&mut EqualTerminatingCondition::new_remembered(optimum.clone()),
&mut NoBetterForCyclesTerminatingCondition::new(100)
]
),
&mut BinaryStringBitPerturbation::new(0.3),
&MinimizingOperator::new(),
&BinaryString::new(vec![1; 10]),
).unwrap();
println!("{:?}", result);
assert_eq!(
result.best_candidate.fit,
0.0
);
assert_eq!(
result.best_candidate.pos,
optimum
);
}
#[test]
fn test_local_search_one_max() {
let one_max = OneMax::new();
let optimum = BinaryString::new(vec![0; 10]);
let result = local_search_first_improving(
&one_max,
&mut
AndTerminatingConditions::new(
vec![
&mut EqualTerminatingCondition::new_remembered(optimum.clone()),
&mut NoBetterForCyclesTerminatingCondition::new(100)
]
),
&mut BinaryStringBitPerturbation::new(0.3),
&MinimizingOperator::new(),
&BinaryString::new(vec![1; 10]),
).unwrap();
println!("{:?}", result);
assert_eq!(
result.best_candidate.fit,
0
);
assert_eq!(
result.best_candidate.pos,
optimum
);
}
// #[test]
// fn test_local_search_labs() {
// let labs = LABS::new();
// let optimum = BinaryString::new(vec![0; 20]);
// let result = local_search_first_improving(
// &labs,
// &mut
// AndTerminatingConditions::new(
// vec![
// // &mut EqualTerminatingCondition::new_remembered(optimum.clone()),
// &mut NoBetterForCyclesTerminatingCondition::new(1000)
// ]
// ),
// &mut BinaryStringBitPerturbation::new(0.1),
// &MinimizingOperator::new(),
// &BinaryString::new(vec![0; 20]),
// ).unwrap();
// println!("{:?}", result);
// assert_eq!(
// result.best_candidate.fit,
// 0
// );
// assert_eq!(
// result.best_candidate.pos,
// optimum
// );
// }
#[test]
fn test_local_search_rosenbrock() {
let rosenbrock = Rosenbrock::new();
let optimum = BinaryString::new(vec![1, 0, 0, 0, 1, 1, 0, 0, 0, 1]);
let bounds = vec![Bounds::new(-16.0, 15.0), Bounds::new(-16.0, 15.0)];
let rosenbrock_wrapped = BinaryFitnessWrapper::new(rosenbrock, bounds);
let result = local_search_first_improving(
&rosenbrock_wrapped,
&mut
AndTerminatingConditions::new(
vec![
&mut EqualTerminatingCondition::new_remembered(optimum.clone()),
&mut NoBetterForCyclesTerminatingCondition::new(100)
]
),
&mut BinaryStringBitPerturbation::new(0.3),
&MinimizingOperator::new(),
&BinaryString::new(vec![0; 10]),
).unwrap();
println!("{:?}", result);
assert_eq!(
result.best_candidate.fit,
0.0
);
assert_eq!(
result.best_candidate.pos,
optimum
);
}
}