Loading...
Searching...
No Matches
vector_column.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 Inria
6 *
7 * Modification(s):
8 * - YYYY/MM Author: Description of the modification
9 */
10
17
18#ifndef PM_VECTOR_COLUMN_H
19#define PM_VECTOR_COLUMN_H
20
21#include <cstddef> // std::size_t
22#include <stdexcept> // std::invalid_argument
23#include <type_traits> // std::is_same_v
24#include <algorithm> // std::binary_search, std::sort
25#include <utility> // std::swap, std::move & std::exchange
26#include <unordered_set>
27#include <vector>
28
29#include <boost/range/iterator_range_core.hpp>
30#include <boost/iterator/indirect_iterator.hpp>
31
33
34namespace Gudhi {
35namespace persistence_matrix {
36
50template <class Master_matrix>
51class Vector_column : public Master_matrix::Row_access_option,
52 public Master_matrix::Column_dimension_option,
53 public Master_matrix::Chain_column_option
54{
55 public:
56 using Master = Master_matrix;
57 using Index = typename Master_matrix::Index;
58 using ID_index = typename Master_matrix::ID_index;
59 using Dimension = typename Master_matrix::Dimension;
60 using Field_element = typename Master_matrix::Element;
61 using Entry = typename Master_matrix::Matrix_entry;
62 using Column_settings = typename Master_matrix::Column_settings;
63
64 private:
65 using Field_operators = typename Master_matrix::Field_operators;
66 using Column_support = std::vector<Entry*>;
67 using Entry_constructor = typename Master_matrix::Entry_constructor;
68
69 class Non_zero_element_iterator
70 : public boost::iterator_facade<Non_zero_element_iterator, const Entry, boost::forward_traversal_tag>
71 {
72 public:
73 Non_zero_element_iterator(std::size_t curr,
74 Column_support const* column,
75 std::unordered_set<ID_index> const* erasedValues)
76 : curr_(curr), column_(column), erasedValues_(erasedValues)
77 {}
78
79 Non_zero_element_iterator(Column_support const* column)
80 : curr_(column->size()), column_(column), erasedValues_(nullptr)
81 {}
82
83 private:
84 friend class boost::iterator_core_access;
85
86 bool equal(Non_zero_element_iterator const& other) const
87 {
88 return curr_ == other.curr_ && column_ == other.column_;
89 }
90
91 const Entry& dereference() const { return *(*column_)[curr_]; }
92
93 void increment()
94 {
95 ++curr_;
96 while (curr_ < column_->size() &&
97 erasedValues_->find((*column_)[curr_]->get_row_index()) != erasedValues_->end()) {
98 ++curr_;
99 }
100 }
101
102 std::size_t curr_;
103 Column_support const* column_;
104 std::unordered_set<ID_index> const* erasedValues_;
105 };
106
107 public:
108 using iterator = boost::indirect_iterator<typename Column_support::iterator>;
109 using const_iterator = boost::indirect_iterator<typename Column_support::const_iterator>;
110 using reverse_iterator = boost::indirect_iterator<typename Column_support::reverse_iterator>;
111 using const_reverse_iterator = boost::indirect_iterator<typename Column_support::const_reverse_iterator>;
112 using Content_range = boost::iterator_range<Non_zero_element_iterator>;
113
114 Vector_column(Column_settings* colSettings = nullptr);
115 template <class Container = typename Master_matrix::Boundary>
116 Vector_column(const Container& nonZeroRowIndices, Column_settings* colSettings);
117 template <class Container = typename Master_matrix::Boundary, class Row_container>
118 Vector_column(Index columnIndex,
119 const Container& nonZeroRowIndices,
120 Row_container* rowContainer,
121 Column_settings* colSettings);
122 template <class Container = typename Master_matrix::Boundary,
123 class = std::enable_if_t<!std::is_arithmetic_v<Container> > >
124 Vector_column(const Container& nonZeroRowIndices, Dimension dimension, Column_settings* colSettings);
125 template <class Container = typename Master_matrix::Boundary,
126 class Row_container,
127 class = std::enable_if_t<!std::is_arithmetic_v<Container> > >
128 Vector_column(Index columnIndex,
129 const Container& nonZeroRowIndices,
130 Dimension dimension,
131 Row_container* rowContainer,
132 Column_settings* colSettings);
133 Vector_column(ID_index idx, Dimension dimension, Column_settings* colSettings);
134 Vector_column(ID_index idx, Field_element e, Dimension dimension, Column_settings* colSettings);
135 template <class Row_container>
136 Vector_column(Index columnIndex,
137 ID_index idx,
138 Dimension dimension,
139 Row_container* rowContainer,
140 Column_settings* colSettings);
141 template <class Row_container>
142 Vector_column(Index columnIndex,
143 ID_index idx,
144 Field_element e,
145 Dimension dimension,
146 Row_container* rowContainer,
147 Column_settings* colSettings);
148 Vector_column(const Vector_column& column, Column_settings* colSettings = nullptr);
149 template <class Row_container>
150 Vector_column(const Vector_column& column,
151 Index columnIndex,
152 Row_container* rowContainer,
153 Column_settings* colSettings = nullptr);
154 Vector_column(Vector_column&& column) noexcept;
155 ~Vector_column();
156
157 std::vector<Field_element> get_content(int columnLength = -1) const;
158 bool is_non_zero(ID_index rowIndex) const;
159 [[nodiscard]] bool is_empty() const;
160 [[nodiscard]] std::size_t size() const;
161
162 template <class Row_index_map>
163 void reorder(const Row_index_map& valueMap,
164 [[maybe_unused]] Index columnIndex = Master_matrix::template get_null_value<Index>());
165 void clear();
166 // do not clear an entry to 0 if the entry was already 0, otherwise size/is_empty will be wrong.
167 void clear(ID_index rowIndex);
168
169 ID_index get_pivot();
170 Field_element get_pivot_value();
171
172 iterator begin() noexcept;
173 const_iterator begin() const noexcept;
174 iterator end() noexcept;
175 const_iterator end() const noexcept;
176 reverse_iterator rbegin() noexcept;
177 const_reverse_iterator rbegin() const noexcept;
178 reverse_iterator rend() noexcept;
179 const_reverse_iterator rend() const noexcept;
180
181 Content_range get_non_zero_content_range() const;
182
183 template <class Entry_range>
184 Vector_column& operator+=(const Entry_range& column);
185 Vector_column& operator+=(Vector_column& column);
186
187 Vector_column& operator*=(const Field_element& v);
188
189 // this = v * this + column
190 template <class Entry_range>
191 Vector_column& multiply_target_and_add(const Field_element& val, const Entry_range& column);
192 Vector_column& multiply_target_and_add(const Field_element& val, Vector_column& column);
193 // this = this + column * v
194 template <class Entry_range>
195 Vector_column& multiply_source_and_add(const Entry_range& column, const Field_element& val);
196 Vector_column& multiply_source_and_add(Vector_column& column, const Field_element& val);
197
198 void push_back(const Entry& entry);
199
200 std::size_t compute_hash_value();
201
202 friend bool operator==(const Vector_column& c1, const Vector_column& c2)
203 {
204 if (&c1 == &c2) return true;
205 if (c1.erasedValues_.empty() && c2.erasedValues_.empty() && c1.column_.size() != c2.column_.size()) return false;
206
207 auto r1 = c1.get_non_zero_content_range();
208 auto r2 = c2.get_non_zero_content_range();
209 return std::equal(r1.begin(), r1.end(), r2.begin(), r2.end(), [](const Entry& e1, const Entry& e2) {
210 return e1.get_row_index() == e2.get_row_index() && e1.get_element() == e2.get_element();
211 });
212 }
213
214 friend bool operator<(const Vector_column& c1, const Vector_column& c2)
215 {
216 if (&c1 == &c2) return false;
217
218 auto r1 = c1.get_non_zero_content_range();
219 auto r2 = c2.get_non_zero_content_range();
220 return std::lexicographical_compare(
221 r1.begin(), r1.end(), r2.begin(), r2.end(), [](const Entry& e1, const Entry& e2) {
222 if (e1.get_row_index() != e2.get_row_index()) return e1.get_row_index() < e2.get_row_index();
223 if (e1.get_element() != e2.get_element()) return e1.get_element() < e2.get_element();
224 return false;
225 });
226 }
227
228 // Disabled with row access.
229 Vector_column& operator=(const Vector_column& other);
230 Vector_column& operator=(Vector_column&& other) noexcept;
231
232 friend void swap(Vector_column& col1, Vector_column& col2) noexcept
233 {
234 swap(static_cast<typename Master_matrix::Row_access_option&>(col1),
235 static_cast<typename Master_matrix::Row_access_option&>(col2));
236 swap(static_cast<typename Master_matrix::Column_dimension_option&>(col1),
237 static_cast<typename Master_matrix::Column_dimension_option&>(col2));
238 swap(static_cast<typename Master_matrix::Chain_column_option&>(col1),
239 static_cast<typename Master_matrix::Chain_column_option&>(col2));
240 col1.column_.swap(col2.column_);
241 col1.erasedValues_.swap(col2.erasedValues_);
242 std::swap(col1.operators_, col2.operators_);
243 std::swap(col1.entryPool_, col2.entryPool_);
244 }
245
246 private:
247 using RA_opt = typename Master_matrix::Row_access_option;
248 using Dim_opt = typename Master_matrix::Column_dimension_option;
249 using Chain_opt = typename Master_matrix::Chain_column_option;
250
251 Column_support column_;
252 // TODO: test other containers? Useless when clear(Index) is never called, how much is it worth it?
253 std::unordered_set<ID_index> erasedValues_;
254 Field_operators const* operators_;
255 Entry_constructor* entryPool_;
256
257 template <class Column, class Entry_iterator, typename F1, typename F2, typename F3, typename F4>
258 friend void _generic_merge_entry_to_column(Column& targetColumn,
259 Entry_iterator& itSource,
260 typename Column::Column_support::iterator& itTarget,
261 F1&& process_target,
262 F2&& process_source,
263 F3&& update_target1,
264 F4&& update_target2,
265 bool& pivotIsZeroed);
266
267 void _delete_entry(Entry* entry);
268 void _delete_entry(typename Column_support::iterator& it);
269 Entry* _insert_entry(Column_support& column, ID_index rowIndex, const Field_element& value);
270 void _update_entry(Index position, ID_index rowIndex, const Field_element& value);
271 template <class Entry_range>
272 bool _add(const Entry_range& column);
273 template <class Entry_range>
274 bool _multiply_target_and_add(const Field_element& val, const Entry_range& column);
275 template <class Entry_range>
276 bool _multiply_source_and_add(const Entry_range& column, const Field_element& val);
277 template <class Entry_range, typename F1, typename F2, typename F3, typename F4>
278 bool _generic_add(const Entry_range& column,
279 F1&& process_target,
280 F2&& process_source,
281 F3&& update_target1,
282 F4&& update_target2);
283 bool _is_lazy_erased(const typename Column_support::const_iterator& it) const;
284 bool _is_lazy_erased(ID_index rowIndex) const;
285};
286
287template <class Master_matrix>
288inline Vector_column<Master_matrix>::Vector_column(Column_settings* colSettings)
289 : RA_opt(),
290 Dim_opt(),
291 Chain_opt(),
292 operators_(Master_matrix::get_operator_ptr(colSettings)),
293 entryPool_(colSettings == nullptr ? nullptr : &(colSettings->entryConstructor))
294{}
295
296template <class Master_matrix>
297template <class Container>
298inline Vector_column<Master_matrix>::Vector_column(const Container& nonZeroRowIndices, Column_settings* colSettings)
299 : Vector_column(nonZeroRowIndices, nonZeroRowIndices.size() == 0 ? 0 : nonZeroRowIndices.size() - 1, colSettings)
300{
301 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
302 "Constructor not available for chain columns, please specify the dimension of the chain.");
303}
304
305template <class Master_matrix>
306template <class Container, class Row_container>
307inline Vector_column<Master_matrix>::Vector_column(Index columnIndex,
308 const Container& nonZeroRowIndices,
309 Row_container* rowContainer,
310 Column_settings* colSettings)
311 : Vector_column(columnIndex,
312 nonZeroRowIndices,
313 nonZeroRowIndices.size() == 0 ? 0 : nonZeroRowIndices.size() - 1,
314 rowContainer,
315 colSettings)
316{
317 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
318 "Constructor not available for chain columns, please specify the dimension of the chain.");
319}
320
321template <class Master_matrix>
322template <class Container, class>
323inline Vector_column<Master_matrix>::Vector_column(const Container& nonZeroRowIndices,
324 Dimension dimension,
325 Column_settings* colSettings)
326 : RA_opt(),
327 Dim_opt(dimension),
328 Chain_opt(nonZeroRowIndices.begin() == nonZeroRowIndices.end()
329 ? Master_matrix::template get_null_value<ID_index>()
330 : Master_matrix::get_row_index(*std::prev(nonZeroRowIndices.end()))),
331 column_(nonZeroRowIndices.size(), nullptr),
332 operators_(Master_matrix::get_operator_ptr(colSettings)),
333 entryPool_(&(colSettings->entryConstructor))
334{
335 Index i = 0;
336 for (const auto& id : nonZeroRowIndices) {
337 _update_entry(i++,
338 Master_matrix::get_row_index(id),
339 Master_matrix::get_coefficient_value(Master_matrix::get_element(id), operators_));
340 }
341}
342
343template <class Master_matrix>
344template <class Container, class Row_container, class>
345inline Vector_column<Master_matrix>::Vector_column(Index columnIndex,
346 const Container& nonZeroRowIndices,
347 Dimension dimension,
348 Row_container* rowContainer,
349 Column_settings* colSettings)
350 : RA_opt(columnIndex, rowContainer),
351 Dim_opt(dimension),
352 Chain_opt(nonZeroRowIndices.begin() == nonZeroRowIndices.end()
353 ? Master_matrix::template get_null_value<ID_index>()
354 : Master_matrix::get_row_index(*std::prev(nonZeroRowIndices.end()))),
355 column_(nonZeroRowIndices.size(), nullptr),
356 operators_(Master_matrix::get_operator_ptr(colSettings)),
357 entryPool_(&(colSettings->entryConstructor))
358{
359 Index i = 0;
360 for (const auto& id : nonZeroRowIndices) {
361 _update_entry(i++,
362 Master_matrix::get_row_index(id),
363 Master_matrix::get_coefficient_value(Master_matrix::get_element(id), operators_));
364 }
365}
366
367template <class Master_matrix>
368inline Vector_column<Master_matrix>::Vector_column(ID_index idx, Dimension dimension, Column_settings* colSettings)
369 : RA_opt(),
370 Dim_opt(dimension),
371 Chain_opt(idx),
372 column_(1, nullptr),
373 operators_(nullptr),
374 entryPool_(&(colSettings->entryConstructor))
375{
376 static_assert(Master_matrix::Option_list::is_z2,
377 "Constructor not available for Zp != Z2. Please specify the coefficient.");
378 _update_entry(0, idx, 1);
379}
380
381template <class Master_matrix>
382inline Vector_column<Master_matrix>::Vector_column(ID_index idx,
383 Field_element e,
384 Dimension dimension,
385 Column_settings* colSettings)
386 : RA_opt(),
387 Dim_opt(dimension),
388 Chain_opt(idx),
389 column_(1, nullptr),
390 operators_(&(colSettings->operators)),
391 entryPool_(&(colSettings->entryConstructor))
392{
393 static_assert(!Master_matrix::Option_list::is_z2,
394 "Constructor not available for Zp == Z2. Please do not specify any coefficient.");
395 _update_entry(0, idx, operators_->get_value(e));
396}
397
398template <class Master_matrix>
399template <class Row_container>
400inline Vector_column<Master_matrix>::Vector_column(Index columnIndex,
401 ID_index idx,
402 Dimension dimension,
403 Row_container* rowContainer,
404 Column_settings* colSettings)
405 : RA_opt(columnIndex, rowContainer),
406 Dim_opt(dimension),
407 Chain_opt(idx),
408 column_(1, nullptr),
409 operators_(nullptr),
410 entryPool_(&(colSettings->entryConstructor))
411{
412 static_assert(Master_matrix::Option_list::is_z2,
413 "Constructor not available for Zp != Z2. Please specify the coefficient.");
414 _update_entry(0, idx, 1);
415}
416
417template <class Master_matrix>
418template <class Row_container>
419inline Vector_column<Master_matrix>::Vector_column(Index columnIndex,
420 ID_index idx,
421 Field_element e,
422 Dimension dimension,
423 Row_container* rowContainer,
424 Column_settings* colSettings)
425 : RA_opt(columnIndex, rowContainer),
426 Dim_opt(dimension),
427 Chain_opt(idx),
428 column_(1, nullptr),
429 operators_(&(colSettings->operators)),
430 entryPool_(&(colSettings->entryConstructor))
431{
432 static_assert(!Master_matrix::Option_list::is_z2,
433 "Constructor not available for Zp == Z2. Please do not specify any coefficient.");
434 _update_entry(0, idx, operators_->get_value(e));
435}
436
437template <class Master_matrix>
438inline Vector_column<Master_matrix>::Vector_column(const Vector_column& column, Column_settings* colSettings)
439 : RA_opt(),
440 Dim_opt(static_cast<const Dim_opt&>(column)),
441 Chain_opt(static_cast<const Chain_opt&>(column)),
442 column_(column.column_.size(), nullptr),
443 erasedValues_(column.erasedValues_),
444 operators_(colSettings == nullptr ? column.operators_ : Master_matrix::get_operator_ptr(colSettings)),
445 entryPool_(colSettings == nullptr ? column.entryPool_ : &(colSettings->entryConstructor))
446{
447 static_assert(!Master_matrix::Option_list::has_row_access,
448 "Simple copy constructor not available when row access option enabled. Please specify the new column "
449 "index and the row container.");
450
451 Index i = 0;
452 for (const Entry* entry : column.column_) {
453 _update_entry(i++, entry->get_row_index(), entry->get_element());
454 }
455}
456
457template <class Master_matrix>
458template <class Row_container>
459inline Vector_column<Master_matrix>::Vector_column(const Vector_column& column,
460 Index columnIndex,
461 Row_container* rowContainer,
462 Column_settings* colSettings)
463 : RA_opt(columnIndex, rowContainer),
464 Dim_opt(static_cast<const Dim_opt&>(column)),
465 Chain_opt(static_cast<const Chain_opt&>(column)),
466 column_(column.column_.size(), nullptr),
467 erasedValues_(column.erasedValues_),
468 operators_(colSettings == nullptr ? column.operators_ : Master_matrix::get_operator_ptr(colSettings)),
469 entryPool_(colSettings == nullptr ? column.entryPool_ : &(colSettings->entryConstructor))
470{
471 Index i = 0;
472 for (const Entry* entry : column.column_) {
473 _update_entry(i++, entry->get_row_index(), entry->get_element());
474 }
475}
476
477template <class Master_matrix>
478inline Vector_column<Master_matrix>::Vector_column(Vector_column&& column) noexcept
479 : RA_opt(std::move(static_cast<RA_opt&>(column))),
480 Dim_opt(std::move(static_cast<Dim_opt&>(column))),
481 Chain_opt(std::move(static_cast<Chain_opt&>(column))),
482 column_(std::move(column.column_)),
483 erasedValues_(std::move(column.erasedValues_)),
484 operators_(std::exchange(column.operators_, nullptr)),
485 entryPool_(std::exchange(column.entryPool_, nullptr))
486{}
487
488template <class Master_matrix>
489inline Vector_column<Master_matrix>::~Vector_column()
490{
491 for (auto* entry : column_) {
492 _delete_entry(entry);
493 }
494}
495
496template <class Master_matrix>
497inline std::vector<typename Vector_column<Master_matrix>::Field_element> Vector_column<Master_matrix>::get_content(
498 int columnLength) const
499{
500 if (columnLength < 0 && column_.size() > 0)
501 columnLength = column_.back()->get_row_index() + 1;
502 else if (columnLength < 0)
503 return std::vector<Field_element>();
504
505 std::vector<Field_element> container(columnLength, 0);
506 auto r = get_non_zero_content_range();
507 for (auto it = r.begin(); it != r.end() && it->get_row_index() < static_cast<ID_index>(columnLength); ++it) {
508 container[it->get_row_index()] = Master_matrix::get_element(*it);
509 }
510 return container;
511}
512
513template <class Master_matrix>
514inline bool Vector_column<Master_matrix>::is_non_zero(ID_index rowIndex) const
515{
516 if (_is_lazy_erased(rowIndex)) return false;
517
518 Entry entry(rowIndex);
519 return std::binary_search(column_.begin(), column_.end(), &entry, [](const Entry* a, const Entry* b) {
520 return a->get_row_index() < b->get_row_index();
521 });
522}
523
524template <class Master_matrix>
525inline bool Vector_column<Master_matrix>::is_empty() const
526{
527 if constexpr (!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type) {
528 return column_.size() == erasedValues_.size(); // assumes that erasedValues is always a subset of column_, which is
529 // wrong if someone cleared an non existing value...
530 } else {
531 return column_.empty();
532 }
533}
534
535template <class Master_matrix>
536inline std::size_t Vector_column<Master_matrix>::size() const
537{
538 if constexpr (!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type) {
539 return column_.size() - erasedValues_.size(); // assumes that erasedValues is always a subset of column_, which is
540 // wrong if someone cleared an non existing value...
541 } else {
542 return column_.size();
543 }
544}
545
546template <class Master_matrix>
547template <class Row_index_map>
548inline void Vector_column<Master_matrix>::reorder(const Row_index_map& valueMap, [[maybe_unused]] Index columnIndex)
549{
550 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
551 "Method not available for chain columns.");
552
553 if (erasedValues_.empty()) { // to avoid useless push_backs.
554 for (Entry* entry : column_) {
555 if constexpr (Master_matrix::Option_list::has_row_access) {
556 RA_opt::unlink(entry);
557 if (columnIndex != Master_matrix::template get_null_value<Index>()) entry->set_column_index(columnIndex);
558 }
559 entry->set_row_index(valueMap.at(entry->get_row_index()));
560 if constexpr (Master_matrix::Option_list::has_intrusive_rows && Master_matrix::Option_list::has_row_access)
561 RA_opt::insert_entry(entry->get_row_index(), entry);
562 }
563
564 // all entries have to be deleted first, to avoid problem with insertion when row is a set
565 if constexpr (!Master_matrix::Option_list::has_intrusive_rows && Master_matrix::Option_list::has_row_access) {
566 for (Entry* entry : column_) {
567 RA_opt::insert_entry(entry->get_row_index(), entry);
568 }
569 }
570
571 std::sort(column_.begin(), column_.end(), [](const Entry* c1, const Entry* c2) { return *c1 < *c2; });
572 } else {
573 Column_support newColumn;
574 for (Entry* entry : column_) {
575 if (!_is_lazy_erased(entry->get_row_index())) {
576 if constexpr (Master_matrix::Option_list::has_row_access) {
577 RA_opt::unlink(entry);
578 if (columnIndex != Master_matrix::template get_null_value<Index>()) entry->set_column_index(columnIndex);
579 }
580 entry->set_row_index(valueMap.at(entry->get_row_index()));
581 newColumn.push_back(entry);
582 if constexpr (Master_matrix::Option_list::has_intrusive_rows && Master_matrix::Option_list::has_row_access)
583 RA_opt::insert_entry(entry->get_row_index(), entry);
584 } else {
585 _delete_entry(entry);
586 }
587 }
588 // all entries have to be deleted first, to avoid problem with insertion when row is a set
589 if constexpr (!Master_matrix::Option_list::has_intrusive_rows && Master_matrix::Option_list::has_row_access) {
590 for (Entry* entry : column_) {
591 RA_opt::insert_entry(entry->get_row_index(), entry);
592 }
593 }
594 std::sort(newColumn.begin(), newColumn.end(), [](const Entry* c1, const Entry* c2) { return *c1 < *c2; });
595 erasedValues_.clear();
596 column_.swap(newColumn);
597 }
598}
599
600template <class Master_matrix>
601inline void Vector_column<Master_matrix>::clear()
602{
603 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
604 "Method not available for chain columns as a base element should not be empty.");
605
606 for (auto* entry : column_) {
607 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(entry);
608 entryPool_->destroy(entry);
609 }
610
611 column_.clear();
612 erasedValues_.clear();
613}
614
615template <class Master_matrix>
616inline void Vector_column<Master_matrix>::clear(ID_index rowIndex)
617{
618 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
619 "Method not available for chain columns.");
620
621 erasedValues_.insert(rowIndex);
622}
623
624template <class Master_matrix>
625inline typename Vector_column<Master_matrix>::ID_index Vector_column<Master_matrix>::get_pivot()
626{
627 static_assert(Master_matrix::isNonBasic,
628 "Method not available for base columns."); // could technically be, but is the notion useful then?
629
630 if constexpr (Master_matrix::Option_list::is_of_boundary_type) {
631 if (column_.empty()) return Master_matrix::template get_null_value<ID_index>();
632 if (erasedValues_.empty()) return column_.back()->get_row_index();
633
634 auto it = erasedValues_.find(column_.back()->get_row_index());
635 while (!column_.empty() && it != erasedValues_.end()) {
636 erasedValues_.erase(it);
637 _delete_entry(column_.back());
638 column_.pop_back();
639 if (!column_.empty()) it = erasedValues_.find(column_.back()->get_row_index());
640 }
641
642 if (column_.empty()) return Master_matrix::template get_null_value<ID_index>();
643 return column_.back()->get_row_index();
644 } else {
645 return Chain_opt::_get_pivot();
646 }
647}
648
649template <class Master_matrix>
650inline typename Vector_column<Master_matrix>::Field_element Vector_column<Master_matrix>::get_pivot_value()
651{
652 static_assert(Master_matrix::isNonBasic,
653 "Method not available for base columns."); // could technically be, but is the notion useful then?
654
655 if constexpr (Master_matrix::Option_list::is_z2) {
656 return 1;
657 } else {
658 if constexpr (Master_matrix::Option_list::is_of_boundary_type) {
659 if (column_.empty()) return 0;
660 if (erasedValues_.empty()) return column_.back()->get_element();
661
662 auto it = erasedValues_.find(column_.back()->get_row_index());
663 while (!column_.empty() && it != erasedValues_.end()) {
664 erasedValues_.erase(it);
665 _delete_entry(column_.back());
666 column_.pop_back();
667 if (!column_.empty()) it = erasedValues_.find(column_.back()->get_row_index());
668 }
669
670 if (column_.empty()) return 0;
671 return column_.back()->get_element();
672 } else {
673 if (Chain_opt::_get_pivot() == Master_matrix::template get_null_value<ID_index>()) return Field_element();
674 for (const Entry* entry : column_) {
675 if (entry->get_row_index() == Chain_opt::_get_pivot()) return entry->get_element();
676 }
677 return Field_element(); // should never happen if chain column is used properly
678 }
679 }
680}
681
682template <class Master_matrix>
683inline typename Vector_column<Master_matrix>::iterator Vector_column<Master_matrix>::begin() noexcept
684{
685 return column_.begin();
686}
687
688template <class Master_matrix>
689inline typename Vector_column<Master_matrix>::const_iterator Vector_column<Master_matrix>::begin() const noexcept
690{
691 return column_.begin();
692}
693
694template <class Master_matrix>
695inline typename Vector_column<Master_matrix>::iterator Vector_column<Master_matrix>::end() noexcept
696{
697 return column_.end();
698}
699
700template <class Master_matrix>
701inline typename Vector_column<Master_matrix>::const_iterator Vector_column<Master_matrix>::end() const noexcept
702{
703 return column_.end();
704}
705
706template <class Master_matrix>
707inline typename Vector_column<Master_matrix>::reverse_iterator Vector_column<Master_matrix>::rbegin() noexcept
708{
709 return column_.rbegin();
710}
711
712template <class Master_matrix>
713inline typename Vector_column<Master_matrix>::const_reverse_iterator Vector_column<Master_matrix>::rbegin()
714 const noexcept
715{
716 return column_.rbegin();
717}
718
719template <class Master_matrix>
720inline typename Vector_column<Master_matrix>::reverse_iterator Vector_column<Master_matrix>::rend() noexcept
721{
722 return column_.rend();
723}
724
725template <class Master_matrix>
726inline typename Vector_column<Master_matrix>::const_reverse_iterator Vector_column<Master_matrix>::rend() const noexcept
727{
728 return column_.rend();
729}
730
731template <class Master_matrix>
732inline typename Vector_column<Master_matrix>::Content_range Vector_column<Master_matrix>::get_non_zero_content_range()
733 const
734{
735 return Content_range(Non_zero_element_iterator(0, &column_, &erasedValues_), Non_zero_element_iterator(&column_));
736}
737
738template <class Master_matrix>
739template <class Entry_range>
740inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::operator+=(const Entry_range& column)
741{
742 static_assert((!Master_matrix::isNonBasic || std::is_same_v<Entry_range, Vector_column>),
743 "For boundary columns, the range has to be a column of same type to help ensure the validity of the "
744 "base element."); // could be removed, if we give the responsibility to the user.
745 static_assert((!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type),
746 "For chain columns, the given column cannot be constant.");
747
748 _add(column);
749
750 return *this;
751}
752
753template <class Master_matrix>
754inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::operator+=(Vector_column& column)
755{
756 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
757 // assumes that the addition never zeros out this column.
758 if (_add(column)) {
759 Chain_opt::_swap_pivots(column);
760 Dim_opt::_swap_dimension(column);
761 }
762 } else {
763 _add(column);
764 }
765
766 return *this;
767}
768
769template <class Master_matrix>
770inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::operator*=(const Field_element& v)
771{
772 Field_element val = Master_matrix::get_coefficient_value(v, operators_);
773
774 if (val == Field_operators::get_additive_identity()) {
775 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
776 throw std::invalid_argument("A chain column should not be multiplied by 0.");
777 } else {
778 clear();
779 }
780 return *this;
781 }
782
783 if (val == Field_operators::get_multiplicative_identity()) return *this;
784
785 // multiply_inplace needs a non-const reference to element, so even if Z2 never reaches here, it won't compile
786 // without the constexpr, as we are not storing a dummy value just for this purpose.
787 if constexpr (!Master_matrix::Option_list::is_z2) {
788 for (Entry* entry : column_) {
789 operators_->multiply_inplace(entry->get_element(), val);
790 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::update_entry(*entry);
791 }
792 }
793
794 return *this;
795}
796
797template <class Master_matrix>
798template <class Entry_range>
799inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::multiply_target_and_add(const Field_element& val,
800 const Entry_range& column)
801{
802 static_assert((!Master_matrix::isNonBasic || std::is_same_v<Entry_range, Vector_column>),
803 "For boundary columns, the range has to be a column of same type to help ensure the validity of the "
804 "base element."); // could be removed, if we give the responsibility to the user.
805 static_assert((!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type),
806 "For chain columns, the given column cannot be constant.");
807
808 _multiply_target_and_add(Master_matrix::get_coefficient_value(val, operators_), column);
809
810 return *this;
811}
812
813template <class Master_matrix>
814inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::multiply_target_and_add(const Field_element& val,
815 Vector_column& column)
816{
817 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
818 // assumes that the addition never zeros out this column.
819 if (_multiply_target_and_add(Master_matrix::get_coefficient_value(val, operators_), column)) {
820 Chain_opt::_swap_pivots(column);
821 Dim_opt::_swap_dimension(column);
822 }
823 } else {
824 _multiply_target_and_add(Master_matrix::get_coefficient_value(val, operators_), column);
825 }
826
827 return *this;
828}
829
830template <class Master_matrix>
831template <class Entry_range>
832inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::multiply_source_and_add(const Entry_range& column,
833 const Field_element& val)
834{
835 static_assert((!Master_matrix::isNonBasic || std::is_same_v<Entry_range, Vector_column>),
836 "For boundary columns, the range has to be a column of same type to help ensure the validity of the "
837 "base element."); // could be removed, if we give the responsibility to the user.
838 static_assert((!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type),
839 "For chain columns, the given column cannot be constant.");
840
841 _multiply_source_and_add(column, Master_matrix::get_coefficient_value(val, operators_));
842
843 return *this;
844}
845
846template <class Master_matrix>
847inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::multiply_source_and_add(Vector_column& column,
848 const Field_element& val)
849{
850 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
851 // assumes that the addition never zeros out this column.
852 if (_multiply_source_and_add(column, Master_matrix::get_coefficient_value(val, operators_))) {
853 Chain_opt::_swap_pivots(column);
854 Dim_opt::_swap_dimension(column);
855 }
856 } else {
857 _multiply_source_and_add(column, Master_matrix::get_coefficient_value(val, operators_));
858 }
859
860 return *this;
861}
862
863template <class Master_matrix>
864inline void Vector_column<Master_matrix>::push_back(const Entry& entry)
865{
866 static_assert(Master_matrix::Option_list::is_of_boundary_type, "`push_back` is not available for Chain matrices.");
867
868 GUDHI_CHECK(entry.get_row_index() > get_pivot(),
869 std::invalid_argument("The new row index has to be higher than the current pivot."));
870
871 _insert_entry(column_, entry.get_row_index(), entry.get_element());
872}
873
874template <class Master_matrix>
875inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::operator=(const Vector_column& other)
876{
877 static_assert(!Master_matrix::Option_list::has_row_access, "= assignment not enabled with row access option.");
878
879 // to avoid destroying the column when building from it-self in the for loop below...
880 if (this == &other) return *this;
881
882 Dim_opt::operator=(other);
883 Chain_opt::operator=(other);
884
885 auto tmpPool = entryPool_;
886 entryPool_ = other.entryPool_;
887
888 while (column_.size() > other.column_.size()) {
889 if (column_.back() != nullptr) {
890 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(column_.back());
891 tmpPool->destroy(column_.back());
892 }
893 column_.pop_back();
894 }
895
896 column_.resize(other.column_.size(), nullptr);
897 Index i = 0;
898 for (const Entry* entry : other.column_) {
899 if (column_[i] != nullptr) {
900 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(column_[i]);
901 tmpPool->destroy(column_[i]);
902 }
903 _update_entry(i++, entry->get_row_index(), entry->get_element());
904 }
905 erasedValues_ = other.erasedValues_;
906 operators_ = other.operators_;
907
908 return *this;
909}
910
911template <class Master_matrix>
912inline Vector_column<Master_matrix>& Vector_column<Master_matrix>::operator=(Vector_column&& other) noexcept
913{
914 static_assert(!Master_matrix::Option_list::has_row_access, "= assignment not enabled with row access option.");
915
916 // to avoid destroying the column before building from it-self...
917 if (&column_ == &(other.column_)) return *this;
918
919 Dim_opt::operator=(std::move(other));
920 Chain_opt::operator=(std::move(other));
921
922 for (auto* entry : column_) {
923 if (entry != nullptr) _delete_entry(entry);
924 }
925
926 column_ = std::move(other.column_);
927 erasedValues_ = std::move(other.erasedValues_);
928 operators_ = std::exchange(other.operators_, nullptr);
929 entryPool_ = std::exchange(other.entryPool_, nullptr);
930
931 return *this;
932}
933
934template <class Master_matrix>
935inline std::size_t Vector_column<Master_matrix>::compute_hash_value()
936{
937 std::size_t seed = 0;
938 for (const Entry& entry : get_non_zero_content_range()) {
939 seed ^= std::hash<unsigned int>()(entry.get_row_index() * static_cast<unsigned int>(entry.get_element())) +
940 0x9e3779b9 + (seed << 6) + (seed >> 2);
941 }
942 return seed;
943}
944
945template <class Master_matrix>
946inline void Vector_column<Master_matrix>::_delete_entry(Entry* entry)
947{
948 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(entry);
949 entryPool_->destroy(entry);
950}
951
952template <class Master_matrix>
953inline void Vector_column<Master_matrix>::_delete_entry(typename Column_support::iterator& it)
954{
955 _delete_entry(*it);
956 ++it;
957}
958
959template <class Master_matrix>
960inline typename Vector_column<Master_matrix>::Entry*
961Vector_column<Master_matrix>::_insert_entry(Column_support& column, ID_index rowIndex, const Field_element& value)
962{
963 Entry* newEntry;
964 if constexpr (Master_matrix::Option_list::has_row_access) {
965 newEntry = entryPool_->construct(RA_opt::get_column_index(), rowIndex);
966 } else {
967 newEntry = entryPool_->construct(rowIndex);
968 }
969 newEntry->set_element(value);
970 column.push_back(newEntry);
971 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::insert_entry(rowIndex, newEntry);
972 return newEntry;
973}
974
975template <class Master_matrix>
976inline void Vector_column<Master_matrix>::_update_entry(Index position, ID_index rowIndex, const Field_element& value)
977{
978 if constexpr (Master_matrix::Option_list::has_row_access) {
979 column_[position] = entryPool_->construct(RA_opt::get_column_index(), rowIndex);
980 } else {
981 column_[position] = entryPool_->construct(rowIndex);
982 }
983 column_[position]->set_element(value);
984 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::insert_entry(rowIndex, column_[position]);
985}
986
987template <class Master_matrix>
988template <class Entry_range>
989inline bool Vector_column<Master_matrix>::_add(const Entry_range& column)
990{
991 if (column.begin() == column.end()) return false;
992 if (column_.empty()) { // chain should never enter here.
993 auto get_range = [](const Entry_range& column) -> decltype(auto) {
994 if constexpr (std::is_same_v<Entry_range, Vector_column<Master_matrix> >) {
995 return column.get_non_zero_content_range();
996 } else {
997 return column;
998 }
999 };
1000 column_.resize(column.size());
1001 Index i = 0;
1002 for (const Entry& entry : get_range(column)) {
1003 _update_entry(i++, entry.get_row_index(), entry.get_element());
1004 }
1005 column_.resize(i); // i <= column.size(), so it should not trigger a reallocation
1006 erasedValues_.clear();
1007 return true;
1008 }
1009
1010 Column_support newColumn;
1011 newColumn.reserve(column_.size() + column.size()); // safe upper bound
1012
1013 auto pivotIsZeroed = _generic_add(
1014 column,
1015 [&](Entry* entryTarget) { newColumn.push_back(entryTarget); },
1016 [&](typename Entry_range::const_iterator& itSource, const typename Column_support::iterator&) {
1017 _insert_entry(newColumn, itSource->get_row_index(), itSource->get_element());
1018 },
1019 [&](Field_element& targetElement, typename Entry_range::const_iterator& itSource) {
1020 if constexpr (!Master_matrix::Option_list::is_z2)
1021 operators_->add_inplace(targetElement, itSource->get_element());
1022 },
1023 [&](Entry* entryTarget) { newColumn.push_back(entryTarget); });
1024
1025 column_.swap(newColumn);
1026
1027 return pivotIsZeroed;
1028}
1029
1030template <class Master_matrix>
1031template <class Entry_range>
1032inline bool Vector_column<Master_matrix>::_multiply_target_and_add(const Field_element& val, const Entry_range& column)
1033{
1034 if (val == Field_operators::get_additive_identity()) {
1035 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
1036 throw std::invalid_argument("A chain column should not be multiplied by 0.");
1037 // this would not only mess up the base, but also the pivots stored.
1038 } else {
1039 clear();
1040 }
1041 }
1042
1043 if (column_.empty() || val == Field_operators::get_multiplicative_identity()) {
1044 return _add(column);
1045 }
1046
1047 // multiply_inplace needs a non-const reference to element, so even if Z2 never reaches here, it won't compile
1048 // without the constexpr, as we are not storing a dummy value just for this purpose.
1049 if constexpr (!Master_matrix::Option_list::is_z2) {
1050 Column_support newColumn;
1051 newColumn.reserve(column_.size() + column.size()); // safe upper bound
1052
1053 auto pivotIsZeroed = _generic_add(
1054 column,
1055 [&](Entry* entryTarget) {
1056 operators_->multiply_inplace(entryTarget->get_element(), val);
1057 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::update_entry(*entryTarget);
1058 newColumn.push_back(entryTarget);
1059 },
1060 [&](typename Entry_range::const_iterator& itSource, const typename Column_support::iterator&) {
1061 _insert_entry(newColumn, itSource->get_row_index(), itSource->get_element());
1062 },
1063 [&](Field_element& targetElement, typename Entry_range::const_iterator& itSource) {
1064 operators_->multiply_and_add_inplace_front(targetElement, val, itSource->get_element());
1065 },
1066 [&](Entry* entryTarget) { newColumn.push_back(entryTarget); });
1067
1068 column_.swap(newColumn);
1069
1070 return pivotIsZeroed;
1071 } else {
1072 return false; // we should never arrive here, just to suppress the warning
1073 }
1074}
1075
1076template <class Master_matrix>
1077template <class Entry_range>
1078inline bool Vector_column<Master_matrix>::_multiply_source_and_add(const Entry_range& column, const Field_element& val)
1079{
1080 if (val == Field_operators::get_additive_identity() || column.begin() == column.end()) {
1081 return false;
1082 }
1083
1084 if (val == Field_operators::get_multiplicative_identity()) {
1085 return _add(column);
1086 }
1087
1088 // multiply_inplace needs a non-const reference to element, so even if Z2 never reaches here, it won't compile
1089 // without the constexpr, as we are not storing a dummy value just for this purpose.
1090 if constexpr (!Master_matrix::Option_list::is_z2) {
1091 Column_support newColumn;
1092 newColumn.reserve(column_.size() + column.size()); // safe upper bound
1093
1094 auto pivotIsZeroed = _generic_add(
1095 column,
1096 [&](Entry* entryTarget) { newColumn.push_back(entryTarget); },
1097 [&](typename Entry_range::const_iterator& itSource, const typename Column_support::iterator&) {
1098 Entry* newEntry = _insert_entry(newColumn, itSource->get_row_index(), itSource->get_element());
1099 operators_->multiply_inplace(newEntry->get_element(), val);
1100 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::update_entry(*newEntry);
1101 },
1102 [&](Field_element& targetElement, typename Entry_range::const_iterator& itSource) {
1103 operators_->multiply_and_add_inplace_back(itSource->get_element(), val, targetElement);
1104 },
1105 [&](Entry* entryTarget) { newColumn.push_back(entryTarget); });
1106
1107 column_.swap(newColumn);
1108
1109 return pivotIsZeroed;
1110 } else {
1111 return false; // we should never arrive here, just to suppress the warning
1112 }
1113}
1114
1115template <class Master_matrix>
1116template <class Entry_range, typename F1, typename F2, typename F3, typename F4>
1117inline bool Vector_column<Master_matrix>::_generic_add(const Entry_range& column,
1118 F1&& process_target,
1119 F2&& process_source,
1120 F3&& update_target1,
1121 F4&& update_target2)
1122{
1123 auto updateTargetIterator = [&](typename Column_support::iterator& itTarget) {
1124 while (_is_lazy_erased(itTarget)) {
1125 _delete_entry(*itTarget);
1126 ++itTarget;
1127 }
1128 };
1129 auto updateSourceIterator = [&](typename Entry_range::const_iterator& itSource) {
1130 if constexpr (std::is_same_v<Entry_range, Vector_column<Master_matrix> >) {
1131 while (itSource != column.end() && column._is_lazy_erased(itSource->get_row_index())) ++itSource;
1132 }
1133 };
1134
1135 bool pivotIsZeroed = false;
1136
1137 auto itTarget = column_.begin();
1138 auto itSource = column.begin();
1139 while (itTarget != column_.end() && itSource != column.end()) {
1140 updateTargetIterator(itTarget);
1141 updateSourceIterator(itSource);
1142 if (itTarget == column_.end() || itSource == column.end()) break;
1143
1144 _generic_merge_entry_to_column(*this,
1145 itSource,
1146 itTarget,
1147 std::forward<F1>(process_target),
1148 std::forward<F2>(process_source),
1149 std::forward<F3>(update_target1),
1150 std::forward<F4>(update_target2),
1151 pivotIsZeroed);
1152 }
1153
1154 while (itSource != column.end()) {
1155 updateSourceIterator(itSource);
1156 if (itSource == column.end()) break;
1157
1158 std::forward<F2>(process_source)(itSource, column_.end());
1159 ++itSource;
1160 }
1161
1162 while (itTarget != column_.end()) {
1163 updateTargetIterator(itTarget);
1164 if (itTarget == column_.end()) break;
1165
1166 std::forward<F1>(process_target)(*itTarget);
1167 ++itTarget;
1168 }
1169
1170 erasedValues_.clear();
1171
1172 return pivotIsZeroed;
1173}
1174
1175template <class Master_matrix>
1176inline bool Vector_column<Master_matrix>::_is_lazy_erased(const typename Column_support::const_iterator& it) const
1177{
1178 if constexpr (!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type) {
1179 return it != column_.end() && erasedValues_.find((*it)->get_row_index()) != erasedValues_.end();
1180 } else {
1181 return false;
1182 }
1183}
1184
1185template <class Master_matrix>
1186inline bool Vector_column<Master_matrix>::_is_lazy_erased(ID_index rowIndex) const
1187{
1188 if constexpr (!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type) {
1189 return erasedValues_.find(rowIndex) != erasedValues_.end();
1190 } else {
1191 return false;
1192 }
1193}
1194
1195} // namespace persistence_matrix
1196} // namespace Gudhi
1197
1206template <class Master_matrix>
1207struct std::hash<Gudhi::persistence_matrix::Vector_column<Master_matrix> > {
1208 std::size_t operator()(const Gudhi::persistence_matrix::Vector_column<Master_matrix>& column) const
1209 {
1210 return column.compute_hash_value();
1211 }
1212};
1213
1214#endif // PM_VECTOR_COLUMN_H
Matrix entry class. Stores by default only the row index it belongs to, but can also store its column...
Definition entry_types.h:163
Column class following the PersistenceMatrixColumn concept.
Definition vector_column.h:54
Contains helper methods for column addition and column hasher.
Persistence matrix namespace.
Definition FieldOperators.h:18
Gudhi namespace.
Definition SimplicialComplexForAlpha.h:14