freealg 0.1.12__py3-none-any.whl → 0.1.14__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.
@@ -110,7 +110,7 @@ class KestenMcKay(object):
110
110
  # density
111
111
  # =======
112
112
 
113
- def density(self, x=None, plot=False, latex=False, save=False):
113
+ def density(self, x=None, plot=False, latex=False, save=False, eig=None):
114
114
  """
115
115
  Density of distribution.
116
116
 
@@ -137,6 +137,10 @@ class KestenMcKay(object):
137
137
  assumed to the save filename (with the file extension). This option
138
138
  is relevant only if ``plot=True``.
139
139
 
140
+ eig : numpy.array, default=None
141
+ A collection of eigenvalues to compare to via histogram. This
142
+ option is relevant only if ``plot=True``.
143
+
140
144
  Returns
141
145
  -------
142
146
 
@@ -173,7 +177,11 @@ class KestenMcKay(object):
173
177
  numpy.sqrt(4.0 * (self.d - 1.0) - x[mask]**2)
174
178
 
175
179
  if plot:
176
- plot_density(x, rho, label='', latex=latex, save=save)
180
+ if eig is not None:
181
+ label = 'Estimate'
182
+ else:
183
+ label = ''
184
+ plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
177
185
 
178
186
  return rho
179
187
 
@@ -539,9 +547,9 @@ class KestenMcKay(object):
539
547
 
540
548
  return samples
541
549
 
542
- # ============
543
- # Haar unitary
544
- # ============
550
+ # ===============
551
+ # haar orthogonal
552
+ # ===============
545
553
 
546
554
  def _haar_orthogonal(self, n, k, seed=None):
547
555
  """
@@ -108,7 +108,7 @@ class MarchenkoPastur(object):
108
108
  # density
109
109
  # =======
110
110
 
111
- def density(self, x=None, plot=False, latex=False, save=False):
111
+ def density(self, x=None, plot=False, latex=False, save=False, eig=None):
112
112
  """
113
113
  Density of distribution.
114
114
 
@@ -135,6 +135,10 @@ class MarchenkoPastur(object):
135
135
  assumed to the save filename (with the file extension). This option
136
136
  is relevant only if ``plot=True``.
137
137
 
138
+ eig : numpy.array, default=None
139
+ A collection of eigenvalues to compare to via histogram. This
140
+ option is relevant only if ``plot=True``.
141
+
138
142
  Returns
139
143
  -------
140
144
 
@@ -171,7 +175,11 @@ class MarchenkoPastur(object):
171
175
  numpy.sqrt((self.lam_p - x[mask]) * (x[mask] - self.lam_m))
172
176
 
173
177
  if plot:
174
- plot_density(x, rho, label='', latex=latex, save=save)
178
+ if eig is not None:
179
+ label = 'Estimate'
180
+ else:
181
+ label = ''
182
+ plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
175
183
 
176
184
  return rho
177
185
 
@@ -114,7 +114,7 @@ class Meixner(object):
114
114
  # density
115
115
  # =======
116
116
 
117
- def density(self, x=None, plot=False, latex=False, save=False):
117
+ def density(self, x=None, plot=False, latex=False, save=False, eig=None):
118
118
  """
119
119
  Density of distribution.
120
120
 
@@ -141,6 +141,10 @@ class Meixner(object):
141
141
  assumed to the save filename (with the file extension). This option
142
142
  is relevant only if ``plot=True``.
143
143
 
144
+ eig : numpy.array, default=None
145
+ A collection of eigenvalues to compare to via histogram. This
146
+ option is relevant only if ``plot=True``.
147
+
144
148
  Returns
145
149
  -------
146
150
 
@@ -188,7 +192,11 @@ class Meixner(object):
188
192
  rho[mask] = numer[mask] / denom[mask]
189
193
 
190
194
  if plot:
