deba@2316
|
1 |
/* -*- C++ -*-
|
deba@2316
|
2 |
*
|
deba@2316
|
3 |
* This file is a part of LEMON, a generic C++ optimization library
|
deba@2316
|
4 |
*
|
deba@2316
|
5 |
* Copyright (C) 2003-2006
|
deba@2316
|
6 |
* Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
|
deba@2316
|
7 |
* (Egervary Research Group on Combinatorial Optimization, EGRES).
|
deba@2316
|
8 |
*
|
deba@2316
|
9 |
* Permission to use, modify and distribute this software is granted
|
deba@2316
|
10 |
* provided that this copyright notice appears in all copies. For
|
deba@2316
|
11 |
* precise terms see the accompanying LICENSE file.
|
deba@2316
|
12 |
*
|
deba@2316
|
13 |
* This software is provided "AS IS" with no warranty of any kind,
|
deba@2316
|
14 |
* express or implied, and with no claim as to its suitability for any
|
deba@2316
|
15 |
* purpose.
|
deba@2316
|
16 |
*
|
deba@2316
|
17 |
*/
|
deba@2316
|
18 |
|
deba@2316
|
19 |
#ifndef LEMON_LP_UTILS_H
|
deba@2316
|
20 |
#define LEMON_LP_UTILS_H
|
deba@2316
|
21 |
|
deba@2316
|
22 |
#include <lemon/lp_base.h>
|
deba@2316
|
23 |
|
deba@2316
|
24 |
#include <lemon/lemon_reader.h>
|
deba@2363
|
25 |
#include <lemon/lemon_writer.h>
|
deba@2316
|
26 |
|
deba@2316
|
27 |
namespace lemon {
|
deba@2316
|
28 |
|
deba@2316
|
29 |
class LpReader : public LemonReader::SectionReader {
|
deba@2316
|
30 |
typedef LemonReader::SectionReader Parent;
|
deba@2316
|
31 |
public:
|
deba@2316
|
32 |
|
deba@2316
|
33 |
|
deba@2316
|
34 |
/// \brief Constructor.
|
deba@2316
|
35 |
///
|
deba@2316
|
36 |
/// Constructor for LpReader. It creates the LpReader and attach
|
deba@2316
|
37 |
/// it into the given LemonReader. The lp reader will add
|
deba@2316
|
38 |
/// variables, constraints and objective function to the
|
deba@2316
|
39 |
/// given lp solver.
|
deba@2316
|
40 |
LpReader(LemonReader& _reader, LpSolverBase& _lp,
|
deba@2316
|
41 |
const std::string& _name = std::string())
|
deba@2316
|
42 |
: Parent(_reader), lp(_lp), name(_name) {}
|
deba@2316
|
43 |
|
deba@2316
|
44 |
|
deba@2316
|
45 |
/// \brief Destructor.
|
deba@2316
|
46 |
///
|
deba@2316
|
47 |
/// Destructor for NodeSetReader.
|
deba@2316
|
48 |
virtual ~LpReader() {}
|
deba@2316
|
49 |
|
deba@2316
|
50 |
private:
|
deba@2316
|
51 |
LpReader(const LpReader&);
|
deba@2316
|
52 |
void operator=(const LpReader&);
|
deba@2316
|
53 |
|
deba@2316
|
54 |
protected:
|
deba@2316
|
55 |
|
deba@2316
|
56 |
/// \brief Gives back true when the SectionReader can process
|
deba@2316
|
57 |
/// the section with the given header line.
|
deba@2316
|
58 |
///
|
deba@2316
|
59 |
/// It gives back true when the header line starts with \c \@lp,
|
deba@2316
|
60 |
/// and the header line's name and the nodeset's name are the same.
|
deba@2316
|
61 |
virtual bool header(const std::string& line) {
|
deba@2316
|
62 |
std::istringstream ls(line);
|
deba@2316
|
63 |
std::string command;
|
deba@2316
|
64 |
std::string id;
|
deba@2316
|
65 |
ls >> command >> id;
|
deba@2316
|
66 |
return command == "@lp" && name == id;
|
deba@2316
|
67 |
}
|
deba@2316
|
68 |
|
deba@2316
|
69 |
private:
|
deba@2316
|
70 |
|
deba@2364
|
71 |
enum SubSection {
|
deba@2364
|
72 |
CONSTRAINTS, BOUNDS, OBJECTIVE
|
deba@2364
|
73 |
};
|
deba@2364
|
74 |
|
deba@2316
|
75 |
enum Relation {
|
deba@2316
|
76 |
LE, EQ, GE
|
deba@2316
|
77 |
};
|
deba@2316
|
78 |
|
deba@2364
|
79 |
std::istream& readConstraint(std::istream& is) {
|
deba@2364
|
80 |
LpSolverBase::Constr c;
|
deba@2316
|
81 |
char x;
|
deba@2316
|
82 |
LpSolverBase::Expr e1, e2;
|
deba@2316
|
83 |
Relation op1;
|
deba@2316
|
84 |
is >> std::ws;
|
deba@2316
|
85 |
readExpression(is, e1);
|
deba@2316
|
86 |
is >> std::ws;
|
deba@2316
|
87 |
readRelation(is, op1);
|
deba@2316
|
88 |
is >> std::ws;
|
deba@2316
|
89 |
readExpression(is, e2);
|
deba@2364
|
90 |
is >> std::ws;
|
deba@2316
|
91 |
if (!is.get(x)) {
|
deba@2316
|
92 |
if (op1 == LE) {
|
deba@2364
|
93 |
if (e1.size() == 0) {
|
deba@2364
|
94 |
c = e2 >= e1;
|
deba@2364
|
95 |
} else {
|
deba@2364
|
96 |
c = e1 <= e2;
|
deba@2364
|
97 |
}
|
deba@2316
|
98 |
c = e1 <= e2;
|
deba@2316
|
99 |
} else if (op1 == GE) {
|
deba@2364
|
100 |
if (e1.size() == 0) {
|
deba@2364
|
101 |
c = e2 <= e1;
|
deba@2364
|
102 |
} else {
|
deba@2364
|
103 |
c = e1 >= e2;
|
deba@2364
|
104 |
}
|
deba@2316
|
105 |
} else {
|
deba@2364
|
106 |
if (e1.size() == 0) {
|
deba@2364
|
107 |
c = e2 == e1;
|
deba@2364
|
108 |
} else {
|
deba@2364
|
109 |
c = e1 == e2;
|
deba@2364
|
110 |
}
|
deba@2316
|
111 |
}
|
deba@2316
|
112 |
} else {
|
deba@2316
|
113 |
is.putback(x);
|
deba@2316
|
114 |
LpSolverBase::Expr e3;
|
deba@2316
|
115 |
Relation op2;
|
deba@2316
|
116 |
readRelation(is, op2);
|
deba@2316
|
117 |
is >> std::ws;
|
deba@2316
|
118 |
readExpression(is, e3);
|
deba@2316
|
119 |
if (!e1.empty() || !e3.empty()) {
|
deba@2316
|
120 |
throw DataFormatError("Wrong range format");
|
deba@2316
|
121 |
}
|
deba@2316
|
122 |
if (op2 != op1 || op1 == EQ) {
|
deba@2316
|
123 |
throw DataFormatError("Wrong range format");
|
deba@2316
|
124 |
}
|
deba@2316
|
125 |
if (op1 == LE) {
|
deba@2316
|
126 |
c = e1.constComp() <= e2 <= e3.constComp();
|
deba@2316
|
127 |
} else {
|
deba@2316
|
128 |
c = e1.constComp() >= e2 >= e3.constComp();
|
deba@2316
|
129 |
}
|
deba@2364
|
130 |
is >> std::ws;
|
deba@2364
|
131 |
if (is.get(x)) {
|
deba@2364
|
132 |
throw DataFormatError("Wrong variable bounds format");
|
deba@2364
|
133 |
}
|
deba@2316
|
134 |
}
|
deba@2364
|
135 |
lp.addRow(c);
|
deba@2364
|
136 |
return is;
|
deba@2364
|
137 |
}
|
deba@2364
|
138 |
|
deba@2364
|
139 |
std::istream& readObjective(std::istream& is) {
|
deba@2364
|
140 |
is >> std::ws;
|
deba@2364
|
141 |
std::string sense;
|
deba@2364
|
142 |
is >> sense;
|
deba@2364
|
143 |
if (sense != "min" && sense != "max") {
|
deba@2364
|
144 |
throw DataFormatError("Wrong objective function format");
|
deba@2364
|
145 |
}
|
deba@2364
|
146 |
LpSolverBase::Expr expr;
|
deba@2364
|
147 |
is >> std::ws;
|
deba@2364
|
148 |
readExpression(is, expr);
|
deba@2364
|
149 |
lp.setObj(expr);
|
deba@2364
|
150 |
if (sense == "min") {
|
deba@2364
|
151 |
lp.min();
|
deba@2364
|
152 |
} else {
|
deba@2364
|
153 |
lp.max();
|
deba@2364
|
154 |
}
|
deba@2364
|
155 |
return is;
|
deba@2364
|
156 |
}
|
deba@2364
|
157 |
|
deba@2364
|
158 |
std::istream& readBounds(std::istream& is) {
|
deba@2364
|
159 |
LpSolverBase::Col c;
|
deba@2364
|
160 |
char x;
|
deba@2364
|
161 |
is >> std::ws;
|
deba@2364
|
162 |
if (!is.get(x)) {
|
deba@2364
|
163 |
throw DataFormatError("Wrong variable bounds format");
|
deba@2364
|
164 |
}
|
deba@2364
|
165 |
if (varFirstChar(x)) {
|
deba@2364
|
166 |
is.putback(x);
|
deba@2364
|
167 |
readCol(is, c);
|
deba@2364
|
168 |
is >> std::ws;
|
deba@2364
|
169 |
Relation op1;
|
deba@2364
|
170 |
readRelation(is, op1);
|
deba@2364
|
171 |
double v;
|
deba@2364
|
172 |
readNum(is, v);
|
deba@2364
|
173 |
if (op1 == EQ) {
|
deba@2364
|
174 |
lp.colLowerBound(c, v);
|
deba@2364
|
175 |
lp.colUpperBound(c, v);
|
deba@2364
|
176 |
} else if (op1 == LE) {
|
deba@2364
|
177 |
lp.colUpperBound(c, v);
|
deba@2364
|
178 |
} else {
|
deba@2364
|
179 |
lp.colLowerBound(c, v);
|
deba@2364
|
180 |
}
|
deba@2364
|
181 |
is >> std::ws;
|
deba@2364
|
182 |
if (is.get(x)) {
|
deba@2364
|
183 |
throw DataFormatError("Wrong variable bounds format");
|
deba@2364
|
184 |
}
|
deba@2364
|
185 |
} else {
|
deba@2364
|
186 |
is.putback(x);
|
deba@2364
|
187 |
double v;
|
deba@2364
|
188 |
readNum(is, v);
|
deba@2364
|
189 |
is >> std::ws;
|
deba@2364
|
190 |
Relation op1;
|
deba@2364
|
191 |
readRelation(is, op1);
|
deba@2364
|
192 |
is >> std::ws;
|
deba@2364
|
193 |
readCol(is, c);
|
deba@2364
|
194 |
is >> std::ws;
|
deba@2364
|
195 |
if (is.get(x)) {
|
deba@2364
|
196 |
is.putback(x);
|
deba@2364
|
197 |
is >> std::ws;
|
deba@2364
|
198 |
Relation op2;
|
deba@2364
|
199 |
readRelation(is, op2);
|
deba@2364
|
200 |
double w;
|
deba@2364
|
201 |
is >> std::ws;
|
deba@2364
|
202 |
readNum(is, w);
|
deba@2364
|
203 |
if (op2 != op1 || op1 == EQ) {
|
deba@2364
|
204 |
throw DataFormatError("Wrong range format");
|
deba@2364
|
205 |
}
|
deba@2364
|
206 |
if (op1 == LE) {
|
deba@2364
|
207 |
lp.colLowerBound(c, v);
|
deba@2364
|
208 |
lp.colUpperBound(c, w);
|
deba@2364
|
209 |
} else {
|
deba@2364
|
210 |
lp.colLowerBound(c, w);
|
deba@2364
|
211 |
lp.colUpperBound(c, v);
|
deba@2364
|
212 |
}
|
deba@2364
|
213 |
if (is.get(x)) {
|
deba@2364
|
214 |
throw DataFormatError("Wrong variable bounds format");
|
deba@2364
|
215 |
}
|
deba@2364
|
216 |
} else {
|
deba@2364
|
217 |
if (op1 == EQ) {
|
deba@2364
|
218 |
lp.colLowerBound(c, v);
|
deba@2364
|
219 |
lp.colUpperBound(c, v);
|
deba@2364
|
220 |
} else if (op1 == LE) {
|
deba@2364
|
221 |
lp.colLowerBound(c, v);
|
deba@2364
|
222 |
} else {
|
deba@2364
|
223 |
lp.colUpperBound(c, v);
|
deba@2364
|
224 |
}
|
deba@2364
|
225 |
}
|
deba@2364
|
226 |
}
|
deba@2364
|
227 |
return is;
|
deba@2316
|
228 |
}
|
deba@2316
|
229 |
|
deba@2316
|
230 |
std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) {
|
deba@2316
|
231 |
LpSolverBase::Col c;
|
deba@2316
|
232 |
double d;
|
deba@2316
|
233 |
char x;
|
deba@2316
|
234 |
readElement(is, c, d);
|
deba@2316
|
235 |
if (c != INVALID) {
|
deba@2316
|
236 |
e += d * c;
|
deba@2316
|
237 |
} else {
|
deba@2316
|
238 |
e += d;
|
deba@2316
|
239 |
}
|
deba@2316
|
240 |
is >> std::ws;
|
deba@2316
|
241 |
while (is.get(x) && (x == '+' || x == '-')) {
|
deba@2316
|
242 |
is >> std::ws;
|
deba@2316
|
243 |
readElement(is, c, d);
|
deba@2316
|
244 |
if (c != INVALID) {
|
deba@2316
|
245 |
e += (x == '+' ? d : -d) * c;
|
deba@2316
|
246 |
} else {
|
deba@2316
|
247 |
e += (x == '+' ? d : -d);
|
deba@2316
|
248 |
}
|
deba@2316
|
249 |
is >> std::ws;
|
deba@2316
|
250 |
}
|
deba@2316
|
251 |
if (!is) {
|
deba@2316
|
252 |
is.clear();
|
deba@2316
|
253 |
} else {
|
deba@2316
|
254 |
is.putback(x);
|
deba@2316
|
255 |
}
|
deba@2316
|
256 |
return is;
|
deba@2316
|
257 |
}
|
deba@2316
|
258 |
|
deba@2316
|
259 |
std::istream& readElement(std::istream& is,
|
deba@2316
|
260 |
LpSolverBase::Col& c, double& d) {
|
deba@2316
|
261 |
d = 1.0;
|
deba@2316
|
262 |
c = INVALID;
|
deba@2316
|
263 |
char x, y;
|
deba@2316
|
264 |
if (!is.get(x)) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
265 |
if (x == '+' || x == '-') {
|
deba@2316
|
266 |
is >> std::ws;
|
deba@2316
|
267 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
268 |
while (is.get(x) && (x == '+' || x == '-')) {
|
deba@2316
|
269 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
270 |
is >> std::ws;
|
deba@2316
|
271 |
}
|
deba@2316
|
272 |
if (!is) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
273 |
}
|
deba@2316
|
274 |
if (numFirstChar(x)) {
|
deba@2316
|
275 |
is.putback(x);
|
deba@2316
|
276 |
double e;
|
deba@2316
|
277 |
readNum(is, e);
|
deba@2316
|
278 |
d *= e;
|
deba@2316
|
279 |
} else if (varFirstChar(x)) {
|
deba@2316
|
280 |
is.putback(x);
|
deba@2316
|
281 |
LpSolverBase::Col f;
|
deba@2316
|
282 |
readCol(is, f);
|
deba@2316
|
283 |
c = f;
|
deba@2316
|
284 |
} else {
|
deba@2316
|
285 |
throw DataFormatError("Invalid expression format");
|
deba@2316
|
286 |
}
|
deba@2316
|
287 |
is >> std::ws;
|
deba@2316
|
288 |
while (is.get(y) && (y == '*' || y == '/')) {
|
deba@2316
|
289 |
is >> std::ws;
|
deba@2316
|
290 |
if (!is.get(x)) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
291 |
if (x == '+' || x == '-') {
|
deba@2316
|
292 |
is >> std::ws;
|
deba@2316
|
293 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
294 |
while (is.get(x) && (x == '+' || x == '-')) {
|
deba@2316
|
295 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
296 |
is >> std::ws;
|
deba@2316
|
297 |
}
|
deba@2316
|
298 |
if (!is) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
299 |
}
|
deba@2316
|
300 |
if (numFirstChar(x)) {
|
deba@2316
|
301 |
is.putback(x);
|
deba@2316
|
302 |
double e;
|
deba@2316
|
303 |
readNum(is, e);
|
deba@2316
|
304 |
if (y == '*') {
|
deba@2316
|
305 |
d *= e;
|
deba@2316
|
306 |
} else {
|
deba@2316
|
307 |
d /= e;
|
deba@2316
|
308 |
}
|
deba@2316
|
309 |
} else if (varFirstChar(x)) {
|
deba@2316
|
310 |
is.putback(x);
|
deba@2316
|
311 |
LpSolverBase::Col f;
|
deba@2316
|
312 |
readCol(is, f);
|
deba@2316
|
313 |
if (y == '*') {
|
deba@2316
|
314 |
if (c == INVALID) {
|
deba@2316
|
315 |
c = f;
|
deba@2316
|
316 |
} else {
|
deba@2316
|
317 |
throw DataFormatError("Quadratic element in expression");
|
deba@2316
|
318 |
}
|
deba@2316
|
319 |
} else {
|
deba@2316
|
320 |
throw DataFormatError("Division by variable");
|
deba@2316
|
321 |
}
|
deba@2316
|
322 |
} else {
|
deba@2316
|
323 |
throw DataFormatError("Invalid expression format");
|
deba@2316
|
324 |
}
|
deba@2316
|
325 |
is >> std::ws;
|
deba@2316
|
326 |
}
|
deba@2316
|
327 |
if (!is) {
|
deba@2316
|
328 |
is.clear();
|
deba@2316
|
329 |
} else {
|
deba@2316
|
330 |
is.putback(y);
|
deba@2316
|
331 |
}
|
deba@2316
|
332 |
return is;
|
deba@2316
|
333 |
}
|
deba@2316
|
334 |
|
deba@2316
|
335 |
std::istream& readCol(std::istream& is, LpSolverBase::Col& c) {
|
deba@2316
|
336 |
char x;
|
deba@2316
|
337 |
std::string var;
|
deba@2316
|
338 |
while (is.get(x) && varChar(x)) {
|
deba@2316
|
339 |
var += x;
|
deba@2316
|
340 |
}
|
deba@2316
|
341 |
if (!is) {
|
deba@2316
|
342 |
is.clear();
|
deba@2316
|
343 |
} else {
|
deba@2316
|
344 |
is.putback(x);
|
deba@2316
|
345 |
}
|
deba@2316
|
346 |
ColMap::const_iterator it = cols.find(var);
|
deba@2316
|
347 |
if (cols.find(var) != cols.end()) {
|
deba@2316
|
348 |
c = it->second;
|
deba@2316
|
349 |
} else {
|
deba@2316
|
350 |
c = lp.addCol();
|
deba@2316
|
351 |
cols.insert(std::make_pair(var, c));
|
deba@2316
|
352 |
lp.colName(c, var);
|
deba@2316
|
353 |
}
|
deba@2316
|
354 |
return is;
|
deba@2316
|
355 |
}
|
deba@2316
|
356 |
|
deba@2316
|
357 |
std::istream& readNum(std::istream& is, double& d) {
|
deba@2316
|
358 |
is >> d;
|
deba@2316
|
359 |
if (!is) throw DataFormatError("Wrong number format");
|
deba@2316
|
360 |
return is;
|
deba@2316
|
361 |
}
|
deba@2316
|
362 |
|
deba@2316
|
363 |
std::istream& readRelation(std::istream& is, Relation& op) {
|
deba@2316
|
364 |
char x, y;
|
deba@2316
|
365 |
if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
|
deba@2316
|
366 |
throw DataFormatError("Wrong relation operator");
|
deba@2316
|
367 |
}
|
deba@2316
|
368 |
if (!is.get(y) || y != '=') {
|
deba@2316
|
369 |
throw DataFormatError("Wrong relation operator");
|
deba@2316
|
370 |
}
|
deba@2316
|
371 |
switch (x) {
|
deba@2316
|
372 |
case '<': op = LE;
|
deba@2316
|
373 |
break;
|
deba@2316
|
374 |
case '=': op = EQ;
|
deba@2316
|
375 |
break;
|
deba@2316
|
376 |
case '>': op = GE;
|
deba@2316
|
377 |
break;
|
deba@2316
|
378 |
}
|
deba@2316
|
379 |
return is;
|
deba@2316
|
380 |
}
|
deba@2316
|
381 |
|
deba@2316
|
382 |
static bool relationFirstChar(char c) {
|
deba@2316
|
383 |
return c == '<' || c == '=' || c == '>';
|
deba@2316
|
384 |
}
|
deba@2316
|
385 |
|
deba@2316
|
386 |
static bool varFirstChar(char c) {
|
deba@2316
|
387 |
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
deba@2316
|
388 |
}
|
deba@2316
|
389 |
|
deba@2316
|
390 |
static bool numFirstChar(char c) {
|
deba@2316
|
391 |
return (c >= '0' && c <= '9') || c == '.';
|
deba@2316
|
392 |
}
|
deba@2316
|
393 |
|
deba@2316
|
394 |
static bool varChar(char c) {
|
deba@2316
|
395 |
return (c >= '0' && c <= '9') ||
|
deba@2364
|
396 |
(c >= 'a' && c <= 'z') ||
|
deba@2364
|
397 |
(c >= 'A' && c <= 'Z') ||
|
deba@2364
|
398 |
c == '[' || c == ']';
|
deba@2316
|
399 |
}
|
deba@2316
|
400 |
|
deba@2316
|
401 |
protected:
|
deba@2316
|
402 |
|
deba@2316
|
403 |
/// \brief Reader function of the section.
|
deba@2316
|
404 |
///
|
deba@2316
|
405 |
/// It reads the content of the section.
|
deba@2316
|
406 |
virtual void read(std::istream& is) {
|
deba@2316
|
407 |
std::string line;
|
deba@2364
|
408 |
SubSection sub = CONSTRAINTS;
|
deba@2316
|
409 |
while (getline(is, line)) {
|
deba@2316
|
410 |
std::istringstream ls(line);
|
deba@2364
|
411 |
std::string type;
|
deba@2364
|
412 |
ls >> type;
|
deba@2364
|
413 |
if (type == "constraints") {
|
deba@2364
|
414 |
sub = CONSTRAINTS;
|
deba@2364
|
415 |
} else if (type == "bounds") {
|
deba@2364
|
416 |
sub = BOUNDS;
|
deba@2364
|
417 |
} else if (type == "objective") {
|
deba@2364
|
418 |
sub = OBJECTIVE;
|
deba@2316
|
419 |
} else {
|
deba@2316
|
420 |
ls.str(line);
|
deba@2364
|
421 |
switch (sub) {
|
deba@2364
|
422 |
case CONSTRAINTS:
|
deba@2364
|
423 |
readConstraint(ls);
|
deba@2364
|
424 |
break;
|
deba@2364
|
425 |
case BOUNDS:
|
deba@2364
|
426 |
readBounds(ls);
|
deba@2364
|
427 |
break;
|
deba@2364
|
428 |
case OBJECTIVE:
|
deba@2364
|
429 |
readObjective(ls);
|
deba@2364
|
430 |
break;
|
deba@2364
|
431 |
}
|
deba@2316
|
432 |
}
|
deba@2316
|
433 |
}
|
deba@2316
|
434 |
}
|
deba@2316
|
435 |
|
deba@2316
|
436 |
virtual void missing() {
|
deba@2316
|
437 |
ErrorMessage msg;
|
deba@2316
|
438 |
msg << "Lp section not found in file: @lp " << name;
|
deba@2316
|
439 |
throw IoParameterError(msg.message());
|
deba@2316
|
440 |
}
|
deba@2316
|
441 |
|
deba@2316
|
442 |
private:
|
deba@2316
|
443 |
|
deba@2316
|
444 |
typedef std::map<std::string, LpSolverBase::Col> ColMap;
|
deba@2316
|
445 |
|
deba@2316
|
446 |
LpSolverBase& lp;
|
deba@2316
|
447 |
std::string name;
|
deba@2316
|
448 |
ColMap cols;
|
deba@2316
|
449 |
};
|
deba@2316
|
450 |
|
deba@2363
|
451 |
|
deba@2364
|
452 |
class LpWriter : public LemonWriter::SectionWriter {
|
deba@2364
|
453 |
typedef LemonWriter::SectionWriter Parent;
|
deba@2364
|
454 |
public:
|
deba@2363
|
455 |
|
deba@2363
|
456 |
|
deba@2364
|
457 |
/// \brief Constructor.
|
deba@2364
|
458 |
///
|
deba@2364
|
459 |
/// Constructor for LpWriter. It creates the LpWriter and attach
|
deba@2364
|
460 |
/// it into the given LemonWriter. The lp writer will add
|
deba@2364
|
461 |
/// variables, constraints and objective function to the
|
deba@2364
|
462 |
/// given lp solver.
|
deba@2364
|
463 |
LpWriter(LemonWriter& _writer, LpSolverBase& _lp,
|
deba@2364
|
464 |
const std::string& _name = std::string())
|
deba@2364
|
465 |
: Parent(_writer), lp(_lp), name(_name) {}
|
deba@2363
|
466 |
|
deba@2363
|
467 |
|
deba@2364
|
468 |
/// \brief Destructor.
|
deba@2364
|
469 |
///
|
deba@2364
|
470 |
/// Destructor for NodeSetWriter.
|
deba@2364
|
471 |
virtual ~LpWriter() {}
|
deba@2363
|
472 |
|
deba@2364
|
473 |
private:
|
deba@2364
|
474 |
LpWriter(const LpWriter&);
|
deba@2364
|
475 |
void operator=(const LpWriter&);
|
deba@2363
|
476 |
|
deba@2364
|
477 |
protected:
|
deba@2363
|
478 |
|
deba@2364
|
479 |
/// \brief Gives back true when the SectionWriter can process
|
deba@2364
|
480 |
/// the section with the given header line.
|
deba@2364
|
481 |
///
|
deba@2364
|
482 |
/// It gives back the header line of the \c \@lp section.
|
deba@2364
|
483 |
virtual std::string header() {
|
deba@2364
|
484 |
std::ostringstream ls;
|
deba@2364
|
485 |
ls << "@lp " << name;
|
deba@2364
|
486 |
return ls.str();
|
deba@2364
|
487 |
}
|
deba@2363
|
488 |
|
deba@2364
|
489 |
private:
|
deba@2363
|
490 |
|
deba@2364
|
491 |
void createCols() {
|
deba@2364
|
492 |
int num = 1;
|
deba@2363
|
493 |
|
deba@2364
|
494 |
std::map<std::string, LpSolverBase::Col> inverse;
|
deba@2364
|
495 |
|
deba@2364
|
496 |
for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) {
|
deba@2364
|
497 |
std::string name = lp.colName(it);
|
deba@2364
|
498 |
if (!name.empty() && inverse.find(name) == inverse.end()) {
|
deba@2364
|
499 |
cols.insert(std::make_pair(it, name));
|
deba@2364
|
500 |
inverse.insert(std::make_pair(name, it));
|
deba@2364
|
501 |
} else {
|
deba@2364
|
502 |
std::ostringstream ls;
|
deba@2364
|
503 |
ls << "var" << num;
|
deba@2364
|
504 |
++num;
|
deba@2364
|
505 |
cols.insert(std::make_pair(it, ls.str()));
|
deba@2364
|
506 |
inverse.insert(std::make_pair(ls.str(), it));
|
deba@2364
|
507 |
}
|
deba@2364
|
508 |
}
|
deba@2364
|
509 |
}
|
deba@2364
|
510 |
|
deba@2364
|
511 |
void writeExpression(std::ostream& os, const LpSolverBase::Expr& e) {
|
deba@2364
|
512 |
bool first = true;
|
deba@2364
|
513 |
for (LpSolverBase::Expr::const_iterator jt = e.begin();
|
deba@2364
|
514 |
jt != e.end(); ++jt) {
|
deba@2364
|
515 |
if (jt->second < 0.0) {
|
deba@2364
|
516 |
os << "- ";
|
deba@2364
|
517 |
if (jt->second != -1.0) {
|
deba@2364
|
518 |
os << -jt->second << " * ";
|
deba@2364
|
519 |
}
|
deba@2364
|
520 |
} else if (jt->second > 0.0) {
|
deba@2364
|
521 |
if (!first) {
|
deba@2364
|
522 |
os << "+ ";
|
deba@2364
|
523 |
}
|
deba@2364
|
524 |
if (jt->second != 1.0) {
|
deba@2364
|
525 |
os << jt->second << " * ";
|
deba@2364
|
526 |
}
|
deba@2364
|
527 |
}
|
deba@2364
|
528 |
first = false;
|
deba@2364
|
529 |
os << cols[jt->first] << " ";
|
deba@2364
|
530 |
}
|
deba@2364
|
531 |
if (e.constComp() < 0.0) {
|
deba@2364
|
532 |
os << "- " << -e.constComp() << " ";
|
deba@2364
|
533 |
} else if (e.constComp() > 0.0) {
|
deba@2364
|
534 |
if (!first) {
|
deba@2364
|
535 |
os << "+ ";
|
deba@2364
|
536 |
}
|
deba@2364
|
537 |
os << e.constComp() << " ";
|
deba@2364
|
538 |
}
|
deba@2364
|
539 |
}
|
deba@2364
|
540 |
|
deba@2364
|
541 |
protected:
|
deba@2364
|
542 |
|
deba@2364
|
543 |
/// \brief Writer function of the section.
|
deba@2364
|
544 |
///
|
deba@2364
|
545 |
/// It writes the content of the section.
|
deba@2364
|
546 |
virtual void write(std::ostream& os) {
|
deba@2364
|
547 |
createCols();
|
deba@2364
|
548 |
|
deba@2364
|
549 |
os << "constraints" << std::endl;
|
deba@2363
|
550 |
|
deba@2364
|
551 |
for (LpSolverBase::RowIt it(lp); it != INVALID; ++it) {
|
deba@2364
|
552 |
double lower, upper;
|
deba@2364
|
553 |
lp.getRowBounds(it, lower, upper);
|
deba@2364
|
554 |
if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF)
|
deba@2364
|
555 |
continue;
|
deba@2364
|
556 |
os << " ";
|
deba@2364
|
557 |
|
deba@2364
|
558 |
if (lower != upper) {
|
deba@2364
|
559 |
if (lower != -LpSolverBase::INF) {
|
deba@2364
|
560 |
os << lower << " <= ";
|
deba@2364
|
561 |
}
|
deba@2364
|
562 |
|
deba@2364
|
563 |
writeExpression(os, lp.row(it));
|
deba@2364
|
564 |
|
deba@2364
|
565 |
if (upper != LpSolverBase::INF) {
|
deba@2364
|
566 |
os << "<= " << upper << " ";
|
deba@2364
|
567 |
}
|
deba@2364
|
568 |
} else {
|
deba@2363
|
569 |
|
deba@2364
|
570 |
writeExpression(os, lp.row(it));
|
deba@2364
|
571 |
os << "== " << upper << " ";
|
deba@2364
|
572 |
|
deba@2364
|
573 |
}
|
deba@2363
|
574 |
|
deba@2364
|
575 |
os << std::endl;
|
deba@2364
|
576 |
}
|
deba@2363
|
577 |
|
deba@2364
|
578 |
os << "bounds" << std::endl;
|
deba@2364
|
579 |
|
deba@2364
|
580 |
for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) {
|
deba@2364
|
581 |
double lower = lp.colLowerBound(it);
|
deba@2364
|
582 |
double upper = lp.colUpperBound(it);
|
deba@2364
|
583 |
if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF)
|
deba@2364
|
584 |
continue;
|
deba@2364
|
585 |
os << " ";
|
deba@2363
|
586 |
|
deba@2364
|
587 |
if (lower != upper) {
|
deba@2364
|
588 |
if (lower != -LpSolverBase::INF) {
|
deba@2364
|
589 |
os << lower << " <= ";
|
deba@2364
|
590 |
}
|
deba@2364
|
591 |
|
deba@2364
|
592 |
os << cols[it] << " ";
|
deba@2364
|
593 |
|
deba@2364
|
594 |
if (upper != LpSolverBase::INF) {
|
deba@2364
|
595 |
os << "<= " << upper << " ";
|
deba@2364
|
596 |
}
|
deba@2364
|
597 |
} else {
|
deba@2364
|
598 |
os << cols[it] << " == " << upper << " ";
|
deba@2364
|
599 |
}
|
deba@2364
|
600 |
os << std::endl;
|
deba@2364
|
601 |
}
|
deba@2363
|
602 |
|
deba@2364
|
603 |
os << "objective" << std::endl;
|
deba@2364
|
604 |
os << " ";
|
deba@2364
|
605 |
if (lp.is_min()) {
|
deba@2364
|
606 |
os << "min ";
|
deba@2364
|
607 |
} else {
|
deba@2364
|
608 |
os << "max ";
|
deba@2364
|
609 |
}
|
deba@2364
|
610 |
writeExpression(os, lp.obj());
|
deba@2364
|
611 |
os << std::endl;
|
deba@2364
|
612 |
}
|
deba@2364
|
613 |
|
deba@2363
|
614 |
|
deba@2364
|
615 |
private:
|
deba@2363
|
616 |
|
deba@2364
|
617 |
typedef std::map<LpSolverBase::Col, std::string> ColMap;
|
deba@2363
|
618 |
|
deba@2364
|
619 |
LpSolverBase& lp;
|
deba@2364
|
620 |
std::string name;
|
deba@2364
|
621 |
ColMap cols;
|
deba@2364
|
622 |
};
|
deba@2363
|
623 |
|
deba@2316
|
624 |
}
|
deba@2316
|
625 |
|
deba@2316
|
626 |
#endif
|