Skip to content

Commit

Permalink
Merge pull request #181 from scipopt/internal-plugin-wrappers
Browse files Browse the repository at this point in the history
Add access to internal scip separator in callbacks
  • Loading branch information
mmghannam authored Jan 9, 2025
2 parents c620bbf + d77571c commit edbaf49
Show file tree
Hide file tree
Showing 7 changed files with 429 additions and 30 deletions.
80 changes: 76 additions & 4 deletions src/branchrule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -238,4 +274,40 @@ mod tests {

assert!(solved.n_nodes() > 1);
}

struct InternalBranchRuleDataTester;

impl BranchRule for InternalBranchRuleDataTester {
fn execute(
&mut self,
_model: Model<Solving>,
_candidates: Vec<BranchingCandidate>,
) -> 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();
}
}
7 changes: 4 additions & 3 deletions src/col.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ impl PartialEq for Col {
#[cfg(test)]
mod tests {
use crate::{
minimal_model, BasisStatus, EventMask, Eventhdlr, Model, ModelWithProblem,
ProblemOrSolving, Solving, VarType,
minimal_model, BasisStatus, Event, EventMask, Eventhdlr, Model, ModelWithProblem,
ProblemOrSolving, SCIPEventhdlr, Solving, VarType,
};

struct ColTesterEventHandler;
Expand All @@ -204,7 +204,8 @@ mod tests {
EventMask::FIRST_LP_SOLVED
}

fn execute(&mut self, model: Model<Solving>) {
fn execute(&mut self, model: Model<Solving>, _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();
Expand Down
85 changes: 80 additions & 5 deletions src/eventhdlr.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -10,11 +10,13 @@ pub trait Eventhdlr {
///
/// # Arguments
/// * `model` - the current model of the SCIP instance in `Solving` stage
fn execute(&mut self, model: Model<Solving>);
/// * `eventhdlr` - the event handler
/// * `event` - the event mask that triggered the event handler
fn execute(&mut self, model: Model<Solving>, eventhdlr: SCIPEventhdlr, event: Event);
}

/// The EventMask represents different states or actions within an optimization problem.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct EventMask(u64);

impl EventMask {
Expand Down Expand Up @@ -147,6 +149,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 {
Expand All @@ -169,11 +176,41 @@ impl From<EventMask> 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;

Expand All @@ -186,7 +223,12 @@ mod tests {
EventMask::LP_EVENT | EventMask::NODE_EVENT
}

fn execute(&mut self, _model: Model<Solving>) {
fn execute(
&mut self,
_model: Model<Solving>,
_eventhdlr: crate::SCIPEventhdlr,
_event: Event,
) {
*self.counter.borrow_mut() += 1;
}
}
Expand All @@ -208,4 +250,37 @@ 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<Solving>,
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();
}
}
Loading

0 comments on commit edbaf49

Please sign in to comment.