#[path = "callback.rs"]
pub mod callback;
#[path = "expr.rs"]
pub mod expr;
use ffi;
use itertools::{Itertools, Zip};
use std::cell::Cell;
use std::ffi::CString;
use std::iter;
use std::mem::transmute;
use std::ops::{Deref, DerefMut};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::ptr::{null, null_mut};
use std::rc::Rc;
use std::slice::Iter;
use attr;
use attribute::{Attr, AttrArray};
use self::callback::{Callback, New};
use self::expr::{LinExpr, QuadExpr};
use env::{Env, EnvAPI};
use error::{Error, Result};
use util;
#[derive(Debug,Clone,Copy)]
pub enum VarType {
Binary,
Continuous,
Integer
}
impl Into<ffi::c_char> for VarType {
fn into(self) -> ffi::c_char {
match self {
VarType::Binary => 'B' as ffi::c_char,
VarType::Continuous => 'C' as ffi::c_char,
VarType::Integer => 'I' as ffi::c_char,
}
}
}
#[derive(Debug,Copy,Clone)]
pub enum ConstrSense {
Equal,
Greater,
Less
}
impl Into<ffi::c_char> for ConstrSense {
fn into(self) -> ffi::c_char {
match self {
ConstrSense::Equal => '=' as ffi::c_char,
ConstrSense::Less => '<' as ffi::c_char,
ConstrSense::Greater => '>' as ffi::c_char,
}
}
}
#[derive(Debug,Copy,Clone)]
pub enum ModelSense {
Minimize = 1,
Maximize = -1
}
impl Into<i32> for ModelSense {
fn into(self) -> i32 { (unsafe { transmute::<_, i8>(self) }) as i32 }
}
#[test]
fn modelsense_conversion_success() {
use self::ModelSense;
assert_eq!(Into::<i32>::into(ModelSense::Minimize), 1i32);
assert_eq!(Into::<i32>::into(ModelSense::Maximize), -1i32);
}
#[derive(Debug,Copy,Clone)]
pub enum SOSType {
SOSType1 = 1,
SOSType2 = 2
}
impl Into<i32> for SOSType {
fn into(self) -> i32 { (unsafe { transmute::<_, i8>(self) }) as i32 }
}
#[derive(Debug,Copy,Clone,PartialEq)]
pub enum Status {
Loaded = 1,
Optimal,
Infeasible,
InfOrUnbd,
Unbounded,
CutOff,
IterationLimit,
NodeLimit,
TimeLimit,
SolutionLimit,
Interrupted,
Numeric,
SubOptimal,
InProgress
}
impl From<i32> for Status {
fn from(val: i32) -> Status {
match val {
1...14 => unsafe { transmute(val as i8) },
_ => panic!("cannot convert to Status: {}", val)
}
}
}
#[derive(Debug,Copy,Clone)]
pub enum RelaxType {
Linear = 0,
Quadratic = 1,
Cardinality = 2
}
impl Into<i32> for RelaxType {
fn into(self) -> i32 { (unsafe { transmute::<_, i8>(self) }) as i32 }
}
#[derive(Debug, Clone)]
pub struct Proxy(Rc<Cell<i32>>);
impl Proxy {
fn new(idx: i32) -> Proxy { Proxy(Rc::new(Cell::new(idx))) }
pub fn index(&self) -> i32 { self.0.get() }
fn set_index(&mut self, value: i32) { self.0.set(value) }
pub fn get<A: AttrArray>(&self, model: &Model, attr: A) -> Result<A::Out> { model.get_element(attr, self.index()) }
pub fn set<A: AttrArray>(&self, model: &mut Model, attr: A, val: A::Out) -> Result<()> {
model.set_element(attr, self.index(), val)
}
pub fn remove(&mut self) {
let orig = self.index();
self.set_index(-3 - orig);
}
}
impl PartialEq for Proxy {
fn eq(&self, other: &Proxy) -> bool { self.0.as_ref() as *const Cell<i32> == other.0.as_ref() as *const Cell<i32> }
}
macro_rules! impl_traits_for_proxy {
{$($t:ident)*} => { $(
impl $t {
fn new(idx: i32) -> $t { $t(Proxy::new(idx)) }
}
impl Deref for $t {
type Target = Proxy;
fn deref(&self) -> &Proxy { &self.0 }
}
impl DerefMut for $t {
fn deref_mut(&mut self) -> &mut Proxy { &mut self.0 }
}
impl PartialEq for $t {
fn eq(&self, other:&$t) -> bool { self.0.eq(&other.0) }
}
)* }
}
#[derive(Debug, Clone)]
pub struct Var(Proxy);
impl Var {
pub fn get_type(&self, model: &Model) -> Result<(char, f64, f64)> {
let lb = self.get(&model, attr::LB)?;
let ub = self.get(&model, attr::UB)?;
let vtype: i8 = self.get(&model, attr::VType)?;
let vtype = vtype as u8 as char;
Ok((vtype, lb, ub))
}
}
#[derive(Clone,Debug)]
pub struct Constr(Proxy);
#[derive(Clone,Debug)]
pub struct QConstr(Proxy);
#[derive(Clone,Debug)]
pub struct SOS(Proxy);
impl_traits_for_proxy! { Var Constr QConstr SOS }
struct CallbackData<'a> {
model: &'a Model,
callback: &'a mut FnMut(Callback) -> Result<()>
}
#[allow(unused_variables)]
#[allow(transmute_ptr_to_ref)]
extern "C" fn callback_wrapper(model: *mut ffi::GRBmodel, cbdata: *mut ffi::c_void, loc: ffi::c_int,
usrdata: *mut ffi::c_void)
-> ffi::c_int {
let mut usrdata = unsafe { transmute::<_, &mut CallbackData>(usrdata) };
let (callback, model) = (&mut usrdata.callback, &usrdata.model);
match Callback::new(cbdata, loc.into(), model) {
Err(err) => {
println!("failed to create context: {:?}", err);
-3
}
Ok(context) => {
match catch_unwind(AssertUnwindSafe(|| if callback(context).is_ok() { 0 } else { -1 })) {
Ok(ret) => ret,
Err(e) => -3000,
}
}
}
}
#[allow(unused_variables)]
extern "C" fn null_callback_wrapper(model: *mut ffi::GRBmodel, cbdata: *mut ffi::c_void, loc: ffi::c_int,
usrdata: *mut ffi::c_void)
-> ffi::c_int {
0
}
pub struct Model {
model: *mut ffi::GRBmodel,
env: Env,
updatemode: Option<i32>,
vars: Vec<Var>,
constrs: Vec<Constr>,
qconstrs: Vec<QConstr>,
sos: Vec<SOS>
}
pub trait FromRaw {
fn from_raw(model: *mut ffi::GRBmodel) -> Result<Model>;
}
impl FromRaw for Model {
fn from_raw(model: *mut ffi::GRBmodel) -> Result<Model> {
use env::FromRaw;
let env = unsafe { ffi::GRBgetenv(model) };
if env.is_null() {
return Err(Error::FromAPI("Failed to retrieve GRBenv from given model".to_owned(),
2002));
}
let env = Env::from_raw(env);
let mut model = Model {
model: model,
env: env,
updatemode: None,
vars: Vec::new(),
constrs: Vec::new(),
qconstrs: Vec::new(),
sos: Vec::new()
};
try!(model.populate());
Ok(model)
}
}
impl Model {
pub fn new(modelname: &str, env: &Env) -> Result<Model> {
let modelname = try!(CString::new(modelname));
let mut model = null_mut();
try!(env.check_apicall(unsafe {
ffi::GRBnewmodel(env.get_ptr(),
&mut model,
modelname.as_ptr(),
0,
null(),
null(),
null(),
null(),
null())
}));
Self::from_raw(model)
}
pub fn read_from(filename: &str, env: &Env) -> Result<Model> {
let filename = try!(CString::new(filename));
let mut model = null_mut();
try!(env.check_apicall(unsafe { ffi::GRBreadmodel(env.get_ptr(), filename.as_ptr(), &mut model) }));
Self::from_raw(model)
}
pub fn copy(&self) -> Result<Model> {
let copied = unsafe { ffi::GRBcopymodel(self.model) };
if copied.is_null() {
return Err(Error::FromAPI("Failed to create a copy of the model".to_owned(), 20002));
}
Model::from_raw(copied)
}
pub fn fixed(&self) -> Result<Model> {
let fixed = unsafe { ffi::GRBfixedmodel(self.model) };
if fixed.is_null() {
return Err(Error::FromAPI("failed to create fixed model".to_owned(), 20002));
}
Model::from_raw(fixed)
}
pub fn relax(&self) -> Result<Model> {
let relaxed = unsafe { ffi::GRBrelaxmodel(self.model) };
if relaxed.is_null() {
return Err(Error::FromAPI("failed to create relaxed model".to_owned(), 20002));
}
Model::from_raw(relaxed)
}
pub fn presolve(&self) -> Result<Model> {
let presolved = unsafe { ffi::GRBpresolvemodel(self.model) };
if presolved.is_null() {
return Err(Error::FromAPI("failed to create presolved model".to_owned(), 20002));
}
Model::from_raw(presolved)
}
pub fn feasibility(&self) -> Result<Model> {
let feasibility = unsafe { ffi::GRBfeasibility(self.model) };
if feasibility.is_null() {
return Err(Error::FromAPI("failed to create feasibility model".to_owned(), 20002));
}
Model::from_raw(feasibility)
}
pub fn get_env(&self) -> &Env { &self.env }
pub fn get_env_mut(&mut self) -> &mut Env { &mut self.env }
fn remove_items<P: DerefMut<Target = Proxy> + Clone>(vec: &[P]) -> (Vec<P>, Vec<i32>) {
let (added, removed): (Vec<_>, _) = vec.iter().cloned().partition(|v| v.index() >= -1);
let mut buf = Vec::with_capacity(removed.len());
for mut elem in removed.into_iter() {
if elem.index() < -2 {
buf.push(-3 - elem.index())
}
elem.set_index(-2);
}
(added, buf)
}
fn rearrange<P: DerefMut<Target = Proxy>>(mut vec: Vec<P>) -> Vec<P> {
for (i, mut elem) in vec.iter_mut().enumerate() {
elem.set_index(i as i32);
}
vec
}
pub fn update(&mut self) -> Result<()> {
let (vars, delind) = Self::remove_items(&self.vars);
if !delind.is_empty() {
try!(self.check_apicall(unsafe { ffi::GRBdelvars(self.model, delind.len() as ffi::c_int, delind.as_ptr()) }));
}
let (constrs, delind) = Self::remove_items(&self.constrs);
if !delind.is_empty() {
try!(self.check_apicall(unsafe { ffi::GRBdelconstrs(self.model, delind.len() as ffi::c_int, delind.as_ptr()) }));
}
let (qconstrs, delind) = Self::remove_items(&self.qconstrs);
if !delind.is_empty() {
try!(self.check_apicall(unsafe { ffi::GRBdelqconstrs(self.model, delind.len() as ffi::c_int, delind.as_ptr()) }));
}
let (sos, delind) = Self::remove_items(&self.sos);
if !delind.is_empty() {
try!(self.check_apicall(unsafe { ffi::GRBdelsos(self.model, delind.len() as ffi::c_int, delind.as_ptr()) }));
}
try!(self.check_apicall(unsafe { ffi::GRBupdatemodel(self.model) }));
self.vars = Self::rearrange(vars);
self.constrs = Self::rearrange(constrs);
self.qconstrs = Self::rearrange(qconstrs);
self.sos = Self::rearrange(sos);
self.updatemode = None;
Ok(())
}
fn get_update_mode(&mut self) -> Result<i32> {
match self.updatemode {
Some(mode) => Ok(mode),
None => {
use param;
let mode = try!(self.env.get(param::UpdateMode));
self.updatemode = Some(mode);
Ok(mode)
}
}
}
pub fn optimize(&mut self) -> Result<()> {
try!(self.update());
self.check_apicall(unsafe { ffi::GRBoptimize(self.model) })
}
pub fn optimize_async(&mut self) -> Result<()> {
try!(self.update());
self.check_apicall(unsafe { ffi::GRBoptimizeasync(self.model) })
}
#[allow(useless_transmute)]
pub fn optimize_with_callback<F>(&mut self, mut callback: F) -> Result<()>
where F: FnMut(Callback) -> Result<()> + 'static
{
try!(self.update());
let usrdata = CallbackData {
model: self,
callback: &mut callback
};
try!(self.check_apicall(unsafe { ffi::GRBsetcallbackfunc(self.model, callback_wrapper, transmute(&usrdata)) }));
try!(self.check_apicall(unsafe { ffi::GRBoptimize(self.model) }));
self.check_apicall(unsafe { ffi::GRBsetcallbackfunc(self.model, null_callback_wrapper, null_mut()) })
}
pub fn sync(&self) -> Result<()> { self.check_apicall(unsafe { ffi::GRBsync(self.model) }) }
pub fn compute_iis(&mut self) -> Result<()> { self.check_apicall(unsafe { ffi::GRBcomputeIIS(self.model) }) }
pub fn terminate(&self) { unsafe { ffi::GRBterminate(self.model) } }
pub fn reset(&self) -> Result<()> { self.check_apicall(unsafe { ffi::GRBresetmodel(self.model) }) }
pub fn tune(&self) -> Result<()> { self.check_apicall(unsafe { ffi::GRBtunemodel(self.model) }) }
pub fn get_tune_result(&self, n: i32) -> Result<()> {
self.check_apicall(unsafe { ffi::GRBgettuneresult(self.model, n) })
}
#[deprecated]
pub fn get_concurrent_env(&self, num: i32) -> Result<Env> {
use env::FromRaw;
let env = unsafe { ffi::GRBgetconcurrentenv(self.model, num) };
if env.is_null() {
return Err(Error::FromAPI("Cannot get a concurrent environment.".to_owned(), 20003));
}
Ok(Env::from_raw(env))
}
#[deprecated]
pub fn discard_concurrent_envs(&self) { unsafe { ffi::GRBdiscardconcurrentenvs(self.model) } }
pub fn message(&self, message: &str) { self.env.message(message); }
pub fn read(&mut self, filename: &str) -> Result<()> {
let filename = try!(CString::new(filename));
self.check_apicall(unsafe { ffi::GRBread(self.model, filename.as_ptr()) })
}
pub fn write(&self, filename: &str) -> Result<()> {
let filename = try!(CString::new(filename));
self.check_apicall(unsafe { ffi::GRBwrite(self.model, filename.as_ptr()) })
}
pub fn add_var(&mut self, name: &str, vtype: VarType, obj: f64, lb: f64, ub: f64, colconstrs: &[Constr],
colvals: &[f64])
-> Result<Var> {
if colconstrs.len() != colvals.len() {
return Err(Error::InconsitentDims);
}
let colconstrs = {
let mut buf = Vec::with_capacity(colconstrs.len());
for elem in colconstrs.iter() {
let idx = elem.index();
if idx < 0 {
return Err(Error::InconsitentDims);
}
buf.push(idx);
}
buf
};
let name = try!(CString::new(name));
try!(self.check_apicall(unsafe {
ffi::GRBaddvar(self.model,
colvals.len() as ffi::c_int,
colconstrs.as_ptr(),
colvals.as_ptr(),
obj,
lb,
ub,
vtype.into(),
name.as_ptr())
}));
let col_no = if try!(self.get_update_mode()) != 0 {
self.vars.len() as i32
} else {
-1
};
self.vars.push(Var::new(col_no));
Ok(self.vars.last().cloned().unwrap())
}
pub fn add_vars(&mut self, names: &[&str], vtypes: &[VarType], objs: &[f64], lbs: &[f64], ubs: &[f64],
colconstrs: &[&[Constr]], colvals: &[&[f64]])
-> Result<Vec<Var>> {
if names.len() != vtypes.len() || vtypes.len() != objs.len() || objs.len() != lbs.len() ||
lbs.len() != ubs.len() || ubs.len() != colconstrs.len() || colconstrs.len() != colvals.len() {
return Err(Error::InconsitentDims);
}
let names = {
let mut buf = Vec::with_capacity(names.len());
for &name in names.into_iter() {
let name = try!(CString::new(name));
buf.push(name.as_ptr());
}
buf
};
let vtypes = {
let mut buf = Vec::with_capacity(vtypes.len());
for &vtype in vtypes.into_iter() {
let vtype = vtype.into();
buf.push(vtype);
}
buf
};
let (beg, ind, val) = {
let len_ind = colconstrs.iter().fold(0usize, |e, &c| e + c.len());
let mut buf_beg = Vec::with_capacity(colconstrs.len());
let mut buf_ind = Vec::with_capacity(len_ind);
let mut buf_val: Vec<f64> = Vec::with_capacity(len_ind);
let mut beg = 0i32;
for (constrs, &vals) in Zip::new((colconstrs, colvals)) {
if constrs.len() != vals.len() {
return Err(Error::InconsitentDims);
}
buf_beg.push(beg);
beg += constrs.len() as i32;
for c in constrs.iter() {
let idx = c.index();
if idx < 0 {
return Err(Error::InconsitentDims);
}
buf_ind.push(idx);
}
buf_val.extend(vals);
}
(buf_beg, buf_ind, buf_val)
};
try!(self.check_apicall(unsafe {
ffi::GRBaddvars(self.model,
names.len() as ffi::c_int,
beg.len() as ffi::c_int,
beg.as_ptr(),
ind.as_ptr(),
val.as_ptr(),
objs.as_ptr(),
lbs.as_ptr(),
ubs.as_ptr(),
vtypes.as_ptr(),
names.as_ptr())
}));
let mode = try!(self.get_update_mode());
let xcols = self.vars.len();
let cols = self.vars.len() + names.len();
for col_no in xcols..cols {
self.vars.push(Var::new(if mode != 0 { col_no as i32 } else { -1 }));
}
Ok(self.vars[xcols..].iter().cloned().collect_vec())
}
pub fn add_constr(&mut self, name: &str, expr: LinExpr, sense: ConstrSense, rhs: f64) -> Result<Constr> {
let constrname = try!(CString::new(name));
let (vars, coeff, offset) = expr.into();
try!(self.check_apicall(unsafe {
ffi::GRBaddconstr(self.model,
coeff.len() as ffi::c_int,
vars.as_ptr(),
coeff.as_ptr(),
sense.into(),
rhs - offset,
constrname.as_ptr())
}));
let row_no = if try!(self.get_update_mode()) != 0 {
self.constrs.len() as i32
} else {
-1
};
self.constrs.push(Constr::new(row_no));
Ok(self.constrs.last().cloned().unwrap())
}
pub fn add_constrs(&mut self, name: &[&str], expr: &[LinExpr], sense: &[ConstrSense], rhs: &[f64])
-> Result<Vec<Constr>> {
let mut constrnames = Vec::with_capacity(name.len());
for &s in name.iter() {
let name = try!(CString::new(s));
constrnames.push(name.as_ptr());
}
let expr: Vec<(_, _, _)> = expr.into_iter().cloned().map(|e| e.into()).collect_vec();
let sense = sense.iter().map(|&s| s.into()).collect_vec();
let rhs = Zip::new((rhs, &expr)).map(|(rhs, expr)| rhs - expr.2).collect_vec();
let mut beg = Vec::with_capacity(expr.len());
let numnz = expr.iter().map(|expr| expr.0.len()).sum();
let mut ind = Vec::with_capacity(numnz);
let mut val = Vec::with_capacity(numnz);
for expr in expr.iter() {
let nz = ind.len();
beg.push(nz as i32);
ind.extend(&expr.0);
val.extend(&expr.1);
}
try!(self.check_apicall(unsafe {
ffi::GRBaddconstrs(self.model,
constrnames.len() as ffi::c_int,
beg.len() as ffi::c_int,
beg.as_ptr(),
ind.as_ptr(),
val.as_ptr(),
sense.as_ptr(),
rhs.as_ptr(),
constrnames.as_ptr())
}));
let mode = try!(self.get_update_mode());
let xrows = self.constrs.len();
let rows = self.constrs.len() + constrnames.len();
for row_no in xrows..rows {
self.constrs.push(Constr::new(if mode != 0 { row_no as i32 } else { -1 }));
}
Ok(self.constrs[xrows..].iter().cloned().collect_vec())
}
pub fn add_range(&mut self, name: &str, expr: LinExpr, lb: f64, ub: f64) -> Result<(Var, Constr)> {
let constrname = try!(CString::new(name));
let (vars, coeff, offset) = expr.into();
try!(self.check_apicall(unsafe {
ffi::GRBaddrangeconstr(self.model,
coeff.len() as ffi::c_int,
vars.as_ptr(),
coeff.as_ptr(),
lb - offset,
ub - offset,
constrname.as_ptr())
}));
let mode = try!(self.get_update_mode());
let col_no = if mode != 0 {
self.vars.len() as i32
} else {
-1
};
self.vars.push(Var::new(col_no));
let row_no = if mode != 0 {
self.constrs.len() as i32
} else {
-1
};
self.constrs.push(Constr::new(row_no));
Ok((self.vars.last().cloned().unwrap(), self.constrs.last().cloned().unwrap()))
}
pub fn add_ranges(&mut self, names: &[&str], expr: &[LinExpr], lb: &[f64], ub: &[f64])
-> Result<(Vec<Var>, Vec<Constr>)> {
let mut constrnames = Vec::with_capacity(names.len());
for &s in names.iter() {
let name = try!(CString::new(s));
constrnames.push(name.as_ptr());
}
let expr: Vec<(_, _, _)> = expr.into_iter().cloned().map(|e| e.into()).collect_vec();
let lhs = Zip::new((lb, &expr)).map(|(lb, expr)| lb - expr.2).collect_vec();
let rhs = Zip::new((ub, &expr)).map(|(ub, expr)| ub - expr.2).collect_vec();
let mut beg = Vec::with_capacity(expr.len());
let numnz = expr.iter().map(|expr| expr.0.len()).sum();
let mut ind = Vec::with_capacity(numnz);
let mut val = Vec::with_capacity(numnz);
for expr in expr.iter() {
let nz = ind.len();
beg.push(nz as i32);
ind.extend(&expr.0);
val.extend(&expr.1);
}
try!(self.check_apicall(unsafe {
ffi::GRBaddrangeconstrs(self.model,
constrnames.len() as ffi::c_int,
beg.len() as ffi::c_int,
beg.as_ptr(),
ind.as_ptr(),
val.as_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
constrnames.as_ptr())
}));
let mode = try!(self.get_update_mode());
let xcols = self.vars.len();
let cols = self.vars.len() + names.len();
for col_no in xcols..cols {
self.vars.push(Var::new(if mode != 0 { col_no as i32 } else { -1 }));
}
let xrows = self.constrs.len();
let rows = self.constrs.len() + constrnames.len();
for row_no in xrows..rows {
self.constrs.push(Constr::new(if mode != 0 { row_no as i32 } else { -1 }));
}
Ok((self.vars[xcols..].iter().cloned().collect_vec(), self.constrs[xrows..].iter().cloned().collect_vec()))
}
pub fn add_qconstr(&mut self, constrname: &str, expr: QuadExpr, sense: ConstrSense, rhs: f64) -> Result<QConstr> {
let constrname = try!(CString::new(constrname));
let (lind, lval, qrow, qcol, qval, offset) = expr.into();
try!(self.check_apicall(unsafe {
ffi::GRBaddqconstr(self.model,
lval.len() as ffi::c_int,
lind.as_ptr(),
lval.as_ptr(),
qval.len() as ffi::c_int,
qrow.as_ptr(),
qcol.as_ptr(),
qval.as_ptr(),
sense.into(),
rhs - offset,
constrname.as_ptr())
}));
let qrow_no = if try!(self.get_update_mode()) != 0 {
self.qconstrs.len() as i32
} else {
-1
};
self.qconstrs.push(QConstr::new(qrow_no));
Ok(self.qconstrs.last().cloned().unwrap())
}
pub fn add_sos(&mut self, vars: &[Var], weights: &[f64], sostype: SOSType) -> Result<SOS> {
if vars.len() != weights.len() {
return Err(Error::InconsitentDims);
}
let vars = vars.iter().map(|v| v.index()).collect_vec();
let beg = 0;
try!(self.check_apicall(unsafe {
ffi::GRBaddsos(self.model,
1,
vars.len() as ffi::c_int,
&sostype.into(),
&beg,
vars.as_ptr(),
weights.as_ptr())
}));
let sos_no = if try!(self.get_update_mode()) != 0 {
self.sos.len() as i32
} else {
-1
};
self.sos.push(SOS::new(sos_no));
Ok(self.sos.last().cloned().unwrap())
}
pub fn set_objective<Expr: Into<QuadExpr>>(&mut self, expr: Expr, sense: ModelSense) -> Result<()> {
if !self.updatemode.is_none() {
return Err(Error::FromAPI("The objective function cannot be set before any pending modifies existed".to_owned(),
50000));
}
let (lind, lval, qrow, qcol, qval, _) = Into::<QuadExpr>::into(expr).into();
try!(self.del_qpterms());
try!(self.add_qpterms(qrow.as_slice(), qcol.as_slice(), qval.as_slice()));
try!(self.set_list(attr::Obj, lind.as_slice(), lval.as_slice()));
self.set(attr::ModelSense, sense.into())
}
pub fn get<A: Attr>(&self, attr: A) -> Result<A::Out> {
let mut value: A::Buf = util::Init::init();
try!(self.check_apicall(unsafe {
use util::AsRawPtr;
A::get_attr(self.model, attr.into().as_ptr(), value.as_rawptr())
}));
Ok(util::Into::into(value))
}
pub fn set<A: Attr>(&mut self, attr: A, value: A::Out) -> Result<()> {
try!(self.check_apicall(unsafe { A::set_attr(self.model, attr.into().as_ptr(), util::From::from(value)) }));
self.update()
}
fn get_element<A: AttrArray>(&self, attr: A, element: i32) -> Result<A::Out> {
if element < 0 {
return Err(Error::InconsitentDims);
}
let mut value: A::Buf = util::Init::init();
try!(self.check_apicall(unsafe {
use util::AsRawPtr;
A::get_attrelement(self.model, attr.into().as_ptr(), element, value.as_rawptr())
}));
Ok(util::Into::into(value))
}
fn set_element<A: AttrArray>(&mut self, attr: A, element: i32, value: A::Out) -> Result<()> {
if element < 0 {
return Err(Error::InconsitentDims);
}
try!(self.check_apicall(unsafe {
A::set_attrelement(self.model,
attr.into().as_ptr(),
element,
util::From::from(value))
}));
self.update()
}
pub fn get_values<A: AttrArray, P>(&self, attr: A, item: &[P]) -> Result<Vec<A::Out>>
where P: Deref<Target = Proxy>
{
self.get_list(attr,
item.iter().map(|e| e.index()).collect_vec().as_slice())
}
fn get_list<A: AttrArray>(&self, attr: A, ind: &[i32]) -> Result<Vec<A::Out>> {
let mut values: Vec<_> = iter::repeat(util::Init::init()).take(ind.len()).collect();
let ind = {
let mut buf = Vec::with_capacity(ind.len());
for &i in ind {
if i < 0 {
return Err(Error::InconsitentDims);
}
buf.push(i);
}
buf
};
try!(self.check_apicall(unsafe {
A::get_attrlist(self.model,
attr.into().as_ptr(),
ind.len() as ffi::c_int,
ind.as_ptr(),
values.as_mut_ptr())
}));
Ok(values.into_iter().map(util::Into::into).collect())
}
pub fn set_values<A: AttrArray, P>(&mut self, attr: A, item: &[P], val: &[A::Out]) -> Result<()>
where P: Deref<Target = Proxy>
{
try!(self.set_list(attr,
item.iter().map(|e| e.index()).collect_vec().as_slice(),
val));
self.update()
}
fn set_list<A: AttrArray>(&mut self, attr: A, ind: &[i32], values: &[A::Out]) -> Result<()> {
if ind.len() != values.len() {
return Err(Error::InconsitentDims);
}
let ind = {
let mut buf = Vec::with_capacity(ind.len());
for &i in ind {
if i < 0 {
return Err(Error::InconsitentDims);
}
buf.push(i);
}
buf
};
let values = try!(A::to_rawsets(values));
assert_eq!(ind.len(), values.len());
self.check_apicall(unsafe {
A::set_attrlist(self.model,
attr.into().as_ptr(),
values.len() as ffi::c_int,
ind.as_ptr(),
values.as_ptr())
})
}
pub fn feas_relax(&mut self, relaxtype: RelaxType, minrelax: bool, vars: &[Var], lbpen: &[f64], ubpen: &[f64],
constrs: &[Constr], rhspen: &[f64])
-> Result<(f64, Iter<Var>, Iter<Constr>, Iter<QConstr>)> {
if vars.len() != lbpen.len() || vars.len() != ubpen.len() {
return Err(Error::InconsitentDims);
}
if constrs.len() != rhspen.len() {
return Err(Error::InconsitentDims);
}
let mut pen_lb = vec![super::INFINITY; self.vars.len()];
let mut pen_ub = vec![super::INFINITY; self.vars.len()];
for (v, &lb, &ub) in Zip::new((vars, lbpen, ubpen)) {
let idx = v.index();
if idx >= self.vars.len() as i32 {
return Err(Error::InconsitentDims);
}
pen_lb[idx as usize] = lb;
pen_ub[idx as usize] = ub;
}
let mut pen_rhs = vec![super::INFINITY; self.constrs.len()];
for (c, &rhs) in Zip::new((constrs, rhspen)) {
let idx = c.index();
if idx >= self.constrs.len() as i32 {
return Err(Error::InconsitentDims);
}
pen_rhs[idx as usize] = rhs;
}
let minrelax = if minrelax { 1 } else { 0 };
let feasobj = 0f64;
try!(self.check_apicall(unsafe {
ffi::GRBfeasrelax(self.model,
relaxtype.into(),
minrelax,
pen_lb.as_ptr(),
pen_ub.as_ptr(),
pen_rhs.as_ptr(),
&feasobj)
}));
try!(self.update());
let cols = try!(self.get(attr::NumVars)) as usize;
let rows = try!(self.get(attr::NumConstrs)) as usize;
let qrows = try!(self.get(attr::NumQConstrs)) as usize;
let xcols = self.vars.len();
let xrows = self.constrs.len();
let xqrows = self.qconstrs.len();
self.vars.extend((xcols..cols).map(|idx| Var::new(idx as i32)));
self.constrs.extend((xrows..rows).map(|idx| Constr::new(idx as i32)));
self.qconstrs.extend((xqrows..qrows).map(|idx| QConstr::new(idx as i32)));
Ok((feasobj, self.vars[xcols..].iter(), self.constrs[xrows..].iter(), self.qconstrs[xqrows..].iter()))
}
pub fn set_pwl_obj(&mut self, var: &Var, x: &[f64], y: &[f64]) -> Result<()> {
if x.len() != y.len() {
return Err(Error::InconsitentDims);
}
try!(self.check_apicall(unsafe {
ffi::GRBsetpwlobj(self.model,
var.index(),
x.len() as ffi::c_int,
x.as_ptr(),
y.as_ptr())
}));
self.update()
}
pub fn status(&self) -> Result<Status> { self.get(attr::Status).map(|val| val.into()) }
pub fn get_vars(&self) -> Iter<Var> { self.vars.iter() }
pub fn get_constrs(&self) -> Iter<Constr> { self.constrs.iter() }
pub fn get_qconstrs(&self) -> Iter<QConstr> { self.qconstrs.iter() }
pub fn get_sos(&self) -> Iter<SOS> { self.sos.iter() }
pub fn remove<P: DerefMut<Target = Proxy>>(&mut self, mut item: P) { item.remove() }
pub fn get_coeff(&self, var: &Var, constr: &Constr) -> Result<f64> {
let mut value = 0.0;
try!(self.check_apicall(unsafe { ffi::GRBgetcoeff(self.model, var.index(), constr.index(), &mut value) }));
Ok(value)
}
pub fn set_coeff(&mut self, var: &Var, constr: &Constr, value: f64) -> Result<()> {
try!(self.check_apicall(unsafe { ffi::GRBchgcoeffs(self.model, 1, &constr.index(), &var.index(), &value) }));
self.update()
}
pub fn set_coeffs(&mut self, vars: &[&Var], constrs: &[&Constr], values: &[f64]) -> Result<()> {
if vars.len() != values.len() || constrs.len() != values.len() {
return Err(Error::InconsitentDims);
}
let vars = vars.iter().map(|v| v.index()).collect_vec();
let constrs = constrs.iter().map(|c| c.index()).collect_vec();
try!(self.check_apicall(unsafe {
ffi::GRBchgcoeffs(self.model,
vars.len() as ffi::c_int,
constrs.as_ptr(),
vars.as_ptr(),
values.as_ptr())
}));
self.update()
}
fn populate(&mut self) -> Result<()> {
let cols = try!(self.get(attr::NumVars)) as usize;
let rows = try!(self.get(attr::NumConstrs)) as usize;
let numqconstrs = try!(self.get(attr::NumQConstrs)) as usize;
let numsos = try!(self.get(attr::NumSOS)) as usize;
self.vars = (0..cols).map(|idx| Var::new(idx as i32)).collect_vec();
self.constrs = (0..rows).map(|idx| Constr::new(idx as i32)).collect_vec();
self.qconstrs = (0..numqconstrs).map(|idx| QConstr::new(idx as i32)).collect_vec();
self.sos = (0..numsos).map(|idx| SOS::new(idx as i32)).collect_vec();
self.updatemode = None;
Ok(())
}
fn add_qpterms(&mut self, qrow: &[i32], qcol: &[i32], qval: &[f64]) -> Result<()> {
try!(self.check_apicall(unsafe {
ffi::GRBaddqpterms(self.model,
qrow.len() as ffi::c_int,
qrow.as_ptr(),
qcol.as_ptr(),
qval.as_ptr())
}));
self.update()
}
fn del_qpterms(&mut self) -> Result<()> {
try!(self.check_apicall(unsafe { ffi::GRBdelq(self.model) }));
self.update()
}
fn check_apicall(&self, error: ffi::c_int) -> Result<()> {
if error != 0 {
use env::ErrorFromAPI;
return Err(self.env.error_from_api(error));
}
Ok(())
}
}
impl Drop for Model {
fn drop(&mut self) {
unsafe { ffi::GRBfreemodel(self.model) };
self.model = null_mut();
}
}
#[test]
fn removing_variable_should_be_successed() {
use super::*;
let mut env = Env::new("").unwrap();
env.set(param::OutputFlag, 0).unwrap();
let mut model = Model::new("hoge", &env).unwrap();
let x = model.add_var("x", Binary, 0.0, 0.0, 1.0, &[], &[]).unwrap();
let y = model.add_var("y", Binary, 0.0, 0.0, 1.0, &[], &[]).unwrap();
assert_eq!(x.index(), -1);
assert_eq!(y.index(), -1);
model.update().unwrap();
assert_eq!(x.index(), 0);
assert_eq!(y.index(), 1);
let z = model.add_var("z", Binary, 0.0, 0.0, 1.0, &[], &[]).unwrap();
assert_eq!(x.index(), 0);
assert_eq!(y.index(), 1);
assert_eq!(z.index(), -1);
model.update().unwrap();
assert_eq!(x.index(), 0);
assert_eq!(y.index(), 1);
assert_eq!(z.index(), 2);
model.remove(y.clone());
assert_eq!(x.index(), 0);
assert_eq!(y.index(), -4);
assert_eq!(z.index(), 2);
model.update().unwrap();
assert_eq!(x.index(), 0);
assert_eq!(y.index(), -2);
assert_eq!(z.index(), 1);
assert_eq!(model.get(attr::NumVars).unwrap(), 2);
}