Skip to content

Commit

Permalink
Methods to directly add rows and cols to highs model (#9)
Browse files Browse the repository at this point in the history
* Update Cargo.toml

* Methods to add rows and cols to highs model directly

* Add "panics" section to add_row, add_col methods

* Check solution value
  • Loading branch information
mmghannam authored Nov 26, 2023
1 parent d47cac2 commit 31ba3f3
Showing 1 changed file with 98 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,87 @@ impl Model {
unsafe { highs_call!(Highs_run(self.highs.mut_ptr())) }
.map(|_| SolvedModel { highs: self.highs })
}

/// Adds a new constraint to the highs model.
///
/// # Panics
///
/// If HIGHS returns an error status value.
pub fn add_row(
mut self,
bounds: impl RangeBounds<f64>,
row_factors: impl IntoIterator<Item = (Col, f64)>,
) -> Self {
self.try_add_row(bounds, row_factors)
.unwrap_or_else(|e| panic!("HiGHS error: {:?}", e));
self
}


/// Tries to add a new constraint to the highs model.
///
/// Returns the status of the model after adding the row.
pub fn try_add_row(
&mut self,
bounds: impl RangeBounds<f64>,
row_factors: impl IntoIterator<Item = (Col, f64)>,
) -> Result<HighsStatus, HighsStatus> {
let (cols, factors): (Vec<_>, Vec<_>) = row_factors.into_iter().unzip();
unsafe {
highs_call!(
Highs_addRow(
self.highs.mut_ptr(),
bound_value(bounds.start_bound()).unwrap_or(f64::NEG_INFINITY),
bound_value(bounds.end_bound()).unwrap_or(f64::INFINITY),
cols.len().try_into().unwrap(),
cols.into_iter().map(|c| c.0.try_into().unwrap()).collect::<Vec<_>>().as_ptr(),
factors.as_ptr()
)
)
}
}


/// Adds a new variable to the highs model.
///
/// # Panics
///
/// If HIGHS returns an error status value.
pub fn add_col(
mut self,
col_factor: f64,
bounds: impl RangeBounds<f64>,
row_factors: impl IntoIterator<Item = (Row, f64)>,
) -> Self {
self.try_add_column(col_factor, bounds, row_factors)
.unwrap_or_else(|e| panic!("HiGHS error: {:?}", e));
self
}

/// Tries to add a new variable to the highs model.
///
/// Returns the status of the model after adding the column.
pub fn try_add_column(
&mut self,
col_factor: f64,
bounds: impl RangeBounds<f64>,
row_factors: impl IntoIterator<Item = (Row, f64)>,
) -> Result<HighsStatus, HighsStatus> {
let (rows, factors): (Vec<_>, Vec<_>) = row_factors.into_iter().unzip();
unsafe {
highs_call!(
Highs_addCol(
self.highs.mut_ptr(),
col_factor,
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.try_into().unwrap()).collect::<Vec<_>>().as_ptr(),
factors.as_ptr()
)
)
}
}
}

impl From<SolvedModel> for Model {
Expand Down Expand Up @@ -555,4 +636,21 @@ mod test {
problem.add_row(2..3, row_factors);
let _ = problem.optimise(Sense::Minimise).try_solve();
}

#[test]
fn test_add_row_and_col() {
let mut model = Model::new::<Problem<ColMatrix>>(Problem::default())
.add_col(1., 1.0.., vec![])
.add_row(..1.0, vec![(Col(0), 1.0)]);
let solved = model.solve();
assert_eq!(solved.status(), HighsModelStatus::Optimal);
let solution = solved.get_solution();
assert_eq!(solution.columns(), vec![1.0]);

let model = Model::from(solved)
.add_col(1., ..1.0, vec![])
.add_row(2.0.., vec![(Col(1), 1.0)]);
let solved = model.solve();
assert_eq!(solved.status(), HighsModelStatus::Infeasible);
}
}

0 comments on commit 31ba3f3

Please sign in to comment.