freealg 0.7.8__tar.gz → 0.7.10__tar.gz

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 (63) hide show
  1. {freealg-0.7.8 → freealg-0.7.10}/PKG-INFO +1 -1
  2. freealg-0.7.10/freealg/__version__.py +1 -0
  3. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_decompress.py +1 -0
  4. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_homotopy.py +27 -16
  5. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_moments.py +43 -32
  6. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/algebraic_form.py +58 -112
  7. {freealg-0.7.8 → freealg-0.7.10}/freealg.egg-info/PKG-INFO +1 -1
  8. freealg-0.7.8/freealg/__version__.py +0 -1
  9. {freealg-0.7.8 → freealg-0.7.10}/AUTHORS.txt +0 -0
  10. {freealg-0.7.8 → freealg-0.7.10}/CHANGELOG.rst +0 -0
  11. {freealg-0.7.8 → freealg-0.7.10}/LICENSE.txt +0 -0
  12. {freealg-0.7.8 → freealg-0.7.10}/MANIFEST.in +0 -0
  13. {freealg-0.7.8 → freealg-0.7.10}/README.rst +0 -0
  14. {freealg-0.7.8 → freealg-0.7.10}/freealg/__init__.py +0 -0
  15. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/__init__.py +0 -0
  16. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_constraints.py +0 -0
  17. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_continuation_algebraic.py +0 -0
  18. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_decompress2.py +0 -0
  19. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_discriminant.py +0 -0
  20. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_edge.py +0 -0
  21. {freealg-0.7.8 → freealg-0.7.10}/freealg/_algebraic_form/_sheets_util.py +0 -0
  22. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/__init__.py +0 -0
  23. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_chebyshev.py +0 -0
  24. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_damp.py +0 -0
  25. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_decompress.py +0 -0
  26. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_density_util.py +0 -0
  27. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_jacobi.py +0 -0
  28. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_linalg.py +0 -0
  29. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_pade.py +0 -0
  30. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_plot_util.py +0 -0
  31. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_sample.py +0 -0
  32. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_series.py +0 -0
  33. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/_support.py +0 -0
  34. {freealg-0.7.8 → freealg-0.7.10}/freealg/_free_form/free_form.py +0 -0
  35. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/__init__.py +0 -0
  36. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/_continuation_genus0.py +0 -0
  37. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/_continuation_genus1.py +0 -0
  38. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/_elliptic_functions.py +0 -0
  39. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/_sphere_maps.py +0 -0
  40. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/_torus_maps.py +0 -0
  41. {freealg-0.7.8 → freealg-0.7.10}/freealg/_geometric_form/geometric_form.py +0 -0
  42. {freealg-0.7.8 → freealg-0.7.10}/freealg/_util.py +0 -0
  43. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/__init__.py +0 -0
  44. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_chiral_block.py +0 -0
  45. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_deformed_marchenko_pastur.py +0 -0
  46. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_deformed_wigner.py +0 -0
  47. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_kesten_mckay.py +0 -0
  48. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_marchenko_pastur.py +0 -0
  49. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_meixner.py +0 -0
  50. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_wachter.py +0 -0
  51. {freealg-0.7.8 → freealg-0.7.10}/freealg/distributions/_wigner.py +0 -0
  52. {freealg-0.7.8 → freealg-0.7.10}/freealg/visualization/__init__.py +0 -0
  53. {freealg-0.7.8 → freealg-0.7.10}/freealg/visualization/_glue_util.py +0 -0
  54. {freealg-0.7.8 → freealg-0.7.10}/freealg/visualization/_rgb_hsv.py +0 -0
  55. {freealg-0.7.8 → freealg-0.7.10}/freealg.egg-info/SOURCES.txt +0 -0
  56. {freealg-0.7.8 → freealg-0.7.10}/freealg.egg-info/dependency_links.txt +0 -0
  57. {freealg-0.7.8 → freealg-0.7.10}/freealg.egg-info/not-zip-safe +0 -0
  58. {freealg-0.7.8 → freealg-0.7.10}/freealg.egg-info/requires.txt +0 -0
  59. {freealg-0.7.8 → freealg-0.7.10}/freealg.egg-info/top_level.txt +0 -0
  60. {freealg-0.7.8 → freealg-0.7.10}/pyproject.toml +0 -0
  61. {freealg-0.7.8 → freealg-0.7.10}/requirements.txt +0 -0
  62. {freealg-0.7.8 → freealg-0.7.10}/setup.cfg +0 -0
  63. {freealg-0.7.8 → freealg-0.7.10}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.7.8
