multipers 2.3.0__cp312-cp312-win_amd64.whl → 2.3.2b1__cp312-cp312-win_amd64.whl

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

Potentially problematic release.


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

Files changed (54) hide show
  1. multipers/_signed_measure_meta.py +71 -65
  2. multipers/array_api/__init__.py +39 -0
  3. multipers/array_api/numpy.py +34 -0
  4. multipers/array_api/torch.py +35 -0
  5. multipers/distances.py +6 -2
  6. multipers/{ml/convolutions.py → filtrations/density.py} +67 -13
  7. multipers/filtrations/filtrations.py +76 -17
  8. multipers/function_rips.cp312-win_amd64.pyd +0 -0
  9. multipers/grids.cp312-win_amd64.pyd +0 -0
  10. multipers/grids.pyx +144 -61
  11. multipers/gudhi/Simplex_tree_multi_interface.h +36 -1
  12. multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
  13. multipers/gudhi/gudhi/One_critical_filtration.h +18 -9
  14. multipers/gudhi/mma_interface_h0.h +1 -1
  15. multipers/gudhi/mma_interface_matrix.h +10 -1
  16. multipers/gudhi/naive_merge_tree.h +1 -1
  17. multipers/gudhi/truc.h +555 -42
  18. multipers/io.cp312-win_amd64.pyd +0 -0
  19. multipers/io.pyx +26 -93
  20. multipers/ml/mma.py +3 -3
  21. multipers/ml/point_clouds.py +2 -2
  22. multipers/ml/signed_measures.py +63 -65
  23. multipers/mma_structures.cp312-win_amd64.pyd +0 -0
  24. multipers/mma_structures.pxd +2 -1
  25. multipers/mma_structures.pyx +56 -16
  26. multipers/mma_structures.pyx.tp +14 -5
  27. multipers/multiparameter_module_approximation/approximation.h +48 -14
  28. multipers/multiparameter_module_approximation.cp312-win_amd64.pyd +0 -0
  29. multipers/multiparameter_module_approximation.pyx +25 -7
  30. multipers/plots.py +2 -1
  31. multipers/point_measure.cp312-win_amd64.pyd +0 -0
  32. multipers/point_measure.pyx +6 -2
  33. multipers/simplex_tree_multi.cp312-win_amd64.pyd +0 -0
  34. multipers/simplex_tree_multi.pxd +1 -0
  35. multipers/simplex_tree_multi.pyx +584 -142
  36. multipers/simplex_tree_multi.pyx.tp +80 -23
  37. multipers/slicer.cp312-win_amd64.pyd +0 -0
  38. multipers/slicer.pxd +799 -197
  39. multipers/slicer.pxd.tp +24 -5
  40. multipers/slicer.pyx +5654 -1427
  41. multipers/slicer.pyx.tp +208 -48
  42. multipers/tbb12.dll +0 -0
  43. multipers/tbbbind_2_5.dll +0 -0
  44. multipers/tbbmalloc.dll +0 -0
  45. multipers/tbbmalloc_proxy.dll +0 -0
  46. multipers/tensor/tensor.h +1 -1
  47. multipers/tests/__init__.py +9 -4
  48. multipers/torch/diff_grids.py +30 -7
  49. multipers/torch/rips_density.py +1 -1
  50. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/METADATA +4 -25
  51. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/RECORD +54 -51
  52. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/WHEEL +1 -1
  53. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info/licenses}/LICENSE +0 -0
  54. {multipers-2.3.0.dist-info → multipers-2.3.2b1.dist-info}/top_level.txt +0 -0
@@ -66,7 +66,8 @@ cimport cython
66
66
  from gudhi.simplex_tree import SimplexTree ## Small hack for typing
67
67
  from typing import Iterable,Literal,Optional
68
68
  from tqdm import tqdm
69
- from multipers.grids import Lstrategies, compute_grid
69
+ from multipers.grids import Lstrategies, compute_grid, sanitize_grid
70
+ from multipers.array_api import api_from_tensor
70
71
  from multipers.point_measure import signed_betti, rank_decomposition_by_rectangles, sparsify
71
72
 
72
73
  from warnings import warn
