COIN-OR::LEMON - Graph Library

source: lemon-0.x/lemon/lp_utils.h @ 2368:6b2e8b734ae7

Last change on this file since 2368:6b2e8b734ae7 was 2368:6b2e8b734ae7, checked in by Balazs Dezso, 17 years ago

Bug fixes
Documentation

File size: 24.7 KB
Line 
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
31namespace 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.setObj(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.is_min()) {
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
Note: See TracBrowser for help on using the repository browser.