multipers 2.3.1__cp312-cp312-win_amd64.whl → 2.3.2__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 (49) hide show
  1. multipers/_signed_measure_meta.py +71 -65
  2. multipers/array_api/__init__.py +39 -0
  3. multipers/array_api/numpy.py +34 -0
  4. multipers/array_api/torch.py +35 -0
  5. multipers/distances.py +6 -2
  6. multipers/filtrations/density.py +23 -12
  7. multipers/filtrations/filtrations.py +74 -15
  8. multipers/function_rips.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 +35 -0
  12. multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
  13. multipers/gudhi/gudhi/One_critical_filtration.h +17 -9
  14. multipers/gudhi/mma_interface_matrix.h +5 -3
  15. multipers/gudhi/truc.h +488 -42
  16. multipers/io.cp312-win_amd64.pyd +0 -0
  17. multipers/io.pyx +16 -86
  18. multipers/ml/mma.py +4 -4
  19. multipers/ml/signed_measures.py +60 -62
  20. multipers/mma_structures.cp312-win_amd64.pyd +0 -0
  21. multipers/mma_structures.pxd +2 -1
  22. multipers/mma_structures.pyx +56 -12
  23. multipers/mma_structures.pyx.tp +14 -3
  24. multipers/multiparameter_module_approximation/approximation.h +45 -13
  25. multipers/multiparameter_module_approximation.cp312-win_amd64.pyd +0 -0
  26. multipers/multiparameter_module_approximation.pyx +24 -7
  27. multipers/plots.py +1 -0
  28. multipers/point_measure.cp312-win_amd64.pyd +0 -0
  29. multipers/point_measure.pyx +6 -2
  30. multipers/simplex_tree_multi.cp312-win_amd64.pyd +0 -0
  31. multipers/simplex_tree_multi.pxd +1 -0
  32. multipers/simplex_tree_multi.pyx +535 -113
  33. multipers/simplex_tree_multi.pyx.tp +79 -19
  34. multipers/slicer.cp312-win_amd64.pyd +0 -0
  35. multipers/slicer.pxd +699 -217
  36. multipers/slicer.pxd.tp +22 -6
  37. multipers/slicer.pyx +5315 -1365
  38. multipers/slicer.pyx.tp +202 -46
  39. multipers/tbb12.dll +0 -0
  40. multipers/tbbbind_2_5.dll +0 -0
  41. multipers/tbbmalloc.dll +0 -0
  42. multipers/tbbmalloc_proxy.dll +0 -0
  43. multipers/tests/__init__.py +9 -4
  44. multipers/torch/diff_grids.py +30 -7
  45. {multipers-2.3.1.dist-info → multipers-2.3.2.dist-info}/METADATA +4 -25
  46. {multipers-2.3.1.dist-info → multipers-2.3.2.dist-info}/RECORD +49 -46
  47. {multipers-2.3.1.dist-info → multipers-2.3.2.dist-info}/WHEEL +1 -1
  48. {multipers-2.3.1.dist-info → multipers-2.3.2.dist-info/licenses}/LICENSE +0 -0
  49. {multipers-2.3.1.dist-info → multipers-2.3.2.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,8 +99,8 @@ 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)
@@ -182,7 +183,7 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
182
183
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[{{FSHORT}}, {{CTYPE}}]())
183
184
  self.set_num_parameter(num_parameters)
184
185
  self._is_function_simplextree = False
185
- self.filtration_grid=[[]*num_parameters]
186
+ self.filtration_grid=[]
186
187
 
187
188
  def __dealloc__(self):
188
189
  cdef Simplex_tree_multi_interface[{{FSHORT}},{{CTYPE}}]* ptr = self.get_ptr()
@@ -971,7 +972,16 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
971
972
  out = self.get_ptr().get_edge_list()
972
973
  return out
973
974
 
