From 429d4f06cede4d97144592eff91689fc1c707474 Mon Sep 17 00:00:00 2001 From: vrouvrea Date: Wed, 10 Dec 2014 14:04:18 +0000 Subject: Add persistent unit test - warning fix - cpplint fix git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/cpplint_test@346 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 9ef774de762c68a449b2a9791085f58c39a0705e --- CMakeLists.txt | 1 + scripts/check_code_coverage.sh | 3 +- .../example/rips_multifield_persistence.cpp | 2 + .../include/gudhi/Persistent_cohomology.h | 1365 ++++++++++---------- .../include/gudhi/Persistent_cohomology/Field_Zp.h | 196 +-- .../gudhi/Persistent_cohomology/Multi_field.h | 255 ++-- .../Persistent_cohomology_column.h | 249 ++-- src/Persistent_cohomology/test/CMakeLists.txt | 40 + src/Persistent_cohomology/test/README | 12 + .../test/persistent_cohomology_unit_test.cpp | 170 +++ ...persistent_cohomology_unit_test_multi_field.cpp | 129 ++ ...le_for_multi_field_unit_test.txt.REMOVED.git-id | 1 + .../test/simplex_tree_file_for_unit_test.txt | 98 ++ src/Simplex_tree/include/gudhi/Simplex_tree.h | 63 +- .../gudhi/Simplex_tree/Simplex_tree_iterators.h | 15 +- .../Simplex_tree_node_explicit_storage.h | 25 +- .../include/gudhi/Simplex_tree/indexing_tag.h | 7 +- src/Simplex_tree/test/CMakeLists.txt | 18 +- src/Simplex_tree/test/README | 2 +- src/Simplex_tree/test/UnitTestSimplexTree.cpp | 400 ------ src/Simplex_tree/test/simplex_tree_unit_test.cpp | 400 ++++++ 21 files changed, 1949 insertions(+), 1502 deletions(-) create mode 100644 src/Persistent_cohomology/test/CMakeLists.txt create mode 100644 src/Persistent_cohomology/test/README create mode 100644 src/Persistent_cohomology/test/persistent_cohomology_unit_test.cpp create mode 100644 src/Persistent_cohomology/test/persistent_cohomology_unit_test_multi_field.cpp create mode 100644 src/Persistent_cohomology/test/simplex_tree_file_for_multi_field_unit_test.txt.REMOVED.git-id create mode 100644 src/Persistent_cohomology/test/simplex_tree_file_for_unit_test.txt delete mode 100644 src/Simplex_tree/test/UnitTestSimplexTree.cpp create mode 100644 src/Simplex_tree/test/simplex_tree_unit_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aa218fd..deb5f28d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ else() add_subdirectory(src/Simplex_tree/test) add_subdirectory(src/Simplex_tree/example) + add_subdirectory(src/Persistent_cohomology/test) add_subdirectory(src/Persistent_cohomology/example) add_subdirectory(src/Skeleton_blocker/example) add_subdirectory(src/Contraction/example) diff --git a/scripts/check_code_coverage.sh b/scripts/check_code_coverage.sh index b518f21a..c5f99048 100755 --- a/scripts/check_code_coverage.sh +++ b/scripts/check_code_coverage.sh @@ -12,8 +12,9 @@ rm -f $COVERAGE_FILE $LOG_FILE lcov --capture --directory $UT_DIR_TO_CHECK --no-external --output-file $COVERAGE_FILE 2>&1 | tee -a $LOG_FILE lcov --summary $COVERAGE_FILE 2>&1 | tee -a $LOG_FILE +genhtml $COVERAGE_FILE --output-directory $UT_DIR_TO_CHECK/lcov # CLEAN AFTER USE -#lcov --directory $UT_DIR_TO_CHECK --zerocounters 2>&1 | tee -a $LOG_FILE +lcov --directory $UT_DIR_TO_CHECK --zerocounters 2>&1 | tee -a $LOG_FILE LINE_PERCENTAGE=`grep "lines......:" $LOG_FILE` PERC_PER_LINE=${LINE_PERCENTAGE:14:3} diff --git a/src/Persistent_cohomology/example/rips_multifield_persistence.cpp b/src/Persistent_cohomology/example/rips_multifield_persistence.cpp index 95ef8809..c3203131 100644 --- a/src/Persistent_cohomology/example/rips_multifield_persistence.cpp +++ b/src/Persistent_cohomology/example/rips_multifield_persistence.cpp @@ -69,6 +69,8 @@ int main (int argc, char * argv[]) st.insert_graph(prox_graph); // insert the proximity graph in the simplex tree st.expansion( dim_max ); // expand the graph until dimension dim_max + std::cout << "st=" << st << std::endl; + // Sort the simplices in the order of the filtration st.initialize_filtration(); diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h index cd4a2bcc..a5867013 100644 --- a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h @@ -1,162 +1,169 @@ - /* 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): Clément Maria - * - * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) - * - * 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 . - */ - -#ifndef _PERSISTENCECOMPUTATION_SIMPLEXTREE_ -#define _PERSISTENCECOMPUTATION_SIMPLEXTREE_ +/* 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): Clément Maria + * + * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) + * + * 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 . + */ + +#ifndef SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_H_ +#define SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_H_ + +#include +#include #include #include #include #include #include -#include "gudhi/Persistent_cohomology/Persistent_cohomology_column.h" -#include "gudhi/Persistent_cohomology/Field_Zp.h" -namespace Gudhi{ +#include +#include +#include +#include +#include + +namespace Gudhi { /** \defgroup persistent_cohomology Persistent Cohomology Package - * - * Computation of persistent cohomology using the algorithm of - * \cite DBLP:journals/dcg/SilvaMV11 and \cite DBLP:journals/corr/abs-1208-5018 - * and the Compressed Annotation Matrix - * implementation of \cite DBLP:conf/esa/BoissonnatDM13 - * - * - * - - The theory of homology consists in attaching to a topological space a sequence of - (homology) groups, - capturing global topological features - like connected components, holes, cavities, etc. Persistent homology studies the evolution - -- birth, life and death -- of - these features when the topological space is changing. Consequently, the theory is essentially - composed of three elements: - topological spaces, their homology groups and an evolution scheme. - -
Topological Spaces:
- Topological spaces are represented by simplicial complexes. - Let \f$V = \{1, \cdots ,|V|\}\f$ be a set of vertices. - A simplex \f$\sigma\f$ is a subset of vertices - \f$\sigma \subseteq V\f$. A simplicial complex \f$\mathbf{K}\f$ - on \f$V\f$ is a collection of simplices \f$\{\sigma\}\f$, - \f$\sigma \subseteq V\f$, such that \f$\tau \subseteq \sigma \in \mathbf{K} - \Rightarrow \tau \in \mathbf{K}\f$. The dimension \f$n=|\sigma|-1\f$ of \f$\sigma\f$ - is its number of elements minus 1. A filtration of a simplicial complex is - a function \f$f:\mathbf{K} \rightarrow \mathbb{R}\f$ satisfying \f$f(\tau)\leq - f(\sigma)\f$ whenever \f$\tau \subseteq \sigma\f$. - - We define the concept FilteredComplex which enumerates the requirements for a class - to represent a filtered complex from which persistent homology may be computed. - We use the vocabulary of simplicial complexes, but the concept - is valid for any type of cell complex. The main requirements - are the definition of: - \li type Indexing_tag, which is a model of the concept - IndexingTag, - describing the nature of the indexing scheme, - \li type Simplex_handle to manipulate simplices, - \li method int dimension(Simplex_handle) returning - the dimension of a simplex, - \li type and method Boundary_simplex_range - boundary_simplex_range(Simplex_handle) that returns - a range giving access to the codimension 1 subsimplices of the - input simplex, as-well-as the coefficients \f$(-1)^i\f$ in the - definition of the operator \f$\partial\f$. The iterators have - value type Simplex_handle, - \li type and method - Filtration_simplex_range filtration_simplex_range () - that returns a range giving - access to all the simplices of the complex read in the order - assigned by the indexing scheme, - \li type and method - Filtration_value filtration (Simplex_handle) that returns the value of - the filtration on the simplex represented by the handle. - -
Homology:
- For a ring \f$\mathcal{R}\f$, the group of n-chains, - denoted \f$\mathbf{C}_n(\mathbf{K},\mathcal{R})\f$, of \f$\mathbf{K}\f$ is the - group of formal sums of - n-simplices with \f$\mathcal{R}\f$ coefficients. The boundary operator is a - linear operator - \f$\partial_n: \mathbf{C}_n(\mathbf{K},\mathcal{R}) \rightarrow \mathbf{C}_{n-1}(\mathbf{K},\mathcal{R})\f$ - such that \f$\partial_n \sigma = \partial_n [v_0, \cdots , v_n] = - \sum_{i=0}^n (-1)^{i}[v_0,\cdots ,\widehat{v_i}, \cdots,v_n]\f$, - where \f$\widehat{v_i}\f$ means \f$v_i\f$ is omitted from the list. The chain - groups form a sequence: - - \f[\cdots \ \ \mathbf{C}_n(\mathbf{K},\mathcal{R}) \xrightarrow{\ \partial_n\ } \mathbf{C}_{n-1}(\mathbf{K},\mathcal{R}) - \xrightarrow{\partial_{n-1}} \cdots \xrightarrow{\ \partial_2 \ } - \mathbf{C}_1(\mathbf{K},\mathcal{R}) \xrightarrow{\ \partial_1 \ } \mathbf{C}_0(\mathbf{K},\mathcal{R}) \f] - - of finitely many groups \f$\mathbf{C}_n(\mathbf{K},\mathcal{R})\f$ and homomorphisms - \f$\partial_n\f$, indexed by the dimension \f$n \geq 0\f$. - The boundary operators satisfy the property \f$\partial_n \circ \partial_{n+1}=0\f$ - for every \f$n > 0\f$ - and we define the homology groups: - - \f[\mathbf{H}_n(\mathbf{K},\mathcal{R}) = \ker \partial_n / \mathrm{im} \ \partial_{n+1}\f] - - We refer to \cite Munkres-elementsalgtop1984 for an introduction to homology - theory and to \cite DBLP:books/daglib/0025666 for an introduction to persistent homology. - -
Indexing Scheme:
- "Changing" a simplicial complex consists in applying a simplicial map. - An indexing scheme is a directed graph together with a traversal - order, such that two - consecutive nodes in the graph are connected by an arrow (either forward or backward). - The nodes represent simplicial complexes and the directed edges simplicial maps. - - From the computational point of view, there are two types of indexing schemes of - interest - in persistent homology: linear ones - \f$\bullet \longrightarrow \bullet \longrightarrow \cdots \longrightarrow \bullet - \longrightarrow \bullet\f$ - in persistent homology \cite DBLP:journals/dcg/ZomorodianC05 , - and zigzag ones - \f$\bullet \longrightarrow \bullet \longleftarrow \cdots - \longrightarrow \bullet - \longleftarrow \bullet \f$ in zigzag persistent - homology \cite DBLP:journals/focm/CarlssonS10. - These indexing schemes have a natural left-to-right traversal order, and we - describe them with ranges and iterators. - In the current release of the Gudhi library, only the linear case is implemented. - - In the following, we consider the case where the indexing scheme is induced - by a filtration. - Ordering the simplices - by increasing filtration values (breaking ties so as a simplex appears after - its subsimplices of same filtration value) provides an indexing scheme. - - - -
Implementations:
- We use the Compressed Annotation Matrix of \cite DBLP:conf/esa/BoissonnatDM13 to - implement the - persistent cohomology algorithm of \cite DBLP:journals/dcg/SilvaMV11 - and \cite DBLP:conf/compgeom/DeyFW14 for persistence in the class Persistent_cohomology. - - The coefficient fields available as models of CoefficientField are Field_Zp - for \f$\mathbb{Z}_p\f$ (for any prime p) and Multi_field for the multi-field persistence algorithm - -- computing persistence simultaneously in various coefficient fields -- described - in \cite boissonnat:hal-00922572. + * + * Computation of persistent cohomology using the algorithm of + * \cite DBLP:journals/dcg/SilvaMV11 and \cite DBLP:journals/corr/abs-1208-5018 + * and the Compressed Annotation Matrix + * implementation of \cite DBLP:conf/esa/BoissonnatDM13 + * + * + * + + The theory of homology consists in attaching to a topological space a sequence of + (homology) groups, + capturing global topological features + like connected components, holes, cavities, etc. Persistent homology studies the evolution + -- birth, life and death -- of + these features when the topological space is changing. Consequently, the theory is essentially + composed of three elements: + topological spaces, their homology groups and an evolution scheme. + +
Topological Spaces:
+ Topological spaces are represented by simplicial complexes. + Let \f$V = \{1, \cdots ,|V|\}\f$ be a set of vertices. + A simplex \f$\sigma\f$ is a subset of vertices + \f$\sigma \subseteq V\f$. A simplicial complex \f$\mathbf{K}\f$ + on \f$V\f$ is a collection of simplices \f$\{\sigma\}\f$, + \f$\sigma \subseteq V\f$, such that \f$\tau \subseteq \sigma \in \mathbf{K} + \Rightarrow \tau \in \mathbf{K}\f$. The dimension \f$n=|\sigma|-1\f$ of \f$\sigma\f$ + is its number of elements minus 1. A filtration of a simplicial complex is + a function \f$f:\mathbf{K} \rightarrow \mathbb{R}\f$ satisfying \f$f(\tau)\leq + f(\sigma)\f$ whenever \f$\tau \subseteq \sigma\f$. + + We define the concept FilteredComplex which enumerates the requirements for a class + to represent a filtered complex from which persistent homology may be computed. + We use the vocabulary of simplicial complexes, but the concept + is valid for any type of cell complex. The main requirements + are the definition of: + \li type Indexing_tag, which is a model of the concept + IndexingTag, + describing the nature of the indexing scheme, + \li type Simplex_handle to manipulate simplices, + \li method int dimension(Simplex_handle) returning + the dimension of a simplex, + \li type and method Boundary_simplex_range + boundary_simplex_range(Simplex_handle) that returns + a range giving access to the codimension 1 subsimplices of the + input simplex, as-well-as the coefficients \f$(-1)^i\f$ in the + definition of the operator \f$\partial\f$. The iterators have + value type Simplex_handle, + \li type and method + Filtration_simplex_range filtration_simplex_range () + that returns a range giving + access to all the simplices of the complex read in the order + assigned by the indexing scheme, + \li type and method + Filtration_value filtration (Simplex_handle) that returns the value of + the filtration on the simplex represented by the handle. + +
Homology:
+ For a ring \f$\mathcal{R}\f$, the group of n-chains, + denoted \f$\mathbf{C}_n(\mathbf{K},\mathcal{R})\f$, of \f$\mathbf{K}\f$ is the + group of formal sums of + n-simplices with \f$\mathcal{R}\f$ coefficients. The boundary operator is a + linear operator + \f$\partial_n: \mathbf{C}_n(\mathbf{K},\mathcal{R}) \rightarrow \mathbf{C}_{n-1}(\mathbf{K},\mathcal{R})\f$ + such that \f$\partial_n \sigma = \partial_n [v_0, \cdots , v_n] = + \sum_{i=0}^n (-1)^{i}[v_0,\cdots ,\widehat{v_i}, \cdots,v_n]\f$, + where \f$\widehat{v_i}\f$ means \f$v_i\f$ is omitted from the list. The chain + groups form a sequence: + + \f[\cdots \ \ \mathbf{C}_n(\mathbf{K},\mathcal{R}) \xrightarrow{\ \partial_n\ } \mathbf{C}_{n-1}(\mathbf{K},\mathcal{R}) + \xrightarrow{\partial_{n-1}} \cdots \xrightarrow{\ \partial_2 \ } + \mathbf{C}_1(\mathbf{K},\mathcal{R}) \xrightarrow{\ \partial_1 \ } \mathbf{C}_0(\mathbf{K},\mathcal{R}) \f] + + of finitely many groups \f$\mathbf{C}_n(\mathbf{K},\mathcal{R})\f$ and homomorphisms + \f$\partial_n\f$, indexed by the dimension \f$n \geq 0\f$. + The boundary operators satisfy the property \f$\partial_n \circ \partial_{n+1}=0\f$ + for every \f$n > 0\f$ + and we define the homology groups: + + \f[\mathbf{H}_n(\mathbf{K},\mathcal{R}) = \ker \partial_n / \mathrm{im} \ \partial_{n+1}\f] + + We refer to \cite Munkres-elementsalgtop1984 for an introduction to homology + theory and to \cite DBLP:books/daglib/0025666 for an introduction to persistent homology. + +
Indexing Scheme:
+ "Changing" a simplicial complex consists in applying a simplicial map. + An indexing scheme is a directed graph together with a traversal + order, such that two + consecutive nodes in the graph are connected by an arrow (either forward or backward). + The nodes represent simplicial complexes and the directed edges simplicial maps. + + From the computational point of view, there are two types of indexing schemes of + interest + in persistent homology: linear ones + \f$\bullet \longrightarrow \bullet \longrightarrow \cdots \longrightarrow \bullet + \longrightarrow \bullet\f$ + in persistent homology \cite DBLP:journals/dcg/ZomorodianC05 , + and zigzag ones + \f$\bullet \longrightarrow \bullet \longleftarrow \cdots + \longrightarrow \bullet + \longleftarrow \bullet \f$ in zigzag persistent + homology \cite DBLP:journals/focm/CarlssonS10. + These indexing schemes have a natural left-to-right traversal order, and we + describe them with ranges and iterators. + In the current release of the Gudhi library, only the linear case is implemented. + + In the following, we consider the case where the indexing scheme is induced + by a filtration. + Ordering the simplices + by increasing filtration values (breaking ties so as a simplex appears after + its subsimplices of same filtration value) provides an indexing scheme. + + + +
Implementations:
+ We use the Compressed Annotation Matrix of \cite DBLP:conf/esa/BoissonnatDM13 to + implement the + persistent cohomology algorithm of \cite DBLP:journals/dcg/SilvaMV11 + and \cite DBLP:conf/compgeom/DeyFW14 for persistence in the class Persistent_cohomology. + + The coefficient fields available as models of CoefficientField are Field_Zp + for \f$\mathbb{Z}_p\f$ (for any prime p) and Multi_field for the multi-field persistence algorithm + -- computing persistence simultaneously in various coefficient fields -- described + in \cite boissonnat:hal-00922572. \author Clément Maria @@ -167,606 +174,578 @@ namespace Gudhi{ */ /** \brief Computes the persistent cohomology of a filtered complex. -* -* The computation is implemented with a Compressed Annotation Matrix -* (CAM)\cite DBLP:conf/esa/BoissonnatDM13, -* and is adapted to the computation of Multi-Field Persistent Homology (MF) -* \cite boissonnat:hal-00922572 . -* -* \implements PersistentHomology -* -*/ -//Memory allocation policy: classic, use a mempool, etc.*/ - template < class FilteredComplex - , class CoefficientField - > //to do mem allocation policy: classic, mempool, etc. - class Persistent_cohomology { + * + * The computation is implemented with a Compressed Annotation Matrix + * (CAM)\cite DBLP:conf/esa/BoissonnatDM13, + * and is adapted to the computation of Multi-Field Persistent Homology (MF) + * \cite boissonnat:hal-00922572 . + * + * \implements PersistentHomology + * + */ +// Memory allocation policy: classic, use a mempool, etc.*/ +template // to do mem allocation policy: classic, mempool, etc. +class Persistent_cohomology { public: - - typedef FilteredComplex Complex_ds; + typedef FilteredComplex Complex_ds; // Data attached to each simplex to interface with a Property Map. - typedef typename Complex_ds::Simplex_key Simplex_key; - typedef typename Complex_ds::Simplex_handle Simplex_handle; - typedef typename Complex_ds::Filtration_value Filtration_value; - typedef typename CoefficientField::Element Arith_element; + typedef typename Complex_ds::Simplex_key Simplex_key; + typedef typename Complex_ds::Simplex_handle Simplex_handle; + typedef typename Complex_ds::Filtration_value Filtration_value; + typedef typename CoefficientField::Element Arith_element; // Compressed Annotation Matrix types: // Column type - typedef Persistent_cohomology_column < Simplex_key - , Arith_element - > Column; // contains 1 set_hook + typedef Persistent_cohomology_column Column; // contains 1 set_hook // Cell type - typedef typename Column::Cell Cell; // contains 2 list_hooks + typedef typename Column::Cell Cell; // contains 2 list_hooks // Remark: constant_time_size must be false because base_hook_cam_h has auto_unlink link_mode - typedef boost::intrusive::list < Cell - , boost::intrusive::constant_time_size - , boost::intrusive::base_hook< base_hook_cam_h > - > Hcell; - - typedef boost::intrusive::set < Column - , boost::intrusive::constant_time_size - > Cam; + typedef boost::intrusive::list, + boost::intrusive::base_hook > Hcell; + + typedef boost::intrusive::set > Cam; // Sparse column type for the annotation of the boundary of an element. - typedef std::vector< std::pair > A_ds_type; + typedef std::vector > A_ds_type; // Persistent interval type. The Arith_element field is used for the multi-field framework. - typedef boost::tuple< Simplex_handle - , Simplex_handle - , Arith_element > Persistent_interval; - -/** \brief Initializes the Persistent_cohomology class. - * - * @param[in] cpx Complex for which the persistent homology is compiuted. - cpx is a model of FilteredComplex - * - * @param[in] persistence_dim_max if true, the persistent homology for the maximal dimension in the - * complex is computed. If false, it is ignored. Default is false. - */ -Persistent_cohomology ( Complex_ds & cpx - , bool persistence_dim_max = false ) -: cpx_ (&cpx) -, dim_max_(cpx.dimension()) // upper bound on the dimension of the simplices -, coeff_field_() // initialize the field coefficient structure. -, ds_rank_ (cpx_->num_simplices()) // union-find -, ds_parent_(cpx_->num_simplices()) // union-find -, ds_repr_ (cpx_->num_simplices(),NULL) // union-find -> annotation vectors -, dsets_(&ds_rank_[0],&ds_parent_[0]) // union-find -, cam_() // collection of annotation vectors -, zero_cocycles_() // union-find -> Simplex_key of creator for 0-homology -, transverse_idx_() // key -> row -, persistent_pairs_() -, interval_length_policy(&cpx,0) -, column_pool_(new boost::object_pool< Column > ()) // memory pools for the CAM -, cell_pool_(new boost::object_pool< Cell > ()) -{ - if( persistence_dim_max ) { ++dim_max_; } - Simplex_key idx_fil = 0; - for(auto & sh : cpx_->filtration_simplex_range()) - { - cpx_->assign_key(sh,idx_fil); ++idx_fil; - dsets_.make_set(cpx_->key(sh)); + typedef boost::tuple Persistent_interval; + + /** \brief Initializes the Persistent_cohomology class. + * + * @param[in] cpx Complex for which the persistent homology is compiuted. + cpx is a model of FilteredComplex + * + * @param[in] persistence_dim_max if true, the persistent homology for the maximal dimension in the + * complex is computed. If false, it is ignored. Default is false. + */ + explicit Persistent_cohomology(Complex_ds& cpx) + : cpx_(&cpx), + dim_max_(cpx.dimension()), // upper bound on the dimension of the simplices + coeff_field_(), // initialize the field coefficient structure. + ds_rank_(cpx_->num_simplices()), // union-find + ds_parent_(cpx_->num_simplices()), // union-find + ds_repr_(cpx_->num_simplices(), NULL), // union-find -> annotation vectors + dsets_(&ds_rank_[0], &ds_parent_[0]), // union-find + cam_(), // collection of annotation vectors + zero_cocycles_(), // union-find -> Simplex_key of creator for 0-homology + transverse_idx_(), // key -> row + persistent_pairs_(), + interval_length_policy(&cpx, 0), + column_pool_(new boost::object_pool()), // memory pools for the CAM + cell_pool_(new boost::object_pool()) { + Simplex_key idx_fil = 0; + for (auto & sh : cpx_->filtration_simplex_range()) { + cpx_->assign_key(sh, idx_fil); + ++idx_fil; + dsets_.make_set(cpx_->key(sh)); + } } -} - -~Persistent_cohomology() -{ -//Clean the remaining columns in the matrix. - for(auto & cam_ref : cam_) { cam_ref.col_.clear(); } -//Clean the transversal lists - for( auto & transverse_ref : transverse_idx_ ) - { transverse_ref.second.row_->clear(); delete transverse_ref.second.row_; } -//Clear the memory pools - delete column_pool_; - delete cell_pool_; -} - -private: -struct length_interval { - length_interval ( Complex_ds * cpx - , Filtration_value min_length) - : cpx_(cpx) - , min_length_(min_length) {} - - bool operator()(Simplex_handle sh1, Simplex_handle sh2) - { return cpx_->filtration(sh2) - cpx_->filtration(sh1) > min_length_; } - - void set_length(Filtration_value new_length) { min_length_ = new_length; } - - Complex_ds * cpx_; - Filtration_value min_length_; -}; + /** \brief Initializes the Persistent_cohomology class. + * + * @param[in] cpx Complex for which the persistent homology is compiuted. + cpx is a model of FilteredComplex + * + * @param[in] persistence_dim_max if true, the persistent homology for the maximal dimension in the + * complex is computed. If false, it is ignored. Default is false. + */ + Persistent_cohomology(Complex_ds& cpx, bool persistence_dim_max) + : Persistent_cohomology(cpx) { + if (persistence_dim_max) { + ++dim_max_; + } + } -public: -/** \brief Initializes the coefficient field.*/ -void init_coefficients( int charac ) { coeff_field_.init(charac); } -/** \brief Initializes the coefficient field for multi-field persistent homology.*/ -void init_coefficients( int charac_min - , int charac_max ) { coeff_field_.init(charac_min,charac_max); } - -/** \brief Compute the persistent homology of the filtered simplicial - * complex. - * - * @param[in] min_interval_length the computation disgards all intervals of length - * less or equal than min_interval_length - * - * Assumes that the filtration provided by the simplicial complex is - * valid. Undefined behavior otherwise. */ -void compute_persistent_cohomology ( Filtration_value min_interval_length = 0 ) -{ - interval_length_policy.set_length(min_interval_length); - // Compute all finite intervals - for( auto sh : cpx_->filtration_simplex_range() ) - { - int dim_simplex = cpx_->dimension(sh); - switch(dim_simplex) { - case 0 : break; - case 1 : update_cohomology_groups_edge( sh ) ; break; - default: update_cohomology_groups( sh, dim_simplex ); break; + ~Persistent_cohomology() { +// Clean the remaining columns in the matrix. + for (auto & cam_ref : cam_) { + cam_ref.col_.clear(); } +// Clean the transversal lists + for (auto & transverse_ref : transverse_idx_) { + transverse_ref.second.row_->clear(); + delete transverse_ref.second.row_; + } +// Clear the memory pools + delete column_pool_; + delete cell_pool_; } - // Compute infinite intervals of dimension 0 - Simplex_key key; - for(auto v_sh : cpx_->skeleton_simplex_range(0)) //for all 0-dimensional simplices - { - key = cpx_->key(v_sh); - - if( ds_parent_[key] == key //root of its tree - && zero_cocycles_.find(key) == zero_cocycles_.end() ) - { - persistent_pairs_.push_back( Persistent_interval ( cpx_->simplex(key) - , cpx_->null_simplex() - , coeff_field_.characteristic() ) - ); + + private: + struct length_interval { + length_interval(Complex_ds * cpx, Filtration_value min_length) + : cpx_(cpx), + min_length_(min_length) { + } + + bool operator()(Simplex_handle sh1, Simplex_handle sh2) { + return cpx_->filtration(sh2) - cpx_->filtration(sh1) > min_length_; + } + + void set_length(Filtration_value new_length) { + min_length_ = new_length; } + + Complex_ds * cpx_; + Filtration_value min_length_; + }; + + public: + /** \brief Initializes the coefficient field.*/ + void init_coefficients(uint8_t charac) { + coeff_field_.init(charac); } - for( auto zero_idx : zero_cocycles_ ) - { - persistent_pairs_.push_back( Persistent_interval ( cpx_->simplex(zero_idx.second) - , cpx_->null_simplex() - , coeff_field_.characteristic() ) - ); + /** \brief Initializes the coefficient field for multi-field persistent homology.*/ + void init_coefficients(uint8_t charac_min, uint8_t charac_max) { + coeff_field_.init(charac_min, charac_max); } -// Compute infinite interval of dimension > 0 - for(auto cocycle : transverse_idx_) - { - persistent_pairs_.push_back( Persistent_interval ( cpx_->simplex (cocycle.first) - , cpx_->null_simplex() - , cocycle.second.characteristics_ ) ); + + /** \brief Compute the persistent homology of the filtered simplicial + * complex. + * + * @param[in] min_interval_length the computation disgards all intervals of length + * less or equal than min_interval_length + * + * Assumes that the filtration provided by the simplicial complex is + * valid. Undefined behavior otherwise. */ + void compute_persistent_cohomology(Filtration_value min_interval_length = 0) { + interval_length_policy.set_length(min_interval_length); + // Compute all finite intervals + for (auto sh : cpx_->filtration_simplex_range()) { + int dim_simplex = cpx_->dimension(sh); + switch (dim_simplex) { + case 0: + break; + case 1: + update_cohomology_groups_edge(sh); + break; + default: + update_cohomology_groups(sh, dim_simplex); + break; + } + } + // Compute infinite intervals of dimension 0 + Simplex_key key; + for (auto v_sh : cpx_->skeleton_simplex_range(0)) { // for all 0-dimensional simplices + key = cpx_->key(v_sh); + + if (ds_parent_[key] == key // root of its tree + && zero_cocycles_.find(key) == zero_cocycles_.end()) { + persistent_pairs_.push_back( + Persistent_interval(cpx_->simplex(key), cpx_->null_simplex(), coeff_field_.characteristic())); + } + } + for (auto zero_idx : zero_cocycles_) { + persistent_pairs_.push_back( + Persistent_interval(cpx_->simplex(zero_idx.second), cpx_->null_simplex(), coeff_field_.characteristic())); + } +// Compute infinite interval of dimension > 0 + for (auto cocycle : transverse_idx_) { + persistent_pairs_.push_back( + Persistent_interval(cpx_->simplex(cocycle.first), cpx_->null_simplex(), cocycle.second.characteristics_)); + } } -} - - - -private: -/** \brief Update the cohomology groups under the insertion of an edge. - * - * The 0-homology is maintained with a simple Union-Find data structure, which - * explains the existance of a specific function of edge insertions. */ -void update_cohomology_groups_edge ( Simplex_handle sigma ) -{ - Simplex_handle u,v; - boost::tie(u,v) = cpx_->endpoints(sigma); - - Simplex_key ku = dsets_.find_set( cpx_->key(u) ); - Simplex_key kv = dsets_.find_set( cpx_->key(v) ); - - if(ku != kv ) { // Destroy a connected component - dsets_.link(ku,kv); - // Keys of the simplices which created the connected components containing - // respectively u and v. - Simplex_key idx_coc_u, idx_coc_v; - auto map_it_u = zero_cocycles_.find(ku); - // If the index of the cocycle representing the class is already ku. - if (map_it_u == zero_cocycles_.end()) { idx_coc_u = ku; } - else { idx_coc_u = map_it_u->second; } - - auto map_it_v = zero_cocycles_.find(kv); - // If the index of the cocycle representing the class is already kv. - if (map_it_v == zero_cocycles_.end()) { idx_coc_v = kv; } - else { idx_coc_v = map_it_v->second; } - - if(cpx_->filtration(cpx_->simplex(idx_coc_u)) - < cpx_->filtration(cpx_->simplex(idx_coc_v)) ) // Kill cocycle [idx_coc_v], which is younger. - { - if(interval_length_policy(cpx_->simplex(idx_coc_v),sigma)) { - persistent_pairs_.push_back ( Persistent_interval ( cpx_->simplex(idx_coc_v) - , sigma - , coeff_field_.characteristic() - ) - ); + + private: + /** \brief Update the cohomology groups under the insertion of an edge. + * + * The 0-homology is maintained with a simple Union-Find data structure, which + * explains the existance of a specific function of edge insertions. */ + void update_cohomology_groups_edge(Simplex_handle sigma) { + Simplex_handle u, v; + boost::tie(u, v) = cpx_->endpoints(sigma); + + Simplex_key ku = dsets_.find_set(cpx_->key(u)); + Simplex_key kv = dsets_.find_set(cpx_->key(v)); + + if (ku != kv) { // Destroy a connected component + dsets_.link(ku, kv); + // Keys of the simplices which created the connected components containing + // respectively u and v. + Simplex_key idx_coc_u, idx_coc_v; + auto map_it_u = zero_cocycles_.find(ku); + // If the index of the cocycle representing the class is already ku. + if (map_it_u == zero_cocycles_.end()) { + idx_coc_u = ku; + } else { + idx_coc_u = map_it_u->second; } - // Maintain the index of the 0-cocycle alive. - if( kv != idx_coc_v ) { zero_cocycles_.erase( map_it_v ); } - if( kv == dsets_.find_set(kv) ) { - if( ku != idx_coc_u ) { zero_cocycles_.erase( map_it_u ); } + + auto map_it_v = zero_cocycles_.find(kv); + // If the index of the cocycle representing the class is already kv. + if (map_it_v == zero_cocycles_.end()) { + idx_coc_v = kv; + } else { + idx_coc_v = map_it_v->second; + } + + if (cpx_->filtration(cpx_->simplex(idx_coc_u)) + < cpx_->filtration(cpx_->simplex(idx_coc_v))) { // Kill cocycle [idx_coc_v], which is younger. + if (interval_length_policy(cpx_->simplex(idx_coc_v), sigma)) { + persistent_pairs_.push_back( + Persistent_interval(cpx_->simplex(idx_coc_v), sigma, coeff_field_.characteristic())); + } + // Maintain the index of the 0-cocycle alive. + if (kv != idx_coc_v) { + zero_cocycles_.erase(map_it_v); + } + if (kv == dsets_.find_set(kv)) { + if (ku != idx_coc_u) { + zero_cocycles_.erase(map_it_u); + } zero_cocycles_[kv] = idx_coc_u; } - } - else // Kill cocycle [idx_coc_u], which is younger. - { - if(interval_length_policy(cpx_->simplex(idx_coc_u),sigma)) { - persistent_pairs_.push_back ( Persistent_interval ( cpx_->simplex(idx_coc_u) - , sigma - , coeff_field_.characteristic() - ) - ); + } else { // Kill cocycle [idx_coc_u], which is younger. + if (interval_length_policy(cpx_->simplex(idx_coc_u), sigma)) { + persistent_pairs_.push_back( + Persistent_interval(cpx_->simplex(idx_coc_u), sigma, coeff_field_.characteristic())); + } + // Maintain the index of the 0-cocycle alive. + if (ku != idx_coc_u) { + zero_cocycles_.erase(map_it_u); } - // Maintain the index of the 0-cocycle alive. - if( ku != idx_coc_u ) { zero_cocycles_.erase( map_it_u ); } - if( ku == dsets_.find_set(ku) ) { - if( kv != idx_coc_v ) { zero_cocycles_.erase( map_it_v ); } + if (ku == dsets_.find_set(ku)) { + if (kv != idx_coc_v) { + zero_cocycles_.erase(map_it_v); + } zero_cocycles_[ku] = idx_coc_v; } } - cpx_->assign_key(sigma,cpx_->null_key()); + cpx_->assign_key(sigma, cpx_->null_key()); + } else { // If ku == kv, same connected component: create a 1-cocycle class. + create_cocycle(sigma, coeff_field_.multiplicative_identity(), coeff_field_.characteristic()); + } } - else { // If ku == kv, same connected component: create a 1-cocycle class. - create_cocycle( sigma, coeff_field_.multiplicative_identity(), coeff_field_.characteristic() ); - } -} -/* - * Compute the annotation of the boundary of a simplex. - */ -void annotation_of_the_boundary(std::map< Simplex_key, Arith_element > & map_a_ds - , Simplex_handle sigma - , int dim_sigma ) -{ - // traverses the boundary of sigma, keeps track of the annotation vectors, - // with multiplicity, in a map. - std::map < Column *, int > annotations_in_boundary; - std::pair < typename std::map< Column *, int >::iterator - , bool > result_insert_bound; - int sign = 1 - 2 * (dim_sigma % 2); // \in {-1,1} provides the sign in the - // alternate sum in the boundary. - Simplex_key key; Column * curr_col; - - for( auto sh : cpx_->boundary_simplex_range(sigma) ) - { - key = cpx_->key(sh); - if( key != cpx_->null_key() ) // A simplex with null_key is a killer, and have null annotation - { // vector. + /* + * Compute the annotation of the boundary of a simplex. + */ + void annotation_of_the_boundary( + std::map & map_a_ds, Simplex_handle sigma, + int dim_sigma) { + // traverses the boundary of sigma, keeps track of the annotation vectors, + // with multiplicity, in a map. + std::map annotations_in_boundary; + std::pair::iterator, bool> result_insert_bound; + int sign = 1 - 2 * (dim_sigma % 2); // \in {-1,1} provides the sign in the + // alternate sum in the boundary. + Simplex_key key; + Column * curr_col; + + for (auto sh : cpx_->boundary_simplex_range(sigma)) { + key = cpx_->key(sh); + if (key != cpx_->null_key()) { // A simplex with null_key is a killer, and have null annotation // Find its annotation vector - curr_col = ds_repr_[ dsets_.find_set(key) ]; - if( curr_col != NULL ) - { // and insert it in annotations_in_boundary with multyiplicative factor "sign". - result_insert_bound = - annotations_in_boundary.insert(std::pair(curr_col,sign)); - if( !(result_insert_bound.second) ) { result_insert_bound.first->second += sign; } + curr_col = ds_repr_[dsets_.find_set(key)]; + if (curr_col != NULL) { // and insert it in annotations_in_boundary with multyiplicative factor "sign". + result_insert_bound = annotations_in_boundary.insert(std::pair(curr_col, sign)); + if (!(result_insert_bound.second)) { + result_insert_bound.first->second += sign; + } } } - sign = -sign; - } - // Sum the annotations with multiplicity, using a map - // to represent a sparse vector. - std::pair < typename std::map < Simplex_key, Arith_element >::iterator - , bool > result_insert_a_ds; - - for( auto ann_ref : annotations_in_boundary ) - { - if(ann_ref.second != coeff_field_.additive_identity()) // For all columns in the boundary, - { - for( auto cell_ref : ann_ref.first->col_ ) // insert every cell in map_a_ds with multiplicity - { - Arith_element w_y = - coeff_field_.times(cell_ref.coefficient_ , ann_ref.second); //coefficient * multiplicity - - if( w_y != coeff_field_.additive_identity() ) // if != 0 - { - result_insert_a_ds = map_a_ds.insert(std::pair< Simplex_key - , Arith_element >(cell_ref.key_ , w_y)); - if( !(result_insert_a_ds.second) ) //if cell_ref.key_ already a Key in map_a_ds - { - coeff_field_.plus_equal(result_insert_a_ds.first->second, w_y); - if(result_insert_a_ds.first->second == coeff_field_.additive_identity()) - { map_a_ds.erase(result_insert_a_ds.first); } + sign = -sign; + } + // Sum the annotations with multiplicity, using a map + // to represent a sparse vector. + std::pair::iterator, bool> result_insert_a_ds; + + for (auto ann_ref : annotations_in_boundary) { + if (ann_ref.second != coeff_field_.additive_identity()) { // For all columns in the boundary, + for (auto cell_ref : ann_ref.first->col_) { // insert every cell in map_a_ds with multiplicity + Arith_element w_y = coeff_field_.times(cell_ref.coefficient_, ann_ref.second); // coefficient * multiplicity + + if (w_y != coeff_field_.additive_identity()) { // if != 0 + result_insert_a_ds = map_a_ds.insert(std::pair(cell_ref.key_, w_y)); + if (!(result_insert_a_ds.second)) { // if cell_ref.key_ already a Key in map_a_ds + coeff_field_.plus_equal(result_insert_a_ds.first->second, w_y); + if (result_insert_a_ds.first->second == coeff_field_.additive_identity()) { + map_a_ds.erase(result_insert_a_ds.first); + } + } } } } - } + } } -} -/* - * Update the cohomology groups under the insertion of a simplex. - */ -void update_cohomology_groups ( Simplex_handle sigma - , int dim_sigma ) -{ -//Compute the annotation of the boundary of sigma: - std::map< Simplex_key, Arith_element > map_a_ds; - annotation_of_the_boundary(map_a_ds, sigma, dim_sigma ); + /* + * Update the cohomology groups under the insertion of a simplex. + */ + void update_cohomology_groups(Simplex_handle sigma, int dim_sigma) { +// Compute the annotation of the boundary of sigma: + std::map map_a_ds; + annotation_of_the_boundary(map_a_ds, sigma, dim_sigma); // Update the cohomology groups: - if( map_a_ds.empty() ) { // sigma is a creator in all fields represented in coeff_field_ - if(dim_sigma < dim_max_) { create_cocycle ( sigma - , coeff_field_.multiplicative_identity() - , coeff_field_.characteristic() );} - } - else { // sigma is a destructor in at least a field in coeff_field_ - // Convert map_a_ds to a vector - A_ds_type a_ds; //admits reverse iterators - for ( auto map_a_ds_ref : map_a_ds ) - { - a_ds.push_back( std::pair< Simplex_key - , Arith_element> ( map_a_ds_ref.first - , map_a_ds_ref.second )); - } + if (map_a_ds.empty()) { // sigma is a creator in all fields represented in coeff_field_ + if (dim_sigma < dim_max_) { + create_cocycle(sigma, coeff_field_.multiplicative_identity(), + coeff_field_.characteristic()); + } + } else { // sigma is a destructor in at least a field in coeff_field_ + // Convert map_a_ds to a vector + A_ds_type a_ds; // admits reverse iterators + for (auto map_a_ds_ref : map_a_ds) { + a_ds.push_back( + std::pair(map_a_ds_ref.first, + map_a_ds_ref.second)); + } + + Arith_element inv_x, charac; + Arith_element prod = coeff_field_.characteristic(); // Product of characteristic of the fields + for (auto a_ds_rit = a_ds.rbegin(); + (a_ds_rit != a_ds.rend()) + && (prod != coeff_field_.multiplicative_identity()); ++a_ds_rit) { + std::tie(inv_x, charac) = coeff_field_.inverse(a_ds_rit->second, prod); - - Arith_element inv_x, charac; - Arith_element prod = coeff_field_.characteristic(); // Product of characteristic of the fields - for( auto a_ds_rit = a_ds.rbegin(); - (a_ds_rit != a_ds.rend()) && (prod != coeff_field_.multiplicative_identity()); - ++a_ds_rit ) - { - std::tie(inv_x,charac) = coeff_field_.inverse ( a_ds_rit->second - , prod ); - - if( inv_x != coeff_field_.additive_identity() ) - { - destroy_cocycle ( sigma - , a_ds - , a_ds_rit->first - , inv_x - , charac ); - prod /= charac; + if (inv_x != coeff_field_.additive_identity()) { + destroy_cocycle(sigma, a_ds, a_ds_rit->first, inv_x, charac); + prod /= charac; + } + } + if (prod != coeff_field_.multiplicative_identity() + && dim_sigma < dim_max_) { + create_cocycle(sigma, coeff_field_.multiplicative_identity(prod), prod); } } - if( prod != coeff_field_.multiplicative_identity() && dim_sigma < dim_max_ ) - { create_cocycle( sigma , coeff_field_.multiplicative_identity(prod), prod ); } } -} - -/* \brief Create a new cocycle class. - * - * The class is created by the insertion of the simplex sigma. - * The methods adds a cocycle, representing the new cocycle class, - * to the matrix representing the cohomology groups. - * The new cocycle has value 0 on every simplex except on sigma - * where it worths 1.*/ -void create_cocycle ( Simplex_handle sigma - , Arith_element x - , Arith_element charac ) -{ - Simplex_key key = cpx_->key(sigma); - // Create a column containing only one cell, - Column * new_col = column_pool_->construct(Column(key)); - Cell * new_cell = cell_pool_->construct(Cell (key, x, new_col)); - new_col->col_.push_back(*new_cell); - // and insert it in the matrix, in constant time thanks to the hint cam_.end(). - // Indeed *new_col has the biggest lexicographic value because key is the - // biggest key used so far. - cam_.insert (cam_.end(), *new_col); - // Update the disjoint sets data structure. - Hcell * new_hcell = new Hcell; - new_hcell->push_back(*new_cell); - transverse_idx_[key] = cocycle(charac,new_hcell); //insert the new row - ds_repr_[key] = new_col; -} - -/* \brief Destroy a cocycle class. - * - * The cocycle class is destroyed by the insertion of sigma. - * The methods proceeds to a reduction of the matrix representing - * the cohomology groups using Gauss pivoting. The reduction zeros-out - * the row containing the cell with highest key in - * a_ds, the annotation of the boundary of simplex sigma. This key - * is "death_key".*/ -void destroy_cocycle ( Simplex_handle sigma - , A_ds_type const& a_ds - , Simplex_key death_key - , Arith_element inv_x - , Arith_element charac ) -{ - // Create a finite persistent interval - if(interval_length_policy(cpx_->simplex(death_key),sigma)) { - persistent_pairs_.push_back ( Persistent_interval ( cpx_->simplex(death_key) //creator - , sigma //destructor - , charac ) //fields - ); // for which the interval exists + + /* \brief Create a new cocycle class. + * + * The class is created by the insertion of the simplex sigma. + * The methods adds a cocycle, representing the new cocycle class, + * to the matrix representing the cohomology groups. + * The new cocycle has value 0 on every simplex except on sigma + * where it worths 1.*/ + void create_cocycle(Simplex_handle sigma, Arith_element x, + Arith_element charac) { + Simplex_key key = cpx_->key(sigma); + // Create a column containing only one cell, + Column * new_col = column_pool_->construct(Column(key)); + Cell * new_cell = cell_pool_->construct(Cell(key, x, new_col)); + new_col->col_.push_back(*new_cell); + // and insert it in the matrix, in constant time thanks to the hint cam_.end(). + // Indeed *new_col has the biggest lexicographic value because key is the + // biggest key used so far. + cam_.insert(cam_.end(), *new_col); + // Update the disjoint sets data structure. + Hcell * new_hcell = new Hcell; + new_hcell->push_back(*new_cell); + transverse_idx_[key] = cocycle(charac, new_hcell); // insert the new row + ds_repr_[key] = new_col; } - auto death_key_row = transverse_idx_.find(death_key); // Find the beginning of the row. - std::pair< typename Cam::iterator, bool > result_insert_cam; - - auto row_cell_it = death_key_row->second.row_->begin(); - - while( row_cell_it != death_key_row->second.row_->end() ) // Traverse all cells in - { // the row at index death_key. - Arith_element w = coeff_field_.times_minus( inv_x , row_cell_it->coefficient_ ); - - if( w != coeff_field_.additive_identity() ) - { - Column * curr_col = row_cell_it->self_col_; ++row_cell_it; - // Disconnect the column from the rows in the CAM. - for( auto col_cell_it = curr_col->col_.begin(); - col_cell_it != curr_col->col_.end(); ++col_cell_it ) - { col_cell_it->base_hook_cam_h::unlink(); } - - // Remove the column from the CAM before modifying its value - cam_.erase( cam_.iterator_to(*curr_col) ); - // Proceed to the reduction of the column - plus_equal_column(*curr_col, a_ds, w); - - if( curr_col->col_.empty() ) // If the column is null - { - ds_repr_[ curr_col->class_key_ ] = NULL; - column_pool_->free(curr_col); //delete curr_col; - } - else - { - // Find whether the column obtained is already in the CAM - result_insert_cam = cam_.insert( *curr_col ); - if ( result_insert_cam.second ) // If it was not in the CAM before: insertion has succeeded - { - for ( auto col_cell_it = curr_col->col_.begin(); - col_cell_it != curr_col->col_.end(); ++col_cell_it ) //re-establish the row links - { transverse_idx_[ col_cell_it->key_ ].row_->push_front(*col_cell_it); } + /* \brief Destroy a cocycle class. + * + * The cocycle class is destroyed by the insertion of sigma. + * The methods proceeds to a reduction of the matrix representing + * the cohomology groups using Gauss pivoting. The reduction zeros-out + * the row containing the cell with highest key in + * a_ds, the annotation of the boundary of simplex sigma. This key + * is "death_key".*/ + void destroy_cocycle(Simplex_handle sigma, A_ds_type const& a_ds, + Simplex_key death_key, Arith_element inv_x, + Arith_element charac) { + // Create a finite persistent interval for which the interval exists + if (interval_length_policy(cpx_->simplex(death_key), sigma)) { + persistent_pairs_.push_back(Persistent_interval(cpx_->simplex(death_key) // creator + , sigma // destructor + , charac)); // fields + } + + auto death_key_row = transverse_idx_.find(death_key); // Find the beginning of the row. + std::pair result_insert_cam; + + auto row_cell_it = death_key_row->second.row_->begin(); + + while (row_cell_it != death_key_row->second.row_->end()) { // Traverse all cells in + // the row at index death_key. + Arith_element w = coeff_field_.times_minus(inv_x, row_cell_it->coefficient_); + + if (w != coeff_field_.additive_identity()) { + Column * curr_col = row_cell_it->self_col_; + ++row_cell_it; + // Disconnect the column from the rows in the CAM. + for (auto col_cell_it = curr_col->col_.begin(); + col_cell_it != curr_col->col_.end(); ++col_cell_it) { + col_cell_it->base_hook_cam_h::unlink(); } - else // There is already an identical column in the CAM: - { // merge two disjoint sets. - dsets_.link ( curr_col->class_key_ , - result_insert_cam.first->class_key_ ); - - Simplex_key key_tmp = dsets_.find_set( curr_col->class_key_ ); - ds_repr_[ key_tmp ] = &(*(result_insert_cam.first)); - result_insert_cam.first->class_key_ = key_tmp; - column_pool_->free(curr_col); //delete curr_col; + + // Remove the column from the CAM before modifying its value + cam_.erase(cam_.iterator_to(*curr_col)); + // Proceed to the reduction of the column + plus_equal_column(*curr_col, a_ds, w); + + if (curr_col->col_.empty()) { // If the column is null + ds_repr_[curr_col->class_key_] = NULL; + column_pool_->free(curr_col); // delete curr_col; + } else { + // Find whether the column obtained is already in the CAM + result_insert_cam = cam_.insert(*curr_col); + if (result_insert_cam.second) { // If it was not in the CAM before: insertion has succeeded + for (auto col_cell_it = curr_col->col_.begin(); col_cell_it != curr_col->col_.end(); ++col_cell_it) { + // re-establish the row links + transverse_idx_[col_cell_it->key_].row_->push_front(*col_cell_it); + } + } else { // There is already an identical column in the CAM: + // merge two disjoint sets. + dsets_.link(curr_col->class_key_, + result_insert_cam.first->class_key_); + + Simplex_key key_tmp = dsets_.find_set(curr_col->class_key_); + ds_repr_[key_tmp] = &(*(result_insert_cam.first)); + result_insert_cam.first->class_key_ = key_tmp; + column_pool_->free(curr_col); // delete curr_col; + } } - } + } else { + ++row_cell_it; + } // If w == 0, pass. } - else { ++row_cell_it; } // If w == 0, pass. - } - // Because it is a killer simplex, set the data of sigma to null_key(). - if(charac == coeff_field_.characteristic()) { cpx_->assign_key( sigma, cpx_->null_key() ); } - if(death_key_row->second.characteristics_ == charac) - { - delete death_key_row->second.row_; - transverse_idx_.erase(death_key_row); + // Because it is a killer simplex, set the data of sigma to null_key(). + if (charac == coeff_field_.characteristic()) { + cpx_->assign_key(sigma, cpx_->null_key()); + } + if (death_key_row->second.characteristics_ == charac) { + delete death_key_row->second.row_; + transverse_idx_.erase(death_key_row); + } else { + death_key_row->second.characteristics_ /= charac; + } } - else { death_key_row->second.characteristics_ /= charac; } -} -/* - * Assign: target <- target + w * other. - */ -void plus_equal_column ( Column & target - , A_ds_type const& other //value_type is pair - , Arith_element w ) -{ - auto target_it = target.col_.begin(); auto other_it = other.begin(); - while ( target_it != target.col_.end() && other_it != other.end() ) - { - if(target_it->key_ < other_it->first) { ++target_it; } - else { - if(target_it->key_ > other_it->first) - { - Cell * cell_tmp = cell_pool_->construct(Cell( other_it->first //key - , coeff_field_.additive_identity() - , &target)); - - coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w); - - target.col_.insert( target_it, *cell_tmp ); - - ++other_it; - } - else { //it1->key == it2->key - //target_it->coefficient_ <- target_it->coefficient_ + other_it->second * w - coeff_field_.plus_times_equal( target_it->coefficient_ , other_it->second , w); - if( target_it->coefficient_ == coeff_field_.additive_identity() ) - { - auto tmp_it = target_it; - ++target_it; ++other_it; // iterators remain valid - Cell * tmp_cell_ptr = &(*tmp_it); - target.col_.erase(tmp_it); // removed from column - - coeff_field_.clear_coefficient(tmp_cell_ptr->coefficient_); - cell_pool_->free(tmp_cell_ptr); // delete from memory + /* + * Assign: target <- target + w * other. + */ + void plus_equal_column(Column & target, A_ds_type const& other // value_type is pair + , Arith_element w) { + auto target_it = target.col_.begin(); + auto other_it = other.begin(); + while (target_it != target.col_.end() && other_it != other.end()) { + if (target_it->key_ < other_it->first) { + ++target_it; + } else { + if (target_it->key_ > other_it->first) { + Cell * cell_tmp = cell_pool_->construct(Cell(other_it->first // key + , coeff_field_.additive_identity(), &target)); + + coeff_field_.plus_times_equal(cell_tmp->coefficient_, + other_it->second, w); + + target.col_.insert(target_it, *cell_tmp); + + ++other_it; + } else { // it1->key == it2->key + // target_it->coefficient_ <- target_it->coefficient_ + other_it->second * w + coeff_field_.plus_times_equal(target_it->coefficient_, + other_it->second, w); + if (target_it->coefficient_ == coeff_field_.additive_identity()) { + auto tmp_it = target_it; + ++target_it; + ++other_it; // iterators remain valid + Cell * tmp_cell_ptr = &(*tmp_it); + target.col_.erase(tmp_it); // removed from column + + coeff_field_.clear_coefficient(tmp_cell_ptr->coefficient_); + cell_pool_->free(tmp_cell_ptr); // delete from memory + } else { + ++target_it; + ++other_it; + } } - else { ++target_it; ++other_it; } } } - } - while(other_it != other.end()) - { - Cell * cell_tmp = cell_pool_->construct(Cell( other_it->first //key - , coeff_field_.additive_identity() - , &target)); - - coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w); - - target.col_.insert( target.col_.end(), *cell_tmp ); + while (other_it != other.end()) { + Cell * cell_tmp = cell_pool_->construct(Cell(other_it->first, coeff_field_.additive_identity(), &target)); + coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w); + target.col_.insert(target.col_.end(), *cell_tmp); - ++other_it; + ++other_it; + } } -} -/* - * Compare two intervals by length. - */ -struct cmp_intervals_by_length { - cmp_intervals_by_length( Complex_ds * sc ) : sc_ (sc) {} - bool operator() ( Persistent_interval & p1 - , Persistent_interval & p2 ) - { - return ( sc_->filtration( get<1>(p1) ) - sc_->filtration( get<0>(p1) ) - > sc_->filtration( get<1>(p2) ) - sc_->filtration( get<0>(p2) ) ); - } - Complex_ds * sc_; -}; + /* + * Compare two intervals by length. + */ + struct cmp_intervals_by_length { + explicit cmp_intervals_by_length(Complex_ds * sc) + : sc_(sc) { + } + bool operator()(const Persistent_interval & p1, const Persistent_interval & p2) { + return (sc_->filtration(get < 1 > (p1)) - sc_->filtration(get < 0 > (p1)) + > sc_->filtration(get < 1 > (p2)) - sc_->filtration(get < 0 > (p2))); + } + Complex_ds * sc_; + }; -public: -/** \brief Output the persistence diagram in ostream. - * - * The file format is the following: - * p1*...*pr dim b d - * - * where "dim" is the dimension of the homological feature, - * b and d are respectively the birth and death of the feature and - * p1*...*pr is the product of prime numbers pi such that the homology - * feature exists in homology with Z/piZ coefficients. - */ -void output_diagram(std::ostream& ostream = std::cout) -{ - cmp_intervals_by_length cmp( cpx_ ); - persistent_pairs_.sort( cmp ); - for(auto pair : persistent_pairs_) - { - ostream << get<2>(pair) << " " - << cpx_->dimension(get<0>(pair)) << " " - << cpx_->filtration(get<0>(pair)) << " " - << cpx_->filtration(get<1>(pair)) << " " - << std::endl; + public: + /** \brief Output the persistence diagram in ostream. + * + * The file format is the following: + * p1*...*pr dim b d + * + * where "dim" is the dimension of the homological feature, + * b and d are respectively the birth and death of the feature and + * p1*...*pr is the product of prime numbers pi such that the homology + * feature exists in homology with Z/piZ coefficients. + */ + void output_diagram(std::ostream& ostream = std::cout) { + cmp_intervals_by_length cmp(cpx_); + persistent_pairs_.sort(cmp); + for (auto pair : persistent_pairs_) { + ostream << get<2>(pair) << " " << cpx_->dimension(get<0>(pair)) << " " + << cpx_->filtration(get<0>(pair)) << " " + << cpx_->filtration(get<1>(pair)) << " " << std::endl; + } } -} -private: -/* - * Structure representing a cocycle. - */ -struct cocycle { - cocycle() {} - cocycle( Arith_element characteristics - , Hcell * row ) - : row_(row), characteristics_(characteristics) {} - - Hcell * row_; //points to the corresponding row in the CAM - Arith_element characteristics_; //product of field characteristics for which the cocycle exist -}; + private: + /* + * Structure representing a cocycle. + */ + struct cocycle { + cocycle() { + } + cocycle(Arith_element characteristics, Hcell * row) + : row_(row), + characteristics_(characteristics) { + } -public: - Complex_ds * cpx_; - int dim_max_; - CoefficientField coeff_field_; - -/* Disjoint sets data structure to link the model of FilteredComplex - * with the compressed annotation matrix. - * ds_rank_ is a property map Simplex_key -> int, ds_parent_ is a property map - * Simplex_key -> simplex_key_t */ - std::vector< int > ds_rank_; - std::vector< Simplex_key > ds_parent_; - std::vector< Column * > ds_repr_; - boost::disjoint_sets< int *, Simplex_key * > dsets_; -/* The compressed annotation matrix fields.*/ - Cam cam_; -/* Dictionary establishing the correspondance between the Simplex_key of - * the root vertex in the union-find ds and the Simplex_key of the vertex which - * created the connected component as a 0-dimension homology feature.*/ - std::map zero_cocycles_; -/* Key -> row. */ - std::map< Simplex_key , cocycle > transverse_idx_; -/* Persistent intervals. */ - std::list< Persistent_interval > persistent_pairs_; - length_interval interval_length_policy; - - boost::object_pool< Column > * column_pool_; - boost::object_pool< Cell > * cell_pool_; -}; + Hcell * row_; // points to the corresponding row in the CAM + Arith_element characteristics_; // product of field characteristics for which the cocycle exist + }; -/** @} */ //end defgroup persistent_cohomology + public: + Complex_ds * cpx_; + int dim_max_; + CoefficientField coeff_field_; + + /* Disjoint sets data structure to link the model of FilteredComplex + * with the compressed annotation matrix. + * ds_rank_ is a property map Simplex_key -> int, ds_parent_ is a property map + * Simplex_key -> simplex_key_t */ + std::vector ds_rank_; + std::vector ds_parent_; + std::vector ds_repr_; + boost::disjoint_sets dsets_; + /* The compressed annotation matrix fields.*/ + Cam cam_; + /* Dictionary establishing the correspondance between the Simplex_key of + * the root vertex in the union-find ds and the Simplex_key of the vertex which + * created the connected component as a 0-dimension homology feature.*/ + std::map zero_cocycles_; + /* Key -> row. */ + std::map transverse_idx_; + /* Persistent intervals. */ + std::list persistent_pairs_; + length_interval interval_length_policy; + + boost::object_pool * column_pool_; + boost::object_pool * cell_pool_; +}; -} // namespace GUDHI +/** @} */ // end defgroup persistent_cohomology +} // namespace Gudhi -#endif // _PERSISTENCECOMPUTATION_SIMPLEXTREE_ +#endif // SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_H_ diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h index af0d6605..8b09a800 100644 --- a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h @@ -1,106 +1,122 @@ - /* 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): Clément Maria - * - * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) - * - * 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 . - */ - -#ifndef GUDHI_FIELD_ZP_H -#define GUDHI_FIELD_ZP_H - -namespace Gudhi{ +/* 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): Clément Maria + * + * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) + * + * 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 . + */ + +#ifndef SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_FIELD_ZP_H_ +#define SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_FIELD_ZP_H_ + +#include +#include + +namespace Gudhi { /** \brief Structure representing the coefficient field \f$\mathbb{Z}/p\mathbb{Z}\f$ - * - * \implements CoefficientField - * \ingroup persistent_cohomology - */ + * + * \implements CoefficientField + * \ingroup persistent_cohomology + */ class Field_Zp { -public: -typedef int Element; - -Field_Zp() -: Prime(-1) -, inverse_() {} - -void init(int charac ) { - assert(charac <= 32768); - Prime = charac; - inverse_.clear(); - inverse_.reserve(charac); - inverse_.push_back(0); - for(int i=1 ; i inverse ( Element x - , Element P ) -{ return std::pair(inverse_[x],P); -} // <------ return the product of field characteristic for which x is invertible +// operator= defined on Element -/** Returns -x * y.*/ -Element times_minus ( Element x, Element y ) -{ - Element out = (-x * y) % Prime; - return (out < 0) ? out + Prime : out; -} + /** Returns y * w */ + Element times(Element y, int w) { + assert(Prime != 0); // division by zero + Element res = (y * w) % Prime; + if (res < 0) + return res + Prime; + else + return res; + } + void clear_coefficient(Element x) { + } -bool is_one ( Element x ) { return x == 1; } -bool is_zero ( Element x ) { return x == 0; } + void plus_equal(Element & x, Element y) { + assert(Prime != 0); // division by zero + x = ((x + y) % Prime); + } -//bool is_null() + /** \brief Returns the additive idendity \f$0_{\Bbbk}\f$ of the field.*/ + const Element& additive_identity() const { + return add_id_all; + } + /** \brief Returns the multiplicative identity \f$1_{\Bbbk}\f$ of the field.*/ + const Element& multiplicative_identity(Element P = 0) const { + return mult_id_all; + } + /** Returns the inverse in the field. Modifies P.*/ + std::pair inverse(Element x, Element P) { + return std::pair(inverse_[x], P); + } // <------ return the product of field characteristic for which x is invertible + + /** Returns -x * y.*/ + Element times_minus(Element x, Element y) { + assert(Prime != 0); // division by zero + Element out = (-x * y) % Prime; + return (out < 0) ? out + Prime : out; + } -/** \brief Returns the characteristic \f$p\f$ of the field.*/ -Element characteristic() { return Prime; } + /** \brief Returns the characteristic \f$p\f$ of the field.*/ + const uint8_t& characteristic() const { + return Prime; + } -private: - Element Prime; -/** Property map Element -> Element, which associate to an element its inverse in the field.*/ - std::vector< Element > inverse_; + private: + uint8_t Prime; + /** Property map Element -> Element, which associate to an element its inverse in the field.*/ + std::vector inverse_; + const Element mult_id_all; + const Element add_id_all; }; -} // namespace GUDHI +} // namespace Gudhi -#endif // GUDHI_FIELD_ZP_H +#endif // SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_FIELD_ZP_H_ diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h index 9dd0998c..5ad4e9cf 100644 --- a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h @@ -1,167 +1,188 @@ - /* 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): Clément Maria - * - * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) - * - * 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 . - */ - -#ifndef GUDHI_MULTI_FIELD_H -#define GUDHI_MULTI_FIELD_H - -#include -#include +/* 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): Clément Maria + * + * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) + * + * 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 . + */ + +#ifndef SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_MULTI_FIELD_H_ +#define SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_MULTI_FIELD_H_ + #include -namespace Gudhi{ +#include +#include + +namespace Gudhi { /** \brief Structure representing coefficients in a set of finite fields simultaneously - * using the chinese remainder theorem. - * - * \implements CoefficientField - * \ingroup persistent_cohomology - - * Details on the algorithms may be found in \cite boissonnat:hal-00922572 - */ + * using the chinese remainder theorem. + * + * \implements CoefficientField + * \ingroup persistent_cohomology + + * Details on the algorithms may be found in \cite boissonnat:hal-00922572 + */ class Multi_field { -public: - typedef mpz_class Element; - - Multi_field () {} - -/* Initialize the multi-field. The generation of prime numbers might fail with - * a very small probability.*/ - void init(int min_prime, int max_prime) - { - if(max_prime<2) - { std::cerr << "There is no prime less than " << max_prime << std::endl; } - if(min_prime > max_prime) - { std::cerr << "No prime in ["< max_prime) { + std::cerr << "No prime in [" << min_prime << ":" << max_prime << "]" + << std::endl; + } // fill the list of prime numbers - unsigned int curr_prime = min_prime; - mpz_t tmp_prime; mpz_init_set_ui(tmp_prime,min_prime); - //test if min_prime is prime - int is_prime = mpz_probab_prime_p(tmp_prime,25); //probabilistic primality test - - if(is_prime == 0) //min_prime is composite - { - mpz_nextprime(tmp_prime,tmp_prime); + uint8_t curr_prime = min_prime; + mpz_t tmp_prime; + mpz_init_set_ui(tmp_prime, min_prime); + // test if min_prime is prime + int is_prime = mpz_probab_prime_p(tmp_prime, 25); // probabilistic primality test + + if (is_prime == 0) { // min_prime is composite + mpz_nextprime(tmp_prime, tmp_prime); curr_prime = mpz_get_ui(tmp_prime); } - - while (curr_prime <= max_prime) - { + + while (curr_prime <= max_prime) { primes_.push_back(curr_prime); - mpz_nextprime(tmp_prime,tmp_prime); + mpz_nextprime(tmp_prime, tmp_prime); curr_prime = mpz_get_ui(tmp_prime); } - //set m to primorial(bound_prime) + // set m to primorial(bound_prime) prod_characteristics_ = 1; - for(auto p : primes_) - { mpz_mul_ui(prod_characteristics_.get_mpz_t(), - prod_characteristics_.get_mpz_t(), - p); + for (auto p : primes_) { + mpz_mul_ui(prod_characteristics_.get_mpz_t(), + prod_characteristics_.get_mpz_t(), p); } - num_primes_ = primes_.size(); - - //Uvect_ - Element Ui; Element tmp_elem; - for(auto p : primes_) - { + // Uvect_ + Element Ui; + Element tmp_elem; + for (auto p : primes_) { + assert(p != 0); // division by zero tmp_elem = prod_characteristics_ / p; - //Element tmp_elem_bis = 10; - mpz_powm_ui ( tmp_elem.get_mpz_t() - , tmp_elem.get_mpz_t() - , p - 1 - , prod_characteristics_.get_mpz_t() ); + // Element tmp_elem_bis = 10; + mpz_powm_ui(tmp_elem.get_mpz_t(), tmp_elem.get_mpz_t(), p - 1, + prod_characteristics_.get_mpz_t()); Uvect_.push_back(tmp_elem); } mult_id_all = 0; - for(int idx = 0; idx < num_primes_; ++idx) - { mult_id_all = (mult_id_all + Uvect_[idx]) % prod_characteristics_; } - + for (auto uvect : Uvect_) { + assert(prod_characteristics_ != 0); // division by zero + mult_id_all = (mult_id_all + uvect) % prod_characteristics_; + } } - void clear_coefficient(Element & x) { mpz_clear(x.get_mpz_t()); } + void clear_coefficient(Element & x) { + mpz_clear(x.get_mpz_t()); + } /** \brief Returns the additive idendity \f$0_{\Bbbk}\f$ of the field.*/ - Element additive_identity () { return 0; } + const Element& additive_identity() const { + return add_id_all; + } /** \brief Returns the multiplicative identity \f$1_{\Bbbk}\f$ of the field.*/ - Element multiplicative_identity () { return mult_id_all; }// 1 everywhere + const Element& multiplicative_identity() const { + return mult_id_all; + } // 1 everywhere - Element multiplicative_identity (Element Q) - { - if(Q == prod_characteristics_) { return multiplicative_identity(); } + Element multiplicative_identity(Element Q) { + if (Q == prod_characteristics_) { + return multiplicative_identity(); + } + assert(prod_characteristics_ != 0); // division by zero Element mult_id = 0; - for(int idx = 0; idx < num_primes_; ++idx) { - if( (Q % primes_[idx]) == 0 ) - { mult_id = (mult_id + Uvect_[idx]) % prod_characteristics_; } + for (unsigned int idx = 0; idx < primes_.size(); ++idx) { + assert(primes_[idx] != 0); // division by zero + if ((Q % primes_[idx]) == 0) { + mult_id = (mult_id + Uvect_[idx]) % prod_characteristics_; + } } return mult_id; - } + } /** Returns y * w */ - Element times ( Element y, int w ) { - Element tmp = (y*w) % prod_characteristics_; - if(tmp < 0) return prod_characteristics_ + tmp; + Element times(const Element& y, int w) { + assert(prod_characteristics_ != 0); // division by zero + Element tmp = (y * w) % prod_characteristics_; + if (tmp < 0) + return prod_characteristics_ + tmp; return tmp; } - void plus_equal(Element & x, Element y) - { x += y; x %= prod_characteristics_; } + void plus_equal(Element & x, const Element& y) { + assert(prod_characteristics_ != 0); // division by zero + x += y; + x %= prod_characteristics_; + } /** \brief Returns the characteristic \f$p\f$ of the field.*/ - Element characteristic() { return prod_characteristics_; } + const Element& characteristic() const { + return prod_characteristics_; + } /** Returns the inverse in the field. Modifies P.*/ - std::pair inverse ( Element x - , Element QS ) - { + std::pair inverse(Element x, Element QS) { Element QR; - mpz_gcd( QR.get_mpz_t(), x.get_mpz_t(), QS.get_mpz_t() ); // QR <- gcd(x,QS) - if( QR == QS ) return std::pair(additive_identity() - , multiplicative_identity() ); //partial inverse is 0 + mpz_gcd(QR.get_mpz_t(), x.get_mpz_t(), QS.get_mpz_t()); // QR <- gcd(x,QS) + if (QR == QS) + return std::pair(additive_identity(), multiplicative_identity()); // partial inverse is 0 Element QT = QS / QR; Element inv_qt; mpz_invert(inv_qt.get_mpz_t(), x.get_mpz_t(), QT.get_mpz_t()); - return std::pair( - (inv_qt * multiplicative_identity(QT)) % prod_characteristics_ - , QT ); + assert(prod_characteristics_ != 0); // division by zero + return std::pair( + (inv_qt * multiplicative_identity(QT)) % prod_characteristics_, QT); } /** Returns -x * y.*/ - Element times_minus ( Element x, Element y ) - { return prod_characteristics_ - ((x*y)%prod_characteristics_); } + Element times_minus(const Element& x, const Element& y) { + assert(prod_characteristics_ != 0); // division by zero + return prod_characteristics_ - ((x * y) % prod_characteristics_); + } /** Set x <- x + w * y*/ - void plus_times_equal ( Element & x, Element y, Element w ) - { x = (x + w * y) % prod_characteristics_; } - - Element prod_characteristics_; //product of characteristics of the fields - //represented by the multi-field class - std::vector primes_; //all the characteristics of the fields - std::vector Uvect_; - size_t num_primes_; //number of fields - Element mult_id_all; + void plus_times_equal(Element & x, const Element& y, const Element& w) { + assert(prod_characteristics_ != 0); // division by zero + x = (x + w * y) % prod_characteristics_; + } + Element prod_characteristics_; // product of characteristics of the fields + // represented by the multi-field class + std::vector primes_; // all the characteristics of the fields + std::vector Uvect_; + Element mult_id_all; + const Element add_id_all; }; -} // namespace GUDHI +} // namespace Gudhi -#endif // GUDHI_MULTI_FIELD_H +#endif // SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_MULTI_FIELD_H_ diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h index 0702c58e..632f8e11 100644 --- a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h @@ -1,153 +1,144 @@ - /* 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): Clément Maria - * - * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) - * - * 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 . - */ - -#ifndef GUDHI_COLUMN_LIST_H -#define GUDHI_COLUMN_LIST_H +/* 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): Clément Maria + * + * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France) + * + * 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 . + */ + +#ifndef SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_PERSISTENT_COHOMOLOGY_COLUMN_H_ +#define SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_PERSISTENT_COHOMOLOGY_COLUMN_H_ + +#include #include "boost/tuple/tuple.hpp" #include "boost/intrusive/set.hpp" #include "boost/intrusive/list.hpp" -namespace Gudhi{ +namespace Gudhi { -template < typename SimplexKey - , typename ArithmeticElement - > +template class Persistent_cohomology_column; -struct cam_h_tag; // for horizontal traversal in the CAM -struct cam_v_tag; // for vertical traversal in the CAM +struct cam_h_tag; +// for horizontal traversal in the CAM +struct cam_v_tag; +// for vertical traversal in the CAM -typedef boost::intrusive::list_base_hook - < boost::intrusive::tag < cam_h_tag > - , boost::intrusive::link_mode < boost::intrusive::auto_unlink > //allows .unlink() - > base_hook_cam_h; +typedef boost::intrusive::list_base_hook, + boost::intrusive::link_mode // allows .unlink() +> base_hook_cam_h; -typedef boost::intrusive::list_base_hook - < boost::intrusive::tag < cam_v_tag > - , boost::intrusive::link_mode < boost::intrusive::normal_link > //faster hook, less safe - > base_hook_cam_v; +typedef boost::intrusive::list_base_hook, + boost::intrusive::link_mode // faster hook, less safe +> base_hook_cam_v; /** \internal - * \brief - * - */ -template < typename SimplexKey - , typename ArithmeticElement - > -class Persistent_cohomology_cell -: public base_hook_cam_h -, public base_hook_cam_v -{ - public: - template < class T1, class T2 > friend class Persistent_cohomology; - friend class Persistent_cohomology_column < SimplexKey , ArithmeticElement >; - - typedef Persistent_cohomology_column< SimplexKey, ArithmeticElement > Column; - - Persistent_cohomology_cell( SimplexKey key - , ArithmeticElement x - , Column * self_col) - : key_(key) - , coefficient_(x) - , self_col_(self_col) {} - - SimplexKey key_; - ArithmeticElement coefficient_; - Column * self_col_; -}; - - - + * \brief + * + */ +template +class Persistent_cohomology_cell : public base_hook_cam_h, + public base_hook_cam_v { + public: + template friend class Persistent_cohomology; + friend class Persistent_cohomology_column; + + typedef Persistent_cohomology_column Column; + + Persistent_cohomology_cell(SimplexKey key, ArithmeticElement x, + Column * self_col) + : key_(key), + coefficient_(x), + self_col_(self_col) { + } + SimplexKey key_; + ArithmeticElement coefficient_; + Column * self_col_; +}; /* - * \brief Sparse column for the Compressed Annotation Matrix. - * - * The non-zero coefficients of the column are stored in a - * boost::intrusive::list. Contains a hook to be stored in a - * boost::intrusive::set. - */ -template < typename SimplexKey - , typename ArithmeticElement > -class Persistent_cohomology_column -: public boost::intrusive::set_base_hook - < boost::intrusive::link_mode< boost::intrusive::normal_link > > -{ -private: - template < class T1, class T2 > friend class Persistent_cohomology; - - typedef Persistent_cohomology_cell < SimplexKey, ArithmeticElement > Cell; - typedef boost::intrusive::list < Cell - , boost::intrusive::constant_time_size - , boost::intrusive::base_hook< base_hook_cam_v > - > Col_type; - -/** \brief Creates an empty column.*/ - Persistent_cohomology_column (SimplexKey key) - { + * \brief Sparse column for the Compressed Annotation Matrix. + * + * The non-zero coefficients of the column are stored in a + * boost::intrusive::list. Contains a hook to be stored in a + * boost::intrusive::set. + */ +template +class Persistent_cohomology_column : public boost::intrusive::set_base_hook< + boost::intrusive::link_mode > { + private: + template friend class Persistent_cohomology; + + typedef Persistent_cohomology_cell Cell; + typedef boost::intrusive::list, + boost::intrusive::base_hook > Col_type; + + /** \brief Creates an empty column.*/ + explicit Persistent_cohomology_column(SimplexKey key) { class_key_ = key; col_ = Col_type(); } -public: - /** Copy constructor.*/ - Persistent_cohomology_column( Persistent_cohomology_column const &other ) - : col_() - , class_key_(other.class_key_) - { if(!other.col_.empty()) std::cerr << "Copying a non-empty column.\n"; } - -/** \brief Returns true iff the column is null.*/ - bool is_null() { return col_.empty(); } -/** \brief Returns the key of the representative simplex of - * the set of simplices having this column as annotation vector - * in the compressed annotation matrix.*/ - SimplexKey class_key () { return class_key_; } - -/** \brief Lexicographic comparison of two columns.*/ -friend bool operator< ( const Persistent_cohomology_column& c1 - , const Persistent_cohomology_column& c2) - { - typename Col_type::const_iterator it1 = c1.col_.begin(); - typename Col_type::const_iterator it2 = c2.col_.begin(); - while(it1 != c1.col_.end() && it2 != c2.col_.end()) - { - if(it1->key_ == it2->key_) - { if(it1->coefficient_ == it2->coefficient_) { ++it1; ++it2; } - else { return it1->coefficient_ < it2->coefficient_; } } - else { return it1->key_ < it2->key_; } - } - return (it2 != c2.col_.end()); + public: + /** Copy constructor.*/ + Persistent_cohomology_column(Persistent_cohomology_column const &other) + : col_(), + class_key_(other.class_key_) { + if (!other.col_.empty()) + std::cerr << "Copying a non-empty column.\n"; } - // void display() - // { - // for(auto cell : col_) - // { std::cout << "(" << cell.key_ <<":"<key_ == it2->key_) { + if (it1->coefficient_ == it2->coefficient_) { + ++it1; + ++it2; + } else { + return it1->coefficient_ < it2->coefficient_; + } + } else { + return it1->key_ < it2->key_; + } + } + return (it2 != c2.col_.end()); + } - Col_type col_; - SimplexKey class_key_; + Col_type col_; + SimplexKey class_key_; }; -} // namespace GUDHI +} // namespace Gudhi -#endif // GUDHI_COLUMN_LIST_H +#endif // SRC_PERSISTENT_COHOMOLOGY_INCLUDE_GUDHI_PERSISTENT_COHOMOLOGY_PERSISTENT_COHOMOLOGY_COLUMN_H_ diff --git a/src/Persistent_cohomology/test/CMakeLists.txt b/src/Persistent_cohomology/test/CMakeLists.txt new file mode 100644 index 00000000..0e5bab1b --- /dev/null +++ b/src/Persistent_cohomology/test/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 2.6) +project(GUDHITestSimplexTree) + +# NEED TO FIND BOOST NEEDED COMPONENTS TO LINK WITH +find_package(Boost 1.45.0 COMPONENTS system unit_test_framework) +message("Boost_lib = ${Boost_LIBRARIES}") + +if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --coverage") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} --coverage") +endif() + +include_directories(${Boost_INCLUDE_DIRS}) +add_executable ( persistent_cohomology_unit_test persistent_cohomology_unit_test.cpp ) +target_link_libraries(persistent_cohomology_unit_test ${Boost_LIBRARIES}) + +# Unitary tests +add_test(persistent_cohomology_unit_test ${CMAKE_CURRENT_BINARY_DIR}/persistent_cohomology_unit_test) + +if(GMPXX_FOUND AND GMP_FOUND) + add_executable ( persistent_cohomology_unit_test_multi_field persistent_cohomology_unit_test_multi_field.cpp ) + target_link_libraries(persistent_cohomology_unit_test_multi_field ${Boost_LIBRARIES} ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) + + # Unitary tests + add_test(persistent_cohomology_unit_test_multi_field ${CMAKE_CURRENT_BINARY_DIR}/persistent_cohomology_unit_test_multi_field) +endif() + +if (LCOV_PATH) + # Lcov code coverage of unitary test + add_test(persistent_cohomology_unit_test_coverage.info ${CMAKE_SOURCE_DIR}/scripts/check_code_coverage.sh ${CMAKE_SOURCE_DIR}/src/Persistent_cohomology) +endif() + +if (PYTHON_PATH) + # Cpplint tests on coding style + add_test(Persistent_cohomology.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h) + add_test(Field_Zp.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h) + add_test(Multi_field.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h) + add_test(Persistent_cohomology_column.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h) +endif() \ No newline at end of file diff --git a/src/Persistent_cohomology/test/README b/src/Persistent_cohomology/test/README new file mode 100644 index 00000000..56474fa0 --- /dev/null +++ b/src/Persistent_cohomology/test/README @@ -0,0 +1,12 @@ +To compile: +*********** + +cmake . +make + +To launch with details: +*********************** + +./persistent_cohomology_unit_test --report_level=detailed --log_level=all + + ==> echo $? returns 0 in case of success (non-zero otherwise) diff --git a/src/Persistent_cohomology/test/persistent_cohomology_unit_test.cpp b/src/Persistent_cohomology/test/persistent_cohomology_unit_test.cpp new file mode 100644 index 00000000..e180bea3 --- /dev/null +++ b/src/Persistent_cohomology/test/persistent_cohomology_unit_test.cpp @@ -0,0 +1,170 @@ +#define BOOST_TEST_MODULE const_string test +#include +#include +#include +#include +#include + +#include // std::pair, std::make_pair + +#include // float comparison +#include + +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/reader_utils.h" +#include "gudhi/Simplex_tree.h" +#include "gudhi/Persistent_cohomology.h" + +using namespace Gudhi; + +typedef Simplex_tree<> typeST; + +std::string test_rips_persistence(int coefficient, int min_persistence) { + const std::string inputFile("simplex_tree_file_for_unit_test.txt"); + std::ifstream simplex_tree_stream; + simplex_tree_stream.open(inputFile.c_str()); + typeST st; + simplex_tree_stream >> st; + simplex_tree_stream.close(); + + // Display the Simplex_tree + std::cout << "The complex contains " << st.num_simplices() << " simplices" << " - dimension= " << st.dimension() + << " - filtration= " << st.filtration() << std::endl; + + // Check + BOOST_CHECK(st.num_simplices() == 98); + BOOST_CHECK(st.dimension() == 3); + BOOST_CHECK(st.filtration() == 1.89); + + // Sort the simplices in the order of the filtration + st.initialize_filtration(); + + // Compute the persistence diagram of the complex + Persistent_cohomology, Field_Zp> pcoh(st); + + pcoh.init_coefficients( coefficient ); // initializes the coefficient field for homology + // Check infinite rips + pcoh.compute_persistent_cohomology( min_persistence ); // Minimal lifetime of homology feature to be recorded. + std::ostringstream ossInfinite; + + pcoh.output_diagram(ossInfinite); + std::string strInfinite = ossInfinite.str(); + return strInfinite; +} + +void test_rips_persistence_in_dimension(int dimension) { + std::string value0(" 0 0.02 1.12"); + std::string value1(" 0 0.03 1.13"); + std::string value2(" 0 0.04 1.14"); + std::string value3(" 0 0.05 1.15"); + std::string value4(" 0 0.06 1.16"); + std::string value5(" 0 0.07 1.17"); + std::string value6(" 0 0.08 1.18"); + std::string value7(" 0 0.09 1.19"); + std::string value8(" 0 0 inf" ); + std::string value9(" 0 0.01 inf" ); + + value0.insert(0,std::to_string(dimension)); + value1.insert(0,std::to_string(dimension)); + value2.insert(0,std::to_string(dimension)); + value3.insert(0,std::to_string(dimension)); + value4.insert(0,std::to_string(dimension)); + value5.insert(0,std::to_string(dimension)); + value6.insert(0,std::to_string(dimension)); + value7.insert(0,std::to_string(dimension)); + value8.insert(0,std::to_string(dimension)); + value9.insert(0,std::to_string(dimension)); + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_SINGLE_FIELD DIM=" << dimension << " MIN_PERS=0" << std::endl; + + std::string str_rips_persistence = test_rips_persistence(dimension, 0); + + BOOST_CHECK(str_rips_persistence.find(value0) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value1) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value2) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value3) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value4) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value5) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value6) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value7) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value8) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value9) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_SINGLE_FIELD DIM=" << dimension << " MIN_PERS=1" << std::endl; + + str_rips_persistence = test_rips_persistence(dimension, 1); + + BOOST_CHECK(str_rips_persistence.find(value0) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value1) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value2) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value3) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value4) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value5) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value6) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value7) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value8) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value9) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_SINGLE_FIELD DIM=" << dimension << " MIN_PERS=2" << std::endl; + + str_rips_persistence = test_rips_persistence(dimension, 2); + + BOOST_CHECK(str_rips_persistence.find(value0) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value1) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value2) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value3) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value4) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value5) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value6) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value7) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value8) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value9) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_SINGLE_FIELD DIM=" << dimension << " MIN_PERS=Inf" << std::endl; + + str_rips_persistence = test_rips_persistence(dimension, std::numeric_limits::max()); + + BOOST_CHECK(str_rips_persistence.find(value0) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value1) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value2) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value3) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value4) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value5) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value6) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value7) == std::string::npos); // Check not found + BOOST_CHECK(str_rips_persistence.find(value8) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value9) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_single_field_dim_1 ) +{ + test_rips_persistence_in_dimension(1); +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_single_field_dim_2 ) +{ + test_rips_persistence_in_dimension(2); +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_single_field_dim_3 ) +{ + test_rips_persistence_in_dimension(3); +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_single_field_dim_5 ) +{ + test_rips_persistence_in_dimension(5); +} + +// TODO(VR): not working from 6 +// std::string str_rips_persistence = test_rips_persistence(6, 0); +// TODO(VR): division by zero +// std::string str_rips_persistence = test_rips_persistence(0, 0); diff --git a/src/Persistent_cohomology/test/persistent_cohomology_unit_test_multi_field.cpp b/src/Persistent_cohomology/test/persistent_cohomology_unit_test_multi_field.cpp new file mode 100644 index 00000000..cef29bb0 --- /dev/null +++ b/src/Persistent_cohomology/test/persistent_cohomology_unit_test_multi_field.cpp @@ -0,0 +1,129 @@ +#define BOOST_TEST_MODULE const_string test +#include +#include +#include +#include +#include + +#include // std::pair, std::make_pair + +#include // float comparison +#include + +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/reader_utils.h" +#include "gudhi/Simplex_tree.h" +#include "gudhi/Persistent_cohomology.h" +#include "gudhi/Persistent_cohomology/Multi_field.h" + +using namespace Gudhi; + +typedef Simplex_tree<> typeST; + +std::string test_rips_persistence(int min_coefficient, int max_coefficient, int min_persistence) { + const std::string inputFile("simplex_tree_file_for_multi_field_unit_test.txt"); + std::ifstream simplex_tree_stream; + simplex_tree_stream.open(inputFile.c_str()); + typeST st; + simplex_tree_stream >> st; + simplex_tree_stream.close(); + + // Display the Simplex_tree + std::cout << "The complex contains " << st.num_simplices() << " simplices" << " - dimension= " << st.dimension() + << " - filtration= " << st.filtration() << std::endl; + + // Check + BOOST_CHECK(st.num_simplices() == 6142604); + BOOST_CHECK(st.dimension() == 3); + BOOST_CHECK(st.filtration() == 0.249999); + + // Sort the simplices in the order of the filtration + st.initialize_filtration(); + + // Compute the persistence diagram of the complex + Persistent_cohomology, Multi_field> pcoh(st); + + pcoh.init_coefficients(min_coefficient, max_coefficient); // initializes the coefficient field for homology + // Check infinite rips + pcoh.compute_persistent_cohomology(min_persistence); // Minimal lifetime of homology feature to be recorded. + + std::ostringstream ossRips; + pcoh.output_diagram(ossRips); + + std::string strRips = ossRips.str(); + return strRips; +} + +void test_rips_persistence_in_dimension(int min_dimension, int max_dimension) { + std::string value0(" 0 0 inf"); + std::string value1(" 1 0.0702103 inf"); + std::string value2("2 1 0.0702103 inf"); + std::string value3("2 2 0.159992 inf"); + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_MULTI_FIELD MIN_DIM=" << min_dimension << " MAX_DIM=" << max_dimension << " MIN_PERS=0" << std::endl; + + std::string str_rips_persistence = test_rips_persistence(min_dimension, max_dimension, 1); + + BOOST_CHECK(str_rips_persistence.find(value0) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value1) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value2) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value3) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_MULTI_FIELD DIM=" << min_dimension << " MAX_DIM=" << max_dimension << " MIN_PERS=2" << std::endl; + + str_rips_persistence = test_rips_persistence(min_dimension, max_dimension, 2); + + BOOST_CHECK(str_rips_persistence.find(value0) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value1) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value2) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value3) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_MULTI_FIELD DIM=" << min_dimension << " MAX_DIM=" << max_dimension << " MIN_PERS=3" << std::endl; + + str_rips_persistence = test_rips_persistence(min_dimension, max_dimension, 3); + + BOOST_CHECK(str_rips_persistence.find(value0) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value1) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value2) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value3) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; + + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF RIPS_PERSISTENT_COHOMOLOGY_MULTI_FIELD DIM=" << min_dimension << " MAX_DIM=" << max_dimension << " MIN_PERS=Inf" << std::endl; + + str_rips_persistence = test_rips_persistence(min_dimension, max_dimension, std::numeric_limits::max()); + + BOOST_CHECK(str_rips_persistence.find(value0) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value1) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value2) != std::string::npos); // Check found + BOOST_CHECK(str_rips_persistence.find(value3) != std::string::npos); // Check found + std::cout << "str_rips_persistence=" << str_rips_persistence << std::endl; +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_multi_field_dim_1_2 ) +{ + test_rips_persistence_in_dimension(1, 2); +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_multi_field_dim_2_3 ) +{ + test_rips_persistence_in_dimension(2, 3); +} + +BOOST_AUTO_TEST_CASE( rips_persistent_cohomology_multi_field_dim_1_5 ) +{ + test_rips_persistence_in_dimension(1, 5); +} + +// TODO(VR): not working from 6 +// std::string str_rips_persistence = test_rips_persistence(6, 0); +// TODO(VR): division by zero +// std::string str_rips_persistence = test_rips_persistence(0, 0); +// TODO(VR): is result OK of : +// test_rips_persistence_in_dimension(3, 4); + diff --git a/src/Persistent_cohomology/test/simplex_tree_file_for_multi_field_unit_test.txt.REMOVED.git-id b/src/Persistent_cohomology/test/simplex_tree_file_for_multi_field_unit_test.txt.REMOVED.git-id new file mode 100644 index 00000000..2dd38515 --- /dev/null +++ b/src/Persistent_cohomology/test/simplex_tree_file_for_multi_field_unit_test.txt.REMOVED.git-id @@ -0,0 +1 @@ +ce87199d425b05f51c74cbf635870bfa5abbc7a1 \ No newline at end of file diff --git a/src/Persistent_cohomology/test/simplex_tree_file_for_unit_test.txt b/src/Persistent_cohomology/test/simplex_tree_file_for_unit_test.txt new file mode 100644 index 00000000..90756ce0 --- /dev/null +++ b/src/Persistent_cohomology/test/simplex_tree_file_for_unit_test.txt @@ -0,0 +1,98 @@ +0 0 0 +0 1 0.01 +0 2 0.02 +0 3 0.03 +0 4 0.04 +0 5 0.05 +0 6 0.06 +0 7 0.07 +0 8 0.08 +0 9 0.09 +1 2 1 1.12 +1 3 1 1.13 +1 4 1 1.14 +1 5 1 1.15 +1 6 1 1.16 +1 7 1 1.17 +1 8 1 1.18 +1 9 1 1.19 +1 3 2 1.23 +2 3 2 1 1.23 +1 6 2 1.26 +2 6 2 1 1.26 +1 8 2 1.28 +2 8 2 1 1.28 +1 9 2 1.29 +2 9 2 1 1.29 +1 6 3 1.36 +2 6 3 1 1.36 +2 6 3 2 1.36 +3 6 3 2 1 1.36 +1 7 3 1.37 +2 7 3 1 1.37 +1 8 3 1.38 +2 8 3 1 1.38 +2 8 3 2 1.38 +3 8 3 2 1 1.38 +1 5 4 1.45 +2 5 4 1 1.45 +1 6 4 1.46 +2 6 4 1 1.46 +1 8 4 1.48 +2 8 4 1 1.48 +1 6 5 1.56 +2 6 5 1 1.56 +2 6 5 4 1.56 +3 6 5 4 1 1.56 +1 7 5 1.57 +2 7 5 1 1.57 +1 8 5 1.58 +2 8 5 1 1.58 +2 8 5 4 1.58 +3 8 5 4 1 1.58 +1 9 5 1.59 +2 9 5 1 1.59 +1 7 6 1.67 +2 7 6 1 1.67 +2 7 6 3 1.67 +3 7 6 3 1 1.67 +2 7 6 5 1.67 +3 7 6 5 1 1.67 +1 8 6 1.68 +2 8 6 1 1.68 +2 8 6 2 1.68 +3 8 6 2 1 1.68 +2 8 6 3 1.68 +3 8 6 3 1 1.68 +3 8 6 3 2 1.68 +2 8 6 4 1.68 +3 8 6 4 1 1.68 +2 8 6 5 1.68 +3 8 6 5 1 1.68 +3 8 6 5 4 1.68 +1 9 6 1.69 +2 9 6 1 1.69 +2 9 6 2 1.69 +3 9 6 2 1 1.69 +2 9 6 5 1.69 +3 9 6 5 1 1.69 +1 8 7 1.78 +2 8 7 1 1.78 +2 8 7 3 1.78 +3 8 7 3 1 1.78 +2 8 7 5 1.78 +3 8 7 5 1 1.78 +2 8 7 6 1.78 +3 8 7 6 1 1.78 +3 8 7 6 3 1.78 +3 8 7 6 5 1.78 +1 9 8 1.89 +2 9 8 1 1.89 +2 9 8 2 1.89 +3 9 8 2 1 1.89 +2 9 8 5 1.89 +3 9 8 5 1 1.89 +2 9 8 6 1.89 +3 9 8 6 1 1.89 +3 9 8 6 2 1.89 +3 9 8 6 5 1.89 diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 6430c300..9b3de20a 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -20,20 +20,22 @@ * along with this program. If not, see . */ -#ifndef GUDHI_SIMPLEX_TREE_H -#define GUDHI_SIMPLEX_TREE_H +#ifndef SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_H_ +#define SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_H_ + +#include +#include +#include +#include -#include -#include -#include -#include "gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h" -#include "gudhi/Simplex_tree/Simplex_tree_siblings.h" -#include "gudhi/Simplex_tree/Simplex_tree_iterators.h" -#include "gudhi/Simplex_tree/indexing_tag.h" #include #include #include +#include +#include +#include + namespace Gudhi { /** \defgroup simplex_tree Filtered Complexes Package @@ -107,18 +109,12 @@ class Simplex_tree { /* Type of dictionary Vertex_handle -> Node for traversing the simplex tree. */ typedef typename boost::container::flat_map Dictionary; - friend class Simplex_tree_node_explicit_storage< - Simplex_tree > ; - friend class Simplex_tree_siblings< - Simplex_tree, Dictionary> ; - friend class Simplex_tree_simplex_vertex_iterator< - Simplex_tree > ; - friend class Simplex_tree_boundary_simplex_iterator< - Simplex_tree > ; - friend class Simplex_tree_complex_simplex_iterator< - Simplex_tree > ; - friend class Simplex_tree_skeleton_simplex_iterator< - Simplex_tree > ; + friend class Simplex_tree_node_explicit_storage< Simplex_tree >; + friend class Simplex_tree_siblings< Simplex_tree, Dictionary>; + friend class Simplex_tree_simplex_vertex_iterator< Simplex_tree >; + friend class Simplex_tree_boundary_simplex_iterator< Simplex_tree >; + friend class Simplex_tree_complex_simplex_iterator< Simplex_tree >; + friend class Simplex_tree_skeleton_simplex_iterator< Simplex_tree >; template friend class Persistent_cohomology; /* \brief Set of nodes sharing a same parent in the simplex tree. */ @@ -400,7 +396,7 @@ class Simplex_tree { * Vertex_handle. */ template - Simplex_handle find(RandomAccessVertexRange & s) { + Simplex_handle find(const RandomAccessVertexRange & s) { if (s.begin() == s.end()) std::cerr << "Empty simplex \n"; @@ -597,7 +593,7 @@ class Simplex_tree { * Reverse lexicographic order has the property to always consider the subsimplex of a simplex * to be smaller. The filtration function must be monotonic. */ struct is_before_in_filtration { - is_before_in_filtration(Simplex_tree * st) + explicit is_before_in_filtration(Simplex_tree * st) : st_(st) { } @@ -632,7 +628,7 @@ class Simplex_tree { * boost::graph_traits::directed_category * must be undirected_tag. */ template - void insert_graph(OneSkeletonGraph & skel_graph) { + void insert_graph(const OneSkeletonGraph& skel_graph) { assert(num_simplices() == 0); // the simplex tree must be empty if (boost::num_vertices(skel_graph) == 0) { @@ -738,25 +734,22 @@ class Simplex_tree { } /** \brief Intersects Dictionary 1 [begin1;end1) with Dictionary 2 [begin2,end2) * and assigns the maximal possible Filtration_value to the Nodes. */ - void intersection(std::vector > & intersection, + void intersection(std::vector >& intersection, Dictionary_it begin1, Dictionary_it end1, Dictionary_it begin2, Dictionary_it end2, Filtration_value filtration) { if (begin1 == end1 || begin2 == end2) - return; // 0; + return; // ----->> while (true) { if (begin1->first == begin2->first) { intersection.push_back( std::pair( begin1->first, - Node( - NULL, - maximum(begin1->second.filtration(), - begin2->second.filtration(), filtration)))); + Node(NULL, maximum(begin1->second.filtration(), begin2->second.filtration(), filtration)))); ++begin1; ++begin2; if (begin1 == end1 || begin2 == end2) - return; + return; // ----->> } else { if (begin1->first < begin2->first) { ++begin1; @@ -765,7 +758,7 @@ class Simplex_tree { } else { ++begin2; if (begin2 == end2) - return; + return; // ----->> } } } @@ -784,7 +777,7 @@ class Simplex_tree { * dim idx_1 ... idx_k fil where dim is the dimension of the simplex, * idx_1 ... idx_k are the row index (starting from 0) of the simplices of the boundary * of the simplex, and fil is its filtration value. */ - void print_hasse(std::ostream & os) { + void print_hasse(std::ostream& os) { os << num_simplices() << " " << std::endl; for (auto sh : filtration_simplex_range(Indexing_tag())) { os << dimension(sh) << " "; @@ -832,7 +825,7 @@ std::istream& operator>>(std::istream & is, Simplex_tree & st) { size_t num_simplices = 0; while (read_simplex(is, simplex, fil)) { // read all simplices in the file as a list of vertices ++num_simplices; - int dim = (int) simplex.size() - 1; // Warning : simplex_size needs to be casted in int - Can be 0 + int dim = static_cast(simplex.size() - 1); // Warning : simplex_size needs to be casted in int - Can be 0 if (max_dim < dim) { max_dim = dim; } @@ -852,4 +845,4 @@ std::istream& operator>>(std::istream & is, Simplex_tree & st) { /** @} */ // end defgroup simplex_tree } // namespace Gudhi -#endif // GUDHI_SIMPLEX_TREE_H +#endif // SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_H_ diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h index 54d4d296..06462c88 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h @@ -20,12 +20,13 @@ * along with this program. If not, see . */ -#ifndef SIMPLEX_TREE_ITERATORS_H -#define SIMPLEX_TREE_ITERATORS_H +#ifndef SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_SIMPLEX_TREE_ITERATORS_H_ +#define SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_SIMPLEX_TREE_ITERATORS_H_ -#include #include +#include + namespace Gudhi { /* \addtogroup simplex_tree @@ -47,7 +48,7 @@ class Simplex_tree_simplex_vertex_iterator : public boost::iterator_facade< typedef typename SimplexTree::Siblings Siblings; typedef typename SimplexTree::Vertex_handle Vertex_handle; - Simplex_tree_simplex_vertex_iterator(SimplexTree * st) + explicit Simplex_tree_simplex_vertex_iterator(SimplexTree * st) : // any end() iterator sib_(NULL), v_(st->null_vertex()) { @@ -93,7 +94,7 @@ class Simplex_tree_boundary_simplex_iterator : public boost::iterator_facade< typedef typename SimplexTree::Siblings Siblings; // any end() iterator - Simplex_tree_boundary_simplex_iterator(SimplexTree * st) + explicit Simplex_tree_boundary_simplex_iterator(SimplexTree * st) : last_(st->null_vertex()), sib_(NULL) { } @@ -166,7 +167,7 @@ class Simplex_tree_complex_simplex_iterator : public boost::iterator_facade< : st_(NULL) { } - Simplex_tree_complex_simplex_iterator(SimplexTree * st) + explicit Simplex_tree_complex_simplex_iterator(SimplexTree * st) : st_(st) { if (st == NULL || st->root() == NULL || st->root()->members().empty()) { st_ = NULL; @@ -302,4 +303,4 @@ class Simplex_tree_skeleton_simplex_iterator : public boost::iterator_facade< /* @} */ // end addtogroup simplex_tree } // namespace Gudhi -#endif // SIMPLEX_TREE_ITERATORS_H +#endif // SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_SIMPLEX_TREE_ITERATORS_H_ diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h index 27120f00..1f1a34cc 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h @@ -20,11 +20,10 @@ * along with this program. If not, see . */ -#ifndef GUDHI_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H -#define GUDHI_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H +#ifndef SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H_ +#define SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H_ #include -#include namespace Gudhi { @@ -42,14 +41,10 @@ namespace Gudhi { template class Simplex_tree_node_explicit_storage { public: -// friend SimplexTree; - typedef typename SimplexTree::Siblings Siblings; typedef typename SimplexTree::Filtration_value Filtration_value; typedef typename SimplexTree::Simplex_key Simplex_key; - //private: - //friend class Simplex_tree; // Default constructor. Simplex_tree_node_explicit_storage() : children_(NULL), @@ -68,13 +63,6 @@ class Simplex_tree_node_explicit_storage { simplex_key_ = key; } - /* - * Return true if the node has children, - * false otherwise. - */ - //bool has_children(Vertex label) - //{ //if(children_ == NULL) return false; //for root simplices - // return (children_->parent() == label);} /* * Assign a children to the node */ @@ -92,7 +80,7 @@ class Simplex_tree_node_explicit_storage { return filtration_; } - /* Careful -> has_children() must be true*/ + /* Careful -> children_ can be NULL*/ Siblings * children() { return children_; } @@ -106,11 +94,10 @@ class Simplex_tree_node_explicit_storage { // Data attached to simplex, explicit storage Simplex_key simplex_key_; - Filtration_value filtration_; //value in the filtration - + Filtration_value filtration_; // value in the filtration }; /* @} */ // end addtogroup simplex_tree -}// namespace Gudhi +} // namespace Gudhi -#endif // GUDHI_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H +#endif // SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H_ diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/indexing_tag.h b/src/Simplex_tree/include/gudhi/Simplex_tree/indexing_tag.h index 680458a5..69ffa44b 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/indexing_tag.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/indexing_tag.h @@ -20,6 +20,9 @@ * along with this program. If not, see . */ +#ifndef SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_INDEXING_TAG_H_ +#define SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_INDEXING_TAG_H_ + namespace Gudhi { /** \brief Tag for a linear ordering of simplices. @@ -31,4 +34,6 @@ struct linear_indexing_tag { /* \brief Tag for a zigzag ordering of simplices. */ // struct zigzag_indexing_tag {}; -}// namespace Gudhi +} // namespace Gudhi + +#endif // SRC_SIMPLEX_TREE_INCLUDE_GUDHI_SIMPLEX_TREE_INDEXING_TAG_H_ diff --git a/src/Simplex_tree/test/CMakeLists.txt b/src/Simplex_tree/test/CMakeLists.txt index eeb066d4..d7f968af 100644 --- a/src/Simplex_tree/test/CMakeLists.txt +++ b/src/Simplex_tree/test/CMakeLists.txt @@ -12,22 +12,22 @@ if(NOT MSVC) endif() include_directories(${Boost_INCLUDE_DIRS}) -add_executable ( TestSimplexTree UnitTestSimplexTree.cpp ) -target_link_libraries(TestSimplexTree ${Boost_LIBRARIES}) +add_executable ( simplex_tree_unit_test simplex_tree_unit_test.cpp ) +target_link_libraries(simplex_tree_unit_test ${Boost_LIBRARIES}) # Unitary tests -add_test(TestSimplexTree ${CMAKE_CURRENT_BINARY_DIR}/TestSimplexTree) +add_test(simplex_tree_unit_test ${CMAKE_CURRENT_BINARY_DIR}/simplex_tree_unit_test) if (LCOV_PATH) # Lcov code coverage of unitary test - add_test(TestSimplexTreeCov ${CMAKE_SOURCE_DIR}/scripts/check_code_coverage.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree) + add_test(simplex_tree_unit_test_coverage.info ${CMAKE_SOURCE_DIR}/scripts/check_code_coverage.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree) endif() if (PYTHON_PATH) # Cpplint tests on coding style - add_test(CpplintSimplexTree ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree.h) - add_test(CpplintSimplexTreeIndexingTag ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/indexing_tag.h) - add_test(CpplintSimplexTreeIterator ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h) - add_test(CpplintSimplexTreeNode ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h) - add_test(CpplintSimplexTreeSiblings ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_siblings.h) + add_test(Simplex_tree.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree.h) + add_test(indexing_tag.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/indexing_tag.h) + add_test(Simplex_tree_iterators.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h) + add_test(Simplex_tree_node_explicit_storage.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h) + add_test(Simplex_tree_siblings.h.cpplint ${CMAKE_SOURCE_DIR}/scripts/check_google_style.sh ${CMAKE_SOURCE_DIR}/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_siblings.h) endif() \ No newline at end of file diff --git a/src/Simplex_tree/test/README b/src/Simplex_tree/test/README index 3d6981ff..620bcd5f 100644 --- a/src/Simplex_tree/test/README +++ b/src/Simplex_tree/test/README @@ -7,6 +7,6 @@ make To launch with details: *********************** -./TestSimplexTree --report_level=detailed --log_level=all +./simplex_tree_unit_test --report_level=detailed --log_level=all ==> echo $? returns 0 in case of success (non-zero otherwise) diff --git a/src/Simplex_tree/test/UnitTestSimplexTree.cpp b/src/Simplex_tree/test/UnitTestSimplexTree.cpp deleted file mode 100644 index a3671f56..00000000 --- a/src/Simplex_tree/test/UnitTestSimplexTree.cpp +++ /dev/null @@ -1,400 +0,0 @@ -#define BOOST_TEST_MODULE const_string test -#include -#include -#include -#include -#include - -#include // std::pair, std::make_pair - -#include // float comparison -#include - -#include "gudhi/graph_simplicial_complex.h" -#include "gudhi/reader_utils.h" -#include "gudhi/Simplex_tree.h" - - -using namespace Gudhi; - -typedef Simplex_tree<> typeST; -typedef std::pair< typeST::Simplex_handle, bool > typePairSimplexBool; -typedef std::vector< Vertex_handle > typeVectorVertex; -typedef std::pair typeSimplex; - -const Vertex_handle DEFAULT_VERTEX_HANDLE = (const Vertex_handle)-1; - -void test_empty_simplex_tree(typeST& tst) -{ - BOOST_CHECK( tst.null_vertex() == DEFAULT_VERTEX_HANDLE ); - BOOST_CHECK( tst.filtration() == Filtration_value(0) ); - BOOST_CHECK( tst.num_vertices() == (size_t)0 ); - BOOST_CHECK( tst.num_simplices() == (size_t)0 ); - typeST::Siblings* STRoot = tst.root(); - BOOST_CHECK( STRoot != NULL ); - BOOST_CHECK( STRoot->oncles() == NULL ); - BOOST_CHECK( STRoot->parent() == DEFAULT_VERTEX_HANDLE ); - BOOST_CHECK( tst.dimension() == -1 ); -} - -void test_iterators_on_empty_simplex_tree(typeST& tst) -{ - std::cout << "Iterator on vertices: " << std::endl; - for( auto vertex : tst.complex_vertex_range() ) - { - std::cout << "vertice:" << vertex << std::endl; - BOOST_CHECK( false); // shall be empty - } - std::cout << "Iterator on simplices: " << std::endl; - for( auto simplex : tst.complex_simplex_range() ) - { - BOOST_CHECK( false); // shall be empty - } - - std::cout << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl; - for( auto f_simplex : tst.filtration_simplex_range() ) - { - BOOST_CHECK( false); // shall be empty - std::cout << "test_simplex_tree_contains - filtration=" << tst.filtration(f_simplex) << std::endl; - } -} - -BOOST_AUTO_TEST_CASE( simplex_tree_when_empty ) -{ - const Filtration_value DEFAULT_FILTRATION_VALUE = 0; - - // TEST OF DEFAULT CONSTRUCTOR - std::cout << "********************************************************************" << std::endl; - std::cout << "TEST OF DEFAULT CONSTRUCTOR" << std::endl; - typeST st; - - test_empty_simplex_tree(st); - - test_iterators_on_empty_simplex_tree(st); - // TEST OF EMPTY INSERTION - std::cout << "TEST OF EMPTY INSERTION" << std::endl; - typeVectorVertex simplexVectorEmpty; - BOOST_CHECK(simplexVectorEmpty.empty() == true); - typePairSimplexBool returnEmptyValue = st.insert ( simplexVectorEmpty, DEFAULT_FILTRATION_VALUE ); - BOOST_CHECK(returnEmptyValue.first == typeST::Simplex_handle(NULL)); - BOOST_CHECK(returnEmptyValue.second == true); - - test_empty_simplex_tree(st); - - test_iterators_on_empty_simplex_tree(st); -} - -bool AreAlmostTheSame(float a, float b) -{ - return std::fabs(a - b) < std::numeric_limits::epsilon(); -} - -BOOST_AUTO_TEST_CASE( simplex_tree_from_file ) -{ - // TEST OF INSERTION - std::cout << "********************************************************************" << std::endl; - std::cout << "TEST OF SIMPLEX TREE FROM A FILE" << std::endl; - typeST st; - - std::string inputFile("simplex_tree_for_unit_test.txt"); - std::ifstream simplex_tree_stream(inputFile.c_str()); - simplex_tree_stream >> st; - - // Display the Simplex_tree - std::cout << "The complex contains " << st.num_simplices() << " simplices" << std::endl; - std::cout << " - dimension " << st.dimension() << " - filtration " << st.filtration() << std::endl; - - // Check - BOOST_CHECK(st.num_simplices() == 143353); - BOOST_CHECK(st.dimension() == 3); - BOOST_CHECK(st.filtration() == 0.4); - - int previous_size=0; - for( auto f_simplex : st.filtration_simplex_range() ) - { - // Size of simplex - int size=0; - for( auto vertex : st.simplex_vertex_range(f_simplex) ) - { - size++; - } - BOOST_CHECK(AreAlmostTheSame(st.filtration(f_simplex),(0.1* size))); // Specific test: filtration = 0.1 * simplex_size - BOOST_CHECK(previous_size <= size); // Check list is sorted (because of sorted filtrations in simplex_tree.txt) - previous_size = size; - } - simplex_tree_stream.close(); -} - -void test_simplex_tree_contains(typeST& simplexTree, typeSimplex simplex, int pos) -{ - auto f_simplex = simplexTree.filtration_simplex_range().begin(); - f_simplex += pos; - std::cout << "test_simplex_tree_contains - filtration=" << simplexTree.filtration(*f_simplex) << "||" << simplex.second << std::endl; - BOOST_CHECK( AreAlmostTheSame(simplexTree.filtration(*f_simplex),simplex.second) ); - - typeVectorVertex::iterator simplexIter = simplex.first.end()-1; - for( auto vertex : simplexTree.simplex_vertex_range(*f_simplex) ) - { - std::cout << "test_simplex_tree_contains - vertex=" << vertex << "||" << *simplexIter << std::endl; - BOOST_CHECK( vertex == *simplexIter); - simplexIter--; - } -} - -void test_simplex_tree_insert_returns_true(const typePairSimplexBool& returnValue) -{ - BOOST_CHECK(returnValue.second == true); - typeST::Simplex_handle shReturned = returnValue.first; // Simplex_handle = boost::container::flat_map< Vertex_handle, Node >::iterator - BOOST_CHECK(shReturned != typeST::Simplex_handle(NULL)); -} - -// Global variables -Filtration_value max_fil = 0.0; -int dim_max = -1; - -void set_and_test_simplex_tree_dim_fil(typeST& simplexTree, int vectorSize, const Filtration_value& fil) -{ - if (vectorSize > dim_max+1) - { - dim_max = vectorSize-1; - simplexTree.set_dimension(dim_max); - std::cout << " set_and_test_simplex_tree_dim_fil - dim_max=" << dim_max << std::endl; - } - if (fil > max_fil) - { - max_fil = fil; - simplexTree.set_filtration(max_fil); - std::cout << " set_and_test_simplex_tree_dim_fil - max_fil=" << max_fil << std::endl; - } - int nb_simplices = simplexTree.num_simplices() + 1; - simplexTree.set_num_simplices(nb_simplices); - - BOOST_CHECK( simplexTree.dimension() == dim_max ); - BOOST_CHECK( AreAlmostTheSame(simplexTree.filtration(), max_fil) ); - BOOST_CHECK( simplexTree.num_simplices() == nb_simplices ); -} - -BOOST_AUTO_TEST_CASE( simplex_tree_insertion ) -{ - const Filtration_value FIRST_FILTRATION_VALUE = 0.1; - const Filtration_value SECOND_FILTRATION_VALUE = 0.2; - const Filtration_value THIRD_FILTRATION_VALUE = 0.3; - const Filtration_value FOURTH_FILTRATION_VALUE = 0.4; - Vertex_handle FIRST_VERTEX_HANDLE = (Vertex_handle)0; - Vertex_handle SECOND_VERTEX_HANDLE = (Vertex_handle)1; - Vertex_handle THIRD_VERTEX_HANDLE = (Vertex_handle)2; - Vertex_handle FOURTH_VERTEX_HANDLE = (Vertex_handle)3; - - // TEST OF INSERTION - std::cout << "********************************************************************" << std::endl; - std::cout << "TEST OF INSERTION" << std::endl; - typeST st; - - // ++ FIRST - std::cout << " - INSERT 0" << std::endl; - typeVectorVertex firstSimplexVector; - firstSimplexVector.push_back(FIRST_VERTEX_HANDLE); - BOOST_CHECK( firstSimplexVector.size() == 1 ); - typeSimplex firstSimplex = std::make_pair(firstSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); - typePairSimplexBool returnValue = - st.insert ( firstSimplex.first, firstSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, firstSimplexVector.size(), firstSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)1 ); - - // ++ SECOND - std::cout << " - INSERT 1" << std::endl; - typeVectorVertex secondSimplexVector; - secondSimplexVector.push_back(SECOND_VERTEX_HANDLE); - BOOST_CHECK( secondSimplexVector.size() == 1 ); - typeSimplex secondSimplex = std::make_pair(secondSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); - returnValue = - st.insert ( secondSimplex.first, secondSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, secondSimplexVector.size(), secondSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)2 ); - - // ++ THIRD - std::cout << " - INSERT (0,1)" << std::endl; - typeVectorVertex thirdSimplexVector; - thirdSimplexVector.push_back(FIRST_VERTEX_HANDLE); - thirdSimplexVector.push_back(SECOND_VERTEX_HANDLE); - BOOST_CHECK( thirdSimplexVector.size() == 2 ); - typeSimplex thirdSimplex = std::make_pair(thirdSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); - returnValue = - st.insert ( thirdSimplex.first, thirdSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, thirdSimplexVector.size(), thirdSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)2 ); // Not incremented !! - - // ++ FOURTH - std::cout << " - INSERT 2" << std::endl; - typeVectorVertex fourthSimplexVector; - fourthSimplexVector.push_back(THIRD_VERTEX_HANDLE); - BOOST_CHECK( fourthSimplexVector.size() == 1 ); - typeSimplex fourthSimplex = std::make_pair(fourthSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); - returnValue = - st.insert ( fourthSimplex.first, fourthSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, fourthSimplexVector.size(), fourthSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)3 ); - - // ++ FIFTH - std::cout << " - INSERT (2,0)" << std::endl; - typeVectorVertex fifthSimplexVector; - fifthSimplexVector.push_back(THIRD_VERTEX_HANDLE); - fifthSimplexVector.push_back(FIRST_VERTEX_HANDLE); - BOOST_CHECK( fifthSimplexVector.size() == 2 ); - typeSimplex fifthSimplex = std::make_pair(fifthSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); - returnValue = - st.insert ( fifthSimplex.first, fifthSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, fifthSimplexVector.size(), fifthSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)3 ); // Not incremented !! - - // ++ SIXTH - std::cout << " - INSERT (2,1)" << std::endl; - typeVectorVertex sixthSimplexVector; - sixthSimplexVector.push_back(THIRD_VERTEX_HANDLE); - sixthSimplexVector.push_back(SECOND_VERTEX_HANDLE); - BOOST_CHECK( sixthSimplexVector.size() == 2 ); - typeSimplex sixthSimplex = std::make_pair(sixthSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); - returnValue = - st.insert ( sixthSimplex.first, sixthSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, sixthSimplexVector.size(), sixthSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)3 ); // Not incremented !! - - // ++ SEVENTH - std::cout << " - INSERT (2,1,0)" << std::endl; - typeVectorVertex seventhSimplexVector; - seventhSimplexVector.push_back(THIRD_VERTEX_HANDLE); - seventhSimplexVector.push_back(SECOND_VERTEX_HANDLE); - seventhSimplexVector.push_back(FIRST_VERTEX_HANDLE); - BOOST_CHECK( seventhSimplexVector.size() == 3 ); - typeSimplex seventhSimplex = std::make_pair(seventhSimplexVector, Filtration_value(THIRD_FILTRATION_VALUE)); - returnValue = - st.insert ( seventhSimplex.first, seventhSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, seventhSimplexVector.size(), seventhSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)3 ); // Not incremented !! - - // ++ EIGHTH - std::cout << " - INSERT 3" << std::endl; - typeVectorVertex eighthSimplexVector; - eighthSimplexVector.push_back(FOURTH_VERTEX_HANDLE); - BOOST_CHECK( eighthSimplexVector.size() == 1 ); - typeSimplex eighthSimplex = std::make_pair(eighthSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); - returnValue = - st.insert ( eighthSimplex.first, eighthSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, eighthSimplexVector.size(), eighthSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)4 ); - - // ++ NINETH - std::cout << " - INSERT (3,0)" << std::endl; - typeVectorVertex ninethSimplexVector; - ninethSimplexVector.push_back(FOURTH_VERTEX_HANDLE); - ninethSimplexVector.push_back(FIRST_VERTEX_HANDLE); - BOOST_CHECK( ninethSimplexVector.size() == 2 ); - typeSimplex ninethSimplex = std::make_pair(ninethSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); - returnValue = - st.insert ( ninethSimplex.first, ninethSimplex.second ); - - test_simplex_tree_insert_returns_true(returnValue); - set_and_test_simplex_tree_dim_fil(st, ninethSimplexVector.size(), ninethSimplex.second); - BOOST_CHECK( st.num_vertices() == (size_t)4 ); // Not incremented !! - - // ++ TENTH - std::cout << " - INSERT 0 (already inserted)" << std::endl; - typeVectorVertex tenthSimplexVector; - tenthSimplexVector.push_back(FIRST_VERTEX_HANDLE); - BOOST_CHECK( tenthSimplexVector.size() == 1 ); - typeSimplex tenthSimplex = std::make_pair(tenthSimplexVector, Filtration_value(FOURTH_FILTRATION_VALUE)); // With a different filtration value - returnValue = - st.insert ( tenthSimplex.first, tenthSimplex.second ); - - BOOST_CHECK(returnValue.second == false); - typeST::Simplex_handle shReturned = returnValue.first; // Simplex_handle = boost::container::flat_map< Vertex_handle, Node >::iterator - BOOST_CHECK(shReturned == typeST::Simplex_handle(NULL)); - BOOST_CHECK( st.num_vertices() == (size_t)4 ); // Not incremented !! - BOOST_CHECK( st.dimension() == dim_max ); - BOOST_CHECK( AreAlmostTheSame(st.filtration(), max_fil) ); - - // ++ ELEVENTH - std::cout << " - INSERT (2,1,0) (already inserted)" << std::endl; - typeVectorVertex eleventhSimplexVector; - eleventhSimplexVector.push_back(THIRD_VERTEX_HANDLE); - eleventhSimplexVector.push_back(SECOND_VERTEX_HANDLE); - eleventhSimplexVector.push_back(FIRST_VERTEX_HANDLE); - BOOST_CHECK( eleventhSimplexVector.size() == 3 ); - typeSimplex eleventhSimplex = std::make_pair(eleventhSimplexVector, Filtration_value(FOURTH_FILTRATION_VALUE)); - returnValue = - st.insert ( eleventhSimplex.first, eleventhSimplex.second ); - - BOOST_CHECK(returnValue.second == false); - shReturned = returnValue.first; // Simplex_handle = boost::container::flat_map< Vertex_handle, Node >::iterator - BOOST_CHECK(shReturned == typeST::Simplex_handle(NULL)); - BOOST_CHECK( st.num_vertices() == (size_t)4 ); // Not incremented !! - BOOST_CHECK( st.dimension() == dim_max ); - BOOST_CHECK( AreAlmostTheSame(st.filtration(), max_fil) ); - - // 1 - // o - // /X\ - // o---o---o - // 2 0 3 - - // [0.1] 0 - // [0.1] 1 - // [0.1] 2 - // [0.1] 3 - // [0.2] 1 0 - // [0.2] 2 0 - // [0.2] 2 1 - // [0.2] 3 0 - // [0.3] 2 1 0 - - // !! Be careful, simplex are sorted by filtration value on insertion !! - std::cout << "simplex_tree_insertion - first - 0" << std::endl; - test_simplex_tree_contains(st, firstSimplex, 0); // (0) -> 0 - std::cout << "simplex_tree_insertion - second - 1" << std::endl; - test_simplex_tree_contains(st, secondSimplex, 1); // (1) -> 1 - std::cout << "simplex_tree_insertion - third - 4" << std::endl; - test_simplex_tree_contains(st, thirdSimplex, 4); // (0,1) -> 4 - std::cout << "simplex_tree_insertion - fourth - 2" << std::endl; - test_simplex_tree_contains(st, fourthSimplex, 2); // (2) -> 2 - std::cout << "simplex_tree_insertion - fifth - 5" << std::endl; - test_simplex_tree_contains(st, fifthSimplex, 5); // (2,0) -> 5 - std::cout << "simplex_tree_insertion - sixth - 6" << std::endl; - test_simplex_tree_contains(st, sixthSimplex, 6); //(2,1) -> 6 - std::cout << "simplex_tree_insertion - seventh - 8" << std::endl; - test_simplex_tree_contains(st, seventhSimplex, 8); // (2,1,0) -> 8 - std::cout << "simplex_tree_insertion - eighth - 3" << std::endl; - test_simplex_tree_contains(st, eighthSimplex, 3); // (3) -> 3 - std::cout << "simplex_tree_insertion - nineth - 7" << std::endl; - test_simplex_tree_contains(st, ninethSimplex, 7); // (3,0) -> 7 - - // Display the Simplex_tree - Can not be done in the middle of 2 inserts - std::cout << "The complex contains " << st.num_simplices() << " simplices" << std::endl; - std::cout << " - dimension " << st.dimension() << " - filtration " << st.filtration() << std::endl; - std::cout << std::endl << std::endl << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl; - for( auto f_simplex : st.filtration_simplex_range() ) - { - std::cout << " " << "[" << st.filtration(f_simplex) << "] "; - for( auto vertex : st.simplex_vertex_range(f_simplex) ) - { - std::cout << (int)vertex << " "; - } - std::cout << std::endl; - } - -} diff --git a/src/Simplex_tree/test/simplex_tree_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_unit_test.cpp new file mode 100644 index 00000000..a3671f56 --- /dev/null +++ b/src/Simplex_tree/test/simplex_tree_unit_test.cpp @@ -0,0 +1,400 @@ +#define BOOST_TEST_MODULE const_string test +#include +#include +#include +#include +#include + +#include // std::pair, std::make_pair + +#include // float comparison +#include + +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/reader_utils.h" +#include "gudhi/Simplex_tree.h" + + +using namespace Gudhi; + +typedef Simplex_tree<> typeST; +typedef std::pair< typeST::Simplex_handle, bool > typePairSimplexBool; +typedef std::vector< Vertex_handle > typeVectorVertex; +typedef std::pair typeSimplex; + +const Vertex_handle DEFAULT_VERTEX_HANDLE = (const Vertex_handle)-1; + +void test_empty_simplex_tree(typeST& tst) +{ + BOOST_CHECK( tst.null_vertex() == DEFAULT_VERTEX_HANDLE ); + BOOST_CHECK( tst.filtration() == Filtration_value(0) ); + BOOST_CHECK( tst.num_vertices() == (size_t)0 ); + BOOST_CHECK( tst.num_simplices() == (size_t)0 ); + typeST::Siblings* STRoot = tst.root(); + BOOST_CHECK( STRoot != NULL ); + BOOST_CHECK( STRoot->oncles() == NULL ); + BOOST_CHECK( STRoot->parent() == DEFAULT_VERTEX_HANDLE ); + BOOST_CHECK( tst.dimension() == -1 ); +} + +void test_iterators_on_empty_simplex_tree(typeST& tst) +{ + std::cout << "Iterator on vertices: " << std::endl; + for( auto vertex : tst.complex_vertex_range() ) + { + std::cout << "vertice:" << vertex << std::endl; + BOOST_CHECK( false); // shall be empty + } + std::cout << "Iterator on simplices: " << std::endl; + for( auto simplex : tst.complex_simplex_range() ) + { + BOOST_CHECK( false); // shall be empty + } + + std::cout << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl; + for( auto f_simplex : tst.filtration_simplex_range() ) + { + BOOST_CHECK( false); // shall be empty + std::cout << "test_simplex_tree_contains - filtration=" << tst.filtration(f_simplex) << std::endl; + } +} + +BOOST_AUTO_TEST_CASE( simplex_tree_when_empty ) +{ + const Filtration_value DEFAULT_FILTRATION_VALUE = 0; + + // TEST OF DEFAULT CONSTRUCTOR + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF DEFAULT CONSTRUCTOR" << std::endl; + typeST st; + + test_empty_simplex_tree(st); + + test_iterators_on_empty_simplex_tree(st); + // TEST OF EMPTY INSERTION + std::cout << "TEST OF EMPTY INSERTION" << std::endl; + typeVectorVertex simplexVectorEmpty; + BOOST_CHECK(simplexVectorEmpty.empty() == true); + typePairSimplexBool returnEmptyValue = st.insert ( simplexVectorEmpty, DEFAULT_FILTRATION_VALUE ); + BOOST_CHECK(returnEmptyValue.first == typeST::Simplex_handle(NULL)); + BOOST_CHECK(returnEmptyValue.second == true); + + test_empty_simplex_tree(st); + + test_iterators_on_empty_simplex_tree(st); +} + +bool AreAlmostTheSame(float a, float b) +{ + return std::fabs(a - b) < std::numeric_limits::epsilon(); +} + +BOOST_AUTO_TEST_CASE( simplex_tree_from_file ) +{ + // TEST OF INSERTION + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF SIMPLEX TREE FROM A FILE" << std::endl; + typeST st; + + std::string inputFile("simplex_tree_for_unit_test.txt"); + std::ifstream simplex_tree_stream(inputFile.c_str()); + simplex_tree_stream >> st; + + // Display the Simplex_tree + std::cout << "The complex contains " << st.num_simplices() << " simplices" << std::endl; + std::cout << " - dimension " << st.dimension() << " - filtration " << st.filtration() << std::endl; + + // Check + BOOST_CHECK(st.num_simplices() == 143353); + BOOST_CHECK(st.dimension() == 3); + BOOST_CHECK(st.filtration() == 0.4); + + int previous_size=0; + for( auto f_simplex : st.filtration_simplex_range() ) + { + // Size of simplex + int size=0; + for( auto vertex : st.simplex_vertex_range(f_simplex) ) + { + size++; + } + BOOST_CHECK(AreAlmostTheSame(st.filtration(f_simplex),(0.1* size))); // Specific test: filtration = 0.1 * simplex_size + BOOST_CHECK(previous_size <= size); // Check list is sorted (because of sorted filtrations in simplex_tree.txt) + previous_size = size; + } + simplex_tree_stream.close(); +} + +void test_simplex_tree_contains(typeST& simplexTree, typeSimplex simplex, int pos) +{ + auto f_simplex = simplexTree.filtration_simplex_range().begin(); + f_simplex += pos; + std::cout << "test_simplex_tree_contains - filtration=" << simplexTree.filtration(*f_simplex) << "||" << simplex.second << std::endl; + BOOST_CHECK( AreAlmostTheSame(simplexTree.filtration(*f_simplex),simplex.second) ); + + typeVectorVertex::iterator simplexIter = simplex.first.end()-1; + for( auto vertex : simplexTree.simplex_vertex_range(*f_simplex) ) + { + std::cout << "test_simplex_tree_contains - vertex=" << vertex << "||" << *simplexIter << std::endl; + BOOST_CHECK( vertex == *simplexIter); + simplexIter--; + } +} + +void test_simplex_tree_insert_returns_true(const typePairSimplexBool& returnValue) +{ + BOOST_CHECK(returnValue.second == true); + typeST::Simplex_handle shReturned = returnValue.first; // Simplex_handle = boost::container::flat_map< Vertex_handle, Node >::iterator + BOOST_CHECK(shReturned != typeST::Simplex_handle(NULL)); +} + +// Global variables +Filtration_value max_fil = 0.0; +int dim_max = -1; + +void set_and_test_simplex_tree_dim_fil(typeST& simplexTree, int vectorSize, const Filtration_value& fil) +{ + if (vectorSize > dim_max+1) + { + dim_max = vectorSize-1; + simplexTree.set_dimension(dim_max); + std::cout << " set_and_test_simplex_tree_dim_fil - dim_max=" << dim_max << std::endl; + } + if (fil > max_fil) + { + max_fil = fil; + simplexTree.set_filtration(max_fil); + std::cout << " set_and_test_simplex_tree_dim_fil - max_fil=" << max_fil << std::endl; + } + int nb_simplices = simplexTree.num_simplices() + 1; + simplexTree.set_num_simplices(nb_simplices); + + BOOST_CHECK( simplexTree.dimension() == dim_max ); + BOOST_CHECK( AreAlmostTheSame(simplexTree.filtration(), max_fil) ); + BOOST_CHECK( simplexTree.num_simplices() == nb_simplices ); +} + +BOOST_AUTO_TEST_CASE( simplex_tree_insertion ) +{ + const Filtration_value FIRST_FILTRATION_VALUE = 0.1; + const Filtration_value SECOND_FILTRATION_VALUE = 0.2; + const Filtration_value THIRD_FILTRATION_VALUE = 0.3; + const Filtration_value FOURTH_FILTRATION_VALUE = 0.4; + Vertex_handle FIRST_VERTEX_HANDLE = (Vertex_handle)0; + Vertex_handle SECOND_VERTEX_HANDLE = (Vertex_handle)1; + Vertex_handle THIRD_VERTEX_HANDLE = (Vertex_handle)2; + Vertex_handle FOURTH_VERTEX_HANDLE = (Vertex_handle)3; + + // TEST OF INSERTION + std::cout << "********************************************************************" << std::endl; + std::cout << "TEST OF INSERTION" << std::endl; + typeST st; + + // ++ FIRST + std::cout << " - INSERT 0" << std::endl; + typeVectorVertex firstSimplexVector; + firstSimplexVector.push_back(FIRST_VERTEX_HANDLE); + BOOST_CHECK( firstSimplexVector.size() == 1 ); + typeSimplex firstSimplex = std::make_pair(firstSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); + typePairSimplexBool returnValue = + st.insert ( firstSimplex.first, firstSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, firstSimplexVector.size(), firstSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)1 ); + + // ++ SECOND + std::cout << " - INSERT 1" << std::endl; + typeVectorVertex secondSimplexVector; + secondSimplexVector.push_back(SECOND_VERTEX_HANDLE); + BOOST_CHECK( secondSimplexVector.size() == 1 ); + typeSimplex secondSimplex = std::make_pair(secondSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); + returnValue = + st.insert ( secondSimplex.first, secondSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, secondSimplexVector.size(), secondSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)2 ); + + // ++ THIRD + std::cout << " - INSERT (0,1)" << std::endl; + typeVectorVertex thirdSimplexVector; + thirdSimplexVector.push_back(FIRST_VERTEX_HANDLE); + thirdSimplexVector.push_back(SECOND_VERTEX_HANDLE); + BOOST_CHECK( thirdSimplexVector.size() == 2 ); + typeSimplex thirdSimplex = std::make_pair(thirdSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); + returnValue = + st.insert ( thirdSimplex.first, thirdSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, thirdSimplexVector.size(), thirdSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)2 ); // Not incremented !! + + // ++ FOURTH + std::cout << " - INSERT 2" << std::endl; + typeVectorVertex fourthSimplexVector; + fourthSimplexVector.push_back(THIRD_VERTEX_HANDLE); + BOOST_CHECK( fourthSimplexVector.size() == 1 ); + typeSimplex fourthSimplex = std::make_pair(fourthSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); + returnValue = + st.insert ( fourthSimplex.first, fourthSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, fourthSimplexVector.size(), fourthSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)3 ); + + // ++ FIFTH + std::cout << " - INSERT (2,0)" << std::endl; + typeVectorVertex fifthSimplexVector; + fifthSimplexVector.push_back(THIRD_VERTEX_HANDLE); + fifthSimplexVector.push_back(FIRST_VERTEX_HANDLE); + BOOST_CHECK( fifthSimplexVector.size() == 2 ); + typeSimplex fifthSimplex = std::make_pair(fifthSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); + returnValue = + st.insert ( fifthSimplex.first, fifthSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, fifthSimplexVector.size(), fifthSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)3 ); // Not incremented !! + + // ++ SIXTH + std::cout << " - INSERT (2,1)" << std::endl; + typeVectorVertex sixthSimplexVector; + sixthSimplexVector.push_back(THIRD_VERTEX_HANDLE); + sixthSimplexVector.push_back(SECOND_VERTEX_HANDLE); + BOOST_CHECK( sixthSimplexVector.size() == 2 ); + typeSimplex sixthSimplex = std::make_pair(sixthSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); + returnValue = + st.insert ( sixthSimplex.first, sixthSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, sixthSimplexVector.size(), sixthSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)3 ); // Not incremented !! + + // ++ SEVENTH + std::cout << " - INSERT (2,1,0)" << std::endl; + typeVectorVertex seventhSimplexVector; + seventhSimplexVector.push_back(THIRD_VERTEX_HANDLE); + seventhSimplexVector.push_back(SECOND_VERTEX_HANDLE); + seventhSimplexVector.push_back(FIRST_VERTEX_HANDLE); + BOOST_CHECK( seventhSimplexVector.size() == 3 ); + typeSimplex seventhSimplex = std::make_pair(seventhSimplexVector, Filtration_value(THIRD_FILTRATION_VALUE)); + returnValue = + st.insert ( seventhSimplex.first, seventhSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, seventhSimplexVector.size(), seventhSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)3 ); // Not incremented !! + + // ++ EIGHTH + std::cout << " - INSERT 3" << std::endl; + typeVectorVertex eighthSimplexVector; + eighthSimplexVector.push_back(FOURTH_VERTEX_HANDLE); + BOOST_CHECK( eighthSimplexVector.size() == 1 ); + typeSimplex eighthSimplex = std::make_pair(eighthSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE)); + returnValue = + st.insert ( eighthSimplex.first, eighthSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, eighthSimplexVector.size(), eighthSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)4 ); + + // ++ NINETH + std::cout << " - INSERT (3,0)" << std::endl; + typeVectorVertex ninethSimplexVector; + ninethSimplexVector.push_back(FOURTH_VERTEX_HANDLE); + ninethSimplexVector.push_back(FIRST_VERTEX_HANDLE); + BOOST_CHECK( ninethSimplexVector.size() == 2 ); + typeSimplex ninethSimplex = std::make_pair(ninethSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE)); + returnValue = + st.insert ( ninethSimplex.first, ninethSimplex.second ); + + test_simplex_tree_insert_returns_true(returnValue); + set_and_test_simplex_tree_dim_fil(st, ninethSimplexVector.size(), ninethSimplex.second); + BOOST_CHECK( st.num_vertices() == (size_t)4 ); // Not incremented !! + + // ++ TENTH + std::cout << " - INSERT 0 (already inserted)" << std::endl; + typeVectorVertex tenthSimplexVector; + tenthSimplexVector.push_back(FIRST_VERTEX_HANDLE); + BOOST_CHECK( tenthSimplexVector.size() == 1 ); + typeSimplex tenthSimplex = std::make_pair(tenthSimplexVector, Filtration_value(FOURTH_FILTRATION_VALUE)); // With a different filtration value + returnValue = + st.insert ( tenthSimplex.first, tenthSimplex.second ); + + BOOST_CHECK(returnValue.second == false); + typeST::Simplex_handle shReturned = returnValue.first; // Simplex_handle = boost::container::flat_map< Vertex_handle, Node >::iterator + BOOST_CHECK(shReturned == typeST::Simplex_handle(NULL)); + BOOST_CHECK( st.num_vertices() == (size_t)4 ); // Not incremented !! + BOOST_CHECK( st.dimension() == dim_max ); + BOOST_CHECK( AreAlmostTheSame(st.filtration(), max_fil) ); + + // ++ ELEVENTH + std::cout << " - INSERT (2,1,0) (already inserted)" << std::endl; + typeVectorVertex eleventhSimplexVector; + eleventhSimplexVector.push_back(THIRD_VERTEX_HANDLE); + eleventhSimplexVector.push_back(SECOND_VERTEX_HANDLE); + eleventhSimplexVector.push_back(FIRST_VERTEX_HANDLE); + BOOST_CHECK( eleventhSimplexVector.size() == 3 ); + typeSimplex eleventhSimplex = std::make_pair(eleventhSimplexVector, Filtration_value(FOURTH_FILTRATION_VALUE)); + returnValue = + st.insert ( eleventhSimplex.first, eleventhSimplex.second ); + + BOOST_CHECK(returnValue.second == false); + shReturned = returnValue.first; // Simplex_handle = boost::container::flat_map< Vertex_handle, Node >::iterator + BOOST_CHECK(shReturned == typeST::Simplex_handle(NULL)); + BOOST_CHECK( st.num_vertices() == (size_t)4 ); // Not incremented !! + BOOST_CHECK( st.dimension() == dim_max ); + BOOST_CHECK( AreAlmostTheSame(st.filtration(), max_fil) ); + + // 1 + // o + // /X\ + // o---o---o + // 2 0 3 + + // [0.1] 0 + // [0.1] 1 + // [0.1] 2 + // [0.1] 3 + // [0.2] 1 0 + // [0.2] 2 0 + // [0.2] 2 1 + // [0.2] 3 0 + // [0.3] 2 1 0 + + // !! Be careful, simplex are sorted by filtration value on insertion !! + std::cout << "simplex_tree_insertion - first - 0" << std::endl; + test_simplex_tree_contains(st, firstSimplex, 0); // (0) -> 0 + std::cout << "simplex_tree_insertion - second - 1" << std::endl; + test_simplex_tree_contains(st, secondSimplex, 1); // (1) -> 1 + std::cout << "simplex_tree_insertion - third - 4" << std::endl; + test_simplex_tree_contains(st, thirdSimplex, 4); // (0,1) -> 4 + std::cout << "simplex_tree_insertion - fourth - 2" << std::endl; + test_simplex_tree_contains(st, fourthSimplex, 2); // (2) -> 2 + std::cout << "simplex_tree_insertion - fifth - 5" << std::endl; + test_simplex_tree_contains(st, fifthSimplex, 5); // (2,0) -> 5 + std::cout << "simplex_tree_insertion - sixth - 6" << std::endl; + test_simplex_tree_contains(st, sixthSimplex, 6); //(2,1) -> 6 + std::cout << "simplex_tree_insertion - seventh - 8" << std::endl; + test_simplex_tree_contains(st, seventhSimplex, 8); // (2,1,0) -> 8 + std::cout << "simplex_tree_insertion - eighth - 3" << std::endl; + test_simplex_tree_contains(st, eighthSimplex, 3); // (3) -> 3 + std::cout << "simplex_tree_insertion - nineth - 7" << std::endl; + test_simplex_tree_contains(st, ninethSimplex, 7); // (3,0) -> 7 + + // Display the Simplex_tree - Can not be done in the middle of 2 inserts + std::cout << "The complex contains " << st.num_simplices() << " simplices" << std::endl; + std::cout << " - dimension " << st.dimension() << " - filtration " << st.filtration() << std::endl; + std::cout << std::endl << std::endl << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl; + for( auto f_simplex : st.filtration_simplex_range() ) + { + std::cout << " " << "[" << st.filtration(f_simplex) << "] "; + for( auto vertex : st.simplex_vertex_range(f_simplex) ) + { + std::cout << (int)vertex << " "; + } + std::cout << std::endl; + } + +} -- cgit v1.2.3