freealg 0.7.16__tar.gz → 0.7.17__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 (80) hide show
  1. {freealg-0.7.16 → freealg-0.7.17}/PKG-INFO +1 -1
  2. freealg-0.7.17/freealg/__version__.py +1 -0
  3. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_compound_poisson.py +27 -17
  4. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_deformed_marchenko_pastur.py +16 -72
  5. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_deformed_wigner.py +3 -40
  6. {freealg-0.7.16 → freealg-0.7.17}/freealg.egg-info/PKG-INFO +1 -1
  7. freealg-0.7.16/freealg/__version__.py +0 -1
  8. {freealg-0.7.16 → freealg-0.7.17}/AUTHORS.txt +0 -0
  9. {freealg-0.7.16 → freealg-0.7.17}/CHANGELOG.rst +0 -0
  10. {freealg-0.7.16 → freealg-0.7.17}/LICENSE.txt +0 -0
  11. {freealg-0.7.16 → freealg-0.7.17}/MANIFEST.in +0 -0
  12. {freealg-0.7.16 → freealg-0.7.17}/README.rst +0 -0
  13. {freealg-0.7.16 → freealg-0.7.17}/freealg/__init__.py +0 -0
  14. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/__init__.py +0 -0
  15. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_branch_points.py +0 -0
  16. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_constraints.py +0 -0
  17. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_continuation_algebraic.py +0 -0
  18. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_cusp.py +0 -0
  19. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_cusp_wrap.py +0 -0
  20. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress.py +0 -0
  21. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress2.py +0 -0
  22. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress4.py +0 -0
  23. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress5.py +0 -0
  24. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress6.py +0 -0
  25. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress7.py +0 -0
  26. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress8.py +0 -0
  27. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress9.py +0 -0
  28. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress_new.py +0 -0
  29. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress_new_2.py +0 -0
  30. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_decompress_util.py +0 -0
  31. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_edge.py +0 -0
  32. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_homotopy.py +0 -0
  33. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_homotopy2.py +0 -0
  34. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_homotopy3.py +0 -0
  35. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_homotopy4.py +0 -0
  36. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_homotopy5.py +0 -0
  37. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_moments.py +0 -0
  38. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_sheets_util.py +0 -0
  39. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/_support.py +0 -0
  40. {freealg-0.7.16 → freealg-0.7.17}/freealg/_algebraic_form/algebraic_form.py +0 -0
  41. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/__init__.py +0 -0
  42. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_chebyshev.py +0 -0
  43. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_damp.py +0 -0
  44. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_decompress.py +0 -0
  45. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_density_util.py +0 -0
  46. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_jacobi.py +0 -0
  47. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_linalg.py +0 -0
  48. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_pade.py +0 -0
  49. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_plot_util.py +0 -0
  50. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_sample.py +0 -0
  51. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_series.py +0 -0
  52. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/_support.py +0 -0
  53. {freealg-0.7.16 → freealg-0.7.17}/freealg/_free_form/free_form.py +0 -0
  54. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/__init__.py +0 -0
  55. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/_continuation_genus0.py +0 -0
  56. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/_continuation_genus1.py +0 -0
  57. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/_elliptic_functions.py +0 -0
  58. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/_sphere_maps.py +0 -0
  59. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/_torus_maps.py +0 -0
  60. {freealg-0.7.16 → freealg-0.7.17}/freealg/_geometric_form/geometric_form.py +0 -0
  61. {freealg-0.7.16 → freealg-0.7.17}/freealg/_util.py +0 -0
  62. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/__init__.py +0 -0
  63. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_chiral_block.py +0 -0
  64. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_kesten_mckay.py +0 -0
  65. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_marchenko_pastur.py +0 -0
  66. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_meixner.py +0 -0
  67. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_wachter.py +0 -0
  68. {freealg-0.7.16 → freealg-0.7.17}/freealg/distributions/_wigner.py +0 -0
  69. {freealg-0.7.16 → freealg-0.7.17}/freealg/visualization/__init__.py +0 -0
  70. {freealg-0.7.16 → freealg-0.7.17}/freealg/visualization/_glue_util.py +0 -0
  71. {freealg-0.7.16 → freealg-0.7.17}/freealg/visualization/_rgb_hsv.py +0 -0
  72. {freealg-0.7.16 → freealg-0.7.17}/freealg.egg-info/SOURCES.txt +0 -0
  73. {freealg-0.7.16 → freealg-0.7.17}/freealg.egg-info/dependency_links.txt +0 -0
  74. {freealg-0.7.16 → freealg-0.7.17}/freealg.egg-info/not-zip-safe +0 -0
  75. {freealg-0.7.16 → freealg-0.7.17}/freealg.egg-info/requires.txt +0 -0
  76. {freealg-0.7.16 → freealg-0.7.17}/freealg.egg-info/top_level.txt +0 -0
  77. {freealg-0.7.16 → freealg-0.7.17}/pyproject.toml +0 -0
  78. {freealg-0.7.16 → freealg-0.7.17}/requirements.txt +0 -0
  79. {freealg-0.7.16 → freealg-0.7.17}/setup.cfg +0 -0
  80. {freealg-0.7.16 → freealg-0.7.17}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.7.16
