freealg 0.1.11__py3-none-any.whl → 0.7.12__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.
Files changed (59) hide show
  1. freealg/__init__.py +8 -2
  2. freealg/__version__.py +1 -1
  3. freealg/_algebraic_form/__init__.py +12 -0
  4. freealg/_algebraic_form/_branch_points.py +288 -0
  5. freealg/_algebraic_form/_constraints.py +139 -0
  6. freealg/_algebraic_form/_continuation_algebraic.py +706 -0
  7. freealg/_algebraic_form/_decompress.py +641 -0
  8. freealg/_algebraic_form/_decompress2.py +204 -0
  9. freealg/_algebraic_form/_edge.py +330 -0
  10. freealg/_algebraic_form/_homotopy.py +323 -0
  11. freealg/_algebraic_form/_moments.py +448 -0
  12. freealg/_algebraic_form/_sheets_util.py +145 -0
  13. freealg/_algebraic_form/_support.py +309 -0
  14. freealg/_algebraic_form/algebraic_form.py +1232 -0
  15. freealg/_free_form/__init__.py +16 -0
  16. freealg/{_chebyshev.py → _free_form/_chebyshev.py} +75 -43
  17. freealg/_free_form/_decompress.py +993 -0
  18. freealg/_free_form/_density_util.py +243 -0
  19. freealg/_free_form/_jacobi.py +359 -0
  20. freealg/_free_form/_linalg.py +508 -0
  21. freealg/{_pade.py → _free_form/_pade.py} +42 -208
  22. freealg/{_plot_util.py → _free_form/_plot_util.py} +37 -22
  23. freealg/{_sample.py → _free_form/_sample.py} +58 -22
  24. freealg/_free_form/_series.py +454 -0
  25. freealg/_free_form/_support.py +214 -0
  26. freealg/_free_form/free_form.py +1362 -0
  27. freealg/_geometric_form/__init__.py +13 -0
  28. freealg/_geometric_form/_continuation_genus0.py +175 -0
  29. freealg/_geometric_form/_continuation_genus1.py +275 -0
  30. freealg/_geometric_form/_elliptic_functions.py +174 -0
  31. freealg/_geometric_form/_sphere_maps.py +63 -0
  32. freealg/_geometric_form/_torus_maps.py +118 -0
  33. freealg/_geometric_form/geometric_form.py +1094 -0
  34. freealg/_util.py +56 -110
  35. freealg/distributions/__init__.py +7 -1
  36. freealg/distributions/_chiral_block.py +494 -0
  37. freealg/distributions/_deformed_marchenko_pastur.py +726 -0
  38. freealg/distributions/_deformed_wigner.py +386 -0
  39. freealg/distributions/_kesten_mckay.py +29 -15
  40. freealg/distributions/_marchenko_pastur.py +224 -95
  41. freealg/distributions/_meixner.py +47 -37
  42. freealg/distributions/_wachter.py +29 -17
  43. freealg/distributions/_wigner.py +27 -14
  44. freealg/visualization/__init__.py +12 -0
  45. freealg/visualization/_glue_util.py +32 -0
  46. freealg/visualization/_rgb_hsv.py +125 -0
  47. freealg-0.7.12.dist-info/METADATA +172 -0
  48. freealg-0.7.12.dist-info/RECORD +53 -0
  49. {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/WHEEL +1 -1
  50. freealg/_decompress.py +0 -180
  51. freealg/_jacobi.py +0 -218
  52. freealg/_support.py +0 -85
  53. freealg/freeform.py +0 -967
  54. freealg-0.1.11.dist-info/METADATA +0 -140
  55. freealg-0.1.11.dist-info/RECORD +0 -24
  56. /freealg/{_damp.py → _free_form/_damp.py} +0 -0
  57. {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/licenses/AUTHORS.txt +0 -0
  58. {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/licenses/LICENSE.txt +0 -0
  59. {freealg-0.1.11.dist-info → freealg-0.7.12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,508 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause
2
+ # SPDX-FileType: SOURCE
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify it under
5
+ # the terms of the license found in the LICENSE.txt file in the root directory
6
+ # of this source tree.
7
+
8
+
9
+ # =======
10
+ # Imports
11
+ # =======
12
+
13
+ import numpy
14
+ from .._util import compute_eig
15
+ from .free_form import FreeForm
16
+
17
+ __all__ = ['eigvalsh', 'cond', 'norm', 'trace', 'slogdet']
18
+
19
+
20
+ # ===============
21
+ # subsample apply
22
+ # ===============
23
+
24
+ def _subsample_apply(f, A, output_array=False, seed=None):
25
+ """
26
+ Compute f(A_n) over subsamples A_n of A. If the output of
27
+ f is an array (e.g. eigvals), specify output_array to be True.
28
+ """
29
+
30
+ if (A.ndim != 2) or (A.shape[0] != A.shape[1]):
31
+ raise RuntimeError("Only square matrices are permitted.")
32
+
33
+ n = A.shape[0]
34
+
35
+ # Size of sample matrix
36
+ n_s = int(80.0 * (1.0 + numpy.log(n)))
37
+
38
+ # If matrix is not large enough, return eigenvalues
39
+ if n < n_s:
40
+ return f(A), n, n
41
+
42
+ # Number of samples
43
+ num_samples = int(10 * (n / n_s)**0.5)
44
+
45
+ # Collect eigenvalue samples
46
+ samples = []
47
+ rng = numpy.random.default_rng(seed=seed)
48
+ for _ in range(num_samples):
49
+ indices = rng.choice(n, n_s, replace=False)
50
+ samples.append(f(A[numpy.ix_(indices, indices)]))
51
+
52
+ if output_array:
53
+ return numpy.concatenate(samples), n, n_s
54
+ else:
55
+ return numpy.array(samples), n, n_s
56
+
57
+
58
+ # ========
59
+ # eigvalsh
60
+ # ========
61
+
62
+ def eigvalsh(A, size=None, psd=None, seed=None, plot=False, **kwargs):
63
+ """
64
+ Estimate the eigenvalues of a matrix.
65
+
66
+ This function estimates the eigenvalues of the matrix :math:`\\mathbf{A}`
67
+ or a larger matrix containing :math:`\\mathbf{A}` using free decompression.
68
+
69
+ Parameters
70
+ ----------
71
+
72
+ A : numpy.ndarray
73
+ The symmetric real-valued matrix :math:`\\mathbf{A}` whose eigenvalues
74
+ (or those of a matrix containing :math:`\\mathbf{A}`) are to be
75
+ computed.
76
+
77
+ size : int, default=None
78
+ The size of the matrix containing :math:`\\mathbf{A}` to estimate
79
+ eigenvalues of. If None, returns estimates of the eigenvalues of
80
+ :math:`\\mathbf{A}` itself.
81
+
82
+ psd : bool, default=None
83
+ Determines whether the matrix is positive-semidefinite (PSD; all
84
+ eigenvalues are non-negative). If `None`, the matrix is considered PSD
85
+ if all sampled eigenvalues are positive.
86
+
87
+ seed : int, default=None
88
+ The seed for sampling rows/columns of matirx as well as the Quasi-Monte
89
+ Carlo sampler for eigenvalues from density.
90
+
91
+ plot : bool, default=False
92
+ Print out all relevant plots for diagnosing eigenvalue accuracy.
93
+
94
+ **kwargs : dict, optional
95
+ Pass additional options to the underlying
96
+ :func:`FreeForm.decompress` function.
97
+
98
+ Returns
99
+ -------
100
+
101
+ eigs : numpy.array
102
+ Eigenvalues of decompressed matrix
103
+
104
+ See Also
105
+ --------
106
+
107
+ cond
108
+
109
+ Notes
110
+ -----
111
+
112
+ This is a convenience function for the :class:`freealg.FreeForm` class with
113
+ some effective defaults that work well for common random matrix ensembles.
114
+ For improved performance and plotting utilities, consider fine-tuning
115
+ parameters using the FreeForm class.
116
+
117
+ References
118
+ ----------
119
+
120
+ .. [1] Reference.
121
+
122
+ Examples
123
+ --------
124
+
125
+ .. code-block:: python
126
+ :emphasize-lines: 6
127
+
128
+ >>> from freealg import cond
129
+ >>> from freealg.distributions import MarchenkoPastur
130
+
131
+ >>> mp = MarchenkoPastur(1/50)
132
+ >>> A = mp.matrix(3000)
133
+ >>> eigs = eigvalsh(A)
134
+ """
135
+
136
+ samples, n, n_s = _subsample_apply(compute_eig, A, output_array=True,
137
+ seed=seed)
138
+
139
+ if size is None:
140
+ size = n
141
+
142
+ # If all eigenvalues are positive, set PSD flag
143
+ if psd is None:
144
+ psd = samples.min() > 0
145
+
146
+ ff = FreeForm(samples)
147
+
148
+ # Since we are resampling, we need to provide the correct matrix size
149
+ ff.n = n_s
150
+
151
+ # Perform fit and estimate eigenvalues
152
+ order = 1 + int(len(samples)**0.2)
153
+ ff.fit(method='chebyshev', K=order, projection='sample',
154
+ continuation='wynn-eps', force=True, plot=False, latex=False,
155
+ save=False)
156
+
157
+ if plot:
158
+ ff.density(plot=True)
159
+ ff.stieltjes(plot=True)
160
+
161
+ # Sampling method using Pade seems to need a lower tolerance to properly
162
+ # work. Here we set defaults unless user provides otherwise. Note that the
163
+ # default of tolerance in ff._decompress is much larger (1e-4) for other
164
+ # methods (Newton, and non-sampling projections such as Gaussian and beta)
165
+ # to work properly.
166
+ kwargs.setdefault('tolerance', 1e-9)
167
+ kwargs.setdefault('method', 'secant')
168
+
169
+ eigs = ff.eigvalsh(size, seed=seed, plot=plot, **kwargs)
170
+
171
+ if psd:
172
+ eigs = numpy.abs(eigs)
173
+ eigs.sort()
174
+
175
+ return eigs
176
+
177
+
178
+ # ====
179
+ # cond
180
+ # ====
181
+
182
+ def cond(A, size=None, seed=None, **kwargs):
183
+ """
184
+ Estimate the condition number of a Hermitian positive-definite matrix.
185
+
186
+ This function estimates the condition number of the matrix
187
+ :math:`\\mathbf{A}` or a larger matrix containing :math:`\\mathbf{A}`
188
+ using free decompression.
189
+
190
+ Parameters
191
+ ----------
192
+
193
+ A : numpy.ndarray
194
+ The symmetric real-valued matrix :math:`\\mathbf{A}` whose condition
195
+ number (or that of a matrix containing :math:`\\mathbf{A}`) are to be
196
+ computed.
197
+
198
+ size : int, default=None
199
+ The size of the matrix containing :math:`\\mathbf{A}` to estimate
200
+ eigenvalues of. If None, returns estimates of the eigenvalues of
201
+ :math:`\\mathbf{A}` itself.
202
+
203
+ seed : int, default=None
204
+ The seed for the Quasi-Monte Carlo sampler.
205
+
206
+ **kwargs : dict, optional
207
+ Pass additional options to the underlying
208
+ :func:`FreeForm.decompress` function.
209
+
210
+ Returns
211
+ -------
212
+
213
+ c : float
214
+ Condition number
215
+
216
+ See Also
217
+ --------
218
+
219
+ eigvalsh
220
+ norm
221
+ slogdet
222
+ trace
223
+
224
+ Notes
225
+ -----
226
+
227
+ This is a convenience function using :func:`freealg.eigvalsh`.
228
+
229
+ Examples
230
+ --------
231
+
232
+ .. code-block:: python
233
+ :emphasize-lines: 6
234
+
235
+ >>> from freealg import cond
236
+ >>> from freealg.distributions import MarchenkoPastur
237
+
238
+ >>> mp = MarchenkoPastur(1/50)
239
+ >>> A = mp.matrix(3000)
240
+ >>> cond(A)
241
+ """
242
+
243
+ eigs = eigvalsh(A, size=size, psd=True, seed=seed, **kwargs)
244
+ return eigs.max() / eigs.min()
245
+
246
+
247
+ # ====
248
+ # norm
249
+ # ====
250
+
251
+ def norm(A, size=None, order=2, seed=None, **kwargs):
252
+ """
253
+ Estimate the Schatten norm of a Hermitian matrix.
254
+
255
+ This function estimates the norm of the matrix :math:`\\mathbf{A}` or a
256
+ larger matrix containing :math:`\\mathbf{A}` using free decompression.
257
+
258
+ Parameters
259
+ ----------
260
+
261
+ A : numpy.ndarray
262
+ The symmetric real-valued matrix :math:`\\mathbf{A}` whose condition
263
+ number (or that of a matrix containing :math:`\\mathbf{A}`) are to be
264
+ computed.
265
+
266
+ size : int, default=None
267
+ The size of the matrix containing :math:`\\mathbf{A}` to estimate
268
+ eigenvalues of. If None, returns estimates of the eigenvalues of
269
+ :math:`\\mathbf{A}` itself.
270
+
271
+ order : {float, ``''inf``, ``'-inf'``, ``'fro'``, ``'nuc'``}, default=2
272
+ Order of the norm.
273
+
274
+ * float :math:`p`: Schatten p-norm.
275
+ * ``'inf'``: Largest absolute eigenvalue
276
+ :math:`\\max \\vert \\lambda_i \\vert)`
277
+ * ``'-inf'``: Smallest absolute eigenvalue
278
+ :math:`\\min \\vert \\lambda_i \\vert)`
279
+ * ``'fro'``: Frobenius norm corresponding to :math:`p=2`
280
+ * ``'nuc'``: Nuclear (or trace) norm corresponding to :math:`p=1`
281
+
282
+ seed : int, default=None
283
+ The seed for the Quasi-Monte Carlo sampler.
284
+
285
+ **kwargs : dict, optional
286
+ Pass additional options to the underlying
287
+ :func:`FreeForm.decompress` function.
288
+
289
+ Returns
290
+ -------
291
+
292
+ norm : float
293
+ matrix norm
294
+
295
+ See Also
296
+ --------
297
+
298
+ eigvalsh
299
+ cond
300
+ slogdet
301
+ trace
302
+
303
+ Notes
304
+ -----
305
+
306
+ Thes Schatten :math:`p`-norm is defined by
307
+
308
+ .. math::
309
+
310
+ \\Vert \\mathbf{A} \\Vert_p = \\left(
311
+ \\sum_{i=1}^N \\vert \\lambda_i \\vert^p \\right)^{1/p}.
312
+
313
+ Examples
314
+ --------
315
+
316
+ .. code-block:: python
317
+ :emphasize-lines: 6
318
+
319
+ >>> from freealg import norm
320
+ >>> from freealg.distributions import MarchenkoPastur
321
+
322
+ >>> mp = MarchenkoPastur(1/50)
323
+ >>> A = mp.matrix(3000)
324
+ >>> norm(A, 100_000, order='fro')
325
+ """
326
+
327
+ eigs = eigvalsh(A, size=size, seed=seed, **kwargs)
328
+
329
+ # Check order type and convert to float
330
+ if order == 'nuc':
331
+ order = 1
332
+ elif order == 'fro':
333
+ order = 2
334
+ elif order == 'inf':
335
+ order = float('inf')
336
+ elif order == '-inf':
337
+ order = -float('inf')
338
+ elif not isinstance(order,
339
+ (int, float, numpy.integer, numpy.floating)) \
340
+ and not isinstance(order, (bool, numpy.bool_)):
341
+ raise ValueError('"order" is invalid.')
342
+
343
+ # Compute norm
344
+ if numpy.isinf(order) and not numpy.isneginf(order):
345
+ norm_ = max(numpy.abs(eigs))
346
+
347
+ elif numpy.isneginf(order):
348
+ norm_ = min(numpy.abs(eigs))
349
+
350
+ elif isinstance(order, (int, float, numpy.integer, numpy.floating)) \
351
+ and not isinstance(order, (bool, numpy.bool_)):
352
+ norm_q = numpy.sum(numpy.abs(eigs)**order)
353
+ norm_ = norm_q**(1.0 / order)
354
+
355
+ return norm_
356
+
357
+
358
+ # =====
359
+ # trace
360
+ # =====
361
+
362
+ def trace(A, N=None, p=1.0, seed=None, **kwargs):
363
+ """
364
+ Estimate the trace of a power of a Hermitian matrix.
365
+
366
+ This function estimates the trace of the matrix power :math:`\\mathbf{A}^p`
367
+ or that of a larger matrix containing :math:`\\mathbf{A}`.
368
+
369
+ Parameters
370
+ ----------
371
+
372
+ A : numpy.ndarray
373
+ The symmetric real-valued matrix :math:`\\mathbf{A}` whose trace of
374
+ a power (or that of a matrix containing :math:`\\mathbf{A}`) is to be
375
+ computed.
376
+
377
+ size : int, default=None
378
+ The size of the matrix containing :math:`\\mathbf{A}` to estimate
379
+ eigenvalues of. If None, returns estimates of the eigenvalues of
380
+ :math:`\\mathbf{A}` itself.
381
+
382
+ p : float, default=1.0
383
+ The exponent :math:`p` in :math:`\\mathbf{A}^p`.
384
+
385
+ seed : int, default=None
386
+ The seed for the Quasi-Monte Carlo sampler.
387
+
388
+ **kwargs : dict, optional
389
+ Pass additional options to the underlying
390
+ :func:`FreeForm.decompress` function.
391
+
392
+ Returns
393
+ -------
394
+
395
+ trace : float
396
+ matrix trace
397
+
398
+ See Also
399
+ --------
400
+
401
+ eigvalsh
402
+ cond
403
+ slogdet
404
+ norm
405
+
406
+ Notes
407
+ -----
408
+
409
+ The trace is highly amenable to subsampling: under free decompression
410
+ the average eigenvalue is assumed constant, so the trace increases
411
+ linearly. Traces of powers fall back to :func:`freealg.eigvalsh`.
412
+
413
+ Examples
414
+ --------
415
+
416
+ .. code-block:: python
417
+ :emphasize-lines: 6
418
+
419
+ >>> from freealg import norm
420
+ >>> from freealg.distributions import MarchenkoPastur
421
+
422
+ >>> mp = MarchenkoPastur(1/50)
423
+ >>> A = mp.matrix(3000)
424
+ >>> trace(A, 100_000)
425
+ """
426
+
427
+ if numpy.isclose(p, 1.0):
428
+ samples, n, n_s = _subsample_apply(numpy.trace, A, output_array=False)
429
+ if N is None:
430
+ size = n
431
+ return numpy.mean(samples) * (size / n_s)
432
+
433
+ eig = eigvalsh(A, size=size, seed=seed, **kwargs)
434
+ return numpy.sum(eig ** p)
435
+
436
+
437
+ # =======
438
+ # slogdet
439
+ # =======
440
+
441
+ def slogdet(A, size=None, seed=None, **kwargs):
442
+ """
443
+ Estimate the sign and logarithm of the determinant of a Hermitian matrix.
444
+
445
+ This function estimates the *slogdet* of the matrix :math:`\\mathbf{A}` or
446
+ a larger matrix containing :math:`\\mathbf{A}` using free decompression.
447
+
448
+ Parameters
449
+ ----------
450
+
451
+ A : numpy.ndarray
452
+ The symmetric real-valued matrix :math:`\\mathbf{A}` whose condition
453
+ number (or that of a matrix containing :math:`\\mathbf{A}`) are to be
454
+ computed.
455
+
456
+ size : int, default=None
457
+ The size of the matrix containing :math:`\\mathbf{A}` to estimate
458
+ eigenvalues of. If None, returns estimates of the eigenvalues of
459
+ :math:`\\mathbf{A}` itself.
460
+
461
+ seed : int, default=None
462
+ The seed for the Quasi-Monte Carlo sampler.
463
+
464
+ **kwargs : dict, optional
465
+ Pass additional options to the underlying
466
+ :func:`FreeForm.decompress` function.
467
+
468
+ Returns
469
+ -------
470
+
471
+ sign : float
472
+ Sign of determinant
473
+
474
+ ld : float
475
+ natural logarithm of the absolute value of the determinant
476
+
477
+ See Also
478
+ --------
479
+
480
+ eigvalsh
481
+ cond
482
+ trace
483
+ norm
484
+
485
+ Notes
486
+ -----
487
+
488
+ This is a convenience function using :func:`freealg.eigvalsh`.
489
+
490
+ Examples
491
+ --------
492
+
493
+ .. code-block:: python
494
+ :emphasize-lines: 6
495
+
496
+ >>> from freealg import norm
497
+ >>> from freealg.distributions import MarchenkoPastur
498
+
499
+ >>> mp = MarchenkoPastur(1/50)
500
+ >>> A = mp.matrix(3000)
501
+ >>> sign, ld = slogdet(A, 100_000)
502
+ """
503
+
504
+ eigs = eigvalsh(A, size=size, seed=seed, **kwargs)
505
+ sign = numpy.prod(numpy.sign(eigs))
506
+ ld = numpy.sum(numpy.log(numpy.abs(eigs)))
507
+
508
+ return sign, ld