1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
3 * This file is a part of LEMON, a generic C++ optimization library.
5 * Copyright (C) 2003-2008
6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
9 * Permission to use, modify and distribute this software is granted
10 * provided that this copyright notice appears in all copies. For
11 * precise terms see the accompanying LICENSE file.
13 * This software is provided "AS IS" with no warranty of any kind,
14 * express or implied, and with no claim as to its suitability for any
23 #include <lemon/cplex.h>
26 #include <ilcplex/cplex.h>
31 ///\brief Implementation of the LEMON-CPLEX lp solver interface.
34 CplexEnv::LicenseError::LicenseError(int status) {
35 if (!CPXgeterrorstring(0, status, _message)) {
36 std::strcpy(_message, "Cplex unknown error");
40 CplexEnv::CplexEnv() {
43 _env = CPXopenCPLEX(&status);
47 throw LicenseError(status);
51 CplexEnv::CplexEnv(const CplexEnv& other) {
57 CplexEnv& CplexEnv::operator=(const CplexEnv& other) {
64 CplexEnv::~CplexEnv() {
72 CplexBase::CplexBase() : LpBase() {
74 _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
77 CplexBase::CplexBase(const CplexEnv& env)
78 : LpBase(), _env(env) {
80 _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
83 CplexBase::CplexBase(const CplexBase& cplex)
86 _prob = CPXcloneprob(cplexEnv(), cplex._prob, &status);
91 CplexBase::~CplexBase() {
92 CPXfreeprob(cplexEnv(),&_prob);
95 int CplexBase::_addCol() {
96 int i = CPXgetnumcols(cplexEnv(), _prob);
97 double lb = -INF, ub = INF;
98 CPXnewcols(cplexEnv(), _prob, 1, 0, &lb, &ub, 0, 0);
103 int CplexBase::_addRow() {
104 int i = CPXgetnumrows(cplexEnv(), _prob);
105 const double ub = INF;
107 CPXnewrows(cplexEnv(), _prob, 1, &ub, &s, 0, 0);
112 void CplexBase::_eraseCol(int i) {
113 CPXdelcols(cplexEnv(), _prob, i, i);
116 void CplexBase::_eraseRow(int i) {
117 CPXdelrows(cplexEnv(), _prob, i, i);
120 void CplexBase::_eraseColId(int i) {
122 cols.shiftIndices(i);
124 void CplexBase::_eraseRowId(int i) {
126 rows.shiftIndices(i);
129 void CplexBase::_getColName(int col, std::string &name) const {
131 CPXgetcolname(cplexEnv(), _prob, 0, 0, 0, &size, col, col);
138 std::vector<char> buf(size);
141 CPXgetcolname(cplexEnv(), _prob, &cname, &buf.front(), size,
146 void CplexBase::_setColName(int col, const std::string &name) {
148 cname = const_cast<char*>(name.c_str());
149 CPXchgcolname(cplexEnv(), _prob, 1, &col, &cname);
152 int CplexBase::_colByName(const std::string& name) const {
154 if (CPXgetcolindex(cplexEnv(), _prob,
155 const_cast<char*>(name.c_str()), &index) == 0) {
161 void CplexBase::_getRowName(int row, std::string &name) const {
163 CPXgetrowname(cplexEnv(), _prob, 0, 0, 0, &size, row, row);
170 std::vector<char> buf(size);
173 CPXgetrowname(cplexEnv(), _prob, &cname, &buf.front(), size,
178 void CplexBase::_setRowName(int row, const std::string &name) {
180 cname = const_cast<char*>(name.c_str());
181 CPXchgrowname(cplexEnv(), _prob, 1, &row, &cname);
184 int CplexBase::_rowByName(const std::string& name) const {
186 if (CPXgetrowindex(cplexEnv(), _prob,
187 const_cast<char*>(name.c_str()), &index) == 0) {
193 void CplexBase::_setRowCoeffs(int i, ExprIterator b,
196 std::vector<int> indices;
197 std::vector<int> rowlist;
198 std::vector<Value> values;
200 for(ExprIterator it=b; it!=e; ++it) {
201 indices.push_back(it->first);
202 values.push_back(it->second);
203 rowlist.push_back(i);
206 CPXchgcoeflist(cplexEnv(), _prob, values.size(),
207 &rowlist.front(), &indices.front(), &values.front());
210 void CplexBase::_getRowCoeffs(int i, InsertIterator b) const {
211 int tmp1, tmp2, tmp3, length;
212 CPXgetrows(cplexEnv(), _prob, &tmp1, &tmp2, 0, 0, 0, &length, i, i);
215 std::vector<int> indices(length);
216 std::vector<double> values(length);
218 CPXgetrows(cplexEnv(), _prob, &tmp1, &tmp2,
219 &indices.front(), &values.front(),
220 length, &tmp3, i, i);
222 for (int i = 0; i < length; ++i) {
223 *b = std::make_pair(indices[i], values[i]);
228 void CplexBase::_setColCoeffs(int i, ExprIterator b, ExprIterator e) {
229 std::vector<int> indices;
230 std::vector<int> collist;
231 std::vector<Value> values;
233 for(ExprIterator it=b; it!=e; ++it) {
234 indices.push_back(it->first);
235 values.push_back(it->second);
236 collist.push_back(i);
239 CPXchgcoeflist(cplexEnv(), _prob, values.size(),
240 &indices.front(), &collist.front(), &values.front());
243 void CplexBase::_getColCoeffs(int i, InsertIterator b) const {
245 int tmp1, tmp2, tmp3, length;
246 CPXgetcols(cplexEnv(), _prob, &tmp1, &tmp2, 0, 0, 0, &length, i, i);
249 std::vector<int> indices(length);
250 std::vector<double> values(length);
252 CPXgetcols(cplexEnv(), _prob, &tmp1, &tmp2,
253 &indices.front(), &values.front(),
254 length, &tmp3, i, i);
256 for (int i = 0; i < length; ++i) {
257 *b = std::make_pair(indices[i], values[i]);
263 void CplexBase::_setCoeff(int row, int col, Value value) {
264 CPXchgcoef(cplexEnv(), _prob, row, col, value);
267 CplexBase::Value CplexBase::_getCoeff(int row, int col) const {
268 CplexBase::Value value;
269 CPXgetcoef(cplexEnv(), _prob, row, col, &value);
273 void CplexBase::_setColLowerBound(int i, Value value) {
275 CPXchgbds(cplexEnv(), _prob, 1, &i, &s, &value);
278 CplexBase::Value CplexBase::_getColLowerBound(int i) const {
279 CplexBase::Value res;
280 CPXgetlb(cplexEnv(), _prob, &res, i, i);
281 return res <= -CPX_INFBOUND ? -INF : res;
284 void CplexBase::_setColUpperBound(int i, Value value)
287 CPXchgbds(cplexEnv(), _prob, 1, &i, &s, &value);
290 CplexBase::Value CplexBase::_getColUpperBound(int i) const {
291 CplexBase::Value res;
292 CPXgetub(cplexEnv(), _prob, &res, i, i);
293 return res >= CPX_INFBOUND ? INF : res;
296 CplexBase::Value CplexBase::_getRowLowerBound(int i) const {
298 CPXgetsense(cplexEnv(), _prob, &s, i, i);
299 CplexBase::Value res;
305 CPXgetrhs(cplexEnv(), _prob, &res, i, i);
306 return res <= -CPX_INFBOUND ? -INF : res;
312 CplexBase::Value CplexBase::_getRowUpperBound(int i) const {
314 CPXgetsense(cplexEnv(), _prob, &s, i, i);
315 CplexBase::Value res;
320 CPXgetrhs(cplexEnv(), _prob, &res, i, i);
321 return res >= CPX_INFBOUND ? INF : res;
323 CPXgetrhs(cplexEnv(), _prob, &res, i, i);
326 CPXgetrngval(cplexEnv(), _prob, &rng, i, i);
329 return res >= CPX_INFBOUND ? INF : res;
335 //This is easier to implement
336 void CplexBase::_set_row_bounds(int i, Value lb, Value ub) {
339 CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
340 CPXchgrhs(cplexEnv(), _prob, 1, &i, &ub);
341 } else if (ub == INF) {
343 CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
344 CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
345 } else if (lb == ub){
347 CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
348 CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
351 CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
352 CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
353 double len = ub - lb;
354 CPXchgrngval(cplexEnv(), _prob, 1, &i, &len);
358 void CplexBase::_setRowLowerBound(int i, Value lb)
360 LEMON_ASSERT(lb != INF, "Invalid bound");
361 _set_row_bounds(i, lb, CplexBase::_getRowUpperBound(i));
364 void CplexBase::_setRowUpperBound(int i, Value ub)
367 LEMON_ASSERT(ub != -INF, "Invalid bound");
368 _set_row_bounds(i, CplexBase::_getRowLowerBound(i), ub);
371 void CplexBase::_setObjCoeffs(ExprIterator b, ExprIterator e)
373 std::vector<int> indices;
374 std::vector<Value> values;
375 for(ExprIterator it=b; it!=e; ++it) {
376 indices.push_back(it->first);
377 values.push_back(it->second);
379 CPXchgobj(cplexEnv(), _prob, values.size(),
380 &indices.front(), &values.front());
384 void CplexBase::_getObjCoeffs(InsertIterator b) const
386 int num = CPXgetnumcols(cplexEnv(), _prob);
387 std::vector<Value> x(num);
389 CPXgetobj(cplexEnv(), _prob, &x.front(), 0, num - 1);
390 for (int i = 0; i < num; ++i) {
392 *b = std::make_pair(i, x[i]);
398 void CplexBase::_setObjCoeff(int i, Value obj_coef)
400 CPXchgobj(cplexEnv(), _prob, 1, &i, &obj_coef);
403 CplexBase::Value CplexBase::_getObjCoeff(int i) const
406 CPXgetobj(cplexEnv(), _prob, &x, i, i);
410 void CplexBase::_setSense(CplexBase::Sense sense) {
413 CPXchgobjsen(cplexEnv(), _prob, CPX_MIN);
416 CPXchgobjsen(cplexEnv(), _prob, CPX_MAX);
421 CplexBase::Sense CplexBase::_getSense() const {
422 switch (CPXgetobjsen(cplexEnv(), _prob)) {
428 LEMON_ASSERT(false, "Invalid sense");
429 return CplexBase::Sense();
433 void CplexBase::_clear() {
434 CPXfreeprob(cplexEnv(),&_prob);
436 _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
444 : LpBase(), CplexBase(), LpSolver() {}
446 CplexLp::CplexLp(const CplexEnv& env)
447 : LpBase(), CplexBase(env), LpSolver() {}
449 CplexLp::CplexLp(const CplexLp& other)
450 : LpBase(), CplexBase(other), LpSolver() {}
452 CplexLp::~CplexLp() {}
454 CplexLp* CplexLp::newSolver() const { return new CplexLp; }
455 CplexLp* CplexLp::cloneSolver() const {return new CplexLp(*this); }
457 const char* CplexLp::_solverName() const { return "CplexLp"; }
459 void CplexLp::_clear_temporals() {
466 // The routine returns zero unless an error occurred during the
467 // optimization. Examples of errors include exhausting available
468 // memory (CPXERR_NO_MEMORY) or encountering invalid data in the
469 // CPLEX problem object (CPXERR_NO_PROBLEM). Exceeding a
470 // user-specified CPLEX limit, or proving the model infeasible or
471 // unbounded, are not considered errors. Note that a zero return
472 // value does not necessarily mean that a solution exists. Use query
473 // routines CPXsolninfo, CPXgetstat, and CPXsolution to obtain
474 // further information about the status of the optimization.
475 CplexLp::SolveExitStatus CplexLp::convertStatus(int status) {
476 #if CPX_VERSION >= 800
478 switch (CPXgetstat(cplexEnv(), _prob)) {
479 case CPX_STAT_OPTIMAL:
480 case CPX_STAT_INFEASIBLE:
481 case CPX_STAT_UNBOUNDED:
491 //We want to exclude some cases
492 switch (CPXgetstat(cplexEnv(), _prob)) {
494 case CPX_IT_LIM_FEAS:
495 case CPX_IT_LIM_INFEAS:
496 case CPX_TIME_LIM_FEAS:
497 case CPX_TIME_LIM_INFEAS:
508 CplexLp::SolveExitStatus CplexLp::_solve() {
510 return convertStatus(CPXlpopt(cplexEnv(), _prob));
513 CplexLp::SolveExitStatus CplexLp::solvePrimal() {
515 return convertStatus(CPXprimopt(cplexEnv(), _prob));
518 CplexLp::SolveExitStatus CplexLp::solveDual() {
520 return convertStatus(CPXdualopt(cplexEnv(), _prob));
523 CplexLp::SolveExitStatus CplexLp::solveBarrier() {
525 return convertStatus(CPXbaropt(cplexEnv(), _prob));
528 CplexLp::Value CplexLp::_getPrimal(int i) const {
530 CPXgetx(cplexEnv(), _prob, &x, i, i);
534 CplexLp::Value CplexLp::_getDual(int i) const {
536 CPXgetpi(cplexEnv(), _prob, &y, i, i);
540 CplexLp::Value CplexLp::_getPrimalValue() const {
542 CPXgetobjval(cplexEnv(), _prob, &objval);
546 CplexLp::VarStatus CplexLp::_getColStatus(int i) const {
547 if (_col_status.empty()) {
548 _col_status.resize(CPXgetnumcols(cplexEnv(), _prob));
549 CPXgetbase(cplexEnv(), _prob, &_col_status.front(), 0);
551 switch (_col_status[i]) {
561 LEMON_ASSERT(false, "Wrong column status");
562 return CplexLp::VarStatus();
566 CplexLp::VarStatus CplexLp::_getRowStatus(int i) const {
567 if (_row_status.empty()) {
568 _row_status.resize(CPXgetnumrows(cplexEnv(), _prob));
569 CPXgetbase(cplexEnv(), _prob, 0, &_row_status.front());
571 switch (_row_status[i]) {
577 CPXgetsense(cplexEnv(), _prob, &s, i, i);
578 return s != 'L' ? LOWER : UPPER;
583 LEMON_ASSERT(false, "Wrong row status");
584 return CplexLp::VarStatus();
588 CplexLp::Value CplexLp::_getPrimalRay(int i) const {
589 if (_primal_ray.empty()) {
590 _primal_ray.resize(CPXgetnumcols(cplexEnv(), _prob));
591 CPXgetray(cplexEnv(), _prob, &_primal_ray.front());
593 return _primal_ray[i];
596 CplexLp::Value CplexLp::_getDualRay(int i) const {
597 if (_dual_ray.empty()) {
603 //7.5-os cplex statusai (Vigyazat: a 9.0-asei masok!)
604 // This table lists the statuses, returned by the CPXgetstat()
605 // routine, for solutions to LP problems or mixed integer problems. If
606 // no solution exists, the return value is zero.
608 // For Simplex, Barrier
610 // Optimal solution found
612 // Problem infeasible
616 // Objective limit exceeded in Phase II
618 // Iteration limit exceeded in Phase II
619 // 6 CPX_IT_LIM_INFEAS
620 // Iteration limit exceeded in Phase I
621 // 7 CPX_TIME_LIM_FEAS
622 // Time limit exceeded in Phase II
623 // 8 CPX_TIME_LIM_INFEAS
624 // Time limit exceeded in Phase I
625 // 9 CPX_NUM_BEST_FEAS
626 // Problem non-optimal, singularities in Phase II
627 // 10 CPX_NUM_BEST_INFEAS
628 // Problem non-optimal, singularities in Phase I
629 // 11 CPX_OPTIMAL_INFEAS
630 // Optimal solution found, unscaled infeasibilities
632 // Aborted in Phase II
633 // 13 CPX_ABORT_INFEAS
634 // Aborted in Phase I
635 // 14 CPX_ABORT_DUAL_INFEAS
636 // Aborted in barrier, dual infeasible
637 // 15 CPX_ABORT_PRIM_INFEAS
638 // Aborted in barrier, primal infeasible
639 // 16 CPX_ABORT_PRIM_DUAL_INFEAS
640 // Aborted in barrier, primal and dual infeasible
641 // 17 CPX_ABORT_PRIM_DUAL_FEAS
642 // Aborted in barrier, primal and dual feasible
643 // 18 CPX_ABORT_CROSSOVER
644 // Aborted in crossover
646 // Infeasible or unbounded
650 // Ezeket hova tegyem:
651 // ??case CPX_ABORT_DUAL_INFEAS
652 // ??case CPX_ABORT_CROSSOVER
653 // ??case CPX_INForUNBD
656 //Some more interesting stuff:
658 // CPX_PARAM_PROBMETHOD 1062 int LPMETHOD
663 // 4 Standard Barrier
665 // Description: Method for linear optimization.
666 // Determines which algorithm is used when CPXlpopt() (or "optimize"
667 // in the Interactive Optimizer) is called. Currently the behavior of
668 // the "Automatic" setting is that CPLEX simply invokes the dual
669 // simplex method, but this capability may be expanded in the future
670 // so that CPLEX chooses the method based on problem characteristics
671 #if CPX_VERSION < 900
672 void statusSwitch(CPXENVptr cplexEnv(),int& stat){
674 CPXgetintparam (cplexEnv(),CPX_PARAM_PROBMETHOD,&lpmethod);
676 if (stat==CPX_UNBOUNDED){
680 if (stat==CPX_INFEASIBLE)
686 void statusSwitch(CPXENVptr,int&){}
689 CplexLp::ProblemType CplexLp::_getPrimalType() const {
690 // Unboundedness not treated well: the following is from cplex 9.0 doc
691 // About Unboundedness
693 // The treatment of models that are unbounded involves a few
694 // subtleties. Specifically, a declaration of unboundedness means that
695 // ILOG CPLEX has determined that the model has an unbounded
696 // ray. Given any feasible solution x with objective z, a multiple of
697 // the unbounded ray can be added to x to give a feasible solution
698 // with objective z-1 (or z+1 for maximization models). Thus, if a
699 // feasible solution exists, then the optimal objective is
700 // unbounded. Note that ILOG CPLEX has not necessarily concluded that
701 // a feasible solution exists. Users can call the routine CPXsolninfo
702 // to determine whether ILOG CPLEX has also concluded that the model
703 // has a feasible solution.
705 int stat = CPXgetstat(cplexEnv(), _prob);
706 #if CPX_VERSION >= 800
709 case CPX_STAT_OPTIMAL:
711 case CPX_STAT_UNBOUNDED:
713 case CPX_STAT_INFEASIBLE:
719 statusSwitch(cplexEnv(),stat);
720 //CPXgetstat(cplexEnv(), _prob);
721 //printf("A primal status: %d, CPX_OPTIMAL=%d \n",stat,CPX_OPTIMAL);
724 return UNDEFINED; //Undefined
725 case CPX_OPTIMAL://Optimal
727 case CPX_UNBOUNDED://Unbounded
728 return INFEASIBLE;//In case of dual simplex
730 case CPX_INFEASIBLE://Infeasible
731 // case CPX_IT_LIM_INFEAS:
732 // case CPX_TIME_LIM_INFEAS:
733 // case CPX_NUM_BEST_INFEAS:
734 // case CPX_OPTIMAL_INFEAS:
735 // case CPX_ABORT_INFEAS:
736 // case CPX_ABORT_PRIM_INFEAS:
737 // case CPX_ABORT_PRIM_DUAL_INFEAS:
738 return UNBOUNDED;//In case of dual simplex
741 // case CPX_IT_LIM_FEAS:
742 // case CPX_TIME_LIM_FEAS:
743 // case CPX_NUM_BEST_FEAS:
744 // case CPX_ABORT_FEAS:
745 // case CPX_ABORT_PRIM_DUAL_FEAS:
748 return UNDEFINED; //Everything else comes here
754 //9.0-as cplex verzio statusai
755 // CPX_STAT_ABORT_DUAL_OBJ_LIM
756 // CPX_STAT_ABORT_IT_LIM
757 // CPX_STAT_ABORT_OBJ_LIM
758 // CPX_STAT_ABORT_PRIM_OBJ_LIM
759 // CPX_STAT_ABORT_TIME_LIM
760 // CPX_STAT_ABORT_USER
761 // CPX_STAT_FEASIBLE_RELAXED
762 // CPX_STAT_INFEASIBLE
763 // CPX_STAT_INForUNBD
766 // CPX_STAT_OPTIMAL_FACE_UNBOUNDED
767 // CPX_STAT_OPTIMAL_INFEAS
768 // CPX_STAT_OPTIMAL_RELAXED
769 // CPX_STAT_UNBOUNDED
771 CplexLp::ProblemType CplexLp::_getDualType() const {
772 int stat = CPXgetstat(cplexEnv(), _prob);
773 #if CPX_VERSION >= 800
775 case CPX_STAT_OPTIMAL:
777 case CPX_STAT_UNBOUNDED:
783 statusSwitch(cplexEnv(),stat);
786 return UNDEFINED; //Undefined
787 case CPX_OPTIMAL://Optimal
792 return UNDEFINED; //Everything else comes here
801 : LpBase(), CplexBase(), MipSolver() {
803 #if CPX_VERSION < 800
804 CPXchgprobtype(cplexEnv(), _prob, CPXPROB_MIP);
806 CPXchgprobtype(cplexEnv(), _prob, CPXPROB_MILP);
810 CplexMip::CplexMip(const CplexEnv& env)
811 : LpBase(), CplexBase(env), MipSolver() {
813 #if CPX_VERSION < 800
814 CPXchgprobtype(cplexEnv(), _prob, CPXPROB_MIP);
816 CPXchgprobtype(cplexEnv(), _prob, CPXPROB_MILP);
821 CplexMip::CplexMip(const CplexMip& other)
822 : LpBase(), CplexBase(other), MipSolver() {}
824 CplexMip::~CplexMip() {}
826 CplexMip* CplexMip::newSolver() const { return new CplexMip; }
827 CplexMip* CplexMip::cloneSolver() const {return new CplexMip(*this); }
829 const char* CplexMip::_solverName() const { return "CplexMip"; }
831 void CplexMip::_setColType(int i, CplexMip::ColTypes col_type) {
833 // Note If a variable is to be changed to binary, a call to CPXchgbds
834 // should also be made to change the bounds to 0 and 1.
839 CPXchgctype (cplexEnv(), _prob, 1, &i, &t);
843 CPXchgctype (cplexEnv(), _prob, 1, &i, &t);
850 CplexMip::ColTypes CplexMip::_getColType(int i) const {
852 CPXgetctype (cplexEnv(), _prob, &t, i, i);
859 LEMON_ASSERT(false, "Invalid column type");
865 CplexMip::SolveExitStatus CplexMip::_solve() {
867 status = CPXmipopt (cplexEnv(), _prob);
876 CplexMip::ProblemType CplexMip::_getType() const {
878 int stat = CPXgetstat(cplexEnv(), _prob);
880 //Fortunately, MIP statuses did not change for cplex 8.0
883 // Optimal integer solution has been found.
884 case CPXMIP_OPTIMAL_TOL:
885 // Optimal soluton with the tolerance defined by epgap or epagap has
888 //This also exists in later issues
889 // case CPXMIP_UNBOUNDED:
891 case CPXMIP_INFEASIBLE:
896 //Unboundedness not treated well: the following is from cplex 9.0 doc
897 // About Unboundedness
899 // The treatment of models that are unbounded involves a few
900 // subtleties. Specifically, a declaration of unboundedness means that
901 // ILOG CPLEX has determined that the model has an unbounded
902 // ray. Given any feasible solution x with objective z, a multiple of
903 // the unbounded ray can be added to x to give a feasible solution
904 // with objective z-1 (or z+1 for maximization models). Thus, if a
905 // feasible solution exists, then the optimal objective is
906 // unbounded. Note that ILOG CPLEX has not necessarily concluded that
907 // a feasible solution exists. Users can call the routine CPXsolninfo
908 // to determine whether ILOG CPLEX has also concluded that the model
909 // has a feasible solution.
912 CplexMip::Value CplexMip::_getSol(int i) const {
914 CPXgetmipx(cplexEnv(), _prob, &x, i, i);
918 CplexMip::Value CplexMip::_getSolValue() const {
920 CPXgetmipobjval(cplexEnv(), _prob, &objval);