/* -*- C++ -*- * * This file is a part of LEMON, a generic C++ optimization library * * Copyright (C) 2003-2006 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport * (Egervary Research Group on Combinatorial Optimization, EGRES). * * Permission to use, modify and distribute this software is granted * provided that this copyright notice appears in all copies. For * precise terms see the accompanying LICENSE file. * * This software is provided "AS IS" with no warranty of any kind, * express or implied, and with no claim as to its suitability for any * purpose. * */ #ifndef LEMON_LP_UTILS_H #define LEMON_LP_UTILS_H #include #include #include namespace lemon { class LpReader : public LemonReader::SectionReader { typedef LemonReader::SectionReader Parent; public: /// \brief Constructor. /// /// Constructor for LpReader. It creates the LpReader and attach /// it into the given LemonReader. The lp reader will add /// variables, constraints and objective function to the /// given lp solver. LpReader(LemonReader& _reader, LpSolverBase& _lp, const std::string& _name = std::string()) : Parent(_reader), lp(_lp), name(_name) {} /// \brief Destructor. /// /// Destructor for NodeSetReader. virtual ~LpReader() {} private: LpReader(const LpReader&); void operator=(const LpReader&); protected: /// \brief Gives back true when the SectionReader can process /// the section with the given header line. /// /// It gives back true when the header line starts with \c \@lp, /// and the header line's name and the nodeset's name are the same. virtual bool header(const std::string& line) { std::istringstream ls(line); std::string command; std::string id; ls >> command >> id; return command == "@lp" && name == id; } private: enum SubSection { CONSTRAINTS, BOUNDS, OBJECTIVE }; enum Relation { LE, EQ, GE }; std::istream& readConstraint(std::istream& is) { LpSolverBase::Constr c; char x; LpSolverBase::Expr e1, e2; Relation op1; is >> std::ws; readExpression(is, e1); is >> std::ws; readRelation(is, op1); is >> std::ws; readExpression(is, e2); is >> std::ws; if (!is.get(x)) { if (op1 == LE) { if (e1.size() == 0) { c = e2 >= e1; } else { c = e1 <= e2; } c = e1 <= e2; } else if (op1 == GE) { if (e1.size() == 0) { c = e2 <= e1; } else { c = e1 >= e2; } } else { if (e1.size() == 0) { c = e2 == e1; } else { c = e1 == e2; } } } else { is.putback(x); LpSolverBase::Expr e3; Relation op2; readRelation(is, op2); is >> std::ws; readExpression(is, e3); if (!e1.empty() || !e3.empty()) { throw DataFormatError("Wrong range format"); } if (op2 != op1 || op1 == EQ) { throw DataFormatError("Wrong range format"); } if (op1 == LE) { c = e1.constComp() <= e2 <= e3.constComp(); } else { c = e1.constComp() >= e2 >= e3.constComp(); } is >> std::ws; if (is.get(x)) { throw DataFormatError("Wrong variable bounds format"); } } lp.addRow(c); return is; } std::istream& readObjective(std::istream& is) { is >> std::ws; std::string sense; is >> sense; if (sense != "min" && sense != "max") { throw DataFormatError("Wrong objective function format"); } LpSolverBase::Expr expr; is >> std::ws; readExpression(is, expr); lp.setObj(expr); if (sense == "min") { lp.min(); } else { lp.max(); } return is; } std::istream& readBounds(std::istream& is) { LpSolverBase::Col c; char x; is >> std::ws; if (!is.get(x)) { throw DataFormatError("Wrong variable bounds format"); } if (varFirstChar(x)) { is.putback(x); readCol(is, c); is >> std::ws; Relation op1; readRelation(is, op1); double v; readNum(is, v); if (op1 == EQ) { lp.colLowerBound(c, v); lp.colUpperBound(c, v); } else if (op1 == LE) { lp.colUpperBound(c, v); } else { lp.colLowerBound(c, v); } is >> std::ws; if (is.get(x)) { throw DataFormatError("Wrong variable bounds format"); } } else { is.putback(x); double v; readNum(is, v); is >> std::ws; Relation op1; readRelation(is, op1); is >> std::ws; readCol(is, c); is >> std::ws; if (is.get(x)) { is.putback(x); is >> std::ws; Relation op2; readRelation(is, op2); double w; is >> std::ws; readNum(is, w); if (op2 != op1 || op1 == EQ) { throw DataFormatError("Wrong range format"); } if (op1 == LE) { lp.colLowerBound(c, v); lp.colUpperBound(c, w); } else { lp.colLowerBound(c, w); lp.colUpperBound(c, v); } if (is.get(x)) { throw DataFormatError("Wrong variable bounds format"); } } else { if (op1 == EQ) { lp.colLowerBound(c, v); lp.colUpperBound(c, v); } else if (op1 == LE) { lp.colLowerBound(c, v); } else { lp.colUpperBound(c, v); } } } return is; } std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) { LpSolverBase::Col c; double d; char x; readElement(is, c, d); if (c != INVALID) { e += d * c; } else { e += d; } is >> std::ws; while (is.get(x) && (x == '+' || x == '-')) { is >> std::ws; readElement(is, c, d); if (c != INVALID) { e += (x == '+' ? d : -d) * c; } else { e += (x == '+' ? d : -d); } is >> std::ws; } if (!is) { is.clear(); } else { is.putback(x); } return is; } std::istream& readElement(std::istream& is, LpSolverBase::Col& c, double& d) { d = 1.0; c = INVALID; char x, y; if (!is.get(x)) throw DataFormatError("Cannot find lp element"); if (x == '+' || x == '-') { is >> std::ws; d *= x == '-' ? -1 : 1; while (is.get(x) && (x == '+' || x == '-')) { d *= x == '-' ? -1 : 1; is >> std::ws; } if (!is) throw DataFormatError("Cannot find lp element"); } if (numFirstChar(x)) { is.putback(x); double e; readNum(is, e); d *= e; } else if (varFirstChar(x)) { is.putback(x); LpSolverBase::Col f; readCol(is, f); c = f; } else { throw DataFormatError("Invalid expression format"); } is >> std::ws; while (is.get(y) && (y == '*' || y == '/')) { is >> std::ws; if (!is.get(x)) throw DataFormatError("Cannot find lp element"); if (x == '+' || x == '-') { is >> std::ws; d *= x == '-' ? -1 : 1; while (is.get(x) && (x == '+' || x == '-')) { d *= x == '-' ? -1 : 1; is >> std::ws; } if (!is) throw DataFormatError("Cannot find lp element"); } if (numFirstChar(x)) { is.putback(x); double e; readNum(is, e); if (y == '*') { d *= e; } else { d /= e; } } else if (varFirstChar(x)) { is.putback(x); LpSolverBase::Col f; readCol(is, f); if (y == '*') { if (c == INVALID) { c = f; } else { throw DataFormatError("Quadratic element in expression"); } } else { throw DataFormatError("Division by variable"); } } else { throw DataFormatError("Invalid expression format"); } is >> std::ws; } if (!is) { is.clear(); } else { is.putback(y); } return is; } std::istream& readCol(std::istream& is, LpSolverBase::Col& c) { char x; std::string var; while (is.get(x) && varChar(x)) { var += x; } if (!is) { is.clear(); } else { is.putback(x); } ColMap::const_iterator it = cols.find(var); if (cols.find(var) != cols.end()) { c = it->second; } else { c = lp.addCol(); cols.insert(std::make_pair(var, c)); lp.colName(c, var); } return is; } std::istream& readNum(std::istream& is, double& d) { is >> d; if (!is) throw DataFormatError("Wrong number format"); return is; } std::istream& readRelation(std::istream& is, Relation& op) { char x, y; if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) { throw DataFormatError("Wrong relation operator"); } if (!is.get(y) || y != '=') { throw DataFormatError("Wrong relation operator"); } switch (x) { case '<': op = LE; break; case '=': op = EQ; break; case '>': op = GE; break; } return is; } static bool relationFirstChar(char c) { return c == '<' || c == '=' || c == '>'; } static bool varFirstChar(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } static bool numFirstChar(char c) { return (c >= '0' && c <= '9') || c == '.'; } static bool varChar(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '[' || c == ']'; } protected: /// \brief Reader function of the section. /// /// It reads the content of the section. virtual void read(std::istream& is) { std::string line; SubSection sub = CONSTRAINTS; while (getline(is, line)) { std::istringstream ls(line); std::string type; ls >> type; if (type == "constraints") { sub = CONSTRAINTS; } else if (type == "bounds") { sub = BOUNDS; } else if (type == "objective") { sub = OBJECTIVE; } else { ls.str(line); switch (sub) { case CONSTRAINTS: readConstraint(ls); break; case BOUNDS: readBounds(ls); break; case OBJECTIVE: readObjective(ls); break; } } } } virtual void missing() { ErrorMessage msg; msg << "Lp section not found in file: @lp " << name; throw IoParameterError(msg.message()); } private: typedef std::map ColMap; LpSolverBase& lp; std::string name; ColMap cols; }; class LpWriter : public LemonWriter::SectionWriter { typedef LemonWriter::SectionWriter Parent; public: /// \brief Constructor. /// /// Constructor for LpWriter. It creates the LpWriter and attach /// it into the given LemonWriter. The lp writer will add /// variables, constraints and objective function to the /// given lp solver. LpWriter(LemonWriter& _writer, LpSolverBase& _lp, const std::string& _name = std::string()) : Parent(_writer), lp(_lp), name(_name) {} /// \brief Destructor. /// /// Destructor for NodeSetWriter. virtual ~LpWriter() {} private: LpWriter(const LpWriter&); void operator=(const LpWriter&); protected: /// \brief Gives back true when the SectionWriter can process /// the section with the given header line. /// /// It gives back the header line of the \c \@lp section. virtual std::string header() { std::ostringstream ls; ls << "@lp " << name; return ls.str(); } private: void createCols() { int num = 1; std::map inverse; for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) { std::string name = lp.colName(it); if (!name.empty() && inverse.find(name) == inverse.end()) { cols.insert(std::make_pair(it, name)); inverse.insert(std::make_pair(name, it)); } else { std::ostringstream ls; ls << "var" << num; ++num; cols.insert(std::make_pair(it, ls.str())); inverse.insert(std::make_pair(ls.str(), it)); } } } void writeExpression(std::ostream& os, const LpSolverBase::Expr& e) { bool first = true; for (LpSolverBase::Expr::const_iterator jt = e.begin(); jt != e.end(); ++jt) { if (jt->second < 0.0) { os << "- "; if (jt->second != -1.0) { os << -jt->second << " * "; } } else if (jt->second > 0.0) { if (!first) { os << "+ "; } if (jt->second != 1.0) { os << jt->second << " * "; } } first = false; os << cols[jt->first] << " "; } if (e.constComp() < 0.0) { os << "- " << -e.constComp() << " "; } else if (e.constComp() > 0.0) { if (!first) { os << "+ "; } os << e.constComp() << " "; } } protected: /// \brief Writer function of the section. /// /// It writes the content of the section. virtual void write(std::ostream& os) { createCols(); os << "constraints" << std::endl; for (LpSolverBase::RowIt it(lp); it != INVALID; ++it) { double lower, upper; lp.getRowBounds(it, lower, upper); if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF) continue; os << " "; if (lower != upper) { if (lower != -LpSolverBase::INF) { os << lower << " <= "; } writeExpression(os, lp.row(it)); if (upper != LpSolverBase::INF) { os << "<= " << upper << " "; } } else { writeExpression(os, lp.row(it)); os << "== " << upper << " "; } os << std::endl; } os << "bounds" << std::endl; for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) { double lower = lp.colLowerBound(it); double upper = lp.colUpperBound(it); if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF) continue; os << " "; if (lower != upper) { if (lower != -LpSolverBase::INF) { os << lower << " <= "; } os << cols[it] << " "; if (upper != LpSolverBase::INF) { os << "<= " << upper << " "; } } else { os << cols[it] << " == " << upper << " "; } os << std::endl; } os << "objective" << std::endl; os << " "; if (lp.is_min()) { os << "min "; } else { os << "max "; } writeExpression(os, lp.obj()); os << std::endl; } private: typedef std::map ColMap; LpSolverBase& lp; std::string name; ColMap cols; }; } #endif