diff options
Diffstat (limited to 'include/gudhi/Skeleton_blocker_contractor.h')
-rw-r--r-- | include/gudhi/Skeleton_blocker_contractor.h | 582 |
1 files changed, 0 insertions, 582 deletions
diff --git a/include/gudhi/Skeleton_blocker_contractor.h b/include/gudhi/Skeleton_blocker_contractor.h deleted file mode 100644 index 13086161..00000000 --- a/include/gudhi/Skeleton_blocker_contractor.h +++ /dev/null @@ -1,582 +0,0 @@ -/* This file is part of the Gudhi Library. The Gudhi library - * (Geometric Understanding in Higher Dimensions) is a generic C++ - * library for computational topology. - * - * Author(s): David Salinas - * - * Copyright (C) 2014 Inria - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef SKELETON_BLOCKER_CONTRACTOR_H_ -#define SKELETON_BLOCKER_CONTRACTOR_H_ - -// todo remove the queue to be independent from cgald -#include <gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h> - -#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/Debug_utils.h> - - -#include <boost/scoped_array.hpp> -#include <boost/scoped_ptr.hpp> - -#include <memory> -#include <cassert> -#include <list> -#include <utility> // for pair -#include <vector> - -namespace Gudhi { - -namespace contraction { - -template <class Profile> -Placement_policy<Profile>* make_first_vertex_placement() { - return new First_vertex_placement<Profile>(); -} - -template <class Profile> -Valid_contraction_policy<Profile>* make_link_valid_contraction() { - return new Link_condition_valid_contraction<Profile>(); -} - -/** - *@brief Visitor to remove popable blockers after an edge contraction. - */ -template <class Profile> -class Contraction_visitor_remove_popable : public Contraction_visitor<Profile> { - public: - typedef typename Profile::Point Point; - typedef typename Profile::Complex Complex; - typedef typename Complex::Vertex_handle Vertex_handle; - - void on_contracted(const Profile &profile, boost::optional< Point > placement) override { - profile.complex().remove_all_popable_blockers(profile.v0_handle()); - } -}; - -template <class Profile> -Contraction_visitor<Profile>* make_remove_popable_blockers_visitor() { - return new Contraction_visitor_remove_popable<Profile>(); -} - -/** - *@class Skeleton_blocker_contractor - *@brief Class that allows to contract iteratively edges of a simplicial complex. - *@ingroup contr - * - * @details 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 http://doc.cgal.org/latest/Surface_mesh_simplification/index.html). - * - * 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). - * - */ -template<class GeometricSimplifiableComplex, class EdgeProfile = Edge_profile<GeometricSimplifiableComplex>> -class Skeleton_blocker_contractor : private skeleton_blocker::Dummy_complex_visitor< -typename GeometricSimplifiableComplex::Vertex_handle> { - GeometricSimplifiableComplex& complex_; - - public: - typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex; - typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle; - typedef typename GeometricSimplifiableComplex::Simplex Simplex; - 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<Profile> Cost_policy_; - typedef Placement_policy<Profile> Placement_policy_; - typedef Valid_contraction_policy<Profile> Valid_contraction_policy_; - typedef Contraction_visitor<EdgeProfile> Contraction_visitor_; - typedef Edge_profile_factory<EdgeProfile> Edge_profile_factory_; - - - - typedef boost::optional<double> Cost_type; - typedef boost::optional<Point> 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 popped out - // first. - return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost(); - } - - Self const* algorithm_; - }; - - struct Undirected_edge_id : boost::put_get_helper<size_type, Undirected_edge_id> { - 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<Edge_handle, Compare_cost, Undirected_edge_id> 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> 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<Edge_handle> pop_from_PQ() { - boost::optional<Edge_handle> 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()) { - 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<Point> get_placement(const Profile& profile) const { - return (*placement_policy_)(profile); - } - - bool is_contraction_valid(Profile const& profile, Placement_type placement) const { - if (!valid_contraction_policy_) return true; - 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_handle> edge; - while ((edge = pop_from_PQ()) && ((num_contraction < num_max_contractions) || (unspecified_num_contractions))) { - Profile const& profile = create_profile(*edge); - Cost_type cost(get_data(*edge).cost()); - if (contraction_visitor_) contraction_visitor_->on_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(); - 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"); - } - } - if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached(); - } - - 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<std::pair<Edge_handle, Placement_type > > top_edge() { - boost::optional<std::pair<Edge_handle, Placement_type > > res; - - if (!heap_PQ_->empty()) { - auto edge = heap_PQ_->top(); - Profile const& profile = create_profile(edge); - Placement_type placement = get_placement(profile); - res = std::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<Profile>), - placement_policy_(new First_vertex_placement<Profile>), - valid_contraction_policy_(new Link_condition_valid_contraction<Profile>), - 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<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) : - 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<Edge_handle> 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<Edge_handle> 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<Edge_handle> ax(complex_[std::make_pair(a, x)]); - boost::optional<Edge_handle> 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 * 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 a l'utilisateur ? booleen update_heap_on_removed_blocker? - Simplex 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_> cost_policy_; - std::shared_ptr<Placement_policy_> placement_policy_; - std::shared_ptr<Valid_contraction_policy_> valid_contraction_policy_; - std::shared_ptr<Contraction_visitor_> 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_profile_factory_; - Edge_data_array edge_data_array_; - - boost::scoped_ptr<PQ> heap_PQ_; - int initial_num_edges_heap_; - int current_num_edges_heap_; -}; - -} // namespace contraction - -} // namespace Gudhi - -#endif // SKELETON_BLOCKER_CONTRACTOR_H_ |