3 * This file is a part of LEMON, a generic C++ optimization library
5 * Copyright (C) 2003-2006
6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
9 * Permission to use, modify and distribute this software is granted
10 * provided that this copyright notice appears in all copies. For
11 * precise terms see the accompanying LICENSE file.
13 * This software is provided "AS IS" with no warranty of any kind,
14 * express or implied, and with no claim as to its suitability for any
19 #ifndef LEMON_LP_UTILS_H
20 #define LEMON_LP_UTILS_H
22 #include <lemon/lp_base.h>
24 #include <lemon/lemon_reader.h>
25 #include <lemon/lemon_writer.h>
29 class LpReader : public LemonReader::SectionReader {
30 typedef LemonReader::SectionReader Parent;
34 /// \brief Constructor.
36 /// Constructor for LpReader. It creates the LpReader and attach
37 /// it into the given LemonReader. The lp reader will add
38 /// variables, constraints and objective function to the
40 LpReader(LemonReader& _reader, LpSolverBase& _lp,
41 const std::string& _name = std::string())
42 : Parent(_reader), lp(_lp), name(_name) {}
45 /// \brief Destructor.
47 /// Destructor for NodeSetReader.
48 virtual ~LpReader() {}
51 LpReader(const LpReader&);
52 void operator=(const LpReader&);
56 /// \brief Gives back true when the SectionReader can process
57 /// the section with the given header line.
59 /// It gives back true when the header line starts with \c \@lp,
60 /// and the header line's name and the nodeset's name are the same.
61 virtual bool header(const std::string& line) {
62 std::istringstream ls(line);
66 return command == "@lp" && name == id;
75 std::istream& readConstraint(std::istream& is, LpSolverBase::Constr& c) {
77 LpSolverBase::Expr e1, e2;
80 readExpression(is, e1);
82 readRelation(is, op1);
84 readExpression(is, e2);
88 } else if (op1 == GE) {
95 LpSolverBase::Expr e3;
97 readRelation(is, op2);
99 readExpression(is, e3);
100 if (!e1.empty() || !e3.empty()) {
101 throw DataFormatError("Wrong range format");
103 if (op2 != op1 || op1 == EQ) {
104 throw DataFormatError("Wrong range format");
107 c = e1.constComp() <= e2 <= e3.constComp();
109 c = e1.constComp() >= e2 >= e3.constComp();
114 std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) {
118 readElement(is, c, d);
125 while (is.get(x) && (x == '+' || x == '-')) {
127 readElement(is, c, d);
129 e += (x == '+' ? d : -d) * c;
131 e += (x == '+' ? d : -d);
143 std::istream& readElement(std::istream& is,
144 LpSolverBase::Col& c, double& d) {
148 if (!is.get(x)) throw DataFormatError("Cannot find lp element");
149 if (x == '+' || x == '-') {
151 d *= x == '-' ? -1 : 1;
152 while (is.get(x) && (x == '+' || x == '-')) {
153 d *= x == '-' ? -1 : 1;
156 if (!is) throw DataFormatError("Cannot find lp element");
158 if (numFirstChar(x)) {
163 } else if (varFirstChar(x)) {
169 throw DataFormatError("Invalid expression format");
172 while (is.get(y) && (y == '*' || y == '/')) {
174 if (!is.get(x)) throw DataFormatError("Cannot find lp element");
175 if (x == '+' || x == '-') {
177 d *= x == '-' ? -1 : 1;
178 while (is.get(x) && (x == '+' || x == '-')) {
179 d *= x == '-' ? -1 : 1;
182 if (!is) throw DataFormatError("Cannot find lp element");
184 if (numFirstChar(x)) {
193 } else if (varFirstChar(x)) {
201 throw DataFormatError("Quadratic element in expression");
204 throw DataFormatError("Division by variable");
207 throw DataFormatError("Invalid expression format");
219 std::istream& readCol(std::istream& is, LpSolverBase::Col& c) {
222 while (is.get(x) && varChar(x)) {
230 ColMap::const_iterator it = cols.find(var);
231 if (cols.find(var) != cols.end()) {
235 cols.insert(std::make_pair(var, c));
241 std::istream& readNum(std::istream& is, double& d) {
243 if (!is) throw DataFormatError("Wrong number format");
247 std::istream& readRelation(std::istream& is, Relation& op) {
249 if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
250 throw DataFormatError("Wrong relation operator");
252 if (!is.get(y) || y != '=') {
253 throw DataFormatError("Wrong relation operator");
266 static bool relationFirstChar(char c) {
267 return c == '<' || c == '=' || c == '>';
270 static bool varFirstChar(char c) {
271 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
274 static bool numFirstChar(char c) {
275 return (c >= '0' && c <= '9') || c == '.';
278 static bool varChar(char c) {
279 return (c >= '0' && c <= '9') ||
280 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
284 void addConstraint(const LpSolverBase::Constr& constr) {
285 if (constr.expr().size() != 1) {
288 Lp::Expr e = constr.expr();
289 LpSolverBase::Col col = e.begin()->first;
290 double coeff = e.begin()->second;
291 double lb = LpSolverBase::NaN;
292 double ub = LpSolverBase::NaN;
294 if (constr.upperBounded()) {
295 lb = (constr.lowerBound() - e.constComp()) / coeff;
297 if (constr.lowerBounded()) {
298 ub = (constr.upperBound() - e.constComp()) / coeff;
300 } else if (coeff < 0) {
301 if (constr.upperBounded()) {
302 lb = (constr.upperBound() - e.constComp()) / coeff;
304 if (constr.lowerBounded()) {
305 ub = (constr.lowerBound() - e.constComp()) / coeff;
308 lb = -LpSolverBase::INF;
309 ub = LpSolverBase::INF;
311 lp.colLowerBound(col, lb);
312 lp.colUpperBound(col, ub);
318 /// \brief Reader function of the section.
320 /// It reads the content of the section.
321 virtual void read(std::istream& is) {
323 while (getline(is, line)) {
324 std::istringstream ls(line);
327 if (sense == "min" || sense == "max") {
328 LpSolverBase::Expr expr;
330 readExpression(ls, expr);
332 if (sense == "min") {
339 LpSolverBase::Constr constr;
341 readConstraint(ls, constr);
342 addConstraint(constr);
347 virtual void missing() {
349 msg << "Lp section not found in file: @lp " << name;
350 throw IoParameterError(msg.message());
355 typedef std::map<std::string, LpSolverBase::Col> ColMap;
363 // class LpWriter : public LemonWriter::SectionWriter {
364 // typedef LemonWriter::SectionWriter Parent;
368 // /// \brief Constructor.
370 // /// Constructor for LpWriter. It creates the LpWriter and attach
371 // /// it into the given LemonWriter. The lp writer will add
372 // /// variables, constraints and objective function to the
373 // /// given lp solver.
374 // LpWriter(LemonWriter& _writer, LpSolverBase& _lp,
375 // const std::string& _name = std::string())
376 // : Parent(_writer), lp(_lp), name(_name) {}
379 // /// \brief Destructor.
381 // /// Destructor for NodeSetWriter.
382 // virtual ~LpWriter() {}
385 // LpWriter(const LpWriter&);
386 // void operator=(const LpWriter&);
390 // /// \brief Gives back true when the SectionWriter can process
391 // /// the section with the given header line.
393 // /// It gives back the header line of the \c \@lp section.
394 // virtual std::string header() {
395 // std::ostringstream ls(line);
396 // ls << "@lp " << name;
402 // std::ostream& writeConstraint(std::ostream& is, LpSolverBase::Constr& c) {
406 // LpSolverBase::Expr e1, e2;
409 // writexpression(is, e1);
411 // writeRelation(is, op1);
413 // writexpression(is, e2);
417 // } else if (op1 == GE) {
424 // LpSolverBase::Expr e3;
426 // writeRelation(is, op2);
428 // writexpression(is, e3);
429 // if (!e1.empty() || !e3.empty()) {
430 // throw DataFormatError("Wrong range format");
432 // if (op2 != op1 || op1 == EQ) {
433 // throw DataFormatError("Wrong range format");
436 // c = e1.constComp() <= e2 <= e3.constComp();
438 // c = e1.constComp() >= e2 >= e3.constComp();
443 // std::ostream& writexpression(std::ostream& is, LpSolverBase::Expr& e) {
444 // LpSolverBase::Col c;
447 // writelement(is, c, d);
448 // if (c != INVALID) {
454 // while (is.get(x) && (x == '+' || x == '-')) {
456 // writelement(is, c, d);
457 // if (c != INVALID) {
458 // e += (x == '+' ? d : -d) * c;
460 // e += (x == '+' ? d : -d);
472 // std::ostream& writelement(std::ostream& is,
473 // LpSolverBase::Col& c, double& d) {
477 // if (!is.get(x)) throw DataFormatError("Cannot find lp element");
478 // if (x == '+' || x == '-') {
480 // d *= x == '-' ? -1 : 1;
481 // while (is.get(x) && (x == '+' || x == '-')) {
482 // d *= x == '-' ? -1 : 1;
485 // if (!is) throw DataFormatError("Cannot find lp element");
487 // if (numFirstChar(x)) {
492 // } else if (varFirstChar(x)) {
494 // LpSolverBase::Col f;
498 // throw DataFormatError("Invalid expression format");
501 // while (is.get(y) && (y == '*' || y == '/')) {
503 // if (!is.get(x)) throw DataFormatError("Cannot find lp element");
504 // if (x == '+' || x == '-') {
506 // d *= x == '-' ? -1 : 1;
507 // while (is.get(x) && (x == '+' || x == '-')) {
508 // d *= x == '-' ? -1 : 1;
511 // if (!is) throw DataFormatError("Cannot find lp element");
513 // if (numFirstChar(x)) {
522 // } else if (varFirstChar(x)) {
524 // LpSolverBase::Col f;
527 // if (c == INVALID) {
530 // throw DataFormatError("Quadratic element in expression");
533 // throw DataFormatError("Division by variable");
536 // throw DataFormatError("Invalid expression format");
548 // std::ostream& writeCol(std::ostream& is, LpSolverBase::Col& c) {
551 // while (is.get(x) && varChar(x)) {
559 // ColMap::const_iterator it = cols.find(var);
560 // if (cols.find(var) != cols.end()) {
564 // cols.insert(std::make_pair(var, c));
565 // lp.colName(c, var);
570 // std::ostream& writeNum(std::ostream& is, double& d) {
572 // if (!is) throw DataFormatError("Wrong number format");
576 // std::ostream& writeRelation(std::ostream& is, Relation& op) {
578 // if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
579 // throw DataFormatError("Wrong relation operator");
581 // if (!is.get(y) || y != '=') {
582 // throw DataFormatError("Wrong relation operator");
585 // case '<': op = LE;
587 // case '=': op = EQ;
589 // case '>': op = GE;
595 // static bool relationFirstChar(char c) {
596 // return c == '<' || c == '=' || c == '>';
599 // static bool varFirstChar(char c) {
600 // return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
603 // static bool numFirstChar(char c) {
604 // return (c >= '0' && c <= '9') || c == '.';
607 // static bool varChar(char c) {
608 // return (c >= '0' && c <= '9') ||
609 // (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
613 // void addConstraint(const LpSolverBase::Constr& constr) {
614 // if (constr.expr().size() != 1) {
615 // lp.addRow(constr);
617 // Lp::Expr e = constr.expr();
618 // LpSolverBase::Col col = e.begin()->first;
619 // double coeff = e.begin()->second;
620 // double lb = LpSolverBase::NaN;
621 // double ub = LpSolverBase::NaN;
623 // if (constr.upperBounded()) {
624 // lb = (constr.lowerBound() - e.constComp()) / coeff;
626 // if (constr.lowerBounded()) {
627 // ub = (constr.upperBound() - e.constComp()) / coeff;
629 // } else if (coeff < 0) {
630 // if (constr.upperBounded()) {
631 // lb = (constr.upperBound() - e.constComp()) / coeff;
633 // if (constr.lowerBounded()) {
634 // ub = (constr.lowerBound() - e.constComp()) / coeff;
637 // lb = -LpSolverBase::INF;
638 // ub = LpSolverBase::INF;
640 // lp.colLowerBound(col, lb);
641 // lp.colUpperBound(col, ub);
647 // /// \brief Writer function of the section.
649 // /// It writes the content of the section.
650 // virtual void write(std::ostream& is) {
652 // std::map<std::string, LpSolverBase::Col> vars;
653 // while (getline(is, line)) {
654 // std::istringstream ls(line);
655 // std::string sense;
657 // if (sense == "min" || sense == "max") {
658 // LpSolverBase::Expr expr;
660 // writeExpression(ls, expr);
662 // if (sense == "min") {
669 // LpSolverBase::Constr constr;
671 // writeConstraint(ls, constr);
672 // addConstraint(constr);
677 // virtual void missing() {
679 // msg << "Lp section not found in file: @lp " << name;
680 // throw IoParameterError(msg.message());
685 // typedef std::map<std::string, LpSolverBase::Col> ColMap;