diff -r 041878e6f388 -r 6b2e8b734ae7 lemon/lp_utils.h --- a/lemon/lp_utils.h Mon Feb 19 09:55:43 2007 +0000 +++ b/lemon/lp_utils.h Mon Feb 19 12:11:41 2007 +0000 @@ -24,8 +24,294 @@ #include #include +#include +#include + + namespace lemon { + /// \ingroup gen_opt_tools + /// + /// \brief The map for the result of the lp variables. + /// + /// The map for the result of the lp variables. + class LpResultMap { + public: + typedef LpSolverBase::Col Key; + typedef LpSolverBase::Value Value; + + /// \brief Constructor + /// + /// Constructor + LpResultMap(const LpSolverBase& _lp) : lp(_lp) {} + LpSolverBase::Value operator[](const LpSolverBase::Col col) const { + return lp.primal(col); + } + private: + const LpSolverBase& lp; + }; + + /// \ingroup gen_opt_tools + /// + /// \brief The map for the name of the lp variables. + /// + /// The map for the name of the lp variables. + class LpColNameMap { + public: + typedef LpSolverBase::Col Key; + typedef std::string Value; + + /// \brief Constructor + /// + /// Constructor + LpColNameMap(const LpSolverBase& _lp) : lp(_lp) {} + std::string operator[](const LpSolverBase::Col col) const { + return lp.colName(col); + } + private: + const LpSolverBase& lp; + }; + + /// \ingroup gen_opt_tools + /// + /// \brief Writeable map for the name of the lp variables. + /// + /// Writeable map for the name of the lp variables. + class LpColNameWriteMap { + public: + typedef LpSolverBase::Col Key; + typedef std::string Value; + + /// \brief Constructor + /// + /// Constructor + LpColNameWriteMap(LpSolverBase& _lp) : lp(_lp) {} + std::string operator[](const LpSolverBase::Col col) const { + return lp.colName(col); + } + void set(const LpSolverBase::Col col, const std::string& name) { + lp.colName(col, name); + } + private: + LpSolverBase& lp; + }; + + /// \ingroup gen_opt_tools + /// + /// \brief Returns an \ref LpColNameMap class + /// + /// This function just returns an \ref LpColNameMap class. + /// \relates LpColNameMap + LpColNameMap lpColNameMap(const LpSolverBase& lp) { + return LpColNameMap(lp); + } + + LpColNameWriteMap lpColNameMap(LpSolverBase& lp) { + return LpColNameWriteMap(lp); + } + + /// \ingroup gen_opt_tools + /// + /// \brief Lp variable item reader for Lemon IO + /// + /// This class is an Lp variable item reader for Lemon IO. It makes + /// possible to store lp variables in lemon file. The usage of this + /// class is very simple: + /// + ///\code + /// Graph graph; + /// Lp lp; + /// Graph::EdgeMap var(graph); + /// + /// GraphReader reader(cin, graph); + /// reader.readEdgeMap("lpvar", var, LpColReader(lp)); + /// reader.run(); + ///\endcode + /// + /// If there is already a variable with the same name in the lp then + /// it will be used for the return value. If there is no such variable + /// then it will be constructed. The file could contain \c '-' as value + /// which means that no lp variable associated to the current item and + /// in this case INVALID will be returned. + class LpColReader { + public: + + /// \brief The value type of reader. + /// + /// The value type of reader. + typedef LpSolverBase::Col Value; + + /// \brief Constructor for the reader. + /// + /// Constructor for the reader. + LpColReader(LpSolverBase& _lp) : lp(_lp) {} + + /// \brief Reads an lp variable from the given stream. + /// + /// Reads an lp variable string from the given stream. + void read(std::istream& is, LpSolverBase::Col& col) const { + char x; + is >> std::ws; + if (!is.get(x)) + throw DataFormatError("Wrong Lp Column format!"); + if (varFirstChar(x)) { + std::string var; + var += x; + + while (is.get(x) && varChar(x)) { + var += x; + } + if (!is) { + is.clear(); + } else { + is.putback(x); + } + col = lp.colByName(var); + if (col == INVALID) { + col = lp.addCol(); + lp.colName(col, var); + } + } else if (x == '-') { + col = INVALID; + is >> std::ws; + } else { + std::cerr << x << std::endl; + throw DataFormatError("Wrong Lp Column format"); + } + } + + private: + + static bool varFirstChar(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + static bool varChar(char c) { + return (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '[' || c == ']'; + } + + LpSolverBase& lp; + + }; + + /// \ingroup gen_opt_tools + /// + /// \brief Lp variable item writer for Lemon IO + /// + /// This class is an Lp variable item writer for Lemon IO. It makes + /// possible to store lp variables in lemon file. The usage of this + /// class is very simple: + /// + ///\code + /// Graph graph; + /// Lp lp; + /// Graph::EdgeMap var(graph); + /// + /// GraphWriter writer(cin, graph); + /// writer.writeEdgeMap("lpvar", var, LpColWriter(lp)); + /// writer.run(); + ///\endcode + /// + /// If there is no name associated to the current item then the name + /// will automatically constructed. If the value is INVALID then it + /// will write an \c '-' value to the file. + class LpColWriter { + public: + + /// \brief The value type of writer. + /// + /// The value type of writer. + typedef LpSolverBase::Col Value; + + /// \brief Constructor for the writer. + /// + /// Constructor for the writer. + LpColWriter(const LpSolverBase& _lp) : lp(_lp), num(0) {} + + /// \brief Writes an lp variable to the given stream. + /// + /// Writes an lp variable to the given stream. + void write(std::ostream& os, const LpSolverBase::Col& col) const { + if (col != INVALID) { + std::string name = lp.colName(col); + if (name.empty()) { + std::ostringstream ls; + ls << "x" << num; + ++num; + while (lp.colByName(ls.str()) != INVALID) { + ls.str(std::string()); + ls << "x" << num; + ++num; + } + const_cast(lp).colName(col, ls.str()); + os << ls.str(); + } else { + os << name; + } + } else { + os << "-"; + } + } + + private: + + const LpSolverBase& lp; + mutable int num; + + }; + + /// \ingroup gen_opt_tools + /// + /// \brief Lp section reader for lemon IO + /// + /// This section reader provides an easy way to read an Lp problem + /// from a file. The lemon lp section format contains three parts. + /// + ///\code + /// @lp + /// constraints + /// 7 == x1 - 1 * x2 + /// 2 * x1 + x3 / 2 <= 7 + /// x2 + -3 * x3 >= 8 + /// 3 <= x2 - 2 * x1 <= 8 + /// bounds + /// x1 >= 3 + /// 2 <= x2 <= 5 + /// 0 <= x3 <= 8 + /// objective + /// min x1 + 2 * x2 - x3 + ///\endcode + /// + /// The first part gives the constraints to the lp. The constraints + /// could be equality, lower bound, upper bound or range for an + /// expression or equality, less or more for two expressions. + /// + /// The second part gives the bounds for the variables. The bounds + /// could be the same as for the single expression so equality, + /// lower bound, upper bound or range. + /// + /// The third part is the objective function, it should start with + /// \c min or \c max and then a valid linear expression. + /// + /// The reader constructs automatically the variable for a name if + /// there is not already a variable with the same name. + /// + /// The basic way to read an LP problem is made by the next code: + ///\code + /// Lp lp; + /// + /// LemonReader reader(filename_or_istream); + /// LpReader lpreader(reader, lp); + /// + /// reader.run(); + ///\endcode + /// + /// Of course instead of \c LemonReader you can give a \c + /// GraphReader to the LpReader constructor. Also useful that you + /// can mix lp problems and graphs in the same file. class LpReader : public LemonReader::SectionReader { typedef LemonReader::SectionReader Parent; public: @@ -33,7 +319,7 @@ /// \brief Constructor. /// - /// Constructor for LpReader. It creates the LpReader and attach + /// Constructor for LpReader. It creates the LpReader and attachs /// it into the given LemonReader. The lp reader will add /// variables, constraints and objective function to the /// given lp solver. @@ -44,7 +330,7 @@ /// \brief Destructor. /// - /// Destructor for NodeSetReader. + /// Destructor for LpReader. virtual ~LpReader() {} private: @@ -68,7 +354,7 @@ private: - enum SubSection { + enum Part { CONSTRAINTS, BOUNDS, OBJECTIVE }; @@ -210,6 +496,7 @@ lp.colLowerBound(c, w); lp.colUpperBound(c, v); } + is >> std::ws; if (is.get(x)) { throw DataFormatError("Wrong variable bounds format"); } @@ -343,12 +630,9 @@ } else { is.putback(x); } - ColMap::const_iterator it = cols.find(var); - if (cols.find(var) != cols.end()) { - c = it->second; - } else { + c = lp.colByName(var); + if (c == INVALID) { c = lp.addCol(); - cols.insert(std::make_pair(var, c)); lp.colName(c, var); } return is; @@ -390,7 +674,7 @@ 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') || @@ -405,20 +689,32 @@ /// It reads the content of the section. virtual void read(std::istream& is) { std::string line; - SubSection sub = CONSTRAINTS; + Part part = CONSTRAINTS; while (getline(is, line)) { std::istringstream ls(line); std::string type; ls >> type; if (type == "constraints") { - sub = CONSTRAINTS; + part = CONSTRAINTS; + ls >> std::ws; + char x; + if (ls.get(x)) + throw DataFormatError("Wrong Lp format"); } else if (type == "bounds") { - sub = BOUNDS; + part = BOUNDS; + ls >> std::ws; + char x; + if (ls.get(x)) + throw DataFormatError("Wrong Lp format"); } else if (type == "objective") { - sub = OBJECTIVE; + part = OBJECTIVE; + ls >> std::ws; + char x; + if (ls.get(x)) + throw DataFormatError("Wrong Lp format"); } else { ls.str(line); - switch (sub) { + switch (part) { case CONSTRAINTS: readConstraint(ls); break; @@ -429,7 +725,7 @@ readObjective(ls); break; } - } + } } } @@ -441,14 +737,62 @@ private: - typedef std::map ColMap; LpSolverBase& lp; std::string name; - ColMap cols; }; + /// \ingroup gen_opt_tools + /// + /// \brief Lp section writer for lemon IO + /// + /// This section reader provides an easy way to write an Lp problem + /// to a file. The lemon lp section format contains three parts. + /// + ///\code + /// @lp + /// constraints + /// 7 == x1 - 1 * x2 + /// 2 * x1 + x3 / 2 <= 7 + /// x2 + -3 * x3 >= 8 + /// 3 <= x2 - 2 * x1 <= 8 + /// bounds + /// x1 >= 3 + /// 2 <= x2 <= 5 + /// 0 <= x3 <= 8 + /// objective + /// min x1 + 2 * x2 - x3 + ///\endcode + /// + /// The first part gives the constraints to the lp. The constraints + /// could be equality, lower bound, upper bound or range for an + /// expression or equality, less or more for two expressions. + /// + /// The second part gives the bounds for the variables. The bounds + /// could be the same as for the single expression so equality, + /// lower bound, upper bound or range. + /// + /// The third part is the objective function, it should start with + /// \c min or \c max and then a valid linear expression. + /// + /// If an LP variable does not have name in the writer, then it will + /// automatically created in the writer. This make a slight change + /// in the \c const variable. + /// + /// The basic way to write an LP problem is made by the next code: + ///\code + /// Lp lp; + /// + /// LemonWriter writer(filename_or_ostream); + /// LpWriter lpwriter(writer, lp); + /// + /// writer.run(); + ///\endcode + /// + /// Of course instead of \c LemonWriter you can give a \c + /// GraphWriter to the LpWriter constructor. Also useful that you + /// can mix lp problems and graphs in the same file. class LpWriter : public LemonWriter::SectionWriter { typedef LemonWriter::SectionWriter Parent; public: @@ -457,17 +801,15 @@ /// \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, + /// it into the given LemonWriter. + LpWriter(LemonWriter& _writer, const LpSolverBase& _lp, const std::string& _name = std::string()) : Parent(_writer), lp(_lp), name(_name) {} /// \brief Destructor. /// - /// Destructor for NodeSetWriter. + /// Destructor for LpWriter. virtual ~LpWriter() {} private: @@ -489,21 +831,20 @@ private: void createCols() { - int num = 1; - - std::map inverse; + int num = 0; 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 { + if (name.empty()) { std::ostringstream ls; - ls << "var" << num; + ls << "x" << num; ++num; - cols.insert(std::make_pair(it, ls.str())); - inverse.insert(std::make_pair(ls.str(), it)); + while (lp.colByName(ls.str()) != INVALID) { + ls.str(std::string()); + ls << "x" << num; + ++num; + } + const_cast(lp).colName(it, ls.str()); } } } @@ -526,7 +867,7 @@ } } first = false; - os << cols[jt->first] << " "; + os << lp.colName(jt->first) << " "; } if (e.constComp() < 0.0) { os << "- " << -e.constComp() << " "; @@ -589,13 +930,13 @@ os << lower << " <= "; } - os << cols[it] << " "; + os << lp.colName(it) << " "; if (upper != LpSolverBase::INF) { os << "<= " << upper << " "; } } else { - os << cols[it] << " == " << upper << " "; + os << lp.colName(it) << " == " << upper << " "; } os << std::endl; } @@ -614,11 +955,8 @@ private: - typedef std::map ColMap; - - LpSolverBase& lp; + const LpSolverBase& lp; std::string name; - ColMap cols; }; }