use crate::core::constraint::BoxConstraints;
use crate::core::math::{SampleUniformBox, Scalar};
use crate::core::problem::{CostFunction, Problem};
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, F: PartialOrd>(candidates: &mut [V], costs: &mut [F]) {
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, F> Solver<P, BasicPopulationState<V, F>> for RandomSearch
where
F: Scalar + crate::core::parallel::MaybeSend,
P: CostFunction<Param = V, Output = F>
+ BoxConstraints<Param = V>
+ crate::core::parallel::MaybeSync,
P::Error: crate::core::parallel::MaybeSend,
V: SampleUniformBox + Clone + crate::core::parallel::MaybeSync,
{
type Error = P::Error;
fn init(
&mut self,
problem: &mut Problem<P>,
mut state: BasicPopulationState<V, F>,
) -> Result<BasicPopulationState<V, F>, Self::Error> {
let lo = problem.inner().lower().clone();
let hi = problem.inner().upper().clone();
state.candidates.clear();
state.costs.clear();
for _ in 0..self.lambda {
let x = V::sample_uniform_box(&lo, &hi, &mut self.rng);
state.candidates.push(x);
}
state.costs = problem.cost_batch(&state.candidates)?;
sort_population_ascending(&mut state.candidates, &mut state.costs);
Ok(state)
}
fn next_iter(
&mut self,
problem: &mut Problem<P>,
mut state: BasicPopulationState<V, F>,
) -> Result<(BasicPopulationState<V, F>, Option<TerminationReason>), Self::Error> {
let elite_x = state.candidates[0].clone();
let elite_c = state.costs[0];
let lo = problem.inner().lower().clone();
let hi = problem.inner().upper().clone();
state.candidates.clear();
state.costs.clear();
state.candidates.push(elite_x);
state.costs.push(elite_c);
let mut fresh: Vec<V> = Vec::with_capacity(self.lambda);
for _ in 0..self.lambda {
fresh.push(V::sample_uniform_box(&lo, &hi, &mut self.rng));
}
let fresh_costs = problem.cost_batch(&fresh)?;
state.candidates.extend(fresh);
state.costs.extend(fresh_costs);
sort_population_ascending(&mut state.candidates, &mut state.costs);
state.candidates.truncate(self.lambda);
state.costs.truncate(self.lambda);
Ok((state, None))
}
}