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