multipers 1.1.3__cp311-cp311-macosx_11_0_universal2.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/.dylibs/libtbb.12.12.dylib +0 -0
- multipers/.dylibs/libtbbmalloc.2.12.dylib +0 -0
- multipers/__init__.py +5 -0
- multipers/_old_rank_invariant.pyx +328 -0
- multipers/_signed_measure_meta.py +193 -0
- multipers/data/MOL2.py +350 -0
- multipers/data/UCR.py +18 -0
- multipers/data/__init__.py +1 -0
- multipers/data/graphs.py +466 -0
- multipers/data/immuno_regions.py +27 -0
- multipers/data/minimal_presentation_to_st_bf.py +0 -0
- multipers/data/pytorch2simplextree.py +91 -0
- multipers/data/shape3d.py +101 -0
- multipers/data/synthetic.py +68 -0
- multipers/distances.py +172 -0
- multipers/euler_characteristic.cpython-311-darwin.so +0 -0
- multipers/euler_characteristic.pyx +137 -0
- multipers/function_rips.cpython-311-darwin.so +0 -0
- multipers/function_rips.pyx +102 -0
- multipers/hilbert_function.cpython-311-darwin.so +0 -0
- multipers/hilbert_function.pyi +46 -0
- multipers/hilbert_function.pyx +151 -0
- multipers/io.cpython-311-darwin.so +0 -0
- multipers/io.pyx +176 -0
- multipers/ml/__init__.py +0 -0
- multipers/ml/accuracies.py +61 -0
- multipers/ml/convolutions.py +510 -0
- multipers/ml/invariants_with_persistable.py +79 -0
- multipers/ml/kernels.py +128 -0
- multipers/ml/mma.py +657 -0
- multipers/ml/one.py +472 -0
- multipers/ml/point_clouds.py +191 -0
- multipers/ml/signed_betti.py +50 -0
- multipers/ml/signed_measures.py +1479 -0
- multipers/ml/sliced_wasserstein.py +313 -0
- multipers/ml/tools.py +116 -0
- multipers/mma_structures.cpython-311-darwin.so +0 -0
- multipers/mma_structures.pxd +155 -0
- multipers/mma_structures.pyx +651 -0
- multipers/multiparameter_edge_collapse.py +29 -0
- multipers/multiparameter_module_approximation.cpython-311-darwin.so +0 -0
- multipers/multiparameter_module_approximation.pyi +439 -0
- multipers/multiparameter_module_approximation.pyx +311 -0
- multipers/pickle.py +53 -0
- multipers/plots.py +292 -0
- multipers/point_measure_integration.cpython-311-darwin.so +0 -0
- multipers/point_measure_integration.pyx +59 -0
- multipers/rank_invariant.cpython-311-darwin.so +0 -0
- multipers/rank_invariant.pyx +154 -0
- multipers/simplex_tree_multi.cpython-311-darwin.so +0 -0
- multipers/simplex_tree_multi.pxd +121 -0
- multipers/simplex_tree_multi.pyi +715 -0
- multipers/simplex_tree_multi.pyx +1417 -0
- multipers/slicer.cpython-311-darwin.so +0 -0
- multipers/slicer.pxd +94 -0
- multipers/slicer.pyx +276 -0
- multipers/tensor.pxd +13 -0
- multipers/test.pyx +44 -0
- multipers-1.1.3.dist-info/LICENSE +21 -0
- multipers-1.1.3.dist-info/METADATA +22 -0
- multipers-1.1.3.dist-info/RECORD +63 -0
- multipers-1.1.3.dist-info/WHEEL +5 -0
- multipers-1.1.3.dist-info/top_level.txt +1 -0
|
Binary file
|
|
Binary file
|
multipers/__init__.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
from libcpp.vector cimport vector
|
|
2
|
+
from libc.stdint cimport intptr_t
|
|
3
|
+
from libcpp cimport bool
|
|
4
|
+
from libcpp cimport int
|
|
5
|
+
from libcpp.utility cimport pair
|
|
6
|
+
from typing import Optional,Iterable,Callable
|
|
7
|
+
|
|
8
|
+
ctypedef vector[vector[vector[vector[int]]]] rank2
|
|
9
|
+
ctypedef vector[vector[int]] grid2D
|
|
10
|
+
ctypedef vector[grid2D] grid3D
|
|
11
|
+
ctypedef vector[grid3D] grid4D
|
|
12
|
+
ctypedef vector[grid4D] grid5D
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
ctypedef pair[vector[vector[int]],vector[int]] signed_measure_type
|
|
16
|
+
ctypedef pair[vector[vector[double]],vector[int]] signed_measure_double_type
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
cdef extern from "multi_parameter_rank_invariant/_old_rank_invariant.h" namespace "Gudhi::multiparameter::rank_invariant":
|
|
26
|
+
rank2 get_2drank_invariant(const intptr_t, const vector[int]&, const int) nogil
|
|
27
|
+
grid3D get_2Dhilbert(const intptr_t, const vector[int]&, const vector[int], bool) except + nogil
|
|
28
|
+
vector[signed_measure_type] get_signed_measure(const intptr_t, const vector[int]&, int, vector[int], bool) except + nogil
|
|
29
|
+
grid4D get_3Dhilbert(const intptr_t, const vector[int]&, const vector[int]) except + nogil
|
|
30
|
+
grid5D get_4Dhilbert(const intptr_t, const vector[int]&, const vector[int]) except + nogil
|
|
31
|
+
grid2D get_euler2d(const intptr_t, const vector[int]&, bool, bool) except + nogil
|
|
32
|
+
grid3D get_euler3d(const intptr_t, const vector[int]&, bool, bool) except + nogil
|
|
33
|
+
grid4D get_euler4d(const intptr_t, const vector[int]&, bool, bool) except + nogil
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
cdef extern from "multi_parameter_rank_invariant/function_rips.h" namespace "Gudhi::multiparameter::rank_invariant::degree_rips":
|
|
37
|
+
signed_measure_double_type degree_rips_hilbert_signed_measure(intptr_t, int,int) except + nogil
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
from multipers.simplex_tree_multi import SimplexTreeMulti # Typing hack
|
|
41
|
+
|
|
42
|
+
cimport numpy as cnp
|
|
43
|
+
cnp.import_array()
|
|
44
|
+
import numpy as np
|
|
45
|
+
|
|
46
|
+
# # TODO : make a st python flag for coordinate_st, with grid resolution.
|
|
47
|
+
def rank_invariant2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, int degree):
|
|
48
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
49
|
+
cdef int c_degree = degree
|
|
50
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
51
|
+
cdef vector[vector[vector[vector[int]]]] out
|
|
52
|
+
with nogil:
|
|
53
|
+
out = get_2drank_invariant(ptr, c_grid_shape, c_degree)
|
|
54
|
+
return np.asarray(out, dtype=int)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _hilbert2d(
|
|
59
|
+
simplextree:SimplexTreeMulti,
|
|
60
|
+
grid_shape:np.ndarray|list,
|
|
61
|
+
vector[int] degrees,
|
|
62
|
+
bool mobius_inversion
|
|
63
|
+
):
|
|
64
|
+
# assert simplextree.num_parameters == 2
|
|
65
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
66
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
67
|
+
cdef grid3D out
|
|
68
|
+
with nogil:
|
|
69
|
+
out = get_2Dhilbert(ptr, c_grid_shape, degrees, mobius_inversion)
|
|
70
|
+
return np.array(out, dtype=int)
|
|
71
|
+
|
|
72
|
+
# cdef _sm_2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, int degree):
|
|
73
|
+
# # assert simplextree.num_parameters == 2
|
|
74
|
+
# cdef intptr_t ptr = simplextree.thisptr
|
|
75
|
+
# cdef int c_degree = degree
|
|
76
|
+
# cdef vector[int] c_grid_shape = grid_shape
|
|
77
|
+
# cdef signed_measure_type out
|
|
78
|
+
# with nogil:
|
|
79
|
+
# out = get_2D_SM(ptr, c_grid_shape, c_degree)
|
|
80
|
+
# return np.array(out.first, dtype=int), np.array(out.second, dtype=int)
|
|
81
|
+
|
|
82
|
+
cdef _hilbert3d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[int] degrees):
|
|
83
|
+
# assert simplextree.num_parameters == 3
|
|
84
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
85
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
86
|
+
cdef grid4D out
|
|
87
|
+
with nogil:
|
|
88
|
+
out = get_3Dhilbert(ptr, c_grid_shape, degrees)
|
|
89
|
+
return np.array(out, dtype=int)
|
|
90
|
+
|
|
91
|
+
cdef _hilbert4d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[int] degrees):
|
|
92
|
+
# assert simplextree.num_parameters == 3
|
|
93
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
94
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
95
|
+
cdef grid5D out
|
|
96
|
+
with nogil:
|
|
97
|
+
out = get_4Dhilbert(ptr, c_grid_shape, degrees)
|
|
98
|
+
return np.array(out, dtype=int)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def hilbert(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[int] degrees):
|
|
102
|
+
assert len(grid_shape) >= simplextree.num_parameters, "Grid shape not valid"
|
|
103
|
+
if simplextree.num_parameters == 2:
|
|
104
|
+
out = _hilbert2d(simplextree, grid_shape, degrees, False)
|
|
105
|
+
return out
|
|
106
|
+
if simplextree.num_parameters == 3:
|
|
107
|
+
return _hilbert3d(simplextree, grid_shape, degrees)
|
|
108
|
+
if simplextree.num_parameters == 4:
|
|
109
|
+
return _hilbert4d(simplextree, grid_shape, degrees)
|
|
110
|
+
raise Exception(f"Number of parameter has to be 2,3, or 4.")
|
|
111
|
+
|
|
112
|
+
def euler2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, bool inverse=True, bool zero_pad=False):
|
|
113
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
114
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
115
|
+
cdef vector[vector[int]] out
|
|
116
|
+
with nogil:
|
|
117
|
+
out = get_euler2d(ptr, c_grid_shape, inverse, zero_pad)
|
|
118
|
+
return np.array(out, dtype=int)
|
|
119
|
+
def euler3d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, bool inverse=True, bool zero_pad=False):
|
|
120
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
121
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
122
|
+
cdef grid3D out
|
|
123
|
+
with nogil:
|
|
124
|
+
out = get_euler3d(ptr, c_grid_shape, inverse, zero_pad)
|
|
125
|
+
return np.array(out, dtype=int)
|
|
126
|
+
def euler4d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, bool inverse=True, bool zero_pad=False):
|
|
127
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
128
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
129
|
+
cdef grid4D out
|
|
130
|
+
with nogil:
|
|
131
|
+
out = get_euler4d(ptr, c_grid_shape,inverse, zero_pad)
|
|
132
|
+
return np.array(out, dtype=int)
|
|
133
|
+
|
|
134
|
+
def euler(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, degree:int=None, bool inverse=False, bool zero_pad=False):
|
|
135
|
+
if simplextree.num_parameters == 2:
|
|
136
|
+
return euler2d(simplextree, grid_shape, inverse, zero_pad)
|
|
137
|
+
if simplextree.num_parameters == 3:
|
|
138
|
+
return euler3d(simplextree, grid_shape, inverse, zero_pad)
|
|
139
|
+
if simplextree.num_parameters == 4:
|
|
140
|
+
return euler4d(simplextree, grid_shape, inverse, zero_pad)
|
|
141
|
+
raise Exception(f"Number of parameter has to be 2,3, or 4.")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def signed_measure(
|
|
145
|
+
simplextree:SimplexTreeMulti,
|
|
146
|
+
grid_shape:Optional[np.ndarray]|list|int|None=None,
|
|
147
|
+
degree:Optional[int]|None=None,
|
|
148
|
+
degrees:Optional[Iterable[int]]=None,
|
|
149
|
+
grid_conversion=None,
|
|
150
|
+
infer_grid=True,
|
|
151
|
+
bool unsparse=False,
|
|
152
|
+
invariant:str | None="rank_invariant",
|
|
153
|
+
plot:bool=False,
|
|
154
|
+
grid_strategy:str='exact',
|
|
155
|
+
mass_default:str|None|Iterable[float]='auto',
|
|
156
|
+
**get_filtration_grid_kwargs,
|
|
157
|
+
):
|
|
158
|
+
"""
|
|
159
|
+
Computes a discrete signed measure from various invariants.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
- simplextree : SimplexTreeMulti
|
|
164
|
+
The multifiltered complex on which to compute the invariant.
|
|
165
|
+
The filtrations values are assumed to be the coordinates in that filtration, i.e. integers
|
|
166
|
+
- grid_conversion : (num_parameter) x (filtration values)
|
|
167
|
+
Reconverts the coordinate signed measure in that grid.
|
|
168
|
+
Default behavior is searching the grid in the simplextree.
|
|
169
|
+
- grid_shape : Iterable[int] or Int or None:
|
|
170
|
+
The coordinate grid shape.
|
|
171
|
+
If int, every parameter gets this resolution.
|
|
172
|
+
If None and grid_conversion is also None, the grid is infered from the filtration values.
|
|
173
|
+
- degree : int|None
|
|
174
|
+
If the invariant is hilbert or the rank invariant, the homological degree.
|
|
175
|
+
If the invariant is euler, this parameter is ignored.
|
|
176
|
+
- degrees :
|
|
177
|
+
Compute multiple signed measures of different degrees at once.
|
|
178
|
+
- mass_default='auto' : filtration value or 'auto' or None
|
|
179
|
+
where to put the mass default to ensure having a zero mass measure. If None, the mass default is not corrected.
|
|
180
|
+
- unsparse = False : bool
|
|
181
|
+
Unsparse the output.
|
|
182
|
+
- invariant = None : str
|
|
183
|
+
The invariant to use to compute the signed measure.
|
|
184
|
+
Possible options : 'euler' or 'hilbert' or 'rank_invariant'
|
|
185
|
+
- plot : bool
|
|
186
|
+
Will plot the signed measure if possible, i.e. if num_parameters is 2
|
|
187
|
+
|
|
188
|
+
Output
|
|
189
|
+
------
|
|
190
|
+
|
|
191
|
+
Default
|
|
192
|
+
- dirac locations : np.ndarray of shape (num_diracs x simplextree.num_parameters)
|
|
193
|
+
- dirac weights : np.ndarray of shape (num_diracs)
|
|
194
|
+
|
|
195
|
+
if unsparse is true, returns the unsparsified tensor.
|
|
196
|
+
"""
|
|
197
|
+
cdef bool zero_pad = not (mass_default is None)
|
|
198
|
+
if degree is None and degrees is None:
|
|
199
|
+
degrees=[None]
|
|
200
|
+
elif degree is not None and degrees is None:
|
|
201
|
+
degrees = [degree]
|
|
202
|
+
elif degree is not None and degrees is not None:
|
|
203
|
+
degrees = np.concatenate([[degree], degrees])
|
|
204
|
+
|
|
205
|
+
_unique_degrees = np.unique(degrees)
|
|
206
|
+
assert (_unique_degrees[-1] is None and len(_unique_degrees) == 1) or np.issubdtype(_unique_degrees.dtype,int), f"One invariant at the time ! Asked for computations of {degrees}"
|
|
207
|
+
|
|
208
|
+
if _unique_degrees[0] is None:
|
|
209
|
+
assert invariant not in ["hilbert", "rank_invariant"], f"Provide a degree to compute {invariant} !"
|
|
210
|
+
invariant=2
|
|
211
|
+
degree=0
|
|
212
|
+
else:
|
|
213
|
+
invariant=1 if invariant is None or invariant is "hilbert" else 3
|
|
214
|
+
|
|
215
|
+
if grid_conversion is None and grid_shape is None:
|
|
216
|
+
if len(simplextree.filtration_grid[0]) > 0 and infer_grid:
|
|
217
|
+
grid_conversion = [np.asarray(f) for f in simplextree.filtration_grid]
|
|
218
|
+
else:
|
|
219
|
+
if infer_grid:
|
|
220
|
+
grid_conversion = simplextree.get_filtration_grid(grid_strategy=grid_strategy,**get_filtration_grid_kwargs)
|
|
221
|
+
if zero_pad:
|
|
222
|
+
mass_default_location = (1.1*f[-1] - f[0] for f in grid_conversion) if mass_default=='auto' else mass_default
|
|
223
|
+
grid_conversion = [np.concatenate([f, [xi]]) for f,xi in zip(grid_conversion, mass_default_location)]
|
|
224
|
+
## Copy necessary, as we want a coordinate simplextree
|
|
225
|
+
simplextree_copy = SimplexTreeMulti(simplextree)
|
|
226
|
+
simplextree_copy, simplextree = simplextree, simplextree_copy
|
|
227
|
+
simplextree.grid_squeeze(grid_conversion, coordinate_values=True)
|
|
228
|
+
else:
|
|
229
|
+
grid_shape = np.asarray(simplextree.filtration_bounds()[1], dtype=int)+1
|
|
230
|
+
|
|
231
|
+
if grid_conversion is not None and len(simplextree.filtration_grid[0]) == 0 and infer_grid:
|
|
232
|
+
simplextree.grid_squeeze(grid_conversion, coordinate_values=True)
|
|
233
|
+
try:
|
|
234
|
+
int(grid_shape)
|
|
235
|
+
grid_shape = [grid_shape]*simplextree.num_parameters
|
|
236
|
+
except:
|
|
237
|
+
if grid_shape is None:
|
|
238
|
+
grid_shape = [len(f) for f in grid_conversion]
|
|
239
|
+
|
|
240
|
+
num_degrees = len(degrees)
|
|
241
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
242
|
+
cdef vector[int] c_grid_shape = grid_shape
|
|
243
|
+
cdef vector[signed_measure_type] _out
|
|
244
|
+
cdef int cinvariant =invariant
|
|
245
|
+
# cdef int cdegree = degree
|
|
246
|
+
if invariant == 2: degrees=np.array([],dtype=int)
|
|
247
|
+
cdef vector[int] cdegrees = degrees
|
|
248
|
+
num_parameters = simplextree.num_parameters
|
|
249
|
+
if invariant == 3:
|
|
250
|
+
assert simplextree.num_parameters == 2, "Rank invariant computations are limited for 2-parameter simplextree for the moment"
|
|
251
|
+
assert len(degrees) ==1, 'One degree at the time, WIP.'
|
|
252
|
+
from multipers.ml.signed_betti import rank_decomposition_by_rectangles
|
|
253
|
+
from torch import Tensor
|
|
254
|
+
out = rank_invariant2d(simplextree, grid_shape,degrees[0])
|
|
255
|
+
out = rank_decomposition_by_rectangles(out, threshold= zero_pad)
|
|
256
|
+
out = Tensor(out.copy()).to_sparse()
|
|
257
|
+
def _is_trivial(rectangle:np.ndarray):
|
|
258
|
+
birth=rectangle[:num_parameters]
|
|
259
|
+
death=rectangle[num_parameters:]
|
|
260
|
+
return np.all(birth<=death) and not np.array_equal(birth,death)
|
|
261
|
+
coords = np.asarray(out.indices().T, dtype=int)
|
|
262
|
+
weights = np.asarray(out.values(), dtype=int)
|
|
263
|
+
correct_indices = np.asarray([_is_trivial(rectangle) for rectangle in coords])
|
|
264
|
+
if len(correct_indices) == 0:
|
|
265
|
+
pts, weights = np.empty((0, num_parameters)), np.empty((0))
|
|
266
|
+
else:
|
|
267
|
+
pts = np.asarray(coords[correct_indices])
|
|
268
|
+
weights = weights[correct_indices]
|
|
269
|
+
out = [ ## same output format as others
|
|
270
|
+
(pts,weights)
|
|
271
|
+
]
|
|
272
|
+
else:
|
|
273
|
+
with nogil:
|
|
274
|
+
_out = get_signed_measure(ptr, c_grid_shape, cinvariant, cdegrees, zero_pad)
|
|
275
|
+
|
|
276
|
+
out = [[np.asarray(_out[i].first, dtype=int), np.asarray(_out[i].second, dtype=int)] for i in range(num_degrees)]
|
|
277
|
+
|
|
278
|
+
for i,(pts, weights) in enumerate(out):
|
|
279
|
+
if len(pts) == 0:
|
|
280
|
+
out[i][0]=np.empty(shape=(0,2*num_parameters if invariant == 3 else num_parameters), dtype=int)
|
|
281
|
+
|
|
282
|
+
if unsparse:
|
|
283
|
+
from torch import sparse_coo_tensor
|
|
284
|
+
if invariant == 3:
|
|
285
|
+
grid_shape = list(grid_shape) + list(grid_shape)
|
|
286
|
+
return [np.asarray(sparse_coo_tensor(indices=pts.T,values=weights, size=tuple(grid_shape)).to_dense(), dtype=weights.dtype) for pts,weights in out]
|
|
287
|
+
|
|
288
|
+
if grid_conversion is not None:
|
|
289
|
+
for degree,(pts,weights) in enumerate(out):
|
|
290
|
+
coords = np.empty(shape=pts.shape)
|
|
291
|
+
for i in range(coords.shape[1]):
|
|
292
|
+
coords[:,i] = grid_conversion[i % num_parameters][pts[:,i]]
|
|
293
|
+
out[degree]=(coords, weights)
|
|
294
|
+
|
|
295
|
+
if plot:
|
|
296
|
+
assert simplextree.num_parameters == 2, "Plot only available for 2-parameter persistence."
|
|
297
|
+
from multipers.plots import plot_signed_measures
|
|
298
|
+
plot_signed_measures(out)
|
|
299
|
+
# fig, axes = plt.subplots(nrows=1,ncols=num_degrees)
|
|
300
|
+
# if num_degrees == 1:
|
|
301
|
+
# axes = [axes]
|
|
302
|
+
# for ax, signed_measure in zip(axes,out):
|
|
303
|
+
# plt.sca(ax)
|
|
304
|
+
# # ax.set_aspect('equal')
|
|
305
|
+
# if (invariant != 3):
|
|
306
|
+
# color_weights = np.empty(weights.shape, dtype=int)
|
|
307
|
+
# color_weights[weights>0] = 1
|
|
308
|
+
# color_weights[weights<0] = -1
|
|
309
|
+
# plt.scatter(coords[:,0],coords[:,1], c=color_weights, cmap="coolwarm")
|
|
310
|
+
# else:
|
|
311
|
+
# def _plot_rectangle(rectangle:np.ndarray, weight):
|
|
312
|
+
# x_axis=rectangle[[0,2]]
|
|
313
|
+
# y_axis=rectangle[[1,3]]
|
|
314
|
+
# color = "blue" if weight > 0 else "red"
|
|
315
|
+
# plt.plot(x_axis, y_axis, c=color)
|
|
316
|
+
# for rectangle, weight in zip(coords, weights):
|
|
317
|
+
# _plot_rectangle(rectangle=rectangle, weight=weight)
|
|
318
|
+
return out
|
|
319
|
+
|
|
320
|
+
def degree_rips(simplextree, int num_degrees, int homological_degree):
|
|
321
|
+
cdef intptr_t ptr = simplextree.thisptr
|
|
322
|
+
cdef signed_measure_double_type out
|
|
323
|
+
with nogil:
|
|
324
|
+
out = degree_rips_hilbert_signed_measure(ptr, num_degrees, homological_degree)
|
|
325
|
+
pts, weights = np.asarray(out.first), np.asarray(out.second, dtype=int)
|
|
326
|
+
if len(pts) == 0:
|
|
327
|
+
pts=np.empty(shape=(0,2), dtype=float)
|
|
328
|
+
return pts,weights
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from multipers.simplex_tree_multi import SimplexTreeMulti # Typing hack
|
|
2
|
+
from typing import Optional
|
|
3
|
+
import numpy as np
|
|
4
|
+
from multipers.simplex_tree_multi import _available_strategies
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def signed_measure(
|
|
8
|
+
simplextree: SimplexTreeMulti,
|
|
9
|
+
degree: Optional[int] = None,
|
|
10
|
+
degrees=[None],
|
|
11
|
+
mass_default=None,
|
|
12
|
+
grid_strategy: _available_strategies = "exact",
|
|
13
|
+
invariant: Optional[str] = None,
|
|
14
|
+
plot: bool = False,
|
|
15
|
+
verbose: bool = False,
|
|
16
|
+
n_jobs: int = -1,
|
|
17
|
+
expand_collapse: bool = False,
|
|
18
|
+
backend: str = "multipers",
|
|
19
|
+
thread_id: str = "",
|
|
20
|
+
mpfree_path: Optional[str] = None,
|
|
21
|
+
grid_conversion: Optional[list] = None,
|
|
22
|
+
num_collapses: int = 0,
|
|
23
|
+
**infer_grid_kwargs,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Computes the signed measures given by the decomposition of the hilbert function or the euler characteristic.
|
|
27
|
+
|
|
28
|
+
Input
|
|
29
|
+
-----
|
|
30
|
+
- simplextree:SimplexTreeMulti, the multifiltered simplicial complex. Its recommended to squeeze the simplextree first.
|
|
31
|
+
- mass_default: Either None, or 'auto' or 'inf', or array-like of floats. Where to put the default mass to get a zero-mass measure.
|
|
32
|
+
- degree:int|None / degrees:list[int] the degrees to compute. None represents the euler characteristic.
|
|
33
|
+
- plot:bool, plots the computed measures if true.
|
|
34
|
+
- n_jobs:int, number of jobs. Defaults to #cpu, but when doing parallel computations of signed measures, we recommend setting this to 1.
|
|
35
|
+
- verbose:bool, prints c++ logs.
|
|
36
|
+
|
|
37
|
+
Output
|
|
38
|
+
------
|
|
39
|
+
`[signed_measure_of_degree for degree in degrees]`
|
|
40
|
+
with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
|
|
41
|
+
"""
|
|
42
|
+
assert invariant is None or invariant in [
|
|
43
|
+
"hilbert",
|
|
44
|
+
"rank_invariant",
|
|
45
|
+
"euler",
|
|
46
|
+
"rank",
|
|
47
|
+
"euler_characteristic",
|
|
48
|
+
"hilbert_function",
|
|
49
|
+
]
|
|
50
|
+
assert not plot or simplextree.num_parameters == 2, "Can only plot 2d measures."
|
|
51
|
+
if len(degrees) == 1 and degrees[0] is None and degree is not None:
|
|
52
|
+
degrees = [degree]
|
|
53
|
+
if None in degrees:
|
|
54
|
+
assert len(degrees) == 1
|
|
55
|
+
|
|
56
|
+
if len(degrees) == 0:
|
|
57
|
+
return []
|
|
58
|
+
|
|
59
|
+
if not simplextree._is_squeezed:
|
|
60
|
+
simplextree_ = SimplexTreeMulti(simplextree)
|
|
61
|
+
if grid_conversion is None:
|
|
62
|
+
simplextree_.grid_squeeze(
|
|
63
|
+
grid_strategy=grid_strategy,
|
|
64
|
+
coordinate_values=not (backend == "mpfree"),
|
|
65
|
+
**infer_grid_kwargs,
|
|
66
|
+
) # put a warning ?
|
|
67
|
+
else:
|
|
68
|
+
simplextree_.grid_squeeze(
|
|
69
|
+
grid_conversion,
|
|
70
|
+
coordinate_values=not (backend == "mpfree"),
|
|
71
|
+
**infer_grid_kwargs,
|
|
72
|
+
)
|
|
73
|
+
if num_collapses != 0:
|
|
74
|
+
simplextree_.collapse_edges(num_collapses)
|
|
75
|
+
else:
|
|
76
|
+
simplextree_ = simplextree
|
|
77
|
+
if backend == "mpfree":
|
|
78
|
+
if mpfree_path is not None:
|
|
79
|
+
import multipers.io as mio
|
|
80
|
+
|
|
81
|
+
mio.mpfree_path = mpfree_path
|
|
82
|
+
assert (
|
|
83
|
+
len(degrees) == 1
|
|
84
|
+
and mass_default is None
|
|
85
|
+
and (invariant is None or "hilbert" in invariant)
|
|
86
|
+
)
|
|
87
|
+
from multipers.io import minimal_presentation_from_mpfree
|
|
88
|
+
|
|
89
|
+
minimal_presentation = minimal_presentation_from_mpfree(
|
|
90
|
+
simplextree,
|
|
91
|
+
True,
|
|
92
|
+
degrees[0],
|
|
93
|
+
id=thread_id,
|
|
94
|
+
)
|
|
95
|
+
if grid_conversion is not None:
|
|
96
|
+
grid_conversion = grid_conversion
|
|
97
|
+
elif simplextree._is_squeezed:
|
|
98
|
+
grid_conversion = simplextree.filtration_grid
|
|
99
|
+
else:
|
|
100
|
+
grid_conversion = None
|
|
101
|
+
sms = _signed_measure_from_scc(
|
|
102
|
+
minimal_presentation, grid_conversion=grid_conversion
|
|
103
|
+
)
|
|
104
|
+
if plot:
|
|
105
|
+
from multipers.plots import plot_signed_measures
|
|
106
|
+
|
|
107
|
+
plot_signed_measures(sms)
|
|
108
|
+
return sms
|
|
109
|
+
# assert simplextree.num_parameters == 2
|
|
110
|
+
if mass_default is None:
|
|
111
|
+
mass_default = mass_default
|
|
112
|
+
elif mass_default == "inf":
|
|
113
|
+
mass_default = np.array([np.inf] * simplextree.num_parameters)
|
|
114
|
+
elif mass_default == "auto":
|
|
115
|
+
grid_conversion = [np.asarray(f) for f in simplextree_.filtration_grid]
|
|
116
|
+
mass_default = np.array(
|
|
117
|
+
[1.1 * np.max(f) - 0.1 * np.min(f) for f in grid_conversion]
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
mass_default = np.asarray(mass_default)
|
|
121
|
+
assert (
|
|
122
|
+
mass_default.ndim == 1
|
|
123
|
+
and mass_default.shape[0] == simplextree.num_parameters
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if invariant in ["rank_invariant", "rank"]:
|
|
127
|
+
assert (
|
|
128
|
+
simplextree.num_parameters == 2
|
|
129
|
+
), "Rank invariant only implemented for 2-parameter modules."
|
|
130
|
+
from multipers.rank_invariant import signed_measure as smri
|
|
131
|
+
|
|
132
|
+
sms = smri(
|
|
133
|
+
simplextree_,
|
|
134
|
+
mass_default=mass_default,
|
|
135
|
+
degrees=degrees,
|
|
136
|
+
plot=plot,
|
|
137
|
+
expand_collapse=expand_collapse,
|
|
138
|
+
)
|
|
139
|
+
elif len(degrees) == 1 and degrees[0] is None:
|
|
140
|
+
assert invariant is None or invariant in [
|
|
141
|
+
"euler",
|
|
142
|
+
"euler_characteristic",
|
|
143
|
+
], "Provide a degree to compute hilbert function."
|
|
144
|
+
from multipers.euler_characteristic import euler_signed_measure
|
|
145
|
+
|
|
146
|
+
sms = [
|
|
147
|
+
euler_signed_measure(
|
|
148
|
+
simplextree_,
|
|
149
|
+
mass_default=mass_default,
|
|
150
|
+
verbose=verbose,
|
|
151
|
+
plot=plot,
|
|
152
|
+
grid_conversion=grid_conversion,
|
|
153
|
+
)
|
|
154
|
+
]
|
|
155
|
+
else:
|
|
156
|
+
assert invariant is None or invariant in [
|
|
157
|
+
"hilbert",
|
|
158
|
+
"hilbert_function",
|
|
159
|
+
], "Found homological degrees for euler computation."
|
|
160
|
+
from multipers.hilbert_function import hilbert_signed_measure
|
|
161
|
+
|
|
162
|
+
sms = hilbert_signed_measure(
|
|
163
|
+
simplextree_,
|
|
164
|
+
degrees=degrees,
|
|
165
|
+
mass_default=mass_default,
|
|
166
|
+
verbose=verbose,
|
|
167
|
+
plot=plot,
|
|
168
|
+
n_jobs=n_jobs,
|
|
169
|
+
expand_collapse=expand_collapse,
|
|
170
|
+
grid_conversion=grid_conversion,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return sms
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _signed_measure_from_scc(minimal_presentation, grid_conversion=None):
|
|
177
|
+
pts = np.concatenate([b[0] for b in minimal_presentation if len(b[0]) > 0])
|
|
178
|
+
weights = np.concatenate(
|
|
179
|
+
[
|
|
180
|
+
(1 - 2 * (i % 2)) * np.ones(len(b[0]))
|
|
181
|
+
for i, b in enumerate(minimal_presentation)
|
|
182
|
+
if len(b[0]) > 0
|
|
183
|
+
]
|
|
184
|
+
)
|
|
185
|
+
if grid_conversion is not None:
|
|
186
|
+
pts = np.asarray(pts, dtype=int)
|
|
187
|
+
coords = np.empty(shape=pts.shape, dtype=float)
|
|
188
|
+
for i in range(coords.shape[1]):
|
|
189
|
+
coords[:, i] = np.asarray(grid_conversion[i])[pts[:, i]]
|
|
190
|
+
sm = [(coords, weights)]
|
|
191
|
+
else:
|
|
192
|
+
sm = [(pts, weights)]
|
|
193
|
+
return sm
|