diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c1052..c015eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,12 @@ ## unreleased ### Added - Added method `set_obj_integral` to allow specifying that the objective value is always integral. + - Methods to attach data to `Variable`s. ### Fixed ### Changed - Free sol after adding in `ScipPtr::add_sol()`. - Allow adding a var in `Solving` mode. +- Use shared mutable pointers for variables. ### Removed ## 0.2.5 diff --git a/README.md b/README.md index 5cdb631..313d369 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ fn main() { let vars = solved_model.vars(); for var in vars { - println!("{} = {}", &var.name(), sol.val(var)); + println!("{} = {}", var.borrow().name(), sol.val(var.clone())); } } diff --git a/src/lib.rs b/src/lib.rs index 90c8d54..51af703 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,7 @@ //! let vars = solved_model.vars(); //! //! for var in vars { -//! println!("{} = {}", &var.name(), sol.val(var)); +//! println!("{} = {}", var.borrow().name(), sol.val(var.clone())); //! } #![deny(missing_docs)] diff --git a/src/model.rs b/src/model.rs index 1512254..6e969c8 100644 --- a/src/model.rs +++ b/src/model.rs @@ -13,6 +13,9 @@ use crate::status::Status; use crate::variable::{VarId, VarType, Variable}; use crate::{BranchRule, HeurTiming, Heuristic, Pricer}; +/// Alias for a shared mutable reference-counted pointer. +pub type SharedMut = Rc>; + /// Represents an optimization model. #[non_exhaustive] #[derive(Debug)] @@ -32,22 +35,22 @@ pub struct PluginsIncluded; /// Represents the state of an optimization model where the problem has been created. #[derive(Debug, Clone)] pub struct ProblemCreated { - pub(crate) vars: Rc>>>, - pub(crate) conss: Rc>>>, + pub(crate) vars: SharedMut>>, + pub(crate) conss: SharedMut>>, } /// Represents the state of an optimization model during the solving process (to be used in plugins). #[derive(Debug)] pub struct Solving { - pub(crate) vars: Rc>>>, - pub(crate) conss: Rc>>>, + pub(crate) vars: SharedMut>>, + pub(crate) conss: SharedMut>>, } /// Represents the state of an optimization model that has been solved. #[derive(Debug)] pub struct Solved { - pub(crate) vars: Rc>>>, - pub(crate) conss: Rc>>>, + pub(crate) vars: SharedMut>>, + pub(crate) conss: SharedMut>>, } impl Model { @@ -239,13 +242,13 @@ impl Model { obj: f64, name: &str, var_type: VarType, - ) -> Rc { + ) -> SharedMut { let var = self .scip .create_var(lb, ub, obj, name, var_type) .expect("Failed to create variable in state ProblemCreated"); let var_id = var.index(); - let var = Rc::new(var); + let var = Rc::new(RefCell::new(var)); self.state.vars.borrow_mut().insert(var_id, var.clone()); var } @@ -445,13 +448,13 @@ impl Model { obj: f64, name: &str, var_type: VarType, - ) -> Rc { + ) -> SharedMut { let var = self .scip .create_priced_var(lb, ub, obj, name, var_type) .expect("Failed to create variable in state ProblemCreated"); - let var = Rc::new(var); - let var_id = var.index(); + let var = Rc::new(RefCell::new(var)); + let var_id = var.borrow().index(); self.state.vars.borrow_mut().insert(var_id, var.clone()); var } @@ -467,10 +470,10 @@ impl Model { /// A trait for optimization models with a problem created. pub trait ModelWithProblem { /// Returns a vector of all variables in the optimization model. - fn vars(&self) -> Vec>; + fn vars(&self) -> Vec>; /// Returns the variable with the given ID, if it exists. - fn var(&self, var_id: VarId) -> Option>; + fn var(&self, var_id: VarId) -> Option>; /// Returns the number of variables in the optimization model. fn n_vars(&self) -> usize; @@ -490,12 +493,12 @@ macro_rules! impl_ModelWithProblem { $(impl ModelWithProblem for $t { /// Returns a vector of all variables in the optimization model. - fn vars(&self) -> Vec> { + fn vars(&self) -> Vec> { self.state.vars.borrow().values().map(Rc::clone).collect() } /// Returns the variable with the given ID, if it exists. - fn var(&self, var_id: VarId) -> Option> { + fn var(&self, var_id: VarId) -> Option> { self.state.vars.borrow().get(&var_id).map(Rc::clone) } @@ -547,7 +550,7 @@ pub trait ProblemOrSolving { /// # Panics /// /// This method panics if the variable cannot be added in the current state, or if the variable is not binary. - fn add_cons_coef_setppc(&mut self, cons: Rc, var: Rc); + fn add_cons_coef_setppc(&mut self, cons: Rc, var: SharedMut); /// Adds a coefficient to the given constraint for the given variable and coefficient value. /// @@ -560,7 +563,7 @@ pub trait ProblemOrSolving { /// # Panics /// /// This method panics if the coefficient cannot be added in the current state. - fn add_cons_coef(&mut self, cons: Rc, var: Rc, coef: f64); + fn add_cons_coef(&mut self, cons: Rc, var: SharedMut, coef: f64); /// Adds a new quadratic constraint to the model with the given variables, coefficients, left-hand side, right-hand side, and name. /// /// # Arguments @@ -583,10 +586,10 @@ pub trait ProblemOrSolving { /// This method panics if the constraint cannot be created in the current state. fn add_cons_quadratic( &mut self, - lin_vars: Vec>, + lin_vars: Vec>, lin_coefs: &mut [f64], - quad_vars_1: Vec>, - quad_vars_2: Vec>, + quad_vars_1: Vec>, + quad_vars_2: Vec>, quad_coefs: &mut [f64], lhs: f64, rhs: f64, @@ -611,7 +614,7 @@ pub trait ProblemOrSolving { /// This method panics if the constraint cannot be created in the current state. fn add_cons( &mut self, - vars: Vec>, + vars: Vec>, coefs: &[f64], lhs: f64, rhs: f64, @@ -631,7 +634,7 @@ pub trait ProblemOrSolving { /// # Panics /// /// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary. - fn add_cons_set_part(&mut self, vars: Vec>, name: &str) -> Rc; + fn add_cons_set_part(&mut self, vars: Vec>, name: &str) -> Rc; /// Adds a new set cover constraint to the model with the given variables and name. /// /// # Arguments @@ -646,7 +649,7 @@ pub trait ProblemOrSolving { /// # Panics /// /// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary. - fn add_cons_set_cover(&mut self, vars: Vec>, name: &str) -> Rc; + fn add_cons_set_cover(&mut self, vars: Vec>, name: &str) -> Rc; /// Adds a new set packing constraint to the model with the given variables and name. /// /// # Arguments @@ -661,7 +664,7 @@ pub trait ProblemOrSolving { /// # Panics /// /// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary. - fn add_cons_set_pack(&mut self, vars: Vec>, name: &str) -> Rc; + fn add_cons_set_pack(&mut self, vars: Vec>, name: &str) -> Rc; } macro_rules! impl_ProblemOrSolving { @@ -698,8 +701,8 @@ macro_rules! impl_ProblemOrSolving { /// # Panics /// /// This method panics if the variable cannot be added in the current state, or if the variable is not binary. - fn add_cons_coef_setppc(&mut self, cons: Rc, var: Rc) { - assert_eq!(var.var_type(), VarType::Binary); + fn add_cons_coef_setppc(&mut self, cons: Rc, var: SharedMut) { + assert_eq!(var.borrow().var_type(), VarType::Binary); self.scip .add_cons_coef_setppc(cons, var) .expect("Failed to add constraint coefficient in state ProblemCreated"); @@ -717,7 +720,7 @@ macro_rules! impl_ProblemOrSolving { /// # Panics /// /// This method panics if the coefficient cannot be added in the current state. - fn add_cons_coef(&mut self, cons: Rc, var: Rc, coef: f64) { + fn add_cons_coef(&mut self, cons: Rc, var: SharedMut, coef: f64) { self.scip .add_cons_coef(cons, var, coef) .expect("Failed to add constraint coefficient in state ProblemCreated"); @@ -745,10 +748,10 @@ macro_rules! impl_ProblemOrSolving { /// This method panics if the constraint cannot be created in the current state. fn add_cons_quadratic( &mut self, - lin_vars: Vec>, + lin_vars: Vec>, lin_coefs: &mut [f64], - quad_vars_1: Vec>, - quad_vars_2: Vec>, + quad_vars_1: Vec>, + quad_vars_2: Vec>, quad_coefs: &mut [f64], lhs: f64, rhs: f64, @@ -796,7 +799,7 @@ macro_rules! impl_ProblemOrSolving { /// This method panics if the constraint cannot be created in the current state. fn add_cons( &mut self, - vars: Vec>, + vars: Vec>, coefs: &[f64], lhs: f64, rhs: f64, @@ -826,8 +829,8 @@ macro_rules! impl_ProblemOrSolving { /// # Panics /// /// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary. - fn add_cons_set_part(&mut self, vars: Vec>, name: &str) -> Rc { - assert!(vars.iter().all(|v| v.var_type() == VarType::Binary)); + fn add_cons_set_part(&mut self, vars: Vec>, name: &str) -> Rc { + assert!(vars.iter().all(|v| v.borrow().var_type() == VarType::Binary)); let cons = self .scip .create_cons_set_part(vars, name) @@ -851,8 +854,8 @@ macro_rules! impl_ProblemOrSolving { /// # Panics /// /// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary. - fn add_cons_set_cover(&mut self, vars: Vec>, name: &str) -> Rc { - assert!(vars.iter().all(|v| v.var_type() == VarType::Binary)); + fn add_cons_set_cover(&mut self, vars: Vec>, name: &str) -> Rc { + assert!(vars.iter().all(|v| v.borrow().var_type() == VarType::Binary)); let cons = self .scip .create_cons_set_cover(vars, name) @@ -876,8 +879,8 @@ macro_rules! impl_ProblemOrSolving { /// # Panics /// /// This method panics if the constraint cannot be created in the current state, or if any of the variables are not binary. - fn add_cons_set_pack(&mut self, vars: Vec>, name: &str) -> Rc { - assert!(vars.iter().all(|v| v.var_type() == VarType::Binary)); + fn add_cons_set_pack(&mut self, vars: Vec>, name: &str) -> Rc { + assert!(vars.iter().all(|v| v.borrow().var_type() == VarType::Binary)); let cons = self .scip .create_cons_set_pack(vars, name) @@ -1149,21 +1152,21 @@ mod tests { .set_obj_sense(ObjSense::Maximize); let x1_id = model .add_var(0., f64::INFINITY, 3., "x1", VarType::Integer) - .index(); + .borrow().index(); let x2_id = model .add_var(0., f64::INFINITY, 4., "x2", VarType::Continuous) - .index(); + .borrow().index(); let x1 = model.var(x1_id).unwrap(); let x2 = model.var(x2_id).unwrap(); assert_eq!(model.n_vars(), 2); assert_eq!(model.vars().len(), 2); - assert!(x1.raw != x2.raw); - assert!(x1.var_type() == VarType::Integer); - assert!(x2.var_type() == VarType::Continuous); - assert!(x1.name() == "x1"); - assert!(x2.name() == "x2"); - assert!(x1.obj() == 3.); - assert!(x2.obj() == 4.); + assert!(x1.borrow().raw != x2.borrow().raw); + assert!(x1.borrow().var_type() == VarType::Integer); + assert!(x2.borrow().var_type() == VarType::Continuous); + assert!(x1.borrow().name() == "x1"); + assert!(x2.borrow().name() == "x2"); + assert!(x1.borrow().obj() == 3.); + assert!(x2.borrow().obj() == 4.); } fn create_model() -> Model { diff --git a/src/scip.rs b/src/scip.rs index 3456896..cab672a 100644 --- a/src/scip.rs +++ b/src/scip.rs @@ -1,11 +1,9 @@ use crate::branchrule::{BranchRule, BranchingCandidate}; use crate::pricer::{Pricer, PricerResultState}; -use crate::{ - ffi, scip_call_panic, BranchingResult, Constraint, Eventhdlr, HeurResult, Node, ObjSense, - ParamSetting, Retcode, Solution, Status, VarType, Variable, -}; +use crate::{ffi, scip_call_panic, BranchingResult, Constraint, Eventhdlr, HeurResult, Node, ObjSense, ParamSetting, Retcode, Solution, Status, VarType, Variable, SharedMut}; use crate::{scip_call, HeurTiming, Heuristic}; use core::panic; +use std::cell::RefCell; use std::collections::BTreeMap; use std::ffi::{c_int, CStr, CString}; use std::mem::MaybeUninit; @@ -139,7 +137,7 @@ impl ScipPtr { Ok(()) } - pub(crate) fn vars(&self) -> BTreeMap> { + pub(crate) fn vars(&self) -> BTreeMap> { // NOTE: this method should only be called once per SCIP instance let n_vars = self.n_vars(); let mut vars = BTreeMap::new(); @@ -149,8 +147,9 @@ impl ScipPtr { unsafe { ffi::SCIPcaptureVar(self.raw, scip_var); } - let var = Rc::new(Variable { raw: scip_var }); - vars.insert(var.index(), var); + let var = Rc::new(RefCell::new(Variable { raw: scip_var, data: None })); + let var_id = var.borrow().index(); + vars.insert(var_id, var); } vars } @@ -214,7 +213,7 @@ impl ScipPtr { ) }; let var_ptr = unsafe { var_ptr.assume_init() }; scip_call! { ffi::SCIPaddVar(self.raw, var_ptr) }; - Ok(Variable { raw: var_ptr }) + Ok(Variable { raw: var_ptr, data: None }) } pub(crate) fn create_var_solving( @@ -242,7 +241,7 @@ impl ScipPtr { scip_call! { ffi::SCIPgetTransformedVar(self.raw, var_ptr, transformed_var.as_mut_ptr()) }; let trans_var_ptr = unsafe { transformed_var.assume_init() }; scip_call! { ffi::SCIPreleaseVar(self.raw, &mut var_ptr) }; - Ok(Variable { raw: trans_var_ptr }) + Ok(Variable { raw: trans_var_ptr, data: None }) } pub(crate) fn create_priced_var( @@ -270,12 +269,12 @@ impl ScipPtr { scip_call! { ffi::SCIPgetTransformedVar(self.raw, var_ptr, transformed_var.as_mut_ptr()) }; let trans_var_ptr = unsafe { transformed_var.assume_init() }; scip_call! { ffi::SCIPreleaseVar(self.raw, &mut var_ptr) }; - Ok(Variable { raw: trans_var_ptr }) + Ok(Variable { raw: trans_var_ptr, data: None }) } pub(crate) fn create_cons( &mut self, - vars: Vec>, + vars: Vec>, coefs: &[f64], lhs: f64, rhs: f64, @@ -296,7 +295,7 @@ impl ScipPtr { ) }; let scip_cons = unsafe { scip_cons.assume_init() }; for (i, var) in vars.iter().enumerate() { - scip_call! { ffi::SCIPaddCoefLinear(self.raw, scip_cons, var.raw, coefs[i]) }; + scip_call! { ffi::SCIPaddCoefLinear(self.raw, scip_cons, var.borrow().raw, coefs[i]) }; } scip_call! { ffi::SCIPaddCons(self.raw, scip_cons) }; Ok(Constraint { raw: scip_cons }) @@ -305,7 +304,7 @@ impl ScipPtr { /// Create set partitioning constraint pub(crate) fn create_cons_set_part( &mut self, - vars: Vec>, + vars: Vec>, name: &str, ) -> Result { let c_name = CString::new(name).unwrap(); @@ -319,7 +318,7 @@ impl ScipPtr { ) }; let scip_cons = unsafe { scip_cons.assume_init() }; for var in vars.iter() { - scip_call! { ffi::SCIPaddCoefSetppc(self.raw, scip_cons, var.raw) }; + scip_call! { ffi::SCIPaddCoefSetppc(self.raw, scip_cons, var.borrow().raw) }; } scip_call! { ffi::SCIPaddCons(self.raw, scip_cons) }; Ok(Constraint { raw: scip_cons }) @@ -328,7 +327,7 @@ impl ScipPtr { /// Create set cover constraint pub(crate) fn create_cons_set_cover( &mut self, - vars: Vec>, + vars: Vec>, name: &str, ) -> Result { let c_name = CString::new(name).unwrap(); @@ -342,7 +341,7 @@ impl ScipPtr { ) }; let scip_cons = unsafe { scip_cons.assume_init() }; for var in vars.iter() { - scip_call! { ffi::SCIPaddCoefSetppc(self.raw, scip_cons, var.raw) }; + scip_call! { ffi::SCIPaddCoefSetppc(self.raw, scip_cons, var.borrow().raw) }; } scip_call! { ffi::SCIPaddCons(self.raw, scip_cons) }; Ok(Constraint { raw: scip_cons }) @@ -350,10 +349,10 @@ impl ScipPtr { pub(crate) fn create_cons_quadratic( &mut self, - lin_vars: Vec>, + lin_vars: Vec>, lin_coefs: &mut [f64], - quad_vars_1: Vec>, - quad_vars_2: Vec>, + quad_vars_1: Vec>, + quad_vars_2: Vec>, quad_coefs: &mut [f64], lhs: f64, rhs: f64, @@ -374,9 +373,9 @@ impl ScipPtr { let c_name = CString::new(name).unwrap(); let mut scip_cons = MaybeUninit::uninit(); - let get_ptrs = |vars: Vec>| { + let get_ptrs = |vars: Vec>| { vars.into_iter() - .map(|var_rc| var_rc.raw) + .map(|var_rc| var_rc.borrow().raw) .collect::>() }; let mut lin_var_ptrs = get_ptrs(lin_vars); @@ -405,7 +404,7 @@ impl ScipPtr { /// Create set packing constraint pub(crate) fn create_cons_set_pack( &mut self, - vars: Vec>, + vars: Vec>, name: &str, ) -> Result { let c_name = CString::new(name).unwrap(); @@ -419,7 +418,7 @@ impl ScipPtr { ) }; let scip_cons = unsafe { scip_cons.assume_init() }; for var in vars.iter() { - scip_call! { ffi::SCIPaddCoefSetppc(self.raw, scip_cons, var.raw) }; + scip_call! { ffi::SCIPaddCoefSetppc(self.raw, scip_cons, var.borrow().raw) }; } scip_call! { ffi::SCIPaddCons(self.raw, scip_cons) }; Ok(Constraint { raw: scip_cons }) @@ -440,9 +439,9 @@ impl ScipPtr { pub(crate) fn add_cons_coef_setppc( &mut self, cons: Rc, - var: Rc, + var: SharedMut, ) -> Result<(), Retcode> { - scip_call! { ffi::SCIPaddCoefSetppc(self.raw, cons.raw, var.raw) }; + scip_call! { ffi::SCIPaddCoefSetppc(self.raw, cons.raw, var.borrow().raw) }; Ok(()) } @@ -472,7 +471,7 @@ impl ScipPtr { let mut cands = Vec::with_capacity(nlpcands as usize); for i in 0..nlpcands { let var_ptr = unsafe { *lpcands.add(i as usize) }; - let var = Rc::new(Variable { raw: var_ptr }); + let var = Rc::new(Variable { raw: var_ptr, data: None }); let lp_sol_val = unsafe { *lpcandssol.add(i as usize) }; let frac = lp_sol_val.fract(); cands.push(BranchingCandidate { @@ -838,11 +837,11 @@ impl ScipPtr { pub(crate) fn add_cons_coef( &mut self, cons: Rc, - var: Rc, + var: SharedMut, coef: f64, ) -> Result<(), Retcode> { let cons_is_transformed = unsafe { ffi::SCIPconsIsTransformed(cons.raw) } == 1; - let var_is_transformed = unsafe { ffi::SCIPvarIsTransformed(var.raw) } == 1; + let var_is_transformed = unsafe { ffi::SCIPvarIsTransformed(var.borrow().raw) } == 1; let cons_ptr = if !cons_is_transformed && var_is_transformed { let mut transformed_cons = MaybeUninit::<*mut ffi::SCIP_Cons>::uninit(); scip_call!(ffi::SCIPgetTransformedCons( @@ -859,12 +858,12 @@ impl ScipPtr { let mut transformed_var = MaybeUninit::<*mut ffi::SCIP_Var>::uninit(); scip_call!(ffi::SCIPgetTransformedVar( self.raw, - var.raw, + var.borrow().raw, transformed_var.as_mut_ptr() )); unsafe { transformed_var.assume_init() } } else { - var.raw + var.borrow().raw }; scip_call! { ffi::SCIPaddCoefLinear(self.raw, cons_ptr, var_ptr, coef) }; diff --git a/src/solution.rs b/src/solution.rs index c875838..bb06759 100644 --- a/src/solution.rs +++ b/src/solution.rs @@ -1,8 +1,9 @@ use std::fmt; +use std::ops::Deref; use std::rc::Rc; use crate::variable::Variable; -use crate::{ffi, scip_call_panic}; +use crate::{ffi, scip_call_panic, SharedMut}; /// A wrapper for a SCIP solution. #[derive(PartialEq, Eq)] @@ -18,13 +19,13 @@ impl Solution { } /// Returns the value of a variable in the solution. - pub fn val(&self, var: Rc) -> f64 { - unsafe { ffi::SCIPgetSolVal(self.scip_ptr, self.raw, var.raw) } + pub fn val(&self, var: SharedMut) -> f64 { + unsafe { ffi::SCIPgetSolVal(self.scip_ptr, self.raw, var.borrow().raw) } } /// Sets the value of a variable in the solution. - pub fn set_val(&self, var: Rc, val: f64) { - scip_call_panic!(ffi::SCIPsetSolVal(self.scip_ptr, self.raw, var.raw, val)); + pub fn set_val(&self, var: SharedMut, val: f64) { + scip_call_panic!(ffi::SCIPsetSolVal(self.scip_ptr, self.raw, var.borrow().raw, val)); } } diff --git a/src/variable.rs b/src/variable.rs index 560c11a..a8ee29e 100644 --- a/src/variable.rs +++ b/src/variable.rs @@ -1,13 +1,21 @@ use crate::ffi; use core::panic; +use std::any::Any; /// A type alias for a variable ID. pub type VarId = usize; /// A wrapper for a mutable reference to a SCIP variable. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug)] pub struct Variable { pub(crate) raw: *mut ffi::SCIP_VAR, + pub(crate) data: Option>, +} + +impl PartialEq for Variable { + fn eq(&self, other: &Self) -> bool { + self.raw == other.raw + } } impl Variable { @@ -57,6 +65,16 @@ impl Variable { let status = unsafe { ffi::SCIPvarGetStatus(self.raw) }; status.into() } + + /// Attaches some data to the variable. + pub fn set_data(&mut self, data: T) { + self.data = Some(Box::new(data)); + } + + /// Returns a reference to the attached data. + pub fn get_data(&self) -> Option<&T> { + self.data.as_ref().and_then(|data| data.downcast_ref::()) + } } /// The type of a variable in an optimization problem. @@ -139,15 +157,31 @@ mod tests { let mut model = Model::new().include_default_plugins().create_prob("test"); let var = model.add_var(0.0, 1.0, 2.0, "x", VarType::ImplInt); - assert_eq!(var.index(), 0); - assert_eq!(var.lb(), 0.0); - assert_eq!(var.ub(), 1.0); - assert_eq!(var.obj(), 2.0); - assert_eq!(var.name(), "x"); - assert_eq!(var.var_type(), VarType::ImplInt); - assert_eq!(var.status(), VarStatus::Original); + assert_eq!(var.borrow().index(), 0); + assert_eq!(var.borrow().lb(), 0.0); + assert_eq!(var.borrow().ub(), 1.0); + assert_eq!(var.borrow().obj(), 2.0); + assert_eq!(var.borrow().name(), "x"); + assert_eq!(var.borrow().var_type(), VarType::ImplInt); + assert_eq!(var.borrow().status(), VarStatus::Original); #[cfg(feature = "raw")] - assert!(!var.inner().is_null()); + assert!(!var.borrow().inner().is_null()); + } + + #[test] + fn var_eq() { + let mut model = Model::new().include_default_plugins().create_prob("test"); + let var1 = model.add_var(0.0, 1.0, 2.0, "x", VarType::Integer); + let var2 = model.add_var(0.0, 1.0, 2.0, "x", VarType::Integer); + assert_ne!(var1, var2); + } + + #[test] + fn attach_data() { + let mut model = Model::new().include_default_plugins().create_prob("test"); + let mut var = model.add_var(0.0, 1.0, 2.0, "x", VarType::ImplInt); + var.borrow_mut().set_data(42); + assert_eq!(var.borrow().get_data::().unwrap(), &42); } }