freealg 0.1.4__tar.gz → 0.1.6__tar.gz

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 (32) hide show
  1. {freealg-0.1.4/freealg.egg-info → freealg-0.1.6}/PKG-INFO +1 -1
  2. freealg-0.1.6/freealg/__version__.py +1 -0
  3. {freealg-0.1.4 → freealg-0.1.6}/freealg/_decompress.py +45 -1
  4. {freealg-0.1.4 → freealg-0.1.6}/freealg/_plot_util.py +43 -7
  5. {freealg-0.1.4 → freealg-0.1.6}/freealg/distributions/kesten_mckay.py +20 -5
  6. {freealg-0.1.4 → freealg-0.1.6}/freealg/distributions/marchenko_pastur.py +20 -5
  7. {freealg-0.1.4 → freealg-0.1.6}/freealg/distributions/wachter.py +20 -5
  8. {freealg-0.1.4 → freealg-0.1.6}/freealg/distributions/wigner.py +20 -5
  9. {freealg-0.1.4 → freealg-0.1.6}/freealg/freeform.py +9 -2
  10. {freealg-0.1.4 → freealg-0.1.6/freealg.egg-info}/PKG-INFO +1 -1
  11. freealg-0.1.4/freealg/__version__.py +0 -1
  12. {freealg-0.1.4 → freealg-0.1.6}/CHANGELOG.rst +0 -0
  13. {freealg-0.1.4 → freealg-0.1.6}/LICENSE.txt +0 -0
  14. {freealg-0.1.4 → freealg-0.1.6}/MANIFEST.in +0 -0
  15. {freealg-0.1.4 → freealg-0.1.6}/README.rst +0 -0
  16. {freealg-0.1.4 → freealg-0.1.6}/freealg/__init__.py +0 -0
  17. {freealg-0.1.4 → freealg-0.1.6}/freealg/_chebyshev.py +0 -0
  18. {freealg-0.1.4 → freealg-0.1.6}/freealg/_damp.py +0 -0
  19. {freealg-0.1.4 → freealg-0.1.6}/freealg/_jacobi.py +0 -0
  20. {freealg-0.1.4 → freealg-0.1.6}/freealg/_pade.py +0 -0
  21. {freealg-0.1.4 → freealg-0.1.6}/freealg/_sample.py +0 -0
  22. {freealg-0.1.4 → freealg-0.1.6}/freealg/_util.py +0 -0
  23. {freealg-0.1.4 → freealg-0.1.6}/freealg/distributions/__init__.py +0 -0
  24. {freealg-0.1.4 → freealg-0.1.6}/freealg.egg-info/SOURCES.txt +0 -0
  25. {freealg-0.1.4 → freealg-0.1.6}/freealg.egg-info/dependency_links.txt +0 -0
  26. {freealg-0.1.4 → freealg-0.1.6}/freealg.egg-info/not-zip-safe +0 -0
  27. {freealg-0.1.4 → freealg-0.1.6}/freealg.egg-info/requires.txt +0 -0
  28. {freealg-0.1.4 → freealg-0.1.6}/freealg.egg-info/top_level.txt +0 -0
  29. {freealg-0.1.4 → freealg-0.1.6}/pyproject.toml +0 -0
  30. {freealg-0.1.4 → freealg-0.1.6}/requirements.txt +0 -0
  31. {freealg-0.1.4 → freealg-0.1.6}/setup.cfg +0 -0
  32. {freealg-0.1.4 → freealg-0.1.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -0,0 +1 @@
1
+ __version__ = "0.1.6"
@@ -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
@@ -18,6 +18,7 @@ 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
21
22
 
22
23
  __all__ = ['plot_fit', 'plot_density', 'plot_hilbert', 'plot_stieltjes',
23
24
  'plot_stieltjes_on_disk']
@@ -243,11 +244,42 @@ def _value_formatter(v, pos):
243
244
  return f"{m_val:.1f}"
244
245
 
245
246
 
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
+
246
277
  # ==============
247
278
  # plot stieltjes
248
279
  # ==============
249
280
 
250
- def plot_stieltjes(x, y, m1, m2, support, latex=False, save=False):
281
+ def plot_stieltjes(x, y, m1, m2, support, latex=False, char_curves=None,
282
+ save=False):
251
283
  """
252
284
  """
253
285
 
@@ -275,8 +307,8 @@ def plot_stieltjes(x, y, m1, m2, support, latex=False, save=False):
275
307
  ax0.plot([lam_m, lam_p], [eps, eps], '-', linewidth=1, color='black')
276
308
  ax0.set_xlabel(r'$\mathrm{Re}(z)$')
277
309
  ax0.set_ylabel(r'$\mathrm{Im}(z)$')
278
- ax0.set_title(r'(a) Principal Branch on $\mathbb{H}$ and ' +
279
- r'$\mathbb{H}^{-}$')
310
+ ax0.set_title(r'(a) Principal Branch on $\mathbb{C}^{+}$ and ' +
311
+ r'$\mathbb{C}^{-}$')
280
312
  ax0.set_yticks(numpy.arange(y_min, y_max+0.01, 0.5))
281
313
  ax0.set_xlim([x_min, x_max])
282
314
  ax0.set_ylim([y_min, y_max])
@@ -291,12 +323,16 @@ def plot_stieltjes(x, y, m1, m2, support, latex=False, save=False):
291
323
  ax1.plot([lam_p, x_max], [eps, eps], '-', linewidth=1, color='black')
292
324
  ax1.set_xlabel(r'$\mathrm{Re}(z)$')
293
325
  ax1.set_ylabel(r'$\mathrm{Im}(z)$')
294
- ax1.set_title(r'(b) Principal Branch on $\mathbb{H}$, Secondary ' +
295
- r'Branch on $\mathbb{H}^{-}$')
326
+ ax1.set_title(r'(b) Principal Branch on $\mathbb{C}^{+}$, Secondary ' +
327
+ r'Branch on $\mathbb{C}^{-}$')
296
328
  ax1.set_yticks(numpy.arange(y_min, y_max+0.01, 0.5))
297
329
  ax1.set_xlim([x_min, x_max])
298
330
  ax1.set_ylim([y_min, y_max])
299
331
 
332
+ # Plot characteristic curves
333
+ if char_curves is not None:
334
+ _plot_char_curves(ax1, char_curves)
335
+
300
336
  pos = ax1.get_position()
301
337
  cbar_width = 0.013
302
338
  pad = 0.013
@@ -503,9 +539,9 @@ def plot_samples(x, rho, x_min, x_max, samples, latex=False, save=False):
503
539
 
504
540
  fig, ax = plt.subplots(figsize=(6, 3))
505
541
 
506
- bins = numpy.linspace(x_min, x_max, samples.size // 10)
542
+ bins = numpy.linspace(x_min, x_max, samples.size // 15)
507
543
  _ = ax.hist(samples, bins, density=True, color='silver',
508
- label='Samples histogram')
544
+ edgecolor='none', label='Samples histogram')
509
545
  ax.plot(x, rho, color='black', label='Exact density')
510
546
  ax.legend(fontsize='small')
511
547
  ax.set_ylim(bottom=0)
@@ -21,6 +21,7 @@ try:
21
21
  from scipy.integrate import cumtrapz
22
22
  except ImportError:
23
23
  from scipy.integrate import cumulative_trapezoid as cumtrapz
24
+ from scipy.stats import qmc
24
25
 
25
26
  __all__ = ['KestenMcKay']
26
27
 
@@ -429,8 +430,8 @@ class KestenMcKay(object):
429
430
  # sample
430
431
  # ======
431
432
 
432
- def sample(self, size, x_min=None, x_max=None, plot=False, latex=False,
433
- save=False):
433
+ def sample(self, size, x_min=None, x_max=None, method='qmc', plot=False,
434
+ latex=False, save=False):
434
435
  """
435
436
  Sample from distribution.
436
437
 
@@ -448,6 +449,12 @@ class KestenMcKay(object):
448
449
  Maximum of sample values. If `None`, the right edge of the support
449
450
  is used.
450
451
 
452
+ method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
453
+ Method of drawing samples from uniform distirbution:
454
+
455
+ * ``'mc'``: Monte Carlo
456
+ * ``'qmc'``: Quasi Monte Carlo
457
+
451
458
  plot : bool, default=False
452
459
  If `True`, samples histogram is plotted.
453
460
 
@@ -503,9 +510,17 @@ class KestenMcKay(object):
503
510
  inv_cdf = interp1d(cdf, xs, bounds_error=False,
504
511
  fill_value=(x_min, x_max))
505
512
 
506
- # Sample and map
507
- u = numpy.random.rand(size)
508
- samples = inv_cdf(u)
513
+ # Draw from uniform distribution
514
+ if method == 'mc':
515
+ u = numpy.random.rand(size)
516
+ elif method == 'qmc':
517
+ engine = qmc.Halton(d=1)
518
+ u = engine.random(size)
519
+ else:
520
+ raise ValueError('"method" is invalid.')
521
+
522
+ # Draw from distribution by mapping from inverse CDF
523
+ samples = inv_cdf(u).ravel()
509
524
 
510
525
  if plot:
511
526
  radius = 0.5 * (self.lam_p - self.lam_m)
@@ -20,6 +20,7 @@ try:
20
20
  from scipy.integrate import cumtrapz
21
21
  except ImportError:
22
22
  from scipy.integrate import cumulative_trapezoid as cumtrapz
23
+ from scipy.stats import qmc
23
24
 
24
25
  __all__ = ['MarchenkoPastur']
25
26
 
@@ -435,8 +436,8 @@ class MarchenkoPastur(object):
435
436
  # sample
436
437
  # ======
437
438
 
438
- def sample(self, size, x_min=None, x_max=None, plot=False, latex=False,
439
- save=False):
439
+ def sample(self, size, x_min=None, x_max=None, method='qmc', plot=False,
440
+ latex=False, save=False):
440
441
  """
441
442
  Sample from distribution.
442
443
 
@@ -454,6 +455,12 @@ class MarchenkoPastur(object):
454
455
  Maximum of sample values. If `None`, the right edge of the support
455
456
  is used.
456
457
 
458
+ method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
459
+ Method of drawing samples from uniform distirbution:
460
+
461
+ * ``'mc'``: Monte Carlo
462
+ * ``'qmc'``: Quasi Monte Carlo
463
+
457
464
  plot : bool, default=False
458
465
  If `True`, samples histogram is plotted.
459
466
 
@@ -509,9 +516,17 @@ class MarchenkoPastur(object):
509
516
  inv_cdf = interp1d(cdf, xs, bounds_error=False,
510
517
  fill_value=(x_min, x_max))
511
518
 
512
- # Sample and map
513
- u = numpy.random.rand(size)
514
- samples = inv_cdf(u)
519
+ # Draw from uniform distribution
520
+ if method == 'mc':
521
+ u = numpy.random.rand(size)
522
+ elif method == 'qmc':
523
+ engine = qmc.Halton(d=1)
524
+ u = engine.random(size)
525
+ else:
526
+ raise ValueError('"method" is invalid.')
527
+
528
+ # Draw from distribution by mapping from inverse CDF
529
+ samples = inv_cdf(u).ravel()
515
530
 
516
531
  if plot:
517
532
  radius = 0.5 * (self.lam_p - self.lam_m)
@@ -20,6 +20,7 @@ try:
20
20
  from scipy.integrate import cumtrapz
21
21
  except ImportError:
22
22
  from scipy.integrate import cumulative_trapezoid as cumtrapz
23
+ from scipy.stats import qmc
23
24
 
24
25
  __all__ = ['Wachter']
25
26
 
@@ -432,8 +433,8 @@ class Wachter(object):
432
433
  # sample
433
434
  # ======
434
435
 
435
- def sample(self, size, x_min=None, x_max=None, plot=False, latex=False,
436
- save=False):
436
+ def sample(self, size, x_min=None, x_max=None, method='qmc', plot=False,
437
+ latex=False, save=False):
437
438
  """
438
439
  Sample from distribution.
439
440
 
@@ -451,6 +452,12 @@ class Wachter(object):
451
452
  Maximum of sample values. If `None`, the right edge of the support
452
453
  is used.
453
454
 
455
+ method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
456
+ Method of drawing samples from uniform distirbution:
457
+
458
+ * ``'mc'``: Monte Carlo
459
+ * ``'qmc'``: Quasi Monte Carlo
460
+
454
461
  plot : bool, default=False
455
462
  If `True`, samples histogram is plotted.
456
463
 
@@ -506,9 +513,17 @@ class Wachter(object):
506
513
  inv_cdf = interp1d(cdf, xs, bounds_error=False,
507
514
  fill_value=(x_min, x_max))
508
515
 
509
- # Sample and map
510
- u = numpy.random.rand(size)
511
- samples = inv_cdf(u)
516
+ # Draw from uniform distribution
517
+ if method == 'mc':
518
+ u = numpy.random.rand(size)
519
+ elif method == 'qmc':
520
+ engine = qmc.Halton(d=1)
521
+ u = engine.random(size)
522
+ else:
523
+ raise ValueError('"method" is invalid.')
524
+
525
+ # Draw from distribution by mapping from inverse CDF
526
+ samples = inv_cdf(u).ravel()
512
527
 
513
528
  if plot:
514
529
  radius = 0.5 * (self.lam_p - self.lam_m)
@@ -21,6 +21,7 @@ try:
21
21
  from scipy.integrate import cumtrapz
22
22
  except ImportError:
23
23
  from scipy.integrate import cumulative_trapezoid as cumtrapz
24
+ from scipy.stats import qmc
24
25
 
25
26
  __all__ = ['Wigner']
26
27
 
@@ -416,8 +417,8 @@ class Wigner(object):
416
417
  # sample
417
418
  # ======
418
419
 
419
- def sample(self, size, x_min=None, x_max=None, plot=False, latex=False,
420
- save=False):
420
+ def sample(self, size, x_min=None, x_max=None, method='qmc', plot=False,
421
+ latex=False, save=False):
421
422
  """
422
423
  Sample from distribution.
423
424
 
@@ -435,6 +436,12 @@ class Wigner(object):
435
436
  Maximum of sample values. If `None`, the right edge of the support
436
437
  is used.
437
438
 
439
+ method : {``'mc'``, ``'qmc'``}, default= ``'qmc'``
440
+ Method of drawing samples from uniform distirbution:
441
+
442
+ * ``'mc'``: Monte Carlo
443
+ * ``'qmc'``: Quasi Monte Carlo
444
+
438
445
  plot : bool, default=False
439
446
  If `True`, samples histogram is plotted.
440
447
 
@@ -490,9 +497,17 @@ class Wigner(object):
490
497
  inv_cdf = interp1d(cdf, xs, bounds_error=False,
491
498
  fill_value=(x_min, x_max))
492
499
 
493
- # Sample and map
494
- u = numpy.random.rand(size)
495
- samples = inv_cdf(u)
500
+ # Draw from uniform distribution
501
+ if method == 'mc':
502
+ u = numpy.random.rand(size)
503
+ elif method == 'qmc':
504
+ engine = qmc.Halton(d=1)
505
+ u = engine.random(size)
506
+ else:
507
+ raise ValueError('"method" is invalid.')
508
+
509
+ # Draw from distribution by mapping from inverse CDF
510
+ samples = inv_cdf(u).ravel()
496
511
 
497
512
  if plot:
498
513
  radius = 0.5 * (self.lam_p - self.lam_m)
@@ -631,7 +631,8 @@ class FreeForm(object):
631
631
  # stieltjes
632
632
  # =========
633
633
 
634
- def stieltjes(self, x, y, plot=False, latex=False, save=False):
634
+ def stieltjes(self, x, y, plot=False, latex=False, char_curves=None,
635
+ save=False):
635
636
  """
636
637
  Compute Stieltjes transform of the spectral density over a 2D Cartesian
637
638
  grid on the complex plane.
@@ -655,6 +656,10 @@ class FreeForm(object):
655
656
  If `True`, the plot is rendered using LaTeX. This option is
656
657
  relevant only if ``plot=True``.
657
658
 
659
+ char_curves : numpy.array, default=None
660
+ If ``plot=True``, also plot characteristic curves starting from
661
+ these locations.
662
+
658
663
  save : bool, default=False
659
664
  If not `False`, the plot is saved. If a string is given, it is
660
665
  assumed to the save filename (with the file extension). This option
@@ -742,7 +747,9 @@ class FreeForm(object):
742
747
  m2[mask_m, :] = -m1[mask_m, :] + self._glue(z[mask_m, :])
743
748
 
744
749
  if plot:
745
- plot_stieltjes(x, y, m1, m2, self.support, latex=latex, save=save)
750
+ plot_stieltjes(x, y, m1, m2, self.support, latex=latex,
751
+ char_curves={'matrix': self, 'z': char_curves},
752
+ save=save)
746
753
 
747
754
  return m1, m2
748
755
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freealg
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Free probability for large matrices
5
5
  Keywords: leaderboard bot chat
6
6
  Platform: Linux
@@ -1 +0,0 @@
1
- __version__ = "0.1.4"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes