use super::Direction;
use super::TieBreaker;
use crate::basic_types::Random;
pub struct RandomTieBreaker<Var, Value> {
selected_variable: Option<Var>,
selected_value: Option<Value>,
rng: Box<dyn Random>,
num_variables_considered: usize,
direction: Direction,
}
impl<Var, Value> std::fmt::Debug for RandomTieBreaker<Var, Value> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RandomTieBreaker").finish()
}
}
impl<Var, Value> RandomTieBreaker<Var, Value> {
pub fn new(direction: Direction, rng: Box<dyn Random>) -> Self {
Self {
selected_variable: None,
selected_value: None,
rng,
num_variables_considered: 0,
direction,
}
}
fn reset(&mut self) {
self.selected_variable = None;
self.selected_value = None;
}
}
impl<Var: Copy, Value: PartialOrd> TieBreaker<Var, Value> for RandomTieBreaker<Var, Value> {
fn consider(&mut self, variable: Var, value: Value) {
if let Some(selected_var) = self.selected_variable.as_mut() {
let selected_value = self
.selected_value
.as_ref()
.expect("The random tie breaker selected a variable but not a value...");
match self.direction {
Direction::Maximum => {
if value > *selected_value {
self.num_variables_considered = 1;
self.selected_variable = Some(variable);
self.selected_value = Some(value);
return;
}
}
Direction::Minimum => {
if value < *selected_value {
self.num_variables_considered = 1;
self.selected_variable = Some(variable);
self.selected_value = Some(value);
return;
}
}
}
if value == *selected_value {
self.num_variables_considered += 1;
if self
.rng
.generate_bool(1.0 / self.num_variables_considered as f64)
{
*selected_var = variable;
self.selected_value = Some(value);
}
}
} else {
self.num_variables_considered = 1;
self.selected_variable = Some(variable);
self.selected_value = Some(value);
}
}
fn select(&mut self) -> Option<Var> {
let selected = self.selected_variable;
self.reset();
selected
}
fn get_direction(&self) -> Direction {
self.direction
}
}
#[cfg(test)]
mod tests {
use super::RandomTieBreaker;
use crate::basic_types::tests::TestRandom;
use crate::branching::tie_breaking::TieBreaker;
use crate::branching::tie_breaking::random_tie_breaker::Direction;
#[test]
fn test_selection_new_value() {
let rng = TestRandom::default();
let mut breaker: RandomTieBreaker<i32, i32> =
RandomTieBreaker::new(Direction::Minimum, Box::new(rng));
assert!(breaker.select().is_none());
breaker.consider(0, 1);
let selected = breaker.select();
assert!(selected.is_some());
assert_eq!(selected.unwrap(), 0);
}
#[test]
fn test_selection_between_values_chooses_maximum() {
let rng = TestRandom::default();
let mut breaker: RandomTieBreaker<i32, i32> =
RandomTieBreaker::new(Direction::Maximum, Box::new(rng));
breaker.consider(0, 5);
breaker.consider(1, 10);
let selected = breaker.select();
assert!(selected.is_some());
assert_eq!(selected.unwrap(), 1);
}
#[test]
fn test_selection_between_values_chooses_minimum() {
let rng = TestRandom::default();
let mut breaker: RandomTieBreaker<i32, i32> =
RandomTieBreaker::new(Direction::Minimum, Box::new(rng));
breaker.consider(0, 5);
breaker.consider(1, 10);
let selected = breaker.select();
assert!(selected.is_some());
assert_eq!(selected.unwrap(), 0);
}
#[test]
fn test_selection_between_values_chooses_random_with_seed_second() {
let rng = TestRandom {
bools: vec![true],
..Default::default()
};
let mut breaker: RandomTieBreaker<i32, i32> =
RandomTieBreaker::new(Direction::Maximum, Box::new(rng));
breaker.consider(0, 5);
breaker.consider(1, 5);
let selected = breaker.select();
assert!(selected.is_some());
assert_eq!(selected.unwrap(), 1);
}
#[test]
fn test_selection_between_values_chooses_random_with_seed_first() {
let rng = TestRandom {
bools: vec![false],
..Default::default()
};
let mut breaker: RandomTieBreaker<i32, i32> =
RandomTieBreaker::new(Direction::Maximum, Box::new(rng));
breaker.consider(0, 5);
breaker.consider(1, 5);
let selected = breaker.select();
assert!(selected.is_some());
assert_eq!(selected.unwrap(), 0);
}
}