lp_modeler/solvers/
glpk.rs1extern crate uuid;
2use self::uuid::Uuid;
3
4use std::fs;
5use std::collections::HashMap;
6use std::fs::File;
7use std::io::{Error, BufReader, BufRead};
8use std::process::Command;
9
10use dsl::LpProblem;
11use format::lp_format::*;
12use solvers::{Status, SolverTrait, SolverWithSolutionParsing, Solution};
13
14pub struct GlpkSolver {
15 name: String,
16 command_name: String,
17 temp_solution_file: String,
18}
19
20impl GlpkSolver {
21 pub fn new() -> GlpkSolver {
22 GlpkSolver {
23 name: "Glpk".to_string(),
24 command_name: "glpsol".to_string(),
25 temp_solution_file: format!("{}.sol", Uuid::new_v4().to_string()),
26 }
27 }
28 pub fn command_name(&self, command_name: String) -> GlpkSolver {
29 GlpkSolver {
30 name: self.name.clone(),
31 command_name,
32 temp_solution_file: self.temp_solution_file.clone(),
33 }
34 }
35 pub fn with_temp_solution_file(&self, temp_solution_file: String) -> GlpkSolver {
36 GlpkSolver {
37 name: self.name.clone(),
38 command_name: self.command_name.clone(),
39 temp_solution_file,
40 }
41 }
42}
43
44impl SolverWithSolutionParsing for GlpkSolver {
45 fn read_specific_solution<'a>(&self, f: &File, problem: Option<&'a LpProblem>) -> Result<Solution<'a>, String> {
46 fn read_size(line: Option<Result<String, Error>>) -> Result<usize, String> {
47 match line {
48 Some(Ok(l)) => match l.split_whitespace().nth(1) {
49 Some(value) => match value.parse::<usize>() {
50 Ok(v) => Ok(v),
51 _ => return Err("Incorrect solution format".to_string()),
52 },
53 _ => return Err("Incorrect solution format".to_string()),
54 },
55 _ => return Err("Incorrect solution format".to_string()),
56 }
57 }
58 let mut vars_value: HashMap<_, _> = HashMap::new();
59
60 let file = BufReader::new(f);
61
62 let mut iter = file.lines();
63 let row = match read_size(iter.nth(1)) {
64 Ok(value) => value,
65 Err(e) => return Err(e.to_string()),
66 };
67 let col = match read_size(iter.nth(0)) {
68 Ok(value) => value,
69 Err(e) => return Err(e.to_string()),
70 };
71 let status = match iter.nth(1) {
72 Some(Ok(status_line)) => match &status_line[12..] {
73 "INTEGER OPTIMAL" | "OPTIMAL" => Status::Optimal,
74 "INFEASIBLE (FINAL)" | "INTEGER EMPTY" => Status::Infeasible,
75 "UNDEFINED" => Status::NotSolved,
76 "INTEGER UNDEFINED" | "UNBOUNDED" => Status::Unbounded,
77 _ => {
78 return Err("Incorrect solution format: Unknown solution status".to_string())
79 }
80 },
81 _ => return Err("Incorrect solution format: No solution status found".to_string()),
82 };
83 let mut result_lines = iter.skip(row + 7);
84 for _ in 0..col {
85 let line = match result_lines.next() {
86 Some(Ok(l)) => l,
87 _ => {
88 return Err(
89 "Incorrect solution format: Not all columns are present".to_string()
90 )
91 }
92 };
93 let result_line: Vec<_> = line.split_whitespace().collect();
94 if result_line.len() >= 4 {
95 match result_line[3].parse::<f32>() {
96 Ok(n) => {
97 vars_value.insert(result_line[1].to_string(), n);
98 }
99 Err(e) => return Err(e.to_string()),
100 }
101 } else {
102 return Err(
103 "Incorrect solution format: Column specification has to few fields"
104 .to_string(),
105 );
106 }
107 }
108 if let Some(p) = problem {
109 Ok( Solution::with_problem(status, vars_value, p) )
110 } else {
111 Ok( Solution::new(status, vars_value) )
112 }
113 }
114}
115
116impl SolverTrait for GlpkSolver {
117 type P = LpProblem;
118 fn run<'a>(&self, problem: &'a Self::P) -> Result<Solution<'a>, String> {
119 let file_model = &format!("{}.lp", problem.unique_name);
120
121 match problem.write_lp(file_model) {
122 Ok(_) => {
123 let result = match Command::new(&self.command_name)
124 .arg("--lp")
125 .arg(file_model)
126 .arg("-o")
127 .arg(&self.temp_solution_file)
128 .output()
129 {
130 Ok(r) => {
131 if r.status.success() {
132 self.read_solution(&self.temp_solution_file, Some(problem))
133 } else {
134 Err(r.status.to_string())
135 }
136 }
137 Err(_) => Err(format!("Error running the {} solver", self.name)),
138 };
139 let _ = fs::remove_file(&file_model);
140
141 result
142 }
143 Err(e) => Err(e.to_string()),
144 }
145 }
146}