freealg 0.6.3__py3-none-any.whl → 0.7.1__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 +8 -7
- freealg/__version__.py +1 -1
- freealg/_algebraic_form/__init__.py +11 -0
- freealg/_algebraic_form/_continuation_algebraic.py +503 -0
- freealg/_algebraic_form/_decompress.py +648 -0
- freealg/_algebraic_form/_edge.py +352 -0
- freealg/_algebraic_form/_sheets_util.py +145 -0
- freealg/_algebraic_form/algebraic_form.py +987 -0
- freealg/_freeform/__init__.py +16 -0
- freealg/_freeform/_density_util.py +243 -0
- freealg/{_linalg.py → _freeform/_linalg.py} +1 -1
- freealg/{freeform.py → _freeform/freeform.py} +2 -1
- freealg/_geometric_form/__init__.py +13 -0
- freealg/_geometric_form/_continuation_genus0.py +175 -0
- freealg/_geometric_form/_continuation_genus1.py +275 -0
- freealg/_geometric_form/_elliptic_functions.py +174 -0
- freealg/_geometric_form/_sphere_maps.py +63 -0
- freealg/_geometric_form/_torus_maps.py +118 -0
- freealg/_geometric_form/geometric_form.py +1094 -0
- freealg/_util.py +1 -228
- freealg/distributions/__init__.py +5 -1
- freealg/distributions/_chiral_block.py +440 -0
- freealg/distributions/_deformed_marchenko_pastur.py +617 -0
- freealg/distributions/_deformed_wigner.py +312 -0
- freealg/distributions/_kesten_mckay.py +2 -2
- freealg/distributions/_marchenko_pastur.py +199 -82
- freealg/distributions/_meixner.py +2 -2
- freealg/distributions/_wachter.py +2 -2
- freealg/distributions/_wigner.py +2 -2
- freealg/visualization/__init__.py +12 -0
- freealg/visualization/_glue_util.py +32 -0
- freealg/visualization/_rgb_hsv.py +125 -0
- {freealg-0.6.3.dist-info → freealg-0.7.1.dist-info}/METADATA +1 -1
- freealg-0.7.1.dist-info/RECORD +47 -0
- freealg-0.6.3.dist-info/RECORD +0 -26
- /freealg/{_chebyshev.py → _freeform/_chebyshev.py} +0 -0
- /freealg/{_damp.py → _freeform/_damp.py} +0 -0
- /freealg/{_decompress.py → _freeform/_decompress.py} +0 -0
- /freealg/{_jacobi.py → _freeform/_jacobi.py} +0 -0
- /freealg/{_pade.py → _freeform/_pade.py} +0 -0
- /freealg/{_plot_util.py → _freeform/_plot_util.py} +0 -0
- /freealg/{_sample.py → _freeform/_sample.py} +0 -0
- /freealg/{_series.py → _freeform/_series.py} +0 -0
- /freealg/{_support.py → _freeform/_support.py} +0 -0
- {freealg-0.6.3.dist-info → freealg-0.7.1.dist-info}/WHEEL +0 -0
- {freealg-0.6.3.dist-info → freealg-0.7.1.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.6.3.dist-info → freealg-0.7.1.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.6.3.dist-info → freealg-0.7.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright 2026, Siavash Ameli <sameli@berkeley.edu>
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
+
# SPDX-FileType: SOURCE
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
# the terms of the license found in the LICENSE.txt file in the root directory
|
|
7
|
+
# of this source tree.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# =======
|
|
11
|
+
# Imports
|
|
12
|
+
# =======
|
|
13
|
+
|
|
14
|
+
import numpy
|
|
15
|
+
from .._util import resolve_complex_dtype
|
|
16
|
+
# from .._util import compute_eig
|
|
17
|
+
from ._continuation_algebraic import sample_z_joukowski, \
|
|
18
|
+
filter_z_away_from_cuts, fit_polynomial_relation, eval_P
|
|
19
|
+
from ._edge import evolve_edges, merge_edges
|
|
20
|
+
from ._decompress import decompress_newton
|
|
21
|
+
|
|
22
|
+
# Fallback to previous numpy API
|
|
23
|
+
if not hasattr(numpy, 'trapezoid'):
|
|
24
|
+
numpy.trapezoid = numpy.trapz
|
|
25
|
+
|
|
26
|
+
__all__ = ['AlgebraicForm']
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ==============
|
|
30
|
+
# Algebraic Form
|
|
31
|
+
# ==============
|
|
32
|
+
|
|
33
|
+
class AlgebraicForm(object):
|
|
34
|
+
"""
|
|
35
|
+
Algebraic surrogate for ensemble models.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
|
|
40
|
+
A : numpy.ndarray
|
|
41
|
+
The 2D symmetric :math:`\\mathbf{A}`. The eigenvalues of this will be
|
|
42
|
+
computed upon calling this class. If a 1D array provided, it is
|
|
43
|
+
assumed to be the eigenvalues of :math:`\\mathbf{A}`.
|
|
44
|
+
|
|
45
|
+
support : tuple, default=None
|
|
46
|
+
The support of the density of :math:`\\mathbf{A}`. If `None`, it is
|
|
47
|
+
estimated from the minimum and maximum of the eigenvalues.
|
|
48
|
+
|
|
49
|
+
delta: float, default=1e-6
|
|
50
|
+
Size of perturbations into the upper half plane for Plemelj's
|
|
51
|
+
formula.
|
|
52
|
+
|
|
53
|
+
dtype : {``'complex128'``, ``'complex256'``}, default = ``'complex128'``
|
|
54
|
+
Data type for inner computations of complex variables:
|
|
55
|
+
|
|
56
|
+
* ``'complex128'``: 128-bit complex numbers, equivalent of two double
|
|
57
|
+
precision floating point.
|
|
58
|
+
* ``'complex256'``: 256-bit complex numbers, equivalent of two long
|
|
59
|
+
double precision floating point. This optino is only available on
|
|
60
|
+
Linux machines.
|
|
61
|
+
|
|
62
|
+
When using series acceleration methods (such as setting
|
|
63
|
+
``continuation`` in :func:`fit` function to ``wynn-eps``), setting a
|
|
64
|
+
higher precision floating point arithmetics might improve conference.
|
|
65
|
+
|
|
66
|
+
**kwargs : dict, optional
|
|
67
|
+
Parameters for the :func:`supp` function can also be prescribed
|
|
68
|
+
here when ``support=None``.
|
|
69
|
+
|
|
70
|
+
Attributes
|
|
71
|
+
----------
|
|
72
|
+
|
|
73
|
+
eig : numpy.array
|
|
74
|
+
Eigenvalues of the matrix
|
|
75
|
+
|
|
76
|
+
support: tuple
|
|
77
|
+
The predicted (or given) support :math:`(\\lambda_{\\min},
|
|
78
|
+
\\lambda_{\\max})` of the eigenvalue density.
|
|
79
|
+
|
|
80
|
+
n : int
|
|
81
|
+
Initial array size (assuming a square matrix when :math:`\\mathbf{A}` is
|
|
82
|
+
2D).
|
|
83
|
+
|
|
84
|
+
Methods
|
|
85
|
+
-------
|
|
86
|
+
|
|
87
|
+
fit
|
|
88
|
+
Fit the Jacobi polynomials to the empirical density.
|
|
89
|
+
|
|
90
|
+
density
|
|
91
|
+
Compute the spectral density of the matrix.
|
|
92
|
+
|
|
93
|
+
hilbert
|
|
94
|
+
Compute Hilbert transform of the spectral density
|
|
95
|
+
|
|
96
|
+
stieltjes
|
|
97
|
+
Compute Stieltjes transform of the spectral density
|
|
98
|
+
|
|
99
|
+
decompress
|
|
100
|
+
Free decompression of spectral density
|
|
101
|
+
|
|
102
|
+
eigvalsh
|
|
103
|
+
Estimate the eigenvalues
|
|
104
|
+
|
|
105
|
+
cond
|
|
106
|
+
Estimate the condition number
|
|
107
|
+
|
|
108
|
+
trace
|
|
109
|
+
Estimate the trace of a matrix power
|
|
110
|
+
|
|
111
|
+
slogdet
|
|
112
|
+
Estimate the sign and logarithm of the determinant
|
|
113
|
+
|
|
114
|
+
norm
|
|
115
|
+
Estimate the Schatten norm
|
|
116
|
+
|
|
117
|
+
Examples
|
|
118
|
+
--------
|
|
119
|
+
|
|
120
|
+
.. code-block:: python
|
|
121
|
+
|
|
122
|
+
>>> from freealg import FreeForm
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
# ====
|
|
126
|
+
# init
|
|
127
|
+
# ====
|
|
128
|
+
|
|
129
|
+
# def __init__(self, A, support=None, delta=1e-6, dtype='complex128',
|
|
130
|
+
# **kwargs):
|
|
131
|
+
|
|
132
|
+
def __init__(self, stieltjes, support=None, delta=1e-5, dtype='complex128',
|
|
133
|
+
**kwargs):
|
|
134
|
+
"""
|
|
135
|
+
Initialization.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
# self.A = None
|
|
139
|
+
# self.eig = None
|
|
140
|
+
self.stieltjes = stieltjes
|
|
141
|
+
self.support = support
|
|
142
|
+
self.delta = delta # Offset above real axis to apply Plemelj formula
|
|
143
|
+
|
|
144
|
+
# Data type for complex arrays
|
|
145
|
+
self.dtype = resolve_complex_dtype(dtype)
|
|
146
|
+
|
|
147
|
+
# # Eigenvalues
|
|
148
|
+
# if A.ndim == 1:
|
|
149
|
+
# # When A is a 1D array, it is assumed A is the eigenvalue array.
|
|
150
|
+
# self.eig = A
|
|
151
|
+
# self.n = len(A)
|
|
152
|
+
# elif A.ndim == 2:
|
|
153
|
+
# # When A is a 2D array, it is assumed A is the actual array,
|
|
154
|
+
# # and its eigenvalues will be computed.
|
|
155
|
+
# self.A = A
|
|
156
|
+
# self.n = A.shape[0]
|
|
157
|
+
# assert A.shape[0] == A.shape[1], \
|
|
158
|
+
# 'Only square matrices are permitted.'
|
|
159
|
+
# self.eig = compute_eig(A)
|
|
160
|
+
|
|
161
|
+
# Support
|
|
162
|
+
# if support is None:
|
|
163
|
+
# # Detect support
|
|
164
|
+
# self.lam_m, self.lam_p = supp(self.eig, **kwargs)
|
|
165
|
+
# else:
|
|
166
|
+
# self.lam_m = float(support[0])
|
|
167
|
+
# self.lam_p = float(support[1])
|
|
168
|
+
# self.support = (self.lam_m, self.lam_p)
|
|
169
|
+
|
|
170
|
+
# Initialize
|
|
171
|
+
# self.method = None # fitting rho: jacobi, chebyshev
|
|
172
|
+
self.a_coeffs = None # Polynomial coefficients
|
|
173
|
+
self.cache = {} # Cache inner-computations
|
|
174
|
+
|
|
175
|
+
# ===
|
|
176
|
+
# fit
|
|
177
|
+
# ===
|
|
178
|
+
|
|
179
|
+
def fit(self, deg_m, deg_z, reg=0.0,
|
|
180
|
+
r=[1.25, 6.0, 20.0],
|
|
181
|
+
n_r=[3, 2, 1],
|
|
182
|
+
n_samples=4096,
|
|
183
|
+
y_eps=2e-2,
|
|
184
|
+
x_pad=0.0,
|
|
185
|
+
triangular=None,
|
|
186
|
+
verbose=False):
|
|
187
|
+
"""
|
|
188
|
+
Fits polynomial.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
# Very important: reset cache whenever this function is called. This
|
|
192
|
+
# also empties all references holdign a cache copy.
|
|
193
|
+
# self.cache.clear()
|
|
194
|
+
|
|
195
|
+
# return self.a_coeffs
|
|
196
|
+
|
|
197
|
+
z_fits = []
|
|
198
|
+
for sup in self.support:
|
|
199
|
+
a, b = sup
|
|
200
|
+
|
|
201
|
+
for i in range(len(r)):
|
|
202
|
+
z_fits.append(sample_z_joukowski(a, b, n_samples=n_samples,
|
|
203
|
+
r=r[i], n_r=n_r[i]))
|
|
204
|
+
|
|
205
|
+
z_fit = numpy.concatenate(z_fits)
|
|
206
|
+
|
|
207
|
+
# Remove points too close to ANY cut
|
|
208
|
+
z_fit = filter_z_away_from_cuts(z_fit, self.support, y_eps=y_eps,
|
|
209
|
+
x_pad=x_pad)
|
|
210
|
+
|
|
211
|
+
# ---------
|
|
212
|
+
|
|
213
|
+
m1_fit = self.stieltjes(z_fit)
|
|
214
|
+
a_coeffs = fit_polynomial_relation(z_fit, m1_fit, s=deg_m, deg_z=deg_z,
|
|
215
|
+
ridge_lambda=reg,
|
|
216
|
+
triangular=triangular)
|
|
217
|
+
|
|
218
|
+
self.a_coeffs = a_coeffs
|
|
219
|
+
|
|
220
|
+
if verbose:
|
|
221
|
+
P_res = numpy.abs(eval_P(z_fit, m1_fit, a_coeffs))
|
|
222
|
+
print("fit residual max:", numpy.max(P_res[numpy.isfinite(P_res)]))
|
|
223
|
+
print("fit residual 99.9%:",
|
|
224
|
+
numpy.quantile(P_res[numpy.isfinite(P_res)], 0.999))
|
|
225
|
+
|
|
226
|
+
print('\nCoefficinets')
|
|
227
|
+
with numpy.printoptions(precision=4, suppress=True):
|
|
228
|
+
for i in range(a_coeffs.shape[0]):
|
|
229
|
+
for j in range(a_coeffs.shape[1]):
|
|
230
|
+
v = a_coeffs[i, j]
|
|
231
|
+
print(f"{v.real:>+0.4f}{v.imag:>+0.4f}j", end=" ")
|
|
232
|
+
print('')
|
|
233
|
+
|
|
234
|
+
print('\nCoefficients mangitudes')
|
|
235
|
+
with numpy.printoptions(precision=6, suppress=True):
|
|
236
|
+
print(numpy.abs(a_coeffs))
|
|
237
|
+
|
|
238
|
+
return a_coeffs
|
|
239
|
+
|
|
240
|
+
# =============
|
|
241
|
+
# generate grid
|
|
242
|
+
# =============
|
|
243
|
+
|
|
244
|
+
# def _generate_grid(self, scale, extend=1.0, N=500):
|
|
245
|
+
# """
|
|
246
|
+
# Generate a grid of points to evaluate density / Hilbert / Stieltjes
|
|
247
|
+
# transforms.
|
|
248
|
+
# """
|
|
249
|
+
#
|
|
250
|
+
# radius = 0.5 * (self.lam_p - self.lam_m)
|
|
251
|
+
# center = 0.5 * (self.lam_p + self.lam_m)
|
|
252
|
+
#
|
|
253
|
+
# x_min = numpy.floor(extend * (center - extend * radius * scale))
|
|
254
|
+
# x_max = numpy.ceil(extend * (center + extend * radius * scale))
|
|
255
|
+
#
|
|
256
|
+
# x_min /= extend
|
|
257
|
+
# x_max /= extend
|
|
258
|
+
#
|
|
259
|
+
# return numpy.linspace(x_min, x_max, N)
|
|
260
|
+
|
|
261
|
+
# =======
|
|
262
|
+
# density
|
|
263
|
+
# =======
|
|
264
|
+
|
|
265
|
+
def density(self, x=None, plot=False, latex=False, save=False):
|
|
266
|
+
"""
|
|
267
|
+
Evaluate spectral density.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
|
|
272
|
+
x : numpy.array, default=None
|
|
273
|
+
Positions where density to be evaluated at. If `None`, an interval
|
|
274
|
+
slightly larger than the support interval will be used.
|
|
275
|
+
|
|
276
|
+
plot : bool, default=False
|
|
277
|
+
If `True`, density is plotted.
|
|
278
|
+
|
|
279
|
+
latex : bool, default=False
|
|
280
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
281
|
+
relevant only if ``plot=True``.
|
|
282
|
+
|
|
283
|
+
save : bool, default=False
|
|
284
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
285
|
+
assumed to the save filename (with the file extension). This option
|
|
286
|
+
is relevant only if ``plot=True``.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
|
|
291
|
+
rho : numpy.array
|
|
292
|
+
Density at locations x.
|
|
293
|
+
|
|
294
|
+
See Also
|
|
295
|
+
--------
|
|
296
|
+
hilbert
|
|
297
|
+
stieltjes
|
|
298
|
+
|
|
299
|
+
Examples
|
|
300
|
+
--------
|
|
301
|
+
|
|
302
|
+
.. code-block:: python
|
|
303
|
+
|
|
304
|
+
>>> from freealg import FreeForm
|
|
305
|
+
"""
|
|
306
|
+
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
if self.a_coeffs is None:
|
|
310
|
+
raise RuntimeError('The model needs to be fit using the .fit() ' +
|
|
311
|
+
'function.')
|
|
312
|
+
|
|
313
|
+
# # Create x if not given
|
|
314
|
+
# if x is None:
|
|
315
|
+
# x = self._generate_grid(1.25)
|
|
316
|
+
#
|
|
317
|
+
# # Preallocate density to zero
|
|
318
|
+
# rho = numpy.zeros_like(x)
|
|
319
|
+
#
|
|
320
|
+
# # Compute density only inside support
|
|
321
|
+
# mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
|
|
322
|
+
#
|
|
323
|
+
# if self.method == 'jacobi':
|
|
324
|
+
# rho[mask] = jacobi_density(x[mask], self.psi, self.support,
|
|
325
|
+
# self.alpha, self.beta)
|
|
326
|
+
# elif self.method == 'chebyshev':
|
|
327
|
+
# rho[mask] = chebyshev_density(x[mask], self.psi, self.support)
|
|
328
|
+
# else:
|
|
329
|
+
# raise RuntimeError('"method" is invalid.')
|
|
330
|
+
#
|
|
331
|
+
# # Check density is unit mass
|
|
332
|
+
# mass = numpy.trapezoid(rho, x)
|
|
333
|
+
# if not numpy.isclose(mass, 1.0, atol=1e-2):
|
|
334
|
+
# print(f'"rho" is not unit mass. mass: {mass:>0.3f}. Set ' +
|
|
335
|
+
# r'"force=True".')
|
|
336
|
+
#
|
|
337
|
+
# # Check density is positive
|
|
338
|
+
# min_rho = numpy.min(rho)
|
|
339
|
+
# if min_rho < 0.0 - 1e-3:
|
|
340
|
+
# print(f'"rho" is not positive. min_rho: {min_rho:>0.3f}. Set ' +
|
|
341
|
+
# r'"force=True".')
|
|
342
|
+
#
|
|
343
|
+
# if plot:
|
|
344
|
+
# plot_density(x, rho, eig=self.eig, support=self.support,
|
|
345
|
+
# label='Estimate', latex=latex, save=save)
|
|
346
|
+
#
|
|
347
|
+
# return rho
|
|
348
|
+
|
|
349
|
+
# =======
|
|
350
|
+
# hilbert
|
|
351
|
+
# =======
|
|
352
|
+
|
|
353
|
+
def hilbert(self, x=None, rho=None, plot=False, latex=False, save=False):
|
|
354
|
+
"""
|
|
355
|
+
Compute Hilbert transform of the spectral density.
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
|
|
360
|
+
x : numpy.array, default=None
|
|
361
|
+
The locations where Hilbert transform is evaluated at. If `None`,
|
|
362
|
+
an interval slightly larger than the support interval of the
|
|
363
|
+
spectral density is used.
|
|
364
|
+
|
|
365
|
+
rho : numpy.array, default=None
|
|
366
|
+
Density. If `None`, it will be computed.
|
|
367
|
+
|
|
368
|
+
plot : bool, default=False
|
|
369
|
+
If `True`, density is plotted.
|
|
370
|
+
|
|
371
|
+
latex : bool, default=False
|
|
372
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
373
|
+
relevant only if ``plot=True``.
|
|
374
|
+
|
|
375
|
+
save : bool, default=False
|
|
376
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
377
|
+
assumed to the save filename (with the file extension). This option
|
|
378
|
+
is relevant only if ``plot=True``.
|
|
379
|
+
|
|
380
|
+
Returns
|
|
381
|
+
-------
|
|
382
|
+
|
|
383
|
+
hilb : numpy.array
|
|
384
|
+
The Hilbert transform on the locations `x`.
|
|
385
|
+
|
|
386
|
+
See Also
|
|
387
|
+
--------
|
|
388
|
+
density
|
|
389
|
+
stieltjes
|
|
390
|
+
|
|
391
|
+
Examples
|
|
392
|
+
--------
|
|
393
|
+
|
|
394
|
+
.. code-block:: python
|
|
395
|
+
|
|
396
|
+
>>> from freealg import FreeForm
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
pass
|
|
400
|
+
|
|
401
|
+
if self.a_coeffs is None:
|
|
402
|
+
raise RuntimeError('The model needs to be fit using the .fit() ' +
|
|
403
|
+
'function.')
|
|
404
|
+
|
|
405
|
+
# # Create x if not given
|
|
406
|
+
# if x is None:
|
|
407
|
+
# x = self._generate_grid(1.25)
|
|
408
|
+
#
|
|
409
|
+
# # if (numpy.min(x) > self.lam_m) or (numpy.max(x) < self.lam_p):
|
|
410
|
+
# # raise ValueError('"x" does not encompass support interval.')
|
|
411
|
+
#
|
|
412
|
+
# # Preallocate density to zero
|
|
413
|
+
# if rho is None:
|
|
414
|
+
# rho = self.density(x)
|
|
415
|
+
#
|
|
416
|
+
# # mask of support [lam_m, lam_p]
|
|
417
|
+
# mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
|
|
418
|
+
# x_s = x[mask]
|
|
419
|
+
# rho_s = rho[mask]
|
|
420
|
+
#
|
|
421
|
+
# # Form the matrix of integrands: rho_s / (t - x_i)
|
|
422
|
+
# # Here, we have diff[i,j] = x[i] - x_s[j]
|
|
423
|
+
# diff = x[:, None] - x_s[None, :]
|
|
424
|
+
# D = rho_s[None, :] / diff
|
|
425
|
+
#
|
|
426
|
+
# # Principal-value: wherever t == x_i, then diff == 0, zero that entry
|
|
427
|
+
# # (numpy.isclose handles floating-point exactly)
|
|
428
|
+
# D[numpy.isclose(diff, 0.0)] = 0.0
|
|
429
|
+
#
|
|
430
|
+
# # Integrate each row over t using trapezoid rule on x_s
|
|
431
|
+
# # Namely, hilb[i] = int rho_s(t)/(t - x[i]) dt
|
|
432
|
+
# hilb = numpy.trapezoid(D, x_s, axis=1) / numpy.pi
|
|
433
|
+
#
|
|
434
|
+
# # We use negative sign convention
|
|
435
|
+
# hilb = -hilb
|
|
436
|
+
#
|
|
437
|
+
# if plot:
|
|
438
|
+
# plot_hilbert(x, hilb, support=self.support, latex=latex,
|
|
439
|
+
# save=save)
|
|
440
|
+
#
|
|
441
|
+
# return hilb
|
|
442
|
+
|
|
443
|
+
# =========
|
|
444
|
+
# stieltjes
|
|
445
|
+
# =========
|
|
446
|
+
|
|
447
|
+
def stieltjes(self, x=None, y=None, plot=False, latex=False, save=False):
|
|
448
|
+
"""
|
|
449
|
+
Compute Stieltjes transform of the spectral density on a grid.
|
|
450
|
+
|
|
451
|
+
This function evaluates Stieltjes transform on an array of points, or
|
|
452
|
+
over a 2D Cartesian grid on the complex plane.
|
|
453
|
+
|
|
454
|
+
Parameters
|
|
455
|
+
----------
|
|
456
|
+
|
|
457
|
+
x : numpy.array, default=None
|
|
458
|
+
The x axis of the grid where the Stieltjes transform is evaluated.
|
|
459
|
+
If `None`, an interval slightly larger than the support interval of
|
|
460
|
+
the spectral density is used.
|
|
461
|
+
|
|
462
|
+
y : numpy.array, default=None
|
|
463
|
+
The y axis of the grid where the Stieltjes transform is evaluated.
|
|
464
|
+
If `None`, a grid on the interval ``[-1, 1]`` is used.
|
|
465
|
+
|
|
466
|
+
plot : bool, default=False
|
|
467
|
+
If `True`, density is plotted.
|
|
468
|
+
|
|
469
|
+
latex : bool, default=False
|
|
470
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
471
|
+
relevant only if ``plot=True``.
|
|
472
|
+
|
|
473
|
+
save : bool, default=False
|
|
474
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
475
|
+
assumed to the save filename (with the file extension). This option
|
|
476
|
+
is relevant only if ``plot=True``.
|
|
477
|
+
|
|
478
|
+
Returns
|
|
479
|
+
-------
|
|
480
|
+
|
|
481
|
+
m_p : numpy.ndarray
|
|
482
|
+
The Stieltjes transform on the principal branch.
|
|
483
|
+
|
|
484
|
+
m_m : numpy.ndarray
|
|
485
|
+
The Stieltjes transform continued to the secondary branch.
|
|
486
|
+
|
|
487
|
+
See Also
|
|
488
|
+
--------
|
|
489
|
+
|
|
490
|
+
density
|
|
491
|
+
hilbert
|
|
492
|
+
|
|
493
|
+
Examples
|
|
494
|
+
--------
|
|
495
|
+
|
|
496
|
+
.. code-block:: python
|
|
497
|
+
|
|
498
|
+
>>> from freealg import FreeForm
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
pass
|
|
502
|
+
|
|
503
|
+
if self.a_coeffs is None:
|
|
504
|
+
raise RuntimeError('The model needs to be fit using the .fit() ' +
|
|
505
|
+
'function.')
|
|
506
|
+
|
|
507
|
+
# # Create x if not given
|
|
508
|
+
# if x is None:
|
|
509
|
+
# x = self._generate_grid(2.0, extend=2.0)
|
|
510
|
+
#
|
|
511
|
+
# # Create y if not given
|
|
512
|
+
# if (plot is False) and (y is None):
|
|
513
|
+
# # Do not use a Cartesian grid. Create a 1D array z slightly above
|
|
514
|
+
# # the real line.
|
|
515
|
+
# y = self.delta * 1j
|
|
516
|
+
# z = x.astype(complex) + y # shape (Nx,)
|
|
517
|
+
# else:
|
|
518
|
+
# # Use a Cartesian grid
|
|
519
|
+
# if y is None:
|
|
520
|
+
# y = numpy.linspace(-1, 1, 400)
|
|
521
|
+
# x_grid, y_grid = numpy.meshgrid(x.real, y.real)
|
|
522
|
+
# z = x_grid + 1j * y_grid # shape (Ny, Nx)
|
|
523
|
+
#
|
|
524
|
+
# m1, m2 = self._eval_stieltjes(z, branches=True)
|
|
525
|
+
#
|
|
526
|
+
# if plot:
|
|
527
|
+
# plot_stieltjes(x, y, m1, m2, self.support, latex=latex,
|
|
528
|
+
# save=save)
|
|
529
|
+
#
|
|
530
|
+
# return m1, m2
|
|
531
|
+
|
|
532
|
+
# ==============
|
|
533
|
+
# eval stieltjes
|
|
534
|
+
# ==============
|
|
535
|
+
|
|
536
|
+
def _eval_stieltjes(self, z, branches=False):
|
|
537
|
+
"""
|
|
538
|
+
Compute Stieltjes transform of the spectral density.
|
|
539
|
+
|
|
540
|
+
Parameters
|
|
541
|
+
----------
|
|
542
|
+
|
|
543
|
+
z : numpy.array
|
|
544
|
+
The z values in the complex plan where the Stieltjes transform is
|
|
545
|
+
evaluated.
|
|
546
|
+
|
|
547
|
+
branches : bool, default = False
|
|
548
|
+
Return both the principal and secondary branches of the Stieltjes
|
|
549
|
+
transform. The default ``branches=False`` will return only
|
|
550
|
+
the secondary branch.
|
|
551
|
+
|
|
552
|
+
Returns
|
|
553
|
+
-------
|
|
554
|
+
|
|
555
|
+
m_p : numpy.ndarray
|
|
556
|
+
The Stieltjes transform on the principal branch if
|
|
557
|
+
``branches=True``.
|
|
558
|
+
|
|
559
|
+
m_m : numpy.ndarray
|
|
560
|
+
The Stieltjes transform continued to the secondary branch.
|
|
561
|
+
"""
|
|
562
|
+
|
|
563
|
+
pass
|
|
564
|
+
|
|
565
|
+
# ==========
|
|
566
|
+
# decompress
|
|
567
|
+
# ==========
|
|
568
|
+
|
|
569
|
+
def decompress(self, x, t,
|
|
570
|
+
max_iter=50,
|
|
571
|
+
tol=1e-12,
|
|
572
|
+
armijo=1e-4,
|
|
573
|
+
min_lam=1e-6,
|
|
574
|
+
w_min=1e-14,
|
|
575
|
+
sweep=True,
|
|
576
|
+
verbose=False):
|
|
577
|
+
"""
|
|
578
|
+
Free decompression of spectral density.
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
# Check size argument
|
|
582
|
+
# if numpy.isscalar(size):
|
|
583
|
+
# size = int(size)
|
|
584
|
+
# else:
|
|
585
|
+
# # Check monotonic increment (either all increasing or decreasing)
|
|
586
|
+
# diff = numpy.diff(size)
|
|
587
|
+
# if not (numpy.all(diff >= 0) or numpy.all(diff <= 0)):
|
|
588
|
+
# raise ValueError('"size" increment should be monotonic.')
|
|
589
|
+
|
|
590
|
+
# Decompression ratio equal to e^{t}.
|
|
591
|
+
# alpha = numpy.atleast_1d(size) / self.n
|
|
592
|
+
|
|
593
|
+
# # If the input size was only a scalar, return a 1D rho, otherwise 2D.
|
|
594
|
+
# if numpy.isscalar(size):
|
|
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
|
|
607
|
+
|
|
608
|
+
# Query grid on the real axis + a small imaginary buffer
|
|
609
|
+
z_query = x + 1j * self.delta
|
|
610
|
+
|
|
611
|
+
# Initial condition at t=0 (physical branch)
|
|
612
|
+
w0_list = self.stieltjes(z_query)
|
|
613
|
+
|
|
614
|
+
# Evolve
|
|
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)
|
|
624
|
+
|
|
625
|
+
rho = W.imag / numpy.pi
|
|
626
|
+
|
|
627
|
+
if verbose:
|
|
628
|
+
print("success rate per t:", ok.mean(axis=1))
|
|
629
|
+
|
|
630
|
+
return rho
|
|
631
|
+
|
|
632
|
+
# ====
|
|
633
|
+
# edge
|
|
634
|
+
# ====
|
|
635
|
+
|
|
636
|
+
def edge(self, t, eta=1e-3, dt_max=0.1, max_iter=30, tol=1e-12,
|
|
637
|
+
verbose=False):
|
|
638
|
+
"""
|
|
639
|
+
Evolves spectral edges.
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
edges, ok_edges = evolve_edges(t, self.a_coeffs, support=self.support,
|
|
643
|
+
eta=eta, dt_max=dt_max,
|
|
644
|
+
max_iter=max_iter, tol=tol)
|
|
645
|
+
|
|
646
|
+
edges2, active_k = merge_edges(edges, tol=1e-4)
|
|
647
|
+
|
|
648
|
+
if verbose:
|
|
649
|
+
print("edge success rate:", ok_edges.mean())
|
|
650
|
+
|
|
651
|
+
return edges2, active_k
|
|
652
|
+
|
|
653
|
+
# ========
|
|
654
|
+
# eigvalsh
|
|
655
|
+
# ========
|
|
656
|
+
|
|
657
|
+
def eigvalsh(self, size=None, seed=None, **kwargs):
|
|
658
|
+
"""
|
|
659
|
+
Estimate the eigenvalues.
|
|
660
|
+
|
|
661
|
+
This function estimates the eigenvalues of the freeform matrix
|
|
662
|
+
or a larger matrix containing it using free decompression.
|
|
663
|
+
|
|
664
|
+
Parameters
|
|
665
|
+
----------
|
|
666
|
+
|
|
667
|
+
size : int, default=None
|
|
668
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
669
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
670
|
+
:math:`\\mathbf{A}` itself.
|
|
671
|
+
|
|
672
|
+
seed : int, default=None
|
|
673
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
674
|
+
|
|
675
|
+
**kwargs : dict, optional
|
|
676
|
+
Pass additional options to the underlying
|
|
677
|
+
:func:`FreeForm.decompress` function.
|
|
678
|
+
|
|
679
|
+
Returns
|
|
680
|
+
-------
|
|
681
|
+
|
|
682
|
+
eigs : numpy.array
|
|
683
|
+
Eigenvalues of decompressed matrix
|
|
684
|
+
|
|
685
|
+
See Also
|
|
686
|
+
--------
|
|
687
|
+
|
|
688
|
+
FreeForm.decompress
|
|
689
|
+
FreeForm.cond
|
|
690
|
+
|
|
691
|
+
Notes
|
|
692
|
+
-----
|
|
693
|
+
|
|
694
|
+
All arguments to the `.decompress()` procedure can be provided.
|
|
695
|
+
|
|
696
|
+
Examples
|
|
697
|
+
--------
|
|
698
|
+
|
|
699
|
+
.. code-block:: python
|
|
700
|
+
:emphasize-lines: 1
|
|
701
|
+
|
|
702
|
+
>>> from freealg import FreeForm
|
|
703
|
+
"""
|
|
704
|
+
|
|
705
|
+
# if size is None:
|
|
706
|
+
# size = self.n
|
|
707
|
+
#
|
|
708
|
+
# rho, x = self.decompress(size, **kwargs)
|
|
709
|
+
# eigs = numpy.sort(sample(x, rho, size, method='qmc', seed=seed))
|
|
710
|
+
#
|
|
711
|
+
# return eigs
|
|
712
|
+
pass
|
|
713
|
+
|
|
714
|
+
# ====
|
|
715
|
+
# cond
|
|
716
|
+
# ====
|
|
717
|
+
|
|
718
|
+
def cond(self, size=None, seed=None, **kwargs):
|
|
719
|
+
"""
|
|
720
|
+
Estimate the condition number.
|
|
721
|
+
|
|
722
|
+
This function estimates the condition number of the matrix
|
|
723
|
+
:math:`\\mathbf{A}` or a larger matrix containing :math:`\\mathbf{A}`
|
|
724
|
+
using free decompression.
|
|
725
|
+
|
|
726
|
+
Parameters
|
|
727
|
+
----------
|
|
728
|
+
|
|
729
|
+
size : int, default=None
|
|
730
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
731
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
732
|
+
:math:`\\mathbf{A}` itself.
|
|
733
|
+
|
|
734
|
+
**kwargs : dict, optional
|
|
735
|
+
Pass additional options to the underlying
|
|
736
|
+
:func:`FreeForm.decompress` function.
|
|
737
|
+
|
|
738
|
+
Returns
|
|
739
|
+
-------
|
|
740
|
+
|
|
741
|
+
c : float
|
|
742
|
+
Condition number
|
|
743
|
+
|
|
744
|
+
See Also
|
|
745
|
+
--------
|
|
746
|
+
|
|
747
|
+
FreeForm.eigvalsh
|
|
748
|
+
FreeForm.norm
|
|
749
|
+
FreeForm.slogdet
|
|
750
|
+
FreeForm.trace
|
|
751
|
+
|
|
752
|
+
Examples
|
|
753
|
+
--------
|
|
754
|
+
|
|
755
|
+
.. code-block:: python
|
|
756
|
+
:emphasize-lines: 1
|
|
757
|
+
|
|
758
|
+
>>> from freealg import FreeForm
|
|
759
|
+
"""
|
|
760
|
+
|
|
761
|
+
eigs = self.eigvalsh(size=size, **kwargs)
|
|
762
|
+
return eigs.max() / eigs.min()
|
|
763
|
+
|
|
764
|
+
# =====
|
|
765
|
+
# trace
|
|
766
|
+
# =====
|
|
767
|
+
|
|
768
|
+
def trace(self, size=None, p=1.0, seed=None, **kwargs):
|
|
769
|
+
"""
|
|
770
|
+
Estimate the trace of a power.
|
|
771
|
+
|
|
772
|
+
This function estimates the trace of the matrix power
|
|
773
|
+
:math:`\\mathbf{A}^p` of the freeform or that of a larger matrix
|
|
774
|
+
containing it.
|
|
775
|
+
|
|
776
|
+
Parameters
|
|
777
|
+
----------
|
|
778
|
+
|
|
779
|
+
size : int, default=None
|
|
780
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
781
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
782
|
+
:math:`\\mathbf{A}` itself.
|
|
783
|
+
|
|
784
|
+
p : float, default=1.0
|
|
785
|
+
The exponent :math:`p` in :math:`\\mathbf{A}^p`.
|
|
786
|
+
|
|
787
|
+
seed : int, default=None
|
|
788
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
789
|
+
|
|
790
|
+
**kwargs : dict, optional
|
|
791
|
+
Pass additional options to the underlying
|
|
792
|
+
:func:`FreeForm.decompress` function.
|
|
793
|
+
|
|
794
|
+
Returns
|
|
795
|
+
-------
|
|
796
|
+
|
|
797
|
+
trace : float
|
|
798
|
+
matrix trace
|
|
799
|
+
|
|
800
|
+
See Also
|
|
801
|
+
--------
|
|
802
|
+
|
|
803
|
+
FreeForm.eigvalsh
|
|
804
|
+
FreeForm.cond
|
|
805
|
+
FreeForm.slogdet
|
|
806
|
+
FreeForm.norm
|
|
807
|
+
|
|
808
|
+
Notes
|
|
809
|
+
-----
|
|
810
|
+
|
|
811
|
+
The trace is highly amenable to subsampling: under free decompression
|
|
812
|
+
the average eigenvalue is assumed constant, so the trace increases
|
|
813
|
+
linearly. Traces of powers fall back to :func:`eigvalsh`.
|
|
814
|
+
All arguments to the `.decompress()` procedure can be provided.
|
|
815
|
+
|
|
816
|
+
Examples
|
|
817
|
+
--------
|
|
818
|
+
|
|
819
|
+
.. code-block:: python
|
|
820
|
+
:emphasize-lines: 1
|
|
821
|
+
|
|
822
|
+
>>> from freealg import FreeForm
|
|
823
|
+
"""
|
|
824
|
+
|
|
825
|
+
if numpy.isclose(p, 1.0):
|
|
826
|
+
return numpy.mean(self.eig) * (size / self.n)
|
|
827
|
+
|
|
828
|
+
eig = self.eigvalsh(size=size, seed=seed, **kwargs)
|
|
829
|
+
return numpy.sum(eig ** p)
|
|
830
|
+
|
|
831
|
+
# =======
|
|
832
|
+
# slogdet
|
|
833
|
+
# =======
|
|
834
|
+
|
|
835
|
+
def slogdet(self, size=None, seed=None, **kwargs):
|
|
836
|
+
"""
|
|
837
|
+
Estimate the sign and logarithm of the determinant.
|
|
838
|
+
|
|
839
|
+
This function estimates the *slogdet* of the freeform or that of
|
|
840
|
+
a larger matrix containing it using free decompression.
|
|
841
|
+
|
|
842
|
+
Parameters
|
|
843
|
+
----------
|
|
844
|
+
|
|
845
|
+
size : int, default=None
|
|
846
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
847
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
848
|
+
:math:`\\mathbf{A}` itself.
|
|
849
|
+
|
|
850
|
+
seed : int, default=None
|
|
851
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
852
|
+
|
|
853
|
+
Returns
|
|
854
|
+
-------
|
|
855
|
+
|
|
856
|
+
sign : float
|
|
857
|
+
Sign of determinant
|
|
858
|
+
|
|
859
|
+
ld : float
|
|
860
|
+
natural logarithm of the absolute value of the determinant
|
|
861
|
+
|
|
862
|
+
See Also
|
|
863
|
+
--------
|
|
864
|
+
|
|
865
|
+
FreeForm.eigvalsh
|
|
866
|
+
FreeForm.cond
|
|
867
|
+
FreeForm.trace
|
|
868
|
+
FreeForm.norm
|
|
869
|
+
|
|
870
|
+
Notes
|
|
871
|
+
-----
|
|
872
|
+
|
|
873
|
+
All arguments to the `.decompress()` procedure can be provided.
|
|
874
|
+
|
|
875
|
+
Examples
|
|
876
|
+
--------
|
|
877
|
+
|
|
878
|
+
.. code-block:: python
|
|
879
|
+
:emphasize-lines: 1
|
|
880
|
+
|
|
881
|
+
>>> from freealg import FreeForm
|
|
882
|
+
"""
|
|
883
|
+
|
|
884
|
+
eigs = self.eigvalsh(size=size, seed=seed, **kwargs)
|
|
885
|
+
sign = numpy.prod(numpy.sign(eigs))
|
|
886
|
+
ld = numpy.sum(numpy.log(numpy.abs(eigs)))
|
|
887
|
+
return sign, ld
|
|
888
|
+
|
|
889
|
+
# ====
|
|
890
|
+
# norm
|
|
891
|
+
# ====
|
|
892
|
+
|
|
893
|
+
def norm(self, size=None, order=2, seed=None, **kwargs):
|
|
894
|
+
"""
|
|
895
|
+
Estimate the Schatten norm.
|
|
896
|
+
|
|
897
|
+
This function estimates the norm of the freeform or a larger
|
|
898
|
+
matrix containing it using free decompression.
|
|
899
|
+
|
|
900
|
+
Parameters
|
|
901
|
+
----------
|
|
902
|
+
|
|
903
|
+
size : int, default=None
|
|
904
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
905
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
906
|
+
:math:`\\mathbf{A}` itself.
|
|
907
|
+
|
|
908
|
+
order : {float, ``''inf``, ``'-inf'``, ``'fro'``, ``'nuc'``}, default=2
|
|
909
|
+
Order of the norm.
|
|
910
|
+
|
|
911
|
+
* float :math:`p`: Schatten p-norm.
|
|
912
|
+
* ``'inf'``: Largest absolute eigenvalue
|
|
913
|
+
:math:`\\max \\vert \\lambda_i \\vert)`
|
|
914
|
+
* ``'-inf'``: Smallest absolute eigenvalue
|
|
915
|
+
:math:`\\min \\vert \\lambda_i \\vert)`
|
|
916
|
+
* ``'fro'``: Frobenius norm corresponding to :math:`p=2`
|
|
917
|
+
* ``'nuc'``: Nuclear (or trace) norm corresponding to :math:`p=1`
|
|
918
|
+
|
|
919
|
+
seed : int, default=None
|
|
920
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
921
|
+
|
|
922
|
+
**kwargs : dict, optional
|
|
923
|
+
Pass additional options to the underlying
|
|
924
|
+
:func:`FreeForm.decompress` function.
|
|
925
|
+
|
|
926
|
+
Returns
|
|
927
|
+
-------
|
|
928
|
+
|
|
929
|
+
norm : float
|
|
930
|
+
matrix norm
|
|
931
|
+
|
|
932
|
+
See Also
|
|
933
|
+
--------
|
|
934
|
+
|
|
935
|
+
FreeForm.eigvalsh
|
|
936
|
+
FreeForm.cond
|
|
937
|
+
FreeForm.slogdet
|
|
938
|
+
FreeForm.trace
|
|
939
|
+
|
|
940
|
+
Notes
|
|
941
|
+
-----
|
|
942
|
+
|
|
943
|
+
Thes Schatten :math:`p`-norm is defined by
|
|
944
|
+
|
|
945
|
+
.. math::
|
|
946
|
+
|
|
947
|
+
\\Vert \\mathbf{A} \\Vert_p = \\left(
|
|
948
|
+
\\sum_{i=1}^N \\vert \\lambda_i \\vert^p \\right)^{1/p}.
|
|
949
|
+
|
|
950
|
+
Examples
|
|
951
|
+
--------
|
|
952
|
+
|
|
953
|
+
.. code-block:: python
|
|
954
|
+
:emphasize-lines: 1
|
|
955
|
+
|
|
956
|
+
>>> from freealg import FreeForm
|
|
957
|
+
"""
|
|
958
|
+
|
|
959
|
+
eigs = self.eigvalsh(size, seed=seed, **kwargs)
|
|
960
|
+
|
|
961
|
+
# Check order type and convert to float
|
|
962
|
+
if order == 'nuc':
|
|
963
|
+
order = 1
|
|
964
|
+
elif order == 'fro':
|
|
965
|
+
order = 2
|
|
966
|
+
elif order == 'inf':
|
|
967
|
+
order = float('inf')
|
|
968
|
+
elif order == '-inf':
|
|
969
|
+
order = -float('inf')
|
|
970
|
+
elif not isinstance(order,
|
|
971
|
+
(int, float, numpy.integer, numpy.floating)) \
|
|
972
|
+
and not isinstance(order, (bool, numpy.bool_)):
|
|
973
|
+
raise ValueError('"order" is invalid.')
|
|
974
|
+
|
|
975
|
+
# Compute norm
|
|
976
|
+
if numpy.isinf(order) and not numpy.isneginf(order):
|
|
977
|
+
norm_ = max(numpy.abs(eigs))
|
|
978
|
+
|
|
979
|
+
elif numpy.isneginf(order):
|
|
980
|
+
norm_ = min(numpy.abs(eigs))
|
|
981
|
+
|
|
982
|
+
elif isinstance(order, (int, float, numpy.integer, numpy.floating)) \
|
|
983
|
+
and not isinstance(order, (bool, numpy.bool_)):
|
|
984
|
+
norm_q = numpy.sum(numpy.abs(eigs)**order)
|
|
985
|
+
norm_ = norm_q**(1.0 / order)
|
|
986
|
+
|
|
987
|
+
return norm_
|