lemon/cplex.cc
author Alpar Juttner <alpar@cs.elte.hu>
Tue, 06 Aug 2013 12:04:13 +0200
changeset 805 115031ac8001
parent 785 8d281761dea4
child 799 015801ff09d1
permissions -rw-r--r--
Further fixes for gcc version 3.3 and 4.3 (#470)
     1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library.
     4  *
     5  * Copyright (C) 2003-2009
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8  *
     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.
    12  *
    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
    15  * purpose.
    16  *
    17  */
    18 
    19 #include <iostream>
    20 #include <vector>
    21 #include <cstring>
    22 
    23 #include <lemon/cplex.h>
    24 
    25 extern "C" {
    26 #include <ilcplex/cplex.h>
    27 }
    28 
    29 
    30 ///\file
    31 ///\brief Implementation of the LEMON-CPLEX lp solver interface.
    32 namespace lemon {
    33 
    34   CplexEnv::LicenseError::LicenseError(int status) {
    35     if (!CPXgeterrorstring(0, status, _message)) {
    36       std::strcpy(_message, "Cplex unknown error");
    37     }
    38   }
    39 
    40   CplexEnv::CplexEnv() {
    41     int status;
    42     _cnt = new int;
    43     (*_cnt) = 1;
    44     _env = CPXopenCPLEX(&status);
    45     if (_env == 0) {
    46       delete _cnt;
    47       _cnt = 0;
    48       throw LicenseError(status);
    49     }
    50   }
    51 
    52   CplexEnv::CplexEnv(const CplexEnv& other) {
    53     _env = other._env;
    54     _cnt = other._cnt;
    55     ++(*_cnt);
    56   }
    57 
    58   CplexEnv& CplexEnv::operator=(const CplexEnv& other) {
    59     _env = other._env;
    60     _cnt = other._cnt;
    61     ++(*_cnt);
    62     return *this;
    63   }
    64 
    65   CplexEnv::~CplexEnv() {
    66     --(*_cnt);
    67     if (*_cnt == 0) {
    68       delete _cnt;
    69       CPXcloseCPLEX(&_env);
    70     }
    71   }
    72 
    73   CplexBase::CplexBase() : LpBase() {
    74     int status;
    75     _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
    76     messageLevel(MESSAGE_NOTHING);
    77   }
    78 
    79   CplexBase::CplexBase(const CplexEnv& env)
    80     : LpBase(), _env(env) {
    81     int status;
    82     _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
    83     messageLevel(MESSAGE_NOTHING);
    84   }
    85 
    86   CplexBase::CplexBase(const CplexBase& cplex)
    87     : LpBase() {
    88     int status;
    89     _prob = CPXcloneprob(cplexEnv(), cplex._prob, &status);
    90     rows = cplex.rows;
    91     cols = cplex.cols;
    92     messageLevel(MESSAGE_NOTHING);
    93   }
    94 
    95   CplexBase::~CplexBase() {
    96     CPXfreeprob(cplexEnv(),&_prob);
    97   }
    98 
    99   int CplexBase::_addCol() {
   100     int i = CPXgetnumcols(cplexEnv(), _prob);
   101     double lb = -INF, ub = INF;
   102     CPXnewcols(cplexEnv(), _prob, 1, 0, &lb, &ub, 0, 0);
   103     return i;
   104   }
   105 
   106 
   107   int CplexBase::_addRow() {
   108     int i = CPXgetnumrows(cplexEnv(), _prob);
   109     const double ub = INF;
   110     const char s = 'L';
   111     CPXnewrows(cplexEnv(), _prob, 1, &ub, &s, 0, 0);
   112     return i;
   113   }
   114 
   115 
   116   void CplexBase::_eraseCol(int i) {
   117     CPXdelcols(cplexEnv(), _prob, i, i);
   118   }
   119 
   120   void CplexBase::_eraseRow(int i) {
   121     CPXdelrows(cplexEnv(), _prob, i, i);
   122   }
   123 
   124   void CplexBase::_eraseColId(int i) {
   125     cols.eraseIndex(i);
   126     cols.shiftIndices(i);
   127   }
   128   void CplexBase::_eraseRowId(int i) {
   129     rows.eraseIndex(i);
   130     rows.shiftIndices(i);
   131   }
   132 
   133   void CplexBase::_getColName(int col, std::string &name) const {
   134     int size;
   135     CPXgetcolname(cplexEnv(), _prob, 0, 0, 0, &size, col, col);
   136     if (size == 0) {
   137       name.clear();
   138       return;
   139     }
   140 
   141     size *= -1;
   142     std::vector<char> buf(size);
   143     char *cname;
   144     int tmp;
   145     CPXgetcolname(cplexEnv(), _prob, &cname, &buf.front(), size,
   146                   &tmp, col, col);
   147     name = cname;
   148   }
   149 
   150   void CplexBase::_setColName(int col, const std::string &name) {
   151     char *cname;
   152     cname = const_cast<char*>(name.c_str());
   153     CPXchgcolname(cplexEnv(), _prob, 1, &col, &cname);
   154   }
   155 
   156   int CplexBase::_colByName(const std::string& name) const {
   157     int index;
   158     if (CPXgetcolindex(cplexEnv(), _prob,
   159                        const_cast<char*>(name.c_str()), &index) == 0) {
   160       return index;
   161     }
   162     return -1;
   163   }
   164 
   165   void CplexBase::_getRowName(int row, std::string &name) const {
   166     int size;
   167     CPXgetrowname(cplexEnv(), _prob, 0, 0, 0, &size, row, row);
   168     if (size == 0) {
   169       name.clear();
   170       return;
   171     }
   172 
   173     size *= -1;
   174     std::vector<char> buf(size);
   175     char *cname;
   176     int tmp;
   177     CPXgetrowname(cplexEnv(), _prob, &cname, &buf.front(), size,
   178                   &tmp, row, row);
   179     name = cname;
   180   }
   181 
   182   void CplexBase::_setRowName(int row, const std::string &name) {
   183     char *cname;
   184     cname = const_cast<char*>(name.c_str());
   185     CPXchgrowname(cplexEnv(), _prob, 1, &row, &cname);
   186   }
   187 
   188   int CplexBase::_rowByName(const std::string& name) const {
   189     int index;
   190     if (CPXgetrowindex(cplexEnv(), _prob,
   191                        const_cast<char*>(name.c_str()), &index) == 0) {
   192       return index;
   193     }
   194     return -1;
   195   }
   196 
   197   void CplexBase::_setRowCoeffs(int i, ExprIterator b,
   198                                       ExprIterator e)
   199   {
   200     std::vector<int> indices;
   201     std::vector<int> rowlist;
   202     std::vector<Value> values;
   203 
   204     for(ExprIterator it=b; it!=e; ++it) {
   205       indices.push_back(it->first);
   206       values.push_back(it->second);
   207       rowlist.push_back(i);
   208     }
   209 
   210     CPXchgcoeflist(cplexEnv(), _prob, values.size(),
   211                    &rowlist.front(), &indices.front(), &values.front());
   212   }
   213 
   214   void CplexBase::_getRowCoeffs(int i, InsertIterator b) const {
   215     int tmp1, tmp2, tmp3, length;
   216     CPXgetrows(cplexEnv(), _prob, &tmp1, &tmp2, 0, 0, 0, &length, i, i);
   217 
   218     length = -length;
   219     std::vector<int> indices(length);
   220     std::vector<double> values(length);
   221 
   222     CPXgetrows(cplexEnv(), _prob, &tmp1, &tmp2,
   223                &indices.front(), &values.front(),
   224                length, &tmp3, i, i);
   225 
   226     for (int i = 0; i < length; ++i) {
   227       *b = std::make_pair(indices[i], values[i]);
   228       ++b;
   229     }
   230   }
   231 
   232   void CplexBase::_setColCoeffs(int i, ExprIterator b, ExprIterator e) {
   233     std::vector<int> indices;
   234     std::vector<int> collist;
   235     std::vector<Value> values;
   236 
   237     for(ExprIterator it=b; it!=e; ++it) {
   238       indices.push_back(it->first);
   239       values.push_back(it->second);
   240       collist.push_back(i);
   241     }
   242 
   243     CPXchgcoeflist(cplexEnv(), _prob, values.size(),
   244                    &indices.front(), &collist.front(), &values.front());
   245   }
   246 
   247   void CplexBase::_getColCoeffs(int i, InsertIterator b) const {
   248 
   249     int tmp1, tmp2, tmp3, length;
   250     CPXgetcols(cplexEnv(), _prob, &tmp1, &tmp2, 0, 0, 0, &length, i, i);
   251 
   252     length = -length;
   253     std::vector<int> indices(length);
   254     std::vector<double> values(length);
   255 
   256     CPXgetcols(cplexEnv(), _prob, &tmp1, &tmp2,
   257                &indices.front(), &values.front(),
   258                length, &tmp3, i, i);
   259 
   260     for (int i = 0; i < length; ++i) {
   261       *b = std::make_pair(indices[i], values[i]);
   262       ++b;
   263     }
   264 
   265   }
   266 
   267   void CplexBase::_setCoeff(int row, int col, Value value) {
   268     CPXchgcoef(cplexEnv(), _prob, row, col, value);
   269   }
   270 
   271   CplexBase::Value CplexBase::_getCoeff(int row, int col) const {
   272     CplexBase::Value value;
   273     CPXgetcoef(cplexEnv(), _prob, row, col, &value);
   274     return value;
   275   }
   276 
   277   void CplexBase::_setColLowerBound(int i, Value value) {
   278     const char s = 'L';
   279     CPXchgbds(cplexEnv(), _prob, 1, &i, &s, &value);
   280   }
   281 
   282   CplexBase::Value CplexBase::_getColLowerBound(int i) const {
   283     CplexBase::Value res;
   284     CPXgetlb(cplexEnv(), _prob, &res, i, i);
   285     return res <= -CPX_INFBOUND ? -INF : res;
   286   }
   287 
   288   void CplexBase::_setColUpperBound(int i, Value value)
   289   {
   290     const char s = 'U';
   291     CPXchgbds(cplexEnv(), _prob, 1, &i, &s, &value);
   292   }
   293 
   294   CplexBase::Value CplexBase::_getColUpperBound(int i) const {
   295     CplexBase::Value res;
   296     CPXgetub(cplexEnv(), _prob, &res, i, i);
   297     return res >= CPX_INFBOUND ? INF : res;
   298   }
   299 
   300   CplexBase::Value CplexBase::_getRowLowerBound(int i) const {
   301     char s;
   302     CPXgetsense(cplexEnv(), _prob, &s, i, i);
   303     CplexBase::Value res;
   304 
   305     switch (s) {
   306     case 'G':
   307     case 'R':
   308     case 'E':
   309       CPXgetrhs(cplexEnv(), _prob, &res, i, i);
   310       return res <= -CPX_INFBOUND ? -INF : res;
   311     default:
   312       return -INF;
   313     }
   314   }
   315 
   316   CplexBase::Value CplexBase::_getRowUpperBound(int i) const {
   317     char s;
   318     CPXgetsense(cplexEnv(), _prob, &s, i, i);
   319     CplexBase::Value res;
   320 
   321     switch (s) {
   322     case 'L':
   323     case 'E':
   324       CPXgetrhs(cplexEnv(), _prob, &res, i, i);
   325       return res >= CPX_INFBOUND ? INF : res;
   326     case 'R':
   327       CPXgetrhs(cplexEnv(), _prob, &res, i, i);
   328       {
   329         double rng;
   330         CPXgetrngval(cplexEnv(), _prob, &rng, i, i);
   331         res += rng;
   332       }
   333       return res >= CPX_INFBOUND ? INF : res;
   334     default:
   335       return INF;
   336     }
   337   }
   338 
   339   //This is easier to implement
   340   void CplexBase::_set_row_bounds(int i, Value lb, Value ub) {
   341     if (lb == -INF) {
   342       const char s = 'L';
   343       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
   344       CPXchgrhs(cplexEnv(), _prob, 1, &i, &ub);
   345     } else if (ub == INF) {
   346       const char s = 'G';
   347       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
   348       CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
   349     } else if (lb == ub){
   350       const char s = 'E';
   351       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
   352       CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
   353     } else {
   354       const char s = 'R';
   355       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
   356       CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
   357       double len = ub - lb;
   358       CPXchgrngval(cplexEnv(), _prob, 1, &i, &len);
   359     }
   360   }
   361 
   362   void CplexBase::_setRowLowerBound(int i, Value lb)
   363   {
   364     LEMON_ASSERT(lb != INF, "Invalid bound");
   365     _set_row_bounds(i, lb, CplexBase::_getRowUpperBound(i));
   366   }
   367 
   368   void CplexBase::_setRowUpperBound(int i, Value ub)
   369   {
   370 
   371     LEMON_ASSERT(ub != -INF, "Invalid bound");
   372     _set_row_bounds(i, CplexBase::_getRowLowerBound(i), ub);
   373   }
   374 
   375   void CplexBase::_setObjCoeffs(ExprIterator b, ExprIterator e)
   376   {
   377     std::vector<int> indices;
   378     std::vector<Value> values;
   379     for(ExprIterator it=b; it!=e; ++it) {
   380       indices.push_back(it->first);
   381       values.push_back(it->second);
   382     }
   383     CPXchgobj(cplexEnv(), _prob, values.size(),
   384               &indices.front(), &values.front());
   385 
   386   }
   387 
   388   void CplexBase::_getObjCoeffs(InsertIterator b) const
   389   {
   390     int num = CPXgetnumcols(cplexEnv(), _prob);
   391     std::vector<Value> x(num);
   392 
   393     CPXgetobj(cplexEnv(), _prob, &x.front(), 0, num - 1);
   394     for (int i = 0; i < num; ++i) {
   395       if (x[i] != 0.0) {
   396         *b = std::make_pair(i, x[i]);
   397         ++b;
   398       }
   399     }
   400   }
   401 
   402   void CplexBase::_setObjCoeff(int i, Value obj_coef)
   403   {
   404     CPXchgobj(cplexEnv(), _prob, 1, &i, &obj_coef);
   405   }
   406 
   407   CplexBase::Value CplexBase::_getObjCoeff(int i) const
   408   {
   409     Value x;
   410     CPXgetobj(cplexEnv(), _prob, &x, i, i);
   411     return x;
   412   }
   413 
   414   void CplexBase::_setSense(CplexBase::Sense sense) {
   415     switch (sense) {
   416     case MIN:
   417       CPXchgobjsen(cplexEnv(), _prob, CPX_MIN);
   418       break;
   419     case MAX:
   420       CPXchgobjsen(cplexEnv(), _prob, CPX_MAX);
   421       break;
   422     }
   423   }
   424 
   425   CplexBase::Sense CplexBase::_getSense() const {
   426     switch (CPXgetobjsen(cplexEnv(), _prob)) {
   427     case CPX_MIN:
   428       return MIN;
   429     case CPX_MAX:
   430       return MAX;
   431     default:
   432       LEMON_ASSERT(false, "Invalid sense");
   433       return CplexBase::Sense();
   434     }
   435   }
   436 
   437   void CplexBase::_clear() {
   438     CPXfreeprob(cplexEnv(),&_prob);
   439     int status;
   440     _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
   441   }
   442 
   443   void CplexBase::_messageLevel(MessageLevel level) {
   444     switch (level) {
   445     case MESSAGE_NOTHING:
   446       _message_enabled = false;
   447       break;
   448     case MESSAGE_ERROR:
   449     case MESSAGE_WARNING:
   450     case MESSAGE_NORMAL:
   451     case MESSAGE_VERBOSE:
   452       _message_enabled = true;
   453       break;
   454     }
   455   }
   456 
   457   void CplexBase::_applyMessageLevel() {
   458     CPXsetintparam(cplexEnv(), CPX_PARAM_SCRIND, 
   459                    _message_enabled ? CPX_ON : CPX_OFF);
   460   }
   461 
   462   // CplexLp members
   463 
   464   CplexLp::CplexLp()
   465     : LpBase(), LpSolver(), CplexBase() {}
   466 
   467   CplexLp::CplexLp(const CplexEnv& env)
   468     : LpBase(), LpSolver(), CplexBase(env) {}
   469 
   470   CplexLp::CplexLp(const CplexLp& other)
   471     : LpBase(), LpSolver(), CplexBase(other) {}
   472 
   473   CplexLp::~CplexLp() {}
   474 
   475   CplexLp* CplexLp::newSolver() const { return new CplexLp; }
   476   CplexLp* CplexLp::cloneSolver() const {return new CplexLp(*this); }
   477 
   478   const char* CplexLp::_solverName() const { return "CplexLp"; }
   479 
   480   void CplexLp::_clear_temporals() {
   481     _col_status.clear();
   482     _row_status.clear();
   483     _primal_ray.clear();
   484     _dual_ray.clear();
   485   }
   486 
   487   // The routine returns zero unless an error occurred during the
   488   // optimization. Examples of errors include exhausting available
   489   // memory (CPXERR_NO_MEMORY) or encountering invalid data in the
   490   // CPLEX problem object (CPXERR_NO_PROBLEM). Exceeding a
   491   // user-specified CPLEX limit, or proving the model infeasible or
   492   // unbounded, are not considered errors. Note that a zero return
   493   // value does not necessarily mean that a solution exists. Use query
   494   // routines CPXsolninfo, CPXgetstat, and CPXsolution to obtain
   495   // further information about the status of the optimization.
   496   CplexLp::SolveExitStatus CplexLp::convertStatus(int status) {
   497 #if CPX_VERSION >= 800
   498     if (status == 0) {
   499       switch (CPXgetstat(cplexEnv(), _prob)) {
   500       case CPX_STAT_OPTIMAL:
   501       case CPX_STAT_INFEASIBLE:
   502       case CPX_STAT_UNBOUNDED:
   503         return SOLVED;
   504       default:
   505         return UNSOLVED;
   506       }
   507     } else {
   508       return UNSOLVED;
   509     }
   510 #else
   511     if (status == 0) {
   512       //We want to exclude some cases
   513       switch (CPXgetstat(cplexEnv(), _prob)) {
   514       case CPX_OBJ_LIM:
   515       case CPX_IT_LIM_FEAS:
   516       case CPX_IT_LIM_INFEAS:
   517       case CPX_TIME_LIM_FEAS:
   518       case CPX_TIME_LIM_INFEAS:
   519         return UNSOLVED;
   520       default:
   521         return SOLVED;
   522       }
   523     } else {
   524       return UNSOLVED;
   525     }
   526 #endif
   527   }
   528 
   529   CplexLp::SolveExitStatus CplexLp::_solve() {
   530     _clear_temporals();
   531     _applyMessageLevel();
   532     return convertStatus(CPXlpopt(cplexEnv(), _prob));
   533   }
   534 
   535   CplexLp::SolveExitStatus CplexLp::solvePrimal() {
   536     _clear_temporals();
   537     _applyMessageLevel();
   538     return convertStatus(CPXprimopt(cplexEnv(), _prob));
   539   }
   540 
   541   CplexLp::SolveExitStatus CplexLp::solveDual() {
   542     _clear_temporals();
   543     _applyMessageLevel();
   544     return convertStatus(CPXdualopt(cplexEnv(), _prob));
   545   }
   546 
   547   CplexLp::SolveExitStatus CplexLp::solveBarrier() {
   548     _clear_temporals();
   549     _applyMessageLevel();
   550     return convertStatus(CPXbaropt(cplexEnv(), _prob));
   551   }
   552 
   553   CplexLp::Value CplexLp::_getPrimal(int i) const {
   554     Value x;
   555     CPXgetx(cplexEnv(), _prob, &x, i, i);
   556     return x;
   557   }
   558 
   559   CplexLp::Value CplexLp::_getDual(int i) const {
   560     Value y;
   561     CPXgetpi(cplexEnv(), _prob, &y, i, i);
   562     return y;
   563   }
   564 
   565   CplexLp::Value CplexLp::_getPrimalValue() const {
   566     Value objval;
   567     CPXgetobjval(cplexEnv(), _prob, &objval);
   568     return objval;
   569   }
   570 
   571   CplexLp::VarStatus CplexLp::_getColStatus(int i) const {
   572     if (_col_status.empty()) {
   573       _col_status.resize(CPXgetnumcols(cplexEnv(), _prob));
   574       CPXgetbase(cplexEnv(), _prob, &_col_status.front(), 0);
   575     }
   576     switch (_col_status[i]) {
   577     case CPX_BASIC:
   578       return BASIC;
   579     case CPX_FREE_SUPER:
   580       return FREE;
   581     case CPX_AT_LOWER:
   582       return LOWER;
   583     case CPX_AT_UPPER:
   584       return UPPER;
   585     default:
   586       LEMON_ASSERT(false, "Wrong column status");
   587       return CplexLp::VarStatus();
   588     }
   589   }
   590 
   591   CplexLp::VarStatus CplexLp::_getRowStatus(int i) const {
   592     if (_row_status.empty()) {
   593       _row_status.resize(CPXgetnumrows(cplexEnv(), _prob));
   594       CPXgetbase(cplexEnv(), _prob, 0, &_row_status.front());
   595     }
   596     switch (_row_status[i]) {
   597     case CPX_BASIC:
   598       return BASIC;
   599     case CPX_AT_LOWER:
   600       {
   601         char s;
   602         CPXgetsense(cplexEnv(), _prob, &s, i, i);
   603         return s != 'L' ? LOWER : UPPER;
   604       }
   605     case CPX_AT_UPPER:
   606       return UPPER;
   607     default:
   608       LEMON_ASSERT(false, "Wrong row status");
   609       return CplexLp::VarStatus();
   610     }
   611   }
   612 
   613   CplexLp::Value CplexLp::_getPrimalRay(int i) const {
   614     if (_primal_ray.empty()) {
   615       _primal_ray.resize(CPXgetnumcols(cplexEnv(), _prob));
   616       CPXgetray(cplexEnv(), _prob, &_primal_ray.front());
   617     }
   618     return _primal_ray[i];
   619   }
   620 
   621   CplexLp::Value CplexLp::_getDualRay(int i) const {
   622     if (_dual_ray.empty()) {
   623 
   624     }
   625     return _dual_ray[i];
   626   }
   627 
   628   // Cplex 7.0 status values
   629   // This table lists the statuses, returned by the CPXgetstat()
   630   // routine, for solutions to LP problems or mixed integer problems. If
   631   // no solution exists, the return value is zero.
   632 
   633   // For Simplex, Barrier
   634   // 1          CPX_OPTIMAL
   635   //          Optimal solution found
   636   // 2          CPX_INFEASIBLE
   637   //          Problem infeasible
   638   // 3    CPX_UNBOUNDED
   639   //          Problem unbounded
   640   // 4          CPX_OBJ_LIM
   641   //          Objective limit exceeded in Phase II
   642   // 5          CPX_IT_LIM_FEAS
   643   //          Iteration limit exceeded in Phase II
   644   // 6          CPX_IT_LIM_INFEAS
   645   //          Iteration limit exceeded in Phase I
   646   // 7          CPX_TIME_LIM_FEAS
   647   //          Time limit exceeded in Phase II
   648   // 8          CPX_TIME_LIM_INFEAS
   649   //          Time limit exceeded in Phase I
   650   // 9          CPX_NUM_BEST_FEAS
   651   //          Problem non-optimal, singularities in Phase II
   652   // 10         CPX_NUM_BEST_INFEAS
   653   //          Problem non-optimal, singularities in Phase I
   654   // 11         CPX_OPTIMAL_INFEAS
   655   //          Optimal solution found, unscaled infeasibilities
   656   // 12         CPX_ABORT_FEAS
   657   //          Aborted in Phase II
   658   // 13         CPX_ABORT_INFEAS
   659   //          Aborted in Phase I
   660   // 14          CPX_ABORT_DUAL_INFEAS
   661   //          Aborted in barrier, dual infeasible
   662   // 15          CPX_ABORT_PRIM_INFEAS
   663   //          Aborted in barrier, primal infeasible
   664   // 16          CPX_ABORT_PRIM_DUAL_INFEAS
   665   //          Aborted in barrier, primal and dual infeasible
   666   // 17          CPX_ABORT_PRIM_DUAL_FEAS
   667   //          Aborted in barrier, primal and dual feasible
   668   // 18          CPX_ABORT_CROSSOVER
   669   //          Aborted in crossover
   670   // 19          CPX_INForUNBD
   671   //          Infeasible or unbounded
   672   // 20   CPX_PIVOT
   673   //       User pivot used
   674   //
   675   // Pending return values
   676   // ??case CPX_ABORT_DUAL_INFEAS
   677   // ??case CPX_ABORT_CROSSOVER
   678   // ??case CPX_INForUNBD
   679   // ??case CPX_PIVOT
   680 
   681   //Some more interesting stuff:
   682 
   683   // CPX_PARAM_PROBMETHOD  1062  int  LPMETHOD
   684   // 0 Automatic
   685   // 1 Primal Simplex
   686   // 2 Dual Simplex
   687   // 3 Network Simplex
   688   // 4 Standard Barrier
   689   // Default: 0
   690   // Description: Method for linear optimization.
   691   // Determines which algorithm is used when CPXlpopt() (or "optimize"
   692   // in the Interactive Optimizer) is called. Currently the behavior of
   693   // the "Automatic" setting is that CPLEX simply invokes the dual
   694   // simplex method, but this capability may be expanded in the future
   695   // so that CPLEX chooses the method based on problem characteristics
   696 #if CPX_VERSION < 900
   697   void statusSwitch(CPXENVptr cplexEnv(),int& stat){
   698     int lpmethod;
   699     CPXgetintparam (cplexEnv(),CPX_PARAM_PROBMETHOD,&lpmethod);
   700     if (lpmethod==2){
   701       if (stat==CPX_UNBOUNDED){
   702         stat=CPX_INFEASIBLE;
   703       }
   704       else{
   705         if (stat==CPX_INFEASIBLE)
   706           stat=CPX_UNBOUNDED;
   707       }
   708     }
   709   }
   710 #else
   711   void statusSwitch(CPXENVptr,int&){}
   712 #endif
   713 
   714   CplexLp::ProblemType CplexLp::_getPrimalType() const {
   715     // Unboundedness not treated well: the following is from cplex 9.0 doc
   716     // About Unboundedness
   717 
   718     // The treatment of models that are unbounded involves a few
   719     // subtleties. Specifically, a declaration of unboundedness means that
   720     // ILOG CPLEX has determined that the model has an unbounded
   721     // ray. Given any feasible solution x with objective z, a multiple of
   722     // the unbounded ray can be added to x to give a feasible solution
   723     // with objective z-1 (or z+1 for maximization models). Thus, if a
   724     // feasible solution exists, then the optimal objective is
   725     // unbounded. Note that ILOG CPLEX has not necessarily concluded that
   726     // a feasible solution exists. Users can call the routine CPXsolninfo
   727     // to determine whether ILOG CPLEX has also concluded that the model
   728     // has a feasible solution.
   729 
   730     int stat = CPXgetstat(cplexEnv(), _prob);
   731 #if CPX_VERSION >= 800
   732     switch (stat)
   733       {
   734       case CPX_STAT_OPTIMAL:
   735         return OPTIMAL;
   736       case CPX_STAT_UNBOUNDED:
   737         return UNBOUNDED;
   738       case CPX_STAT_INFEASIBLE:
   739         return INFEASIBLE;
   740       default:
   741         return UNDEFINED;
   742       }
   743 #else
   744     statusSwitch(cplexEnv(),stat);
   745     //CPXgetstat(cplexEnv(), _prob);
   746     switch (stat) {
   747     case 0:
   748       return UNDEFINED; //Undefined
   749     case CPX_OPTIMAL://Optimal
   750       return OPTIMAL;
   751     case CPX_UNBOUNDED://Unbounded
   752       return INFEASIBLE;//In case of dual simplex
   753       //return UNBOUNDED;
   754     case CPX_INFEASIBLE://Infeasible
   755       //    case CPX_IT_LIM_INFEAS:
   756       //     case CPX_TIME_LIM_INFEAS:
   757       //     case CPX_NUM_BEST_INFEAS:
   758       //     case CPX_OPTIMAL_INFEAS:
   759       //     case CPX_ABORT_INFEAS:
   760       //     case CPX_ABORT_PRIM_INFEAS:
   761       //     case CPX_ABORT_PRIM_DUAL_INFEAS:
   762       return UNBOUNDED;//In case of dual simplex
   763       //return INFEASIBLE;
   764       //     case CPX_OBJ_LIM:
   765       //     case CPX_IT_LIM_FEAS:
   766       //     case CPX_TIME_LIM_FEAS:
   767       //     case CPX_NUM_BEST_FEAS:
   768       //     case CPX_ABORT_FEAS:
   769       //     case CPX_ABORT_PRIM_DUAL_FEAS:
   770       //       return FEASIBLE;
   771     default:
   772       return UNDEFINED; //Everything else comes here
   773       //FIXME error
   774     }
   775 #endif
   776   }
   777 
   778   // Cplex 9.0 status values
   779   // CPX_STAT_ABORT_DUAL_OBJ_LIM
   780   // CPX_STAT_ABORT_IT_LIM
   781   // CPX_STAT_ABORT_OBJ_LIM
   782   // CPX_STAT_ABORT_PRIM_OBJ_LIM
   783   // CPX_STAT_ABORT_TIME_LIM
   784   // CPX_STAT_ABORT_USER
   785   // CPX_STAT_FEASIBLE_RELAXED
   786   // CPX_STAT_INFEASIBLE
   787   // CPX_STAT_INForUNBD
   788   // CPX_STAT_NUM_BEST
   789   // CPX_STAT_OPTIMAL
   790   // CPX_STAT_OPTIMAL_FACE_UNBOUNDED
   791   // CPX_STAT_OPTIMAL_INFEAS
   792   // CPX_STAT_OPTIMAL_RELAXED
   793   // CPX_STAT_UNBOUNDED
   794 
   795   CplexLp::ProblemType CplexLp::_getDualType() const {
   796     int stat = CPXgetstat(cplexEnv(), _prob);
   797 #if CPX_VERSION >= 800
   798     switch (stat) {
   799     case CPX_STAT_OPTIMAL:
   800       return OPTIMAL;
   801     case CPX_STAT_UNBOUNDED:
   802       return INFEASIBLE;
   803     default:
   804       return UNDEFINED;
   805     }
   806 #else
   807     statusSwitch(cplexEnv(),stat);
   808     switch (stat) {
   809     case 0:
   810       return UNDEFINED; //Undefined
   811     case CPX_OPTIMAL://Optimal
   812       return OPTIMAL;
   813     case CPX_UNBOUNDED:
   814       return INFEASIBLE;
   815     default:
   816       return UNDEFINED; //Everything else comes here
   817       //FIXME error
   818     }
   819 #endif
   820   }
   821 
   822   // CplexMip members
   823 
   824   CplexMip::CplexMip()
   825     : LpBase(), MipSolver(), CplexBase() {
   826 
   827 #if CPX_VERSION < 800
   828     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MIP);
   829 #else
   830     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MILP);
   831 #endif
   832   }
   833 
   834   CplexMip::CplexMip(const CplexEnv& env)
   835     : LpBase(), MipSolver(), CplexBase(env) {
   836 
   837 #if CPX_VERSION < 800
   838     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MIP);
   839 #else
   840     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MILP);
   841 #endif
   842 
   843   }
   844 
   845   CplexMip::CplexMip(const CplexMip& other)
   846     : LpBase(), MipSolver(), CplexBase(other) {}
   847 
   848   CplexMip::~CplexMip() {}
   849 
   850   CplexMip* CplexMip::newSolver() const { return new CplexMip; }
   851   CplexMip* CplexMip::cloneSolver() const {return new CplexMip(*this); }
   852 
   853   const char* CplexMip::_solverName() const { return "CplexMip"; }
   854 
   855   void CplexMip::_setColType(int i, CplexMip::ColTypes col_type) {
   856 
   857     // Note If a variable is to be changed to binary, a call to CPXchgbds
   858     // should also be made to change the bounds to 0 and 1.
   859 
   860     switch (col_type){
   861     case INTEGER: {
   862       const char t = 'I';
   863       CPXchgctype (cplexEnv(), _prob, 1, &i, &t);
   864     } break;
   865     case REAL: {
   866       const char t = 'C';
   867       CPXchgctype (cplexEnv(), _prob, 1, &i, &t);
   868     } break;
   869     default:
   870       break;
   871     }
   872   }
   873 
   874   CplexMip::ColTypes CplexMip::_getColType(int i) const {
   875     char t;
   876     CPXgetctype (cplexEnv(), _prob, &t, i, i);
   877     switch (t) {
   878     case 'I':
   879       return INTEGER;
   880     case 'C':
   881       return REAL;
   882     default:
   883       LEMON_ASSERT(false, "Invalid column type");
   884       return ColTypes();
   885     }
   886 
   887   }
   888 
   889   CplexMip::SolveExitStatus CplexMip::_solve() {
   890     int status;
   891     _applyMessageLevel();
   892     status = CPXmipopt (cplexEnv(), _prob);
   893     if (status==0)
   894       return SOLVED;
   895     else
   896       return UNSOLVED;
   897 
   898   }
   899 
   900 
   901   CplexMip::ProblemType CplexMip::_getType() const {
   902 
   903     int stat = CPXgetstat(cplexEnv(), _prob);
   904 
   905     //Fortunately, MIP statuses did not change for cplex 8.0
   906     switch (stat) {
   907     case CPXMIP_OPTIMAL:
   908       // Optimal integer solution has been found.
   909     case CPXMIP_OPTIMAL_TOL:
   910       // Optimal soluton with the tolerance defined by epgap or epagap has
   911       // been found.
   912       return OPTIMAL;
   913       //This also exists in later issues
   914       //    case CPXMIP_UNBOUNDED:
   915       //return UNBOUNDED;
   916       case CPXMIP_INFEASIBLE:
   917         return INFEASIBLE;
   918     default:
   919       return UNDEFINED;
   920     }
   921     //Unboundedness not treated well: the following is from cplex 9.0 doc
   922     // About Unboundedness
   923 
   924     // The treatment of models that are unbounded involves a few
   925     // subtleties. Specifically, a declaration of unboundedness means that
   926     // ILOG CPLEX has determined that the model has an unbounded
   927     // ray. Given any feasible solution x with objective z, a multiple of
   928     // the unbounded ray can be added to x to give a feasible solution
   929     // with objective z-1 (or z+1 for maximization models). Thus, if a
   930     // feasible solution exists, then the optimal objective is
   931     // unbounded. Note that ILOG CPLEX has not necessarily concluded that
   932     // a feasible solution exists. Users can call the routine CPXsolninfo
   933     // to determine whether ILOG CPLEX has also concluded that the model
   934     // has a feasible solution.
   935   }
   936 
   937   CplexMip::Value CplexMip::_getSol(int i) const {
   938     Value x;
   939     CPXgetmipx(cplexEnv(), _prob, &x, i, i);
   940     return x;
   941   }
   942 
   943   CplexMip::Value CplexMip::_getSolValue() const {
   944     Value objval;
   945     CPXgetmipobjval(cplexEnv(), _prob, &objval);
   946     return objval;
   947   }
   948 
   949 } //namespace lemon
   950