freealg 0.1.5__py3-none-any.whl → 0.1.7__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.5"
1
+ __version__ = "0.1.7"
freealg/_decompress.py CHANGED
@@ -11,8 +11,9 @@
11
11
  # =======
12
12
 
13
13
  import numpy
14
+ # from scipy.integrate import solve_ivp
14
15
 
15
- __all__ = ['decompress']
16
+ __all__ = ['decompress', 'reverse_characteristics']
16
17
 
17
18
 
18
19
  # ==========
@@ -134,3 +135,46 @@ def decompress(matrix, size, x=None, delta=1e-4, iterations=500, step_size=0.1,
134
135
  rho = rho.reshape(*x.shape)
135
136
 
136
137
  return rho, x, (lb, ub)
138
+
139
+
140
+ # =======================
141
+ # reverse characteristics
142
+ # =======================
143
+
144
+ def reverse_characteristics(matrix, z_inits, T, iterations=500, step_size=0.1,
145
+ tolerance=1e-8):
146
+ """
147
+ """
148
+
149
+ t_span = (0, T)
150
+ t_eval = numpy.linspace(t_span[0], t_span[1], 50)
151
+
152
+ m = matrix._eval_stieltjes
153
+
154
+ def _char_z(z, t):
155
+ return z + (1 / m(z)[1]) * (1 - numpy.exp(t))
156
+
157
+ target_z, target_t = numpy.meshgrid(z_inits, t_eval)
158
+
159
+ z = numpy.full(target_z.shape, numpy.mean(matrix.support) - .1j,
160
+ dtype=numpy.complex128)
161
+
162
+ # Broken Newton steps can produce a lot of warnings. Removing them for now.
163
+ with numpy.errstate(all='ignore'):
164
+ for _ in range(iterations):
165
+ objective = _char_z(z, target_t) - target_z
166
+ mask = numpy.abs(objective) >= tolerance
167
+ if not numpy.any(mask):
168
+ break
169
+ z_m = z[mask]
170
+ t_m = target_t[mask]
171
+
172
+ # Perform finite difference approximation
173
+ dfdz = _char_z(z_m+tolerance, t_m) - _char_z(z_m-tolerance, t_m)
174
+ dfdz /= 2*tolerance
175
+ dfdz[dfdz == 0] = 1.0
176
+
177
+ # Perform Newton step
178
+ z[mask] = z_m - step_size * objective[mask] / dfdz
179
+
180
+ return z
@@ -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']
@@ -61,7 +61,7 @@ class KestenMcKay(object):
61
61
  Notes
62
62
  -----
63
63
 
64
- The Marchenko-Pastur distribution has the absolutely-continuous density
64
+ The Kesten-McKay distribution has the absolutely-continuous density
65
65
 
66
66
  .. math::
67
67
 
@@ -236,10 +236,10 @@ class KestenMcKay(object):
236
236
  x = numpy.linspace(x_min, x_max, 500)
237
237
 
238
238
  def _P(x):
239
- return (self.d - 2.0) * x
239
+ return (self.d - 2.0) * x / (self.d - 1.0)
240
240
 
241
241
  def _Q(x):
242
- return self.d**2 - x**2
242
+ return (self.d**2 - x**2) / (self.d - 1.0)
243
243
 
244
244
  P = _P(x)
245
245
  Q = _Q(x)
@@ -262,15 +262,15 @@ class KestenMcKay(object):
262
262
 
263
263
  def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
264
264
  """
265
- Stieltjes transform (principal or secondary branch)
266
- for Marchenko–Pastur distribution on upper half-plane.
265
+ Stieltjes transform (principal or secondary branch) for Kesten-McKay
266
+ distribution on upper half-plane.
267
267
  """
268
268
 
269
269
  m = numpy.empty_like(z, dtype=complex)
270
270
 
271
271
  sign = -1 if alt_branch else 1
272
- A = self.d**2 - z**2
273
- B = (self.d - 2.0) * z
272
+ A = (self.d**2 - z**2) / (self.d - 1.0)
273
+ B = ((self.d - 2.0) * z) / (self.d - 1.0)
274
274
  D = B**2 - 4 * A
275
275
  sqrtD = numpy.sqrt(D)
276
276
  m1 = (-B + sqrtD) / (2 * A)
@@ -370,7 +370,7 @@ class KestenMcKay(object):
370
370
 
371
371
  .. code-block:: python
372
372
 
373
- >>> m1, m2 = mp.stieltjes(plot=True, on_disk=True)
373
+ >>> m1, m2 = km.stieltjes(plot=True, on_disk=True)
374
374
 
375
375
  .. image:: ../_static/images/plots/km_stieltjes_disk.png
376
376
  :align: center
@@ -0,0 +1,584 @@
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 distributon. 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):
102
+ """
103
+ Initialization.
104
+ """
105
+
106
+ self.a = a
107
+ self.b = b
108
+ self.lam_p = self.a + 2.0 * numpy.sqrt(1.0 + self.b)
109
+ self.lam_m = self.a - 2.0 * numpy.sqrt(1.0 + self.b)
110
+ self.support = (self.lam_m, self.lam_p)
111
+
112
+ # =======
113
+ # density
114
+ # =======
115
+
116
+ def density(self, x=None, plot=False, latex=False, save=False):
117
+ """
118
+ Density of distribution.
119
+
120
+ Parameters
121
+ ----------
122
+
123
+ x : numpy.array, default=None
124
+ The locations where density is evaluated at. If `None`, an interval
125
+ slightly larger than the support interval of the spectral density
126
+ is used.
127
+
128
+ rho : numpy.array, default=None
129
+ Density. If `None`, it will be computed.
130
+
131
+ plot : bool, default=False
132
+ If `True`, density is plotted.
133
+
134
+ latex : bool, default=False
135
+ If `True`, the plot is rendered using LaTeX. This option is
136
+ relevant only if ``plot=True``.
137
+
138
+ save : bool, default=False
139
+ If not `False`, the plot is saved. If a string is given, it is
140
+ assumed to the save filename (with the file extension). This option
141
+ is relevant only if ``plot=True``.
142
+
143
+ Returns
144
+ -------
145
+
146
+ rho : numpy.array
147
+ Density.
148
+
149
+ Examples
150
+ --------
151
+
152
+ .. code-block::python
153
+
154
+ >>> from freealg.distributions import Meixner
155
+ >>> mx = Meixner(2, 3)
156
+ >>> rho = mx.density(plot=True)
157
+
158
+ .. image:: ../_static/images/plots/mx_density.png
159
+ :align: center
160
+ :class: custom-dark
161
+ """
162
+
163
+ # Create x if not given
164
+ if x is None:
165
+ radius = 0.5 * (self.lam_p - self.lam_m)
166
+ center = 0.5 * (self.lam_p + self.lam_m)
167
+ scale = 1.25
168
+ x_min = numpy.floor(center - radius * scale)
169
+ x_max = numpy.ceil(center + radius * scale)
170
+ x = numpy.linspace(x_min, x_max, 500)
171
+
172
+ rho = numpy.zeros_like(x)
173
+ mask = numpy.logical_and(x > self.lam_m, x < self.lam_p)
174
+
175
+ rho[mask] = \
176
+ numpy.sqrt(4.0 * (1.0 + self.b) - (x[mask] - self.a)**2) / \
177
+ (2.0 * numpy.pi * (self.b * x[mask]**2 + self.a * x[mask] + 1))
178
+
179
+ if plot:
180
+ plot_density(x, rho, label='', latex=latex, save=save)
181
+
182
+ return rho
183
+
184
+ # =======
185
+ # hilbert
186
+ # =======
187
+
188
+ def hilbert(self, x=None, plot=False, latex=False, save=False):
189
+ """
190
+ Hilbert transform of the distribution.
191
+
192
+ Parameters
193
+ ----------
194
+
195
+ x : numpy.array, default=None
196
+ The locations where Hilbert transform is evaluated at. If `None`,
197
+ an interval slightly larger than the support interval of the
198
+ spectral density is used.
199
+
200
+ plot : bool, default=False
201
+ If `True`, Hilbert transform is plotted.
202
+
203
+ latex : bool, default=False
204
+ If `True`, the plot is rendered using LaTeX. This option is
205
+ relevant only if ``plot=True``.
206
+
207
+ save : bool, default=False
208
+ If not `False`, the plot is saved. If a string is given, it is
209
+ assumed to the save filename (with the file extension). This option
210
+ is relevant only if ``plot=True``.
211
+
212
+ Returns
213
+ -------
214
+
215
+ hilb : numpy.array
216
+ Hilbert transform.
217
+
218
+ Examples
219
+ --------
220
+
221
+ .. code-block::python
222
+
223
+ >>> from freealg.distributions import Meixner
224
+ >>> mx = Meixner(2, 3)
225
+ >>> hilb = mx.hilbert(plot=True)
226
+
227
+ .. image:: ../_static/images/plots/mx_hilbert.png
228
+ :align: center
229
+ :class: custom-dark
230
+ """
231
+
232
+ # Create x if not given
233
+ if x is None:
234
+ radius = 0.5 * (self.lam_p - self.lam_m)
235
+ center = 0.5 * (self.lam_p + self.lam_m)
236
+ scale = 1.25
237
+ x_min = numpy.floor(center - radius * scale)
238
+ x_max = numpy.ceil(center + radius * scale)
239
+ x = numpy.linspace(x_min, x_max, 500)
240
+
241
+ def _P(x):
242
+ denom = 1.0 + self.b
243
+ return ((1.0 + 2.0 * self.b) * x + self.a) / denom
244
+
245
+ def _Q(x):
246
+ denom = 1.0 + self.b
247
+ return (self.b * x**2 + self.a * x + 1.0) / denom
248
+
249
+ P = _P(x)
250
+ Q = _Q(x)
251
+ Delta2 = P**2 - 4.0 * Q
252
+ Delta = numpy.sqrt(numpy.maximum(Delta2, 0))
253
+ sign = numpy.sign(P)
254
+ hilb = (P - sign * Delta) / (2.0 * Q)
255
+
256
+ # using negative sign convention
257
+ hilb = -hilb
258
+
259
+ if plot:
260
+ plot_hilbert(x, hilb, support=self.support, latex=latex, save=save)
261
+
262
+ return hilb
263
+
264
+ # =======================
265
+ # m mp numeric vectorized
266
+ # =======================
267
+
268
+ def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
269
+ """
270
+ Stieltjes transform (principal or secondary branch) for Meixner
271
+ distribution on upper half-plane.
272
+ """
273
+
274
+ sign = -1 if alt_branch else 1
275
+ denom = 1.0 + self.b
276
+ A = (self.b * z**2 + self.a * z + 1.0) / denom
277
+ B = ((1.0 + 2.0 * self.b) * z + self.a) / denom
278
+ D = B**2 - 4 * A
279
+ sqrtD = numpy.sqrt(D)
280
+ m1 = (-B + sqrtD) / (2 * A)
281
+ m2 = (-B - sqrtD) / (2 * A)
282
+
283
+ # pick correct branch only for non‑masked entries
284
+ upper = z.imag >= 0
285
+ branch = numpy.empty_like(m1)
286
+ branch[upper] = numpy.where(sign*m1[upper].imag > 0, m1[upper],
287
+ m2[upper])
288
+ branch[~upper] = numpy.where(sign*m1[~upper].imag < 0, m1[~upper],
289
+ m2[~upper])
290
+ m = branch
291
+
292
+ return m
293
+
294
+ # ============
295
+ # m mp reflect
296
+ # ============
297
+
298
+ def _m_mp_reflect(self, z, alt_branch=False):
299
+ """
300
+ Analytic continuation using Schwarz reflection.
301
+ """
302
+
303
+ mask_p = z.imag >= 0.0
304
+ mask_n = z.imag < 0.0
305
+
306
+ m = numpy.zeros_like(z)
307
+
308
+ f = self._m_mp_numeric_vectorized
309
+ m[mask_p] = f(z[mask_p], alt_branch=False)
310
+ m[mask_n] = f(z[mask_n], alt_branch=alt_branch)
311
+
312
+ return m
313
+
314
+ # =========
315
+ # stieltjes
316
+ # =========
317
+
318
+ def stieltjes(self, x=None, y=None, plot=False, on_disk=False, latex=False,
319
+ save=False):
320
+ """
321
+ Stieltjes transform of distribution.
322
+
323
+ Parameters
324
+ ----------
325
+
326
+ x : numpy.array, default=None
327
+ The x axis of the grid where the Stieltjes transform is evaluated.
328
+ If `None`, an interval slightly larger than the support interval of
329
+ the spectral density is used.
330
+
331
+ y : numpy.array, default=None
332
+ The y axis of the grid where the Stieltjes transform is evaluated.
333
+ If `None`, a grid on the interval ``[-1, 1]`` is used.
334
+
335
+ plot : bool, default=False
336
+ If `True`, Stieltjes transform is plotted.
337
+
338
+ on_disk : bool, default=False
339
+ If `True`, the Stieltjes transform is mapped on unit disk. This
340
+ option relevant only if ``plot=True``.
341
+
342
+ latex : bool, default=False
343
+ If `True`, the plot is rendered using LaTeX. This option is
344
+ relevant only if ``plot=True``.
345
+
346
+ save : bool, default=False
347
+ If not `False`, the plot is saved. If a string is given, it is
348
+ assumed to the save filename (with the file extension). This option
349
+ is relevant only if ``plot=True``.
350
+
351
+ Returns
352
+ -------
353
+
354
+ m1 : numpy.array
355
+ Stieltjes transform on principal branch.
356
+
357
+ m12 : numpy.array
358
+ Stieltjes transform on secondary branch.
359
+
360
+ Examples
361
+ --------
362
+
363
+ .. code-block:: python
364
+
365
+ >>> from freealg.distributions import Meixner
366
+ >>> mx = Meixner(2, 3)
367
+ >>> m1, m2 = mx.stieltjes(plot=True)
368
+
369
+ .. image:: ../_static/images/plots/mx_stieltjes.png
370
+ :align: center
371
+ :class: custom-dark
372
+
373
+ Plot on unit disk using Cayley transform:
374
+
375
+ .. code-block:: python
376
+
377
+ >>> m1, m2 = mx.stieltjes(plot=True, on_disk=True)
378
+
379
+ .. image:: ../_static/images/plots/mx_stieltjes_disk.png
380
+ :align: center
381
+ :class: custom-dark
382
+ """
383
+
384
+ if (plot is True) and (on_disk is True):
385
+ n_r = 1000
386
+ n_t = 1000
387
+ r_min, r_max = 0, 2.5
388
+ t_min, t_max = 0, 2.0 * numpy.pi
389
+ r = numpy.linspace(r_min, r_max, n_r)
390
+ t = numpy.linspace(t_min, t_max, n_t + 1)[:-1]
391
+ grid_r, grid_t = numpy.meshgrid(r, t)
392
+
393
+ grid_x_D = grid_r * numpy.cos(grid_t)
394
+ grid_y_D = grid_r * numpy.sin(grid_t)
395
+ zeta = grid_x_D + 1j * grid_y_D
396
+
397
+ # Cayley transform mapping zeta on D to z on H
398
+ z_H = 1j * (1 + zeta) / (1 - zeta)
399
+
400
+ m1_D = self._m_mp_reflect(z_H, alt_branch=False)
401
+ m2_D = self._m_mp_reflect(z_H, alt_branch=True)
402
+
403
+ plot_stieltjes_on_disk(r, t, m1_D, m2_D, support=self.support,
404
+ latex=latex, save=save)
405
+
406
+ return m1_D, m2_D
407
+
408
+ # Create x if not given
409
+ if x is None:
410
+ radius = 0.5 * (self.lam_p - self.lam_m)
411
+ center = 0.5 * (self.lam_p + self.lam_m)
412
+ scale = 2.0
413
+ x_min = numpy.floor(2.0 * (center - 2.0 * radius * scale)) / 2.0
414
+ x_max = numpy.ceil(2.0 * (center + 2.0 * radius * scale)) / 2.0
415
+ x = numpy.linspace(x_min, x_max, 500)
416
+
417
+ # Create y if not given
418
+ if y is None:
419
+ y = numpy.linspace(-1, 1, 400)
420
+
421
+ x_grid, y_grid = numpy.meshgrid(x, y)
422
+ z = x_grid + 1j * y_grid # shape (Ny, Nx)
423
+
424
+ m1 = self._m_mp_reflect(z, alt_branch=False)
425
+ m2 = self._m_mp_reflect(z, alt_branch=True)
426
+
427
+ if plot:
428
+ plot_stieltjes(x, y, m1, m2, support=self.support, latex=latex,
429
+ save=save)
430
+
431
+ return m1, m2
432
+
433
+ # ======
434
+ # sample
435
+ # ======
436
+
437
+ def sample(self, size, x_min=None, x_max=None, method='qmc', plot=False,
438
+ latex=False, save=False):
439
+ """
440
+ Sample from distribution.
441
+
442
+ Parameters
443
+ ----------
444
+
445
+ size : int
446
+ Size of sample.
447
+
448
+ x_min : float, default=None
449
+ Minimum of sample values. If `None`, the left edge of the support
450
+ is used.
451
+
452
+ x_max : float, default=None
453
+ Maximum of sample values. If `None`, the right edge of the support
454
+ is used.
455
+
456
+ method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
457
+ Method of drawing samples from uniform distirbution:
458
+
459
+ * ``'mc'``: Monte Carlo
460
+ * ``'qmc'``: Quasi Monte Carlo
461
+
462
+ plot : bool, default=False
463
+ If `True`, samples histogram is plotted.
464
+
465
+ latex : bool, default=False
466
+ If `True`, the plot is rendered using LaTeX. This option is
467
+ relevant only if ``plot=True``.
468
+
469
+ save : bool, default=False
470
+ If not `False`, the plot is saved. If a string is given, it is
471
+ assumed to the save filename (with the file extension). This option
472
+ is relevant only if ``plot=True``.
473
+
474
+ Returns
475
+ -------
476
+
477
+ s : numpy.ndarray
478
+ Samples.
479
+
480
+ Notes
481
+ -----
482
+
483
+ This method uses inverse transform sampling.
484
+
485
+ Examples
486
+ --------
487
+
488
+ .. code-block::python
489
+
490
+ >>> from freealg.distributions import Meixner
491
+ >>> mx = Meixner(2, 3)
492
+ >>> s = mx.sample(2000)
493
+
494
+ .. image:: ../_static/images/plots/mx_samples.png
495
+ :align: center
496
+ :class: custom-dark
497
+ """
498
+
499
+ if x_min is None:
500
+ x_min = self.lam_m
501
+
502
+ if x_max is None:
503
+ x_max = self.lam_p
504
+
505
+ # Grid and PDF
506
+ xs = numpy.linspace(x_min, x_max, size)
507
+ pdf = self.density(xs)
508
+
509
+ # CDF (using cumulative trapezoidal rule)
510
+ cdf = cumtrapz(pdf, xs, initial=0)
511
+ cdf /= cdf[-1] # normalize CDF to 1
512
+
513
+ # Inverse CDF interpolator
514
+ inv_cdf = interp1d(cdf, xs, bounds_error=False,
515
+ fill_value=(x_min, x_max))
516
+
517
+ # Draw from uniform distribution
518
+ if method == 'mc':
519
+ u = numpy.random.rand(size)
520
+ elif method == 'qmc':
521
+ engine = qmc.Halton(d=1)
522
+ u = engine.random(size)
523
+ else:
524
+ raise ValueError('"method" is invalid.')
525
+
526
+ # Draw from distribution by mapping from inverse CDF
527
+ samples = inv_cdf(u).ravel()
528
+
529
+ if plot:
530
+ radius = 0.5 * (self.lam_p - self.lam_m)
531
+ center = 0.5 * (self.lam_p + self.lam_m)
532
+ scale = 1.25
533
+ x_min = numpy.floor(center - radius * scale)
534
+ x_max = numpy.ceil(center + radius * scale)
535
+ x = numpy.linspace(x_min, x_max, 500)
536
+ rho = self.density(x)
537
+ plot_samples(x, rho, x_min, x_max, samples, latex=latex, save=save)
538
+
539
+ return samples
540
+
541
+ # ======
542
+ # matrix
543
+ # ======
544
+
545
+ def matrix(self, size):
546
+ """
547
+ Generate matrix with the spectral density of the distribution.
548
+
549
+ Parameters
550
+ ----------
551
+
552
+ size : int
553
+ Size :math:`n` of the matrix.
554
+
555
+ Returns
556
+ -------
557
+
558
+ Sx : numpy.ndarray
559
+ A matrix of the size :math:`n \\times n`.
560
+
561
+ Sy : numpy.ndarray
562
+ A matrix of the size :math:`n \\times n`.
563
+
564
+ Examples
565
+ --------
566
+
567
+ .. code-block::python
568
+
569
+ >>> from freealg.distributions import Meixner
570
+ >>> mx = Meixner(2, 3)
571
+ >>> A = mx.matrix(2000)
572
+ """
573
+
574
+ n = size
575
+ m1 = int(self.a * n)
576
+ m2 = int(self.b * n)
577
+
578
+ X = numpy.random.randn(n, m1)
579
+ Y = numpy.random.randn(n, m2)
580
+
581
+ Sx = X @ X.T
582
+ Sy = Y @ Y.T
583
+
584
+ return Sx, Sy
@@ -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
@@ -55,7 +55,7 @@ class Wigner(object):
55
55
  Notes