191
- plot_density(x, rho, label='', latex=latex, save=save)
195
+ if eig is not None:
196
+ label = 'Estimate'
197
+ else:
198
+ label = ''
199
+ plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
192
200
 
193
201
  return rho
194
202
 
@@ -115,7 +115,7 @@ class Wachter(object):
115
115
  # density
116
116
  # =======
117
117
 
118
- def density(self, x=None, plot=False, latex=False, save=False):
118
+ def density(self, x=None, plot=False, latex=False, save=False, eig=None):
119
119
  """
120
120
  Density of distribution.
121
121
 
@@ -142,6 +142,10 @@ class Wachter(object):
142
142
  assumed to the save filename (with the file extension). This option
143
143
  is relevant only if ``plot=True``.
144
144
 
145
+ eig : numpy.array, default=None
146
+ A collection of eigenvalues to compare to via histogram. This
147
+ option is relevant only if ``plot=True``.
148
+
145
149
  Returns
146
150
  -------
147
151
 
@@ -179,7 +183,11 @@ class Wachter(object):
179
183
  numpy.sqrt((self.lam_p - x[mask]) * (x[mask] - self.lam_m))
180
184
 
181
185
  if plot:
182
- plot_density(x, rho, label='', latex=latex, save=save)
186
+ if eig is not None:
187
+ label = 'Estimate'
188
+ else:
189
+ label = ''
190
+ plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
183
191
 
184
192
  return rho
185
193
 
@@ -96,7 +96,7 @@ class Wigner(object):
96
96
  # density
97
97
  # =======
98
98
 
99
- def density(self, x=None, plot=False, latex=False, save=False):
99
+ def density(self, x=None, plot=False, latex=False, save=False, eig=None):
100
100
  """
101
101
  Density of distribution.
102
102
 
@@ -123,6 +123,10 @@ class Wigner(object):
123
123
  assumed to the save filename (with the file extension). This option
124
124
  is relevant only if ``plot=True``.
125
125
 
126
+ eig : numpy.array, default=None
127
+ A collection of eigenvalues to compare to via histogram. This
128
+ option is relevant only if ``plot=True``.
129
+
126
130
  Returns
127
131
  -------
128
132
 
@@ -159,7 +163,11 @@ class Wigner(object):
159
163
  numpy.sqrt(self.r**2 - x[mask]**2)
160
164
 
161
165
  if plot:
162
- plot_density(x, rho, label='', latex=latex, save=save)
166
+ if eig is not None:
167
+ label = 'Estimate'
168
+ else:
169
+ label = ''
170
+ plot_density(x, rho, label=label, latex=latex, save=save, eig=eig)
163
171
 
164
172
  return rho
165
173
 
