use std::{fs::File, path::Path};
use eyre::WrapErr;
use crate::{
component::ExecResult,
components::{evaluation, utils::debug, Block, Branch, Component, Loop, Scope},
conditions::Condition,
identifier,
identifier::Identifier,
logging,
problems::{Evaluate, MultiObjectiveProblem, SingleObjectiveProblem},
state::{common, random::Random},
Problem, State,
};
#[derive(Clone)]
pub struct Configuration<P: Problem>(Box<dyn Component<P>>);
impl<P: Problem> Configuration<P> {
pub fn new(heuristic: Box<dyn Component<P>>) -> Self {
Self(heuristic)
}
pub fn builder() -> ConfigurationBuilder<P> {
ConfigurationBuilder::new()
}
pub fn heuristic(&self) -> &dyn Component<P> {
self.0.as_ref()
}
pub fn into_inner(self) -> Box<dyn Component<P>> {
self.0
}
pub fn into_builder(self) -> ConfigurationBuilder<P> {
Self::builder().do_(self.0)
}
pub fn to_ron(&self, path: impl AsRef<Path>) -> ExecResult<()> {
ron::ser::to_writer_pretty(
std::io::BufWriter::new(
File::create(path).wrap_err("failed to create configuration file")?,
),
self.heuristic(),
ron::ser::PrettyConfig::default().struct_names(true),
)
.wrap_err("failed to serialize configuration")
}
pub fn run(&self, problem: &P, state: &mut State<P>) -> ExecResult<()> {
self.0.init(problem, state)?;
self.0.require(problem, &state.requirements())?;
self.0.execute(problem, state)?;
Ok(())
}
pub fn optimize<'a, T>(&self, problem: &P, evaluator: T) -> ExecResult<State<'a, P>>
where
T: Evaluate<Problem = P> + 'a,
{
let mut state = State::new();
state.insert(logging::Log::new());
state.insert(Random::default());
state.insert(common::Populations::<P>::new());
state.insert(common::Evaluator::<P, identifier::Global>::new(evaluator));
self.run(problem, &mut state)?;
Ok(state)
}
pub fn optimize_with<'a>(
&self,
problem: &P,
init_state: impl FnOnce(&mut State<'a, P>) -> ExecResult<()>,
) -> ExecResult<State<'a, P>> {
let mut state = State::new();
state.insert(logging::Log::new());
state.insert(common::Populations::<P>::new());
init_state(&mut state)?;
if !state.contains::<Random>() {
state.insert(Random::default());
}
self.run(problem, &mut state)?;
Ok(state)
}
}
impl<P: Problem> From<Box<dyn Component<P>>> for Configuration<P> {
fn from(heuristic: Box<dyn Component<P>>) -> Self {
Self::new(heuristic)
}
}
pub struct ConfigurationBuilder<P: Problem> {
components: Vec<Box<dyn Component<P>>>,
}
impl<P: Problem> ConfigurationBuilder<P> {
fn new() -> Self {
Self {
components: Vec::new(),
}
}
pub fn do_(mut self, component: Box<dyn Component<P>>) -> Self {
self.components.push(component);
self
}
pub fn do_if_some_(self, component: Option<Box<dyn Component<P>>>) -> Self {
if let Some(component) = component {
self.do_(component)
} else {
self
}
}
pub fn do_many_(mut self, components: impl IntoIterator<Item = Box<dyn Component<P>>>) -> Self {
for component in components {
self.components.push(component);
}
self
}
pub fn while_(
self,
condition: Box<dyn Condition<P>>,
body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
) -> Self {
let components = body(ConfigurationBuilder::new()).components;
self.do_(Loop::new(condition, components))
}
pub fn if_(
self,
condition: Box<dyn Condition<P>>,
body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
) -> Self {
let components = body(ConfigurationBuilder::new()).components;
self.do_(Branch::new(condition, components))
}
pub fn if_else_(
self,
condition: Box<dyn Condition<P>>,
if_body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
else_body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
) -> Self {
let if_components = if_body(ConfigurationBuilder::new()).components;
let else_components = else_body(ConfigurationBuilder::new()).components;
self.do_(Branch::new_with_else(
condition,
if_components,
else_components,
))
}
pub fn scope_(
self,
body: impl FnOnce(ConfigurationBuilder<P>) -> ConfigurationBuilder<P>,
) -> Self {
let components = body(ConfigurationBuilder::new()).components;
self.do_(Scope::new(components))
}
pub fn build(self) -> Configuration<P> {
Configuration::new(Block::new(self.components))
}
pub fn build_component(self) -> Box<dyn Component<P>> {
Block::new(self.components)
}
}
impl<P: Problem> ConfigurationBuilder<P> {
#[track_caller]
pub fn assert(
self,
assert: impl Fn(&State<P>) -> bool + Send + Sync + Clone + 'static,
) -> Self {
self.debug(move |_problem, state| assert!(assert(state)))
}
pub fn debug(
self,
behaviour: impl Fn(&P, &mut State<P>) + Send + Sync + Clone + 'static,
) -> Self {
self.do_(debug::Debug::new(behaviour))
}
pub fn evaluate(self) -> Self {
self.do_(evaluation::PopulationEvaluator::new())
}
pub fn evaluate_with<I>(self) -> Self
where
I: Identifier,
{
self.do_(evaluation::PopulationEvaluator::<I>::new_with())
}
}
impl<P: SingleObjectiveProblem> ConfigurationBuilder<P> {
pub fn update_best_individual(self) -> Self {
self.do_(evaluation::BestIndividualUpdate::new())
}
}
impl<P: MultiObjectiveProblem> ConfigurationBuilder<P> {
pub fn update_pareto_front(self) -> Self {
self.do_(evaluation::ParetoFrontUpdate::new())
}
}