freealg 0.7.9__py3-none-any.whl → 0.7.11__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.
@@ -0,0 +1,309 @@
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
+ import numpy.polynomial.polynomial as poly
16
+
17
+ __all__ = ['compute_support']
18
+
19
+
20
+ # ======================
21
+ # poly coeffs in m and z
22
+ # ======================
23
+
24
+ def _poly_coeffs_in_m_at_z(a_coeffs, z):
25
+
26
+ s = a_coeffs.shape[1] - 1
27
+ a = numpy.empty(s + 1, dtype=numpy.complex128)
28
+ for j in range(s + 1):
29
+ a[j] = poly.polyval(z, a_coeffs[:, j])
30
+ return a
31
+
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
+ # ==============
70
+ # P and partials
71
+ # ==============
72
+
73
+ def _P_and_partials(a_coeffs, z, m):
74
+
75
+ s = a_coeffs.shape[1] - 1
76
+
77
+ a = numpy.empty(s + 1, dtype=numpy.complex128)
78
+ da = numpy.empty(s + 1, dtype=numpy.complex128)
79
+ for j in range(s + 1):
80
+ a[j] = poly.polyval(z, a_coeffs[:, j])
81
+ da[j] = poly.polyval(z, poly.polyder(a_coeffs[:, j]))
82
+
83
+ mpow = 1.0 + 0.0j
84
+ P = 0.0 + 0.0j
85
+ Pz = 0.0 + 0.0j
86
+ for j in range(s + 1):
87
+ P += a[j] * mpow
88
+ Pz += da[j] * mpow
89
+ mpow *= m
90
+
91
+ Pm = 0.0 + 0.0j
92
+ Pmm = 0.0 + 0.0j
93
+ Pzm = 0.0 + 0.0j
94
+ for j in range(1, s + 1):
95
+ Pm += j * a[j] * (m ** (j - 1))
96
+ Pzm += j * da[j] * (m ** (j - 1))
97
+ for j in range(2, s + 1):
98
+ Pmm += j * (j - 1) * a[j] * (m ** (j - 2))
99
+
100
+ return P, Pz, Pm, Pzm, Pmm, a
101
+
102
+
103
+ # ===========
104
+ # newton edge
105
+ # ===========
106
+
107
+ def _newton_edge(a_coeffs, x0, m0, tol=1e-12, max_iter=50):
108
+
109
+ x = float(x0)
110
+ m = float(m0)
111
+
112
+ for _ in range(max_iter):
113
+ z = x + 0.0j
114
+ P, Pz, Pm, Pzm, Pmm, _ = _P_and_partials(a_coeffs, z, m)
115
+
116
+ f0 = float(numpy.real(P))
117
+ f1 = float(numpy.real(Pm))
118
+
119
+ j00 = float(numpy.real(Pz))
120
+ j01 = float(numpy.real(Pm))
121
+ j10 = float(numpy.real(Pzm))
122
+ j11 = float(numpy.real(Pmm))
123
+
124
+ det = j00 * j11 - j01 * j10
125
+ if det == 0.0 or (not numpy.isfinite(det)):
126
+ return x, m, False
127
+
128
+ dx = (-f0 * j11 + f1 * j01) / det
129
+ dm = (-j00 * f1 + j10 * f0) / det
130
+
131
+ x += dx
132
+ m += dm
133
+
134
+ if abs(dx) + abs(dm) < tol:
135
+ return x, m, True
136
+
137
+ return x, m, False
138
+
139
+
140
+ # =============
141
+ # cluster edges
142
+ # =============
143
+
144
+ def _cluster_edges(edges, x_tol):
145
+
146
+ if len(edges) == 0:
147
+ return numpy.array([], dtype=float)
148
+
149
+ edges = numpy.array(sorted(edges), dtype=float)
150
+ out = [edges[0]]
151
+ for e in edges[1:]:
152
+ if abs(e - out[-1]) > x_tol:
153
+ out.append(e)
154
+ return numpy.array(out, dtype=float)
155
+
156
+
157
+ # =======================
158
+ # pick physical root at z
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]
171
+
172
+ # optional strictness: if it violates Herglotz, declare failure
173
+ if not numpy.isfinite(w.real) or not numpy.isfinite(w.imag):
174
+ return w
175
+ if (im_sign * w.imag) <= 0.0:
176
+ return w
177
+
178
+ return w
179
+
180
+
181
+ # ===============
182
+ # compute support
183
+ # ===============
184
+
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])))
264
+
265
+ # Refine each seed by 2D Newton (x,m)
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))
271
+
272
+ edges = _cluster_edges(edges, edge_x_cluster_tol)
273
+ edges.sort()
274
+
275
+ # Build support by testing midpoints between consecutive real edges
276
+ support = []
277
+ m_im_tol = 1e-10
278
+
279
+ for i in range(edges.size - 1):
280
+ a = float(edges[i])
281
+ b = float(edges[i + 1])
282
+ if b <= a:
283
+ continue
284
+
285
+ xmid = 0.5 * (a + b)
286
+
287
+ # roots of P(xmid, m) with real coefficients
288
+ a_m = _poly_coeffs_in_m_at_z(a_coeffs, xmid + 0.0j)
289
+ r = _roots_poly_in_m(a_m, tol=root_tol)
290
+
291
+ # interval is support iff there exists a non-real root (complex pair)
292
+ if numpy.any(numpy.abs(numpy.imag(r)) > m_im_tol):
293
+ support.append((a, b))
294
+
295
+ info = {
296
+ "edges": edges,
297
+ "n_edges": int(edges.size),
298
+ "support": support,
299
+ "n_support": int(len(support)),
300
+ "x_grid": x_grid,
301
+ "rel": rel,
302
+ "thr": float(thr),
303
+ "x_min": float(x_min),
304
+ "x_max": float(x_max),
305
+ "n_scan": int(n_scan),
306
+ "y_eps": float(y_eps),
307
+ }
308
+
309
+ return support, info
@@ -18,10 +18,11 @@ 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 ._decompress import decompress_newton
21
+ from ._decompress import build_time_grid, decompress_newton
22
22
  from ._decompress2 import decompress_coeffs