@@ -98,15 +99,15 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
98
99
  """
99
100
  cdef public intptr_t thisptr
100
101
 
101
- cdef public vector[vector[double]] filtration_grid
102
- cdef public bool _is_function_simplextree
102
+ cdef public object filtration_grid
103
+ cdef public bool _is_function_simplextree # TODO : deprecate
103
104
  # Get the pointer casted as it should be
104
105
  cdef Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}]* get_ptr(self) noexcept nogil:
105
106
  return <Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}]*>(self.thisptr)
106
107
 
107
108
  # cdef Simplex_tree_persistence_interface * pcohptr
108
109
  # Fake constructor that does nothing but documenting the constructor
109
- def __init__(self, other = None, num_parameters:int=2,default_values=[], safe_conversion=False):
110
+ def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
110
111
  """SimplexTreeMulti constructor.
111
112
 
112
113
  :param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
@@ -139,7 +140,7 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
139
140
  def is_kcritical(self)->bool:
140
141
  return {{is_kcritical}}
141
142
  # The real cython constructor
142
- def __cinit__(self, other = None, int num_parameters=2,
143
+ def __cinit__(self, other = None, int num_parameters=-1,
143
144
  default_values=np.asarray([SimplexTreeMulti_{{FSHORT}}.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
144
145
  bool safe_conversion=False,
145
146
  ): #TODO doc
@@ -156,10 +157,13 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
156
157
  if isinstance(other, SimplexTreeMulti_{{FSHORT}}):
157
158
  other_ptr = other.thisptr
158
159
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}](dereference(<Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}]*>other_ptr))) ## prevents calling destructor of other
159
- num_parameters = other.num_parameters
160
+ if num_parameters <=0:
161
+ num_parameters = other.num_parameters
160
162
  self.filtration_grid = other.filtration_grid
161
163
  elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
162
164
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}]())
165
+ if num_parameters <= 0:
166
+ num_parameters = 1
163
167
  if safe_conversion or SAFE_CONVERSION:
164
168
  new_st_multi = _safe_simplextree_multify_{{FSHORT}}(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
165
169
  self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
@@ -174,10 +178,12 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
174
178
  else:
175
179
  raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
176
180
  else:
181
+ if num_parameters <=0:
182
+ num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
177
183
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}]())
178
- self.get_ptr().set_number_of_parameters(num_parameters)
184
+ self.set_num_parameter(num_parameters)
179
185
  self._is_function_simplextree = False
180
- self.filtration_grid=[[]*num_parameters]
186
+ self.filtration_grid=[]
181
187
 
182
188
  def __dealloc__(self):
183
189
  cdef Simplex_tree_multi_interface[{{FSHORT}},{{CTYPE}}]* ptr = self.get_ptr()
@@ -187,6 +193,9 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
187
193
 
188
194
  def __repr__(self):
189
195
  return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
196
+ def __len__(self):
197
+ return self.num_simplices
198
+
190
199
  def __getstate__(self):
191
200
  """:returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
192
201
  :rtype: numpy.array of shape (n,)
@@ -1249,11 +1258,26 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1249
1258
  out = self.get_ptr().get_filtration_values(degrees)
1250
1259
  filtrations_values = [np.asarray(filtration) for filtration in out]
1251
1260
  # Removes infs
1252
- if inf_to_nan:
1261
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
1253
1262
  for i,f in enumerate(filtrations_values):
1254
1263
  filtrations_values[i][f == np.inf] = np.nan
1255
1264
  filtrations_values[i][f == - np.inf] = np.nan
1256
1265
  return filtrations_values
1266
+ def _clean_filtration_grid(self):
1267
+ """
1268
+ Removes the values in filtration_grid that are not linked to any splx.
1269
+ """
1270
+ if not self.is_squeezed:
1271
+ raise ValueError("No grid to clean.")
1272
+ F = self.filtration_grid
1273
+ self.filtration_grid=None
1274
+ cleaned_coordinates = compute_grid(self)
1275
+ new_st = self.grid_squeeze(cleaned_coordinates)
1276
+
1277
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
1278
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
1279
+ return self
1280
+
1257
1281
 
1258
1282
 
1259
1283
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -1289,8 +1313,16 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1289
1313
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
1290
1314
 
1291
1315
 
1292
-
1293
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
1316
+ def grid_squeeze(
1317
+ self,
1318
+ filtration_grid:np.ndarray|list|None=None,
1319
+ bool coordinate_values=True,
1320
+ force=False,
1321
+ strategy:_available_strategies = "exact",
1322
+ grid_strategy=None,
1323
+ inplace=False,
1324
+ **filtration_grid_kwargs
1325
+ )->SimplexTreeMulti_{{FSHORT[:-3] + "i32"}} | SimplexTreeMulti_{{FSHORT}}:
1294
1326
  """
