lemon/lp_utils.h
author deba
Wed, 29 Nov 2006 17:35:31 +0000
changeset 2316 c0fae4bbaa5c
child 2363 2aabce558574
permissions -rw-r--r--
Lp section reader
deba@2316
     1
/* -*- C++ -*-
deba@2316
     2
 *
deba@2316
     3
 * This file is a part of LEMON, a generic C++ optimization library
deba@2316
     4
 *
deba@2316
     5
 * Copyright (C) 2003-2006
deba@2316
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
deba@2316
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
deba@2316
     8
 *
deba@2316
     9
 * Permission to use, modify and distribute this software is granted
deba@2316
    10
 * provided that this copyright notice appears in all copies. For
deba@2316
    11
 * precise terms see the accompanying LICENSE file.
deba@2316
    12
 *
deba@2316
    13
 * This software is provided "AS IS" with no warranty of any kind,
deba@2316
    14
 * express or implied, and with no claim as to its suitability for any
deba@2316
    15
 * purpose.
deba@2316
    16
 *
deba@2316
    17
 */
deba@2316
    18
deba@2316
    19
#ifndef LEMON_LP_UTILS_H
deba@2316
    20
#define LEMON_LP_UTILS_H
deba@2316
    21
deba@2316
    22
#include <lemon/lp_base.h>
deba@2316
    23
deba@2316
    24
#include <lemon/lemon_reader.h>
deba@2316
    25
deba@2316
    26
