From e6d35dde21991e8965089f622752b52c1f55f7ac Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 10 Nov 2025 13:58:50 +0100 Subject: [PATCH 1/2] rust fmt --- src/lib.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aa9b57a..edcf9a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -261,12 +261,12 @@ pub enum Sense { impl Model { /// Return pointer to underlying HiGHS model - pub fn as_ptr(&self) -> *const c_void{ + pub fn as_ptr(&self) -> *const c_void { self.highs.ptr() } /// Return mutable pointer to underlying HiGHS model - pub fn as_mut_ptr(&mut self) -> *mut c_void{ + pub fn as_mut_ptr(&mut self) -> *mut c_void { self.highs.mut_ptr() } @@ -452,10 +452,7 @@ impl Model { bound_value(bounds.start_bound()).unwrap_or(f64::NEG_INFINITY), bound_value(bounds.end_bound()).unwrap_or(f64::INFINITY), rows.len().try_into().unwrap(), - rows.into_iter() - .map(|r| r.0) - .collect::>() - .as_ptr(), + rows.into_iter().map(|r| r.0).collect::>().as_ptr(), factors.as_ptr() )) }?; @@ -602,12 +599,12 @@ impl HighsPtr { impl SolvedModel { /// Return pointer to underlying HiGHS model - pub fn as_ptr(&self) -> *const c_void{ + pub fn as_ptr(&self) -> *const c_void { self.highs.ptr() } /// Return mutable pointer to underlying HiGHS model - pub fn as_mut_ptr(&mut self) -> *mut c_void{ + pub fn as_mut_ptr(&mut self) -> *mut c_void { self.highs.mut_ptr() } From 522c507c592e34c827006820cc3f92a563bd387f Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 10 Nov 2025 14:03:05 +0100 Subject: [PATCH 2/2] Add some lacking values in HighsModelStatus and add SolvedModel::primal_solution_status These modifications are needed if you configure HiGHS to stop before the end of the optimization. The added enum variant to HighsModelStatus and the added `#[non_exhaustive]` to it are breaking changes. fixes https://github.com/rust-or/highs/issues/26 depends on https://github.com/rust-or/highs-sys/pull/37 --- Cargo.toml | 4 ++-- src/lib.rs | 16 ++++++++++++++-- src/status.rs | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 708bad1..d262f84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "highs" -version = "1.12.0" +version = "2.0.0" authors = ["Ophir LOJKINE", "rust-or"] edition = "2021" description = "Safe rust bindings for the HiGHS linear programming solver. See http://highs.dev." @@ -9,5 +9,5 @@ repository = "https://github.com/rust-or/highs" keywords = ["linear-programming", "optimization", "math", "solver"] [dependencies] -highs-sys = "1.11.0" +highs-sys = "1.12.0" log = "0.4.27" diff --git a/src/lib.rs b/src/lib.rs index edcf9a8..3176008 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ use highs_sys::*; pub use matrix_col::{ColMatrix, Row}; pub use matrix_row::{Col, RowMatrix}; -pub use status::{HighsModelStatus, HighsStatus}; +pub use status::{HighsModelStatus, HighsSolutionStatus, HighsStatus}; use crate::options::HighsOptionValue; @@ -615,7 +615,7 @@ impl SolvedModel { unsafe { highs_sys::Highs_getObjectiveValue(self.as_ptr()) } } - /// The status of the solution. Should be Optimal if everything went well. + /// The model status of the solution. Should be Optimal if everything went well. pub fn status(&self) -> HighsModelStatus { let model_status = unsafe { Highs_getModelStatus(self.highs.unsafe_mut_ptr()) }; HighsModelStatus::try_from(model_status).unwrap() @@ -633,6 +633,18 @@ impl SolvedModel { .unwrap() } + /// The primal solution status, reflecting if a solution was found or not. + pub fn primal_solution_status(&self) -> HighsSolutionStatus { + let name = CString::new("primal_solution_status").unwrap(); + let solution_status: &mut HighsInt = &mut -1; + let status = unsafe { + Highs_getIntInfoValue(self.highs.unsafe_mut_ptr(), name.as_ptr(), solution_status) + }; + try_handle_status(status, "Highs_getIntInfoValue") + .map(|_| HighsSolutionStatus::try_from(*solution_status).unwrap()) + .unwrap() + } + /// Get the solution to the problem pub fn get_solution(&self) -> Solution { let cols = self.num_cols(); diff --git a/src/status.rs b/src/status.rs index 6b31786..4e52739 100644 --- a/src/status.rs +++ b/src/status.rs @@ -7,6 +7,7 @@ use highs_sys::*; /// The kinds of results of an optimization #[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Ord, Eq)] +#[non_exhaustive] pub enum HighsModelStatus { /// not initialized NotSet = MODEL_STATUS_NOTSET as isize, @@ -39,12 +40,18 @@ pub enum HighsModelStatus { ObjectiveBound = MODEL_STATUS_OBJECTIVE_BOUND as isize, /// objective target ObjectiveTarget = MODEL_STATUS_OBJECTIVE_TARGET as isize, - /// reached limit + /// reached time limit ReachedTimeLimit = MODEL_STATUS_REACHED_TIME_LIMIT as isize, - /// reached limit + /// reached iteration limit ReachedIterationLimit = MODEL_STATUS_REACHED_ITERATION_LIMIT as isize, /// Unknown model status Unknown = MODEL_STATUS_UNKNOWN as isize, + /// reached solution limit + ReachedSolutionLimit = MODEL_STATUS_REACHED_SOLUTION_LIMIT as isize, + /// interrupted + ReachedInterrupt = MODEL_STATUS_REACHED_INTERRUPT as isize, + /// reached memory limit + ReachedMemoryLimit = MODEL_STATUS_REACHED_MEMORY_LIMIT as isize, } /// This error should never happen: an unexpected status was returned @@ -67,7 +74,6 @@ impl TryFrom for HighsModelStatus { type Error = InvalidStatus; fn try_from(value: c_int) -> Result { - use highs_sys::*; match value { MODEL_STATUS_NOTSET => Ok(Self::NotSet), MODEL_STATUS_LOAD_ERROR => Ok(Self::LoadError), @@ -85,6 +91,9 @@ impl TryFrom for HighsModelStatus { MODEL_STATUS_REACHED_TIME_LIMIT => Ok(Self::ReachedTimeLimit), MODEL_STATUS_REACHED_ITERATION_LIMIT => Ok(Self::ReachedIterationLimit), MODEL_STATUS_UNKNOWN => Ok(Self::Unknown), + MODEL_STATUS_REACHED_SOLUTION_LIMIT => Ok(Self::ReachedSolutionLimit), + MODEL_STATUS_REACHED_INTERRUPT => Ok(Self::ReachedInterrupt), + MODEL_STATUS_REACHED_MEMORY_LIMIT => Ok(Self::ReachedMemoryLimit), n => Err(InvalidStatus(n)), } } @@ -119,3 +128,27 @@ impl TryFrom for HighsStatus { } } } + +/// The status of a solution +#[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Ord, Eq)] +pub enum HighsSolutionStatus { + /// No solution found + None = SOLUTION_STATUS_NONE as isize, + /// No solution exists + Infeasible = SOLUTION_STATUS_INFEASIBLE as isize, + /// A feasible solution was found + Feasible = SOLUTION_STATUS_FEASIBLE as isize, +} + +impl TryFrom for HighsSolutionStatus { + type Error = InvalidStatus; + + fn try_from(value: c_int) -> Result { + match value { + SOLUTION_STATUS_NONE => Ok(Self::None), + SOLUTION_STATUS_INFEASIBLE => Ok(Self::Infeasible), + SOLUTION_STATUS_FEASIBLE => Ok(Self::Feasible), + n => Err(InvalidStatus(n)), + } + } +}