All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
chain_vine_swap.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
20#ifndef PM_CHAIN_VINE_SWAP_H
21#define PM_CHAIN_VINE_SWAP_H
22
23#include <utility> //std::swap & std::move
24#include <cassert>
25#include <functional> //std::function
26#include <stdexcept> //std::invalid_argument
27
28#include "chain_pairing.h"
29
30namespace Gudhi {
31namespace persistence_matrix {
32
43constexpr bool _no_G_death_comparator([[maybe_unused]] unsigned int columnIndex1,
44 [[maybe_unused]] unsigned int columnIndex2)
45{
46 return false;
47}
48
56{
57 friend void swap([[maybe_unused]] Dummy_chain_vine_swap& d1, [[maybe_unused]] Dummy_chain_vine_swap& d2) {}
58
60 template <typename BirthComparatorFunction, typename DeathComparatorFunction>
61 Dummy_chain_vine_swap([[maybe_unused]] const BirthComparatorFunction& birthComparator,
62 [[maybe_unused]] const DeathComparatorFunction& deathComparator) {}
63};
64
72{
73 friend void swap([[maybe_unused]] Dummy_chain_vine_pairing& d1, [[maybe_unused]] Dummy_chain_vine_pairing& d2) {}
74};
75
83template <typename Master_matrix>
84class Chain_barcode_swap : public Chain_pairing<Master_matrix>
85{
86 public:
87 using ID_index = typename Master_matrix::ID_index;
88 using Pos_index = typename Master_matrix::Pos_index;
89 //CP = Chain Pairing
91
102 : CP(static_cast<const CP&>(toCopy)), pivotToPosition_(toCopy.pivotToPosition_){};
109 : CP(std::move(static_cast<CP&>(other))), pivotToPosition_(std::move(other.pivotToPosition_)){};
110
111 protected:
112 using Dictionary = typename Master_matrix::template Dictionary<Pos_index>;
113
114 Dictionary pivotToPosition_; // necessary to keep track of the barcode changes
115
116 void swap_positions(ID_index pivot1, ID_index pivot2) {
117 if constexpr (Master_matrix::Option_list::has_map_column_container) {
118 std::swap(pivotToPosition_.at(pivot1), pivotToPosition_.at(pivot2));
119 } else {
120 std::swap(pivotToPosition_[pivot1], pivotToPosition_[pivot2]);
121 }
122 }
123
124 bool is_negative_in_pair(ID_index pivot) const {
125 Pos_index pos = _get_pivot_position(pivot);
126 return death(pivot) == pos;
127 }
128
129 void positive_transpose(ID_index pivot1, ID_index pivot2) {
130 Pos_index pos1 = _get_pivot_position(pivot1);
131 Pos_index pos2 = _get_pivot_position(pivot2);
132
133 _birth(pos1) = pos2;
134 _birth(pos2) = pos1;
135 std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
136 }
137
138 void negative_transpose(ID_index pivot1, ID_index pivot2) {
139 Pos_index pos1 = _get_pivot_position(pivot1);
140 Pos_index pos2 = _get_pivot_position(pivot2);
141
142 _death(pos1) = pos2;
143 _death(pos2) = pos1;
144 std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
145 }
146
147 void positive_negative_transpose(ID_index pivot1, ID_index pivot2) {
148 Pos_index pos1 = _get_pivot_position(pivot1);
149 Pos_index pos2 = _get_pivot_position(pivot2);
150
151 _birth(pos1) = pos2;
152 _death(pos2) = pos1;
153 std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
154 }
155
156 void negative_positive_transpose(ID_index pivot1, ID_index pivot2) {
157 Pos_index pos1 = _get_pivot_position(pivot1);
158 Pos_index pos2 = _get_pivot_position(pivot2);
159
160 _death(pos1) = pos2;
161 _birth(pos2) = pos1;
162 std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
163 }
164
165 bool are_adjacent(ID_index pivot1, ID_index pivot2) const {
166 Pos_index pos1 = _get_pivot_position(pivot1);
167 Pos_index pos2 = _get_pivot_position(pivot2);
168 return pos1 < pos2 ? (pos2 - pos1) == 1 : (pos1 - pos2) == 1;
169 }
170
171 Chain_barcode_swap& operator=(Chain_barcode_swap other) {
173 pivotToPosition_.swap(other.pivotToPosition_);
174 }
175 friend void swap(Chain_barcode_swap& swap1, Chain_barcode_swap& swap2) {
176 swap(static_cast<Chain_pairing<Master_matrix>&>(swap1), static_cast<Chain_pairing<Master_matrix>&>(swap2));
177 swap1.pivotToPosition_.swap(swap2.pivotToPosition_);
178 }
179
180 Pos_index death(ID_index pivot) const {
181 Pos_index simplexIndex = _get_pivot_position(pivot);
182
183 if constexpr (Master_matrix::Option_list::has_removable_columns) {
184 return CP::indexToBar_.at(simplexIndex)->death;
185 } else {
186 return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).death;
187 }
188 }
189
190 Pos_index birth(ID_index pivot) const {
191 Pos_index simplexIndex = _get_pivot_position(pivot);
192
193 if constexpr (Master_matrix::Option_list::has_removable_columns) {
194 return CP::indexToBar_.at(simplexIndex)->birth;
195 } else {
196 return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).birth;
197 }
198 }
199
200 private:
201 Pos_index _get_pivot_position(ID_index pivot) const {
202 if constexpr (Master_matrix::Option_list::has_map_column_container) {
203 return pivotToPosition_.at(
204 pivot); // quite often called, make public and pass position instead of pivot to avoid find() every time?
205 } else {
206 return pivotToPosition_[pivot];
207 }
208 }
209
210 Pos_index& _death(Pos_index simplexIndex) {
211 if constexpr (Master_matrix::Option_list::has_removable_columns) {
212 return CP::indexToBar_.at(simplexIndex)->death;
213 } else {
214 return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).death;
215 }
216 }
217
218 Pos_index& _birth(Pos_index simplexIndex) {
219 if constexpr (Master_matrix::Option_list::has_removable_columns) {
220 return CP::indexToBar_.at(simplexIndex)->birth;
221 } else {
222 return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).birth;
223 }
224 }
225};
226
235template <class Master_matrix>
236class Chain_vine_swap : public std::conditional<Master_matrix::Option_list::has_column_pairings,
237 Chain_barcode_swap<Master_matrix>,
238 Dummy_chain_vine_pairing
239 >::type
240{
241 public:
242 using Index = typename Master_matrix::Index;
243 using ID_index = typename Master_matrix::ID_index;
244 using Pos_index = typename Master_matrix::Pos_index;
245 using Column_container = typename Master_matrix::Column_container;
246 using Column = typename Master_matrix::Column;
265 Chain_vine_swap(std::function<bool(Pos_index,Pos_index)> birthComparator,
266 std::function<bool(Pos_index,Pos_index)> deathComparator = _no_G_death_comparator);
272 Chain_vine_swap(const Chain_vine_swap& matrixToCopy);
278 Chain_vine_swap(Chain_vine_swap&& other) noexcept;
279
290 Index vine_swap_with_z_eq_1_case(Index columnIndex1, Index columnIndex2);
306 Index vine_swap(Index columnIndex1, Index columnIndex2);
307
315 friend void swap(Chain_vine_swap& swap1, Chain_vine_swap& swap2) {
316 if constexpr (Master_matrix::Option_list::has_column_pairings) {
317 swap(static_cast<Chain_barcode_swap<Master_matrix>&>(swap1),
318 static_cast<Chain_barcode_swap<Master_matrix>&>(swap2));
319 }
320 std::swap(swap1.birthComp_, swap2.birthComp_);
321 std::swap(swap1.deathComp_, swap2.deathComp_);
322 }
323
324 protected:
325 using CP = typename std::conditional<Master_matrix::Option_list::has_column_pairings,
328 >::type;
329
330 private:
331 using Master_chain_matrix = typename Master_matrix::Master_chain_matrix;
332
333 std::function<bool(Pos_index,Pos_index)> birthComp_;
334 std::function<bool(Pos_index,Pos_index)> deathComp_;
336 bool _is_negative_in_pair(Index columnIndex);
337
338 Index _positive_vine_swap(Index columnIndex1, Index columnIndex2);
339 Index _positive_negative_vine_swap(Index columnIndex1, Index columnIndex2);
340 Index _negative_positive_vine_swap(Index columnIndex1, Index columnIndex2);
341 Index _negative_vine_swap(Index columnIndex1, Index columnIndex2);
342
343 constexpr Master_chain_matrix* _matrix() { return static_cast<Master_chain_matrix*>(this); }
344 constexpr const Master_chain_matrix* _matrix() const { return static_cast<const Master_chain_matrix*>(this); }
345};
346
347template <class Master_matrix>
348inline Chain_vine_swap<Master_matrix>::Chain_vine_swap() : CP(), birthComp_(), deathComp_()
349{
350 static_assert(Master_matrix::Option_list::has_column_pairings,
351 "If barcode is not stored, at least a birth comparator has to be specified.");
352}
353
354template <class Master_matrix>
355inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(std::function<bool(Pos_index,Pos_index)> birthComparator,
356 std::function<bool(Pos_index,Pos_index)> deathComparator)
357 : CP(), birthComp_(std::move(birthComparator)), deathComp_(std::move(deathComparator))
358{}
359
360template <class Master_matrix>
362 : CP(static_cast<const CP&>(matrixToCopy)),
363 birthComp_(matrixToCopy.birthComp_),
364 deathComp_(matrixToCopy.deathComp_)
365{}
366
367template <class Master_matrix>
369 : CP(std::move(static_cast<CP&>(other))),
370 birthComp_(std::move(other.birthComp_)),
371 deathComp_(std::move(other.deathComp_))
372{}
373
374template <class Master_matrix>
376 Index columnIndex1, Index columnIndex2)
377{
378 const bool col1IsNeg = _is_negative_in_pair(columnIndex1);
379 const bool col2IsNeg = _is_negative_in_pair(columnIndex2);
380
381 if (col1IsNeg && col2IsNeg) return _negative_vine_swap(columnIndex1, columnIndex2);
382
383 if (col1IsNeg) return _negative_positive_vine_swap(columnIndex1, columnIndex2);
384
385 if (col2IsNeg) return _positive_negative_vine_swap(columnIndex1, columnIndex2);
386
387 return _positive_vine_swap(columnIndex1, columnIndex2);
388}
389
390template <class Master_matrix>
392 Index columnIndex2)
393{
394 if constexpr (Master_matrix::Option_list::has_column_pairings) {
395 GUDHI_CHECK(CP::are_adjacent(_matrix()->get_pivot(columnIndex1), _matrix()->get_pivot(columnIndex2)),
396 std::invalid_argument(
397 "Chain_vine_swap::vine_swap - Columns to be swapped need to be adjacent in the 'real' matrix."));
398 }
399
400 const bool col1IsNeg = _is_negative_in_pair(columnIndex1);
401 const bool col2IsNeg = _is_negative_in_pair(columnIndex2);
402
403 if (col1IsNeg && col2IsNeg) {
404 if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
405 if constexpr (Master_matrix::Option_list::has_column_pairings) {
406 ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
407 ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
408
409 CP::negative_transpose(pivot1, pivot2);
410 CP::swap_positions(pivot1, pivot2);
411 }
412 return columnIndex1;
413 }
414 return _negative_vine_swap(columnIndex1, columnIndex2);
415 }
416
417 if (col1IsNeg) {
418 if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
419 if constexpr (Master_matrix::Option_list::has_column_pairings) {
420 ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
421 ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
422
423 CP::negative_positive_transpose(pivot1, pivot2);
424 CP::swap_positions(pivot1, pivot2);
425 }
426 return columnIndex1;
427 }
428 return _negative_positive_vine_swap(columnIndex1, columnIndex2);
429 }
430
431 if (col2IsNeg) {
432 if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
433 if constexpr (Master_matrix::Option_list::has_column_pairings) {
434 ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
435 ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
436
437 CP::positive_negative_transpose(pivot1, pivot2);
438 CP::swap_positions(pivot1, pivot2);
439 }
440 return columnIndex1;
441 }
442 return _positive_negative_vine_swap(columnIndex1, columnIndex2);
443 }
444
445 if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
446 if constexpr (Master_matrix::Option_list::has_column_pairings) {
447 ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
448 ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
449
450 CP::positive_transpose(pivot1, pivot2);
451 CP::swap_positions(pivot1, pivot2);
452 }
453 return columnIndex1;
454 }
455 return _positive_vine_swap(columnIndex1, columnIndex2);
456}
457
458template <class Master_matrix>
460{
461 CP::operator=(other);
462 std::swap(birthComp_, other.birthComp_);
463 std::swap(deathComp_, other.deathComp_);
464 return *this;
465}
466
467template <class Master_matrix>
468inline bool Chain_vine_swap<Master_matrix>::_is_negative_in_pair(Index columnIndex)
469{
470 if constexpr (Master_matrix::Option_list::has_column_pairings) {
471 return CP::is_negative_in_pair(_matrix()->get_pivot(columnIndex));
472 } else {
473 auto& col = _matrix()->get_column(columnIndex);
474 if (!col.is_paired()) return false;
475 return col.get_pivot() > _matrix()->get_pivot(col.get_paired_chain_index());
476 }
477}
478
479template <class Master_matrix>
480inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_positive_vine_swap(
481 Index columnIndex1, Index columnIndex2)
482{
483 auto& col1 = _matrix()->get_column(columnIndex1);
484 auto& col2 = _matrix()->get_column(columnIndex2);
485
486 if constexpr (Master_matrix::Option_list::has_column_pairings) {
487 CP::swap_positions(col1.get_pivot(), col2.get_pivot());
488 }
489 // TODO: factorize the cases
490 // But for debug it is much more easier to understand what is happening when split like this
491 if (!col1.is_paired()) { // F x *
492 bool hasSmallerBirth;
493 if constexpr (Master_matrix::Option_list::has_column_pairings) {
494 // this order because position were swapped with CP::swap_positions
495 hasSmallerBirth = (CP::birth(col2.get_pivot()) < CP::birth(col1.get_pivot()));
496 } else {
497 hasSmallerBirth = birthComp_(columnIndex1, columnIndex2);
498 }
499
500 if (!col2.is_paired() && hasSmallerBirth) {
501 _matrix()->add_to(columnIndex1, columnIndex2);
502 if constexpr (Master_matrix::Option_list::has_column_pairings) {
503 CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
504 }
505 return columnIndex1;
506 }
507 _matrix()->add_to(columnIndex2, columnIndex1);
508
509 return columnIndex2;
510 }
511
512 if (!col2.is_paired()) { // G x F
513 static_cast<Master_chain_matrix*>(this)->add_to(columnIndex1, columnIndex2);
514 if constexpr (Master_matrix::Option_list::has_column_pairings) {
515 CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
516 }
517 return columnIndex1;
518 }
519
520 bool hasSmallerDeath;
521 if constexpr (Master_matrix::Option_list::has_column_pairings) {
522 // this order because position were swapped with CP::swap_positions
523 hasSmallerDeath = (CP::death(col2.get_pivot()) < CP::death(col1.get_pivot()));
524 } else {
525 hasSmallerDeath = deathComp_(columnIndex1, columnIndex2);
526 }
527
528 // G x G
529 if (hasSmallerDeath)
530 {
531 _matrix()->add_to(col1.get_paired_chain_index(), col2.get_paired_chain_index());
532 _matrix()->add_to(columnIndex1, columnIndex2);
533 if constexpr (Master_matrix::Option_list::has_column_pairings) {
534 CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
535 }
536 return columnIndex1;
537 }
538
539 _matrix()->add_to(col2.get_paired_chain_index(), col1.get_paired_chain_index());
540 _matrix()->add_to(columnIndex2, columnIndex1);
541
542 return columnIndex2;
543}
544
545template <class Master_matrix>
546inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_positive_negative_vine_swap(
547 Index columnIndex1, Index columnIndex2)
548{
549 _matrix()->add_to(columnIndex1, columnIndex2);
550
551 if constexpr (Master_matrix::Option_list::has_column_pairings) {
552 ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
553 ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
554
555 CP::positive_negative_transpose(pivot1, pivot2);
556 CP::swap_positions(pivot1, pivot2);
557 }
558
559 return columnIndex1;
560}
561
562template <class Master_matrix>
563inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_negative_positive_vine_swap(
564 Index columnIndex1, Index columnIndex2)
565{
566 _matrix()->add_to(columnIndex2, columnIndex1);
567
568 if constexpr (Master_matrix::Option_list::has_column_pairings) {
569 CP::swap_positions(_matrix()->get_pivot(columnIndex1), _matrix()->get_pivot(columnIndex2));
570 }
571
572 return columnIndex2;
573}
574
575template <class Master_matrix>
576inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_negative_vine_swap(
577 Index columnIndex1, Index columnIndex2)
578{
579 auto& col1 = _matrix()->get_column(columnIndex1);
580 auto& col2 = _matrix()->get_column(columnIndex2);
581
582 Index pairedIndex1 = col1.get_paired_chain_index();
583 Index pairedIndex2 = col2.get_paired_chain_index();
584
585 bool hasSmallerBirth;
586 if constexpr (Master_matrix::Option_list::has_column_pairings) {
587 hasSmallerBirth = (CP::birth(col1.get_pivot()) < CP::birth(col2.get_pivot()));
588 } else {
589 hasSmallerBirth = birthComp_(pairedIndex1, pairedIndex2);
590 }
591
592 if constexpr (Master_matrix::Option_list::has_column_pairings) {
593 CP::swap_positions(col1.get_pivot(), col2.get_pivot());
594 }
595
596 if (hasSmallerBirth)
597 {
598 _matrix()->add_to(pairedIndex1, pairedIndex2);
599 _matrix()->add_to(columnIndex1, columnIndex2);
600
601 if constexpr (Master_matrix::Option_list::has_column_pairings) {
602 CP::negative_transpose(col1.get_pivot(), col2.get_pivot());
603 }
604
605 return columnIndex1;
606 }
607
608 _matrix()->add_to(pairedIndex2, pairedIndex1);
609 _matrix()->add_to(columnIndex2, columnIndex1);
610
611 return columnIndex2;
612}
613
614} // namespace persistence_matrix
615} // namespace Gudhi
616
617#endif // PM_CHAIN_VINE_SWAP_H
Contains the Gudhi::persistence_matrix::Chain_pairing class and Gudhi::persistence_matrix::Dummy_chai...
Class managing the barcode for Chain_vine_swap.
Definition: chain_vine_swap.h:85
typename Master_matrix::Pos_index Pos_index
Definition: chain_vine_swap.h:88
Chain_barcode_swap()
Default constructor.
Definition: chain_vine_swap.h:95
Chain_barcode_swap(const Chain_barcode_swap &toCopy)
Copy constructor.
Definition: chain_vine_swap.h:101
typename Master_matrix::ID_index ID_index
Definition: chain_vine_swap.h:87
Chain_barcode_swap(Chain_barcode_swap &&other)
Move constructor.
Definition: chain_vine_swap.h:108
Class managing the barcode for Chain_matrix if the option was enabled.
Definition: chain_pairing.h:48
Chain_pairing & operator=(Chain_pairing other)
Assign operator.
Definition: chain_pairing.h:125
Class managing the vine swaps for Chain_matrix.
Definition: chain_vine_swap.h:240
Chain_vine_swap()
Default constructor. Only available if PersistenceMatrixOptions::has_column_pairings is true.
Definition: chain_vine_swap.h:348
typename Master_matrix::Pos_index Pos_index
Definition: chain_vine_swap.h:244
Chain_vine_swap & operator=(Chain_vine_swap other)
Assign operator.
Definition: chain_vine_swap.h:459
typename Master_matrix::Column_container Column_container
Definition: chain_vine_swap.h:245
Index vine_swap(Index columnIndex1, Index columnIndex2)
Does a vine swap between two cells which are consecutive in the filtration. Roughly,...
Definition: chain_vine_swap.h:391
typename Master_matrix::Index Index
Definition: chain_vine_swap.h:242
typename Master_matrix::ID_index ID_index
Definition: chain_vine_swap.h:243
friend void swap(Chain_vine_swap &swap1, Chain_vine_swap &swap2)
Swap operator.
Definition: chain_vine_swap.h:315
typename Master_matrix::Column Column
Definition: chain_vine_swap.h:246
bool(* EventCompFuncPointer)(Pos_index, Pos_index)
Definition: chain_vine_swap.h:247
Index vine_swap_with_z_eq_1_case(Index columnIndex1, Index columnIndex2)
Does the same than vine_swap, but assumes that the swap is non trivial and therefore skips a part of ...
Definition: chain_vine_swap.h:375
constexpr bool _no_G_death_comparator(unsigned int columnIndex1, unsigned int columnIndex2)
Default death comparator. Simply assumes that two positive paired columns are never swapped....
Definition: chain_vine_swap.h:43
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14
STL namespace.
Empty structure. Inherited instead of Chain_barcode_swap, when the barcode is not stored.
Definition: chain_vine_swap.h:72
Empty structure. Inherited instead of Chain_vine_swap, when vine swaps are not enabled.
Definition: chain_vine_swap.h:56