freealg 0.7.3__tar.gz → 0.7.4__tar.gz
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-0.7.3 → freealg-0.7.4}/PKG-INFO +1 -1
- freealg-0.7.4/freealg/__version__.py +1 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_algebraic_form/_continuation_algebraic.py +4 -2
- freealg-0.7.4/freealg/_algebraic_form/_decompress2.py +86 -0
- freealg-0.7.4/freealg/_algebraic_form/_homotopy.py +138 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_algebraic_form/algebraic_form.py +168 -113
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_deformed_wigner.py +7 -19
- {freealg-0.7.3 → freealg-0.7.4}/freealg.egg-info/PKG-INFO +1 -1
- {freealg-0.7.3 → freealg-0.7.4}/freealg.egg-info/SOURCES.txt +2 -0
- freealg-0.7.3/freealg/__version__.py +0 -1
- {freealg-0.7.3 → freealg-0.7.4}/AUTHORS.txt +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/CHANGELOG.rst +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/LICENSE.txt +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/MANIFEST.in +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/README.rst +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/__init__.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_algebraic_form/__init__.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_algebraic_form/_decompress.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_algebraic_form/_edge.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_algebraic_form/_sheets_util.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/__init__.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_chebyshev.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_damp.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_decompress.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_density_util.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_jacobi.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_linalg.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_pade.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_plot_util.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_sample.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_series.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/_support.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_free_form/free_form.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/__init__.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/_continuation_genus0.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/_continuation_genus1.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/_elliptic_functions.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/_sphere_maps.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/_torus_maps.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_geometric_form/geometric_form.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/_util.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/__init__.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_chiral_block.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_deformed_marchenko_pastur.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_kesten_mckay.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_marchenko_pastur.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_meixner.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_wachter.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/distributions/_wigner.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/visualization/__init__.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/visualization/_glue_util.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg/visualization/_rgb_hsv.py +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg.egg-info/dependency_links.txt +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg.egg-info/not-zip-safe +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg.egg-info/requires.txt +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/freealg.egg-info/top_level.txt +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/pyproject.toml +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/requirements.txt +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/setup.cfg +0 -0
- {freealg-0.7.3 → freealg-0.7.4}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.4"
|
|
@@ -23,12 +23,14 @@ __all__ = ['sample_z_joukowski', 'filter_z_away_from_cuts', 'powers',
|
|
|
23
23
|
# normalize coefficients
|
|
24
24
|
# ======================
|
|
25
25
|
|
|
26
|
-
def _normalize_coefficients(
|
|
26
|
+
def _normalize_coefficients(arr):
|
|
27
27
|
"""
|
|
28
28
|
Trim rows and columns on the sides (equivalent to factorizing or reducing
|
|
29
29
|
degree) and normalize so that the sum of the first column is one.
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
+
a = numpy.asarray(arr).copy()
|
|
33
|
+
|
|
32
34
|
if a.size == 0:
|
|
33
35
|
return a
|
|
34
36
|
|
|
@@ -51,7 +53,7 @@ def _normalize_coefficients(a):
|
|
|
51
53
|
a = a[:, first_col:last_col]
|
|
52
54
|
|
|
53
55
|
# --- Normalize so first column sums to 1 ---
|
|
54
|
-
col_sum = numpy.sum(a[:, 0])
|
|
56
|
+
col_sum = numpy.sum(numpy.abs(a[:, 0]))
|
|
55
57
|
if col_sum != 0:
|
|
56
58
|
a = a / col_sum
|
|
57
59
|
|
|
@@ -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
|
|
|
@@ -193,8 +218,6 @@ class AlgebraicForm(object):
|
|
|
193
218
|
# also empties all references holdign a cache copy.
|
|
194
219
|
# self.cache.clear()
|
|
195
220
|
|
|
196
|
-
# return self.a_coeffs
|
|
197
|
-
|
|
198
221
|
z_fits = []
|
|
199
222
|
for sup in self.support:
|
|
200
223
|
a, b = sup
|
|
@@ -205,12 +228,10 @@ class AlgebraicForm(object):
|
|
|
205
228
|
|
|
206
229
|
z_fit = numpy.concatenate(z_fits)
|
|
207
230
|
|
|
208
|
-
# Remove points too close to
|
|
231
|
+
# Remove points too close to any cut
|
|
209
232
|
z_fit = filter_z_away_from_cuts(z_fit, self.support, y_eps=y_eps,
|
|
210
233
|
x_pad=x_pad)
|
|
211
234
|
|
|
212
|
-
# ---------
|
|
213
|
-
|
|
214
235
|
m1_fit = self.stieltjes(z_fit)
|
|
215
236
|
a_coeffs = fit_polynomial_relation(z_fit, m1_fit, s=deg_m, deg_z=deg_z,
|
|
216
237
|
ridge_lambda=reg,
|
|
@@ -225,7 +246,7 @@ class AlgebraicForm(object):
|
|
|
225
246
|
print("fit residual 99.9%:",
|
|
226
247
|
numpy.quantile(P_res[numpy.isfinite(P_res)], 0.999))
|
|
227
248
|
|
|
228
|
-
print('\
|
|
249
|
+
print('\nCoefficients')
|
|
229
250
|
with numpy.printoptions(precision=4, suppress=True):
|
|
230
251
|
for i in range(a_coeffs.shape[0]):
|
|
231
252
|
for j in range(a_coeffs.shape[1]):
|
|
@@ -233,7 +254,7 @@ class AlgebraicForm(object):
|
|
|
233
254
|
print(f"{v.real:>+0.4f}{v.imag:>+0.4f}j", end=" ")
|
|
234
255
|
print('')
|
|
235
256
|
|
|
236
|
-
print('\
|
|
257
|
+
print('\nCoefficient Magnitudes')
|
|
237
258
|
with numpy.printoptions(precision=6, suppress=True):
|
|
238
259
|
print(numpy.abs(a_coeffs))
|
|
239
260
|
|
|
@@ -243,22 +264,22 @@ class AlgebraicForm(object):
|
|
|
243
264
|
# generate grid
|
|
244
265
|
# =============
|
|
245
266
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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)
|
|
262
283
|
|
|
263
284
|
# =======
|
|
264
285
|
# density
|
|
@@ -312,16 +333,19 @@ class AlgebraicForm(object):
|
|
|
312
333
|
raise RuntimeError('The model needs to be fit using the .fit() ' +
|
|
313
334
|
'function.')
|
|
314
335
|
|
|
315
|
-
#
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
#
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
+
|
|
325
349
|
# if self.method == 'jacobi':
|
|
326
350
|
# rho[mask] = jacobi_density(x[mask], self.psi, self.support,
|
|
327
351
|
# self.alpha, self.beta)
|
|
@@ -341,12 +365,12 @@ class AlgebraicForm(object):
|
|
|
341
365
|
# if min_rho < 0.0 - 1e-3:
|
|
342
366
|
# print(f'"rho" is not positive. min_rho: {min_rho:>0.3f}. Set ' +
|
|
343
367
|
# r'"force=True".')
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
|
350
374
|
|
|
351
375
|
# =======
|
|
352
376
|
# hilbert
|
|
@@ -568,68 +592,99 @@ class AlgebraicForm(object):
|
|
|
568
592
|
# decompress
|
|
569
593
|
# ==========
|
|
570
594
|
|
|
571
|
-
def decompress(self, x,
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
min_lam=1e-6,
|
|
576
|
-
w_min=1e-14,
|
|
577
|
-
sweep=True,
|
|
578
|
-
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}):
|
|
579
599
|
"""
|
|
580
600
|
Free decompression of spectral density.
|
|
581
601
|
"""
|
|
582
602
|
|
|
583
603
|
# Check size argument
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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.')
|
|
591
611
|
|
|
592
612
|
# Decompression ratio equal to e^{t}.
|
|
593
|
-
|
|
613
|
+
alpha = numpy.atleast_1d(size) / self.n
|
|
594
614
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
# rho = numpy.squeeze(rho)
|
|
598
|
-
#
|
|
599
|
-
# # Plot only the last size
|
|
600
|
-
# if plot:
|
|
601
|
-
# if numpy.isscalar(size):
|
|
602
|
-
# rho_last = rho
|
|
603
|
-
# else:
|
|
604
|
-
# rho_last = rho[-1, :]
|
|
605
|
-
# plot_density(x, rho_last, support=(lb, ub),
|
|
606
|
-
# label='Decompression', latex=latex, save=save)
|
|
607
|
-
#
|
|
608
|
-
# return rho, x
|
|
615
|
+
def m_fn(z):
|
|
616
|
+
return stieltjes_poly(z, self.a_coeffs)
|
|
609
617
|
|
|
610
|
-
#
|
|
611
|
-
|
|
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
|
|
612
623
|
|
|
613
|
-
#
|
|
614
|
-
|
|
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)
|
|
615
634
|
|
|
616
|
-
|
|
617
|
-
W, ok = decompress_newton(
|
|
618
|
-
z_query, t, self.a_coeffs,
|
|
619
|
-
w0_list=w0_list,
|
|
620
|
-
max_iter=max_iter,
|
|
621
|
-
tol=tol,
|
|
622
|
-
armijo=armijo,
|
|
623
|
-
min_lam=min_lam,
|
|
624
|
-
w_min=w_min,
|
|
625
|
-
sweep=sweep)
|
|
635
|
+
if method == 'one':
|
|
626
636
|
|
|
627
|
-
|
|
637
|
+
# Query grid on the real axis + a small imaginary buffer
|
|
638
|
+
z_query = x + 1j * self.delta
|
|
628
639
|
|
|
629
|
-
|
|
630
|
-
|
|
640
|
+
# Initial condition at t=0 (physical branch)
|
|
641
|
+
w0_list = self.stieltjes(z_query)
|
|
631
642
|
|
|
632
|
-
|
|
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
|
|
633
688
|
|
|
634
689
|
# ====
|
|
635
690
|
# edge
|
|
@@ -290,26 +290,14 @@ class DeformedWigner(object):
|
|
|
290
290
|
A : numpy.ndarray
|
|
291
291
|
A matrix of the size :math:`n \\times n`.
|
|
292
292
|
|
|
293
|
-
Parameters
|
|
294
|
-
----------
|
|
295
|
-
size : int
|
|
296
|
-
Size n of the matrix.
|
|
297
|
-
|
|
298
|
-
seed : int, default=None
|
|
299
|
-
Seed for random number generator.
|
|
300
|
-
|
|
301
|
-
Returns
|
|
302
|
-
-------
|
|
303
|
-
A : numpy.ndarray
|
|
304
|
-
Symmetric matrix of shape (n, n).
|
|
305
|
-
|
|
306
293
|
Notes
|
|
307
294
|
-----
|
|
308
295
|
|
|
309
|
-
Generate an :math:`n
|
|
310
|
-
\\sigma \\mathbf{W}`
|
|
311
|
-
|
|
312
|
-
:math:`H
|
|
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}`.
|
|
313
301
|
|
|
314
302
|
Examples
|
|
315
303
|
--------
|
|
@@ -317,8 +305,8 @@ class DeformedWigner(object):
|
|
|
317
305
|
.. code-block::python
|
|
318
306
|
|
|
319
307
|
>>> from freealg.distributions import DeformedWigner
|
|
320
|
-
>>>
|
|
321
|
-
>>> A =
|
|
308
|
+
>>> dwg = DeformedWigner(1/50)
|
|
309
|
+
>>> A = dwg.matrix(2000)
|
|
322
310
|
"""
|
|
323
311
|
|
|
324
312
|
n = int(size)
|
|
@@ -19,7 +19,9 @@ freealg.egg-info/top_level.txt
|
|
|
19
19
|
freealg/_algebraic_form/__init__.py
|
|
20
20
|
freealg/_algebraic_form/_continuation_algebraic.py
|
|
21
21
|
freealg/_algebraic_form/_decompress.py
|
|
22
|
+
freealg/_algebraic_form/_decompress2.py
|
|
22
23
|
freealg/_algebraic_form/_edge.py
|
|
24
|
+
freealg/_algebraic_form/_homotopy.py
|
|
23
25
|
freealg/_algebraic_form/_sheets_util.py
|
|
24
26
|
freealg/_algebraic_form/algebraic_form.py
|
|
25
27
|
freealg/_free_form/__init__.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.7.3"
|
|
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
|
|
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
|
|
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
|
|
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
|