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 
29 namespace Gudhi {
30 namespace persistence_matrix {
31 
44 template <class Master_matrix>
45 class 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_type = typename Master_matrix::dimension_type;
53  using Field_operators = typename Master_matrix::Field_operators;
54  using Field_element_type = typename Master_matrix::element_type;
55  using Row_type = typename Master_matrix::Row_type;
57  using Cell_constructor = typename Master_matrix::Cell_constructor;
58  using Column_settings = typename Master_matrix::Column_settings;
65  : public Master_matrix::Column_type,
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_type;
70 
71  Column_type(Column_settings* colSettings = nullptr)
72  : Base(colSettings) {}
73  template <class Container_type>
74  Column_type(const Container_type& nonZeroRowIndices, Column_settings* colSettings)
75  : Base(nonZeroRowIndices, colSettings) {}
76  template <class Container_type, class Row_container_type>
77  Column_type(index columnIndex, const Container_type& nonZeroRowIndices, Row_container_type& rowContainer,
78  Column_settings* colSettings)
79  : Base(columnIndex, nonZeroRowIndices, rowContainer, colSettings) {}
80  template <class Container_type>
81  Column_type(const Container_type& nonZeroRowIndices, dimension_type dimension, Column_settings* colSettings)
82  : Base(nonZeroRowIndices, dimension, colSettings) {}
83  template <class Container_type, class Row_container_type>
84  Column_type(index columnIndex, const Container_type& nonZeroRowIndices, dimension_type dimension,
85  Row_container_type& rowContainer, Column_settings* colSettings)
86  : Base(columnIndex, nonZeroRowIndices, dimension, rowContainer, colSettings) {}
87  Column_type(const Column_type& column, Column_settings* colSettings = nullptr)
88  : Base(static_cast<const Base&>(column), colSettings) {}
89  template <class Row_container_type>
90  Column_type(const Column_type& column, index columnIndex, Row_container_type& rowContainer,
91  Column_settings* colSettings = nullptr)
92  : Base(static_cast<const Base&>(column), columnIndex, rowContainer, colSettings) {}
93  Column_type(Column_type&& 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_type(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_type& c) const { return std::hash<Base>()(static_cast<Base>(c)); }
104  };
105 
106  private:
107  index rep_;
108  };
109 
129  template <class Container_type>
130  Base_matrix_with_column_compression(const std::vector<Container_type>& 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_type>
172  void insert_column(const Container_type& column);
181  template <class Boundary_type>
182  void insert_boundary(const Boundary_type& boundary, dimension_type dim = -1);
193  const Column_type& get_column(index columnIndex);
203  const Row_type& get_row(index rowIndex) const;
217  void erase_empty_row(index rowIndex);
218 
225 
237  template <class Cell_range_or_column_index>
238  void add_to(const Cell_range_or_column_index& sourceColumn, index targetColumnIndex);
252  template <class Cell_range_or_column_index>
253  void multiply_target_and_add_to(const Cell_range_or_column_index& sourceColumn,
254  const Field_element_type& coefficient,
255  index targetColumnIndex);
269  template <class Cell_range_or_column_index>
270  void multiply_source_and_add_to(const Field_element_type& coefficient,
271  const Cell_range_or_column_index& sourceColumn,
272  index targetColumnIndex);
273 
282  bool is_zero_cell(index columnIndex, index rowIndex);
290  bool is_zero_column(index columnIndex);
291 
298  void reset(Column_settings* colSettings) {
299  columnToRep_.clear_and_dispose(delete_disposer());
300  columnClasses_ = boost::disjoint_sets_with_storage<>();
301  repToColumn_.clear();
302  nextColumnIndex_ = 0;
303  colSettings_ = colSettings;
304  }
305 
314  matrix1.columnToRep_.swap(matrix2.columnToRep_);
315  std::swap(matrix1.columnClasses_, matrix2.columnClasses_);
316  matrix1.repToColumn_.swap(matrix2.repToColumn_); // be careful when columnPool_ becomes not static
317  std::swap(matrix1.nextColumnIndex_, matrix2.nextColumnIndex_);
318  std::swap(matrix1.colSettings_, matrix2.colSettings_);
319 
320  if constexpr (Master_matrix::Option_list::has_row_access) {
321  swap(static_cast<typename Master_matrix::Matrix_row_access_option&>(matrix1),
322  static_cast<typename Master_matrix::Matrix_row_access_option&>(matrix2));
323  }
324  }
325 
326  void print(); // for debug
327 
328  private:
332  struct delete_disposer {
333  void operator()(Column_type* delete_this) { columnPool_.destroy(delete_this); }
334  };
335 
336  using ra_opt = typename Master_matrix::Matrix_row_access_option;
337  using col_dict_type = boost::intrusive::set<Column_type, boost::intrusive::constant_time_size<false> >;
338 
339  col_dict_type columnToRep_;
340  boost::disjoint_sets_with_storage<> columnClasses_;
342  std::vector<Column_type*> repToColumn_;
344  index nextColumnIndex_;
345  Column_settings* colSettings_;
351  inline static Simple_object_pool<Column_type> columnPool_;
352  inline static const Column_type empty_column_;
354  void _insert_column(index columnIndex);
355  void _insert_double_column(index columnIndex, typename col_dict_type::iterator& doubleIt);
356 };
357 
358 template <class Master_matrix>
360  Column_settings* colSettings)
361  : ra_opt(), nextColumnIndex_(0), colSettings_(colSettings)
362 {}
363 
364 template <class Master_matrix>
365 template <class Container_type>
367  const std::vector<Container_type>& columns, Column_settings* colSettings)
368  : ra_opt(columns.size()),
369  columnClasses_(columns.size()),
370  repToColumn_(columns.size(), nullptr),
371  nextColumnIndex_(0),
372  colSettings_(colSettings)
373 {
374  for (const Container_type& c : columns) {
375  insert_column(c);
376  }
377 }
378 
379 template <class Master_matrix>
381  unsigned int numberOfColumns, Column_settings* colSettings)
382  : ra_opt(numberOfColumns),
383  columnClasses_(numberOfColumns),
384  repToColumn_(numberOfColumns, nullptr),
385  nextColumnIndex_(0),
386  colSettings_(colSettings)
387 {}
388 
389 template <class Master_matrix>
391  const Base_matrix_with_column_compression& matrixToCopy, Column_settings* colSettings)
392  : ra_opt(static_cast<const ra_opt&>(matrixToCopy)),
393  columnClasses_(matrixToCopy.columnClasses_),
394  repToColumn_(matrixToCopy.repToColumn_.size(), nullptr),
395  nextColumnIndex_(0),
396  colSettings_(colSettings == nullptr ? matrixToCopy.colSettings_ : colSettings)
397 {
398  for (const Column_type* col : matrixToCopy.repToColumn_) {
399  if (col != nullptr) {
400  if constexpr (Master_matrix::Option_list::has_row_access) {
401  repToColumn_[nextColumnIndex_] =
402  columnPool_.construct(*col, col->get_column_index(), ra_opt::rows_, colSettings_);
403  } else {
404  repToColumn_[nextColumnIndex_] = columnPool_.construct(*col, colSettings_);
405  }
406  columnToRep_.insert(columnToRep_.end(), *repToColumn_[nextColumnIndex_]);
407  repToColumn_[nextColumnIndex_]->set_rep(nextColumnIndex_);
408  }
409  nextColumnIndex_++;
410  }
411 }
412 
413 template <class Master_matrix>
415  Base_matrix_with_column_compression&& other) noexcept
416  : ra_opt(std::move(static_cast<ra_opt&>(other))),
417  columnToRep_(std::move(other.columnToRep_)),
418  columnClasses_(std::move(other.columnClasses_)),
419  repToColumn_(std::move(other.repToColumn_)),
420  nextColumnIndex_(std::exchange(other.nextColumnIndex_, 0)),
421  colSettings_(std::exchange(other.colSettings_, nullptr))
422 {}
423 
424 template <class Master_matrix>
426 {
427  columnToRep_.clear_and_dispose(delete_disposer());
428 }
429 
430 template <class Master_matrix>
431 template <class Container_type>
433 {
434  insert_boundary(column);
435 }
436 
437 template <class Master_matrix>
438 template <class Boundary_type>
440  dimension_type dim)
441 {
442  // handles a dimension which is not actually stored.
443  // TODO: verify if this is not a problem for the cohomology algorithm and if yes,
444  // change how Column_dimension_option is choosen.
445  if (dim == -1) dim = boundary.size() == 0 ? 0 : boundary.size() - 1;
446 
447  if constexpr (Master_matrix::Option_list::has_row_access && !Master_matrix::Option_list::has_removable_rows) {
448  if (boundary.begin() != boundary.end()) {
449  index pivot;
450  if constexpr (Master_matrix::Option_list::is_z2) {
451  pivot = *std::prev(boundary.end());
452  } else {
453  pivot = std::prev(boundary.end())->first;
454  }
455  if (ra_opt::rows_->size() <= pivot) ra_opt::rows_->resize(pivot + 1);
456  }
457  }
458 
459  if (repToColumn_.size() == nextColumnIndex_) {
460  // could perhaps be avoided, if find_set returns something special when it does not find
461  columnClasses_.link(nextColumnIndex_, nextColumnIndex_);
462  if constexpr (Master_matrix::Option_list::has_row_access) {
463  repToColumn_.push_back(
464  columnPool_.construct(nextColumnIndex_, boundary, dim, ra_opt::rows_, colSettings_));
465  } else {
466  repToColumn_.push_back(columnPool_.construct(boundary, dim, colSettings_));
467  }
468  } else {
469  if constexpr (Master_matrix::Option_list::has_row_access) {
470  repToColumn_[nextColumnIndex_] =
471  columnPool_.construct(nextColumnIndex_, boundary, dim, ra_opt::rows_, colSettings_);
472  } else {
473  repToColumn_[nextColumnIndex_] = columnPool_.construct(boundary, dim, colSettings_);
474  }
475  }
476  _insert_column(nextColumnIndex_);
477 
478  nextColumnIndex_++;
479 }
480 
481 template <class Master_matrix>
484 {
485  auto col = repToColumn_[columnClasses_.find_set(columnIndex)];
486  if (col == nullptr) return empty_column_;
487  return *col;
488 }
489 
490 template <class Master_matrix>
493 {
494  static_assert(Master_matrix::Option_list::has_row_access, "Row access has to be enabled for this method.");
495 
496  return ra_opt::get_row(rowIndex);
497 }
498 
499 template <class Master_matrix>
501 {
502  if constexpr (Master_matrix::Option_list::has_row_access && Master_matrix::Option_list::has_removable_rows) {
503  ra_opt::erase_empty_row(rowIndex);
504  }
505 }
506 
507 template <class Master_matrix>
510 {
511  return nextColumnIndex_;
512 }
513 
514 template <class Master_matrix>
515 template <class Cell_range_or_column_index>
516 inline void Base_matrix_with_column_compression<Master_matrix>::add_to(const Cell_range_or_column_index& sourceColumn,
517  index targetColumnIndex)
518 {
519  // handle case where targetRep == sourceRep?
520  index targetRep = columnClasses_.find_set(targetColumnIndex);
521  Column_type& target = *repToColumn_[targetRep];
522  columnToRep_.erase(target);
523  if constexpr (std::is_integral_v<Cell_range_or_column_index>) {
524  target += get_column(sourceColumn);
525  } else {
526  target += sourceColumn;
527  }
528  _insert_column(targetRep);
529 }
530 
531 template <class Master_matrix>
532 template <class Cell_range_or_column_index>
534  const Cell_range_or_column_index& sourceColumn, const Field_element_type& coefficient, index targetColumnIndex)
535 {
536  // handle case where targetRep == sourceRep?
537  index targetRep = columnClasses_.find_set(targetColumnIndex);
538  Column_type& target = *repToColumn_[targetRep];
539  columnToRep_.erase(target);
540  if constexpr (std::is_integral_v<Cell_range_or_column_index>) {
541  target.multiply_target_and_add(coefficient, get_column(sourceColumn));
542  } else {
543  target.multiply_target_and_add(coefficient, sourceColumn);
544  }
545  _insert_column(targetRep);
546 }
547 
548 template <class Master_matrix>
549 template <class Cell_range_or_column_index>
551  const Field_element_type& coefficient, const Cell_range_or_column_index& sourceColumn, index targetColumnIndex)
552 {
553  // handle case where targetRep == sourceRep?
554  index targetRep = columnClasses_.find_set(targetColumnIndex);
555  Column_type& target = *repToColumn_[targetRep];
556  columnToRep_.erase(target);
557  if constexpr (std::is_integral_v<Cell_range_or_column_index>) {
558  target.multiply_source_and_add(get_column(sourceColumn), coefficient);
559  } else {
560  target.multiply_source_and_add(sourceColumn, coefficient);
561  }
562  _insert_column(targetRep);
563 }
564 
565 template <class Master_matrix>
567 {
568  auto col = repToColumn_[columnClasses_.find_set(columnIndex)];
569  if (col == nullptr) return true;
570  return !col->is_non_zero(rowIndex);
571 }
572 
573 template <class Master_matrix>
575 {
576  auto col = repToColumn_[columnClasses_.find_set(columnIndex)];
577  if (col == nullptr) return true;
578  return col->is_empty();
579 }
580 
581 template <class Master_matrix>
584 {
585  for (auto col : repToColumn_) {
586  if (col != nullptr) {
587  columnPool_.destroy(col);
588  col = nullptr;
589  }
590  }
591  ra_opt::operator=(other);
592  columnClasses_ = other.columnClasses_;
593  columnToRep_.reserve(other.columnToRep_.size());
594  repToColumn_.resize(other.repToColumn_.size(), nullptr);
595  nextColumnIndex_ = 0;
596  colSettings_ = other.colSettings_;
597  for (const Column_type* col : other.repToColumn_) {
598  if constexpr (Master_matrix::Option_list::has_row_access) {
599  repToColumn_[nextColumnIndex_] =
600  columnPool_.construct(*col, col->get_column_index(), ra_opt::rows_, colSettings_);
601  } else {
602  repToColumn_[nextColumnIndex_] = columnPool_.construct(*col, colSettings_);
603  }
604  columnToRep_.insert(columnToRep_.end(), *repToColumn_[nextColumnIndex_]);
605  repToColumn_[nextColumnIndex_]->set_rep(nextColumnIndex_);
606  nextColumnIndex_++;
607  }
608  return *this;
609 }
610 
611 template <class Master_matrix>
613 {
614  std::cout << "Compressed_matrix:\n";
615  for (Column_type& col : columnToRep_) {
616  for (auto e : col->get_content(nextColumnIndex_)) {
617  if (e == 0u)
618  std::cout << "- ";
619  else
620  std::cout << e << " ";
621  }
622  std::cout << "(";
623  for (index i = 0; i < nextColumnIndex_; ++i) {
624  if (columnClasses_.find_set(i) == col.get_rep()) std::cout << i << " ";
625  }
626  std::cout << ")\n";
627  }
628  std::cout << "\n";
629  std::cout << "Row Matrix:\n";
630  for (index i = 0; i < ra_opt::rows_->size(); ++i) {
631  const Row_type& row = ra_opt::rows_[i];
632  for (const auto& cell : row) {
633  std::cout << cell.get_column_index() << " ";
634  }
635  std::cout << "(" << i << ")\n";
636  }
637  std::cout << "\n";
638 }
639 
640 template <class Master_matrix>
641 inline void Base_matrix_with_column_compression<Master_matrix>::_insert_column(index columnIndex)
642 {
643  Column_type& col = *repToColumn_[columnIndex];
644 
645  if (col.is_empty()) {
646  columnPool_.destroy(&col); // delete curr_col;
647  repToColumn_[columnIndex] = nullptr;
648  return;
649  }
650 
651  col.set_rep(columnIndex);
652  auto res = columnToRep_.insert(col);
653  if (res.first->get_rep() != columnIndex) { //if true, then redundant column
654  _insert_double_column(columnIndex, res.first);
655  }
656 }
657 
658 template <class Master_matrix>
659 inline void Base_matrix_with_column_compression<Master_matrix>::_insert_double_column(
660  index columnIndex, typename col_dict_type::iterator& doubleIt)
661 {
662  index doubleRep = doubleIt->get_rep();
663  columnClasses_.link(columnIndex, doubleRep); // both should be representatives
664  index newRep = columnClasses_.find_set(columnIndex);
665 
666  columnPool_.destroy(repToColumn_[columnIndex]);
667  repToColumn_[columnIndex] = nullptr;
668 
669  if (newRep == columnIndex) {
670  std::swap(repToColumn_[doubleRep], repToColumn_[columnIndex]);
671  doubleIt->set_rep(columnIndex);
672  }
673 }
674 
675 } // namespace persistence_matrix
676 } // namespace Gudhi
677 
678 #endif // PM_BASE_MATRIX_COMPRESSION_H
Type for columns. Only one for each "column class" is explicitely 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
bool is_zero_cell(index columnIndex, index rowIndex)
Indicates if the cell at given coordinates has value zero.
Definition: base_matrix_with_column_compression.h:566
void reset(Column_settings *colSettings)
Resets the matrix to an empty matrix.
Definition: base_matrix_with_column_compression.h:298
typename Master_matrix::Column_settings Column_settings
Definition: base_matrix_with_column_compression.h:59
bool is_zero_column(index columnIndex)
Indicates if the column at given index has value zero.
Definition: base_matrix_with_column_compression.h:574
const Column_type & get_column(index columnIndex)
Returns the column at the given MatIdx index. The type of the column depends on the choosen options,...
Definition: base_matrix_with_column_compression.h:483
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:500
typename Master_matrix::dimension_type dimension_type
Definition: base_matrix_with_column_compression.h:49
void multiply_source_and_add_to(const Field_element_type &coefficient, const Cell_range_or_column_index &sourceColumn, index targetColumnIndex)
Multiplies the source column with the coefficiant before adding it to the target column....
Definition: base_matrix_with_column_compression.h:550
void add_to(const Cell_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:516
Base_matrix_with_column_compression(Column_settings *colSettings)
Constructs an empty matrix.
Definition: base_matrix_with_column_compression.h:359
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
void insert_column(const Container_type &column)
Inserts a new ordered column at the end of the matrix by copying the given range of Matrix::cell_rep_...
Definition: base_matrix_with_column_compression.h:432
void insert_boundary(const Boundary_type &boundary, dimension_type dim=-1)
Same as insert_column, only for interface purposes. The given dimension is ignored and not stored.
Definition: base_matrix_with_column_compression.h:439
typename Master_matrix::Cell_constructor Cell_constructor
Definition: base_matrix_with_column_compression.h:57
void multiply_target_and_add_to(const Cell_range_or_column_index &sourceColumn, const Field_element_type &coefficient, index targetColumnIndex)
Multiplies the target column with the coefficiant and then adds the source column to it....
Definition: base_matrix_with_column_compression.h:533
typename Master_matrix::Row_type Row_type
Definition: base_matrix_with_column_compression.h:56
friend void swap(Base_matrix_with_column_compression &matrix1, Base_matrix_with_column_compression &matrix2)
Swap operator.
Definition: base_matrix_with_column_compression.h:313
const Row_type & 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:492
typename Master_matrix::index index
Definition: base_matrix_with_column_compression.h:48
~Base_matrix_with_column_compression()
Destructor.
Definition: base_matrix_with_column_compression.h:425
typename Master_matrix::element_type Field_element_type
Definition: base_matrix_with_column_compression.h:54
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:509
Base_matrix_with_column_compression & operator=(const Base_matrix_with_column_compression &other)
Assign operator.
Definition: base_matrix_with_column_compression.h:583
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14