From 425b462d361286822ee0ed7b5fe00881ba312ea3 Mon Sep 17 00:00:00 2001 From: vrouvrea Date: Fri, 5 Dec 2014 13:32:54 +0000 Subject: Moved into trunk git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/trunk@341 636b058d-ea47-450e-bf9e-a15bfbe3eedb --- .../concept/CoefficientField.h | 52 ++ .../concept/FilteredComplex.h | 143 ++++ .../concept/PersistentHomology.h | 41 ++ src/Persistent_cohomology/example/CMakeLists.txt | 22 + src/Persistent_cohomology/example/README | 47 ++ .../example/performance_rips_persistence.cpp | 168 +++++ .../example/rips_multifield_persistence.cpp | 152 ++++ .../example/rips_persistence.cpp | 146 ++++ .../rips_persistence_from_alpha_shape_3.cpp | 140 ++++ .../include/gudhi/Persistent_cohomology.h | 772 +++++++++++++++++++++ .../include/gudhi/Persistent_cohomology/Field_Zp.h | 106 +++ .../gudhi/Persistent_cohomology/Multi_field.h | 167 +++++ .../Persistent_cohomology_column.h | 153 ++++ 13 files changed, 2109 insertions(+) create mode 100644 src/Persistent_cohomology/concept/CoefficientField.h create mode 100644 src/Persistent_cohomology/concept/FilteredComplex.h create mode 100644 src/Persistent_cohomology/concept/PersistentHomology.h create mode 100644 src/Persistent_cohomology/example/CMakeLists.txt create mode 100644 src/Persistent_cohomology/example/README create mode 100644 src/Persistent_cohomology/example/performance_rips_persistence.cpp create mode 100644 src/Persistent_cohomology/example/rips_multifield_persistence.cpp create mode 100644 src/Persistent_cohomology/example/rips_persistence.cpp create mode 100644 src/Persistent_cohomology/example/rips_persistence_from_alpha_shape_3.cpp create mode 100644 src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h create mode 100644 src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h create mode 100644 src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h create mode 100644 src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h (limited to 'src/Persistent_cohomology') diff --git a/src/Persistent_cohomology/concept/CoefficientField.h b/src/Persistent_cohomology/concept/CoefficientField.h new file mode 100644 index 00000000..953b06c2 --- /dev/null +++ b/src/Persistent_cohomology/concept/CoefficientField.h @@ -0,0 +1,52 @@ + /* 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 . + */ + +/** \brief Concept describing the requirements for a class to represent + * a field of coefficients to compute persistent homology. + */ +struct CoefficientField { + +/** \brief Type of element of the field. + * + * Must be Assignable. */ + typedef unspecified Element; + +/** Default constructible. */ + CoefficientField(); + + void init(Element charac); + void init(Element charac_min, Element charac_max); + +/** Return the characteristic of the field. */ + Element characteristic(); +/** Return the element 1 of the field. */ + Element multiplicative_identity(); +/** Return the element 0 of the field. */ + Element additive_identity(); + +/** Assign: x <- x + y */ + void plus_equal(Element x, Element y); + +/** */ +//... inverse() + + }; \ No newline at end of file diff --git a/src/Persistent_cohomology/concept/FilteredComplex.h b/src/Persistent_cohomology/concept/FilteredComplex.h new file mode 100644 index 00000000..1834903b --- /dev/null +++ b/src/Persistent_cohomology/concept/FilteredComplex.h @@ -0,0 +1,143 @@ + /* 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 . + */ + +/** \brief The concept FilteredComplex describes the requirements + * for a type to implement a filtered cell complex, from which + * one can compute persistent homology via a model of the concept + * PersistentHomology. + */ +struct FilteredComplex +{ +/** Handle to specify a simplex. */ + typedef unspecified Simplex_handle; +/** \brief Key associated to each simplex. + * + * Must be a signed integer type. */ + typedef unspecified Simplex_key; +/** \brief Type for the value of the filtration function. + * + * Must be comparable with <. */ + typedef unspecified Filtration_value; + +/** \brief Specifies the nature of the indexing scheme. + * + * is model of IndexingTag. */ + typedef unspecified Indexing_tag; + +/** Returns a Simplex_hanlde that is different from all simplex handles + * of the simplices. */ + Simplex_handle null_simplex(); +/** \brief Returns the number of simplices in the complex. + * + * Does not count the empty simplex. */ + size_t num_simplices(); +/** \brief Returns the dimension of a simplex. */ + int dimension(Simplex_handle sh); +/** \brief Returns the filtration value of a simplex. + * + * If sh is null_simplex(), returns the maximal value of the + * filtration function on the complex. */ + Filtration_value filtration(Simplex_handle sh); + +/** \brief Returns a key that is different from the keys associated + * to the simplices. */ + Simplex_key null_key (); +/** \brief Returns the key associated to a simplex. */ + Simplex_key key ( Simplex_handle sh ); +/** \brief Returns the simplex associated to a key. + * + * If key is different from null_key(), there must be a unique + * simplex having this key. */ + Simplex_handle simplex ( Simplex_key key ); +/** \brief Assign a key to a simplex. */ + void assign_key(Simplex_handle sh, Simplex_key key); + +/** \brief Iterator on the simplices belonging to the + * boundary of a simplex. + * + * value_type must be 'Simplex_handle'. + */ +typedef unspecified Boundary_simplex_iterator; +/** \brief Range giving access to the simplices in the boundary of + * a simplex. + * + * .begin() and .end() return type Boundary_simplex_iterator. + */ +typedef unspecified Boundary_simplex_range; + +/** \brief Returns a range giving access to all simplices of the + * boundary of a simplex, i.e. + * the set of codimension 1 subsimplices of the Simplex. + * + * If the simplex is \f$[v_0, \cdots ,v_d]\f$, with canonical orientation + * induced by \f$ v_0 < \cdots < v_d \f$, the iterator enumerates the + * simplices of the boundary in the order: + * \f$[v_0,\cdots,\widehat{v_i},\cdots,v_d]\f$ for \f$i\f$ from 0 to d + * + * We note that the alternate sum of the simplices given by the iterator + * gives the chains corresponding to the boundary of the simplex.*/ +Boundary_simplex_range boundary_simplex_range(Simplex_handle sh); + +/** \brief Iterator over all simplices of the complex + * in the order of the indexing scheme. + * + * 'value_type' must be 'Simplex_handle'. + */ +typedef unspecified Filtration_simplex_iterator; +/** \brief Range over the simplices of the complex + * in the order of the filtration. + * + * .begin() and .end() return type Filtration_simplex_iterator.*/ +typedef unspecified Filtration_simplex_range; +/** \brief Returns a range over the simplices of the complex + * in the order of the filtration. + * + * .begin() and .end() return type Filtration_simplex_iterator.*/ +Filtration_simplex_range filtration_simplex_range(); + + +/* \brief Iterator over the simplices of the complex, + * in an arbitrary order. + * + * 'value_type' must be 'Simplex_handle'.*/ +//typedef unspecified Complex_simplex_iterator; +//typedef unspecified Complex_simplex_range; + +/* +* Returns a range over all the simplices of a +* complex. +*/ +//Complex_simplex_range complex_simplex_range(); + +/*************************************************/ /** +* @details Compares the filtration values of simplices s and t +* +* @return -1 if s comes before t in the filtration, +1 +* if it comes later, and 0 if they come at the same time +* +* @note OPTIONAL +* @todo use an enum? Just a bool? +*/ +//int is_before_in_filtration(Simplex_handle s, Simplex_handle t); +/*************************************************/ + +}; diff --git a/src/Persistent_cohomology/concept/PersistentHomology.h b/src/Persistent_cohomology/concept/PersistentHomology.h new file mode 100644 index 00000000..111723a5 --- /dev/null +++ b/src/Persistent_cohomology/concept/PersistentHomology.h @@ -0,0 +1,41 @@ + /* 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 . + */ + +/** \brief Concept describing the requirements for a class to compute + * persistent homology. */ +struct PersistentHomology { + +/** \brief Type of filtered cell complex on which persistent homology + * is computed. + * + * Must be a model of concept FilteredComplex. + */ + typedef unspecified Filtered_complex; + +/** \brief Type of coefficients to be used for computing persistent + * homology. + * + * Must be a model of concept CoefficientField. + */ + typedef unspecified Coefficient_field; + + }; diff --git a/src/Persistent_cohomology/example/CMakeLists.txt b/src/Persistent_cohomology/example/CMakeLists.txt new file mode 100644 index 00000000..dca4a04d --- /dev/null +++ b/src/Persistent_cohomology/example/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 2.6) +project(GUDHIExPersCohom) + +# NEED TO FIND BOOST NEEDED COMPONENTS TO LINK WITH +find_package(Boost 1.45.0 COMPONENTS system program_options) +message("Boost_lib = ${Boost_LIBRARIES}") + +add_executable ( rips_persistence rips_persistence.cpp ) +target_link_libraries(rips_persistence ${Boost_LIBRARIES}) +add_executable ( rips_persistence_from_alpha_shape_3 rips_persistence_from_alpha_shape_3.cpp ) +target_link_libraries(rips_persistence_from_alpha_shape_3 ${Boost_LIBRARIES}) + +if(GMPXX_FOUND AND GMP_FOUND) + message("GMPXX_LIBRARIES = ${GMPXX_LIBRARIES}") + message("GMP_LIBRARIES = ${GMP_LIBRARIES}") + + add_executable ( rips_multifield_persistence rips_multifield_persistence.cpp ) + target_link_libraries(rips_multifield_persistence ${Boost_LIBRARIES} ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) + + add_executable ( performance_rips_persistence performance_rips_persistence.cpp ) + target_link_libraries(performance_rips_persistence ${Boost_LIBRARIES} ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) +endif() diff --git a/src/Persistent_cohomology/example/README b/src/Persistent_cohomology/example/README new file mode 100644 index 00000000..5dd34b85 --- /dev/null +++ b/src/Persistent_cohomology/example/README @@ -0,0 +1,47 @@ +To build the example, run in a Terminal: + +cd /path-to-example/ +cmake . +make + + +Example of use : + +Computation of the persistent homology with Z/2Z coefficients of the Rips complex on points +sampling a Klein bottle: + +./rips_persistence ../../../data/points/Kl.txt -r 0.25 -d 3 -p 2 -m 100 + +output: +210 0 0 inf +210 1 0.0702103 inf +2 1 0.0702103 inf +2 2 0.159992 inf + +with Z/3Z coefficients: + +./rips_persistence ../../../data/points/Kl.txt -r 0.25 -d 3 -p 3 -m 100 + +output: +3 0 0 inf +3 1 0.0702103 inf + +and the computation with Z/2Z and Z/3Z coefficients simultaneously: + +./rips_multifield_persistence ../../../data/points/Kl.txt -r 0.25 -d 3 -p 2 -q 3 -m 100 + +output: +6 0 0 inf +6 1 0.0702103 inf +2 1 0.0702103 inf +2 2 0.159992 inf + +and finally the computation with all Z/pZ for 2 <= p <= 71 (20 first prime numbers): + + ./rips_multifield_persistence ../../../data/points/Kl.txt -r 0.25 -d 3 -p 2 -q 71 -m 100 + +output: +557940830126698960967415390 0 0 inf +557940830126698960967415390 1 0.0702103 inf +2 1 0.0702103 inf +2 2 0.159992 inf diff --git a/src/Persistent_cohomology/example/performance_rips_persistence.cpp b/src/Persistent_cohomology/example/performance_rips_persistence.cpp new file mode 100644 index 00000000..37d41ee1 --- /dev/null +++ b/src/Persistent_cohomology/example/performance_rips_persistence.cpp @@ -0,0 +1,168 @@ + /* 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 . + */ + +#include "gudhi/reader_utils.h" +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/distance_functions.h" +#include "gudhi/Simplex_tree.h" +#include "gudhi/Persistent_cohomology.h" +#include "gudhi/Persistent_cohomology/Multi_field.h" +#include "gudhi/Hasse_complex.h" + +#include + +using namespace Gudhi; + +/* Compute the persistent homology of the complex cpx with coefficients in Z/pZ. */ +template< typename FilteredComplex> +void timing_persistence( FilteredComplex & cpx + , int p ); + +/* Compute multi-field persistent homology of the complex cpx with coefficients in + * Z/rZ for all prime number r in [p;q].*/ +template< typename FilteredComplex> +void timing_persistence( FilteredComplex & cpx + , int p + , int q ); + +/* Timings for the computation of persistent homology with different + * representations of a Rips complex and different coefficient fields. The + * Rips complex is built on a set of 10000 points sampling a Klein bottle embedded + * in dimension 5. + * We represent complexes with a simplex tree and + * with a Hasse diagram. The Hasse diagram represents explicitly all + * codimension 1 incidence relations in the complex, and hence leads to + * a faster computation of persistence because boundaries are precomputed. + * Hovewer, the simplex tree may be constructed directly from a point cloud and + * is more compact. + * We compute persistent homology with coefficient fields Z/2Z and Z/1223Z. + * We present also timings for the computation of multi-field persistent + * homology in all fields Z/rZ for r prime between 2 and 1223. + */ +int main (int argc, char * argv[]) +{ + std::chrono::time_point start, end; + int enlapsed_sec; + + std::string filepoints = "../examples/Kl.txt"; + Filtration_value threshold = 0.3; + int dim_max = 3; + int p = 2; + int q = 1223; + +// Extract the points from the file filepoints + typedef std::vector Point_t; + std::vector< Point_t > points; + read_points( filepoints, points ); + +// Compute the proximity graph of the points + start = std::chrono::system_clock::now(); + Graph_t prox_graph = compute_proximity_graph( points, threshold + , euclidean_distance ); + end = std::chrono::system_clock::now(); + enlapsed_sec = std::chrono::duration_cast(end-start).count(); + std::cout << "Compute Rips graph in " << enlapsed_sec << " sec.\n"; + +// Construct the Rips complex in a Simplex Tree + Simplex_tree<> st; + start = std::chrono::system_clock::now(); + + st.insert_graph(prox_graph); // insert the proximity graph in the simplex tree + st.expansion( dim_max ); // expand the graph until dimension dim_max + + end = std::chrono::system_clock::now(); + enlapsed_sec = std::chrono::duration_cast(end-start).count(); + std::cout << "Compute Rips complex in " << enlapsed_sec << " sec.\n"; + std::cout << " - dimension = " << st.dimension() << std::endl; + std::cout << " - number of simplices = " << st.num_simplices() << std::endl; + +// Sort the simplices in the order of the filtration + start = std::chrono::system_clock::now(); + st.initialize_filtration(); + end = std::chrono::system_clock::now(); + enlapsed_sec = std::chrono::duration_cast(end-start).count(); + std::cout << "Order the simplices of the filtration in " << enlapsed_sec << " sec.\n"; + +// Convert the simplex tree into a hasse diagram + start = std::chrono::system_clock::now(); + Hasse_complex<> hcpx(st); + end = std::chrono::system_clock::now(); + enlapsed_sec = std::chrono::duration_cast(end-start).count(); + std::cout << "Convert the simplex tree into a Hasse diagram in " << enlapsed_sec << " sec.\n"; + + + std::cout << "Timings when using a simplex tree: \n"; + timing_persistence(st,p); + timing_persistence(st,q); + timing_persistence(st,p,q); + + std::cout << "Timings when using a Hasse complex: \n"; + timing_persistence(hcpx,p); + timing_persistence(hcpx,q); + timing_persistence(hcpx,p,q); + + return 0; +} + + +template< typename FilteredComplex> +void +timing_persistence( FilteredComplex & cpx + , int p ) +{ + std::chrono::time_point start, end; + int enlapsed_sec; + + Persistent_cohomology< FilteredComplex, Field_Zp > pcoh (cpx); + pcoh.init_coefficients( p ); //initilizes the coefficient field for homology + + start = std::chrono::system_clock::now(); + + pcoh.compute_persistent_cohomology( INFINITY ); + + end = std::chrono::system_clock::now(); + enlapsed_sec = std::chrono::duration_cast(end-start).count(); + std::cout << " Compute persistent homology in Z/"< +void +timing_persistence( FilteredComplex & cpx + , int p + , int q ) +{ + std::chrono::time_point start, end; + int enlapsed_sec; + + Persistent_cohomology< FilteredComplex, Multi_field > pcoh (cpx); + pcoh.init_coefficients( p, q ); //initilizes the coefficient field for homology + // compute persistent homology, disgarding persistent features of life shorter than min_persistence + + start = std::chrono::system_clock::now(); + + pcoh.compute_persistent_cohomology( INFINITY ); + + end = std::chrono::system_clock::now(); + enlapsed_sec = std::chrono::duration_cast(end-start).count(); + std::cout << " Compute multi-field persistent homology in all coefficient fields Z/pZ " + << "with p in ["<. + */ + +#include "gudhi/reader_utils.h" +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/distance_functions.h" +#include "gudhi/Simplex_tree.h" +#include "gudhi/Persistent_cohomology.h" +#include "gudhi/Persistent_cohomology/Multi_field.h" + +#include + +using namespace Gudhi; + +typedef int Vertex_handle; +typedef double Filtration_value; + +void program_options( int argc, char * argv[] + , std::string & filepoints + , std::string & filediag + , Filtration_value & threshold + , int & dim_max + , int & min_p + , int & max_p + , Filtration_value & min_persistence ); + +int main (int argc, char * argv[]) +{ + std::string filepoints; + std::string filediag; + Filtration_value threshold; + int dim_max; + int min_p; + int max_p; + Filtration_value min_persistence; + + program_options(argc,argv,filepoints,filediag,threshold,dim_max,min_p,max_p,min_persistence); + +// Extract the points from the file filepoints + typedef std::vector Point_t; + std::vector< Point_t > points; + read_points( filepoints, points ); + +// Compute the proximity graph of the points + Graph_t prox_graph = compute_proximity_graph( points, threshold + , euclidean_distance ); + +// Construct the Rips complex in a Simplex Tree + Simplex_tree<> st; + st.insert_graph(prox_graph); // insert the proximity graph in the simplex tree + st.expansion( dim_max ); // expand the graph until dimension dim_max + +// Sort the simplices in the order of the filtration + st.initialize_filtration(); + +// Compute the persistence diagram of the complex + Persistent_cohomology< Simplex_tree<>, Multi_field > pcoh( st ); + pcoh.init_coefficients( min_p, max_p ); //initilizes the coefficient field for homology + // compute persistent homology, disgarding persistent features of life shorter than min_persistence + pcoh.compute_persistent_cohomology( min_persistence ); + +// Output the diagram in filediag + if(filediag.empty()) { pcoh.output_diagram(); } + else { + std::ofstream out(filediag); + pcoh.output_diagram(out); + out.close(); } + + return 0; +} + + + +void program_options( int argc, char * argv[] + , std::string & filepoints + , std::string & filediag + , Filtration_value & threshold + , int & dim_max + , int & min_p + , int & max_p + , Filtration_value & min_persistence ) +{ + namespace po = boost::program_options; + po::options_description hidden("Hidden options"); + hidden.add_options() + ("input-file", po::value(&filepoints), + "Name of file containing a point set. Format is one point per line: X1 ... Xd \n"); + + po::options_description visible("Allowed options"); + visible.add_options() + ("help,h", "produce help message") + ("output-file,o", po::value(&filediag)->default_value(std::string()), + "Name of file in which the persistence diagram is written. Default print in std::cout") + ("max-edge-length,r", po::value(&threshold)->default_value(0), + "Maximal length of an edge for the Rips complex construction.") + ("cpx-dimension,d", po::value(&dim_max)->default_value(1), + "Maximal dimension of the Rips complex we want to compute.") + ("min-field-charac,p", po::value(&min_p)->default_value(2), + "Minimal characteristic p of the coefficient field Z/pZ.") + ("max-field-charac,q", po::value(&max_p)->default_value(1223), + "Minimial characteristic q of the coefficient field Z/pZ.") + ("min-persistence,m", po::value(&min_persistence), + "Minimal lifetime of homology feature to be recorded. Default is 0"); + + po::positional_options_description pos; + pos.add("input-file", 1); + + po::options_description all; all.add(visible).add(hidden); + + po::variables_map vm; + po::store(po::command_line_parser(argc, argv). + options(all).positional(pos).run(), vm); + po::notify(vm); + + if (vm.count("help") || !vm.count("input-file")) + { + std::cout << std::endl; + std::cout << "Compute the persistent homology with various coefficient fields \n"; + std::cout << "of a Rips complex defined on a set of input points. The coefficient \n"; + std::cout << "fields are all the Z/rZ for a prime number r contained in the \n"; + std::cout << "specified range [p,q]\n \n"; + std::cout << "The output diagram contains one bar per line, written with the convention: \n"; + std::cout << " p1*...*pr dim b d \n"; + std::cout << "where dim is the dimension of the homological feature,\n"; + std::cout << "b and d are respectively the birth and death of the feature and \n"; + std::cout << "p1*...*pr is the product of prime numbers pi such that the homology \n"; + std::cout << "feature exists in homology with Z/piZ coefficients."<< std::endl << std::endl; + + std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl; + std::cout << visible << std::endl; + std::abort(); + } +} diff --git a/src/Persistent_cohomology/example/rips_persistence.cpp b/src/Persistent_cohomology/example/rips_persistence.cpp new file mode 100644 index 00000000..d7927223 --- /dev/null +++ b/src/Persistent_cohomology/example/rips_persistence.cpp @@ -0,0 +1,146 @@ + /* 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 . + */ + +#include "gudhi/reader_utils.h" +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/distance_functions.h" +#include "gudhi/Simplex_tree.h" +#include "gudhi/Persistent_cohomology.h" + +#include + +using namespace Gudhi; + +typedef int Vertex_handle; +typedef double Filtration_value; + +void program_options( int argc, char * argv[] + , std::string & filepoints + , std::string & filediag + , Filtration_value & threshold + , int & dim_max + , int & p + , Filtration_value & min_persistence ); + +int main (int argc, char * argv[]) +{ + std::string filepoints; + std::string filediag ; + Filtration_value threshold ; + int dim_max ; + int p ; + Filtration_value min_persistence; + + program_options(argc,argv,filepoints,filediag,threshold,dim_max,p,min_persistence); + +// Extract the points from the file filepoints + typedef std::vector Point_t; + std::vector< Point_t > points; + read_points( filepoints, points ); + +// Compute the proximity graph of the points + Graph_t prox_graph = compute_proximity_graph( points, threshold + , euclidean_distance ); + +// Construct the Rips complex in a Simplex Tree + Simplex_tree<> st; + 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 << "The complex contains " << st.num_simplices() << " simplices \n"; + std::cout << " and has dimension " << st.dimension() << " \n"; + +// Sort the simplices in the order of the filtration + st.initialize_filtration(); + +// Compute the persistence diagram of the complex + Persistent_cohomology< Simplex_tree<>, Field_Zp > pcoh( st ); + pcoh.init_coefficients( p ); //initilizes the coefficient field for homology + + pcoh.compute_persistent_cohomology( min_persistence ); + +// Output the diagram in filediag + if(filediag.empty()) { pcoh.output_diagram(); } + else { + std::ofstream out(filediag); + pcoh.output_diagram(out); + out.close(); } + + return 0; +} + + + +void program_options( int argc, char * argv[] + , std::string & filepoints + , std::string & filediag + , Filtration_value & threshold + , int & dim_max + , int & p + , Filtration_value & min_persistence ) +{ + namespace po = boost::program_options; + po::options_description hidden("Hidden options"); + hidden.add_options() + ("input-file", po::value(&filepoints), + "Name of file containing a point set. Format is one point per line: X1 ... Xd "); + + po::options_description visible("Allowed options", 100); + visible.add_options() + ("help,h", "produce help message") + ("output-file,o", po::value(&filediag)->default_value(std::string()), + "Name of file in which the persistence diagram is written. Default print in std::cout") + ("max-edge-length,r", po::value(&threshold)->default_value(0), + "Maximal length of an edge for the Rips complex construction.") + ("cpx-dimension,d", po::value(&dim_max)->default_value(1), + "Maximal dimension of the Rips complex we want to compute.") + ("field-charac,p", po::value(&p)->default_value(11), + "Characteristic p of the coefficient field Z/pZ for computing homology.") + ("min-persistence,m", po::value(&min_persistence), + "Minimal lifetime of homology feature to be recorded. Default is 0"); + + po::positional_options_description pos; + pos.add("input-file", 1); + + po::options_description all; all.add(visible).add(hidden); + + po::variables_map vm; + po::store(po::command_line_parser(argc, argv). + options(all).positional(pos).run(), vm); + po::notify(vm); + + if (vm.count("help") || !vm.count("input-file")) + { + std::cout << std::endl; + std::cout << "Compute the persistent homology with coefficient field Z/pZ \n"; + std::cout << "of a Rips complex defined on a set of input points.\n \n"; + std::cout << "The output diagram contains one bar per line, written with the convention: \n"; + std::cout << " p dim b d \n"; + std::cout << "where dim is the dimension of the homological feature,\n"; + std::cout << "b and d are respectively the birth and death of the feature and \n"; + std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl; + + std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl; + std::cout << visible << std::endl; + std::abort(); + } +} diff --git a/src/Persistent_cohomology/example/rips_persistence_from_alpha_shape_3.cpp b/src/Persistent_cohomology/example/rips_persistence_from_alpha_shape_3.cpp new file mode 100644 index 00000000..ddaafea2 --- /dev/null +++ b/src/Persistent_cohomology/example/rips_persistence_from_alpha_shape_3.cpp @@ -0,0 +1,140 @@ + /* 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): Vincent Rouvreau + * + * Copyright (C) 2014 INRIA Saclay (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 . + */ + +#include "gudhi/reader_utils.h" +#include "gudhi/graph_simplicial_complex.h" +#include "gudhi/distance_functions.h" +#include "gudhi/Simplex_tree.h" +#include "gudhi/Persistent_cohomology.h" + +#include + +using namespace Gudhi; + +typedef int Vertex_handle; +typedef double Filtration_value; + +void program_options( int argc, char * argv[] + , std::string & simplex_tree_file + , std::string & output_file + , int & p + , Filtration_value & min_persistence ); + +int main (int argc, char * argv[]) +{ + std::string simplex_tree_file; + std::string output_file ; + int p ; + Filtration_value min_persistence; + + program_options(argc,argv,simplex_tree_file,output_file,p,min_persistence); + + std::cout << "Simplex_tree from file=" << simplex_tree_file.c_str() << " - output_file=" << output_file.c_str() << std::endl; + std::cout << " - p=" << p << " - min_persistence=" << min_persistence << std::endl; + + // Construct the Rips complex in a Simplex Tree + Simplex_tree<> simplex_tree; + + std::ifstream simplex_tree_stream(simplex_tree_file); + simplex_tree_stream >> simplex_tree; + + std::cout << "The complex contains " << simplex_tree.num_simplices() << " simplices" << std::endl; + std::cout << " - dimension " << simplex_tree.dimension() << " - filtration " << simplex_tree.filtration() << std::endl; + + /* + std::cout << std::endl << std::endl << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl; + for( auto f_simplex : simplex_tree.filtration_simplex_range() ) + { std::cout << " " << "[" << simplex_tree.filtration(f_simplex) << "] "; + for( auto vertex : simplex_tree.simplex_vertex_range(f_simplex) ) + { std::cout << vertex << " "; } + std::cout << std::endl; + }*/ + + // Sort the simplices in the order of the filtration + simplex_tree.initialize_filtration(); + + // Compute the persistence diagram of the complex + Persistent_cohomology< Simplex_tree<>, Field_Zp > pcoh( simplex_tree ); + pcoh.init_coefficients( p ); //initilizes the coefficient field for homology + + pcoh.compute_persistent_cohomology( min_persistence ); + + // Output the diagram in output_file + if(output_file.empty()) { pcoh.output_diagram(); } + else { + std::ofstream out(output_file); + pcoh.output_diagram(out); + out.close(); } + + return 0; +} + + + +void program_options( int argc, char * argv[] + , std::string & simplex_tree_file + , std::string & output_file + , int & p + , Filtration_value & min_persistence ) +{ + namespace po = boost::program_options; + po::options_description hidden("Hidden options"); + hidden.add_options() + ("input-file", po::value(&simplex_tree_file), + "Name of file containing a simplex set. Format is one simplex per line (cf. reader_utils.h - read_simplex): Dim1 X11 X12 ... X1d Fil1 "); + + po::options_description visible("Allowed options", 100); + visible.add_options() + ("help,h", "produce help message") + ("output-file,o", po::value(&output_file)->default_value(std::string()), + "Name of file in which the persistence diagram is written. Default print in std::cout") + ("field-charac,p", po::value(&p)->default_value(11), + "Characteristic p of the coefficient field Z/pZ for computing homology.") + ("min-persistence,m", po::value(&min_persistence), + "Minimal lifetime of homology feature to be recorded. Default is 0"); + + po::positional_options_description pos; + pos.add("input-file", 1); + + po::options_description all; all.add(visible).add(hidden); + + po::variables_map vm; + po::store(po::command_line_parser(argc, argv). + options(all).positional(pos).run(), vm); + po::notify(vm); + + if (vm.count("help") || !vm.count("input-file")) + { + std::cout << std::endl; + std::cout << "Compute the persistent homology with coefficient field Z/pZ \n"; + std::cout << "of a Rips complex defined on a set of input points.\n \n"; + std::cout << "The output diagram contains one bar per line, written with the convention: \n"; + std::cout << " p dim b d \n"; + std::cout << "where dim is the dimension of the homological feature,\n"; + std::cout << "b and d are respectively the birth and death of the feature and \n"; + std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl; + + std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl; + std::cout << visible << std::endl; + std::abort(); + } +} diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h new file mode 100644 index 00000000..cd4a2bcc --- /dev/null +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h @@ -0,0 +1,772 @@ + /* 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_ + +#include +#include +#include +#include +#include +#include "gudhi/Persistent_cohomology/Persistent_cohomology_column.h" +#include "gudhi/Persistent_cohomology/Field_Zp.h" + +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. + + + \author Clément Maria + \version 1.0 + \date 2014 + \copyright GNU General Public License v3. + @{ + */ + +/** \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 { + public: + + 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; +// Compressed Annotation Matrix types: + // Column type + typedef Persistent_cohomology_column < Simplex_key + , Arith_element + > Column; // contains 1 set_hook + // Cell type + 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; +// Sparse column type for the annotation of the boundary of an element. + typedef std::vector< std::pair > 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)); + } +} + +~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_; +}; + + +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; + } + } + // 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() + ) + ); + } + // 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() + ) + ); + } + // 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 ); } + zero_cocycles_[ku] = idx_coc_v; + } + } + 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() ); + } +} + +/* + * 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. + // 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; } + } + } + 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); } + } + } + } + } + } +} + +/* + * 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: + 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 )); + } + + + 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( 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 + } + + 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); } + } + 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. + } + + // 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; } +} + +/* + * 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; } + } + } + } + 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 ); + + ++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_; +}; + +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 +}; + +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_; +}; + +/** @} */ //end defgroup persistent_cohomology + +} // namespace GUDHI + +#endif // _PERSISTENCECOMPUTATION_SIMPLEXTREE_ diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h new file mode 100644 index 00000000..af0d6605 --- /dev/null +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Field_Zp.h @@ -0,0 +1,106 @@ + /* 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{ + +/** \brief Structure representing the coefficient field \f$\mathbb{Z}/p\mathbb{Z}\f$ + * + * \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 + +/** Returns -x * y.*/ +Element times_minus ( Element x, Element y ) +{ + Element out = (-x * y) % Prime; + return (out < 0) ? out + Prime : out; +} + + +bool is_one ( Element x ) { return x == 1; } +bool is_zero ( Element x ) { return x == 0; } + +//bool is_null() + +/** \brief Returns the characteristic \f$p\f$ of the field.*/ +Element characteristic() { return Prime; } + +private: + Element Prime; +/** Property map Element -> Element, which associate to an element its inverse in the field.*/ + std::vector< Element > inverse_; +}; + +} // namespace GUDHI + +#endif // GUDHI_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 new file mode 100644 index 00000000..9dd0998c --- /dev/null +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Multi_field.h @@ -0,0 +1,167 @@ + /* 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 +#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 + */ +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 ["< 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 + 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 ); + } + /** Returns -x * y.*/ + Element times_minus ( Element x, Element y ) + { 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; + +}; + +} // namespace GUDHI + +#endif // GUDHI_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 new file mode 100644 index 00000000..0702c58e --- /dev/null +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h @@ -0,0 +1,153 @@ + /* 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 + +#include "boost/tuple/tuple.hpp" +#include "boost/intrusive/set.hpp" +#include "boost/intrusive/list.hpp" + +namespace Gudhi{ + +template < typename SimplexKey + , typename ArithmeticElement + > +class Persistent_cohomology_column; + +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::tag < cam_v_tag > + , boost::intrusive::link_mode < boost::intrusive::normal_link > //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 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) + { + 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()); + } + + // void display() + // { + // for(auto cell : col_) + // { std::cout << "(" << cell.key_ <<":"<