multipers 2.3.0__cp310-cp310-win_amd64.whl → 2.3.2b1__cp310-cp310-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of multipers might be problematic. Click here for more details.

Files changed (54) hide show
  1. multipers/_signed_measure_meta.py +71 -65
  2. multipers/array_api/__init__.py +39 -0
  3. multipers/array_api/numpy.py +34 -0
  4. multipers/array_api/torch.py +35 -0
  5. multipers/distances.py +6 -2
  6. multipers/{ml/convolutions.py → filtrations/density.py} +67 -13
  7. multipers/filtrations/filtrations.py +76 -17
  8. multipers/function_rips.cp310-win_amd64.pyd +0 -0
  9. multipers/grids.cp310-win_amd64.pyd +0 -0
  10. multipers/grids.pyx +144 -61
  11. multipers/gudhi/Simplex_tree_multi_interface.h +36 -1
  12. multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
  13. multipers/gudhi/gudhi/One_critical_filtration.h +18 -9
  14. multipers/gudhi/mma_interface_h0.h +1 -1
  15. multipers/gudhi/mma_interface_matrix.h +10 -1
  16. multipers/gudhi/naive_merge_tree.h +1 -1
  17. multipers/gudhi/truc.h +555 -42
  18. multipers/io.cp310-win_amd64.pyd +0 -0
  19. multipers/io.pyx +26 -93
  20. multipers/ml/mma.py +3 -3
  21. multipers/ml/point_clouds.py +2 -2
  22. multipers/ml/signed_measures.py +63 -65
  23. multipers/mma_structures.cp310-win_amd64.pyd +0 -0
  24. multipers/mma_structures.pxd +2 -1
  25. multipers/mma_structures.pyx +56 -16
  26. multipers/mma_structures.pyx.tp +14 -5
  27. multipers/multiparameter_module_approximation/approximation.h +48 -14
  28. multipers/multiparameter_module_approximation.cp310-win_amd64.pyd +0 -0
  29. multipers/multiparameter_module_approximation.pyx +25 -7
  30. multipers/plots.py +2 -1
  31. multipers/point_measure.cp310-win_amd64.pyd +0 -0
  32. multipers/point_measure.pyx +6 -2
  33. multipers/simplex_tree_multi.cp310-win_amd64.pyd +0 -0
  34. multipers/simplex_tree_multi.pxd +1 -0
  35. multipers/simplex_tree_multi.pyx +584 -142
  36. multipers/simplex_tree_multi.pyx.tp +80 -23
  37. multipers/slicer.cp310-win_amd64.pyd +0 -0
  38. multipers/slicer.pxd +799 -197
  39. multipers/slicer.pxd.tp +24 -5
  40. multipers/slicer.pyx +5653 -1426
  41. multipers/slicer.pyx.tp +208 -48
  42. multipers/tbb12.dll +0 -0
  43. multipers/tbbbind_2_5.dll +0 -0
  44. multipers/tbbmalloc.dll +0 -0
  45. multipers/tbbmalloc_proxy.dll +0 -0
  46. multipers/tensor/tensor.h +1 -1
  47. multipers/tests/__init__.py +9 -4
  48. multipers/torch/diff_grids.py +30 -7
  49. multipers/torch/rips_density.py +1 -1
  50. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/METADATA +4 -25
  51. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/RECORD +54 -51
  52. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/WHEEL +1 -1
  53. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info/licenses}/LICENSE +0 -0
  54. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/top_level.txt +0 -0
multipers/gudhi/truc.h CHANGED
@@ -1,21 +1,27 @@
1
1
  #pragma once
2
+ #include "gudhi/Matrix.h"
3
+ #include "gudhi/mma_interface_matrix.h"
2
4
  #include "gudhi/Multi_persistence/Line.h"
3
5
  #include "multiparameter_module_approximation/format_python-cpp.h"
4
6
  #include <gudhi/One_critical_filtration.h>
5
7
  #include <gudhi/Multi_critical_filtration.h>
6
8
  #include <algorithm>
7
- #include <boost/mpl/aux_/na_fwd.hpp>
8
9
  #include <cassert>
10
+ #include <csignal>
9
11
  #include <cstddef>
10
12
  #include <cstdint>
11
- // #include <gudhi/Simplex_tree/multi_filtrations/Finitely_critical_filtrations.h>
12
13
  #include <iostream>
13
14
  #include <limits>
14
15
  #include <numeric>
15
16
  #include <oneapi/tbb/enumerable_thread_specific.h>
16
17
  #include <oneapi/tbb/parallel_for.h>
18
+ #include <oneapi/tbb/parallel_sort.h>
17
19
  #include <oneapi/tbb/task_group.h>
20
+ #include <oneapi/tbb/mutex.h>
21
+ #include <ostream>
22
+ #include <ranges>
18
23
  #include <sstream>
24
+ #include <stdexcept>
19
25
  #include <string>
20
26
  #include <utility>
21
27
  #include <vector>
