Skip to content

Commit 083171e

Browse files
committed
Ergonomic generic way to add scip objects & variable builder
1 parent 7a6dddb commit 083171e

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed

src/builder.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use crate::{Model, ModelWithProblem, ProblemCreated, VarType, Variable};
2+
3+
/// A trait for adding two values together.
4+
pub trait CanBeAddedToModel {
5+
/// The return type after adding to the model (e.g. `Variable` / `Constraint` ).
6+
type Return;
7+
/// How to add the value to the model.
8+
fn add(&self, model: &mut Model<ProblemCreated>) -> Self::Return;
9+
}
10+
11+
/// A builder for variables.
12+
pub struct VarBuilder {
13+
name: Option<String>,
14+
obj: f64,
15+
lb: f64,
16+
ub: f64,
17+
var_type: VarType,
18+
}
19+
20+
impl Default for VarBuilder {
21+
fn default() -> Self {
22+
VarBuilder {
23+
name: None,
24+
obj: 0.0,
25+
lb: 0.0,
26+
ub: f64::INFINITY,
27+
var_type: VarType::Continuous,
28+
}
29+
}
30+
}
31+
32+
impl VarBuilder {
33+
/// Sets the variable to be an integer variable.
34+
pub fn integer(mut self, lb: isize, ub: isize) -> Self {
35+
self.lb = lb as f64;
36+
self.ub = ub as f64;
37+
self.var_type = VarType::Integer;
38+
self
39+
}
40+
41+
/// Sets the variable to be a binary variable.
42+
pub fn binary(mut self) -> Self {
43+
self.lb = 0.0;
44+
self.ub = 1.0;
45+
self.var_type = VarType::Binary;
46+
self
47+
}
48+
49+
/// Sets the variable to be a continuous variable.
50+
pub fn continuous(mut self, lb: f64, ub: f64) -> Self {
51+
self.lb = lb;
52+
self.ub = ub;
53+
self.var_type = VarType::Continuous;
54+
self
55+
}
56+
57+
/// Sets the variable to be an implicit integer variable.
58+
pub fn impl_int(mut self, lb: f64, ub: f64) -> Self {
59+
self.lb = lb;
60+
self.ub = ub;
61+
self.var_type = VarType::ImplInt;
62+
self
63+
}
64+
65+
/// Sets the name of the variable.
66+
pub fn name(mut self, name: String) -> Self {
67+
self.name = Some(name);
68+
self
69+
}
70+
71+
/// Sets the objective coefficient of the variable.
72+
pub fn obj(mut self, obj: f64) -> Self {
73+
self.obj = obj;
74+
self
75+
}
76+
}
77+
78+
impl CanBeAddedToModel for VarBuilder {
79+
type Return = Variable;
80+
fn add(&self, model: &mut Model<ProblemCreated>) -> Variable {
81+
let name = self.name.clone().unwrap_or_else(|| {
82+
let n_vars = model.n_vars();
83+
format!("x{}", n_vars)
84+
});
85+
86+
model.add_var(self.lb, self.ub, self.obj, &name, self.var_type)
87+
}
88+
}
89+
90+
#[cfg(test)]
91+
mod tests {
92+
use super::*;
93+
94+
#[test]
95+
fn test_var_builder() {
96+
let var = VarBuilder::default()
97+
.name("x".to_string())
98+
.obj(1.0)
99+
.continuous(0.0, 1.0);
100+
101+
assert_eq!(var.name, Some("x".to_string()));
102+
assert_eq!(var.obj, 1.0);
103+
assert_eq!(var.lb, 0.0);
104+
assert_eq!(var.ub, 1.0);
105+
}
106+
107+
#[test]
108+
fn test_var_builder_add() {
109+
let mut model = Model::default()
110+
.set_obj_sense(crate::ObjSense::Maximize);
111+
let var = VarBuilder::default()
112+
.name("x".to_string())
113+
.obj(1.0)
114+
.continuous(0.0, 1.0);
115+
116+
let var = model.add(var);
117+
118+
assert_eq!(model.n_vars(), 1);
119+
assert_eq!(var.name(), "x");
120+
assert_eq!(var.obj(), 1.0);
121+
assert_eq!(var.lb(), 0.0);
122+
assert_eq!(var.ub(), 1.0);
123+
124+
let solved = model.solve();
125+
assert_eq!(solved.status(), crate::Status::Optimal);
126+
assert_eq!(solved.obj_val(), 1.0);
127+
}
128+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ mod param;
7070
/// Contains the `Row` struct, which represents a row in an LP relaxation.
7171
pub mod row;
7272

73+
/// Contains methods for creating scip objects in an ergonomic way.
74+
pub mod builder;
75+
7376
pub use row::*;
7477

7578
/// A macro for calling a `SCIP` function and returning an error if the return code is not `SCIP_OKAY`.

src/model.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::{ffi, Row, Separator};
1111
use crate::{BranchRule, HeurTiming, Heuristic, Pricer};
1212
use scip_sys::SCIP;
1313
use std::rc::Rc;
14+
use crate::builder::CanBeAddedToModel;
1415

1516
/// Represents an optimization model.
1617
#[non_exhaustive]
@@ -171,6 +172,11 @@ impl Model<ProblemCreated> {
171172
}
172173
}
173174

175+
/// Adds anything that could be added to the model (variables, constraints, etc).
176+
pub fn add<R, O: CanBeAddedToModel<Return=R>>(&mut self, object: O) -> R {
177+
object.add(self)
178+
}
179+
174180
/// Includes a new branch rule in the model with the given name, description, priority, maximum depth, maximum bound distance, and implementation.
175181
///
176182
/// # Arguments
@@ -551,7 +557,8 @@ pub trait ModelWithProblem {
551557
fn write(&self, path: &str, ext: &str) -> Result<(), Retcode>;
552558
}
553559

554-
trait ModelStageWithProblem {}
560+
/// A trait for model stages that have a problem.
561+
pub trait ModelStageWithProblem {}
555562
impl ModelStageWithProblem for ProblemCreated {}
556563
impl ModelStageWithProblem for Solved {}
557564
impl ModelStageWithProblem for Solving {}
@@ -803,7 +810,9 @@ pub trait ProblemOrSolving {
803810
name: &str,
804811
) -> Constraint;
805812
}
806-
trait ModelStageProblemOrSolving {}
813+
814+
/// A trait for model stages that have a problem or are during solving.
815+
pub trait ModelStageProblemOrSolving {}
807816
impl ModelStageProblemOrSolving for ProblemCreated {}
808817
impl ModelStageProblemOrSolving for Solving {}
809818

0 commit comments

Comments
 (0)