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