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