diff --git a/src/lib.rs b/src/lib.rs index defe34b..fab392f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,7 @@ pub use solvers::scip::scip as default_solver; pub use solvers::{ solver_name, DualValues, ModelWithSOS1, ResolutionError, Solution, SolutionWithDual, Solver, - SolverModel, StaticSolver, WithMipGap, + SolverModel, StaticSolver, WithInitialSolution, WithMipGap, }; pub use variable::{variable, ProblemVariables, Variable, VariableDefinition}; diff --git a/src/solvers/coin_cbc.rs b/src/solvers/coin_cbc.rs index 291714b..3c0eb21 100644 --- a/src/solvers/coin_cbc.rs +++ b/src/solvers/coin_cbc.rs @@ -5,7 +5,7 @@ use std::convert::TryInto; use coin_cbc::{raw::Status, Col, Model, Sense, Solution as CbcSolution}; -use crate::solvers::{MipGapError, ModelWithSOS1, WithMipGap}; +use crate::solvers::{MipGapError, ModelWithSOS1, WithInitialSolution, WithMipGap}; use crate::variable::{UnsolvedProblem, VariableDefinition}; use crate::{ constraint::ConstraintReference, @@ -166,6 +166,19 @@ impl SolverModel for CoinCbcProblem { } } +impl WithInitialSolution for CoinCbcProblem { + fn with_initial_solution( + mut self, + solution: impl IntoIterator, + ) -> Self { + for (var, val) in solution { + self.model + .set_col_initial_solution(self.columns[var.index()], val); + } + self + } +} + /// Unfortunately, the current version of cbc silently ignores /// sos constraints on continuous variables. /// See @@ -218,3 +231,34 @@ impl WithMipGap for CoinCbcProblem { } } } + +#[cfg(test)] +mod tests { + use crate::{variables, Solution, SolverModel, WithInitialSolution}; + use float_eq::assert_float_eq; + + #[test] + fn solve_problem_with_initial_solution() { + let limit = 3.0; + // Solve problem once + variables! { + vars: + 0.0 <= v <= limit; + }; + let pb = vars.maximise(v).using(super::coin_cbc); + let sol = pb.solve().unwrap(); + assert_float_eq!(sol.value(v), limit, abs <= 1e-8); + // Recreate problem and solve with initial solution + let initial_solution = vec![(v, sol.value(v))]; + variables! { + vars: + 0.0 <= v <= limit; + }; + let pb = vars + .maximise(v) + .using(super::coin_cbc) + .with_initial_solution(initial_solution); + let sol = pb.solve().unwrap(); + assert_float_eq!(sol.value(v), limit, abs <= 1e-8); + } +} diff --git a/src/solvers/mod.rs b/src/solvers/mod.rs index 0b2423a..6414191 100644 --- a/src/solvers/mod.rs +++ b/src/solvers/mod.rs @@ -199,6 +199,12 @@ pub trait SolverModel { fn name() -> &'static str; } +/// A solver that can take an initial solution to a problem before solving it +pub trait WithInitialSolution { + /// Sets the initial solution to the problem + fn with_initial_solution(self, solution: impl IntoIterator) -> Self; +} + /// A problem solution pub trait Solution { /// Get the optimal value of a variable of the problem