use std::cmp;
use crate::containers::HashSet;
use crate::containers::KeyedVec;
use crate::containers::SparseSet;
use crate::create_statistics_struct;
use crate::engine::Assignments;
use crate::predicate;
use crate::predicates::Predicate;
use crate::predicates::PredicateType;
use crate::statistics::moving_averages::CumulativeMovingAverage;
use crate::statistics::moving_averages::MovingAverage;
use crate::variables::DomainId;
#[derive(Clone, Debug)]
pub(super) struct SemanticMinimiser {
original_domains: KeyedVec<DomainId, SimpleIntegerDomain>,
domains: KeyedVec<DomainId, SimpleIntegerDomain>,
present_ids: SparseSet<DomainId>,
helper: Vec<Predicate>,
statistics: SemanticMinimiserStatistics,
}
create_statistics_struct!(SemanticMinimiserStatistics {
average_number_of_removed_atomic_constraints_semantic: CumulativeMovingAverage<u64>,
});
impl Default for SemanticMinimiser {
fn default() -> Self {
Self {
original_domains: Default::default(),
domains: Default::default(),
present_ids: SparseSet::new(vec![]),
helper: Vec::default(),
statistics: SemanticMinimiserStatistics::default(),
}
}
}
impl SemanticMinimiser {
pub(crate) fn minimise_internal(
&mut self,
assignments: &Assignments,
nogood: &mut Vec<Predicate>,
) {
self.accommodate(assignments);
self.clean_up();
self.apply_predicates(nogood);
let len_before = nogood.len();
for domain_id in self.present_ids.iter() {
if self.domains[domain_id].inconsistent {
*nogood = vec![Predicate::trivially_false()];
return;
}
self.domains[domain_id].add_domain_description_to_vector(
*domain_id,
&self.original_domains[domain_id],
&mut self.helper,
);
}
*nogood = self.helper.clone();
self.statistics
.average_number_of_removed_atomic_constraints_semantic
.add_term((len_before - nogood.len()) as u64);
}
}
impl SemanticMinimiser {
fn apply_predicates(&mut self, nogood: &Vec<Predicate>) {
for predicate in nogood {
self.present_ids.insert(predicate.get_domain());
let domain_id = predicate.get_domain();
let value = predicate.get_right_hand_side();
match predicate.get_predicate_type() {
PredicateType::LowerBound => {
self.domains[domain_id].tighten_lower_bound(value);
}
PredicateType::UpperBound => {
self.domains[domain_id].tighten_upper_bound(value);
}
PredicateType::NotEqual => {
self.domains[domain_id].add_hole(value);
}
PredicateType::Equal => {
self.domains[domain_id].assign(value);
}
}
}
for domain_id in self.present_ids.iter() {
self.domains[*domain_id].propagate_holes_on_lower_bound();
self.domains[*domain_id].propagate_holes_on_upper_bound();
self.domains[*domain_id].remove_redundant_holes();
self.domains[*domain_id].update_consistency();
}
}
fn accommodate(&mut self, context: &Assignments) {
assert!(self.domains.len() == self.original_domains.len());
while (self.domains.len() as u32) < context.num_domains() {
let domain_id = DomainId::new(self.domains.len() as u32);
let lower_bound = context.get_initial_lower_bound(domain_id);
let upper_bound = context.get_initial_upper_bound(domain_id);
let holes = context.get_initial_holes(domain_id);
self.grow(lower_bound, upper_bound, holes);
}
}
fn grow(&mut self, lower_bound: i32, upper_bound: i32, holes: Vec<i32>) {
let mut initial_domain = SimpleIntegerDomain {
lower_bound,
upper_bound,
holes: HashSet::from_iter(holes.iter().cloned()),
inconsistent: false,
};
initial_domain.propagate_holes_on_lower_bound();
initial_domain.propagate_holes_on_upper_bound();
initial_domain.remove_redundant_holes();
initial_domain.update_consistency();
let _ = self.original_domains.push(initial_domain.clone());
let _ = self.domains.push(initial_domain);
}
pub(crate) fn clean_up(&mut self) {
let vals: Vec<DomainId> = self.present_ids.iter().copied().collect();
for domain_id in vals {
self.present_ids.remove(&domain_id);
self.domains[domain_id] = self.original_domains[domain_id].clone();
}
self.helper.clear();
}
}
#[derive(Clone, Default, Debug)]
struct SimpleIntegerDomain {
lower_bound: i32,
upper_bound: i32,
holes: HashSet<i32>,
inconsistent: bool,
}
impl SimpleIntegerDomain {
fn tighten_lower_bound(&mut self, lower_bound: i32) {
self.lower_bound = cmp::max(self.lower_bound, lower_bound);
}
fn tighten_upper_bound(&mut self, upper_bound: i32) {
self.upper_bound = cmp::min(self.upper_bound, upper_bound);
}
fn add_hole(&mut self, hole: i32) {
if self.lower_bound <= hole && hole <= self.upper_bound {
let _ = self.holes.insert(hole);
}
}
fn assign(&mut self, value: i32) {
if self.lower_bound > self.upper_bound
|| self.lower_bound > value
|| self.upper_bound < value
{
self.inconsistent = true;
}
else {
self.lower_bound = value;
self.upper_bound = value;
}
}
fn propagate_holes_on_lower_bound(&mut self) {
while self.holes.contains(&self.lower_bound) && self.lower_bound <= self.upper_bound {
self.lower_bound += 1;
}
}
fn propagate_holes_on_upper_bound(&mut self) {
while self.holes.contains(&self.upper_bound) && self.lower_bound <= self.upper_bound {
self.upper_bound -= 1;
}
}
fn update_consistency(&mut self) {
if !self.inconsistent {
self.inconsistent = self.lower_bound > self.upper_bound;
}
}
fn remove_redundant_holes(&mut self) {
if self.inconsistent {
return;
}
self.holes
.retain(|hole| self.lower_bound < *hole && *hole < self.upper_bound);
}
fn add_domain_description_to_vector(
&self,
domain_id: DomainId,
original_domain: &SimpleIntegerDomain,
description: &mut Vec<Predicate>,
) {
if self.lower_bound == self.upper_bound
&& self.lower_bound != original_domain.lower_bound
&& self.upper_bound != original_domain.upper_bound
{
description.push(predicate![domain_id == self.lower_bound]);
return;
}
if self.lower_bound != original_domain.lower_bound {
description.push(predicate![domain_id >= self.lower_bound]);
}
if self.upper_bound != original_domain.upper_bound {
description.push(predicate![domain_id <= self.upper_bound]);
}
for hole in self.holes.iter() {
if self.lower_bound < *hole
&& *hole < self.upper_bound
&& !original_domain.holes.contains(hole)
{
description.push(predicate![domain_id != *hole])
}
}
}
}