Base_matrix_with_column_compression.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): Hannah Schreiber
4 *
5 * Copyright (C) 2022-24 Inria
6 *
7 * Modification(s):
8 * - YYYY/MM Author: Description of the modification
9 */
10
17#ifndef PM_BASE_MATRIX_COMPRESSION_H
18#define PM_BASE_MATRIX_COMPRESSION_H
19
20#include <iostream> //print() only
21#include <vector>
22#include <utility> //std::swap, std::move & std::exchange
23
24#include <boost/intrusive/set.hpp>
25#include <boost/pending/disjoint_sets.hpp>
26
27#include <gudhi/Simple_object_pool.h>
28
29namespace Gudhi {
30namespace persistence_matrix {
31
44template <class Master_matrix>
45class Base_matrix_with_column_compression : protected Master_matrix::Matrix_row_access_option
46{
47 public:
48 using Index = typename Master_matrix::Index;
49 using Dimension = typename Master_matrix::Dimension;
53 using Field_operators = typename Master_matrix::Field_operators;
54 using Field_element = typename Master_matrix::Element;
55 using Row = typename Master_matrix::Row;
57 using Entry_constructor = typename Master_matrix::Entry_constructor;
58 using Column_settings = typename Master_matrix::Column_settings;
64 class Column
65 : public Master_matrix::Column,
66 public boost::intrusive::set_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link> >
67 {
68 public:
69 using Base = typename Master_matrix::Column;
70
71 Column(Column_settings* colSettings = nullptr)
72 : Base(colSettings) {}
73 template <class Container>
74 Column(const Container& nonZeroRowIndices, Column_settings* colSettings)
75 : Base(nonZeroRowIndices, colSettings) {}
76 template <class Container, class Row_container>
77 Column(Index columnIndex, const Container& nonZeroRowIndices, Row_container& rowContainer,
78 Column_settings* colSettings)
79 : Base(columnIndex, nonZeroRowIndices, rowContainer, colSettings) {}
80 template <class Container>
81 Column(const Container& nonZeroRowIndices, Dimension dimension, Column_settings* colSettings)
82 : Base(nonZeroRowIndices, dimension, colSettings) {}
83 template <class Container, class Row_container>
84 Column(Index columnIndex, const Container& nonZeroRowIndices, Dimension dimension,
85 Row_container& rowContainer, Column_settings* colSettings)
86 : Base(columnIndex, nonZeroRowIndices, dimension, rowContainer, colSettings) {}
87 Column(const Column& column, Column_settings* colSettings = nullptr)
88 : Base(static_cast<const Base&>(column), colSettings) {}
89 template <class Row_container>
90 Column(const Column& column, Index columnIndex, Row_container& rowContainer,
91 Column_settings* colSettings = nullptr)
92 : Base(static_cast<const Base&>(column), columnIndex, rowContainer, colSettings) {}
93 Column(Column&& column) noexcept : Base(std::move(static_cast<Base&>(column))) {}
94
95 //TODO: is it possible to make this work?
96 // template <class... U>
97 // Column(U&&... u) : Base(std::forward<U>(u)...) {}
98
99 Index get_rep() const { return rep_; }
100 void set_rep(const Index& rep) { rep_ = rep; }
101
102 struct Hasher {
103 size_t operator()(const Column& c) const { return std::hash<Base>()(static_cast<Base>(c)); }
104 };
105
106 private:
107 Index rep_;
108 };
109
129 template <class Container>
130 Base_matrix_with_column_compression(const std::vector<Container>& columns,
131 Column_settings* colSettings);
139 Base_matrix_with_column_compression(unsigned int numberOfColumns,
140 Column_settings* colSettings);
151 Column_settings* colSettings = nullptr);
162
171 template <class Container>
172 void insert_column(const Container& column);
182 template <class Boundary_range>
183 void insert_boundary(const Boundary_range& boundary,
184 Dimension dim = Master_matrix::template get_null_value<Dimension>());
195 const Column& get_column(Index columnIndex);
205 const Row& get_row(Index rowIndex) const;
219 void erase_empty_row(Index rowIndex);
220
227
239 template <class Entry_range_or_column_index>
240 void add_to(const Entry_range_or_column_index& sourceColumn, Index targetColumnIndex);
254 template <class Entry_range_or_column_index>
255 void multiply_target_and_add_to(const Entry_range_or_column_index& sourceColumn,
256 const Field_element& coefficient,
257 Index targetColumnIndex);
271 template <class Entry_range_or_column_index>
272 void multiply_source_and_add_to(const Field_element& coefficient,
273 const Entry_range_or_column_index& sourceColumn,
274 Index targetColumnIndex);
275
284 bool is_zero_entry(Index columnIndex, Index rowIndex);
292 bool is_zero_column(Index columnIndex);
293
299 void reset(Column_settings* colSettings) {
300 columnToRep_.clear_and_dispose(Delete_disposer(this));
301 columnClasses_ = boost::disjoint_sets_with_storage<>();
302 repToColumn_.clear();
303 nextColumnIndex_ = 0;
304 colSettings_ = colSettings;
305 }
306
315 matrix1.columnToRep_.swap(matrix2.columnToRep_);
316 std::swap(matrix1.columnClasses_, matrix2.columnClasses_);
317 matrix1.repToColumn_.swap(matrix2.repToColumn_);
318 std::swap(matrix1.nextColumnIndex_, matrix2.nextColumnIndex_);
319 std::swap(matrix1.colSettings_, matrix2.colSettings_);
320 std::swap(matrix1.columnPool_, matrix2.columnPool_);
321
322 if constexpr (Master_matrix::Option_list::has_row_access) {
323 swap(static_cast<typename Master_matrix::Matrix_row_access_option&>(matrix1),
324 static_cast<typename Master_matrix::Matrix_row_access_option&>(matrix2));
325 }
326 }
327
328 void print(); // for debug
329
330 private:
334 struct Delete_disposer {
335 Delete_disposer(Base_matrix_with_column_compression* matrix) : matrix_(matrix) {}
336
337 void operator()(Column* delete_this) { matrix_->columnPool_->destroy(delete_this); }
338
340 };
341
342 using RA_opt = typename Master_matrix::Matrix_row_access_option;
343 using Col_dict = boost::intrusive::set<Column, boost::intrusive::constant_time_size<false> >;
344
345 Col_dict columnToRep_;
346 boost::disjoint_sets_with_storage<> columnClasses_;
348 std::vector<Column*> repToColumn_;
350 Index nextColumnIndex_;
351 Column_settings* colSettings_;
356 std::unique_ptr<Simple_object_pool<Column> > columnPool_;
357 inline static const Column empty_column_;
359 void _insert_column(Index columnIndex);
360 void _insert_double_column(Index columnIndex, typename Col_dict::iterator& doubleIt);
361};
362
363template <class Master_matrix>
365 Column_settings* colSettings)
366 : RA_opt(), nextColumnIndex_(0), colSettings_(colSettings), columnPool_(new Simple_object_pool<Column>)
367{}
368
369template <class Master_matrix>
370template <class Container>
372 const std::vector<Container>& columns, Column_settings* colSettings)
373 : RA_opt(columns.size()),
374 columnClasses_(columns.size()),
375 repToColumn_(columns.size(), nullptr),
376 nextColumnIndex_(0),
377 colSettings_(colSettings),
378 columnPool_(new Simple_object_pool<Column>)
379{
380 for (const Container& c : columns) {
381 insert_column(c);
382 }
383}
384
385template <class Master_matrix>
387 unsigned int numberOfColumns, Column_settings* colSettings)
388 : RA_opt(numberOfColumns),
389 columnClasses_(numberOfColumns),
390 repToColumn_(numberOfColumns, nullptr),
391 nextColumnIndex_(0),
392 colSettings_(colSettings),
393 columnPool_(new Simple_object_pool<Column>)
394{}
395
396template <class Master_matrix>
398 const Base_matrix_with_column_compression& matrixToCopy, Column_settings* colSettings)
399 : RA_opt(static_cast<const RA_opt&>(matrixToCopy)),
400 columnClasses_(matrixToCopy.columnClasses_),
401 repToColumn_(matrixToCopy.repToColumn_.size(), nullptr),
402 nextColumnIndex_(0),
403 colSettings_(colSettings == nullptr ? matrixToCopy.colSettings_ : colSettings),
404 columnPool_(new Simple_object_pool<Column>)
405{
406 for (const Column* col : matrixToCopy.repToColumn_) {
407 if (col != nullptr) {
408 if constexpr (Master_matrix::Option_list::has_row_access) {
409 repToColumn_[nextColumnIndex_] =
410 columnPool_->construct(*col, col->get_column_index(), RA_opt::rows_, colSettings_);
411 } else {
412 repToColumn_[nextColumnIndex_] = columnPool_->construct(*col, colSettings_);
413 }
414 columnToRep_.insert(columnToRep_.end(), *repToColumn_[nextColumnIndex_]);
415 repToColumn_[nextColumnIndex_]->set_rep(nextColumnIndex_);
416 }
417 nextColumnIndex_++;
418 }
419}
420
421template <class Master_matrix>
424 : RA_opt(std::move(static_cast<RA_opt&>(other))),
425 columnToRep_(std::move(other.columnToRep_)),
426 columnClasses_(std::move(other.columnClasses_)),
427 repToColumn_(std::move(other.repToColumn_)),
428 nextColumnIndex_(std::exchange(other.nextColumnIndex_, 0)),
429 colSettings_(std::exchange(other.colSettings_, nullptr)),
430 columnPool_(std::exchange(other.columnPool_, nullptr))
431{}
432
433template <class Master_matrix>
435{
436 columnToRep_.clear_and_dispose(Delete_disposer(this));
437}
438
439template <class Master_matrix>
440template <class Container>
442{
443 insert_boundary(column);
444}
445
446template <class Master_matrix>
447template <class Boundary_range>
449 Dimension dim)
450{
451 // handles a dimension which is not actually stored.
452 // TODO: verify if this is not a problem for the cohomology algorithm and if yes,
453 // change how Column_dimension_option is chosen.
454 if (dim == Master_matrix::template get_null_value<Dimension>()) dim = boundary.size() == 0 ? 0 : boundary.size() - 1;
455
456 if constexpr (Master_matrix::Option_list::has_row_access && !Master_matrix::Option_list::has_removable_rows) {
457 if (boundary.begin() != boundary.end()) {
458 Index pivot;
459 if constexpr (Master_matrix::Option_list::is_z2) {
460 pivot = *std::prev(boundary.end());
461 } else {
462 pivot = std::prev(boundary.end())->first;
463 }
464 if (RA_opt::rows_->size() <= pivot) RA_opt::rows_->resize(pivot + 1);
465 }
466 }
467
468 if (repToColumn_.size() == nextColumnIndex_) {
469 // could perhaps be avoided, if find_set returns something special when it does not find
470 columnClasses_.link(nextColumnIndex_, nextColumnIndex_);
471 if constexpr (Master_matrix::Option_list::has_row_access) {
472 repToColumn_.push_back(
473 columnPool_->construct(nextColumnIndex_, boundary, dim, RA_opt::rows_, colSettings_));
474 } else {
475 repToColumn_.push_back(columnPool_->construct(boundary, dim, colSettings_));
476 }
477 } else {
478 if constexpr (Master_matrix::Option_list::has_row_access) {
479 repToColumn_[nextColumnIndex_] =
480 columnPool_->construct(nextColumnIndex_, boundary, dim, RA_opt::rows_, colSettings_);
481 } else {
482 repToColumn_[nextColumnIndex_] = columnPool_->construct(boundary, dim, colSettings_);
483 }
484 }
485 _insert_column(nextColumnIndex_);
486
487 nextColumnIndex_++;
488}
489
490template <class Master_matrix>
493{
494 auto col = repToColumn_[columnClasses_.find_set(columnIndex)];
495 if (col == nullptr) return empty_column_;
496 return *col;
497}
498
499template <class Master_matrix>
502{
503 static_assert(Master_matrix::Option_list::has_row_access, "Row access has to be enabled for this method.");
504
505 return RA_opt::get_row(rowIndex);
506}
507
508template <class Master_matrix>
510{
511 if constexpr (Master_matrix::Option_list::has_row_access && Master_matrix::Option_list::has_removable_rows) {
512 RA_opt::erase_empty_row(rowIndex);
513 }
514}
515
516template <class Master_matrix>
519{
520 return nextColumnIndex_;
521}
522
523template <class Master_matrix>
524template <class Entry_range_or_column_index>
525inline void Base_matrix_with_column_compression<Master_matrix>::add_to(const Entry_range_or_column_index& sourceColumn,
526 Index targetColumnIndex)
527{
528 // handle case where targetRep == sourceRep?
529 Index targetRep = columnClasses_.find_set(targetColumnIndex);
530 Column& target = *repToColumn_[targetRep];
531 columnToRep_.erase(target);
532 if constexpr (std::is_integral_v<Entry_range_or_column_index>) {
533 target += get_column(sourceColumn);
534 } else {
535 target += sourceColumn;
536 }
537 _insert_column(targetRep);
538}
539
540template <class Master_matrix>
541template <class Entry_range_or_column_index>
543 const Entry_range_or_column_index& sourceColumn, const Field_element& coefficient, Index targetColumnIndex)
544{
545 // handle case where targetRep == sourceRep?
546 Index targetRep = columnClasses_.find_set(targetColumnIndex);
547 Column& target = *repToColumn_[targetRep];
548 columnToRep_.erase(target);
549 if constexpr (std::is_integral_v<Entry_range_or_column_index>) {
550 target.multiply_target_and_add(coefficient, get_column(sourceColumn));
551 } else {
552 target.multiply_target_and_add(coefficient, sourceColumn);
553 }
554 _insert_column(targetRep);
555}
556
557template <class Master_matrix>
558template <class Entry_range_or_column_index>
560 const Field_element& coefficient, const Entry_range_or_column_index& sourceColumn, Index targetColumnIndex)
561{
562 // handle case where targetRep == sourceRep?
563 Index targetRep = columnClasses_.find_set(targetColumnIndex);
564 Column& target = *repToColumn_[targetRep];
565 columnToRep_.erase(target);
566 if constexpr (std::is_integral_v<Entry_range_or_column_index>) {
567 target.multiply_source_and_add(get_column(sourceColumn), coefficient);
568 } else {
569 target.multiply_source_and_add(sourceColumn, coefficient);
570 }
571 _insert_column(targetRep);
572}
573
574template <class Master_matrix>
576{
577 auto col = repToColumn_[columnClasses_.find_set(columnIndex)];
578 if (col == nullptr) return true;
579 return !col->is_non_zero(rowIndex);
580}
581
582template <class Master_matrix>
584{
585 auto col = repToColumn_[columnClasses_.find_set(columnIndex)];
586 if (col == nullptr) return true;
587 return col->is_empty();
588}
589
590template <class Master_matrix>
593{
594 for (auto col : repToColumn_) {
595 if (col != nullptr) {
596 columnPool_->destroy(col);
597 col = nullptr;
598 }
599 }
600 RA_opt::operator=(other);
601 columnClasses_ = other.columnClasses_;
602 columnToRep_.reserve(other.columnToRep_.size());
603 repToColumn_.resize(other.repToColumn_.size(), nullptr);
604 nextColumnIndex_ = 0;
605 colSettings_ = other.colSettings_;
606 for (const Column* col : other.repToColumn_) {
607 if constexpr (Master_matrix::Option_list::has_row_access) {
608 repToColumn_[nextColumnIndex_] =
609 columnPool_->construct(*col, col->get_column_index(), RA_opt::rows_, colSettings_);
610 } else {
611 repToColumn_[nextColumnIndex_] = columnPool_->construct(*col, colSettings_);
612 }
613 columnToRep_.insert(columnToRep_.end(), *repToColumn_[nextColumnIndex_]);
614 repToColumn_[nextColumnIndex_]->set_rep(nextColumnIndex_);
615 nextColumnIndex_++;
616 }
617 return *this;
618}
619
620template <class Master_matrix>
622{
623 std::cout << "Compressed_matrix:\n";
624 for (Column& col : columnToRep_) {
625 for (auto e : col->get_content(nextColumnIndex_)) {
626 if (e == 0u)
627 std::cout << "- ";
628 else
629 std::cout << e << " ";
630 }
631 std::cout << "(";
632 for (Index i = 0; i < nextColumnIndex_; ++i) {
633 if (columnClasses_.find_set(i) == col.get_rep()) std::cout << i << " ";
634 }
635 std::cout << ")\n";
636 }
637 std::cout << "\n";
638 std::cout << "Row Matrix:\n";
639 for (Index i = 0; i < RA_opt::rows_->size(); ++i) {
640 const Row& row = RA_opt::rows_[i];
641 for (const auto& entry : row) {
642 std::cout << entry.get_column_index() << " ";
643 }
644 std::cout << "(" << i << ")\n";
645 }
646 std::cout << "\n";
647}
648
649template <class Master_matrix>
650inline void Base_matrix_with_column_compression<Master_matrix>::_insert_column(Index columnIndex)
651{
652 Column& col = *repToColumn_[columnIndex];
653
654 if (col.is_empty()) {
655 columnPool_->destroy(&col); // delete curr_col;
656 repToColumn_[columnIndex] = nullptr;
657 return;
658 }
659
660 col.set_rep(columnIndex);
661 auto res = columnToRep_.insert(col);
662 if (res.first->get_rep() != columnIndex) { //if true, then redundant column
663 _insert_double_column(columnIndex, res.first);
664 }
665}
666
667template <class Master_matrix>
668inline void Base_matrix_with_column_compression<Master_matrix>::_insert_double_column(
669 Index columnIndex, typename Col_dict::iterator& doubleIt)
670{
671 Index doubleRep = doubleIt->get_rep();
672 columnClasses_.link(columnIndex, doubleRep); // both should be representatives
673 Index newRep = columnClasses_.find_set(columnIndex);
674
675 columnPool_->destroy(repToColumn_[columnIndex]);
676 repToColumn_[columnIndex] = nullptr;
677
678 if (newRep == columnIndex) {
679 std::swap(repToColumn_[doubleRep], repToColumn_[columnIndex]);
680 doubleIt->set_rep(columnIndex);
681 }
682}
683
684} // namespace persistence_matrix
685} // namespace Gudhi
686
687#endif // PM_BASE_MATRIX_COMPRESSION_H
Type for columns. Only one for each "column class" is explicitly constructed.
Definition: Base_matrix_with_column_compression.h:67
A base matrix (also see Base_matrix), but with column compression. That is, all identical columns in ...
Definition: Base_matrix_with_column_compression.h:46
typename Master_matrix::Index Index
Definition: Base_matrix_with_column_compression.h:48
typename Master_matrix::Row Row
Definition: Base_matrix_with_column_compression.h:56
void reset(Column_settings *colSettings)
Resets the matrix to an empty matrix.
Definition: Base_matrix_with_column_compression.h:299
typename Master_matrix::Column_settings Column_settings
Definition: Base_matrix_with_column_compression.h:59
typename Master_matrix::Dimension Dimension
Definition: Base_matrix_with_column_compression.h:49
const Row & get_row(Index rowIndex) const
Only available if PersistenceMatrixOptions::has_row_access is true. Returns the row at the given row ...
Definition: Base_matrix_with_column_compression.h:501
void add_to(const Entry_range_or_column_index &sourceColumn, Index targetColumnIndex)
Adds column represented by sourceColumn onto the column at targetColumnIndex in the matrix.
Definition: Base_matrix_with_column_compression.h:525
Base_matrix_with_column_compression(Column_settings *colSettings)
Constructs an empty matrix.
Definition: Base_matrix_with_column_compression.h:364
typename Master_matrix::Field_operators Field_operators
Field operators class. Necessary only if PersistenceMatrixOptions::is_z2 is false.
Definition: Base_matrix_with_column_compression.h:53
bool is_zero_column(Index columnIndex)
Indicates if the column at given index has value zero.
Definition: Base_matrix_with_column_compression.h:583
Index get_number_of_columns() const
Returns the current number of columns in the matrix, counting also the redundant columns.
Definition: Base_matrix_with_column_compression.h:518
void multiply_source_and_add_to(const Field_element &coefficient, const Entry_range_or_column_index &sourceColumn, Index targetColumnIndex)
Multiplies the source column with the coefficient before adding it to the target column....
Definition: Base_matrix_with_column_compression.h:559
void multiply_target_and_add_to(const Entry_range_or_column_index &sourceColumn, const Field_element &coefficient, Index targetColumnIndex)
Multiplies the target column with the coefficient and then adds the source column to it....
Definition: Base_matrix_with_column_compression.h:542
friend void swap(Base_matrix_with_column_compression &matrix1, Base_matrix_with_column_compression &matrix2)
Swap operator.
Definition: Base_matrix_with_column_compression.h:314
~Base_matrix_with_column_compression()
Destructor.
Definition: Base_matrix_with_column_compression.h:434
void insert_column(const Container &column)
Inserts a new ordered column at the end of the matrix by copying the given range of Matrix::Entry_rep...
Definition: Base_matrix_with_column_compression.h:441
void erase_empty_row(Index rowIndex)
If PersistenceMatrixOptions::has_row_access and PersistenceMatrixOptions::has_removable_rows are true...
Definition: Base_matrix_with_column_compression.h:509
typename Master_matrix::Entry_constructor Entry_constructor
Definition: Base_matrix_with_column_compression.h:57
typename Master_matrix::Element Field_element
Definition: Base_matrix_with_column_compression.h:54
const Column & get_column(Index columnIndex)
Returns the column at the given MatIdx index. The type of the column depends on the chosen options,...
Definition: Base_matrix_with_column_compression.h:492
void insert_boundary(const Boundary_range &boundary, Dimension dim=Master_matrix::template get_null_value< Dimension >())
Same as insert_column, only for interface purposes. The given dimension is ignored and not stored.
Definition: Base_matrix_with_column_compression.h:448
Base_matrix_with_column_compression & operator=(const Base_matrix_with_column_compression &other)
Assign operator.
Definition: Base_matrix_with_column_compression.h:592
bool is_zero_entry(Index columnIndex, Index rowIndex)
Indicates if the entry at given coordinates has value zero.
Definition: Base_matrix_with_column_compression.h:575
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14