Bosons

Bosonic modes are symbolic like fermions, but need a finite local dimension for numerics, which number conservation keeps small. We use this to study the Bose-Hubbard model, where onsite repulsion suppresses number fluctuations, and a dipole-conserving model, where a second weighted NumberConservation constrains the dynamics and shapes the entanglement.

Bosonic operators and spaces

@boson a declares one mode and @bosons b a field of modes b[i]; the adjoint ' is the creation operator. Expressions are simplified using $[a, a^\dagger] = 1$, and modes on different sites commute. A Hilbert space needs a local dimension $d$, keeping the occupations $0, \dots, d-1$ of each mode: hilbert_space(a, d) for one mode and hilbert_space(b, labels, d) for a field, optionally followed by a constraint.

using FermionicHilbertSpaces@boson a@bosons ba * a' - a' * a == 1b[1]' * b[2] == b[2] * b[1]'(a' + a)^2 - a^2 - a'^2
1I + 2*a†*a

Bose-Hubbard model

The Bose-Hubbard Hamiltonian has hopping $t$ and onsite repulsion $U$,

\[H = -t \sum_{i=1}^{N-1} (b_i^\dagger b_{i+1} + \mathrm{h.c.}) + \frac{U}{2} \sum_{i=1}^N n_i(n_i - 1), \qquad n_i = b_i^\dagger b_i.\]

and conserves particle number, which reduces the Hilbert space dimension. A local dimension of one more than the particle number gives no truncation error.

N = 8M = 8local_dim = M + 1Hfull = hilbert_space(b, 1:N, local_dim)H = hilbert_space(b, 1:N, local_dim, NumberConservation(M))dim(Hfull), dim(H)
(43046721, 6435)

We build the matrices once and reuse them in a sweep over $U$.

n = [b[i]' * b[i] for i in 1:N]hop_sym = sum(b[i]' * b[i+1] + hc for i in 1:(N-1))int_sym = sum(n[i] * (n[i] - 1) for i in 1:N)K = representation(hop_sym, H)D = representation(int_sym, H)n_ops = [representation(n[i], H) for i in 1:N]n2_ops = [representation(n[i]^2, H) for i in 1:N]hop_ops = [representation(b[i]' * b[i+1], H) for i in 1:(N-1)]using LinearAlgebra, Arpackfunction ground_state_analysis(U; t=1.0)    M = -t * K + (U / 2) * D    _, vecs = eigs(M; nev=1, which=:SR)    psi = vecs[:, 1]    density = [dot(psi, op, psi) for op in n_ops]    variance = sum(dot(psi, n2, psi) - d^2 for (n2, d) in zip(n2_ops, density)) / N    hopping = sum(dot(psi, op, psi) for op in hop_ops) / (N - 1)    return (; density, variance, hopping)end
ground_state_analysis (generic function with 1 method)

Onsite repulsion suppresses particle fluctuations and hopping (left) and flattens the density profile (right).

Us = range(0, 16; length=20)sweep = [ground_state_analysis(U) for U in Us];using Plotsp1 = plot(Us, [res.variance for res in sweep]; label="Number variance ⟨Δn²⟩", xlabel="U/t", ylabel="Observable", lw=2)plot!(p1, Us, [res.hopping for res in sweep]; label="Hopping amplitude ⟨bᵢ†bᵢ₊₁⟩", lw=2)p2 = plot(1:N, ground_state_analysis(0).density; label="U/t = 0", m=:circle, lw=2,    xlabel="Site i", ylabel="⟨nᵢ⟩", xticks=1:N, title="Density profiles")plot!(p2, 1:N, ground_state_analysis(6).density; label="U/t = 6", m=:square, lw=2)plot(p1, p2; layout=(1, 2), size=(600, 300), frame=:box)
Example block output

Dipole-conserving model

A second conserved quantity is handled by a weighted NumberConservation, and constraints combine with *. The model

\[ H = -J \sum_i (b_{i-1}^\dagger b_{i+1}^\dagger b_i b_i + h.c.) + U/2 \sum_i n_i(n_i-1)\]

conserves the particle number $N = \sum n_i$ and the dipole moment $D = \sum i n_i$.

@bosons bfunction hamiltonian(b, N; J, U)    -J * sum(b[i-1]' * b[i+1]' * b[i] * b[i] + hc for i in 2:(N-1)) +        U / 2 * sum(b[i]'b[i] * (b[i]'b[i] - 1) for i in 1:N)endN = 6M = 6local_dim = M + 1   # Large enough to avoid truncation errorDtot = sum(1:N)     # dipole of the uniform statenumber_cons = NumberConservation(M)dipole_cons = NumberConservation(Dtot; weights=1:N)Hfull = hilbert_space(b, 1:N, local_dim)HN = hilbert_space(b, 1:N, local_dim, number_cons)                # only N fixedHND = hilbert_space(b, 1:N, local_dim, number_cons * dipole_cons) # N and D fixeddim(Hfull), dim(HN), dim(HND)
(117649, 462, 32)

The half-system entanglement entropy of the ground state follows from a partial trace map onto the first half of the chain.

ptmap = partial_trace(HND => subregion([b[i] for i in 1:div(N, 2)], HND))Us = range(0, 5, 50)entropies = map(Us) do U    H = hamiltonian(b, N; J=1.0, U)    Ψ0 = eigvecs(representation(H, HND, :dense))[:, 1]    sum(-λ * log(λ) for λ in eigvals(ptmap(Ψ0)) if λ > 1e-12)endplot(Us, entropies; xlabel="U/J", ylabel="Half-system entropy", legend=false, ylims=(0, 2.1), frame=:box, size=(400, 250), lw=2)
Example block output

This page was generated using Literate.jl.