All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Pages
bounded_degree_mst.hpp
Go to the documentation of this file.
1 //=======================================================================
2 // Copyright (c)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See
5 // accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //=======================================================================
15 #ifndef PAAL_BOUNDED_DEGREE_MST_HPP
16 #define PAAL_BOUNDED_DEGREE_MST_HPP
17 
18 
23 
24 #include <boost/bimap.hpp>
25 #include <boost/range/as_array.hpp>
26 #include <boost/graph/connected_components.hpp>
27 
28 namespace paal {
29 namespace ir {
30 
31 namespace {
32 struct bounded_degree_mst_compare_traits {
33  static const double EPSILON;
34 };
35 
36 const double bounded_degree_mst_compare_traits::EPSILON = 1e-10;
37 }
38 
51 template <typename Graph, typename DegreeBounds, typename CostMap,
52  typename VertexIndex, typename SpanningTreeOutputIterator,
55  public:
59  bounded_degree_mst(const Graph & g, const DegreeBounds & deg_bounds,
60  CostMap cost_map, VertexIndex index,
61  SpanningTreeOutputIterator result_spanning_tree, Oracle oracle = Oracle{}) :
62  m_g(g), m_cost_map(cost_map), m_index(index), m_deg_bounds(deg_bounds),
63  m_result_spanning_tree(result_spanning_tree),
64  m_compare(bounded_degree_mst_compare_traits::EPSILON),
65  m_oracle(oracle)
66  {}
67 
68  using Edge = typename boost::graph_traits<Graph>::edge_descriptor;
69  using Vertex = typename boost::graph_traits<Graph>::vertex_descriptor;
70 
71  using EdgeMap = boost::bimap<Edge, lp::col_id>;
72  using VertexMap = std::unordered_map<lp::row_id, Vertex>;
73 
74  using EdgeMapOriginal = std::vector<std::pair<lp::col_id, Edge>>;
75 
76  using ErrorMessage = boost::optional<std::string>;
77 
81  ErrorMessage check_input_validity() {
82  // Is g connected?
83  std::vector<int> components(num_vertices(m_g));
84  int num = boost::connected_components(m_g, &components[0]);
85 
86  if (num > 1) {
87  return ErrorMessage{ "The graph is not connected." };
88  }
89 
90  return ErrorMessage{};
91  }
92 
101  template <typename LP>
102  auto get_find_violation(LP & lp) {
103  using candidate = bdmst_violation_checker::Candidate;
104  return m_oracle([&](){return m_violation_checker.get_violation_candidates(*this, lp);},
105  [&](candidate c){return m_violation_checker.check_violation(c, *this);},
106  [&](candidate c){return m_violation_checker.add_violated_constraint(c, *this, lp);});
107  }
108 
112  const Graph &get_graph() const { return m_g; }
113 
117  const VertexIndex &get_index() const { return m_index; }
118 
122  void remove_column(lp::col_id col_id) {
123  auto ret = m_edge_map.right.erase(col_id);
124  assert(ret == 1);
125  }
126 
130  void bind_edge_to_col(Edge e, lp::col_id col) {
131  m_edge_map_original.push_back(
132  typename EdgeMapOriginal::value_type(col, e));
133  m_edge_map.insert(typename EdgeMap::value_type(e, col));
134  }
135 
139  decltype(get(std::declval<CostMap>(), std::declval<Edge>()))
140  get_cost(Edge e) {
141  return get(m_cost_map, e);
142  }
143 
147  decltype(std::declval<DegreeBounds>()(get(std::declval<VertexIndex>(),
148  std::declval<Vertex>())))
149  get_degree_bound(Vertex v) {
150  return m_deg_bounds(get(m_index, v));
151  }
152 
157  boost::optional<lp::col_id> edge_to_col(Edge e) const {
158  auto i = m_edge_map.left.find(e);
159  if (i != m_edge_map.left.end()) {
160  return i->second;
161  } else {
162  return boost::none;
163  }
164  }
165 
169  const EdgeMap &get_edge_map() const { return m_edge_map; }
170 
174  const EdgeMapOriginal &get_original_edges_map() const {
175  return m_edge_map_original;
176  }
177 
182  *m_result_spanning_tree = e;
183  ++m_result_spanning_tree;
184  }
185 
190  return m_compare;
191  }
192 
196  void bind_vertex_to_row(Vertex v, lp::row_id row) {
197  m_vertex_map.insert(typename VertexMap::value_type(row, v));
198  }
199 
203  void remove_row(lp::row_id row_id) {
204  auto ret = m_vertex_map.erase(row_id);
205  assert(ret == 1);
206  }
207 
212  boost::optional<Vertex> row_to_vertex(lp::row_id row) {
213  auto i = m_vertex_map.find(row);
214  if (i != m_vertex_map.end()) {
215  return i->second;
216  } else {
217  return boost::none;
218  }
219  }
220 
221  private:
222  Edge col_to_edge(lp::col_id col) {
223  auto i = m_edge_map.right.find(col);
224  assert(i != m_edge_map.right.end());
225  return i->second;
226  }
227 
228  const Graph &m_g;
229  CostMap m_cost_map;
230  VertexIndex m_index;
231  const DegreeBounds &m_deg_bounds;
232  SpanningTreeOutputIterator m_result_spanning_tree;
233  bdmst_violation_checker m_violation_checker;
234 
235  EdgeMapOriginal m_edge_map_original;
236  EdgeMap m_edge_map;
237  VertexMap m_vertex_map;
238 
239  const utils::compare<double> m_compare;
240 
241  Oracle m_oracle;
242 };
243 
244 namespace detail {
263 template <typename Oracle = lp::random_violated_separation_oracle,
264  typename Graph,
265  typename DegreeBounds, typename CostMap, typename VertexIndex,
266  typename SpanningTreeOutputIterator>
267 bounded_degree_mst<Graph, DegreeBounds, CostMap, VertexIndex, SpanningTreeOutputIterator, Oracle>
268 make_bounded_degree_mst(const Graph & g, const DegreeBounds & deg_bounds,
269  CostMap cost_map, VertexIndex vertex_index,
270  SpanningTreeOutputIterator result_spanning_tree,
271  Oracle oracle = Oracle()) {
272  return bounded_degree_mst<Graph, DegreeBounds, CostMap, VertexIndex,
273  SpanningTreeOutputIterator, Oracle>(g, deg_bounds, cost_map, vertex_index,
274  result_spanning_tree, oracle);
275 }
276 } // detail
277 
299 template <typename Oracle = lp::random_violated_separation_oracle,
300  typename Graph,
301  typename DegreeBounds, typename SpanningTreeOutputIterator,
302  typename P, typename T, typename R>
303 auto
304 make_bounded_degree_mst(const Graph & g,
305  const DegreeBounds & deg_bounds,
306  const boost::bgl_named_params<P, T, R> & params,
307  SpanningTreeOutputIterator result_spanning_tree,
308  Oracle oracle = Oracle())
309  -> bounded_degree_mst<Graph, DegreeBounds,
310  decltype(choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight)),
311  decltype(choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index)),
312  SpanningTreeOutputIterator, Oracle> {
313 
314  return detail::make_bounded_degree_mst(g, deg_bounds,
315  choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight),
316  choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index),
317  result_spanning_tree, oracle);
318 }
319 
337 template <typename Oracle = lp::random_violated_separation_oracle,
338  typename Graph, typename DegreeBounds, typename SpanningTreeOutputIterator>
339 auto
340 make_bounded_degree_mst(const Graph & g, const DegreeBounds & deg_bounds,
341  SpanningTreeOutputIterator result_spanning_tree,
342  Oracle oracle = Oracle()) ->
343  decltype(make_bounded_degree_mst(g, deg_bounds, boost::no_named_parameters(), result_spanning_tree, oracle)) {
344  return make_bounded_degree_mst(g, deg_bounds, boost::no_named_parameters(), result_spanning_tree, oracle);
345 }
346 
354  bdmst_round_condition(double epsilon =
355  bounded_degree_mst_compare_traits::EPSILON)
356  : m_round_zero(epsilon) {}
357 
363  template <typename Problem, typename LP>
364  boost::optional<double> operator()(Problem &problem, const LP &lp,
365  lp::col_id col) {
366  auto ret = m_round_zero(problem, lp, col);
367  if (ret) {
368  problem.remove_column(col);
369  }
370  return ret;
371  }
372 
373  private:
374  round_condition_equals<0> m_round_zero;
375 };
376 
388  template <typename Problem, typename LP>
389  bool operator()(Problem &problem, const LP &lp, lp::row_id row) {
390  auto vertex = problem.row_to_vertex(row);
391  if (vertex) {
392  auto ret = (lp.get_row_degree(row) <=
393  problem.get_degree_bound(*vertex) + 1);
394  if (ret) {
395  problem.remove_row(row);
396  }
397  return ret;
398  } else
399  return false;
400  }
401 };
402 
406 struct bdmst_init {
411  template <typename Problem, typename LP>
412  void operator()(Problem &problem, LP &lp) {
413  lp.set_lp_name("bounded degree minimum spanning tree");
414  lp.set_optimization_type(lp::MINIMIZE);
415 
416  add_variables(problem, lp);
417  add_degree_bound_constraints(problem, lp);
418  add_all_set_equality(problem, lp);
419  }
420 
421  private:
426  template <typename Problem, typename LP>
427  void add_variables(Problem & problem, LP & lp) {
428  for (auto e : boost::as_array(edges(problem.get_graph()))) {
429  auto col = lp.add_column(problem.get_cost(e), 0, 1);
430  problem.bind_edge_to_col(e, col);
431  }
432  }
433 
439  template <typename Problem, typename LP>
440  void add_degree_bound_constraints(Problem &problem, LP &lp) {
441  auto const &g = problem.get_graph();
442 
443  for (auto v : boost::as_array(vertices(g))) {
444  lp::linear_expression expr;
445 
446  for (auto e : boost::as_array(out_edges(v, g))) {
447  expr += *(problem.edge_to_col(e));
448  }
449 
450  auto row =
451  lp.add_row(std::move(expr) <= problem.get_degree_bound(v));
452  problem.bind_vertex_to_row(v, row);
453  }
454  }
455 
460  template <typename Problem, typename LP>
461  void add_all_set_equality(Problem &problem, LP &lp) {
462  lp::linear_expression expr;
463  for (auto col : lp.get_columns()) {
464  expr += col;
465  }
466  lp.add_row(std::move(expr) == num_vertices(problem.get_graph()) - 1);
467  }
468 };
469 
477  bdmst_set_solution(double epsilon =
478  bounded_degree_mst_compare_traits::EPSILON)
479  : m_compare(epsilon) {}
480 
485  template <typename Problem, typename GetSolution>
486  void operator()(Problem & problem, const GetSolution & solution) {
487  for (auto col_and_edge : problem.get_original_edges_map()) {
488  if (m_compare.e(solution(col_and_edge.first), 1)) {
489  problem.add_to_result_spanning_tree(col_and_edge.second);
490  }
491  }
492  }
493 
494 private:
495  const utils::compare<double> m_compare;
496 };
497 
498 template <typename Init = bdmst_init,
499  typename RoundCondition = bdmst_round_condition,
500  typename RelaxContition = bdmst_relax_condition,
501  typename SetSolution = bdmst_set_solution,
502  typename SolveLPToExtremePoint = ir::row_generation_solve_lp<>,
503  typename ResolveLpToExtremePoint = ir::row_generation_solve_lp<>>
504 using bdmst_ir_components =
505  IRcomponents<Init, RoundCondition, RelaxContition, SetSolution,
506  SolveLPToExtremePoint, ResolveLpToExtremePoint>;
507 
508 namespace detail {
532 template <typename Oracle = lp::random_violated_separation_oracle,
533  typename Graph,
534  typename DegreeBounds, typename CostMap, typename VertexIndex,
535  typename SpanningTreeOutputIterator,
536  typename IRcomponents = bdmst_ir_components<>,
537  typename Visitor = trivial_visitor>
539  const Graph & g,
540  const DegreeBounds & deg_bounds,
541  CostMap cost_map,
542  VertexIndex vertex_index,
543  SpanningTreeOutputIterator result_spanning_tree,
545  Oracle oracle = Oracle(),
546  Visitor visitor = Visitor()) {
547 
548  auto bdmst = make_bounded_degree_mst(g, deg_bounds, cost_map, vertex_index, result_spanning_tree, oracle);
549  return solve_iterative_rounding(bdmst, std::move(components), std::move(visitor));
550 }
551 } // detail
552 
576 template <typename Oracle = lp::random_violated_separation_oracle,
577  typename Graph,
578  typename DegreeBounds, typename SpanningTreeOutputIterator,
579  typename IRcomponents = bdmst_ir_components<>,
580  typename Visitor = trivial_visitor, typename P, typename T,
581  typename R>
583  const Graph & g,
584  const DegreeBounds & deg_bounds,
585  const boost::bgl_named_params<P, T, R> & params,
586  SpanningTreeOutputIterator result_spanning_tree,
588  Oracle oracle = Oracle(),
589  Visitor visitor = Visitor()) {
590 
592  choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight),
593  choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index),
594  std::move(result_spanning_tree), std::move(components),
595  std::move(oracle), std::move(visitor));
596 }
597 
617 template <typename Oracle = lp::random_violated_separation_oracle, typename Graph,
618  typename DegreeBounds, typename SpanningTreeOutputIterator,
619  typename IRcomponents = bdmst_ir_components<>,
620  typename Visitor = trivial_visitor>
622  const Graph & g,
623  const DegreeBounds & deg_bounds,
624  SpanningTreeOutputIterator result_spanning_tree,
626  Oracle oracle = Oracle(),
627  Visitor visitor = Visitor()) {
628 
629  return bounded_degree_mst_iterative_rounding(g, deg_bounds,
630  boost::no_named_parameters(), std::move(result_spanning_tree),
631  std::move(components), std::move(oracle), std::move(visitor));
632 }
633 
634 }
635 }
636 #endif // PAAL_BOUNDED_DEGREE_MST_HPP
const EdgeMapOriginal & get_original_edges_map() const
The class for solving the Bounded Degree MST problem using Iterative Rounding.
decltype(std::declval< DegreeBounds >()(get(std::declval< VertexIndex >(), std::declval< Vertex >()))) get_degree_bound(Vertex v)
decltype(get(std::declval< CostMap >(), std::declval< Edge >())) get_cost(Edge e)
const CandidateList & get_violation_candidates(const Problem &problem, const LP &lp)
The common LP solvers base class. Responsible for:
Definition: lp_base.hpp:55
const EdgeMap & get_edge_map() const
col_id add_column(double cost_coef=0, double lb=0., double ub=lp_traits::PLUS_INF, const std::string &name="")
Definition: lp_base.hpp:92
bool operator()(Problem &problem, const LP &lp, lp::row_id row)
void bind_vertex_to_row(Vertex v, lp::row_id row)
const VertexIndex & get_index() const
bounded_degree_mst(const Graph &g, const DegreeBounds &deg_bounds, CostMap cost_map, VertexIndex index, SpanningTreeOutputIterator result_spanning_tree, Oracle oracle=Oracle{})
void bind_edge_to_col(Edge e, lp::col_id col)
void operator()(Problem &problem, LP &lp)
void set_lp_name(const std::string problem_name)
Definition: lp_base.hpp:78
bounded_degree_mst< Graph, DegreeBounds, CostMap, VertexIndex, SpanningTreeOutputIterator, Oracle > make_bounded_degree_mst(const Graph &g, const DegreeBounds &deg_bounds, CostMap cost_map, VertexIndex vertex_index, SpanningTreeOutputIterator result_spanning_tree, Oracle oracle=Oracle())
Creates a bounded_degree_mst object. Non-named version.
auto make_bounded_degree_mst(const Graph &g, const DegreeBounds &deg_bounds, const boost::bgl_named_params< P, T, R > &params, SpanningTreeOutputIterator result_spanning_tree, Oracle oracle=Oracle()) -> bounded_degree_mst< Graph, DegreeBounds, decltype(choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight)), decltype(choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index)), SpanningTreeOutputIterator, Oracle >
utils::compare< double > get_compare() const
IRResult bounded_degree_mst_iterative_rounding(const Graph &g, const DegreeBounds &deg_bounds, CostMap cost_map, VertexIndex vertex_index, SpanningTreeOutputIterator result_spanning_tree, IRcomponents components=IRcomponents(), Oracle oracle=Oracle(), Visitor visitor=Visitor())
Solves the Bounded Degree MST problem using Iterative Rounding. Non-named version.
boost::optional< lp::col_id > edge_to_col(Edge e) const
int get_row_degree(row_id row) const
Definition: lp_base.hpp:246
typename components::type< Args...> IRcomponents
Iterative rounding components.
const ColSet & get_columns() const
Definition: lp_base.hpp:216
boost::optional< Vertex > row_to_vertex(lp::row_id row)
row_id add_row(const double_bounded_expression &constraint=double_bounded_expression{}, const std::string &name="")
Definition: lp_base.hpp:109
void add_violated_constraint(Candidate violating_pair, const Problem &problem, LP &lp)
bdmst_set_solution(double epsilon=bounded_degree_mst_compare_traits::EPSILON)
std::pair< lp::problem_type, IRSolutionCost > IRResult
IRResult solve_iterative_rounding(Problem &problem, IRcomponents components, Visitor visitor=Visitor())
Solves an Iterative Rounding problem.
bool e(T a, T b) const
equals
Definition: floating.hpp:33
void remove_row(lp::row_id row_id)
IRResult bounded_degree_mst_iterative_rounding(const Graph &g, const DegreeBounds &deg_bounds, const boost::bgl_named_params< P, T, R > &params, SpanningTreeOutputIterator result_spanning_tree, IRcomponents components=IRcomponents(), Oracle oracle=Oracle(), Visitor visitor=Visitor())
Solves the Bounded Degree MST problem using Iterative Rounding. Named version.
void operator()(Problem &problem, const GetSolution &solution)
const Graph & get_graph() const
void remove_column(lp::col_id col_id)
bdmst_round_condition(double epsilon=bounded_degree_mst_compare_traits::EPSILON)
Violation check_violation(Candidate candidate, const Problem &problem)
boost::optional< double > operator()(Problem &problem, const LP &lp, lp::col_id col)