freealg 0.1.11__tar.gz → 0.1.13__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.
Files changed (36) hide show
  1. {freealg-0.1.11 → freealg-0.1.13}/PKG-INFO +3 -2
  2. {freealg-0.1.11 → freealg-0.1.13}/README.rst +1 -1
  3. freealg-0.1.13/freealg/__version__.py +1 -0
  4. freealg-0.1.13/freealg/_decompress.py +288 -0
  5. {freealg-0.1.11 → freealg-0.1.13}/freealg/freeform.py +9 -9
  6. {freealg-0.1.11 → freealg-0.1.13}/freealg.egg-info/PKG-INFO +3 -2
  7. {freealg-0.1.11 → freealg-0.1.13}/freealg.egg-info/requires.txt +1 -0
  8. {freealg-0.1.11 → freealg-0.1.13}/requirements.txt +1 -0
  9. freealg-0.1.11/freealg/__version__.py +0 -1
  10. freealg-0.1.11/freealg/_decompress.py +0 -180
  11. {freealg-0.1.11 → freealg-0.1.13}/AUTHORS.txt +0 -0
  12. {freealg-0.1.11 → freealg-0.1.13}/CHANGELOG.rst +0 -0
  13. {freealg-0.1.11 → freealg-0.1.13}/LICENSE.txt +0 -0
  14. {freealg-0.1.11 → freealg-0.1.13}/MANIFEST.in +0 -0
  15. {freealg-0.1.11 → freealg-0.1.13}/freealg/__init__.py +0 -0
  16. {freealg-0.1.11 → freealg-0.1.13}/freealg/_chebyshev.py +0 -0
  17. {freealg-0.1.11 → freealg-0.1.13}/freealg/_damp.py +0 -0
  18. {freealg-0.1.11 → freealg-0.1.13}/freealg/_jacobi.py +0 -0
  19. {freealg-0.1.11 → freealg-0.1.13}/freealg/_pade.py +0 -0
  20. {freealg-0.1.11 → freealg-0.1.13}/freealg/_plot_util.py +0 -0
  21. {freealg-0.1.11 → freealg-0.1.13}/freealg/_sample.py +0 -0
  22. {freealg-0.1.11 → freealg-0.1.13}/freealg/_support.py +0 -0
  23. {freealg-0.1.11 → freealg-0.1.13}/freealg/_util.py +0 -0
  24. {freealg-0.1.11 → freealg-0.1.13}/freealg/distributions/__init__.py +0 -0
  25. {freealg-0.1.11 → freealg-0.1.13}/freealg/distributions/_kesten_mckay.py +0 -0
  26. {freealg-0.1.11 → freealg-0.1.13}/freealg/distributions/_marchenko_pastur.py +0 -0
  27. {freealg-0.1.11 → freealg-0.1.13}/freealg/distributions/_meixner.py +0 -0
  28. {freealg-0.1.11 → freealg-0.1.13}/freealg/distributions/_wachter.py +0 -0
  29. {freealg-0.1.11 → freealg-0.1.13}/freealg/distributions/_wigner.py +0 -0
  30. {freealg-0.1.11 → freealg-0.1.13}/freealg.egg-info/SOURCES.txt +0 -0
  31. {freealg-0.1.11 → freealg-0.1.13}/freealg.egg-info/dependency_links.txt +0 -0
  32. {freealg-0.1.11 → freealg-0.1.13}/freealg.egg-info/not-zip-safe +0 -0
  33. {freealg-0.1.11 → freealg-0.1.13}/freealg.egg-info/top_level.txt +0 -0
  34. {freealg-0.1.11 → freealg-0.1.13}/pyproject.toml +0 -0
  35. {freealg-0.1.11 → freealg-0.1.13}/setup.cfg +0 -0
  36. {freealg-0.1.11 → freealg-0.1.13}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -31,6 +31,7 @@ Requires-Dist: texplot
31
31
  Requires-Dist: matplotlib
32
32
  Requires-Dist: colorcet
33
33
  Requires-Dist: statsmodels
34
+ Requires-Dist: numba
34
35
  Provides-Extra: test
35
36
  Requires-Dist: tox; extra == "test"
36
37
  Requires-Dist: pytest-cov; extra == "test"
@@ -108,7 +109,7 @@ smaller Wishart matrix.
108
109
  >>> A = mp.matrix(1000) # Sample a 1000 x 1000 Wishart matrix
109
110
  >>> eigs = fa.eigfree(A, 100_000) # Estimate the eigenvalues of 100000 x 100000
110
111
 
111
- For more details on how to interface with *freealg* check out the `Quick Start Guide <https://github.com/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`.
112
+ For more details on how to interface with *freealg* check out the `Quick Start Guide <https://github.com/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`__.
112
113
 
