multipers 2.3.1__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.
- multipers/_signed_measure_meta.py +71 -65
- multipers/array_api/__init__.py +39 -0
- multipers/array_api/numpy.py +34 -0
- multipers/array_api/torch.py +35 -0
- multipers/distances.py +6 -2
- multipers/filtrations/density.py +23 -12
- multipers/filtrations/filtrations.py +74 -15
- multipers/function_rips.cp310-win_amd64.pyd +0 -0
- multipers/grids.cp310-win_amd64.pyd +0 -0
- multipers/grids.pyx +144 -61
- multipers/gudhi/Simplex_tree_multi_interface.h +35 -0
- multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
- multipers/gudhi/gudhi/One_critical_filtration.h +17 -9
- multipers/gudhi/mma_interface_matrix.h +5 -3
- multipers/gudhi/truc.h +488 -42
- multipers/io.cp310-win_amd64.pyd +0 -0
- multipers/io.pyx +16 -86
- multipers/ml/mma.py +3 -3
- multipers/ml/signed_measures.py +60 -62
- multipers/mma_structures.cp310-win_amd64.pyd +0 -0
- multipers/mma_structures.pxd +2 -1
- multipers/mma_structures.pyx +56 -12
- multipers/mma_structures.pyx.tp +14 -3
- multipers/multiparameter_module_approximation/approximation.h +45 -13
- multipers/multiparameter_module_approximation.cp310-win_amd64.pyd +0 -0
- multipers/multiparameter_module_approximation.pyx +22 -6
- multipers/plots.py +1 -0
- multipers/point_measure.cp310-win_amd64.pyd +0 -0
- multipers/point_measure.pyx +6 -2
- multipers/simplex_tree_multi.cp310-win_amd64.pyd +0 -0
- multipers/simplex_tree_multi.pxd +1 -0
- multipers/simplex_tree_multi.pyx +487 -109
- multipers/simplex_tree_multi.pyx.tp +67 -18
- multipers/slicer.cp310-win_amd64.pyd +0 -0
- multipers/slicer.pxd +699 -217
- multipers/slicer.pxd.tp +22 -6
- multipers/slicer.pyx +5311 -1364
- multipers/slicer.pyx.tp +199 -46
- multipers/tbb12.dll +0 -0
- multipers/tbbbind_2_5.dll +0 -0
- multipers/tbbmalloc.dll +0 -0
- multipers/tbbmalloc_proxy.dll +0 -0
- multipers/tests/__init__.py +9 -4
- multipers/torch/diff_grids.py +30 -7
- {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/METADATA +4 -25
- {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/RECORD +49 -46
- {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/WHEEL +1 -1
- {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info/licenses}/LICENSE +0 -0
- {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,
|
|
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(
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
initial_grid = x
|
|
96
|
-
|
|
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
|
-
|
|
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[<
|
|
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[
|
|
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=
|
|
299
|
-
|
|
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,
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
1044
|
-
|
|
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
|
-
}
|
|
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
|
|
67
|
-
static const bool
|
|
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
|
|