use crate::scip::ScipPtr;
use crate::{Retcode, Row, Variable, ffi, scip_call, scip_call_panic};
use std::rc::Rc;
pub struct Diver {
pub(crate) scip: Rc<ScipPtr>,
}
impl Diver {
pub fn chg_var_lb(&mut self, var: &Variable, new_bound: f64) {
scip_call_panic! { ffi::SCIPchgVarLbDive(self.scip.raw, var.inner(), new_bound) }
}
pub fn chg_var_ub(&mut self, var: &Variable, new_bound: f64) {
scip_call_panic! { ffi::SCIPchgVarUbDive(self.scip.raw, var.inner(), new_bound) }
}
pub fn chg_var_obj(&mut self, var: &Variable, new_obj: f64) {
scip_call_panic! { ffi::SCIPchgVarObjDive(self.scip.raw, var.inner(), new_obj) }
}
pub fn solve_lp(&mut self, iteration_limit: Option<usize>) -> Result<bool, Retcode> {
let mut limit = -1;
if let Some(iterations) = iteration_limit {
limit = iterations.try_into().unwrap();
}
let mut lperror = 0;
let mut cutoff = 0;
scip_call! { ffi::SCIPsolveDiveLP(self.scip.raw, limit, &mut lperror, &mut cutoff) }
if lperror != 0 {
return Err(Retcode::LpError);
}
Ok(unsafe { ffi::SCIPgetLPSolstat(self.scip.raw) }
== ffi::SCIP_LPSolStat_SCIP_LPSOLSTAT_OPTIMAL)
}
pub fn add_row(&mut self, row: &Row) {
scip_call_panic! { ffi::SCIPaddRowDive(self.scip.raw, row.inner()) }
}
pub fn chg_row_lhs(&mut self, row: &Row, new_lhs: f64) {
scip_call_panic! { ffi::SCIPchgRowLhsDive(self.scip.raw, row.inner(), new_lhs) }
}
pub fn chg_row_rhs(&mut self, row: &Row, new_rhs: f64) {
scip_call_panic! { ffi::SCIPchgRowRhsDive(self.scip.raw, row.inner(), new_rhs) }
}
pub fn var_obj(&self, var: &Variable) -> f64 {
unsafe { ffi::SCIPgetVarObjDive(self.scip.raw, var.inner()) }
}
pub fn var_lb(&self, var: &Variable) -> f64 {
unsafe { ffi::SCIPgetVarLbDive(self.scip.raw, var.inner()) }
}
pub fn var_ub(&self, var: &Variable) -> f64 {
unsafe { ffi::SCIPgetVarUbDive(self.scip.raw, var.inner()) }
}
pub fn last_dive_node(&self) -> usize {
unsafe { ffi::SCIPgetLastDivenode(self.scip.raw) as usize }
}
pub fn chg_cutoff_bound(&mut self, cutoff: f64) {
scip_call_panic! { ffi::SCIPchgCutoffboundDive(self.scip.raw, cutoff) }
}
}
impl Drop for Diver {
fn drop(&mut self) {
assert_eq!(
unsafe { ffi::SCIPinDive(self.scip.raw) },
1,
"SCIP is expected to be in diving mode before Diver is dropped."
);
unsafe { ffi::SCIPendDive(self.scip.raw) };
}
}
#[cfg(test)]
mod tests {
use crate::model::Model;
use crate::prelude::{eventhdlr, row};
use crate::{Event, EventMask, SCIPEventhdlr, Solving};
use crate::{Eventhdlr, LPStatus, ModelWithProblem, ParamSetting, ffi};
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
#[test]
fn test_diver() {
struct DivingTester {
checked: Rc<AtomicBool>,
}
impl Eventhdlr for DivingTester {
fn get_type(&self) -> EventMask {
EventMask::NODE_SOLVED
}
fn execute(
&mut self,
mut model: Model<Solving>,
_eventhdlr: SCIPEventhdlr,
_event: Event,
) {
if self.checked.load(Ordering::SeqCst) {
return;
}
let mut diver = model.start_diving();
let vars = model.vars();
for var in vars {
diver.chg_var_obj(&var, 0.0);
assert_eq!(diver.var_obj(&var), 0.0);
}
let result = diver.solve_lp(None).unwrap();
assert!(result);
assert_eq!(model.lp_status(), LPStatus::Optimal);
assert!(model.lp_obj_val().abs() < 1e-6);
let current_node = model.focus_node().number();
assert_eq!(diver.last_dive_node(), current_node);
diver.add_row(&model.add(row().eq(-1.0))); diver.solve_lp(None).unwrap();
assert_eq!(model.lp_status(), LPStatus::Infeasible);
drop(diver);
assert_eq!(unsafe { ffi::SCIPinDive(model.scip.raw) }, 0);
self.checked.store(true, Ordering::SeqCst);
}
}
let checked = Rc::new(AtomicBool::new(false));
let mut model = Model::new()
.include_default_plugins()
.read_prob("data/test/simple.mps")
.unwrap()
.hide_output()
.set_presolving(ParamSetting::Off)
.set_separating(ParamSetting::Off)
.set_heuristics(ParamSetting::Off);
model.add(eventhdlr(DivingTester {
checked: checked.clone(),
}));
model.solve();
assert!(
checked.load(Ordering::SeqCst),
"diving assertions never ran"
);
}
}