use std::cmp::PartialOrd;
use std::collections::hash_map::Entry;
use std::hash::Hash;
use fxhash::FxHashMap;
use crate::search_space::{SearchSpace, GuidedSpace, TotalNeighborGeneration, PartialNeighborGeneration, Identifiable, ParetoDominanceSpace, ToSolution};
use crate::search_decorator::SearchSpaceDecorator;
#[derive(Debug)]
pub struct DominanceInfo<B> {
val: B,
iter: u32,
}
#[derive(Debug)]
pub struct DominanceStore<Id, B> {
store: FxHashMap<Id, DominanceInfo<B>>,
nb_gets: usize,
nb_dominations: usize,
nb_updates: usize,
}
impl<Id, B> Default for DominanceStore<Id, B> where Id:Eq+Hash, B:PartialOrd {
fn default() -> Self {
Self {
store: FxHashMap::default(),
nb_gets: 0,
nb_dominations: 0,
nb_updates: 0
}
}
}
impl<Id, B> DominanceStore<Id, B> where Id:Eq+Hash, B:PartialOrd {
pub fn is_dominated_or_add(&mut self, pe:Id, b:B, iter:u32) -> bool {
self.nb_gets += 1;
match self.store.entry(pe) {
Entry::Occupied(o) => {
let info = o.into_mut();
if info.val < b
|| (info.val == b && info.iter == iter)
{
self.nb_dominations += 1;
true } else {
info.val = b;
info.iter = iter;
self.nb_updates += 1;
false
}
}
Entry::Vacant(v) => {
v.insert(DominanceInfo {
val: b,
iter,
});
false
}
}
}
pub fn display_statistics(&self) {
let format = |e| human_format::Formatter::new().with_decimals(1).format(e);
println!("{:>25}{:>15}", "nb elts", format(self.store.len() as f64));
println!("{:>25}{:>15}", "nb gets", format(self.nb_gets as f64));
println!("{:>25}{:>15}", "nb pruned", format(self.nb_dominations as f64));
println!("{:>25}{:>15}", "nb updates", format(self.nb_updates as f64));
}
pub fn export_statistics(&self, json:&mut serde_json::Value) {
json["gcost_dom_nb_elts"] = serde_json::json!(self.store.len());
json["gcost_dom_nb_gets"] = serde_json::json!(self.nb_gets);
json["gcost_dom_nb_pruned"] = serde_json::json!(self.nb_dominations);
json["gcost_dom_nb_updates"] = serde_json::json!(self.nb_updates);
}
}
#[derive(Debug)]
pub struct GcostDominanceTsDecorator<Space, Id, B> {
s: Space,
current_iter: u32,
store: DominanceStore<Id, B>,
}
impl<N,G,Space,Id,B> GuidedSpace<N,G> for GcostDominanceTsDecorator<Space, Id, B>
where Space:GuidedSpace<N,G>
{
fn guide(&mut self, n: &N) -> G { self.s.guide(n) }
}
impl<N,Sol,Space,Id,B> ToSolution<N,Sol> for GcostDominanceTsDecorator<Space, Id, B>
where Space:ToSolution<N,Sol> {
fn solution(&mut self, node: &mut N) -> Sol { self.s.solution(node) }
}
impl<N,Space,Id,B> SearchSpace<N,B> for GcostDominanceTsDecorator<Space, Id, B>
where Space:SearchSpace<N,B>, B:serde::Serialize+PartialOrd, Id:Hash+Eq
{
fn initial(&mut self) -> N { self.s.initial() }
fn bound(&mut self, node: &N) -> B { self.s.bound(node) }
fn g_cost(&mut self, node: &N) -> B { self.s.g_cost(node) }
fn goal(&mut self, node: &N) -> bool { self.s.goal(node) }
fn restart(&mut self, msg: String) {
self.current_iter += 1;
self.s.restart(msg);
}
fn handle_new_best(&mut self, n: N) -> N {
self.s.handle_new_best(n)
}
fn stop_search(&mut self, _msg: String) {
self.s.stop_search(_msg);
}
fn display_statistics(&self) {
self.store.display_statistics();
println!();
self.s.display_statistics();
}
fn json_statistics(&self, json:&mut serde_json::Value) {
self.store.export_statistics(json);
self.s.json_statistics(json);
}
}
impl<N, Space, Id, B> TotalNeighborGeneration<N> for GcostDominanceTsDecorator<Space, Id, B>
where
Space: TotalNeighborGeneration<N>+Identifiable<N, Id>+SearchSpace<N,B>,
Id: Eq + Hash,
B: PartialOrd
{
fn neighbors(&mut self, node: &mut N) -> Vec<N> {
let pe = self.s.id(node);
let bound = self.s.g_cost(node);
if self.store.is_dominated_or_add(pe, bound, self.current_iter) { Vec::new() }
else { self.s.neighbors(node) }
}
}
impl<Space, Id, B> SearchSpaceDecorator<Space> for GcostDominanceTsDecorator<Space, Id, B> {
fn unwrap(&self) -> &Space { &self.s }
}
impl<Space, Id, B> GcostDominanceTsDecorator<Space, Id, B> where Id:Hash+Eq, B:PartialOrd {
pub fn unwrap(&self) -> &Space { &self.s }
pub fn new(s: Space) -> Self {
Self {
s,
store: DominanceStore::default(),
current_iter: 0,
}
}
}
impl<N,Space,Id,B> ParetoDominanceSpace<N> for GcostDominanceTsDecorator<Space, Id, B>
where Space: ParetoDominanceSpace<N>
{
fn dominates(&self, a:&N, b:&N) -> bool { self.s.dominates(a,b) }
}
impl<N,Space,Id,B> PartialNeighborGeneration<N> for GcostDominanceTsDecorator<Space, Id, B>
where
Space: PartialNeighborGeneration<N>+Identifiable<N, Id>+SearchSpace<N,B>,
Id: Eq + Hash,
B: PartialOrd
{
fn next_neighbor(&mut self, node: &mut N) -> Option<N> {
match self.s.next_neighbor(node) {
None => { None },
Some(mut c) => {
let id = self.s.id(&mut c);
let prefix_bound = self.s.g_cost(&c);
if self.store.is_dominated_or_add(id, prefix_bound, self.current_iter) {
self.next_neighbor(node) } else { Some(c) }
}
}
}
}