Gudhi  1.1.0
 All Classes Functions 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 GUDHI_SKELETON_BLOCKER_CONTRACTOR_H_
24 #define GUDHI_SKELETON_BLOCKER_CONTRACTOR_H_
25 
26 #include <memory>
27 #include <cassert>
28 
29 // todo remove the queue to be independent from cgald
30 #include "gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h"
31 
32 
33 #include <list>
34 #include <boost/scoped_array.hpp>
35 #include <boost/scoped_ptr.hpp>
36 
37 
38 #include "gudhi/Contraction/Edge_profile.h"
39 #include "gudhi/Contraction/policies/Cost_policy.h"
40 #include "gudhi/Contraction/policies/Edge_length_cost.h"
41 #include "gudhi/Contraction/policies/Placement_policy.h"
42 #include "gudhi/Contraction/policies/First_vertex_placement.h"
43 
44 #include "gudhi/Contraction/policies/Valid_contraction_policy.h"
45 #include "gudhi/Contraction/policies/Dummy_valid_contraction.h" //xxx remove
46 #include "gudhi/Contraction/policies/Link_condition_valid_contraction.h" //xxx remove
47 
48 #include "gudhi/Contraction/policies/Contraction_visitor.h"
49 
50 #include "gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h"
51 
52 #include "gudhi/Utils.h"
53 
54 namespace Gudhi{
55 
56 namespace contraction {
57 
58 
59 
60 
61 template <class Profile>
62 Placement_policy<Profile>* make_first_vertex_placement(){
63  return new First_vertex_placement<Profile>();
64 }
65 
66 template <class Profile>
67 Valid_contraction_policy<Profile>* make_link_valid_contraction(){
68  return new Link_condition_valid_contraction<Profile>();
69 }
70 
71 
75 template <class Profile>
77 public:
78  typedef typename Profile::Point Point;
79  typedef typename Profile::Complex Complex;
80  typedef typename Complex::Vertex_handle Vertex_handle;
81 
82  //todo explore only neighborhood with stack
83  void on_contracted(const Profile &profile, boost::optional< Point > placement) override{
84  std::list<Vertex_handle> vertex_to_check;
85  vertex_to_check.push_front(profile.v0_handle());
86 
87  while(!vertex_to_check.empty()){
88  Vertex_handle v = vertex_to_check.front();
89  vertex_to_check.pop_front();
90 
91  bool blocker_popable_found=true;
92  while (blocker_popable_found){
93  blocker_popable_found = false;
94  for(auto block : profile.complex().blocker_range(v)){
95  if (profile.complex().is_popable_blocker(block)) {
96  for(Vertex_handle nv : *block)
97  if(nv!=v) vertex_to_check.push_back(nv);
98  profile.complex().delete_blocker(block);
99  blocker_popable_found = true;
100  break;
101  }
102  }
103  }
104  }
105  }
106 };
107 
108 template <class Profile>
109 Contraction_visitor<Profile>* make_remove_popable_blockers_visitor(){
111 }
112 
113 
114 
115 
116 
117 
134 template<
135 class GeometricSimplifiableComplex,
136 class EdgeProfile = Edge_profile<GeometricSimplifiableComplex>
137 >
138 class Skeleton_blocker_contractor : private skbl::Dummy_complex_visitor<typename GeometricSimplifiableComplex::Vertex_handle>{
139 
140  GeometricSimplifiableComplex& complex_;
141 
142 public:
143  typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex;
144  typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle;
145  typedef typename GeometricSimplifiableComplex::Simplex_handle Simplex_handle;
146  typedef typename GeometricSimplifiableComplex::Simplex_handle_iterator Simplex_handle_iterator;
147 
148 
149 
150  typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle;
151 
152  typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge;
153  typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle;
154  typedef typename GeometricSimplifiableComplex::Point Point;
155 
156  typedef EdgeProfile Profile;
157 
158 
163  typedef Edge_profile_factory<EdgeProfile> Edge_profile_factory_;
164 
165 
166 
167  typedef boost::optional<double> Cost_type;
168  typedef boost::optional<Point> Placement_type ;
169 
170  typedef size_t size_type;
171 
173 
174 
175 
176 private:
177 
178  struct Compare_id
179  {
180  Compare_id() : algorithm_(0) {}
181 
182  Compare_id( Self const* aAlgorithm ) : algorithm_(aAlgorithm) {}
183 
184  bool operator() ( Edge_handle a, Edge_handle b ) const
185  {
186  return algorithm_->get_undirected_edge_id(a) < algorithm_->get_undirected_edge_id(b);
187  }
188 
189  Self const* algorithm_ ;
190  } ;
191 
192  struct Compare_cost
193  {
194  Compare_cost() : algorithm_(0) {
195  }
196 
197  Compare_cost( Self const* aAlgorithm ) : algorithm_(aAlgorithm) {
198  }
199 
200  bool operator() ( Edge_handle a, Edge_handle b ) const
201  {
202  // NOTE: A cost is an optional<> value.
203  // Absent optionals are ordered first; that is, "none < T" and "T > none" for any defined T != none.
204  // In consequence, edges with undefined costs will be promoted to the top of the priority queue and poped out first.
205  return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost();
206  }
207 
208  Self const* algorithm_ ;
209 
210  } ;
211 
212  struct Undirected_edge_id : boost::put_get_helper<size_type, Undirected_edge_id>
213  {
214  typedef boost::readable_property_map_tag category;
215  typedef size_type value_type;
216  typedef size_type reference;
217  typedef Edge_handle key_type;
218 
219  Undirected_edge_id() : algorithm_(0) {}
220 
221  Undirected_edge_id( Self const* aAlgorithm ) : algorithm_(aAlgorithm) {}
222 
223  size_type operator[] ( Edge_handle e ) const { return algorithm_->get_undirected_edge_id(e); }
224 
225  Self const* algorithm_ ;
226  } ;
227 
228  typedef CGAL::Modifiable_priority_queue<Edge_handle,Compare_cost,Undirected_edge_id> PQ ;
229  typedef typename PQ::handle pq_handle ;
230 
231 
232  // An Edge_data is associated with EVERY edge in the complex (collapsible or not).
233  // It relates the edge with the PQ-handle needed to update the priority queue
234  // It also relates the edge with a policy-based cache
235  class Edge_data
236  {
237  public :
238 
239  Edge_data() : PQHandle_(),cost_() {}
240 
241  Cost_type const& cost() const { return cost_ ; }
242  Cost_type & cost() { return cost_ ; }
243 
244  pq_handle PQ_handle() const { return PQHandle_ ;}
245 
246  bool is_in_PQ() const { return PQHandle_ != PQ::null_handle() ; }
247 
248  void set_PQ_handle( pq_handle h ) { PQHandle_ = h ; }
249 
250  void reset_PQ_handle() { PQHandle_ = PQ::null_handle() ; }
251 
252  private:
253  pq_handle PQHandle_ ;
254  Cost_type cost_ ;
255 
256  } ;
257  typedef Edge_data* Edge_data_ptr ;
258  typedef boost::scoped_array<Edge_data> Edge_data_array ;
259 
260 
261  int get_undirected_edge_id ( Edge_handle edge ) const {
262  return complex_[edge].index() ;
263  }
264 
265 
266  const Edge_data& get_data ( Edge_handle edge ) const
267  {
268  return edge_data_array_[get_undirected_edge_id(edge)];
269  }
270 
271 
272  Edge_data& get_data ( Edge_handle edge )
273  {
274  return edge_data_array_[get_undirected_edge_id(edge)];
275  }
276 
277  Cost_type get_cost(const Profile & profile) const{
278  return (*cost_policy_)(profile,get_placement(profile));
279  }
280 
281  Profile create_profile(Edge_handle edge) const{
282  if(edge_profile_factory_)
283  return edge_profile_factory_->make_profile(complex_,edge);
284  else
285  return Profile(complex_,edge);
286  }
287 
288 
289  void insert_in_PQ( Edge_handle edge, Edge_data& data )
290  {
291  data.set_PQ_handle(heap_PQ_->push(edge));
292  ++current_num_edges_heap_;
293  }
294 
295  void update_in_PQ( Edge_handle edge, Edge_data& data )
296  {
297  data.set_PQ_handle(heap_PQ_->update(edge,data.PQ_handle())) ;
298  }
299 
300  void remove_from_PQ( Edge_handle edge, Edge_data& data )
301  {
302  data.set_PQ_handle(heap_PQ_->erase(edge,data.PQ_handle()));
303  --current_num_edges_heap_;
304  }
305 
306  boost::optional<Edge_handle> pop_from_PQ() {
307  boost::optional<Edge_handle> edge = heap_PQ_->extract_top();
308  if ( edge )
309  {
310  get_data(*edge).reset_PQ_handle();
311  }
312  return edge ;
313  }
314 
315 
316 
317 private:
318 
326  void collect_edges(){
327  //
328  // Loop over all the edges in the complex in the heap
329  //
330  size_type size = complex_.num_edges();
331  DBG("Collecting edges ...");
332  DBGMSG("num edges :",size);
333 
334  edge_data_array_.reset( new Edge_data[size] ) ;
335 
336  heap_PQ_.reset( new PQ (size, Compare_cost(this), Undirected_edge_id(this) ) ) ;
337 
338  std::size_t id = 0 ;
339 
340  //xxx do a parralel for
341  for(auto edge : complex_.edge_range()){
342  // Edge_handle edge = *edge_it;
343  complex_[edge].index() = id++;
344  Profile const& profile = create_profile(edge);
345  Edge_data& data = get_data(edge);
346  data.cost() = get_cost(profile) ;
347  ++initial_num_edges_heap_;
348  insert_in_PQ(edge,data);
349  if(contraction_visitor_) contraction_visitor_->on_collected(profile,data.cost());
350  }
351 
352  DBG("Edges collected.");
353 
354  }
355 
356  bool should_stop(double lCost,const Profile &profile) const{
357  return false;
358  }
359 
360  boost::optional<Point> get_placement(const Profile& profile) const{
361  return (*placement_policy_)(profile);
362  }
363 
364  bool is_contraction_valid( Profile const& profile, Placement_type placement ) const{
365  return (*valid_contraction_policy_)(profile,placement);
366  }
367 
368 
369 public:
378  void contract_edges(int num_max_contractions=-1){
379 
380  DBG("\n\nContract edges");
381  int num_contraction = 0 ;
382 
383  bool unspecified_num_contractions = (num_max_contractions == -1);
384  //
385  // Pops and processes each edge from the PQ
386  //
387  boost::optional<Edge_handle> edge ;
388  while ( (edge = pop_from_PQ())&& ((num_contraction<num_max_contractions)||(unspecified_num_contractions)))
389  {
390  Profile const& profile = create_profile(*edge);
391  Cost_type cost(get_data(*edge).cost());
392  if(contraction_visitor_) contraction_visitor_->on_selected(profile,cost,0,0);
393 
394  DBGMSG("\n\n---- Pop edge - num vertices :",complex_.num_vertices());
395 
396  if (cost){
397  DBGMSG("sqrt(cost):",std::sqrt(*cost));
398  if (should_stop(*cost,profile) )
399  {
400  if(contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
401  DBG("should_stop");
402  break ;
403  }
404  Placement_type placement = get_placement(profile);
405  if ( is_contraction_valid(profile,placement) && placement )
406  {
407  DBG("contraction_valid");
408  contract_edge(profile,placement);
409  ++ num_contraction;
410  }
411  else
412  {
413  DBG("contraction not valid");
414  if(contraction_visitor_) contraction_visitor_->on_non_valid(profile);
415  }
416  }
417  else
418  {
419  DBG("uncomputable cost");
420  }
421  }
422  if(contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
423  }
424 
425 
426  bool is_in_heap(Edge_handle edge) const{
427  if(heap_PQ_->empty()) return false;
428  else{
429  return edge_data_array_[get_undirected_edge_id(edge)].is_in_PQ();
430  }
431  }
432 
433  bool is_heap_empty() const{
434  return heap_PQ_->empty();
435  }
436 
437 
443  boost::optional<std::pair<Edge_handle,Placement_type > > top_edge(){
444  boost::optional<std::pair<Edge_handle,Placement_type > > res;
445 
446  if(!heap_PQ_->empty()) {
447  auto edge = heap_PQ_->top();
448  Profile const& profile = create_profile(edge);
449  Placement_type placement = get_placement(profile);
450  res = make_pair(edge,placement);
451  DBGMSG("top edge:",complex_[edge]);
452 
453  }
454  return res;
455  }
456 
457 
464  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex)
465  :complex_(complex),
466  cost_policy_(new Edge_length_cost<Profile>),
467  placement_policy_(new First_vertex_placement<Profile>),
468  valid_contraction_policy_(new Link_condition_valid_contraction<Profile>),
469  contraction_visitor_(new Contraction_visitor_()),
470  edge_profile_factory_(0),
471  initial_num_edges_heap_(0),
472  current_num_edges_heap_(0)
473  {
474  complex_.set_visitor(this);
475  if(contraction_visitor_) contraction_visitor_->on_started(complex);
476  collect_edges();
477  }
478 
483  Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex,
484  Cost_policy_ *cost_policy,
485  Placement_policy_ * placement_policy = new First_vertex_placement<Profile>,
487  Contraction_visitor_* contraction_visitor = new Contraction_visitor_(),
488  Edge_profile_factory_* edge_profile_factory = NULL
489  ):
490  complex_(complex),
491  cost_policy_(cost_policy),
492  placement_policy_(placement_policy),
493  valid_contraction_policy_(valid_contraction_policy),
494  contraction_visitor_(contraction_visitor),
495  edge_profile_factory_(edge_profile_factory),
496  initial_num_edges_heap_(0),
497  current_num_edges_heap_(0)
498  {
499  complex_.set_visitor(this);
500  if(contraction_visitor) contraction_visitor->on_started(complex);
501  collect_edges();
502  }
503 
504 
506  complex_.set_visitor(0);
507  }
508 
509 private:
510 
511 
512  void contract_edge(const Profile& profile, Placement_type placement ) {
513  if(contraction_visitor_) contraction_visitor_->on_contracting(profile,placement);
514 
515  assert(complex_.contains_vertex(profile.v0_handle()));
516  assert(complex_.contains_vertex(profile.v1_handle()));
517  assert(placement);
518 
519  profile.complex().point(profile.v0_handle()) = *placement;
520 
521  // remark : this is not necessary since v1 will be deactivated
522  // profile.complex().point(profile.v1_handle()) = *placement;
523 
524  complex_.contract_edge(profile.v0_handle(),profile.v1_handle());
525 
526  assert(complex_.contains_vertex(profile.v0_handle()));
527  assert(!complex_.contains_vertex(profile.v1_handle()));
528 
529  update_changed_edges();
530 
531  // the visitor could do something as complex_.remove_popable_blockers();
532  if(contraction_visitor_) contraction_visitor_->on_contracted(profile,placement);
533  }
534 
535 private:
536 
537  // every time the visitor's method on_changed_edge is called, it adds an
538  // edge to changed_edges_
539  std::vector< Edge_handle > changed_edges_;
540 
545  inline void on_changed_edge(Vertex_handle a,Vertex_handle b) override{
546  boost::optional<Edge_handle> ab(complex_[std::make_pair(a,b)]);
547  assert(ab);
548  changed_edges_.push_back(*ab);
549  }
550 
551  void update_changed_edges(){
552  //xxx do a parralel for
553 
554  DBG("update edges");
555 
556  // sequential loop
557  for(auto ab : changed_edges_){
558  //1-get the Edge_handle corresponding to ab
559  //2-change the data in mEdgeArray[ab.id()]
560  //3-update the heap
561  Edge_data& data = get_data(ab);
562  Profile const& profile = create_profile(ab);
563  data.cost() = get_cost(profile) ;
564  if ( data.is_in_PQ()){
565  update_in_PQ(ab,data);
566  }
567  else{
568  insert_in_PQ(ab,data);
569  }
570  }
571  changed_edges_.clear();
572  }
573 
574 
575 private:
576  void on_remove_edge(Vertex_handle a,Vertex_handle b) override{
577 
578  boost::optional<Edge_handle> ab((complex_[std::make_pair(a,b)]));
579  assert(ab);
580  Edge_data& lData = get_data(*ab) ;
581  if ( lData.is_in_PQ() )
582  {
583  remove_from_PQ(*ab,lData) ;
584  }
585  }
586 private:
592  void on_swaped_edge(Vertex_handle a,Vertex_handle b,Vertex_handle x) override{
593  boost::optional<Edge_handle> ax(complex_[std::make_pair(a,x)]);
594  boost::optional<Edge_handle> bx(complex_[std::make_pair(b,x)]);
595  assert(ax&& bx);
596  complex_[*ax].index() =complex_[*bx].index();
597  }
598 private:
604  void on_delete_blocker(const Simplex_handle * blocker) override{
605  // we go for all pairs xy that belongs to the blocker
606  // note that such pairs xy are necessarily edges of the complex
607  // by definition of a blocker
608 
609  // todo uniqument utile pour la link condition
610  // laisser à l'utilisateur? booleen update_heap_on_removed_blocker?
611  Simplex_handle blocker_copy(*blocker);
612  for (auto x = blocker_copy.begin(); x!= blocker_copy.end(); ++x){
613  for(auto y=x ; ++y != blocker_copy.end(); ){
614  auto edge_descr(complex_[std::make_pair(*x,*y)]);
615  assert(edge_descr);
616  Edge_data& data = get_data(*edge_descr);
617  Profile const& profile = create_profile(*edge_descr);
618  data.cost() = get_cost(profile) ;
619 
620  // If the edge is already in the heap
621  // its priority has not changed.
622  // If the edge is not present, we reinsert it
623  // remark : we could also reinsert the edge
624  // only if it is valid
625  if ( !data.is_in_PQ() ){
626  insert_in_PQ(*edge_descr,data);
627  }
628  }
629  }
630  }
631 
632 
633 private:
634  std::shared_ptr<Cost_policy_> cost_policy_;
635  std::shared_ptr<Placement_policy_> placement_policy_;
636  std::shared_ptr<Valid_contraction_policy_> valid_contraction_policy_;
637  std::shared_ptr<Contraction_visitor_> contraction_visitor_;
638 
639  //in case the user wants to do something special when the edge profile
640  //are created (for instance add some info)
641  std::shared_ptr<Edge_profile_factory_> edge_profile_factory_;
642  Edge_data_array edge_data_array_ ;
643 
644  boost::scoped_ptr<PQ> heap_PQ_ ;
645  int initial_num_edges_heap_;
646  int current_num_edges_heap_;
647 
648 };
649 
650 } // namespace contraction
651 } // namespace GUDHI
652 
653 #endif /* GUDHI_SKELETON_BLOCKER_CONTRACTOR_H_ */
Skeleton_blocker_contractor(GeometricSimplifiableComplex &complex)
Constructor with default policies.
Definition: Skeleton_blocker_contractor.h:464
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:378
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:483
Interface for a visitor of the edge contraction process.
Definition: Contraction_visitor.h:39
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:443
A dummy visitor of a simplicial complex that does nothing.
Definition: Skeleton_blocker_complex_visitor.h:74
Policy to specify the cost of contracting an edge.
Definition: Cost_policy.h:36
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:138
Policy to specify if an edge contraction is valid or not.
Definition: Valid_contraction_policy.h:33
Visitor to remove popable blockers after an edge contraction.
Definition: Skeleton_blocker_contractor.h:76
Policy to specify where the merged point had to be placed after an edge contraction.
Definition: Placement_policy.h:36