use crate::api::constraint_set::{ConstraintSet, IncrementalConstraint};
use crate::constraint::IncrementalBiConstraint;
use solverforge_core::score::SoftScore;
use solverforge_core::{ConstraintRef, ImpactType};
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
struct Queen {
row: i64,
col: i64,
}
#[derive(Clone)]
struct NQueensSolution {
queens: Vec<Queen>,
}
#[test]
fn test_evaluate_no_conflicts() {
let constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row, |_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col, |_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 1, col: 1 },
Queen { row: 2, col: 2 },
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(0));
assert_eq!(constraint.match_count(&solution), 0);
}
#[test]
fn test_evaluate_with_conflicts() {
let constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 0, col: 1 }, Queen { row: 2, col: 2 },
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(-1));
assert_eq!(constraint.match_count(&solution), 1);
}
#[test]
fn test_incremental_insert() {
let mut constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 0, col: 1 },
Queen { row: 2, col: 2 },
],
};
constraint.initialize(&solution);
constraint.reset();
let delta = constraint.on_insert(&solution, 0, 0);
assert_eq!(delta, SoftScore::of(0));
let delta = constraint.on_insert(&solution, 1, 0);
assert_eq!(delta, SoftScore::of(-1));
let delta = constraint.on_insert(&solution, 2, 0);
assert_eq!(delta, SoftScore::of(0));
}
#[test]
fn test_incremental_retract() {
let mut constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![Queen { row: 0, col: 0 }, Queen { row: 0, col: 1 }],
};
constraint.initialize(&solution);
constraint.reset();
constraint.on_insert(&solution, 0, 0);
constraint.on_insert(&solution, 1, 0);
let delta = constraint.on_retract(&solution, 0, 0);
assert_eq!(delta, SoftScore::of(1)); }
#[test]
fn test_reward_type() {
let constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Adjacent queens"),
ImpactType::Reward,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row, |_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| {
a.col < b.col && (a.col - b.col).abs() == 1
},
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(2),
false,
);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 0, col: 1 }, ],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(2));
}
#[test]
fn test_dynamic_weight() {
let constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Column distance"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|s: &NQueensSolution, a_idx: usize, b_idx: usize| {
let a = &s.queens[a_idx];
let b = &s.queens[b_idx];
SoftScore::of((b.col - a.col).abs())
},
false,
);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 0, col: 3 }, ],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(-3));
}
#[test]
fn test_multiple_conflicts() {
let constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 0, col: 1 }, Queen { row: 0, col: 2 }, ],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(-3));
assert_eq!(constraint.match_count(&solution), 3);
}
#[test]
fn test_reset() {
let mut constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![Queen { row: 0, col: 0 }, Queen { row: 0, col: 1 }],
};
constraint.initialize(&solution);
constraint.reset();
constraint.on_insert(&solution, 0, 0);
constraint.on_insert(&solution, 1, 0);
constraint.reset();
let delta = constraint.on_insert(&solution, 0, 0);
assert_eq!(delta, SoftScore::of(0));
}
#[test]
fn test_in_constraint_set() {
let c1 = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let constraints = (c1,);
let solution = NQueensSolution {
queens: vec![
Queen { row: 0, col: 0 },
Queen { row: 0, col: 1 },
Queen { row: 2, col: 2 },
],
};
assert_eq!(constraints.evaluate_all(&solution), SoftScore::of(-1));
}
#[test]
fn test_out_of_bounds() {
let mut constraint = IncrementalBiConstraint::new(
ConstraintRef::new("", "Row conflict"),
ImpactType::Penalty,
(|s: &NQueensSolution| s.queens.as_slice()) as fn(&NQueensSolution) -> &[Queen],
|_s: &NQueensSolution, q: &Queen, _idx: usize| q.row,
|_s: &NQueensSolution, a: &Queen, b: &Queen, _ai: usize, _bi: usize| a.col < b.col,
|_s: &NQueensSolution, _a_idx: usize, _b_idx: usize| SoftScore::of(1),
false,
);
let solution = NQueensSolution {
queens: vec![Queen { row: 0, col: 0 }],
};
constraint.initialize(&solution);
assert_eq!(constraint.on_insert(&solution, 100, 0), SoftScore::of(0));
assert_eq!(constraint.on_retract(&solution, 100, 0), SoftScore::of(0));
}