Skeleton_blocker_contractor.h
1 /* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2  * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3  * Author(s): David Salinas
4  *
5  * Copyright (C) 2014 Inria
6  *
7  * Modification(s):
8  * - 2019/08 Vincent Rouvreau: Fix issue #10 for CGAL
9  * - YYYY/MM Author: Description of the modification
10  */
11 
12 #ifndef SKELETON_BLOCKER_CONTRACTOR_H_
13 #define SKELETON_BLOCKER_CONTRACTOR_H_
14 
15 #include <gudhi/Contraction/Edge_profile.h>
16 #include <gudhi/Contraction/policies/Cost_policy.h>
17 #include <gudhi/Contraction/policies/Edge_length_cost.h>
18 #include <gudhi/Contraction/policies/Placement_policy.h>
19 #include <gudhi/Contraction/policies/First_vertex_placement.h>
20 #include <gudhi/Contraction/policies/Valid_contraction_policy.h>
21 #include <gudhi/Contraction/policies/Dummy_valid_contraction.h> // xxx remove
22 #include <gudhi/Contraction/policies/Link_condition_valid_contraction.h> // xxx remove
23 #include <gudhi/Contraction/policies/Contraction_visitor.h>
24 
25 #include <gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h>
26 #include <gudhi/Debug_utils.h>
27 
28 // todo remove the queue to be independent from cgal
29 #include <CGAL/Modifiable_priority_queue.h>
30 #include <CGAL/version.h> // for CGAL_VERSION_NR
31 
32 #include <boost/scoped_array.hpp>
33 #include <boost/scoped_ptr.hpp>
34 
35 #include <memory>
36 #include <cassert>
37 #include <list>
38 #include <utility> // for pair
39 #include <vector>
40 
41 // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10
42 #if CGAL_VERSION_NR < 1041101000
43 # error Skeleton_blocker_contractor is only available for CGAL >= 4.11
44 #endif
45 
46 namespace Gudhi {
47 
48 namespace contraction {
49 
50 template <class Profile>
51 Placement_policy<Profile>* make_first_vertex_placement() {
52  return new First_vertex_placement<Profile>();
53 }
54 
55 template <class Profile>
56 Valid_contraction_policy<Profile>* make_link_valid_contraction() {
57  return new Link_condition_valid_contraction<Profile>();
58 }
59 
63 template <class Profile>
65  public:
66  typedef typename Profile::Point Point;
67  typedef typename Profile::Complex Complex;
68  typedef typename Complex::Vertex_handle Vertex_handle;
69 
70  void on_contracted(const Profile &profile, boost::optional< Point > placement) override {
71  profile.complex().remove_all_popable_blockers(profile.v0_handle());
72  }
73 };
74 
75 template <class Profile>
76 Contraction_visitor<Profile>* make_remove_popable_blockers_visitor() {
78 }
79 
96 template<class GeometricSimplifiableComplex, class EdgeProfile = Edge_profile<GeometricSimplifiableComplex>>
98 typename GeometricSimplifiableComplex::Vertex_handle> {
99  GeometricSimplifiableComplex& complex_;
100 
101  public:
102  typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex;
103  typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle;
104  typedef typename GeometricSimplifiableComplex::Simplex Simplex;
105  typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle;
106  typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge;
107  typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle;
108  typedef typename GeometricSimplifiableComplex::Point Point;
109 
110  typedef EdgeProfile Profile;
111 
112 
117  typedef Edge_profile_factory<EdgeProfile> Edge_profile_factory_;
118 
119 
120 
121  typedef boost::optional<double> Cost_type;
122  typedef boost::optional<Point> Placement_type;
123 
124  typedef size_t size_type;
125 
127 
128  private:
129  struct Compare_id {
130  Compare_id() : algorithm_(0) { }
131 
132  Compare_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
133 
134  bool operator()(Edge_handle a, Edge_handle b) const {
135  return algorithm_->get_undirected_edge_id(a) < algorithm_->get_undirected_edge_id(b);
136  }
137 
138  Self const* algorithm_;
139  };
140 
141  struct Compare_cost {
142  Compare_cost() : algorithm_(0) { }
143 
144  Compare_cost(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
145 
146  bool operator()(Edge_handle a, Edge_handle b) const {
147  // NOTE: A cost is an optional<> value.
148  // Absent optionals are ordered first; that is, "none < T" and "T > none" for any defined T != none.
149  // In consequence, edges with undefined costs will be promoted to the top of the priority queue and popped out
150  // first.
151  return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost();
152  }
153 
154  Self const* algorithm_;
155  };
156 
157  struct Undirected_edge_id : boost::put_get_helper<size_type, Undirected_edge_id> {
158  typedef boost::readable_property_map_tag category;
159  typedef size_type value_type;
160  typedef size_type reference;
161  typedef Edge_handle key_type;
162 
163  Undirected_edge_id() : algorithm_(0) { }
164 
165  Undirected_edge_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
166 
167  size_type operator[](Edge_handle e) const {
168  return algorithm_->get_undirected_edge_id(e);
169  }
170 
171  Self const* algorithm_;
172  };
173 
174 #if CGAL_VERSION_NR < 1050500000
175  typedef CGAL::Modifiable_priority_queue<Edge_handle, Compare_cost, Undirected_edge_id> PQ;
176 #else
177  typedef CGAL::Modifiable_priority_queue<Edge_handle, Compare_cost, Undirected_edge_id, CGAL::CGAL_BOOST_PENDING_RELAXED_HEAP> PQ;
178 #endif
179 
180  typedef bool pq_handle;
181 
182 
183  // An Edge_data is associated with EVERY edge in the complex (collapsible or not).
184  // It relates the edge with the PQ-handle needed to update the priority queue
185  // It also relates the edge with a policy-based cache
186 
187  class Edge_data {
188  public:
189  Edge_data() : PQHandle_(), cost_() { }
190 
191  Cost_type const& cost() const {
192  return cost_;
193  }
194 
195  Cost_type & cost() {
196  return cost_;
197  }
198 
199  pq_handle PQ_handle() const {
200  return PQHandle_;
201  }
202 
203  bool is_in_PQ() const {
204  return PQHandle_ != false;
205  }
206 
207  void set_PQ_handle(pq_handle h) {
208  PQHandle_ = h;
209  }
210 
211  void reset_PQ_handle() {
212  PQHandle_ = false;
213  }
214 
215  private:
216  pq_handle PQHandle_;
217  Cost_type cost_;
218  };
219  typedef Edge_data* Edge_data_ptr;
220  typedef boost::scoped_array<Edge_data> Edge_data_array;
221 
222  int get_undirected_edge_id(Edge_handle edge) const {
223  return complex_[edge].index();
224  }
225 
226  const Edge_data& get_data(Edge_handle edge) const {
227  return edge_data_array_[get_undirected_edge_id(edge)];
228  }
229 
230  Edge_data& get_data(Edge_handle edge) {
231  return edge_data_array_[get_undirected_edge_id(edge)];
232  }
233 
234  Cost_type get_cost(const Profile & profile) const {
235  return (*cost_policy_)(profile, get_placement(profile));
236  }
237 
238  Profile create_profile(Edge_handle edge) const {
239  if (edge_profile_factory_)
240  return edge_profile_factory_->make_profile(complex_, edge);
241  else
242  return Profile(complex_, edge);
243  }
244 
245  void insert_in_PQ(Edge_handle edge, Edge_data& data) {
246  heap_PQ_->push(edge);
247  data.set_PQ_handle(true);
248  ++current_num_edges_heap_;
249  }
250 
251  void update_in_PQ(Edge_handle edge, Edge_data& data) {
252 #if CGAL_VERSION_NR < 1050500000
253  data.set_PQ_handle(heap_PQ_->update(edge, data.PQ_handle()));
254 #else
255  heap_PQ_->update(edge);
256 #endif
257  }
258 
259  void remove_from_PQ(Edge_handle edge, Edge_data& data) {
260  heap_PQ_->erase(edge);
261  data.set_PQ_handle(false);
262  --current_num_edges_heap_;
263  }
264 
265  boost::optional<Edge_handle> pop_from_PQ() {
266  boost::optional<Edge_handle> edge;
267  if (!heap_PQ_->empty()) {
268  edge = heap_PQ_->top();
269  heap_PQ_->pop();
270  get_data(*edge).reset_PQ_handle();
271  }
272  return edge;
273  }
274 
275  private:
283  void collect_edges() {
284  //
285  // Loop over all the edges in the complex in the heap
286  //
287  size_type size = complex_.num_edges();
288  DBG("Collecting edges ...");
289  DBGMSG("num edges :", size);
290 
291  edge_data_array_.reset(new Edge_data[size]);
292 
293  heap_PQ_.reset(new PQ(size, Compare_cost(this), Undirected_edge_id(this)));
294 
295  std::size_t id = 0;
296 
297  // xxx do a parallel for
298  for (auto edge : complex_.edge_range()) {
299  complex_[edge].index() = id++;
300  Profile const& profile = create_profile(edge);
301  Edge_data& data = get_data(edge);
302  data.cost() = get_cost(profile);
303  ++initial_num_edges_heap_;
304  insert_in_PQ(edge, data);
305  if (contraction_visitor_) contraction_visitor_->on_collected(profile, data.cost());
306  }
307 
308  DBG("Edges collected.");
309  }
310 
311  bool should_stop(double lCost, const Profile &profile) const {
312  return false;
313  }
314 
315  boost::optional<Point> get_placement(const Profile& profile) const {
316  return (*placement_policy_)(profile);
317  }
318 
319  bool is_contraction_valid(Profile const& profile, Placement_type placement) const {
320  if (!valid_contraction_policy_) return true;
321  return (*valid_contraction_policy_)(profile, placement);
322  }
323 
324 
325  public:
334  void contract_edges(int num_max_contractions = -1) {
335  DBG("\n\nContract edges");
336  int num_contraction = 0;
337 
338  bool unspecified_num_contractions = (num_max_contractions == -1);
339  //
340  // Pops and processes each edge from the PQ
341  //
342  boost::optional<Edge_handle> edge;
343  while ((edge = pop_from_PQ()) && ((num_contraction < num_max_contractions) || (unspecified_num_contractions))) {
344  Profile const& profile = create_profile(*edge);
345  Cost_type cost(get_data(*edge).cost());
346  if (contraction_visitor_) contraction_visitor_->on_selected(profile, cost, 0, 0);
347 
348  DBGMSG("\n\n---- Pop edge - num vertices :", complex_.num_vertices());
349 
350  if (cost) {
351  DBGMSG("sqrt(cost):", std::sqrt(*cost));
352  if (should_stop(*cost, profile)) {
353  if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
354  DBG("should_stop");
355  break;
356  }
357  Placement_type placement = get_placement(profile);
358  if (is_contraction_valid(profile, placement) && placement) {
359  DBG("contraction_valid");
360  contract_edge(profile, placement);
361  ++num_contraction;
362  } else {
363  DBG("contraction not valid");
364  if (contraction_visitor_) contraction_visitor_->on_non_valid(profile);
365  }
366  } else {
367  DBG("uncomputable cost");
368  }
369  }
370  if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
371  }
372 
373  bool is_in_heap(Edge_handle edge) const {
374  if (heap_PQ_->empty()) {
375  return false;
376  } else {
377  return edge_data_array_[get_undirected_edge_id(edge)].is_in_PQ();
378  }
379  }
380 
381  bool is_heap_empty() const {
382  return heap_PQ_->empty();
383  }
384 
390  boost::optional<std::pair<Edge_handle, Placement_type > > top_edge() {
391  boost::optional<std::pair<Edge_handle, Placement_type > > res;
392 
393  if (!heap_PQ_->empty()) {
394  auto edge = heap_PQ_->top();
395  Profile const& profile = create_profile(edge);
396  Placement_type placement = get_placement(profile);
397  res = std::make_pair(edge, placement);
398  DBGMSG("top edge:", complex_[edge]);
399  }
400  return res;
401  }
402 
409  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex)
410  : complex_(complex),
411  cost_policy_(new Edge_length_cost<Profile>),
412  placement_policy_(new First_vertex_placement<Profile>),
413  valid_contraction_policy_(new Link_condition_valid_contraction<Profile>),
414  contraction_visitor_(new Contraction_visitor_()),
415  edge_profile_factory_(0),
416  initial_num_edges_heap_(0),
417  current_num_edges_heap_(0) {
418  complex_.set_visitor(this);
419  if (contraction_visitor_) contraction_visitor_->on_started(complex);
420  collect_edges();
421  }
422 
427  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex,
428  Cost_policy_ *cost_policy,
429  Placement_policy_ * placement_policy = new First_vertex_placement<Profile>,
430  Valid_contraction_policy_ * valid_contraction_policy =
432  Contraction_visitor_* contraction_visitor = new Contraction_visitor_(),
433  Edge_profile_factory_* edge_profile_factory = NULL) :
434  complex_(complex),
435  cost_policy_(cost_policy),
436  placement_policy_(placement_policy),
437  valid_contraction_policy_(valid_contraction_policy),
438  contraction_visitor_(contraction_visitor),
439  edge_profile_factory_(edge_profile_factory),
440  initial_num_edges_heap_(0),
441  current_num_edges_heap_(0) {
442  complex_.set_visitor(this);
443  if (contraction_visitor) contraction_visitor->on_started(complex);
444  collect_edges();
445  }
446 
448  complex_.set_visitor(0);
449  }
450 
451  private:
452  void contract_edge(const Profile& profile, Placement_type placement) {
453  if (contraction_visitor_) contraction_visitor_->on_contracting(profile, placement);
454 
455  assert(complex_.contains_vertex(profile.v0_handle()));
456  assert(complex_.contains_vertex(profile.v1_handle()));
457  assert(placement);
458 
459  profile.complex().point(profile.v0_handle()) = *placement;
460 
461  // remark : this is not necessary since v1 will be deactivated
462  // profile.complex().point(profile.v1_handle()) = *placement;
463 
464  complex_.contract_edge(profile.v0_handle(), profile.v1_handle());
465 
466  assert(complex_.contains_vertex(profile.v0_handle()));
467  assert(!complex_.contains_vertex(profile.v1_handle()));
468 
469  update_changed_edges();
470 
471  // the visitor could do something as complex_.remove_popable_blockers();
472  if (contraction_visitor_) contraction_visitor_->on_contracted(profile, placement);
473  }
474 
475  private:
476  // every time the visitor's method on_changed_edge is called, it adds an
477  // edge to changed_edges_
478  std::vector< Edge_handle > changed_edges_;
479 
484  inline void on_changed_edge(Vertex_handle a, Vertex_handle b) override {
485  boost::optional<Edge_handle> ab(complex_[std::make_pair(a, b)]);
486  assert(ab);
487  changed_edges_.push_back(*ab);
488  }
489 
490  void update_changed_edges() {
491  // xxx do a parallel for
492  DBG("update edges");
493 
494  // sequential loop
495  for (auto ab : changed_edges_) {
496  // 1-get the Edge_handle corresponding to ab
497  // 2-change the data in mEdgeArray[ab.id()]
498  // 3-update the heap
499  Edge_data& data = get_data(ab);
500  Profile const& profile = create_profile(ab);
501  data.cost() = get_cost(profile);
502  if (data.is_in_PQ()) {
503  update_in_PQ(ab, data);
504  } else {
505  insert_in_PQ(ab, data);
506  }
507  }
508  changed_edges_.clear();
509  }
510 
511 
512  private:
513  void on_remove_edge(Vertex_handle a, Vertex_handle b) override {
514  boost::optional<Edge_handle> ab((complex_[std::make_pair(a, b)]));
515  assert(ab);
516  Edge_data& lData = get_data(*ab);
517  if (lData.is_in_PQ()) {
518  remove_from_PQ(*ab, lData);
519  }
520  }
521 
522  private:
528  void on_swaped_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) override {
529  boost::optional<Edge_handle> ax(complex_[std::make_pair(a, x)]);
530  boost::optional<Edge_handle> bx(complex_[std::make_pair(b, x)]);
531  assert(ax && bx);
532  complex_[*ax].index() = complex_[*bx].index();
533  }
534 
535  private:
541  void on_delete_blocker(const Simplex * blocker) override {
542  // we go for all pairs xy that belongs to the blocker
543  // note that such pairs xy are necessarily edges of the complex
544  // by definition of a blocker
545 
546  // todo uniqument utile pour la link condition
547  // laisser a l'utilisateur ? boolean update_heap_on_removed_blocker?
548  Simplex blocker_copy(*blocker);
549  for (auto x = blocker_copy.begin(); x != blocker_copy.end(); ++x) {
550  for (auto y = x; ++y != blocker_copy.end();) {
551  auto edge_descr(complex_[std::make_pair(*x, *y)]);
552  assert(edge_descr);
553  Edge_data& data = get_data(*edge_descr);
554  Profile const& profile = create_profile(*edge_descr);
555  data.cost() = get_cost(profile);
556 
557  // If the edge is already in the heap
558  // its priority has not changed.
559  // If the edge is not present, we reinsert it
560  // remark : we could also reinsert the edge
561  // only if it is valid
562  if (!data.is_in_PQ()) {
563  insert_in_PQ(*edge_descr, data);
564  }
565  }
566  }
567  }
568 
569 
570  private:
571  std::shared_ptr<Cost_policy_> cost_policy_;
572  std::shared_ptr<Placement_policy_> placement_policy_;
573  std::shared_ptr<Valid_contraction_policy_> valid_contraction_policy_;
574  std::shared_ptr<Contraction_visitor_> contraction_visitor_;
575 
576  // in case the user wants to do something special when the edge profile
577  // are created (for instance add some info)
578  std::shared_ptr<Edge_profile_factory_> edge_profile_factory_;
579  Edge_data_array edge_data_array_;
580 
581  boost::scoped_ptr<PQ> heap_PQ_;
582  int initial_num_edges_heap_;
583  int current_num_edges_heap_;
584 };
585 
586 } // namespace contraction
587 
588 } // namespace Gudhi
589 
590 #endif // SKELETON_BLOCKER_CONTRACTOR_H_
Visitor to remove popable blockers after an edge contraction.
Definition: Skeleton_blocker_contractor.h:64
Interface for a visitor of the edge contraction process.
Definition: Contraction_visitor.h:27
Policy to specify the cost of contracting an edge.
Definition: Cost_policy.h:25
return a cost corresponding to the squared length of the edge
Definition: Edge_length_cost.h:24
Places the contracted point onto the first point of the edge.
Definition: First_vertex_placement.h:24
Policy to specify where the merged point had to be placed after an edge contraction.
Definition: Placement_policy.h:25
Class that allows to contract iteratively edges of a simplicial complex.
Definition: Skeleton_blocker_contractor.h:98
void contract_edges(int num_max_contractions=-1)
Contract edges.
Definition: Skeleton_blocker_contractor.h:334
boost::optional< std::pair< Edge_handle, Placement_type > > top_edge()
Returns an Edge_handle and a Placement_type. This pair consists in the edge with the lowest cost in t...
Definition: Skeleton_blocker_contractor.h:390
Skeleton_blocker_contractor(GeometricSimplifiableComplex &complex, Cost_policy_ *cost_policy, Placement_policy_ *placement_policy=new First_vertex_placement< Profile >, Valid_contraction_policy_ *valid_contraction_policy=new Link_condition_valid_contraction< Profile >, Contraction_visitor_ *contraction_visitor=new Contraction_visitor_(), Edge_profile_factory_ *edge_profile_factory=NULL)
Constructor with customed policies.
Definition: Skeleton_blocker_contractor.h:427
Skeleton_blocker_contractor(GeometricSimplifiableComplex &complex)
Constructor with default policies.
Definition: Skeleton_blocker_contractor.h:409
Policy to specify if an edge contraction is valid or not.
Definition: Valid_contraction_policy.h:23
A dummy visitor of a simplicial complex that does nothing.
Definition: Skeleton_blocker_complex_visitor.h:65
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14
Handle type for the vertices of a cell complex.
Definition: VertexHandle.h:15