cplex_dynamic/
load.rs

1// #[macro_use]
2// extern crate lazy_static;
3
4extern crate dlopen;
5// #[macro_use]
6// extern crate dlopen_derive;
7use dlopen::wrapper::{Container, WrapperApi};
8use std::path::PathBuf;
9
10// #[macro_use]
11// extern crate const_cstr;
12extern crate libc;
13use libc::{c_char, c_double, c_int};
14use std::ffi::CString;
15// extern crate dunce;
16
17/// Used by CPLEX to represent a variable that has no upper bound.
18pub const INFINITY: f64 = 1.0E+20;
19pub const EPINT_ID: c_int = 2010; // identifier of integrality tolerance parameter 'EpInt'
20pub const EPINT: c_double = 1e-5; // default value for 'EpInt'
21
22pub enum CEnv {}
23
24pub enum CProblem {}
25
26type CInt = c_int;
27
28lazy_static! {
29    static ref CPLEX_API: dlopen::wrapper::Container<Api> = {
30        let mut lib_path: Option<PathBuf> = None;
31        #[cfg(target_os = "linux")]
32        let pattern = "./**/cplex*.so";
33        #[cfg(target_os = "windows")]
34        let pattern = "./**/cplex*.dll";
35        #[cfg(target_os = "macos")]
36        let pattern = "./**/cplex*.dylib";
37
38        for entry in glob::glob(pattern).unwrap() {
39            match entry {
40                Ok(path) => {
41                    lib_path = Some(path);
42                    break;
43                }
44                Err(e) => panic!("glob error! {:?}", e),
45            }
46        }
47        if lib_path.is_none() {
48            println!("Should first put lib file (looking like `cplex***.dll` or `cplex***.so`) under the root folder or its subfolder.")
49        }
50        let cont: Container<Api> = unsafe { Container::load(lib_path.unwrap()) }
51            .expect("Could not open library or load symbols");
52        cont
53    };
54}
55
56#[allow(non_snake_case)]
57#[allow(dead_code)]
58#[derive(WrapperApi)]
59struct Api {
60    CPXopenCPLEX: fn(status: *mut c_int) -> *mut CEnv,
61    CPXcreateprob: fn(env: *mut CEnv, status: *mut c_int, name: *const c_char) -> *mut CProblem,
62    CPXsetintparam: fn(env: *mut CEnv, param: c_int, value: c_int) -> c_int,
63    CPXsetdblparam: fn(env: *mut CEnv, param: c_int, value: c_double) -> c_int,
64    CPXgetintparam: fn(env: *mut CEnv, param: c_int, value: *mut c_int) -> c_int,
65    CPXgetdblparam: fn(env: *mut CEnv, param: c_int, value: *mut c_double) -> c_int,
66    CPXchgprobtype: fn(env: *mut CEnv, lp: *mut CProblem, ptype: c_int) -> c_int,
67    // adding variables and constraints
68    CPXnewcols: fn(
69        env: *mut CEnv,
70        lp: *mut CProblem,
71        count: CInt,
72        obj: *const c_double,
73        lb: *const c_double,
74        ub: *const c_double,
75        xctype: *const c_char,
76        name: *const *const c_char,
77    ) -> c_int,
78    CPXaddrows: fn(
79        env: *mut CEnv,
80        lp: *mut CProblem,
81        col_count: CInt,
82        row_count: CInt,
83        nz_count: CInt,
84        rhs: *const c_double,
85        sense: *const c_char,
86        rmatbeg: *const CInt,
87        rmatind: *const CInt,
88        rmatval: *const c_double,
89        col_name: *const *const c_char,
90        row_name: *const *const c_char,
91    ) -> c_int,
92    CPXaddcols: fn(
93        env: *mut CEnv,
94        lp: *mut CProblem,
95        col_count: CInt,
96        nz_count: CInt,
97        obj: *const c_double,
98        cmatbeg: *const CInt,
99        cmatind: *const CInt,
100        cmatval: *const c_double,
101        lb: *const c_double,
102        ub: *const c_double,
103        col_name: *const *const c_char,
104    ) -> c_int,
105    CPXaddlazyconstraints: fn(
106        env: *mut CEnv,
107        lp: *mut CProblem,
108        row_count: CInt,
109        nz_count: CInt,
110        rhs: *const c_double,
111        sense: *const c_char,
112        rmatbeg: *const CInt,
113        rmatind: *const CInt,
114        rmatval: *const c_double,
115        row_name: *const *const c_char,
116    ) -> c_int,
117    // querying
118    CPXgetnumcols: fn(env: *const CEnv, lp: *mut CProblem) -> CInt,
119    // setting objective
120    CPXchgobj: fn(
121        env: *mut CEnv,
122        lp: *mut CProblem,
123        cnt: CInt,
124        indices: *const CInt,
125        values: *const c_double,
126    ) -> c_int,
127    // changes the coefficient in the quadratic objective
128    CPXchgqpcoef:
129        fn(env: *mut CEnv, lp: *mut CProblem, i: CInt, j: CInt, newvalue: c_double) -> c_int,
130
131    // accesses the quadratic coefficient
132    CPXgetqpcoef: fn(
133        env: *mut CEnv,
134        lp: *mut CProblem,
135        row_num: CInt,
136        col_num: CInt,
137        coef_p: *mut c_double,
138    ) -> c_int,
139
140    CPXchgobjsen: fn(env: *mut CEnv, lp: *mut CProblem, maxormin: c_int) -> c_int,
141    // solving
142    CPXlpopt: fn(env: *mut CEnv, lp: *mut CProblem) -> c_int,
143    CPXmipopt: fn(env: *mut CEnv, lp: *mut CProblem) -> c_int,
144    // getting solution
145    CPXgetstat: fn(env: *mut CEnv, lp: *mut CProblem) -> c_int,
146    CPXgetobjval: fn(env: *mut CEnv, lp: *mut CProblem, objval: *mut c_double) -> c_int,
147    CPXgetx:
148        fn(env: *mut CEnv, lp: *mut CProblem, x: *mut c_double, begin: CInt, end: CInt) -> c_int,
149    CPXsolution: fn(
150        env: *mut CEnv,
151        lp: *mut CProblem,
152        lpstat_p: *mut c_int,
153        objval_p: *mut c_double,
154        x: *mut c_double,
155        pi: *mut c_double,
156        slack: *mut c_double,
157        dj: *mut c_double,
158    ) -> c_int,
159    // adding initial solution
160    CPXaddmipstarts: fn(
161        env: *mut CEnv,
162        lp: *mut CProblem,
163        mcnt: CInt,
164        nzcnt: CInt,
165        beg: *const CInt,
166        varindices: *const CInt,
167        values: *const c_double,
168        effortlevel: *const CInt,
169        mipstartname: *const *const c_char,
170    ) -> c_int,
171    // debugging
172    CPXgeterrorstring: fn(env: *mut CEnv, errcode: c_int, buff: *mut c_char) -> *mut c_char,
173    CPXwriteprob:
174        fn(env: *mut CEnv, lp: *mut CProblem, fname: *const c_char, ftype: *const c_char) -> c_int,
175    // freeing
176    CPXcloseCPLEX: fn(env: *const *mut CEnv) -> c_int,
177    CPXfreeprob: fn(env: *mut CEnv, lp: *const *mut CProblem) -> c_int,
178    // continuous quadratic optimization
179    CPXqpopt: fn(env: *mut CEnv, lp: *mut CProblem) -> c_int,
180}
181
182fn errstr(env: *mut CEnv, errcode: c_int) -> Result<String, String> {
183    unsafe {
184        let mut buf = vec![0i8; 1024];
185        let res = CPLEX_API.CPXgeterrorstring(env, errcode, buf.as_mut_ptr());
186        if res == std::ptr::null_mut() {
187            Err(format!("No error string for {}", errcode))
188        } else {
189            Ok(String::from_utf8(
190                buf.iter()
191                    .take_while(|&&i| i != 0 && i != '\n' as i8)
192                    .map(|&i| i as u8)
193                    .collect::<Vec<u8>>(),
194            )
195            .unwrap())
196        }
197    }
198}
199
200#[derive(Copy, Clone, Debug)]
201enum ParamType {
202    Integer(c_int),
203    Double(c_double),
204    Boolean(c_int),
205}
206
207#[derive(Copy, Clone, Debug)]
208pub enum EnvParam {
209    Threads(u64),
210    ScreenOutput(bool),
211    RelativeGap(f64),
212    /// When true, set CPX_PARALLEL_DETERMINISTIC (default). When false, set
213    /// CPX_PARALLEL_OPPORTUNISTIC.
214    ParallelDeterministic(bool),
215}
216
217impl EnvParam {
218    fn to_id(&self) -> c_int {
219        use EnvParam::*;
220        match self {
221            &Threads(_) => 1067,
222            &ScreenOutput(_) => 1035,
223            &RelativeGap(_) => 2009,
224            &ParallelDeterministic(_) => 1109,
225        }
226    }
227
228    fn param_type(&self) -> ParamType {
229        use EnvParam::*;
230        use ParamType::*;
231        match self {
232            &Threads(t) => Integer(t as c_int),
233            &ScreenOutput(b) => Boolean(b as c_int),
234            &RelativeGap(g) => Double(g as c_double),
235            &ParallelDeterministic(b) => Integer(if b { 1 } else { -1 }),
236        }
237    }
238}
239
240/// A CPLEX Environment. An `Env` is necessary to create a
241/// `Problem`.
242pub struct Env {
243    inner: *mut CEnv,
244}
245
246impl Env {
247    pub fn new() -> Result<Env, String> {
248        unsafe {
249            let mut status = 0;
250            // println!("env = 111");
251            // let env = CPXopenCPLEX(&mut status);
252
253            let env = CPLEX_API.CPXopenCPLEX(&mut status);
254            // println!("env = {:?}", env);
255            if env == std::ptr::null_mut() {
256                Err(format!(
257                    "CPLEX returned NULL for CPXopenCPLEX (status: {})",
258                    status
259                ))
260            } else {
261                // CPXsetintparam(env, 1035, 1); //ScreenOutput
262                // CPXsetintparam(env, 1056, 1); //Read_DataCheck
263                Ok(Env { inner: env })
264            }
265        }
266    }
267
268    /// Set an environment parameter. e.g.
269    ///
270    /// ```
271    /// use rplex::{Env, EnvParam};
272    ///
273    /// let mut env = Env::new().unwrap();
274    /// env.set_param(EnvParam::ScreenOutput(true)).unwrap();
275    /// env.set_param(EnvParam::RelativeGap(0.01)).unwrap();
276    /// ```
277    pub fn set_param(&mut self, p: EnvParam) -> Result<(), String> {
278        unsafe {
279            let status = match p.param_type() {
280                ParamType::Integer(i) => CPLEX_API.CPXsetintparam(self.inner, p.to_id(), i),
281                ParamType::Boolean(b) => CPLEX_API.CPXsetintparam(self.inner, p.to_id(), b),
282                ParamType::Double(d) => CPLEX_API.CPXsetdblparam(self.inner, p.to_id(), d),
283            };
284
285            if status != 0 {
286                return match errstr(self.inner, status) {
287                    Ok(s) => Err(s),
288                    Err(e) => Err(e),
289                };
290            } else {
291                return Ok(());
292            }
293        }
294    }
295}
296
297impl Drop for Env {
298    fn drop(&mut self) {
299        unsafe {
300            assert!(CPLEX_API.CPXcloseCPLEX(&self.inner) == 0);
301        }
302    }
303}
304
305/// A Variable in a Problem.
306///
307/// The general usage pattern is to create Variables outside of a
308/// Problem with `var!(...)` and then add them to the Problem with
309/// `prob.add_variable(...)`.
310///
311/// ```
312/// #[macro_use]
313/// extern crate rplex;
314///
315/// use rplex::{Env, Problem, Variable};
316///
317/// fn main() {
318///     let env = Env::new().unwrap();
319///     let mut prob = Problem::new(&env, "dummy").unwrap();
320///     prob.add_variable(var!("x" -> 4.0 as Binary)).unwrap();
321///     prob.add_variable(var!(0.0 <= "y" <= 100.0  -> 3.0 as Integer)).unwrap();
322///     prob.add_variable(var!(0.0 <= "z" <= 4.5 -> 2.0)).unwrap();
323/// }
324/// ```
325#[derive(Clone, Debug)]
326pub struct Variable {
327    index: Option<usize>,
328    ty: VariableType,
329    obj: f64,
330    lb: f64,
331    ub: f64,
332    name: String,
333}
334
335impl Variable {
336    pub fn new<S>(ty: VariableType, obj: f64, lb: f64, ub: f64, name: S) -> Variable
337    where
338        S: Into<String>,
339    {
340        Variable {
341            index: None,
342            ty: ty,
343            obj: obj,
344            lb: lb,
345            ub: ub,
346            name: name.into(),
347        }
348    }
349}
350
351/// Expressive creation of variables.
352///
353/// The general syntax is:
354///
355/// `var!(lower <= "name" <= upper -> objective as type)`
356///
357/// See `Variable` for examples.
358#[macro_export]
359macro_rules! var {
360    ($lb:tt <= $name:tt <= $ub:tt -> $obj:tt as $vt:path) => {
361        {
362            use $crate::VariableType::*;
363            $crate::Variable::new ($vt, $obj, $lb, $ub, $name)
364        }
365    };
366    // continuous shorthand
367    ($lb:tt <= $name:tt <= $ub:tt -> $obj:tt) => (var!($lb <= $name <= $ub -> $obj as Continuous));
368    // omit either lb or ub
369    ($lb:tt <= $name:tt -> $obj:tt) => (var!($lb <= $name <= INFINITY -> $obj));
370    ($name:tt <= $ub:tt -> $obj:tt) => (var!(0.0 <= $name <= $ub -> $obj));
371    // omit both
372    ($name:tt -> $obj:tt) => (var!(0.0 <= $name -> $obj));
373
374    // typed version
375    ($lb:tt <= $name:tt -> $obj:tt as $vt:path) => (var!($lb <= $name <= INFINITY -> $obj as $vt));
376    ($name:tt <= $ub:tt -> $obj:tt as $vt:path) => (var!(0.0 <= $name <= $ub -> $obj as $vt));
377    ($name:tt -> $obj:tt as Binary) => (var!(0.0 <= $name <= 1.0 -> $obj as Binary));
378    ($name:tt -> $obj:tt as $vt:path) => (var!(0.0 <= $name -> $obj as $vt));
379}
380
381/// A variable with weight (row) coefficient. Used in the construction
382/// of `Constraint`s.
383#[derive(Clone, Debug)]
384pub struct WeightedVariable {
385    var: usize,
386    weight: f64,
387}
388
389impl WeightedVariable {
390    /// Create a `WeightedVariable` from a `Variable`. Does not keep a
391    /// borrowed copy of `var`.
392    pub fn new_var(var: &Variable, weight: f64) -> Self {
393        WeightedVariable {
394            var: var.index.unwrap(),
395            weight: weight,
396        }
397    }
398
399    /// Create a `WeightedVariable` from a column index. This method
400    /// is used by the `con!` macro, as the return value of
401    /// `prob.add_variable` is a column index and thus the most common
402    /// value.
403    pub fn new_idx(idx: usize, weight: f64) -> Self {
404        WeightedVariable {
405            var: idx,
406            weight: weight,
407        }
408    }
409}
410
411/// A Constraint (row) object for a `Problem`.
412///
413/// The recommended way to build these is with the `con!` macro.
414///
415/// ```
416/// #[macro_use]
417/// extern crate rplex;
418///
419/// use rplex::{Env, Problem, Variable};
420///
421/// fn main() {
422///     let env = Env::new().unwrap();
423///     let mut prob = Problem::new(&env, "dummy").unwrap();
424///     let x = prob.add_variable(var!("x" -> 4.0 as Binary)).unwrap();
425///     let y = prob.add_variable(var!(0.0 <= "y" <= 100.0  -> 3.0 as Integer)).unwrap();
426///     let z = prob.add_variable(var!(0.0 <= "z" <= 4.5 -> 2.0)).unwrap();
427///     prob.add_constraint(con!("dummy": 20.0 = 1.0 x + 2.0 y + 3.0 z)).unwrap();
428///     prob.add_constraint(con!("dummy2": 1.0 <= (-1.0) x + 1.0 y)).unwrap();
429/// }
430/// ```
431///
432/// However, constraints can also be constructed manually from
433/// `WeightedVariables`. This can be useful if your constraints don't
434/// quite fit the grammar allowed by the `con!` macro. You can create
435/// part of the constraint using `con!`, then augment it with
436/// `add_wvar` to obtain the constraint you need.The following example
437/// is identical to the above.
438///
439/// ```
440/// #[macro_use]
441/// extern crate rplex;
442///
443/// use rplex::{Env, Problem, Constraint, ConstraintType, WeightedVariable};
444///
445/// fn main() {
446///     let env = Env::new().unwrap();
447///     let mut prob = Problem::new(&env, "dummy").unwrap();
448///     let x = prob.add_variable(var!("x" -> 4.0 as Binary)).unwrap();
449///     let y = prob.add_variable(var!(0.0 <= "y" <= 100.0  -> 3.0 as Integer)).unwrap();
450///     let z = prob.add_variable(var!(0.0 <= "z" <= 4.5 -> 2.0)).unwrap();
451///     let mut dummy = Constraint::new(ConstraintType::Eq, 20.0, "dummy");
452///     dummy.add_wvar(WeightedVariable::new_idx(x, 1.0));
453///     dummy.add_wvar(WeightedVariable::new_idx(y, 2.0));
454///     dummy.add_wvar(WeightedVariable::new_idx(z, 3.0));
455///     prob.add_constraint(dummy).unwrap();
456///     let mut dummy2 = Constraint::new(ConstraintType::GreaterThanEq, 1.0, "dummy2");
457///     dummy2.add_wvar(WeightedVariable::new_idx(x, -1.0));
458///     dummy2.add_wvar(WeightedVariable::new_idx(y, 1.0));
459///     prob.add_constraint(dummy2).unwrap();
460/// }
461/// ```
462#[derive(Clone, Debug)]
463pub struct Constraint {
464    index: Option<usize>,
465    vars: Vec<WeightedVariable>,
466    ty: ConstraintType,
467    rhs: f64,
468    name: String,
469}
470
471impl Constraint {
472    pub fn new<S, F>(ty: ConstraintType, rhs: F, name: S) -> Constraint
473    where
474        S: Into<String>,
475        F: Into<f64>,
476    {
477        Constraint {
478            index: None,
479            vars: vec![],
480            ty: ty,
481            rhs: rhs.into(),
482            name: name.into(),
483        }
484    }
485
486    /// Move a `WeightedVariable` into the Constraint.
487    pub fn add_wvar(&mut self, wvar: WeightedVariable) {
488        self.vars.push(wvar)
489    }
490}
491
492#[macro_export]
493#[doc(hidden)]
494macro_rules! con_ty {
495    (=) => {
496        $crate::ConstraintType::Eq
497    };
498    (>=) => {
499        $crate::ConstraintType::LessThanEq
500    };
501    (<=) => {
502        $crate::ConstraintType::GreaterThanEq
503    };
504}
505
506/// Expressive macro for writing constraints.
507///
508/// # Examples
509/// ## Basic Form: Explicit sum of variables
510/// ```
511/// # #[macro_use]
512/// # extern crate rplex;
513/// # fn main() {
514/// let x1 = 1; let x2 = 2;
515/// con!("basic": 0.0 <= 1.0 x1 + (-1.0) x2);
516/// # }
517/// ```
518/// ## Basic Form: Unweighted sum of variables
519/// ```
520/// # #[macro_use]
521/// # extern crate rplex;
522/// # fn main() {
523/// let xs = vec![1, 2];
524/// con!("basic sum": 0.0 >= sum (&xs));
525/// # }
526/// ```
527/// ## Basic Form: Weighted sum of variables
528/// ```
529/// # #[macro_use]
530/// # extern crate rplex;
531/// # fn main() {
532/// let xs = vec![(1, 1.0), (2, -1.0)];
533/// con!("basic weighted sum": 0.0 = wsum (&xs));
534/// # }
535/// ```
536///
537/// ## Mixed Forms
538/// These can be mixed at will, separated by `+`.
539///
540/// ```
541/// # #[macro_use]
542/// # extern crate rplex;
543/// # fn main() {
544/// let x1 = 1; let x2 = 2;
545/// let ys = vec![3, 4];
546/// let zs = vec![(5, 1.0), (6, -1.0)];
547/// con!("mixed sum": 0.0 <= 1.0 x1 + (-1.0) x2 + sum (&ys) + wsum (&zs));
548/// # }
549/// ```
550#[macro_export]
551macro_rules! con {
552    (@inner $con:ident sum $body:expr) => {
553        for &var in $body {
554            $con.add_wvar($crate::WeightedVariable::new_idx(var, 1.0));
555        }
556    };
557    (@inner $con:ident wsum $body:expr) => {
558        for &(var, weight) in $body {
559            $con.add_wvar($crate::WeightedVariable::new_idx(var, weight));
560        }
561    };
562    (@inner $con:ident $weight:tt $var:expr) => {
563        $con.add_wvar($crate::WeightedVariable::new_idx($var, $weight));
564    };
565    ($name:tt : $rhs:tt $cmp:tt $c1:tt $x1:tt $(+ $c:tt $x:tt)*) => {
566        {
567            let mut con = $crate::Constraint::new(con_ty!($cmp), $rhs, $name);
568            con!(@inner con $c1 $x1);
569            $(
570                con!(@inner con $c $x);
571            )*
572            con
573        }
574    };
575}
576
577/// A CPLEX Problem.
578#[allow(dead_code)]
579pub struct Problem<'a> {
580    inner: *mut CProblem,
581    /// The Environment to which the Problem belongs.
582    env: &'a Env,
583    /// The name of the problem.
584    name: String,
585    variables: Vec<Variable>,
586    constraints: Vec<Constraint>,
587    lazy_constraints: Vec<Constraint>,
588}
589
590/// Solution to a CPLEX Problem.
591///
592/// Currently, there is no way to select which variables are extracted
593/// when using `prob.solve()`. I am currently unfamiliar with the C
594/// API for managing variables that remain unbound in the solution,
595/// and so am unsure how to represent them.
596#[derive(Clone, Debug)]
597pub struct Solution {
598    /// The value of the objective reached by CPLEX.
599    pub objective: f64,
600    /// The values bound to each variable.
601    pub variables: Vec<VariableValue>,
602}
603
604#[derive(Copy, Clone, Debug)]
605pub enum ObjectiveType {
606    Maximize,
607    Minimize,
608}
609
610#[derive(Copy, Clone, Debug)]
611pub enum VariableType {
612    Continuous,
613    Binary,
614    Integer,
615    /// A variable bounded by `[lb, ub]` or equal to 0.
616    SemiContinuous,
617    /// An integer variable bounded by `[lb, ub]` or equal to 0.
618    SemiInteger,
619}
620
621#[derive(Copy, Clone, Debug, PartialEq)]
622pub enum VariableValue {
623    Continuous(f64),
624    Binary(bool),
625    Integer(CInt),
626    SemiContinuous(f64),
627    SemiInteger(CInt),
628}
629
630/// Kind of (in)equality of a Constraint.
631///
632/// Note that the direction of the inequality is *opposite* what one
633/// might expect from the `con!` macro. This is because the right- and
634/// left-hand sides are flipped in the macro.
635#[derive(Copy, Clone, Debug)]
636pub enum ConstraintType {
637    LessThanEq,
638    Eq,
639    GreaterThanEq,
640    /// `Ranged` is currently unimplemented.
641    Ranged,
642}
643
644#[derive(Copy, Clone, Debug, PartialEq, Eq)]
645pub enum ProblemType {
646    Linear,
647    MixedInteger,
648    ContinuousQuadratic,
649}
650
651impl VariableType {
652    fn to_c(&self) -> c_char {
653        match self {
654            &VariableType::Continuous => 'C' as c_char,
655            &VariableType::Binary => 'B' as c_char,
656            &VariableType::Integer => 'I' as c_char,
657            &VariableType::SemiContinuous => 'S' as c_char,
658            &VariableType::SemiInteger => 'N' as c_char,
659        }
660    }
661}
662
663impl ConstraintType {
664    fn to_c(&self) -> c_char {
665        match self {
666            &ConstraintType::LessThanEq => 'L' as c_char,
667            &ConstraintType::Eq => 'E' as c_char,
668            &ConstraintType::GreaterThanEq => 'G' as c_char,
669            &ConstraintType::Ranged => unimplemented!(),
670        }
671    }
672}
673
674impl ObjectiveType {
675    fn to_c(&self) -> c_int {
676        match self {
677            &ObjectiveType::Minimize => 1 as c_int,
678            &ObjectiveType::Maximize => -1 as c_int,
679        }
680    }
681}
682
683impl<'a> Problem<'a> {
684    pub fn new<S>(env: &'a Env, name: S) -> Result<Self, String>
685    where
686        S: Into<String>,
687    {
688        let mut status = 0;
689        let name = name.into();
690        let prob = CPLEX_API.CPXcreateprob(
691            env.inner,
692            &mut status,
693            CString::new(name.as_str()).unwrap().as_ptr(),
694        );
695        if prob == std::ptr::null_mut() {
696            Err(format!(
697                "CPLEX returned NULL for CPXcreateprob ({} ({}))",
698                errstr(env.inner, status).unwrap(),
699                status
700            ))
701        } else {
702            Ok(Problem {
703                inner: prob,
704                env: env,
705                name: name,
706                variables: vec![],
707                constraints: vec![],
708                lazy_constraints: vec![],
709            })
710        }
711    }
712
713    /// Add a variable to the problem. The Variable is **moved** into
714    /// the problem. At this time, it is not possible to get a
715    /// reference to it back.
716    ///
717    /// The column index for the Variable is returned.
718    pub fn add_variable(&mut self, var: Variable) -> Result<usize, String> {
719        let status = CPLEX_API.CPXnewcols(
720            self.env.inner,
721            self.inner,
722            1,
723            &var.obj,
724            &var.lb,
725            &var.ub,
726            &var.ty.to_c(),
727            &CString::new(var.name.as_str()).unwrap().as_ptr(),
728        );
729
730        if status != 0 {
731            Err(format!(
732                "Failed to add {:?} variable {} ({} ({}))",
733                var.ty,
734                var.name,
735                errstr(self.env.inner, status).unwrap(),
736                status
737            ))
738        } else {
739            let index = CPLEX_API.CPXgetnumcols(self.env.inner, self.inner) as usize - 1;
740            self.variables.push(Variable {
741                index: Some(index),
742                ..var
743            });
744            Ok(index)
745        }
746    }
747
748    /// Add a constraint to the problem.
749    ///
750    /// The row index for the constraint is returned.
751    pub fn add_constraint(&mut self, con: Constraint) -> Result<usize, String> {
752        let (ind, val): (Vec<CInt>, Vec<f64>) = con
753            .vars
754            .iter()
755            .filter(|wv| wv.weight != 0.0)
756            .map(|wv| (wv.var as CInt, wv.weight))
757            .unzip();
758        let nz = val.len() as CInt;
759
760        let status = CPLEX_API.CPXaddrows(
761            self.env.inner,
762            self.inner,
763            0,
764            1,
765            nz,
766            &con.rhs,
767            &con.ty.to_c(),
768            &0,
769            ind.as_ptr(),
770            val.as_ptr(),
771            std::ptr::null(),
772            &CString::new(con.name.as_str()).unwrap().as_ptr(),
773        );
774
775        if status != 0 {
776            Err(format!(
777                "Failed to add {:?} constraint {} ({} ({}))",
778                con.ty,
779                con.name,
780                errstr(self.env.inner, status).unwrap(),
781                status
782            ))
783        } else {
784            let index = self.constraints.len();
785            self.constraints.push(Constraint {
786                index: Some(index),
787                ..con
788            });
789            Ok(index)
790        }
791    }
792
793    /// Adds a lazy constraint to the problem.
794    ///
795    /// Returns the index of the constraint. Unclear if this has any value whatsoever.
796    pub fn add_lazy_constraint(&mut self, con: Constraint) -> Result<usize, String> {
797        let (ind, val): (Vec<CInt>, Vec<f64>) = con
798            .vars
799            .iter()
800            .filter(|wv| wv.weight != 0.0)
801            .map(|wv| (wv.var as CInt, wv.weight))
802            .unzip();
803        let nz = val.len() as CInt;
804
805        let status = CPLEX_API.CPXaddlazyconstraints(
806            self.env.inner,
807            self.inner,
808            1,
809            nz,
810            &con.rhs,
811            &con.ty.to_c(),
812            &0,
813            ind.as_ptr(),
814            val.as_ptr(),
815            &CString::new(con.name.as_str()).unwrap().as_ptr(),
816        );
817
818        if status != 0 {
819            Err(format!(
820                "Failed to add {:?} constraint {} ({} ({}))",
821                con.ty,
822                con.name,
823                errstr(self.env.inner, status).unwrap(),
824                status
825            ))
826        } else {
827            let index = self.lazy_constraints.len();
828            self.constraints.push(Constraint {
829                index: Some(index),
830                ..con
831            });
832            Ok(index)
833        }
834    }
835
836    /// Set the objective coefficients. A Constraint object is used
837    /// because it encodes a weighted sum, although it is semantically
838    /// incorrect. The right-hand-side and kind of (in)equality of the
839    /// Constraint are ignored.
840    pub fn set_objective(&mut self, ty: ObjectiveType, con: Constraint) -> Result<(), String> {
841        let (ind, val): (Vec<CInt>, Vec<f64>) = con
842            .vars
843            .iter()
844            .map(|wv| (wv.var as CInt, wv.weight))
845            .unzip();
846
847        let status = CPLEX_API.CPXchgobj(
848            self.env.inner,
849            self.inner,
850            con.vars.len() as CInt,
851            ind.as_ptr(),
852            val.as_ptr(),
853        );
854
855        if status != 0 {
856            Err(format!(
857                "Failed to set objective weights ({} ({}))",
858                errstr(self.env.inner, status).unwrap(),
859                status
860            ))
861        } else {
862            self.set_objective_type(ty)
863        }
864    }
865
866    ///
867    pub fn set_qp_objective(
868        &mut self,
869        ty: ObjectiveType,
870        con: Constraint,
871        qp_var: Vec<usize>,
872        beta: f64,
873    ) -> Result<(), String> {
874        let (ind, val): (Vec<CInt>, Vec<f64>) = con
875            .vars
876            .iter()
877            .map(|wv| (wv.var as CInt, wv.weight))
878            .unzip();
879
880        let status = CPLEX_API.CPXchgobj(
881            self.env.inner,
882            self.inner,
883            con.vars.len() as CInt,
884            ind.as_ptr(),
885            val.as_ptr(),
886        );
887        for i in qp_var.into_iter() {
888            let _status_qp = CPLEX_API.CPXchgqpcoef(
889                self.env.inner,
890                self.inner,
891                i as CInt,
892                i as CInt,
893                beta as c_double,
894            );
895        }
896
897        if status != 0 {
898            Err(format!(
899                "Failed to set objective weights ({} ({}))",
900                errstr(self.env.inner, status).unwrap(),
901                status
902            ))
903        } else {
904            self.set_objective_type(ty)
905        }
906    }
907
908    /// Change the objective type. Default: `ObjectiveType::Minimize`.
909    ///
910    /// It is recommended to use this in conjunction with objective
911    /// coefficients set by the `var!` macro rather than using
912    /// `set_objective`.
913    pub fn set_objective_type(&mut self, ty: ObjectiveType) -> Result<(), String> {
914        let status = CPLEX_API.CPXchgobjsen(self.env.inner, self.inner, ty.to_c());
915        if status != 0 {
916            Err(format!(
917                "Failed to set objective type to {:?} ({} ({}))",
918                ty,
919                errstr(self.env.inner, status).unwrap(),
920                status
921            ))
922        } else {
923            Ok(())
924        }
925    }
926
927    /// Write the problem to a file named `name`. At this time, it is
928    /// not possible to use a `Write` object instead, as this calls C
929    /// code directly.
930    pub fn write<S>(&self, name: S) -> Result<(), String>
931    where
932        S: Into<String>,
933    {
934        let status = CPLEX_API.CPXwriteprob(
935            self.env.inner,
936            self.inner,
937            CString::new(name.into().as_str()).unwrap().as_ptr(),
938            std::ptr::null(),
939        );
940        if status != 0 {
941            return match errstr(self.env.inner, status) {
942                Ok(s) => Err(s),
943                Err(e) => Err(e),
944            };
945        } else {
946            Ok(())
947        }
948    }
949
950    /// Add an initial solution to the problem.
951    ///
952    /// `vars` is an array of indices (i.e. the result of `prob.add_variable`) and `values` are
953    /// their values.
954    pub fn add_initial_soln(&mut self, vars: &[usize], values: &[f64]) -> Result<(), String> {
955        assert!(values.len() == vars.len());
956
957        let vars = vars.into_iter().map(|&u| u as CInt).collect::<Vec<_>>();
958
959        let status = CPLEX_API.CPXaddmipstarts(
960            self.env.inner,
961            self.inner,
962            1,
963            vars.len() as CInt,
964            &0,
965            vars.as_ptr(),
966            values.as_ptr(),
967            &0,
968            &std::ptr::null(),
969        );
970        if status != 0 {
971            return match errstr(self.env.inner, status) {
972                Ok(s) => Err(s),
973                Err(e) => Err(e),
974            };
975        } else {
976            Ok(())
977        }
978    }
979
980    /// Solve the Problem, returning a `Solution` object with the
981    /// result.
982    pub fn solve_as(&mut self, pt: ProblemType) -> Result<Solution, String> {
983        // TODO: support multiple solution types...
984
985        if pt == ProblemType::Linear {
986            let status = CPLEX_API.CPXchgprobtype(self.env.inner, self.inner, 0);
987
988            if status != 0 {
989                return Err(format!(
990                    "Failed to convert to LP problem ({} ({}))",
991                    errstr(self.env.inner, status).unwrap(),
992                    status
993                ));
994            }
995        }
996        let status = match pt {
997            ProblemType::MixedInteger => CPLEX_API.CPXmipopt(self.env.inner, self.inner),
998            ProblemType::Linear => CPLEX_API.CPXlpopt(self.env.inner, self.inner),
999            ProblemType::ContinuousQuadratic => CPLEX_API.CPXqpopt(self.env.inner, self.inner),
1000        };
1001        if status != 0 {
1002            CPLEX_API.CPXwriteprob(
1003                self.env.inner,
1004                self.inner,
1005                CString::new("lpex1.lp").unwrap().as_ptr(),
1006                std::ptr::null(),
1007            );
1008            return Err(format!(
1009                "LP Optimization failed ({} ({}))",
1010                errstr(self.env.inner, status).unwrap(),
1011                status
1012            ));
1013        }
1014
1015        let mut objval: f64 = 0.0;
1016        let status = CPLEX_API.CPXgetobjval(self.env.inner, self.inner, &mut objval);
1017        if status != 0 {
1018            CPLEX_API.CPXwriteprob(
1019                self.env.inner,
1020                self.inner,
1021                CString::new("lpex1.lp").unwrap().as_ptr(),
1022                std::ptr::null(),
1023            );
1024            return Err(format!(
1025                "Failed to retrieve objective value ({} ({}))",
1026                errstr(self.env.inner, status).unwrap(),
1027                status
1028            ));
1029        }
1030
1031        let mut xs = vec![0f64; self.variables.len()];
1032        let status = CPLEX_API.CPXgetx(
1033            self.env.inner,
1034            self.inner,
1035            xs.as_mut_ptr(),
1036            0,
1037            self.variables.len() as CInt - 1,
1038        );
1039        if status != 0 {
1040            return Err(format!(
1041                "Failed to retrieve values for variables ({} ({}))",
1042                errstr(self.env.inner, status).unwrap(),
1043                status
1044            ));
1045        }
1046
1047        let mut eps = EPINT;
1048        CPLEX_API.CPXgetdblparam(self.env.inner, EPINT_ID, &mut eps);
1049
1050        return Ok(Solution {
1051            objective: objval,
1052            variables: xs
1053                .iter()
1054                .zip(self.variables.iter())
1055                .map(|(&x, v)| match v.ty {
1056                    VariableType::Binary => VariableValue::Binary(x <= 1.0 + eps && x >= 1.0 - eps),
1057                    VariableType::Continuous => VariableValue::Continuous(x),
1058                    VariableType::Integer => VariableValue::Integer(x as CInt),
1059                    VariableType::SemiContinuous => VariableValue::SemiContinuous(x),
1060                    VariableType::SemiInteger => VariableValue::SemiInteger(x as CInt),
1061                })
1062                .collect::<Vec<VariableValue>>(),
1063        });
1064    }
1065
1066    /// Solve the problem as a Mixed Integer Program
1067    pub fn solve(&mut self, prob_type: ProblemType) -> Result<Solution, String> {
1068        self.solve_as(prob_type)
1069    }
1070}
1071
1072impl<'a> Drop for Problem<'a> {
1073    fn drop(&mut self) {
1074        assert!(CPLEX_API.CPXfreeprob(self.env.inner, &self.inner) == 0);
1075    }
1076}