From 425b462d361286822ee0ed7b5fe00881ba312ea3 Mon Sep 17 00:00:00 2001 From: vrouvrea Date: Fri, 5 Dec 2014 13:32:54 +0000 Subject: Moved into trunk git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/trunk@341 636b058d-ea47-450e-bf9e-a15bfbe3eedb --- .../include/gudhi/Skeleton_blocker_contractor.h | 634 +++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 src/Contraction/include/gudhi/Skeleton_blocker_contractor.h (limited to 'src/Contraction/include/gudhi/Skeleton_blocker_contractor.h') diff --git a/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h b/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h new file mode 100644 index 00000000..4dc7952c --- /dev/null +++ b/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h @@ -0,0 +1,634 @@ +/* + * Skeleton_blocker_contractor.h + * + * Created on: Feb 11, 2014 + * Author: dsalinas + */ + +#ifndef GUDHI_SKELETON_BLOCKER_CONTRACTOR_H_ +#define GUDHI_SKELETON_BLOCKER_CONTRACTOR_H_ + +#include +#include + +// todo remove the queue to be independent from cgald +#include "gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h" +//#include + + +#include +#include +#include + + +#include "gudhi/Contraction/Edge_profile.h" +#include "gudhi/Contraction/policies/Cost_policy.h" +#include "gudhi/Contraction/policies/Edge_length_cost.h" +#include "gudhi/Contraction/policies/Placement_policy.h" +#include "gudhi/Contraction/policies/First_vertex_placement.h" + +#include "gudhi/Contraction/policies/Valid_contraction_policy.h" +#include "gudhi/Contraction/policies/Dummy_valid_contraction.h" //xxx remove +#include "gudhi/Contraction/policies/Link_condition_valid_contraction.h" //xxx remove + +#include "gudhi/Contraction/policies/Contraction_visitor.h" + +#include "gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h" + +#include "gudhi/Utils.h" + +namespace Gudhi{ + +namespace contraction { + + +template +Placement_policy* make_first_vertex_placement(){ + return new First_vertex_placement(); +} + +template +Valid_contraction_policy* make_link_valid_contraction(){ + return new Link_condition_valid_contraction(); +} + + +// visitor that remove popable blockers after an edge contraction +template +class Contraction_visitor_remove_popable : public Contraction_visitor{ +public: + typedef typename Profile::Point Point; + typedef typename Profile::Complex Complex; + typedef typename Complex::Vertex_handle Vertex_handle; + + //todo explore only neighborhood with stack + void on_contracted(const Profile &profile, boost::optional< Point > placement) override{ + std::list vertex_to_check; + vertex_to_check.push_front(profile.v0_handle()); + + while(!vertex_to_check.empty()){ + Vertex_handle v = vertex_to_check.front(); + vertex_to_check.pop_front(); + + bool blocker_popable_found=true; + while (blocker_popable_found){ + blocker_popable_found = false; + for(auto block : profile.complex().blocker_range(v)){ + if (profile.complex().is_popable_blocker(block)) { + for(Vertex_handle nv : *block) + if(nv!=v) vertex_to_check.push_back(nv); + profile.complex().delete_blocker(block); + blocker_popable_found = true; + break; + } + } + } + } + } +}; + +template +Contraction_visitor* make_remove_popable_blockers_visitor(){ + return new Contraction_visitor_remove_popable(); +} + + + + + + +/** + *@class Skeleton_blocker_contractor + *@brief Class that allows to contract iteratively edges of a simplicial complex. + * + * @details Basically, the simplification algorithm consists in iteratively picking the + * edge with lowest cost and performing an edge contraction if the contraction is valid. + * This class is policy based (and much inspired from the edge collapse package of CGAL). + * + * Policies that can be changed are : + * - the cost policy : how much cost an edge contraction + * - the placement policy : where will be placed the contraction point + * - the valid contraction policy : is the contraction valid. For instance, it can be + * a topological condition (link condition) or a geometrical condition (normals messed up). + * + * TODO expliquer la pile + */ +template< +class GeometricSimplifiableComplex, +class EdgeProfile = Edge_profile +> +class Skeleton_blocker_contractor : private skbl::Dummy_complex_visitor{ + + GeometricSimplifiableComplex& complex_; + +public: + typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex; + typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle; + typedef typename GeometricSimplifiableComplex::Simplex_handle Simplex_handle; + typedef typename GeometricSimplifiableComplex::Simplex_handle_iterator Simplex_handle_iterator; + + + + typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle; + + typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge; + typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle; + typedef typename GeometricSimplifiableComplex::Point Point; + + typedef EdgeProfile Profile; + + + typedef Cost_policy Cost_policy_; + typedef Placement_policy Placement_policy_; + typedef Valid_contraction_policy Valid_contraction_policy_; + typedef Contraction_visitor Contraction_visitor_; + typedef Edge_profile_factory Edge_profile_factory_; + + + + typedef boost::optional Cost_type; + typedef boost::optional Placement_type ; + + typedef size_t size_type; + + typedef Skeleton_blocker_contractor Self ; + + + +private: + + struct Compare_id + { + Compare_id() : algorithm_(0) {} + + Compare_id( Self const* aAlgorithm ) : algorithm_(aAlgorithm) {} + + bool operator() ( Edge_handle a, Edge_handle b ) const + { + return algorithm_->get_undirected_edge_id(a) < algorithm_->get_undirected_edge_id(b); + } + + Self const* algorithm_ ; + } ; + + struct Compare_cost + { + Compare_cost() : algorithm_(0) { + } + + Compare_cost( Self const* aAlgorithm ) : algorithm_(aAlgorithm) { + } + + bool operator() ( Edge_handle a, Edge_handle b ) const + { + // NOTE: A cost is an optional<> value. + // Absent optionals are ordered first; that is, "none < T" and "T > none" for any defined T != none. + // In consequence, edges with undefined costs will be promoted to the top of the priority queue and poped out first. + return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost(); + } + + Self const* algorithm_ ; + + } ; + + struct Undirected_edge_id : boost::put_get_helper + { + typedef boost::readable_property_map_tag category; + typedef size_type value_type; + typedef size_type reference; + typedef Edge_handle key_type; + + Undirected_edge_id() : algorithm_(0) {} + + Undirected_edge_id( Self const* aAlgorithm ) : algorithm_(aAlgorithm) {} + + size_type operator[] ( Edge_handle e ) const { return algorithm_->get_undirected_edge_id(e); } + + Self const* algorithm_ ; + } ; + + typedef CGAL::Modifiable_priority_queue PQ ; + typedef typename PQ::handle pq_handle ; + + + // An Edge_data is associated with EVERY edge in the complex (collapsible or not). + // It relates the edge with the PQ-handle needed to update the priority queue + // It also relates the edge with a policy-based cache + class Edge_data + { + public : + + Edge_data() : PQHandle_(),cost_() {} + + Cost_type const& cost() const { return cost_ ; } + Cost_type & cost() { return cost_ ; } + + pq_handle PQ_handle() const { return PQHandle_ ;} + + bool is_in_PQ() const { return PQHandle_ != PQ::null_handle() ; } + + void set_PQ_handle( pq_handle h ) { PQHandle_ = h ; } + + void reset_PQ_handle() { PQHandle_ = PQ::null_handle() ; } + + private: + pq_handle PQHandle_ ; + Cost_type cost_ ; + + } ; + typedef Edge_data* Edge_data_ptr ; + typedef boost::scoped_array Edge_data_array ; + + + int get_undirected_edge_id ( Edge_handle edge ) const { + return complex_[edge].index() ; + } + + + const Edge_data& get_data ( Edge_handle edge ) const + { + return edge_data_array_[get_undirected_edge_id(edge)]; + } + + + Edge_data& get_data ( Edge_handle edge ) + { + return edge_data_array_[get_undirected_edge_id(edge)]; + } + + Cost_type get_cost(const Profile & profile) const{ + return (*cost_policy_)(profile,get_placement(profile)); + } + + Profile create_profile(Edge_handle edge) const{ + if(edge_profile_factory_) + return edge_profile_factory_->make_profile(complex_,edge); + else + return Profile(complex_,edge); + } + + + void insert_in_PQ( Edge_handle edge, Edge_data& data ) + { + data.set_PQ_handle(heap_PQ_->push(edge)); + ++current_num_edges_heap_; + } + + void update_in_PQ( Edge_handle edge, Edge_data& data ) + { + data.set_PQ_handle(heap_PQ_->update(edge,data.PQ_handle())) ; + } + + void remove_from_PQ( Edge_handle edge, Edge_data& data ) + { + data.set_PQ_handle(heap_PQ_->erase(edge,data.PQ_handle())); + --current_num_edges_heap_; + } + + boost::optional pop_from_PQ() { + boost::optional edge = heap_PQ_->extract_top(); + if ( edge ) + { + get_data(*edge).reset_PQ_handle(); + } + return edge ; + } + + + +private: + + /** + * @brief Collect edges. + * + * Iterates over all edges of the simplicial complex and + * 1) inserts them in the priority queue sorted according to the Cost policy. + * 2) set the id() field of each edge + */ + void collect_edges(){ + // + // Loop over all the edges in the complex in the heap + // + size_type size = complex_.num_edges(); + DBG("Collecting edges ..."); + DBGMSG("num edges :",size); + + edge_data_array_.reset( new Edge_data[size] ) ; + + heap_PQ_.reset( new PQ (size, Compare_cost(this), Undirected_edge_id(this) ) ) ; + + std::size_t id = 0 ; + + //xxx do a parralel for + for(auto edge : complex_.edge_range()){ + // Edge_handle edge = *edge_it; + complex_[edge].index() = id++; + Profile const& profile = create_profile(edge); + Edge_data& data = get_data(edge); + data.cost() = get_cost(profile) ; + ++initial_num_edges_heap_; + insert_in_PQ(edge,data); + if(contraction_visitor_) contraction_visitor_->on_collected(profile,data.cost()); + } + + DBG("Edges collected."); + + } + + bool should_stop(double lCost,const Profile &profile) const{ + return false; + } + + boost::optional get_placement(const Profile& profile) const{ + return (*placement_policy_)(profile); + } + + bool is_contraction_valid( Profile const& profile, Placement_type placement ) const{ + return (*valid_contraction_policy_)(profile,placement); + } + + +public: + /** + * \brief Contract edges. + * + * While the heap is not empty, it extracts the edge with the minimum + * cost in the heap then try to contract it. + * It stops when the Stop policy says so or when the number of contractions + * given by 'num_max_contractions' is reached (if this number is positive). + */ + void contract_edges(int num_max_contractions=-1){ + + DBG("\n\nContract edges"); + int num_contraction = 0 ; + + bool unspecified_num_contractions = (num_max_contractions == -1); + // + // Pops and processes each edge from the PQ + // + boost::optional edge ; + while ( (edge = pop_from_PQ())&& ((num_contractionon_selected(profile,cost,0,0); + + DBGMSG("\n\n---- Pop edge - num vertices :",complex_.num_vertices()); + + if (cost){ + DBGMSG("sqrt(cost):",std::sqrt(*cost)); + if (should_stop(*cost,profile) ) + { + if(contraction_visitor_) contraction_visitor_->on_stop_condition_reached(profile); + DBG("should_stop"); + break ; + } + Placement_type placement = get_placement(profile); + if ( is_contraction_valid(profile,placement) && placement ) + { + DBG("contraction_valid"); + contract_edge(profile,placement); + ++ num_contraction; + } + else + { + DBG("contraction not valid"); + if(contraction_visitor_) contraction_visitor_->on_non_valid(profile); + } + } + else + { + DBG("uncomputable cost"); + } + } + } + + + bool is_in_heap(Edge_handle edge) const{ + if(heap_PQ_->empty()) return false; + else{ + return edge_data_array_[get_undirected_edge_id(edge)].is_in_PQ(); + } + } + + bool is_heap_empty() const{ + return heap_PQ_->empty(); + } + + + /** + * @brief Returns an Edge_handle and a Placement_type. This pair consists in + * the edge with the lowest cost in the heap together with its placement. + * The returned value is initialized iff the heap is non-empty. + */ + boost::optional > top_edge(){ + boost::optional > res; + + if(!heap_PQ_->empty()) { + auto edge = heap_PQ_->top(); + Profile const& profile = create_profile(edge); + Placement_type placement = get_placement(profile); + res = make_pair(edge,placement); + DBGMSG("top edge:",complex_[edge]); + + } + return res; + } + + + /** + * @brief Constructor with default policies. + * + * @details The default cost, placement, valid and visitor policies + * are respectively : the edge length, the first point, the link condition + */ + Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex) + :complex_(complex), + cost_policy_(new Edge_length_cost), + placement_policy_(new First_vertex_placement), + valid_contraction_policy_(new Link_condition_valid_contraction), + contraction_visitor_(new Contraction_visitor_()), + edge_profile_factory_(0), + initial_num_edges_heap_(0), + current_num_edges_heap_(0) + { + complex_.set_visitor(this); + if(contraction_visitor_) contraction_visitor_->on_started(complex); + collect_edges(); + } + + /** + * @brief Constructor with customed policies. + * @remark Policies destruction is handle by the class with smart pointers. + */ + Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex, + Cost_policy_ *cost_policy, + Placement_policy_ * placement_policy = new First_vertex_placement, + Valid_contraction_policy_ * valid_contraction_policy = new Link_condition_valid_contraction, + Contraction_visitor_* contraction_visitor = new Contraction_visitor_(), + Edge_profile_factory_* edge_profile_factory = NULL + ): + complex_(complex), + cost_policy_(cost_policy), + placement_policy_(placement_policy), + valid_contraction_policy_(valid_contraction_policy), + contraction_visitor_(contraction_visitor), + edge_profile_factory_(edge_profile_factory), + initial_num_edges_heap_(0), + current_num_edges_heap_(0) + { + complex_.set_visitor(this); + if(contraction_visitor) contraction_visitor->on_started(complex); + collect_edges(); + } + + + ~Skeleton_blocker_contractor(){ + complex_.set_visitor(0); + } + +private: + + + void contract_edge(const Profile& profile, Placement_type placement ) { + if(contraction_visitor_) contraction_visitor_->on_contracting(profile,placement); + + assert(complex_.contains_vertex(profile.v0_handle())); + assert(complex_.contains_vertex(profile.v1_handle())); + assert(placement); + + profile.complex().point(profile.v0_handle()) = *placement; + + // remark : this is not necessary since v1 will be deactivated + // profile.complex().point(profile.v1_handle()) = *placement; + + complex_.contract_edge(profile.v0_handle(),profile.v1_handle()); + + assert(complex_.contains_vertex(profile.v0_handle())); + assert(!complex_.contains_vertex(profile.v1_handle())); + + update_changed_edges(); + + // the visitor could do something as complex_.remove_popable_blockers(); + if(contraction_visitor_) contraction_visitor_->on_contracted(profile,placement); + } + +private: + + // every time the visitor's method on_changed_edge is called, it adds an + // edge to changed_edges_ + std::vector< Edge_handle > changed_edges_; + + /** + * @brief we update the cost and the position in the heap of an edge that has + * been changed + */ + inline void on_changed_edge(Vertex_handle a,Vertex_handle b) override{ + boost::optional ab(complex_[std::make_pair(a,b)]); + assert(ab); + changed_edges_.push_back(*ab); + } + + void update_changed_edges(){ + //xxx do a parralel for + + DBG("update edges"); + + // sequential loop + for(auto ab : changed_edges_){ + //1-get the Edge_handle corresponding to ab + //2-change the data in mEdgeArray[ab.id()] + //3-update the heap + Edge_data& data = get_data(ab); + Profile const& profile = create_profile(ab); + data.cost() = get_cost(profile) ; + if ( data.is_in_PQ()){ + update_in_PQ(ab,data); + } + else{ + insert_in_PQ(ab,data); + } + } + changed_edges_.clear(); + } + + +private: + void on_remove_edge(Vertex_handle a,Vertex_handle b) override{ + + boost::optional ab((complex_[std::make_pair(a,b)])); + assert(ab); + Edge_data& lData = get_data(*ab) ; + if ( lData.is_in_PQ() ) + { + remove_from_PQ(*ab,lData) ; + } + } +private: + /** + * @brief Called when the edge 'ax' has been added while the edge 'bx' + * is still there but will be removed on next instruction. + * We assign the index of 'bx' to the edge index of 'ax' + */ + void on_swaped_edge(Vertex_handle a,Vertex_handle b,Vertex_handle x) override{ + boost::optional ax(complex_[std::make_pair(a,x)]); + boost::optional bx(complex_[std::make_pair(b,x)]); + assert(ax&& bx); + complex_[*ax].index() =complex_[*bx].index(); + } +private: + /** + * @brief Called when a blocker is removed. + * All the edges that passes through the blocker may be edge-contractible + * again and are thus reinserted in the heap. + */ + void on_delete_blocker(const Simplex_handle * blocker) override{ + // we go for all pairs xy that belongs to the blocker + // note that such pairs xy are necessarily edges of the complex + // by definition of a blocker + + // todo uniqument utile pour la link condition + // laisser à l'utilisateur? booleen update_heap_on_removed_blocker? + Simplex_handle blocker_copy(*blocker); + for (auto x = blocker_copy.begin(); x!= blocker_copy.end(); ++x){ + for(auto y=x ; ++y != blocker_copy.end(); ){ + auto edge_descr(complex_[std::make_pair(*x,*y)]); + assert(edge_descr); + Edge_data& data = get_data(*edge_descr); + Profile const& profile = create_profile(*edge_descr); + data.cost() = get_cost(profile) ; + + // If the edge is already in the heap + // its priority has not changed. + // If the edge is not present, we reinsert it + // remark : we could also reinsert the edge + // only if it is valid + if ( !data.is_in_PQ() ){ + insert_in_PQ(*edge_descr,data); + } + } + } + } + + +private: + std::shared_ptr cost_policy_; + std::shared_ptr placement_policy_; + std::shared_ptr valid_contraction_policy_; + std::shared_ptr contraction_visitor_; + + //in case the user wants to do something special when the edge profile + //are created (for instance add some info) + std::shared_ptr edge_profile_factory_; + Edge_data_array edge_data_array_ ; + + boost::scoped_ptr heap_PQ_ ; + int initial_num_edges_heap_; + int current_num_edges_heap_; + +}; + +} // namespace contraction +} // namespace GUDHI + +#endif /* GUDHI_SKELETON_BLOCKER_CONTRACTOR_H_ */ -- cgit v1.2.3