freealg 0.1.11__py3-none-any.whl → 0.7.12__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.
- freealg/__init__.py +8 -2
- freealg/__version__.py +1 -1
- freealg/_algebraic_form/__init__.py +12 -0
- freealg/_algebraic_form/_branch_points.py +288 -0
- freealg/_algebraic_form/_constraints.py +139 -0
- freealg/_algebraic_form/_continuation_algebraic.py +706 -0
- freealg/_algebraic_form/_decompress.py +641 -0
- freealg/_algebraic_form/_decompress2.py +204 -0
- freealg/_algebraic_form/_edge.py +330 -0
- freealg/_algebraic_form/_homotopy.py +323 -0
- freealg/_algebraic_form/_moments.py +448 -0
- freealg/_algebraic_form/_sheets_util.py +145 -0
- freealg/_algebraic_form/_support.py +309 -0
- freealg/_algebraic_form/algebraic_form.py +1232 -0
- freealg/_free_form/__init__.py +16 -0
- freealg/{_chebyshev.py → _free_form/_chebyshev.py} +75 -43
- freealg/_free_form/_decompress.py +993 -0
- freealg/_free_form/_density_util.py +243 -0
- freealg/_free_form/_jacobi.py +359 -0
- freealg/_free_form/_linalg.py +508 -0
- freealg/{_pade.py → _free_form/_pade.py} +42 -208
- freealg/{_plot_util.py → _free_form/_plot_util.py} +37 -22
- freealg/{_sample.py → _free_form/_sample.py} +58 -22
- freealg/_free_form/_series.py +454 -0
- freealg/_free_form/_support.py +214 -0
- freealg/_free_form/free_form.py +1362 -0
- freealg/_geometric_form/__init__.py +13 -0
- freealg/_geometric_form/_continuation_genus0.py +175 -0
- freealg/_geometric_form/_continuation_genus1.py +275 -0
- freealg/_geometric_form/_elliptic_functions.py +174 -0
- freealg/_geometric_form/_sphere_maps.py +63 -0
- freealg/_geometric_form/_torus_maps.py +118 -0
- freealg/_geometric_form/geometric_form.py +1094 -0
- freealg/_util.py +56 -110
- freealg/distributions/__init__.py +7 -1
- freealg/distributions/_chiral_block.py +494 -0
- freealg/distributions/_deformed_marchenko_pastur.py +726 -0
- freealg/distributions/_deformed_wigner.py +386 -0
- freealg/distributions/_kesten_mckay.py +29 -15
- freealg/distributions/_marchenko_pastur.py +224 -95
- freealg/distributions/_meixner.py +47 -37
- freealg/distributions/_wachter.py +29 -17
- freealg/distributions/_wigner.py +27 -14
- freealg/visualization/__init__.py +12 -0
- freealg/visualization/_glue_util.py +32 -0
- freealg/visualization/_rgb_hsv.py +125 -0
- freealg-0.7.12.dist-info/METADATA +172 -0
- freealg-0.7.12.dist-info/RECORD +53 -0
- {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/WHEEL +1 -1
- freealg/_decompress.py +0 -180
- freealg/_jacobi.py +0 -218
- freealg/_support.py +0 -85
- freealg/freeform.py +0 -967
- freealg-0.1.11.dist-info/METADATA +0 -140
- freealg-0.1.11.dist-info/RECORD +0 -24
- /freealg/{_damp.py → _free_form/_damp.py} +0 -0
- {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/top_level.txt +0 -0
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from .._plot_util import plot_density, plot_hilbert,
|
|
17
|
-
plot_stieltjes_on_disk, plot_samples
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
|
+
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
|
+
from ..visualization import glue_branches
|
|
18
19
|
|
|
19
20
|
try:
|
|
20
21
|
from scipy.integrate import cumtrapz
|
|
@@ -94,21 +95,26 @@ class MarchenkoPastur(object):
|
|
|
94
95
|
# init
|
|
95
96
|
# ====
|
|
96
97
|
|
|
97
|
-
def __init__(self, lam):
|
|
98
|
+
def __init__(self, lam, sigma=1.0):
|
|
98
99
|
"""
|
|
99
100
|
Initialization.
|
|
100
101
|
"""
|
|
101
102
|
|
|
102
103
|
self.lam = lam
|
|
103
|
-
self.
|
|
104
|
-
|
|
105
|
-
self.
|
|
104
|
+
self.sigma = sigma
|
|
105
|
+
|
|
106
|
+
# self.lam_p = (1 + numpy.sqrt(self.lam))**2
|
|
107
|
+
# self.lam_m = (1 - numpy.sqrt(self.lam))**2
|
|
108
|
+
self.lam_p = sigma**2 * (1.0 + numpy.sqrt(lam))**2
|
|
109
|
+
self.lam_m = sigma**2 * (1.0 - numpy.sqrt(lam))**2
|
|
110
|
+
|
|
111
|
+
self.supp = (self.lam_m, self.lam_p)
|
|
106
112
|
|
|
107
113
|
# =======
|
|
108
114
|
# density
|
|
109
115
|
# =======
|
|
110
116
|
|
|
111
|
-
def density(self, x=None, plot=False, latex=False, save=False):
|
|
117
|
+
def density(self, x=None, plot=False, latex=False, save=False, eig=None):
|
|
112
118
|
"""
|
|
113
119
|
Density of distribution.
|
|
114
120
|
|
|
@@ -117,7 +123,7 @@ class MarchenkoPastur(object):
|
|
|
117
123
|
|
|
118
124
|
x : numpy.array, default=None
|
|
119
125
|
The locations where density is evaluated at. If `None`, an interval
|
|
120
|
-
slightly larger than the
|
|
126
|
+
slightly larger than the supp interval of the spectral density
|
|
121
127
|
is used.
|
|
122
128
|
|
|
123
129
|
rho : numpy.array, default=None
|
|
@@ -132,9 +138,13 @@ class MarchenkoPastur(object):
|
|
|
132
138
|
|
|
133
139
|
save : bool, default=False
|
|
134
140
|
If not `False`, the plot is saved. If a string is given, it is
|
|
135
|
-
|
|
141
|
+
assumed to the save filename (with the file extension). This option
|
|
136
142
|
is relevant only if ``plot=True``.
|
|
137
143
|
|
|
144
|
+
eig : numpy.array, default=None
|
|
145
|
+
A collection of eigenvalues to compare to via histogram. This
|
|
146
|
+
option is relevant only if ``plot=True``.
|
|
147
|
+
|
|
138
148
|
Returns
|
|
139
149
|
-------
|
|
140
150
|
|
|
@@ -164,17 +174,42 @@ class MarchenkoPastur(object):
|
|
|
164
174
|
x_max = numpy.ceil(center + radius * scale)
|
|
165
175
|
x = numpy.linspace(x_min, x_max, 500)
|
|
166
176
|
|
|
177
|
+
# Unpack parameters
|
|
178
|
+
lam = self.lam
|
|
179
|
+
lam_p = self.lam_p
|
|
180
|
+
lam_m = self.lam_m
|
|
181
|
+
sigma = self.sigma
|
|
182
|
+
|
|
167
183
|
rho = numpy.zeros_like(x)
|
|
168
|
-
mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
|
|
184
|
+
# mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
|
|
185
|
+
mask = (x > lam_m) & (x < lam_p)
|
|
186
|
+
|
|
187
|
+
# rho[mask] = (1.0 / (2.0 * numpy.pi * x[mask] * self.lam)) * \
|
|
188
|
+
# numpy.sqrt((self.lam_p - x[mask]) * (x[mask] - self.lam_m))
|
|
169
189
|
|
|
170
|
-
rho[mask] =
|
|
171
|
-
|
|
190
|
+
rho[mask] = numpy.sqrt((lam_p - x[mask]) * (x[mask] - lam_m)) / \
|
|
191
|
+
(lam * x[mask] * 2 * numpy.pi * sigma**2)
|
|
172
192
|
|
|
173
193
|
if plot:
|
|
174
|
-
|
|
194
|
+
if eig is not None:
|
|
195
|
+
label = 'Theoretical'
|
|
196
|
+
else:
|
|
197
|
+
label = ''
|
|
198
|
+
plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
|
|
175
199
|
|
|
176
200
|
return rho
|
|
177
201
|
|
|
202
|
+
# =======
|
|
203
|
+
# support
|
|
204
|
+
# =======
|
|
205
|
+
|
|
206
|
+
def support(self):
|
|
207
|
+
"""
|
|
208
|
+
supp
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
return [self.supp]
|
|
212
|
+
|
|
178
213
|
# =======
|
|
179
214
|
# hilbert
|
|
180
215
|
# =======
|
|
@@ -188,7 +223,7 @@ class MarchenkoPastur(object):
|
|
|
188
223
|
|
|
189
224
|
x : numpy.array, default=None
|
|
190
225
|
The locations where Hilbert transform is evaluated at. If `None`,
|
|
191
|
-
an interval slightly larger than the
|
|
226
|
+
an interval slightly larger than the supp interval of the
|
|
192
227
|
spectral density is used.
|
|
193
228
|
|
|
194
229
|
plot : bool, default=False
|
|
@@ -249,7 +284,7 @@ class MarchenkoPastur(object):
|
|
|
249
284
|
hilb = -hilb
|
|
250
285
|
|
|
251
286
|
if plot:
|
|
252
|
-
plot_hilbert(x, hilb, support=self.
|
|
287
|
+
plot_hilbert(x, hilb, support=self.supp, latex=latex, save=save)
|
|
253
288
|
|
|
254
289
|
return hilb
|
|
255
290
|
|
|
@@ -257,59 +292,126 @@ class MarchenkoPastur(object):
|
|
|
257
292
|
# m mp numeric vectorized
|
|
258
293
|
# =======================
|
|
259
294
|
|
|
260
|
-
def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
|
|
295
|
+
# def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
|
|
296
|
+
# """
|
|
297
|
+
# Stieltjes transform (principal or secondary branch)
|
|
298
|
+
# for Marchenko-Pastur distribution on upper half-plane.
|
|
299
|
+
# """
|
|
300
|
+
#
|
|
301
|
+
# sigma = 1.0
|
|
302
|
+
# m = numpy.empty_like(z, dtype=complex)
|
|
303
|
+
#
|
|
304
|
+
# # When z is too small, do not use quadratic form.
|
|
305
|
+
# mask = numpy.abs(z) < tol
|
|
306
|
+
# m[mask] = 1 / (sigma**2 * (1 - self.lam))
|
|
307
|
+
#
|
|
308
|
+
# # Use quadratic form
|
|
309
|
+
# not_mask = ~mask
|
|
310
|
+
# if numpy.any(not_mask):
|
|
311
|
+
#
|
|
312
|
+
# sign = -1 if alt_branch else 1
|
|
313
|
+
# A = self.lam * sigma**2 * z[not_mask]
|
|
314
|
+
# B = z[not_mask] - sigma**2 * (1 - self.lam)
|
|
315
|
+
# D = B**2 - 4 * A
|
|
316
|
+
# sqrtD = numpy.sqrt(D)
|
|
317
|
+
# m1 = (-B + sqrtD) / (2 * A)
|
|
318
|
+
# m2 = (-B - sqrtD) / (2 * A)
|
|
319
|
+
#
|
|
320
|
+
# # pick correct branch only for non-masked entries
|
|
321
|
+
# upper = z[not_mask].imag >= 0
|
|
322
|
+
# branch = numpy.empty_like(m1)
|
|
323
|
+
# branch[upper] = numpy.where(sign*m1[upper].imag > 0, m1[upper],
|
|
324
|
+
# m2[upper])
|
|
325
|
+
# branch[~upper] = numpy.where(sign*m1[~upper].imag < 0,
|
|
326
|
+
# m1[~upper], m2[~upper])
|
|
327
|
+
# m[not_mask] = branch
|
|
328
|
+
#
|
|
329
|
+
# return m
|
|
330
|
+
|
|
331
|
+
# =============
|
|
332
|
+
# sqrt pos imag
|
|
333
|
+
# =============
|
|
334
|
+
|
|
335
|
+
def _sqrt_pos_imag(self, z):
|
|
261
336
|
"""
|
|
262
|
-
|
|
263
|
-
for Marchenko–Pastur distribution on upper half-plane.
|
|
337
|
+
Square root on a branch cut with always positive imaginary part.
|
|
264
338
|
"""
|
|
265
339
|
|
|
266
|
-
|
|
267
|
-
|
|
340
|
+
sq = numpy.sqrt(z)
|
|
341
|
+
sq = numpy.where(sq.imag < 0, -sq, sq)
|
|
268
342
|
|
|
269
|
-
|
|
270
|
-
mask = numpy.abs(z) < tol
|
|
271
|
-
m[mask] = 1 / (sigma**2 * (1 - self.lam))
|
|
272
|
-
|
|
273
|
-
# Use quadratic form
|
|
274
|
-
not_mask = ~mask
|
|
275
|
-
if numpy.any(not_mask):
|
|
276
|
-
|
|
277
|
-
sign = -1 if alt_branch else 1
|
|
278
|
-
A = self.lam * sigma**2 * z
|
|
279
|
-
B = z - sigma**2 * (1 - self.lam)
|
|
280
|
-
D = B**2 - 4 * A
|
|
281
|
-
sqrtD = numpy.sqrt(D)
|
|
282
|
-
m1 = (-B + sqrtD) / (2 * A)
|
|
283
|
-
m2 = (-B - sqrtD) / (2 * A)
|
|
284
|
-
|
|
285
|
-
# pick correct branch only for non‑masked entries
|
|
286
|
-
upper = z[not_mask].imag >= 0
|
|
287
|
-
branch = numpy.empty_like(m1)
|
|
288
|
-
branch[upper] = numpy.where(sign*m1[upper].imag > 0, m1[upper],
|
|
289
|
-
m2[upper])
|
|
290
|
-
branch[~upper] = numpy.where(sign*m1[~upper].imag < 0, m1[~upper],
|
|
291
|
-
m2[~upper])
|
|
292
|
-
m[not_mask] = branch
|
|
293
|
-
|
|
294
|
-
return m
|
|
343
|
+
return sq
|
|
295
344
|
|
|
296
345
|
# ============
|
|
297
346
|
# m mp reflect
|
|
298
347
|
# ============
|
|
299
348
|
|
|
300
|
-
def _m_mp_reflect(self, z, alt_branch=False):
|
|
349
|
+
# def _m_mp_reflect(self, z, alt_branch=False):
|
|
350
|
+
# """
|
|
351
|
+
# Analytic continuation using Schwarz reflection.
|
|
352
|
+
# """
|
|
353
|
+
#
|
|
354
|
+
# mask_p = z.imag >= 0.0
|
|
355
|
+
# mask_n = z.imag < 0.0
|
|
356
|
+
#
|
|
357
|
+
# m = numpy.zeros_like(z)
|
|
358
|
+
#
|
|
359
|
+
# f = self._m_mp_numeric_vectorized
|
|
360
|
+
# m[mask_p] = f(z[mask_p], alt_branch=False)
|
|
361
|
+
# m[mask_n] = f(z[mask_n], alt_branch=alt_branch)
|
|
362
|
+
#
|
|
363
|
+
# return m
|
|
364
|
+
|
|
365
|
+
# ================
|
|
366
|
+
# stieltjes branch
|
|
367
|
+
# ================
|
|
368
|
+
|
|
369
|
+
def _stieltjes_branch(self, z, alt_branch=False, tol=1e-8):
|
|
301
370
|
"""
|
|
302
|
-
Analytic continuation using Schwarz reflection.
|
|
303
371
|
"""
|
|
304
372
|
|
|
305
|
-
|
|
306
|
-
|
|
373
|
+
# Unpack parameters
|
|
374
|
+
lam = self.lam
|
|
375
|
+
sigma = self.sigma
|
|
376
|
+
|
|
377
|
+
z = numpy.asarray(z, dtype=complex)
|
|
378
|
+
m = numpy.empty_like(z, dtype=complex)
|
|
379
|
+
|
|
380
|
+
def _eval_upper(zu):
|
|
381
|
+
mu = numpy.empty_like(zu, dtype=complex)
|
|
382
|
+
|
|
383
|
+
mask = numpy.abs(zu) < tol
|
|
384
|
+
if numpy.any(mask):
|
|
385
|
+
if alt_branch:
|
|
386
|
+
mu[mask] = numpy.inf + 0.0j
|
|
387
|
+
else:
|
|
388
|
+
mu[mask] = 1.0 / (sigma**2 * (1.0 - lam))
|
|
389
|
+
|
|
390
|
+
not_mask = ~mask
|
|
391
|
+
if numpy.any(not_mask):
|
|
392
|
+
sign = -1 if alt_branch else 1
|
|
307
393
|
|
|
308
|
-
|
|
394
|
+
A = lam * sigma**2 * zu[not_mask]
|
|
395
|
+
B = zu[not_mask] - sigma**2 * (1.0 - lam)
|
|
396
|
+
D = B**2 - 4.0 * A
|
|
309
397
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
398
|
+
sqrtD = self._sqrt_pos_imag(D)
|
|
399
|
+
|
|
400
|
+
r1 = (-B + sqrtD) / (2.0 * A)
|
|
401
|
+
r2 = (-B - sqrtD) / (2.0 * A)
|
|
402
|
+
|
|
403
|
+
mu[not_mask] = numpy.where(sign * r1.imag > 0.0, r1, r2)
|
|
404
|
+
|
|
405
|
+
return mu
|
|
406
|
+
|
|
407
|
+
mask_p = numpy.imag(z) >= 0.0
|
|
408
|
+
if numpy.any(mask_p):
|
|
409
|
+
m[mask_p] = _eval_upper(z[mask_p])
|
|
410
|
+
|
|
411
|
+
mask_n = ~mask_p
|
|
412
|
+
if numpy.any(mask_n):
|
|
413
|
+
z_ref = numpy.conjugate(z[mask_n])
|
|
414
|
+
m[mask_n] = numpy.conjugate(_eval_upper(z_ref))
|
|
313
415
|
|
|
314
416
|
return m
|
|
315
417
|
|
|
@@ -317,8 +419,8 @@ class MarchenkoPastur(object):
|
|
|
317
419
|
# stieltjes
|
|
318
420
|
# =========
|
|
319
421
|
|
|
320
|
-
def stieltjes(self,
|
|
321
|
-
save=False):
|
|
422
|
+
def stieltjes(self, z=None, x=None, y=None, alt_branch='both', plot=False,
|
|
423
|
+
on_disk=False, latex=False, save=False):
|
|
322
424
|
"""
|
|
323
425
|
Stieltjes transform of distribution.
|
|
324
426
|
|
|
@@ -327,13 +429,17 @@ class MarchenkoPastur(object):
|
|
|
327
429
|
|
|
328
430
|
x : numpy.array, default=None
|
|
329
431
|
The x axis of the grid where the Stieltjes transform is evaluated.
|
|
330
|
-
If `None`, an interval slightly larger than the
|
|
432
|
+
If `None`, an interval slightly larger than the supp interval of
|
|
331
433
|
the spectral density is used.
|
|
332
434
|
|
|
333
435
|
y : numpy.array, default=None
|
|
334
436
|
The y axis of the grid where the Stieltjes transform is evaluated.
|
|
335
437
|
If `None`, a grid on the interval ``[-1, 1]`` is used.
|
|
336
438
|
|
|
439
|
+
alt_branch : {``True``, ``False``, ``'both'``} default=``'both'``
|
|
440
|
+
If `True`, returns non-physical branch. If `False`, returns
|
|
441
|
+
physical branch. If ``'both'``, returns both.
|
|
442
|
+
|
|
337
443
|
plot : bool, default=False
|
|
338
444
|
If `True`, Stieltjes transform is plotted.
|
|
339
445
|
|
|
@@ -399,38 +505,57 @@ class MarchenkoPastur(object):
|
|
|
399
505
|
# Cayley transform mapping zeta on D to z on H
|
|
400
506
|
z_H = 1j * (1 + zeta) / (1 - zeta)
|
|
401
507
|
|
|
402
|
-
m1_D = self._m_mp_reflect(z_H, alt_branch=False)
|
|
403
|
-
m2_D = self._m_mp_reflect(z_H, alt_branch=True)
|
|
508
|
+
# m1_D = self._m_mp_reflect(z_H, alt_branch=False)
|
|
509
|
+
# m2_D = self._m_mp_reflect(z_H, alt_branch=True)
|
|
510
|
+
m1_D = self._stieltjes_branch(z_H, alt_branch=False)
|
|
511
|
+
m2_D = self._stieltjes_branch(z_H, alt_branch=True)
|
|
512
|
+
m12_D = glue_branches(z_H, m1_D, m2_D)
|
|
404
513
|
|
|
405
|
-
plot_stieltjes_on_disk(r, t, m1_D,
|
|
514
|
+
plot_stieltjes_on_disk(r, t, m1_D, m12_D, support=self.supp,
|
|
406
515
|
latex=latex, save=save)
|
|
407
516
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
x
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
517
|
+
if alt_branch == 'both':
|
|
518
|
+
return m1_D, m2_D
|
|
519
|
+
elif alt_branch is True:
|
|
520
|
+
return m2_D
|
|
521
|
+
else:
|
|
522
|
+
return m1_D
|
|
523
|
+
|
|
524
|
+
if z is None:
|
|
525
|
+
# Create x if not given
|
|
526
|
+
if x is None:
|
|
527
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
528
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
529
|
+
scale = 2.0
|
|
530
|
+
x_min = numpy.floor(
|
|
531
|
+
2.0 * (center - 2.0 * radius * scale)) / 2.0
|
|
532
|
+
x_max = numpy.ceil(
|
|
533
|
+
2.0 * (center + 2.0 * radius * scale)) / 2.0
|
|
534
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
535
|
+
|
|
536
|
+
# Create y if not given
|
|
537
|
+
if y is None:
|
|
538
|
+
y = numpy.linspace(-1, 1, 400)
|
|
539
|
+
|
|
540
|
+
x_grid, y_grid = numpy.meshgrid(x, y)
|
|
541
|
+
z = x_grid + 1j * y_grid # shape (Ny, Nx)
|
|
542
|
+
|
|
543
|
+
# m1 = self._m_mp_reflect(z, alt_branch=False)
|
|
544
|
+
# m2 = self._m_mp_reflect(z, alt_branch=True)
|
|
545
|
+
m1 = self._stieltjes_branch(z, alt_branch=False)
|
|
546
|
+
m2 = self._stieltjes_branch(z, alt_branch=True)
|
|
428
547
|
|
|
429
548
|
if plot:
|
|
430
|
-
|
|
549
|
+
m12 = glue_branches(z, m1, m2)
|
|
550
|
+
plot_stieltjes(x, y, m1, m12, support=self.supp, latex=latex,
|
|
431
551
|
save=save)
|
|
432
552
|
|
|
433
|
-
|
|
553
|
+
if alt_branch == 'both':
|
|
554
|
+
return m1, m2
|
|
555
|
+
elif alt_branch is True:
|
|
556
|
+
return m2
|
|
557
|
+
else:
|
|
558
|
+
return m1
|
|
434
559
|
|
|
435
560
|
# ======
|
|
436
561
|
# sample
|
|
@@ -448,11 +573,11 @@ class MarchenkoPastur(object):
|
|
|
448
573
|
Size of sample.
|
|
449
574
|
|
|
450
575
|
x_min : float, default=None
|
|
451
|
-
Minimum of sample values. If `None`, the left edge of the
|
|
576
|
+
Minimum of sample values. If `None`, the left edge of the supp
|
|
452
577
|
is used.
|
|
453
578
|
|
|
454
579
|
x_max : float, default=None
|
|
455
|
-
Maximum of sample values. If `None`, the right edge of the
|
|
580
|
+
Maximum of sample values. If `None`, the right edge of the supp
|
|
456
581
|
is used.
|
|
457
582
|
|
|
458
583
|
method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
|
|
@@ -501,9 +626,6 @@ class MarchenkoPastur(object):
|
|
|
501
626
|
:class: custom-dark
|
|
502
627
|
"""
|
|
503
628
|
|
|
504
|
-
if seed is not None:
|
|
505
|
-
numpy.random.seed(seed)
|
|
506
|
-
|
|
507
629
|
if x_min is None:
|
|
508
630
|
x_min = self.lam_m
|
|
509
631
|
|
|
@@ -522,14 +644,23 @@ class MarchenkoPastur(object):
|
|
|
522
644
|
inv_cdf = interp1d(cdf, xs, bounds_error=False,
|
|
523
645
|
fill_value=(x_min, x_max))
|
|
524
646
|
|
|
647
|
+
# Random generator
|
|
648
|
+
rng = numpy.random.default_rng(seed)
|
|
649
|
+
|
|
525
650
|
# Draw from uniform distribution
|
|
526
651
|
if method == 'mc':
|
|
527
|
-
u =
|
|
652
|
+
u = rng.random(size)
|
|
653
|
+
|
|
528
654
|
elif method == 'qmc':
|
|
529
|
-
|
|
530
|
-
|
|
655
|
+
try:
|
|
656
|
+
engine = qmc.Halton(d=1, scramble=True, rng=rng)
|
|
657
|
+
except TypeError:
|
|
658
|
+
# Older scipy versions
|
|
659
|
+
engine = qmc.Halton(d=1, scramble=True, seed=rng)
|
|
660
|
+
u = engine.random(size).ravel()
|
|
661
|
+
|
|
531
662
|
else:
|
|
532
|
-
raise
|
|
663
|
+
raise NotImplementedError('"method" is invalid.')
|
|
533
664
|
|
|
534
665
|
# Draw from distribution by mapping from inverse CDF
|
|
535
666
|
samples = inv_cdf(u).ravel()
|
|
@@ -579,14 +710,12 @@ class MarchenkoPastur(object):
|
|
|
579
710
|
>>> A = mp.matrix(2000)
|
|
580
711
|
"""
|
|
581
712
|
|
|
582
|
-
if seed is not None:
|
|
583
|
-
numpy.random.seed(seed)
|
|
584
|
-
|
|
585
713
|
# Parameters
|
|
586
714
|
m = int(size / self.lam)
|
|
587
715
|
|
|
588
716
|
# Generate random matrix X (n x m) with i.i.d. standard normal entries.
|
|
589
|
-
|
|
717
|
+
rng = numpy.random.default_rng(seed)
|
|
718
|
+
X = rng.standard_normal((size, m))
|
|
590
719
|
|
|
591
720
|
# Form the sample covariance matrix A = (1/m)*XX^T.
|
|
592
721
|
A = X @ X.T / m
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from .._plot_util import plot_density, plot_hilbert,
|
|
17
|
-
plot_stieltjes_on_disk, plot_samples
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
|
+
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
18
|
|
|
19
19
|
try:
|
|
20
20
|
from scipy.integrate import cumtrapz
|
|
@@ -83,7 +83,7 @@ class Meixner(object):
|
|
|
83
83
|
|
|
84
84
|
.. [1] Saitoh, N. & Yosnida, M. (2001). The infinite divisibility and
|
|
85
85
|
orthogonal polynomials with a constant recursion formula in free
|
|
86
|
-
probability theory. Probab. Math. Statist., 21, 159
|
|
86
|
+
probability theory. Probab. Math. Statist., 21, 159-170.
|
|
87
87
|
|
|
88
88
|
Examples
|
|
89
89
|
--------
|
|
@@ -114,7 +114,7 @@ class Meixner(object):
|
|
|
114
114
|
# density
|
|
115
115
|
# =======
|
|
116
116
|
|
|
117
|
-
def density(self, x=None, plot=False, latex=False, save=False):
|
|
117
|
+
def density(self, x=None, plot=False, latex=False, save=False, eig=None):
|
|
118
118
|
"""
|
|
119
119
|
Density of distribution.
|
|
120
120
|
|
|
@@ -141,6 +141,10 @@ class Meixner(object):
|
|
|
141
141
|
assumed to the save filename (with the file extension). This option
|
|
142
142
|
is relevant only if ``plot=True``.
|
|
143
143
|
|
|
144
|
+
eig : numpy.array, default=None
|
|
145
|
+
A collection of eigenvalues to compare to via histogram. This
|
|
146
|
+
option is relevant only if ``plot=True``.
|
|
147
|
+
|
|
144
148
|
Returns
|
|
145
149
|
-------
|
|
146
150
|
|
|
@@ -173,22 +177,20 @@ class Meixner(object):
|
|
|
173
177
|
rho = numpy.zeros_like(x)
|
|
174
178
|
mask = numpy.logical_and(x > self.lam_m, x < self.lam_p)
|
|
175
179
|
|
|
176
|
-
# rho[mask] = \
|
|
177
|
-
# numpy.sqrt(4.0 * (1.0 + self.b) - (x[mask] - self.a)**2) / \
|
|
178
|
-
# (2.0 * numpy.pi * (self.b * x[mask]**2 + self.a * x[mask] + 1))
|
|
179
|
-
|
|
180
180
|
numer = numpy.zeros_like(x)
|
|
181
181
|
denom = numpy.ones_like(x)
|
|
182
182
|
numer[mask] = self.c * numpy.sqrt(4.0 * self.b - (x[mask] - self.a)**2)
|
|
183
|
-
denom[mask] =
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
denom[mask] *= 2 * numpy.pi
|
|
187
|
-
|
|
183
|
+
denom[mask] = 2.0 * numpy.pi * (
|
|
184
|
+
(1.0 - self.c) * x[mask]**2 + self.a * self.c * x[mask] +
|
|
185
|
+
self.b * self.c**2)
|
|
188
186
|
rho[mask] = numer[mask] / denom[mask]
|
|
189
187
|
|
|
190
188
|
if plot:
|
|
191
|
-
|
|
189
|
+
if eig is not None:
|
|
190
|
+
label = 'Theoretical'
|
|
191
|
+
else:
|
|
192
|
+
label = ''
|
|
193
|
+
plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
|
|
192
194
|
|
|
193
195
|
return rho
|
|
194
196
|
|
|
@@ -252,14 +254,14 @@ class Meixner(object):
|
|
|
252
254
|
def _P(x):
|
|
253
255
|
# denom = 1.0 + self.b
|
|
254
256
|
# return ((1.0 + 2.0 * self.b) * x + self.a) / denom
|
|
255
|
-
P = (
|
|
257
|
+
P = (self.c - 2.0) * x - self.a * self.c
|
|
256
258
|
return P
|
|
257
259
|
|
|
258
260
|
def _Q(x):
|
|
259
261
|
# denom = 1.0 + self.b
|
|
260
262
|
# return (self.b * x**2 + self.a * x + 1.0) / denom
|
|
261
|
-
Q = (
|
|
262
|
-
self.b * self.c**2
|
|
263
|
+
Q = (1.0 - self.c) * x**2 + self.a * self.c * x + \
|
|
264
|
+
self.b * self.c**2
|
|
263
265
|
return Q
|
|
264
266
|
|
|
265
267
|
P = _P(x)
|
|
@@ -269,9 +271,6 @@ class Meixner(object):
|
|
|
269
271
|
sign = numpy.sign(P)
|
|
270
272
|
hilb = (P - sign * Delta) / (2.0 * Q)
|
|
271
273
|
|
|
272
|
-
# using negative sign convention
|
|
273
|
-
hilb = -hilb
|
|
274
|
-
|
|
275
274
|
if plot:
|
|
276
275
|
plot_hilbert(x, hilb, support=self.support, latex=latex, save=save)
|
|
277
276
|
|
|
@@ -291,23 +290,28 @@ class Meixner(object):
|
|
|
291
290
|
# denom = 1.0 + self.b
|
|
292
291
|
# A = (self.b * z**2 + self.a * z + 1.0) / denom
|
|
293
292
|
# B = ((1.0 + 2.0 * self.b) * z + self.a) / denom
|
|
294
|
-
A = ((1.0 - self.c) * z**2 + self.a * self.c * z +
|
|
295
|
-
|
|
296
|
-
B = ((self.c - 2.0) * z - self.a * self.c) / 2.0
|
|
293
|
+
# A = ((1.0 - self.c) * z**2 + self.a * self.c * z +
|
|
294
|
+
# self.b * self.c**2) / 4.0
|
|
295
|
+
# B = ((self.c - 2.0) * z - self.a * self.c) / 2.0
|
|
296
|
+
|
|
297
|
+
Q = (1.0 - self.c) * z**2 + self.a * self.c * z + \
|
|
298
|
+
self.b * self.c**2
|
|
299
|
+
P = (self.c - 2.0) * z - self.a * self.c
|
|
297
300
|
|
|
298
301
|
# D = B**2 - 4 * A
|
|
299
302
|
# sqrtD = numpy.sqrt(D)
|
|
300
303
|
|
|
301
304
|
# Avoid numpy picking the wrong branch
|
|
302
|
-
d = 2 * numpy.sqrt(1.0 + self.b)
|
|
303
|
-
r_min = self.a - d
|
|
304
|
-
r_max = self.a + d
|
|
305
|
-
sqrtD = numpy.sqrt(z - r_min) * numpy.sqrt(z - r_max)
|
|
305
|
+
# d = 2 * numpy.sqrt(1.0 + self.b)
|
|
306
|
+
# r_min = self.a - d
|
|
307
|
+
# r_max = self.a + d
|
|
308
|
+
# sqrtD = numpy.sqrt(z - r_min) * numpy.sqrt(z - r_max)
|
|
309
|
+
sqrtD = numpy.sqrt(P**2 - 4.0 * Q)
|
|
306
310
|
|
|
307
|
-
m1 = (
|
|
308
|
-
m2 = (
|
|
311
|
+
m1 = (P + sqrtD) / (2 * Q)
|
|
312
|
+
m2 = (P - sqrtD) / (2 * Q)
|
|
309
313
|
|
|
310
|
-
# pick correct branch only for non
|
|
314
|
+
# pick correct branch only for non-masked entries
|
|
311
315
|
upper = z.imag >= 0
|
|
312
316
|
branch = numpy.empty_like(m1)
|
|
313
317
|
branch[upper] = numpy.where(
|
|
@@ -526,9 +530,6 @@ class Meixner(object):
|
|
|
526
530
|
:class: custom-dark
|
|
527
531
|
"""
|
|
528
532
|
|
|
529
|
-
if seed is not None:
|
|
530
|
-
numpy.random.seed(seed)
|
|
531
|
-
|
|
532
533
|
if x_min is None:
|
|
533
534
|
x_min = self.lam_m
|
|
534
535
|
|
|
@@ -547,14 +548,23 @@ class Meixner(object):
|
|
|
547
548
|
inv_cdf = interp1d(cdf, xs, bounds_error=False,
|
|
548
549
|
fill_value=(x_min, x_max))
|
|
549
550
|
|
|
551
|
+
# Random generator
|
|
552
|
+
rng = numpy.random.default_rng(seed)
|
|
553
|
+
|
|
550
554
|
# Draw from uniform distribution
|
|
551
555
|
if method == 'mc':
|
|
552
|
-
u =
|
|
556
|
+
u = rng.random(size)
|
|
557
|
+
|
|
553
558
|
elif method == 'qmc':
|
|
554
|
-
|
|
555
|
-
|
|
559
|
+
try:
|
|
560
|
+
engine = qmc.Halton(d=1, scramble=True, rng=rng)
|
|
561
|
+
except TypeError:
|
|
562
|
+
# Older scipy versions
|
|
563
|
+
engine = qmc.Halton(d=1, scramble=True, seed=rng)
|
|
564
|
+
u = engine.random(size).ravel()
|
|
565
|
+
|
|
556
566
|
else:
|
|
557
|
-
raise
|
|
567
|
+
raise NotImplementedError('"method" is invalid.')
|
|
558
568
|
|
|
559
569
|
# Draw from distribution by mapping from inverse CDF
|
|
560
570
|
samples = inv_cdf(u).ravel()
|