Loading...
Searching...
No Matches
zigzag_persistence.h
Go to the documentation of this file.
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) 2021 Inria
6 *
7 * Modification(s):
8 * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug
9 * - 2023/05 Hannah Schreiber: Addition of infinite bars
10 * - 2024/06 Hannah Schreiber: Separation of the zigzag algorithm from the filtration value management
11 * - YYYY/MM Author: Description of the modification
12 */
13
19
20#ifndef ZIGZAG_PERSISTENCE_H_
21#define ZIGZAG_PERSISTENCE_H_
22
23#include <cmath>
24#include <set>
25#include <utility>
26#include <vector>
27
28#include <boost/iterator/indirect_iterator.hpp>
29#include <boost/version.hpp>
30#if BOOST_VERSION >= 108100
31#include <boost/unordered/unordered_flat_map.hpp> //don't exist for lower versions of boost
32// #include <boost/unordered/unordered_map.hpp>
33#else
34#include <unordered_map>
35#endif
36
37#include <gudhi/Matrix.h>
38
39namespace Gudhi {
40namespace zigzag_persistence {
41
49template <Gudhi::persistence_matrix::Column_types column_type>
51 static const bool has_row_access = true;
52 static const bool has_column_pairings = false;
53 static const bool has_vine_update = true;
54 static const bool is_of_boundary_type = false;
55 static const bool has_map_column_container = true;
56 static const bool has_removable_columns = true;
57 static const bool has_removable_rows = true;
58};
59
74
75// TODO: add the possibility of something else than Z2. Which means that the possibility of vineyards without Z2
76// also needs to be implemented. The theory needs to be done first.
148template <class ZigzagOptions = Default_zigzag_options>
150{
151 public:
153 using Index = typename Options::Internal_key;
154 using Dimension = typename Options::Dimension;
155
156 private:
157#if BOOST_VERSION >= 108100
158 using Birth_dictionary = boost::unordered_flat_map<Index, Index>;
159 // using Birth_dictionary = boost::unordered_map<Index, Index>; /**< Dictionary type. */
160#else
161 using Birth_dictionary = std::unordered_map<Index, Index>;
162#endif
163 using Matrix_options = Zigzag_matrix_options<Options::column_type>;
165 using Matrix_index = typename Matrix::Index;
166
186 struct Birth_ordering {
190 Birth_ordering() : birthToPos_(), maxBirthPos_(0), minBirthPos_(-1) {}
191
199 void add_birth_forward(Index arrowNumber) { // amortized constant time
200 birthToPos_.emplace_hint(birthToPos_.end(), arrowNumber, maxBirthPos_);
201 ++maxBirthPos_;
202 }
210 void add_birth_backward(Index arrowNumber) { // amortized constant time
211 birthToPos_.emplace_hint(birthToPos_.end(), arrowNumber, minBirthPos_);
212 --minBirthPos_;
213 }
214
222 void remove_birth(Index birth) { birthToPos_.erase(birth); }
230 bool birth_order(Index k1, Index k2) const { return birthToPos_.at(k1) < birthToPos_.at(k2); }
238 bool reverse_birth_order(Index k1, Index k2) const { return birthToPos_.at(k1) > birthToPos_.at(k2); }
239
240 private:
241 Birth_dictionary birthToPos_;
242 Index maxBirthPos_;
243 Index minBirthPos_;
244 };
245
246 public:
264 Zigzag_persistence(std::function<void(Dimension, Index, Index)> stream_interval,
265 unsigned int preallocationSize = 0)
266 : matrix_(
267 preallocationSize,
268 [this](Matrix_index columnIndex1, Matrix_index columnIndex2) -> bool {
269 if (matrix_.get_column(columnIndex1).is_paired()) {
270 return matrix_.get_pivot(columnIndex1) < matrix_.get_pivot(columnIndex2);
271 }
272 return birthOrdering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2));
273 },
274 [this](Matrix_index columnIndex1, Matrix_index columnIndex2) -> bool { return false; }),
275 numArrow_(-1),
276 stream_interval_(std::move(stream_interval)) {}
277
288 template <class BoundaryRange = std::initializer_list<Index> >
289 Index insert_cell(const BoundaryRange& boundary, Dimension dimension) {
290 ++numArrow_;
291 _process_forward_arrow(boundary, dimension);
292 return numArrow_;
293 }
294
301 Index remove_cell(Index arrowNumber) {
302 ++numArrow_;
303 _process_backward_arrow(arrowNumber);
304 return numArrow_;
305 }
306
313 Index apply_identity() { return ++numArrow_; }
314
323 template <typename F>
324 void get_current_infinite_intervals(F&& stream_infinite_interval) {
325 for (auto& p : births_) {
326 auto& col = matrix_.get_column(p.first);
327 stream_infinite_interval(col.get_dimension(), p.second);
328 }
329 }
330
331 private:
340 template <class BoundaryRange>
341 void _process_forward_arrow(const BoundaryRange& boundary, Dimension dim) {
342 std::vector<Matrix_index> chainsInF = matrix_.insert_boundary(numArrow_, boundary, dim);
343
344 if (!chainsInF.empty()) {
345 _apply_surjective_reflection_diamond(dim, chainsInF);
346 } else {
347 birthOrdering_.add_birth_forward(numArrow_);
348 births_[matrix_.get_column_with_pivot(numArrow_)] = numArrow_;
349 }
350 }
351
363 void _apply_surjective_reflection_diamond(Dimension dim, const std::vector<Matrix_index>& chainsInF) {
364 // fp is the largest death index for <=d
365 // Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx
366 auto chainFp = chainsInF[0]; // col_fp, with largest death <d index.
367
368 // chainsInF is ordered, from .begin() to end(), by decreasing lowest_idx_. The
369 // lowest_idx_ is also the death of the chain in the right suffix of the
370 // filtration (all backward arrows). Consequently, the chains in F are ordered by
371 // decreasing death for <d.
372 // Pair the col_fi, i = 1 ... p-1, according to the reflection diamond principle
373 // Order the fi by reverse birth ordering <=_b
374 auto cmp_birth = [this](Index k1, Index k2) -> bool {
375 return birthOrdering_.reverse_birth_order(k1, k2);
376 }; // true iff b(k1) >b b(k2)
377
378 // availableBirth: for all i by >d value of the d_i,
379 // contains at step i all b_j, j > i, and maybe b_i if not stolen
380 std::set<Index, decltype(cmp_birth)> availableBirth(cmp_birth);
381 // for f1 to f_{p} (i by <=d), insertion in availableBirth sorts by >=b
382 for (auto& chainF : chainsInF) {
383 availableBirth.insert(births_.at(chainF));
384 }
385
386 auto maxbIt = availableBirth.begin(); // max birth cycle
387 auto maxb = *maxbIt; // max birth value, for persistence diagram
388 availableBirth.erase(maxbIt); // remove max birth cycle (stolen)
389
390 auto lastModifiedChainIt = chainsInF.rbegin();
391
392 // consider all death indices by increasing <d order i.e., increasing lowest_idx_
393 for (auto chainFIt = chainsInF.rbegin(); // by increasing death order <d
394 *chainFIt != chainFp; ++chainFIt) // chain_fp=*begin() has max death
395 { // find which reduced col has this birth
396 auto birthIt = availableBirth.find(births_.at(*chainFIt));
397 if (birthIt == availableBirth.end()) // birth is not available. *chain_f_it
398 { // must become the sum of all chains in F with smaller death index.
399 // this gives as birth the maximal birth of all chains with strictly larger
400 // death <=> the maximal available death.
401 // Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and
402 // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set
403 // c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on
404 // the right (of death the maximal<d death(c_i)), and is [c_i + c_i-1 + ... +
405 // c_1] + kernel = [c_f + c_f-1 + ... + c_i+1] on the left (of birth the max<b
406 // of the birth of the c_j, j>i <=> the max<b available birth).
407 // N.B. some of the c_k, k<i, have already been modified to be equal to
408 // c_k + c_k-1 + ... + c_1. The largest k with this property is maintained in
409 // last_modified_chain_it (no need to compute from scratch the full sum).
410
411 // last_modified is equal to c_k+...+c_1, all c_j, i>j>k, are indeed c_j
412 // set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1)
413 for (auto chainPassedIt = lastModifiedChainIt; chainPassedIt != chainFIt; ++chainPassedIt) {
414 // all with smaller <d death
415 matrix_.add_to(*chainPassedIt, *chainFIt);
416 }
417 lastModifiedChainIt = chainFIt; // new cumulated c_i+...+c_1
418 // remove the max available death
419 auto maxAvailBirthIt = availableBirth.begin(); // max because order by decreasing <b
420 Index maxAvailBirth = *maxAvailBirthIt; // max available birth
421
422 births_.at(*chainFIt) = maxAvailBirth; // give new birth
423 availableBirth.erase(maxAvailBirthIt); // remove birth from availability
424 } else {
425 availableBirth.erase(birthIt);
426 } // birth not available anymore, do not
427 } // modify *chain_f_it.
428
429 birthOrdering_.remove_birth(maxb);
430 births_.erase(chainFp);
431
432 // Update persistence diagram with left interval [fil(b_max) ; fil(m))
433 stream_interval_(dim - 1, maxb, numArrow_);
434 }
435
441 void _process_backward_arrow(Index cellID) {
442 // column whose key is the one of the removed cell
443 Matrix_index currCol = matrix_.get_column_with_pivot(cellID);
444
445 // Record all columns that get affected by the transpositions, i.e., have a coeff
446 std::vector<Matrix_index> modifiedColumns;
447 const auto& row = matrix_.get_row(cellID);
448 modifiedColumns.reserve(row.size());
449 std::transform(row.begin(), row.end(), std::back_inserter(modifiedColumns),
450 [](const auto& cell) { return cell.get_column_index(); });
451 // Sort by left-to-right order in the matrix_ (no order maintained in rows)
452 std::stable_sort(modifiedColumns.begin(), modifiedColumns.end(), [this](Matrix_index i1, Matrix_index i2) {
453 return matrix_.get_pivot(i1) < matrix_.get_pivot(i2);
454 });
455
456 // Modifies curr_col, not the other one.
457 for (auto otherColIt = std::next(modifiedColumns.begin()); otherColIt != modifiedColumns.end(); ++otherColIt) {
458 currCol = matrix_.vine_swap_with_z_eq_1_case(currCol, *otherColIt);
459 }
460
461 // curr_col points to the column to remove by restriction of K to K-{\sigma}
462 auto& col = matrix_.get_column(currCol);
463 if (!col.is_paired()) { // in F
464 auto it = births_.find(currCol);
465 stream_interval_(col.get_dimension(), it->second, numArrow_);
466 birthOrdering_.remove_birth(it->second);
467 births_.erase(it);
468 } else { // in H -> paired with c_g, that now belongs to F now
469 // maintain the <=b order
470 birthOrdering_.add_birth_backward(numArrow_);
471 births_[col.get_paired_chain_index()] = numArrow_;
472 }
473
474 // cannot be in G as the removed cell is maximal
475 matrix_.remove_maximal_cell(cellID, {}); // also un-pairs c_g if in H
476 }
477
478 private:
479 Matrix matrix_;
480 Birth_dictionary births_;
481 Birth_ordering birthOrdering_;
482 Index numArrow_;
483 std::function<void(Dimension, Index, Index)> stream_interval_;
484}; // end class Zigzag_persistence
485
486} // namespace zigzag_persistence
487} // namespace Gudhi
488
489#endif // ZIGZAG_PERSISTENCE_H_
Contains Gudhi::persistence_matrix::Matrix class.
Data structure for matrices, and in particular thought for matrices representing filtered complexes i...
Definition Matrix.h:147
typename Matrix_options::Index Index
Definition Matrix.h:150
Zigzag_persistence(std::function< void(Dimension, Index, Index)> stream_interval, unsigned int preallocationSize=0)
Constructor of the Zigzag_persistence class.
Definition zigzag_persistence.h:264
Index remove_cell(Index arrowNumber)
Updates the zigzag persistence diagram after the removal of the given cell.
Definition zigzag_persistence.h:301
Index insert_cell(const BoundaryRange &boundary, Dimension dimension)
Updates the zigzag persistence diagram after the insertion of the given cell.
Definition zigzag_persistence.h:289
Index apply_identity()
To use when a cell is neither inserted nor removed, but the filtration moves along the identity opera...
Definition zigzag_persistence.h:313
typename Options::Dimension Dimension
Definition zigzag_persistence.h:154
ZigzagOptions Options
Definition zigzag_persistence.h:152
typename Options::Internal_key Index
Definition zigzag_persistence.h:153
void get_current_infinite_intervals(F &&stream_infinite_interval)
Outputs through the given callback method all birth indices which are currently not paired with a dea...
Definition zigzag_persistence.h:324
Column_types
List of column types.
Definition persistence_matrix_options.h:32
@ NAIVE_VECTOR
Definition persistence_matrix_options.h:39
Gudhi namespace.
Definition SimplicialComplexForAlpha.h:14
Default option structure for Matrix class. See the PersistenceMatrixOptions concept for a more detail...
Definition persistence_matrix_options.h:80
Default options for Zigzag_persistence.
Definition zigzag_persistence.h:65
int Internal_key
Definition zigzag_persistence.h:66
int Dimension
Definition zigzag_persistence.h:67
static const Gudhi::persistence_matrix::Column_types column_type
Column type use by the internal matrix.
Definition zigzag_persistence.h:71
Options for the internal matrix of Zigzag_persistence.
Definition zigzag_persistence.h:50
List of options used for the zigzag persistence computation.
Definition ZigzagOptions.h:60
unspecified Dimension
Type for the dimension values.
Definition ZigzagOptions.h:69
unspecified Internal_key
Numerical type for the cell IDs used internally and other indexations. It must be signed.
Definition ZigzagOptions.h:64