Skip to content

Commit

Permalink
Impr genericity for Problem and solver
Browse files Browse the repository at this point in the history
  • Loading branch information
jcavat committed Oct 1, 2016
1 parent 93cec36 commit b0a9ee1
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 74 deletions.
17 changes: 1 addition & 16 deletions src/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,22 +409,7 @@ impl Problem for LpProblem {
fn solve<T: SolverTrait>(&self, s: T) -> Result<(Status, HashMap<String,f32>), String> {

let file_model = "test.lp";

match self.write_lp(file_model) {
Ok(_) => {
// Sometimes, we have to read on stdin to know the status
let status = try!(s.run_solver(file_model));

// Otherwise, the status is written on the output file
let (status_read, res) = try!(s.read_solution());
let _ = fs::remove_file(file_model);
match status {
Some(s) => Ok((s, res)),
_ => Ok((status_read, res))
}
},
Err(e) => Err(e.to_string())
}
s.run_solver(self)
}
}

Expand Down
129 changes: 84 additions & 45 deletions src/solvers.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std;
use std::fs;
use std::fs::{File};
use std::io::prelude::*;
use std::process::Command;
use std::collections::HashMap;
use std::io::BufReader;
use problem::{LpProblem, LpObjective};
use problem::{LpFileFormat};
use std::fs::File;


