lemon/cplex.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Fri, 17 Apr 2009 18:04:36 +0200
changeset 609 e6927fe719e6
parent 461 08d495d48089
child 540 9db62975c32b
permissions -rw-r--r--
Support >= and <= constraints in NetworkSimplex (#219, #234)

By default the same inequality constraints are supported as by
Circulation (the GEQ form), but the LEQ form can also be selected
using the problemType() function.

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