lp_solvers/solvers/
auto.rs

1//! Auto solvers automatically find which of their child solvers is installed on
2//! the user's computer and uses it. The [AllSolvers] solvers tries all the supported solvers.
3
4use crate::lp_format::{LpObjective, LpProblem};
5use crate::problem::{Problem, StrExpression, Variable};
6#[cfg(feature = "cplex")]
7use crate::solvers::cplex::Cplex;
8use crate::solvers::{CbcSolver, GlpkSolver, GurobiSolver, Solution};
9
10use super::SolverTrait;
11
12/// A solver that tries multiple solvers
13#[derive(Debug, Clone)]
14pub struct AutoSolver<SOLVER, NEXT>(SOLVER, NEXT);
15
16/// The tail of a list of solvers. This one has no children and never finds any solver.
17#[derive(Debug, Clone, Default)]
18pub struct NoSolver;
19
20#[cfg(not(feature = "cplex"))]
21type Cplex = NoSolver;
22
23/// An [AutoSolver] that tries, in order: Gurobi, Cplex, Cbc and Glpk
24pub type AllSolvers = AutoSolver<
25    GurobiSolver,
26    AutoSolver<Cplex, AutoSolver<CbcSolver, AutoSolver<GlpkSolver, NoSolver>>>,
27>;
28
29impl SolverTrait for NoSolver {
30    fn run<'a, P: LpProblem<'a>>(&self, _problem: &'a P) -> Result<Solution, String> {
31        Err("No solver available".to_string())
32    }
33}
34
35/// The default AutoSolver contains all supported solvers
36impl<A: Default, B: Default> Default for AutoSolver<A, B> {
37    fn default() -> Self {
38        AutoSolver(A::default(), B::default())
39    }
40}
41
42impl<SOLVER: Default, NEXT: Default> AutoSolver<SOLVER, NEXT> {
43    /// Instantiate an AutoSolver with all supported solvers
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    /// Instantiate an AutoSolver with the given solvers
49    pub fn with_solver<NewSolver>(self, solver: NewSolver) -> AutoSolver<NewSolver, Self> {
50        AutoSolver(solver, self)
51    }
52}
53
54impl<S: SolverTrait, T: SolverTrait> SolverTrait for AutoSolver<S, T> {
55    fn run<'a, P: LpProblem<'a>>(&self, problem: &'a P) -> Result<Solution, String> {
56        // Try solving a dummy problem (to avoid writing a large problem to disk if not necessary)
57        let works = self
58            .0
59            .run(&Problem {
60                name: "dummy".to_string(),
61                sense: LpObjective::Minimize,
62                objective: StrExpression("x".to_string()),
63                variables: vec![Variable {
64                    name: "x".to_string(),
65                    is_integer: false,
66                    lower_bound: 0.0,
67                    upper_bound: 1.0,
68                }],
69                constraints: vec![],
70            })
71            .is_ok();
72        if works {
73            self.0.run(problem)
74        } else {
75            self.1.run(problem)
76        }
77    }
78}