freealg 0.7.17__py3-none-any.whl → 0.7.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. freealg/__init__.py +8 -6
  2. freealg/__version__.py +1 -1
  3. freealg/_algebraic_form/_branch_points.py +18 -18
  4. freealg/_algebraic_form/_continuation_algebraic.py +13 -13
  5. freealg/_algebraic_form/_cusp.py +15 -15
  6. freealg/_algebraic_form/_cusp_wrap.py +6 -6
  7. freealg/_algebraic_form/_decompress.py +16 -16
  8. freealg/_algebraic_form/_decompress4.py +31 -31
  9. freealg/_algebraic_form/_decompress5.py +23 -23
  10. freealg/_algebraic_form/_decompress6.py +13 -13
  11. freealg/_algebraic_form/_decompress7.py +15 -15
  12. freealg/_algebraic_form/_decompress8.py +17 -17
  13. freealg/_algebraic_form/_decompress9.py +18 -18
  14. freealg/_algebraic_form/_decompress_new.py +17 -17
  15. freealg/_algebraic_form/_decompress_new_2.py +57 -57
  16. freealg/_algebraic_form/_decompress_util.py +10 -10
  17. freealg/_algebraic_form/_decompressible.py +292 -0
  18. freealg/_algebraic_form/_edge.py +10 -10
  19. freealg/_algebraic_form/_homotopy4.py +9 -9
  20. freealg/_algebraic_form/_homotopy5.py +9 -9
  21. freealg/_algebraic_form/_support.py +19 -19
  22. freealg/_algebraic_form/algebraic_form.py +262 -468
  23. freealg/_base_form.py +401 -0
  24. freealg/_free_form/__init__.py +1 -4
  25. freealg/_free_form/_density_util.py +1 -1
  26. freealg/_free_form/_plot_util.py +3 -511
  27. freealg/_free_form/free_form.py +8 -367
  28. freealg/_util.py +59 -11
  29. freealg/distributions/__init__.py +2 -1
  30. freealg/distributions/_base_distribution.py +163 -0
  31. freealg/distributions/_chiral_block.py +137 -11
  32. freealg/distributions/_compound_poisson.py +141 -47
  33. freealg/distributions/_deformed_marchenko_pastur.py +138 -33
  34. freealg/distributions/_deformed_wigner.py +98 -9
  35. freealg/distributions/_fuss_catalan.py +269 -0
  36. freealg/distributions/_kesten_mckay.py +4 -130
  37. freealg/distributions/_marchenko_pastur.py +8 -196
  38. freealg/distributions/_meixner.py +4 -130
  39. freealg/distributions/_wachter.py +4 -130
  40. freealg/distributions/_wigner.py +10 -127
  41. freealg/visualization/__init__.py +2 -2
  42. freealg/visualization/{_rgb_hsv.py → _domain_coloring.py} +37 -29
  43. freealg/visualization/_plot_util.py +513 -0
  44. {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/METADATA +1 -1
  45. freealg-0.7.18.dist-info/RECORD +74 -0
  46. freealg-0.7.17.dist-info/RECORD +0 -69
  47. /freealg/{_free_form/_sample.py → _sample.py} +0 -0
  48. /freealg/{_free_form/_support.py → _support.py} +0 -0
  49. {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/WHEEL +0 -0
  50. {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/licenses/AUTHORS.txt +0 -0
  51. {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/licenses/LICENSE.txt +0 -0
  52. {freealg-0.7.17.dist-info → freealg-0.7.18.dist-info}/top_level.txt +0 -0
@@ -7,14 +7,16 @@
7
7
  # directory of this source tree.
8
8
 
9
9
 
10
- # ======
11
- # Import
12
- # ======
10
+ # =======
11
+ # Imports
12
+ # =======
13
13
 
14
14
  import numpy
15
15
  import collections
16
+ from ..visualization._plot_util import plot_density
16
17
  from .._geometric_form._elliptic_functions import ellipj
17
18
  from .._geometric_form._continuation_genus1 import mobius_z
19
+ from ._base_distribution import BaseDistribution
18
20
 
19
21
  __all__ = ['ChiralBlock']
20
22
 
@@ -23,7 +25,7 @@ __all__ = ['ChiralBlock']
23
25
  # Chiral Block
24
26
  # ============
25
27
 
26
- class ChiralBlock(object):
28
+ class ChiralBlock(BaseDistribution):
27
29
  """
28
30
  Twisted chiral block model.
29
31
 
@@ -31,7 +33,55 @@ class ChiralBlock(object):
31
33
  ----------
32
34
 
33
35
  alpha : float
36
+ The parameter :math:`\\alpha`. See notes below.
37
+
34
38
  beta : float
39
+ The parameter :math:`\\beta`. See notes below.
40
+
41
+ c : float
42
+ Ratio parameter :math:`c = p / n`. See notes below. It must be > 0.
43
+
44
+ Methods
45
+ -------
46
+
47
+ density
48
+ Spectral density of distribution.
49
+
50
+ roots
51
+ Roots of polynomial implicitly representing Stieltjes transform
52
+
53
+ stieltjes
54
+ Stieltjes transform
55
+
56
+ support
57
+ Support intervals of distribution
58
+
59
+ sample
60
+ Sample from distribution.
61
+
62
+ matrix
63
+ Generate matrix with its empirical spectral density of distribution
64
+
65
+ poly
66
+ Polynomial coefficients implicitly representing the Stieltjes
67
+
68
+ Notes
69
+ -----
70
+
71
+ The chiral block model corresponds to the bipartite biregular graph with
72
+ the matrix
73
+
74
+ .. math::
75
+
76
+ \\mathbf{A} =
77
+ \\begin{bmatrix}
78
+ \\alpha \\mathbf{I} & \\mathbf{X} \\\\
79
+ \\mathbf{A}^{\\intercal} & \\beta \\mathbf{I}
80
+
81
+ \\end{bmatrix},
82
+
83
+ where :math:`\\mathbf{X} \\in \\mathbb{R}^{p \\times n}`, and
84
+ :math:`c = p / n \\in (0, \\infty)`.
35
85
  """
36
86
 
37
87
  # ====
@@ -47,15 +97,74 @@ class ChiralBlock(object):
47
97
  self.beta = beta
48
98
  self.c = c
49
99
 
100
+ # Bounds for smallest and largest eigenvalues
101
+ center = 0.5 * (alpha + beta)
102
+ dim = 0.5 * numpy.abs(beta - alpha)
103
+ skew = 1.0 + numpy.sqrt(c)
104
+
105
+ self.lam_lb = center - numpy.sqrt(dim**2 + skew**2)
106
+ self.lam_ub = center + numpy.sqrt(dim**2 + skew**2)
107
+
50
108
  # =======
51
109
  # density
52
110
  # =======
53
111
 
54
- def density(self, x):
112
+ def density(self, x=None, eta=1e-3, ac_only=True, plot=False, latex=False,
113
+ save=False, eig=None):
55
114
  """
56
- Absolutely continous density, and the atom.
115
+ Density of distribution.
116
+
117
+ Parameters
118
+ ----------
119
+
120
+ x : numpy.array, default=None
121
+ The locations where density is evaluated at. If `None`, an interval
122
+ slightly larger than the supp interval of the spectral density
123
+ is used.
124
+
125
+ rho : numpy.array, default=None
126
+ Density. If `None`, it will be computed.
127
+
128
+ eta : float, default=1e-3
129
+ The offset :math:`\\eta` to remove atom.
130
+
131
+ ac_only : bool, default=True
132
+ If `True`, it returns the absolutely-continuous part of density.
133
+
134
+ plot : bool, default=False
135
+ If `True`, density is plotted.
136
+
137
+ latex : bool, default=False
138
+ If `True`, the plot is rendered using LaTeX. This option is
139
+ relevant only if ``plot=True``.
140
+
141
+ save : bool, default=False
142
+ If not `False`, the plot is saved. If a string is given, it is
143
+ assumed to the save filename (with the file extension). This option
144
+ is relevant only if ``plot=True``.
145
+
146
+ eig : numpy.array, default=None
147
+ A collection of eigenvalues to compare to via histogram. This
148
+ option is relevant only if ``plot=True``.
149
+
150
+ Returns
151
+ -------
152
+
153
+ rho : numpy.array
154
+ Density.
57
155
  """
58
156
 
157
+ # Create x if not given
158
+ if x is None:
159
+ radius = 0.5 * (self.lam_ub - self.lam_lb)
160
+ center = 0.5 * (self.lam_ub + self.lam_lb)
161
+ scale = 1.25
162
+ x_min = numpy.floor(center - radius * scale)
163
+ x_max = numpy.ceil(center + radius * scale)
164
+ x = numpy.linspace(x_min, x_max, 500)
165
+ else:
166
+ x = numpy.asarray(x, dtype=numpy.float64)
167
+
59
168
  # Parameters
60
169
  alpha = self.alpha
61
170
  beta = self.beta
@@ -86,18 +195,35 @@ class ChiralBlock(object):
86
195
  rho = numpy.where(numpy.isfinite(rho), rho, 0.0)
87
196
  rho = numpy.maximum(rho, 0.0)
88
197
 
89
- # Atom location and weight
198
+ # Atoms
199
+ atoms = None
90
200
  if numpy.abs(c - 1.0) < 1e-4:
91
- atom_loc = None
92
- atom_w = None
201
+ atoms = None
93
202
  elif c > 1.0:
94
203
  atom_loc = alpha
95
204
  atom_w = (c - 1.0) / (c + 1.0)
205
+ atoms = [(atom_loc, atom_w)]
96
206
  elif c < 1.0:
97
207
  atom_loc = beta
98
208
  atom_w = (1.0 - c) / (c + 1.0)
209
+ atoms = [(atom_loc, atom_w)]
210
+
211
+ # Optional: remove the atom at zero (only for visualization of AC part)
212
+ if (atoms is not None) and (ac_only is True):
213
+ atom = atom_w * (float(eta) / numpy.pi) / \
214
+ (x * x + float(eta) * float(eta))
215
+ rho = rho - atom
216
+ rho = numpy.maximum(rho, 0.0)
217
+
218
+ if plot:
219
+ if eig is not None:
220
+ label = 'Theoretical'
221
+ else:
222
+ label = ''
223
+ plot_density(x, rho, atoms=atoms, label=label, latex=latex,
224
+ save=save, eig=eig)
99
225
 
100
- return rho, atom_loc, atom_w
226
+ return rho, atoms
101
227
 
102
228
  # ===========
103
229
  # sqrt like t
@@ -118,7 +244,7 @@ class ChiralBlock(object):
118
244
 
119
245
  def stieltjes(self, z, alt_branch=False):
120
246
  """
121
- Physical Stieltjes transform
247
+ Stieltjes transform
122
248
  """
123
249
 
124
250
  # Parameters
@@ -12,7 +12,9 @@
12
12
  # =======
13
13
 
14
14
  import numpy
15
+ from ..visualization._plot_util import plot_density
15
16
  from .._algebraic_form._sheets_util import _pick_physical_root_scalar
17
+ from ._base_distribution import BaseDistribution
16
18
 
17
19
  __all__ = ['CompoundPoisson']
18
20
 
@@ -21,9 +23,45 @@ __all__ = ['CompoundPoisson']
21
23
  # Compound Poisson
22
24
  # ================
23
25
 
24
- class CompoundPoisson(object):
26
+ class CompoundPoisson(BaseDistribution):
25
27
  """
26
- Two-atom free compound Poisson model (MP-like, FD-closed).
28
+ Compound free Poisson distribution
29
+
30
+ Parameters
31
+ ----------
32
+
33
+ t1, t2 : float
34
+ Jump sizes (must be > 0). For PSD-like support, keep them > 0.
35
+
36
+ w1 : float
37
+ Mixture weight in (0, 1) for ``t1``. Second weight is ``1-w1``.
38
+
39
+ lam : float
40
+ Total rate (intensity), must be > 0.
41
+
42
+ Methods
43
+ -------
44
+
45
+ density
46
+ Spectral density of distribution.
47
+
48
+ roots
49
+ Roots of polynomial implicitly representing Stieltjes transform
50
+
51
+ stieltjes
52
+ Stieltjes transform
53
+
54
+ support
55
+ Support intervals of distribution
56
+
57
+ sample
58
+ Sample from distribution.
59
+
60
+ matrix
61
+ Generate matrix with its empirical spectral density of distribution
62
+
63
+ poly
64
+ Polynomial coefficients implicitly representing the Stieltjes
27
65
 
28
66
  Notes
29
67
  -----
@@ -68,16 +106,7 @@ class CompoundPoisson(object):
68
106
 
69
107
  def __init__(self, t1, t2, w1, lam):
70
108
  """
71
- Parameters
72
- ----------
73
- t1, t2 : float
74
- Jump sizes (must be > 0). For PSD-like support, keep them > 0.
75
-
76
- w1 : float
77
- Mixture weight in (0, 1) for t1. Second weight is 1-w1.
78
-
79
- lam : float
80
- Total rate (intensity), must be > 0.
109
+ Initialization.
81
110
  """
82
111
 
83
112
  t1 = float(t1)
@@ -99,6 +128,14 @@ class CompoundPoisson(object):
99
128
  self.w1 = w1
100
129
  self.lam = lam
101
130
 
131
+ # Bounds for smallest and largest eigenvalues
132
+ if lam < 1.0:
133
+ # In this case, there is an atom at the origin
134
+ self.lam_lb = 0.0
135
+ else:
136
+ self.lam_lb = numpy.min([t1, t2]) * (1 - numpy.sqrt(lam))**2
137
+ self.lam_ub = numpy.max([t1, t2]) * (1 + numpy.sqrt(lam))**2
138
+
102
139
  # ====================
103
140
  # roots cubic m scalar
104
141
  # ====================
@@ -183,6 +220,8 @@ class CompoundPoisson(object):
183
220
 
184
221
  def stieltjes(self, z, max_iter=100, tol=1e-12):
185
222
  """
223
+ Stieltjes transform
224
+
186
225
  Physical/Herglotz branch of m(z) for the two-atom compound Poisson law.
187
226
  Fast masked Newton in m, keeping z's original shape.
188
227
  """
@@ -258,32 +297,100 @@ class CompoundPoisson(object):
258
297
  # density
259
298
  # =======
260
299
 
261
- def density(self, x, eta=2e-4, max_iter=100, tol=1e-12, ac_only=True):
300
+ def density(self, x=None, eta=2e-4, max_iter=100, tol=1e-12, ac_only=True,
301
+ plot=False, latex=False, save=False, eig=None):
262
302
  """
263
- Density rho(x) from Im m(x + i eta) / pi.
264
-
265
- ac_only: bool, default=True
266
- If `True`, computes the absolutely-continuous (AC) part. This
267
- matters when for :math:`\\lambda < 1`, the distribution has an
268
- atom at zero of mass :math:`1 - \\lambda`, and for plotting, this
269
- often shows as a spike. Setting this option to true prevents this
270
- issue in plotting. Also, to compute support, this option allows
271
- proper detection of the support intervals.
303
+ Density of distribution.
304
+
305
+ Parameters
306
+ ----------
307
+
308
+ x : numpy.array, default=None
309
+ The locations where density is evaluated at. If `None`, an interval
310
+ slightly larger than the supp interval of the spectral density
311
+ is used.
312
+
313
+ rho : numpy.array, default=None
314
+ Density. If `None`, it will be computed.
315
+
316
+ eta : float, default=2e-4
317
+ The offset :math:`\\eta` from the real axis where the density
318
+ is evaluated using Plemelj formula at :math:`z = x + i \\eta`.
319
+
320
+ max_iter : int, default=100
321
+ Maximum number of Newton iterations to solve for the Stieltjes
322
+ root.
323
+
324
+ tol : float, default=1e-12
325
+ Tolerance for Newton iterations to solve for the Stieltjes root.
326
+
327
+ ac_only : bool, default=True
328
+ If `True`, it returns the absolutely-continuous part of density.
329
+
330
+ plot : bool, default=False
331
+ If `True`, density is plotted.
332
+
333
+ latex : bool, default=False
334
+ If `True`, the plot is rendered using LaTeX. This option is
335
+ relevant only if ``plot=True``.
336
+
337
+ save : bool, default=False
338
+ If not `False`, the plot is saved. If a string is given, it is
339
+ assumed to the save filename (with the file extension). This option
340
+ is relevant only if ``plot=True``.
341
+
342
+ eig : numpy.array, default=None
343
+ A collection of eigenvalues to compare to via histogram. This
344
+ option is relevant only if ``plot=True``.
345
+
346
+ Returns
347
+ -------
348
+
349
+ rho : numpy.array
350
+ Density.
351
+
272
352
  """
273
353
 
354
+ # Create x if not given
355
+ if x is None:
356
+ radius = 0.5 * (self.lam_ub - self.lam_lb)
357
+ center = 0.5 * (self.lam_ub + self.lam_lb)
358
+ scale = 1.25
359
+ x_min = numpy.floor(center - radius * scale)
360
+ x_max = numpy.ceil(center + radius * scale)
361
+ x = numpy.linspace(x_min, x_max, 500)
362
+ else:
363
+ x = numpy.asarray(x, dtype=numpy.float64)
364
+
365
+ z = x + 1j * float(eta)
366
+ m = self.stieltjes(z)
367
+
274
368
  z = numpy.asarray(x, dtype=numpy.float64) + 1j * float(eta)
275
369
  m = self.stieltjes(z, max_iter=max_iter, tol=tol)
276
370
  rho = numpy.imag(m) / numpy.pi
277
371
 
278
- # If it has an atom, subtract from density to get the absolutely
279
- # continuous part before processing the support
280
- if (ac_only is True) and (self.lam < 1.0):
372
+ # Atoms
373
+ atoms = None
374
+ if self.lam < 1.0:
375
+ atom_loc = 0.0
376
+ atom_w = 1.0 - self.lam
377
+ atoms = [(atom_loc, atom_w)]
378
+
379
+ # Optional: remove the atom at zero (only for visualization of AC part)
380
+ if (atoms is not None) and (ac_only is True):
281
381
  zr = z.real
282
- atom = (1.0 - self.lam) * \
283
- (float(eta) / (numpy.pi * (zr*zr + float(eta)**2)))
382
+ atom = atom_w * (float(eta) / (numpy.pi * (zr*zr + float(eta)**2)))
284
383
  rho = rho - atom
285
384
  rho = numpy.maximum(rho, 0.0)
286
385
 
386
+ if plot:
387
+ if eig is not None:
388
+ label = 'Theoretical'
389
+ else:
390
+ label = ''
391
+ plot_density(x, rho, atoms=atoms, label=label, latex=latex,
392
+ save=save, eig=eig)
393
+
287
394
  return rho
288
395
 
289
396
  # =====
@@ -292,7 +399,7 @@ class CompoundPoisson(object):
292
399
 
293
400
  def roots(self, z):
294
401
  """
295
- Return all algebraic branches (roots of the cubic) at scalar z.
402
+ Roots of polynomial implicitly representing Stieltjes transform
296
403
  """
297
404
 
298
405
  z = numpy.asarray(z, dtype=numpy.complex128)
@@ -307,10 +414,11 @@ class CompoundPoisson(object):
307
414
  def support(self, eta=2e-4, n_probe=4000, thr=5e-4, x_max=None,
308
415
  x_pad=0.05, method='probe'):
309
416
  """
310
- Estimate support intervals by probing rho(x) on a grid.
417
+ Support intervals of distribution
311
418
 
312
419
  Parameters
313
420
  ----------
421
+
314
422
  method : {'probe'}
315
423
  Only probing is implemented here.
316
424
  """
@@ -350,27 +458,13 @@ class CompoundPoisson(object):
350
458
 
351
459
  return intervals
352
460
 
353
- # ==========
354
- # rho scalar
355
- # ==========
356
-
357
- def rho_scalar(self, x, eta=2e-4, max_iter=100, tol=1e-12):
358
- """
359
- Scalar density helper (returns float).
360
- """
361
-
362
- x = float(x)
363
- z = x + 1j * float(eta)
364
- m = self.stieltjes(z, max_iter=max_iter, tol=tol)
365
- return float(numpy.imag(m) / numpy.pi)
366
-
367
461
  # ======
368
462
  # matrix
369
463
  # ======
370
464
 
371
465
  def matrix(self, size, seed=None):
372
466
  """
373
- Generate a symmetric random matrix whose ESD approximates this law.
467
+ Generate matrix with the spectral density of the distribution.
374
468
 
375
469
  Parameters
376
470
  ----------
@@ -417,6 +511,7 @@ class CompoundPoisson(object):
417
511
  if n <= 0:
418
512
  raise ValueError("size must be a positive integer.")
419
513
 
514
+ # Unpack parameters
420
515
  t1 = float(self.t1)
421
516
  t2 = float(self.t2)
422
517
  w1 = float(self.w1)
@@ -452,10 +547,9 @@ class CompoundPoisson(object):
452
547
 
453
548
  def poly(self):
454
549
  """
455
- Return a_coeffs for the exact cubic P(z,m)=0 of the two-atom free
456
- compound Poisson model.
550
+ Polynomial coefficients implicitly representing the Stieltjes
457
551
 
458
- a_coeffs[i, j] is the coefficient of z^i m^j.
552
+ coeffs[i, j] is the coefficient of z^i m^j.
459
553
  Shape is (2, 4) since deg_z=1 and deg_m=3.
460
554
 
461
555
  Coefficients match _roots_cubic_m_scalar.