use std::cmp::Ordering;
use std::hash::Hash;
use std::sync::Arc;
use compare::Compare;
use crate::core::abstraction::dp::{Problem, Relaxation};
use crate::core::abstraction::heuristics::{LoadVars, VariableHeuristic, WidthHeuristic};
use crate::core::common::{Decision, Domain, Edge, Layer, Node, NodeInfo, Variable, VarSet};
use crate::core::implementation::heuristics::{FromLongestPath, MinLP, NaturalOrder, NbUnassigned};
use crate::core::implementation::mdd::config::Config;
use crate::core::implementation::mdd::flat::FlatMDD;
use crate::core::implementation::mdd::pooled::PooledMDD;
use std::marker::PhantomData;
use std::borrow::Borrow;
pub struct MDDBuilder<T, PB, RLX,
LV = FromLongestPath,
VS = NaturalOrder,
WIDTH= NbUnassigned,
NS = MinLP> {
pb : PB,
rlx: RLX,
lv : LV,
vs : VS,
w : WIDTH,
ns : NS,
_phantom: PhantomData<T>
}
pub fn mdd_builder<T, PB, RLX>(pb: PB, rlx: RLX) -> MDDBuilder<T, PB, RLX>
where PB: Problem<T>, RLX: Relaxation<T> {
let lv = FromLongestPath::new(pb.all_vars());
MDDBuilder {
pb, rlx, lv,
vs: NaturalOrder,
w : NbUnassigned,
ns: MinLP,
_phantom: PhantomData
}
}
pub fn mdd_builder_ref<T, PB, RLX>(pb: &PB, rlx: RLX) -> MDDBuilder<T, PbRef<&PB, PB>, RLX>
where PB: Problem<T>, RLX: Relaxation<T> {
mdd_builder(PbRef::new(pb), rlx)
}
impl <T, PB, RLX, LV, VS, WIDTH, NS> MDDBuilder<T, PB, RLX, LV, VS, WIDTH, NS>
where T : Eq + Hash + Clone,
PB : Problem<T>,
RLX : Relaxation<T>,
LV : LoadVars<T>,
VS : VariableHeuristic<T>,
WIDTH: WidthHeuristic,
NS : Compare<Node<T>> {
pub fn with_load_vars<H>(self, h: H) -> MDDBuilder<T, PB, RLX, H, VS, WIDTH, NS> {
MDDBuilder {
pb : self.pb,
rlx: self.rlx,
lv : h,
vs : self.vs,
w : self.w,
ns : self.ns,
_phantom: PhantomData
}
}
pub fn with_branch_heuristic<H>(self, h: H) -> MDDBuilder<T, PB, RLX, LV, H, WIDTH, NS> {
MDDBuilder {
pb : self.pb,
rlx: self.rlx,
lv : self.lv,
vs : h,
w : self.w,
ns : self.ns,
_phantom: PhantomData
}
}
pub fn with_max_width<H>(self, h: H) -> MDDBuilder<T, PB, RLX, LV, VS, H, NS> {
MDDBuilder {
pb : self.pb,
rlx: self.rlx,
lv : self.lv,
vs : self.vs,
w : h,
ns : self.ns,
_phantom: PhantomData
}
}
pub fn with_nodes_selection_heuristic<H>(self, h: H) -> MDDBuilder<T, PB, RLX, LV, VS, WIDTH, H> {
MDDBuilder {
pb : self.pb,
rlx: self.rlx,
lv : self.lv,
vs : self.vs,
w : self.w,
ns : h,
_phantom: PhantomData
}
}
pub fn config(self) -> MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS> {
MDDConfig::new(self.pb, self.rlx, self.lv, self.vs, self.w, self.ns)
}
#[allow(clippy::type_complexity)] pub fn build(self) -> FlatMDD<T, MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS>> {
self.into_flat()
}
#[allow(clippy::type_complexity)] pub fn into_flat(self) -> FlatMDD<T, MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS>> {
FlatMDD::new(self.config())
}
#[allow(clippy::type_complexity)] pub fn into_pooled(self) -> PooledMDD<T, MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS>> {
PooledMDD::new(self.config())
}
}
#[derive(Clone)]
pub struct MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS> {
pb : PB,
relax : RLX,
lv : LV,
vs : VS,
width : WIDTH,
ns : NS,
vars : VarSet,
_phantom: PhantomData<T>
}
impl <T, PB, RLX, LV, VS, WIDTH, NS> Config<T> for MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS>
where T : Eq + Hash + Clone,
PB : Problem<T>,
RLX : Relaxation<T>,
LV : LoadVars<T>,
VS : VariableHeuristic<T>,
WIDTH: WidthHeuristic,
NS : Compare<Node<T>> {
fn root_node(&self) -> Node<T> {
self.pb.root_node()
}
fn impacted_by(&self, state: &T, v: Variable) -> bool {
self.pb.impacted_by(state, v)
}
fn load_vars(&mut self, root: &Node<T>) {
self.vars = self.lv.variables(root);
}
fn nb_free_vars(&self) -> usize {
self.vars.len()
}
fn select_var(&self, current: Layer<'_, T>, next: Layer<'_, T>) -> Option<Variable> {
self.vs.next_var(&self.vars, current, next)
}
fn remove_var(&mut self, v: Variable) {
self.vars.remove(v)
}
fn domain_of<'b>(&self, state: &'b T, v: Variable) -> Domain<'b> {
self.pb.domain_of(state, v)
}
fn max_width(&self) -> usize {
self.width.max_width(&self.vars)
}
fn branch(&self, state: &T, info: Arc<NodeInfo>, d: Decision) -> Node<T> {
let next = self.transition_state(state, d);
let cost = self.transition_cost (state, d);
let path = NodeInfo {
is_exact : info.is_exact,
is_relaxed: false,
lp_len : info.lp_len + cost,
ub : info.ub,
lp_arc : Some(Edge{src: info, decision: d}),
};
Node { state: next, info: path}
}
fn estimate_ub(&self, state: &T, info: &NodeInfo) -> i32 {
self.relax.estimate_ub(state, info)
}
fn compare(&self, x: &Node<T>, y: &Node<T>) -> Ordering {
self.ns.compare(x, y)
}
fn merge_nodes(&self, nodes: &[Node<T>]) -> Node<T> {
self.relax.merge_nodes(nodes)
}
fn transition_state(&self, state: &T, d: Decision) -> T {
self.pb.transition(state, &self.vars, d)
}
fn transition_cost(&self, state: &T, d: Decision) -> i32 {
self.pb.transition_cost(state, &self.vars, d)
}
}
impl <T, PB, RLX, LV, VS, WIDTH, NS> MDDConfig<T, PB, RLX, LV, VS, WIDTH, NS>
where T : Eq + Hash + Clone,
PB : Problem<T>,
RLX : Relaxation<T>,
LV : LoadVars<T>,
VS : VariableHeuristic<T>,
WIDTH: WidthHeuristic,
NS : Compare<Node<T>> {
fn new(pb: PB, relax: RLX, lv: LV, vs: VS, width: WIDTH, ns: NS) -> Self {
let vars = VarSet::all(pb.nb_vars());
MDDConfig { pb, relax, lv, vs, width, ns, vars, _phantom: PhantomData }
}
}
#[derive(Clone)]
pub struct PbRef<X, P> {
reference: X,
_phantom : PhantomData<P>
}
impl <X, P> PbRef<X, P> {
pub fn new(x: X) -> Self {
PbRef {reference: x, _phantom: PhantomData}
}
}
impl <T, P, X> Problem<T> for PbRef<X, P>
where P: Problem<T>,
X: Borrow<P> {
fn nb_vars(&self) -> usize {
self.reference.borrow().nb_vars()
}
fn initial_state(&self) -> T {
self.reference.borrow().initial_state()
}
fn initial_value(&self) -> i32 {
self.reference.borrow().initial_value()
}
fn domain_of<'a>(&self, state: &'a T, var: Variable) -> Domain<'a> {
self.reference.borrow().domain_of(state, var)
}
fn transition(&self, state: &T, vars: &VarSet, d: Decision) -> T {
self.reference.borrow().transition(state, vars, d)
}
fn transition_cost(&self, state: &T, vars: &VarSet, d: Decision) -> i32 {
self.reference.borrow().transition_cost(state, vars, d)
}
fn impacted_by(&self, state: &T, variable: Variable) -> bool {
self.reference.borrow().impacted_by(state, variable)
}
fn root_node(&self) -> Node<T> {
self.reference.borrow().root_node()
}
fn all_vars(&self) -> VarSet {
self.reference.borrow().all_vars()
}
}
#[cfg(test)]
mod test_config_builder {
use std::sync::Arc;
use mock_it::verify;
use crate::core::abstraction::dp::Problem;
use crate::core::abstraction::mdd::MDD;
use crate::core::common::{Decision, Edge, Layer, Node, NodeInfo, Variable, VarSet};
use crate::core::implementation::mdd::builder::mdd_builder_ref;
use crate::core::implementation::mdd::config::Config;
use crate::test_utils::{MockLoadVars, MockMaxWidth, MockNodeSelectionHeuristic, MockProblem, MockRelax, MockVariableHeuristic, Nothing, Proxy};
#[test]
fn root_node_is_pass_though_for_problem() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, relax).config();
let _ = config.root_node();
assert!(verify(prob.root_node.was_called_with(Nothing)));
}
#[test]
fn impacted_by_is_pass_though_for_problem() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, relax).config();
let _ = config.impacted_by(&0, Variable(0));
assert!(verify(prob.impacted_by.was_called_with((0, Variable(0)))));
let _ = config.impacted_by(&100, Variable(25));
assert!(verify(prob.impacted_by.was_called_with((100, Variable(25)))));
}
#[test]
fn load_vars_is_pass_through_for_heuristic() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let lv = MockLoadVars::default();
let mut config = mdd_builder_ref(&prob, relax).with_load_vars(Proxy::new(&lv)).config();
let node = Node::new(0, 0, None, true, false);
config.load_vars(&node);
assert!(verify(lv.variables.was_called_with(node.clone())));
let node = Node::new(10, 1000, Some(Edge{ src: Arc::new(node.info), decision: Decision{variable: Variable(7), value: 12}}), true, false);
config.load_vars(&node);
assert!(verify(lv.variables.was_called_with(node)));
}
#[test]
fn domain_is_pass_though_for_problem() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, relax).config();
let _ = config.domain_of(&0, Variable(2));
assert!(verify(prob.domain_of.was_called_with((0, Variable(2)))));
let _ = config.domain_of(&20, Variable(34));
assert!(verify(prob.domain_of.was_called_with((20, Variable(34)))));
}
#[test]
fn nb_free_vars_defaults_to_all_vars() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, relax).config();
assert_eq!(5, config.nb_free_vars());
}
#[test]
fn nb_free_vars_decreases_upon_remove() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let mut config = mdd_builder_ref(&prob, relax).config();
config.remove_var(Variable(3));
assert_eq!(4, config.nb_free_vars());
}
#[test]
fn removed_var_cannot_be_selected_anymore() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let mut config = mdd_builder_ref(&prob, relax).config();
let data = vec![];
let mut layer1 = Layer::Plain(data.iter());
let mut layer2 = Layer::Plain(data.iter());
let mut avail= 5;
while let Some(v) = config.select_var(layer1, layer2) {
config.remove_var(v);
avail -= 1;
assert_eq!(avail, config.nb_free_vars());
layer1 = Layer::Plain(data.iter());
layer2 = Layer::Plain(data.iter());
}
assert_eq!(0, avail);
}
#[test]
fn max_width_is_pass_through_for_heuristic() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let heu = MockMaxWidth::default();
let mut config = mdd_builder_ref(&prob, relax).with_max_width(Proxy::new(&heu)).config();
let mut vs = prob.all_vars();
while ! vs.is_empty() {
let _ = config.max_width();
assert!(verify(heu.max_width.was_called_with(vs.clone())));
if let Some(v) = vs.iter().next() {
vs.remove(v);
config.remove_var(v);
}
}
}
#[test]
fn branch_applies_the_desired_transition() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, relax).config();
let varset = VarSet::all(5);
let state = 0;
let decision= Decision {variable: Variable(4), value: 7};
let info = NodeInfo {is_exact: true, lp_len: 10, lp_arc: None, ub: 100, is_relaxed: false};
let info = Arc::new(info);
prob.transition.given((state, varset.clone(), decision)).will_return(10);
prob.transition_cost.given((state, varset, decision)).will_return(666);
assert_eq!(
config.branch(&state, info.clone(), decision),
Node {
state: 10,
info: NodeInfo {
is_exact: true,
is_relaxed: false,
lp_len : 676,
lp_arc : Some(Edge {
src: info,
decision
}),
ub: 100
}
}
);
}
#[test]
fn branch_maintains_the_exact_flat() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, relax).config();
let varset = VarSet::all(5);
let state = 0;
let decision= Decision {variable: Variable(4), value: 7};
let info = NodeInfo {is_exact: false, lp_len: 10, lp_arc: None, ub: 100, is_relaxed: true};
let info = Arc::new(info);
prob.transition.given((state, varset.clone(), decision)).will_return(10);
prob.transition_cost.given((state, varset, decision)).will_return(666);
assert_eq!(
config.branch(&state, info.clone(), decision),
Node {
state: 10,
info: NodeInfo {
is_exact: false,
is_relaxed: true,
lp_len : 676,
lp_arc : Some(Edge {
src: info,
decision
}),
ub: 100
}
}
);
}
#[test]
fn estimate_ub_is_a_pass_through_for_relaxation() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, Proxy::new(&relax)).config();
let state = 12;
let info = NodeInfo {is_exact: false, lp_len: 28, lp_arc: None, ub: 40, is_relaxed: true};
config.estimate_ub(&state, &info);
assert!(verify(relax.estimate_ub.was_called_with((state, info))));
}
#[test]
fn merge_nodes_is_a_pass_through_for_relaxation_empty() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, Proxy::new(&relax)).config();
let nodes = vec![];
config.merge_nodes(&nodes);
assert!(verify(relax.merge_nodes.was_called_with(nodes)));
}
#[test]
fn merge_nodes_is_a_pass_through_for_relaxation_nonempty() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let config = mdd_builder_ref(&prob, Proxy::new(&relax)).config();
let nodes = vec![Node {state: 129, info: NodeInfo{is_exact: false, lp_len: 27, lp_arc: None, ub: 65, is_relaxed: true}}];
config.merge_nodes(&nodes);
assert!(verify(relax.merge_nodes.was_called_with(nodes)));
}
#[test]
fn compare_is_a_pass_through_to_node_selection_heuristic() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let heu = MockNodeSelectionHeuristic::default();
let config = mdd_builder_ref(&prob, Proxy::new(&relax)).with_nodes_selection_heuristic(Proxy::new(&heu)).config();
let node_a = Node {state: 129, info: NodeInfo{is_exact: false, lp_len: 27, lp_arc: None, ub: 65, is_relaxed: false}};
let node_b = Node {state: 123, info: NodeInfo{is_exact: true, lp_len: 24, lp_arc: None, ub: 99, is_relaxed: false}};
config.compare(&node_a, &node_b);
assert!(verify(heu.compare.was_called_with((node_a, node_b))));
let node_a = Node {state: 129, info: NodeInfo{is_exact: false, lp_len: 27, lp_arc: None, ub: 65, is_relaxed: false}};
let node_b = Node {state: 123, info: NodeInfo{is_exact: true, lp_len: 24, lp_arc: None, ub: 99, is_relaxed: false}};
config.compare(&node_b, &node_a);
assert!(verify(heu.compare.was_called_with((node_b, node_a))));
let node_b = Node {state: 123, info: NodeInfo{is_exact: true, lp_len: 24, lp_arc: None, ub: 99, is_relaxed: false}};
config.compare(&node_b, &node_b);
assert!(verify(heu.compare.was_called_with((node_b.clone(), node_b))));
}
#[test]
fn select_var_is_a_pass_through_to_heuristic() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let heu = MockVariableHeuristic::default();
let config = mdd_builder_ref(&prob, Proxy::new(&relax)).with_branch_heuristic(Proxy::new(&heu)).config();
let data = vec![];
let curr = Layer::Plain(data.iter());
let next = Layer::Plain(data.iter());
config.select_var(curr, next);
assert!(verify(heu.next_var.was_called_with((prob.all_vars(), vec![], vec![]))));
let data1 = vec![Node {state: 129, info: NodeInfo{is_exact: false, lp_len: 27, lp_arc: None, ub: 65, is_relaxed: false}}];
let data2 = vec![Node {state: 123, info: NodeInfo{is_exact: true, lp_len: 24, lp_arc: None, ub: 99, is_relaxed: false}}];
let curr = Layer::Plain(data1.iter());
let next = Layer::Plain(data2.iter());
config.select_var(curr, next);
assert!(verify(heu.next_var.was_called_with((prob.all_vars(), data1, data2))))
}
#[test]
fn it_can_build_an_mdd() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let mdd = mdd_builder_ref(&prob, Proxy::new(&relax)).build();
assert_eq!(prob.root_node(), mdd.root())
}
#[test]
fn it_can_build_a_flat_mdd() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let mdd = mdd_builder_ref(&prob, Proxy::new(&relax)).into_flat();
assert_eq!(prob.root_node(), mdd.root())
}
#[test]
fn it_can_build_a_pooled_mdd() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let mdd = mdd_builder_ref(&prob, Proxy::new(&relax)).into_pooled();
assert_eq!(prob.root_node(), mdd.root())
}
#[test]
fn it_can_build_an_mdd_from_ref() {
let prob = MockProblem::default();
let relax = MockRelax::default();
let mdd = mdd_builder_ref(&prob, Proxy::new(&relax)).build();
assert_eq!(prob.root_node(), mdd.root())
}
}