113
114
 
114
115
  Test
@@ -42,7 +42,7 @@ smaller Wishart matrix.
42
42
  >>> A = mp.matrix(1000) # Sample a 1000 x 1000 Wishart matrix
43
43
  >>> eigs = fa.eigfree(A, 100_000) # Estimate the eigenvalues of 100000 x 100000
44
44
 
45
- For more details on how to interface with *freealg* check out the `Quick Start Guide <https://github.com/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`.
45
+ For more details on how to interface with *freealg* check out the `Quick Start Guide <https://github.com/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`__.
46
46
 
47
47
 
48
48
  Test
@@ -0,0 +1 @@
1
+ __version__ = "0.1.13"
@@ -0,0 +1,288 @@
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
+ # from scipy.integrate import solve_ivp
15
+
16
+ __all__ = ['decompress', 'reverse_characteristics']
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
+ )
138
+
139
+ # ==========
140
+ # decompress
141
+ # ==========
142
+
143
+ def decompress(freeform, size, x=None, delta=1e-6, max_iter=500,
144
+ tolerance=1e-12):
145
+ """
146
+ Free decompression of spectral density.
147
+
148
+ Parameters
149
+ ----------
150
+
151
+ freeform : FreeForm
152
+ The initial freeform object of matrix to be decompressed
153
+
154
+ size : int
155
+ Size of the decompressed matrix.
156
+
157
+ x : numpy.array, default=None
158
+ Positions where density to be evaluated at. If `None`, an interval
159
+ slightly larger than the support interval will be used.
160
+
161
+ delta: float, default=1e-4
162
+ Size of the perturbation into the upper half plane for Plemelj's
163
+ formula.
164
+
165
+ max_iter: int, default=500
166
+ Maximum number of secant method iterations.
167
+
168
+ tolerance: float, default=1e-12
169
+ Tolerance for the solution obtained by the secant method solver.
170
+
171
+ Returns
172
+ -------
173
+
174
+ rho : numpy.array
175
+ Spectral density
176
+
177
+ See Also
178
+ --------
179
+
180
+ density
181
+ stieltjes
182
+
183
+ Notes
184
+ -----
185
+
186
+ Work in progress.
187
+
188
+ References
189
+ ----------
190
+
191
+ .. [1] tbd
192
+
193
+ Examples
194
+ --------
195
+
196
+ .. code-block:: python
197
+
198
+ >>> from freealg import FreeForm
199
+ """
200
+
201
+ alpha = size / freeform.n
202
+ m = freeform._eval_stieltjes
203
+ # Lower and upper bound on new support
204
+ hilb_lb = (1 / m(freeform.lam_m + delta * 1j)[1]).real
205
+ hilb_ub = (1 / m(freeform.lam_p + delta * 1j)[1]).real
206
+ lb = freeform.lam_m - (alpha - 1) * hilb_lb
207
+ ub = freeform.lam_p - (alpha - 1) * hilb_ub
208
+
209
+ # Create x if not given
210
+ if x is None:
211
+ radius = 0.5 * (ub - lb)
212
+ center = 0.5 * (ub + lb)
213
+ scale = 1.25
214
+ x_min = numpy.floor(center - radius * scale)
215
+ x_max = numpy.ceil(center + radius * scale)
216
+ x = numpy.linspace(x_min, x_max, 500)
217
+
218
+ # Ensure that input is an array
219
+ x = numpy.asarray(x)
220
+ target = x + delta * 1j
221
+ if numpy.isclose(alpha, 1.0):
222
+ return freeform.density(x), x, freeform.support
223
+
224
+ # Characteristic curve map
225
+ def _char_z(z):
226
+ return z + (1 / m(z)[1]) * (1 - alpha)
227
+
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
+ )
238
+
239
+ # Plemelj's formula
240
+ z = roots
241
+ char_s = m(z)[1] / alpha
242
+ rho = numpy.maximum(0, char_s.imag / numpy.pi)
243
+ rho[numpy.isnan(rho) | numpy.isinf(rho)] = 0
244
+
245
+ return rho.reshape(*x.shape), x, (lb, ub)
246
+
247
+
248
+ # =======================
249
+ # reverse characteristics
250
+ # =======================
251
+
252
+ def reverse_characteristics(freeform, z_inits, T, iterations=500,
253
+ step_size=0.1, tolerance=1e-8):
254
+ """
255
+ """
256
+
257
+ t_span = (0, T)
258
+ t_eval = numpy.linspace(t_span[0], t_span[1], 50)
259
+
260
+ m = freeform._eval_stieltjes
261
+
262
+ def _char_z(z, t):
263
+ return z + (1 / m(z)[1]) * (1 - numpy.exp(t))
264
+
265
+ target_z, target_t = numpy.meshgrid(z_inits, t_eval)
266
+
267
+ z = numpy.full(target_z.shape, numpy.mean(freeform.support) - .1j,
268
+ dtype=numpy.complex128)
269
+
270
+ # Broken Newton steps can produce a lot of warnings. Removing them for now.
271
+ with numpy.errstate(all='ignore'):
272
+ for _ in range(iterations):
273
+ objective = _char_z(z, target_t) - target_z
274
+ mask = numpy.abs(objective) >= tolerance
275
+ if not numpy.any(mask):
276
+ break
277
+ z_m = z[mask]
278
+ t_m = target_t[mask]
279
+
280
+ # Perform finite difference approximation
281
+ dfdz = _char_z(z_m+tolerance, t_m) - _char_z(z_m-tolerance, t_m)
282
+ dfdz /= 2*tolerance
283
+ dfdz[dfdz == 0] = 1.0
284
+
285
+ # Perform Newton step
286
+ z[mask] = z_m - step_size * objective[mask] / dfdz
287
+
288
+ return z
@@ -783,8 +783,8 @@ class FreeForm(object):
783
783
  # decompress
