use crate::core::constraint::BoxConstrained;
use crate::core::math::SampleUniformBox;
use crate::core::problem::CostFunction;
use crate::core::rng::{ChaCha8Rng, SeedableRng};
use crate::core::solver::Solver;
use crate::core::state::BasicPopulationState;
use crate::core::termination::TerminationReason;
pub struct RandomSearch {
lambda: usize,
rng: ChaCha8Rng,
}
impl RandomSearch {
pub fn new(lambda: usize, seed: u64) -> Self {
assert!(lambda >= 1, "RandomSearch requires lambda >= 1");
Self {
lambda,
rng: ChaCha8Rng::seed_from_u64(seed),
}
}
}
fn sort_population_ascending<V>(candidates: &mut [V], costs: &mut [f64]) {
let n = candidates.len();
debug_assert_eq!(n, costs.len());
let mut idx: Vec<usize> = (0..n).collect();
idx.sort_by(|&i, &j| {
costs[i]
.partial_cmp(&costs[j])
.unwrap_or(std::cmp::Ordering::Equal)
});
apply_permutation(candidates, &idx);
apply_permutation(costs, &idx);
}
fn apply_permutation<T>(slice: &mut [T], idx: &[usize]) {
let mut visited = vec![false; slice.len()];
for start in 0..slice.len() {
if visited[start] || idx[start] == start {
visited[start] = true;
continue;
}
let mut current = start;
loop {
let next = idx[current];
visited[current] = true;
if next == start {
break;
}
slice.swap(current, next);
current = next;
}
}
}
impl<P, V> Solver<P, BasicPopulationState<V>> for RandomSearch
where
P: CostFunction<Param = V, Output = f64> + BoxConstrained<Param = V>,
V: SampleUniformBox + Clone,
{
fn init(&mut self, problem: &P, mut state: BasicPopulationState<V>) -> BasicPopulationState<V> {
let lo = problem.lower();
let hi = problem.upper();
state.candidates.clear();
state.costs.clear();
for _ in 0..self.lambda {
let x = V::sample_uniform_box(lo, hi, &mut self.rng);
let c = problem.cost(&x);
state.candidates.push(x);
state.costs.push(c);
}
state.cost_evals += self.lambda as u64;
sort_population_ascending(&mut state.candidates, &mut state.costs);
state
}
fn next_iter(
&mut self,
problem: &P,
mut state: BasicPopulationState<V>,
) -> (BasicPopulationState<V>, Option<TerminationReason>) {
let elite_x = state.candidates[0].clone();
let elite_c = state.costs[0];
let lo = problem.lower();
let hi = problem.upper();
state.candidates.clear();
state.costs.clear();
state.candidates.push(elite_x);
state.costs.push(elite_c);
for _ in 0..self.lambda {
let x = V::sample_uniform_box(lo, hi, &mut self.rng);
let c = problem.cost(&x);
state.candidates.push(x);
state.costs.push(c);
}
state.cost_evals += self.lambda as u64;
sort_population_ascending(&mut state.candidates, &mut state.costs);
state.candidates.truncate(self.lambda);
state.costs.truncate(self.lambda);
(state, None)
}
}