1#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)]
2
3#[cfg(feature = "link")]
4use std::os::raw::c_int;
5
6#[repr(i32)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum HighsReturnStatus {
11 Ok = 0,
12 Warning = 1,
13 Error = 2,
14}
15
16#[repr(i32)]
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum HighsModelStatus {
19 NotSet = 0,
20 LoadError = 1,
21 ModelError = 2,
22 Infeasible = 8,
23 Optimal = 7,
24 Unbounded = 9,
25 SolutionLimit = 11,
26 TimeLimit = 12,
27}
28
29impl HighsModelStatus {
30 pub fn is_success(self) -> bool {
31 matches!(self, Self::Optimal | Self::SolutionLimit | Self::TimeLimit)
32 }
33 pub fn is_optimal(self) -> bool {
34 self == Self::Optimal
35 }
36}
37
38#[repr(C)]
41pub struct HighsHandle {
42 _p: [u8; 0],
43}
44
45#[cfg(feature = "link")]
48unsafe extern "C" {
49 pub fn highs_create() -> *mut HighsHandle;
50 pub fn highs_destroy(h: *mut HighsHandle);
51 pub fn highs_add_col(h: *mut HighsHandle, cost: f64, lb: f64, ub: f64) -> HighsReturnStatus;
52 pub fn highs_add_row(
53 h: *mut HighsHandle,
54 lb: f64,
55 ub: f64,
56 num_nz: c_int,
57 idx: *const c_int,
58 val: *const f64,
59 ) -> HighsReturnStatus;
60 pub fn highs_change_col_integer_type(
61 h: *mut HighsHandle,
62 col: c_int,
63 is_integer: c_int,
64 ) -> HighsReturnStatus;
65 pub fn highs_set_time_limit(h: *mut HighsHandle, seconds: f64) -> HighsReturnStatus;
66 pub fn highs_set_mip_rel_gap(h: *mut HighsHandle, gap: f64) -> HighsReturnStatus;
67 pub fn highs_run(h: *mut HighsHandle) -> HighsReturnStatus;
68 pub fn highs_get_model_status(h: *mut HighsHandle) -> HighsModelStatus;
69 pub fn highs_get_objective_value(h: *mut HighsHandle) -> f64;
70 pub fn highs_get_col_value(h: *mut HighsHandle, col: c_int) -> f64;
71 pub fn highs_get_mip_gap(h: *mut HighsHandle) -> f64;
72}
73
74#[cfg(feature = "link")]
77pub mod safe {
78 #[allow(clippy::wildcard_imports)]
79 use super::*;
80 use std::ptr::NonNull;
81
82 pub struct HighsSolver {
83 ptr: NonNull<HighsHandle>,
84 num_cols: i32,
85 }
86
87 impl HighsSolver {
88 pub fn new() -> Self {
89 unsafe {
90 Self {
91 ptr: NonNull::new(highs_create()).expect("highs_create returned null"),
92 num_cols: 0,
93 }
94 }
95 }
96
97 pub fn add_col(&mut self, cost: f64, lb: f64, ub: f64) -> i32 {
99 unsafe { highs_add_col(self.ptr.as_ptr(), cost, lb, ub) };
100 let idx = self.num_cols;
101 self.num_cols += 1;
102 idx
103 }
104
105 pub fn add_int_col(&mut self, cost: f64, lb: f64, ub: f64) -> i32 {
107 let col = self.add_col(cost, lb, ub);
108 unsafe { highs_change_col_integer_type(self.ptr.as_ptr(), col, 1) };
109 col
110 }
111
112 pub fn add_bin_col(&mut self, cost: f64) -> i32 {
114 self.add_int_col(cost, 0.0, 1.0)
115 }
116
117 pub fn add_row(&mut self, lb: f64, ub: f64, indices: &[i32], coeffs: &[f64]) {
119 assert_eq!(indices.len(), coeffs.len());
120 unsafe {
121 highs_add_row(
122 self.ptr.as_ptr(),
123 lb,
124 ub,
125 i32::try_from(indices.len()).expect("row nnz fits i32"),
126 indices.as_ptr(),
127 coeffs.as_ptr(),
128 );
129 }
130 }
131
132 pub fn set_time_limit(&mut self, secs: f64) {
133 unsafe {
134 highs_set_time_limit(self.ptr.as_ptr(), secs);
135 }
136 }
137
138 pub fn set_mip_rel_gap(&mut self, gap: f64) {
139 unsafe {
140 highs_set_mip_rel_gap(self.ptr.as_ptr(), gap);
141 }
142 }
143
144 pub fn run(&mut self) -> HighsModelStatus {
145 unsafe {
146 highs_run(self.ptr.as_ptr());
147 highs_get_model_status(self.ptr.as_ptr())
148 }
149 }
150
151 pub fn objective_value(&self) -> f64 {
152 unsafe { highs_get_objective_value(self.ptr.as_ptr()) }
153 }
154
155 pub fn col_value(&self, col: i32) -> f64 {
156 unsafe { highs_get_col_value(self.ptr.as_ptr(), col) }
157 }
158
159 pub fn mip_gap(&self) -> f64 {
160 unsafe { highs_get_mip_gap(self.ptr.as_ptr()) }
161 }
162 }
163
164 impl Default for HighsSolver {
165 fn default() -> Self {
166 Self::new()
167 }
168 }
169
170 impl Drop for HighsSolver {
171 fn drop(&mut self) {
172 unsafe { highs_destroy(self.ptr.as_ptr()) }
173 }
174 }
175}
176
177#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn status_values() {
185 assert_eq!(HighsModelStatus::Optimal as i32, 7);
186 assert!(HighsModelStatus::Optimal.is_success());
187 assert!(!HighsModelStatus::Infeasible.is_success());
188 }
189
190 #[cfg(feature = "link")]
191 mod integration {
192 use super::super::safe::*;
193
194 #[test]
195 fn mip_binary_knapsack() {
196 let mut s = HighsSolver::new();
198 let x = s.add_bin_col(-5.0); let y = s.add_bin_col(-4.0);
200 let z = s.add_bin_col(-3.0);
201 s.add_row(f64::NEG_INFINITY, 5.0, &[x, y, z], &[2.0, 3.0, 2.0]);
202 let status = s.run();
203 assert!(status.is_success());
204 assert!((-s.objective_value() - 9.0).abs() < 0.5);
206 }
207 }
208}