#[derive(Debug, PartialEq)]
Expand All @@ -17,8 +20,10 @@ pub enum Status {

// TODO: Let only run_solver which run, read and send the solution
pub trait SolverTrait {
fn run_solver(&self, file_model: &str) -> Result<Option<Status>, String>;
fn read_solution(&self) -> Result<(Status, HashMap<String,f32>), String>;
fn run_solver(&self, problem: &LpProblem) -> Result<(Status, HashMap<String,f32>), String>;
}

pub trait LinearSolverTrait : SolverTrait {
}

pub struct GurobiSolver {
Expand All @@ -39,36 +44,7 @@ impl GurobiSolver {
pub fn command_name(&self, command_name: String) -> GurobiSolver {
GurobiSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
}
}
impl CbcSolver {
pub fn new() -> CbcSolver {
CbcSolver { name: "Cbc".to_string(), command_name: "cbc".to_string(), temp_solution_file: "sol.sol".to_string() }
}
pub fn command_name(&self, command_name: String) -> CbcSolver {
CbcSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
}
}

impl SolverTrait for GurobiSolver {
fn run_solver(&self, file_model: &str) -> Result<Option<Status>, String> {
match Command::new(&self.command_name).arg(format!("ResultFile={}", self.temp_solution_file)).arg(file_model).output() {
Ok(r) => {
let mut status = Status::SubOptimal;
if String::from_utf8(r.stdout).expect("").contains("Optimal solution found") {
status = Status::Optimal;
}
if r.status.success() {
Ok(Some(status))
} else {
Err(r.status.to_string())
}
},
Err(_) => Err(format!("Error running the {} solver", self.name)),
}
}

fn read_solution(&self) -> Result<(Status, HashMap<String, f32>), String> {

fn read_specific_solution(f: &File) -> Result<(Status, HashMap<String, f32>), String> {

let mut vars_value: HashMap<_,_> = HashMap::new();
Expand Down Expand Up @@ -107,19 +83,12 @@ impl SolverTrait for GurobiSolver {
}
}
}

impl SolverTrait for CbcSolver {
fn run_solver(&self, file_model: &str) -> Result<Option<Status>, String> {
match Command::new(&self.command_name).arg(file_model).arg("solve").arg("solution").arg(&self.temp_solution_file).output() {
Ok(r) => {
if r.status.success(){
Ok(None)
}else{
Err(r.status.to_string())
}
},
Err(_) => Err(format!("Error running the {} solver", self.name)),
}
impl CbcSolver {
pub fn new() -> CbcSolver {
CbcSolver { name: "Cbc".to_string(), command_name: "cbc".to_string(), temp_solution_file: "sol.sol".to_string() }
}
pub fn command_name(&self, command_name: String) -> CbcSolver {
CbcSolver { name: self.name.clone(), command_name: command_name, temp_solution_file: self.temp_solution_file.clone() }
}

fn read_solution(&self) -> Result<(Status, HashMap<String, f32>), String> {
Expand Down Expand Up @@ -166,3 +135,73 @@ impl SolverTrait for CbcSolver {
}
}


impl LinearSolverTrait for GurobiSolver {}
impl SolverTrait for GurobiSolver {
fn run_solver(&self, problem: &LpProblem) -> Result<(Status, HashMap<String,f32>), String> {

use std::fs::File;
use std::io::prelude::*;
let file_model = "test.lp";

fn write_lp(problem: &LpProblem, file_model: &str) -> std::io::Result<()> {
let mut buffer = try!(File::create(file_model));
try!(buffer.write(problem.to_lp_file_format().as_bytes()));
Ok(())
}

match write_lp(problem, file_model) {
Ok(_) => {
match Command::new(&self.command_name).arg(format!("ResultFile={}", self.temp_solution_file)).arg(file_model).output() {
Ok(r) => {
let mut status = Status::SubOptimal;
if String::from_utf8(r.stdout).expect("").contains("Optimal solution found") {
status = Status::Optimal;
}
if r.status.success() {
let (_, res) = try!(self.read_solution());
Ok((status, res))
} else {
Err(r.status.to_string())
}
},
Err(_) => Err(format!("Error running the {} solver", self.name)),
}
},
Err(e) => Err(e.to_string()),
}
}
}

impl LinearSolverTrait for CbcSolver {}
impl SolverTrait for CbcSolver {
fn run_solver(&self, problem: &LpProblem) -> Result<(Status, HashMap<String,f32>), String> {

use std::fs::File;
use std::io::prelude::*;
let file_model = "test.lp";

fn write_lp(problem: &LpProblem, file_model: &str) -> std::io::Result<()> {
let mut buffer = try!(File::create(file_model));
try!(buffer.write(problem.to_lp_file_format().as_bytes()));
Ok(())
}

match write_lp(problem, file_model) {
Ok(_) => {
match Command::new(&self.command_name).arg(format!("ResultFile={}", self.temp_solution_file)).arg(file_model).output() {
Ok(r) => {
if r.status.success(){
self.read_solution()
}else{
Err(r.status.to_string())
}
},
Err(_) => Err(format!("Error running the {} solver", self.name)),
}
},
Err(e) => Err(e.to_string()),
}
}
}

27 changes: 14 additions & 13 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use variables::*;
use variables::LpExpression::*;
use std::rc::Rc;
use operations::LpOperations;
use problem::LpFileFormat;


#[test]
Expand All @@ -15,29 +16,29 @@ fn expressions_creation() {
}

#[test]
fn expressions_to_string() {
fn expressions_to_lp_file_format() {
let ref a = LpInteger::new("a");
let ref b = LpInteger::new("b");
let ref c = LpInteger::new("c");

assert_eq!((a + 2*b + c).to_string(), "a + 2 b + c");
assert_eq!((a + b*2 + c).to_string(), "a + 2 b + c");
assert_eq!((a + b*2 + 3 * 2 * c).to_string(), "a + 2 b + 6 c");
assert_eq!((a + 2).to_string(), "a + 2");
assert_eq!((2*a + 2*b -4*c).to_string(), "2 a + 2 b - 4 c");
assert_eq!((-2*a).to_string(), "-2 a");
assert_eq!((a + 2*b + c).to_lp_file_format(), "a + 2 b + c");
assert_eq!((a + b*2 + c).to_lp_file_format(), "a + 2 b + c");
assert_eq!((a + b*2 + 3 * 2 * c).to_lp_file_format(), "a + 2 b + 6 c");
assert_eq!((a + 2).to_lp_file_format(), "a + 2");
assert_eq!((2*a + 2*b -4*c).to_lp_file_format(), "2 a + 2 b - 4 c");
assert_eq!((-2*a).to_lp_file_format(), "-2 a");
}


#[test]
fn constraints_to_string() {
fn constraints_to_lp_file_format() {
let ref a = LpInteger::new("a");
let ref b = LpInteger::new("b");
let ref c = LpInteger::new("c");

assert_eq!((a+b).equal(10).to_string(), "a + b = 10");
assert_eq!((2*a + b).ge(10).to_string(), "2 a + b >= 10");
assert_eq!((2*a + b + 20).ge(c).to_string(), "2 a + b - c >= -20");
assert_eq!((-a).ge(10).to_string(), "-a >= 10");
assert_eq!((2*a - 20 + b).ge(-c).to_string(), "2 a + b + c >= 20");
assert_eq!((a+b).equal(10).to_lp_file_format(), "a + b = 10");
assert_eq!((2*a + b).ge(10).to_lp_file_format(), "2 a + b >= 10");
assert_eq!((2*a + b + 20).ge(c).to_lp_file_format(), "2 a + b - c >= -20");
assert_eq!((-a).ge(10).to_lp_file_format(), "-a >= 10");
assert_eq!((2*a - 20 + b).ge(-c).to_lp_file_format(), "2 a + b + c >= 20");
}

0 comments on commit b0a9ee1

Please sign in to comment.