Skip to content
This repository has been archived by the owner on Oct 1, 2020. It is now read-only.

Commit

Permalink
the great var migration
Browse files Browse the repository at this point in the history
  • Loading branch information
duane committed Jun 28, 2016
1 parent aea13b3 commit 81cffa9
Show file tree
Hide file tree
Showing 8 changed files with 526 additions and 338 deletions.
242 changes: 129 additions & 113 deletions src/expr.rs

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/grammar.lalrpop
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::str::FromStr;
use expr::{Scalar, LinearExpression, LinearRelation, Relation};
use problem::{Problem, ProblemObjective};
use var::Var;

grammar;

pub Scalar: Scalar = <s:r"-?[0-9]+\.?[0-9]*([eE]-?[0-9]+)?"> => f64::from_str(s).unwrap();

pub Variable: String = <s:r"[_a-zA-Z][_a-zA-Z0-9]*"> => String::from(s);
pub Variable: Var = <s:r"[_a-zA-Z][_a-zA-Z0-9]*"> => Var::from(s);

pub Term: (Option<Scalar>, Option<String>) = {
pub Term: (Option<Scalar>, Option<Var>) = {
<s:Scalar> => (Some(s), None),
<v:Variable> => (None, Some(v)),
<s:Scalar> "*"? <v:Variable> => (Some(s), Some(v))
Expand All @@ -17,7 +18,7 @@ pub Term: (Option<Scalar>, Option<String>) = {
pub Expression: LinearExpression = {
<e:(Term "+")*> <u:Term> => {
let mut expr = LinearExpression::new();
let mut terms: Vec<(Option<Scalar>, Option<String>)> = e.into_iter().map(|t| t.0).collect();
let mut terms: Vec<(Option<Scalar>, Option<Var>)> = e.into_iter().map(|t| t.0).collect();
terms.push(u);
for (scalar, var) in terms.into_iter() {
let term = match (scalar, var) {
Expand Down
264 changes: 137 additions & 127 deletions src/grammar.rs

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions src/grammar_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod tests {
use expr::*;
use grammar;
use var::Var;

fn parse_scalar_lalr() {
assert!(approx_eq(0.0, grammar::parse_Scalar("0.0").unwrap()));
Expand All @@ -17,45 +18,45 @@ mod tests {

#[test]
fn parse_lalr_identifiers() {
assert_eq!(String::from("x"), grammar::parse_Variable("x").unwrap());
assert_eq!(String::from("_"), grammar::parse_Variable("_").unwrap());
assert_eq!(String::from("x2"), grammar::parse_Variable("x2").unwrap());
assert_eq!(Var::from("x"), grammar::parse_Variable("x").unwrap());
assert_eq!(Var::from("_"), grammar::parse_Variable("_").unwrap());
assert_eq!(Var::from("x2"), grammar::parse_Variable("x2").unwrap());
assert!(grammar::parse_Variable("9").is_err());
}

#[test]
fn parse_lalr_terms() {
let term1 = grammar::parse_Term("2x").unwrap();
assert!(approx_eq(2.0, term1.0.unwrap()));
assert_eq!(String::from("x"), term1.1.unwrap());
assert_eq!(Var::from("x"), term1.1.unwrap());

let term2 = grammar::parse_Term("-43.x2").unwrap();
assert!(approx_eq(-43.0, term2.0.unwrap()));
assert_eq!(String::from("x2"), term2.1.unwrap());
assert_eq!(Var::from("x2"), term2.1.unwrap());

let term3 = grammar::parse_Term("y").unwrap();
assert!(term3.0.is_none());
assert_eq!(String::from("y"), term3.1.unwrap());
assert_eq!(Var::from("y"), term3.1.unwrap());

let term4 = grammar::parse_Term("0.4").unwrap();
assert!(approx_eq(0.4, term4.0.unwrap()));
assert!(term4.1.is_none());

let term5 = grammar::parse_Term("-72.3 x3").unwrap();
assert!(approx_eq(-72.3, term5.0.unwrap()));
assert_eq!(String::from("x3"), term5.1.unwrap());
assert_eq!(Var::from("x3"), term5.1.unwrap());

let term6 = grammar::parse_Term("-72.3*x3").unwrap();
assert!(approx_eq(-72.3, term6.0.unwrap()));
assert_eq!(String::from("x3"), term6.1.unwrap());
assert_eq!(Var::from("x3"), term6.1.unwrap());
}

#[test]
fn parse_lalr_exprs() {
let expr = grammar::parse_Expression("-42.3 x4 + 92 +-92.x4+0.0x2+-92.3x3+-82").unwrap();
assert!(approx_eq(-134.3, expr.get_coefficient(&String::from("x4"))));
assert!(approx_eq(0.0, expr.get_coefficient(&String::from("x2"))));
assert!(approx_eq(-92.3, expr.get_coefficient(&String::from("x3"))));
assert!(approx_eq(-134.3, expr.get_coefficient(&Var::from("x4"))));
assert!(approx_eq(0.0, expr.get_coefficient(&Var::from("x2"))));
assert!(approx_eq(-92.3, expr.get_coefficient(&Var::from("x3"))));
assert!(approx_eq(10.0, expr.get_constant()));
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ pub mod state;
pub mod problem;
pub mod grammar;
pub mod tableau;
pub mod var;

mod grammar_test;
75 changes: 52 additions & 23 deletions src/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::fmt::{Display, Error, Formatter};
use std::collections::{HashMap, HashSet, LinkedList};
use state::*;
use tableau::*;
use var::*;

#[derive(Clone)]
pub enum ProblemObjective {
Expand All @@ -20,10 +21,11 @@ impl ProblemObjective {
/// extern crate constraint;
/// use constraint::expr::{approx_eq, LinearExpression};
/// use constraint::problem::ProblemObjective;
/// use constraint::var::Var;
///
/// fn main() {
/// let objective = ProblemObjective::Minimize(LinearExpression::from(String::from("x")));
/// assert!(approx_eq(1.0, objective.get_expr().get_coefficient(&String::from("x"))));
/// let objective = ProblemObjective::Minimize(LinearExpression::from(Var::from("x")));
/// assert!(approx_eq(1.0, objective.get_expr().get_coefficient(&Var::from("x"))));
/// assert!(approx_eq(0.0, objective.get_expr().get_constant()));
/// }
/// ```
Expand All @@ -43,12 +45,13 @@ impl ProblemObjective {
/// extern crate constraint;
/// use constraint::expr::{approx_eq, LinearExpression};
/// use constraint::problem::ProblemObjective;
/// use constraint::var::Var;
///
/// fn main() {
/// let mut objective = ProblemObjective::Minimize(LinearExpression::new());
/// assert!(approx_eq(0.0, objective.get_expr().get_coefficient(&String::from("x"))));
/// objective.set_expr(LinearExpression::from(String::from("x")));
/// assert!(approx_eq(1.0, objective.get_expr().get_coefficient(&String::from("x"))));
/// assert!(approx_eq(0.0, objective.get_expr().get_coefficient(&Var::from("x"))));
/// objective.set_expr(LinearExpression::from(Var::from("x")));
/// assert!(approx_eq(1.0, objective.get_expr().get_coefficient(&Var::from("x"))));
/// }
/// ```
pub fn set_expr(&mut self, expr: LinearExpression) {
Expand All @@ -58,7 +61,7 @@ impl ProblemObjective {
}
}

fn substitute(&mut self, v: &String, e: &LinearExpression) {
fn substitute(&mut self, v: &Var, e: &LinearExpression) {
let mut f_e = self.get_mut_expr();
f_e.substitute(v, e);
}
Expand Down Expand Up @@ -97,10 +100,11 @@ impl Problem {
/// extern crate constraint;
/// use constraint::expr::{LinearExpression, LinearRelation, Relation};
/// use constraint::problem::{Problem, ProblemObjective};
/// use constraint::var::Var;
///
/// fn main() {
/// let objective = ProblemObjective::Minimize(LinearExpression::from(String::from("x")));
/// let constraints = vec!(LinearRelation::new(LinearExpression::from("x"), Relation::EQ, LinearExpression::from(5.0)));
/// let objective = ProblemObjective::Minimize(LinearExpression::from(Var::from("x")));
/// let constraints = vec!(LinearRelation::new(LinearExpression::from(Var::from("x")), Relation::EQ, LinearExpression::from(5.0)));
/// let _ = Problem::new(objective, constraints);
/// }
/// ```
Expand All @@ -125,7 +129,7 @@ impl Problem {
}

// Returns true iff lr is (0 <= VAR)
fn get_restrained_var<'s, 'r>(&'s self, lr: &'r LinearRelation) -> Option<String> {
fn get_restrained_var<'s, 'r>(&'s self, lr: &'r LinearRelation) -> Option<Var> {
let lhs_valid = lr.lhs.terms().is_empty() && approx_eq(0.0, lr.lhs.get_constant());
let op_valid = lr.op == Relation::LEQ;
let rhs_valid = lr.rhs.terms().len() == 1 && *lr.rhs.terms().values().next().unwrap() >= 0.0;
Expand All @@ -141,13 +145,13 @@ impl Problem {
// 0 == rhs - lhs - s_n, 0 <= s_n
// 0 >= (rhs - lhs - + s_n), 0 <= s_n
// 0 == (rhs - lhs), 0 <= s_n
fn convert_leq_to_eq<'s, 'r>(&'s self, lr: &'r mut LinearRelation, namer: &mut Namer) -> Option<String> {
fn convert_leq_to_eq<'s, 'r>(&'s self, lr: &'r mut LinearRelation, namer: &mut Namer) -> Option<Var> {
if lr.op == Relation::LEQ {
let slack = namer.vend();
lr.lhs.plus_this(&LinearExpression::from(slack.as_ref()));
let slack = Var(namer.vend(), true);
lr.lhs.plus_this(&LinearExpression::from(slack.clone()));
lr.lhs.times_this(-1.0);
lr.rhs.plus_this(&lr.lhs);
lr.lhs = LinearExpression::new();
lr.lhs = LinearExpression::new();

lr.op = Relation::EQ;
Some(slack)
Expand All @@ -165,14 +169,15 @@ impl Problem {
/// extern crate constraint;
/// use constraint::expr::{approx_eq, LinearExpression, LinearRelation, Relation};
/// use constraint::problem::{Problem, ProblemObjective};
/// use constraint::var::Var;
///
/// fn main() {
/// let objective = ProblemObjective::Minimize(LinearExpression::from(String::from("x")));
/// let constraints = vec!(LinearRelation::new(LinearExpression::from("x"), Relation::EQ, LinearExpression::from(5.0)));
/// let objective = ProblemObjective::Minimize(LinearExpression::from(Var::from("x")));
/// let constraints = vec!(LinearRelation::new(LinearExpression::from(Var::from("x")), Relation::EQ, LinearExpression::from(5.0)));
/// let problem = Problem::new(objective, constraints);
/// let tableau = problem.augmented_simplex().unwrap();
/// let basic_feasible_solution = tableau.get_basic_feasible_solution();
/// assert!(approx_eq(5.0, *basic_feasible_solution.get(&String::from("x")).unwrap()));
/// assert!(approx_eq(5.0, *basic_feasible_solution.get(&Var::from("x")).unwrap()));
/// }
pub fn augmented_simplex(&self) -> Result<Tableau, String> {
let mut slack_namer = Namer::init("s_");
Expand All @@ -184,10 +189,10 @@ impl Problem {
// 2: Note if this forms a `restrained` constraint for a specific variable: 0 <= var. Special case. Else, treat like normal slack: -var + s_n <= 0.
// 3: Convert LEQs to equations, generating a slack variable. Add to restricted variables.
// 4: Return: <equations, slack variables>
fn augmented_simplex_phase_one(&self, slack_namer: &mut Namer) -> (LinkedList<LinearRelation>, HashSet<String>) {
fn augmented_simplex_phase_one(&self, slack_namer: &mut Namer) -> (LinkedList<LinearRelation>, HashSet<Var>) {
let raw_constraints: LinkedList<LinearRelation> = self.subject_to.iter().map(|c|{c.clone()}).collect();
let mut normalized_constraints = LinkedList::<LinearRelation>::new();
let mut restrained_vars = HashSet::<String>::new();
let mut restrained_vars = HashSet::<Var>::new();

for mut constraint in raw_constraints.into_iter() {
match constraint.op {
Expand Down Expand Up @@ -246,14 +251,14 @@ impl Problem {
// Move resulting equation into c_u.
fn augmented_simplex_phase_two(&self,
constraints: &LinkedList<LinearRelation>,
restrained_variables: &HashSet<String>) -> Result<Tableau, String> {
restrained_variables: &HashSet<Var>) -> Result<Tableau, String> {
let mut c_e = constraints.clone();
let mut c_s = Vec::<LinearRelation>::new();
let mut c_u = HashMap::<String, LinearExpression>::new();
let mut c_u = HashMap::<Var, LinearExpression>::new();
let mut new_f = self.objective.clone();

for ref mut constraint in c_e.iter_mut() {
let mut vars: HashSet<String> = constraint.lhs.terms().keys().chain(constraint.rhs.terms().keys()).map(|s| s.clone()).collect();
let mut vars: HashSet<Var> = constraint.lhs.terms().keys().chain(constraint.rhs.terms().keys()).map(|s| s.clone()).collect();

// first substitute any pending changes.
for ref var in vars.iter() {
Expand Down Expand Up @@ -299,7 +304,7 @@ impl Problem {
println!("{}", r);
}
println!("with restrained variables:");
let vars: Vec<&str> = restrained_variables.iter().map(|s|s.as_ref()).collect();
let vars: Vec<&str> = restrained_variables.iter().map(|s|s.name().as_ref()).collect();
println!("0 <= {}", vars.join(", "));

let mut tableau = Tableau::new();
Expand All @@ -309,7 +314,7 @@ impl Problem {
}

for constraint in c_s.into_iter() {
let p_vars: HashSet<String> = tableau.parametric_vars_iter().map(|s| s.clone()).collect();
let p_vars: HashSet<Var> = tableau.parametric_vars_iter().map(|s| s.clone()).collect();
let o_var = constraint.lhs.terms().keys().chain(constraint.rhs.terms().keys()).find(|s| !p_vars.contains(s.clone()));
if o_var.is_none() {
return Err(format!("{} is not a viable simplex equation", constraint));
Expand Down Expand Up @@ -338,6 +343,30 @@ impl Display for Problem {
#[cfg(test)]
mod test {
use grammar::*;
use expr::*;
use problem::*;
use var::Var;

#[test]
fn const_equation() {
let objective = ProblemObjective::Minimize(LinearExpression::from(Var::from("x")));
let constraints = vec!(LinearRelation::new(LinearExpression::from(Var::from("x")), Relation::EQ, LinearExpression::from(5.0)));
let problem = Problem::new(objective, constraints);
let tableau = problem.augmented_simplex().unwrap();
let basic_feasible_solution = tableau.get_basic_feasible_solution();
assert!(approx_eq(5.0, *basic_feasible_solution.get(&Var::from("x")).unwrap()));
}

#[test]
fn one_slack() {
let objective = ProblemObjective::Minimize(LinearExpression::from(Var::from("x")));
let constraints = vec!(LinearRelation::new(LinearExpression::from(Var::from("x")), Relation::LEQ, LinearExpression::from(-5.0)));
let problem = Problem::new(objective, constraints);
let tableau = problem.augmented_simplex().unwrap();
tableau.print();
assert_eq!(1, tableau.get_parametric_vars().len());
assert!(tableau.is_parametric(&Var::from("s_1")));
}

#[test]
fn test_problem_parse() {
Expand Down
Loading

0 comments on commit 81cffa9

Please sign in to comment.