784
784
  # ==========
785
785
 
786
- def decompress(self, size, x=None, iterations=500, eigvals=True,
787
- step_size=0.1, tolerance=1e-6, seed=None, plot=False,
786
+ def decompress(self, size, x=None, max_iter=500, eigvals=True,
787
+ step_size=0.1, tolerance=1e-9, seed=None, plot=False,
788
788
  latex=False, save=False):
789
789
  """
790
790
  Free decompression of spectral density.
@@ -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
- iterations: int, default=500
803
- Maximum number of Newton iterations.
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.
@@ -808,7 +808,7 @@ class FreeForm(object):
808
808
  step_size: float, default=0.1
809
809
  Step size for Newton iterations.
810
810
 
811
- tolerance: float, default=1e-6
811
+ tolerance: float, default=1e-9
812
812
  Tolerance for the solution obtained by the Newton solver. Also
813
813
  used for the finite difference approximation to the derivative.
814
814
 
@@ -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
- iterations=iterations,
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:
@@ -956,12 +955,13 @@ def eigfree(A, N = None, psd = None):
956
955
  ff.n = n_s
957
956
 
958
957
  # Perform fit and estimate eigenvalues
959
- order = 1 + int(len(samples)**.25)
958
+ order = 1 + int(len(samples)**.2)
960
959
  ff.fit(method='chebyshev', K=order, projection='sample', damp='jackson',
961
- force=True, plot=False, latex=False, save=False, reg=0.01)
960
+ force=True, plot=False, latex=False, save=False, reg=0.05)
962
961
  _, _, eigs = ff.decompress(N)
963
962
 
964
963
  if psd:
965
964
  eigs = numpy.abs(eigs)
965
+ eigs.sort()
966
966
 
967
967
  return eigs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -31,6 +31,7 @@ Requires-Dist: texplot
31
31
  Requires-Dist: matplotlib
32
32
  Requires-Dist: colorcet
33
33
  Requires-Dist: statsmodels
34
+ Requires-Dist: numba
34
35
  Provides-Extra: test
35
36
  Requires-Dist: tox; extra == "test"
36
37
  Requires-Dist: pytest-cov; extra == "test"
@@ -108,7 +109,7 @@ smaller Wishart matrix.
108
109
  >>> A = mp.matrix(1000) # Sample a 1000 x 1000 Wishart matrix
109
110
  >>> eigs = fa.eigfree(A, 100_000) # Estimate the eigenvalues of 100000 x 100000
110
111
 
111
- For more details on how to interface with *freealg* check out the `Quick Start Guide <https://github.com/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`.
112
+ For more details on how to interface with *freealg* check out the `Quick Start Guide <https://github.com/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`__.
112
113
 
113
114
 
114
115
  Test
@@ -4,6 +4,7 @@ texplot
4
4
  matplotlib
5
5
  colorcet
6
6
  statsmodels
7
+ numba
7
8
 
8
9
  [docs]
9
10
  sphinx
@@ -4,3 +4,4 @@ texplot
4
4
  matplotlib
5
5
  colorcet
6
6
  statsmodels
7
+ numba
@@ -1 +0,0 @@
1
- __version__ = "0.1.11"
@@ -1,180 +0,0 @@
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
- # from scipy.integrate import solve_ivp
15
-
16
- __all__ = ['decompress', 'reverse_characteristics']
17
-
18
-
19
- # ==========
20
- # decompress
21
- # ==========
22
-
23
- def decompress(freeform, size, x=None, delta=1e-4, iterations=500,
24
- step_size=0.1, tolerance=1e-4):
25
- """
26
- Free decompression of spectral density.
27
-
28
- Parameters
29
- ----------
30
-
31
- freeform : FreeForm
32
- The initial freeform object of matrix to be decompressed
33
-
34
- size : int
35
- Size of the decompressed matrix.
36
-
37
- x : numpy.array, default=None
38
- Positions where density to be evaluated at. If `None`, an interval
39
- slightly larger than the support interval will be used.
40
-
41
- delta: float, default=1e-4
42
- Size of the perturbation into the upper half plane for Plemelj's
43
- formula.
44
-
45
- iterations: int, default=500
46
- Maximum number of Newton iterations.
47
-
48
- step_size: float, default=0.1
49
- Step size for Newton iterations.
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.
54
-
55
- Returns
56
- -------
57
-
58
- rho : numpy.array
59
- Spectral density
60
-
61
- See Also
62
- --------
63
-
64
- density
65
- stieltjes
66
-
67
- Notes
68
- -----
69
-
70
- Work in progress.
71
-
72
- References
73
- ----------
74
-
75
- .. [1] tbd
76
-
77
- Examples
78
- --------
79
-
80
- .. code-block:: python
81
-
82
- >>> from freealg import FreeForm
83
- """
84
-
85
- alpha = size / freeform.n
86
- m = freeform._eval_stieltjes
87
- # Lower and upper bound on new support
88
- hilb_lb = (1 / m(freeform.lam_m + delta * 1j)[1]).real
89
- hilb_ub = (1 / m(freeform.lam_p + delta * 1j)[1]).real
90
- lb = freeform.lam_m - (alpha - 1) * hilb_lb
91
- ub = freeform.lam_p - (alpha - 1) * hilb_ub
92
-
93
- # Create x if not given
94
- if x is None:
95
- radius = 0.5 * (ub - lb)
96
- center = 0.5 * (ub + lb)
97
- scale = 1.25
98
- x_min = numpy.floor(center - radius * scale)
99
- x_max = numpy.ceil(center + radius * scale)
100
- x = numpy.linspace(x_min, x_max, 500)
101
-
102
- def _char_z(z):
103
- return z + (1 / m(z)[1]) * (1 - alpha)
104
-
105
- # Ensure that input is an array
106
- x = numpy.asarray(x)
107
-
108
- target = x + delta * 1j
109
-
110
- z = numpy.full(target.shape, numpy.mean(freeform.support) - .1j,
111
- dtype=numpy.complex128)
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
127
-
128
- # Perform Newton step
129
- z[mask] = z_m - step_size * objective[mask] / dfdz
130
-
131
- # Plemelj's formula
132
- char_s = m(z)[1] / alpha
133
- rho = numpy.maximum(0, char_s.imag / numpy.pi)
134
- rho[numpy.isnan(rho) | numpy.isinf(rho)] = 0
135
- rho = rho.reshape(*x.shape)
136
-
137
- return rho, x, (lb, ub)
138
-
139
-
140
- # =======================
141
- # reverse characteristics
142
- # =======================
143
-
144
- def reverse_characteristics(freeform, z_inits, T, iterations=500,
145
- step_size=0.1, tolerance=1e-8):
146
- """
147
- """
148
-
149
- t_span = (0, T)
150
- t_eval = numpy.linspace(t_span[0], t_span[1], 50)
151
-
152
- m = freeform._eval_stieltjes
153
-
154
- def _char_z(z, t):
155
- return z + (1 / m(z)[1]) * (1 - numpy.exp(t))
156
-
157
- target_z, target_t = numpy.meshgrid(z_inits, t_eval)
158
-
159
- z = numpy.full(target_z.shape, numpy.mean(freeform.support) - .1j,
160
- dtype=numpy.complex128)
161
-
162
- # Broken Newton steps can produce a lot of warnings. Removing them for now.
163
- with numpy.errstate(all='ignore'):
164
- for _ in range(iterations):
165
- objective = _char_z(z, target_t) - target_z
166
- mask = numpy.abs(objective) >= tolerance
167
- if not numpy.any(mask):
168
- break
169
- z_m = z[mask]
170
- t_m = target_t[mask]
171
-
172
- # Perform finite difference approximation
173
- dfdz = _char_z(z_m+tolerance, t_m) - _char_z(z_m-tolerance, t_m)
174
- dfdz /= 2*tolerance
175
- dfdz[dfdz == 0] = 1.0
176
-
177
- # Perform Newton step
178
- z[mask] = z_m - step_size * objective[mask] / dfdz
179
-
180
- return z
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