multipers 2.3.0__cp310-cp310-win_amd64.whl → 2.3.2__cp310-cp310-win_amd64.whl

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

Potentially problematic release.


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

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.cp310-win_amd64.pyd +0 -0
  9. multipers/grids.cp310-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.cp310-win_amd64.pyd +0 -0
  19. multipers/io.pyx +26 -93
  20. multipers/ml/mma.py +4 -4
  21. multipers/ml/point_clouds.py +2 -2
  22. multipers/ml/signed_measures.py +63 -65
  23. multipers/mma_structures.cp310-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.cp310-win_amd64.pyd +0 -0
  29. multipers/multiparameter_module_approximation.pyx +27 -8
  30. multipers/plots.py +2 -1
  31. multipers/point_measure.cp310-win_amd64.pyd +0 -0
  32. multipers/point_measure.pyx +6 -2
  33. multipers/simplex_tree_multi.cp310-win_amd64.pyd +0 -0
  34. multipers/simplex_tree_multi.pxd +1 -0
  35. multipers/simplex_tree_multi.pyx +632 -146
  36. multipers/simplex_tree_multi.pyx.tp +92 -24
  37. multipers/slicer.cp310-win_amd64.pyd +0 -0
  38. multipers/slicer.pxd +779 -177
  39. multipers/slicer.pxd.tp +24 -5
  40. multipers/slicer.pyx +5657 -1427
  41. multipers/slicer.pyx.tp +211 -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.2.dist-info}/METADATA +4 -25
  51. {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info}/RECORD +54 -51
  52. {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info}/WHEEL +1 -1
  53. {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info/licenses}/LICENSE +0 -0
  54. {multipers-2.3.0.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,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,)
@@ -963,7 +972,16 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
963
972
  out = self.get_ptr().get_edge_list()
964
973
  return out
965
974
 
966
- 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}}:
967
985
  """Edge collapse for 1-critical 2-parameter clique complex (see https://arxiv.org/abs/2211.05574).
968
986
  It uses the code from the github repository https://github.com/aj-alonso/filtration_domination .
969
987
 
@@ -1013,6 +1031,8 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1013
1031
  edges = _collapse_edge_list(edges, num=num, full=full, strong=strong, progress=progress)
1014
1032
  # Retrieves the collapsed simplicial complex
1015
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()
1016
1036
  return self
1017
1037
 
1018
1038
  @cython.inline
@@ -1249,11 +1269,26 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1249
1269
  out = self.get_ptr().get_filtration_values(degrees)
1250
1270
  filtrations_values = [np.asarray(filtration) for filtration in out]
1251
1271
  # Removes infs
1252
- if inf_to_nan:
1272
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
1253
1273
  for i,f in enumerate(filtrations_values):
1254
1274
  filtrations_values[i][f == np.inf] = np.nan
1255
1275
  filtrations_values[i][f == - np.inf] = np.nan
1256
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
+
1257
1292
 
1258
1293
 
1259
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]:
@@ -1289,8 +1324,16 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1289
1324
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
1290
1325
 
1291
1326
 
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):
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}}:
1294
1337
  """
1295
1338
  Fit the filtration of the simplextree to a grid.
1296
1339
 
@@ -1301,26 +1344,50 @@ cdef class SimplexTreeMulti_{{FSHORT}}:
1301
1344
  """
1302
1345
  if not force and self.is_squeezed:
1303
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
+
1304
1358
  #TODO : multi-critical
1305
1359
  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
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
1310
1367
  if coordinate_values and inplace:
1311
- self.filtration_grid = c_filtration_grid
1368
+ self.filtration_grid = filtration_grid
1312
1369
  if inplace or not coordinate_values:
1313
1370
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
1314
1371
  else:
1315
1372
  out = SimplexTreeMulti_{{FSHORT[:-3] + "i32"}}(num_parameters=self.num_parameters)
1316
1373
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
1317
- out.filtration_grid = c_filtration_grid
1374
+ out.filtration_grid = filtration_grid
1318
1375
  return out
1319
1376
  return self
1320
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
+
1321
1388
  @property
1322
1389
  def is_squeezed(self)->bool:
1323
- 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
1324
1391
 
1325
1392
  @property
1326
1393
  def dtype(self)->type:
@@ -1632,7 +1699,7 @@ def is_simplextree_multi(input)->bool:
1632
1699
 
1633
1700
 
1634
1701
 
1635
- def SimplexTreeMulti(input=None, int num_parameters=2, dtype:type = np.float64, bool kcritical = False,**kwargs) -> SimplexTreeMulti_type:
1702
+ def SimplexTreeMulti(input=None, int num_parameters=-1, dtype:type = np.float64, bool kcritical = False,**kwargs) -> SimplexTreeMulti_type:
1636
1703
  """SimplexTreeMulti constructor.
1637
1704
 
1638
1705
  :param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
@@ -1804,7 +1871,8 @@ def _euler_signed_measure(simplextree, mass_default=None, bool verbose=False):
1804
1871
  `[signed_measure_of_degree for degree in degrees]`
1805
1872
  with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
1806
1873
  """
1807
- assert len(simplextree.filtration_grid[0]) > 0, "Squeeze grid first."
1874
+ if not simplextree.is_squeezed:
1875
+ raise ValueError("Squeeze grid first.")
1808
1876
  cdef bool zero_pad = mass_default is not None
1809
1877
  # assert simplextree.num_parameters == 2
1810
1878
  grid_shape = np.array([len(f) for f in simplextree.filtration_grid])
@@ -1898,9 +1966,9 @@ def _rank_signed_measure(simplextree, vector[indices_type] degrees, mass_default
1898
1966
  else:
1899
1967
  mass_default = np.asarray(mass_default)
1900
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,)"
1901
- if zero_pad:
1902
- for i, _ in enumerate(grid_shape):
1903
- 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
1904
1972
  # grid_conversion = tuple(np.concatenate([f, [mass_default[i]]]) for i,f in enumerate(grid_conversion))
1905
1973
 
1906
1974
  assert len(grid_shape) == simplextree.num_parameters, "Grid shape size has to be the number of parameters."
Binary file