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.

@@ -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
@@ -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()
@@ -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, interpolation="closest_observation")
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
 
@@ -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
- return x
32
-
33
- c = _np.concatenate([[0], _np.cumsum(c[:-1])])
34
- return x[c]
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
 
@@ -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 : np.asarray(x, dtype=pts.dtype)
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
- _asarray_weights = lambda x : torch.from_numpy(x).type(pts.dtype)
184
- _asarray_grid = lambda x : x.type(pts.dtype)
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
- pts, sample_weights=_asarray_weights(pts_weights)
188
- ).score_samples(_asarray_grid(grid_iterator))
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 = np.asarray([np.quantile(filtration, [a, b], axis=1, method='closest_observation') for filtration in filtrations_values])
178
- min_filtration, max_filtration = np.min(boxes, axis=(0,1)), np.max(boxes, axis=(0,1)) # box, birth/death, 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(to_unique(f) for f in filtrations_values)
188
+ F=tuple(api.unique(f) for f in filtrations_values)
188
189
  elif strategy == "quantile":
189
- F = tuple(to_unique(f) for f in filtrations_values)
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( np.quantile(f, q=np.linspace(0,1,num=int(r*_q_factor)), axis=0, method='closest_observation') for f,r in zip(F, resolution) )
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(to_unique(f) for f in F)
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(np.linspace(np.min(f),np.max(f),num=r, dtype=np.asarray(f).dtype) for f,r in zip(filtrations_values, resolution))
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
- F = tuple(_torch_regular_closest(f,r, unique) for f,r in zip(filtrations_values, resolution))
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
- if not isinstance(grid[0], np.ndarray):
218
- import torch
219
- assert isinstance(grid[0], torch.Tensor)
220
- from multipers.torch.diff_grids import todense
221
- return todense(grid)
222
- dtype = grid[0].dtype
223
- if product_order:
224
- return np.fromiter(product(*grid), dtype=np.dtype((dtype, len(grid))), count=np.prod([len(f) for f in grid]))
225
- mesh = np.meshgrid(*grid)
226
- coordinates = np.concatenate(tuple(stuff.ravel()[:,None] for stuff in mesh), axis=1, dtype=dtype)
227
- return coordinates
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 _todo_regular_closest(some_float[:] f, int r, bool unique):
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(some_float[:] f, int r, bool unique):
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)
Binary file
@@ -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(self._normalization_factors)
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(compute_grid(
788
- f_ax.T, resolution=self.resolution, strategy=self.grid_strategy
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
@@ -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.T, ax=ax)
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.T, ax=ax)
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")
@@ -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.T, ax=ax)
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")
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", 20)
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
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[double] _multiparameter_module_approximation_f64(object slicer, One_critical_filtration[double] direction, double max_error, Box[double] box, bool threshold, bool complete, bool verbose):
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[double] mod
2019
+ cdef Module[float] mod
2020
2020
  if False:
2021
2021
  pass
2022
- elif isinstance(slicer, mps._KSlicer_Matrix0_vine_f64):
2022
+ elif isinstance(slicer, mps._KSlicer_Matrix0_vine_f32):
2023
2023
  with nogil:
2024
- mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix0_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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._KSlicer_Matrix1_vine_f64):
2026
+ elif isinstance(slicer, mps._KSlicer_Matrix1_vine_f32):
2027
2027
  with nogil:
2028
- mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix1_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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._Slicer_Matrix0_vine_f64):
2030
+ elif isinstance(slicer, mps._Slicer_Matrix0_vine_f32):
2031
2031
  with nogil:
2032
- mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix0_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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._Slicer_Matrix1_vine_f64):
2034
+ elif isinstance(slicer, mps._Slicer_Matrix1_vine_f32):
2035
2035
  with nogil:
2036
- mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix1_vine_f64*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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[float] _multiparameter_module_approximation_f32(object slicer, One_critical_filtration[float] direction, float max_error, Box[float] box, bool threshold, bool complete, bool verbose):
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[float] mod
2043
+ cdef Module[double] mod
2044
2044
  if False:
2045
2045
  pass
2046
- elif isinstance(slicer, mps._KSlicer_Matrix0_vine_f32):
2046
+ elif isinstance(slicer, mps._KSlicer_Matrix0_vine_f64):
2047
2047
  with nogil:
2048
- mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix0_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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._KSlicer_Matrix1_vine_f32):
2050
+ elif isinstance(slicer, mps._KSlicer_Matrix1_vine_f64):
2051
2051
  with nogil:
2052
- mod = multiparameter_module_approximation(dereference(<C_KSlicer_Matrix1_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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._Slicer_Matrix0_vine_f32):
2054
+ elif isinstance(slicer, mps._Slicer_Matrix0_vine_f64):
2055
2055
  with nogil:
2056
- mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix0_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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._Slicer_Matrix1_vine_f32):
2058
+ elif isinstance(slicer, mps._Slicer_Matrix1_vine_f64):
2059
2059
  with nogil:
