lp_modeler/solvers/
glpk.rs

1extern 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}