From 0960301247377852cafe03b6262489dabf85055b Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 23 Jan 2026 17:20:07 +0100 Subject: [PATCH 01/13] add Triangular CTMRG This code is based on its (asymetric) implementation in TNRKit.jl, which was based on the paper https://arxiv.org/pdf/2510.04907 --- src/PEPSKit.jl | 11 +- src/algorithms/select_algorithm.jl | 7 + src/algorithms/triangular_ctmrg/ctmrg.jl | 50 +++ .../triangular_ctmrg/simultaneous.jl | 319 ++++++++++++++++++ src/algorithms/triangular_ctmrg/utility.jl | 56 +++ src/environments/ctmrg_tria_environments.jl | 77 +++++ src/networks/infinitetriangularnetwork.jl | 174 ++++++++++ src/networks/local_triangular_sandwich.jl | 43 +++ src/networks/tensors_tria.jl | 101 ++++++ test/ctmrg/triangular.jl | 95 ++++++ 10 files changed, 932 insertions(+), 1 deletion(-) create mode 100644 src/algorithms/triangular_ctmrg/ctmrg.jl create mode 100644 src/algorithms/triangular_ctmrg/simultaneous.jl create mode 100644 src/algorithms/triangular_ctmrg/utility.jl create mode 100644 src/environments/ctmrg_tria_environments.jl create mode 100644 src/networks/infinitetriangularnetwork.jl create mode 100644 src/networks/local_triangular_sandwich.jl create mode 100644 src/networks/tensors_tria.jl create mode 100644 test/ctmrg/triangular.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 2aeb865b6..e5a23f596 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -72,6 +72,9 @@ include("utility/initialization.jl") include("networks/tensors.jl") include("networks/local_sandwich.jl") include("networks/infinitesquarenetwork.jl") +include("networks/tensors_tria.jl") +include("networks/local_triangular_sandwich.jl") +include("networks/infinitetriangularnetwork.jl") include("states/infinitepeps.jl") include("states/infinitepartitionfunction.jl") @@ -89,6 +92,7 @@ include("operators/lattices/squarelattice.jl") include("operators/models.jl") include("environments/ctmrg_environments.jl") +include("environments/ctmrg_tria_environments.jl") include("environments/vumps_environments.jl") include("environments/suweight.jl") include("environments/bp_environments.jl") @@ -139,6 +143,10 @@ include("algorithms/ctmrg/gaugefix.jl") include("algorithms/ctmrg/c4v.jl") include("algorithms/ctmrg/initialization.jl") +include("algorithms/triangular_ctmrg/ctmrg.jl") +include("algorithms/triangular_ctmrg/utility.jl") +include("algorithms/triangular_ctmrg/simultaneous.jl") + include("algorithms/truncation/truncationschemes.jl") include("algorithms/truncation/fullenv_truncation.jl") include("algorithms/truncation/bond_tensor.jl") @@ -177,6 +185,7 @@ using .Defaults: set_scheduler! export set_scheduler! export EighAdjoint, IterEigh, SVDAdjoint, IterSVD, OrthAdjoint, QRAdjoint export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG +export CTMRGTriaEnv, SimultaneousCTMRGTria export initialize_ctmrg_environment, RandomInitialization, ProductStateInitialization, ApplicationInitialization, IdentityInitialization export corner, edge, setcorner!, setedge! @@ -200,7 +209,7 @@ export ALSTruncation, FullEnvTruncation export SimpleUpdate export TimeEvolver, timestep, time_evolve -export InfiniteSquareNetwork +export InfiniteSquareNetwork, InfiniteTriangularNetwork export InfinitePartitionFunction export InfinitePEPS, InfiniteTransferPEPS export SUWeight diff --git a/src/algorithms/select_algorithm.jl b/src/algorithms/select_algorithm.jl index c121fb3fd..5eb29c4b2 100644 --- a/src/algorithms/select_algorithm.jl +++ b/src/algorithms/select_algorithm.jl @@ -158,3 +158,10 @@ function select_algorithm( return CTMRGAlgorithm(; alg, tol, verbosity, decomposition_alg, kwargs...) end + +function select_algorithm( + ::typeof(leading_boundary), + env₀::CTMRGTriaEnv + ) + return SimultaneousCTMRGTria() +end diff --git a/src/algorithms/triangular_ctmrg/ctmrg.jl b/src/algorithms/triangular_ctmrg/ctmrg.jl new file mode 100644 index 000000000..9e06e2ad4 --- /dev/null +++ b/src/algorithms/triangular_ctmrg/ctmrg.jl @@ -0,0 +1,50 @@ +abstract type CTMRGTriaAlgorithm end + +function leading_boundary(env₀::CTMRGTriaEnv, network::InfiniteTriangularNetwork; kwargs...) + alg = select_algorithm(leading_boundary, env₀; kwargs...) + return leading_boundary(env₀, network, alg) +end +function leading_boundary( + env₀::CTMRGTriaEnv, network::InfiniteTriangularNetwork, alg::CTMRGTriaAlgorithm + ) + log = ignore_derivatives(() -> MPSKit.IterLog("CTMRG")) + return LoggingExtras.withlevel(; alg.verbosity) do + env = deepcopy(env₀) + S_old = DiagonalTensorMap.(id.(domain.(env₀.C))) + η = one(real(scalartype(network))) + ctmrg_loginit!(log, η, network, env₀) + local info + for iter in 1:(alg.maxiter) + env, S = ctmrg_iteration(network, env, alg) # Grow and renormalize in all 4 directions + η = calc_convergence(S, S_old) + S_old = copy(S) + + if η ≤ alg.tol && iter ≥ alg.miniter + ctmrg_logfinish!(log, iter, η, network, env) + break + end + if iter == alg.maxiter + ctmrg_logcancel!(log, iter, η, network, env) + else + ctmrg_logiter!(log, iter, η, network, env) + end + info = (; convergence_metric = η) + end + return env, info + end +end +function leading_boundary(env₀::CTMRGTriaEnv, state, args...; kwargs...) + return leading_boundary(env₀, InfiniteTriangularNetwork(state), args...; kwargs...) +end + +function calc_convergence(S_new::Array{T, 3}, S_old::Array{T, 3}) where {E, S, T <: DiagonalTensorMap{E, S}} + ε = Inf + function dist(S1, S2) + if space(S1) == space(S2) + return norm(S1^4 - S2^4) + else + return Inf + end + end + return maximum(dist.(S_new, S_old)) +end diff --git a/src/algorithms/triangular_ctmrg/simultaneous.jl b/src/algorithms/triangular_ctmrg/simultaneous.jl new file mode 100644 index 000000000..4c3610bcd --- /dev/null +++ b/src/algorithms/triangular_ctmrg/simultaneous.jl @@ -0,0 +1,319 @@ +struct SimultaneousCTMRGTria <: CTMRGTriaAlgorithm + tol::Float64 + maxiter::Int + miniter::Int + verbosity::Int + conditioning::Bool + projector_alg::Symbol + trunctype::Symbol +end +function SimultaneousCTMRGTria(; + tol = Defaults.ctmrg_tol, + maxiter = Defaults.ctmrg_maxiter, + miniter = Defaults.ctmrg_miniter, + verbosity = Defaults.ctmrg_verbosity, + conditioning = true, + projector_alg = :twothirds, + trunctype = :truncrank + ) + return SimultaneousCTMRGTria(tol, maxiter, miniter, verbosity, conditioning, projector_alg, trunctype) +end + +# Based on +# https://arxiv.org/pdf/2510.04907 + +function ctmrg_iteration(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv, alg::SimultaneousCTMRGTria) + trunc_type = getfield(@__MODULE__, alg.trunctype) + trunc = trunc_type(dim(domain(env.C[1, 1, 1])[1])) + Pas, Pbs, S = calculate_projectors(network, env, trunc, alg.projector_alg) + + renormalize_corners!(network, env, Pas, Pbs) + normalize_corners!(env) + + Ẽas, Ẽbs, Ẽastr, Ẽbstr = semi_renormalize(network, env, Pas, Pbs, trunc) + Qas, Qbs = build_matrix_second_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; alg.conditioning) + + env = renormalize_edges(env, Ẽas, Ẽbs, Qas, Qbs) + normalize_edges!(env) + return env, S +end + +function calculate_projectors(network, env, trunc, projector_alg) + if projector_alg == :full + return calculate_full_projectors(network, env, trunc) + elseif projector_alg == :twothirds + return calculate_twothirds_projectors(network, env, trunc) + else + @error "projector_alg = $projector_alg not defined" + end +end + +function calculate_twothirds_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 1, 1}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) + ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) + ρρ = ρL * ρR + ρρ /= norm(ρρ) + + U, S, V = svd_trunc(ρρ; trunc = trunc & trunctol(; atol = 1.0e-20)) + + Pb = ρR * V' * sdiag_pow(S, -1 / 2) + Pa = sdiag_pow(S, -1 / 2) * U' * ρL + return Pa, Pb, S + end + return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) +end + +function calculate_full_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 1, 1}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) + ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) + ρ̄ = build_double_corner_matrix_triangular(network, env, mod1(dir + 3, 6), r, c) + ρ̄ /= norm(ρ̄) + Ū, S̄, V̄ᴴ = svd_trunc(ρ̄; trunc = trunctol(; atol = 1.0e-20)) + ρ̄ᴿ = Ū * sqrt(S̄) + ρ̄ᴸ = sqrt(S̄) * V̄ᴴ + ρρ = ρ̄ᴸ * ρL * ρR * ρ̄ᴿ + ρρ /= norm(ρρ) + + U, S, Vᴴ = svd_trunc(ρρ; trunc = trunctol(; atol = 1.0e-20)) + U, S, Vᴴ = svd_trunc(ρρ; trunc) + U, S, Vᴴ = svd_trunc(ρρ; trunc = trunc & trunctol(; atol = 1.0e-20)) + + Pb = ρR * ρ̄ᴿ * Vᴴ' * sdiag_pow(S, -1 / 2) + Pa = sdiag_pow(S, -1 / 2) * U' * ρ̄ᴸ * ρL + + return Pa, Pb, S + end + return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) +end + +function renormalize_corners!(network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PEPSTriaSandwich} + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1} + new_corners′ = similar(coordinates, T_proj) + new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) + @tensor opt = true env_C_new[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2 11; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7 12; 8] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * + Pas[mod1(dir - 1, 6), r, c][-1; 4 5 13] * Pbs[dir, r, c][8 9 14; -4] + return env_C_new + end + return new_corners +end + +function renormalize_corners!(network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PFTriaTensor} + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = AbstractTensorMap{E, S, 2, 1} + new_corners′ = similar(coordinates, T_proj) + new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) + @tensor opt = true env_C_new[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7; 8] * + rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] + return env_C_new + end + return new_corners +end + +function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, dir::Int, r::Int, c::Int) where {P <: PFTriaTensor} + @tensor opt = true mat[-1 -2; -3 -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3; 2] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4; -3] * rotl60(network[r, c], mod(dir - 1, 6))[7 -2 -4; 5 3 4] + return mat +end + +function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, dir::Int, r::Int, c::Int) where {P <: PEPSTriaSandwich} + @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5 8; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3 9; 2] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7 10; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4 11; -4] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 5 3 4 -5 -2 7] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 8 9 11 -6 -3 10]) + return mat +end + +function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Pas, Pbs, dir, r, c) where {P <: PEPSTriaSandwich} + @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := Pas[dir, r, c][-1; 1 2 8] * Pbs[dir, r, c][6 7 9; -4] * + env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5 11; 6] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 3 5 7 -5 -2 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 10 11 9 -6 -3 8]) + return mat / norm(mat) +end + +function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Pas, Pbs, dir, r, c) where {P <: PFTriaTensor} + @tensor opt = true mat[-1 -2; -3 -4] := Pas[dir, r, c][-1; 1 2] * Pbs[dir, r, c][6 7; -3] * + env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5; 6] * rotl60(network[r, c], mod(dir - 1, 6))[2 -2 -4; 3 5 7] + return mat / norm(mat) +end + +function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 2}} + return permute(t, ((1, 3), (2,))) +end + +function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 3}} + return permute(t, ((1, 3, 4), (2,))) +end + +function semi_renormalize(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv, Pas, Pbs, trunc) + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + mat = semi_renormalize_edge(network, env, Pas, Pbs, dir, r, c) + + U, S, V = svd_trunc(mat; trunc = trunctol(; atol = 1.0e-20)) + + Ẽb = U * sqrt(S) + Ẽa = _permute_edge(sqrt(S) * V) + + Utr, Str, Vtr = svd_trunc(mat; trunc = trunc & trunctol(; atol = 1.0e-20)) + Ẽbtr = Utr * sqrt(Str) + Ẽatr = _permute_edge(sqrt(Str) * Vtr) + + return Ẽa, Ẽb, Ẽatr, Ẽbtr + end + return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3), getindex.(projectors, 4) +end + +function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PEPSTriaSandwich} + @tensor opt = true σL[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2 10; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3 11; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5 12; 4] * + Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9 13; -4] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7 14; 6] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[15 2 9 -2 7 5 3] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 13 -3 14 12 11]) + @tensor opt = true σR[-1; -2 -3 -4] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2 10; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3 11; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5 12; 6] * + Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7 13; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9 14; 8] * + rotl60(ket(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 9 2 3 5 7 -3] * conj(rotl60(bra(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 14 10 11 12 13 -4]) + return σL, σR +end + +function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PFTriaTensor} + @tensor opt = true σL[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5; 4] * + Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9; -3] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7; 6] * + rotl60(network[r, c], mod(dir - 1, 6))[3 5 7; 2 9 -2] + @tensor opt = true σR[-1; -2 -3] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5; 6] * + Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9; 8] * + rotl60(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...], mod(dir - 1, 6))[-3 7 5; 9 2 3] + return σL, σR +end + +function build_matrix_second_projectors(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; conditioning = true) + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 1, 1}, AbstractTensorMap{E, S, 1, 1}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + σL, σR = build_halfinfinite_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) + if conditioning + σL /= norm(σL) + σR /= norm(σR) + UL, SL, VLᴴ = svd_trunc(σL; trunc = trunctol(; atol = 1.0e-20)) + UR, SR, VRᴴ = svd_trunc(σR; trunc = trunctol(; atol = 1.0e-20)) + + FLU = sqrt(SL) * VLᴴ + FRU = UR * sqrt(SR) + + mat = FLU * FRU + mat /= norm(mat) + WU, SU, QUᴴ = svd_trunc(mat; trunc = trunc & trunctol(; atol = 1.0e-20)) + + Qa = sdiag_pow(SU, -1 / 2) * WU' * FLU + Qb = FRU * QUᴴ' * sdiag_pow(SU, -1 / 2) + else + mat = σL * σR + mat /= norm(mat) + U, S, V = svd_trunc(mat; trunc = trunc & trunctol(; atol = 1.0e-20)) + Qa = sdiag_pow(S, -1 / 2) * U' * σL + Qb = σR * V' * sdiag_pow(S, -1 / 2) + end + return Qa, Qb + end + return getindex.(projectors, 1), getindex.(projectors, 2) +end + +function renormalize_edges(env::CTMRGTriaEnv, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} + coordinates = collect(Iterators.product(axes(env.Ea)...)) + T_proj = Tuple{AbstractTensorMap{E, S, 3, 1}, AbstractTensorMap{E, S, 3, 1}} + new_edges′ = similar(env.Ea, T_proj) + new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) + @tensor Eb_new[-1 -2 -3; -4] := Ẽbs[dir, r, c][-1 -2 -3; 1] * Qbs[dir, r, c][1; -4] + @tensor Ea_new[-1 -2 -3; -4] := Qas[dir, r, c][-1; 1] * Ẽas[dir, r, c][1 -2 -3; -4] + return Ea_new, Eb_new + end + return CTMRGTriaEnv(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) +end + +function renormalize_edges(env::CTMRGTriaEnv, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} + coordinates = collect(Iterators.product(axes(env.Ea)...)) + T_proj = Tuple{AbstractTensorMap{E, S, 2, 1}, AbstractTensorMap{E, S, 2, 1}} + new_edges′ = similar(env.Ea, T_proj) + new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) + @tensor Eb_new[-1 -2; -3] := Ẽbs[dir, r, c][-1 -2; 1] * Qbs[dir, r, c][1; -3] + @tensor Ea_new[-1 -2; -3] := Qas[dir, r, c][-1; 1] * Ẽas[dir, r, c][1 -2; -3] + return Ea_new, Eb_new + end + return CTMRGTriaEnv(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) +end + +function normalize_corners!(env) + coordinates = collect(Iterators.product(axes(env.Ea)...)) + new_env′ = similar(env.C, typeof(env.C[1, 1, 1])) + new_env = dtmap!!(new_env′, coordinates) do (dir, r, c) + env_C_new = env.C[dir, r, c] / norm(env.C[dir, r, c]) + return env_C_new + end + return new_env +end + +function normalize_edges!(env) + for dir in 1:6 + env.Ea[dir] /= norm(env.Ea[dir]) + env.Eb[dir] /= norm(env.Eb[dir]) + end + return env +end + +function calculate_error(Ss, Ss_prev) + ε = Inf + for (S, S_prev) in zip(Ss, Ss_prev) + if space(S) == space(S_prev) + ε = norm(S^4 - S_prev^4) + else + return Inf + end + end + return ε +end + +# Expectation_values + +function network_value(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv) + return zero(scalartype(network)) +end + +function energy(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, H) where {P <: PEPSTriaSandwich} + (r, c) = (1, 1) + numerator = @tensor opt = true ket(network[r, c])[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, c])[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(network[r, c])[dLb DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, c])[dRb DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + env.C[1][χNW DLt120 DLb120; χNa] * env.C[2][χNb DRt60 DRb60; χNE] * env.C[3][χNE DRt0 DRb0; χSE] * + env.C[4][χSE DRt300 DRb300; χSa] * env.C[5][χSb DLt240 DLb240; χSW] * env.C[6][χSW DLt180 DLb180; χNW] * + env.Eb[1][χNa DLt60 DLb60; χNC] * env.Ea[1][χNC DRt120 DRb120; χNb] * + env.Eb[4][χSa DRt240 DRb240; χSC] * env.Ea[4][χSC DLt300 DLb300; χSb] * + H[dLb dRb; dLt dRt] + + denumerator = @tensor opt = true ket(network[r, c])[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, c])[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(network[r, c])[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, c])[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + env.C[1][χNW DLt120 DLb120; χNa] * env.C[2][χNb DRt60 DRb60; χNE] * env.C[3][χNE DRt0 DRb0; χSE] * + env.C[4][χSE DRt300 DRb300; χSa] * env.C[5][χSb DLt240 DLb240; χSW] * env.C[6][χSW DLt180 DLb180; χNW] * + env.Eb[1][χNa DLt60 DLb60; χNC] * env.Ea[1][χNC DRt120 DRb120; χNb] * + env.Eb[4][χSa DRt240 DRb240; χSC] * env.Ea[4][χSC DLt300 DLb300; χSb] + return numerator / denumerator +end diff --git a/src/algorithms/triangular_ctmrg/utility.jl b/src/algorithms/triangular_ctmrg/utility.jl new file mode 100644 index 000000000..0230e0944 --- /dev/null +++ b/src/algorithms/triangular_ctmrg/utility.jl @@ -0,0 +1,56 @@ +function _coordinates(dir::Int, rot::Int, r::Int, c::Int, unitcell::Tuple{Int, Int}) + rows, cols = unitcell + if mod1(dir + rot, 6) == 1 + return (mod1(dir, 6), mod1(r - 1, rows), mod1(c, cols)) + elseif mod1(dir + rot, 6) == 2 + return (mod1(dir, 6), mod1(r - 1, rows), mod1(c + 1, cols)) + elseif mod1(dir + rot, 6) == 3 + return (mod1(dir, 6), mod1(r, rows), mod1(c + 1, cols)) + elseif mod1(dir + rot, 6) == 4 + return (mod1(dir, 6), mod1(r + 1, rows), mod1(c, cols)) + elseif mod1(dir + rot, 6) == 5 + return (mod1(dir, 6), mod1(r + 1, rows), mod1(c - 1, cols)) + elseif mod1(dir + rot, 6) == 6 + return (mod1(dir, 6), mod1(r, rows), mod1(c - 1, cols)) + end +end + +function _truncway(trunctype::Symbol, D::E) where {E <: ElementarySpace} + if trunctype == :truncdim + return truncdim(dim(D)) + else + return notrunc() + end +end + +function rotr60(A::T, dir::Int) where {E, S, T <: AbstractTensorMap{E, S, 1, 6}} + if dir == 0 + return A + else + return rotr60(permute(A, ((1,), (7, 2, 3, 4, 5, 6))), dir - 1) + end +end + +function rotr60(A::T, dir::Int) where {E, S, T <: AbstractTensorMap{E, S, 3, 3}} + if dir == 0 + return A + else + return rotr60(permute(A, ((2, 3, 6), (1, 4, 5))), dir - 1) + end +end + +function rotl60(A::T, dir::Int) where {E, S, T <: AbstractTensorMap{E, S, 1, 6}} + if dir == 0 + return A + else + return rotl60(permute(A, ((1,), (3, 4, 5, 6, 7, 2))), dir - 1) + end +end + +function rotl60(A::T, dir::Int) where {E, S, T <: AbstractTensorMap{E, S, 3, 3}} + if dir == 0 + return A + else + return rotl60(permute(A, ((4, 1, 2), (5, 6, 3))), dir - 1) + end +end diff --git a/src/environments/ctmrg_tria_environments.jl b/src/environments/ctmrg_tria_environments.jl new file mode 100644 index 000000000..6d961100a --- /dev/null +++ b/src/environments/ctmrg_tria_environments.jl @@ -0,0 +1,77 @@ +#TODO: Add docs and figure +struct CTMRGTriaEnv{T} + "6 x rows x cols array of corner C tensors, where the first dimension specifies the spatial direction" + C::Array{T, 3} + "6 x rows x cols array of edge Ta tensors, where the first dimension specifies the spatial direction" + Ea::Array{T, 3} + "6 x rows x cols array of edge Ta tensors, where the first dimension specifies the spatial direction" + Eb::Array{T, 3} +end +# function CTMRGTriaEnv(corners::Array{C, 3}, edges::Array{T, 3}) where {C, T} +# foreach(check_environment_virtualspace, edges) +# return CTMRGTriaEnv{C, T}(corners, edges) +# end + +# """ +# CTMRGEnv( +# [f=randn, T=ComplexF64], Ds_north::A, Ds_east::A, chis_north::B, [chis_east::B], [chis_south::B], [chis_west::B] +# ) where {A<:AbstractMatrix{<:VectorSpace}, B<:AbstractMatrix{<:ElementarySpace}} + +# Construct a CTMRG environment by specifying matrices of north and east virtual spaces of the +# corresponding partition function and the north, east, south and west virtual spaces of the +# environment. Each respective matrix entry corresponds to a site in the unit cell. By +# default, the virtual environment spaces for all directions are taken to be the same. + +# The environment virtual spaces for each site correspond to the north or east virtual space +# of the corresponding edge tensor for each direction. Specifically, for a given site +# `(r, c)`, `chis_north[r, c]` corresponds to the east space of the north edge tensor, +# `chis_east[r, c]` corresponds to the north space of the east edge tensor, +# `chis_south[r, c]` corresponds to the east space of the south edge tensor, and +# `chis_west[r, c]` corresponds to the north space of the west edge tensor. + +# Each entry of the `Ds_north` and `Ds_east` matrices corresponds to an effective local space +# of the partition function, and can be represented as an `ElementarySpace` (e.g. for the case +# of a partition function defined in terms of local rank-4 tensors) or a `ProductSpace` (e.g. +# for the case of a network representing overlaps of PEPSs and PEPOs). +# """ +function get_Ds(D::A) where {A <: ProductSpace} + return [dir > 3 ? reverse(D') : D for dir in 1:6] +end + +function get_Ds(D::A) where {A <: ElementarySpace} + return [dir > 3 ? D' : D for dir in 1:6] +end + +function CTMRGTriaEnv( + f, T, D::Union{A, B}, chis::B; unitcell::Tuple{Int, Int} = (1, 1) + ) where { + A <: ProductSpace, B <: ElementarySpace, + } + if typeof(D) <: ElementarySpace + N = 1 + else + N = length(D) + end + st = spacetype(D) + T_type = tensormaptype(st, N + 1, 1, T) + + Cs = Array{T_type}(undef, 6, unitcell...) + Eas = Array{T_type}(undef, 6, unitcell...) + Ebs = Array{T_type}(undef, 6, unitcell...) + + Ds = get_Ds(D) + for dir in 1:6, r in 1:unitcell[1], c in 1:unitcell[2] + C = _edge_tensor(f, T, chis, Ds[dir], chis) + Ea = _edge_tensor(f, T, chis, Ds[dir], chis) + Eb = _edge_tensor(f, T, chis, Ds[mod1(dir + 1, 6)], chis) + + C /= norm(C) + Ea /= norm(Ea) + Eb /= norm(Eb) + + Cs[dir, r, c] = C + Eas[dir, r, c] = Ea + Ebs[dir, r, c] = Eb + end + return CTMRGTriaEnv(Cs, Eas, Ebs) +end diff --git a/src/networks/infinitetriangularnetwork.jl b/src/networks/infinitetriangularnetwork.jl new file mode 100644 index 000000000..2ffa61ab0 --- /dev/null +++ b/src/networks/infinitetriangularnetwork.jl @@ -0,0 +1,174 @@ +const PEPSTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} + +""" +$(TYPEDEF) + +Contractible square network. Wraps a matrix of 'rank-6-tensor-like' objects. + +## Fields + +$(TYPEDFIELDS) +""" +struct InfiniteTriangularNetwork{O} + A::Matrix{O} + InfiniteTriangularNetwork{O}(A::Matrix{O}) where {O} = new{O}(A) + function InfiniteTriangularNetwork(A::Matrix) + # for I in eachindex(IndexCartesian(), A) + # d, w = Tuple(I) + # northwest_virtualspace(A[d, w]) == + # _elementwise_dual(southeast_virtualspace(A[_prev(d, end), w])) || throw( + # SpaceMismatch("North virtual space at site $((d, w)) does not match.") + # ) + # northeast_virtualspace(A[d, w]) == + # _elementwise_dual(southwest_virtualspace(A[d, _next(w, end)])) || + # throw(SpaceMismatch("East virtual space at site $((d, w)) does not match.")) + # east_virtualspace(A[d, w]) == + # _elementwise_dual(west_virtualspace(A[d, _next(w, end)])) || + # throw(SpaceMismatch("East virtual space at site $((d, w)) does not match.")) + # end + return InfiniteTriangularNetwork{eltype(A)}(A) + end +end +InfiniteTriangularNetwork(n::InfiniteTriangularNetwork) = n + +## Unit cell interface + +unitcell(n::InfiniteTriangularNetwork) = n.A +Base.size(n::InfiniteTriangularNetwork, args...) = size(unitcell(n), args...) +Base.length(n::InfiniteTriangularNetwork) = length(unitcell(n)) +Base.eltype(n::InfiniteTriangularNetwork) = eltype(typeof(n)) +Base.eltype(::Type{InfiniteTriangularNetwork{O}}) where {O} = O + +Base.copy(n::InfiniteTriangularNetwork) = InfiniteTriangularNetwork(copy(unitcell(n))) +function Base.similar(n::InfiniteTriangularNetwork, T::Type{TorA} = scalartype(n)) where {TorA} + return InfiniteTriangularNetwork(map(t -> similar(t, T), unitcell(n))) +end +function Base.repeat(n::InfiniteTriangularNetwork, counts...) + return InfiniteTriangularNetwork(repeat(unitcell(n), counts...)) +end + +## Indexing +Base.getindex(n::InfiniteTriangularNetwork, args...) = Base.getindex(unitcell(n), args...) +function Base.setindex!(n::InfiniteTriangularNetwork, args...) + return (Base.setindex!(unitcell(n), args...); n) +end +Base.axes(n::InfiniteTriangularNetwork, args...) = axes(unitcell(n), args...) +eachcoordinate(n::InfiniteTriangularNetwork) = collect(Iterators.product(axes(n)...)) +function eachcoordinate(n::InfiniteTriangularNetwork, dirs) + return collect(Iterators.product(dirs, axes(n, 1), axes(n, 2))) +end + +## Spaces + +TensorKit.spacetype(::Type{T}) where {T <: InfiniteTriangularNetwork} = spacetype(eltype(T)) +function virtualspace(n::InfiniteTriangularNetwork, r::Int, c::Int, dir) + Nr, Nc = size(n) + return virtualspace(n[mod1(r, Nr), mod1(c, Nc)], dir) +end + +## Vector interface + +function VectorInterface.scalartype(::Type{T}) where {T <: InfiniteTriangularNetwork} + return scalartype(eltype(T)) +end +function VectorInterface.zerovector(A::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(zerovector(unitcell(A))) +end + +## Math (for Zygote accumulation) + +function Base.:+(A₁::InfiniteTriangularNetwork, A₂::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(_add_localsandwich.(unitcell(A₁), unitcell(A₂))) +end +function Base.:-(A₁::InfiniteTriangularNetwork, A₂::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(_subtract_localsandwich.(unitcell(A₁), unitcell(A₂))) +end +function Base.:*(α::Number, A::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(_mul_localsandwich.(α, unitcell(A))) +end +Base.:*(A::InfiniteTriangularNetwork, α::Number) = α * A +function Base.:/(A::InfiniteTriangularNetwork, α::Number) + return A * inv(α) +end +function LinearAlgebra.dot(A₁::InfiniteTriangularNetwork, A₂::InfiniteTriangularNetwork) + return dot(unitcell(A₁), unitcell(A₂)) +end +LinearAlgebra.norm(A::InfiniteTriangularNetwork) = norm(unitcell(A)) + +## (Approximate) equality + +function Base.:(==)(A₁::InfiniteTriangularNetwork, A₂::InfiniteTriangularNetwork) + return all(zip(unitcell(A₁), unitcell(A₂))) do (p₁, p₂) + return p₁ == p₂ + end +end +function Base.isapprox(A₁::InfiniteTriangularNetwork, A₂::InfiniteTriangularNetwork; kwargs...) + return all(zip(unitcell(A₁), unitcell(A₂))) do (p₁, p₂) + return _isapprox_localsandwich(p₁, p₂; kwargs...) + end +end + +## Rotations + +function rotl60(n::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(rotl60(_rotl60_localsandwich.(unitcell(n)))) +end +function rotr60(n::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(rotr60(_rotr60_localsandwich.(unitcell(n)))) +end +function rot180(n::InfiniteTriangularNetwork) + return InfiniteTriangularNetwork(rot180(_rot180_localsandwich.(unitcell(n)))) +end + +## Chainrules + +# generic implementation +function ChainRulesCore.rrule( + ::typeof(Base.getindex), network::InfiniteTriangularNetwork, r::Int, c::Int + ) + O = network[r, c] + + function getindex_pullback(ΔO_) + ΔO = map(unthunk, ΔO_) + if ΔO isa Tangent + ΔO = ChainRulesCore.construct(typeof(O), ChainRulesCore.backing(ΔO)) + end + Δnetwork = zerovector(network) + Δnetwork[r, c] = ΔO + return NoTangent(), Δnetwork, NoTangent(), NoTangent() + end + return O, getindex_pullback +end + +# specialized PFTensor implementation +function ChainRulesCore.rrule( + ::typeof(Base.getindex), network::InfiniteTriangularNetwork{<:PFTensor}, r::Int, c::Int + ) + O = network[r, c] + + function getindex_pullback(ΔO_) + ΔO = unthunk(ΔO_) + Δnetwork = zerovector(network) + Δnetwork[r, c] = ΔO + return NoTangent(), Δnetwork, NoTangent(), NoTangent() + end + return O, getindex_pullback +end + +# function ChainRulesCore.rrule(::typeof(rotl90), network::InfiniteTriangularNetwork) +# network´ = rotl90(network) +# function rotl90_pullback(Δnetwork_) +# Δnetwork = unthunk(Δnetwork_) +# return NoTangent(), rotr90(Δnetwork) +# end +# return network´, rotl90_pullback +# end + +# function ChainRulesCore.rrule(::typeof(rotr90), network::InfiniteTriangularNetwork) +# network´ = rotr90(network) +# function rotr90_pullback(Δnetwork) +# Δnetwork = unthunk(Δnetwork) +# return NoTangent(), rotl90(Δnetwork) +# end +# return network´, rotr90_pullback +# end diff --git a/src/networks/local_triangular_sandwich.jl b/src/networks/local_triangular_sandwich.jl new file mode 100644 index 000000000..380fa18e9 --- /dev/null +++ b/src/networks/local_triangular_sandwich.jl @@ -0,0 +1,43 @@ +# +# Interface for local effective-rank-6 tensor sandwiches +# + +# route all virtualspace getters through a single method for convenience +northwest_virtualspace(O::AbstractTensorMap{E, S, 1, 6}, args...) where {E, S} = domain(O)[1] +northeast_virtualspace(O::AbstractTensorMap{E, S, 1, 6}, args...) where {E, S} = domain(O)[2] +east_virtualspace(O::AbstractTensorMap{E, S, 1, 6}, args...) where {E, S} = domain(O)[3] +southeast_virtualspace(O::AbstractTensorMap{E, S, 1, 6}, args...) where {E, S} = domain(O)[4] +southwest_virtualspace(O::AbstractTensorMap{E, S, 1, 6}, args...) where {E, S} = domain(O)[5] +west_virtualspace(O::AbstractTensorMap{E, S, 1, 6}, args...) where {E, S} = domain(O)[6] + +## Rotations + +# generic local interface +_rotl60_localsandwich(O) = rotl60.(O) +_rotr60_localsandwich(O) = rotr60.(O) + +## PartitionFunction + +# specialized local rotation interface +_rotl60_localsandwich(O::PFTriaTensor) = rotl60(O) +_rotr60_localsandwich(O::PFTriaTensor) = rotr60(O) +_rot180_localsandwich(O::PFTriaTensor) = rot180(O) + +# specialized local math interface +_add_localsandwich(O1::PFTriaTensor, O2::PFTriaTensor) = O1 + O2 +_subtract_localsandwich(O1::PFTriaTensor, O2::PFTriaTensor) = O1 - O2 +_mul_localsandwich(α::Number, O::PFTriaTensor) = α * O +_isapprox_localsandwich(O1::PFTriaTensor, O2::PFTriaTensor; kwargs...) = isapprox(O1, O2; kwargs...) + +## PEPS + +const PEPSTriaSandwich{T <: PEPSTriaTensor} = Tuple{T, T} + +ket(O::PEPSTriaSandwich) = O[1] +bra(O::PEPSTriaSandwich) = O[2] + +function virtualspace(O::PEPSTriaSandwich, dir) + return virtualspace(ket(O), dir) ⊗ virtualspace(bra(O), dir)' +end + +TensorKit.spacetype(::Type{P}) where {P <: PEPSTriaSandwich} = spacetype(eltype(P)) diff --git a/src/networks/tensors_tria.jl b/src/networks/tensors_tria.jl new file mode 100644 index 000000000..cca27ff1f --- /dev/null +++ b/src/networks/tensors_tria.jl @@ -0,0 +1,101 @@ +# +# Partition function +# + +""" + const PartitionFunctionTriaTensor{S} + +Default type for partition function tensors with 6 virtual indices, conventionally ordered +as: ``T : W ⊗ SW ⊗ SE ← NW ⊗ NE ⊗ E``. Here ``NW``, ``NE``, ``E``, ``SE``, ``SW`` and ``W`` denote the +north-west (120°), north-east (60°), east (0°), south-east (300°), south-west (240°) and west (180°) spaces, respectively, +where the angles denote the directions of the legs with respect to the positive horizontal axis. + +``` + NW NE + ╲ ╱ + ╲ ╱ + ╲ ╱ + W ----- T ----- E + ╱ ╲ + ╱ ╲ + ╱ ╲ + SW P SE +``` +""" +const PartitionFunctionTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 3, 3} +const PFTriaTensor = PartitionFunctionTriaTensor + +""" + PartitionFunctionTriaTensor(f, ::Type{T}, NWspace::S, + [NEspace::S], [Espace::S], [SEspace::S], + [SWspace::S], [Wspace::S]) where {T,S<:Union{Int,ElementarySpace}} + +Construct a PartitionFunctionTriaTensor tensor based on the +north-west, north-east, east, south-east, south-west and west spaces +The tensor elements are generated based on `f` and the element type is specified in `T`. +""" +function PartitionFunctionTriaTensor( + f, ::Type{T}, + NWspace::S, NEspace::S = NWspace, Espace::S = NWspace, SEspace::S = NWspace, + SWspace::S = NWspace, Wspace::S = NWspace, + ) where {T, S <: ElementarySpace} + return f(T, Wspace ⊗ SWspace ⊗ SEspace ← NWspace ⊗ NEspace ⊗ Espace) +end + +function virtualspace(t::PFTriaTensor, dir) + invp = (4, 5, 6, 3, 2, 1) # internally, virtual directions are ordered as NW, NE, E, SE, SW... + return space(t, invp[dir]) +end + +# +# PEPS +# + +""" + const PEPSTriaTensor{S} + +Default type for PEPS tensors with a single physical index, and 6 virtual indices, +conventionally ordered as: ``T : P ← NW ⊗ NE ⊗ E ⊗ SE ⊗ SW ⊗ W``. Here, ``P`` denotes the physical space +and ``NW``, ``NE``, ``E``, ``SE``, ``SW`` and ``W`` denote the north-west (120°), north-east (60°), +east (0°), south-east (300°), south-west (240°) and west (180°) spaces, respectively, +where the angles denote the directions of the legs with respect to the positive horizontal axis. + +``` +``` + NW NE + ╲ ╱ + ╲ ╱ + ╲ ╱ + W ----- T ----- E + ╱|╲ + ╱ | ╲ + ╱ | ╲ + SW P SE +``` +``` +""" +const PEPSTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} + +""" + PEPSTensor(f, ::Type{T}, Pspace::S, NWspace::S, + [NEspace::S], [Espace::S], [SEspace::S], + [SWspace::S], [Wspace::S]) where {T,S<:Union{Int,ElementarySpace}} + +Construct a PEPS tensor based on the physical, north-west, north-east, east, south-east, south-west and west spaces. +The tensor elements are generated based on `f` and the element type is specified in `T`. +""" +function PEPSTriaTensor( + f, ::Type{T}, + Pspace::S, + NWspace::S, NEspace::S = NWspace, Espace::S = NWspace, SEspace::S = NWspace, + SWspace::S = NWspace, Wspace::S = NWspace, + ) where {T, S <: ElementarySpace} + return f(T, Pspace ← NWspace ⊗ NEspace ⊗ Espace ⊗ SEspace ⊗ SWspace ⊗ Wspace) +end + +rotl60(t::PEPSTriaTensor) = permute(t, ((1,), (3, 4, 5, 6, 7, 2))) +rotr60(t::PEPSTriaTensor) = permute(t, ((1,), (7, 2, 3, 4, 5, 6))) +Base.rot180(t::PEPSTriaTensor) = permute(t, ((1,), (5, 6, 7, 2, 3, 4))) + +physicalspace(t::PEPSTriaTensor) = space(t, 1) +virtualspace(t::PEPSTriaTensor, dir) = space(t, dir + 1) diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl new file mode 100644 index 000000000..f3973a4ad --- /dev/null +++ b/test/ctmrg/triangular.jl @@ -0,0 +1,95 @@ +using Test +using Random +using MatrixAlgebraKit +using TensorKit +using MPSKit +using PEPSKit +using OptimKit +using Zygote + +@testset "CTM_triangular - Random tensor" begin + for conditioning in [true false] + for projector_alg in [:twothirds :full] + alg = SimultaneousCTMRGTria(; conditioning, projector_alg) + T = Float64 + χ = 7 + pspace = ℂ^2 + vspace = ℂ^3 + envspace = ℂ^χ + + ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') + bra = copy(ket) + pf = randn(T, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) + sandwiches = [pf, (ket, bra)] + unitcell = (1, 1) + + for (sandwich, vspace) in zip(sandwiches, [vspace, vspace ⊗ vspace']) + network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) + env₀ = CTMRGTriaEnv(randn, T, vspace, envspace; unitcell) + env, info = leading_boundary(env₀, network, alg) + @test info.convergence_metric < 1.0 + end + end + end +end + +@testset "CTM_triangular - Differentiability" begin + for conditioning in [true false] + for projector_alg in [:twothirds :full] + alg = SimultaneousCTMRGTria(; conditioning, projector_alg) + T = Float64 + χ = 7 + pspace = ℂ^2 + vspace = ℂ^3 + envspace = ℂ^χ + ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') + bra = copy(ket) + + unitcell = (1, 1) + network = InfiniteTriangularNetwork(fill((ket, bra), unitcell)) + env₀ = CTMRGTriaEnv(randn, T, vspace ⊗ vspace', envspace; unitcell) + + optimizer_alg = LBFGS(4; maxiter = 10) + real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) + reuse_env = true + peps = copy(ket) + + function peps_retract(x, η, α) + peps = x[1] + env = deepcopy(x[2]) + + retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) + peps´ = InfinitePEPS(map(first, retractions)) + ξ = InfinitePEPS(map(last, retractions)) + + return (peps´, env), ξ + end + retract = peps_retract + + + # optimize operator cost function + (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( + (peps₀, env₀), optimizer_alg; + retract, inner = real_inner, finalize!, (transport!) = (peps_transport!), + ) do (peps, env) + start_time = time_ns() + E, gs = withgradient(peps) do ψ + env′, info = hook_pullback( + leading_boundary, env, ψ, alg.boundary_alg; + alg_rrule = alg.gradient_alg, + ) + # ignore_derivatives() do + # reuse_env && update!(env, env′) + # push!(truncation_errors, info.truncation_error) + # push!(condition_numbers, info.condition_number) + # end + return energy(ψ, env′, operator) + end + g = only(gs) # `withgradient` returns tuple of gradients `gs` + push!(gradnorms_unitcell, norm.(g.A)) + push!(times, (time_ns() - start_time) * 1.0e-9) + return E, g + end + end + end +end From e6414ee6d77b1388a2a29aae4c39bff8579357de Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 23 Jan 2026 17:27:41 +0100 Subject: [PATCH 02/13] small fixes --- test/ctmrg/triangular.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index f3973a4ad..e18ac28d0 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -52,7 +52,7 @@ end optimizer_alg = LBFGS(4; maxiter = 10) real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) reuse_env = true - peps = copy(ket) + peps₀ = copy(ket) function peps_retract(x, η, α) peps = x[1] @@ -70,7 +70,7 @@ end # optimize operator cost function (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( (peps₀, env₀), optimizer_alg; - retract, inner = real_inner, finalize!, (transport!) = (peps_transport!), + retract, inner = real_inner, ) do (peps, env) start_time = time_ns() E, gs = withgradient(peps) do ψ From c36b3a0efdeda578c7ce3a4454d53dfed9ab5572 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 23 Feb 2026 20:25:49 +0100 Subject: [PATCH 03/13] updates on Triangular CTMRG Name changes to make them more consistent and verbose Use index convention in contractions Add some stuff necessary for differentiability --- src/PEPSKit.jl | 16 +- .../ctmrg_contractions_triangular.jl | 58 +++ .../ctmrg.jl | 8 +- .../ctmrg_triangular/simultaneous.jl | 240 +++++++++ .../utility.jl | 4 + .../fixed_point_differentiation.jl | 464 ++++++++++++++++++ src/algorithms/select_algorithm.jl | 2 +- src/algorithms/toolbox_triangular.jl | 126 +++++ .../triangular_ctmrg/simultaneous.jl | 319 ------------ ...ts.jl => ctmrg_environments_triangular.jl} | 10 +- src/networks/infinitetriangularnetwork.jl | 2 +- src/networks/local_triangular_sandwich.jl | 24 +- ...{tensors_tria.jl => tensors_triangular.jl} | 30 +- src/states/infinitepepstriangular.jl | 268 ++++++++++ test/ctmrg/triangular.jl | 176 ++++--- 15 files changed, 1328 insertions(+), 419 deletions(-) create mode 100644 src/algorithms/contractions/ctmrg_contractions_triangular.jl rename src/algorithms/{triangular_ctmrg => ctmrg_triangular}/ctmrg.jl (82%) create mode 100644 src/algorithms/ctmrg_triangular/simultaneous.jl rename src/algorithms/{triangular_ctmrg => ctmrg_triangular}/utility.jl (92%) create mode 100644 src/algorithms/optimization/fixed_point_differentiation.jl create mode 100644 src/algorithms/toolbox_triangular.jl delete mode 100644 src/algorithms/triangular_ctmrg/simultaneous.jl rename src/environments/{ctmrg_tria_environments.jl => ctmrg_environments_triangular.jl} (92%) rename src/networks/{tensors_tria.jl => tensors_triangular.jl} (75%) create mode 100644 src/states/infinitepepstriangular.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index e5a23f596..0e222aec7 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -72,11 +72,12 @@ include("utility/initialization.jl") include("networks/tensors.jl") include("networks/local_sandwich.jl") include("networks/infinitesquarenetwork.jl") -include("networks/tensors_tria.jl") +include("networks/tensors_triangular.jl") include("networks/local_triangular_sandwich.jl") include("networks/infinitetriangularnetwork.jl") include("states/infinitepeps.jl") +include("states/infinitepepstriangular.jl") include("states/infinitepartitionfunction.jl") include("utility/symmetrization.jl") @@ -92,7 +93,7 @@ include("operators/lattices/squarelattice.jl") include("operators/models.jl") include("environments/ctmrg_environments.jl") -include("environments/ctmrg_tria_environments.jl") +include("environments/ctmrg_environments_triangular.jl") include("environments/vumps_environments.jl") include("environments/suweight.jl") include("environments/bp_environments.jl") @@ -143,9 +144,10 @@ include("algorithms/ctmrg/gaugefix.jl") include("algorithms/ctmrg/c4v.jl") include("algorithms/ctmrg/initialization.jl") -include("algorithms/triangular_ctmrg/ctmrg.jl") -include("algorithms/triangular_ctmrg/utility.jl") -include("algorithms/triangular_ctmrg/simultaneous.jl") +include("algorithms/contractions/ctmrg_contractions_triangular.jl") +include("algorithms/ctmrg_triangular/ctmrg.jl") +include("algorithms/ctmrg_triangular/utility.jl") +include("algorithms/ctmrg_triangular/simultaneous.jl") include("algorithms/truncation/truncationschemes.jl") include("algorithms/truncation/fullenv_truncation.jl") @@ -174,6 +176,8 @@ include("algorithms/expectation_value/network_value.jl") include("algorithms/expectation_value/correlator_adapters.jl") include("algorithms/expectation_value/correlators.jl") include("algorithms/toolbox.jl") +include("algorithms/toolbox_triangular.jl") +include("algorithms/correlators.jl") include("algorithms/optimization/implicit_differentiation.jl") include("algorithms/optimization/preconditioning.jl") @@ -189,6 +193,7 @@ export CTMRGTriaEnv, SimultaneousCTMRGTria export initialize_ctmrg_environment, RandomInitialization, ProductStateInitialization, ApplicationInitialization, IdentityInitialization export corner, edge, setcorner!, setedge! +export CTMRGEnvTriangular, SimultaneousCTMRGTriangular # CTMRGTriaEnv, SimultaneousCTMRGTria export FixedSpaceTruncation, SiteDependentTruncation export HalfInfiniteProjector, FullInfiniteProjector export C4vCTMRG, C4vEighProjector, C4vQRProjector @@ -212,6 +217,7 @@ export TimeEvolver, timestep, time_evolve export InfiniteSquareNetwork, InfiniteTriangularNetwork export InfinitePartitionFunction export InfinitePEPS, InfiniteTransferPEPS +export InfinitePEPSTriangular export SUWeight export InfinitePEPO, InfiniteTransferPEPO diff --git a/src/algorithms/contractions/ctmrg_contractions_triangular.jl b/src/algorithms/contractions/ctmrg_contractions_triangular.jl new file mode 100644 index 000000000..d00515d6b --- /dev/null +++ b/src/algorithms/contractions/ctmrg_contractions_triangular.jl @@ -0,0 +1,58 @@ +function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, dir::Int, r::Int, c::Int) where {P <: PFTensorTriangular} + @tensor opt = true mat[-1 -2; -3 -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3; 2] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4; -3] * rotl60(network[r, c], mod(dir - 1, 6))[7 -2 -4; 5 3 4] + return mat +end + +function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, dir::Int, r::Int, c::Int) where {P <: PEPSSandwichTriangular} + @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5 8; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3 9; 2] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7 10; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4 11; -4] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 5 3 4 -5 -2 7] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 8 9 11 -6 -3 10]) + return mat +end + +function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Pas, Pbs, dir, r, c) where {P <: PEPSSandwichTriangular} + @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := Pas[dir, r, c][-1; 1 2 8] * Pbs[dir, r, c][6 7 9; -4] * + env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5 11; 6] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 3 5 7 -5 -2 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 10 11 9 -6 -3 8]) + return mat / norm(mat) +end + +function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Pas, Pbs, dir, r, c) where {P <: PFTensorTriangular} + @tensor opt = true mat[-1 -2; -3 -4] := Pas[dir, r, c][-1; 1 2] * Pbs[dir, r, c][6 7; -3] * + env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5; 6] * rotl60(network[r, c], mod(dir - 1, 6))[2 -2 -4; 3 5 7] + return mat / norm(mat) +end + +function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PEPSSandwichTriangular} + @tensor opt = true σL[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2 10; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3 11; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5 12; 4] * + Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9 13; -4] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7 14; 6] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[15 2 9 -2 7 5 3] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 13 -3 14 12 11]) + @tensor opt = true σR[-1; -2 -3 -4] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2 10; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3 11; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5 12; 6] * + Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7 13; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9 14; 8] * + rotl60(ket(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 9 2 3 5 7 -3] * conj(rotl60(bra(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 14 10 11 12 13 -4]) + return σL, σR +end + +function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PFTensorTriangular} + @tensor opt = true σL[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5; 4] * + Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9; -3] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7; 6] * + rotl60(network[r, c], mod(dir - 1, 6))[3 5 7; 2 9 -2] + @tensor opt = true σR[-1; -2 -3] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5; 6] * + Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9; 8] * + rotl60(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...], mod(dir - 1, 6))[-3 7 5; 9 2 3] + return σL, σR +end + +function renormalize_corner_triangular((dir, r, c), network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PFTensorTriangular} + @tensor opt = true env_C_new[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7; 8] * + rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] + return env_C_new +end + +function renormalize_corner_triangular((dir, r, c), network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PEPSSandwichTriangular} + @tensor opt = true env_C_new[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2 11; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7 12; 8] * + rotl60(ket(network[r, c]), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * + Pas[mod1(dir - 1, 6), r, c][-1; 4 5 13] * Pbs[dir, r, c][8 9 14; -4] + return env_C_new +end \ No newline at end of file diff --git a/src/algorithms/triangular_ctmrg/ctmrg.jl b/src/algorithms/ctmrg_triangular/ctmrg.jl similarity index 82% rename from src/algorithms/triangular_ctmrg/ctmrg.jl rename to src/algorithms/ctmrg_triangular/ctmrg.jl index 9e06e2ad4..f981de1e9 100644 --- a/src/algorithms/triangular_ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg_triangular/ctmrg.jl @@ -1,11 +1,11 @@ -abstract type CTMRGTriaAlgorithm end +abstract type CTMRGAlgorithmTriangular end -function leading_boundary(env₀::CTMRGTriaEnv, network::InfiniteTriangularNetwork; kwargs...) +function leading_boundary(env₀::CTMRGEnvTriangular, network::InfiniteTriangularNetwork; kwargs...) alg = select_algorithm(leading_boundary, env₀; kwargs...) return leading_boundary(env₀, network, alg) end function leading_boundary( - env₀::CTMRGTriaEnv, network::InfiniteTriangularNetwork, alg::CTMRGTriaAlgorithm + env₀::CTMRGEnvTriangular, network::InfiniteTriangularNetwork, alg::CTMRGAlgorithmTriangular ) log = ignore_derivatives(() -> MPSKit.IterLog("CTMRG")) return LoggingExtras.withlevel(; alg.verbosity) do @@ -33,7 +33,7 @@ function leading_boundary( return env, info end end -function leading_boundary(env₀::CTMRGTriaEnv, state, args...; kwargs...) +function leading_boundary(env₀::CTMRGEnvTriangular, state, args...; kwargs...) return leading_boundary(env₀, InfiniteTriangularNetwork(state), args...; kwargs...) end diff --git a/src/algorithms/ctmrg_triangular/simultaneous.jl b/src/algorithms/ctmrg_triangular/simultaneous.jl new file mode 100644 index 000000000..cddda61a2 --- /dev/null +++ b/src/algorithms/ctmrg_triangular/simultaneous.jl @@ -0,0 +1,240 @@ +struct SimultaneousCTMRGTriangular <: CTMRGAlgorithmTriangular + tol::Float64 + maxiter::Int + miniter::Int + verbosity::Int + conditioning::Bool + projector_alg::Symbol + trunctype::Symbol +end +function SimultaneousCTMRGTriangular(; + tol = Defaults.ctmrg_tol, + maxiter = Defaults.ctmrg_maxiter, + miniter = Defaults.ctmrg_miniter, + verbosity = Defaults.ctmrg_verbosity, + conditioning = true, + projector_alg = :twothirds, + trunctype = :truncrank + ) + return SimultaneousCTMRGTriangular(tol, maxiter, miniter, verbosity, conditioning, projector_alg, trunctype) +end + +# Based on +# https://arxiv.org/pdf/2510.04907 + +function ctmrg_iteration(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular, alg::SimultaneousCTMRGTriangular) + trunc_type = getfield(@__MODULE__, alg.trunctype) + if trunc_type == FixedSpaceTruncation + trunc = FixedSpaceTruncation() + else + trunc = trunc_type(dim(domain(env.C[1, 1, 1])[1])) + end + Pas, Pbs, S = calculate_projectors(network, env, trunc, alg.projector_alg) + + env = renormalize_corners!(network, env, Pas, Pbs) + env = normalize_corners!(env) + + Ẽas, Ẽbs, Ẽastr, Ẽbstr = semi_renormalize(network, env, Pas, Pbs, trunc) + Qas, Qbs = build_matrix_second_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; alg.conditioning) + + env = renormalize_edges(env, Ẽas, Ẽbs, Qas, Qbs) + normalize_edges!(env) + return env, S +end + +function calculate_projectors(network, env, trunc, projector_alg) + if projector_alg == :full + return calculate_full_projectors(network, env, trunc) + elseif projector_alg == :twothirds + return calculate_twothirds_projectors(network, env, trunc) + else + @error "projector_alg = $projector_alg not defined" + end +end + +function calculate_twothirds_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, DiagonalTensorMap{real(E), S}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) + ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) + ρρ = ρL * ρR + ρρ /= norm(ρρ) + + U, S, V = svd_trunc(ρρ; trunc) + + Pb = ρR * V' * sdiag_pow(S, -1 / 2) + Pa = sdiag_pow(S, -1 / 2) * U' * ρL + return Pa, Pb, S + end + return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) +end + +function calculate_full_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, DiagonalTensorMap{real(E), S}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) + ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) + ρ̄ = build_double_corner_matrix_triangular(network, env, mod1(dir + 3, 6), r, c) + ρ̄ /= norm(ρ̄) + Ū, S̄, V̄ᴴ = svd_full(ρ̄) + ρ̄ᴿ = Ū * sqrt(S̄) + ρ̄ᴸ = sqrt(S̄) * V̄ᴴ + ρρ = ρ̄ᴸ * ρL * ρR * ρ̄ᴿ + ρρ /= norm(ρρ) + + U, S, Vᴴ = svd_trunc(ρρ; trunc) + + Pb = ρR * ρ̄ᴿ * Vᴴ' * sdiag_pow(S, -1 / 2) + Pa = sdiag_pow(S, -1 / 2) * U' * ρ̄ᴸ * ρL + + return Pa, Pb, S + end + return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) +end + +function renormalize_corners!(network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PEPSSandwichTriangular} + coordinates = eachcoordinate(network, 1:6) + new_corners′ = similar(env.C) + new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) + return renormalize_corner_triangular((dir, r, c), network, env, Pas, Pbs) + end + return CTMRGEnvTriangular(new_corners, env.Ea, env.Eb) +end + +function renormalize_corners!(network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PFTensorTriangular} + coordinates = eachcoordinate(network, 1:6) + new_corners′ = similar(env.C) + new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) + return renormalize_corner_triangular((dir, r, c), network, env, Pas, Pbs) + end + return CTMRGEnvTriangular(new_corners, env.Ea, env.Eb) +end + +function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 2}} + return permute(t, ((1, 3), (2,))) +end + +function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 3}} + return permute(t, ((1, 3, 4), (2,))) +end + +function semi_renormalize(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular, Pas, Pbs, trunc) + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + mat = semi_renormalize_edge(network, env, Pas, Pbs, dir, r, c) + + U, S, V = svd_full(mat) + + Ẽb = U * sqrt(S) + Ẽa = _permute_edge(sqrt(S) * V) + + Utr, Str, Vtr = svd_trunc(mat; trunc) + Ẽbtr = Utr * sqrt(Str) + Ẽatr = _permute_edge(sqrt(Str) * Vtr) + + return Ẽa, Ẽb, Ẽatr, Ẽbtr + end + return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3), getindex.(projectors, 4) +end + +function build_matrix_second_projectors(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; conditioning = true) + coordinates = eachcoordinate(network, 1:6) + E = scalartype(env.C[1, 1, 1]) + S = spacetype(env.C[1, 1, 1]) + T_proj = Tuple{AbstractTensorMap{E, S, 1, 1}, AbstractTensorMap{E, S, 1, 1}} + projectors′ = similar(coordinates, T_proj) + projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + σL, σR = build_halfinfinite_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) + if conditioning + σL /= norm(σL) + σR /= norm(σR) + UL, SL, VLᴴ = svd_full(σL) + UR, SR, VRᴴ = svd_full(σR) + + FLU = sqrt(SL) * VLᴴ + FRU = UR * sqrt(SR) + + mat = FLU * FRU + mat /= norm(mat) + WU, SU, QUᴴ = svd_trunc(mat; trunc) + + Qa = sdiag_pow(SU, -1 / 2) * WU' * FLU + Qb = FRU * QUᴴ' * sdiag_pow(SU, -1 / 2) + else + mat = σL * σR + mat /= norm(mat) + U, S, V = svd_trunc(mat; trunc) + Qa = sdiag_pow(S, -1 / 2) * U' * σL + Qb = σR * V' * sdiag_pow(S, -1 / 2) + end + return Qa, Qb + end + return getindex.(projectors, 1), getindex.(projectors, 2) +end + +function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} + coordinates = collect(Iterators.product(axes(env.Ea)...)) + T_proj = Tuple{AbstractTensorMap{E, S, 3, 1}, AbstractTensorMap{E, S, 3, 1}} + new_edges′ = similar(env.Ea, T_proj) + new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) + Eb_new = Ẽbs[dir, r, c] * Qbs[dir, r, c] + Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,),(2,3,4))), ((1,2,3),(4,))) + return Ea_new, Eb_new + end + return CTMRGEnvTriangular(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) +end + +function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} + coordinates = collect(Iterators.product(axes(env.Ea)...)) + T_proj = Tuple{AbstractTensorMap{E, S, 2, 1}, AbstractTensorMap{E, S, 2, 1}} + new_edges′ = similar(env.Ea, T_proj) + new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) + Eb_new = Ẽbs[dir, r, c] * Qbs[dir, r, c] + Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,),(2,3))), ((1,2),(3,))) + return Ea_new, Eb_new + end + return CTMRGEnvTriangular(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) +end + +function normalize_corners!(env) + coordinates = collect(Iterators.product(axes(env.Ea)...)) + new_corners′ = similar(env.C, typeof(env.C[1, 1, 1])) + new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) + env_C_new = env.C[dir, r, c] / norm(env.C[dir, r, c]) + return env_C_new + end + return CTMRGEnvTriangular(new_corners, env.Ea, env.Eb) +end + +function normalize_edges!(env) + (r, c) = (1,1) + for dir in 1:6 + env.Ea[dir,r,c] /= norm(env.Ea[dir,r,c]) + env.Eb[dir,r,c] /= norm(env.Eb[dir,r,c]) + end + return env +end + +function calculate_error(Ss, Ss_prev) + ε = Inf + for (S, S_prev) in zip(Ss, Ss_prev) + if space(S) == space(S_prev) + ε = norm(S^4 - S_prev^4) + else + return Inf + end + end + return ε +end diff --git a/src/algorithms/triangular_ctmrg/utility.jl b/src/algorithms/ctmrg_triangular/utility.jl similarity index 92% rename from src/algorithms/triangular_ctmrg/utility.jl rename to src/algorithms/ctmrg_triangular/utility.jl index 0230e0944..faf1e7066 100644 --- a/src/algorithms/triangular_ctmrg/utility.jl +++ b/src/algorithms/ctmrg_triangular/utility.jl @@ -15,6 +15,10 @@ function _coordinates(dir::Int, rot::Int, r::Int, c::Int, unitcell::Tuple{Int, I end end +function _coordinates(dir::Int, r::Int, c::Int, unitcell::Tuple{Int, Int}) + return _coordinates(dir, 0, r, c; unitcell)[2:3] +end + function _truncway(trunctype::Symbol, D::E) where {E <: ElementarySpace} if trunctype == :truncdim return truncdim(dim(D)) diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl new file mode 100644 index 000000000..4ac24cb2c --- /dev/null +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -0,0 +1,464 @@ +abstract type GradMode{F} end + +const GRADIENT_MODE_SYMBOLS = IdDict{Symbol, Type{<:GradMode}}() +const LINSOLVER_SOLVER_SYMBOLS = IdDict{Symbol, Type{<:KrylovKit.LinearSolver}}( + :gmres => GMRES, :bicgstab => BiCGStab +) +const EIGSOLVER_SOLVER_SYMBOLS = IdDict{Symbol, Type{<:KrylovKit.KrylovAlgorithm}}( + :arnoldi => Arnoldi +) + +""" + GradMode(; kwargs...) + +Keyword argument parser returning the appropriate `GradMode` algorithm struct. +""" +function GradMode(; + alg = Defaults.gradient_alg, + tol = Defaults.gradient_tol, + maxiter = Defaults.gradient_maxiter, + verbosity = Defaults.gradient_verbosity, + iterscheme = Defaults.gradient_iterscheme, + solver_alg = (;), + ) + # replace symbol with GradMode alg type + haskey(GRADIENT_MODE_SYMBOLS, alg) || + throw(ArgumentError("unknown GradMode algorithm: $alg")) + alg_type = GRADIENT_MODE_SYMBOLS[alg] + + # parse GradMode algorithm + gradient_algorithm = if alg_type <: Union{GeomSum, ManualIter} + alg_type{iterscheme}(tol, maxiter, verbosity) + elseif alg_type <: Union{<:LinSolver, <:EigSolver} + solver = if solver_alg isa NamedTuple # determine linear/eigen solver algorithm + solver_kwargs = (; tol, maxiter, verbosity, solver_alg...) + + solver_type = if alg_type <: LinSolver # replace symbol with solver alg type + solver_kwargs = (; alg = Defaults.gradient_linsolver, solver_kwargs...) + haskey(LINSOLVER_SOLVER_SYMBOLS, solver_kwargs.alg) || throw( + ArgumentError("unknown LinSolver solver: $(solver_kwargs.alg)"), + ) + LINSOLVER_SOLVER_SYMBOLS[solver_kwargs.alg] + elseif alg_type <: EigSolver + solver_kwargs = (; + alg = Defaults.gradient_eigsolver, + eager = Defaults.gradient_eigsolver_eager, + solver_kwargs..., + ) + haskey(EIGSOLVER_SOLVER_SYMBOLS, solver_kwargs.alg) || throw( + ArgumentError("unknown EigSolver solver: $(solver_kwargs.alg)"), + ) + EIGSOLVER_SOLVER_SYMBOLS[solver_kwargs.alg] + end + + solver_kwargs = Base.structdiff(solver_kwargs, (; alg = nothing)) # remove `alg` keyword argument + solver_type(; solver_kwargs...) + else + solver_alg + end + + alg_type{iterscheme}(solver) + else + throw(ArgumentError("unknown gradient algorithm: $alg")) + end + + return gradient_algorithm +end + +iterscheme(::GradMode{F}) where {F} = F + +""" +$(TYPEDEF) + +Gradient mode for CTMRG using explicit evaluation of the geometric sum. + +## Fields + +$(TYPEDFIELDS) + +## Constructors + + GeomSum(; kwargs...) + +Construct the `GeomSum` algorithm struct based on the following keyword arguments: + +* `tol::Real=$(Defaults.gradient_tol)` : Convergence tolerance for the difference of norms of two consecutive summands in the geometric sum. +* `maxiter::Int=$(Defaults.gradient_maxiter)` : Maximal number of gradient iterations. +* `verbosity::Int=$(Defaults.gradient_verbosity)` : Output information verbosity that can be one of the following: + 0. Suppress output information + 1. Print convergence warnings + 2. Information at each gradient iteration +* `iterscheme::Symbol=:$(Defaults.gradient_iterscheme)` : Style of CTMRG iteration which is being differentiated, which can be: + - `:fixed` : the differentiated CTMRG iteration uses a pre-computed SVD with a fixed set of gauges + - `:diffgauge` : the differentiated iteration consists of a CTMRG iteration and a subsequent gauge-fixing step such that the gauge-fixing procedure is differentiated as well +""" +struct GeomSum{F} <: GradMode{F} + tol::Real + maxiter::Int + verbosity::Int +end +GeomSum(; kwargs...) = GradMode(; alg = :geomsum, kwargs...) + +GRADIENT_MODE_SYMBOLS[:geomsum] = GeomSum + +""" +$(TYPEDEF) + +Gradient mode for CTMRG using manual iteration to solve the linear problem. + +## Fields + +$(TYPEDFIELDS) + +## Constructors + + ManualIter(; kwargs...) + +Construct the `ManualIter` algorithm struct based on the following keyword arguments: + +* `tol::Real=$(Defaults.gradient_tol)` : Convergence tolerance for the norm difference of two consecutive `dx` contributions. +* `maxiter::Int=$(Defaults.gradient_maxiter)` : Maximal number of gradient iterations. +* `verbosity::Int=$(Defaults.gradient_verbosity)` : Output information verbosity that can be one of the following: + 0. Suppress output information + 1. Print convergence warnings + 2. Information at each gradient iteration +* `iterscheme::Symbol=:$(Defaults.gradient_iterscheme)` : Style of CTMRG iteration which is being differentiated, which can be: + - `:fixed` : the differentiated CTMRG iteration uses a pre-computed SVD with a fixed set of gauges + - `:diffgauge` : the differentiated iteration consists of a CTMRG iteration and a subsequent gauge-fixing step such that the gauge-fixing procedure is differentiated as well +""" +struct ManualIter{F} <: GradMode{F} + tol::Real + maxiter::Int + verbosity::Int +end +ManualIter(; kwargs...) = GradMode(; alg = :manualiter, kwargs...) + +GRADIENT_MODE_SYMBOLS[:manualiter] = ManualIter + +""" +$(TYPEDEF) + +Gradient mode wrapper around `KrylovKit.LinearSolver` for solving the gradient linear +problem using iterative solvers. + +## Fields + +$(TYPEDFIELDS) + +## Constructors + + LinSolver(; kwargs...) + +Construct the `LinSolver` algorithm struct based on the following keyword arguments: + +* `tol::Real=$(Defaults.gradient_tol)` : Convergence tolerance of the linear solver. +* `maxiter::Int=$(Defaults.gradient_maxiter)` : Maximal number of solver iterations. +* `verbosity::Int=$(Defaults.gradient_verbosity)` : Output information verbosity of the linear solver. +* `iterscheme::Symbol=:$(Defaults.gradient_iterscheme)` : Style of CTMRG iteration which is being differentiated, which can be: + - `:fixed` : the differentiated CTMRG iteration uses a pre-computed SVD with a fixed set of gauges + - `:diffgauge` : the differentiated iteration consists of a CTMRG iteration and a subsequent gauge-fixing step such that the gauge-fixing procedure is differentiated as well +* `solver_alg::Union{KrylovKit.LinearSolver,NamedTuple}=(; alg::Symbol=:$(Defaults.gradient_linsolver)` : Linear solver algorithm which, if supplied directly as a `KrylovKit.LinearSolver` overrides the above specified `tol`, `maxiter` and `verbosity`. Alternatively, it can be supplied via a `NamedTuple` where `alg` can be one of the following: + - `:gmres` : GMRES iterative linear solver, see [`KrylovKit.GMRES`](@extref) for details + - `:bicgstab` : BiCGStab iterative linear solver, see [`KrylovKit.BiCGStab`](@extref) for details +""" +struct LinSolver{F} <: GradMode{F} + solver_alg::KrylovKit.LinearSolver +end +LinSolver(; kwargs...) = GradMode(; alg = :linsolver, kwargs...) + +GRADIENT_MODE_SYMBOLS[:linsolver] = LinSolver + +""" +$(TYPEDEF) + +Gradient mode wrapper around `KrylovKit.KrylovAlgorithm` for solving the gradient linear +problem as an eigenvalue problem. + +## Fields + +$(TYPEDFIELDS) + +## Constructors + + EigSolver(; kwargs...) + +Construct the `EigSolver` algorithm struct based on the following keyword arguments: + +* `tol::Real=$(Defaults.gradient_tol)` : Convergence tolerance of the eigen solver. +* `maxiter::Int=$(Defaults.gradient_maxiter)` : Maximal number of solver iterations. +* `verbosity::Int=$(Defaults.gradient_verbosity)` : Output information verbosity of the linear solver. +* `iterscheme::Symbol=:$(Defaults.gradient_iterscheme)` : Style of CTMRG iteration which is being differentiated, which can be: + - `:fixed` : the differentiated CTMRG iteration uses a pre-computed SVD with a fixed set of gauges + - `:diffgauge` : the differentiated iteration consists of a CTMRG iteration and a subsequent gauge-fixing step such that the gauge-fixing procedure is differentiated as well +* `solver_alg::Union{KrylovKit.KrylovAlgorithm,NamedTuple}=(; alg=:$(Defaults.gradient_eigsolver)` : Eigen solver algorithm which, if supplied directly as a `KrylovKit.KrylovAlgorithm` overrides the above specified `tol`, `maxiter` and `verbosity`. Alternatively, it can be supplied via a `NamedTuple` where `alg` can be one of the following: + - `:arnoldi` : Arnoldi Krylov algorithm, see [`KrylovKit.Arnoldi`](@extref) for details +""" +struct EigSolver{F} <: GradMode{F} + solver_alg::KrylovKit.KrylovAlgorithm +end +EigSolver(; kwargs...) = GradMode(; alg = :eigsolver, kwargs...) + +GRADIENT_MODE_SYMBOLS[:eigsolver] = EigSolver + +#= +Evaluating the gradient of the cost function for CTMRG: +- The gradient of the cost function for CTMRG can be computed using automatic differentiation (AD) or explicit evaluation of the geometric sum. +- With AD, the gradient is computed by differentiating the cost function with respect to the PEPS tensors, including computing the environment tensors. +- With explicit evaluation of the geometric sum, the gradient is computed by differentiating the cost function with the environment kept fixed, and then manually adding the gradient contributions from the environments. +=# + +function _rrule( + gradmode::GradMode{:diffgauge}, + config::RuleConfig, + ::typeof(leading_boundary), + envinit, + state, + alg::Union{CTMRGAlgorithm,CTMRGAlgorithmTriangular}, + ) + env, info = leading_boundary(envinit, state, alg) + alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation + + function leading_boundary_diffgauge_pullback((Δenv′, Δinfo)) + Δenv = unthunk(Δenv′) + + # find partial gradients of gauge-fixed single CTMRG iteration + function f(A, x) + return gauge_fix(x, ctmrg_iteration(InfiniteSquareNetwork(A), x, alg_fixed)[1])[1] + end + _, env_vjp = rrule_via_ad(config, f, state, env) + + # evaluate the geometric sum + ∂f∂A(x)::typeof(state) = env_vjp(x)[2] + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] + ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) + + return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() + end + + return (env, info), leading_boundary_diffgauge_pullback +end + +# Here f is differentiated from an pre-computed SVD with fixed U, S and V +function _rrule( + gradmode::GradMode{:fixed}, + config::RuleConfig, + ::typeof(MPSKit.leading_boundary), + envinit, + state, + alg::SimultaneousCTMRG, + ) + env, = leading_boundary(envinit, state, alg) + alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation + env_conv, info = ctmrg_iteration(InfiniteSquareNetwork(state), env, alg_fixed) + env_fixed, signs = gauge_fix(env, env_conv) + + # Fix SVD + svd_alg_fixed = _fix_svd_algorithm(alg.projector_alg.svd_alg, signs, info) + alg_fixed = @set alg.projector_alg.svd_alg = svd_alg_fixed + alg_fixed = @set alg_fixed.projector_alg.trunc = notrunc() + + function leading_boundary_fixed_pullback((Δenv′, Δinfo)) + Δenv = unthunk(Δenv′) + + function f(A, x) + return fix_global_phases( + x, ctmrg_iteration(InfiniteSquareNetwork(A), x, alg_fixed)[1] + ) + end + _, env_vjp = rrule_via_ad(config, f, state, env_fixed) + + # evaluate the geometric sum + ∂f∂A(x)::typeof(state) = env_vjp(x)[2] + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] + ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) + + return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() + end + + return (env_fixed, info), leading_boundary_fixed_pullback +end + +function _rrule( + gradmode::GradMode{:fixed}, + config::RuleConfig, + ::typeof(MPSKit.leading_boundary), + envinit, + state, + alg::SimultaneousCTMRGTriangular, + ) + env, = leading_boundary(envinit, state, alg) + alg_fixed = @set alg.trunctype = :FixedSpaceTruncation # fix spaces during differentiation + env_conv, info = ctmrg_iteration(InfiniteTriangularNetwork(state), env, alg_fixed) + env_fixed, signs = gauge_fix(env, env_conv) + + # Fix SVD + svd_alg_fixed = _fix_svd_algorithm(alg.projector_alg.svd_alg, signs, info) + alg_fixed = @set alg.projector_alg.svd_alg = svd_alg_fixed + alg_fixed = @set alg_fixed.projector_alg.trunc = notrunc() + + function leading_boundary_fixed_pullback((Δenv′, Δinfo)) + Δenv = unthunk(Δenv′) + + function f(A, x) + return fix_global_phases( + x, ctmrg_iteration(InfiniteTriangularNetwork(A), x, alg_fixed)[1] + ) + end + _, env_vjp = rrule_via_ad(config, f, state, env_fixed) + + # evaluate the geometric sum + ∂f∂A(x)::typeof(state) = env_vjp(x)[2] + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] + ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) + + return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() + end + + return (env_fixed, info), leading_boundary_fixed_pullback +end + +function _fix_svd_algorithm(alg::SVDAdjoint, signs, info) + # embed gauge signs in larger space to fix gauge of full U and V on truncated subspace + rowsize, colsize = size(signs, 2), size(signs, 3) + signs_full = map(Iterators.product(1:4, 1:rowsize, 1:colsize)) do (dir, r, c) + σ = signs[dir, r, c] + r_sign, c_sign = if dir == NORTH # take unit cell interdependency of signs into account + r, _prev(c, colsize) + elseif dir == EAST + _prev(r, rowsize), c + elseif dir == SOUTH + r, _next(c, colsize) + elseif dir == WEST + _next(r, rowsize), c + end + extended_space = domain(info.U_full[dir, r_sign, c_sign]) ← codomain(info.V_full[dir, r_sign, c_sign]) + extended_σ = zeros(scalartype(σ), extended_space) + for (c, b) in blocks(extended_σ) + σc = block(σ, c) + kept_dim = size(σc, 1) + b[diagind(b)] .= one(scalartype(σ)) # put ones on the diagonal + b[1:kept_dim, 1:kept_dim] .= σc # set to σ on kept subspace + end + return extended_σ + end + + # fix kept and full U and V + U_fixed, V_fixed = fix_relative_phases(info.U, info.V, signs) + U_full_fixed, V_full_fixed = fix_relative_phases(info.U_full, info.V_full, signs_full) + return SVDAdjoint(; + fwd_alg = FixedSVD(U_fixed, info.S, V_fixed, U_full_fixed, info.S_full, V_full_fixed), + rrule_alg = alg.rrule_alg, + ) +end +function _fix_svd_algorithm(alg::SVDAdjoint{F}, signs, info) where {F <: IterSVD} + # fix kept U and V only since iterative SVD doesn't have access to full spectrum + U_fixed, V_fixed = fix_relative_phases(info.U, info.V, signs) + return SVDAdjoint(; + fwd_alg = FixedSVD(U_fixed, info.S, V_fixed, nothing, nothing, nothing), + rrule_alg = alg.rrule_alg, + ) +end + +@doc """ + fpgrad(∂F∂x, ∂f∂x, ∂f∂A, y0, alg) + +Compute the gradient of the CTMRG fixed point by solving the following equation: + +dx = ∑ₙ (∂f∂x)ⁿ ∂f∂A dA = (1 - ∂f∂x)⁻¹ ∂f∂A dA + +where `∂F∂x` is the gradient of the cost function with respect to the PEPS tensors, `∂f∂x` +is the partial gradient of the CTMRG iteration with respect to the environment tensors, +`∂f∂A` is the partial gradient of the CTMRG iteration with respect to the PEPS tensors, and +`y0` is the initial guess for the fixed-point iteration. The function returns the gradient +`dx` of the fixed-point iteration. +""" +fpgrad + +# TODO: can we construct an implementation that does not need to evaluate the vjp +# twice if both ∂f∂A and ∂f∂x are needed? +function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, _, alg::GeomSum) + g = ∂F∂x + dx = ∂f∂A(g) # n = 0 term: ∂F∂x ∂f∂A + ϵ = 2 * alg.tol + for i in 1:(alg.maxiter) + g = ∂f∂x(g) + Σₙ = ∂f∂A(g) + dx += Σₙ + ϵnew = norm(Σₙ) # TODO: normalize this error? + Δϵ = ϵ - ϵnew + alg.verbosity > 1 && + @printf("Gradient iter: %3d ‖Σₙ‖: %.2e Δ‖Σₙ‖: %.2e\n", i, ϵnew, Δϵ) + ϵ = ϵnew + + ϵ < alg.tol && break + if alg.verbosity > 0 && i == alg.maxiter + @warn "gradient fixed-point iteration reached maximal number of iterations at ‖Σₙ‖ = $ϵ" + end + end + return dx +end + +function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, y₀, alg::ManualIter) + y = deepcopy(y₀) # Do not mutate y₀ + dx = ∂f∂A(y) + ϵ = 1.0 + for i in 1:(alg.maxiter) + y′ = ∂F∂x + ∂f∂x(y) + + dxnew = ∂f∂A(y′) + ϵnew = norm(dxnew - dx) + Δϵ = ϵ - ϵnew + alg.verbosity > 1 && @printf( + "Gradient iter: %3d ‖Cᵢ₊₁-Cᵢ‖/N: %.2e Δ‖Cᵢ₊₁-Cᵢ‖/N: %.2e\n", i, ϵnew, Δϵ + ) + y = y′ + dx = dxnew + ϵ = ϵnew + + ϵ < alg.tol && break + if alg.verbosity > 0 && i == alg.maxiter + @warn "gradient fixed-point iteration reached maximal number of iterations at ‖Cᵢ₊₁-Cᵢ‖ = $ϵ" + end + end + return dx +end + +function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, y₀, alg::LinSolver) + y, info = reallinsolve(∂f∂x, ∂F∂x, y₀, alg.solver_alg, 1, -1) + if alg.solver_alg.verbosity > 0 && info.converged != 1 + @warn("gradient fixed-point iteration reached maximal number of iterations:", info) + end + + return ∂f∂A(y) +end + +function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, x₀, alg::EigSolver) + function f(X) + y = ∂f∂x(X[1]) + return (y + X[2] * ∂F∂x, X[2]) + end + X₀ = (x₀, one(scalartype(x₀))) + _, vecs, info = realeigsolve(f, X₀, 1, :LM, alg.solver_alg) + if alg.solver_alg.verbosity > 0 && info.converged < 1 + @warn("gradient fixed-point iteration reached maximal number of iterations:", info) + end + if norm(vecs[1][2]) < 1.0e-2 * alg.solver_alg.tol + @warn "Fixed-point gradient computation using Arnoldi failed:\n\tauxiliary component should be finite but was $(vecs[1][2])\n\tpossibly the Jacobian does not have a unique eigenvalue 1" + @info "Falling back to linear solver for fixed-point gradient computation." + backup_ls_alg = _alg_or_nt( + GradMode, + (; + alg = :linsolver, + tol = alg.solver_alg.tol, + maxiter = alg.solver_alg.maxiter * alg.solver_alg.krylovdim, + verbosity = alg.solver_alg.verbosity, + iterscheme = iterscheme(alg), + ) + ) + return fpgrad(∂F∂x, ∂f∂x, ∂f∂A, x₀, backup_ls_alg) + else + y = scale(vecs[1][1], 1 / vecs[1][2]) + end + + return ∂f∂A(y) +end diff --git a/src/algorithms/select_algorithm.jl b/src/algorithms/select_algorithm.jl index 5eb29c4b2..7faf9564f 100644 --- a/src/algorithms/select_algorithm.jl +++ b/src/algorithms/select_algorithm.jl @@ -161,7 +161,7 @@ end function select_algorithm( ::typeof(leading_boundary), - env₀::CTMRGTriaEnv + env₀::CTMRGEnvTriangular ) return SimultaneousCTMRGTria() end diff --git a/src/algorithms/toolbox_triangular.jl b/src/algorithms/toolbox_triangular.jl new file mode 100644 index 000000000..cdf2f663d --- /dev/null +++ b/src/algorithms/toolbox_triangular.jl @@ -0,0 +1,126 @@ +function network_value(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular) + return prod(Iterators.product(axes(network)...)) do (r, c) + nw_corners = complex(_contract_corners((r,c), network, env)) + nw_full = complex(_contract_site_large((r,c), network, env)) + nw_0 = complex(_contract_edges_0((r,c), network, env)) + nw_60 = complex(_contract_edges_60((r,c), network, env)) + nw_120 = complex(_contract_edges_120((r,c), network, env)) + return (nw_full * nw_corners^2 / (nw_0 * nw_60 * nw_120))^(1 / 3) + end +end + +function _contract_edges_0((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[r,c][DL180 DL240 DL300; DL120 DL60 DL0] * network[r,_next(c,end)][DL0 DR240 DR300; DR120 DR60 DR0] * + env.C[1,_prev(r,end),c][χNW DL120; χNa] * env.C[2,_prev(r,end),_next(c+1,end)][χNb DR60; χNE] * env.C[3,r,_next(c+1,end)][χNE DR0; χSE] * + env.C[4,_next(r,end),_next(c,end)][χSE DR300; χSa] * env.C[5,_next(r,end),_prev(c,end)][χSb DL240; χSW] * env.C[6,r,_prev(c,end)][χSW DL180; χNW] * + env.Eb[1,_prev(r,end),_next(c,end)][χNa DL60; χNC] * env.Ea[1,_prev(r,end),_next(c,end)][χNC DR120; χNb] * + env.Eb[4,_next(r,end),c][χSa DR240; χSC] * env.Ea[4,_next(r,end),c][χSC DL300; χSb] +end + +function _contract_edges_60((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[r,c][DTR180 DBL60 DTR300; DTR120 DTR60 DTR0] * network[_next(r,end),_prev(c,end)][DBL180 DBL240 DBL300; DBL120 DBL60 DBL0] * + env.C[1,_prev(r,end),c][χNWb DTR120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTR60; χNE] * env.C[3,r,_next(c,end)][χNE DTR0; χSEa] * + env.C[4,_next(r+1,end),_prev(c,end)][χSEb DBL300; χS] * env.C[5,_next(r+1,end),_prev(c-1,end)][χS DBL240; χSW] * env.C[6,_next(r,end),_prev(c-1,end)][χSW DBL180; χNWa] * + env.Eb[3,_next(r,end),c][χSEa DTR300; χSEC] * env.Ea[3,_next(r,end),c][χSEC DBL0; χSEb] * + env.Eb[6,r,_prev(c,end)][χNWa DBL120; χNWC] * env.Ea[6,r,_prev(c,end)][χNWC DTR180; χNWb] +end + +function _contract_edges_120((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[r,c][DTL180 DTL240 DTL300; DTL120 DTL60 DTL0] * network[_next(r,end),c][DBR180 DBR240 DBR300; DTL300 DBR60 DBR0] * + env.C[1,_prev(r,end),c][χNW DTL120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTL60; χNEa] * env.C[3,_next(r,end),_next(c,end)][χNEb DBR0; χSE] * + env.C[4,_next(r+1,end),c][χSE DBR300; χS] * env.C[5,_next(r+1,end),_prev(c,end)][χS DBR240; χSWa] * env.C[6,r,_prev(c,end)][χSWb DTL180; χNW] * + env.Eb[2,r,_next(c,end)][χNEa DTL0; χNEC] * env.Ea[2,r,_next(c,end)][χNEC DBR60; χNEb] * + env.Eb[5,_next(r,end),_prev(c,end)][χSWa DBR180; χSWC] * env.Ea[5,_next(r,end),_prev(c,end)][χSWC DTL240; χSWb] +end + +function _contract_site_large((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[_prev(r,end),c][DNW180 DW60 DNW300; DNW120 DNW60 DNW0] * network[_prev(r,end),_next(c,end)][DNW0 DNE240 DNE300; DNE120 DNE60 DNE0] * + network[r,_next(c,end)][DE180 DE240 DE300; DNE300 DE60 DE0] * network[_next(r,end),c][DSE180 DSE240 DSE300; DSE120 DE240 DSE0] * network[_next(r,end),_prev(c,end)][DSW180 DSW240 DSW300; DSW120 DSW60 DSE180] * + network[r,_prev(c,end)][DW180 DW240 DSW120; DW120 DW60 DW0] * network[r,c][DW0 DSW60 DSE120; DNW300 DNE240 DE180] * + env.C[1,_prev(r-1,end),c][χNWa DNW120; χNb] * env.Eb[1,_prev(r-1,end),_next(c,end)][χNb DNW60; χNC] * env.Ea[1,_prev(r-1,end),_next(c,end)][χNC DNE120; χNa] * + env.C[2,_prev(r-1,end),_next(c+1,end)][χNa DNE60; χNEb] * env.Eb[2,_prev(r,end),_next(c+1,end)][χNEb DNE0; χNEC] * env.Ea[2,_prev(r,end),_next(c+1,end)][χNEC DE60; χNEa] * + env.C[3,r,_next(c+1,end)][χNEa DE0; χSEb] * env.Eb[3,_next(r,end),_next(c,end)][χSEb DE300; χSEC] * env.Ea[3,_next(r,end),_next(c,end)][χSEC DSE0; χSEa] * + env.C[4,_next(r+1,end),c][χSEa DSE300; χSb] * env.Eb[4,_next(r+1,end),_prev(c,end)][χSb DSE240; χSC] * env.Ea[4,_next(r+1,end),_prev(c,end)][χSC DSW300; χSa] * + env.C[5,_next(r+1,end),_prev(c-1,end)][χSa DSW240; χSWb] * env.Eb[5,_next(r,end),_prev(c-1,end)][χSWb DSW180; χSWC] * env.Ea[5,_next(r,end),_prev(c-1,end)][χSWC DW240; χSWa] * + env.C[6,r,_prev(c-1,end)][χSWa DW180; χNWb] * env.Eb[6,_prev(r,end),_prev(c,end)][χNWb DW120; χNWC] * env.Ea[6,_prev(r,end),_prev(c,end)][χNWC DNW180; χNWa] +end + +function _contract_corners((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true env.C[1,_prev(r,end),c][χNW D120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN D60; χNE] * env.C[3,r,_next(c,end)][χNE D0; χSE] * + env.C[4,_next(r,end),c][χSE D300; χS] * env.C[5,_next(r,end),_prev(c,end)][χS D240; χSW] * env.C[6,r,_prev(c,end)][χSW D180; χNW] * + network[r,c][D180 D240 D300; D120 D60 D0] +end + +### For + +function _contract_edges_0((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[r,c])[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r,_next(c,end)])[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(network[r,c])[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r,_next(c,end)])[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + env.C[1,_prev(r,end),c][χNW DLt120 DLb120; χNa] * env.C[2,_prev(r,end),_next(c+1,end)][χNb DRt60 DRb60; χNE] * env.C[3,r,_next(c+1,end)][χNE DRt0 DRb0; χSE] * + env.C[4,_next(r,end),_next(c,end)][χSE DRt300 DRb300; χSa] * env.C[5,_next(r,end),_prev(c,end)][χSb DLt240 DLb240; χSW] * env.C[6,r,_prev(c,end)][χSW DLt180 DLb180; χNW] * + env.Eb[1,_prev(r,end),_next(c,end)][χNa DLt60 DLb60; χNC] * env.Ea[1,_prev(r,end),_next(c,end)][χNC DRt120 DRb120; χNb] * + env.Eb[4,_next(r,end),c][χSa DRt240 DRb240; χSC] * env.Ea[4,_next(r,end),c][χSC DLt300 DLb300; χSb] +end + +function _contract_edges_0((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E,S,2,2}) where {P <: PEPSSandwichTriangular, E, S} + return @tensor opt = true ket(network[r,c])[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r,_next(c,end)])[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(network[r,c])[dLb; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r,_next(c,end)])[dRb; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + env.C[1,_prev(r,end),c][χNW DLt120 DLb120; χNa] * env.C[2,_prev(r,end),_next(c+1,end)][χNb DRt60 DRb60; χNE] * env.C[3,r,_next(c+1,end)][χNE DRt0 DRb0; χSE] * + env.C[4,_next(r,end),_next(c,end)][χSE DRt300 DRb300; χSa] * env.C[5,_next(r,end),_prev(c,end)][χSb DLt240 DLb240; χSW] * env.C[6,r,_prev(c,end)][χSW DLt180 DLb180; χNW] * + env.Eb[1,_prev(r,end),_next(c,end)][χNa DLt60 DLb60; χNC] * env.Ea[1,_prev(r,end),_next(c,end)][χNC DRt120 DRb120; χNb] * + env.Eb[4,_next(r,end),c][χSa DRt240 DRb240; χSC] * env.Ea[4,_next(r,end),c][χSC DLt300 DLb300; χSb] * + op[dLb dRb; dLt dRt] +end + +function _contract_edges_60((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[r,c])[dTR; DTRt120 DTRt60 DTRt0 DTRt300 DBLt60 DTRt180] * ket(network[_next(r,end),_prev(c,end)])[dBL; DBLt120 DBLt60 DBLt0 DBLt300 DBLt240 DBLt180] * + conj(bra(network[r,c])[dTR; DTRb120 DTRb60 DTRb0 DTRb300 DBLb60 DTRb180]) * conj(bra(network[_next(r,end),_prev(c,end)])[dBL; DBLb120 DBLb60 DBLb0 DBLb300 DBLb240 DBLb180]) * + env.C[1,_prev(r,end),c][χNWb DTRt120 DTRb120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTRt60 DTRb60; χNE] * env.C[3,r,_next(c,end)][χNE DTRt0 DTRb0; χSEa] * + env.C[4,_next(r+1,end),_prev(c,end)][χSEb DBLt300 DBLb300; χS] * env.C[5,_next(r+1,end),_prev(c-1,end)][χS DBLt240 DBLb240; χSW] * env.C[6,_next(r,end),_prev(c-1,end)][χSW DBLt180 DBLb180; χNWa] * + env.Eb[3,_next(r,end),c][χSEa DTRt300 DTRb300; χSEC] * env.Ea[3,_next(r,end),c][χSEC DBLt0 DBLb0; χSEb] * + env.Eb[6,r,_prev(c,end)][χNWa DBLt120 DBLb120; χNWC] * env.Ea[6,r,_prev(c,end)][χNWC DTRt180 DTRb180; χNWb] +end + +function _contract_edges_120((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[r,c])[dTL; DTLt120 DTLt60 DTLt0 DTLt300 DTLt240 DTLt180] * ket(network[_next(r,end),c])[dBR; DTLt300 DBRt60 DBRt0 DBRt300 DBRt240 DBRt180] * + conj(bra(network[r,c])[dTL; DTLb120 DTLb60 DTLb0 DTLb300 DTLb240 DTLb180]) * conj(bra(network[_next(r,end),c])[dBR; DTLb300 DBRb60 DBRb0 DBRb300 DBRb240 DBRb180]) * + env.C[1,_prev(r,end),c][χNW DTLt120 DTLb120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTLt60 DTLb60; χNEa] * env.C[3,_next(r,end),_next(c,end)][χNEb DBRt0 DBRb0; χSE] * + env.C[4,_next(r+1,end),c][χSE DBRt300 DBRb300; χS] * env.C[5,_next(r+1,end),_prev(c,end)][χS DBRt240 DBRb240; χSWa] * env.C[6,r,_prev(c,end)][χSWb DTLt180 DTLb180; χNW] * + env.Eb[2,r,_next(c,end)][χNEa DTLt0 DTLb0; χNEC] * env.Ea[2,r,_next(c,end)][χNEC DBRt60 DBRb60; χNEb] * + env.Eb[5,_next(r,end),_prev(c,end)][χSWa DBRt180 DBRb180; χSWC] * env.Ea[5,_next(r,end),_prev(c,end)][χSWC DTLt240 DTLb240; χSWb] +end + +function _contract_site_large((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[_prev(r,end),c])[dNW; DNWt120 DNWt60 DNWt0 DNWt300 DWt60 DNWt180] * ket(network[_prev(r,end),_next(c,end)])[dNE; DNEt120 DNEt60 DNEt0 DNEt300 DNEt240 DNWt0] * + ket(network[r,_next(c,end)])[dE; DNEt300 DEt60 DEt0 DEt300 DEt240 DEt180] * ket(network[_next(r,end),c])[dSE; DSEt120 DEt240 DSEt0 DSEt300 DSEt240 DSEt180] * ket(network[_next(r,end),_prev(c,end)])[dSW; DSWt120 DSWt60 DSEt180 DSWt300 DSWt240 DSWt180] * + ket(network[r,_prev(c,end)])[dW; DWt120 DWt60 DWt0 DSWt120 DWt240 DWt180] * ket(network[r,c])[dCenter; DNWt300 DNEt240 DEt180 DSEt120 DSWt60 DWt0] * + conj(bra(network[_prev(r,end),c])[dNW; DNWb120 DNWb60 DNWb0 DNWb300 DWb60 DNWb180]) * conj(bra(network[_prev(r,end),_next(c,end)])[dNE; DNEb120 DNEb60 DNEb0 DNEb300 DNEb240 DNWb0]) * + conj(bra(network[r,_next(c,end)])[dE; DNEb300 DEb60 DEb0 DEb300 DEb240 DEb180]) * conj(bra(network[_next(r,end),c])[dSE; DSEb120 DEb240 DSEb0 DSEb300 DSEb240 DSEb180]) * conj(bra(network[_next(r,end),_prev(c,end)])[dSW; DSWb120 DSWb60 DSEb180 DSWb300 DSWb240 DSWb180]) * + conj(bra(network[r,_prev(c,end)])[dW; DWb120 DWb60 DWb0 DSWb120 DWb240 DWb180]) * conj(bra(network[r,c])[dCenter; DNWb300 DNEb240 DEb180 DSEb120 DSWb60 DWb0]) * + env.C[1,_prev(r-1,end),c][χNWa DNWt120 DNWb120; χNb] * env.Eb[1,_prev(r-1,end),_next(c,end)][χNb DNWt60 DNWb60; χNC] * env.Ea[1,_prev(r-1,end),_next(c,end)][χNC DNEt120 DNEb120; χNa] * + env.C[2,_prev(r-1,end),_next(c+1,end)][χNa DNEt60 DNEb60; χNEb] * env.Eb[2,_prev(r,end),_next(c+1,end)][χNEb DNEt0 DNEb0; χNEC] * env.Ea[2,_prev(r,end),_next(c+1,end)][χNEC DEt60 DEb60; χNEa] * + env.C[3,r,_next(c+1,end)][χNEa DEt0 DEb0; χSEb] * env.Eb[3,_next(r,end),_next(c,end)][χSEb DEt300 DEb300; χSEC] * env.Ea[3,_next(r,end),_next(c,end)][χSEC DSEt0 DSEb0; χSEa] * + env.C[4,_next(r+1,end),c][χSEa DSEt300 DSEb300; χSb] * env.Eb[4,_next(r+1,end),_prev(c,end)][χSb DSEt240 DSEb240; χSC] * env.Ea[4,_next(r+1,end),_prev(c,end)][χSC DSWt300 DSWb300; χSa] * + env.C[5,_next(r+1,end),_prev(c-1,end)][χSa DSWt240 DSWb240; χSWb] * env.Eb[5,_next(r,end),_prev(c-1,end)][χSWb DSWt180 DSWb180; χSWC] * env.Ea[5,_next(r,end),_prev(c-1,end)][χSWC DWt240 DWb240; χSWa] * + env.C[6,r,_prev(c-1,end)][χSWa DWt180 DWb180; χNWb] * env.Eb[6,_prev(r,end),_prev(c,end)][χNWb DWt120 DWb120; χNWC] * env.Ea[6,_prev(r,end),_prev(c,end)][χNWC DNWt180 DNWb180; χNWa] +end + +function _contract_corners((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true env.C[1,_prev(r,end),c][χNW Dt120 Db120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN Dt60 Db60; χNE] * env.C[3,r,_next(c,end)][χNE Dt0 Db0; χSE] * + env.C[4,_next(r,end),c][χSE Dt300 Db300; χS] * env.C[5,_next(r,end),_prev(c,end)][χS Dt240 Db240; χSW] * env.C[6,r,_prev(c,end)][χSW Dt180 Db180; χNW] * + ket(network[r,c])[d; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r,c])[d; Db120 Db60 Db0 Db300 Db240 Db180]) +end + +function _contract_corners((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E,S,1,1}) where {P <: PEPSSandwichTriangular, E, S} + return @tensor opt = true env.C[1,_prev(r,end),c][χNW Dt120 Db120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN Dt60 Db60; χNE] * env.C[3,r,_next(c,end)][χNE Dt0 Db0; χSE] * + env.C[4,_next(r,end),c][χSE Dt300 Db300; χS] * env.C[5,_next(r,end),_prev(c,end)][χS Dt240 Db240; χSW] * env.C[6,r,_prev(c,end)][χSW Dt180 Db180; χNW] * + ket(network[r,c])[dt; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r,c])[db; Db120 Db60 Db0 Db300 Db240 Db180]) * op[db; dt] +end + +function energy(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, onesite_op, twosite_op) where {P <: PEPSSandwichTriangular} + return sum(Iterators.product(axes(network)...)) do (r, c) + expval_onesite = _contract_corners((r,c), network, env, onesite_op) / _contract_corners((r,c), network, env) + expval_twosite = _contract_edges_0((r,c), network, env, twosite_op) / _contract_edges_0((r,c), network, env) + return expval_onesite + expval_twosite + end +end diff --git a/src/algorithms/triangular_ctmrg/simultaneous.jl b/src/algorithms/triangular_ctmrg/simultaneous.jl deleted file mode 100644 index 4c3610bcd..000000000 --- a/src/algorithms/triangular_ctmrg/simultaneous.jl +++ /dev/null @@ -1,319 +0,0 @@ -struct SimultaneousCTMRGTria <: CTMRGTriaAlgorithm - tol::Float64 - maxiter::Int - miniter::Int - verbosity::Int - conditioning::Bool - projector_alg::Symbol - trunctype::Symbol -end -function SimultaneousCTMRGTria(; - tol = Defaults.ctmrg_tol, - maxiter = Defaults.ctmrg_maxiter, - miniter = Defaults.ctmrg_miniter, - verbosity = Defaults.ctmrg_verbosity, - conditioning = true, - projector_alg = :twothirds, - trunctype = :truncrank - ) - return SimultaneousCTMRGTria(tol, maxiter, miniter, verbosity, conditioning, projector_alg, trunctype) -end - -# Based on -# https://arxiv.org/pdf/2510.04907 - -function ctmrg_iteration(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv, alg::SimultaneousCTMRGTria) - trunc_type = getfield(@__MODULE__, alg.trunctype) - trunc = trunc_type(dim(domain(env.C[1, 1, 1])[1])) - Pas, Pbs, S = calculate_projectors(network, env, trunc, alg.projector_alg) - - renormalize_corners!(network, env, Pas, Pbs) - normalize_corners!(env) - - Ẽas, Ẽbs, Ẽastr, Ẽbstr = semi_renormalize(network, env, Pas, Pbs, trunc) - Qas, Qbs = build_matrix_second_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; alg.conditioning) - - env = renormalize_edges(env, Ẽas, Ẽbs, Qas, Qbs) - normalize_edges!(env) - return env, S -end - -function calculate_projectors(network, env, trunc, projector_alg) - if projector_alg == :full - return calculate_full_projectors(network, env, trunc) - elseif projector_alg == :twothirds - return calculate_twothirds_projectors(network, env, trunc) - else - @error "projector_alg = $projector_alg not defined" - end -end - -function calculate_twothirds_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 1, 1}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) - ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) - ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) - ρρ = ρL * ρR - ρρ /= norm(ρρ) - - U, S, V = svd_trunc(ρρ; trunc = trunc & trunctol(; atol = 1.0e-20)) - - Pb = ρR * V' * sdiag_pow(S, -1 / 2) - Pa = sdiag_pow(S, -1 / 2) * U' * ρL - return Pa, Pb, S - end - return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) -end - -function calculate_full_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 1, 1}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) - ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) - ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) - ρ̄ = build_double_corner_matrix_triangular(network, env, mod1(dir + 3, 6), r, c) - ρ̄ /= norm(ρ̄) - Ū, S̄, V̄ᴴ = svd_trunc(ρ̄; trunc = trunctol(; atol = 1.0e-20)) - ρ̄ᴿ = Ū * sqrt(S̄) - ρ̄ᴸ = sqrt(S̄) * V̄ᴴ - ρρ = ρ̄ᴸ * ρL * ρR * ρ̄ᴿ - ρρ /= norm(ρρ) - - U, S, Vᴴ = svd_trunc(ρρ; trunc = trunctol(; atol = 1.0e-20)) - U, S, Vᴴ = svd_trunc(ρρ; trunc) - U, S, Vᴴ = svd_trunc(ρρ; trunc = trunc & trunctol(; atol = 1.0e-20)) - - Pb = ρR * ρ̄ᴿ * Vᴴ' * sdiag_pow(S, -1 / 2) - Pa = sdiag_pow(S, -1 / 2) * U' * ρ̄ᴸ * ρL - - return Pa, Pb, S - end - return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) -end - -function renormalize_corners!(network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PEPSTriaSandwich} - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1} - new_corners′ = similar(coordinates, T_proj) - new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) - @tensor opt = true env_C_new[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2 11; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7 12; 8] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * - Pas[mod1(dir - 1, 6), r, c][-1; 4 5 13] * Pbs[dir, r, c][8 9 14; -4] - return env_C_new - end - return new_corners -end - -function renormalize_corners!(network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PFTriaTensor} - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = AbstractTensorMap{E, S, 2, 1} - new_corners′ = similar(coordinates, T_proj) - new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) - @tensor opt = true env_C_new[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7; 8] * - rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] - return env_C_new - end - return new_corners -end - -function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, dir::Int, r::Int, c::Int) where {P <: PFTriaTensor} - @tensor opt = true mat[-1 -2; -3 -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3; 2] * - env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4; -3] * rotl60(network[r, c], mod(dir - 1, 6))[7 -2 -4; 5 3 4] - return mat -end - -function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, dir::Int, r::Int, c::Int) where {P <: PEPSTriaSandwich} - @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5 8; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3 9; 2] * - env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7 10; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4 11; -4] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 5 3 4 -5 -2 7] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 8 9 11 -6 -3 10]) - return mat -end - -function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Pas, Pbs, dir, r, c) where {P <: PEPSTriaSandwich} - @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := Pas[dir, r, c][-1; 1 2 8] * Pbs[dir, r, c][6 7 9; -4] * - env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5 11; 6] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 3 5 7 -5 -2 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 10 11 9 -6 -3 8]) - return mat / norm(mat) -end - -function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Pas, Pbs, dir, r, c) where {P <: PFTriaTensor} - @tensor opt = true mat[-1 -2; -3 -4] := Pas[dir, r, c][-1; 1 2] * Pbs[dir, r, c][6 7; -3] * - env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5; 6] * rotl60(network[r, c], mod(dir - 1, 6))[2 -2 -4; 3 5 7] - return mat / norm(mat) -end - -function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 2}} - return permute(t, ((1, 3), (2,))) -end - -function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 3}} - return permute(t, ((1, 3, 4), (2,))) -end - -function semi_renormalize(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv, Pas, Pbs, trunc) - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) - mat = semi_renormalize_edge(network, env, Pas, Pbs, dir, r, c) - - U, S, V = svd_trunc(mat; trunc = trunctol(; atol = 1.0e-20)) - - Ẽb = U * sqrt(S) - Ẽa = _permute_edge(sqrt(S) * V) - - Utr, Str, Vtr = svd_trunc(mat; trunc = trunc & trunctol(; atol = 1.0e-20)) - Ẽbtr = Utr * sqrt(Str) - Ẽatr = _permute_edge(sqrt(Str) * Vtr) - - return Ẽa, Ẽb, Ẽatr, Ẽbtr - end - return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3), getindex.(projectors, 4) -end - -function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PEPSTriaSandwich} - @tensor opt = true σL[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2 10; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3 11; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5 12; 4] * - Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9 13; -4] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7 14; 6] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[15 2 9 -2 7 5 3] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 13 -3 14 12 11]) - @tensor opt = true σR[-1; -2 -3 -4] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2 10; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3 11; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5 12; 6] * - Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7 13; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9 14; 8] * - rotl60(ket(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 9 2 3 5 7 -3] * conj(rotl60(bra(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 14 10 11 12 13 -4]) - return σL, σR -end - -function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PFTriaTensor} - @tensor opt = true σL[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5; 4] * - Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9; -3] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7; 6] * - rotl60(network[r, c], mod(dir - 1, 6))[3 5 7; 2 9 -2] - @tensor opt = true σR[-1; -2 -3] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5; 6] * - Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9; 8] * - rotl60(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...], mod(dir - 1, 6))[-3 7 5; 9 2 3] - return σL, σR -end - -function build_matrix_second_projectors(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; conditioning = true) - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 1, 1}, AbstractTensorMap{E, S, 1, 1}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) - σL, σR = build_halfinfinite_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) - if conditioning - σL /= norm(σL) - σR /= norm(σR) - UL, SL, VLᴴ = svd_trunc(σL; trunc = trunctol(; atol = 1.0e-20)) - UR, SR, VRᴴ = svd_trunc(σR; trunc = trunctol(; atol = 1.0e-20)) - - FLU = sqrt(SL) * VLᴴ - FRU = UR * sqrt(SR) - - mat = FLU * FRU - mat /= norm(mat) - WU, SU, QUᴴ = svd_trunc(mat; trunc = trunc & trunctol(; atol = 1.0e-20)) - - Qa = sdiag_pow(SU, -1 / 2) * WU' * FLU - Qb = FRU * QUᴴ' * sdiag_pow(SU, -1 / 2) - else - mat = σL * σR - mat /= norm(mat) - U, S, V = svd_trunc(mat; trunc = trunc & trunctol(; atol = 1.0e-20)) - Qa = sdiag_pow(S, -1 / 2) * U' * σL - Qb = σR * V' * sdiag_pow(S, -1 / 2) - end - return Qa, Qb - end - return getindex.(projectors, 1), getindex.(projectors, 2) -end - -function renormalize_edges(env::CTMRGTriaEnv, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} - coordinates = collect(Iterators.product(axes(env.Ea)...)) - T_proj = Tuple{AbstractTensorMap{E, S, 3, 1}, AbstractTensorMap{E, S, 3, 1}} - new_edges′ = similar(env.Ea, T_proj) - new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) - @tensor Eb_new[-1 -2 -3; -4] := Ẽbs[dir, r, c][-1 -2 -3; 1] * Qbs[dir, r, c][1; -4] - @tensor Ea_new[-1 -2 -3; -4] := Qas[dir, r, c][-1; 1] * Ẽas[dir, r, c][1 -2 -3; -4] - return Ea_new, Eb_new - end - return CTMRGTriaEnv(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) -end - -function renormalize_edges(env::CTMRGTriaEnv, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} - coordinates = collect(Iterators.product(axes(env.Ea)...)) - T_proj = Tuple{AbstractTensorMap{E, S, 2, 1}, AbstractTensorMap{E, S, 2, 1}} - new_edges′ = similar(env.Ea, T_proj) - new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) - @tensor Eb_new[-1 -2; -3] := Ẽbs[dir, r, c][-1 -2; 1] * Qbs[dir, r, c][1; -3] - @tensor Ea_new[-1 -2; -3] := Qas[dir, r, c][-1; 1] * Ẽas[dir, r, c][1 -2; -3] - return Ea_new, Eb_new - end - return CTMRGTriaEnv(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) -end - -function normalize_corners!(env) - coordinates = collect(Iterators.product(axes(env.Ea)...)) - new_env′ = similar(env.C, typeof(env.C[1, 1, 1])) - new_env = dtmap!!(new_env′, coordinates) do (dir, r, c) - env_C_new = env.C[dir, r, c] / norm(env.C[dir, r, c]) - return env_C_new - end - return new_env -end - -function normalize_edges!(env) - for dir in 1:6 - env.Ea[dir] /= norm(env.Ea[dir]) - env.Eb[dir] /= norm(env.Eb[dir]) - end - return env -end - -function calculate_error(Ss, Ss_prev) - ε = Inf - for (S, S_prev) in zip(Ss, Ss_prev) - if space(S) == space(S_prev) - ε = norm(S^4 - S_prev^4) - else - return Inf - end - end - return ε -end - -# Expectation_values - -function network_value(network::InfiniteTriangularNetwork, env::CTMRGTriaEnv) - return zero(scalartype(network)) -end - -function energy(network::InfiniteTriangularNetwork{P}, env::CTMRGTriaEnv, H) where {P <: PEPSTriaSandwich} - (r, c) = (1, 1) - numerator = @tensor opt = true ket(network[r, c])[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, c])[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * - conj(bra(network[r, c])[dLb DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, c])[dRb DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * - env.C[1][χNW DLt120 DLb120; χNa] * env.C[2][χNb DRt60 DRb60; χNE] * env.C[3][χNE DRt0 DRb0; χSE] * - env.C[4][χSE DRt300 DRb300; χSa] * env.C[5][χSb DLt240 DLb240; χSW] * env.C[6][χSW DLt180 DLb180; χNW] * - env.Eb[1][χNa DLt60 DLb60; χNC] * env.Ea[1][χNC DRt120 DRb120; χNb] * - env.Eb[4][χSa DRt240 DRb240; χSC] * env.Ea[4][χSC DLt300 DLb300; χSb] * - H[dLb dRb; dLt dRt] - - denumerator = @tensor opt = true ket(network[r, c])[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, c])[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * - conj(bra(network[r, c])[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, c])[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * - env.C[1][χNW DLt120 DLb120; χNa] * env.C[2][χNb DRt60 DRb60; χNE] * env.C[3][χNE DRt0 DRb0; χSE] * - env.C[4][χSE DRt300 DRb300; χSa] * env.C[5][χSb DLt240 DLb240; χSW] * env.C[6][χSW DLt180 DLb180; χNW] * - env.Eb[1][χNa DLt60 DLb60; χNC] * env.Ea[1][χNC DRt120 DRb120; χNb] * - env.Eb[4][χSa DRt240 DRb240; χSC] * env.Ea[4][χSC DLt300 DLb300; χSb] - return numerator / denumerator -end diff --git a/src/environments/ctmrg_tria_environments.jl b/src/environments/ctmrg_environments_triangular.jl similarity index 92% rename from src/environments/ctmrg_tria_environments.jl rename to src/environments/ctmrg_environments_triangular.jl index 6d961100a..433cc7b47 100644 --- a/src/environments/ctmrg_tria_environments.jl +++ b/src/environments/ctmrg_environments_triangular.jl @@ -1,5 +1,5 @@ #TODO: Add docs and figure -struct CTMRGTriaEnv{T} +struct CTMRGEnvTriangular{T} "6 x rows x cols array of corner C tensors, where the first dimension specifies the spatial direction" C::Array{T, 3} "6 x rows x cols array of edge Ta tensors, where the first dimension specifies the spatial direction" @@ -7,9 +7,9 @@ struct CTMRGTriaEnv{T} "6 x rows x cols array of edge Ta tensors, where the first dimension specifies the spatial direction" Eb::Array{T, 3} end -# function CTMRGTriaEnv(corners::Array{C, 3}, edges::Array{T, 3}) where {C, T} +# function CTMRGEnvTriangular(corners::Array{C, 3}, edges::Array{T, 3}) where {C, T} # foreach(check_environment_virtualspace, edges) -# return CTMRGTriaEnv{C, T}(corners, edges) +# return CTMRGEnvTriangular{C, T}(corners, edges) # end # """ @@ -42,7 +42,7 @@ function get_Ds(D::A) where {A <: ElementarySpace} return [dir > 3 ? D' : D for dir in 1:6] end -function CTMRGTriaEnv( +function CTMRGEnvTriangular( f, T, D::Union{A, B}, chis::B; unitcell::Tuple{Int, Int} = (1, 1) ) where { A <: ProductSpace, B <: ElementarySpace, @@ -73,5 +73,5 @@ function CTMRGTriaEnv( Eas[dir, r, c] = Ea Ebs[dir, r, c] = Eb end - return CTMRGTriaEnv(Cs, Eas, Ebs) + return CTMRGEnvTriangular(Cs, Eas, Ebs) end diff --git a/src/networks/infinitetriangularnetwork.jl b/src/networks/infinitetriangularnetwork.jl index 2ffa61ab0..953f595dd 100644 --- a/src/networks/infinitetriangularnetwork.jl +++ b/src/networks/infinitetriangularnetwork.jl @@ -1,4 +1,4 @@ -const PEPSTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} +const PEPSTensorTriangular{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} """ $(TYPEDEF) diff --git a/src/networks/local_triangular_sandwich.jl b/src/networks/local_triangular_sandwich.jl index 380fa18e9..a5fe0ab99 100644 --- a/src/networks/local_triangular_sandwich.jl +++ b/src/networks/local_triangular_sandwich.jl @@ -19,25 +19,25 @@ _rotr60_localsandwich(O) = rotr60.(O) ## PartitionFunction # specialized local rotation interface -_rotl60_localsandwich(O::PFTriaTensor) = rotl60(O) -_rotr60_localsandwich(O::PFTriaTensor) = rotr60(O) -_rot180_localsandwich(O::PFTriaTensor) = rot180(O) +_rotl60_localsandwich(O::PFTensorTriangular) = rotl60(O) +_rotr60_localsandwich(O::PFTensorTriangular) = rotr60(O) +_rot180_localsandwich(O::PFTensorTriangular) = rot180(O) # specialized local math interface -_add_localsandwich(O1::PFTriaTensor, O2::PFTriaTensor) = O1 + O2 -_subtract_localsandwich(O1::PFTriaTensor, O2::PFTriaTensor) = O1 - O2 -_mul_localsandwich(α::Number, O::PFTriaTensor) = α * O -_isapprox_localsandwich(O1::PFTriaTensor, O2::PFTriaTensor; kwargs...) = isapprox(O1, O2; kwargs...) +_add_localsandwich(O1::PFTensorTriangular, O2::PFTensorTriangular) = O1 + O2 +_subtract_localsandwich(O1::PFTensorTriangular, O2::PFTensorTriangular) = O1 - O2 +_mul_localsandwich(α::Number, O::PFTensorTriangular) = α * O +_isapprox_localsandwich(O1::PFTensorTriangular, O2::PFTensorTriangular; kwargs...) = isapprox(O1, O2; kwargs...) ## PEPS -const PEPSTriaSandwich{T <: PEPSTriaTensor} = Tuple{T, T} +const PEPSSandwichTriangular{T <: PEPSTensorTriangular} = Tuple{T, T} -ket(O::PEPSTriaSandwich) = O[1] -bra(O::PEPSTriaSandwich) = O[2] +ket(O::PEPSSandwichTriangular) = O[1] +bra(O::PEPSSandwichTriangular) = O[2] -function virtualspace(O::PEPSTriaSandwich, dir) +function virtualspace(O::PEPSSandwichTriangular, dir) return virtualspace(ket(O), dir) ⊗ virtualspace(bra(O), dir)' end -TensorKit.spacetype(::Type{P}) where {P <: PEPSTriaSandwich} = spacetype(eltype(P)) +TensorKit.spacetype(::Type{P}) where {P <: PEPSSandwichTriangular} = spacetype(eltype(P)) diff --git a/src/networks/tensors_tria.jl b/src/networks/tensors_triangular.jl similarity index 75% rename from src/networks/tensors_tria.jl rename to src/networks/tensors_triangular.jl index cca27ff1f..00c60de23 100644 --- a/src/networks/tensors_tria.jl +++ b/src/networks/tensors_triangular.jl @@ -3,7 +3,7 @@ # """ - const PartitionFunctionTriaTensor{S} + const PartitionFunctionTensorTriangular{S} Default type for partition function tensors with 6 virtual indices, conventionally ordered as: ``T : W ⊗ SW ⊗ SE ← NW ⊗ NE ⊗ E``. Here ``NW``, ``NE``, ``E``, ``SE``, ``SW`` and ``W`` denote the @@ -22,19 +22,19 @@ where the angles denote the directions of the legs with respect to the positive SW P SE ``` """ -const PartitionFunctionTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 3, 3} -const PFTriaTensor = PartitionFunctionTriaTensor +const PartitionFunctionTensorTriangular{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 3, 3} +const PFTensorTriangular = PartitionFunctionTensorTriangular """ - PartitionFunctionTriaTensor(f, ::Type{T}, NWspace::S, + PartitionFunctionTensorTriangular(f, ::Type{T}, NWspace::S, [NEspace::S], [Espace::S], [SEspace::S], [SWspace::S], [Wspace::S]) where {T,S<:Union{Int,ElementarySpace}} -Construct a PartitionFunctionTriaTensor tensor based on the +Construct a PartitionFunctionTensorTriangular tensor based on the north-west, north-east, east, south-east, south-west and west spaces The tensor elements are generated based on `f` and the element type is specified in `T`. """ -function PartitionFunctionTriaTensor( +function PartitionFunctionTensorTriangular( f, ::Type{T}, NWspace::S, NEspace::S = NWspace, Espace::S = NWspace, SEspace::S = NWspace, SWspace::S = NWspace, Wspace::S = NWspace, @@ -42,7 +42,7 @@ function PartitionFunctionTriaTensor( return f(T, Wspace ⊗ SWspace ⊗ SEspace ← NWspace ⊗ NEspace ⊗ Espace) end -function virtualspace(t::PFTriaTensor, dir) +function virtualspace(t::PFTensorTriangular, dir) invp = (4, 5, 6, 3, 2, 1) # internally, virtual directions are ordered as NW, NE, E, SE, SW... return space(t, invp[dir]) end @@ -52,7 +52,7 @@ end # """ - const PEPSTriaTensor{S} + const PEPSTensorTriangular{S} Default type for PEPS tensors with a single physical index, and 6 virtual indices, conventionally ordered as: ``T : P ← NW ⊗ NE ⊗ E ⊗ SE ⊗ SW ⊗ W``. Here, ``P`` denotes the physical space @@ -74,7 +74,7 @@ where the angles denote the directions of the legs with respect to the positive ``` ``` """ -const PEPSTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} +const PEPSTensorTriangular{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} """ PEPSTensor(f, ::Type{T}, Pspace::S, NWspace::S, @@ -84,7 +84,7 @@ const PEPSTriaTensor{S <: ElementarySpace} = AbstractTensorMap{<:Any, S, 1, 6} Construct a PEPS tensor based on the physical, north-west, north-east, east, south-east, south-west and west spaces. The tensor elements are generated based on `f` and the element type is specified in `T`. """ -function PEPSTriaTensor( +function PEPSTensorTriangular( f, ::Type{T}, Pspace::S, NWspace::S, NEspace::S = NWspace, Espace::S = NWspace, SEspace::S = NWspace, @@ -93,9 +93,9 @@ function PEPSTriaTensor( return f(T, Pspace ← NWspace ⊗ NEspace ⊗ Espace ⊗ SEspace ⊗ SWspace ⊗ Wspace) end -rotl60(t::PEPSTriaTensor) = permute(t, ((1,), (3, 4, 5, 6, 7, 2))) -rotr60(t::PEPSTriaTensor) = permute(t, ((1,), (7, 2, 3, 4, 5, 6))) -Base.rot180(t::PEPSTriaTensor) = permute(t, ((1,), (5, 6, 7, 2, 3, 4))) +rotl60(t::PEPSTensorTriangular) = permute(t, ((1,), (3, 4, 5, 6, 7, 2))) +rotr60(t::PEPSTensorTriangular) = permute(t, ((1,), (7, 2, 3, 4, 5, 6))) +Base.rot180(t::PEPSTensorTriangular) = permute(t, ((1,), (5, 6, 7, 2, 3, 4))) -physicalspace(t::PEPSTriaTensor) = space(t, 1) -virtualspace(t::PEPSTriaTensor, dir) = space(t, dir + 1) +physicalspace(t::PEPSTensorTriangular) = space(t, 1) +virtualspace(t::PEPSTensorTriangular, dir) = space(t, dir + 1) diff --git a/src/states/infinitepepstriangular.jl b/src/states/infinitepepstriangular.jl new file mode 100644 index 000000000..07ab09a08 --- /dev/null +++ b/src/states/infinitepepstriangular.jl @@ -0,0 +1,268 @@ +""" + struct InfinitePEPSTriangular{T<:PEPSTensorTriangularTriangular} + +Represents an infinite projected entangled-pair state on a 2D triangular lattice. + +## Fields + +$(TYPEDFIELDS) +""" +struct InfinitePEPSTriangular{T <: PEPSTensorTriangular} + A::Matrix{T} + InfinitePEPSTriangular{T}(A::Matrix{T}) where {T <: PEPSTensorTriangular} = new{T}(A) + function InfinitePEPSTriangular(A::Array{T, 2}) where {T <: PEPSTensorTriangular} + bosonic_braiding = BraidingStyle(sectortype(T)) === Bosonic() + # for (d, w) in Tuple.(CartesianIndices(A)) + # (bosonic_braiding || !isdual(physicalspace(A[d, w]))) || + # throw(ArgumentError("Dual physical spaces for symmetry sectors with non-trivial twists are not allowed (for now).")) + # north_virtualspace(A[d, w]) == south_virtualspace(A[_prev(d, end), w])' || + # throw( + # SpaceMismatch("North virtual space at site $((d, w)) does not match.") + # ) + # east_virtualspace(A[d, w]) == west_virtualspace(A[d, _next(w, end)])' || + # throw(SpaceMismatch("East virtual space at site $((d, w)) does not match.")) + # dim(space(A[d, w])) > 0 || @warn "no fusion channels at site ($d, $w)" + # end + return new{T}(A) + end +end + +## Constructors + +""" + InfinitePEPSTriangular(A::AbstractMatrix{T}) + +Create an `InfinitePEPSTriangular` by specifying a matrix containing the PEPS tensors at each site in +the unit cell. +""" +function InfinitePEPSTriangular(A::AbstractMatrix{<:PEPSTensorTriangular}) + return InfinitePEPSTriangular(Array(deepcopy(A))) # TODO: find better way to copy +end + +""" + InfinitePEPSTriangular([f=randn, T=ComplexF64,] Pspaces::A, Nspaces::A, [Espaces::A]) where {A<:AbstractMatrix{ElementarySpace}} + +Create an `InfinitePEPSTriangular` by specifying the physical, north virtual and east virtual spaces +of the PEPS tensor at each site in the unit cell as a matrix. +""" +function InfinitePEPSTriangular( + f, T::Type{<:Number}, Pspaces::M, NWspaces::M, NEspaces::M = NWspaces, Espaces::M = NWspaces + ) where {M <: AbstractMatrix{<:ElementarySpace}} + size(Pspaces) == size(NEspaces) == size(NEspaces) == size(Espaces) || + throw(ArgumentError("Input spaces should have equal sizes.")) + + SEspaces = adjoint.(circshift(NWspaces, (-1, 0))) + SWspaces = adjoint.(NEspaces) + Wspaces = adjoint.(circshift(Espaces, (0, 1))) + + A = map(Pspaces, NWspaces, NEspaces, Espaces, SEspaces, SWspaces, Wspaces) do NW, NE, E, SE, SW, W + return PEPSTensorTriangular(f, T, P, NW, NE, E, SE, SW, W) + end + + return InfinitePEPSTriangular(A) +end +function InfinitePEPSTriangular( + Pspaces::A, virtual_spaces...; kwargs... + ) where {A <: Union{AbstractMatrix{<:ElementarySpace}, ElementarySpace}} + return InfinitePEPSTriangular(randn, ComplexF64, Pspaces, virtual_spaces...; kwargs...) +end + +""" + InfinitePEPSTriangular(A::PEPSTensorTriangular; unitcell=(1, 1)) + +Create an `InfinitePEPSTriangular` by specifying a tensor and unit cell. + +The unit cell is labeled as a matrix which means that any tensor in the unit cell, +regardless if PEPS tensor or environment tensor, is obtained by shifting the row +and column index `[r, c]` by one, respectively: +``` + | | | +---C[r-1,c-1]---T[r-1,c]---T[r-1,c+1]--- + | || || +---T[r,c-1]=====AA[r,c]====AA[r,c+1]==== + | || || +---T[r+1,c-1]===AA[r+1,c]==AA[r+1,c+1]== + | || || +``` +The unit cell has periodic boundary conditions, so `[r, c]` is indexed modulo the +size of the unit cell. +""" +function InfinitePEPSTriangular(A::T; unitcell::Tuple{Int, Int} = (1, 1)) where {T <: PEPSTensorTriangular} + return InfinitePEPSTriangular(fill(A, unitcell)) +end + +# expand PEPS spaces to unit cell size +function _fill_state_virtual_spaces_triangular( + NWspace::S, NEspace::S = NWspace, Espace::S = NWspace; unitcell::Tuple{Int, Int} = (1, 1) + ) where {S <: ElementarySpace} + return fill(NWspace, unitcell), fill(NEspace, unitcell), fill(Espace, unitcell) +end + +""" + InfinitePEPSTriangular([f=randn, T=ComplexF64,] Pspace, Nspace, [Espace]; unitcell=(1,1)) + +Create an InfinitePEPSTriangular by specifying its physical, north and east spaces and unit cell. +""" +function InfinitePEPSTriangular( + f, T::Type{<:Number}, Pspace::S, vspaces...; unitcell::Tuple{Int, Int} = (1, 1) + ) where {S <: ElementarySpace} + return InfinitePEPSTriangular( + f, T, + _fill_state_physical_spaces(Pspace; unitcell), + _fill_state_virtual_spaces_triangular(vspaces...; unitcell)..., + ) +end + +## Unit cell interface + +unitcell(t::InfinitePEPSTriangular) = t.A +Base.size(A::InfinitePEPSTriangular, args...) = size(unitcell(A), args...) +Base.length(A::InfinitePEPSTriangular) = length(unitcell(A)) +Base.eltype(::Type{InfinitePEPSTriangular{T}}) where {T} = T +Base.eltype(A::InfinitePEPSTriangular) = eltype(typeof(A)) + +Base.copy(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(copy(unitcell(A))) +function Base.similar(A::InfinitePEPSTriangular, T::Type{TorA} = scalartype(A)) where {TorA} + return InfinitePEPSTriangular(map(t -> similar(t, T), unitcell(A))) +end +Base.repeat(A::InfinitePEPSTriangular, counts...) = InfinitePEPSTriangular(repeat(unitcell(A), counts...)) + +Base.getindex(A::InfinitePEPSTriangular, args...) = Base.getindex(unitcell(A), args...) +Base.setindex!(A::InfinitePEPSTriangular, args...) = (Base.setindex!(unitcell(A), args...); A) +Base.axes(A::InfinitePEPSTriangular, args...) = axes(unitcell(A), args...) +eachcoordinate(A::InfinitePEPSTriangular) = collect(Iterators.product(axes(A)...)) +function eachcoordinate(A::InfinitePEPSTriangular, dirs) + return collect(Iterators.product(dirs, axes(A, 1), axes(A, 2))) +end + +## Spaces + +TensorKit.spacetype(::Type{T}) where {T <: InfinitePEPSTriangular} = spacetype(eltype(T)) +virtualspace(n::InfinitePEPSTriangular, dir) = virtualspace.(unitcell(n), dir) +function virtualspace(n::InfinitePEPSTriangular, r::Int, c::Int, dir) + Nr, Nc = size(n) + return virtualspace(n[mod1(r, Nr), mod1(c, Nc)], dir) +end +physicalspace(n::InfinitePEPSTriangular) = physicalspace.(unitcell(n)) +function physicalspace(n::InfinitePEPSTriangular, r::Int, c::Int) + Nr, Nc = size(n) + return physicalspace(n[mod1(r, Nr), mod1(c, Nc)]) +end + +## InfiniteTriangularNetwork interface + +function InfiniteTriangularNetwork(top::InfinitePEPSTriangular, bot::InfinitePEPSTriangular = top) + size(top) == size(bot) || throw( + ArgumentError("Top PEPS, bottom PEPS and PEPO rows should have the same length") + ) + return InfiniteTriangularNetwork(map(tuple, unitcell(top), unitcell(bot))) +end + +## Vector interface + +VI.scalartype(::Type{NT}) where {NT <: InfinitePEPSTriangular} = scalartype(eltype(NT)) +VI.zerovector(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(zerovector(unitcell(A))) + +function VI.scale(ψ::InfinitePEPSTriangular, α::Number) + _scale = Base.Fix2(scale, α) + return InfinitePEPSTriangular(map(_scale, unitcell(ψ))) +end +function VI.scale!(ψ::InfinitePEPSTriangular, α::Number) + _scale! = Base.Fix2(scale!, α) + foreach(_scale!, unitcell(ψ)) + return ψ +end +function VI.scale!(ψ₁::InfinitePEPSTriangular, ψ₂::InfinitePEPSTriangular, α::Number) + _scale!(x, y) = scale!(x, y, α) + foreach(_scale!, unitcell(ψ₁), unitcell(ψ₂)) + return ψ₁ +end +VI.scale!!(ψ::InfinitePEPSTriangular, α::Number) = scale!(ψ, α) +VI.scale!!(ψ₁::InfinitePEPSTriangular, ψ₂::InfinitePEPSTriangular, α::Number) = scale!(ψ₁, ψ₂, α) + +function VI.add(ψ₁::InfinitePEPSTriangular, ψ₂::InfinitePEPSTriangular, α::Number, β::Number) + _add(x, y) = add(x, y, α, β) + return InfinitePEPSTriangular(map(_add, unitcell(ψ₁), unitcell(ψ₂))) +end +function VI.add!(ψ₁::InfinitePEPSTriangular, ψ₂::InfinitePEPSTriangular, α::Number, β::Number) + _add!(x, y) = add!(x, y, α, β) + foreach(_add!, unitcell(ψ₁), unitcell(ψ₂)) + return ψ₁ +end +VI.add!!(ψ₁::InfinitePEPSTriangular, ψ₂::InfinitePEPSTriangular, α::Number, β::Number) = add!(ψ₁, ψ₂, α, β) + +## Math + +function Base.:+(A₁::InfinitePEPSTriangular, A₂::InfinitePEPSTriangular) + return InfinitePEPSTriangular(unitcell(A₁) + unitcell(A₂)) +end +function Base.:-(A₁::InfinitePEPSTriangular, A₂::InfinitePEPSTriangular) + return InfinitePEPSTriangular(unitcell(A₁) - unitcell(A₂)) +end +Base.:*(α::Number, A::InfinitePEPSTriangular) = InfinitePEPSTriangular(α * unitcell(A)) +Base.:*(A::InfinitePEPSTriangular, α::Number) = α * A +Base.:/(A::InfinitePEPSTriangular, α::Number) = InfinitePEPSTriangular(unitcell(A) / α) +LinearAlgebra.dot(A₁::InfinitePEPSTriangular, A₂::InfinitePEPSTriangular) = dot(unitcell(A₁), unitcell(A₂)) +LinearAlgebra.norm(A::InfinitePEPSTriangular) = norm(unitcell(A)) + +## (Approximate) equality +function Base.:(==)(A₁::InfinitePEPSTriangular, A₂::InfinitePEPSTriangular) + return all(zip(unitcell(A₁), unitcell(A₂))) do (p₁, p₂) + return p₁ == p₂ + end +end +function Base.isapprox(A₁::InfinitePEPSTriangular, A₂::InfinitePEPSTriangular; kwargs...) + return all(zip(unitcell(A₁), unitcell(A₂))) do (p₁, p₂) + return isapprox(p₁, p₂; kwargs...) + end +end + +## Rotations + +# rotl60(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(rotl60(_rotl60_localsandwich.(unitcell(A)))) +# rotr60(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(rotr60(_rotl60_localsandwich.(unitcell(A)))) +# Base.rot180(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(rot180(rot180.(unitcell(A)))) + +## FiniteDifferences vectorization + +""" + to_vec(A::InfinitePEPSTriangular) -> vec, state_from_vec + +Vectorize an `InfinitePEPSTriangular` into a vector of real numbers. A vectorized infinite PEPS can +retrieved again as an `InfinitePEPSTriangular` by application of the `state_from_vec` map. +""" +function FiniteDifferences.to_vec(A::InfinitePEPSTriangular) + vec, back = FiniteDifferences.to_vec(unitcell(A)) + function state_from_vec(vec) + return NWType(back(vec)) + end + return vec, state_from_vec +end + +## Chainrules + +function ChainRulesCore.rrule(::typeof(Base.getindex), network::InfinitePEPSTriangular, args...) + tensor = network[args...] + + function getindex_pullback(Δtensor_) + Δtensor = unthunk(Δtensor_) + Δnetwork = zerovector(network) + Δnetwork[args...] = Δtensor + return NoTangent(), Δnetwork, NoTangent(), NoTangent() + end + return tensor, getindex_pullback +end + +function ChainRulesCore.rrule( + ::Type{InfiniteTriangularNetwork}, top::InfinitePEPSTriangular, bot::InfinitePEPSTriangular + ) + network = InfiniteTriangularNetwork(top, bot) + + function InfiniteTriangularNetwork_pullback(Δnetwork_) + Δnetwork = unthunk(Δnetwork_) + Δtop = InfinitePEPSTriangular(map(ket, unitcell(Δnetwork))) + Δbot = InfinitePEPSTriangular(map(bra, unitcell(Δnetwork))) + return NoTangent(), Δtop, Δbot + end + return network, InfiniteTriangularNetwork_pullback +end diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index e18ac28d0..c71647d42 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -6,26 +6,51 @@ using MPSKit using PEPSKit using OptimKit using Zygote +using LinearAlgebra +using KrylovKit + +const ising_βc_triangular = BigFloat(BigFloat(asinh(BigFloat(sqrt(BigFloat(1.0) / BigFloat(3.0))))) / BigFloat(2.0)) +const f_onsager_triangular::BigFloat = -3.20253248660790791834355252025862951439 + +function classical_ising_triangular(β) + t = Float64[exp(β) exp(-β); exp(-β) exp(β)] + + r = eigen(t) + nt = r.vectors * sqrt(LinearAlgebra.Diagonal(r.values)) * r.vectors + + O = zeros(2, 2, 2, 2, 2, 2) + O[1, 1, 1, 1, 1, 1] = 1 + O[2, 2, 2, 2, 2, 2] = 1 + + H = [1 1; 1 -1] / sqrt(2) + + @tensor o[-1 -2 -3; -4 -5 -6] := O[1 2 3; 4 5 6] * nt[-1; 1] * nt[-2; 2] * nt[-3; 3] * nt[-4; 4] * nt[-5; 5] * nt[-6; 6] + @tensor o2[-1 -2 -3; -4 -5 -6] := o[1 2 3; 4 5 6] * H[-1; 1] * H[-2; 2] * H[-3; 3] * H[-4; 4] * H[-5; 5] * H[-6; 6] + return TensorMap(o2, ℂ^2 * ℂ^2 * ℂ^2, ℂ^2 * ℂ^2 * ℂ^2) +end @testset "CTM_triangular - Random tensor" begin for conditioning in [true false] for projector_alg in [:twothirds :full] - alg = SimultaneousCTMRGTria(; conditioning, projector_alg) - T = Float64 + Random.seed!(79413165445) + alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) + eltype = ComplexF64 χ = 7 pspace = ℂ^2 vspace = ℂ^3 envspace = ℂ^χ - ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') + ket = randn(eltype, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') bra = copy(ket) - pf = randn(T, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) + pf = randn(eltype, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) sandwiches = [pf, (ket, bra)] - unitcell = (1, 1) + # sandwiches = [(ket, bra), pf] + unitcell = (2, 2) for (sandwich, vspace) in zip(sandwiches, [vspace, vspace ⊗ vspace']) + # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) - env₀ = CTMRGTriaEnv(randn, T, vspace, envspace; unitcell) + env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) env, info = leading_boundary(env₀, network, alg) @test info.convergence_metric < 1.0 end @@ -33,63 +58,100 @@ using Zygote end end -@testset "CTM_triangular - Differentiability" begin +@testset "CTM_triangular - Classical Ising" begin for conditioning in [true false] for projector_alg in [:twothirds :full] - alg = SimultaneousCTMRGTria(; conditioning, projector_alg) - T = Float64 - χ = 7 - pspace = ℂ^2 - vspace = ℂ^3 - envspace = ℂ^χ - ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') - bra = copy(ket) + Random.seed!(156484561351) + alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) unitcell = (1, 1) - network = InfiniteTriangularNetwork(fill((ket, bra), unitcell)) - env₀ = CTMRGTriaEnv(randn, T, vspace ⊗ vspace', envspace; unitcell) - - optimizer_alg = LBFGS(4; maxiter = 10) - real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) - reuse_env = true - peps₀ = copy(ket) + T = classical_ising_triangular(ising_βc_triangular) + pf = InfiniteTriangularNetwork(fill(T, unitcell)) - function peps_retract(x, η, α) - peps = x[1] - env = deepcopy(x[2]) - - retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) - peps´ = InfinitePEPS(map(first, retractions)) - ξ = InfinitePEPS(map(last, retractions)) + χ = 20 + vspace = codomain(T)[1] + envspace = ℂ^χ + eltype = Float64 + env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) + env, info = leading_boundary(env₀, pf, alg) - return (peps´, env), ξ - end - retract = peps_retract - - - # optimize operator cost function - (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( - (peps₀, env₀), optimizer_alg; - retract, inner = real_inner, - ) do (peps, env) - start_time = time_ns() - E, gs = withgradient(peps) do ψ - env′, info = hook_pullback( - leading_boundary, env, ψ, alg.boundary_alg; - alg_rrule = alg.gradient_alg, - ) - # ignore_derivatives() do - # reuse_env && update!(env, env′) - # push!(truncation_errors, info.truncation_error) - # push!(condition_numbers, info.condition_number) - # end - return energy(ψ, env′, operator) - end - g = only(gs) # `withgradient` returns tuple of gradients `gs` - push!(gradnorms_unitcell, norm.(g.A)) - push!(times, (time_ns() - start_time) * 1.0e-9) - return E, g - end + nw_value = network_value(pf, env) + lz = real(log(nw_value)) + fs = lz * -1 / ising_βc_triangular + @test fs ≈ f_onsager_triangular rtol = 1.0e-4 end end end + +# @testset "CTM_triangular - Differentiability" begin +# for conditioning in [true false] +# for projector_alg in [:twothirds :full] +# ctm_alg = SimultaneousCTMRGTriangular(; conditioning, projector_alg) +# T = ComplexF64 +# S = Trivial +# χ = 7 +# pspace = ℂ^2 + +# J = 1.0 +# g = 2.5 + +# ZZ = rmul!(PEPSKit.σᶻᶻ(T, S), -J) +# X = rmul!(PEPSKit.σˣ(T, S), g * -J) + +# vspace = ℂ^3 +# envspace = ℂ^χ +# ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') +# bra = copy(ket) + +# unitcell = (1, 1) +# network = InfiniteTriangularNetwork(fill((ket, bra), unitcell)) +# env₀ = CTMRGEnvTriangular(randn, T, vspace ⊗ vspace', envspace; unitcell) + +# env, info = leading_boundary(env₀, network, ctm_alg) +# E₀ = PEPSKit.energy(network, env, X, ZZ) +# optimizer_alg = LBFGS(4; maxiter = 10) +# real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) +# reuse_env = true +# peps₀ = InfinitePEPSTriangular(copy(ket)) + +# function peps_retract(x, η, α) +# peps = x[1] +# env = deepcopy(x[2]) + +# retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) +# peps´ = InfinitePEPS(map(first, retractions)) +# ξ = InfinitePEPS(map(last, retractions)) + +# return (peps´, env), ξ +# end +# retract = peps_retract + +# # alg = PEPSOptimize() +# gradtol = 1e-3 +# gradient_alg = EigSolver(; solver_alg=Arnoldi(; tol=gradtol, eager=true), iterscheme=:fixed) +# # optimize operator cost function +# (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( +# (peps₀, env₀), optimizer_alg; +# retract, inner = real_inner, +# ) do (peps, env) +# start_time = time_ns() +# E, gs = withgradient(peps) do ψ +# env′, info = PEPSKit.hook_pullback( +# leading_boundary, env, ψ, ctm_alg; +# alg_rrule = gradient_alg, +# ) +# # ignore_derivatives() do +# # reuse_env && update!(env, env′) +# # push!(truncation_errors, info.truncation_error) +# # push!(condition_numbers, info.condition_number) +# # end +# return energy(ψ, env′, operator_onesite, operator_twosite) +# end +# g = only(gs) # `withgradient` returns tuple of gradients `gs` +# push!(gradnorms_unitcell, norm.(g.A)) +# push!(times, (time_ns() - start_time) * 1.0e-9) +# return E, g +# end +# end +# end +# end From 9c582f4aaed6cd1563c55ea198169f7b12759e61 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 24 Feb 2026 13:46:40 +0100 Subject: [PATCH 04/13] fix import of Base.rot180 --- src/networks/infinitetriangularnetwork.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks/infinitetriangularnetwork.jl b/src/networks/infinitetriangularnetwork.jl index 953f595dd..1383c83fe 100644 --- a/src/networks/infinitetriangularnetwork.jl +++ b/src/networks/infinitetriangularnetwork.jl @@ -116,7 +116,7 @@ end function rotr60(n::InfiniteTriangularNetwork) return InfiniteTriangularNetwork(rotr60(_rotr60_localsandwich.(unitcell(n)))) end -function rot180(n::InfiniteTriangularNetwork) +function Base.rot180(n::InfiniteTriangularNetwork) return InfiniteTriangularNetwork(rot180(_rot180_localsandwich.(unitcell(n)))) end From f7df34c18136b3847acdfd122e877dd55e9df7d9 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 24 Feb 2026 13:47:36 +0100 Subject: [PATCH 05/13] Fix formatting --- .../ctmrg_contractions_triangular.jl | 8 +- .../ctmrg_triangular/simultaneous.jl | 10 +- .../fixed_point_differentiation.jl | 14 +- src/algorithms/toolbox_triangular.jl | 178 +++++++++--------- test/ctmrg/triangular.jl | 4 +- 5 files changed, 107 insertions(+), 107 deletions(-) diff --git a/src/algorithms/contractions/ctmrg_contractions_triangular.jl b/src/algorithms/contractions/ctmrg_contractions_triangular.jl index d00515d6b..1ef921c8b 100644 --- a/src/algorithms/contractions/ctmrg_contractions_triangular.jl +++ b/src/algorithms/contractions/ctmrg_contractions_triangular.jl @@ -46,13 +46,13 @@ end function renormalize_corner_triangular((dir, r, c), network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PFTensorTriangular} @tensor opt = true env_C_new[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7; 8] * - rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] + rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] return env_C_new end function renormalize_corner_triangular((dir, r, c), network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PEPSSandwichTriangular} @tensor opt = true env_C_new[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2 11; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7 12; 8] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * - Pas[mod1(dir - 1, 6), r, c][-1; 4 5 13] * Pbs[dir, r, c][8 9 14; -4] + rotl60(ket(network[r, c]), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * + Pas[mod1(dir - 1, 6), r, c][-1; 4 5 13] * Pbs[dir, r, c][8 9 14; -4] return env_C_new -end \ No newline at end of file +end diff --git a/src/algorithms/ctmrg_triangular/simultaneous.jl b/src/algorithms/ctmrg_triangular/simultaneous.jl index cddda61a2..81b4f980c 100644 --- a/src/algorithms/ctmrg_triangular/simultaneous.jl +++ b/src/algorithms/ctmrg_triangular/simultaneous.jl @@ -190,7 +190,7 @@ function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs:: new_edges′ = similar(env.Ea, T_proj) new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) Eb_new = Ẽbs[dir, r, c] * Qbs[dir, r, c] - Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,),(2,3,4))), ((1,2,3),(4,))) + Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,), (2, 3, 4))), ((1, 2, 3), (4,))) return Ea_new, Eb_new end return CTMRGEnvTriangular(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) @@ -202,7 +202,7 @@ function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs:: new_edges′ = similar(env.Ea, T_proj) new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) Eb_new = Ẽbs[dir, r, c] * Qbs[dir, r, c] - Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,),(2,3))), ((1,2),(3,))) + Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,), (2, 3))), ((1, 2), (3,))) return Ea_new, Eb_new end return CTMRGEnvTriangular(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) @@ -219,10 +219,10 @@ function normalize_corners!(env) end function normalize_edges!(env) - (r, c) = (1,1) + (r, c) = (1, 1) for dir in 1:6 - env.Ea[dir,r,c] /= norm(env.Ea[dir,r,c]) - env.Eb[dir,r,c] /= norm(env.Eb[dir,r,c]) + env.Ea[dir, r, c] /= norm(env.Ea[dir, r, c]) + env.Eb[dir, r, c] /= norm(env.Eb[dir, r, c]) end return env end diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index 4ac24cb2c..3e55bad12 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -213,7 +213,7 @@ function _rrule( ::typeof(leading_boundary), envinit, state, - alg::Union{CTMRGAlgorithm,CTMRGAlgorithmTriangular}, + alg::Union{CTMRGAlgorithm, CTMRGAlgorithmTriangular}, ) env, info = leading_boundary(envinit, state, alg) alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation @@ -279,12 +279,12 @@ function _rrule( end function _rrule( - gradmode::GradMode{:fixed}, - config::RuleConfig, - ::typeof(MPSKit.leading_boundary), - envinit, - state, - alg::SimultaneousCTMRGTriangular, + gradmode::GradMode{:fixed}, + config::RuleConfig, + ::typeof(MPSKit.leading_boundary), + envinit, + state, + alg::SimultaneousCTMRGTriangular, ) env, = leading_boundary(envinit, state, alg) alg_fixed = @set alg.trunctype = :FixedSpaceTruncation # fix spaces during differentiation diff --git a/src/algorithms/toolbox_triangular.jl b/src/algorithms/toolbox_triangular.jl index cdf2f663d..ab67be0ef 100644 --- a/src/algorithms/toolbox_triangular.jl +++ b/src/algorithms/toolbox_triangular.jl @@ -1,126 +1,126 @@ function network_value(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular) return prod(Iterators.product(axes(network)...)) do (r, c) - nw_corners = complex(_contract_corners((r,c), network, env)) - nw_full = complex(_contract_site_large((r,c), network, env)) - nw_0 = complex(_contract_edges_0((r,c), network, env)) - nw_60 = complex(_contract_edges_60((r,c), network, env)) - nw_120 = complex(_contract_edges_120((r,c), network, env)) + nw_corners = complex(_contract_corners((r, c), network, env)) + nw_full = complex(_contract_site_large((r, c), network, env)) + nw_0 = complex(_contract_edges_0((r, c), network, env)) + nw_60 = complex(_contract_edges_60((r, c), network, env)) + nw_120 = complex(_contract_edges_120((r, c), network, env)) return (nw_full * nw_corners^2 / (nw_0 * nw_60 * nw_120))^(1 / 3) end end -function _contract_edges_0((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} - return @tensor opt = true network[r,c][DL180 DL240 DL300; DL120 DL60 DL0] * network[r,_next(c,end)][DL0 DR240 DR300; DR120 DR60 DR0] * - env.C[1,_prev(r,end),c][χNW DL120; χNa] * env.C[2,_prev(r,end),_next(c+1,end)][χNb DR60; χNE] * env.C[3,r,_next(c+1,end)][χNE DR0; χSE] * - env.C[4,_next(r,end),_next(c,end)][χSE DR300; χSa] * env.C[5,_next(r,end),_prev(c,end)][χSb DL240; χSW] * env.C[6,r,_prev(c,end)][χSW DL180; χNW] * - env.Eb[1,_prev(r,end),_next(c,end)][χNa DL60; χNC] * env.Ea[1,_prev(r,end),_next(c,end)][χNC DR120; χNb] * - env.Eb[4,_next(r,end),c][χSa DR240; χSC] * env.Ea[4,_next(r,end),c][χSC DL300; χSb] +function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[r, c][DL180 DL240 DL300; DL120 DL60 DL0] * network[r, _next(c, end)][DL0 DR240 DR300; DR120 DR60 DR0] * + env.C[1, _prev(r, end), c][χNW DL120; χNa] * env.C[2, _prev(r, end), _next(c + 1, end)][χNb DR60; χNE] * env.C[3, r, _next(c + 1, end)][χNE DR0; χSE] * + env.C[4, _next(r, end), _next(c, end)][χSE DR300; χSa] * env.C[5, _next(r, end), _prev(c, end)][χSb DL240; χSW] * env.C[6, r, _prev(c, end)][χSW DL180; χNW] * + env.Eb[1, _prev(r, end), _next(c, end)][χNa DL60; χNC] * env.Ea[1, _prev(r, end), _next(c, end)][χNC DR120; χNb] * + env.Eb[4, _next(r, end), c][χSa DR240; χSC] * env.Ea[4, _next(r, end), c][χSC DL300; χSb] end -function _contract_edges_60((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} - return @tensor opt = true network[r,c][DTR180 DBL60 DTR300; DTR120 DTR60 DTR0] * network[_next(r,end),_prev(c,end)][DBL180 DBL240 DBL300; DBL120 DBL60 DBL0] * - env.C[1,_prev(r,end),c][χNWb DTR120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTR60; χNE] * env.C[3,r,_next(c,end)][χNE DTR0; χSEa] * - env.C[4,_next(r+1,end),_prev(c,end)][χSEb DBL300; χS] * env.C[5,_next(r+1,end),_prev(c-1,end)][χS DBL240; χSW] * env.C[6,_next(r,end),_prev(c-1,end)][χSW DBL180; χNWa] * - env.Eb[3,_next(r,end),c][χSEa DTR300; χSEC] * env.Ea[3,_next(r,end),c][χSEC DBL0; χSEb] * - env.Eb[6,r,_prev(c,end)][χNWa DBL120; χNWC] * env.Ea[6,r,_prev(c,end)][χNWC DTR180; χNWb] +function _contract_edges_60((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[r, c][DTR180 DBL60 DTR300; DTR120 DTR60 DTR0] * network[_next(r, end), _prev(c, end)][DBL180 DBL240 DBL300; DBL120 DBL60 DBL0] * + env.C[1, _prev(r, end), c][χNWb DTR120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN DTR60; χNE] * env.C[3, r, _next(c, end)][χNE DTR0; χSEa] * + env.C[4, _next(r + 1, end), _prev(c, end)][χSEb DBL300; χS] * env.C[5, _next(r + 1, end), _prev(c - 1, end)][χS DBL240; χSW] * env.C[6, _next(r, end), _prev(c - 1, end)][χSW DBL180; χNWa] * + env.Eb[3, _next(r, end), c][χSEa DTR300; χSEC] * env.Ea[3, _next(r, end), c][χSEC DBL0; χSEb] * + env.Eb[6, r, _prev(c, end)][χNWa DBL120; χNWC] * env.Ea[6, r, _prev(c, end)][χNWC DTR180; χNWb] end -function _contract_edges_120((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} - return @tensor opt = true network[r,c][DTL180 DTL240 DTL300; DTL120 DTL60 DTL0] * network[_next(r,end),c][DBR180 DBR240 DBR300; DTL300 DBR60 DBR0] * - env.C[1,_prev(r,end),c][χNW DTL120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTL60; χNEa] * env.C[3,_next(r,end),_next(c,end)][χNEb DBR0; χSE] * - env.C[4,_next(r+1,end),c][χSE DBR300; χS] * env.C[5,_next(r+1,end),_prev(c,end)][χS DBR240; χSWa] * env.C[6,r,_prev(c,end)][χSWb DTL180; χNW] * - env.Eb[2,r,_next(c,end)][χNEa DTL0; χNEC] * env.Ea[2,r,_next(c,end)][χNEC DBR60; χNEb] * - env.Eb[5,_next(r,end),_prev(c,end)][χSWa DBR180; χSWC] * env.Ea[5,_next(r,end),_prev(c,end)][χSWC DTL240; χSWb] +function _contract_edges_120((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[r, c][DTL180 DTL240 DTL300; DTL120 DTL60 DTL0] * network[_next(r, end), c][DBR180 DBR240 DBR300; DTL300 DBR60 DBR0] * + env.C[1, _prev(r, end), c][χNW DTL120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN DTL60; χNEa] * env.C[3, _next(r, end), _next(c, end)][χNEb DBR0; χSE] * + env.C[4, _next(r + 1, end), c][χSE DBR300; χS] * env.C[5, _next(r + 1, end), _prev(c, end)][χS DBR240; χSWa] * env.C[6, r, _prev(c, end)][χSWb DTL180; χNW] * + env.Eb[2, r, _next(c, end)][χNEa DTL0; χNEC] * env.Ea[2, r, _next(c, end)][χNEC DBR60; χNEb] * + env.Eb[5, _next(r, end), _prev(c, end)][χSWa DBR180; χSWC] * env.Ea[5, _next(r, end), _prev(c, end)][χSWC DTL240; χSWb] end -function _contract_site_large((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} - return @tensor opt = true network[_prev(r,end),c][DNW180 DW60 DNW300; DNW120 DNW60 DNW0] * network[_prev(r,end),_next(c,end)][DNW0 DNE240 DNE300; DNE120 DNE60 DNE0] * - network[r,_next(c,end)][DE180 DE240 DE300; DNE300 DE60 DE0] * network[_next(r,end),c][DSE180 DSE240 DSE300; DSE120 DE240 DSE0] * network[_next(r,end),_prev(c,end)][DSW180 DSW240 DSW300; DSW120 DSW60 DSE180] * - network[r,_prev(c,end)][DW180 DW240 DSW120; DW120 DW60 DW0] * network[r,c][DW0 DSW60 DSE120; DNW300 DNE240 DE180] * - env.C[1,_prev(r-1,end),c][χNWa DNW120; χNb] * env.Eb[1,_prev(r-1,end),_next(c,end)][χNb DNW60; χNC] * env.Ea[1,_prev(r-1,end),_next(c,end)][χNC DNE120; χNa] * - env.C[2,_prev(r-1,end),_next(c+1,end)][χNa DNE60; χNEb] * env.Eb[2,_prev(r,end),_next(c+1,end)][χNEb DNE0; χNEC] * env.Ea[2,_prev(r,end),_next(c+1,end)][χNEC DE60; χNEa] * - env.C[3,r,_next(c+1,end)][χNEa DE0; χSEb] * env.Eb[3,_next(r,end),_next(c,end)][χSEb DE300; χSEC] * env.Ea[3,_next(r,end),_next(c,end)][χSEC DSE0; χSEa] * - env.C[4,_next(r+1,end),c][χSEa DSE300; χSb] * env.Eb[4,_next(r+1,end),_prev(c,end)][χSb DSE240; χSC] * env.Ea[4,_next(r+1,end),_prev(c,end)][χSC DSW300; χSa] * - env.C[5,_next(r+1,end),_prev(c-1,end)][χSa DSW240; χSWb] * env.Eb[5,_next(r,end),_prev(c-1,end)][χSWb DSW180; χSWC] * env.Ea[5,_next(r,end),_prev(c-1,end)][χSWC DW240; χSWa] * - env.C[6,r,_prev(c-1,end)][χSWa DW180; χNWb] * env.Eb[6,_prev(r,end),_prev(c,end)][χNWb DW120; χNWC] * env.Ea[6,_prev(r,end),_prev(c,end)][χNWC DNW180; χNWa] +function _contract_site_large((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true network[_prev(r, end), c][DNW180 DW60 DNW300; DNW120 DNW60 DNW0] * network[_prev(r, end), _next(c, end)][DNW0 DNE240 DNE300; DNE120 DNE60 DNE0] * + network[r, _next(c, end)][DE180 DE240 DE300; DNE300 DE60 DE0] * network[_next(r, end), c][DSE180 DSE240 DSE300; DSE120 DE240 DSE0] * network[_next(r, end), _prev(c, end)][DSW180 DSW240 DSW300; DSW120 DSW60 DSE180] * + network[r, _prev(c, end)][DW180 DW240 DSW120; DW120 DW60 DW0] * network[r, c][DW0 DSW60 DSE120; DNW300 DNE240 DE180] * + env.C[1, _prev(r - 1, end), c][χNWa DNW120; χNb] * env.Eb[1, _prev(r - 1, end), _next(c, end)][χNb DNW60; χNC] * env.Ea[1, _prev(r - 1, end), _next(c, end)][χNC DNE120; χNa] * + env.C[2, _prev(r - 1, end), _next(c + 1, end)][χNa DNE60; χNEb] * env.Eb[2, _prev(r, end), _next(c + 1, end)][χNEb DNE0; χNEC] * env.Ea[2, _prev(r, end), _next(c + 1, end)][χNEC DE60; χNEa] * + env.C[3, r, _next(c + 1, end)][χNEa DE0; χSEb] * env.Eb[3, _next(r, end), _next(c, end)][χSEb DE300; χSEC] * env.Ea[3, _next(r, end), _next(c, end)][χSEC DSE0; χSEa] * + env.C[4, _next(r + 1, end), c][χSEa DSE300; χSb] * env.Eb[4, _next(r + 1, end), _prev(c, end)][χSb DSE240; χSC] * env.Ea[4, _next(r + 1, end), _prev(c, end)][χSC DSW300; χSa] * + env.C[5, _next(r + 1, end), _prev(c - 1, end)][χSa DSW240; χSWb] * env.Eb[5, _next(r, end), _prev(c - 1, end)][χSWb DSW180; χSWC] * env.Ea[5, _next(r, end), _prev(c - 1, end)][χSWC DW240; χSWa] * + env.C[6, r, _prev(c - 1, end)][χSWa DW180; χNWb] * env.Eb[6, _prev(r, end), _prev(c, end)][χNWb DW120; χNWC] * env.Ea[6, _prev(r, end), _prev(c, end)][χNWC DNW180; χNWa] end -function _contract_corners((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} - return @tensor opt = true env.C[1,_prev(r,end),c][χNW D120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN D60; χNE] * env.C[3,r,_next(c,end)][χNE D0; χSE] * - env.C[4,_next(r,end),c][χSE D300; χS] * env.C[5,_next(r,end),_prev(c,end)][χS D240; χSW] * env.C[6,r,_prev(c,end)][χSW D180; χNW] * - network[r,c][D180 D240 D300; D120 D60 D0] +function _contract_corners((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PFTensorTriangular} + return @tensor opt = true env.C[1, _prev(r, end), c][χNW D120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN D60; χNE] * env.C[3, r, _next(c, end)][χNE D0; χSE] * + env.C[4, _next(r, end), c][χSE D300; χS] * env.C[5, _next(r, end), _prev(c, end)][χS D240; χSW] * env.C[6, r, _prev(c, end)][χSW D180; χNW] * + network[r, c][D180 D240 D300; D120 D60 D0] end -### For +### For -function _contract_edges_0((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[r,c])[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r,_next(c,end)])[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * - conj(bra(network[r,c])[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r,_next(c,end)])[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * - env.C[1,_prev(r,end),c][χNW DLt120 DLb120; χNa] * env.C[2,_prev(r,end),_next(c+1,end)][χNb DRt60 DRb60; χNE] * env.C[3,r,_next(c+1,end)][χNE DRt0 DRb0; χSE] * - env.C[4,_next(r,end),_next(c,end)][χSE DRt300 DRb300; χSa] * env.C[5,_next(r,end),_prev(c,end)][χSb DLt240 DLb240; χSW] * env.C[6,r,_prev(c,end)][χSW DLt180 DLb180; χNW] * - env.Eb[1,_prev(r,end),_next(c,end)][χNa DLt60 DLb60; χNC] * env.Ea[1,_prev(r,end),_next(c,end)][χNC DRt120 DRb120; χNb] * - env.Eb[4,_next(r,end),c][χSa DRt240 DRb240; χSC] * env.Ea[4,_next(r,end),c][χSC DLt300 DLb300; χSb] +function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[r, c])[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, _next(c, end)])[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(network[r, c])[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, _next(c, end)])[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + env.C[1, _prev(r, end), c][χNW DLt120 DLb120; χNa] * env.C[2, _prev(r, end), _next(c + 1, end)][χNb DRt60 DRb60; χNE] * env.C[3, r, _next(c + 1, end)][χNE DRt0 DRb0; χSE] * + env.C[4, _next(r, end), _next(c, end)][χSE DRt300 DRb300; χSa] * env.C[5, _next(r, end), _prev(c, end)][χSb DLt240 DLb240; χSW] * env.C[6, r, _prev(c, end)][χSW DLt180 DLb180; χNW] * + env.Eb[1, _prev(r, end), _next(c, end)][χNa DLt60 DLb60; χNC] * env.Ea[1, _prev(r, end), _next(c, end)][χNC DRt120 DRb120; χNb] * + env.Eb[4, _next(r, end), c][χSa DRt240 DRb240; χSC] * env.Ea[4, _next(r, end), c][χSC DLt300 DLb300; χSb] end -function _contract_edges_0((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E,S,2,2}) where {P <: PEPSSandwichTriangular, E, S} - return @tensor opt = true ket(network[r,c])[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r,_next(c,end)])[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * - conj(bra(network[r,c])[dLb; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r,_next(c,end)])[dRb; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * - env.C[1,_prev(r,end),c][χNW DLt120 DLb120; χNa] * env.C[2,_prev(r,end),_next(c+1,end)][χNb DRt60 DRb60; χNE] * env.C[3,r,_next(c+1,end)][χNE DRt0 DRb0; χSE] * - env.C[4,_next(r,end),_next(c,end)][χSE DRt300 DRb300; χSa] * env.C[5,_next(r,end),_prev(c,end)][χSb DLt240 DLb240; χSW] * env.C[6,r,_prev(c,end)][χSW DLt180 DLb180; χNW] * - env.Eb[1,_prev(r,end),_next(c,end)][χNa DLt60 DLb60; χNC] * env.Ea[1,_prev(r,end),_next(c,end)][χNC DRt120 DRb120; χNb] * - env.Eb[4,_next(r,end),c][χSa DRt240 DRb240; χSC] * env.Ea[4,_next(r,end),c][χSC DLt300 DLb300; χSb] * +function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E, S, 2, 2}) where {P <: PEPSSandwichTriangular, E, S} + return @tensor opt = true ket(network[r, c])[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, _next(c, end)])[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(network[r, c])[dLb; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, _next(c, end)])[dRb; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + env.C[1, _prev(r, end), c][χNW DLt120 DLb120; χNa] * env.C[2, _prev(r, end), _next(c + 1, end)][χNb DRt60 DRb60; χNE] * env.C[3, r, _next(c + 1, end)][χNE DRt0 DRb0; χSE] * + env.C[4, _next(r, end), _next(c, end)][χSE DRt300 DRb300; χSa] * env.C[5, _next(r, end), _prev(c, end)][χSb DLt240 DLb240; χSW] * env.C[6, r, _prev(c, end)][χSW DLt180 DLb180; χNW] * + env.Eb[1, _prev(r, end), _next(c, end)][χNa DLt60 DLb60; χNC] * env.Ea[1, _prev(r, end), _next(c, end)][χNC DRt120 DRb120; χNb] * + env.Eb[4, _next(r, end), c][χSa DRt240 DRb240; χSC] * env.Ea[4, _next(r, end), c][χSC DLt300 DLb300; χSb] * op[dLb dRb; dLt dRt] end -function _contract_edges_60((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[r,c])[dTR; DTRt120 DTRt60 DTRt0 DTRt300 DBLt60 DTRt180] * ket(network[_next(r,end),_prev(c,end)])[dBL; DBLt120 DBLt60 DBLt0 DBLt300 DBLt240 DBLt180] * - conj(bra(network[r,c])[dTR; DTRb120 DTRb60 DTRb0 DTRb300 DBLb60 DTRb180]) * conj(bra(network[_next(r,end),_prev(c,end)])[dBL; DBLb120 DBLb60 DBLb0 DBLb300 DBLb240 DBLb180]) * - env.C[1,_prev(r,end),c][χNWb DTRt120 DTRb120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTRt60 DTRb60; χNE] * env.C[3,r,_next(c,end)][χNE DTRt0 DTRb0; χSEa] * - env.C[4,_next(r+1,end),_prev(c,end)][χSEb DBLt300 DBLb300; χS] * env.C[5,_next(r+1,end),_prev(c-1,end)][χS DBLt240 DBLb240; χSW] * env.C[6,_next(r,end),_prev(c-1,end)][χSW DBLt180 DBLb180; χNWa] * - env.Eb[3,_next(r,end),c][χSEa DTRt300 DTRb300; χSEC] * env.Ea[3,_next(r,end),c][χSEC DBLt0 DBLb0; χSEb] * - env.Eb[6,r,_prev(c,end)][χNWa DBLt120 DBLb120; χNWC] * env.Ea[6,r,_prev(c,end)][χNWC DTRt180 DTRb180; χNWb] +function _contract_edges_60((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[r, c])[dTR; DTRt120 DTRt60 DTRt0 DTRt300 DBLt60 DTRt180] * ket(network[_next(r, end), _prev(c, end)])[dBL; DBLt120 DBLt60 DBLt0 DBLt300 DBLt240 DBLt180] * + conj(bra(network[r, c])[dTR; DTRb120 DTRb60 DTRb0 DTRb300 DBLb60 DTRb180]) * conj(bra(network[_next(r, end), _prev(c, end)])[dBL; DBLb120 DBLb60 DBLb0 DBLb300 DBLb240 DBLb180]) * + env.C[1, _prev(r, end), c][χNWb DTRt120 DTRb120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN DTRt60 DTRb60; χNE] * env.C[3, r, _next(c, end)][χNE DTRt0 DTRb0; χSEa] * + env.C[4, _next(r + 1, end), _prev(c, end)][χSEb DBLt300 DBLb300; χS] * env.C[5, _next(r + 1, end), _prev(c - 1, end)][χS DBLt240 DBLb240; χSW] * env.C[6, _next(r, end), _prev(c - 1, end)][χSW DBLt180 DBLb180; χNWa] * + env.Eb[3, _next(r, end), c][χSEa DTRt300 DTRb300; χSEC] * env.Ea[3, _next(r, end), c][χSEC DBLt0 DBLb0; χSEb] * + env.Eb[6, r, _prev(c, end)][χNWa DBLt120 DBLb120; χNWC] * env.Ea[6, r, _prev(c, end)][χNWC DTRt180 DTRb180; χNWb] end -function _contract_edges_120((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[r,c])[dTL; DTLt120 DTLt60 DTLt0 DTLt300 DTLt240 DTLt180] * ket(network[_next(r,end),c])[dBR; DTLt300 DBRt60 DBRt0 DBRt300 DBRt240 DBRt180] * - conj(bra(network[r,c])[dTL; DTLb120 DTLb60 DTLb0 DTLb300 DTLb240 DTLb180]) * conj(bra(network[_next(r,end),c])[dBR; DTLb300 DBRb60 DBRb0 DBRb300 DBRb240 DBRb180]) * - env.C[1,_prev(r,end),c][χNW DTLt120 DTLb120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN DTLt60 DTLb60; χNEa] * env.C[3,_next(r,end),_next(c,end)][χNEb DBRt0 DBRb0; χSE] * - env.C[4,_next(r+1,end),c][χSE DBRt300 DBRb300; χS] * env.C[5,_next(r+1,end),_prev(c,end)][χS DBRt240 DBRb240; χSWa] * env.C[6,r,_prev(c,end)][χSWb DTLt180 DTLb180; χNW] * - env.Eb[2,r,_next(c,end)][χNEa DTLt0 DTLb0; χNEC] * env.Ea[2,r,_next(c,end)][χNEC DBRt60 DBRb60; χNEb] * - env.Eb[5,_next(r,end),_prev(c,end)][χSWa DBRt180 DBRb180; χSWC] * env.Ea[5,_next(r,end),_prev(c,end)][χSWC DTLt240 DTLb240; χSWb] +function _contract_edges_120((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[r, c])[dTL; DTLt120 DTLt60 DTLt0 DTLt300 DTLt240 DTLt180] * ket(network[_next(r, end), c])[dBR; DTLt300 DBRt60 DBRt0 DBRt300 DBRt240 DBRt180] * + conj(bra(network[r, c])[dTL; DTLb120 DTLb60 DTLb0 DTLb300 DTLb240 DTLb180]) * conj(bra(network[_next(r, end), c])[dBR; DTLb300 DBRb60 DBRb0 DBRb300 DBRb240 DBRb180]) * + env.C[1, _prev(r, end), c][χNW DTLt120 DTLb120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN DTLt60 DTLb60; χNEa] * env.C[3, _next(r, end), _next(c, end)][χNEb DBRt0 DBRb0; χSE] * + env.C[4, _next(r + 1, end), c][χSE DBRt300 DBRb300; χS] * env.C[5, _next(r + 1, end), _prev(c, end)][χS DBRt240 DBRb240; χSWa] * env.C[6, r, _prev(c, end)][χSWb DTLt180 DTLb180; χNW] * + env.Eb[2, r, _next(c, end)][χNEa DTLt0 DTLb0; χNEC] * env.Ea[2, r, _next(c, end)][χNEC DBRt60 DBRb60; χNEb] * + env.Eb[5, _next(r, end), _prev(c, end)][χSWa DBRt180 DBRb180; χSWC] * env.Ea[5, _next(r, end), _prev(c, end)][χSWC DTLt240 DTLb240; χSWb] end -function _contract_site_large((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[_prev(r,end),c])[dNW; DNWt120 DNWt60 DNWt0 DNWt300 DWt60 DNWt180] * ket(network[_prev(r,end),_next(c,end)])[dNE; DNEt120 DNEt60 DNEt0 DNEt300 DNEt240 DNWt0] * - ket(network[r,_next(c,end)])[dE; DNEt300 DEt60 DEt0 DEt300 DEt240 DEt180] * ket(network[_next(r,end),c])[dSE; DSEt120 DEt240 DSEt0 DSEt300 DSEt240 DSEt180] * ket(network[_next(r,end),_prev(c,end)])[dSW; DSWt120 DSWt60 DSEt180 DSWt300 DSWt240 DSWt180] * - ket(network[r,_prev(c,end)])[dW; DWt120 DWt60 DWt0 DSWt120 DWt240 DWt180] * ket(network[r,c])[dCenter; DNWt300 DNEt240 DEt180 DSEt120 DSWt60 DWt0] * - conj(bra(network[_prev(r,end),c])[dNW; DNWb120 DNWb60 DNWb0 DNWb300 DWb60 DNWb180]) * conj(bra(network[_prev(r,end),_next(c,end)])[dNE; DNEb120 DNEb60 DNEb0 DNEb300 DNEb240 DNWb0]) * - conj(bra(network[r,_next(c,end)])[dE; DNEb300 DEb60 DEb0 DEb300 DEb240 DEb180]) * conj(bra(network[_next(r,end),c])[dSE; DSEb120 DEb240 DSEb0 DSEb300 DSEb240 DSEb180]) * conj(bra(network[_next(r,end),_prev(c,end)])[dSW; DSWb120 DSWb60 DSEb180 DSWb300 DSWb240 DSWb180]) * - conj(bra(network[r,_prev(c,end)])[dW; DWb120 DWb60 DWb0 DSWb120 DWb240 DWb180]) * conj(bra(network[r,c])[dCenter; DNWb300 DNEb240 DEb180 DSEb120 DSWb60 DWb0]) * - env.C[1,_prev(r-1,end),c][χNWa DNWt120 DNWb120; χNb] * env.Eb[1,_prev(r-1,end),_next(c,end)][χNb DNWt60 DNWb60; χNC] * env.Ea[1,_prev(r-1,end),_next(c,end)][χNC DNEt120 DNEb120; χNa] * - env.C[2,_prev(r-1,end),_next(c+1,end)][χNa DNEt60 DNEb60; χNEb] * env.Eb[2,_prev(r,end),_next(c+1,end)][χNEb DNEt0 DNEb0; χNEC] * env.Ea[2,_prev(r,end),_next(c+1,end)][χNEC DEt60 DEb60; χNEa] * - env.C[3,r,_next(c+1,end)][χNEa DEt0 DEb0; χSEb] * env.Eb[3,_next(r,end),_next(c,end)][χSEb DEt300 DEb300; χSEC] * env.Ea[3,_next(r,end),_next(c,end)][χSEC DSEt0 DSEb0; χSEa] * - env.C[4,_next(r+1,end),c][χSEa DSEt300 DSEb300; χSb] * env.Eb[4,_next(r+1,end),_prev(c,end)][χSb DSEt240 DSEb240; χSC] * env.Ea[4,_next(r+1,end),_prev(c,end)][χSC DSWt300 DSWb300; χSa] * - env.C[5,_next(r+1,end),_prev(c-1,end)][χSa DSWt240 DSWb240; χSWb] * env.Eb[5,_next(r,end),_prev(c-1,end)][χSWb DSWt180 DSWb180; χSWC] * env.Ea[5,_next(r,end),_prev(c-1,end)][χSWC DWt240 DWb240; χSWa] * - env.C[6,r,_prev(c-1,end)][χSWa DWt180 DWb180; χNWb] * env.Eb[6,_prev(r,end),_prev(c,end)][χNWb DWt120 DWb120; χNWC] * env.Ea[6,_prev(r,end),_prev(c,end)][χNWC DNWt180 DNWb180; χNWa] +function _contract_site_large((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true ket(network[_prev(r, end), c])[dNW; DNWt120 DNWt60 DNWt0 DNWt300 DWt60 DNWt180] * ket(network[_prev(r, end), _next(c, end)])[dNE; DNEt120 DNEt60 DNEt0 DNEt300 DNEt240 DNWt0] * + ket(network[r, _next(c, end)])[dE; DNEt300 DEt60 DEt0 DEt300 DEt240 DEt180] * ket(network[_next(r, end), c])[dSE; DSEt120 DEt240 DSEt0 DSEt300 DSEt240 DSEt180] * ket(network[_next(r, end), _prev(c, end)])[dSW; DSWt120 DSWt60 DSEt180 DSWt300 DSWt240 DSWt180] * + ket(network[r, _prev(c, end)])[dW; DWt120 DWt60 DWt0 DSWt120 DWt240 DWt180] * ket(network[r, c])[dCenter; DNWt300 DNEt240 DEt180 DSEt120 DSWt60 DWt0] * + conj(bra(network[_prev(r, end), c])[dNW; DNWb120 DNWb60 DNWb0 DNWb300 DWb60 DNWb180]) * conj(bra(network[_prev(r, end), _next(c, end)])[dNE; DNEb120 DNEb60 DNEb0 DNEb300 DNEb240 DNWb0]) * + conj(bra(network[r, _next(c, end)])[dE; DNEb300 DEb60 DEb0 DEb300 DEb240 DEb180]) * conj(bra(network[_next(r, end), c])[dSE; DSEb120 DEb240 DSEb0 DSEb300 DSEb240 DSEb180]) * conj(bra(network[_next(r, end), _prev(c, end)])[dSW; DSWb120 DSWb60 DSEb180 DSWb300 DSWb240 DSWb180]) * + conj(bra(network[r, _prev(c, end)])[dW; DWb120 DWb60 DWb0 DSWb120 DWb240 DWb180]) * conj(bra(network[r, c])[dCenter; DNWb300 DNEb240 DEb180 DSEb120 DSWb60 DWb0]) * + env.C[1, _prev(r - 1, end), c][χNWa DNWt120 DNWb120; χNb] * env.Eb[1, _prev(r - 1, end), _next(c, end)][χNb DNWt60 DNWb60; χNC] * env.Ea[1, _prev(r - 1, end), _next(c, end)][χNC DNEt120 DNEb120; χNa] * + env.C[2, _prev(r - 1, end), _next(c + 1, end)][χNa DNEt60 DNEb60; χNEb] * env.Eb[2, _prev(r, end), _next(c + 1, end)][χNEb DNEt0 DNEb0; χNEC] * env.Ea[2, _prev(r, end), _next(c + 1, end)][χNEC DEt60 DEb60; χNEa] * + env.C[3, r, _next(c + 1, end)][χNEa DEt0 DEb0; χSEb] * env.Eb[3, _next(r, end), _next(c, end)][χSEb DEt300 DEb300; χSEC] * env.Ea[3, _next(r, end), _next(c, end)][χSEC DSEt0 DSEb0; χSEa] * + env.C[4, _next(r + 1, end), c][χSEa DSEt300 DSEb300; χSb] * env.Eb[4, _next(r + 1, end), _prev(c, end)][χSb DSEt240 DSEb240; χSC] * env.Ea[4, _next(r + 1, end), _prev(c, end)][χSC DSWt300 DSWb300; χSa] * + env.C[5, _next(r + 1, end), _prev(c - 1, end)][χSa DSWt240 DSWb240; χSWb] * env.Eb[5, _next(r, end), _prev(c - 1, end)][χSWb DSWt180 DSWb180; χSWC] * env.Ea[5, _next(r, end), _prev(c - 1, end)][χSWC DWt240 DWb240; χSWa] * + env.C[6, r, _prev(c - 1, end)][χSWa DWt180 DWb180; χNWb] * env.Eb[6, _prev(r, end), _prev(c, end)][χNWb DWt120 DWb120; χNWC] * env.Ea[6, _prev(r, end), _prev(c, end)][χNWC DNWt180 DNWb180; χNWa] end -function _contract_corners((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true env.C[1,_prev(r,end),c][χNW Dt120 Db120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN Dt60 Db60; χNE] * env.C[3,r,_next(c,end)][χNE Dt0 Db0; χSE] * - env.C[4,_next(r,end),c][χSE Dt300 Db300; χS] * env.C[5,_next(r,end),_prev(c,end)][χS Dt240 Db240; χSW] * env.C[6,r,_prev(c,end)][χSW Dt180 Db180; χNW] * - ket(network[r,c])[d; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r,c])[d; Db120 Db60 Db0 Db300 Db240 Db180]) +function _contract_corners((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + return @tensor opt = true env.C[1, _prev(r, end), c][χNW Dt120 Db120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN Dt60 Db60; χNE] * env.C[3, r, _next(c, end)][χNE Dt0 Db0; χSE] * + env.C[4, _next(r, end), c][χSE Dt300 Db300; χS] * env.C[5, _next(r, end), _prev(c, end)][χS Dt240 Db240; χSW] * env.C[6, r, _prev(c, end)][χSW Dt180 Db180; χNW] * + ket(network[r, c])[d; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r, c])[d; Db120 Db60 Db0 Db300 Db240 Db180]) end -function _contract_corners((r,c)::Tuple{Int,Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E,S,1,1}) where {P <: PEPSSandwichTriangular, E, S} - return @tensor opt = true env.C[1,_prev(r,end),c][χNW Dt120 Db120; χN] * env.C[2,_prev(r,end),_next(c,end)][χN Dt60 Db60; χNE] * env.C[3,r,_next(c,end)][χNE Dt0 Db0; χSE] * - env.C[4,_next(r,end),c][χSE Dt300 Db300; χS] * env.C[5,_next(r,end),_prev(c,end)][χS Dt240 Db240; χSW] * env.C[6,r,_prev(c,end)][χSW Dt180 Db180; χNW] * - ket(network[r,c])[dt; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r,c])[db; Db120 Db60 Db0 Db300 Db240 Db180]) * op[db; dt] +function _contract_corners((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E, S, 1, 1}) where {P <: PEPSSandwichTriangular, E, S} + return @tensor opt = true env.C[1, _prev(r, end), c][χNW Dt120 Db120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN Dt60 Db60; χNE] * env.C[3, r, _next(c, end)][χNE Dt0 Db0; χSE] * + env.C[4, _next(r, end), c][χSE Dt300 Db300; χS] * env.C[5, _next(r, end), _prev(c, end)][χS Dt240 Db240; χSW] * env.C[6, r, _prev(c, end)][χSW Dt180 Db180; χNW] * + ket(network[r, c])[dt; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r, c])[db; Db120 Db60 Db0 Db300 Db240 Db180]) * op[db; dt] end function energy(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, onesite_op, twosite_op) where {P <: PEPSSandwichTriangular} return sum(Iterators.product(axes(network)...)) do (r, c) - expval_onesite = _contract_corners((r,c), network, env, onesite_op) / _contract_corners((r,c), network, env) - expval_twosite = _contract_edges_0((r,c), network, env, twosite_op) / _contract_edges_0((r,c), network, env) + expval_onesite = _contract_corners((r, c), network, env, onesite_op) / _contract_corners((r, c), network, env) + expval_twosite = _contract_edges_0((r, c), network, env, twosite_op) / _contract_edges_0((r, c), network, env) return expval_onesite + expval_twosite end end diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index c71647d42..d55c3ce48 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -48,7 +48,7 @@ end unitcell = (2, 2) for (sandwich, vspace) in zip(sandwiches, [vspace, vspace ⊗ vspace']) - # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) + # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) env, info = leading_boundary(env₀, network, alg) @@ -97,7 +97,7 @@ end # ZZ = rmul!(PEPSKit.σᶻᶻ(T, S), -J) # X = rmul!(PEPSKit.σˣ(T, S), g * -J) - + # vspace = ℂ^3 # envspace = ℂ^χ # ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') From 21899c2152db92a38f01840313566c796d44ab6c Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 24 Feb 2026 14:04:21 +0100 Subject: [PATCH 06/13] add triangular test --- test/ctmrg/triangular.jl | 226 +++++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index d55c3ce48..2e1f2ffcd 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -29,129 +29,129 @@ function classical_ising_triangular(β) return TensorMap(o2, ℂ^2 * ℂ^2 * ℂ^2, ℂ^2 * ℂ^2 * ℂ^2) end -@testset "CTM_triangular - Random tensor" begin - for conditioning in [true false] - for projector_alg in [:twothirds :full] - Random.seed!(79413165445) - alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) - eltype = ComplexF64 - χ = 7 - pspace = ℂ^2 - vspace = ℂ^3 - envspace = ℂ^χ - - ket = randn(eltype, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') - bra = copy(ket) - pf = randn(eltype, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) - sandwiches = [pf, (ket, bra)] - # sandwiches = [(ket, bra), pf] - unitcell = (2, 2) - - for (sandwich, vspace) in zip(sandwiches, [vspace, vspace ⊗ vspace']) - # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) - network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) - env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) - env, info = leading_boundary(env₀, network, alg) - @test info.convergence_metric < 1.0 - end - end - end -end - -@testset "CTM_triangular - Classical Ising" begin - for conditioning in [true false] - for projector_alg in [:twothirds :full] - Random.seed!(156484561351) - - alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) - unitcell = (1, 1) - T = classical_ising_triangular(ising_βc_triangular) - pf = InfiniteTriangularNetwork(fill(T, unitcell)) - - χ = 20 - vspace = codomain(T)[1] - envspace = ℂ^χ - eltype = Float64 - env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) - env, info = leading_boundary(env₀, pf, alg) - - nw_value = network_value(pf, env) - lz = real(log(nw_value)) - fs = lz * -1 / ising_βc_triangular - @test fs ≈ f_onsager_triangular rtol = 1.0e-4 - end - end -end - -# @testset "CTM_triangular - Differentiability" begin +# @testset "CTM_triangular - Random tensor" begin # for conditioning in [true false] # for projector_alg in [:twothirds :full] -# ctm_alg = SimultaneousCTMRGTriangular(; conditioning, projector_alg) -# T = ComplexF64 -# S = Trivial +# Random.seed!(79413165445) +# alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) +# eltype = ComplexF64 # χ = 7 # pspace = ℂ^2 - -# J = 1.0 -# g = 2.5 - -# ZZ = rmul!(PEPSKit.σᶻᶻ(T, S), -J) -# X = rmul!(PEPSKit.σˣ(T, S), g * -J) - # vspace = ℂ^3 # envspace = ℂ^χ -# ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') -# bra = copy(ket) -# unitcell = (1, 1) -# network = InfiniteTriangularNetwork(fill((ket, bra), unitcell)) -# env₀ = CTMRGEnvTriangular(randn, T, vspace ⊗ vspace', envspace; unitcell) - -# env, info = leading_boundary(env₀, network, ctm_alg) -# E₀ = PEPSKit.energy(network, env, X, ZZ) -# optimizer_alg = LBFGS(4; maxiter = 10) -# real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) -# reuse_env = true -# peps₀ = InfinitePEPSTriangular(copy(ket)) +# ket = randn(eltype, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') +# bra = copy(ket) +# pf = randn(eltype, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) +# sandwiches = [pf, (ket, bra)] +# # sandwiches = [(ket, bra), pf] +# unitcell = (2, 2) + +# for (sandwich, vspace) in zip(sandwiches, [vspace, vspace ⊗ vspace']) +# # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) +# network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) +# env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) +# env, info = leading_boundary(env₀, network, alg) +# @test info.convergence_metric < 1.0 +# end +# end +# end +# end -# function peps_retract(x, η, α) -# peps = x[1] -# env = deepcopy(x[2]) +# @testset "CTM_triangular - Classical Ising" begin +# for conditioning in [true false] +# for projector_alg in [:twothirds :full] +# Random.seed!(156484561351) -# retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) -# peps´ = InfinitePEPS(map(first, retractions)) -# ξ = InfinitePEPS(map(last, retractions)) +# alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) +# unitcell = (1, 1) +# T = classical_ising_triangular(ising_βc_triangular) +# pf = InfiniteTriangularNetwork(fill(T, unitcell)) -# return (peps´, env), ξ -# end -# retract = peps_retract - -# # alg = PEPSOptimize() -# gradtol = 1e-3 -# gradient_alg = EigSolver(; solver_alg=Arnoldi(; tol=gradtol, eager=true), iterscheme=:fixed) -# # optimize operator cost function -# (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( -# (peps₀, env₀), optimizer_alg; -# retract, inner = real_inner, -# ) do (peps, env) -# start_time = time_ns() -# E, gs = withgradient(peps) do ψ -# env′, info = PEPSKit.hook_pullback( -# leading_boundary, env, ψ, ctm_alg; -# alg_rrule = gradient_alg, -# ) -# # ignore_derivatives() do -# # reuse_env && update!(env, env′) -# # push!(truncation_errors, info.truncation_error) -# # push!(condition_numbers, info.condition_number) -# # end -# return energy(ψ, env′, operator_onesite, operator_twosite) -# end -# g = only(gs) # `withgradient` returns tuple of gradients `gs` -# push!(gradnorms_unitcell, norm.(g.A)) -# push!(times, (time_ns() - start_time) * 1.0e-9) -# return E, g -# end +# χ = 20 +# vspace = codomain(T)[1] +# envspace = ℂ^χ +# eltype = Float64 +# env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) +# env, info = leading_boundary(env₀, pf, alg) + +# nw_value = network_value(pf, env) +# lz = real(log(nw_value)) +# fs = lz * -1 / ising_βc_triangular +# @test fs ≈ f_onsager_triangular rtol = 1.0e-4 # end # end # end + +@testset "CTM_triangular - Differentiability" begin + for conditioning in [true false] + for projector_alg in [:twothirds :full] + ctm_alg = SimultaneousCTMRGTriangular(; conditioning, projector_alg) + T = ComplexF64 + S = Trivial + χ = 7 + pspace = ℂ^2 + + J = 1.0 + g = 2.5 + + ZZ = rmul!(PEPSKit.σᶻᶻ(T, S), -J) + X = rmul!(PEPSKit.σˣ(T, S), g * -J) + + vspace = ℂ^3 + envspace = ℂ^χ + ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') + bra = copy(ket) + + unitcell = (1, 1) + network = InfiniteTriangularNetwork(fill((ket, bra), unitcell)) + env₀ = CTMRGEnvTriangular(randn, T, vspace ⊗ vspace', envspace; unitcell) + + env, info = leading_boundary(env₀, network, ctm_alg) + E₀ = PEPSKit.energy(network, env, X, ZZ) + optimizer_alg = LBFGS(4; maxiter = 10) + real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) + reuse_env = true + peps₀ = InfinitePEPSTriangular(copy(ket)) + + function peps_retract(x, η, α) + peps = x[1] + env = deepcopy(x[2]) + + retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) + peps´ = InfinitePEPS(map(first, retractions)) + ξ = InfinitePEPS(map(last, retractions)) + + return (peps´, env), ξ + end + retract = peps_retract + + # alg = PEPSOptimize() + gradtol = 1.0e-3 + gradient_alg = EigSolver(; solver_alg = Arnoldi(; tol = gradtol, eager = true), iterscheme = :fixed) + # optimize operator cost function + (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( + (peps₀, env₀), optimizer_alg; + retract, inner = real_inner, + ) do (peps, env) + start_time = time_ns() + E, gs = withgradient(peps) do ψ + env′, info = PEPSKit.hook_pullback( + leading_boundary, env, ψ, ctm_alg; + alg_rrule = gradient_alg, + ) + # ignore_derivatives() do + # reuse_env && update!(env, env′) + # push!(truncation_errors, info.truncation_error) + # push!(condition_numbers, info.condition_number) + # end + return energy(ψ, env′, operator_onesite, operator_twosite) + end + g = only(gs) # `withgradient` returns tuple of gradients `gs` + push!(gradnorms_unitcell, norm.(g.A)) + push!(times, (time_ns() - start_time) * 1.0e-9) + return E, g + end + end + end +end From 71b333652c68293bb6fdcdca6dbca977dec31412 Mon Sep 17 00:00:00 2001 From: leburgel Date: Mon, 2 Mar 2026 13:50:56 +0100 Subject: [PATCH 07/13] Patches for differentiability --- src/PEPSKit.jl | 4 + .../ctmrg_contractions_triangular.jl | 94 ++++-- .../ctmrg_triangular/simultaneous.jl | 139 +++++---- .../fixed_point_differentiation.jl | 38 +-- src/algorithms/select_algorithm.jl | 7 - src/algorithms/toolbox_triangular.jl | 61 ++-- .../ctmrg_environments_triangular.jl | 128 ++++++--- src/networks/infinitetriangularnetwork.jl | 18 -- src/states/infinitepepstriangular.jl | 43 +-- test/ctmrg/triangular.jl | 269 ++++++++++-------- 10 files changed, 436 insertions(+), 365 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 0e222aec7..0bdf99147 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -144,6 +144,10 @@ include("algorithms/ctmrg/gaugefix.jl") include("algorithms/ctmrg/c4v.jl") include("algorithms/ctmrg/initialization.jl") +include("algorithms/truncation/truncationschemes.jl") +include("algorithms/truncation/fullenv_truncation.jl") +include("algorithms/truncation/bond_truncation.jl") + include("algorithms/contractions/ctmrg_contractions_triangular.jl") include("algorithms/ctmrg_triangular/ctmrg.jl") include("algorithms/ctmrg_triangular/utility.jl") diff --git a/src/algorithms/contractions/ctmrg_contractions_triangular.jl b/src/algorithms/contractions/ctmrg_contractions_triangular.jl index 1ef921c8b..42b777923 100644 --- a/src/algorithms/contractions/ctmrg_contractions_triangular.jl +++ b/src/algorithms/contractions/ctmrg_contractions_triangular.jl @@ -1,58 +1,104 @@ function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, dir::Int, r::Int, c::Int) where {P <: PFTensorTriangular} - @tensor opt = true mat[-1 -2; -3 -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3; 2] * - env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4; -3] * rotl60(network[r, c], mod(dir - 1, 6))[7 -2 -4; 5 3 4] + @tensor opt = true mat[-1 -2; -3 -4] := + env.C[_coordinates(dir, 0, r, c, size(network))...][6 5; 1] * + env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3; 2] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7; 6] * + env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4; -3] * + rotl60(network[r, c], mod(dir - 1, 6))[7 -2 -4; 5 3 4] return mat end function build_double_corner_matrix_triangular(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, dir::Int, r::Int, c::Int) where {P <: PEPSSandwichTriangular} - @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := env.C[_coordinates(dir, 0, r, c, size(network))...][6 5 8; 1] * env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3 9; 2] * - env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7 10; 6] * env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4 11; -4] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 5 3 4 -5 -2 7] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 8 9 11 -6 -3 10]) + O_r_c = network[r, c] + @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := + env.C[_coordinates(dir, 0, r, c, size(network))...][6 5 8; 1] * + env.C[_coordinates(dir + 1, 0, r, c, size(network))...][1 3 9; 2] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][-1 7 10; 6] * + env.Eb[_coordinates(dir + 1, 0, r, c, size(network))...][2 4 11; -4] * + rotl60(ket(O_r_c), mod(dir - 1, 6))[12; 5 3 4 -5 -2 7] * + conj(rotl60(bra(O_r_c), mod(dir - 1, 6))[12; 8 9 11 -6 -3 10]) return mat end function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Pas, Pbs, dir, r, c) where {P <: PEPSSandwichTriangular} - @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := Pas[dir, r, c][-1; 1 2 8] * Pbs[dir, r, c][6 7 9; -4] * - env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5 11; 6] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[12; 3 5 7 -5 -2 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[12; 10 11 9 -6 -3 8]) + O_r_c = network[r, c] + @tensor opt = true mat[-1 -2 -3; -4 -5 -6] := + Pas[dir, r, c][-1; 1 2 8] * Pbs[dir, r, c][6 7 9; -4] * + env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 4] * + env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5 11; 6] * + rotl60(ket(O_r_c), mod(dir - 1, 6))[12; 3 5 7 -5 -2 2] * + conj(rotl60(bra(O_r_c), mod(dir - 1, 6))[12; 10 11 9 -6 -3 8]) return mat / norm(mat) end function semi_renormalize_edge(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Pas, Pbs, dir, r, c) where {P <: PFTensorTriangular} - @tensor opt = true mat[-1 -2; -3 -4] := Pas[dir, r, c][-1; 1 2] * Pbs[dir, r, c][6 7; -3] * - env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3; 4] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5; 6] * rotl60(network[r, c], mod(dir - 1, 6))[2 -2 -4; 3 5 7] + @tensor opt = true mat[-1 -2; -3 -4] := + Pas[dir, r, c][-1; 1 2] * + Pbs[dir, r, c][6 7; -3] * + env.Ea[_coordinates(dir, 0, r, c, size(network))...][1 3; 4] * + env.Eb[_coordinates(dir, 1, r, c, size(network))...][4 5; 6] * + rotl60(network[r, c], mod(dir - 1, 6))[2 -2 -4; 3 5 7] return mat / norm(mat) end function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PEPSSandwichTriangular} - @tensor opt = true σL[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2 10; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3 11; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5 12; 4] * - Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9 13; -4] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7 14; 6] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[15 2 9 -2 7 5 3] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 13 -3 14 12 11]) - @tensor opt = true σR[-1; -2 -3 -4] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2 10; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3 11; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5 12; 6] * - Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7 13; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9 14; 8] * - rotl60(ket(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 9 2 3 5 7 -3] * conj(rotl60(bra(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...]), mod(dir - 1, 6))[15; 14 10 11 12 13 -4]) + O_r_c = network[r, c] + O_rp2_c = network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...] + @tensor opt = true σL[-1 -2 -3; -4] := + env.C[_coordinates(dir, 0, r, c, size(network))...][1 2 10; 8] * + env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3 11; 1] * + env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5 12; 4] * + Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9 13; -4] * + Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7 14; 6] * + rotl60(ket(O_r_c), mod(dir - 1, 6))[15 2 9 -2 7 5 3] * + conj(rotl60(bra(O_r_c), mod(dir - 1, 6))[15; 10 13 -3 14 12 11]) + @tensor opt = true σR[-1; -2 -3 -4] := + env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2 10; 1] * + env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3 11; 4] * + env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5 12; 6] * + Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7 13; -2] * + Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9 14; 8] * + rotl60(ket(O_rp2_c), mod(dir - 1, 6))[15; 9 2 3 5 7 -3] * + conj(rotl60(bra(O_rp2_c), mod(dir - 1, 6))[15; 14 10 11 12 13 -4]) return σL, σR end function build_halfinfinite_projectors(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) where {P <: PFTensorTriangular} - @tensor opt = true σL[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 2; 8] * env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3; 1] * env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5; 4] * - Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9; -3] * Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7; 6] * + @tensor opt = true σL[-1 -2; -3] := + env.C[_coordinates(dir, 0, r, c, size(network))...][1 2; 8] * + env.C[_coordinates(dir - 1, 0, r, c, size(network))...][4 3; 1] * + env.C[_coordinates(dir - 2, 0, r, c, size(network))...][6 5; 4] * + Ẽbs[_coordinates(dir, 1, r, c, size(network))...][8 9; -3] * + Ẽastr[_coordinates(dir - 3, 0, r, c, size(network))...][-1 7; 6] * rotl60(network[r, c], mod(dir - 1, 6))[3 5 7; 2 9 -2] - @tensor opt = true σR[-1; -2 -3] := env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2; 1] * env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3; 4] * env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5; 6] * - Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7; -2] * Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9; 8] * + @tensor opt = true σR[-1; -2 -3] := + env.C[_coordinates(dir + 1, 0, r, c, size(network))...][8 2; 1] * + env.C[_coordinates(dir + 2, 0, r, c, size(network))...][1 3; 4] * + env.C[_coordinates(dir + 3, 0, r, c, size(network))...][4 5; 6] * + Ẽbstr[_coordinates(dir + 3, -1, r, c, size(network))...][6 7; -2] * + Ẽas[_coordinates(dir, 0, r, c, size(network))...][-1 9; 8] * rotl60(network[_coordinates(dir + 2, 0, r, c, size(network))[2:3]...], mod(dir - 1, 6))[-3 7 5; 9 2 3] return σL, σR end function renormalize_corner_triangular((dir, r, c), network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PFTensorTriangular} - @tensor opt = true env_C_new[-1 -2; -3] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7; 8] * - rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] + @tensor opt = true env_C_new[-1 -2; -3] := + env.C[_coordinates(dir, 0, r, c, size(network))...][1 3; 6] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2; 1] * + env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7; 8] * + rotl60(network[r, c], mod(dir - 1, 6))[2 5 -2; 3 7 9] * + Pas[mod1(dir - 1, 6), r, c][-1; 4 5] * Pbs[dir, r, c][8 9; -3] return env_C_new end function renormalize_corner_triangular((dir, r, c), network::InfiniteTriangularNetwork{P}, env, Pas, Pbs) where {P <: PEPSSandwichTriangular} - @tensor opt = true env_C_new[-1 -2 -3; -4] := env.C[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 6] * env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2 11; 1] * env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7 12; 8] * - rotl60(ket(network[r, c]), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * conj(rotl60(bra(network[r, c]), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * + O_r_c = network[r, c] + @tensor opt = true env_C_new[-1 -2 -3; -4] := + env.C[_coordinates(dir, 0, r, c, size(network))...][1 3 10; 6] * + env.Ea[_coordinates(dir - 1, 0, r, c, size(network))...][4 2 11; 1] * + env.Eb[_coordinates(dir, 1, r, c, size(network))...][6 7 12; 8] * + rotl60(ket(O_r_c), mod(dir - 1, 6))[15; 3 7 9 -2 5 2] * + conj(rotl60(bra(O_r_c), mod(dir - 1, 6))[15; 10 12 14 -3 13 11]) * Pas[mod1(dir - 1, 6), r, c][-1; 4 5 13] * Pbs[dir, r, c][8 9 14; -4] return env_C_new end diff --git a/src/algorithms/ctmrg_triangular/simultaneous.jl b/src/algorithms/ctmrg_triangular/simultaneous.jl index 81b4f980c..90e1321bb 100644 --- a/src/algorithms/ctmrg_triangular/simultaneous.jl +++ b/src/algorithms/ctmrg_triangular/simultaneous.jl @@ -32,13 +32,13 @@ function ctmrg_iteration(network::InfiniteTriangularNetwork, env::CTMRGEnvTriang Pas, Pbs, S = calculate_projectors(network, env, trunc, alg.projector_alg) env = renormalize_corners!(network, env, Pas, Pbs) - env = normalize_corners!(env) + env = normalize_corners(env) Ẽas, Ẽbs, Ẽastr, Ẽbstr = semi_renormalize(network, env, Pas, Pbs, trunc) Qas, Qbs = build_matrix_second_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; alg.conditioning) env = renormalize_edges(env, Ẽas, Ẽbs, Qas, Qbs) - normalize_edges!(env) + env = normalize_edges(env) return env, S end @@ -52,48 +52,49 @@ function calculate_projectors(network, env, trunc, projector_alg) end end +# TODO: remove once this once everything is plumbed through a proper projector algorithm +_truncation_strategy(trunc::TruncationStrategy, _) = trunc +function _truncation_strategy(::FixedSpaceTruncation, edge) + tspace = space(edge, 1) + return isdual(tspace) ? truncspace(flip(tspace)) : truncspace(tspace) +end + function calculate_twothirds_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, DiagonalTensorMap{real(E), S}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) - ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) - ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) + projectors = dtmap(eachcoordinate(network, 1:6)) do (dir, r, c) + ρL = build_double_corner_matrix_triangular(network, env, _prev(dir, 6), r, c) + ρR = build_double_corner_matrix_triangular(network, env, _next(dir, 6), r, c) ρρ = ρL * ρR ρρ /= norm(ρρ) + trunc = _truncation_strategy(trunc, env.Ea[dir, r, c]) U, S, V = svd_trunc(ρρ; trunc) + iqsrtS = sdiag_pow(S, -0.5) - Pb = ρR * V' * sdiag_pow(S, -1 / 2) - Pa = sdiag_pow(S, -1 / 2) * U' * ρL + Pb = ρR * V' * iqsrtS + Pa = iqsrtS * U' * ρL return Pa, Pb, S end return getindex.(projectors, 1), getindex.(projectors, 2), getindex.(projectors, 3) end function calculate_full_projectors(network::InfiniteTriangularNetwork{P}, env, trunc) where {P} - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 1, 2 + (network[1, 1] isa Tuple)}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, DiagonalTensorMap{real(E), S}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + projectors = dtmap(eachcoordinate(network, 1:6)) do (dir, r, c) ρL = build_double_corner_matrix_triangular(network, env, mod1(dir - 1, 6), r, c) ρR = build_double_corner_matrix_triangular(network, env, mod1(dir + 1, 6), r, c) ρ̄ = build_double_corner_matrix_triangular(network, env, mod1(dir + 3, 6), r, c) ρ̄ /= norm(ρ̄) Ū, S̄, V̄ᴴ = svd_full(ρ̄) - ρ̄ᴿ = Ū * sqrt(S̄) - ρ̄ᴸ = sqrt(S̄) * V̄ᴴ + sqrtS̄ = sdiag_pow(S̄, 0.5) + ρ̄ᴿ = Ū * sqrtS̄ + ρ̄ᴸ = sqrtS̄ * V̄ᴴ ρρ = ρ̄ᴸ * ρL * ρR * ρ̄ᴿ ρρ /= norm(ρρ) + trunc = _truncation_strategy(trunc, env.Ea[dir, r, c]) U, S, Vᴴ = svd_trunc(ρρ; trunc) - - Pb = ρR * ρ̄ᴿ * Vᴴ' * sdiag_pow(S, -1 / 2) - Pa = sdiag_pow(S, -1 / 2) * U' * ρ̄ᴸ * ρL + isqrtS = sdiag_pow(S, -0.5) + Pb = ρR * ρ̄ᴿ * Vᴴ' * isqrtS + Pa = isqrtS * U' * ρ̄ᴸ * ρL return Pa, Pb, S end @@ -127,22 +128,21 @@ function _permute_edge(t::Union{T1, T2}) where {E, S, T1 <: AbstractTensorMap{E, end function semi_renormalize(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular, Pas, Pbs, trunc) - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}, AbstractTensorMap{E, S, 2 + (network[1, 1] isa Tuple), 1}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + projectors = dtmap(eachcoordinate(network, 1:6)) do (dir, r, c) mat = semi_renormalize_edge(network, env, Pas, Pbs, dir, r, c) U, S, V = svd_full(mat) - Ẽb = U * sqrt(S) - Ẽa = _permute_edge(sqrt(S) * V) + sqrtS = sdiag_pow(S, 0.5) + Ẽb = U * sqrtS + Ẽa = _permute_edge(sqrtS * V) + + trunc = _truncation_strategy(trunc, env.Ea[dir, r, c]) Utr, Str, Vtr = svd_trunc(mat; trunc) - Ẽbtr = Utr * sqrt(Str) - Ẽatr = _permute_edge(sqrt(Str) * Vtr) + sqrt_Str = sdiag_pow(Str, 0.5) + Ẽbtr = Utr * sqrt_Str + Ẽatr = _permute_edge(sqrt_Str * Vtr) return Ẽa, Ẽb, Ẽatr, Ẽbtr end @@ -150,57 +150,56 @@ function semi_renormalize(network::InfiniteTriangularNetwork, env::CTMRGEnvTrian end function build_matrix_second_projectors(network::InfiniteTriangularNetwork, env::CTMRGEnvTriangular, Ẽas, Ẽbs, Ẽastr, Ẽbstr, trunc; conditioning = true) - coordinates = eachcoordinate(network, 1:6) - E = scalartype(env.C[1, 1, 1]) - S = spacetype(env.C[1, 1, 1]) - T_proj = Tuple{AbstractTensorMap{E, S, 1, 1}, AbstractTensorMap{E, S, 1, 1}} - projectors′ = similar(coordinates, T_proj) - projectors = dtmap!!(projectors′, coordinates) do (dir, r, c) + projectors = dtmap(eachcoordinate(network, 1:6)) do (dir, r, c) + trunc = _truncation_strategy(trunc, env.Ea[dir, r, c]) σL, σR = build_halfinfinite_projectors(network, env, Ẽas, Ẽbs, Ẽastr, Ẽbstr, dir, r, c) if conditioning σL /= norm(σL) σR /= norm(σR) - UL, SL, VLᴴ = svd_full(σL) - UR, SR, VRᴴ = svd_full(σR) + _, SL, VLᴴ = svd_full(σL) + UR, SR, _ = svd_full(σR) - FLU = sqrt(SL) * VLᴴ - FRU = UR * sqrt(SR) + sqrtSL = sdiag_pow(SL, 0.5) + sqrtSR = sdiag_pow(SR, 0.5) + + FLU = sqrtSL * VLᴴ + FRU = UR * sqrtSR mat = FLU * FRU mat /= norm(mat) WU, SU, QUᴴ = svd_trunc(mat; trunc) + isqrtSU = sdiag_pow(SU, -0.5) - Qa = sdiag_pow(SU, -1 / 2) * WU' * FLU - Qb = FRU * QUᴴ' * sdiag_pow(SU, -1 / 2) + Qa = isqrtSU * WU' * FLU + Qb = FRU * QUᴴ' * isqrtSU else mat = σL * σR mat /= norm(mat) U, S, V = svd_trunc(mat; trunc) - Qa = sdiag_pow(S, -1 / 2) * U' * σL - Qb = σR * V' * sdiag_pow(S, -1 / 2) + isqrtS = sdiag_pow(S, -0.5) + Qa = isqrtS * U' * σL + Qb = σR * V' * isqrtS end return Qa, Qb end - return getindex.(projectors, 1), getindex.(projectors, 2) + PL = map(x -> x[1], projectors) + PR = map(x -> x[2], projectors) + return PL, PR end function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 3, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} - coordinates = collect(Iterators.product(axes(env.Ea)...)) - T_proj = Tuple{AbstractTensorMap{E, S, 3, 1}, AbstractTensorMap{E, S, 3, 1}} - new_edges′ = similar(env.Ea, T_proj) - new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) + new_edges = dtmap(eachcoordinate(env, 1:6)) do (dir, r, c) Eb_new = Ẽbs[dir, r, c] * Qbs[dir, r, c] Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,), (2, 3, 4))), ((1, 2, 3), (4,))) return Ea_new, Eb_new end - return CTMRGEnvTriangular(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) + Ea = map(x -> x[1], new_edges) + Eb = map(x -> x[2], new_edges) + return CTMRGEnvTriangular(env.C, Ea, Eb) end -function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {E, S, T1 <: AbstractTensorMap{E, S, 2, 1}, T2 <: AbstractTensorMap{E, S, 1, 1}} - coordinates = collect(Iterators.product(axes(env.Ea)...)) - T_proj = Tuple{AbstractTensorMap{E, S, 2, 1}, AbstractTensorMap{E, S, 2, 1}} - new_edges′ = similar(env.Ea, T_proj) - new_edges = dtmap!!(new_edges′, coordinates) do (dir, r, c) +function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs::Array{T1, 3}, Qas::Array{T2, 3}, Qbs::Array{T2, 3}) where {T1 <: CTMRGEdgeTensor, T2 <: CTMRGCornerTensor} + new_edges = dtmap(eachcoordinate(env, 1:6)) do (dir, r, c) Eb_new = Ẽbs[dir, r, c] * Qbs[dir, r, c] Ea_new = permute(Qas[dir, r, c] * permute(Ẽas[dir, r, c], ((1,), (2, 3))), ((1, 2), (3,))) return Ea_new, Eb_new @@ -208,23 +207,21 @@ function renormalize_edges(env::CTMRGEnvTriangular, Ẽas::Array{T1, 3}, Ẽbs:: return CTMRGEnvTriangular(env.C, getindex.(new_edges, 1), getindex.(new_edges, 2)) end -function normalize_corners!(env) - coordinates = collect(Iterators.product(axes(env.Ea)...)) - new_corners′ = similar(env.C, typeof(env.C[1, 1, 1])) - new_corners = dtmap!!(new_corners′, coordinates) do (dir, r, c) - env_C_new = env.C[dir, r, c] / norm(env.C[dir, r, c]) - return env_C_new +function normalize_corners(env::CTMRGEnvTriangular) + C_normalized = map(env.C) do C + return C / norm(C) end - return CTMRGEnvTriangular(new_corners, env.Ea, env.Eb) + return CTMRGEnvTriangular(C_normalized, env.Ea, env.Eb) end -function normalize_edges!(env) - (r, c) = (1, 1) - for dir in 1:6 - env.Ea[dir, r, c] /= norm(env.Ea[dir, r, c]) - env.Eb[dir, r, c] /= norm(env.Eb[dir, r, c]) +function normalize_edges(env::CTMRGEnvTriangular) + Ea_normalized = map(env.Ea) do e + return e / norm(e) + end + Eb_normalized = map(env.Eb) do e + return e / norm(e) end - return env + return CTMRGEnvTriangular(env.C, Ea_normalized, Eb_normalized) end function calculate_error(Ss, Ss_prev) diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index 3e55bad12..cb2436d9e 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -213,7 +213,7 @@ function _rrule( ::typeof(leading_boundary), envinit, state, - alg::Union{CTMRGAlgorithm, CTMRGAlgorithmTriangular}, + alg::CTMRGAlgorithm, ) env, info = leading_boundary(envinit, state, alg) alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation @@ -279,42 +279,16 @@ function _rrule( end function _rrule( - gradmode::GradMode{:fixed}, + gradmode::GradMode, config::RuleConfig, ::typeof(MPSKit.leading_boundary), envinit, state, - alg::SimultaneousCTMRGTriangular, + alg::CTMRGAlgorithmTriangular, ) - env, = leading_boundary(envinit, state, alg) - alg_fixed = @set alg.trunctype = :FixedSpaceTruncation # fix spaces during differentiation - env_conv, info = ctmrg_iteration(InfiniteTriangularNetwork(state), env, alg_fixed) - env_fixed, signs = gauge_fix(env, env_conv) - - # Fix SVD - svd_alg_fixed = _fix_svd_algorithm(alg.projector_alg.svd_alg, signs, info) - alg_fixed = @set alg.projector_alg.svd_alg = svd_alg_fixed - alg_fixed = @set alg_fixed.projector_alg.trunc = notrunc() - - function leading_boundary_fixed_pullback((Δenv′, Δinfo)) - Δenv = unthunk(Δenv′) - - function f(A, x) - return fix_global_phases( - x, ctmrg_iteration(InfiniteTriangularNetwork(A), x, alg_fixed)[1] - ) - end - _, env_vjp = rrule_via_ad(config, f, state, env_fixed) - - # evaluate the geometric sum - ∂f∂A(x)::typeof(state) = env_vjp(x)[2] - ∂f∂x(x)::typeof(env) = env_vjp(x)[3] - ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) - - return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() - end - - return (env_fixed, info), leading_boundary_fixed_pullback + msg = "Fixed-point differentiation is not available for `CTMRGAlgorithmTriangular`, \ + since gauge fixing for triangular CTMRG is currently not implemented." + throw(ArgumentError(msg)) end function _fix_svd_algorithm(alg::SVDAdjoint, signs, info) diff --git a/src/algorithms/select_algorithm.jl b/src/algorithms/select_algorithm.jl index 7faf9564f..c121fb3fd 100644 --- a/src/algorithms/select_algorithm.jl +++ b/src/algorithms/select_algorithm.jl @@ -158,10 +158,3 @@ function select_algorithm( return CTMRGAlgorithm(; alg, tol, verbosity, decomposition_alg, kwargs...) end - -function select_algorithm( - ::typeof(leading_boundary), - env₀::CTMRGEnvTriangular - ) - return SimultaneousCTMRGTria() -end diff --git a/src/algorithms/toolbox_triangular.jl b/src/algorithms/toolbox_triangular.jl index ab67be0ef..99da822fc 100644 --- a/src/algorithms/toolbox_triangular.jl +++ b/src/algorithms/toolbox_triangular.jl @@ -54,8 +54,10 @@ end ### For function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[r, c])[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, _next(c, end)])[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * - conj(bra(network[r, c])[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, _next(c, end)])[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + O_r_c = network[r, c] + O_r_cn = network[r, _next(c, end)] + return @tensor opt = true ket(O_r_c)[dL; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(O_r_cn)[dR; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(O_r_c)[dL; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(O_r_cn)[dR; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * env.C[1, _prev(r, end), c][χNW DLt120 DLb120; χNa] * env.C[2, _prev(r, end), _next(c + 1, end)][χNb DRt60 DRb60; χNE] * env.C[3, r, _next(c + 1, end)][χNE DRt0 DRb0; χSE] * env.C[4, _next(r, end), _next(c, end)][χSE DRt300 DRb300; χSa] * env.C[5, _next(r, end), _prev(c, end)][χSb DLt240 DLb240; χSW] * env.C[6, r, _prev(c, end)][χSW DLt180 DLb180; χNW] * env.Eb[1, _prev(r, end), _next(c, end)][χNa DLt60 DLb60; χNC] * env.Ea[1, _prev(r, end), _next(c, end)][χNC DRt120 DRb120; χNb] * @@ -63,8 +65,10 @@ function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularN end function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E, S, 2, 2}) where {P <: PEPSSandwichTriangular, E, S} - return @tensor opt = true ket(network[r, c])[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(network[r, _next(c, end)])[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * - conj(bra(network[r, c])[dLb; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(network[r, _next(c, end)])[dRb; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * + O_r_c = network[r, c] + O_r_cn = network[r, _next(c, end)] + return @tensor opt = true ket(O_r_c)[dLt; DLt120 DLt60 DLt0 DLt300 DLt240 DLt180] * ket(O_r_cn)[dRt; DRt120 DRt60 DRt0 DRt300 DRt240 DLt0] * + conj(bra(O_r_c)[dLb; DLb120 DLb60 DLb0 DLb300 DLb240 DLb180]) * conj(bra(O_r_cn)[dRb; DRb120 DRb60 DRb0 DRb300 DRb240 DLb0]) * env.C[1, _prev(r, end), c][χNW DLt120 DLb120; χNa] * env.C[2, _prev(r, end), _next(c + 1, end)][χNb DRt60 DRb60; χNE] * env.C[3, r, _next(c + 1, end)][χNE DRt0 DRb0; χSE] * env.C[4, _next(r, end), _next(c, end)][χSE DRt300 DRb300; χSa] * env.C[5, _next(r, end), _prev(c, end)][χSb DLt240 DLb240; χSW] * env.C[6, r, _prev(c, end)][χSW DLt180 DLb180; χNW] * env.Eb[1, _prev(r, end), _next(c, end)][χNa DLt60 DLb60; χNC] * env.Ea[1, _prev(r, end), _next(c, end)][χNC DRt120 DRb120; χNb] * @@ -73,8 +77,10 @@ function _contract_edges_0((r, c)::Tuple{Int, Int}, network::InfiniteTriangularN end function _contract_edges_60((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[r, c])[dTR; DTRt120 DTRt60 DTRt0 DTRt300 DBLt60 DTRt180] * ket(network[_next(r, end), _prev(c, end)])[dBL; DBLt120 DBLt60 DBLt0 DBLt300 DBLt240 DBLt180] * - conj(bra(network[r, c])[dTR; DTRb120 DTRb60 DTRb0 DTRb300 DBLb60 DTRb180]) * conj(bra(network[_next(r, end), _prev(c, end)])[dBL; DBLb120 DBLb60 DBLb0 DBLb300 DBLb240 DBLb180]) * + O_r_c = network[r, c] + O_rn_cn = network[_next(r, end), _next(c, end)] + return @tensor opt = true ket(O_r_c)[dTR; DTRt120 DTRt60 DTRt0 DTRt300 DBLt60 DTRt180] * ket(O_rn_cn)[dBL; DBLt120 DBLt60 DBLt0 DBLt300 DBLt240 DBLt180] * + conj(bra(O_r_c)[dTR; DTRb120 DTRb60 DTRb0 DTRb300 DBLb60 DTRb180]) * conj(bra(O_rn_cn)[dBL; DBLb120 DBLb60 DBLb0 DBLb300 DBLb240 DBLb180]) * env.C[1, _prev(r, end), c][χNWb DTRt120 DTRb120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN DTRt60 DTRb60; χNE] * env.C[3, r, _next(c, end)][χNE DTRt0 DTRb0; χSEa] * env.C[4, _next(r + 1, end), _prev(c, end)][χSEb DBLt300 DBLb300; χS] * env.C[5, _next(r + 1, end), _prev(c - 1, end)][χS DBLt240 DBLb240; χSW] * env.C[6, _next(r, end), _prev(c - 1, end)][χSW DBLt180 DBLb180; χNWa] * env.Eb[3, _next(r, end), c][χSEa DTRt300 DTRb300; χSEC] * env.Ea[3, _next(r, end), c][χSEC DBLt0 DBLb0; χSEb] * @@ -82,8 +88,10 @@ function _contract_edges_60((r, c)::Tuple{Int, Int}, network::InfiniteTriangular end function _contract_edges_120((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[r, c])[dTL; DTLt120 DTLt60 DTLt0 DTLt300 DTLt240 DTLt180] * ket(network[_next(r, end), c])[dBR; DTLt300 DBRt60 DBRt0 DBRt300 DBRt240 DBRt180] * - conj(bra(network[r, c])[dTL; DTLb120 DTLb60 DTLb0 DTLb300 DTLb240 DTLb180]) * conj(bra(network[_next(r, end), c])[dBR; DTLb300 DBRb60 DBRb0 DBRb300 DBRb240 DBRb180]) * + O_r_c = network[r, c] + O_rn_c = network[_next(r, end), c] + return @tensor opt = true ket(O_r_c)[dTL; DTLt120 DTLt60 DTLt0 DTLt300 DTLt240 DTLt180] * ket(O_rn_c)[dBR; DTLt300 DBRt60 DBRt0 DBRt300 DBRt240 DBRt180] * + conj(bra(O_r_c)[dTL; DTLb120 DTLb60 DTLb0 DTLb300 DTLb240 DTLb180]) * conj(bra(O_rn_c)[dBR; DTLb300 DBRb60 DBRb0 DBRb300 DBRb240 DBRb180]) * env.C[1, _prev(r, end), c][χNW DTLt120 DTLb120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN DTLt60 DTLb60; χNEa] * env.C[3, _next(r, end), _next(c, end)][χNEb DBRt0 DBRb0; χSE] * env.C[4, _next(r + 1, end), c][χSE DBRt300 DBRb300; χS] * env.C[5, _next(r + 1, end), _prev(c, end)][χS DBRt240 DBRb240; χSWa] * env.C[6, r, _prev(c, end)][χSWb DTLt180 DTLb180; χNW] * env.Eb[2, r, _next(c, end)][χNEa DTLt0 DTLb0; χNEC] * env.Ea[2, r, _next(c, end)][χNEC DBRt60 DBRb60; χNEb] * @@ -91,12 +99,19 @@ function _contract_edges_120((r, c)::Tuple{Int, Int}, network::InfiniteTriangula end function _contract_site_large((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} - return @tensor opt = true ket(network[_prev(r, end), c])[dNW; DNWt120 DNWt60 DNWt0 DNWt300 DWt60 DNWt180] * ket(network[_prev(r, end), _next(c, end)])[dNE; DNEt120 DNEt60 DNEt0 DNEt300 DNEt240 DNWt0] * - ket(network[r, _next(c, end)])[dE; DNEt300 DEt60 DEt0 DEt300 DEt240 DEt180] * ket(network[_next(r, end), c])[dSE; DSEt120 DEt240 DSEt0 DSEt300 DSEt240 DSEt180] * ket(network[_next(r, end), _prev(c, end)])[dSW; DSWt120 DSWt60 DSEt180 DSWt300 DSWt240 DSWt180] * - ket(network[r, _prev(c, end)])[dW; DWt120 DWt60 DWt0 DSWt120 DWt240 DWt180] * ket(network[r, c])[dCenter; DNWt300 DNEt240 DEt180 DSEt120 DSWt60 DWt0] * - conj(bra(network[_prev(r, end), c])[dNW; DNWb120 DNWb60 DNWb0 DNWb300 DWb60 DNWb180]) * conj(bra(network[_prev(r, end), _next(c, end)])[dNE; DNEb120 DNEb60 DNEb0 DNEb300 DNEb240 DNWb0]) * - conj(bra(network[r, _next(c, end)])[dE; DNEb300 DEb60 DEb0 DEb300 DEb240 DEb180]) * conj(bra(network[_next(r, end), c])[dSE; DSEb120 DEb240 DSEb0 DSEb300 DSEb240 DSEb180]) * conj(bra(network[_next(r, end), _prev(c, end)])[dSW; DSWb120 DSWb60 DSEb180 DSWb300 DSWb240 DSWb180]) * - conj(bra(network[r, _prev(c, end)])[dW; DWb120 DWb60 DWb0 DSWb120 DWb240 DWb180]) * conj(bra(network[r, c])[dCenter; DNWb300 DNEb240 DEb180 DSEb120 DSWb60 DWb0]) * + O_r_c = network[r, c] + O_rp_c = network[_prev(r, end), c] + O_rp_cn = network[_prev(r, end), _next(c, end)] + O_rn_cp = network[_next(r, end), _prev(c, end)] + O_rn_c = network[_next(r, end), c] + O_r_cn = network[r, _next(c, end)] + O_r_cp = network[r, _prev(c, end)] + return @tensor opt = true ket(O_rp_c)[dNW; DNWt120 DNWt60 DNWt0 DNWt300 DWt60 DNWt180] * ket(O_rp_cn)[dNE; DNEt120 DNEt60 DNEt0 DNEt300 DNEt240 DNWt0] * + ket(O_r_cn)[dE; DNEt300 DEt60 DEt0 DEt300 DEt240 DEt180] * ket(O_rn_c)[dSE; DSEt120 DEt240 DSEt0 DSEt300 DSEt240 DSEt180] * ket(O_rn_cp)[dSW; DSWt120 DSWt60 DSEt180 DSWt300 DSWt240 DSWt180] * + ket(O_r_cp)[dW; DWt120 DWt60 DWt0 DSWt120 DWt240 DWt180] * ket(O_r_c)[dCenter; DNWt300 DNEt240 DEt180 DSEt120 DSWt60 DWt0] * + conj(bra(O_rp_c)[dNW; DNWb120 DNWb60 DNWb0 DNWb300 DWb60 DNWb180]) * conj(bra(O_rp_cn)[dNE; DNEb120 DNEb60 DNEb0 DNEb300 DNEb240 DNWb0]) * + conj(bra(O_r_cn)[dE; DNEb300 DEb60 DEb0 DEb300 DEb240 DEb180]) * conj(bra(O_rn_c)[dSE; DSEb120 DEb240 DSEb0 DSEb300 DSEb240 DSEb180]) * conj(bra(O_rn_cp)[dSW; DSWb120 DSWb60 DSEb180 DSWb300 DSWb240 DSWb180]) * + conj(bra(O_r_cp)[dW; DWb120 DWb60 DWb0 DSWb120 DWb240 DWb180]) * conj(bra(O_r_c)[dCenter; DNWb300 DNEb240 DEb180 DSEb120 DSWb60 DWb0]) * env.C[1, _prev(r - 1, end), c][χNWa DNWt120 DNWb120; χNb] * env.Eb[1, _prev(r - 1, end), _next(c, end)][χNb DNWt60 DNWb60; χNC] * env.Ea[1, _prev(r - 1, end), _next(c, end)][χNC DNEt120 DNEb120; χNa] * env.C[2, _prev(r - 1, end), _next(c + 1, end)][χNa DNEt60 DNEb60; χNEb] * env.Eb[2, _prev(r, end), _next(c + 1, end)][χNEb DNEt0 DNEb0; χNEC] * env.Ea[2, _prev(r, end), _next(c + 1, end)][χNEC DEt60 DEb60; χNEa] * env.C[3, r, _next(c + 1, end)][χNEa DEt0 DEb0; χSEb] * env.Eb[3, _next(r, end), _next(c, end)][χSEb DEt300 DEb300; χSEC] * env.Ea[3, _next(r, end), _next(c, end)][χSEC DSEt0 DSEb0; χSEa] * @@ -106,21 +121,33 @@ function _contract_site_large((r, c)::Tuple{Int, Int}, network::InfiniteTriangul end function _contract_corners((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular) where {P <: PEPSSandwichTriangular} + O_r_c = network[r, c] return @tensor opt = true env.C[1, _prev(r, end), c][χNW Dt120 Db120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN Dt60 Db60; χNE] * env.C[3, r, _next(c, end)][χNE Dt0 Db0; χSE] * env.C[4, _next(r, end), c][χSE Dt300 Db300; χS] * env.C[5, _next(r, end), _prev(c, end)][χS Dt240 Db240; χSW] * env.C[6, r, _prev(c, end)][χSW Dt180 Db180; χNW] * - ket(network[r, c])[d; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r, c])[d; Db120 Db60 Db0 Db300 Db240 Db180]) + ket(O_r_c)[d; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(O_r_c)[d; Db120 Db60 Db0 Db300 Db240 Db180]) end function _contract_corners((r, c)::Tuple{Int, Int}, network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, op::AbstractTensorMap{E, S, 1, 1}) where {P <: PEPSSandwichTriangular, E, S} + O_r_c = network[r, c] return @tensor opt = true env.C[1, _prev(r, end), c][χNW Dt120 Db120; χN] * env.C[2, _prev(r, end), _next(c, end)][χN Dt60 Db60; χNE] * env.C[3, r, _next(c, end)][χNE Dt0 Db0; χSE] * env.C[4, _next(r, end), c][χSE Dt300 Db300; χS] * env.C[5, _next(r, end), _prev(c, end)][χS Dt240 Db240; χSW] * env.C[6, r, _prev(c, end)][χSW Dt180 Db180; χNW] * - ket(network[r, c])[dt; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(network[r, c])[db; Db120 Db60 Db0 Db300 Db240 Db180]) * op[db; dt] + ket(O_r_c)[dt; Dt120 Dt60 Dt0 Dt300 Dt240 Dt180] * conj(bra(O_r_c)[db; Db120 Db60 Db0 Db300 Db240 Db180]) * op[db; dt] end function energy(network::InfiniteTriangularNetwork{P}, env::CTMRGEnvTriangular, onesite_op, twosite_op) where {P <: PEPSSandwichTriangular} - return sum(Iterators.product(axes(network)...)) do (r, c) + E = sum(Iterators.product(axes(network)...)) do (r, c) expval_onesite = _contract_corners((r, c), network, env, onesite_op) / _contract_corners((r, c), network, env) expval_twosite = _contract_edges_0((r, c), network, env, twosite_op) / _contract_edges_0((r, c), network, env) return expval_onesite + expval_twosite end + ignore_derivatives() do + isapprox(imag(E), 0; atol = sqrt(eps(real(E)))) || + @warn "Expectation value is not real: $E." + end + return real(E) +end +function energy(state::InfinitePEPSTriangular, env::CTMRGEnvTriangular, args...) + return energy(InfiniteTriangularNetwork(state), env, args...) end + +# TODO: write a proper cost_function that works on an InfinitePEPSTriangular and a LocalOperator, and use it in the optimization loop. For now, we just compute the energy using the network contraction functions above. diff --git a/src/environments/ctmrg_environments_triangular.jl b/src/environments/ctmrg_environments_triangular.jl index 433cc7b47..c2fea0a7c 100644 --- a/src/environments/ctmrg_environments_triangular.jl +++ b/src/environments/ctmrg_environments_triangular.jl @@ -1,65 +1,34 @@ #TODO: Add docs and figure -struct CTMRGEnvTriangular{T} +struct CTMRGEnvTriangular{TC, TA, TB} "6 x rows x cols array of corner C tensors, where the first dimension specifies the spatial direction" - C::Array{T, 3} + C::Array{TC, 3} "6 x rows x cols array of edge Ta tensors, where the first dimension specifies the spatial direction" - Ea::Array{T, 3} - "6 x rows x cols array of edge Ta tensors, where the first dimension specifies the spatial direction" - Eb::Array{T, 3} + Ea::Array{TA, 3} + "6 x rows x cols array of edge Tb tensors, where the first dimension specifies the spatial direction" + Eb::Array{TB, 3} end -# function CTMRGEnvTriangular(corners::Array{C, 3}, edges::Array{T, 3}) where {C, T} -# foreach(check_environment_virtualspace, edges) -# return CTMRGEnvTriangular{C, T}(corners, edges) -# end - -# """ -# CTMRGEnv( -# [f=randn, T=ComplexF64], Ds_north::A, Ds_east::A, chis_north::B, [chis_east::B], [chis_south::B], [chis_west::B] -# ) where {A<:AbstractMatrix{<:VectorSpace}, B<:AbstractMatrix{<:ElementarySpace}} - -# Construct a CTMRG environment by specifying matrices of north and east virtual spaces of the -# corresponding partition function and the north, east, south and west virtual spaces of the -# environment. Each respective matrix entry corresponds to a site in the unit cell. By -# default, the virtual environment spaces for all directions are taken to be the same. - -# The environment virtual spaces for each site correspond to the north or east virtual space -# of the corresponding edge tensor for each direction. Specifically, for a given site -# `(r, c)`, `chis_north[r, c]` corresponds to the east space of the north edge tensor, -# `chis_east[r, c]` corresponds to the north space of the east edge tensor, -# `chis_south[r, c]` corresponds to the east space of the south edge tensor, and -# `chis_west[r, c]` corresponds to the north space of the west edge tensor. -# Each entry of the `Ds_north` and `Ds_east` matrices corresponds to an effective local space -# of the partition function, and can be represented as an `ElementarySpace` (e.g. for the case -# of a partition function defined in terms of local rank-4 tensors) or a `ProductSpace` (e.g. -# for the case of a network representing overlaps of PEPSs and PEPOs). -# """ function get_Ds(D::A) where {A <: ProductSpace} - return [dir > 3 ? reverse(D') : D for dir in 1:6] + return [dir > 3 ? _elementwise_dual(D) : D for dir in 1:6] end function get_Ds(D::A) where {A <: ElementarySpace} - return [dir > 3 ? D' : D for dir in 1:6] + return [dir > 3 ? ProductSpace(D') : ProductSpace(D) for dir in 1:6] end function CTMRGEnvTriangular( f, T, D::Union{A, B}, chis::B; unitcell::Tuple{Int, Int} = (1, 1) - ) where { - A <: ProductSpace, B <: ElementarySpace, - } - if typeof(D) <: ElementarySpace - N = 1 - else - N = length(D) - end - st = spacetype(D) + ) where {A <: ProductSpace, B <: ElementarySpace} + Ds = get_Ds(D) + N = length(Ds[1]) + st = spacetype(Ds[1]) + T_type = tensormaptype(st, N + 1, 1, T) Cs = Array{T_type}(undef, 6, unitcell...) Eas = Array{T_type}(undef, 6, unitcell...) Ebs = Array{T_type}(undef, 6, unitcell...) - Ds = get_Ds(D) for dir in 1:6, r in 1:unitcell[1], c in 1:unitcell[2] C = _edge_tensor(f, T, chis, Ds[dir], chis) Ea = _edge_tensor(f, T, chis, Ds[dir], chis) @@ -75,3 +44,76 @@ function CTMRGEnvTriangular( end return CTMRGEnvTriangular(Cs, Eas, Ebs) end + +Base.size(env::CTMRGEnvTriangular, args...) = size(env.C, args...) +Base.axes(x::CTMRGEnvTriangular, args...) = axes(x.C, args...) +function eachcoordinate(x::CTMRGEnvTriangular) + return collect(Iterators.product(axes(x, 2), axes(x, 3))) +end +function eachcoordinate(x::CTMRGEnvTriangular, dirs) + return collect(Iterators.product(dirs, axes(x, 2), axes(x, 3))) +end +Base.real(env::CTMRGEnvTriangular) = CTMRGEnvTriangular(real.(env.C), real.(env.Ea), real.(env.Eb)) +Base.complex(env::CTMRGEnvTriangular) = CTMRGEnvTriangular(complex.(env.C), complex.(env.Ea), complex.(env.Eb)) + +cornertype(env::CTMRGEnvTriangular) = cornertype(typeof(env)) +cornertype(::Type{CTMRGEnvTriangular{T}}) where {T} = T +edgetype(env::CTMRGEnvTriangular) = edgetype(typeof(env)) +edgetype(::Type{CTMRGEnvTriangular{T}}) where {T} = T + +TensorKit.spacetype(::Type{E}) where {E <: CTMRGEnvTriangular} = spacetype(cornertype(E)) + +# In-place update of environment +function update!(env::CTMRGEnvTriangular{T}, env´::CTMRGEnvTriangular{T}) where {T} + env.C .= env´.C + env.Ea .= env´.Ea + env.Eb .= env´.Eb + return env +end + +# Custom adjoint for CTMRGEnvTriangular constructor, needed for fixed-point differentiation +function ChainRulesCore.rrule( + ::Type{CTMRGEnvTriangular}, C::Array{T, 3}, Ea::Array{T, 3}, Eb::Array{T, 3} + ) where {T} + function triangularenv_pullback(Δenv_) + Δenv = unthunk(Δenv_) + return NoTangent(), Δenv.C, Δenv.Ea, Δenv.Eb + end + return CTMRGEnvTriangular(C, Ea, Eb), triangularenv_pullback +end + +# math basics used for tangent accumulation in backwards pass +function Base.:+(e₁::CTMRGEnvTriangular, e₂::CTMRGEnvTriangular) + return CTMRGEnvTriangular(e₁.C + e₂.C, e₁.Ea + e₂.Ea, e₁.Eb + e₂.Eb) +end +function Base.:-(e₁::CTMRGEnvTriangular, e₂::CTMRGEnvTriangular) + return CTMRGEnvTriangular(e₁.C - e₂.C, e₁.Ea - e₂.Ea, e₁.Eb - e₂.Eb) +end +Base.:*(α::Number, e::CTMRGEnvTriangular) = CTMRGEnvTriangular(α * e.C, α * e.Ea, α * e.Eb) + +# Custom adjoint for CTMRGEnvTriangular getproperty, to avoid creating named tuples in backward pass +function ChainRulesCore.rrule(::typeof(getproperty), e::CTMRGEnvTriangular, name::Symbol) + result = getproperty(e, name) + if name === :C + function C_pullback(ΔC_) + ΔC = unthunk(ΔC_) + return NoTangent(), CTMRGEnvTriangular(ΔC, zerovector(e.Ea), zerovector(e.Eb)) + end + return result, C_pullback + elseif name === :Ea + function Ea_pullback(ΔEa_) + ΔEa = unthunk(ΔEa_) + return NoTangent(), CTMRGEnvTriangular(zerovector(e.C), ΔEa, zerovector(e.Eb)) + end + return result, Ea_pullback + elseif name === :Eb + function Eb_pullback(ΔEb_) + ΔEb = unthunk(ΔEb_) + return NoTangent(), CTMRGEnvTriangular(zerovector(e.C), zerovector(e.Ea), ΔEb) + end + return result, Eb_pullback + else + # this should never happen because already errored in forwards pass + throw(ArgumentError("No rrule for getproperty of $name")) + end +end diff --git a/src/networks/infinitetriangularnetwork.jl b/src/networks/infinitetriangularnetwork.jl index 1383c83fe..d8eebdd34 100644 --- a/src/networks/infinitetriangularnetwork.jl +++ b/src/networks/infinitetriangularnetwork.jl @@ -154,21 +154,3 @@ function ChainRulesCore.rrule( end return O, getindex_pullback end - -# function ChainRulesCore.rrule(::typeof(rotl90), network::InfiniteTriangularNetwork) -# network´ = rotl90(network) -# function rotl90_pullback(Δnetwork_) -# Δnetwork = unthunk(Δnetwork_) -# return NoTangent(), rotr90(Δnetwork) -# end -# return network´, rotl90_pullback -# end - -# function ChainRulesCore.rrule(::typeof(rotr90), network::InfiniteTriangularNetwork) -# network´ = rotr90(network) -# function rotr90_pullback(Δnetwork) -# Δnetwork = unthunk(Δnetwork) -# return NoTangent(), rotl90(Δnetwork) -# end -# return network´, rotr90_pullback -# end diff --git a/src/states/infinitepepstriangular.jl b/src/states/infinitepepstriangular.jl index 07ab09a08..a8660e982 100644 --- a/src/states/infinitepepstriangular.jl +++ b/src/states/infinitepepstriangular.jl @@ -12,17 +12,7 @@ struct InfinitePEPSTriangular{T <: PEPSTensorTriangular} InfinitePEPSTriangular{T}(A::Matrix{T}) where {T <: PEPSTensorTriangular} = new{T}(A) function InfinitePEPSTriangular(A::Array{T, 2}) where {T <: PEPSTensorTriangular} bosonic_braiding = BraidingStyle(sectortype(T)) === Bosonic() - # for (d, w) in Tuple.(CartesianIndices(A)) - # (bosonic_braiding || !isdual(physicalspace(A[d, w]))) || - # throw(ArgumentError("Dual physical spaces for symmetry sectors with non-trivial twists are not allowed (for now).")) - # north_virtualspace(A[d, w]) == south_virtualspace(A[_prev(d, end), w])' || - # throw( - # SpaceMismatch("North virtual space at site $((d, w)) does not match.") - # ) - # east_virtualspace(A[d, w]) == west_virtualspace(A[d, _next(w, end)])' || - # throw(SpaceMismatch("East virtual space at site $((d, w)) does not match.")) - # dim(space(A[d, w])) > 0 || @warn "no fusion channels at site ($d, $w)" - # end + # TODO: check braiding and spaces return new{T}(A) end end @@ -55,7 +45,7 @@ function InfinitePEPSTriangular( SWspaces = adjoint.(NEspaces) Wspaces = adjoint.(circshift(Espaces, (0, 1))) - A = map(Pspaces, NWspaces, NEspaces, Espaces, SEspaces, SWspaces, Wspaces) do NW, NE, E, SE, SW, W + A = map(Pspaces, NWspaces, NEspaces, Espaces, SEspaces, SWspaces, Wspaces) do P, NW, NE, E, SE, SW, W return PEPSTensorTriangular(f, T, P, NW, NE, E, SE, SW, W) end @@ -76,13 +66,7 @@ The unit cell is labeled as a matrix which means that any tensor in the unit cel regardless if PEPS tensor or environment tensor, is obtained by shifting the row and column index `[r, c]` by one, respectively: ``` - | | | ----C[r-1,c-1]---T[r-1,c]---T[r-1,c+1]--- - | || || ----T[r,c-1]=====AA[r,c]====AA[r,c+1]==== - | || || ----T[r+1,c-1]===AA[r+1,c]==AA[r+1,c+1]== - | || || +TODO: diagram ``` The unit cell has periodic boundary conditions, so `[r, c]` is indexed modulo the size of the unit cell. @@ -217,27 +201,6 @@ function Base.isapprox(A₁::InfinitePEPSTriangular, A₂::InfinitePEPSTriangula end end -## Rotations - -# rotl60(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(rotl60(_rotl60_localsandwich.(unitcell(A)))) -# rotr60(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(rotr60(_rotl60_localsandwich.(unitcell(A)))) -# Base.rot180(A::InfinitePEPSTriangular) = InfinitePEPSTriangular(rot180(rot180.(unitcell(A)))) - -## FiniteDifferences vectorization - -""" - to_vec(A::InfinitePEPSTriangular) -> vec, state_from_vec - -Vectorize an `InfinitePEPSTriangular` into a vector of real numbers. A vectorized infinite PEPS can -retrieved again as an `InfinitePEPSTriangular` by application of the `state_from_vec` map. -""" -function FiniteDifferences.to_vec(A::InfinitePEPSTriangular) - vec, back = FiniteDifferences.to_vec(unitcell(A)) - function state_from_vec(vec) - return NWType(back(vec)) - end - return vec, state_from_vec -end ## Chainrules diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index 2e1f2ffcd..45741c4e5 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -1,6 +1,5 @@ using Test using Random -using MatrixAlgebraKit using TensorKit using MPSKit using PEPSKit @@ -9,6 +8,17 @@ using Zygote using LinearAlgebra using KrylovKit +sd = 123456 +Random.seed!(sd) + +D = 2 +χ = 7 +T = ComplexF64 + +ctmrg_tol = 1.0e-8 +ctmrg_maxiter = 300 +ctmrg_verbosity = 2 + const ising_βc_triangular = BigFloat(BigFloat(asinh(BigFloat(sqrt(BigFloat(1.0) / BigFloat(3.0))))) / BigFloat(2.0)) const f_onsager_triangular::BigFloat = -3.20253248660790791834355252025862951439 @@ -29,129 +39,162 @@ function classical_ising_triangular(β) return TensorMap(o2, ℂ^2 * ℂ^2 * ℂ^2, ℂ^2 * ℂ^2 * ℂ^2) end -# @testset "CTM_triangular - Random tensor" begin -# for conditioning in [true false] -# for projector_alg in [:twothirds :full] -# Random.seed!(79413165445) -# alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) -# eltype = ComplexF64 -# χ = 7 -# pspace = ℂ^2 -# vspace = ℂ^3 -# envspace = ℂ^χ - -# ket = randn(eltype, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') -# bra = copy(ket) -# pf = randn(eltype, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) -# sandwiches = [pf, (ket, bra)] -# # sandwiches = [(ket, bra), pf] -# unitcell = (2, 2) - -# for (sandwich, vspace) in zip(sandwiches, [vspace, vspace ⊗ vspace']) -# # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) -# network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) -# env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) -# env, info = leading_boundary(env₀, network, alg) -# @test info.convergence_metric < 1.0 -# end -# end -# end -# end - -# @testset "CTM_triangular - Classical Ising" begin -# for conditioning in [true false] -# for projector_alg in [:twothirds :full] -# Random.seed!(156484561351) - -# alg = SimultaneousCTMRGTriangular(; maxiter = 300, conditioning, projector_alg) -# unitcell = (1, 1) -# T = classical_ising_triangular(ising_βc_triangular) -# pf = InfiniteTriangularNetwork(fill(T, unitcell)) - -# χ = 20 -# vspace = codomain(T)[1] -# envspace = ℂ^χ -# eltype = Float64 -# env₀ = CTMRGEnvTriangular(randn, eltype, vspace, envspace; unitcell) -# env, info = leading_boundary(env₀, pf, alg) - -# nw_value = network_value(pf, env) -# lz = real(log(nw_value)) -# fs = lz * -1 / ising_βc_triangular -# @test fs ≈ f_onsager_triangular rtol = 1.0e-4 -# end -# end -# end - -@testset "CTM_triangular - Differentiability" begin +@testset "CTM_triangular - Random tensor" begin for conditioning in [true false] for projector_alg in [:twothirds :full] - ctm_alg = SimultaneousCTMRGTriangular(; conditioning, projector_alg) - T = ComplexF64 - S = Trivial - χ = 7 - pspace = ℂ^2 - - J = 1.0 - g = 2.5 - - ZZ = rmul!(PEPSKit.σᶻᶻ(T, S), -J) - X = rmul!(PEPSKit.σˣ(T, S), g * -J) + Random.seed!(79413165445) + alg = SimultaneousCTMRGTriangular(; + tol = ctmrg_tol, maxiter = ctmrg_maxiter, verbosity = ctmrg_verbosity, + conditioning, projector_alg + ) + pspace = ComplexSpace(2) + vspace = ComplexSpace(D) + envspace = ComplexSpace(χ) - vspace = ℂ^3 - envspace = ℂ^χ ket = randn(T, pspace, vspace ⊗ vspace ⊗ vspace ⊗ vspace' ⊗ vspace' ⊗ vspace') bra = copy(ket) + pf = randn(T, vspace ⊗ vspace ⊗ vspace, vspace ⊗ vspace ⊗ vspace) + sandwiches = [pf, (ket, bra)] + # sandwiches = [(ket, bra), pf] + unitcell = (2, 2) + + for (sandwich, V) in zip(sandwiches, [vspace, vspace ⊗ vspace']) + # for (sandwich, vspace) in zip(sandwiches, [vspace ⊗ vspace', vspace]) + network = InfiniteTriangularNetwork(fill(sandwich, unitcell)) + env₀ = CTMRGEnvTriangular(randn, T, V, envspace; unitcell) + env, info = leading_boundary(env₀, network, alg) + @test info.convergence_metric < 1.0 # this is not much of a test + end + end + end +end - unitcell = (1, 1) - network = InfiniteTriangularNetwork(fill((ket, bra), unitcell)) - env₀ = CTMRGEnvTriangular(randn, T, vspace ⊗ vspace', envspace; unitcell) +@testset "CTM_triangular - Classical Ising" begin + χ_local = 20 + T_local = Float64 - env, info = leading_boundary(env₀, network, ctm_alg) - E₀ = PEPSKit.energy(network, env, X, ZZ) - optimizer_alg = LBFGS(4; maxiter = 10) - real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) - reuse_env = true - peps₀ = InfinitePEPSTriangular(copy(ket)) + for conditioning in [true false] + for projector_alg in [:twothirds :full] + Random.seed!(156484561351) + + alg = SimultaneousCTMRGTriangular(; + tol = ctmrg_tol, maxiter = ctmrg_maxiter, verbosity = ctmrg_verbosity, + conditioning, projector_alg + ) + sz = (1, 1) + T = classical_ising_triangular(ising_βc_triangular) + pf = InfiniteTriangularNetwork(fill(T, sz)) + + vspace = codomain(T)[1] + envspace = ComplexSpace(χ_local) + env₀ = CTMRGEnvTriangular(randn, T_local, vspace, envspace; unitcell = sz) + env, info = leading_boundary(env₀, pf, alg) + + nw_value = network_value(pf, env) + lz = real(log(nw_value)) + fs = lz * -1 / ising_βc_triangular + @test fs ≈ f_onsager_triangular rtol = 1.0e-4 + end + end +end - function peps_retract(x, η, α) - peps = x[1] - env = deepcopy(x[2]) - retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) - peps´ = InfinitePEPS(map(first, retractions)) - ξ = InfinitePEPS(map(last, retractions)) +## Gradient tests for triangular CTMRG +# ------------------------------------ + +Pspaces = [ComplexSpace(2)] +Vspaces = [ComplexSpace(χbond)] +Espaces = [ComplexSpace(χenv)] +# TODO: actually add triangular lattice models... +models = [transverse_field_ising(InfiniteSquare(); J = 1.0, g = 1.0)] +names = ["Heisenberg"] + +gradtol = 1.0e-4 +ctmrg_verbosity = 0 +ctmrg_algs = [[:triangular]] +projector_algs = [[:twothirds, :full]] +conditionings = [[true, false]] +# svd_rrule_algs = nothing # TODO: allow this +gradient_algs = [[nothing]] +# gradient_iterschemes = [[:fixed, :diffgauge]] # TODO: support this +steps = -0.01:0.005:0.01 + +# have to use a custom retraction, since the state type of hardcoded... +function peps_triangular_retract(x, η, α) + peps = x[1] + env = deepcopy(x[2]) + + retractions = norm_preserving_retract.(unitcell(peps), unitcell(η), α) + peps´ = typeof(peps)(map(first, retractions)) + ξ = typeof(peps)(map(last, retractions)) + + return (peps´, env), ξ +end - return (peps´, env), ξ - end - retract = peps_retract - - # alg = PEPSOptimize() - gradtol = 1.0e-3 - gradient_alg = EigSolver(; solver_alg = Arnoldi(; tol = gradtol, eager = true), iterscheme = :fixed) - # optimize operator cost function - (peps_final, env_final), cost_final, ∂cost, numfg, convergence_history = optimize( - (peps₀, env₀), optimizer_alg; - retract, inner = real_inner, - ) do (peps, env) - start_time = time_ns() - E, gs = withgradient(peps) do ψ - env′, info = PEPSKit.hook_pullback( - leading_boundary, env, ψ, ctm_alg; - alg_rrule = gradient_alg, - ) - # ignore_derivatives() do - # reuse_env && update!(env, env′) - # push!(truncation_errors, info.truncation_error) - # push!(condition_numbers, info.condition_number) - # end - return energy(ψ, env′, operator_onesite, operator_twosite) - end - g = only(gs) # `withgradient` returns tuple of gradients `gs` - push!(gradnorms_unitcell, norm.(g.A)) - push!(times, (time_ns() - start_time) * 1.0e-9) - return E, g + +@testset "Triangular CTMRG energy gradient test for $(names[i]) model" verbose = true for i in + eachindex( + models + ) + Pspace = Pspaces[i] + Vspace = Vspaces[i] + Espace = Espaces[i] + # calgs = ctmrg_algs[i] + palgs = projector_algs[i] + conds = conditionings[i] + # salgs = svd_rrule_algs[i] + galgs = gradient_algs[i] + # gischemes = gradient_iterschemes[i] + + # TODO: allow passing actual LocalOperator + H = models[i] + O1 = last(first(filter(t -> length(first(t)) == 1, H.terms))) + O2 = last(first(filter(t -> length(first(t)) == 2, H.terms))) + + @testset "ctmrg_alg=:simultaneous_triangular, projector_alg=:$projector_alg, conditioning=$conditioning, gradient_alg=:$gradient_alg" for ( + projector_alg, conditioning, gradient_alg, + ) in Iterators.product(palgs, conds, galgs) + + @info "optimtest of ctmrg_alg=:simultaneous_triangular, projector_alg=:$projector_alg, conditioning=$conditioning, gradient_alg=:$gradient_alg on $(names[i])" + Random.seed!(42039482030) + dir = InfinitePEPSTriangular(Pspace, Vspace) + psi = InfinitePEPSTriangular(Pspace, Vspace) + + # TODO: actually go through CTMRGAlgorithm selection + contrete_ctmrg_alg = SimultaneousCTMRGTriangular(; + projector_alg, conditioning, verbosity = ctmrg_verbosity, + trunctype = :FixedSpaceTruncation + ) + # instantiate because hook_pullback doesn't go through the keyword selector... + concrete_gradient_alg = if isnothing(gradient_alg) + nothing # TODO: add this to the PEPSKit.GradMode selector? + else + PEPSKit.GradMode(; alg = gradient_alg, tol = gradtol, iterscheme = gradient_iterscheme) + end + env, = leading_boundary( + CTMRGEnvTriangular(randn, ComplexF64, Vspace ⊗ Vspace', Espace), + psi, contrete_ctmrg_alg + ) + alphas, fs, dfs1, dfs2 = OptimKit.optimtest( + (psi, env), + dir; + alpha = steps, + retract = peps_triangular_retract, + inner = PEPSKit.real_inner, + ) do (peps, env) + E, g = Zygote.withgradient(peps) do psi + env2, = PEPSKit.hook_pullback( + leading_boundary, + env, + psi, + contrete_ctmrg_alg; + alg_rrule = concrete_gradient_alg, + ) + return PEPSKit.energy(psi, env2, O1, O2) end + + return E, only(g) end + @test dfs1 ≈ dfs2 atol = 1.0e-2 end end From ecaf5e8dd74fe546246bff891de30a8c4d278c2a Mon Sep 17 00:00:00 2001 From: leburgel Date: Mon, 2 Mar 2026 15:28:44 +0100 Subject: [PATCH 08/13] Fix renaming typo --- test/ctmrg/triangular.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index 45741c4e5..b451dbb8f 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -103,8 +103,8 @@ end # ------------------------------------ Pspaces = [ComplexSpace(2)] -Vspaces = [ComplexSpace(χbond)] -Espaces = [ComplexSpace(χenv)] +Vspaces = [ComplexSpace(D)] +Espaces = [ComplexSpace(χ)] # TODO: actually add triangular lattice models... models = [transverse_field_ising(InfiniteSquare(); J = 1.0, g = 1.0)] names = ["Heisenberg"] From 7e59999124d3daaf9112e1e1312eb800832e2d6a Mon Sep 17 00:00:00 2001 From: leburgel Date: Tue, 3 Mar 2026 07:38:12 +0100 Subject: [PATCH 09/13] Add missing imports --- test/ctmrg/triangular.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ctmrg/triangular.jl b/test/ctmrg/triangular.jl index b451dbb8f..554a4a793 100644 --- a/test/ctmrg/triangular.jl +++ b/test/ctmrg/triangular.jl @@ -8,6 +8,8 @@ using Zygote using LinearAlgebra using KrylovKit +using PEPSKit: unitcell, norm_preserving_retract + sd = 123456 Random.seed!(sd) From ad54df8d8da1cf5df934870b7b89f992e4f1daad Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 27 Oct 2026 16:30:15 +0100 Subject: [PATCH 10/13] fix merge conflict fixedpoint --- .../fixed_point_differentiation.jl | 209 +++++++++++++----- 1 file changed, 149 insertions(+), 60 deletions(-) diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index cb2436d9e..5f9dcb2d1 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -200,6 +200,41 @@ EigSolver(; kwargs...) = GradMode(; alg = :eigsolver, kwargs...) GRADIENT_MODE_SYMBOLS[:eigsolver] = EigSolver +""" + _check_algorithm_combination(boundary_alg, gradient_alg_or_symmetrization) + _check_algorithm_combination(boundary_alg, gradient_alg, symmetrization) + +Check for allowed combinations of gradient algorithm and boundary algorithm to be used for +computing the gradient of a `leading_boundary` call. Throws an error containing a +recommended fix if the combination is not allowed or broken. +""" +function _check_algorithm_combination(boundary_alg, gradient_alg_or_symmetrization) end +function _check_algorithm_combination(boundary_alg, gradient_alg, symmetrization) + _check_algorithm_combination(boundary_alg, gradient_alg) + _check_algorithm_combination(boundary_alg, symmetrization) + return nothing +end +function _check_algorithm_combination(::SequentialCTMRG, ::GradMode{:fixed}) + msg = "`:fixed` mode is not compatible with `SequentialCTMRG` since the sequential \ + application of SVDs does not allow to differentiate through a fixed set of \ + gauges; select SimultaneousCTMRG instead to use :fixed mode" + throw(ArgumentError(msg)) +end +function _check_algorithm_combination(::C4vCTMRG{<:C4vEighProjector}, ::GradMode{:diffgauge}) + msg = "`:diffgauge` mode is currently not compatible with eigh-based C4v CTMRG; \ + either switch to a different projector algorithm (e.g. `c4v_qr`), or use :fixed \ + mode for differentiation instead." + throw(ArgumentError(msg)) +end +function _check_algorithm_combination(::C4vCTMRG, symm::Union{Nothing, <:SymmetrizationStyle}) + if !(symm isa RotateReflect) + msg = "C4vCTMRG optimization is compatible only with RotateReflect symmetrization. \ + Make sure to set `symmetrization = RotateReflect()`." + throw(ArgumentError(msg)) + end + return nothing +end + #= Evaluating the gradient of the cost function for CTMRG: - The gradient of the cost function for CTMRG can be computed using automatic differentiation (AD) or explicit evaluation of the geometric sum. @@ -207,6 +242,14 @@ Evaluating the gradient of the cost function for CTMRG: - With explicit evaluation of the geometric sum, the gradient is computed by differentiating the cost function with the environment kept fixed, and then manually adding the gradient contributions from the environments. =# +_scrambling_env_gauge(::CTMRGAlgorithm) = ScramblingEnvGauge() +_scrambling_env_gauge(::C4vCTMRG) = ScramblingEnvGaugeC4v() + +function _set_fixed_truncation(alg::CTMRGAlgorithm) + alg_fixed = @set alg.projector_alg = _set_truncation(alg.projector_alg, FixedSpaceTruncation()) + return alg_fixed +end + function _rrule( gradmode::GradMode{:diffgauge}, config::RuleConfig, @@ -215,21 +258,26 @@ function _rrule( state, alg::CTMRGAlgorithm, ) + _check_algorithm_combination(alg, gradmode) + env, info = leading_boundary(envinit, state, alg) - alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation + alg_fixed = _set_fixed_truncation(alg) # fix spaces during differentiation + alg_gauge = _scrambling_env_gauge(alg) # TODO: make this a field in GradMode? + + # prepare iterating function corresponding to a single gauge-fixed CTMRG iteration + function f(A, x) + return gauge_fix(ctmrg_iteration(InfiniteSquareNetwork(A), x, alg_fixed)[1], x, alg_gauge)[1] + end + # compute its pullback + _, env_vjp = rrule_via_ad(config, f, state, env) + # split off state and environment parts + ∂f∂A(x)::typeof(state) = env_vjp(x)[2] + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] function leading_boundary_diffgauge_pullback((Δenv′, Δinfo)) Δenv = unthunk(Δenv′) - # find partial gradients of gauge-fixed single CTMRG iteration - function f(A, x) - return gauge_fix(x, ctmrg_iteration(InfiniteSquareNetwork(A), x, alg_fixed)[1])[1] - end - _, env_vjp = rrule_via_ad(config, f, state, env) - # evaluate the geometric sum - ∂f∂A(x)::typeof(state) = env_vjp(x)[2] - ∂f∂x(x)::typeof(env) = env_vjp(x)[3] ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() @@ -238,6 +286,18 @@ function _rrule( return (env, info), leading_boundary_diffgauge_pullback end +function _rrule( + gradmode::GradMode, + config::RuleConfig, + ::typeof(MPSKit.leading_boundary), + envinit, + state, + alg::CTMRGAlgorithmTriangular, +) + msg = "Fixed-point differentiation is not available for `CTMRGAlgorithmTriangular`, \ + since gauge fixing for triangular CTMRG is currently not implemented." + throw(ArgumentError(msg)) +end # Here f is differentiated from an pre-computed SVD with fixed U, S and V function _rrule( gradmode::GradMode{:fixed}, @@ -245,73 +305,66 @@ function _rrule( ::typeof(MPSKit.leading_boundary), envinit, state, - alg::SimultaneousCTMRG, + alg::CTMRGAlgorithm, ) + _check_algorithm_combination(alg, gradmode) + env, = leading_boundary(envinit, state, alg) - alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation + alg_fixed = _set_fixed_truncation(alg) # fix spaces during differentiation + alg_gauge = _scrambling_env_gauge(alg) # TODO: make this a field in GradMode? env_conv, info = ctmrg_iteration(InfiniteSquareNetwork(state), env, alg_fixed) - env_fixed, signs = gauge_fix(env, env_conv) + _, signs = gauge_fix(env_conv, env, alg_gauge) - # Fix SVD - svd_alg_fixed = _fix_svd_algorithm(alg.projector_alg.svd_alg, signs, info) - alg_fixed = @set alg.projector_alg.svd_alg = svd_alg_fixed - alg_fixed = @set alg_fixed.projector_alg.trunc = notrunc() + # fix decomposition + alg_fixed = gauge_fix(alg, signs, info) + + # prepare iterating function corresponding to a single CTMRG iteration with a + # gauge-fixed projector + function f(A, x) + return fix_global_phases( + ctmrg_iteration(InfiniteSquareNetwork(A), x, alg_fixed)[1], x, + ) + end + # prepare its pullback + _, env_vjp = rrule_via_ad(config, f, state, env) + # split off state and environment parts + ∂f∂A(x)::typeof(state) = env_vjp(x)[2] + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] function leading_boundary_fixed_pullback((Δenv′, Δinfo)) Δenv = unthunk(Δenv′) - function f(A, x) - return fix_global_phases( - x, ctmrg_iteration(InfiniteSquareNetwork(A), x, alg_fixed)[1] - ) - end - _, env_vjp = rrule_via_ad(config, f, state, env_fixed) - # evaluate the geometric sum - ∂f∂A(x)::typeof(state) = env_vjp(x)[2] - ∂f∂x(x)::typeof(env) = env_vjp(x)[3] ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() end - return (env_fixed, info), leading_boundary_fixed_pullback -end - -function _rrule( - gradmode::GradMode, - config::RuleConfig, - ::typeof(MPSKit.leading_boundary), - envinit, - state, - alg::CTMRGAlgorithmTriangular, - ) - msg = "Fixed-point differentiation is not available for `CTMRGAlgorithmTriangular`, \ - since gauge fixing for triangular CTMRG is currently not implemented." - throw(ArgumentError(msg)) + return (env, info), leading_boundary_fixed_pullback end -function _fix_svd_algorithm(alg::SVDAdjoint, signs, info) +function gauge_fix(alg::SVDAdjoint, signs, info) # embed gauge signs in larger space to fix gauge of full U and V on truncated subspace rowsize, colsize = size(signs, 2), size(signs, 3) - signs_full = map(Iterators.product(1:4, 1:rowsize, 1:colsize)) do (dir, r, c) - σ = signs[dir, r, c] - r_sign, c_sign = if dir == NORTH # take unit cell interdependency of signs into account - r, _prev(c, colsize) + inds = info.truncation_indices + signs_full = map(Iterators.product(1:4, 1:rowsize, 1:colsize)) do (dir, row, col) + σ = signs[dir, row, col] + row_sign, col_sign = if dir == NORTH # take unit cell interdependency of signs into account + row, _prev(col, colsize) elseif dir == EAST - _prev(r, rowsize), c + _prev(row, rowsize), col elseif dir == SOUTH - r, _next(c, colsize) + row, _next(col, colsize) elseif dir == WEST - _next(r, rowsize), c + _next(row, rowsize), col end - extended_space = domain(info.U_full[dir, r_sign, c_sign]) ← codomain(info.V_full[dir, r_sign, c_sign]) - extended_σ = zeros(scalartype(σ), extended_space) - for (c, b) in blocks(extended_σ) - σc = block(σ, c) - kept_dim = size(σc, 1) - b[diagind(b)] .= one(scalartype(σ)) # put ones on the diagonal - b[1:kept_dim, 1:kept_dim] .= σc # set to σ on kept subspace + + ind = inds[dir, row_sign, col_sign] + extended_σ = id(scalartype(σ), domain(info.S_full[dir, row_sign, col_sign])) + for (c, b) in blocks(σ) + I = get(ind, c, nothing) + @assert !isnothing(I) + block(extended_σ, c)[I, I] = b end return extended_σ end @@ -320,15 +373,51 @@ function _fix_svd_algorithm(alg::SVDAdjoint, signs, info) U_fixed, V_fixed = fix_relative_phases(info.U, info.V, signs) U_full_fixed, V_full_fixed = fix_relative_phases(info.U_full, info.V_full, signs_full) return SVDAdjoint(; - fwd_alg = FixedSVD(U_fixed, info.S, V_fixed, U_full_fixed, info.S_full, V_full_fixed), + fwd_alg = FixedSVD(U_fixed, info.S, V_fixed, U_full_fixed, info.S_full, V_full_fixed, inds), rrule_alg = alg.rrule_alg, ) end -function _fix_svd_algorithm(alg::SVDAdjoint{F}, signs, info) where {F <: IterSVD} +function gauge_fix(alg::SVDAdjoint{F}, signs, info) where {F <: IterSVD} # fix kept U and V only since iterative SVD doesn't have access to full spectrum U_fixed, V_fixed = fix_relative_phases(info.U, info.V, signs) return SVDAdjoint(; - fwd_alg = FixedSVD(U_fixed, info.S, V_fixed, nothing, nothing, nothing), + fwd_alg = FixedSVD(U_fixed, info.S, V_fixed, nothing, nothing, nothing, nothing), + rrule_alg = alg.rrule_alg, + ) +end +function gauge_fix(alg::EighAdjoint, signs, info) + σ = signs[1] + inds = info.truncation_indices + + # embed gauge signs in larger space to fix gauge of full V on truncated subspace + extended_σ = id(scalartype(σ), domain(info.D_full)) + for (c, b) in blocks(σ) + I = get(inds, c, nothing) + @assert !isnothing(I) + block(extended_σ, c)[I, I] = b + end + + # fix kept and full V + V_fixed = info.V * σ' + V_full_fixed = info.V_full * extended_σ' + return EighAdjoint(; + fwd_alg = FixedEig(info.D, V_fixed, info.D_full, V_full_fixed, inds), + rrule_alg = alg.rrule_alg, + ) +end +function gauge_fix(alg::EighAdjoint{F}, signs, info) where {F <: IterEigh} + # fix kept V only since iterative decomposition doesn't have access to full spectrum + V_fixed = info.V * signs[1]' + return EighAdjoint(; + fwd_alg = FixedEig(info.D, V_fixed, nothing, nothing, nothing), + rrule_alg = alg.rrule_alg, + ) +end +function gauge_fix(alg::QRAdjoint, signs, info) + Q_fixed = info.Q * signs[1]' + R_fixed = signs[1] * info.R + return QRAdjoint(; + fwd_alg = FixedQR(Q_fixed, R_fixed), rrule_alg = alg.rrule_alg, ) end @@ -409,7 +498,7 @@ end function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, x₀, alg::EigSolver) function f(X) y = ∂f∂x(X[1]) - return (y + X[2] * ∂F∂x, X[2]) + return (VI.add!!(y, ∂F∂x, X[2]), X[2]) end X₀ = (x₀, one(scalartype(x₀))) _, vecs, info = realeigsolve(f, X₀, 1, :LM, alg.solver_alg) @@ -431,8 +520,8 @@ function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, x₀, alg::EigSolver) ) return fpgrad(∂F∂x, ∂f∂x, ∂f∂A, x₀, backup_ls_alg) else - y = scale(vecs[1][1], 1 / vecs[1][2]) + y = VI.scale!!(vecs[1][1], inv(vecs[1][2])) end return ∂f∂A(y) -end +end \ No newline at end of file From 41d017bdad390b133f5d2d90cd5cdfb7486b41d2 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 27 Oct 2026 16:34:05 +0100 Subject: [PATCH 11/13] Update PEPSKit.jl --- src/PEPSKit.jl | 62 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 0bdf99147..b43b18cf1 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -23,7 +23,6 @@ using KrylovKit using KrylovKit: Lanczos, BlockLanczos using TensorOperations, OptimKit -import OptimKit: fixedpoint using ChainRulesCore, Zygote import TupleTools @@ -35,9 +34,20 @@ using MPSKit.DynamicTols: adapt_solver using MPSKit: @log_initialization, @log_iteration, @log_convergence, @log_nonconvergence using MPSKit.IterativeLoggers: with_verbosity import MPSKit.DynamicTols: _updatetol +using MPSKit: DynamicTol +using MPSKit.DynamicTols: adapt_solver +using MPSKit: @log_initialization, @log_iteration, @log_convergence, @log_nonconvergence +using MPSKit.IterativeLoggers: with_verbosity +import MPSKit.DynamicTols: _updatetol import MPSKit: tensorexpr, leading_boundary, loginit!, logiter!, logfinish!, logcancel!, physicalspace import MPSKit: infinite_temperature_density_matrix +using TensorKitTensors: fuse_charge +import TensorKitTensors.SpinOperators as SO +import TensorKitTensors.FermionOperators as FO +import TensorKitTensors.HubbardOperators as HO +import TensorKitTensors.TJOperators as TJ +import TensorKitTensors.BosonOperators as BO using TensorKitTensors: fuse_charge import TensorKitTensors.SpinOperators as SO import TensorKitTensors.FermionOperators as FO @@ -45,6 +55,9 @@ import TensorKitTensors.HubbardOperators as HO import TensorKitTensors.TJOperators as TJ import TensorKitTensors.BosonOperators as BO using MPSKitModels +import MPSKitModels: + transverse_field_ising, heisenberg_XYZ, heisenberg_XXZ, + hubbard_model, bose_hubbard_model, tj_model import MPSKitModels: transverse_field_ising, heisenberg_XYZ, heisenberg_XXZ, hubbard_model, bose_hubbard_model, tj_model @@ -57,17 +70,22 @@ include("Defaults.jl") # Include first to allow for docstring interpolation wit include("utility/util.jl") include("utility/contraction_labels.jl") include("utility/tensor_traces.jl") +include("utility/contraction_labels.jl") +include("utility/tensor_traces.jl") include("utility/indexing.jl") include("utility/diffable_threads.jl") include("utility/twistdual.jl") +include("utility/twistdual.jl") include("utility/eigh.jl") include("utility/svd.jl") include("utility/orth.jl") +include("utility/orth.jl") include("utility/rotations.jl") include("utility/hook_pullback.jl") include("utility/autoopt.jl") include("utility/retractions.jl") include("utility/initialization.jl") +include("utility/initialization.jl") include("networks/tensors.jl") include("networks/local_sandwich.jl") @@ -88,6 +106,9 @@ include("operators/localoperator.jl") include("operators/localoperator/dense.jl") include("operators/localoperator/tensorproduct.jl") include("operators/localoperator/mpo.jl") +include("operators/localoperator/dense.jl") +include("operators/localoperator/tensorproduct.jl") +include("operators/localoperator/mpo.jl") include("operators/localcircuit.jl") include("operators/lattices/squarelattice.jl") include("operators/models.jl") @@ -98,6 +119,7 @@ include("environments/vumps_environments.jl") include("environments/suweight.jl") include("environments/bp_environments.jl") include("environments/product_state_environments.jl") +include("environments/product_state_environments.jl") include("algorithms/contractions/ctmrg/types.jl") include("algorithms/contractions/ctmrg/expr.jl") @@ -109,9 +131,12 @@ include("algorithms/contractions/ctmrg/fullinf_env.jl") include("algorithms/contractions/ctmrg/renormalize_corner.jl") include("algorithms/contractions/ctmrg/renormalize_edge.jl") include("algorithms/contractions/ctmrg/network_value.jl") +include("algorithms/contractions/ctmrg/network_value.jl") include("algorithms/contractions/ctmrg/gaugefix.jl") include("algorithms/contractions/ctmrg/characteristic_equations.jl") +include("algorithms/contractions/ctmrg/characteristic_equations.jl") +include("algorithms/contractions/absorb.jl") include("algorithms/contractions/absorb.jl") include("algorithms/contractions/absorb_weight.jl") include("algorithms/contractions/transfer.jl") @@ -123,12 +148,20 @@ include("algorithms/contractions/local_patch/patch_contractions.jl") include("algorithms/contractions/local_patch/densitymatrix/generic.jl") include("algorithms/contractions/local_patch/densitymatrix/ctmrg.jl") include("algorithms/contractions/local_patch/densitymatrix/bp.jl") +include("algorithms/contractions/bp_messages.jl") +include("algorithms/contractions/local_patch/expr_utils.jl") +include("algorithms/contractions/local_patch/network_expr.jl") +include("algorithms/contractions/local_patch/patch_contractions.jl") +include("algorithms/contractions/local_patch/densitymatrix/generic.jl") +include("algorithms/contractions/local_patch/densitymatrix/ctmrg.jl") +include("algorithms/contractions/local_patch/densitymatrix/bp.jl") include("algorithms/contractions/bondenv/benv_tools.jl") include("algorithms/contractions/bondenv/gaugefix.jl") include("algorithms/contractions/bondenv/als_solve.jl") include("algorithms/contractions/bondenv/benv_ctm.jl") include("algorithms/contractions/correlator/peps.jl") include("algorithms/contractions/correlator/pepo_purified.jl") +include("algorithms/contractions/correlator/pepo_purified.jl") include("algorithms/contractions/correlator/pepo_1layer.jl") include("algorithms/ctmrg/sparse_environments.jl") @@ -138,16 +171,24 @@ include("algorithms/ctmrg/projectors/halfinfinite.jl") include("algorithms/ctmrg/projectors/fullinfinite.jl") include("algorithms/ctmrg/projectors/c4v_eigh.jl") include("algorithms/ctmrg/projectors/c4v_qr.jl") +include("algorithms/ctmrg/projectors/projectors.jl") +include("algorithms/ctmrg/projectors/halfinfinite.jl") +include("algorithms/ctmrg/projectors/fullinfinite.jl") +include("algorithms/ctmrg/projectors/c4v_eigh.jl") +include("algorithms/ctmrg/projectors/c4v_qr.jl") include("algorithms/ctmrg/simultaneous.jl") include("algorithms/ctmrg/sequential.jl") include("algorithms/ctmrg/gaugefix.jl") include("algorithms/ctmrg/c4v.jl") include("algorithms/ctmrg/initialization.jl") +include("algorithms/ctmrg/initialization.jl") include("algorithms/truncation/truncationschemes.jl") include("algorithms/truncation/fullenv_truncation.jl") include("algorithms/truncation/bond_truncation.jl") +include("algorithms/compress/local.jl") + include("algorithms/contractions/ctmrg_contractions_triangular.jl") include("algorithms/ctmrg_triangular/ctmrg.jl") include("algorithms/ctmrg_triangular/utility.jl") @@ -179,10 +220,17 @@ include("algorithms/expectation_value/patch_contractions.jl") include("algorithms/expectation_value/network_value.jl") include("algorithms/expectation_value/correlator_adapters.jl") include("algorithms/expectation_value/correlators.jl") +include("algorithms/expectation_value/expectation_value.jl") +include("algorithms/expectation_value/reduced_densitymatrix.jl") +include("algorithms/expectation_value/patch_contractions.jl") +include("algorithms/expectation_value/network_value.jl") +include("algorithms/expectation_value/correlator_adapters.jl") +include("algorithms/expectation_value/correlators.jl") include("algorithms/toolbox.jl") include("algorithms/toolbox_triangular.jl") -include("algorithms/correlators.jl") +include("algorithms/optimization/implicit_differentiation.jl") +include("algorithms/optimization/preconditioning.jl") include("algorithms/optimization/implicit_differentiation.jl") include("algorithms/optimization/preconditioning.jl") include("algorithms/optimization/peps_optimization.jl") @@ -192,12 +240,12 @@ include("algorithms/select_algorithm.jl") using .Defaults: set_scheduler! export set_scheduler! export EighAdjoint, IterEigh, SVDAdjoint, IterSVD, OrthAdjoint, QRAdjoint +export EighAdjoint, IterEigh, SVDAdjoint, IterSVD, OrthAdjoint, QRAdjoint export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG -export CTMRGTriaEnv, SimultaneousCTMRGTria +export CTMRGEnvTriangular, SimultaneousCTMRGTriangular # CTMRGTriaEnv, SimultaneousCTMRGTria export initialize_ctmrg_environment, RandomInitialization, ProductStateInitialization, ApplicationInitialization, IdentityInitialization export corner, edge, setcorner!, setedge! -export CTMRGEnvTriangular, SimultaneousCTMRGTriangular # CTMRGTriaEnv, SimultaneousCTMRGTria export FixedSpaceTruncation, SiteDependentTruncation export HalfInfiniteProjector, FullInfiniteProjector export C4vCTMRG, C4vEighProjector, C4vQRProjector @@ -208,11 +256,15 @@ export reduced_densitymatrix, expectation_value, network_value, cost_function export correlator, correlation_length export leading_boundary export PEPSOptimize, FixedPointGradient, GeomSum, ManualIter, ImplicitGradient +export PEPSOptimize, FixedPointGradient, GeomSum, ManualIter, ImplicitGradient export fixedpoint export LocalTruncation export compress +export LocalTruncation +export compress + export absorb_weight export ALSTruncation, FullEnvTruncation export SimpleUpdate @@ -238,4 +290,4 @@ export transverse_field_ising, heisenberg_XYZ, heisenberg_XXZ, j1_j2_model, bose_hubbard_model export pwave_superconductor, hubbard_model, tj_model -end # module +end # module \ No newline at end of file From 8a22365be7211dc2d87abaee3768245787337eeb Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 27 Oct 2026 16:56:11 +0100 Subject: [PATCH 12/13] fix formatting --- src/PEPSKit.jl | 2 +- .../optimization/fixed_point_differentiation.jl | 16 ++++++++-------- src/operators/transfermatrix.jl | 8 ++++---- test/testsuite/ctmrg/unitcell.jl | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index b43b18cf1..3301b634e 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -290,4 +290,4 @@ export transverse_field_ising, heisenberg_XYZ, heisenberg_XXZ, j1_j2_model, bose_hubbard_model export pwave_superconductor, hubbard_model, tj_model -end # module \ No newline at end of file +end # module diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index 5f9dcb2d1..c8005f7fd 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -287,13 +287,13 @@ function _rrule( end function _rrule( - gradmode::GradMode, - config::RuleConfig, - ::typeof(MPSKit.leading_boundary), - envinit, - state, - alg::CTMRGAlgorithmTriangular, -) + gradmode::GradMode, + config::RuleConfig, + ::typeof(MPSKit.leading_boundary), + envinit, + state, + alg::CTMRGAlgorithmTriangular, + ) msg = "Fixed-point differentiation is not available for `CTMRGAlgorithmTriangular`, \ since gauge fixing for triangular CTMRG is currently not implemented." throw(ArgumentError(msg)) @@ -524,4 +524,4 @@ function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, x₀, alg::EigSolver) end return ∂f∂A(y) -end \ No newline at end of file +end diff --git a/src/operators/transfermatrix.jl b/src/operators/transfermatrix.jl index fd94825e1..b1162fb6a 100644 --- a/src/operators/transfermatrix.jl +++ b/src/operators/transfermatrix.jl @@ -153,10 +153,10 @@ function initialize_mps( return InfiniteMPS( [ f( - T, - virtualspaces[_prev(i, end)] * _elementwise_dual(north_virtualspace(O, i)), - virtualspaces[mod1(i, end)], - ) for i in 1:length(O) + T, + virtualspaces[_prev(i, end)] * _elementwise_dual(north_virtualspace(O, i)), + virtualspaces[mod1(i, end)], + ) for i in 1:length(O) ]; kwargs... ) end diff --git a/test/testsuite/ctmrg/unitcell.jl b/test/testsuite/ctmrg/unitcell.jl index fb7b24e70..abcd74ebe 100644 --- a/test/testsuite/ctmrg/unitcell.jl +++ b/test/testsuite/ctmrg/unitcell.jl @@ -31,11 +31,11 @@ function test_unitcell( Pspaces, [ (c,) => adapt( - AT, randn( - scalartype(peps), - Pspaces[c], Pspaces[c], - ) - ) for c in CartesianIndices(unitcell) + AT, randn( + scalartype(peps), + Pspaces[c], Pspaces[c], + ) + ) for c in CartesianIndices(unitcell) ]..., ) @test expectation_value(peps, random_op, env) isa Number From ef0b544a903dbf708ee3d70e903681b2607d1d40 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 27 Oct 2026 22:43:11 +0100 Subject: [PATCH 13/13] small fixed related to merge conflicts --- src/PEPSKit.jl | 16 +--------------- src/algorithms/ctmrg_triangular/ctmrg.jl | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 3301b634e..5bda89392 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -23,6 +23,7 @@ using KrylovKit using KrylovKit: Lanczos, BlockLanczos using TensorOperations, OptimKit +import OptimKit: fixedpoint using ChainRulesCore, Zygote import TupleTools @@ -34,20 +35,9 @@ using MPSKit.DynamicTols: adapt_solver using MPSKit: @log_initialization, @log_iteration, @log_convergence, @log_nonconvergence using MPSKit.IterativeLoggers: with_verbosity import MPSKit.DynamicTols: _updatetol -using MPSKit: DynamicTol -using MPSKit.DynamicTols: adapt_solver -using MPSKit: @log_initialization, @log_iteration, @log_convergence, @log_nonconvergence -using MPSKit.IterativeLoggers: with_verbosity -import MPSKit.DynamicTols: _updatetol import MPSKit: tensorexpr, leading_boundary, loginit!, logiter!, logfinish!, logcancel!, physicalspace import MPSKit: infinite_temperature_density_matrix -using TensorKitTensors: fuse_charge -import TensorKitTensors.SpinOperators as SO -import TensorKitTensors.FermionOperators as FO -import TensorKitTensors.HubbardOperators as HO -import TensorKitTensors.TJOperators as TJ -import TensorKitTensors.BosonOperators as BO using TensorKitTensors: fuse_charge import TensorKitTensors.SpinOperators as SO import TensorKitTensors.FermionOperators as FO @@ -55,9 +45,6 @@ import TensorKitTensors.HubbardOperators as HO import TensorKitTensors.TJOperators as TJ import TensorKitTensors.BosonOperators as BO using MPSKitModels -import MPSKitModels: - transverse_field_ising, heisenberg_XYZ, heisenberg_XXZ, - hubbard_model, bose_hubbard_model, tj_model import MPSKitModels: transverse_field_ising, heisenberg_XYZ, heisenberg_XXZ, hubbard_model, bose_hubbard_model, tj_model @@ -256,7 +243,6 @@ export reduced_densitymatrix, expectation_value, network_value, cost_function export correlator, correlation_length export leading_boundary export PEPSOptimize, FixedPointGradient, GeomSum, ManualIter, ImplicitGradient -export PEPSOptimize, FixedPointGradient, GeomSum, ManualIter, ImplicitGradient export fixedpoint export LocalTruncation diff --git a/src/algorithms/ctmrg_triangular/ctmrg.jl b/src/algorithms/ctmrg_triangular/ctmrg.jl index f981de1e9..3e1eff763 100644 --- a/src/algorithms/ctmrg_triangular/ctmrg.jl +++ b/src/algorithms/ctmrg_triangular/ctmrg.jl @@ -8,7 +8,7 @@ function leading_boundary( env₀::CTMRGEnvTriangular, network::InfiniteTriangularNetwork, alg::CTMRGAlgorithmTriangular ) log = ignore_derivatives(() -> MPSKit.IterLog("CTMRG")) - return LoggingExtras.withlevel(; alg.verbosity) do + return with_verbosity(; alg.verbosity) do env = deepcopy(env₀) S_old = DiagonalTensorMap.(id.(domain.(env₀.C))) η = one(real(scalartype(network)))