Lp section reader
authordeba
Wed, 29 Nov 2006 17:35:31 +0000
changeset 2316c0fae4bbaa5c
parent 2315 bd09e00b64bb
child 2317 6d3ed14efb68
Lp section reader
lemon/Makefile.am
lemon/lp_utils.h
     1.1 --- a/lemon/Makefile.am	Wed Nov 29 17:34:29 2006 +0000
     1.2 +++ b/lemon/Makefile.am	Wed Nov 29 17:35:31 2006 +0000
     1.3 @@ -76,6 +76,7 @@
     1.4  	lemon/lp_glpk.h \
     1.5  	lemon/lp_skeleton.h \
     1.6  	lemon/lp_soplex.h \
     1.7 +	lemon/lp_utils.h \
     1.8  	lemon/map_iterator.h \
     1.9  	lemon/maps.h \
    1.10  	lemon/matrix_maps.h \
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/lemon/lp_utils.h	Wed Nov 29 17:35:31 2006 +0000
     2.3 @@ -0,0 +1,364 @@
     2.4 +/* -*- C++ -*-
     2.5 + *
     2.6 + * This file is a part of LEMON, a generic C++ optimization library
     2.7 + *
     2.8 + * Copyright (C) 2003-2006
     2.9 + * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
    2.10 + * (Egervary Research Group on Combinatorial Optimization, EGRES).
    2.11 + *
    2.12 + * Permission to use, modify and distribute this software is granted
    2.13 + * provided that this copyright notice appears in all copies. For
    2.14 + * precise terms see the accompanying LICENSE file.
    2.15 + *
    2.16 + * This software is provided "AS IS" with no warranty of any kind,
    2.17 + * express or implied, and with no claim as to its suitability for any
    2.18 + * purpose.
    2.19 + *
    2.20 + */
    2.21 +
    2.22 +#ifndef LEMON_LP_UTILS_H
    2.23 +#define LEMON_LP_UTILS_H
    2.24 +
    2.25 +#include <lemon/lp_base.h>
    2.26 +
    2.27 +#include <lemon/lemon_reader.h>
    2.28 +
    2.29 +namespace lemon {
    2.30 +
    2.31 +  class LpReader : public LemonReader::SectionReader {
    2.32 +    typedef LemonReader::SectionReader Parent;
    2.33 +  public:
    2.34 +
    2.35 +
    2.36 +    /// \brief Constructor.
    2.37 +    ///
    2.38 +    /// Constructor for LpReader. It creates the LpReader and attach
    2.39 +    /// it into the given LemonReader. The lp reader will add
    2.40 +    /// variables, constraints and objective function to the
    2.41 +    /// given lp solver.
    2.42 +    LpReader(LemonReader& _reader, LpSolverBase& _lp, 
    2.43 +             const std::string& _name = std::string())
    2.44 +      : Parent(_reader), lp(_lp), name(_name) {} 
    2.45 +
    2.46 +
    2.47 +    /// \brief Destructor.
    2.48 +    ///
    2.49 +    /// Destructor for NodeSetReader.
    2.50 +    virtual ~LpReader() {}
    2.51 +
    2.52 +  private:
    2.53 +    LpReader(const LpReader&);
    2.54 +    void operator=(const LpReader&);
    2.55 +  
    2.56 +  protected:
    2.57 +
    2.58 +    /// \brief Gives back true when the SectionReader can process 
    2.59 +    /// the section with the given header line.
    2.60 +    ///
    2.61 +    /// It gives back true when the header line starts with \c \@lp,
    2.62 +    /// and the header line's name and the nodeset's name are the same.
    2.63 +    virtual bool header(const std::string& line) {
    2.64 +      std::istringstream ls(line);
    2.65 +      std::string command;
    2.66 +      std::string id;
    2.67 +      ls >> command >> id;
    2.68 +      return command == "@lp" && name == id;
    2.69 +    }
    2.70 +
    2.71 +  private:
    2.72 +
    2.73 +    enum Relation {
    2.74 +      LE, EQ, GE
    2.75 +    };
    2.76 +
    2.77 +    std::istream& readConstraint(std::istream& is, LpSolverBase::Constr& c) {
    2.78 +      char x;
    2.79 +      LpSolverBase::Expr e1, e2;
    2.80 +      Relation op1;
    2.81 +      is >> std::ws;
    2.82 +      readExpression(is, e1);
    2.83 +      is >> std::ws;
    2.84 +      readRelation(is, op1);
    2.85 +      is >> std::ws;
    2.86 +      readExpression(is, e2);
    2.87 +      if (!is.get(x)) {
    2.88 +        if (op1 == LE) {
    2.89 +          c = e1 <= e2;
    2.90 +        } else if (op1 == GE) {
    2.91 +          c = e1 >= e2;
    2.92 +        } else {
    2.93 +          c = e1 == e2;
    2.94 +        }
    2.95 +      } else {
    2.96 +        is.putback(x);
    2.97 +        LpSolverBase::Expr e3;
    2.98 +        Relation op2;
    2.99 +        readRelation(is, op2);
   2.100 +        is >> std::ws;
   2.101 +        readExpression(is, e3);
   2.102 +        if (!e1.empty() || !e3.empty()) {
   2.103 +          throw DataFormatError("Wrong range format");
   2.104 +        }
   2.105 +        if (op2 != op1 || op1 == EQ) {
   2.106 +          throw DataFormatError("Wrong range format");
   2.107 +        }
   2.108 +        if (op1 == LE) {
   2.109 +          c = e1.constComp() <= e2 <= e3.constComp();
   2.110 +        } else {
   2.111 +          c = e1.constComp() >= e2 >= e3.constComp();
   2.112 +        }
   2.113 +      }
   2.114 +    }
   2.115 +
   2.116 +    std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) {
   2.117 +      LpSolverBase::Col c;
   2.118 +      double d;
   2.119 +      char x;
   2.120 +      readElement(is, c, d);
   2.121 +      if (c != INVALID) {
   2.122 +        e += d * c;
   2.123 +      } else {
   2.124 +        e += d;
   2.125 +      }
   2.126 +      is >> std::ws;
   2.127 +      while (is.get(x) && (x == '+' || x == '-')) {
   2.128 +        is >> std::ws;
   2.129 +        readElement(is, c, d);
   2.130 +        if (c != INVALID) {
   2.131 +          e += (x == '+' ? d : -d) * c;
   2.132 +        } else {
   2.133 +          e += (x == '+' ? d : -d);
   2.134 +        }
   2.135 +        is >> std::ws;
   2.136 +      }
   2.137 +      if (!is) {
   2.138 +        is.clear();
   2.139 +      } else {
   2.140 +        is.putback(x);
   2.141 +      }
   2.142 +      return is;
   2.143 +    }
   2.144 +
   2.145 +    std::istream& readElement(std::istream& is, 
   2.146 +                              LpSolverBase::Col& c, double& d) { 
   2.147 +      d = 1.0;
   2.148 +      c = INVALID;
   2.149 +      char x, y;
   2.150 +      if (!is.get(x)) throw DataFormatError("Cannot find lp element");
   2.151 +      if (x == '+' || x == '-') {
   2.152 +        is >> std::ws;
   2.153 +        d *= x == '-' ? -1 : 1;
   2.154 +        while (is.get(x) && (x == '+' || x == '-')) {
   2.155 +          d *= x == '-' ? -1 : 1;
   2.156 +          is >> std::ws;
   2.157 +        }
   2.158 +        if (!is) throw DataFormatError("Cannot find lp element");
   2.159 +      }
   2.160 +      if (numFirstChar(x)) {
   2.161 +        is.putback(x);
   2.162 +        double e;
   2.163 +        readNum(is, e);
   2.164 +        d *= e;
   2.165 +      } else if (varFirstChar(x)) {
   2.166 +        is.putback(x);
   2.167 +        LpSolverBase::Col f;
   2.168 +        readCol(is, f);
   2.169 +        c = f;
   2.170 +      } else {
   2.171 +        throw DataFormatError("Invalid expression format");          
   2.172 +      }
   2.173 +      is >> std::ws;
   2.174 +      while (is.get(y) && (y == '*' || y == '/')) {
   2.175 +        is >> std::ws;
   2.176 +        if (!is.get(x)) throw DataFormatError("Cannot find lp element");
   2.177 +        if (x == '+' || x == '-') {
   2.178 +          is >> std::ws;
   2.179 +          d *= x == '-' ? -1 : 1;
   2.180 +          while (is.get(x) && (x == '+' || x == '-')) {
   2.181 +            d *= x == '-' ? -1 : 1;
   2.182 +            is >> std::ws;
   2.183 +          }
   2.184 +          if (!is) throw DataFormatError("Cannot find lp element");
   2.185 +        }
   2.186 +        if (numFirstChar(x)) {
   2.187 +          is.putback(x);
   2.188 +          double e;
   2.189 +          readNum(is, e);
   2.190 +          if (y == '*') {
   2.191 +            d *= e;
   2.192 +          } else {
   2.193 +            d /= e;
   2.194 +          }
   2.195 +        } else if (varFirstChar(x)) {
   2.196 +          is.putback(x);
   2.197 +          LpSolverBase::Col f;
   2.198 +          readCol(is, f);
   2.199 +          if (y == '*') {
   2.200 +            if (c == INVALID) {
   2.201 +              c = f;
   2.202 +            } else {
   2.203 +              throw DataFormatError("Quadratic element in expression");
   2.204 +            }
   2.205 +          } else {
   2.206 +            throw DataFormatError("Division by variable");
   2.207 +          }
   2.208 +        } else {
   2.209 +          throw DataFormatError("Invalid expression format");          
   2.210 +        }
   2.211 +        is >> std::ws;
   2.212 +      }
   2.213 +      if (!is) {
   2.214 +        is.clear();
   2.215 +      } else {
   2.216 +        is.putback(y);
   2.217 +      }
   2.218 +      return is;
   2.219 +    }
   2.220 +
   2.221 +    std::istream& readCol(std::istream& is, LpSolverBase::Col& c) {
   2.222 +      char x;
   2.223 +      std::string var;
   2.224 +      while (is.get(x) && varChar(x)) {
   2.225 +        var += x;
   2.226 +      }
   2.227 +      if (!is) {
   2.228 +        is.clear();
   2.229 +      } else {
   2.230 +        is.putback(x);
   2.231 +      }
   2.232 +      ColMap::const_iterator it = cols.find(var);
   2.233 +      if (cols.find(var) != cols.end()) {
   2.234 +        c = it->second;
   2.235 +      } else {
   2.236 +        c = lp.addCol();
   2.237 +        cols.insert(std::make_pair(var, c));
   2.238 +        lp.colName(c, var);
   2.239 +      }
   2.240 +      return is;
   2.241 +    }
   2.242 +
   2.243 +    std::istream& readNum(std::istream& is, double& d) {
   2.244 +      is >> d;
   2.245 +      if (!is) throw DataFormatError("Wrong number format");
   2.246 +      return is;
   2.247 +    }
   2.248 +
   2.249 +    std::istream& readRelation(std::istream& is, Relation& op) {
   2.250 +      char x, y;
   2.251 +      if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
   2.252 +        throw DataFormatError("Wrong relation operator");
   2.253 +      }
   2.254 +      if (!is.get(y) || y != '=') {
   2.255 +        throw DataFormatError("Wrong relation operator");
   2.256 +      }
   2.257 +      switch (x) {
   2.258 +      case '<': op = LE; 
   2.259 +        break;
   2.260 +      case '=': op = EQ; 
   2.261 +        break;
   2.262 +      case '>': op = GE; 
   2.263 +        break;
   2.264 +      }
   2.265 +      return is;
   2.266 +    }
   2.267 +
   2.268 +    static bool relationFirstChar(char c) {
   2.269 +      return c == '<' || c == '=' || c == '>';
   2.270 +    }
   2.271 +
   2.272 +    static bool varFirstChar(char c) {
   2.273 +      return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
   2.274 +    }
   2.275 +
   2.276 +    static bool numFirstChar(char c) {
   2.277 +      return (c >= '0' && c <= '9') || c == '.';
   2.278 +    }
   2.279 +
   2.280 +    static bool varChar(char c) {
   2.281 +      return (c >= '0' && c <= '9') || 
   2.282 +        (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
   2.283 +    }
   2.284 +
   2.285 +
   2.286 +    void addConstraint(const LpSolverBase::Constr& constr) {
   2.287 +      if (constr.expr().size() != 1) {
   2.288 +        lp.addRow(constr);
   2.289 +      } else {
   2.290 +        Lp::Expr e = constr.expr();
   2.291 +        LpSolverBase::Col col = e.begin()->first;
   2.292 +        double coeff = e.begin()->second;
   2.293 +        double lb = LpSolverBase::NaN;
   2.294 +        double ub = LpSolverBase::NaN;
   2.295 +        if (coeff > 0) {
   2.296 +          if (constr.upperBounded()) {
   2.297 +            lb = (constr.lowerBound() - e.constComp()) / coeff;
   2.298 +          }
   2.299 +          if (constr.lowerBounded()) {
   2.300 +            ub = (constr.upperBound() - e.constComp()) / coeff;
   2.301 +          }
   2.302 +        } else if (coeff < 0) {
   2.303 +          if (constr.upperBounded()) {
   2.304 +            lb = (constr.upperBound() - e.constComp()) / coeff;
   2.305 +          }
   2.306 +          if (constr.lowerBounded()) {
   2.307 +            ub = (constr.lowerBound() - e.constComp()) / coeff;
   2.308 +          }
   2.309 +        } else {
   2.310 +          lb = -LpSolverBase::INF;
   2.311 +          ub = LpSolverBase::INF;
   2.312 +        }
   2.313 +        lp.colLowerBound(col, lb);
   2.314 +        lp.colUpperBound(col, ub);
   2.315 +      }
   2.316 +    }
   2.317 +    
   2.318 +  protected:
   2.319 +
   2.320 +    /// \brief Reader function of the section.
   2.321 +    ///
   2.322 +    /// It reads the content of the section.
   2.323 +    virtual void read(std::istream& is) {
   2.324 +      std::string line;
   2.325 +      std::map<std::string, LpSolverBase::Col> vars;
   2.326 +      while (getline(is, line)) {
   2.327 +        std::istringstream ls(line);
   2.328 +        std::string sense;
   2.329 +        ls >> sense;
   2.330 +        if (sense == "min" || sense == "max") {
   2.331 +          LpSolverBase::Expr expr;
   2.332 +          ls >> std::ws;
   2.333 +          readExpression(ls, expr);
   2.334 +          lp.setObj(expr);
   2.335 +          if (sense == "min") {
   2.336 +            lp.min();
   2.337 +          } else {
   2.338 +            lp.max();
   2.339 +          }
   2.340 +        } else {
   2.341 +          ls.str(line);
   2.342 +          LpSolverBase::Constr constr;
   2.343 +          ls >> std::ws;
   2.344 +          readConstraint(ls, constr);
   2.345 +          addConstraint(constr);
   2.346 +        }        
   2.347 +      }
   2.348 +    }
   2.349 +      
   2.350 +    virtual void missing() {
   2.351 +      ErrorMessage msg;
   2.352 +      msg << "Lp section not found in file: @lp " << name;
   2.353 +      throw IoParameterError(msg.message());
   2.354 +    }
   2.355 +
   2.356 +  private:
   2.357 +
   2.358 +    typedef std::map<std::string, LpSolverBase::Col> ColMap;
   2.359 +      
   2.360 +    LpSolverBase& lp;
   2.361 +    std::string name;
   2.362 +    ColMap cols;
   2.363 +  };
   2.364 +
   2.365 +}
   2.366 +
   2.367 +#endif