Limbo
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
LpDualMcf.h
Go to the documentation of this file.
1 
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 
30 using std::cout;
31 using std::endl;
32 using std::string;
33 using std::pair;
34 using std::make_pair;
35 using boost::int32_t;
36 using boost::int64_t;
37 using boost::unordered_map;
38 using boost::iequals;
39 
41 namespace limbo
42 {
44 namespace solvers
45 {
47 namespace lpmcf
48 {
49 
54 template <typename T1, typename T2>
55 struct 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 
139 template <typename T = int64_t>
140 class 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;
176  value_type weight;
177  value_type value;
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;
212  value_type constant;
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>(),
243  {
244  // in case of overflow
245  value_type lb = l;
246  value_type ub = r;
247  if (l <= (double)limbo::lowest<value_type>())
248  lb = limbo::lowest<value_type>();
249  if (r >= (double)std::numeric_limits<value_type>::max())
251  assert_msg(lb <= ub, "failed to add bound " << 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  assert_msg(m_hVariable.insert(make_pair(xi, variable_type(xi, lb, ub, 0))).second,
257  "failed to insert variable " << xi << " to hash table");
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  assert_msg(found->second.range.first <= found->second.range.second,
263  "failed to set bound " << 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  }
274  virtual void add_constraint(std::string const& /*cname*/, LpParser::TermArray const& terms, char compare, double constant)
275  {
276  assert(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  }
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  }
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())
339  assert_msg(m_hConstraint.insert(
340  make_pair(
341  make_pair(xi, xj),
342  constraint_type(xi, xj, cij)
343  )
344  ).second,
345  "failed to add constraint for " << xi << " - " << xj << " >= " << cij
346  );
347  else // automatically reduce constraints
348  found->second.constant = std::max(found->second.constant, cij);
349  }
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  assert_msg(found != m_hVariable.end(), "failed to add objective " << w << " " << xi);
362 
363  found->second.weight += w;
364  }
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  }
399  typename alg_type::ProblemType operator()()
400  {
401  prepare();
402 #ifdef DEBUG
403  this->print_graph("graph.lp");
404 #endif
405  return run();
406  }
410  value_type solution(string const& xi) const
411  {
412  BOOST_AUTO(found, m_hVariable.find(xi));
413  assert_msg(found != m_hVariable.end(), "failed to find variable " << xi);
414 
415  return found->second.value;
416  }
420  void read_lp(string const& filename)
421  {
422  LpParser::read(*this, filename);
423  }
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  }
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  assert_msg(found1 != m_hVariable.end(), "failed to find variable1 " << constraint.variable.first << " in preparing arcs");
535  assert_msg(found2 != m_hVariable.end(), "failed to find variable2 " << constraint.variable.second << " in preparing arcs");
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  }
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;
616  cout << "INFEASIBLE" << endl;
617  break;
618  case alg_type::UNBOUNDED:
619  cout << "UNBOUNDED" << endl;
620  break;
621  }
622 
623  assert_msg(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 
651  value_type m_M;
652  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
bool is_lower_bounded() const
check if the variable is lower bounded
Definition: LpDualMcf.h:195
arc_flow_map_type m_hFlow
solution of min-cost flow, which is the dual solution of LP
Definition: Lgf.h:262
Base class for lp database. Only pure virtual functions are defined. User needs to inheritate this cl...
Definition: LpDataBase.h:114
graph_type m_graph
input graph
Definition: Lgf.h:254
virtual void print_solution(string const &filename) const
print solution
Definition: Lgf.h:170
hash_pair(base_type const &rhs)
copy constructor
Definition: LpDualMcf.h:71
node_name_map_type m_hName
node name in mcf
Definition: Lgf.h:259
node_value_map_type m_hSupply
node supply in mcf
Definition: Lgf.h:258
void prepare()
prepare before run
Definition: LpDualMcf.h:513
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 run()
kernel function to run algorithm
Definition: LpDualMcf.h:590
std::vector< Term > TermArray
array of terms
Definition: LpDataBase.h:105
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
virtual void add_constraint(string const &xi, string const &xj, cost_type const &cij)
add constraint .
Definition: LpDualMcf.h:335
constraint object in the primal linear programming problem. standard format: maps to arc ...
Definition: LpDualMcf.h:209
cost_type m_totalcost
total cost after solving
Definition: Lgf.h:260
arc_cost_map_type m_hCost
arc cost in mcf
Definition: Lgf.h:257
LpDualMcf(value_type max_limit=(value_type(2)<< (sizeof(value_type)*8 *3/4)))
constructor
Definition: LpDualMcf.h:225
value_type weight
weight in the objective, i.e.,
Definition: LpDualMcf.h:176
pair< value_type, value_type > range
pair of
Definition: LpDualMcf.h:175
alg_type::ProblemType operator()(string const &filename)
API to run the algorithm with input file.
Definition: LpDualMcf.h:385
T value_type
value_type can only be integer types
Definition: LpDualMcf.h:144
solve network flow graph with min-cost flow
Definition: Lgf.h:54
virtual void add_objective(bool minimize, LpParser::TermArray const &terms)
add object callback for LpParser::LpDataBase
Definition: LpDualMcf.h:315
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
node_pot_map_type m_hPot
dual solution of min-cost flow, which is the solution of LP
Definition: Lgf.h:263
constraint_type(string const &xi, string const &xj, value_type const &c)
constructor
Definition: LpDualMcf.h:219
the model is infeasible
Definition: Solvers.h:37
arc_value_map_type m_hLower
lower bound of flow, usually zero
Definition: Lgf.h:255
mathematical utilities such as abs
bool empty() const
check empty
Definition: LpDualMcf.h:426
value_type constant
constant in the right hand side, i.e.,
Definition: LpDualMcf.h:212
bool operator==(base_type const &rhs) const
override equality comparison
Definition: LpDualMcf.h:76
pair< string, string > variable
variable and
Definition: LpDualMcf.h:211
void set_integer(std::string const &, bool)
set integer variables param vname integer variables param binary denotes whether they are binary vari...
Definition: LpDualMcf.h:368
namespace for Limbo
Definition: GraphUtility.h:22
bool iequals(string const &s1, string const &s2)
check two strings equal, case-insensitive
Definition: String.h:108
std::iterator_traits< Iterator >::value_type max(Iterator first, Iterator last)
get max of an array
Definition: Math.h:61
virtual void print_solution(string const &filename) const
print solutions into a file including primal problem and dual problem
Definition: LpDualMcf.h:431
solve linear programming with min-cost flow
alg_type::ProblemType operator()()
API to run the algorithm.
Definition: LpDualMcf.h:399
the model is unbounded
Definition: Solvers.h:39
void print_problem(string const &filename) const
print primal problem in LP format to a file
Definition: LpDualMcf.h:453
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
bool is_upper_bounded() const
check if the variable is upper bounded
Definition: LpDualMcf.h:198
hash_pair(first_type const &a, second_type const &b)
constructor
Definition: LpDualMcf.h:68
unordered_map< string, variable_type > m_hVariable
variable map
Definition: LpDualMcf.h:653
Lgf< value_type > base_type1
inherit from limbo::solvers::lpmcf::Lgf
Definition: LpDualMcf.h:146
optimally solved
Definition: Solvers.h:36
Driver for Lp parser.
node_type m_addl_node
additional node, only valid when m_is_bounded = true
Definition: LpDualMcf.h:649
double lowest< double >()
specialization for floating point types
Definition: Math.h:163
void read_lp(string const &filename)
read lp format
Definition: LpDualMcf.h:420
friend std::size_t hash_value(base_type const &key)
compute hash value for a pair of data
Definition: LpDualMcf.h:82
virtual void print_graph(string const &filename) const
print graph
Definition: Lgf.h:112
bool is_bounded() const
check if lp problem is bounded
Definition: LpDualMcf.h:375
unordered_map< hash_pair< string, string >, constraint_type > m_hConstraint
constraint map
Definition: LpDualMcf.h:654
bool is_bounded() const
check if the variable is bounded
Definition: LpDualMcf.h:201
arc_value_map_type m_hUpper
upper bound of flow, arc capacity in mcf
Definition: Lgf.h:256
variable in the primal linear programming problem. standard format: maps to node ...
Definition: LpDualMcf.h:172
std::iterator_traits< Iterator >::value_type min(Iterator first, Iterator last)
get min of an array
Definition: Math.h:77
hash calculator for pairs
Definition: LpDualMcf.h:55
bool read(LpDataBase &db, const string &lpFile)
API for LpParser. Read LP file and initialize database by calling user-defined callback functions...
value_type solution(string const &xi) const
get solution to
Definition: LpDualMcf.h:410
LP solved with min-cost flow.
Definition: LpDualMcf.h:140
void is_bounded(bool v)
set if the problem is bounded
Definition: LpDualMcf.h:378
#define assert_msg(condition, message)
assertion with message
Definition: AssertMsg.h:34
LpParser::LpDataBase base_type2
inherit from LpParser::LpDataBase
Definition: LpDualMcf.h:148
bool m_is_bounded
whether the problem is bounded or not
Definition: LpDualMcf.h:648