freealg 0.3.5__py3-none-any.whl → 0.3.6__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 +2 -2
- freealg/__version__.py +1 -1
- freealg/{eigh.py → _linalg.py} +88 -49
- freealg/freeform.py +377 -27
- {freealg-0.3.5.dist-info → freealg-0.3.6.dist-info}/METADATA +4 -4
- {freealg-0.3.5.dist-info → freealg-0.3.6.dist-info}/RECORD +10 -10
- {freealg-0.3.5.dist-info → freealg-0.3.6.dist-info}/WHEEL +0 -0
- {freealg-0.3.5.dist-info → freealg-0.3.6.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.3.5.dist-info → freealg-0.3.6.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.3.5.dist-info → freealg-0.3.6.dist-info}/top_level.txt +0 -0
freealg/__init__.py
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
# directory of this source tree.
|
|
8
8
|
|
|
9
9
|
from .freeform import FreeForm
|
|
10
|
-
from .
|
|
10
|
+
from ._linalg import eigvalsh, cond, norm, trace, slogdet
|
|
11
11
|
from . import distributions
|
|
12
12
|
|
|
13
|
-
__all__ = ['FreeForm', 'distributions', '
|
|
13
|
+
__all__ = ['FreeForm', 'distributions', 'eigvalsh', 'cond', 'norm', 'trace',
|
|
14
14
|
'slogdet']
|
|
15
15
|
|
|
16
16
|
from .__version__ import __version__ # noqa: F401 E402
|
freealg/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.6"
|
freealg/{eigh.py → _linalg.py}
RENAMED
|
@@ -14,7 +14,7 @@ import numpy
|
|
|
14
14
|
from ._util import compute_eig
|
|
15
15
|
from .freeform import FreeForm
|
|
16
16
|
|
|
17
|
-
__all__ = ['
|
|
17
|
+
__all__ = ['eigvalsh', 'cond', 'norm', 'trace', 'slogdet']
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
# ===============
|
|
@@ -53,11 +53,11 @@ def _subsample_apply(f, A, output_array=False):
|
|
|
53
53
|
return numpy.array(samples), n, n_s
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
56
|
+
# ========
|
|
57
|
+
# eigvalsh
|
|
58
|
+
# ========
|
|
59
59
|
|
|
60
|
-
def
|
|
60
|
+
def eigvalsh(A, size=None, psd=None, seed=None, plot=False, **kwargs):
|
|
61
61
|
"""
|
|
62
62
|
Estimate the eigenvalues of a matrix.
|
|
63
63
|
|
|
@@ -72,7 +72,7 @@ def eigh(A, N=None, psd=None, plots=False):
|
|
|
72
72
|
(or those of a matrix containing :math:`\\mathbf{A}`) are to be
|
|
73
73
|
computed.
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
size : int, default=None
|
|
76
76
|
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
77
77
|
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
78
78
|
:math:`\\mathbf{A}` itself.
|
|
@@ -82,9 +82,16 @@ def eigh(A, N=None, psd=None, plots=False):
|
|
|
82
82
|
eigenvalues are non-negative). If `None`, the matrix is considered PSD
|
|
83
83
|
if all sampled eigenvalues are positive.
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
seed : int, default=None
|
|
86
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
87
|
+
|
|
88
|
+
plot : bool, default=False
|
|
86
89
|
Print out all relevant plots for diagnosing eigenvalue accuracy.
|
|
87
90
|
|
|
91
|
+
**kwargs : dict, optional
|
|
92
|
+
Pass additional options to the underlying
|
|
93
|
+
:func:`FreeForm.decompress` function.
|
|
94
|
+
|
|
88
95
|
Returns
|
|
89
96
|
-------
|
|
90
97
|
|
|
@@ -101,7 +108,7 @@ def eigh(A, N=None, psd=None, plots=False):
|
|
|
101
108
|
|
|
102
109
|
This is a convenience function for the :class:`freealg.FreeForm` class with
|
|
103
110
|
some effective defaults that work well for common random matrix ensembles.
|
|
104
|
-
For improved performance and plotting
|
|
111
|
+
For improved performance and plotting utilities, consider fine-tuning
|
|
105
112
|
parameters using the FreeForm class.
|
|
106
113
|
|
|
107
114
|
References
|
|
@@ -120,13 +127,13 @@ def eigh(A, N=None, psd=None, plots=False):
|
|
|
120
127
|
|
|
121
128
|
>>> mp = MarchenkoPastur(1/50)
|
|
122
129
|
>>> A = mp.matrix(3000)
|
|
123
|
-
>>> eigs =
|
|
130
|
+
>>> eigs = eigvalsh(A)
|
|
124
131
|
"""
|
|
125
132
|
|
|
126
133
|
samples, n, n_s = _subsample_apply(compute_eig, A, output_array=True)
|
|
127
134
|
|
|
128
|
-
if
|
|
129
|
-
|
|
135
|
+
if size is None:
|
|
136
|
+
size = n
|
|
130
137
|
|
|
131
138
|
# If all eigenvalues are positive, set PSD flag
|
|
132
139
|
if psd is None:
|
|
@@ -137,15 +144,15 @@ def eigh(A, N=None, psd=None, plots=False):
|
|
|
137
144
|
ff.n = n_s
|
|
138
145
|
|
|
139
146
|
# Perform fit and estimate eigenvalues
|
|
140
|
-
order = 1 + int(len(samples)
|
|
147
|
+
order = 1 + int(len(samples)**0.2)
|
|
141
148
|
ff.fit(method='chebyshev', K=order, projection='sample',
|
|
142
149
|
force=True, plot=False, latex=False, save=False)
|
|
143
150
|
|
|
144
|
-
if
|
|
151
|
+
if plot:
|
|
145
152
|
ff.density(plot=True)
|
|
146
153
|
ff.stieltjes(plot=True)
|
|
147
154
|
|
|
148
|
-
|
|
155
|
+
eigs = ff.eigvalsh(size, seed=seed, plot=plot, **kwargs)
|
|
149
156
|
|
|
150
157
|
if psd:
|
|
151
158
|
eigs = numpy.abs(eigs)
|
|
@@ -158,7 +165,7 @@ def eigh(A, N=None, psd=None, plots=False):
|
|
|
158
165
|
# cond
|
|
159
166
|
# ====
|
|
160
167
|
|
|
161
|
-
def cond(A,
|
|
168
|
+
def cond(A, size=None, seed=None, **kwargs):
|
|
162
169
|
"""
|
|
163
170
|
Estimate the condition number of a Hermitian positive-definite matrix.
|
|
164
171
|
|
|
@@ -174,11 +181,18 @@ def cond(A, N=None):
|
|
|
174
181
|
number (or that of a matrix containing :math:`\\mathbf{A}`) are to be
|
|
175
182
|
computed.
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
size : int, default=None
|
|
178
185
|
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
179
186
|
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
180
187
|
:math:`\\mathbf{A}` itself.
|
|
181
188
|
|
|
189
|
+
seed : int, default=None
|
|
190
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
191
|
+
|
|
192
|
+
**kwargs : dict, optional
|
|
193
|
+
Pass additional options to the underlying
|
|
194
|
+
:func:`FreeForm.decompress` function.
|
|
195
|
+
|
|
182
196
|
Returns
|
|
183
197
|
-------
|
|
184
198
|
|
|
@@ -188,7 +202,7 @@ def cond(A, N=None):
|
|
|
188
202
|
See Also
|
|
189
203
|
--------
|
|
190
204
|
|
|
191
|
-
|
|
205
|
+
eigvalsh
|
|
192
206
|
norm
|
|
193
207
|
slogdet
|
|
194
208
|
trace
|
|
@@ -196,7 +210,7 @@ def cond(A, N=None):
|
|
|
196
210
|
Notes
|
|
197
211
|
-----
|
|
198
212
|
|
|
199
|
-
This is a convenience function using :func:`freealg.
|
|
213
|
+
This is a convenience function using :func:`freealg.eigvalsh`.
|
|
200
214
|
|
|
201
215
|
Examples
|
|
202
216
|
--------
|
|
@@ -212,7 +226,7 @@ def cond(A, N=None):
|
|
|
212
226
|
>>> cond(A)
|
|
213
227
|
"""
|
|
214
228
|
|
|
215
|
-
eigs =
|
|
229
|
+
eigs = eigvalsh(A, size=size, psd=True, seed=seed, **kwargs)
|
|
216
230
|
return eigs.max() / eigs.min()
|
|
217
231
|
|
|
218
232
|
|
|
@@ -220,7 +234,7 @@ def cond(A, N=None):
|
|
|
220
234
|
# norm
|
|
221
235
|
# ====
|
|
222
236
|
|
|
223
|
-
def norm(A,
|
|
237
|
+
def norm(A, size=None, order=2, seed=None, **kwargs):
|
|
224
238
|
"""
|
|
225
239
|
Estimate the Schatten norm of a Hermitian matrix.
|
|
226
240
|
|
|
@@ -235,7 +249,7 @@ def norm(A, N=None, order=None):
|
|
|
235
249
|
number (or that of a matrix containing :math:`\\mathbf{A}`) are to be
|
|
236
250
|
computed.
|
|
237
251
|
|
|
238
|
-
|
|
252
|
+
size : int, default=None
|
|
239
253
|
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
240
254
|
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
241
255
|
:math:`\\mathbf{A}` itself.
|
|
@@ -251,6 +265,13 @@ def norm(A, N=None, order=None):
|
|
|
251
265
|
* ``'fro'``: Frobenius norm corresponding to :math:`p=2`
|
|
252
266
|
* ``'nuc'``: Nuclear (or trace) norm corresponding to :math:`p=1`
|
|
253
267
|
|
|
268
|
+
seed : int, default=None
|
|
269
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
270
|
+
|
|
271
|
+
**kwargs : dict, optional
|
|
272
|
+
Pass additional options to the underlying
|
|
273
|
+
:func:`FreeForm.decompress` function.
|
|
274
|
+
|
|
254
275
|
Returns
|
|
255
276
|
-------
|
|
256
277
|
|
|
@@ -260,7 +281,7 @@ def norm(A, N=None, order=None):
|
|
|
260
281
|
See Also
|
|
261
282
|
--------
|
|
262
283
|
|
|
263
|
-
|
|
284
|
+
eigvalsh
|
|
264
285
|
cond
|
|
265
286
|
slogdet
|
|
266
287
|
trace
|
|
@@ -289,29 +310,34 @@ def norm(A, N=None, order=None):
|
|
|
289
310
|
>>> norm(A, 100_000, order='fro')
|
|
290
311
|
"""
|
|
291
312
|
|
|
292
|
-
eigs =
|
|
313
|
+
eigs = eigvalsh(A, size=size, seed=seed, **kwargs)
|
|
314
|
+
|
|
315
|
+
# Check order type and convert to float
|
|
316
|
+
if order == 'nuc':
|
|
317
|
+
order = 1
|
|
318
|
+
elif order == 'fro':
|
|
319
|
+
order = 2
|
|
320
|
+
elif order == 'inf':
|
|
321
|
+
order = float('inf')
|
|
322
|
+
elif order == '-inf':
|
|
323
|
+
order = -float('inf')
|
|
324
|
+
elif not isinstance(order,
|
|
325
|
+
(int, float, numpy.integer, numpy.floating)) \
|
|
326
|
+
and not isinstance(order, (bool, numpy.bool_)):
|
|
327
|
+
raise ValueError('"order" is invalid.')
|
|
293
328
|
|
|
294
|
-
|
|
329
|
+
# Compute norm
|
|
330
|
+
if numpy.isinf(order) and not numpy.isneginf(order):
|
|
295
331
|
norm_ = max(numpy.abs(eigs))
|
|
296
332
|
|
|
297
|
-
elif
|
|
333
|
+
elif numpy.isneginf(order):
|
|
298
334
|
norm_ = min(numpy.abs(eigs))
|
|
299
335
|
|
|
300
|
-
elif (order
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
elif (order == 'fro') or (order == 2.0):
|
|
304
|
-
norm_2 = numpy.sum(numpy.abs(eigs)**2)
|
|
305
|
-
norm_ = numpy.sqrt(norm_2)
|
|
306
|
-
|
|
307
|
-
elif isinstance(order, (int, float, numpy.integer, numpy.floating)) and \
|
|
308
|
-
not isinstance(order, (bool, numpy.bool_)):
|
|
336
|
+
elif isinstance(order, (int, float, numpy.integer, numpy.floating)) \
|
|
337
|
+
and not isinstance(order, (bool, numpy.bool_)):
|
|
309
338
|
norm_q = numpy.sum(numpy.abs(eigs)**order)
|
|
310
339
|
norm_ = norm_q**(1.0 / order)
|
|
311
340
|
|
|
312
|
-
else:
|
|
313
|
-
raise ValueError('"order" is invalid.')
|
|
314
|
-
|
|
315
341
|
return norm_
|
|
316
342
|
|
|
317
343
|
|
|
@@ -319,7 +345,7 @@ def norm(A, N=None, order=None):
|
|
|
319
345
|
# trace
|
|
320
346
|
# =====
|
|
321
347
|
|
|
322
|
-
def trace(A, N=None, p=1.0):
|
|
348
|
+
def trace(A, N=None, p=1.0, seed=None, **kwargs):
|
|
323
349
|
"""
|
|
324
350
|
Estimate the trace of a power of a Hermitian matrix.
|
|
325
351
|
|
|
@@ -334,7 +360,7 @@ def trace(A, N=None, p=1.0):
|
|
|
334
360
|
a power (or that of a matrix containing :math:`\\mathbf{A}`) is to be
|
|
335
361
|
computed.
|
|
336
362
|
|
|
337
|
-
|
|
363
|
+
size : int, default=None
|
|
338
364
|
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
339
365
|
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
340
366
|
:math:`\\mathbf{A}` itself.
|
|
@@ -342,6 +368,12 @@ def trace(A, N=None, p=1.0):
|
|
|
342
368
|
p : float, default=1.0
|
|
343
369
|
The exponent :math:`p` in :math:`\\mathbf{A}^p`.
|
|
344
370
|
|
|
371
|
+
seed : int, default=None
|
|
372
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
373
|
+
|
|
374
|
+
**kwargs : dict, optional
|
|
375
|
+
Pass additional options to the underlying
|
|
376
|
+
:func:`FreeForm.decompress` function.
|
|
345
377
|
|
|
346
378
|
Returns
|
|
347
379
|
-------
|
|
@@ -352,7 +384,7 @@ def trace(A, N=None, p=1.0):
|
|
|
352
384
|
See Also
|
|
353
385
|
--------
|
|
354
386
|
|
|
355
|
-
|
|
387
|
+
eigvalsh
|
|
356
388
|
cond
|
|
357
389
|
slogdet
|
|
358
390
|
norm
|
|
@@ -362,7 +394,7 @@ def trace(A, N=None, p=1.0):
|
|
|
362
394
|
|
|
363
395
|
The trace is highly amenable to subsampling: under free decompression
|
|
364
396
|
the average eigenvalue is assumed constant, so the trace increases
|
|
365
|
-
linearly. Traces of powers fall back to :func:`freealg.
|
|
397
|
+
linearly. Traces of powers fall back to :func:`freealg.eigvalsh`.
|
|
366
398
|
|
|
367
399
|
Examples
|
|
368
400
|
--------
|
|
@@ -381,10 +413,10 @@ def trace(A, N=None, p=1.0):
|
|
|
381
413
|
if numpy.isclose(p, 1.0):
|
|
382
414
|
samples, n, n_s = _subsample_apply(numpy.trace, A, output_array=False)
|
|
383
415
|
if N is None:
|
|
384
|
-
|
|
385
|
-
return numpy.mean(samples) * (
|
|
416
|
+
size = n
|
|
417
|
+
return numpy.mean(samples) * (size / n_s)
|
|
386
418
|
|
|
387
|
-
eig =
|
|
419
|
+
eig = eigvalsh(A, size=size, seed=seed, **kwargs)
|
|
388
420
|
return numpy.sum(eig ** p)
|
|
389
421
|
|
|
390
422
|
|
|
@@ -392,7 +424,7 @@ def trace(A, N=None, p=1.0):
|
|
|
392
424
|
# slogdet
|
|
393
425
|
# =======
|
|
394
426
|
|
|
395
|
-
def slogdet(A,
|
|
427
|
+
def slogdet(A, size=None, seed=None, **kwargs):
|
|
396
428
|
"""
|
|
397
429
|
Estimate the sign and logarithm of the determinant of a Hermitian matrix.
|
|
398
430
|
|
|
@@ -407,11 +439,18 @@ def slogdet(A, N=None):
|
|
|
407
439
|
number (or that of a matrix containing :math:`\\mathbf{A}`) are to be
|
|
408
440
|
computed.
|
|
409
441
|
|
|
410
|
-
|
|
442
|
+
size : int, default=None
|
|
411
443
|
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
412
444
|
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
413
445
|
:math:`\\mathbf{A}` itself.
|
|
414
446
|
|
|
447
|
+
seed : int, default=None
|
|
448
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
449
|
+
|
|
450
|
+
**kwargs : dict, optional
|
|
451
|
+
Pass additional options to the underlying
|
|
452
|
+
:func:`FreeForm.decompress` function.
|
|
453
|
+
|
|
415
454
|
Returns
|
|
416
455
|
-------
|
|
417
456
|
|
|
@@ -424,7 +463,7 @@ def slogdet(A, N=None):
|
|
|
424
463
|
See Also
|
|
425
464
|
--------
|
|
426
465
|
|
|
427
|
-
|
|
466
|
+
eigvalsh
|
|
428
467
|
cond
|
|
429
468
|
trace
|
|
430
469
|
norm
|
|
@@ -432,7 +471,7 @@ def slogdet(A, N=None):
|
|
|
432
471
|
Notes
|
|
433
472
|
-----
|
|
434
473
|
|
|
435
|
-
This is a convenience function using :func:`freealg.
|
|
474
|
+
This is a convenience function using :func:`freealg.eigvalsh`.
|
|
436
475
|
|
|
437
476
|
Examples
|
|
438
477
|
--------
|
|
@@ -448,7 +487,7 @@ def slogdet(A, N=None):
|
|
|
448
487
|
>>> sign, ld = slogdet(A, 100_000)
|
|
449
488
|
"""
|
|
450
489
|
|
|
451
|
-
eigs =
|
|
490
|
+
eigs = eigvalsh(A, size=size, seed=seed, **kwargs)
|
|
452
491
|
sign = numpy.prod(numpy.sign(eigs))
|
|
453
492
|
ld = numpy.sum(numpy.log(numpy.abs(eigs)))
|
|
454
493
|
|
freealg/freeform.py
CHANGED
|
@@ -108,6 +108,21 @@ class FreeForm(object):
|
|
|
108
108
|
decompress
|
|
109
109
|
Free decompression of spectral density
|
|
110
110
|
|
|
111
|
+
eigvalsh
|
|
112
|
+
Estimate the eigenvalues
|
|
113
|
+
|
|
114
|
+
cond
|
|
115
|
+
Estimate the condition number
|
|
116
|
+
|
|
117
|
+
trace
|
|
118
|
+
Estimate the trace of a matrix power
|
|
119
|
+
|
|
120
|
+
slogdet
|
|
121
|
+
Estimate the sign and logarithm of the determinant
|
|
122
|
+
|
|
123
|
+
norm
|
|
124
|
+
Estimate the Schatten norm
|
|
125
|
+
|
|
111
126
|
Examples
|
|
112
127
|
--------
|
|
113
128
|
|
|
@@ -411,9 +426,30 @@ class FreeForm(object):
|
|
|
411
426
|
# density
|
|
412
427
|
# =======
|
|
413
428
|
|
|
429
|
+
def _grid(self, scale, extend=1.0, N=500):
|
|
430
|
+
"""
|
|
431
|
+
Return a grid of points to evaluate density / Hilbert / Stieltjes
|
|
432
|
+
transforms.
|
|
433
|
+
"""
|
|
434
|
+
|
|
435
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
436
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
437
|
+
|
|
438
|
+
x_min = numpy.floor(extend * (center - extend * radius * scale))
|
|
439
|
+
x_max = numpy.ceil(extend * (center + extend * radius * scale))
|
|
440
|
+
|
|
441
|
+
x_min /= extend
|
|
442
|
+
x_max /= extend
|
|
443
|
+
|
|
444
|
+
return numpy.linspace(x_min, x_max, N)
|
|
445
|
+
|
|
446
|
+
# =======
|
|
447
|
+
# density
|
|
448
|
+
# =======
|
|
449
|
+
|
|
414
450
|
def density(self, x=None, plot=False, latex=False, save=False):
|
|
415
451
|
"""
|
|
416
|
-
Evaluate density.
|
|
452
|
+
Evaluate spectral density.
|
|
417
453
|
|
|
418
454
|
Parameters
|
|
419
455
|
----------
|
|
@@ -459,12 +495,7 @@ class FreeForm(object):
|
|
|
459
495
|
|
|
460
496
|
# Create x if not given
|
|
461
497
|
if x is None:
|
|
462
|
-
|
|
463
|
-
center = 0.5 * (self.lam_p + self.lam_m)
|
|
464
|
-
scale = 1.25
|
|
465
|
-
x_min = numpy.floor(center - radius * scale)
|
|
466
|
-
x_max = numpy.ceil(center + radius * scale)
|
|
467
|
-
x = numpy.linspace(x_min, x_max, 500)
|
|
498
|
+
x = self._grid(1.25)
|
|
468
499
|
|
|
469
500
|
# Preallocate density to zero
|
|
470
501
|
rho = numpy.zeros_like(x)
|
|
@@ -554,12 +585,7 @@ class FreeForm(object):
|
|
|
554
585
|
|
|
555
586
|
# Create x if not given
|
|
556
587
|
if x is None:
|
|
557
|
-
|
|
558
|
-
center = 0.5 * (self.lam_p + self.lam_m)
|
|
559
|
-
scale = 1.25
|
|
560
|
-
x_min = numpy.floor(center - radius * scale)
|
|
561
|
-
x_max = numpy.ceil(center + radius * scale)
|
|
562
|
-
x = numpy.linspace(x_min, x_max, 500)
|
|
588
|
+
x = self._grid(1.25)
|
|
563
589
|
|
|
564
590
|
# if (numpy.min(x) > self.lam_m) or (numpy.max(x) < self.lam_p):
|
|
565
591
|
# raise ValueError('"x" does not encompass support interval.')
|
|
@@ -683,12 +709,8 @@ class FreeForm(object):
|
|
|
683
709
|
|
|
684
710
|
# Create x if not given
|
|
685
711
|
if x is None:
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
scale = 2.0
|
|
689
|
-
x_min = numpy.floor(2.0 * (center - 2.0 * radius * scale)) / 2.0
|
|
690
|
-
x_max = numpy.ceil(2.0 * (center + 2.0 * radius * scale)) / 2.0
|
|
691
|
-
x = numpy.linspace(x_min, x_max, 500)
|
|
712
|
+
x = self._grid(2.0, extend=2.0)
|
|
713
|
+
|
|
692
714
|
if not cartesian:
|
|
693
715
|
# Evaluate slightly above the real line
|
|
694
716
|
x = x.astype(complex)
|
|
@@ -803,7 +825,7 @@ class FreeForm(object):
|
|
|
803
825
|
# ==========
|
|
804
826
|
|
|
805
827
|
def decompress(self, size, x=None, max_iter=500, eigvals=True,
|
|
806
|
-
tolerance=1e-9,
|
|
828
|
+
tolerance=1e-9, plot=False,
|
|
807
829
|
latex=False, save=False):
|
|
808
830
|
"""
|
|
809
831
|
Free decompression of spectral density.
|
|
@@ -828,9 +850,6 @@ class FreeForm(object):
|
|
|
828
850
|
Tolerance for the solution obtained by the Newton solver. Also
|
|
829
851
|
used for the finite difference approximation to the derivative.
|
|
830
852
|
|
|
831
|
-
seed : int, default=None
|
|
832
|
-
Seed for random number generator. Used for QMC sampling.
|
|
833
|
-
|
|
834
853
|
plot : bool, default=False
|
|
835
854
|
If `True`, density is plotted.
|
|
836
855
|
|
|
@@ -890,8 +909,339 @@ class FreeForm(object):
|
|
|
890
909
|
plot_density(x, rho, support=(lb, ub),
|
|
891
910
|
label='Decompression', latex=latex, save=save)
|
|
892
911
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
912
|
+
return x, rho
|
|
913
|
+
|
|
914
|
+
# ========
|
|
915
|
+
# eigvalsh
|
|
916
|
+
# ========
|
|
917
|
+
|
|
918
|
+
def eigvalsh(self, size=None, seed=None, **kwargs):
|
|
919
|
+
"""
|
|
920
|
+
Estimate the eigenvalues.
|
|
921
|
+
|
|
922
|
+
This function estimates the eigenvalues of the freeform matrix
|
|
923
|
+
or a larger matrix containing it using free decompression.
|
|
924
|
+
|
|
925
|
+
Parameters
|
|
926
|
+
----------
|
|
927
|
+
|
|
928
|
+
size : int, default=None
|
|
929
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
930
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
931
|
+
:math:`\\mathbf{A}` itself.
|
|
932
|
+
|
|
933
|
+
seed : int, default=None
|
|
934
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
935
|
+
|
|
936
|
+
**kwargs : dict, optional
|
|
937
|
+
Pass additional options to the underlying
|
|
938
|
+
:func:`FreeForm.decompress` function.
|
|
939
|
+
|
|
940
|
+
Returns
|
|
941
|
+
-------
|
|
942
|
+
|
|
943
|
+
eigs : numpy.array
|
|
944
|
+
Eigenvalues of decompressed matrix
|
|
945
|
+
|
|
946
|
+
See Also
|
|
947
|
+
--------
|
|
948
|
+
|
|
949
|
+
FreeForm.decompress
|
|
950
|
+
FreeForm.cond
|
|
951
|
+
|
|
952
|
+
Notes
|
|
953
|
+
-----
|
|
954
|
+
|
|
955
|
+
All arguments to the `.decompress()` procedure can be provided.
|
|
956
|
+
|
|
957
|
+
Examples
|
|
958
|
+
--------
|
|
959
|
+
|
|
960
|
+
.. code-block:: python
|
|
961
|
+
:emphasize-lines: 1
|
|
962
|
+
|
|
963
|
+
>>> from freealg import FreeForm
|
|
964
|
+
"""
|
|
965
|
+
|
|
966
|
+
if size is None:
|
|
967
|
+
x = self._grid(1.25)
|
|
968
|
+
rho = self.density(x)
|
|
969
|
+
size = self.n
|
|
896
970
|
else:
|
|
897
|
-
|
|
971
|
+
x, rho = self.decompress(size, **kwargs)
|
|
972
|
+
eigs = numpy.sort(qmc_sample(x, rho, size, seed=seed))
|
|
973
|
+
return eigs
|
|
974
|
+
|
|
975
|
+
# ====
|
|
976
|
+
# cond
|
|
977
|
+
# ====
|
|
978
|
+
|
|
979
|
+
def cond(self, size=None, seed=None, **kwargs):
|
|
980
|
+
"""
|
|
981
|
+
Estimate the condition number.
|
|
982
|
+
|
|
983
|
+
This function estimates the condition number of the matrix
|
|
984
|
+
:math:`\\mathbf{A}` or a larger matrix containing :math:`\\mathbf{A}`
|
|
985
|
+
using free decompression.
|
|
986
|
+
|
|
987
|
+
Parameters
|
|
988
|
+
----------
|
|
989
|
+
|
|
990
|
+
size : int, default=None
|
|
991
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
992
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
993
|
+
:math:`\\mathbf{A}` itself.
|
|
994
|
+
|
|
995
|
+
**kwargs : dict, optional
|
|
996
|
+
Pass additional options to the underlying
|
|
997
|
+
:func:`FreeForm.decompress` function.
|
|
998
|
+
|
|
999
|
+
Returns
|
|
1000
|
+
-------
|
|
1001
|
+
|
|
1002
|
+
c : float
|
|
1003
|
+
Condition number
|
|
1004
|
+
|
|
1005
|
+
See Also
|
|
1006
|
+
--------
|
|
1007
|
+
|
|
1008
|
+
FreeForm.eigvalsh
|
|
1009
|
+
FreeForm.norm
|
|
1010
|
+
FreeForm.slogdet
|
|
1011
|
+
FreeForm.trace
|
|
1012
|
+
|
|
1013
|
+
Examples
|
|
1014
|
+
--------
|
|
1015
|
+
|
|
1016
|
+
.. code-block:: python
|
|
1017
|
+
:emphasize-lines: 1
|
|
1018
|
+
|
|
1019
|
+
>>> from freealg import FreeForm
|
|
1020
|
+
"""
|
|
1021
|
+
|
|
1022
|
+
eigs = self.eigvalsh(size=size, **kwargs)
|
|
1023
|
+
return eigs.max() / eigs.min()
|
|
1024
|
+
|
|
1025
|
+
# =====
|
|
1026
|
+
# trace
|
|
1027
|
+
# =====
|
|
1028
|
+
|
|
1029
|
+
def trace(self, size=None, p=1.0, seed=None, **kwargs):
|
|
1030
|
+
"""
|
|
1031
|
+
Estimate the trace of a power.
|
|
1032
|
+
|
|
1033
|
+
This function estimates the trace of the matrix power
|
|
1034
|
+
:math:`\\mathbf{A}^p` of the freeform or that of a larger matrix
|
|
1035
|
+
containing it.
|
|
1036
|
+
|
|
1037
|
+
Parameters
|
|
1038
|
+
----------
|
|
1039
|
+
|
|
1040
|
+
size : int, default=None
|
|
1041
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1042
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1043
|
+
:math:`\\mathbf{A}` itself.
|
|
1044
|
+
|
|
1045
|
+
p : float, default=1.0
|
|
1046
|
+
The exponent :math:`p` in :math:`\\mathbf{A}^p`.
|
|
1047
|
+
|
|
1048
|
+
seed : int, default=None
|
|
1049
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
1050
|
+
|
|
1051
|
+
**kwargs : dict, optional
|
|
1052
|
+
Pass additional options to the underlying
|
|
1053
|
+
:func:`FreeForm.decompress` function.
|
|
1054
|
+
|
|
1055
|
+
Returns
|
|
1056
|
+
-------
|
|
1057
|
+
|
|
1058
|
+
trace : float
|
|
1059
|
+
matrix trace
|
|
1060
|
+
|
|
1061
|
+
See Also
|
|
1062
|
+
--------
|
|
1063
|
+
|
|
1064
|
+
FreeForm.eigvalsh
|
|
1065
|
+
FreeForm.cond
|
|
1066
|
+
FreeForm.slogdet
|
|
1067
|
+
FreeForm.norm
|
|
1068
|
+
|
|
1069
|
+
Notes
|
|
1070
|
+
-----
|
|
1071
|
+
|
|
1072
|
+
The trace is highly amenable to subsampling: under free decompression
|
|
1073
|
+
the average eigenvalue is assumed constant, so the trace increases
|
|
1074
|
+
linearly. Traces of powers fall back to :func:`eigvalsh`.
|
|
1075
|
+
All arguments to the `.decompress()` procedure can be provided.
|
|
1076
|
+
|
|
1077
|
+
Examples
|
|
1078
|
+
--------
|
|
1079
|
+
|
|
1080
|
+
.. code-block:: python
|
|
1081
|
+
:emphasize-lines: 6
|
|
1082
|
+
|
|
1083
|
+
>>> ...
|
|
1084
|
+
"""
|
|
1085
|
+
if numpy.isclose(p, 1.0):
|
|
1086
|
+
return numpy.mean(self.eig) * (size / self.n)
|
|
1087
|
+
|
|
1088
|
+
eig = self.eigvalsh(size=size, seed=seed, **kwargs)
|
|
1089
|
+
return numpy.sum(eig ** p)
|
|
1090
|
+
|
|
1091
|
+
# =======
|
|
1092
|
+
# slogdet
|
|
1093
|
+
# =======
|
|
1094
|
+
|
|
1095
|
+
def slogdet(self, size=None, seed=None, **kwargs):
|
|
1096
|
+
"""
|
|
1097
|
+
Estimate the sign and logarithm of the determinant.
|
|
1098
|
+
|
|
1099
|
+
This function estimates the *slogdet* of the freeform or that of
|
|
1100
|
+
a larger matrix containing it using free decompression.
|
|
1101
|
+
|
|
1102
|
+
Parameters
|
|
1103
|
+
----------
|
|
1104
|
+
|
|
1105
|
+
size : int, default=None
|
|
1106
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1107
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1108
|
+
:math:`\\mathbf{A}` itself.
|
|
1109
|
+
|
|
1110
|
+
seed : int, default=None
|
|
1111
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
1112
|
+
|
|
1113
|
+
Returns
|
|
1114
|
+
-------
|
|
1115
|
+
|
|
1116
|
+
sign : float
|
|
1117
|
+
Sign of determinant
|
|
1118
|
+
|
|
1119
|
+
ld : float
|
|
1120
|
+
natural logarithm of the absolute value of the determinant
|
|
1121
|
+
|
|
1122
|
+
See Also
|
|
1123
|
+
--------
|
|
1124
|
+
|
|
1125
|
+
FreeForm.eigvalsh
|
|
1126
|
+
FreeForm.cond
|
|
1127
|
+
FreeForm.trace
|
|
1128
|
+
FreeForm.norm
|
|
1129
|
+
|
|
1130
|
+
Notes
|
|
1131
|
+
-----
|
|
1132
|
+
|
|
1133
|
+
All arguments to the `.decompress()` procedure can be provided.
|
|
1134
|
+
|
|
1135
|
+
Examples
|
|
1136
|
+
--------
|
|
1137
|
+
|
|
1138
|
+
.. code-block:: python
|
|
1139
|
+
:emphasize-lines: 6
|
|
1140
|
+
|
|
1141
|
+
>>> ...
|
|
1142
|
+
"""
|
|
1143
|
+
|
|
1144
|
+
eigs = self.eigvalsh(size=size, seed=seed, **kwargs)
|
|
1145
|
+
sign = numpy.prod(numpy.sign(eigs))
|
|
1146
|
+
ld = numpy.sum(numpy.log(numpy.abs(eigs)))
|
|
1147
|
+
return sign, ld
|
|
1148
|
+
|
|
1149
|
+
# ====
|
|
1150
|
+
# norm
|
|
1151
|
+
# ====
|
|
1152
|
+
|
|
1153
|
+
def norm(self, size=None, order=2, seed=None, **kwargs):
|
|
1154
|
+
"""
|
|
1155
|
+
Estimate the Schatten norm.
|
|
1156
|
+
|
|
1157
|
+
This function estimates the norm of the freeform or a larger
|
|
1158
|
+
matrix containing it using free decompression.
|
|
1159
|
+
|
|
1160
|
+
Parameters
|
|
1161
|
+
----------
|
|
1162
|
+
|
|
1163
|
+
size : int, default=None
|
|
1164
|
+
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1165
|
+
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1166
|
+
:math:`\\mathbf{A}` itself.
|
|
1167
|
+
|
|
1168
|
+
order : {float, ``''inf``, ``'-inf'``, ``'fro'``, ``'nuc'``}, default=2
|
|
1169
|
+
Order of the norm.
|
|
1170
|
+
|
|
1171
|
+
* float :math:`p`: Schatten p-norm.
|
|
1172
|
+
* ``'inf'``: Largest absolute eigenvalue
|
|
1173
|
+
:math:`\\max \\vert \\lambda_i \\vert)`
|
|
1174
|
+
* ``'-inf'``: Smallest absolute eigenvalue
|
|
1175
|
+
:math:`\\min \\vert \\lambda_i \\vert)`
|
|
1176
|
+
* ``'fro'``: Frobenius norm corresponding to :math:`p=2`
|
|
1177
|
+
* ``'nuc'``: Nuclear (or trace) norm corresponding to :math:`p=1`
|
|
1178
|
+
|
|
1179
|
+
seed : int, default=None
|
|
1180
|
+
The seed for the Quasi-Monte Carlo sampler.
|
|
1181
|
+
|
|
1182
|
+
**kwargs : dict, optional
|
|
1183
|
+
Pass additional options to the underlying
|
|
1184
|
+
:func:`FreeForm.decompress` function.
|
|
1185
|
+
|
|
1186
|
+
Returns
|
|
1187
|
+
-------
|
|
1188
|
+
|
|
1189
|
+
norm : float
|
|
1190
|
+
matrix norm
|
|
1191
|
+
|
|
1192
|
+
See Also
|
|
1193
|
+
--------
|
|
1194
|
+
|
|
1195
|
+
FreeForm.eigvalsh
|
|
1196
|
+
FreeForm.cond
|
|
1197
|
+
FreeForm.slogdet
|
|
1198
|
+
FreeForm.trace
|
|
1199
|
+
|
|
1200
|
+
Notes
|
|
1201
|
+
-----
|
|
1202
|
+
|
|
1203
|
+
Thes Schatten :math:`p`-norm is defined by
|
|
1204
|
+
|
|
1205
|
+
.. math::
|
|
1206
|
+
|
|
1207
|
+
\\Vert \\mathbf{A} \\Vert_p = \\left(
|
|
1208
|
+
\\sum_{i=1}^N \\vert \\lambda_i \\vert^p \\right)^{1/p}.
|
|
1209
|
+
|
|
1210
|
+
Examples
|
|
1211
|
+
--------
|
|
1212
|
+
|
|
1213
|
+
.. code-block:: python
|
|
1214
|
+
:emphasize-lines: 6
|
|
1215
|
+
|
|
1216
|
+
>>> ...
|
|
1217
|
+
"""
|
|
1218
|
+
|
|
1219
|
+
eigs = self.eigvalsh(size, seed=seed, **kwargs)
|
|
1220
|
+
|
|
1221
|
+
# Check order type and convert to float
|
|
1222
|
+
if order == 'nuc':
|
|
1223
|
+
order = 1
|
|
1224
|
+
elif order == 'fro':
|
|
1225
|
+
order = 2
|
|
1226
|
+
elif order == 'inf':
|
|
1227
|
+
order = float('inf')
|
|
1228
|
+
elif order == '-inf':
|
|
1229
|
+
order = -float('inf')
|
|
1230
|
+
elif not isinstance(order,
|
|
1231
|
+
(int, float, numpy.integer, numpy.floating)) \
|
|
1232
|
+
and not isinstance(order, (bool, numpy.bool_)):
|
|
1233
|
+
raise ValueError('"order" is invalid.')
|
|
1234
|
+
|
|
1235
|
+
# Compute norm
|
|
1236
|
+
if numpy.isinf(order) and not numpy.isneginf(order):
|
|
1237
|
+
norm_ = max(numpy.abs(eigs))
|
|
1238
|
+
|
|
1239
|
+
elif numpy.isneginf(order):
|
|
1240
|
+
norm_ = min(numpy.abs(eigs))
|
|
1241
|
+
|
|
1242
|
+
elif isinstance(order, (int, float, numpy.integer, numpy.floating)) \
|
|
1243
|
+
and not isinstance(order, (bool, numpy.bool_)):
|
|
1244
|
+
norm_q = numpy.sum(numpy.abs(eigs)**order)
|
|
1245
|
+
norm_ = norm_q**(1.0 / order)
|
|
1246
|
+
|
|
1247
|
+
return norm_
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freealg
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Free probability for large matrices
|
|
5
5
|
Home-page: https://github.com/ameli/freealg
|
|
6
6
|
Download-URL: https://github.com/ameli/freealg/archive/main.zip
|
|
@@ -102,10 +102,10 @@ using a much smaller Wishart matrix.
|
|
|
102
102
|
>>> import freealg as fa
|
|
103
103
|
>>> mp = fa.distributions.MarchenkoPastur(1/50) # Wishart matrices with aspect ratio 1/50
|
|
104
104
|
>>> A = mp.matrix(1000) # Sample a 1000 x 1000 Wishart matrix
|
|
105
|
-
>>> eigs = fa.
|
|
105
|
+
>>> eigs = fa.eigvalsh(A, 100_000) # Estimate the eigenvalues of 100000 x 100000
|
|
106
106
|
|
|
107
107
|
For more details on how to interface with *freealg* check out the
|
|
108
|
-
`
|
|
108
|
+
`Live Demo <https://colab.research.google.com/github/ameli/freealg/blob/main/notebooks/quick_start.ipynb>`__.
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
Test
|
|
@@ -129,7 +129,7 @@ How to Contribute
|
|
|
129
129
|
=================
|
|
130
130
|
|
|
131
131
|
We welcome contributions via GitHub's pull request. Developers should review
|
|
132
|
-
our
|
|
132
|
+
our `Contributing Guidelines <https://github.com/ameli/freealg/blob/main/CONTRIBUTING.rst>`__
|
|
133
133
|
before submitting their code. If you do not feel comfortable modifying the
|
|
134
134
|
code, we also welcome feature requests and bug reports.
|
|
135
135
|
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
freealg/__init__.py,sha256=
|
|
2
|
-
freealg/__version__.py,sha256=
|
|
1
|
+
freealg/__init__.py,sha256=KfzqG7qig6ZZCKqgHcC2ApTg4rTLkrbJsvVoJd8UFG8,625
|
|
2
|
+
freealg/__version__.py,sha256=W_9dCm49nLvZulVAvvsafxLJjVBSKDBHz9K7szFZllo,22
|
|
3
3
|
freealg/_chebyshev.py,sha256=dsAj3YEpmkzB65smluZ0Fi5IZSdpnQXBSIuKMg19grA,5523
|
|
4
4
|
freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
|
|
5
5
|
freealg/_decompress.py,sha256=0MYoO3lqwMgNYlVriaRNUqUwY3XYyZZsDAtNRBq6rhE,10470
|
|
6
6
|
freealg/_jacobi.py,sha256=AT4ONSHGGDxVKE3MGMLyMR8uDFiO-e9u3x5udYfdJJk,5635
|
|
7
|
+
freealg/_linalg.py,sha256=Vl1iB0rG-PVjEqoUbCXNxk6BmeujEbBucG2ZdBJoFjA,12490
|
|
7
8
|
freealg/_pade.py,sha256=Diw850oH2OLQeUrBR-q19TmjSBoBvXl6ogp4o1s2UIo,15184
|
|
8
9
|
freealg/_plot_util.py,sha256=U4alp7Pzg315_7jJdu1UB0tIUcxUovQgHDHsUYoa2Z0,19728
|
|
9
10
|
freealg/_sample.py,sha256=ckC75eqv-mRP1F5BnhvsjfLTaoAzHK8bebl9bCRZYDo,2561
|
|
10
11
|
freealg/_support.py,sha256=mYlrsdLbniqFgFofbHrZIiYSyJudPqtyMQQTPBD9Y6M,6439
|
|
11
12
|
freealg/_util.py,sha256=8Tvz-XODtKYoU76ODmF1TBaIYLlr6-AXiyoMDwDSxVg,3779
|
|
12
|
-
freealg/
|
|
13
|
-
freealg/freeform.py,sha256=8emyCQ6AUjp_HB1gWQ-ecddlDgfxHGr3PqXSyoPxeMo,28593
|
|
13
|
+
freealg/freeform.py,sha256=8yW7dwuUeQ52ewovhnpGbXZrpEZssuUj4djVoD5K2Iw,37065
|
|
14
14
|
freealg/distributions/__init__.py,sha256=t_yZyEkW_W_tSV9IvgYXtVASxD2BEdiNVXcV2ebMy8M,579
|
|
15
15
|
freealg/distributions/_kesten_mckay.py,sha256=210RF2OQEYLZBeLB6wmbdHnZPs_9ldDNHm_FMlg5tis,19881
|
|
16
16
|
freealg/distributions/_marchenko_pastur.py,sha256=kchFccRMuVF2Cus_99vdEwuRimkHzEUV8xt5kZFg7ZI,16994
|
|
17
17
|
freealg/distributions/_meixner.py,sha256=ws7t_EUa7V0s97dgMQIJLv1b6qMLqf9fLLbTJQudf_8,17512
|
|
18
18
|
freealg/distributions/_wachter.py,sha256=Hna_MXqAPjuRkeilLPMf4Xg_3C6tTu5oZLEQnA-RuE4,16897
|
|
19
19
|
freealg/distributions/_wigner.py,sha256=SxgPLtvIVBi9m4De-oBD0x6-2Je_eBqpDrpDYcoLuis,15871
|
|
20
|
-
freealg-0.3.
|
|
21
|
-
freealg-0.3.
|
|
22
|
-
freealg-0.3.
|
|
23
|
-
freealg-0.3.
|
|
24
|
-
freealg-0.3.
|
|
25
|
-
freealg-0.3.
|
|
20
|
+
freealg-0.3.6.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
|
|
21
|
+
freealg-0.3.6.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
|
|
22
|
+
freealg-0.3.6.dist-info/METADATA,sha256=xkGdM65EQRVE_drmJRkzO3LaZh0dMDOGMJ8iTFFTw-E,4994
|
|
23
|
+
freealg-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
freealg-0.3.6.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
|
|
25
|
+
freealg-0.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|