lemon/cbc.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Thu, 12 Nov 2009 23:26:13 +0100
changeset 806 fa6f37d7a25b
parent 576 745e182d0139
child 974 b1744d7bdb47
child 998 7fdaa05a69a1
permissions -rw-r--r--
Entirely rework CapacityScaling (#180)

- Use the new interface similarly to NetworkSimplex.
- Rework the implementation using an efficient internal structure
for handling the residual network. This improvement made the
code much faster (up to 2-5 times faster on large graphs).
- Handle GEQ supply type (LEQ is not supported).
- Handle negative costs for arcs of finite capacity.
(Note that this algorithm cannot handle arcs of negative cost
and infinite upper bound, thus it returns UNBOUNDED if such
an arc exists.)
- Extend the documentation.
deba@567
     1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
deba@567
     2
 *
deba@567
     3
 * This file is a part of LEMON, a generic C++ optimization library.
deba@567
     4
 *
deba@567
     5
 * Copyright (C) 2003-2009
deba@567
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
deba@567
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
deba@567
     8
 *
deba@567
     9
 * Permission to use, modify and distribute this software is granted
deba@567
    10
 * provided that this copyright notice appears in all copies. For
deba@567
    11
 * precise terms see the accompanying LICENSE file.
deba@567
    12
 *
deba@567
    13
 * This software is provided "AS IS" with no warranty of any kind,
deba@567
    14
 * express or implied, and with no claim as to its suitability for any
deba@567
    15
 * purpose.
deba@567
    16
 *
deba@567
    17
 */
deba@567
    18
deba@567
    19
///\file
deba@567
    20
///\brief Implementation of the CBC MIP solver interface.
deba@567
    21
deba@567
    22
#include "cbc.h"
deba@567
    23
deba@567
    24
#include <coin/CoinModel.hpp>
deba@567
    25
#include <coin/CbcModel.hpp>
deba@567
    26
#include <coin/OsiSolverInterface.hpp>
deba@567
    27
deba@567
    28
#ifdef COIN_HAS_CLP
deba@567
    29
#include "coin/OsiClpSolverInterface.hpp"
deba@567
    30
#endif
deba@567
    31
#ifdef COIN_HAS_OSL
deba@567
    32
#include "coin/OsiOslSolverInterface.hpp"
deba@567
    33
#endif
deba@567
    34
deba@567
    35
#include "coin/CbcCutGenerator.hpp"
deba@567
    36
#include "coin/CbcHeuristicLocal.hpp"
deba@567
    37
#include "coin/CbcHeuristicGreedy.hpp"
deba@567
    38
#include "coin/CbcHeuristicFPump.hpp"
deba@567
    39
#include "coin/CbcHeuristicRINS.hpp"
deba@567
    40
deba@567
    41
#include "coin/CglGomory.hpp"
deba@567
    42
#include "coin/CglProbing.hpp"
deba@567
    43
#include "coin/CglKnapsackCover.hpp"
deba@567
    44
#include "coin/CglOddHole.hpp"
deba@567
    45
#include "coin/CglClique.hpp"
deba@567
    46
#include "coin/CglFlowCover.hpp"
deba@567
    47
#include "coin/CglMixedIntegerRounding.hpp"
deba@567
    48
deba@567
    49
#include "coin/CbcHeuristic.hpp"
deba@567
    50
deba@567
    51
namespace lemon {
deba@567
    52
deba@567
    53
  CbcMip::CbcMip() {
deba@567
    54
    _prob = new CoinModel();
deba@567
    55
    _prob->setProblemName("LEMON");
deba@567
    56
    _osi_solver = 0;
deba@567
    57
    _cbc_model = 0;
deba@576
    58
    messageLevel(MESSAGE_NOTHING);
deba@567
    59
  }
deba@567
    60
deba@567
    61
  CbcMip::CbcMip(const CbcMip& other) {
deba@567
    62
    _prob = new CoinModel(*other._prob);
deba@576
    63
    _prob->setProblemName("LEMON");
deba@567
    64
    _osi_solver = 0;
deba@567
    65
    _cbc_model = 0;
deba@576
    66
    messageLevel(MESSAGE_NOTHING);
deba@567
    67
  }
deba@567
    68
deba@567
    69
  CbcMip::~CbcMip() {
deba@567
    70
    delete _prob;
deba@567
    71
    if (_osi_solver) delete _osi_solver;
deba@567
    72
    if (_cbc_model) delete _cbc_model;
deba@567
    73
  }
deba@567
    74
deba@567
    75
  const char* CbcMip::_solverName() const { return "CbcMip"; }
deba@567
    76
deba@567
    77
  int CbcMip::_addCol() {
deba@567
    78
    _prob->addColumn(0, 0, 0, -COIN_DBL_MAX, COIN_DBL_MAX, 0.0, 0, false);
deba@567
    79
    return _prob->numberColumns() - 1;
deba@567
    80
  }
deba@567
    81
deba@567
    82
  CbcMip* CbcMip::newSolver() const {
deba@567
    83
    CbcMip* newlp = new CbcMip;
deba@567
    84
    return newlp;
deba@567
    85
  }
deba@567
    86
deba@567
    87
  CbcMip* CbcMip::cloneSolver() const {
deba@567
    88
    CbcMip* copylp = new CbcMip(*this);
deba@567
    89
    return copylp;
deba@567
    90
  }
deba@567
    91
deba@567
    92
  int CbcMip::_addRow() {
deba@567
    93
    _prob->addRow(0, 0, 0, -COIN_DBL_MAX, COIN_DBL_MAX);
deba@567
    94
    return _prob->numberRows() - 1;
deba@567
    95
  }
deba@567
    96
deba@746
    97
  int CbcMip::_addRow(Value l, ExprIterator b, ExprIterator e, Value u) {
deba@746
    98
    std::vector<int> indexes;
deba@746
    99
    std::vector<Value> values;
deba@746
   100
deba@746
   101
    for(ExprIterator it = b; it != e; ++it) {
deba@746
   102
      indexes.push_back(it->first);
deba@746
   103
      values.push_back(it->second);
deba@746
   104
    }
deba@746
   105
deba@746
   106
    _prob->addRow(values.size(), &indexes.front(), &values.front(), l, u);
deba@746
   107
    return _prob->numberRows() - 1;
deba@746
   108
  }
deba@567
   109
deba@567
   110
  void CbcMip::_eraseCol(int i) {
deba@567
   111
    _prob->deleteColumn(i);
deba@567
   112
  }
deba@567
   113
deba@567
   114
  void CbcMip::_eraseRow(int i) {
deba@567
   115
    _prob->deleteRow(i);
deba@567
   116
  }
deba@567
   117
deba@567
   118
  void CbcMip::_eraseColId(int i) {
deba@567
   119
    cols.eraseIndex(i);
deba@567
   120
  }
deba@567
   121
deba@567
   122
  void CbcMip::_eraseRowId(int i) {
deba@567
   123
    rows.eraseIndex(i);
deba@567
   124
  }
deba@567
   125
deba@567
   126
  void CbcMip::_getColName(int c, std::string& name) const {
deba@567
   127
    name = _prob->getColumnName(c);
deba@567
   128
  }
deba@567
   129
deba@567
   130
  void CbcMip::_setColName(int c, const std::string& name) {
deba@567
   131
    _prob->setColumnName(c, name.c_str());
deba@567
   132
  }
deba@567
   133
deba@567
   134
  int CbcMip::_colByName(const std::string& name) const {
deba@567
   135
    return _prob->column(name.c_str());
deba@567
   136
  }
deba@567
   137
deba@567
   138
  void CbcMip::_getRowName(int r, std::string& name) const {
deba@567
   139
    name = _prob->getRowName(r);
deba@567
   140
  }
deba@567
   141
deba@567
   142
  void CbcMip::_setRowName(int r, const std::string& name) {
deba@567
   143
    _prob->setRowName(r, name.c_str());
deba@567
   144
  }
deba@567
   145
deba@567
   146
  int CbcMip::_rowByName(const std::string& name) const {
deba@567
   147
    return _prob->row(name.c_str());
deba@567
   148
  }
deba@567
   149
deba@567
   150
  void CbcMip::_setRowCoeffs(int i, ExprIterator b, ExprIterator e) {
deba@567
   151
    for (ExprIterator it = b; it != e; ++it) {
deba@567
   152
      _prob->setElement(i, it->first, it->second);
deba@567
   153
    }
deba@567
   154
  }
deba@567
   155
deba@567
   156
  void CbcMip::_getRowCoeffs(int ix, InsertIterator b) const {
deba@567
   157
    int length = _prob->numberRows();
deba@567
   158
deba@567
   159
    std::vector<int> indices(length);
deba@567
   160
    std::vector<Value> values(length);
deba@567
   161
deba@567
   162
    length = _prob->getRow(ix, &indices[0], &values[0]);
deba@567
   163
deba@567
   164
    for (int i = 0; i < length; ++i) {
deba@567
   165
      *b = std::make_pair(indices[i], values[i]);
deba@567
   166
      ++b;
deba@567
   167
    }
deba@567
   168
  }
deba@567
   169
deba@567
   170
  void CbcMip::_setColCoeffs(int ix, ExprIterator b, ExprIterator e) {
deba@567
   171
    for (ExprIterator it = b; it != e; ++it) {
deba@567
   172
      _prob->setElement(it->first, ix, it->second);
deba@567
   173
    }
deba@567
   174
  }
deba@567
   175
deba@567
   176
  void CbcMip::_getColCoeffs(int ix, InsertIterator b) const {
deba@567
   177
    int length = _prob->numberColumns();
deba@567
   178
deba@567
   179
    std::vector<int> indices(length);
deba@567
   180
    std::vector<Value> values(length);
deba@567
   181
deba@567
   182
    length = _prob->getColumn(ix, &indices[0], &values[0]);
deba@567
   183
deba@567
   184
    for (int i = 0; i < length; ++i) {
deba@567
   185
      *b = std::make_pair(indices[i], values[i]);
deba@567
   186
      ++b;
deba@567
   187
    }
deba@567
   188
  }
deba@567
   189
deba@567
   190
  void CbcMip::_setCoeff(int ix, int jx, Value value) {
deba@567
   191
    _prob->setElement(ix, jx, value);
deba@567
   192
  }
deba@567
   193
deba@567
   194
  CbcMip::Value CbcMip::_getCoeff(int ix, int jx) const {
deba@567
   195
    return _prob->getElement(ix, jx);
deba@567
   196
  }
deba@567
   197
deba@567
   198
deba@567
   199
  void CbcMip::_setColLowerBound(int i, Value lo) {
deba@567
   200
    LEMON_ASSERT(lo != INF, "Invalid bound");
deba@567
   201
    _prob->setColumnLower(i, lo == - INF ? - COIN_DBL_MAX : lo);
deba@567
   202
  }
deba@567
   203
deba@567
   204
  CbcMip::Value CbcMip::_getColLowerBound(int i) const {
deba@567
   205
    double val = _prob->getColumnLower(i);
deba@567
   206
    return val == - COIN_DBL_MAX ? - INF : val;
deba@567
   207
  }
deba@567
   208
deba@567
   209
  void CbcMip::_setColUpperBound(int i, Value up) {
deba@567
   210
    LEMON_ASSERT(up != -INF, "Invalid bound");
deba@567
   211
    _prob->setColumnUpper(i, up == INF ? COIN_DBL_MAX : up);
deba@567
   212
  }
deba@567
   213
deba@567
   214
  CbcMip::Value CbcMip::_getColUpperBound(int i) const {
deba@567
   215
    double val = _prob->getColumnUpper(i);
deba@567
   216
    return val == COIN_DBL_MAX ? INF : val;
deba@567
   217
  }
deba@567
   218
deba@567
   219
  void CbcMip::_setRowLowerBound(int i, Value lo) {
deba@567
   220
    LEMON_ASSERT(lo != INF, "Invalid bound");
deba@567
   221
    _prob->setRowLower(i, lo == - INF ? - COIN_DBL_MAX : lo);
deba@567
   222
  }
deba@567
   223
deba@567
   224
  CbcMip::Value CbcMip::_getRowLowerBound(int i) const {
deba@567
   225
    double val = _prob->getRowLower(i);
deba@567
   226
    return val == - COIN_DBL_MAX ? - INF : val;
deba@567
   227
  }
deba@567
   228
deba@567
   229
  void CbcMip::_setRowUpperBound(int i, Value up) {
deba@567
   230
    LEMON_ASSERT(up != -INF, "Invalid bound");
deba@567
   231
    _prob->setRowUpper(i, up == INF ? COIN_DBL_MAX : up);
deba@567
   232
  }
deba@567
   233
deba@567
   234
  CbcMip::Value CbcMip::_getRowUpperBound(int i) const {
deba@567
   235
    double val = _prob->getRowUpper(i);
deba@567
   236
    return val == COIN_DBL_MAX ? INF : val;
deba@567
   237
  }
deba@567
   238
deba@567
   239
  void CbcMip::_setObjCoeffs(ExprIterator b, ExprIterator e) {
deba@567
   240
    int num = _prob->numberColumns();
deba@567
   241
    for (int i = 0; i < num; ++i) {
deba@567
   242
      _prob->setColumnObjective(i, 0.0);
deba@567
   243
    }
deba@567
   244
    for (ExprIterator it = b; it != e; ++it) {
deba@567
   245
      _prob->setColumnObjective(it->first, it->second);
deba@567
   246
    }
deba@567
   247
  }
deba@567
   248
deba@567
   249
  void CbcMip::_getObjCoeffs(InsertIterator b) const {
deba@567
   250
    int num = _prob->numberColumns();
deba@567
   251
    for (int i = 0; i < num; ++i) {
deba@567
   252
      Value coef = _prob->getColumnObjective(i);
deba@567
   253
      if (coef != 0.0) {
deba@567
   254
        *b = std::make_pair(i, coef);
deba@567
   255
        ++b;
deba@567
   256
      }
deba@567
   257
    }
deba@567
   258
  }
deba@567
   259
deba@567
   260
  void CbcMip::_setObjCoeff(int i, Value obj_coef) {
deba@567
   261
    _prob->setColumnObjective(i, obj_coef);
deba@567
   262
  }
deba@567
   263
deba@567
   264
  CbcMip::Value CbcMip::_getObjCoeff(int i) const {
deba@567
   265
    return _prob->getColumnObjective(i);
deba@567
   266
  }
deba@567
   267
deba@567
   268
  CbcMip::SolveExitStatus CbcMip::_solve() {
deba@567
   269
deba@567
   270
    if (_osi_solver) {
deba@567
   271
      delete _osi_solver;
deba@567
   272
    }
deba@567
   273
#ifdef COIN_HAS_CLP
deba@567
   274
    _osi_solver = new OsiClpSolverInterface();
deba@567
   275
#elif COIN_HAS_OSL
deba@567
   276
    _osi_solver = new OsiOslSolverInterface();
deba@567
   277
#else
deba@567
   278
#error Cannot instantiate Osi solver
deba@567
   279
#endif
deba@567
   280
deba@567
   281
    _osi_solver->loadFromCoinModel(*_prob);
deba@567
   282
deba@567
   283
    if (_cbc_model) {
deba@567
   284
      delete _cbc_model;
deba@567
   285
    }
deba@567
   286
    _cbc_model= new CbcModel(*_osi_solver);
deba@567
   287
deba@576
   288
    _osi_solver->messageHandler()->setLogLevel(_message_level);
deba@576
   289
    _cbc_model->setLogLevel(_message_level);
deba@567
   290
deba@567
   291
    _cbc_model->initialSolve();
deba@567
   292
    _cbc_model->solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry);
deba@567
   293
deba@567
   294
    if (!_cbc_model->isInitialSolveAbandoned() &&
deba@567
   295
        _cbc_model->isInitialSolveProvenOptimal() &&
deba@567
   296
        !_cbc_model->isInitialSolveProvenPrimalInfeasible() &&
deba@567
   297
        !_cbc_model->isInitialSolveProvenDualInfeasible()) {
deba@567
   298
deba@567
   299
      CglProbing generator1;
deba@567
   300
      generator1.setUsingObjective(true);
deba@567
   301
      generator1.setMaxPass(3);
deba@567
   302
      generator1.setMaxProbe(100);
deba@567
   303
      generator1.setMaxLook(50);
deba@567
   304
      generator1.setRowCuts(3);
deba@567
   305
      _cbc_model->addCutGenerator(&generator1, -1, "Probing");
deba@567
   306
deba@567
   307
      CglGomory generator2;
deba@567
   308
      generator2.setLimit(300);
deba@567
   309
      _cbc_model->addCutGenerator(&generator2, -1, "Gomory");
deba@567
   310
deba@567
   311
      CglKnapsackCover generator3;
deba@567
   312
      _cbc_model->addCutGenerator(&generator3, -1, "Knapsack");
deba@567
   313
deba@567
   314
      CglOddHole generator4;
deba@567
   315
      generator4.setMinimumViolation(0.005);
deba@567
   316
      generator4.setMinimumViolationPer(0.00002);
deba@567
   317
      generator4.setMaximumEntries(200);
deba@567
   318
      _cbc_model->addCutGenerator(&generator4, -1, "OddHole");
deba@567
   319
deba@567
   320
      CglClique generator5;
deba@567
   321
      generator5.setStarCliqueReport(false);
deba@567
   322
      generator5.setRowCliqueReport(false);
deba@567
   323
      _cbc_model->addCutGenerator(&generator5, -1, "Clique");
deba@567
   324
deba@567
   325
      CglMixedIntegerRounding mixedGen;
deba@567
   326
      _cbc_model->addCutGenerator(&mixedGen, -1, "MixedIntegerRounding");
deba@567
   327
deba@567
   328
      CglFlowCover flowGen;
deba@567
   329
      _cbc_model->addCutGenerator(&flowGen, -1, "FlowCover");
deba@567
   330
deba@567
   331
#ifdef COIN_HAS_CLP
deba@567
   332
      OsiClpSolverInterface* osiclp =
deba@567
   333
        dynamic_cast<OsiClpSolverInterface*>(_cbc_model->solver());
deba@567
   334
      if (osiclp->getNumRows() < 300 && osiclp->getNumCols() < 500) {
deba@567
   335
        osiclp->setupForRepeatedUse(2, 0);
deba@567
   336
      }
deba@567
   337
#endif
deba@567
   338
deba@567
   339
      CbcRounding heuristic1(*_cbc_model);
deba@567
   340
      heuristic1.setWhen(3);
deba@567
   341
      _cbc_model->addHeuristic(&heuristic1);
deba@567
   342
deba@567
   343
      CbcHeuristicLocal heuristic2(*_cbc_model);
deba@567
   344
      heuristic2.setWhen(3);
deba@567
   345
      _cbc_model->addHeuristic(&heuristic2);
deba@567
   346
deba@567
   347
      CbcHeuristicGreedyCover heuristic3(*_cbc_model);
deba@567
   348
      heuristic3.setAlgorithm(11);
deba@567
   349
      heuristic3.setWhen(3);
deba@567
   350
      _cbc_model->addHeuristic(&heuristic3);
deba@567
   351
deba@567
   352
      CbcHeuristicFPump heuristic4(*_cbc_model);
deba@567
   353
      heuristic4.setWhen(3);
deba@567
   354
      _cbc_model->addHeuristic(&heuristic4);
deba@567
   355
deba@567
   356
      CbcHeuristicRINS heuristic5(*_cbc_model);
deba@567
   357
      heuristic5.setWhen(3);
deba@567
   358
      _cbc_model->addHeuristic(&heuristic5);
deba@567
   359
deba@567
   360
      if (_cbc_model->getNumCols() < 500) {
deba@567
   361
        _cbc_model->setMaximumCutPassesAtRoot(-100);
deba@567
   362
      } else if (_cbc_model->getNumCols() < 5000) {
deba@567
   363
        _cbc_model->setMaximumCutPassesAtRoot(100);
deba@567
   364
      } else {
deba@567
   365
        _cbc_model->setMaximumCutPassesAtRoot(20);
deba@567
   366
      }
deba@567
   367
deba@567
   368
      if (_cbc_model->getNumCols() < 5000) {
deba@567
   369
        _cbc_model->setNumberStrong(10);
deba@567
   370
      }
deba@567
   371
deba@567
   372
      _cbc_model->solver()->setIntParam(OsiMaxNumIterationHotStart, 100);
deba@567
   373
      _cbc_model->branchAndBound();
deba@567
   374
    }
deba@567
   375
deba@567
   376
    if (_cbc_model->isAbandoned()) {
deba@567
   377
      return UNSOLVED;
deba@567
   378
    } else {
deba@567
   379
      return SOLVED;
deba@567
   380
    }
deba@567
   381
  }
deba@567
   382
deba@567
   383
  CbcMip::Value CbcMip::_getSol(int i) const {
deba@567
   384
    return _cbc_model->getColSolution()[i];
deba@567
   385
  }
deba@567
   386
deba@567
   387
  CbcMip::Value CbcMip::_getSolValue() const {
deba@567
   388
    return _cbc_model->getObjValue();
deba@567
   389
  }
deba@567
   390
deba@567
   391
  CbcMip::ProblemType CbcMip::_getType() const {
deba@567
   392
    if (_cbc_model->isProvenOptimal()) {
deba@567
   393
      return OPTIMAL;
deba@567
   394
    } else if (_cbc_model->isContinuousUnbounded()) {
deba@567
   395
      return UNBOUNDED;
deba@567
   396
    }
deba@567
   397
    return FEASIBLE;
deba@567
   398
  }
deba@567
   399
deba@567
   400
  void CbcMip::_setSense(Sense sense) {
deba@567
   401
    switch (sense) {
deba@567
   402
    case MIN:
deba@567
   403
      _prob->setOptimizationDirection(1.0);
deba@567
   404
      break;
deba@567
   405
    case MAX:
deba@567
   406
      _prob->setOptimizationDirection(- 1.0);
deba@567
   407
      break;
deba@567
   408
    }
deba@567
   409
  }
deba@567
   410
deba@567
   411
  CbcMip::Sense CbcMip::_getSense() const {
deba@567
   412
    if (_prob->optimizationDirection() > 0.0) {
deba@567
   413
      return MIN;
deba@567
   414
    } else if (_prob->optimizationDirection() < 0.0) {
deba@567
   415
      return MAX;
deba@567
   416
    } else {
deba@567
   417
      LEMON_ASSERT(false, "Wrong sense");
deba@567
   418
      return CbcMip::Sense();
deba@567
   419
    }
deba@567
   420
  }
deba@567
   421
deba@567
   422
  void CbcMip::_setColType(int i, CbcMip::ColTypes col_type) {
deba@567
   423
    switch (col_type){
deba@567
   424
    case INTEGER:
deba@567
   425
      _prob->setInteger(i);
deba@567
   426
      break;
deba@567
   427
    case REAL:
deba@567
   428
      _prob->setContinuous(i);
deba@567
   429
      break;
deba@567
   430
    default:;
deba@567
   431
      LEMON_ASSERT(false, "Wrong sense");
deba@567
   432
    }
deba@567
   433
  }
deba@567
   434
deba@567
   435
  CbcMip::ColTypes CbcMip::_getColType(int i) const {
deba@567
   436
    return _prob->getColumnIsInteger(i) ? INTEGER : REAL;
deba@567
   437
  }
deba@567
   438
deba@567
   439
  void CbcMip::_clear() {
deba@567
   440
    delete _prob;
deba@567
   441
    if (_osi_solver) {
deba@567
   442
      delete _osi_solver;
deba@567
   443
      _osi_solver = 0;
deba@567
   444
    }
deba@567
   445
    if (_cbc_model) {
deba@567
   446
      delete _cbc_model;
deba@567
   447
      _cbc_model = 0;
deba@567
   448
    }
deba@567
   449
deba@567
   450
    _prob = new CoinModel();
deba@567
   451
    rows.clear();
deba@567
   452
    cols.clear();
deba@567
   453
  }
deba@567
   454
deba@576
   455
  void CbcMip::_messageLevel(MessageLevel level) {
deba@576
   456
    switch (level) {
deba@576
   457
    case MESSAGE_NOTHING:
deba@576
   458
      _message_level = 0;
deba@576
   459
      break;
deba@576
   460
    case MESSAGE_ERROR:
deba@576
   461
      _message_level = 1;
deba@576
   462
      break;
deba@576
   463
    case MESSAGE_WARNING:
deba@576
   464
      _message_level = 1;
deba@576
   465
      break;
deba@576
   466
    case MESSAGE_NORMAL:
deba@576
   467
      _message_level = 2;
deba@576
   468
      break;
deba@576
   469
    case MESSAGE_VERBOSE:
deba@576
   470
      _message_level = 3;
deba@576
   471
      break;
deba@576
   472
    }
deba@567
   473
  }
deba@567
   474
deba@567
   475
} //END OF NAMESPACE LEMON