2060
- mod = multiparameter_module_approximation(dereference(<C_Slicer_Matrix1_vine_f32*>(slicer_ptr)), direction, max_error, box, threshold, complete, verbose)
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 _from_bitmapf32(image, **slicer_kwargs):
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.float32) - _Slicer._inf_value()
19898
- cdef float[:,:] F = filtration_values
19899
- cdef float[:,:] c_img = image.reshape(-1,num_parameters)
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 _from_bitmapi32(image, **slicer_kwargs):
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.int32) - _Slicer._inf_value()
19936
- cdef int32_t[:,:] F = filtration_values
19937
- cdef int32_t[:,:] c_img = image.reshape(-1,num_parameters)
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 _from_bitmapi64(image, **slicer_kwargs):
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.int64) - _Slicer._inf_value()
19974
- cdef int64_t[:,:] F = filtration_values
19975
- cdef int64_t[:,:] c_img = image.reshape(-1,num_parameters)
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.3b6
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=fscSXOekUX_TkxucOKpb3oNJoct_IrjMZcqiwxPvmSY,17930
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=sDLCoM2S7sqYiqjNYrtmTNQgmuUewXe9aZsf5VTPeTg,332288
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=jjmqPWuMVftLpRvEVAl7SyAf0RdFnqeqNTBNhtqDqZM,479744
11
- multipers/grids.pyx,sha256=ONN_RKkuxqwb9IaS9gd42FUGaiBLc8QWcd3L-ubZjKE,16575
12
- multipers/io.cp313-win_amd64.pyd,sha256=WM0rASfVqREFD-rp8W0HbJNjFkiaOIl8y60lghRGILg,220160
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=ZxyeciW9bPLkvHfa57IFSxtcQhP-C46CZ9EGN6M9G-Q,1280000
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=4zNC6ePfFKMvx0MrH-FqJVouyTMciRc49oevKDnsJhI,109530
17
- multipers/mma_structures.pyx.tp,sha256=hWuuk9USDFa8CbQVRHNjWSAdGgpkWKvSCNjTtVuSzwM,42299
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=f0dY4vjrIwUzMS92s_Fkj4-W65xJ2PxxS7zIrSIMQUQ,450560
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=-yOaufE_flzrZDQIgwhchvJIo0MBJMrUJKuo_sKG6IM,14918
23
- multipers/point_measure.cp313-win_amd64.pyd,sha256=UlCNj-x36P8dGJJY4r7McYw8RUjkCS0UMxc-0VRktL8,596480
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=m2GZ5vqmjcPPqfaQxuUsPuxrKf6ynN8b4M6EMn_GVCA,3612672
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=wlZ4jNniPFPuVRjzfTXxVXYJNAkbrqwB66LRgcyt1mI,11666432
30
- multipers/slicer.pxd,sha256=fzA-lL_QWyUMf9rRSpcKG35QNS9UXEmVC9r7kR0geho,185230
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=fQ0zZwgwWZPXWw_rKvMCwbHXw2RQTJFSLkxiEHrrzVE,894493
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=072Lk60jfw3re6iCY3EorteHEoQ7vbUgpMpuGCpfh04,1109
41
- multipers/array_api/numpy.py,sha256=1l9zL3jP8xhKYpXRjGzhHw4DP_tGdMQ3WRS8Tcmf3xc,816
42
- multipers/array_api/torch.py,sha256=o8ekBau6G46LHd8-b_cWu_vPWIBiTfE4Dz5UL0xWwxY,1257
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=z_QwAi_5EdUoKXc6s7N-qWM7n6r5hWHiHeJkLPWhE-w,19818
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=LCf0gAfQ7y4q9gI7s72Y4nwgP6U8HZenwGrMDdK-azY,58339
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.3b6.dist-info/licenses/LICENSE,sha256=UsQRnvlo_9wpQS9DNt52GEraERHwK2GIRwuqr2Yv5JI,1071
182
- multipers-2.3.3b6.dist-info/METADATA,sha256=0zSGxpVm4912cviaU4-M6i-GMYDdQtoDLr25L35Xq6I,9817
183
- multipers-2.3.3b6.dist-info/WHEEL,sha256=qV0EIPljj1XC_vuSatRWjn02nZIz3N1t8jsZz7HBr2U,101
184
- multipers-2.3.3b6.dist-info/top_level.txt,sha256=L9e0AGmhRzrNw9FpuUx-zlqi5NcBOmrI9wYY8kYWr8A,10
185
- multipers-2.3.3b6.dist-info/RECORD,,
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,,