freealg 0.0.3__py3-none-any.whl → 0.1.1__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/__version__.py +1 -1
- freealg/_chebyshev.py +39 -5
- freealg/_jacobi.py +37 -5
- freealg/_pade.py +356 -8
- freealg/_plot_util.py +6 -28
- freealg/_sample.py +85 -0
- freealg/distributions/__init__.py +4 -4
- freealg/distributions/kesten_mckay.py +559 -0
- freealg/distributions/marchenko_pastur.py +4 -3
- freealg/distributions/wachter.py +568 -0
- freealg/distributions/wigner.py +552 -0
- freealg/freeform.py +122 -32
- {freealg-0.0.3.dist-info → freealg-0.1.1.dist-info}/METADATA +3 -1
- freealg-0.1.1.dist-info/RECORD +21 -0
- freealg-0.0.3.dist-info/RECORD +0 -17
- {freealg-0.0.3.dist-info → freealg-0.1.1.dist-info}/WHEEL +0 -0
- {freealg-0.0.3.dist-info → freealg-0.1.1.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.0.3.dist-info → freealg-0.1.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright 2025, 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
|
+
import networkx as nx
|
|
16
|
+
from scipy.interpolate import interp1d
|
|
17
|
+
from .._plot_util import plot_density, plot_hilbert, plot_stieltjes, \
|
|
18
|
+
plot_stieltjes_on_disk, plot_samples
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from scipy.integrate import cumtrapz
|
|
22
|
+
except ImportError:
|
|
23
|
+
from scipy.integrate import cumulative_trapezoid as cumtrapz
|
|
24
|
+
|
|
25
|
+
__all__ = ['Wigner']
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ======
|
|
29
|
+
# Wigner
|
|
30
|
+
# ======
|
|
31
|
+
|
|
32
|
+
class Wigner(object):
|
|
33
|
+
"""
|
|
34
|
+
Wigner semicircle distribution.
|
|
35
|
+
|
|
36
|
+
Methods
|
|
37
|
+
-------
|
|
38
|
+
|
|
39
|
+
density
|
|
40
|
+
Spectral density of distribution.
|
|
41
|
+
|
|
42
|
+
hilbert
|
|
43
|
+
Hilbert transform of distribution.
|
|
44
|
+
|
|
45
|
+
stieltjes
|
|
46
|
+
Stieltjes transform of distribution.
|
|
47
|
+
|
|
48
|
+
sample
|
|
49
|
+
Sample from distribution.
|
|
50
|
+
|
|
51
|
+
matrix
|
|
52
|
+
Generate matrix with its empirical spectral density of distribution
|
|
53
|
+
|
|
54
|
+
Notes
|
|
55
|
+
-----
|
|
56
|
+
|
|
57
|
+
The Marchenko-Pastur distribution has the absolutely-continuous density
|
|
58
|
+
|
|
59
|
+
.. math::
|
|
60
|
+
|
|
61
|
+
\\mathrm{d} \\rho(x) = \\frac{1}{2 \\pi}
|
|
62
|
+
\\sqrt{(4 - x^2}
|
|
63
|
+
\\mathbf{1}_{x \\in [\\lambda_{-}, \\lambda_{+}]} \\mathrm{d}{x}
|
|
64
|
+
|
|
65
|
+
with :math:`\\lambda_{\\pm} = \\pm 2` being the edges of the support
|
|
66
|
+
|
|
67
|
+
References
|
|
68
|
+
----------
|
|
69
|
+
|
|
70
|
+
.. [1] Wigner, E. P. (1955). Characteristic vectors of bordered matrices
|
|
71
|
+
with infinite dimensions. Annals of Mathematics, 62(3), 548-564.421
|
|
72
|
+
|
|
73
|
+
Examples
|
|
74
|
+
--------
|
|
75
|
+
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
|
|
78
|
+
>>> from freealg.distributions import Wigner
|
|
79
|
+
>>> wg = Wigner()
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# ====
|
|
83
|
+
# init
|
|
84
|
+
# ====
|
|
85
|
+
|
|
86
|
+
def __init__(self):
|
|
87
|
+
"""
|
|
88
|
+
Initialization.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
self.lam_p = 2.0
|
|
92
|
+
self.lam_m = -2.0
|
|
93
|
+
self.support = (self.lam_m, self.lam_p)
|
|
94
|
+
|
|
95
|
+
# =======
|
|
96
|
+
# density
|
|
97
|
+
# =======
|
|
98
|
+
|
|
99
|
+
def density(self, x=None, plot=False, latex=False, save=False):
|
|
100
|
+
"""
|
|
101
|
+
Density of distribution.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
|
|
106
|
+
x : numpy.array, default=None
|
|
107
|
+
The locations where density is evaluated at. If `None`, an interval
|
|
108
|
+
slightly larger than the support interval of the spectral density
|
|
109
|
+
is used.
|
|
110
|
+
|
|
111
|
+
rho : numpy.array, default=None
|
|
112
|
+
Density. If `None`, it will be computed.
|
|
113
|
+
|
|
114
|
+
plot : bool, default=False
|
|
115
|
+
If `True`, density is plotted.
|
|
116
|
+
|
|
117
|
+
latex : bool, default=False
|
|
118
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
119
|
+
relevant only if ``plot=True``.
|
|
120
|
+
|
|
121
|
+
save : bool, default=False
|
|
122
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
123
|
+
assumed to the save filename (with the file extension). This option
|
|
124
|
+
is relevant only if ``plot=True``.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
|
|
129
|
+
rho : numpy.array
|
|
130
|
+
Density.
|
|
131
|
+
|
|
132
|
+
Examples
|
|
133
|
+
--------
|
|
134
|
+
|
|
135
|
+
.. code-block::python
|
|
136
|
+
|
|
137
|
+
>>> from freealg.distributions import Wigner
|
|
138
|
+
>>> wg = Wigner()
|
|
139
|
+
>>> rho = wg.density(plot=True)
|
|
140
|
+
|
|
141
|
+
.. image:: ../_static/images/plots/wg_density.png
|
|
142
|
+
:align: center
|
|
143
|
+
:class: custom-dark
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
# Create x if not given
|
|
147
|
+
if x is None:
|
|
148
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
149
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
150
|
+
scale = 1.25
|
|
151
|
+
x_min = numpy.floor(center - radius * scale)
|
|
152
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
153
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
154
|
+
|
|
155
|
+
rho = numpy.zeros_like(x)
|
|
156
|
+
mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
|
|
157
|
+
|
|
158
|
+
rho[mask] = (1.0 / (2.0 * numpy.pi)) * \
|
|
159
|
+
numpy.sqrt(4.0 - x[mask]**2)
|
|
160
|
+
|
|
161
|
+
if plot:
|
|
162
|
+
plot_density(x, rho, label='', latex=latex, save=save)
|
|
163
|
+
|
|
164
|
+
return rho
|
|
165
|
+
|
|
166
|
+
# =======
|
|
167
|
+
# hilbert
|
|
168
|
+
# =======
|
|
169
|
+
|
|
170
|
+
def hilbert(self, x=None, plot=False, latex=False, save=False):
|
|
171
|
+
"""
|
|
172
|
+
Hilbert transform of the distribution.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
|
|
177
|
+
x : numpy.array, default=None
|
|
178
|
+
The locations where Hilbert transform is evaluated at. If `None`,
|
|
179
|
+
an interval slightly larger than the support interval of the
|
|
180
|
+
spectral density is used.
|
|
181
|
+
|
|
182
|
+
plot : bool, default=False
|
|
183
|
+
If `True`, Hilbert transform is plotted.
|
|
184
|
+
|
|
185
|
+
latex : bool, default=False
|
|
186
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
187
|
+
relevant only if ``plot=True``.
|
|
188
|
+
|
|
189
|
+
save : bool, default=False
|
|
190
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
191
|
+
assumed to the save filename (with the file extension). This option
|
|
192
|
+
is relevant only if ``plot=True``.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
|
|
197
|
+
hilb : numpy.array
|
|
198
|
+
Hilbert transform.
|
|
199
|
+
|
|
200
|
+
Examples
|
|
201
|
+
--------
|
|
202
|
+
|
|
203
|
+
.. code-block::python
|
|
204
|
+
|
|
205
|
+
>>> from freealg.distributions import Wigner
|
|
206
|
+
>>> wg = Wigner()
|
|
207
|
+
>>> hilb = wg.hilbert(plot=True)
|
|
208
|
+
|
|
209
|
+
.. image:: ../_static/images/plots/wg_hilbert.png
|
|
210
|
+
:align: center
|
|
211
|
+
:class: custom-dark
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
# Create x if not given
|
|
215
|
+
if x is None:
|
|
216
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
217
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
218
|
+
scale = 1.25
|
|
219
|
+
x_min = numpy.floor(center - radius * scale)
|
|
220
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
221
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
222
|
+
|
|
223
|
+
def _P(x):
|
|
224
|
+
return x
|
|
225
|
+
|
|
226
|
+
def _Q(x):
|
|
227
|
+
return 1.0
|
|
228
|
+
|
|
229
|
+
P = _P(x)
|
|
230
|
+
Q = _Q(x)
|
|
231
|
+
Delta2 = P**2 - 4.0 * Q
|
|
232
|
+
Delta = numpy.sqrt(numpy.maximum(Delta2, 0))
|
|
233
|
+
sign = numpy.sign(P)
|
|
234
|
+
hilb = (P - sign * Delta) / (2.0 * Q)
|
|
235
|
+
|
|
236
|
+
# using negative sign convention
|
|
237
|
+
hilb = -hilb
|
|
238
|
+
|
|
239
|
+
if plot:
|
|
240
|
+
plot_hilbert(x, hilb, support=self.support, latex=latex, save=save)
|
|
241
|
+
|
|
242
|
+
return hilb
|
|
243
|
+
|
|
244
|
+
# =======================
|
|
245
|
+
# m mp numeric vectorized
|
|
246
|
+
# =======================
|
|
247
|
+
|
|
248
|
+
def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
|
|
249
|
+
"""
|
|
250
|
+
Stieltjes transform (principal or secondary branch)
|
|
251
|
+
for Marchenko–Pastur distribution on upper half-plane.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
m = numpy.empty_like(z, dtype=complex)
|
|
255
|
+
|
|
256
|
+
# Use quadratic form
|
|
257
|
+
sign = -1 if alt_branch else 1
|
|
258
|
+
A = 1.0
|
|
259
|
+
B = z
|
|
260
|
+
D = B**2 - 4 * A
|
|
261
|
+
sqrtD = numpy.sqrt(D)
|
|
262
|
+
m1 = (-B + sqrtD) / (2 * A)
|
|
263
|
+
m2 = (-B - sqrtD) / (2 * A)
|
|
264
|
+
|
|
265
|
+
# pick correct branch
|
|
266
|
+
upper = z.imag >= 0
|
|
267
|
+
branch = numpy.empty_like(m1)
|
|
268
|
+
branch[upper] = numpy.where(sign*m1[upper].imag > 0, m1[upper],
|
|
269
|
+
m2[upper])
|
|
270
|
+
branch[~upper] = numpy.where(sign*m1[~upper].imag < 0, m1[~upper],
|
|
271
|
+
m2[~upper])
|
|
272
|
+
m = branch
|
|
273
|
+
|
|
274
|
+
return m
|
|
275
|
+
|
|
276
|
+
# ============
|
|
277
|
+
# m mp reflect
|
|
278
|
+
# ============
|
|
279
|
+
|
|
280
|
+
def _m_mp_reflect(self, z, alt_branch=False):
|
|
281
|
+
"""
|
|
282
|
+
Analytic continuation using Schwarz reflection.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
mask_p = z.imag >= 0.0
|
|
286
|
+
mask_n = z.imag < 0.0
|
|
287
|
+
|
|
288
|
+
m = numpy.zeros_like(z)
|
|
289
|
+
|
|
290
|
+
f = self._m_mp_numeric_vectorized
|
|
291
|
+
m[mask_p] = f(z[mask_p], alt_branch=False)
|
|
292
|
+
m[mask_n] = f(z[mask_n], alt_branch=alt_branch)
|
|
293
|
+
|
|
294
|
+
return m
|
|
295
|
+
|
|
296
|
+
# =========
|
|
297
|
+
# stieltjes
|
|
298
|
+
# =========
|
|
299
|
+
|
|
300
|
+
def stieltjes(self, x=None, y=None, plot=False, on_disk=False, latex=False,
|
|
301
|
+
save=False):
|
|
302
|
+
"""
|
|
303
|
+
Stieltjes transform of distribution.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
|
|
308
|
+
x : numpy.array, default=None
|
|
309
|
+
The x axis of the grid where the Stieltjes transform is evaluated.
|
|
310
|
+
If `None`, an interval slightly larger than the support interval of
|
|
311
|
+
the spectral density is used.
|
|
312
|
+
|
|
313
|
+
y : numpy.array, default=None
|
|
314
|
+
The y axis of the grid where the Stieltjes transform is evaluated.
|
|
315
|
+
If `None`, a grid on the interval ``[-1, 1]`` is used.
|
|
316
|
+
|
|
317
|
+
plot : bool, default=False
|
|
318
|
+
If `True`, Stieltjes transform is plotted.
|
|
319
|
+
|
|
320
|
+
on_disk : bool, default=False
|
|
321
|
+
If `True`, the Stieltjes transform is mapped on unit disk. This
|
|
322
|
+
option relevant only if ``plot=True``.
|
|
323
|
+
|
|
324
|
+
latex : bool, default=False
|
|
325
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
326
|
+
relevant only if ``plot=True``.
|
|
327
|
+
|
|
328
|
+
save : bool, default=False
|
|
329
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
330
|
+
assumed to the save filename (with the file extension). This option
|
|
331
|
+
is relevant only if ``plot=True``.
|
|
332
|
+
|
|
333
|
+
Returns
|
|
334
|
+
-------
|
|
335
|
+
|
|
336
|
+
m1 : numpy.array
|
|
337
|
+
Stieltjes transform on principal branch.
|
|
338
|
+
|
|
339
|
+
m12 : numpy.array
|
|
340
|
+
Stieltjes transform on secondary branch.
|
|
341
|
+
|
|
342
|
+
Examples
|
|
343
|
+
--------
|
|
344
|
+
|
|
345
|
+
.. code-block:: python
|
|
346
|
+
|
|
347
|
+
>>> from freealg.distributions import Wigner
|
|
348
|
+
>>> wg = Wigner()
|
|
349
|
+
>>> m1, m2 = wg.stieltjes(plot=True)
|
|
350
|
+
|
|
351
|
+
.. image:: ../_static/images/plots/wg_stieltjes.png
|
|
352
|
+
:align: center
|
|
353
|
+
:class: custom-dark
|
|
354
|
+
|
|
355
|
+
Plot on unit disk using Cayley transform:
|
|
356
|
+
|
|
357
|
+
.. code-block:: python
|
|
358
|
+
|
|
359
|
+
>>> m1, m2 = mp.stieltjes(plot=True, on_disk=True)
|
|
360
|
+
|
|
361
|
+
.. image:: ../_static/images/plots/wg_stieltjes_disk.png
|
|
362
|
+
:align: center
|
|
363
|
+
:class: custom-dark
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
if (plot is True) and (on_disk is True):
|
|
367
|
+
n_r = 1000
|
|
368
|
+
n_t = 1000
|
|
369
|
+
r_min, r_max = 0, 2.5
|
|
370
|
+
t_min, t_max = 0, 2.0 * numpy.pi
|
|
371
|
+
r = numpy.linspace(r_min, r_max, n_r)
|
|
372
|
+
t = numpy.linspace(t_min, t_max, n_t + 1)[:-1]
|
|
373
|
+
grid_r, grid_t = numpy.meshgrid(r, t)
|
|
374
|
+
|
|
375
|
+
grid_x_D = grid_r * numpy.cos(grid_t)
|
|
376
|
+
grid_y_D = grid_r * numpy.sin(grid_t)
|
|
377
|
+
zeta = grid_x_D + 1j * grid_y_D
|
|
378
|
+
|
|
379
|
+
# Cayley transform mapping zeta on D to z on H
|
|
380
|
+
z_H = 1j * (1 + zeta) / (1 - zeta)
|
|
381
|
+
|
|
382
|
+
m1_D = self._m_mp_reflect(z_H, alt_branch=False)
|
|
383
|
+
m2_D = self._m_mp_reflect(z_H, alt_branch=True)
|
|
384
|
+
|
|
385
|
+
plot_stieltjes_on_disk(r, t, m1_D, m2_D, support=self.support,
|
|
386
|
+
latex=latex, save=save)
|
|
387
|
+
|
|
388
|
+
return m1_D, m2_D
|
|
389
|
+
|
|
390
|
+
# Create x if not given
|
|
391
|
+
if x is None:
|
|
392
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
393
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
394
|
+
scale = 2.0
|
|
395
|
+
x_min = numpy.floor(2.0 * (center - 2.0 * radius * scale)) / 2.0
|
|
396
|
+
x_max = numpy.ceil(2.0 * (center + 2.0 * radius * scale)) / 2.0
|
|
397
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
398
|
+
|
|
399
|
+
# Create y if not given
|
|
400
|
+
if y is None:
|
|
401
|
+
y = numpy.linspace(-1, 1, 400)
|
|
402
|
+
|
|
403
|
+
x_grid, y_grid = numpy.meshgrid(x, y)
|
|
404
|
+
z = x_grid + 1j * y_grid # shape (Ny, Nx)
|
|
405
|
+
|
|
406
|
+
m1 = self._m_mp_reflect(z, alt_branch=False)
|
|
407
|
+
m2 = self._m_mp_reflect(z, alt_branch=True)
|
|
408
|
+
|
|
409
|
+
if plot:
|
|
410
|
+
plot_stieltjes(x, y, m1, m2, support=self.support, latex=latex,
|
|
411
|
+
save=save)
|
|
412
|
+
|
|
413
|
+
return m1, m2
|
|
414
|
+
|
|
415
|
+
# ======
|
|
416
|
+
# sample
|
|
417
|
+
# ======
|
|
418
|
+
|
|
419
|
+
def sample(self, size, x_min=None, x_max=None, plot=False, latex=False,
|
|
420
|
+
save=False):
|
|
421
|
+
"""
|
|
422
|
+
Sample from distribution.
|
|
423
|
+
|
|
424
|
+
Parameters
|
|
425
|
+
----------
|
|
426
|
+
|
|
427
|
+
size : int
|
|
428
|
+
Size of sample.
|
|
429
|
+
|
|
430
|
+
x_min : float, default=None
|
|
431
|
+
Minimum of sample values. If `None`, the left edge of the support
|
|
432
|
+
is used.
|
|
433
|
+
|
|
434
|
+
x_max : float, default=None
|
|
435
|
+
Maximum of sample values. If `None`, the right edge of the support
|
|
436
|
+
is used.
|
|
437
|
+
|
|
438
|
+
plot : bool, default=False
|
|
439
|
+
If `True`, samples histogram is plotted.
|
|
440
|
+
|
|
441
|
+
latex : bool, default=False
|
|
442
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
443
|
+
relevant only if ``plot=True``.
|
|
444
|
+
|
|
445
|
+
save : bool, default=False
|
|
446
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
447
|
+
assumed to the save filename (with the file extension). This option
|
|
448
|
+
is relevant only if ``plot=True``.
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
|
|
453
|
+
s : numpy.ndarray
|
|
454
|
+
Samples.
|
|
455
|
+
|
|
456
|
+
Notes
|
|
457
|
+
-----
|
|
458
|
+
|
|
459
|
+
This method uses inverse transform sampling.
|
|
460
|
+
|
|
461
|
+
Examples
|
|
462
|
+
--------
|
|
463
|
+
|
|
464
|
+
.. code-block::python
|
|
465
|
+
|
|
466
|
+
>>> from freealg.distributions import Wigner
|
|
467
|
+
>>> wg = Wigner()
|
|
468
|
+
>>> s = wg.sample(2000)
|
|
469
|
+
|
|
470
|
+
.. image:: ../_static/images/plots/wg_samples.png
|
|
471
|
+
:align: center
|
|
472
|
+
:class: custom-dark
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
if x_min is None:
|
|
476
|
+
x_min = self.lam_m
|
|
477
|
+
|
|
478
|
+
if x_max is None:
|
|
479
|
+
x_max = self.lam_p
|
|
480
|
+
|
|
481
|
+
# Grid and PDF
|
|
482
|
+
xs = numpy.linspace(x_min, x_max, size)
|
|
483
|
+
pdf = self.density(xs)
|
|
484
|
+
|
|
485
|
+
# CDF (using cumulative trapezoidal rule)
|
|
486
|
+
cdf = cumtrapz(pdf, xs, initial=0)
|
|
487
|
+
cdf /= cdf[-1] # normalize CDF to 1
|
|
488
|
+
|
|
489
|
+
# Inverse CDF interpolator
|
|
490
|
+
inv_cdf = interp1d(cdf, xs, bounds_error=False,
|
|
491
|
+
fill_value=(x_min, x_max))
|
|
492
|
+
|
|
493
|
+
# Sample and map
|
|
494
|
+
u = numpy.random.rand(size)
|
|
495
|
+
samples = inv_cdf(u)
|
|
496
|
+
|
|
497
|
+
if plot:
|
|
498
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
499
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
500
|
+
scale = 1.25
|
|
501
|
+
x_min = numpy.floor(center - radius * scale)
|
|
502
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
503
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
504
|
+
rho = self.density(x)
|
|
505
|
+
plot_samples(x, rho, x_min, x_max, samples, latex=latex, save=save)
|
|
506
|
+
|
|
507
|
+
return samples
|
|
508
|
+
|
|
509
|
+
# ======
|
|
510
|
+
# matrix
|
|
511
|
+
# ======
|
|
512
|
+
|
|
513
|
+
def matrix(self, size):
|
|
514
|
+
"""
|
|
515
|
+
Generate matrix with the spectral density of the distribution.
|
|
516
|
+
|
|
517
|
+
Parameters
|
|
518
|
+
----------
|
|
519
|
+
|
|
520
|
+
size : int
|
|
521
|
+
Size :math:`n` of the matrix.
|
|
522
|
+
|
|
523
|
+
Returns
|
|
524
|
+
-------
|
|
525
|
+
|
|
526
|
+
A : numpy.ndarray
|
|
527
|
+
A matrix of the size :math:`n \\times n`.
|
|
528
|
+
|
|
529
|
+
Examples
|
|
530
|
+
--------
|
|
531
|
+
|
|
532
|
+
.. code-block::python
|
|
533
|
+
|
|
534
|
+
>>> from freealg.distributions import Wigner
|
|
535
|
+
>>> wg = Wigner()
|
|
536
|
+
>>> A = wg.matrix(2000)
|
|
537
|
+
"""
|
|
538
|
+
|
|
539
|
+
# Parameters
|
|
540
|
+
n = size
|
|
541
|
+
p = 1.0 / size
|
|
542
|
+
|
|
543
|
+
# Random graph
|
|
544
|
+
G = nx.erdos_renyi_graph(n, p)
|
|
545
|
+
|
|
546
|
+
# Adjancency
|
|
547
|
+
A = nx.to_numpy_array(G) # shape (n,n), 0/1 entries
|
|
548
|
+
|
|
549
|
+
# Center & scale to get the semicircle
|
|
550
|
+
A_c = (A - p) / numpy.sqrt(n * p * (1-p))
|
|
551
|
+
|
|
552
|
+
return A_c
|