Skip to content

Commit

Permalink
Merge pull request #34 from vcoppe/clean-dominance
Browse files Browse the repository at this point in the history
Clear dominance entries for unreachable layers
  • Loading branch information
xgillard authored Feb 2, 2024
2 parents dfa72ec + df20750 commit 1752b1e
Show file tree
Hide file tree
Showing 20 changed files with 92 additions and 77 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ fn main() {
let width = FixedWidth(100); // here we mean max 100 nodes per layer

// 5. Add a dominance relation checker
let dominance = SimpleDominanceChecker::new(KPDominance);
let dominance = SimpleDominanceChecker::new(KPDominance, problem.nb_variables());

// 6. Decide of a cutoff heuristic (if you don't want to let the solver run for ever)
let cutoff = NoCutoff; // might as well be a TimeBudget (or something else)
Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/alp/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn main() {
let ranking = AlpRanking;

let width = max_width(&problem, args.width);
let dominance = SimpleDominanceChecker::new(AlpDominance);
let dominance = SimpleDominanceChecker::new(AlpDominance, problem.nb_variables());
let cutoff = cutoff(args.duration);
let mut fringe = NoDupFringe::new(MaxUB::new(&ranking));

Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/alp/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn solve_id(id: &str) -> isize {
let ranking = AlpRanking;

let width = NbUnassignedWidth(problem.nb_variables());
let dominance = SimpleDominanceChecker::new(AlpDominance);
let dominance = SimpleDominanceChecker::new(AlpDominance, problem.nb_variables());
let cutoff = NoCutoff;
let mut fringe = NoDupFringe::new(MaxUB::new(&ranking));

Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/knapsack/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ fn main() {
let relaxation= KPRelax{pb: &problem};
let heuristic= KPRanking;
let width = max_width(problem.nb_variables(), args.width);
let dominance = SimpleDominanceChecker::new(KPDominance);
let dominance = SimpleDominanceChecker::new(KPDominance, problem.nb_variables());
let cutoff = TimeBudget::new(Duration::from_secs(15));//NoCutoff;
let mut fringe = SimpleFringe::new(MaxUB::new(&heuristic));

Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/knapsack/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn solve_id(id: &str) -> isize {
let ranking = KPRanking;

let width = NbUnassignedWidth(problem.nb_variables());
let dominance = SimpleDominanceChecker::new(KPDominance);
let dominance = SimpleDominanceChecker::new(KPDominance, problem.nb_variables());
let cutoff = NoCutoff;
let mut fringe = NoDupFringe::new(MaxUB::new(&ranking));

Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/lcs/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ fn main() {
let ranking = LcsRanking;

let width = max_width(&problem, args.width);
let dominance = SimpleDominanceChecker::new(LcsDominance);
let dominance = SimpleDominanceChecker::new(LcsDominance, problem.nb_variables());
let cutoff = cutoff(args.duration);
let mut fringe = NoDupFringe::new(MaxUB::new(&ranking));

Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/lcs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn solve_id(id: &str) -> isize {
let ranking = LcsRanking;

let width = FixedWidth(100);
let dominance = SimpleDominanceChecker::new(LcsDominance);
let dominance = SimpleDominanceChecker::new(LcsDominance, problem.nb_variables());
let cutoff = NoCutoff;
let mut fringe = NoDupFringe::new(MaxUB::new(&ranking));

Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/tsptw/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn main() {
let pb = Tsptw::new(inst);
let relax = TsptwRelax::new(&pb);
let width = TsptwWidth::new(pb.nb_variables(), args.width.unwrap_or(1));
let dominance = SimpleDominanceChecker::new(TsptwDominance);
let dominance = SimpleDominanceChecker::new(TsptwDominance, pb.nb_variables());
let cutoff = TimeBudget::new(Duration::from_secs(args.duration.unwrap_or(u64::MAX)));
let mut fringe = NoDupFringe::new(MaxUB::new(&TsptwRanking));
let mut solver = DefaultCachingSolver::custom(
Expand Down
2 changes: 1 addition & 1 deletion ddo/examples/tsptw/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn solve(instance: &str, width: Option<usize>, threads: Option<usize>) -> f3
let mut fringe = NoDupFringe::new(MaxUB::new(&TsptwRanking));
let relax = TsptwRelax::new(&pb);
let width = TsptwWidth::new(pb.nb_variables(), width.unwrap_or(1));
let dominance = SimpleDominanceChecker::new(TsptwDominance);
let dominance = SimpleDominanceChecker::new(TsptwDominance, pb.nb_variables());
let cutoff = NoCutoff;
let mut solver = DefaultCachingSolver::custom(
&pb,
Expand Down
5 changes: 4 additions & 1 deletion ddo/src/abstraction/dominance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@ pub struct DominanceCheckResult {
pub trait DominanceChecker {
type State;

/// Removes all entries associated with states at the given depth.
fn clear_layer(&self, depth: usize);

/// Returns true if the state is dominated by a stored one, and a potential
/// pruning threshold, and inserts the (key, value) pair otherwise
fn is_dominated_or_insert(&self, state: Arc<Self::State>, value: isize) -> DominanceCheckResult;
fn is_dominated_or_insert(&self, state: Arc<Self::State>, depth: usize, value: isize) -> DominanceCheckResult;

/// Comparator to order states by increasing value, regardless of their key
fn cmp(&self, a: &Self::State, val_a: isize, b: &Self::State, val_b: isize) -> Ordering;
Expand Down
4 changes: 3 additions & 1 deletion ddo/src/implementation/dominance/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ impl<T> Default for EmptyDominanceChecker<T> {
impl<T> DominanceChecker for EmptyDominanceChecker<T> {
type State = T;

fn is_dominated_or_insert(&self, _: Arc<Self::State>, _: isize) -> DominanceCheckResult {
fn clear_layer(&self, _: usize) {}

fn is_dominated_or_insert(&self, _: Arc<Self::State>, _: usize, _: isize) -> DominanceCheckResult {
DominanceCheckResult { dominated: false, threshold: None }
}

Expand Down
118 changes: 64 additions & 54 deletions ddo/src/implementation/dominance/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,29 @@ struct DominanceEntry<T> {
value: isize,
}

type DominanceMap<K, S> = DashMap<K, Vec<DominanceEntry<S>>, fxhash::FxBuildHasher>;

#[derive(Debug)]
pub struct SimpleDominanceChecker<D>
where
D: Dominance,
D::Key: Eq + PartialEq + Hash,
{
dominance: D,
data: DashMap<D::Key, Vec<DominanceEntry<D::State>>, fxhash::FxBuildHasher>,
data: Vec<DominanceMap<D::Key, D::State>>,
}

impl<D> SimpleDominanceChecker<D>
where
D: Dominance,
D::Key: Eq + PartialEq + Hash,
{
pub fn new(dominance: D) -> Self {
Self { dominance, data: Default::default() }
pub fn new(dominance: D, nb_variables: usize) -> Self {
let mut data = vec![];
for _ in 0..=nb_variables {
data.push(Default::default());
}
Self { dominance, data }
}
}

Expand All @@ -58,9 +64,13 @@ where
{
type State = D::State;

fn is_dominated_or_insert(&self, state: Arc<Self::State>, value: isize) -> DominanceCheckResult {
fn clear_layer(&self, depth: usize) {
self.data[depth].clear();
}

fn is_dominated_or_insert(&self, state: Arc<Self::State>, depth: usize, value: isize) -> DominanceCheckResult {
if let Some(key) = self.dominance.get_key(state.clone()) {
match self.data.entry(key) {
match self.data[depth].entry(key) {
Entry::Occupied(mut e) => {
let mut dominated = false;
let mut threshold = Some(isize::MAX);
Expand Down Expand Up @@ -113,104 +123,104 @@ mod tests {

#[test]
fn not_dominated_when_keys_are_different() {
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue);
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![3, 0]), 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![2, 0]), 2));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![1, 0]), 1));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![3, 0]), 0, 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![2, 0]), 0, 2));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![1, 0]), 0, 1));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 0));
}

#[test]
fn dominated_when_keys_are_equal() {
let dominance = SimpleDominanceChecker::new(DummyDominance);
let dominance = SimpleDominanceChecker::new(DummyDominance, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 3]), 0));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 3]), 0, 0));

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 2]), 2);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 2]), 0, 2);
assert!(res.dominated);

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 1);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 0, 1);
assert!(res.dominated);

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 0);
assert!(res.dominated);

