freealg 0.1.6__py3-none-any.whl → 0.1.8__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/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.6"
1
+ __version__ = "0.1.8"
freealg/_plot_util.py CHANGED
@@ -18,7 +18,6 @@ import matplotlib
18
18
  import colorsys
19
19
  import matplotlib.ticker as ticker
20
20
  import matplotlib.gridspec as gridspec
21
- from ._decompress import reverse_characteristics
22
21
 
23
22
  __all__ = ['plot_fit', 'plot_density', 'plot_hilbert', 'plot_stieltjes',
24
23
  'plot_stieltjes_on_disk']
@@ -244,42 +243,11 @@ def _value_formatter(v, pos):
244
243
  return f"{m_val:.1f}"
245
244
 
246
245
 
247
- # ================
248
- # plot char curves
249
- # ================
250
- def _plot_char_curves(ax, char_curves):
251
- """
252
- """
253
-
254
- curves = reverse_characteristics(char_curves['matrix'],
255
- char_curves['z'], 4)
256
- lw = 2
257
- for idx in range(curves.shape[1]):
258
-
259
- creal, cimag = curves[:, idx].real, curves[:, idx].imag
260
-
261
- ax.plot(creal, cimag, ':', color='white', linewidth=lw,
262
- alpha=0.75)
263
-
264
- ax.annotate(
265
- '', # no text
266
- xy=(creal[-1], cimag[-1]), # arrow tip at final point
267
- xytext=(creal[-2], cimag[-2]), # tail at penultimate point
268
- arrowprops=dict(
269
- arrowstyle='-|>', # simple arrow head
270
- mutation_scale=5, # size of the head
271
- color='white',
272
- lw=lw, alpha=0.75, # arrow shaft line width
273
- )
274
- )
275
-
276
-
277
246
  # ==============
278
247
  # plot stieltjes
279
248
  # ==============
280
249
 
281
- def plot_stieltjes(x, y, m1, m2, support, latex=False, char_curves=None,
282
- save=False):
250
+ def plot_stieltjes(x, y, m1, m2, support, latex=False, save=False):
283
251
  """
284
252
  """
285
253
 
