use super::independent_variable_value_brancher::IndependentVariableValueBrancher;
use crate::DefaultBrancher;
use crate::basic_types::DeletablePredicateIdGenerator;
use crate::basic_types::PredicateId;
use crate::basic_types::SolutionReference;
use crate::branching::Brancher;
use crate::branching::BrancherEvent;
use crate::branching::SelectionContext;
use crate::branching::value_selection::RandomSplitter;
use crate::branching::variable_selection::RandomSelector;
use crate::containers::KeyValueHeap;
use crate::containers::StorageKey;
use crate::create_statistics_struct;
use crate::engine::Assignments;
use crate::engine::predicates::predicate::Predicate;
use crate::propagation::ReadDomains;
use crate::results::Solution;
use crate::statistics::Statistic;
use crate::statistics::StatisticLogger;
use crate::statistics::moving_averages::CumulativeMovingAverage;
use crate::statistics::moving_averages::MovingAverage;
use crate::variables::DomainId;
#[derive(Debug)]
pub struct AutonomousSearch<BackupBrancher> {
predicate_id_info: DeletablePredicateIdGenerator,
heap: KeyValueHeap<PredicateId, f64>,
dormant_predicates: Vec<Predicate>,
increment: f64,
max_threshold: f64,
decay_factor: f64,
best_known_solution: Option<Solution>,
backup_brancher: BackupBrancher,
statistics: AutonomousSearchStatistics,
should_synchronise: bool,
}
create_statistics_struct!(AutonomousSearchStatistics {
num_backup_called: usize,
num_predicates_removed: usize,
num_calls: usize,
num_predicates_added: usize,
average_size_of_heap: CumulativeMovingAverage<usize>,
num_assigned_predicates_encountered: usize,
});
const DEFAULT_VSIDS_INCREMENT: f64 = 1.0;
const DEFAULT_VSIDS_MAX_THRESHOLD: f64 = 1e100;
const DEFAULT_VSIDS_DECAY_FACTOR: f64 = 0.95;
const DEFAULT_VSIDS_VALUE: f64 = 0.0;
impl DefaultBrancher {
pub fn default_over_all_variables(assignments: &Assignments) -> DefaultBrancher {
AutonomousSearch {
predicate_id_info: DeletablePredicateIdGenerator::default(),
heap: KeyValueHeap::default(),
dormant_predicates: vec![],
increment: DEFAULT_VSIDS_INCREMENT,
max_threshold: DEFAULT_VSIDS_MAX_THRESHOLD,
decay_factor: DEFAULT_VSIDS_DECAY_FACTOR,
best_known_solution: None,
should_synchronise: false,
backup_brancher: IndependentVariableValueBrancher::new(
RandomSelector::new(assignments.get_domains()),
RandomSplitter,
),
statistics: Default::default(),
}
}
pub fn add_domain(&mut self, domain: DomainId) {
self.backup_brancher.variable_selector.add_domain(domain);
}
}
impl<BackupSelector> AutonomousSearch<BackupSelector> {
pub fn new(backup_brancher: BackupSelector) -> Self {
AutonomousSearch {
predicate_id_info: DeletablePredicateIdGenerator::default(),
heap: KeyValueHeap::default(),
dormant_predicates: vec![],
increment: DEFAULT_VSIDS_INCREMENT,
max_threshold: DEFAULT_VSIDS_MAX_THRESHOLD,
decay_factor: DEFAULT_VSIDS_DECAY_FACTOR,
best_known_solution: None,
should_synchronise: false,
backup_brancher,
statistics: Default::default(),
}
}
fn resize_heap(&mut self, id: PredicateId) {
while self.heap.len() <= id.index() {
self.heap.grow(id, DEFAULT_VSIDS_VALUE);
}
}
fn bump_activity(&mut self, predicate: Predicate) {
self.statistics.num_predicates_added +=
(!self.predicate_id_info.has_id_for_predicate(predicate)) as usize;
let id = self.predicate_id_info.get_id(predicate);
self.resize_heap(id);
self.heap.restore_key(id);
let activity = self.heap.get_value(id);
if activity + self.increment >= self.max_threshold {
self.heap.divide_values(self.max_threshold);
self.increment /= self.max_threshold;
}
self.heap.increment(id, self.increment);
}
fn decay_activities(&mut self) {
self.increment *= 1.0 / self.decay_factor;
}
fn next_candidate_predicate(&mut self, context: &mut SelectionContext) -> Option<Predicate> {
loop {
if let Some((candidate, _)) = self.heap.peek_max() {
let predicate = self
.predicate_id_info
.get_predicate(*candidate)
.expect("Expected predicate id to exist");
if context.is_predicate_assigned(predicate) {
self.statistics.num_assigned_predicates_encountered += 1;
let _ = self.heap.pop_max();
let predicate_id = self.predicate_id_info.get_id(predicate);
self.heap.delete_key(predicate_id);
self.predicate_id_info.delete_id(predicate_id);
self.dormant_predicates.push(predicate);
} else {
return Some(predicate);
}
} else {
return None;
}
}
}
fn determine_polarity(&self, predicate: Predicate) -> Predicate {
if let Some(solution) = &self.best_known_solution {
if !solution.contains_domain_id(predicate.get_domain()) {
return predicate;
}
if solution.evaluate_predicate(predicate) == Some(true) {
predicate
} else {
!predicate
}
} else {
predicate
}
}
fn synchronise_internal(&mut self) {
self.dormant_predicates.drain(..).for_each(|predicate| {
let id = self.predicate_id_info.get_id(predicate);
while self.heap.len() <= id.index() {
self.heap.grow(id, DEFAULT_VSIDS_VALUE);
}
self.heap.restore_key(id);
});
}
}
impl<BackupBrancher: Brancher> Brancher for AutonomousSearch<BackupBrancher> {
fn next_decision(&mut self, context: &mut SelectionContext) -> Option<Predicate> {
if self.should_synchronise {
self.synchronise_internal();
self.should_synchronise = false;
}
self.statistics.num_calls += 1;
self.statistics
.average_size_of_heap
.add_term(self.heap.num_nonremoved_elements());
let result = self
.next_candidate_predicate(context)
.map(|predicate| self.determine_polarity(predicate));
if result.is_none() && !context.are_all_variables_assigned() {
self.statistics.num_backup_called += 1;
self.backup_brancher.next_decision(context)
} else {
result
}
}
fn log_statistics(&self, statistic_logger: StatisticLogger) {
let statistic_logger = statistic_logger.attach_to_prefix("AutonomousSearch");
self.statistics.log(statistic_logger);
}
fn on_backtrack(&mut self) {
self.backup_brancher.on_backtrack()
}
fn synchronise(&mut self, context: &mut SelectionContext) {
self.should_synchronise = true;
self.backup_brancher.synchronise(context);
}
fn on_conflict(&mut self) {
self.decay_activities();
self.backup_brancher.on_conflict();
}
fn on_solution(&mut self, solution: SolutionReference) {
self.best_known_solution = Some(solution.into());
self.backup_brancher.on_solution(solution);
}
fn on_appearance_in_conflict_predicate(&mut self, predicate: Predicate) {
self.bump_activity(predicate);
self.backup_brancher
.on_appearance_in_conflict_predicate(predicate);
}
fn on_restart(&mut self) {
self.backup_brancher.on_restart();
}
fn on_unassign_integer(&mut self, variable: DomainId, value: i32) {
self.backup_brancher.on_unassign_integer(variable, value)
}
fn is_restart_pointless(&mut self) -> bool {
false
}
fn subscribe_to_events(&self) -> Vec<BrancherEvent> {
[
BrancherEvent::Solution,
BrancherEvent::Conflict,
BrancherEvent::Backtrack,
BrancherEvent::Synchronise,
BrancherEvent::AppearanceInConflictPredicate,
]
.into_iter()
.chain(self.backup_brancher.subscribe_to_events())
.collect()
}
}
#[cfg(test)]
mod tests {
use super::AutonomousSearch;
use crate::basic_types::tests::TestRandom;
use crate::branching::Brancher;
use crate::branching::SelectionContext;
use crate::engine::Assignments;
use crate::engine::notifications::NotificationEngine;
use crate::predicate;
use crate::results::SolutionReference;
#[test]
fn brancher_picks_bumped_values() {
let mut assignments = Assignments::default();
let x = assignments.grow(0, 10);
let y = assignments.grow(-10, 0);
let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5));
brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5));
brancher.on_appearance_in_conflict_predicate(predicate!(y >= -5));
(0..100).for_each(|_| brancher.on_conflict());
}
#[test]
fn dormant_values() {
let mut notification_engine = NotificationEngine::default();
let mut assignments = Assignments::default();
let x = assignments.grow(0, 10);
notification_engine.grow();
let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
let predicate = predicate!(x >= 5);
brancher.on_appearance_in_conflict_predicate(predicate);
let decision = brancher.next_decision(&mut SelectionContext::new(
&assignments,
&mut TestRandom::default(),
));
assert_eq!(decision, Some(predicate));
assignments.new_checkpoint();
let _ = assignments.post_predicate(predicate!(x >= 5), None, &mut notification_engine);
assignments.new_checkpoint();
let _ = assignments.post_predicate(predicate!(x >= 7), None, &mut notification_engine);
assignments.new_checkpoint();
let _ = assignments.post_predicate(predicate!(x >= 10), None, &mut notification_engine);
assignments.new_checkpoint();
let decision = brancher.next_decision(&mut SelectionContext::new(
&assignments,
&mut TestRandom::default(),
));
assert!(decision.is_none());
assert!(brancher.dormant_predicates.contains(&predicate));
let _ = assignments.synchronise(3, &mut notification_engine);
let decision = brancher.next_decision(&mut SelectionContext::new(
&assignments,
&mut TestRandom::default(),
));
assert!(decision.is_none());
assert!(brancher.dormant_predicates.contains(&predicate));
let _ = assignments.synchronise(0, &mut notification_engine);
brancher.synchronise(&mut SelectionContext::new(
&assignments,
&mut TestRandom::default(),
));
let decision = brancher.next_decision(&mut SelectionContext::new(
&assignments,
&mut TestRandom::default(),
));
assert_eq!(decision, Some(predicate));
assert!(!brancher.dormant_predicates.contains(&predicate));
}
#[test]
fn uses_fallback() {
let mut assignments = Assignments::default();
let x = assignments.grow(0, 10);
let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
let result = brancher.next_decision(&mut SelectionContext::new(
&assignments,
&mut TestRandom {
integers: vec![2],
usizes: vec![0],
bools: vec![false],
weighted_choice: |_| unreachable!(),
},
));
assert_eq!(result, Some(predicate!(x <= 2)));
}
#[test]
fn uses_stored_solution() {
let mut notification_engine = NotificationEngine::default();
let mut assignments = Assignments::default();
let x = assignments.grow(0, 10);
notification_engine.grow();
assignments.new_checkpoint();
let _ = assignments.post_predicate(predicate!(x == 7), None, &mut notification_engine);
let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
brancher.on_solution(SolutionReference::new(&assignments));
let _ = assignments.synchronise(0, &mut notification_engine);
assert_eq!(
predicate!(x >= 5),
brancher.determine_polarity(predicate!(x >= 5))
);
assert_eq!(
!predicate!(x >= 10),
brancher.determine_polarity(predicate!(x >= 10))
);
assert_eq!(
predicate!(x <= 8),
brancher.determine_polarity(predicate!(x <= 8))
);
assert_eq!(
!predicate!(x <= 5),
brancher.determine_polarity(predicate!(x <= 5))
);
brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5));
let result = brancher.next_decision(&mut SelectionContext::new(
&assignments,
&mut TestRandom::default(),
));
assert_eq!(result, Some(predicate!(x >= 5)));
}
}