Skip to content

API — random ensembles

random_semicircle(n, field='real', variance=1.0, seed=None)

Generate an \(n\times n\) Hermitian (self-adjoint) Wigner matrix whose eigenvalues follow (as \(n\to\infty\)) a semicircle law with variance parameter \(c=\texttt{variance}\), i.e. support \([-2\sqrt{c},\,2\sqrt{c}]\).

Conventions (entry variances): - \(\texttt{field}=\text{"real"}\) (GOE normalization): off-diagonal \(\operatorname{Var}(H_{ij}) = c/n\), diagonal \(\operatorname{Var}(H_{ii}) = 2c/n\). - \(\texttt{field}=\text{"complex"}\) (GUE normalization): off-diagonal \(\mathbb E|H_{ij}|^2 = c/n\), diagonal \(\operatorname{Var}(H_{ii}) = c/n\).

Parameters:

Name Type Description Default
n int

Matrix size.

required
field (real, complex)

Real symmetric (GOE-type) or complex Hermitian (GUE-type).

"real","complex"
variance float

Semicircle variance parameter \(c>0\). The spectrum concentrates on \([-2\sqrt{c},\,2\sqrt{c}]\) in the large-\(n\) limit.

1.0
seed int or Generator

Random seed or generator.

None

Returns:

Name Type Description
H ndarray

Hermitian matrix (dtype float64 for real, complex128 for complex).

Notes

Implementation uses the convenient symmetrize-and-scale recipe:

  • Real: \(H = \dfrac{X + X^\top}{\sqrt{2n}}\), \(X_{ij}\sim\mathcal N(0,1)\). This yields \(\operatorname{Var}(H_{ij})=1/n\) (off-diagonal), \(\operatorname{Var}(H_{ii})=2/n\).
  • Complex: draw \(Z_{ij}\sim \mathcal{CN}(0,1)\) (i.e. \((X+iY)/\sqrt{2}\) with \(X,Y\sim\mathcal N(0,1)\)), then \(H = \dfrac{Z + Z^\ast}{\sqrt{2n}}\), giving \(\mathbb E|H_{ij}|^2=1/n\) and \(\operatorname{Var}(H_{ii})=1/n\). Finally multiply by \(\sqrt{c}\).
Source code in src/free_matrix_laws/ensembles.py
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def random_semicircle(n: int,
                      field: str = "real",
                      variance: float = 1.0,
                      seed=None):
    r'''
    Generate an $n\times n$ Hermitian (self-adjoint) Wigner matrix whose eigenvalues
    follow (as $n\to\infty$) a semicircle law with variance parameter $c=\texttt{variance}$,
    i.e. support $[-2\sqrt{c},\,2\sqrt{c}]$.

    Conventions (entry variances):
    - $\texttt{field}=\text{"real"}$ (GOE normalization):
      off-diagonal $\operatorname{Var}(H_{ij}) = c/n$, diagonal $\operatorname{Var}(H_{ii}) = 2c/n$.
    - $\texttt{field}=\text{"complex"}$ (GUE normalization):
      off-diagonal $\mathbb E|H_{ij}|^2 = c/n$, diagonal $\operatorname{Var}(H_{ii}) = c/n$.

    Parameters
    ----------
    n : int
        Matrix size.
    field : {"real","complex"}, default "real"
        Real symmetric (GOE-type) or complex Hermitian (GUE-type).
    variance : float, default 1.0
        Semicircle variance parameter $c>0$. The spectrum concentrates on
        $[-2\sqrt{c},\,2\sqrt{c}]$ in the large-$n$ limit.
    seed : int or numpy.random.Generator, optional
        Random seed or generator.

    Returns
    -------
    H : ndarray
        Hermitian matrix (dtype float64 for real, complex128 for complex).

    Notes
    -----
    Implementation uses the convenient symmetrize-and-scale recipe:

    - Real: $H = \dfrac{X + X^\top}{\sqrt{2n}}$, $X_{ij}\sim\mathcal N(0,1)$.
      This yields $\operatorname{Var}(H_{ij})=1/n$ (off-diagonal), $\operatorname{Var}(H_{ii})=2/n$.
    - Complex: draw $Z_{ij}\sim \mathcal{CN}(0,1)$ (i.e. $(X+iY)/\sqrt{2}$ with $X,Y\sim\mathcal N(0,1)$),
      then $H = \dfrac{Z + Z^\ast}{\sqrt{2n}}$, giving $\mathbb E|H_{ij}|^2=1/n$ and
      $\operatorname{Var}(H_{ii})=1/n$. Finally multiply by $\sqrt{c}$.
    '''
    if variance <= 0:
        raise ValueError("variance must be > 0")
    rng = np.random.default_rng(seed)

    if field == "real":
        X = rng.standard_normal((n, n))
        H = (X + X.T) / np.sqrt(2.0 * n)          # GOE normalization
        if variance != 1.0:
            H = np.sqrt(variance) * H
        return H

    if field == "complex":
        X = rng.standard_normal((n, n))
        Y = rng.standard_normal((n, n))
        Z = (X + 1j * Y) / np.sqrt(2.0)           # CN(0,1) entries
        H = (Z + Z.conj().T) / np.sqrt(2.0 * n)   # GUE normalization
        if variance != 1.0:
            H = np.sqrt(variance) * H
        return H

    raise ValueError('field must be "real" or "complex"')