1use crate::scip::ScipPtr;
2use crate::{Col, ffi};
3use core::panic;
4use scip_sys::SCIP_Status;
5use std::rc::Rc;
6
7pub type VarId = usize;
9
10#[derive(Debug, Clone)]
12#[allow(dead_code)]
13pub struct Variable {
14 pub(crate) raw: *mut ffi::SCIP_VAR,
15 pub(crate) scip: Rc<ScipPtr>,
16}
17
18impl PartialEq for Variable {
19 fn eq(&self, other: &Self) -> bool {
20 self.index() == other.index() && self.raw == other.raw
21 }
22}
23
24impl Eq for Variable {}
25
26impl Variable {
27 pub fn inner(&self) -> *mut ffi::SCIP_VAR {
29 self.raw
30 }
31
32 pub fn index(&self) -> usize {
34 let id = unsafe { ffi::SCIPvarGetIndex(self.raw) };
35 assert!(id >= 0);
36 id as usize
37 }
38
39 pub fn name(&self) -> String {
41 let name = unsafe { ffi::SCIPvarGetName(self.raw) };
42 let name = unsafe { std::ffi::CStr::from_ptr(name) };
43 name.to_str().unwrap().to_string()
44 }
45
46 pub fn obj(&self) -> f64 {
48 unsafe { ffi::SCIPvarGetObj(self.raw) }
49 }
50
51 pub fn lb(&self) -> f64 {
53 unsafe { ffi::SCIPvarGetLbLocal(self.raw) }
54 }
55
56 pub fn ub(&self) -> f64 {
58 unsafe { ffi::SCIPvarGetUbLocal(self.raw) }
59 }
60
61 pub fn lb_local(&self) -> f64 {
63 unsafe { ffi::SCIPvarGetLbLocal(self.raw) }
64 }
65
66 pub fn ub_local(&self) -> f64 {
68 unsafe { ffi::SCIPvarGetUbLocal(self.raw) }
69 }
70
71 pub fn var_type(&self) -> VarType {
73 let var_type = unsafe { ffi::SCIPvarGetType(self.raw) };
74 var_type.into()
75 }
76
77 pub fn status(&self) -> VarStatus {
79 let status = unsafe { ffi::SCIPvarGetStatus(self.raw) };
80 status.into()
81 }
82
83 pub fn col(&self) -> Option<Col> {
85 if self.is_in_lp() {
86 let col_ptr = unsafe { ffi::SCIPvarGetCol(self.raw) };
87 let col = Col {
88 raw: col_ptr,
89 scip: Rc::clone(&self.scip),
90 };
91 Some(col)
92 } else {
93 None
94 }
95 }
96
97 pub fn is_in_lp(&self) -> bool {
99 (unsafe { ffi::SCIPvarIsInLP(self.raw) }) != 0
100 }
101
102 pub fn sol_val(&self) -> f64 {
104 unsafe { ffi::SCIPgetVarSol(self.scip.raw, self.raw) }
105 }
106
107 pub fn is_deleted(&self) -> bool {
109 unsafe { ffi::SCIPvarIsDeleted(self.raw) != 0 }
110 }
111
112 pub fn is_transformed(&self) -> bool {
114 unsafe { ffi::SCIPvarIsTransformed(self.raw) != 0 }
115 }
116
117 pub fn is_original(&self) -> bool {
119 unsafe { ffi::SCIPvarIsOriginal(self.raw) != 0 }
120 }
121
122 pub fn is_negated(&self) -> bool {
124 unsafe { ffi::SCIPvarIsNegated(self.raw) != 0 }
125 }
126
127 pub fn is_removable(&self) -> bool {
129 unsafe { ffi::SCIPvarIsRemovable(self.raw) != 0 }
130 }
131
132 pub fn is_trans_from_orig(&self) -> bool {
134 unsafe { ffi::SCIPvarIsTransformedOrigvar(self.raw) != 0 }
135 }
136
137 pub fn is_active(&self) -> bool {
139 unsafe { ffi::SCIPvarIsActive(self.raw) != 0 }
140 }
141
142 pub fn transformed(&self) -> Option<Variable> {
144 let var_ptr = unsafe { ffi::SCIPvarGetTransVar(self.raw) };
145 if var_ptr.is_null() {
146 return None;
147 }
148
149 let var = Variable {
150 raw: var_ptr,
151 scip: Rc::clone(&self.scip),
152 };
153 Some(var)
154 }
155}
156
157#[derive(Debug, PartialEq, Eq, Clone, Copy)]
159pub enum VarType {
160 Continuous,
162 Integer,
164 Binary,
166 ImplInt,
168}
169
170impl From<VarType> for ffi::SCIP_Vartype {
171 fn from(var_type: VarType) -> Self {
172 match var_type {
173 VarType::Continuous => ffi::SCIP_Vartype_SCIP_VARTYPE_CONTINUOUS,
174 VarType::Integer => ffi::SCIP_Vartype_SCIP_VARTYPE_INTEGER,
175 VarType::Binary => ffi::SCIP_Vartype_SCIP_VARTYPE_BINARY,
176 VarType::ImplInt => ffi::SCIP_Vartype_SCIP_VARTYPE_IMPLINT,
177 }
178 }
179}
180
181impl From<ffi::SCIP_Vartype> for VarType {
182 fn from(var_type: ffi::SCIP_Vartype) -> Self {
183 match var_type {
184 ffi::SCIP_Vartype_SCIP_VARTYPE_CONTINUOUS => VarType::Continuous,
185 ffi::SCIP_Vartype_SCIP_VARTYPE_INTEGER => VarType::Integer,
186 ffi::SCIP_Vartype_SCIP_VARTYPE_BINARY => VarType::Binary,
187 ffi::SCIP_Vartype_SCIP_VARTYPE_IMPLINT => VarType::ImplInt,
188 _ => panic!("Unknown VarType {:?}", var_type),
189 }
190 }
191}
192
193#[derive(Debug, PartialEq, Eq, Clone, Copy)]
195pub enum VarStatus {
196 Original,
198 Loose,
200 Column,
202 Fixed,
204 Aggregated,
206 MultiAggregated,
208 NegatedVar,
210}
211
212impl From<SCIP_Status> for VarStatus {
213 fn from(status: SCIP_Status) -> Self {
214 match status {
215 ffi::SCIP_Varstatus_SCIP_VARSTATUS_ORIGINAL => VarStatus::Original,
216 ffi::SCIP_Varstatus_SCIP_VARSTATUS_LOOSE => VarStatus::Loose,
217 ffi::SCIP_Varstatus_SCIP_VARSTATUS_COLUMN => VarStatus::Column,
218 ffi::SCIP_Varstatus_SCIP_VARSTATUS_FIXED => VarStatus::Fixed,
219 ffi::SCIP_Varstatus_SCIP_VARSTATUS_AGGREGATED => VarStatus::Aggregated,
220 ffi::SCIP_Varstatus_SCIP_VARSTATUS_MULTAGGR => VarStatus::MultiAggregated,
221 ffi::SCIP_Varstatus_SCIP_VARSTATUS_NEGATED => VarStatus::NegatedVar,
222 _ => panic!("Unhandled SCIP variable status {:?}", status),
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::{Model, ObjSense, ProblemOrSolving, minimal_model};
231
232 #[test]
233 fn var_data() {
234 let mut model = Model::new().include_default_plugins().create_prob("test");
235 let var = model.add_var(0.0, 1.0, 2.0, "x", VarType::ImplInt);
236
237 assert_eq!(var.index(), 0);
238 assert_eq!(var.lb(), 0.0);
239 assert_eq!(var.lb_local(), 0.0);
240 assert_eq!(var.ub(), 1.0);
241 assert_eq!(var.ub_local(), 1.0);
242 assert_eq!(var.obj(), 2.0);
243 assert_eq!(var.name(), "x");
244 assert_eq!(var.var_type(), VarType::ImplInt);
245 assert_eq!(var.status(), VarStatus::Original);
246 assert!(!var.is_in_lp());
247 assert!(!var.is_deleted());
248 assert!(!var.is_transformed());
249 assert!(var.is_original());
250 assert!(!var.is_negated());
251 assert!(!var.is_removable());
252 assert!(var.is_active());
253
254 assert!(!var.inner().is_null());
255 }
256
257 #[test]
258 fn var_memory_safety() {
259 let mut model = Model::new()
260 .hide_output()
261 .include_default_plugins()
262 .create_prob("test")
263 .set_obj_sense(ObjSense::Maximize);
264
265 let x1 = model.add_var(0., f64::INFINITY, 3., "x1", VarType::Integer);
266
267 drop(model);
268 assert_eq!(x1.name(), "x1");
269 }
270
271 #[test]
272 fn var_sol_val() {
273 let mut model = minimal_model();
274 let x = model.add_var(0.0, 1.0, 1.0, "x", VarType::Binary);
275 let _cons = model.add_cons(vec![&x], &[1.0], 1.0, 1.0, "cons1");
276
277 model.solve();
278
279 assert_eq!(x.sol_val(), 1.0);
280 }
281}