freealg/eigfree.py ADDED
@@ -0,0 +1,120 @@
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 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 compute_eig
16
+ from .freeform import FreeForm
17
+
18
+ __all__ = ['eigfree']
19
+
20
+
21
+ # ========
22
+ # eig free
23
+ # ========
24
+
25
+
26
+ def eigfree(A, N=None, psd=None, plots=False):
27
+ """
28
+ Estimate the eigenvalues of a matrix.
29
+
30
+ This function estimates the eigenvalues of the matrix :math:`\\mathbf{A}`
31
+ or a larger matrix containing :math:`\\mathbf{A}` using free decompression.
32
+
33
+ Parameters
34
+ ----------
35
+
36
+ A : numpy.ndarray
37
+ The symmetric real-valued matrix :math:`\\mathbf{A}` whose eigenvalues
38
+ (or those of a matrix containing :math:`\\mathbf{A}`) are to be
39
+ computed.
40
+
41
+ N : int, default=None
42
+ The size of the matrix containing :math:`\\mathbf{A}` to estimate
43
+ eigenvalues of. If None, returns estimates of the eigenvalues of
44
+ :math:`\\mathbf{A}` itself.
45
+
46
+ psd: bool, default=None
47
+ Determines whether the matrix is positive-semidefinite (PSD; all
48
+ eigenvalues are non-negative). If None, the matrix is considered PSD if
49
+ all sampled eigenvalues are positive.
50
+
51
+ plots : bool, default=False
52
+ Print out all relevant plots for diagnosing eigenvalue accuracy.
53
+
54
+ Notes
55
+ -----
56
+
57
+ This is a convenience function for the FreeForm class with some effective
58
+ defaults that work well for common random matrix ensembles. For improved
59
+ performance and plotting utilites, consider finetuning parameters using
60
+ the FreeForm class.
61
+
62
+ References
63
+ ----------
64
+
65
+ .. [1] Reference.
66
+
67
+ Examples
68
+ --------
69
+
70
+ .. code-block:: python
71
+
72
+ >>> from freealg import FreeForm
73
+ """
74
+
75
+ if A.ndim != 2 or A.shape[0] != A.shape[1]:
76
+ raise RuntimeError("Only square matrices are permitted.")
77
+ n = A.shape[0]
78
+
79
+ if N is None:
80
+ N = n
81
+
82
+ # Size of sample matrix
83
+ n_s = int(80*(1 + numpy.log(n)))
84
+ # If matrix is not large enough, return eigenvalues
85
+ if n < n_s:
86
+ return compute_eig(A)
87
+ # Number of samples
88
+ num_samples = int(10 * (n / n_s)**0.5)
89
+
90
+ # Collect eigenvalue samples
91
+ samples = []
92
+ for _ in range(num_samples):
93
+ indices = numpy.random.choice(n, n_s, replace=False)
94
+ samples.append(compute_eig(A[numpy.ix_(indices, indices)]))
95
+ samples = numpy.concatenate(samples).ravel()
96
+
97
+ # If all eigenvalues are positive, set PSD flag
98
+ if psd is None:
99
+ psd = samples.min() > 0
100
+
101
+ ff = FreeForm(samples)
102
+ # Since we are resampling, we need to provide the correct matrix size
103
+ ff.n = n_s
104
+
105
+ # Perform fit and estimate eigenvalues
106
+ order = 1 + int(len(samples)**.2)
107
+ ff.fit(method='chebyshev', K=order, projection='sample',
108
+ force=True, plot=False, latex=False, save=False)
109
+
110
+ if plots:
111
+ ff.density(plot=True)
112
+ ff.stieltjes(plot=True)
113
+
114
+ _, _, eigs = ff.decompress(N, plot=plots)
115
+
116
+ if psd:
117
+ eigs = numpy.abs(eigs)
118
+ eigs.sort()
119
+
120
+ return eigs
freealg/freeform.py CHANGED
@@ -28,7 +28,7 @@ from ._decompress import decompress
28
28
  from ._sample import qmc_sample
29
29
  from ._support import detect_support
30
30
 
31
- __all__ = ['FreeForm', 'eigfree']
31
+ __all__ = ['FreeForm']
32
32
 
33
33
 
34
34
  # =========
@@ -75,15 +75,15 @@ class FreeForm(object):
75
75
  Eigenvalues of the matrix
76
76
 
77
77
  support: tuple
78
- The predicted (or given) support :math:`(\lambda_\min, \lambda_\max)` of the
79
- eigenvalue density.
78
+ The predicted (or given) support :math:`(\\lambda_{\\min},
79
+ \\lambda_{\\max})` of the eigenvalue density.
80
80
 
81
81
  psi : numpy.array
82
82
  Jacobi coefficients.
83
83
 
84
- n : int
85
- Initial array size (assuming a square matrix when :math:`\\mathbf{A}`
86
- is 2D).
84
+ n : int
85
+ Initial array size (assuming a square matrix when :math:`\\mathbf{A}` is
86
+ 2D).
87
87
 
88
88
  Methods
89
89
  -------
