freealg 0.5.4__py3-none-any.whl → 0.6.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/__init__.py +4 -1
- freealg/__version__.py +1 -1
- freealg/_chebyshev.py +31 -26
- freealg/_decompress.py +20 -15
- freealg/_jacobi.py +140 -53
- freealg/_pade.py +10 -10
- freealg/_sample.py +42 -18
- freealg/_series.py +9 -9
- freealg/_support.py +35 -22
- freealg/_util.py +138 -35
- freealg/distributions/_kesten_mckay.py +1 -1
- freealg/distributions/_marchenko_pastur.py +1 -1
- freealg/distributions/_meixner.py +2 -2
- freealg/distributions/_wachter.py +1 -1
- freealg/freeform.py +98 -69
- {freealg-0.5.4.dist-info → freealg-0.6.1.dist-info}/METADATA +1 -1
- freealg-0.6.1.dist-info/RECORD +26 -0
- freealg-0.5.4.dist-info/RECORD +0 -26
- {freealg-0.5.4.dist-info → freealg-0.6.1.dist-info}/WHEEL +0 -0
- {freealg-0.5.4.dist-info → freealg-0.6.1.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.5.4.dist-info → freealg-0.6.1.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.5.4.dist-info → freealg-0.6.1.dist-info}/top_level.txt +0 -0
freealg/_sample.py
CHANGED
|
@@ -15,7 +15,7 @@ from scipy.integrate import cumulative_trapezoid
|
|
|
15
15
|
from scipy.interpolate import PchipInterpolator
|
|
16
16
|
from scipy.stats import qmc
|
|
17
17
|
|
|
18
|
-
__all__ = ['
|
|
18
|
+
__all__ = ['sample']
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# =============
|
|
@@ -32,60 +32,75 @@ def _quantile_func(x, rho, clamp=1e-4, eps=1e-8):
|
|
|
32
32
|
rho_clamp[rho < clamp] = eps
|
|
33
33
|
cdf = cumulative_trapezoid(rho_clamp, x, initial=0)
|
|
34
34
|
cdf /= cdf[-1]
|
|
35
|
+
cdf_inv = PchipInterpolator(cdf, x, extrapolate=False)
|
|
35
36
|
|
|
36
|
-
return
|
|
37
|
+
return cdf_inv
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
40
|
+
# ======
|
|
41
|
+
# sample
|
|
42
|
+
# ======
|
|
42
43
|
|
|
43
|
-
def
|
|
44
|
+
def sample(x, rho, num_pts, method='qmc', seed=None):
|
|
44
45
|
"""
|
|
45
|
-
Low-discrepancy sampling from
|
|
46
|
-
Quasi-Monte Carlo.
|
|
46
|
+
Low-discrepancy sampling from density estimate.
|
|
47
47
|
|
|
48
48
|
Parameters
|
|
49
49
|
----------
|
|
50
50
|
|
|
51
|
-
x : numpy.array
|
|
52
|
-
Sorted abscissae at which the density has been evaluated.
|
|
51
|
+
x : numpy.array
|
|
52
|
+
Sorted abscissae at which the density has been evaluated. Shape `(n,)`.
|
|
53
53
|
|
|
54
|
-
rho : numpy.array
|
|
54
|
+
rho : numpy.array
|
|
55
55
|
Density values corresponding to `x`. Must be non-negative and define
|
|
56
56
|
a valid probability density (i.e., integrate to 1 over the support).
|
|
57
|
+
Shape `(n,)`.
|
|
57
58
|
|
|
58
59
|
num_pts : int
|
|
59
60
|
Number of sample points to generate from the density estimate.
|
|
60
61
|
|
|
62
|
+
method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
|
|
63
|
+
Method of drawing samples from uniform distribution:
|
|
64
|
+
|
|
65
|
+
* ``'mc'``: Monte Carlo
|
|
66
|
+
* ``'qmc'``: Quasi Monte Carlo
|
|
67
|
+
|
|
61
68
|
seed : int, default=None
|
|
62
69
|
Seed for random number generator
|
|
63
70
|
|
|
64
71
|
Returns
|
|
65
72
|
-------
|
|
73
|
+
|
|
66
74
|
samples : numpy.array, shape (num_pts,)
|
|
67
75
|
Samples drawn from the estimated density using a one-dimensional Halton
|
|
68
76
|
sequence mapped through the estimated quantile function.
|
|
69
77
|
|
|
70
78
|
See Also
|
|
71
79
|
--------
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
|
|
81
|
+
freealg.supp
|
|
82
|
+
freealg.kde
|
|
83
|
+
|
|
84
|
+
Notes
|
|
85
|
+
-----
|
|
86
|
+
|
|
87
|
+
The underlying Quasi-Monte Carlo engine uses ``scipy.stats.qmc.Halton``
|
|
88
|
+
function for generating low-discrepancy points.
|
|
75
89
|
|
|
76
90
|
Examples
|
|
77
91
|
--------
|
|
78
92
|
|
|
79
93
|
.. code-block:: python
|
|
94
|
+
:emphasize-lines: 8
|
|
80
95
|
|
|
81
96
|
>>> import numpy
|
|
82
|
-
>>> from freealg import
|
|
97
|
+
>>> from freealg import sample
|
|
83
98
|
|
|
84
99
|
>>> # density of Beta(3,1) on [0,1]
|
|
85
100
|
>>> x = numpy.linspace(0, 1, 200)
|
|
86
101
|
>>> rho = 3 * x**2
|
|
87
102
|
|
|
88
|
-
>>> samples =
|
|
103
|
+
>>> samples = sample(x, rho, num_pts=1000, method='qmc')
|
|
89
104
|
>>> assert samples.shape == (1000,)
|
|
90
105
|
|
|
91
106
|
>>> # Empirical mean should be close to 3/4
|
|
@@ -94,8 +109,17 @@ def qmc_sample(x, rho, num_pts, seed=None):
|
|
|
94
109
|
|
|
95
110
|
rng = numpy.random.default_rng(seed)
|
|
96
111
|
quantile = _quantile_func(x, rho)
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
|
|
113
|
+
# Draw from uniform distribution
|
|
114
|
+
if method == 'mc':
|
|
115
|
+
u = rng.random(num_pts)
|
|
116
|
+
elif method == 'qmc':
|
|
117
|
+
engine = qmc.Halton(d=1, rng=rng)
|
|
118
|
+
u = engine.random(num_pts)
|
|
119
|
+
else:
|
|
120
|
+
raise NotImplementedError('"method" is invalid.')
|
|
121
|
+
|
|
122
|
+
# Draw from distribution by mapping from inverse CDF
|
|
99
123
|
samples = quantile(u)
|
|
100
124
|
|
|
101
125
|
return samples.ravel()
|
freealg/_series.py
CHANGED
|
@@ -183,13 +183,13 @@ def wynn_rho(Sn, beta=0.0):
|
|
|
183
183
|
-------
|
|
184
184
|
|
|
185
185
|
S : numpy.ndarray
|
|
186
|
-
A 1D array of shape ``(d,)`` giving the rho
|
|
186
|
+
A 1D array of shape ``(d,)`` giving the rho-accelerated estimate
|
|
187
187
|
of the series limit for each component.
|
|
188
188
|
|
|
189
189
|
Notes
|
|
190
190
|
-----
|
|
191
191
|
|
|
192
|
-
Let ``S_n`` be the *n
|
|
192
|
+
Let ``S_n`` be the *n*-th partial sum of the (possibly divergent)
|
|
193
193
|
sequence. Wynn's rho algorithm builds a triangular table
|
|
194
194
|
``rho[k, n]`` (row *k*, column *n*) as follows:
|
|
195
195
|
|
|
@@ -200,7 +200,7 @@ def wynn_rho(Sn, beta=0.0):
|
|
|
200
200
|
(n + beta + k - 1) / (rho[k-1, n+1] - rho[k-1, n])
|
|
201
201
|
|
|
202
202
|
Only even rows (k even) provide improved approximants. As with
|
|
203
|
-
``wynn_epsilon``, we apply the scalar recursion component
|
|
203
|
+
``wynn_epsilon``, we apply the scalar recursion component-wise so that a
|
|
204
204
|
slowly converging component does not stall the others.
|
|
205
205
|
"""
|
|
206
206
|
|
|
@@ -255,7 +255,7 @@ def wynn_rho(Sn, beta=0.0):
|
|
|
255
255
|
|
|
256
256
|
def levin_u(Sn, omega=None, beta=0.0):
|
|
257
257
|
"""
|
|
258
|
-
Levin u
|
|
258
|
+
Levin u-transform (vector form).
|
|
259
259
|
|
|
260
260
|
Parameters
|
|
261
261
|
----------
|
|
@@ -339,13 +339,13 @@ def weniger_delta(Sn):
|
|
|
339
339
|
-------
|
|
340
340
|
|
|
341
341
|
S : numpy.ndarray
|
|
342
|
-
Array of shape (d,) giving the
|
|
343
|
-
component.
|
|
342
|
+
Array of shape (d,) giving the delta2 accelerated limit estimate for
|
|
343
|
+
each component.
|
|
344
344
|
"""
|
|
345
345
|
|
|
346
346
|
N, d = Sn.shape
|
|
347
347
|
|
|
348
|
-
# Need at least three partial sums to form
|
|
348
|
+
# Need at least three partial sums to form delta2
|
|
349
349
|
if N < 3:
|
|
350
350
|
return Sn[-1, :].copy()
|
|
351
351
|
|
|
@@ -384,14 +384,14 @@ def brezinski_theta(Sn):
|
|
|
384
384
|
----------
|
|
385
385
|
|
|
386
386
|
Sn : numpy.ndarray
|
|
387
|
-
A 2
|
|
387
|
+
A 2-D array of the size ``(N, d)``, where `N` is the number of partial
|
|
388
388
|
sums and `d` is the vector size.
|
|
389
389
|
|
|
390
390
|
Returns
|
|
391
391
|
-------
|
|
392
392
|
|
|
393
393
|
S : numpy.ndarray
|
|
394
|
-
A 1
|
|
394
|
+
A 1-D array of the size ``(d,)``. The theta-accelerated estimate of
|
|
395
395
|
the series limit in each vector component.
|
|
396
396
|
"""
|
|
397
397
|
|
freealg/_support.py
CHANGED
|
@@ -14,7 +14,7 @@ import numpy
|
|
|
14
14
|
import numba
|
|
15
15
|
from scipy.stats import gaussian_kde
|
|
16
16
|
|
|
17
|
-
__all__ = ['support_from_density', '
|
|
17
|
+
__all__ = ['support_from_density', 'supp']
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
# ====================
|
|
@@ -34,26 +34,26 @@ def support_from_density(dx, density):
|
|
|
34
34
|
n = density.shape[0]
|
|
35
35
|
target = 1.0 / dx
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# compute total_sum once
|
|
38
38
|
total_sum = 0.0
|
|
39
39
|
for t in range(n):
|
|
40
40
|
total_sum += density[t]
|
|
41
41
|
|
|
42
|
-
#
|
|
42
|
+
# set up our "best-so-far" trackers
|
|
43
43
|
large = 1e300
|
|
44
44
|
best_nonneg_sum = large
|
|
45
45
|
best_nonneg_idx = -1
|
|
46
46
|
best_nonpos_sum = -large
|
|
47
47
|
best_nonpos_idx = -1
|
|
48
48
|
|
|
49
|
-
#
|
|
49
|
+
# seed with first element (i.e. prefix_sum for k=1)
|
|
50
50
|
prefix_sum = density[0]
|
|
51
51
|
if prefix_sum >= 0.0:
|
|
52
52
|
best_nonneg_sum, best_nonneg_idx = prefix_sum, 1
|
|
53
53
|
else:
|
|
54
54
|
best_nonpos_sum, best_nonpos_idx = prefix_sum, 1
|
|
55
55
|
|
|
56
|
-
#
|
|
56
|
+
# sweep j from 2, ..., n-1, updating prefix_sum on the fly
|
|
57
57
|
optimal_i, optimal_j = 1, 2
|
|
58
58
|
minimal_cost = large
|
|
59
59
|
|
|
@@ -88,7 +88,7 @@ def support_from_density(dx, density):
|
|
|
88
88
|
minimal_cost = total_cost
|
|
89
89
|
optimal_i, optimal_j = i_cand, j
|
|
90
90
|
|
|
91
|
-
# update our prefix
|
|
91
|
+
# update our prefix-sum trackers
|
|
92
92
|
if prefix_sum >= 0.0:
|
|
93
93
|
if prefix_sum < best_nonneg_sum:
|
|
94
94
|
best_nonneg_sum, best_nonneg_idx = prefix_sum, j
|
|
@@ -99,36 +99,34 @@ def support_from_density(dx, density):
|
|
|
99
99
|
return optimal_i, optimal_j
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
102
|
+
# ====
|
|
103
|
+
# supp
|
|
104
|
+
# ====
|
|
105
105
|
|
|
106
|
-
def
|
|
106
|
+
def supp(eigs, method='asymp', k=None, p=0.001):
|
|
107
107
|
"""
|
|
108
108
|
Estimates the support of the eigenvalue density.
|
|
109
109
|
|
|
110
110
|
Parameters
|
|
111
111
|
----------
|
|
112
112
|
|
|
113
|
-
method : {``'range'``, ``'asymp'``, ``'jackknife'``, ``'regression'``,
|
|
114
|
-
|
|
115
|
-
default= ``'asymp'``
|
|
113
|
+
method : {``'range'``, ``'asymp'``, ``'jackknife'``, ``'regression'``, \
|
|
114
|
+
``'interior'``, ``'interior_smooth'``}, default= ``'asymp'``
|
|
116
115
|
The method of support estimation:
|
|
117
116
|
|
|
118
117
|
* ``'range'``: no estimation; the support is the range of the
|
|
119
|
-
|
|
118
|
+
eigenvalues.
|
|
120
119
|
* ``'asymp'``: assume the relative error in the min/max estimator is
|
|
121
|
-
|
|
122
|
-
* ``'jackknife'``: estimates the support using Quenouille's [1]
|
|
123
|
-
|
|
124
|
-
range.
|
|
120
|
+
:math:`1/n`.
|
|
121
|
+
* ``'jackknife'``: estimates the support using Quenouille's [1]_
|
|
122
|
+
jackknife estimator. Fast and simple, more accurate than the range.
|
|
125
123
|
* ``'regression'``: estimates the support by performing a regression
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
under the assumption that the edge behavior is of square-root type.
|
|
125
|
+
Often most accurate.
|
|
128
126
|
* ``'interior'``: estimates a support assuming the range overestimates;
|
|
129
|
-
|
|
127
|
+
uses quantiles :math:`(p, 1-p)`.
|
|
130
128
|
* ``'interior_smooth'``: same as ``'interior'`` but using kernel
|
|
131
|
-
|
|
129
|
+
density estimation, from [2]_.
|
|
132
130
|
|
|
133
131
|
k : int, default = None
|
|
134
132
|
Number of extreme order statistics to use for ``method='regression'``.
|
|
@@ -140,6 +138,21 @@ def detect_support(eigs, method='asymp', k=None, p=0.001, **kwargs):
|
|
|
140
138
|
This value should be between 0 and 1, ideally a small number close to
|
|
141
139
|
zero.
|
|
142
140
|
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
|
|
144
|
+
lam_m : float
|
|
145
|
+
Lower end of support interval :math:`[\\lambda_{-}, \\lambda_{+}]`.
|
|
146
|
+
|
|
147
|
+
lam_p : float
|
|
148
|
+
Upper end of support interval :math:`[\\lambda_{-}, \\lambda_{+}]`.
|
|
149
|
+
|
|
150
|
+
See Also
|
|
151
|
+
--------
|
|
152
|
+
|
|
153
|
+
freealg.sample
|
|
154
|
+
freealg.kde
|
|
155
|
+
|
|
143
156
|
References
|
|
144
157
|
----------
|
|
145
158
|
|
freealg/_util.py
CHANGED
|
@@ -13,14 +13,60 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
import scipy
|
|
16
|
+
from scipy.stats import gaussian_kde
|
|
16
17
|
from scipy.stats import beta
|
|
18
|
+
# from statsmodels.nonparametric.kde import KDEUnivariate
|
|
17
19
|
from scipy.optimize import minimize
|
|
20
|
+
import matplotlib.pyplot as plt
|
|
21
|
+
import texplot
|
|
22
|
+
from ._plot_util import _auto_bins
|
|
18
23
|
|
|
19
24
|
# Fallback to previous API
|
|
20
25
|
if not hasattr(numpy, 'trapezoid'):
|
|
21
26
|
numpy.trapezoid = numpy.trapz
|
|
22
27
|
|
|
23
|
-
__all__ = ['compute_eig', '
|
|
28
|
+
__all__ = ['resolve_complex_dtype', 'compute_eig', 'kde', 'force_density']
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# =====================
|
|
32
|
+
# resolve complex dtype
|
|
33
|
+
# =====================
|
|
34
|
+
|
|
35
|
+
def resolve_complex_dtype(dtype):
|
|
36
|
+
"""
|
|
37
|
+
Convert a user-supplied dtype name to a NumPy dtype object and fall back
|
|
38
|
+
safely if the requested precision is unavailable.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Normalise the string
|
|
42
|
+
dtype = str(dtype).lower()
|
|
43
|
+
|
|
44
|
+
if not isinstance(numpy.dtype(dtype), numpy.dtype):
|
|
45
|
+
raise ValueError(f'{dtype} is not a recognized numpy dtype.')
|
|
46
|
+
elif not numpy.issubdtype(numpy.dtype(dtype), numpy.complexfloating):
|
|
47
|
+
raise ValueError(f'{dtype} is not a complex dtype.')
|
|
48
|
+
|
|
49
|
+
if dtype in {'complex128', '128'}:
|
|
50
|
+
cdtype = numpy.complex128
|
|
51
|
+
|
|
52
|
+
elif dtype in ['complex256', '256', 'longcomplex', 'clongcomplex']:
|
|
53
|
+
|
|
54
|
+
complex256_found = False
|
|
55
|
+
for name in ['complex256', 'clongcomplex']:
|
|
56
|
+
if hasattr(numpy, name):
|
|
57
|
+
cdtype = getattr(numpy, name)
|
|
58
|
+
complex256_found = True
|
|
59
|
+
|
|
60
|
+
if not complex256_found:
|
|
61
|
+
raise RuntimeWarning(
|
|
62
|
+
'NumPy on this platform has no 256-bit complex type. ' +
|
|
63
|
+
'Falling back to complex128.')
|
|
64
|
+
cdtype = numpy.complex128
|
|
65
|
+
|
|
66
|
+
else:
|
|
67
|
+
raise ValueError('Unsupported dtype.')
|
|
68
|
+
|
|
69
|
+
return cdtype
|
|
24
70
|
|
|
25
71
|
|
|
26
72
|
# ===========
|
|
@@ -37,50 +83,107 @@ def compute_eig(A, lower=False):
|
|
|
37
83
|
return eig
|
|
38
84
|
|
|
39
85
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
86
|
+
# ===
|
|
87
|
+
# kde
|
|
88
|
+
# ===
|
|
43
89
|
|
|
44
|
-
def
|
|
90
|
+
def kde(eig, xs, lam_m, lam_p, h, kernel='beta', plot=False):
|
|
45
91
|
"""
|
|
46
|
-
|
|
92
|
+
Kernel density estimation of eigenvalues.
|
|
47
93
|
|
|
48
94
|
Parameters
|
|
49
95
|
----------
|
|
50
|
-
eig : (n,) 1-D array of samples
|
|
51
|
-
xs : evaluation grid (must lie within [lam_m, lam_p])
|
|
52
|
-
lam_m, lam_p : float, support endpoints (lam_m < lam_p)
|
|
53
|
-
h : bandwidth in rescaled units (0 < h < 1)
|
|
54
96
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
97
|
+
eig : numpy.array
|
|
98
|
+
1D array of samples of size `n`.
|
|
99
|
+
|
|
100
|
+
xs : numpy.array
|
|
101
|
+
1D array of evaluation grid (must lie within ``[lam_m, lam_p]``)
|
|
102
|
+
|
|
103
|
+
lam_m : float
|
|
104
|
+
Lower end of the support endpoints with ``lam_m < lam_p``.
|
|
59
105
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
raise ValueError("lam_p must be larger than lam_m")
|
|
106
|
+
lam_p : float
|
|
107
|
+
Upper end of the support endpoints with ``lam_m < lam_p``.
|
|
63
108
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
t = (xs - lam_m) / span
|
|
109
|
+
h : float
|
|
110
|
+
Kernel bandwidth in rescaled units where ``0 < h < 1``.
|
|
67
111
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
u = u[mask]
|
|
112
|
+
kernel : {``'gaussian'``, ``'beta'``}, default= ``'beta'``
|
|
113
|
+
Kernel function using either Gaussian or Beta distribution.
|
|
71
114
|
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
plot : bool, default=False
|
|
116
|
+
If `True`, the KDE is plotted.
|
|
74
117
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
|
|
121
|
+
pdf : numpy.ndarray
|
|
122
|
+
Probability distribution function with the same length as ``xs``.
|
|
123
|
+
|
|
124
|
+
See Also
|
|
125
|
+
--------
|
|
126
|
+
|
|
127
|
+
freealg.supp
|
|
128
|
+
freealg.sample
|
|
129
|
+
"""
|
|
81
130
|
|
|
82
|
-
|
|
83
|
-
|
|
131
|
+
if kernel == 'gaussian':
|
|
132
|
+
pdf = gaussian_kde(eig, bw_method=h)(xs)
|
|
133
|
+
|
|
134
|
+
# Adaptive KDE
|
|
135
|
+
# k = KDEUnivariate(eig)
|
|
136
|
+
# k.fit(kernel='gau', bw='silverman', fft=False, weights=None,
|
|
137
|
+
# gridsize=1024, adaptive=True)
|
|
138
|
+
# pdf = k.evaluate(xs)
|
|
139
|
+
|
|
140
|
+
elif kernel == 'beta':
|
|
141
|
+
|
|
142
|
+
span = lam_p - lam_m
|
|
143
|
+
if span <= 0:
|
|
144
|
+
raise ValueError("lam_p must be larger than lam_m")
|
|
145
|
+
|
|
146
|
+
# map samples and grid to [0, 1]
|
|
147
|
+
u = (eig - lam_m) / span
|
|
148
|
+
t = (xs - lam_m) / span
|
|
149
|
+
|
|
150
|
+
if u.min() < 0 or u.max() > 1:
|
|
151
|
+
mask = (u > 0) & (u < 1)
|
|
152
|
+
u = u[mask]
|
|
153
|
+
|
|
154
|
+
pdf = numpy.zeros_like(xs, dtype=float)
|
|
155
|
+
n = len(u)
|
|
156
|
+
|
|
157
|
+
# tiny positive number to keep shape parameters > 0
|
|
158
|
+
eps = 1e-6
|
|
159
|
+
for ui in u:
|
|
160
|
+
a = max(ui / h + 1.0, eps)
|
|
161
|
+
b = max((1.0 - ui) / h + 1.0, eps)
|
|
162
|
+
pdf += beta.pdf(t, a, b)
|
|
163
|
+
|
|
164
|
+
pdf /= n * span # renormalise
|
|
165
|
+
pdf[(t < 0) | (t > 1)] = 0.0 # exact zeros outside
|
|
166
|
+
|
|
167
|
+
else:
|
|
168
|
+
raise NotImplementedError('"kernel" is invalid.')
|
|
169
|
+
|
|
170
|
+
if plot:
|
|
171
|
+
with texplot.theme(use_latex=False):
|
|
172
|
+
fig, ax = plt.subplots(figsize=(6, 4))
|
|
173
|
+
|
|
174
|
+
x_min = numpy.min(xs)
|
|
175
|
+
x_max = numpy.max(xs)
|
|
176
|
+
bins = numpy.linspace(x_min, x_max, _auto_bins(eig))
|
|
177
|
+
_ = ax.hist(eig, bins, density=True, color='silver',
|
|
178
|
+
edgecolor='none', label='Samples histogram')
|
|
179
|
+
ax.plot(xs, pdf, color='black', label='KDE')
|
|
180
|
+
ax.set_xlabel(r'$x$')
|
|
181
|
+
ax.set_ylabel(r'$\\rho(x)$')
|
|
182
|
+
ax.set_xlim([xs[0], xs[-1]])
|
|
183
|
+
ax.set_ylim(bottom=0)
|
|
184
|
+
ax.set_title('Kernel Density Estimation')
|
|
185
|
+
ax.legend(fontsize='x-small')
|
|
186
|
+
plt.show()
|
|
84
187
|
|
|
85
188
|
return pdf
|
|
86
189
|
|
|
@@ -95,8 +198,8 @@ def force_density(psi0, support, density, grid, alpha=0.0, beta=0.0):
|
|
|
95
198
|
min 0.5 ||psi - psi0||^2
|
|
96
199
|
s.t. F_pos psi >= 0 (positivity on grid)
|
|
97
200
|
psi[0] = psi0[0] (mass)
|
|
98
|
-
f(lam_m)
|
|
99
|
-
f(lam_p)
|
|
201
|
+
f(lam_m) psi = 0 (zero at left edge)
|
|
202
|
+
f(lam_p) psi = 0 (zero at right edge)
|
|
100
203
|
"""
|
|
101
204
|
|
|
102
205
|
lam_m, lam_p = support
|
|
@@ -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
|
|
@@ -290,7 +290,7 @@ class MarchenkoPastur(object):
|
|
|
290
290
|
m1 = (-B + sqrtD) / (2 * A)
|
|
291
291
|
m2 = (-B - sqrtD) / (2 * A)
|
|
292
292
|
|
|
293
|
-
# pick correct branch only for non
|
|
293
|
+
# pick correct branch only for non-masked entries
|
|
294
294
|
upper = z[not_mask].imag >= 0
|
|
295
295
|
branch = numpy.empty_like(m1)
|
|
296
296
|
branch[upper] = numpy.where(sign*m1[upper].imag > 0, m1[upper],
|
|
@@ -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
|
--------
|
|
@@ -315,7 +315,7 @@ class Meixner(object):
|
|
|
315
315
|
m1 = (-B + sqrtD) / (2 * A)
|
|
316
316
|
m2 = (-B - sqrtD) / (2 * A)
|
|
317
317
|
|
|
318
|
-
# pick correct branch only for non
|
|
318
|
+
# pick correct branch only for non-masked entries
|
|
319
319
|
upper = z.imag >= 0
|
|
320
320
|
branch = numpy.empty_like(m1)
|
|
321
321
|
branch[upper] = numpy.where(
|
|
@@ -290,7 +290,7 @@ class Wachter(object):
|
|
|
290
290
|
m1 = (-B + sqrtD) / (2 * A)
|
|
291
291
|
m2 = (-B - sqrtD) / (2 * A)
|
|
292
292
|
|
|
293
|
-
# pick correct branch only for non
|
|
293
|
+
# pick correct branch only for non-masked entries
|
|
294
294
|
upper = z.imag >= 0
|
|
295
295
|
branch = numpy.empty_like(m1)
|
|
296
296
|
branch[upper] = numpy.where(sign*m1[upper].imag > 0, m1[upper],
|