freealg 0.0.2__py3-none-any.whl → 0.0.3__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/_decompress.py +136 -0
- freealg/_jacobi.py +0 -2
- freealg/_plot_util.py +64 -52
- freealg/freeform.py +218 -49
- {freealg-0.0.2.dist-info → freealg-0.0.3.dist-info}/METADATA +1 -1
- freealg-0.0.3.dist-info/RECORD +17 -0
- freealg-0.0.2.dist-info/RECORD +0 -16
- {freealg-0.0.2.dist-info → freealg-0.0.3.dist-info}/WHEEL +0 -0
- {freealg-0.0.2.dist-info → freealg-0.0.3.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.0.2.dist-info → freealg-0.0.3.dist-info}/top_level.txt +0 -0
freealg/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.3"
|
freealg/_decompress.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
# SPDX-FileType: SOURCE
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
# the terms of the license found in the LICENSE.txt file in the root directory
|
|
6
|
+
# of this source tree.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# =======
|
|
10
|
+
# Imports
|
|
11
|
+
# =======
|
|
12
|
+
|
|
13
|
+
import numpy
|
|
14
|
+
|
|
15
|
+
__all__ = ['decompress']
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ==========
|
|
19
|
+
# decompress
|
|
20
|
+
# ==========
|
|
21
|
+
|
|
22
|
+
def decompress(matrix, size, x=None, delta=1e-4, iterations=500, step_size=0.1,
|
|
23
|
+
tolerance=1e-4):
|
|
24
|
+
"""
|
|
25
|
+
Free decompression of spectral density.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
|
|
30
|
+
matrix : FreeForm
|
|
31
|
+
The initial matrix to be decompressed
|
|
32
|
+
|
|
33
|
+
size : int
|
|
34
|
+
Size of the decompressed matrix.
|
|
35
|
+
|
|
36
|
+
x : numpy.array, default=None
|
|
37
|
+
Positions where density to be evaluated at. If `None`, an interval
|
|
38
|
+
slightly larger than the support interval will be used.
|
|
39
|
+
|
|
40
|
+
delta: float, default=1e-4
|
|
41
|
+
Size of the perturbation into the upper half plane for Plemelj's
|
|
42
|
+
formula.
|
|
43
|
+
|
|
44
|
+
iterations: int, default=500
|
|
45
|
+
Maximum number of Newton iterations.
|
|
46
|
+
|
|
47
|
+
step_size: float, default=0.1
|
|
48
|
+
Step size for Newton iterations.
|
|
49
|
+
|
|
50
|
+
tolerance: float, default=1e-4
|
|
51
|
+
Tolerance for the solution obtained by the Newton solver. Also
|
|
52
|
+
used for the finite difference approximation to the derivative.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
|
|
57
|
+
rho : numpy.array
|
|
58
|
+
Spectral density
|
|
59
|
+
|
|
60
|
+
See Also
|
|
61
|
+
--------
|
|
62
|
+
|
|
63
|
+
density
|
|
64
|
+
stieltjes
|
|
65
|
+
|
|
66
|
+
Notes
|
|
67
|
+
-----
|
|
68
|
+
|
|
69
|
+
Work in progress.
|
|
70
|
+
|
|
71
|
+
References
|
|
72
|
+
----------
|
|
73
|
+
|
|
74
|
+
.. [1] tbd
|
|
75
|
+
|
|
76
|
+
Examples
|
|
77
|
+
--------
|
|
78
|
+
|
|
79
|
+
.. code-block:: python
|
|
80
|
+
|
|
81
|
+
>>> from freealg import FreeForm
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
alpha = size / matrix.n
|
|
85
|
+
m = matrix._eval_stieltjes
|
|
86
|
+
# Lower and upper bound on new support
|
|
87
|
+
hilb_lb = (1 / m(matrix.lam_m + delta * 1j)[1]).real
|
|
88
|
+
hilb_ub = (1 / m(matrix.lam_p + delta * 1j)[1]).real
|
|
89
|
+
lb = matrix.lam_m - (alpha - 1) * hilb_lb
|
|
90
|
+
ub = matrix.lam_p - (alpha - 1) * hilb_ub
|
|
91
|
+
|
|
92
|
+
# Create x if not given
|
|
93
|
+
if x is None:
|
|
94
|
+
radius = 0.5 * (ub - lb)
|
|
95
|
+
center = 0.5 * (ub + lb)
|
|
96
|
+
scale = 1.25
|
|
97
|
+
x_min = numpy.floor(center - radius * scale)
|
|
98
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
99
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
100
|
+
|
|
101
|
+
def _char_z(z):
|
|
102
|
+
return z + (1 / m(z)[1]) * (1 - alpha)
|
|
103
|
+
|
|
104
|
+
# Ensure that input is an array
|
|
105
|
+
x = numpy.asarray(x)
|
|
106
|
+
|
|
107
|
+
target = x + delta * 1j
|
|
108
|
+
|
|
109
|
+
z = numpy.full(target.shape, numpy.mean(matrix.support) - .1j,
|
|
110
|
+
dtype=numpy.complex128)
|
|
111
|
+
|
|
112
|
+
# Broken Newton steps can produce a lot of warnings. Removing them
|
|
113
|
+
# for now.
|
|
114
|
+
with numpy.errstate(all='ignore'):
|
|
115
|
+
for _ in range(iterations):
|
|
116
|
+
objective = _char_z(z) - target
|
|
117
|
+
mask = numpy.abs(objective) >= tolerance
|
|
118
|
+
if not numpy.any(mask):
|
|
119
|
+
break
|
|
120
|
+
z_m = z[mask]
|
|
121
|
+
|
|
122
|
+
# Perform finite difference approximation
|
|
123
|
+
dfdz = _char_z(z_m+tolerance) - _char_z(z_m-tolerance)
|
|
124
|
+
dfdz /= 2*tolerance
|
|
125
|
+
dfdz[dfdz == 0] = 1.0
|
|
126
|
+
|
|
127
|
+
# Perform Newton step
|
|
128
|
+
z[mask] = z_m - step_size * objective[mask] / dfdz
|
|
129
|
+
|
|
130
|
+
# Plemelj's formula
|
|
131
|
+
char_s = m(z)[1] / alpha
|
|
132
|
+
rho = numpy.maximum(0, char_s.imag / numpy.pi)
|
|
133
|
+
rho[numpy.isnan(rho) | numpy.isinf(rho)] = 0
|
|
134
|
+
rho = rho.reshape(*x.shape)
|
|
135
|
+
|
|
136
|
+
return rho, x, (lb, ub)
|
freealg/_jacobi.py
CHANGED
|
@@ -174,9 +174,7 @@ def jacobi_stieltjes(z, psi, support, alpha=0.0, beta=0.0, n_base=40):
|
|
|
174
174
|
integrand = w_nodes * P_k_nodes # (n_quad,)
|
|
175
175
|
|
|
176
176
|
# Broadcast over z: shape (n_quad, ...) / ...
|
|
177
|
-
# diff = u_z[None, ...] - t_nodes[:, None] # (n_quad, ...)
|
|
178
177
|
diff = u_z[None, ...] - t_nodes[:, None, None] # (n_quad, Ny, Nx)
|
|
179
|
-
# m_k = (integrand[:, None] / diff).sum(axis=0) # shape like z
|
|
180
178
|
m_k = (integrand[:, None, None] / diff).sum(axis=0)
|
|
181
179
|
|
|
182
180
|
# Accumulate with factor 2/span
|
freealg/_plot_util.py
CHANGED
|
@@ -19,28 +19,21 @@ import colorsys
|
|
|
19
19
|
import matplotlib.ticker as ticker
|
|
20
20
|
import matplotlib.gridspec as gridspec
|
|
21
21
|
|
|
22
|
-
__all__ = ['
|
|
23
|
-
'plot_stieltjes_on_disk'
|
|
22
|
+
__all__ = ['plot_fit', 'plot_density', 'plot_hilbert', 'plot_stieltjes',
|
|
23
|
+
'plot_stieltjes_on_disk']
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
# plot coeff
|
|
28
|
-
#
|
|
26
|
+
# ==============
|
|
27
|
+
# plot coeff fit
|
|
28
|
+
# ==============
|
|
29
29
|
|
|
30
|
-
def
|
|
30
|
+
def plot_coeff_fit(psi, latex=False, save=False):
|
|
31
31
|
"""
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
with texplot.theme(use_latex=latex):
|
|
35
35
|
|
|
36
36
|
fig, ax = plt.subplots(figsize=(6, 2.7))
|
|
37
|
-
n = numpy.arange(1, 1+psi.size)
|
|
38
|
-
ax.plot(n, psi**2, '-o', markersize=3, color='black')
|
|
39
|
-
ax.set_xlim([n[0]-1e-3, n[-1]+1e-3])
|
|
40
|
-
ax.set_xlabel(r'$k$')
|
|
41
|
-
ax.set_ylabel(r'$\vert \psi_k \vert^2$')
|
|
42
|
-
ax.set_title('Spectral Energy per Mode')
|
|
43
|
-
ax.set_yscale('log')
|
|
44
37
|
|
|
45
38
|
# Save
|
|
46
39
|
if save is False:
|
|
@@ -58,6 +51,64 @@ def plot_coeff(psi, latex=False, save=False):
|
|
|
58
51
|
show_and_save=save_status, verbose=True)
|
|
59
52
|
|
|
60
53
|
|
|
54
|
+
# ========
|
|
55
|
+
# plot fit
|
|
56
|
+
# ========
|
|
57
|
+
|
|
58
|
+
def plot_fit(psi, x_supp, g_supp, g_supp_approx, support, latex=False,
|
|
59
|
+
save=False):
|
|
60
|
+
"""
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
with texplot.theme(use_latex=latex):
|
|
64
|
+
|
|
65
|
+
fig, ax = plt.subplots(figsize=(9, 3), ncols=2)
|
|
66
|
+
|
|
67
|
+
# Plot psi
|
|
68
|
+
n = numpy.arange(1, 1+psi.size)
|
|
69
|
+
ax[0].plot(n, psi**2, '-o', markersize=3, color='black')
|
|
70
|
+
ax[0].set_xlim([n[0]-1e-3, n[-1]+1e-3])
|
|
71
|
+
ax[0].set_xlabel(r'$k$')
|
|
72
|
+
ax[0].set_ylabel(r'$\vert \psi_k \vert^2$')
|
|
73
|
+
ax[0].set_title('Spectral Energy per Mode')
|
|
74
|
+
ax[0].set_yscale('log')
|
|
75
|
+
|
|
76
|
+
# Plot pade
|
|
77
|
+
lam_m, lam_p = support
|
|
78
|
+
g_supp_min = numpy.min(g_supp)
|
|
79
|
+
g_supp_max = numpy.max(g_supp)
|
|
80
|
+
g_supp_dif = g_supp_max - g_supp_min
|
|
81
|
+
g_min = g_supp_min - g_supp_dif * 1.1
|
|
82
|
+
g_max = g_supp_max + g_supp_dif * 1.1
|
|
83
|
+
|
|
84
|
+
ax[1].plot(x_supp, g_supp, color='firebrick',
|
|
85
|
+
label=r'$2 \pi \times $ Hilbert Transform')
|
|
86
|
+
ax[1].plot(x_supp, g_supp_approx, color='black', label='Pade estimate')
|
|
87
|
+
ax[1].legend(fontsize='small')
|
|
88
|
+
ax[1].set_xlim([lam_m, lam_p])
|
|
89
|
+
ax[1].set_ylim([g_min, g_max])
|
|
90
|
+
ax[1].set_title('Approximation of Glue Function')
|
|
91
|
+
ax[1].set_xlabel(r'$x$')
|
|
92
|
+
ax[1].set_ylabel(r'$G(x)$')
|
|
93
|
+
|
|
94
|
+
plt.tight_layout()
|
|
95
|
+
|
|
96
|
+
# Save
|
|
97
|
+
if save is False:
|
|
98
|
+
save_status = False
|
|
99
|
+
save_filename = ''
|
|
100
|
+
else:
|
|
101
|
+
save_status = True
|
|
102
|
+
if isinstance(save, str):
|
|
103
|
+
save_filename = save
|
|
104
|
+
else:
|
|
105
|
+
save_filename = 'fit.pdf'
|
|
106
|
+
|
|
107
|
+
texplot.show_or_save_plot(plt, default_filename=save_filename,
|
|
108
|
+
transparent_background=True, dpi=400,
|
|
109
|
+
show_and_save=save_status, verbose=True)
|
|
110
|
+
|
|
111
|
+
|
|
61
112
|
# ============
|
|
62
113
|
# plot density
|
|
63
114
|
# ============
|
|
@@ -462,45 +513,6 @@ def plot_stieltjes_on_disk(r, t, m1_D, m2_D, support, latex=False, save=False):
|
|
|
462
513
|
show_and_save=save_status, verbose=True)
|
|
463
514
|
|
|
464
515
|
|
|
465
|
-
# =============
|
|
466
|
-
# plot glue fit
|
|
467
|
-
# =============
|
|
468
|
-
|
|
469
|
-
def plot_glue_fit(x_supp, g_supp, g_supp_approx, support, latex=False,
|
|
470
|
-
save=False):
|
|
471
|
-
"""
|
|
472
|
-
"""
|
|
473
|
-
|
|
474
|
-
with texplot.theme(use_latex=latex):
|
|
475
|
-
|
|
476
|
-
fig, ax = plt.subplots(figsize=(6, 3))
|
|
477
|
-
|
|
478
|
-
lam_m, lam_p = support
|
|
479
|
-
ax.plot(x_supp, g_supp, color='black', label='Glue target')
|
|
480
|
-
ax.plot(x_supp, g_supp_approx, color='firebrick',
|
|
481
|
-
label='Glue estimate')
|
|
482
|
-
ax.legend(fontsize='small')
|
|
483
|
-
ax.set_xlim([lam_m, lam_p])
|
|
484
|
-
ax.set_title('Approximation of Glue function on real axis')
|
|
485
|
-
ax.set_xlabel(r'$x$')
|
|
486
|
-
ax.set_ylabel(r'$G(x)$')
|
|
487
|
-
|
|
488
|
-
# Save
|
|
489
|
-
if save is False:
|
|
490
|
-
save_status = False
|
|
491
|
-
save_filename = ''
|
|
492
|
-
else:
|
|
493
|
-
save_status = True
|
|
494
|
-
if isinstance(save, str):
|
|
495
|
-
save_filename = save
|
|
496
|
-
else:
|
|
497
|
-
save_filename = 'glue_fit.pdf'
|
|
498
|
-
|
|
499
|
-
texplot.show_or_save_plot(plt, default_filename=save_filename,
|
|
500
|
-
transparent_background=True, dpi=400,
|
|
501
|
-
show_and_save=save_status, verbose=True)
|
|
502
|
-
|
|
503
|
-
|
|
504
516
|
# ============
|
|
505
517
|
# plot samples
|
|
506
518
|
# ============
|
freealg/freeform.py
CHANGED
|
@@ -12,15 +12,16 @@
|
|
|
12
12
|
# =======
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
|
+
from scipy.stats import gaussian_kde
|
|
15
16
|
from functools import partial
|
|
16
17
|
from ._util import compute_eig, force_density
|
|
17
18
|
from ._jacobi import jacobi_proj, jacobi_approx, jacobi_stieltjes
|
|
18
19
|
from ._chebyshev import chebyshev_proj, chebyshev_approx, chebyshev_stieltjes
|
|
19
20
|
from ._damp import jackson_damping, lanczos_damping, fejer_damping, \
|
|
20
21
|
exponential_damping, parzen_damping
|
|
21
|
-
from ._plot_util import
|
|
22
|
-
plot_stieltjes, plot_glue_fit
|
|
22
|
+
from ._plot_util import plot_fit, plot_density, plot_hilbert, plot_stieltjes
|
|
23
23
|
from ._pade import fit_pade, eval_pade
|
|
24
|
+
from ._decompress import decompress
|
|
24
25
|
|
|
25
26
|
__all__ = ['FreeForm']
|
|
26
27
|
|
|
@@ -45,6 +46,13 @@ class FreeForm(object):
|
|
|
45
46
|
The support of the density of :math:`\\mathbf{A}`. If `None`, it is
|
|
46
47
|
estimated from the minimum and maximum of the eigenvalues.
|
|
47
48
|
|
|
49
|
+
p : float, default=0.001
|
|
50
|
+
The edges of the support of the distribution is detected by the
|
|
51
|
+
:math:`p`-quantile on the left and :math:`(1-p)`-quantile on the right.
|
|
52
|
+
If the argument ``support`` is directly provided, this option is
|
|
53
|
+
ignored. This value should be between 0 and 1, ideally a small
|
|
54
|
+
number close to zero.
|
|
55
|
+
|
|
48
56
|
Notes
|
|
49
57
|
-----
|
|
50
58
|
|
|
@@ -64,6 +72,10 @@ class FreeForm(object):
|
|
|
64
72
|
psi : numpy.array
|
|
65
73
|
Jacobi coefficients.
|
|
66
74
|
|
|
75
|
+
n : int
|
|
76
|
+
Initial array size (assuming a square matrix when :math:`\\mathbf{A}`
|
|
77
|
+
is 2D)
|
|
78
|
+
|
|
67
79
|
Methods
|
|
68
80
|
-------
|
|
69
81
|
|
|
@@ -94,7 +106,7 @@ class FreeForm(object):
|
|
|
94
106
|
# init
|
|
95
107
|
# ====
|
|
96
108
|
|
|
97
|
-
def __init__(self, A, support=None):
|
|
109
|
+
def __init__(self, A, support=None, p=0.001):
|
|
98
110
|
"""
|
|
99
111
|
Initialization.
|
|
100
112
|
"""
|
|
@@ -106,16 +118,20 @@ class FreeForm(object):
|
|
|
106
118
|
if A.ndim == 1:
|
|
107
119
|
# When A is a 1D array, it is assumed A is the eigenvalue array.
|
|
108
120
|
self.eig = A
|
|
121
|
+
self.n = len(A)
|
|
109
122
|
elif A.ndim == 2:
|
|
110
123
|
# When A is a 2D array, it is assumed A is the actual array,
|
|
111
124
|
# and its eigenvalues will be computed.
|
|
112
125
|
self.A = A
|
|
126
|
+
self.n = A.shape[0]
|
|
127
|
+
assert A.shape[0] == A.shape[1], \
|
|
128
|
+
'Only square matrices are permitted.'
|
|
113
129
|
self.eig = compute_eig(A)
|
|
114
130
|
|
|
115
131
|
# Support
|
|
116
132
|
if support is None:
|
|
117
|
-
self.lam_m = self.eig
|
|
118
|
-
|
|
133
|
+
self.lam_m, self.lam_p = self._detect_support(self.eig, p,
|
|
134
|
+
smoothen=True)
|
|
119
135
|
else:
|
|
120
136
|
self.lam_m = support[0]
|
|
121
137
|
self.lam_p = support[1]
|
|
@@ -126,20 +142,46 @@ class FreeForm(object):
|
|
|
126
142
|
self.psi = None
|
|
127
143
|
self.alpha = None
|
|
128
144
|
self.beta = None
|
|
145
|
+
self._pade_sol = None
|
|
146
|
+
|
|
147
|
+
# ==============
|
|
148
|
+
# detect support
|
|
149
|
+
# ==============
|
|
150
|
+
|
|
151
|
+
def _detect_support(self, eig, p, smoothen=True):
|
|
152
|
+
"""
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
# Using quantile directly.
|
|
156
|
+
if smoothen:
|
|
157
|
+
kde = gaussian_kde(eig)
|
|
158
|
+
xs = numpy.linspace(eig.min(), eig.max(), 1000)
|
|
159
|
+
fs = kde(xs)
|
|
160
|
+
|
|
161
|
+
cdf = numpy.cumsum(fs)
|
|
162
|
+
cdf /= cdf[-1]
|
|
163
|
+
|
|
164
|
+
lam_m = numpy.interp(p, cdf, xs)
|
|
165
|
+
lam_p = numpy.interp(1-p, cdf, xs)
|
|
166
|
+
else:
|
|
167
|
+
lam_m, lam_p = numpy.quantile(eig, [p, 1-p])
|
|
168
|
+
|
|
169
|
+
return lam_m, lam_p
|
|
129
170
|
|
|
130
171
|
# ===
|
|
131
172
|
# fit
|
|
132
173
|
# ===
|
|
133
174
|
|
|
134
175
|
def fit(self, method='jacobi', K=10, alpha=0.0, beta=0.0, reg=0.0,
|
|
135
|
-
damp=None, force=False,
|
|
176
|
+
damp=None, force=False, pade_p=1, pade_q=1, plot=False,
|
|
177
|
+
latex=False, save=False):
|
|
136
178
|
"""
|
|
137
179
|
Fit model to eigenvalues.
|
|
138
180
|
|
|
139
181
|
Parameters
|
|
140
182
|
----------
|
|
141
183
|
|
|
142
|
-
method : {``'jacobi'``, ``'chebyshev'``}, default
|
|
184
|
+
method : {``'jacobi'``, ``'chebyshev'``}, default= ``'jacobi'``
|
|
143
185
|
Method of approximation, either by Jacobi polynomials or Chebyshev
|
|
144
186
|
polynomials of the second kind.
|
|
145
187
|
|
|
@@ -167,8 +209,15 @@ class FreeForm(object):
|
|
|
167
209
|
If `True`, it forces the density to have unit mass and to be
|
|
168
210
|
strictly positive.
|
|
169
211
|
|
|
212
|
+
pade_p : int, default=1
|
|
213
|
+
Degree of polynomial :math:`P(z)`. See notes below.
|
|
214
|
+
|
|
215
|
+
pade_q : int, default=1
|
|
216
|
+
Degree of polynomial :math:`Q(z)`. See notes below.
|
|
217
|
+
|
|
170
218
|
plot : bool, default=False
|
|
171
|
-
If `True`,
|
|
219
|
+
If `True`, the approximation coefficients and pade approximaton to
|
|
220
|
+
the Hilbert traosnform are plotted.
|
|
172
221
|
|
|
173
222
|
latex : bool, default=False
|
|
174
223
|
If `True`, the plot is rendered using LaTeX. This option is
|
|
@@ -245,8 +294,24 @@ class FreeForm(object):
|
|
|
245
294
|
self.alpha = alpha
|
|
246
295
|
self.beta = beta
|
|
247
296
|
|
|
297
|
+
# For holomorphic continuation for the lower half-plane
|
|
298
|
+
x_supp = numpy.linspace(self.lam_m, self.lam_p, 1000)
|
|
299
|
+
g_supp = 2.0 * numpy.pi * self.hilbert(x_supp)
|
|
300
|
+
|
|
301
|
+
# Fit a pade approximation
|
|
302
|
+
self._pade_sol = fit_pade(x_supp, g_supp, self.lam_m,
|
|
303
|
+
self.lam_p, pade_p, pade_q, delta=1e-8,
|
|
304
|
+
B=numpy.inf, S=numpy.inf)
|
|
305
|
+
|
|
248
306
|
if plot:
|
|
249
|
-
|
|
307
|
+
# Unpack optimized parameters
|
|
308
|
+
s = self._pade_sol['s']
|
|
309
|
+
a = self._pade_sol['a']
|
|
310
|
+
b = self._pade_sol['b']
|
|
311
|
+
|
|
312
|
+
g_supp_approx = eval_pade(x_supp[None, :], s, a, b)[0, :]
|
|
313
|
+
plot_fit(psi, x_supp, g_supp, g_supp_approx, support=self.support,
|
|
314
|
+
latex=latex, save=save)
|
|
250
315
|
|
|
251
316
|
return self.psi
|
|
252
317
|
|
|
@@ -444,41 +509,28 @@ class FreeForm(object):
|
|
|
444
509
|
# glue
|
|
445
510
|
# ====
|
|
446
511
|
|
|
447
|
-
def _glue(self, z
|
|
512
|
+
def _glue(self, z):
|
|
448
513
|
"""
|
|
449
514
|
"""
|
|
450
515
|
|
|
451
|
-
# Holomorphic continuation for the lower half-plane
|
|
452
|
-
x_supp = numpy.linspace(self.lam_m, self.lam_p, 1000)
|
|
453
|
-
g_supp = 2.0 * numpy.pi * self.hilbert(x_supp)
|
|
454
|
-
|
|
455
|
-
# Fit a pade approximation
|
|
456
|
-
sol = fit_pade(x_supp, g_supp, self.lam_m, self.lam_p, p, q,
|
|
457
|
-
delta=1e-8, B=numpy.inf, S=numpy.inf)
|
|
458
|
-
|
|
459
516
|
# Unpack optimized parameters
|
|
460
|
-
s =
|
|
461
|
-
a =
|
|
462
|
-
b =
|
|
517
|
+
s = self._pade_sol['s']
|
|
518
|
+
a = self._pade_sol['a']
|
|
519
|
+
b = self._pade_sol['b']
|
|
463
520
|
|
|
464
521
|
# Glue function
|
|
465
522
|
g = eval_pade(z, s, a, b)
|
|
466
523
|
|
|
467
|
-
if plot_glue:
|
|
468
|
-
g_supp_approx = eval_pade(x_supp[None, :], s, a, b)[0, :]
|
|
469
|
-
plot_glue_fit(x_supp, g_supp, g_supp_approx, support=self.support,
|
|
470
|
-
latex=latex, save=save)
|
|
471
|
-
|
|
472
524
|
return g
|
|
473
525
|
|
|
474
526
|
# =========
|
|
475
527
|
# stieltjes
|
|
476
528
|
# =========
|
|
477
529
|
|
|
478
|
-
def stieltjes(self, x, y,
|
|
479
|
-
latex=False, save=False):
|
|
530
|
+
def stieltjes(self, x, y, plot=False, latex=False, save=False):
|
|
480
531
|
"""
|
|
481
|
-
Compute Stieltjes transform of the spectral density
|
|
532
|
+
Compute Stieltjes transform of the spectral density over a 2D Cartesian
|
|
533
|
+
grid on the complex plane.
|
|
482
534
|
|
|
483
535
|
Parameters
|
|
484
536
|
----------
|
|
@@ -492,19 +544,9 @@ class FreeForm(object):
|
|
|
492
544
|
The y axis of the grid where the Stieltjes transform is evaluated.
|
|
493
545
|
If `None`, a grid on the interval ``[-1, 1]`` is used.
|
|
494
546
|
|
|
495
|
-
p : int, default=1
|
|
496
|
-
Degree of polynomial :math:`P(z)`. See notes below.
|
|
497
|
-
|
|
498
|
-
q : int, default=1
|
|
499
|
-
Degree of polynomial :math:`Q(z)`. See notes below.
|
|
500
|
-
|
|
501
547
|
plot : bool, default=False
|
|
502
548
|
If `True`, density is plotted.
|
|
503
549
|
|
|
504
|
-
plot_glue : bool, default=False
|
|
505
|
-
If `True`, the fit of glue function to Hilbert transform is
|
|
506
|
-
plotted.
|
|
507
|
-
|
|
508
550
|
latex : bool, default=False
|
|
509
551
|
If `True`, the plot is rendered using LaTeX. This option is
|
|
510
552
|
relevant only if ``plot=True``.
|
|
@@ -536,7 +578,7 @@ class FreeForm(object):
|
|
|
536
578
|
References
|
|
537
579
|
----------
|
|
538
580
|
|
|
539
|
-
.. [1]
|
|
581
|
+
.. [1] tbd
|
|
540
582
|
|
|
541
583
|
Examples
|
|
542
584
|
--------
|
|
@@ -591,35 +633,154 @@ class FreeForm(object):
|
|
|
591
633
|
m1[mask_m, :] = numpy.conjugate(
|
|
592
634
|
stieltjes(numpy.conjugate(z[mask_m, :])))
|
|
593
635
|
|
|
636
|
+
# Second Reimann sheet
|
|
594
637
|
m2[mask_p, :] = m1[mask_p, :]
|
|
595
|
-
m2[mask_m, :] = -m1[mask_m, :] + self._glue(
|
|
596
|
-
z[mask_m, :], p, q, plot_glue=plot_glue, latex=latex,
|
|
597
|
-
save=save)
|
|
638
|
+
m2[mask_m, :] = -m1[mask_m, :] + self._glue(z[mask_m, :])
|
|
598
639
|
|
|
599
640
|
if plot:
|
|
600
641
|
plot_stieltjes(x, y, m1, m2, self.support, latex=latex, save=save)
|
|
601
642
|
|
|
602
643
|
return m1, m2
|
|
603
644
|
|
|
645
|
+
# ==============
|
|
646
|
+
# eval stieltjes
|
|
647
|
+
# ==============
|
|
648
|
+
|
|
649
|
+
def _eval_stieltjes(self, z):
|
|
650
|
+
"""
|
|
651
|
+
Compute Stieltjes transform of the spectral density.
|
|
652
|
+
|
|
653
|
+
Parameters
|
|
654
|
+
----------
|
|
655
|
+
|
|
656
|
+
z : numpy.array
|
|
657
|
+
The z values in the complex plan where the Stieltjes transform is
|
|
658
|
+
evaluated.
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
Returns
|
|
662
|
+
-------
|
|
663
|
+
|
|
664
|
+
m_p : numpy.ndarray
|
|
665
|
+
The Stieltjes transform on the principal branch.
|
|
666
|
+
|
|
667
|
+
m_m : numpy.ndarray
|
|
668
|
+
The Stieltjes transform continued to the secondary branch.
|
|
669
|
+
|
|
670
|
+
See Also
|
|
671
|
+
--------
|
|
672
|
+
density
|
|
673
|
+
hilbert
|
|
674
|
+
|
|
675
|
+
Notes
|
|
676
|
+
-----
|
|
677
|
+
|
|
678
|
+
Notes.
|
|
679
|
+
|
|
680
|
+
References
|
|
681
|
+
----------
|
|
682
|
+
|
|
683
|
+
.. [1] tbd
|
|
684
|
+
|
|
685
|
+
Examples
|
|
686
|
+
--------
|
|
687
|
+
|
|
688
|
+
.. code-block:: python
|
|
689
|
+
|
|
690
|
+
>>> from freealg import FreeForm
|
|
691
|
+
"""
|
|
692
|
+
|
|
693
|
+
if self.psi is None:
|
|
694
|
+
raise RuntimeError('"fit" the model first.')
|
|
695
|
+
|
|
696
|
+
z = numpy.asarray(z)
|
|
697
|
+
shape = z.shape
|
|
698
|
+
if len(shape) == 0:
|
|
699
|
+
shape = (1,)
|
|
700
|
+
z = z.reshape(-1, 1)
|
|
701
|
+
|
|
702
|
+
# Stieltjes function
|
|
703
|
+
if self.method == 'jacobi':
|
|
704
|
+
stieltjes = partial(jacobi_stieltjes, psi=self.psi,
|
|
705
|
+
support=self.support, alpha=self.alpha,
|
|
706
|
+
beta=self.beta)
|
|
707
|
+
elif self.method == 'chebyshev':
|
|
708
|
+
stieltjes = partial(chebyshev_stieltjes, psi=self.psi,
|
|
709
|
+
support=self.support)
|
|
710
|
+
|
|
711
|
+
mask_p = z.imag >= 0.0
|
|
712
|
+
mask_m = z.imag < 0.0
|
|
713
|
+
|
|
714
|
+
m1 = numpy.zeros_like(z)
|
|
715
|
+
m2 = numpy.zeros_like(z)
|
|
716
|
+
|
|
717
|
+
# Upper half-plane
|
|
718
|
+
m1[mask_p] = stieltjes(z[mask_p].reshape(-1, 1)).reshape(-1)
|
|
719
|
+
|
|
720
|
+
# Lower half-plane, use Schwarz reflection
|
|
721
|
+
m1[mask_m] = numpy.conjugate(
|
|
722
|
+
stieltjes(numpy.conjugate(z[mask_m].reshape(-1, 1)))).reshape(-1)
|
|
723
|
+
|
|
724
|
+
# Second Reimann sheet
|
|
725
|
+
m2[mask_p] = m1[mask_p]
|
|
726
|
+
m2[mask_m] = -m1[mask_m] + self._glue(
|
|
727
|
+
z[mask_m].reshape(-1, 1)).reshape(-1)
|
|
728
|
+
|
|
729
|
+
m1, m2 = m1.reshape(*shape), m2.reshape(*shape)
|
|
730
|
+
|
|
731
|
+
return m1, m2
|
|
732
|
+
|
|
604
733
|
# ==========
|
|
605
734
|
# decompress
|
|
606
735
|
# ==========
|
|
607
736
|
|
|
608
|
-
def decompress(self,
|
|
737
|
+
def decompress(self, size, x=None, delta=1e-4, iterations=500,
|
|
738
|
+
step_size=0.1, tolerance=1e-4, plot=False, latex=False,
|
|
739
|
+
save=False):
|
|
609
740
|
"""
|
|
610
741
|
Free decompression of spectral density.
|
|
611
742
|
|
|
612
743
|
Parameters
|
|
613
744
|
----------
|
|
614
745
|
|
|
615
|
-
|
|
616
|
-
Size of the matrix.
|
|
746
|
+
size : int
|
|
747
|
+
Size of the decompressed matrix.
|
|
748
|
+
|
|
749
|
+
x : numpy.array, default=None
|
|
750
|
+
Positions where density to be evaluated at. If `None`, an interval
|
|
751
|
+
slightly larger than the support interval will be used.
|
|
752
|
+
|
|
753
|
+
delta: float, default=1e-4
|
|
754
|
+
Size of the perturbation into the upper half plane for Plemelj's
|
|
755
|
+
formula.
|
|
756
|
+
|
|
757
|
+
iterations: int, default=500
|
|
758
|
+
Maximum number of Newton iterations.
|
|
759
|
+
|
|
760
|
+
step_size: float, default=0.1
|
|
761
|
+
Step size for Newton iterations.
|
|
762
|
+
|
|
763
|
+
tolerance: float, default=1e-4
|
|
764
|
+
Tolerance for the solution obtained by the Newton solver. Also
|
|
765
|
+
used for the finite difference approximation to the derivative.
|
|
766
|
+
|
|
767
|
+
plot : bool, default=False
|
|
768
|
+
If `True`, density is plotted.
|
|
769
|
+
|
|
770
|
+
latex : bool, default=False
|
|
771
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
772
|
+
relevant only if ``plot=True``.
|
|
773
|
+
|
|
774
|
+
save : bool, default=False
|
|
775
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
776
|
+
assumed to the save filename (with the file extension). This option
|
|
777
|
+
is relevant only if ``plot=True``.
|
|
617
778
|
|
|
618
779
|
Returns
|
|
619
780
|
-------
|
|
620
781
|
|
|
621
782
|
rho : numpy.array
|
|
622
|
-
|
|
783
|
+
Spectral density
|
|
623
784
|
|
|
624
785
|
See Also
|
|
625
786
|
--------
|
|
@@ -630,7 +791,7 @@ class FreeForm(object):
|
|
|
630
791
|
Notes
|
|
631
792
|
-----
|
|
632
793
|
|
|
633
|
-
|
|
794
|
+
Work in progress.
|
|
634
795
|
|
|
635
796
|
References
|
|
636
797
|
----------
|
|
@@ -645,4 +806,12 @@ class FreeForm(object):
|
|
|
645
806
|
>>> from freealg import FreeForm
|
|
646
807
|
"""
|
|
647
808
|
|
|
648
|
-
|
|
809
|
+
rho, x, (lb, ub) = decompress(self, size, x=x, delta=delta,
|
|
810
|
+
iterations=iterations,
|
|
811
|
+
step_size=step_size, tolerance=tolerance)
|
|
812
|
+
|
|
813
|
+
if plot:
|
|
814
|
+
plot_density(x.reshape(-1), rho.reshape(-1), support=(lb, ub),
|
|
815
|
+
label='Decompression', latex=latex, save=save)
|
|
816
|
+
|
|
817
|
+
return rho
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
freealg/__init__.py,sha256=K92neXJZ9VE1U_j_pj28Qyq1MzlMXhOuYK2ZihgwCaU,463
|
|
2
|
+
freealg/__version__.py,sha256=4GZKi13lDTD25YBkGakhZyEQZWTER_OWQMNPoH_UM2c,22
|
|
3
|
+
freealg/_chebyshev.py,sha256=Cw48gXF6kd3IAQuLTEWadySeKGnY9TynzX9MNmycMUU,4697
|
|
4
|
+
freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
|
|
5
|
+
freealg/_decompress.py,sha256=H7ocq09gQnCY-q_8xHi6qyYj3qp239MCgj406hn0yeE,3344
|
|
6
|
+
freealg/_jacobi.py,sha256=HVnaujwAcaILVFGEgxk26UyIaLdP2FZMY44C8TG3Qcc,4763
|
|
7
|
+
freealg/_pade.py,sha256=rWILLpEL910YdixC2c5Cw77HqogAYoxi2YZgRVXEELM,4022
|
|
8
|
+
freealg/_plot_util.py,sha256=Q_LWLbqJefjympbcF2ylpeiYupbj5YAvTLwnpjEDeC4,19009
|
|
9
|
+
freealg/_util.py,sha256=wJ-t8LMZZFEr2PsZEVqTJP_jQTQ3rHUf0dO27F6L7YQ,2310
|
|
10
|
+
freealg/freeform.py,sha256=PsI0cnN38J-97jXDMSqERxgJZw-Tr13pPbIGmpgnRcc,23846
|
|
11
|
+
freealg/distributions/__init__.py,sha256=7t4HbP_EofiFDYLH6jbD94AIumOdcHn1y_Qo54mpLFM,614
|
|
12
|
+
freealg/distributions/marchenko_pastur.py,sha256=k8SoEgB2DFXLqGF-Hyqm8Sfh1DlF3khMssuxak5Uaqw,15827
|
|
13
|
+
freealg-0.0.3.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
|
|
14
|
+
freealg-0.0.3.dist-info/METADATA,sha256=Z1wF6CU_RomE2Q9ICEuYyNr6R3vQgrW0eZ3HIuEkRs8,2888
|
|
15
|
+
freealg-0.0.3.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
16
|
+
freealg-0.0.3.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
|
|
17
|
+
freealg-0.0.3.dist-info/RECORD,,
|
freealg-0.0.2.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
freealg/__init__.py,sha256=K92neXJZ9VE1U_j_pj28Qyq1MzlMXhOuYK2ZihgwCaU,463
|
|
2
|
-
freealg/__version__.py,sha256=QvlVh4JTl3JL7jQAja76yKtT-IvF4631ASjWY1wS6AQ,22
|
|
3
|
-
freealg/_chebyshev.py,sha256=Cw48gXF6kd3IAQuLTEWadySeKGnY9TynzX9MNmycMUU,4697
|
|
4
|
-
freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
|
|
5
|
-
freealg/_jacobi.py,sha256=F9K0IzbExpxGQY2OJQb-XT4Ee1zWTZvMXRUdU8cbwSE,4916
|
|
6
|
-
freealg/_pade.py,sha256=rWILLpEL910YdixC2c5Cw77HqogAYoxi2YZgRVXEELM,4022
|
|
7
|
-
freealg/_plot_util.py,sha256=OILqOqKdLGEabk41KnmxNftItzMMFHOL-LZ35yNvdyk,18653
|
|
8
|
-
freealg/_util.py,sha256=wJ-t8LMZZFEr2PsZEVqTJP_jQTQ3rHUf0dO27F6L7YQ,2310
|
|
9
|
-
freealg/freeform.py,sha256=LLkGoPLxvn8VMHuVQ1LGNkcAYZUi3GduFB8qlsr69qg,18595
|
|
10
|
-
freealg/distributions/__init__.py,sha256=7t4HbP_EofiFDYLH6jbD94AIumOdcHn1y_Qo54mpLFM,614
|
|
11
|
-
freealg/distributions/marchenko_pastur.py,sha256=k8SoEgB2DFXLqGF-Hyqm8Sfh1DlF3khMssuxak5Uaqw,15827
|
|
12
|
-
freealg-0.0.2.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
|
|
13
|
-
freealg-0.0.2.dist-info/METADATA,sha256=lMjLvkUpPUAr_QPthjnrIfg9KTs8DLEpTG68w8Ak0rU,2888
|
|
14
|
-
freealg-0.0.2.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
15
|
-
freealg-0.0.2.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
|
|
16
|
-
freealg-0.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|