freealg 0.7.7__tar.gz → 0.7.9__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 (64) hide show
  1. {freealg-0.7.7 → freealg-0.7.9}/PKG-INFO +1 -1
  2. freealg-0.7.9/freealg/__version__.py +1 -0
  3. freealg-0.7.9/freealg/_algebraic_form/_homotopy.py +291 -0
  4. freealg-0.7.9/freealg/_algebraic_form/_moments.py +443 -0
  5. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/algebraic_form.py +55 -120
  6. {freealg-0.7.7 → freealg-0.7.9}/freealg.egg-info/PKG-INFO +1 -1
  7. {freealg-0.7.7 → freealg-0.7.9}/freealg.egg-info/SOURCES.txt +1 -0
  8. freealg-0.7.7/freealg/__version__.py +0 -1
  9. freealg-0.7.7/freealg/_algebraic_form/_homotopy.py +0 -138
  10. {freealg-0.7.7 → freealg-0.7.9}/AUTHORS.txt +0 -0
  11. {freealg-0.7.7 → freealg-0.7.9}/CHANGELOG.rst +0 -0
  12. {freealg-0.7.7 → freealg-0.7.9}/LICENSE.txt +0 -0
  13. {freealg-0.7.7 → freealg-0.7.9}/MANIFEST.in +0 -0
  14. {freealg-0.7.7 → freealg-0.7.9}/README.rst +0 -0
  15. {freealg-0.7.7 → freealg-0.7.9}/freealg/__init__.py +0 -0
  16. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/__init__.py +0 -0
  17. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_constraints.py +0 -0
  18. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_continuation_algebraic.py +0 -0
  19. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_decompress.py +0 -0
  20. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_decompress2.py +0 -0
  21. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_discriminant.py +0 -0
  22. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_edge.py +0 -0
  23. {freealg-0.7.7 → freealg-0.7.9}/freealg/_algebraic_form/_sheets_util.py +0 -0
  24. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/__init__.py +0 -0
  25. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_chebyshev.py +0 -0
  26. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_damp.py +0 -0
  27. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_decompress.py +0 -0
  28. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_density_util.py +0 -0
  29. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_jacobi.py +0 -0
  30. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_linalg.py +0 -0
  31. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_pade.py +0 -0
  32. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_plot_util.py +0 -0
  33. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_sample.py +0 -0
  34. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_series.py +0 -0
  35. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/_support.py +0 -0
  36. {freealg-0.7.7 → freealg-0.7.9}/freealg/_free_form/free_form.py +0 -0
  37. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/__init__.py +0 -0
  38. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/_continuation_genus0.py +0 -0
  39. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/_continuation_genus1.py +0 -0
  40. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/_elliptic_functions.py +0 -0
  41. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/_sphere_maps.py +0 -0
  42. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/_torus_maps.py +0 -0
  43. {freealg-0.7.7 → freealg-0.7.9}/freealg/_geometric_form/geometric_form.py +0 -0
  44. {freealg-0.7.7 → freealg-0.7.9}/freealg/_util.py +0 -0
  45. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/__init__.py +0 -0
  46. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_chiral_block.py +0 -0
  47. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_deformed_marchenko_pastur.py +0 -0
  48. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_deformed_wigner.py +0 -0
  49. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_kesten_mckay.py +0 -0
  50. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_marchenko_pastur.py +0 -0
  51. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_meixner.py +0 -0
  52. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_wachter.py +0 -0
  53. {freealg-0.7.7 → freealg-0.7.9}/freealg/distributions/_wigner.py +0 -0
  54. {freealg-0.7.7 → freealg-0.7.9}/freealg/visualization/__init__.py +0 -0
  55. {freealg-0.7.7 → freealg-0.7.9}/freealg/visualization/_glue_util.py +0 -0
  56. {freealg-0.7.7 → freealg-0.7.9}/freealg/visualization/_rgb_hsv.py +0 -0
  57. {freealg-0.7.7 → freealg-0.7.9}/freealg.egg-info/dependency_links.txt +0 -0
  58. {freealg-0.7.7 → freealg-0.7.9}/freealg.egg-info/not-zip-safe +0 -0
  59. {freealg-0.7.7 → freealg-0.7.9}/freealg.egg-info/requires.txt +0 -0
  60. {freealg-0.7.7 → freealg-0.7.9}/freealg.egg-info/top_level.txt +0 -0
  61. {freealg-0.7.7 → freealg-0.7.9}/pyproject.toml +0 -0
  62. {freealg-0.7.7 → freealg-0.7.9}/requirements.txt +0 -0
  63. {freealg-0.7.7 → freealg-0.7.9}/setup.cfg +0 -0
  64. {freealg-0.7.7 → freealg-0.7.9}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.7.7
