lemon/lp_utils.h
author deba
Mon, 19 Feb 2007 18:21:28 +0000
changeset 2369 6ae1a97055a2
parent 2368 6b2e8b734ae7
child 2370 ed6539025f27
permissions -rw-r--r--
Naming convention changes

setObj => obj
is_min => isMin
is_max => isMax
     1 /* -*- C++ -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library
     4  *
     5  * Copyright (C) 2003-2006
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8  *
     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.
    12  *
    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
    15  * purpose.
    16  *
    17  */
    18 
    19 #ifndef LEMON_LP_UTILS_H
    20 #define LEMON_LP_UTILS_H
    21 
    22 #include <lemon/lp_base.h>
    23 
    24 #include <lemon/lemon_reader.h>
    25 #include <lemon/lemon_writer.h>
    26 
    27 #include <map>
    28 #include <set>
    29 
    30 
    31 namespace lemon {
    32 
    33   /// \ingroup gen_opt_tools
    34   ///
    35   /// \brief The map for the result of the lp variables.
    36   ///
    37   /// The map for the result of the lp variables.
    38   class LpResultMap {
    39   public:
    40     typedef LpSolverBase::Col Key;
    41     typedef LpSolverBase::Value Value;
    42 
    43     /// \brief Constructor
    44     ///
    45     /// Constructor
    46     LpResultMap(const LpSolverBase& _lp) : lp(_lp) {}
    47     LpSolverBase::Value operator[](const LpSolverBase::Col col) const {
    48       return lp.primal(col);
    49     }
    50   private:
    51     const LpSolverBase& lp;
    52   };
    53 
    54   /// \ingroup gen_opt_tools
    55   ///
    56   /// \brief The map for the name of the lp variables.
    57   ///
    58   /// The map for the name of the lp variables.
    59   class LpColNameMap {
    60   public:
    61     typedef LpSolverBase::Col Key;
    62     typedef std::string Value;
    63 
    64     /// \brief Constructor
    65     ///
    66     /// Constructor
    67     LpColNameMap(const LpSolverBase& _lp) : lp(_lp) {}
    68     std::string operator[](const LpSolverBase::Col col) const {
    69       return lp.colName(col);
    70     }
    71   private:
    72     const LpSolverBase& lp;
    73   };
    74 
    75   /// \ingroup gen_opt_tools
    76   ///
    77   /// \brief Writeable map for the name of the lp variables.
    78   ///
    79   /// Writeable map for the name of the lp variables.
    80   class LpColNameWriteMap {
    81   public:
    82     typedef LpSolverBase::Col Key;
    83     typedef std::string Value;
    84 
    85     /// \brief Constructor
    86     ///
    87     /// Constructor
    88     LpColNameWriteMap(LpSolverBase& _lp) : lp(_lp) {}
    89     std::string operator[](const LpSolverBase::Col col) const {
    90       return lp.colName(col);
    91     }
    92     void set(const LpSolverBase::Col col, const std::string& name) {
    93       lp.colName(col, name);
    94     }
    95   private:
    96     LpSolverBase& lp;
    97   };
    98 
    99   /// \ingroup gen_opt_tools
   100   ///
   101   /// \brief Returns an \ref LpColNameMap class
   102   ///
   103   /// This function just returns an \ref LpColNameMap class.
   104   /// \relates LpColNameMap
   105   LpColNameMap lpColNameMap(const LpSolverBase& lp) {
   106     return LpColNameMap(lp);
   107   }
   108 
   109   LpColNameWriteMap lpColNameMap(LpSolverBase& lp) {
   110     return LpColNameWriteMap(lp);
   111   }
   112 
   113   /// \ingroup gen_opt_tools
   114   ///
   115   /// \brief Lp variable item reader for Lemon IO
   116   ///
   117   /// This class is an Lp variable item reader for Lemon IO. It makes
   118   /// possible to store lp variables in lemon file. The usage of this
   119   /// class is very simple:
   120   ///
   121   ///\code
   122   /// Graph graph;
   123   /// Lp lp;
   124   /// Graph::EdgeMap<Lp::Col> var(graph); 
   125   ///
   126   /// GraphReader<Graph> reader(cin, graph);
   127   /// reader.readEdgeMap("lpvar", var, LpColReader(lp));
   128   /// reader.run();
   129   ///\endcode
   130   ///
   131   /// If there is already a variable with the same name in the lp then
   132   /// it will be used for the return value. If there is no such variable
   133   /// then it will be constructed. The file could contain \c '-' as value
   134   /// which means that no lp variable associated to the current item and
   135   /// in this case INVALID will be returned.
   136   class LpColReader {
   137   public:
   138 
   139     /// \brief The value type of reader.
   140     ///
   141     /// The value type of reader.
   142     typedef LpSolverBase::Col Value;
   143 
   144     /// \brief Constructor for the reader.
   145     ///
   146     /// Constructor for the reader.
   147     LpColReader(LpSolverBase& _lp) : lp(_lp) {}
   148 
   149     /// \brief Reads an lp variable from the given stream.
   150     ///
   151     /// Reads an lp variable string from the given stream.
   152     void read(std::istream& is, LpSolverBase::Col& col) const {
   153       char x;
   154       is >> std::ws;
   155       if (!is.get(x))
   156         throw DataFormatError("Wrong Lp Column format!");
   157       if (varFirstChar(x)) {
   158         std::string var;
   159         var += x;
   160         
   161         while (is.get(x) && varChar(x)) {
   162           var += x;
   163         }
   164         if (!is) {
   165           is.clear();
   166         } else {
   167           is.putback(x);
   168         }
   169         col = lp.colByName(var);
   170         if (col == INVALID) {
   171           col = lp.addCol();
   172           lp.colName(col, var);
   173         }
   174       } else if (x == '-') {
   175         col = INVALID;
   176         is >> std::ws;
   177       } else {
   178         std::cerr << x << std::endl;
   179         throw DataFormatError("Wrong Lp Column format");
   180       }
   181     }
   182 
   183   private:
   184 
   185     static bool varFirstChar(char c) {
   186       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
   187     }
   188 
   189     static bool varChar(char c) {
   190       return (c >= '0' && c <= '9') || 
   191         (c >= 'a' && c <= 'z') || 
   192         (c >= 'A' && c <= 'Z') ||
   193         c == '[' || c == ']';
   194     }
   195     
   196     LpSolverBase& lp;
   197 
   198   };
   199 
   200   /// \ingroup gen_opt_tools
   201   ///
   202   /// \brief Lp variable item writer for Lemon IO
   203   ///
   204   /// This class is an Lp variable item writer for Lemon IO. It makes
   205   /// possible to store lp variables in lemon file. The usage of this
   206   /// class is very simple:
   207   ///
   208   ///\code
   209   /// Graph graph;
   210   /// Lp lp;
   211   /// Graph::EdgeMap<Lp::Col> var(graph); 
   212   ///
   213   /// GraphWriter<Graph> writer(cin, graph);
   214   /// writer.writeEdgeMap("lpvar", var, LpColWriter(lp));
   215   /// writer.run();
   216   ///\endcode
   217   ///
   218   /// If there is no name associated to the current item then the name
   219   /// will automatically constructed. If the value is INVALID then it
   220   /// will write an \c '-' value to the file.
   221   class LpColWriter {
   222   public:
   223 
   224     /// \brief The value type of writer.
   225     ///
   226     /// The value type of writer.
   227     typedef LpSolverBase::Col Value;
   228 
   229     /// \brief Constructor for the writer.
   230     ///
   231     /// Constructor for the writer.
   232     LpColWriter(const LpSolverBase& _lp) : lp(_lp), num(0) {}
   233 
   234     /// \brief Writes an lp variable to the given stream.
   235     ///
   236     /// Writes an lp variable to the given stream.
   237     void write(std::ostream& os, const LpSolverBase::Col& col) const {
   238       if (col != INVALID) {
   239         std::string name = lp.colName(col);
   240         if (name.empty()) {
   241           std::ostringstream ls;
   242           ls << "x" << num;
   243           ++num;
   244           while (lp.colByName(ls.str()) != INVALID) {
   245             ls.str(std::string());
   246             ls << "x" << num;
   247             ++num;
   248           }
   249           const_cast<LpSolverBase&>(lp).colName(col, ls.str());
   250           os << ls.str();
   251         } else {
   252           os << name;
   253         }
   254       } else {
   255         os << "-";
   256       }
   257     }
   258 
   259   private:
   260 
   261     const LpSolverBase& lp;
   262     mutable int num;
   263 
   264   };
   265 
   266   /// \ingroup gen_opt_tools
   267   ///
   268   /// \brief Lp section reader for lemon IO
   269   ///
   270   /// This section reader provides an easy way to read an Lp problem
   271   /// from a file. The lemon lp section format contains three parts.
   272   ///
   273   ///\code
   274   /// @lp
   275   /// constraints
   276   ///   7 == x1 - 1 * x2
   277   ///   2 * x1 + x3 / 2 <= 7
   278   ///   x2 + -3 * x3 >= 8
   279   ///   3 <= x2 - 2 * x1 <= 8
   280   /// bounds
   281   ///   x1 >= 3
   282   ///   2 <= x2 <= 5
   283   ///   0 <= x3 <= 8
   284   /// objective
   285   ///   min x1 + 2 * x2 - x3
   286   ///\endcode
   287   ///
   288   /// The first part gives the constraints to the lp. The constraints
   289   /// could be equality, lower bound, upper bound or range for an
   290   /// expression or equality, less or more for two expressions.
   291   ///
   292   /// The second part gives the bounds for the variables. The bounds
   293   /// could be the same as for the single expression so equality,
   294   /// lower bound, upper bound or range.
   295   ///
   296   /// The third part is the objective function, it should start with
   297   /// \c min or \c max and then a valid linear expression.
   298   ///
   299   /// The reader constructs automatically the variable for a name if
   300   /// there is not already a variable with the same name.
   301   ///
   302   /// The basic way to read an LP problem is made by the next code:
   303   ///\code
   304   /// Lp lp;
   305   ///
   306   /// LemonReader reader(filename_or_istream);
   307   /// LpReader lpreader(reader, lp);
   308   /// 
   309   /// reader.run();
   310   ///\endcode
   311   ///
   312   /// Of course instead of \c LemonReader you can give a \c
   313   /// GraphReader to the LpReader constructor. Also useful that you
   314   /// can mix lp problems and graphs in the same file.
   315   class LpReader : public LemonReader::SectionReader {
   316     typedef LemonReader::SectionReader Parent;
   317   public:
   318 
   319 
   320     /// \brief Constructor.
   321     ///
   322     /// Constructor for LpReader. It creates the LpReader and attachs
   323     /// it into the given LemonReader. The lp reader will add
   324     /// variables, constraints and objective function to the
   325     /// given lp solver.
   326     LpReader(LemonReader& _reader, LpSolverBase& _lp, 
   327              const std::string& _name = std::string())
   328       : Parent(_reader), lp(_lp), name(_name) {} 
   329 
   330 
   331     /// \brief Destructor.
   332     ///
   333     /// Destructor for LpReader.
   334     virtual ~LpReader() {}
   335 
   336   private:
   337     LpReader(const LpReader&);
   338     void operator=(const LpReader&);
   339   
   340   protected:
   341 
   342     /// \brief Gives back true when the SectionReader can process 
   343     /// the section with the given header line.
   344     ///
   345     /// It gives back true when the header line starts with \c \@lp,
   346     /// and the header line's name and the nodeset's name are the same.
   347     virtual bool header(const std::string& line) {
   348       std::istringstream ls(line);
   349       std::string command;
   350       std::string id;
   351       ls >> command >> id;
   352       return command == "@lp" && name == id;
   353     }
   354 
   355   private:
   356 
   357     enum Part {
   358       CONSTRAINTS, BOUNDS, OBJECTIVE
   359     };
   360 
   361     enum Relation {
   362       LE, EQ, GE
   363     };
   364 
   365     std::istream& readConstraint(std::istream& is) {
   366       LpSolverBase::Constr c;
   367       char x;
   368       LpSolverBase::Expr e1, e2;
   369       Relation op1;
   370       is >> std::ws;
   371       readExpression(is, e1);
   372       is >> std::ws;
   373       readRelation(is, op1);
   374       is >> std::ws;
   375       readExpression(is, e2);
   376       is >> std::ws;
   377       if (!is.get(x)) {
   378         if (op1 == LE) {
   379           if (e1.size() == 0) {
   380             c = e2 >= e1;
   381           } else {
   382             c = e1 <= e2;
   383           }
   384           c = e1 <= e2;
   385         } else if (op1 == GE) {
   386           if (e1.size() == 0) {
   387             c = e2 <= e1;
   388           } else {
   389             c = e1 >= e2;
   390           }
   391         } else {
   392           if (e1.size() == 0) {
   393             c = e2 == e1;
   394           } else {
   395             c = e1 == e2;
   396           }
   397         }
   398       } else {
   399         is.putback(x);
   400         LpSolverBase::Expr e3;
   401         Relation op2;
   402         readRelation(is, op2);
   403         is >> std::ws;
   404         readExpression(is, e3);
   405         if (!e1.empty() || !e3.empty()) {
   406           throw DataFormatError("Wrong range format");
   407         }
   408         if (op2 != op1 || op1 == EQ) {
   409           throw DataFormatError("Wrong range format");
   410         }
   411         if (op1 == LE) {
   412           c = e1.constComp() <= e2 <= e3.constComp();
   413         } else {
   414           c = e1.constComp() >= e2 >= e3.constComp();
   415         }
   416         is >> std::ws;
   417         if (is.get(x)) {
   418           throw DataFormatError("Wrong variable bounds format");
   419         }
   420       }
   421       lp.addRow(c);
   422       return is;
   423     }
   424 
   425     std::istream& readObjective(std::istream& is) {
   426       is >> std::ws;
   427       std::string sense;
   428       is >> sense;
   429       if (sense != "min" && sense != "max") {
   430         throw DataFormatError("Wrong objective function format");
   431       }
   432       LpSolverBase::Expr expr;
   433       is >> std::ws;
   434       readExpression(is, expr);
   435       lp.obj(expr);
   436       if (sense == "min") {
   437         lp.min();
   438       } else {
   439         lp.max();
   440       }
   441       return is;
   442     }
   443 
   444     std::istream& readBounds(std::istream& is) {
   445       LpSolverBase::Col c;
   446       char x;
   447       is >> std::ws;
   448       if (!is.get(x)) {
   449         throw DataFormatError("Wrong variable bounds format");
   450       }
   451       if (varFirstChar(x)) {
   452         is.putback(x);
   453         readCol(is, c);
   454         is >> std::ws;
   455         Relation op1;
   456         readRelation(is, op1);
   457         double v;
   458         readNum(is, v);
   459         if (op1 == EQ) {
   460           lp.colLowerBound(c, v);
   461           lp.colUpperBound(c, v);
   462         } else if (op1 == LE) {
   463           lp.colUpperBound(c, v);
   464         } else {
   465           lp.colLowerBound(c, v);
   466         }
   467         is >> std::ws;
   468         if (is.get(x)) {
   469           throw DataFormatError("Wrong variable bounds format");
   470         }
   471       } else {
   472         is.putback(x);
   473         double v;
   474         readNum(is, v);
   475         is >> std::ws;
   476         Relation op1;
   477         readRelation(is, op1);
   478         is >> std::ws;
   479         readCol(is, c);
   480         is >> std::ws;
   481         if (is.get(x)) {
   482           is.putback(x);
   483           is >> std::ws;
   484           Relation op2;
   485           readRelation(is, op2);
   486           double w;
   487           is >> std::ws;
   488           readNum(is, w);
   489           if (op2 != op1 || op1 == EQ) {
   490             throw DataFormatError("Wrong range format");
   491           }
   492           if (op1 == LE) {
   493             lp.colLowerBound(c, v);
   494             lp.colUpperBound(c, w);
   495           } else {
   496             lp.colLowerBound(c, w);
   497             lp.colUpperBound(c, v);
   498           }
   499           is >> std::ws;
   500           if (is.get(x)) {
   501             throw DataFormatError("Wrong variable bounds format");
   502           }
   503         } else {
   504           if (op1 == EQ) {
   505             lp.colLowerBound(c, v);
   506             lp.colUpperBound(c, v);
   507           } else if (op1 == LE) {
   508             lp.colLowerBound(c, v);
   509           } else {
   510             lp.colUpperBound(c, v);
   511           }
   512         }
   513       }
   514       return is;
   515     }
   516 
   517     std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) {
   518       LpSolverBase::Col c;
   519       double d;
   520       char x;
   521       readElement(is, c, d);
   522       if (c != INVALID) {
   523         e += d * c;
   524       } else {
   525         e += d;
   526       }
   527       is >> std::ws;
   528       while (is.get(x) && (x == '+' || x == '-')) {
   529         is >> std::ws;
   530         readElement(is, c, d);
   531         if (c != INVALID) {
   532           e += (x == '+' ? d : -d) * c;
   533         } else {
   534           e += (x == '+' ? d : -d);
   535         }
   536         is >> std::ws;
   537       }
   538       if (!is) {
   539         is.clear();
   540       } else {
   541         is.putback(x);
   542       }
   543       return is;
   544     }
   545 
   546     std::istream& readElement(std::istream& is, 
   547                               LpSolverBase::Col& c, double& d) { 
   548       d = 1.0;
   549       c = INVALID;
   550       char x, y;
   551       if (!is.get(x)) throw DataFormatError("Cannot find lp element");
   552       if (x == '+' || x == '-') {
   553         is >> std::ws;
   554         d *= x == '-' ? -1 : 1;
   555         while (is.get(x) && (x == '+' || x == '-')) {
   556           d *= x == '-' ? -1 : 1;
   557           is >> std::ws;
   558         }
   559         if (!is) throw DataFormatError("Cannot find lp element");
   560       }
   561       if (numFirstChar(x)) {
   562         is.putback(x);
   563         double e;
   564         readNum(is, e);
   565         d *= e;
   566       } else if (varFirstChar(x)) {
   567         is.putback(x);
   568         LpSolverBase::Col f;
   569         readCol(is, f);
   570         c = f;
   571       } else {
   572         throw DataFormatError("Invalid expression format");          
   573       }
   574       is >> std::ws;
   575       while (is.get(y) && (y == '*' || y == '/')) {
   576         is >> std::ws;
   577         if (!is.get(x)) throw DataFormatError("Cannot find lp element");
   578         if (x == '+' || x == '-') {
   579           is >> std::ws;
   580           d *= x == '-' ? -1 : 1;
   581           while (is.get(x) && (x == '+' || x == '-')) {
   582             d *= x == '-' ? -1 : 1;
   583             is >> std::ws;
   584           }
   585           if (!is) throw DataFormatError("Cannot find lp element");
   586         }
   587         if (numFirstChar(x)) {
   588           is.putback(x);
   589           double e;
   590           readNum(is, e);
   591           if (y == '*') {
   592             d *= e;
   593           } else {
   594             d /= e;
   595           }
   596         } else if (varFirstChar(x)) {
   597           is.putback(x);
   598           LpSolverBase::Col f;
   599           readCol(is, f);
   600           if (y == '*') {
   601             if (c == INVALID) {
   602               c = f;
   603             } else {
   604               throw DataFormatError("Quadratic element in expression");
   605             }
   606           } else {
   607             throw DataFormatError("Division by variable");
   608           }
   609         } else {
   610           throw DataFormatError("Invalid expression format");          
   611         }
   612         is >> std::ws;
   613       }
   614       if (!is) {
   615         is.clear();
   616       } else {
   617         is.putback(y);
   618       }
   619       return is;
   620     }
   621 
   622     std::istream& readCol(std::istream& is, LpSolverBase::Col& c) {
   623       char x;
   624       std::string var;
   625       while (is.get(x) && varChar(x)) {
   626         var += x;
   627       }
   628       if (!is) {
   629         is.clear();
   630       } else {
   631         is.putback(x);
   632       }
   633       c = lp.colByName(var);
   634       if (c == INVALID) {
   635         c = lp.addCol();
   636         lp.colName(c, var);
   637       }
   638       return is;
   639     }
   640 
   641     std::istream& readNum(std::istream& is, double& d) {
   642       is >> d;
   643       if (!is) throw DataFormatError("Wrong number format");
   644       return is;
   645     }
   646 
   647     std::istream& readRelation(std::istream& is, Relation& op) {
   648       char x, y;
   649       if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
   650         throw DataFormatError("Wrong relation operator");
   651       }
   652       if (!is.get(y) || y != '=') {
   653         throw DataFormatError("Wrong relation operator");
   654       }
   655       switch (x) {
   656       case '<': op = LE; 
   657         break;
   658       case '=': op = EQ; 
   659         break;
   660       case '>': op = GE; 
   661         break;
   662       }
   663       return is;
   664     }
   665 
   666     static bool relationFirstChar(char c) {
   667       return c == '<' || c == '=' || c == '>';
   668     }
   669 
   670     static bool varFirstChar(char c) {
   671       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
   672     }
   673 
   674     static bool numFirstChar(char c) {
   675       return (c >= '0' && c <= '9') || c == '.';
   676     }
   677     
   678     static bool varChar(char c) {
   679       return (c >= '0' && c <= '9') || 
   680         (c >= 'a' && c <= 'z') || 
   681         (c >= 'A' && c <= 'Z') ||
   682         c == '[' || c == ']';
   683     }
   684     
   685   protected:
   686 
   687     /// \brief Reader function of the section.
   688     ///
   689     /// It reads the content of the section.
   690     virtual void read(std::istream& is) {
   691       std::string line;
   692       Part part = CONSTRAINTS;
   693       while (getline(is, line)) {
   694         std::istringstream ls(line);
   695         std::string type;
   696         ls >> type;
   697         if (type == "constraints") {
   698           part = CONSTRAINTS;
   699           ls >> std::ws;
   700           char x;
   701           if (ls.get(x))
   702             throw DataFormatError("Wrong Lp format");
   703         } else if (type == "bounds") {
   704           part = BOUNDS;
   705           ls >> std::ws;
   706           char x;
   707           if (ls.get(x))
   708             throw DataFormatError("Wrong Lp format");
   709         } else if (type == "objective") {
   710           part = OBJECTIVE;
   711           ls >> std::ws;
   712           char x;
   713           if (ls.get(x))
   714             throw DataFormatError("Wrong Lp format");
   715         } else {
   716           ls.str(line);
   717           switch (part) {
   718           case CONSTRAINTS:
   719             readConstraint(ls);
   720             break;
   721           case BOUNDS:
   722             readBounds(ls);
   723             break;
   724           case OBJECTIVE:
   725             readObjective(ls);
   726             break;
   727           }
   728         }
   729       }
   730     }
   731       
   732     virtual void missing() {
   733       ErrorMessage msg;
   734       msg << "Lp section not found in file: @lp " << name;
   735       throw IoParameterError(msg.message());
   736     }
   737 
   738   private:
   739 
   740       
   741     LpSolverBase& lp;
   742     std::string name;
   743   };
   744 
   745 
   746   /// \ingroup gen_opt_tools
   747   ///
   748   /// \brief Lp section writer for lemon IO
   749   ///
   750   /// This section reader provides an easy way to write an Lp problem
   751   /// to a file. The lemon lp section format contains three parts.
   752   ///
   753   ///\code
   754   /// @lp
   755   /// constraints
   756   ///   7 == x1 - 1 * x2
   757   ///   2 * x1 + x3 / 2 <= 7
   758   ///   x2 + -3 * x3 >= 8
   759   ///   3 <= x2 - 2 * x1 <= 8
   760   /// bounds
   761   ///   x1 >= 3
   762   ///   2 <= x2 <= 5
   763   ///   0 <= x3 <= 8
   764   /// objective
   765   ///   min x1 + 2 * x2 - x3
   766   ///\endcode
   767   ///
   768   /// The first part gives the constraints to the lp. The constraints
   769   /// could be equality, lower bound, upper bound or range for an
   770   /// expression or equality, less or more for two expressions.
   771   ///
   772   /// The second part gives the bounds for the variables. The bounds
   773   /// could be the same as for the single expression so equality,
   774   /// lower bound, upper bound or range.
   775   ///
   776   /// The third part is the objective function, it should start with
   777   /// \c min or \c max and then a valid linear expression.
   778   ///
   779   /// If an LP variable does not have name in the writer, then it will
   780   /// automatically created in the writer. This make a slight change
   781   /// in the \c const variable.
   782   ///
   783   /// The basic way to write an LP problem is made by the next code:
   784   ///\code
   785   /// Lp lp;
   786   ///
   787   /// LemonWriter writer(filename_or_ostream);
   788   /// LpWriter lpwriter(writer, lp);
   789   /// 
   790   /// writer.run();
   791   ///\endcode
   792   ///
   793   /// Of course instead of \c LemonWriter you can give a \c
   794   /// GraphWriter to the LpWriter constructor. Also useful that you
   795   /// can mix lp problems and graphs in the same file.
   796   class LpWriter : public LemonWriter::SectionWriter {
   797     typedef LemonWriter::SectionWriter Parent;
   798   public:
   799 
   800 
   801     /// \brief Constructor.
   802     ///
   803     /// Constructor for LpWriter. It creates the LpWriter and attach
   804     /// it into the given LemonWriter.
   805     LpWriter(LemonWriter& _writer, const LpSolverBase& _lp, 
   806              const std::string& _name = std::string())
   807       : Parent(_writer), lp(_lp), name(_name) {} 
   808 
   809 
   810     /// \brief Destructor.
   811     ///
   812     /// Destructor for LpWriter.
   813     virtual ~LpWriter() {}
   814 
   815   private:
   816     LpWriter(const LpWriter&);
   817     void operator=(const LpWriter&);
   818   
   819   protected:
   820 
   821     /// \brief Gives back true when the SectionWriter can process 
   822     /// the section with the given header line.
   823     ///
   824     /// It gives back the header line of the \c \@lp section.
   825     virtual std::string header() {
   826       std::ostringstream ls;
   827       ls << "@lp " << name;
   828       return ls.str();
   829     }
   830 
   831   private:
   832 
   833     void createCols() {
   834       int num = 0;
   835 
   836       for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) {
   837         std::string name = lp.colName(it);
   838         if (name.empty()) {
   839           std::ostringstream ls;
   840           ls << "x" << num;
   841           ++num;
   842           while (lp.colByName(ls.str()) != INVALID) {
   843             ls.str(std::string());
   844             ls << "x" << num;
   845             ++num;
   846           }
   847           const_cast<LpSolverBase&>(lp).colName(it, ls.str());
   848         }
   849       }
   850     }
   851     
   852     void writeExpression(std::ostream& os, const LpSolverBase::Expr& e) {
   853       bool first = true;
   854       for (LpSolverBase::Expr::const_iterator jt = e.begin(); 
   855            jt != e.end(); ++jt) {
   856         if (jt->second < 0.0) {
   857           os << "- ";
   858           if (jt->second != -1.0) {
   859             os << -jt->second << " * ";
   860           }
   861         } else if (jt->second > 0.0) {
   862           if (!first) {
   863             os << "+ ";
   864           }
   865           if (jt->second != 1.0) {
   866             os << jt->second << " * ";
   867           }          
   868         }
   869         first = false;
   870         os << lp.colName(jt->first) << " ";
   871       }
   872       if (e.constComp() < 0.0) {
   873         os << "- " << -e.constComp() << " ";
   874       } else if (e.constComp() > 0.0) {
   875         if (!first) {
   876           os << "+ ";
   877         }
   878         os << e.constComp() << " ";
   879       } 
   880     }
   881 
   882   protected:
   883 
   884     /// \brief Writer function of the section.
   885     ///
   886     /// It writes the content of the section.
   887     virtual void write(std::ostream& os) {
   888       createCols();
   889 
   890       os << "constraints" << std::endl;
   891       
   892       for (LpSolverBase::RowIt it(lp); it != INVALID; ++it) {
   893         double lower, upper;
   894         lp.getRowBounds(it, lower, upper);
   895         if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF) 
   896           continue;
   897         os << "   ";
   898         
   899         if (lower != upper) {
   900           if (lower != -LpSolverBase::INF) {
   901             os << lower << " <= ";
   902           }
   903           
   904           writeExpression(os, lp.row(it));
   905           
   906           if (upper != LpSolverBase::INF) {
   907             os << "<= " << upper << " ";
   908           }
   909         } else {
   910 
   911           writeExpression(os, lp.row(it));
   912           os << "== " << upper << " ";
   913           
   914         }
   915 
   916         os << std::endl;
   917       }
   918 
   919       os << "bounds" << std::endl;
   920       
   921       for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) {
   922         double lower = lp.colLowerBound(it);
   923         double upper = lp.colUpperBound(it);
   924         if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF) 
   925           continue;
   926         os << "   ";
   927 
   928         if (lower != upper) {
   929           if (lower != -LpSolverBase::INF) {
   930             os << lower << " <= ";
   931           }
   932           
   933           os << lp.colName(it) << " ";
   934           
   935           if (upper != LpSolverBase::INF) {
   936             os << "<= " << upper << " ";
   937           }
   938         } else {
   939           os << lp.colName(it) << " == " << upper << " ";
   940         }
   941         os << std::endl;
   942       }
   943 
   944       os << "objective" << std::endl;
   945       os << "   ";
   946       if (lp.isMin()) {
   947         os << "min ";
   948       } else {
   949         os << "max ";
   950       }
   951       writeExpression(os, lp.obj());
   952       os << std::endl;
   953     }
   954       
   955 
   956   private:
   957 
   958     const LpSolverBase& lp;
   959     std::string name;
   960   };
   961 
   962 }
   963 
   964 #endif