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