use std::{vec, collections::BinaryHeap};
use ddo::*;
use smallbitset::Set32;
use crate::ub_utils::all_mst;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PspState {
pub time: usize,
pub next: isize,
pub prev_demands: Vec<isize>,
}
pub const IDLE: isize = -1;
#[derive(Debug)]
pub struct Psp {
pub n_items: usize,
pub horizon: usize,
pub stocking: Vec<usize>,
pub changeover: Vec<Vec<usize>>,
pub prev_demands: Vec<Vec<isize>>,
pub rem_demands: Vec<Vec<isize>>,
}
impl Problem for Psp {
type State = PspState;
fn nb_variables(&self) -> usize {
self.horizon
}
fn initial_state(&self) -> Self::State {
let mut prev_demands = vec![];
for i in 0..self.n_items {
prev_demands.push(self.prev_demands[i][self.horizon]);
}
PspState {
time: self.horizon,
next: -1,
prev_demands
}
}
fn initial_value(&self) -> isize {
0
}
fn transition(&self, state: &Self::State, decision: ddo::Decision) -> Self::State {
let mut ret = state.clone();
ret.time -= 1;
if decision.value != IDLE {
let d = decision.value as usize;
ret.next = decision.value;
ret.prev_demands[d] = self.prev_demands[d][state.prev_demands[d] as usize];
}
ret
}
fn transition_cost(&self, state: &Self::State, _: &Self::State, decision: ddo::Decision) -> isize {
if decision.value == IDLE {
0
} else {
let d = decision.value as usize;
let t = decision.variable.id() as isize;
let duration = state.prev_demands[d] - t;
let stocking = self.stocking[d] as isize * duration;
let changeover =
if state.next != -1 {
self.changeover[d][state.next as usize]
} else {
0
};
-(changeover as isize + stocking)
}
}
fn next_variable(&self, depth: usize, _: &mut dyn Iterator<Item = &Self::State>)
-> Option<ddo::Variable> {
if depth < self.horizon {
Some(Variable(self.horizon - depth - 1))
} else {
None
}
}
fn for_each_in_domain(&self, variable: ddo::Variable, state: &Self::State, f: &mut dyn ddo::DecisionCallback) {
let t = variable.id() as isize;
let dom = (0..self.n_items).filter(|i| state.prev_demands[*i] >= t).collect::<Vec<usize>>();
let rem_demands = (0..self.n_items).filter(|i| state.prev_demands[*i] >= 0).map(|i| self.rem_demands[i][state.prev_demands[i] as usize]).sum::<isize>();
if rem_demands > t + 1 {
return;
}
for i in dom.iter() {
f.apply(Decision {variable, value: *i as isize});
}
if rem_demands < t + 1 {
f.apply(Decision {variable, value: IDLE});
}
}
}
pub struct PspRelax<'a>{
pb: &'a Psp,
mst: Vec<usize>,
}
impl <'a> PspRelax<'a> {
pub fn new(pb: &'a Psp) -> Self {
let mst = all_mst(&pb.changeover);
Self { pb, mst }
}
fn members(state: &PspState) -> Set32 {
let mut mem = Set32::empty();
for (i, d) in state.prev_demands.iter().copied().enumerate() {
if d >= 0 {
mem = mem.add(i);
}
}
if state.next != -1 {
mem = mem.add(state.next as usize);
}
mem
}
}
impl Relaxation for PspRelax<'_> {
type State = PspState;
fn merge(&self, states: &mut dyn Iterator<Item = &Self::State>) -> Self::State {
let mut time = self.pb.horizon;
let mut prev_demands = vec![isize::MAX; self.pb.n_items];
for s in states {
time = time.min(s.time);
prev_demands.iter_mut()
.zip(s.prev_demands.iter().copied())
.for_each(|(x, y)| *x = y.min(*x));
}
PspState{time, next: -1, prev_demands}
}
fn relax(
&self,
_source: &Self::State,
_dest: &Self::State,
_new: &Self::State,
_decision: Decision,
cost: isize,
) -> isize {
cost
}
fn fast_upper_bound(&self, state: &Self::State) -> isize {
let idx: u32 = u32::from(Self::members(state));
let co = self.mst[idx as usize] as isize;
let mut prev_demands = state.prev_demands.clone();
let mut ww = 0;
let mut items = BinaryHeap::new();
for time in (0..state.time).rev() {
for (i, demand) in prev_demands.iter_mut().enumerate() {
while *demand >= time as isize {
items.push((self.pb.stocking[i], *demand));
*demand = self.pb.prev_demands[i][*demand as usize];
}
}
if let Some((cost, deadline)) = items.pop() {
ww += cost as isize * (time as isize - deadline);
}
}
-(co + ww)
}
}
pub struct PspRanking;
impl StateRanking for PspRanking {
type State = PspState;
fn compare(&self, a: &Self::State, b: &Self::State) -> std::cmp::Ordering {
let tot_a = a.prev_demands.iter().sum::<isize>();
let tot_b = b.prev_demands.iter().sum::<isize>();
tot_a.cmp(&tot_b)
}
}