56
56
  -----
57
57
 
58
- The Marchenko-Pastur distribution has the absolutely-continuous density
58
+ The Wigner distribution has the absolutely-continuous density
59
59
 
60
60
  .. math::
61
61
 
@@ -248,8 +248,8 @@ class Wigner(object):
248
248
 
249
249
  def _m_mp_numeric_vectorized(self, z, alt_branch=False, tol=1e-8):
250
250
  """
251
- Stieltjes transform (principal or secondary branch)
252
- for Marchenko–Pastur distribution on upper half-plane.
251
+ Stieltjes transform (principal or secondary branch) for Wigner
252
+ distribution on upper half-plane.
253
253
  """
254
254
 
255
255
  m = numpy.empty_like(z, dtype=complex)
@@ -357,7 +357,7 @@ class Wigner(object):
357
357
 
358
358
  .. code-block:: python
359
359
 
360
- >>> m1, m2 = mp.stieltjes(plot=True, on_disk=True)
360
+ >>> m1, m2 = wg.stieltjes(plot=True, on_disk=True)
361
361
 
362
362
  .. image:: ../_static/images/plots/wg_stieltjes_disk.png
363
363
  :align: center
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -24,6 +24,7 @@ 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
@@ -0,0 +1,23 @@
1
+ freealg/__init__.py,sha256=K92neXJZ9VE1U_j_pj28Qyq1MzlMXhOuYK2ZihgwCaU,463
2
+ freealg/__version__.py,sha256=YpKDcdV7CqL8n45u267wKtyloM13FSVbOdrqgNZnSLM,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=nvCEPKVjZCYNt-MLlFSzTfj8PTlcLmmGW9AefvYJxuU,15977
14
+ freealg/distributions/marchenko_pastur.py,sha256=GwDTN-7au2h7H7PnZkQfs6bas8fNhgEnQ-hTWsBMZuE,16403
15
+ freealg/distributions/meixner.py,sha256=y_iLB5LqvkT9P_rGa46ZCmk_EFfPS_3VuFCZpeD2ooI,16172
16
+ freealg/distributions/wachter.py,sha256=IYKok4stONv0RrigSRDWGLEyBmg6j3hS8c9_fw31kZw,16276
17
+ freealg/distributions/wigner.py,sha256=LE-KDxFb8q7-ifWUv7_LrEpOGYYTMdVPFfzIhm4vJKg,15426
18
+ freealg-0.1.7.dist-info/licenses/AUTHORS.txt,sha256=0b67Nz4_JgIzUupHJTAZxu5QdSUM_HRM_X_w4xCb17o,30
19
+ freealg-0.1.7.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
20
+ freealg-0.1.7.dist-info/METADATA,sha256=zCRGrnjGgjLlx9WToqSyBR_jqFsMzZIxPbWOM4fMgYE,2965
21
+ freealg-0.1.7.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
22
+ freealg-0.1.7.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
23
+ freealg-0.1.7.dist-info/RECORD,,
@@ -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=rPSfWgIeq2YWVPyESOAwCBt8vftsTpIkuLAGDEzyRQc,22
3
- freealg/_chebyshev.py,sha256=X6u5pKjR1HPZ-KbCfr7zT6HRwB6pZMADvVS3sT5LTkA,5638
4
- freealg/_damp.py,sha256=k2vtBtWOxQBf4qXaWu_En81lQBXbEO4QbxxWpvuVhdE,1802
5
- freealg/_decompress.py,sha256=H7ocq09gQnCY-q_8xHi6qyYj3qp239MCgj406hn0yeE,3344
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=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.5.dist-info/licenses/LICENSE.txt,sha256=J-EEYEtxb3VVf_Bn1TYfWnpY5lMFIM15iLDDcnaDTPA,1443
18
- freealg-0.1.5.dist-info/METADATA,sha256=sG6aPyglynO4hXzXNnMPDliEtC5JLlrOMy84e_KMDzU,2939
19
- freealg-0.1.5.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
20
- freealg-0.1.5.dist-info/top_level.txt,sha256=eR2wrgYwDdnnJ9Zf5PruPqe4kQav0GMvRsqct6y00Q8,8
21
- freealg-0.1.5.dist-info/RECORD,,