Affine decompositions with multi-indices and additional parameters

In this example, we want to explore the capabilities of the central AffineDecomposition type. To advance the previous examples where we covered the magnetization — a very simple observable that consists of only one affine term and a parameter-independent coefficient — we now turn to observables where the indices are multi-indices $r = (r_1, \dots, r_d)$ and the coefficients can depend on additional parameters $p$, aside from the $\bm{\mu}$ parameter points that are present in the Hamiltonian:

\[O(\bm{\mu}, p) = \sum_{q=1}^Q \alpha_q(\bm{\mu}, p)\, O_q\]

To stay within the realm of quantum magnetism, we will consider the so-called spin structure factor

\[\mathcal{S}(k) = \frac{1}{L} \sum_{r,r'=1}^L e^{-i (r - r') k} S^z_r S^z_{r'},\quad \alpha_{r,r'}(k) = \frac{e^{-i (r - r') k}}{L}, \quad O_{r,r'} = S^z_r S^z_{r'}\]

with a wave vector parameter $k$, to discuss the implementation of a more complicated observable.

To provide a physical setup, we again use the XXZ chain from The reduced basis workflow:

using LinearAlgebra
using SparseArrays
using ReducedBasis

σx = sparse([0.0 1.0; 1.0 0.0])
σy = sparse([0.0 -im; im 0.0])
σz = sparse([1.0 0.0; 0.0 -1.0])

function to_global(op::M, L::Int, i::Int) where {M<:AbstractMatrix}
    d = size(op, 1)
    if i == 1
        kron(op, M(I, d^(L - 1), d^(L - 1)))
    elseif i == L
        kron(M(I, d^(L - 1), d^(L - 1)), op)
    else
        kron(kron(M(I, d^(i - 1), d^(i - 1)), op), M(I, d^(L - i), d^(L - i)))
    end
end

function xxz_chain(L)
    H1 = 0.25 * sum(1:L-1) do i
        to_global(σx, L, i) * to_global(σx, L, i + 1) +
        to_global(σy, L, i) * to_global(σy, L, i + 1)
    end
    H2 = 0.25 * sum(1:L-1) do i
        to_global(σz, L, i) * to_global(σz, L, i + 1)
    end
    H3 = 0.5  * sum(1:L) do i
        to_global(σz, L, i)
    end
    AffineDecomposition([H1, H2, H3], μ -> [1.0, μ[1], -μ[2]])
end;

Construct the Hamiltonian for an XXZ chain with 6 sites ...

L = 6
H = xxz_chain(L);

... and generate a RB surrogate using an exact diagonalization solver.

Δ  = range(-1.0, 2.5; length=40)
hJ = range( 0.0, 3.5; length=40)
grid_train = RegularGrid(Δ, hJ)
greedy = Greedy(; estimator=Residual())
lobpcg = LOBPCG(; tol_degeneracy=1e-4)
qrcomp = QRCompress(; tol=1e-9)
rbres = assemble(H, grid_train, greedy, lobpcg, qrcomp);
n      max. err    ‖BᵀB-I‖     time      μ
------------------------------------------------------------
1      NaN         4.74e-15     224ms    [-1.0, 0.0]
2      2.33        4.76e-15     269ms    [2.231, 0.538]
3      0.946       4.83e-15    83.8ms    [0.795, 0.897]
4      0.508       4.94e-15    94.1ms    [1.154, 1.795]
5      0.472       5.96e-15     104ms    [2.5, 2.603]
6      0.385       6.5e-15     91.8ms    [0.346, 0.0]
7      0.0727      1.12e-14     103ms    [-0.551, 0.0]
8      0.0554      1.58e-14     107ms    [-0.462, 0.269]
9      0.0163      7.3e-14      121ms    [1.423, 0.359]
10     0.00522     1.12e-13     127ms    [-0.372, 0.449]
11     0.004       3.48e-13     152ms    [1.692, 1.436]
12     0.00151     6.09e-13     159ms    [-0.821, 0.0]
13     0.00039     1.01e-12     176ms    [-0.821, 0.09]
reached residual target

Now the task is to implement the double-sum in $\mathcal{S}$, as well as the $k$-dependency in the coefficients. The double-sum can be encoded by putting all $S^z_r S^z_{r'}$ combinations into an $L \times L$ matrix:

terms = map(idx -> to_global(σz, L, idx[1]) * to_global(σz, L, idx[2]),
            Iterators.product(1:L, 1:L));

Correspondingly, the coefficient function now has to map one $k$ value to a matrix of coefficients of the same size as the terms matrix:

coefficients = k -> map(idx -> cis(-(idx[1] - idx[2]) * k) / L,
                        Iterators.product(1:L, 1:L));

One feature of the structure factor that also shows up in many other affine decompositions with double-sums is that the term indices commute, i.e. $O_{r,r'} = O_{r',r}$. In that case, only the upper triangular matrix has to be computed since $B^\dagger O_{r,r'} B = B^\dagger O_{r',r} B$ are the same in the compressed affine decomposition. So let's create the AffineDecomposition and compress, exploiting this symmetry using the symmetric_terms keyword argument:

SFspin    = AffineDecomposition(terms, coefficients)
sfspin, _ = compress(SFspin, rbres.basis; symmetric_terms=true);

In the online evaluation of the structure factor, we then need to define some wave vector values and compute the structure factor at each of them. As usual, we first define a finer online grid of points and the matching online solver:

Δ_online    = range(first(Δ), last(Δ); length=100)
hJ_online   = range(first(hJ), last(hJ); length=100)
grid_online = RegularGrid(Δ_online, hJ_online)
fulldiag    = FullDiagonalization(lobpcg);

And then we map the grid points to the corresponding structure factor values for a set of different wave vectors:

using Statistics
wavevectors = [0.0, π/4, π/2, π]
sf = [zeros(size(grid_online)) for _ in 1:length(wavevectors)]
for (idx, μ) in pairs(grid_online)
    _, φ_rb = solve(rbres.h, rbres.basis.metric, μ, fulldiag)
    for (i, k) in enumerate(wavevectors)
        sf[i][idx] = mean(u -> real(dot(u, sfspin(k), u)), eachcol(φ_rb))
    end
end

Here we again see the convenience of measuring observables in the online stage; adding more wavevector values does not significantly increase the computational cost, since it corresponds to a mere reevaluation of the coefficient functions and small vector-matrix products. Finally, let us see how the structure factor behaves for the different wave vector values:

using Plots
kwargs = (; xlabel=raw"$\Delta$", ylabel=raw"$h/J$", colorbar=true, leg=false)
hms = []
for (i, q) in enumerate(wavevectors)
    push!(hms, heatmap(grid_online.ranges[1], grid_online.ranges[2], sf[i]';
                       title="\$k = $(round(q/π; digits=3))\\pi\$", kwargs...))
end
plot(hms...)
Example block output

It can be nicely seen that the spin structure factor indicates the ferromagnetic phase at $k=0$ and then moves through the magnetization plateaus until it reaches the antiferromagnetic plateau at $k=\pi$.