let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue);
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 3));

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 2);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 2);
assert!(res.dominated);

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 1);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 1);
assert!(res.dominated);

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 0);
assert!(res.dominated);

let res = dominance.is_dominated_or_insert(Arc::new(vec![0, -1]), 3);
let res = dominance.is_dominated_or_insert(Arc::new(vec![0, -1]), 0, 3);
assert!(res.dominated);
}

#[test]
fn not_dominated_when_keys_are_equal() {
let dominance = SimpleDominanceChecker::new(DummyDominance);
let dominance = SimpleDominanceChecker::new(DummyDominance, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0, 3]), 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0, 3]), 1));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1, 1]), 5));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0, 4]), 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0, 3]), 0, 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0, 3]), 0, 1));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1, 1]), 0, 5));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0, 4]), 0, 3));

let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue);
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 3]), 0));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 1));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 5));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 3));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 3]), 0, 0));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 0, 1));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 5));
}

#[test]
fn pruning_threshold_when_value_is_used() {
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue);
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 3));
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(2) }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 2));
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(2) }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 1));
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(3) }, dominance.is_dominated_or_insert(Arc::new(vec![0, -1]), 0));
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 3));
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(2) }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 2));
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(2) }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 1));
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(3) }, dominance.is_dominated_or_insert(Arc::new(vec![0, -1]), 0, 0));
}

