freealg 0.7.1__py3-none-any.whl → 0.7.4__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 +1 -1
- freealg/__version__.py +1 -1
- freealg/_algebraic_form/_continuation_algebraic.py +48 -5
- freealg/_algebraic_form/_decompress2.py +86 -0
- freealg/_algebraic_form/_homotopy.py +138 -0
- freealg/_algebraic_form/algebraic_form.py +171 -114
- freealg/{_freeform → _free_form}/__init__.py +1 -1
- freealg/{_freeform → _free_form}/_linalg.py +1 -1
- freealg/{_freeform → _free_form}/_support.py +5 -5
- freealg/distributions/_chiral_block.py +69 -15
- freealg/distributions/_deformed_marchenko_pastur.py +71 -13
- freealg/distributions/_deformed_wigner.py +46 -16
- freealg/distributions/_kesten_mckay.py +1 -1
- freealg/distributions/_marchenko_pastur.py +1 -1
- freealg/distributions/_meixner.py +1 -1
- freealg/distributions/_wachter.py +1 -1
- freealg/distributions/_wigner.py +1 -1
- {freealg-0.7.1.dist-info → freealg-0.7.4.dist-info}/METADATA +1 -1
- freealg-0.7.4.dist-info/RECORD +49 -0
- {freealg-0.7.1.dist-info → freealg-0.7.4.dist-info}/WHEEL +1 -1
- freealg-0.7.1.dist-info/RECORD +0 -47
- /freealg/{_freeform → _free_form}/_chebyshev.py +0 -0
- /freealg/{_freeform → _free_form}/_damp.py +0 -0
- /freealg/{_freeform → _free_form}/_decompress.py +0 -0
- /freealg/{_freeform → _free_form}/_density_util.py +0 -0
- /freealg/{_freeform → _free_form}/_jacobi.py +0 -0
- /freealg/{_freeform → _free_form}/_pade.py +0 -0
- /freealg/{_freeform → _free_form}/_plot_util.py +0 -0
- /freealg/{_freeform → _free_form}/_sample.py +0 -0
- /freealg/{_freeform → _free_form}/_series.py +0 -0
- /freealg/{_freeform/freeform.py → _free_form/free_form.py} +0 -0
- {freealg-0.7.1.dist-info → freealg-0.7.4.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.7.1.dist-info → freealg-0.7.4.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.7.1.dist-info → freealg-0.7.4.dist-info}/top_level.txt +0 -0
freealg/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# under the terms of the license found in the LICENSE.txt file in the root
|
|
7
7
|
# directory of this source tree.
|
|
8
8
|
|
|
9
|
-
from .
|
|
9
|
+
from ._free_form import FreeForm, eigvalsh, cond, norm, trace, slogdet, supp, \
|
|
10
10
|
sample, kde
|
|
11
11
|
from ._algebraic_form import AlgebraicForm
|
|
12
12
|
from ._geometric_form import GeometricForm
|
freealg/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.7.
|
|
1
|
+
__version__ = "0.7.4"
|
|
@@ -14,10 +14,50 @@
|
|
|
14
14
|
import numpy
|
|
15
15
|
from .._geometric_form._continuation_genus0 import joukowski_z
|
|
16
16
|
|
|
17
|
-
__all__ = [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
__all__ = ['sample_z_joukowski', 'filter_z_away_from_cuts', 'powers',
|
|
18
|
+
'fit_polynomial_relation', 'eval_P', 'eval_roots',
|
|
19
|
+
'build_sheets_from_roots']
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ======================
|
|
23
|
+
# normalize coefficients
|
|
24
|
+
# ======================
|
|
25
|
+
|
|
26
|
+
def _normalize_coefficients(arr):
|
|
27
|
+
"""
|
|
28
|
+
Trim rows and columns on the sides (equivalent to factorizing or reducing
|
|
29
|
+
degree) and normalize so that the sum of the first column is one.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
a = numpy.asarray(arr).copy()
|
|
33
|
+
|
|
34
|
+
if a.size == 0:
|
|
35
|
+
return a
|
|
36
|
+
|
|
37
|
+
# --- Trim zero rows (top and bottom) ---
|
|
38
|
+
non_zero_rows = numpy.any(a != 0, axis=1)
|
|
39
|
+
if not numpy.any(non_zero_rows):
|
|
40
|
+
return a[:0, :0]
|
|
41
|
+
|
|
42
|
+
first_row = numpy.argmax(non_zero_rows)
|
|
43
|
+
last_row = len(non_zero_rows) - numpy.argmax(non_zero_rows[::-1])
|
|
44
|
+
a = a[first_row:last_row, :]
|
|
45
|
+
|
|
46
|
+
# --- Trim zero columns (left and right) ---
|
|
47
|
+
non_zero_cols = numpy.any(a != 0, axis=0)
|
|
48
|
+
if not numpy.any(non_zero_cols):
|
|
49
|
+
return a[:, :0]
|
|
50
|
+
|
|
51
|
+
first_col = numpy.argmax(non_zero_cols)
|
|
52
|
+
last_col = len(non_zero_cols) - numpy.argmax(non_zero_cols[::-1])
|
|
53
|
+
a = a[:, first_col:last_col]
|
|
54
|
+
|
|
55
|
+
# --- Normalize so first column sums to 1 ---
|
|
56
|
+
col_sum = numpy.sum(numpy.abs(a[:, 0]))
|
|
57
|
+
if col_sum != 0:
|
|
58
|
+
a = a / col_sum
|
|
59
|
+
|
|
60
|
+
return a
|
|
21
61
|
|
|
22
62
|
|
|
23
63
|
# ==================
|
|
@@ -91,7 +131,7 @@ def powers(x, deg):
|
|
|
91
131
|
# =======================
|
|
92
132
|
|
|
93
133
|
def fit_polynomial_relation(z, m, s, deg_z, ridge_lambda=0.0, weights=None,
|
|
94
|
-
triangular=None):
|
|
134
|
+
triangular=None, normalize=False):
|
|
95
135
|
|
|
96
136
|
z = numpy.asarray(z, dtype=complex).ravel()
|
|
97
137
|
m = numpy.asarray(m, dtype=complex).ravel()
|
|
@@ -167,6 +207,9 @@ def fit_polynomial_relation(z, m, s, deg_z, ridge_lambda=0.0, weights=None,
|
|
|
167
207
|
for k, (i, j) in enumerate(pairs):
|
|
168
208
|
full[i, j] = coef[k]
|
|
169
209
|
|
|
210
|
+
if normalize:
|
|
211
|
+
full = _normalize_coefficients(full)
|
|
212
|
+
|
|
170
213
|
return full
|
|
171
214
|
|
|
172
215
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# =======
|
|
2
|
+
# Imports
|
|
3
|
+
# =======
|
|
4
|
+
|
|
5
|
+
import numpy
|
|
6
|
+
from scipy.special import comb
|
|
7
|
+
from ._continuation_algebraic import _normalize_coefficients
|
|
8
|
+
|
|
9
|
+
__all__ = ['decompress_coeffs']
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# =================
|
|
13
|
+
# decompress_coeffs
|
|
14
|
+
# =================
|
|
15
|
+
|
|
16
|
+
def decompress_coeffs(a, t, normalize=True):
|
|
17
|
+
"""
|
|
18
|
+
Compute the decompressed coefficients A[r, s](t) induced by
|
|
19
|
+
the transform Q_t(z, m) = m^L P(z + (1 - e^{-t}) / m, e^t m).
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
a : array_like of float, shape (L+1, K+1)
|
|
24
|
+
Coefficients defining P(z, m) in the monomial basis:
|
|
25
|
+
P(z, m) = sum_{j=0..L} sum_{k=0..K} a[j, k] z^j m^k.
|
|
26
|
+
t : float
|
|
27
|
+
Time parameter.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
A : ndarray, shape (L+1, L+K+1)
|
|
32
|
+
Coefficients A[r, s](t) such that
|
|
33
|
+
sum_{r=0..L} sum_{s=0..L+K} A[r, s](t) z^r m^s = 0,
|
|
34
|
+
normalized by normalize_coefficients.
|
|
35
|
+
"""
|
|
36
|
+
a = numpy.asarray(a)
|
|
37
|
+
if a.ndim != 2:
|
|
38
|
+
raise ValueError("a must be a 2D array-like of shape (L+1, K+1).")
|
|
39
|
+
|
|
40
|
+
l_degree = a.shape[0] - 1
|
|
41
|
+
k_degree = a.shape[1] - 1
|
|
42
|
+
|
|
43
|
+
c = 1.0 - numpy.exp(-t)
|
|
44
|
+
|
|
45
|
+
# Scale columns of a by e^{t k}: scaled[j, k] = a[j, k] e^{t k}.
|
|
46
|
+
exp_factors = numpy.exp(numpy.arange(k_degree + 1) * t)
|
|
47
|
+
scaled = a * exp_factors
|
|
48
|
+
|
|
49
|
+
# Output coefficients.
|
|
50
|
+
out_dtype = numpy.result_type(a, float)
|
|
51
|
+
a_out = numpy.zeros((l_degree + 1, l_degree + k_degree + 1),
|
|
52
|
+
dtype=out_dtype)
|
|
53
|
+
|
|
54
|
+
# Precompute binomial(j, r) * c^{j-r} for all j, r (lower-triangular).
|
|
55
|
+
j_inds = numpy.arange(l_degree + 1)[:, None]
|
|
56
|
+
r_inds = numpy.arange(l_degree + 1)[None, :]
|
|
57
|
+
mask = r_inds <= j_inds
|
|
58
|
+
|
|
59
|
+
binom_weights = numpy.zeros((l_degree + 1, l_degree + 1), dtype=float)
|
|
60
|
+
binom_weights[mask] = comb(j_inds, r_inds, exact=False)[mask]
|
|
61
|
+
binom_weights[mask] *= (c ** (j_inds - r_inds))[mask]
|
|
62
|
+
|
|
63
|
+
# Main accumulation:
|
|
64
|
+
# For fixed j and r, add:
|
|
65
|
+
# A[r, (L - j + r) + k] += binom_weights[j, r] * scaled[j, k],
|
|
66
|
+
# for k = 0..K.
|
|
67
|
+
for j in range(l_degree + 1):
|
|
68
|
+
row_scaled = scaled[j]
|
|
69
|
+
if numpy.all(row_scaled == 0):
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
base0 = l_degree - j
|
|
73
|
+
row_b = binom_weights[j]
|
|
74
|
+
|
|
75
|
+
for r in range(j + 1):
|
|
76
|
+
coeff = row_b[r]
|
|
77
|
+
if coeff == 0:
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
start = base0 + r
|
|
81
|
+
a_out[r, start:start + (k_degree + 1)] += coeff * row_scaled
|
|
82
|
+
|
|
83
|
+
if normalize:
|
|
84
|
+
return _normalize_coefficients(a_out)
|
|
85
|
+
|
|
86
|
+
return a_out
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# =======
|
|
2
|
+
# Imports
|
|
3
|
+
# =======
|
|
4
|
+
|
|
5
|
+
import numpy
|
|
6
|
+
|
|
7
|
+
__all__ = ['stieltjes_poly']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# =====================
|
|
11
|
+
# stieltjes select root
|
|
12
|
+
# =====================
|
|
13
|
+
|
|
14
|
+
def stieltjes_select_root(roots, z, w_prev=None):
|
|
15
|
+
"""
|
|
16
|
+
Select the Stieltjes-branch root among candidates at a given z.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
roots : array_like of complex
|
|
21
|
+
Candidate roots for m at the given z.
|
|
22
|
+
z : complex
|
|
23
|
+
Evaluation point. The Stieltjes/Herglotz branch satisfies
|
|
24
|
+
sign(Im(m)) = sign(Im(z)) away from the real axis.
|
|
25
|
+
w_prev : complex or None, optional
|
|
26
|
+
Previous continuation value used to enforce continuity. If None,
|
|
27
|
+
the asymptotic target -1/z is used.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
w : complex
|
|
32
|
+
Selected root corresponding to the Stieltjes branch.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
z = complex(z)
|
|
36
|
+
roots = numpy.asarray(roots, dtype=numpy.complex128).ravel()
|
|
37
|
+
|
|
38
|
+
if roots.size == 0:
|
|
39
|
+
raise ValueError("roots must contain at least one candidate root.")
|
|
40
|
+
|
|
41
|
+
desired_sign = numpy.sign(z.imag)
|
|
42
|
+
|
|
43
|
+
if w_prev is None:
|
|
44
|
+
target = -1.0 / z
|
|
45
|
+
else:
|
|
46
|
+
target = complex(w_prev)
|
|
47
|
+
|
|
48
|
+
# Apply a soft Herglotz sign filter: prefer roots with Im(w) having the
|
|
49
|
+
# same sign as Im(z), allowing tiny numerical violations near the axis.
|
|
50
|
+
imag_roots = numpy.imag(roots)
|
|
51
|
+
|
|
52
|
+
good = roots[numpy.sign(imag_roots) == desired_sign]
|
|
53
|
+
if good.size == 0:
|
|
54
|
+
good = roots[(imag_roots * desired_sign) > -1e-12]
|
|
55
|
+
|
|
56
|
+
candidates = good if good.size > 0 else roots
|
|
57
|
+
idx = int(numpy.argmin(numpy.abs(candidates - target)))
|
|
58
|
+
return candidates[idx]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ==============
|
|
62
|
+
# stieltjes poly
|
|
63
|
+
# ==============
|
|
64
|
+
|
|
65
|
+
def stieltjes_poly(z, a, eps=None, height=1e4, steps=100):
|
|
66
|
+
"""
|
|
67
|
+
Evaluate the Stieltjes-branch solution m(z) of an algebraic equation.
|
|
68
|
+
|
|
69
|
+
The coefficients `a` define a polynomial relation
|
|
70
|
+
P(z, m) = 0,
|
|
71
|
+
where P is a polynomial in z and m with monomial-basis coefficients
|
|
72
|
+
arranged so that for fixed z, the coefficients of the polynomial in m
|
|
73
|
+
can be assembled from powers of z.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
z : complex
|
|
78
|
+
Evaluation point. Must be a single value.
|
|
79
|
+
a : ndarray, shape (L, K)
|
|
80
|
+
Coefficient matrix defining P(z, m) in the monomial basis.
|
|
81
|
+
eps : float or None, optional
|
|
82
|
+
If Im(z) == 0, use z + i*eps as the boundary evaluation point.
|
|
83
|
+
If None and Im(z) == 0, eps is set to 1e-8 * max(1, |z|).
|
|
84
|
+
height : float, optional
|
|
85
|
+
Imaginary height used for the starting point z0 in the same
|
|
86
|
+
half-plane as the evaluation point.
|
|
87
|
+
steps : int, optional
|
|
88
|
+
Number of continuation steps along the homotopy path.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
w : complex
|
|
93
|
+
Value of the Stieltjes-branch solution m(z) (or m(z+i*eps) if z is
|
|
94
|
+
real).
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
z = complex(z)
|
|
98
|
+
a = numpy.asarray(a)
|
|
99
|
+
|
|
100
|
+
if a.ndim != 2:
|
|
101
|
+
raise ValueError('a must be a 2D array.')
|
|
102
|
+
|
|
103
|
+
if steps < 1:
|
|
104
|
+
raise ValueError("steps must be a positive integer.")
|
|
105
|
+
|
|
106
|
+
a_l, _ = a.shape
|
|
107
|
+
|
|
108
|
+
def poly_coeffs_m(z_val):
|
|
109
|
+
z_powers = z_val ** numpy.arange(a_l)
|
|
110
|
+
return (z_powers @ a)[::-1]
|
|
111
|
+
|
|
112
|
+
def poly_roots(z_val):
|
|
113
|
+
coeffs = numpy.asarray(poly_coeffs_m(z_val), dtype=numpy.complex128)
|
|
114
|
+
return numpy.roots(coeffs)
|
|
115
|
+
|
|
116
|
+
# If user asked for a real-axis value, interpret as boundary value from C+.
|
|
117
|
+
if z.imag == 0.0:
|
|
118
|
+
if eps is None:
|
|
119
|
+
eps = 1e-8 * max(1.0, abs(z))
|
|
120
|
+
z_eval = z + 1j * float(eps)
|
|
121
|
+
else:
|
|
122
|
+
z_eval = z
|
|
123
|
+
|
|
124
|
+
half_sign = numpy.sign(z_eval.imag)
|
|
125
|
+
if half_sign == 0.0:
|
|
126
|
+
half_sign = 1.0
|
|
127
|
+
|
|
128
|
+
z0 = 1j * float(half_sign) * float(height)
|
|
129
|
+
|
|
130
|
+
# Initialize at z0 via asymptotic / Im-sign selection.
|
|
131
|
+
w_prev = stieltjes_select_root(poly_roots(z0), z0, w_prev=None)
|
|
132
|
+
|
|
133
|
+
# Straight-line homotopy from z0 to z_eval.
|
|
134
|
+
for tau in numpy.linspace(0.0, 1.0, int(steps) + 1)[1:]:
|
|
135
|
+
z_tau = z0 + tau * (z_eval - z0)
|
|
136
|
+
w_prev = stieltjes_select_root(poly_roots(z_tau), z_tau, w_prev=w_prev)
|
|
137
|
+
|
|
138
|
+
return w_prev
|
|
@@ -11,13 +11,18 @@
|
|
|
11
11
|
# Imports
|
|
12
12
|
# =======
|
|
13
13
|
|
|
14
|
+
import inspect
|
|
14
15
|
import numpy
|
|
15
|
-
from .._util import resolve_complex_dtype
|
|
16
|
+
from .._util import resolve_complex_dtype, compute_eig
|
|
16
17
|
# from .._util import compute_eig
|
|
17
18
|
from ._continuation_algebraic import sample_z_joukowski, \
|
|
18
19
|
filter_z_away_from_cuts, fit_polynomial_relation, eval_P
|
|
19
20
|
from ._edge import evolve_edges, merge_edges
|
|
20
21
|
from ._decompress import decompress_newton
|
|
22
|
+
from ._decompress2 import decompress_coeffs
|
|
23
|
+
from ._homotopy import stieltjes_poly
|
|
24
|
+
from .._free_form._support import supp
|
|
25
|
+
from .._free_form._plot_util import plot_density
|
|
21
26
|
|
|
22
27
|
# Fallback to previous numpy API
|
|
23
28
|
if not hasattr(numpy, 'trapezoid'):
|
|
@@ -129,46 +134,66 @@ class AlgebraicForm(object):
|
|
|
129
134
|
# def __init__(self, A, support=None, delta=1e-6, dtype='complex128',
|
|
130
135
|
# **kwargs):
|
|
131
136
|
|
|
132
|
-
def __init__(self,
|
|
137
|
+
def __init__(self, A, support=None, delta=1e-5, dtype='complex128',
|
|
133
138
|
**kwargs):
|
|
134
139
|
"""
|
|
135
140
|
Initialization.
|
|
136
141
|
"""
|
|
137
142
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
self.stieltjes =
|
|
143
|
+
self.A = None
|
|
144
|
+
self.eig = None
|
|
145
|
+
self.stieltjes = None
|
|
141
146
|
self.support = support
|
|
142
147
|
self.delta = delta # Offset above real axis to apply Plemelj formula
|
|
143
148
|
|
|
144
149
|
# Data type for complex arrays
|
|
145
150
|
self.dtype = resolve_complex_dtype(dtype)
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
if inspect.isclass(A) and hasattr(A, "stieltjes") and \
|
|
153
|
+
callable(getattr(A, "stieltjes", None)):
|
|
154
|
+
# This is one of the distribution objects, like MarchenkoPastur
|
|
155
|
+
self.stieltjes = A.stieltjes
|
|
156
|
+
self.n = 1
|
|
157
|
+
|
|
158
|
+
elif callable(A):
|
|
159
|
+
# This is a custom function
|
|
160
|
+
self.stieltjes = A
|
|
161
|
+
self.n = 1
|
|
162
|
+
|
|
163
|
+
else:
|
|
164
|
+
# Eigenvalues
|
|
165
|
+
if A.ndim == 1:
|
|
166
|
+
# If A is a 1D array, it is assumed A is the eigenvalues array.
|
|
167
|
+
self.eig = A
|
|
168
|
+
self.n = len(A)
|
|
169
|
+
elif A.ndim == 2:
|
|
170
|
+
# When A is a 2D array, it is assumed A is the actual array,
|
|
171
|
+
# and its eigenvalues will be computed.
|
|
172
|
+
self.A = A
|
|
173
|
+
self.n = A.shape[0]
|
|
174
|
+
assert A.shape[0] == A.shape[1], \
|
|
175
|
+
'Only square matrices are permitted.'
|
|
176
|
+
self.eig = compute_eig(A)
|
|
177
|
+
|
|
178
|
+
# Use empirical Stieltjes function
|
|
179
|
+
self.stieltjes = lambda z: \
|
|
180
|
+
numpy.mean(1.0/(self.eig-z[:, numpy.newaxis]), axis=-1)
|
|
160
181
|
|
|
161
182
|
# Support
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
if support is None:
|
|
184
|
+
if self.eig is None:
|
|
185
|
+
raise RuntimeError("Support must be provided without data")
|
|
186
|
+
# Detect support
|
|
187
|
+
self.lam_m, self.lam_p = supp(self.eig, **kwargs)
|
|
188
|
+
self.support = [(self.lam_m, self.lam_p)]
|
|
189
|
+
self.broad_support = self.support[0]
|
|
190
|
+
else:
|
|
191
|
+
self.support = support
|
|
192
|
+
self.lam_m = min([s[0] for s in self.support])
|
|
193
|
+
self.lam_p = max([s[1] for s in self.support])
|
|
194
|
+
self.broad_support = (self.lam_m, self.lam_p)
|
|
169
195
|
|
|
170
196
|
# Initialize
|
|
171
|
-
# self.method = None # fitting rho: jacobi, chebyshev
|
|
172
197
|
self.a_coeffs = None # Polynomial coefficients
|
|
173
198
|
self.cache = {} # Cache inner-computations
|
|
174
199
|
|
|
@@ -183,6 +208,7 @@ class AlgebraicForm(object):
|
|
|
183
208
|
y_eps=2e-2,
|
|
184
209
|
x_pad=0.0,
|
|
185
210
|
triangular=None,
|
|
211
|
+
normalize=False,
|
|
186
212
|
verbose=False):
|
|
187
213
|
"""
|
|
188
214
|
Fits polynomial.
|
|
@@ -192,8 +218,6 @@ class AlgebraicForm(object):
|
|
|
192
218
|
# also empties all references holdign a cache copy.
|
|
193
219
|
# self.cache.clear()
|
|
194
220
|
|
|
195
|
-
# return self.a_coeffs
|
|
196
|
-
|
|
197
221
|
z_fits = []
|
|
198
222
|
for sup in self.support:
|
|
199
223
|
a, b = sup
|
|
@@ -204,16 +228,15 @@ class AlgebraicForm(object):
|
|
|
204
228
|
|
|
205
229
|
z_fit = numpy.concatenate(z_fits)
|
|
206
230
|
|
|
207
|
-
# Remove points too close to
|
|
231
|
+
# Remove points too close to any cut
|
|
208
232
|
z_fit = filter_z_away_from_cuts(z_fit, self.support, y_eps=y_eps,
|
|
209
233
|
x_pad=x_pad)
|
|
210
234
|
|
|
211
|
-
# ---------
|
|
212
|
-
|
|
213
235
|
m1_fit = self.stieltjes(z_fit)
|
|
214
236
|
a_coeffs = fit_polynomial_relation(z_fit, m1_fit, s=deg_m, deg_z=deg_z,
|
|
215
237
|
ridge_lambda=reg,
|
|
216
|
-
triangular=triangular
|
|
238
|
+
triangular=triangular,
|
|
239
|
+
normalize=normalize)
|
|
217
240
|
|
|
218
241
|
self.a_coeffs = a_coeffs
|
|
219
242
|
|
|
@@ -223,7 +246,7 @@ class AlgebraicForm(object):
|
|
|
223
246
|
print("fit residual 99.9%:",
|
|
224
247
|
numpy.quantile(P_res[numpy.isfinite(P_res)], 0.999))
|
|
225
248
|
|
|
226
|
-
print('\
|
|
249
|
+
print('\nCoefficients')
|
|
227
250
|
with numpy.printoptions(precision=4, suppress=True):
|
|
228
251
|
for i in range(a_coeffs.shape[0]):
|
|
229
252
|
for j in range(a_coeffs.shape[1]):
|
|
@@ -231,7 +254,7 @@ class AlgebraicForm(object):
|
|
|
231
254
|
print(f"{v.real:>+0.4f}{v.imag:>+0.4f}j", end=" ")
|
|
232
255
|
print('')
|
|
233
256
|
|
|
234
|
-
print('\
|
|
257
|
+
print('\nCoefficient Magnitudes')
|
|
235
258
|
with numpy.printoptions(precision=6, suppress=True):
|
|
236
259
|
print(numpy.abs(a_coeffs))
|
|
237
260
|
|
|
@@ -241,22 +264,22 @@ class AlgebraicForm(object):
|
|
|
241
264
|
# generate grid
|
|
242
265
|
# =============
|
|
243
266
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
267
|
+
def _generate_grid(self, scale, extend=1.0, N=500):
|
|
268
|
+
"""
|
|
269
|
+
Generate a grid of points to evaluate density / Hilbert / Stieltjes
|
|
270
|
+
transforms.
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
274
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
275
|
+
|
|
276
|
+
x_min = numpy.floor(extend * (center - extend * radius * scale))
|
|
277
|
+
x_max = numpy.ceil(extend * (center + extend * radius * scale))
|
|
278
|
+
|
|
279
|
+
x_min /= extend
|
|
280
|
+
x_max /= extend
|
|
281
|
+
|
|
282
|
+
return numpy.linspace(x_min, x_max, N)
|
|
260
283
|
|
|
261
284
|
# =======
|
|
262
285
|
# density
|
|
@@ -310,16 +333,19 @@ class AlgebraicForm(object):
|
|
|
310
333
|
raise RuntimeError('The model needs to be fit using the .fit() ' +
|
|
311
334
|
'function.')
|
|
312
335
|
|
|
313
|
-
#
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
#
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
336
|
+
# Create x if not given
|
|
337
|
+
if x is None:
|
|
338
|
+
x = self._generate_grid(1.25)
|
|
339
|
+
|
|
340
|
+
# Preallocate density to zero
|
|
341
|
+
rho = numpy.zeros_like(x)
|
|
342
|
+
|
|
343
|
+
for idx, x_i in enumerate(x):
|
|
344
|
+
m_i = stieltjes_poly(x_i, self.a_coeffs)
|
|
345
|
+
rho[idx] = m_i.imag
|
|
346
|
+
|
|
347
|
+
rho = rho / numpy.pi
|
|
348
|
+
|
|
323
349
|
# if self.method == 'jacobi':
|
|
324
350
|
# rho[mask] = jacobi_density(x[mask], self.psi, self.support,
|
|
325
351
|
# self.alpha, self.beta)
|
|
@@ -339,12 +365,12 @@ class AlgebraicForm(object):
|
|
|
339
365
|
# if min_rho < 0.0 - 1e-3:
|
|
340
366
|
# print(f'"rho" is not positive. min_rho: {min_rho:>0.3f}. Set ' +
|
|
341
367
|
# r'"force=True".')
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
368
|
+
|
|
369
|
+
if plot:
|
|
370
|
+
plot_density(x, rho, eig=self.eig, support=self.broad_support,
|
|
371
|
+
label='Estimate', latex=latex, save=save)
|
|
372
|
+
|
|
373
|
+
return rho
|
|
348
374
|
|
|
349
375
|
# =======
|
|
350
376
|
# hilbert
|
|
@@ -566,68 +592,99 @@ class AlgebraicForm(object):
|
|
|
566
592
|
# decompress
|
|
567
593
|
# ==========
|
|
568
594
|
|
|
569
|
-
def decompress(self, x,
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
min_lam=1e-6,
|
|
574
|
-
w_min=1e-14,
|
|
575
|
-
sweep=True,
|
|
576
|
-
verbose=False):
|
|
595
|
+
def decompress(self, size, x=None, method='one', plot=False, latex=False,
|
|
596
|
+
save=False, verbose=False, newton_opt={
|
|
597
|
+
'max_iter': 50, 'tol': 1e-12, 'armijo': 1e-4,
|
|
598
|
+
'min_lam': 1e-6, 'w_min': 1e-14, 'sweep': True}):
|
|
577
599
|
"""
|
|
578
600
|
Free decompression of spectral density.
|
|
579
601
|
"""
|
|
580
602
|
|
|
581
603
|
# Check size argument
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
604
|
+
if numpy.isscalar(size):
|
|
605
|
+
size = int(size)
|
|
606
|
+
else:
|
|
607
|
+
# Check monotonic increment (either all increasing or decreasing)
|
|
608
|
+
diff = numpy.diff(size)
|
|
609
|
+
if not (numpy.all(diff >= 0) or numpy.all(diff <= 0)):
|
|
610
|
+
raise ValueError('"size" increment should be monotonic.')
|
|
589
611
|
|
|
590
612
|
# Decompression ratio equal to e^{t}.
|
|
591
|
-
|
|
613
|
+
alpha = numpy.atleast_1d(size) / self.n
|
|
592
614
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
# rho = numpy.squeeze(rho)
|
|
596
|
-
#
|
|
597
|
-
# # Plot only the last size
|
|
598
|
-
# if plot:
|
|
599
|
-
# if numpy.isscalar(size):
|
|
600
|
-
# rho_last = rho
|
|
601
|
-
# else:
|
|
602
|
-
# rho_last = rho[-1, :]
|
|
603
|
-
# plot_density(x, rho_last, support=(lb, ub),
|
|
604
|
-
# label='Decompression', latex=latex, save=save)
|
|
605
|
-
#
|
|
606
|
-
# return rho, x
|
|
615
|
+
def m_fn(z):
|
|
616
|
+
return stieltjes_poly(z, self.a_coeffs)
|
|
607
617
|
|
|
608
|
-
#
|
|
609
|
-
|
|
618
|
+
# Lower and upper bound on new support
|
|
619
|
+
hilb_lb = (1.0 / m_fn(self.lam_m + self.delta * 1j).item()).real
|
|
620
|
+
hilb_ub = (1.0 / m_fn(self.lam_p + self.delta * 1j).item()).real
|
|
621
|
+
lb = self.lam_m - (numpy.max(alpha) - 1) * hilb_lb
|
|
622
|
+
ub = self.lam_p - (numpy.max(alpha) - 1) * hilb_ub
|
|
610
623
|
|
|
611
|
-
#
|
|
612
|
-
|
|
624
|
+
# Create x if not given
|
|
625
|
+
if x is None:
|
|
626
|
+
radius = 0.5 * (ub - lb)
|
|
627
|
+
center = 0.5 * (ub + lb)
|
|
628
|
+
scale = 1.25
|
|
629
|
+
x_min = numpy.floor(center - radius * scale)
|
|
630
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
631
|
+
x = numpy.linspace(x_min, x_max, 200)
|
|
632
|
+
else:
|
|
633
|
+
x = numpy.asarray(x)
|
|
613
634
|
|
|
614
|
-
|
|
615
|
-
W, ok = decompress_newton(
|
|
616
|
-
z_query, t, self.a_coeffs,
|
|
617
|
-
w0_list=w0_list,
|
|
618
|
-
max_iter=max_iter,
|
|
619
|
-
tol=tol,
|
|
620
|
-
armijo=armijo,
|
|
621
|
-
min_lam=min_lam,
|
|
622
|
-
w_min=w_min,
|
|
623
|
-
sweep=sweep)
|
|
635
|
+
if method == 'one':
|
|
624
636
|
|
|
625
|
-
|
|
637
|
+
# Query grid on the real axis + a small imaginary buffer
|
|
638
|
+
z_query = x + 1j * self.delta
|
|
626
639
|
|
|
627
|
-
|
|
628
|
-
|
|
640
|
+
# Initial condition at t=0 (physical branch)
|
|
641
|
+
w0_list = self.stieltjes(z_query)
|
|
629
642
|
|
|
630
|
-
|
|
643
|
+
# Times
|
|
644
|
+
t = numpy.log(alpha)
|
|
645
|
+
|
|
646
|
+
# Evolve
|
|
647
|
+
W, ok = decompress_newton(
|
|
648
|
+
z_query, t, self.a_coeffs,
|
|
649
|
+
w0_list=w0_list, **newton_opt)
|
|
650
|
+
|
|
651
|
+
rho = W.imag / numpy.pi
|
|
652
|
+
|
|
653
|
+
if verbose:
|
|
654
|
+
print("success rate per t:", ok.mean(axis=1))
|
|
655
|
+
|
|
656
|
+
elif method == 'two':
|
|
657
|
+
|
|
658
|
+
# Preallocate density to zero
|
|
659
|
+
rho = numpy.zeros((alpha.size, x.size), dtype=float)
|
|
660
|
+
|
|
661
|
+
# Decompress to each alpha
|
|
662
|
+
for i in range(alpha.size):
|
|
663
|
+
coeffs_i = decompress_coeffs(self.a_coeffs,
|
|
664
|
+
numpy.log(alpha[i]))
|
|
665
|
+
for j, x_j in enumerate(x):
|
|
666
|
+
m_j = stieltjes_poly(x_j, coeffs_i)
|
|
667
|
+
rho[i, j] = m_j.imag
|
|
668
|
+
|
|
669
|
+
rho = rho / numpy.pi
|
|
670
|
+
|
|
671
|
+
else:
|
|
672
|
+
raise ValueError('"method" is invalid.')
|
|
673
|
+
|
|
674
|
+
# If the input size was only a scalar, return a 1D rho, otherwise 2D.
|
|
675
|
+
if numpy.isscalar(size):
|
|
676
|
+
rho = numpy.squeeze(rho)
|
|
677
|
+
|
|
678
|
+
# Plot only the last size
|
|
679
|
+
if plot:
|
|
680
|
+
if numpy.isscalar(size):
|
|
681
|
+
rho_last = rho
|
|
682
|
+
else:
|
|
683
|
+
rho_last = rho[-1, :]
|
|
684
|
+
plot_density(x, rho_last, support=(lb, ub),
|
|
685
|
+
label='Decompression', latex=latex, save=save)
|
|
686
|
+
|
|
687
|
+
return rho, x
|
|
631
688
|
|
|
632
689
|
# ====
|
|
633
690
|
# edge
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# under the terms of the license found in the LICENSE.txt file in the root
|
|
7
7
|
# directory of this source tree.
|
|
8
8
|
|
|
9
|
-
from .
|
|
9
|
+
from .free_form import FreeForm
|
|
10
10
|
from ._linalg import eigvalsh, cond, norm, trace, slogdet
|
|
11
11
|
from ._support import supp
|
|
12
12
|
from ._sample import sample
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# =======
|
|
12
12
|
|
|
13
13
|
import numpy
|
|
14
|
-
import numba
|
|
14
|
+
# import numba
|
|
15
15
|
from scipy.stats import gaussian_kde
|
|
16
16
|
|
|
17
17
|
__all__ = ['support_from_density', 'supp']
|
|
@@ -21,10 +21,10 @@ __all__ = ['support_from_density', 'supp']
|
|
|
21
21
|
# support from density
|
|
22
22
|
# ====================
|
|
23
23
|
|
|
24
|
-
@numba.njit(numba.types.UniTuple(numba.types.int64, 2)(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
))
|
|
24
|
+
# @numba.njit(numba.types.UniTuple(numba.types.int64, 2)(
|
|
25
|
+
# numba.types.float64,
|
|
26
|
+
# numba.types.float64[::1]
|
|
27
|
+
# ))
|
|
28
28
|
def support_from_density(dx, density):
|
|
29
29
|
"""
|
|
30
30
|
Estimates the support from a collection of noisy observations of a
|
|
@@ -404,7 +404,7 @@ class ChiralBlock(object):
|
|
|
404
404
|
----------
|
|
405
405
|
|
|
406
406
|
size : int
|
|
407
|
-
|
|
407
|
+
Total size :math:`N = n + m` of the returned matrix.
|
|
408
408
|
|
|
409
409
|
seed : int, default=None
|
|
410
410
|
Seed for random number generator.
|
|
@@ -413,7 +413,24 @@ class ChiralBlock(object):
|
|
|
413
413
|
-------
|
|
414
414
|
|
|
415
415
|
A : numpy.ndarray
|
|
416
|
-
|
|
416
|
+
Symmetric matrix of shape :math:`N \\times N`.
|
|
417
|
+
|
|
418
|
+
Notes
|
|
419
|
+
-----
|
|
420
|
+
|
|
421
|
+
Generate a :math:`(n+m) x (n+m)` matrix
|
|
422
|
+
|
|
423
|
+
.. math::
|
|
424
|
+
|
|
425
|
+
H =
|
|
426
|
+
\\begin{bmatrix}
|
|
427
|
+
\\alpha \\mathbf{I}_n & (1/\\sqrt{m})) \\mathbf{X} \\
|
|
428
|
+
(1/\\sqrt{m})) \\mathbf{X}^{\\intercal} & \\beta \\mathbf{I}_m
|
|
429
|
+
\\end{bmatrix}
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
where :math:`\\mathbf{X}` has i.i.d. :math:`N(0,1)` entries and
|
|
433
|
+
:math:`n/m` approximates :math:`c`.
|
|
417
434
|
|
|
418
435
|
Examples
|
|
419
436
|
--------
|
|
@@ -425,16 +442,53 @@ class ChiralBlock(object):
|
|
|
425
442
|
>>> A = mp.matrix(2000)
|
|
426
443
|
"""
|
|
427
444
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
#
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
445
|
+
N = int(size)
|
|
446
|
+
if N <= 1:
|
|
447
|
+
raise ValueError("size must be an integer >= 2.")
|
|
448
|
+
|
|
449
|
+
# Unpack parameters
|
|
450
|
+
alpha = float(self.alpha)
|
|
451
|
+
beta = float(self.beta)
|
|
452
|
+
c = float(self.c)
|
|
453
|
+
|
|
454
|
+
rng = numpy.random.default_rng(seed)
|
|
455
|
+
|
|
456
|
+
# Choose n,m so that n/m approx c and n+m = N.
|
|
457
|
+
# Solve n = c m and n + m = N -> m = N/(c+1), n = cN/(c+1).
|
|
458
|
+
m = int(round(N / (c + 1.0)))
|
|
459
|
+
m = max(1, min(N - 1, m))
|
|
460
|
+
n = N - m
|
|
461
|
+
|
|
462
|
+
# Optionally refine to get ratio closer to c (cheap local search).
|
|
463
|
+
# This keeps deterministic behavior.
|
|
464
|
+
best_n = n
|
|
465
|
+
best_m = m
|
|
466
|
+
best_err = abs((n / float(m)) - c)
|
|
467
|
+
for dm in (-2, -1, 0, 1, 2):
|
|
468
|
+
mm = m + dm
|
|
469
|
+
if mm <= 0 or mm >= N:
|
|
470
|
+
continue
|
|
471
|
+
nn = N - mm
|
|
472
|
+
err = abs((nn / float(mm)) - c)
|
|
473
|
+
if err < best_err:
|
|
474
|
+
best_err = err
|
|
475
|
+
best_n = nn
|
|
476
|
+
best_m = mm
|
|
477
|
+
n = best_n
|
|
478
|
+
m = best_m
|
|
479
|
+
|
|
480
|
+
# Draw X (n x m) with i.i.d. entries
|
|
481
|
+
X = rng.standard_normal((n, m))
|
|
482
|
+
|
|
483
|
+
# Assemble H
|
|
484
|
+
H = numpy.zeros((N, N), dtype=numpy.float64)
|
|
485
|
+
|
|
486
|
+
H[:n, :n] = alpha * numpy.eye(n, dtype=numpy.float64)
|
|
487
|
+
H[n:, n:] = beta * numpy.eye(m, dtype=numpy.float64)
|
|
488
|
+
|
|
489
|
+
s = 1.0 / numpy.sqrt(float(m))
|
|
490
|
+
B = s * X
|
|
491
|
+
H[:n, n:] = B
|
|
492
|
+
H[n:, :n] = B.T
|
|
493
|
+
|
|
494
|
+
return H
|
|
@@ -70,6 +70,15 @@ class DeformedMarchenkoPastur(object):
|
|
|
70
70
|
Initialization.
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
|
+
if not (0.0 <= w1 <= 1.0):
|
|
74
|
+
raise ValueError("w1 must be in [0, 1].")
|
|
75
|
+
|
|
76
|
+
if c < 0.0:
|
|
77
|
+
raise ValueError("c must be >= 0.")
|
|
78
|
+
|
|
79
|
+
if t1 < 0.0 or t2 < 0.0:
|
|
80
|
+
raise ValueError("t1 and t2 must be >= 0 for a covariance model.")
|
|
81
|
+
|
|
73
82
|
self.t1 = t1
|
|
74
83
|
self.t2 = t2
|
|
75
84
|
self.w1 = w1
|
|
@@ -592,6 +601,24 @@ class DeformedMarchenkoPastur(object):
|
|
|
592
601
|
A : numpy.ndarray
|
|
593
602
|
A matrix of the size :math:`n \\times n`.
|
|
594
603
|
|
|
604
|
+
Notes
|
|
605
|
+
-----
|
|
606
|
+
|
|
607
|
+
Generate an :math:`n x n` sample covariance matrix :math:`\\mathbf{S}`
|
|
608
|
+
whose ESD converges to :math:`H \\boxtimes MP_c`, where
|
|
609
|
+
:math:`H = w_1 \\delta_{t_1} + (1-w_1) \\delta_{t_2}`.
|
|
610
|
+
|
|
611
|
+
Finite :math:`n` construction:
|
|
612
|
+
|
|
613
|
+
* :math:`m` is chosen so that :math:`n/m` approx :math:`c` (when
|
|
614
|
+
:math:`c>0`),
|
|
615
|
+
* :math:`Z` has i.i.d. :math:`N(0,1)`,
|
|
616
|
+
* :math:`\\boldsymbol{\\Sigma}` has eigenvalues :math:`t_1`,
|
|
617
|
+
:math:`t_2` with proportions
|
|
618
|
+
:math:`w_1`, and :math:`1-w_1`,
|
|
619
|
+
* :math:`\\mathbf{S} = (1/m) \\boldsymbol{\\Sigma}^{1/2} \\mathbf{Z}
|
|
620
|
+
\\mathbf{Z}^T \\boldsymbol{\\Sigma}^{1/2}`.
|
|
621
|
+
|
|
595
622
|
Examples
|
|
596
623
|
--------
|
|
597
624
|
|
|
@@ -602,16 +629,47 @@ class DeformedMarchenkoPastur(object):
|
|
|
602
629
|
>>> A = mp.matrix(2000)
|
|
603
630
|
"""
|
|
604
631
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
#
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
632
|
+
n = int(size)
|
|
633
|
+
if n <= 0:
|
|
634
|
+
raise ValueError("size must be a positive integer.")
|
|
635
|
+
|
|
636
|
+
# Unpack parameters
|
|
637
|
+
t1 = float(self.t1)
|
|
638
|
+
t2 = float(self.t2)
|
|
639
|
+
w1 = float(self.w1)
|
|
640
|
+
c = float(self.c)
|
|
641
|
+
|
|
642
|
+
rng = numpy.random.default_rng(seed)
|
|
643
|
+
|
|
644
|
+
# Choose m so that n/m approx c (for c>0). For c=0, return population
|
|
645
|
+
# Sigma.
|
|
646
|
+
if c == 0.0:
|
|
647
|
+
n1 = int(round(w1 * n))
|
|
648
|
+
n1 = max(0, min(n, n1))
|
|
649
|
+
d = numpy.empty(n, dtype=numpy.float64)
|
|
650
|
+
d[:n1] = t1
|
|
651
|
+
d[n1:] = t2
|
|
652
|
+
rng.shuffle(d)
|
|
653
|
+
return numpy.diag(d)
|
|
654
|
+
|
|
655
|
+
# m must be positive integer
|
|
656
|
+
m = int(round(n / c)) if c > 0.0 else n
|
|
657
|
+
m = max(1, m)
|
|
658
|
+
|
|
659
|
+
# Build diagonal Sigma^{1/2} with two atoms
|
|
660
|
+
n1 = int(round(w1 * n))
|
|
661
|
+
n1 = max(0, min(n, n1))
|
|
662
|
+
|
|
663
|
+
s = numpy.empty(n, dtype=numpy.float64)
|
|
664
|
+
s[:n1] = numpy.sqrt(t1)
|
|
665
|
+
s[n1:] = numpy.sqrt(t2)
|
|
666
|
+
rng.shuffle(s)
|
|
667
|
+
|
|
668
|
+
# Draw Z and form X = Sigma^{1/2} Z / sqrt(m)
|
|
669
|
+
Z = rng.standard_normal((n, m))
|
|
670
|
+
X = (s[:, None] * Z) / numpy.sqrt(m)
|
|
671
|
+
|
|
672
|
+
# Sample covariance
|
|
673
|
+
S = X @ X.T
|
|
674
|
+
|
|
675
|
+
return S
|
|
@@ -35,6 +35,9 @@ class DeformedWigner(object):
|
|
|
35
35
|
Initialization.
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
|
+
if not (0.0 <= w1 <= 1.0):
|
|
39
|
+
raise ValueError("w1 must be in [0, 1].")
|
|
40
|
+
|
|
38
41
|
self.t1 = t1
|
|
39
42
|
self.t2 = t2
|
|
40
43
|
self.w1 = w1
|
|
@@ -287,26 +290,53 @@ class DeformedWigner(object):
|
|
|
287
290
|
A : numpy.ndarray
|
|
288
291
|
A matrix of the size :math:`n \\times n`.
|
|
289
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
|
+
|
|
290
302
|
Examples
|
|
291
303
|
--------
|
|
292
304
|
|
|
293
305
|
.. code-block::python
|
|
294
306
|
|
|
295
|
-
>>> from freealg.distributions import
|
|
296
|
-
>>>
|
|
297
|
-
>>> A =
|
|
307
|
+
>>> from freealg.distributions import DeformedWigner
|
|
308
|
+
>>> dwg = DeformedWigner(1/50)
|
|
309
|
+
>>> A = dwg.matrix(2000)
|
|
298
310
|
"""
|
|
299
311
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
#
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
#
|
|
311
|
-
|
|
312
|
-
|
|
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
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from ..
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
17
|
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
18
|
|
|
19
19
|
try:
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from ..
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
17
|
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
18
|
from ..visualization import glue_branches
|
|
19
19
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from ..
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
17
|
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
18
|
|
|
19
19
|
try:
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from ..
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
17
|
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
18
|
|
|
19
19
|
try:
|
freealg/distributions/_wigner.py
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from scipy.interpolate import interp1d
|
|
16
|
-
from ..
|
|
16
|
+
from .._free_form._plot_util import plot_density, plot_hilbert, \
|
|
17
17
|
plot_stieltjes, plot_stieltjes_on_disk, plot_samples
|
|
18
18
|
|
|
19
19
|
try:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
freealg/__init__.py,sha256=SjcYb6HWmaclnnM-m1eC1honZRyfNBWYDYBx23kSdjo,833
|
|
2
|
+
freealg/__version__.py,sha256=A6fZ_oURo3l_Fa_K29LgV21A4Onqu3NquwGYzL05E1Y,22
|
|
3
|
+
freealg/_util.py,sha256=E254DRTCeST7h1QHrfLeyg9jQntRaOyui1oXD7nMaWQ,1890
|
|
4
|
+
freealg/_algebraic_form/__init__.py,sha256=MIB_jVgw2qI-JW_ypqaFSeNAB6c4GvpjNySnap_a6hg,398
|
|
5
|
+
freealg/_algebraic_form/_continuation_algebraic.py,sha256=q81HNWG6sLx05Gbbpqi1LBI7Y7cxT-1F7uLMyRapAfI,14237
|
|
6
|
+
freealg/_algebraic_form/_decompress.py,sha256=gGtixLOVxlMy5S-NsXgoA7lIrB7u7nUZImQk1mIDo3s,21101
|
|
7
|
+
freealg/_algebraic_form/_decompress2.py,sha256=Ng9w9xmGe9M-DApp35IeNeQlvszfzT4NZx5BQn0lQ3I,2459
|
|
8
|
+
freealg/_algebraic_form/_edge.py,sha256=7l9QyLJDxaEY4WB6MCUFtfEZSf04wyHwH7YPHFJXSbM,10690
|
|
9
|
+
freealg/_algebraic_form/_homotopy.py,sha256=VIAa3ijwm4coZBXBPA_Tp__yCRcvEyipai7tOmvcthU,3989
|
|
10
|
+
freealg/_algebraic_form/_sheets_util.py,sha256=6OLzWQKu-gN8rxM2rbpbN8TjNZFmD8UJ-8t9kcZdkCo,4174
|
|
11
|
+
freealg/_algebraic_form/algebraic_form.py,sha256=mok3Jta_K0Vo1caAm0fI81LNgs0eB3CKM5RzZLEFPY4,30499
|
|
12
|
+
freealg/_free_form/__init__.py,sha256=5cnSX7kHci3wKx6-BEFhmVY_NjjmQAq1JjWPTEqETTg,611
|
|
13
|
+
freealg/_free_form/_chebyshev.py,sha256=zkyVA8NLf7uUKlJdLz4ijd_SurdsqUgkA5nHGWSybaE,6916
|
|
14
|
+
freealg/_free_form/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
|
|
15
|
+
freealg/_free_form/_decompress.py,sha256=_i37IToZ6oN9DdLXOM8r4y92EbazzcylhcnWwUOpaj0,32108
|
|
16
|
+
freealg/_free_form/_density_util.py,sha256=C_0lKA5QzKAdxPRLJvajNO9zaw2QliZ-n7SI8g2a53M,6745
|
|
17
|
+
freealg/_free_form/_jacobi.py,sha256=z0X6Ws_BEo_h8EQBzDNHGFhLF9F2PUmnGeBVs0bNL7w,10709
|
|
18
|
+
freealg/_free_form/_linalg.py,sha256=GNiQpT193VWxLOejVdAUPQdNJas4ms8US3YR8ZaYXGA,13143
|
|
19
|
+
freealg/_free_form/_pade.py,sha256=_y89r7rVc2E5lgiN_ZjnxzW2IaVevWwpq5ISor2NVOo,10310
|
|
20
|
+
freealg/_free_form/_plot_util.py,sha256=GKvmc1wjVGeqoomrULPbzBEt6P86FdoR2idBLYh5EDY,20068
|
|
21
|
+
freealg/_free_form/_sample.py,sha256=rhfd_83TCTvvJh8cG8TzEYO4OR8VbtND2YCNtWEhMa8,3205
|
|
22
|
+
freealg/_free_form/_series.py,sha256=33LLCUe4svmV0eWyzhP_XClfDzccQHTW9WBJlYlLfHY,11475
|
|
23
|
+
freealg/_free_form/_support.py,sha256=B6oSqW7MGikpJlTGzNbnMF63LhI5al2ccToqcncTPYs,6624
|
|
24
|
+
freealg/_free_form/free_form.py,sha256=UaRLYSvA0ib5wy1xnLy3_9ndCJ6gUdhNswSm0m9-7go,43571
|
|
25
|
+
freealg/_geometric_form/__init__.py,sha256=mWsXP0nXs3pY8RfUDhPRTgIfhOigKqw_VmoWnJOw2a0,485
|
|
26
|
+
freealg/_geometric_form/_continuation_genus0.py,sha256=4jiXfQaA6w3IhVkJgtKVVjqqtBmavq78FY_YTGUQyY0,4026
|
|
27
|
+
freealg/_geometric_form/_continuation_genus1.py,sha256=X8NZ1_6PxhJJLXZk5ASeGwxej_KwH3-ftuXkBrOFmgU,6021
|
|
28
|
+
freealg/_geometric_form/_elliptic_functions.py,sha256=Rr_pb1A_FjrJlraYQj2G5shdO6f77aVQN2eQzrvIygI,4109
|
|
29
|
+
freealg/_geometric_form/_sphere_maps.py,sha256=NlhTgWXKWXKdyR2dQxMmePsIwHp7IWyYh6uoxgW5Osc,1465
|
|
30
|
+
freealg/_geometric_form/_torus_maps.py,sha256=7m5QsbmnXTWHJE5rWjKG3_TnErHEEQ41vW-3hsOc3lo,3338
|
|
31
|
+
freealg/_geometric_form/geometric_form.py,sha256=whHKYQdakqShtR-jCEugevnje72JEr9M0HSvZ2BYoKs,33379
|
|
32
|
+
freealg/distributions/__init__.py,sha256=POoSAPVazz83y79pfdKeevPtKqpSATNb8S3ocYMdEzI,798
|
|
33
|
+
freealg/distributions/_chiral_block.py,sha256=YSC_Sk1u1UKlWVE1GGJEjX6VrWqpEEYNxBZ2j8Csc3A,13897
|
|
34
|
+
freealg/distributions/_deformed_marchenko_pastur.py,sha256=ov7mIqEj5I5i9euzTSBSng8BrryxVDUhi3hYNah53bI,19394
|
|
35
|
+
freealg/distributions/_deformed_wigner.py,sha256=9rekAGCKSTEWvMP0l25Lor60p4Gq2oxJuxHXmcJJ1AE,8046
|
|
36
|
+
freealg/distributions/_kesten_mckay.py,sha256=Uv3QuUYsfXbPMovXSO_pN3wdkc1fTKGVX7iuHDgBRqk,20089
|
|
37
|
+
freealg/distributions/_marchenko_pastur.py,sha256=eemaxDosKpV37TAn9uSiYpljIkVNoTWDJ9ZfYb8tAeY,20646
|
|
38
|
+
freealg/distributions/_meixner.py,sha256=GbcWeHrXMxK3Hdj4pCTESlMts9dEPQE1DhClxYprWfg,17590
|
|
39
|
+
freealg/distributions/_wachter.py,sha256=5nP4iqxgxltEHhZdb1ytei1zy_bDEI1cvldZdxlIXRk,17090
|
|
40
|
+
freealg/distributions/_wigner.py,sha256=epgx6ne6R_7to5j6-QsWIAVFJQFquWMmYgnZYMN4wUI,16069
|
|
41
|
+
freealg/visualization/__init__.py,sha256=NLq_zwueF7ytZ8sl8zLPqm-AODxxXNvfMozHGmmklcE,435
|
|
42
|
+
freealg/visualization/_glue_util.py,sha256=2oKnEYjUOS4OZfivmciVLauVr53kyHMwi6c2zRKilTQ,693
|
|
43
|
+
freealg/visualization/_rgb_hsv.py,sha256=rEskxXxSlKKxIrHRslVkgxHtD010L3ge9YtcVsOPl8E,3650
|
|
44
|
+
freealg-0.7.4.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
|
|
45
|
+
freealg-0.7.4.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
|
|
46
|
+
freealg-0.7.4.dist-info/METADATA,sha256=PbWMFiZoF0N2_Z5HONh85lI2oGnKzC5k57SklP4UZMc,5516
|
|
47
|
+
freealg-0.7.4.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
48
|
+
freealg-0.7.4.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
|
|
49
|
+
freealg-0.7.4.dist-info/RECORD,,
|
freealg-0.7.1.dist-info/RECORD
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
freealg/__init__.py,sha256=ApA8Dl6Sm29NN_hrT-YRmLFLhrXWEb5N1mc0e0_tDDE,832
|
|
2
|
-
freealg/__version__.py,sha256=2KJZDSMOG7KS82AxYOrZ4ZihYxX0wjfUjDsIZh3L024,22
|
|
3
|
-
freealg/_util.py,sha256=E254DRTCeST7h1QHrfLeyg9jQntRaOyui1oXD7nMaWQ,1890
|
|
4
|
-
freealg/_algebraic_form/__init__.py,sha256=MIB_jVgw2qI-JW_ypqaFSeNAB6c4GvpjNySnap_a6hg,398
|
|
5
|
-
freealg/_algebraic_form/_continuation_algebraic.py,sha256=y2ZKppTM41SBmhD3AsPN02c-MIuJWwrH1EqG385aOdY,13036
|
|
6
|
-
freealg/_algebraic_form/_decompress.py,sha256=gGtixLOVxlMy5S-NsXgoA7lIrB7u7nUZImQk1mIDo3s,21101
|
|
7
|
-
freealg/_algebraic_form/_edge.py,sha256=7l9QyLJDxaEY4WB6MCUFtfEZSf04wyHwH7YPHFJXSbM,10690
|
|
8
|
-
freealg/_algebraic_form/_sheets_util.py,sha256=6OLzWQKu-gN8rxM2rbpbN8TjNZFmD8UJ-8t9kcZdkCo,4174
|
|
9
|
-
freealg/_algebraic_form/algebraic_form.py,sha256=MFWS-zxacFprUWgFWDNdygIk4GFU6Bp-Ed_ns4wVBmk,28298
|
|
10
|
-
freealg/_freeform/__init__.py,sha256=fxHAqlCBK0AqLrK9VhA3FMeid8nutCy5UwsUSgkIhrI,610
|
|
11
|
-
freealg/_freeform/_chebyshev.py,sha256=zkyVA8NLf7uUKlJdLz4ijd_SurdsqUgkA5nHGWSybaE,6916
|
|
12
|
-
freealg/_freeform/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
|
|
13
|
-
freealg/_freeform/_decompress.py,sha256=_i37IToZ6oN9DdLXOM8r4y92EbazzcylhcnWwUOpaj0,32108
|
|
14
|
-
freealg/_freeform/_density_util.py,sha256=C_0lKA5QzKAdxPRLJvajNO9zaw2QliZ-n7SI8g2a53M,6745
|
|
15
|
-
freealg/_freeform/_jacobi.py,sha256=z0X6Ws_BEo_h8EQBzDNHGFhLF9F2PUmnGeBVs0bNL7w,10709
|
|
16
|
-
freealg/_freeform/_linalg.py,sha256=U7b_FJwfjU1itO4R4K831Zm2rnUB5eNOdOzvjPSeH98,13142
|
|
17
|
-
freealg/_freeform/_pade.py,sha256=_y89r7rVc2E5lgiN_ZjnxzW2IaVevWwpq5ISor2NVOo,10310
|
|
18
|
-
freealg/_freeform/_plot_util.py,sha256=GKvmc1wjVGeqoomrULPbzBEt6P86FdoR2idBLYh5EDY,20068
|
|
19
|
-
freealg/_freeform/_sample.py,sha256=rhfd_83TCTvvJh8cG8TzEYO4OR8VbtND2YCNtWEhMa8,3205
|
|
20
|
-
freealg/_freeform/_series.py,sha256=33LLCUe4svmV0eWyzhP_XClfDzccQHTW9WBJlYlLfHY,11475
|
|
21
|
-
freealg/_freeform/_support.py,sha256=nxDa2OFlWBgjD0_1qoSMWG7kub6-GIuxIA04n5bdaYw,6614
|
|
22
|
-
freealg/_freeform/freeform.py,sha256=UaRLYSvA0ib5wy1xnLy3_9ndCJ6gUdhNswSm0m9-7go,43571
|
|
23
|
-
freealg/_geometric_form/__init__.py,sha256=mWsXP0nXs3pY8RfUDhPRTgIfhOigKqw_VmoWnJOw2a0,485
|
|
24
|
-
freealg/_geometric_form/_continuation_genus0.py,sha256=4jiXfQaA6w3IhVkJgtKVVjqqtBmavq78FY_YTGUQyY0,4026
|
|
25
|
-
freealg/_geometric_form/_continuation_genus1.py,sha256=X8NZ1_6PxhJJLXZk5ASeGwxej_KwH3-ftuXkBrOFmgU,6021
|
|
26
|
-
freealg/_geometric_form/_elliptic_functions.py,sha256=Rr_pb1A_FjrJlraYQj2G5shdO6f77aVQN2eQzrvIygI,4109
|
|
27
|
-
freealg/_geometric_form/_sphere_maps.py,sha256=NlhTgWXKWXKdyR2dQxMmePsIwHp7IWyYh6uoxgW5Osc,1465
|
|
28
|
-
freealg/_geometric_form/_torus_maps.py,sha256=7m5QsbmnXTWHJE5rWjKG3_TnErHEEQ41vW-3hsOc3lo,3338
|
|
29
|
-
freealg/_geometric_form/geometric_form.py,sha256=whHKYQdakqShtR-jCEugevnje72JEr9M0HSvZ2BYoKs,33379
|
|
30
|
-
freealg/distributions/__init__.py,sha256=POoSAPVazz83y79pfdKeevPtKqpSATNb8S3ocYMdEzI,798
|
|
31
|
-
freealg/distributions/_chiral_block.py,sha256=NxwD-2qx7-ewg5TwMALnPoesUNIWPDktXhyH8RSKi2w,12350
|
|
32
|
-
freealg/distributions/_deformed_marchenko_pastur.py,sha256=cZN3XnAxK4L2zkteE4XL5rlslRz34ArUlInc9TzQIZA,17568
|
|
33
|
-
freealg/distributions/_deformed_wigner.py,sha256=ZC7dj-ViSxaYdHRs45Yx6vf9Yw0WaTm4qZ7FumE0EEo,7239
|
|
34
|
-
freealg/distributions/_kesten_mckay.py,sha256=Abnpz7cRUPvReqbkfXvOh7onrjjvuLDdUUx3wFpu1yA,20088
|
|
35
|
-
freealg/distributions/_marchenko_pastur.py,sha256=G8nzyeZ2kfNIHfv4D2p0uN_9_0Z98JqKvr9PvV9k4o8,20645
|
|
36
|
-
freealg/distributions/_meixner.py,sha256=oxtXpFhEnehPuGHp8d0XEdGCGwmP_KoNJABZRV5GgcQ,17589
|
|
37
|
-
freealg/distributions/_wachter.py,sha256=qn8-_vBoCcpOtsuxXZK6vGd8XABqAgT5wO5FmoSvrc8,17089
|
|
38
|
-
freealg/distributions/_wigner.py,sha256=C8pTM0LMrU79TYIVRvcgPUACG9mwRzMRajS-98YNHPw,16068
|
|
39
|
-
freealg/visualization/__init__.py,sha256=NLq_zwueF7ytZ8sl8zLPqm-AODxxXNvfMozHGmmklcE,435
|
|
40
|
-
freealg/visualization/_glue_util.py,sha256=2oKnEYjUOS4OZfivmciVLauVr53kyHMwi6c2zRKilTQ,693
|
|
41
|
-
freealg/visualization/_rgb_hsv.py,sha256=rEskxXxSlKKxIrHRslVkgxHtD010L3ge9YtcVsOPl8E,3650
|
|
42
|
-
freealg-0.7.1.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
|
|
43
|
-
freealg-0.7.1.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
|
|
44
|
-
freealg-0.7.1.dist-info/METADATA,sha256=xd4C9NmSiyjjLBVQjxFqzMx8VVpQ2rCK-x4tD55afEs,5516
|
|
45
|
-
freealg-0.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
46
|
-
freealg-0.7.1.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
|
|
47
|
-
freealg-0.7.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|