@@ -390,10 +390,10 @@ class FreeForm(object):
390
390
  x_supp = numpy.linspace(self.lam_m, self.lam_p, 1000)
391
391
  g_supp = 2.0 * numpy.pi * self.hilbert(x_supp)
392
392
  self._pade_sol = fit_pade(x_supp, g_supp, self.lam_m, self.lam_p,
393
- p=pade_p, q=pade_q, odd_side=odd_side,
394
- pade_reg=pade_reg, safety=1.0, max_outer=40,
395
- xtol=1e-12, ftol=1e-12, optimizer=optimizer,
396
- verbose=0)
393
+ p=pade_p, q=pade_q, odd_side=odd_side,
394
+ pade_reg=pade_reg, safety=1.0,
395
+ max_outer=40, xtol=1e-12, ftol=1e-12,
396
+ optimizer=optimizer, verbose=0)
397
397
 
398
398
  if plot:
399
399
  g_supp_approx = eval_pade(x_supp[None, :], self._pade_sol)[0, :]
@@ -449,7 +449,8 @@ class FreeForm(object):
449
449
  """
450
450
 
451
451
  if self.psi is None:
452
- raise RuntimeError('The spectral density needs to be fit using the .fit() function.')
452
+ raise RuntimeError('The spectral density needs to be fit using ' +
453
+ 'the .fit() function.')
453
454
 
454
455
  # Create x if not given
455
456
  if x is None:
@@ -543,7 +544,8 @@ class FreeForm(object):
543
544
  """
544
545
 
545
546
  if self.psi is None:
546
- raise RuntimeError('The spectral density needs to be fit using the .fit() function.')
547
+ raise RuntimeError('The spectral density needs to be fit using ' +
548
+ 'the .fit() function.')
547
549
 
548
550
  # Create x if not given
549
551
  if x is None:
@@ -605,8 +607,10 @@ class FreeForm(object):
605
607
 
606
608
  def stieltjes(self, x=None, y=None, plot=False, latex=False, save=False):
607
609
  """
608
- Compute Stieltjes transform of the spectral density, evaluated on an array
609
- of points, or over a 2D Cartesian grid on the complex plane.
610
+ Compute Stieltjes transform of the spectral density on a grid.
611
+
612
+ This function evaluates Stieltjes transform on an array of points, or
613
+ over a 2D Cartesian grid on the complex plane.
610
614
 
611
615
  Parameters
612
616
  ----------
@@ -665,11 +669,11 @@ class FreeForm(object):
665
669
  """
666
670
 
667
671
  if self.psi is None:
668
- raise RuntimeError('The spectral density needs to be fit using the .fit() function.')
669
-
672
+ raise RuntimeError('The spectral density needs to be fit using ' +
673
+ 'the .fit() function.')
670
674
 
671
- # Determine whether the Stieltjes transform is to be computed on
672
- # a Cartesian grid
675
+ # Determine whether the Stieltjes transform is to be computed on a
676
+ # Cartesian grid
673
677
  cartesian = plot | (y is not None)
674
678
 
675
679
  # Create x if not given
@@ -693,8 +697,8 @@ class FreeForm(object):
693
697
  z = x_grid + 1j * y_grid # shape (Ny, Nx)
694
698
  else:
695
699
  z = x
696
-
697
- m1, m2 = self._eval_stieltjes(z)
700
+
701
+ m1, m2 = self._eval_stieltjes(z, branches=True)
698
702
 
699
703
  if plot:
700
704
  plot_stieltjes(x, y, m1, m2, self.support, latex=latex, save=save)
@@ -705,7 +709,7 @@ class FreeForm(object):
705
709
  # eval stieltjes
706
710
  # ==============
707
711
 