974
- def collapse_edges(self, int num=1, int max_dimension = 0, bool progress=False, bool strong=True, bool full=False, bool ignore_warning=False)->SimplexTreeMulti_{{FSHORT}}:
975
+ def collapse_edges(
976
+ self,
977
+ int num=1,
978
+ int max_dimension = 0,
979
+ bool progress=False,
980
+ bool strong=True,
981
+ bool full=False,
982
+ bool ignore_warning=False,
983
+ bool auto_clean=True,
984
+ )->SimplexTreeMulti_{{FSHORT}}:
975
985
  """Edge collapse for 1-critical 2-parameter clique complex (see https://arxiv.org/abs/2211.05574).
976
986
  It uses the code from the github repository https://github.com/aj-alonso/filtration_domination .
977
987
 
@@ -1021,6 +1031,8 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1021
1031
  edges = _collapse_edge_list(edges, num=num, full=full, strong=strong, progress=progress)
1022
1032
  # Retrieves the collapsed simplicial complex
1023
1033
  self._reconstruct_from_edge_list(edges, swap=True, expand_dimension=max_dimension)
1034
+ if self.is_squeezed and auto_clean:
1035
+ self._clean_filtration_grid()
1024
1036
  return self
1025
1037
 
1026
1038
  @cython.inline
@@ -1257,11 +1269,26 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1257
1269
  out = self.get_ptr().get_filtration_values(degrees)
1258
1270
  filtrations_values = [np.asarray(filtration) for filtration in out]
1259
1271
  # Removes infs
1260
- if inf_to_nan:
1272
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
1261
1273
  for i,f in enumerate(filtrations_values):
1262
1274
  filtrations_values[i][f == np.inf] = np.nan
1263
1275
  filtrations_values[i][f == - np.inf] = np.nan
1264
1276
  return filtrations_values
1277
+ def _clean_filtration_grid(self):
1278
+ """
1279
+ Removes the values in filtration_grid that are not linked to any splx.
1280
+ """
1281
+ if not self.is_squeezed:
1282
+ raise ValueError("No grid to clean.")
1283
+ F = self.filtration_grid
1284
+ self.filtration_grid=None
1285
+ cleaned_coordinates = compute_grid(self)
1286
+ new_st = self.grid_squeeze(cleaned_coordinates)
1287
+
1288
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
1289
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
1290
+ return self
1291
+
1265
1292
 
1266
1293
 
1267
1294
  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]:
@@ -1297,8 +1324,16 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1297
1324
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
1298
1325
 
1299
1326
 
1300
-
1301
- 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):
1327
+ def grid_squeeze(
1328
+ self,
1329
+ filtration_grid:np.ndarray|list|None=None,
1330
+ bool coordinate_values=True,
1331
+ bool force=False,
1332
+ str strategy:_available_strategies = "exact",
1333
+ grid_strategy=None,
1334
+ bool inplace=False,
1335
+ **filtration_grid_kwargs
1336
+ )->SimplexTreeMulti_{{FSHORT[:-3] + "i32"}} | SimplexTreeMulti_{{FSHORT}}:
1302
1337
  """
1303
1338
  Fit the filtration of the simplextree to a grid.
1304
1339
 
@@ -1309,26 +1344,50 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1309
1344
  """
1310
1345
  if not force and self.is_squeezed:
1311
1346
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
1347
+
1348
+ if grid_strategy is not None:
1349
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
1350
+ strategy=grid_strategy
1351
+
1352
+ if self.is_squeezed:
1353
+ warn("(copy warning) Squeezing an already squeezed slicer.")
1354
+ temp = self.unsqueeze()
1355
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
1356
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
1357
+
1312
1358
  #TODO : multi-critical
1313
1359
  if filtration_grid is None:
1314
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
1315
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
1316
- 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()}"
1317
- cdef intptr_t ptr = self.thisptr
1360
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
1361
+ else:
1362
+ filtration_grid = sanitize_grid(filtration_grid)
1363
+ if len(filtration_grid) != self.num_parameters:
1364
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
1365
+ api = api_from_tensor(filtration_grid[0])
1366
+ 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
1318
1367
  if coordinate_values and inplace:
1319
- self.filtration_grid = c_filtration_grid
1368
+ self.filtration_grid = filtration_grid
1320
1369
  if inplace or not coordinate_values:
1321
1370
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
1322
1371
  else:
1323
1372
  out = SimplexTreeMulti_{{FSHORT[:-3] + "i32"}}(num_parameters=self.num_parameters)
1324
1373
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
1325
- out.filtration_grid = c_filtration_grid
1374
+ out.filtration_grid = filtration_grid
1326
1375
  return out
1327
1376
  return self
1328
1377
 
1378
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_{{FSHORT[:-3] + "f64"}}:
1379
+ from multipers.grids import sanitize_grid
1380
+ grid = self.filtration_grid if grid is None else grid
1381
+
1382
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
1383
+ new_slicer = SimplexTreeMulti_{{FSHORT[:-3] + "f64"}}()
1384
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
1385
+
1386
+ return new_slicer
1387
+
1329
1388
  @property
1330
1389
  def is_squeezed(self)->bool:
1331
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
1390
+ 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
1332
1391
 
1333
1392
  @property
1334
1393
  def dtype(self)->type:
@@ -1812,7 +1871,8 @@ def _euler_signed_measure(simplextree, mass_default=None, bool verbose=False):
1812
1871
  `[signed_measure_of_degree for degree in degrees]`
1813
1872
  with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
1814
1873
  """
1815
- assert len(simplextree.filtration_grid[0]) > 0, "Squeeze grid first."
1874
+ if not simplextree.is_squeezed:
1875
+ raise ValueError("Squeeze grid first.")
1816
1876
  cdef bool zero_pad = mass_default is not None
1817
1877
  # assert simplextree.num_parameters == 2
1818
1878
  grid_shape = np.array([len(f) for f in simplextree.filtration_grid])
@@ -1906,9 +1966,9 @@ def _rank_signed_measure(simplextree, vector[indices_type] degrees, mass_default
1906
1966
  else:
1907
1967
  mass_default = np.asarray(mass_default)
1908
1968
  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,)"
1909
- if zero_pad:
1910
- for i, _ in enumerate(grid_shape):
1911
- grid_shape[i] += 1 # adds a 0
1969
+ # if zero_pad:
1970
+ # for i, _ in enumerate(grid_shape):
1971
+ # grid_shape[i] += 1 # adds a 0
1912
1972
  # grid_conversion = tuple(np.concatenate([f, [mass_default[i]]]) for i,f in enumerate(grid_conversion))
1913
1973
 
1914
1974
  assert len(grid_shape) == simplextree.num_parameters, "Grid shape size has to be the number of parameters."
Binary file