3
+ Version: 0.7.17
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.17"
@@ -372,23 +372,6 @@ class CompoundPoisson(object):
372
372
  """
373
373
  Generate a symmetric random matrix whose ESD approximates this law.
374
374
 
375
- Construction
376
- ------------
377
- Use a sum of two independent (rotationally invariant) Wishart terms,
378
- which are asymptotically free:
379
-
380
- A = s1 * (1/m1) Z1 Z1^T + s2 * (1/m2) Z2 Z2^T,
381
-
382
- where Zi are n x mi i.i.d. N(0,1). Choose aspect ratios ci = n/mi and
383
- scales si so each term has R-transform
384
-
385
- Ri(w) = lam_i * a_i / (1 - a_i w),
386
-
387
- with lam_1 = lam*w1, lam_2 = lam*(1-w1). This is achieved by setting
388
-
389
- c_i = 1/lam_i,
390
- s_i = a_i * lam_i.
391
-
392
375
  Parameters
393
376
  ----------
394
377
  size : int
@@ -401,6 +384,33 @@ class CompoundPoisson(object):
401
384
  -------
402
385
  A : numpy.ndarray
403
386
  Symmetric matrix (n x n).
387
+
388
+ Notes
389
+ -----
390
+
391
+ Use a sum of two independent (rotationally invariant) Wishart terms,
392
+ which are asymptotically free:
393
+
394
+ .. math::
395
+
396
+ A = s_1 \\frac{1}{m_1} \\mathbf{Z}_1 \\mathbf{Z}_1^{\\intercal} +
397
+ s_2 * \\frac{1}{m_2} \\mathbf{Z}_2 \\mathbf{Z}_2^{\\intefcal},
398
+
399
+ where :math:`\\mathbf{Z}_i` are :math:`n \\times m_`i` i.i.d.
400
+ :math:`N(0,1)`. Choose aspect ratios :math:`c_i = n/m_i` and
401
+ scales :math:`s_i` so each term has R-transform
402
+
403
+ .. math::
404
+
405
+ R_i(w) = \\lambda_i \\frac{a_i}{(1 - a_i w},
406
+
407
+ with :math:`\\lambda_1 = \\lambda w1`,
408
+ :math:`\\lambda_2 = \\lambda (1-w_1)`. This is achieved by setting
409
+
410
+ .. math::
411
+
412
+ c_i = 1 / \\lambda_i,
413
+ s_i = a_i * \\lambda_i.
404
414
  """
405
415
 
406
416
  n = int(size)
@@ -246,91 +246,35 @@ class DeformedMarchenkoPastur(object):
246
246
  # density
247
247
  # =======
248
248
 
249
- def density(self, x, eta=1e-3):
249
+ def density(self, x, eta=1e-3, ac_only=True):
250
250
  """
251
251
  Density via Stieltjes inversion with robust x-continuation.
252
252
 
253
- Notes:
253
+ Notes
254
+ -----
255
+
254
256
  - Do not warm-start across x<0 (MP-type support is >=0).
255
257
  - Reset warm-start when previous u is (nearly) real.
256
258
  - If Newton lands on a non-Herglotz root, fall back to cubic roots +
257
259
  pick.
258
- """
259
260
 