708
- def _eval_stieltjes(self, z):
712
+ def _eval_stieltjes(self, z, branches=False):
709
713
  """
710
714
  Compute Stieltjes transform of the spectral density.
711
715
 
@@ -716,12 +720,18 @@ class FreeForm(object):
716
720
  The z values in the complex plan where the Stieltjes transform is
717
721
  evaluated.
718
722
 
723
+ branches : bool, default = False
724
+ Return both the principal and secondary branches of the Stieltjes
725
+ transform. The default ``branches=False`` will return only
726
+ the secondary branch.
727
+
719
728
 
720
729
  Returns
721
730
  -------
722
731
 
723
732
  m_p : numpy.ndarray
724
- The Stieltjes transform on the principal branch.
733
+ The Stieltjes transform on the principal branch if
734
+ ``branches=True``.
725
735
 
726
736
  m_m : numpy.ndarray
727
737
  The Stieltjes transform continued to the secondary branch.
@@ -737,14 +747,15 @@ class FreeForm(object):
737
747
  z = z.reshape(-1, 1)
738
748
 
739
749
  # # Set the number of bases as the number of x points insides support
740
- # mask_sup = numpy.logical_and(z.real >= self.lam_m, z.real <= self.lam_p)
750
+ # mask_sup = numpy.logical_and(z.real >= self.lam_m,
751
+ # z.real <= self.lam_p)
741
752
  # n_base = 2 * numpy.sum(mask_sup)
742
753
 
743
754
  # Stieltjes function
744
755
  if self.method == 'jacobi':
745
756
  stieltjes = partial(jacobi_stieltjes, psi=self.psi,
746
757
  support=self.support, alpha=self.alpha,
747
- beta=self.beta) # n_base = n_base
758
+ beta=self.beta) # n_base = n_base
748
759
  elif self.method == 'chebyshev':
749
760
  stieltjes = partial(chebyshev_stieltjes, psi=self.psi,
750
761
  support=self.support)
@@ -760,31 +771,34 @@ class FreeForm(object):
760
771
  m1[mask_p] = stieltjes(z[mask_p].reshape(-1, 1)).ravel()
761
772
 
762
773
  # Lower half-plane, use Schwarz reflection
763
- m1[mask_m] = numpy.conjugate(
764
- stieltjes(numpy.conjugate(z[mask_m].reshape(-1, 1)))).ravel()
774
+ z_conj = numpy.conjugate(z[mask_m].reshape(-1, 1))
775
+ m1[mask_m] = numpy.conjugate(stieltjes(z_conj)).ravel()
765
776
 
766
777
  # Second Riemann sheet
767
778
  m2[mask_p] = m1[mask_p]
768
779
  m2[mask_m] = -m1[mask_m] + self._glue(
769
780
  z[mask_m].reshape(-1, 1)).ravel()
770
-
771
- else:
772
- m2[:] = stieltjes(z.reshape(-1,1)).reshape(*m2.shape)
773
- m1[mask_p] = m2[mask_p]
774
- m1[mask_m] = numpy.conjugate(
775
- stieltjes(numpy.conjugate(z[mask_m].reshape(-1,1)))
776
- ).ravel()
777
781
 
778
- m1, m2 = m1.reshape(*shape), m2.reshape(*shape)
779
-
780
- return m1, m2
782
+ else:
783
+ m2[:] = stieltjes(z.reshape(-1, 1)).reshape(*m2.shape)
784
+ if branches:
785
+ m1[mask_p] = m2[mask_p]
786
+ m1[mask_m] = numpy.conjugate(
787
+ stieltjes(numpy.conjugate(z[mask_m].reshape(-1, 1)))
788
+ ).ravel()
789
+
790
+ if not branches:
791
+ return m2.reshape(*shape)
792
+ else:
793
+ m1, m2 = m1.reshape(*shape), m2.reshape(*shape)
794
+ return m1, m2
781
795
 
782
796
  # ==========
783
797
  # decompress
784
798
  # ==========
785
799
 
786
- def decompress(self, size, x=None, iterations=500, eigvals=True,
787
- step_size=0.1, tolerance=1e-9, seed=None, plot=False,
800
+ def decompress(self, size, x=None, max_iter=500, eigvals=True,
801
+ tolerance=1e-9, seed=None, plot=False,
788
802
  latex=False, save=False):
789
803
  """
