freealg 0.1.12__py3-none-any.whl → 0.1.13__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 +143 -35
- freealg/freeform.py +4 -5
- {freealg-0.1.12.dist-info → freealg-0.1.13.dist-info}/METADATA +1 -1
- {freealg-0.1.12.dist-info → freealg-0.1.13.dist-info}/RECORD +9 -9
- {freealg-0.1.12.dist-info → freealg-0.1.13.dist-info}/WHEEL +0 -0
- {freealg-0.1.12.dist-info → freealg-0.1.13.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.1.12.dist-info → freealg-0.1.13.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.1.12.dist-info → freealg-0.1.13.dist-info}/top_level.txt +0 -0
freealg/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.13"
|
freealg/_decompress.py
CHANGED
|
@@ -15,13 +15,133 @@ import numpy
|
|
|
15
15
|
|
|
16
16
|
__all__ = ['decompress', 'reverse_characteristics']
|
|
17
17
|
|
|
18
|
+
def secant_complex(f, z0, z1, a=0+0j, tol=1e-12, max_iter=100,
|
|
19
|
+
alpha=0.5, max_bt=12, eps=1e-30, verbose=False):
|
|
20
|
+
"""
|
|
21
|
+
Solves :math:``f(z) = a`` for many starting points simultaneously
|
|
22
|
+
using the secant method in the complex plane.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
f : callable
|
|
27
|
+
Function that accepts and returns complex `ndarray`s.
|
|
28
|
+
|
|
29
|
+
z0, z1 : array_like
|
|
30
|
+
Two initial guesses. ``z1`` may be broadcast to ``z0``.
|
|
31
|
+
|
|
32
|
+
a : complex or array_like, optional
|
|
33
|
+
Right‑hand‑side targets (broadcasted to ``z0``). Defaults to ``0+0j``.
|
|
34
|
+
|
|
35
|
+
tol : float, optional
|
|
36
|
+
Convergence criterion on ``|f(z) - a|``. Defaults to ``1e-12``.
|
|
37
|
+
|
|
38
|
+
max_iter : int, optional
|
|
39
|
+
Maximum number of secant iterations. Defaults to ``100``.
|
|
40
|
+
|
|
41
|
+
alpha : float, optional
|
|
42
|
+
Back‑tracking shrink factor (``0 < alpha < 1``). Defaults to ``0.5``.
|
|
43
|
+
|
|
44
|
+
max_bt : int, optional
|
|
45
|
+
Maximum back‑tracking trials per iteration. Defaults to ``12``.
|
|
46
|
+
|
|
47
|
+
eps : float, optional
|
|
48
|
+
Safeguard added to tiny denominators. Defaults to ``1e-30``.
|
|
49
|
+
|
|
50
|
+
verbose : bool, optional
|
|
51
|
+
If *True*, prints progress every 10 iterations.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
roots : ndarray
|
|
56
|
+
Estimated roots, shaped like the broadcast inputs.
|
|
57
|
+
residuals : ndarray
|
|
58
|
+
Final residuals ``|f(root) - a|``.
|
|
59
|
+
iterations : ndarray
|
|
60
|
+
Iteration count for each point.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
# Broadcast inputs
|
|
64
|
+
z0, z1, a = numpy.broadcast_arrays(
|
|
65
|
+
numpy.asarray(z0, numpy.complex128),
|
|
66
|
+
numpy.asarray(z1, numpy.complex128),
|
|
67
|
+
numpy.asarray(a, numpy.complex128),
|
|
68
|
+
)
|
|
69
|
+
orig_shape = z0.shape
|
|
70
|
+
z0, z1, a = (x.ravel() for x in (z0, z1, a))
|
|
71
|
+
|
|
72
|
+
n_points = z0.size
|
|
73
|
+
roots = z1.copy()
|
|
74
|
+
iterations = numpy.zeros(n_points, dtype=int)
|
|
75
|
+
|
|
76
|
+
f0 = f(z0) - a
|
|
77
|
+
f1 = f(z1) - a
|
|
78
|
+
residuals = numpy.abs(f1)
|
|
79
|
+
converged = residuals < tol
|
|
80
|
+
|
|
81
|
+
# Entering main loop
|
|
82
|
+
for k in range(max_iter):
|
|
83
|
+
active = ~converged
|
|
84
|
+
if not active.any():
|
|
85
|
+
break
|
|
86
|
+
|
|
87
|
+
# Secant step
|
|
88
|
+
denom = f1 - f0
|
|
89
|
+
denom = numpy.where(numpy.abs(denom) < eps, denom + eps, denom)
|
|
90
|
+
dz = (z1 - z0) * f1 / denom
|
|
91
|
+
z2 = z1 - dz
|
|
92
|
+
f2 = f(z2) - a
|
|
93
|
+
|
|
94
|
+
# Line search by backtracking
|
|
95
|
+
worse = (numpy.abs(f2) >= numpy.abs(f1)) & active
|
|
96
|
+
if worse.any():
|
|
97
|
+
shrink = numpy.ones_like(dz)
|
|
98
|
+
for _ in range(max_bt):
|
|
99
|
+
shrink[worse] *= alpha
|
|
100
|
+
z_try = z1[worse] - shrink[worse] * dz[worse]
|
|
101
|
+
f_try = f(z_try) - a[worse]
|
|
102
|
+
|
|
103
|
+
improved = numpy.abs(f_try) < numpy.abs(f1[worse])
|
|
104
|
+
if not improved.any():
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
idx = numpy.flatnonzero(worse)[improved]
|
|
108
|
+
z2[idx], f2[idx] = z_try[improved], f_try[improved]
|
|
109
|
+
worse[idx] = False
|
|
110
|
+
if not worse.any():
|
|
111
|
+
break
|
|
112
|
+
|
|
113
|
+
# Book‑keeping
|
|
114
|
+
newly_conv = (numpy.abs(f2) < tol) & active
|
|
115
|
+
converged[newly_conv] = True
|
|
116
|
+
iterations[newly_conv] = k + 1
|
|
117
|
+
roots[newly_conv] = z2[newly_conv]
|
|
118
|
+
residuals[newly_conv] = numpy.abs(f2[newly_conv])
|
|
119
|
+
|
|
120
|
+
still = active & ~newly_conv
|
|
121
|
+
z0[still], z1[still] = z1[still], z2[still]
|
|
122
|
+
f0[still], f1[still] = f1[still], f2[still]
|
|
123
|
+
|
|
124
|
+
if verbose and k % 10 == 0:
|
|
125
|
+
print(f"Iter {k}: {converged.sum()} / {n_points} converged")
|
|
126
|
+
|
|
127
|
+
# Non‑converged points
|
|
128
|
+
remaining = ~converged
|
|
129
|
+
roots[remaining] = z1[remaining]
|
|
130
|
+
residuals[remaining] = numpy.abs(f1[remaining])
|
|
131
|
+
iterations[remaining] = max_iter
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
roots.reshape(orig_shape),
|
|
135
|
+
residuals.reshape(orig_shape),
|
|
136
|
+
iterations.reshape(orig_shape),
|
|
137
|
+
)
|
|
18
138
|
|
|
19
139
|
# ==========
|
|
20
140
|
# decompress
|
|
21
141
|
# ==========
|
|
22
142
|
|
|
23
|
-
def decompress(freeform, size, x=None, delta=1e-6,
|
|
24
|
-
|
|
143
|
+
def decompress(freeform, size, x=None, delta=1e-6, max_iter=500,
|
|
144
|
+
tolerance=1e-12):
|
|
25
145
|
"""
|
|
26
146
|
Free decompression of spectral density.
|
|
27
147
|
|
|
@@ -42,15 +162,11 @@ def decompress(freeform, size, x=None, delta=1e-6, iterations=500,
|
|
|
42
162
|
Size of the perturbation into the upper half plane for Plemelj's
|
|
43
163
|
formula.
|
|
44
164
|
|
|
45
|
-
|
|
46
|
-
Maximum number of
|
|
165
|
+
max_iter: int, default=500
|
|
166
|
+
Maximum number of secant method iterations.
|
|
47
167
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
tolerance: float, default=1e-4
|
|
52
|
-
Tolerance for the solution obtained by the Newton solver. Also
|
|
53
|
-
used for the finite difference approximation to the derivative.
|
|
168
|
+
tolerance: float, default=1e-12
|
|
169
|
+
Tolerance for the solution obtained by the secant method solver.
|
|
54
170
|
|
|
55
171
|
Returns
|
|
56
172
|
-------
|
|
@@ -99,42 +215,34 @@ def decompress(freeform, size, x=None, delta=1e-6, iterations=500,
|
|
|
99
215
|
x_max = numpy.ceil(center + radius * scale)
|
|
100
216
|
x = numpy.linspace(x_min, x_max, 500)
|
|
101
217
|
|
|
102
|
-
def _char_z(z):
|
|
103
|
-
return z + (1 / m(z)[1]) * (1 - alpha)
|
|
104
|
-
|
|
105
218
|
# Ensure that input is an array
|
|
106
219
|
x = numpy.asarray(x)
|
|
107
|
-
|
|
108
220
|
target = x + delta * 1j
|
|
221
|
+
if numpy.isclose(alpha, 1.0):
|
|
222
|
+
return freeform.density(x), x, freeform.support
|
|
109
223
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# Broken Newton steps can produce a lot of warnings. Removing them
|
|
114
|
-
# for now.
|
|
115
|
-
with numpy.errstate(all='ignore'):
|
|
116
|
-
for _ in range(iterations):
|
|
117
|
-
objective = _char_z(z) - target
|
|
118
|
-
mask = numpy.abs(objective) >= tolerance
|
|
119
|
-
if not numpy.any(mask):
|
|
120
|
-
break
|
|
121
|
-
z_m = z[mask]
|
|
122
|
-
|
|
123
|
-
# Perform finite difference approximation
|
|
124
|
-
dfdz = _char_z(z_m+tolerance) - _char_z(z_m-tolerance)
|
|
125
|
-
dfdz /= 2*tolerance
|
|
126
|
-
dfdz[dfdz == 0] = 1.0
|
|
224
|
+
# Characteristic curve map
|
|
225
|
+
def _char_z(z):
|
|
226
|
+
return z + (1 / m(z)[1]) * (1 - alpha)
|
|
127
227
|
|
|
128
|
-
|
|
129
|
-
|
|
228
|
+
z0 = numpy.full(target.shape, numpy.mean(freeform.support) + delta*1j,
|
|
229
|
+
dtype=numpy.complex128)
|
|
230
|
+
z1 = z0 - numpy.log(alpha) * 1j
|
|
231
|
+
|
|
232
|
+
roots, _, _ = secant_complex(
|
|
233
|
+
_char_z, z0, z1,
|
|
234
|
+
a=target,
|
|
235
|
+
tol=tolerance,
|
|
236
|
+
max_iter=max_iter
|
|
237
|
+
)
|
|
130
238
|
|
|
131
239
|
# Plemelj's formula
|
|
240
|
+
z = roots
|
|
132
241
|
char_s = m(z)[1] / alpha
|
|
133
242
|
rho = numpy.maximum(0, char_s.imag / numpy.pi)
|
|
134
243
|
rho[numpy.isnan(rho) | numpy.isinf(rho)] = 0
|
|
135
|
-
rho = rho.reshape(*x.shape)
|
|
136
244
|
|
|
137
|
-
return rho, x, (lb, ub)
|
|
245
|
+
return rho.reshape(*x.shape), x, (lb, ub)
|
|
138
246
|
|
|
139
247
|
|
|
140
248
|
# =======================
|
freealg/freeform.py
CHANGED
|
@@ -783,7 +783,7 @@ class FreeForm(object):
|
|
|
783
783
|
# decompress
|
|
784
784
|
# ==========
|
|
785
785
|
|
|
786
|
-
def decompress(self, size, x=None,
|
|
786
|
+
def decompress(self, size, x=None, max_iter=500, eigvals=True,
|
|
787
787
|
step_size=0.1, tolerance=1e-9, seed=None, plot=False,
|
|
788
788
|
latex=False, save=False):
|
|
789
789
|
"""
|
|
@@ -799,8 +799,8 @@ class FreeForm(object):
|
|
|
799
799
|
Positions where density to be evaluated at. If `None`, an interval
|
|
800
800
|
slightly larger than the support interval will be used.
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
Maximum number of
|
|
802
|
+
max_iter: int, default=500
|
|
803
|
+
Maximum number of secant method iterations.
|
|
804
804
|
|
|
805
805
|
eigvals: bool, default=True
|
|
806
806
|
Return estimated (sampled) eigenvalues as well as the density.
|
|
@@ -867,8 +867,7 @@ class FreeForm(object):
|
|
|
867
867
|
size = int(size)
|
|
868
868
|
|
|
869
869
|
rho, x, (lb, ub) = decompress(self, size, x=x, delta=self.delta,
|
|
870
|
-
|
|
871
|
-
step_size=step_size, tolerance=tolerance)
|
|
870
|
+
max_iter=max_iter, tolerance=tolerance)
|
|
872
871
|
x, rho = x.ravel(), rho.ravel()
|
|
873
872
|
|
|
874
873
|
if plot:
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
freealg/__init__.py,sha256=YqewBd3fq4nm-L3oGcExhEDR2wtVcrtggkSGzfpDqr4,528
|
|
2
|
-
freealg/__version__.py,sha256=
|
|
2
|
+
freealg/__version__.py,sha256=khDKUuWafURKVs5EAZkpOMiUHI2-V7axlqrWLPUpuZo,23
|
|
3
3
|
freealg/_chebyshev.py,sha256=oDJtRCwKEHazitzVBDbfdQz1jkyfsJOJfcAfo-PNkNo,5745
|
|
4
4
|
freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
|
|
5
|
-
freealg/_decompress.py,sha256=
|
|
5
|
+
freealg/_decompress.py,sha256=vTnxV_7XPYUVVnWCnALCrZkQT77IAPm_CzrWMgiTiqg,7986
|
|
6
6
|
freealg/_jacobi.py,sha256=AT4ONSHGGDxVKE3MGMLyMR8uDFiO-e9u3x5udYfdJJk,5635
|
|
7
7
|
freealg/_pade.py,sha256=yREJYSmnWaVUNRBNxjuQUqeLe_XSaGa9_VzV6HG5RkA,15164
|
|
8
8
|
freealg/_plot_util.py,sha256=BOYre8FPhrxmW1VRj3I40dCjWTFqUBTInmXc3wFunKQ,19648
|
|
9
9
|
freealg/_sample.py,sha256=ckC75eqv-mRP1F5BnhvsjfLTaoAzHK8bebl9bCRZYDo,2561
|
|
10
10
|
freealg/_support.py,sha256=A8hUjfKnSkHm09KLcEkeEXeTieKjhH-sVPd7I3_p4VE,3100
|
|
11
11
|
freealg/_util.py,sha256=PWLXcsTb0-FinGWvNiY12D-f4CHQB5bP_W3ThqfY4FY,3681
|
|
12
|
-
freealg/freeform.py,sha256=
|
|
12
|
+
freealg/freeform.py,sha256=_HDgchJaeryUTgywobSM4Yr8SjXi6pRVG8kQkTKDdMM,30375
|
|
13
13
|
freealg/distributions/__init__.py,sha256=t_yZyEkW_W_tSV9IvgYXtVASxD2BEdiNVXcV2ebMy8M,579
|
|
14
14
|
freealg/distributions/_kesten_mckay.py,sha256=HDMjbM1AcNxlwrpYeGmRqcbP10QsLI5RCeKvjVK3tOk,19566
|
|
15
15
|
freealg/distributions/_marchenko_pastur.py,sha256=th921hlEEtTbnHnRyBgT54a_e-9ZzAl9rB78O9FjorY,16688
|
|
16
16
|
freealg/distributions/_meixner.py,sha256=ItE0zYG2vhyUkObxbx4bDZaJ0BHVQWPzAJGLdMz10l4,17206
|
|
17
17
|
freealg/distributions/_wachter.py,sha256=lw70PT3TZlCf7mHU8IqoygXFUWB4IL57obkng0_ZGeI,16591
|
|
18
18
|
freealg/distributions/_wigner.py,sha256=2ZSPjgmDr9q9qiz6jO6yhXFo4ALHfxK1f0EzolzhRNE,15565
|
|
19
|
-
freealg-0.1.
|
|
20
|
-
freealg-0.1.
|
|
21
|
-
freealg-0.1.
|
|
22
|
-
freealg-0.1.
|
|
23
|
-
freealg-0.1.
|
|
24
|
-
freealg-0.1.
|
|
19
|
+
freealg-0.1.13.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
|
|
20
|
+
freealg-0.1.13.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
|
|
21
|
+
freealg-0.1.13.dist-info/METADATA,sha256=83nEmVJt6xwHPu6o0qlYN0JQ_zYuqgIPgQBvHsAdvkE,4029
|
|
22
|
+
freealg-0.1.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
freealg-0.1.13.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
|
|
24
|
+
freealg-0.1.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|