Loading...
Searching...
No Matches
Persistent_cohomology.h
1/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2 * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3 * Author(s): Clément Maria
4 *
5 * Copyright (C) 2014 Inria
6 *
7 * Modification(s):
8 * - YYYY/MM Author: Description of the modification
9 */
10
11#ifndef PERSISTENT_COHOMOLOGY_H_
12#define PERSISTENT_COHOMOLOGY_H_
13
14#include <gudhi/Persistent_cohomology/Persistent_cohomology_column.h>
15#include <gudhi/Persistent_cohomology/Field_Zp.h>
16#include <gudhi/Simple_object_pool.h>
17
18#include <boost/intrusive/set.hpp>
19#include <boost/pending/disjoint_sets.hpp>
20#include <boost/intrusive/list.hpp>
21#include <boost/version.hpp>
22#if BOOST_VERSION >= 108100
23#include <boost/unordered/unordered_flat_map.hpp>
24#else
25#include <boost/unordered_map.hpp>
26#endif
27
28#include <iostream>
29#include <map>
30#include <utility>
31#include <list>
32#include <vector>
33#include <set>
34#include <fstream> // std::ofstream
35#include <limits> // for numeric_limits<>
36#include <tuple>
37#include <algorithm>
38#include <string>
39#include <stdexcept> // for std::out_of_range
40
41namespace Gudhi {
42
43namespace persistent_cohomology {
44
57// TODO(CM): Memory allocation policy: classic, use a mempool, etc.
58template<class FilteredComplex, class CoefficientField>
60 public:
61 // Data attached to each simplex to interface with a Property Map.
62
73 typedef std::tuple<Simplex_handle, Simplex_handle, Arith_element> Persistent_interval;
74
75 private:
76 // Compressed Annotation Matrix types:
77 // Column type
78 typedef Persistent_cohomology_column<Simplex_key, Arith_element> Column; // contains 1 set_hook
79 // Cell type
80 typedef typename Column::Cell Cell; // contains 2 list_hooks
81 // Remark: constant_time_size must be false because base_hook_cam_h has auto_unlink link_mode
82 typedef boost::intrusive::list<Cell,
83 boost::intrusive::constant_time_size<false>,
84 boost::intrusive::base_hook<base_hook_cam_h> > Hcell;
85
86 typedef boost::intrusive::set<Column,
87 boost::intrusive::constant_time_size<false> > Cam;
88 // Sparse column type for the annotation of the boundary of an element.
89 typedef std::vector<std::pair<Simplex_key, Arith_element> > A_ds_type;
90
91 public:
102 explicit Persistent_cohomology(FilteredComplex& cpx, bool persistence_dim_max = false)
103 : cpx_(&cpx),
104 dim_max_(cpx.dimension()), // upper bound on the dimension of the simplices
105 coeff_field_(), // initialize the field coefficient structure.
106 num_simplices_(cpx_->num_simplices()), // num_simplices save to avoid to call thrice the function
107 ds_rank_(num_simplices_), // union-find
108 ds_parent_(num_simplices_), // union-find
109 ds_repr_(num_simplices_, NULL), // union-find -> annotation vectors
110 dsets_(ds_rank_.data(), ds_parent_.data()), // union-find
111 cam_(), // collection of annotation vectors
112 zero_cocycles_(), // union-find -> Simplex_key of creator for 0-homology
113 transverse_idx_(), // key -> row
114 persistent_pairs_(),
115 interval_length_policy(&cpx, 0),
116 column_pool_(), // memory pools for the CAM
117 cell_pool_() {
118 if (num_simplices_ > std::numeric_limits<Simplex_key>::max()) {
119 // num_simplices must be strictly lower than the limit, because a value is reserved for null_key.
120 throw std::out_of_range("The number of simplices is more than Simplex_key type numeric limit.");
121 }
122 if (persistence_dim_max) {
123 ++dim_max_;
124 }
125 }
126
128 // Clean the transversal lists
129 for (auto & transverse_ref : transverse_idx_) {
130 // Destruct all the cells
131 transverse_ref.second.row_->clear_and_dispose([&](Cell*p){p->~Cell();});
132 delete transverse_ref.second.row_;
133 }
134 }
135
136 private:
137 struct length_interval {
138 length_interval(FilteredComplex * cpx, Filtration_value min_length)
139 : cpx_(cpx),
140 min_length_(min_length) {
141 }
142
143 bool operator()(Simplex_handle sh1, Simplex_handle sh2) {
144 return cpx_->filtration(sh2) - cpx_->filtration(sh1) > min_length_;
145 }
146
147 void set_length(Filtration_value new_length) {
148 min_length_ = new_length;
149 }
150
151 FilteredComplex * cpx_;
152 Filtration_value min_length_;
153 };
154
155 public:
157 void init_coefficients(int charac) {
158 coeff_field_.init(charac);
159 }
160
161 void init_coefficients(int charac_min, int charac_max) {
162 coeff_field_.init(charac_min, charac_max);
163 }
164
173 void compute_persistent_cohomology(Filtration_value min_interval_length = 0) {
174 if (dim_max_ <= 0)
175 return; // --------->>
176
177 interval_length_policy.set_length(min_interval_length);
178 Simplex_key idx_fil = -1;
179 std::vector<Simplex_key> vertices; // so we can check the connected components at the end
180 // Compute all finite intervals
181 for (auto sh : cpx_->filtration_simplex_range()) {
182 cpx_->assign_key(sh, ++idx_fil);
183 dsets_.make_set(cpx_->key(sh));
184 int dim_simplex = cpx_->dimension(sh);
185 switch (dim_simplex) {
186 case 0:
187 vertices.push_back(idx_fil);
188 break;
189 case 1:
190 update_cohomology_groups_edge(sh);
191 break;
192 default:
193 update_cohomology_groups(sh, dim_simplex);
194 break;
195 }
196 }
197 // Compute infinite intervals of dimension 0
198 for (Simplex_key key : vertices) { // for all 0-dimensional simplices
199 if (ds_parent_[key] == key // root of its tree
200 && zero_cocycles_.find(key) == zero_cocycles_.end()) {
201 persistent_pairs_.emplace_back(
202 cpx_->simplex(key), cpx_->null_simplex(), coeff_field_.characteristic());
203 }
204 }
205 for (auto zero_idx : zero_cocycles_) {
206 persistent_pairs_.emplace_back(
207 cpx_->simplex(zero_idx.second), cpx_->null_simplex(), coeff_field_.characteristic());
208 }
209 // Compute infinite interval of dimension > 0
210 for (auto cocycle : transverse_idx_) {
211 persistent_pairs_.emplace_back(
212 cpx_->simplex(cocycle.first), cpx_->null_simplex(), cocycle.second.characteristics_);
213 }
214 }
215
221 void compute_persistent_cohomology_without_optimizations(Filtration_value min_interval_length = 0) {
222 if (dim_max_ <= 0)
223 return; // --------->>
224
225 interval_length_policy.set_length(min_interval_length);
226 Simplex_key idx_fil = -1;
227 // Compute all finite intervals
228 for (auto sh : cpx_->filtration_simplex_range()) {
229 cpx_->assign_key(sh, ++idx_fil);
230 dsets_.make_set(cpx_->key(sh));
231 int dim_simplex = cpx_->dimension(sh);
232 update_cohomology_groups(sh, dim_simplex);
233 }
234
235 for (auto cocycle : transverse_idx_) {
236 persistent_pairs_.emplace_back(
237 cpx_->simplex(cocycle.first), cpx_->null_simplex(), cocycle.second.characteristics_);
238 }
239 }
240
241 private:
246 void update_cohomology_groups_edge(Simplex_handle sigma) {
247 Simplex_handle u, v;
248 boost::tie(u, v) = cpx_->endpoints(sigma);
249
250 Simplex_key ku = dsets_.find_set(cpx_->key(u));
251 Simplex_key kv = dsets_.find_set(cpx_->key(v));
252
253 if (ku != kv) { // Destroy a connected component
254 dsets_.link(ku, kv);
255 // Keys of the simplices which created the connected components containing
256 // respectively u and v.
257 Simplex_key idx_coc_u, idx_coc_v;
258 auto map_it_u = zero_cocycles_.find(ku);
259 // If the index of the cocycle representing the class is already ku.
260 if (map_it_u == zero_cocycles_.end()) {
261 idx_coc_u = ku;
262 } else {
263 idx_coc_u = map_it_u->second;
264 }
265
266 auto map_it_v = zero_cocycles_.find(kv);
267 // If the index of the cocycle representing the class is already kv.
268 if (map_it_v == zero_cocycles_.end()) {
269 idx_coc_v = kv;
270 } else {
271 idx_coc_v = map_it_v->second;
272 }
273
274 if (cpx_->filtration(cpx_->simplex(idx_coc_u))
275 < cpx_->filtration(cpx_->simplex(idx_coc_v))) { // Kill cocycle [idx_coc_v], which is younger.
276 if (interval_length_policy(cpx_->simplex(idx_coc_v), sigma)) {
277 persistent_pairs_.emplace_back(
278 cpx_->simplex(idx_coc_v), sigma, coeff_field_.characteristic());
279 }
280 // Maintain the index of the 0-cocycle alive.
281 if (kv != idx_coc_v) {
282 zero_cocycles_.erase(map_it_v);
283 }
284 if (kv == dsets_.find_set(kv)) {
285 if (ku != idx_coc_u) {
286 zero_cocycles_.erase(map_it_u);
287 }
288 zero_cocycles_[kv] = idx_coc_u;
289 }
290 } else { // Kill cocycle [idx_coc_u], which is younger.
291 if (interval_length_policy(cpx_->simplex(idx_coc_u), sigma)) {
292 persistent_pairs_.emplace_back(
293 cpx_->simplex(idx_coc_u), sigma, coeff_field_.characteristic());
294 }
295 // Maintain the index of the 0-cocycle alive.
296 if (ku != idx_coc_u) {
297 zero_cocycles_.erase(map_it_u);
298 }
299 if (ku == dsets_.find_set(ku)) {
300 if (kv != idx_coc_v) {
301 zero_cocycles_.erase(map_it_v);
302 }
303 zero_cocycles_[ku] = idx_coc_v;
304 }
305 }
306 cpx_->assign_key(sigma, cpx_->null_key());
307 } else if (dim_max_ > 1) { // If ku == kv, same connected component: create a 1-cocycle class.
308 create_cocycle(sigma, coeff_field_.multiplicative_identity(), coeff_field_.characteristic());
309 }
310 }
311
312 /*
313 * Compute the annotation of the boundary of a simplex.
314 */
315 void annotation_of_the_boundary(
316 std::map<Simplex_key, Arith_element> & map_a_ds, Simplex_handle sigma,
317 int dim_sigma) {
318 // traverses the boundary of sigma, keeps track of the annotation vectors,
319 // with multiplicity. We used to sum the coefficients directly in
320 // annotations_in_boundary by using a map, we now do it later.
321 typedef std::pair<Column *, int> annotation_t;
322 thread_local std::vector<annotation_t> annotations_in_boundary;
323 annotations_in_boundary.clear();
324 int sign = 1 - 2 * (dim_sigma % 2); // \in {-1,1} provides the sign in the
325 // alternate sum in the boundary.
326 Simplex_key key;
327 Column * curr_col;
328
329 for (auto sh : cpx_->boundary_simplex_range(sigma)) {
330 key = cpx_->key(sh);
331 if (key != cpx_->null_key()) { // A simplex with null_key is a killer, and have null annotation
332 // Find its annotation vector
333 curr_col = ds_repr_[dsets_.find_set(key)];
334 if (curr_col != NULL) { // and insert it in annotations_in_boundary with multiplicative factor "sign".
335 annotations_in_boundary.emplace_back(curr_col, sign);
336 }
337 }
338 sign = -sign;
339 }
340 // Place identical annotations consecutively so we can easily sum their multiplicities.
341 std::sort(annotations_in_boundary.begin(), annotations_in_boundary.end(),
342 [](annotation_t const& a, annotation_t const& b) { return a.first < b.first; });
343
344 // Sum the annotations with multiplicity, using a map<key,coeff>
345 // to represent a sparse vector.
346 std::pair<typename std::map<Simplex_key, Arith_element>::iterator, bool> result_insert_a_ds;
347
348 for (auto ann_it = annotations_in_boundary.begin(); ann_it != annotations_in_boundary.end(); /**/) {
349 Column* col = ann_it->first;
350 int mult = ann_it->second;
351 while (++ann_it != annotations_in_boundary.end() && ann_it->first == col) {
352 mult += ann_it->second;
353 }
354 // The following test is just a heuristic, it is not required, and it is fine that is misses p == 0.
355 if (mult != coeff_field_.additive_identity()) { // For all columns in the boundary,
356 for (auto&& cell_ref : col->col_) { // insert every cell in map_a_ds with multiplicity
357 Arith_element w_y = coeff_field_.times(cell_ref.coefficient_, mult); // coefficient * multiplicity
358
359 if (w_y != coeff_field_.additive_identity()) { // if != 0
360 result_insert_a_ds = map_a_ds.emplace(cell_ref.key_, w_y);
361 if (!(result_insert_a_ds.second)) { // if cell_ref.key_ already a Key in map_a_ds
362 result_insert_a_ds.first->second = coeff_field_.plus_equal(result_insert_a_ds.first->second, w_y);
363 if (result_insert_a_ds.first->second == coeff_field_.additive_identity()) {
364 map_a_ds.erase(result_insert_a_ds.first);
365 }
366 }
367 }
368 }
369 }
370 }
371 }
372
373 /*
374 * Update the cohomology groups under the insertion of a simplex.
375 */
376 void update_cohomology_groups(Simplex_handle sigma, int dim_sigma) {
377// Compute the annotation of the boundary of sigma:
378 std::map<Simplex_key, Arith_element> map_a_ds;
379 annotation_of_the_boundary(map_a_ds, sigma, dim_sigma);
380// Update the cohomology groups:
381 if (map_a_ds.empty()) { // sigma is a creator in all fields represented in coeff_field_
382 if (dim_sigma < dim_max_) {
383 create_cocycle(sigma, coeff_field_.multiplicative_identity(),
384 coeff_field_.characteristic());
385 }
386 } else { // sigma is a destructor in at least a field in coeff_field_
387 // Convert map_a_ds to a sorted vector
388 A_ds_type a_ds; // admits reverse iterators
389 for (auto map_a_ds_ref : map_a_ds) {
390 a_ds.emplace_back(map_a_ds_ref.first, map_a_ds_ref.second);
391 }
392
393 Arith_element inv_x, charac;
394 Arith_element prod = coeff_field_.characteristic(); // Product of characteristic of the fields
395 for (auto a_ds_rit = a_ds.rbegin();
396 (a_ds_rit != a_ds.rend())
397 && (prod != coeff_field_.multiplicative_identity()); ++a_ds_rit) {
398 std::tie(inv_x, charac) = coeff_field_.inverse(a_ds_rit->second, prod);
399
400 if (inv_x != coeff_field_.additive_identity()) {
401 destroy_cocycle(sigma, a_ds, a_ds_rit->first, inv_x, charac);
402 prod /= charac;
403 }
404 }
405 if (prod != coeff_field_.multiplicative_identity()
406 && dim_sigma < dim_max_) {
407 create_cocycle(sigma, coeff_field_.multiplicative_identity(prod), prod);
408 }
409 }
410 }
411
412 /* \brief Create a new cocycle class.
413 *
414 * The class is created by the insertion of the simplex sigma.
415 * The methods adds a cocycle, representing the new cocycle class,
416 * to the matrix representing the cohomology groups.
417 * The new cocycle has value 0 on every simplex except on sigma
418 * where it worths 1.*/
419 void create_cocycle(Simplex_handle sigma, Arith_element x,
420 Arith_element charac) {
421 Simplex_key key = cpx_->key(sigma);
422 // Create a column containing only one cell,
423 Column * new_col = column_pool_.construct(key);
424 Cell * new_cell = cell_pool_.construct(key, x, new_col);
425 new_col->col_.push_back(*new_cell);
426 // and insert it in the matrix, in constant time thanks to the hint cam_.end().
427 // Indeed *new_col has the biggest lexicographic value because key is the
428 // biggest key used so far.
429 cam_.insert(cam_.end(), *new_col);
430 // Update the disjoint sets data structure.
431 Hcell * new_hcell = new Hcell;
432 new_hcell->push_back(*new_cell);
433 transverse_idx_[key] = cocycle(charac, new_hcell); // insert the new row
434 ds_repr_[key] = new_col;
435 }
436
437 /* \brief Destroy a cocycle class.
438 *
439 * The cocycle class is destroyed by the insertion of sigma.
440 * The methods proceeds to a reduction of the matrix representing
441 * the cohomology groups using Gauss pivoting. The reduction zeros-out
442 * the row containing the cell with highest key in
443 * a_ds, the annotation of the boundary of simplex sigma. This key
444 * is "death_key".*/
445 void destroy_cocycle(Simplex_handle sigma, A_ds_type const& a_ds,
446 Simplex_key death_key, Arith_element inv_x,
447 Arith_element charac) {
448 // Create a finite persistent interval for which the interval exists
449 if (interval_length_policy(cpx_->simplex(death_key), sigma)) {
450 persistent_pairs_.emplace_back(cpx_->simplex(death_key) // creator
451 , sigma // destructor
452 , charac); // fields
453 }
454
455 auto death_key_row = transverse_idx_.find(death_key); // Find the beginning of the row.
456 std::pair<typename Cam::iterator, bool> result_insert_cam;
457
458 auto row_cell_it = death_key_row->second.row_->begin();
459
460 while (row_cell_it != death_key_row->second.row_->end()) { // Traverse all cells in
461 // the row at index death_key.
462 Arith_element w = coeff_field_.times_minus(inv_x, row_cell_it->coefficient_);
463
464 if (w != coeff_field_.additive_identity()) {
465 Column * curr_col = row_cell_it->self_col_;
466 ++row_cell_it;
467 // Disconnect the column from the rows in the CAM.
468 for (auto& col_cell : curr_col->col_) {
469 col_cell.base_hook_cam_h::unlink();
470 }
471
472 // Remove the column from the CAM before modifying its value
473 cam_.erase(cam_.iterator_to(*curr_col));
474 // Proceed to the reduction of the column
475 plus_equal_column(*curr_col, a_ds, w);
476
477 if (curr_col->col_.empty()) { // If the column is null
478 ds_repr_[curr_col->class_key_] = NULL;
479 column_pool_.destroy(curr_col); // delete curr_col;
480 } else {
481 // Find whether the column obtained is already in the CAM
482 result_insert_cam = cam_.insert(*curr_col);
483 if (result_insert_cam.second) { // If it was not in the CAM before: insertion has succeeded
484 for (auto& col_cell : curr_col->col_) {
485 // re-establish the row links
486 transverse_idx_[col_cell.key_].row_->push_front(col_cell);
487 }
488 } else { // There is already an identical column in the CAM:
489 // merge two disjoint sets.
490 dsets_.link(curr_col->class_key_,
491 result_insert_cam.first->class_key_);
492
493 Simplex_key key_tmp = dsets_.find_set(curr_col->class_key_);
494 ds_repr_[key_tmp] = &(*(result_insert_cam.first));
495 result_insert_cam.first->class_key_ = key_tmp;
496 // intrusive containers don't own their elements, we have to release them manually
497 curr_col->col_.clear_and_dispose([&](Cell*p){cell_pool_.destroy(p);});
498 column_pool_.destroy(curr_col); // delete curr_col;
499 }
500 }
501 } else {
502 ++row_cell_it;
503 } // If w == 0, pass.
504 }
505
506 // Because it is a killer simplex, set the data of sigma to null_key().
507 if (charac == coeff_field_.characteristic()) {
508 cpx_->assign_key(sigma, cpx_->null_key());
509 }
510 if (death_key_row->second.characteristics_ == charac) {
511 delete death_key_row->second.row_;
512 transverse_idx_.erase(death_key_row);
513 } else {
514 death_key_row->second.characteristics_ /= charac;
515 }
516 }
517
518 /*
519 * Assign: target <- target + w * other.
520 */
521 void plus_equal_column(Column & target, A_ds_type const& other // value_type is pair<Simplex_key,Arith_element>
522 , Arith_element w) {
523 auto target_it = target.col_.begin();
524 auto other_it = other.begin();
525 while (target_it != target.col_.end() && other_it != other.end()) {
526 if (target_it->key_ < other_it->first) {
527 ++target_it;
528 } else {
529 if (target_it->key_ > other_it->first) {
530 Cell * cell_tmp = cell_pool_.construct(Cell(other_it->first // key
531 , coeff_field_.additive_identity(), &target));
532
533 cell_tmp->coefficient_ = coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w);
534
535 target.col_.insert(target_it, *cell_tmp);
536
537 ++other_it;
538 } else { // it1->key == it2->key
539 // target_it->coefficient_ <- target_it->coefficient_ + other_it->second * w
540 target_it->coefficient_ = coeff_field_.plus_times_equal(target_it->coefficient_, other_it->second, w);
541 if (target_it->coefficient_ == coeff_field_.additive_identity()) {
542 auto tmp_it = target_it;
543 ++target_it;
544 ++other_it; // iterators remain valid
545 Cell * tmp_cell_ptr = &(*tmp_it);
546 target.col_.erase(tmp_it); // removed from column
547
548 cell_pool_.destroy(tmp_cell_ptr); // delete from memory
549 } else {
550 ++target_it;
551 ++other_it;
552 }
553 }
554 }
555 }
556 while (other_it != other.end()) {
557 Cell * cell_tmp = cell_pool_.construct(Cell(other_it->first, coeff_field_.additive_identity(), &target));
558 cell_tmp->coefficient_ = coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w);
559 target.col_.insert(target.col_.end(), *cell_tmp);
560
561 ++other_it;
562 }
563 }
564
565 /*
566 * Compare two intervals by length.
567 */
568 struct cmp_intervals_by_length {
569 explicit cmp_intervals_by_length(FilteredComplex * sc)
570 : sc_(sc) {
571 }
572 bool operator()(const Persistent_interval & p1, const Persistent_interval & p2) {
573 return (sc_->filtration(get < 1 > (p1)) - sc_->filtration(get < 0 > (p1))
574 > sc_->filtration(get < 1 > (p2)) - sc_->filtration(get < 0 > (p2)));
575 }
576 FilteredComplex * sc_;
577 };
578
579 public:
590 void output_diagram(std::ostream& ostream = std::cout) {
591 cmp_intervals_by_length cmp(cpx_);
592 std::sort(std::begin(persistent_pairs_), std::end(persistent_pairs_), cmp);
593 for (auto pair : persistent_pairs_) {
594 ostream << get<2>(pair) << " " << cpx_->dimension(get<0>(pair)) << " "
595 << cpx_->filtration(get<0>(pair)) << " "
596 << cpx_->filtration(get<1>(pair)) << " " << std::endl;
597 }
598 }
599
600 void write_output_diagram(std::string diagram_name) {
601 std::ofstream diagram_out(diagram_name.c_str());
602 diagram_out.exceptions(diagram_out.failbit);
603 cmp_intervals_by_length cmp(cpx_);
604 std::sort(std::begin(persistent_pairs_), std::end(persistent_pairs_), cmp);
605 for (auto pair : persistent_pairs_) {
606 diagram_out << cpx_->dimension(get<0>(pair)) << " "
607 << cpx_->filtration(get<0>(pair)) << " "
608 << cpx_->filtration(get<1>(pair)) << std::endl;
609 }
610 }
611
615 std::vector<int> betti_numbers() const {
616 // Init Betti numbers vector with zeros until Simplicial complex dimension and don't allocate a vector of negative
617 // size for an empty complex
618 std::vector<int> betti_numbers(std::max(dim_max_, 0));
619
620 for (auto pair : persistent_pairs_) {
621 // Count never ended persistence intervals
622 if (cpx_->null_simplex() == get<1>(pair)) {
623 // Increment corresponding betti number
624 betti_numbers[cpx_->dimension(get<0>(pair))] += 1;
625 }
626 }
627 return betti_numbers;
628 }
629
635 int betti_number(int dimension) const {
636 int betti_number = 0;
637
638 for (auto pair : persistent_pairs_) {
639 // Count never ended persistence intervals
640 if (cpx_->null_simplex() == get<1>(pair)) {
641 if (cpx_->dimension(get<0>(pair)) == dimension) {
642 // Increment betti number found
643 ++betti_number;
644 }
645 }
646 }
647 return betti_number;
648 }
649
656 // Init Betti numbers vector with zeros until Simplicial complex dimension and don't allocate a vector of negative
657 // size for an empty complex
658 std::vector<int> betti_numbers(std::max(dim_max_, 0));
659 for (auto pair : persistent_pairs_) {
660 // Count persistence intervals that covers the given interval
661 // null_simplex test : if the function is called with to=+infinity, we still get something useful. And it will
662 // still work if we change the complex filtration function to reject null simplices.
663 if (cpx_->filtration(get<0>(pair)) <= from &&
664 (get<1>(pair) == cpx_->null_simplex() || cpx_->filtration(get<1>(pair)) > to)) {
665 // Increment corresponding betti number
666 betti_numbers[cpx_->dimension(get<0>(pair))] += 1;
667 }
668 }
669 return betti_numbers;
670 }
671
678 int persistent_betti_number(int dimension, Filtration_value from, Filtration_value to) const {
679 int betti_number = 0;
680
681 for (auto pair : persistent_pairs_) {
682 // Count persistence intervals that covers the given interval
683 // null_simplex test : if the function is called with to=+infinity, we still get something useful. And it will
684 // still work if we change the complex filtration function to reject null simplices.
685 if (cpx_->filtration(get<0>(pair)) <= from &&
686 (get<1>(pair) == cpx_->null_simplex() || cpx_->filtration(get<1>(pair)) > to)) {
687 if (cpx_->dimension(get<0>(pair)) == dimension) {
688 // Increment betti number found
689 ++betti_number;
690 }
691 }
692 }
693 return betti_number;
694 }
695
699 const std::vector<Persistent_interval>& get_persistent_pairs() const {
700 return persistent_pairs_;
701 }
702
707 std::vector< std::pair< Filtration_value , Filtration_value > >
708 intervals_in_dimension(int dimension) {
709 std::vector< std::pair< Filtration_value , Filtration_value > > result;
710 // auto && pair, to avoid unnecessary copying
711 for (auto && pair : persistent_pairs_) {
712 if (cpx_->dimension(get<0>(pair)) == dimension) {
713 result.emplace_back(cpx_->filtration(get<0>(pair)), cpx_->filtration(get<1>(pair)));
714 }
715 }
716 return result;
717 }
718
719 private:
720 /*
721 * Structure representing a cocycle.
722 */
723 struct cocycle {
724 cocycle()
725 : row_(nullptr),
726 characteristics_() {
727 }
728 cocycle(Arith_element characteristics, Hcell * row)
729 : row_(row),
730 characteristics_(characteristics) {
731 }
732
733 Hcell * row_; // points to the corresponding row in the CAM
734 Arith_element characteristics_; // product of field characteristics for which the cocycle exist
735 };
736
737 public:
738 FilteredComplex * cpx_;
739 int dim_max_;
740 CoefficientField coeff_field_;
741 size_t num_simplices_;
742
743 /* Disjoint sets data structure to link the model of FilteredComplex
744 * with the compressed annotation matrix.
745 * ds_rank_ is a property map Simplex_key -> int, ds_parent_ is a property map
746 * Simplex_key -> simplex_key_t */
747 std::vector<int> ds_rank_;
748 std::vector<Simplex_key> ds_parent_;
749 std::vector<Column *> ds_repr_;
750 boost::disjoint_sets<int *, Simplex_key *> dsets_;
751 /* The compressed annotation matrix fields.*/
752 Cam cam_;
753#if BOOST_VERSION >= 108100
754 /* Dictionary establishing the correspondence between the Simplex_key of
755 * the root vertex in the union-find ds and the Simplex_key of the vertex which
756 * created the connected component as a 0-dimension homology feature.*/
757 boost::unordered_flat_map<Simplex_key, Simplex_key> zero_cocycles_;
758 /* Key -> row. */
759 boost::unordered_flat_map<Simplex_key, cocycle> transverse_idx_;
760#else
761 boost::unordered_map<Simplex_key, Simplex_key> zero_cocycles_;
762 boost::unordered_map<Simplex_key, cocycle> transverse_idx_;
763#endif
764 /* Persistent intervals. */
765 std::vector<Persistent_interval> persistent_pairs_;
766 length_interval interval_length_policy;
767
768 Simple_object_pool<Column> column_pool_;
769 Simple_object_pool<Cell> cell_pool_;
770};
771
772} // namespace persistent_cohomology
773
774} // namespace Gudhi
775
776#endif // PERSISTENT_COHOMOLOGY_H_
Computes the persistent cohomology of a filtered complex.
Definition Persistent_cohomology.h:59
std::vector< int > persistent_betti_numbers(Filtration_value from, Filtration_value to) const
Returns the persistent Betti numbers.
Definition Persistent_cohomology.h:655
std::vector< std::pair< Filtration_value, Filtration_value > > intervals_in_dimension(int dimension)
Returns persistence intervals for a given dimension.
Definition Persistent_cohomology.h:708
std::vector< int > betti_numbers() const
Returns Betti numbers.
Definition Persistent_cohomology.h:615
void output_diagram(std::ostream &ostream=std::cout)
Output the persistence diagram in ostream.
Definition Persistent_cohomology.h:590
std::tuple< Simplex_handle, Simplex_handle, Arith_element > Persistent_interval
Definition Persistent_cohomology.h:73
Simplex_tree::Simplex_handle Simplex_handle
Definition Persistent_cohomology.h:66
Persistent_cohomology(FilteredComplex &cpx, bool persistence_dim_max=false)
Initializes the Persistent_cohomology class.
Definition Persistent_cohomology.h:102
int persistent_betti_number(int dimension, Filtration_value from, Filtration_value to) const
Returns the persistent Betti number of the dimension passed by parameter.
Definition Persistent_cohomology.h:678
int betti_number(int dimension) const
Returns the Betti number of the dimension passed by parameter.
Definition Persistent_cohomology.h:635
Simplex_tree::Simplex_key Simplex_key
Definition Persistent_cohomology.h:64
void compute_persistent_cohomology(Filtration_value min_interval_length=0)
Compute the persistent homology of the filtered simplicial complex.
Definition Persistent_cohomology.h:173
void init_coefficients(int charac)
Initializes the coefficient field.
Definition Persistent_cohomology.h:157
void init_coefficients(int charac_min, int charac_max)
Initializes the coefficient field for multi-field persistent homology.
Definition Persistent_cohomology.h:161
Field_Zp::Element Arith_element
Definition Persistent_cohomology.h:70
Simplex_tree::Filtration_value Filtration_value
Definition Persistent_cohomology.h:68
const std::vector< Persistent_interval > & get_persistent_pairs() const
Returns a list of persistence birth and death FilteredComplex::Simplex_handle pairs.
Definition Persistent_cohomology.h:699
Gudhi namespace.
Definition SimplicialComplexForAlpha.h:14
unspecified Element
Type of element of the field.
Definition CoefficientField.h:19
The concept FilteredComplex describes the requirements for a type to implement a filtered cell comple...
Definition FilteredComplex.h:17
unspecified Simplex_key
Data stored for each simplex.
Definition FilteredComplex.h:91
Filtration_value filtration(Simplex_handle sh)
Returns the filtration value of a simplex.
void assign_key(Simplex_handle sh, Simplex_key n)
Store a number for a simplex, which can later be retrieved with key(sh).
Simplex_handle null_simplex()
Returns a Simplex_handle that is different from all simplex handles of the simplices.
Filtration_simplex_range filtration_simplex_range()
Returns a range over the simplices of the complex in the order of the filtration.
unspecified Simplex_handle
Handle to specify a simplex.
Definition FilteredComplex.h:19
Simplex_key key(Simplex_handle sh)
Returns the number stored for a simplex by assign_key.
unspecified Filtration_value
Type for the value of the filtration function.
Definition FilteredComplex.h:23
int dimension(Simplex_handle sh)
Returns the dimension of a simplex.
Simplex_handle simplex(size_t idx)
Returns the simplex that has index idx in the filtration.