use crate::ffi;
use crate::variable::Variable;
use scip_sys::SCIP_Result;
use std::rc::Rc;
pub trait BranchRule {
fn execute(&mut self, candidates: Vec<BranchingCandidate>) -> BranchingResult;
}
#[derive(Debug, Clone, PartialEq)]
pub enum BranchingResult {
DidNotRun,
BranchOn(BranchingCandidate),
CutOff,
CustomBranching,
Separated,
ReduceDom,
ConsAdded,
}
impl From<BranchingResult> for SCIP_Result {
fn from(val: BranchingResult) -> Self {
match val {
BranchingResult::DidNotRun => ffi::SCIP_Result_SCIP_DIDNOTRUN,
BranchingResult::BranchOn(_) => ffi::SCIP_Result_SCIP_BRANCHED,
BranchingResult::CutOff => ffi::SCIP_Result_SCIP_CUTOFF,
BranchingResult::CustomBranching => ffi::SCIP_Result_SCIP_BRANCHED,
BranchingResult::Separated => ffi::SCIP_Result_SCIP_SEPARATED,
BranchingResult::ReduceDom => ffi::SCIP_Result_SCIP_REDUCEDDOM,
BranchingResult::ConsAdded => ffi::SCIP_Result_SCIP_CONSADDED,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BranchingCandidate {
pub var: Rc<Variable>,
pub lp_sol_val: f64,
pub frac: f64,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::ModelWithProblem;
use crate::Solving;
use crate::{model::Model, status::Status};
struct PanickingBranchingRule;
impl BranchRule for PanickingBranchingRule {
fn execute(&mut self, _candidates: Vec<BranchingCandidate>) -> BranchingResult {
panic!("Not implemented")
}
}
#[test]
#[should_panic]
fn panicking_branchrule() {
let br = PanickingBranchingRule {};
Model::new()
.hide_output()
.include_default_plugins()
.read_prob("data/test/gen-ip054.mps")
.unwrap()
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
.solve();
}
struct FirstChoosingBranchingRule {
pub chosen: Option<BranchingCandidate>,
}
impl BranchRule for FirstChoosingBranchingRule {
fn execute(&mut self, candidates: Vec<BranchingCandidate>) -> BranchingResult {
self.chosen = Some(candidates[0].clone());
BranchingResult::DidNotRun
}
}
#[test]
fn choosing_first_branching_rule() {
let br = FirstChoosingBranchingRule { chosen: None };
let model = Model::new()
.set_longint_param("limits/nodes", 2) .unwrap()
.hide_output()
.include_default_plugins()
.read_prob("data/test/gen-ip054.mps")
.unwrap()
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br));
let solved = model.solve();
assert_eq!(solved.status(), Status::NodeLimit);
}
struct CuttingOffBranchingRule;
impl BranchRule for CuttingOffBranchingRule {
fn execute(&mut self, _candidates: Vec<BranchingCandidate>) -> BranchingResult {
BranchingResult::CutOff
}
}
#[test]
fn cutting_off_branching_rule() {
let br = CuttingOffBranchingRule {};
let model = Model::new()
.hide_output()
.include_default_plugins()
.read_prob("data/test/gen-ip054.mps")
.unwrap()
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
.solve();
assert_eq!(model.n_nodes(), 1);
}
struct FirstBranchingRule {
model: Model<Solving>,
}
impl BranchRule for FirstBranchingRule {
fn execute(&mut self, candidates: Vec<BranchingCandidate>) -> BranchingResult {
assert!(self.model.n_vars() >= candidates.len());
BranchingResult::BranchOn(candidates[0].clone())
}
}
#[test]
fn first_branching_rule() {
let model = Model::new()
.hide_output()
.set_longint_param("limits/nodes", 2)
.unwrap() .include_default_plugins()
.read_prob("data/test/gen-ip054.mps")
.unwrap();
let br = FirstBranchingRule {
model: model.clone_for_plugins(),
};
let solved = model
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
.solve();
assert!(solved.n_nodes() > 1);
}
struct CustomBranchingRule {
model: Model<Solving>,
}
impl BranchRule for CustomBranchingRule {
fn execute(&mut self, _candidates: Vec<BranchingCandidate>) -> BranchingResult {
self.model.create_child();
BranchingResult::CustomBranching
}
}
#[test]
fn custom_branching_rule() {
let model = Model::new()
.hide_output()
.set_longint_param("limits/nodes", 2)
.unwrap() .include_default_plugins()
.read_prob("data/test/gen-ip054.mps")
.unwrap();
let br = CustomBranchingRule {
model: model.clone_for_plugins(),
};
let solved = model
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
.solve();
assert!(solved.n_nodes() > 1);
}
}