790
804
  Free decompression of spectral density.
@@ -799,15 +813,12 @@ class FreeForm(object):
799
813
  Positions where density to be evaluated at. If `None`, an interval
800
814
  slightly larger than the support interval will be used.
801
815
 
802
- iterations: int, default=500
803
- Maximum number of Newton iterations.
816
+ max_iter: int, default=500
817
+ Maximum number of secant method iterations.
804
818
 
805
819
  eigvals: bool, default=True
806
820
  Return estimated (sampled) eigenvalues as well as the density.
807
821
 
808
- step_size: float, default=0.1
809
- Step size for Newton iterations.
810
-
811
822
  tolerance: float, default=1e-9
812
823
  Tolerance for the solution obtained by the Newton solver. Also
813
824
  used for the finite difference approximation to the derivative.
@@ -867,8 +878,7 @@ class FreeForm(object):
867
878
  size = int(size)
868
879
 
869
880
  rho, x, (lb, ub) = decompress(self, size, x=x, delta=self.delta,
870
- iterations=iterations,
871
- step_size=step_size, tolerance=tolerance)
881
+ max_iter=max_iter, tolerance=tolerance)
872
882
  x, rho = x.ravel(), rho.ravel()
873
883
 
874
884
  if plot:
@@ -881,14 +891,15 @@ class FreeForm(object):
881
891
  else:
882
892
  return x, rho
883
893
 
884
- def eigfree(A, N = None, psd = None):
894
+
895
+ def eigfree(A, N=None, psd=None, plots=False):
885
896
  """
886
897
  Estimate the eigenvalues of a matrix :math:`\\mathbf{A}` or a larger matrix
887
898
  containing :math:`\\mathbf{A}` using free decompression.
888
899
 
889
900
  This is a convenience function for the FreeForm class with some effective
890
901
  defaults that work well for common random matrix ensembles. For improved
891
- performance and plotting utilites, consider finetuning parameters using
902
+ performance and plotting utilites, consider finetuning parameters using
892
903
  the FreeForm class.
893
904
 
894
905
  Parameters
@@ -896,18 +907,22 @@ def eigfree(A, N = None, psd = None):
896
907
 
897
908
  A : numpy.ndarray
898
909
  The symmetric real-valued matrix :math:`\\mathbf{A}` whose eigenvalues
899
- (or those of a matrix containing :math:`\\mathbf{A}`) are to be computed.
910
+ (or those of a matrix containing :math:`\\mathbf{A}`) are to be
911
+ computed.
900
912
 
901
913
  N : int, default=None
902
914
  The size of the matrix containing :math:`\\mathbf{A}` to estimate
903
915
  eigenvalues of. If None, returns estimates of the eigenvalues of
904
916
  :math:`\\mathbf{A}` itself.
905
917
 
906
- psd: bool, default=None
907
- Determines whether the matrix is positive-semidefinite (PSD; all
918
+ psd : bool, default=None
919
+ Determines whether the matrix is positive-semidefinite (PSD; all
908
920
  eigenvalues are non-negative). If None, the matrix is considered PSD if
909
921
  all sampled eigenvalues are positive.
910
922
 
923
+ plots : bool, default=False
924
+ Print out all relevant plots for diagnosing eigenvalue accuracy.
925
+
911
926
  Notes
912
927
  -----
913
928
 
@@ -925,20 +940,24 @@ def eigfree(A, N = None, psd = None):
925
940
 
926
941
  >>> from freealg import FreeForm
927
942
  """
943
+ if A.ndim != 2 or A.shape[0] != A.shape[1]:
944
+ raise RuntimeError("Only square matrices are permitted.")
928
945
  n = A.shape[0]
