use std::fmt::Debug;
use crate::basic_types::sequence_generators::ConstantSequence;
use crate::basic_types::sequence_generators::GeometricSequence;
use crate::basic_types::sequence_generators::LubySequence;
use crate::basic_types::sequence_generators::SequenceGenerator;
use crate::basic_types::sequence_generators::SequenceGeneratorType;
use crate::pumpkin_assert_simple;
use crate::statistics::moving_averages::CumulativeMovingAverage;
use crate::statistics::moving_averages::MovingAverage;
use crate::statistics::moving_averages::WindowedMovingAverage;
#[derive(Debug, Clone, Copy)]
pub struct RestartOptions {
pub sequence_generator_type: SequenceGeneratorType,
pub base_interval: u64,
pub min_num_conflicts_before_first_restart: u64,
pub lbd_coef: f64,
pub num_assigned_coef: f64,
pub num_assigned_window: u64,
pub geometric_coef: Option<f64>,
pub no_restarts: bool,
}
impl Default for RestartOptions {
fn default() -> Self {
Self {
sequence_generator_type: SequenceGeneratorType::Constant,
base_interval: 50,
min_num_conflicts_before_first_restart: 10000,
lbd_coef: 1.25,
num_assigned_coef: 1.4,
num_assigned_window: 5000,
geometric_coef: None,
no_restarts: false,
}
}
}
#[derive(Debug)]
pub(crate) struct RestartStrategy {
sequence_generator: Box<dyn SequenceGenerator>,
number_of_conflicts_encountered_since_restart: u64,
number_of_conflicts_until_restart: u64,
minimum_number_of_conflicts_before_first_restart: u64,
lbd_short_term_moving_average: Box<dyn MovingAverage<u64>>,
lbd_coefficient: f64,
lbd_long_term_moving_average: Box<dyn MovingAverage<u64>>,
number_of_variables_coefficient: f64,
number_of_assigned_variables_moving_average: Box<dyn MovingAverage<u64>>,
number_of_restarts: u64,
number_of_blocked_restarts: u64,
no_restarts: bool,
}
impl Default for RestartStrategy {
fn default() -> Self {
RestartStrategy::new(RestartOptions::default())
}
}
impl RestartStrategy {
pub(crate) fn new(options: RestartOptions) -> Self {
let mut sequence_generator: Box<dyn SequenceGenerator> =
match options.sequence_generator_type {
SequenceGeneratorType::Constant => Box::new(ConstantSequence::new(
options.base_interval as i64,
)),
SequenceGeneratorType::Geometric => Box::new(GeometricSequence::new(
options.base_interval as i64,
options.geometric_coef.expect(
"Using the geometric sequence for restarts, but the parameter restarts-geometric-coef is not defined.",
),
)),
SequenceGeneratorType::Luby => Box::new(LubySequence::new(options.base_interval as i64)),
};
let number_of_conflicts_until_restart = sequence_generator.next().try_into().expect("Expected restart generator to generate a positive value but it generated a negative one");
RestartStrategy {
sequence_generator,
number_of_conflicts_encountered_since_restart: 0,
number_of_conflicts_until_restart,
minimum_number_of_conflicts_before_first_restart: options
.min_num_conflicts_before_first_restart,
lbd_short_term_moving_average: Box::new(WindowedMovingAverage::new(
options.base_interval,
)),
lbd_coefficient: options.lbd_coef,
lbd_long_term_moving_average: Box::<CumulativeMovingAverage<u64>>::default(),
number_of_variables_coefficient: options.num_assigned_coef,
number_of_assigned_variables_moving_average: Box::new(WindowedMovingAverage::new(
options.num_assigned_window,
)),
number_of_restarts: 0,
number_of_blocked_restarts: 0,
no_restarts: options.no_restarts,
}
}
pub(crate) fn should_restart(&self) -> bool {
if self.no_restarts {
return false;
}
if self.should_restart_first_time() {
return false;
}
if !self.should_trigger_later_restart() {
return false;
}
self.lbd_long_term_moving_average.value() * self.lbd_coefficient
<= self.lbd_short_term_moving_average.value()
}
fn should_restart_first_time(&self) -> bool {
self.number_of_restarts == 0
&& self.number_of_conflicts_encountered_since_restart
< self.minimum_number_of_conflicts_before_first_restart
}
pub(crate) fn notify_conflict(&mut self, lbd: u32, number_of_pruned_values: u64) {
if self.no_restarts {
return;
}
self.number_of_assigned_variables_moving_average
.add_term(number_of_pruned_values);
self.lbd_short_term_moving_average.add_term(lbd as u64);
self.lbd_long_term_moving_average.add_term(lbd as u64);
self.number_of_conflicts_encountered_since_restart += 1;
if self.should_block_restart(number_of_pruned_values) {
self.number_of_blocked_restarts += 1;
self.reset_values()
}
}
fn should_block_restart(&self, number_of_pruned_values: u64) -> bool {
if self.should_restart_first_time() {
return false;
}
let close_to_solution = number_of_pruned_values as f64
> self.number_of_assigned_variables_moving_average.value()
* self.number_of_variables_coefficient;
self.should_trigger_later_restart() && close_to_solution
}
fn should_trigger_later_restart(&self) -> bool {
self.number_of_conflicts_until_restart <= self.number_of_conflicts_encountered_since_restart
}
pub(crate) fn notify_restart(&mut self) {
pumpkin_assert_simple!(!self.no_restarts);
self.number_of_restarts += 1;
self.reset_values()
}
fn reset_values(&mut self) {
pumpkin_assert_simple!(!self.no_restarts);
self.number_of_conflicts_until_restart =
self.sequence_generator.next().try_into().expect("Expected restart generator to generate a positive value but it generated a negative one");
self.number_of_conflicts_encountered_since_restart = 0;
self.lbd_short_term_moving_average
.adapt(self.number_of_conflicts_until_restart);
}
}