#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)]
#[cfg(feature = "link")]
use std::os::raw::c_int;
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HighsReturnStatus {
Ok = 0,
Warning = 1,
Error = 2,
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HighsModelStatus {
NotSet = 0,
LoadError = 1,
ModelError = 2,
Infeasible = 8,
Optimal = 7,
Unbounded = 9,
SolutionLimit = 11,
TimeLimit = 12,
}
impl HighsModelStatus {
pub fn is_success(self) -> bool {
matches!(self, Self::Optimal | Self::SolutionLimit | Self::TimeLimit)
}
pub fn is_optimal(self) -> bool {
self == Self::Optimal
}
}
#[repr(C)]
pub struct HighsHandle {
_p: [u8; 0],
}
#[cfg(feature = "link")]
unsafe extern "C" {
pub fn highs_create() -> *mut HighsHandle;
pub fn highs_destroy(h: *mut HighsHandle);
pub fn highs_add_col(h: *mut HighsHandle, cost: f64, lb: f64, ub: f64) -> HighsReturnStatus;
pub fn highs_add_row(
h: *mut HighsHandle,
lb: f64,
ub: f64,
num_nz: c_int,
idx: *const c_int,
val: *const f64,
) -> HighsReturnStatus;
pub fn highs_change_col_integer_type(
h: *mut HighsHandle,
col: c_int,
is_integer: c_int,
) -> HighsReturnStatus;
pub fn highs_set_time_limit(h: *mut HighsHandle, seconds: f64) -> HighsReturnStatus;
pub fn highs_set_mip_rel_gap(h: *mut HighsHandle, gap: f64) -> HighsReturnStatus;
pub fn highs_run(h: *mut HighsHandle) -> HighsReturnStatus;
pub fn highs_get_model_status(h: *mut HighsHandle) -> HighsModelStatus;
pub fn highs_get_objective_value(h: *mut HighsHandle) -> f64;
pub fn highs_get_col_value(h: *mut HighsHandle, col: c_int) -> f64;
pub fn highs_get_mip_gap(h: *mut HighsHandle) -> f64;
}
#[cfg(feature = "link")]
pub mod safe {
#[allow(clippy::wildcard_imports)]
use super::*;
use std::ptr::NonNull;
pub struct HighsSolver {
ptr: NonNull<HighsHandle>,
num_cols: i32,
}
impl HighsSolver {
pub fn new() -> Self {
unsafe {
Self {
ptr: NonNull::new(highs_create()).expect("highs_create returned null"),
num_cols: 0,
}
}
}
pub fn add_col(&mut self, cost: f64, lb: f64, ub: f64) -> i32 {
unsafe { highs_add_col(self.ptr.as_ptr(), cost, lb, ub) };
let idx = self.num_cols;
self.num_cols += 1;
idx
}
pub fn add_int_col(&mut self, cost: f64, lb: f64, ub: f64) -> i32 {
let col = self.add_col(cost, lb, ub);
unsafe { highs_change_col_integer_type(self.ptr.as_ptr(), col, 1) };
col
}
pub fn add_bin_col(&mut self, cost: f64) -> i32 {
self.add_int_col(cost, 0.0, 1.0)
}
pub fn add_row(&mut self, lb: f64, ub: f64, indices: &[i32], coeffs: &[f64]) {
assert_eq!(indices.len(), coeffs.len());
unsafe {
highs_add_row(
self.ptr.as_ptr(),
lb,
ub,
i32::try_from(indices.len()).expect("row nnz fits i32"),
indices.as_ptr(),
coeffs.as_ptr(),
);
}
}
pub fn set_time_limit(&mut self, secs: f64) {
unsafe {
highs_set_time_limit(self.ptr.as_ptr(), secs);
}
}
pub fn set_mip_rel_gap(&mut self, gap: f64) {
unsafe {
highs_set_mip_rel_gap(self.ptr.as_ptr(), gap);
}
}
pub fn run(&mut self) -> HighsModelStatus {
unsafe {
highs_run(self.ptr.as_ptr());
highs_get_model_status(self.ptr.as_ptr())
}
}
pub fn objective_value(&self) -> f64 {
unsafe { highs_get_objective_value(self.ptr.as_ptr()) }
}
pub fn col_value(&self, col: i32) -> f64 {
unsafe { highs_get_col_value(self.ptr.as_ptr(), col) }
}
pub fn mip_gap(&self) -> f64 {
unsafe { highs_get_mip_gap(self.ptr.as_ptr()) }
}
}
impl Default for HighsSolver {
fn default() -> Self {
Self::new()
}
}
impl Drop for HighsSolver {
fn drop(&mut self) {
unsafe { highs_destroy(self.ptr.as_ptr()) }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_values() {
assert_eq!(HighsModelStatus::Optimal as i32, 7);
assert!(HighsModelStatus::Optimal.is_success());
assert!(!HighsModelStatus::Infeasible.is_success());
}
#[cfg(feature = "link")]
mod integration {
use super::super::safe::*;
#[test]
fn mip_binary_knapsack() {
let mut s = HighsSolver::new();
let x = s.add_bin_col(-5.0); let y = s.add_bin_col(-4.0);
let z = s.add_bin_col(-3.0);
s.add_row(f64::NEG_INFINITY, 5.0, &[x, y, z], &[2.0, 3.0, 2.0]);
let status = s.run();
assert!(status.is_success());
assert!((-s.objective_value() - 9.0).abs() < 0.5);
}
}
}