use crate::scip::ScipPtr;
use crate::{Model, Solving, Variable, ffi};
use scip_sys::SCIPeventGetVar;
use std::ops::{BitOr, BitOrAssign};
use std::rc::Rc;
pub trait Eventhdlr {
fn get_type(&self) -> EventMask;
fn execute(&mut self, model: Model<Solving>, eventhdlr: SCIPEventhdlr, event: Event);
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct EventMask(u64);
impl EventMask {
pub const DISABLED: Self = EventMask(0x000000000);
pub const VAR_ADDED: Self = EventMask(0x000000001);
pub const VAR_DELETED: Self = EventMask(0x000000002);
pub const VAR_FIXED: Self = EventMask(0x000000004);
pub const VAR_UNLOCKED: Self = EventMask(0x000000008);
pub const OBJ_CHANGED: Self = EventMask(0x000000010);
pub const GLB_CHANGED: Self = EventMask(0x000000020);
pub const GUB_CHANGED: Self = EventMask(0x000000040);
pub const LB_TIGHTENED: Self = EventMask(0x000000080);
pub const LB_RELAXED: Self = EventMask(0x000000100);
pub const UB_TIGHTENED: Self = EventMask(0x000000200);
pub const UB_RELAXED: Self = EventMask(0x000000400);
pub const GHOLE_ADDED: Self = EventMask(0x000000800);
pub const GHOLE_REMOVED: Self = EventMask(0x000001000);
pub const LHOLE_ADDED: Self = EventMask(0x000002000);
pub const LHOLE_REMOVED: Self = EventMask(0x000004000);
pub const IMPL_ADDED: Self = EventMask(0x000008000);
pub const TYPE_CHANGED: Self = EventMask(0x000010000);
pub const IMPL_TYPE_CHANGED: Self = EventMask(0x000020000);
pub const PRESOLVE_ROUND: Self = EventMask(0x000040000);
pub const NODE_FOCUSED: Self = EventMask(0x000080000);
pub const NODE_FEASIBLE: Self = EventMask(0x000100000);
pub const NODE_INFEASIBLE: Self = EventMask(0x000200000);
pub const NODE_BRANCHED: Self = EventMask(0x000400000);
pub const NODE_DELETE: Self = EventMask(0x000800000);
pub const DUAL_BOUND_IMPROVED: Self = EventMask(0x001000000);
pub const FIRST_LP_SOLVED: Self = EventMask(0x002000000);
pub const LP_SOLVED: Self = EventMask(0x004000000);
pub const POOR_SOL_FOUND: Self = EventMask(0x008000000);
pub const BEST_SOL_FOUND: Self = EventMask(0x010000000);
pub const ROW_ADDED_SEPA: Self = EventMask(0x020000000);
pub const ROW_DELETED_SEPA: Self = EventMask(0x040000000);
pub const ROW_ADDED_LP: Self = EventMask(0x080000000);
pub const ROW_DELETED_LP: Self = EventMask(0x100000000);
pub const ROW_COEF_CHANGED: Self = EventMask(0x200000000);
pub const ROW_CONST_CHANGED: Self = EventMask(0x400000000);
pub const ROW_SIDE_CHANGED: Self = EventMask(0x800000000);
pub const SYNC: Self = EventMask(0x1000000000);
pub const GBD_CHANGED: Self = Self(Self::GLB_CHANGED.0 | Self::GUB_CHANGED.0);
pub const LB_CHANGED: Self = Self(Self::LB_TIGHTENED.0 | Self::LB_RELAXED.0);
pub const UB_CHANGED: Self = Self(Self::UB_TIGHTENED.0 | Self::UB_RELAXED.0);
pub const BOUND_TIGHTENED: Self = Self(Self::LB_TIGHTENED.0 | Self::UB_TIGHTENED.0);
pub const BOUND_RELAXED: Self = Self(Self::LB_RELAXED.0 | Self::UB_RELAXED.0);
pub const BOUND_CHANGED: Self = Self(Self::LB_CHANGED.0 | Self::UB_CHANGED.0);
pub const GHOLE_CHANGED: Self = Self(Self::GHOLE_ADDED.0 | Self::GHOLE_REMOVED.0);
pub const LHOLE_CHANGED: Self = Self(Self::LHOLE_ADDED.0 | Self::LHOLE_REMOVED.0);
pub const HOLE_CHANGED: Self = Self(Self::GHOLE_CHANGED.0 | Self::LHOLE_CHANGED.0);
pub const DOM_CHANGED: Self = Self(Self::BOUND_CHANGED.0 | Self::HOLE_CHANGED.0);
pub const VAR_CHANGED: Self = Self(
Self::VAR_FIXED.0
| Self::VAR_UNLOCKED.0
| Self::OBJ_CHANGED.0
| Self::GBD_CHANGED.0
| Self::DOM_CHANGED.0
| Self::IMPL_ADDED.0
| Self::VAR_DELETED.0
| Self::TYPE_CHANGED.0,
);
pub const VAR_EVENT: Self =
Self(Self::VAR_ADDED.0 | Self::VAR_CHANGED.0 | Self::TYPE_CHANGED.0);
pub const NODE_SOLVED: Self =
Self(Self::NODE_FEASIBLE.0 | Self::NODE_INFEASIBLE.0 | Self::NODE_BRANCHED.0);
pub const NODE_EVENT: Self = Self(Self::NODE_FOCUSED.0 | Self::NODE_SOLVED.0);
pub const LP_EVENT: Self = Self(Self::FIRST_LP_SOLVED.0 | Self::LP_SOLVED.0);
pub const SOL_FOUND: Self = Self(Self::POOR_SOL_FOUND.0 | Self::BEST_SOL_FOUND.0);
pub const SOL_EVENT: Self = Self(Self::SOL_FOUND.0);
pub const ROW_CHANGED: Self =
Self(Self::ROW_COEF_CHANGED.0 | Self::ROW_CONST_CHANGED.0 | Self::ROW_SIDE_CHANGED.0);
pub const ROW_EVENT: Self = Self(
Self::ROW_ADDED_SEPA.0
| Self::ROW_DELETED_SEPA.0
| Self::ROW_ADDED_LP.0
| Self::ROW_DELETED_LP.0
| Self::ROW_CHANGED.0,
);
pub fn matches(&self, mask: EventMask) -> bool {
self.0 & mask.0 != 0
}
}
impl BitOr for EventMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
EventMask(self.0 | rhs.0)
}
}
impl BitOrAssign for EventMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl From<EventMask> for u64 {
fn from(mask: EventMask) -> Self {
mask.0
}
}
pub struct SCIPEventhdlr {
pub(crate) raw: *mut ffi::SCIP_EVENTHDLR,
}
impl SCIPEventhdlr {
pub fn inner(&self) -> *mut ffi::SCIP_EVENTHDLR {
self.raw
}
pub fn name(&self) -> String {
unsafe {
let name = ffi::SCIPeventhdlrGetName(self.raw);
std::ffi::CStr::from_ptr(name)
.to_string_lossy()
.into_owned()
}
}
}
pub struct Event {
pub(crate) raw: *mut ffi::SCIP_EVENT,
pub(crate) scip: Rc<ScipPtr>,
}
impl Event {
pub fn inner(&self) -> *mut ffi::SCIP_EVENT {
self.raw
}
pub fn event_type(&self) -> EventMask {
let event_type = unsafe { ffi::SCIPeventGetType(self.raw) };
EventMask(event_type)
}
pub fn var(&self) -> Option<Variable> {
if self
.event_type()
.matches(EventMask::VAR_EVENT | EventMask::VAR_FIXED | EventMask::VAR_DELETED)
{
let var_ptr = unsafe { SCIPeventGetVar(self.raw) };
assert!(!var_ptr.is_null());
Some(Variable {
raw: var_ptr,
scip: self.scip.clone(),
})
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use crate::eventhdlr::{EventMask, Eventhdlr};
use crate::model::Model;
use crate::prelude::eventhdlr;
use crate::{Event, Solving};
use std::cell::RefCell;
use std::rc::Rc;
struct CountingEventHdlr {
counter: Rc<RefCell<usize>>,
}
impl Eventhdlr for CountingEventHdlr {
fn get_type(&self) -> EventMask {
EventMask::LP_EVENT | EventMask::NODE_EVENT
}
fn execute(
&mut self,
_model: Model<Solving>,
_eventhdlr: crate::SCIPEventhdlr,
_event: Event,
) {
*self.counter.borrow_mut() += 1;
}
}
#[test]
fn test_eventhdlr() {
let counter = Rc::new(RefCell::new(0));
let eh = CountingEventHdlr {
counter: counter.clone(),
};
let mut model = Model::new()
.hide_output()
.include_default_plugins()
.read_prob("data/test/simple.lp")
.unwrap();
model.add(eventhdlr(eh).name("CountingEventHdlr"));
model.solve();
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");
assert!(event.var().is_none())
}
}
#[test]
fn test_internal_eventhdlr() {
let mut model = Model::new()
.hide_output()
.include_default_plugins()
.read_prob("data/test/simple.lp")
.unwrap();
model.add(eventhdlr(InternalSCIPEventHdlrTester).name("InternalSCIPEventHdlrTester"));
model.solve();
}
}