use ddo::{Problem, Variable, Decision};
use smallbitset::Set256;
use crate::{instance::TsptwInstance, state::{ElapsedTime, Position, TsptwState}};
#[derive(Clone)]
pub struct Tsptw {
pub instance: TsptwInstance,
pub initial : TsptwState,
}
impl Tsptw {
pub fn new(inst: TsptwInstance) -> Self {
let mut must_visit = Set256::default();
(1..inst.nb_nodes).for_each(|i| {must_visit.add_inplace(i as usize);});
let state = TsptwState {
position : Position::Node(0),
elapsed : ElapsedTime::FixedAmount{duration: 0},
must_visit,
maybe_visit: None,
depth : 0
};
Self { instance: inst, initial: state }
}
}
impl Problem for Tsptw {
type State = TsptwState;
fn nb_variables(&self) -> usize {
self.instance.nb_nodes as usize
}
fn initial_state(&self) -> TsptwState {
self.initial.clone()
}
fn initial_value(&self) -> isize {
0
}
fn for_each_in_domain(&self, variable: Variable, state: &Self::State, f: &mut dyn ddo::DecisionCallback) {
if state.depth as usize == self.nb_variables() - 1 {
if self.can_move_to(state, 0) {
f.apply(Decision { variable, value: 0 })
}
return;
}
for i in state.must_visit.iter() {
if !self.can_move_to(state, i) {
return;
}
}
for i in state.must_visit.iter() {
f.apply(Decision { variable, value: i as isize })
}
if let Some(maybe_visit) = &state.maybe_visit {
for i in maybe_visit.iter() {
if self.can_move_to(state, i) {
f.apply(Decision { variable, value: i as isize })
}
}
}
}
fn transition(&self, state: &TsptwState, d: Decision) -> TsptwState {
let mut remaining = state.must_visit;
remaining.remove_inplace(d.value as usize);
let mut maybes = state.maybe_visit;
if let Some(maybe) = maybes.as_mut() {
maybe.remove_inplace(d.value as usize);
}
let time = self.arrival_time(state, d.value as usize);
TsptwState {
position : Position::Node(d.value as u16),
elapsed : time,
must_visit: remaining,
maybe_visit: maybes,
depth: state.depth + 1
}
}
fn transition_cost(&self, state: &TsptwState, _: &Self::State, d: Decision) -> isize {
let twj = self.instance.timewindows[d.value as usize];
let travel_time = self.min_distance_to(state, d.value as usize);
let waiting_time = match state.elapsed {
ElapsedTime::FixedAmount{duration} =>
if (duration + travel_time) < twj.earliest {
twj.earliest - (duration + travel_time)
} else {
0
},
ElapsedTime::FuzzyAmount{earliest, ..} =>
if (earliest + travel_time) < twj.earliest {
twj.earliest - (earliest + travel_time)
} else {
0
}
};
-( (travel_time + waiting_time) as isize)
}
fn next_variable(&self, depth: usize, _: &mut dyn Iterator<Item = &Self::State>)
-> Option<Variable> {
if depth == self.nb_variables() {
None
} else {
Some(Variable(depth))
}
}
}
impl Tsptw {
pub fn can_move_to(&self, state: &TsptwState, j: usize) -> bool {
let twj = self.instance.timewindows[j];
let min_arrival = state.elapsed.add_duration(self.min_distance_to(state, j));
match min_arrival {
ElapsedTime::FixedAmount{duration} => duration <= twj.latest,
ElapsedTime::FuzzyAmount{earliest, ..} => earliest <= twj.latest,
}
}
fn arrival_time(&self, state: &TsptwState, j: usize) -> ElapsedTime {
let min_arrival = state.elapsed.add_duration(self.min_distance_to(state, j));
let max_arrival = state.elapsed.add_duration(self.max_distance_to(state, j));
let min_arrival = match min_arrival {
ElapsedTime::FixedAmount{duration} => duration,
ElapsedTime::FuzzyAmount{earliest, ..} => earliest
};
let max_arrival = match max_arrival {
ElapsedTime::FixedAmount{duration} => duration,
ElapsedTime::FuzzyAmount{latest, ..} => latest
};
let arrival_time =
if min_arrival.eq(&max_arrival) {
ElapsedTime::FixedAmount{duration: min_arrival}
} else {
ElapsedTime::FuzzyAmount{earliest: min_arrival, latest: max_arrival}
};
let twj = self.instance.timewindows[j];
match arrival_time {
ElapsedTime::FixedAmount{duration} => {
ElapsedTime::FixedAmount{duration: duration.max(twj.earliest)}
},
ElapsedTime::FuzzyAmount{mut earliest, mut latest} => {
earliest = earliest.max(twj.earliest);
latest = latest.min(twj.latest);
if earliest.eq(&latest) {
ElapsedTime::FixedAmount{duration: earliest}
} else {
ElapsedTime::FuzzyAmount{earliest, latest}
}
},
}
}
fn min_distance_to(&self, state: &TsptwState, j: usize) -> usize {
match &state.position {
Position::Node(i) => self.instance.distances[*i as usize][j],
Position::Virtual(candidates) =>
candidates.iter()
.map(|i| self.instance.distances[i][j])
.min()
.unwrap()
}
}
fn max_distance_to(&self, state: &TsptwState, j: usize) -> usize {
match &state.position {
Position::Node(i) => self.instance.distances[*i as usize][j],
Position::Virtual(candidates) =>
candidates.iter()
.map(|i| self.instance.distances[i][j])
.max()
.unwrap()
}
}
}