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
|
@@ -0,0 +1,386 @@
|
|
|
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
|
|
6
|
+
# under the terms of the license found in the LICENSE.txt file in the root
|
|
7
|
+
# directory of this source tree.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# =======
|
|
11
|
+
# Imports
|
|
12
|
+
# =======
|
|
13
|
+
|
|
14
|
+
import numpy
|
|
15
|
+
from .._algebraic_form._sheets_util import _pick_physical_root_scalar
|
|
16
|
+
|
|
17
|
+
__all__ = ['DeformedWigner']
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ===============
|
|
21
|
+
# Deformed Wigner
|
|
22
|
+
# ===============
|
|
23
|
+
|
|
24
|
+
class DeformedWigner(object):
|
|
25
|
+
"""
|
|
26
|
+
Deformed Wiger
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# ====
|
|
30
|
+
# init
|
|
31
|
+
# ====
|
|
32
|
+
|
|
33
|
+
def __init__(self, t1, t2, w1, sigma=1.0):
|
|
34
|
+
"""
|
|
35
|
+
Initialization.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
if not (0.0 <= w1 <= 1.0):
|
|
39
|
+
raise ValueError("w1 must be in [0, 1].")
|
|
40
|
+
|
|
41
|
+
self.t1 = t1
|
|
42
|
+
self.t2 = t2
|
|
43
|
+
self.w1 = w1
|
|
44
|
+
self.sigma = sigma
|
|
45
|
+
|
|
46
|
+
# ==================
|
|
47
|
+
# roots cubic scalar
|
|
48
|
+
# ==================
|
|
49
|
+
|
|
50
|
+
def _roots_cubic_scalar(self, z):
|
|
51
|
+
"""
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Unpack parameters
|
|
55
|
+
t1 = self.t1
|
|
56
|
+
t2 = self.t2
|
|
57
|
+
w1 = self.w1
|
|
58
|
+
sigma = self.sigma
|
|
59
|
+
|
|
60
|
+
w2 = 1.0 - w1
|
|
61
|
+
s2 = sigma * sigma
|
|
62
|
+
a1 = t1 - z
|
|
63
|
+
a2 = t2 - z
|
|
64
|
+
|
|
65
|
+
c3 = s2 * s2
|
|
66
|
+
c2 = -s2 * (a1 + a2)
|
|
67
|
+
c1 = (a1 * a2) + s2
|
|
68
|
+
c0 = -(w1 * a2 + w2 * a1)
|
|
69
|
+
|
|
70
|
+
return numpy.roots([c3, c2, c1, c0])
|
|
71
|
+
|
|
72
|
+
# =========
|
|
73
|
+
# stieltjes
|
|
74
|
+
# =========
|
|
75
|
+
|
|
76
|
+
def stieltjes(self, z, max_iter=100, tol=1e-12):
|
|
77
|
+
"""
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
# Unpack parameters
|
|
81
|
+
t1 = self.t1
|
|
82
|
+
t2 = self.t2
|
|
83
|
+
w1 = self.w1
|
|
84
|
+
sigma = self.sigma
|
|
85
|
+
|
|
86
|
+
w2 = 1.0 - w1
|
|
87
|
+
s2 = sigma * sigma
|
|
88
|
+
|
|
89
|
+
z = numpy.asarray(z, dtype=numpy.complex128)
|
|
90
|
+
scalar = (z.ndim == 0)
|
|
91
|
+
if scalar:
|
|
92
|
+
z = z.reshape((1,))
|
|
93
|
+
|
|
94
|
+
m = -1.0 / z
|
|
95
|
+
active = numpy.isfinite(m)
|
|
96
|
+
|
|
97
|
+
for _ in range(int(max_iter)):
|
|
98
|
+
if not numpy.any(active):
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
ma = m[active]
|
|
102
|
+
za = z[active]
|
|
103
|
+
|
|
104
|
+
d1 = (t1 - za - s2 * ma)
|
|
105
|
+
d2 = (t2 - za - s2 * ma)
|
|
106
|
+
|
|
107
|
+
f = ma - (w1 / d1 + w2 / d2)
|
|
108
|
+
fp = 1.0 - (w1 * s2 / (d1 * d1) + w2 * s2 / (d2 * d2))
|
|
109
|
+
|
|
110
|
+
step = f / fp
|
|
111
|
+
ma2 = ma - step
|
|
112
|
+
m[active] = ma2
|
|
113
|
+
|
|
114
|
+
conv = numpy.abs(step) < tol * (1.0 + numpy.abs(ma2))
|
|
115
|
+
idx = numpy.where(active)[0]
|
|
116
|
+
active[idx[conv]] = False
|
|
117
|
+
|
|
118
|
+
sign = numpy.where(numpy.imag(z) >= 0.0, 1.0, -1.0)
|
|
119
|
+
bad = (sign * numpy.imag(m) <= 0.0) | (~numpy.isfinite(m))
|
|
120
|
+
|
|
121
|
+
if numpy.any(bad):
|
|
122
|
+
zf = z.ravel()
|
|
123
|
+
mf = m.ravel()
|
|
124
|
+
bad_idx = numpy.where(bad.ravel())[0]
|
|
125
|
+
for i in bad_idx:
|
|
126
|
+
r = self._roots_cubic_scalar(zf[i])
|
|
127
|
+
mf[i] = _pick_physical_root_scalar(zf[i], r)
|
|
128
|
+
m = mf.reshape(z.shape)
|
|
129
|
+
|
|
130
|
+
if scalar:
|
|
131
|
+
return m.reshape(())
|
|
132
|
+
return m
|
|
133
|
+
|
|
134
|
+
# =======
|
|
135
|
+
# density
|
|
136
|
+
# =======
|
|
137
|
+
|
|
138
|
+
def density(self, x, eta=1e-3):
|
|
139
|
+
"""
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
# Unpack parameters
|
|
143
|
+
t1 = self.t1
|
|
144
|
+
t2 = self.t2
|
|
145
|
+
w1 = self.w1
|
|
146
|
+
sigma = self.sigma
|
|
147
|
+
|
|
148
|
+
x = numpy.asarray(x, dtype=numpy.float64)
|
|
149
|
+
z = x + 1j * float(eta)
|
|
150
|
+
|
|
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)
|
|
183
|
+
rho = numpy.imag(m) / numpy.pi
|
|
184
|
+
rho = numpy.maximum(rho, 0.0)
|
|
185
|
+
return rho
|
|
186
|
+
|
|
187
|
+
# =====
|
|
188
|
+
# roots
|
|
189
|
+
# =====
|
|
190
|
+
|
|
191
|
+
def roots(self, z):
|
|
192
|
+
"""
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
z = numpy.asarray(z, dtype=numpy.complex128)
|
|
196
|
+
scalar = (z.ndim == 0)
|
|
197
|
+
if scalar:
|
|
198
|
+
z = z.reshape((1,))
|
|
199
|
+
|
|
200
|
+
zf = z.ravel()
|
|
201
|
+
out = numpy.empty((zf.size, 3), dtype=numpy.complex128)
|
|
202
|
+
for i in range(zf.size):
|
|
203
|
+
out[i, :] = self._roots_cubic_scalar(zf[i])
|
|
204
|
+
|
|
205
|
+
out = out.reshape(z.shape + (3,))
|
|
206
|
+
if scalar:
|
|
207
|
+
return out.reshape((3,))
|
|
208
|
+
return out
|
|
209
|
+
|
|
210
|
+
# =======
|
|
211
|
+
# support
|
|
212
|
+
# =======
|
|
213
|
+
|
|
214
|
+
def support(self, y_probe=1e-6):
|
|
215
|
+
"""
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
# Unpack parameters
|
|
219
|
+
t1 = self.t1
|
|
220
|
+
t2 = self.t2
|
|
221
|
+
w1 = self.w1
|
|
222
|
+
sigma = self.sigma
|
|
223
|
+
|
|
224
|
+
w2 = 1.0 - w1
|
|
225
|
+
|
|
226
|
+
p_a = numpy.poly1d([-1.0, t1])
|
|
227
|
+
p_b = numpy.poly1d([-1.0, t2])
|
|
228
|
+
|
|
229
|
+
pa2 = p_a * p_a
|
|
230
|
+
pb2 = p_b * p_b
|
|
231
|
+
|
|
232
|
+
eq = pa2 * pb2 - (sigma * sigma) * (w1 * pb2 + w2 * pa2)
|
|
233
|
+
u_roots = numpy.roots(eq.coeffs)
|
|
234
|
+
|
|
235
|
+
ucrit = []
|
|
236
|
+
for r in u_roots:
|
|
237
|
+
if numpy.isfinite(r) and abs(r.imag) < 1e-10:
|
|
238
|
+
ucrit.append(float(r.real))
|
|
239
|
+
ucrit.sort()
|
|
240
|
+
|
|
241
|
+
def G(u):
|
|
242
|
+
return w1 / (t1 - u) + w2 / (t2 - u)
|
|
243
|
+
|
|
244
|
+
def z_of_u(u):
|
|
245
|
+
return u - (sigma * sigma) * G(u)
|
|
246
|
+
|
|
247
|
+
edges = []
|
|
248
|
+
for u in ucrit:
|
|
249
|
+
x = z_of_u(u)
|
|
250
|
+
if numpy.isfinite(x):
|
|
251
|
+
x = float(numpy.real(x))
|
|
252
|
+
if (len(edges) == 0) or (abs(x - edges[-1]) > 1e-8):
|
|
253
|
+
edges.append(x)
|
|
254
|
+
|
|
255
|
+
if len(edges) < 2:
|
|
256
|
+
return []
|
|
257
|
+
|
|
258
|
+
thr = 100.0 * float(y_probe)
|
|
259
|
+
cuts = []
|
|
260
|
+
for i in range(len(edges) - 1):
|
|
261
|
+
xm = 0.5 * (edges[i] + edges[i + 1])
|
|
262
|
+
z = xm + 1j * float(y_probe)
|
|
263
|
+
r = self._roots_cubic_scalar(z)
|
|
264
|
+
m = _pick_physical_root_scalar(z, r)
|
|
265
|
+
if numpy.imag(m) > thr:
|
|
266
|
+
cuts.append((edges[i], edges[i + 1]))
|
|
267
|
+
|
|
268
|
+
return cuts
|
|
269
|
+
|
|
270
|
+
# ======
|
|
271
|
+
# matrix
|
|
272
|
+
# ======
|
|
273
|
+
|
|
274
|
+
def matrix(self, size, seed=None):
|
|
275
|
+
"""
|
|
276
|
+
Generate matrix with the spectral density of the distribution.
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
----------
|
|
280
|
+
|
|
281
|
+
size : int
|
|
282
|
+
Size :math:`n` of the matrix.
|
|
283
|
+
|
|
284
|
+
seed : int, default=None
|
|
285
|
+
Seed for random number generator.
|
|
286
|
+
|
|
287
|
+
Returns
|
|
288
|
+
-------
|
|
289
|
+
|
|
290
|
+
A : numpy.ndarray
|
|
291
|
+
A matrix of the size :math:`n \\times n`.
|
|
292
|
+
|
|
293
|
+
Notes
|
|
294
|
+
-----
|
|
295
|
+
|
|
296
|
+
Generate an :math:`n \\times n` matrix
|
|
297
|
+
:math:`\\mathbf{A} = \\mathbf{T} + \\sigma \\mathbf{W}`
|
|
298
|
+
whose ESD converges to
|
|
299
|
+
:math:`H \\boxplus \\mathrm{SC}_{\\sigma^2}`, where
|
|
300
|
+
:math:`H = w_1 \\delta_{t_1} + (1 - w_1) \\delta_{t_2}`.
|
|
301
|
+
|
|
302
|
+
Examples
|
|
303
|
+
--------
|
|
304
|
+
|
|
305
|
+
.. code-block::python
|
|
306
|
+
|
|
307
|
+
>>> from freealg.distributions import DeformedWigner
|
|
308
|
+
>>> dwg = DeformedWigner(1/50)
|
|
309
|
+
>>> A = dwg.matrix(2000)
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
n = int(size)
|
|
313
|
+
if n <= 0:
|
|
314
|
+
raise ValueError("size must be a positive integer.")
|
|
315
|
+
|
|
316
|
+
# Unpack parameters
|
|
317
|
+
t1 = float(self.t1)
|
|
318
|
+
t2 = float(self.t2)
|
|
319
|
+
w1 = float(self.w1)
|
|
320
|
+
sigma = float(self.sigma)
|
|
321
|
+
|
|
322
|
+
# RNG
|
|
323
|
+
rng = numpy.random.default_rng(seed)
|
|
324
|
+
|
|
325
|
+
# T part
|
|
326
|
+
n1 = int(round(w1 * n))
|
|
327
|
+
n1 = max(0, min(n, n1))
|
|
328
|
+
|
|
329
|
+
d = numpy.empty(n, dtype=numpy.float64)
|
|
330
|
+
d[:n1] = t1
|
|
331
|
+
d[n1:] = t2
|
|
332
|
+
rng.shuffle(d) # randomize positions
|
|
333
|
+
T = numpy.diag(d)
|
|
334
|
+
|
|
335
|
+
# W part: Symmetric Wigner with variance 1/n (up to symmetry)
|
|
336
|
+
G = rng.standard_normal((n, n))
|
|
337
|
+
W = (G + G.T) * (0.5 / numpy.sqrt(n))
|
|
338
|
+
|
|
339
|
+
# Compose
|
|
340
|
+
A = T + sigma * W
|
|
341
|
+
|
|
342
|
+
return A
|
|
343
|
+
|
|
344
|
+
# ====
|
|
345
|
+
# poly
|
|
346
|
+
# ====
|
|
347
|
+
|
|
348
|
+
def poly(self):
|
|
349
|
+
"""
|
|
350
|
+
Return a_coeffs for the exact cubic P(z,m)=0 of the two-atom deformed
|
|
351
|
+
Wigner model.
|
|
352
|
+
|
|
353
|
+
a_coeffs[i, j] is the coefficient of z^i m^j.
|
|
354
|
+
Shape is (deg_z+1, deg_m+1) = (3, 4).
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
t1 = float(self.t1)
|
|
358
|
+
t2 = float(self.t2)
|
|
359
|
+
w1 = float(self.w1)
|
|
360
|
+
w2 = 1.0 - w1
|
|
361
|
+
sigma = float(self.sigma)
|
|
362
|
+
s2 = sigma * sigma
|
|
363
|
+
|
|
364
|
+
a = numpy.zeros((3, 4), dtype=numpy.complex128)
|
|
365
|
+
|
|
366
|
+
# m^0 column (a0(z) = z - (w1 t2 + w2 t1))
|
|
367
|
+
a[0, 0] = -(w1 * t2 + w2 * t1)
|
|
368
|
+
a[1, 0] = 1.0
|
|
369
|
+
a[2, 0] = 0.0
|
|
370
|
+
|
|
371
|
+
# m^1 column (a1(z) = z^2 - (t1+t2)z + t1 t2 + s2)
|
|
372
|
+
a[0, 1] = t1 * t2 + s2
|
|
373
|
+
a[1, 1] = -(t1 + t2)
|
|
374
|
+
a[2, 1] = 1.0
|
|
375
|
+
|
|
376
|
+
# m^2 column (a2(z) = s2 z - s2 (t1+t2))
|
|
377
|
+
a[0, 2] = -s2 * (t1 + t2)
|
|
378
|
+
a[1, 2] = 2.0 * s2
|
|
379
|
+
a[2, 2] = 0.0
|
|
380
|
+
|
|
381
|
+
# m^3 column (a3(z) = s2^2)
|
|
382
|
+
a[0, 3] = s2 * s2
|
|
383
|
+
a[1, 3] = 0.0
|
|
384
|
+
a[2, 3] = 0.0
|
|
385
|
+
|
|
386
|
+
return a
|
|
@@ -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
|
|
@@ -78,7 +78,7 @@ class KestenMcKay(object):
|
|
|
78
78
|
----------
|
|
79
79
|
|
|
80
80
|
.. [1] Kesten, H. (1959). Symmetric random walks on groups. Transactions of
|
|
81
|
-
the American Mathematical Society, 92(2), 336
|
|
81
|
+
the American Mathematical Society, 92(2), 336-354.
|
|
82
82
|
|
|
83
83
|
.. [2] McKay, B. D. (1981). The expected eigenvalue distribution of a large
|
|
84
84
|
regular graph. Linear Algebra and its Applications, 40, 203-216
|
|
@@ -110,7 +110,7 @@ class KestenMcKay(object):
|
|
|
110
110
|
# density
|
|
111
111
|
# =======
|
|
112
112
|
|
|
113
|
-
def density(self, x=None, plot=False, latex=False, save=False):
|
|
113
|
+
def density(self, x=None, plot=False, latex=False, save=False, eig=None):
|
|
114
114
|
"""
|
|
115
115
|
Density of distribution.
|
|
116
116
|
|
|
@@ -137,6 +137,10 @@ class KestenMcKay(object):
|
|
|
137
137
|
assumed to the save filename (with the file extension). This option
|
|
138
138
|
is relevant only if ``plot=True``.
|
|
139
139
|
|
|
140
|
+
eig : numpy.array, default=None
|
|
141
|
+
A collection of eigenvalues to compare to via histogram. This
|
|
142
|
+
option is relevant only if ``plot=True``.
|
|
143
|
+
|
|
140
144
|
Returns
|
|
141
145
|
-------
|
|
142
146
|
|
|
@@ -173,7 +177,11 @@ class KestenMcKay(object):
|
|
|
173
177
|
numpy.sqrt(4.0 * (self.d - 1.0) - x[mask]**2)
|
|
174
178
|
|
|
175
179
|
if plot:
|
|
176
|
-
|
|
180
|
+
if eig is not None:
|
|
181
|
+
label = 'Theoretical'
|
|
182
|
+
else:
|
|
183
|
+
label = ''
|
|
184
|
+
plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
|
|
177
185
|
|
|
178
186
|
return rho
|
|
179
187
|
|
|
@@ -494,9 +502,6 @@ class KestenMcKay(object):
|
|
|
494
502
|
:class: custom-dark
|
|
495
503
|
"""
|
|
496
504
|
|
|
497
|
-
if seed is not None:
|
|
498
|
-
numpy.random.seed(seed)
|
|
499
|
-
|
|
500
505
|
if x_min is None:
|
|
501
506
|
x_min = self.lam_m
|
|
502
507
|
|
|
@@ -515,14 +520,23 @@ class KestenMcKay(object):
|
|
|
515
520
|
inv_cdf = interp1d(cdf, xs, bounds_error=False,
|
|
516
521
|
fill_value=(x_min, x_max))
|
|
517
522
|
|
|
523
|
+
# Random generator
|
|
524
|
+
rng = numpy.random.default_rng(seed)
|
|
525
|
+
|
|
518
526
|
# Draw from uniform distribution
|
|
519
527
|
if method == 'mc':
|
|
520
|
-
u =
|
|
528
|
+
u = rng.random(size)
|
|
529
|
+
|
|
521
530
|
elif method == 'qmc':
|
|
522
|
-
|
|
523
|
-
|
|
531
|
+
try:
|
|
532
|
+
engine = qmc.Halton(d=1, scramble=True, rng=rng)
|
|
533
|
+
except TypeError:
|
|
534
|
+
# Older scipy versions
|
|
535
|
+
engine = qmc.Halton(d=1, scramble=True, seed=rng)
|
|
536
|
+
u = engine.random(size).ravel()
|
|
537
|
+
|
|
524
538
|
else:
|
|
525
|
-
raise
|
|
539
|
+
raise NotImplementedError('"method" is invalid.')
|
|
526
540
|
|
|
527
541
|
# Draw from distribution by mapping from inverse CDF
|
|
528
542
|
samples = inv_cdf(u).ravel()
|
|
@@ -539,9 +553,9 @@ class KestenMcKay(object):
|
|
|
539
553
|
|
|
540
554
|
return samples
|
|
541
555
|
|
|
542
|
-
#
|
|
543
|
-
#
|
|
544
|
-
#
|
|
556
|
+
# ===============
|
|
557
|
+
# haar orthogonal
|
|
558
|
+
# ===============
|
|
545
559
|
|
|
546
560
|
def _haar_orthogonal(self, n, k, seed=None):
|
|
547
561
|
"""
|