use microlp::{ComparisonOp, OptimizationDirection, Problem, Variable};
#[derive(Clone, Copy, PartialEq)]
pub enum Domain {
Real,
Integer,
}
#[derive(Clone)]
pub struct VarSpec {
pub obj_coeff: f64,
pub min: f64,
pub max: f64,
pub domain: Domain,
}
#[derive(Clone)]
pub struct ConstraintSpec {
pub terms: Vec<(usize, f64)>, pub op: ComparisonOp,
pub rhs: f64,
}
#[derive(Clone)]
pub struct ModelSpec {
pub vars: Vec<VarSpec>,
pub constraints: Vec<ConstraintSpec>,
}
pub struct Builder {
pub problem: Problem,
pub spec: ModelSpec,
pub vars: Vec<Variable>,
}
impl Builder {
pub fn new(direction: OptimizationDirection) -> Self {
Builder {
problem: Problem::new(direction),
spec: ModelSpec {
vars: vec![],
constraints: vec![],
},
vars: vec![],
}
}
pub fn real(&mut self, obj_coeff: f64, min: f64, max: f64) -> Variable {
let v = self.problem.add_var(obj_coeff, (min, max));
self.push_spec(v, obj_coeff, min, max, Domain::Real);
v
}
pub fn integer(&mut self, obj_coeff: f64, min: i32, max: i32) -> Variable {
let v = self.problem.add_integer_var(obj_coeff, (min, max));
self.push_spec(v, obj_coeff, min as f64, max as f64, Domain::Integer);
v
}
pub fn binary(&mut self, obj_coeff: f64) -> Variable {
let v = self.problem.add_binary_var(obj_coeff);
self.push_spec(v, obj_coeff, 0.0, 1.0, Domain::Integer);
v
}
fn push_spec(&mut self, v: Variable, obj_coeff: f64, min: f64, max: f64, domain: Domain) {
assert_eq!(v.idx(), self.spec.vars.len(), "shadow model out of sync");
self.spec.vars.push(VarSpec {
obj_coeff,
min,
max,
domain,
});
self.vars.push(v);
}
pub fn constraint(&mut self, terms: &[(Variable, f64)], op: ComparisonOp, rhs: f64) {
self.problem.add_constraint(terms, op, rhs);
self.spec.constraints.push(ConstraintSpec {
terms: terms.iter().map(|(v, c)| (v.idx(), *c)).collect(),
op,
rhs,
});
}
}
#[derive(Clone, Copy)]
pub struct Tol {
pub abs: f64,
pub rel: f64,
}
impl Tol {
pub const DEFAULT: Tol = Tol {
abs: 1e-6,
rel: 1e-6,
};
pub fn matches(&self, got: f64, expected: f64) -> bool {
(got - expected).abs() <= self.slack(expected)
}
pub fn slack(&self, expected: f64) -> f64 {
self.abs + self.rel * expected.abs()
}
}
pub enum Expected {
Objective {
value: f64,
tol: Tol,
vars: Option<Vec<(Variable, f64)>>,
},
Infeasible,
Unbounded,
}
impl Expected {
pub fn objective(value: f64) -> Expected {
Expected::Objective {
value,
tol: Tol::DEFAULT,
vars: None,
}
}
pub fn objective_tol(value: f64, tol: Tol) -> Expected {
Expected::Objective {
value,
tol,
vars: None,
}
}
pub fn unique(value: f64, vars: Vec<(Variable, f64)>) -> Expected {
Expected::Objective {
value,
tol: Tol::DEFAULT,
vars: Some(vars),
}
}
}