From 9c572b7445a0a2d74b46f051aa1a89960c14fe32 Mon Sep 17 00:00:00 2001 From: Mohammed Ghannam Date: Thu, 9 Jan 2025 12:46:03 +0100 Subject: [PATCH 1/3] Add access to internal scip separator in callbacks --- src/scip.rs | 6 +- src/separator.rs | 147 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/src/scip.rs b/src/scip.rs index be2ba51..50b3139 100644 --- a/src/scip.rs +++ b/src/scip.rs @@ -2,7 +2,8 @@ use crate::branchrule::{BranchRule, BranchingCandidate}; use crate::pricer::{Pricer, PricerResultState}; use crate::{ ffi, scip_call_panic, BranchingResult, Constraint, Eventhdlr, HeurResult, Model, Node, - ObjSense, ParamSetting, Retcode, Separator, Solution, Solving, Status, VarType, Variable, + ObjSense, ParamSetting, Retcode, SCIPSeparator, Separator, Solution, Solving, Status, VarType, + Variable, }; use crate::{scip_call, HeurTiming, Heuristic}; use core::panic; @@ -990,7 +991,8 @@ impl ScipPtr { scip: Rc::new(scip_ptr), state: Solving, }; - let sep_res = unsafe { (*rule_ptr).execute_lp(model) }; + let separator = SCIPSeparator { raw: separator }; + let sep_res = unsafe { (*rule_ptr).execute_lp(model, separator) }; unsafe { *result = sep_res.into() }; diff --git a/src/separator.rs b/src/separator.rs index 6ff21ba..99dcee0 100644 --- a/src/separator.rs +++ b/src/separator.rs @@ -1,10 +1,17 @@ -use crate::{ffi, Model, Solving}; -use scip_sys::SCIP_Result; +use crate::{ffi, scip_call, Model, Retcode, Row, Solving}; +use scip_sys::{SCIP_Result, SCIP_ROW}; /// A trait for defining custom separation routines. pub trait Separator { /// Execute the separation routine on LP solutions. - fn execute_lp(&mut self, model: Model) -> SeparationResult; + /// + /// # Arguments + /// * `model` - the current model of the SCIP instance in `Solving` stage. + /// * `sep` - the internal separator object. + /// + /// # Returns + /// * `SeparationResult` indicating the result of the separation routine. + fn execute_lp(&mut self, model: Model, sep: SCIPSeparator) -> SeparationResult; } /// The result of a separation routine. @@ -58,6 +65,81 @@ impl From for SCIP_Result { } } +/// A wrapper struct for the internal ffi::SCIP_SEPA +pub struct SCIPSeparator { + pub(crate) raw: *mut ffi::SCIP_SEPA, +} + +impl SCIPSeparator { + /// Returns the name of the separator. + pub fn name(&self) -> String { + unsafe { + let name_ptr = ffi::SCIPsepaGetName(self.raw); + let name = std::ffi::CStr::from_ptr(name_ptr).to_str().unwrap(); + name.to_string() + } + } + + /// Returns the description of the separator. + pub fn desc(&self) -> String { + unsafe { + let desc_ptr = ffi::SCIPsepaGetDesc(self.raw); + let desc = std::ffi::CStr::from_ptr(desc_ptr).to_str().unwrap(); + desc.to_string() + } + } + + /// Returns the priority of the separator. + pub fn priority(&self) -> i32 { + unsafe { ffi::SCIPsepaGetPriority(self.raw) } + } + + /// Returns the frequency of the separator. + pub fn freq(&self) -> i32 { + unsafe { ffi::SCIPsepaGetFreq(self.raw) } + } + + /// Set the frequency of the separator. + pub fn set_freq(&mut self, freq: i32) { + unsafe { ffi::SCIPsepaSetFreq(self.raw, freq) } + } + + /// Returns the maxbounddist of the separator. + pub fn maxbounddist(&self) -> f64 { + unsafe { ffi::SCIPsepaGetMaxbounddist(self.raw) } + } + + /// Returns whether the separator is delayed. + pub fn is_delayed(&self) -> bool { + (unsafe { ffi::SCIPsepaIsDelayed(self.raw) }) != 0 + } + + /// Creates an empty LP row. + pub fn create_empty_row( + &self, + model: Model, + name: &str, + lhs: f64, + rhs: f64, + local: bool, + modifiable: bool, + removable: bool, + ) -> Result { + let name = std::ffi::CString::new(name).unwrap(); + let local = if local { 1 } else { 0 }; + let modifiable = if modifiable { 1 } else { 0 }; + let removable = if removable { 1 } else { 0 }; + + let mut row: *mut SCIP_ROW = std::ptr::null_mut(); + scip_call! { ffi::SCIPcreateEmptyRowSepa(model.scip.raw, &mut row, self.raw, name.as_ptr(), lhs, rhs, local, modifiable, removable) } + + Ok(Row { + raw: row, + scip: model.scip.clone(), + }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -68,7 +150,7 @@ mod tests { struct NotRunningSeparator; impl Separator for NotRunningSeparator { - fn execute_lp(&mut self, _model: Model) -> SeparationResult { + fn execute_lp(&mut self, _model: Model, _sepa: SCIPSeparator) -> SeparationResult { SeparationResult::DidNotRun } } @@ -102,7 +184,11 @@ mod tests { struct ConsAddingSeparator {} impl Separator for ConsAddingSeparator { - fn execute_lp(&mut self, mut model: Model) -> SeparationResult { + fn execute_lp( + &mut self, + mut model: Model, + _sepa: SCIPSeparator, + ) -> SeparationResult { // adds a row representing the sum of all variables >= 1 let vars = model.vars(); let varlen = vars.len(); @@ -140,4 +226,55 @@ mod tests { assert_eq!(solved.status(), crate::Status::Infeasible); } + + struct InternalSeparatorDataTester; + + impl Separator for InternalSeparatorDataTester { + fn execute_lp(&mut self, model: Model, sep: SCIPSeparator) -> SeparationResult { + assert_eq!(sep.name(), "InternalSeparatorDataTester"); + assert_eq!(sep.desc(), "Internal separator data tester"); + assert_eq!(sep.priority(), 1000000); + assert_eq!(sep.freq(), 1); + assert_eq!(sep.maxbounddist(), 1.0); + assert!(!sep.is_delayed()); + let row = sep + .create_empty_row(model, "test", 0.0, 1.0, true, false, false) + .unwrap(); + assert_eq!(row.name(), "test"); + assert_eq!(row.lhs(), 0.0); + assert_eq!(row.rhs(), 1.0); + assert_eq!(row.n_non_zeroes(), 0); + assert!(!row.is_modifiable()); + assert!(!row.is_removable()); + assert!(row.is_local()); + + SeparationResult::DidNotRun + } + } + + #[test] + fn test_internal_scip_separator() { + let model = Model::new() + .hide_output() + .set_longint_param("limits/nodes", 2) + .unwrap() // only call brancher once + .include_default_plugins() + .read_prob("data/test/gen-ip054.mps") + .unwrap(); + + let sep = InternalSeparatorDataTester; + + model + .include_separator( + "InternalSeparatorDataTester", + "Internal separator data tester", + 1000000, + 1, + 1.0, + false, + false, + Box::new(sep), + ) + .solve(); + } } From b87a799086f1a1ae064bc43adb21e798a7eb41ad Mon Sep 17 00:00:00 2001 From: Mohammed Ghannam Date: Thu, 9 Jan 2025 14:47:22 +0100 Subject: [PATCH 2/3] Add access to plugins internal data --- src/branchrule.rs | 80 ++++++++++++++++++++++++++++++++++++++--- src/col.rs | 8 ++--- src/eventhdlr.rs | 71 +++++++++++++++++++++++++++++++++--- src/pricer.rs | 92 +++++++++++++++++++++++++++++++++++++++++++---- src/row.rs | 5 +-- src/scip.rs | 21 ++++++----- 6 files changed, 248 insertions(+), 29 deletions(-) diff --git a/src/branchrule.rs b/src/branchrule.rs index 8a8c6d5..7106dc6 100644 --- a/src/branchrule.rs +++ b/src/branchrule.rs @@ -63,6 +63,46 @@ pub struct BranchingCandidate { pub frac: f64, } +/// A wrapper struct for the internal ffi::SCIP_BRANCHRULE +pub struct SCIPBranchRule { + pub(crate) raw: *mut ffi::SCIP_BRANCHRULE, +} + +impl SCIPBranchRule { + /// Returns the name of the branch rule. + pub fn name(&self) -> String { + unsafe { + let name_ptr = ffi::SCIPbranchruleGetName(self.raw); + let name = std::ffi::CStr::from_ptr(name_ptr).to_str().unwrap(); + name.to_string() + } + } + + /// Returns the description of the branch rule. + pub fn desc(&self) -> String { + unsafe { + let desc_ptr = ffi::SCIPbranchruleGetDesc(self.raw); + let desc = std::ffi::CStr::from_ptr(desc_ptr).to_str().unwrap(); + desc.to_string() + } + } + + /// Returns the priority of the branch rule. + pub fn priority(&self) -> i32 { + unsafe { ffi::SCIPbranchruleGetPriority(self.raw) } + } + + /// Returns the maxdepth of the branch rule. + pub fn maxdepth(&self) -> i32 { + unsafe { ffi::SCIPbranchruleGetMaxdepth(self.raw) } + } + + /// Returns the maxbounddist of the branch rule. + pub fn maxbounddist(&self) -> f64 { + unsafe { ffi::SCIPbranchruleGetMaxbounddist(self.raw) } + } +} + #[cfg(test)] mod tests { use super::*; @@ -99,10 +139,6 @@ mod tests { let solved = model.solve(); assert_eq!(solved.status(), Status::NodeLimit); - // assert!(br.chosen.is_some()); - // let candidate = br.chosen.unwrap(); - // assert!(candidate.lp_sol_val.fract() > 0.); - // assert!(candidate.frac > 0. && candidate.frac < 1.); } struct CuttingOffBranchingRule; @@ -238,4 +274,40 @@ mod tests { assert!(solved.n_nodes() > 1); } + + struct InternalBranchRuleDataTester; + + impl BranchRule for InternalBranchRuleDataTester { + fn execute( + &mut self, + _model: Model, + _candidates: Vec, + ) -> BranchingResult { + BranchingResult::DidNotRun + } + } + + #[test] + fn test_internal_scip_branch_rule() { + let model = Model::new() + .hide_output() + .set_longint_param("limits/nodes", 2) + .unwrap() // only call brancher once + .include_default_plugins() + .read_prob("data/test/gen-ip054.mps") + .unwrap(); + + let br = InternalBranchRuleDataTester; + + model + .include_branch_rule( + "InternalBranchRuleDataTester", + "Internal branch rule data tester", + 1000000, + 1, + 1.0, + Box::new(br), + ) + .solve(); + } } diff --git a/src/col.rs b/src/col.rs index ccf6c16..f04cd3f 100644 --- a/src/col.rs +++ b/src/col.rs @@ -192,10 +192,7 @@ impl PartialEq for Col { #[cfg(test)] mod tests { - use crate::{ - minimal_model, BasisStatus, EventMask, Eventhdlr, Model, ModelWithProblem, - ProblemOrSolving, Solving, VarType, - }; + use crate::{minimal_model, BasisStatus, Event, EventMask, Eventhdlr, Model, ModelWithProblem, ProblemOrSolving, SCIPEventhdlr, Solving, VarType}; struct ColTesterEventHandler; @@ -204,7 +201,8 @@ mod tests { EventMask::FIRST_LP_SOLVED } - fn execute(&mut self, model: Model) { + fn execute(&mut self, model: Model, _eventhdlr: SCIPEventhdlr, event: Event) { + assert_eq!(event.event_type(), EventMask::FIRST_LP_SOLVED); let vars = model.vars(); let first_var = vars[0].clone(); let col = first_var.col().unwrap(); diff --git a/src/eventhdlr.rs b/src/eventhdlr.rs index efaa32b..789be8e 100644 --- a/src/eventhdlr.rs +++ b/src/eventhdlr.rs @@ -1,4 +1,4 @@ -use crate::{Model, Solving}; +use crate::{ffi, Model, Solving}; use std::ops::{BitOr, BitOrAssign}; /// Trait used to define custom event handlers. @@ -10,11 +10,14 @@ pub trait Eventhdlr { /// /// # Arguments /// * `model` - the current model of the SCIP instance in `Solving` stage - fn execute(&mut self, model: Model); + /// * `eventhdlr` - the event handler + /// * `event` - the event mask that triggered the event handler + fn execute(&mut self, model: Model, eventhdlr: SCIPEventhdlr, event: Event); } /// The EventMask represents different states or actions within an optimization problem. #[derive(Debug, Copy, Clone)] +#[derive(PartialEq)] pub struct EventMask(u64); impl EventMask { @@ -147,6 +150,11 @@ impl EventMask { | Self::ROW_DELETED_LP.0 | Self::ROW_CHANGED.0, ); + + /// Event mask matches some other mask + pub fn matches(&self, mask: EventMask) -> bool { + self.0 & mask.0 != 0 + } } impl BitOr for EventMask { @@ -169,11 +177,41 @@ impl From for u64 { } } +/// Wrapper for the internal SCIP event handler. +pub struct SCIPEventhdlr { + pub(crate) raw: *mut ffi::SCIP_EVENTHDLR, +} + +impl SCIPEventhdlr { + /// Returns the name of the event handler. + pub fn name(&self) -> String { + unsafe { + let name = ffi::SCIPeventhdlrGetName(self.raw); + std::ffi::CStr::from_ptr(name) + .to_string_lossy() + .into_owned() + } + } +} + +/// Wrapper for the internal SCIP event. +pub struct Event { + pub(crate) raw: *mut ffi::SCIP_EVENT, +} + +impl Event { + /// Returns the event type of the event. + pub fn event_type(&self) -> EventMask { + let event_type = unsafe { ffi::SCIPeventGetType(self.raw) }; + EventMask(event_type) + } +} + #[cfg(test)] mod tests { use crate::eventhdlr::{EventMask, Eventhdlr}; use crate::model::Model; - use crate::Solving; + use crate::{Event, Solving}; use std::cell::RefCell; use std::rc::Rc; @@ -186,7 +224,7 @@ mod tests { EventMask::LP_EVENT | EventMask::NODE_EVENT } - fn execute(&mut self, _model: Model) { + fn execute(&mut self, _model: Model, _eventhdlr: crate::SCIPEventhdlr, _event: Event) { *self.counter.borrow_mut() += 1; } } @@ -208,4 +246,29 @@ mod tests { assert!(*counter.borrow() > 1); } + + + struct InternalSCIPEventHdlrTester; + + impl Eventhdlr for InternalSCIPEventHdlrTester { + fn get_type(&self) -> EventMask { + EventMask::LP_EVENT | EventMask::NODE_EVENT + } + + fn execute(&mut self, _model: Model, eventhdlr: crate::SCIPEventhdlr, event: Event) { + assert!(self.get_type().matches(event.event_type())); + assert_eq!(eventhdlr.name(), "InternalSCIPEventHdlrTester"); + } + } + + #[test] + fn test_internal_eventhdlr() { + Model::new() + .hide_output() + .include_default_plugins() + .read_prob("data/test/simple.lp") + .unwrap() + .include_eventhdlr("InternalSCIPEventHdlrTester", "", Box::new(InternalSCIPEventHdlrTester)) + .solve(); + } } diff --git a/src/pricer.rs b/src/pricer.rs index f75e47b..3ebdb7c 100644 --- a/src/pricer.rs +++ b/src/pricer.rs @@ -6,9 +6,10 @@ pub trait Pricer { /// Generates negative reduced cost columns. /// /// # Arguments - /// * `model` - the current model of the SCIP instance in `Solving` stage + /// * `model`: the current model of the SCIP instance in `Solving` stage. + /// * `pricer`: the internal pricer object. /// * `farkas`: If true, the pricer should generate columns to repair feasibility of LP. - fn generate_columns(&mut self, model: Model, farkas: bool) -> PricerResult; + fn generate_columns(&mut self, model: Model, pricer: SCIPPricer, farkas: bool) -> PricerResult; } /// An enum representing the possible states of a `PricerResult`. @@ -44,6 +45,55 @@ impl From for SCIP_Result { } } + +/// A wrapper around a SCIP pricer object. +pub struct SCIPPricer { + pub(crate) raw: *mut ffi::SCIP_PRICER, +} + +impl SCIPPricer { + /// Returns the name of the pricer. + pub fn name(&self) -> String { + unsafe { + let name = ffi::SCIPpricerGetName(self.raw); + std::ffi::CStr::from_ptr(name) + .to_string_lossy() + .into_owned() + } + } + + /// Returns the description of the pricer. + pub fn desc(&self) -> String { + unsafe { + let desc = ffi::SCIPpricerGetDesc(self.raw); + std::ffi::CStr::from_ptr(desc) + .to_string_lossy() + .into_owned() + } + } + + /// Returns the priority of the pricer. + pub fn priority(&self) -> i32 { + unsafe { + ffi::SCIPpricerGetPriority(self.raw) + } + } + + /// Returns the delay of the pricer. + pub fn is_delayed(&self) -> bool { + unsafe { + ffi::SCIPpricerIsDelayed(self.raw) != 0 + } + } + + /// Returns whether the pricer is active. + pub fn is_active(&self) -> bool { + unsafe { + ffi::SCIPpricerIsActive(self.raw) != 0 + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -55,7 +105,7 @@ mod tests { struct LyingPricer; impl Pricer for LyingPricer { - fn generate_columns(&mut self, _model: Model, _farkas: bool) -> PricerResult { + fn generate_columns(&mut self, _model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { PricerResult { state: PricerResultState::FoundColumns, lower_bound: None, @@ -81,7 +131,7 @@ mod tests { struct EarlyStoppingPricer; impl Pricer for EarlyStoppingPricer { - fn generate_columns(&mut self, _model: Model, _farkas: bool) -> PricerResult { + fn generate_columns(&mut self, _model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { PricerResult { state: PricerResultState::StopEarly, lower_bound: None, @@ -108,7 +158,7 @@ mod tests { struct OptimalPricer; impl Pricer for OptimalPricer { - fn generate_columns(&mut self, _model: Model, _farkas: bool) -> PricerResult { + fn generate_columns(&mut self, _model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { PricerResult { state: PricerResultState::NoColumns, lower_bound: None, @@ -144,7 +194,7 @@ mod tests { } impl Pricer for AddSameColumnPricer { - fn generate_columns(&mut self, mut model: Model, _farkas: bool) -> PricerResult { + fn generate_columns(&mut self, mut model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { assert_eq!(self.data.a, (0..1000).collect::>()); if self.added { PricerResult { @@ -196,4 +246,34 @@ mod tests { .solve(); assert_eq!(solved.status(), Status::Optimal); } + + struct InternalSCIPPricerTester; + + impl Pricer for InternalSCIPPricerTester { + fn generate_columns(&mut self, _model: Model, pricer: SCIPPricer, _farkas: bool) -> PricerResult { + assert_eq!(pricer.name(), "internal"); + assert_eq!(pricer.desc(), "internal pricer"); + assert_eq!(pricer.priority(), 100); + assert!(!pricer.is_delayed()); + assert!(pricer.is_active()); + PricerResult { + state: PricerResultState::NoColumns, + lower_bound: None, + } + } + } + + #[test] + fn internal_pricer() { + let pricer = InternalSCIPPricerTester {}; + + let model = crate::model::Model::new() + .hide_output() + .include_default_plugins() + .read_prob("data/test/simple.lp") + .unwrap() + .include_pricer("internal", "internal pricer", 100, false, Box::new(pricer)); + + model.solve(); + } } diff --git a/src/row.rs b/src/row.rs index ce9b7eb..76288b6 100644 --- a/src/row.rs +++ b/src/row.rs @@ -225,7 +225,8 @@ impl From for RowOrigin { #[cfg(test)] mod tests { - use crate::{ + use crate::Event; +use crate::{ minimal_model, EventMask, Eventhdlr, HasScipPtr, Model, ModelWithProblem, ProblemOrSolving, Solving, VarType, }; @@ -237,7 +238,7 @@ mod tests { EventMask::FIRST_LP_SOLVED } - fn execute(&mut self, model: Model) { + fn execute(&mut self, model: Model, _eventhdlr: crate::SCIPEventhdlr, _event: Event) { let first_cons = model.conss()[0].clone(); let row = first_cons.row().unwrap(); assert_eq!(row.n_non_zeroes(), 1); diff --git a/src/scip.rs b/src/scip.rs index 50b3139..375976f 100644 --- a/src/scip.rs +++ b/src/scip.rs @@ -1,10 +1,6 @@ use crate::branchrule::{BranchRule, BranchingCandidate}; use crate::pricer::{Pricer, PricerResultState}; -use crate::{ - ffi, scip_call_panic, BranchingResult, Constraint, Eventhdlr, HeurResult, Model, Node, - ObjSense, ParamSetting, Retcode, SCIPSeparator, Separator, Solution, Solving, Status, VarType, - Variable, -}; +use crate::{ffi, scip_call_panic, BranchingResult, Constraint, Event, Eventhdlr, HeurResult, Model, Node, ObjSense, ParamSetting, Retcode, SCIPEventhdlr, SCIPPricer, SCIPSeparator, Separator, Solution, Solving, Status, VarType, Variable}; use crate::{scip_call, HeurTiming, Heuristic}; use core::panic; use scip_sys::{SCIP_Cons, SCIP_Var, Scip, SCIP_SOL}; @@ -593,7 +589,7 @@ impl ScipPtr { extern "C" fn eventhdlrexec( scip: *mut ffi::SCIP, eventhdlr: *mut ffi::SCIP_EVENTHDLR, - _event: *mut ffi::SCIP_EVENT, + event: *mut ffi::SCIP_EVENT, _event_data: *mut ffi::SCIP_EVENTDATA, ) -> ffi::SCIP_Retcode { let data_ptr = unsafe { ffi::SCIPeventhdlrGetData(eventhdlr) }; @@ -604,7 +600,13 @@ impl ScipPtr { scip: Rc::new(scip_ptr), state: Solving, }; - unsafe { (*eventhdlr_ptr).execute(model) }; + let eventhdlr = SCIPEventhdlr { + raw: eventhdlr, + }; + let event = Event { + raw: event, + }; + unsafe { (*eventhdlr_ptr).execute(model, eventhdlr, event) }; Retcode::Okay.into() } @@ -783,7 +785,10 @@ impl ScipPtr { state: Solving, }; - let pricing_res = unsafe { (*pricer_ptr).generate_columns(model, farkas) }; + let pricer = SCIPPricer { + raw: pricer, + }; + let pricing_res = unsafe { (*pricer_ptr).generate_columns(model, pricer, farkas) }; if !farkas { if let Some(lb) = pricing_res.lower_bound { From d77571c298efb67cf9d47006c2a53bd627242a40 Mon Sep 17 00:00:00 2001 From: Mohammed Ghannam Date: Thu, 9 Jan 2025 14:47:54 +0100 Subject: [PATCH 3/3] Cargo fmt --- src/col.rs | 5 +++- src/eventhdlr.rs | 24 +++++++++++++++----- src/pricer.rs | 59 +++++++++++++++++++++++++++++++++--------------- src/row.rs | 9 ++++++-- src/scip.rs | 18 +++++++-------- 5 files changed, 78 insertions(+), 37 deletions(-) diff --git a/src/col.rs b/src/col.rs index f04cd3f..bb6f6ce 100644 --- a/src/col.rs +++ b/src/col.rs @@ -192,7 +192,10 @@ impl PartialEq for Col { #[cfg(test)] mod tests { - use crate::{minimal_model, BasisStatus, Event, EventMask, Eventhdlr, Model, ModelWithProblem, ProblemOrSolving, SCIPEventhdlr, Solving, VarType}; + use crate::{ + minimal_model, BasisStatus, Event, EventMask, Eventhdlr, Model, ModelWithProblem, + ProblemOrSolving, SCIPEventhdlr, Solving, VarType, + }; struct ColTesterEventHandler; diff --git a/src/eventhdlr.rs b/src/eventhdlr.rs index 789be8e..b5b9bff 100644 --- a/src/eventhdlr.rs +++ b/src/eventhdlr.rs @@ -16,8 +16,7 @@ pub trait Eventhdlr { } /// The EventMask represents different states or actions within an optimization problem. -#[derive(Debug, Copy, Clone)] -#[derive(PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct EventMask(u64); impl EventMask { @@ -224,7 +223,12 @@ mod tests { EventMask::LP_EVENT | EventMask::NODE_EVENT } - fn execute(&mut self, _model: Model, _eventhdlr: crate::SCIPEventhdlr, _event: Event) { + fn execute( + &mut self, + _model: Model, + _eventhdlr: crate::SCIPEventhdlr, + _event: Event, + ) { *self.counter.borrow_mut() += 1; } } @@ -247,7 +251,6 @@ mod tests { assert!(*counter.borrow() > 1); } - struct InternalSCIPEventHdlrTester; impl Eventhdlr for InternalSCIPEventHdlrTester { @@ -255,7 +258,12 @@ mod tests { EventMask::LP_EVENT | EventMask::NODE_EVENT } - fn execute(&mut self, _model: Model, eventhdlr: crate::SCIPEventhdlr, event: Event) { + fn execute( + &mut self, + _model: Model, + eventhdlr: crate::SCIPEventhdlr, + event: Event, + ) { assert!(self.get_type().matches(event.event_type())); assert_eq!(eventhdlr.name(), "InternalSCIPEventHdlrTester"); } @@ -268,7 +276,11 @@ mod tests { .include_default_plugins() .read_prob("data/test/simple.lp") .unwrap() - .include_eventhdlr("InternalSCIPEventHdlrTester", "", Box::new(InternalSCIPEventHdlrTester)) + .include_eventhdlr( + "InternalSCIPEventHdlrTester", + "", + Box::new(InternalSCIPEventHdlrTester), + ) .solve(); } } diff --git a/src/pricer.rs b/src/pricer.rs index 3ebdb7c..f176130 100644 --- a/src/pricer.rs +++ b/src/pricer.rs @@ -7,9 +7,14 @@ pub trait Pricer { /// /// # Arguments /// * `model`: the current model of the SCIP instance in `Solving` stage. - /// * `pricer`: the internal pricer object. + /// * `pricer`: the internal pricer object. /// * `farkas`: If true, the pricer should generate columns to repair feasibility of LP. - fn generate_columns(&mut self, model: Model, pricer: SCIPPricer, farkas: bool) -> PricerResult; + fn generate_columns( + &mut self, + model: Model, + pricer: SCIPPricer, + farkas: bool, + ) -> PricerResult; } /// An enum representing the possible states of a `PricerResult`. @@ -45,7 +50,6 @@ impl From for SCIP_Result { } } - /// A wrapper around a SCIP pricer object. pub struct SCIPPricer { pub(crate) raw: *mut ffi::SCIP_PRICER, @@ -74,23 +78,17 @@ impl SCIPPricer { /// Returns the priority of the pricer. pub fn priority(&self) -> i32 { - unsafe { - ffi::SCIPpricerGetPriority(self.raw) - } + unsafe { ffi::SCIPpricerGetPriority(self.raw) } } /// Returns the delay of the pricer. pub fn is_delayed(&self) -> bool { - unsafe { - ffi::SCIPpricerIsDelayed(self.raw) != 0 - } + unsafe { ffi::SCIPpricerIsDelayed(self.raw) != 0 } } /// Returns whether the pricer is active. pub fn is_active(&self) -> bool { - unsafe { - ffi::SCIPpricerIsActive(self.raw) != 0 - } + unsafe { ffi::SCIPpricerIsActive(self.raw) != 0 } } } @@ -105,7 +103,12 @@ mod tests { struct LyingPricer; impl Pricer for LyingPricer { - fn generate_columns(&mut self, _model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { + fn generate_columns( + &mut self, + _model: Model, + _pricer: SCIPPricer, + _farkas: bool, + ) -> PricerResult { PricerResult { state: PricerResultState::FoundColumns, lower_bound: None, @@ -131,7 +134,12 @@ mod tests { struct EarlyStoppingPricer; impl Pricer for EarlyStoppingPricer { - fn generate_columns(&mut self, _model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { + fn generate_columns( + &mut self, + _model: Model, + _pricer: SCIPPricer, + _farkas: bool, + ) -> PricerResult { PricerResult { state: PricerResultState::StopEarly, lower_bound: None, @@ -158,7 +166,12 @@ mod tests { struct OptimalPricer; impl Pricer for OptimalPricer { - fn generate_columns(&mut self, _model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { + fn generate_columns( + &mut self, + _model: Model, + _pricer: SCIPPricer, + _farkas: bool, + ) -> PricerResult { PricerResult { state: PricerResultState::NoColumns, lower_bound: None, @@ -194,7 +207,12 @@ mod tests { } impl Pricer for AddSameColumnPricer { - fn generate_columns(&mut self, mut model: Model, _pricer: SCIPPricer, _farkas: bool) -> PricerResult { + fn generate_columns( + &mut self, + mut model: Model, + _pricer: SCIPPricer, + _farkas: bool, + ) -> PricerResult { assert_eq!(self.data.a, (0..1000).collect::>()); if self.added { PricerResult { @@ -250,7 +268,12 @@ mod tests { struct InternalSCIPPricerTester; impl Pricer for InternalSCIPPricerTester { - fn generate_columns(&mut self, _model: Model, pricer: SCIPPricer, _farkas: bool) -> PricerResult { + fn generate_columns( + &mut self, + _model: Model, + pricer: SCIPPricer, + _farkas: bool, + ) -> PricerResult { assert_eq!(pricer.name(), "internal"); assert_eq!(pricer.desc(), "internal pricer"); assert_eq!(pricer.priority(), 100); @@ -262,7 +285,7 @@ mod tests { } } } - + #[test] fn internal_pricer() { let pricer = InternalSCIPPricerTester {}; diff --git a/src/row.rs b/src/row.rs index 76288b6..cb0c660 100644 --- a/src/row.rs +++ b/src/row.rs @@ -226,7 +226,7 @@ impl From for RowOrigin { #[cfg(test)] mod tests { use crate::Event; -use crate::{ + use crate::{ minimal_model, EventMask, Eventhdlr, HasScipPtr, Model, ModelWithProblem, ProblemOrSolving, Solving, VarType, }; @@ -238,7 +238,12 @@ use crate::{ EventMask::FIRST_LP_SOLVED } - fn execute(&mut self, model: Model, _eventhdlr: crate::SCIPEventhdlr, _event: Event) { + fn execute( + &mut self, + model: Model, + _eventhdlr: crate::SCIPEventhdlr, + _event: Event, + ) { let first_cons = model.conss()[0].clone(); let row = first_cons.row().unwrap(); assert_eq!(row.n_non_zeroes(), 1); diff --git a/src/scip.rs b/src/scip.rs index 375976f..0301a04 100644 --- a/src/scip.rs +++ b/src/scip.rs @@ -1,6 +1,10 @@ use crate::branchrule::{BranchRule, BranchingCandidate}; use crate::pricer::{Pricer, PricerResultState}; -use crate::{ffi, scip_call_panic, BranchingResult, Constraint, Event, Eventhdlr, HeurResult, Model, Node, ObjSense, ParamSetting, Retcode, SCIPEventhdlr, SCIPPricer, SCIPSeparator, Separator, Solution, Solving, Status, VarType, Variable}; +use crate::{ + ffi, scip_call_panic, BranchingResult, Constraint, Event, Eventhdlr, HeurResult, Model, Node, + ObjSense, ParamSetting, Retcode, SCIPEventhdlr, SCIPPricer, SCIPSeparator, Separator, Solution, + Solving, Status, VarType, Variable, +}; use crate::{scip_call, HeurTiming, Heuristic}; use core::panic; use scip_sys::{SCIP_Cons, SCIP_Var, Scip, SCIP_SOL}; @@ -600,12 +604,8 @@ impl ScipPtr { scip: Rc::new(scip_ptr), state: Solving, }; - let eventhdlr = SCIPEventhdlr { - raw: eventhdlr, - }; - let event = Event { - raw: event, - }; + let eventhdlr = SCIPEventhdlr { raw: eventhdlr }; + let event = Event { raw: event }; unsafe { (*eventhdlr_ptr).execute(model, eventhdlr, event) }; Retcode::Okay.into() } @@ -785,9 +785,7 @@ impl ScipPtr { state: Solving, }; - let pricer = SCIPPricer { - raw: pricer, - }; + let pricer = SCIPPricer { raw: pricer }; let pricing_res = unsafe { (*pricer_ptr).generate_columns(model, pricer, farkas) }; if !farkas {