From 399a6000955707c7fc6655b725374cf003936004 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 1 Nov 2024 21:12:57 +0800 Subject: [PATCH 01/75] remove CTMRG redundant logging --- src/algorithms/ctmrg/ctmrg.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 79d3f033e..00fc45977 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -115,10 +115,8 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRG) ctmrg_loginit!(log, η, N) for iter in 1:(alg.maxiter) env, = ctmrg_iter(state, env, alg) # Grow and renormalize in all 4 directions - η, CS, TS = calc_convergence(env, CS, TS) N = norm(state, env) - ctmrg_logiter!(log, iter, η, N) if η ≤ alg.tol && iter ≥ alg.miniter ctmrg_logfinish!(log, iter, η, N) From 55f1e0085db0d17526ebfe3950f4826b11979dad Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 6 Nov 2024 16:15:32 +0800 Subject: [PATCH 02/75] add simple update algorithm --- src/PEPSKit.jl | 8 + src/algorithms/timeevol/simpleupdate.jl | 186 ++++++++++++++++++++++++ src/states/suweight.jl | 46 ++++++ src/utility/mirror.jl | 14 ++ src/utility/svd.jl | 13 ++ src/utility/util.jl | 25 ++++ 6 files changed, 292 insertions(+) create mode 100644 src/algorithms/timeevol/simpleupdate.jl create mode 100644 src/states/suweight.jl create mode 100644 src/utility/mirror.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index c8a6b032b..957c3f511 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -17,12 +17,14 @@ include("utility/util.jl") include("utility/diffable_threads.jl") include("utility/svd.jl") include("utility/rotations.jl") +include("utility/mirror.jl") include("utility/diffset.jl") include("utility/hook_pullback.jl") include("utility/autoopt.jl") include("states/abstractpeps.jl") include("states/infinitepeps.jl") +include("states/suweight.jl") include("operators/transferpeps.jl") include("operators/infinitepepo.jl") @@ -43,6 +45,9 @@ include("algorithms/ctmrg/sparse_environments.jl") include("algorithms/ctmrg/ctmrg.jl") include("algorithms/ctmrg/gaugefix.jl") +include("algorithms/timeevol/simpleupdate.jl") +include("algorithms/timeevol/fullupdate.jl") + include("algorithms/toolbox.jl") include("algorithms/peps_opt.jl") @@ -167,6 +172,9 @@ export leading_boundary export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint +export simpleupdate!, absorb_wt + +export SUWeight export InfinitePEPS, InfiniteTransferPEPS export InfinitePEPO, InfiniteTransferPEPO export initializeMPS, initializePEPS diff --git a/src/algorithms/timeevol/simpleupdate.jl b/src/algorithms/timeevol/simpleupdate.jl new file mode 100644 index 000000000..52400ae95 --- /dev/null +++ b/src/algorithms/timeevol/simpleupdate.jl @@ -0,0 +1,186 @@ +""" +Mirror the unit cell of an iPEPS by its anti-diagonal line +""" +function mirror_antidiag!(peps::InfinitePEPS) + peps.A[:] = mirror_antidiag(peps.A) + for (i, t) in enumerate(peps.A) + peps.A[i] = permute(t, (1,), (3,2,5,4)) + end +end + +""" +Mirror the unit cell of an iPEPS with weights by its anti-diagonal line +""" +function mirror_antidiag!(wts::SUWeight) + wts.x[:], wts.y[:] = mirror_antidiag(wts.y), mirror_antidiag(wts.x) +end + +""" +Absorb environment weight on axis `ax` into tensor `t` at position `(row,col)` +``` + ↓ + y[r,c] + ↓ + ←x[r,c-1] ← T[r,c] ← x[r,c] ← + ↓ + y[r+1,c] + ↓ +``` +""" +function absorb_wt( + t::AbstractTensorMap, row::Int, col::Int, + ax::Int, wts::SUWeight; + sqrtwt::Bool=false, invwt::Bool=false +) + Nr, Nc = size(wts) + @assert 1 <= row <= Nr && 1 <= col <= Nc + @assert 2 <= ax <= 5 + pow = (sqrtwt ? 1/2 : 1) * (invwt ? -1 : 1) + if ax == 2 # north + wt = wts.y[row, col] + elseif ax == 3 # east + wt = wts.x[row, col] + elseif ax == 4 # south + wt = wts.y[_next(row,Nr), col] + else # west + wt = wts.x[row, _prev(col,Nc)] + end + wt2 = sdiag_pow(wt, pow) + indices_t = collect(-1:-1:-5) + indices_t[ax] = 1 + indices_wt = (ax in (2,3) ? [1,-ax] : [-ax,1]) + t2 = ncon((t, wt2), (indices_t, indices_wt)) + # restore codomain and domain + t2 = permute(t2, (1,), Tuple(2:5)) + return t2 +end + + +""" +Simple update of bond `wts.x[r,c]` +``` + y[r,c] y[r,c+1] + ↓ ↓ + x[r,c-1] ←- T[r,c] ←- x[r,c] ←- T[r,c+1] ← x[r,c+1] + ↓ ↓ + y[r+1,c] y[r+1,c+1] +``` +""" +function _su_bondx!( + row::Int, col::Int, gate::AbstractTensorMap, + peps::InfinitePEPS, wts::SUWeight, + Dcut::Int, svderr::Float64=1e-10 +) + Nr, Nc = size(peps) + @assert 1 <= row <= Nr && 1 <= col <= Nc + row2, col2 = row, _next(col,Nc) + T1, T2 = peps[row,col], peps[row2,col2] + # absorb environment weights + for ax in (2,4,5) + T1 = absorb_wt(T1, row, col, ax, wts) + end + for ax in (2,3,4) + T2 = absorb_wt(T2, row2, col2, ax, wts) + end + # absorb bond weight + T1 = absorb_wt(T1, row, col, 3, wts; sqrtwt=true) + T2 = absorb_wt(T2, row2, col2, 5, wts; sqrtwt=true) + # QR and LQ decomposition + """ + 2 1 1 2 + ↓ ↗ ↓ ↗ + 5 ← T ← 3 ====> 3 ← X ← 4 ← 1 ← aR ← 3 + ↓ ↓ + 4 2 + """ + X, aR = leftorth(T1, ((2,4,5), (1,3)), alg=QRpos()) + """ + 2 1 2 2 + ↓ ↗ ↗ ↓ + 5 ← T ← 3 ====> 1 ← bL ← 3 ← 1 ← Y ← 3 + ↓ ↓ + 4 4 + """ + bL, Y = rightorth(T2, ((5,1), (2,3,4)), alg=LQpos()) + # apply gate + """ + -2 -3 + ↑ ↑ + |----gate---| + ↑ ↑ + 1 2 + ↑ ↑ + -1← aR -← 3 -← bL ← -4 + """ + tmp = ncon((gate, aR, bL), ([-2,-3,1,2], [-1,1,3], [3,2,-4])) + # SVD + truncscheme = truncerr(svderr) & truncdim(Dcut) + aR, s, bL, ϵ = tsvd(tmp, ((1,2), (3,4)); trunc=truncscheme) + """ + -2 -1 -1 -2 + | ↗ ↗ | + -5- X ← 1 ← aR - -3 -5 - bL ← 1 ← Y - -3 + | | + -4 -4 + """ + T1 = ncon((X, aR), ([-2,-4,-5,1], [1,-1,-3])) + T2 = ncon((bL, Y), ([-5,-1,1], [1,-2,-3,-4])) + # remove environment weights + for ax in (2,4,5) + T1 = absorb_wt(T1, row, col, ax, wts; invwt=true) + end + for ax in (2,3,4) + T2 = absorb_wt(T2, row2, col2, ax, wts; invwt=true) + end + # update tensor dict and weight on current bond (with normalization) + peps.A[row,col], peps.A[row2,col2] = T1, T2 + wts.x[row,col] = s / maxabs(s) + return ϵ +end + + +""" +One round of simple update on the input InfinitePEPS `peps` +and SUWeight `wts` with the nearest neighbor gate `gate` + +When `bipartite === true` (for square lattice), the unit cell size should be 2 x 2, +and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` +""" +function simpleupdate!( + gate::AbstractTensorMap, peps::InfinitePEPS, wts::SUWeight, + Dcut::Int, svderr::Float64=1e-10; bipartite::Bool=false, +) + Nr, Nc = size(peps) + if bipartite + @assert Nr == Nc == 2 + end + # TODO: make algorithm independent on the choice of dual in the network + for (r, c) in Iterators.product(1:Nr, 1:Nc) + @assert [isdual(space(peps.A[r, c], ax)) for ax in 1:5] == [0,1,1,0,0] + @assert [isdual(space(wts.x[r, c], ax)) for ax in 1:2] == [0,1] + @assert [isdual(space(wts.y[r, c], ax)) for ax in 1:2] == [0,1] + end + for direction in 1:2 + # mirror the y-weights to x-direction + # to update them using code for x-weights + if direction == 2 + mirror_antidiag!(peps); mirror_antidiag!(wts) + end + if bipartite + ϵ = _su_bondx!(1, 1, gate, peps, wts, Dcut, svderr) + (peps.A[2,2], peps.A[2,1], wts.x[2,2]) = deepcopy.((peps.A[1,1], peps.A[1,2], wts.x[1,1])) + ϵ = _su_bondx!(2, 1, gate, peps, wts, Dcut, svderr) + (peps.A[1,2], peps.A[1,1], wts.x[1,2]) = deepcopy.((peps.A[2,1], peps.A[2,2], wts.x[2,1])) + else + for site in CartesianIndices(peps.A) + row, col = Tuple(site) + ϵ = _su_bondx!(row, col, gate, peps, wts, Dcut) + end + end + if direction == 2 + mirror_antidiag!(peps); mirror_antidiag!(wts) + end + end + return nothing +end + diff --git a/src/states/suweight.jl b/src/states/suweight.jl new file mode 100644 index 000000000..97b758322 --- /dev/null +++ b/src/states/suweight.jl @@ -0,0 +1,46 @@ +""" +Schmidt bond weight used in simple/cluster update +""" +struct SUWeight{T<:AbstractTensorMap} + x::Matrix{T} + y::Matrix{T} + + function SUWeight(wxs::Matrix{T}, wys::Matrix{T}) where {T} + new{T}(wxs, wys) + end +end + +function Base.size(wts::SUWeight) + @assert size(wts.x) == size(wts.y) + return size(wts.x) +end + +function Base.:(==)(wts1::SUWeight, wts2::SUWeight) + return wts1.x == wts2.x && wts1.y == wts2.y +end + +function Base.:(+)(wts1::SUWeight, wts2::SUWeight) + return SUWeight(wts1.x + wts2.x, wts1.y + wts2.y) +end + +function Base.:(-)(wts1::SUWeight, wts2::SUWeight) + return SUWeight(wts1.x - wts2.x, wts1.y - wts2.y) +end + +function Base.iterate(wts::SUWeight, state=1) + nx = prod(size(wts.x)) + if 1 <= state <= nx + return wts.x[state], state+1 + elseif nx+1 <= state <= 2*nx + return wts.y[state-nx], state+1 + else + return nothing + end +end + +function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) + return ( + isapprox(wts1.x, wts2.x; atol=atol, rtol=rtol) && + isapprox(wts1.y, wts2.y; atol=atol, rtol=rtol) + ) +end diff --git a/src/utility/mirror.jl b/src/utility/mirror.jl new file mode 100644 index 000000000..f9bc74ae7 --- /dev/null +++ b/src/utility/mirror.jl @@ -0,0 +1,14 @@ +""" +Mirror a matrix by its anti-diagonal line +(the 45 degree line through the lower-left corner) + +The element originally at [r, c] is moved [Nc-c+1, Nr-r+1], +i.e. the element now at [r, c] was originally at [Nr-c+1, Nc-r+1] +""" +function mirror_antidiag(arr::AbstractMatrix) + Nr, Nc = size(arr) + return collect( + arr[Nr-c+1, Nc-r+1] + for (r, c) in Iterators.product(1:Nc, 1:Nr) + ) +end diff --git a/src/utility/svd.jl b/src/utility/svd.jl index a590c87a9..20950ac93 100644 --- a/src/utility/svd.jl +++ b/src/utility/svd.jl @@ -291,3 +291,16 @@ function _lorentz_broaden(x::Real, ε=1e-12) x′ = 1 / x return x′ / (x′^2 + ε) end + + +""" +Given `tsvd` result `u`, `s` and `vh`, +absorb singular values `s` into `u` and `vh` by +``` + u -> u * sqrt(s), vh -> sqrt(s) * vh +``` +""" +function absorb_s(u::AbstractTensorMap, s::AbstractTensorMap, vh::AbstractTensorMap) + sqrt_s = sdiag_pow(s, 0.5) + return u * sqrt_s, sqrt_s * vh +end diff --git a/src/utility/util.jl b/src/utility/util.jl index 0b9fd4c49..181eb3a97 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -21,6 +21,31 @@ function _elementwise_mult(a::AbstractTensorMap, b::AbstractTensorMap) return dst end +""" +Return the maximum absolute value of tensor elements +""" +function maxabs(t::AbstractTensorMap) + maxel = 0.0 + for (k, b) in blocks(t) + maxelb = maximum(abs.(b)) + if maxelb > maxel + maxel = maxelb + end + end + return maxel +end + +""" +Compute S^(pow) for diagonal matrices `S` +""" +function sdiag_pow(S::AbstractTensorMap, pow::Real) + S2 = similar(S) + for (k, b) in blocks(S) + copyto!(blocks(S2)[k], diagm(diag(b).^pow)) + end + return S2 +end + # Compute √S⁻¹ for diagonal TensorMaps _safe_inv(a, tol) = abs(a) < tol ? zero(a) : inv(a) function sdiag_inv_sqrt(S::AbstractTensorMap; tol::Real=eps(eltype(S))^(3 / 4)) From f9d19fd04950ed6082c535228b1f42b1c1139e30 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 6 Nov 2024 20:39:13 +0800 Subject: [PATCH 03/75] add full update core algorithm --- src/PEPSKit.jl | 3 +- src/algorithms/ctmrg/ctmrg.jl | 29 ++- src/algorithms/timeevol/fu_gaugefix.jl | 92 +++++++ src/algorithms/timeevol/fu_optimize.jl | 325 ++++++++++++++++++++++++ src/algorithms/timeevol/fullupdate.jl | 195 ++++++++++++++ src/algorithms/timeevol/simpleupdate.jl | 27 +- src/environments/ctmrg_environments.jl | 38 +++ src/states/infinitepeps.jl | 12 + 8 files changed, 701 insertions(+), 20 deletions(-) create mode 100644 src/algorithms/timeevol/fu_gaugefix.jl create mode 100644 src/algorithms/timeevol/fu_optimize.jl create mode 100644 src/algorithms/timeevol/fullupdate.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 957c3f511..bd71e7287 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -64,7 +64,7 @@ include("utility/symmetrization.jl") const ctmrgscheme = :simultaneous const reuse_env = true const trscheme = FixedSpaceTruncation() - const fwd_alg = TensorKit.SVD() + const fwd_alg = TensorKit.SDD() const rrule_alg = Arnoldi(; tol=1e-2fpgrad_tol, krylovdim=48, verbosity=-1) const svd_alg = SVDAdjoint(; fwd_alg, rrule_alg) const optimizer = LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=2) @@ -173,6 +173,7 @@ export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint export simpleupdate!, absorb_wt +export fullupdate! export SUWeight export InfinitePEPS, InfiniteTransferPEPS diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 8e74436f8..79fb5317e 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -133,6 +133,28 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRG) end end +""" +Perform CTMRG left move on the `col`-th column +""" +function ctmrg_leftmove( + col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG +) + """ + ----> left move + C1 ← T1 ← r-1 + ↓ ‖ + T4 = M == r + ↓ ‖ + C4 → T3 → r+1 + c-1 c + """ + enlarged_envs = ctmrg_expand( + eachcoordinate(envs, [4, 1])[:, :, col], state, envs + ) + projectors, info = ctmrg_projectors(col, enlarged_envs, envs, alg) + envs = ctmrg_renormalize(col, projectors, state, envs, alg) + return envs, info +end """ ctmrg_iter(state, envs::CTMRGEnv, alg::CTMRG) -> envs′, info @@ -143,17 +165,12 @@ function ctmrg_iter(state, envs::CTMRGEnv, alg::SequentialCTMRG) ϵ = zero(real(scalartype(state))) for _ in 1:4 # rotate for col in 1:size(state, 2) # left move column-wise - enlarged_envs = ctmrg_expand( - eachcoordinate(envs, [4, 1])[:, :, col], state, envs - ) - projectors, info = ctmrg_projectors(col, enlarged_envs, envs, alg) - envs = ctmrg_renormalize(col, projectors, state, envs, alg) + envs, info = ctmrg_leftmove(col, state, envs, alg) ϵ = max(ϵ, info.err) end state = rotate_north(state, EAST) envs = rotate_north(envs, EAST) end - return envs, (; err=ϵ) end function ctmrg_iter(state, envs::CTMRGEnv, alg::SimultaneousCTMRG) diff --git a/src/algorithms/timeevol/fu_gaugefix.jl b/src/algorithms/timeevol/fu_gaugefix.jl new file mode 100644 index 000000000..760d781d1 --- /dev/null +++ b/src/algorithms/timeevol/fu_gaugefix.jl @@ -0,0 +1,92 @@ +""" +Replace `env` by its positive/negative approximant `± Z Z†` +(returns the sign and Z†) +``` + |-→ 1 2 ←-| + | | + |----env----| |←--- Z ---→| + |→ 1 2 ←| = ↑ + |← 3 4 →| |---→ Z† ←--| + |-----------| | | + |←- 3 4 -→| +``` +""" +function positive_approx(env::AbstractTensorMap) + @assert [isdual(space(env, ax)) for ax in 1:4] == [0,0,1,1] + # hermitize env, and perform eigen-decomposition + # env = U D U' + D, U = eigh((env + env')/2) + # determine env is (mostly) positive or negative + sgn = sign(mean(vcat((diag(b) for (k,b) in blocks(D))...))) + if sgn == -1 + D *= -1 + end + # set negative eigenvalues to 0 + for (k, b) in blocks(D) + for i in diagind(b) + if b[i] < 0 + b[i] = 0.0 + end + end + end + Zdg = sdiag_pow(D, 1/2) * U' + return sgn, Zdg +end + +""" +Fix local gauge of the env tensor around a bond +""" +function fu_fixgauge( + Zdg::AbstractTensorMap, + X::AbstractTensorMap, Y::AbstractTensorMap, + aR::AbstractTensorMap, bL::AbstractTensorMap +) + #= + 1 1 + ↑ ↑ + 2 → Z† ← 3 = 2 → QR ← 3 1 ← R ← 2 + + 1 + ↑ + = 2 → L → 1 3 → QL ← 2 + =# + QR, R = leftorth(Zdg, ((1,2), (3,)), alg=QRpos()) + QL, L = leftorth(Zdg, ((1,3), (2,)), alg=QRpos()) + @assert !isdual(codomain(R)[1]) && !isdual(domain(R)[1]) + @assert !isdual(codomain(L)[1]) && !isdual(domain(L)[1]) + Rinv, Linv = inv(R), inv(L) + #= fix gauge of aR, bL, Z† + + ↑ + |→-(Linv -→ Z† ← Rinv)←-| + | | + ↑ ↑ + | ↑ ↑ | + |← (L ← aR) ← (bL → R) →| + |-----------------------| + + -2 -2 + ↑ ↑ + -1 ← L ← 1 ← aR2 ← -3 -1 ← bL2 → 1 → R → -3 + + -1 + ↑ + -2 → Linv → 1 → Z† ← 2 ← Rinv ← -3 + =# + aR = ncon([L, aR], [[-1,1], [1,-2,-3]]) + bL = ncon([bL, R], [[-1,-2,1], [-3,1]]) + Zdg = permute( + ncon([Zdg, Linv, Rinv], [[-1,1,2], [1,-2], [2,-3]]), + (1,), (2,3) + ) + #= fix gauge of X, Y + -1 -1 + | | + -4 - X ← 1 ← Linv ← -2 -4 → Rinv → 1 → Y - -2 + | | + -3 -3 + =# + X = ncon([X, Linv], [[-1,1,-3,-4], [1,-2]]) + Y = ncon([Y, Rinv], [[-1,-2,-3,1], [1,-4]]) + return Zdg, X, Y, aR, bL +end diff --git a/src/algorithms/timeevol/fu_optimize.jl b/src/algorithms/timeevol/fu_optimize.jl new file mode 100644 index 000000000..a89f8d3e0 --- /dev/null +++ b/src/algorithms/timeevol/fu_optimize.jl @@ -0,0 +1,325 @@ +""" +Construct the environment (norm) tensor +``` + left half right half + C1 -χ4 - T1 ------- χ6 ------- T1 - χ8 - C2 r-1 + | ‖ ‖ | + χ2 DNX DNY χ10 + | ‖ ‖ | + T4 =DWX= XX = DX = = DY = YY =DEY= T2 r + | ‖ ‖ | + χ1 DSX DSY χ9 + | ‖ ‖ | + C4 -χ3 - T3 ------- χ5 ------- T3 - χ7 - C3 r+1 + c-1 c c+1 c+2 +``` +which can be more simply denoted as +``` + |------------| + |→ DX1 DY1 ←| axis order + |← DX0 DX1 →| (DX1, DY1, DX0, DY0) + |------------| +``` +The axes 1, 2 (or 3, 4) come from X†, Y† (or X, Y) +""" +function tensor_env( + row::Int, col::Int, X::AbstractTensorMap, + Y::AbstractTensorMap, envs::CTMRGEnv +) + Nr, Nc = size(envs.corners)[[2,3]] + cm1 = _prev(col, Nc); + cp1 = _next(col, Nc); cp2 = _next(cp1, Nc) + rm1 = _prev(row, Nr); rp1 = _next(row, Nr) + c1 = envs.corners[1, rm1, cm1] + c2 = envs.corners[2, rm1, cp2] + c3 = envs.corners[3, rp1, cp2] + c4 = envs.corners[4, rp1, cm1] + t1X, t1Y = envs.edges[1, rm1, col], envs.edges[1, rm1, cp1] + t2 = envs.edges[2, row, cp2] + t3X, t3Y = envs.edges[3, rp1, col], envs.edges[3, rp1, cp1] + t4 = envs.edges[4, row, cm1] + # left half + @autoopt @tensor lhalf[DX1, DX0, χ5, χ6] := ( + c4[χ3, χ1] * t4[χ1, DWX0, DWX1, χ2] * c1[χ2, χ4] * + t3X[χ5, DSX0, DSX1, χ3] * X[DNX0, DX0, DSX0, DWX0] * + conj(X[DNX1, DX1, DSX1, DWX1]) * t1X[χ4, DNX0, DNX1, χ6] + ) + # right half + @autoopt @tensor rhalf[DY1, DY0, χ5, χ6] := ( + c3[χ9, χ7] * t2[χ10, DEY0, DEY1, χ9] * c2[χ8, χ10] * + t3Y[χ7, DSY0, DSY1, χ5] * Y[DNY0, DEY0, DSY0, DY0] * + conj(Y[DNY1, DEY1, DSY1, DY1]) * t1Y[χ6, DNY0, DNY1, χ8] + ) + # combine + @autoopt @tensor env[DX1, DY1; DX0, DY0] := ( + lhalf[DX1, DX0, χ5, χ6] * rhalf[DY1, DY0, χ5, χ6] + ) + return env +end + + +""" +Construct the tensor +``` + |------------env------------| + |→ DX1 Db1 → bL† ← DY1 ←| + | ↑ | + | db | + | ↑ | + |← DX0 Db0 ← bL -→ DY0 →| + |---------------------------| +``` +""" +function tensor_Ra(env::AbstractTensorMap, bL::AbstractTensorMap) + @autoopt @tensor Ra[DX1, Db1, DX0, Db0] := ( + env[DX1, DY1, DX0, DY0] * + bL[Db0, db, DY0] * conj(bL[Db1, db, DY1]) + ) + return Ra +end + + +""" +Construct the tensor +``` + |--------------env--------------| + |→ DX1 Db1 → bL† ← DY1 ←| + | ↑ | + | da db | + | ↑ ↑ | + |← DX0 ←- aR2 ←- D ← bL2 → DY0 →| + |-------------------------------| +``` +""" +function tensor_Sa( + env::AbstractTensorMap, aR2::AbstractTensorMap, + bL::AbstractTensorMap, bL2::AbstractTensorMap +) + @autoopt @tensor Sa[DX1, Db1, da] := ( + env[DX1, DY1, DX0, DY0] * conj(bL[Db1, db, DY1]) * + bL2[D, db, DY0] * aR2[DX0, da, D] + ) + return Sa +end + + +""" +Construct the tensor +``` + |------------env------------| + |→ DX1 → aR† → Da1 DY1 ←| + | ↑ | + | da | + | ↑ | + |← DX0 ← aR ←- Da0 DY0 →| + |---------------------------| +``` +""" +function tensor_Rb(env::AbstractTensorMap, aR::AbstractTensorMap) + @autoopt @tensor Rb[Da1, DY1, Da0, DY0] := ( + env[DX1, DY1, DX0, DY0] * + aR[DX0, da, Da0] * conj(aR[DX1, da, Da1]) + ) + return Rb +end + + +""" +Construct the tensor +``` + |--------------env--------------| + |→ DX1 → aR† → Da1 DY1 ←| + | ↑ | + | da db | + | ↑ ↑ | + |← DX0 ← aR2 ← D ←- bL2 -→ DY0 →| + |-------------------------------| +``` +""" +function tensor_Sb( + env::AbstractTensorMap, aR::AbstractTensorMap, + aR2::AbstractTensorMap, bL2::AbstractTensorMap +) + @autoopt @tensor Sb[Da1, DY1, db] := ( + env[DX1, DY1, DX0, DY0] * conj(aR[DX1, da, Da1]) * + aR2[DX0, da, D] * bL2[D, db, DY0] + ) + return Sb +end + + +""" +Calculate the norm +``` + |--------------env--------------| + |→ DX1 → aR1†→ D1 → bL1† ← DY1 ←| + | ↑ ↑ | + | da db | + | ↑ ↑ | + |← DX0 ← aR2 ← D0 ← bL2 → DY0 -→| + |-------------------------------| +``` +""" +function inner_prod( + env::AbstractTensorMap, + aR1::AbstractTensorMap, bL1::AbstractTensorMap, + aR2::AbstractTensorMap, bL2::AbstractTensorMap +) + @autoopt @tensor t[:] := ( + env[DX1, DY1, DX0, DY0] * + conj(aR1[DX1, da, D1]) * conj(bL1[D1, db, DY1]) * + aR2[DX0, da, D0] * bL2[D0, db, DY0] + ) + return first(blocks(t))[2][1] +end + +""" +Calculate the cost function +``` + f(a,b) = | |Psi(a,b)> - |Psi(a2,b2)> |^2 + = + + - 2 Re +``` +""" +function cost_func( + env::AbstractTensorMap, + aR::AbstractTensorMap, bL::AbstractTensorMap, + aR2::AbstractTensorMap, bL2::AbstractTensorMap +) + t1 = inner_prod(env, aR, bL, aR, bL) + t2 = inner_prod(env, aR2, bL2, aR2, bL2) + t3 = inner_prod(env, aR, bL, aR2, bL2) + return real(t1) + real(t2) - 2 * real(t3) +end + + +""" +Calculate the approximate local inner product +`` +``` + |→ aR1† → D1 → bL1† ←| + | ↑ ↑ | + DW da db DE + | ↑ ↑ | + |← aR2 ←- D0 ← bL2 -→| +``` +""" +function inner_prod_local( + aR1::AbstractTensorMap, bL1::AbstractTensorMap, + aR2::AbstractTensorMap, bL2::AbstractTensorMap +) + @autoopt @tensor t[:] := ( + conj(aR1[DW, da, D1]) * conj(bL1[D1, db, DE]) * + aR2[DW, da, D0] * bL2[D0, db, DE] + ) + return first(blocks(t))[2][1] +end + +""" +Calculate the fidelity using aR, bL +between two evolution steps +``` + || + --------------------------------------------- + sqrt( ) +``` +""" +function local_fidelity( + aR1::AbstractTensorMap, bL1::AbstractTensorMap, + aR2::AbstractTensorMap, bL2::AbstractTensorMap +) + b12 = inner_prod_local(aR1, bL1, aR2, bL2) + b11 = inner_prod_local(aR1, bL1, aR1, bL1) + b22 = inner_prod_local(aR2, bL2, aR2, bL2) + return abs(b12) / sqrt(abs(b11*b22)) +end + +""" +Solving the equations +``` + Ra aR = Sa, Rb bL = Sb +``` +""" +function solve_ab( + R::AbstractTensorMap, S::AbstractTensorMap, + ab0::AbstractTensorMap +) + f(x) = ncon((R, x), ([-1,-2,1,2], [1,2,-3])) + ab, info = linsolve(f, S, permute(ab0, (1,3,2)), 0, 1) + return permute(ab, (1,3,2)), info +end + +""" +Minimize the cost function +``` + fix bL: + d(aR,aR†) = aR† Ra aR - aR† Sa - Sa† aR + T + minimized by Ra aR = Sa + + fix aR: + d(bL,bL†) = bL† Rb bL - bL† Sb - Sb† bL + T + minimized by Rb bL = Sb +``` +`aR0`, `bL0` are initial values of `aR`, `bL` +""" +function fu_optimize( + aR0::AbstractTensorMap, bL0::AbstractTensorMap, + aR2::AbstractTensorMap, bL2::AbstractTensorMap, + env::AbstractTensorMap; + maxiter::Int=50, maxdiff::Float64=1e-15, + check_int::Int=1, verbose::Bool=false +) + if verbose + println("---- Iterative optimization ----") + @printf( + "%-6s%12s%12s%12s %10s\n", + "Step", "Cost", "ϵ_d", "ϵ_ab", "Time/s" + ) + end + aR, bL = deepcopy(aR0), deepcopy(bL0) + time0 = time() + cost00 = cost_func(env, aR, bL, aR2, bL2) + fid00 = local_fidelity(aR, bL, aR2, bL2) + cost0, fid0 = cost00, fid00 + # no need to further optimize + if abs(cost0) < 5e-15 + if verbose + time1 = time() + println(@sprintf( + "%-6d%12.3e%12.3e%12.3e %10.3f\n", + 0, cost0, NaN, NaN, time1 - time0 + )) + end + return aR, bL, cost0 + end + for count in 1:maxiter + time0 = time() + Ra = tensor_Ra(env, bL) + Sa = tensor_Sa(env, aR2, bL, bL2) + aR, info_a = solve_ab(Ra, Sa, aR) + Rb = tensor_Rb(env, aR) + Sb = tensor_Sb(env, aR, aR2, bL2) + bL, info_b = solve_ab(Rb, Sb, bL) + cost = cost_func(env, aR, bL, aR2, bL2) + fid = local_fidelity(aR, bL, aR2, bL2) + diff_d = abs(cost - cost0) / cost00 + diff_ab = abs(fid - fid0) / fid00 + time1 = time() + if verbose && (count == 1 || count % check_int == 0) + @printf( + "%-6d%12.3e%12.3e%12.3e %10.3f\n", + count, cost, diff_d, diff_ab, time1 - time0 + ) + end + if diff_ab < maxdiff + break + end + aR0, bL0 = deepcopy(aR), deepcopy(bL) + cost0, fid0 = cost, fid + if count == maxiter + println("Warning: max iter $maxiter reached for optimization") + end + end + return aR, bL, cost0 +end + diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl new file mode 100644 index 000000000..fdede0825 --- /dev/null +++ b/src/algorithms/timeevol/fullupdate.jl @@ -0,0 +1,195 @@ +include("fu_gaugefix.jl") +include("fu_optimize.jl") + +""" +CTMRG left-move to update CTMRGEnv in the c-th column +``` + ---> absorb + C1 ← T1 ← r-1 + ↓ ‖ + T4 = M' = r + ↓ ‖ + C4 → T3 → r+1 + c-1 c +``` +""" +function ctmrg_leftmove!( + col::Int, peps::InfinitePEPS, envs::CTMRGEnv, + chi::Int, svderr::Float64=1e-9 +) + trscheme = truncerr(svderr) & truncdim(chi) + alg = CTMRG( + verbosity=0, miniter=1, maxiter=10, + trscheme=trscheme, ctmrgscheme=:sequential + ) + envs2, info = ctmrg_leftmove(col, peps, envs, alg) + envs.corners[:, :, col] = envs2.corners[:, :, col] + envs.edges[:, :, col] = envs2.edges[:, :, col] + return info +end + +""" +CTMRG right-move to update CTMRGEnv in the c-th column +``` + absorb <--- + ←-- T1 ← C2 r-1 + ‖ ↑ + === M' = T2 r + ‖ ↑ + --→ T3 → C3 r+1 + c c+1 +``` +""" +function ctmrg_rightmove!( + col::Int, peps::InfinitePEPS, envs::CTMRGEnv, + chi::Int, svderr::Float64=1e-9 +) + Nr, Nc = size(peps) + @assert 1 <= col <= Nc + PEPSKit.rot180!(envs) + ctmrg_leftmove!(Nc + 1 - col, rot180(peps), envs, chi, svderr) + PEPSKit.rot180!(envs) + return nothing +end + +""" +Update all horizontal bonds in the c-th column +(i.e. `(r,c) (r,c+1)` for all `r = 1, ..., Nr`). +To update rows, rotate the network clockwise by 90 degrees. +""" +function update_column!( + col::Int, gate::AbstractTensorMap, + peps::InfinitePEPS, envs::CTMRGEnv, + Dcut::Int, chi::Int; + svderr::Float64=1e-9, maxiter::Int=50, + maxdiff::Float64=1e-15, gaugefix::Bool=true, +) + Nr, Nc = size(peps) + @assert 1 <= col <= Nc + localfid = 0.0 + costs = zeros(Nr) + truncscheme = truncerr(svderr) & truncdim(Dcut) + #= Axis order of X, aR, Y, bL + + 1 2 2 1 + | ↗ ↗ | + 4 - X ← 2 1 ← aR ← 3 1 ← bL → 3 4 → Y - 2 + | | + 3 3 + =# + for row in 1:Nr + cp1 = _next(col, Nc) + A, B = peps[row, col], peps[row, cp1] + # TODO: relax dual requirement on the bonds + @assert !isdual(domain(A)[2]) + #= QR and LQ decomposition + + 2 1 1 2 + | ↗ | ↗ + 5 - A ← 3 ====> 4 - X ← 2 1 ← aR ← 3 + | | + 4 3 + =# + X, aR0 = leftorth(A, ((2, 4, 5), (1, 3)); alg=QRpos()) + X = permute(X, (1, 4, 2, 3)) + #= + 2 1 2 2 + | ↗ ↗ | + 5 → B - 3 ====> 1 ← bL → 3 1 → Y - 3 + | | + 4 4 + =# + Y, bL0 = leftorth(B, ((2, 3, 4), (1, 5)); alg=QRpos()) + bL0 = permute(bL0, (3, 2, 1)) + env = tensor_env(row, col, X, Y, envs) + # positive/negative-definite approximant: env = ± Z Z† + sgn, Zdg = positive_approx(env) + # fix gauge + if gaugefix + Zdg, X, Y, aR0, bL0 = fu_fixgauge(Zdg, X, Y, aR0, bL0) + end + env = sgn * Zdg' * Zdg + #= apply gate + + -2 -3 + ↑ ↑ + |----gate---| + ↑ ↑ + 1 2 + ↑ ↑ + -1← aR -← 3 -← bL → -4 + =# + tmp = ncon((gate, aR0, bL0), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) + # initialize truncated tensors using simple SVD truncation + aR2, s, bL2, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncerr(1e-15)) + aR, s_cut, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncscheme) + aR2, bL2 = absorb_s(aR2, s, bL2) + aR, bL = absorb_s(aR, s_cut, bL) + # optimize aR, bL + aR, bL, cost = fu_optimize( + aR, bL, aR2, bL2, env; maxiter=maxiter, maxdiff=maxdiff, verbose=false + ) + costs[row] = cost + aR /= maxabs(aR) + bL /= maxabs(bL) + localfid += local_fidelity(aR, bL, aR0, bL0) + #= update and normalize peps, ms + + -2 -1 -1 -2 + | ↗ ↗ | + -5- X ← 1 ← aR ← -3 -5 ← bL → 1 → Y - -3 + | | + -4 -4 + =# + peps.A[row, col] = permute( + ncon([X, aR], [[-2, 1, -4, -5], [1, -1, -3]]), (1,), Tuple(2:5) + ) + peps.A[row, cp1] = permute( + ncon([bL, Y], [[-5, -1, 1], [-2, -3, -4, 1]]), (1,), Tuple(2:5) + ) + # normalize + for c_ in [col, cp1] + peps.A[row, c_] /= maxabs(peps.A[row, c_]) + end + end + # update CTMRGEnv + ctmrg_leftmove!(col, peps, envs, chi, svderr) + ctmrg_rightmove!(_next(col, Nc), peps, envs, chi, svderr) + return localfid, costs +end + +""" +One round of full update on the input InfinitePEPS `peps` and its CTMRGEnv `envs` + +When `cheap === true`, use half-infinite environment to construct CTMRG projectors. +Otherwise, use full-infinite environment instead. + +Reference: Physical Review B 92, 035142 (2015) +""" +function fullupdate!( + gate::AbstractTensorMap, peps::InfinitePEPS, envs::CTMRGEnv, + Dcut::Int, chi::Int, svderr::Float64=1e-9 +) + Nr, Nc = size(peps) + fid, maxcost = 0.0, 0.0 + for col in 1:Nc + tmpfid, costs = update_column!( + col, gate, peps, envs, Dcut, chi; svderr=svderr + ) + fid += tmpfid + maxcost = max(maxcost, maximum(costs)) + end + rotr90!(peps) + rotr90!(envs) + for row in 1:Nr + tmpfid, costs = update_column!( + row, gate, peps, envs, Dcut, chi; svderr=svderr + ) + fid += tmpfid + maxcost = max(maxcost, maximum(costs)) + end + rotl90!(peps) + rotl90!(envs) + fid /= (2 * Nr * Nc) + return fid, maxcost +end diff --git a/src/algorithms/timeevol/simpleupdate.jl b/src/algorithms/timeevol/simpleupdate.jl index 52400ae95..2e464cde4 100644 --- a/src/algorithms/timeevol/simpleupdate.jl +++ b/src/algorithms/timeevol/simpleupdate.jl @@ -17,6 +17,8 @@ end """ Absorb environment weight on axis `ax` into tensor `t` at position `(row,col)` + +Weights around the tensor at `(row, col)` are ``` ↓ y[r,c] @@ -50,7 +52,6 @@ function absorb_wt( indices_t[ax] = 1 indices_wt = (ax in (2,3) ? [1,-ax] : [-ax,1]) t2 = ncon((t, wt2), (indices_t, indices_wt)) - # restore codomain and domain t2 = permute(t2, (1,), Tuple(2:5)) return t2 end @@ -85,25 +86,24 @@ function _su_bondx!( # absorb bond weight T1 = absorb_wt(T1, row, col, 3, wts; sqrtwt=true) T2 = absorb_wt(T2, row2, col2, 5, wts; sqrtwt=true) - # QR and LQ decomposition - """ + #= QR and LQ decomposition + 2 1 1 2 ↓ ↗ ↓ ↗ 5 ← T ← 3 ====> 3 ← X ← 4 ← 1 ← aR ← 3 ↓ ↓ 4 2 - """ - X, aR = leftorth(T1, ((2,4,5), (1,3)), alg=QRpos()) - """ + 2 1 2 2 ↓ ↗ ↗ ↓ 5 ← T ← 3 ====> 1 ← bL ← 3 ← 1 ← Y ← 3 ↓ ↓ 4 4 - """ + =# + X, aR = leftorth(T1, ((2,4,5), (1,3)), alg=QRpos()) bL, Y = rightorth(T2, ((5,1), (2,3,4)), alg=LQpos()) - # apply gate - """ + #= apply gate + -2 -3 ↑ ↑ |----gate---| @@ -111,18 +111,18 @@ function _su_bondx!( 1 2 ↑ ↑ -1← aR -← 3 -← bL ← -4 - """ + =# tmp = ncon((gate, aR, bL), ([-2,-3,1,2], [-1,1,3], [3,2,-4])) # SVD truncscheme = truncerr(svderr) & truncdim(Dcut) aR, s, bL, ϵ = tsvd(tmp, ((1,2), (3,4)); trunc=truncscheme) - """ + #= -2 -1 -1 -2 | ↗ ↗ | -5- X ← 1 ← aR - -3 -5 - bL ← 1 ← Y - -3 | | -4 -4 - """ + =# T1 = ncon((X, aR), ([-2,-4,-5,1], [1,-1,-3])) T2 = ncon((bL, Y), ([-5,-1,1], [1,-2,-3,-4])) # remove environment weights @@ -132,7 +132,8 @@ function _su_bondx!( for ax in (2,3,4) T2 = absorb_wt(T2, row2, col2, ax, wts; invwt=true) end - # update tensor dict and weight on current bond (with normalization) + # update tensor dict and weight on current bond + # (max element of weight is normalized to 1) peps.A[row,col], peps.A[row2,col2] = T1, T2 wts.x[row,col] = s / maxabs(s) return ϵ diff --git a/src/environments/ctmrg_environments.jl b/src/environments/ctmrg_environments.jl index 460faf5ba..65cd05c15 100644 --- a/src/environments/ctmrg_environments.jl +++ b/src/environments/ctmrg_environments.jl @@ -371,6 +371,44 @@ function Base.rotl90(env::CTMRGEnv{C,T}) where {C,T} return CTMRGEnv(copy(corners′), copy(edges′)) end +# in-place rotations (incompatible with autodiff) +""" +Rotate the CTMRGEnv `envs` left 90 degrees (anti-clockwise) in place +""" +function rotl90!(envs::CTMRGEnv) + envs2 = deepcopy(envs) + for dir in 1:4 + dir2 = _prev(dir, 4) + envs.corners[dir2, :, :] = rotl90(envs2.corners[dir, :, :]) + envs.edges[dir2, :, :] = rotl90(envs2.edges[dir, :, :]) + end + return nothing +end +""" +Rotate the CTMRGEnv `envs` right 90 degrees (clockwise) in place +""" +function rotr90!(envs::CTMRGEnv) + envs2 = deepcopy(envs) + for dir in 1:4 + dir2 = _next(dir, 4) + envs.corners[dir2, :, :] = rotr90(envs2.corners[dir, :, :]) + envs.edges[dir2, :, :] = rotr90(envs2.edges[dir, :, :]) + end + return nothing +end +""" +Rotate the CTMRGEnv `envs` 180 degrees in place +""" +function rot180!(envs::CTMRGEnv) + envs2 = deepcopy(envs) + for dir in 1:4 + dir2 = _next(_next(dir, 4), 4) + envs.corners[dir2, :, :] = rot180(envs2.corners[dir, :, :]) + envs.edges[dir2, :, :] = rot180(envs2.edges[dir, :, :]) + end + return nothing +end + Base.eltype(env::CTMRGEnv) = eltype(env.corners[1]) Base.axes(x::CTMRGEnv, args...) = axes(x.corners, args...) function eachcoordinate(x::CTMRGEnv) diff --git a/src/states/infinitepeps.jl b/src/states/infinitepeps.jl index 51b353d49..827244e92 100644 --- a/src/states/infinitepeps.jl +++ b/src/states/infinitepeps.jl @@ -172,6 +172,18 @@ Base.rotl90(t::InfinitePEPS) = InfinitePEPS(rotl90(rotl90.(t.A))) Base.rotr90(t::InfinitePEPS) = InfinitePEPS(rotr90(rotr90.(t.A))) Base.rot180(t::InfinitePEPS) = InfinitePEPS(rot180(rot180.(t.A))) +# In-place rotations +function rotl90!(peps::InfinitePEPS) + peps.A[:] = rotl90(rotl90.(peps.A)) +end +function rotr90!(peps::InfinitePEPS) + peps.A[:] = rotr90(rotr90.(peps.A)) +end +function rot180!(peps::InfinitePEPS) + peps.A[:] = rot180(rot180.(peps.A)) +end + + # Chainrules function ChainRulesCore.rrule( ::typeof(Base.getindex), state::InfinitePEPS, row::Int, col::Int From 0119c5559f8c0c8367164ee02e753016b0b1bbaa Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 7 Nov 2024 09:06:16 +0800 Subject: [PATCH 04/75] improve formatting --- src/algorithms/ctmrg/ctmrg.jl | 8 +- src/algorithms/timeevol/fu_gaugefix.jl | 33 +++--- src/algorithms/timeevol/fu_optimize.jl | 148 +++++++++++++----------- src/algorithms/timeevol/fullupdate.jl | 46 ++++---- src/algorithms/timeevol/simpleupdate.jl | 93 ++++++++------- src/states/infinitepeps.jl | 4 +- src/states/suweight.jl | 8 +- src/utility/mirror.jl | 5 +- src/utility/svd.jl | 1 - src/utility/util.jl | 2 +- 10 files changed, 185 insertions(+), 163 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 79fb5317e..1ef2f5c1f 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -136,9 +136,7 @@ end """ Perform CTMRG left move on the `col`-th column """ -function ctmrg_leftmove( - col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG -) +function ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) """ ----> left move C1 ← T1 ← r-1 @@ -148,9 +146,7 @@ function ctmrg_leftmove( C4 → T3 → r+1 c-1 c """ - enlarged_envs = ctmrg_expand( - eachcoordinate(envs, [4, 1])[:, :, col], state, envs - ) + enlarged_envs = ctmrg_expand(eachcoordinate(envs, [4, 1])[:, :, col], state, envs) projectors, info = ctmrg_projectors(col, enlarged_envs, envs, alg) envs = ctmrg_renormalize(col, projectors, state, envs, alg) return envs, info diff --git a/src/algorithms/timeevol/fu_gaugefix.jl b/src/algorithms/timeevol/fu_gaugefix.jl index 760d781d1..3ee68069f 100644 --- a/src/algorithms/timeevol/fu_gaugefix.jl +++ b/src/algorithms/timeevol/fu_gaugefix.jl @@ -12,12 +12,12 @@ Replace `env` by its positive/negative approximant `± Z Z†` ``` """ function positive_approx(env::AbstractTensorMap) - @assert [isdual(space(env, ax)) for ax in 1:4] == [0,0,1,1] + @assert [isdual(space(env, ax)) for ax in 1:4] == [0, 0, 1, 1] # hermitize env, and perform eigen-decomposition # env = U D U' - D, U = eigh((env + env')/2) + D, U = eigh((env + env') / 2) # determine env is (mostly) positive or negative - sgn = sign(mean(vcat((diag(b) for (k,b) in blocks(D))...))) + sgn = sign(mean(vcat((diag(b) for (k, b) in blocks(D))...))) if sgn == -1 D *= -1 end @@ -29,7 +29,7 @@ function positive_approx(env::AbstractTensorMap) end end end - Zdg = sdiag_pow(D, 1/2) * U' + Zdg = sdiag_pow(D, 1 / 2) * U' return sgn, Zdg end @@ -37,9 +37,11 @@ end Fix local gauge of the env tensor around a bond """ function fu_fixgauge( - Zdg::AbstractTensorMap, - X::AbstractTensorMap, Y::AbstractTensorMap, - aR::AbstractTensorMap, bL::AbstractTensorMap + Zdg::AbstractTensorMap, + X::AbstractTensorMap, + Y::AbstractTensorMap, + aR::AbstractTensorMap, + bL::AbstractTensorMap, ) #= 1 1 @@ -50,8 +52,8 @@ function fu_fixgauge( ↑ = 2 → L → 1 3 → QL ← 2 =# - QR, R = leftorth(Zdg, ((1,2), (3,)), alg=QRpos()) - QL, L = leftorth(Zdg, ((1,3), (2,)), alg=QRpos()) + QR, R = leftorth(Zdg, ((1, 2), (3,)); alg=QRpos()) + QL, L = leftorth(Zdg, ((1, 3), (2,)); alg=QRpos()) @assert !isdual(codomain(R)[1]) && !isdual(domain(R)[1]) @assert !isdual(codomain(L)[1]) && !isdual(domain(L)[1]) Rinv, Linv = inv(R), inv(L) @@ -73,12 +75,9 @@ function fu_fixgauge( ↑ -2 → Linv → 1 → Z† ← 2 ← Rinv ← -3 =# - aR = ncon([L, aR], [[-1,1], [1,-2,-3]]) - bL = ncon([bL, R], [[-1,-2,1], [-3,1]]) - Zdg = permute( - ncon([Zdg, Linv, Rinv], [[-1,1,2], [1,-2], [2,-3]]), - (1,), (2,3) - ) + aR = ncon([L, aR], [[-1, 1], [1, -2, -3]]) + bL = ncon([bL, R], [[-1, -2, 1], [-3, 1]]) + Zdg = permute(ncon([Zdg, Linv, Rinv], [[-1, 1, 2], [1, -2], [2, -3]]), (1,), (2, 3)) #= fix gauge of X, Y -1 -1 | | @@ -86,7 +85,7 @@ function fu_fixgauge( | | -3 -3 =# - X = ncon([X, Linv], [[-1,1,-3,-4], [1,-2]]) - Y = ncon([Y, Rinv], [[-1,-2,-3,1], [1,-4]]) + X = ncon([X, Linv], [[-1, 1, -3, -4], [1, -2]]) + Y = ncon([Y, Rinv], [[-1, -2, -3, 1], [1, -4]]) return Zdg, X, Y, aR, bL end diff --git a/src/algorithms/timeevol/fu_optimize.jl b/src/algorithms/timeevol/fu_optimize.jl index a89f8d3e0..e90d4d1af 100644 --- a/src/algorithms/timeevol/fu_optimize.jl +++ b/src/algorithms/timeevol/fu_optimize.jl @@ -23,13 +23,14 @@ which can be more simply denoted as The axes 1, 2 (or 3, 4) come from X†, Y† (or X, Y) """ function tensor_env( - row::Int, col::Int, X::AbstractTensorMap, - Y::AbstractTensorMap, envs::CTMRGEnv + row::Int, col::Int, X::AbstractTensorMap, Y::AbstractTensorMap, envs::CTMRGEnv ) - Nr, Nc = size(envs.corners)[[2,3]] - cm1 = _prev(col, Nc); - cp1 = _next(col, Nc); cp2 = _next(cp1, Nc) - rm1 = _prev(row, Nr); rp1 = _next(row, Nr) + Nr, Nc = size(envs.corners)[[2, 3]] + cm1 = _prev(col, Nc) + cp1 = _next(col, Nc) + cp2 = _next(cp1, Nc) + rm1 = _prev(row, Nr) + rp1 = _next(row, Nr) c1 = envs.corners[1, rm1, cm1] c2 = envs.corners[2, rm1, cp2] c3 = envs.corners[3, rp1, cp2] @@ -40,15 +41,23 @@ function tensor_env( t4 = envs.edges[4, row, cm1] # left half @autoopt @tensor lhalf[DX1, DX0, χ5, χ6] := ( - c4[χ3, χ1] * t4[χ1, DWX0, DWX1, χ2] * c1[χ2, χ4] * - t3X[χ5, DSX0, DSX1, χ3] * X[DNX0, DX0, DSX0, DWX0] * - conj(X[DNX1, DX1, DSX1, DWX1]) * t1X[χ4, DNX0, DNX1, χ6] + c4[χ3, χ1] * + t4[χ1, DWX0, DWX1, χ2] * + c1[χ2, χ4] * + t3X[χ5, DSX0, DSX1, χ3] * + X[DNX0, DX0, DSX0, DWX0] * + conj(X[DNX1, DX1, DSX1, DWX1]) * + t1X[χ4, DNX0, DNX1, χ6] ) # right half @autoopt @tensor rhalf[DY1, DY0, χ5, χ6] := ( - c3[χ9, χ7] * t2[χ10, DEY0, DEY1, χ9] * c2[χ8, χ10] * - t3Y[χ7, DSY0, DSY1, χ5] * Y[DNY0, DEY0, DSY0, DY0] * - conj(Y[DNY1, DEY1, DSY1, DY1]) * t1Y[χ6, DNY0, DNY1, χ8] + c3[χ9, χ7] * + t2[χ10, DEY0, DEY1, χ9] * + c2[χ8, χ10] * + t3Y[χ7, DSY0, DSY1, χ5] * + Y[DNY0, DEY0, DSY0, DY0] * + conj(Y[DNY1, DEY1, DSY1, DY1]) * + t1Y[χ6, DNY0, DNY1, χ8] ) # combine @autoopt @tensor env[DX1, DY1; DX0, DY0] := ( @@ -57,7 +66,6 @@ function tensor_env( return env end - """ Construct the tensor ``` @@ -72,13 +80,11 @@ Construct the tensor """ function tensor_Ra(env::AbstractTensorMap, bL::AbstractTensorMap) @autoopt @tensor Ra[DX1, Db1, DX0, Db0] := ( - env[DX1, DY1, DX0, DY0] * - bL[Db0, db, DY0] * conj(bL[Db1, db, DY1]) + env[DX1, DY1, DX0, DY0] * bL[Db0, db, DY0] * conj(bL[Db1, db, DY1]) ) return Ra end - """ Construct the tensor ``` @@ -92,17 +98,17 @@ Construct the tensor ``` """ function tensor_Sa( - env::AbstractTensorMap, aR2::AbstractTensorMap, - bL::AbstractTensorMap, bL2::AbstractTensorMap + env::AbstractTensorMap, + aR2::AbstractTensorMap, + bL::AbstractTensorMap, + bL2::AbstractTensorMap, ) @autoopt @tensor Sa[DX1, Db1, da] := ( - env[DX1, DY1, DX0, DY0] * conj(bL[Db1, db, DY1]) * - bL2[D, db, DY0] * aR2[DX0, da, D] + env[DX1, DY1, DX0, DY0] * conj(bL[Db1, db, DY1]) * bL2[D, db, DY0] * aR2[DX0, da, D] ) return Sa end - """ Construct the tensor ``` @@ -117,13 +123,11 @@ Construct the tensor """ function tensor_Rb(env::AbstractTensorMap, aR::AbstractTensorMap) @autoopt @tensor Rb[Da1, DY1, Da0, DY0] := ( - env[DX1, DY1, DX0, DY0] * - aR[DX0, da, Da0] * conj(aR[DX1, da, Da1]) + env[DX1, DY1, DX0, DY0] * aR[DX0, da, Da0] * conj(aR[DX1, da, Da1]) ) return Rb end - """ Construct the tensor ``` @@ -137,17 +141,17 @@ Construct the tensor ``` """ function tensor_Sb( - env::AbstractTensorMap, aR::AbstractTensorMap, - aR2::AbstractTensorMap, bL2::AbstractTensorMap + env::AbstractTensorMap, + aR::AbstractTensorMap, + aR2::AbstractTensorMap, + bL2::AbstractTensorMap, ) @autoopt @tensor Sb[Da1, DY1, db] := ( - env[DX1, DY1, DX0, DY0] * conj(aR[DX1, da, Da1]) * - aR2[DX0, da, D] * bL2[D, db, DY0] + env[DX1, DY1, DX0, DY0] * conj(aR[DX1, da, Da1]) * aR2[DX0, da, D] * bL2[D, db, DY0] ) return Sb end - """ Calculate the norm ``` @@ -161,14 +165,18 @@ Calculate the norm ``` """ function inner_prod( - env::AbstractTensorMap, - aR1::AbstractTensorMap, bL1::AbstractTensorMap, - aR2::AbstractTensorMap, bL2::AbstractTensorMap + env::AbstractTensorMap, + aR1::AbstractTensorMap, + bL1::AbstractTensorMap, + aR2::AbstractTensorMap, + bL2::AbstractTensorMap, ) @autoopt @tensor t[:] := ( env[DX1, DY1, DX0, DY0] * - conj(aR1[DX1, da, D1]) * conj(bL1[D1, db, DY1]) * - aR2[DX0, da, D0] * bL2[D0, db, DY0] + conj(aR1[DX1, da, D1]) * + conj(bL1[D1, db, DY1]) * + aR2[DX0, da, D0] * + bL2[D0, db, DY0] ) return first(blocks(t))[2][1] end @@ -182,9 +190,11 @@ Calculate the cost function ``` """ function cost_func( - env::AbstractTensorMap, - aR::AbstractTensorMap, bL::AbstractTensorMap, - aR2::AbstractTensorMap, bL2::AbstractTensorMap + env::AbstractTensorMap, + aR::AbstractTensorMap, + bL::AbstractTensorMap, + aR2::AbstractTensorMap, + bL2::AbstractTensorMap, ) t1 = inner_prod(env, aR, bL, aR, bL) t2 = inner_prod(env, aR2, bL2, aR2, bL2) @@ -192,7 +202,6 @@ function cost_func( return real(t1) + real(t2) - 2 * real(t3) end - """ Calculate the approximate local inner product `` @@ -205,12 +214,13 @@ Calculate the approximate local inner product ``` """ function inner_prod_local( - aR1::AbstractTensorMap, bL1::AbstractTensorMap, - aR2::AbstractTensorMap, bL2::AbstractTensorMap + aR1::AbstractTensorMap, + bL1::AbstractTensorMap, + aR2::AbstractTensorMap, + bL2::AbstractTensorMap, ) @autoopt @tensor t[:] := ( - conj(aR1[DW, da, D1]) * conj(bL1[D1, db, DE]) * - aR2[DW, da, D0] * bL2[D0, db, DE] + conj(aR1[DW, da, D1]) * conj(bL1[D1, db, DE]) * aR2[DW, da, D0] * bL2[D0, db, DE] ) return first(blocks(t))[2][1] end @@ -225,13 +235,15 @@ between two evolution steps ``` """ function local_fidelity( - aR1::AbstractTensorMap, bL1::AbstractTensorMap, - aR2::AbstractTensorMap, bL2::AbstractTensorMap + aR1::AbstractTensorMap, + bL1::AbstractTensorMap, + aR2::AbstractTensorMap, + bL2::AbstractTensorMap, ) b12 = inner_prod_local(aR1, bL1, aR2, bL2) b11 = inner_prod_local(aR1, bL1, aR1, bL1) b22 = inner_prod_local(aR2, bL2, aR2, bL2) - return abs(b12) / sqrt(abs(b11*b22)) + return abs(b12) / sqrt(abs(b11 * b22)) end """ @@ -240,13 +252,10 @@ Solving the equations Ra aR = Sa, Rb bL = Sb ``` """ -function solve_ab( - R::AbstractTensorMap, S::AbstractTensorMap, - ab0::AbstractTensorMap -) - f(x) = ncon((R, x), ([-1,-2,1,2], [1,2,-3])) - ab, info = linsolve(f, S, permute(ab0, (1,3,2)), 0, 1) - return permute(ab, (1,3,2)), info +function solve_ab(R::AbstractTensorMap, S::AbstractTensorMap, ab0::AbstractTensorMap) + f(x) = ncon((R, x), ([-1, -2, 1, 2], [1, 2, -3])) + ab, info = linsolve(f, S, permute(ab0, (1, 3, 2)), 0, 1) + return permute(ab, (1, 3, 2)), info end """ @@ -263,18 +272,19 @@ Minimize the cost function `aR0`, `bL0` are initial values of `aR`, `bL` """ function fu_optimize( - aR0::AbstractTensorMap, bL0::AbstractTensorMap, - aR2::AbstractTensorMap, bL2::AbstractTensorMap, + aR0::AbstractTensorMap, + bL0::AbstractTensorMap, + aR2::AbstractTensorMap, + bL2::AbstractTensorMap, env::AbstractTensorMap; - maxiter::Int=50, maxdiff::Float64=1e-15, - check_int::Int=1, verbose::Bool=false + maxiter::Int=50, + maxdiff::Float64=1e-15, + check_int::Int=1, + verbose::Bool=false, ) if verbose println("---- Iterative optimization ----") - @printf( - "%-6s%12s%12s%12s %10s\n", - "Step", "Cost", "ϵ_d", "ϵ_ab", "Time/s" - ) + @printf("%-6s%12s%12s%12s %10s\n", "Step", "Cost", "ϵ_d", "ϵ_ab", "Time/s") end aR, bL = deepcopy(aR0), deepcopy(bL0) time0 = time() @@ -285,10 +295,11 @@ function fu_optimize( if abs(cost0) < 5e-15 if verbose time1 = time() - println(@sprintf( - "%-6d%12.3e%12.3e%12.3e %10.3f\n", - 0, cost0, NaN, NaN, time1 - time0 - )) + println( + @sprintf( + "%-6d%12.3e%12.3e%12.3e %10.3f\n", 0, cost0, NaN, NaN, time1 - time0 + ) + ) end return aR, bL, cost0 end @@ -307,8 +318,12 @@ function fu_optimize( time1 = time() if verbose && (count == 1 || count % check_int == 0) @printf( - "%-6d%12.3e%12.3e%12.3e %10.3f\n", - count, cost, diff_d, diff_ab, time1 - time0 + "%-6d%12.3e%12.3e%12.3e %10.3f\n", + count, + cost, + diff_d, + diff_ab, + time1 - time0 ) end if diff_ab < maxdiff @@ -322,4 +337,3 @@ function fu_optimize( end return aR, bL, cost0 end - diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl index fdede0825..e0a16efda 100644 --- a/src/algorithms/timeevol/fullupdate.jl +++ b/src/algorithms/timeevol/fullupdate.jl @@ -14,13 +14,11 @@ CTMRG left-move to update CTMRGEnv in the c-th column ``` """ function ctmrg_leftmove!( - col::Int, peps::InfinitePEPS, envs::CTMRGEnv, - chi::Int, svderr::Float64=1e-9 + col::Int, peps::InfinitePEPS, envs::CTMRGEnv, chi::Int, svderr::Float64=1e-9 ) trscheme = truncerr(svderr) & truncdim(chi) - alg = CTMRG( - verbosity=0, miniter=1, maxiter=10, - trscheme=trscheme, ctmrgscheme=:sequential + alg = CTMRG(; + verbosity=0, miniter=1, maxiter=10, trscheme=trscheme, ctmrgscheme=:sequential ) envs2, info = ctmrg_leftmove(col, peps, envs, alg) envs.corners[:, :, col] = envs2.corners[:, :, col] @@ -41,8 +39,7 @@ CTMRG right-move to update CTMRGEnv in the c-th column ``` """ function ctmrg_rightmove!( - col::Int, peps::InfinitePEPS, envs::CTMRGEnv, - chi::Int, svderr::Float64=1e-9 + col::Int, peps::InfinitePEPS, envs::CTMRGEnv, chi::Int, svderr::Float64=1e-9 ) Nr, Nc = size(peps) @assert 1 <= col <= Nc @@ -58,11 +55,16 @@ Update all horizontal bonds in the c-th column To update rows, rotate the network clockwise by 90 degrees. """ function update_column!( - col::Int, gate::AbstractTensorMap, - peps::InfinitePEPS, envs::CTMRGEnv, - Dcut::Int, chi::Int; - svderr::Float64=1e-9, maxiter::Int=50, - maxdiff::Float64=1e-15, gaugefix::Bool=true, + col::Int, + gate::AbstractTensorMap, + peps::InfinitePEPS, + envs::CTMRGEnv, + Dcut::Int, + chi::Int; + svderr::Float64=1e-9, + maxiter::Int=50, + maxdiff::Float64=1e-15, + gaugefix::Bool=true, ) Nr, Nc = size(peps) @assert 1 <= col <= Nc @@ -110,7 +112,7 @@ function update_column!( end env = sgn * Zdg' * Zdg #= apply gate - + -2 -3 ↑ ↑ |----gate---| @@ -134,7 +136,7 @@ function update_column!( bL /= maxabs(bL) localfid += local_fidelity(aR, bL, aR0, bL0) #= update and normalize peps, ms - + -2 -1 -1 -2 | ↗ ↗ | -5- X ← 1 ← aR ← -3 -5 ← bL → 1 → Y - -3 @@ -167,24 +169,24 @@ Otherwise, use full-infinite environment instead. Reference: Physical Review B 92, 035142 (2015) """ function fullupdate!( - gate::AbstractTensorMap, peps::InfinitePEPS, envs::CTMRGEnv, - Dcut::Int, chi::Int, svderr::Float64=1e-9 + gate::AbstractTensorMap, + peps::InfinitePEPS, + envs::CTMRGEnv, + Dcut::Int, + chi::Int, + svderr::Float64=1e-9, ) Nr, Nc = size(peps) fid, maxcost = 0.0, 0.0 for col in 1:Nc - tmpfid, costs = update_column!( - col, gate, peps, envs, Dcut, chi; svderr=svderr - ) + tmpfid, costs = update_column!(col, gate, peps, envs, Dcut, chi; svderr=svderr) fid += tmpfid maxcost = max(maxcost, maximum(costs)) end rotr90!(peps) rotr90!(envs) for row in 1:Nr - tmpfid, costs = update_column!( - row, gate, peps, envs, Dcut, chi; svderr=svderr - ) + tmpfid, costs = update_column!(row, gate, peps, envs, Dcut, chi; svderr=svderr) fid += tmpfid maxcost = max(maxcost, maximum(costs)) end diff --git a/src/algorithms/timeevol/simpleupdate.jl b/src/algorithms/timeevol/simpleupdate.jl index 2e464cde4..a57795bea 100644 --- a/src/algorithms/timeevol/simpleupdate.jl +++ b/src/algorithms/timeevol/simpleupdate.jl @@ -4,7 +4,7 @@ Mirror the unit cell of an iPEPS by its anti-diagonal line function mirror_antidiag!(peps::InfinitePEPS) peps.A[:] = mirror_antidiag(peps.A) for (i, t) in enumerate(peps.A) - peps.A[i] = permute(t, (1,), (3,2,5,4)) + peps.A[i] = permute(t, (1,), (3, 2, 5, 4)) end end @@ -12,7 +12,7 @@ end Mirror the unit cell of an iPEPS with weights by its anti-diagonal line """ function mirror_antidiag!(wts::SUWeight) - wts.x[:], wts.y[:] = mirror_antidiag(wts.y), mirror_antidiag(wts.x) + return wts.x[:], wts.y[:] = mirror_antidiag(wts.y), mirror_antidiag(wts.x) end """ @@ -30,33 +30,36 @@ Weights around the tensor at `(row, col)` are ``` """ function absorb_wt( - t::AbstractTensorMap, row::Int, col::Int, - ax::Int, wts::SUWeight; - sqrtwt::Bool=false, invwt::Bool=false + t::AbstractTensorMap, + row::Int, + col::Int, + ax::Int, + wts::SUWeight; + sqrtwt::Bool=false, + invwt::Bool=false, ) Nr, Nc = size(wts) @assert 1 <= row <= Nr && 1 <= col <= Nc @assert 2 <= ax <= 5 - pow = (sqrtwt ? 1/2 : 1) * (invwt ? -1 : 1) + pow = (sqrtwt ? 1 / 2 : 1) * (invwt ? -1 : 1) if ax == 2 # north wt = wts.y[row, col] elseif ax == 3 # east wt = wts.x[row, col] elseif ax == 4 # south - wt = wts.y[_next(row,Nr), col] + wt = wts.y[_next(row, Nr), col] else # west - wt = wts.x[row, _prev(col,Nc)] + wt = wts.x[row, _prev(col, Nc)] end wt2 = sdiag_pow(wt, pow) indices_t = collect(-1:-1:-5) indices_t[ax] = 1 - indices_wt = (ax in (2,3) ? [1,-ax] : [-ax,1]) + indices_wt = (ax in (2, 3) ? [1, -ax] : [-ax, 1]) t2 = ncon((t, wt2), (indices_t, indices_wt)) t2 = permute(t2, (1,), Tuple(2:5)) return t2 end - """ Simple update of bond `wts.x[r,c]` ``` @@ -68,26 +71,30 @@ Simple update of bond `wts.x[r,c]` ``` """ function _su_bondx!( - row::Int, col::Int, gate::AbstractTensorMap, - peps::InfinitePEPS, wts::SUWeight, - Dcut::Int, svderr::Float64=1e-10 + row::Int, + col::Int, + gate::AbstractTensorMap, + peps::InfinitePEPS, + wts::SUWeight, + Dcut::Int, + svderr::Float64=1e-10, ) Nr, Nc = size(peps) @assert 1 <= row <= Nr && 1 <= col <= Nc - row2, col2 = row, _next(col,Nc) - T1, T2 = peps[row,col], peps[row2,col2] + row2, col2 = row, _next(col, Nc) + T1, T2 = peps[row, col], peps[row2, col2] # absorb environment weights - for ax in (2,4,5) + for ax in (2, 4, 5) T1 = absorb_wt(T1, row, col, ax, wts) end - for ax in (2,3,4) + for ax in (2, 3, 4) T2 = absorb_wt(T2, row2, col2, ax, wts) end # absorb bond weight T1 = absorb_wt(T1, row, col, 3, wts; sqrtwt=true) T2 = absorb_wt(T2, row2, col2, 5, wts; sqrtwt=true) #= QR and LQ decomposition - + 2 1 1 2 ↓ ↗ ↓ ↗ 5 ← T ← 3 ====> 3 ← X ← 4 ← 1 ← aR ← 3 @@ -100,10 +107,10 @@ function _su_bondx!( ↓ ↓ 4 4 =# - X, aR = leftorth(T1, ((2,4,5), (1,3)), alg=QRpos()) - bL, Y = rightorth(T2, ((5,1), (2,3,4)), alg=LQpos()) + X, aR = leftorth(T1, ((2, 4, 5), (1, 3)); alg=QRpos()) + bL, Y = rightorth(T2, ((5, 1), (2, 3, 4)); alg=LQpos()) #= apply gate - + -2 -3 ↑ ↑ |----gate---| @@ -112,10 +119,10 @@ function _su_bondx!( ↑ ↑ -1← aR -← 3 -← bL ← -4 =# - tmp = ncon((gate, aR, bL), ([-2,-3,1,2], [-1,1,3], [3,2,-4])) + tmp = ncon((gate, aR, bL), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) # SVD truncscheme = truncerr(svderr) & truncdim(Dcut) - aR, s, bL, ϵ = tsvd(tmp, ((1,2), (3,4)); trunc=truncscheme) + aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncscheme) #= -2 -1 -1 -2 | ↗ ↗ | @@ -123,23 +130,22 @@ function _su_bondx!( | | -4 -4 =# - T1 = ncon((X, aR), ([-2,-4,-5,1], [1,-1,-3])) - T2 = ncon((bL, Y), ([-5,-1,1], [1,-2,-3,-4])) + T1 = ncon((X, aR), ([-2, -4, -5, 1], [1, -1, -3])) + T2 = ncon((bL, Y), ([-5, -1, 1], [1, -2, -3, -4])) # remove environment weights - for ax in (2,4,5) + for ax in (2, 4, 5) T1 = absorb_wt(T1, row, col, ax, wts; invwt=true) end - for ax in (2,3,4) + for ax in (2, 3, 4) T2 = absorb_wt(T2, row2, col2, ax, wts; invwt=true) end # update tensor dict and weight on current bond # (max element of weight is normalized to 1) - peps.A[row,col], peps.A[row2,col2] = T1, T2 - wts.x[row,col] = s / maxabs(s) + peps.A[row, col], peps.A[row2, col2] = T1, T2 + wts.x[row, col] = s / maxabs(s) return ϵ end - """ One round of simple update on the input InfinitePEPS `peps` and SUWeight `wts` with the nearest neighbor gate `gate` @@ -148,8 +154,12 @@ When `bipartite === true` (for square lattice), the unit cell size should be 2 x and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` """ function simpleupdate!( - gate::AbstractTensorMap, peps::InfinitePEPS, wts::SUWeight, - Dcut::Int, svderr::Float64=1e-10; bipartite::Bool=false, + gate::AbstractTensorMap, + peps::InfinitePEPS, + wts::SUWeight, + Dcut::Int, + svderr::Float64=1e-10; + bipartite::Bool=false, ) Nr, Nc = size(peps) if bipartite @@ -157,21 +167,24 @@ function simpleupdate!( end # TODO: make algorithm independent on the choice of dual in the network for (r, c) in Iterators.product(1:Nr, 1:Nc) - @assert [isdual(space(peps.A[r, c], ax)) for ax in 1:5] == [0,1,1,0,0] - @assert [isdual(space(wts.x[r, c], ax)) for ax in 1:2] == [0,1] - @assert [isdual(space(wts.y[r, c], ax)) for ax in 1:2] == [0,1] + @assert [isdual(space(peps.A[r, c], ax)) for ax in 1:5] == [0, 1, 1, 0, 0] + @assert [isdual(space(wts.x[r, c], ax)) for ax in 1:2] == [0, 1] + @assert [isdual(space(wts.y[r, c], ax)) for ax in 1:2] == [0, 1] end for direction in 1:2 # mirror the y-weights to x-direction # to update them using code for x-weights if direction == 2 - mirror_antidiag!(peps); mirror_antidiag!(wts) + mirror_antidiag!(peps) + mirror_antidiag!(wts) end if bipartite ϵ = _su_bondx!(1, 1, gate, peps, wts, Dcut, svderr) - (peps.A[2,2], peps.A[2,1], wts.x[2,2]) = deepcopy.((peps.A[1,1], peps.A[1,2], wts.x[1,1])) + (peps.A[2, 2], peps.A[2, 1], wts.x[2, 2]) = + deepcopy.((peps.A[1, 1], peps.A[1, 2], wts.x[1, 1])) ϵ = _su_bondx!(2, 1, gate, peps, wts, Dcut, svderr) - (peps.A[1,2], peps.A[1,1], wts.x[1,2]) = deepcopy.((peps.A[2,1], peps.A[2,2], wts.x[2,1])) + (peps.A[1, 2], peps.A[1, 1], wts.x[1, 2]) = + deepcopy.((peps.A[2, 1], peps.A[2, 2], wts.x[2, 1])) else for site in CartesianIndices(peps.A) row, col = Tuple(site) @@ -179,9 +192,9 @@ function simpleupdate!( end end if direction == 2 - mirror_antidiag!(peps); mirror_antidiag!(wts) + mirror_antidiag!(peps) + mirror_antidiag!(wts) end end return nothing end - diff --git a/src/states/infinitepeps.jl b/src/states/infinitepeps.jl index 827244e92..423c6f08f 100644 --- a/src/states/infinitepeps.jl +++ b/src/states/infinitepeps.jl @@ -175,15 +175,17 @@ Base.rot180(t::InfinitePEPS) = InfinitePEPS(rot180(rot180.(t.A))) # In-place rotations function rotl90!(peps::InfinitePEPS) peps.A[:] = rotl90(rotl90.(peps.A)) + return nothing end function rotr90!(peps::InfinitePEPS) peps.A[:] = rotr90(rotr90.(peps.A)) + return nothing end function rot180!(peps::InfinitePEPS) peps.A[:] = rot180(rot180.(peps.A)) + return nothing end - # Chainrules function ChainRulesCore.rrule( ::typeof(Base.getindex), state::InfinitePEPS, row::Int, col::Int diff --git a/src/states/suweight.jl b/src/states/suweight.jl index 97b758322..19421d333 100644 --- a/src/states/suweight.jl +++ b/src/states/suweight.jl @@ -6,7 +6,7 @@ struct SUWeight{T<:AbstractTensorMap} y::Matrix{T} function SUWeight(wxs::Matrix{T}, wys::Matrix{T}) where {T} - new{T}(wxs, wys) + return new{T}(wxs, wys) end end @@ -30,9 +30,9 @@ end function Base.iterate(wts::SUWeight, state=1) nx = prod(size(wts.x)) if 1 <= state <= nx - return wts.x[state], state+1 - elseif nx+1 <= state <= 2*nx - return wts.y[state-nx], state+1 + return wts.x[state], state + 1 + elseif nx + 1 <= state <= 2 * nx + return wts.y[state - nx], state + 1 else return nothing end diff --git a/src/utility/mirror.jl b/src/utility/mirror.jl index f9bc74ae7..8028d0b32 100644 --- a/src/utility/mirror.jl +++ b/src/utility/mirror.jl @@ -7,8 +7,5 @@ i.e. the element now at [r, c] was originally at [Nr-c+1, Nc-r+1] """ function mirror_antidiag(arr::AbstractMatrix) Nr, Nc = size(arr) - return collect( - arr[Nr-c+1, Nc-r+1] - for (r, c) in Iterators.product(1:Nc, 1:Nr) - ) + return collect(arr[Nr - c + 1, Nc - r + 1] for (r, c) in Iterators.product(1:Nc, 1:Nr)) end diff --git a/src/utility/svd.jl b/src/utility/svd.jl index 20950ac93..7d9741095 100644 --- a/src/utility/svd.jl +++ b/src/utility/svd.jl @@ -292,7 +292,6 @@ function _lorentz_broaden(x::Real, ε=1e-12) return x′ / (x′^2 + ε) end - """ Given `tsvd` result `u`, `s` and `vh`, absorb singular values `s` into `u` and `vh` by diff --git a/src/utility/util.jl b/src/utility/util.jl index 181eb3a97..5b61dd2bc 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -41,7 +41,7 @@ Compute S^(pow) for diagonal matrices `S` function sdiag_pow(S::AbstractTensorMap, pow::Real) S2 = similar(S) for (k, b) in blocks(S) - copyto!(blocks(S2)[k], diagm(diag(b).^pow)) + copyto!(blocks(S2)[k], diagm(diag(b) .^ pow)) end return S2 end From 043a7feb5d6f6d6aa63c6dac51b6d66142044791 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 7 Nov 2024 11:00:33 +0800 Subject: [PATCH 05/75] add TODO for svd initialization of ALS optimization --- src/algorithms/timeevol/fullupdate.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl index e0a16efda..b63edc281 100644 --- a/src/algorithms/timeevol/fullupdate.jl +++ b/src/algorithms/timeevol/fullupdate.jl @@ -123,6 +123,7 @@ function update_column!( =# tmp = ncon((gate, aR0, bL0), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) # initialize truncated tensors using simple SVD truncation + # TODO: return truncated and untruncated SVD result at once, without repeated calculation aR2, s, bL2, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncerr(1e-15)) aR, s_cut, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncscheme) aR2, bL2 = absorb_s(aR2, s, bL2) From 09270827af2a69b8ead12c2dc971cc223ec28dbf Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 7 Nov 2024 16:30:32 +0800 Subject: [PATCH 06/75] remove a redundant SVD in full update --- src/algorithms/timeevol/fu_optimize.jl | 143 +++++++++++-------------- src/algorithms/timeevol/fullupdate.jl | 13 +-- 2 files changed, 69 insertions(+), 87 deletions(-) diff --git a/src/algorithms/timeevol/fu_optimize.jl b/src/algorithms/timeevol/fu_optimize.jl index e90d4d1af..de52d3b56 100644 --- a/src/algorithms/timeevol/fu_optimize.jl +++ b/src/algorithms/timeevol/fu_optimize.jl @@ -88,23 +88,18 @@ end """ Construct the tensor ``` - |--------------env--------------| - |→ DX1 Db1 → bL† ← DY1 ←| - | ↑ | - | da db | - | ↑ ↑ | - |← DX0 ←- aR2 ←- D ← bL2 → DY0 →| - |-------------------------------| + |-----------env-----------| + |→ DX1 Db1 → bL† ← DY1 ←| + | ↑ | + | da db | + | ↑ ↑ | + |← DX0 ←- aR2 bL2 -→ DY0 →| + |-------------------------| ``` """ -function tensor_Sa( - env::AbstractTensorMap, - aR2::AbstractTensorMap, - bL::AbstractTensorMap, - bL2::AbstractTensorMap, -) +function tensor_Sa(env::AbstractTensorMap, bL::AbstractTensorMap, aR2bL2::AbstractTensorMap) @autoopt @tensor Sa[DX1, Db1, da] := ( - env[DX1, DY1, DX0, DY0] * conj(bL[Db1, db, DY1]) * bL2[D, db, DY0] * aR2[DX0, da, D] + env[DX1, DY1, DX0, DY0] * conj(bL[Db1, db, DY1]) * aR2bL2[DX0, da, db, DY0] ) return Sa end @@ -131,23 +126,18 @@ end """ Construct the tensor ``` - |--------------env--------------| - |→ DX1 → aR† → Da1 DY1 ←| - | ↑ | - | da db | - | ↑ ↑ | - |← DX0 ← aR2 ← D ←- bL2 -→ DY0 →| - |-------------------------------| + |-----------env-----------| + |→ DX1 → aR† → Da1 DY1 ←| + | ↑ | + | da db | + | ↑ ↑ | + |← DX0 ←- aR2 bL2 -→ DY0 →| + |-------------------------| ``` """ -function tensor_Sb( - env::AbstractTensorMap, - aR::AbstractTensorMap, - aR2::AbstractTensorMap, - bL2::AbstractTensorMap, -) +function tensor_Sb(env::AbstractTensorMap, aR::AbstractTensorMap, aR2bL2::AbstractTensorMap) @autoopt @tensor Sb[Da1, DY1, db] := ( - env[DX1, DY1, DX0, DY0] * conj(aR[DX1, da, Da1]) * aR2[DX0, da, D] * bL2[D, db, DY0] + env[DX1, DY1, DX0, DY0] * conj(aR[DX1, da, Da1]) * aR2bL2[DX0, da, db, DY0] ) return Sb end @@ -155,32 +145,37 @@ end """ Calculate the norm ``` - |--------------env--------------| - |→ DX1 → aR1†→ D1 → bL1† ← DY1 ←| - | ↑ ↑ | - | da db | - | ↑ ↑ | - |← DX0 ← aR2 ← D0 ← bL2 → DY0 -→| - |-------------------------------| + |----------env----------| + |→ DX1 → aR1bL1† ← DY1 ←| + | ↑ ↑ | + | da db | + | ↑ ↑ | + |← DX0 ← aR2bL2 → DY0 -→| + |-----------------------| ``` """ function inner_prod( - env::AbstractTensorMap, - aR1::AbstractTensorMap, - bL1::AbstractTensorMap, - aR2::AbstractTensorMap, - bL2::AbstractTensorMap, + env::AbstractTensorMap, aR1bL1::AbstractTensorMap, aR2bL2::AbstractTensorMap ) @autoopt @tensor t[:] := ( - env[DX1, DY1, DX0, DY0] * - conj(aR1[DX1, da, D1]) * - conj(bL1[D1, db, DY1]) * - aR2[DX0, da, D0] * - bL2[D0, db, DY0] + env[DX1, DY1, DX0, DY0] * conj(aR1bL1[DX1, da, db, DY1]) * aR2bL2[DX0, da, db, DY0] ) return first(blocks(t))[2][1] end +""" +Contract the axis between `aR` and `bL` tensors +""" +function _combine_aRbL(aR::AbstractTensorMap, bL::AbstractTensorMap) + #= + da db + ↑ ↑ + ← DX ← aR ← D ← bL → DY → + =# + @tensor aRbL[DX, da, db, DY] := aR[DX, da, D] * bL[D, db, DY] + return aRbL +end + """ Calculate the cost function ``` @@ -193,12 +188,12 @@ function cost_func( env::AbstractTensorMap, aR::AbstractTensorMap, bL::AbstractTensorMap, - aR2::AbstractTensorMap, - bL2::AbstractTensorMap, + aR2bL2::AbstractTensorMap, ) - t1 = inner_prod(env, aR, bL, aR, bL) - t2 = inner_prod(env, aR2, bL2, aR2, bL2) - t3 = inner_prod(env, aR, bL, aR2, bL2) + aRbL = _combine_aRbL(aR, bL) + t1 = inner_prod(env, aRbL, aRbL) + t2 = inner_prod(env, aR2bL2, aR2bL2) + t3 = inner_prod(env, aRbL, aR2bL2) return real(t1) + real(t2) - 2 * real(t3) end @@ -206,22 +201,15 @@ end Calculate the approximate local inner product `` ``` - |→ aR1† → D1 → bL1† ←| - | ↑ ↑ | - DW da db DE - | ↑ ↑ | - |← aR2 ←- D0 ← bL2 -→| + |→ aR1bL1† ←| + | ↑ ↑ | + DW da db DE + | ↑ ↑ | + |← aR2 bL2 →| ``` """ -function inner_prod_local( - aR1::AbstractTensorMap, - bL1::AbstractTensorMap, - aR2::AbstractTensorMap, - bL2::AbstractTensorMap, -) - @autoopt @tensor t[:] := ( - conj(aR1[DW, da, D1]) * conj(bL1[D1, db, DE]) * aR2[DW, da, D0] * bL2[D0, db, DE] - ) +function inner_prod_local(aR1bL1::AbstractTensorMap, aR2bL2::AbstractTensorMap) + @autoopt @tensor t[:] := (conj(aR1bL1[DW, da, db, DE]) * aR2bL2[DW, da, db, DE]) return first(blocks(t))[2][1] end @@ -235,14 +223,12 @@ between two evolution steps ``` """ function local_fidelity( - aR1::AbstractTensorMap, - bL1::AbstractTensorMap, - aR2::AbstractTensorMap, - bL2::AbstractTensorMap, + aR1::AbstractTensorMap, bL1::AbstractTensorMap, aR2bL2::AbstractTensorMap ) - b12 = inner_prod_local(aR1, bL1, aR2, bL2) - b11 = inner_prod_local(aR1, bL1, aR1, bL1) - b22 = inner_prod_local(aR2, bL2, aR2, bL2) + aR1bL1 = _combine_aRbL(aR1, bL1) + b12 = inner_prod_local(aR1bL1, aR2bL2) + b11 = inner_prod_local(aR1bL1, aR1bL1) + b22 = inner_prod_local(aR2bL2, aR2bL2) return abs(b12) / sqrt(abs(b11 * b22)) end @@ -274,8 +260,7 @@ Minimize the cost function function fu_optimize( aR0::AbstractTensorMap, bL0::AbstractTensorMap, - aR2::AbstractTensorMap, - bL2::AbstractTensorMap, + aR2bL2::AbstractTensorMap, env::AbstractTensorMap; maxiter::Int=50, maxdiff::Float64=1e-15, @@ -288,8 +273,8 @@ function fu_optimize( end aR, bL = deepcopy(aR0), deepcopy(bL0) time0 = time() - cost00 = cost_func(env, aR, bL, aR2, bL2) - fid00 = local_fidelity(aR, bL, aR2, bL2) + cost00 = cost_func(env, aR, bL, aR2bL2) + fid00 = local_fidelity(aR, bL, aR2bL2) cost0, fid0 = cost00, fid00 # no need to further optimize if abs(cost0) < 5e-15 @@ -306,13 +291,13 @@ function fu_optimize( for count in 1:maxiter time0 = time() Ra = tensor_Ra(env, bL) - Sa = tensor_Sa(env, aR2, bL, bL2) + Sa = tensor_Sa(env, bL, aR2bL2) aR, info_a = solve_ab(Ra, Sa, aR) Rb = tensor_Rb(env, aR) - Sb = tensor_Sb(env, aR, aR2, bL2) + Sb = tensor_Sb(env, aR, aR2bL2) bL, info_b = solve_ab(Rb, Sb, bL) - cost = cost_func(env, aR, bL, aR2, bL2) - fid = local_fidelity(aR, bL, aR2, bL2) + cost = cost_func(env, aR, bL, aR2bL2) + fid = local_fidelity(aR, bL, aR2bL2) diff_d = abs(cost - cost0) / cost00 diff_ab = abs(fid - fid0) / fid00 time1 = time() diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl index b63edc281..0b7ee9ad8 100644 --- a/src/algorithms/timeevol/fullupdate.jl +++ b/src/algorithms/timeevol/fullupdate.jl @@ -121,21 +121,18 @@ function update_column!( ↑ ↑ -1← aR -← 3 -← bL → -4 =# - tmp = ncon((gate, aR0, bL0), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) - # initialize truncated tensors using simple SVD truncation - # TODO: return truncated and untruncated SVD result at once, without repeated calculation - aR2, s, bL2, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncerr(1e-15)) - aR, s_cut, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncscheme) - aR2, bL2 = absorb_s(aR2, s, bL2) + aR2bL2 = ncon((gate, aR0, bL0), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) + # initialize truncated tensors using SVD truncation + aR, s_cut, bL, ϵ = tsvd(aR2bL2, ((1, 2), (3, 4)); trunc=truncscheme) aR, bL = absorb_s(aR, s_cut, bL) # optimize aR, bL aR, bL, cost = fu_optimize( - aR, bL, aR2, bL2, env; maxiter=maxiter, maxdiff=maxdiff, verbose=false + aR, bL, aR2bL2, env; maxiter=maxiter, maxdiff=maxdiff, verbose=false ) costs[row] = cost aR /= maxabs(aR) bL /= maxabs(bL) - localfid += local_fidelity(aR, bL, aR0, bL0) + localfid += local_fidelity(aR, bL, _combine_aRbL(aR0, bL0)) #= update and normalize peps, ms -2 -1 -1 -2 From 6735adeb33badfcee4d2274a12ff2f56bf20ebf7 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 10 Nov 2024 16:37:03 +0800 Subject: [PATCH 07/75] prepare for addition of full-infinite env CTMRG --- src/algorithms/timeevol/fullupdate.jl | 33 +++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl index 0b7ee9ad8..e82657797 100644 --- a/src/algorithms/timeevol/fullupdate.jl +++ b/src/algorithms/timeevol/fullupdate.jl @@ -1,6 +1,9 @@ include("fu_gaugefix.jl") include("fu_optimize.jl") +# TODO: add option to use full-infinite environment +# for CTMRG moves when it is implemented in PEPSKit + """ CTMRG left-move to update CTMRGEnv in the c-th column ``` @@ -14,7 +17,12 @@ CTMRG left-move to update CTMRGEnv in the c-th column ``` """ function ctmrg_leftmove!( - col::Int, peps::InfinitePEPS, envs::CTMRGEnv, chi::Int, svderr::Float64=1e-9 + col::Int, + peps::InfinitePEPS, + envs::CTMRGEnv, + chi::Int, + svderr::Float64=1e-9; + cheap::Bool=true, ) trscheme = truncerr(svderr) & truncdim(chi) alg = CTMRG(; @@ -39,7 +47,12 @@ CTMRG right-move to update CTMRGEnv in the c-th column ``` """ function ctmrg_rightmove!( - col::Int, peps::InfinitePEPS, envs::CTMRGEnv, chi::Int, svderr::Float64=1e-9 + col::Int, + peps::InfinitePEPS, + envs::CTMRGEnv, + chi::Int, + svderr::Float64=1e-9; + cheap::Bool=true, ) Nr, Nc = size(peps) @assert 1 <= col <= Nc @@ -64,6 +77,7 @@ function update_column!( svderr::Float64=1e-9, maxiter::Int=50, maxdiff::Float64=1e-15, + cheap=true, gaugefix::Bool=true, ) Nr, Nc = size(peps) @@ -153,8 +167,8 @@ function update_column!( end end # update CTMRGEnv - ctmrg_leftmove!(col, peps, envs, chi, svderr) - ctmrg_rightmove!(_next(col, Nc), peps, envs, chi, svderr) + ctmrg_leftmove!(col, peps, envs, chi, svderr; cheap=cheap) + ctmrg_rightmove!(_next(col, Nc), peps, envs, chi, svderr; cheap=cheap) return localfid, costs end @@ -172,19 +186,24 @@ function fullupdate!( envs::CTMRGEnv, Dcut::Int, chi::Int, - svderr::Float64=1e-9, + svderr::Float64=1e-9; + cheap=false, ) Nr, Nc = size(peps) fid, maxcost = 0.0, 0.0 for col in 1:Nc - tmpfid, costs = update_column!(col, gate, peps, envs, Dcut, chi; svderr=svderr) + tmpfid, costs = update_column!( + col, gate, peps, envs, Dcut, chi; svderr=svderr, cheap=cheap + ) fid += tmpfid maxcost = max(maxcost, maximum(costs)) end rotr90!(peps) rotr90!(envs) for row in 1:Nr - tmpfid, costs = update_column!(row, gate, peps, envs, Dcut, chi; svderr=svderr) + tmpfid, costs = update_column!( + row, gate, peps, envs, Dcut, chi; svderr=svderr, cheap=cheap + ) fid += tmpfid maxcost = max(maxcost, maximum(costs)) end From 3a82f06528aa9ddaa74cb1c858617732f6345fdb Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 14 Nov 2024 10:00:20 +0800 Subject: [PATCH 08/75] Add `length` for SUWeight --- src/states/suweight.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/states/suweight.jl b/src/states/suweight.jl index 19421d333..037ac322d 100644 --- a/src/states/suweight.jl +++ b/src/states/suweight.jl @@ -38,6 +38,11 @@ function Base.iterate(wts::SUWeight, state=1) end end +function Base.length(wts::SUWeight) + @assert size(wts.x) == size(wts.y) + return 2 * prod(size(wts.x)) +end + function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) return ( isapprox(wts1.x, wts2.x; atol=atol, rtol=rtol) && From 9577febf02548a89e04af27522bdba44da1cca10 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 15 Nov 2024 15:14:19 +0800 Subject: [PATCH 09/75] Define `Base.show` for `SUWeight` --- src/states/suweight.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/states/suweight.jl b/src/states/suweight.jl index 037ac322d..6995ab9a8 100644 --- a/src/states/suweight.jl +++ b/src/states/suweight.jl @@ -27,6 +27,17 @@ function Base.:(-)(wts1::SUWeight, wts2::SUWeight) return SUWeight(wts1.x - wts2.x, wts1.y - wts2.y) end +function Base.show(io::IO, wts::SUWeight) + N1, N2 = size(wts) + for (direction, r, c) in Iterators.product("xy", 1:N1, 1:N2) + println(io, "$direction[$r,$c]: ") + wt = (direction == 'x' ? wts.x[r,c] : wts.y[r,c]) + for (k, b) in blocks(wt) + println(io, k, " = ", diag(b)) + end + end +end + function Base.iterate(wts::SUWeight, state=1) nx = prod(size(wts.x)) if 1 <= state <= nx From a1c3e062c690a32384c73e3bd7a6acd93e77b89b Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 17 Nov 2024 11:29:49 +0800 Subject: [PATCH 10/75] add test for simple update --- src/PEPSKit.jl | 9 +- src/algorithms/contractions/ctmrg_rhos.jl | 189 ++++++++++++++++++++ src/algorithms/contractions/measure_rhos.jl | 39 ++++ src/algorithms/ctmrg/gaugefix.jl | 13 ++ src/algorithms/timeevol/fullupdate.jl | 112 +++++++++++- src/algorithms/timeevol/simpleupdate.jl | 67 ++++++- test/heisenberg_sufu.jl | 59 ++++++ test/utility/heis.jl | 106 +++++++++++ 8 files changed, 590 insertions(+), 4 deletions(-) create mode 100644 src/algorithms/contractions/ctmrg_rhos.jl create mode 100644 src/algorithms/contractions/measure_rhos.jl create mode 100644 test/heisenberg_sufu.jl create mode 100644 test/utility/heis.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index bd71e7287..0726ca290 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -40,6 +40,8 @@ include("environments/transferpepo_environments.jl") include("algorithms/contractions/localoperator.jl") include("algorithms/contractions/ctmrg_contractions.jl") +include("algorithms/contractions/ctmrg_rhos.jl") +include("algorithms/contractions/measure_rhos.jl") include("algorithms/ctmrg/sparse_environments.jl") include("algorithms/ctmrg/ctmrg.jl") @@ -172,8 +174,11 @@ export leading_boundary export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint -export simpleupdate!, absorb_wt -export fullupdate! +export absorb_wt, absorb_wt! +export su_iter!, simpleupdate! +export fu_iter!, fullupdate! +export meas_site, meas_bond +export calrho_site, calrho_bondx, calrho_bondy, calrho_all export SUWeight export InfinitePEPS, InfiniteTransferPEPS diff --git a/src/algorithms/contractions/ctmrg_rhos.jl b/src/algorithms/contractions/ctmrg_rhos.jl new file mode 100644 index 000000000..13585fc4b --- /dev/null +++ b/src/algorithms/contractions/ctmrg_rhos.jl @@ -0,0 +1,189 @@ +""" +Calculate 1-site rho at site `(r,c)` +``` + C1 - χ4 - T1 - χ6 - C2 r-1 + | ‖ | + χ2 DN χ8 + | ‖ | + T4 = DW =k/b = DE = T2 r + | ‖ | + χ1 DS χ7 + | ‖ | + C4 - χ3 - T3 - χ5 - C3 r+1 + c-1 c c+1 +``` +Indices d0, d1 are physical indices of ket, bra +""" +function calrho_site( + row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket +) + N1, N2 = size(ket) + @assert 1 <= row <= N1 && 1 <= col <= N2 + rp1, rm1 = _next(row, N1), _prev(row, N1) + cp1, cm1 = _next(col, N2), _prev(col, N2) + tket, tbra = ket[row, col], bra[row, col] + c1 = envs.corners[1, rm1, cm1] + t1 = envs.edges[1, rm1, col] + c2 = envs.corners[2, rm1, cp1] + t2 = envs.edges[2, row, cp1] + c3 = envs.corners[3, rp1, cp1] + t3 = envs.edges[3, rp1, col] + c4 = envs.corners[4, rp1, cm1] + t4 = envs.edges[4, row, cm1] + PEPSKit.@autoopt @tensor rho1[d1; d0] := ( + c4[χ3, χ1] * + t4[χ1, DW0, DW1, χ2] * + c1[χ2, χ4] * + t3[χ5, DS0, DS1, χ3] * + tket[d0, DN0, DE0, DS0, DW0] * + conj(tbra[d1, DN1, DE1, DS1, DW1]) * + t1[χ4, DN0, DN1, χ6] * + c3[χ7, χ5] * + t2[χ8, DE0, DE1, χ7] * + c2[χ6, χ8] + ) + return rho1 +end + +""" +Calculate 2-site rho on sites `(r,c)(r,c+1)` +``` + C1 - χ4 - T1 - χ6 - T1 - χ8 - C2 r-1 + | ‖ ‖ | + χ2 DN1 DN2 χ10 + | ‖ ‖ | + T4 = DW =k/b = DM =k/b = DE = T2 r + | ‖ ‖ | + χ1 DS1 DS2 χ9 + | ‖ ‖ | + C4 - χ3 - T3 - χ5 - T3 - χ7 - C3 r+1 + c-1 c c+1 c+2 +``` +Indices d0, d1 are physical indices of ket, bra +""" +function calrho_bondx( + row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket +) + N1, N2 = size(ket) + @assert 1 <= row <= N1 && 1 <= col <= N2 + rp1, rm1 = _next(row, N1), _prev(row, N1) + cp1, cm1 = _next(col, N2), _prev(col, N2) + cp2 = _next(cp1, N2) + tket1, tbra1 = ket[row, col], bra[row, col] + tket2, tbra2 = ket[row, cp1], bra[row, cp1] + c1 = envs.corners[1, rm1, cm1] + t11, t12 = envs.edges[1, rm1, col], envs.edges[1, rm1, cp1] + c2 = envs.corners[2, rm1, cp2] + t2 = envs.edges[2, row, cp2] + c3 = envs.corners[3, rp1, cp2] + t31, t32 = envs.edges[3, rp1, col], envs.edges[3, rp1, cp1] + c4 = envs.corners[4, rp1, cm1] + t4 = envs.edges[4, row, cm1] + PEPSKit.@autoopt @tensor rho2[d11, d21; d10, d20] := ( + c4[χ3, χ1] * + t4[χ1, DW0, DW1, χ2] * + c1[χ2, χ4] * + t31[χ5, DS10, DS11, χ3] * + tket1[d10, DN10, DM0, DS10, DW0] * + conj(tbra1[d11, DN11, DM1, DS11, DW1]) * + t11[χ4, DN10, DN11, χ6] * + t32[χ7, DS20, DS21, χ5] * + tket2[d20, DN20, DE0, DS20, DM0] * + conj(tbra2[d21, DN21, DE1, DS21, DM1]) * + t12[χ6, DN20, DN21, χ8] * + c3[χ9, χ7] * + t2[χ10, DE0, DE1, χ9] * + c2[χ8, χ10] + ) + return rho2 +end + +""" +Calculate 2-site rho on sites `(r,c)(r-1,c)` +``` + C1 - χ9 - T1 -χ10 - C2 r-2 + | ‖ | + χ7 DN χ8 + | ‖ | + T4 = DW2=k/b =DE2 = T2 r-1 + | ‖ | + χ5 DM χ6 + | ‖ | + T4 = DW1=k/b =DE1 = T2 r + | ‖ | + χ3 DS χ4 + | ‖ | + C4 - χ1 - T3 - χ2 - C3 r+1 + c-1 c c+1 +``` +Indices d0, d1 are physical indices of ket, bra +""" +function calrho_bondy( + row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket +) + N1, N2 = size(ket) + @assert 1 <= row <= N1 && 1 <= col <= N2 + rp1, rm1 = _next(row, N1), _prev(row, N1) + cp1, cm1 = _next(col, N2), _prev(col, N2) + rm2 = _prev(rm1, N1) + tket1, tbra1 = ket[row, col], bra[row, col] + tket2, tbra2 = ket[rm1, col], bra[rm1, col] + c1 = envs.corners[1, rm2, cm1] + t1 = envs.edges[1, rm2, col] + c2 = envs.corners[2, rm2, cp1] + t21, t22 = envs.edges[2, row, cp1], envs.edges[2, rm1, cp1] + c3 = envs.corners[3, rp1, cp1] + t3 = envs.edges[3, rp1, col] + c4 = envs.corners[4, rp1, cm1] + t41, t42 = envs.edges[4, row, cm1], envs.edges[4, rm1, cm1] + PEPSKit.@autoopt @tensor rho2[d11, d21; d10, d20] := ( + c4[χ1, χ3] * + t3[χ2, DS0, DS1, χ1] * + c3[χ4, χ2] * + t41[χ3, DW10, DW11, χ5] * + tket1[d10, DM0, DE10, DS0, DW10] * + conj(tbra1[d11, DM1, DE11, DS1, DW11]) * + t21[χ6, DE10, DE11, χ4] * + t42[χ5, DW20, DW21, χ7] * + tket2[d20, DN0, DE20, DM0, DW20] * + conj(tbra2[d21, DN1, DE21, DM1, DW21]) * + t22[χ8, DE20, DE21, χ6] * + c1[χ7, χ9] * + t1[χ9, DN0, DN1, χ10] * + c2[χ10, χ8] + ) + return rho2 +end + +""" +Calculate rho for all sites +""" +function calrho_allsites(envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket) + Nr, Nc = size(ket) + return collect( + calrho_site(r, c, envs, ket, bra) for (r, c) in Iterators.product(1:Nr, 1:Nc) + ) +end + +""" +Calculate rho for all nearest-neighbor bonds +""" +function calrho_allnbs(envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket) + Nr, Nc = size(ket) + rhoxss = collect( + calrho_bondx(r, c, envs, ket, bra) for (r, c) in Iterators.product(1:Nr, 1:Nc) + ) + rhoyss = collect( + calrho_bondy(r, c, envs, ket, bra) for (r, c) in Iterators.product(1:Nr, 1:Nc) + ) + return [rhoxss, rhoyss] +end + +""" +Calculate rho for all sites and nearest-neighbor bonds +""" +function calrho_all(envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket) + rho1ss = calrho_allsites(envs, ket, bra) + rho2sss = calrho_allnbs(envs, ket, bra) + return rho1ss, rho2sss +end diff --git a/src/algorithms/contractions/measure_rhos.jl b/src/algorithms/contractions/measure_rhos.jl new file mode 100644 index 000000000..a075a0776 --- /dev/null +++ b/src/algorithms/contractions/measure_rhos.jl @@ -0,0 +1,39 @@ +""" +Get identity operator on the physical space +""" +function _getid_from_rho(rho::AbstractTensorMap) + Pspace = codomain(rho)[1] + if isdual(Pspace) + Pspace = adjoint(Pspace) + end + return TensorKit.id(Pspace) +end + +""" +Measure `` using 1-site rho +""" +function meas_site(op::AbstractTensorMap, rho1::AbstractTensorMap) + Id = _getid_from_rho(rho1) + val = ncon((rho1, op), ([1, 2], [1, 2])) + nrm = ncon((rho1, Id), ([1, 2], [1, 2])) + meas = first(blocks(val / nrm))[2][1] + return meas +end + +""" +Measure `` using 2-site rho +""" +function meas_bond(op1::AbstractTensorMap, op2::AbstractTensorMap, rho2::AbstractTensorMap) + return meas_bond(op1 ⊗ op2, rho2) +end + +""" +Measure `` using 2-site rho +""" +function meas_bond(gate::AbstractTensorMap, rho2::AbstractTensorMap) + Id = _getid_from_rho(rho2) + val = ncon((rho2, gate), ([1, 2, 3, 4], [1, 2, 3, 4])) + nrm = ncon((rho2, Id ⊗ Id), ([1, 2, 3, 4], [1, 2, 3, 4])) + meas = first(blocks(val / nrm))[2][1] + return meas +end diff --git a/src/algorithms/ctmrg/gaugefix.jl b/src/algorithms/ctmrg/gaugefix.jl index 5e63d5ef8..060db6335 100644 --- a/src/algorithms/ctmrg/gaugefix.jl +++ b/src/algorithms/ctmrg/gaugefix.jl @@ -173,6 +173,19 @@ function calc_convergence(envs, CSold, TSold) return max(ΔCS, ΔTS), CSnew, TSnew end +""" +Calculate convergence of CTMRG by comparing the singular values of CTM tensors +""" +function calc_convergence(envsNew::CTMRGEnv, envsOld::CTMRGEnv) + CSNew = map(x -> tsvd(x)[2], envsNew.corners) + TSNew = map(x -> tsvd(x)[2], envsNew.edges) + CSOld = map(x -> tsvd(x)[2], envsOld.corners) + TSOld = map(x -> tsvd(x)[2], envsOld.edges) + ΔCS = maximum(_singular_value_distance, zip(CSOld, CSNew)) + ΔTS = maximum(_singular_value_distance, zip(TSOld, TSNew)) + return max(ΔCS, ΔTS) +end + @non_differentiable calc_convergence(args...) """ diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl index e82657797..f25e346f8 100644 --- a/src/algorithms/timeevol/fullupdate.jl +++ b/src/algorithms/timeevol/fullupdate.jl @@ -180,7 +180,7 @@ Otherwise, use full-infinite environment instead. Reference: Physical Review B 92, 035142 (2015) """ -function fullupdate!( +function fu_iter!( gate::AbstractTensorMap, peps::InfinitePEPS, envs::CTMRGEnv, @@ -212,3 +212,113 @@ function fullupdate!( fid /= (2 * Nr * Nc) return fid, maxcost end + +# TODO: pass Hamiltonian gate as `LocalOperator` +""" +Perform full update +""" +function fullupdate!( + peps::InfinitePEPS, + envs::CTMRGEnv, + ham::AbstractTensorMap, + dt::Float64, + Dcut::Int, + chi::Int; + evolstep::Int=5000, + svderr::Float64=1e-9, + rgint::Int=10, + rgtol::Float64=1e-6, + rgmaxiter::Int=10, + ctmrgscheme=:sequential, + cheap=false, +) + time_start = time() + N1, N2 = size(peps) + @assert endswith(folder, "/") + # CTMRG algorithm to reconverge environment + ctm_alg = CTMRG(; + tol=rgtol, + maxiter=rgmaxiter, + miniter=1, + verbosity=2, + trscheme=truncerr(svderr) & truncdim(chi), + svd_alg=SVDAdjoint(; fwd_alg=TensorKit.SDD()), + ctmrgscheme=ctmrgscheme, + ) + @printf( + "%-4s %7s%10s%12s%11s %s/%s\n", + "step", + "dt", + "energy", + "Δe", + "svd_diff", + "speed", + "meas(s)" + ) + flush(stdout) + gate = exp(-dt * ham) + esite0, peps0, envs0 = Inf, deepcopy(peps), deepcopy(envs) + diff_energy = 0.0 + for count in 1:evolstep + time0 = time() + fid, cost = fu_iter!(gate, peps, envs, Dcut, chi, svderr; cheap=cheap) + time1 = time() + if count == 1 || count % rgint == 0 + meast0 = time() + # reconverge `env` (in place) + println(stderr, "---- FU step $count: reconverging envs ----") + envs2 = leading_boundary(envs, peps, ctm_alg) + envs.edges[:], envs.corners[:] = envs2.edges, envs2.corners + # TODO: monitor energy with costfun + # esite = costfun(peps, envs, ham) + rho2sss = calrho_allnbs(envs, peps) + ebonds = [collect(meas_bond(ham, rho2) for rho2 in rho2sss[n]) for n in 1:2] + esite = sum(sum(ebonds)) / (N1 * N2) + meast1 = time() + # monitor change of CTMRGEnv by its singular values + diff_energy = esite - esite0 + diff_ctm = calc_convergence(envs, envs0) + @printf( + "%-4d %7.0e%10.5f%12.3e%11.3e %.3f/%.3f\n", + count, + dt, + esite, + diff_energy, + diff_ctm, + time1 - time0, + meast1 - meast0 + ) + if diff_energy > 0 + @printf("Energy starts to increase. Abort evolution.\n") + # restore peps and envs at last checking + peps.A[:] = peps0.A + envs.corners[:], envs.edges[:] = envs0.corners, envs0.edges + break + end + esite0, peps0, envs0 = esite, deepcopy(peps), deepcopy(envs) + end + end + # reconverge the environment tensors + for io in (stdout, stderr) + @printf(io, "Reconverging final envs ... \n") + flush(io) + end + envs2 = leading_boundary( + envs, + peps, + CTMRG(; + tol=1e-10, + maxiter=50, + miniter=1, + verbosity=2, + trscheme=truncerr(svderr) & truncdim(chi), + svd_alg=SVDAdjoint(; fwd_alg=TensorKit.SDD()), + ctmrgscheme=ctmrgscheme, + ), + ) + envs.edges[:], envs.corners[:] = envs2.edges, envs2.corners + time_end = time() + @printf("Evolution time: %.3f s\n\n", time_end - time_start) + print(stderr, "\n----------\n\n") + return esite0, diff_energy +end diff --git a/src/algorithms/timeevol/simpleupdate.jl b/src/algorithms/timeevol/simpleupdate.jl index a57795bea..451ff8c9f 100644 --- a/src/algorithms/timeevol/simpleupdate.jl +++ b/src/algorithms/timeevol/simpleupdate.jl @@ -60,6 +60,19 @@ function absorb_wt( return t2 end +""" +Absorb bond weights into iPEPS site tensors +""" +function absorb_wt!(peps::InfinitePEPS, wts::SUWeight) + N1, N2 = size(peps) + for (r, c) in Iterators.product(1:N1, 1:N2) + for ax in 2:5 + peps.A[r, c] = absorb_wt(peps.A[r, c], r, c, ax, wts; sqrtwt=true) + end + end + return nothing +end + """ Simple update of bond `wts.x[r,c]` ``` @@ -153,7 +166,7 @@ and SUWeight `wts` with the nearest neighbor gate `gate` When `bipartite === true` (for square lattice), the unit cell size should be 2 x 2, and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` """ -function simpleupdate!( +function su_iter!( gate::AbstractTensorMap, peps::InfinitePEPS, wts::SUWeight, @@ -198,3 +211,55 @@ function simpleupdate!( end return nothing end + +function compare_weights(wts1::SUWeight, wts2::SUWeight) + wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1, wts2)) + wtdiff /= 2 * prod(size(wts1)) + return wtdiff +end + +""" +Perform simple update (maximum `evolstep` iterations) +with nearest neighbor Hamiltonian `ham` and time step `dt` +until the change of bond weights is smaller than `wtdiff_tol` +""" +function simpleupdate!( + peps::InfinitePEPS, + wts::SUWeight, + ham::AbstractTensorMap, + dt::Float64, + Dcut::Int; + evolstep::Int=400000, + svderr::Float64=1e-10, + wtdiff_tol::Float64=1e-10, + bipartite::Bool=false, + check_int::Int=500, +) + time_start = time() + N1, N2 = size(peps) + if bipartite + @assert N1 == N2 == 2 + end + @printf("%-9s%6s%12s %s\n", "Step", "dt", "wt_diff", "speed/s") + # exponentiating the 2-site Hamiltonian gate + gate = exp(-dt * ham) + wtdiff = 1e+3 + wts0 = deepcopy(wts) + for count in 1:evolstep + time0 = time() + su_iter!(gate, peps, wts, Dcut, svderr; bipartite=bipartite) + wtdiff = compare_weights(wts, wts0) + stop = (wtdiff < wtdiff_tol) || (count == evolstep) + wts0 = deepcopy(wts) + time1 = time() + if ((count == 1) || (count % check_int == 0) || stop) + @printf("%-9d%6.0e%12.3e %.3f\n", count, dt, wtdiff, time1 - time0) + end + if stop + break + end + end + time_end = time() + @printf("Evolution time: %.2f s\n\n", time_end - time_start) + return wtdiff +end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl new file mode 100644 index 000000000..e0a9e28c2 --- /dev/null +++ b/test/heisenberg_sufu.jl @@ -0,0 +1,59 @@ +using Test +using Random +using PEPSKit +using TensorKit +import Statistics: mean +include("utility/heis.jl") +using .OpsHeis, .RhoMeasureHeis + +# benchmark data for D = 3 is from +# Phys. Rev. B 94, 035133 (2016) + +# random initialization of 2x2 iPEPS and CTMRGEnv +# (using real numbersf) +Dcut = 3 +χenv = 24 +N1, N2 = 2, 2 +Pspace = ℂ^2 +Vspace = ℂ^Dcut +Random.seed!(0) +peps = InfinitePEPS( + collect( + TensorMap(rand, Float64, Pspace, Vspace ⊗ Vspace ⊗ Vspace' ⊗ Vspace') for + (row, col) in Iterators.product(1:N1, 1:N2) + ), +) +wts = SUWeight( + collect(id(Vspace) for (row, col) in Iterators.product(1:N1, 1:N2)), + collect(id(Vspace) for (row, col) in Iterators.product(1:N1, 1:N2)), +) +# normalize peps +for ind in CartesianIndices(peps.A) + peps.A[ind] /= PEPSKit.maxabs(peps.A[ind]) +end +# Heisenberg model Hamiltonian +ham = gen_gate() + +# simple update energy and magnetization +dts = [1e-2, 1e-3, 4e-4, 1e-4] +tols = [1e-6, 1e-7, 1e-8, 1e-9] +for (dt, tol) in zip(dts, tols) + simpleupdate!(peps, wts, ham, dt, Dcut; evolstep=30000, wtdiff_tol=tol) +end +# absort weight into site tensors +absorb_wt!(peps, wts) +# CTMRG +envs = CTMRGEnv(rand, Float64, peps, ℂ^χenv) +trscheme = truncerr(1e-9) & truncdim(χenv) +ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) +envs = leading_boundary(envs, peps, ctm_alg) +# measure physical quantities +rho1ss, rho2sss = calrho_all(envs, peps) +result = measrho_all(rho1ss, rho2sss) +display(result) +@test isapprox(result["e_site"], -0.6633; atol=1e-3) +@test isapprox(mean(result["mag_norm"]), 0.3972; atol=1e-3) + +# full update energy and magnetization +# e_fu = -0.6654 +# mag_fu = 0.3634 diff --git a/test/utility/heis.jl b/test/utility/heis.jl new file mode 100644 index 000000000..4dbd68275 --- /dev/null +++ b/test/utility/heis.jl @@ -0,0 +1,106 @@ +module OpsHeis + +export gen_gate, gen_siteop, gen_bondop +using TensorKit + +""" +Create 1-site operators for Heisenberg model +""" +function gen_siteop(name::String) + Pspace = ℂ^2 + if name == "Id" + return id(Pspace) + end + op = TensorMap(zeros, Pspace, Pspace) + if name == "Nud" + block(op, Trivial())[:] = [1.0 0.0; 0.0 1.0] + elseif name == "Sp" + block(op, Trivial())[:] = [0.0 1.0; 0.0 0.0] + elseif name == "Sm" + block(op, Trivial())[:] = [0.0 0.0; 1.0 0.0] + elseif name == "Sz" + block(op, Trivial())[:] = [1.0 0.0; 0.0 -1.0] / 2 + elseif name == "Sx" + block(op, Trivial())[:] = [0.0 1.0; 1.0 0.0] / 2 + elseif name == "iSy" + block(op, Trivial())[:] = [0.0 1.0; -1.0 0.0] / 2 + else + throw(ArgumentError("Invalid 1-site spin operator")) + end + return op +end + +""" +Create 2-site operators for Heisenberg model +""" +function gen_bondop(name1::String, name2::String) + op1 = gen_siteop(name1) + op2 = gen_siteop(name2) + op = op1 ⊗ op2 + return op +end + +""" +Create nearest neighbor gate for Heisenberg model +""" +function gen_gate(J::Float64=1.0; dens_shift::Bool=false) + heis = + J * ( + (1 / 2) * gen_bondop("Sp", "Sm") + + (1 / 2) * gen_bondop("Sm", "Sp") + + gen_bondop("Sz", "Sz") + ) + if dens_shift + heis = heis - (J / 4) * gen_bondop("Nud", "Nud") + end + return heis +end + +end + +module RhoMeasureHeis + +export measrho_all, cal_Esite + +using TensorKit +using PEPSKit +using Statistics: mean +using ..OpsHeis + +function cal_mags(rho1ss::Matrix{<:AbstractTensorMap}) + Pspace = codomain(rho1ss[1, 1])[1]' + Sas = [gen_siteop(name) for name in ("Sx", "iSy", "Sz")] + return [collect(meas_site(Sa, rho1) for rho1 in rho1ss) for Sa in Sas] +end + +function cal_spincor(rho2ss::Matrix{<:AbstractTensorMap}) + SpSm = gen_bondop("Sp", "Sm") + SzSz = gen_bondop("Sz", "Sz") + return collect(meas_bond(SpSm, rho2) + meas_bond(SzSz, rho2) for rho2 in rho2ss) +end + +function cal_Esite(rho2sss::Vector{<:Matrix{<:AbstractTensorMap}}) + N1, N2 = size(rho2sss[1]) + gate1 = gen_gate(; dens_shift=false) + # 1st neighbor bond energy + ebond1s = [collect(meas_bond(gate1, rho2) for rho2 in rho2sss[n]) for n in 1:2] + esite = sum(sum(ebond1s)) / (N1 * N2) + return esite, ebond1s +end + +function measrho_all( + rho1ss::Matrix{<:AbstractTensorMap}, rho2sss::Vector{<:Matrix{<:AbstractTensorMap}} +) + results = Dict{String,Any}() + N1, N2 = size(rho1ss) + results["e_site"], results["energy"] = cal_Esite(rho2sss) + results["mag"] = cal_mags(rho1ss) + results["mag_norm"] = collect( + norm([results["mag"][n][r, c] for n in 1:3]) for + (r, c) in Iterators.product(1:N1, 1:N2) + ) + results["spincor"] = [cal_spincor(rho2ss) for rho2ss in rho2sss] + return results +end + +end From 448176ceade38d593c192083e21ad62f0ffe75eb Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 17 Nov 2024 17:30:18 +0800 Subject: [PATCH 11/75] add test for full update --- src/algorithms/contractions/ctmrg_rhos.jl | 41 +++++++++++++++++++ src/algorithms/timeevol/fullupdate.jl | 3 -- test/heisenberg_sufu.jl | 49 +++++++++++++---------- test/runtests.jl | 2 +- 4 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/algorithms/contractions/ctmrg_rhos.jl b/src/algorithms/contractions/ctmrg_rhos.jl index 13585fc4b..7898d25f0 100644 --- a/src/algorithms/contractions/ctmrg_rhos.jl +++ b/src/algorithms/contractions/ctmrg_rhos.jl @@ -155,6 +155,47 @@ function calrho_bondy( return rho2 end +# TODO: add rhos on next nearest neighbor bonds + +""" +Calculate 2-site rho on 2nd nearest neighbor sites `(r,c)(r-1,c+1)` +``` + C1 -χ10 - T1 -χ11 - T1 -χ12 - C2 r-2 + | ‖ ‖ | + χ8 DN1 DN2 χ9 + | ‖ ‖ | + T4 =DW2= k/b =DH2= k/b =DE2== T2 r-1 + | ‖ ‖ | + χ6 DV1 DV2 χ7 + | ‖ ‖ | + T4 =DW1= k/b =DH1= k/b =DE1== T2 r + | ‖ ‖ | + χ4 DS1 DS2 χ5 + | ‖ ‖ | + C4 - χ1 - T3 - χ2 - T3 - χ3 - C4 r+1 + c-1 c c+1 c+2 +``` +Indices d0, d1 are physical indices of ket, bra +""" +function calrho_bondd1( + row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket +) + N1, N2 = size(ket) + @assert 1 <= row <= N1 && 1 <= col <= N2 + throw("not implemented") +end + +""" +Calculate 2-site rho on 2nd nearest neighbor sites `(r,c+1)(r-1,c)` +""" +function calrho_bondd2( + row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket +) + N1, N2 = size(ket) + @assert 1 <= row <= N1 && 1 <= col <= N2 + throw("not implemented") +end + """ Calculate rho for all sites """ diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/timeevol/fullupdate.jl index f25e346f8..3eb85a4bf 100644 --- a/src/algorithms/timeevol/fullupdate.jl +++ b/src/algorithms/timeevol/fullupdate.jl @@ -234,7 +234,6 @@ function fullupdate!( ) time_start = time() N1, N2 = size(peps) - @assert endswith(folder, "/") # CTMRG algorithm to reconverge environment ctm_alg = CTMRG(; tol=rgtol, @@ -255,7 +254,6 @@ function fullupdate!( "speed", "meas(s)" ) - flush(stdout) gate = exp(-dt * ham) esite0, peps0, envs0 = Inf, deepcopy(peps), deepcopy(envs) diff_energy = 0.0 @@ -301,7 +299,6 @@ function fullupdate!( # reconverge the environment tensors for io in (stdout, stderr) @printf(io, "Reconverging final envs ... \n") - flush(io) end envs2 = leading_boundary( envs, diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index e0a9e28c2..86372e674 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -1,28 +1,22 @@ using Test +using Printf using Random using PEPSKit using TensorKit import Statistics: mean include("utility/heis.jl") -using .OpsHeis, .RhoMeasureHeis +import .OpsHeis: gen_gate +import .RhoMeasureHeis: measrho_all # benchmark data for D = 3 is from # Phys. Rev. B 94, 035133 (2016) -# random initialization of 2x2 iPEPS and CTMRGEnv -# (using real numbersf) -Dcut = 3 -χenv = 24 +# random initialization of 2x2 iPEPS and CTMRGEnv (using real numbers) +Dcut, χenv = 4, 16 N1, N2 = 2, 2 -Pspace = ℂ^2 -Vspace = ℂ^Dcut +Pspace, Vspace = ℂ^2, ℂ^Dcut Random.seed!(0) -peps = InfinitePEPS( - collect( - TensorMap(rand, Float64, Pspace, Vspace ⊗ Vspace ⊗ Vspace' ⊗ Vspace') for - (row, col) in Iterators.product(1:N1, 1:N2) - ), -) +peps = InfinitePEPS(rand, Float64, 2, Dcut; unitcell=(N1, N2)) wts = SUWeight( collect(id(Vspace) for (row, col) in Iterators.product(1:N1, 1:N2)), collect(id(Vspace) for (row, col) in Iterators.product(1:N1, 1:N2)), @@ -34,11 +28,12 @@ end # Heisenberg model Hamiltonian ham = gen_gate() -# simple update energy and magnetization +# simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-7, 1e-8, 1e-9] -for (dt, tol) in zip(dts, tols) - simpleupdate!(peps, wts, ham, dt, Dcut; evolstep=30000, wtdiff_tol=tol) +for (n, (dt, tol)) in enumerate(zip(dts, tols)) + Dcut2 = (n == 1 ? Dcut + 1 : Dcut) + simpleupdate!(peps, wts, ham, dt, Dcut2; bipartite=true, evolstep=30000, wtdiff_tol=tol) end # absort weight into site tensors absorb_wt!(peps, wts) @@ -50,10 +45,20 @@ envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities rho1ss, rho2sss = calrho_all(envs, peps) result = measrho_all(rho1ss, rho2sss) -display(result) -@test isapprox(result["e_site"], -0.6633; atol=1e-3) -@test isapprox(mean(result["mag_norm"]), 0.3972; atol=1e-3) +@printf("Energy = %.8f\n", result["e_site"]) +@printf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) +@test isapprox(result["e_site"], -0.6675; atol=1e-3) +@test isapprox(mean(result["mag_norm"]), 0.3767; atol=1e-3) -# full update energy and magnetization -# e_fu = -0.6654 -# mag_fu = 0.3634 +# continue with full update +dts = [2e-2, 1e-2, 5e-3, 1e-3, 5e-4] +for dt in dts + fullupdate!(peps, envs, ham, dt, Dcut, χenv; rgmaxiter=5, cheap=true) +end +# measure physical quantities +rho1ss, rho2sss = calrho_all(envs, peps) +result = measrho_all(rho1ss, rho2sss) +@printf("Energy = %.8f\n", result["e_site"]) +@printf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) +@test isapprox(result["e_site"], -0.66875; atol=1e-4) +@test isapprox(mean(result["mag_norm"]), 0.3510; atol=1e-3) diff --git a/test/runtests.jl b/test/runtests.jl index 07a4cd2f0..b15ad1c6e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -53,7 +53,7 @@ end @time @safetestset "Heisenberg model" begin include("heisenberg.jl") end - @time @safetestset "Heisenberg model" begin + @time @safetestset "J1-J2 model" begin include("j1j2_model.jl") end @time @safetestset "P-wave superconductor" begin From 18948737c5efd4d36a24a9f8248041af5824a827 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 17 Nov 2024 17:32:29 +0800 Subject: [PATCH 12/75] update formatting --- src/states/suweight.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/states/suweight.jl b/src/states/suweight.jl index 6995ab9a8..a232b1c17 100644 --- a/src/states/suweight.jl +++ b/src/states/suweight.jl @@ -31,7 +31,7 @@ function Base.show(io::IO, wts::SUWeight) N1, N2 = size(wts) for (direction, r, c) in Iterators.product("xy", 1:N1, 1:N2) println(io, "$direction[$r,$c]: ") - wt = (direction == 'x' ? wts.x[r,c] : wts.y[r,c]) + wt = (direction == 'x' ? wts.x[r, c] : wts.y[r, c]) for (k, b) in blocks(wt) println(io, k, " = ", diag(b)) end From 98992980fb3cace482d440573bce50ccc4d869ff Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 18 Nov 2024 11:14:56 +0800 Subject: [PATCH 13/75] Refactor calc_convergence Co-authored-by: Lukas Devos --- src/algorithms/ctmrg/gaugefix.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/algorithms/ctmrg/gaugefix.jl b/src/algorithms/ctmrg/gaugefix.jl index 060db6335..b22aa256f 100644 --- a/src/algorithms/ctmrg/gaugefix.jl +++ b/src/algorithms/ctmrg/gaugefix.jl @@ -177,13 +177,9 @@ end Calculate convergence of CTMRG by comparing the singular values of CTM tensors """ function calc_convergence(envsNew::CTMRGEnv, envsOld::CTMRGEnv) - CSNew = map(x -> tsvd(x)[2], envsNew.corners) - TSNew = map(x -> tsvd(x)[2], envsNew.edges) CSOld = map(x -> tsvd(x)[2], envsOld.corners) TSOld = map(x -> tsvd(x)[2], envsOld.edges) - ΔCS = maximum(_singular_value_distance, zip(CSOld, CSNew)) - ΔTS = maximum(_singular_value_distance, zip(TSOld, TSNew)) - return max(ΔCS, ΔTS) + return calc_convergence(envsNew, CSOld, TSOld) end @non_differentiable calc_convergence(args...) From b4d43b5f8887d6c9d050de0313a355dcc9e60457 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 18 Nov 2024 11:19:50 +0800 Subject: [PATCH 14/75] Update sdiag_pow for latest TensorKit Co-authored-by: Lukas Devos --- src/utility/util.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utility/util.jl b/src/utility/util.jl index 5b61dd2bc..8c240903f 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -41,7 +41,7 @@ Compute S^(pow) for diagonal matrices `S` function sdiag_pow(S::AbstractTensorMap, pow::Real) S2 = similar(S) for (k, b) in blocks(S) - copyto!(blocks(S2)[k], diagm(diag(b) .^ pow)) + copyto!(block(S2, k), diagm(diag(b) .^ pow)) end return S2 end From 4bc9e46ba4034db3f512fc4fed3cab0b8cfb38ca Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 18 Nov 2024 12:35:07 +0800 Subject: [PATCH 15/75] Rename folder "timeevol" to "time_evolution" --- src/PEPSKit.jl | 4 ++-- src/algorithms/{timeevol => time_evolution}/fu_gaugefix.jl | 0 src/algorithms/{timeevol => time_evolution}/fu_optimize.jl | 0 src/algorithms/{timeevol => time_evolution}/fullupdate.jl | 0 src/algorithms/{timeevol => time_evolution}/simpleupdate.jl | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename src/algorithms/{timeevol => time_evolution}/fu_gaugefix.jl (100%) rename src/algorithms/{timeevol => time_evolution}/fu_optimize.jl (100%) rename src/algorithms/{timeevol => time_evolution}/fullupdate.jl (100%) rename src/algorithms/{timeevol => time_evolution}/simpleupdate.jl (100%) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 0726ca290..32f66b607 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -47,8 +47,8 @@ include("algorithms/ctmrg/sparse_environments.jl") include("algorithms/ctmrg/ctmrg.jl") include("algorithms/ctmrg/gaugefix.jl") -include("algorithms/timeevol/simpleupdate.jl") -include("algorithms/timeevol/fullupdate.jl") +include("algorithms/time_evolution/simpleupdate.jl") +include("algorithms/time_evolution/fullupdate.jl") include("algorithms/toolbox.jl") diff --git a/src/algorithms/timeevol/fu_gaugefix.jl b/src/algorithms/time_evolution/fu_gaugefix.jl similarity index 100% rename from src/algorithms/timeevol/fu_gaugefix.jl rename to src/algorithms/time_evolution/fu_gaugefix.jl diff --git a/src/algorithms/timeevol/fu_optimize.jl b/src/algorithms/time_evolution/fu_optimize.jl similarity index 100% rename from src/algorithms/timeevol/fu_optimize.jl rename to src/algorithms/time_evolution/fu_optimize.jl diff --git a/src/algorithms/timeevol/fullupdate.jl b/src/algorithms/time_evolution/fullupdate.jl similarity index 100% rename from src/algorithms/timeevol/fullupdate.jl rename to src/algorithms/time_evolution/fullupdate.jl diff --git a/src/algorithms/timeevol/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl similarity index 100% rename from src/algorithms/timeevol/simpleupdate.jl rename to src/algorithms/time_evolution/simpleupdate.jl From 1e97ec042fb64481211871bfb7575aff0b217ccb Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 18 Nov 2024 15:05:13 +0800 Subject: [PATCH 16/75] Replace `maxabs` by infinity norm --- src/algorithms/time_evolution/fullupdate.jl | 10 +++++----- src/algorithms/time_evolution/simpleupdate.jl | 2 +- src/utility/util.jl | 14 -------------- test/heisenberg_sufu.jl | 2 +- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/algorithms/time_evolution/fullupdate.jl b/src/algorithms/time_evolution/fullupdate.jl index 3eb85a4bf..a8d01eb3c 100644 --- a/src/algorithms/time_evolution/fullupdate.jl +++ b/src/algorithms/time_evolution/fullupdate.jl @@ -144,8 +144,8 @@ function update_column!( aR, bL, aR2bL2, env; maxiter=maxiter, maxdiff=maxdiff, verbose=false ) costs[row] = cost - aR /= maxabs(aR) - bL /= maxabs(bL) + aR /= norm(aR, Inf) + bL /= norm(bL, Inf) localfid += local_fidelity(aR, bL, _combine_aRbL(aR0, bL0)) #= update and normalize peps, ms @@ -163,7 +163,7 @@ function update_column!( ) # normalize for c_ in [col, cp1] - peps.A[row, c_] /= maxabs(peps.A[row, c_]) + peps.A[row, c_] /= norm(peps.A[row, c_], Inf) end end # update CTMRGEnv @@ -187,7 +187,7 @@ function fu_iter!( Dcut::Int, chi::Int, svderr::Float64=1e-9; - cheap=false, + cheap=true, ) Nr, Nc = size(peps) fid, maxcost = 0.0, 0.0 @@ -230,7 +230,7 @@ function fullupdate!( rgtol::Float64=1e-6, rgmaxiter::Int=10, ctmrgscheme=:sequential, - cheap=false, + cheap=true, ) time_start = time() N1, N2 = size(peps) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 451ff8c9f..6b5cc9b71 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -155,7 +155,7 @@ function _su_bondx!( # update tensor dict and weight on current bond # (max element of weight is normalized to 1) peps.A[row, col], peps.A[row2, col2] = T1, T2 - wts.x[row, col] = s / maxabs(s) + wts.x[row, col] = s / norm(s, Inf) return ϵ end diff --git a/src/utility/util.jl b/src/utility/util.jl index 8c240903f..5b894bf95 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -21,20 +21,6 @@ function _elementwise_mult(a::AbstractTensorMap, b::AbstractTensorMap) return dst end -""" -Return the maximum absolute value of tensor elements -""" -function maxabs(t::AbstractTensorMap) - maxel = 0.0 - for (k, b) in blocks(t) - maxelb = maximum(abs.(b)) - if maxelb > maxel - maxel = maxelb - end - end - return maxel -end - """ Compute S^(pow) for diagonal matrices `S` """ diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 86372e674..8786e8bcc 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -23,7 +23,7 @@ wts = SUWeight( ) # normalize peps for ind in CartesianIndices(peps.A) - peps.A[ind] /= PEPSKit.maxabs(peps.A[ind]) + peps.A[ind] /= norm(peps.A[ind], Inf) end # Heisenberg model Hamiltonian ham = gen_gate() From a1c4a2d1aa80a88ec880005262dc5cc1dcd03fa1 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 18 Nov 2024 15:21:26 +0800 Subject: [PATCH 17/75] Focus on simple update --- src/PEPSKit.jl | 2 - src/algorithms/time_evolution/fu_gaugefix.jl | 91 ------ src/algorithms/time_evolution/fu_optimize.jl | 324 ------------------- src/algorithms/time_evolution/fullupdate.jl | 321 ------------------ test/heisenberg_sufu.jl | 16 +- test/runtests.jl | 3 + 6 files changed, 4 insertions(+), 753 deletions(-) delete mode 100644 src/algorithms/time_evolution/fu_gaugefix.jl delete mode 100644 src/algorithms/time_evolution/fu_optimize.jl delete mode 100644 src/algorithms/time_evolution/fullupdate.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 32f66b607..df8bbc882 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -48,7 +48,6 @@ include("algorithms/ctmrg/ctmrg.jl") include("algorithms/ctmrg/gaugefix.jl") include("algorithms/time_evolution/simpleupdate.jl") -include("algorithms/time_evolution/fullupdate.jl") include("algorithms/toolbox.jl") @@ -176,7 +175,6 @@ export fixedpoint export absorb_wt, absorb_wt! export su_iter!, simpleupdate! -export fu_iter!, fullupdate! export meas_site, meas_bond export calrho_site, calrho_bondx, calrho_bondy, calrho_all diff --git a/src/algorithms/time_evolution/fu_gaugefix.jl b/src/algorithms/time_evolution/fu_gaugefix.jl deleted file mode 100644 index 3ee68069f..000000000 --- a/src/algorithms/time_evolution/fu_gaugefix.jl +++ /dev/null @@ -1,91 +0,0 @@ -""" -Replace `env` by its positive/negative approximant `± Z Z†` -(returns the sign and Z†) -``` - |-→ 1 2 ←-| - | | - |----env----| |←--- Z ---→| - |→ 1 2 ←| = ↑ - |← 3 4 →| |---→ Z† ←--| - |-----------| | | - |←- 3 4 -→| -``` -""" -function positive_approx(env::AbstractTensorMap) - @assert [isdual(space(env, ax)) for ax in 1:4] == [0, 0, 1, 1] - # hermitize env, and perform eigen-decomposition - # env = U D U' - D, U = eigh((env + env') / 2) - # determine env is (mostly) positive or negative - sgn = sign(mean(vcat((diag(b) for (k, b) in blocks(D))...))) - if sgn == -1 - D *= -1 - end - # set negative eigenvalues to 0 - for (k, b) in blocks(D) - for i in diagind(b) - if b[i] < 0 - b[i] = 0.0 - end - end - end - Zdg = sdiag_pow(D, 1 / 2) * U' - return sgn, Zdg -end - -""" -Fix local gauge of the env tensor around a bond -""" -function fu_fixgauge( - Zdg::AbstractTensorMap, - X::AbstractTensorMap, - Y::AbstractTensorMap, - aR::AbstractTensorMap, - bL::AbstractTensorMap, -) - #= - 1 1 - ↑ ↑ - 2 → Z† ← 3 = 2 → QR ← 3 1 ← R ← 2 - - 1 - ↑ - = 2 → L → 1 3 → QL ← 2 - =# - QR, R = leftorth(Zdg, ((1, 2), (3,)); alg=QRpos()) - QL, L = leftorth(Zdg, ((1, 3), (2,)); alg=QRpos()) - @assert !isdual(codomain(R)[1]) && !isdual(domain(R)[1]) - @assert !isdual(codomain(L)[1]) && !isdual(domain(L)[1]) - Rinv, Linv = inv(R), inv(L) - #= fix gauge of aR, bL, Z† - - ↑ - |→-(Linv -→ Z† ← Rinv)←-| - | | - ↑ ↑ - | ↑ ↑ | - |← (L ← aR) ← (bL → R) →| - |-----------------------| - - -2 -2 - ↑ ↑ - -1 ← L ← 1 ← aR2 ← -3 -1 ← bL2 → 1 → R → -3 - - -1 - ↑ - -2 → Linv → 1 → Z† ← 2 ← Rinv ← -3 - =# - aR = ncon([L, aR], [[-1, 1], [1, -2, -3]]) - bL = ncon([bL, R], [[-1, -2, 1], [-3, 1]]) - Zdg = permute(ncon([Zdg, Linv, Rinv], [[-1, 1, 2], [1, -2], [2, -3]]), (1,), (2, 3)) - #= fix gauge of X, Y - -1 -1 - | | - -4 - X ← 1 ← Linv ← -2 -4 → Rinv → 1 → Y - -2 - | | - -3 -3 - =# - X = ncon([X, Linv], [[-1, 1, -3, -4], [1, -2]]) - Y = ncon([Y, Rinv], [[-1, -2, -3, 1], [1, -4]]) - return Zdg, X, Y, aR, bL -end diff --git a/src/algorithms/time_evolution/fu_optimize.jl b/src/algorithms/time_evolution/fu_optimize.jl deleted file mode 100644 index de52d3b56..000000000 --- a/src/algorithms/time_evolution/fu_optimize.jl +++ /dev/null @@ -1,324 +0,0 @@ -""" -Construct the environment (norm) tensor -``` - left half right half - C1 -χ4 - T1 ------- χ6 ------- T1 - χ8 - C2 r-1 - | ‖ ‖ | - χ2 DNX DNY χ10 - | ‖ ‖ | - T4 =DWX= XX = DX = = DY = YY =DEY= T2 r - | ‖ ‖ | - χ1 DSX DSY χ9 - | ‖ ‖ | - C4 -χ3 - T3 ------- χ5 ------- T3 - χ7 - C3 r+1 - c-1 c c+1 c+2 -``` -which can be more simply denoted as -``` - |------------| - |→ DX1 DY1 ←| axis order - |← DX0 DX1 →| (DX1, DY1, DX0, DY0) - |------------| -``` -The axes 1, 2 (or 3, 4) come from X†, Y† (or X, Y) -""" -function tensor_env( - row::Int, col::Int, X::AbstractTensorMap, Y::AbstractTensorMap, envs::CTMRGEnv -) - Nr, Nc = size(envs.corners)[[2, 3]] - cm1 = _prev(col, Nc) - cp1 = _next(col, Nc) - cp2 = _next(cp1, Nc) - rm1 = _prev(row, Nr) - rp1 = _next(row, Nr) - c1 = envs.corners[1, rm1, cm1] - c2 = envs.corners[2, rm1, cp2] - c3 = envs.corners[3, rp1, cp2] - c4 = envs.corners[4, rp1, cm1] - t1X, t1Y = envs.edges[1, rm1, col], envs.edges[1, rm1, cp1] - t2 = envs.edges[2, row, cp2] - t3X, t3Y = envs.edges[3, rp1, col], envs.edges[3, rp1, cp1] - t4 = envs.edges[4, row, cm1] - # left half - @autoopt @tensor lhalf[DX1, DX0, χ5, χ6] := ( - c4[χ3, χ1] * - t4[χ1, DWX0, DWX1, χ2] * - c1[χ2, χ4] * - t3X[χ5, DSX0, DSX1, χ3] * - X[DNX0, DX0, DSX0, DWX0] * - conj(X[DNX1, DX1, DSX1, DWX1]) * - t1X[χ4, DNX0, DNX1, χ6] - ) - # right half - @autoopt @tensor rhalf[DY1, DY0, χ5, χ6] := ( - c3[χ9, χ7] * - t2[χ10, DEY0, DEY1, χ9] * - c2[χ8, χ10] * - t3Y[χ7, DSY0, DSY1, χ5] * - Y[DNY0, DEY0, DSY0, DY0] * - conj(Y[DNY1, DEY1, DSY1, DY1]) * - t1Y[χ6, DNY0, DNY1, χ8] - ) - # combine - @autoopt @tensor env[DX1, DY1; DX0, DY0] := ( - lhalf[DX1, DX0, χ5, χ6] * rhalf[DY1, DY0, χ5, χ6] - ) - return env -end - -""" -Construct the tensor -``` - |------------env------------| - |→ DX1 Db1 → bL† ← DY1 ←| - | ↑ | - | db | - | ↑ | - |← DX0 Db0 ← bL -→ DY0 →| - |---------------------------| -``` -""" -function tensor_Ra(env::AbstractTensorMap, bL::AbstractTensorMap) - @autoopt @tensor Ra[DX1, Db1, DX0, Db0] := ( - env[DX1, DY1, DX0, DY0] * bL[Db0, db, DY0] * conj(bL[Db1, db, DY1]) - ) - return Ra -end - -""" -Construct the tensor -``` - |-----------env-----------| - |→ DX1 Db1 → bL† ← DY1 ←| - | ↑ | - | da db | - | ↑ ↑ | - |← DX0 ←- aR2 bL2 -→ DY0 →| - |-------------------------| -``` -""" -function tensor_Sa(env::AbstractTensorMap, bL::AbstractTensorMap, aR2bL2::AbstractTensorMap) - @autoopt @tensor Sa[DX1, Db1, da] := ( - env[DX1, DY1, DX0, DY0] * conj(bL[Db1, db, DY1]) * aR2bL2[DX0, da, db, DY0] - ) - return Sa -end - -""" -Construct the tensor -``` - |------------env------------| - |→ DX1 → aR† → Da1 DY1 ←| - | ↑ | - | da | - | ↑ | - |← DX0 ← aR ←- Da0 DY0 →| - |---------------------------| -``` -""" -function tensor_Rb(env::AbstractTensorMap, aR::AbstractTensorMap) - @autoopt @tensor Rb[Da1, DY1, Da0, DY0] := ( - env[DX1, DY1, DX0, DY0] * aR[DX0, da, Da0] * conj(aR[DX1, da, Da1]) - ) - return Rb -end - -""" -Construct the tensor -``` - |-----------env-----------| - |→ DX1 → aR† → Da1 DY1 ←| - | ↑ | - | da db | - | ↑ ↑ | - |← DX0 ←- aR2 bL2 -→ DY0 →| - |-------------------------| -``` -""" -function tensor_Sb(env::AbstractTensorMap, aR::AbstractTensorMap, aR2bL2::AbstractTensorMap) - @autoopt @tensor Sb[Da1, DY1, db] := ( - env[DX1, DY1, DX0, DY0] * conj(aR[DX1, da, Da1]) * aR2bL2[DX0, da, db, DY0] - ) - return Sb -end - -""" -Calculate the norm -``` - |----------env----------| - |→ DX1 → aR1bL1† ← DY1 ←| - | ↑ ↑ | - | da db | - | ↑ ↑ | - |← DX0 ← aR2bL2 → DY0 -→| - |-----------------------| -``` -""" -function inner_prod( - env::AbstractTensorMap, aR1bL1::AbstractTensorMap, aR2bL2::AbstractTensorMap -) - @autoopt @tensor t[:] := ( - env[DX1, DY1, DX0, DY0] * conj(aR1bL1[DX1, da, db, DY1]) * aR2bL2[DX0, da, db, DY0] - ) - return first(blocks(t))[2][1] -end - -""" -Contract the axis between `aR` and `bL` tensors -""" -function _combine_aRbL(aR::AbstractTensorMap, bL::AbstractTensorMap) - #= - da db - ↑ ↑ - ← DX ← aR ← D ← bL → DY → - =# - @tensor aRbL[DX, da, db, DY] := aR[DX, da, D] * bL[D, db, DY] - return aRbL -end - -""" -Calculate the cost function -``` - f(a,b) = | |Psi(a,b)> - |Psi(a2,b2)> |^2 - = + - - 2 Re -``` -""" -function cost_func( - env::AbstractTensorMap, - aR::AbstractTensorMap, - bL::AbstractTensorMap, - aR2bL2::AbstractTensorMap, -) - aRbL = _combine_aRbL(aR, bL) - t1 = inner_prod(env, aRbL, aRbL) - t2 = inner_prod(env, aR2bL2, aR2bL2) - t3 = inner_prod(env, aRbL, aR2bL2) - return real(t1) + real(t2) - 2 * real(t3) -end - -""" -Calculate the approximate local inner product -`` -``` - |→ aR1bL1† ←| - | ↑ ↑ | - DW da db DE - | ↑ ↑ | - |← aR2 bL2 →| -``` -""" -function inner_prod_local(aR1bL1::AbstractTensorMap, aR2bL2::AbstractTensorMap) - @autoopt @tensor t[:] := (conj(aR1bL1[DW, da, db, DE]) * aR2bL2[DW, da, db, DE]) - return first(blocks(t))[2][1] -end - -""" -Calculate the fidelity using aR, bL -between two evolution steps -``` - || - --------------------------------------------- - sqrt( ) -``` -""" -function local_fidelity( - aR1::AbstractTensorMap, bL1::AbstractTensorMap, aR2bL2::AbstractTensorMap -) - aR1bL1 = _combine_aRbL(aR1, bL1) - b12 = inner_prod_local(aR1bL1, aR2bL2) - b11 = inner_prod_local(aR1bL1, aR1bL1) - b22 = inner_prod_local(aR2bL2, aR2bL2) - return abs(b12) / sqrt(abs(b11 * b22)) -end - -""" -Solving the equations -``` - Ra aR = Sa, Rb bL = Sb -``` -""" -function solve_ab(R::AbstractTensorMap, S::AbstractTensorMap, ab0::AbstractTensorMap) - f(x) = ncon((R, x), ([-1, -2, 1, 2], [1, 2, -3])) - ab, info = linsolve(f, S, permute(ab0, (1, 3, 2)), 0, 1) - return permute(ab, (1, 3, 2)), info -end - -""" -Minimize the cost function -``` - fix bL: - d(aR,aR†) = aR† Ra aR - aR† Sa - Sa† aR + T - minimized by Ra aR = Sa - - fix aR: - d(bL,bL†) = bL† Rb bL - bL† Sb - Sb† bL + T - minimized by Rb bL = Sb -``` -`aR0`, `bL0` are initial values of `aR`, `bL` -""" -function fu_optimize( - aR0::AbstractTensorMap, - bL0::AbstractTensorMap, - aR2bL2::AbstractTensorMap, - env::AbstractTensorMap; - maxiter::Int=50, - maxdiff::Float64=1e-15, - check_int::Int=1, - verbose::Bool=false, -) - if verbose - println("---- Iterative optimization ----") - @printf("%-6s%12s%12s%12s %10s\n", "Step", "Cost", "ϵ_d", "ϵ_ab", "Time/s") - end - aR, bL = deepcopy(aR0), deepcopy(bL0) - time0 = time() - cost00 = cost_func(env, aR, bL, aR2bL2) - fid00 = local_fidelity(aR, bL, aR2bL2) - cost0, fid0 = cost00, fid00 - # no need to further optimize - if abs(cost0) < 5e-15 - if verbose - time1 = time() - println( - @sprintf( - "%-6d%12.3e%12.3e%12.3e %10.3f\n", 0, cost0, NaN, NaN, time1 - time0 - ) - ) - end - return aR, bL, cost0 - end - for count in 1:maxiter - time0 = time() - Ra = tensor_Ra(env, bL) - Sa = tensor_Sa(env, bL, aR2bL2) - aR, info_a = solve_ab(Ra, Sa, aR) - Rb = tensor_Rb(env, aR) - Sb = tensor_Sb(env, aR, aR2bL2) - bL, info_b = solve_ab(Rb, Sb, bL) - cost = cost_func(env, aR, bL, aR2bL2) - fid = local_fidelity(aR, bL, aR2bL2) - diff_d = abs(cost - cost0) / cost00 - diff_ab = abs(fid - fid0) / fid00 - time1 = time() - if verbose && (count == 1 || count % check_int == 0) - @printf( - "%-6d%12.3e%12.3e%12.3e %10.3f\n", - count, - cost, - diff_d, - diff_ab, - time1 - time0 - ) - end - if diff_ab < maxdiff - break - end - aR0, bL0 = deepcopy(aR), deepcopy(bL) - cost0, fid0 = cost, fid - if count == maxiter - println("Warning: max iter $maxiter reached for optimization") - end - end - return aR, bL, cost0 -end diff --git a/src/algorithms/time_evolution/fullupdate.jl b/src/algorithms/time_evolution/fullupdate.jl deleted file mode 100644 index a8d01eb3c..000000000 --- a/src/algorithms/time_evolution/fullupdate.jl +++ /dev/null @@ -1,321 +0,0 @@ -include("fu_gaugefix.jl") -include("fu_optimize.jl") - -# TODO: add option to use full-infinite environment -# for CTMRG moves when it is implemented in PEPSKit - -""" -CTMRG left-move to update CTMRGEnv in the c-th column -``` - ---> absorb - C1 ← T1 ← r-1 - ↓ ‖ - T4 = M' = r - ↓ ‖ - C4 → T3 → r+1 - c-1 c -``` -""" -function ctmrg_leftmove!( - col::Int, - peps::InfinitePEPS, - envs::CTMRGEnv, - chi::Int, - svderr::Float64=1e-9; - cheap::Bool=true, -) - trscheme = truncerr(svderr) & truncdim(chi) - alg = CTMRG(; - verbosity=0, miniter=1, maxiter=10, trscheme=trscheme, ctmrgscheme=:sequential - ) - envs2, info = ctmrg_leftmove(col, peps, envs, alg) - envs.corners[:, :, col] = envs2.corners[:, :, col] - envs.edges[:, :, col] = envs2.edges[:, :, col] - return info -end - -""" -CTMRG right-move to update CTMRGEnv in the c-th column -``` - absorb <--- - ←-- T1 ← C2 r-1 - ‖ ↑ - === M' = T2 r - ‖ ↑ - --→ T3 → C3 r+1 - c c+1 -``` -""" -function ctmrg_rightmove!( - col::Int, - peps::InfinitePEPS, - envs::CTMRGEnv, - chi::Int, - svderr::Float64=1e-9; - cheap::Bool=true, -) - Nr, Nc = size(peps) - @assert 1 <= col <= Nc - PEPSKit.rot180!(envs) - ctmrg_leftmove!(Nc + 1 - col, rot180(peps), envs, chi, svderr) - PEPSKit.rot180!(envs) - return nothing -end - -""" -Update all horizontal bonds in the c-th column -(i.e. `(r,c) (r,c+1)` for all `r = 1, ..., Nr`). -To update rows, rotate the network clockwise by 90 degrees. -""" -function update_column!( - col::Int, - gate::AbstractTensorMap, - peps::InfinitePEPS, - envs::CTMRGEnv, - Dcut::Int, - chi::Int; - svderr::Float64=1e-9, - maxiter::Int=50, - maxdiff::Float64=1e-15, - cheap=true, - gaugefix::Bool=true, -) - Nr, Nc = size(peps) - @assert 1 <= col <= Nc - localfid = 0.0 - costs = zeros(Nr) - truncscheme = truncerr(svderr) & truncdim(Dcut) - #= Axis order of X, aR, Y, bL - - 1 2 2 1 - | ↗ ↗ | - 4 - X ← 2 1 ← aR ← 3 1 ← bL → 3 4 → Y - 2 - | | - 3 3 - =# - for row in 1:Nr - cp1 = _next(col, Nc) - A, B = peps[row, col], peps[row, cp1] - # TODO: relax dual requirement on the bonds - @assert !isdual(domain(A)[2]) - #= QR and LQ decomposition - - 2 1 1 2 - | ↗ | ↗ - 5 - A ← 3 ====> 4 - X ← 2 1 ← aR ← 3 - | | - 4 3 - =# - X, aR0 = leftorth(A, ((2, 4, 5), (1, 3)); alg=QRpos()) - X = permute(X, (1, 4, 2, 3)) - #= - 2 1 2 2 - | ↗ ↗ | - 5 → B - 3 ====> 1 ← bL → 3 1 → Y - 3 - | | - 4 4 - =# - Y, bL0 = leftorth(B, ((2, 3, 4), (1, 5)); alg=QRpos()) - bL0 = permute(bL0, (3, 2, 1)) - env = tensor_env(row, col, X, Y, envs) - # positive/negative-definite approximant: env = ± Z Z† - sgn, Zdg = positive_approx(env) - # fix gauge - if gaugefix - Zdg, X, Y, aR0, bL0 = fu_fixgauge(Zdg, X, Y, aR0, bL0) - end - env = sgn * Zdg' * Zdg - #= apply gate - - -2 -3 - ↑ ↑ - |----gate---| - ↑ ↑ - 1 2 - ↑ ↑ - -1← aR -← 3 -← bL → -4 - =# - aR2bL2 = ncon((gate, aR0, bL0), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) - # initialize truncated tensors using SVD truncation - aR, s_cut, bL, ϵ = tsvd(aR2bL2, ((1, 2), (3, 4)); trunc=truncscheme) - aR, bL = absorb_s(aR, s_cut, bL) - # optimize aR, bL - aR, bL, cost = fu_optimize( - aR, bL, aR2bL2, env; maxiter=maxiter, maxdiff=maxdiff, verbose=false - ) - costs[row] = cost - aR /= norm(aR, Inf) - bL /= norm(bL, Inf) - localfid += local_fidelity(aR, bL, _combine_aRbL(aR0, bL0)) - #= update and normalize peps, ms - - -2 -1 -1 -2 - | ↗ ↗ | - -5- X ← 1 ← aR ← -3 -5 ← bL → 1 → Y - -3 - | | - -4 -4 - =# - peps.A[row, col] = permute( - ncon([X, aR], [[-2, 1, -4, -5], [1, -1, -3]]), (1,), Tuple(2:5) - ) - peps.A[row, cp1] = permute( - ncon([bL, Y], [[-5, -1, 1], [-2, -3, -4, 1]]), (1,), Tuple(2:5) - ) - # normalize - for c_ in [col, cp1] - peps.A[row, c_] /= norm(peps.A[row, c_], Inf) - end - end - # update CTMRGEnv - ctmrg_leftmove!(col, peps, envs, chi, svderr; cheap=cheap) - ctmrg_rightmove!(_next(col, Nc), peps, envs, chi, svderr; cheap=cheap) - return localfid, costs -end - -""" -One round of full update on the input InfinitePEPS `peps` and its CTMRGEnv `envs` - -When `cheap === true`, use half-infinite environment to construct CTMRG projectors. -Otherwise, use full-infinite environment instead. - -Reference: Physical Review B 92, 035142 (2015) -""" -function fu_iter!( - gate::AbstractTensorMap, - peps::InfinitePEPS, - envs::CTMRGEnv, - Dcut::Int, - chi::Int, - svderr::Float64=1e-9; - cheap=true, -) - Nr, Nc = size(peps) - fid, maxcost = 0.0, 0.0 - for col in 1:Nc - tmpfid, costs = update_column!( - col, gate, peps, envs, Dcut, chi; svderr=svderr, cheap=cheap - ) - fid += tmpfid - maxcost = max(maxcost, maximum(costs)) - end - rotr90!(peps) - rotr90!(envs) - for row in 1:Nr - tmpfid, costs = update_column!( - row, gate, peps, envs, Dcut, chi; svderr=svderr, cheap=cheap - ) - fid += tmpfid - maxcost = max(maxcost, maximum(costs)) - end - rotl90!(peps) - rotl90!(envs) - fid /= (2 * Nr * Nc) - return fid, maxcost -end - -# TODO: pass Hamiltonian gate as `LocalOperator` -""" -Perform full update -""" -function fullupdate!( - peps::InfinitePEPS, - envs::CTMRGEnv, - ham::AbstractTensorMap, - dt::Float64, - Dcut::Int, - chi::Int; - evolstep::Int=5000, - svderr::Float64=1e-9, - rgint::Int=10, - rgtol::Float64=1e-6, - rgmaxiter::Int=10, - ctmrgscheme=:sequential, - cheap=true, -) - time_start = time() - N1, N2 = size(peps) - # CTMRG algorithm to reconverge environment - ctm_alg = CTMRG(; - tol=rgtol, - maxiter=rgmaxiter, - miniter=1, - verbosity=2, - trscheme=truncerr(svderr) & truncdim(chi), - svd_alg=SVDAdjoint(; fwd_alg=TensorKit.SDD()), - ctmrgscheme=ctmrgscheme, - ) - @printf( - "%-4s %7s%10s%12s%11s %s/%s\n", - "step", - "dt", - "energy", - "Δe", - "svd_diff", - "speed", - "meas(s)" - ) - gate = exp(-dt * ham) - esite0, peps0, envs0 = Inf, deepcopy(peps), deepcopy(envs) - diff_energy = 0.0 - for count in 1:evolstep - time0 = time() - fid, cost = fu_iter!(gate, peps, envs, Dcut, chi, svderr; cheap=cheap) - time1 = time() - if count == 1 || count % rgint == 0 - meast0 = time() - # reconverge `env` (in place) - println(stderr, "---- FU step $count: reconverging envs ----") - envs2 = leading_boundary(envs, peps, ctm_alg) - envs.edges[:], envs.corners[:] = envs2.edges, envs2.corners - # TODO: monitor energy with costfun - # esite = costfun(peps, envs, ham) - rho2sss = calrho_allnbs(envs, peps) - ebonds = [collect(meas_bond(ham, rho2) for rho2 in rho2sss[n]) for n in 1:2] - esite = sum(sum(ebonds)) / (N1 * N2) - meast1 = time() - # monitor change of CTMRGEnv by its singular values - diff_energy = esite - esite0 - diff_ctm = calc_convergence(envs, envs0) - @printf( - "%-4d %7.0e%10.5f%12.3e%11.3e %.3f/%.3f\n", - count, - dt, - esite, - diff_energy, - diff_ctm, - time1 - time0, - meast1 - meast0 - ) - if diff_energy > 0 - @printf("Energy starts to increase. Abort evolution.\n") - # restore peps and envs at last checking - peps.A[:] = peps0.A - envs.corners[:], envs.edges[:] = envs0.corners, envs0.edges - break - end - esite0, peps0, envs0 = esite, deepcopy(peps), deepcopy(envs) - end - end - # reconverge the environment tensors - for io in (stdout, stderr) - @printf(io, "Reconverging final envs ... \n") - end - envs2 = leading_boundary( - envs, - peps, - CTMRG(; - tol=1e-10, - maxiter=50, - miniter=1, - verbosity=2, - trscheme=truncerr(svderr) & truncdim(chi), - svd_alg=SVDAdjoint(; fwd_alg=TensorKit.SDD()), - ctmrgscheme=ctmrgscheme, - ), - ) - envs.edges[:], envs.corners[:] = envs2.edges, envs2.corners - time_end = time() - @printf("Evolution time: %.3f s\n\n", time_end - time_start) - print(stderr, "\n----------\n\n") - return esite0, diff_energy -end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 8786e8bcc..60112dea5 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -8,8 +8,7 @@ include("utility/heis.jl") import .OpsHeis: gen_gate import .RhoMeasureHeis: measrho_all -# benchmark data for D = 3 is from -# Phys. Rev. B 94, 035133 (2016) +# benchmark data is from Phys. Rev. B 94, 035133 (2016) # random initialization of 2x2 iPEPS and CTMRGEnv (using real numbers) Dcut, χenv = 4, 16 @@ -49,16 +48,3 @@ result = measrho_all(rho1ss, rho2sss) @printf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) @test isapprox(result["e_site"], -0.6675; atol=1e-3) @test isapprox(mean(result["mag_norm"]), 0.3767; atol=1e-3) - -# continue with full update -dts = [2e-2, 1e-2, 5e-3, 1e-3, 5e-4] -for dt in dts - fullupdate!(peps, envs, ham, dt, Dcut, χenv; rgmaxiter=5, cheap=true) -end -# measure physical quantities -rho1ss, rho2sss = calrho_all(envs, peps) -result = measrho_all(rho1ss, rho2sss) -@printf("Energy = %.8f\n", result["e_site"]) -@printf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) -@test isapprox(result["e_site"], -0.66875; atol=1e-4) -@test isapprox(mean(result["mag_norm"]), 0.3510; atol=1e-3) diff --git a/test/runtests.jl b/test/runtests.jl index b15ad1c6e..19008ebb8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -53,6 +53,9 @@ end @time @safetestset "Heisenberg model" begin include("heisenberg.jl") end + @time @safetestset "Heisenberg model (simple and full update)" begin + include("heisenberg_sufu.jl") + end @time @safetestset "J1-J2 model" begin include("j1j2_model.jl") end From 65533e451bf86e7c97777227633fdba8ad94a21d Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Tue, 19 Nov 2024 00:04:24 +0800 Subject: [PATCH 18/75] implement `InfiniteWeightPEPS` --- src/PEPSKit.jl | 6 +- src/algorithms/time_evolution/simpleupdate.jl | 110 ++++-------- src/states/infiniteweightpeps.jl | 163 ++++++++++++++++++ src/states/suweight.jl | 62 ------- test/heisenberg_sufu.jl | 19 +- 5 files changed, 210 insertions(+), 150 deletions(-) create mode 100644 src/states/infiniteweightpeps.jl delete mode 100644 src/states/suweight.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index df8bbc882..b1e386415 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -24,7 +24,7 @@ include("utility/autoopt.jl") include("states/abstractpeps.jl") include("states/infinitepeps.jl") -include("states/suweight.jl") +include("states/infiniteweightpeps.jl") include("operators/transferpeps.jl") include("operators/infinitepepo.jl") @@ -173,13 +173,13 @@ export leading_boundary export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint -export absorb_wt, absorb_wt! +export absorb_wt export su_iter!, simpleupdate! export meas_site, meas_bond export calrho_site, calrho_bondx, calrho_bondy, calrho_all -export SUWeight export InfinitePEPS, InfiniteTransferPEPS +export SUWeight, InfiniteWeightPEPS export InfinitePEPO, InfiniteTransferPEPO export initializeMPS, initializePEPS export ReflectDepth, ReflectWidth, Rotate, RotateReflect diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 6b5cc9b71..eff5e6718 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -1,20 +1,3 @@ -""" -Mirror the unit cell of an iPEPS by its anti-diagonal line -""" -function mirror_antidiag!(peps::InfinitePEPS) - peps.A[:] = mirror_antidiag(peps.A) - for (i, t) in enumerate(peps.A) - peps.A[i] = permute(t, (1,), (3, 2, 5, 4)) - end -end - -""" -Mirror the unit cell of an iPEPS with weights by its anti-diagonal line -""" -function mirror_antidiag!(wts::SUWeight) - return wts.x[:], wts.y[:] = mirror_antidiag(wts.y), mirror_antidiag(wts.x) -end - """ Absorb environment weight on axis `ax` into tensor `t` at position `(row,col)` @@ -34,22 +17,22 @@ function absorb_wt( row::Int, col::Int, ax::Int, - wts::SUWeight; + weights::SUWeight; sqrtwt::Bool=false, invwt::Bool=false, ) - Nr, Nc = size(wts) + Nr, Nc = size(weights) @assert 1 <= row <= Nr && 1 <= col <= Nc @assert 2 <= ax <= 5 pow = (sqrtwt ? 1 / 2 : 1) * (invwt ? -1 : 1) if ax == 2 # north - wt = wts.y[row, col] + wt = weights.y[row, col] elseif ax == 3 # east - wt = wts.x[row, col] + wt = weights.x[row, col] elseif ax == 4 # south - wt = wts.y[_next(row, Nr), col] + wt = weights.y[_next(row, Nr), col] else # west - wt = wts.x[row, _prev(col, Nc)] + wt = weights.x[row, _prev(col, Nc)] end wt2 = sdiag_pow(wt, pow) indices_t = collect(-1:-1:-5) @@ -61,20 +44,7 @@ function absorb_wt( end """ -Absorb bond weights into iPEPS site tensors -""" -function absorb_wt!(peps::InfinitePEPS, wts::SUWeight) - N1, N2 = size(peps) - for (r, c) in Iterators.product(1:N1, 1:N2) - for ax in 2:5 - peps.A[r, c] = absorb_wt(peps.A[r, c], r, c, ax, wts; sqrtwt=true) - end - end - return nothing -end - -""" -Simple update of bond `wts.x[r,c]` +Simple update of bond `peps.weights.x[r,c]` ``` y[r,c] y[r,c+1] ↓ ↓ @@ -87,25 +57,24 @@ function _su_bondx!( row::Int, col::Int, gate::AbstractTensorMap, - peps::InfinitePEPS, - wts::SUWeight, + peps::InfiniteWeightPEPS, Dcut::Int, svderr::Float64=1e-10, ) Nr, Nc = size(peps) @assert 1 <= row <= Nr && 1 <= col <= Nc row2, col2 = row, _next(col, Nc) - T1, T2 = peps[row, col], peps[row2, col2] + T1, T2 = peps.vertices[row, col], peps.vertices[row2, col2] # absorb environment weights for ax in (2, 4, 5) - T1 = absorb_wt(T1, row, col, ax, wts) + T1 = absorb_wt(T1, row, col, ax, peps.weights) end for ax in (2, 3, 4) - T2 = absorb_wt(T2, row2, col2, ax, wts) + T2 = absorb_wt(T2, row2, col2, ax, peps.weights) end # absorb bond weight - T1 = absorb_wt(T1, row, col, 3, wts; sqrtwt=true) - T2 = absorb_wt(T2, row2, col2, 5, wts; sqrtwt=true) + T1 = absorb_wt(T1, row, col, 3, peps.weights; sqrtwt=true) + T2 = absorb_wt(T2, row2, col2, 5, peps.weights; sqrtwt=true) #= QR and LQ decomposition 2 1 1 2 @@ -147,29 +116,28 @@ function _su_bondx!( T2 = ncon((bL, Y), ([-5, -1, 1], [1, -2, -3, -4])) # remove environment weights for ax in (2, 4, 5) - T1 = absorb_wt(T1, row, col, ax, wts; invwt=true) + T1 = absorb_wt(T1, row, col, ax, peps.weights; invwt=true) end for ax in (2, 3, 4) - T2 = absorb_wt(T2, row2, col2, ax, wts; invwt=true) + T2 = absorb_wt(T2, row2, col2, ax, peps.weights; invwt=true) end # update tensor dict and weight on current bond # (max element of weight is normalized to 1) - peps.A[row, col], peps.A[row2, col2] = T1, T2 - wts.x[row, col] = s / norm(s, Inf) + peps.vertices[row, col], peps.vertices[row2, col2] = T1, T2 + peps.weights.x[row, col] = s / norm(s, Inf) return ϵ end """ -One round of simple update on the input InfinitePEPS `peps` -and SUWeight `wts` with the nearest neighbor gate `gate` +One round of simple update on the input +InfiniteWeightPEPS `peps` with the nearest neighbor gate `gate` When `bipartite === true` (for square lattice), the unit cell size should be 2 x 2, and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` """ function su_iter!( gate::AbstractTensorMap, - peps::InfinitePEPS, - wts::SUWeight, + peps::InfiniteWeightPEPS, Dcut::Int, svderr::Float64=1e-10; bipartite::Bool=false, @@ -180,33 +148,31 @@ function su_iter!( end # TODO: make algorithm independent on the choice of dual in the network for (r, c) in Iterators.product(1:Nr, 1:Nc) - @assert [isdual(space(peps.A[r, c], ax)) for ax in 1:5] == [0, 1, 1, 0, 0] - @assert [isdual(space(wts.x[r, c], ax)) for ax in 1:2] == [0, 1] - @assert [isdual(space(wts.y[r, c], ax)) for ax in 1:2] == [0, 1] + @assert [isdual(space(peps.vertices[r, c], ax)) for ax in 1:5] == [0, 1, 1, 0, 0] + @assert [isdual(space(peps.weights.x[r, c], ax)) for ax in 1:2] == [0, 1] + @assert [isdual(space(peps.weights.y[r, c], ax)) for ax in 1:2] == [0, 1] end for direction in 1:2 # mirror the y-weights to x-direction # to update them using code for x-weights if direction == 2 mirror_antidiag!(peps) - mirror_antidiag!(wts) end if bipartite - ϵ = _su_bondx!(1, 1, gate, peps, wts, Dcut, svderr) - (peps.A[2, 2], peps.A[2, 1], wts.x[2, 2]) = - deepcopy.((peps.A[1, 1], peps.A[1, 2], wts.x[1, 1])) - ϵ = _su_bondx!(2, 1, gate, peps, wts, Dcut, svderr) - (peps.A[1, 2], peps.A[1, 1], wts.x[1, 2]) = - deepcopy.((peps.A[2, 1], peps.A[2, 2], wts.x[2, 1])) + ϵ = _su_bondx!(1, 1, gate, peps, Dcut, svderr) + (peps.vertices[2, 2], peps.vertices[2, 1], peps.weights.x[2, 2]) = + deepcopy.((peps.vertices[1, 1], peps.vertices[1, 2], peps.weights.x[1, 1])) + ϵ = _su_bondx!(2, 1, gate, peps, Dcut, svderr) + (peps.vertices[1, 2], peps.vertices[1, 1], peps.weights.x[1, 2]) = + deepcopy.((peps.vertices[2, 1], peps.vertices[2, 2], peps.weights.x[2, 1])) else - for site in CartesianIndices(peps.A) + for site in CartesianIndices(peps.vertices) row, col = Tuple(site) - ϵ = _su_bondx!(row, col, gate, peps, wts, Dcut) + ϵ = _su_bondx!(row, col, gate, peps, Dcut) end end if direction == 2 mirror_antidiag!(peps) - mirror_antidiag!(wts) end end return nothing @@ -214,8 +180,7 @@ end function compare_weights(wts1::SUWeight, wts2::SUWeight) wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1, wts2)) - wtdiff /= 2 * prod(size(wts1)) - return wtdiff + return wtdiff / (2 * prod(size(wts1))) end """ @@ -224,8 +189,7 @@ with nearest neighbor Hamiltonian `ham` and time step `dt` until the change of bond weights is smaller than `wtdiff_tol` """ function simpleupdate!( - peps::InfinitePEPS, - wts::SUWeight, + peps::InfiniteWeightPEPS, ham::AbstractTensorMap, dt::Float64, Dcut::Int; @@ -244,13 +208,13 @@ function simpleupdate!( # exponentiating the 2-site Hamiltonian gate gate = exp(-dt * ham) wtdiff = 1e+3 - wts0 = deepcopy(wts) + wts0 = deepcopy(peps.weights) for count in 1:evolstep time0 = time() - su_iter!(gate, peps, wts, Dcut, svderr; bipartite=bipartite) - wtdiff = compare_weights(wts, wts0) + su_iter!(gate, peps, Dcut, svderr; bipartite=bipartite) + wtdiff = compare_weights(peps.weights, wts0) stop = (wtdiff < wtdiff_tol) || (count == evolstep) - wts0 = deepcopy(wts) + wts0 = deepcopy(peps.weights) time1 = time() if ((count == 1) || (count % check_int == 0) || stop) @printf("%-9d%6.0e%12.3e %.3f\n", count, dt, wtdiff, time1 - time0) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl new file mode 100644 index 000000000..22cedc24e --- /dev/null +++ b/src/states/infiniteweightpeps.jl @@ -0,0 +1,163 @@ + +""" + const PEPSWeight{S} + +Default type for PEPS bond weights with 2 virtual indices, +conventionally ordered as: ``wt : ES ← WN``. +Here, `ES`, `WN` denote the east/south, west/north spaces, respectively. +""" +const PEPSWeight{S} = AbstractTensorMap{S,1,1} where {S<:ElementarySpace} + +""" +Schmidt bond weight used in simple/cluster update +""" +struct SUWeight{E<:PEPSWeight} + x::Matrix{E} + y::Matrix{E} +end + +function Base.size(wts::SUWeight) + @assert size(wts.x) == size(wts.y) + return size(wts.x) +end + +function Base.eltype(wts::SUWeight) + @assert eltype(wts.x) == eltype(wts.y) + return eltype(wts.x) +end + +function Base.:(==)(wts1::SUWeight, wts2::SUWeight) + return wts1.x == wts2.x && wts1.y == wts2.y +end + +function Base.:(+)(wts1::SUWeight, wts2::SUWeight) + return SUWeight(wts1.x + wts2.x, wts1.y + wts2.y) +end + +function Base.:(-)(wts1::SUWeight, wts2::SUWeight) + return SUWeight(wts1.x - wts2.x, wts1.y - wts2.y) +end + +function Base.show(io::IO, wts::SUWeight) + N1, N2 = size(wts) + for (direction, r, c) in Iterators.product("xy", 1:N1, 1:N2) + println(io, "$direction[$r,$c]: ") + wt = (direction == 'x' ? wts.x[r, c] : wts.y[r, c]) + for (k, b) in blocks(wt) + println(io, k, " = ", diag(b)) + end + end +end + +function Base.iterate(wts::SUWeight, state=1) + nx = prod(size(wts.x)) + if 1 <= state <= nx + return wts.x[state], state + 1 + elseif nx + 1 <= state <= 2 * nx + return wts.y[state - nx], state + 1 + else + return nothing + end +end + +function Base.length(wts::SUWeight) + @assert size(wts.x) == size(wts.y) + return 2 * prod(size(wts.x)) +end + +function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) + return ( + isapprox(wts1.x, wts2.x; atol=atol, rtol=rtol) && + isapprox(wts1.y, wts2.y; atol=atol, rtol=rtol) + ) +end + +""" +Represents an infinite projected entangled-pair state on a 2D square lattice +consisting of vertex tensors and bond weights +""" +struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight} <: AbstractPEPS + vertices::Matrix{T} + weights::SUWeight{E} + + function InfiniteWeightPEPS( + vertices::Matrix{T}, weights::SUWeight{E} + ) where {T<:PEPSTensor,E<:PEPSWeight} + @assert size(vertices) == size(weights) + Nr, Nc = size(vertices) + for (r, c) in Iterators.product(1:Nr, 1:Nc) + space(weights.y[r, c], 1)' == space(vertices[r, c], 2) || throw( + SpaceMismatch("South space of bond weight y$((r, c)) does not match.") + ) + space(weights.y[r, c], 2)' == space(vertices[_prev(r, Nr), c], 4) || throw( + SpaceMismatch("North space of bond weight y$((r, c)) does not match.") + ) + space(weights.x[r, c], 1)' == space(vertices[r, c], 3) || + throw(SpaceMismatch("West space of bond weight x$((r, c)) does not match.")) + space(weights.x[r, c], 2)' == space(vertices[r, _next(c, Nc)], 5) || + throw(SpaceMismatch("West space of bond weight x$((r, c)) does not match.")) + end + return new{T,E}(vertices, weights) + end +end + +""" +Create an InfiniteWeightPEPS from matrices of vertex tensors, +x-weights and y-weights +""" +function InfiniteWeightPEPS( + vertices::Matrix{T}, wts_x::Matrix{E}, wts_y::Matrix{E} +) where {T<:PEPSTensor,E<:PEPSWeight} + weights = SUWeight(wts_x, wts_y) + return InfiniteWeightPEPS(vertices, weights) +end + +""" +Create an InfiniteWeightPEPS by specifying its physical, north and east spaces and unit cell. +Spaces can be specified either via `Int` or via `ElementarySpace`. +Bond weights are initialized as identity matrices. +""" +function InfiniteWeightPEPS( + f, T, Pspace::S, Nspace::S, Espace::S=Nspace; unitcell::Tuple{Int,Int}=(1, 1) +) where {S<:ElementarySpace} + vertices = InfinitePEPS(f, T, Pspace, Nspace, Espace; unitcell=unitcell).A + weights = SUWeight(fill(id(Espace), unitcell), fill(id(Nspace), unitcell)) + return InfiniteWeightPEPS(vertices, weights) +end + +""" +Absorb bond weights into vertex tensors +""" +function InfinitePEPS(peps::InfiniteWeightPEPS) + vertices = deepcopy(peps.vertices) + N1, N2 = size(vertices) + for (r, c) in Iterators.product(1:N1, 1:N2) + for ax in 2:5 + vertices[r, c] = absorb_wt(vertices[r, c], r, c, ax, peps.weights; sqrtwt=true) + end + end + return InfinitePEPS(vertices) +end + +function Base.size(peps::InfiniteWeightPEPS) + @assert size(peps.weights.x) == size(peps.weights.y) == size(peps.vertices) + return size(peps.vertices) +end + +function Base.eltype(peps::InfiniteWeightPEPS) + @assert eltype(peps.weights) == eltype(peps.vertices) + return eltype(peps.vertices) +end + +""" +Mirror the unit cell of an iPEPS with weights by its anti-diagonal line +""" +function mirror_antidiag!(peps::InfiniteWeightPEPS) + peps.vertices[:] = mirror_antidiag(peps.vertices) + for (i, t) in enumerate(peps.vertices) + peps.vertices[i] = permute(t, (1,), (3, 2, 5, 4)) + end + peps.weights.x[:], peps.weights.y[:] = mirror_antidiag(peps.weights.y), + mirror_antidiag(peps.weights.x) + return nothing +end diff --git a/src/states/suweight.jl b/src/states/suweight.jl deleted file mode 100644 index a232b1c17..000000000 --- a/src/states/suweight.jl +++ /dev/null @@ -1,62 +0,0 @@ -""" -Schmidt bond weight used in simple/cluster update -""" -struct SUWeight{T<:AbstractTensorMap} - x::Matrix{T} - y::Matrix{T} - - function SUWeight(wxs::Matrix{T}, wys::Matrix{T}) where {T} - return new{T}(wxs, wys) - end -end - -function Base.size(wts::SUWeight) - @assert size(wts.x) == size(wts.y) - return size(wts.x) -end - -function Base.:(==)(wts1::SUWeight, wts2::SUWeight) - return wts1.x == wts2.x && wts1.y == wts2.y -end - -function Base.:(+)(wts1::SUWeight, wts2::SUWeight) - return SUWeight(wts1.x + wts2.x, wts1.y + wts2.y) -end - -function Base.:(-)(wts1::SUWeight, wts2::SUWeight) - return SUWeight(wts1.x - wts2.x, wts1.y - wts2.y) -end - -function Base.show(io::IO, wts::SUWeight) - N1, N2 = size(wts) - for (direction, r, c) in Iterators.product("xy", 1:N1, 1:N2) - println(io, "$direction[$r,$c]: ") - wt = (direction == 'x' ? wts.x[r, c] : wts.y[r, c]) - for (k, b) in blocks(wt) - println(io, k, " = ", diag(b)) - end - end -end - -function Base.iterate(wts::SUWeight, state=1) - nx = prod(size(wts.x)) - if 1 <= state <= nx - return wts.x[state], state + 1 - elseif nx + 1 <= state <= 2 * nx - return wts.y[state - nx], state + 1 - else - return nothing - end -end - -function Base.length(wts::SUWeight) - @assert size(wts.x) == size(wts.y) - return 2 * prod(size(wts.x)) -end - -function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) - return ( - isapprox(wts1.x, wts2.x; atol=atol, rtol=rtol) && - isapprox(wts1.y, wts2.y; atol=atol, rtol=rtol) - ) -end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 60112dea5..23d877c9e 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -10,19 +10,14 @@ import .RhoMeasureHeis: measrho_all # benchmark data is from Phys. Rev. B 94, 035133 (2016) -# random initialization of 2x2 iPEPS and CTMRGEnv (using real numbers) +# random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) Dcut, χenv = 4, 16 N1, N2 = 2, 2 -Pspace, Vspace = ℂ^2, ℂ^Dcut Random.seed!(0) -peps = InfinitePEPS(rand, Float64, 2, Dcut; unitcell=(N1, N2)) -wts = SUWeight( - collect(id(Vspace) for (row, col) in Iterators.product(1:N1, 1:N2)), - collect(id(Vspace) for (row, col) in Iterators.product(1:N1, 1:N2)), -) -# normalize peps -for ind in CartesianIndices(peps.A) - peps.A[ind] /= norm(peps.A[ind], Inf) +peps = InfiniteWeightPEPS(rand, Float64, ℂ^2, ℂ^Dcut; unitcell=(N1, N2)) +# normalize vertex tensors +for ind in CartesianIndices(peps.vertices) + peps.vertices[ind] /= norm(peps.vertices[ind], Inf) end # Heisenberg model Hamiltonian ham = gen_gate() @@ -32,10 +27,10 @@ dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-7, 1e-8, 1e-9] for (n, (dt, tol)) in enumerate(zip(dts, tols)) Dcut2 = (n == 1 ? Dcut + 1 : Dcut) - simpleupdate!(peps, wts, ham, dt, Dcut2; bipartite=true, evolstep=30000, wtdiff_tol=tol) + simpleupdate!(peps, ham, dt, Dcut2; bipartite=true, evolstep=30000, wtdiff_tol=tol) end # absort weight into site tensors -absorb_wt!(peps, wts) +peps = InfinitePEPS(peps) # CTMRG envs = CTMRGEnv(rand, Float64, peps, ℂ^χenv) trscheme = truncerr(1e-9) & truncdim(χenv) From b1a0083bcfbe372640919dfbbff44c8b8eef51b9 Mon Sep 17 00:00:00 2001 From: sanderdemeyer <80397440+Sander-De-Meyer@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:48:40 +0100 Subject: [PATCH 19/75] Update to the Heisenberg tensors Using MPSKitModels, the functions gen_siteop and gen_bondop are not necessary anymore. The relevant tensors and their tensor product can be defined directly from MPSKitModels --- test/utility/heis.jl | 57 +++++++------------------------------------- 1 file changed, 8 insertions(+), 49 deletions(-) diff --git a/test/utility/heis.jl b/test/utility/heis.jl index 4dbd68275..672ecea6a 100644 --- a/test/utility/heis.jl +++ b/test/utility/heis.jl @@ -1,57 +1,16 @@ module OpsHeis -export gen_gate, gen_siteop, gen_bondop -using TensorKit - -""" -Create 1-site operators for Heisenberg model -""" -function gen_siteop(name::String) - Pspace = ℂ^2 - if name == "Id" - return id(Pspace) - end - op = TensorMap(zeros, Pspace, Pspace) - if name == "Nud" - block(op, Trivial())[:] = [1.0 0.0; 0.0 1.0] - elseif name == "Sp" - block(op, Trivial())[:] = [0.0 1.0; 0.0 0.0] - elseif name == "Sm" - block(op, Trivial())[:] = [0.0 0.0; 1.0 0.0] - elseif name == "Sz" - block(op, Trivial())[:] = [1.0 0.0; 0.0 -1.0] / 2 - elseif name == "Sx" - block(op, Trivial())[:] = [0.0 1.0; 1.0 0.0] / 2 - elseif name == "iSy" - block(op, Trivial())[:] = [0.0 1.0; -1.0 0.0] / 2 - else - throw(ArgumentError("Invalid 1-site spin operator")) - end - return op -end - -""" -Create 2-site operators for Heisenberg model -""" -function gen_bondop(name1::String, name2::String) - op1 = gen_siteop(name1) - op2 = gen_siteop(name2) - op = op1 ⊗ op2 - return op -end +export gen_gate +using TensorKit, MPSKitModels """ Create nearest neighbor gate for Heisenberg model """ function gen_gate(J::Float64=1.0; dens_shift::Bool=false) - heis = - J * ( - (1 / 2) * gen_bondop("Sp", "Sm") + - (1 / 2) * gen_bondop("Sm", "Sp") + - gen_bondop("Sz", "Sz") - ) + Pspace = ℂ^2 + heis = J*S_exchange() if dens_shift - heis = heis - (J / 4) * gen_bondop("Nud", "Nud") + heis = heis - (J / 4) * id(Pspace) ⊗ id(Pspace) end return heis end @@ -69,13 +28,13 @@ using ..OpsHeis function cal_mags(rho1ss::Matrix{<:AbstractTensorMap}) Pspace = codomain(rho1ss[1, 1])[1]' - Sas = [gen_siteop(name) for name in ("Sx", "iSy", "Sz")] + Sas = [S_x(), im*S_y(), S_z()] return [collect(meas_site(Sa, rho1) for rho1 in rho1ss) for Sa in Sas] end function cal_spincor(rho2ss::Matrix{<:AbstractTensorMap}) - SpSm = gen_bondop("Sp", "Sm") - SzSz = gen_bondop("Sz", "Sz") + SpSm2 = S_plus() ⊗ S_min() + SzSz2 = S_z() ⊗ S_z() return collect(meas_bond(SpSm, rho2) + meas_bond(SzSz, rho2) for rho2 in rho2ss) end From 376be3749b66599c719dc93e40109cf3180440c1 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 20 Nov 2024 19:37:52 +0800 Subject: [PATCH 20/75] Minor fix after using operators from MPSKitModels --- test/heisenberg_sufu.jl | 3 +-- test/utility/heis.jl | 42 +++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 23d877c9e..a30daab81 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -5,8 +5,7 @@ using PEPSKit using TensorKit import Statistics: mean include("utility/heis.jl") -import .OpsHeis: gen_gate -import .RhoMeasureHeis: measrho_all +import .RhoMeasureHeis: gen_gate, measrho_all # benchmark data is from Phys. Rev. B 94, 035133 (2016) diff --git a/test/utility/heis.jl b/test/utility/heis.jl index 672ecea6a..7b38d04e3 100644 --- a/test/utility/heis.jl +++ b/test/utility/heis.jl @@ -1,43 +1,42 @@ -module OpsHeis +module RhoMeasureHeis + +export gen_gate, measrho_all, cal_Esite -export gen_gate -using TensorKit, MPSKitModels +using TensorKit, PEPSKit, MPSKitModels +using Statistics: mean """ Create nearest neighbor gate for Heisenberg model """ function gen_gate(J::Float64=1.0; dens_shift::Bool=false) - Pspace = ℂ^2 - heis = J*S_exchange() + heis = J * S_exchange() if dens_shift + Pspace = ℂ^2 heis = heis - (J / 4) * id(Pspace) ⊗ id(Pspace) end return heis end -end - -module RhoMeasureHeis - -export measrho_all, cal_Esite - -using TensorKit -using PEPSKit -using Statistics: mean -using ..OpsHeis - +""" +Measure magnetization on each site +""" function cal_mags(rho1ss::Matrix{<:AbstractTensorMap}) - Pspace = codomain(rho1ss[1, 1])[1]' - Sas = [S_x(), im*S_y(), S_z()] + Sas = [S_x(), im * S_y(), S_z()] return [collect(meas_site(Sa, rho1) for rho1 in rho1ss) for Sa in Sas] end +""" +Measure spin correlation on each nearest neighbor bond +""" function cal_spincor(rho2ss::Matrix{<:AbstractTensorMap}) - SpSm2 = S_plus() ⊗ S_min() - SzSz2 = S_z() ⊗ S_z() + SpSm = S_plus() ⊗ S_min() + SzSz = S_z() ⊗ S_z() return collect(meas_bond(SpSm, rho2) + meas_bond(SzSz, rho2) for rho2 in rho2ss) end +""" +Measure energy on each nearest neighbor bond +""" function cal_Esite(rho2sss::Vector{<:Matrix{<:AbstractTensorMap}}) N1, N2 = size(rho2sss[1]) gate1 = gen_gate(; dens_shift=false) @@ -47,6 +46,9 @@ function cal_Esite(rho2sss::Vector{<:Matrix{<:AbstractTensorMap}}) return esite, ebond1s end +""" +Measure physical quantities for Heisenberg model +""" function measrho_all( rho1ss::Matrix{<:AbstractTensorMap}, rho2sss::Vector{<:Matrix{<:AbstractTensorMap}} ) From 8690f8a534b182707ba8645df30f370401f29fc0 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 20 Nov 2024 21:19:58 +0800 Subject: [PATCH 21/75] Use Julia's logging system; shorten SU test --- src/algorithms/time_evolution/simpleupdate.jl | 40 +++++++++++-------- src/states/infiniteweightpeps.jl | 5 +++ test/heisenberg_sufu.jl | 8 ++-- test/utility/heis.jl | 8 ++-- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index eff5e6718..014e7f775 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -13,14 +13,14 @@ Weights around the tensor at `(row, col)` are ``` """ function absorb_wt( - t::AbstractTensorMap, + t::T, row::Int, col::Int, ax::Int, weights::SUWeight; sqrtwt::Bool=false, invwt::Bool=false, -) +) where {T<:PEPSTensor} Nr, Nc = size(weights) @assert 1 <= row <= Nr && 1 <= col <= Nc @assert 2 <= ax <= 5 @@ -112,8 +112,8 @@ function _su_bondx!( | | -4 -4 =# - T1 = ncon((X, aR), ([-2, -4, -5, 1], [1, -1, -3])) - T2 = ncon((bL, Y), ([-5, -1, 1], [1, -2, -3, -4])) + @tensor T1[-1; -2 -3 -4 -5] := X[-2, -4, -5, 1] * aR[1, -1, -3] + @tensor T2[-1; -2 -3 -4 -5] := bL[-5, -1, 1] * Y[1, -2, -3, -4] # remove environment weights for ax in (2, 4, 5) T1 = absorb_wt(T1, row, col, ax, peps.weights; invwt=true) @@ -178,11 +178,6 @@ function su_iter!( return nothing end -function compare_weights(wts1::SUWeight, wts2::SUWeight) - wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1, wts2)) - return wtdiff / (2 * prod(size(wts1))) -end - """ Perform simple update (maximum `evolstep` iterations) with nearest neighbor Hamiltonian `ham` and time step `dt` @@ -204,7 +199,6 @@ function simpleupdate!( if bipartite @assert N1 == N2 == 2 end - @printf("%-9s%6s%12s %s\n", "Step", "dt", "wt_diff", "speed/s") # exponentiating the 2-site Hamiltonian gate gate = exp(-dt * ham) wtdiff = 1e+3 @@ -213,17 +207,31 @@ function simpleupdate!( time0 = time() su_iter!(gate, peps, Dcut, svderr; bipartite=bipartite) wtdiff = compare_weights(peps.weights, wts0) - stop = (wtdiff < wtdiff_tol) || (count == evolstep) + converge = wtdiff < wtdiff_tol + cancel = count == evolstep wts0 = deepcopy(peps.weights) time1 = time() - if ((count == 1) || (count % check_int == 0) || stop) - @printf("%-9d%6.0e%12.3e %.3f\n", count, dt, wtdiff, time1 - time0) + if ((count == 1) || (count % check_int == 0) || converge || cancel) + label = (converge ? "conv" : (cancel ? "cancel" : "iter")) + message = @sprintf( + "SU %s %-7d: dt = %.0e, weight diff = %.3e, time = %.3f sec\n", + label, + count, + dt, + wtdiff, + time1 - ((converge || cancel) ? time_start : time0) + ) + if cancel + @warn message + elseif converge || (count == 1) + @info message + else + @debug message + end end - if stop + if converge || cancel break end end - time_end = time() - @printf("Evolution time: %.2f s\n\n", time_end - time_start) return wtdiff end diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 22cedc24e..01c8cd27a 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -72,6 +72,11 @@ function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) ) end +function compare_weights(wts1::SUWeight, wts2::SUWeight) + wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1, wts2)) + return wtdiff / (2 * prod(size(wts1))) +end + """ Represents an infinite projected entangled-pair state on a 2D square lattice consisting of vertex tensors and bond weights diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index a30daab81..03ba665cb 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -23,10 +23,10 @@ ham = gen_gate() # simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] -tols = [1e-6, 1e-7, 1e-8, 1e-9] +tols = [1e-6, 1e-7, 5e-8, 1e-8] for (n, (dt, tol)) in enumerate(zip(dts, tols)) Dcut2 = (n == 1 ? Dcut + 1 : Dcut) - simpleupdate!(peps, ham, dt, Dcut2; bipartite=true, evolstep=30000, wtdiff_tol=tol) + simpleupdate!(peps, ham, dt, Dcut2; bipartite=true, evolstep=10000, wtdiff_tol=tol) end # absort weight into site tensors peps = InfinitePEPS(peps) @@ -38,7 +38,7 @@ envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities rho1ss, rho2sss = calrho_all(envs, peps) result = measrho_all(rho1ss, rho2sss) -@printf("Energy = %.8f\n", result["e_site"]) -@printf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) +@info @sprintf("Energy = %.8f\n", result["e_site"]) +@info @sprintf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) @test isapprox(result["e_site"], -0.6675; atol=1e-3) @test isapprox(mean(result["mag_norm"]), 0.3767; atol=1e-3) diff --git a/test/utility/heis.jl b/test/utility/heis.jl index 7b38d04e3..904ae658e 100644 --- a/test/utility/heis.jl +++ b/test/utility/heis.jl @@ -9,7 +9,7 @@ using Statistics: mean Create nearest neighbor gate for Heisenberg model """ function gen_gate(J::Float64=1.0; dens_shift::Bool=false) - heis = J * S_exchange() + heis = J * real(S_exchange()) if dens_shift Pspace = ℂ^2 heis = heis - (J / 4) * id(Pspace) ⊗ id(Pspace) @@ -21,7 +21,7 @@ end Measure magnetization on each site """ function cal_mags(rho1ss::Matrix{<:AbstractTensorMap}) - Sas = [S_x(), im * S_y(), S_z()] + Sas = real.([S_x(), im * S_y(), S_z()]) return [collect(meas_site(Sa, rho1) for rho1 in rho1ss) for Sa in Sas] end @@ -29,8 +29,8 @@ end Measure spin correlation on each nearest neighbor bond """ function cal_spincor(rho2ss::Matrix{<:AbstractTensorMap}) - SpSm = S_plus() ⊗ S_min() - SzSz = S_z() ⊗ S_z() + SpSm = real(S_plus() ⊗ S_min()) + SzSz = real(S_z() ⊗ S_z()) return collect(meas_bond(SpSm, rho2) + meas_bond(SzSz, rho2) for rho2 in rho2ss) end From c5d586dfb7eefa15fb59535904bbcdd79761ec92 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 21 Nov 2024 11:21:08 +0800 Subject: [PATCH 22/75] Solve deprecation warning for `permute` --- src/algorithms/time_evolution/simpleupdate.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 014e7f775..368bda5d5 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -38,8 +38,7 @@ function absorb_wt( indices_t = collect(-1:-1:-5) indices_t[ax] = 1 indices_wt = (ax in (2, 3) ? [1, -ax] : [-ax, 1]) - t2 = ncon((t, wt2), (indices_t, indices_wt)) - t2 = permute(t2, (1,), Tuple(2:5)) + t2 = permute(ncon((t, wt2), (indices_t, indices_wt)), ((1,), Tuple(2:5))) return t2 end @@ -101,7 +100,7 @@ function _su_bondx!( ↑ ↑ -1← aR -← 3 -← bL ← -4 =# - tmp = ncon((gate, aR, bL), ([-2, -3, 1, 2], [-1, 1, 3], [3, 2, -4])) + @tensor tmp[:] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] # SVD truncscheme = truncerr(svderr) & truncdim(Dcut) aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncscheme) From a7d46ef53e8fecf1979e5a4b09d7a1edd72f0ee0 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 21 Nov 2024 17:06:44 +0800 Subject: [PATCH 23/75] Remove buggy in-place rotations and reflections --- src/PEPSKit.jl | 2 +- src/algorithms/time_evolution/simpleupdate.jl | 35 +++++----- src/environments/ctmrg_environments.jl | 67 ++++++++----------- src/states/infinitepeps.jl | 14 ---- src/states/infiniteweightpeps.jl | 19 +++--- test/heisenberg_sufu.jl | 5 +- 6 files changed, 63 insertions(+), 79 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index b1e386415..acfff1e6b 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -174,7 +174,7 @@ export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint export absorb_wt -export su_iter!, simpleupdate! +export su_iter, simpleupdate export meas_site, meas_bond export calrho_site, calrho_bondx, calrho_bondy, calrho_all diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 368bda5d5..8a5448468 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -134,7 +134,7 @@ InfiniteWeightPEPS `peps` with the nearest neighbor gate `gate` When `bipartite === true` (for square lattice), the unit cell size should be 2 x 2, and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` """ -function su_iter!( +function su_iter( gate::AbstractTensorMap, peps::InfiniteWeightPEPS, Dcut::Int, @@ -151,30 +151,35 @@ function su_iter!( @assert [isdual(space(peps.weights.x[r, c], ax)) for ax in 1:2] == [0, 1] @assert [isdual(space(peps.weights.y[r, c], ax)) for ax in 1:2] == [0, 1] end + peps2 = deepcopy(peps) for direction in 1:2 # mirror the y-weights to x-direction # to update them using code for x-weights if direction == 2 - mirror_antidiag!(peps) + peps2 = mirror_antidiag(peps2) end if bipartite - ϵ = _su_bondx!(1, 1, gate, peps, Dcut, svderr) - (peps.vertices[2, 2], peps.vertices[2, 1], peps.weights.x[2, 2]) = - deepcopy.((peps.vertices[1, 1], peps.vertices[1, 2], peps.weights.x[1, 1])) - ϵ = _su_bondx!(2, 1, gate, peps, Dcut, svderr) - (peps.vertices[1, 2], peps.vertices[1, 1], peps.weights.x[1, 2]) = - deepcopy.((peps.vertices[2, 1], peps.vertices[2, 2], peps.weights.x[2, 1])) + ϵ = _su_bondx!(1, 1, gate, peps2, Dcut, svderr) + (peps2.vertices[2, 2], peps2.vertices[2, 1], peps2.weights.x[2, 2]) = + deepcopy.(( + peps2.vertices[1, 1], peps2.vertices[1, 2], peps2.weights.x[1, 1] + )) + ϵ = _su_bondx!(2, 1, gate, peps2, Dcut, svderr) + (peps2.vertices[1, 2], peps2.vertices[1, 1], peps2.weights.x[1, 2]) = + deepcopy.(( + peps2.vertices[2, 1], peps2.vertices[2, 2], peps2.weights.x[2, 1] + )) else - for site in CartesianIndices(peps.vertices) + for site in CartesianIndices(peps2.vertices) row, col = Tuple(site) - ϵ = _su_bondx!(row, col, gate, peps, Dcut) + ϵ = _su_bondx!(row, col, gate, peps2, Dcut) end end if direction == 2 - mirror_antidiag!(peps) + peps2 = mirror_antidiag(peps2) end end - return nothing + return peps2 end """ @@ -182,7 +187,7 @@ Perform simple update (maximum `evolstep` iterations) with nearest neighbor Hamiltonian `ham` and time step `dt` until the change of bond weights is smaller than `wtdiff_tol` """ -function simpleupdate!( +function simpleupdate( peps::InfiniteWeightPEPS, ham::AbstractTensorMap, dt::Float64, @@ -204,7 +209,7 @@ function simpleupdate!( wts0 = deepcopy(peps.weights) for count in 1:evolstep time0 = time() - su_iter!(gate, peps, Dcut, svderr; bipartite=bipartite) + peps = su_iter(gate, peps, Dcut, svderr; bipartite=bipartite) wtdiff = compare_weights(peps.weights, wts0) converge = wtdiff < wtdiff_tol cancel = count == evolstep @@ -232,5 +237,5 @@ function simpleupdate!( break end end - return wtdiff + return peps, wtdiff end diff --git a/src/environments/ctmrg_environments.jl b/src/environments/ctmrg_environments.jl index 65cd05c15..39f2dbd6c 100644 --- a/src/environments/ctmrg_environments.jl +++ b/src/environments/ctmrg_environments.jl @@ -160,7 +160,7 @@ end """ CTMRGEnv( - [f=randn, ComplexF64], D_north::S, D_south::S, chi_north::S, [chi_east::S], [chi_south::S], [chi_west::S]; unitcell::Tuple{Int,Int}=(1, 1), + [f=randn, ComplexF64], D_north::S, D_east::S, chi_north::S, [chi_east::S], [chi_south::S], [chi_west::S]; unitcell::Tuple{Int,Int}=(1, 1), ) where {S<:Union{Int,ElementarySpace}} Construct a CTMRG environment by specifying the north and east virtual spaces of the @@ -173,7 +173,7 @@ corresponding edge tensor for each direction. """ function CTMRGEnv( D_north::S, - D_south::S, + D_east::S, chi_north::S, chi_east::S=chi_north, chi_south::S=chi_north, @@ -184,7 +184,7 @@ function CTMRGEnv( randn, ComplexF64, fill(D_north, unitcell), - fill(D_south, unitcell), + fill(D_east, unitcell), fill(chi_north, unitcell), fill(chi_east, unitcell), fill(chi_south, unitcell), @@ -195,7 +195,7 @@ function CTMRGEnv( f, T, D_north::S, - D_south::S, + D_east::S, chi_north::S, chi_east::S=chi_north, chi_south::S=chi_north, @@ -206,7 +206,7 @@ function CTMRGEnv( f, T, fill(D_north, unitcell), - fill(D_south, unitcell), + fill(D_east, unitcell), fill(chi_north, unitcell), fill(chi_east, unitcell), fill(chi_south, unitcell), @@ -362,51 +362,42 @@ function Base.rotl90(env::CTMRGEnv{C,T}) where {C,T} Array{C,3}(undef, 4, size(env.corners, 3), size(env.corners, 2)) ) edges′ = Zygote.Buffer(Array{T,3}(undef, 4, size(env.edges, 3), size(env.edges, 2))) - for dir in 1:4 - corners′[_prev(dir, 4), :, :] = rotl90(env.corners[dir, :, :]) - edges′[_prev(dir, 4), :, :] = rotl90(env.edges[dir, :, :]) + dir2 = _prev(dir, 4) + corners′[dir2, :, :] = rotl90(env.corners[dir, :, :]) + edges′[dir2, :, :] = rotl90(env.edges[dir, :, :]) end - return CTMRGEnv(copy(corners′), copy(edges′)) end -# in-place rotations (incompatible with autodiff) -""" -Rotate the CTMRGEnv `envs` left 90 degrees (anti-clockwise) in place -""" -function rotl90!(envs::CTMRGEnv) - envs2 = deepcopy(envs) - for dir in 1:4 - dir2 = _prev(dir, 4) - envs.corners[dir2, :, :] = rotl90(envs2.corners[dir, :, :]) - envs.edges[dir2, :, :] = rotl90(envs2.edges[dir, :, :]) - end - return nothing -end -""" -Rotate the CTMRGEnv `envs` right 90 degrees (clockwise) in place -""" -function rotr90!(envs::CTMRGEnv) - envs2 = deepcopy(envs) +# Rotate corners & edges clockwise +function Base.rotr90(env::CTMRGEnv{C,T}) where {C,T} + # Initialize rotated corners & edges with rotated sizes + corners′ = Zygote.Buffer( + Array{C,3}(undef, 4, size(env.corners, 3), size(env.corners, 2)) + ) + edges′ = Zygote.Buffer(Array{T,3}(undef, 4, size(env.edges, 3), size(env.edges, 2))) for dir in 1:4 dir2 = _next(dir, 4) - envs.corners[dir2, :, :] = rotr90(envs2.corners[dir, :, :]) - envs.edges[dir2, :, :] = rotr90(envs2.edges[dir, :, :]) + corners′[dir2, :, :] = rotr90(env.corners[dir, :, :]) + edges′[dir2, :, :] = rotr90(env.edges[dir, :, :]) end - return nothing + return CTMRGEnv(copy(corners′), copy(edges′)) end -""" -Rotate the CTMRGEnv `envs` 180 degrees in place -""" -function rot180!(envs::CTMRGEnv) - envs2 = deepcopy(envs) + +# Rotate corners & edges by 180 degrees +function Base.rot180(env::CTMRGEnv{C,T}) where {C,T} + # Initialize rotated corners & edges with rotated sizes + corners′ = Zygote.Buffer( + Array{C,3}(undef, 4, size(env.corners, 2), size(env.corners, 3)) + ) + edges′ = Zygote.Buffer(Array{T,3}(undef, 4, size(env.edges, 2), size(env.edges, 3))) for dir in 1:4 dir2 = _next(_next(dir, 4), 4) - envs.corners[dir2, :, :] = rot180(envs2.corners[dir, :, :]) - envs.edges[dir2, :, :] = rot180(envs2.edges[dir, :, :]) + corners′[dir2, :, :] = rot180(env.corners[dir, :, :]) + edges′[dir2, :, :] = rot180(env.edges[dir, :, :]) end - return nothing + return CTMRGEnv(copy(corners′), copy(edges′)) end Base.eltype(env::CTMRGEnv) = eltype(env.corners[1]) diff --git a/src/states/infinitepeps.jl b/src/states/infinitepeps.jl index 423c6f08f..51b353d49 100644 --- a/src/states/infinitepeps.jl +++ b/src/states/infinitepeps.jl @@ -172,20 +172,6 @@ Base.rotl90(t::InfinitePEPS) = InfinitePEPS(rotl90(rotl90.(t.A))) Base.rotr90(t::InfinitePEPS) = InfinitePEPS(rotr90(rotr90.(t.A))) Base.rot180(t::InfinitePEPS) = InfinitePEPS(rot180(rot180.(t.A))) -# In-place rotations -function rotl90!(peps::InfinitePEPS) - peps.A[:] = rotl90(rotl90.(peps.A)) - return nothing -end -function rotr90!(peps::InfinitePEPS) - peps.A[:] = rotr90(rotr90.(peps.A)) - return nothing -end -function rot180!(peps::InfinitePEPS) - peps.A[:] = rot180(rot180.(peps.A)) - return nothing -end - # Chainrules function ChainRulesCore.rrule( ::typeof(Base.getindex), state::InfinitePEPS, row::Int, col::Int diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 01c8cd27a..2760cb2c2 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -113,8 +113,7 @@ x-weights and y-weights function InfiniteWeightPEPS( vertices::Matrix{T}, wts_x::Matrix{E}, wts_y::Matrix{E} ) where {T<:PEPSTensor,E<:PEPSWeight} - weights = SUWeight(wts_x, wts_y) - return InfiniteWeightPEPS(vertices, weights) + return InfiniteWeightPEPS(vertices, SUWeight(wts_x, wts_y)) end """ @@ -131,7 +130,7 @@ function InfiniteWeightPEPS( end """ -Absorb bond weights into vertex tensors +Create `InfinitePEPS` from `InfiniteWeightPEPS` by absorbing bond weights into vertex tensors """ function InfinitePEPS(peps::InfiniteWeightPEPS) vertices = deepcopy(peps.vertices) @@ -157,12 +156,12 @@ end """ Mirror the unit cell of an iPEPS with weights by its anti-diagonal line """ -function mirror_antidiag!(peps::InfiniteWeightPEPS) - peps.vertices[:] = mirror_antidiag(peps.vertices) - for (i, t) in enumerate(peps.vertices) - peps.vertices[i] = permute(t, (1,), (3, 2, 5, 4)) +function mirror_antidiag(peps::InfiniteWeightPEPS) + vertices2 = mirror_antidiag(peps.vertices) + for (i, t) in enumerate(vertices2) + vertices2[i] = permute(t, ((1,), (3, 2, 5, 4))) end - peps.weights.x[:], peps.weights.y[:] = mirror_antidiag(peps.weights.y), - mirror_antidiag(peps.weights.x) - return nothing + weights2_x = mirror_antidiag(peps.weights.y) + weights2_y = mirror_antidiag(peps.weights.x) + return InfiniteWeightPEPS(vertices2, weights2_x, weights2_y) end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 03ba665cb..474cffccd 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -26,7 +26,10 @@ dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-7, 5e-8, 1e-8] for (n, (dt, tol)) in enumerate(zip(dts, tols)) Dcut2 = (n == 1 ? Dcut + 1 : Dcut) - simpleupdate!(peps, ham, dt, Dcut2; bipartite=true, evolstep=10000, wtdiff_tol=tol) + result = simpleupdate( + peps, ham, dt, Dcut2; bipartite=true, evolstep=10000, wtdiff_tol=tol + ) + global peps = result[1] end # absort weight into site tensors peps = InfinitePEPS(peps) From 53d5a55df9308a70757794e74cbadd6e661a0750 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 21 Nov 2024 20:31:36 +0800 Subject: [PATCH 24/75] Minor refactoring --- src/algorithms/time_evolution/simpleupdate.jl | 44 ------------------- src/states/infiniteweightpeps.jl | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 8a5448468..82fcdbff1 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -1,47 +1,3 @@ -""" -Absorb environment weight on axis `ax` into tensor `t` at position `(row,col)` - -Weights around the tensor at `(row, col)` are -``` - ↓ - y[r,c] - ↓ - ←x[r,c-1] ← T[r,c] ← x[r,c] ← - ↓ - y[r+1,c] - ↓ -``` -""" -function absorb_wt( - t::T, - row::Int, - col::Int, - ax::Int, - weights::SUWeight; - sqrtwt::Bool=false, - invwt::Bool=false, -) where {T<:PEPSTensor} - Nr, Nc = size(weights) - @assert 1 <= row <= Nr && 1 <= col <= Nc - @assert 2 <= ax <= 5 - pow = (sqrtwt ? 1 / 2 : 1) * (invwt ? -1 : 1) - if ax == 2 # north - wt = weights.y[row, col] - elseif ax == 3 # east - wt = weights.x[row, col] - elseif ax == 4 # south - wt = weights.y[_next(row, Nr), col] - else # west - wt = weights.x[row, _prev(col, Nc)] - end - wt2 = sdiag_pow(wt, pow) - indices_t = collect(-1:-1:-5) - indices_t[ax] = 1 - indices_wt = (ax in (2, 3) ? [1, -ax] : [-ax, 1]) - t2 = permute(ncon((t, wt2), (indices_t, indices_wt)), ((1,), Tuple(2:5))) - return t2 -end - """ Simple update of bond `peps.weights.x[r,c]` ``` diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 2760cb2c2..790e92bb8 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -129,6 +129,50 @@ function InfiniteWeightPEPS( return InfiniteWeightPEPS(vertices, weights) end +""" +Absorb environment weight on axis `ax` into tensor `t` at position `(row,col)` + +Weights around the tensor at `(row, col)` are +``` + ↓ + y[r,c] + ↓ + ←x[r,c-1] ← T[r,c] ← x[r,c] ← + ↓ + y[r+1,c] + ↓ +``` +""" +function absorb_wt( + t::T, + row::Int, + col::Int, + ax::Int, + weights::SUWeight; + sqrtwt::Bool=false, + invwt::Bool=false, +) where {T<:PEPSTensor} + Nr, Nc = size(weights) + @assert 1 <= row <= Nr && 1 <= col <= Nc + @assert 2 <= ax <= 5 + pow = (sqrtwt ? 1 / 2 : 1) * (invwt ? -1 : 1) + if ax == 2 # north + wt = weights.y[row, col] + elseif ax == 3 # east + wt = weights.x[row, col] + elseif ax == 4 # south + wt = weights.y[_next(row, Nr), col] + else # west + wt = weights.x[row, _prev(col, Nc)] + end + wt2 = sdiag_pow(wt, pow) + indices_t = collect(-1:-1:-5) + indices_t[ax] = 1 + indices_wt = (ax in (2, 3) ? [1, -ax] : [-ax, 1]) + t2 = permute(ncon((t, wt2), (indices_t, indices_wt)), ((1,), Tuple(2:5))) + return t2 +end + """ Create `InfinitePEPS` from `InfiniteWeightPEPS` by absorbing bond weights into vertex tensors """ From a026db8530561b263ac6f654cfd5a6dba9b0d5ca Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 22 Nov 2024 17:05:08 +0800 Subject: [PATCH 25/75] Print more message during simple update --- src/algorithms/time_evolution/simpleupdate.jl | 8 +------- test/heisenberg_sufu.jl | 10 +++++----- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 82fcdbff1..f583e6861 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -181,13 +181,7 @@ function simpleupdate( wtdiff, time1 - ((converge || cancel) ? time_start : time0) ) - if cancel - @warn message - elseif converge || (count == 1) - @info message - else - @debug message - end + cancel ? (@warn message) : (@info message) end if converge || cancel break diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 474cffccd..bf1982d73 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -40,8 +40,8 @@ ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequen envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities rho1ss, rho2sss = calrho_all(envs, peps) -result = measrho_all(rho1ss, rho2sss) -@info @sprintf("Energy = %.8f\n", result["e_site"]) -@info @sprintf("Staggered magnetization = %.8f\n", mean(result["mag_norm"])) -@test isapprox(result["e_site"], -0.6675; atol=1e-3) -@test isapprox(mean(result["mag_norm"]), 0.3767; atol=1e-3) +meas = measrho_all(rho1ss, rho2sss) +@info @sprintf("Energy = %.8f\n", meas["e_site"]) +@info @sprintf("Staggered magnetization = %.8f\n", mean(meas["mag_norm"])) +@test isapprox(meas["e_site"], -0.6675; atol=1e-3) +@test isapprox(mean(meas["mag_norm"]), 0.3767; atol=1e-3) From 2643b68f853392f81a5905295d986ef58d9153f9 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sat, 23 Nov 2024 11:46:27 +0800 Subject: [PATCH 26/75] Replace custom rho with existing exp. value calculation --- src/PEPSKit.jl | 4 - src/algorithms/contractions/ctmrg_rhos.jl | 230 -------------------- src/algorithms/contractions/measure_rhos.jl | 39 ---- test/heisenberg_sufu.jl | 9 +- test/localop.jl | 4 + test/utility/heis.jl | 67 ------ test/utility/measure_heis.jl | 53 +++++ 7 files changed, 62 insertions(+), 344 deletions(-) delete mode 100644 src/algorithms/contractions/ctmrg_rhos.jl delete mode 100644 src/algorithms/contractions/measure_rhos.jl create mode 100644 test/localop.jl delete mode 100644 test/utility/heis.jl create mode 100644 test/utility/measure_heis.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index acfff1e6b..b0e147f3a 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -40,8 +40,6 @@ include("environments/transferpepo_environments.jl") include("algorithms/contractions/localoperator.jl") include("algorithms/contractions/ctmrg_contractions.jl") -include("algorithms/contractions/ctmrg_rhos.jl") -include("algorithms/contractions/measure_rhos.jl") include("algorithms/ctmrg/sparse_environments.jl") include("algorithms/ctmrg/ctmrg.jl") @@ -175,8 +173,6 @@ export fixedpoint export absorb_wt export su_iter, simpleupdate -export meas_site, meas_bond -export calrho_site, calrho_bondx, calrho_bondy, calrho_all export InfinitePEPS, InfiniteTransferPEPS export SUWeight, InfiniteWeightPEPS diff --git a/src/algorithms/contractions/ctmrg_rhos.jl b/src/algorithms/contractions/ctmrg_rhos.jl deleted file mode 100644 index 7898d25f0..000000000 --- a/src/algorithms/contractions/ctmrg_rhos.jl +++ /dev/null @@ -1,230 +0,0 @@ -""" -Calculate 1-site rho at site `(r,c)` -``` - C1 - χ4 - T1 - χ6 - C2 r-1 - | ‖ | - χ2 DN χ8 - | ‖ | - T4 = DW =k/b = DE = T2 r - | ‖ | - χ1 DS χ7 - | ‖ | - C4 - χ3 - T3 - χ5 - C3 r+1 - c-1 c c+1 -``` -Indices d0, d1 are physical indices of ket, bra -""" -function calrho_site( - row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket -) - N1, N2 = size(ket) - @assert 1 <= row <= N1 && 1 <= col <= N2 - rp1, rm1 = _next(row, N1), _prev(row, N1) - cp1, cm1 = _next(col, N2), _prev(col, N2) - tket, tbra = ket[row, col], bra[row, col] - c1 = envs.corners[1, rm1, cm1] - t1 = envs.edges[1, rm1, col] - c2 = envs.corners[2, rm1, cp1] - t2 = envs.edges[2, row, cp1] - c3 = envs.corners[3, rp1, cp1] - t3 = envs.edges[3, rp1, col] - c4 = envs.corners[4, rp1, cm1] - t4 = envs.edges[4, row, cm1] - PEPSKit.@autoopt @tensor rho1[d1; d0] := ( - c4[χ3, χ1] * - t4[χ1, DW0, DW1, χ2] * - c1[χ2, χ4] * - t3[χ5, DS0, DS1, χ3] * - tket[d0, DN0, DE0, DS0, DW0] * - conj(tbra[d1, DN1, DE1, DS1, DW1]) * - t1[χ4, DN0, DN1, χ6] * - c3[χ7, χ5] * - t2[χ8, DE0, DE1, χ7] * - c2[χ6, χ8] - ) - return rho1 -end - -""" -Calculate 2-site rho on sites `(r,c)(r,c+1)` -``` - C1 - χ4 - T1 - χ6 - T1 - χ8 - C2 r-1 - | ‖ ‖ | - χ2 DN1 DN2 χ10 - | ‖ ‖ | - T4 = DW =k/b = DM =k/b = DE = T2 r - | ‖ ‖ | - χ1 DS1 DS2 χ9 - | ‖ ‖ | - C4 - χ3 - T3 - χ5 - T3 - χ7 - C3 r+1 - c-1 c c+1 c+2 -``` -Indices d0, d1 are physical indices of ket, bra -""" -function calrho_bondx( - row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket -) - N1, N2 = size(ket) - @assert 1 <= row <= N1 && 1 <= col <= N2 - rp1, rm1 = _next(row, N1), _prev(row, N1) - cp1, cm1 = _next(col, N2), _prev(col, N2) - cp2 = _next(cp1, N2) - tket1, tbra1 = ket[row, col], bra[row, col] - tket2, tbra2 = ket[row, cp1], bra[row, cp1] - c1 = envs.corners[1, rm1, cm1] - t11, t12 = envs.edges[1, rm1, col], envs.edges[1, rm1, cp1] - c2 = envs.corners[2, rm1, cp2] - t2 = envs.edges[2, row, cp2] - c3 = envs.corners[3, rp1, cp2] - t31, t32 = envs.edges[3, rp1, col], envs.edges[3, rp1, cp1] - c4 = envs.corners[4, rp1, cm1] - t4 = envs.edges[4, row, cm1] - PEPSKit.@autoopt @tensor rho2[d11, d21; d10, d20] := ( - c4[χ3, χ1] * - t4[χ1, DW0, DW1, χ2] * - c1[χ2, χ4] * - t31[χ5, DS10, DS11, χ3] * - tket1[d10, DN10, DM0, DS10, DW0] * - conj(tbra1[d11, DN11, DM1, DS11, DW1]) * - t11[χ4, DN10, DN11, χ6] * - t32[χ7, DS20, DS21, χ5] * - tket2[d20, DN20, DE0, DS20, DM0] * - conj(tbra2[d21, DN21, DE1, DS21, DM1]) * - t12[χ6, DN20, DN21, χ8] * - c3[χ9, χ7] * - t2[χ10, DE0, DE1, χ9] * - c2[χ8, χ10] - ) - return rho2 -end - -""" -Calculate 2-site rho on sites `(r,c)(r-1,c)` -``` - C1 - χ9 - T1 -χ10 - C2 r-2 - | ‖ | - χ7 DN χ8 - | ‖ | - T4 = DW2=k/b =DE2 = T2 r-1 - | ‖ | - χ5 DM χ6 - | ‖ | - T4 = DW1=k/b =DE1 = T2 r - | ‖ | - χ3 DS χ4 - | ‖ | - C4 - χ1 - T3 - χ2 - C3 r+1 - c-1 c c+1 -``` -Indices d0, d1 are physical indices of ket, bra -""" -function calrho_bondy( - row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket -) - N1, N2 = size(ket) - @assert 1 <= row <= N1 && 1 <= col <= N2 - rp1, rm1 = _next(row, N1), _prev(row, N1) - cp1, cm1 = _next(col, N2), _prev(col, N2) - rm2 = _prev(rm1, N1) - tket1, tbra1 = ket[row, col], bra[row, col] - tket2, tbra2 = ket[rm1, col], bra[rm1, col] - c1 = envs.corners[1, rm2, cm1] - t1 = envs.edges[1, rm2, col] - c2 = envs.corners[2, rm2, cp1] - t21, t22 = envs.edges[2, row, cp1], envs.edges[2, rm1, cp1] - c3 = envs.corners[3, rp1, cp1] - t3 = envs.edges[3, rp1, col] - c4 = envs.corners[4, rp1, cm1] - t41, t42 = envs.edges[4, row, cm1], envs.edges[4, rm1, cm1] - PEPSKit.@autoopt @tensor rho2[d11, d21; d10, d20] := ( - c4[χ1, χ3] * - t3[χ2, DS0, DS1, χ1] * - c3[χ4, χ2] * - t41[χ3, DW10, DW11, χ5] * - tket1[d10, DM0, DE10, DS0, DW10] * - conj(tbra1[d11, DM1, DE11, DS1, DW11]) * - t21[χ6, DE10, DE11, χ4] * - t42[χ5, DW20, DW21, χ7] * - tket2[d20, DN0, DE20, DM0, DW20] * - conj(tbra2[d21, DN1, DE21, DM1, DW21]) * - t22[χ8, DE20, DE21, χ6] * - c1[χ7, χ9] * - t1[χ9, DN0, DN1, χ10] * - c2[χ10, χ8] - ) - return rho2 -end - -# TODO: add rhos on next nearest neighbor bonds - -""" -Calculate 2-site rho on 2nd nearest neighbor sites `(r,c)(r-1,c+1)` -``` - C1 -χ10 - T1 -χ11 - T1 -χ12 - C2 r-2 - | ‖ ‖ | - χ8 DN1 DN2 χ9 - | ‖ ‖ | - T4 =DW2= k/b =DH2= k/b =DE2== T2 r-1 - | ‖ ‖ | - χ6 DV1 DV2 χ7 - | ‖ ‖ | - T4 =DW1= k/b =DH1= k/b =DE1== T2 r - | ‖ ‖ | - χ4 DS1 DS2 χ5 - | ‖ ‖ | - C4 - χ1 - T3 - χ2 - T3 - χ3 - C4 r+1 - c-1 c c+1 c+2 -``` -Indices d0, d1 are physical indices of ket, bra -""" -function calrho_bondd1( - row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket -) - N1, N2 = size(ket) - @assert 1 <= row <= N1 && 1 <= col <= N2 - throw("not implemented") -end - -""" -Calculate 2-site rho on 2nd nearest neighbor sites `(r,c+1)(r-1,c)` -""" -function calrho_bondd2( - row::Int, col::Int, envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket -) - N1, N2 = size(ket) - @assert 1 <= row <= N1 && 1 <= col <= N2 - throw("not implemented") -end - -""" -Calculate rho for all sites -""" -function calrho_allsites(envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket) - Nr, Nc = size(ket) - return collect( - calrho_site(r, c, envs, ket, bra) for (r, c) in Iterators.product(1:Nr, 1:Nc) - ) -end - -""" -Calculate rho for all nearest-neighbor bonds -""" -function calrho_allnbs(envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket) - Nr, Nc = size(ket) - rhoxss = collect( - calrho_bondx(r, c, envs, ket, bra) for (r, c) in Iterators.product(1:Nr, 1:Nc) - ) - rhoyss = collect( - calrho_bondy(r, c, envs, ket, bra) for (r, c) in Iterators.product(1:Nr, 1:Nc) - ) - return [rhoxss, rhoyss] -end - -""" -Calculate rho for all sites and nearest-neighbor bonds -""" -function calrho_all(envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket) - rho1ss = calrho_allsites(envs, ket, bra) - rho2sss = calrho_allnbs(envs, ket, bra) - return rho1ss, rho2sss -end diff --git a/src/algorithms/contractions/measure_rhos.jl b/src/algorithms/contractions/measure_rhos.jl deleted file mode 100644 index a075a0776..000000000 --- a/src/algorithms/contractions/measure_rhos.jl +++ /dev/null @@ -1,39 +0,0 @@ -""" -Get identity operator on the physical space -""" -function _getid_from_rho(rho::AbstractTensorMap) - Pspace = codomain(rho)[1] - if isdual(Pspace) - Pspace = adjoint(Pspace) - end - return TensorKit.id(Pspace) -end - -""" -Measure `` using 1-site rho -""" -function meas_site(op::AbstractTensorMap, rho1::AbstractTensorMap) - Id = _getid_from_rho(rho1) - val = ncon((rho1, op), ([1, 2], [1, 2])) - nrm = ncon((rho1, Id), ([1, 2], [1, 2])) - meas = first(blocks(val / nrm))[2][1] - return meas -end - -""" -Measure `` using 2-site rho -""" -function meas_bond(op1::AbstractTensorMap, op2::AbstractTensorMap, rho2::AbstractTensorMap) - return meas_bond(op1 ⊗ op2, rho2) -end - -""" -Measure `` using 2-site rho -""" -function meas_bond(gate::AbstractTensorMap, rho2::AbstractTensorMap) - Id = _getid_from_rho(rho2) - val = ncon((rho2, gate), ([1, 2, 3, 4], [1, 2, 3, 4])) - nrm = ncon((rho2, Id ⊗ Id), ([1, 2, 3, 4], [1, 2, 3, 4])) - meas = first(blocks(val / nrm))[2][1] - return meas -end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index bf1982d73..0c58ff583 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -4,8 +4,8 @@ using Random using PEPSKit using TensorKit import Statistics: mean -include("utility/heis.jl") -import .RhoMeasureHeis: gen_gate, measrho_all +include("utility/measure_heis.jl") +import .MeasureHeis: gen_gate, measure_heis # benchmark data is from Phys. Rev. B 94, 035133 (2016) @@ -19,6 +19,7 @@ for ind in CartesianIndices(peps.vertices) peps.vertices[ind] /= norm(peps.vertices[ind], Inf) end # Heisenberg model Hamiltonian +H = heisenberg_XYZ(InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) ham = gen_gate() # simple update @@ -39,8 +40,8 @@ trscheme = truncerr(1e-9) & truncdim(χenv) ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities -rho1ss, rho2sss = calrho_all(envs, peps) -meas = measrho_all(rho1ss, rho2sss) +meas = measure_heis(peps, H, envs) +display(meas) @info @sprintf("Energy = %.8f\n", meas["e_site"]) @info @sprintf("Staggered magnetization = %.8f\n", mean(meas["mag_norm"])) @test isapprox(meas["e_site"], -0.6675; atol=1e-3) diff --git a/test/localop.jl b/test/localop.jl new file mode 100644 index 000000000..100e057ee --- /dev/null +++ b/test/localop.jl @@ -0,0 +1,4 @@ +using MPSKitModels +using PEPSKit + +LocalOperator diff --git a/test/utility/heis.jl b/test/utility/heis.jl deleted file mode 100644 index 904ae658e..000000000 --- a/test/utility/heis.jl +++ /dev/null @@ -1,67 +0,0 @@ -module RhoMeasureHeis - -export gen_gate, measrho_all, cal_Esite - -using TensorKit, PEPSKit, MPSKitModels -using Statistics: mean - -""" -Create nearest neighbor gate for Heisenberg model -""" -function gen_gate(J::Float64=1.0; dens_shift::Bool=false) - heis = J * real(S_exchange()) - if dens_shift - Pspace = ℂ^2 - heis = heis - (J / 4) * id(Pspace) ⊗ id(Pspace) - end - return heis -end - -""" -Measure magnetization on each site -""" -function cal_mags(rho1ss::Matrix{<:AbstractTensorMap}) - Sas = real.([S_x(), im * S_y(), S_z()]) - return [collect(meas_site(Sa, rho1) for rho1 in rho1ss) for Sa in Sas] -end - -""" -Measure spin correlation on each nearest neighbor bond -""" -function cal_spincor(rho2ss::Matrix{<:AbstractTensorMap}) - SpSm = real(S_plus() ⊗ S_min()) - SzSz = real(S_z() ⊗ S_z()) - return collect(meas_bond(SpSm, rho2) + meas_bond(SzSz, rho2) for rho2 in rho2ss) -end - -""" -Measure energy on each nearest neighbor bond -""" -function cal_Esite(rho2sss::Vector{<:Matrix{<:AbstractTensorMap}}) - N1, N2 = size(rho2sss[1]) - gate1 = gen_gate(; dens_shift=false) - # 1st neighbor bond energy - ebond1s = [collect(meas_bond(gate1, rho2) for rho2 in rho2sss[n]) for n in 1:2] - esite = sum(sum(ebond1s)) / (N1 * N2) - return esite, ebond1s -end - -""" -Measure physical quantities for Heisenberg model -""" -function measrho_all( - rho1ss::Matrix{<:AbstractTensorMap}, rho2sss::Vector{<:Matrix{<:AbstractTensorMap}} -) - results = Dict{String,Any}() - N1, N2 = size(rho1ss) - results["e_site"], results["energy"] = cal_Esite(rho2sss) - results["mag"] = cal_mags(rho1ss) - results["mag_norm"] = collect( - norm([results["mag"][n][r, c] for n in 1:3]) for - (r, c) in Iterators.product(1:N1, 1:N2) - ) - results["spincor"] = [cal_spincor(rho2ss) for rho2ss in rho2sss] - return results -end - -end diff --git a/test/utility/measure_heis.jl b/test/utility/measure_heis.jl new file mode 100644 index 000000000..4827ff61a --- /dev/null +++ b/test/utility/measure_heis.jl @@ -0,0 +1,53 @@ +module MeasureHeis + +export gen_gate, measure_heis + +using TensorKit +import MPSKitModels: S_x, S_y, S_z, S_exchange +using PEPSKit +using Statistics: mean + +""" +Measure magnetization on each site +""" +function cal_mags(peps::InfinitePEPS, envs::CTMRGEnv) + Nr, Nc = size(peps) + lattice = collect(space(t, 1) for t in peps.A) + Sas = real.([S_x(), im * S_y(), S_z()]) + return [ + collect( + expectation_value( + peps, LocalOperator(lattice, (CartesianIndex(r, c),) => Sa), envs + ) for (r, c) in Iterators.product(1:Nr, 1:Nc) + ) for Sa in Sas + ] +end + +""" +Measure physical quantities for Heisenberg model +""" +function measure_heis(peps::InfinitePEPS, H::LocalOperator, envs::CTMRGEnv) + results = Dict{String,Any}() + Nr, Nc = size(peps) + results["e_site"] = costfun(peps, envs, H) / (Nr * Nc) + results["mag"] = cal_mags(peps, envs) + results["mag_norm"] = collect( + norm([results["mag"][n][r, c] for n in 1:3]) for + (r, c) in Iterators.product(1:Nr, 1:Nc) + ) + return results +end + +""" +Create nearest neighbor gate for Heisenberg model +""" +function gen_gate(J::Float64=1.0; dens_shift::Bool=false) + heis = J * real(S_exchange()) + if dens_shift + Pspace = ℂ^2 + heis = heis - (J / 4) * id(Pspace) ⊗ id(Pspace) + end + return heis +end + +end From 23f92bfe877172ad53a40012d22b9a3cb96093fb Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sat, 23 Nov 2024 16:10:56 +0800 Subject: [PATCH 27/75] Integrate SU with LocalOperator --- src/PEPSKit.jl | 1 + src/algorithms/time_evolution/gatetools.jl | 23 ++++++++ src/algorithms/time_evolution/simpleupdate.jl | 56 +++++++++++++------ test/heisenberg_sufu.jl | 14 +++-- test/utility/measure_heis.jl | 14 +---- 5 files changed, 72 insertions(+), 36 deletions(-) create mode 100644 src/algorithms/time_evolution/gatetools.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index b0e147f3a..d461ea2f9 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -45,6 +45,7 @@ include("algorithms/ctmrg/sparse_environments.jl") include("algorithms/ctmrg/ctmrg.jl") include("algorithms/ctmrg/gaugefix.jl") +include("algorithms/time_evolution/gatetools.jl") include("algorithms/time_evolution/simpleupdate.jl") include("algorithms/toolbox.jl") diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl new file mode 100644 index 000000000..ae2d3c7be --- /dev/null +++ b/src/algorithms/time_evolution/gatetools.jl @@ -0,0 +1,23 @@ +""" +Convert Hamiltonian `H` with nearest neighbor terms to `exp(-dt * H)` +""" +function get_gate(dt::Float64, H::LocalOperator) + return LocalOperator(H.lattice, Tuple(ind => exp(-dt * op) for (ind, op) in H.terms)...) +end + +""" +Get the term of a 2-site gate acting on a certain bond. +Input `gate` should only include one term for each nearest neighbor bond. +""" +function get_gateterm(gate::LocalOperator, bond::NTuple{2,CartesianIndex{2}}) + label = findall(p -> p.first == bond, gate.terms) + if length(label) == 0 + # try reversed site order + label = findall(p -> p.first == reverse(bond), gate.terms) + @assert length(label) == 1 + return permute(gate.terms[label[1]].second, ((2, 1), (4, 3))) + else + @assert length(label) == 1 + return gate.terms[label[1]].second + end +end diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index f583e6861..b633335db 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -11,11 +11,11 @@ Simple update of bond `peps.weights.x[r,c]` function _su_bondx!( row::Int, col::Int, - gate::AbstractTensorMap, + gate::AbstractTensorMap{S,2,2}, peps::InfiniteWeightPEPS, Dcut::Int, svderr::Float64=1e-10, -) +) where {S} Nr, Nc = size(peps) @assert 1 <= row <= Nr && 1 <= col <= Nc row2, col2 = row, _next(col, Nc) @@ -91,12 +91,13 @@ When `bipartite === true` (for square lattice), the unit cell size should be 2 x and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` """ function su_iter( - gate::AbstractTensorMap, + gate::LocalOperator, peps::InfiniteWeightPEPS, Dcut::Int, svderr::Float64=1e-10; bipartite::Bool=false, ) + @assert size(gate.lattice) == size(peps) Nr, Nc = size(peps) if bipartite @assert Nr == Nc == 2 @@ -115,20 +116,41 @@ function su_iter( peps2 = mirror_antidiag(peps2) end if bipartite - ϵ = _su_bondx!(1, 1, gate, peps2, Dcut, svderr) - (peps2.vertices[2, 2], peps2.vertices[2, 1], peps2.weights.x[2, 2]) = - deepcopy.(( - peps2.vertices[1, 1], peps2.vertices[1, 2], peps2.weights.x[1, 1] - )) - ϵ = _su_bondx!(2, 1, gate, peps2, Dcut, svderr) - (peps2.vertices[1, 2], peps2.vertices[1, 1], peps2.weights.x[1, 2]) = - deepcopy.(( - peps2.vertices[2, 1], peps2.vertices[2, 2], peps2.weights.x[2, 1] - )) + for r in 1:2 + rp1 = _next(r, 2) + term = get_gateterm( + gate, + if direction == 1 + (CartesianIndex(r, 1), CartesianIndex(r, 2)) + else + #= the bond currently at [r, 1] [r, 2] + was originally at [2, 3-r] [1, 3-r] before mirroring =# + (CartesianIndex(2, 3 - r), CartesianIndex(1, 3 - r)) + end, + ) + ϵ = _su_bondx!(r, 1, term, peps2, Dcut, svderr) + peps2.vertices[rp1, 2] = deepcopy(peps2.vertices[r, 1]) + peps2.vertices[rp1, 1] = deepcopy(peps2.vertices[r, 2]) + peps2.weights.x[rp1, 2] = deepcopy(peps2.weights.x[r, 1]) + end else for site in CartesianIndices(peps2.vertices) - row, col = Tuple(site) - ϵ = _su_bondx!(row, col, gate, peps2, Dcut) + r, c = Tuple(site) + term = get_gateterm( + gate, + if direction == 1 + (CartesianIndex(r, c), CartesianIndex(r, c + 1)) + else + #= the bond currently at [r, c] [r, c+1] + was originally at [Nr-c+1, Nc-r+1] [Nr-c, Nc-r+1] + before mirroring =# + ( + CartesianIndex((c == Nr ? Nr + 1 : Nr - c + 1), Nc - r + 1), + CartesianIndex((c == Nr ? Nr : Nr - c), Nc - r + 1), + ) + end, + ) + ϵ = _su_bondx!(r, c, term, peps2, Dcut) end end if direction == 2 @@ -145,7 +167,7 @@ until the change of bond weights is smaller than `wtdiff_tol` """ function simpleupdate( peps::InfiniteWeightPEPS, - ham::AbstractTensorMap, + ham::LocalOperator, dt::Float64, Dcut::Int; evolstep::Int=400000, @@ -160,7 +182,7 @@ function simpleupdate( @assert N1 == N2 == 2 end # exponentiating the 2-site Hamiltonian gate - gate = exp(-dt * ham) + gate = get_gate(dt, ham) wtdiff = 1e+3 wts0 = deepcopy(peps.weights) for count in 1:evolstep diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 0c58ff583..2fc26227f 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -5,7 +5,7 @@ using PEPSKit using TensorKit import Statistics: mean include("utility/measure_heis.jl") -import .MeasureHeis: gen_gate, measure_heis +import .MeasureHeis: measure_heis # benchmark data is from Phys. Rev. B 94, 035133 (2016) @@ -19,16 +19,18 @@ for ind in CartesianIndices(peps.vertices) peps.vertices[ind] /= norm(peps.vertices[ind], Inf) end # Heisenberg model Hamiltonian -H = heisenberg_XYZ(InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) -ham = gen_gate() +# (already only includes nearest neighbor terms) +ham = heisenberg_XYZ(InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) +# convert to real tensors +ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) # simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] -tols = [1e-6, 1e-7, 5e-8, 1e-8] +tols = [1e-6, 1e-8, 1e-8, 1e-8] for (n, (dt, tol)) in enumerate(zip(dts, tols)) Dcut2 = (n == 1 ? Dcut + 1 : Dcut) result = simpleupdate( - peps, ham, dt, Dcut2; bipartite=true, evolstep=10000, wtdiff_tol=tol + peps, ham, dt, Dcut2; bipartite=false, evolstep=10000, wtdiff_tol=tol ) global peps = result[1] end @@ -40,7 +42,7 @@ trscheme = truncerr(1e-9) & truncdim(χenv) ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities -meas = measure_heis(peps, H, envs) +meas = measure_heis(peps, ham, envs) display(meas) @info @sprintf("Energy = %.8f\n", meas["e_site"]) @info @sprintf("Staggered magnetization = %.8f\n", mean(meas["mag_norm"])) diff --git a/test/utility/measure_heis.jl b/test/utility/measure_heis.jl index 4827ff61a..549ecc39f 100644 --- a/test/utility/measure_heis.jl +++ b/test/utility/measure_heis.jl @@ -1,6 +1,6 @@ module MeasureHeis -export gen_gate, measure_heis +export measure_heis using TensorKit import MPSKitModels: S_x, S_y, S_z, S_exchange @@ -38,16 +38,4 @@ function measure_heis(peps::InfinitePEPS, H::LocalOperator, envs::CTMRGEnv) return results end -""" -Create nearest neighbor gate for Heisenberg model -""" -function gen_gate(J::Float64=1.0; dens_shift::Bool=false) - heis = J * real(S_exchange()) - if dens_shift - Pspace = ℂ^2 - heis = heis - (J / 4) * id(Pspace) ⊗ id(Pspace) - end - return heis -end - end From 61225736b129cef2a75acccfe0f8a2c86c9542e6 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 24 Nov 2024 19:11:38 +0800 Subject: [PATCH 28/75] remove accidentally added test --- test/localop.jl | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 test/localop.jl diff --git a/test/localop.jl b/test/localop.jl deleted file mode 100644 index 100e057ee..000000000 --- a/test/localop.jl +++ /dev/null @@ -1,4 +0,0 @@ -using MPSKitModels -using PEPSKit - -LocalOperator From 7a1d5d57fb798ac32ea0fff150c6651142e86acd Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 24 Nov 2024 19:41:35 +0800 Subject: [PATCH 29/75] Add rotation and reflection of LocalOperator --- src/algorithms/time_evolution/gatetools.jl | 80 +++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index ae2d3c7be..9e431e700 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -2,7 +2,9 @@ Convert Hamiltonian `H` with nearest neighbor terms to `exp(-dt * H)` """ function get_gate(dt::Float64, H::LocalOperator) - return LocalOperator(H.lattice, Tuple(ind => exp(-dt * op) for (ind, op) in H.terms)...) + return LocalOperator( + H.lattice, Tuple(sites => exp(-dt * op) for (sites, op) in H.terms)... + ) end """ @@ -21,3 +23,79 @@ function get_gateterm(gate::LocalOperator, bond::NTuple{2,CartesianIndex{2}}) return gate.terms[label[1]].second end end + +""" +Get the position of `site` after reflection about the anti-diagonal line +""" +function _mirror_antidiag_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(1 - c + Ncol, 1 - r + Nrow) +end + +""" +Get the position of `site` after clockwise (right) rotation by 90 degrees +""" +function _rotr90_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(c, 1 + Nrow - r) +end + +""" +Get the position of `site` after counter-clockwise (left) rotation by 90 degrees +""" +function _rotl90_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(1 + Ncol - c, r) +end + +""" +Get the position of `site` after rotation by 180 degrees +""" +function _rot180_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(1 + Nrow - r, 1 + Ncol - c) +end + +function mirror_antidiag(H::LocalOperator) + lattice2 = mirror_antidiag(H.lattice) + terms2 = ( + (Tuple(_mirror_antidiag_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + +function Base.rotr90(H::LocalOperator) + lattice2 = rotr90(H.lattice) + terms2 = ( + (Tuple(_rotr90_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + +function Base.rotl90(H::LocalOperator) + lattice2 = rotl90(H.lattice) + terms2 = ( + (Tuple(_rotl90_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + +function Base.rot180(H::LocalOperator) + lattice2 = rot180(H.lattice) + terms2 = ( + (Tuple(_rot180_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end From e616b00d360a95e29d5431e75874882cbba6a60f Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 24 Nov 2024 20:33:32 +0800 Subject: [PATCH 30/75] Improve `get_gateterm` --- src/algorithms/time_evolution/gatetools.jl | 21 ++++++++++++++-- src/algorithms/time_evolution/simpleupdate.jl | 25 ++++--------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index 9e431e700..90df249e0 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -7,15 +7,32 @@ function get_gate(dt::Float64, H::LocalOperator) ) end +""" +Check if two 2-site bonds are related by a (periodic) lattice translation +""" +function is_equivalent( + bond1::NTuple{2,CartesianIndex{2}}, + bond2::NTuple{2,CartesianIndex{2}}, + (Nrow, Ncol)::NTuple{2,Int}, +) + r1 = bond1[1] - bond1[2] + r2 = bond2[1] - bond2[2] + shift_row = bond1[1][1] - bond2[1][1] + shift_col = bond1[1][2] - bond2[1][2] + return r1 == r2 && mod(shift_row, Nrow) == 0 && mod(shift_col, Ncol) == 0 +end + """ Get the term of a 2-site gate acting on a certain bond. Input `gate` should only include one term for each nearest neighbor bond. """ function get_gateterm(gate::LocalOperator, bond::NTuple{2,CartesianIndex{2}}) - label = findall(p -> p.first == bond, gate.terms) + label = findall(p -> is_equivalent(p.first, bond, size(gate.lattice)), gate.terms) if length(label) == 0 # try reversed site order - label = findall(p -> p.first == reverse(bond), gate.terms) + label = findall( + p -> is_equivalent(p.first, reverse(bond), size(gate.lattice)), gate.terms + ) @assert length(label) == 1 return permute(gate.terms[label[1]].second, ((2, 1), (4, 3))) else diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index b633335db..9063ffc89 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -109,6 +109,7 @@ function su_iter( @assert [isdual(space(peps.weights.y[r, c], ax)) for ax in 1:2] == [0, 1] end peps2 = deepcopy(peps) + gate_mirrored = mirror_antidiag(gate) for direction in 1:2 # mirror the y-weights to x-direction # to update them using code for x-weights @@ -119,14 +120,8 @@ function su_iter( for r in 1:2 rp1 = _next(r, 2) term = get_gateterm( - gate, - if direction == 1 - (CartesianIndex(r, 1), CartesianIndex(r, 2)) - else - #= the bond currently at [r, 1] [r, 2] - was originally at [2, 3-r] [1, 3-r] before mirroring =# - (CartesianIndex(2, 3 - r), CartesianIndex(1, 3 - r)) - end, + direction == 1 ? gate : gate_mirrored, + (CartesianIndex(r, 1), CartesianIndex(r, 2)) ) ϵ = _su_bondx!(r, 1, term, peps2, Dcut, svderr) peps2.vertices[rp1, 2] = deepcopy(peps2.vertices[r, 1]) @@ -137,18 +132,8 @@ function su_iter( for site in CartesianIndices(peps2.vertices) r, c = Tuple(site) term = get_gateterm( - gate, - if direction == 1 - (CartesianIndex(r, c), CartesianIndex(r, c + 1)) - else - #= the bond currently at [r, c] [r, c+1] - was originally at [Nr-c+1, Nc-r+1] [Nr-c, Nc-r+1] - before mirroring =# - ( - CartesianIndex((c == Nr ? Nr + 1 : Nr - c + 1), Nc - r + 1), - CartesianIndex((c == Nr ? Nr : Nr - c), Nc - r + 1), - ) - end, + direction == 1 ? gate : gate_mirrored, + (CartesianIndex(r, c), CartesianIndex(r, c + 1)) ) ϵ = _su_bondx!(r, c, term, peps2, Dcut) end From ff17a713c99a506cb3c4f536a97dab252bd298af Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 25 Nov 2024 21:33:26 +0800 Subject: [PATCH 31/75] fix format --- src/algorithms/time_evolution/simpleupdate.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 9063ffc89..2c1bf02c1 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -121,7 +121,7 @@ function su_iter( rp1 = _next(r, 2) term = get_gateterm( direction == 1 ? gate : gate_mirrored, - (CartesianIndex(r, 1), CartesianIndex(r, 2)) + (CartesianIndex(r, 1), CartesianIndex(r, 2)), ) ϵ = _su_bondx!(r, 1, term, peps2, Dcut, svderr) peps2.vertices[rp1, 2] = deepcopy(peps2.vertices[r, 1]) @@ -133,7 +133,7 @@ function su_iter( r, c = Tuple(site) term = get_gateterm( direction == 1 ? gate : gate_mirrored, - (CartesianIndex(r, c), CartesianIndex(r, c + 1)) + (CartesianIndex(r, c), CartesianIndex(r, c + 1)), ) ϵ = _su_bondx!(r, c, term, peps2, Dcut) end From 69d52d43f851ff84f33be5097aacefcfbb534f2c Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 25 Nov 2024 21:36:04 +0800 Subject: [PATCH 32/75] fix format again --- src/algorithms/time_evolution/gatetools.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index 90df249e0..5f035affc 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -19,7 +19,7 @@ function is_equivalent( r2 = bond2[1] - bond2[2] shift_row = bond1[1][1] - bond2[1][1] shift_col = bond1[1][2] - bond2[1][2] - return r1 == r2 && mod(shift_row, Nrow) == 0 && mod(shift_col, Ncol) == 0 + return r1 == r2 && mod(shift_row, Nrow) == 0 && mod(shift_col, Ncol) == 0 end """ From aa519a609cd70f107159f32ad7330e25fc134ebe Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 27 Nov 2024 10:10:32 +0800 Subject: [PATCH 33/75] Introduce `SimpleUpdate` algorithm struct --- src/PEPSKit.jl | 2 +- src/algorithms/time_evolution/simpleupdate.jl | 59 ++++++++++--------- test/heisenberg_sufu.jl | 7 ++- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index d461ea2f9..0ef805479 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -173,7 +173,7 @@ export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint export absorb_wt -export su_iter, simpleupdate +export su_iter, SimpleUpdate export InfinitePEPS, InfiniteTransferPEPS export SUWeight, InfiniteWeightPEPS diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 2c1bf02c1..592818c65 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -1,3 +1,15 @@ +""" +Algorithm struct for simple update (SU) of infinite PEPS with bond weights/ +Each SU run is converged when the singular value difference becomes smaller than `tol`. +""" +struct SimpleUpdate + dt::Float64 + tol::Float64 + maxiter::Int + trscheme::TensorKit.TruncationScheme +end +const SU = SimpleUpdate + """ Simple update of bond `peps.weights.x[r,c]` ``` @@ -13,8 +25,7 @@ function _su_bondx!( col::Int, gate::AbstractTensorMap{S,2,2}, peps::InfiniteWeightPEPS, - Dcut::Int, - svderr::Float64=1e-10, + alg::SimpleUpdate, ) where {S} Nr, Nc = size(peps) @assert 1 <= row <= Nr && 1 <= col <= Nc @@ -58,8 +69,7 @@ function _su_bondx!( =# @tensor tmp[:] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] # SVD - truncscheme = truncerr(svderr) & truncdim(Dcut) - aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncscheme) + aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=alg.trscheme) #= -2 -1 -1 -2 | ↗ ↗ | @@ -91,11 +101,7 @@ When `bipartite === true` (for square lattice), the unit cell size should be 2 x and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` """ function su_iter( - gate::LocalOperator, - peps::InfiniteWeightPEPS, - Dcut::Int, - svderr::Float64=1e-10; - bipartite::Bool=false, + gate::LocalOperator, peps::InfiniteWeightPEPS, alg::SimpleUpdate; bipartite::Bool=false ) @assert size(gate.lattice) == size(peps) Nr, Nc = size(peps) @@ -123,7 +129,7 @@ function su_iter( direction == 1 ? gate : gate_mirrored, (CartesianIndex(r, 1), CartesianIndex(r, 2)), ) - ϵ = _su_bondx!(r, 1, term, peps2, Dcut, svderr) + ϵ = _su_bondx!(r, 1, term, peps2, alg) peps2.vertices[rp1, 2] = deepcopy(peps2.vertices[r, 1]) peps2.vertices[rp1, 1] = deepcopy(peps2.vertices[r, 2]) peps2.weights.x[rp1, 2] = deepcopy(peps2.weights.x[r, 1]) @@ -135,7 +141,7 @@ function su_iter( direction == 1 ? gate : gate_mirrored, (CartesianIndex(r, c), CartesianIndex(r, c + 1)), ) - ϵ = _su_bondx!(r, c, term, peps2, Dcut) + ϵ = _su_bondx!(r, c, term, peps2, alg) end end if direction == 2 @@ -146,18 +152,17 @@ function su_iter( end """ -Perform simple update (maximum `evolstep` iterations) -with nearest neighbor Hamiltonian `ham` and time step `dt` -until the change of bond weights is smaller than `wtdiff_tol` +Perform simple update with nearest neighbor Hamiltonian `ham`. +Evolution information is printed every `check_int` steps. + +This function is deliberately not exported, +since time evolution algorithms is sensitive to both initialization +and the choice of evolution parameters. """ -function simpleupdate( +function _simpleupdate( peps::InfiniteWeightPEPS, ham::LocalOperator, - dt::Float64, - Dcut::Int; - evolstep::Int=400000, - svderr::Float64=1e-10, - wtdiff_tol::Float64=1e-10, + alg::SimpleUpdate; bipartite::Bool=false, check_int::Int=500, ) @@ -167,15 +172,15 @@ function simpleupdate( @assert N1 == N2 == 2 end # exponentiating the 2-site Hamiltonian gate - gate = get_gate(dt, ham) - wtdiff = 1e+3 + gate = get_gate(alg.dt, ham) + wtdiff = 1.0 wts0 = deepcopy(peps.weights) - for count in 1:evolstep + for count in 1:(alg.maxiter) time0 = time() - peps = su_iter(gate, peps, Dcut, svderr; bipartite=bipartite) + peps = su_iter(gate, peps, alg; bipartite=bipartite) wtdiff = compare_weights(peps.weights, wts0) - converge = wtdiff < wtdiff_tol - cancel = count == evolstep + converge = (wtdiff < alg.tol) + cancel = (count == alg.maxiter) wts0 = deepcopy(peps.weights) time1 = time() if ((count == 1) || (count % check_int == 0) || converge || cancel) @@ -184,7 +189,7 @@ function simpleupdate( "SU %s %-7d: dt = %.0e, weight diff = %.3e, time = %.3f sec\n", label, count, - dt, + alg.dt, wtdiff, time1 - ((converge || cancel) ? time_start : time0) ) diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 2fc26227f..f8242226c 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -27,11 +27,12 @@ ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.term # simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-8, 1e-8, 1e-8] +maxiter = 10000 for (n, (dt, tol)) in enumerate(zip(dts, tols)) Dcut2 = (n == 1 ? Dcut + 1 : Dcut) - result = simpleupdate( - peps, ham, dt, Dcut2; bipartite=false, evolstep=10000, wtdiff_tol=tol - ) + trscheme = truncerr(1e-10) & truncdim(Dcut2) + alg = SimpleUpdate(dt, tol, maxiter, trscheme) + result = PEPSKit._simpleupdate(peps, ham, alg; bipartite=false) global peps = result[1] end # absort weight into site tensors From 2dd63106ae49a19f8dd576dfa03d29be9ab11240 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 27 Nov 2024 17:38:22 +0800 Subject: [PATCH 34/75] Export `simpleupdate`; remove abbreviated `SU` --- src/PEPSKit.jl | 2 +- src/algorithms/time_evolution/simpleupdate.jl | 7 +------ test/heisenberg_sufu.jl | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 0ef805479..1ad5996ad 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -173,7 +173,7 @@ export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint export absorb_wt -export su_iter, SimpleUpdate +export su_iter, simpleupdate, SimpleUpdate export InfinitePEPS, InfiniteTransferPEPS export SUWeight, InfiniteWeightPEPS diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 592818c65..ed35bee61 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -8,7 +8,6 @@ struct SimpleUpdate maxiter::Int trscheme::TensorKit.TruncationScheme end -const SU = SimpleUpdate """ Simple update of bond `peps.weights.x[r,c]` @@ -154,12 +153,8 @@ end """ Perform simple update with nearest neighbor Hamiltonian `ham`. Evolution information is printed every `check_int` steps. - -This function is deliberately not exported, -since time evolution algorithms is sensitive to both initialization -and the choice of evolution parameters. """ -function _simpleupdate( +function simpleupdate( peps::InfiniteWeightPEPS, ham::LocalOperator, alg::SimpleUpdate; diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index f8242226c..ab189ae3a 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -32,7 +32,7 @@ for (n, (dt, tol)) in enumerate(zip(dts, tols)) Dcut2 = (n == 1 ? Dcut + 1 : Dcut) trscheme = truncerr(1e-10) & truncdim(Dcut2) alg = SimpleUpdate(dt, tol, maxiter, trscheme) - result = PEPSKit._simpleupdate(peps, ham, alg; bipartite=false) + result = simpleupdate(peps, ham, alg; bipartite=false) global peps = result[1] end # absort weight into site tensors From ca1423f797b8ace8d3c3b73a4c4a97ebde018d21 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 28 Nov 2024 10:53:08 +0800 Subject: [PATCH 35/75] add FixedSpaceTruncation for simple update --- src/algorithms/time_evolution/simpleupdate.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index ed35bee61..0ff27135c 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -9,6 +9,14 @@ struct SimpleUpdate trscheme::TensorKit.TruncationScheme end +function truncation_scheme(alg::SimpleUpdate, v::ElementarySpace) + if alg.trscheme isa FixedSpaceTruncation + return truncspace(v) + else + return alg.trscheme + end +end + """ Simple update of bond `peps.weights.x[r,c]` ``` @@ -68,7 +76,7 @@ function _su_bondx!( =# @tensor tmp[:] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] # SVD - aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=alg.trscheme) + aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncation_scheme(alg, space(T1, 3))) #= -2 -1 -1 -2 | ↗ ↗ | From 460ea5ecd183328eb3c88df777d5bd32784ec033 Mon Sep 17 00:00:00 2001 From: sanderdemeyer <80397440+Sander-De-Meyer@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:01:53 +0100 Subject: [PATCH 36/75] Create heisenberg_sufu_onesite This tests whether one-body terms can be accurately handled by SU by rewriting them as one-body terms. This is just an example and should probably not be in the final test set. --- test/heisenberg_sufu_onesite.jl | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 test/heisenberg_sufu_onesite.jl diff --git a/test/heisenberg_sufu_onesite.jl b/test/heisenberg_sufu_onesite.jl new file mode 100644 index 000000000..8fd81c38b --- /dev/null +++ b/test/heisenberg_sufu_onesite.jl @@ -0,0 +1,71 @@ +using Test +using Printf +using Random +using PEPSKit +using TensorKit +using OptimKit +using KrylovKit +import Statistics: mean +import MPSKitModels: S_x, S_y, S_z, S_exchange +include("utility/measure_heis.jl") +import .MeasureHeis: measure_heis + +# random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) +Dcut, χenv = 4, 16 +N1, N2 = 2, 2 +Random.seed!(0) +peps = InfiniteWeightPEPS(rand, Float64, ℂ^2, ℂ^Dcut; unitcell=(N1, N2)) +# normalize vertex tensors +for ind in CartesianIndices(peps.vertices) + peps.vertices[ind] /= norm(peps.vertices[ind], Inf) +end + +# Heisenberg model Hamiltonian +# (only includes nearest neighbor terms) +lattice = InfiniteSquare(N1, N2) +onsite = TensorMap([1.0 0.0; 0.0 1.0], ℂ^2, ℂ^2) +ham = heisenberg_XYZ(lattice; Jx=1.0, Jy=1.0, Jz=1.0) +# convert to real tensors +ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) + +# Include the onsite operators in two ways +ham_SU = LocalOperator( + ham.lattice, Tuple(sites => op + (S_z() ⊗ onsite)/2 for (sites, op) in ham.terms if length(sites) == 2)... + ) +ham_CTMRG = LocalOperator(ham.lattice, Tuple(ind => op for (ind, op) in ham.terms)..., ((idx,) => S_z() for idx in vertices(lattice))...) + +# simple update with ham_SU +dts = [1e-2, 1e-3, 4e-4, 1e-4] +tols = [1e-6, 1e-8, 1e-8, 1e-8] +maxiter = 10000 +for (n, (dt, tol)) in enumerate(zip(dts, tols)) + Dcut2 = (n == 1 ? Dcut + 1 : Dcut) + trscheme = truncerr(1e-10) & truncdim(Dcut2) + alg = SimpleUpdate(dt, tol, maxiter, trscheme) + result = simpleupdate(peps, ham_SU, alg; bipartite=false) + global peps = result[1] +end +# absort weight into site tensors +peps = InfinitePEPS(peps) +# CTMRG +envs = CTMRGEnv(rand, Float64, peps, ℂ^χenv) +trscheme = truncerr(1e-9) & truncdim(χenv) +ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:simultaneous) +envs = leading_boundary(envs, peps, ctm_alg) +# measure physical quantities +meas = measure_heis(peps, ham_SU, envs) + +# CTMRG with ham_CTMRG +psi_init = InfinitePEPS(2, Dcut; unitcell = (N1, N2)) +env0 = CTMRGEnv(psi_init, ComplexSpace(χenv)); +env_init = leading_boundary(env0, psi_init, ctm_alg); + +opt_alg = PEPSOptimize(; + boundary_alg=ctm_alg, + optimizer=LBFGS(4; maxiter=100, gradtol=1e-3, verbosity=2), + gradient_alg=LinSolver(; solver=GMRES(; tol=1e-6), iterscheme=:fixed), + reuse_env=true, +) +result = fixedpoint(psi_init, ham_CTMRG, opt_alg, env_init) + +@test isapprox(result.E/(N1*N2), meas["e_site"], atol=1e-2) \ No newline at end of file From 4c3391106991f1339678e2bd4d5a357137be75f9 Mon Sep 17 00:00:00 2001 From: sanderdemeyer <80397440+Sander-De-Meyer@users.noreply.github.com> Date: Thu, 28 Nov 2024 17:28:33 +0100 Subject: [PATCH 37/75] format fix --- test/heisenberg_sufu_onesite.jl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/heisenberg_sufu_onesite.jl b/test/heisenberg_sufu_onesite.jl index 8fd81c38b..67642ff2b 100644 --- a/test/heisenberg_sufu_onesite.jl +++ b/test/heisenberg_sufu_onesite.jl @@ -30,9 +30,17 @@ ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.term # Include the onsite operators in two ways ham_SU = LocalOperator( - ham.lattice, Tuple(sites => op + (S_z() ⊗ onsite)/2 for (sites, op) in ham.terms if length(sites) == 2)... - ) -ham_CTMRG = LocalOperator(ham.lattice, Tuple(ind => op for (ind, op) in ham.terms)..., ((idx,) => S_z() for idx in vertices(lattice))...) + ham.lattice, + Tuple( + sites => op + (S_z() ⊗ onsite) / 2 for + (sites, op) in ham.terms if length(sites) == 2 + )..., +) +ham_CTMRG = LocalOperator( + ham.lattice, + Tuple(ind => op for (ind, op) in ham.terms)..., + ((idx,) => S_z() for idx in vertices(lattice))..., +) # simple update with ham_SU dts = [1e-2, 1e-3, 4e-4, 1e-4] @@ -56,7 +64,7 @@ envs = leading_boundary(envs, peps, ctm_alg) meas = measure_heis(peps, ham_SU, envs) # CTMRG with ham_CTMRG -psi_init = InfinitePEPS(2, Dcut; unitcell = (N1, N2)) +psi_init = InfinitePEPS(2, Dcut; unitcell=(N1, N2)) env0 = CTMRGEnv(psi_init, ComplexSpace(χenv)); env_init = leading_boundary(env0, psi_init, ctm_alg); @@ -68,4 +76,4 @@ opt_alg = PEPSOptimize(; ) result = fixedpoint(psi_init, ham_CTMRG, opt_alg, env_init) -@test isapprox(result.E/(N1*N2), meas["e_site"], atol=1e-2) \ No newline at end of file +@test isapprox(result.E / (N1 * N2), meas["e_site"], atol=1e-2) \ No newline at end of file From fd96a58190693120daf83c5f0f09f406bcb76df4 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 29 Nov 2024 12:23:22 +0800 Subject: [PATCH 38/75] Add spin U(1) symmetry to Heisenberg model SU test --- src/algorithms/time_evolution/simpleupdate.jl | 1 + test/heisenberg_sufu.jl | 23 ++++++++++++++----- test/utility/measure_heis.jl | 13 +++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 0ff27135c..d50434e20 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -187,6 +187,7 @@ function simpleupdate( wts0 = deepcopy(peps.weights) time1 = time() if ((count == 1) || (count % check_int == 0) || converge || cancel) + @info "Space of x-weight at [1, 1] = $(space(peps.weights.x[1, 1], 1))" label = (converge ? "conv" : (cancel ? "cancel" : "iter")) message = @sprintf( "SU %s %-7d: dt = %.0e, weight diff = %.3e, time = %.3f sec\n", diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index ab189ae3a..36fc9706b 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -10,17 +10,29 @@ import .MeasureHeis: measure_heis # benchmark data is from Phys. Rev. B 94, 035133 (2016) # random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) -Dcut, χenv = 4, 16 +Dcut, χenv, symm = 4, 16, Trivial N1, N2 = 2, 2 Random.seed!(0) -peps = InfiniteWeightPEPS(rand, Float64, ℂ^2, ℂ^Dcut; unitcell=(N1, N2)) +if symm == Trivial + Pspace = ℂ^2 + Vspace = ℂ^Dcut + Espace = ℂ^χenv +elseif symm == U1Irrep + Pspace = ℂ[U1Irrep](1//2 => 1, -1//2 => 1) + Vspace = ℂ[U1Irrep](0 => Dcut ÷ 2, 1//2 => Dcut ÷ 4, -1//2 => Dcut ÷ 4) + Espace = ℂ[U1Irrep](0 => χenv ÷ 2, 1//2 => χenv ÷ 4, -1//2 => χenv ÷ 4) +else + error("Not implemented") +end + +peps = InfiniteWeightPEPS(rand, Float64, Pspace, Vspace; unitcell=(N1, N2)) # normalize vertex tensors for ind in CartesianIndices(peps.vertices) peps.vertices[ind] /= norm(peps.vertices[ind], Inf) end # Heisenberg model Hamiltonian # (already only includes nearest neighbor terms) -ham = heisenberg_XYZ(InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) +ham = heisenberg_XYZ(ComplexF64, symm, InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) # convert to real tensors ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) @@ -29,8 +41,7 @@ dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-8, 1e-8, 1e-8] maxiter = 10000 for (n, (dt, tol)) in enumerate(zip(dts, tols)) - Dcut2 = (n == 1 ? Dcut + 1 : Dcut) - trscheme = truncerr(1e-10) & truncdim(Dcut2) + trscheme = truncerr(1e-10) & truncdim(Dcut) alg = SimpleUpdate(dt, tol, maxiter, trscheme) result = simpleupdate(peps, ham, alg; bipartite=false) global peps = result[1] @@ -38,7 +49,7 @@ end # absort weight into site tensors peps = InfinitePEPS(peps) # CTMRG -envs = CTMRGEnv(rand, Float64, peps, ℂ^χenv) +envs = CTMRGEnv(rand, Float64, peps, Espace) trscheme = truncerr(1e-9) & truncdim(χenv) ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) envs = leading_boundary(envs, peps, ctm_alg) diff --git a/test/utility/measure_heis.jl b/test/utility/measure_heis.jl index 549ecc39f..531d017b3 100644 --- a/test/utility/measure_heis.jl +++ b/test/utility/measure_heis.jl @@ -13,7 +13,16 @@ Measure magnetization on each site function cal_mags(peps::InfinitePEPS, envs::CTMRGEnv) Nr, Nc = size(peps) lattice = collect(space(t, 1) for t in peps.A) - Sas = real.([S_x(), im * S_y(), S_z()]) + # detect symmetry on physical axis + symm = sectortype(space(peps.A[1,1])) + if symm == Trivial + Sas = real.([S_x(symm), im * S_y(symm), S_z(symm)]) + elseif symm == U1Irrep + # only Sz preserves + Sas = real.([S_z(symm)]) + else + throw(ArgumentError("Unrecognized symmetry on physical axis")) + end return [ collect( expectation_value( @@ -32,7 +41,7 @@ function measure_heis(peps::InfinitePEPS, H::LocalOperator, envs::CTMRGEnv) results["e_site"] = costfun(peps, envs, H) / (Nr * Nc) results["mag"] = cal_mags(peps, envs) results["mag_norm"] = collect( - norm([results["mag"][n][r, c] for n in 1:3]) for + norm([mags[r, c] for mags in results["mag"]]) for (r, c) in Iterators.product(1:Nr, 1:Nc) ) return results From 0d47dc1a58190be243fbdecb10eb72e16446a38e Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 29 Nov 2024 12:29:44 +0800 Subject: [PATCH 39/75] Add SU-AD test for heisenberg --- ...eisenberg_sufu_onesite.jl => heisenberg_suad.jl} | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) rename test/{heisenberg_sufu_onesite.jl => heisenberg_suad.jl} (88%) diff --git a/test/heisenberg_sufu_onesite.jl b/test/heisenberg_suad.jl similarity index 88% rename from test/heisenberg_sufu_onesite.jl rename to test/heisenberg_suad.jl index 67642ff2b..889eb47ca 100644 --- a/test/heisenberg_sufu_onesite.jl +++ b/test/heisenberg_suad.jl @@ -62,18 +62,17 @@ ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:simult envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities meas = measure_heis(peps, ham_SU, envs) +display(meas) -# CTMRG with ham_CTMRG -psi_init = InfinitePEPS(2, Dcut; unitcell=(N1, N2)) -env0 = CTMRGEnv(psi_init, ComplexSpace(χenv)); -env_init = leading_boundary(env0, psi_init, ctm_alg); - +# continue with auto-diff optimization opt_alg = PEPSOptimize(; boundary_alg=ctm_alg, optimizer=LBFGS(4; maxiter=100, gradtol=1e-3, verbosity=2), gradient_alg=LinSolver(; solver=GMRES(; tol=1e-6), iterscheme=:fixed), reuse_env=true, ) -result = fixedpoint(psi_init, ham_CTMRG, opt_alg, env_init) +result = fixedpoint(peps, ham_CTMRG, opt_alg, envs) +meas2 = measure_heis(result.peps, ham_CTMRG, result.env) +display(meas2) -@test isapprox(result.E / (N1 * N2), meas["e_site"], atol=1e-2) \ No newline at end of file +@test isapprox(result.E / (N1 * N2), meas["e_site"], atol=1e-2) From df4c4b112e91666ab8194f7206f16089bbf4d243 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 29 Nov 2024 13:50:43 +0800 Subject: [PATCH 40/75] fix formatting --- src/algorithms/time_evolution/simpleupdate.jl | 2 +- test/utility/measure_heis.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index d50434e20..fbe12297d 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -187,7 +187,7 @@ function simpleupdate( wts0 = deepcopy(peps.weights) time1 = time() if ((count == 1) || (count % check_int == 0) || converge || cancel) - @info "Space of x-weight at [1, 1] = $(space(peps.weights.x[1, 1], 1))" + @info "Space of x-weight at [1, 1] = $(space(peps.weights.x[1, 1], 1))" label = (converge ? "conv" : (cancel ? "cancel" : "iter")) message = @sprintf( "SU %s %-7d: dt = %.0e, weight diff = %.3e, time = %.3f sec\n", diff --git a/test/utility/measure_heis.jl b/test/utility/measure_heis.jl index 531d017b3..e63b1b86d 100644 --- a/test/utility/measure_heis.jl +++ b/test/utility/measure_heis.jl @@ -14,7 +14,7 @@ function cal_mags(peps::InfinitePEPS, envs::CTMRGEnv) Nr, Nc = size(peps) lattice = collect(space(t, 1) for t in peps.A) # detect symmetry on physical axis - symm = sectortype(space(peps.A[1,1])) + symm = sectortype(space(peps.A[1, 1])) if symm == Trivial Sas = real.([S_x(symm), im * S_y(symm), S_z(symm)]) elseif symm == U1Irrep From f85dcb040858d55762edda7b55e30fce46f69230 Mon Sep 17 00:00:00 2001 From: sanderdemeyer <80397440+Sander-De-Meyer@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:24:14 +0100 Subject: [PATCH 41/75] add check that all operators are twosite --- src/algorithms/time_evolution/gatetools.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index 5f035affc..b2187a09c 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -2,6 +2,7 @@ Convert Hamiltonian `H` with nearest neighbor terms to `exp(-dt * H)` """ function get_gate(dt::Float64, H::LocalOperator) + @assert all([length(op.dom) for (_, op) in H.terms] .== 2) "Only two-body terms allowed" return LocalOperator( H.lattice, Tuple(sites => exp(-dt * op) for (sites, op) in H.terms)... ) From e7026eb10a5674f19b8bfc9e32ecdf262b1b5b1d Mon Sep 17 00:00:00 2001 From: sanderdemeyer <80397440+Sander-De-Meyer@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:31:38 +0100 Subject: [PATCH 42/75] update check two-body terms the code now also checks whether all interactions are defined on nearest neighbours --- src/algorithms/time_evolution/gatetools.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index b2187a09c..91412038b 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -2,7 +2,10 @@ Convert Hamiltonian `H` with nearest neighbor terms to `exp(-dt * H)` """ function get_gate(dt::Float64, H::LocalOperator) - @assert all([length(op.dom) for (_, op) in H.terms] .== 2) "Only two-body terms allowed" + @assert all([ + length(op.dom) == 2 && norm(Tuple(terms[2] - terms[1])) == 1.0 for + (terms, op) in H.terms + ]) "Only nearest-neighbour terms allowed" return LocalOperator( H.lattice, Tuple(sites => exp(-dt * op) for (sites, op) in H.terms)... ) From 003c4e2878184fe8d1d01ead1e7f4d4bcf4866fd Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 5 Dec 2024 14:44:44 +0800 Subject: [PATCH 43/75] Change PEPSOptimize parameters in `suad` test --- test/heisenberg_suad.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/heisenberg_suad.jl b/test/heisenberg_suad.jl index 889eb47ca..7902c9897 100644 --- a/test/heisenberg_suad.jl +++ b/test/heisenberg_suad.jl @@ -68,7 +68,7 @@ display(meas) opt_alg = PEPSOptimize(; boundary_alg=ctm_alg, optimizer=LBFGS(4; maxiter=100, gradtol=1e-3, verbosity=2), - gradient_alg=LinSolver(; solver=GMRES(; tol=1e-6), iterscheme=:fixed), + gradient_alg=LinSolver(; solver=GMRES(; tol=1e-6), iterscheme=:diffgauge), reuse_env=true, ) result = fixedpoint(peps, ham_CTMRG, opt_alg, envs) From ef5e08065df2ecf19979807729ad2367d4634332 Mon Sep 17 00:00:00 2001 From: sanderdemeyer <80397440+Sander-De-Meyer@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:09:00 +0100 Subject: [PATCH 44/75] Hubbard model added the Hubbard model as in the pull request from lkdvos added test for simple update on the Hubbard model for t = 1, U = 6, half filling --- src/PEPSKit.jl | 2 +- src/operators/lattices/squarelattice.jl | 2 + src/operators/models.jl | 24 ++++++++ test/hubbard_su.jl | 75 +++++++++++++++++++++++++ test/runtests.jl | 3 + 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 test/hubbard_su.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 1ad5996ad..451f6af87 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -183,6 +183,6 @@ export ReflectDepth, ReflectWidth, Rotate, RotateReflect export symmetrize!, symmetrize_retract_and_finalize! export showtypeofgrad export InfiniteSquare, vertices, nearest_neighbours, next_nearest_neighbours -export transverse_field_ising, heisenberg_XYZ, j1_j2, pwave_superconductor +export transverse_field_ising, heisenberg_XYZ, j1_j2, pwave_superconductor, hubbard_model end # module diff --git a/src/operators/lattices/squarelattice.jl b/src/operators/lattices/squarelattice.jl index 097a4f7be..a2f3d9b5e 100644 --- a/src/operators/lattices/squarelattice.jl +++ b/src/operators/lattices/squarelattice.jl @@ -12,6 +12,8 @@ struct InfiniteSquare <: AbstractLattice{2} end end +Base.size(lattice::InfiniteSquare) = (lattice.Nrows, lattice.Ncols) + function vertices(lattice::InfiniteSquare) return CartesianIndices((1:(lattice.Nrows), 1:(lattice.Ncols))) end diff --git a/src/operators/models.jl b/src/operators/models.jl index 324397ff3..f066c37d7 100644 --- a/src/operators/models.jl +++ b/src/operators/models.jl @@ -122,3 +122,27 @@ function pwave_superconductor( (neighbor => hy for neighbor in y_neighbors)..., ) end + +function MPSKitModels.hubbard_model( + T::Type{<:Number}, + particle_symmetry::Type{<:Sector}, + spin_symmetry::Type{<:Sector}, + lattice::InfiniteSquare; + t=1.0, + U=1.0, + mu=0.0, + n::Integer=0, +) + @assert n == 0 "Currently no support for imposing a fixed particle number" + hopping = + MPSKitModels.e⁺e⁻(T, particle_symmetry, spin_symmetry) + + MPSKitModels.e⁻e⁺(T, particle_symmetry, spin_symmetry) + interaction_term = MPSKitModels.nꜛnꜜ(T, particle_symmetry, spin_symmetry) + N = MPSKitModels.e_number(T, particle_symmetry, spin_symmetry) + + return LocalOperator( + fill(domain(hopping)[1], size(lattice)), + (neighbor => -t * hopping for neighbor in nearest_neighbours(lattice))..., + ((idx,) => U * interaction_term - mu * N for idx in vertices(lattice))..., + ) +end diff --git a/test/hubbard_su.jl b/test/hubbard_su.jl new file mode 100644 index 000000000..8bf8ea0fb --- /dev/null +++ b/test/hubbard_su.jl @@ -0,0 +1,75 @@ +using Test +using Printf +using Random +using PEPSKit +using TensorKit +import Statistics: mean +import LinearAlgebra: I +include("utility/measure_heis.jl") +import .MeasureHeis: measure_heis + +# random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) +Dcut, χenv, symm = 8, 20, Trivial +N1, N2 = 2, 2 +Random.seed!(0) +if symm == Trivial + Pspace = Vect[fℤ₂]((0) => 2, (1) => 2) + Vspace = Vect[fℤ₂]((0) => Dcut / 2, (1) => Dcut / 2) + Espace = Vect[fℤ₂]((0) => χenv / 2, (1) => χenv / 2) +else + error("Not implemented") +end + +peps = InfiniteWeightPEPS(rand, Float64, Pspace, Vspace; unitcell=(N1, N2)) +# normalize vertex tensors +for ind in CartesianIndices(peps.vertices) + peps.vertices[ind] /= norm(peps.vertices[ind], Inf) +end +# Hubbard model Hamiltonian +t = 1.0 +U = 6.0 +ham = hubbard_model(Float64, Trivial, Trivial, InfiniteSquare(N1, N2); t=t, U=U, mu=U / 2) +# convert to real tensors +ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) + +unit = TensorMap(Matrix{ComplexF64}(I, 4, 4), Pspace, Pspace) +one_site = [op for (ind, op) in ham.terms if length(ind) == 1][1] + +# Convert to a Hamiltonian that only includes nearest-neighbour interactions +ham = LocalOperator( + ham.lattice, + Tuple( + sites => op + (one_site ⊗ unit) / 2 for + (sites, op) in ham.terms if length(sites) == 2 + )..., +) + +# simple update +dts = [1e-2, 1e-3, 4e-4, 1e-4] +tols = [1e-6, 1e-8, 1e-8, 1e-8] +maxiter = 10000 +for (n, (dt, tol)) in enumerate(zip(dts, tols)) + trscheme = truncerr(1e-10) & truncdim(Dcut) + alg = SimpleUpdate(dt, tol, maxiter, trscheme) + result = simpleupdate(peps, ham, alg; bipartite=false) + global peps = result[1] +end + +# absort weight into site tensors +peps = InfinitePEPS(peps) +# CTMRG +envs = CTMRGEnv(rand, ComplexF64, peps, Espace) +trscheme = truncerr(1e-9) & truncdim(χenv) +ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) +envs = leading_boundary(envs, peps, ctm_alg) +# measure physical quantities +E = expectation_value(peps, ham, envs) +@info @sprintf("Energy = %.8f\n", real(E / (N1 * N2))) + +""" +Benchmark values of the ground state energy, based on https://www.osti.gov/servlets/purl/1565498 (A benchmark study of the two-dimensional Hubbard model +with auxiliary-field quantum Monte Carlo method) +""" +E_exact = Dict(0 => -1.62, 2 => -0.176, 4 => 0.8603, 6 => -0.6567, 8 => -0.5243) + +@test isapprox(real(E / (N1 * N2)), E_exact[U] - U / 2; atol=1e-2) diff --git a/test/runtests.jl b/test/runtests.jl index 19008ebb8..45bd68e31 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -56,6 +56,9 @@ end @time @safetestset "Heisenberg model (simple and full update)" begin include("heisenberg_sufu.jl") end + @time @safetestset "Hubbard model (simple update)" begin + include("hubbard_su.jl") + end @time @safetestset "J1-J2 model" begin include("j1j2_model.jl") end From 5d4feba59614b37adff7bebba2ecfb037f44bc83 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 6 Dec 2024 15:47:17 +0800 Subject: [PATCH 45/75] Clean up test on Hubbard model --- test/hubbard_su.jl | 54 ++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/test/hubbard_su.jl b/test/hubbard_su.jl index 8bf8ea0fb..5d56c2490 100644 --- a/test/hubbard_su.jl +++ b/test/hubbard_su.jl @@ -3,19 +3,15 @@ using Printf using Random using PEPSKit using TensorKit -import Statistics: mean -import LinearAlgebra: I -include("utility/measure_heis.jl") -import .MeasureHeis: measure_heis +# using AppleAccelerate # for Apple Silicon machines # random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) -Dcut, χenv, symm = 8, 20, Trivial +Dcut, symm = 8, Trivial N1, N2 = 2, 2 -Random.seed!(0) +Random.seed!(10) if symm == Trivial - Pspace = Vect[fℤ₂]((0) => 2, (1) => 2) - Vspace = Vect[fℤ₂]((0) => Dcut / 2, (1) => Dcut / 2) - Espace = Vect[fℤ₂]((0) => χenv / 2, (1) => χenv / 2) + Pspace = Vect[fℤ₂](0 => 2, 1 => 2) + Vspace = Vect[fℤ₂](0 => Dcut / 2, 1 => Dcut / 2) else error("Not implemented") end @@ -25,21 +21,18 @@ peps = InfiniteWeightPEPS(rand, Float64, Pspace, Vspace; unitcell=(N1, N2)) for ind in CartesianIndices(peps.vertices) peps.vertices[ind] /= norm(peps.vertices[ind], Inf) end -# Hubbard model Hamiltonian +# Hubbard model Hamiltonian at half-filling t = 1.0 U = 6.0 ham = hubbard_model(Float64, Trivial, Trivial, InfiniteSquare(N1, N2); t=t, U=U, mu=U / 2) -# convert to real tensors -ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) - -unit = TensorMap(Matrix{ComplexF64}(I, 4, 4), Pspace, Pspace) -one_site = [op for (ind, op) in ham.terms if length(ind) == 1][1] # Convert to a Hamiltonian that only includes nearest-neighbour interactions +unit = TensorKit.id(Pspace) +one_site = [op for (ind, op) in ham.terms if length(ind) == 1][1] ham = LocalOperator( ham.lattice, Tuple( - sites => op + (one_site ⊗ unit) / 2 for + sites => op + (one_site ⊗ unit + unit ⊗ one_site) / 4 for (sites, op) in ham.terms if length(sites) == 2 )..., ) @@ -58,18 +51,23 @@ end # absort weight into site tensors peps = InfinitePEPS(peps) # CTMRG -envs = CTMRGEnv(rand, ComplexF64, peps, Espace) -trscheme = truncerr(1e-9) & truncdim(χenv) -ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) -envs = leading_boundary(envs, peps, ctm_alg) -# measure physical quantities -E = expectation_value(peps, ham, envs) -@info @sprintf("Energy = %.8f\n", real(E / (N1 * N2))) +χenv0, χenv = 6, 20 +Espace = Vect[fℤ₂](0 => χenv0 / 2, 1 => χenv0 / 2) +envs = CTMRGEnv(rand, Float64, peps, Espace) +for χ in [χenv0, χenv] + trscheme = truncerr(1e-9) & truncdim(χ) + ctm_alg = CTMRG(; tol=1e-10, verbosity=3, trscheme=trscheme, ctmrgscheme=:sequential) + global envs = leading_boundary(envs, peps, ctm_alg) +end """ -Benchmark values of the ground state energy, based on https://www.osti.gov/servlets/purl/1565498 (A benchmark study of the two-dimensional Hubbard model -with auxiliary-field quantum Monte Carlo method) +Benchmark values of the ground state energy from +Qin, M., Shi, H., & Zhang, S. (2016). Benchmark study of the two-dimensional Hubbard model with auxiliary-field quantum Monte Carlo method. Physical Review B, 94(8), 085103. """ -E_exact = Dict(0 => -1.62, 2 => -0.176, 4 => 0.8603, 6 => -0.6567, 8 => -0.5243) - -@test isapprox(real(E / (N1 * N2)), E_exact[U] - U / 2; atol=1e-2) +# measure physical quantities +E = costfun(peps, envs, ham) / (N1 * N2) +Es_exact = Dict(0 => -1.62, 2 => -0.176, 4 => 0.8603, 6 => -0.6567, 8 => -0.5243) +E_exact = Es_exact[U] - U / 2 +@info @sprintf("Energy = %.8f\n", E) +@info @sprintf("Benchmark energy = %.8f\n", E_exact) +@test isapprox(E, E_exact; atol=1e-2) From 2e0cccd215339317c5e3d19f782dfce025c8cb9f Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Fri, 6 Dec 2024 17:09:03 +0800 Subject: [PATCH 46/75] Add t-J model Hamiltonian --- src/PEPSKit.jl | 3 ++- src/operators/models.jl | 42 +++++++++++++++++++++++++++++++++++------ test/hubbard_su.jl | 20 +++++--------------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 451f6af87..c29ca06ba 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -183,6 +183,7 @@ export ReflectDepth, ReflectWidth, Rotate, RotateReflect export symmetrize!, symmetrize_retract_and_finalize! export showtypeofgrad export InfiniteSquare, vertices, nearest_neighbours, next_nearest_neighbours -export transverse_field_ising, heisenberg_XYZ, j1_j2, pwave_superconductor, hubbard_model +export transverse_field_ising, heisenberg_XYZ, j1_j2 +export pwave_superconductor, hubbard_model, tj_model end # module diff --git a/src/operators/models.jl b/src/operators/models.jl index f066c37d7..e88ddd25d 100644 --- a/src/operators/models.jl +++ b/src/operators/models.jl @@ -134,15 +134,45 @@ function MPSKitModels.hubbard_model( n::Integer=0, ) @assert n == 0 "Currently no support for imposing a fixed particle number" + N = MPSKitModels.e_number(T, particle_symmetry, spin_symmetry) + pspace = space(N, 1) + unit = TensorKit.id(pspace) hopping = MPSKitModels.e⁺e⁻(T, particle_symmetry, spin_symmetry) + MPSKitModels.e⁻e⁺(T, particle_symmetry, spin_symmetry) interaction_term = MPSKitModels.nꜛnꜜ(T, particle_symmetry, spin_symmetry) - N = MPSKitModels.e_number(T, particle_symmetry, spin_symmetry) + site_term = U * interaction_term - mu * N + h = (-t) * hopping + (1 / 4) * (site_term ⊗ unit + unit ⊗ site_term) + return nearest_neighbour_hamiltonian(fill(pspace, size(lattice)), h) +end - return LocalOperator( - fill(domain(hopping)[1], size(lattice)), - (neighbor => -t * hopping for neighbor in nearest_neighbours(lattice))..., - ((idx,) => U * interaction_term - mu * N for idx in vertices(lattice))..., - ) +""" +Reload MPSKitModels.tj_model + +# Arguments +""" +function MPSKitModels.tj_model( + T::Type{<:Number}, + particle_symmetry::Type{<:Sector}, + spin_symmetry::Type{<:Sector}, + lattice::InfiniteSquare; + t=2.5, + J=1.0, + mu=0.0, + slave_fermion::Bool=false, +) + hopping = + TJOperators.e_plusmin(particle_symmetry, spin_symmetry; slave_fermion) + + TJOperators.e_minplus(particle_symmetry, spin_symmetry; slave_fermion) + num = TJOperators.e_number(particle_symmetry, spin_symmetry; slave_fermion) + heis = + TJOperators.S_exchange(particle_symmetry, spin_symmetry; slave_fermion) - + (1 / 4) * (num ⊗ num) + pspace = space(num, 1) + unit = TensorKit.id(pspace) + h = (-t) * hopping + J * heis - (mu / 4) * (num ⊗ unit + unit ⊗ num) + if T <: Real + h = real(h) + end + return nearest_neighbour_hamiltonian(fill(pspace, size(lattice)), h) end diff --git a/test/hubbard_su.jl b/test/hubbard_su.jl index 5d56c2490..12f3c1416 100644 --- a/test/hubbard_su.jl +++ b/test/hubbard_su.jl @@ -22,21 +22,9 @@ for ind in CartesianIndices(peps.vertices) peps.vertices[ind] /= norm(peps.vertices[ind], Inf) end # Hubbard model Hamiltonian at half-filling -t = 1.0 -U = 6.0 +t, U = 1.0, 6.0 ham = hubbard_model(Float64, Trivial, Trivial, InfiniteSquare(N1, N2); t=t, U=U, mu=U / 2) -# Convert to a Hamiltonian that only includes nearest-neighbour interactions -unit = TensorKit.id(Pspace) -one_site = [op for (ind, op) in ham.terms if length(ind) == 1][1] -ham = LocalOperator( - ham.lattice, - Tuple( - sites => op + (one_site ⊗ unit + unit ⊗ one_site) / 4 for - (sites, op) in ham.terms if length(sites) == 2 - )..., -) - # simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-8, 1e-8, 1e-8] @@ -53,10 +41,12 @@ peps = InfinitePEPS(peps) # CTMRG χenv0, χenv = 6, 20 Espace = Vect[fℤ₂](0 => χenv0 / 2, 1 => χenv0 / 2) -envs = CTMRGEnv(rand, Float64, peps, Espace) +envs = CTMRGEnv(randn, Float64, peps, Espace) for χ in [χenv0, χenv] trscheme = truncerr(1e-9) & truncdim(χ) - ctm_alg = CTMRG(; tol=1e-10, verbosity=3, trscheme=trscheme, ctmrgscheme=:sequential) + ctm_alg = CTMRG(; + maxiter=40, tol=1e-10, verbosity=3, trscheme=trscheme, ctmrgscheme=:sequential + ) global envs = leading_boundary(envs, peps, ctm_alg) end From 4650c3c1125287005aca757c3b12c1363e2334ed Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 09:40:07 +0800 Subject: [PATCH 47/75] Squashed commit of the following: commit e2545a379f0efb0e70087bb2a526ce08a6986bcc Merge: 36973e7 9532507 Author: Paul Brehmer Date: Thu Dec 5 11:06:04 2024 +0100 Merge pull request #90 from QuantumKitHub/pb-improve-sequential Make `:sequential` act column-wise commit 9532507408401bb09916ff38834c19a085ff1639 Merge: 77fc207 36973e7 Author: Lukas Devos Date: Wed Dec 4 15:32:45 2024 -0500 Merge branch 'master' into pb-improve-sequential commit 77fc207184198bdc8a1aa9d7d9b0acf78129d576 Author: Lukas Devos Date: Tue Dec 3 15:33:15 2024 -0500 reenable expansion for simultaneous ctmrg commit 51f2cc4baefe57b8351e5ab3f6835f37726a5db3 Author: Lukas Devos Date: Tue Dec 3 14:18:36 2024 -0500 excise expansion step commit 3754b67cfe249ffb0aeff33cebd0d22ac322a9c9 Merge: 8a09a82 08cf1dc Author: Paul Brehmer Date: Fri Nov 8 14:57:56 2024 +0100 Merge branch 'master' into pb-improve-sequential --- src/algorithms/ctmrg/ctmrg.jl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index e799c2586..0e5266e3a 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -161,7 +161,8 @@ function ctmrg_iter(state, envs::CTMRGEnv, alg::SequentialCTMRG) ϵ = zero(real(scalartype(state))) for _ in 1:4 # rotate for col in 1:size(state, 2) # left move column-wise - envs, info = ctmrg_leftmove(col, state, envs, alg) + projectors, info = ctmrg_projectors(col, state, envs, alg) + envs = ctmrg_renormalize(col, projectors, state, envs, alg) ϵ = max(ϵ, info.err) end state = rotate_north(state, EAST) @@ -212,7 +213,7 @@ In the `:sequential` mode the projectors are computed for the column `col`, wher in the `:simultaneous` mode, all projectors (and corresponding SVDs) are computed in parallel. """ function ctmrg_projectors( - col::Int, enlarged_envs, envs::CTMRGEnv{C,E}, alg::SequentialCTMRG + col::Int, state::InfinitePEPS, envs::CTMRGEnv{C,E}, alg::SequentialCTMRG ) where {C,E} projector_alg = alg.projector_alg ϵ = zero(real(scalartype(envs))) @@ -221,7 +222,9 @@ function ctmrg_projectors( coordinates = eachcoordinate(envs)[:, col] projectors = dtmap(coordinates) do (r, c) r′ = _prev(r, size(envs.corners, 2)) - QQ = halfinfinite_environment(enlarged_envs[1, r], enlarged_envs[2, r′]) + Q1 = TensorMap(EnlargedCorner(state, envs, (SOUTHWEST, r, c)), SOUTHWEST) + Q2 = TensorMap(EnlargedCorner(state, envs, (NORTHWEST, r′, c)), NORTHWEST) + QQ = halfinfinite_environment(Q1, Q2) trscheme = truncation_scheme(projector_alg, envs.edges[WEST, r′, c]) svd_alg = svd_algorithm(projector_alg, (WEST, r, c)) U, S, V, ϵ_local = PEPSKit.tsvd!(QQ, svd_alg; trunc=trscheme) @@ -236,7 +239,7 @@ function ctmrg_projectors( end # Compute projectors - return build_projectors(U, S, V, enlarged_envs[1, r], enlarged_envs[2, r′]) + return build_projectors(U, S, V, Q1, Q2) end return (map(first, projectors), map(last, projectors)), (; err=ϵ) end From 0c5e2236031f5840dfb7aeafdfe5c6d5f41e04d2 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 09:56:26 +0800 Subject: [PATCH 48/75] Update `ctmrg_leftmove` with latest upstream --- src/algorithms/ctmrg/ctmrg.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 0e5266e3a..92b0cce55 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -146,8 +146,7 @@ function ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) C4 → T3 → r+1 c-1 c """ - enlarged_envs = ctmrg_expand(eachcoordinate(envs, [4, 1])[:, :, col], state, envs) - projectors, info = ctmrg_projectors(col, enlarged_envs, envs, alg) + projectors, info = ctmrg_projectors(col, state, envs, alg) envs = ctmrg_renormalize(col, projectors, state, envs, alg) return envs, info end @@ -161,8 +160,7 @@ function ctmrg_iter(state, envs::CTMRGEnv, alg::SequentialCTMRG) ϵ = zero(real(scalartype(state))) for _ in 1:4 # rotate for col in 1:size(state, 2) # left move column-wise - projectors, info = ctmrg_projectors(col, state, envs, alg) - envs = ctmrg_renormalize(col, projectors, state, envs, alg) + envs, info = ctmrg_leftmove(col, state, envs, alg) ϵ = max(ϵ, info.err) end state = rotate_north(state, EAST) From 02401973a29ae3c9a49662fdd2d8061f374488ac Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 11:28:22 +0800 Subject: [PATCH 49/75] Apply suggestions from code review Co-authored-by: Lukas Devos --- src/algorithms/time_evolution/simpleupdate.jl | 4 ++-- src/states/infiniteweightpeps.jl | 11 ++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index fbe12297d..f39b66049 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -74,9 +74,9 @@ function _su_bondx!( ↑ ↑ -1← aR -← 3 -← bL ← -4 =# - @tensor tmp[:] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] + @tensor tmp[-1 -2; -4 -3] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] # SVD - aR, s, bL, ϵ = tsvd(tmp, ((1, 2), (3, 4)); trunc=truncation_scheme(alg, space(T1, 3))) + aR, s, bL, ϵ = tsvd!(tmp; trunc=truncation_scheme(alg, space(T1, 3))) #= -2 -1 -1 -2 | ↗ ↗ | diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 790e92bb8..8a97e587b 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -49,15 +49,8 @@ function Base.show(io::IO, wts::SUWeight) end end -function Base.iterate(wts::SUWeight, state=1) - nx = prod(size(wts.x)) - if 1 <= state <= nx - return wts.x[state], state + 1 - elseif nx + 1 <= state <= 2 * nx - return wts.y[state - nx], state + 1 - else - return nothing - end +function Base.iterate(wts::SUWeight, state...) + return iterate(Iterators.flatten((wts.x, wts.y), state...) end function Base.length(wts::SUWeight) From 406291b411f693d0cd42513f9fd366f5f2586a46 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 11:30:35 +0800 Subject: [PATCH 50/75] Rename for `absorb_weight` --- src/PEPSKit.jl | 2 +- src/algorithms/time_evolution/simpleupdate.jl | 12 ++++++------ src/states/infiniteweightpeps.jl | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index c29ca06ba..a69e5331e 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -172,7 +172,7 @@ export leading_boundary export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint -export absorb_wt +export absorb_weight export su_iter, simpleupdate, SimpleUpdate export InfinitePEPS, InfiniteTransferPEPS diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index f39b66049..030472444 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -40,14 +40,14 @@ function _su_bondx!( T1, T2 = peps.vertices[row, col], peps.vertices[row2, col2] # absorb environment weights for ax in (2, 4, 5) - T1 = absorb_wt(T1, row, col, ax, peps.weights) + T1 = absorb_weight(T1, row, col, ax, peps.weights) end for ax in (2, 3, 4) - T2 = absorb_wt(T2, row2, col2, ax, peps.weights) + T2 = absorb_weight(T2, row2, col2, ax, peps.weights) end # absorb bond weight - T1 = absorb_wt(T1, row, col, 3, peps.weights; sqrtwt=true) - T2 = absorb_wt(T2, row2, col2, 5, peps.weights; sqrtwt=true) + T1 = absorb_weight(T1, row, col, 3, peps.weights; sqrtwt=true) + T2 = absorb_weight(T2, row2, col2, 5, peps.weights; sqrtwt=true) #= QR and LQ decomposition 2 1 1 2 @@ -88,10 +88,10 @@ function _su_bondx!( @tensor T2[-1; -2 -3 -4 -5] := bL[-5, -1, 1] * Y[1, -2, -3, -4] # remove environment weights for ax in (2, 4, 5) - T1 = absorb_wt(T1, row, col, ax, peps.weights; invwt=true) + T1 = absorb_weight(T1, row, col, ax, peps.weights; invwt=true) end for ax in (2, 3, 4) - T2 = absorb_wt(T2, row2, col2, ax, peps.weights; invwt=true) + T2 = absorb_weight(T2, row2, col2, ax, peps.weights; invwt=true) end # update tensor dict and weight on current bond # (max element of weight is normalized to 1) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 8a97e587b..463b2d993 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -50,7 +50,7 @@ function Base.show(io::IO, wts::SUWeight) end function Base.iterate(wts::SUWeight, state...) - return iterate(Iterators.flatten((wts.x, wts.y), state...) + return iterate(Iterators.flatten((wts.x, wts.y), state...)) end function Base.length(wts::SUWeight) @@ -136,7 +136,7 @@ Weights around the tensor at `(row, col)` are ↓ ``` """ -function absorb_wt( +function absorb_weight( t::T, row::Int, col::Int, @@ -174,7 +174,7 @@ function InfinitePEPS(peps::InfiniteWeightPEPS) N1, N2 = size(vertices) for (r, c) in Iterators.product(1:N1, 1:N2) for ax in 2:5 - vertices[r, c] = absorb_wt(vertices[r, c], r, c, ax, peps.weights; sqrtwt=true) + vertices[r, c] = absorb_weight(vertices[r, c], r, c, ax, peps.weights; sqrtwt=true) end end return InfinitePEPS(vertices) From cde03567a5ffc7eb8c9fb76eaf8da50ad745c326 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 12:05:16 +0800 Subject: [PATCH 51/75] Improve SUWeight construction --- {test => examples}/hubbard_su.jl | 0 src/algorithms/time_evolution/simpleupdate.jl | 2 +- src/states/infiniteweightpeps.jl | 24 ++++++++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) rename {test => examples}/hubbard_su.jl (100%) diff --git a/test/hubbard_su.jl b/examples/hubbard_su.jl similarity index 100% rename from test/hubbard_su.jl rename to examples/hubbard_su.jl diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 030472444..820cee529 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -74,7 +74,7 @@ function _su_bondx!( ↑ ↑ -1← aR -← 3 -← bL ← -4 =# - @tensor tmp[-1 -2; -4 -3] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] + @tensor tmp[-1 -2; -3 -4] := gate[-2, -3, 1, 2] * aR[-1, 1, 3] * bL[3, 2, -4] # SVD aR, s, bL, ϵ = tsvd!(tmp; trunc=truncation_scheme(alg, space(T1, 3))) #= diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 463b2d993..1b85a1610 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -14,15 +14,24 @@ Schmidt bond weight used in simple/cluster update struct SUWeight{E<:PEPSWeight} x::Matrix{E} y::Matrix{E} + + function SUWeight(x::Matrix{E}, y::Matrix{E}) where {E<:PEPSWeight} + if size(x) != size(y) + throw( + ArgumentError( + "Matrices for x-weights and y-weights must have the same size, but got size(x) = $(size(x)) and size(y) = $(size(y)).", + ), + ) + end + return new{E}(x, y) + end end function Base.size(wts::SUWeight) - @assert size(wts.x) == size(wts.y) return size(wts.x) end function Base.eltype(wts::SUWeight) - @assert eltype(wts.x) == eltype(wts.y) return eltype(wts.x) end @@ -50,12 +59,7 @@ function Base.show(io::IO, wts::SUWeight) end function Base.iterate(wts::SUWeight, state...) - return iterate(Iterators.flatten((wts.x, wts.y), state...)) -end - -function Base.length(wts::SUWeight) - @assert size(wts.x) == size(wts.y) - return 2 * prod(size(wts.x)) + return iterate(Iterators.flatten((wts.x, wts.y)), state...) end function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) @@ -174,7 +178,9 @@ function InfinitePEPS(peps::InfiniteWeightPEPS) N1, N2 = size(vertices) for (r, c) in Iterators.product(1:N1, 1:N2) for ax in 2:5 - vertices[r, c] = absorb_weight(vertices[r, c], r, c, ax, peps.weights; sqrtwt=true) + vertices[r, c] = absorb_weight( + vertices[r, c], r, c, ax, peps.weights; sqrtwt=true + ) end end return InfinitePEPS(vertices) From 417e1cfbf27eb8ac35906d23694681774a7e8d14 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 17:34:29 +0800 Subject: [PATCH 52/75] Move geometric operations on LocalOperator --- src/algorithms/time_evolution/gatetools.jl | 76 -------------------- src/operators/localoperator.jl | 80 ++++++++++++++++++++++ 2 files changed, 80 insertions(+), 76 deletions(-) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index 91412038b..4e1675228 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -44,79 +44,3 @@ function get_gateterm(gate::LocalOperator, bond::NTuple{2,CartesianIndex{2}}) return gate.terms[label[1]].second end end - -""" -Get the position of `site` after reflection about the anti-diagonal line -""" -function _mirror_antidiag_site( - site::S, (Nrow, Ncol)::NTuple{2,Int} -) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} - r, c = site[1], site[2] - return CartesianIndex(1 - c + Ncol, 1 - r + Nrow) -end - -""" -Get the position of `site` after clockwise (right) rotation by 90 degrees -""" -function _rotr90_site( - site::S, (Nrow, Ncol)::NTuple{2,Int} -) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} - r, c = site[1], site[2] - return CartesianIndex(c, 1 + Nrow - r) -end - -""" -Get the position of `site` after counter-clockwise (left) rotation by 90 degrees -""" -function _rotl90_site( - site::S, (Nrow, Ncol)::NTuple{2,Int} -) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} - r, c = site[1], site[2] - return CartesianIndex(1 + Ncol - c, r) -end - -""" -Get the position of `site` after rotation by 180 degrees -""" -function _rot180_site( - site::S, (Nrow, Ncol)::NTuple{2,Int} -) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} - r, c = site[1], site[2] - return CartesianIndex(1 + Nrow - r, 1 + Ncol - c) -end - -function mirror_antidiag(H::LocalOperator) - lattice2 = mirror_antidiag(H.lattice) - terms2 = ( - (Tuple(_mirror_antidiag_site(site, size(H.lattice)) for site in sites) => op) for - (sites, op) in H.terms - ) - return LocalOperator(lattice2, terms2...) -end - -function Base.rotr90(H::LocalOperator) - lattice2 = rotr90(H.lattice) - terms2 = ( - (Tuple(_rotr90_site(site, size(H.lattice)) for site in sites) => op) for - (sites, op) in H.terms - ) - return LocalOperator(lattice2, terms2...) -end - -function Base.rotl90(H::LocalOperator) - lattice2 = rotl90(H.lattice) - terms2 = ( - (Tuple(_rotl90_site(site, size(H.lattice)) for site in sites) => op) for - (sites, op) in H.terms - ) - return LocalOperator(lattice2, terms2...) -end - -function Base.rot180(H::LocalOperator) - lattice2 = rot180(H.lattice) - terms2 = ( - (Tuple(_rot180_site(site, size(H.lattice)) for site in sites) => op) for - (sites, op) in H.terms - ) - return LocalOperator(lattice2, terms2...) -end diff --git a/src/operators/localoperator.jl b/src/operators/localoperator.jl index 2698ce8c0..a69f9bb41 100644 --- a/src/operators/localoperator.jl +++ b/src/operators/localoperator.jl @@ -108,3 +108,83 @@ end Base.:-(O::LocalOperator) = -1 * O Base.:-(O1::LocalOperator, O2::LocalOperator) = O1 + (-O2) + +# Rotation and mirroring +# ---------------------- + +""" +Get the position of `site` after reflection about the anti-diagonal line +""" +function _mirror_antidiag_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(1 - c + Ncol, 1 - r + Nrow) +end + +""" +Get the position of `site` after clockwise (right) rotation by 90 degrees +""" +function _rotr90_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(c, 1 + Nrow - r) +end + +""" +Get the position of `site` after counter-clockwise (left) rotation by 90 degrees +""" +function _rotl90_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(1 + Ncol - c, r) +end + +""" +Get the position of `site` after rotation by 180 degrees +""" +function _rot180_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} +) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + r, c = site[1], site[2] + return CartesianIndex(1 + Nrow - r, 1 + Ncol - c) +end + +function mirror_antidiag(H::LocalOperator) + lattice2 = mirror_antidiag(H.lattice) + terms2 = ( + (Tuple(_mirror_antidiag_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + +function Base.rotr90(H::LocalOperator) + lattice2 = rotr90(H.lattice) + terms2 = ( + (Tuple(_rotr90_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + +function Base.rotl90(H::LocalOperator) + lattice2 = rotl90(H.lattice) + terms2 = ( + (Tuple(_rotl90_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + +function Base.rot180(H::LocalOperator) + lattice2 = rot180(H.lattice) + terms2 = ( + (Tuple(_rot180_site(site, size(H.lattice)) for site in sites) => op) for + (sites, op) in H.terms + ) + return LocalOperator(lattice2, terms2...) +end + From 68b94b07a56f5345608e63af710d09003ba365ce Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 18:02:01 +0800 Subject: [PATCH 53/75] Remove `sdiag_inv_sqrt` (replaced with `sdiag_pow`) --- src/algorithms/ctmrg/ctmrg.jl | 4 ++-- src/utility/util.jl | 44 +++++++++++------------------------ 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 92b0cce55..2377561a1 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -315,7 +315,7 @@ function build_projectors( Q::AbstractTensorMap{E,3,3}, Q_next::AbstractTensorMap{E,3,3}, ) where {E<:ElementarySpace} - isqS = sdiag_inv_sqrt(S) + isqS = sdiag_pow(S, -0.5) P_left = Q_next * V' * isqS P_right = isqS * U' * Q return P_left, P_right @@ -327,7 +327,7 @@ function build_projectors( Q::EnlargedCorner, Q_next::EnlargedCorner, ) where {E<:ElementarySpace} - isqS = sdiag_inv_sqrt(S) + isqS = sdiag_pow(S, -0.5) P_left = left_projector(Q.E_1, Q.C, Q.E_2, V, isqS, Q.ket, Q.bra) P_right = right_projector( Q_next.E_1, Q_next.C, Q_next.E_2, U, isqS, Q_next.ket, Q_next.bra diff --git a/src/utility/util.jl b/src/utility/util.jl index 5b894bf95..68ef13c03 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -21,49 +21,31 @@ function _elementwise_mult(a::AbstractTensorMap, b::AbstractTensorMap) return dst end +_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a .^ pow """ -Compute S^(pow) for diagonal matrices `S` +Compute `S^pow` for diagonal matrices `S` """ -function sdiag_pow(S::AbstractTensorMap, pow::Real) - S2 = similar(S) - for (k, b) in blocks(S) - copyto!(block(S2, k), diagm(diag(b) .^ pow)) - end - return S2 -end - -# Compute √S⁻¹ for diagonal TensorMaps -_safe_inv(a, tol) = abs(a) < tol ? zero(a) : inv(a) -function sdiag_inv_sqrt(S::AbstractTensorMap; tol::Real=eps(eltype(S))^(3 / 4)) +function sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4)) tol *= norm(S, Inf) # Relative tol w.r.t. largest singular value (use norm(∘, Inf) to make differentiable) - invsq = similar(S) - - if sectortype(S) == Trivial + Spow = similar(S) + for (k, b) in blocks(S) copyto!( - invsq.data, - LinearAlgebra.diagm(_safe_inv.(LinearAlgebra.diag(S.data), tol) .^ (1 / 2)), + blocks(Spow)[k], + LinearAlgebra.diagm(_safe_pow.(LinearAlgebra.diag(b), pow, tol)), ) - else - for (k, b) in blocks(S) - copyto!( - blocks(invsq)[k], - LinearAlgebra.diagm(_safe_inv.(LinearAlgebra.diag(b), tol) .^ (1 / 2)), - ) - end end - - return invsq + return Spow end function ChainRulesCore.rrule( - ::typeof(sdiag_inv_sqrt), S::AbstractTensorMap; tol::Real=eps(eltype(S))^(3 / 4) + ::typeof(sdiag_pow), S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4) ) tol *= norm(S, Inf) - invsq = sdiag_inv_sqrt(S; tol) - function sdiag_inv_sqrt_pullback(c̄) - return (ChainRulesCore.NoTangent(), -1 / 2 * _elementwise_mult(c̄, invsq'^3)) + spow = sdiag_pow(S, pow - 1; tol) + function sdiag_pow_pullback(c̄) + return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow)) end - return invsq, sdiag_inv_sqrt_pullback + return invsq, sdiag_pow_pullback end # Check whether diagonals contain degenerate values up to absolute or relative tolerance From ee6b16eb61b76a718dbc126906b61f983b720cdd Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 9 Dec 2024 20:10:38 +0800 Subject: [PATCH 54/75] Fix rrule for sdiag_pow and formatting --- src/algorithms/ctmrg/ctmrg.jl | 4 +- src/operators/localoperator.jl | 1 - src/utility/util.jl | 7 +-- test/heisenberg_suad.jl | 78 ---------------------------------- test/heisenberg_sufu.jl | 13 ++---- test/runtests.jl | 3 -- test/utility/measure_heis.jl | 50 ---------------------- 7 files changed, 10 insertions(+), 146 deletions(-) delete mode 100644 test/heisenberg_suad.jl delete mode 100644 test/utility/measure_heis.jl diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 2377561a1..b7b004672 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -137,7 +137,7 @@ end Perform CTMRG left move on the `col`-th column """ function ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) - """ + #= ----> left move C1 ← T1 ← r-1 ↓ ‖ @@ -145,7 +145,7 @@ function ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) ↓ ‖ C4 → T3 → r+1 c-1 c - """ + =# projectors, info = ctmrg_projectors(col, state, envs, alg) envs = ctmrg_renormalize(col, projectors, state, envs, alg) return envs, info diff --git a/src/operators/localoperator.jl b/src/operators/localoperator.jl index a69f9bb41..64b80984c 100644 --- a/src/operators/localoperator.jl +++ b/src/operators/localoperator.jl @@ -187,4 +187,3 @@ function Base.rot180(H::LocalOperator) ) return LocalOperator(lattice2, terms2...) end - diff --git a/src/utility/util.jl b/src/utility/util.jl index 68ef13c03..66218978c 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -41,11 +41,12 @@ function ChainRulesCore.rrule( ::typeof(sdiag_pow), S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4) ) tol *= norm(S, Inf) - spow = sdiag_pow(S, pow - 1; tol) + spow = sdiag_pow(S, pow; tol) + spow2 = sdiag_pow(S, pow - 1; tol) function sdiag_pow_pullback(c̄) - return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow)) + return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow2)) end - return invsq, sdiag_pow_pullback + return spow, sdiag_pow_pullback end # Check whether diagonals contain degenerate values up to absolute or relative tolerance diff --git a/test/heisenberg_suad.jl b/test/heisenberg_suad.jl deleted file mode 100644 index 7902c9897..000000000 --- a/test/heisenberg_suad.jl +++ /dev/null @@ -1,78 +0,0 @@ -using Test -using Printf -using Random -using PEPSKit -using TensorKit -using OptimKit -using KrylovKit -import Statistics: mean -import MPSKitModels: S_x, S_y, S_z, S_exchange -include("utility/measure_heis.jl") -import .MeasureHeis: measure_heis - -# random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) -Dcut, χenv = 4, 16 -N1, N2 = 2, 2 -Random.seed!(0) -peps = InfiniteWeightPEPS(rand, Float64, ℂ^2, ℂ^Dcut; unitcell=(N1, N2)) -# normalize vertex tensors -for ind in CartesianIndices(peps.vertices) - peps.vertices[ind] /= norm(peps.vertices[ind], Inf) -end - -# Heisenberg model Hamiltonian -# (only includes nearest neighbor terms) -lattice = InfiniteSquare(N1, N2) -onsite = TensorMap([1.0 0.0; 0.0 1.0], ℂ^2, ℂ^2) -ham = heisenberg_XYZ(lattice; Jx=1.0, Jy=1.0, Jz=1.0) -# convert to real tensors -ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) - -# Include the onsite operators in two ways -ham_SU = LocalOperator( - ham.lattice, - Tuple( - sites => op + (S_z() ⊗ onsite) / 2 for - (sites, op) in ham.terms if length(sites) == 2 - )..., -) -ham_CTMRG = LocalOperator( - ham.lattice, - Tuple(ind => op for (ind, op) in ham.terms)..., - ((idx,) => S_z() for idx in vertices(lattice))..., -) - -# simple update with ham_SU -dts = [1e-2, 1e-3, 4e-4, 1e-4] -tols = [1e-6, 1e-8, 1e-8, 1e-8] -maxiter = 10000 -for (n, (dt, tol)) in enumerate(zip(dts, tols)) - Dcut2 = (n == 1 ? Dcut + 1 : Dcut) - trscheme = truncerr(1e-10) & truncdim(Dcut2) - alg = SimpleUpdate(dt, tol, maxiter, trscheme) - result = simpleupdate(peps, ham_SU, alg; bipartite=false) - global peps = result[1] -end -# absort weight into site tensors -peps = InfinitePEPS(peps) -# CTMRG -envs = CTMRGEnv(rand, Float64, peps, ℂ^χenv) -trscheme = truncerr(1e-9) & truncdim(χenv) -ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:simultaneous) -envs = leading_boundary(envs, peps, ctm_alg) -# measure physical quantities -meas = measure_heis(peps, ham_SU, envs) -display(meas) - -# continue with auto-diff optimization -opt_alg = PEPSOptimize(; - boundary_alg=ctm_alg, - optimizer=LBFGS(4; maxiter=100, gradtol=1e-3, verbosity=2), - gradient_alg=LinSolver(; solver=GMRES(; tol=1e-6), iterscheme=:diffgauge), - reuse_env=true, -) -result = fixedpoint(peps, ham_CTMRG, opt_alg, envs) -meas2 = measure_heis(result.peps, ham_CTMRG, result.env) -display(meas2) - -@test isapprox(result.E / (N1 * N2), meas["e_site"], atol=1e-2) diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 36fc9706b..32b3095eb 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -4,8 +4,6 @@ using Random using PEPSKit using TensorKit import Statistics: mean -include("utility/measure_heis.jl") -import .MeasureHeis: measure_heis # benchmark data is from Phys. Rev. B 94, 035133 (2016) @@ -39,7 +37,7 @@ ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.term # simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] tols = [1e-6, 1e-8, 1e-8, 1e-8] -maxiter = 10000 +maxiter = 5000 for (n, (dt, tol)) in enumerate(zip(dts, tols)) trscheme = truncerr(1e-10) & truncdim(Dcut) alg = SimpleUpdate(dt, tol, maxiter, trscheme) @@ -54,9 +52,6 @@ trscheme = truncerr(1e-9) & truncdim(χenv) ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities -meas = measure_heis(peps, ham, envs) -display(meas) -@info @sprintf("Energy = %.8f\n", meas["e_site"]) -@info @sprintf("Staggered magnetization = %.8f\n", mean(meas["mag_norm"])) -@test isapprox(meas["e_site"], -0.6675; atol=1e-3) -@test isapprox(mean(meas["mag_norm"]), 0.3767; atol=1e-3) +e_site = costfun(peps, envs, ham) +@info @sprintf("Energy = %.8f\n", e_site) +@test isapprox(e_site, -0.6675; atol=1e-3) diff --git a/test/runtests.jl b/test/runtests.jl index 45bd68e31..19008ebb8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -56,9 +56,6 @@ end @time @safetestset "Heisenberg model (simple and full update)" begin include("heisenberg_sufu.jl") end - @time @safetestset "Hubbard model (simple update)" begin - include("hubbard_su.jl") - end @time @safetestset "J1-J2 model" begin include("j1j2_model.jl") end diff --git a/test/utility/measure_heis.jl b/test/utility/measure_heis.jl deleted file mode 100644 index e63b1b86d..000000000 --- a/test/utility/measure_heis.jl +++ /dev/null @@ -1,50 +0,0 @@ -module MeasureHeis - -export measure_heis - -using TensorKit -import MPSKitModels: S_x, S_y, S_z, S_exchange -using PEPSKit -using Statistics: mean - -""" -Measure magnetization on each site -""" -function cal_mags(peps::InfinitePEPS, envs::CTMRGEnv) - Nr, Nc = size(peps) - lattice = collect(space(t, 1) for t in peps.A) - # detect symmetry on physical axis - symm = sectortype(space(peps.A[1, 1])) - if symm == Trivial - Sas = real.([S_x(symm), im * S_y(symm), S_z(symm)]) - elseif symm == U1Irrep - # only Sz preserves - Sas = real.([S_z(symm)]) - else - throw(ArgumentError("Unrecognized symmetry on physical axis")) - end - return [ - collect( - expectation_value( - peps, LocalOperator(lattice, (CartesianIndex(r, c),) => Sa), envs - ) for (r, c) in Iterators.product(1:Nr, 1:Nc) - ) for Sa in Sas - ] -end - -""" -Measure physical quantities for Heisenberg model -""" -function measure_heis(peps::InfinitePEPS, H::LocalOperator, envs::CTMRGEnv) - results = Dict{String,Any}() - Nr, Nc = size(peps) - results["e_site"] = costfun(peps, envs, H) / (Nr * Nc) - results["mag"] = cal_mags(peps, envs) - results["mag_norm"] = collect( - norm([mags[r, c] for mags in results["mag"]]) for - (r, c) in Iterators.product(1:Nr, 1:Nc) - ) - return results -end - -end From f6434f413f52bfa107bf663ab13abd07610cfaf3 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Tue, 10 Dec 2024 10:32:41 +0800 Subject: [PATCH 55/75] Fix Heisenberg SU test --- src/algorithms/toolbox.jl | 4 ++-- test/heisenberg_sufu.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 13e725e36..b89a072a8 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -58,11 +58,11 @@ function LinearAlgebra.norm(peps::InfinitePEPS, env::CTMRGEnv) end """ - correlation_length(peps::InfinitePEPS, env::CTMRGEnv; howmany=2) + correlation_length(peps::InfinitePEPS, env::CTMRGEnv; num_vals=2) Compute the PEPS correlation length based on the horizontal and vertical transfer matrices. Additionally the (normalized) eigenvalue spectrum is -returned. Specify the number of computed eigenvalues with `howmany`. +returned. Specify the number of computed eigenvalues with `num_vals`. """ function MPSKit.correlation_length(peps::InfinitePEPS, env::CTMRGEnv; num_vals=2) T = scalartype(peps) diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 32b3095eb..1b9fd2e14 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -52,6 +52,6 @@ trscheme = truncerr(1e-9) & truncdim(χenv) ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities -e_site = costfun(peps, envs, ham) +e_site = costfun(peps, envs, ham) / (N1 * N2) @info @sprintf("Energy = %.8f\n", e_site) @test isapprox(e_site, -0.6675; atol=1e-3) From 635b9cad36693ee02829680ca69f7f9bdc503477 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Tue, 10 Dec 2024 14:57:47 +0800 Subject: [PATCH 56/75] Improve some docstring --- src/states/infiniteweightpeps.jl | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 1b85a1610..9e8626b7b 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -127,8 +127,10 @@ function InfiniteWeightPEPS( end """ -Absorb environment weight on axis `ax` into tensor `t` at position `(row,col)` + absorb_weight(t::T, row::Int, col::Int, ax::Int, weights::SUWeight; sqrtwt::Bool=false, invwt::Bool=false) where {T<:PEPSTensor} +Absorb or remove environment weight on axis `ax` of PEPS tensor `t` +known to be located at position (`row`, `col`) in the unit cell. Weights around the tensor at `(row, col)` are ``` ↓ @@ -139,6 +141,32 @@ Weights around the tensor at `(row, col)` are y[r+1,c] ↓ ``` + +# Arguments +- `t::T`: The tensor of type `T` (a subtype of `PEPSTensor`) to which the weight will be absorbed. +- `row::Int`: The row index specifying the position in the tensor network. +- `col::Int`: The column index specifying the position in the tensor network. +- `ax::Int`: The axis along which the weight is absorbed. +- `weights::SUWeight`: The weight object to absorb into the tensor. +- `sqrtwt::Bool=false` (optional): If `true`, the square root of the weight is used during absorption. +- `invwt::Bool=false` (optional): If `true`, the inverse of the weight is used during absorption. + +# Details +The optional keywords `sqrtwt` and `invwt` allow for additional transformations on the weight before absorption. +If both `sqrtwt` and `invwt` are `true`, the square root of the inverse weight will be used. +The first axis of `t` should be the physical axis. + +# Examples +```julia +# Absorb the weight into the 2nd axis of tensor at position (2, 3) +absorb_weight(t, 2, 3, 2, weights) + +# Absorb the square root of the weight into the tensor +absorb_weight(t, 2, 3, 2, weights; sqrtwt=true) + +# Absorb the inverse of (i.e. remove) the weight into the tensor +absorb_weight(t, 2, 3, 2, weights; invwt=true) +``` """ function absorb_weight( t::T, From 995cceec971660ec0853536c69cdf0432ce0915a Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Tue, 10 Dec 2024 15:59:52 +0800 Subject: [PATCH 57/75] Add back `length` for `SUWeight` --- src/states/infiniteweightpeps.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 9e8626b7b..d8fdf9fe8 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -62,6 +62,10 @@ function Base.iterate(wts::SUWeight, state...) return iterate(Iterators.flatten((wts.x, wts.y)), state...) end +function Base.length(wts::SUWeight) + return 2 * prod(size(wts.x)) +end + function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) return ( isapprox(wts1.x, wts2.x; atol=atol, rtol=rtol) && From be133bc3ec47626d648aec6a84e13fdd0a3d5640 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 10 Dec 2024 07:24:28 -0500 Subject: [PATCH 58/75] update actions This should allow the tests to run, because the secrets are now explicitly passed on --- .github/workflows/{CI.yml => Tests.yml} | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) rename .github/workflows/{CI.yml => Tests.yml} (74%) diff --git a/.github/workflows/CI.yml b/.github/workflows/Tests.yml similarity index 74% rename from .github/workflows/CI.yml rename to .github/workflows/Tests.yml index 85c759003..425a4907f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/Tests.yml @@ -1,4 +1,4 @@ -name: CI +name: Tests on: push: branches: @@ -23,8 +23,8 @@ jobs: fail-fast: false matrix: version: - - 'lts' - - '1' + - 'lts' # minimal supported version + - '1' # latest released Julia version group: - ctmrg - boundarymps @@ -34,9 +34,12 @@ jobs: - ubuntu-latest - macOS-latest - windows-latest - uses: "QuantumKitHub/.github/.github/workflows/tests.yml@main" + uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/Tests.yml@main" with: group: "${{ matrix.group }}" + nthreads: 4 julia-version: "${{ matrix.version }}" os: "${{ matrix.os }}" - secrets: inherit \ No newline at end of file + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + From ea0d4bde6e240b4c821e7c68d08a4cb39070d2e2 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Tue, 10 Dec 2024 23:11:12 +0800 Subject: [PATCH 59/75] Refactoring `SUWeight` --- src/algorithms/time_evolution/simpleupdate.jl | 18 +-- src/states/infiniteweightpeps.jl | 113 +++++------------- 2 files changed, 42 insertions(+), 89 deletions(-) diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index 820cee529..ef75e061a 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -18,13 +18,13 @@ function truncation_scheme(alg::SimpleUpdate, v::ElementarySpace) end """ -Simple update of bond `peps.weights.x[r,c]` +Simple update of the x-bond `peps.weights[1,r,c]` ``` - y[r,c] y[r,c+1] + [2,r,c] [2,r,c+1] ↓ ↓ - x[r,c-1] ←- T[r,c] ←- x[r,c] ←- T[r,c+1] ← x[r,c+1] + [1,r,c-1] ← T[r,c] ← [1,r,c] ←- T[r,c+1] ← [1,r,c+1] ↓ ↓ - y[r+1,c] y[r+1,c+1] + [2,r+1,c] [2,r+1,c+1] ``` """ function _su_bondx!( @@ -96,7 +96,7 @@ function _su_bondx!( # update tensor dict and weight on current bond # (max element of weight is normalized to 1) peps.vertices[row, col], peps.vertices[row2, col2] = T1, T2 - peps.weights.x[row, col] = s / norm(s, Inf) + peps.weights[1, row, col] = s / norm(s, Inf) return ϵ end @@ -118,8 +118,8 @@ function su_iter( # TODO: make algorithm independent on the choice of dual in the network for (r, c) in Iterators.product(1:Nr, 1:Nc) @assert [isdual(space(peps.vertices[r, c], ax)) for ax in 1:5] == [0, 1, 1, 0, 0] - @assert [isdual(space(peps.weights.x[r, c], ax)) for ax in 1:2] == [0, 1] - @assert [isdual(space(peps.weights.y[r, c], ax)) for ax in 1:2] == [0, 1] + @assert [isdual(space(peps.weights[1, r, c], ax)) for ax in 1:2] == [0, 1] + @assert [isdual(space(peps.weights[2, r, c], ax)) for ax in 1:2] == [0, 1] end peps2 = deepcopy(peps) gate_mirrored = mirror_antidiag(gate) @@ -139,7 +139,7 @@ function su_iter( ϵ = _su_bondx!(r, 1, term, peps2, alg) peps2.vertices[rp1, 2] = deepcopy(peps2.vertices[r, 1]) peps2.vertices[rp1, 1] = deepcopy(peps2.vertices[r, 2]) - peps2.weights.x[rp1, 2] = deepcopy(peps2.weights.x[r, 1]) + peps2.weights[1, rp1, 2] = deepcopy(peps2.weights[1, r, 1]) end else for site in CartesianIndices(peps2.vertices) @@ -187,7 +187,7 @@ function simpleupdate( wts0 = deepcopy(peps.weights) time1 = time() if ((count == 1) || (count % check_int == 0) || converge || cancel) - @info "Space of x-weight at [1, 1] = $(space(peps.weights.x[1, 1], 1))" + @info "Space of x-weight at [1, 1] = $(space(peps.weights[1, 1, 1], 1))" label = (converge ? "conv" : (cancel ? "cancel" : "iter")) message = @sprintf( "SU %s %-7d: dt = %.0e, weight diff = %.3e, time = %.3f sec\n", diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index d8fdf9fe8..7cd3565c6 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -11,71 +11,12 @@ const PEPSWeight{S} = AbstractTensorMap{S,1,1} where {S<:ElementarySpace} """ Schmidt bond weight used in simple/cluster update """ -struct SUWeight{E<:PEPSWeight} - x::Matrix{E} - y::Matrix{E} - - function SUWeight(x::Matrix{E}, y::Matrix{E}) where {E<:PEPSWeight} - if size(x) != size(y) - throw( - ArgumentError( - "Matrices for x-weights and y-weights must have the same size, but got size(x) = $(size(x)) and size(y) = $(size(y)).", - ), - ) - end - return new{E}(x, y) - end -end - -function Base.size(wts::SUWeight) - return size(wts.x) -end - -function Base.eltype(wts::SUWeight) - return eltype(wts.x) -end - -function Base.:(==)(wts1::SUWeight, wts2::SUWeight) - return wts1.x == wts2.x && wts1.y == wts2.y -end - -function Base.:(+)(wts1::SUWeight, wts2::SUWeight) - return SUWeight(wts1.x + wts2.x, wts1.y + wts2.y) -end - -function Base.:(-)(wts1::SUWeight, wts2::SUWeight) - return SUWeight(wts1.x - wts2.x, wts1.y - wts2.y) -end - -function Base.show(io::IO, wts::SUWeight) - N1, N2 = size(wts) - for (direction, r, c) in Iterators.product("xy", 1:N1, 1:N2) - println(io, "$direction[$r,$c]: ") - wt = (direction == 'x' ? wts.x[r, c] : wts.y[r, c]) - for (k, b) in blocks(wt) - println(io, k, " = ", diag(b)) - end - end -end - -function Base.iterate(wts::SUWeight, state...) - return iterate(Iterators.flatten((wts.x, wts.y)), state...) -end - -function Base.length(wts::SUWeight) - return 2 * prod(size(wts.x)) -end - -function Base.isapprox(wts1::SUWeight, wts2::SUWeight; atol=0.0, rtol=1e-5) - return ( - isapprox(wts1.x, wts2.x; atol=atol, rtol=rtol) && - isapprox(wts1.y, wts2.y; atol=atol, rtol=rtol) - ) -end +const SUWeight{E} = Array{E,3} where {E<:PEPSWeight} function compare_weights(wts1::SUWeight, wts2::SUWeight) + @assert size(wts1) == size(wts2) wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1, wts2)) - return wtdiff / (2 * prod(size(wts1))) + return wtdiff / length(wts1) end """ @@ -89,18 +30,18 @@ struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight} <: AbstractPEPS function InfiniteWeightPEPS( vertices::Matrix{T}, weights::SUWeight{E} ) where {T<:PEPSTensor,E<:PEPSWeight} - @assert size(vertices) == size(weights) + @assert size(vertices) == size(weights)[2:end] Nr, Nc = size(vertices) for (r, c) in Iterators.product(1:Nr, 1:Nc) - space(weights.y[r, c], 1)' == space(vertices[r, c], 2) || throw( + space(weights[2, r, c], 1)' == space(vertices[r, c], 2) || throw( SpaceMismatch("South space of bond weight y$((r, c)) does not match.") ) - space(weights.y[r, c], 2)' == space(vertices[_prev(r, Nr), c], 4) || throw( + space(weights[2, r, c], 2)' == space(vertices[_prev(r, Nr), c], 4) || throw( SpaceMismatch("North space of bond weight y$((r, c)) does not match.") ) - space(weights.x[r, c], 1)' == space(vertices[r, c], 3) || + space(weights[1, r, c], 1)' == space(vertices[r, c], 3) || throw(SpaceMismatch("West space of bond weight x$((r, c)) does not match.")) - space(weights.x[r, c], 2)' == space(vertices[r, _next(c, Nc)], 5) || + space(weights[1, r, c], 2)' == space(vertices[r, _next(c, Nc)], 5) || throw(SpaceMismatch("West space of bond weight x$((r, c)) does not match.")) end return new{T,E}(vertices, weights) @@ -109,12 +50,18 @@ end """ Create an InfiniteWeightPEPS from matrices of vertex tensors, -x-weights and y-weights +and separate matrices of weights on each type of bond. """ function InfiniteWeightPEPS( - vertices::Matrix{T}, wts_x::Matrix{E}, wts_y::Matrix{E} + vertices::Matrix{T}, weight_mats::Matrix{E}... ) where {T<:PEPSTensor,E<:PEPSWeight} - return InfiniteWeightPEPS(vertices, SUWeight(wts_x, wts_y)) + n_mat = length(weight_mats) + Nr, Nc = size(weight_mats[1]) + @assert all((Nr, Nc) == size(weight_mat) for weight_mat in weight_mats) + weights = collect( + weight_mats[d][r, c] for (d, r, c) in Iterators.product(1:n_mat, 1:Nr, 1:Nc) + ) + return InfiniteWeightPEPS(vertices, weights) end """ @@ -126,7 +73,11 @@ function InfiniteWeightPEPS( f, T, Pspace::S, Nspace::S, Espace::S=Nspace; unitcell::Tuple{Int,Int}=(1, 1) ) where {S<:ElementarySpace} vertices = InfinitePEPS(f, T, Pspace, Nspace, Espace; unitcell=unitcell).A - weights = SUWeight(fill(id(Espace), unitcell), fill(id(Nspace), unitcell)) + Nr, Nc = unitcell + weights = collect( + id(d == 1 ? Espace : Nspace) for + (d, r, c) in Iterators.product(1:2, 1:Nr, 1:Nc) + ) return InfiniteWeightPEPS(vertices, weights) end @@ -181,18 +132,18 @@ function absorb_weight( sqrtwt::Bool=false, invwt::Bool=false, ) where {T<:PEPSTensor} - Nr, Nc = size(weights) + Nr, Nc = size(weights)[2:end] @assert 1 <= row <= Nr && 1 <= col <= Nc @assert 2 <= ax <= 5 pow = (sqrtwt ? 1 / 2 : 1) * (invwt ? -1 : 1) if ax == 2 # north - wt = weights.y[row, col] + wt = weights[2, row, col] elseif ax == 3 # east - wt = weights.x[row, col] + wt = weights[1, row, col] elseif ax == 4 # south - wt = weights.y[_next(row, Nr), col] + wt = weights[2, _next(row, Nr), col] else # west - wt = weights.x[row, _prev(col, Nc)] + wt = weights[1, row, _prev(col, Nc)] end wt2 = sdiag_pow(wt, pow) indices_t = collect(-1:-1:-5) @@ -219,7 +170,7 @@ function InfinitePEPS(peps::InfiniteWeightPEPS) end function Base.size(peps::InfiniteWeightPEPS) - @assert size(peps.weights.x) == size(peps.weights.y) == size(peps.vertices) + @assert size(peps.weights)[2:end] == size(peps.vertices) return size(peps.vertices) end @@ -232,11 +183,13 @@ end Mirror the unit cell of an iPEPS with weights by its anti-diagonal line """ function mirror_antidiag(peps::InfiniteWeightPEPS) + Nr, Nc = size(peps) vertices2 = mirror_antidiag(peps.vertices) for (i, t) in enumerate(vertices2) vertices2[i] = permute(t, ((1,), (3, 2, 5, 4))) end - weights2_x = mirror_antidiag(peps.weights.y) - weights2_y = mirror_antidiag(peps.weights.x) - return InfiniteWeightPEPS(vertices2, weights2_x, weights2_y) + weights2 = similar(peps.weights, (2, Nc, Nr)) + weights2[1, :, :] = mirror_antidiag(peps.weights[2, :, :]) + weights2[2, :, :] = mirror_antidiag(peps.weights[1, :, :]) + return InfiniteWeightPEPS(vertices2, weights2) end From 36baf09ac0acb03bb4086404352fe31b3beb17b3 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Tue, 10 Dec 2024 23:44:17 +0800 Subject: [PATCH 60/75] Update simple update test --- src/states/infiniteweightpeps.jl | 3 +-- test/heisenberg_sufu.jl | 46 +++++++++++++++++--------------- test/runtests.jl | 3 --- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 7cd3565c6..45b9054ed 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -75,8 +75,7 @@ function InfiniteWeightPEPS( vertices = InfinitePEPS(f, T, Pspace, Nspace, Espace; unitcell=unitcell).A Nr, Nc = unitcell weights = collect( - id(d == 1 ? Espace : Nspace) for - (d, r, c) in Iterators.product(1:2, 1:Nr, 1:Nc) + id(d == 1 ? Espace : Nspace) for (d, r, c) in Iterators.product(1:2, 1:Nr, 1:Nc) ) return InfiniteWeightPEPS(vertices, weights) end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl index 1b9fd2e14..1154f8697 100644 --- a/test/heisenberg_sufu.jl +++ b/test/heisenberg_sufu.jl @@ -3,26 +3,16 @@ using Printf using Random using PEPSKit using TensorKit -import Statistics: mean - -# benchmark data is from Phys. Rev. B 94, 035133 (2016) +using KrylovKit +using OptimKit # random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) -Dcut, χenv, symm = 4, 16, Trivial +Dcut, χenv = 2, 16 N1, N2 = 2, 2 Random.seed!(0) -if symm == Trivial - Pspace = ℂ^2 - Vspace = ℂ^Dcut - Espace = ℂ^χenv -elseif symm == U1Irrep - Pspace = ℂ[U1Irrep](1//2 => 1, -1//2 => 1) - Vspace = ℂ[U1Irrep](0 => Dcut ÷ 2, 1//2 => Dcut ÷ 4, -1//2 => Dcut ÷ 4) - Espace = ℂ[U1Irrep](0 => χenv ÷ 2, 1//2 => χenv ÷ 4, -1//2 => χenv ÷ 4) -else - error("Not implemented") -end - +Pspace = ℂ^2 +Vspace = ℂ^Dcut +Espace = ℂ^χenv peps = InfiniteWeightPEPS(rand, Float64, Pspace, Vspace; unitcell=(N1, N2)) # normalize vertex tensors for ind in CartesianIndices(peps.vertices) @@ -30,16 +20,17 @@ for ind in CartesianIndices(peps.vertices) end # Heisenberg model Hamiltonian # (already only includes nearest neighbor terms) -ham = heisenberg_XYZ(ComplexF64, symm, InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) +ham = heisenberg_XYZ(ComplexF64, Trivial, InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) # convert to real tensors ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) # simple update dts = [1e-2, 1e-3, 4e-4, 1e-4] -tols = [1e-6, 1e-8, 1e-8, 1e-8] +tols = [1e-7, 1e-8, 1e-8, 1e-8] maxiter = 5000 for (n, (dt, tol)) in enumerate(zip(dts, tols)) - trscheme = truncerr(1e-10) & truncdim(Dcut) + Dcut2 = (n == 1) ? Dcut + 2 : Dcut + trscheme = truncerr(1e-10) & truncdim(Dcut2) alg = SimpleUpdate(dt, tol, maxiter, trscheme) result = simpleupdate(peps, ham, alg; bipartite=false) global peps = result[1] @@ -53,5 +44,18 @@ ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequen envs = leading_boundary(envs, peps, ctm_alg) # measure physical quantities e_site = costfun(peps, envs, ham) / (N1 * N2) -@info @sprintf("Energy = %.8f\n", e_site) -@test isapprox(e_site, -0.6675; atol=1e-3) +@info @sprintf("Simple update energy = %.8f\n", e_site) +# benchmark data from Phys. Rev. B 94, 035133 (2016) +@test isapprox(e_site, -0.6594; atol=1e-3) + +# continue with auto differentiation +ctm_alg = CTMRG() +opt_alg = PEPSOptimize(; + boundary_alg=ctm_alg, optimizer=LBFGS(4; gradtol=1e-3, verbosity=2) +) +result = fixedpoint(peps, ham, opt_alg, envs) +ξ_h, ξ_v, = correlation_length(result.peps, result.env) +e_site2 = result.E / (N1 * N2) +@info @sprintf("Auto diff energy = %.8f\n", e_site) +@test e_site2 ≈ -0.6694421 atol = 1e-2 +@test all(@. ξ_h > 0 && ξ_v > 0) diff --git a/test/runtests.jl b/test/runtests.jl index 19008ebb8..b15ad1c6e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -53,9 +53,6 @@ end @time @safetestset "Heisenberg model" begin include("heisenberg.jl") end - @time @safetestset "Heisenberg model (simple and full update)" begin - include("heisenberg_sufu.jl") - end @time @safetestset "J1-J2 model" begin include("j1j2_model.jl") end From d68d8b959ab937ea6dd8203e3ed7ce838b95ee89 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 10 Dec 2024 11:26:41 -0500 Subject: [PATCH 61/75] disable multithreading --- .github/workflows/Tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index 425a4907f..1f19b865e 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -37,7 +37,6 @@ jobs: uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/Tests.yml@main" with: group: "${{ matrix.group }}" - nthreads: 4 julia-version: "${{ matrix.version }}" os: "${{ matrix.os }}" secrets: From 12c42979295d7157ce876236eee76165550ebfa6 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 10 Dec 2024 11:27:59 -0500 Subject: [PATCH 62/75] remove superfluous broadcats --- src/utility/util.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utility/util.jl b/src/utility/util.jl index 66218978c..554649ff3 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -21,7 +21,7 @@ function _elementwise_mult(a::AbstractTensorMap, b::AbstractTensorMap) return dst end -_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a .^ pow +_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a^pow """ Compute `S^pow` for diagonal matrices `S` """ From 0a980d98fc44cba023225b2490f6062043fafc00 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Tue, 10 Dec 2024 19:46:42 +0100 Subject: [PATCH 63/75] Add function signatures --- src/algorithms/ctmrg/ctmrg.jl | 5 +++- src/algorithms/ctmrg/gaugefix.jl | 4 ++- src/algorithms/time_evolution/gatetools.jl | 10 +++++-- src/algorithms/time_evolution/simpleupdate.jl | 29 +++++++++++++------ src/operators/localoperator.jl | 29 ++++++++++++++++--- src/operators/models.jl | 15 ++++++---- src/states/infiniteweightpeps.jl | 27 +++++++++++++---- src/utility/mirror.jl | 9 +++--- src/utility/svd.jl | 12 -------- src/utility/util.jl | 20 +++++++++++-- 10 files changed, 114 insertions(+), 46 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index b7b004672..317a72315 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -134,7 +134,9 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRG) end """ -Perform CTMRG left move on the `col`-th column + ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) + +Perform CTMRG left move on the `col`-th column. """ function ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) #= @@ -150,6 +152,7 @@ function ctmrg_leftmove(col::Int, state, envs::CTMRGEnv, alg::SequentialCTMRG) envs = ctmrg_renormalize(col, projectors, state, envs, alg) return envs, info end + """ ctmrg_iter(state, envs::CTMRGEnv, alg::CTMRG) -> envs′, info diff --git a/src/algorithms/ctmrg/gaugefix.jl b/src/algorithms/ctmrg/gaugefix.jl index b22aa256f..338395b22 100644 --- a/src/algorithms/ctmrg/gaugefix.jl +++ b/src/algorithms/ctmrg/gaugefix.jl @@ -174,7 +174,9 @@ function calc_convergence(envs, CSold, TSold) end """ -Calculate convergence of CTMRG by comparing the singular values of CTM tensors + calc_convergence(envsNew::CTMRGEnv, envsOld::CTMRGEnv) + +Calculate convergence of CTMRG by comparing the singular values of CTM tensors. """ function calc_convergence(envsNew::CTMRGEnv, envsOld::CTMRGEnv) CSOld = map(x -> tsvd(x)[2], envsOld.corners) diff --git a/src/algorithms/time_evolution/gatetools.jl b/src/algorithms/time_evolution/gatetools.jl index 4e1675228..91ebaa0fc 100644 --- a/src/algorithms/time_evolution/gatetools.jl +++ b/src/algorithms/time_evolution/gatetools.jl @@ -1,5 +1,7 @@ """ -Convert Hamiltonian `H` with nearest neighbor terms to `exp(-dt * H)` + get_gate(dt::Float64, H::LocalOperator) + +Compute `exp(-dt * H)` from the nearest neighbor Hamiltonian `H`. """ function get_gate(dt::Float64, H::LocalOperator) @assert all([ @@ -12,7 +14,9 @@ function get_gate(dt::Float64, H::LocalOperator) end """ -Check if two 2-site bonds are related by a (periodic) lattice translation + is_equivalent(bond1::NTuple{2,CartesianIndex{2}}, bond2::NTuple{2,CartesianIndex{2}}, (Nrow, Ncol)::NTuple{2,Int}) + +Check if two 2-site bonds are related by a (periodic) lattice translation. """ function is_equivalent( bond1::NTuple{2,CartesianIndex{2}}, @@ -27,6 +31,8 @@ function is_equivalent( end """ + get_gateterm(gate::LocalOperator, bond::NTuple{2,CartesianIndex{2}}) + Get the term of a 2-site gate acting on a certain bond. Input `gate` should only include one term for each nearest neighbor bond. """ diff --git a/src/algorithms/time_evolution/simpleupdate.jl b/src/algorithms/time_evolution/simpleupdate.jl index ef75e061a..685d069b4 100644 --- a/src/algorithms/time_evolution/simpleupdate.jl +++ b/src/algorithms/time_evolution/simpleupdate.jl @@ -1,5 +1,7 @@ """ -Algorithm struct for simple update (SU) of infinite PEPS with bond weights/ + struct SimpleUpdate + +Algorithm struct for simple update (SU) of infinite PEPS with bond weights. Each SU run is converged when the singular value difference becomes smaller than `tol`. """ struct SimpleUpdate @@ -18,7 +20,11 @@ function truncation_scheme(alg::SimpleUpdate, v::ElementarySpace) end """ -Simple update of the x-bond `peps.weights[1,r,c]` +_su_bondx!(row::Int, col::Int, gate::AbstractTensorMap{S,2,2}, + peps::InfiniteWeightPEPS, alg::SimpleUpdate) where {S<:ElementarySpace} + +Simple update of the x-bond `peps.weights[1,r,c]`. + ``` [2,r,c] [2,r,c+1] ↓ ↓ @@ -33,7 +39,7 @@ function _su_bondx!( gate::AbstractTensorMap{S,2,2}, peps::InfiniteWeightPEPS, alg::SimpleUpdate, -) where {S} +) where {S<:ElementarySpace} Nr, Nc = size(peps) @assert 1 <= row <= Nr && 1 <= col <= Nc row2, col2 = row, _next(col, Nc) @@ -101,11 +107,9 @@ function _su_bondx!( end """ -One round of simple update on the input -InfiniteWeightPEPS `peps` with the nearest neighbor gate `gate` + su_iter(gate::LocalOperator, peps::InfiniteWeightPEPS, alg::SimpleUpdate; bipartite::Bool=false) -When `bipartite === true` (for square lattice), the unit cell size should be 2 x 2, -and the tensor and x/y weight at `(row, col)` is the same as `(row+1, col+1)` +One round of simple update on `peps` applying the nearest neighbor `gate`. """ function su_iter( gate::LocalOperator, peps::InfiniteWeightPEPS, alg::SimpleUpdate; bipartite::Bool=false @@ -159,8 +163,15 @@ function su_iter( end """ -Perform simple update with nearest neighbor Hamiltonian `ham`. -Evolution information is printed every `check_int` steps. + simpleupdate(peps::InfiniteWeightPEPS, ham::LocalOperator, alg::SimpleUpdate; + bipartite::Bool=false, check_int::Int=500) + +Perform simple update with nearest neighbor Hamiltonian `ham`, where the evolution +information is printed every `check_int` steps. + +If `bipartite == true` (for square lattice), a unit cell size of `(2, 2)` is assumed, +as well as tensors and x/y weights which are the same across the diagonals, i.e. at +`(row, col)` and `(row+1, col+1)`. """ function simpleupdate( peps::InfiniteWeightPEPS, diff --git a/src/operators/localoperator.jl b/src/operators/localoperator.jl index 64b80984c..90845de0c 100644 --- a/src/operators/localoperator.jl +++ b/src/operators/localoperator.jl @@ -113,7 +113,11 @@ Base.:-(O1::LocalOperator, O2::LocalOperator) = O1 + (-O2) # ---------------------- """ -Get the position of `site` after reflection about the anti-diagonal line + _mirror_antidiag_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} + ) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + +Get the position of `site` after reflection about the anti-diagonal line. """ function _mirror_antidiag_site( site::S, (Nrow, Ncol)::NTuple{2,Int} @@ -123,7 +127,11 @@ function _mirror_antidiag_site( end """ -Get the position of `site` after clockwise (right) rotation by 90 degrees + _rotr90_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} + ) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + +Get the position of `site` after clockwise (right) rotation by 90 degrees. """ function _rotr90_site( site::S, (Nrow, Ncol)::NTuple{2,Int} @@ -133,7 +141,11 @@ function _rotr90_site( end """ -Get the position of `site` after counter-clockwise (left) rotation by 90 degrees + _rotl90_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} + ) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + +Get the position of `site` after counter-clockwise (left) rotation by 90 degrees. """ function _rotl90_site( site::S, (Nrow, Ncol)::NTuple{2,Int} @@ -143,7 +155,11 @@ function _rotl90_site( end """ -Get the position of `site` after rotation by 180 degrees + _rot180_site( + site::S, (Nrow, Ncol)::NTuple{2,Int} + ) where {S<:Union{CartesianIndex{2},NTuple{2,Int}}} + +Get the position of `site` after rotation by 180 degrees. """ function _rot180_site( site::S, (Nrow, Ncol)::NTuple{2,Int} @@ -152,6 +168,11 @@ function _rot180_site( return CartesianIndex(1 + Nrow - r, 1 + Ncol - c) end +""" + mirror_antidiag(H::LocalOperator) + +Mirror a `LocalOperator` across the anti-diagonal axis of its lattice. +""" function mirror_antidiag(H::LocalOperator) lattice2 = mirror_antidiag(H.lattice) terms2 = ( diff --git a/src/operators/models.jl b/src/operators/models.jl index e88ddd25d..1ca82cd35 100644 --- a/src/operators/models.jl +++ b/src/operators/models.jl @@ -1,5 +1,13 @@ ## Model Hamiltonians # ------------------- +""" + nearest_neighbour_hamiltonian( + lattice::Matrix{S}, h::AbstractTensorMap{S,2,2} + ) where {S} + +Create a nearest neighbor `LocalOperator` by specifying the 2-site interaction term `h` +which acts both in horizontal and vertical direction. +""" function nearest_neighbour_hamiltonian( lattice::Matrix{S}, h::AbstractTensorMap{S,2,2} ) where {S} @@ -54,7 +62,7 @@ end """ j1_j2([elt::Type{T}], [symm::Type{S}], [lattice::InfiniteSquare]; - J1=1.0, J2=1.0, spin=1//2, sublattice=true) + J1=1.0, J2=1.0, spin=1//2, sublattice=true) Square lattice J₁-J₂ model. The `sublattice` kwarg enables a single site unit cell via a sublattice rotation. @@ -146,11 +154,6 @@ function MPSKitModels.hubbard_model( return nearest_neighbour_hamiltonian(fill(pspace, size(lattice)), h) end -""" -Reload MPSKitModels.tj_model - -# Arguments -""" function MPSKitModels.tj_model( T::Type{<:Number}, particle_symmetry::Type{<:Sector}, diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 45b9054ed..463955235 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -9,7 +9,9 @@ Here, `ES`, `WN` denote the east/south, west/north spaces, respectively. const PEPSWeight{S} = AbstractTensorMap{S,1,1} where {S<:ElementarySpace} """ -Schmidt bond weight used in simple/cluster update + const SUWeight{E} + +Schmidt bond weight used in simple/cluster update. """ const SUWeight{E} = Array{E,3} where {E<:PEPSWeight} @@ -20,8 +22,10 @@ function compare_weights(wts1::SUWeight, wts2::SUWeight) end """ + struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight} <: AbstractPEPS + Represents an infinite projected entangled-pair state on a 2D square lattice -consisting of vertex tensors and bond weights +consisting of vertex tensors and bond weights. """ struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight} <: AbstractPEPS vertices::Matrix{T} @@ -49,6 +53,10 @@ struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight} <: AbstractPEPS end """ + InfiniteWeightPEPS( + vertices::Matrix{T}, weight_mats::Matrix{E}... + ) where {T<:PEPSTensor,E<:PEPSWeight} + Create an InfiniteWeightPEPS from matrices of vertex tensors, and separate matrices of weights on each type of bond. """ @@ -65,6 +73,10 @@ function InfiniteWeightPEPS( end """ + InfiniteWeightPEPS( + f, T, Pspace::S, Nspace::S, Espace::S=Nspace; unitcell::Tuple{Int,Int}=(1, 1) + ) where {S<:ElementarySpace} + Create an InfiniteWeightPEPS by specifying its physical, north and east spaces and unit cell. Spaces can be specified either via `Int` or via `ElementarySpace`. Bond weights are initialized as identity matrices. @@ -81,7 +93,8 @@ function InfiniteWeightPEPS( end """ - absorb_weight(t::T, row::Int, col::Int, ax::Int, weights::SUWeight; sqrtwt::Bool=false, invwt::Bool=false) where {T<:PEPSTensor} + absorb_weight(t::T, row::Int, col::Int, ax::Int, weights::SUWeight; + sqrtwt::Bool=false, invwt::Bool=false) where {T<:PEPSTensor} Absorb or remove environment weight on axis `ax` of PEPS tensor `t` known to be located at position (`row`, `col`) in the unit cell. @@ -153,7 +166,9 @@ function absorb_weight( end """ -Create `InfinitePEPS` from `InfiniteWeightPEPS` by absorbing bond weights into vertex tensors + InfinitePEPS(peps::InfiniteWeightPEPS) + +Create `InfinitePEPS` from `InfiniteWeightPEPS` by absorbing bond weights into vertex tensors. """ function InfinitePEPS(peps::InfiniteWeightPEPS) vertices = deepcopy(peps.vertices) @@ -179,7 +194,9 @@ function Base.eltype(peps::InfiniteWeightPEPS) end """ -Mirror the unit cell of an iPEPS with weights by its anti-diagonal line + mirror_antidiag(peps::InfiniteWeightPEPS) + +Mirror the unit cell of an iPEPS with weights by its anti-diagonal line. """ function mirror_antidiag(peps::InfiniteWeightPEPS) Nr, Nc = size(peps) diff --git a/src/utility/mirror.jl b/src/utility/mirror.jl index 8028d0b32..406262131 100644 --- a/src/utility/mirror.jl +++ b/src/utility/mirror.jl @@ -1,9 +1,10 @@ """ -Mirror a matrix by its anti-diagonal line -(the 45 degree line through the lower-left corner) + mirror_antidiag(arr::AbstractMatrix) -The element originally at [r, c] is moved [Nc-c+1, Nr-r+1], -i.e. the element now at [r, c] was originally at [Nr-c+1, Nc-r+1] +Mirror a matrix by its anti-diagonal line (the 45 degree line through the lower-left corner). + +The element originally at [r, c] is moved [Nc-c+1, Nr-r+1], i.e. the element now at [r, c] +was originally at [Nr-c+1, Nc-r+1] """ function mirror_antidiag(arr::AbstractMatrix) Nr, Nc = size(arr) diff --git a/src/utility/svd.jl b/src/utility/svd.jl index 7d9741095..a590c87a9 100644 --- a/src/utility/svd.jl +++ b/src/utility/svd.jl @@ -291,15 +291,3 @@ function _lorentz_broaden(x::Real, ε=1e-12) x′ = 1 / x return x′ / (x′^2 + ε) end - -""" -Given `tsvd` result `u`, `s` and `vh`, -absorb singular values `s` into `u` and `vh` by -``` - u -> u * sqrt(s), vh -> sqrt(s) * vh -``` -""" -function absorb_s(u::AbstractTensorMap, s::AbstractTensorMap, vh::AbstractTensorMap) - sqrt_s = sdiag_pow(s, 0.5) - return u * sqrt_s, sqrt_s * vh -end diff --git a/src/utility/util.jl b/src/utility/util.jl index 554649ff3..1471bd43e 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -21,9 +21,12 @@ function _elementwise_mult(a::AbstractTensorMap, b::AbstractTensorMap) return dst end -_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a^pow +_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a .^ pow + """ -Compute `S^pow` for diagonal matrices `S` + sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4)) + +Compute `S^pow` for diagonal matrices `S`. """ function sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4)) tol *= norm(S, Inf) # Relative tol w.r.t. largest singular value (use norm(∘, Inf) to make differentiable) @@ -37,6 +40,19 @@ function sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 return Spow end +""" + absorb_s(u::AbstractTensorMap, s::AbstractTensorMap, vh::AbstractTensorMap) + +Given `tsvd` result `u`, `s` and `vh`, absorb singular values `s` into `u` and `vh` by: +``` + u -> u * sqrt(s), vh -> sqrt(s) * vh +``` +""" +function absorb_s(u::AbstractTensorMap, s::AbstractTensorMap, vh::AbstractTensorMap) + sqrt_s = sdiag_pow(s, 0.5) + return u * sqrt_s, sqrt_s * vh +end + function ChainRulesCore.rrule( ::typeof(sdiag_pow), S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4) ) From 9ad9a24563732d290a524b7a459c3defb488f5a3 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Tue, 10 Dec 2024 20:30:43 +0100 Subject: [PATCH 64/75] Merge Heisenberg tests --- src/algorithms/peps_opt.jl | 4 +- src/utility/util.jl | 1 - test/heisenberg.jl | 112 ++++++++++++++++++++++++++++--------- test/heisenberg_sufu.jl | 61 -------------------- 4 files changed, 89 insertions(+), 89 deletions(-) delete mode 100644 test/heisenberg_sufu.jl diff --git a/src/algorithms/peps_opt.jl b/src/algorithms/peps_opt.jl index 39e138a1e..8b927f935 100644 --- a/src/algorithms/peps_opt.jl +++ b/src/algorithms/peps_opt.jl @@ -159,8 +159,8 @@ function fixedpoint( if scalartype(env₀) <: Real env₀ = complex(env₀) - @warn "the provided real environment was converted to a complex environment since\ - :fixed mode generally produces complex gauges; use :diffgauge mode instead to work\ + @warn "the provided real environment was converted to a complex environment since \ + :fixed mode generally produces complex gauges; use :diffgauge mode instead to work \ with purely real environments" end diff --git a/src/utility/util.jl b/src/utility/util.jl index 1471bd43e..9ca568933 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -2,7 +2,6 @@ _next(i, total) = mod1(i + 1, total) _prev(i, total) = mod1(i - 1, total) -# iterator over each coordinates """ eachcoordinate(x, dirs=1:4) diff --git a/test/heisenberg.jl b/test/heisenberg.jl index 1c05b084c..df54f0ad9 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -1,40 +1,102 @@ using Test using Random +using Printf using PEPSKit using TensorKit using KrylovKit using OptimKit # initialize parameters -χbond = 2 +Dbond = 2 χenv = 16 ctm_alg = CTMRG() opt_alg = PEPSOptimize(; boundary_alg=ctm_alg, optimizer=LBFGS(4; gradtol=1e-3, verbosity=2) ) +# compare against Juraj Hasik's data: +# https://github.com/jurajHasik/j1j2_ipeps_states/blob/main/single-site_pg-C4v-A1/j20.0/state_1s_A1_j20.0_D2_chi_opt48.dat +E_ref = -0.6602310934799577 -# initialize states -Random.seed!(91283219347) -H = heisenberg_XYZ(InfiniteSquare()) -psi_init = InfinitePEPS(2, χbond) -env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) - -# find fixedpoint -result = fixedpoint(psi_init, H, opt_alg, env_init) -ξ_h, ξ_v, = correlation_length(result.peps, result.env) - -@test result.E ≈ -0.6694421 atol = 1e-2 -@test all(@. ξ_h > 0 && ξ_v > 0) - -# same test but for 1x2 unit cell -unitcell = (1, 2) -H_1x2 = heisenberg_XYZ(InfiniteSquare(unitcell...)) -psi_init_1x2 = InfinitePEPS(2, χbond; unitcell) -env_init_1x2 = leading_boundary( - CTMRGEnv(psi_init_1x2, ComplexSpace(χenv)), psi_init_1x2, ctm_alg -) -result_1x2 = fixedpoint(psi_init_1x2, H_1x2, opt_alg, env_init_1x2) -ξ_h_1x2, ξ_v_1x2, = correlation_length(result_1x2.peps, result_1x2.env) +@testset "(1, 1) unit cell AD optimization" begin + # initialize states + Random.seed!(123) + H = heisenberg_XYZ(InfiniteSquare()) + psi_init = InfinitePEPS(2, Dbond) + env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) + + # optimize energy and compute correlation lengths + result = fixedpoint(psi_init, H, opt_alg, env_init) + ξ_h, ξ_v, = correlation_length(result.peps, result.env) + + @test result.E ≈ E_ref atol = 1e-2 + @test all(@. ξ_h > 0 && ξ_v > 0) +end + +@testset "(1, 2) unit cell AD optimization" begin + # initialize states + Random.seed!(456) + unitcell = (1, 2) + H_1x2 = heisenberg_XYZ(InfiniteSquare(unitcell...)) + psi_init_1x2 = InfinitePEPS(2, Dbond; unitcell) + env_init_1x2 = leading_boundary( + CTMRGEnv(psi_init_1x2, ComplexSpace(χenv)), psi_init_1x2, ctm_alg + ) + + # optimize energy and compute correlation lengths + result_1x2 = fixedpoint(psi_init_1x2, H_1x2, opt_alg, env_init_1x2) + ξ_h_1x2, ξ_v_1x2, = correlation_length(result_1x2.peps, result_1x2.env) + + @test result_1x2.E ≈ 2 * E_ref atol = 1e-2 + @test all(@. ξ_h_1x2 > 0 && ξ_v_1x2 > 0) +end + +@testset "Simple update into AD optimization" begin + # random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) + Random.seed!(789) + N1, N2 = 2, 2 + Pspace = ℂ^2 + Vspace = ℂ^Dbond + Espace = ℂ^χenv + peps = InfiniteWeightPEPS(rand, Float64, Pspace, Vspace; unitcell=(N1, N2)) + + # normalize vertex tensors + for ind in CartesianIndices(peps.vertices) + peps.vertices[ind] /= norm(peps.vertices[ind], Inf) + end + # Heisenberg model Hamiltonian (already only includes nearest neighbor terms) + ham = heisenberg_XYZ(InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) + # convert to real tensors + ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) + + # simple update + dts = [1e-2, 1e-3, 4e-4, 1e-4] + tols = [1e-7, 1e-8, 1e-8, 1e-8] + maxiter = 5000 + for (n, (dt, tol)) in enumerate(zip(dts, tols)) + Dbond2 = (n == 1) ? Dbond + 2 : Dbond + trscheme = truncerr(1e-10) & truncdim(Dbond2) + alg = SimpleUpdate(dt, tol, maxiter, trscheme) + result = simpleupdate(peps, ham, alg; bipartite=false) + peps = result[1] + end + + # absorb weight into site tensors and CTMRG + peps = InfinitePEPS(peps) + envs = CTMRGEnv(rand, Float64, peps, Espace) + trscheme = truncerr(1e-9) & truncdim(χenv) + envs = leading_boundary(envs, peps, CTMRG(; trscheme, ctmrgscheme=:sequential)) + + # measure physical quantities + e_site = costfun(peps, envs, ham) / (N1 * N2) + @info @sprintf("Simple update energy = %.8f\n", e_site) + # benchmark data from Phys. Rev. B 94, 035133 (2016) + @test isapprox(e_site, -0.6594; atol=1e-3) -@test result_1x2.E ≈ 2 * result.E atol = 1e-2 -@test all(@. ξ_h_1x2 > 0 && ξ_v_1x2 > 0) + # continue with auto differentiation + result = fixedpoint(peps, ham, opt_alg, envs) + ξ_h, ξ_v, = correlation_length(result.peps, result.env) + e_site2 = result.E / (N1 * N2) + @info @sprintf("Auto diff energy = %.8f\n", e_site) + @test e_site2 ≈ E_ref atol = 1e-2 + @test all(@. ξ_h > 0 && ξ_v > 0) +end diff --git a/test/heisenberg_sufu.jl b/test/heisenberg_sufu.jl deleted file mode 100644 index 1154f8697..000000000 --- a/test/heisenberg_sufu.jl +++ /dev/null @@ -1,61 +0,0 @@ -using Test -using Printf -using Random -using PEPSKit -using TensorKit -using KrylovKit -using OptimKit - -# random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) -Dcut, χenv = 2, 16 -N1, N2 = 2, 2 -Random.seed!(0) -Pspace = ℂ^2 -Vspace = ℂ^Dcut -Espace = ℂ^χenv -peps = InfiniteWeightPEPS(rand, Float64, Pspace, Vspace; unitcell=(N1, N2)) -# normalize vertex tensors -for ind in CartesianIndices(peps.vertices) - peps.vertices[ind] /= norm(peps.vertices[ind], Inf) -end -# Heisenberg model Hamiltonian -# (already only includes nearest neighbor terms) -ham = heisenberg_XYZ(ComplexF64, Trivial, InfiniteSquare(N1, N2); Jx=1.0, Jy=1.0, Jz=1.0) -# convert to real tensors -ham = LocalOperator(ham.lattice, Tuple(ind => real(op) for (ind, op) in ham.terms)...) - -# simple update -dts = [1e-2, 1e-3, 4e-4, 1e-4] -tols = [1e-7, 1e-8, 1e-8, 1e-8] -maxiter = 5000 -for (n, (dt, tol)) in enumerate(zip(dts, tols)) - Dcut2 = (n == 1) ? Dcut + 2 : Dcut - trscheme = truncerr(1e-10) & truncdim(Dcut2) - alg = SimpleUpdate(dt, tol, maxiter, trscheme) - result = simpleupdate(peps, ham, alg; bipartite=false) - global peps = result[1] -end -# absort weight into site tensors -peps = InfinitePEPS(peps) -# CTMRG -envs = CTMRGEnv(rand, Float64, peps, Espace) -trscheme = truncerr(1e-9) & truncdim(χenv) -ctm_alg = CTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme, ctmrgscheme=:sequential) -envs = leading_boundary(envs, peps, ctm_alg) -# measure physical quantities -e_site = costfun(peps, envs, ham) / (N1 * N2) -@info @sprintf("Simple update energy = %.8f\n", e_site) -# benchmark data from Phys. Rev. B 94, 035133 (2016) -@test isapprox(e_site, -0.6594; atol=1e-3) - -# continue with auto differentiation -ctm_alg = CTMRG() -opt_alg = PEPSOptimize(; - boundary_alg=ctm_alg, optimizer=LBFGS(4; gradtol=1e-3, verbosity=2) -) -result = fixedpoint(peps, ham, opt_alg, envs) -ξ_h, ξ_v, = correlation_length(result.peps, result.env) -e_site2 = result.E / (N1 * N2) -@info @sprintf("Auto diff energy = %.8f\n", e_site) -@test e_site2 ≈ -0.6694421 atol = 1e-2 -@test all(@. ξ_h > 0 && ξ_v > 0) From dfb45935f7fec101b356ba26dff227bdc6ddc15a Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 11 Dec 2024 10:30:15 +0800 Subject: [PATCH 65/75] Fix docstring and error messages --- examples/hubbard_su.jl | 1 - src/states/infiniteweightpeps.jl | 38 ++++++++++++++------------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/hubbard_su.jl b/examples/hubbard_su.jl index 12f3c1416..4bd848462 100644 --- a/examples/hubbard_su.jl +++ b/examples/hubbard_su.jl @@ -3,7 +3,6 @@ using Printf using Random using PEPSKit using TensorKit -# using AppleAccelerate # for Apple Silicon machines # random initialization of 2x2 iPEPS with weights and CTMRGEnv (using real numbers) Dcut, symm = 8, Trivial diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 463955235..d18c397d5 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -2,16 +2,15 @@ """ const PEPSWeight{S} -Default type for PEPS bond weights with 2 virtual indices, -conventionally ordered as: ``wt : ES ← WN``. -Here, `ES`, `WN` denote the east/south, west/north spaces, respectively. +Default type for PEPS bond weights with 2 virtual indices, conventionally ordered as: ``wt : WS ← EN``. +`WS`, `EN` denote the west/south, east/north spaces for x/y-weights on the square lattice, respectively. """ const PEPSWeight{S} = AbstractTensorMap{S,1,1} where {S<:ElementarySpace} """ const SUWeight{E} -Schmidt bond weight used in simple/cluster update. +Array of Schmidt bond weights used in simple/cluster update. """ const SUWeight{E} = Array{E,3} where {E<:PEPSWeight} @@ -46,7 +45,7 @@ struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight} <: AbstractPEPS space(weights[1, r, c], 1)' == space(vertices[r, c], 3) || throw(SpaceMismatch("West space of bond weight x$((r, c)) does not match.")) space(weights[1, r, c], 2)' == space(vertices[r, _next(c, Nc)], 5) || - throw(SpaceMismatch("West space of bond weight x$((r, c)) does not match.")) + throw(SpaceMismatch("East space of bond weight x$((r, c)) does not match.")) end return new{T,E}(vertices, weights) end @@ -58,7 +57,7 @@ end ) where {T<:PEPSTensor,E<:PEPSWeight} Create an InfiniteWeightPEPS from matrices of vertex tensors, -and separate matrices of weights on each type of bond. +and separate matrices of weights on each type of bond at all locations in the unit cell. """ function InfiniteWeightPEPS( vertices::Matrix{T}, weight_mats::Matrix{E}... @@ -77,8 +76,7 @@ end f, T, Pspace::S, Nspace::S, Espace::S=Nspace; unitcell::Tuple{Int,Int}=(1, 1) ) where {S<:ElementarySpace} -Create an InfiniteWeightPEPS by specifying its physical, north and east spaces and unit cell. -Spaces can be specified either via `Int` or via `ElementarySpace`. +Create an InfiniteWeightPEPS by specifying its physical, north and east spaces (as `ElementarySpace`s) and unit cell size. Bond weights are initialized as identity matrices. """ function InfiniteWeightPEPS( @@ -100,28 +98,26 @@ Absorb or remove environment weight on axis `ax` of PEPS tensor `t` known to be located at position (`row`, `col`) in the unit cell. Weights around the tensor at `(row, col)` are ``` - ↓ - y[r,c] - ↓ - ←x[r,c-1] ← T[r,c] ← x[r,c] ← - ↓ - y[r+1,c] - ↓ + ↓ + [2,r,c] + ↓ + ← [1,r,c-1] ← T[r,c] ← [1,r,c] ← + ↓ + [1,r+1,c] + ↓ ``` # Arguments -- `t::T`: The tensor of type `T` (a subtype of `PEPSTensor`) to which the weight will be absorbed. +- `t::T`: The tensor of type `T` (a subtype of `PEPSTensor`) to which the weight will be absorbed. The first axis of `t` should be the physical axis. - `row::Int`: The row index specifying the position in the tensor network. - `col::Int`: The column index specifying the position in the tensor network. - `ax::Int`: The axis along which the weight is absorbed. - `weights::SUWeight`: The weight object to absorb into the tensor. -- `sqrtwt::Bool=false` (optional): If `true`, the square root of the weight is used during absorption. -- `invwt::Bool=false` (optional): If `true`, the inverse of the weight is used during absorption. +- `sqrtwt::Bool=false` (optional): If `true`, the square root of the weight is absorbed. +- `invwt::Bool=false` (optional): If `true`, the inverse of the weight is absorbed. # Details -The optional keywords `sqrtwt` and `invwt` allow for additional transformations on the weight before absorption. -If both `sqrtwt` and `invwt` are `true`, the square root of the inverse weight will be used. -The first axis of `t` should be the physical axis. +The optional kwargs `sqrtwt` and `invwt` allow taking the square root or the inverse of the weight before absorption. # Examples ```julia From e0dd9855971b03ba9b1ad439e732f36d4ff64a44 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 11 Dec 2024 11:54:21 +0800 Subject: [PATCH 66/75] Promote `SUWeight` to a custom `struct` --- src/states/infiniteweightpeps.jl | 60 +++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index d18c397d5..0977980c5 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -8,15 +8,43 @@ Default type for PEPS bond weights with 2 virtual indices, conventionally ordere const PEPSWeight{S} = AbstractTensorMap{S,1,1} where {S<:ElementarySpace} """ - const SUWeight{E} + struct SUWeight{E<:PEPSWeight} -Array of Schmidt bond weights used in simple/cluster update. +Schmidt bond weights used in simple/cluster update. +Weight elements are always real. """ -const SUWeight{E} = Array{E,3} where {E<:PEPSWeight} +struct SUWeight{E<:PEPSWeight} + data::Array{E,3} + + function SUWeight(data::Array{E,3}) where {E<:PEPSWeight} + @assert eltype(data[1]) <: Real + return new{E}(data) + end +end + +function SUWeight(wts_mats::AbstractMatrix{E}...) where {E<:PEPSWeight} + n_mat = length(wts_mats) + Nr, Nc = size(wts_mats[1]) + @assert all((Nr, Nc) == size(wts_mat) for wts_mat in wts_mats) + weights = collect( + wts_mats[d][r, c] for (d, r, c) in Iterators.product(1:n_mat, 1:Nr, 1:Nc) + ) + return SUWeight(weights) +end + +## Shape and size +Base.size(W::SUWeight) = size(W.data) +Base.size(W::SUWeight, i) = size(W.data, i) +Base.length(W::SUWeight) = length(W.data) +Base.eltype(W::SUWeight) = eltype(W.data[1]) + +Base.getindex(W::SUWeight, args...) = Base.getindex(W.data, args...) +Base.setindex!(W::SUWeight, args...) = (Base.setindex!(W.data, args...); W) +Base.axes(W::SUWeight, args...) = axes(W.data, args...) function compare_weights(wts1::SUWeight, wts2::SUWeight) @assert size(wts1) == size(wts2) - wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1, wts2)) + wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1.data, wts2.data)) return wtdiff / length(wts1) end @@ -62,13 +90,7 @@ and separate matrices of weights on each type of bond at all locations in the un function InfiniteWeightPEPS( vertices::Matrix{T}, weight_mats::Matrix{E}... ) where {T<:PEPSTensor,E<:PEPSWeight} - n_mat = length(weight_mats) - Nr, Nc = size(weight_mats[1]) - @assert all((Nr, Nc) == size(weight_mat) for weight_mat in weight_mats) - weights = collect( - weight_mats[d][r, c] for (d, r, c) in Iterators.product(1:n_mat, 1:Nr, 1:Nc) - ) - return InfiniteWeightPEPS(vertices, weights) + return InfiniteWeightPEPS(vertices, SUWeight(weight_mats...)) end """ @@ -77,7 +99,8 @@ end ) where {S<:ElementarySpace} Create an InfiniteWeightPEPS by specifying its physical, north and east spaces (as `ElementarySpace`s) and unit cell size. -Bond weights are initialized as identity matrices. +Use `T` to specify the element type of the vertex tensors. +Bond weights are initialized as identity matrices of element type `Float64`. """ function InfiniteWeightPEPS( f, T, Pspace::S, Nspace::S, Espace::S=Nspace; unitcell::Tuple{Int,Int}=(1, 1) @@ -87,7 +110,7 @@ function InfiniteWeightPEPS( weights = collect( id(d == 1 ? Espace : Nspace) for (d, r, c) in Iterators.product(1:2, 1:Nr, 1:Nc) ) - return InfiniteWeightPEPS(vertices, weights) + return InfiniteWeightPEPS(vertices, SUWeight(weights)) end """ @@ -180,13 +203,11 @@ function InfinitePEPS(peps::InfiniteWeightPEPS) end function Base.size(peps::InfiniteWeightPEPS) - @assert size(peps.weights)[2:end] == size(peps.vertices) return size(peps.vertices) end function Base.eltype(peps::InfiniteWeightPEPS) - @assert eltype(peps.weights) == eltype(peps.vertices) - return eltype(peps.vertices) + return (eltype(peps.vertices[1]), eltype(peps.weights[1])) end """ @@ -200,8 +221,7 @@ function mirror_antidiag(peps::InfiniteWeightPEPS) for (i, t) in enumerate(vertices2) vertices2[i] = permute(t, ((1,), (3, 2, 5, 4))) end - weights2 = similar(peps.weights, (2, Nc, Nr)) - weights2[1, :, :] = mirror_antidiag(peps.weights[2, :, :]) - weights2[2, :, :] = mirror_antidiag(peps.weights[1, :, :]) - return InfiniteWeightPEPS(vertices2, weights2) + weights2_x = mirror_antidiag(peps.weights[2, :, :]) + weights2_y = mirror_antidiag(peps.weights[1, :, :]) + return InfiniteWeightPEPS(vertices2, weights2_x, weights2_y) end From 8e8794437deceb5094dee6eea4d98e6ba1535c10 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 11 Dec 2024 11:58:48 +0800 Subject: [PATCH 67/75] Fix formatting --- src/states/infiniteweightpeps.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 0977980c5..761fe378e 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -22,7 +22,7 @@ struct SUWeight{E<:PEPSWeight} end end -function SUWeight(wts_mats::AbstractMatrix{E}...) where {E<:PEPSWeight} +function SUWeight(wts_mats::AbstractMatrix{E}...) where {E<:PEPSWeight} n_mat = length(wts_mats) Nr, Nc = size(wts_mats[1]) @assert all((Nr, Nc) == size(wts_mat) for wts_mat in wts_mats) @@ -44,7 +44,9 @@ Base.axes(W::SUWeight, args...) = axes(W.data, args...) function compare_weights(wts1::SUWeight, wts2::SUWeight) @assert size(wts1) == size(wts2) - wtdiff = sum(_singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1.data, wts2.data)) + wtdiff = sum( + _singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1.data, wts2.data) + ) return wtdiff / length(wts1) end @@ -117,7 +119,7 @@ end absorb_weight(t::T, row::Int, col::Int, ax::Int, weights::SUWeight; sqrtwt::Bool=false, invwt::Bool=false) where {T<:PEPSTensor} -Absorb or remove environment weight on axis `ax` of PEPS tensor `t` +Absorb or remove environment weight on axis `ax` of vertex tensor `t` known to be located at position (`row`, `col`) in the unit cell. Weights around the tensor at `(row, col)` are ``` @@ -131,7 +133,7 @@ Weights around the tensor at `(row, col)` are ``` # Arguments -- `t::T`: The tensor of type `T` (a subtype of `PEPSTensor`) to which the weight will be absorbed. The first axis of `t` should be the physical axis. +- `t::T`: The vertex tensor to which the weight will be absorbed. The first axis of `t` should be the physical axis. - `row::Int`: The row index specifying the position in the tensor network. - `col::Int`: The column index specifying the position in the tensor network. - `ax::Int`: The axis along which the weight is absorbed. From 6ecb00b9d1251b4cab8d8512854183d1592eaee2 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 11 Dec 2024 11:01:29 +0100 Subject: [PATCH 68/75] Fix pow rrule and make eltype more consistent --- src/environments/transferpeps_environments.jl | 3 +-- src/operators/infinitepepo.jl | 2 +- src/states/infinitepeps.jl | 2 +- src/utility/svd.jl | 2 +- src/utility/util.jl | 13 ++++++++----- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/environments/transferpeps_environments.jl b/src/environments/transferpeps_environments.jl index 4332c2325..316eaf778 100644 --- a/src/environments/transferpeps_environments.jl +++ b/src/environments/transferpeps_environments.jl @@ -120,8 +120,7 @@ function MPSKit.transfer_spectrum( @assert size(below) == size(O) numrows = size(above, 1) - envtype = eltype(init[1]) - eigenvals = Vector{Vector{scalartype(envtype)}}(undef, numrows) + eigenvals = Vector{Vector{scalartype(init[1])}}(undef, numrows) @threads for cr in 1:numrows L0, = init[cr] diff --git a/src/operators/infinitepepo.jl b/src/operators/infinitepepo.jl index e136aa70b..f274c0b2b 100644 --- a/src/operators/infinitepepo.jl +++ b/src/operators/infinitepepo.jl @@ -109,7 +109,7 @@ end Base.size(T::InfinitePEPO) = size(T.A) Base.size(T::InfinitePEPO, i) = size(T.A, i) Base.length(T::InfinitePEPO) = length(T.A) -Base.eltype(T::InfinitePEPO) = eltype(T.A) +Base.eltype(T::InfinitePEPO) = eltype(T.A[1]) VectorInterface.scalartype(T::InfinitePEPO) = scalartype(T.A) ## Copy diff --git a/src/states/infinitepeps.jl b/src/states/infinitepeps.jl index 51b353d49..40a6b1b66 100644 --- a/src/states/infinitepeps.jl +++ b/src/states/infinitepeps.jl @@ -113,7 +113,7 @@ end Base.size(T::InfinitePEPS) = size(T.A) Base.size(T::InfinitePEPS, i) = size(T.A, i) Base.length(T::InfinitePEPS) = length(T.A) -Base.eltype(T::InfinitePEPS) = eltype(T.A) +Base.eltype(T::InfinitePEPS) = eltype(T.A[1]) VectorInterface.scalartype(T::InfinitePEPS) = scalartype(T.A) ## Copy diff --git a/src/utility/svd.jl b/src/utility/svd.jl index a590c87a9..cfd9a1f9b 100644 --- a/src/utility/svd.jl +++ b/src/utility/svd.jl @@ -71,7 +71,7 @@ the iterative SVD didn't converge, the algorithm falls back to a dense SVD. end function random_start_vector(t::Matrix) - return randn(eltype(t), size(t, 1)) + return randn(scalartype(t), size(t, 1)) end # Compute SVD data block-wise using KrylovKit algorithm diff --git a/src/utility/util.jl b/src/utility/util.jl index 9ca568933..2ef829669 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -23,11 +23,11 @@ end _safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a .^ pow """ - sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4)) + sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(scalartype(S))^(3 / 4)) Compute `S^pow` for diagonal matrices `S`. """ -function sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4)) +function sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(scalartype(S))^(3 / 4)) tol *= norm(S, Inf) # Relative tol w.r.t. largest singular value (use norm(∘, Inf) to make differentiable) Spow = similar(S) for (k, b) in blocks(S) @@ -53,13 +53,16 @@ function absorb_s(u::AbstractTensorMap, s::AbstractTensorMap, vh::AbstractTensor end function ChainRulesCore.rrule( - ::typeof(sdiag_pow), S::AbstractTensorMap, pow::Real; tol::Real=eps(eltype(S))^(3 / 4) + ::typeof(sdiag_pow), + S::AbstractTensorMap, + pow::Real; + tol::Real=eps(scalartype(S))^(3 / 4), ) tol *= norm(S, Inf) spow = sdiag_pow(S, pow; tol) - spow2 = sdiag_pow(S, pow - 1; tol) + spow_minus1 = sdiag_pow(S, pow - 1; tol) function sdiag_pow_pullback(c̄) - return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow2)) + return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow_minus1')) end return spow, sdiag_pow_pullback end From 41596d1c69bd6465303354ea5f96af9ba692ea21 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 11 Dec 2024 18:59:44 +0800 Subject: [PATCH 69/75] Fix `similar` for `InfinitePEPO` after modifying `eltype` --- src/operators/infinitepepo.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operators/infinitepepo.jl b/src/operators/infinitepepo.jl index f274c0b2b..ad7c3b1d1 100644 --- a/src/operators/infinitepepo.jl +++ b/src/operators/infinitepepo.jl @@ -114,7 +114,7 @@ VectorInterface.scalartype(T::InfinitePEPO) = scalartype(T.A) ## Copy Base.copy(T::InfinitePEPO) = InfinitePEPO(copy(T.A)) -Base.similar(T::InfinitePEPO) = InfinitePEPO(similar(T.A)) +Base.similar(T::InfinitePEPO) = InfinitePEPO(similar.(T.A)) Base.repeat(T::InfinitePEPO, counts...) = InfinitePEPO(repeat(T.A, counts...)) Base.getindex(T::InfinitePEPO, args...) = Base.getindex(T.A, args...) From 17eac3dc517ee5acab34696a9ff802e91377a6c1 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Wed, 11 Dec 2024 19:09:44 +0800 Subject: [PATCH 70/75] Remove `eltype` of `InfiniteWeightPEPS` --- src/states/infiniteweightpeps.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 761fe378e..13e8b5a55 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -208,10 +208,6 @@ function Base.size(peps::InfiniteWeightPEPS) return size(peps.vertices) end -function Base.eltype(peps::InfiniteWeightPEPS) - return (eltype(peps.vertices[1]), eltype(peps.weights[1])) -end - """ mirror_antidiag(peps::InfiniteWeightPEPS) From 20a68f976e9cce5b70dcb639b40f470abeef33f5 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 11 Dec 2024 13:52:57 +0100 Subject: [PATCH 71/75] Scrap eltype for CTMRGEnv, add args to similar(::PEPO) --- src/environments/ctmrg_environments.jl | 1 - src/operators/infinitepepo.jl | 2 +- src/states/infiniteweightpeps.jl | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/environments/ctmrg_environments.jl b/src/environments/ctmrg_environments.jl index 248a49686..7bca4b903 100644 --- a/src/environments/ctmrg_environments.jl +++ b/src/environments/ctmrg_environments.jl @@ -400,7 +400,6 @@ function Base.rot180(env::CTMRGEnv{C,T}) where {C,T} return CTMRGEnv(copy(corners′), copy(edges′)) end -Base.eltype(env::CTMRGEnv) = eltype(env.corners[1]) Base.axes(x::CTMRGEnv, args...) = axes(x.corners, args...) function eachcoordinate(x::CTMRGEnv) return collect(Iterators.product(axes(x, 2), axes(x, 3))) diff --git a/src/operators/infinitepepo.jl b/src/operators/infinitepepo.jl index ad7c3b1d1..e7465e73e 100644 --- a/src/operators/infinitepepo.jl +++ b/src/operators/infinitepepo.jl @@ -114,7 +114,7 @@ VectorInterface.scalartype(T::InfinitePEPO) = scalartype(T.A) ## Copy Base.copy(T::InfinitePEPO) = InfinitePEPO(copy(T.A)) -Base.similar(T::InfinitePEPO) = InfinitePEPO(similar.(T.A)) +Base.similar(T::InfinitePEPO, args...) = InfinitePEPO(similar.(T.A, args...)) Base.repeat(T::InfinitePEPO, counts...) = InfinitePEPO(repeat(T.A, counts...)) Base.getindex(T::InfinitePEPO, args...) = Base.getindex(T.A, args...) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index 13e8b5a55..b3106a4dd 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -214,7 +214,6 @@ end Mirror the unit cell of an iPEPS with weights by its anti-diagonal line. """ function mirror_antidiag(peps::InfiniteWeightPEPS) - Nr, Nc = size(peps) vertices2 = mirror_antidiag(peps.vertices) for (i, t) in enumerate(vertices2) vertices2[i] = permute(t, ((1,), (3, 2, 5, 4))) From 76889d356ee57017f7dce73ede8c3482683eb002 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 11 Dec 2024 14:12:16 +0100 Subject: [PATCH 72/75] Fix conj in sdiag_pow rrule --- src/utility/util.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utility/util.jl b/src/utility/util.jl index 2ef829669..999f1a122 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -60,9 +60,9 @@ function ChainRulesCore.rrule( ) tol *= norm(S, Inf) spow = sdiag_pow(S, pow; tol) - spow_minus1 = sdiag_pow(S, pow - 1; tol) + spow_minus1_conj = sdiag_pow(S', pow - 1; tol) function sdiag_pow_pullback(c̄) - return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow_minus1')) + return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow_minus1_conj)) end return spow, sdiag_pow_pullback end From 187efb187a66fd9f66890cb81243487765eac920 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 11 Dec 2024 14:26:37 +0100 Subject: [PATCH 73/75] Cicrumvent Heisenberg tests errors by using GMRES to differentiate SVD --- test/heisenberg.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/heisenberg.jl b/test/heisenberg.jl index df54f0ad9..a8bbc4261 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -1,6 +1,6 @@ using Test using Random -using Printf +using Accessors using PEPSKit using TensorKit using KrylovKit @@ -88,15 +88,17 @@ end # measure physical quantities e_site = costfun(peps, envs, ham) / (N1 * N2) - @info @sprintf("Simple update energy = %.8f\n", e_site) + @info "Simple update energy = $e_site" # benchmark data from Phys. Rev. B 94, 035133 (2016) @test isapprox(e_site, -0.6594; atol=1e-3) # continue with auto differentiation - result = fixedpoint(peps, ham, opt_alg, envs) + svd_alg_gmres = SVDAdjoint(; rrule_alg=GMRES(; tol=1e-8)) + opt_alg_gmres = @set opt_alg.boundary_alg.projector_alg.svd_alg = svd_alg_gmres + result = fixedpoint(peps, ham, opt_alg_gmres, envs) # sensitivity warnings and degeneracies due to SU(2)? ξ_h, ξ_v, = correlation_length(result.peps, result.env) e_site2 = result.E / (N1 * N2) - @info @sprintf("Auto diff energy = %.8f\n", e_site) + @info "Auto diff energy = $e_site2" @test e_site2 ≈ E_ref atol = 1e-2 @test all(@. ξ_h > 0 && ξ_v > 0) end From 993577ddb7c9b6daf09a80ffd8e2a23d566e59ec Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 11 Dec 2024 10:47:02 -0500 Subject: [PATCH 74/75] small improvements --- src/operators/infinitepepo.jl | 7 ++++--- src/states/infinitepeps.jl | 7 ++++--- src/states/infiniteweightpeps.jl | 9 ++++----- src/utility/util.jl | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/operators/infinitepepo.jl b/src/operators/infinitepepo.jl index e7465e73e..6fad9fd3b 100644 --- a/src/operators/infinitepepo.jl +++ b/src/operators/infinitepepo.jl @@ -109,12 +109,13 @@ end Base.size(T::InfinitePEPO) = size(T.A) Base.size(T::InfinitePEPO, i) = size(T.A, i) Base.length(T::InfinitePEPO) = length(T.A) -Base.eltype(T::InfinitePEPO) = eltype(T.A[1]) -VectorInterface.scalartype(T::InfinitePEPO) = scalartype(T.A) +Base.eltype(T::InfinitePEPO) = eltype(typeof(T)) +Base.eltype(::Type{<:InfinitePEPO{T}}) where {T} = T +VectorInterface.scalartype(::Type{T}) where {T<:InfinitePEPO} = scalartype(eltype(T)) ## Copy Base.copy(T::InfinitePEPO) = InfinitePEPO(copy(T.A)) -Base.similar(T::InfinitePEPO, args...) = InfinitePEPO(similar.(T.A, args...)) +Base.similar(T::InfinitePEPO, args...) = InfinitePEPO(similar(T.A, args...)) Base.repeat(T::InfinitePEPO, counts...) = InfinitePEPO(repeat(T.A, counts...)) Base.getindex(T::InfinitePEPO, args...) = Base.getindex(T.A, args...) diff --git a/src/states/infinitepeps.jl b/src/states/infinitepeps.jl index 40a6b1b66..fea4ab1fd 100644 --- a/src/states/infinitepeps.jl +++ b/src/states/infinitepeps.jl @@ -113,12 +113,13 @@ end Base.size(T::InfinitePEPS) = size(T.A) Base.size(T::InfinitePEPS, i) = size(T.A, i) Base.length(T::InfinitePEPS) = length(T.A) -Base.eltype(T::InfinitePEPS) = eltype(T.A[1]) -VectorInterface.scalartype(T::InfinitePEPS) = scalartype(T.A) +Base.eltype(T::InfinitePEPS) = eltype(typeof(T)) +Base.eltype(::Type{<:InfinitePEPS{T}}) where {T} = T +VectorInterface.scalartype(::Type{T}) where {T<:InfinitePEPS} = scalartype(eltype(T)) ## Copy Base.copy(T::InfinitePEPS) = InfinitePEPS(copy(T.A)) -Base.similar(T::InfinitePEPS, args...) = InfinitePEPS(similar.(T.A, args...)) +Base.similar(T::InfinitePEPS, args...) = InfinitePEPS(similar(T.A, args...)) Base.repeat(T::InfinitePEPS, counts...) = InfinitePEPS(repeat(T.A, counts...)) Base.getindex(T::InfinitePEPS, args...) = Base.getindex(T.A, args...) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index b3106a4dd..91303bed1 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -36,7 +36,9 @@ end Base.size(W::SUWeight) = size(W.data) Base.size(W::SUWeight, i) = size(W.data, i) Base.length(W::SUWeight) = length(W.data) -Base.eltype(W::SUWeight) = eltype(W.data[1]) +Base.eltype(W::SUWeight) = eltype(typeof(W)) +Base.eltype(::Type{SUWeight{E}}) where {E} = E +VectorInterface.scalartype(::Type{T}) where {T<:SUWeight} = scalartype(eltype(T)) Base.getindex(W::SUWeight, args...) = Base.getindex(W.data, args...) Base.setindex!(W::SUWeight, args...) = (Base.setindex!(W.data, args...); W) @@ -44,10 +46,7 @@ Base.axes(W::SUWeight, args...) = axes(W.data, args...) function compare_weights(wts1::SUWeight, wts2::SUWeight) @assert size(wts1) == size(wts2) - wtdiff = sum( - _singular_value_distance((wt1, wt2)) for (wt1, wt2) in zip(wts1.data, wts2.data) - ) - return wtdiff / length(wts1) + return sum(_singular_value_distance, zip(wts1.data, wts2.data)) / length(wts1) end """ diff --git a/src/utility/util.jl b/src/utility/util.jl index 999f1a122..45182f1d1 100644 --- a/src/utility/util.jl +++ b/src/utility/util.jl @@ -20,7 +20,7 @@ function _elementwise_mult(a::AbstractTensorMap, b::AbstractTensorMap) return dst end -_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a .^ pow +_safe_pow(a, pow, tol) = (pow < 0 && abs(a) < tol) ? zero(a) : a^pow """ sdiag_pow(S::AbstractTensorMap, pow::Real; tol::Real=eps(scalartype(S))^(3 / 4)) @@ -60,9 +60,9 @@ function ChainRulesCore.rrule( ) tol *= norm(S, Inf) spow = sdiag_pow(S, pow; tol) - spow_minus1_conj = sdiag_pow(S', pow - 1; tol) + spow_minus1_conj = scale!(sdiag_pow(S', pow - 1; tol), pow) function sdiag_pow_pullback(c̄) - return (ChainRulesCore.NoTangent(), pow * _elementwise_mult(c̄, spow_minus1_conj)) + return (ChainRulesCore.NoTangent(), _elementwise_mult(c̄, spow_minus1_conj)) end return spow, sdiag_pow_pullback end From 8fc39e018d2e4637d7fc5b330602ef15d815c2fa Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 11 Dec 2024 11:04:09 -0500 Subject: [PATCH 75/75] update MPSKitModels compat --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index cffc9c1aa..ba49bc982 100644 --- a/Project.toml +++ b/Project.toml @@ -32,7 +32,7 @@ KrylovKit = "0.8" LinearAlgebra = "1" LoggingExtras = "1" MPSKit = "0.11" -MPSKitModels = "0.3" +MPSKitModels = "0.3.5" OhMyThreads = "0.7" OptimKit = "0.3" Printf = "1"