alpar@461: /* -*- mode: C++; indent-tabs-mode: nil; -*-
alpar@461:  *
alpar@461:  * This file is a part of LEMON, a generic C++ optimization library.
alpar@461:  *
alpar@461:  * Copyright (C) 2003-2008
alpar@461:  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
alpar@461:  * (Egervary Research Group on Combinatorial Optimization, EGRES).
alpar@461:  *
alpar@461:  * Permission to use, modify and distribute this software is granted
alpar@461:  * provided that this copyright notice appears in all copies. For
alpar@461:  * precise terms see the accompanying LICENSE file.
alpar@461:  *
alpar@461:  * This software is provided "AS IS" with no warranty of any kind,
alpar@461:  * express or implied, and with no claim as to its suitability for any
alpar@461:  * purpose.
alpar@461:  *
alpar@461:  */
alpar@461: 
alpar@461: #include <iostream>
alpar@461: #include <vector>
alpar@461: #include <cstring>
alpar@461: 
alpar@461: #include <lemon/cplex.h>
alpar@461: 
alpar@461: extern "C" {
alpar@461: #include <ilcplex/cplex.h>
alpar@461: }
alpar@461: 
alpar@461: 
alpar@461: ///\file
alpar@461: ///\brief Implementation of the LEMON-CPLEX lp solver interface.
alpar@461: namespace lemon {
alpar@461: 
alpar@461:   CplexEnv::LicenseError::LicenseError(int status) {
alpar@461:     if (!CPXgeterrorstring(0, status, _message)) {
alpar@461:       std::strcpy(_message, "Cplex unknown error");
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   CplexEnv::CplexEnv() {
alpar@461:     int status;
alpar@461:     _cnt = new int;
alpar@461:     _env = CPXopenCPLEX(&status);
alpar@461:     if (_env == 0) {
alpar@461:       delete _cnt;
alpar@461:       _cnt = 0;
alpar@461:       throw LicenseError(status);
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   CplexEnv::CplexEnv(const CplexEnv& other) {
alpar@461:     _env = other._env;
alpar@461:     _cnt = other._cnt;
alpar@461:     ++(*_cnt);
alpar@461:   }
alpar@461: 
alpar@461:   CplexEnv& CplexEnv::operator=(const CplexEnv& other) {
alpar@461:     _env = other._env;
alpar@461:     _cnt = other._cnt;
alpar@461:     ++(*_cnt);
alpar@461:     return *this;
alpar@461:   }
alpar@461: 
alpar@461:   CplexEnv::~CplexEnv() {
alpar@461:     --(*_cnt);
alpar@461:     if (*_cnt == 0) {
alpar@461:       delete _cnt;
alpar@461:       CPXcloseCPLEX(&_env);
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::CplexBase() : LpBase() {
alpar@461:     int status;
alpar@461:     _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::CplexBase(const CplexEnv& env)
alpar@461:     : LpBase(), _env(env) {
alpar@461:     int status;
alpar@461:     _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::CplexBase(const CplexBase& cplex)
alpar@461:     : LpBase() {
alpar@461:     int status;
alpar@461:     _prob = CPXcloneprob(cplexEnv(), cplex._prob, &status);
alpar@461:     rows = cplex.rows;
alpar@461:     cols = cplex.cols;
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::~CplexBase() {
alpar@461:     CPXfreeprob(cplexEnv(),&_prob);
alpar@461:   }
alpar@461: 
alpar@461:   int CplexBase::_addCol() {
alpar@461:     int i = CPXgetnumcols(cplexEnv(), _prob);
alpar@461:     double lb = -INF, ub = INF;
alpar@461:     CPXnewcols(cplexEnv(), _prob, 1, 0, &lb, &ub, 0, 0);
alpar@461:     return i;
alpar@461:   }
alpar@461: 
alpar@461: 
alpar@461:   int CplexBase::_addRow() {
alpar@461:     int i = CPXgetnumrows(cplexEnv(), _prob);
alpar@461:     const double ub = INF;
alpar@461:     const char s = 'L';
alpar@461:     CPXnewrows(cplexEnv(), _prob, 1, &ub, &s, 0, 0);
alpar@461:     return i;
alpar@461:   }
alpar@461: 
alpar@461: 
alpar@461:   void CplexBase::_eraseCol(int i) {
alpar@461:     CPXdelcols(cplexEnv(), _prob, i, i);
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_eraseRow(int i) {
alpar@461:     CPXdelrows(cplexEnv(), _prob, i, i);
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_eraseColId(int i) {
alpar@461:     cols.eraseIndex(i);
alpar@461:     cols.shiftIndices(i);
alpar@461:   }
alpar@461:   void CplexBase::_eraseRowId(int i) {
alpar@461:     rows.eraseIndex(i);
alpar@461:     rows.shiftIndices(i);
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_getColName(int col, std::string &name) const {
alpar@461:     int size;
alpar@461:     CPXgetcolname(cplexEnv(), _prob, 0, 0, 0, &size, col, col);
alpar@461:     if (size == 0) {
alpar@461:       name.clear();
alpar@461:       return;
alpar@461:     }
alpar@461: 
alpar@461:     size *= -1;
alpar@461:     std::vector<char> buf(size);
alpar@461:     char *cname;
alpar@461:     int tmp;
alpar@461:     CPXgetcolname(cplexEnv(), _prob, &cname, &buf.front(), size,
alpar@461:                   &tmp, col, col);
alpar@461:     name = cname;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setColName(int col, const std::string &name) {
alpar@461:     char *cname;
alpar@461:     cname = const_cast<char*>(name.c_str());
alpar@461:     CPXchgcolname(cplexEnv(), _prob, 1, &col, &cname);
alpar@461:   }
alpar@461: 
alpar@461:   int CplexBase::_colByName(const std::string& name) const {
alpar@461:     int index;
alpar@461:     if (CPXgetcolindex(cplexEnv(), _prob,
alpar@461:                        const_cast<char*>(name.c_str()), &index) == 0) {
alpar@461:       return index;
alpar@461:     }
alpar@461:     return -1;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_getRowName(int row, std::string &name) const {
alpar@461:     int size;
alpar@461:     CPXgetrowname(cplexEnv(), _prob, 0, 0, 0, &size, row, row);
alpar@461:     if (size == 0) {
alpar@461:       name.clear();
alpar@461:       return;
alpar@461:     }
alpar@461: 
alpar@461:     size *= -1;
alpar@461:     std::vector<char> buf(size);
alpar@461:     char *cname;
alpar@461:     int tmp;
alpar@461:     CPXgetrowname(cplexEnv(), _prob, &cname, &buf.front(), size,
alpar@461:                   &tmp, row, row);
alpar@461:     name = cname;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setRowName(int row, const std::string &name) {
alpar@461:     char *cname;
alpar@461:     cname = const_cast<char*>(name.c_str());
alpar@461:     CPXchgrowname(cplexEnv(), _prob, 1, &row, &cname);
alpar@461:   }
alpar@461: 
alpar@461:   int CplexBase::_rowByName(const std::string& name) const {
alpar@461:     int index;
alpar@461:     if (CPXgetrowindex(cplexEnv(), _prob,
alpar@461:                        const_cast<char*>(name.c_str()), &index) == 0) {
alpar@461:       return index;
alpar@461:     }
alpar@461:     return -1;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setRowCoeffs(int i, ExprIterator b,
alpar@461:                                       ExprIterator e)
alpar@461:   {
alpar@461:     std::vector<int> indices;
alpar@461:     std::vector<int> rowlist;
alpar@461:     std::vector<Value> values;
alpar@461: 
alpar@461:     for(ExprIterator it=b; it!=e; ++it) {
alpar@461:       indices.push_back(it->first);
alpar@461:       values.push_back(it->second);
alpar@461:       rowlist.push_back(i);
alpar@461:     }
alpar@461: 
alpar@461:     CPXchgcoeflist(cplexEnv(), _prob, values.size(),
alpar@461:                    &rowlist.front(), &indices.front(), &values.front());
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_getRowCoeffs(int i, InsertIterator b) const {
alpar@461:     int tmp1, tmp2, tmp3, length;
alpar@461:     CPXgetrows(cplexEnv(), _prob, &tmp1, &tmp2, 0, 0, 0, &length, i, i);
alpar@461: 
alpar@461:     length = -length;
alpar@461:     std::vector<int> indices(length);
alpar@461:     std::vector<double> values(length);
alpar@461: 
alpar@461:     CPXgetrows(cplexEnv(), _prob, &tmp1, &tmp2,
alpar@461:                &indices.front(), &values.front(),
alpar@461:                length, &tmp3, i, i);
alpar@461: 
alpar@461:     for (int i = 0; i < length; ++i) {
alpar@461:       *b = std::make_pair(indices[i], values[i]);
alpar@461:       ++b;
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setColCoeffs(int i, ExprIterator b, ExprIterator e) {
alpar@461:     std::vector<int> indices;
alpar@461:     std::vector<int> collist;
alpar@461:     std::vector<Value> values;
alpar@461: 
alpar@461:     for(ExprIterator it=b; it!=e; ++it) {
alpar@461:       indices.push_back(it->first);
alpar@461:       values.push_back(it->second);
alpar@461:       collist.push_back(i);
alpar@461:     }
alpar@461: 
alpar@461:     CPXchgcoeflist(cplexEnv(), _prob, values.size(),
alpar@461:                    &indices.front(), &collist.front(), &values.front());
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_getColCoeffs(int i, InsertIterator b) const {
alpar@461: 
alpar@461:     int tmp1, tmp2, tmp3, length;
alpar@461:     CPXgetcols(cplexEnv(), _prob, &tmp1, &tmp2, 0, 0, 0, &length, i, i);
alpar@461: 
alpar@461:     length = -length;
alpar@461:     std::vector<int> indices(length);
alpar@461:     std::vector<double> values(length);
alpar@461: 
alpar@461:     CPXgetcols(cplexEnv(), _prob, &tmp1, &tmp2,
alpar@461:                &indices.front(), &values.front(),
alpar@461:                length, &tmp3, i, i);
alpar@461: 
alpar@461:     for (int i = 0; i < length; ++i) {
alpar@461:       *b = std::make_pair(indices[i], values[i]);
alpar@461:       ++b;
alpar@461:     }
alpar@461: 
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setCoeff(int row, int col, Value value) {
alpar@461:     CPXchgcoef(cplexEnv(), _prob, row, col, value);
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Value CplexBase::_getCoeff(int row, int col) const {
alpar@461:     CplexBase::Value value;
alpar@461:     CPXgetcoef(cplexEnv(), _prob, row, col, &value);
alpar@461:     return value;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setColLowerBound(int i, Value value) {
alpar@461:     const char s = 'L';
alpar@461:     CPXchgbds(cplexEnv(), _prob, 1, &i, &s, &value);
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Value CplexBase::_getColLowerBound(int i) const {
alpar@461:     CplexBase::Value res;
alpar@461:     CPXgetlb(cplexEnv(), _prob, &res, i, i);
alpar@461:     return res <= -CPX_INFBOUND ? -INF : res;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setColUpperBound(int i, Value value)
alpar@461:   {
alpar@461:     const char s = 'U';
alpar@461:     CPXchgbds(cplexEnv(), _prob, 1, &i, &s, &value);
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Value CplexBase::_getColUpperBound(int i) const {
alpar@461:     CplexBase::Value res;
alpar@461:     CPXgetub(cplexEnv(), _prob, &res, i, i);
alpar@461:     return res >= CPX_INFBOUND ? INF : res;
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Value CplexBase::_getRowLowerBound(int i) const {
alpar@461:     char s;
alpar@461:     CPXgetsense(cplexEnv(), _prob, &s, i, i);
alpar@461:     CplexBase::Value res;
alpar@461: 
alpar@461:     switch (s) {
alpar@461:     case 'G':
alpar@461:     case 'R':
alpar@461:     case 'E':
alpar@461:       CPXgetrhs(cplexEnv(), _prob, &res, i, i);
alpar@461:       return res <= -CPX_INFBOUND ? -INF : res;
alpar@461:     default:
alpar@461:       return -INF;
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Value CplexBase::_getRowUpperBound(int i) const {
alpar@461:     char s;
alpar@461:     CPXgetsense(cplexEnv(), _prob, &s, i, i);
alpar@461:     CplexBase::Value res;
alpar@461: 
alpar@461:     switch (s) {
alpar@461:     case 'L':
alpar@461:     case 'E':
alpar@461:       CPXgetrhs(cplexEnv(), _prob, &res, i, i);
alpar@461:       return res >= CPX_INFBOUND ? INF : res;
alpar@461:     case 'R':
alpar@461:       CPXgetrhs(cplexEnv(), _prob, &res, i, i);
alpar@461:       {
alpar@461:         double rng;
alpar@461:         CPXgetrngval(cplexEnv(), _prob, &rng, i, i);
alpar@461:         res += rng;
alpar@461:       }
alpar@461:       return res >= CPX_INFBOUND ? INF : res;
alpar@461:     default:
alpar@461:       return INF;
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   //This is easier to implement
alpar@461:   void CplexBase::_set_row_bounds(int i, Value lb, Value ub) {
alpar@461:     if (lb == -INF) {
alpar@461:       const char s = 'L';
alpar@461:       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
alpar@461:       CPXchgrhs(cplexEnv(), _prob, 1, &i, &ub);
alpar@461:     } else if (ub == INF) {
alpar@461:       const char s = 'G';
alpar@461:       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
alpar@461:       CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
alpar@461:     } else if (lb == ub){
alpar@461:       const char s = 'E';
alpar@461:       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
alpar@461:       CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
alpar@461:     } else {
alpar@461:       const char s = 'R';
alpar@461:       CPXchgsense(cplexEnv(), _prob, 1, &i, &s);
alpar@461:       CPXchgrhs(cplexEnv(), _prob, 1, &i, &lb);
alpar@461:       double len = ub - lb;
alpar@461:       CPXchgrngval(cplexEnv(), _prob, 1, &i, &len);
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setRowLowerBound(int i, Value lb)
alpar@461:   {
alpar@461:     LEMON_ASSERT(lb != INF, "Invalid bound");
alpar@461:     _set_row_bounds(i, lb, CplexBase::_getRowUpperBound(i));
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setRowUpperBound(int i, Value ub)
alpar@461:   {
alpar@461: 
alpar@461:     LEMON_ASSERT(ub != -INF, "Invalid bound");
alpar@461:     _set_row_bounds(i, CplexBase::_getRowLowerBound(i), ub);
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setObjCoeffs(ExprIterator b, ExprIterator e)
alpar@461:   {
alpar@461:     std::vector<int> indices;
alpar@461:     std::vector<Value> values;
alpar@461:     for(ExprIterator it=b; it!=e; ++it) {
alpar@461:       indices.push_back(it->first);
alpar@461:       values.push_back(it->second);
alpar@461:     }
alpar@461:     CPXchgobj(cplexEnv(), _prob, values.size(),
alpar@461:               &indices.front(), &values.front());
alpar@461: 
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_getObjCoeffs(InsertIterator b) const
alpar@461:   {
alpar@461:     int num = CPXgetnumcols(cplexEnv(), _prob);
alpar@461:     std::vector<Value> x(num);
alpar@461: 
alpar@461:     CPXgetobj(cplexEnv(), _prob, &x.front(), 0, num - 1);
alpar@461:     for (int i = 0; i < num; ++i) {
alpar@461:       if (x[i] != 0.0) {
alpar@461:         *b = std::make_pair(i, x[i]);
alpar@461:         ++b;
alpar@461:       }
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setObjCoeff(int i, Value obj_coef)
alpar@461:   {
alpar@461:     CPXchgobj(cplexEnv(), _prob, 1, &i, &obj_coef);
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Value CplexBase::_getObjCoeff(int i) const
alpar@461:   {
alpar@461:     Value x;
alpar@461:     CPXgetobj(cplexEnv(), _prob, &x, i, i);
alpar@461:     return x;
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_setSense(CplexBase::Sense sense) {
alpar@461:     switch (sense) {
alpar@461:     case MIN:
alpar@461:       CPXchgobjsen(cplexEnv(), _prob, CPX_MIN);
alpar@461:       break;
alpar@461:     case MAX:
alpar@461:       CPXchgobjsen(cplexEnv(), _prob, CPX_MAX);
alpar@461:       break;
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   CplexBase::Sense CplexBase::_getSense() const {
alpar@461:     switch (CPXgetobjsen(cplexEnv(), _prob)) {
alpar@461:     case CPX_MIN:
alpar@461:       return MIN;
alpar@461:     case CPX_MAX:
alpar@461:       return MAX;
alpar@461:     default:
alpar@461:       LEMON_ASSERT(false, "Invalid sense");
alpar@461:       return CplexBase::Sense();
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@461:   void CplexBase::_clear() {
alpar@461:     CPXfreeprob(cplexEnv(),&_prob);
alpar@461:     int status;
alpar@461:     _prob = CPXcreateprob(cplexEnv(), &status, "Cplex problem");
alpar@461:     rows.clear();
alpar@461:     cols.clear();
alpar@461:   }
alpar@461: 
alpar@462:   // CplexLp members
alpar@461: 
alpar@462:   CplexLp::CplexLp()
alpar@461:     : LpBase(), CplexBase(), LpSolver() {}
alpar@461: 
alpar@462:   CplexLp::CplexLp(const CplexEnv& env)
alpar@461:     : LpBase(), CplexBase(env), LpSolver() {}
alpar@461: 
alpar@462:   CplexLp::CplexLp(const CplexLp& other)
alpar@461:     : LpBase(), CplexBase(other), LpSolver() {}
alpar@461: 
alpar@462:   CplexLp::~CplexLp() {}
alpar@461: 
alpar@462:   CplexLp* CplexLp::_newSolver() const { return new CplexLp; }
alpar@462:   CplexLp* CplexLp::_cloneSolver() const {return new CplexLp(*this); }
alpar@461: 
alpar@462:   const char* CplexLp::_solverName() const { return "CplexLp"; }
alpar@461: 
alpar@462:   void CplexLp::_clear_temporals() {
alpar@461:     _col_status.clear();
alpar@461:     _row_status.clear();
alpar@461:     _primal_ray.clear();
alpar@461:     _dual_ray.clear();
alpar@461:   }
alpar@461: 
alpar@461:   // The routine returns zero unless an error occurred during the
alpar@461:   // optimization. Examples of errors include exhausting available
alpar@461:   // memory (CPXERR_NO_MEMORY) or encountering invalid data in the
alpar@461:   // CPLEX problem object (CPXERR_NO_PROBLEM). Exceeding a
alpar@461:   // user-specified CPLEX limit, or proving the model infeasible or
alpar@461:   // unbounded, are not considered errors. Note that a zero return
alpar@461:   // value does not necessarily mean that a solution exists. Use query
alpar@461:   // routines CPXsolninfo, CPXgetstat, and CPXsolution to obtain
alpar@461:   // further information about the status of the optimization.
alpar@462:   CplexLp::SolveExitStatus CplexLp::convertStatus(int status) {
alpar@461: #if CPX_VERSION >= 800
alpar@461:     if (status == 0) {
alpar@461:       switch (CPXgetstat(cplexEnv(), _prob)) {
alpar@461:       case CPX_STAT_OPTIMAL:
alpar@461:       case CPX_STAT_INFEASIBLE:
alpar@461:       case CPX_STAT_UNBOUNDED:
alpar@461:         return SOLVED;
alpar@461:       default:
alpar@461:         return UNSOLVED;
alpar@461:       }
alpar@461:     } else {
alpar@461:       return UNSOLVED;
alpar@461:     }
alpar@461: #else
alpar@461:     if (status == 0) {
alpar@461:       //We want to exclude some cases
alpar@461:       switch (CPXgetstat(cplexEnv(), _prob)) {
alpar@461:       case CPX_OBJ_LIM:
alpar@461:       case CPX_IT_LIM_FEAS:
alpar@461:       case CPX_IT_LIM_INFEAS:
alpar@461:       case CPX_TIME_LIM_FEAS:
alpar@461:       case CPX_TIME_LIM_INFEAS:
alpar@461:         return UNSOLVED;
alpar@461:       default:
alpar@461:         return SOLVED;
alpar@461:       }
alpar@461:     } else {
alpar@461:       return UNSOLVED;
alpar@461:     }
alpar@461: #endif
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::SolveExitStatus CplexLp::_solve() {
alpar@461:     _clear_temporals();
alpar@461:     return convertStatus(CPXlpopt(cplexEnv(), _prob));
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::SolveExitStatus CplexLp::solvePrimal() {
alpar@461:     _clear_temporals();
alpar@461:     return convertStatus(CPXprimopt(cplexEnv(), _prob));
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::SolveExitStatus CplexLp::solveDual() {
alpar@461:     _clear_temporals();
alpar@461:     return convertStatus(CPXdualopt(cplexEnv(), _prob));
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::SolveExitStatus CplexLp::solveBarrier() {
alpar@461:     _clear_temporals();
alpar@461:     return convertStatus(CPXbaropt(cplexEnv(), _prob));
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::Value CplexLp::_getPrimal(int i) const {
alpar@461:     Value x;
alpar@461:     CPXgetx(cplexEnv(), _prob, &x, i, i);
alpar@461:     return x;
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::Value CplexLp::_getDual(int i) const {
alpar@461:     Value y;
alpar@461:     CPXgetpi(cplexEnv(), _prob, &y, i, i);
alpar@461:     return y;
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::Value CplexLp::_getPrimalValue() const {
alpar@461:     Value objval;
alpar@461:     CPXgetobjval(cplexEnv(), _prob, &objval);
alpar@461:     return objval;
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::VarStatus CplexLp::_getColStatus(int i) const {
alpar@461:     if (_col_status.empty()) {
alpar@461:       _col_status.resize(CPXgetnumcols(cplexEnv(), _prob));
alpar@461:       CPXgetbase(cplexEnv(), _prob, &_col_status.front(), 0);
alpar@461:     }
alpar@461:     switch (_col_status[i]) {
alpar@461:     case CPX_BASIC:
alpar@461:       return BASIC;
alpar@461:     case CPX_FREE_SUPER:
alpar@461:       return FREE;
alpar@461:     case CPX_AT_LOWER:
alpar@461:       return LOWER;
alpar@461:     case CPX_AT_UPPER:
alpar@461:       return UPPER;
alpar@461:     default:
alpar@461:       LEMON_ASSERT(false, "Wrong column status");
alpar@462:       return CplexLp::VarStatus();
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::VarStatus CplexLp::_getRowStatus(int i) const {
alpar@461:     if (_row_status.empty()) {
alpar@461:       _row_status.resize(CPXgetnumrows(cplexEnv(), _prob));
alpar@461:       CPXgetbase(cplexEnv(), _prob, 0, &_row_status.front());
alpar@461:     }
alpar@461:     switch (_row_status[i]) {
alpar@461:     case CPX_BASIC:
alpar@461:       return BASIC;
alpar@461:     case CPX_AT_LOWER:
alpar@461:       {
alpar@461:         char s;
alpar@461:         CPXgetsense(cplexEnv(), _prob, &s, i, i);
alpar@461:         return s != 'L' ? LOWER : UPPER;
alpar@461:       }
alpar@461:     case CPX_AT_UPPER:
alpar@461:       return UPPER;
alpar@461:     default:
alpar@461:       LEMON_ASSERT(false, "Wrong row status");
alpar@462:       return CplexLp::VarStatus();
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::Value CplexLp::_getPrimalRay(int i) const {
alpar@461:     if (_primal_ray.empty()) {
alpar@461:       _primal_ray.resize(CPXgetnumcols(cplexEnv(), _prob));
alpar@461:       CPXgetray(cplexEnv(), _prob, &_primal_ray.front());
alpar@461:     }
alpar@461:     return _primal_ray[i];
alpar@461:   }
alpar@461: 
alpar@462:   CplexLp::Value CplexLp::_getDualRay(int i) const {
alpar@461:     if (_dual_ray.empty()) {
alpar@461: 
alpar@461:     }
alpar@461:     return _dual_ray[i];
alpar@461:   }
alpar@461: 
alpar@461:   //7.5-os cplex statusai (Vigyazat: a 9.0-asei masok!)
alpar@461:   // This table lists the statuses, returned by the CPXgetstat()
alpar@461:   // routine, for solutions to LP problems or mixed integer problems. If
alpar@461:   // no solution exists, the return value is zero.
alpar@461: 
alpar@461:   // For Simplex, Barrier
alpar@461:   // 1          CPX_OPTIMAL
alpar@461:   //          Optimal solution found
alpar@461:   // 2          CPX_INFEASIBLE
alpar@461:   //          Problem infeasible
alpar@461:   // 3    CPX_UNBOUNDED
alpar@461:   //          Problem unbounded
alpar@461:   // 4          CPX_OBJ_LIM
alpar@461:   //          Objective limit exceeded in Phase II
alpar@461:   // 5          CPX_IT_LIM_FEAS
alpar@461:   //          Iteration limit exceeded in Phase II
alpar@461:   // 6          CPX_IT_LIM_INFEAS
alpar@461:   //          Iteration limit exceeded in Phase I
alpar@461:   // 7          CPX_TIME_LIM_FEAS
alpar@461:   //          Time limit exceeded in Phase II
alpar@461:   // 8          CPX_TIME_LIM_INFEAS
alpar@461:   //          Time limit exceeded in Phase I
alpar@461:   // 9          CPX_NUM_BEST_FEAS
alpar@461:   //          Problem non-optimal, singularities in Phase II
alpar@461:   // 10         CPX_NUM_BEST_INFEAS
alpar@461:   //          Problem non-optimal, singularities in Phase I
alpar@461:   // 11         CPX_OPTIMAL_INFEAS
alpar@461:   //          Optimal solution found, unscaled infeasibilities
alpar@461:   // 12         CPX_ABORT_FEAS
alpar@461:   //          Aborted in Phase II
alpar@461:   // 13         CPX_ABORT_INFEAS
alpar@461:   //          Aborted in Phase I
alpar@461:   // 14          CPX_ABORT_DUAL_INFEAS
alpar@461:   //          Aborted in barrier, dual infeasible
alpar@461:   // 15          CPX_ABORT_PRIM_INFEAS
alpar@461:   //          Aborted in barrier, primal infeasible
alpar@461:   // 16          CPX_ABORT_PRIM_DUAL_INFEAS
alpar@461:   //          Aborted in barrier, primal and dual infeasible
alpar@461:   // 17          CPX_ABORT_PRIM_DUAL_FEAS
alpar@461:   //          Aborted in barrier, primal and dual feasible
alpar@461:   // 18          CPX_ABORT_CROSSOVER
alpar@461:   //          Aborted in crossover
alpar@461:   // 19          CPX_INForUNBD
alpar@461:   //          Infeasible or unbounded
alpar@461:   // 20   CPX_PIVOT
alpar@461:   //       User pivot used
alpar@461:   //
alpar@461:   //     Ezeket hova tegyem:
alpar@461:   // ??case CPX_ABORT_DUAL_INFEAS
alpar@461:   // ??case CPX_ABORT_CROSSOVER
alpar@461:   // ??case CPX_INForUNBD
alpar@461:   // ??case CPX_PIVOT
alpar@461: 
alpar@461:   //Some more interesting stuff:
alpar@461: 
alpar@461:   // CPX_PARAM_PROBMETHOD  1062  int  LPMETHOD
alpar@461:   // 0 Automatic
alpar@461:   // 1 Primal Simplex
alpar@461:   // 2 Dual Simplex
alpar@461:   // 3 Network Simplex
alpar@461:   // 4 Standard Barrier
alpar@461:   // Default: 0
alpar@461:   // Description: Method for linear optimization.
alpar@461:   // Determines which algorithm is used when CPXlpopt() (or "optimize"
alpar@461:   // in the Interactive Optimizer) is called. Currently the behavior of
alpar@461:   // the "Automatic" setting is that CPLEX simply invokes the dual
alpar@461:   // simplex method, but this capability may be expanded in the future
alpar@461:   // so that CPLEX chooses the method based on problem characteristics
alpar@461: #if CPX_VERSION < 900
alpar@461:   void statusSwitch(CPXENVptr cplexEnv(),int& stat){
alpar@461:     int lpmethod;
alpar@461:     CPXgetintparam (cplexEnv(),CPX_PARAM_PROBMETHOD,&lpmethod);
alpar@461:     if (lpmethod==2){
alpar@461:       if (stat==CPX_UNBOUNDED){
alpar@461:         stat=CPX_INFEASIBLE;
alpar@461:       }
alpar@461:       else{
alpar@461:         if (stat==CPX_INFEASIBLE)
alpar@461:           stat=CPX_UNBOUNDED;
alpar@461:       }
alpar@461:     }
alpar@461:   }
alpar@461: #else
alpar@461:   void statusSwitch(CPXENVptr,int&){}
alpar@461: #endif
alpar@461: 
alpar@462:   CplexLp::ProblemType CplexLp::_getPrimalType() const {
alpar@461:     // Unboundedness not treated well: the following is from cplex 9.0 doc
alpar@461:     // About Unboundedness
alpar@461: 
alpar@461:     // The treatment of models that are unbounded involves a few
alpar@461:     // subtleties. Specifically, a declaration of unboundedness means that
alpar@461:     // ILOG CPLEX has determined that the model has an unbounded
alpar@461:     // ray. Given any feasible solution x with objective z, a multiple of
alpar@461:     // the unbounded ray can be added to x to give a feasible solution
alpar@461:     // with objective z-1 (or z+1 for maximization models). Thus, if a
alpar@461:     // feasible solution exists, then the optimal objective is
alpar@461:     // unbounded. Note that ILOG CPLEX has not necessarily concluded that
alpar@461:     // a feasible solution exists. Users can call the routine CPXsolninfo
alpar@461:     // to determine whether ILOG CPLEX has also concluded that the model
alpar@461:     // has a feasible solution.
alpar@461: 
alpar@461:     int stat = CPXgetstat(cplexEnv(), _prob);
alpar@461: #if CPX_VERSION >= 800
alpar@461:     switch (stat)
alpar@461:       {
alpar@461:       case CPX_STAT_OPTIMAL:
alpar@461:         return OPTIMAL;
alpar@461:       case CPX_STAT_UNBOUNDED:
alpar@461:         return UNBOUNDED;
alpar@461:       case CPX_STAT_INFEASIBLE:
alpar@461:         return INFEASIBLE;
alpar@461:       default:
alpar@461:         return UNDEFINED;
alpar@461:       }
alpar@461: #else
alpar@461:     statusSwitch(cplexEnv(),stat);
alpar@461:     //CPXgetstat(cplexEnv(), _prob);
alpar@461:     //printf("A primal status: %d, CPX_OPTIMAL=%d \n",stat,CPX_OPTIMAL);
alpar@461:     switch (stat) {
alpar@461:     case 0:
alpar@461:       return UNDEFINED; //Undefined
alpar@461:     case CPX_OPTIMAL://Optimal
alpar@461:       return OPTIMAL;
alpar@461:     case CPX_UNBOUNDED://Unbounded
alpar@461:       return INFEASIBLE;//In case of dual simplex
alpar@461:       //return UNBOUNDED;
alpar@461:     case CPX_INFEASIBLE://Infeasible
alpar@461:       //    case CPX_IT_LIM_INFEAS:
alpar@461:       //     case CPX_TIME_LIM_INFEAS:
alpar@461:       //     case CPX_NUM_BEST_INFEAS:
alpar@461:       //     case CPX_OPTIMAL_INFEAS:
alpar@461:       //     case CPX_ABORT_INFEAS:
alpar@461:       //     case CPX_ABORT_PRIM_INFEAS:
alpar@461:       //     case CPX_ABORT_PRIM_DUAL_INFEAS:
alpar@461:       return UNBOUNDED;//In case of dual simplex
alpar@461:       //return INFEASIBLE;
alpar@461:       //     case CPX_OBJ_LIM:
alpar@461:       //     case CPX_IT_LIM_FEAS:
alpar@461:       //     case CPX_TIME_LIM_FEAS:
alpar@461:       //     case CPX_NUM_BEST_FEAS:
alpar@461:       //     case CPX_ABORT_FEAS:
alpar@461:       //     case CPX_ABORT_PRIM_DUAL_FEAS:
alpar@461:       //       return FEASIBLE;
alpar@461:     default:
alpar@461:       return UNDEFINED; //Everything else comes here
alpar@461:       //FIXME error
alpar@461:     }
alpar@461: #endif
alpar@461:   }
alpar@461: 
alpar@461:   //9.0-as cplex verzio statusai
alpar@461:   // CPX_STAT_ABORT_DUAL_OBJ_LIM
alpar@461:   // CPX_STAT_ABORT_IT_LIM
alpar@461:   // CPX_STAT_ABORT_OBJ_LIM
alpar@461:   // CPX_STAT_ABORT_PRIM_OBJ_LIM
alpar@461:   // CPX_STAT_ABORT_TIME_LIM
alpar@461:   // CPX_STAT_ABORT_USER
alpar@461:   // CPX_STAT_FEASIBLE_RELAXED
alpar@461:   // CPX_STAT_INFEASIBLE
alpar@461:   // CPX_STAT_INForUNBD
alpar@461:   // CPX_STAT_NUM_BEST
alpar@461:   // CPX_STAT_OPTIMAL
alpar@461:   // CPX_STAT_OPTIMAL_FACE_UNBOUNDED
alpar@461:   // CPX_STAT_OPTIMAL_INFEAS
alpar@461:   // CPX_STAT_OPTIMAL_RELAXED
alpar@461:   // CPX_STAT_UNBOUNDED
alpar@461: 
alpar@462:   CplexLp::ProblemType CplexLp::_getDualType() const {
alpar@461:     int stat = CPXgetstat(cplexEnv(), _prob);
alpar@461: #if CPX_VERSION >= 800
alpar@461:     switch (stat) {
alpar@461:     case CPX_STAT_OPTIMAL:
alpar@461:       return OPTIMAL;
alpar@461:     case CPX_STAT_UNBOUNDED:
alpar@461:       return INFEASIBLE;
alpar@461:     default:
alpar@461:       return UNDEFINED;
alpar@461:     }
alpar@461: #else
alpar@461:     statusSwitch(cplexEnv(),stat);
alpar@461:     switch (stat) {
alpar@461:     case 0:
alpar@461:       return UNDEFINED; //Undefined
alpar@461:     case CPX_OPTIMAL://Optimal
alpar@461:       return OPTIMAL;
alpar@461:     case CPX_UNBOUNDED:
alpar@461:       return INFEASIBLE;
alpar@461:     default:
alpar@461:       return UNDEFINED; //Everything else comes here
alpar@461:       //FIXME error
alpar@461:     }
alpar@461: #endif
alpar@461:   }
alpar@461: 
alpar@462:   // CplexMip members
alpar@461: 
alpar@462:   CplexMip::CplexMip()
alpar@461:     : LpBase(), CplexBase(), MipSolver() {
alpar@461: 
alpar@461: #if CPX_VERSION < 800
alpar@461:     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MIP);
alpar@461: #else
alpar@461:     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MILP);
alpar@461: #endif
alpar@461:   }
alpar@461: 
alpar@462:   CplexMip::CplexMip(const CplexEnv& env)
alpar@461:     : LpBase(), CplexBase(env), MipSolver() {
alpar@461: 
alpar@461: #if CPX_VERSION < 800
alpar@461:     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MIP);
alpar@461: #else
alpar@461:     CPXchgprobtype(cplexEnv(),  _prob, CPXPROB_MILP);
alpar@461: #endif
alpar@461: 
alpar@461:   }
alpar@461: 
alpar@462:   CplexMip::CplexMip(const CplexMip& other)
alpar@461:     : LpBase(), CplexBase(other), MipSolver() {}
alpar@461: 
alpar@462:   CplexMip::~CplexMip() {}
alpar@461: 
alpar@462:   CplexMip* CplexMip::_newSolver() const { return new CplexMip; }
alpar@462:   CplexMip* CplexMip::_cloneSolver() const {return new CplexMip(*this); }
alpar@461: 
alpar@462:   const char* CplexMip::_solverName() const { return "CplexMip"; }
alpar@461: 
alpar@462:   void CplexMip::_setColType(int i, CplexMip::ColTypes col_type) {
alpar@461: 
alpar@461:     // Note If a variable is to be changed to binary, a call to CPXchgbds
alpar@461:     // should also be made to change the bounds to 0 and 1.
alpar@461: 
alpar@461:     switch (col_type){
alpar@461:     case INTEGER: {
alpar@461:       const char t = 'I';
alpar@461:       CPXchgctype (cplexEnv(), _prob, 1, &i, &t);
alpar@461:     } break;
alpar@461:     case REAL: {
alpar@461:       const char t = 'C';
alpar@461:       CPXchgctype (cplexEnv(), _prob, 1, &i, &t);
alpar@461:     } break;
alpar@461:     default:
alpar@461:       break;
alpar@461:     }
alpar@461:   }
alpar@461: 
alpar@462:   CplexMip::ColTypes CplexMip::_getColType(int i) const {
alpar@461:     char t;
alpar@461:     CPXgetctype (cplexEnv(), _prob, &t, i, i);
alpar@461:     switch (t) {
alpar@461:     case 'I':
alpar@461:       return INTEGER;
alpar@461:     case 'C':
alpar@461:       return REAL;
alpar@461:     default:
alpar@461:       LEMON_ASSERT(false, "Invalid column type");
alpar@461:       return ColTypes();
alpar@461:     }
alpar@461: 
alpar@461:   }
alpar@461: 
alpar@462:   CplexMip::SolveExitStatus CplexMip::_solve() {
alpar@461:     int status;
alpar@461:     status = CPXmipopt (cplexEnv(), _prob);
alpar@461:     if (status==0)
alpar@461:       return SOLVED;
alpar@461:     else
alpar@461:       return UNSOLVED;
alpar@461: 
alpar@461:   }
alpar@461: 
alpar@461: 
alpar@462:   CplexMip::ProblemType CplexMip::_getType() const {
alpar@461: 
alpar@461:     int stat = CPXgetstat(cplexEnv(), _prob);
alpar@461: 
alpar@461:     //Fortunately, MIP statuses did not change for cplex 8.0
alpar@461:     switch (stat) {
alpar@461:     case CPXMIP_OPTIMAL:
alpar@461:       // Optimal integer solution has been found.
alpar@461:     case CPXMIP_OPTIMAL_TOL:
alpar@461:       // Optimal soluton with the tolerance defined by epgap or epagap has
alpar@461:       // been found.
alpar@461:       return OPTIMAL;
alpar@461:       //This also exists in later issues
alpar@461:       //    case CPXMIP_UNBOUNDED:
alpar@461:       //return UNBOUNDED;
alpar@461:       case CPXMIP_INFEASIBLE:
alpar@461:         return INFEASIBLE;
alpar@461:     default:
alpar@461:       return UNDEFINED;
alpar@461:     }
alpar@461:     //Unboundedness not treated well: the following is from cplex 9.0 doc
alpar@461:     // About Unboundedness
alpar@461: 
alpar@461:     // The treatment of models that are unbounded involves a few
alpar@461:     // subtleties. Specifically, a declaration of unboundedness means that
alpar@461:     // ILOG CPLEX has determined that the model has an unbounded
alpar@461:     // ray. Given any feasible solution x with objective z, a multiple of
alpar@461:     // the unbounded ray can be added to x to give a feasible solution
alpar@461:     // with objective z-1 (or z+1 for maximization models). Thus, if a
alpar@461:     // feasible solution exists, then the optimal objective is
alpar@461:     // unbounded. Note that ILOG CPLEX has not necessarily concluded that
alpar@461:     // a feasible solution exists. Users can call the routine CPXsolninfo
alpar@461:     // to determine whether ILOG CPLEX has also concluded that the model
alpar@461:     // has a feasible solution.
alpar@461:   }
alpar@461: 
alpar@462:   CplexMip::Value CplexMip::_getSol(int i) const {
alpar@461:     Value x;
alpar@461:     CPXgetmipx(cplexEnv(), _prob, &x, i, i);
alpar@461:     return x;
alpar@461:   }
alpar@461: 
alpar@462:   CplexMip::Value CplexMip::_getSolValue() const {
alpar@461:     Value objval;
alpar@461:     CPXgetmipobjval(cplexEnv(), _prob, &objval);
alpar@461:     return objval;
alpar@461:   }
alpar@461: 
alpar@461: } //namespace lemon
alpar@461: