lp_modeler/solvers/
mod.rs1use std::collections::HashMap;
27
28use dsl::{Problem, LpContinuous, LpBinary, LpInteger, LpProblem, LpExprNode, LpExprOp, LpExprArenaIndex};
29
30pub mod cbc;
31pub use self::cbc::*;
32
33pub mod gurobi;
34pub use self::gurobi::*;
35
36pub mod glpk;
37pub use self::glpk::*;
38
39#[cfg(feature = "minilp")]
40pub mod minilp;
41#[cfg(feature = "minilp")]
42pub use self::minilp::*;
43
44#[cfg(feature = "native_coin_cbc")]
45pub mod native_cbc;
46#[cfg(feature = "native_coin_cbc")]
47pub use self::native_cbc::*;
48
49use std::fs::File;
50use std::fs;
51use util::is_zero;
52
53#[derive(Debug, PartialEq, Clone)]
54pub enum Status {
55 Optimal,
56 SubOptimal,
57 Infeasible,
58 Unbounded,
59 NotSolved,
60}
61
62#[derive(Debug, Clone)]
63pub struct Solution<'a> {
64 pub status: Status,
65 pub results: HashMap<String, f32>,
66 pub related_problem: Option<&'a LpProblem>
67}
68impl Solution<'_> {
69 pub fn new<'a>(status: Status, results: HashMap<String, f32>) -> Solution<'a> {
70 Solution {
71 status,
72 results,
73 related_problem: None
74 }
75 }
76 pub fn with_problem(status: Status, results: HashMap<String, f32>, problem: &LpProblem) -> Solution {
77 Solution {
78 status,
79 results,
80 related_problem: Some(problem)
81 }
82 }
83 fn check_possible_solution(&self) {
84 match &self.status {
85 Status::Unbounded | Status::NotSolved | Status::Infeasible => panic!("Solution must be optimal or suboptimal"),
86 _ => ()
87 }
88 }
89 pub fn get_raw_value(&self, name: &str) -> f32 {
90 self.check_possible_solution();
91 *self.results.get(name).expect("No value found for this variable. Check if the variable has been used in the related problem.")
92 }
93 pub fn get_bool(&self, var: &LpBinary) -> bool {
94 self.check_possible_solution();
95 self.results.get(&var.name).and_then(|&f| if is_zero(1.0-f) { Some(true) } else if is_zero(f) { Some(false) } else { None } ).expect("Result value cannot be interpreted as boolean")
96 }
97 pub fn get_float(&self, var: &LpContinuous) -> f32 {
98 self.check_possible_solution();
99 *self.results.get(&var.name).expect("No value found for this variable. Check if the variable has been used in the related problem.")
100 }
101 pub fn get_int(&self, var: &LpInteger) -> i32 {
102 self.check_possible_solution();
103 let &f = self.results.get(&var.name).expect("No value found for this variable. Check if the variable has been used in the related problem.");
104 let i = f as i32;
105 assert!( is_zero( f-(i as f32)), format!("Value {} cannot be interpreted as integer.", f) );
106 i
107 }
108 pub fn eval(&self) -> Option<f32> {
109 self.related_problem
110 .and_then( |problem| {
111 match &problem.obj_expr_arena {
112 Some(obj_expr_arena) => Some( self.eval_with(&obj_expr_arena.get_root_index(), &self.results) ),
113 None => None
114 }
115 })
116 }
117 fn eval_with(&self, index: &LpExprArenaIndex, values: &HashMap<String, f32>) -> f32 {
118 match self.related_problem.unwrap().obj_expr_arena.as_ref().unwrap().expr_ref_at(*index) {
119 LpExprNode::LpCompExpr(operation, left, right) => {
120 match operation {
121 LpExprOp::Addition => self.eval_with(left, values) + self.eval_with(right, values),
122 LpExprOp::Multiplication => self.eval_with(left, values) * self.eval_with(right, values),
123 LpExprOp::Subtraction => self.eval_with(left, values) - self.eval_with(right, values),
124 }
125 },
126 LpExprNode::ConsBin(LpBinary { name })
127 | LpExprNode::ConsCont(LpContinuous { name, .. })
128 | LpExprNode::ConsInt(LpInteger { name, .. }) => *values.get(name).unwrap_or(&0f32),
129 LpExprNode::LitVal(n) => *n,
130 LpExprNode::EmptyExpr => 0.0
131 }
132 }
133}
134
135pub trait SolverTrait {
136 type P: Problem;
137 fn run<'a>(&self, problem: &'a Self::P) -> Result<Solution<'a>, String>;
138}
139
140pub trait SolverWithSolutionParsing {
141 fn read_solution<'a>(&self, temp_solution_file: &String, problem: Option<&'a LpProblem>) -> Result<Solution<'a>, String> {
142 match File::open( temp_solution_file ) {
143 Ok(f) => {
144 let res = self.read_specific_solution(&f, problem)?;
145 let _ = fs::remove_file(temp_solution_file);
146 Ok(res)
147 }
148 Err(_) => return Err("Cannot open file".to_string()),
149 }
150 }
151 fn read_specific_solution<'a>(&self, f: &File, problem: Option<&'a LpProblem>) -> Result<Solution<'a>, String>;
152}
153
154pub trait WithMaxSeconds<T> {
155 fn max_seconds(&self) -> Option<u32>;
156 fn with_max_seconds(&self, seconds: u32) -> T;
157}
158
159pub trait WithNbThreads<T> {
160 fn nb_threads(&self) -> Option<u32>;
161 fn with_nb_threads(&self, threads: u32) -> T;
162}