freealg 0.7.12__py3-none-any.whl → 0.7.15__py3-none-any.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.
- freealg/__version__.py +1 -1
- freealg/_algebraic_form/_cusp.py +357 -0
- freealg/_algebraic_form/_cusp_wrap.py +268 -0
- freealg/_algebraic_form/_decompress2.py +2 -0
- freealg/_algebraic_form/_decompress4.py +739 -0
- freealg/_algebraic_form/_decompress5.py +738 -0
- freealg/_algebraic_form/_decompress6.py +492 -0
- freealg/_algebraic_form/_decompress7.py +355 -0
- freealg/_algebraic_form/_decompress8.py +369 -0
- freealg/_algebraic_form/_decompress9.py +363 -0
- freealg/_algebraic_form/_decompress_new.py +431 -0
- freealg/_algebraic_form/_decompress_new_2.py +1631 -0
- freealg/_algebraic_form/_decompress_util.py +172 -0
- freealg/_algebraic_form/_homotopy2.py +289 -0
- freealg/_algebraic_form/_homotopy3.py +215 -0
- freealg/_algebraic_form/_homotopy4.py +320 -0
- freealg/_algebraic_form/_homotopy5.py +185 -0
- freealg/_algebraic_form/_moments.py +0 -1
- freealg/_algebraic_form/_support.py +132 -177
- freealg/_algebraic_form/algebraic_form.py +21 -2
- freealg/distributions/_compound_poisson.py +481 -0
- freealg/distributions/_deformed_marchenko_pastur.py +6 -7
- {freealg-0.7.12.dist-info → freealg-0.7.15.dist-info}/METADATA +1 -1
- {freealg-0.7.12.dist-info → freealg-0.7.15.dist-info}/RECORD +28 -12
- {freealg-0.7.12.dist-info → freealg-0.7.15.dist-info}/WHEEL +0 -0
- {freealg-0.7.12.dist-info → freealg-0.7.15.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.7.12.dist-info → freealg-0.7.15.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.7.12.dist-info → freealg-0.7.15.dist-info}/top_level.txt +0 -0
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
import numpy.polynomial.polynomial as poly
|
|
16
|
+
from ._homotopy5 import StieltjesPoly
|
|
16
17
|
|
|
17
18
|
__all__ = ['compute_support']
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
#
|
|
21
|
-
# poly coeffs in m
|
|
22
|
-
#
|
|
21
|
+
# =====================
|
|
22
|
+
# poly coeffs in m at z
|
|
23
|
+
# =====================
|
|
23
24
|
|
|
24
25
|
def _poly_coeffs_in_m_at_z(a_coeffs, z):
|
|
25
|
-
|
|
26
26
|
s = a_coeffs.shape[1] - 1
|
|
27
27
|
a = numpy.empty(s + 1, dtype=numpy.complex128)
|
|
28
28
|
for j in range(s + 1):
|
|
@@ -30,48 +30,11 @@ def _poly_coeffs_in_m_at_z(a_coeffs, z):
|
|
|
30
30
|
return a
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
# ===============
|
|
34
|
-
# roots poly in m
|
|
35
|
-
# ===============
|
|
36
|
-
|
|
37
|
-
def _roots_poly_in_m(c_asc, tol=0.0):
|
|
38
|
-
|
|
39
|
-
c = numpy.asarray(c_asc, dtype=numpy.complex128).ravel()
|
|
40
|
-
if c.size <= 1:
|
|
41
|
-
return numpy.array([], dtype=numpy.complex128)
|
|
42
|
-
|
|
43
|
-
k = c.size - 1
|
|
44
|
-
while k > 0 and abs(c[k]) <= tol:
|
|
45
|
-
k -= 1
|
|
46
|
-
c = c[:k + 1]
|
|
47
|
-
if c.size <= 1:
|
|
48
|
-
return numpy.array([], dtype=numpy.complex128)
|
|
49
|
-
|
|
50
|
-
return numpy.roots(c[::-1])
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# ================
|
|
54
|
-
# dPdm coeffs at z
|
|
55
|
-
# ================
|
|
56
|
-
|
|
57
|
-
def _dPdm_coeffs_at_z(a_coeffs, z):
|
|
58
|
-
|
|
59
|
-
a = _poly_coeffs_in_m_at_z(a_coeffs, z)
|
|
60
|
-
s = a.size - 1
|
|
61
|
-
if s <= 0:
|
|
62
|
-
return numpy.array([0.0 + 0.0j], dtype=numpy.complex128)
|
|
63
|
-
d = numpy.empty(s, dtype=numpy.complex128)
|
|
64
|
-
for j in range(1, s + 1):
|
|
65
|
-
d[j - 1] = j * a[j]
|
|
66
|
-
return d
|
|
67
|
-
|
|
68
|
-
|
|
69
33
|
# ==============
|
|
70
34
|
# P and partials
|
|
71
35
|
# ==============
|
|
72
36
|
|
|
73
37
|
def _P_and_partials(a_coeffs, z, m):
|
|
74
|
-
|
|
75
38
|
s = a_coeffs.shape[1] - 1
|
|
76
39
|
|
|
77
40
|
a = numpy.empty(s + 1, dtype=numpy.complex128)
|
|
@@ -97,7 +60,7 @@ def _P_and_partials(a_coeffs, z, m):
|
|
|
97
60
|
for j in range(2, s + 1):
|
|
98
61
|
Pmm += j * (j - 1) * a[j] * (m ** (j - 2))
|
|
99
62
|
|
|
100
|
-
return P, Pz, Pm, Pzm, Pmm
|
|
63
|
+
return P, Pz, Pm, Pzm, Pmm
|
|
101
64
|
|
|
102
65
|
|
|
103
66
|
# ===========
|
|
@@ -105,13 +68,12 @@ def _P_and_partials(a_coeffs, z, m):
|
|
|
105
68
|
# ===========
|
|
106
69
|
|
|
107
70
|
def _newton_edge(a_coeffs, x0, m0, tol=1e-12, max_iter=50):
|
|
108
|
-
|
|
109
71
|
x = float(x0)
|
|
110
72
|
m = float(m0)
|
|
111
73
|
|
|
112
74
|
for _ in range(max_iter):
|
|
113
75
|
z = x + 0.0j
|
|
114
|
-
P, Pz, Pm, Pzm, Pmm
|
|
76
|
+
P, Pz, Pm, Pzm, Pmm = _P_and_partials(a_coeffs, z, m)
|
|
115
77
|
|
|
116
78
|
f0 = float(numpy.real(P))
|
|
117
79
|
f1 = float(numpy.real(Pm))
|
|
@@ -142,10 +104,8 @@ def _newton_edge(a_coeffs, x0, m0, tol=1e-12, max_iter=50):
|
|
|
142
104
|
# =============
|
|
143
105
|
|
|
144
106
|
def _cluster_edges(edges, x_tol):
|
|
145
|
-
|
|
146
107
|
if len(edges) == 0:
|
|
147
108
|
return numpy.array([], dtype=float)
|
|
148
|
-
|
|
149
109
|
edges = numpy.array(sorted(edges), dtype=float)
|
|
150
110
|
out = [edges[0]]
|
|
151
111
|
for e in edges[1:]:
|
|
@@ -154,156 +114,151 @@ def _cluster_edges(edges, x_tol):
|
|
|
154
114
|
return numpy.array(out, dtype=float)
|
|
155
115
|
|
|
156
116
|
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
def _pick_physical_root_at_z(a_coeffs, z, im_sign=+1):
|
|
162
|
-
|
|
163
|
-
a = _poly_coeffs_in_m_at_z(a_coeffs, z)
|
|
164
|
-
r = _roots_poly_in_m(a)
|
|
165
|
-
if r.size == 0:
|
|
166
|
-
return numpy.nan + 1j * numpy.nan
|
|
167
|
-
|
|
168
|
-
w_ref = -1.0 / z
|
|
169
|
-
idx = int(numpy.argmin(numpy.abs(r - w_ref)))
|
|
170
|
-
w = r[idx]
|
|
117
|
+
# ===========
|
|
118
|
+
# bisect edge
|
|
119
|
+
# ===========
|
|
171
120
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
121
|
+
def _bisect_edge(stieltjes_poly, x_lo, x_hi, eta, im_thr, max_iter=60):
|
|
122
|
+
z_lo = x_lo + 1j * eta
|
|
123
|
+
z_hi = x_hi + 1j * eta
|
|
124
|
+
f_lo = float(numpy.imag(stieltjes_poly.evaluate_scalar(z_lo)) - im_thr)
|
|
125
|
+
f_hi = float(numpy.imag(stieltjes_poly.evaluate_scalar(z_hi)) - im_thr)
|
|
126
|
+
|
|
127
|
+
if (not numpy.isfinite(f_lo)) or (not numpy.isfinite(f_hi)):
|
|
128
|
+
return 0.5 * (x_lo + x_hi)
|
|
129
|
+
if f_lo == 0.0:
|
|
130
|
+
return float(x_lo)
|
|
131
|
+
if f_hi == 0.0:
|
|
132
|
+
return float(x_hi)
|
|
133
|
+
if f_lo * f_hi > 0.0:
|
|
134
|
+
return 0.5 * (x_lo + x_hi)
|
|
135
|
+
|
|
136
|
+
a = float(x_lo)
|
|
137
|
+
b = float(x_hi)
|
|
138
|
+
fa = f_lo
|
|
139
|
+
# fb = f_hi
|
|
177
140
|
|
|
178
|
-
|
|
141
|
+
for _ in range(max_iter):
|
|
142
|
+
c = 0.5 * (a + b)
|
|
143
|
+
z_c = c + 1j * eta
|
|
144
|
+
fc = float(numpy.imag(stieltjes_poly.evaluate_scalar(z_c)) - im_thr)
|
|
145
|
+
if not numpy.isfinite(fc):
|
|
146
|
+
return c
|
|
147
|
+
if fc == 0.0 or (b - a) < 1e-14 * (1.0 + abs(c)):
|
|
148
|
+
return c
|
|
149
|
+
if fa * fc <= 0.0:
|
|
150
|
+
b = c
|
|
151
|
+
# fb = fc
|
|
152
|
+
else:
|
|
153
|
+
a = c
|
|
154
|
+
fa = fc
|
|
155
|
+
|
|
156
|
+
return 0.5 * (a + b)
|
|
179
157
|
|
|
180
158
|
|
|
181
159
|
# ===============
|
|
182
160
|
# compute support
|
|
183
161
|
# ===============
|
|
184
162
|
|
|
185
|
-
def compute_support(a_coeffs,
|
|
186
|
-
x_min,
|
|
187
|
-
x_max,
|
|
188
|
-
n_scan=4000,
|
|
189
|
-
y_eps=1e-3,
|
|
190
|
-
im_sign=+1,
|
|
191
|
-
root_tol=0.0,
|
|
192
|
-
edge_rel_tol=1e-6,
|
|
193
|
-
edge_x_cluster_tol=1e-3,
|
|
194
|
-
newton_tol=1e-12):
|
|
195
|
-
"""
|
|
196
|
-
Fast support from fitted polynomial using branch-point system P=0, Pm=0.
|
|
197
|
-
|
|
198
|
-
Returns
|
|
199
|
-
-------
|
|
200
|
-
support : list of (a,b)
|
|
201
|
-
info : dict (edges, rel_res_curve, etc.)
|
|
202
|
-
"""
|
|
203
|
-
|
|
204
|
-
a_coeffs = numpy.asarray(a_coeffs)
|
|
205
|
-
x_grid = numpy.linspace(float(x_min), float(x_max), int(n_scan))
|
|
206
|
-
|
|
207
|
-
# For each x, find best real critical point m (Pm=0) minimizing rel
|
|
208
|
-
# residual.
|
|
209
|
-
rel = numpy.full(x_grid.size, numpy.inf, dtype=float)
|
|
210
|
-
m_star = numpy.full(x_grid.size, numpy.nan, dtype=float)
|
|
211
|
-
|
|
212
|
-
for i, x in enumerate(x_grid):
|
|
213
|
-
z = x + 0.0j
|
|
214
|
-
dcoef = _dPdm_coeffs_at_z(a_coeffs, z)
|
|
215
|
-
mr = _roots_poly_in_m(dcoef, tol=root_tol)
|
|
216
|
-
|
|
217
|
-
best = numpy.inf
|
|
218
|
-
best_m = numpy.nan
|
|
219
|
-
|
|
220
|
-
for w in mr:
|
|
221
|
-
# accept nearly-real roots; numerical roots can have small imag
|
|
222
|
-
# part
|
|
223
|
-
if abs(w.imag) > 1e-6 * (1.0 + abs(w.real)):
|
|
224
|
-
continue
|
|
225
|
-
m = float(w.real)
|
|
226
|
-
P, _, _, _, _, a = _P_and_partials(a_coeffs, z, m)
|
|
227
|
-
|
|
228
|
-
denom = 1.0
|
|
229
|
-
am = 1.0
|
|
230
|
-
for j in range(a.size):
|
|
231
|
-
denom += abs(a[j]) * abs(am)
|
|
232
|
-
am *= m
|
|
233
|
-
|
|
234
|
-
r = abs(numpy.real(P)) / denom
|
|
235
|
-
if numpy.isfinite(r) and r < best:
|
|
236
|
-
best = float(r)
|
|
237
|
-
best_m = m
|
|
238
|
-
|
|
239
|
-
rel[i] = best
|
|
240
|
-
m_star[i] = best_m
|
|
241
|
-
|
|
242
|
-
# Pick candidate edges as local minima of rel(x), below an automatic scale.
|
|
243
|
-
rel_f = rel[numpy.isfinite(rel)]
|
|
244
|
-
if rel_f.size == 0:
|
|
245
|
-
return [], {"edges": numpy.array([], dtype=float), "n_edges": 0}
|
|
246
|
-
|
|
247
|
-
med = float(numpy.median(rel_f))
|
|
248
|
-
min_rel = float(numpy.min(rel_f))
|
|
249
|
-
|
|
250
|
-
# accept local minima up to a factor above the best one, but never abov
|
|
251
|
-
# background scale
|
|
252
|
-
thr = min(0.1 * med, max(float(edge_rel_tol), 1e4 * min_rel))
|
|
253
|
-
|
|
254
|
-
edges0 = []
|
|
255
|
-
seeds = []
|
|
256
|
-
|
|
257
|
-
for i in range(1, x_grid.size - 1):
|
|
258
|
-
if not numpy.isfinite(rel[i]):
|
|
259
|
-
continue
|
|
260
|
-
if rel[i] <= rel[i - 1] and rel[i] <= rel[i + 1] and rel[i] < thr and \
|
|
261
|
-
numpy.isfinite(m_star[i]):
|
|
262
|
-
edges0.append(float(x_grid[i]))
|
|
263
|
-
seeds.append((float(x_grid[i]), float(m_star[i])))
|
|
163
|
+
def compute_support(a_coeffs, x_min, x_max, n_scan=4000, **kwargs):
|
|
264
164
|
|
|
265
|
-
|
|
266
|
-
edges = []
|
|
267
|
-
for x0, m0 in seeds:
|
|
268
|
-
xe, me, ok = _newton_edge(a_coeffs, x0, m0, tol=newton_tol)
|
|
269
|
-
if ok and numpy.isfinite(xe) and numpy.isfinite(me):
|
|
270
|
-
edges.append(float(xe))
|
|
165
|
+
a_coeffs = numpy.asarray(a_coeffs, dtype=numpy.complex128)
|
|
271
166
|
|
|
272
|
-
|
|
273
|
-
|
|
167
|
+
x_min = float(x_min)
|
|
168
|
+
x_max = float(x_max)
|
|
169
|
+
n_scan = int(n_scan)
|
|
274
170
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
171
|
+
scale = max(1.0, abs(x_max - x_min), abs(x_min), abs(x_max))
|
|
172
|
+
eta = kwargs.get('eta', None)
|
|
173
|
+
if eta is None:
|
|
174
|
+
eta = 1e-6 * scale
|
|
175
|
+
eta = float(eta)
|
|
176
|
+
|
|
177
|
+
vopt = {
|
|
178
|
+
'lam_space': 1.0,
|
|
179
|
+
'lam_asym': 1.0,
|
|
180
|
+
'lam_tiny_im': 200.0,
|
|
181
|
+
'tiny_im': 0.5 * eta,
|
|
182
|
+
'tol_im': 1e-14,
|
|
183
|
+
}
|
|
184
|
+
vopt.update(kwargs.get('viterbi_opt', {}) or {})
|
|
185
|
+
stieltjes = StieltjesPoly(a_coeffs, viterbi_opt=vopt)
|
|
186
|
+
|
|
187
|
+
x_grid = numpy.linspace(x_min, x_max, n_scan)
|
|
188
|
+
z_grid = x_grid + 1j * eta
|
|
189
|
+
m_grid = stieltjes(z_grid)
|
|
190
|
+
im_grid = numpy.imag(m_grid)
|
|
191
|
+
|
|
192
|
+
max_im = float(numpy.nanmax(im_grid)) \
|
|
193
|
+
if numpy.any(numpy.isfinite(im_grid)) else 0.0
|
|
278
194
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
195
|
+
thr_rel = float(kwargs.get('thr_rel', 1e-3))
|
|
196
|
+
thr_abs = kwargs.get('thr_abs', None)
|
|
197
|
+
|
|
198
|
+
im_thr = thr_rel * max_im
|
|
199
|
+
im_thr = max(im_thr, 10.0 * eta)
|
|
200
|
+
if thr_abs is not None:
|
|
201
|
+
im_thr = max(im_thr, float(thr_abs))
|
|
202
|
+
|
|
203
|
+
mask = numpy.isfinite(im_grid) & (im_grid > im_thr)
|
|
204
|
+
|
|
205
|
+
runs = []
|
|
206
|
+
i = 0
|
|
207
|
+
while i < mask.size:
|
|
208
|
+
if not mask[i]:
|
|
209
|
+
i += 1
|
|
210
|
+
continue
|
|
211
|
+
j = i
|
|
212
|
+
while j + 1 < mask.size and mask[j + 1]:
|
|
213
|
+
j += 1
|
|
214
|
+
runs.append((i, j))
|
|
215
|
+
i = j + 1
|
|
216
|
+
|
|
217
|
+
edges = []
|
|
218
|
+
for i0, i1 in runs:
|
|
219
|
+
if i0 == 0 or i1 == mask.size - 1:
|
|
283
220
|
continue
|
|
284
221
|
|
|
285
|
-
|
|
222
|
+
xL = _bisect_edge(stieltjes, x_grid[i0 - 1], x_grid[i0], eta, im_thr)
|
|
223
|
+
xR = _bisect_edge(stieltjes, x_grid[i1], x_grid[i1 + 1], eta, im_thr)
|
|
286
224
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
r = _roots_poly_in_m(a_m, tol=root_tol)
|
|
225
|
+
edges.append(float(xL))
|
|
226
|
+
edges.append(float(xR))
|
|
290
227
|
|
|
291
|
-
|
|
292
|
-
|
|
228
|
+
edge_x_cluster_tol = float(kwargs.get('edge_x_cluster_tol', 1e-8 * scale))
|
|
229
|
+
edges = _cluster_edges(edges, edge_x_cluster_tol)
|
|
230
|
+
|
|
231
|
+
refine = bool(kwargs.get('refine', True))
|
|
232
|
+
if refine and edges.size > 0:
|
|
233
|
+
newton_tol = float(kwargs.get('newton_tol', 1e-12))
|
|
234
|
+
edges_ref = []
|
|
235
|
+
for x0 in edges:
|
|
236
|
+
m0 = float(numpy.real(stieltjes.evaluate_scalar(x0 + 1j * eta)))
|
|
237
|
+
xe, _, ok = _newton_edge(a_coeffs, x0, m0, tol=newton_tol)
|
|
238
|
+
edges_ref.append(float(xe)
|
|
239
|
+
if ok and numpy.isfinite(xe) else float(x0))
|
|
240
|
+
edges = _cluster_edges(edges_ref, edge_x_cluster_tol)
|
|
241
|
+
|
|
242
|
+
edges.sort()
|
|
243
|
+
support = []
|
|
244
|
+
for k in range(0, edges.size - 1, 2):
|
|
245
|
+
a = float(edges[k])
|
|
246
|
+
b = float(edges[k + 1])
|
|
247
|
+
if b > a:
|
|
293
248
|
support.append((a, b))
|
|
294
249
|
|
|
295
250
|
info = {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
251
|
+
'x_grid': x_grid,
|
|
252
|
+
'eta': eta,
|
|
253
|
+
'm_grid': m_grid,
|
|
254
|
+
'im_grid': im_grid,
|
|
255
|
+
'im_thr': float(im_thr),
|
|
256
|
+
'edges': edges,
|
|
257
|
+
'support': support,
|
|
258
|
+
'x_min': x_min,
|
|
259
|
+
'x_max': x_max,
|
|
260
|
+
'n_scan': n_scan,
|
|
261
|
+
'scale': scale,
|
|
307
262
|
}
|
|
308
263
|
|
|
309
264
|
return support, info
|
|
@@ -323,7 +323,26 @@ class AlgebraicForm(object):
|
|
|
323
323
|
status['res_99_9'] = float(res_99_9)
|
|
324
324
|
status['fit_metrics'] = fit_metrics
|
|
325
325
|
self.status = status
|
|
326
|
-
|
|
326
|
+
|
|
327
|
+
# -----------------
|
|
328
|
+
|
|
329
|
+
# Inflate a bit to make sure all points are searched
|
|
330
|
+
# x_min, x_max = self._inflate_broad_support(inflate=0.2)
|
|
331
|
+
# scale = float(max(1.0, abs(x_max - x_min), abs(x_min), abs(x_max)))
|
|
332
|
+
# eta = 1e-6 * scale
|
|
333
|
+
#
|
|
334
|
+
# vopt = {
|
|
335
|
+
# 'lam_space': 1.0,
|
|
336
|
+
# 'lam_asym': 1.0,
|
|
337
|
+
# 'lam_tiny_im': 200.0,
|
|
338
|
+
# 'tiny_im': 0.5 * eta,
|
|
339
|
+
# 'tol_im': 1e-14,
|
|
340
|
+
# }
|
|
341
|
+
|
|
342
|
+
# NOTE overwrite init
|
|
343
|
+
self._stieltjes = StieltjesPoly(self.a_coeffs)
|
|
344
|
+
# self._stieltjes = StieltjesPoly(self.a_coeffs, viterbi_opt=vopt)
|
|
345
|
+
|
|
327
346
|
self._moments_base = AlgebraicStieltjesMoments(a_coeffs)
|
|
328
347
|
self.moments = Moments(self._moments_base)
|
|
329
348
|
|
|
@@ -760,7 +779,7 @@ class AlgebraicForm(object):
|
|
|
760
779
|
|
|
761
780
|
def mom(k):
|
|
762
781
|
return self.moments(k, t_i)
|
|
763
|
-
|
|
782
|
+
|
|
764
783
|
stieltjes_i = StieltjesPoly(coeffs_i, mom)
|
|
765
784
|
rho[i, :] = stieltjes_i(x).imag
|
|
766
785
|
|