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