@@ -329,10 +297,6 @@ def plot_stieltjes(x, y, m1, m2, support, latex=False, char_curves=None,
329
297
  ax1.set_xlim([x_min, x_max])
330
298
  ax1.set_ylim([y_min, y_max])
331
299
 
332
- # Plot characteristic curves
333
- if char_curves is not None:
334
- _plot_char_curves(ax1, char_curves)
335
-
336
300
  pos = ax1.get_position()
337
301
  cbar_width = 0.013
338
302
  pad = 0.013
@@ -10,7 +10,6 @@ from .marchenko_pastur import MarchenkoPastur
10
10
  from .wigner import Wigner
11
11
  from .kesten_mckay import KestenMcKay
12
12
  from .wachter import Wachter
13
- # from .meixner import meixner
13
+ from .meixner import Meixner
14
14
 
15
- # __all__ = ['MarchenkoPastur', 'Wigner', 'KestenMcKay', 'Wachter', 'Meixner']
16
- __all__ = ['MarchenkoPastur', 'Wigner', 'KestenMcKay', 'Wachter']
15
+ __all__ = ['MarchenkoPastur', 'Wigner', 'KestenMcKay', 'Wachter', 'Meixner']
@@ -12,7 +12,6 @@
12
12
  # =======
13
13
 
14
14
  import numpy
15
- import networkx as nx
16
15
  from scipy.interpolate import interp1d
17
16
  from .._plot_util import plot_density, plot_hilbert, plot_stieltjes, \
18
17
  plot_stieltjes_on_disk, plot_samples
@@ -61,7 +60,7 @@ class KestenMcKay(object):
61
60
  Notes
62
61
  -----
63
62
 
64
- The Marchenko-Pastur distribution has the absolutely-continuous density
63
+ The Kesten-McKay distribution has the absolutely-continuous density
65
64
 
66
65
  .. math::
67
66
 
@@ -236,10 +235,10 @@ class KestenMcKay(object):
236
235
  x = numpy.linspace(x_min, x_max, 500)
237
236
 
238
237
  def _P(x):
239
- return (self.d - 2.0) * x
238
+ return (self.d - 2.0) * x / (self.d - 1.0)
240
239
 
241
240
  def _Q(x):
242
- return self.d**2 - x**2
241
+ return (self.d**2 - x**2) / (self.d - 1.0)
243
242
 
244
243
  P = _P(x)
245
244
  Q = _Q(x)
@@ -262,15 +261,15 @@ class KestenMcKay(object):
262
261
 
263
262
  def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
264
263
  """
265
- Stieltjes transform (principal or secondary branch)
266
- for Marchenko–Pastur distribution on upper half-plane.
264
+ Stieltjes transform (principal or secondary branch) for Kesten-McKay
265
+ distribution on upper half-plane.
267
266
  """
268
267
 
269
268
  m = numpy.empty_like(z, dtype=complex)
270
269
 
271
270
  sign = -1 if alt_branch else 1
272
- A = self.d**2 - z**2
273
- B = (self.d - 2.0) * z
271
+ A = (self.d**2 - z**2) / (self.d - 1.0)
272
+ B = ((self.d - 2.0) * z) / (self.d - 1.0)
274
273
  D = B**2 - 4 * A
275
274
  sqrtD = numpy.sqrt(D)
276
275
  m1 = (-B + sqrtD) / (2 * A)
@@ -370,7 +369,7 @@ class KestenMcKay(object):
370
369
 
371
370
  .. code-block:: python
372
371
 
373
- >>> m1, m2 = mp.stieltjes(plot=True, on_disk=True)
372
+ >>> m1, m2 = km.stieltjes(plot=True, on_disk=True)
374
373
 
375
374
  .. image:: ../_static/images/plots/km_stieltjes_disk.png
376
375
  :align: center
@@ -450,7 +449,7 @@ class KestenMcKay(object):
450
449
  is used.
451
450
 
452
451
  method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
453
- Method of drawing samples from uniform distirbution:
452
+ Method of drawing samples from uniform distribution:
454
453
 
455
454
  * ``'mc'``: Monte Carlo
456
455
  * ``'qmc'``: Quasi Monte Carlo
@@ -534,6 +533,39 @@ class KestenMcKay(object):
534
533
 
535
534
  return samples
536
535
 
536
+ # ============
537
+ # Haar unitary
538
+ # ============
539
+
540
+ def _haar_orthogonal(self, n, k):
541
+ """
542
+ Haar-distributed O(n) via the Mezzadri QR trick.
543
+
544
+ References
545
+ ----------
546
+
547
+ .. [1] Francesco Mezzadri. How to generate random matrices from the
548
+ classical compact groups. https://arxiv.org/pdf/math-ph/0609050
549
+
550
+ Notes
551
+ -----
552
+
553
+ Taking the QR of a normal-Gaussian matrix gives an orthonormal basis,
554
+ but the columns of that Q are not uniform on the sphere, as they are
555
+ biased by the signs or phases in the R-factor.
556
+
557
+ With Mezzadri method, columns of Q are rescaled by the reciprocals of
558
+ the diagonals of R phase, resulting in a matrix that is exactly
559
+ uniformly distributed under Haar measure O(n).
560
+ """
561
+
562
+ rng = numpy.random.default_rng()
563
+ Z = rng.standard_normal((n, k))
564
+ Q, R = numpy.linalg.qr(Z, mode='reduced') # Q is n by k
565
+ Q *= numpy.sign(numpy.diag(R))
566
+
567
+ return Q
568
+
537
569
  # ======
538
570
  # matrix
539
571
  # ======
@@ -554,6 +586,43 @@ class KestenMcKay(object):
554
586
  A : numpy.ndarray
555
587
  A matrix of the size :math:`n \\times n`.
556
588
 
589
+ Notes
590
+ -----
591
+
592
+ If the parameter :math:`d` is even, the matrtix is generated from
593
+
594
+ .. math::
595
+
596
+ \\mathbf{A} = \\sum_{i=1}^{d/2} \\mathbf{O}_i +
597
+ \\mathbf{O}_o^{\\intercal},
598
+
599
+ where :math:`\\mathbf{O}_i` are randomly generated orthogonal matrices
600
+ with Haar. This method is fast but :math:`d` has to be even.
601
+
602
+ If all other :math:`d`, the following is used:
603
+
604
+ .. math::
605
+
606
+ \\mathbf{A} = \\mathbf{P} \\mathbf{O} \\mathbf{D} \\mathbf{O}^{-1}
607
+ \\mathbf{P},
608
+
609
+ where :math:`\\mathbf{D}` is diagonal matrix with entries
610
+ :math:`\\pm 1`, :math:`\\mathbf{O}` is orthogonal with Haar measure,
611
+ and :math:`\\mathbf{P}` is a projection matrix. For more details, see
612
+ Section 5 and 6 of [1]_.
613
+
614
+ The orthogonal matrices are genrated using the method of [2]_.
615
+
616
+ References
617
+ ----------
618
+
619
+ .. [1] Iris S. A. Longoria and James A. Mingo, Freely Independent Coin
620
+ Tosses, Standard Young Tableaux, and the Kesten--McKay Law.
621
+ https://arxiv.org/abs/2009.11950
622
+
623
+ .. [2] Francesco Mezzadri. How to generate random matrices from the
624
+ classical compact groups. https://arxiv.org/pdf/math-ph/0609050
625
+
557
626
  Examples
558
627
  --------
559
628
 
@@ -564,11 +633,38 @@ class KestenMcKay(object):
564
633
  >>> A = km.matrix(2000)
565
634
  """
566
635
 
567
- n = size
568
- G = nx.random_regular_graph(self.d, n)
569
- A = nx.to_numpy_array(G, dtype=float) # shape (n,n)
570
-
571
- mu = self.d / n
572
- A_c = A - mu * numpy.ones((n, n))
573
-
574
- return A_c
636
+ if (self.d >= 2) and (self.d % 2 == 0):
637
+ # Uses algorithm 1 . Only if d is even. This is much faster than
638
+ # algorithm 2.
639
+ n = size
640
+ rng = numpy.random.default_rng()
641
+ m = self.d // 2
642
+ A = numpy.zeros((n, n))
643
+
644
+ for _ in range(m):
645
+ O_ = self._haar_orthogonal(n, n)
646
+ A += O_ + O_.T
647
+ else:
648
+ # Uses algorithm 2. Only when d is odd, but this algorithm works
649
+ # for any d (even and odd), but it takes much longer to comute
650
+ # especially if d is larger. As such, as only use algorithm 1 when
651
+ # d is even and use algorithm 2 for the rest.
652
+ n = size * self.d
653
+ rng = numpy.random.default_rng()
654
+
655
+ # Deterministic pieces
656
+ k = size
657
+ if k == 0:
658
+ raise ValueError('Choose size larger then d.')
659
+
660
+ # Projection rows of O
661
+ Q = self._haar_orthogonal(n, k)
662
+ O_k = Q.T
663
+
664
+ # diagonal D with equal \pm 1 (trace 0)
665
+ diag = numpy.ones(n, dtype=float)
666
+ diag[:n//2] = -1
667
+ rng.shuffle(diag)
668
+ A = (n/k) * (O_k * diag) @ O_k.T
669
+
670
+ return A
@@ -456,7 +456,7 @@ class MarchenkoPastur(object):
456
456
  is used.
457
457
 
458
458
  method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
459
- Method of drawing samples from uniform distirbution:
459
+ Method of drawing samples from uniform distribution:
460
460
 
461
461
  * ``'mc'``: Monte Carlo
462
462
  * ``'qmc'``: Quasi Monte Carlo
@@ -0,0 +1,601 @@
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 .._plot_util import plot_density, plot_hilbert, plot_stieltjes, \
17
+ plot_stieltjes_on_disk, plot_samples
18
+
19
+ try:
20
+ from scipy.integrate import cumtrapz
21
+ except ImportError:
22
+ from scipy.integrate import cumulative_trapezoid as cumtrapz
23
+ from scipy.stats import qmc
24
+
25
+ __all__ = ['Meixner']
26
+
27
+
28
+ # =======
29
+ # Meixner
30
+ # =======
31
+
32
+ class Meixner(object):
33
+ """
34
+ Meixner distribution.
35
+
36
+ Parameters
37
+ ----------
38
+
39
+ a : float
40
+ Parameter :math:`a` of the distribution. See Notes.
41
+
42
+ b : float
43
+ Parameter :math:`b` of the distribution. See Notes.
44
+
45
+ Methods
46
+ -------
47
+
48
+ density
49
+ Spectral density of distribution.
50
+
51
+ hilbert
52
+ Hilbert transform of distribution.
53
+
54
+ stieltjes
55
+ Stieltjes transform of distribution.
56
+
57
+ sample
58
+ Sample from distribution.
59
+
60
+ matrix
61
+ Generate matrix with its empirical spectral density of distribution
62
+
63
+ Notes
64
+ -----
65
+
66
+ The Meixner distribution has the absolutely-continuous density
67
+
68
+ .. math::
69
+
70
+ \\mathrm{d} \\rho(x) =
71
+ \\frac{4(1+b) - (x-a)^2}{2 \\pi (b x^2 + a x + 1)}
72
+ \\mathbf{1}_{x \\in [\\lambda_{-}, \\lambda_{+}]} \\mathrm{d}{x}
73
+
74
+ where :math:`a, b` are the shape parameters of the distribution. The edges
75
+ of the support are
76
+
77
+ .. math::
78
+
79
+ \\lambda_{\\pm} = a \\pm 2 \\sqrt{1 + b}.
80
+
81
+ References
82
+ ----------
83
+
84
+ .. [1] Saitoh, N. & Yosnida, M. (2001). The infinite divisibility and
85
+ orthogonal polynomials with a constant recursion formula in free
86
+ probability theory. Probab. Math. Statist., 21, 159–170.
87
+
88
+ Examples
89
+ --------
90
+
91
+ .. code-block:: python
92
+
93
+ >>> from freealg.distributions import Meixner
94
+ >>> mx = Meixner(2, 3)
95
+ """
96
+
97
+ # ====
98
+ # init
99
+ # ====
100
+
101
+ def __init__(self, a, b, c):
102
+ """
103
+ Initialization.
104
+ """
105
+
106
+ self.a = a
107
+ self.b = b
108
+ self.c = c
109
+ self.lam_p = self.a + 2.0 * numpy.sqrt(self.b)
110
+ self.lam_m = self.a - 2.0 * numpy.sqrt(self.b)
111
+ self.support = (self.lam_m, self.lam_p)
112
+
113
+ # =======
114
+ # density
115
+ # =======
116
+
117
+ def density(self, x=None, plot=False, latex=False, save=False):
118
+ """
119
+ Density of distribution.
120
+
121
+ Parameters
122
+ ----------
123
+
124
+ x : numpy.array, default=None
125
+ The locations where density is evaluated at. If `None`, an interval
126
+ slightly larger than the support interval of the spectral density
127
+ is used.
128
+
129
+ rho : numpy.array, default=None
130
+ Density. If `None`, it will be computed.
131
+
132
+ plot : bool, default=False
133
+ If `True`, density is plotted.
134
+
135
+ latex : bool, default=False
136
+ If `True`, the plot is rendered using LaTeX. This option is
137
+ relevant only if ``plot=True``.
138
+
139
+ save : bool, default=False
140
+ If not `False`, the plot is saved. If a string is given, it is
141
+ assumed to the save filename (with the file extension). This option
142
+ is relevant only if ``plot=True``.
143
+
144
+ Returns
145
+ -------
146
+
147
+ rho : numpy.array
148
+ Density.
149
+
150
+ Examples
151
+ --------
152
+
153
+ .. code-block::python
154
+
155
+ >>> from freealg.distributions import Meixner
156
+ >>> mx = Meixner(2, 3)
157
+ >>> rho = mx.density(plot=True)
158
+
159
+ .. image:: ../_static/images/plots/mx_density.png
160
+ :align: center
161
+ :class: custom-dark
162
+ """
163
+
164
+ # Create x if not given
165
+ if x is None:
166
+ radius = 0.5 * (self.lam_p - self.lam_m)
167
+ center = 0.5 * (self.lam_p + self.lam_m)
168
+ scale = 1.25
169
+ x_min = numpy.floor(center - radius * scale)
170
+ x_max = numpy.ceil(center + radius * scale)
171
+ x = numpy.linspace(x_min, x_max, 500)
172
+
173
+ rho = numpy.zeros_like(x)
174
+ mask = numpy.logical_and(x > self.lam_m, x < self.lam_p)
175
+
176
+ # rho[mask] = \
177
+ # numpy.sqrt(4.0 * (1.0 + self.b) - (x[mask] - self.a)**2) / \
178
+ # (2.0 * numpy.pi * (self.b * x[mask]**2 + self.a * x[mask] + 1))
179
+
180
+ numer = numpy.zeros_like(x)
181
+ denom = numpy.ones_like(x)
182
+ numer[mask] = self.c * numpy.sqrt(4.0 * self.b - (x[mask] - self.a)**2)
183
+ denom[mask] = (1 - self.c)*(x[mask] - self.a)**2
184
+ denom[mask] += self.a * (2 - self.c)*(x[mask] - self.a)
185
+ denom[mask] += self.a**2 + self.b * self.c**2
186
+ denom[mask] *= 2 * numpy.pi
187
+
188
+ rho[mask] = numer[mask] / denom[mask]
189
+
190
+ if plot:
191
+ plot_density(x, rho, label='', latex=latex, save=save)
192
+
193
+ return rho
194
+
195
+ # =======
196
+ # hilbert
197
+ # =======
198
+
199
+ def hilbert(self, x=None, plot=False, latex=False, save=False):
200
+ """
201
+ Hilbert transform of the distribution.
202
+
203
+ Parameters
204
+ ----------
205
+
206
+ x : numpy.array, default=None
207
+ The locations where Hilbert transform is evaluated at. If `None`,
208
+ an interval slightly larger than the support interval of the
209
+ spectral density is used.
210
+
211
+ plot : bool, default=False
212
+ If `True`, Hilbert transform is plotted.
213
+
214
+ latex : bool, default=False
215
+ If `True`, the plot is rendered using LaTeX. This option is
216
+ relevant only if ``plot=True``.
217
+
218
+ save : bool, default=False
219
+ If not `False`, the plot is saved. If a string is given, it is
220
+ assumed to the save filename (with the file extension). This option
221
+ is relevant only if ``plot=True``.
222
+
223
+ Returns
224
+ -------
225
+
226
+ hilb : numpy.array
227
+ Hilbert transform.
228
+
229
+ Examples
230
+ --------
231
+
232
+ .. code-block::python
233
+
234
+ >>> from freealg.distributions import Meixner
235
+ >>> mx = Meixner(2, 3)
236
+ >>> hilb = mx.hilbert(plot=True)
237
+
238
+ .. image:: ../_static/images/plots/mx_hilbert.png
239
+ :align: center
240
+ :class: custom-dark
241
+ """
242
+
243
+ # Create x if not given
244
+ if x is None:
245
+ radius = 0.5 * (self.lam_p - self.lam_m)
246
+ center = 0.5 * (self.lam_p + self.lam_m)
247
+ scale = 1.25
248
+ x_min = numpy.floor(center - radius * scale)
249
+ x_max = numpy.ceil(center + radius * scale)
250
+ x = numpy.linspace(x_min, x_max, 500)
251
+
252
+ def _P(x):
253
+ # denom = 1.0 + self.b
254
+ # return ((1.0 + 2.0 * self.b) * x + self.a) / denom
255
+ P = ((self.c - 2.0) * x - self.a * self.c) / 2.0
256
+ return P
257
+
258
+ def _Q(x):
259
+ # denom = 1.0 + self.b
260
+ # return (self.b * x**2 + self.a * x + 1.0) / denom
261
+ Q = ((1.0 - self.c) * x**2 + self.a * self.c * x +
262
+ self.b * self.c**2) / 4.0
263
+ return Q
264
+
265
+ P = _P(x)
266
+ Q = _Q(x)
267
+ Delta2 = P**2 - 4.0 * Q
268
+ Delta = numpy.sqrt(numpy.maximum(Delta2, 0))
269
+ sign = numpy.sign(P)
270
+ hilb = (P - sign * Delta) / (2.0 * Q)
271
+
272
+ # using negative sign convention
273
+ hilb = -hilb
274
+
275
+ if plot:
276
+ plot_hilbert(x, hilb, support=self.support, latex=latex, save=save)
277
+
278
+ return hilb
279
+
280
+ # =======================
281
+ # m mp numeric vectorized
282
+ # =======================
283
+
284
+ def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
285
+ """
286
+ Stieltjes transform (principal or secondary branch) for Meixner
287
+ distribution on upper half-plane.
288
+ """
289
+
290
+ sign = -1 if alt_branch else 1
291
+ # denom = 1.0 + self.b
292
+ # A = (self.b * z**2 + self.a * z + 1.0) / denom
293
+ # B = ((1.0 + 2.0 * self.b) * z + self.a) / denom
294
+ A = ((1.0 - self.c) * z**2 + self.a * self.c * z +
295
+ self.b * self.c**2) / 4.0
296
+ B = ((self.c - 2.0) * z - self.a * self.c) / 2.0
297
+
298
+ # D = B**2 - 4 * A
299
+ # sqrtD = numpy.sqrt(D)
300
+
301
+ # Avoid numpy picking the wrong branch
302
+ d = 2 * numpy.sqrt(1.0 + self.b)
303
+ r_min = self.a - d
304
+ r_max = self.a + d
305
+ sqrtD = numpy.sqrt(z - r_min) * numpy.sqrt(z - r_max)
306
+
307
+ m1 = (-B + sqrtD) / (2 * A)
308
+ m2 = (-B - sqrtD) / (2 * A)
309
+
310
+ # pick correct branch only for non‑masked entries
311
+ upper = z.imag >= 0
312
+ branch = numpy.empty_like(m1)
313
+ branch[upper] = numpy.where(
314
+ sign*m1[upper].imag > 0, m1[upper], m2[upper])
315
+ branch[~upper] = numpy.where(
316
+ sign*m1[~upper].imag < 0, m1[~upper], m2[~upper])
317
+ m = branch
318
+
319
+ return m
320
+
321
+ # ============
322
+ # m mp reflect
323
+ # ============
324
+
325
+ def _m_mp_reflect(self, z, alt_branch=False):
326
+ """
327
+ Analytic continuation using Schwarz reflection.
328
+ """
329
+
330
+ mask_p = z.imag >= 0.0
331
+ mask_n = z.imag < 0.0
332
+
333
+ m = numpy.zeros_like(z)
334
+
335
+ f = self._m_mp_numeric_vectorized
336
+ m[mask_p] = f(z[mask_p], alt_branch=False)
337
+ m[mask_n] = f(z[mask_n], alt_branch=alt_branch)
338
+
339
+ return m
340
+
341
+ # =========
342
+ # stieltjes
343
+ # =========
344
+
345
+ def stieltjes(self, x=None, y=None, plot=False, on_disk=False, latex=False,
346
+ save=False):
347
+ """
348
+ Stieltjes transform of distribution.
349
+
350
+ Parameters
351
+ ----------
352
+
353
+ x : numpy.array, default=None
354
+ The x axis of the grid where the Stieltjes transform is evaluated.
355
+ If `None`, an interval slightly larger than the support interval of
356
+ the spectral density is used.
357
+
358
+ y : numpy.array, default=None
359
+ The y axis of the grid where the Stieltjes transform is evaluated.
360
+ If `None`, a grid on the interval ``[-1, 1]`` is used.
361
+
362
+ plot : bool, default=False
363
+ If `True`, Stieltjes transform is plotted.
364
+
365
+ on_disk : bool, default=False
366
+ If `True`, the Stieltjes transform is mapped on unit disk. This
367
+ option relevant only if ``plot=True``.
368
+
369
+ latex : bool, default=False
370
+ If `True`, the plot is rendered using LaTeX. This option is
371
+ relevant only if ``plot=True``.
372
+
373
+ save : bool, default=False
374
+ If not `False`, the plot is saved. If a string is given, it is
375
+ assumed to the save filename (with the file extension). This option
376
+ is relevant only if ``plot=True``.
377
+
378
+ Returns
379
+ -------
380
+
381
+ m1 : numpy.array
382
+ Stieltjes transform on principal branch.
383
+
384
+ m12 : numpy.array
385
+ Stieltjes transform on secondary branch.
386
+
387
+ Examples
388
+ --------
389
+
390
+ .. code-block:: python
391
+
392
+ >>> from freealg.distributions import Meixner
393
+ >>> mx = Meixner(2, 3)
394
+ >>> m1, m2 = mx.stieltjes(plot=True)
395
+
396
+ .. image:: ../_static/images/plots/mx_stieltjes.png
397
+ :align: center
398
+ :class: custom-dark
399
+
400
+ Plot on unit disk using Cayley transform:
401
+
402
+ .. code-block:: python
403
+
404
+ >>> m1, m2 = mx.stieltjes(plot=True, on_disk=True)
405
+
406
+ .. image:: ../_static/images/plots/mx_stieltjes_disk.png
407
+ :align: center
408
+ :class: custom-dark
409
+ """
410
+
411
+ if (plot is True) and (on_disk is True):
412
+ n_r = 1000
413
+ n_t = 1000
414
+ r_min, r_max = 0, 2.5
415
+ t_min, t_max = 0, 2.0 * numpy.pi
416
+ r = numpy.linspace(r_min, r_max, n_r)
417
+ t = numpy.linspace(t_min, t_max, n_t + 1)[:-1]
418
+ grid_r, grid_t = numpy.meshgrid(r, t)
419
+
420
+ grid_x_D = grid_r * numpy.cos(grid_t)
421
+ grid_y_D = grid_r * numpy.sin(grid_t)
422
+ zeta = grid_x_D + 1j * grid_y_D
423
+
424
+ # Cayley transform mapping zeta on D to z on H
425
+ z_H = 1j * (1 + zeta) / (1 - zeta)
426
+
427
+ m1_D = self._m_mp_reflect(z_H, alt_branch=False)
428
+ m2_D = self._m_mp_reflect(z_H, alt_branch=True)
429
+
430
+ plot_stieltjes_on_disk(r, t, m1_D, m2_D, support=self.support,
431
+ latex=latex, save=save)
432
+
433
+ return m1_D, m2_D
434
+
435
+ # Create x if not given
436
+ if x is None:
437
+ radius = 0.5 * (self.lam_p - self.lam_m)
438
+ center = 0.5 * (self.lam_p + self.lam_m)
439
+ scale = 2.0
440
+ x_min = numpy.floor(2.0 * (center - 2.0 * radius * scale)) / 2.0
441
+ x_max = numpy.ceil(2.0 * (center + 2.0 * radius * scale)) / 2.0
442
+ x = numpy.linspace(x_min, x_max, 500)
443
+
444
+ # Create y if not given
445
+ if y is None:
446
+ y = numpy.linspace(-1, 1, 400)
447
+
448
+ x_grid, y_grid = numpy.meshgrid(x, y)
449
+ z = x_grid + 1j * y_grid # shape (Ny, Nx)
450
+
451
+ m1 = self._m_mp_reflect(z, alt_branch=False)
452
+ m2 = self._m_mp_reflect(z, alt_branch=True)
453
+
454
+ if plot:
455
+ plot_stieltjes(x, y, m1, m2, support=self.support, latex=latex,
456
+ save=save)
457
+
458
+ return m1, m2
459
+
460
+ # ======
461
+ # sample
462
+ # ======
463
+
464
+ def sample(self, size, x_min=None, x_max=None, method='qmc', plot=False,
465
+ latex=False, save=False):
466
+ """
467
+ Sample from distribution.
468
+
469
+ Parameters
470
+ ----------
471
+
472
+ size : int
473
+ Size of sample.
474
+
475
+ x_min : float, default=None
476
+ Minimum of sample values. If `None`, the left edge of the support
477
+ is used.
478
+
479
+ x_max : float, default=None
480
+ Maximum of sample values. If `None`, the right edge of the support
481
+ is used.
482
+
483
+ method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
484
+ Method of drawing samples from uniform distribution:
485
+
486
+ * ``'mc'``: Monte Carlo
487
+ * ``'qmc'``: Quasi Monte Carlo
488
+
489
+ plot : bool, default=False
490
+ If `True`, samples histogram is plotted.
491
+
492
+ latex : bool, default=False
493
+ If `True`, the plot is rendered using LaTeX. This option is
494
+ relevant only if ``plot=True``.
495
+
496
+ save : bool, default=False
497
+ If not `False`, the plot is saved. If a string is given, it is
498
+ assumed to the save filename (with the file extension). This option
499
+ is relevant only if ``plot=True``.
500
+
501
+ Returns
502
+ -------
503
+
504
+ s : numpy.ndarray
505
+ Samples.
506
+
507
+ Notes
508
+ -----
509
+
510
+ This method uses inverse transform sampling.
511
+
512
+ Examples
513
+ --------
514
+
515
+ .. code-block::python
516
+
517
+ >>> from freealg.distributions import Meixner
518
+ >>> mx = Meixner(2, 3)
519
+ >>> s = mx.sample(2000)
520
+
521
+ .. image:: ../_static/images/plots/mx_samples.png
522
+ :align: center
523
+ :class: custom-dark
524
+ """
525
+
526
+ if x_min is None:
527
+ x_min = self.lam_m
528
+
529
+ if x_max is None:
530
+ x_max = self.lam_p
531
+
532
+ # Grid and PDF
533
+ xs = numpy.linspace(x_min, x_max, size)
534
+ pdf = self.density(xs)
535
+
536
+ # CDF (using cumulative trapezoidal rule)
537
+ cdf = cumtrapz(pdf, xs, initial=0)
538
+ cdf /= cdf[-1] # normalize CDF to 1
539
+
540
+ # Inverse CDF interpolator
541
+ inv_cdf = interp1d(cdf, xs, bounds_error=False,
542
+ fill_value=(x_min, x_max))
543
+
544
+ # Draw from uniform distribution
545
+ if method == 'mc':
546
+ u = numpy.random.rand(size)
547
+ elif method == 'qmc':
548
+ engine = qmc.Halton(d=1)
549
+ u = engine.random(size)
550
+ else:
551
+ raise ValueError('"method" is invalid.')
552
+
553
+ # Draw from distribution by mapping from inverse CDF
554
+ samples = inv_cdf(u).ravel()
555
+
556
+ if plot:
557
+ radius = 0.5 * (self.lam_p - self.lam_m)
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)
563
+ rho = self.density(x)
564
+ plot_samples(x, rho, x_min, x_max, samples, latex=latex, save=save)
565
+
566
+ return samples
567
+
568
+ # ======
569
+ # matrix
570
+ # ======
571
+
572
+ def matrix(self, size):
573
+ """
574
+ Generate matrix with the spectral density of the distribution.
575
+
576
+ Parameters
577
+ ----------
578
+
579
+ size : int
580
+ Size :math:`n` of the matrix.
581
+
582
+ Returns
583
+ -------
584
+
585
+ Sx : numpy.ndarray
586
+ A matrix of the size :math:`n \\times n`.
587
+
588
+ Sy : numpy.ndarray
589
+ A matrix of the size :math:`n \\times n`.
590
+
591
+ Examples
592
+ --------
593
+
594
+ .. code-block::python
595
+
596
+ >>> from freealg.distributions import Meixner
597
+ >>> mx = Meixner(2, 3)
598
+ >>> A = mx.matrix(2000)
599
+ """
600
+
601
+ raise NotImplementedError
@@ -63,7 +63,7 @@ class Wachter(object):
63
63
  Notes
64
64
  -----
65
65
 
66
- The Marchenko-Pastur distribution has the absolutely-continuous density
66
+ The Wachter distribution has the absolutely-continuous density
67
67
 
68
68
  .. math::
69
69
 
@@ -241,10 +241,12 @@ class Wachter(object):
241
241
  x = numpy.linspace(x_min, x_max, 500)
242
242
 
243
243
  def _P(x):
244
- return 1.0 - self.a + (self.a + self.b - 2.0) * x
244
+ denom = self.a + self.b - 1.0
245
+ return (1.0 - self.a + (self.a + self.b - 2.0) * x) / denom
245
246
 
246
247
  def _Q(x):
247
- return x * (1.0 - x)
248
+ denom = self.a + self.b - 1.0
249
+ return x * (1.0 - x) / denom
248
250
 
249
251
  P = _P(x)
250
252
  Q = _Q(x)
@@ -267,13 +269,14 @@ class Wachter(object):
267
269
 
268
270
  def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
269
271
  """
270
- Stieltjes transform (principal or secondary branch)
271
- for Marchenko–Pastur distribution on upper half-plane.
272
+ Stieltjes transform (principal or secondary branch) for Wachter
273
+ distribution on upper half-plane.
272
274
  """
273
275
 
274
276
  sign = -1 if alt_branch else 1
275
- A = z * (1.0 - z)
276
- B = 1.0 - self.a + (self.a + self.b - 2.0) * z
277
+ denom = self.a + self.b - 1.0
278
+ A = (z * (1.0 - z)) / denom
279
+ B = (1.0 - self.a + (self.a + self.b - 2.0) * z) / denom
277
280
  D = B**2 - 4 * A
278
281
  sqrtD = numpy.sqrt(D)
279
282
  m1 = (-B + sqrtD) / (2 * A)
@@ -373,7 +376,7 @@ class Wachter(object):
373
376
 
374
377
  .. code-block:: python
375
378
 
376
- >>> m1, m2 = mp.stieltjes(plot=True, on_disk=True)
379
+ >>> m1, m2 = wa.stieltjes(plot=True, on_disk=True)
377
380
 
378
381
  .. image:: ../_static/images/plots/wa_stieltjes_disk.png
379
382
  :align: center
@@ -453,7 +456,7 @@ class Wachter(object):
453
456
  is used.
454
457
 
455
458
  method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
456
- Method of drawing samples from uniform distirbution:
459
+ Method of drawing samples from uniform distribution:
457
460
 
458
461
  * ``'mc'``: Monte Carlo
459
462
  * ``'qmc'``: Quasi Monte Carlo
@@ -12,7 +12,6 @@
12
12
  # =======
13
13
 
14
14
  import numpy
15
- import networkx as nx
16
15
  from scipy.interpolate import interp1d
17
16
  from .._plot_util import plot_density, plot_hilbert, plot_stieltjes, \
18
17
  plot_stieltjes_on_disk, plot_samples
@@ -55,7 +54,7 @@ class Wigner(object):
55
54
  Notes
56
55
  -----
57
56
 
58
- The Marchenko-Pastur distribution has the absolutely-continuous density
57
+ The Wigner distribution has the absolutely-continuous density
59
58
 
60
59
  .. math::
61
60
 
@@ -77,20 +76,20 @@ class Wigner(object):
77
76
  .. code-block:: python
78
77
 
79
78
  >>> from freealg.distributions import Wigner
80
- >>> wg = Wigner()
79
+ >>> wg = Wigner(1)
81
80
  """
82
81
 
83
82
  # ====
84
83
  # init
85
84
  # ====
86
85
 
87
- def __init__(self):
86
+ def __init__(self, r):
88
87
  """
89
88
  Initialization.
90
89
  """
91
-
92
- self.lam_p = 2.0
93
- self.lam_m = -2.0
90
+ self.r = r
91
+ self.lam_p = self.r
92
+ self.lam_m = -self.r
94
93
  self.support = (self.lam_m, self.lam_p)
95
94
 
96
95
  # =======
@@ -136,7 +135,7 @@ class Wigner(object):
136
135
  .. code-block::python
137
136
 
138
137
  >>> from freealg.distributions import Wigner
139
- >>> wg = Wigner()
138
+ >>> wg = Wigner(1)
140
139
  >>> rho = wg.density(plot=True)
141
140
 
142
141
  .. image:: ../_static/images/plots/wg_density.png
@@ -156,8 +155,8 @@ class Wigner(object):
156
155
  rho = numpy.zeros_like(x)
157
156
  mask = numpy.logical_and(x >= self.lam_m, x <= self.lam_p)
158
157
 
159
- rho[mask] = (1.0 / (2.0 * numpy.pi)) * \
160
- numpy.sqrt(4.0 - x[mask]**2)
158
+ rho[mask] = (2.0 / (numpy.pi * self.r**2)) * \
159
+ numpy.sqrt(self.r**2 - x[mask]**2)
161
160
 
162
161
  if plot:
163
162
  plot_density(x, rho, label='', latex=latex, save=save)
@@ -204,7 +203,7 @@ class Wigner(object):
204
203
  .. code-block::python
205
204
 
206
205
  >>> from freealg.distributions import Wigner
207
- >>> wg = Wigner()
206
+ >>> wg = Wigner(1)
208
207
  >>> hilb = wg.hilbert(plot=True)
209
208
 
210
209
  .. image:: ../_static/images/plots/wg_hilbert.png
@@ -225,7 +224,7 @@ class Wigner(object):
225
224
  return x
226
225
 
227
226
  def _Q(x):
228
- return 1.0
227
+ return (self.r**2) / 4.0
229
228
 
230
229
  P = _P(x)
231
230
  Q = _Q(x)
@@ -248,15 +247,15 @@ class Wigner(object):
248
247
 
249
248
  def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
250
249
  """
251
- Stieltjes transform (principal or secondary branch)
252
- for Marchenko–Pastur distribution on upper half-plane.
250
+ Stieltjes transform (principal or secondary branch) for Wigner
251
+ distribution on upper half-plane.
253
252
  """
254
253
 
255
254
  m = numpy.empty_like(z, dtype=complex)
256
255
 
257
256
  # Use quadratic form
258
257
  sign = -1 if alt_branch else 1
259
- A = 1.0
258
+ A = (self.r**2) / 4.0
260
259
  B = z
261
260
  D = B**2 - 4 * A
262
261
  sqrtD = numpy.sqrt(D)
@@ -346,7 +345,7 @@ class Wigner(object):
346
345
  .. code-block:: python
347
346
 
348
347
  >>> from freealg.distributions import Wigner
349
- >>> wg = Wigner()
348
+ >>> wg = Wigner(1)
350
349
  >>> m1, m2 = wg.stieltjes(plot=True)
351
350
 
352
351
  .. image:: ../_static/images/plots/wg_stieltjes.png
@@ -357,7 +356,7 @@ class Wigner(object):
357
356
 
358
357
  .. code-block:: python
359
358
 
360
- >>> m1, m2 = mp.stieltjes(plot=True, on_disk=True)
359
+ >>> m1, m2 = wg.stieltjes(plot=True, on_disk=True)
361
360
 
362
361
  .. image:: ../_static/images/plots/wg_stieltjes_disk.png
363
362
  :align: center
@@ -437,7 +436,7 @@ class Wigner(object):
437
436
  is used.
438
437
 
439
438
  method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
440
- Method of drawing samples from uniform distirbution:
439
+ Method of drawing samples from uniform distribution:
441
440
 
442
441
  * ``'mc'``: Monte Carlo
443
442
  * ``'qmc'``: Quasi Monte Carlo
@@ -471,7 +470,7 @@ class Wigner(object):
471
470
  .. code-block::python
472
471
 
473
472
  >>> from freealg.distributions import Wigner
474
- >>> wg = Wigner()
473
+ >>> wg = Wigner(1)
475
474
  >>> s = wg.sample(2000)
476
475
 
477
476
  .. image:: ../_static/images/plots/wg_samples.png
@@ -553,15 +552,7 @@ class Wigner(object):
553
552
 
554
553
  # Parameters
555
554
  n = size
556
- p = 1.0 / size
557
-
558
- # Random graph
559
- G = nx.erdos_renyi_graph(n, p)
560
-
561
- # Adjancency
562
- A = nx.to_numpy_array(G) # shape (n,n), 0/1 entries
563
-
564
- # Center & scale to get the semicircle
565
- A_c = (A - p) / numpy.sqrt(n * p * (1-p))
555
+ X = numpy.random.randn(n, n)
556
+ X = (numpy.triu(X, 0) + numpy.triu(X, 1).T)
566
557
 
567
- return A_c
558
+ return X * (self.r / (2.0 * numpy.sqrt(n)))
freealg/freeform.py CHANGED
@@ -631,8 +631,7 @@ class FreeForm(object):
631
631
  # stieltjes
632
632
  # =========
633
633
 
634
- def stieltjes(self, x, y, plot=False, latex=False, char_curves=None,
635
- save=False):
634
+ def stieltjes(self, x, y, plot=False, latex=False, save=False):
636
635
  """
637
636
  Compute Stieltjes transform of the spectral density over a 2D Cartesian
638
637
  grid on the complex plane.
@@ -656,10 +655,6 @@ class FreeForm(object):
656
655
  If `True`, the plot is rendered using LaTeX. This option is
657
656
  relevant only if ``plot=True``.
658
657
 
659
- char_curves : numpy.array, default=None
660
- If ``plot=True``, also plot characteristic curves starting from
661
- these locations.
662
-
663
658
  save : bool, default=False
664
659
  If not `False`, the plot is saved. If a string is given, it is
665
660
  assumed to the save filename (with the file extension). This option
@@ -747,9 +742,7 @@ class FreeForm(object):
747
742
  m2[mask_m, :] = -m1[mask_m, :] + self._glue(z[mask_m, :])
748
743
 
749
744
  if plot:
750
- plot_stieltjes(x, y, m1, m2, self.support, latex=latex,
751
- char_curves={'matrix': self, 'z': char_curves},
752
- save=save)
745
+ plot_stieltjes(x, y, m1, m2, self.support, latex=latex, save=save)
753
746
 
754
747
  return m1, m2
755
748
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -24,12 +24,12 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Python: >=3.9
25
25
  Description-Content-Type: text/x-rst
26
26
  License-File: LICENSE.txt
27
+ License-File: AUTHORS.txt
27
28
  Requires-Dist: numpy
28
29
  Requires-Dist: scipy
29
30
  Requires-Dist: texplot
30
31
  Requires-Dist: matplotlib
31
32
  Requires-Dist: colorcet
32
- Requires-Dist: networkx
33
33
  Requires-Dist: statsmodels
34
34
  Provides-Extra: test
35
35
  Provides-Extra: docs
@@ -0,0 +1,23 @@
1
+ freealg/__init__.py,sha256=K92neXJZ9VE1U_j_pj28Qyq1MzlMXhOuYK2ZihgwCaU,463
2
+ freealg/__version__.py,sha256=C69ADlbQREQlR15trneyA2sk8x0-oH4rDAX5fsv19_U,22
3
+ freealg/_chebyshev.py,sha256=X6u5pKjR1HPZ-KbCfr7zT6HRwB6pZMADvVS3sT5LTkA,5638
4
+ freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
5
+ freealg/_decompress.py,sha256=7U2lL8F5z76aFuZJBsPj70jEVRuzvJHnIh5FSw-aLME,4680
6
+ freealg/_jacobi.py,sha256=AT4ONSHGGDxVKE3MGMLyMR8uDFiO-e9u3x5udYfdJJk,5635
7
+ freealg/_pade.py,sha256=mP96wEPfIzHLZ6PDB5OyhmSA8N1uVPVUkmJa3ebXXiU,13623
8
+ freealg/_plot_util.py,sha256=wVx99GRdIFu_wzmG8f5JSDZ65BJohnuSBm3mZ58wElg,18426
9
+ freealg/_sample.py,sha256=K1ZxKoiuPbEKyh-swL5X7gz1kYcQno6Mof0o1xF38tg,2323
10
+ freealg/_util.py,sha256=alJ9s1U_sHL7dXq7hI10fa8CF_AZ6Xmy_QsoyDYPSDQ,3677
11
+ freealg/freeform.py,sha256=kbh7UoOJkAVFKj2Zmddy803-asoslkqn-gWJ-HpLN7U,28750
12
+ freealg/distributions/__init__.py,sha256=ufiL5OG_Jyma3D2il0BedhGuilROilbmSjxqoiz45GE,574
13
+ freealg/distributions/kesten_mckay.py,sha256=DNxDleEPg0NVsIuECU__24ianK_VE_kuU7c8aRFJBOE,19283
14
+ freealg/distributions/marchenko_pastur.py,sha256=_lZMKu7TmGu9dkusEJ9ZO5OD04TUuvoQcqN4u5PdAQ4,16403
15
+ freealg/distributions/meixner.py,sha256=PgVjAwvr2Xq-5Ghx002ByGWPlfDPs2Zfhh8BmILkoks,16957
16
+ freealg/distributions/wachter.py,sha256=vhyboqYXqisoUCNCVZDhuM3VL5E-IVBSpTamotN-4TI,16276
17
+ freealg/distributions/wigner.py,sha256=ZcEYcYBUJdLp-5dEqc_h0_qPv49z6ed4S9l6jwptpyE,15331
18
+ freealg-0.1.8.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
19
+ freealg-0.1.8.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
20
+ freealg-0.1.8.dist-info/METADATA,sha256=KtQ-AACsZ-KH8Jn-m_QovDFmIzxbIGLeQeGObvY9mRA,2941
21
+ freealg-0.1.8.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
22
+ freealg-0.1.8.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
23
+ freealg-0.1.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ Siavash Ameli
2
+ Liam Hodgkinson
@@ -1,21 +0,0 @@
1
- freealg/__init__.py,sha256=K92neXJZ9VE1U_j_pj28Qyq1MzlMXhOuYK2ZihgwCaU,463
2
- freealg/__version__.py,sha256=n3oM6B_EMz93NsTI18NNZd-jKFcUPzUkbIKj5VFK5ok,22
3
- freealg/_chebyshev.py,sha256=X6u5pKjR1HPZ-KbCfr7zT6HRwB6pZMADvVS3sT5LTkA,5638
4
- freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
5
- freealg/_decompress.py,sha256=7U2lL8F5z76aFuZJBsPj70jEVRuzvJHnIh5FSw-aLME,4680
6
- freealg/_jacobi.py,sha256=AT4ONSHGGDxVKE3MGMLyMR8uDFiO-e9u3x5udYfdJJk,5635
7
- freealg/_pade.py,sha256=mP96wEPfIzHLZ6PDB5OyhmSA8N1uVPVUkmJa3ebXXiU,13623
8
- freealg/_plot_util.py,sha256=Ng9c_U9kxsRb16GsvVa2u4AiXUKw0oD4E3BB_e9d9h8,19575
9
- freealg/_sample.py,sha256=K1ZxKoiuPbEKyh-swL5X7gz1kYcQno6Mof0o1xF38tg,2323
10
- freealg/_util.py,sha256=alJ9s1U_sHL7dXq7hI10fa8CF_AZ6Xmy_QsoyDYPSDQ,3677
11
- freealg/freeform.py,sha256=pZM4IUVegXjKCi9484Gz4bPkYt0tRvslTrha_yKW7y4,29042
12
- freealg/distributions/__init__.py,sha256=Hnk9bJi4Wy8I_1uuskRyrT2DUpPN1YmBY5uK7XI3U_o,644
13
- freealg/distributions/kesten_mckay.py,sha256=Oq2FCX60seojy7LDn8nYPrbqinmXv4YC-93VxlmDQ6M,15913
14
- freealg/distributions/marchenko_pastur.py,sha256=GwDTN-7au2h7H7PnZkQfs6bas8fNhgEnQ-hTWsBMZuE,16403
15
- freealg/distributions/wachter.py,sha256=2eqbJY4S1MqLjgqO6qY06m3-_s-bKTuSiryS_ZH_xvI,16136
16
- freealg/distributions/wigner.py,sha256=MSrB-HLMzOwnWDDzw5XPLsoL4LEIV35w5jWeL-qDn9Y,15448
17
- freealg-0.1.6.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
18
- freealg-0.1.6.dist-info/METADATA,sha256=eIE88q1eRccGUJdZmKYfjoP6p-XTP4y7tBkKrCb3jfU,2939
19
- freealg-0.1.6.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
20
- freealg-0.1.6.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
21
- freealg-0.1.6.dist-info/RECORD,,