freealg 0.7.11__py3-none-any.whl → 0.7.14__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/__init__.py +2 -2
- freealg/__version__.py +1 -1
- freealg/_algebraic_form/__init__.py +2 -1
- freealg/_algebraic_form/_constraints.py +53 -12
- freealg/_algebraic_form/_cusp.py +357 -0
- freealg/_algebraic_form/_cusp_wrap.py +268 -0
- freealg/_algebraic_form/_decompress.py +330 -381
- freealg/_algebraic_form/_decompress2.py +120 -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/_edge.py +46 -68
- freealg/_algebraic_form/_homotopy.py +62 -30
- 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 +43 -57
- freealg/_algebraic_form/_support.py +132 -177
- freealg/_algebraic_form/algebraic_form.py +163 -30
- freealg/distributions/__init__.py +3 -1
- freealg/distributions/_compound_poisson.py +464 -0
- freealg/distributions/_deformed_marchenko_pastur.py +51 -0
- freealg/distributions/_deformed_wigner.py +44 -0
- {freealg-0.7.11.dist-info → freealg-0.7.14.dist-info}/METADATA +2 -1
- {freealg-0.7.11.dist-info → freealg-0.7.14.dist-info}/RECORD +36 -20
- {freealg-0.7.11.dist-info → freealg-0.7.14.dist-info}/WHEEL +1 -1
- {freealg-0.7.11.dist-info → freealg-0.7.14.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.7.11.dist-info → freealg-0.7.14.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.7.11.dist-info → freealg-0.7.14.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
|
|
@@ -18,12 +18,35 @@ from ._continuation_algebraic import sample_z_joukowski, \
|
|
|
18
18
|
filter_z_away_from_cuts, fit_polynomial_relation, \
|
|
19
19
|
sanity_check_stieltjes_branch, eval_P
|
|
20
20
|
from ._edge import evolve_edges, merge_edges
|
|
21
|
-
from .
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
from ._cusp_wrap import cusp_wrap
|
|
22
|
+
|
|
23
|
+
# Decompress with Newton
|
|
24
|
+
# from ._decompress import build_time_grid, decompress_newton
|
|
25
|
+
from ._decompress_util import build_time_grid
|
|
26
|
+
# from ._decompress4 import decompress_newton # WORKS (mass issue)
|
|
27
|
+
# from ._decompress5 import build_time_grid, decompress_newton
|
|
28
|
+
# from ._decompress6 import build_time_grid, decompress_newton
|
|
29
|
+
# from ._decompress4_2 import build_time_grid, decompress_newton
|
|
30
|
+
# from ._decompress_new_2 import build_time_grid, decompress_newton
|
|
31
|
+
# from ._decompress_new import build_time_grid, decompress_newton
|
|
32
|
+
# from ._decompress6 import decompress_newton
|
|
33
|
+
# from ._decompress7 import decompress_newton
|
|
34
|
+
# from ._decompress8 import decompress_newton
|
|
35
|
+
from ._decompress9 import decompress_newton # With Predictor/Corrector
|
|
36
|
+
|
|
37
|
+
# Decompress with coefficients
|
|
38
|
+
from ._decompress2 import decompress_coeffs, plot_candidates
|
|
39
|
+
|
|
40
|
+
# Homotopy
|
|
41
|
+
# from ._homotopy import StieltjesPoly
|
|
42
|
+
# from ._homotopy2 import StieltjesPoly
|
|
43
|
+
# from ._homotopy3 import StieltjesPoly # Viterbi
|
|
44
|
+
# from ._homotopy4 import StieltjesPoly
|
|
45
|
+
from ._homotopy5 import StieltjesPoly
|
|
46
|
+
|
|
24
47
|
from ._branch_points import compute_branch_points
|
|
25
48
|
from ._support import compute_support
|
|
26
|
-
from ._moments import
|
|
49
|
+
from ._moments import Moments, AlgebraicStieltjesMoments
|
|
27
50
|
from .._free_form._support import supp
|
|
28
51
|
from .._free_form._plot_util import plot_density, plot_hilbert, plot_stieltjes
|
|
29
52
|
|
|
@@ -180,7 +203,7 @@ class AlgebraicForm(object):
|
|
|
180
203
|
# Use empirical Stieltjes function
|
|
181
204
|
self._stieltjes = lambda z: \
|
|
182
205
|
numpy.mean(1.0/(self.eig-z[:, numpy.newaxis]), axis=-1)
|
|
183
|
-
self._moments =
|
|
206
|
+
self._moments = Moments(self.eig) # NOTE (never used)
|
|
184
207
|
|
|
185
208
|
# broad support
|
|
186
209
|
if self.support is None:
|
|
@@ -189,10 +212,10 @@ class AlgebraicForm(object):
|
|
|
189
212
|
|
|
190
213
|
# Detect support
|
|
191
214
|
self.lam_m, self.lam_p = supp(self.eig, **kwargs)
|
|
192
|
-
self.broad_support = (self.lam_m, self.lam_p)
|
|
215
|
+
self.broad_support = (float(self.lam_m), float(self.lam_p))
|
|
193
216
|
else:
|
|
194
|
-
self.lam_m = min([s[0] for s in self.support])
|
|
195
|
-
self.lam_p = max([s[1] for s in self.support])
|
|
217
|
+
self.lam_m = float(min([s[0] for s in self.support]))
|
|
218
|
+
self.lam_p = float(max([s[1] for s in self.support]))
|
|
196
219
|
self.broad_support = (self.lam_m, self.lam_p)
|
|
197
220
|
|
|
198
221
|
# Initialize
|
|
@@ -257,7 +280,7 @@ class AlgebraicForm(object):
|
|
|
257
280
|
if self.support is not None:
|
|
258
281
|
possible_support = self.support
|
|
259
282
|
else:
|
|
260
|
-
possible_support = self.broad_support
|
|
283
|
+
possible_support = [self.broad_support]
|
|
261
284
|
|
|
262
285
|
for sup in possible_support:
|
|
263
286
|
a, b = sup
|
|
@@ -300,7 +323,28 @@ class AlgebraicForm(object):
|
|
|
300
323
|
status['res_99_9'] = float(res_99_9)
|
|
301
324
|
status['fit_metrics'] = fit_metrics
|
|
302
325
|
self.status = status
|
|
303
|
-
|
|
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
|
+
|
|
346
|
+
self._moments_base = AlgebraicStieltjesMoments(a_coeffs)
|
|
347
|
+
self.moments = Moments(self._moments_base)
|
|
304
348
|
|
|
305
349
|
if verbose:
|
|
306
350
|
print(f'fit residual max : {res_max:>0.4e}')
|
|
@@ -349,7 +393,7 @@ class AlgebraicForm(object):
|
|
|
349
393
|
# estimate support
|
|
350
394
|
# ================
|
|
351
395
|
|
|
352
|
-
def estimate_support(self, a_coeffs=None, n_scan=4000):
|
|
396
|
+
def estimate_support(self, a_coeffs=None, scan_range=None, n_scan=4000):
|
|
353
397
|
"""
|
|
354
398
|
"""
|
|
355
399
|
|
|
@@ -360,7 +404,10 @@ class AlgebraicForm(object):
|
|
|
360
404
|
a_coeffs = self.a_coeffs
|
|
361
405
|
|
|
362
406
|
# Inflate a bit to make sure all points are searched
|
|
363
|
-
|
|
407
|
+
if scan_range is not None:
|
|
408
|
+
x_min, x_max = scan_range
|
|
409
|
+
else:
|
|
410
|
+
x_min, x_max = self._inflate_broad_support(inflate=0.2)
|
|
364
411
|
|
|
365
412
|
est_support, info = compute_support(a_coeffs, x_min=x_min, x_max=x_max,
|
|
366
413
|
n_scan=n_scan)
|
|
@@ -371,7 +418,7 @@ class AlgebraicForm(object):
|
|
|
371
418
|
# estimate branch points
|
|
372
419
|
# ======================
|
|
373
420
|
|
|
374
|
-
def estimate_branch_points(self):
|
|
421
|
+
def estimate_branch_points(self, tol=1e-15, real_tol=None):
|
|
375
422
|
"""
|
|
376
423
|
Compute global branch points and zeros of leading a_j
|
|
377
424
|
"""
|
|
@@ -380,7 +427,7 @@ class AlgebraicForm(object):
|
|
|
380
427
|
raise RuntimeError('Call "fit" first.')
|
|
381
428
|
|
|
382
429
|
bp, leading_zeros, info = compute_branch_points(
|
|
383
|
-
self.a_coeffs, tol=
|
|
430
|
+
self.a_coeffs, tol=tol, real_tol=real_tol)
|
|
384
431
|
|
|
385
432
|
return bp, leading_zeros, info
|
|
386
433
|
|
|
@@ -460,7 +507,8 @@ class AlgebraicForm(object):
|
|
|
460
507
|
x = self._generate_grid(1.25)
|
|
461
508
|
|
|
462
509
|
# Preallocate density to zero
|
|
463
|
-
|
|
510
|
+
z = x.astype(complex) + 1j * self.delta
|
|
511
|
+
rho = self._stieltjes(z).imag / numpy.pi
|
|
464
512
|
|
|
465
513
|
if plot:
|
|
466
514
|
plot_density(x, rho, eig=self.eig, support=self.broad_support,
|
|
@@ -654,10 +702,10 @@ class AlgebraicForm(object):
|
|
|
654
702
|
# ==========
|
|
655
703
|
|
|
656
704
|
def decompress(self, size, x=None, method='one', plot=False, latex=False,
|
|
657
|
-
save=False, verbose=False,
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
705
|
+
save=False, verbose=False, min_n_times=10,
|
|
706
|
+
newton_opt={'max_iter': 50, 'tol': 1e-12, 'armijo': 1e-4,
|
|
707
|
+
'min_lam': 1e-6, 'w_min': 1e-14,
|
|
708
|
+
'sweep': True}):
|
|
661
709
|
"""
|
|
662
710
|
Free decompression of spectral density.
|
|
663
711
|
"""
|
|
@@ -704,7 +752,7 @@ class AlgebraicForm(object):
|
|
|
704
752
|
# Ensure there are at least min_n_times time t, including requested
|
|
705
753
|
# times, and especially time t = 0
|
|
706
754
|
t_all, idx_req = build_time_grid(
|
|
707
|
-
size, self.n,
|
|
755
|
+
size, self.n, min_n_times=min_n_times)
|
|
708
756
|
|
|
709
757
|
# Evolve
|
|
710
758
|
W, ok = decompress_newton(
|
|
@@ -726,9 +774,13 @@ class AlgebraicForm(object):
|
|
|
726
774
|
|
|
727
775
|
# Decompress to each alpha
|
|
728
776
|
for i in range(alpha.size):
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
777
|
+
t_i = numpy.log(alpha[i])
|
|
778
|
+
coeffs_i = decompress_coeffs(self.a_coeffs, t_i)
|
|
779
|
+
|
|
780
|
+
def mom(k):
|
|
781
|
+
return self.moments(k, t_i)
|
|
782
|
+
|
|
783
|
+
stieltjes_i = StieltjesPoly(coeffs_i, mom)
|
|
732
784
|
rho[i, :] = stieltjes_i(x).imag
|
|
733
785
|
|
|
734
786
|
rho = rho / numpy.pi
|
|
@@ -751,6 +803,49 @@ class AlgebraicForm(object):
|
|
|
751
803
|
|
|
752
804
|
return rho, x
|
|
753
805
|
|
|
806
|
+
# ==========
|
|
807
|
+
# candidates
|
|
808
|
+
# ==========
|
|
809
|
+
|
|
810
|
+
def candidates(self, size, x=None, verbose=False):
|
|
811
|
+
|
|
812
|
+
# Check size argument
|
|
813
|
+
if numpy.isscalar(size):
|
|
814
|
+
size = int(size)
|
|
815
|
+
else:
|
|
816
|
+
# Check monotonic increment (either all increasing or decreasing)
|
|
817
|
+
diff = numpy.diff(size)
|
|
818
|
+
if not (numpy.all(diff >= 0) or numpy.all(diff <= 0)):
|
|
819
|
+
raise ValueError('"size" increment should be monotonic.')
|
|
820
|
+
|
|
821
|
+
# Decompression ratio equal to e^{t}.
|
|
822
|
+
alpha = numpy.atleast_1d(size) / self.n
|
|
823
|
+
|
|
824
|
+
# Lower and upper bound on new support
|
|
825
|
+
hilb_lb = \
|
|
826
|
+
(1.0 / self._stieltjes(self.lam_m + self.delta * 1j).item()).real
|
|
827
|
+
hilb_ub = \
|
|
828
|
+
(1.0 / self._stieltjes(self.lam_p + self.delta * 1j).item()).real
|
|
829
|
+
lb = self.lam_m - (numpy.max(alpha) - 1) * hilb_lb
|
|
830
|
+
ub = self.lam_p - (numpy.max(alpha) - 1) * hilb_ub
|
|
831
|
+
|
|
832
|
+
# Create x if not given
|
|
833
|
+
if x is None:
|
|
834
|
+
radius = 0.5 * (ub - lb)
|
|
835
|
+
center = 0.5 * (ub + lb)
|
|
836
|
+
scale = 1.25
|
|
837
|
+
x_min = numpy.floor(center - radius * scale)
|
|
838
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
839
|
+
x = numpy.linspace(x_min, x_max, 2000)
|
|
840
|
+
else:
|
|
841
|
+
x = numpy.asarray(x)
|
|
842
|
+
|
|
843
|
+
for i in range(alpha.size):
|
|
844
|
+
t_i = numpy.log(alpha[i])
|
|
845
|
+
coeffs_i = decompress_coeffs(self.a_coeffs, t_i)
|
|
846
|
+
plot_candidates(coeffs_i, x, size=int(alpha[i]*self.n),
|
|
847
|
+
verbose=verbose)
|
|
848
|
+
|
|
754
849
|
# ====
|
|
755
850
|
# edge
|
|
756
851
|
# ====
|
|
@@ -759,6 +854,9 @@ class AlgebraicForm(object):
|
|
|
759
854
|
verbose=False):
|
|
760
855
|
"""
|
|
761
856
|
Evolves spectral edges.
|
|
857
|
+
|
|
858
|
+
Fix: if t is a scalar or length-1 array, we prepend t=0 internally so
|
|
859
|
+
evolve_edges actually advances from the initialization at t=0.
|
|
762
860
|
"""
|
|
763
861
|
|
|
764
862
|
if self.support is not None:
|
|
@@ -768,18 +866,53 @@ class AlgebraicForm(object):
|
|
|
768
866
|
else:
|
|
769
867
|
raise RuntimeError('Call "fit" first.')
|
|
770
868
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
869
|
+
t = numpy.asarray(t, dtype=float).ravel()
|
|
870
|
+
|
|
871
|
+
if t.size == 1:
|
|
872
|
+
t1 = float(t[0])
|
|
873
|
+
if t1 == 0.0:
|
|
874
|
+
t_grid = numpy.array([0.0], dtype=float)
|
|
875
|
+
complex_edges, ok_edges = evolve_edges(
|
|
876
|
+
t_grid, self.a_coeffs, support=known_support, eta=eta,
|
|
877
|
+
dt_max=dt_max, max_iter=max_iter, tol=tol
|
|
878
|
+
)
|
|
879
|
+
else:
|
|
880
|
+
# prepend 0 and drop it after evolution
|
|
881
|
+
t_grid = numpy.array([0.0, t1], dtype=float)
|
|
882
|
+
complex_edges2, ok_edges2 = evolve_edges(
|
|
883
|
+
t_grid, self.a_coeffs, support=known_support, eta=eta,
|
|
884
|
+
dt_max=dt_max, max_iter=max_iter, tol=tol
|
|
885
|
+
)
|
|
886
|
+
complex_edges = complex_edges2[-1:, :]
|
|
887
|
+
ok_edges = ok_edges2[-1:, :]
|
|
888
|
+
else:
|
|
889
|
+
# For vector t, require it starts at 0 for correct initialization
|
|
890
|
+
# (you can relax this if you want by prepending 0 similarly).
|
|
891
|
+
complex_edges, ok_edges = evolve_edges(
|
|
892
|
+
t, self.a_coeffs, support=known_support, eta=eta,
|
|
893
|
+
dt_max=dt_max, max_iter=max_iter, tol=tol
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
real_edges = complex_edges.real
|
|
775
897
|
|
|
776
|
-
# Remove spurious edges
|
|
777
|
-
|
|
898
|
+
# Remove spurious edges / merges for plotting
|
|
899
|
+
real_merged_edges, active_k = merge_edges(real_edges, tol=1e-4)
|
|
778
900
|
|
|
779
901
|
if verbose:
|
|
780
902
|
print("edge success rate:", ok_edges.mean())
|
|
781
903
|
|
|
782
|
-
return
|
|
904
|
+
return complex_edges, real_merged_edges, active_k
|
|
905
|
+
|
|
906
|
+
# ====
|
|
907
|
+
# cusp
|
|
908
|
+
# ====
|
|
909
|
+
|
|
910
|
+
def cusp(self, t_grid):
|
|
911
|
+
"""
|
|
912
|
+
"""
|
|
913
|
+
|
|
914
|
+
return cusp_wrap(self, t_grid, edge_kwargs=None, max_iter=50,
|
|
915
|
+
tol=1.0e-12)
|
|
783
916
|
|
|
784
917
|
# ========
|
|
785
918
|
# eigvalsh
|