Skip to content

Commit 30432f0

Browse files
authored
Merge pull request #208 from scipopt/plugin-builders
Ergonomic builders for plugins
2 parents 6c30ae9 + e63b279 commit 30432f0

16 files changed

+738
-253
lines changed

src/branchrule.rs

+21-42
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl SCIPBranchRule {
114114
mod tests {
115115
use super::*;
116116
use crate::model::ModelWithProblem;
117+
use crate::prelude::branchrule;
117118
use crate::{model::Model, status::Status, Solving};
118119

119120
struct FirstChoosingBranchingRule {
@@ -129,10 +130,6 @@ mod tests {
129130
) -> BranchingResult {
130131
self.chosen = Some(candidates[0].clone());
131132
assert_eq!(branchrule.name(), "FirstChoosingBranchingRule");
132-
assert_eq!(branchrule.desc(), "");
133-
assert_eq!(branchrule.priority(), 100000);
134-
assert_eq!(branchrule.maxdepth(), 1000);
135-
assert_eq!(branchrule.maxbounddist(), 1.0);
136133
BranchingResult::DidNotRun
137134
}
138135
}
@@ -141,21 +138,15 @@ mod tests {
141138
fn choosing_first_branching_rule() {
142139
let br = FirstChoosingBranchingRule { chosen: None };
143140

144-
let model = Model::new()
141+
let mut model = Model::new()
145142
.set_longint_param("limits/nodes", 2) // only call brancher once
146143
.unwrap()
147144
.hide_output()
148145
.include_default_plugins()
149146
.read_prob("data/test/gen-ip054.mps")
150-
.unwrap()
151-
.include_branch_rule(
152-
"FirstChoosingBranchingRule",
153-
"",
154-
100000,
155-
1000,
156-
1.,
157-
Box::new(br),
158-
);
147+
.unwrap();
148+
149+
model.add(branchrule(br).name("FirstChoosingBranchingRule"));
159150

160151
let solved = model.solve();
161152
assert_eq!(solved.status(), Status::NodeLimit);
@@ -179,13 +170,13 @@ mod tests {
179170
let br = CuttingOffBranchingRule {};
180171

181172
// create model from miplib instance gen-ip054
182-
let model = Model::new()
173+
let mut model = Model::new()
183174
.hide_output()
184175
.include_default_plugins()
185176
.read_prob("data/test/gen-ip054.mps")
186-
.unwrap()
187-
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
188-
.solve();
177+
.unwrap();
178+
model.add(branchrule(br).maxdepth(10));
179+
let model = model.solve();
189180
assert_eq!(model.n_nodes(), 1);
190181
}
191182

@@ -205,7 +196,7 @@ mod tests {
205196

206197
#[test]
207198
fn first_branching_rule() {
208-
let model = Model::new()
199+
let mut model = Model::new()
209200
.hide_output()
210201
.set_longint_param("limits/nodes", 2)
211202
.unwrap() // only call brancher once
@@ -214,9 +205,8 @@ mod tests {
214205
.unwrap();
215206

216207
let br = FirstBranchingRule;
217-
let solved = model
218-
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
219-
.solve();
208+
model.add(branchrule(br).name("FirstBranchingRule").maxdepth(1000));
209+
let solved = model.solve();
220210

221211
assert!(solved.n_nodes() > 1);
222212
}
@@ -237,7 +227,7 @@ mod tests {
237227

238228
#[test]
239229
fn custom_branching_rule() {
240-
let model = Model::new()
230+
let mut model = Model::new()
241231
.hide_output()
242232
.set_longint_param("limits/nodes", 2)
243233
.unwrap() // only call brancher once
@@ -246,9 +236,8 @@ mod tests {
246236
.unwrap();
247237

248238
let br = CustomBranchingRule;
249-
let solved = model
250-
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
251-
.solve();
239+
model.add(branchrule(br));
240+
let solved = model.solve();
252241

253242
assert!(solved.n_nodes() > 1);
254243
}
@@ -283,7 +272,7 @@ mod tests {
283272

284273
#[test]
285274
fn highest_bound_branch_rule() {
286-
let model = Model::new()
275+
let mut model = Model::new()
287276
.hide_output()
288277
.set_longint_param("limits/nodes", 2)
289278
.unwrap() // only call brancher once
@@ -292,9 +281,8 @@ mod tests {
292281
.unwrap();
293282

294283
let br = HighestBoundBranchRule;
295-
let solved = model
296-
.include_branch_rule("", "", 100000, 1000, 1., Box::new(br))
297-
.solve();
284+
model.add(branchrule(br));
285+
let solved = model.solve();
298286

299287
assert!(solved.n_nodes() > 1);
300288
}
@@ -314,7 +302,7 @@ mod tests {
314302

315303
#[test]
316304
fn test_internal_scip_branch_rule() {
317-
let model = Model::new()
305+
let mut model = Model::new()
318306
.hide_output()
319307
.set_longint_param("limits/nodes", 2)
320308
.unwrap() // only call brancher once
@@ -323,16 +311,7 @@ mod tests {
323311
.unwrap();
324312

325313
let br = InternalBranchRuleDataTester;
326-
327-
model
328-
.include_branch_rule(
329-
"InternalBranchRuleDataTester",
330-
"Internal branch rule data tester",
331-
1000000,
332-
1,
333-
1.0,
334-
Box::new(br),
335-
)
336-
.solve();
314+
model.add(branchrule(br).maxdepth(1));
315+
model.solve();
337316
}
338317
}

src/builder/branchrule.rs

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use crate::builder::CanBeAddedToModel;
2+
use crate::{BranchRule, Model, ProblemCreated};
3+
4+
/// A builder for easily creating branch rules. It can be created using the `branch_rule` function.
5+
pub struct BranchRuleBuilder<R: BranchRule> {
6+
name: Option<String>,
7+
desc: Option<String>,
8+
priority: i32,
9+
maxdepth: i32,
10+
maxbounddist: f64,
11+
rule: R,
12+
}
13+
14+
impl<R: BranchRule> BranchRuleBuilder<R> {
15+
/// Creates a new `BranchRuleBuilder` with the given branch rule.
16+
///
17+
/// Defaults:
18+
/// - `name`: empty string
19+
/// - `desc`: empty string
20+
/// - `priority`: 100000
21+
/// - `maxdepth`: -1 (unlimited)
22+
/// - `maxbounddist`: 1.0 (applies on all nodes)
23+
pub fn new(rule: R) -> Self {
24+
BranchRuleBuilder {
25+
name: None,
26+
desc: None,
27+
priority: 100000,
28+
maxdepth: -1,
29+
maxbounddist: 1.0,
30+
rule,
31+
}
32+
}
33+
34+
/// Sets the name of the branch rule.
35+
pub fn name(mut self, name: &str) -> Self {
36+
self.name = Some(name.to_string());
37+
self
38+
}
39+
40+
/// Sets the description of the branch rule.
41+
pub fn desc(mut self, desc: &str) -> Self {
42+
self.desc = Some(desc.to_string());
43+
self
44+
}
45+
46+
/// Sets the priority of the branch rule.
47+
///
48+
/// When SCIP decides which branch rule to call, it considers their priorities.
49+
/// A higher value indicates a higher priority.
50+
pub fn priority(mut self, priority: i32) -> Self {
51+
self.priority = priority;
52+
self
53+
}
54+
55+
/// Sets the maximum depth level up to which this branch rule should be used.
56+
///
57+
/// If this is -1, the branch rule can be used at any depth.
58+
pub fn maxdepth(mut self, maxdepth: i32) -> Self {
59+
self.maxdepth = maxdepth;
60+
self
61+
}
62+
63+
/// Sets the maximum relative bound distance from the current node's dual bound to
64+
/// primal bound compared to the best node's dual bound for applying the branch rule.
65+
///
66+
/// A value of 0.0 means the rule can only be applied on the current best node,
67+
/// while 1.0 means it can be applied on all nodes.
68+
pub fn maxbounddist(mut self, maxbounddist: f64) -> Self {
69+
self.maxbounddist = maxbounddist;
70+
self
71+
}
72+
}
73+
74+
/// Creates a new default `BranchRuleBuilder` from a branch rule.
75+
/// This function allows you to write:
76+
/// ```rust
77+
/// use russcip::{BranchRule, BranchingCandidate, BranchingResult, SCIPBranchRule, Solving};
78+
/// use russcip::prelude::*;
79+
///
80+
/// struct MyBranchRule;
81+
/// impl BranchRule for MyBranchRule {fn execute(&mut self, model: Model<Solving>, branchrule: SCIPBranchRule, candidates: Vec<BranchingCandidate>) -> BranchingResult {
82+
/// todo!()
83+
/// }
84+
/// }
85+
///
86+
/// let rule = branchrule(MyBranchRule)
87+
/// .name("My Branch Rule")
88+
/// .desc("A custom branch rule")
89+
/// .priority(100)
90+
/// .maxdepth(10)
91+
/// .maxbounddist(0.5);
92+
///
93+
/// let mut model = Model::default();
94+
/// model.add(rule);
95+
/// ```
96+
pub fn branchrule<R: BranchRule>(rule: R) -> BranchRuleBuilder<R> {
97+
BranchRuleBuilder::new(rule)
98+
}
99+
100+
impl<R: BranchRule + 'static> CanBeAddedToModel for BranchRuleBuilder<R> {
101+
type Return = ();
102+
103+
fn add(self, model: &mut Model<ProblemCreated>) {
104+
// Use empty strings as defaults if name or description are not provided.
105+
let name = self.name.unwrap_or_else(|| "".into());
106+
let desc = self.desc.unwrap_or_else(|| "".into());
107+
let rule_box = Box::new(self.rule);
108+
model.include_branch_rule(
109+
&name,
110+
&desc,
111+
self.priority,
112+
self.maxdepth,
113+
self.maxbounddist,
114+
rule_box,
115+
);
116+
}
117+
}
118+
119+
impl<R: BranchRule> From<R> for BranchRuleBuilder<R> {
120+
fn from(rule: R) -> Self {
121+
BranchRuleBuilder::new(rule)
122+
}
123+
}

src/builder/eventhdlr.rs

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::builder::CanBeAddedToModel;
2+
use crate::{Eventhdlr, Model, ProblemCreated};
3+
4+
/// A builder for easily creating event handlers. It can be created using the `eventhdlr` function.
5+
pub struct EventHdlrBuilder<E: Eventhdlr> {
6+
name: Option<String>,
7+
desc: Option<String>,
8+
eventhdlr: E,
9+
}
10+
11+
impl<E: Eventhdlr> EventHdlrBuilder<E> {
12+
/// Creates a new `EventHdlrBuilder` with the given event handler.
13+
///
14+
/// # Defaults
15+
/// - `name`: empty string
16+
/// - `desc`: empty string
17+
pub fn new(eventhdlr: E) -> Self {
18+
EventHdlrBuilder {
19+
name: None,
20+
desc: None,
21+
eventhdlr,
22+
}
23+
}
24+
25+
/// Sets the name of the event handler.
26+
pub fn name(mut self, name: &str) -> Self {
27+
self.name = Some(name.to_string());
28+
self
29+
}
30+
31+
/// Sets the description of the event handler.
32+
pub fn desc(mut self, desc: &str) -> Self {
33+
self.desc = Some(desc.to_string());
34+
self
35+
}
36+
}
37+
38+
/// Creates a new default `EventHdlrBuilder` from an event handler.
39+
/// This function allows you to write:
40+
/// ```rust
41+
/// use russcip::prelude::*;
42+
/// use russcip::{Event, EventMask, Eventhdlr, ProblemCreated, SCIPEventhdlr, Solving};
43+
///
44+
/// struct MyEventHandler;
45+
/// impl Eventhdlr for MyEventHandler {
46+
/// fn get_type(&self) -> EventMask {
47+
/// todo!()
48+
/// }
49+
///
50+
/// fn execute(&mut self, model: Model<Solving>, eventhdlr: SCIPEventhdlr, event: Event) {
51+
/// todo!()
52+
/// }
53+
///
54+
/// }
55+
/// let ev = eventhdlr(MyEventHandler {}).name("My Event Handler");
56+
/// let mut model = Model::default();
57+
/// model.add(eventhdlr(MyEventHandler {}));
58+
/// ```
59+
pub fn eventhdlr<E: Eventhdlr>(ev: E) -> EventHdlrBuilder<E> {
60+
EventHdlrBuilder::new(ev)
61+
}
62+
63+
impl<E: Eventhdlr + 'static> CanBeAddedToModel for EventHdlrBuilder<E> {
64+
type Return = ();
65+
66+
fn add(self, model: &mut Model<ProblemCreated>) {
67+
let name = self.name.unwrap_or_else(|| "".into());
68+
let desc = self.desc.unwrap_or_else(|| "".into());
69+
let eventhdlr = Box::new(self.eventhdlr);
70+
model.include_eventhdlr(&name, &desc, eventhdlr);
71+
}
72+
}

0 commit comments

Comments
 (0)