Add support for rational coefficients in linear expressions.
Coef has been redefined to Rational32.
This commit is contained in:
parent
20e5242624
commit
ab6267b46c
@ -1,9 +1,10 @@
|
||||
//! Equality implementation.
|
||||
|
||||
use std::rc::Rc;
|
||||
use num_rational::Ratio;
|
||||
use num_traits::Zero;
|
||||
|
||||
use ::{Constraint,LinExpr,PsResult,PuzzleSearch,Val,VarToken};
|
||||
use intdiv::IntDiv;
|
||||
|
||||
pub struct Equality {
|
||||
// The equation: 0 = constant + coef1 * var1 + coef2 * var2 + ...
|
||||
@ -42,7 +43,7 @@ impl Constraint for Equality {
|
||||
|
||||
for (&var, &coef) in self.eqn.coef.iter() {
|
||||
if let Some(val) = search.get_assigned(var) {
|
||||
sum += coef * val;
|
||||
sum = sum + coef * Ratio::from_integer(val);
|
||||
} else {
|
||||
// If we find more than one unassigned variable,
|
||||
// cannot assign any other variables.
|
||||
@ -58,13 +59,13 @@ impl Constraint for Equality {
|
||||
if let Some((var, coef)) = unassigned_var {
|
||||
// sum + coef * var = 0.
|
||||
let val = -sum / coef;
|
||||
if sum + coef * val == 0 {
|
||||
try!(search.set_candidate(var, val));
|
||||
if val.is_integer() {
|
||||
try!(search.set_candidate(var, val.to_integer()));
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
if sum != 0 {
|
||||
if !sum.is_zero() {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
@ -78,12 +79,12 @@ impl Constraint for Equality {
|
||||
|
||||
for (&var, &coef) in self.eqn.coef.iter() {
|
||||
let (min_val, max_val) = try!(search.get_min_max(var));
|
||||
if coef > 0 {
|
||||
sum_min += coef * min_val;
|
||||
sum_max += coef * max_val;
|
||||
if coef > Ratio::zero() {
|
||||
sum_min = sum_min + coef * Ratio::from_integer(min_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(max_val);
|
||||
} else {
|
||||
sum_min += coef * max_val;
|
||||
sum_max += coef * min_val;
|
||||
sum_min = sum_min + coef * Ratio::from_integer(max_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(min_val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +95,7 @@ impl Constraint for Equality {
|
||||
let mut iter = self.eqn.coef.iter().cycle();
|
||||
while iters > 0 {
|
||||
iters = iters - 1;
|
||||
if !(sum_min <= 0 && 0 <= sum_max) {
|
||||
if !(sum_min <= Ratio::zero() && Ratio::zero() <= sum_max) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
@ -106,24 +107,24 @@ impl Constraint for Equality {
|
||||
let (min_val, max_val) = try!(search.get_min_max(var));
|
||||
let (min_bnd, max_bnd);
|
||||
|
||||
if coef > 0 {
|
||||
min_bnd = (coef * max_val - sum_max).div_round_up(coef);
|
||||
max_bnd = (coef * min_val - sum_min).div_round_down(coef);
|
||||
if coef > Ratio::zero() {
|
||||
min_bnd = ((coef * Ratio::from_integer(max_val) - sum_max) / coef).ceil().to_integer();
|
||||
max_bnd = ((coef * Ratio::from_integer(min_val) - sum_min) / coef).floor().to_integer();
|
||||
} else {
|
||||
min_bnd = (coef * max_val - sum_min).div_round_up(coef);
|
||||
max_bnd = (coef * min_val - sum_max).div_round_down(coef);
|
||||
min_bnd = ((coef * Ratio::from_integer(max_val) - sum_min) / coef).ceil().to_integer();
|
||||
max_bnd = ((coef * Ratio::from_integer(min_val) - sum_max) / coef).floor().to_integer();
|
||||
}
|
||||
|
||||
if min_val < min_bnd || max_bnd < max_val {
|
||||
let (new_min, new_max)
|
||||
= try!(search.bound_candidate_range(var, min_bnd, max_bnd));
|
||||
|
||||
if coef > 0 {
|
||||
sum_min = sum_min + coef * (new_min - min_val);
|
||||
sum_max = sum_max + coef * (new_max - max_val);
|
||||
if coef > Ratio::zero() {
|
||||
sum_min = sum_min + coef * Ratio::from_integer(new_min - min_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(new_max - max_val);
|
||||
} else {
|
||||
sum_min = sum_min + coef * (new_max - max_val);
|
||||
sum_max = sum_max + coef * (new_min - min_val);
|
||||
sum_min = sum_min + coef * Ratio::from_integer(new_max - max_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(new_min - min_val);
|
||||
}
|
||||
|
||||
iters = self.eqn.coef.len();
|
||||
|
@ -7,6 +7,7 @@ extern crate num_traits;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Index;
|
||||
use num_rational::Rational32;
|
||||
|
||||
pub use constraint::Constraint;
|
||||
pub use puzzle::Puzzle;
|
||||
@ -20,7 +21,7 @@ pub struct VarToken(usize);
|
||||
pub type Val = i32;
|
||||
|
||||
/// The type of the coefficients in a linear expression.
|
||||
pub type Coef = i32;
|
||||
pub type Coef = Rational32;
|
||||
|
||||
/// A linear expression.
|
||||
///
|
||||
|
@ -4,8 +4,10 @@ use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::convert::From;
|
||||
use std::ops::{Add,Mul,Neg,Sub};
|
||||
use num_rational::{Ratio,Rational32};
|
||||
use num_traits::{One,Zero};
|
||||
|
||||
use ::{LinExpr,VarToken};
|
||||
use ::{Coef,LinExpr,VarToken};
|
||||
|
||||
macro_rules! impl_commutative_op {
|
||||
($LHS:ident + $RHS:ident) => {
|
||||
@ -31,12 +33,24 @@ macro_rules! impl_subtract_op {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoCoef: Zero {
|
||||
fn into_coef(self) -> Coef;
|
||||
}
|
||||
|
||||
impl IntoCoef for i32 {
|
||||
fn into_coef(self) -> Coef { Ratio::from_integer(self) }
|
||||
}
|
||||
|
||||
impl IntoCoef for Rational32 {
|
||||
fn into_coef(self) -> Coef { self }
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
impl From<i32> for LinExpr {
|
||||
fn from(constant: i32) -> Self {
|
||||
impl<T: IntoCoef> From<T> for LinExpr {
|
||||
fn from(constant: T) -> Self {
|
||||
LinExpr {
|
||||
constant: constant,
|
||||
constant: constant.into_coef(),
|
||||
coef: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@ -45,10 +59,10 @@ impl From<i32> for LinExpr {
|
||||
impl From<VarToken> for LinExpr {
|
||||
fn from(var: VarToken) -> Self {
|
||||
let mut coef = HashMap::new();
|
||||
coef.insert(var, 1);
|
||||
coef.insert(var, Ratio::one());
|
||||
|
||||
LinExpr {
|
||||
constant: 0,
|
||||
constant: Ratio::zero(),
|
||||
coef: coef,
|
||||
}
|
||||
}
|
||||
@ -65,26 +79,30 @@ impl Neg for VarToken {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<i32> for VarToken {
|
||||
impl<T: IntoCoef> Add<T> for VarToken {
|
||||
type Output = LinExpr;
|
||||
fn add(self, rhs: i32) -> Self::Output {
|
||||
fn add(self, rhs: T) -> Self::Output {
|
||||
LinExpr::from(self) + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl_commutative_op!(i32 + VarToken);
|
||||
impl_commutative_op!(Rational32 + VarToken);
|
||||
|
||||
impl_subtract_op!(VarToken - i32);
|
||||
impl_subtract_op!(i32 - VarToken);
|
||||
impl_subtract_op!(VarToken - Rational32);
|
||||
impl_subtract_op!(Rational32 - VarToken);
|
||||
|
||||
impl Mul<i32> for VarToken {
|
||||
impl<T: IntoCoef> Mul<T> for VarToken {
|
||||
type Output = LinExpr;
|
||||
fn mul(self, rhs: i32) -> Self::Output {
|
||||
fn mul(self, rhs: T) -> Self::Output {
|
||||
LinExpr::from(self) * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl_commutative_op!(i32 * VarToken);
|
||||
impl_commutative_op!(Rational32 * VarToken);
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Var-Var */
|
||||
@ -110,29 +128,35 @@ impl Neg for LinExpr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<i32> for LinExpr {
|
||||
impl<T: IntoCoef> Add<T> for LinExpr {
|
||||
type Output = LinExpr;
|
||||
fn add(mut self, rhs: i32) -> Self::Output {
|
||||
self.constant = self.constant + rhs;
|
||||
fn add(mut self, rhs: T) -> Self::Output {
|
||||
self.constant = self.constant + rhs.into_coef();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl_commutative_op!(i32 + LinExpr);
|
||||
impl_commutative_op!(Rational32 + LinExpr);
|
||||
|
||||
impl_subtract_op!(LinExpr - i32);
|
||||
impl_subtract_op!(i32 - LinExpr);
|
||||
impl_subtract_op!(LinExpr - Rational32);
|
||||
impl_subtract_op!(Rational32 - LinExpr);
|
||||
|
||||
impl Mul<i32> for LinExpr {
|
||||
impl<T: IntoCoef> Mul<T> for LinExpr {
|
||||
type Output = LinExpr;
|
||||
fn mul(mut self, rhs: i32) -> Self::Output {
|
||||
if rhs == 0 {
|
||||
self.constant = 0;
|
||||
fn mul(mut self, rhs: T) -> Self::Output {
|
||||
if rhs.is_zero() {
|
||||
self.constant = Ratio::zero();
|
||||
self.coef = HashMap::new();
|
||||
} else if rhs != 1 {
|
||||
self.constant = self.constant * rhs;
|
||||
for coef in self.coef.values_mut() {
|
||||
*coef *= rhs;
|
||||
} else {
|
||||
let rhs = rhs.into_coef();
|
||||
if rhs != Ratio::one() {
|
||||
self.constant = self.constant * rhs;
|
||||
for coef in self.coef.values_mut() {
|
||||
*coef = *coef * rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +165,7 @@ impl Mul<i32> for LinExpr {
|
||||
}
|
||||
|
||||
impl_commutative_op!(i32 * LinExpr);
|
||||
impl_commutative_op!(Rational32 * LinExpr);
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Expr-Var */
|
||||
@ -173,10 +198,11 @@ impl Add for LinExpr {
|
||||
e.insert(a2);
|
||||
},
|
||||
Entry::Occupied(mut e) => {
|
||||
if *e.get() + a2 == 0 {
|
||||
let new_coef = *e.get() + a2;
|
||||
if new_coef.is_zero() {
|
||||
e.remove();
|
||||
} else {
|
||||
*e.get_mut() += a2;
|
||||
*e.get_mut() = new_coef;
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -192,6 +218,7 @@ impl_subtract_op!(LinExpr - LinExpr);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num_rational::Ratio;
|
||||
use ::Puzzle;
|
||||
|
||||
#[test]
|
||||
@ -204,11 +231,17 @@ mod tests {
|
||||
let _ = x + 1;
|
||||
let _ = x - 1;
|
||||
let _ = x * 1;
|
||||
let _ = x + Ratio::new(1, 2);
|
||||
let _ = x - Ratio::new(1, 2);
|
||||
let _ = x * Ratio::new(1, 2);
|
||||
|
||||
// expr = const + var;
|
||||
let _ = 1 + x;
|
||||
let _ = 1 - x;
|
||||
let _ = 1 * x;
|
||||
let _ = Ratio::new(1, 2) + x;
|
||||
let _ = Ratio::new(1, 2) - x;
|
||||
let _ = Ratio::new(1, 2) * x;
|
||||
|
||||
// expr = var + var;
|
||||
let _ = -x;
|
||||
@ -219,11 +252,17 @@ mod tests {
|
||||
let _ = (x + y) + 1;
|
||||
let _ = (x + y) - 1;
|
||||
let _ = (x + y) * 1;
|
||||
let _ = (x + y) + Ratio::new(1, 2);
|
||||
let _ = (x + y) - Ratio::new(1, 2);
|
||||
let _ = (x + y) * Ratio::new(1, 2);
|
||||
|
||||
// expr = const + expr;
|
||||
let _ = 1 + (x + y);
|
||||
let _ = 1 - (x + y);
|
||||
let _ = 1 * (x + y);
|
||||
let _ = Ratio::new(1, 2) + (x + y);
|
||||
let _ = Ratio::new(1, 2) - (x + y);
|
||||
let _ = Ratio::new(1, 2) * (x + y);
|
||||
|
||||
// expr = expr + var;
|
||||
let _ = (x + 1) + y;
|
||||
|
Loading…
Reference in New Issue
Block a user