1295
1327
  Fit the filtration of the simplextree to a grid.
1296
1328
 
@@ -1301,26 +1333,50 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1301
1333
  """
1302
1334
  if not force and self.is_squeezed:
1303
1335
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
1336
+
1337
+ if grid_strategy is not None:
1338
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
1339
+ strategy=grid_strategy
1340
+
1341
+ if self.is_squeezed:
1342
+ warn("(copy warning) Squeezing an already squeezed slicer.")
1343
+ temp = self.unsqueeze()
1344
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
1345
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
1346
+
1304
1347
  #TODO : multi-critical
1305
1348
  if filtration_grid is None:
1306
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
1307
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
1308
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
1309
- cdef intptr_t ptr = self.thisptr
1349
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
1350
+ else:
1351
+ filtration_grid = sanitize_grid(filtration_grid)
1352
+ if len(filtration_grid) != self.num_parameters:
1353
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
1354
+ api = api_from_tensor(filtration_grid[0])
1355
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
1310
1356
  if coordinate_values and inplace:
1311
- self.filtration_grid = c_filtration_grid
1357
+ self.filtration_grid = filtration_grid
1312
1358
  if inplace or not coordinate_values:
1313
1359
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
1314
1360
  else:
1315
1361
  out = SimplexTreeMulti_{{FSHORT[:-3] + "i32"}}(num_parameters=self.num_parameters)
1316
1362
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
1317
- out.filtration_grid = c_filtration_grid
1363
+ out.filtration_grid = filtration_grid
1318
1364
  return out
1319
1365
  return self
1320
1366
 
1367
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_{{FSHORT[:-3] + "f64"}}:
1368
+ from multipers.grids import sanitize_grid
1369
+ grid = self.filtration_grid if grid is None else grid
1370
+
1371
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
1372
+ new_slicer = SimplexTreeMulti_{{FSHORT[:-3] + "f64"}}()
1373
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
1374
+
1375
+ return new_slicer
1376
+
1321
1377
  @property
1322
1378
  def is_squeezed(self)->bool:
1323
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
1379
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
1324
1380
 
1325
1381
  @property
1326
1382
  def dtype(self)->type:
@@ -1632,7 +1688,7 @@ def is_simplextree_multi(input)->bool:
1632
1688
 
1633
1689
 
1634
1690
 
1635
- def SimplexTreeMulti(input=None, int num_parameters=2, dtype:type = np.float64, bool kcritical = False,**kwargs) -> SimplexTreeMulti_type:
1691
+ def SimplexTreeMulti(input=None, int num_parameters=-1, dtype:type = np.float64, bool kcritical = False,**kwargs) -> SimplexTreeMulti_type:
1636
1692
  """SimplexTreeMulti constructor.
1637
1693
 
1638
1694
  :param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
@@ -1804,7 +1860,8 @@ def _euler_signed_measure(simplextree, mass_default=None, bool verbose=False):
1804
1860
  `[signed_measure_of_degree for degree in degrees]`
1805
1861
  with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
1806
1862
  """
1807
- assert len(simplextree.filtration_grid[0]) > 0, "Squeeze grid first."
1863
+ if not simplextree.is_squeezed:
1864
+ raise ValueError("Squeeze grid first.")
1808
1865
  cdef bool zero_pad = mass_default is not None
1809
1866
  # assert simplextree.num_parameters == 2
1810
1867
  grid_shape = np.array([len(f) for f in simplextree.filtration_grid])
@@ -1898,9 +1955,9 @@ def _rank_signed_measure(simplextree, vector[indices_type] degrees, mass_default
1898
1955
  else:
1899
1956
  mass_default = np.asarray(mass_default)
1900
1957
  assert mass_default.ndim == 1 and mass_default.shape[0] == simplextree.num_parameters, "Mass default has to be an array like of shape (num_parameters,)"
1901
- if zero_pad:
1902
- for i, _ in enumerate(grid_shape):
1903
- grid_shape[i] += 1 # adds a 0
1958
+ # if zero_pad:
1959
+ # for i, _ in enumerate(grid_shape):
1960
+ # grid_shape[i] += 1 # adds a 0
1904
1961
  # grid_conversion = tuple(np.concatenate([f, [mass_default[i]]]) for i,f in enumerate(grid_conversion))
1905
1962
 
1906
1963
  assert len(grid_shape) == simplextree.num_parameters, "Grid shape size has to be the number of parameters."
Binary file