260
- # Unpack parameters
261
- t1 = self.t1
262
- t2 = self.t2
263
- w1 = self.w1
264
- c = self.c
261
+ If ac_only is True and c < 1, subtract the smeared atom at zero of mass
262
+ (1-c) for visualization.
263
+ """
265
264
 
266
265
  x = numpy.asarray(x, dtype=numpy.float64)
267
- rho = numpy.zeros_like(x, dtype=numpy.float64)
268
-
269
- c = float(c)
270
- if c < 0.0:
271
- raise ValueError("c must be >= 0.")
272
- if c == 0.0:
273
- # Degenerate: μ = H when c=0, so m(z)=E[1/(t-z)] and rho from Im m.
274
- z = x + 1j * float(eta)
275
- w2 = 1.0 - w1
276
- m = (w1 / (t1 - z)) + (w2 / (t2 - z))
277
- rho = numpy.maximum(numpy.imag(m) / numpy.pi, 0.0)
278
- return rho
279
-
280
- # MP-type spectra live on x>=0; probing code includes x<0 (x_min<0),
281
- # so we keep rho(x<0)=0 and DO NOT carry warm-start across 0.
282
- mask = (x >= 0.0)
283
- if not numpy.any(mask):
284
- return rho
285
-
286
- xp = x[mask]
287
-
288
- # Preserve original order (support probing uses increasing xp).
289
- order = numpy.argsort(xp)
290
- inv = numpy.empty_like(order)
291
- inv[order] = numpy.arange(order.size)
292
-
293
- xp_sorted = xp[order]
294
- z = xp_sorted + 1j * float(eta)
295
- zf = z.ravel()
296
-
297
- u = numpy.empty_like(zf, dtype=numpy.complex128)
298
- u_prev = None
299
-
300
- # thresholds
301
- imag_eps = 1e-14
302
-
303
- w2 = 1.0 - w1
304
-
305
- for i in range(zf.size):
306
- zi = zf[i]
307
-
308
- # Warm start only if previous iterate had meaningful imaginary part
309
- # (otherwise we risk sticking to a real branch across the bulk).
310
- if (u_prev is None) or (abs(u_prev.imag) <= imag_eps):
311
- ui0 = -1.0 / zi
312
- else:
313
- ui0 = complex(u_prev)
314
-
315
- ui, _ = self._solve_u_newton(zi, u0=ui0, max_iter=120, tol=1e-13)
316
-
317
- # Enforce Herglotz: sign(Im z) == sign(Im u) (eta>0 => Im u must be
318
- # >0)
319
- if (not numpy.isfinite(ui)) or (ui.imag <= 0.0):
320
- u_roots = self._roots_cubic_u_scalar(zi)
321
- ui = _pick_physical_root_scalar(zi, u_roots)
322
-
323
- u[i] = ui
324
- u_prev = ui
266
+ z = x + 1j * float(eta)
325
267
 
326
- m = (u + (1.0 - c) / zf) / c
327
- rh = numpy.maximum(numpy.imag(m) / numpy.pi, 0.0)
268
+ m = self.stieltjes(z)
269
+ rho = numpy.imag(m) / numpy.pi
328
270
 
329
- # Unsort back
330
- rh = rh.reshape(xp_sorted.shape)
331
- rho[mask] = rh[inv]
271
+ # Optional: remove the atom at zero (only for visualization of AC part)
272
+ if ac_only and (self.c > 1.0):
273
+ w0 = 1.0 - 1.0 / self.c
274
+ rho = rho - w0 * (float(eta) / numpy.pi) / \
275
+ (x * x + float(eta) * float(eta))
332
276
 
333
- return rho
277
+ return numpy.maximum(rho, 0.0)
334
278
 
335
279
  # =====
336
280
  # roots
@@ -139,50 +139,13 @@ class DeformedWigner(object):
139
139
  """
140
140
  """
141
141
 
142
- # Unpack parameters
143
- t1 = self.t1
144
- t2 = self.t2
145
- w1 = self.w1
146
- sigma = self.sigma
147
-
148
142
  x = numpy.asarray(x, dtype=numpy.float64)
149
143
  z = x + 1j * float(eta)
150
144
 
151
- zf = z.ravel()
152
- m = numpy.empty_like(zf, dtype=numpy.complex128)
153
-
154
- m_prev = None
155
- for i in range(zf.size):
156
- zi = zf[i]
157
- if m_prev is None:
158
- mi = -1.0 / zi
159
- else:
160
- mi = complex(m_prev)
161
-
162
- for _ in range(80):
163
- d1 = (t1 - zi - (sigma * sigma) * mi)
164
- d2 = (t2 - zi - (sigma * sigma) * mi)
165
-
166
- f = mi - (w1 / d1 + (1.0 - w1) / d2)
167
- fp = 1.0 - (
168
- w1 * (sigma * sigma) / (d1 * d1) +
169
- (1.0 - w1) * (sigma * sigma) / (d2 * d2)
170
- )
171
-
172
- step = f / fp
173
- mi2 = mi - step
174
- if abs(step) < 1e-12 * (1.0 + abs(mi2)):
175
- mi = mi2
176
- break
177
- mi = mi2
178
-
179
- m[i] = mi
180
- m_prev = mi
181
-
182
- m = m.reshape(z.shape)
145
+ m = self.stieltjes(z)
183
146
  rho = numpy.imag(m) / numpy.pi
184
- rho = numpy.maximum(rho, 0.0)
185
- return rho
147
+
148
+ return numpy.maximum(rho, 0.0)
186
149
 
187
150
  # =====
188
151
  # roots
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.7.16
3
+ Version: 0.7.17
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.16"
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