23
23
  from ._homotopy import StieltjesPoly
24
- from ._discriminant import compute_singular_points
24
+ from ._branch_points import compute_branch_points
25
+ from ._support import compute_support
25
26
  from ._moments import MomentsESD
26
27
  from .._free_form._support import supp
27
28
  from .._free_form._plot_util import plot_density, plot_hilbert, plot_stieltjes
@@ -63,7 +64,7 @@ class AlgebraicForm(object):
63
64
  * ``'complex128'``: 128-bit complex numbers, equivalent of two double
64
65
  precision floating point.
65
66
  * ``'complex256'``: 256-bit complex numbers, equivalent of two long
66
- double precision floating point. This optino is only available on
67
+ double precision floating point. This option is only available on
67
68
  Linux machines.
68
69
 
69
70
  When using series acceleration methods (such as setting
@@ -133,9 +134,6 @@ class AlgebraicForm(object):
133
134
  # init
134
135
  # ====
135
136
 
136
- # def __init__(self, A, support=None, delta=1e-6, dtype='complex128',
137
- # **kwargs):
138
-
139
137
  def __init__(self, A, support=None, delta=1e-5, dtype='complex128',
140
138
  **kwargs):
141
139
  """
@@ -145,8 +143,9 @@ class AlgebraicForm(object):
145
143
  self.A = None
146
144
  self.eig = None
147
145
  self._stieltjes = None
148
- self.moments = None
146
+ self._moments = None
149
147
  self.support = support
148
+ self.est_support = None # Estimated from polynmial after fitting
150
149
  self.delta = delta # Offset above real axis to apply Plemelj formula
151
150
 
152
151
  # Data type for complex arrays
@@ -155,6 +154,7 @@ class AlgebraicForm(object):
155
154
  if hasattr(A, 'stieltjes') and callable(getattr(A, 'stieltjes', None)):
156
155
  # This is one of the distribution objects, like MarchenkoPastur
157
156
  self._stieltjes = A.stieltjes
157
+ self.support = A.support()
158
158
  self.n = 1
159
159
 
160
160
  elif callable(A):
@@ -180,18 +180,17 @@ class AlgebraicForm(object):
180
180
  # Use empirical Stieltjes function
181
181
  self._stieltjes = lambda z: \
182
182
  numpy.mean(1.0/(self.eig-z[:, numpy.newaxis]), axis=-1)
183
- self.moments = MomentsESD(self.eig)
183
+ self._moments = MomentsESD(self.eig) # NOTE (never used)
184
184
 
185
- # Support
186
- if support is None:
185
+ # broad support
186
+ if self.support is None:
187
187
  if self.eig is None:
188
188
  raise RuntimeError("Support must be provided without data")
189
+
189
190
  # Detect support
190
191
  self.lam_m, self.lam_p = supp(self.eig, **kwargs)
191
- self.support = [(self.lam_m, self.lam_p)]
192
- self.broad_support = self.support[0]
192
+ self.broad_support = (self.lam_m, self.lam_p)
193
193
  else:
194
- self.support = support
195
194
  self.lam_m = min([s[0] for s in self.support])
196
195
  self.lam_p = max([s[1] for s in self.support])
197
196
  self.broad_support = (self.lam_m, self.lam_p)
@@ -251,7 +250,16 @@ class AlgebraicForm(object):
251
250
  # self.cache.clear()
252
251
 
253
252
  z_fits = []
254
- for sup in self.support:
253
+
254
+ # Sampling around support, or broad_support. This is only needed to
255
+ # ensure sampled points are not hiting the support itself is not used
256
+ # in any computation. If support is not known, use broad support.
257
+ if self.support is not None:
258
+ possible_support = self.support
259
+ else:
260
+ possible_support = self.broad_support
261
+
262
+ for sup in possible_support:
255
263
  a, b = sup
256
264
 
257
265
  for i in range(len(r)):
@@ -261,7 +269,7 @@ class AlgebraicForm(object):
261
269
  z_fit = numpy.concatenate(z_fits)
262
270
 
263
271
  # Remove points too close to any cut
264
- z_fit = filter_z_away_from_cuts(z_fit, self.support, y_eps=y_eps,
272
+ z_fit = filter_z_away_from_cuts(z_fit, possible_support, y_eps=y_eps,
265
273
  x_pad=x_pad)
266
274
 
267
275
  # Fitting (w_inf = None means adaptive weight selection)
@@ -271,11 +279,11 @@ class AlgebraicForm(object):
271
279
  triangular=triangular, normalize=normalize, mu=mu,
272
280
  mu_reg=mu_reg)
273
281
 
274
- # Compute global branhc points, zeros of leading a_j, and support
275
- branch_points, a_s_zero, support = compute_singular_points(a_coeffs)
276
-
277
282
  self.a_coeffs = a_coeffs
278
283
 
284
+ # Estimate support from the fitted polynomial
285
+ self.est_support, _ = self.estimate_support(a_coeffs)
286
+
279
287
  # Reporting error
280
288
  P_res = numpy.abs(eval_P(z_fit, m1_fit, a_coeffs))
281
289
  res_max = numpy.max(P_res[numpy.isfinite(P_res)])
@@ -288,13 +296,11 @@ class AlgebraicForm(object):
288
296
  eta=max(y_eps, 1e-2), n_x=128,
289
297
  max_bad_frac=0.05)
290
298
 
291
- status['branch_points'] = branch_points
292
- status['a_s_zero'] = a_s_zero
293
299
  status['res_max'] = float(res_max)
294
300
  status['res_99_9'] = float(res_99_9)
295
301
  status['fit_metrics'] = fit_metrics
296
302
  self.status = status
297
- self._stieltjes = StieltjesPoly(self.a_coeffs)
303
+ self._stieltjes = StieltjesPoly(self.a_coeffs) # NOTE overwrite init
298
304
 
299
305
  if verbose:
300
306
  print(f'fit residual max : {res_max:>0.4e}')
@@ -319,7 +325,64 @@ class AlgebraicForm(object):
319
325
  else:
320
326
  print('\nStieltjes sanity check: OK')
321
327
 
322
- return a_coeffs, support, status
328
+ return a_coeffs, self.est_support, status
329
+
330
+ # =====================
331
+ # inflate broad support
332
+ # =====================
333
+
334
+ def _inflate_broad_support(self, inflate=0.0):
335
+ """
336
+ """
337
+
338
+ min_supp, max_supp = self.broad_support
339
+
340
+ c_supp = 0.5 * (max_supp + min_supp)
341
+ r_supp = 0.5 * (max_supp - min_supp)
342
+
343
+ x_min = c_supp - r_supp * (1.0 + inflate)
344
+ x_max = c_supp + r_supp * (1.0 + inflate)
345
+
346
+ return x_min, x_max
347
+
348
+ # ================
349
+ # estimate support
350
+ # ================
351
+
352
+ def estimate_support(self, a_coeffs=None, n_scan=4000):
353
+ """
354
+ """
355
+
356
+ if a_coeffs is None:
357
+ if self.a_coeffs is None:
358
+ raise RuntimeError('Call "fit" first.')
359
+ else:
360
+ a_coeffs = self.a_coeffs
361
+
362
+ # Inflate a bit to make sure all points are searched
363
+ x_min, x_max = self._inflate_broad_support(inflate=0.2)
364
+
365
+ est_support, info = compute_support(a_coeffs, x_min=x_min, x_max=x_max,
366
+ n_scan=n_scan)
367
+
368
+ return est_support, info
369
+
370
+ # ======================
371
+ # estimate branch points
372
+ # ======================
373
+
374
+ def estimate_branch_points(self):
375
+ """
376
+ Compute global branch points and zeros of leading a_j
377
+ """
378
+
379
+ if self.a_coeffs is None:
380
+ raise RuntimeError('Call "fit" first.')
381
+
382
+ bp, leading_zeros, info = compute_branch_points(
383
+ self.a_coeffs, tol=1e-12, real_tol=None)
384
+
385
+ return bp, leading_zeros, info
323
386
 
324
387
  # =============
325
388
  # generate grid
@@ -462,11 +525,11 @@ class AlgebraicForm(object):
462
525
 
463
526
  # Preallocate density to zero
464
527
  hilb = -self._stieltjes(x).real / numpy.pi
465
-
528
+
466
529
  if plot:
467
- plot_hilbert(x, hilb, support=self.support, latex=latex,
530
+ plot_hilbert(x, hilb, support=self.broad_support, latex=latex,
468
531
  save=save)
469
-
532
+
470
533
  return hilb
471
534
 
472
535
  # =========
@@ -531,7 +594,7 @@ class AlgebraicForm(object):
531
594
  # Create x if not given
532
595
  if x is None:
533
596
  x = self._generate_grid(2.0, extend=2.0)[::2]
534
-
597
+
535
598
  # Create y if not given
536
599
  if (plot is False) and (y is None):
537
600
  # Do not use a Cartesian grid. Create a 1D array z slightly above
@@ -544,13 +607,13 @@ class AlgebraicForm(object):
544
607
  y = numpy.linspace(-1, 1, 200)
545
608
  x_grid, y_grid = numpy.meshgrid(x.real, y.real)
546
609
  z = x_grid + 1j * y_grid # shape (Ny, Nx)
547
-
610
+
548
611
  m = self._stieltjes(z, progress=True)
549
-
612
+
550
613
  if plot:
551
614
  plot_stieltjes(x, y, m, m, self.broad_support, latex=latex,
552
615
  save=save)
553
-
616
+
554
617
  return m
555
618
 
556
619
  # ==============
@@ -592,8 +655,9 @@ class AlgebraicForm(object):
592
655
 
593
656
  def decompress(self, size, x=None, method='one', plot=False, latex=False,
594
657
  save=False, verbose=False, newton_opt={
595
- 'max_iter': 50, 'tol': 1e-12, 'armijo': 1e-4,
596
- 'min_lam': 1e-6, 'w_min': 1e-14, 'sweep': True}):
658
+ 'min_n_times': 10, 'max_iter': 50, 'tol': 1e-12,
659
+ 'armijo': 1e-4, 'min_lam': 1e-6, 'w_min': 1e-14,
660
+ 'sweep': True}):
597
661
  """
598
662
  Free decompression of spectral density.
599
663
  """
@@ -611,8 +675,10 @@ class AlgebraicForm(object):
611
675
  alpha = numpy.atleast_1d(size) / self.n
612
676
 
613
677
  # Lower and upper bound on new support
614
- hilb_lb = (1.0 / self._stieltjes(self.lam_m + self.delta * 1j).item()).real
615
- hilb_ub = (1.0 / self._stieltjes(self.lam_p + self.delta * 1j).item()).real
678
+ hilb_lb = \
679
+ (1.0 / self._stieltjes(self.lam_m + self.delta * 1j).item()).real
680
+ hilb_ub = \
681
+ (1.0 / self._stieltjes(self.lam_p + self.delta * 1j).item()).real
616
682
  lb = self.lam_m - (numpy.max(alpha) - 1) * hilb_lb
617
683
  ub = self.lam_p - (numpy.max(alpha) - 1) * hilb_ub
618
684
 
@@ -632,24 +698,23 @@ class AlgebraicForm(object):
632
698
  # Query grid on the real axis + a small imaginary buffer
633
699
  z_query = x + 1j * self.delta
634
700
 
635
- # Initial condition at t=0 (physical branch)
701
+ # Initial condition at t = 0 (physical branch)
636
702
  w0_list = self._stieltjes(z_query)
637
703
 
638
- # Times
639
- t = numpy.log(alpha)
640
-
641
- # Ensure it starts from t = 0
642
- t = numpy.concatenate([numpy.zeros(1), t])
704
+ # Ensure there are at least min_n_times time t, including requested
705
+ # times, and especially time t = 0
706
+ t_all, idx_req = build_time_grid(
707
+ size, self.n, min_n_time=newton_opt.get("min_n_time", 0))
643
708
 
644
709
  # Evolve
645
710
  W, ok = decompress_newton(
646
- z_query, t, self.a_coeffs,
711
+ z_query, t_all, self.a_coeffs,
647
712
  w0_list=w0_list, **newton_opt)
648
713
 
649
- rho = W.imag / numpy.pi
714
+ rho_all = W.imag / numpy.pi
650
715
 
651
- # Remove time zero
652
- rho = rho[1:, :]
716
+ # return only the user-requested ones
717
+ rho = rho_all[idx_req]
653
718
 
654
719
  if verbose:
655
720
  print("success rate per t:", ok.mean(axis=1))
@@ -696,10 +761,19 @@ class AlgebraicForm(object):
696
761
  Evolves spectral edges.
697
762
  """
698
763
 
699
- edges, ok_edges = evolve_edges(t, self.a_coeffs, support=self.support,
700
- eta=eta, dt_max=dt_max,
701
- max_iter=max_iter, tol=tol)
764
+ if self.support is not None:
765
+ known_support = self.support
766
+ elif self.est_support is not None:
767
+ known_support = self.est_support
768
+ else:
769
+ raise RuntimeError('Call "fit" first.')
770
+
771
+ edges, ok_edges = evolve_edges(t, self.a_coeffs,
772
+ support=known_support, eta=eta,
773
+ dt_max=dt_max, max_iter=max_iter,
774
+ tol=tol)
702
775
 
776
+ # Remove spurious edges, where two edge cross and are no longer valid.
703
777
  edges2, active_k = merge_edges(edges, tol=1e-4)
704
778
 
705
779
  if verbose:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.7.9
3
+ Version: 0.7.11
4
4
  Summary: Free probability for large matrices
5
5
  Home-page: https://github.com/ameli/freealg
6
6
  Download-URL: https://github.com/ameli/freealg/archive/main.zip
@@ -1,17 +1,18 @@
1
1
  freealg/__init__.py,sha256=SjcYb6HWmaclnnM-m1eC1honZRyfNBWYDYBx23kSdjo,833
2
- freealg/__version__.py,sha256=Plj3sh67uLqQt2X1uejJ0B3ggLdj4Fr1HPHcgyqtneU,22
2
+ freealg/__version__.py,sha256=wcKNUm4uZK839U_xaeQFOEaUVqbjHp-EU_6MMaml4qk,23
3
3
  freealg/_util.py,sha256=RzccUCORgzrI9NdNqwMVugiHU0uDKkJFcIyjFMUOnv8,2518
4
4
  freealg/_algebraic_form/__init__.py,sha256=MIB_jVgw2qI-JW_ypqaFSeNAB6c4GvpjNySnap_a6hg,398
5
+ freealg/_algebraic_form/_branch_points.py,sha256=jzvHszw7xFe9B15a5RZV3pGfCGtndvrKJ4GIX6F3qhc,7814
5
6
  freealg/_algebraic_form/_constraints.py,sha256=37U7nvtCTocuS7l_nfUznkPi195PY7eXFzeiikrv3B0,2448
6
- freealg/_algebraic_form/_continuation_algebraic.py,sha256=KundB9VfX61a35VRxLFyuvB5A51QdT4PD2ffAMjrKR0,19383
7
- freealg/_algebraic_form/_decompress.py,sha256=gGtixLOVxlMy5S-NsXgoA7lIrB7u7nUZImQk1mIDo3s,21101
7
+ freealg/_algebraic_form/_continuation_algebraic.py,sha256=vVHFlMJYeXm97pgwEceJB2rGJeGOVhk_Ywg6mjoIA-g,19390
8
+ freealg/_algebraic_form/_decompress.py,sha256=uKiq5jlwmOvGriptIDz97fQiKs_F10uH6eMX1Ix43PQ,22538
8
9
  freealg/_algebraic_form/_decompress2.py,sha256=Ng9w9xmGe9M-DApp35IeNeQlvszfzT4NZx5BQn0lQ3I,2459
9
- freealg/_algebraic_form/_discriminant.py,sha256=755pproom6-xThFARaH20m4GuBwwZS2rc0Y80Yg6NzY,5331
10
10
  freealg/_algebraic_form/_edge.py,sha256=7l9QyLJDxaEY4WB6MCUFtfEZSf04wyHwH7YPHFJXSbM,10690
11
- freealg/_algebraic_form/_homotopy.py,sha256=LXDd30MNy8BdKr5-_1TCcL1OW74EIhqWWr3JomVSh2M,9532
12
- freealg/_algebraic_form/_moments.py,sha256=5lS0I7dFd15rE00FVi01ferOS77RHJdgLYf5pIzOiaw,12171
11
+ freealg/_algebraic_form/_homotopy.py,sha256=q5z8YmrT_8m7L3qw_4FD1Sd5eELIvAiAHr2ucOLW258,9508
12
+ freealg/_algebraic_form/_moments.py,sha256=u55RpvQhIMJFGsq8LZ3IlnTKxNgQPhwnPuYUS34YEyw,12400
13
13
  freealg/_algebraic_form/_sheets_util.py,sha256=6OLzWQKu-gN8rxM2rbpbN8TjNZFmD8UJ-8t9kcZdkCo,4174
14
- freealg/_algebraic_form/algebraic_form.py,sha256=us0cAzSy2CiESgJ1sse0Cw8n5m1f9VYaQilCHA1Aq3s,30620
14
+ freealg/_algebraic_form/_support.py,sha256=9go_3NjmesSW1e08CiDu8oflpGmAbsh9iZRidMvlARI,7951
15
+ freealg/_algebraic_form/algebraic_form.py,sha256=L5qJFYOX5Qm8LlrBv6YwyuTxROUdn6cCjfXoDklmrlQ,32962
15
16
  freealg/_free_form/__init__.py,sha256=5cnSX7kHci3wKx6-BEFhmVY_NjjmQAq1JjWPTEqETTg,611
16
17
  freealg/_free_form/_chebyshev.py,sha256=zkyVA8NLf7uUKlJdLz4ijd_SurdsqUgkA5nHGWSybaE,6916
17
18
  freealg/_free_form/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
@@ -44,9 +45,9 @@ freealg/distributions/_wigner.py,sha256=epgx6ne6R_7to5j6-QsWIAVFJQFquWMmYgnZYMN4
44
45
  freealg/visualization/__init__.py,sha256=NLq_zwueF7ytZ8sl8zLPqm-AODxxXNvfMozHGmmklcE,435
45
46
  freealg/visualization/_glue_util.py,sha256=2oKnEYjUOS4OZfivmciVLauVr53kyHMwi6c2zRKilTQ,693
46
47
  freealg/visualization/_rgb_hsv.py,sha256=rEskxXxSlKKxIrHRslVkgxHtD010L3ge9YtcVsOPl8E,3650
47
- freealg-0.7.9.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
48
- freealg-0.7.9.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
49
- freealg-0.7.9.dist-info/METADATA,sha256=-Yx24ecrU6zjZ5XgN1HjMQPpGrU9PBrbaXq3idRoKC4,5516
50
- freealg-0.7.9.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
51
- freealg-0.7.9.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
52
- freealg-0.7.9.dist-info/RECORD,,
48
+ freealg-0.7.11.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
49
+ freealg-0.7.11.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
50
+ freealg-0.7.11.dist-info/METADATA,sha256=Cinwx4ei_4R4aY743wVBq_DreuX8KPlwARHCWT--AWo,5517
51
+ freealg-0.7.11.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
52
+ freealg-0.7.11.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
53
+ freealg-0.7.11.dist-info/RECORD,,