Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ impl Model<Solved> {
pub trait ModelWithProblem {
/// Returns a vector of all variables in the optimization model.
fn vars(&self) -> Vec<Variable>;
/// Returns a vector of all original variables in the optimization model.
fn orig_vars(&self) -> Vec<Variable>;

/// Returns the variable with the given ID, if it exists.
fn var(&self, var_id: VarId) -> Option<Variable>;
Expand All @@ -551,7 +553,19 @@ impl ModelStageWithProblem for Solving {}
impl<S: ModelStageWithProblem> ModelWithProblem for Model<S> {
/// Returns a vector of all variables in the optimization model.
fn vars(&self) -> Vec<Variable> {
let scip_vars = self.scip.vars(false);
let scip_vars = self.scip.vars(false, false);
scip_vars
.into_values()
.map(|v| Variable {
raw: v,
scip: self.scip.clone(),
})
.collect()
}

/// Returns a vector of all original variables in the optimization model.
fn orig_vars(&self) -> Vec<Variable> {
let scip_vars = self.scip.vars(true, false);
scip_vars
.into_values()
.map(|v| Variable {
Expand All @@ -563,7 +577,7 @@ impl<S: ModelStageWithProblem> ModelWithProblem for Model<S> {

/// Returns the variable with the given ID, if it exists.
fn var(&self, var_id: VarId) -> Option<Variable> {
let vars = self.scip.vars(false);
let vars = self.scip.vars(false, false);
for (i, v) in vars {
if i == var_id {
return Some(Variable {
Expand Down
55 changes: 55 additions & 0 deletions src/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,58 @@ impl ScipParameter for String {
model.str_param(name)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bool() {
let model = Model::default();
let model = bool::set(model, "display/allviols", true).unwrap();
assert!(model.param::<bool>("display/allviols"));
let model = model.set_param("display/allviols", false);
assert!(!bool::get(&model, "display/allviols"));
}

#[test]
fn test_i64() {
let model = Model::default();
assert_eq!(
model.param::<i64>("constraints/components/nodelimit"),
10000i64
);
let model = model.set_param("constraints/components/nodelimit", 100i64);
assert_eq!(
model.param::<i64>("constraints/components/nodelimit"),
100i64
);
}

#[test]
fn test_i32() {
let model = Model::default();
assert_eq!(model.param::<i32>("conflict/minmaxvars"), 0i32);
let model = model.set_param("conflict/minmaxvars", 100i32);
assert_eq!(model.param::<i32>("conflict/minmaxvars"), 100i32);
}

#[test]
fn test_f64() {
let model = Model::default();
assert_eq!(model.param::<f64>("limits/time"), 1e+20);
let model = model.set_param("limits/time", 100.0);
assert_eq!(model.param::<f64>("limits/time"), 100.0);
}

#[test]
fn test_str() {
let model = Model::default();
assert_eq!(model.param::<String>("visual/vbcfilename"), "-".to_string());
let model = model.set_param("visual/vbcfilename", "test".to_string());
assert_eq!(
model.param::<String>("visual/vbcfilename"),
"test".to_string()
);
}
}
10 changes: 7 additions & 3 deletions src/scip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl ScipPtr {
std::ptr::null_mut()
));
// capture vars and cons since they were not created by the user (and SCIP will free them later)
self.vars(true);
self.vars(false, true);
self.conss(true);
Ok(())
}
Expand Down Expand Up @@ -194,11 +194,15 @@ impl ScipPtr {
Ok(())
}

pub(crate) fn vars(&self, capture: bool) -> BTreeMap<usize, *mut SCIP_Var> {
pub(crate) fn vars(&self, original: bool, capture: bool) -> BTreeMap<usize, *mut SCIP_Var> {
// NOTE: this method should only be called once per SCIP instance
let n_vars = self.n_vars();
let mut vars = BTreeMap::new();
let scip_vars = unsafe { ffi::SCIPgetVars(self.raw) };
let scip_vars = if original {
unsafe { ffi::SCIPgetOrigVars(self.raw) }
} else {
unsafe { ffi::SCIPgetVars(self.raw) }
};
for i in 0..n_vars {
let scip_var = unsafe { *scip_vars.add(i) };
if capture {
Expand Down
42 changes: 42 additions & 0 deletions src/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,41 @@ impl Variable {
pub fn sol_val(&self) -> f64 {
unsafe { ffi::SCIPgetVarSol(self.scip.raw, self.raw) }
}

/// Returns whether the variable is deleted.
pub fn is_deleted(&self) -> bool {
unsafe { ffi::SCIPvarIsDeleted(self.raw) != 0 }
}

/// Returns whether the variable is transformed.
pub fn is_transformed(&self) -> bool {
unsafe { ffi::SCIPvarIsTransformed(self.raw) != 0 }
}

/// Returns whether the variable is original.
pub fn is_original(&self) -> bool {
unsafe { ffi::SCIPvarIsOriginal(self.raw) != 0 }
}

/// Returns whether the variable is negated.
pub fn is_negated(&self) -> bool {
unsafe { ffi::SCIPvarIsNegated(self.raw) != 0 }
}

/// Returns whether the variable is removable (due to aging in the LP).
pub fn is_removable(&self) -> bool {
unsafe { ffi::SCIPvarIsRemovable(self.raw) != 0 }
}

/// Returns whether the variable is a directed counterpart of an original variable.
pub fn is_trans_from_orig(&self) -> bool {
unsafe { ffi::SCIPvarIsTransformedOrigvar(self.raw) != 0 }
}

/// Returns whether the variable is active (i.e., neither fixed nor aggregated).
pub fn is_active(&self) -> bool {
unsafe { ffi::SCIPvarIsActive(self.raw) != 0 }
}
}

/// The type of variable in an optimization problem.
Expand Down Expand Up @@ -194,6 +229,13 @@ mod tests {
assert_eq!(var.name(), "x");
assert_eq!(var.var_type(), VarType::ImplInt);
assert_eq!(var.status(), VarStatus::Original);
assert!(!var.is_in_lp());
assert!(!var.is_deleted());
assert!(!var.is_transformed());
assert!(var.is_original());
assert!(!var.is_negated());
assert!(!var.is_removable());
assert!(var.is_active());

assert!(!var.inner().is_null());
}
Expand Down
Loading