#[test]
fn entry_is_added_only_when_dominant() {
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue);
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 3));
assert!(dominance.data.get(&0).is_some());
assert_eq!(1, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 3));
assert!(dominance.data[0].get(&0).is_some());
assert_eq!(1, dominance.data[0].get(&0).unwrap().len());

assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(2) }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 1));
assert_eq!(1, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: true, threshold: Some(2) }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 1));
assert_eq!(1, dominance.data[0].get(&0).unwrap().len());

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 1));
assert_eq!(2, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 0, 1));
assert_eq!(2, dominance.data[0].get(&0).unwrap().len());

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, -1]), 5));
assert_eq!(3, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, -1]), 0, 5));
assert_eq!(3, dominance.data[0].get(&0).unwrap().len());
}

#[test]
fn entry_is_removed_when_dominated() {
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue);
let dominance = SimpleDominanceChecker::new(DummyDominanceWithValue, 0);

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 3));
assert!(dominance.data.get(&0).is_some());
assert_eq!(1, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 0]), 0, 3));
assert!(dominance.data[0].get(&0).is_some());
assert_eq!(1, dominance.data[0].get(&0).unwrap().len());

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 5));
assert_eq!(1, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 1]), 0, 5));
assert_eq!(1, dominance.data[0].get(&0).unwrap().len());

assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 2]), 7));
assert_eq!(1, dominance.data.get(&0).unwrap().len());
assert_eq!(DominanceCheckResult{ dominated: false, threshold: None }, dominance.is_dominated_or_insert(Arc::new(vec![0, 2]), 0, 7));
assert_eq!(1, dominance.data[0].get(&0).unwrap().len());
}

struct DummyDominance;
Expand Down
4 changes: 2 additions & 2 deletions ddo/src/implementation/heuristics/cutoff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ use crate::Cutoff;
/// };
/// let relaxation = KPRelax{pb: &problem};
/// let width = FixedWidth(100);
/// let dominance = SimpleDominanceChecker::new(KPDominance);
/// let dominance = SimpleDominanceChecker::new(KPDominance, problem.nb_variables());
/// let heuristic = KPRanking;
/// let mut fringe = SimpleFringe::new(MaxUB::new(&heuristic));
/// #
Expand Down Expand Up @@ -281,7 +281,7 @@ impl Cutoff for NoCutoff {
/// };
/// let relaxation = KPRelax{pb: &problem};
/// let width = FixedWidth(100);
/// let dominance = SimpleDominanceChecker::new(KPDominance);
/// let dominance = SimpleDominanceChecker::new(KPDominance, problem.nb_variables());
/// let heuristic = KPRanking;
///
/// // this solver will be allowed to run for 30 seconds
Expand Down
Loading

0 comments on commit 1752b1e

Please sign in to comment.