multipers 2.3.1__cp313-cp313-win_amd64.whl → 2.3.2b1__cp313-cp313-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 (49) 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/filtrations/density.py +23 -12
  7. multipers/filtrations/filtrations.py +74 -15
  8. multipers/function_rips.cp313-win_amd64.pyd +0 -0
  9. multipers/grids.cp313-win_amd64.pyd +0 -0
  10. multipers/grids.pyx +144 -61
  11. multipers/gudhi/Simplex_tree_multi_interface.h +35 -0
  12. multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
  13. multipers/gudhi/gudhi/One_critical_filtration.h +17 -9
  14. multipers/gudhi/mma_interface_matrix.h +5 -3
  15. multipers/gudhi/truc.h +488 -42
  16. multipers/io.cp313-win_amd64.pyd +0 -0
  17. multipers/io.pyx +16 -86
  18. multipers/ml/mma.py +3 -3
  19. multipers/ml/signed_measures.py +60 -62
  20. multipers/mma_structures.cp313-win_amd64.pyd +0 -0
  21. multipers/mma_structures.pxd +2 -1
  22. multipers/mma_structures.pyx +56 -12
  23. multipers/mma_structures.pyx.tp +14 -3
  24. multipers/multiparameter_module_approximation/approximation.h +45 -13
  25. multipers/multiparameter_module_approximation.cp313-win_amd64.pyd +0 -0
  26. multipers/multiparameter_module_approximation.pyx +22 -6
  27. multipers/plots.py +1 -0
  28. multipers/point_measure.cp313-win_amd64.pyd +0 -0
  29. multipers/point_measure.pyx +6 -2
  30. multipers/simplex_tree_multi.cp313-win_amd64.pyd +0 -0
  31. multipers/simplex_tree_multi.pxd +1 -0
  32. multipers/simplex_tree_multi.pyx +487 -109
  33. multipers/simplex_tree_multi.pyx.tp +67 -18
  34. multipers/slicer.cp313-win_amd64.pyd +0 -0
  35. multipers/slicer.pxd +719 -237
  36. multipers/slicer.pxd.tp +22 -6
  37. multipers/slicer.pyx +5311 -1364
  38. multipers/slicer.pyx.tp +199 -46
  39. multipers/tbb12.dll +0 -0
  40. multipers/tbbbind_2_5.dll +0 -0
  41. multipers/tbbmalloc.dll +0 -0
  42. multipers/tbbmalloc_proxy.dll +0 -0
  43. multipers/tests/__init__.py +9 -4
  44. multipers/torch/diff_grids.py +30 -7
  45. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/METADATA +4 -25
  46. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/RECORD +49 -46
  47. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/WHEEL +1 -1
  48. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info/licenses}/LICENSE +0 -0
  49. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/top_level.txt +0 -0
multipers/grids.pyx CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  from libc.stdint cimport intptr_t, int32_t, int64_t
3
- from libcpp cimport bool,int,long, float
3
+ from libcpp cimport bool,int, float
4
4
 
5
5
  cimport numpy as cnp
6
6
  import numpy as np
@@ -9,7 +9,8 @@ cnp.import_array()
9
9
 
10
10
  from typing import Iterable,Literal,Optional
11
11
  from itertools import product
12
-
12
+ from multipers.array_api import api_from_tensor, api_from_tensors
13
+ from multipers.array_api import numpy as npapi
13
14
 
14
15
  available_strategies = ["regular","regular_closest", "regular_left", "partition", "quantile", "precomputed"]
15
16
  Lstrategies = Literal["regular","regular_closest", "regular_left", "partition", "quantile", "precomputed"]
