From 1dca3301efaf7fa294081f5e013a1b5cec026bf2 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 23 Jan 2026 17:20:07 +0100 Subject: [PATCH 1/9] 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 6047b2068..69af18712 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -38,6 +38,9 @@ include("utility/retractions.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") @@ -49,6 +52,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") @@ -72,6 +76,10 @@ include("algorithms/ctmrg/simultaneous.jl") include("algorithms/ctmrg/sequential.jl") include("algorithms/ctmrg/gaugefix.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_truncation.jl") @@ -100,6 +108,7 @@ using .Defaults: set_scheduler! export set_scheduler! export SVDAdjoint, FullSVDReverseRule, IterSVD export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG +export CTMRGTriaEnv, SimultaneousCTMRGTria export FixedSpaceTruncation, SiteDependentTruncation export HalfInfiniteProjector, FullInfiniteProjector export LocalOperator, physicalspace @@ -115,7 +124,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 a2a177a61..fe5767308 100644 --- a/src/algorithms/select_algorithm.jl +++ b/src/algorithms/select_algorithm.jl @@ -75,3 +75,10 @@ function select_algorithm( return CTMRGAlgorithm(; alg, tol, verbosity, svd_alg = svd_algorithm, 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 d1618ec860c51131b73ed91a36190dd588d2b7ae Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 23 Jan 2026 17:27:41 +0100 Subject: [PATCH 2/9] 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 019d1a828eadc210cd36b3facb7f423ac0eef1bb Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 23 Feb 2026 20:25:49 +0100 Subject: [PATCH 3/9] 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 | 41 ++- 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, 903 insertions(+), 421 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/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 69af18712..ecc516f32 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -38,11 +38,12 @@ include("utility/retractions.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("operators/infinitepepo.jl") @@ -52,7 +53,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") @@ -76,9 +77,10 @@ include("algorithms/ctmrg/simultaneous.jl") include("algorithms/ctmrg/sequential.jl") include("algorithms/ctmrg/gaugefix.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") @@ -95,6 +97,7 @@ include("algorithms/bp/gaugefix.jl") include("algorithms/transfermatrix.jl") include("algorithms/toolbox.jl") +include("algorithms/toolbox_triangular.jl") include("algorithms/correlators.jl") include("utility/symmetrization.jl") @@ -108,7 +111,7 @@ using .Defaults: set_scheduler! export set_scheduler! export SVDAdjoint, FullSVDReverseRule, IterSVD export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG -export CTMRGTriaEnv, SimultaneousCTMRGTria +export CTMRGEnvTriangular, SimultaneousCTMRGTriangular # CTMRGTriaEnv, SimultaneousCTMRGTria export FixedSpaceTruncation, SiteDependentTruncation export HalfInfiniteProjector, FullInfiniteProjector export LocalOperator, physicalspace @@ -127,6 +130,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 index 8c11a0d33..4ac24cb2c 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::CTMRGAlgorithm, + alg::Union{CTMRGAlgorithm,CTMRGAlgorithmTriangular}, ) env, info = leading_boundary(envinit, state, alg) alg_fixed = @set alg.projector_alg.trunc = FixedSpaceTruncation() # fix spaces during differentiation @@ -278,6 +278,45 @@ function _rrule( 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) diff --git a/src/algorithms/select_algorithm.jl b/src/algorithms/select_algorithm.jl index fe5767308..1e9872657 100644 --- a/src/algorithms/select_algorithm.jl +++ b/src/algorithms/select_algorithm.jl @@ -78,7 +78,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 9fd5991e39c6746b8b3570ea0c3ac9eb64d2629c Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 24 Feb 2026 13:46:40 +0100 Subject: [PATCH 4/9] 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 885b343c446c03d9e4bb2d75661251f5c572a378 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 24 Feb 2026 13:47:36 +0100 Subject: [PATCH 5/9] 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 8beabd66b..216fd3aec 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -242,7 +242,7 @@ function _rrule( ::typeof(leading_boundary), envinit, state, - alg::Union{CTMRGAlgorithm,CTMRGAlgorithmTriangular}, + alg::Union{CTMRGAlgorithm, CTMRGAlgorithmTriangular}, ) _check_algorithm_combination(alg, gradmode) @@ -318,12 +318,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 7e1b9d1c38640ebd2f4e38d19ae019645479b979 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 24 Feb 2026 14:04:21 +0100 Subject: [PATCH 6/9] add triangular test --- test/ctmrg/triangular.jl | 226 +++++++++++++++++++-------------------- test/runtests.jl | 3 + 2 files changed, 116 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 diff --git a/test/runtests.jl b/test/runtests.jl index 496f51dfa..8359b1c42 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -52,6 +52,9 @@ end @time @safetestset "correlation length" begin include("ctmrg/correlation_length.jl") end + @time @safetestset "Triangular" begin + include("ctmrg/triangular.jl") + end end if GROUP == "ALL" || GROUP == "BP" @time @safetestset "Unit cell bond matching" begin From 1319cff94c136662c4e356df8a8d2e5477a0fb6a Mon Sep 17 00:00:00 2001 From: leburgel Date: Mon, 2 Mar 2026 13:50:56 +0100 Subject: [PATCH 7/9] Patches for differentiability --- src/PEPSKit.jl | 8 +- .../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(+), 369 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 0642071d2..b2ad632c0 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -105,15 +105,15 @@ include("algorithms/ctmrg/sequential.jl") include("algorithms/ctmrg/gaugefix.jl") include("algorithms/ctmrg/c4v.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") include("algorithms/ctmrg_triangular/simultaneous.jl") -include("algorithms/truncation/truncationschemes.jl") -include("algorithms/truncation/fullenv_truncation.jl") -include("algorithms/truncation/bond_truncation.jl") - include("algorithms/time_evolution/evoltools.jl") include("algorithms/time_evolution/time_evolve.jl") include("algorithms/time_evolution/simpleupdate.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 216fd3aec..59ce2b53d 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -242,7 +242,7 @@ function _rrule( ::typeof(leading_boundary), envinit, state, - alg::Union{CTMRGAlgorithm, CTMRGAlgorithmTriangular}, + alg::CTMRGAlgorithm, ) _check_algorithm_combination(alg, gradmode) @@ -318,42 +318,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 gauge_fix(alg::SVDAdjoint, signs, info) diff --git a/src/algorithms/select_algorithm.jl b/src/algorithms/select_algorithm.jl index c36ac2f6e..8ba6beda1 100644 --- a/src/algorithms/select_algorithm.jl +++ b/src/algorithms/select_algorithm.jl @@ -79,10 +79,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 03f5eb7f7477716d4e7c17ff4e625d20e45d998f Mon Sep 17 00:00:00 2001 From: leburgel Date: Mon, 2 Mar 2026 15:28:44 +0100 Subject: [PATCH 8/9] 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 a1447f3112737b19e125c0d792a51866b60db445 Mon Sep 17 00:00:00 2001 From: leburgel Date: Tue, 3 Mar 2026 07:38:12 +0100 Subject: [PATCH 9/9] 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)