1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use crate::{Model, Retcode, Row, Solution, Solving, ffi, scip_call};
use scip_sys::SCIP_ROW;
use std::fmt::Debug;
/// A trait for implementing custom constraint handlers.
///
/// # Example
/// An example of using a custom constraint handler to enforce subtour elimination constraints in a TSP can be found
/// [here](https://github.com/scipopt/russcip/blob/main/examples/README.md).
pub trait Conshdlr {
/// Check if the (primal) solution satisfies the constraint.
///
/// # Arguments
/// * `model` - The current model in solving state.
/// * `conshdlr` - The internal SCIP constraint handler.
/// * `solution` - The solution to be checked.
fn check(&mut self, model: Model<Solving>, conshdlr: SCIPConshdlr, solution: &Solution)
-> bool;
/// Enforce the constraint for the current sub-problem's (LP) solution.
///
/// # Arguments
/// * `model` - The current model in solving state.
/// * `conshdlr` - The internal SCIP constraint handler.
///
/// # Returns
/// * `ConshdlrResult` - The result of enforcing the constraint.
fn enforce(&mut self, model: Model<Solving>, conshdlr: SCIPConshdlr) -> ConshdlrResult;
}
/// The result of enforcing a constraint handler.
pub enum ConshdlrResult {
/// States that the problem is feasible.
Feasible,
/// States that the problem is infeasible.
CutOff,
/// Added another constraint that resolves the infeasibility.
ConsAdded,
/// Reduced the domain of a variable.
ReducedDom,
/// Added a cutting plane that separates the lp solution.
Separated,
/// Request to resolve the LP.
SolveLP,
/// Created a branching.
Branched,
}
impl From<ConshdlrResult> for ffi::SCIP_Result {
fn from(result: ConshdlrResult) -> Self {
match result {
ConshdlrResult::Feasible => ffi::SCIP_Result_SCIP_FEASIBLE,
ConshdlrResult::CutOff => ffi::SCIP_Result_SCIP_CUTOFF,
ConshdlrResult::ConsAdded => ffi::SCIP_Result_SCIP_CONSADDED,
ConshdlrResult::ReducedDom => ffi::SCIP_Result_SCIP_REDUCEDDOM,
ConshdlrResult::Separated => ffi::SCIP_Result_SCIP_SEPARATED,
ConshdlrResult::SolveLP => ffi::SCIP_Result_SCIP_SOLVELP,
ConshdlrResult::Branched => ffi::SCIP_Result_SCIP_BRANCHED,
}
}
}
/// Wrapper for the internal SCIP constraint handler.
#[derive(Debug)]
pub struct SCIPConshdlr {
pub(crate) raw: *mut ffi::SCIP_CONSHDLR,
}
impl SCIPConshdlr {
/// Returns a raw pointer to the underlying `ffi::SCIP_CONSHDLR` struct.
pub fn inner(&self) -> *mut ffi::SCIP_CONSHDLR {
self.raw
}
/// Returns the name of the constraint handler.
pub fn name(&self) -> String {
let name = unsafe { ffi::SCIPconshdlrGetName(self.raw) };
let name = unsafe { std::ffi::CStr::from_ptr(name) };
name.to_str().unwrap().to_string()
}
/// Returns the description of the constraint handler.
pub fn desc(&self) -> String {
let desc = unsafe { ffi::SCIPconshdlrGetDesc(self.raw) };
let desc = unsafe { std::ffi::CStr::from_ptr(desc) };
desc.to_str().unwrap().to_string()
}
/// Creates an empty row for the constraint handler.
pub fn create_empty_row(
&self,
model: &Model<Solving>,
name: &str,
lhs: f64,
rhs: f64,
local: bool,
modifiable: bool,
removable: bool,
) -> Result<Row, Retcode> {
let name = std::ffi::CString::new(name).unwrap();
let local = if local { 1 } else { 0 };
let modifiable = if modifiable { 1 } else { 0 };
let removable = if removable { 1 } else { 0 };
let mut row: *mut SCIP_ROW = std::ptr::null_mut();
scip_call! { ffi::SCIPcreateEmptyRowConshdlr(model.scip.raw, &mut row, self.raw, name.as_ptr(), lhs, rhs, local, modifiable, removable) }
Ok(Row {
raw: row,
scip: model.scip.clone(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Status;
#[test]
fn all_inf_conshdlr() {
struct AllInfeasibleConshdlr;
impl Conshdlr for AllInfeasibleConshdlr {
fn check(
&mut self,
_model: Model<Solving>,
_conshdlr: SCIPConshdlr,
_solution: &Solution,
) -> bool {
false
}
fn enforce(
&mut self,
_model: Model<Solving>,
_conshdlr: SCIPConshdlr,
) -> ConshdlrResult {
ConshdlrResult::CutOff
}
}
let mut model = Model::default();
model.include_conshdlr(
"AllInfeasibleConshdlr",
"All infeasible constraint handler",
-1,
-1,
Box::new(AllInfeasibleConshdlr {}),
);
let solved = model.solve();
assert_eq!(solved.status(), Status::Infeasible);
}
}