@@ -17,13 +18,22 @@ Lstrategies = Literal["regular","regular_closest", "regular_left", "partition",
17
18
  ctypedef fused some_int:
18
19
  int32_t
19
20
  int64_t
20
- int
21
- long
22
21
 
23
22
  ctypedef fused some_float:
24
23
  float
25
24
  double
26
25
 
26
+ def sanitize_grid(grid, bool numpyfy=False):
27
+ if len(grid) == 0:
28
+ raise ValueError("empty filtration grid")
29
+ api = api_from_tensors(*grid)
30
+ if numpyfy:
31
+ grid = tuple(api.asnumpy(g) for g in grid)
32
+ else:
33
+ # copy here may not be necessary, but cheap
34
+ grid = tuple(api.astensor(g) for g in grid)
35
+ assert np.all([g.ndim==1 for g in grid])
36
+ return grid
27
37
 
28
38
  def compute_grid(
29
39
  x,
@@ -63,37 +73,57 @@ def compute_grid(
63
73
 
64
74
 
65
75
  cdef bool is_numpy_compatible = True
66
- if is_slicer(x):
76
+ if (is_slicer(x) or is_simplextree_multi(x)) and x.is_squeezed:
77
+ initial_grid = x.filtration_grid
78
+ api = api_from_tensors(*initial_grid)
79
+ elif is_slicer(x):
67
80
  initial_grid = x.get_filtrations_values().T
81
+ api = npapi
68
82
  elif is_simplextree_multi(x):
69
83
  initial_grid = x.get_filtration_grid()
84
+ api = npapi
70
85
  elif is_mma(x):
71
86
  initial_grid = x.get_filtration_values()
87
+ api = npapi
72
88
  elif isinstance(x, np.ndarray):
73
89
  initial_grid = x
90
+ api = npapi
74
91
  else:
75
92
  x = tuple(x)
76
93
  if len(x) == 0: return []
77
94
  first = x[0]
78
95
  ## is_sm, i.e., iterable tuple(pts,weights)
79
- if isinstance(x[0], tuple) and getattr(x[0][0], "shape", None) is not None:
96
+ if isinstance(first, tuple) and getattr(first[0], "shape", None) is not None:
80
97
  initial_grid = tuple(f[0].T for f in x)
81
- if isinstance(initial_grid[0], np.ndarray):
82
- initial_grid = np.concatenate(initial_grid, axis=1)
83
- else:
84
- is_numpy_compatbile = False
85
- import torch
86
- assert isinstance(first[0], torch.Tensor), "Only numpy and torch are supported ftm."
87
- initial_grid = torch.cat(initial_grid, axis=1)
98
+ api = api_from_tensors(*initial_grid)
99
+ initial_grid = api.cat(initial_grid, axis=1)
100
+ # if isinstance(initial_grid[0], np.ndarray):
101
+ # initial_grid = np.concatenate(initial_grid, axis=1)
102
+ # else:
103
+ # is_numpy_compatible = False
104
+ # import torch
105
+ # assert isinstance(first[0], torch.Tensor), "Only numpy and torch are supported ftm."
106
+ # initial_grid = torch.cat(initial_grid, axis=1)
88
107
  ## is grid-like (num_params, num_pts)
89
- elif isinstance(first,list) or isinstance(first, tuple) or isinstance(first, np.ndarray):
90
- initial_grid = tuple(f for f in x)
91
108
  else:
92
- is_numpy_compatible = False
93
- import torch
94
- assert isinstance(first, torch.Tensor), "Only numpy and torch are supported ftm."
95
- initial_grid = x
96
- if is_numpy_compatible:
109
+ api = api_from_tensors(*x)
110
+ initial_grid = tuple(api.astensor(f) for f in x)
111
+ # elif isinstance(first,list) or isinstance(first, tuple) or isinstance(first, np.ndarray):
112
+ # initial_grid = tuple(f for f in x)
113
+ # else:
114
+ # is_numpy_compatible = False
115
+ # import torch
116
+ # assert isinstance(first, torch.Tensor), "Only numpy and torch are supported ftm."
117
+ # initial_grid = x
118
+
119
+ num_parameters = len(initial_grid)
120
+ try:
121
+ int(resolution)
122
+ resolution = [resolution]*num_parameters
123
+ except TypeError:
124
+ pass
125
+
126
+ if api is npapi:
97
127
  return _compute_grid_numpy(
98
128
  initial_grid,
99
129
  resolution=resolution,
@@ -104,8 +134,10 @@ def compute_grid(
104
134
  dense = dense,
105
135
  )
106
136
  from multipers.torch.diff_grids import get_grid
107
- return get_grid(strategy)(initial_grid,resolution)
108
-
137
+ grid = get_grid(strategy)(initial_grid,resolution)
138
+ if dense:
139
+ grid = todense(grid)
140
+ return grid
109
141
 
110
142
 
111
143
 
@@ -136,14 +168,6 @@ def _compute_grid_numpy(
136
168
  Iterable[array[float, ndim=1]] : the 1d-grid for each parameter.
137
169
  """
138
170
  num_parameters = len(filtrations_values)
139
- if resolution is None and strategy not in ["exact", "precomputed"]:
140
- raise ValueError("Resolution must be provided for this strategy.")
141
- elif resolution is not None:
142
- try:
143
- int(resolution)
144
- resolution = [resolution]*num_parameters
145
- except:
146
- pass
147
171
  try:
148
172
  a,b=drop_quantiles
149
173
  except:
@@ -208,7 +232,7 @@ def todense(grid, bool product_order=False):
208
232
  def _todo_regular_closest(some_float[:] f, int r, bool unique):
209
233
  f_array = np.asarray(f)
210
234
  f_regular = np.linspace(np.min(f), np.max(f),num=r, dtype=f_array.dtype)
211
- f_regular_closest = np.asarray([f[<long>np.argmin(np.abs(f_array-f_regular[i]))] for i in range(r)])
235
+ f_regular_closest = np.asarray([f[<int64_t>np.argmin(np.abs(f_array-f_regular[i]))] for i in range(r)])
212
236
  if unique: f_regular_closest = np.unique(f_regular_closest)
213
237
  return f_regular_closest
214
238
 
@@ -254,7 +278,7 @@ def push_to_grid(some_float[:,:] points, grid, bool return_coordinate=False):
254
278
  pushes the points onto the grid.
255
279
  """
256
280
  num_points, num_parameters = points.shape[0], points.shape[1]
257
- cdef cnp.ndarray[long,ndim=2] coordinates = np.empty((num_points, num_parameters),dtype=np.int64)
281
+ cdef cnp.ndarray[int64_t,ndim=2] coordinates = np.empty((num_points, num_parameters),dtype=np.int64)
258
282
  for parameter in range(num_parameters):
259
283
  coordinates[:,parameter] = np.searchsorted(grid[parameter],points[:,parameter])
260
284
  if return_coordinate:
@@ -271,7 +295,31 @@ def coarsen_points(some_float[:,:] points, strategy="exact", int resolution=-1,
271
295
  return push_to_grid(points, grid, coordinate), grid
272
296
  return push_to_grid(points, grid, coordinate)
273
297
 
274
-
298
+ def _inf_value(array):
299
+ if isinstance(array, type|np.dtype):
300
+ dtype = np.dtype(array) # torch types are not types
301
+ elif isinstance(array, np.ndarray):
302
+ dtype = np.dtype(array.dtype)
303
+ else:
304
+ import torch
305
+ if isinstance(array, torch.Tensor):
306
+ dtype=array.dtype
307
+ elif isinstance(array, torch.dtype):
308
+ dtype=array
309
+ else:
310
+ raise ValueError(f"unknown input of type {type(array)=} {array=}")
311
+
312
+ if isinstance(dtype, np.dtype):
313
+ if dtype.kind == 'f':
314
+ return np.asarray(np.inf,dtype=dtype)
315
+ if dtype.kind == 'i':
316
+ return np.iinfo(dtype).max
317
+ # torch only here.
318
+ if dtype.is_floating_point:
319
+ return torch.tensor(torch.inf, dtype=dtype)
320
+ else:
321
+ return torch.iinfo(dtype).max
322
+ raise ValueError(f"Dtype must be integer or floating like (got {dtype})")
275
323
 
276
324
  def evaluate_in_grid(pts, grid, mass_default=None):
277
325
  """
@@ -280,6 +328,7 @@ def evaluate_in_grid(pts, grid, mass_default=None):
280
328
  - pts: of the form array[int, ndim=2]
281
329
  - grid of the form Iterable[array[float, ndim=1]]
282
330
  """
331
+ assert pts.ndim == 2
283
332
  first_filtration = grid[0]
284
333
  dtype = first_filtration.dtype
285
334
  if isinstance(first_filtration, np.ndarray):
@@ -295,12 +344,18 @@ def evaluate_in_grid(pts, grid, mass_default=None):
295
344
  def empty_like(x):
296
345
  return torch.empty(x.shape,dtype=dtype)
297
346
 
298
- coords= empty_like(pts)
299
- for i in range(coords.shape[1]):
347
+ coords=empty_like(pts)
348
+ cdef int dim = coords.shape[1]
349
+ pts_inf = _inf_value(pts)
350
+ coords_inf = _inf_value(coords)
351
+ idx = np.argwhere(pts == pts_inf)
352
+ pts[idx] == 0
353
+ for i in range(dim):
300
354
  coords[:,i] = grid[i][pts[:,i]]
355
+ coords[idx] = coords_inf
301
356
  return coords
302
357
 
303
- def sm_in_grid(pts, weights, grid, int num_parameters=-1, mass_default=None):
358
+ def sm_in_grid(pts, weights, grid, mass_default=None):
304
359
  """Given a measure whose points are coordinates,
305
360
  pushes this measure in this grid.
306
361
  Input
@@ -310,34 +365,33 @@ def sm_in_grid(pts, weights, grid, int num_parameters=-1, mass_default=None):
310
365
  - grid of the form Iterable[array[float, ndim=1]]
311
366
  - num_parameters: number of parameters
312
367
  """
313
- first_filtration = grid[0]
314
- dtype = first_filtration.dtype
315
- def to_int(x):
316
- return np.asarray(x,dtype=np.int64)
317
- if isinstance(first_filtration, np.ndarray):
318
- if mass_default is not None:
319
- grid = tuple(np.concatenate([g, [m]]) for g,m in zip(grid, mass_default))
320
- def empty_like(x, weights):
321
- return np.empty_like(x, dtype=dtype), np.asarray(weights)
322
- else:
323
- import torch
324
- # assert isinstance(first_filtration, torch.Tensor), f"Invalid grid type. Got {type(grid[0])}, expected numpy or torch array."
368
+ if pts.ndim != 2:
369
+ raise ValueError(f"invalid dirac locations. got {pts.ndim=} != 2")
370
+ if len(grid) == 0:
371
+ raise ValueError(f"Empty grid given. Got {grid=}")
372
+ cdef int num_parameters = pts.shape[1]
373
+ if mass_default is None:
374
+ api = api_from_tensors(*grid)
375
+ else:
376
+ api = api_from_tensors(*grid, mass_default)
377
+
378
+ _grid = list(grid)
379
+ _mass_default = None if mass_default is None else api.astensor(mass_default)
380
+ while len(_grid) < num_parameters:
381
+ _grid += [api.cat([
382
+ (gt:=api.astensor(g))[1:],
383
+ api.astensor(_inf_value(api.asnumpy(gt))).reshape(1)
384
+ ]) for g in grid]
325
385
  if mass_default is not None:
326
- grid = tuple(torch.cat([g, torch.tensor(m)[None]]) for g,m in zip(grid, mass_default))
327
- def empty_like(x, weights):
328
- return torch.empty(x.shape,dtype=dtype), torch.from_numpy(weights)
329
-
330
- pts = to_int(pts)
331
- coords,weights = empty_like(pts,weights)
332
- for i in range(coords.shape[1]):
333
- if num_parameters > 0:
334
- coords[:,i] = grid[i%num_parameters][pts[:,i]]
335
- else:
336
- coords[:,i] = grid[i][pts[:,i]]
386
+ _mass_default = api.cat([_mass_default,mass_default])
387
+ grid = tuple(_grid)
388
+ mass_default = _mass_default
389
+
390
+ coords = evaluate_in_grid(np.asarray(pts, dtype=int), grid, mass_default)
337
391
  return (coords, weights)
338
392
 
339
393
  # TODO : optimize with memoryviews / typing
340
- def sms_in_grid(sms, grid, int num_parameters=-1, mass_default=None):
394
+ def sms_in_grid(sms, grid, mass_default=None):
341
395
  """Given a measure whose points are coordinates,
342
396
  pushes this measure in this grid.
343
397
  Input
@@ -346,5 +400,34 @@ def sms_in_grid(sms, grid, int num_parameters=-1, mass_default=None):
346
400
  where signed_measure_like = tuple(array[int, ndim=2], array[int])
347
401
  - grid of the form Iterable[array[float, ndim=1]]
348
402
  """
349
- sms = tuple(sm_in_grid(pts,weights,grid=grid,num_parameters=num_parameters, mass_default=mass_default) for pts,weights in sms)
403
+ sms = tuple(sm_in_grid(pts,weights,grid=grid, mass_default=mass_default) for pts,weights in sms)
350
404
  return sms
405
+
406
+
407
+ def _push_pts_to_line(pts, basepoint, direction=None):
408
+ api = api_from_tensors(pts, basepoint)
409
+ pts = api.astensor(pts)
410
+ basepoint = api.astensor(basepoint)
411
+ num_parameters = basepoint.shape[0]
412
+ if direction is not None:
413
+ if not api.is_promotable(direction):
414
+ raise ValueError(f"Incompatible input types. Got {type(pts)=}, {type(basepoint)=}, {type(direction)=}")
415
+
416
+ direction = api.astensor(direction)
417
+ ok_idx = direction > 0
418
+ if ok_idx.sum() == 0:
419
+ raise ValueError(f"Got invalid direction {direction}")
420
+ zero_idx = None if ok_idx.all() else direction == 0
421
+ else:
422
+ direction = api.tensor([1], dtype=int)
423
+ ok_idx = slice(None)
424
+ zero_idx = None
425
+ xa = api.maxvalues(
426
+ (pts[:, ok_idx] - basepoint[ok_idx]) / direction[ok_idx], axis=1, keepdims=True
427
+ )
428
+ if zero_idx is not None:
429
+ xb = api.where(pts[:, zero_idx] <= basepoint[zero_idx], -np.inf, np.inf)
430
+ xs = api.maxvalues(api.cat([xa, xb], axis=1), axis=1, keepdims=True)
431
+ else:
432
+ xs = xa
433
+ return xs.squeeze()
@@ -21,6 +21,8 @@
21
21
  #include "multiparameter_module_approximation/format_python-cpp.h"
22
22
 
23
23
  #include <iostream>
24
+ #include <ranges>
25
+ #include <stdexcept>
24
26
  #include <utility> // std::pair
25
27
  #include <vector>
26
28
  #include <limits> // has_quiet_NaN
@@ -292,6 +294,39 @@ class Simplex_tree_multi_interface
292
294
  }
293
295
  }
294
296
 
297
+ void unsqueeze_filtration(const intptr_t grid_st_ptr,
298
+ const std::vector<std::vector<double>> &grid) { // TODO : this is const but GUDHI
299
+ if constexpr (Filtration::is_multicritical())
300
+ throw std::invalid_argument("Multicritical not supported yet");
301
+ else {
302
+ constexpr const bool verbose = false;
303
+ using int_fil_type = decltype(std::declval<Filtration>().template as_type<std::int32_t>());
304
+ using st_coord_type = Simplex_tree_multi_interface<int_fil_type, int32_t>;
305
+ st_coord_type &grid_st = *(st_coord_type *)grid_st_ptr; // TODO : maybe fix this.
306
+ std::vector<int> simplex_vertex;
307
+ int num_parameters = grid_st.get_number_of_parameters();
308
+ for (auto &simplex_handle : grid_st.complex_simplex_range()) {
309
+ const auto &simplex_filtration = grid_st.filtration(simplex_handle);
310
+ if constexpr (verbose) std::cout << "Filtration " << simplex_filtration << "\n";
311
+ Filtration splx_filtration(simplex_filtration.size(), 1.);
312
+ if (simplex_filtration.is_finite()) {
313
+ for (auto i : std::views::iota(num_parameters)) splx_filtration[i] = grid[i][simplex_filtration[i]];
314
+ } else if (simplex_filtration.is_plus_inf()) {
315
+ splx_filtration = Filtration().inf();
316
+ } else if (simplex_filtration.is_minus_inf()) {
317
+ splx_filtration = Filtration().minus_inf();
318
+ } else if (simplex_filtration.is_nan()) {
319
+ splx_filtration = Filtration().nan();
320
+ }
321
+ if constexpr (verbose) std::cout << "Filtration " << splx_filtration << "\n";
322
+ for (const auto s : grid_st.simplex_vertex_range(simplex_handle)) simplex_vertex.push_back(s);
323
+ this->insert_simplex(simplex_vertex, splx_filtration);
324
+ if constexpr (verbose) std::cout << "Coords in st" << this->filtration(this->find(simplex_vertex)) << std::endl;
325
+ simplex_vertex.clear();
326
+ }
327
+ }
328
+ }
329
+
295
330
  void squeeze_filtration(const intptr_t outptr,
296
331
  const std::vector<std::vector<double>> &grid) { // TODO : this is const but GUDHI
297
332
  constexpr const bool verbose = false;
@@ -149,6 +149,9 @@ class Box {
149
149
  lowerCorner_ -= delta;
150
150
  upperCorner_ += delta;
151
151
  }
152
+ friend bool operator==(const Box& a, const Box&b){
153
+ return a.upperCorner_ == b.upperCorner_ && a.lowerCorner_ == b.lowerCorner_;
154
+ }
152
155
 
153
156
  /**
154
157
  * @brief Outstream operator.
@@ -30,7 +30,7 @@
30
30
  #include <gudhi/Debug_utils.h>
31
31
 
32
32
  namespace Gudhi {
33
- namespace multi_filtration {
33
+ namespace multi_filtration {
34
34
 
35
35
  /**
36
36
  * @class One_critical_filtration one_critical_filtration.h gudhi/one_critical_filtration.h
@@ -1034,14 +1034,21 @@ class One_critical_filtration : public std::vector<T> {
1034
1034
  "The grid should not be smaller than the number of parameters in the filtration value.");
1035
1035
  for (std::size_t parameter = 0u; parameter < Base::size(); ++parameter) {
1036
1036
  const auto &filtration = grid[parameter];
1037
- auto d =
1038
- std::distance(filtration.begin(),
1039
- std::lower_bound(filtration.begin(),
1040
- filtration.end(),
1041
- static_cast<typename oned_array::value_type>(Base::operator[](parameter))));
1042
1037
  int num_filtration_values = filtration.size();
1043
- d = d == num_filtration_values ? std::max(num_filtration_values - 1,0) : d;
1044
- Base::operator[](parameter) = coordinate ? static_cast<T>(d) : static_cast<T>(filtration[d]);
1038
+ auto this_at_param = static_cast<typename oned_array::value_type>(Base::operator[](parameter));
1039
+ auto it = std::lower_bound(filtration.begin(), filtration.end(), this_at_param);
1040
+ std::size_t idx;
1041
+ if (it == filtration.end()) {
1042
+ idx = num_filtration_values - 1;
1043
+ } else if (it == filtration.begin()) {
1044
+ idx = 0;
1045
+ } else {
1046
+ auto prev = it - 1;
1047
+ idx = (std::abs(*prev - this_at_param) <= std::abs(*it - this_at_param))
1048
+ ? std::distance(filtration.begin(), prev)
1049
+ : std::distance(filtration.begin(), it);
1050
+ }
1051
+ Base::operator[](parameter) = coordinate ? static_cast<T>(idx) : static_cast<T>(filtration[idx]);
1045
1052
  }
1046
1053
  }
1047
1054
 
@@ -1394,7 +1401,8 @@ class One_critical_filtration : public std::vector<T> {
1394
1401
  }
1395
1402
  };
1396
1403
 
1397
- }} // namespace Gudhi::multi_filtration
1404
+ } // namespace multi_filtration
1405
+ } // namespace Gudhi
1398
1406
 
1399
1407
  namespace std {
1400
1408
 
@@ -60,17 +60,19 @@ struct No_vine_multi_persistence_options : Gudhi::persistence_matrix::Default_op
60
60
  static const bool has_vine_update = false;
61
61
  };
62
62
 
63
- template <Gudhi::persistence_matrix::Column_types column_type = Gudhi::persistence_matrix::Column_types::INTRUSIVE_SET>
63
+ template <Gudhi::persistence_matrix::Column_types column_type = Gudhi::persistence_matrix::Column_types::INTRUSIVE_SET, bool row_access = true>
64
64
  struct fix_presentation_options : Gudhi::persistence_matrix::Default_options<column_type, true> {
65
65
  using Index = std::uint32_t;
66
- static const bool has_map_column_container = true;
67
- static const bool has_removable_columns = true; // WARN : idx will change if map is not true
66
+ static const bool has_row_access = row_access;
67
+ static const bool has_map_column_container = false;
68
+ static const bool has_removable_columns = false; // WARN : idx will change if map is not true
68
69
  };
69
70
 
70
71
  template <class Matrix_options, class Boundary_matrix_type>
71
72
  class Persistence_backend_matrix {
72
73
  public:
73
74
  using matrix_type = Gudhi::persistence_matrix::Matrix<Matrix_options>;
75
+ using options = Matrix_options;
74
76
  using cycle_type = typename matrix_type::Cycle;
75
77
  static const bool is_vine = Matrix_options::has_vine_update;
76
78