freealg 0.7.17__py3-none-any.whl → 0.7.18__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 -6
- freealg/__version__.py +1 -1
- freealg/_algebraic_form/_branch_points.py +18 -18
- freealg/_algebraic_form/_continuation_algebraic.py +13 -13
- freealg/_algebraic_form/_cusp.py +15 -15
- freealg/_algebraic_form/_cusp_wrap.py +6 -6
- freealg/_algebraic_form/_decompress.py +16 -16
- freealg/_algebraic_form/_decompress4.py +31 -31
- freealg/_algebraic_form/_decompress5.py +23 -23
- freealg/_algebraic_form/_decompress6.py +13 -13
- freealg/_algebraic_form/_decompress7.py +15 -15
- freealg/_algebraic_form/_decompress8.py +17 -17
- freealg/_algebraic_form/_decompress9.py +18 -18
- freealg/_algebraic_form/_decompress_new.py +17 -17
- freealg/_algebraic_form/_decompress_new_2.py +57 -57
- freealg/_algebraic_form/_decompress_util.py +10 -10
- freealg/_algebraic_form/_decompressible.py +292 -0
- freealg/_algebraic_form/_edge.py +10 -10
- freealg/_algebraic_form/_homotopy4.py +9 -9
- freealg/_algebraic_form/_homotopy5.py +9 -9
- freealg/_algebraic_form/_support.py +19 -19
- freealg/_algebraic_form/algebraic_form.py +262 -468
- freealg/_base_form.py +401 -0
- freealg/_free_form/__init__.py +1 -4
- freealg/_free_form/_density_util.py +1 -1
- freealg/_free_form/_plot_util.py +3 -511
- freealg/_free_form/free_form.py +8 -367
- freealg/_util.py +59 -11
- freealg/distributions/__init__.py +2 -1
- freealg/distributions/_base_distribution.py +163 -0
- freealg/distributions/_chiral_block.py +137 -11
- freealg/distributions/_compound_poisson.py +141 -47
- freealg/distributions/_deformed_marchenko_pastur.py +138 -33
- freealg/distributions/_deformed_wigner.py +98 -9
- freealg/distributions/_fuss_catalan.py +269 -0
- freealg/distributions/_kesten_mckay.py +4 -130
- freealg/distributions/_marchenko_pastur.py +8 -196
- freealg/distributions/_meixner.py +4 -130
- freealg/distributions/_wachter.py +4 -130
- freealg/distributions/_wigner.py +10 -127
- freealg/visualization/__init__.py +2 -2
- freealg/visualization/{_rgb_hsv.py → _domain_coloring.py} +37 -29
- freealg/visualization/_plot_util.py +513 -0
- {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/METADATA +1 -1
- freealg-0.7.18.dist-info/RECORD +74 -0
- freealg-0.7.17.dist-info/RECORD +0 -69
- /freealg/{_free_form/_sample.py → _sample.py} +0 -0
- /freealg/{_free_form/_support.py → _support.py} +0 -0
- {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/WHEEL +0 -0
- {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/licenses/AUTHORS.txt +0 -0
- {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/licenses/LICENSE.txt +0 -0
- {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/top_level.txt +0 -0
freealg/_free_form/free_form.py
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import numpy
|
|
15
15
|
from functools import partial
|
|
16
|
-
from .._util import
|
|
16
|
+
from .._util import compute_eig
|
|
17
17
|
from ._density_util import kde, force_density
|
|
18
18
|
from ._jacobi import jacobi_sample_proj, jacobi_kernel_proj, jacobi_density, \
|
|
19
19
|
jacobi_stieltjes
|
|
@@ -21,11 +21,13 @@ from ._chebyshev import chebyshev_sample_proj, chebyshev_kernel_proj, \
|
|
|
21
21
|
chebyshev_density, chebyshev_stieltjes
|
|
22
22
|
from ._damp import jackson_damping, lanczos_damping, fejer_damping, \
|
|
23
23
|
exponential_damping, parzen_damping
|
|
24
|
-
from ._plot_util import plot_fit
|
|
24
|
+
from ._plot_util import plot_fit
|
|
25
|
+
from ..visualization._plot_util import plot_density, plot_hilbert, \
|
|
26
|
+
plot_stieltjes
|
|
25
27
|
from ._pade import fit_pade, eval_pade
|
|
26
28
|
from ._decompress import decompress
|
|
27
|
-
from
|
|
28
|
-
from
|
|
29
|
+
from .._support import supp
|
|
30
|
+
from .._base_form import BaseForm
|
|
29
31
|
|
|
30
32
|
# Fallback to previous numpy API
|
|
31
33
|
if not hasattr(numpy, 'trapezoid'):
|
|
@@ -38,7 +40,7 @@ __all__ = ['FreeForm']
|
|
|
38
40
|
# Free Form
|
|
39
41
|
# =========
|
|
40
42
|
|
|
41
|
-
class FreeForm(
|
|
43
|
+
class FreeForm(BaseForm):
|
|
42
44
|
"""
|
|
43
45
|
Free probability for large matrices.
|
|
44
46
|
|
|
@@ -143,12 +145,7 @@ class FreeForm(object):
|
|
|
143
145
|
Initialization.
|
|
144
146
|
"""
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
self.eig = None
|
|
148
|
-
self.delta = delta # Offset above real axis to apply Plemelj formula
|
|
149
|
-
|
|
150
|
-
# Data type for complex arrays
|
|
151
|
-
self.dtype = resolve_complex_dtype(dtype)
|
|
148
|
+
super().__init__(delta, dtype)
|
|
152
149
|
|
|
153
150
|
# Eigenvalues
|
|
154
151
|
if A.ndim == 1:
|
|
@@ -467,27 +464,6 @@ class FreeForm(object):
|
|
|
467
464
|
|
|
468
465
|
return self.psi
|
|
469
466
|
|
|
470
|
-
# =============
|
|
471
|
-
# generate grid
|
|
472
|
-
# =============
|
|
473
|
-
|
|
474
|
-
def _generate_grid(self, scale, extend=1.0, N=500):
|
|
475
|
-
"""
|
|
476
|
-
Generate a grid of points to evaluate density / Hilbert / Stieltjes
|
|
477
|
-
transforms.
|
|
478
|
-
"""
|
|
479
|
-
|
|
480
|
-
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
481
|
-
center = 0.5 * (self.lam_p + self.lam_m)
|
|
482
|
-
|
|
483
|
-
x_min = numpy.floor(extend * (center - extend * radius * scale))
|
|
484
|
-
x_max = numpy.ceil(extend * (center + extend * radius * scale))
|
|
485
|
-
|
|
486
|
-
x_min /= extend
|
|
487
|
-
x_max /= extend
|
|
488
|
-
|
|
489
|
-
return numpy.linspace(x_min, x_max, N)
|
|
490
|
-
|
|
491
467
|
# =======
|
|
492
468
|
# density
|
|
493
469
|
# =======
|
|
@@ -1025,338 +1001,3 @@ class FreeForm(object):
|
|
|
1025
1001
|
label='Decompression', latex=latex, save=save)
|
|
1026
1002
|
|
|
1027
1003
|
return rho, x
|
|
1028
|
-
|
|
1029
|
-
# ========
|
|
1030
|
-
# eigvalsh
|
|
1031
|
-
# ========
|
|
1032
|
-
|
|
1033
|
-
def eigvalsh(self, size=None, seed=None, **kwargs):
|
|
1034
|
-
"""
|
|
1035
|
-
Estimate the eigenvalues.
|
|
1036
|
-
|
|
1037
|
-
This function estimates the eigenvalues of the freeform matrix
|
|
1038
|
-
or a larger matrix containing it using free decompression.
|
|
1039
|
-
|
|
1040
|
-
Parameters
|
|
1041
|
-
----------
|
|
1042
|
-
|
|
1043
|
-
size : int, default=None
|
|
1044
|
-
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1045
|
-
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1046
|
-
:math:`\\mathbf{A}` itself.
|
|
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
|
-
eigs : numpy.array
|
|
1059
|
-
Eigenvalues of decompressed matrix
|
|
1060
|
-
|
|
1061
|
-
See Also
|
|
1062
|
-
--------
|
|
1063
|
-
|
|
1064
|
-
FreeForm.decompress
|
|
1065
|
-
FreeForm.cond
|
|
1066
|
-
|
|
1067
|
-
Notes
|
|
1068
|
-
-----
|
|
1069
|
-
|
|
1070
|
-
All arguments to the `.decompress()` procedure can be provided.
|
|
1071
|
-
|
|
1072
|
-
Examples
|
|
1073
|
-
--------
|
|
1074
|
-
|
|
1075
|
-
.. code-block:: python
|
|
1076
|
-
:emphasize-lines: 1
|
|
1077
|
-
|
|
1078
|
-
>>> from freealg import FreeForm
|
|
1079
|
-
"""
|
|
1080
|
-
|
|
1081
|
-
if size is None:
|
|
1082
|
-
size = self.n
|
|
1083
|
-
|
|
1084
|
-
rho, x = self.decompress(size, **kwargs)
|
|
1085
|
-
eigs = numpy.sort(sample(x, rho, size, method='qmc', seed=seed))
|
|
1086
|
-
|
|
1087
|
-
return eigs
|
|
1088
|
-
|
|
1089
|
-
# ====
|
|
1090
|
-
# cond
|
|
1091
|
-
# ====
|
|
1092
|
-
|
|
1093
|
-
def cond(self, size=None, seed=None, **kwargs):
|
|
1094
|
-
"""
|
|
1095
|
-
Estimate the condition number.
|
|
1096
|
-
|
|
1097
|
-
This function estimates the condition number of the matrix
|
|
1098
|
-
:math:`\\mathbf{A}` or a larger matrix containing :math:`\\mathbf{A}`
|
|
1099
|
-
using free decompression.
|
|
1100
|
-
|
|
1101
|
-
Parameters
|
|
1102
|
-
----------
|
|
1103
|
-
|
|
1104
|
-
size : int, default=None
|
|
1105
|
-
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1106
|
-
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1107
|
-
:math:`\\mathbf{A}` itself.
|
|
1108
|
-
|
|
1109
|
-
**kwargs : dict, optional
|
|
1110
|
-
Pass additional options to the underlying
|
|
1111
|
-
:func:`FreeForm.decompress` function.
|
|
1112
|
-
|
|
1113
|
-
Returns
|
|
1114
|
-
-------
|
|
1115
|
-
|
|
1116
|
-
c : float
|
|
1117
|
-
Condition number
|
|
1118
|
-
|
|
1119
|
-
See Also
|
|
1120
|
-
--------
|
|
1121
|
-
|
|
1122
|
-
FreeForm.eigvalsh
|
|
1123
|
-
FreeForm.norm
|
|
1124
|
-
FreeForm.slogdet
|
|
1125
|
-
FreeForm.trace
|
|
1126
|
-
|
|
1127
|
-
Examples
|
|
1128
|
-
--------
|
|
1129
|
-
|
|
1130
|
-
.. code-block:: python
|
|
1131
|
-
:emphasize-lines: 1
|
|
1132
|
-
|
|
1133
|
-
>>> from freealg import FreeForm
|
|
1134
|
-
"""
|
|
1135
|
-
|
|
1136
|
-
eigs = self.eigvalsh(size=size, **kwargs)
|
|
1137
|
-
return eigs.max() / eigs.min()
|
|
1138
|
-
|
|
1139
|
-
# =====
|
|
1140
|
-
# trace
|
|
1141
|
-
# =====
|
|
1142
|
-
|
|
1143
|
-
def trace(self, size=None, p=1.0, seed=None, **kwargs):
|
|
1144
|
-
"""
|
|
1145
|
-
Estimate the trace of a power.
|
|
1146
|
-
|
|
1147
|
-
This function estimates the trace of the matrix power
|
|
1148
|
-
:math:`\\mathbf{A}^p` of the freeform or that of a larger matrix
|
|
1149
|
-
containing it.
|
|
1150
|
-
|
|
1151
|
-
Parameters
|
|
1152
|
-
----------
|
|
1153
|
-
|
|
1154
|
-
size : int, default=None
|
|
1155
|
-
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1156
|
-
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1157
|
-
:math:`\\mathbf{A}` itself.
|
|
1158
|
-
|
|
1159
|
-
p : float, default=1.0
|
|
1160
|
-
The exponent :math:`p` in :math:`\\mathbf{A}^p`.
|
|
1161
|
-
|
|
1162
|
-
seed : int, default=None
|
|
1163
|
-
The seed for the Quasi-Monte Carlo sampler.
|
|
1164
|
-
|
|
1165
|
-
**kwargs : dict, optional
|
|
1166
|
-
Pass additional options to the underlying
|
|
1167
|
-
:func:`FreeForm.decompress` function.
|
|
1168
|
-
|
|
1169
|
-
Returns
|
|
1170
|
-
-------
|
|
1171
|
-
|
|
1172
|
-
trace : float
|
|
1173
|
-
matrix trace
|
|
1174
|
-
|
|
1175
|
-
See Also
|
|
1176
|
-
--------
|
|
1177
|
-
|
|
1178
|
-
FreeForm.eigvalsh
|
|
1179
|
-
FreeForm.cond
|
|
1180
|
-
FreeForm.slogdet
|
|
1181
|
-
FreeForm.norm
|
|
1182
|
-
|
|
1183
|
-
Notes
|
|
1184
|
-
-----
|
|
1185
|
-
|
|
1186
|
-
The trace is highly amenable to subsampling: under free decompression
|
|
1187
|
-
the average eigenvalue is assumed constant, so the trace increases
|
|
1188
|
-
linearly. Traces of powers fall back to :func:`eigvalsh`.
|
|
1189
|
-
All arguments to the `.decompress()` procedure can be provided.
|
|
1190
|
-
|
|
1191
|
-
Examples
|
|
1192
|
-
--------
|
|
1193
|
-
|
|
1194
|
-
.. code-block:: python
|
|
1195
|
-
:emphasize-lines: 1
|
|
1196
|
-
|
|
1197
|
-
>>> from freealg import FreeForm
|
|
1198
|
-
"""
|
|
1199
|
-
|
|
1200
|
-
if numpy.isclose(p, 1.0):
|
|
1201
|
-
return numpy.mean(self.eig) * (size / self.n)
|
|
1202
|
-
|
|
1203
|
-
eig = self.eigvalsh(size=size, seed=seed, **kwargs)
|
|
1204
|
-
return numpy.sum(eig ** p)
|
|
1205
|
-
|
|
1206
|
-
# =======
|
|
1207
|
-
# slogdet
|
|
1208
|
-
# =======
|
|
1209
|
-
|
|
1210
|
-
def slogdet(self, size=None, seed=None, **kwargs):
|
|
1211
|
-
"""
|
|
1212
|
-
Estimate the sign and logarithm of the determinant.
|
|
1213
|
-
|
|
1214
|
-
This function estimates the *slogdet* of the freeform or that of
|
|
1215
|
-
a larger matrix containing it using free decompression.
|
|
1216
|
-
|
|
1217
|
-
Parameters
|
|
1218
|
-
----------
|
|
1219
|
-
|
|
1220
|
-
size : int, default=None
|
|
1221
|
-
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1222
|
-
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1223
|
-
:math:`\\mathbf{A}` itself.
|
|
1224
|
-
|
|
1225
|
-
seed : int, default=None
|
|
1226
|
-
The seed for the Quasi-Monte Carlo sampler.
|
|
1227
|
-
|
|
1228
|
-
Returns
|
|
1229
|
-
-------
|
|
1230
|
-
|
|
1231
|
-
sign : float
|
|
1232
|
-
Sign of determinant
|
|
1233
|
-
|
|
1234
|
-
ld : float
|
|
1235
|
-
natural logarithm of the absolute value of the determinant
|
|
1236
|
-
|
|
1237
|
-
See Also
|
|
1238
|
-
--------
|
|
1239
|
-
|
|
1240
|
-
FreeForm.eigvalsh
|
|
1241
|
-
FreeForm.cond
|
|
1242
|
-
FreeForm.trace
|
|
1243
|
-
FreeForm.norm
|
|
1244
|
-
|
|
1245
|
-
Notes
|
|
1246
|
-
-----
|
|
1247
|
-
|
|
1248
|
-
All arguments to the `.decompress()` procedure can be provided.
|
|
1249
|
-
|
|
1250
|
-
Examples
|
|
1251
|
-
--------
|
|
1252
|
-
|
|
1253
|
-
.. code-block:: python
|
|
1254
|
-
:emphasize-lines: 1
|
|
1255
|
-
|
|
1256
|
-
>>> from freealg import FreeForm
|
|
1257
|
-
"""
|
|
1258
|
-
|
|
1259
|
-
eigs = self.eigvalsh(size=size, seed=seed, **kwargs)
|
|
1260
|
-
sign = numpy.prod(numpy.sign(eigs))
|
|
1261
|
-
ld = numpy.sum(numpy.log(numpy.abs(eigs)))
|
|
1262
|
-
return sign, ld
|
|
1263
|
-
|
|
1264
|
-
# ====
|
|
1265
|
-
# norm
|
|
1266
|
-
# ====
|
|
1267
|
-
|
|
1268
|
-
def norm(self, size=None, order=2, seed=None, **kwargs):
|
|
1269
|
-
"""
|
|
1270
|
-
Estimate the Schatten norm.
|
|
1271
|
-
|
|
1272
|
-
This function estimates the norm of the freeform or a larger
|
|
1273
|
-
matrix containing it using free decompression.
|
|
1274
|
-
|
|
1275
|
-
Parameters
|
|
1276
|
-
----------
|
|
1277
|
-
|
|
1278
|
-
size : int, default=None
|
|
1279
|
-
The size of the matrix containing :math:`\\mathbf{A}` to estimate
|
|
1280
|
-
eigenvalues of. If None, returns estimates of the eigenvalues of
|
|
1281
|
-
:math:`\\mathbf{A}` itself.
|
|
1282
|
-
|
|
1283
|
-
order : {float, ``''inf``, ``'-inf'``, ``'fro'``, ``'nuc'``}, default=2
|
|
1284
|
-
Order of the norm.
|
|
1285
|
-
|
|
1286
|
-
* float :math:`p`: Schatten p-norm.
|
|
1287
|
-
* ``'inf'``: Largest absolute eigenvalue
|
|
1288
|
-
:math:`\\max \\vert \\lambda_i \\vert)`
|
|
1289
|
-
* ``'-inf'``: Smallest absolute eigenvalue
|
|
1290
|
-
:math:`\\min \\vert \\lambda_i \\vert)`
|
|
1291
|
-
* ``'fro'``: Frobenius norm corresponding to :math:`p=2`
|
|
1292
|
-
* ``'nuc'``: Nuclear (or trace) norm corresponding to :math:`p=1`
|
|
1293
|
-
|
|
1294
|
-
seed : int, default=None
|
|
1295
|
-
The seed for the Quasi-Monte Carlo sampler.
|
|
1296
|
-
|
|
1297
|
-
**kwargs : dict, optional
|
|
1298
|
-
Pass additional options to the underlying
|
|
1299
|
-
:func:`FreeForm.decompress` function.
|
|
1300
|
-
|
|
1301
|
-
Returns
|
|
1302
|
-
-------
|
|
1303
|
-
|
|
1304
|
-
norm : float
|
|
1305
|
-
matrix norm
|
|
1306
|
-
|
|
1307
|
-
See Also
|
|
1308
|
-
--------
|
|
1309
|
-
|
|
1310
|
-
FreeForm.eigvalsh
|
|
1311
|
-
FreeForm.cond
|
|
1312
|
-
FreeForm.slogdet
|
|
1313
|
-
FreeForm.trace
|
|
1314
|
-
|
|
1315
|
-
Notes
|
|
1316
|
-
-----
|
|
1317
|
-
|
|
1318
|
-
Thes Schatten :math:`p`-norm is defined by
|
|
1319
|
-
|
|
1320
|
-
.. math::
|
|
1321
|
-
|
|
1322
|
-
\\Vert \\mathbf{A} \\Vert_p = \\left(
|
|
1323
|
-
\\sum_{i=1}^N \\vert \\lambda_i \\vert^p \\right)^{1/p}.
|
|
1324
|
-
|
|
1325
|
-
Examples
|
|
1326
|
-
--------
|
|
1327
|
-
|
|
1328
|
-
.. code-block:: python
|
|
1329
|
-
:emphasize-lines: 1
|
|
1330
|
-
|
|
1331
|
-
>>> from freealg import FreeForm
|
|
1332
|
-
"""
|
|
1333
|
-
|
|
1334
|
-
eigs = self.eigvalsh(size, seed=seed, **kwargs)
|
|
1335
|
-
|
|
1336
|
-
# Check order type and convert to float
|
|
1337
|
-
if order == 'nuc':
|
|
1338
|
-
order = 1
|
|
1339
|
-
elif order == 'fro':
|
|
1340
|
-
order = 2
|
|
1341
|
-
elif order == 'inf':
|
|
1342
|
-
order = float('inf')
|
|
1343
|
-
elif order == '-inf':
|
|
1344
|
-
order = -float('inf')
|
|
1345
|
-
elif not isinstance(order,
|
|
1346
|
-
(int, float, numpy.integer, numpy.floating)) \
|
|
1347
|
-
and not isinstance(order, (bool, numpy.bool_)):
|
|
1348
|
-
raise ValueError('"order" is invalid.')
|
|
1349
|
-
|
|
1350
|
-
# Compute norm
|
|
1351
|
-
if numpy.isinf(order) and not numpy.isneginf(order):
|
|
1352
|
-
norm_ = max(numpy.abs(eigs))
|
|
1353
|
-
|
|
1354
|
-
elif numpy.isneginf(order):
|
|
1355
|
-
norm_ = min(numpy.abs(eigs))
|
|
1356
|
-
|
|
1357
|
-
elif isinstance(order, (int, float, numpy.integer, numpy.floating)) \
|
|
1358
|
-
and not isinstance(order, (bool, numpy.bool_)):
|
|
1359
|
-
norm_q = numpy.sum(numpy.abs(eigs)**order)
|
|
1360
|
-
norm_ = norm_q**(1.0 / order)
|
|
1361
|
-
|
|
1362
|
-
return norm_
|
freealg/_util.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import numpy
|
|
15
15
|
import scipy
|
|
16
16
|
|
|
17
|
-
__all__ = ['resolve_complex_dtype', 'compute_eig', '
|
|
17
|
+
__all__ = ['resolve_complex_dtype', 'compute_eig', 'submatrix']
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
# =====================
|
|
@@ -72,24 +72,72 @@ def compute_eig(A, lower=False):
|
|
|
72
72
|
return eig
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
75
|
+
# =========
|
|
76
|
+
# submatrix
|
|
77
|
+
# =========
|
|
78
78
|
|
|
79
|
-
def
|
|
79
|
+
def submatrix(matrix, size, paired=True, seed=None):
|
|
80
80
|
"""
|
|
81
|
-
|
|
81
|
+
Randomly sample a submatrix from a larger matrix.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
|
|
86
|
+
matrix : numpy.ndarray
|
|
87
|
+
A 2D square array
|
|
88
|
+
|
|
89
|
+
size : int
|
|
90
|
+
Number of rows and columns of the submatrix
|
|
91
|
+
|
|
92
|
+
paired : bool, default=True
|
|
93
|
+
If `True`, the rows and columns are sampled with the same random
|
|
94
|
+
indices. If `False`, separate random indices are used for selecting
|
|
95
|
+
rows and columns.
|
|
96
|
+
|
|
97
|
+
seed : int, default=None
|
|
98
|
+
Seed for random number generation. If `None`, results will not be
|
|
99
|
+
reproducible.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
|
|
104
|
+
sub : numpy.ndarray
|
|
105
|
+
A 2D array with the number of rows/columns specified by ``size``.
|
|
106
|
+
|
|
107
|
+
See Also
|
|
108
|
+
--------
|
|
109
|
+
|
|
110
|
+
freealg.sample
|
|
111
|
+
|
|
112
|
+
Examples
|
|
113
|
+
--------
|
|
114
|
+
|
|
115
|
+
.. code-block:: python
|
|
116
|
+
:emphasize-lines: 5
|
|
117
|
+
|
|
118
|
+
>>> import numpy
|
|
119
|
+
>>> from freealg import submatrix
|
|
120
|
+
|
|
121
|
+
>>> A = numpy.random.randn(1000, 1000)
|
|
122
|
+
>>> B = submatrix(A, size=500, paired=True, seed=0)
|
|
82
123
|
"""
|
|
83
124
|
|
|
84
125
|
if matrix.shape[0] != matrix.shape[1]:
|
|
85
126
|
raise ValueError("Matrix must be square")
|
|
86
127
|
|
|
87
128
|
n = matrix.shape[0]
|
|
88
|
-
if
|
|
89
|
-
raise ValueError("Submatrix size cannot exceed matrix size")
|
|
129
|
+
if size > n:
|
|
130
|
+
raise ValueError("Submatrix size cannot exceed matrix size.")
|
|
90
131
|
|
|
91
132
|
rng = numpy.random.default_rng(seed)
|
|
92
|
-
idx = rng.choice(n, size=submatrix_size, replace=False)
|
|
93
|
-
idx = numpy.sort(idx) # optional, preserves original ordering
|
|
94
133
|
|
|
95
|
-
|
|
134
|
+
idx_row = rng.choice(n, size=size, replace=False)
|
|
135
|
+
idx_row = numpy.sort(idx_row) # optional, preserves original ordering
|
|
136
|
+
|
|
137
|
+
if paired:
|
|
138
|
+
idx_col = idx_row
|
|
139
|
+
else:
|
|
140
|
+
idx_col = rng.choice(n, size=size, replace=False)
|
|
141
|
+
idx_col = numpy.sort(idx_col) # optional, preserves original ordering
|
|
142
|
+
|
|
143
|
+
return matrix[numpy.ix_(idx_row, idx_col)]
|
|
@@ -15,7 +15,8 @@ from ._chiral_block import ChiralBlock
|
|
|
15
15
|
from ._deformed_wigner import DeformedWigner
|
|
16
16
|
from ._deformed_marchenko_pastur import DeformedMarchenkoPastur
|
|
17
17
|
from ._compound_poisson import CompoundPoisson
|
|
18
|
+
from ._fuss_catalan import FussCatalan
|
|
18
19
|
|
|
19
20
|
__all__ = ['MarchenkoPastur', 'Wigner', 'KestenMcKay', 'Wachter', 'Meixner',
|
|
20
21
|
'ChiralBlock', 'DeformedWigner', 'DeformedMarchenkoPastur',
|
|
21
|
-
'CompoundPoisson']
|
|
22
|
+
'CompoundPoisson', 'FussCatalan']
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright 2025, 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
|
|
6
|
+
# under the terms of the license found in the LICENSE.txt file in the root
|
|
7
|
+
# directory of this source tree.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# =======
|
|
11
|
+
# Imports
|
|
12
|
+
# =======
|
|
13
|
+
|
|
14
|
+
import numpy
|
|
15
|
+
from scipy.interpolate import interp1d
|
|
16
|
+
from ..visualization._plot_util import plot_samples
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from scipy.integrate import cumtrapz
|
|
20
|
+
except ImportError:
|
|
21
|
+
from scipy.integrate import cumulative_trapezoid as cumtrapz
|
|
22
|
+
from scipy.stats import qmc
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# =================
|
|
26
|
+
# Base Distribution
|
|
27
|
+
# =================
|
|
28
|
+
|
|
29
|
+
class BaseDistribution(object):
|
|
30
|
+
"""
|
|
31
|
+
Base class for distributions.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# ====
|
|
35
|
+
# init
|
|
36
|
+
# ====
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
"""
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
self.lam_m = None
|
|
43
|
+
self.lam_p = None
|
|
44
|
+
|
|
45
|
+
# ======
|
|
46
|
+
# sample
|
|
47
|
+
# ======
|
|
48
|
+
|
|
49
|
+
def sample(self, size, x_min=None, x_max=None, method='qmc', seed=None,
|
|
50
|
+
plot=False, latex=False, save=False):
|
|
51
|
+
"""
|
|
52
|
+
Sample from distribution.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
|
|
57
|
+
size : int
|
|
58
|
+
Size of sample.
|
|
59
|
+
|
|
60
|
+
x_min : float, default=None
|
|
61
|
+
Minimum of sample values. If `None`, the left edge of the support
|
|
62
|
+
is used.
|
|
63
|
+
|
|
64
|
+
x_max : float, default=None
|
|
65
|
+
Maximum of sample values. If `None`, the right edge of the support
|
|
66
|
+
is used.
|
|
67
|
+
|
|
68
|
+
method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
|
|
69
|
+
Method of drawing samples from uniform distribution:
|
|
70
|
+
|
|
71
|
+
* ``'mc'``: Monte Carlo
|
|
72
|
+
* ``'qmc'``: Quasi Monte Carlo
|
|
73
|
+
|
|
74
|
+
seed : int, default=None,
|
|
75
|
+
Seed for random number generator.
|
|
76
|
+
|
|
77
|
+
plot : bool, default=False
|
|
78
|
+
If `True`, samples histogram is plotted.
|
|
79
|
+
|
|
80
|
+
latex : bool, default=False
|
|
81
|
+
If `True`, the plot is rendered using LaTeX. This option is
|
|
82
|
+
relevant only if ``plot=True``.
|
|
83
|
+
|
|
84
|
+
save : bool, default=False
|
|
85
|
+
If not `False`, the plot is saved. If a string is given, it is
|
|
86
|
+
assumed to the save filename (with the file extension). This option
|
|
87
|
+
is relevant only if ``plot=True``.
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
|
|
92
|
+
s : numpy.ndarray
|
|
93
|
+
Samples.
|
|
94
|
+
|
|
95
|
+
Notes
|
|
96
|
+
-----
|
|
97
|
+
|
|
98
|
+
This method uses inverse transform sampling.
|
|
99
|
+
|
|
100
|
+
Examples
|
|
101
|
+
--------
|
|
102
|
+
|
|
103
|
+
.. code-block::python
|
|
104
|
+
|
|
105
|
+
>>> from freealg.distributions import KestenMcKay
|
|
106
|
+
>>> km = KestenMcKay(3)
|
|
107
|
+
>>> s = km.sample(2000)
|
|
108
|
+
|
|
109
|
+
.. image:: ../_static/images/plots/km_samples.png
|
|
110
|
+
:align: center
|
|
111
|
+
:class: custom-dark
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
if x_min is None:
|
|
115
|
+
x_min = self.lam_m
|
|
116
|
+
|
|
117
|
+
if x_max is None:
|
|
118
|
+
x_max = self.lam_p
|
|
119
|
+
|
|
120
|
+
# Grid and PDF
|
|
121
|
+
xs = numpy.linspace(x_min, x_max, size)
|
|
122
|
+
pdf = self.density(xs)
|
|
123
|
+
|
|
124
|
+
# CDF (using cumulative trapezoidal rule)
|
|
125
|
+
cdf = cumtrapz(pdf, xs, initial=0)
|
|
126
|
+
cdf /= cdf[-1] # normalize CDF to 1
|
|
127
|
+
|
|
128
|
+
# Inverse CDF interpolator
|
|
129
|
+
inv_cdf = interp1d(cdf, xs, bounds_error=False,
|
|
130
|
+
fill_value=(x_min, x_max))
|
|
131
|
+
|
|
132
|
+
# Random generator
|
|
133
|
+
rng = numpy.random.default_rng(seed)
|
|
134
|
+
|
|
135
|
+
# Draw from uniform distribution
|
|
136
|
+
if method == 'mc':
|
|
137
|
+
u = rng.random(size)
|
|
138
|
+
|
|
139
|
+
elif method == 'qmc':
|
|
140
|
+
try:
|
|
141
|
+
engine = qmc.Halton(d=1, scramble=True, rng=rng)
|
|
142
|
+
except TypeError:
|
|
143
|
+
# Older scipy versions
|
|
144
|
+
engine = qmc.Halton(d=1, scramble=True, seed=rng)
|
|
145
|
+
u = engine.random(size).ravel()
|
|
146
|
+
|
|
147
|
+
else:
|
|
148
|
+
raise NotImplementedError('"method" is invalid.')
|
|
149
|
+
|
|
150
|
+
# Draw from distribution by mapping from inverse CDF
|
|
151
|
+
samples = inv_cdf(u).ravel()
|
|
152
|
+
|
|
153
|
+
if plot:
|
|
154
|
+
radius = 0.5 * (self.lam_p - self.lam_m)
|
|
155
|
+
center = 0.5 * (self.lam_p + self.lam_m)
|
|
156
|
+
scale = 1.25
|
|
157
|
+
x_min = numpy.floor(center - radius * scale)
|
|
158
|
+
x_max = numpy.ceil(center + radius * scale)
|
|
159
|
+
x = numpy.linspace(x_min, x_max, 500)
|
|
160
|
+
rho = self.density(x)
|
|
161
|
+
plot_samples(x, rho, x_min, x_max, samples, latex=latex, save=save)
|
|
162
|
+
|
|
163
|
+
return samples
|