1extern crate dlopen;
5use dlopen::wrapper::{Container, WrapperApi};
8use std::path::PathBuf;
9
10extern crate libc;
13use libc::{c_char, c_double, c_int};
14use std::ffi::CString;
15pub const INFINITY: f64 = 1.0E+20;
19pub const EPINT_ID: c_int = 2010; pub const EPINT: c_double = 1e-5; pub 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 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 CPXgetnumcols: fn(env: *const CEnv, lp: *mut CProblem) -> CInt,
119 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 CPXchgqpcoef:
129 fn(env: *mut CEnv, lp: *mut CProblem, i: CInt, j: CInt, newvalue: c_double) -> c_int,
130
131 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 CPXlpopt: fn(env: *mut CEnv, lp: *mut CProblem) -> c_int,
143 CPXmipopt: fn(env: *mut CEnv, lp: *mut CProblem) -> c_int,
144 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 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 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 CPXcloseCPLEX: fn(env: *const *mut CEnv) -> c_int,
177 CPXfreeprob: fn(env: *mut CEnv, lp: *const *mut CProblem) -> c_int,
178 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 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
240pub 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 let env = CPLEX_API.CPXopenCPLEX(&mut status);
254 if env == std::ptr::null_mut() {
256 Err(format!(
257 "CPLEX returned NULL for CPXopenCPLEX (status: {})",
258 status
259 ))
260 } else {
261 Ok(Env { inner: env })
264 }
265 }
266 }
267
268 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#[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#[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 ($lb:tt <= $name:tt <= $ub:tt -> $obj:tt) => (var!($lb <= $name <= $ub -> $obj as Continuous));
368 ($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 ($name:tt -> $obj:tt) => (var!(0.0 <= $name -> $obj));
373
374 ($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#[derive(Clone, Debug)]
384pub struct WeightedVariable {
385 var: usize,
386 weight: f64,
387}
388
389impl WeightedVariable {
390 pub fn new_var(var: &Variable, weight: f64) -> Self {
393 WeightedVariable {
394 var: var.index.unwrap(),
395 weight: weight,
396 }
397 }
398
399 pub fn new_idx(idx: usize, weight: f64) -> Self {
404 WeightedVariable {
405 var: idx,
406 weight: weight,
407 }
408 }
409}
410
411#[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 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#[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#[allow(dead_code)]
579pub struct Problem<'a> {
580 inner: *mut CProblem,
581 env: &'a Env,
583 name: String,
585 variables: Vec<Variable>,
586 constraints: Vec<Constraint>,
587 lazy_constraints: Vec<Constraint>,
588}
589
590#[derive(Clone, Debug)]
597pub struct Solution {
598 pub objective: f64,
600 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 SemiContinuous,
617 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#[derive(Copy, Clone, Debug)]
636pub enum ConstraintType {
637 LessThanEq,
638 Eq,
639 GreaterThanEq,
640 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 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 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 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 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 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 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 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 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 pub fn solve_as(&mut self, pt: ProblemType) -> Result<Solution, String> {
983 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 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}