Loading...
Searching...
No Matches
unordered_set_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_UNORDERED_SET_COLUMN_H
19#define PM_UNORDERED_SET_COLUMN_H
20
21#include <stdexcept>
22#include <type_traits>
23#include <algorithm> // std::lexicographical_compare
24#include <utility> //std::swap, std::move & std::exchange
25#include <set>
26#include <vector>
27
28#include <boost/iterator/indirect_iterator.hpp>
29#if BOOST_VERSION >= 108100
30#include <boost/unordered/unordered_flat_set.hpp>
31#else
32#include <unordered_set>
33#endif
34
36
37namespace Gudhi {
38namespace persistence_matrix {
39
40// For unordered_set container. Outside of Unordered_set_column because of a msvc bug who can't compile properly
41// unordered_flat_set if the hash method is nested.
42template <class Entry>
43struct EntryPointerHash {
44 size_t operator()(const Entry* c) const { return std::hash<Entry>()(*c); }
45};
46
47template <class Entry>
48struct EntryPointerEq {
49 bool operator()(const Entry* c1, const Entry* c2) const { return *c1 == *c2; }
50};
51
64template <class Master_matrix>
65class Unordered_set_column : public Master_matrix::Row_access_option,
66 public Master_matrix::Column_dimension_option,
67 public Master_matrix::Chain_column_option
68{
69 public:
70 using Master = Master_matrix;
71 using Index = typename Master_matrix::Index;
72 using ID_index = typename Master_matrix::ID_index;
73 using Dimension = typename Master_matrix::Dimension;
74 using Field_element = typename Master_matrix::Element;
75 using Entry = typename Master_matrix::Matrix_entry;
76 using Column_settings = typename Master_matrix::Column_settings;
77
78 private:
79 using Field_operators = typename Master_matrix::Field_operators;
80 using Entry_constructor = typename Master_matrix::Entry_constructor;
81
82 struct EntryPointerComp {
83 bool operator()(const Entry* c1, const Entry* c2) const { return *c1 < *c2; }
84 };
85
86#if BOOST_VERSION >= 108100
87 using Column_support = boost::unordered_flat_set<Entry*, EntryPointerHash<Entry>, EntryPointerEq<Entry>>;
88#else
89 using Column_support = std::unordered_set<Entry*, EntryPointerHash<Entry>, EntryPointerEq<Entry>>;
90#endif
91
92 public:
93 using iterator = boost::indirect_iterator<typename Column_support::iterator>;
94 using const_iterator = boost::indirect_iterator<typename Column_support::const_iterator>;
95 using Content_range = std::vector<Entry>;
96
97 Unordered_set_column(Column_settings* colSettings = nullptr);
98 template <class Container = typename Master_matrix::Boundary>
99 Unordered_set_column(const Container& nonZeroRowIndices, Column_settings* colSettings);
100 template <class Container = typename Master_matrix::Boundary, class Row_container>
101 Unordered_set_column(Index columnIndex,
102 const Container& nonZeroRowIndices,
103 Row_container* rowContainer,
104 Column_settings* colSettings);
105 template <class Container = typename Master_matrix::Boundary,
106 class = std::enable_if_t<!std::is_arithmetic_v<Container>>>
107 Unordered_set_column(const Container& nonZeroRowIndices, Dimension dimension, Column_settings* colSettings);
108 template <class Container = typename Master_matrix::Boundary,
109 class Row_container,
110 class = std::enable_if_t<!std::is_arithmetic_v<Container>>>
111 Unordered_set_column(Index columnIndex,
112 const Container& nonZeroRowIndices,
113 Dimension dimension,
114 Row_container* rowContainer,
115 Column_settings* colSettings);
116 Unordered_set_column(ID_index idx, Dimension dimension, Column_settings* colSettings);
117 Unordered_set_column(ID_index idx,
118 Field_element e,
119 Dimension dimension,
120 Column_settings* colSettings);
121 template <class Row_container>
122 Unordered_set_column(Index columnIndex,
123 ID_index idx,
124 Dimension dimension,
125 Row_container* rowContainer,
126 Column_settings* colSettings);
127 template <class Row_container>
128 Unordered_set_column(Index columnIndex,
129 ID_index idx,
130 Field_element e,
131 Dimension dimension,
132 Row_container* rowContainer,
133 Column_settings* colSettings);
134 Unordered_set_column(const Unordered_set_column& column, Column_settings* colSettings = nullptr);
135 template <class Row_container>
136 Unordered_set_column(const Unordered_set_column& column,
137 Index columnIndex,
138 Row_container* rowContainer,
139 Column_settings* colSettings = nullptr);
140 Unordered_set_column(Unordered_set_column&& column) noexcept;
141 ~Unordered_set_column();
142
143 std::vector<Field_element> get_content(int columnLength = -1) const;
144 bool is_non_zero(ID_index rowIndex) const;
145 [[nodiscard]] bool is_empty() const;
146 [[nodiscard]] std::size_t size() const;
147
148 template <class Row_index_map>
149 void reorder(const Row_index_map& valueMap,
150 [[maybe_unused]] Index columnIndex = Master_matrix::template get_null_value<Index>());
151 void clear();
152 void clear(ID_index rowIndex);
153
154 ID_index get_pivot() const;
155 Field_element get_pivot_value() const;
156
157 iterator begin() noexcept;
158 const_iterator begin() const noexcept;
159 iterator end() noexcept;
160 const_iterator end() const noexcept;
161
162 Content_range get_non_zero_content_range() const;
163
164 template <class Entry_range>
165 Unordered_set_column& operator+=(const Entry_range& column);
166 Unordered_set_column& operator+=(Unordered_set_column& column);
167
168 Unordered_set_column& operator*=(const Field_element& v);
169
170 // this = v * this + column
171 template <class Entry_range>
172 Unordered_set_column& multiply_target_and_add(const Field_element& val, const Entry_range& column);
173 Unordered_set_column& multiply_target_and_add(const Field_element& val, Unordered_set_column& column);
174 // this = this + column * v
175 template <class Entry_range>
176 Unordered_set_column& multiply_source_and_add(const Entry_range& column, const Field_element& val);
177 Unordered_set_column& multiply_source_and_add(Unordered_set_column& column, const Field_element& val);
178
179 void push_back(const Entry& entry);
180
181 friend bool operator==(const Unordered_set_column& c1, const Unordered_set_column& c2)
182 {
183 if (&c1 == &c2) return true;
184 if (c1.column_.size() != c2.column_.size()) return false;
185
186 for (Entry* entry : c1.column_) {
187 auto it = c2.column_.find(entry);
188 if (it == c2.column_.end()) return false;
189 if (Master_matrix::get_element(**it) != Master_matrix::get_element(*entry)) return false;
190 }
191 return true;
192 }
193
194 friend bool operator<(const Unordered_set_column& c1, const Unordered_set_column& c2)
195 {
196 if (&c1 == &c2) return false;
197
198 auto comp = [](const Entry* n1, const Entry* n2) -> bool {
199 Index r1 = Master_matrix::get_row_index(*n1);
200 Index r2 = Master_matrix::get_row_index(*n2);
201 Field_element e1 = Master_matrix::get_element(*n1);
202 Field_element e2 = Master_matrix::get_element(*n2);
203
204 if (r1 != r2) return r1 < r2;
205 if (e1 != e2) return e1 < e2;
206
207 return false;
208 };
209
210 std::set<Entry*, decltype(comp)> entries1(comp), entries2(comp);
211 entries1.insert(c1.column_.begin(), c1.column_.end());
212 entries2.insert(c2.column_.begin(), c2.column_.end());
213
214 return std::lexicographical_compare(entries1.begin(), entries1.end(), entries2.begin(), entries2.end(), comp);
215 }
216
217 // Disabled with row access.
218 Unordered_set_column& operator=(const Unordered_set_column& other);
219 Unordered_set_column& operator=(Unordered_set_column&& other) noexcept;
220
221 friend void swap(Unordered_set_column& col1, Unordered_set_column& col2) noexcept
222 {
223 swap(static_cast<typename Master_matrix::Row_access_option&>(col1),
224 static_cast<typename Master_matrix::Row_access_option&>(col2));
225 swap(static_cast<typename Master_matrix::Column_dimension_option&>(col1),
226 static_cast<typename Master_matrix::Column_dimension_option&>(col2));
227 swap(static_cast<typename Master_matrix::Chain_column_option&>(col1),
228 static_cast<typename Master_matrix::Chain_column_option&>(col2));
229 col1.column_.swap(col2.column_);
230 std::swap(col1.operators_, col2.operators_);
231 std::swap(col1.entryPool_, col2.entryPool_);
232 }
233
234 private:
235 using RA_opt = typename Master_matrix::Row_access_option;
236 using Dim_opt = typename Master_matrix::Column_dimension_option;
237 using Chain_opt = typename Master_matrix::Chain_column_option;
238
239 Column_support column_;
240 Field_operators const* operators_;
241 Entry_constructor* entryPool_;
242
243 void _delete_entry(typename Column_support::iterator& it);
244 Entry* _insert_entry(ID_index rowIndex, const Field_element& value);
245 template <class Entry_range>
246 bool _add(const Entry_range& column);
247 template <class Entry_range>
248 bool _multiply_target_and_add(const Field_element& val, const Entry_range& column);
249 template <class Entry_range>
250 bool _multiply_source_and_add(const Entry_range& column, const Field_element& val);
251 template <class Entry_range, typename F1, typename F2>
252 bool _generic_add(const Entry_range& source, F1&& process_source, F2&& update_target);
253};
254
255template <class Master_matrix>
256inline Unordered_set_column<Master_matrix>::Unordered_set_column(Column_settings* colSettings)
257 : RA_opt(),
258 Dim_opt(),
259 Chain_opt(),
260 operators_(Master_matrix::get_operator_ptr(colSettings)),
261 entryPool_(colSettings == nullptr ? nullptr : &(colSettings->entryConstructor))
262{}
263
264template <class Master_matrix>
265template <class Container>
266inline Unordered_set_column<Master_matrix>::Unordered_set_column(const Container& nonZeroRowIndices,
267 Column_settings* colSettings)
268 : Unordered_set_column(nonZeroRowIndices,
269 nonZeroRowIndices.size() == 0 ? 0 : nonZeroRowIndices.size() - 1,
270 colSettings)
271{
272 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
273 "Constructor not available for chain columns, please specify the dimension of the chain.");
274}
275
276template <class Master_matrix>
277template <class Container, class Row_container>
278inline Unordered_set_column<Master_matrix>::Unordered_set_column(Index columnIndex,
279 const Container& nonZeroRowIndices,
280 Row_container* rowContainer,
281 Column_settings* colSettings)
282 : Unordered_set_column(columnIndex,
283 nonZeroRowIndices,
284 nonZeroRowIndices.size() == 0 ? 0 : nonZeroRowIndices.size() - 1,
285 rowContainer,
286 colSettings)
287{
288 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
289 "Constructor not available for chain columns, please specify the dimension of the chain.");
290}
291
292template <class Master_matrix>
293template <class Container, class>
294inline Unordered_set_column<Master_matrix>::Unordered_set_column(const Container& nonZeroRowIndices,
295 Dimension dimension,
296 Column_settings* colSettings)
297 : RA_opt(),
298 Dim_opt(dimension),
299 Chain_opt(nonZeroRowIndices.begin() == nonZeroRowIndices.end()
300 ? Master_matrix::template get_null_value<ID_index>()
301 : Master_matrix::get_row_index(*std::prev(nonZeroRowIndices.end()))),
302 column_(nonZeroRowIndices.size()),
303 operators_(Master_matrix::get_operator_ptr(colSettings)),
304 entryPool_(&(colSettings->entryConstructor))
305{
306 for (const auto& id : nonZeroRowIndices) {
307 _insert_entry(Master_matrix::get_row_index(id),
308 Master_matrix::get_coefficient_value(Master_matrix::get_element(id), operators_));
309 }
310}
311
312template <class Master_matrix>
313template <class Container, class Row_container, class>
314inline Unordered_set_column<Master_matrix>::Unordered_set_column(Index columnIndex,
315 const Container& nonZeroRowIndices,
316 Dimension dimension,
317 Row_container* rowContainer,
318 Column_settings* colSettings)
319 : RA_opt(columnIndex, rowContainer),
320 Dim_opt(dimension),
321 Chain_opt(nonZeroRowIndices.begin() == nonZeroRowIndices.end()
322 ? Master_matrix::template get_null_value<ID_index>()
323 : Master_matrix::get_row_index(*std::prev(nonZeroRowIndices.end()))),
324 column_(nonZeroRowIndices.size()),
325 operators_(Master_matrix::get_operator_ptr(colSettings)),
326 entryPool_(&(colSettings->entryConstructor))
327{
328 for (const auto& id : nonZeroRowIndices) {
329 _insert_entry(Master_matrix::get_row_index(id),
330 Master_matrix::get_coefficient_value(Master_matrix::get_element(id), operators_));
331 }
332}
333
334template <class Master_matrix>
335inline Unordered_set_column<Master_matrix>::Unordered_set_column(ID_index idx,
336 Dimension dimension,
337 Column_settings* colSettings)
338 : RA_opt(),
339 Dim_opt(dimension),
340 Chain_opt(idx),
341 column_(1),
342 operators_(nullptr),
343 entryPool_(&(colSettings->entryConstructor))
344{
345 static_assert(Master_matrix::Option_list::is_z2,
346 "Constructor not available for Zp != Z2. Please specify the coefficient.");
347 _insert_entry(idx, 1);
348}
349
350template <class Master_matrix>
351inline Unordered_set_column<Master_matrix>::Unordered_set_column(ID_index idx,
352 Field_element e,
353 Dimension dimension,
354 Column_settings* colSettings)
355 : RA_opt(),
356 Dim_opt(dimension),
357 Chain_opt(idx),
358 column_(1),
359 operators_(&(colSettings->operators)),
360 entryPool_(&(colSettings->entryConstructor))
361{
362 static_assert(!Master_matrix::Option_list::is_z2,
363 "Constructor not available for Zp == Z2. Please do not specify any coefficient.");
364 _insert_entry(idx, operators_->get_value(e));
365}
366
367template <class Master_matrix>
368template <class Row_container>
369inline Unordered_set_column<Master_matrix>::Unordered_set_column(Index columnIndex,
370 ID_index idx,
371 Dimension dimension,
372 Row_container* rowContainer,
373 Column_settings* colSettings)
374 : RA_opt(columnIndex, rowContainer),
375 Dim_opt(dimension),
376 Chain_opt(idx),
377 column_(1),
378 operators_(nullptr),
379 entryPool_(&(colSettings->entryConstructor))
380{
381 static_assert(Master_matrix::Option_list::is_z2,
382 "Constructor not available for Zp != Z2. Please specify the coefficient.");
383 _insert_entry(idx, 1);
384}
385
386template <class Master_matrix>
387template <class Row_container>
388inline Unordered_set_column<Master_matrix>::Unordered_set_column(Index columnIndex,
389 ID_index idx,
390 Field_element e,
391 Dimension dimension,
392 Row_container* rowContainer,
393 Column_settings* colSettings)
394 : RA_opt(columnIndex, rowContainer),
395 Dim_opt(dimension),
396 Chain_opt(idx),
397 column_(1),
398 operators_(&(colSettings->operators)),
399 entryPool_(&(colSettings->entryConstructor))
400{
401 static_assert(!Master_matrix::Option_list::is_z2,
402 "Constructor not available for Zp == Z2. Please do not specify any coefficient.");
403 _insert_entry(idx, operators_->get_value(e));
404}
405
406template <class Master_matrix>
407inline Unordered_set_column<Master_matrix>::Unordered_set_column(const Unordered_set_column& column,
408 Column_settings* colSettings)
409 : RA_opt(),
410 Dim_opt(static_cast<const Dim_opt&>(column)),
411 Chain_opt(static_cast<const Chain_opt&>(column)),
412 column_(column.column_.bucket_count()),
413 operators_(colSettings == nullptr ? column.operators_ : Master_matrix::get_operator_ptr(colSettings)),
414 entryPool_(colSettings == nullptr ? column.entryPool_ : &(colSettings->entryConstructor))
415{
416 static_assert(!Master_matrix::Option_list::has_row_access,
417 "Simple copy constructor not available when row access option enabled. Please specify the new column "
418 "index and the row container.");
419
420 for (const Entry* entry : column.column_) {
421 _insert_entry(entry->get_row_index(), entry->get_element());
422 }
423}
424
425template <class Master_matrix>
426template <class Row_container>
427inline Unordered_set_column<Master_matrix>::Unordered_set_column(const Unordered_set_column& column,
428 Index columnIndex,
429 Row_container* rowContainer,
430 Column_settings* colSettings)
431 : RA_opt(columnIndex, rowContainer),
432 Dim_opt(static_cast<const Dim_opt&>(column)),
433 Chain_opt(static_cast<const Chain_opt&>(column)),
434 column_(column.column_.bucket_count()),
435 operators_(colSettings == nullptr ? column.operators_ : Master_matrix::get_operator_ptr(colSettings)),
436 entryPool_(colSettings == nullptr ? column.entryPool_ : &(colSettings->entryConstructor))
437{
438 for (const Entry* entry : column.column_) {
439 _insert_entry(entry->get_row_index(), entry->get_element());
440 }
441}
442
443template <class Master_matrix>
444inline Unordered_set_column<Master_matrix>::Unordered_set_column(Unordered_set_column&& column) noexcept
445 : RA_opt(std::move(static_cast<RA_opt&>(column))),
446 Dim_opt(std::move(static_cast<Dim_opt&>(column))),
447 Chain_opt(std::move(static_cast<Chain_opt&>(column))),
448 column_(std::move(column.column_)),
449 operators_(std::exchange(column.operators_, nullptr)),
450 entryPool_(std::exchange(column.entryPool_, nullptr))
451{}
452
453template <class Master_matrix>
454inline Unordered_set_column<Master_matrix>::~Unordered_set_column()
455{
456 for (auto* entry : column_) {
457 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(entry);
458 entryPool_->destroy(entry);
459 }
460}
461
462template <class Master_matrix>
463inline std::vector<typename Unordered_set_column<Master_matrix>::Field_element>
464Unordered_set_column<Master_matrix>::get_content(int columnLength) const
465{
466 if (columnLength < 0 && column_.size() > 0)
467 columnLength = (*std::max_element(column_.begin(), column_.end(), EntryPointerComp()))->get_row_index() + 1;
468 else if (columnLength < 0)
469 return std::vector<Field_element>();
470
471 std::vector<Field_element> container(columnLength, 0);
472 for (auto it = column_.begin(); it != column_.end(); ++it) {
473 if ((*it)->get_row_index() < static_cast<ID_index>(columnLength)) {
474 container[(*it)->get_row_index()] = Master_matrix::get_element(**it);
475 }
476 }
477 return container;
478}
479
480template <class Master_matrix>
481inline bool Unordered_set_column<Master_matrix>::is_non_zero(ID_index rowIndex) const
482{
483 Entry entry(rowIndex);
484 return column_.find(&entry) != column_.end();
485}
486
487template <class Master_matrix>
488inline bool Unordered_set_column<Master_matrix>::is_empty() const
489{
490 return column_.empty();
491}
492
493template <class Master_matrix>
494inline std::size_t Unordered_set_column<Master_matrix>::size() const
495{
496 return column_.size();
497}
498
499template <class Master_matrix>
500template <class Row_index_map>
501inline void Unordered_set_column<Master_matrix>::reorder(const Row_index_map& valueMap,
502 [[maybe_unused]] Index columnIndex)
503{
504 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
505 "Method not available for chain columns.");
506
507 Column_support newSet;
508
509 for (Entry* entry : column_) {
510 if constexpr (Master_matrix::Option_list::has_row_access) {
511 RA_opt::unlink(entry);
512 if (columnIndex != Master_matrix::template get_null_value<Index>()) entry->set_column_index(columnIndex);
513 }
514 entry->set_row_index(valueMap.at(entry->get_row_index()));
515 newSet.insert(entry);
516 if constexpr (Master_matrix::Option_list::has_row_access &&
517 Master_matrix::Option_list::has_intrusive_rows) // intrusive list
518 RA_opt::insert_entry(entry->get_row_index(), entry);
519 }
520
521 // when row is a set, all entries have to be deleted first, to avoid colliding when inserting
522 if constexpr (Master_matrix::Option_list::has_row_access && !Master_matrix::Option_list::has_intrusive_rows) { // set
523 for (Entry* entry : newSet) {
524 RA_opt::insert_entry(entry->get_row_index(), entry);
525 }
526 }
527
528 column_.swap(newSet);
529}
530
531template <class Master_matrix>
532inline void Unordered_set_column<Master_matrix>::clear()
533{
534 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
535 "Method not available for chain columns as a base element should not be empty.");
536
537 for (auto* entry : column_) {
538 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(entry);
539 entryPool_->destroy(entry);
540 }
541
542 column_.clear();
543}
544
545template <class Master_matrix>
546inline void Unordered_set_column<Master_matrix>::clear(ID_index rowIndex)
547{
548 static_assert(!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type,
549 "Method not available for chain columns.");
550
551 auto entry = entryPool_->construct(rowIndex);
552 auto it = column_.find(entry);
553 if (it != column_.end()) {
554 _delete_entry(it);
555 }
556 entryPool_->destroy(entry);
557}
558
559template <class Master_matrix>
560inline typename Unordered_set_column<Master_matrix>::ID_index Unordered_set_column<Master_matrix>::get_pivot() const
561{
562 static_assert(Master_matrix::isNonBasic,
563 "Method not available for base columns."); // could technically be, but is the notion useful then?
564
565 if constexpr (Master_matrix::Option_list::is_of_boundary_type) {
566 if (column_.empty()) return Master_matrix::template get_null_value<ID_index>();
567 // linear search could be avoided with storing the pivot. But even then, some modifications of the column requires
568 // the max, so not clear how much it is worth it.
569 return (*std::max_element(column_.begin(), column_.end(), EntryPointerComp()))->get_row_index();
570 } else {
571 return Chain_opt::_get_pivot();
572 }
573}
574
575template <class Master_matrix>
576inline typename Unordered_set_column<Master_matrix>::Field_element
577Unordered_set_column<Master_matrix>::get_pivot_value() const
578{
579 static_assert(Master_matrix::isNonBasic,
580 "Method not available for base columns."); // could technically be, but is the notion useful then?
581
582 if constexpr (Master_matrix::Option_list::is_z2) {
583 return 1;
584 } else {
585 if constexpr (Master_matrix::Option_list::is_of_boundary_type) {
586 if (column_.empty()) return 0;
587 return (*std::max_element(column_.begin(), column_.end(), EntryPointerComp()))->get_element();
588 } else {
589 if (Chain_opt::_get_pivot() == Master_matrix::template get_null_value<ID_index>()) return Field_element();
590 for (const Entry* entry : column_) {
591 if (entry->get_row_index() == Chain_opt::_get_pivot()) return entry->get_element();
592 }
593 return Field_element(); // should never happen if chain column is used properly
594 }
595 }
596}
597
598template <class Master_matrix>
599inline typename Unordered_set_column<Master_matrix>::iterator Unordered_set_column<Master_matrix>::begin() noexcept
600{
601 return column_.begin();
602}
603
604template <class Master_matrix>
605inline typename Unordered_set_column<Master_matrix>::const_iterator Unordered_set_column<Master_matrix>::begin()
606 const noexcept
607{
608 return column_.begin();
609}
610
611template <class Master_matrix>
612inline typename Unordered_set_column<Master_matrix>::iterator Unordered_set_column<Master_matrix>::end() noexcept
613{
614 return column_.end();
615}
616
617template <class Master_matrix>
618inline typename Unordered_set_column<Master_matrix>::const_iterator Unordered_set_column<Master_matrix>::end()
619 const noexcept
620{
621 return column_.end();
622}
623
624template <class Master_matrix>
625inline typename Unordered_set_column<Master_matrix>::Content_range
626Unordered_set_column<Master_matrix>::get_non_zero_content_range() const
627{
628 Content_range res(column_.size());
629 std::size_t i = 0;
630 for (const auto& entry : column_) res[i++] = *entry;
631 std::sort(res.begin(), res.end());
632 return res;
633}
634
635template <class Master_matrix>
636template <class Entry_range>
637inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::operator+=(const Entry_range& column)
638{
639 static_assert((!Master_matrix::isNonBasic || std::is_same_v<Entry_range, Unordered_set_column>),
640 "For boundary columns, the range has to be a column of same type to help ensure the validity of the "
641 "base element."); // could be removed, if we give the responsibility to the user.
642 static_assert((!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type),
643 "For chain columns, the given column cannot be constant.");
644
645 _add(column);
646
647 return *this;
648}
649
650template <class Master_matrix>
651inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::operator+=(
652 Unordered_set_column& column)
653{
654 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
655 // assumes that the addition never zeros out this column.
656 if (_add(column)) {
657 Chain_opt::_swap_pivots(column);
658 Dim_opt::_swap_dimension(column);
659 }
660 } else {
661 _add(column);
662 }
663
664 return *this;
665}
666
667template <class Master_matrix>
668inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::operator*=(const Field_element& v)
669{
670 Field_element val = Master_matrix::get_coefficient_value(v, operators_);
671
672 if (val == Field_operators::get_additive_identity()) {
673 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
674 throw std::invalid_argument("A chain column should not be multiplied by 0.");
675 } else {
676 clear();
677 }
678 return *this;
679 }
680
681 if (val == Field_operators::get_multiplicative_identity()) return *this;
682
683 // multiply_inplace needs a non-const reference to element, so even if Z2 never reaches here, it won't compile
684 // without the constexpr, as we are not storing a dummy value just for this purpose.
685 if constexpr (!Master_matrix::Option_list::is_z2) {
686 for (Entry* entry : column_) {
687 operators_->multiply_inplace(entry->get_element(), val);
688 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::update_entry(*entry);
689 }
690 }
691
692 return *this;
693}
694
695template <class Master_matrix>
696template <class Entry_range>
697inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::multiply_target_and_add(
698 const Field_element& val,
699 const Entry_range& column)
700{
701 static_assert((!Master_matrix::isNonBasic || std::is_same_v<Entry_range, Unordered_set_column>),
702 "For boundary columns, the range has to be a column of same type to help ensure the validity of the "
703 "base element."); // could be removed, if we give the responsibility to the user.
704 static_assert((!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type),
705 "For chain columns, the given column cannot be constant.");
706
707 _multiply_target_and_add(Master_matrix::get_coefficient_value(val, operators_), column);
708
709 return *this;
710}
711
712template <class Master_matrix>
713inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::multiply_target_and_add(
714 const Field_element& val,
715 Unordered_set_column& column)
716{
717 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
718 // assumes that the addition never zeros out this column.
719 if (_multiply_target_and_add(Master_matrix::get_coefficient_value(val, operators_), column)) {
720 Chain_opt::_swap_pivots(column);
721 Dim_opt::_swap_dimension(column);
722 }
723 } else {
724 _multiply_target_and_add(Master_matrix::get_coefficient_value(val, operators_), column);
725 }
726
727 return *this;
728}
729
730template <class Master_matrix>
731template <class Entry_range>
732inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::multiply_source_and_add(
733 const Entry_range& column,
734 const Field_element& val)
735{
736 static_assert((!Master_matrix::isNonBasic || std::is_same_v<Entry_range, Unordered_set_column>),
737 "For boundary columns, the range has to be a column of same type to help ensure the validity of the "
738 "base element."); // could be removed, if we give the responsibility to the user.
739 static_assert((!Master_matrix::isNonBasic || Master_matrix::Option_list::is_of_boundary_type),
740 "For chain columns, the given column cannot be constant.");
741
742 _multiply_source_and_add(column, Master_matrix::get_coefficient_value(val, operators_));
743
744 return *this;
745}
746
747template <class Master_matrix>
748inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::multiply_source_and_add(
749 Unordered_set_column& column,
750 const Field_element& val)
751{
752 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
753 // assumes that the addition never zeros out this column.
754 if (_multiply_source_and_add(column, Master_matrix::get_coefficient_value(val, operators_))) {
755 Chain_opt::_swap_pivots(column);
756 Dim_opt::_swap_dimension(column);
757 }
758 } else {
759 _multiply_source_and_add(column, Master_matrix::get_coefficient_value(val, operators_));
760 }
761
762 return *this;
763}
764
765template <class Master_matrix>
766inline void Unordered_set_column<Master_matrix>::push_back(const Entry& entry)
767{
768 static_assert(Master_matrix::Option_list::is_of_boundary_type, "`push_back` is not available for Chain matrices.");
769
770 GUDHI_CHECK(entry.get_row_index() > get_pivot(),
771 std::invalid_argument("The new row index has to be higher than the current pivot."));
772
773 _insert_entry(entry.get_row_index(), entry.get_element());
774}
775
776template <class Master_matrix>
777inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::operator=(
778 const Unordered_set_column& other)
779{
780 static_assert(!Master_matrix::Option_list::has_row_access, "= assignment not enabled with row access option.");
781
782 // to avoid destroying the column when building from it-self in the for loop below...
783 if (this == &other) return *this;
784
785 Dim_opt::operator=(other);
786 Chain_opt::operator=(other);
787
788 for (auto* entry : column_) {
789 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(entry);
790 entryPool_->destroy(entry);
791 }
792 column_.clear();
793
794 operators_ = other.operators_;
795 entryPool_ = other.entryPool_;
796
797 for (const Entry* entry : other.column_) {
798 _insert_entry(entry->get_row_index(), entry->get_element());
799 }
800
801 return *this;
802}
803
804template <class Master_matrix>
805inline Unordered_set_column<Master_matrix>& Unordered_set_column<Master_matrix>::operator=(
806 Unordered_set_column&& other) noexcept
807{
808 static_assert(!Master_matrix::Option_list::has_row_access, "= assignment not enabled with row access option.");
809
810 // to avoid destroying the column before building from it-self...
811 if (&column_ == &(other.column_)) return *this;
812
813 Dim_opt::operator=(std::move(other));
814 Chain_opt::operator=(std::move(other));
815
816 for (auto* entry : column_) {
817 if (entry != nullptr) entryPool_->destroy(entry);
818 }
819
820 column_ = std::move(other.column_);
821 operators_ = std::exchange(other.operators_, nullptr);
822 entryPool_ = std::exchange(other.entryPool_, nullptr);
823
824 return *this;
825}
826
827template <class Master_matrix>
828inline void Unordered_set_column<Master_matrix>::_delete_entry(typename Column_support::iterator& it)
829{
830 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::unlink(*it);
831 entryPool_->destroy(*it);
832 auto tmp = it++;
833 // it = column_.erase(it);
834 column_.erase(tmp);
835}
836
837template <class Master_matrix>
838inline typename Unordered_set_column<Master_matrix>::Entry* Unordered_set_column<Master_matrix>::_insert_entry(
839 ID_index rowIndex,
840 const Field_element& value)
841{
842 Entry* newEntry;
843 if constexpr (Master_matrix::Option_list::has_row_access) {
844 newEntry = entryPool_->construct(RA_opt::get_column_index(), rowIndex);
845 } else {
846 newEntry = entryPool_->construct(rowIndex);
847 }
848 newEntry->set_element(value);
849 column_.insert(newEntry);
850 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::insert_entry(rowIndex, newEntry);
851 return newEntry;
852}
853
854template <class Master_matrix>
855template <class Entry_range>
856inline bool Unordered_set_column<Master_matrix>::_add(const Entry_range& column)
857{
858 if (column.begin() == column.end()) return false;
859 if (column_.empty()) { // chain should never enter here.
860 for (const Entry& entry : column) {
861 _insert_entry(entry.get_row_index(), entry.get_element());
862 }
863 return true;
864 }
865
866 return _generic_add(
867 column,
868 [&](const Entry& oldEntry, Entry* newEntry) {
869 newEntry->set_element(oldEntry.get_element());
870 },
871 [&](Entry* targetEntry, const Entry& sourceEntry) {
872 if constexpr (!Master_matrix::Option_list::is_z2)
873 operators_->add_inplace(targetEntry->get_element(), sourceEntry.get_element());
874 });
875}
876
877template <class Master_matrix>
878template <class Entry_range>
879inline bool Unordered_set_column<Master_matrix>::_multiply_target_and_add(const Field_element& val,
880 const Entry_range& column)
881{
882 // because the column is unordered, I don't see a way to do both operations in one go
883 // without guarantees on the entry range...
884 operator*=(val);
885 return _add(column) || val == Field_operators::get_additive_identity();
886}
887
888template <class Master_matrix>
889template <class Entry_range>
890inline bool Unordered_set_column<Master_matrix>::_multiply_source_and_add(const Entry_range& column,
891 const Field_element& val)
892{
893 if (val == Field_operators::get_additive_identity() || column.begin() == column.end()) {
894 return false;
895 }
896
897 if (val == Field_operators::get_multiplicative_identity()) {
898 return _add(column);
899 }
900
901 // multiply_inplace needs a non-const reference to element, so even if Z2 never reaches here, it won't compile
902 // without the constexpr, as we are not storing a dummy value just for this purpose.
903 if constexpr (!Master_matrix::Option_list::is_z2) {
904 return _generic_add(
905 column,
906 [&](const Entry& oldEntry, Entry* newEntry) {
907 newEntry->set_element(oldEntry.get_element());
908 operators_->multiply_inplace(newEntry->get_element(), val);
909 },
910 [&](Entry* targetEntry, const Entry& sourceEntry) {
911 operators_->multiply_and_add_inplace_back(sourceEntry.get_element(), val, targetEntry->get_element());
912 });
913 } else {
914 return false; // we should never arrive here, just to suppress the warning
915 }
916}
917
918template <class Master_matrix>
919template <class Entry_range, typename F1, typename F2>
920inline bool Unordered_set_column<Master_matrix>::_generic_add(const Entry_range& source,
921 [[maybe_unused]] F1&& process_source,
922 [[maybe_unused]] F2&& update_target)
923{
924 bool pivotIsZeroed = false;
925
926 for (const Entry& entry : source) {
927 Entry* newEntry;
928 if constexpr (Master_matrix::Option_list::has_row_access) {
929 newEntry = entryPool_->construct(RA_opt::get_column_index(), entry.get_row_index());
930 } else {
931 newEntry = entryPool_->construct(entry.get_row_index());
932 }
933 auto res = column_.insert(newEntry);
934 if (res.second) {
935 std::forward<F1>(process_source)(entry, newEntry);
936 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::insert_entry(entry.get_row_index(), newEntry);
937 } else {
938 entryPool_->destroy(newEntry);
939 if constexpr (Master_matrix::Option_list::is_z2) {
940 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
941 if (entry.get_row_index() == Chain_opt::_get_pivot()) pivotIsZeroed = true;
942 }
943 _delete_entry(res.first);
944 } else {
945 std::forward<F2>(update_target)(*res.first, entry);
946 if ((*res.first)->get_element() == Field_operators::get_additive_identity()) {
947 if constexpr (Master_matrix::isNonBasic && !Master_matrix::Option_list::is_of_boundary_type) {
948 if ((*res.first)->get_row_index() == Chain_opt::_get_pivot()) pivotIsZeroed = true;
949 }
950 _delete_entry(res.first);
951 } else {
952 if constexpr (Master_matrix::Option_list::has_row_access) RA_opt::update_entry(**res.first);
953 }
954 }
955 }
956 }
957
958 return pivotIsZeroed;
959}
960
961} // namespace persistence_matrix
962} // namespace Gudhi
963
972template <class Master_matrix>
973struct std::hash<Gudhi::persistence_matrix::Unordered_set_column<Master_matrix>> {
974 std::size_t operator()(const Gudhi::persistence_matrix::Unordered_set_column<Master_matrix>& column) const
975 {
976 // can't use Gudhi::persistence_matrix::hash_column because unordered
977 std::size_t seed = 0;
978 for (const auto& entry : column) {
979 seed ^= std::hash<unsigned int>()(entry.get_row_index() * static_cast<unsigned int>(entry.get_element()));
980 }
981 return seed;
982 }
983};
984
985#endif // PM_UNORDERED_SET_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 unordered_set_column.h:68
Contains different versions of Gudhi::persistence_matrix::Entry factories.
Persistence matrix namespace.
Definition FieldOperators.h:18
Gudhi namespace.
Definition SimplicialComplexForAlpha.h:14