Optimizing the 2D Heisenberg model
In this example we want to provide a basic rundown of PEPSKit's optimization workflow for PEPS. To that end, we will consider the two-dimensional Heisenberg model on a square lattice
\[H = \sum_{\langle i,j \rangle} \left ( J_x S^{x}_i S^{x}_j + J_y S^{y}_i S^{y}_j + J_z S^{z}_i S^{z}_j \right )\]
Here, we want to set $J_x = J_y = J_z = 1$ where the Heisenberg model is in the antiferromagnetic regime. Due to the bipartite sublattice structure of antiferromagnetic order one needs a PEPS ansatz with a $2 \times 2$ unit cell. This can be circumvented by performing a unitary sublattice rotation on all B-sites resulting in a change of parameters to $(J_x, J_y, J_z)=(-1, 1, -1)$. This gives us a unitarily equivalent Hamiltonian (with the same spectrum) with a ground state on a single-site unit cell.
Let us get started by fixing the random seed of this example to make it deterministic:
using Random
Random.seed!(123456789);
We're going to need only two packages: TensorKit
, since we use that for all the underlying tensor operations, and PEPSKit
itself. So let us import these:
using TensorKit, PEPSKit
Defining the Heisenberg Hamiltonian
To create the sublattice rotated Heisenberg Hamiltonian on an infinite square lattice, we use the heisenberg_XYZ
method from MPSKitModels which is redefined for the InfiniteSquare
and reexported in PEPSKit:
H = heisenberg_XYZ(InfiniteSquare(); Jx=-1, Jy=1, Jz=-1)
LocalOperator{Tuple{Pair{Tuple{CartesianIndex{2}, CartesianIndex{2}}, TensorKit.TensorMap{ComplexF64, TensorKit.ComplexSpace, 2, 2, Vector{ComplexF64}}}, Pair{Tuple{CartesianIndex{2}, CartesianIndex{2}}, TensorKit.TensorMap{ComplexF64, TensorKit.ComplexSpace, 2, 2, Vector{ComplexF64}}}}, TensorKit.ComplexSpace}(TensorKit.ComplexSpace[ℂ^2;;], ((CartesianIndex(1, 1), CartesianIndex(1, 2)) => TensorMap((ℂ^2 ⊗ ℂ^2) ← (ℂ^2 ⊗ ℂ^2)):
[:, :, 1, 1] =
-0.25 + 0.0im 0.0 + 0.0im
0.0 + 0.0im -0.5 + 0.0im
[:, :, 2, 1] =
0.0 + 0.0im 0.0 + 0.0im
0.25 + 0.0im 0.0 + 0.0im
[:, :, 1, 2] =
0.0 + 0.0im 0.25 + 0.0im
0.0 + 0.0im 0.0 + 0.0im
[:, :, 2, 2] =
-0.5 + 0.0im 0.0 + 0.0im
0.0 + 0.0im -0.25 + 0.0im
, (CartesianIndex(1, 1), CartesianIndex(2, 1)) => TensorMap((ℂ^2 ⊗ ℂ^2) ← (ℂ^2 ⊗ ℂ^2)):
[:, :, 1, 1] =
-0.25 + 0.0im 0.0 + 0.0im
0.0 + 0.0im -0.5 + 0.0im
[:, :, 2, 1] =
0.0 + 0.0im 0.0 + 0.0im
0.25 + 0.0im 0.0 + 0.0im
[:, :, 1, 2] =
0.0 + 0.0im 0.25 + 0.0im
0.0 + 0.0im 0.0 + 0.0im
[:, :, 2, 2] =
-0.5 + 0.0im 0.0 + 0.0im
0.0 + 0.0im -0.25 + 0.0im
))
Setting up the algorithms and initial guesses
Next, we set the simulation parameters. During optimization, the PEPS will be contracted using CTMRG and the PEPS gradient will be computed by differentiating through the CTMRG routine using AD. Since the algorithmic stack that implements this is rather elaborate, the amount of settings one can configure is also quite large. To reduce this complexity, PEPSKit defaults to (presumably) reasonable settings which also dynamically adapts to the user-specified parameters.
First, we set the bond dimension Dbond
of the virtual PEPS indices and the environment dimension χenv
of the virtual corner and transfer matrix indices.
Dbond = 2
χenv = 16;
To configure the CTMRG algorithm, we create a NamedTuple
containing different keyword arguments. To see a description of all arguments, see the docstring of leading_boundary
. Here, we want to converge the CTMRG environments up to a specific tolerance and during the CTMRG run keep all index dimensions fixed:
boundary_alg = (; tol=1e-10, trscheme=(; alg=:fixedspace));
Let us also configure the optimizer algorithm. We are going to optimize the PEPS using the L-BFGS optimizer from OptimKit. Again, we specify the convergence tolerance (for the gradient norm) as well as the maximal number of iterations and the BFGS memory size (which is used to approximate the Hessian):
optimizer_alg = (; alg=:lbfgs, tol=1e-4, maxiter=100, lbfgs_memory=16);
Additionally, during optimization, we want to reuse the previous CTMRG environment to initialize the CTMRG run of the current optimization step using the reuse_env
argument. And to control the output information, we set the verbosity
:
reuse_env = true
verbosity = 3;
Next, we initialize a random PEPS which will be used as an initial guess for the optimization. To get a PEPS with physical dimension 2 (since we have a spin-1/2 Hamiltonian) with complex-valued random Gaussian entries, we set:
peps₀ = InfinitePEPS(randn, ComplexF64, 2, Dbond)
InfinitePEPS{TensorKit.TensorMap{ComplexF64, TensorKit.ComplexSpace, 1, 4, Vector{ComplexF64}}}(TensorKit.TensorMap{ComplexF64, TensorKit.ComplexSpace, 1, 4, Vector{ComplexF64}}[TensorMap(ℂ^2 ← (ℂ^2 ⊗ ℂ^2 ⊗ (ℂ^2)' ⊗ (ℂ^2)')):
[:, :, 1, 1, 1] =
0.07382174258286094 + 0.12820373667088403im 0.7897519397510839 + 0.9113654266438473im
0.2553716885006697 - 0.4358399804354269im -1.0272416446076236 - 0.12635062198157215im
[:, :, 2, 1, 1] =
0.16833628450178303 - 0.10088950122180829im -0.9702030532300809 + 0.010730752411986726im
-1.6804460553576506 + 0.29081053879369084im 0.6844811667615024 + 0.09101537356941222im
[:, :, 1, 2, 1] =
0.5085938050744258 + 0.3786892551842583im 1.0020057959636561 - 1.4704891009758718im
-0.6153328223084331 + 0.10417896606055738im 0.6024931811537675 - 1.0348374874397468im
[:, :, 2, 2, 1] =
-0.027201695938305456 + 0.5778042099380925im 0.09232089635078945 + 0.6143070126937361im
1.0707115218777772 - 0.5747168579241235im -0.5819741818511422 - 0.9842624134267605im
[:, :, 1, 1, 2] =
1.2332543810053822 - 1.7783531996396438im 0.8887723728085348 + 0.7809798723615474im
1.2251189302516847 - 0.6853683793073324im 1.5333834584675397 - 0.13856216581406375im
[:, :, 2, 1, 2] =
0.1406381347783769 + 0.6630243440357264im -0.7294596235434386 + 0.40327909254711103im
0.7212056487788236 + 0.24320971945037498im 0.9991347929322827 + 0.0017902515981375842im
[:, :, 1, 2, 2] =
0.34282910982693904 - 0.4865238029567361im 0.9380949844871762 - 0.6985342237892025im
-0.7437083517319159 - 0.6895708849529253im -0.8981092940164176 + 0.9720706252141459im
[:, :, 2, 2, 2] =
-0.8897079923413616 - 0.7145412189457411im 0.07771261045117502 - 0.6400190994609709im
-1.6099412157243007 + 0.8855200965611144im 0.7357380595021633 + 0.4626916850143416im
;;])
The last thing we need before we can start the optimization is an initial CTMRG environment. Typically, a random environment which we converge on peps₀
serves as a good starting point. To contract a PEPS starting from an environment using CTMRG, we call leading_boundary
:
env_random = CTMRGEnv(randn, ComplexF64, peps₀, ℂ^χenv);
env₀, info_ctmrg = leading_boundary(env_random, peps₀; boundary_alg...);
[ Info: CTMRG init: obj = -2.749614463601e+00 +3.639628057806e+00im err = 1.0000e+00
[ Info: CTMRG conv 27: obj = +9.727103564786e+00 err = 2.6201331116e-11 time = 0.11 sec
Besides the converged environment, leading_boundary
also returns a NamedTuple
of informational quantities such as the last maximal truncation error - that is, the SVD approximation error incurred in the last CTMRG iteration, maximized over all spatial directions and unit cell entries:
@show info_ctmrg.truncation_error;
info_ctmrg.truncation_error = 0.0008076332824218652
Ground state search
Finally, we can start the optimization by calling fixedpoint
on H
with our settings for the boundary (CTMRG) algorithm and the optimizer. This might take a while (especially the precompilation of AD code in this case):
peps, env, E, info_opt = fixedpoint(
H, peps₀, env₀; boundary_alg, optimizer_alg, reuse_env, verbosity
);
[ Info: LBFGS: initializing with f = 0.000601645310, ‖∇f‖ = 9.3547e-01
[ Info: LBFGS: iter 1, time 530.06 s: f = -0.489780515318, ‖∇f‖ = 6.0029e-01, α = 5.94e+01, m = 0, nfg = 5
[ Info: LBFGS: iter 2, time 530.54 s: f = -0.501969370083, ‖∇f‖ = 5.3739e-01, α = 2.80e-01, m = 1, nfg = 2
[ Info: LBFGS: iter 3, time 530.74 s: f = -0.523150697049, ‖∇f‖ = 3.9920e-01, α = 1.00e+00, m = 2, nfg = 1
[ Info: LBFGS: iter 4, time 531.20 s: f = -0.538654572532, ‖∇f‖ = 4.1550e-01, α = 2.29e-01, m = 3, nfg = 2
[ Info: LBFGS: iter 5, time 532.38 s: f = -0.549895732330, ‖∇f‖ = 4.4023e-01, α = 6.96e-02, m = 4, nfg = 4
[ Info: LBFGS: iter 6, time 532.92 s: f = -0.568903773751, ‖∇f‖ = 4.8251e-01, α = 2.23e-01, m = 5, nfg = 2
[ Info: LBFGS: iter 7, time 533.16 s: f = -0.586868032368, ‖∇f‖ = 4.2837e-01, α = 1.00e+00, m = 6, nfg = 1
[ Info: LBFGS: iter 8, time 533.38 s: f = -0.599838784884, ‖∇f‖ = 2.2069e-01, α = 1.00e+00, m = 7, nfg = 1
[ Info: LBFGS: iter 9, time 533.60 s: f = -0.606610614420, ‖∇f‖ = 1.9251e-01, α = 1.00e+00, m = 8, nfg = 1
[ Info: LBFGS: iter 10, time 533.84 s: f = -0.624864046816, ‖∇f‖ = 2.9515e-01, α = 1.00e+00, m = 9, nfg = 1
[ Info: LBFGS: iter 11, time 534.08 s: f = -0.638375159059, ‖∇f‖ = 2.3675e-01, α = 1.00e+00, m = 10, nfg = 1
[ Info: LBFGS: iter 12, time 534.31 s: f = -0.644407080181, ‖∇f‖ = 3.2337e-01, α = 1.00e+00, m = 11, nfg = 1
[ Info: LBFGS: iter 13, time 534.50 s: f = -0.651446429016, ‖∇f‖ = 1.3169e-01, α = 1.00e+00, m = 12, nfg = 1
[ Info: LBFGS: iter 14, time 534.72 s: f = -0.654528109114, ‖∇f‖ = 6.6176e-02, α = 1.00e+00, m = 13, nfg = 1
[ Info: LBFGS: iter 15, time 534.91 s: f = -0.655971360805, ‖∇f‖ = 5.1875e-02, α = 1.00e+00, m = 14, nfg = 1
[ Info: LBFGS: iter 16, time 535.10 s: f = -0.657229359697, ‖∇f‖ = 5.8978e-02, α = 1.00e+00, m = 15, nfg = 1
[ Info: LBFGS: iter 17, time 535.32 s: f = -0.658531955935, ‖∇f‖ = 5.5554e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 18, time 535.52 s: f = -0.659295132854, ‖∇f‖ = 3.0496e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 19, time 536.06 s: f = -0.659541951176, ‖∇f‖ = 2.2298e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 20, time 536.20 s: f = -0.659737986411, ‖∇f‖ = 2.7588e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 21, time 536.36 s: f = -0.659907309305, ‖∇f‖ = 1.9371e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 22, time 536.54 s: f = -0.660097028826, ‖∇f‖ = 1.4424e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 23, time 536.71 s: f = -0.660261859699, ‖∇f‖ = 1.2401e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 24, time 536.87 s: f = -0.660393163588, ‖∇f‖ = 1.9193e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 25, time 537.07 s: f = -0.660497281869, ‖∇f‖ = 1.3339e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 26, time 537.25 s: f = -0.660573874568, ‖∇f‖ = 1.2488e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 27, time 537.44 s: f = -0.660741356378, ‖∇f‖ = 1.6202e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 28, time 537.65 s: f = -0.660904313170, ‖∇f‖ = 1.8663e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 29, time 537.86 s: f = -0.661016299819, ‖∇f‖ = 1.3961e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 30, time 538.08 s: f = -0.661073848395, ‖∇f‖ = 8.0058e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 31, time 538.31 s: f = -0.661115845926, ‖∇f‖ = 7.7807e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 32, time 538.56 s: f = -0.661170946003, ‖∇f‖ = 9.2025e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 33, time 538.80 s: f = -0.661189817521, ‖∇f‖ = 1.7374e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 34, time 539.03 s: f = -0.661228749942, ‖∇f‖ = 5.5230e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 35, time 539.24 s: f = -0.661241674095, ‖∇f‖ = 4.6578e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 36, time 539.47 s: f = -0.661255546757, ‖∇f‖ = 5.3449e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 37, time 539.65 s: f = -0.661267660051, ‖∇f‖ = 1.2056e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 38, time 539.85 s: f = -0.661283904430, ‖∇f‖ = 6.5960e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 39, time 540.02 s: f = -0.661292307599, ‖∇f‖ = 4.6473e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 40, time 540.23 s: f = -0.661305251122, ‖∇f‖ = 5.5816e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 41, time 540.44 s: f = -0.661333861848, ‖∇f‖ = 1.0256e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 42, time 540.63 s: f = -0.661388067313, ‖∇f‖ = 1.3577e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 43, time 540.83 s: f = -0.661466697789, ‖∇f‖ = 2.6785e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 44, time 541.03 s: f = -0.661613249837, ‖∇f‖ = 1.8758e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 45, time 541.23 s: f = -0.661815950052, ‖∇f‖ = 3.1866e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 46, time 541.44 s: f = -0.661914266112, ‖∇f‖ = 2.3377e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 47, time 541.66 s: f = -0.661987630419, ‖∇f‖ = 2.6251e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 48, time 541.89 s: f = -0.662122898853, ‖∇f‖ = 4.1624e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 49, time 542.13 s: f = -0.662298584531, ‖∇f‖ = 1.1873e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 50, time 542.32 s: f = -0.662344209408, ‖∇f‖ = 8.9765e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 51, time 542.53 s: f = -0.662406985361, ‖∇f‖ = 9.1973e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 52, time 542.74 s: f = -0.662448422288, ‖∇f‖ = 1.4069e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 53, time 542.95 s: f = -0.662470844143, ‖∇f‖ = 1.1027e-02, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 54, time 543.18 s: f = -0.662486913078, ‖∇f‖ = 3.8679e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 55, time 543.81 s: f = -0.662492606250, ‖∇f‖ = 2.6466e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 56, time 543.99 s: f = -0.662499228449, ‖∇f‖ = 2.5451e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 57, time 544.22 s: f = -0.662501449522, ‖∇f‖ = 6.7506e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 58, time 544.38 s: f = -0.662506093367, ‖∇f‖ = 2.2951e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 59, time 544.54 s: f = -0.662508017653, ‖∇f‖ = 1.3999e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 60, time 544.69 s: f = -0.662509746635, ‖∇f‖ = 1.4558e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 61, time 544.86 s: f = -0.662510852584, ‖∇f‖ = 2.9108e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 62, time 545.02 s: f = -0.662511459247, ‖∇f‖ = 2.2460e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 63, time 545.19 s: f = -0.662511826357, ‖∇f‖ = 7.7832e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 64, time 545.35 s: f = -0.662511947523, ‖∇f‖ = 6.8985e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 65, time 545.52 s: f = -0.662512259234, ‖∇f‖ = 9.7159e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 66, time 545.70 s: f = -0.662512598991, ‖∇f‖ = 9.8695e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 67, time 546.08 s: f = -0.662512857448, ‖∇f‖ = 1.7013e-03, α = 5.19e-01, m = 16, nfg = 2
[ Info: LBFGS: iter 68, time 546.27 s: f = -0.662513219985, ‖∇f‖ = 6.8818e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 69, time 546.46 s: f = -0.662513359115, ‖∇f‖ = 7.6621e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 70, time 546.65 s: f = -0.662513515960, ‖∇f‖ = 6.8437e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 71, time 546.84 s: f = -0.662513618580, ‖∇f‖ = 1.1894e-03, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 72, time 547.02 s: f = -0.662513786456, ‖∇f‖ = 4.9346e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 73, time 547.18 s: f = -0.662513847396, ‖∇f‖ = 4.4509e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 74, time 547.34 s: f = -0.662513979836, ‖∇f‖ = 4.8820e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 75, time 547.51 s: f = -0.662514103588, ‖∇f‖ = 4.8320e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 76, time 547.69 s: f = -0.662514142873, ‖∇f‖ = 7.4691e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 77, time 547.90 s: f = -0.662514224367, ‖∇f‖ = 2.1563e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 78, time 548.10 s: f = -0.662514238736, ‖∇f‖ = 1.6048e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 79, time 548.30 s: f = -0.662514255026, ‖∇f‖ = 1.8486e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 80, time 548.51 s: f = -0.662514264792, ‖∇f‖ = 1.7897e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 81, time 548.70 s: f = -0.662514271376, ‖∇f‖ = 1.2336e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: iter 82, time 548.87 s: f = -0.662514276574, ‖∇f‖ = 1.1665e-04, α = 1.00e+00, m = 16, nfg = 1
[ Info: LBFGS: converged after 83 iterations and time 549.04 s: f = -0.662514280757, ‖∇f‖ = 9.1781e-05
Note that fixedpoint
returns the final optimized PEPS, the last converged environment, the final energy estimate as well as a NamedTuple
of diagnostics. This allows us to, e.g., analyze the number of cost function calls or the history of gradient norms to evaluate the convergence rate:
@show info_opt.fg_evaluations info_opt.gradnorms[1:10:end];
info_opt.fg_evaluations = 95
info_opt.gradnorms[1:10:end] = [0.9354698847828358, 0.29515098058470357, 0.02758843397008457, 0.0080057855195194, 0.005581593433505604, 0.008976519236226013, 0.0014558090494201758, 0.0006843650270592786, 0.00017896547683177183]
Let's now compare the optimized energy against an accurate Quantum Monte Carlo estimate by Sandvik, where the energy per site was found to be $E_{\text{ref}}=−0.6694421$. From our simple optimization we find:
@show E;
E = -0.6625142807571207
While this energy is in the right ballpark, there is still quite some deviation from the accurate reference energy. This, however, can be attributed to the small bond dimension - an optimization with larger bond dimension would approach this value much more closely.
A more reasonable comparison would be against another finite bond dimension PEPS simulation. For example, Juraj Hasik's data from $J_1\text{-}J_2$ PEPS simulations yields $E_{D=2,\chi=16}=-0.660231\dots$ which is more in line with what we find here.
Compute the correlation lengths and transfer matrix spectra
In practice, in order to obtain an accurate and variational energy estimate, one would need to compute multiple energies at different environment dimensions and extrapolate in, e.g., the correlation length or the second gap of the transfer matrix spectrum. For that, we would need the correlation_length
function, which computes the horizontal and vertical correlation lengths and transfer matrix spectra for all unit cell coordinates:
ξ_h, ξ_v, λ_h, λ_v = correlation_length(peps, env)
@show ξ_h ξ_v;
ξ_h = [1.0343595729516264]
ξ_v = [1.0242394972193403]
Computing observables
As a last thing, we want to see how we can compute expectation values of observables, given the optimized PEPS and its CTMRG environment. To compute, e.g., the magnetization, we first need to define the observable as a TensorMap
:
σ_z = TensorMap([1.0 0.0; 0.0 -1.0], ℂ^2, ℂ^2)
TensorMap(ℂ^2 ← ℂ^2):
1.0 0.0
0.0 -1.0
In order to be able to contract it with the PEPS and environment, we define need to define a LocalOperator
and specify on which physical spaces and sites the observable acts. That way, the PEPS-environment-operator contraction gets automatically generated (also works for multi-site operators!). See the LocalOperator
docstring for more details. The magnetization is just a single-site observable, so we have:
M = LocalOperator(fill(ℂ^2, 1, 1), (CartesianIndex(1, 1),) => σ_z)
LocalOperator{Tuple{Pair{Tuple{CartesianIndex{2}}, TensorKit.TensorMap{Float64, TensorKit.ComplexSpace, 1, 1, Vector{Float64}}}}, TensorKit.ComplexSpace}(TensorKit.ComplexSpace[ℂ^2;;], ((CartesianIndex(1, 1),) => TensorMap(ℂ^2 ← ℂ^2):
1.0 0.0
0.0 -1.0
,))
Finally, to evaluate the expecation value on the LocalOperator
, we call:
@show expectation_value(peps, M, env);
expectation_value(peps, M, env) = -0.7550757719796017 - 6.317220878853466e-16im
This page was generated using Literate.jl.