multipers 2.3.3b6__cp313-cp313-win_amd64.whl → 2.3.3b7__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.
- multipers/_signed_measure_meta.py +4 -7
- multipers/array_api/__init__.py +18 -1
- multipers/array_api/numpy.py +64 -1
- multipers/array_api/torch.py +62 -4
- multipers/filtrations/density.py +11 -52
- multipers/function_rips.cp313-win_amd64.pyd +0 -0
- multipers/grids.cp313-win_amd64.pyd +0 -0
- multipers/grids.pyx +73 -32
- multipers/io.cp313-win_amd64.pyd +0 -0
- multipers/ml/signed_measures.py +105 -27
- multipers/mma_structures.cp313-win_amd64.pyd +0 -0
- multipers/mma_structures.pyx +2 -2
- multipers/mma_structures.pyx.tp +1 -1
- multipers/multiparameter_module_approximation.cp313-win_amd64.pyd +0 -0
- multipers/plots.py +1 -1
- multipers/point_measure.cp313-win_amd64.pyd +0 -0
- multipers/simplex_tree_multi.cp313-win_amd64.pyd +0 -0
- multipers/slicer.cp313-win_amd64.pyd +0 -0
- multipers/slicer.pxd +20 -20
- multipers/slicer.pyx +14 -14
- {multipers-2.3.3b6.dist-info → multipers-2.3.3b7.dist-info}/METADATA +1 -1
- {multipers-2.3.3b6.dist-info → multipers-2.3.3b7.dist-info}/RECORD +25 -25
- {multipers-2.3.3b6.dist-info → multipers-2.3.3b7.dist-info}/WHEEL +0 -0
- {multipers-2.3.3b6.dist-info → multipers-2.3.3b7.dist-info}/licenses/LICENSE +0 -0
- {multipers-2.3.3b6.dist-info → multipers-2.3.3b7.dist-info}/top_level.txt +0 -0
|
@@ -279,10 +279,6 @@ def signed_measure(
|
|
|
279
279
|
ignore_inf=ignore_infinite_filtration_values,
|
|
280
280
|
)
|
|
281
281
|
fix_mass_default = False
|
|
282
|
-
|
|
283
|
-
if "hook" in invariant:
|
|
284
|
-
from multipers.point_measure import rectangle_to_hook_minimal_signed_barcode
|
|
285
|
-
sms = [rectangle_to_hook_minimal_signed_barcode(pts,w) for pts,w in sms]
|
|
286
282
|
if verbose:
|
|
287
283
|
print("Done.")
|
|
288
284
|
elif filtered_complex_.is_minpres:
|
|
@@ -345,9 +341,6 @@ def signed_measure(
|
|
|
345
341
|
expand_collapse=expand_collapse,
|
|
346
342
|
)
|
|
347
343
|
fix_mass_default = False
|
|
348
|
-
if "hook" in invariant:
|
|
349
|
-
from multipers.point_measure import rectangle_to_hook_minimal_signed_barcode
|
|
350
|
-
sms = [rectangle_to_hook_minimal_signed_barcode(pts,w) for pts,w in sms]
|
|
351
344
|
if verbose:
|
|
352
345
|
print("Done.")
|
|
353
346
|
elif len(degrees) == 1 and degrees[0] is None:
|
|
@@ -420,6 +413,10 @@ def signed_measure(
|
|
|
420
413
|
sms = zero_out_sms(sms, mass_default=mass_default)
|
|
421
414
|
if verbose:
|
|
422
415
|
print("Done.")
|
|
416
|
+
|
|
417
|
+
if invariant == "hook":
|
|
418
|
+
from multipers.point_measure import rectangle_to_hook_minimal_signed_barcode
|
|
419
|
+
sms = [rectangle_to_hook_minimal_signed_barcode(pts,w) for pts,w in sms]
|
|
423
420
|
if plot:
|
|
424
421
|
plot_signed_measures(sms)
|
|
425
422
|
return sms
|
multipers/array_api/__init__.py
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import multipers.array_api.numpy as npapi
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def api_from_tensor(x, *, verbose: bool = False):
|
|
4
|
+
def api_from_tensor(x, *, verbose: bool = False, strict=False):
|
|
5
|
+
if strict:
|
|
6
|
+
if npapi.is_tensor(x):
|
|
7
|
+
return npapi
|
|
8
|
+
import multipers.array_api.torch as torchapi
|
|
9
|
+
|
|
10
|
+
if torchapi.is_tensor(x):
|
|
11
|
+
return torchapi
|
|
12
|
+
raise ValueError(f"Unsupported (strict) type {type(x)=}")
|
|
5
13
|
if npapi.is_promotable(x):
|
|
6
14
|
if verbose:
|
|
7
15
|
print("using numpy backend")
|
|
@@ -43,3 +51,12 @@ def api_from_tensors(*args):
|
|
|
43
51
|
def to_numpy(x):
|
|
44
52
|
api = api_from_tensor(x)
|
|
45
53
|
return api.asnumpy(x)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def check_keops():
|
|
57
|
+
import os
|
|
58
|
+
|
|
59
|
+
if os.name == "nt":
|
|
60
|
+
# see https://github.com/getkeops/keops/pull/421
|
|
61
|
+
return False
|
|
62
|
+
return npapi.check_keops()
|
multipers/array_api/numpy.py
CHANGED
|
@@ -19,10 +19,69 @@ max = _np.max
|
|
|
19
19
|
repeat_interleave = _np.repeat
|
|
20
20
|
cdist = cdist # type: ignore[no-redef]
|
|
21
21
|
unique = _np.unique
|
|
22
|
+
inf = _np.inf
|
|
23
|
+
searchsorted = _np.searchsorted
|
|
24
|
+
LazyTensor = None
|
|
25
|
+
|
|
26
|
+
# Test keops
|
|
27
|
+
_is_keops_available = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def check_keops():
|
|
31
|
+
global _is_keops_available, LazyTensor
|
|
32
|
+
if _is_keops_available is not None:
|
|
33
|
+
return _is_keops_available
|
|
34
|
+
import pykeops.numpy as pknp
|
|
35
|
+
from pykeops.numpy import LazyTensor as LT
|
|
36
|
+
|
|
37
|
+
formula = "SqNorm2(x - y)"
|
|
38
|
+
var = ["x = Vi(3)", "y = Vj(3)"]
|
|
39
|
+
expected_res = _np.array([63.0, 90.0])
|
|
40
|
+
x = _np.arange(1, 10).reshape(-1, 3).astype("float32")
|
|
41
|
+
y = _np.arange(3, 9).reshape(-1, 3).astype("float32")
|
|
42
|
+
|
|
43
|
+
my_conv = pknp.Genred(formula, var)
|
|
44
|
+
try:
|
|
45
|
+
_is_keops_available = _np.allclose(my_conv(x, y).flatten(), expected_res)
|
|
46
|
+
LazyTensor = LT
|
|
47
|
+
except:
|
|
48
|
+
from warnings import warn
|
|
49
|
+
|
|
50
|
+
warn("Could not initialize keops (numpy). using workarounds")
|
|
51
|
+
_is_keops_available = False
|
|
52
|
+
|
|
53
|
+
return _is_keops_available
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def from_numpy(x):
|
|
57
|
+
return _np.asarray(x)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def ascontiguous(x):
|
|
61
|
+
return _np.ascontiguousarray(x)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def sort(x, axis=-1):
|
|
65
|
+
return _np.sort(x, axis=axis)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def device(x): # type: ignore[no-unused-arg]
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# type: ignore[no-unused-arg]
|
|
73
|
+
def linspace(low, high, r, device=None, dtype=None):
|
|
74
|
+
return _np.linspace(low, high, r, dtype=dtype)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def cartesian_product(*arrays, dtype=None):
|
|
78
|
+
mesh = _np.meshgrid(*arrays, indexing="ij")
|
|
79
|
+
coordinates = _np.stack(mesh, axis=-1).reshape(-1, len(arrays)).astype(dtype)
|
|
80
|
+
return coordinates
|
|
22
81
|
|
|
23
82
|
|
|
24
83
|
def quantile_closest(x, q, axis=None):
|
|
25
|
-
return _np.quantile(x, q, axis=axis,
|
|
84
|
+
return _np.quantile(x, q, axis=axis, method="closest_observation")
|
|
26
85
|
|
|
27
86
|
|
|
28
87
|
def minvalues(x: _np.ndarray, **kwargs):
|
|
@@ -33,6 +92,10 @@ def maxvalues(x: _np.ndarray, **kwargs):
|
|
|
33
92
|
return _np.max(x, **kwargs)
|
|
34
93
|
|
|
35
94
|
|
|
95
|
+
def is_tensor(x):
|
|
96
|
+
return isinstance(x, _np.ndarray)
|
|
97
|
+
|
|
98
|
+
|
|
36
99
|
def is_promotable(x):
|
|
37
100
|
return isinstance(x, _np.ndarray | list | tuple)
|
|
38
101
|
|
multipers/array_api/torch.py
CHANGED
|
@@ -15,6 +15,60 @@ zeros = _t.zeros
|
|
|
15
15
|
min = _t.min
|
|
16
16
|
max = _t.max
|
|
17
17
|
repeat_interleave = _t.repeat_interleave
|
|
18
|
+
linspace = _t.linspace
|
|
19
|
+
cartesian_product = _t.cartesian_prod
|
|
20
|
+
inf = _t.inf
|
|
21
|
+
searchsorted = _t.searchsorted
|
|
22
|
+
LazyTensor = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_is_keops_available = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def check_keops():
|
|
29
|
+
global _is_keops_available, LazyTensor
|
|
30
|
+
if _is_keops_available is not None:
|
|
31
|
+
return _is_keops_available
|
|
32
|
+
try:
|
|
33
|
+
import pykeops.torch as pknp
|
|
34
|
+
from pykeops.torch import LazyTensor as LT
|
|
35
|
+
|
|
36
|
+
formula = "SqNorm2(x - y)"
|
|
37
|
+
var = ["x = Vi(3)", "y = Vj(3)"]
|
|
38
|
+
expected_res = _t.tensor([63.0, 90.0])
|
|
39
|
+
x = _t.arange(1, 10, dtype=_t.float32).view(-1, 3)
|
|
40
|
+
y = _t.arange(3, 9, dtype=_t.float32).view(-1, 3)
|
|
41
|
+
|
|
42
|
+
my_conv = pknp.Genred(formula, var)
|
|
43
|
+
_is_keops_available = _t.allclose(
|
|
44
|
+
my_conv(x, y).view(-1), _t.tensor(expected_res).type(_t.float32)
|
|
45
|
+
)
|
|
46
|
+
LazyTensor = LT
|
|
47
|
+
|
|
48
|
+
except:
|
|
49
|
+
from warnings import warn
|
|
50
|
+
|
|
51
|
+
warn("Could not initialize keops (torch). using workarounds")
|
|
52
|
+
|
|
53
|
+
_is_keops_available = False
|
|
54
|
+
|
|
55
|
+
return _is_keops_available
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def from_numpy(x):
|
|
59
|
+
return _t.from_numpy(x)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def ascontiguous(x):
|
|
63
|
+
return _t.as_tensor(x).contiguous()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def device(x):
|
|
67
|
+
return x.device
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def sort(x, axis=-1):
|
|
71
|
+
return _t.sort(x, dim=axis).values
|
|
18
72
|
|
|
19
73
|
|
|
20
74
|
# in our context, this allows to get a correct gradient.
|
|
@@ -28,10 +82,10 @@ def unique(x, assume_sorted=False, _mean=True):
|
|
|
28
82
|
_, c = _t.unique(x, sorted=True, return_counts=True)
|
|
29
83
|
if _mean:
|
|
30
84
|
x = _t.segment_reduce(data=x, reduce="mean", lengths=c, unsafe=True, axis=0)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return x
|
|
85
|
+
else:
|
|
86
|
+
c = _np.concatenate([[0], _np.cumsum(c[:-1])])
|
|
87
|
+
x = x[c]
|
|
88
|
+
return x
|
|
35
89
|
|
|
36
90
|
|
|
37
91
|
def quantile_closest(x, q, axis=None):
|
|
@@ -50,6 +104,10 @@ def asnumpy(x):
|
|
|
50
104
|
return x.detach().numpy()
|
|
51
105
|
|
|
52
106
|
|
|
107
|
+
def is_tensor(x):
|
|
108
|
+
return isinstance(x, _t.Tensor)
|
|
109
|
+
|
|
110
|
+
|
|
53
111
|
def is_promotable(x):
|
|
54
112
|
return isinstance(x, _t.Tensor)
|
|
55
113
|
|
multipers/filtrations/density.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from collections.abc import Callable, Iterable
|
|
2
2
|
from typing import Any, Literal, Union
|
|
3
|
+
|
|
3
4
|
import numpy as np
|
|
4
5
|
|
|
6
|
+
from multipers.array_api import api_from_tensor, api_from_tensors
|
|
5
7
|
|
|
6
|
-
from multipers.array_api import api_from_tensor
|
|
7
8
|
global available_kernels
|
|
8
9
|
available_kernels = Union[
|
|
9
10
|
Literal[
|
|
@@ -176,23 +177,24 @@ def _pts_convolution_pykeops(
|
|
|
176
177
|
Pykeops convolution
|
|
177
178
|
"""
|
|
178
179
|
if isinstance(pts, np.ndarray):
|
|
179
|
-
_asarray_weights = lambda x
|
|
180
|
+
_asarray_weights = lambda x: np.asarray(x, dtype=pts.dtype)
|
|
180
181
|
_asarray_grid = _asarray_weights
|
|
181
182
|
else:
|
|
182
183
|
import torch
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
|
|
185
|
+
_asarray_weights = lambda x: torch.from_numpy(x).type(pts.dtype)
|
|
186
|
+
_asarray_grid = lambda x: x.type(pts.dtype)
|
|
185
187
|
kde = KDE(kernel=kernel, bandwidth=bandwidth, **more_kde_args)
|
|
186
|
-
return kde.fit(
|
|
187
|
-
|
|
188
|
-
)
|
|
188
|
+
return kde.fit(pts, sample_weights=_asarray_weights(pts_weights)).score_samples(
|
|
189
|
+
_asarray_grid(grid_iterator)
|
|
190
|
+
)
|
|
189
191
|
|
|
190
192
|
|
|
191
193
|
def gaussian_kernel(x_i, y_j, bandwidth):
|
|
192
194
|
D = x_i.shape[-1]
|
|
193
195
|
exponent = -(((x_i - y_j) / bandwidth) ** 2).sum(dim=-1) / 2
|
|
194
196
|
# float is necessary for some reason (pykeops fails)
|
|
195
|
-
kernel = (exponent).exp() / float((bandwidth*np.sqrt(2 * np.pi))**D)
|
|
197
|
+
kernel = (exponent).exp() / float((bandwidth * np.sqrt(2 * np.pi)) ** D)
|
|
196
198
|
return kernel
|
|
197
199
|
|
|
198
200
|
|
|
@@ -359,49 +361,6 @@ class KDE:
|
|
|
359
361
|
)
|
|
360
362
|
|
|
361
363
|
|
|
362
|
-
def batch_signed_measure_convolutions(
|
|
363
|
-
signed_measures, # array of shape (num_data,num_pts,D)
|
|
364
|
-
x, # array of shape (num_x, D) or (num_data, num_x, D)
|
|
365
|
-
bandwidth, # either float or matrix if multivariate kernel
|
|
366
|
-
kernel: available_kernels,
|
|
367
|
-
):
|
|
368
|
-
"""
|
|
369
|
-
Input
|
|
370
|
-
-----
|
|
371
|
-
- signed_measures: unragged, of shape (num_data, num_pts, D+1)
|
|
372
|
-
where last coord is weights, (0 for dummy points)
|
|
373
|
-
- x : the points to convolve (num_x,D)
|
|
374
|
-
- bandwidth : the bandwidths or covariance matrix inverse or ... of the kernel
|
|
375
|
-
- kernel : "gaussian", "multivariate_gaussian", "exponential", or Callable (x_i, y_i, bandwidth)->float
|
|
376
|
-
|
|
377
|
-
Output
|
|
378
|
-
------
|
|
379
|
-
Array of shape (num_convolutions, (num_axis), num_data,
|
|
380
|
-
Array of shape (num_convolutions, (num_axis), num_data, max_x_size)
|
|
381
|
-
"""
|
|
382
|
-
if signed_measures.ndim == 2:
|
|
383
|
-
signed_measures = signed_measures[None, :, :]
|
|
384
|
-
sms = signed_measures[..., :-1]
|
|
385
|
-
weights = signed_measures[..., -1]
|
|
386
|
-
if isinstance(signed_measures, np.ndarray):
|
|
387
|
-
from pykeops.numpy import LazyTensor
|
|
388
|
-
else:
|
|
389
|
-
import torch
|
|
390
|
-
|
|
391
|
-
assert isinstance(signed_measures, torch.Tensor)
|
|
392
|
-
from pykeops.torch import LazyTensor
|
|
393
|
-
|
|
394
|
-
_sms = LazyTensor(sms[..., None, :].contiguous())
|
|
395
|
-
_x = x[..., None, :, :].contiguous()
|
|
396
|
-
|
|
397
|
-
sms_kernel = _kernel(kernel)(_sms, _x, bandwidth)
|
|
398
|
-
out = (sms_kernel * weights[..., None, None].contiguous()).sum(
|
|
399
|
-
signed_measures.ndim - 2
|
|
400
|
-
)
|
|
401
|
-
assert out.shape[-1] == 1, "Pykeops bug fixed, TODO : refix this "
|
|
402
|
-
out = out[..., 0] ## pykeops bug + ensures its a tensor
|
|
403
|
-
# assert out.shape == (x.shape[0], x.shape[1]), f"{x.shape=}, {out.shape=}"
|
|
404
|
-
return out
|
|
405
364
|
|
|
406
365
|
|
|
407
366
|
class DTM:
|
|
@@ -532,7 +491,7 @@ class KNNmean:
|
|
|
532
491
|
|
|
533
492
|
# Symbolic distance matrix:
|
|
534
493
|
if self.metric == "euclidean":
|
|
535
|
-
D_ij = ((X_i - X_j) ** 2).sum(-1) ** (1/2)
|
|
494
|
+
D_ij = ((X_i - X_j) ** 2).sum(-1) ** (1 / 2)
|
|
536
495
|
elif self.metric == "manhattan":
|
|
537
496
|
D_ij = (X_i - X_j).abs().sum(-1)
|
|
538
497
|
elif self.metric == "angular":
|
|
Binary file
|
|
Binary file
|
multipers/grids.pyx
CHANGED
|
@@ -11,6 +11,7 @@ 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
13
|
from multipers.array_api import numpy as npapi
|
|
14
|
+
from multipers.array_api import check_keops
|
|
14
15
|
|
|
15
16
|
available_strategies = ["regular","regular_closest", "regular_left", "partition", "quantile", "precomputed"]
|
|
16
17
|
Lstrategies = Literal["regular","regular_closest", "regular_left", "partition", "quantile", "precomputed"]
|
|
@@ -168,39 +169,39 @@ def _compute_grid_numpy(
|
|
|
168
169
|
Iterable[array[float, ndim=1]] : the 1d-grid for each parameter.
|
|
169
170
|
"""
|
|
170
171
|
num_parameters = len(filtrations_values)
|
|
172
|
+
api = api_from_tensors(filtrations_values)
|
|
171
173
|
try:
|
|
172
174
|
a,b=drop_quantiles
|
|
173
175
|
except:
|
|
174
176
|
a,b=drop_quantiles,drop_quantiles
|
|
175
177
|
|
|
176
178
|
if a != 0 or b != 0:
|
|
177
|
-
boxes =
|
|
178
|
-
min_filtration, max_filtration =
|
|
179
|
+
boxes = api.astensor([api.quantile_closest(filtration, [a, b], axis=1) for filtration in filtrations_values])
|
|
180
|
+
min_filtration, max_filtration = api.minvalues(boxes, axis=(0,1)), api.maxvalues(boxes, axis=(0,1)) # box, birth/death, filtration
|
|
179
181
|
filtrations_values = [
|
|
180
182
|
filtration[(m<filtration) * (filtration <M)]
|
|
181
183
|
for filtration, m,M in zip(filtrations_values, min_filtration, max_filtration)
|
|
182
184
|
]
|
|
183
185
|
|
|
184
|
-
to_unique = lambda f : np.unique(f) if isinstance(f,np.ndarray) else f.unique()
|
|
185
186
|
## match doesn't work with cython BUG
|
|
186
187
|
if strategy == "exact":
|
|
187
|
-
F=tuple(
|
|
188
|
+
F=tuple(api.unique(f) for f in filtrations_values)
|
|
188
189
|
elif strategy == "quantile":
|
|
189
|
-
F = tuple(
|
|
190
|
+
F = tuple(api.unique(f) for f in filtrations_values)
|
|
190
191
|
max_resolution = [min(len(f),r) for f,r in zip(F,resolution)]
|
|
191
|
-
F = tuple(
|
|
192
|
+
F = tuple( api.quantile_closest(f, q=np.linspace(0,1,num=int(r*_q_factor)), axis=0) for f,r in zip(F, resolution) )
|
|
192
193
|
if unique:
|
|
193
|
-
F = tuple(
|
|
194
|
+
F = tuple(api.unique(f) for f in F)
|
|
194
195
|
if np.all(np.asarray(max_resolution) > np.asarray([len(f) for f in F])):
|
|
195
196
|
return _compute_grid_numpy(filtrations_values=filtrations_values, resolution=resolution, strategy="quantile",_q_factor=1.5*_q_factor)
|
|
196
197
|
elif strategy == "regular":
|
|
197
|
-
F = tuple(
|
|
198
|
+
F = tuple(_todo_regular(f,r,api) for f,r in zip(filtrations_values, resolution))
|
|
198
199
|
elif strategy == "regular_closest":
|
|
199
|
-
F = tuple(_todo_regular_closest(f,r, unique) for f,r in zip(filtrations_values, resolution))
|
|
200
|
+
F = tuple(_todo_regular_closest(f,r, unique,api) for f,r in zip(filtrations_values, resolution))
|
|
200
201
|
elif strategy == "regular_left":
|
|
201
|
-
F = tuple(_todo_regular_left(f,r, unique) for f,r in zip(filtrations_values, resolution))
|
|
202
|
-
elif strategy == "torch_regular_closest":
|
|
203
|
-
|
|
202
|
+
F = tuple(_todo_regular_left(f,r, unique,api) for f,r in zip(filtrations_values, resolution))
|
|
203
|
+
# elif strategy == "torch_regular_closest":
|
|
204
|
+
# F = tuple(_torch_regular_closest(f,r, unique) for f,r in zip(filtrations_values, resolution))
|
|
204
205
|
elif strategy == "partition":
|
|
205
206
|
F = tuple(_todo_partition(f,r, unique) for f,r in zip(filtrations_values, resolution))
|
|
206
207
|
elif strategy == "precomputed":
|
|
@@ -214,41 +215,75 @@ def _compute_grid_numpy(
|
|
|
214
215
|
def todense(grid, bool product_order=False):
|
|
215
216
|
if len(grid) == 0:
|
|
216
217
|
return np.empty(0)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return
|
|
218
|
+
api = api_from_tensors(grid)
|
|
219
|
+
# if product_order:
|
|
220
|
+
# if not api.backend ==np:
|
|
221
|
+
# raise NotImplementedError("only numpy here.")
|
|
222
|
+
# return np.fromiter(product(*grid), dtype=np.dtype((dtype, len(grid))), count=np.prod([len(f) for f in grid]))
|
|
223
|
+
return api.cartesian_product(*grid)
|
|
224
|
+
# if not isinstance(grid[0], np.ndarray):
|
|
225
|
+
# import torch
|
|
226
|
+
# assert isinstance(grid[0], torch.Tensor)
|
|
227
|
+
# from multipers.torch.diff_grids import todense
|
|
228
|
+
# return todense(grid)
|
|
229
|
+
# dtype = grid[0].dtype
|
|
230
|
+
# if product_order:
|
|
231
|
+
# return np.fromiter(product(*grid), dtype=np.dtype((dtype, len(grid))), count=np.prod([len(f) for f in grid]))
|
|
232
|
+
# mesh = np.meshgrid(*grid)
|
|
233
|
+
# coordinates = np.stack(mesh, axis=-1).reshape(-1, len(grid)).astype(dtype)
|
|
234
|
+
# return coordinates
|
|
228
235
|
|
|
229
236
|
|
|
230
237
|
|
|
231
238
|
## TODO : optimize. Pykeops ?
|
|
232
|
-
def
|
|
239
|
+
def _todo_regular(f, int r, api):
|
|
240
|
+
with api.no_grad():
|
|
241
|
+
return api.linspace(api.min(f), api.max(f), r)
|
|
242
|
+
|
|
243
|
+
def _project_on_1d_grid(f,grid, bool unique, api):
|
|
244
|
+
# api=api_from_tensors(f,grid)
|
|
245
|
+
if f.ndim != 1:
|
|
246
|
+
raise ValueError(f"Got ndim!=1. {f=}")
|
|
247
|
+
f = api.unique(f)
|
|
248
|
+
with api.no_grad():
|
|
249
|
+
_f = api.LazyTensor(f[:, None, None])
|
|
250
|
+
_f_reg = api.LazyTensor(grid[None, :, None])
|
|
251
|
+
indices = (_f - _f_reg).abs().argmin(0).ravel()
|
|
252
|
+
f = api.cat([f, api.tensor([api.inf], dtype=f.dtype)])
|
|
253
|
+
f_proj = f[indices]
|
|
254
|
+
if unique:
|
|
255
|
+
f_proj = api.unique(f_proj)
|
|
256
|
+
return f_proj
|
|
257
|
+
|
|
258
|
+
def _todo_regular_closest_keops(f, int r, bool unique, api):
|
|
259
|
+
f = api.astensor(f)
|
|
260
|
+
with api.no_grad():
|
|
261
|
+
f_regular = api.linspace(api.min(f), api.max(f), r, device = api.device(f),dtype=f.dtype)
|
|
262
|
+
return _project_on_1d_grid(f,f_regular,unique,api)
|
|
263
|
+
|
|
264
|
+
def _todo_regular_closest_old(some_float[:] f, int r, bool unique, api=None):
|
|
233
265
|
f_array = np.asarray(f)
|
|
234
266
|
f_regular = np.linspace(np.min(f), np.max(f),num=r, dtype=f_array.dtype)
|
|
235
|
-
f_regular_closest = np.asarray([f[<int64_t>np.argmin(np.abs(f_array-f_regular[i]))] for i in range(r)])
|
|
267
|
+
f_regular_closest = np.asarray([f[<int64_t>np.argmin(np.abs(f_array-f_regular[i]))] for i in range(r)], dtype=f_array.dtype)
|
|
236
268
|
if unique: f_regular_closest = np.unique(f_regular_closest)
|
|
237
269
|
return f_regular_closest
|
|
238
270
|
|
|
239
|
-
def _todo_regular_left(
|
|
271
|
+
def _todo_regular_left(f, int r, bool unique,api):
|
|
272
|
+
sorted_f = api.sort(f)
|
|
273
|
+
with api.no_grad():
|
|
274
|
+
f_regular = api.linspace(sorted_f[0],sorted_f[-1],r, dtype=sorted_f.dtype, device=api.device(sorted_f))
|
|
275
|
+
idx=api.searchsorted(sorted_f,f_regular)
|
|
276
|
+
f_regular_closest = sorted_f[idx]
|
|
277
|
+
if unique: f_regular_closest = api.unique(f_regular_closest)
|
|
278
|
+
return f_regular_closest
|
|
279
|
+
|
|
280
|
+
def _todo_regular_left_old(some_float[:] f, int r, bool unique):
|
|
240
281
|
sorted_f = np.sort(f)
|
|
241
282
|
f_regular = np.linspace(sorted_f[0],sorted_f[-1],num=r, dtype=sorted_f.dtype)
|
|
242
283
|
f_regular_closest = sorted_f[np.searchsorted(sorted_f,f_regular)]
|
|
243
284
|
if unique: f_regular_closest = np.unique(f_regular_closest)
|
|
244
285
|
return f_regular_closest
|
|
245
286
|
|
|
246
|
-
def _torch_regular_closest(f, int r, bool unique=True):
|
|
247
|
-
import torch
|
|
248
|
-
f_regular = torch.linspace(f.min(),f.max(), r, dtype=f.dtype)
|
|
249
|
-
f_regular_closest =torch.tensor([f[(f-x).abs().argmin()] for x in f_regular])
|
|
250
|
-
if unique: f_regular_closest = f_regular_closest.unique()
|
|
251
|
-
return f_regular_closest
|
|
252
287
|
|
|
253
288
|
def _todo_partition(some_float[:] data,int resolution, bool unique):
|
|
254
289
|
if data.shape[0] < resolution: resolution=data.shape[0]
|
|
@@ -259,6 +294,12 @@ def _todo_partition(some_float[:] data,int resolution, bool unique):
|
|
|
259
294
|
return f
|
|
260
295
|
|
|
261
296
|
|
|
297
|
+
if check_keops():
|
|
298
|
+
_todo_regular_closest = _todo_regular_closest_keops
|
|
299
|
+
else:
|
|
300
|
+
_todo_regular_closest = _todo_regular_closest_old
|
|
301
|
+
|
|
302
|
+
|
|
262
303
|
def compute_bounding_box(stuff, inflate = 0.):
|
|
263
304
|
r"""
|
|
264
305
|
Returns a array of shape (2, num_parameters)
|
multipers/io.cp313-win_amd64.pyd
CHANGED
|
Binary file
|
multipers/ml/signed_measures.py
CHANGED
|
@@ -9,12 +9,107 @@ from sklearn.base import BaseEstimator, TransformerMixin
|
|
|
9
9
|
from tqdm import tqdm
|
|
10
10
|
|
|
11
11
|
import multipers as mp
|
|
12
|
-
from multipers.array_api import api_from_tensor
|
|
12
|
+
from multipers.array_api import api_from_tensor, api_from_tensors
|
|
13
13
|
from multipers.filtrations.density import available_kernels, convolution_signed_measures
|
|
14
|
-
from multipers.grids import compute_grid
|
|
14
|
+
from multipers.grids import compute_grid, todense
|
|
15
15
|
from multipers.point_measure import rank_decomposition_by_rectangles, signed_betti
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def batch_signed_measure_convolutions(
|
|
19
|
+
signed_measures, # array of shape (num_data,num_pts,D)
|
|
20
|
+
x, # array of shape (num_x, D) or (num_data, num_x, D)
|
|
21
|
+
bandwidth, # either float or matrix if multivariate kernel
|
|
22
|
+
kernel: available_kernels,
|
|
23
|
+
api=None,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Input
|
|
27
|
+
-----
|
|
28
|
+
- signed_measures: unragged, of shape (num_data, num_pts, D+1)
|
|
29
|
+
where last coord is weights, (0 for dummy points)
|
|
30
|
+
- x : the points to convolve (num_x,D)
|
|
31
|
+
- bandwidth : the bandwidths or covariance matrix inverse or ... of the kernel
|
|
32
|
+
- kernel : "gaussian", "multivariate_gaussian", "exponential", or Callable (x_i, y_i, bandwidth)->float
|
|
33
|
+
|
|
34
|
+
Output
|
|
35
|
+
------
|
|
36
|
+
Array of shape (num_convolutions, (num_axis), num_data,
|
|
37
|
+
Array of shape (num_convolutions, (num_axis), num_data, max_x_size)
|
|
38
|
+
"""
|
|
39
|
+
from multipers.filtrations.density import _kernel
|
|
40
|
+
|
|
41
|
+
if api is None:
|
|
42
|
+
api = api_from_tensors(signed_measures, x)
|
|
43
|
+
if signed_measures.ndim == 2:
|
|
44
|
+
signed_measures = signed_measures[None, :, :]
|
|
45
|
+
sms = signed_measures[..., :-1]
|
|
46
|
+
weights = signed_measures[..., -1]
|
|
47
|
+
_sms = api.LazyTensor(api.ascontiguous(sms[..., None, :]))
|
|
48
|
+
_x = api.ascontiguous(x[..., None, :, :])
|
|
49
|
+
|
|
50
|
+
sms_kernel = _kernel(kernel)(_sms, _x, bandwidth)
|
|
51
|
+
out = (sms_kernel * api.ascontiguous(weights[..., None, None])).sum(
|
|
52
|
+
signed_measures.ndim - 2
|
|
53
|
+
)
|
|
54
|
+
assert out.shape[-1] == 1, "Pykeops bug fixed, TODO : refix this "
|
|
55
|
+
out = out[..., 0] ## pykeops bug + ensures its a tensor
|
|
56
|
+
# assert out.shape == (x.shape[0], x.shape[1]), f"{x.shape=}, {out.shape=}"
|
|
57
|
+
return out
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def sm2deep(signed_measure, api=None):
|
|
61
|
+
if api is None:
|
|
62
|
+
api = api_from_tensor(signed_measure[0])
|
|
63
|
+
dirac_positions, dirac_signs = signed_measure
|
|
64
|
+
dtype = dirac_positions.dtype
|
|
65
|
+
new_shape = list(dirac_positions.shape)
|
|
66
|
+
new_shape[1] += 1
|
|
67
|
+
c = api.empty(new_shape, dtype=dtype)
|
|
68
|
+
c[:, :-1] = dirac_positions
|
|
69
|
+
c[:, -1] = api.astensor(dirac_signs)
|
|
70
|
+
return c
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def deep_unrag(sms, api=None):
|
|
74
|
+
if api is None:
|
|
75
|
+
api = api_from_tensor(sms[0][0])
|
|
76
|
+
num_sm = len(sms)
|
|
77
|
+
if num_sm == 0:
|
|
78
|
+
return api.tensor([])
|
|
79
|
+
first = sms[0][0]
|
|
80
|
+
num_parameters = first.shape[1]
|
|
81
|
+
dtype = first.dtype
|
|
82
|
+
deep_sms = tuple(sm2deep(sm, api=api) for sm in sms)
|
|
83
|
+
max_num_pts = np.max([sm[0].shape[0] for sm in sms])
|
|
84
|
+
unragged_sms = api.zeros((num_sm, max_num_pts, num_parameters + 1), dtype=dtype)
|
|
85
|
+
|
|
86
|
+
for data in range(num_sm):
|
|
87
|
+
sm = deep_sms[data]
|
|
88
|
+
a, b = sm.shape
|
|
89
|
+
unragged_sms[data, :a, :b] = sm
|
|
90
|
+
return unragged_sms
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def sm_convolution(
|
|
94
|
+
sms,
|
|
95
|
+
grid,
|
|
96
|
+
bandwidth,
|
|
97
|
+
kernel: available_kernels = "gaussian",
|
|
98
|
+
plot: bool = False,
|
|
99
|
+
**plt_kwargs,
|
|
100
|
+
):
|
|
101
|
+
dense_grid = todense(grid)
|
|
102
|
+
api = api_from_tensors(sms[0][0], dense_grid)
|
|
103
|
+
sms = deep_unrag(sms, api=api)
|
|
104
|
+
convs = batch_signed_measure_convolutions(
|
|
105
|
+
sms, dense_grid, bandwidth, kernel, api=api
|
|
106
|
+
).reshape(sms.shape[0], *(len(g) for g in grid))
|
|
107
|
+
if plot:
|
|
108
|
+
from multipers.plots import plot_surfaces
|
|
109
|
+
plot_surfaces((grid, convs), **plt_kwargs)
|
|
110
|
+
return convs
|
|
111
|
+
|
|
112
|
+
|
|
18
113
|
class FilteredComplex2SignedMeasure(BaseEstimator, TransformerMixin):
|
|
19
114
|
"""
|
|
20
115
|
Input
|
|
@@ -547,27 +642,6 @@ def rescale_sparse_signed_measure(
|
|
|
547
642
|
return out
|
|
548
643
|
|
|
549
644
|
|
|
550
|
-
def sm2deep(signed_measure):
|
|
551
|
-
dirac_positions, dirac_signs = signed_measure
|
|
552
|
-
dtype = dirac_positions.dtype
|
|
553
|
-
new_shape = list(dirac_positions.shape)
|
|
554
|
-
new_shape[1] += 1
|
|
555
|
-
if isinstance(dirac_positions, np.ndarray):
|
|
556
|
-
c = np.empty(new_shape, dtype=dtype)
|
|
557
|
-
c[:, :-1] = dirac_positions
|
|
558
|
-
c[:, -1] = dirac_signs
|
|
559
|
-
|
|
560
|
-
else:
|
|
561
|
-
import torch
|
|
562
|
-
|
|
563
|
-
c = torch.empty(new_shape, dtype=dtype)
|
|
564
|
-
c[:, :-1] = dirac_positions
|
|
565
|
-
if isinstance(dirac_signs, np.ndarray):
|
|
566
|
-
dirac_signs = torch.from_numpy(dirac_signs)
|
|
567
|
-
c[:, -1] = dirac_signs
|
|
568
|
-
return c
|
|
569
|
-
|
|
570
|
-
|
|
571
645
|
class SignedMeasureFormatter(BaseEstimator, TransformerMixin):
|
|
572
646
|
"""
|
|
573
647
|
Input
|
|
@@ -759,7 +833,9 @@ class SignedMeasureFormatter(BaseEstimator, TransformerMixin):
|
|
|
759
833
|
self._filtrations_bounds.append(filtration_bounds)
|
|
760
834
|
self._normalization_factors.append(normalization_factors)
|
|
761
835
|
self._filtrations_bounds = self._backend.astensor(self._filtrations_bounds)
|
|
762
|
-
self._normalization_factors = self._backend.astensor(
|
|
836
|
+
self._normalization_factors = self._backend.astensor(
|
|
837
|
+
self._normalization_factors
|
|
838
|
+
)
|
|
763
839
|
# else:
|
|
764
840
|
# (
|
|
765
841
|
# self._filtrations_bounds,
|
|
@@ -784,9 +860,11 @@ class SignedMeasureFormatter(BaseEstimator, TransformerMixin):
|
|
|
784
860
|
]
|
|
785
861
|
# axis, filtration_values
|
|
786
862
|
filtration_values = [
|
|
787
|
-
self._backend.astensor(
|
|
788
|
-
|
|
789
|
-
|
|
863
|
+
self._backend.astensor(
|
|
864
|
+
compute_grid(
|
|
865
|
+
f_ax.T, resolution=self.resolution, strategy=self.grid_strategy
|
|
866
|
+
)
|
|
867
|
+
)
|
|
790
868
|
for f_ax in filtration_values
|
|
791
869
|
]
|
|
792
870
|
self._infered_grids = filtration_values
|
|
Binary file
|
multipers/mma_structures.pyx
CHANGED
|
@@ -751,7 +751,7 @@ cdef class PyModule_f64:
|
|
|
751
751
|
axs = [plt.gca()]
|
|
752
752
|
for image, degree, i in zip(image_vector, degrees, range(num_degrees)):
|
|
753
753
|
ax = axs[i]
|
|
754
|
-
temp = multipers.plots.plot_surface(grid, image
|
|
754
|
+
temp = multipers.plots.plot_surface(grid, image, ax=ax)
|
|
755
755
|
plt.colorbar(temp, ax = ax)
|
|
756
756
|
if degree < 0 :
|
|
757
757
|
ax.set_title(rf"$H_{i}$ $2$-persistence image")
|
|
@@ -1712,7 +1712,7 @@ cdef class PyModule_f32:
|
|
|
1712
1712
|
axs = [plt.gca()]
|
|
1713
1713
|
for image, degree, i in zip(image_vector, degrees, range(num_degrees)):
|
|
1714
1714
|
ax = axs[i]
|
|
1715
|
-
temp = multipers.plots.plot_surface(grid, image
|
|
1715
|
+
temp = multipers.plots.plot_surface(grid, image, ax=ax)
|
|
1716
1716
|
plt.colorbar(temp, ax = ax)
|
|
1717
1717
|
if degree < 0 :
|
|
1718
1718
|
ax.set_title(rf"$H_{i}$ $2$-persistence image")
|
multipers/mma_structures.pyx.tp
CHANGED
|
@@ -773,7 +773,7 @@ cdef class PyModule_{{SHORT}}:
|
|
|
773
773
|
axs = [plt.gca()]
|
|
774
774
|
for image, degree, i in zip(image_vector, degrees, range(num_degrees)):
|
|
775
775
|
ax = axs[i]
|
|
776
|
-
temp = multipers.plots.plot_surface(grid, image
|
|
776
|
+
temp = multipers.plots.plot_surface(grid, image, ax=ax)
|
|
777
777
|
plt.colorbar(temp, ax = ax)
|
|
778
778
|
if degree < 0 :
|
|
779
779
|
ax.set_title(rf"$H_{i}$ $2$-persistence image")
|
|
Binary file
|
multipers/plots.py
CHANGED
|
@@ -216,7 +216,7 @@ def plot_surface(
|
|
|
216
216
|
return im
|
|
217
217
|
|
|
218
218
|
if contour:
|
|
219
|
-
levels = plt_args.pop("levels",
|
|
219
|
+
levels = plt_args.pop("levels", 50)
|
|
220
220
|
im = ax.contourf(grid[0], grid[1], hf.T, cmap=cmap, levels=levels, **plt_args)
|
|
221
221
|
else:
|
|
222
222
|
im = ax.pcolormesh(grid[0], grid[1], hf.T, cmap=cmap, **plt_args)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
multipers/slicer.pxd
CHANGED
|
@@ -2013,51 +2013,51 @@ cdef extern from "multiparameter_module_approximation/approximation.h" namespace
|
|
|
2013
2013
|
|
|
2014
2014
|
import multipers.slicer as mps
|
|
2015
2015
|
from cython.operator cimport dereference
|
|
2016
|
-
cdef inline Module[
|
|
2016
|
+
cdef inline Module[float] _multiparameter_module_approximation_f32(object slicer, One_critical_filtration[float] direction, float max_error, Box[float] box, bool threshold, bool complete, bool verbose):
|
|
2017
2017
|
import multipers.slicer as mps
|
|
2018
2018
|
cdef intptr_t slicer_ptr = <intptr_t>(slicer.get_ptr())
|
|
2019
|
-
cdef Module[
|
|
2019
|
+
cdef Module[float] mod
|
|
2020
2020
|
if False:
|
|
2021
2021
|
pass
|
|
2022
|
-
elif isinstance(slicer, mps.
|
|
2022
|
+
elif isinstance(slicer, mps._KSlicer_Matrix0_vine_f32):
|
|
2023
2023
|
with nogil:
|
|
2024
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2024
|
+
mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix0_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2025
2025
|
return mod
|
|
2026
|
-
elif isinstance(slicer, mps.
|
|
2026
|
+
elif isinstance(slicer, mps._KSlicer_Matrix1_vine_f32):
|
|
2027
2027
|
with nogil:
|
|
2028
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2028
|
+
mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix1_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2029
2029
|
return mod
|
|
2030
|
-
elif isinstance(slicer, mps.
|
|
2030
|
+
elif isinstance(slicer, mps._Slicer_Matrix0_vine_f32):
|
|
2031
2031
|
with nogil:
|
|
2032
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2032
|
+
mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix0_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2033
2033
|
return mod
|
|
2034
|
-
elif isinstance(slicer, mps.
|
|
2034
|
+
elif isinstance(slicer, mps._Slicer_Matrix1_vine_f32):
|
|
2035
2035
|
with nogil:
|
|
2036
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2036
|
+
mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix1_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2037
2037
|
return mod
|
|
2038
2038
|
else:
|
|
2039
2039
|
raise ValueError(f"Unsupported slicer type {type(slicer)}")
|
|
2040
|
-
cdef inline Module[
|
|
2040
|
+
cdef inline Module[double] _multiparameter_module_approximation_f64(object slicer, One_critical_filtration[double] direction, double max_error, Box[double] box, bool threshold, bool complete, bool verbose):
|
|
2041
2041
|
import multipers.slicer as mps
|
|
2042
2042
|
cdef intptr_t slicer_ptr = <intptr_t>(slicer.get_ptr())
|
|
2043
|
-
cdef Module[
|
|
2043
|
+
cdef Module[double] mod
|
|
2044
2044
|
if False:
|
|
2045
2045
|
pass
|
|
2046
|
-
elif isinstance(slicer, mps.
|
|
2046
|
+
elif isinstance(slicer, mps._KSlicer_Matrix0_vine_f64):
|
|
2047
2047
|
with nogil:
|
|
2048
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2048
|
+
mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix0_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2049
2049
|
return mod
|
|
2050
|
-
elif isinstance(slicer, mps.
|
|
2050
|
+
elif isinstance(slicer, mps._KSlicer_Matrix1_vine_f64):
|
|
2051
2051
|
with nogil:
|
|
2052
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2052
|
+
mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix1_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2053
2053
|
return mod
|
|
2054
|
-
elif isinstance(slicer, mps.
|
|
2054
|
+
elif isinstance(slicer, mps._Slicer_Matrix0_vine_f64):
|
|
2055
2055
|
with nogil:
|
|
2056
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2056
|
+
mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix0_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2057
2057
|
return mod
|
|
2058
|
-
elif isinstance(slicer, mps.
|
|
2058
|
+
elif isinstance(slicer, mps._Slicer_Matrix1_vine_f64):
|
|
2059
2059
|
with nogil:
|
|
2060
|
-
mod = multiparameter_module_approximation(dereference(<
|
|
2060
|
+
mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix1_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
|
|
2061
2061
|
return mod
|
|
2062
2062
|
else:
|
|
2063
2063
|
raise ValueError(f"Unsupported slicer type {type(slicer)}")
|
multipers/slicer.pyx
CHANGED
|
@@ -19876,7 +19876,7 @@ cdef extern from "gudhi/cubical_to_boundary.h" namespace "":
|
|
|
19876
19876
|
void _to_boundary(const vector[unsigned int]&, vector[vector[unsigned int]]&, vector[int]&) except + nogil
|
|
19877
19877
|
void get_vertices(unsigned int, cset[unsigned int]&, const vector[vector[unsigned int]]&) nogil
|
|
19878
19878
|
|
|
19879
|
-
def
|
|
19879
|
+
def _from_bitmapi64(image, **slicer_kwargs):
|
|
19880
19880
|
from multipers import Slicer
|
|
19881
19881
|
dtype = slicer_kwargs.get("dtype", image.dtype)
|
|
19882
19882
|
slicer_kwargs["dtype"] = dtype
|
|
@@ -19894,9 +19894,9 @@ def _from_bitmapf32(image, **slicer_kwargs):
|
|
|
19894
19894
|
cdef cset[unsigned int] vertices
|
|
19895
19895
|
|
|
19896
19896
|
cdef unsigned int num_gens = gen_dims.size()
|
|
19897
|
-
filtration_values = np.zeros(shape=(num_gens, num_parameters), dtype = np.
|
|
19898
|
-
cdef
|
|
19899
|
-
cdef
|
|
19897
|
+
filtration_values = np.zeros(shape=(num_gens, num_parameters), dtype = np.int64) - _Slicer._inf_value()
|
|
19898
|
+
cdef int64_t[:,:] F = filtration_values
|
|
19899
|
+
cdef int64_t[:,:] c_img = image.reshape(-1,num_parameters)
|
|
19900
19900
|
with nogil:
|
|
19901
19901
|
for i in range(num_gens):
|
|
19902
19902
|
# with gil:
|
|
@@ -19914,7 +19914,7 @@ def _from_bitmapf32(image, **slicer_kwargs):
|
|
|
19914
19914
|
# print(f"F = {np.asarray(F[i])}")
|
|
19915
19915
|
slicer = _Slicer(gen_maps, gen_dims, filtration_values)
|
|
19916
19916
|
return slicer
|
|
19917
|
-
def
|
|
19917
|
+
def _from_bitmapf32(image, **slicer_kwargs):
|
|
19918
19918
|
from multipers import Slicer
|
|
19919
19919
|
dtype = slicer_kwargs.get("dtype", image.dtype)
|
|
19920
19920
|
slicer_kwargs["dtype"] = dtype
|
|
@@ -19932,9 +19932,9 @@ def _from_bitmapi32(image, **slicer_kwargs):
|
|
|
19932
19932
|
cdef cset[unsigned int] vertices
|
|
19933
19933
|
|
|
19934
19934
|
cdef unsigned int num_gens = gen_dims.size()
|
|
19935
|
-
filtration_values = np.zeros(shape=(num_gens, num_parameters), dtype = np.
|
|
19936
|
-
cdef
|
|
19937
|
-
cdef
|
|
19935
|
+
filtration_values = np.zeros(shape=(num_gens, num_parameters), dtype = np.float32) - _Slicer._inf_value()
|
|
19936
|
+
cdef float[:,:] F = filtration_values
|
|
19937
|
+
cdef float[:,:] c_img = image.reshape(-1,num_parameters)
|
|
19938
19938
|
with nogil:
|
|
19939
19939
|
for i in range(num_gens):
|
|
19940
19940
|
# with gil:
|
|
@@ -19952,7 +19952,7 @@ def _from_bitmapi32(image, **slicer_kwargs):
|
|
|
19952
19952
|
# print(f"F = {np.asarray(F[i])}")
|
|
19953
19953
|
slicer = _Slicer(gen_maps, gen_dims, filtration_values)
|
|
19954
19954
|
return slicer
|
|
19955
|
-
def
|
|
19955
|
+
def _from_bitmapi32(image, **slicer_kwargs):
|
|
19956
19956
|
from multipers import Slicer
|
|
19957
19957
|
dtype = slicer_kwargs.get("dtype", image.dtype)
|
|
19958
19958
|
slicer_kwargs["dtype"] = dtype
|
|
@@ -19970,9 +19970,9 @@ def _from_bitmapi64(image, **slicer_kwargs):
|
|
|
19970
19970
|
cdef cset[unsigned int] vertices
|
|
19971
19971
|
|
|
19972
19972
|
cdef unsigned int num_gens = gen_dims.size()
|
|
19973
|
-
filtration_values = np.zeros(shape=(num_gens, num_parameters), dtype = np.
|
|
19974
|
-
cdef
|
|
19975
|
-
cdef
|
|
19973
|
+
filtration_values = np.zeros(shape=(num_gens, num_parameters), dtype = np.int32) - _Slicer._inf_value()
|
|
19974
|
+
cdef int32_t[:,:] F = filtration_values
|
|
19975
|
+
cdef int32_t[:,:] c_img = image.reshape(-1,num_parameters)
|
|
19976
19976
|
with nogil:
|
|
19977
19977
|
for i in range(num_gens):
|
|
19978
19978
|
# with gil:
|
|
@@ -20031,12 +20031,12 @@ def _from_bitmapf64(image, **slicer_kwargs):
|
|
|
20031
20031
|
|
|
20032
20032
|
def from_bitmap(img, **kwargs):
|
|
20033
20033
|
img = np.asarray(img)
|
|
20034
|
+
if img.dtype == np.int64:
|
|
20035
|
+
return _from_bitmapi64(img, **kwargs)
|
|
20034
20036
|
if img.dtype == np.float32:
|
|
20035
20037
|
return _from_bitmapf32(img, **kwargs)
|
|
20036
20038
|
if img.dtype == np.int32:
|
|
20037
20039
|
return _from_bitmapi32(img, **kwargs)
|
|
20038
|
-
if img.dtype == np.int64:
|
|
20039
|
-
return _from_bitmapi64(img, **kwargs)
|
|
20040
20040
|
if img.dtype == np.float64:
|
|
20041
20041
|
return _from_bitmapf64(img, **kwargs)
|
|
20042
20042
|
raise ValueError(f"Invalid dtype. Got {img.dtype=}, was expecting {available_dtype=}.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multipers
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.3b7
|
|
4
4
|
Summary: Multiparameter Topological Persistence for Machine Learning
|
|
5
5
|
Author-email: David Loiseaux <david.lapous@proton.me>, Hannah Schreiber <hannah.schreiber@inria.fr>
|
|
6
6
|
Maintainer-email: David Loiseaux <david.lapous@proton.me>
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
multipers/__init__.py,sha256=y4BfbXAY2M2yZYob758IegZHwIbRgmKrph6lwe6WR6Q,733
|
|
2
|
-
multipers/_signed_measure_meta.py,sha256=
|
|
2
|
+
multipers/_signed_measure_meta.py,sha256=YgGhy6miY2sagJMzV8JAfos-aoR4JV6F7RUNjpmT3nU,17676
|
|
3
3
|
multipers/_slicer_meta.py,sha256=mjIGR-B6HnQLSiMowEQ8EWQkD_IF3bBnDVzvwrZugZ4,7203
|
|
4
4
|
multipers/distances.py,sha256=uAZj2GtUQp50OxN2qU7sl2JqsmJ74IG9j5tZapLO2Us,6220
|
|
5
5
|
multipers/filtration_conversions.pxd,sha256=Je7a3F4zS1PQn6Ul1YCXgA6p39X2FouStru-XtN-aOw,10800
|
|
6
6
|
multipers/filtration_conversions.pxd.tp,sha256=_9tUvZVUA7J_RUM3q7BxY48fYgDHCUA7Xhy4nBfLLs0,3309
|
|
7
7
|
multipers/filtrations.pxd,sha256=08ONkZNCjs8Nme8lcD9myPz-K662sA-EDpSwzgC2_ts,9461
|
|
8
|
-
multipers/function_rips.cp313-win_amd64.pyd,sha256=
|
|
8
|
+
multipers/function_rips.cp313-win_amd64.pyd,sha256=Vy6KUtnb9z6Ocb_m8D1xK9nVRjVskSl19HjhOSEWUtY,332288
|
|
9
9
|
multipers/function_rips.pyx,sha256=j5NjbK3YrAv_2s8YHB1JB0k6m9NC7RQCSFlJe-w_kgE,5252
|
|
10
|
-
multipers/grids.cp313-win_amd64.pyd,sha256=
|
|
11
|
-
multipers/grids.pyx,sha256=
|
|
12
|
-
multipers/io.cp313-win_amd64.pyd,sha256=
|
|
10
|
+
multipers/grids.cp313-win_amd64.pyd,sha256=hfX8m-SdBYlM_i9V_RF08xvcLJKt0TIyIZ2DMr2Nsss,472576
|
|
11
|
+
multipers/grids.pyx,sha256=4329-4IwlvZKEsmVXrG91kNl1pi-q5RzSrl8EHo2EKk,17981
|
|
12
|
+
multipers/io.cp313-win_amd64.pyd,sha256=wtJDNCLp22AQqsO9iEJCG_MCifueTf8Lc8Jiq2jXTEY,220160
|
|
13
13
|
multipers/io.pyx,sha256=pQBH_rSqaCZqDSxTLnhlyECP3fLbX2tR_RKJHydHm_0,22210
|
|
14
|
-
multipers/mma_structures.cp313-win_amd64.pyd,sha256=
|
|
14
|
+
multipers/mma_structures.cp313-win_amd64.pyd,sha256=IbiCf8rnWhcpzFOVJayY9a7tBPW25-_USXxcwuYeqJ4,1280512
|
|
15
15
|
multipers/mma_structures.pxd,sha256=jh1QnQRidt_VK0CK7losQi6rAl_1qG5DNuR23J42pUA,6595
|
|
16
|
-
multipers/mma_structures.pyx,sha256=
|
|
17
|
-
multipers/mma_structures.pyx.tp,sha256=
|
|
16
|
+
multipers/mma_structures.pyx,sha256=1FQ_sqkpHHkCyCLLKkR9lUgZKDZFt-YgjnDd8NGmw6Q,109526
|
|
17
|
+
multipers/mma_structures.pyx.tp,sha256=QLo4ZZDnFuWNot771jYkYHc9ZQq6CJIpkRkeRwGi7XA,42297
|
|
18
18
|
multipers/multiparameter_edge_collapse.py,sha256=MFt0eKQQSv2354omeIqOmzASYTKIMsYdxZHFZauQr8g,1229
|
|
19
|
-
multipers/multiparameter_module_approximation.cp313-win_amd64.pyd,sha256=
|
|
19
|
+
multipers/multiparameter_module_approximation.cp313-win_amd64.pyd,sha256=30WzGs1urkKh5i3IluiRxsLUVnMHRKPwrHGjymNdb7U,450560
|
|
20
20
|
multipers/multiparameter_module_approximation.pyx,sha256=wp7la7Z9wBngnfw6WOVddf93mPyXf4HfNT6dKW2Z0r0,9030
|
|
21
21
|
multipers/pickle.py,sha256=YYVt4iHiD16E1x5Yn_4mX6P5P8rKi56pNGjJo5IzPhc,2579
|
|
22
|
-
multipers/plots.py,sha256
|
|
23
|
-
multipers/point_measure.cp313-win_amd64.pyd,sha256=
|
|
22
|
+
multipers/plots.py,sha256=ZJmOD5fgy0y_U-rjhR6pZ2WD8HFndpKMh5IKlU62QkI,14918
|
|
23
|
+
multipers/point_measure.cp313-win_amd64.pyd,sha256=HeyDYV6wRyvBf0YwwpsJ0tY3pl-XnV7LHNEhFy4NyLA,596480
|
|
24
24
|
multipers/point_measure.pyx,sha256=ovpyvjMdOPBOK_0TT0VoK4b2t8m5InFdKmRBB6biTts,14077
|
|
25
|
-
multipers/simplex_tree_multi.cp313-win_amd64.pyd,sha256=
|
|
25
|
+
multipers/simplex_tree_multi.cp313-win_amd64.pyd,sha256=wBH0k6ELNIH6kAAXbYM74-8AZecNqUY5wjmRA_kE3q4,3612672
|
|
26
26
|
multipers/simplex_tree_multi.pxd,sha256=KpyDEQNPoMC2sOU9-d4LtrGXx_UVCJGxMJ1kk1AzHgU,6631
|
|
27
27
|
multipers/simplex_tree_multi.pyx,sha256=13x2WFMExcukd_2CrLOJa3Edex3WNqaSSrGBmPhYoDM,499100
|
|
28
28
|
multipers/simplex_tree_multi.pyx.tp,sha256=YFzVUJPWNOOpRiyOgaYlFR_DJ_57rAQLsOp2W0l71_s,89318
|
|
29
|
-
multipers/slicer.cp313-win_amd64.pyd,sha256=
|
|
30
|
-
multipers/slicer.pxd,sha256=
|
|
29
|
+
multipers/slicer.cp313-win_amd64.pyd,sha256=rOlKuzDnyAfQiaK0lJz0E6HkbOQZI-Hjye9TuIiMYGQ,11666432
|
|
30
|
+
multipers/slicer.pxd,sha256=bHcV5COzDcjhaVcL3bWSHivUYtdkSx4bdj24fVOe_DU,185230
|
|
31
31
|
multipers/slicer.pxd.tp,sha256=fLOUPtPGqiY9o1fPDyc_whBrgKLh_6HVfvl7OtE-34Y,10243
|
|
32
|
-
multipers/slicer.pyx,sha256=
|
|
32
|
+
multipers/slicer.pyx,sha256=jgPdo6ERvmQOYJE-L4XtOFOeRsZO5qGsVSpgLKj604E,894493
|
|
33
33
|
multipers/slicer.pyx.tp,sha256=GCL7xUZccfmnIoY4bk05L5ODkSNzEzXgodp0VkH-xT8,44584
|
|
34
34
|
multipers/tbb12.dll,sha256=flSoIAkNVECA5vNkh5jLn1jcZNxTw3yb2tuTtxn6oEE,344576
|
|
35
35
|
multipers/tbbbind_2_5.dll,sha256=4JWlgl4aJJ3m5NIE85AgQi46REU3UkIJwNsKnh9aK_c,23552
|
|
@@ -37,9 +37,9 @@ multipers/tbbmalloc.dll,sha256=r9y_3nR6Rpc68g1F7Oci8EpunjWIJQU-zjLIVXc9SDY,11366
|
|
|
37
37
|
multipers/tbbmalloc_proxy.dll,sha256=5vcuiOpM9KTAY7daWxCsAc2MU_l8K3TWpw0kz-Xh0sk,30720
|
|
38
38
|
multipers/tensor.pxd,sha256=MSmaMU0sOP9CHLmg4dym7nOGaI1S4cOdM01TQ9flI54,417
|
|
39
39
|
multipers/test.pyx,sha256=-g7WU-jKrZK8H0c-6eAPsfrApjvTKrUoswVYFu8LoV4,1798
|
|
40
|
-
multipers/array_api/__init__.py,sha256=
|
|
41
|
-
multipers/array_api/numpy.py,sha256=
|
|
42
|
-
multipers/array_api/torch.py,sha256=
|
|
40
|
+
multipers/array_api/__init__.py,sha256=jyvi7Gzd7Si_imgaodz80fcA-68MikAGk8g79_vj6yo,1545
|
|
41
|
+
multipers/array_api/numpy.py,sha256=HZHJitXSb2Y3f-JNg47afmYoExgqT6dOx7sfcM9vvaA,2324
|
|
42
|
+
multipers/array_api/torch.py,sha256=7PKiSgA4TCaTspsC7wiMXUtHgSiTfSFyNtuMYU31xNo,2550
|
|
43
43
|
multipers/data/MOL2.py,sha256=nLZHy2OSFN9Z2uJKsbqWOEG2R7G-uH6dCLHG48UjvR4,15428
|
|
44
44
|
multipers/data/UCR.py,sha256=PuT8l3i26y0goBzIESwdgJAe6YFCyDiWSoxECcP5rhs,798
|
|
45
45
|
multipers/data/__init__.py,sha256=w7uUe4LOHbdbKU4R8MNs7em65wZJN0v5ukoG1otFanQ,24
|
|
@@ -50,7 +50,7 @@ multipers/data/pytorch2simplextree.py,sha256=cvOJTUleK_qEbcpygRD77GuQl_0qDsSjjD6
|
|
|
50
50
|
multipers/data/shape3d.py,sha256=AE-vvjKrhKxOwMo-lurUsFqqLjIg5obo-RTbRZF_5Mk,3893
|
|
51
51
|
multipers/data/synthetic.py,sha256=RvLWIBE5j99kJSt-D7cnPGI3c7skD4p8_qofJbMIXM0,3078
|
|
52
52
|
multipers/filtrations/__init__.py,sha256=Lg0EHe2cxT32UQAg0kr_Vpua-xPBZxGol8VIfz8UwWk,319
|
|
53
|
-
multipers/filtrations/density.py,sha256=
|
|
53
|
+
multipers/filtrations/density.py,sha256=XNPHCwyC8FwAdSfNp1ofxcxfP5ZFGZ6K-DUeXsfM4eg,18155
|
|
54
54
|
multipers/filtrations/filtrations.py,sha256=r3WS4GMBvcJ0ryIMbNHFgep0FCtl3h404V_SbMM8hN0,13333
|
|
55
55
|
multipers/gudhi/Persistence_slices_interface.h,sha256=QnUeCCKi9K8CfqI3W5i3Ra1Jy2Z1IIivr3MIpnBsnYU,6562
|
|
56
56
|
multipers/gudhi/Simplex_tree_interface.h,sha256=kkq8pE3jKGLY1dK7sYpb_uERHaWGurrRXfaw_ygs-mY,10217
|
|
@@ -146,7 +146,7 @@ multipers/ml/kernels.py,sha256=XWfvKY68-c9E-MpRzdNqGzGD6K24Aizx95TkiSeAtIY,6175
|
|
|
146
146
|
multipers/ml/mma.py,sha256=mSwYqSHWGiboEiMnxArCuhHyrOExPYdML2Mbx4dTO7s,23950
|
|
147
147
|
multipers/ml/one.py,sha256=np5jM8gywm65TsK1yeZ1BDWqP-Ym-7hz4brTXI_0imk,20119
|
|
148
148
|
multipers/ml/point_clouds.py,sha256=nTkSjSzQy6S10-sZ0uwBp_Fs2EIWleB7yHncK2b_xLg,13770
|
|
149
|
-
multipers/ml/signed_measures.py,sha256=
|
|
149
|
+
multipers/ml/signed_measures.py,sha256=pwV6-XAQnS5itlfGgA64qxPomBqOuqsd-ulnkoYFdto,60981
|
|
150
150
|
multipers/ml/sliced_wasserstein.py,sha256=jgq4ND3EWwwJBopqRvfJLsoOptiMHjS3zEAENBmPJDc,18644
|
|
151
151
|
multipers/ml/tools.py,sha256=DOPcqmvZP2bA7M08GrwccdebwDq1HEwYdhNRGT7eZMI,3453
|
|
152
152
|
multipers/multi_parameter_rank_invariant/diff_helpers.h,sha256=wMCOhAewWd6-lulLND0y8M0MZoru6zn_8J3qfXDjLds,3477
|
|
@@ -178,8 +178,8 @@ multipers/tests/__init__.py,sha256=-7Fj-zFAfBJv18trg0CPglQTmYu_ehySZGqtJzPlN8U,1
|
|
|
178
178
|
multipers/torch/__init__.py,sha256=OLxIiZ389uCqehpUxBPUI_x1SYu531onc4tiTscAuIw,27
|
|
179
179
|
multipers/torch/diff_grids.py,sha256=2YK-c351tBpj8sfzjf26fbE1l0xlWse7oVVfDHD3zwM,7492
|
|
180
180
|
multipers/torch/rips_density.py,sha256=H-kmSzY8hXhmVn15Oltc71DHs1IUHg5oPRgNyWW8L4Q,11706
|
|
181
|
-
multipers-2.3.
|
|
182
|
-
multipers-2.3.
|
|
183
|
-
multipers-2.3.
|
|
184
|
-
multipers-2.3.
|
|
185
|
-
multipers-2.3.
|
|
181
|
+
multipers-2.3.3b7.dist-info/licenses/LICENSE,sha256=UsQRnvlo_9wpQS9DNt52GEraERHwK2GIRwuqr2Yv5JI,1071
|
|
182
|
+
multipers-2.3.3b7.dist-info/METADATA,sha256=rWZTaAJnyv6X11zYE1N_EMCUrmhHZ4bmaK_uG4E7Ma8,9817
|
|
183
|
+
multipers-2.3.3b7.dist-info/WHEEL,sha256=qV0EIPljj1XC_vuSatRWjn02nZIz3N1t8jsZz7HBr2U,101
|
|
184
|
+
multipers-2.3.3b7.dist-info/top_level.txt,sha256=L9e0AGmhRzrNw9FpuUx-zlqi5NcBOmrI9wYY8kYWr8A,10
|
|
185
|
+
multipers-2.3.3b7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|