use std::fmt::Debug;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use super::bounder::ScoreBounder;
use super::node::ExhaustiveSearchNode;
pub trait ExhaustiveSearchDecider<S: PlanningSolution, D: Director<S>>: Send + Debug {
fn expand(
&self,
parent_index: usize,
parent: &ExhaustiveSearchNode<S>,
score_director: &mut D,
) -> Vec<ExhaustiveSearchNode<S>>;
fn total_entities(&self, score_director: &D) -> usize;
}
pub struct SimpleDecider<S: PlanningSolution, V: Clone + Send + Sync + 'static, B = ()> {
descriptor_index: usize,
variable_name: String,
values: Vec<V>,
bounder: Option<B>,
setter: fn(&mut S, usize, Option<V>),
}
impl<S: PlanningSolution, V: Clone + Send + Sync + 'static> SimpleDecider<S, V, ()> {
pub fn new(
descriptor_index: usize,
variable_name: impl Into<String>,
values: Vec<V>,
setter: fn(&mut S, usize, Option<V>),
) -> Self {
Self {
descriptor_index,
variable_name: variable_name.into(),
values,
bounder: None,
setter,
}
}
}
impl<S: PlanningSolution, V: Clone + Send + Sync + 'static, B> SimpleDecider<S, V, B> {
pub fn with_bounder<B2>(self, bounder: B2) -> SimpleDecider<S, V, B2> {
SimpleDecider {
descriptor_index: self.descriptor_index,
variable_name: self.variable_name,
values: self.values,
bounder: Some(bounder),
setter: self.setter,
}
}
}
impl<S: PlanningSolution, V: Clone + Send + Sync + Debug + 'static, B: Debug> Debug
for SimpleDecider<S, V, B>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SimpleDecider")
.field("descriptor_index", &self.descriptor_index)
.field("variable_name", &self.variable_name)
.field("value_count", &self.values.len())
.finish()
}
}
impl<S, V, B, D> ExhaustiveSearchDecider<S, D> for SimpleDecider<S, V, B>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
B: ScoreBounder<S, D>,
D: Director<S>,
{
fn expand(
&self,
parent_index: usize,
parent: &ExhaustiveSearchNode<S>,
score_director: &mut D,
) -> Vec<ExhaustiveSearchNode<S>> {
let entity_index = parent.depth();
let new_depth = parent.depth() + 1;
let total = self.total_entities(score_director);
if entity_index >= total {
return Vec::new();
}
let mut children = Vec::with_capacity(self.values.len());
for (value_index, value) in self.values.iter().enumerate() {
score_director.before_variable_changed(self.descriptor_index, entity_index);
(self.setter)(
score_director.working_solution_mut(),
entity_index,
Some(value.clone()),
);
score_director.after_variable_changed(self.descriptor_index, entity_index);
let score = score_director.calculate_score();
let mut child = ExhaustiveSearchNode::child(
parent_index,
new_depth,
score,
entity_index,
value_index,
);
if let Some(ref bounder) = self.bounder {
if let Some(bound) = bounder.calculate_optimistic_bound(score_director) {
child.set_optimistic_bound(bound);
}
}
children.push(child);
score_director.before_variable_changed(self.descriptor_index, entity_index);
(self.setter)(score_director.working_solution_mut(), entity_index, None);
score_director.after_variable_changed(self.descriptor_index, entity_index);
}
children
}
fn total_entities(&self, score_director: &D) -> usize {
score_director
.entity_count(self.descriptor_index)
.unwrap_or(0)
}
}
impl<S: PlanningSolution, D: Director<S>> ScoreBounder<S, D> for () {
fn calculate_optimistic_bound(&self, _score_director: &D) -> Option<S::Score> {
None
}
}
#[cfg(test)]
#[path = "decider_tests.rs"]
mod tests;