@@ -27,6 +33,12 @@ namespace multiparameter {
27
33
  namespace truc_interface {
28
34
  using index_type = std::uint32_t;
29
35
 
36
+ template <typename T, typename = void>
37
+ struct has_columns : std::false_type {};
38
+
39
+ template <typename T>
40
+ struct has_columns<T, std::void_t<typename T::options>> : std::true_type {};
41
+
30
42
  class PresentationStructure {
31
43
  public:
32
44
  PresentationStructure() {}
@@ -71,9 +83,10 @@ class PresentationStructure {
71
83
 
72
84
  inline friend std::ostream &operator<<(std::ostream &stream, const PresentationStructure &structure) {
73
85
  stream << "Boundary:\n";
74
- stream << "{";
75
- for (const auto &stuff : structure.generators) {
76
- stream << "{";
86
+ stream << "{\n";
87
+ for (auto i : std::views::iota(0u, structure.size())) {
88
+ const auto &stuff = structure.generators[i];
89
+ stream << i << ": {";
77
90
  for (auto truc : stuff) stream << truc << ", ";
78
91
 
79
92
  if (!stuff.empty()) stream << "\b" << "\b ";
@@ -116,6 +129,32 @@ class PresentationStructure {
116
129
  return idx;
117
130
  }
118
131
 
132
+ PresentationStructure permute(const std::vector<index_type> &order) const {
133
+ if (order.size() > generators.size()) {
134
+ throw std::invalid_argument("Permutation order must have the same size as the number of generators.");
135
+ }
136
+ index_type flag = -1;
137
+ std::vector<index_type> inverse_order(generators.size(), flag);
138
+ for (std::size_t i = 0; i < order.size(); i++) {
139
+ inverse_order[order[i]] = i;
140
+ }
141
+ std::vector<std::vector<index_type>> new_generators(order.size());
142
+ std::vector<int> new_generator_dimensions(order.size());
143
+
144
+ for (auto i : std::views::iota(0u, order.size())) {
145
+ new_generators[i].reserve(generators[order[i]].size());
146
+ for (std::size_t j = 0; j < generators[order[i]].size(); j++) {
147
+ index_type stuff = inverse_order[generators[order[i]][j]];
148
+ if (stuff != flag) new_generators[i].push_back(stuff);
149
+ }
150
+ std::sort(new_generators[i].begin(), new_generators[i].end());
151
+ new_generator_dimensions[i] = generator_dimensions[order[i]];
152
+ }
153
+ return PresentationStructure(new_generators, new_generator_dimensions);
154
+ }
155
+
156
+ void update_matrix(std::vector<std::vector<index_type>> &new_gens) { std::swap(generators, new_gens); }
157
+
119
158
  private:
120
159
  std::vector<std::vector<index_type>> generators;
121
160
  std::vector<int> generator_dimensions;
@@ -184,7 +223,7 @@ class SimplicialStructure {
184
223
 
185
224
  inline unsigned int max_dimension() const { return max_dimension_; }
186
225
 
187
- int prune_above_dimension(int dim) { throw; }
226
+ int prune_above_dimension([[maybe_unused]] int dim) { throw "Not implemented"; }
188
227
 
189
228
  private:
190
229
  std::vector<std::vector<index_type>> boundaries;
@@ -196,9 +235,11 @@ template <class PersBackend, class Structure, class MultiFiltration>
196
235
  class Truc {
197
236
  public:
198
237
  using Filtration_value = MultiFiltration;
238
+ using MultiFiltrations = std::vector<MultiFiltration>;
199
239
  using value_type = typename MultiFiltration::value_type;
200
240
  using split_barcode =
201
241
  std::vector<std::vector<std::pair<typename MultiFiltration::value_type, typename MultiFiltration::value_type>>>;
242
+ using split_barcode_idx = std::vector<std::vector<std::pair<int, int>>>;
202
243
  template <typename value_type = value_type>
203
244
  using flat_barcode = std::vector<std::pair<int, std::pair<value_type, value_type>>>;
204
245
 
@@ -238,18 +279,389 @@ class Truc {
238
279
  std::iota(generator_order.begin(), generator_order.end(), 0); // range
239
280
  }
240
281
 
241
- Truc() {};
242
-
243
- Truc &operator=(const Truc &other) {
244
- generator_filtration_values = other.generator_filtration_values;
245
- generator_order = other.generator_order;
246
- structure = other.structure;
247
- persistence = other.persistence;
248
- filtration_container = other.filtration_container;
282
+ Truc(const Truc &other)
283
+ : generator_filtration_values(other.generator_filtration_values),
284
+ generator_order(other.generator_order),
285
+ structure(other.structure),
286
+ filtration_container(other.filtration_container),
287
+ persistence(other.persistence) {
249
288
  persistence._update_permutation_ptr(generator_order);
289
+ }
290
+
291
+ Truc &operator=(Truc other) {
292
+ if (this != &other) {
293
+ generator_filtration_values = other.generator_filtration_values;
294
+ generator_order = other.generator_order;
295
+ structure = other.structure;
296
+ filtration_container = other.filtration_container;
297
+ persistence = other.persistence;
298
+ persistence._update_permutation_ptr(generator_order);
299
+ }
250
300
  return *this;
301
+ }
302
+
303
+ Truc() {};
304
+
305
+ inline bool dimension_order(const index_type &i, const index_type &j) const {
306
+ return structure.dimension(i) < structure.dimension(j);
307
+ };
308
+
309
+ inline bool colexical_order(const index_type &i, const index_type &j) const {
310
+ if (structure.dimension(i) > structure.dimension(j)) return false;
311
+ if (structure.dimension(i) < structure.dimension(j)) return true;
312
+ if constexpr (MultiFiltration::is_multicritical()) // TODO : this may not be the best
313
+ throw "Not implemented in the multicritical case";
314
+
315
+ for (int idx = generator_filtration_values[i].num_parameters() - 1; idx >= 0; --idx) {
316
+ if (generator_filtration_values[i][idx] < generator_filtration_values[j][idx])
317
+ return true;
318
+ else if (generator_filtration_values[i][idx] > generator_filtration_values[j][idx])
319
+ return false;
320
+ }
321
+ return false;
322
+ };
323
+
324
+ // TODO : inside of MultiFiltration
325
+ inline static bool lexical_order(const MultiFiltration &a, const MultiFiltration &b) {
326
+ if constexpr (MultiFiltration::is_multicritical()) // TODO : this may not be the best
327
+ throw "Not implemented in the multicritical case";
328
+ if (a.is_plus_inf() || a.is_nan() || b.is_minus_inf()) return false;
329
+ if (b.is_plus_inf() || b.is_nan() || a.is_minus_inf()) return true;
330
+ for (auto idx = 0u; idx < a.num_parameters(); ++idx) {
331
+ if (a[idx] < b[idx])
332
+ return true;
333
+ else if (a[idx] > b[idx])
334
+ return false;
335
+ }
336
+ return false;
337
+ };
338
+
339
+ inline bool lexical_order(const index_type &i, const index_type &j) const {
340
+ if (structure.dimension(i) > structure.dimension(j)) return false;
341
+ if (structure.dimension(i) < structure.dimension(j)) return true;
342
+ if constexpr (MultiFiltration::is_multicritical()) // TODO : this may not be the best
343
+ throw "Not implemented in the multicritical case";
344
+
345
+ for (int idx = 0; idx < generator_filtration_values[i].num_parameters(); ++idx) {
346
+ if (generator_filtration_values[i][idx] < generator_filtration_values[j][idx])
347
+ return true;
348
+ else if (generator_filtration_values[i][idx] > generator_filtration_values[j][idx])
349
+ return false;
350
+ }
351
+ return false;
251
352
  };
252
353
 
354
+ inline Truc permute(const std::vector<index_type> &permutation) const {
355
+ auto num_new_gen = permutation.size();
356
+ if (permutation.size() > this->num_generators()) {
357
+ throw std::invalid_argument("Invalid permutation size. Got " + std::to_string(num_new_gen) + " expected " +
358
+ std::to_string(this->num_generators()) + ".");
359
+ }
360
+ std::vector<MultiFiltration> new_filtration(num_new_gen);
361
+ for (auto i : std::views::iota(0u, num_new_gen)) { // assumes permutation has correct indices.
362
+ new_filtration[i] = generator_filtration_values[permutation[i]];
363
+ }
364
+ return Truc(structure.permute(permutation), new_filtration);
365
+ }
366
+
367
+ template <class Fun>
368
+ inline std::pair<Truc, std::vector<index_type>> rearange_sort(const Fun &&fun) const {
369
+ std::vector<index_type> permutation(generator_order.size());
370
+ std::iota(permutation.begin(), permutation.end(), 0);
371
+ tbb::parallel_sort(permutation.begin(), permutation.end(), [&](std::size_t i, std::size_t j) { return fun(i, j); });
372
+ return {permute(permutation), permutation};
373
+ }
374
+
375
+ std::pair<Truc, std::vector<index_type>> colexical_rearange() const {
376
+ return rearange_sort([this](std::size_t i, std::size_t j) { return this->colexical_order(i, j); });
377
+ }
378
+
379
+ template <bool generator_only = false>
380
+ std::conditional_t<generator_only, std::pair<std::vector<std::vector<index_type>>, MultiFiltrations>, Truc>
381
+ projective_cover_kernel(int dim) {
382
+ if constexpr (MultiFiltration::is_multicritical() || !std::is_same_v<Structure, PresentationStructure> ||
383
+ !has_columns<PersBackend>::value) // TODO : this may not be the best
384
+ {
385
+ throw std::invalid_argument("Not implemented for this Truc");
386
+ } else {
387
+ // TODO : this only works for 2 parameter modules. Optimize w.r.t. this.
388
+ const bool verbose = false;
389
+ // filtration values are assumed to be dim + colexicographically sorted
390
+ // vector seem to be good here
391
+ using SmallMatrix = Gudhi::persistence_matrix::Matrix<
392
+ Gudhi::multiparameter::truc_interface::fix_presentation_options<PersBackend::options::column_type, false>>;
393
+
394
+ int nd = 0;
395
+ int ndpp = 0;
396
+ for (auto i : std::views::iota(0u, structure.size())) {
397
+ if (structure.dimension(i) == dim) {
398
+ nd++;
399
+ } else if (structure.dimension(i) == dim + 1) {
400
+ ndpp++;
401
+ } else {
402
+ throw std::invalid_argument("This truc contains bad dimensions. Got " +
403
+ std::to_string(structure.dimension(i)) + " expected " + std::to_string(dim) +
404
+ " or " + std::to_string(dim + 1) + " in position " + std::to_string(i) + " .");
405
+ }
406
+ }
407
+ if (ndpp == 0)
408
+ throw std::invalid_argument("Given dimension+1 has no simplices. Got " + std::to_string(nd) + " " +
409
+ std::to_string(ndpp) + ".");
410
+ // lexico iterator
411
+ auto lex_cmp = [&](const MultiFiltration &a, const MultiFiltration &b) { return lexical_order(a, b); };
412
+
413
+ struct SmallQueue {
414
+ SmallQueue() {};
415
+
416
+ struct MFWrapper {
417
+ MFWrapper(const MultiFiltration &g) : g(g) {};
418
+
419
+ MFWrapper(const MultiFiltration &g, int col) : g(g) { some_cols.insert(col); }
420
+
421
+ MFWrapper(const MultiFiltration &g, std::initializer_list<int> cols)
422
+ : g(g), some_cols(cols.begin(), cols.end()) {}
423
+
424
+ inline void insert(int col) const { some_cols.insert(col); }
425
+
426
+ inline bool operator<(const MFWrapper &other) const { return lexical_order(g, other.g); }
427
+
428
+ public:
429
+ MultiFiltration g;
430
+ mutable std::set<int> some_cols;
431
+ };
432
+
433
+ inline void insert(const MultiFiltration &g, int col) {
434
+ auto it = queue.find(g);
435
+ if (it != queue.end()) {
436
+ it->insert(col);
437
+ } else {
438
+ queue.emplace(g, col);
439
+ }
440
+ };
441
+
442
+ inline void insert(const MultiFiltration &g, const std::initializer_list<int> &cols) {
443
+ auto it = queue.find(g);
444
+ if (it != queue.end()) {
445
+ for (int c : cols) it->insert(c);
446
+ } else {
447
+ queue.emplace(g, cols);
448
+ }
449
+ };
450
+
451
+ inline bool empty() const { return queue.empty(); }
452
+
453
+ inline MultiFiltration pop() {
454
+ if (queue.empty()) [[unlikely]]
455
+ throw std::runtime_error("Queue is empty");
456
+
457
+ auto out = std::move(*queue.begin());
458
+ queue.erase(queue.begin());
459
+ std::swap(last_cols, out.some_cols);
460
+ return out.g;
461
+ }
462
+
463
+ const auto &get_current_cols() const { return last_cols; }
464
+
465
+ private:
466
+ std::set<MFWrapper> queue;
467
+ std::set<int> last_cols;
468
+ };
469
+
470
+ SmallQueue lexico_it;
471
+ SmallMatrix M(nd + ndpp);
472
+ for (int i = 0; i < nd + ndpp; i++) {
473
+ const auto &b = structure[i];
474
+ M.insert_boundary(b);
475
+ }
476
+ SmallMatrix N(nd + ndpp); // slave
477
+ for (auto i : std::views::iota(0u, static_cast<unsigned int>(nd + ndpp))) {
478
+ N.insert_boundary({i});
479
+ };
480
+
481
+ auto get_fil = [&](int i) -> MultiFiltration & { return generator_filtration_values[i]; };
482
+ auto get_pivot = [&](int j) -> int {
483
+ const auto &col = M.get_column(j);
484
+ return col.size() > 0 ? (*col.rbegin()).get_row_index() : -1;
485
+ };
486
+
487
+ if constexpr (verbose) {
488
+ std::cout << "Initial matrix (" << nd << " + " << ndpp << "):" << std::endl;
489
+ for (int i = 0; i < nd + ndpp; i++) {
490
+ std::cout << "Column " << i << " : {";
491
+ for (const auto &j : M.get_column(i)) std::cout << j << " ";
492
+ std::cout << "} | " << get_fil(i) << std::endl;
493
+ }
494
+ }
495
+
496
+ // TODO : pivot caches are small : maybe use a flat container instead ?
497
+ std::vector<std::set<int>> pivot_cache(nd + ndpp); // this[pivot] = cols of given pivot (<=nd)
498
+ std::vector<bool> reduced_columns(nd + ndpp); // small cache
499
+ MultiFiltration grid_value;
500
+
501
+ std::vector<std::vector<index_type>> out_structure;
502
+ out_structure.reserve(2 * ndpp);
503
+ std::vector<MultiFiltration> out_filtration;
504
+ out_filtration.reserve(2 * ndpp);
505
+ std::vector<int> out_dimension;
506
+ out_dimension.reserve(2 * ndpp);
507
+ if constexpr (!generator_only) {
508
+ for (auto i : std::views::iota(nd, nd + ndpp)) {
509
+ out_structure.push_back({});
510
+ out_filtration.push_back(this->get_filtration_values()[i]);
511
+ out_dimension.push_back(this->structure.dimension(i));
512
+ }
513
+ }
514
+ // pivot cache
515
+ if constexpr (verbose) {
516
+ std::cout << "Initial pivot cache:\n";
517
+ }
518
+ for (int j : std::views::iota(nd, nd + ndpp)) {
519
+ int col_pivot = get_pivot(j);
520
+ if (col_pivot < 0) {
521
+ reduced_columns[j] = true;
522
+ continue;
523
+ };
524
+ auto &current_pivot_cache = pivot_cache[col_pivot];
525
+ current_pivot_cache.emplace_hint(current_pivot_cache.cend(), j); // j is increasing
526
+ }
527
+ if constexpr (verbose) {
528
+ int i = 0;
529
+ for (const auto &cols : pivot_cache) {
530
+ std::cout << " - " << i++ << " : ";
531
+ for (const auto &col : cols) {
532
+ std::cout << col << " ";
533
+ }
534
+ std::cout << std::endl;
535
+ }
536
+ }
537
+
538
+ // if constexpr (!use_grid) {
539
+ if constexpr (verbose) std::cout << "Initial grid queue:\n";
540
+ for (int j : std::views::iota(nd, nd + ndpp)) {
541
+ int col_pivot = get_pivot(j);
542
+ if (col_pivot < 0) continue;
543
+ lexico_it.insert(get_fil(j), j);
544
+ auto it = pivot_cache[col_pivot].find(j);
545
+ if (it == pivot_cache[col_pivot].end()) [[unlikely]]
546
+ throw std::runtime_error("Column " + std::to_string(j) + " not in pivot cache");
547
+ it++;
548
+ // for (int k : pivot_cache[col_pivot]) {
549
+ for (auto _k = it; _k != pivot_cache[col_pivot].end(); ++_k) {
550
+ int k = *_k;
551
+ if (k <= j) [[unlikely]]
552
+ throw std::runtime_error("Column " + std::to_string(k) + " is not a future column");
553
+ auto prev = get_fil(k);
554
+ prev.push_to_least_common_upper_bound(get_fil(j));
555
+ if constexpr (verbose) std::cout << " - (" << j << ", " << k << ") are interacting at " << prev << "\n";
556
+ lexico_it.insert(std::move(prev), k);
557
+ }
558
+ }
559
+ // TODO : check poset cache ?
560
+ if constexpr (verbose) std::cout << std::flush;
561
+ // }
562
+ auto reduce_column = [&](int j) -> bool {
563
+ int pivot = get_pivot(j);
564
+ if constexpr (verbose) std::cout << "Reducing column " << j << " with pivot " << pivot << "\n";
565
+ if (pivot < 0) {
566
+ if (!reduced_columns[j]) {
567
+ std::vector<index_type> _b(N.get_column(j).begin(), N.get_column(j).end());
568
+ for (auto &stuff : _b) stuff -= nd;
569
+ out_structure.push_back(std::move(_b));
570
+ out_filtration.emplace_back(grid_value.begin(), grid_value.end());
571
+ if constexpr (!generator_only) out_dimension.emplace_back(this->structure.dimension(j) + 1);
572
+ reduced_columns[j] = true;
573
+ }
574
+ return false;
575
+ }
576
+ if constexpr (verbose) std::cout << "Previous registered pivot : " << *pivot_cache[pivot].begin() << std::endl;
577
+ // WARN : we lazy update variables linked with col j...
578
+ if (pivot_cache[pivot].size() == 0) {
579
+ return false;
580
+ }
581
+ for (int k : pivot_cache[pivot]) {
582
+ if (k >= j) { // cannot reduce more here. this is a (local) pivot.
583
+ return false;
584
+ }
585
+ if (get_fil(k) <= grid_value) {
586
+ M.add_to(k, j);
587
+ N.add_to(k, j);
588
+ // std::cout << "Adding " << k << " to " << j << " at grid time " << grid_value << std::endl;
589
+ pivot_cache[pivot].erase(j);
590
+ // WARN : we update the pivot cache after the update loop
591
+ if (get_pivot(j) >= pivot) {
592
+ throw std::runtime_error("Addition failed ? current " + std::to_string(get_pivot(j)) + " previous " +
593
+ std::to_string(pivot));
594
+ }
595
+ return true; // pivot has changed
596
+ }
597
+ }
598
+ return false; // for loop exhausted (j may not be there because of lazy)
599
+ };
600
+ auto chores_after_new_pivot = [&](int j) {
601
+ int col_pivot = get_pivot(j);
602
+ if (col_pivot < 0) {
603
+ if (!reduced_columns[j]) throw std::runtime_error("Empty column should have been detected before");
604
+ return;
605
+ };
606
+ auto [it, was_there] = pivot_cache[col_pivot].insert(j);
607
+ it++;
608
+ // if constexpr (!use_grid) {
609
+ for (auto _k = it; _k != pivot_cache[col_pivot].end(); ++_k) {
610
+ int k = *_k;
611
+ if (k <= j) [[unlikely]]
612
+ throw std::runtime_error("(chores) col " + std::to_string(k) + " is not a future column");
613
+ if (get_fil(k) >= get_fil(j)) continue;
614
+ auto prev = get_fil(k);
615
+ prev.push_to_least_common_upper_bound(get_fil(j));
616
+ if (lex_cmp(grid_value, prev)) {
617
+ if constexpr (verbose)
618
+ std::cout << "(chores) Updating grid queue, (" << j << ", " << k << ") are interacting at " << prev
619
+ << std::endl;
620
+ lexico_it.insert(prev, k);
621
+ }
622
+ }
623
+ // }
624
+ };
625
+ if constexpr (verbose) {
626
+ std::cout << "Initially reduced columns: [";
627
+ for (int i = 0; i < nd + ndpp; i++) {
628
+ std::cout << reduced_columns[i] << ", ";
629
+ }
630
+ std::cout << "]" << std::endl;
631
+ }
632
+ while (!lexico_it.empty()) {
633
+ // if constexpr (use_grid) {
634
+ // grid_value = lexico_it.next();
635
+ // } else {
636
+ grid_value = std::move(lexico_it.pop());
637
+ // }
638
+ if constexpr (verbose) {
639
+ std::cout << "Grid value: " << grid_value << std::endl;
640
+ std::cout << " Reduced cols: ";
641
+ for (int i = 0; i < nd + ndpp; i++) {
642
+ std::cout << reduced_columns[i] << ", ";
643
+ }
644
+ std::cout << "]" << std::endl;
645
+ }
646
+
647
+ for (int i : lexico_it.get_current_cols()) {
648
+ if constexpr (false) {
649
+ if ((reduced_columns[i] || !(get_fil(i) <= grid_value))) continue;
650
+ if ((get_fil(i) > grid_value)) break;
651
+ }
652
+ while (reduce_column(i));
653
+ chores_after_new_pivot(i);
654
+ }
655
+ }
656
+ // std::cout<< grid_.str() << std::endl;
657
+ if constexpr (generator_only)
658
+ return {out_structure, out_dimension};
659
+ else {
660
+ return Truc(out_structure, out_dimension, out_filtration);
661
+ }
662
+ }
663
+ }
664
+
253
665
  template <bool ignore_inf>
254
666
  std::vector<std::pair<int, std::vector<index_type>>> get_current_boundary_matrix() {
255
667
  std::vector<index_type> permutation(generator_order.size());
@@ -261,9 +673,9 @@ class Truc {
261
673
  return filtration_container[val] == MultiFiltration::Generator::T_inf;
262
674
  }),
263
675
  permutation.end());
264
- std::sort(permutation.begin(), permutation.end());
676
+ tbb::parallel_sort(permutation.begin(), permutation.end());
265
677
  }
266
- std::sort(permutation.begin(), permutation.end(), [&](std::size_t i, std::size_t j) {
678
+ tbb::parallel_sort(permutation.begin(), permutation.end(), [&](std::size_t i, std::size_t j) {
267
679
  if (structure.dimension(i) > structure.dimension(j)) return false;
268
680
  if (structure.dimension(i) < structure.dimension(j)) return true;
269
681
  return filtration_container[i] < filtration_container[j];
@@ -320,7 +732,9 @@ class Truc {
320
732
 
321
733
  template <class array1d>
322
734
  inline void set_one_filtration(const array1d &truc) {
323
- assert(truc.size() == this->num_generators());
735
+ if (truc.size() != this->num_generators())
736
+ throw std::invalid_argument("(setting one filtration) Bad size. Got " + std::to_string(truc.size()) +
737
+ " expected " + std::to_string(this->num_generators()));
324
738
  this->filtration_container = truc;
325
739
  }
326
740
 
@@ -334,7 +748,7 @@ class Truc {
334
748
  const bool ignore_inf) const { // needed ftm as PersBackend only points there
335
749
  constexpr const bool verbose = false;
336
750
  if (one_filtration.size() != this->num_generators()) {
337
- throw;
751
+ throw std::runtime_error("The one parameter filtration doesn't have a proper size.");
338
752
  }
339
753
  out_gen_order.resize(this->num_generators());
340
754
  std::iota(out_gen_order.begin(),
@@ -375,7 +789,7 @@ class Truc {
375
789
  return PersBackend(structure, out_gen_order);
376
790
  }
377
791
 
378
- inline const bool has_persistence() { return this->persistence.size(); };
792
+ inline bool has_persistence() const { return this->persistence.size(); };
379
793
 
380
794
  inline void compute_persistence(const bool ignore_inf = true) {
381
795
  this->persistence = this->compute_persistence_out(
@@ -419,6 +833,49 @@ class Truc {
419
833
  vineyard_update(this->persistence, this->filtration_container, this->generator_order);
420
834
  }
421
835
 
836
+ inline split_barcode_idx get_barcode_idx(
837
+ PersBackend &persistence) const {
838
+ auto barcode_indices = persistence.get_barcode();
839
+ split_barcode_idx out(this->structure.max_dimension() + 1); // TODO : This doesn't allow for negative dimensions
840
+ for (const auto &bar : barcode_indices) {
841
+ int death = bar.death == static_cast<typename PersBackend::pos_index>(-1) ? -1 : bar.death;
842
+ out[bar.dim].push_back({bar.birth, death});
843
+ }
844
+ return out;
845
+ }
846
+
847
+ // puts the degree-ordered bc starting out_ptr, and returns the "next" pointer.
848
+ // corresond to an array of shape (num_bar, 2);
849
+ template <bool return_shape = false>
850
+ inline std::conditional_t<return_shape, std::pair<std::vector<int>, int*>, int*> get_barcode_idx(
851
+ PersBackend &persistence,
852
+ int *start_ptr) const {
853
+ const auto &bc = persistence.barcode();
854
+ if (bc.size() == 0) return start_ptr;
855
+ std::vector<int> shape(this->structure.max_dimension());
856
+ for (const auto &b : bc) shape[b.dim]++;
857
+ // dim in barcode may be unsorted...
858
+ std::vector<int *> ptr_shifts(shape.size());
859
+ int shape_cumsum = 0;
860
+ for (auto i : std::views::iota(0u, bc.size())) {
861
+ if (i != 0u) shape_cumsum += shape[i - 1];
862
+ // 2 for (birth, death)
863
+ ptr_shifts[i] = 2 * shape_cumsum + start_ptr;
864
+ }
865
+ for (const auto &b : bc) {
866
+ int *current_loc = ptr_shifts[b.dim];
867
+ *(current_loc++) = b.birth;
868
+ *(current_loc++) = b.death == static_cast<typename PersBackend::pos_index>(-1) ? -1 : b.death;
869
+ }
870
+
871
+ if constexpr (return_shape)
872
+ return {shape, ptr_shifts.back()};
873
+ else
874
+ return ptr_shifts.back();
875
+ }
876
+
877
+
878
+
422
879
  inline split_barcode get_barcode(
423
880
  PersBackend &persistence,
424
881
  const std::vector<typename MultiFiltration::value_type> &filtration_container) const {
@@ -454,7 +911,7 @@ class Truc {
454
911
  << " --" << bar.death << "(" << death_filtration << ")"
455
912
  << " dim " << bar.dim << std::endl;
456
913
  }
457
- if (birth_filtration < death_filtration)
914
+ if (birth_filtration <= death_filtration)
458
915
  out[bar.dim].push_back({birth_filtration, death_filtration});
459
916
  else {
460
917
  out[bar.dim].push_back({inf, inf});
@@ -465,6 +922,8 @@ class Truc {
465
922
 
466
923
  inline split_barcode get_barcode() { return get_barcode(this->persistence, this->filtration_container); }
467
924
 
925
+ inline split_barcode_idx get_barcode_idx() { return get_barcode_idx(this->persistence); }
926
+
468
927
  template <typename value_type = value_type>
469
928
  static inline flat_nodim_barcode<value_type> get_flat_nodim_barcode(
470
929
  PersBackend &persistence,
@@ -492,7 +951,7 @@ class Truc {
492
951
  << std::endl;
493
952
  }
494
953
 
495
- if (birth_filtration < death_filtration)
954
+ if (birth_filtration <= death_filtration)
496
955
  out[idx] = {birth_filtration, death_filtration};
497
956
  else {
498
957
  out[idx] = {inf, inf};
@@ -529,7 +988,7 @@ class Truc {
529
988
  << std::endl;
530
989
  }
531
990
 
532
- if (birth_filtration < death_filtration)
991
+ if (birth_filtration <= death_filtration)
533
992
  out[idx] = {bar.dim, {birth_filtration, death_filtration}};
534
993
  else {
535
994
  out[idx] = {bar.dim, {inf, inf}};
@@ -635,6 +1094,8 @@ class Truc {
635
1094
  return out;
636
1095
  }
637
1096
 
1097
+ inline int get_dimension(int i) const { return structure.dimension(i); }
1098
+
638
1099
  inline void prune_above_dimension(int max_dim) {
639
1100
  int idx = structure.prune_above_dimension(max_dim);
640
1101
  generator_filtration_values.resize(idx);
@@ -651,12 +1112,15 @@ class Truc {
651
1112
  return out;
652
1113
  }
653
1114
 
654
- auto coarsen_on_grid(const std::vector<std::vector<typename MultiFiltration::value_type>> grid) {
1115
+ auto coarsen_on_grid(const std::vector<std::vector<typename MultiFiltration::value_type>>& grid) {
655
1116
  using return_type = decltype(std::declval<MultiFiltration>().template as_type<std::int32_t>());
656
1117
  std::vector<return_type> coords(this->num_generators());
657
- for (std::size_t gen = 0u; gen < coords.size(); ++gen) {
1118
+ // for (std::size_t gen = 0u; gen < coords.size(); ++gen) { // TODO : parallelize
1119
+ // coords[gen] = compute_coordinates_in_grid<int32_t>(generator_filtration_values[gen], grid);
1120
+ // }
1121
+ tbb::parallel_for(static_cast<std::size_t>(0u), coords.size(), [&](std::size_t gen){
658
1122
  coords[gen] = compute_coordinates_in_grid<int32_t>(generator_filtration_values[gen], grid);
659
- }
1123
+ });
660
1124
  return Truc<PersBackend, Structure, return_type>(structure, coords);
661
1125
  }
662
1126
 
@@ -718,7 +1182,7 @@ class Truc {
718
1182
 
719
1183
  inline TrucThread weak_copy() const { return TrucThread(*truc_ptr); }
720
1184
 
721
- inline const bool has_persistence() { return this->persistence.size(); };
1185
+ inline bool has_persistence() const { return this->persistence.size(); };
722
1186
 
723
1187
  inline const PersBackend &get_persistence() const { return persistence; }
724
1188
 
@@ -769,6 +1233,10 @@ class Truc {
769
1233
 
770
1234
  inline split_barcode get_barcode() { return truc_ptr->get_barcode(this->persistence, this->filtration_container); }
771
1235
 
1236
+ inline split_barcode_idx get_barcode_idx() {
1237
+ return truc_ptr->get_barcode_idx(this->persistence);
1238
+ }
1239
+
772
1240
  inline std::size_t num_generators() const { return this->truc_ptr->structure.size(); }
773
1241
 
774
1242
  inline std::size_t num_parameters() const {
@@ -783,6 +1251,14 @@ class Truc {
783
1251
  return this->filtration_container;
784
1252
  }
785
1253
 
1254
+ template <class array1d>
1255
+ inline void set_one_filtration(const array1d &truc) {
1256
+ if (truc.size() != this->num_generators())
1257
+ throw std::invalid_argument("(setting one filtration) Bad size. Got " + std::to_string(truc.size()) +
1258
+ " expected " + std::to_string(this->num_generators()));
1259
+ this->filtration_container = truc;
1260
+ }
1261
+
786
1262
  private:
787
1263
  const Truc *truc_ptr;
788
1264
  std::vector<index_type> generator_order; // size fixed at construction time,
@@ -795,21 +1271,34 @@ class Truc {
795
1271
  * returns barcodes of the f(multipers)
796
1272
  *
797
1273
  */
798
- template <typename Fun, typename Fun_arg>
799
- inline std::vector<split_barcode> barcodes(Fun &&f, const std::vector<Fun_arg> &args, const bool ignore_inf = true) {
1274
+ template <typename Fun, typename Fun_arg, bool idx = false, bool custom = false>
1275
+ inline std::conditional_t<idx, std::vector<split_barcode_idx>, std::vector<split_barcode>>
1276
+ barcodes(Fun &&f, const std::vector<Fun_arg> &args, const bool ignore_inf = true) {
800
1277
  if (args.size() == 0) {
801
1278
  return {};
802
1279
  }
803
- std::vector<split_barcode> out(args.size());
1280
+ std::conditional_t<idx, std::vector<split_barcode_idx>, std::vector<split_barcode>> out(args.size());
804
1281
 
805
1282
  if constexpr (PersBackend::is_vine) {
806
- this->push_to(f(args[0]));
1283
+ if constexpr (custom)
1284
+ this->set_one_filtration(f(args[0]));
1285
+ else
1286
+ this->push_to(f(args[0]));
807
1287
  this->compute_persistence();
808
- out[0] = this->get_barcode();
1288
+ if constexpr (idx)
1289
+ out[0] = this->get_barcode_idx();
1290
+ else
1291
+ out[0] = this->get_barcode();
809
1292
  for (auto i = 1u; i < args.size(); ++i) {
810
- this->push_to(f(args[i]));
1293
+ if constexpr (custom)
1294
+ this->set_one_filtration(f(args[i]));
1295
+ else
1296
+ this->push_to(f(args[i]));
811
1297
  this->vineyard_update();
812
- out[i] = this->get_barcode();
1298
+ if constexpr (idx)
1299
+ out[i] = this->get_barcode_idx();
1300
+ else
1301
+ out[i] = this->get_barcode();
813
1302
  }
814
1303
 
815
1304
  } else {
@@ -817,9 +1306,15 @@ class Truc {
817
1306
  tbb::enumerable_thread_specific<ThreadSafe> thread_locals(local_template);
818
1307
  tbb::parallel_for(static_cast<std::size_t>(0), args.size(), [&](const std::size_t &i) {
819
1308
  ThreadSafe &s = thread_locals.local();
820
- s.push_to(f(args[i]));
1309
+ if constexpr (custom)
1310
+ s.set_one_filtration(f(args[i]));
1311
+ else
1312
+ s.push_to(f(args[i]));
821
1313
  s.compute_persistence(ignore_inf);
822
- out[i] = s.get_barcode();
1314
+ if constexpr (idx) {
1315
+ out[i] = s.get_barcode_idx();
1316
+ } else
1317
+ out[i] = s.get_barcode();
823
1318
  });
824
1319
  }
825
1320
  return out;
@@ -827,19 +1322,37 @@ class Truc {
827
1322
 
828
1323
  // FOR Python interface, but I'm not fan. Todo: do the lambda function in
829
1324
  // cython?
830
- inline std::vector<split_barcode> persistence_on_lines(const std::vector<std::vector<value_type>> &basepoints) {
1325
+ inline std::vector<split_barcode> persistence_on_lines(const std::vector<std::vector<value_type>> &basepoints,
1326
+ bool ignore_inf) {
831
1327
  return barcodes(
832
1328
  [](const std::vector<value_type> &basepoint) { return Gudhi::multi_persistence::Line<value_type>(basepoint); },
833
- basepoints);
1329
+ basepoints,
1330
+ ignore_inf);
1331
+ }
1332
+
1333
+ inline std::vector<split_barcode_idx> custom_persistences(const value_type *filtrations, int size, bool ignore_inf) {
1334
+ std::vector<const value_type *> args(size);
1335
+ for (auto i = 0; i < size; ++i) args[i] = filtrations + this->num_generators() * i;
1336
+
1337
+ auto fun = [&](const value_type *one_filtration_ptr) {
1338
+ std::vector<value_type> fil(this->num_generators());
1339
+ for (auto i : std::views::iota(0u, this->num_generators())) {
1340
+ fil[i] = *(one_filtration_ptr + i);
1341
+ }
1342
+ return std::move(fil);
1343
+ };
1344
+ return barcodes<decltype(fun), const value_type *, true, true>(std::move(fun), args, ignore_inf);
834
1345
  }
835
1346
 
836
1347
  inline std::vector<split_barcode> persistence_on_lines(
837
- const std::vector<std::pair<std::vector<value_type>, std::vector<value_type>>> &bp_dirs) {
1348
+ const std::vector<std::pair<std::vector<value_type>, std::vector<value_type>>> &bp_dirs,
1349
+ bool ignore_inf) {
838
1350
  return barcodes(
839
1351
  [](const std::pair<std::vector<value_type>, std::vector<value_type>> &bpdir) {
840
1352
  return Gudhi::multi_persistence::Line<value_type>(bpdir.first, bpdir.second);
841
1353
  },
842
- bp_dirs);
1354
+ bp_dirs,
1355
+ ignore_inf);
843
1356
  }
844
1357
 
845
1358
  void build_from_scc_file(const std::string &inFilePath,
@@ -877,9 +1390,9 @@ class Truc {
877
1390
  // bool reverse);
878
1391
 
879
1392
  private:
880
- std::vector<MultiFiltration> generator_filtration_values; // defined at construction time. Const
881
- std::vector<index_type> generator_order; // size fixed at construction time, // TODO : CHANGE THAT TO UINT32
882
- Structure structure; // defined at construction time. Const
1393
+ MultiFiltrations generator_filtration_values; // defined at construction time. Const
1394
+ std::vector<index_type> generator_order; // size fixed at construction time
1395
+ Structure structure; // defined at construction time. Const
883
1396
  std::vector<typename MultiFiltration::value_type> filtration_container; // filtration of the current slice
884
1397
  PersBackend persistence; // generated by the structure, and generator_order.
885
1398