How to: density for a polynomial in semicircular variables (via linearization)
This page documents the helper iteration step hfsc_map and the convenience wrapper
polynomial_semicircle_density (short aliases are polynomial_density and get_density_C), used to approximate the
density of a self-adjoint polynomial in free semicircular variables after you have
built a self-adjoint linearization.
Background (what these functions compute)
Let \(p\) be a self-adjoint polynomial in semicircular variables \(X_1,\dots,X_s\). Assume you already have a self-adjoint linearization \(L_p\) of \(p\) in the form
where \(a_0,a_1,\dots,a_s\) are fixed complex matrices (of the same size).
To recover the scalar Cauchy transform of \(p\) from the linearization, one considers the regularized block-diagonal matrix
and the quasi-resolvent
The scalar Cauchy transform of \(p\) is then obtained from the \((1,1)\) entry/block via the limit \(\varepsilon\downarrow 0\); numerically, we use a small \(\varepsilon\) and approximate
Finally, the density at a real point \(x\) is obtained by Stieltjes inversion: for \(z=x+i\varepsilon\),
What you need to provide
These functions assume you already know:
- The linearization bias matrix \(a_0\) (called
abelow). - The list/stack of Kraus matrices
AA = (A_1,\dots,A_s)that define the covariance map $$ \eta(B) = \sum_{i=1}^s A_i\,B\,A_i^\ast. $$
In typical linearizations, the \(A_i\) are built from the coefficient matrices \(a_j\) of the linearization, but this construction is outside the scope of this how-to.
API
_hfsc_map(G, z, a0, A)
This is a single fixed-point step used inside an iteration scheme.
Conceptually, it implements a half-averaged update
where \(W(G)\) is the “raw” update derived from the linearized equation and the map \(\eta\).
polynomial_semicircle_density(x, a0, A, eps=1e-2, ...)
This computes the density at a real point \(x\) by: 1) solving for \(G(z)\) at \(z=x+i\,\varepsilon\) by iteration, and 2) returning \(-(1/\pi)\Im(G(z)_{11})\).
Example: anticommutator \(X_1X_2 + X_2X_1\) via a \(3\times 3\) linearization
A standard self-adjoint linearization for the anticommutator is the matrix
with
Once you have built the corresponding Kraus family A for \(\eta\), you can evaluate the
density numerically:
import numpy as np
from free_matrix_laws.transforms import polynomial_semicircle_density
# linearization bias a0:
a0 = np.array([
[0, 0, 0],
[0, 0,-1],
[0,-1, 0],
], dtype=complex)
# A = (A1,...,As) should be prepared for your problem.
# Here we assume you already have it:
A = ... # list of (n,n) arrays or stacked array (s,n,n)
x = 0.0
fx = polynomial_semicircle_density(x, a0, A, eps=1e-2, maxiter=10_000)
print("f(x) ≈", fx)
Tips
- Use \(z=x+i\varepsilon\) with \(\varepsilon\approx 10^{-2}\)–\(10^{-3}\).
- If the iteration stalls, increase \(\varepsilon\), relax
tol, or reducemaxiter. - Warm-starting (re-using the solution for a nearby \(z\)) can speed up convergence a lot.
- The returned density is a numerical approximation; decreasing \(\varepsilon\) sharpens it but may require tighter tolerances.
What is actually returned
polynomial_semicircle_density(x, ...) returns
$$
f(x)\;\approx\;-\frac{1}{\pi}\,\operatorname{Im}\,G(x+i\varepsilon)_{11}.
$$
In many linearization setups, the \((1,1)\) block corresponds exactly to the scalar resolvent of the polynomial. If your linearization uses a larger \((1,1)\) block, replace \(G_{11}\) by the normalized trace of that block.