Add equality constraint.

This is a very simple equality constraint.  It only assigns a variable
when all other variables in the equation have been assigned.  Possible
improvements:

- We can find the min and max range by considering the coefficients
  and the max and min values of the other variables in the equation.

- When there are only 2 variables remaining, we can eliminate the
  incompatible candidates and then substitute one variable for the
  other in other constraints.
This commit is contained in:
David Wang 2017-03-01 07:33:54 +11:00
parent bddb31dd5b
commit 7e908457a2
2 changed files with 104 additions and 0 deletions

102
src/constraint/equality.rs Normal file
View File

@ -0,0 +1,102 @@
//! Equality implementation.
use ::{Constraint,LinExpr,PuzzleSearch,Val,VarToken};
pub struct Equality {
// The equation: 0 = constant + coef1 * var1 + coef2 * var2 + ...
eqn: LinExpr,
}
impl Equality {
/// Allocate a new Equality constraint.
///
/// # Examples
///
/// ```
/// let mut magic_square = puzzle_solver::Puzzle::new();
/// let vars = magic_square.new_vars_with_candidates_2d(3, 3,
/// &[1,2,3,4,5,6,7,8,9]);
///
/// puzzle_solver::constraint::Equality::new(
/// vars[0][0] + vars[0][1] + vars[0][2] - 15);
/// ```
pub fn new(eqn: LinExpr) -> Self {
Equality {
eqn: eqn,
}
}
}
impl Constraint for Equality {
fn vars<'a>(&'a self) -> Box<Iterator<Item=&'a VarToken> + 'a> {
Box::new(self.eqn.coef.keys())
}
fn on_assigned(&self, search: &mut PuzzleSearch, _: VarToken, _: Val)
-> bool {
let mut sum = self.eqn.constant;
let mut unassigned_var = None;
for (&var, &coef) in self.eqn.coef.iter() {
if let Some(val) = search.get_assigned(var) {
sum += coef * val;
} else {
// If we find more than one unassigned variable,
// cannot assign any other variables.
if unassigned_var.is_some() {
return true;
} else {
unassigned_var = Some((var, coef));
}
}
}
// If we have exactly one unassigned variable, assign it.
if let Some((var, coef)) = unassigned_var {
// sum + coef * var = 0.
let val = -sum / coef;
if sum + coef * val == 0 {
search.set_candidate(var, val);
} else {
return false;
}
} else {
if sum != 0 {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use ::Puzzle;
use super::Equality;
#[test]
fn test_contradiction() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[3]);
let v1 = puzzle.new_var_with_candidates(&[0,1]);
puzzle.add_constraint(Box::new(Equality::new(v0 + 2 * v1 - 4)));
let search = puzzle.step();
assert!(search.is_none());
}
#[test]
fn test_assign() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1]);
let v1 = puzzle.new_var_with_candidates(&[1,2,3]);
puzzle.add_constraint(Box::new(Equality::new(v0 + v1 - 4)));
let search = puzzle.step().expect("contradiction");
assert_eq!(search[v0], 1);
assert_eq!(search[v1], 3);
}
}

View File

@ -31,5 +31,7 @@ pub trait Constraint {
}
pub use self::alldifferent::AllDifferent;
pub use self::equality::Equality;
mod alldifferent;
mod equality;