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