Limbo 3.5.4
Loading...
Searching...
No Matches
LpDualMcf.h
Go to the documentation of this file.
1
10
11#ifndef _LIMBO_SOLVERS_LPMCF_LPDUALMCF_H
12#define _LIMBO_SOLVERS_LPMCF_LPDUALMCF_H
13
14#include <cstdlib>
15#include <iostream>
16#include <cassert>
17#include <string>
18#include <cctype>
19#include <ctime>
20#include <boost/cstdint.hpp>
21#include <boost/unordered_map.hpp>
22#include <boost/foreach.hpp>
23#include <boost/typeof/typeof.hpp>
24#include <boost/algorithm/string/predicate.hpp>
25
28#include <limbo/math/Math.h>
29
30using std::cout;
31using std::endl;
32using std::string;
33using std::pair;
34using std::make_pair;
35using boost::int32_t;
36using boost::int64_t;
37using boost::unordered_map;
38using boost::iequals;
39
41namespace limbo
42{
44namespace solvers
45{
47namespace lpmcf
48{
49
54template <typename T1, typename T2>
55struct hash_pair : pair<T1, T2>
56{
58 typedef pair<T1, T2> base_type;
59 using typename base_type::first_type;
60 using typename base_type::second_type;
62
64 hash_pair() : base_type() {}
68 hash_pair(first_type const& a, second_type const& b) : base_type(a, b) {}
71 hash_pair(base_type const& rhs) : base_type(rhs) {}
72
76 bool operator==(base_type const& rhs) const
77 {return this->first == rhs.first && this->second == rhs.second;}
78
82 friend std::size_t hash_value(base_type const& key)
83 {
84 std::size_t seed = 0;
85 boost::hash_combine(seed, key.first);
86 boost::hash_combine(seed, key.second);
87 return seed;
88 }
89};
90
139template <typename T = int64_t>
140class LpDualMcf : public Lgf<T>, public LpParser::LpDataBase
141{
142 public:
144 typedef T value_type;
150 using typename base_type1::cost_type;
151 using typename base_type1::graph_type;
152 using typename base_type1::node_type;
153 using typename base_type1::arc_type;
154 using typename base_type1::node_value_map_type;
155 using typename base_type1::node_name_map_type;
156 using typename base_type1::arc_value_map_type;
157 using typename base_type1::arc_cost_map_type;
158 using typename base_type1::arc_flow_map_type;
159 using typename base_type1::node_pot_map_type;
160
161 // I don't know why it does not work with
162 // using typename base_type1::alg_type;
163 typedef typename base_type1::alg_type alg_type;
165
173 {
174 string name;
175 pair<value_type, value_type> range;
178 node_type node;
179
186 variable_type(string const& n,
187 value_type const& l = 0,
188 value_type const& r = std::numeric_limits<value_type>::max(),
189 value_type const& w = 0,
190 value_type const& v = 0)
191 : name(n), range(make_pair(l, r)), weight(w), value(v) {}
192
195 bool is_lower_bounded() const {return range.first != limbo::lowest<value_type>();}
198 bool is_upper_bounded() const {return range.second != std::numeric_limits<value_type>::max();}
201 bool is_bounded() const {return is_lower_bounded() || is_upper_bounded();}
202 };
203
210 {
211 pair<string, string> variable;
213 arc_type arc;
214
219 constraint_type(string const& xi, string const& xj, value_type const& c)
220 : variable(make_pair(xi, xj)), constant(c) {}
221 };
222
225 LpDualMcf(value_type max_limit = (value_type(2) << (sizeof(value_type)*8*3/4)))
226 : base_type1(),
227 base_type2(),
228 m_is_bounded(false),
229 m_M(max_limit) // use as unlimited number
230 {
231 if (m_M < 0) m_M = -m_M; // make sure m_M is positive
232 }
233
240 virtual void add_variable(string const& xi,
241 double l = limbo::lowest<double>(),
242 double r = std::numeric_limits<value_type>::max())
243 {
244 // in case of overflow
245 value_type lb = l;
246 value_type ub = r;
247 if (l <= (double)limbo::lowest<value_type>())
249 if (r >= (double)std::numeric_limits<value_type>::max())
250 ub = std::numeric_limits<value_type>::max();
251 limboAssertMsg(lb <= ub, "failed to add bound %lu <= %lu <= %lu", lb, xi, ub);
252
253 // no variables with the same name is allowed
254 BOOST_AUTO(found, m_hVariable.find(xi));
255 if (found == m_hVariable.end())
256 limboAssertMsg(m_hVariable.insert(make_pair(xi, variable_type(xi, lb, ub, 0))).second,
257 "failed to insert variable %lu to hash table", xi);
258 else
259 {
260 found->second.range.first = std::max(found->second.range.first, (value_type)lb);
261 found->second.range.second = std::min(found->second.range.second, (value_type)ub);
262 limboAssertMsg(found->second.range.first <= found->second.range.second,
263 "failed to set bound %lu <= %lu <= %lu", found->second.range.first, xi, found->second.range.second);
264 }
265 // if user set bounds to variables
266 // switch to bounded mode, which means there will be an additional node to the graph
267 if (lb != limbo::lowest<value_type>() || ub != std::numeric_limits<value_type>::max())
268 this->is_bounded(true);
269 }
270
274 virtual void add_constraint(std::string const& /*cname*/, LpParser::TermArray const& terms, char compare, double constant)
275 {
276 limboAssert(terms.size() == 2 && terms[0].coef*terms[1].coef < 0);
277 // in case some variables are not added yet
278 add_variable(terms[0].var);
279 add_variable(terms[1].var);
280 string xi = terms[0].var;
281 string xj = terms[1].var;
282 if (compare == '<' || compare == '=')
283 {
284 if (terms[0].coef > 0)
285 {
286 xi = terms[1].var;
287 xj = terms[0].var;
288 }
289 else
290 {
291 xi = terms[0].var;
292 xj = terms[1].var;
293 }
294 constant = -constant;
295 add_constraint(xi, xj, constant);
296 }
297 if (compare == '>' || compare == '=')
298 {
299 if (terms[0].coef > 0)
300 {
301 xi = terms[0].var;
302 xj = terms[1].var;
303 }
304 else
305 {
306 xi = terms[1].var;
307 xj = terms[0].var;
308 }
309 add_constraint(xi, xj, constant);
310 }
311 }
312
315 virtual void add_objective(bool minimize, LpParser::TermArray const& terms)
316 {
317 for (LpParser::TermArray::const_iterator it = terms.begin(); it != terms.end(); ++it)
318 {
319 // in case variable is not added yet
320 add_variable(it->var);
321
322 if (minimize) // minimize objective
323 add_objective(it->var, it->coef);
324 else // maximize objective
325 add_objective(it->var, -it->coef);
326 }
327 }
328
335 virtual void add_constraint(string const& xi, string const& xj, cost_type const& cij)
336 {
337 BOOST_AUTO(found, m_hConstraint.find(make_pair(xi, xj)));
338 if (found == m_hConstraint.end())
340 make_pair(
341 make_pair(xi, xj),
342 constraint_type(xi, xj, cij)
343 )
344 ).second,
345 "failed to add constraint for %lu - %lu >= %lu", xi, xj, cij
346 );
347 else // automatically reduce constraints
348 found->second.constant = std::max(found->second.constant, cij);
349 }
350
356 virtual void add_objective(string const& xi, value_type const& w)
357 {
358 if (w == 0) return;
359
360 BOOST_AUTO(found, m_hVariable.find(xi));
361 limboAssertMsg(found != m_hVariable.end(), "failed to add objective %lu %lu", w, xi);
362
363 found->second.weight += w;
364 }
365
368 void set_integer(std::string const& /*vname*/, bool /*binary*/)
369 {
370 // ignored
371 }
372
375 bool is_bounded() const {return m_is_bounded;}
378 void is_bounded(bool v) {m_is_bounded = v;}
379
385 typename alg_type::ProblemType operator()(string const& filename)
386 {
387 read_lp(filename);
388 typename alg_type::ProblemType status = (*this)();
389 if (status == alg_type::OPTIMAL)
390 this->print_solution(filename+".sol");
391
392 return status;
393 }
394
399 typename alg_type::ProblemType operator()()
400 {
401 prepare();
402#ifdef DEBUG
403 this->print_graph("graph.lp");
404#endif
405 return run();
406 }
407
410 value_type solution(string const& xi) const
411 {
412 BOOST_AUTO(found, m_hVariable.find(xi));
413 limboAssertMsg(found != m_hVariable.end(), "failed to find variable %lu", xi);
414
415 return found->second.value;
416 }
417
420 void read_lp(string const& filename)
421 {
422 LpParser::read(*this, filename);
423 }
424
426 bool empty() const {return m_hVariable.empty();}
427
431 virtual void print_solution(string const& filename) const
432 {
433 this->base_type1::print_solution(filename);
434
435 std::ofstream out (filename.c_str(), std::ios::app);
436 if (!out.good())
437 {
438 cout << "failed to open " << filename << endl;
439 return;
440 }
441
442 out << "############# LP Solution #############" << endl;
443 for (BOOST_AUTO(it, this->m_hVariable.begin()); it != this->m_hVariable.end(); ++it)
444 {
445 variable_type const& variable = it->second;
446 out << this->m_hName[variable.node] << ": " << variable.value << endl;
447 }
448
449 out.close();
450 }
451
453 void print_problem(string const& filename) const
454 {
455 std::ofstream out (filename.c_str());
456 if (!out.good())
457 {
458 cout << "failed to open " << filename << endl;
459 return;
460 }
461
462 // print objective
463 out << "Minimize\n";
464 for (BOOST_AUTO(it, this->m_hVariable.begin()); it != this->m_hVariable.end(); ++it)
465 {
466 variable_type const& variable = it->second;
467 if (variable.weight == 0) continue;
468
469 out << "\t" << " + " << variable.weight << " " << variable.name << endl;
470 }
471 // print constraints
472 out << "Subject To\n";
473 for (BOOST_AUTO(it, this->m_hConstraint.begin()); it != this->m_hConstraint.end(); ++it)
474 {
475 constraint_type const& constraint = it->second;
476 out << "\t" << constraint.variable.first
477 << " - " << constraint.variable.second
478 << " >= " << constraint.constant << endl;
479 }
480 // print bounds
481 out << "Bounds\n";
482 for (BOOST_AUTO(it, this->m_hVariable.begin()); it != this->m_hVariable.end(); ++it)
483 {
484 variable_type const& variable = it->second;
485 out << "\t";
486 // both lower bound and upper bound
487 if (variable.range.first != limbo::lowest<value_type>()
488 && variable.range.second != std::numeric_limits<value_type>::max())
489 out << variable.range.first << " <= "
490 << variable.name << " <= " << variable.range.second << endl;
491 // lower bound only
492 else if (variable.range.first != limbo::lowest<value_type>())
493 out << variable.name << " >= " << variable.range.first << endl;
494 // upper bound only
495 else if (variable.range.second != std::numeric_limits<value_type>::max())
496 out << variable.name << " <= " << variable.range.second << endl;
497 // no bounds
498 else
499 out << variable.name << " free\n";
500 }
501 // print data type (integer)
502 out << "Generals\n";
503 for (BOOST_AUTO(it, this->m_hVariable.begin()); it != this->m_hVariable.end(); ++it)
504 {
505 variable_type const& variable = it->second;
506 out << "\t" << variable.name << endl;
507 }
508 out << "End";
509 out.close();
510 }
511 protected:
513 void prepare()
514 {
515 // 1. preparing nodes
516 // set supply to its weight in the objective
517 for (BOOST_AUTO(it, m_hVariable.begin()); it != m_hVariable.end(); ++it)
518 {
519 variable_type& variable = it->second;
520 node_type const& node = this->m_graph.addNode();
521 variable.node = node;
522 this->m_hSupply[node] = variable.weight;
523 this->m_hName[node] = variable.name;
524 }
525
526 // 2. preparing arcs
527 // arcs constraints like xi - xj >= cij
528 // add arc from node i to node j with cost -cij and capacity unlimited
529 for (BOOST_AUTO(it, m_hConstraint.begin()); it != m_hConstraint.end(); ++it)
530 {
531 constraint_type& constraint = it->second;
532 BOOST_AUTO(found1, m_hVariable.find(constraint.variable.first));
533 BOOST_AUTO(found2, m_hVariable.find(constraint.variable.second));
534 limboAssertMsg(found1 != m_hVariable.end(), "failed to find variable1 %lu in preparing arcs", constraint.variable.first);
535 limboAssertMsg(found2 != m_hVariable.end(), "failed to find variable2 %lu in preparing arcs", constraint.variable.second);
536
537 variable_type const& variable1 = found1->second;
538 variable_type const& variable2 = found2->second;
539
540 node_type const& node1 = variable1.node;
541 node_type const& node2 = variable2.node;
542
543 arc_type const& arc = this->m_graph.addArc(node1, node2);
544 constraint.arc = arc;
545
546 this->m_hCost[arc] = -constraint.constant;
547 this->m_hLower[arc] = 0;
548 //m_hUpper[arc] = std::numeric_limits<value_type>::max();
549 this->m_hUpper[arc] = m_M;
550 }
551 // 3. arcs for variable bounds
552 // from node to additional node
553 if (this->is_bounded())
554 {
555 // 3.1 create additional node
556 // its corresponding weight is the negative sum of weight for other nodes
557 m_addl_node = this->m_graph.addNode();
558 value_type addl_weight = 0;
559 for (BOOST_AUTO(it, m_hVariable.begin()); it != m_hVariable.end(); ++it)
560 addl_weight -= it->second.weight;
561 this->m_hSupply[m_addl_node] = addl_weight;
562 this->m_hName[m_addl_node] = "lpmcf_additional_node";
563
564 for (BOOST_AUTO(it, m_hVariable.begin()); it != m_hVariable.end(); ++it)
565 {
566 variable_type const& variable = it->second;
567 // has lower bound
568 // add arc from node to additional node with cost d and cap unlimited
569 if (variable.is_lower_bounded())
570 {
571 arc_type const& arc = this->m_graph.addArc(variable.node, m_addl_node);
572 this->m_hCost[arc] = -variable.range.first;
573 this->m_hLower[arc] = 0;
574 this->m_hUpper[arc] = m_M;
575 }
576 // has upper bound
577 // add arc from additional node to node with cost u and capacity unlimited
578 if (variable.is_upper_bounded())
579 {
580 arc_type const& arc = this->m_graph.addArc(m_addl_node, variable.node);
581 this->m_hCost[arc] = variable.range.second;
582 this->m_hLower[arc] = 0;
583 this->m_hUpper[arc] = m_M;
584 }
585 }
586 }
587 }
588
590 typename alg_type::ProblemType run()
591 {
592 // 1. choose algorithm
593 alg_type alg (this->m_graph);
594
595 // 1.1 for robustness
596 // if problem is empty, also return OPTIMAL
597 if (this->empty())
598 return alg_type::OPTIMAL;
599
600 // 2. run
601 typename alg_type::ProblemType status = alg.reset().resetParams()
602 .lowerMap(this->m_hLower)
603 .upperMap(this->m_hUpper)
604 .costMap(this->m_hCost)
605 .supplyMap(this->m_hSupply)
606 .run();
607
608 // 3. check results
609#ifdef DEBUG
610 switch (status)
611 {
612 case alg_type::OPTIMAL:
613 cout << "OPTIMAL" << endl;
614 break;
615 case alg_type::INFEASIBLE:
616 cout << "INFEASIBLE" << endl;
617 break;
618 case alg_type::UNBOUNDED:
619 cout << "UNBOUNDED" << endl;
620 break;
621 }
622
623 limboAssertMsg(status == alg_type::OPTIMAL, "failed to achieve OPTIMAL solution");
624#endif
625 // 4. update solution
626 if (status == alg_type::OPTIMAL)
627 {
628 this->m_totalcost = alg.template totalCost<cost_type>();
629 // get dual solution of LP, which is the flow of mcf, skip this if not necessary
630 alg.flowMap(this->m_hFlow);
631 // get solution of LP, which is the dual solution of mcf
632 alg.potentialMap(this->m_hPot);
633
634 // update solution
635 value_type addl_value = 0;
636 if (this->is_bounded())
637 addl_value = this->m_hPot[m_addl_node];
638 for (BOOST_AUTO(it, m_hVariable.begin()); it != m_hVariable.end(); ++it)
639 {
640 variable_type& variable = it->second;
641 variable.value = this->m_hPot[variable.node]-addl_value;
642 }
643 }
644
645 return status;
646 }
647
649 node_type m_addl_node;
650
653 unordered_map<string, variable_type> m_hVariable;
654 unordered_map<hash_pair<string, string>, constraint_type> m_hConstraint;
655};
656
657}}} // namespace lpmcf // namespace solvers // limbo
658
659#endif
#define limboAssertMsg(condition, args...)
custom assertion with message
Definition AssertMsg.h:24
#define limboAssert(condition)
custom assertion without message
Definition AssertMsg.h:36
solve linear programming with min-cost flow
Driver for Lp parser.
mathematical utilities such as abs
Base class for lp database. Only pure virtual functions are defined. User needs to inheritate this ...
Definition LpDataBase.h:115
virtual void print_graph(string const &filename) const
Definition Lgf.h:112
node_value_map_type m_hSupply
Definition Lgf.h:258
node_name_map_type m_hName
Definition Lgf.h:259
arc_value_map_type m_hLower
Definition Lgf.h:255
virtual void print_solution(string const &filename) const
Definition Lgf.h:170
arc_value_map_type m_hUpper
Definition Lgf.h:256
LpParser::LpDataBase base_type2
inherit from LpParser::LpDataBase
Definition LpDualMcf.h:148
value_type solution(string const &xi) const
get solution to
Definition LpDualMcf.h:410
Lgf< value_type > base_type1
inherit from limbo::solvers::lpmcf::Lgf
Definition LpDualMcf.h:146
void read_lp(string const &filename)
read lp format
Definition LpDualMcf.h:420
void print_problem(string const &filename) const
print primal problem in LP format to a file
Definition LpDualMcf.h:453
T value_type
value_type can only be integer types
Definition LpDualMcf.h:144
void set_integer(std::string const &, bool)
set integer variables param vname integer variables param binary denotes whether they are binary va...
Definition LpDualMcf.h:368
virtual void add_objective(bool minimize, LpParser::TermArray const &terms)
add object callback for LpParser::LpDataBase
Definition LpDualMcf.h:315
LpDualMcf(value_type max_limit=(value_type(2)<<(sizeof(value_type) *8 *3/4)))
constructor
Definition LpDualMcf.h:225
alg_type::ProblemType run()
kernel function to run algorithm
Definition LpDualMcf.h:590
void prepare()
prepare before run
Definition LpDualMcf.h:513
alg_type::ProblemType operator()(string const &filename)
API to run the algorithm with input file.
Definition LpDualMcf.h:385
unordered_map< string, variable_type > m_hVariable
variable map
Definition LpDualMcf.h:653
virtual void print_solution(string const &filename) const
print solutions into a file including primal problem and dual problem
Definition LpDualMcf.h:431
virtual void add_constraint(std::string const &, LpParser::TermArray const &terms, char compare, double constant)
add constraint callback for LpParser::LpDataBase
Definition LpDualMcf.h:274
virtual void add_variable(string const &xi, double l=limbo::lowest< double >(), double r=std::numeric_limits< value_type >::max())
add variable with range. default range is .
Definition LpDualMcf.h:240
void is_bounded(bool v)
set if the problem is bounded
Definition LpDualMcf.h:378
virtual void add_objective(string const &xi, value_type const &w)
add linear terms for objective function of the primal linear programming problem.
Definition LpDualMcf.h:356
alg_type::ProblemType operator()()
API to run the algorithm.
Definition LpDualMcf.h:399
unordered_map< hash_pair< string, string >, constraint_type > m_hConstraint
constraint map
Definition LpDualMcf.h:654
virtual void add_constraint(string const &xi, string const &xj, cost_type const &cij)
add constraint .
Definition LpDualMcf.h:335
bool empty() const
check empty
Definition LpDualMcf.h:426
bool is_bounded() const
check if lp problem is bounded
Definition LpDualMcf.h:375
bool m_is_bounded
whether the problem is bounded or not
Definition LpDualMcf.h:648
node_type m_addl_node
additional node, only valid when m_is_bounded = true
Definition LpDualMcf.h:649
std::vector< Term > TermArray
array of terms
Definition LpDataBase.h:105
bool read(LpDataBase &db, const string &lpFile)
API for LpParser. Read LP file and initialize database by calling user-defined callback functions.
namespace for Limbo.Solvers.lpmcf
Definition Lgf.h:44
namespace for Limbo.Solvers
namespace for Limbo
T lowest()
generic function to get lowest value of numbers
constraint object in the primal linear programming problem. standard format: maps to arc .
Definition LpDualMcf.h:210
constraint_type(string const &xi, string const &xj, value_type const &c)
constructor
Definition LpDualMcf.h:219
pair< string, string > variable
variable and
Definition LpDualMcf.h:211
value_type constant
constant in the right hand side, i.e.,
Definition LpDualMcf.h:212
variable in the primal linear programming problem. standard format: maps to node ,...
Definition LpDualMcf.h:173
bool is_bounded() const
check if the variable is bounded
Definition LpDualMcf.h:201
pair< value_type, value_type > range
pair of
Definition LpDualMcf.h:175
variable_type(string const &n, value_type const &l=0, value_type const &r=std::numeric_limits< value_type >::max(), value_type const &w=0, value_type const &v=0)
constructor
Definition LpDualMcf.h:186
bool is_lower_bounded() const
check if the variable is lower bounded
Definition LpDualMcf.h:195
bool is_upper_bounded() const
check if the variable is upper bounded
Definition LpDualMcf.h:198
value_type weight
weight in the objective, i.e.,
Definition LpDualMcf.h:176
hash_pair(first_type const &a, second_type const &b)
constructor
Definition LpDualMcf.h:68
bool operator==(base_type const &rhs) const
override equality comparison
Definition LpDualMcf.h:76
friend std::size_t hash_value(base_type const &key)
compute hash value for a pair of data
Definition LpDualMcf.h:82
hash_pair(base_type const &rhs)
copy constructor
Definition LpDualMcf.h:71