929
-
946
+
947
+ if N is None:
948
+ N = n
949
+
930
950
  # Size of sample matrix
931
951
  n_s = int(80*(1 + numpy.log(n)))
932
-
933
952
  # If matrix is not large enough, return eigenvalues
934
953
  if n < n_s:
935
954
  return compute_eig(A)
936
-
937
- if N is None:
938
- N = n
939
-
940
955
  # Number of samples
941
956
  num_samples = int(10 * (n / n_s)**0.5)
957
+ # else:
958
+ # # Use the entire matrix given
959
+ # n_s = n
960
+ # num_samples = 1
942
961
 
943
962
  # Collect eigenvalue samples
944
963
  samples = []
@@ -957,12 +976,17 @@ def eigfree(A, N = None, psd = None):
957
976
 
958
977
  # Perform fit and estimate eigenvalues
959
978
  order = 1 + int(len(samples)**.2)
960
- ff.fit(method='chebyshev', K=order, projection='sample', damp='jackson',
961
- force=True, plot=False, latex=False, save=False, reg=0.05)
962
- _, _, eigs = ff.decompress(N)
979
+ ff.fit(method='chebyshev', K=order, projection='sample',
980
+ force=True, plot=False, latex=False, save=False)
981
+
982
+ if plots:
983
+ ff.density(plot=True)
984
+ ff.stieltjes(plot=True)
985
+
986
+ _, _, eigs = ff.decompress(N, plot=plots)
963
987
 
964
988
  if psd:
965
989
  eigs = numpy.abs(eigs)
966
990
  eigs.sort()
967
991
 
968
- return eigs
992
+ return eigs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.12
3
+ Version: 0.1.14
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -50,6 +50,12 @@ Dynamic: summary
50
50
  :width: 240
51
51
  :class: custom-dark
52
52
 
53
+ `Paper <https://arxiv.org/abs/2506.11994>`__ |
54
+ `Slides <https://www.dropbox.com/scl/fi/03gjuyz17k9yhsqy0isoz/free_decomporession_slides.pdf?rlkey=8f82mhciyl2ju02l7hv1md5li&st=26xmhjga&dl=0>`__ |
55
+ `Docs <https://ameli.github.io/freealg>`__
56
+
57
+ .. `Slides <https://ameli.github.io/freealg/_static/data/slides.pdf>`__ |
58
+
53
59
  *freealg* is a Python package that employs **free** probability to evaluate the spectral
54
60
  densities of large matrix **form**\ s. The fundamental algorithm employed by *freealg* is
55
61
  **free decompression**, which extrapolates from the empirical spectral densities of small
@@ -120,15 +126,19 @@ requests and bug reports.
120
126
  How to Cite
121
127
  ===========
122
128
 
123
- If you use this work, please cite the `arXiv paper <https://arxiv.org/abs/2506.11994>`.
129
+ If you use this work, please cite the `arXiv paper <https://arxiv.org/abs/2506.11994>`__.
124
130
 
125
131
  .. code::
126
132
 
127
- @article{ameli2025spectral,
128
- title={Spectral Estimation with Free Decompression},
129
- author={Siavash Ameli and Chris van der Heide and Liam Hodgkinson and Michael W. Mahoney},
130
- journal={arXiv preprint arXiv:2506.11994},
131
- year={2025}
133
+ @article{spectral2025,
134
+ title={Spectral Estimation with Free Decompression},
135
+ author={Siavash Ameli and Chris van der Heide and Liam Hodgkinson and Michael W. Mahoney},
136
+ year={2025},
137
+ eprint={2506.11994},
138
+ archivePrefix={arXiv},
139
+ primaryClass={stat.ML},
140
+ url={https://arxiv.org/abs/2506.11994},
141
+ journal={arXiv preprint arXiv:2506.11994},
132
142
  }
133
143
 
134
144