3
+ Version: 0.7.10
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
@@ -0,0 +1 @@
1
+ __version__ = "0.7.10"
@@ -504,6 +504,7 @@ def decompress_newton(z_list, t_grid, a_coeffs, w0_list=None,
504
504
  ok : ndarray of bool, same shape as W
505
505
  Convergence flags from the accepted solve at each point.
506
506
  """
507
+
507
508
  z_list = numpy.asarray(z_list, dtype=complex).ravel()
508
509
  t_grid = numpy.asarray(t_grid, dtype=float).ravel()
509
510
  nt = t_grid.size
@@ -4,13 +4,14 @@
4
4
 
5
5
  import numpy
6
6
  from ._moments import AlgebraicStieltjesMoments
7
+ from tqdm import tqdm
7
8
 
8
- __all__ = ['stieltjes_poly']
9
+ __all__ = ['StieltjesPoly']
9
10
 
10
11
 
11
- # =====================
12
+ # ===========
12
13
  # select root
13
- # =====================
14
+ # ===========
14
15
 
15
16
  def select_root(roots, z, target):
16
17
  """
@@ -125,13 +126,15 @@ class StieltjesPoly(object):
125
126
  self.steps = steps
126
127
  self.order = order
127
128
 
128
- # Objects depending only on a
129
129
  self.mom = AlgebraicStieltjesMoments(a)
130
- self._zpows_exp = numpy.arange(self.a_l)
131
130
  self.rad = 1.0 + self.height * self.mom.radius(self.order)
131
+ self.z0_p = 1j * self.rad
132
+ self.m0_p = self.mom.stieltjes(self.z0_p, self.order)
133
+ self.z0_m = -1j * self.rad
134
+ self.m0_m = self.mom.stieltjes(self.z0_m, self.order)
132
135
 
133
136
  def _poly_coeffs_m(self, z_val):
134
- z_powers = z_val ** self._zpows_exp
137
+ z_powers = z_val ** numpy.arange(self.a_l)
135
138
  return (z_powers @ self.a)[::-1]
136
139
 
137
140
  def _poly_roots(self, z_val):
@@ -165,14 +168,18 @@ class StieltjesPoly(object):
165
168
  if half_sign == 0.0:
166
169
  half_sign = 1.0
167
170
 
168
- # If z is outside radius of convergence, no homotopy
169
- # necessary
170
- if numpy.abs(z) > self.rad:
171
- target = self.mom.stieltjes(z, self.order)
172
- return select_root(self._poly_roots(z), z, target)
171
+ # # If z is outside radius of convergence, no homotopy
172
+ # # necessary
173
+ # if numpy.abs(z) > self.rad:
174
+ # target = self.mom.stieltjes(z, self.order)
175
+ # return select_root(self._poly_roots(z), z, target)
173
176
 
174
- z0 = 1j * float(half_sign) * self.rad
175
- target = self.mom.stieltjes(z0, self.order)
177
+ if half_sign > 0.0:
178
+ z0 = self.z0_p
179
+ target = self.m0_p
180
+ else:
181
+ z0 = self.z0_m
182
+ target = self.m0_m
176
183
 
177
184
  # Initialize at z0
178
185
  w_prev = select_root(self._poly_roots(z0), z0, target)
@@ -184,7 +191,7 @@ class StieltjesPoly(object):
184
191
 
185
192
  return w_prev
186
193
 
187
- def __call__(self, z):
194
+ def __call__(self, z, progress=False):
188
195
  # Scalar fast-path
189
196
  if numpy.isscalar(z):
190
197
  return self.evaluate(z)
@@ -194,7 +201,11 @@ class StieltjesPoly(object):
194
201
  out = numpy.empty(z_arr.shape, dtype=numpy.complex128)
195
202
 
196
203
  # Iterate over indices so we can pass Python scalars into evaluate()
197
- for idx in numpy.ndindex(z_arr.shape):
204
+ if progress:
205
+ indices = tqdm(numpy.ndindex(z_arr.shape), total=z_arr.size)
206
+ else:
207
+ indices = numpy.ndindex(z_arr.shape)
208
+ for idx in indices:
198
209
  out[idx] = self.evaluate(z_arr[idx])
199
210
 
200
211
  return out
@@ -254,7 +265,7 @@ class StieltjesPoly(object):
254
265
  # coeffs = numpy.asarray(poly_coeffs_m(z_val), dtype=numpy.complex128)
255
266
  # return numpy.roots(coeffs)
256
267
 
257
- # # If user asked for a real-axis value, interpret as boundary value from C+.
268
+ # # If user asked a real-axis value, interpret as boundary value from C+.
258
269
  # if z.imag == 0.0:
259
270
  # if eps is None:
260
271
  # eps = 1e-8 * max(1.0, abs(z))
@@ -1,9 +1,13 @@
1
+ # =======
2
+ # Imports
3
+ # =======
4
+
1
5
  import numpy
2
6
 
3
7
 
4
- # =========
8
+ # =======
5
9
  # Moments
6
- # =========
10
+ # =======
7
11
 
8
12
  class MomentsESD(object):
9
13
  """
@@ -85,9 +89,9 @@ class MomentsESD(object):
85
89
  # (a_{n,0},...,a_{n,n-1})
86
90
  self._a = {0: numpy.array([1.0])}
87
91
 
88
- # ----------
89
- # moments
90
- # ----------
92
+ # =
93
+ # m
94
+ # =
91
95
 
92
96
  def m(self, n):
93
97
  """
@@ -111,9 +115,9 @@ class MomentsESD(object):
111
115
  self._m[n] = numpy.mean(self.eig ** n)
112
116
  return self._m[n]
113
117
 
114
- # -------------
115
- # coefficients
116
- # -------------
118
+ # ======
119
+ # coeffs
120
+ # ======
117
121
 
118
122
  def coeffs(self, n):
119
123
  """
@@ -129,7 +133,8 @@ class MomentsESD(object):
129
133
  -------
130
134
 
131
135
  a_n : numpy.ndarray
132
- Array of shape ``(n,)`` containing :math:`(a_{n,0}, \\dots, a_{n,n-1})`.
136
+ Array of shape ``(n,)`` containing :math:`(a_{n,0},
137
+ \\dots, a_{n,n-1})`.
133
138
  """
134
139
 
135
140
  if n in self._a:
@@ -143,6 +148,10 @@ class MomentsESD(object):
143
148
  self._compute_row(n)
144
149
  return self._a[n]
145
150
 
151
+ # ===========
152
+ # compute row
153
+ # ===========
154
+
146
155
  def _compute_row(self, n):
147
156
  """
148
157
  Compute and memoize the coefficient row :math:`a_n`.
@@ -212,9 +221,9 @@ class MomentsESD(object):
212
221
 
213
222
  self._a[n] = a_n
214
223
 
215
- # ----------
224
+ # --------
216
225
  # evaluate
217
- # ----------
226
+ # --------
218
227
 
219
228
  def __call__(self, n, t=0.0):
220
229
  """
@@ -254,18 +263,18 @@ class MomentsESD(object):
254
263
  k = numpy.arange(n, dtype=float)
255
264
  return numpy.dot(a_n, numpy.exp(k * t))
256
265
 
266
+
257
267
  # ===========================
258
268
  # Algebraic Stieltjes Moments
259
269
  # ===========================
260
270
 
261
-
262
271
  class AlgebraicStieltjesMoments(object):
263
272
  """
264
273
  Given coefficients a[i,j] for P(z,m)=sum_{i,j} a[i,j] z^i m^j,
265
274
  compute the large-|z| branch
266
275
  m(z) = sum_{k>=0} mu_series[k] / z^{k+1}.
267
276
 
268
- Convention here: choose mu0 (the leading coefficient) by solving the
277
+ Convention here: choose mu0 (the leading coefficient) by solving the
269
278
  leading-diagonal equation and (by default) picking the root closest
270
279
  to -1, i.e. m(z) ~ -1/z.
271
280
 
@@ -281,7 +290,7 @@ class AlgebraicStieltjesMoments(object):
281
290
  if self.a.ndim != 2:
282
291
  raise ValueError("a must be a 2D NumPy array with a[i,j]=a_{ij}.")
283
292
 
284
- self.I = self.a.shape[0] - 1
293
+ self.I = self.a.shape[0] - 1 # noqa: E741
285
294
  self.J = self.a.shape[1] - 1
286
295
 
287
296
  nz = numpy.argwhere(self.a != 0)
@@ -320,7 +329,8 @@ class AlgebraicStieltjesMoments(object):
320
329
  if j > 0:
321
330
  self.A0 += j * coeff * self.mu0pow[j - 1]
322
331
  if self.A0 == 0:
323
- raise ValueError("A0 is zero for this mu0; the sequential recursion is degenerate.")
332
+ raise ValueError("A0 is zero for this mu0; the sequential " +
333
+ "recursion is degenerate.")
324
334
 
325
335
  # Stored series moments mu_series[0..]
326
336
  self._mu = [self.mu0]
@@ -344,14 +354,17 @@ class AlgebraicStieltjesMoments(object):
344
354
  coeffs[j] = self.a[i, j]
345
355
 
346
356
  if not numpy.any(coeffs != 0):
347
- raise ValueError("Leading diagonal polynomial is identically zero; cannot determine mu0.")
357
+ raise ValueError("Leading diagonal polynomial is identically " +
358
+ "zero; cannot determine mu0.")
348
359
 
349
360
  deg = int(numpy.max(numpy.nonzero(coeffs)[0]))
350
- roots = numpy.roots(coeffs[:deg + 1][::-1]) # descending powers for numpy.roots
361
+
362
+ # descending powers for numpy.roots
363
+ roots = numpy.roots(coeffs[:deg + 1][::-1])
351
364
 
352
365
  # Targetting mu0 = -1 for ~ -1/z asymptotics
353
366
  mu0 = roots[numpy.argmin(numpy.abs(roots + 1))]
354
-
367
+
355
368
  if abs(mu0.imag) < 1e-12:
356
369
  mu0 = mu0.real
357
370
  return mu0
@@ -363,7 +376,8 @@ class AlgebraicStieltjesMoments(object):
363
376
 
364
377
  # Compute f[j] = coefficient of w^k in (S_trunc(w))^j,
365
378
  # where S_trunc uses mu_0..mu_{k-1} only (i.e. mu_k treated as 0).
366
- # Key fact: in the true c[j,k], mu_k can only appear linearly as j*mu_k*mu0^{j-1}.
379
+ # Key fact: in the true c[j,k], mu_k can only appear linearly as
380
+ # j*mu_k*mu0^{j-1}.
367
381
  f = [0] * (self.J + 1)
368
382
  f[0] = 0
369
383
  for j in range(1, self.J + 1):
@@ -371,8 +385,9 @@ class AlgebraicStieltjesMoments(object):
371
385
  # sum_{t=1..k-1} mu_t * c[j-1, k-t]
372
386
  for t in range(1, k):
373
387
  ssum += self._mu[t] * self._c[j - 1][k - t]
374
- # recurrence: c[j,k] = mu0*c[j-1,k] + sum_{t=1..k-1} mu_t*c[j-1,k-t] + mu_k*c[j-1,0]
375
- # with mu_k=0 for f, and c[j-1,k]=f[j-1]
388
+ # recurrence: c[j,k] = mu0*c[j-1,k] + sum_{t=1..k-1}
389
+ # mu_t*c[j-1,k-t] + mu_k*c[j-1,0] with mu_k=0 for f,
390
+ # and c[j-1,k]=f[j-1]
376
391
  f[j] = self.mu0 * f[j - 1] + ssum
377
392
 
378
393
  # Build the linear equation for mu_k:
@@ -386,7 +401,8 @@ class AlgebraicStieltjesMoments(object):
386
401
  continue
387
402
  rest += coeff * f[j]
388
403
 
389
- # lower diagonals s=1..k contribute coeff*c[j,k-s] (already known since k-s < k)
404
+ # lower diagonals s=1..k contribute coeff*c[j,k-s] (already known
405
+ # since k-s < k)
390
406
  for s in range(1, k + 1):
391
407
  entries = self.diag.get(s)
392
408
  if not entries:
@@ -402,7 +418,8 @@ class AlgebraicStieltjesMoments(object):
402
418
  mu_k = -rest / self.A0
403
419
  self._mu.append(mu_k)
404
420
 
405
- # Now append the new column k to c using the full convolution recurrence:
421
+ # Now append the new column k to c using the full convolution
422
+ # recurrence:
406
423
  # c[j,k] = sum_{t=0..k} mu_t * c[j-1,k-t]
407
424
  for j in range(self.J + 1):
408
425
  self._c[j].append(0)
@@ -430,9 +447,10 @@ class AlgebraicStieltjesMoments(object):
430
447
  # Estimate the radius of convergence of the Stieltjes
431
448
  # series
432
449
  if N < 3:
433
- raise RuntimeError("Order is too small, choose a larger value of N")
450
+ raise RuntimeError("N is too small, choose a larger value.")
434
451
  self._ensure(N)
435
- return max([numpy.abs(self._mu[j] / self._mu[j-1]) for j in range(2,N+1)])
452
+ return max([numpy.abs(self._mu[j] / self._mu[j-1])
453
+ for j in range(2, N+1)])
436
454
 
437
455
  def stieltjes(self, z, N):
438
456
  # Estimate Stieltjes transform (root) using moment
@@ -441,10 +459,3 @@ class AlgebraicStieltjesMoments(object):
441
459
  mu = self.moments(N)
442
460
  return -numpy.sum(z[..., numpy.newaxis]**(-numpy.arange(N+1)-1) * mu,
443
461
  axis=-1)
444
-
445
- def target_pt(self, N=15):
446
- # Obtain an estimate of the Stieltjes transform at a
447
- # single point z where the estimate is likely reliable
448
- z = 1j + 2j * self.radius(N)
449
- return z, self.stieltjes(z, N)
450
-
@@ -24,7 +24,7 @@ from ._homotopy import StieltjesPoly
24
24
  from ._discriminant import compute_singular_points
25
25
  from ._moments import MomentsESD
26
26
  from .._free_form._support import supp
27
- from .._free_form._plot_util import plot_density
27
+ from .._free_form._plot_util import plot_density, plot_hilbert, plot_stieltjes
28
28
 
29
29
  # Fallback to previous numpy API
30
30
  if not hasattr(numpy, 'trapezoid'):
@@ -63,7 +63,7 @@ class AlgebraicForm(object):
63
63
  * ``'complex128'``: 128-bit complex numbers, equivalent of two double
64
64
  precision floating point.
65
65
  * ``'complex256'``: 256-bit complex numbers, equivalent of two long
66
- double precision floating point. This optino is only available on
66
+ double precision floating point. This option is only available on
67
67
  Linux machines.
68
68
 
69
69
  When using series acceleration methods (such as setting
@@ -144,8 +144,8 @@ class AlgebraicForm(object):
144
144
 
145
145
  self.A = None
146
146
  self.eig = None
147
- self.stieltjes = None
148
- self.moments = None
147
+ self._stieltjes = None
148
+ self._moments = None
149
149
  self.support = support
150
150
  self.delta = delta # Offset above real axis to apply Plemelj formula
151
151
 
@@ -154,12 +154,12 @@ class AlgebraicForm(object):
154
154
 
155
155
  if hasattr(A, 'stieltjes') and callable(getattr(A, 'stieltjes', None)):
156
156
  # This is one of the distribution objects, like MarchenkoPastur
157
- self.stieltjes = A.stieltjes
157
+ self._stieltjes = A.stieltjes
158
158
  self.n = 1
159
159
 
160
160
  elif callable(A):
161
161
  # This is a custom function
162
- self.stieltjes = A
162
+ self._stieltjes = A
163
163
  self.n = 1
164
164
 
165
165
  else:
@@ -178,9 +178,9 @@ class AlgebraicForm(object):
178
178
  self.eig = compute_eig(A)
179
179
 
180
180
  # Use empirical Stieltjes function
181
- self.stieltjes = lambda z: \
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
185
  # Support
186
186
  if support is None:
@@ -265,13 +265,13 @@ class AlgebraicForm(object):
265
265
  x_pad=x_pad)
266
266
 
267
267
  # Fitting (w_inf = None means adaptive weight selection)
268
- m1_fit = self.stieltjes(z_fit)
268
+ m1_fit = self._stieltjes(z_fit)
269
269
  a_coeffs, fit_metrics = fit_polynomial_relation(
270
270
  z_fit, m1_fit, s=deg_m, deg_z=deg_z, ridge_lambda=reg,
271
271
  triangular=triangular, normalize=normalize, mu=mu,
272
272
  mu_reg=mu_reg)
273
273
 
274
- # Compute global branhc points, zeros of leading a_j, and support
274
+ # Compute global branch points, zeros of leading a_j, and support
275
275
  branch_points, a_s_zero, support = compute_singular_points(a_coeffs)
276
276
 
277
277
  self.a_coeffs = a_coeffs
@@ -294,7 +294,7 @@ class AlgebraicForm(object):
294
294
  status['res_99_9'] = float(res_99_9)
295
295
  status['fit_metrics'] = fit_metrics
296
296
  self.status = status
297
- self.stieltjes = StieltjesPoly(self.a_coeffs)
297
+ self._stieltjes = StieltjesPoly(self.a_coeffs) # NOTE overwrite init
298
298
 
299
299
  if verbose:
300
300
  print(f'fit residual max : {res_max:>0.4e}')
@@ -388,8 +388,6 @@ class AlgebraicForm(object):
388
388
  >>> from freealg import FreeForm
389
389
  """
390
390
 
391
- pass
392
-
393
391
  if self.a_coeffs is None:
394
392
  raise RuntimeError('The model needs to be fit using the .fit() ' +
395
393
  'function.')
@@ -399,27 +397,7 @@ class AlgebraicForm(object):
399
397
  x = self._generate_grid(1.25)
400
398
 
401
399
  # Preallocate density to zero
402
- rho = self.stieltjes(x).imag / numpy.pi
403
-
404
- # if self.method == 'jacobi':
405
- # rho[mask] = jacobi_density(x[mask], self.psi, self.support,
406
- # self.alpha, self.beta)
407
- # elif self.method == 'chebyshev':
408
- # rho[mask] = chebyshev_density(x[mask], self.psi, self.support)
409
- # else:
410
- # raise RuntimeError('"method" is invalid.')
411
- #
412
- # # Check density is unit mass
413
- # mass = numpy.trapezoid(rho, x)
414
- # if not numpy.isclose(mass, 1.0, atol=1e-2):
415
- # print(f'"rho" is not unit mass. mass: {mass:>0.3f}. Set ' +
416
- # r'"force=True".')
417
- #
418
- # # Check density is positive
419
- # min_rho = numpy.min(rho)
420
- # if min_rho < 0.0 - 1e-3:
421
- # print(f'"rho" is not positive. min_rho: {min_rho:>0.3f}. Set ' +
422
- # r'"force=True".')
400
+ rho = self._stieltjes(x).imag / numpy.pi
423
401
 
424
402
  if plot:
425
403
  plot_density(x, rho, eig=self.eig, support=self.broad_support,
@@ -431,7 +409,7 @@ class AlgebraicForm(object):
431
409
  # hilbert
432
410
  # =======
433
411
 
434
- def hilbert(self, x=None, rho=None, plot=False, latex=False, save=False):
412
+ def hilbert(self, x=None, plot=False, latex=False, save=False):
435
413
  """
436
414
  Compute Hilbert transform of the spectral density.
437
415
 
@@ -443,9 +421,6 @@ class AlgebraicForm(object):
443
421
  an interval slightly larger than the support interval of the
444
422
  spectral density is used.
445
423
 
446
- rho : numpy.array, default=None
447
- Density. If `None`, it will be computed.
448
-
449
424
  plot : bool, default=False
450
425
  If `True`, density is plotted.
451
426
 
@@ -477,49 +452,22 @@ class AlgebraicForm(object):
477
452
  >>> from freealg import FreeForm
478
453
  """
479
454
 
480
- pass
481
-
482
455
  if self.a_coeffs is None:
483
456
  raise RuntimeError('The model needs to be fit using the .fit() ' +
484
457
  'function.')
485
458
 
486
- # # Create x if not given
487
- # if x is None:
488
- # x = self._generate_grid(1.25)
489
- #
490
- # # if (numpy.min(x) > self.lam_m) or (numpy.max(x) < self.lam_p):
491
- # # raise ValueError('"x" does not encompass support interval.')
492
- #
493
- # # Preallocate density to zero
494
- # if rho is None:
495
- # rho = self.density(x)
496
- #
497
- # # mask of support [lam_m, lam_p]
498
- # mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
499
- # x_s = x[mask]
500
- # rho_s = rho[mask]
501
- #
502
- # # Form the matrix of integrands: rho_s / (t - x_i)
503
- # # Here, we have diff[i,j] = x[i] - x_s[j]
504
- # diff = x[:, None] - x_s[None, :]
505
- # D = rho_s[None, :] / diff
506
- #
507
- # # Principal-value: wherever t == x_i, then diff == 0, zero that entry
508
- # # (numpy.isclose handles floating-point exactly)
509
- # D[numpy.isclose(diff, 0.0)] = 0.0
510
- #
511
- # # Integrate each row over t using trapezoid rule on x_s
512
- # # Namely, hilb[i] = int rho_s(t)/(t - x[i]) dt
513
- # hilb = numpy.trapezoid(D, x_s, axis=1) / numpy.pi
514
- #
515
- # # We use negative sign convention
516
- # hilb = -hilb
517
- #
518
- # if plot:
519
- # plot_hilbert(x, hilb, support=self.support, latex=latex,
520
- # save=save)
521
- #
522
- # return hilb
459
+ # Create x if not given
460
+ if x is None:
461
+ x = self._generate_grid(1.25)
462
+
463
+ # Preallocate density to zero
464
+ hilb = -self._stieltjes(x).real / numpy.pi
465
+
466
+ if plot:
467
+ plot_hilbert(x, hilb, support=self.support, latex=latex,
468
+ save=save)
469
+
470
+ return hilb
523
471
 
524
472
  # =========
525
473
  # stieltjes
@@ -559,12 +507,9 @@ class AlgebraicForm(object):
559
507
  Returns
560
508
  -------
561
509
 
562
- m_p : numpy.ndarray
510
+ m : numpy.ndarray
563
511
  The Stieltjes transform on the principal branch.
564
512
 
565
- m_m : numpy.ndarray
566
- The Stieltjes transform continued to the secondary branch.
567
-
568
513
  See Also
569
514
  --------
570
515
 
@@ -579,36 +524,34 @@ class AlgebraicForm(object):
579
524
  >>> from freealg import FreeForm
580
525
  """
581
526
 
582
- pass
583
-
584
527
  if self.a_coeffs is None:
585
528
  raise RuntimeError('The model needs to be fit using the .fit() ' +
586
529
  'function.')
587
530
 
588
- # # Create x if not given
589
- # if x is None:
590
- # x = self._generate_grid(2.0, extend=2.0)
591
- #
592
- # # Create y if not given
593
- # if (plot is False) and (y is None):
594
- # # Do not use a Cartesian grid. Create a 1D array z slightly above
595
- # # the real line.
596
- # y = self.delta * 1j
597
- # z = x.astype(complex) + y # shape (Nx,)
598
- # else:
599
- # # Use a Cartesian grid
600
- # if y is None:
601
- # y = numpy.linspace(-1, 1, 400)
602
- # x_grid, y_grid = numpy.meshgrid(x.real, y.real)
603
- # z = x_grid + 1j * y_grid # shape (Ny, Nx)
604
- #
605
- # m1, m2 = self._eval_stieltjes(z, branches=True)
606
- #
607
- # if plot:
608
- # plot_stieltjes(x, y, m1, m2, self.support, latex=latex,
609
- # save=save)
610
- #
611
- # return m1, m2
531
+ # Create x if not given
532
+ if x is None:
533
+ x = self._generate_grid(2.0, extend=2.0)[::2]
534
+
535
+ # Create y if not given
536
+ if (plot is False) and (y is None):
537
+ # Do not use a Cartesian grid. Create a 1D array z slightly above
538
+ # the real line.
539
+ y = self.delta * 1j
540
+ z = x.astype(complex) + y # shape (Nx,)
541
+ else:
542
+ # Use a Cartesian grid
543
+ if y is None:
544
+ y = numpy.linspace(-1, 1, 200)
545
+ x_grid, y_grid = numpy.meshgrid(x.real, y.real)
546
+ z = x_grid + 1j * y_grid # shape (Ny, Nx)
547
+
548
+ m = self._stieltjes(z, progress=True)
549
+
550
+ if plot:
551
+ plot_stieltjes(x, y, m, m, self.broad_support, latex=latex,
552
+ save=save)
553
+
554
+ return m
612
555
 
613
556
  # ==============
614
557
  # eval stieltjes
@@ -668,8 +611,10 @@ class AlgebraicForm(object):
668
611
  alpha = numpy.atleast_1d(size) / self.n
669
612
 
670
613
  # Lower and upper bound on new support
671
- hilb_lb = (1.0 / self.stieltjes(self.lam_m + self.delta * 1j).item()).real
672
- hilb_ub = (1.0 / self.stieltjes(self.lam_p + self.delta * 1j).item()).real
614
+ hilb_lb = \
615
+ (1.0 / self._stieltjes(self.lam_m + self.delta * 1j).item()).real
616
+ hilb_ub = \
617
+ (1.0 / self._stieltjes(self.lam_p + self.delta * 1j).item()).real
673
618
  lb = self.lam_m - (numpy.max(alpha) - 1) * hilb_lb
674
619
  ub = self.lam_p - (numpy.max(alpha) - 1) * hilb_ub
675
620
 
@@ -690,13 +635,14 @@ class AlgebraicForm(object):
690
635
  z_query = x + 1j * self.delta
691
636
 
692
637
  # Initial condition at t=0 (physical branch)
693
- w0_list = self.stieltjes(z_query)
638
+ w0_list = self._stieltjes(z_query)
694
639
 
695
640
  # Times
696
641
  t = numpy.log(alpha)
697
642
 
698
643
  # Ensure it starts from t = 0
699
- t = numpy.concatenate([numpy.zeros(1), t])
644
+ if t[0] > 1.0:
645
+ t = numpy.concatenate([numpy.zeros(1), t])
700
646
 
701
647
  # Evolve
702
648
  W, ok = decompress_newton(
@@ -721,7 +667,7 @@ class AlgebraicForm(object):
721
667
  coeffs_i = decompress_coeffs(self.a_coeffs,
722
668
  numpy.log(alpha[i]))
723
669
  stieltjes_i = StieltjesPoly(coeffs_i)
724
- rho[i, :] = stieltjes_i.imag
670
+ rho[i, :] = stieltjes_i(x).imag
725
671
 
726
672
  rho = rho / numpy.pi
727
673
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.7.8
3
+ Version: 0.7.10
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 +0,0 @@
1
- __version__ = "0.7.8"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes