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
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/Debug_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 Simplex;
111  typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle;
112  typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge;
113  typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle;
114  typedef typename GeometricSimplifiableComplex::Point Point;
115 
116  typedef EdgeProfile Profile;
117 
118 
123  typedef Edge_profile_factory<EdgeProfile> Edge_profile_factory_;
124 
125 
126 
127  typedef boost::optional<double> Cost_type;
128  typedef boost::optional<Point> Placement_type;
129 
130  typedef size_t size_type;
131 
133 
134  private:
135  struct Compare_id {
136  Compare_id() : algorithm_(0) { }
137 
138  Compare_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
139 
140  bool operator()(Edge_handle a, Edge_handle b) const {
141  return algorithm_->get_undirected_edge_id(a) < algorithm_->get_undirected_edge_id(b);
142  }
143 
144  Self const* algorithm_;
145  };
146 
147  struct Compare_cost {
148  Compare_cost() : algorithm_(0) { }
149 
150  Compare_cost(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
151 
152  bool operator()(Edge_handle a, Edge_handle b) const {
153  // NOTE: A cost is an optional<> value.
154  // Absent optionals are ordered first; that is, "none < T" and "T > none" for any defined T != none.
155  // In consequence, edges with undefined costs will be promoted to the top of the priority queue and popped out
156  // first.
157  return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost();
158  }
159 
160  Self const* algorithm_;
161  };
162 
163  struct Undirected_edge_id : boost::put_get_helper<size_type, Undirected_edge_id> {
164  typedef boost::readable_property_map_tag category;
165  typedef size_type value_type;
166  typedef size_type reference;
167  typedef Edge_handle key_type;
168 
169  Undirected_edge_id() : algorithm_(0) { }
170 
171  Undirected_edge_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
172 
173  size_type operator[](Edge_handle e) const {
174  return algorithm_->get_undirected_edge_id(e);
175  }
176 
177  Self const* algorithm_;
178  };
179 
180  typedef CGAL::Modifiable_priority_queue<Edge_handle, Compare_cost, Undirected_edge_id> PQ;
181  typedef typename PQ::handle pq_handle;
182 
183 
184  // An Edge_data is associated with EVERY edge in the complex (collapsible or not).
185  // It relates the edge with the PQ-handle needed to update the priority queue
186  // It also relates the edge with a policy-based cache
187 
188  class Edge_data {
189  public:
190  Edge_data() : PQHandle_(), cost_() { }
191 
192  Cost_type const& cost() const {
193  return cost_;
194  }
195 
196  Cost_type & cost() {
197  return cost_;
198  }
199 
200  pq_handle PQ_handle() const {
201  return PQHandle_;
202  }
203 
204  bool is_in_PQ() const {
205  return PQHandle_ != PQ::null_handle();
206  }
207 
208  void set_PQ_handle(pq_handle h) {
209  PQHandle_ = h;
210  }
211 
212  void reset_PQ_handle() {
213  PQHandle_ = PQ::null_handle();
214  }
215 
216  private:
217  pq_handle PQHandle_;
218  Cost_type cost_;
219  };
220  typedef Edge_data* Edge_data_ptr;
221  typedef boost::scoped_array<Edge_data> Edge_data_array;
222 
223  int get_undirected_edge_id(Edge_handle edge) const {
224  return complex_[edge].index();
225  }
226 
227  const Edge_data& get_data(Edge_handle edge) const {
228  return edge_data_array_[get_undirected_edge_id(edge)];
229  }
230 
231  Edge_data& get_data(Edge_handle edge) {
232  return edge_data_array_[get_undirected_edge_id(edge)];
233  }
234 
235  Cost_type get_cost(const Profile & profile) const {
236  return (*cost_policy_)(profile, get_placement(profile));
237  }
238 
239  Profile create_profile(Edge_handle edge) const {
240  if (edge_profile_factory_)
241  return edge_profile_factory_->make_profile(complex_, edge);
242  else
243  return Profile(complex_, edge);
244  }
245 
246  void insert_in_PQ(Edge_handle edge, Edge_data& data) {
247  data.set_PQ_handle(heap_PQ_->push(edge));
248  ++current_num_edges_heap_;
249  }
250 
251  void update_in_PQ(Edge_handle edge, Edge_data& data) {
252  data.set_PQ_handle(heap_PQ_->update(edge, data.PQ_handle()));
253  }
254 
255  void remove_from_PQ(Edge_handle edge, Edge_data& data) {
256  data.set_PQ_handle(heap_PQ_->erase(edge, data.PQ_handle()));
257  --current_num_edges_heap_;
258  }
259 
260  boost::optional<Edge_handle> pop_from_PQ() {
261  boost::optional<Edge_handle> edge = heap_PQ_->extract_top();
262  if (edge)
263  get_data(*edge).reset_PQ_handle();
264  return edge;
265  }
266 
267  private:
275  void collect_edges() {
276  //
277  // Loop over all the edges in the complex in the heap
278  //
279  size_type size = complex_.num_edges();
280  DBG("Collecting edges ...");
281  DBGMSG("num edges :", size);
282 
283  edge_data_array_.reset(new Edge_data[size]);
284 
285  heap_PQ_.reset(new PQ(size, Compare_cost(this), Undirected_edge_id(this)));
286 
287  std::size_t id = 0;
288 
289  // xxx do a parralel for
290  for (auto edge : complex_.edge_range()) {
291  complex_[edge].index() = id++;
292  Profile const& profile = create_profile(edge);
293  Edge_data& data = get_data(edge);
294  data.cost() = get_cost(profile);
295  ++initial_num_edges_heap_;
296  insert_in_PQ(edge, data);
297  if (contraction_visitor_) contraction_visitor_->on_collected(profile, data.cost());
298  }
299 
300  DBG("Edges collected.");
301  }
302 
303  bool should_stop(double lCost, const Profile &profile) const {
304  return false;
305  }
306 
307  boost::optional<Point> get_placement(const Profile& profile) const {
308  return (*placement_policy_)(profile);
309  }
310 
311  bool is_contraction_valid(Profile const& profile, Placement_type placement) const {
312  if (!valid_contraction_policy_) return true;
313  return (*valid_contraction_policy_)(profile, placement);
314  }
315 
316 
317  public:
326  void contract_edges(int num_max_contractions = -1) {
327  DBG("\n\nContract edges");
328  int num_contraction = 0;
329 
330  bool unspecified_num_contractions = (num_max_contractions == -1);
331  //
332  // Pops and processes each edge from the PQ
333  //
334  boost::optional<Edge_handle> edge;
335  while ((edge = pop_from_PQ()) && ((num_contraction < num_max_contractions) || (unspecified_num_contractions))) {
336  Profile const& profile = create_profile(*edge);
337  Cost_type cost(get_data(*edge).cost());
338  if (contraction_visitor_) contraction_visitor_->on_selected(profile, cost, 0, 0);
339 
340  DBGMSG("\n\n---- Pop edge - num vertices :", complex_.num_vertices());
341 
342  if (cost) {
343  DBGMSG("sqrt(cost):", std::sqrt(*cost));
344  if (should_stop(*cost, profile)) {
345  if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
346  DBG("should_stop");
347  break;
348  }
349  Placement_type placement = get_placement(profile);
350  if (is_contraction_valid(profile, placement) && placement) {
351  DBG("contraction_valid");
352  contract_edge(profile, placement);
353  ++num_contraction;
354  } else {
355  DBG("contraction not valid");
356  if (contraction_visitor_) contraction_visitor_->on_non_valid(profile);
357  }
358  } else {
359  DBG("uncomputable cost");
360  }
361  }
362  if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
363  }
364 
365  bool is_in_heap(Edge_handle edge) const {
366  if (heap_PQ_->empty()) {
367  return false;
368  } else {
369  return edge_data_array_[get_undirected_edge_id(edge)].is_in_PQ();
370  }
371  }
372 
373  bool is_heap_empty() const {
374  return heap_PQ_->empty();
375  }
376 
382  boost::optional<std::pair<Edge_handle, Placement_type > > top_edge() {
383  boost::optional<std::pair<Edge_handle, Placement_type > > res;
384 
385  if (!heap_PQ_->empty()) {
386  auto edge = heap_PQ_->top();
387  Profile const& profile = create_profile(edge);
388  Placement_type placement = get_placement(profile);
389  res = std::make_pair(edge, placement);
390  DBGMSG("top edge:", complex_[edge]);
391  }
392  return res;
393  }
394 
401  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex)
402  : complex_(complex),
403  cost_policy_(new Edge_length_cost<Profile>),
404  placement_policy_(new First_vertex_placement<Profile>),
405  valid_contraction_policy_(new Link_condition_valid_contraction<Profile>),
406  contraction_visitor_(new Contraction_visitor_()),
407  edge_profile_factory_(0),
408  initial_num_edges_heap_(0),
409  current_num_edges_heap_(0) {
410  complex_.set_visitor(this);
411  if (contraction_visitor_) contraction_visitor_->on_started(complex);
412  collect_edges();
413  }
414 
419  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex,
420  Cost_policy_ *cost_policy,
421  Placement_policy_ * placement_policy = new First_vertex_placement<Profile>,
422  Valid_contraction_policy_ * valid_contraction_policy =
424  Contraction_visitor_* contraction_visitor = new Contraction_visitor_(),
425  Edge_profile_factory_* edge_profile_factory = NULL) :
426  complex_(complex),
427  cost_policy_(cost_policy),
428  placement_policy_(placement_policy),
429  valid_contraction_policy_(valid_contraction_policy),
430  contraction_visitor_(contraction_visitor),
431  edge_profile_factory_(edge_profile_factory),
432  initial_num_edges_heap_(0),
433  current_num_edges_heap_(0) {
434  complex_.set_visitor(this);
435  if (contraction_visitor) contraction_visitor->on_started(complex);
436  collect_edges();
437  }
438 
440  complex_.set_visitor(0);
441  }
442 
443  private:
444  void contract_edge(const Profile& profile, Placement_type placement) {
445  if (contraction_visitor_) contraction_visitor_->on_contracting(profile, placement);
446 
447  assert(complex_.contains_vertex(profile.v0_handle()));
448  assert(complex_.contains_vertex(profile.v1_handle()));
449  assert(placement);
450 
451  profile.complex().point(profile.v0_handle()) = *placement;
452 
453  // remark : this is not necessary since v1 will be deactivated
454  // profile.complex().point(profile.v1_handle()) = *placement;
455 
456  complex_.contract_edge(profile.v0_handle(), profile.v1_handle());
457 
458  assert(complex_.contains_vertex(profile.v0_handle()));
459  assert(!complex_.contains_vertex(profile.v1_handle()));
460 
461  update_changed_edges();
462 
463  // the visitor could do something as complex_.remove_popable_blockers();
464  if (contraction_visitor_) contraction_visitor_->on_contracted(profile, placement);
465  }
466 
467  private:
468  // every time the visitor's method on_changed_edge is called, it adds an
469  // edge to changed_edges_
470  std::vector< Edge_handle > changed_edges_;
471 
476  inline void on_changed_edge(Vertex_handle a, Vertex_handle b) override {
477  boost::optional<Edge_handle> ab(complex_[std::make_pair(a, b)]);
478  assert(ab);
479  changed_edges_.push_back(*ab);
480  }
481 
482  void update_changed_edges() {
483  // xxx do a parralel for
484  DBG("update edges");
485 
486  // sequential loop
487  for (auto ab : changed_edges_) {
488  // 1-get the Edge_handle corresponding to ab
489  // 2-change the data in mEdgeArray[ab.id()]
490  // 3-update the heap
491  Edge_data& data = get_data(ab);
492  Profile const& profile = create_profile(ab);
493  data.cost() = get_cost(profile);
494  if (data.is_in_PQ()) {
495  update_in_PQ(ab, data);
496  } else {
497  insert_in_PQ(ab, data);
498  }
499  }
500  changed_edges_.clear();
501  }
502 
503 
504  private:
505  void on_remove_edge(Vertex_handle a, Vertex_handle b) override {
506  boost::optional<Edge_handle> ab((complex_[std::make_pair(a, b)]));
507  assert(ab);
508  Edge_data& lData = get_data(*ab);
509  if (lData.is_in_PQ()) {
510  remove_from_PQ(*ab, lData);
511  }
512  }
513 
514  private:
520  void on_swaped_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) override {
521  boost::optional<Edge_handle> ax(complex_[std::make_pair(a, x)]);
522  boost::optional<Edge_handle> bx(complex_[std::make_pair(b, x)]);
523  assert(ax && bx);
524  complex_[*ax].index() = complex_[*bx].index();
525  }
526 
527  private:
533  void on_delete_blocker(const Simplex * blocker) override {
534  // we go for all pairs xy that belongs to the blocker
535  // note that such pairs xy are necessarily edges of the complex
536  // by definition of a blocker
537 
538  // todo uniqument utile pour la link condition
539  // laisser a l'utilisateur ? booleen update_heap_on_removed_blocker?
540  Simplex blocker_copy(*blocker);
541  for (auto x = blocker_copy.begin(); x != blocker_copy.end(); ++x) {
542  for (auto y = x; ++y != blocker_copy.end();) {
543  auto edge_descr(complex_[std::make_pair(*x, *y)]);
544  assert(edge_descr);
545  Edge_data& data = get_data(*edge_descr);
546  Profile const& profile = create_profile(*edge_descr);
547  data.cost() = get_cost(profile);
548 
549  // If the edge is already in the heap
550  // its priority has not changed.
551  // If the edge is not present, we reinsert it
552  // remark : we could also reinsert the edge
553  // only if it is valid
554  if (!data.is_in_PQ()) {
555  insert_in_PQ(*edge_descr, data);
556  }
557  }
558  }
559  }
560 
561 
562  private:
563  std::shared_ptr<Cost_policy_> cost_policy_;
564  std::shared_ptr<Placement_policy_> placement_policy_;
565  std::shared_ptr<Valid_contraction_policy_> valid_contraction_policy_;
566  std::shared_ptr<Contraction_visitor_> contraction_visitor_;
567 
568  // in case the user wants to do something special when the edge profile
569  // are created (for instance add some info)
570  std::shared_ptr<Edge_profile_factory_> edge_profile_factory_;
571  Edge_data_array edge_data_array_;
572 
573  boost::scoped_ptr<PQ> heap_PQ_;
574  int initial_num_edges_heap_;
575  int current_num_edges_heap_;
576 };
577 
578 } // namespace contraction
579 
580 } // namespace Gudhi
581 
582 #endif // SKELETON_BLOCKER_CONTRACTOR_H_
Skeleton_blocker_contractor(GeometricSimplifiableComplex &complex)
Constructor with default policies.
Definition: Skeleton_blocker_contractor.h:401
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:326
Definition: SimplicialComplexForAlpha.h:26
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:419
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:382
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:76
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
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
GUDHI  Version 2.2.0  - C++ library for Topological Data Analysis (TDA) and Higher Dimensional Geometry Understanding.  - Copyright : GPL v3 Generated on Thu Jun 14 2018 15:00:55 for GUDHI by Doxygen 1.8.13