freealg 0.7.16__py3-none-any.whl → 0.7.18__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.
Files changed (52) hide show
  1. freealg/__init__.py +8 -6
  2. freealg/__version__.py +1 -1
  3. freealg/_algebraic_form/_branch_points.py +18 -18
  4. freealg/_algebraic_form/_continuation_algebraic.py +13 -13
  5. freealg/_algebraic_form/_cusp.py +15 -15
  6. freealg/_algebraic_form/_cusp_wrap.py +6 -6
  7. freealg/_algebraic_form/_decompress.py +16 -16
  8. freealg/_algebraic_form/_decompress4.py +31 -31
  9. freealg/_algebraic_form/_decompress5.py +23 -23
  10. freealg/_algebraic_form/_decompress6.py +13 -13
  11. freealg/_algebraic_form/_decompress7.py +15 -15
  12. freealg/_algebraic_form/_decompress8.py +17 -17
  13. freealg/_algebraic_form/_decompress9.py +18 -18
  14. freealg/_algebraic_form/_decompress_new.py +17 -17
  15. freealg/_algebraic_form/_decompress_new_2.py +57 -57
  16. freealg/_algebraic_form/_decompress_util.py +10 -10
  17. freealg/_algebraic_form/_decompressible.py +292 -0
  18. freealg/_algebraic_form/_edge.py +10 -10
  19. freealg/_algebraic_form/_homotopy4.py +9 -9
  20. freealg/_algebraic_form/_homotopy5.py +9 -9
  21. freealg/_algebraic_form/_support.py +19 -19
  22. freealg/_algebraic_form/algebraic_form.py +262 -468
  23. freealg/_base_form.py +401 -0
  24. freealg/_free_form/__init__.py +1 -4
  25. freealg/_free_form/_density_util.py +1 -1
  26. freealg/_free_form/_plot_util.py +3 -511
  27. freealg/_free_form/free_form.py +8 -367
  28. freealg/_util.py +59 -11
  29. freealg/distributions/__init__.py +2 -1
  30. freealg/distributions/_base_distribution.py +163 -0
  31. freealg/distributions/_chiral_block.py +137 -11
  32. freealg/distributions/_compound_poisson.py +168 -64
  33. freealg/distributions/_deformed_marchenko_pastur.py +137 -88
  34. freealg/distributions/_deformed_wigner.py +92 -40
  35. freealg/distributions/_fuss_catalan.py +269 -0
  36. freealg/distributions/_kesten_mckay.py +4 -130
  37. freealg/distributions/_marchenko_pastur.py +8 -196
  38. freealg/distributions/_meixner.py +4 -130
  39. freealg/distributions/_wachter.py +4 -130
  40. freealg/distributions/_wigner.py +10 -127
  41. freealg/visualization/__init__.py +2 -2
  42. freealg/visualization/{_rgb_hsv.py → _domain_coloring.py} +37 -29
  43. freealg/visualization/_plot_util.py +513 -0
  44. {freealg-0.7.16.dist-info → freealg-0.7.18.dist-info}/METADATA +1 -1
  45. freealg-0.7.18.dist-info/RECORD +74 -0
  46. freealg-0.7.16.dist-info/RECORD +0 -69
  47. /freealg/{_free_form/_sample.py → _sample.py} +0 -0
  48. /freealg/{_free_form/_support.py → _support.py} +0 -0
  49. {freealg-0.7.16.dist-info → freealg-0.7.18.dist-info}/WHEEL +0 -0
  50. {freealg-0.7.16.dist-info → freealg-0.7.18.dist-info}/licenses/AUTHORS.txt +0 -0
  51. {freealg-0.7.16.dist-info → freealg-0.7.18.dist-info}/licenses/LICENSE.txt +0 -0
  52. {freealg-0.7.16.dist-info → freealg-0.7.18.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,292 @@
1
+ # SPDX-FileCopyrightText: Copyright 2026, Siavash Ameli <sameli@berkeley.edu>
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+ # SPDX-FileType: SOURCE
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify it under
6
+ # the terms of the license found in the LICENSE.txt file in the root directory
7
+ # of this source tree.
8
+
9
+
10
+ # =======
11
+ # Imports
12
+ # =======
13
+
14
+ import numpy
15
+
16
+ __all__ = ['precheck_laurent']
17
+
18
+
19
+ # =====
20
+ # L add
21
+ # =====
22
+
23
+ def L_add(A, B):
24
+
25
+ C = dict(A)
26
+ for p, c in B.items():
27
+ C[p] = C.get(p, 0.0) + c
28
+ if abs(C[p]) == 0:
29
+ C.pop(p, None)
30
+ return C
31
+
32
+
33
+ # =======
34
+ # L scale
35
+ # =======
36
+
37
+ def L_scale(A, s):
38
+ return {p: s*c for p, c in A.items()}
39
+
40
+
41
+ # =====
42
+ # L mul
43
+ # =====
44
+
45
+ def L_mul(A, B, pmin, pmax):
46
+
47
+ C = {}
48
+ for pa, ca in A.items():
49
+ for pb, cb in B.items():
50
+ p = pa + pb
51
+ if p < pmin or p > pmax:
52
+ continue
53
+ C[p] = C.get(p, 0.0) + ca*cb
54
+
55
+ return C
56
+
57
+
58
+ # =====
59
+ # L pow
60
+ # =====
61
+
62
+ def L_pow(A, k, pmin, pmax):
63
+
64
+ if k == 0:
65
+ return {0: 1.0+0.0j}
66
+ if k == 1:
67
+ return dict(A)
68
+ # fast exponentiation
69
+ out = {0: 1.0+0.0j}
70
+ base = dict(A)
71
+ e = k
72
+ while e > 0:
73
+ if e & 1:
74
+ out = L_mul(out, base, pmin, pmax)
75
+ e >>= 1
76
+ if e:
77
+ base = L_mul(base, base, pmin, pmax)
78
+ return out
79
+
80
+
81
+ # ============
82
+ # L inv series
83
+ # ============
84
+
85
+ def L_inv_series(A, pmin, pmax):
86
+ """
87
+ Invert a *power series* around power 0: requires A[0] != 0 and no negative
88
+ powers.
89
+
90
+ Returns power series B with nonnegative powers only.
91
+ """
92
+
93
+ if any(p < 0 for p in A.keys()):
94
+ raise ValueError("L_inv_series expects no negative powers")
95
+
96
+ a0 = A.get(0, 0.0)
97
+ if abs(a0) < 1e-18:
98
+ raise ValueError("Need nonzero constant term to invert")
99
+
100
+ # compute up to pmax (nonnegative)
101
+ B = {0: 1.0/a0}
102
+ for n in range(1, pmax+1):
103
+ s = 0.0+0.0j
104
+ for k in range(1, n+1):
105
+ s += A.get(k, 0.0) * B.get(n-k, 0.0)
106
+ B[n] = -s/a0
107
+
108
+ # clip
109
+ B = {p: c for p, c in B.items() if 0 <= p <= pmax}
110
+ return B
111
+
112
+
113
+ # ==================
114
+ # Ptau Laurent alpha
115
+ # ==================
116
+
117
+ def Ptau_Laurent_alpha(a, tau, alpha, mu, pmin, pmax):
118
+ """
119
+ Build Laurent series of P_tau(z,m) at z=1/w,
120
+ m(w)=-(alpha w + mu1 w^2 + ...).
121
+
122
+ Returns dict power->coeff for powers in [pmin,pmax].
123
+ """
124
+
125
+ # m(w)
126
+ m = {1: -(alpha+0.0j)}
127
+ for k, muk in enumerate(mu, start=1):
128
+ m[k+1] = -(muk+0.0j)
129
+
130
+ # y = tau m
131
+ y = L_scale(m, tau)
132
+
133
+ # m = w*m1(w) with m1(w) = -(alpha + mu1 w + mu2 w^2 + ...)
134
+ m1 = {0: -(alpha+0.0j)}
135
+ for k, muk in enumerate(mu, start=1):
136
+ m1[k] = -(muk+0.0j)
137
+
138
+ inv_m1 = L_inv_series(m1, pmin=0, pmax=max(0, pmax))
139
+
140
+ # 1/m = w^{-1} * inv_m1
141
+ inv_m = {p-1: c for p, c in inv_m1.items() if (p-1) >= pmin}
142
+
143
+ # z = 1/w
144
+ z = {-1: 1.0+0.0j}
145
+
146
+ c = (1.0 - 1.0/tau)
147
+ zeta = L_add(z, L_scale(inv_m, c)) # z + c*(1/m)
148
+
149
+ deg_z = a.shape[0]-1
150
+ deg_m = a.shape[1]-1
151
+
152
+ out = {}
153
+ zeta_pows = [L_pow(zeta, i, pmin, pmax) for i in range(deg_z+1)]
154
+ y_pows = [L_pow(y, j, pmin, pmax) for j in range(deg_m+1)]
155
+
156
+ for i in range(deg_z+1):
157
+ for j in range(deg_m+1):
158
+ coeff = a[i, j]
159
+ if abs(coeff) < 1e-18:
160
+ continue
161
+ term = L_mul(zeta_pows[i], y_pows[j], pmin, pmax)
162
+ out = L_add(out, L_scale(term, coeff))
163
+
164
+ return out
165
+
166
+
167
+ # ======================
168
+ # solve laurent alpha ls
169
+ # ======================
170
+
171
+ def solve_laurent_alpha_ls(a, tau, K=8, L=None, max_iter=40, tol_step=1e-12):
172
+ """
173
+ Solve for x = [alpha, mu1..muK] so that Laurent coeffs vanish
174
+ for powers p in [-L, ..., K]. Uses LS on both Re and Im parts (robust).
175
+ """
176
+
177
+ deg_z = a.shape[0]-1
178
+ if L is None:
179
+ L = deg_z + 2
180
+
181
+ pmin, pmax = -L, K
182
+ powers = list(range(pmin, pmax+1))
183
+
184
+ # unknowns: alpha + mu1..muK
185
+ x = numpy.zeros(K+1, dtype=numpy.float64)
186
+ x[0] = 1.0 # alpha init
187
+
188
+ def build_out(xx):
189
+ alpha = float(xx[0])
190
+ mu = xx[1:]
191
+ return Ptau_Laurent_alpha(a, tau, alpha, mu, pmin, pmax)
192
+
193
+ # LS Newton / Gauss-Newton
194
+ for it in range(max_iter):
195
+ out = build_out(x)
196
+
197
+ # complex residual vector r_p = coeff(p)
198
+ r = numpy.array([out.get(p, 0.0+0.0j) for p in powers],
199
+ dtype=numpy.complex128)
200
+
201
+ # stack real+imag (this is the key fix)
202
+ F = numpy.concatenate([r.real, r.imag], axis=0)
203
+ nrm = numpy.linalg.norm(F)
204
+
205
+ if nrm < 1e-12:
206
+ return x, True, out, powers
207
+
208
+ # Jacobian by FD
209
+ eps = 1e-6
210
+ J = numpy.zeros((F.size, x.size), dtype=numpy.float64)
211
+ for k in range(x.size):
212
+ x2 = x.copy()
213
+ x2[k] += eps
214
+ out2 = build_out(x2)
215
+ r2 = numpy.array([out2.get(p, 0.0+0.0j) for p in powers],
216
+ dtype=numpy.complex128)
217
+ F2 = numpy.concatenate([r2.real, r2.imag], axis=0)
218
+ J[:, k] = (F2 - F) / eps
219
+
220
+ # LS step
221
+ dx, *_ = numpy.linalg.lstsq(J, -F, rcond=None)
222
+
223
+ if numpy.linalg.norm(dx) < tol_step:
224
+ return x, False, out, powers
225
+
226
+ x += dx
227
+
228
+ return x, False, out, powers
229
+
230
+
231
+ # ================
232
+ # precheck laurent
233
+ # ================
234
+
235
+ def precheck_laurent(a, tau, K_list=(6, 8, 10), L=3, tol=1e-8, verbose=True):
236
+ """
237
+ For fixed tau, try several K and pick the best (smallest max |coeff|).
238
+ Returns dict with bestK, alpha, max_abs, ok, and per-K details.
239
+ """
240
+
241
+ deg_z = a.shape[0]-1
242
+ if L is None:
243
+ L = deg_z + 2
244
+
245
+ best = None
246
+ perK = []
247
+
248
+ for K in K_list:
249
+ x, ok_solve, out, powers = solve_laurent_alpha_ls(a, tau, K=K, L=L)
250
+ alpha = x[0]
251
+ coeffs = numpy.array([out.get(p, 0.0+0.0j) for p in powers],
252
+ dtype=numpy.complex128)
253
+ max_abs = float(numpy.max(numpy.abs(coeffs)))
254
+ worst_p = powers[int(numpy.argmax(numpy.abs(coeffs)))]
255
+ perK.append((K, alpha, max_abs, worst_p, ok_solve))
256
+
257
+ if best is None or max_abs < best["max_abs"]:
258
+ best = {
259
+ "K": K,
260
+ "alpha": alpha,
261
+ "max_abs": max_abs,
262
+ "worst_p": worst_p,
263
+ "ok_solve": ok_solve
264
+ }
265
+
266
+ alphas = numpy.array([row[1] for row in perK], dtype=float) # alpha per K
267
+ alpha_std = float(numpy.std(alphas))
268
+ alpha_span = float(numpy.max(alphas) - numpy.min(alphas))
269
+
270
+ ok = (best["max_abs"] <= tol) and best["ok_solve"]
271
+ ok = ok and (alpha_std < 1e-3) # or use alpha_span < 3e-3
272
+
273
+ if verbose:
274
+ print(f"--- tau={tau} --- ok={ok} bestK={best['K']} "
275
+ f"max_abs={best['max_abs']:.3e} "
276
+ f"alpha={best['alpha']:.12g} worst_p={best['worst_p']}")
277
+
278
+ print(f" alpha_std={alpha_std:.3e}, alpha_span={alpha_span:.3e}, "
279
+ f"alphas={alphas}")
280
+
281
+ for (K, alpha, max_abs, worst_p, ok_solve) in perK:
282
+ print(f" K={K:2d} max_abs={max_abs:.3e} worst_p={worst_p:2d} "
283
+ f"alpha={alpha:.12g} solve_ok={ok_solve}")
284
+
285
+ res = {
286
+ "best": best,
287
+ "perK": perK,
288
+ "alpha_std": alpha_std,
289
+ "alpha_span": alpha_span
290
+ }
291
+
292
+ return ok, res
@@ -22,7 +22,7 @@ __all__ = ['evolve_edges', 'merge_edges']
22
22
  # edge newton step
23
23
  # ================
24
24
 
25
- def _edge_newton_step(t, zeta, y, a_coeffs, max_iter=30, tol=1e-12):
25
+ def _edge_newton_step(t, zeta, y, coeffs, max_iter=30, tol=1e-12):
26
26
  """
27
27
  """
28
28
 
@@ -30,7 +30,7 @@ def _edge_newton_step(t, zeta, y, a_coeffs, max_iter=30, tol=1e-12):
30
30
  c = tau - 1.0
31
31
 
32
32
  for _ in range(max_iter):
33
- P, Pz, Py = eval_P_partials(zeta, y, a_coeffs)
33
+ P, Pz, Py = eval_P_partials(zeta, y, coeffs)
34
34
 
35
35
  # F1 = P(zeta,y)
36
36
  F1 = complex(P)
@@ -45,11 +45,11 @@ def _edge_newton_step(t, zeta, y, a_coeffs, max_iter=30, tol=1e-12):
45
45
  eps_z = 1e-8 * (1.0 + abs(zeta))
46
46
  eps_y = 1e-8 * (1.0 + abs(y))
47
47
 
48
- Pp, Pzp, Pyp = eval_P_partials(zeta + eps_z, y, a_coeffs)
48
+ Pp, Pzp, Pyp = eval_P_partials(zeta + eps_z, y, coeffs)
49
49
  F1_zp = (complex(Pp) - F1) / eps_z
50
50
  F2_zp = (complex((y * y) * Pyp - c * Pzp) - F2) / eps_z
51
51
 
52
- Pp, Pzp, Pyp = eval_P_partials(zeta, y + eps_y, a_coeffs)
52
+ Pp, Pzp, Pyp = eval_P_partials(zeta, y + eps_y, coeffs)
53
53
  F1_yp = (complex(Pp) - F1) / eps_y
54
54
  F2_yp = (complex(((y + eps_y) * (y + eps_y)) * Pyp - c * Pzp) - F2) / \
55
55
  eps_y
@@ -102,7 +102,7 @@ def _pick_physical_root(z, roots):
102
102
  # init edge point from support
103
103
  # ============================
104
104
 
105
- def _init_edge_point_from_support(x_edge, a_coeffs, eta=1e-3):
105
+ def _init_edge_point_from_support(x_edge, coeffs, eta=1e-3):
106
106
  """
107
107
  Initialize (zeta,y) at t=0 for an edge near x_edge.
108
108
 
@@ -111,7 +111,7 @@ def _init_edge_point_from_support(x_edge, a_coeffs, eta=1e-3):
111
111
  """
112
112
 
113
113
  z = complex(x_edge + 1j * eta)
114
- roots = eval_roots(numpy.array([z]), a_coeffs)[0]
114
+ roots = eval_roots(numpy.array([z]), coeffs)[0]
115
115
  y = _pick_physical_root(z, roots)
116
116
 
117
117
  # Move zeta to real axis as initial guess
@@ -119,7 +119,7 @@ def _init_edge_point_from_support(x_edge, a_coeffs, eta=1e-3):
119
119
 
120
120
  # Refine zeta,y to satisfy P=0 and Py=0 at t=0 (branch point)
121
121
  # This uses the same Newton system with c=0, i.e. F2 = y^2 Py.
122
- zeta, y, ok = _edge_newton_step(0.0, zeta, y, a_coeffs, max_iter=50,
122
+ zeta, y, ok = _edge_newton_step(0.0, zeta, y, coeffs, max_iter=50,
123
123
  tol=1e-10)
124
124
 
125
125
  return zeta, y, ok
@@ -131,7 +131,7 @@ def _init_edge_point_from_support(x_edge, a_coeffs, eta=1e-3):
131
131
 
132
132
  def evolve_edges(
133
133
  t_grid,
134
- a_coeffs,
134
+ coeffs,
135
135
  support=None,
136
136
  eta=1e-3,
137
137
  dt_max=0.1,
@@ -185,7 +185,7 @@ def evolve_edges(
185
185
  y = numpy.empty(m, dtype=numpy.complex128)
186
186
 
187
187
  for j in range(m):
188
- z0, y0, ok0 = _init_edge_point_from_support(endpoints0[j], a_coeffs,
188
+ z0, y0, ok0 = _init_edge_point_from_support(endpoints0[j], coeffs,
189
189
  eta=eta)
190
190
  zeta[j] = z0
191
191
  y[j] = y0
@@ -210,7 +210,7 @@ def evolve_edges(
210
210
  t = t0 + dt * (ks / float(n_sub))
211
211
  for j in range(m):
212
212
  zeta[j], y[j], okj = _edge_newton_step(
213
- t, zeta[j], y[j], a_coeffs, max_iter=max_iter, tol=tol
213
+ t, zeta[j], y[j], coeffs, max_iter=max_iter, tol=tol
214
214
  )
215
215
  ok[it, j] = okj
216
216
 
@@ -23,13 +23,13 @@ __all__ = ["StieltjesPoly"]
23
23
  # Poly -> roots in m for z
24
24
  # =========================
25
25
 
26
- def _poly_m_coeffs(a_coeffs, z):
26
+ def _poly_m_coeffs(coeffs, z):
27
27
  """Return coefficients b_j for \sum_j b_j m^j = 0 at fixed z.
28
28
 
29
- a_coeffs[i,j] is coeff of z^i m^j.
29
+ coeffs[i,j] is coeff of z^i m^j.
30
30
  Returns b of length (deg_m+1) with b[j] = \sum_i a[i,j] z^i.
31
31
  """
32
- a = numpy.asarray(a_coeffs, dtype=numpy.complex128)
32
+ a = numpy.asarray(coeffs, dtype=numpy.complex128)
33
33
  deg_z = a.shape[0] - 1
34
34
  deg_m = a.shape[1] - 1
35
35
 
@@ -47,9 +47,9 @@ def _poly_m_coeffs(a_coeffs, z):
47
47
  return b
48
48
 
49
49
 
50
- def _roots_m(a_coeffs, z):
50
+ def _roots_m(coeffs, z):
51
51
  """All algebraic roots in m at fixed z."""
52
- b = _poly_m_coeffs(a_coeffs, z)
52
+ b = _poly_m_coeffs(coeffs, z)
53
53
 
54
54
  # Drop leading zeros in highest power to keep numpy.roots stable
55
55
  # numpy.roots expects highest degree first.
@@ -239,9 +239,9 @@ def _viterbi_track(z_list, roots_list, mL, mR, *,
239
239
  class StieltjesPoly(object):
240
240
  """Callable m(z) for P(z,m)=0 using robust branch selection."""
241
241
 
242
- def __init__(self, a_coeffs, *,
242
+ def __init__(self, coeffs, *,
243
243
  viterbi_opt=None):
244
- self.a_coeffs = numpy.asarray(a_coeffs, dtype=numpy.complex128)
244
+ self.coeffs = numpy.asarray(coeffs, dtype=numpy.complex128)
245
245
  self.viterbi_opt = dict(viterbi_opt or {})
246
246
 
247
247
  # ----------
@@ -249,7 +249,7 @@ class StieltjesPoly(object):
249
249
  # ----------
250
250
 
251
251
  def evaluate_scalar(self, z, target=None):
252
- r = _roots_m(self.a_coeffs, z)
252
+ r = _roots_m(self.coeffs, z)
253
253
  return _pick_physical_root_scalar(z, r, target=target)
254
254
 
255
255
  # ---------------
@@ -267,7 +267,7 @@ class StieltjesPoly(object):
267
267
  z_list = z.ravel()
268
268
 
269
269
  # roots for each point
270
- roots_list = [_roots_m(self.a_coeffs, zi) for zi in z_list]
270
+ roots_list = [_roots_m(self.coeffs, zi) for zi in z_list]
271
271
 
272
272
  # boundary anchors via scalar selection
273
273
  mL = self.evaluate_scalar(z_list[0])
@@ -8,8 +8,8 @@
8
8
  import numpy
9
9
 
10
10
 
11
- def _poly_coeffs_in_m(a_coeffs, z):
12
- a = a_coeffs
11
+ def _poly_coeffs_in_m(coeffs, z):
12
+ a = coeffs
13
13
  dz = a.shape[0] - 1
14
14
  s = a.shape[1] - 1
15
15
  zp = numpy.array([z**i for i in range(dz + 1)], dtype=numpy.complex128)
@@ -19,8 +19,8 @@ def _poly_coeffs_in_m(a_coeffs, z):
19
19
  return coeff_m
20
20
 
21
21
 
22
- def _roots_m(a_coeffs, z):
23
- coeff_m = _poly_coeffs_in_m(a_coeffs, z)
22
+ def _roots_m(coeffs, z):
23
+ coeff_m = _poly_coeffs_in_m(coeffs, z)
24
24
  c = coeff_m[::-1]
25
25
  while c.size > 1 and numpy.abs(c[0]) == 0:
26
26
  c = c[1:]
@@ -96,12 +96,12 @@ def _viterbi_1d(z_list, roots_all, *, lam_space, lam_asym,
96
96
  class StieltjesPoly(object):
97
97
  """Callable m(z) for P(z,m)=0 using robust branch selection."""
98
98
 
99
- def __init__(self, a_coeffs, *, viterbi_opt=None):
100
- self.a_coeffs = numpy.asarray(a_coeffs, dtype=numpy.complex128)
99
+ def __init__(self, coeffs, *, viterbi_opt=None):
100
+ self.coeffs = numpy.asarray(coeffs, dtype=numpy.complex128)
101
101
  self.viterbi_opt = dict(viterbi_opt or {})
102
102
 
103
103
  def evaluate_scalar(self, z, target=None):
104
- r = _roots_m(self.a_coeffs, z)
104
+ r = _roots_m(self.coeffs, z)
105
105
  if r.size == 0:
106
106
  return numpy.nan + 1j * numpy.nan
107
107
  tol_im = float(self.viterbi_opt.get("tol_im", 1e-14))
@@ -123,11 +123,11 @@ class StieltjesPoly(object):
123
123
 
124
124
  if z.ndim == 1 and z.size >= 2:
125
125
  z_list = z.ravel()
126
- s = self.a_coeffs.shape[1] - 1
126
+ s = self.coeffs.shape[1] - 1
127
127
  roots_all = numpy.empty((z_list.size, s), dtype=numpy.complex128)
128
128
  ok_all = numpy.ones(z_list.size, dtype=bool)
129
129
  for k in range(z_list.size):
130
- r = _roots_m(self.a_coeffs, z_list[k])
130
+ r = _roots_m(self.coeffs, z_list[k])
131
131
  if r.size != s:
132
132
  ok_all[k] = False
133
133
  if r.size == 0:
@@ -15,18 +15,18 @@ import numpy
15
15
  import numpy.polynomial.polynomial as poly
16
16
  from ._homotopy5 import StieltjesPoly
17
17
 
18
- __all__ = ['compute_support']
18
+ __all__ = ['estimate_support']
19
19
 
20
20
 
21
21
  # =====================
22
22
  # poly coeffs in m at z
23
23
  # =====================
24
24
 
25
- def _poly_coeffs_in_m_at_z(a_coeffs, z):
26
- s = a_coeffs.shape[1] - 1
25
+ def _poly_coeffs_in_m_at_z(coeffs, z):
26
+ s = coeffs.shape[1] - 1
27
27
  a = numpy.empty(s + 1, dtype=numpy.complex128)
28
28
  for j in range(s + 1):
29
- a[j] = poly.polyval(z, a_coeffs[:, j])
29
+ a[j] = poly.polyval(z, coeffs[:, j])
30
30
  return a
31
31
 
32
32
 
@@ -34,14 +34,14 @@ def _poly_coeffs_in_m_at_z(a_coeffs, z):
34
34
  # P and partials
35
35
  # ==============
36
36
 
37
- def _P_and_partials(a_coeffs, z, m):
38
- s = a_coeffs.shape[1] - 1
37
+ def _P_and_partials(coeffs, z, m):
38
+ s = coeffs.shape[1] - 1
39
39
 
40
40
  a = numpy.empty(s + 1, dtype=numpy.complex128)
41
41
  da = numpy.empty(s + 1, dtype=numpy.complex128)
42
42
  for j in range(s + 1):
43
- a[j] = poly.polyval(z, a_coeffs[:, j])
44
- da[j] = poly.polyval(z, poly.polyder(a_coeffs[:, j]))
43
+ a[j] = poly.polyval(z, coeffs[:, j])
44
+ da[j] = poly.polyval(z, poly.polyder(coeffs[:, j]))
45
45
 
46
46
  mpow = 1.0 + 0.0j
47
47
  P = 0.0 + 0.0j
@@ -67,13 +67,13 @@ def _P_and_partials(a_coeffs, z, m):
67
67
  # newton edge
68
68
  # ===========
69
69
 
70
- def _newton_edge(a_coeffs, x0, m0, tol=1e-12, max_iter=50):
70
+ def _newton_edge(coeffs, x0, m0, tol=1e-12, max_iter=50):
71
71
  x = float(x0)
72
72
  m = float(m0)
73
73
 
74
74
  for _ in range(max_iter):
75
75
  z = x + 0.0j
76
- P, Pz, Pm, Pzm, Pmm = _P_and_partials(a_coeffs, z, m)
76
+ P, Pz, Pm, Pzm, Pmm = _P_and_partials(coeffs, z, m)
77
77
 
78
78
  f0 = float(numpy.real(P))
79
79
  f1 = float(numpy.real(Pm))
@@ -157,12 +157,12 @@ def _bisect_edge(stieltjes_poly, x_lo, x_hi, eta, im_thr, max_iter=60):
157
157
 
158
158
 
159
159
  # ===============
160
- # compute support
160
+ # estimate support
161
161
  # ===============
162
162
 
163
- def compute_support(a_coeffs, x_min, x_max, n_scan=4000, **kwargs):
163
+ def estimate_support(coeffs, x_min, x_max, n_scan=4000, **kwargs):
164
164
 
165
- a_coeffs = numpy.asarray(a_coeffs, dtype=numpy.complex128)
165
+ coeffs = numpy.asarray(coeffs, dtype=numpy.complex128)
166
166
 
167
167
  x_min = float(x_min)
168
168
  x_max = float(x_max)
@@ -182,7 +182,7 @@ def compute_support(a_coeffs, x_min, x_max, n_scan=4000, **kwargs):
182
182
  'tol_im': 1e-14,
183
183
  }
184
184
  vopt.update(kwargs.get('viterbi_opt', {}) or {})
185
- stieltjes = StieltjesPoly(a_coeffs, viterbi_opt=vopt)
185
+ stieltjes = StieltjesPoly(coeffs, viterbi_opt=vopt)
186
186
 
187
187
  x_grid = numpy.linspace(x_min, x_max, n_scan)
188
188
  z_grid = x_grid + 1j * eta
@@ -234,18 +234,18 @@ def compute_support(a_coeffs, x_min, x_max, n_scan=4000, **kwargs):
234
234
  edges_ref = []
235
235
  for x0 in edges:
236
236
  m0 = float(numpy.real(stieltjes.evaluate_scalar(x0 + 1j * eta)))
237
- xe, _, ok = _newton_edge(a_coeffs, x0, m0, tol=newton_tol)
237
+ xe, _, ok = _newton_edge(coeffs, x0, m0, tol=newton_tol)
238
238
  edges_ref.append(float(xe)
239
239
  if ok and numpy.isfinite(xe) else float(x0))
240
240
  edges = _cluster_edges(edges_ref, edge_x_cluster_tol)
241
241
 
242
242
  edges.sort()
243
- support = []
243
+ est_supp = []
244
244
  for k in range(0, edges.size - 1, 2):
245
245
  a = float(edges[k])
246
246
  b = float(edges[k + 1])
247
247
  if b > a:
248
- support.append((a, b))
248
+ est_supp.append((a, b))
249
249
 
250
250
  info = {
251
251
  'x_grid': x_grid,
@@ -254,11 +254,11 @@ def compute_support(a_coeffs, x_min, x_max, n_scan=4000, **kwargs):
254
254
  'im_grid': im_grid,
255
255
  'im_thr': float(im_thr),
256
256
  'edges': edges,
257
- 'support': support,
257
+ 'est_supp': est_supp,
258
258
  'x_min': x_min,
259
259
  'x_max': x_max,
260
260
  'n_scan': n_scan,
261
261
  'scale': scale,
262
262
  }
263
263
 
264
- return support, info
264
+ return est_supp, info