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