Gudhi  1.2.0
 All Classes Functions Variables Typedefs Friends Groups Pages
Skeleton_blocker_contractor.h
1 /* This file is part of the Gudhi Library. The Gudhi library
2  * (Geometric Understanding in Higher Dimensions) is a generic C++
3  * library for computational topology.
4  *
5  * Author(s): David Salinas
6  *
7  * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #ifndef SKELETON_BLOCKER_CONTRACTOR_H_
24 #define SKELETON_BLOCKER_CONTRACTOR_H_
25 
26 // todo remove the queue to be independent from cgald
27 #include <gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h>
28 
29 #include <gudhi/Contraction/Edge_profile.h>
30 #include <gudhi/Contraction/policies/Cost_policy.h>
31 #include <gudhi/Contraction/policies/Edge_length_cost.h>
32 #include <gudhi/Contraction/policies/Placement_policy.h>
33 #include <gudhi/Contraction/policies/First_vertex_placement.h>
34 #include <gudhi/Contraction/policies/Valid_contraction_policy.h>
35 #include <gudhi/Contraction/policies/Dummy_valid_contraction.h> // xxx remove
36 #include <gudhi/Contraction/policies/Link_condition_valid_contraction.h> // xxx remove
37 #include <gudhi/Contraction/policies/Contraction_visitor.h>
38 
39 #include <gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h>
40 #include <gudhi/Utils.h>
41 
42 
43 #include <boost/scoped_array.hpp>
44 #include <boost/scoped_ptr.hpp>
45 
46 #include <memory>
47 #include <cassert>
48 #include <list>
49 #include <utility> // for pair
50 #include <vector>
51 
52 namespace Gudhi {
53 
54 namespace contraction {
55 
56 template <class Profile>
57 Placement_policy<Profile>* make_first_vertex_placement() {
58  return new First_vertex_placement<Profile>();
59 }
60 
61 template <class Profile>
62 Valid_contraction_policy<Profile>* make_link_valid_contraction() {
63  return new Link_condition_valid_contraction<Profile>();
64 }
65 
69 template <class Profile>
71  public:
72  typedef typename Profile::Point Point;
73  typedef typename Profile::Complex Complex;
74  typedef typename Complex::Vertex_handle Vertex_handle;
75 
76  void on_contracted(const Profile &profile, boost::optional< Point > placement) override {
77  profile.complex().remove_all_popable_blockers(profile.v0_handle());
78  }
79 };
80 
81 template <class Profile>
82 Contraction_visitor<Profile>* make_remove_popable_blockers_visitor() {
84 }
85 
102 template<class GeometricSimplifiableComplex, class EdgeProfile = Edge_profile<GeometricSimplifiableComplex>>
104 typename GeometricSimplifiableComplex::Vertex_handle> {
105  GeometricSimplifiableComplex& complex_;
106 
107  public:
108  typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex;
109  typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle;
110  typedef typename GeometricSimplifiableComplex::Simplex_handle Simplex_handle;
111  typedef typename GeometricSimplifiableComplex::Simplex_handle_iterator Simplex_handle_iterator;
112 
113 
114 
115  typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle;
116 
117  typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge;
118  typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle;
119  typedef typename GeometricSimplifiableComplex::Point Point;
120 
121  typedef EdgeProfile Profile;
122 
123 
128  typedef Edge_profile_factory<EdgeProfile> Edge_profile_factory_;
129 
130 
131 
132  typedef boost::optional<double> Cost_type;
133  typedef boost::optional<Point> Placement_type;
134 
135  typedef size_t size_type;
136 
138 
139  private:
140  struct Compare_id {
141  Compare_id() : algorithm_(0) { }
142 
143  Compare_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
144 
145  bool operator()(Edge_handle a, Edge_handle b) const {
146  return algorithm_->get_undirected_edge_id(a) < algorithm_->get_undirected_edge_id(b);
147  }
148 
149  Self const* algorithm_;
150  };
151 
152  struct Compare_cost {
153  Compare_cost() : algorithm_(0) { }
154 
155  Compare_cost(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
156 
157  bool operator()(Edge_handle a, Edge_handle b) const {
158  // NOTE: A cost is an optional<> value.
159  // Absent optionals are ordered first; that is, "none < T" and "T > none" for any defined T != none.
160  // In consequence, edges with undefined costs will be promoted to the top of the priority queue and popped out
161  // first.
162  return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost();
163  }
164 
165  Self const* algorithm_;
166  };
167 
168  struct Undirected_edge_id : boost::put_get_helper<size_type, Undirected_edge_id> {
169  typedef boost::readable_property_map_tag category;
170  typedef size_type value_type;
171  typedef size_type reference;
172  typedef Edge_handle key_type;
173 
174  Undirected_edge_id() : algorithm_(0) { }
175 
176  Undirected_edge_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
177 
178  size_type operator[](Edge_handle e) const {
179  return algorithm_->get_undirected_edge_id(e);
180  }
181 
182  Self const* algorithm_;
183  };
184 
185  typedef CGAL::Modifiable_priority_queue<Edge_handle, Compare_cost, Undirected_edge_id> PQ;
186  typedef typename PQ::handle pq_handle;
187 
188 
189  // An Edge_data is associated with EVERY edge in the complex (collapsible or not).
190  // It relates the edge with the PQ-handle needed to update the priority queue
191  // It also relates the edge with a policy-based cache
192 
193  class Edge_data {
194  public:
195  Edge_data() : PQHandle_(), cost_() { }
196 
197  Cost_type const& cost() const {
198  return cost_;
199  }
200 
201  Cost_type & cost() {
202  return cost_;
203  }
204 
205  pq_handle PQ_handle() const {
206  return PQHandle_;
207  }
208 
209  bool is_in_PQ() const {
210  return PQHandle_ != PQ::null_handle();
211  }
212 
213  void set_PQ_handle(pq_handle h) {
214  PQHandle_ = h;
215  }
216 
217  void reset_PQ_handle() {
218  PQHandle_ = PQ::null_handle();
219  }
220 
221  private:
222  pq_handle PQHandle_;
223  Cost_type cost_;
224  };
225  typedef Edge_data* Edge_data_ptr;
226  typedef boost::scoped_array<Edge_data> Edge_data_array;
227 
228  int get_undirected_edge_id(Edge_handle edge) const {
229  return complex_[edge].index();
230  }
231 
232  const Edge_data& get_data(Edge_handle edge) const {
233  return edge_data_array_[get_undirected_edge_id(edge)];
234  }
235 
236  Edge_data& get_data(Edge_handle edge) {
237  return edge_data_array_[get_undirected_edge_id(edge)];
238  }
239 
240  Cost_type get_cost(const Profile & profile) const {
241  return (*cost_policy_)(profile, get_placement(profile));
242  }
243 
244  Profile create_profile(Edge_handle edge) const {
245  if (edge_profile_factory_)
246  return edge_profile_factory_->make_profile(complex_, edge);
247  else
248  return Profile(complex_, edge);
249  }
250 
251  void insert_in_PQ(Edge_handle edge, Edge_data& data) {
252  data.set_PQ_handle(heap_PQ_->push(edge));
253  ++current_num_edges_heap_;
254  }
255 
256  void update_in_PQ(Edge_handle edge, Edge_data& data) {
257  data.set_PQ_handle(heap_PQ_->update(edge, data.PQ_handle()));
258  }
259 
260  void remove_from_PQ(Edge_handle edge, Edge_data& data) {
261  data.set_PQ_handle(heap_PQ_->erase(edge, data.PQ_handle()));
262  --current_num_edges_heap_;
263  }
264 
265  boost::optional<Edge_handle> pop_from_PQ() {
266  boost::optional<Edge_handle> edge = heap_PQ_->extract_top();
267  if (edge)
268  get_data(*edge).reset_PQ_handle();
269  return edge;
270  }
271 
272  private:
280  void collect_edges() {
281  //
282  // Loop over all the edges in the complex in the heap
283  //
284  size_type size = complex_.num_edges();
285  DBG("Collecting edges ...");
286  DBGMSG("num edges :", size);
287 
288  edge_data_array_.reset(new Edge_data[size]);
289 
290  heap_PQ_.reset(new PQ(size, Compare_cost(this), Undirected_edge_id(this)));
291 
292  std::size_t id = 0;
293 
294  // xxx do a parralel for
295  for (auto edge : complex_.edge_range()) {
296  complex_[edge].index() = id++;
297  Profile const& profile = create_profile(edge);
298  Edge_data& data = get_data(edge);
299  data.cost() = get_cost(profile);
300  ++initial_num_edges_heap_;
301  insert_in_PQ(edge, data);
302  if (contraction_visitor_) contraction_visitor_->on_collected(profile, data.cost());
303  }
304 
305  DBG("Edges collected.");
306  }
307 
308  bool should_stop(double lCost, const Profile &profile) const {
309  return false;
310  }
311 
312  boost::optional<Point> get_placement(const Profile& profile) const {
313  return (*placement_policy_)(profile);
314  }
315 
316  bool is_contraction_valid(Profile const& profile, Placement_type placement) const {
317  if (!valid_contraction_policy_) return true;
318  return (*valid_contraction_policy_)(profile, placement);
319  }
320 
321 
322  public:
331  void contract_edges(int num_max_contractions = -1) {
332  DBG("\n\nContract edges");
333  int num_contraction = 0;
334 
335  bool unspecified_num_contractions = (num_max_contractions == -1);
336  //
337  // Pops and processes each edge from the PQ
338  //
339  boost::optional<Edge_handle> edge;
340  while ((edge = pop_from_PQ()) && ((num_contraction < num_max_contractions) || (unspecified_num_contractions))) {
341  Profile const& profile = create_profile(*edge);
342  Cost_type cost(get_data(*edge).cost());
343  if (contraction_visitor_) contraction_visitor_->on_selected(profile, cost, 0, 0);
344 
345  DBGMSG("\n\n---- Pop edge - num vertices :", complex_.num_vertices());
346 
347  if (cost) {
348  DBGMSG("sqrt(cost):", std::sqrt(*cost));
349  if (should_stop(*cost, profile)) {
350  if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
351  DBG("should_stop");
352  break;
353  }
354  Placement_type placement = get_placement(profile);
355  if (is_contraction_valid(profile, placement) && placement) {
356  DBG("contraction_valid");
357  contract_edge(profile, placement);
358  ++num_contraction;
359  } else {
360  DBG("contraction not valid");
361  if (contraction_visitor_) contraction_visitor_->on_non_valid(profile);
362  }
363  } else {
364  DBG("uncomputable cost");
365  }
366  }
367  if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
368  }
369 
370  bool is_in_heap(Edge_handle edge) const {
371  if (heap_PQ_->empty()) {
372  return false;
373  } else {
374  return edge_data_array_[get_undirected_edge_id(edge)].is_in_PQ();
375  }
376  }
377 
378  bool is_heap_empty() const {
379  return heap_PQ_->empty();
380  }
381 
387  boost::optional<std::pair<Edge_handle, Placement_type > > top_edge() {
388  boost::optional<std::pair<Edge_handle, Placement_type > > res;
389 
390  if (!heap_PQ_->empty()) {
391  auto edge = heap_PQ_->top();
392  Profile const& profile = create_profile(edge);
393  Placement_type placement = get_placement(profile);
394  res = std::make_pair(edge, placement);
395  DBGMSG("top edge:", complex_[edge]);
396  }
397  return res;
398  }
399 
406  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex)
407  : complex_(complex),
408  cost_policy_(new Edge_length_cost<Profile>),
409  placement_policy_(new First_vertex_placement<Profile>),
410  valid_contraction_policy_(new Link_condition_valid_contraction<Profile>),
411  contraction_visitor_(new Contraction_visitor_()),
412  edge_profile_factory_(0),
413  initial_num_edges_heap_(0),
414  current_num_edges_heap_(0) {
415  complex_.set_visitor(this);
416  if (contraction_visitor_) contraction_visitor_->on_started(complex);
417  collect_edges();
418  }
419 
424  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex,
425  Cost_policy_ *cost_policy,
426  Placement_policy_ * placement_policy = new First_vertex_placement<Profile>,
427  Valid_contraction_policy_ * valid_contraction_policy =
429  Contraction_visitor_* contraction_visitor = new Contraction_visitor_(),
430  Edge_profile_factory_* edge_profile_factory = NULL) :
431  complex_(complex),
432  cost_policy_(cost_policy),
433  placement_policy_(placement_policy),
434  valid_contraction_policy_(valid_contraction_policy),
435  contraction_visitor_(contraction_visitor),
436  edge_profile_factory_(edge_profile_factory),
437  initial_num_edges_heap_(0),
438  current_num_edges_heap_(0) {
439  complex_.set_visitor(this);
440  if (contraction_visitor) contraction_visitor->on_started(complex);
441  collect_edges();
442  }
443 
445  complex_.set_visitor(0);
446  }
447 
448  private:
449  void contract_edge(const Profile& profile, Placement_type placement) {
450  if (contraction_visitor_) contraction_visitor_->on_contracting(profile, placement);
451 
452  assert(complex_.contains_vertex(profile.v0_handle()));
453  assert(complex_.contains_vertex(profile.v1_handle()));
454  assert(placement);
455 
456  profile.complex().point(profile.v0_handle()) = *placement;
457 
458  // remark : this is not necessary since v1 will be deactivated
459  // profile.complex().point(profile.v1_handle()) = *placement;
460 
461  complex_.contract_edge(profile.v0_handle(), profile.v1_handle());
462 
463  assert(complex_.contains_vertex(profile.v0_handle()));
464  assert(!complex_.contains_vertex(profile.v1_handle()));
465 
466  update_changed_edges();
467 
468  // the visitor could do something as complex_.remove_popable_blockers();
469  if (contraction_visitor_) contraction_visitor_->on_contracted(profile, placement);
470  }
471 
472  private:
473  // every time the visitor's method on_changed_edge is called, it adds an
474  // edge to changed_edges_
475  std::vector< Edge_handle > changed_edges_;
476 
481  inline void on_changed_edge(Vertex_handle a, Vertex_handle b) override {
482  boost::optional<Edge_handle> ab(complex_[std::make_pair(a, b)]);
483  assert(ab);
484  changed_edges_.push_back(*ab);
485  }
486 
487  void update_changed_edges() {
488  // xxx do a parralel for
489  DBG("update edges");
490 
491  // sequential loop
492  for (auto ab : changed_edges_) {
493  // 1-get the Edge_handle corresponding to ab
494  // 2-change the data in mEdgeArray[ab.id()]
495  // 3-update the heap
496  Edge_data& data = get_data(ab);
497  Profile const& profile = create_profile(ab);
498  data.cost() = get_cost(profile);
499  if (data.is_in_PQ()) {
500  update_in_PQ(ab, data);
501  } else {
502  insert_in_PQ(ab, data);
503  }
504  }
505  changed_edges_.clear();
506  }
507 
508 
509  private:
510  void on_remove_edge(Vertex_handle a, Vertex_handle b) override {
511  boost::optional<Edge_handle> ab((complex_[std::make_pair(a, b)]));
512  assert(ab);
513  Edge_data& lData = get_data(*ab);
514  if (lData.is_in_PQ()) {
515  remove_from_PQ(*ab, lData);
516  }
517  }
518 
519  private:
525  void on_swaped_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) override {
526  boost::optional<Edge_handle> ax(complex_[std::make_pair(a, x)]);
527  boost::optional<Edge_handle> bx(complex_[std::make_pair(b, x)]);
528  assert(ax && bx);
529  complex_[*ax].index() = complex_[*bx].index();
530  }
531 
532  private:
538  void on_delete_blocker(const Simplex_handle * blocker) override {
539  // we go for all pairs xy that belongs to the blocker
540  // note that such pairs xy are necessarily edges of the complex
541  // by definition of a blocker
542 
543  // todo uniqument utile pour la link condition
544  // laisser a l'utilisateur ? booleen update_heap_on_removed_blocker?
545  Simplex_handle blocker_copy(*blocker);
546  for (auto x = blocker_copy.begin(); x != blocker_copy.end(); ++x) {
547  for (auto y = x; ++y != blocker_copy.end();) {
548  auto edge_descr(complex_[std::make_pair(*x, *y)]);
549  assert(edge_descr);
550  Edge_data& data = get_data(*edge_descr);
551  Profile const& profile = create_profile(*edge_descr);
552  data.cost() = get_cost(profile);
553 
554  // If the edge is already in the heap
555  // its priority has not changed.
556  // If the edge is not present, we reinsert it
557  // remark : we could also reinsert the edge
558  // only if it is valid
559  if (!data.is_in_PQ()) {
560  insert_in_PQ(*edge_descr, data);
561  }
562  }
563  }
564  }
565 
566 
567  private:
568  std::shared_ptr<Cost_policy_> cost_policy_;
569  std::shared_ptr<Placement_policy_> placement_policy_;
570  std::shared_ptr<Valid_contraction_policy_> valid_contraction_policy_;
571  std::shared_ptr<Contraction_visitor_> contraction_visitor_;
572 
573  // in case the user wants to do something special when the edge profile
574  // are created (for instance add some info)
575  std::shared_ptr<Edge_profile_factory_> edge_profile_factory_;
576  Edge_data_array edge_data_array_;
577 
578  boost::scoped_ptr<PQ> heap_PQ_;
579  int initial_num_edges_heap_;
580  int current_num_edges_heap_;
581 };
582 
583 } // namespace contraction
584 
585 } // namespace Gudhi
586 
587 #endif // SKELETON_BLOCKER_CONTRACTOR_H_
Skeleton_blocker_contractor(GeometricSimplifiableComplex &complex)
Constructor with default policies.
Definition: Skeleton_blocker_contractor.h:406
Places the contracted point onto the first point of the edge.
Definition: First_vertex_placement.h:36
void contract_edges(int num_max_contractions=-1)
Contract edges.
Definition: Skeleton_blocker_contractor.h:331
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:424
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:387
Interface for a visitor of the edge contraction process.
Definition: Contraction_visitor.h:39
A dummy visitor of a simplicial complex that does nothing.
Definition: Skeleton_blocker_complex_visitor.h:75
Policy to specify the cost of contracting an edge.
Definition: Cost_policy.h:37
return a cost corresponding to the squared length of the edge
Definition: Edge_length_cost.h:36
Class that allows to contract iteratively edges of a simplicial complex.
Definition: Skeleton_blocker_contractor.h:103
Abstract simplex used in Skeleton blockers data-structure.
Definition: Skeleton_blocker_simplex.h:50
Definition: SkeletonBlockerDS.h:60
Policy to specify if an edge contraction is valid or not.
Definition: Valid_contraction_policy.h:35
Visitor to remove popable blockers after an edge contraction.
Definition: Skeleton_blocker_contractor.h:70
Policy to specify where the merged point had to be placed after an edge contraction.
Definition: Placement_policy.h:37