use std::collections::HashMap;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use crate::Constraint;
use crate::variable::UnsolvedProblem;
use crate::{IntoAffineExpression, Variable, constraint::ConstraintReference};
#[cfg(feature = "cplex-rs")]
#[cfg_attr(docsrs, doc(cfg(feature = "cplex-rs")))]
pub mod cplex;
#[cfg(feature = "coin_cbc")]
#[cfg_attr(docsrs, doc(cfg(feature = "coin_cbc")))]
pub mod coin_cbc;
#[cfg(feature = "microlp")]
#[cfg_attr(docsrs, doc(cfg(feature = "microlp")))]
pub mod microlp;
#[cfg(feature = "lpsolve")]
#[cfg_attr(docsrs, doc(cfg(feature = "lpsolve")))]
pub mod lpsolve;
#[cfg(feature = "highs")]
#[cfg_attr(docsrs, doc(cfg(feature = "highs")))]
pub mod highs;
#[cfg(feature = "scip")]
#[cfg_attr(docsrs, doc(cfg(feature = "scip")))]
pub mod scip;
#[cfg(feature = "lp-solvers")]
#[cfg_attr(docsrs, doc(cfg(feature = "lp-solvers")))]
pub mod lp_solvers;
#[cfg(feature = "clarabel")]
#[cfg_attr(docsrs, doc(cfg(feature = "clarabel")))]
pub mod clarabel;
pub trait Solver {
type Model: SolverModel;
fn create_model(&mut self, problem: UnsolvedProblem) -> Self::Model;
fn name() -> &'static str;
}
pub fn solver_name<T: Solver>(_: T) -> &'static str {
<T as Solver>::name()
}
pub trait StaticSolver: Solver + 'static {}
impl<T> StaticSolver for T where T: Solver + 'static {}
impl<SOLVER, MODEL> Solver for SOLVER
where
SOLVER: FnMut(UnsolvedProblem) -> MODEL,
MODEL: SolverModel,
{
type Model = MODEL;
fn create_model(&mut self, pb: UnsolvedProblem) -> Self::Model {
self(pb)
}
fn name() -> &'static str {
MODEL::name()
}
}
#[derive(Eq, PartialEq, Clone, Copy)]
pub enum ObjectiveDirection {
Maximisation,
Minimisation,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ResolutionError {
Unbounded,
Infeasible,
Other(&'static str),
Str(String),
}
impl Display for ResolutionError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ResolutionError::Unbounded => write!(
f,
"Unbounded: The objective can be made infinitely large without violating any constraints."
),
ResolutionError::Infeasible => write!(
f,
"Infeasible: The problem contains contradictory constraints. No solution exists."
),
ResolutionError::Other(s) => write!(
f,
"An unexpected error occurred while running the optimizer: {}.",
s
),
ResolutionError::Str(s) => write!(
f,
"An unexpected error occurred while running the optimizer: {}.",
s
),
}
}
}
impl From<String> for ResolutionError {
fn from(s: String) -> Self {
ResolutionError::Str(s)
}
}
impl Error for ResolutionError {}
#[derive(Debug, PartialEq, Clone)]
pub enum MipGapError {
Negative,
Infinite,
Other(String),
}
impl Display for MipGapError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MipGapError::Negative => write!(f, "Negative: The MIP gap is negative"),
MipGapError::Infinite => write!(f, "Infinite: The MIP gap is infinite"),
MipGapError::Other(s) => {
write!(f, "An unexpected error occurred setting the MIP Gap: {s}")
}
}
}
}
impl Error for MipGapError {}
pub trait SolverModel {
type Solution: Solution;
type Error: std::error::Error;
fn with(mut self, constraint: Constraint) -> Self
where
Self: Sized,
{
self.add_constraint(constraint);
self
}
fn with_all(mut self, constraints: impl IntoIterator<Item = Constraint>) -> Self
where
Self: Sized,
{
for constraint in constraints {
self.add_constraint(constraint);
}
self
}
fn solve(self) -> Result<Self::Solution, Self::Error>;
fn add_constraint(&mut self, c: Constraint) -> ConstraintReference;
fn name() -> &'static str;
}
pub trait WithInitialSolution {
fn with_initial_solution(self, solution: impl IntoIterator<Item = (Variable, f64)>) -> Self;
}
pub trait WithTimeLimit {
fn with_time_limit<T: Into<f64>>(self, seconds: T) -> Self;
}
#[derive(Clone, Copy, Debug)]
pub enum SolutionStatus {
Optimal,
TimeLimit,
GapLimit,
}
pub trait Solution {
fn status(&self) -> SolutionStatus;
fn value(&self, variable: Variable) -> f64;
fn eval<E: IntoAffineExpression>(&self, expr: E) -> f64
where
Self: Sized,
{
expr.eval_with(self)
}
}
impl<N: Into<f64> + Clone> Solution for HashMap<Variable, N> {
fn status(&self) -> SolutionStatus {
SolutionStatus::Optimal
}
fn value(&self, variable: Variable) -> f64 {
self[&variable].clone().into()
}
}
pub trait DualValues {
fn dual(&self, c: ConstraintReference) -> f64;
}
pub trait SolutionWithDual<'a> {
type Dual: DualValues;
fn compute_dual(&'a mut self) -> Self::Dual;
}
#[allow(clippy::upper_case_acronyms)]
pub trait ModelWithSOS1 {
fn add_sos1<I: IntoAffineExpression>(&mut self, variables_and_weights: I);
fn with_sos1<I: IntoAffineExpression>(mut self, variables_and_weights: I) -> Self
where
Self: Sized,
{
self.add_sos1(variables_and_weights);
self
}
}
pub trait WithMipGap {
fn mip_gap(&self) -> Option<f32>;
fn with_mip_gap(self, mip_gap: f32) -> Result<Self, MipGapError>
where
Self: Sized;
}