namespace lemon {
deba@2316
    27
deba@2316
    28
  class LpReader : public LemonReader::SectionReader {
deba@2316
    29
    typedef LemonReader::SectionReader Parent;
deba@2316
    30
  public:
deba@2316
    31
deba@2316
    32
deba@2316
    33
    /// \brief Constructor.
deba@2316
    34
    ///
deba@2316
    35
    /// Constructor for LpReader. It creates the LpReader and attach
deba@2316
    36
    /// it into the given LemonReader. The lp reader will add
deba@2316
    37
    /// variables, constraints and objective function to the
deba@2316
    38
    /// given lp solver.
deba@2316
    39
    LpReader(LemonReader& _reader, LpSolverBase& _lp, 
deba@2316
    40
             const std::string& _name = std::string())
deba@2316
    41
      : Parent(_reader), lp(_lp), name(_name) {} 
deba@2316
    42
deba@2316
    43
deba@2316
    44
    /// \brief Destructor.
deba@2316
    45
    ///
deba@2316
    46
    /// Destructor for NodeSetReader.
deba@2316
    47
    virtual ~LpReader() {}
deba@2316
    48
deba@2316
    49
  private:
deba@2316
    50
    LpReader(const LpReader&);
deba@2316
    51
    void operator=(const LpReader&);
deba@2316
    52
  
deba@2316
    53
  protected:
deba@2316
    54
deba@2316
    55
    /// \brief Gives back true when the SectionReader can process 
deba@2316
    56
    /// the section with the given header line.
deba@2316
    57
    ///
deba@2316
    58
    /// It gives back true when the header line starts with \c \@lp,
deba@2316
    59
    /// and the header line's name and the nodeset's name are the same.
deba@2316
    60
    virtual bool header(const std::string& line) {
deba@2316
    61
      std::istringstream ls(line);
deba@2316
    62
      std::string command;
deba@2316
    63
      std::string id;
deba@2316
    64
      ls >> command >> id;
deba@2316
    65
      return command == "@lp" && name == id;
deba@2316
    66
    }
deba@2316
    67
deba@2316
    68
  private:
deba@2316
    69
deba@2316
    70
    enum Relation {
deba@2316
    71
      LE, EQ, GE
deba@2316
    72
    };
deba@2316
    73
deba@2316
    74
    std::istream& readConstraint(std::istream& is, LpSolverBase::Constr& c) {
deba@2316
    75
      char x;
deba@2316
    76
      LpSolverBase::Expr e1, e2;
deba@2316
    77
      Relation op1;
deba@2316
    78
      is >> std::ws;
deba@2316
    79
      readExpression(is, e1);
deba@2316
    80
      is >> std::ws;
deba@2316
    81
      readRelation(is, op1);
deba@2316
    82
      is >> std::ws;
deba@2316
    83
      readExpression(is, e2);
deba@2316
    84
      if (!is.get(x)) {
deba@2316
    85
        if (op1 == LE) {
deba@2316
    86
          c = e1 <= e2;
deba@2316
    87
        } else if (op1 == GE) {
deba@2316
    88
          c = e1 >= e2;
deba@2316
    89
        } else {
deba@2316
    90
          c = e1 == e2;
deba@2316
    91
        }
deba@2316
    92
      } else {
deba@2316
    93
        is.putback(x);
deba@2316
    94
        LpSolverBase::Expr e3;
deba@2316
    95
        Relation op2;
deba@2316
    96
        readRelation(is, op2);
deba@2316
    97
        is >> std::ws;
deba@2316
    98
        readExpression(is, e3);
deba@2316
    99
        if (!e1.empty() || !e3.empty()) {
deba@2316
   100
          throw DataFormatError("Wrong range format");
deba@2316
   101
        }
deba@2316
   102
        if (op2 != op1 || op1 == EQ) {
deba@2316
   103
          throw DataFormatError("Wrong range format");
deba@2316
   104
        }
deba@2316
   105
        if (op1 == LE) {
deba@2316
   106
          c = e1.constComp() <= e2 <= e3.constComp();
deba@2316
   107
        } else {
deba@2316
   108
          c = e1.constComp() >= e2 >= e3.constComp();
deba@2316
   109
        }
deba@2316
   110
      }
deba@2316
   111
    }
deba@2316
   112
deba@2316
   113
    std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) {
deba@2316
   114
      LpSolverBase::Col c;
deba@2316
   115
      double d;
deba@2316
   116
      char x;
deba@2316
   117
      readElement(is, c, d);
deba@2316
   118
      if (c != INVALID) {
deba@2316
   119
        e += d * c;
deba@2316
   120
      } else {
deba@2316
   121
        e += d;
deba@2316
   122
      }
deba@2316
   123
      is >> std::ws;
deba@2316
   124
      while (is.get(x) && (x == '+' || x == '-')) {
deba@2316
   125
        is >> std::ws;
deba@2316
   126
        readElement(is, c, d);
deba@2316
   127
        if (c != INVALID) {
deba@2316
   128
          e += (x == '+' ? d : -d) * c;
deba@2316
   129
        } else {
deba@2316
   130
          e += (x == '+' ? d : -d);
deba@2316
   131
        }
deba@2316
   132
        is >> std::ws;
deba@2316
   133
      }
deba@2316
   134
      if (!is) {
deba@2316
   135
        is.clear();
deba@2316
   136
      } else {
deba@2316
   137
        is.putback(x);
deba@2316
   138
      }
deba@2316
   139
      return is;
deba@2316
   140
    }
deba@2316
   141
deba@2316
   142
    std::istream& readElement(std::istream& is, 
deba@2316
   143
                              LpSolverBase::Col& c, double& d) { 
deba@2316
   144
      d = 1.0;
deba@2316
   145
      c = INVALID;
deba@2316
   146
      char x, y;
deba@2316
   147
      if (!is.get(x)) throw DataFormatError("Cannot find lp element");
deba@2316
   148
      if (x == '+' || x == '-') {
deba@2316
   149
        is >> std::ws;
deba@2316
   150
        d *= x == '-' ? -1 : 1;
deba@2316
   151
        while (is.get(x) && (x == '+' || x == '-')) {
deba@2316
   152
          d *= x == '-' ? -1 : 1;
deba@2316
   153
          is >> std::ws;
deba@2316
   154
        }
deba@2316
   155
        if (!is) throw DataFormatError("Cannot find lp element");
deba@2316
   156
      }
deba@2316
   157
      if (numFirstChar(x)) {
deba@2316
   158
        is.putback(x);
deba@2316
   159
        double e;
deba@2316
   160
        readNum(is, e);
deba@2316
   161
        d *= e;
deba@2316
   162
      } else if (varFirstChar(x)) {
deba@2316
   163
        is.putback(x);
deba@2316
   164
        LpSolverBase::Col f;
deba@2316
   165
        readCol(is, f);
deba@2316
   166
        c = f;
deba@2316
   167
      } else {
deba@2316
   168
        throw DataFormatError("Invalid expression format");          
deba@2316
   169
      }
deba@2316
   170
      is >> std::ws;
deba@2316
   171
      while (is.get(y) && (y == '*' || y == '/')) {
deba@2316
   172
        is >> std::ws;
deba@2316
   173
        if (!is.get(x)) throw DataFormatError("Cannot find lp element");
deba@2316
   174
        if (x == '+' || x == '-') {
deba@2316
   175
          is >> std::ws;
deba@2316
   176
          d *= x == '-' ? -1 : 1;
deba@2316
   177
          while (is.get(x) && (x == '+' || x == '-')) {
deba@2316
   178
            d *= x == '-' ? -1 : 1;
deba@2316
   179
            is >> std::ws;
deba@2316
   180
          }
deba@2316
   181
          if (!is) throw DataFormatError("Cannot find lp element");
deba@2316
   182
        }
deba@2316
   183
        if (numFirstChar(x)) {
deba@2316
   184
          is.putback(x);
deba@2316
   185
          double e;
deba@2316
   186
          readNum(is, e);
deba@2316
   187
          if (y == '*') {
deba@2316
   188
            d *= e;
deba@2316
   189
          } else {
deba@2316
   190
            d /= e;
deba@2316
   191
          }
deba@2316
   192
        } else if (varFirstChar(x)) {
deba@2316
   193
          is.putback(x);
deba@2316
   194
          LpSolverBase::Col f;
deba@2316
   195
          readCol(is, f);
deba@2316
   196
          if (y == '*') {
deba@2316
   197
            if (c == INVALID) {
deba@2316
   198
              c = f;
deba@2316
   199
            } else {
deba@2316
   200
              throw DataFormatError("Quadratic element in expression");
deba@2316
   201
            }
deba@2316
   202
          } else {
deba@2316
   203
            throw DataFormatError("Division by variable");
deba@2316
   204
          }
deba@2316
   205
        } else {
deba@2316
   206
          throw DataFormatError("Invalid expression format");          
deba@2316
   207
        }
deba@2316
   208
        is >> std::ws;
deba@2316
   209
      }
deba@2316
   210
      if (!is) {
deba@2316
   211
        is.clear();
deba@2316
   212
      } else {
deba@2316
   213
        is.putback(y);
deba@2316
   214
      }
deba@2316
   215
      return is;
deba@2316
   216
    }
deba@2316
   217
deba@2316
   218
    std::istream& readCol(std::istream& is, LpSolverBase::Col& c) {
deba@2316
   219
      char x;
deba@2316
   220
      std::string var;
deba@2316
   221
      while (is.get(x) && varChar(x)) {
deba@2316
   222
        var += x;
deba@2316
   223
      }
deba@2316
   224
      if (!is) {
deba@2316
   225
        is.clear();
deba@2316
   226
      } else {
deba@2316
   227
        is.putback(x);
deba@2316
   228
      }
deba@2316
   229
      ColMap::const_iterator it = cols.find(var);
deba@2316
   230
      if (cols.find(var) != cols.end()) {
deba@2316
   231
        c = it->second;
deba@2316
   232
      } else {
deba@2316
   233
        c = lp.addCol();
deba@2316
   234
        cols.insert(std::make_pair(var, c));
deba@2316
   235
        lp.colName(c, var);
deba@2316
   236
      }
deba@2316
   237
      return is;
deba@2316
   238
    }
deba@2316
   239
deba@2316
   240
    std::istream& readNum(std::istream& is, double& d) {
deba@2316
   241
      is >> d;
deba@2316
   242
      if (!is) throw DataFormatError("Wrong number format");
deba@2316
   243
      return is;
deba@2316
   244
    }
deba@2316
   245
deba@2316
   246
    std::istream& readRelation(std::istream& is, Relation& op) {
deba@2316
   247
      char x, y;
deba@2316
   248
      if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
deba@2316
   249
        throw DataFormatError("Wrong relation operator");
deba@2316
   250
      }
deba@2316
   251
      if (!is.get(y) || y != '=') {
deba@2316
   252
        throw DataFormatError("Wrong relation operator");
deba@2316
   253
      }
deba@2316
   254
      switch (x) {
deba@2316
   255
      case '<': op = LE; 
deba@2316
   256
        break;
deba@2316
   257
      case '=': op = EQ; 
deba@2316
   258
        break;
deba@2316
   259
      case '>': op = GE; 
deba@2316
   260
        break;
deba@2316
   261
      }
deba@2316
   262
      return is;
deba@2316
   263
    }
