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@2316
|
25 |
|
deba@2316
|
26 |
namespace lemon {
|
deba@2316
|
27 |
|
deba@2316
|
28 |
class LpReader : public LemonReader::SectionReader {
|
deba@2316
|
29 |
typedef LemonReader::SectionReader Parent;
|
deba@2316
|
30 |
public:
|
deba@2316
|
31 |
|
deba@2316
|
32 |
|
deba@2316
|
33 |
/// \brief Constructor.
|
deba@2316
|
34 |
///
|
deba@2316
|
35 |
/// Constructor for LpReader. It creates the LpReader and attach
|
deba@2316
|
36 |
/// it into the given LemonReader. The lp reader will add
|
deba@2316
|
37 |
/// variables, constraints and objective function to the
|
deba@2316
|
38 |
/// given lp solver.
|
deba@2316
|
39 |
LpReader(LemonReader& _reader, LpSolverBase& _lp,
|
deba@2316
|
40 |
const std::string& _name = std::string())
|
deba@2316
|
41 |
: Parent(_reader), lp(_lp), name(_name) {}
|
deba@2316
|
42 |
|
deba@2316
|
43 |
|
deba@2316
|
44 |
/// \brief Destructor.
|
deba@2316
|
45 |
///
|
deba@2316
|
46 |
/// Destructor for NodeSetReader.
|
deba@2316
|
47 |
virtual ~LpReader() {}
|
deba@2316
|
48 |
|
deba@2316
|
49 |
private:
|
deba@2316
|
50 |
LpReader(const LpReader&);
|
deba@2316
|
51 |
void operator=(const LpReader&);
|
deba@2316
|
52 |
|
deba@2316
|
53 |
protected:
|
deba@2316
|
54 |
|
deba@2316
|
55 |
/// \brief Gives back true when the SectionReader can process
|
deba@2316
|
56 |
/// the section with the given header line.
|
deba@2316
|
57 |
///
|
deba@2316
|
58 |
/// It gives back true when the header line starts with \c \@lp,
|
deba@2316
|
59 |
/// and the header line's name and the nodeset's name are the same.
|
deba@2316
|
60 |
virtual bool header(const std::string& line) {
|
deba@2316
|
61 |
std::istringstream ls(line);
|
deba@2316
|
62 |
std::string command;
|
deba@2316
|
63 |
std::string id;
|
deba@2316
|
64 |
ls >> command >> id;
|
deba@2316
|
65 |
return command == "@lp" && name == id;
|
deba@2316
|
66 |
}
|
deba@2316
|
67 |
|
deba@2316
|
68 |
private:
|
deba@2316
|
69 |
|
deba@2316
|
70 |
enum Relation {
|
deba@2316
|
71 |
LE, EQ, GE
|
deba@2316
|
72 |
};
|
deba@2316
|
73 |
|
deba@2316
|
74 |
std::istream& readConstraint(std::istream& is, LpSolverBase::Constr& c) {
|
deba@2316
|
75 |
char x;
|
deba@2316
|
76 |
LpSolverBase::Expr e1, e2;
|
deba@2316
|
77 |
Relation op1;
|
deba@2316
|
78 |
is >> std::ws;
|
deba@2316
|
79 |
readExpression(is, e1);
|
deba@2316
|
80 |
is >> std::ws;
|
deba@2316
|
81 |
readRelation(is, op1);
|
deba@2316
|
82 |
is >> std::ws;
|
deba@2316
|
83 |
readExpression(is, e2);
|
deba@2316
|
84 |
if (!is.get(x)) {
|
deba@2316
|
85 |
if (op1 == LE) {
|
deba@2316
|
86 |
c = e1 <= e2;
|
deba@2316
|
87 |
} else if (op1 == GE) {
|
deba@2316
|
88 |
c = e1 >= e2;
|
deba@2316
|
89 |
} else {
|
deba@2316
|
90 |
c = e1 == e2;
|
deba@2316
|
91 |
}
|
deba@2316
|
92 |
} else {
|
deba@2316
|
93 |
is.putback(x);
|
deba@2316
|
94 |
LpSolverBase::Expr e3;
|
deba@2316
|
95 |
Relation op2;
|
deba@2316
|
96 |
readRelation(is, op2);
|
deba@2316
|
97 |
is >> std::ws;
|
deba@2316
|
98 |
readExpression(is, e3);
|
deba@2316
|
99 |
if (!e1.empty() || !e3.empty()) {
|
deba@2316
|
100 |
throw DataFormatError("Wrong range format");
|
deba@2316
|
101 |
}
|
deba@2316
|
102 |
if (op2 != op1 || op1 == EQ) {
|
deba@2316
|
103 |
throw DataFormatError("Wrong range format");
|
deba@2316
|
104 |
}
|
deba@2316
|
105 |
if (op1 == LE) {
|
deba@2316
|
106 |
c = e1.constComp() <= e2 <= e3.constComp();
|
deba@2316
|
107 |
} else {
|
deba@2316
|
108 |
c = e1.constComp() >= e2 >= e3.constComp();
|
deba@2316
|
109 |
}
|
deba@2316
|
110 |
}
|
deba@2316
|
111 |
}
|
deba@2316
|
112 |
|
deba@2316
|
113 |
std::istream& readExpression(std::istream& is, LpSolverBase::Expr& e) {
|
deba@2316
|
114 |
LpSolverBase::Col c;
|
deba@2316
|
115 |
double d;
|
deba@2316
|
116 |
char x;
|
deba@2316
|
117 |
readElement(is, c, d);
|
deba@2316
|
118 |
if (c != INVALID) {
|
deba@2316
|
119 |
e += d * c;
|
deba@2316
|
120 |
} else {
|
deba@2316
|
121 |
e += d;
|
deba@2316
|
122 |
}
|
deba@2316
|
123 |
is >> std::ws;
|
deba@2316
|
124 |
while (is.get(x) && (x == '+' || x == '-')) {
|
deba@2316
|
125 |
is >> std::ws;
|
deba@2316
|
126 |
readElement(is, c, d);
|
deba@2316
|
127 |
if (c != INVALID) {
|
deba@2316
|
128 |
e += (x == '+' ? d : -d) * c;
|
deba@2316
|
129 |
} else {
|
deba@2316
|
130 |
e += (x == '+' ? d : -d);
|
deba@2316
|
131 |
}
|
deba@2316
|
132 |
is >> std::ws;
|
deba@2316
|
133 |
}
|
deba@2316
|
134 |
if (!is) {
|
deba@2316
|
135 |
is.clear();
|
deba@2316
|
136 |
} else {
|
deba@2316
|
137 |
is.putback(x);
|
deba@2316
|
138 |
}
|
deba@2316
|
139 |
return is;
|
deba@2316
|
140 |
}
|
deba@2316
|
141 |
|
deba@2316
|
142 |
std::istream& readElement(std::istream& is,
|
deba@2316
|
143 |
LpSolverBase::Col& c, double& d) {
|
deba@2316
|
144 |
d = 1.0;
|
deba@2316
|
145 |
c = INVALID;
|
deba@2316
|
146 |
char x, y;
|
deba@2316
|
147 |
if (!is.get(x)) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
148 |
if (x == '+' || x == '-') {
|
deba@2316
|
149 |
is >> std::ws;
|
deba@2316
|
150 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
151 |
while (is.get(x) && (x == '+' || x == '-')) {
|
deba@2316
|
152 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
153 |
is >> std::ws;
|
deba@2316
|
154 |
}
|
deba@2316
|
155 |
if (!is) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
156 |
}
|
deba@2316
|
157 |
if (numFirstChar(x)) {
|
deba@2316
|
158 |
is.putback(x);
|
deba@2316
|
159 |
double e;
|
deba@2316
|
160 |
readNum(is, e);
|
deba@2316
|
161 |
d *= e;
|
deba@2316
|
162 |
} else if (varFirstChar(x)) {
|
deba@2316
|
163 |
is.putback(x);
|
deba@2316
|
164 |
LpSolverBase::Col f;
|
deba@2316
|
165 |
readCol(is, f);
|
deba@2316
|
166 |
c = f;
|
deba@2316
|
167 |
} else {
|
deba@2316
|
168 |
throw DataFormatError("Invalid expression format");
|
deba@2316
|
169 |
}
|
deba@2316
|
170 |
is >> std::ws;
|
deba@2316
|
171 |
while (is.get(y) && (y == '*' || y == '/')) {
|
deba@2316
|
172 |
is >> std::ws;
|
deba@2316
|
173 |
if (!is.get(x)) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
174 |
if (x == '+' || x == '-') {
|
deba@2316
|
175 |
is >> std::ws;
|
deba@2316
|
176 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
177 |
while (is.get(x) && (x == '+' || x == '-')) {
|
deba@2316
|
178 |
d *= x == '-' ? -1 : 1;
|
deba@2316
|
179 |
is >> std::ws;
|
deba@2316
|
180 |
}
|
deba@2316
|
181 |
if (!is) throw DataFormatError("Cannot find lp element");
|
deba@2316
|
182 |
}
|
deba@2316
|
183 |
if (numFirstChar(x)) {
|
deba@2316
|
184 |
is.putback(x);
|
deba@2316
|
185 |
double e;
|
deba@2316
|
186 |
readNum(is, e);
|
deba@2316
|
187 |
if (y == '*') {
|
deba@2316
|
188 |
d *= e;
|
deba@2316
|
189 |
} else {
|
deba@2316
|
190 |
d /= e;
|
deba@2316
|
191 |
}
|
deba@2316
|
192 |
} else if (varFirstChar(x)) {
|
deba@2316
|
193 |
is.putback(x);
|
deba@2316
|
194 |
LpSolverBase::Col f;
|
deba@2316
|
195 |
readCol(is, f);
|
deba@2316
|
196 |
if (y == '*') {
|
deba@2316
|
197 |
if (c == INVALID) {
|
deba@2316
|
198 |
c = f;
|
deba@2316
|
199 |
} else {
|
deba@2316
|
200 |
throw DataFormatError("Quadratic element in expression");
|
deba@2316
|
201 |
}
|
deba@2316
|
202 |
} else {
|
deba@2316
|
203 |
throw DataFormatError("Division by variable");
|
deba@2316
|
204 |
}
|
deba@2316
|
205 |
} else {
|
deba@2316
|
206 |
throw DataFormatError("Invalid expression format");
|
deba@2316
|
207 |
}
|
deba@2316
|
208 |
is >> std::ws;
|
deba@2316
|
209 |
}
|
deba@2316
|
210 |
if (!is) {
|
deba@2316
|
211 |
is.clear();
|
deba@2316
|
212 |
} else {
|
deba@2316
|
213 |
is.putback(y);
|
deba@2316
|
214 |
}
|
deba@2316
|
215 |
return is;
|
deba@2316
|
216 |
}
|
deba@2316
|
217 |
|
deba@2316
|
218 |
std::istream& readCol(std::istream& is, LpSolverBase::Col& c) {
|
deba@2316
|
219 |
char x;
|
deba@2316
|
220 |
std::string var;
|
deba@2316
|
221 |
while (is.get(x) && varChar(x)) {
|
deba@2316
|
222 |
var += x;
|
deba@2316
|
223 |
}
|
deba@2316
|
224 |
if (!is) {
|
deba@2316
|
225 |
is.clear();
|
deba@2316
|
226 |
} else {
|
deba@2316
|
227 |
is.putback(x);
|
deba@2316
|
228 |
}
|
deba@2316
|
229 |
ColMap::const_iterator it = cols.find(var);
|
deba@2316
|
230 |
if (cols.find(var) != cols.end()) {
|
deba@2316
|
231 |
c = it->second;
|
deba@2316
|
232 |
} else {
|
deba@2316
|
233 |
c = lp.addCol();
|
deba@2316
|
234 |
cols.insert(std::make_pair(var, c));
|
deba@2316
|
235 |
lp.colName(c, var);
|
deba@2316
|
236 |
}
|
deba@2316
|
237 |
return is;
|
deba@2316
|
238 |
}
|
deba@2316
|
239 |
|
deba@2316
|
240 |
std::istream& readNum(std::istream& is, double& d) {
|
deba@2316
|
241 |
is >> d;
|
deba@2316
|
242 |
if (!is) throw DataFormatError("Wrong number format");
|
deba@2316
|
243 |
return is;
|
deba@2316
|
244 |
}
|
deba@2316
|
245 |
|
deba@2316
|
246 |
std::istream& readRelation(std::istream& is, Relation& op) {
|
deba@2316
|
247 |
char x, y;
|
deba@2316
|
248 |
if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
|
deba@2316
|
249 |
throw DataFormatError("Wrong relation operator");
|
deba@2316
|
250 |
}
|
deba@2316
|
251 |
if (!is.get(y) || y != '=') {
|
deba@2316
|
252 |
throw DataFormatError("Wrong relation operator");
|
deba@2316
|
253 |
}
|
deba@2316
|
254 |
switch (x) {
|
deba@2316
|
255 |
case '<': op = LE;
|
deba@2316
|
256 |
break;
|
deba@2316
|
257 |
case '=': op = EQ;
|
deba@2316
|
258 |
break;
|
deba@2316
|
259 |
case '>': op = GE;
|
deba@2316
|
260 |
break;
|
deba@2316
|
261 |
}
|
deba@2316
|
262 |
return is;
|
deba@2316
|
263 |
}
|
deba@2316
|
264 |
|
deba@2316
|
265 |
static bool relationFirstChar(char c) {
|
deba@2316
|
266 |
return c == '<' || c == '=' || c == '>';
|
deba@2316
|
267 |
}
|
deba@2316
|
268 |
|
deba@2316
|
269 |
static bool varFirstChar(char c) {
|
deba@2316
|
270 |
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
deba@2316
|
271 |
}
|
deba@2316
|
272 |
|
deba@2316
|
273 |
static bool numFirstChar(char c) {
|
deba@2316
|
274 |
return (c >= '0' && c <= '9') || c == '.';
|
deba@2316
|
275 |
}
|
deba@2316
|
276 |
|
deba@2316
|
277 |
static bool varChar(char c) {
|
deba@2316
|
278 |
return (c >= '0' && c <= '9') ||
|
deba@2316
|
279 |
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
deba@2316
|
280 |
}
|
deba@2316
|
281 |
|
deba@2316
|
282 |
|
deba@2316
|
283 |
void addConstraint(const LpSolverBase::Constr& constr) {
|
deba@2316
|
284 |
if (constr.expr().size() != 1) {
|
deba@2316
|
285 |
lp.addRow(constr);
|
deba@2316
|
286 |
} else {
|
deba@2316
|
287 |
Lp::Expr e = constr.expr();
|
deba@2316
|
288 |
LpSolverBase::Col col = e.begin()->first;
|
deba@2316
|
289 |
double coeff = e.begin()->second;
|
deba@2316
|
290 |
double lb = LpSolverBase::NaN;
|
deba@2316
|
291 |
double ub = LpSolverBase::NaN;
|
deba@2316
|
292 |
if (coeff > 0) {
|
deba@2316
|
293 |
if (constr.upperBounded()) {
|
deba@2316
|
294 |
lb = (constr.lowerBound() - e.constComp()) / coeff;
|
deba@2316
|
295 |
}
|
deba@2316
|
296 |
if (constr.lowerBounded()) {
|
deba@2316
|
297 |
ub = (constr.upperBound() - e.constComp()) / coeff;
|
deba@2316
|
298 |
}
|
deba@2316
|
299 |
} else if (coeff < 0) {
|
deba@2316
|
300 |
if (constr.upperBounded()) {
|
deba@2316
|
301 |
lb = (constr.upperBound() - e.constComp()) / coeff;
|
deba@2316
|
302 |
}
|
deba@2316
|
303 |
if (constr.lowerBounded()) {
|
deba@2316
|
304 |
ub = (constr.lowerBound() - e.constComp()) / coeff;
|
deba@2316
|
305 |
}
|
deba@2316
|
306 |
} else {
|
deba@2316
|
307 |
lb = -LpSolverBase::INF;
|
deba@2316
|
308 |
ub = LpSolverBase::INF;
|
deba@2316
|
309 |
}
|
deba@2316
|
310 |
lp.colLowerBound(col, lb);
|
deba@2316
|
311 |
lp.colUpperBound(col, ub);
|
deba@2316
|
312 |
}
|
deba@2316
|
313 |
}
|
deba@2316
|
314 |
|
deba@2316
|
315 |
protected:
|
deba@2316
|
316 |
|
deba@2316
|
317 |
/// \brief Reader function of the section.
|
deba@2316
|
318 |
///
|
deba@2316
|
319 |
/// It reads the content of the section.
|
deba@2316
|
320 |
virtual void read(std::istream& is) {
|
deba@2316
|
321 |
std::string line;
|
deba@2316
|
322 |
std::map<std::string, LpSolverBase::Col> vars;
|
deba@2316
|
323 |
while (getline(is, line)) {
|
deba@2316
|
324 |
std::istringstream ls(line);
|
deba@2316
|
325 |
std::string sense;
|
deba@2316
|
326 |
ls >> sense;
|
deba@2316
|
327 |
if (sense == "min" || sense == "max") {
|
deba@2316
|
328 |
LpSolverBase::Expr expr;
|
deba@2316
|
329 |
ls >> std::ws;
|
deba@2316
|
330 |
readExpression(ls, expr);
|
deba@2316
|
331 |
lp.setObj(expr);
|
deba@2316
|
332 |
if (sense == "min") {
|
deba@2316
|
333 |
lp.min();
|
deba@2316
|
334 |
} else {
|
deba@2316
|
335 |
lp.max();
|
deba@2316
|
336 |
}
|
deba@2316
|
337 |
} else {
|
deba@2316
|
338 |
ls.str(line);
|
deba@2316
|
339 |
LpSolverBase::Constr constr;
|
deba@2316
|
340 |
ls >> std::ws;
|
deba@2316
|
341 |
readConstraint(ls, constr);
|
deba@2316
|
342 |
addConstraint(constr);
|
deba@2316
|
343 |
}
|
deba@2316
|
344 |
}
|
deba@2316
|
345 |
}
|
deba@2316
|
346 |
|
deba@2316
|
347 |
virtual void missing() {
|
deba@2316
|
348 |
ErrorMessage msg;
|
deba@2316
|
349 |
msg << "Lp section not found in file: @lp " << name;
|
deba@2316
|
350 |
throw IoParameterError(msg.message());
|
deba@2316
|
351 |
}
|
deba@2316
|
352 |
|
deba@2316
|
353 |
private:
|
deba@2316
|
354 |
|
deba@2316
|
355 |
typedef std::map<std::string, LpSolverBase::Col> ColMap;
|
deba@2316
|
356 |
|
deba@2316
|
357 |
LpSolverBase& lp;
|
deba@2316
|
358 |
std::string name;
|
deba@2316
|
359 |
ColMap cols;
|
deba@2316
|
360 |
};
|
deba@2316
|
361 |
|
deba@2316
|
362 |
}
|
deba@2316
|
363 |
|
deba@2316
|
364 |
#endif
|