3
+ Version: 0.7.9
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.9"
@@ -0,0 +1,291 @@
1
+ # =======
2
+ # Imports
3
+ # =======
4
+
5
+ import numpy
6
+ from ._moments import AlgebraicStieltjesMoments
7
+ from tqdm import tqdm
8
+
9
+ __all__ = ['stieltjes_poly']
10
+
11
+
12
+ # =====================
13
+ # select root
14
+ # =====================
15
+
16
+ def select_root(roots, z, target):
17
+ """
18
+ Select the root among Herglotz candidates at a given z closest to a
19
+ given target
20
+
21
+ Parameters
22
+ ----------
23
+ roots : array_like of complex
24
+ Candidate roots for m at the given z.
25
+ z : complex
26
+ Evaluation point. The Stieltjes/Herglotz branch satisfies
27
+ sign(Im(m)) = sign(Im(z)) away from the real axis.
28
+ target : complex
29
+ Previous continuation value used to enforce continuity, or
30
+ target value.
31
+
32
+ Returns
33
+ -------
34
+ w : complex
35
+ Selected root corresponding to the Stieltjes branch.
36
+ """
37
+
38
+ z = complex(z)
39
+ roots = numpy.asarray(roots, dtype=numpy.complex128).ravel()
40
+
41
+ if roots.size == 0:
42
+ raise ValueError("roots must contain at least one candidate root.")
43
+
44
+ desired_sign = numpy.sign(z.imag)
45
+
46
+ # Apply a soft Herglotz sign filter: prefer roots with Im(w) having the
47
+ # same sign as Im(z), allowing tiny numerical violations near the axis.
48
+ imag_roots = numpy.imag(roots)
49
+
50
+ good = roots[numpy.sign(imag_roots) == desired_sign]
51
+ if good.size == 0:
52
+ good = roots[(imag_roots * desired_sign) > -1e-12]
53
+
54
+ candidates = good if good.size > 0 else roots
55
+ idx = int(numpy.argmin(numpy.abs(candidates - target)))
56
+ return candidates[idx]
57
+
58
+
59
+ # ==============
60
+ # stieltjes poly
61
+ # ==============
62
+
63
+ class StieltjesPoly(object):
64
+ """
65
+ Stieltjes-branch evaluator for an algebraic equation P(z, m) = 0.
66
+
67
+ This class represents the Stieltjes-branch solution m(z) of an algebraic
68
+ equation defined by a polynomial relation
69
+
70
+ P(z, m) = 0,
71
+
72
+ where P is a polynomial in z and m with monomial-basis coefficients.
73
+ The coefficient matrix ``a`` is fixed at construction time, and all
74
+ quantities depending only on ``a`` are precomputed. Evaluation at a
75
+ complex point ``z`` is performed via :meth:`evaluate`. The instance is
76
+ also callable; :meth:`__call__` supports scalar or vector inputs and
77
+ applies :meth:`evaluate` elementwise.
78
+
79
+ The Stieltjes branch is selected by initializing in the appropriate
80
+ half-plane using an asymptotic Stieltjes estimate and then performing
81
+ homotopy continuation along a straight-line path in the complex plane.
82
+
83
+ Parameters
84
+ ----------
85
+ a : ndarray, shape (L, K)
86
+ Coefficient matrix defining P(z, m) in the monomial basis. For fixed
87
+ z, the coefficients of the polynomial in m are assembled from powers
88
+ of z.
89
+ eps : float or None, optional
90
+ If Im(z) == 0, use z + i*eps as the boundary evaluation point.
91
+ If None and Im(z) == 0, eps is set to 1e-8 * max(1, |z|).
92
+ height : float, default = 2.0
93
+ Imaginary height used for the starting point z0 in the same
94
+ half-plane as the evaluation point.
95
+ steps : int, default = 100
96
+ Number of continuation steps along the homotopy path.
97
+ order : int, default = 15
98
+ Number of moments in Stieltjes estimate
99
+
100
+ Methods
101
+ -------
102
+ evaluate(z)
103
+ Evaluate the Stieltjes-branch solution m(z) at a single complex point.
104
+
105
+ __call__(z)
106
+ If ``z`` is scalar, returns ``evaluate(z, ...)``.
107
+ If ``z`` is array-like, returns an array of the same shape, where each
108
+ entry is computed by calling ``evaluate`` on the corresponding element.
109
+
110
+ Notes
111
+ -----
112
+ If an input ``z`` value is real (Im(z) == 0), the evaluation is interpreted
113
+ as a boundary value by replacing that element with z + i*eps. If ``eps`` is
114
+ None, eps is chosen per element as 1e-8 * max(1, |z|).
115
+ """
116
+
117
+ def __init__(self, a, eps=None, height=2.0, steps=100, order=15):
118
+ a = numpy.asarray(a)
119
+ if a.ndim != 2:
120
+ raise ValueError("a must be a 2D array.")
121
+
122
+ self.a = a
123
+ self.a_l, _ = a.shape
124
+ self.eps = eps
125
+ self.height = height
126
+ self.steps = steps
127
+ self.order = order
128
+
129
+ self.mom = AlgebraicStieltjesMoments(a)
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)
135
+
136
+ def _poly_coeffs_m(self, z_val):
137
+ z_powers = z_val ** numpy.arange(self.a_l)
138
+ return (z_powers @ self.a)[::-1]
139
+
140
+ def _poly_roots(self, z_val):
141
+ coeffs = numpy.asarray(self._poly_coeffs_m(z_val),
142
+ dtype=numpy.complex128)
143
+ return numpy.roots(coeffs)
144
+
145
+ def evaluate(self, z, eps=None, height=2.0, steps=100, order=15):
146
+ """
147
+ Evaluate the Stieltjes-branch solution m(z) at a single point.
148
+
149
+ Parameters are as in the original function, except ``a`` is fixed at
150
+ construction time.
151
+ """
152
+ z = complex(z)
153
+
154
+ if steps < 1:
155
+ raise ValueError("steps must be a positive integer.")
156
+
157
+ # Boundary-value interpretation on the real axis
158
+ if z.imag == 0.0:
159
+ if self.eps is None:
160
+ eps_loc = 1e-8 * max(1.0, abs(z))
161
+ else:
162
+ eps_loc = float(self.eps)
163
+ z_eval = z + 1j * eps_loc
164
+ else:
165
+ z_eval = z
166
+
167
+ half_sign = numpy.sign(z_eval.imag)
168
+ if half_sign == 0.0:
169
+ half_sign = 1.0
170
+
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)
176
+
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
183
+
184
+ # Initialize at z0
185
+ w_prev = select_root(self._poly_roots(z0), z0, target)
186
+
187
+ # Straight-line homotopy continuation
188
+ for tau in numpy.linspace(0.0, 1.0, int(self.steps) + 1)[1:]:
189
+ z_tau = z0 + tau * (z_eval - z0)
190
+ w_prev = select_root(self._poly_roots(z_tau), z_tau, w_prev)
191
+
192
+ return w_prev
193
+
194
+ def __call__(self, z, progress=False):
195
+ # Scalar fast-path
196
+ if numpy.isscalar(z):
197
+ return self.evaluate(z)
198
+
199
+ # Array-like: evaluate elementwise, preserving shape
200
+ z_arr = numpy.asarray(z)
201
+ out = numpy.empty(z_arr.shape, dtype=numpy.complex128)
202
+
203
+ # Iterate over indices so we can pass Python scalars into evaluate()
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:
209
+ out[idx] = self.evaluate(z_arr[idx])
210
+
211
+ return out
212
+
213
+
214
+ # def stieltjes_poly(z, a, eps=None, height=2., steps=100, order=15):
215
+ # """
216
+ # Evaluate the Stieltjes-branch solution m(z) of an algebraic equation.
217
+
218
+ # The coefficients `a` define a polynomial relation
219
+ # P(z, m) = 0,
220
+ # where P is a polynomial in z and m with monomial-basis coefficients
221
+ # arranged so that for fixed z, the coefficients of the polynomial in m
222
+ # can be assembled from powers of z.
223
+
224
+ # Parameters
225
+ # ----------
226
+ # z : complex
227
+ # Evaluation point. Must be a single value.
228
+ # a : ndarray, shape (L, K)
229
+ # Coefficient matrix defining P(z, m) in the monomial basis.
230
+ # eps : float or None, optional
231
+ # If Im(z) == 0, use z + i*eps as the boundary evaluation point.
232
+ # If None and Im(z) == 0, eps is set to 1e-8 * max(1, |z|).
233
+ # height : float, default = 2.0
234
+ # Imaginary height used for the starting point z0 in the same
235
+ # half-plane as the evaluation point.
236
+ # steps : int, default = 100
237
+ # Number of continuation steps along the homotopy path.
238
+ # order : int, default = 15
239
+ # Number of moments in Stieltjes estimate
240
+
241
+ # Returns
242
+ # -------
243
+ # w : complex
244
+ # Value of the Stieltjes-branch solution m(z) (or m(z+i*eps) if z is
245
+ # real).
246
+ # """
247
+
248
+ # z = complex(z)
249
+ # a = numpy.asarray(a)
250
+
251
+ # if a.ndim != 2:
252
+ # raise ValueError('a must be a 2D array.')
253
+
254
+ # if steps < 1:
255
+ # raise ValueError("steps must be a positive integer.")
256
+
257
+ # a_l, _ = a.shape
258
+ # mom = AlgebraicStieltjesMoments(a)
259
+
260
+ # def poly_coeffs_m(z_val):
261
+ # z_powers = z_val ** numpy.arange(a_l)
262
+ # return (z_powers @ a)[::-1]
263
+
264
+ # def poly_roots(z_val):
265
+ # coeffs = numpy.asarray(poly_coeffs_m(z_val), dtype=numpy.complex128)
266
+ # return numpy.roots(coeffs)
267
+
268
+ # # If user asked for a real-axis value, interpret as boundary value from C+.
269
+ # if z.imag == 0.0:
270
+ # if eps is None:
271
+ # eps = 1e-8 * max(1.0, abs(z))
272
+ # z_eval = z + 1j * float(eps)
273
+ # else:
274
+ # z_eval = z
275
+
276
+ # half_sign = numpy.sign(z_eval.imag)
277
+ # if half_sign == 0.0:
278
+ # half_sign = 1.0
279
+
280
+ # z0 = 1j * float(half_sign) * (1. + height * mom.radius(order))
281
+ # target = mom.stieltjes(z0, order)
282
+
283
+ # # Initialize at z0 via asymptotic / Im-sign selection.
284
+ # w_prev = select_root(poly_roots(z0), z0, target)
285
+
286
+ # # Straight-line homotopy from z0 to z_eval.
287
+ # for tau in numpy.linspace(0.0, 1.0, int(steps) + 1)[1:]:
288
+ # z_tau = z0 + tau * (z_eval - z0)
289
+ # w_prev = select_root(poly_roots(z_tau), z_tau, w_prev)
290
+
291
+ # return w_prev