deba@2316
   264
deba@2316
   265
    static bool relationFirstChar(char c) {
deba@2316
   266
      return c == '<' || c == '=' || c == '>';
deba@2316
   267
    }
deba@2316
   268
deba@2316
   269
    static bool varFirstChar(char c) {
deba@2316
   270
      return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
deba@2316
   271
    }
deba@2316
   272
deba@2316
   273
    static bool numFirstChar(char c) {
deba@2316
   274
      return (c >= '0' && c <= '9') || c == '.';
deba@2316
   275
    }
deba@2316
   276
deba@2316
   277
    static bool varChar(char c) {
deba@2316
   278
      return (c >= '0' && c <= '9') || 
deba@2316
   279
        (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
deba@2316
   280
    }
deba@2316
   281
deba@2316
   282
deba@2316
   283
    void addConstraint(const LpSolverBase::Constr& constr) {
deba@2316
   284
      if (constr.expr().size() != 1) {
deba@2316
   285
        lp.addRow(constr);
deba@2316
   286
      } else {
deba@2316
   287
        Lp::Expr e = constr.expr();
deba@2316
   288
        LpSolverBase::Col col = e.begin()->first;
deba@2316
   289
        double coeff = e.begin()->second;
deba@2316
   290
        double lb = LpSolverBase::NaN;
deba@2316
   291
        double ub = LpSolverBase::NaN;
deba@2316
   292
        if (coeff > 0) {
deba@2316
   293
          if (constr.upperBounded()) {
deba@2316
   294
            lb = (constr.lowerBound() - e.constComp()) / coeff;
deba@2316
   295
          }
deba@2316
   296
          if (constr.lowerBounded()) {
deba@2316
   297
            ub = (constr.upperBound() - e.constComp()) / coeff;
deba@2316
   298
          }
deba@2316
   299
        } else if (coeff < 0) {
deba@2316
   300
          if (constr.upperBounded()) {
deba@2316
   301
            lb = (constr.upperBound() - e.constComp()) / coeff;
deba@2316
   302
          }
deba@2316
   303
          if (constr.lowerBounded()) {
deba@2316
   304
            ub = (constr.lowerBound() - e.constComp()) / coeff;
deba@2316
   305
          }
deba@2316
   306
        } else {
deba@2316
   307
          lb = -LpSolverBase::INF;
deba@2316
   308
          ub = LpSolverBase::INF;
deba@2316
   309
        }
deba@2316
   310
        lp.colLowerBound(col, lb);
deba@2316
   311
        lp.colUpperBound(col, ub);
deba@2316
   312
      }
deba@2316
   313
    }
deba@2316
   314
    
deba@2316
   315
  protected:
deba@2316
   316
deba@2316
   317
    /// \brief Reader function of the section.
deba@2316
   318
    ///
deba@2316
   319
    /// It reads the content of the section.
deba@2316
   320
    virtual void read(std::istream& is) {
deba@2316
   321
      std::string line;
deba@2316
   322
      std::map<std::string, LpSolverBase::Col> vars;
deba@2316
   323
      while (getline(is, line)) {
deba@2316
   324
        std::istringstream ls(line);
deba@2316
   325
        std::string sense;
deba@2316
   326
        ls >> sense;
deba@2316
   327
        if (sense == "min" || sense == "max") {
deba@2316
   328
          LpSolverBase::Expr expr;
deba@2316
   329
          ls >> std::ws;
deba@2316
   330
          readExpression(ls, expr);
deba@2316
   331
          lp.setObj(expr);
deba@2316
   332
          if (sense == "min") {
deba@2316
   333
            lp.min();
deba@2316
   334
          } else {
deba@2316
   335
            lp.max();
deba@2316
   336
          }
deba@2316
   337
        } else {
deba@2316
   338
          ls.str(line);
deba@2316
   339
          LpSolverBase::Constr constr;
deba@2316
   340
          ls >> std::ws;
deba@2316
   341
          readConstraint(ls, constr);
deba@2316
   342
          addConstraint(constr);
deba@2316
   343
        }        
deba@2316
   344
      }
deba@2316
   345
    }
deba@2316
   346
      
deba@2316
   347
    virtual void missing() {
deba@2316
   348
      ErrorMessage msg;
deba@2316
   349
      msg << "Lp section not found in file: @lp " << name;
deba@2316
   350
      throw IoParameterError(msg.message());
deba@2316
   351
    }
deba@2316
   352
deba@2316
   353
  private:
deba@2316
   354
deba@2316
   355
    typedef std::map<std::string, LpSolverBase::Col> ColMap;
deba@2316
   356
      
deba@2316
   357
    LpSolverBase& lp;
deba@2316
   358
    std::string name;
deba@2316
   359
    ColMap cols;
deba@2316
   360
  };
deba@2316
   361
deba@2316
   362
}
deba@2316
   363
deba@2316
   364
#endif