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