use crate::construction::heuristics::evaluators::{evaluate_job_insertion, InsertionPosition};
use crate::construction::heuristics::{InsertionContext, RouteContext};
use crate::construction::Quota;
use crate::models::common::Cost;
use crate::models::problem::Job;
use crate::models::solution::Activity;
use crate::utils::map_reduce;
use std::borrow::Borrow;
use std::ops::Deref;
use std::sync::Arc;
pub enum InsertionResult {
Success(InsertionSuccess),
Failure(InsertionFailure),
}
pub struct InsertionSuccess {
pub cost: Cost,
pub job: Job,
pub activities: Vec<(Activity, usize)>,
pub context: RouteContext,
}
pub struct InsertionFailure {
pub constraint: i32,
pub job: Option<Job>,
}
pub trait RouteSelector {
fn select<'a>(&'a self, ctx: &'a InsertionContext, job: &'a Job) -> Box<dyn Iterator<Item = RouteContext> + 'a>;
}
pub struct AllRouteSelector {}
impl Default for AllRouteSelector {
fn default() -> Self {
Self {}
}
}
impl RouteSelector for AllRouteSelector {
fn select<'a>(&'a self, ctx: &'a InsertionContext, _job: &'a Job) -> Box<dyn Iterator<Item = RouteContext> + 'a> {
Box::new(ctx.solution.routes.iter().cloned().chain(ctx.solution.registry.next()))
}
}
pub trait JobSelector {
fn select<'a>(&'a self, ctx: &'a mut InsertionContext) -> Box<dyn Iterator<Item = Job> + 'a>;
}
pub struct AllJobSelector {}
impl Default for AllJobSelector {
fn default() -> Self {
Self {}
}
}
impl JobSelector for AllJobSelector {
fn select<'a>(&'a self, ctx: &'a mut InsertionContext) -> Box<dyn Iterator<Item = Job> + 'a> {
Box::new(ctx.solution.required.iter().cloned())
}
}
pub trait JobMapReducer {
fn reduce<'a>(
&'a self,
ctx: &'a InsertionContext,
jobs: Vec<Job>,
map: Box<dyn Fn(&Job) -> InsertionResult + Send + Sync + 'a>,
) -> InsertionResult;
}
pub struct PairJobMapReducer {
result_selector: Box<dyn ResultSelector + Send + Sync>,
}
impl PairJobMapReducer {
pub fn new(result_selector: Box<dyn ResultSelector + Send + Sync>) -> Self {
Self { result_selector }
}
}
impl JobMapReducer for PairJobMapReducer {
fn reduce<'a>(
&'a self,
ctx: &'a InsertionContext,
jobs: Vec<Job>,
map: Box<dyn Fn(&Job) -> InsertionResult + Send + Sync + 'a>,
) -> InsertionResult {
map_reduce(
&jobs,
|job| map.deref()(&job),
InsertionResult::make_failure,
|a, b| self.result_selector.select(&ctx, a, b),
)
}
}
pub trait ResultSelector {
fn select(&self, ctx: &InsertionContext, left: InsertionResult, right: InsertionResult) -> InsertionResult;
}
pub struct BestResultSelector {}
impl Default for BestResultSelector {
fn default() -> Self {
Self {}
}
}
impl ResultSelector for BestResultSelector {
fn select(&self, _: &InsertionContext, left: InsertionResult, right: InsertionResult) -> InsertionResult {
InsertionResult::choose_best_result(left, right)
}
}
pub struct InsertionHeuristic {
insertion_position: InsertionPosition,
}
impl Default for InsertionHeuristic {
fn default() -> Self {
InsertionHeuristic::new(InsertionPosition::Any)
}
}
impl InsertionHeuristic {
pub fn new(insertion_position: InsertionPosition) -> Self {
Self { insertion_position }
}
}
impl InsertionHeuristic {
pub fn process(
&self,
route_selector: &(dyn RouteSelector + Send + Sync),
job_selector: &(dyn JobSelector + Send + Sync),
job_reducer: &(dyn JobMapReducer + Send + Sync),
ctx: InsertionContext,
quota: &Option<Arc<dyn Quota + Send + Sync>>,
) -> InsertionContext {
let mut ctx = ctx;
prepare_ctx(&mut ctx);
while !ctx.solution.required.is_empty() && !quota.as_ref().map_or(false, |q| q.is_reached()) {
let jobs = job_selector.select(&mut ctx).collect::<Vec<Job>>();
let result = job_reducer.reduce(
&ctx,
jobs,
Box::new(|job| evaluate_job_insertion(&job, &ctx, route_selector, self.insertion_position)),
);
insert(result, &mut ctx);
}
finalize_ctx(&mut ctx);
ctx
}
}
impl InsertionResult {
pub fn make_success(cost: Cost, job: Job, activities: Vec<(Activity, usize)>, route_ctx: RouteContext) -> Self {
Self::Success(InsertionSuccess { cost, job, activities, context: route_ctx })
}
pub fn make_failure() -> Self {
Self::make_failure_with_code(-1, None)
}
pub fn make_failure_with_code(code: i32, job: Option<Job>) -> Self {
Self::Failure(InsertionFailure { constraint: code, job })
}
pub fn choose_best_result(left: Self, right: Self) -> Self {
match (left.borrow(), right.borrow()) {
(Self::Success(_), Self::Failure(_)) => left,
(Self::Failure(_), Self::Success(_)) => right,
(Self::Success(lhs), Self::Success(rhs)) => {
if lhs.cost > rhs.cost {
right
} else {
left
}
}
_ => right,
}
}
}
fn prepare_ctx(ctx: &mut InsertionContext) {
ctx.solution.required.extend(ctx.solution.unassigned.drain().map(|(job, _)| job));
ctx.problem.constraint.accept_solution_state(&mut ctx.solution);
}
fn finalize_ctx(ctx: &mut InsertionContext) {
ctx.solution.unassigned.extend(ctx.solution.required.drain(0..).map(|job| (job, 0)));
ctx.problem.constraint.accept_solution_state(&mut ctx.solution);
}
fn insert(result: InsertionResult, ctx: &mut InsertionContext) {
match result {
InsertionResult::Success(success) => {
let is_new_route = ctx.solution.registry.use_route(&success.context);
let route_index = ctx.solution.routes.iter().position(|ctx| ctx == &success.context).unwrap_or_else(|| {
assert!(is_new_route);
ctx.solution.routes.push(success.context.deep_copy());
ctx.solution.routes.len() - 1
});
let route_ctx = ctx.solution.routes.get_mut(route_index).unwrap();
let route = route_ctx.route_mut();
success.activities.into_iter().for_each(|(a, index)| {
route.tour.insert_at(a, index + 1);
});
let job = success.job;
ctx.solution.required.retain(|j| *j != job);
ctx.problem.constraint.accept_insertion(&mut ctx.solution, route_index, &job);
}
InsertionResult::Failure(failure) => {
if let Some(job) = failure.job {
ctx.solution.unassigned.insert(job.clone(), failure.constraint);
ctx.solution.required.retain(|j| *j != job);
}
}
}
}