From 6211f4d6662e1aed7baaa723d2c60a6a88d4fb06 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Tue, 4 Feb 2025 17:36:28 +0100 Subject: [PATCH 01/26] Update PEPSKit.Defaults --- src/PEPSKit.jl | 105 +++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 314c8083..e14b4096 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -61,81 +61,92 @@ include("utility/symmetrization.jl") """ module Defaults - const ctmrg_maxiter = 100 - const ctmrg_miniter = 4 - const ctmrg_tol = 1e-8 - const fpgrad_maxiter = 30 - const fpgrad_tol = 1e-6 - const reuse_env = true - const trscheme = FixedSpaceTruncation() - const fwd_alg = TensorKit.SDD() - const rrule_alg = Arnoldi(; tol=1e-2fpgrad_tol, krylovdim=48, verbosity=-1) - const svd_alg = SVDAdjoint(; fwd_alg, rrule_alg) - const projector_alg_type = HalfInfiniteProjector - const projector_alg = projector_alg_type(svd_alg, trscheme, 2) - const ctmrg_alg = SimultaneousCTMRG( - ctmrg_tol, ctmrg_maxiter, ctmrg_miniter, 2, projector_alg - ) - const optimizer = LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=3) - const gradient_linsolver = KrylovKit.BiCGStab(; - maxiter=Defaults.fpgrad_maxiter, tol=Defaults.fpgrad_tol - ) - const iterscheme = :fixed - const gradient_alg = LinSolver(; solver=gradient_linsolver, iterscheme) - const scheduler = Ref{Scheduler}(Threads.nthreads() == 1 ? SerialScheduler() : DynamicScheduler()) - end -Module containing default values that represent typical algorithm parameters. +Module containing default algorithm parameter values and arguments. -- `ctmrg_maxiter`: Maximal number of CTMRG iterations per run -- `ctmrg_miniter`: Minimal number of CTMRG carried out -- `ctmrg_tol`: Tolerance checking singular value and norm convergence -- `fpgrad_maxiter`: Maximal number of iterations for computing the CTMRG fixed-point gradient -- `fpgrad_tol`: Convergence tolerance for the fixed-point gradient iteration -- `reuse_env`: If `true`, the current optimization step is initialized on the previous environment -- `trscheme`: Truncation scheme for SVDs and other decompositions -- `fwd_alg`: SVD algorithm that is used in the forward pass +# CTMRG +- `ctmrg_tol=1e-8`: Tolerance checking singular value and norm convergence +- `ctmrg_maxiter=100`: Maximal number of CTMRG iterations per run +- `ctmrg_miniter=4`: Minimal number of CTMRG carried out +- `trscheme=FixedSpaceTruncation()`: Truncation scheme for SVDs and other decompositions +- `fwd_alg=TensorKit.SDD()`: SVD algorithm that is used in the forward pass - `rrule_alg`: Reverse-rule for differentiating that SVD -- `svd_alg`: Combination of `fwd_alg` and `rrule_alg` -- `projector_alg_type`: Default type of projector algorithm + + ``` + rrule_alg = Arnoldi(; tol=ctmrg_tol, krylovdim=48, verbosity=-1) + ``` + +- `svd_alg=SVDAdjoint(; fwd_alg, rrule_alg)`: Combination of `fwd_alg` and `rrule_alg` +- `projector_alg_type=HalfInfiniteProjector`: Default type of projector algorithm - `projector_alg`: Algorithm to compute CTMRG projectors + + ``` + projector_alg = projector_alg_type(; svd_alg, trscheme, verbosity=0) + ``` + - `ctmrg_alg`: Algorithm for performing CTMRG runs -- `optimizer`: Optimization algorithm for PEPS ground-state optimization + + ``` + ctmrg_alg = SimultaneousCTMRG( + ctmrg_tol, ctmrg_maxiter, ctmrg_miniter, 2, projector_alg + ) + ``` + +# Optimization +- `fpgrad_maxiter=30`: Maximal number of iterations for computing the CTMRG fixed-point gradient +- `fpgrad_tol=1e-6`: Convergence tolerance for the fixed-point gradient iteration +- `iterscheme=:fixed`: Scheme for differentiating one CTMRG iteration - `gradient_linsolver`: Default linear solver for the `LinSolver` gradient algorithm -- `iterscheme`: Scheme for differentiating one CTMRG iteration + + ``` + gradient_linsolver=KrylovKit.BiCGStab(; maxiter=fpgrad_maxiter, tol=fpgrad_tol) + ``` + - `gradient_alg`: Algorithm to compute the gradient fixed-point -- `scheduler`: Multi-threading scheduler which can be accessed via `set_scheduler!` + + ``` + gradient_alg = LinSolver(; solver=gradient_linsolver, iterscheme) + ``` + +- `reuse_env=true`: If `true`, the current optimization step is initialized on the previous environment +- `optimizer=LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=3)`: The default `OptimKit.OptimizerAlgorithm` + +# OhMyThreads scheduler +- `scheduler=Ref{Scheduler}(...)`: Multi-threading scheduler which can be accessed via `set_scheduler!` """ module Defaults - using TensorKit, KrylovKit, OptimKit, OhMyThreads + using TensorKit, KrylovKit, OhMyThreads + using Manopt using PEPSKit: LinSolver, FixedSpaceTruncation, SVDAdjoint, HalfInfiniteProjector, SimultaneousCTMRG + + # CTMRG const ctmrg_tol = 1e-8 const ctmrg_maxiter = 100 const ctmrg_miniter = 4 - const fpgrad_maxiter = 30 - const fpgrad_tol = 1e-6 const sparse = false - const reuse_env = true const trscheme = FixedSpaceTruncation() const fwd_alg = TensorKit.SDD() - const rrule_alg = Arnoldi(; tol=1e-2fpgrad_tol, krylovdim=48, verbosity=-1) + const rrule_alg = Arnoldi(; tol=ctmrg_tol, krylovdim=48, verbosity=-1) const svd_alg = SVDAdjoint(; fwd_alg, rrule_alg) const projector_alg_type = HalfInfiniteProjector - const projector_alg = projector_alg_type(svd_alg, trscheme, 2) + const projector_alg = projector_alg_type(; svd_alg, trscheme, verbosity=0) const ctmrg_alg = SimultaneousCTMRG( ctmrg_tol, ctmrg_maxiter, ctmrg_miniter, 2, projector_alg ) - const optimizer = LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=3) - const gradient_linsolver = KrylovKit.BiCGStab(; - maxiter=Defaults.fpgrad_maxiter, tol=Defaults.fpgrad_tol - ) + + # Optimization + const fpgrad_maxiter = 30 + const fpgrad_tol = 1e-6 + const gradient_linsolver = KrylovKit.BiCGStab(; maxiter=fpgrad_maxiter, tol=fpgrad_tol) const iterscheme = :fixed const gradient_alg = LinSolver(; solver=gradient_linsolver, iterscheme) + const reuse_env = true + const optimizer = LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=3) # OhMyThreads scheduler defaults const scheduler = Ref{Scheduler}() From 1916e2bb28c5a178f351384ad08bf3a504dcb47c Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Tue, 4 Feb 2025 17:39:59 +0100 Subject: [PATCH 02/26] Break up peps_opt.jl into two files --- src/PEPSKit.jl | 3 +- .../fixed_point_differentiation.jl} | 133 +----------------- .../optimization/peps_optimization.jl | 130 +++++++++++++++++ 3 files changed, 133 insertions(+), 133 deletions(-) rename src/algorithms/{peps_opt.jl => optimization/fixed_point_differentiation.jl} (62%) create mode 100644 src/algorithms/optimization/peps_optimization.jl diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index e14b4096..f8de8fab 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -55,7 +55,8 @@ include("algorithms/time_evolution/simpleupdate.jl") include("algorithms/toolbox.jl") -include("algorithms/peps_opt.jl") +include("algorithms/optimization/fixed_point_differentiation.jl") +include("algorithms/optimization/peps_optimization.jl") include("utility/symmetrization.jl") diff --git a/src/algorithms/peps_opt.jl b/src/algorithms/optimization/fixed_point_differentiation.jl similarity index 62% rename from src/algorithms/peps_opt.jl rename to src/algorithms/optimization/fixed_point_differentiation.jl index 9d7b2056..ef3e249c 100644 --- a/src/algorithms/peps_opt.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -76,137 +76,6 @@ function LinSolver(; return LinSolver{iterscheme}(solver) end -""" - PEPSOptimize{G}(; boundary_alg=Defaults.ctmrg_alg, optimizer::OptimKit.OptimizationAlgorithm=Defaults.optimizer - reuse_env::Bool=true, gradient_alg::G=Defaults.gradient_alg) - -Algorithm struct that represent PEPS ground-state optimization using AD. -Set the algorithm to contract the infinite PEPS in `boundary_alg`; -currently only `CTMRGAlgorithm`s are supported. The `optimizer` computes the gradient directions -based on the CTMRG gradient and updates the PEPS parameters. In this optimization, -the CTMRG runs can be started on the converged environments of the previous optimizer -step by setting `reuse_env` to true. Otherwise a random environment is used at each -step. The CTMRG gradient itself is computed using the `gradient_alg` algorithm. -""" -struct PEPSOptimize{G} - boundary_alg::CTMRGAlgorithm - optimizer::OptimKit.OptimizationAlgorithm - reuse_env::Bool - gradient_alg::G - - function PEPSOptimize( # Inner constructor to prohibit illegal setting combinations - boundary_alg::CTMRGAlgorithm, - optimizer, - reuse_env, - gradient_alg::G, - ) where {G} - if gradient_alg isa GradMode - if boundary_alg isa SequentialCTMRG && iterscheme(gradient_alg) === :fixed - throw(ArgumentError(":sequential and :fixed are not compatible")) - end - end - return new{G}(boundary_alg, optimizer, reuse_env, gradient_alg) - end -end -function PEPSOptimize(; - boundary_alg=Defaults.ctmrg_alg, - optimizer=Defaults.optimizer, - reuse_env=Defaults.reuse_env, - gradient_alg=Defaults.gradient_alg, -) - return PEPSOptimize(boundary_alg, optimizer, reuse_env, gradient_alg) -end - -""" - fixedpoint(ψ₀::InfinitePEPS{T}, H, alg::PEPSOptimize, [env₀::CTMRGEnv]; - finalize!=OptimKit._finalize!, symmetrization=nothing) where {T} - -Optimize `ψ₀` with respect to the Hamiltonian `H` according to the parameters supplied -in `alg`. The initial environment `env₀` serves as an initial guess for the first CTMRG run. -By default, a random initial environment is used. - -The `finalize!` kwarg can be used to insert a function call after each optimization step -by utilizing the `finalize!` kwarg of `OptimKit.optimize`. -The function maps `(peps, envs), f, g = finalize!((peps, envs), f, g, numiter)`. -The `symmetrization` kwarg accepts `nothing` or a `SymmetrizationStyle`, in which case the -PEPS and PEPS gradient are symmetrized after each optimization iteration. Note that this -requires a symmmetric `ψ₀` and `env₀` to converge properly. - -The function returns a `NamedTuple` which contains the following entries: -- `peps`: final `InfinitePEPS` -- `env`: `CTMRGEnv` corresponding to the final PEPS -- `E`: final energy -- `E_history`: convergence history of the energy function -- `grad`: final energy gradient -- `gradnorm_history`: convergence history of the energy gradient norms -- `numfg`: total number of calls to the energy function -""" -function fixedpoint( - ψ₀::InfinitePEPS{F}, - H, - alg::PEPSOptimize, - env₀::CTMRGEnv=CTMRGEnv(ψ₀, field(F)^20); - (finalize!)=OptimKit._finalize!, - symmetrization=nothing, -) where {F} - if isnothing(symmetrization) - retract = peps_retract - else - retract, symm_finalize! = symmetrize_retract_and_finalize!(symmetrization) - fin! = finalize! # Previous finalize! - finalize! = (x, f, g, numiter) -> fin!(symm_finalize!(x, f, g, numiter)..., numiter) - end - - if scalartype(env₀) <: Real - env₀ = complex(env₀) - @warn "the provided real environment was converted to a complex environment since \ - :fixed mode generally produces complex gauges; use :diffgauge mode instead to work \ - with purely real environments" - end - - (peps, env), E, ∂E, numfg, convhistory = optimize( - (ψ₀, env₀), alg.optimizer; retract, inner=real_inner, finalize! - ) do (peps, envs) - E, gs = withgradient(peps) do ψ - envs´ = hook_pullback( - leading_boundary, - envs, - ψ, - alg.boundary_alg; - alg_rrule=alg.gradient_alg, - ) - ignore_derivatives() do - alg.reuse_env && update!(envs, envs´) - end - return costfun(ψ, envs´, H) - end - g = only(gs) # `withgradient` returns tuple of gradients `gs` - return E, g - end - - return (; - peps, - env, - E, - E_history=convhistory[:, 1], - grad=∂E, - gradnorm_history=convhistory[:, 2], - numfg, - ) -end - -# Update PEPS unit cell in non-mutating way -# Note: Both x and η are InfinitePEPS during optimization -function peps_retract(x, η, α) - peps = deepcopy(x[1]) - peps.A .+= η.A .* α - env = deepcopy(x[2]) - return (peps, env), η -end - -# Take real valued part of dot product -real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) - #= Evaluating the gradient of the cost function for CTMRG: - The gradient of the cost function for CTMRG can be computed using automatic differentiation (AD) or explicit evaluation of the geometric sum. @@ -353,4 +222,4 @@ function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, y₀, alg::LinSolver) end return ∂f∂A(y) -end +end \ No newline at end of file diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl new file mode 100644 index 00000000..5abdedff --- /dev/null +++ b/src/algorithms/optimization/peps_optimization.jl @@ -0,0 +1,130 @@ +""" + PEPSOptimize{G}(; boundary_alg=Defaults.ctmrg_alg, optimizer::OptimKit.OptimizationAlgorithm=Defaults.optimizer + reuse_env::Bool=true, gradient_alg::G=Defaults.gradient_alg) + +Algorithm struct that represent PEPS ground-state optimization using AD. +Set the algorithm to contract the infinite PEPS in `boundary_alg`; +currently only `CTMRGAlgorithm`s are supported. The `optimizer` computes the gradient directions +based on the CTMRG gradient and updates the PEPS parameters. In this optimization, +the CTMRG runs can be started on the converged environments of the previous optimizer +step by setting `reuse_env` to true. Otherwise a random environment is used at each +step. The CTMRG gradient itself is computed using the `gradient_alg` algorithm. +""" +struct PEPSOptimize{G} + boundary_alg::CTMRGAlgorithm + optimizer::OptimKit.OptimizationAlgorithm + reuse_env::Bool + gradient_alg::G + + function PEPSOptimize( # Inner constructor to prohibit illegal setting combinations + boundary_alg::CTMRGAlgorithm, + optimizer, + reuse_env, + gradient_alg::G, + ) where {G} + if gradient_alg isa GradMode + if boundary_alg isa SequentialCTMRG && iterscheme(gradient_alg) === :fixed + throw(ArgumentError(":sequential and :fixed are not compatible")) + end + end + return new{G}(boundary_alg, optimizer, reuse_env, gradient_alg) + end +end +function PEPSOptimize(; + boundary_alg=Defaults.ctmrg_alg, + optimizer=Defaults.optimizer, + reuse_env=Defaults.reuse_env, + gradient_alg=Defaults.gradient_alg, +) + return PEPSOptimize(boundary_alg, optimizer, reuse_env, gradient_alg) +end + +""" + fixedpoint(ψ₀::InfinitePEPS{T}, H, alg::PEPSOptimize, [env₀::CTMRGEnv]; + finalize!=OptimKit._finalize!, symmetrization=nothing) where {T} + +Optimize `ψ₀` with respect to the Hamiltonian `H` according to the parameters supplied +in `alg`. The initial environment `env₀` serves as an initial guess for the first CTMRG run. +By default, a random initial environment is used. + +The `finalize!` kwarg can be used to insert a function call after each optimization step +by utilizing the `finalize!` kwarg of `OptimKit.optimize`. +The function maps `(peps, envs), f, g = finalize!((peps, envs), f, g, numiter)`. +The `symmetrization` kwarg accepts `nothing` or a `SymmetrizationStyle`, in which case the +PEPS and PEPS gradient are symmetrized after each optimization iteration. Note that this +requires a symmmetric `ψ₀` and `env₀` to converge properly. + +The function returns a `NamedTuple` which contains the following entries: +- `peps`: final `InfinitePEPS` +- `env`: `CTMRGEnv` corresponding to the final PEPS +- `E`: final energy +- `E_history`: convergence history of the energy function +- `grad`: final energy gradient +- `gradnorm_history`: convergence history of the energy gradient norms +- `numfg`: total number of calls to the energy function +""" +function fixedpoint( + ψ₀::InfinitePEPS{F}, + H, + alg::PEPSOptimize, + env₀::CTMRGEnv=CTMRGEnv(ψ₀, field(F)^20); + (finalize!)=OptimKit._finalize!, + symmetrization=nothing, +) where {F} + if isnothing(symmetrization) + retract = peps_retract + else + retract, symm_finalize! = symmetrize_retract_and_finalize!(symmetrization) + fin! = finalize! # Previous finalize! + finalize! = (x, f, g, numiter) -> fin!(symm_finalize!(x, f, g, numiter)..., numiter) + end + + if scalartype(env₀) <: Real + env₀ = complex(env₀) + @warn "the provided real environment was converted to a complex environment since \ + :fixed mode generally produces complex gauges; use :diffgauge mode instead to work \ + with purely real environments" + end + + (peps, env), E, ∂E, numfg, convhistory = optimize( + (ψ₀, env₀), alg.optimizer; retract, inner=real_inner, finalize! + ) do (peps, envs) + E, gs = withgradient(peps) do ψ + envs´ = hook_pullback( + leading_boundary, + envs, + ψ, + alg.boundary_alg; + alg_rrule=alg.gradient_alg, + ) + ignore_derivatives() do + alg.reuse_env && update!(envs, envs´) + end + return costfun(ψ, envs´, H) + end + g = only(gs) # `withgradient` returns tuple of gradients `gs` + return E, g + end + + return (; + peps, + env, + E, + E_history=convhistory[:, 1], + grad=∂E, + gradnorm_history=convhistory[:, 2], + numfg, + ) +end + +# Update PEPS unit cell in non-mutating way +# Note: Both x and η are InfinitePEPS during optimization +function peps_retract(x, η, α) + peps = deepcopy(x[1]) + peps.A .+= η.A .* α + env = deepcopy(x[2]) + return (peps, env), η +end + +# Take real valued part of dot product +real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) From c4e0436575caa61cbd0e535244dc8a35a7b4d4f0 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Tue, 4 Feb 2025 17:53:19 +0100 Subject: [PATCH 03/26] Add symmetrization to PEPSOptimize --- src/PEPSKit.jl | 2 +- .../optimization/peps_optimization.jl | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index f8de8fab..9b14b9cb 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -110,7 +110,7 @@ Module containing default algorithm parameter values and arguments. ``` - `reuse_env=true`: If `true`, the current optimization step is initialized on the previous environment -- `optimizer=LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=3)`: The default `OptimKit.OptimizerAlgorithm` +- `optimizer=LBFGS(32; maxiter=100, gradtol=1e-4, verbosity=3)`: Default `OptimKit.OptimizerAlgorithm` for PEPS optimization # OhMyThreads scheduler - `scheduler=Ref{Scheduler}(...)`: Multi-threading scheduler which can be accessed via `set_scheduler!` diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 5abdedff..fe27dd17 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -12,31 +12,34 @@ step. The CTMRG gradient itself is computed using the `gradient_alg` algorithm. """ struct PEPSOptimize{G} boundary_alg::CTMRGAlgorithm + gradient_alg::G optimizer::OptimKit.OptimizationAlgorithm reuse_env::Bool - gradient_alg::G + symmetrization::Union{Nothing,SymmetrizationStyle} function PEPSOptimize( # Inner constructor to prohibit illegal setting combinations boundary_alg::CTMRGAlgorithm, + gradient_alg::G, optimizer, reuse_env, - gradient_alg::G, + symmetrization, ) where {G} if gradient_alg isa GradMode if boundary_alg isa SequentialCTMRG && iterscheme(gradient_alg) === :fixed throw(ArgumentError(":sequential and :fixed are not compatible")) end end - return new{G}(boundary_alg, optimizer, reuse_env, gradient_alg) + return new{G}(boundary_alg, gradient_alg, optimizer, reuse_env, symmetrization) end end function PEPSOptimize(; boundary_alg=Defaults.ctmrg_alg, + gradient_alg=Defaults.gradient_alg, optimizer=Defaults.optimizer, reuse_env=Defaults.reuse_env, - gradient_alg=Defaults.gradient_alg, + symmetrization=nothing, ) - return PEPSOptimize(boundary_alg, optimizer, reuse_env, gradient_alg) + return PEPSOptimize(boundary_alg, gradient_alg, optimizer, reuse_env, symmetrization) end """ @@ -69,12 +72,11 @@ function fixedpoint( alg::PEPSOptimize, env₀::CTMRGEnv=CTMRGEnv(ψ₀, field(F)^20); (finalize!)=OptimKit._finalize!, - symmetrization=nothing, ) where {F} - if isnothing(symmetrization) + if isnothing(alg.symmetrization) retract = peps_retract else - retract, symm_finalize! = symmetrize_retract_and_finalize!(symmetrization) + retract, symm_finalize! = symmetrize_retract_and_finalize!(alg.symmetrization) fin! = finalize! # Previous finalize! finalize! = (x, f, g, numiter) -> fin!(symm_finalize!(x, f, g, numiter)..., numiter) end From ab794ba94c395c95db4ca99189d6f1ad3b03d37b Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Tue, 4 Feb 2025 17:53:50 +0100 Subject: [PATCH 04/26] Fix realness warning in fixedpoint --- src/algorithms/optimization/peps_optimization.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index fe27dd17..6625c96a 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -81,7 +81,7 @@ function fixedpoint( finalize! = (x, f, g, numiter) -> fin!(symm_finalize!(x, f, g, numiter)..., numiter) end - if scalartype(env₀) <: Real + if scalartype(env₀) <: Real && iterscheme(alg.gradient_alg) == :fixed env₀ = complex(env₀) @warn "the provided real environment was converted to a complex environment since \ :fixed mode generally produces complex gauges; use :diffgauge mode instead to work \ From 42766e39a3d54e458499d01223f43c623bc1337e Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 5 Feb 2025 10:21:00 +0100 Subject: [PATCH 05/26] Rename `costfun` to `cost_function` and add docstring --- examples/hubbard_su.jl | 2 +- src/PEPSKit.jl | 2 +- src/algorithms/optimization/peps_optimization.jl | 2 +- src/algorithms/toolbox.jl | 8 +++++++- test/ctmrg/flavors.jl | 4 ++-- test/ctmrg/gradients.jl | 2 +- test/heisenberg.jl | 2 +- 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/hubbard_su.jl b/examples/hubbard_su.jl index 6478a28e..e7d72b57 100644 --- a/examples/hubbard_su.jl +++ b/examples/hubbard_su.jl @@ -53,7 +53,7 @@ Es_exact = Dict(0 => -1.62, 2 => -0.176, 4 => 0.8603, 6 => -0.6567, 8 => -0.5243 E_exact = Es_exact[U] - U / 2 # measure energy -E = costfun(peps, envs, ham) / (N1 * N2) +E = cost_function(peps, envs, ham) / (N1 * N2) @info "Energy = $E" @info "Benchmark energy = $E_exact" @test isapprox(E, E_exact; atol=5e-2) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 9b14b9cb..13d3cca1 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -200,7 +200,7 @@ export SVDAdjoint, IterSVD export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG export FixedSpaceTruncation, HalfInfiniteProjector, FullInfiniteProjector export LocalOperator -export expectation_value, costfun, product_peps, correlation_length +export expectation_value, cost_function, product_peps, correlation_length export leading_boundary export PEPSOptimize, GeomSum, ManualIter, LinSolver export fixedpoint diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 6625c96a..94a27975 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -102,7 +102,7 @@ function fixedpoint( ignore_derivatives() do alg.reuse_env && update!(envs, envs´) end - return costfun(ψ, envs´, H) + return cost_function(ψ, envs´, H) end g = only(gs) # `withgradient` returns tuple of gradients `gs` return E, g diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 29a8f828..cc1d6edf 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -37,7 +37,13 @@ function MPSKit.expectation_value( return expectation_value(pf, CartesianIndex(op[1]) => op[2], envs) end -function costfun(peps::InfinitePEPS, envs::CTMRGEnv, O::LocalOperator) +""" + cost_function(peps::InfinitePEPS, envs::CTMRGEnv, O::LocalOperator) + +Real part of expectation value of `O`. Prints a warning if the expectation value +yields a finite imaginary part (up to a tolerance). +""" +function cost_function(peps::InfinitePEPS, envs::CTMRGEnv, O::LocalOperator) E = MPSKit.expectation_value(peps, O, envs) ignore_derivatives() do isapprox(imag(E), 0; atol=sqrt(eps(real(E)))) || diff --git a/test/ctmrg/flavors.jl b/test/ctmrg/flavors.jl index 5d8b440c..63f53be4 100644 --- a/test/ctmrg/flavors.jl +++ b/test/ctmrg/flavors.jl @@ -50,8 +50,8 @@ projector_algs = [HalfInfiniteProjector, FullInfiniteProjector] # compare Heisenberg energies H = heisenberg_XYZ(InfiniteSquare(unitcell...)) - E_sequential = costfun(psi, env_sequential, H) - E_simultaneous = costfun(psi, env_simultaneous, H) + E_sequential = cost_function(psi, env_sequential, H) + E_simultaneous = cost_function(psi, env_simultaneous, H) @test E_sequential ≈ E_simultaneous rtol = 1e-3 end diff --git a/test/ctmrg/gradients.jl b/test/ctmrg/gradients.jl index 648d48bd..25d1be0d 100644 --- a/test/ctmrg/gradients.jl +++ b/test/ctmrg/gradients.jl @@ -71,7 +71,7 @@ steps = -0.01:0.005:0.01 ) do (peps, envs) E, g = Zygote.withgradient(peps) do psi envs2 = PEPSKit.hook_pullback(leading_boundary, envs, psi, ctmrg_alg; alg_rrule) - return costfun(psi, envs2, models[i]) + return cost_function(psi, envs2, models[i]) end return E, only(g) diff --git a/test/heisenberg.jl b/test/heisenberg.jl index 48e58be0..fa64d373 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -86,7 +86,7 @@ end envs = leading_boundary(envs₀, peps, SimultaneousCTMRG()) # measure physical quantities - e_site = costfun(peps, envs, ham) / (N1 * N2) + e_site = cost_function(peps, envs, ham) / (N1 * N2) @info "Simple update energy = $e_site" # benchmark data from Phys. Rev. B 94, 035133 (2016) @test isapprox(e_site, -0.6594; atol=1e-3) From 9afaf5fe414b9a902ffa27bec6c25b14a636a907 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 5 Feb 2025 10:42:58 +0100 Subject: [PATCH 06/26] Clean up tests --- test/ctmrg/gradients.jl | 7 +++---- test/heisenberg.jl | 22 ++++++++++------------ test/j1j2_model.jl | 3 ++- test/pwave.jl | 6 +++--- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/test/ctmrg/gradients.jl b/test/ctmrg/gradients.jl index 25d1be0d..6800d70e 100644 --- a/test/ctmrg/gradients.jl +++ b/test/ctmrg/gradients.jl @@ -50,17 +50,16 @@ steps = -0.01:0.005:0.01 models ) Pspace = Pspaces[i] - Vspace = Pspaces[i] + Vspace = Vspaces[i] Espace = Espaces[i] gms = gradmodes[i] calgs = ctmrg_algs[i] - psi_init = InfinitePEPS(Pspace, Vspace, Vspace) @testset "$ctmrg_alg and $alg_rrule" for (ctmrg_alg, alg_rrule) in Iterators.product(calgs, gms) @info "optimtest of $ctmrg_alg and $alg_rrule on $(names[i])" Random.seed!(42039482030) - dir = InfinitePEPS(Pspace, Vspace, Vspace) - psi = InfinitePEPS(Pspace, Vspace, Vspace) + dir = InfinitePEPS(Pspace, Vspace) + psi = InfinitePEPS(Pspace, Vspace) env = leading_boundary(CTMRGEnv(psi, Espace), psi, ctmrg_alg) alphas, fs, dfs1, dfs2 = OptimKit.optimtest( (psi, env), diff --git a/test/heisenberg.jl b/test/heisenberg.jl index fa64d373..9dfa2f47 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -36,18 +36,16 @@ end # initialize states Random.seed!(456) unitcell = (1, 2) - H_1x2 = heisenberg_XYZ(InfiniteSquare(unitcell...)) - psi_init_1x2 = InfinitePEPS(2, Dbond; unitcell) - env_init_1x2 = leading_boundary( - CTMRGEnv(psi_init_1x2, ComplexSpace(χenv)), psi_init_1x2, ctm_alg - ) + H = heisenberg_XYZ(InfiniteSquare(unitcell...)) + psi_init = InfinitePEPS(2, Dbond; unitcell) + env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # optimize energy and compute correlation lengths - result_1x2 = fixedpoint(psi_init_1x2, H_1x2, opt_alg, env_init_1x2) - ξ_h_1x2, ξ_v_1x2, = correlation_length(result_1x2.peps, result_1x2.env) + result = fixedpoint(psi_init, H, opt_alg, env_init) + ξ_h, ξ_v, = correlation_length(result.peps, result.env) - @test result_1x2.E ≈ 2 * E_ref atol = 1e-2 - @test all(@. ξ_h_1x2 > 0 && ξ_v_1x2 > 0) + @test result.E ≈ 2 * E_ref atol = 1e-2 + @test all(@. ξ_h > 0 && ξ_v > 0) end @testset "Simple update into AD optimization" begin @@ -94,9 +92,9 @@ end # continue with auto differentiation svd_alg_gmres = SVDAdjoint(; rrule_alg=GMRES(; tol=1e-5)) opt_alg_gmres = @set opt_alg.boundary_alg.projector_alg.svd_alg = svd_alg_gmres - result = fixedpoint(peps, ham, opt_alg_gmres, envs) # sensitivity warnings and degeneracies due to SU(2)? - ξ_h, ξ_v, = correlation_length(result.peps, result.env) - e_site2 = result.E / (N1 * N2) + result_final = fixedpoint(peps, ham, opt_alg_gmres, envs) # sensitivity warnings and degeneracies due to SU(2)? + ξ_h, ξ_v, = correlation_length(result_final.peps, result_final.env) + e_site2 = result_final.E / (N1 * N2) @info "Auto diff energy = $e_site2" @test e_site2 ≈ E_ref atol = 1e-2 @test all(@. ξ_h > 0 && ξ_v > 0) diff --git a/test/j1j2_model.jl b/test/j1j2_model.jl index 5d13bba7..7aa5e21b 100644 --- a/test/j1j2_model.jl +++ b/test/j1j2_model.jl @@ -13,6 +13,7 @@ opt_alg = PEPSOptimize(; boundary_alg=ctm_alg, optimizer=LBFGS(4; gradtol=1e-3, verbosity=3), gradient_alg=LinSolver(; iterscheme=:diffgauge), + symmetrization=RotateReflect(), ) # initialize states @@ -23,7 +24,7 @@ psi_init = symmetrize!(psi_init, RotateReflect()) env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg); # find fixedpoint -result = fixedpoint(psi_init, H, opt_alg, env_init; symmetrization=RotateReflect()) +result = fixedpoint(psi_init, H, opt_alg, env_init) ξ_h, ξ_v, = correlation_length(result.peps, result.env) # compare against Juraj Hasik's data: diff --git a/test/pwave.jl b/test/pwave.jl index 4e36b14e..67199005 100644 --- a/test/pwave.jl +++ b/test/pwave.jl @@ -8,16 +8,16 @@ using OptimKit # Initialize parameters unitcell = (2, 2) H = pwave_superconductor(InfiniteSquare(unitcell...)) -χbond = 2 +Dbond = 2 χenv = 16 -ctm_alg = SimultaneousCTMRG(; maxiter=150) +ctm_alg = SimultaneousCTMRG() opt_alg = PEPSOptimize(; boundary_alg=ctm_alg, optimizer=LBFGS(4; maxiter=10, gradtol=1e-3, verbosity=3) ) # initialize states Pspace = Vect[FermionParity](0 => 1, 1 => 1) -Vspace = Vect[FermionParity](0 => χbond ÷ 2, 1 => χbond ÷ 2) +Vspace = Vect[FermionParity](0 => Dbond ÷ 2, 1 => Dbond ÷ 2) Envspace = Vect[FermionParity](0 => χenv ÷ 2, 1 => χenv ÷ 2) Random.seed!(91283219347) psi_init = InfinitePEPS(Pspace, Vspace, Vspace; unitcell) From dba99e4dff4a26442a4ee39d97b8028d34d22ef4 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 5 Feb 2025 10:54:55 +0100 Subject: [PATCH 07/26] Make PEPSKit.fixedpoint argument order consistent with MPSKit.fixedpoint (operator first) --- README.md | 2 +- docs/src/index.md | 2 +- examples/heisenberg.jl | 2 +- .../optimization/peps_optimization.jl | 24 ++++++++++++------- test/heisenberg.jl | 6 ++--- test/j1j2_model.jl | 2 +- test/pwave.jl | 2 +- test/tf_ising.jl | 4 ++-- 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 381cb8d9..94d263cf 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ opt_alg = PEPSOptimize(; # ground state search state = InfinitePEPS(2, D) ctm = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) -result = fixedpoint(state, H, opt_alg, ctm) +result = fixedpoint(H, state, ctm, opt_alg) @show result.E # -0.6625... ``` diff --git a/docs/src/index.md b/docs/src/index.md index 115349e1..2040c4c4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -37,7 +37,7 @@ opt_alg = PEPSOptimize(; # ground state search state = InfinitePEPS(2, D) ctm = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) -result = fixedpoint(state, H, opt_alg, ctm) +result = fixedpoint(H, state, ctm, opt_alg) @show result.E # -0.6625... ``` diff --git a/examples/heisenberg.jl b/examples/heisenberg.jl index 3b0b0d65..a566a3e6 100644 --- a/examples/heisenberg.jl +++ b/examples/heisenberg.jl @@ -27,5 +27,5 @@ opt_alg = PEPSOptimize(; # Of course there is a noticable bias for small χbond and χenv. ψ₀ = InfinitePEPS(2, χbond) env₀ = leading_boundary(CTMRGEnv(ψ₀, ℂ^χenv), ψ₀, ctm_alg) -result = fixedpoint(ψ₀, H, opt_alg, env₀) +result = fixedpoint(H, ψ, env₀, opt_alg₀) @show result.E diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 94a27975..52644710 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -43,10 +43,12 @@ function PEPSOptimize(; end """ - fixedpoint(ψ₀::InfinitePEPS{T}, H, alg::PEPSOptimize, [env₀::CTMRGEnv]; + + fixedpoint(operator, peps₀::InfinitePEPS{F}, [env₀::CTMRGEnv]; kwargs...) + fixedpoint(operator, peps₀::InfinitePEPS{T}, alg::PEPSOptimize, [env₀::CTMRGEnv]; finalize!=OptimKit._finalize!, symmetrization=nothing) where {T} -Optimize `ψ₀` with respect to the Hamiltonian `H` according to the parameters supplied +Optimize `peps₀` with respect to the `operator` according to the parameters supplied in `alg`. The initial environment `env₀` serves as an initial guess for the first CTMRG run. By default, a random initial environment is used. @@ -67,10 +69,16 @@ The function returns a `NamedTuple` which contains the following entries: - `numfg`: total number of calls to the energy function """ function fixedpoint( - ψ₀::InfinitePEPS{F}, - H, - alg::PEPSOptimize, - env₀::CTMRGEnv=CTMRGEnv(ψ₀, field(F)^20); + operator, peps₀::InfinitePEPS{F}, env₀::CTMRGEnv=CTMRGEnv(peps₀, field(F)^20); kwargs... +) + alg = fixedpoint_selector(; kwargs...) # TODO: implement fixedpoint_selector + return fixedpoint(operator, peps₀, env₀, alg) +end +function fixedpoint( + operator, + peps₀::InfinitePEPS{F}, + env₀::CTMRGEnv, + alg::PEPSOptimize; (finalize!)=OptimKit._finalize!, ) where {F} if isnothing(alg.symmetrization) @@ -89,7 +97,7 @@ function fixedpoint( end (peps, env), E, ∂E, numfg, convhistory = optimize( - (ψ₀, env₀), alg.optimizer; retract, inner=real_inner, finalize! + (peps₀, env₀), alg.optimizer; retract, inner=real_inner, finalize! ) do (peps, envs) E, gs = withgradient(peps) do ψ envs´ = hook_pullback( @@ -102,7 +110,7 @@ function fixedpoint( ignore_derivatives() do alg.reuse_env && update!(envs, envs´) end - return cost_function(ψ, envs´, H) + return cost_function(ψ, envs´, operator) end g = only(gs) # `withgradient` returns tuple of gradients `gs` return E, g diff --git a/test/heisenberg.jl b/test/heisenberg.jl index 9dfa2f47..1d7daaaf 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -25,7 +25,7 @@ E_ref = -0.6602310934799577 env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # optimize energy and compute correlation lengths - result = fixedpoint(psi_init, H, opt_alg, env_init) + result = fixedpoint(H, psi_init, env_init, opt_alg) ξ_h, ξ_v, = correlation_length(result.peps, result.env) @test result.E ≈ E_ref atol = 1e-2 @@ -41,7 +41,7 @@ end env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # optimize energy and compute correlation lengths - result = fixedpoint(psi_init, H, opt_alg, env_init) + result = fixedpoint(H, psi_init, env_init, opt_alg) ξ_h, ξ_v, = correlation_length(result.peps, result.env) @test result.E ≈ 2 * E_ref atol = 1e-2 @@ -92,7 +92,7 @@ end # continue with auto differentiation svd_alg_gmres = SVDAdjoint(; rrule_alg=GMRES(; tol=1e-5)) opt_alg_gmres = @set opt_alg.boundary_alg.projector_alg.svd_alg = svd_alg_gmres - result_final = fixedpoint(peps, ham, opt_alg_gmres, envs) # sensitivity warnings and degeneracies due to SU(2)? + result_final = fixedpoint(ham, peps, envs, opt_alg_gmres) # sensitivity warnings and degeneracies due to SU(2)? ξ_h, ξ_v, = correlation_length(result_final.peps, result_final.env) e_site2 = result_final.E / (N1 * N2) @info "Auto diff energy = $e_site2" diff --git a/test/j1j2_model.jl b/test/j1j2_model.jl index 7aa5e21b..2e850a02 100644 --- a/test/j1j2_model.jl +++ b/test/j1j2_model.jl @@ -24,7 +24,7 @@ psi_init = symmetrize!(psi_init, RotateReflect()) env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg); # find fixedpoint -result = fixedpoint(psi_init, H, opt_alg, env_init) +result = fixedpoint(H, psi_init, env_init, opt_alg) ξ_h, ξ_v, = correlation_length(result.peps, result.env) # compare against Juraj Hasik's data: diff --git a/test/pwave.jl b/test/pwave.jl index 67199005..dd033e35 100644 --- a/test/pwave.jl +++ b/test/pwave.jl @@ -24,7 +24,7 @@ psi_init = InfinitePEPS(Pspace, Vspace, Vspace; unitcell) env_init = leading_boundary(CTMRGEnv(psi_init, Envspace), psi_init, ctm_alg); # find fixedpoint -result = fixedpoint(psi_init, H, opt_alg, env_init) +result = fixedpoint(H, psi_init, env_init, opt_alg) # comparison with Gaussian PEPS minimum at D=2 on 1000x1000 square lattice with aPBC @test result.E / prod(size(psi_init)) ≈ -2.6241 atol = 5e-2 diff --git a/test/tf_ising.jl b/test/tf_ising.jl index 90377021..b494c2d6 100644 --- a/test/tf_ising.jl +++ b/test/tf_ising.jl @@ -31,7 +31,7 @@ psi_init = InfinitePEPS(2, χbond) env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # find fixedpoint -result = fixedpoint(psi_init, H, opt_alg, env_init) +result = fixedpoint(H, psi_init, env_init, opt_alg) ξ_h, ξ_v, = correlation_length(result.peps, result.env) # compute magnetization @@ -45,7 +45,7 @@ magn = expectation_value(result.peps, M, result.env) # find fixedpoint in polarized phase and compute correlations lengths H_polar = transverse_field_ising(InfiniteSquare(); g=4.5) -result_polar = fixedpoint(psi_init, H_polar, opt_alg, env_init) +result_polar = fixedpoint(H_polar, psi_init, env_init, opt_alg) ξ_h_polar, ξ_v_polar, = correlation_length(result_polar.peps, result_polar.env) @test ξ_h_polar < ξ_h @test ξ_v_polar < ξ_v From 80f003d1cf5bbe0def975e385ab2279111919795 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 5 Feb 2025 18:13:23 +0100 Subject: [PATCH 08/26] Add truncation_error and condition_number to CTMRG info return tuples --- src/algorithms/ctmrg/ctmrg.jl | 7 +++++-- src/algorithms/ctmrg/sequential.jl | 15 ++++++++++++--- src/algorithms/ctmrg/simultaneous.jl | 16 +++++++++++++--- .../optimization/fixed_point_differentiation.jl | 2 +- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index c5225f9d..7b8a8d6a 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -38,10 +38,13 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRGAlgorithm) env = deepcopy(envinit) log = ignore_derivatives(() -> MPSKit.IterLog("CTMRG")) + truncation_error = zero(real(scalartype(state))) + condition_number = one(real(scalartype(state))) + info = (; truncation_error, condition_number) return LoggingExtras.withlevel(; alg.verbosity) do ctmrg_loginit!(log, η, state, envinit) for iter in 1:(alg.maxiter) - env, = ctmrg_iteration(state, env, alg) # Grow and renormalize in all 4 directions + env, info = ctmrg_iteration(state, env, alg) # Grow and renormalize in all 4 directions η, CS, TS = calc_convergence(env, CS, TS) if η ≤ alg.tol && iter ≥ alg.miniter @@ -54,7 +57,7 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRGAlgorithm) ctmrg_logiter!(log, iter, η, state, env) end end - return env + return env, info end end diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 4404a7f5..5249e780 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -31,12 +31,14 @@ function SequentialCTMRG(; end function ctmrg_iteration(state, envs::CTMRGEnv, alg::SequentialCTMRG) - ϵ = zero(real(scalartype(state))) + truncation_error = zero(real(scalartype(state))) + condition_number = zero(real(scalartype(state))) for _ in 1:4 # rotate for col in 1:size(state, 2) # left move column-wise projectors, info = sequential_projectors(col, state, envs, alg.projector_alg) envs = renormalize_sequentially(col, projectors, state, envs) - ϵ = max(ϵ, info.err) + truncation_error = max(truncation_error, info.truncation_error) + condition_number = max(condition_number, info.condition_number) end state = rotate_north(state, EAST) envs = rotate_north(envs, EAST) @@ -57,17 +59,24 @@ function sequential_projectors( ) # SVD half-infinite environment column-wise ϵ = Zygote.Buffer(zeros(real(scalartype(envs)), size(envs, 2))) + S = Zygote.Buffer( + zeros(size(envs, 2)), tensormaptype(spacetype(T), 1, 1, real(scalartype(T))) + ) coordinates = eachcoordinate(envs)[:, col] projectors = dtmap(coordinates) do (r, c) trscheme = truncation_scheme(alg, envs.edges[WEST, _prev(r, size(envs, 2)), c]) proj, info = sequential_projectors( (WEST, r, c), state, envs, @set(alg.trscheme = trscheme) ) + S[r] = info.S ϵ[r] = info.err / norm(info.S) return proj end - return (map(first, projectors), map(last, projectors)), (; err=maximum(copy(ϵ))) + truncation_error = maximum(copy(ϵ)) + condition_number = maximum(_condition_number, S) + info = (; truncation_error, condition_number) + return (map(first, projectors), map(last, projectors)), info end function sequential_projectors( coordinate::NTuple{3,Int}, diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index c592d64e..85736603 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -48,6 +48,15 @@ function _prealloc_svd(edges::Array{E,N}) where {E,N} return U, S, V end +# Compute condition number σ_max / σ_min for diagonal singular value TensorMap +function _condition_number(S::AbstractTensorMap) + # Take maximal condition number over all blocks + return maximum(blocks(S)) do (_, b) + b_diag = diag(b) + return maximum(b_diag) / minimum(b_diag) + end +end + """ simultaneous_projectors(enlarged_corners::Array{E,3}, envs::CTMRGEnv, alg::ProjectorAlgorithm) simultaneous_projectors(coordinate, enlarged_corners::Array{E,3}, alg::ProjectorAlgorithm) @@ -74,9 +83,10 @@ function simultaneous_projectors( return proj end - P_left = map(first, projectors) - P_right = map(last, projectors) - return (P_left, P_right), (; err=maximum(copy(ϵ)), U=copy(U), S=copy(S), V=copy(V)) + truncation_error = maximum(copy(ϵ)) + condition_number = maximum(_condition_number, S) + info = (; truncation_error, condition_number, U=copy(U), S=copy(S), V=copy(V)) + return (map(first, projectors), map(last, projectors)), info end function simultaneous_projectors( coordinate, enlarged_corners::Array{E,3}, alg::HalfInfiniteProjector diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index ef3e249c..d32e9995 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -222,4 +222,4 @@ function fpgrad(∂F∂x, ∂f∂x, ∂f∂A, y₀, alg::LinSolver) end return ∂f∂A(y) -end \ No newline at end of file +end From 6a8e7395760003193ee64a9f43152ad14c434956 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 5 Feb 2025 18:18:09 +0100 Subject: [PATCH 09/26] Adapt leading_boundary calls to additional info return value --- README.md | 2 +- docs/src/index.md | 2 +- examples/boundary_mps.jl | 4 ++-- examples/heisenberg.jl | 2 +- examples/hubbard_su.jl | 2 +- src/algorithms/optimization/peps_optimization.jl | 2 +- test/boundarymps/vumps.jl | 4 ++-- test/ctmrg/fixed_iterscheme.jl | 4 ++-- test/ctmrg/flavors.jl | 6 +++--- test/ctmrg/gaugefix.jl | 4 ++-- test/ctmrg/gradients.jl | 4 ++-- test/ctmrg/jacobian_real_linear.jl | 2 +- test/ctmrg/partition_function.jl | 2 +- test/heisenberg.jl | 6 +++--- test/j1j2_model.jl | 2 +- test/pwave.jl | 2 +- test/tf_ising.jl | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 94d263cf..9b2eb406 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ opt_alg = PEPSOptimize(; # ground state search state = InfinitePEPS(2, D) -ctm = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) +ctm, = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) result = fixedpoint(H, state, ctm, opt_alg) @show result.E # -0.6625... diff --git a/docs/src/index.md b/docs/src/index.md index 2040c4c4..a70793b5 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -36,7 +36,7 @@ opt_alg = PEPSOptimize(; # ground state search state = InfinitePEPS(2, D) -ctm = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) +ctm, = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) result = fixedpoint(H, state, ctm, opt_alg) @show result.E # -0.6625... diff --git a/examples/boundary_mps.jl b/examples/boundary_mps.jl index 3047d606..a8d8578d 100644 --- a/examples/boundary_mps.jl +++ b/examples/boundary_mps.jl @@ -32,7 +32,7 @@ mps, envs, ϵ = leading_boundary(mps, T, VUMPS()) N = abs(prod(expectation_value(mps, T))) # This can be compared to the result obtained using the CTMRG algorithm -ctm = leading_boundary( +ctm, = leading_boundary( peps, SimultaneousCTMRG(; verbosity=1), CTMRGEnv(peps, ComplexSpace(20)) ) N´ = abs(norm(peps, ctm)) @@ -55,7 +55,7 @@ mps2 = PEPSKit.initializeMPS(T2, fill(ComplexSpace(20), 2, 2)) mps2, envs2, ϵ = leading_boundary(mps2, T2, VUMPS()) N2 = abs(prod(expectation_value(mps2, T2))) -ctm2 = leading_boundary( +ctm2, = leading_boundary( peps2, SimultaneousCTMRG(; verbosity=1), CTMRGEnv(peps2, ComplexSpace(20)) ) N2´ = abs(norm(peps2, ctm2)) diff --git a/examples/heisenberg.jl b/examples/heisenberg.jl index a566a3e6..63d9bb22 100644 --- a/examples/heisenberg.jl +++ b/examples/heisenberg.jl @@ -26,6 +26,6 @@ opt_alg = PEPSOptimize(; # E/N = −0.6694421, which is a QMC estimate from https://arxiv.org/abs/1101.3281. # Of course there is a noticable bias for small χbond and χenv. ψ₀ = InfinitePEPS(2, χbond) -env₀ = leading_boundary(CTMRGEnv(ψ₀, ℂ^χenv), ψ₀, ctm_alg) +env₀, = leading_boundary(CTMRGEnv(ψ₀, ℂ^χenv), ψ₀, ctm_alg) result = fixedpoint(H, ψ, env₀, opt_alg₀) @show result.E diff --git a/examples/hubbard_su.jl b/examples/hubbard_su.jl index e7d72b57..3e0f6542 100644 --- a/examples/hubbard_su.jl +++ b/examples/hubbard_su.jl @@ -43,7 +43,7 @@ Espace = Vect[fℤ₂](0 => χenv0 / 2, 1 => χenv0 / 2) envs = CTMRGEnv(randn, Float64, peps, Espace) for χ in [χenv0, χenv] ctm_alg = SequentialCTMRG(; maxiter=300, tol=1e-7) - envs = leading_boundary(envs, peps, ctm_alg) + envs, = leading_boundary(envs, peps, ctm_alg) end # Benchmark values of the ground state energy from diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 52644710..3bc94fe1 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -100,7 +100,7 @@ function fixedpoint( (peps₀, env₀), alg.optimizer; retract, inner=real_inner, finalize! ) do (peps, envs) E, gs = withgradient(peps) do ψ - envs´ = hook_pullback( + envs´, = hook_pullback( leading_boundary, envs, ψ, diff --git a/test/boundarymps/vumps.jl b/test/boundarymps/vumps.jl index 272bc948..f878a8c3 100644 --- a/test/boundarymps/vumps.jl +++ b/test/boundarymps/vumps.jl @@ -17,7 +17,7 @@ const vumps_alg = VUMPS(; alg_eigsolve=MPSKit.Defaults.alg_eigsolve(; ishermitia mps, envs, ϵ = leading_boundary(mps, T, vumps_alg) N = abs(sum(expectation_value(mps, T))) - ctm = leading_boundary( + ctm, = leading_boundary( CTMRGEnv(psi, ComplexSpace(20)), psi, SimultaneousCTMRG(; verbosity=1) ) N´ = abs(norm(psi, ctm)) @@ -33,7 +33,7 @@ end mps, envs, ϵ = leading_boundary(mps, T, vumps_alg) N = abs(prod(expectation_value(mps, T))) - ctm = leading_boundary( + ctm, = leading_boundary( CTMRGEnv(psi, ComplexSpace(20)), psi, SimultaneousCTMRG(; verbosity=1) ) N´ = abs(norm(psi, ctm)) diff --git a/test/ctmrg/fixed_iterscheme.jl b/test/ctmrg/fixed_iterscheme.jl index 208fdece..39eccf5c 100644 --- a/test/ctmrg/fixed_iterscheme.jl +++ b/test/ctmrg/fixed_iterscheme.jl @@ -30,7 +30,7 @@ atol = 1e-5 # initialize states Random.seed!(2394823842) psi = InfinitePEPS(2, χbond; unitcell) - env_conv1 = leading_boundary(CTMRGEnv(psi, ComplexSpace(χenv)), psi, ctm_alg) + env_conv1, = leading_boundary(CTMRGEnv(psi, ComplexSpace(χenv)), psi, ctm_alg) # do extra iteration to get SVD env_conv2, info = ctmrg_iteration(psi, env_conv1, ctm_alg) @@ -61,7 +61,7 @@ end Random.seed!(91283219347) psi = InfinitePEPS(2, χbond) env_init = CTMRGEnv(psi, ComplexSpace(χenv)) - env_conv1 = leading_boundary(env_init, psi, ctm_alg_iter) + env_conv1, = leading_boundary(env_init, psi, ctm_alg_iter) # do extra iteration to get SVD env_conv2_iter, info_iter = ctmrg_iteration(psi, env_conv1, ctm_alg_iter) diff --git a/test/ctmrg/flavors.jl b/test/ctmrg/flavors.jl index 63f53be4..b3d935a5 100644 --- a/test/ctmrg/flavors.jl +++ b/test/ctmrg/flavors.jl @@ -19,10 +19,10 @@ projector_algs = [HalfInfiniteProjector, FullInfiniteProjector] # compute environments Random.seed!(32350283290358) psi = InfinitePEPS(2, χbond; unitcell) - env_sequential = leading_boundary( + env_sequential, = leading_boundary( CTMRGEnv(psi, ComplexSpace(χenv)), psi, ctm_alg_sequential ) - env_simultaneous = leading_boundary( + env_simultaneous, = leading_boundary( CTMRGEnv(psi, ComplexSpace(χenv)), psi, ctm_alg_simultaneous ) @@ -68,7 +68,7 @@ end χs = [16 17 18; 15 20 21; 14 19 22] psi = InfinitePEPS(Ds, Ds, Ds) env = CTMRGEnv(psi, rand(10:20, 3, 3), rand(10:20, 3, 3)) - env2 = leading_boundary(env, psi, ctm_alg) + env2, = leading_boundary(env, psi, ctm_alg) # check that the space is fixed @test all(space.(env.corners) .== space.(env2.corners)) diff --git a/test/ctmrg/gaugefix.jl b/test/ctmrg/gaugefix.jl index 13d9a15b..52231498 100644 --- a/test/ctmrg/gaugefix.jl +++ b/test/ctmrg/gaugefix.jl @@ -20,7 +20,7 @@ function _pre_converge_env( Random.seed!(seed) # Seed RNG to make random environment consistent psi = InfinitePEPS(rand, T, physical_space, peps_space; unitcell) env₀ = CTMRGEnv(psi, ctm_space) - env_conv = leading_boundary(env₀, psi, SequentialCTMRG(; tol)) + env_conv, = leading_boundary(env₀, psi, SequentialCTMRG(; tol)) return env_conv, psi end @@ -45,7 +45,7 @@ end alg = ctmrg_alg(; tol, projector_alg) env_pre, psi = preconv[(S, T, unitcell)] env_pre - env = leading_boundary(env_pre, psi, alg) + env, = leading_boundary(env_pre, psi, alg) env′, = ctmrg_iteration(psi, env, alg) env_fixed, = gauge_fix(env, env′) @test calc_elementwise_convergence(env, env_fixed) ≈ 0 atol = atol diff --git a/test/ctmrg/gradients.jl b/test/ctmrg/gradients.jl index 6800d70e..4227cc3e 100644 --- a/test/ctmrg/gradients.jl +++ b/test/ctmrg/gradients.jl @@ -60,7 +60,7 @@ steps = -0.01:0.005:0.01 Random.seed!(42039482030) dir = InfinitePEPS(Pspace, Vspace) psi = InfinitePEPS(Pspace, Vspace) - env = leading_boundary(CTMRGEnv(psi, Espace), psi, ctmrg_alg) + env, = leading_boundary(CTMRGEnv(psi, Espace), psi, ctmrg_alg) alphas, fs, dfs1, dfs2 = OptimKit.optimtest( (psi, env), dir; @@ -69,7 +69,7 @@ steps = -0.01:0.005:0.01 inner=PEPSKit.real_inner, ) do (peps, envs) E, g = Zygote.withgradient(peps) do psi - envs2 = PEPSKit.hook_pullback(leading_boundary, envs, psi, ctmrg_alg; alg_rrule) + envs2, = PEPSKit.hook_pullback(leading_boundary, envs, psi, ctmrg_alg; alg_rrule) return cost_function(psi, envs2, models[i]) end diff --git a/test/ctmrg/jacobian_real_linear.jl b/test/ctmrg/jacobian_real_linear.jl index d91c4529..c00fa90a 100644 --- a/test/ctmrg/jacobian_real_linear.jl +++ b/test/ctmrg/jacobian_real_linear.jl @@ -19,7 +19,7 @@ Dbond, χenv = 2, 16 @testset "$iterscheme and $ctm_alg" for (iterscheme, ctm_alg) in algs Random.seed!(123521938519) state = InfinitePEPS(2, Dbond) - envs = leading_boundary(CTMRGEnv(state, ComplexSpace(χenv)), state, ctm_alg) + envs, = leading_boundary(CTMRGEnv(state, ComplexSpace(χenv)), state, ctm_alg) # follow code of _rrule if iterscheme == :fixed diff --git a/test/ctmrg/partition_function.jl b/test/ctmrg/partition_function.jl index 8995d6ab..13a20ffb 100644 --- a/test/ctmrg/partition_function.jl +++ b/test/ctmrg/partition_function.jl @@ -98,7 +98,7 @@ projector_algs = [HalfInfiniteProjector, FullInfiniteProjector] ctm_styles, projector_algs ) ctm_alg = ctm_style(; maxiter=150, projector_alg) - env = leading_boundary(env0, Z, ctm_alg) + env, = leading_boundary(env0, Z, ctm_alg) # check observables λ = PEPSKit.value(Z, env) diff --git a/test/heisenberg.jl b/test/heisenberg.jl index 1d7daaaf..27d9db21 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -22,7 +22,7 @@ E_ref = -0.6602310934799577 Random.seed!(123) H = heisenberg_XYZ(InfiniteSquare()) psi_init = InfinitePEPS(2, Dbond) - env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) + env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # optimize energy and compute correlation lengths result = fixedpoint(H, psi_init, env_init, opt_alg) @@ -38,7 +38,7 @@ end unitcell = (1, 2) H = heisenberg_XYZ(InfiniteSquare(unitcell...)) psi_init = InfinitePEPS(2, Dbond; unitcell) - env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) + env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # optimize energy and compute correlation lengths result = fixedpoint(H, psi_init, env_init, opt_alg) @@ -81,7 +81,7 @@ end # absorb weight into site tensors and CTMRG peps = InfinitePEPS(wpeps) envs₀ = CTMRGEnv(rand, Float64, peps, Espace) - envs = leading_boundary(envs₀, peps, SimultaneousCTMRG()) + envs, = leading_boundary(envs₀, peps, SimultaneousCTMRG()) # measure physical quantities e_site = cost_function(peps, envs, ham) / (N1 * N2) diff --git a/test/j1j2_model.jl b/test/j1j2_model.jl index 2e850a02..b05b7294 100644 --- a/test/j1j2_model.jl +++ b/test/j1j2_model.jl @@ -21,7 +21,7 @@ Random.seed!(91283219347) H = j1_j2(InfiniteSquare(); J2=0.25) psi_init = product_peps(2, χbond; noise_amp=1e-1) psi_init = symmetrize!(psi_init, RotateReflect()) -env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg); +env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg); # find fixedpoint result = fixedpoint(H, psi_init, env_init, opt_alg) diff --git a/test/pwave.jl b/test/pwave.jl index dd033e35..320bd130 100644 --- a/test/pwave.jl +++ b/test/pwave.jl @@ -21,7 +21,7 @@ Vspace = Vect[FermionParity](0 => Dbond ÷ 2, 1 => Dbond ÷ 2) Envspace = Vect[FermionParity](0 => χenv ÷ 2, 1 => χenv ÷ 2) Random.seed!(91283219347) psi_init = InfinitePEPS(Pspace, Vspace, Vspace; unitcell) -env_init = leading_boundary(CTMRGEnv(psi_init, Envspace), psi_init, ctm_alg); +env_init, = leading_boundary(CTMRGEnv(psi_init, Envspace), psi_init, ctm_alg); # find fixedpoint result = fixedpoint(H, psi_init, env_init, opt_alg) diff --git a/test/tf_ising.jl b/test/tf_ising.jl index b494c2d6..e0e39a98 100644 --- a/test/tf_ising.jl +++ b/test/tf_ising.jl @@ -28,7 +28,7 @@ opt_alg = PEPSOptimize(; H = transverse_field_ising(InfiniteSquare(); g) Random.seed!(2928528935) psi_init = InfinitePEPS(2, χbond) -env_init = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) +env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) # find fixedpoint result = fixedpoint(H, psi_init, env_init, opt_alg) From 31e32bde0e56b87ca9a738c10b5e7bc5e00af6ea Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Wed, 5 Feb 2025 18:28:05 +0100 Subject: [PATCH 10/26] Adapt leading_boundary rrule to info tuple --- .../optimization/fixed_point_differentiation.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index d32e9995..2e2ccadf 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -91,9 +91,9 @@ function _rrule( state, alg::CTMRGAlgorithm, ) - envs = leading_boundary(envinit, state, alg) + envs, info = leading_boundary(envinit, state, alg) - function leading_boundary_diffgauge_pullback(Δenvs′) + function leading_boundary_diffgauge_pullback((Δenvs′, Δinfo)) Δenvs = unthunk(Δenvs′) # find partial gradients of gauge_fixed single CTMRG iteration @@ -108,7 +108,7 @@ function _rrule( return NoTangent(), ZeroTangent(), ∂F∂envs, NoTangent() end - return envs, leading_boundary_diffgauge_pullback + return (envs, info), leading_boundary_diffgauge_pullback end # Here f is differentiated from an pre-computed SVD with fixed U, S and V @@ -134,7 +134,7 @@ function _rrule( alg_fixed = @set alg.projector_alg.svd_alg = svd_alg_fixed alg_fixed = @set alg_fixed.projector_alg.trscheme = notrunc() - function leading_boundary_fixed_pullback(Δenvs′) + function leading_boundary_fixed_pullback((Δenvs′, Δinfo)) Δenvs = unthunk(Δenvs′) f(A, x) = fix_global_phases(x, ctmrg_iteration(A, x, alg_fixed)[1]) @@ -148,7 +148,7 @@ function _rrule( return NoTangent(), ZeroTangent(), ∂F∂envs, NoTangent() end - return envs_fixed, leading_boundary_fixed_pullback + return (envs_fixed, info), leading_boundary_fixed_pullback end @doc """ From ba17a815092a5e17cdba12ded95110c449ca9817 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Thu, 6 Feb 2025 15:09:06 +0100 Subject: [PATCH 11/26] Make changes runnable --- src/PEPSKit.jl | 7 +++---- src/algorithms/ctmrg/simultaneous.jl | 1 + src/algorithms/optimization/fixed_point_differentiation.jl | 2 +- src/algorithms/optimization/peps_optimization.jl | 6 +++--- test/ctmrg/gradients.jl | 4 +++- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/PEPSKit.jl b/src/PEPSKit.jl index 13d3cca1..feb31244 100644 --- a/src/PEPSKit.jl +++ b/src/PEPSKit.jl @@ -55,11 +55,11 @@ include("algorithms/time_evolution/simpleupdate.jl") include("algorithms/toolbox.jl") +include("utility/symmetrization.jl") + include("algorithms/optimization/fixed_point_differentiation.jl") include("algorithms/optimization/peps_optimization.jl") -include("utility/symmetrization.jl") - """ module Defaults @@ -116,8 +116,7 @@ Module containing default algorithm parameter values and arguments. - `scheduler=Ref{Scheduler}(...)`: Multi-threading scheduler which can be accessed via `set_scheduler!` """ module Defaults - using TensorKit, KrylovKit, OhMyThreads - using Manopt + using TensorKit, KrylovKit, OptimKit, OhMyThreads using PEPSKit: LinSolver, FixedSpaceTruncation, diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index 85736603..00cd5cd6 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -56,6 +56,7 @@ function _condition_number(S::AbstractTensorMap) return maximum(b_diag) / minimum(b_diag) end end +@non_differentiable _condition_number(S::AbstractTensorMap) """ simultaneous_projectors(enlarged_corners::Array{E,3}, envs::CTMRGEnv, alg::ProjectorAlgorithm) diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index 2e2ccadf..03b2ec25 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -121,7 +121,7 @@ function _rrule( alg::SimultaneousCTMRG, ) @assert !isnothing(alg.projector_alg.svd_alg.rrule_alg) - envs = leading_boundary(envinit, state, alg) + envs, = leading_boundary(envinit, state, alg) envs_conv, info = ctmrg_iteration(state, envs, alg) envs_fixed, signs = gauge_fix(envs, envs_conv) diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 3bc94fe1..87d72158 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -70,17 +70,17 @@ The function returns a `NamedTuple` which contains the following entries: """ function fixedpoint( operator, peps₀::InfinitePEPS{F}, env₀::CTMRGEnv=CTMRGEnv(peps₀, field(F)^20); kwargs... -) +) where {F} alg = fixedpoint_selector(; kwargs...) # TODO: implement fixedpoint_selector return fixedpoint(operator, peps₀, env₀, alg) end function fixedpoint( operator, - peps₀::InfinitePEPS{F}, + peps₀::InfinitePEPS, env₀::CTMRGEnv, alg::PEPSOptimize; (finalize!)=OptimKit._finalize!, -) where {F} +) if isnothing(alg.symmetrization) retract = peps_retract else diff --git a/test/ctmrg/gradients.jl b/test/ctmrg/gradients.jl index 4227cc3e..4bf4aebe 100644 --- a/test/ctmrg/gradients.jl +++ b/test/ctmrg/gradients.jl @@ -69,7 +69,9 @@ steps = -0.01:0.005:0.01 inner=PEPSKit.real_inner, ) do (peps, envs) E, g = Zygote.withgradient(peps) do psi - envs2, = PEPSKit.hook_pullback(leading_boundary, envs, psi, ctmrg_alg; alg_rrule) + envs2, = PEPSKit.hook_pullback( + leading_boundary, envs, psi, ctmrg_alg; alg_rrule + ) return cost_function(psi, envs2, models[i]) end From 3dc602f5258c93eda78b9923aff2c2a80f754688 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Thu, 6 Feb 2025 16:46:37 +0100 Subject: [PATCH 12/26] Add collection of truncation errors, condition numbers, etc. during optimization and update `fixedpoint` return values --- src/algorithms/ctmrg/sequential.jl | 6 +- .../optimization/peps_optimization.jl | 99 +++++++++++++------ src/utility/symmetrization.jl | 20 ---- 3 files changed, 71 insertions(+), 54 deletions(-) diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 5249e780..1488f706 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -55,10 +55,10 @@ Compute CTMRG projectors in the `:sequential` scheme either for an entire column for a specific `coordinate` (where `dir=WEST` is already implied in the `:sequential` scheme). """ function sequential_projectors( - col::Int, state::InfiniteSquareNetwork, envs::CTMRGEnv, alg::ProjectorAlgorithm -) + col::Int, state::InfiniteSquareNetwork{T}, envs::CTMRGEnv, alg::ProjectorAlgorithm +) where {T} # SVD half-infinite environment column-wise - ϵ = Zygote.Buffer(zeros(real(scalartype(envs)), size(envs, 2))) + ϵ = Zygote.Buffer(zeros(real(scalartype(T)), size(envs, 2))) S = Zygote.Buffer( zeros(size(envs, 2)), tensormaptype(spacetype(T), 1, 1, real(scalartype(T))) ) diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 87d72158..50f3456d 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -43,13 +43,12 @@ function PEPSOptimize(; end """ - fixedpoint(operator, peps₀::InfinitePEPS{F}, [env₀::CTMRGEnv]; kwargs...) fixedpoint(operator, peps₀::InfinitePEPS{T}, alg::PEPSOptimize, [env₀::CTMRGEnv]; - finalize!=OptimKit._finalize!, symmetrization=nothing) where {T} + finalize!=OptimKit._finalize!) where {T} -Optimize `peps₀` with respect to the `operator` according to the parameters supplied -in `alg`. The initial environment `env₀` serves as an initial guess for the first CTMRG run. +Optimize `operator` starting from `peps₀` according to the parameters supplied in `alg`. +The initial environment `env₀` serves as an initial guess for the first CTMRG run. By default, a random initial environment is used. The `finalize!` kwarg can be used to insert a function call after each optimization step @@ -57,30 +56,33 @@ by utilizing the `finalize!` kwarg of `OptimKit.optimize`. The function maps `(peps, envs), f, g = finalize!((peps, envs), f, g, numiter)`. The `symmetrization` kwarg accepts `nothing` or a `SymmetrizationStyle`, in which case the PEPS and PEPS gradient are symmetrized after each optimization iteration. Note that this -requires a symmmetric `ψ₀` and `env₀` to converge properly. +requires a symmmetric `peps₀` and `env₀` to converge properly. -The function returns a `NamedTuple` which contains the following entries: -- `peps`: final `InfinitePEPS` -- `env`: `CTMRGEnv` corresponding to the final PEPS -- `E`: final energy -- `E_history`: convergence history of the energy function -- `grad`: final energy gradient -- `gradnorm_history`: convergence history of the energy gradient norms -- `numfg`: total number of calls to the energy function +The function returns the final PEPS, CTMRG environment and cost value, as well as an +information `NamedTuple` which contains the following entries: +- `last_gradient`: last gradient of the cost function +- `fg_evaluations`: number of evaluations of the cost and gradient function +- `costs`: history of cost values +- `gradnorms`: history of gradient norms +- `truncation_errors`: history of truncation errors of the boundary algorithm +- `condition_numbers`: history of condition numbers of the CTMRG environments +- `gradnorms_unitcell`: history of gradient norms for each respective unit cell entry +- `times`: history of times each optimization step took """ function fixedpoint( - operator, peps₀::InfinitePEPS{F}, env₀::CTMRGEnv=CTMRGEnv(peps₀, field(F)^20); kwargs... -) where {F} + operator, peps₀::InfinitePEPS{T}, env₀::CTMRGEnv=CTMRGEnv(peps₀, field(T)^20); kwargs... +) where {T} alg = fixedpoint_selector(; kwargs...) # TODO: implement fixedpoint_selector return fixedpoint(operator, peps₀, env₀, alg) end function fixedpoint( operator, - peps₀::InfinitePEPS, + peps₀::InfinitePEPS{T}, env₀::CTMRGEnv, alg::PEPSOptimize; (finalize!)=OptimKit._finalize!, -) +) where {T} + # setup retract and finalize! for symmetrization if isnothing(alg.symmetrization) retract = peps_retract else @@ -89,6 +91,7 @@ function fixedpoint( finalize! = (x, f, g, numiter) -> fin!(symm_finalize!(x, f, g, numiter)..., numiter) end + # check realness compatibility if scalartype(env₀) <: Real && iterscheme(alg.gradient_alg) == :fixed env₀ = complex(env₀) @warn "the provided real environment was converted to a complex environment since \ @@ -96,35 +99,49 @@ function fixedpoint( with purely real environments" end - (peps, env), E, ∂E, numfg, convhistory = optimize( + # initialize info collection vectors + truncation_errors = Vector{real(scalartype(T))}() + condition_numbers = Vector{real(scalartype(T))}() + gradnorms_unitcell = Vector{Matrix{real(scalartype(T))}}() + times = Float64[] + + # optimize operator cost function + (peps_final, env_final), cost, ∂cost, numfg, convergence_history = optimize( (peps₀, env₀), alg.optimizer; retract, inner=real_inner, finalize! - ) do (peps, envs) + ) do (peps, env) + start_time = time_ns() E, gs = withgradient(peps) do ψ - envs´, = hook_pullback( + env′, info = hook_pullback( leading_boundary, - envs, + env, ψ, alg.boundary_alg; alg_rrule=alg.gradient_alg, ) ignore_derivatives() do - alg.reuse_env && update!(envs, envs´) + alg.reuse_env && update!(env, env′) + push!(truncation_errors, info.truncation_error) + push!(condition_numbers, info.condition_number) end - return cost_function(ψ, envs´, operator) + return cost_function(ψ, env′, operator) end g = only(gs) # `withgradient` returns tuple of gradients `gs` + push!(gradnorms_unitcell, norm.(g.A)) + push!(times, (time_ns() - start_time) * 1e-9) return E, g end - return (; - peps, - env, - E, - E_history=convhistory[:, 1], - grad=∂E, - gradnorm_history=convhistory[:, 2], - numfg, + info = ( + last_gradient=∂cost, + fg_evaluations=numfg, + costs=convergence_history[:, 1], + gradnorms=convergence_history[:, 2], + truncation_errors, + condition_numbers, + gradnorms_unitcell, + times, ) + return peps_final, env_final, cost, info end # Update PEPS unit cell in non-mutating way @@ -138,3 +155,23 @@ end # Take real valued part of dot product real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) + +""" + symmetrize_retract_and_finalize!(symm::SymmetrizationStyle) + +Return the `retract` and `finalize!` function for symmetrizing the `peps` and `grad` tensors. +""" +function symmetrize_retract_and_finalize!(symm::SymmetrizationStyle) + finf = function symmetrize_finalize!((peps, envs), E, grad, _) + grad_symm = symmetrize!(grad, symm) + return (peps, envs), E, grad_symm + end + retf = function symmetrize_retract((peps, envs), η, α) + peps_symm = deepcopy(peps) + peps_symm.A .+= η.A .* α + envs′ = deepcopy(envs) + symmetrize!(peps_symm, symm) + return (peps_symm, envs′), η + end + return retf, finf +end diff --git a/src/utility/symmetrization.jl b/src/utility/symmetrization.jl index f1b51920..1f53f930 100644 --- a/src/utility/symmetrization.jl +++ b/src/utility/symmetrization.jl @@ -216,23 +216,3 @@ function symmetrize!(peps::InfinitePEPS, symm::RotateReflect) end return peps end - -""" - symmetrize_retract_and_finalize!(symm::SymmetrizationStyle) - -Return the `retract` and `finalize!` function for symmetrizing the `peps` and `grad` tensors. -""" -function symmetrize_retract_and_finalize!(symm::SymmetrizationStyle) - finf = function symmetrize_finalize!((peps, envs), E, grad, _) - grad_symm = symmetrize!(grad, symm) - return (peps, envs), E, grad_symm - end - retf = function symmetrize_retract((peps, envs), η, α) - peps_symm = deepcopy(peps) - peps_symm.A .+= η.A .* α - e = deepcopy(envs) - symmetrize!(peps_symm, symm) - return (peps_symm, e), η - end - return retf, finf -end From e773be597fcec9fdde77e1dca46e4d763200be07 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Thu, 6 Feb 2025 17:12:38 +0100 Subject: [PATCH 13/26] Adapt tests and examples to new fixedpoint return values --- README.md | 4 ++-- docs/src/index.md | 4 ++-- examples/heisenberg.jl | 4 ++-- test/heisenberg.jl | 26 +++++++++++++------------- test/j1j2_model.jl | 12 ++++++------ test/pwave.jl | 8 ++++---- test/tf_ising.jl | 16 ++++++++-------- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 9b2eb406..fee1a4f5 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ opt_alg = PEPSOptimize(; # ground state search state = InfinitePEPS(2, D) ctm, = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) -result = fixedpoint(H, state, ctm, opt_alg) +peps, env, E, = fixedpoint(H, state, ctm, opt_alg) -@show result.E # -0.6625... +@show E # -0.6625... ``` diff --git a/docs/src/index.md b/docs/src/index.md index a70793b5..dd1d66e5 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -37,7 +37,7 @@ opt_alg = PEPSOptimize(; # ground state search state = InfinitePEPS(2, D) ctm, = leading_boundary(CTMRGEnv(state, ComplexSpace(chi)), state, ctm_alg) -result = fixedpoint(H, state, ctm, opt_alg) +peps, env, E, = fixedpoint(H, state, ctm, opt_alg) -@show result.E # -0.6625... +@show E # -0.6625... ``` diff --git a/examples/heisenberg.jl b/examples/heisenberg.jl index 63d9bb22..d54a7eb3 100644 --- a/examples/heisenberg.jl +++ b/examples/heisenberg.jl @@ -27,5 +27,5 @@ opt_alg = PEPSOptimize(; # Of course there is a noticable bias for small χbond and χenv. ψ₀ = InfinitePEPS(2, χbond) env₀, = leading_boundary(CTMRGEnv(ψ₀, ℂ^χenv), ψ₀, ctm_alg) -result = fixedpoint(H, ψ, env₀, opt_alg₀) -@show result.E +peps, env, E, = fixedpoint(H, ψ, env₀, opt_alg₀) +@show E diff --git a/test/heisenberg.jl b/test/heisenberg.jl index 27d9db21..87048f85 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -21,14 +21,14 @@ E_ref = -0.6602310934799577 # initialize states Random.seed!(123) H = heisenberg_XYZ(InfiniteSquare()) - psi_init = InfinitePEPS(2, Dbond) - env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) + peps₀ = InfinitePEPS(2, Dbond) + env₀, = leading_boundary(CTMRGEnv(peps₀, ComplexSpace(χenv)), peps₀, ctm_alg) # optimize energy and compute correlation lengths - result = fixedpoint(H, psi_init, env_init, opt_alg) - ξ_h, ξ_v, = correlation_length(result.peps, result.env) + peps, env, E, = fixedpoint(H, peps₀, env₀, opt_alg) + ξ_h, ξ_v, = correlation_length(peps, env) - @test result.E ≈ E_ref atol = 1e-2 + @test E ≈ E_ref atol = 1e-2 @test all(@. ξ_h > 0 && ξ_v > 0) end @@ -37,14 +37,14 @@ end Random.seed!(456) unitcell = (1, 2) H = heisenberg_XYZ(InfiniteSquare(unitcell...)) - psi_init = InfinitePEPS(2, Dbond; unitcell) - env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) + peps₀ = InfinitePEPS(2, Dbond; unitcell) + env₀, = leading_boundary(CTMRGEnv(peps₀, ComplexSpace(χenv)), peps₀, ctm_alg) # optimize energy and compute correlation lengths - result = fixedpoint(H, psi_init, env_init, opt_alg) - ξ_h, ξ_v, = correlation_length(result.peps, result.env) + peps, env, E, = fixedpoint(H, peps₀, env₀, opt_alg) + ξ_h, ξ_v, = correlation_length(peps, env) - @test result.E ≈ 2 * E_ref atol = 1e-2 + @test E ≈ 2 * E_ref atol = 1e-2 @test all(@. ξ_h > 0 && ξ_v > 0) end @@ -92,9 +92,9 @@ end # continue with auto differentiation svd_alg_gmres = SVDAdjoint(; rrule_alg=GMRES(; tol=1e-5)) opt_alg_gmres = @set opt_alg.boundary_alg.projector_alg.svd_alg = svd_alg_gmres - result_final = fixedpoint(ham, peps, envs, opt_alg_gmres) # sensitivity warnings and degeneracies due to SU(2)? - ξ_h, ξ_v, = correlation_length(result_final.peps, result_final.env) - e_site2 = result_final.E / (N1 * N2) + peps_final, env_final, E_final, = fixedpoint(ham, peps, envs, opt_alg_gmres) # sensitivity warnings and degeneracies due to SU(2)? + ξ_h, ξ_v, = correlation_length(peps_final, env_final) + e_site2 = E_final / (N1 * N2) @info "Auto diff energy = $e_site2" @test e_site2 ≈ E_ref atol = 1e-2 @test all(@. ξ_h > 0 && ξ_v > 0) diff --git a/test/j1j2_model.jl b/test/j1j2_model.jl index b05b7294..9591c22b 100644 --- a/test/j1j2_model.jl +++ b/test/j1j2_model.jl @@ -19,17 +19,17 @@ opt_alg = PEPSOptimize(; # initialize states Random.seed!(91283219347) H = j1_j2(InfiniteSquare(); J2=0.25) -psi_init = product_peps(2, χbond; noise_amp=1e-1) -psi_init = symmetrize!(psi_init, RotateReflect()) -env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg); +peps₀ = product_peps(2, χbond; noise_amp=1e-1) +peps₀ = symmetrize!(peps₀, RotateReflect()) +env_init, = leading_boundary(CTMRGEnv(peps₀, ComplexSpace(χenv)), peps₀, ctm_alg); # find fixedpoint -result = fixedpoint(H, psi_init, env_init, opt_alg) -ξ_h, ξ_v, = correlation_length(result.peps, result.env) +peps, env, E, = fixedpoint(H, peps₀, env₀, opt_alg) +ξ_h, ξ_v, = correlation_length(peps, env) # compare against Juraj Hasik's data: # https://github.com/jurajHasik/j1j2_ipeps_states/blob/main/single-site_pg-C4v-A1/j20.25/state_1s_A1_j20.25_D2_chi_opt48.dat ξ_ref = -1 / log(0.2723596743547324) -@test result.E ≈ -0.5618837021945925 atol = 1e-3 +@test E ≈ -0.5618837021945925 atol = 1e-3 @test all(@. isapprox(ξ_h, ξ_ref; atol=1e-1) && isapprox(ξ_v, ξ_ref; atol=1e-1)) @test ξ_h ≈ ξ_v atol = 1e-6 # Test symmetrization of optimized PEPS and environment diff --git a/test/pwave.jl b/test/pwave.jl index 320bd130..00a65391 100644 --- a/test/pwave.jl +++ b/test/pwave.jl @@ -20,11 +20,11 @@ Pspace = Vect[FermionParity](0 => 1, 1 => 1) Vspace = Vect[FermionParity](0 => Dbond ÷ 2, 1 => Dbond ÷ 2) Envspace = Vect[FermionParity](0 => χenv ÷ 2, 1 => χenv ÷ 2) Random.seed!(91283219347) -psi_init = InfinitePEPS(Pspace, Vspace, Vspace; unitcell) -env_init, = leading_boundary(CTMRGEnv(psi_init, Envspace), psi_init, ctm_alg); +peps₀ = InfinitePEPS(Pspace, Vspace, Vspace; unitcell) +env₀, = leading_boundary(CTMRGEnv(peps₀, Envspace), peps₀, ctm_alg); # find fixedpoint -result = fixedpoint(H, psi_init, env_init, opt_alg) +_, _, E, = fixedpoint(H, peps₀, env₀, opt_alg) # comparison with Gaussian PEPS minimum at D=2 on 1000x1000 square lattice with aPBC -@test result.E / prod(size(psi_init)) ≈ -2.6241 atol = 5e-2 +@test E / prod(size(peps₀)) ≈ -2.6241 atol = 5e-2 diff --git a/test/tf_ising.jl b/test/tf_ising.jl index e0e39a98..c58c2683 100644 --- a/test/tf_ising.jl +++ b/test/tf_ising.jl @@ -27,17 +27,17 @@ opt_alg = PEPSOptimize(; # initialize states H = transverse_field_ising(InfiniteSquare(); g) Random.seed!(2928528935) -psi_init = InfinitePEPS(2, χbond) -env_init, = leading_boundary(CTMRGEnv(psi_init, ComplexSpace(χenv)), psi_init, ctm_alg) +peps₀ = InfinitePEPS(2, χbond) +env₀, = leading_boundary(CTMRGEnv(peps₀, ComplexSpace(χenv)), peps₀, ctm_alg) # find fixedpoint -result = fixedpoint(H, psi_init, env_init, opt_alg) -ξ_h, ξ_v, = correlation_length(result.peps, result.env) +peps, env, E, = fixedpoint(H, peps₀, env₀, opt_alg) +ξ_h, ξ_v, = correlation_length(peps, env) # compute magnetization -σx = TensorMap(scalartype(psi_init)[0 1; 1 0], ℂ^2, ℂ^2) +σx = TensorMap(scalartype(peps₀)[0 1; 1 0], ℂ^2, ℂ^2) M = LocalOperator(H.lattice, (CartesianIndex(1, 1),) => σx) -magn = expectation_value(result.peps, M, result.env) +magn = expectation_value(peps, M, env) @test result.E ≈ e atol = 1e-2 @test imag(magn) ≈ 0 atol = 1e-6 @@ -45,7 +45,7 @@ magn = expectation_value(result.peps, M, result.env) # find fixedpoint in polarized phase and compute correlations lengths H_polar = transverse_field_ising(InfiniteSquare(); g=4.5) -result_polar = fixedpoint(H_polar, psi_init, env_init, opt_alg) -ξ_h_polar, ξ_v_polar, = correlation_length(result_polar.peps, result_polar.env) +peps_polar, env_polar, = fixedpoint(H_polar, peps₀, env₀, opt_alg) +ξ_h_polar, ξ_v_polar, = correlation_length(peps_polar, env_polar) @test ξ_h_polar < ξ_h @test ξ_v_polar < ξ_v From a027a3f32447701ad356a2078773f38fe450e45d Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Thu, 6 Feb 2025 17:20:52 +0100 Subject: [PATCH 14/26] Fix tests (1st try) --- src/algorithms/ctmrg/sequential.jl | 2 +- test/tf_ising.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 1488f706..2a1914db 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -44,7 +44,7 @@ function ctmrg_iteration(state, envs::CTMRGEnv, alg::SequentialCTMRG) envs = rotate_north(envs, EAST) end - return envs, (; err=ϵ) + return envs, (; truncation_error, condition_number) end """ diff --git a/test/tf_ising.jl b/test/tf_ising.jl index c58c2683..f4ad251c 100644 --- a/test/tf_ising.jl +++ b/test/tf_ising.jl @@ -39,7 +39,7 @@ peps, env, E, = fixedpoint(H, peps₀, env₀, opt_alg) M = LocalOperator(H.lattice, (CartesianIndex(1, 1),) => σx) magn = expectation_value(peps, M, env) -@test result.E ≈ e atol = 1e-2 +@test E ≈ e atol = 1e-2 @test imag(magn) ≈ 0 atol = 1e-6 @test abs(magn) ≈ mˣ atol = 5e-2 From 799d9e82ca755ae697a911435c16a73eeec47e7e Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Thu, 6 Feb 2025 17:42:14 +0100 Subject: [PATCH 15/26] Fix tests (2nd try) --- src/algorithms/optimization/peps_optimization.jl | 3 ++- test/ctmrg/fixed_iterscheme.jl | 4 ++-- test/j1j2_model.jl | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 50f3456d..b152695e 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -44,7 +44,7 @@ end """ fixedpoint(operator, peps₀::InfinitePEPS{F}, [env₀::CTMRGEnv]; kwargs...) - fixedpoint(operator, peps₀::InfinitePEPS{T}, alg::PEPSOptimize, [env₀::CTMRGEnv]; + fixedpoint(operator, peps₀::InfinitePEPS{T}, env₀::CTMRGEnv, alg::PEPSOptimize; finalize!=OptimKit._finalize!) where {T} Optimize `operator` starting from `peps₀` according to the parameters supplied in `alg`. @@ -72,6 +72,7 @@ information `NamedTuple` which contains the following entries: function fixedpoint( operator, peps₀::InfinitePEPS{T}, env₀::CTMRGEnv=CTMRGEnv(peps₀, field(T)^20); kwargs... ) where {T} + throw(error("method not yet implemented")) alg = fixedpoint_selector(; kwargs...) # TODO: implement fixedpoint_selector return fixedpoint(operator, peps₀, env₀, alg) end diff --git a/test/ctmrg/fixed_iterscheme.jl b/test/ctmrg/fixed_iterscheme.jl index 39eccf5c..0cf75183 100644 --- a/test/ctmrg/fixed_iterscheme.jl +++ b/test/ctmrg/fixed_iterscheme.jl @@ -60,8 +60,8 @@ end # initialize states Random.seed!(91283219347) psi = InfinitePEPS(2, χbond) - env_init = CTMRGEnv(psi, ComplexSpace(χenv)) - env_conv1, = leading_boundary(env_init, psi, ctm_alg_iter) + env₀ = CTMRGEnv(psi, ComplexSpace(χenv)) + env_conv1, = leading_boundary(env₀, psi, ctm_alg_iter) # do extra iteration to get SVD env_conv2_iter, info_iter = ctmrg_iteration(psi, env_conv1, ctm_alg_iter) diff --git a/test/j1j2_model.jl b/test/j1j2_model.jl index 9591c22b..391f81a2 100644 --- a/test/j1j2_model.jl +++ b/test/j1j2_model.jl @@ -21,7 +21,7 @@ Random.seed!(91283219347) H = j1_j2(InfiniteSquare(); J2=0.25) peps₀ = product_peps(2, χbond; noise_amp=1e-1) peps₀ = symmetrize!(peps₀, RotateReflect()) -env_init, = leading_boundary(CTMRGEnv(peps₀, ComplexSpace(χenv)), peps₀, ctm_alg); +env₀, = leading_boundary(CTMRGEnv(peps₀, ComplexSpace(χenv)), peps₀, ctm_alg); # find fixedpoint peps, env, E, = fixedpoint(H, peps₀, env₀, opt_alg) From a5c5e2aed43a04b968411ed6e407432b499b39b8 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 11:15:44 +0100 Subject: [PATCH 16/26] Apply suggestions --- src/algorithms/ctmrg/sequential.jl | 9 +++--- .../optimization/peps_optimization.jl | 29 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 2a1914db..9841480d 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -55,12 +55,13 @@ Compute CTMRG projectors in the `:sequential` scheme either for an entire column for a specific `coordinate` (where `dir=WEST` is already implied in the `:sequential` scheme). """ function sequential_projectors( - col::Int, state::InfiniteSquareNetwork{T}, envs::CTMRGEnv, alg::ProjectorAlgorithm -) where {T} + col::Int, state::InfiniteSquareNetwork, envs::CTMRGEnv, alg::ProjectorAlgorithm +) # SVD half-infinite environment column-wise - ϵ = Zygote.Buffer(zeros(real(scalartype(T)), size(envs, 2))) + T = promote_type(real(scalartype(state)), real(scalartype(envs))) + ϵ = Zygote.Buffer(zeros(T, size(envs, 2))) S = Zygote.Buffer( - zeros(size(envs, 2)), tensormaptype(spacetype(T), 1, 1, real(scalartype(T))) + zeros(size(envs, 2)), tensormaptype(spacetype(eltype(state)), 1, 1, T) ) coordinates = eachcoordinate(envs)[:, col] projectors = dtmap(coordinates) do (r, c) diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index b152695e..9295a26b 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -43,13 +43,13 @@ function PEPSOptimize(; end """ - fixedpoint(operator, peps₀::InfinitePEPS{F}, [env₀::CTMRGEnv]; kwargs...) - fixedpoint(operator, peps₀::InfinitePEPS{T}, env₀::CTMRGEnv, alg::PEPSOptimize; - finalize!=OptimKit._finalize!) where {T} + fixedpoint(operator, peps₀::InfinitePEPS, env₀::CTMRGEnv; kwargs...) + fixedpoint(operator, peps₀::InfinitePEPS, env₀::CTMRGEnv, alg::PEPSOptimize; + finalize!=OptimKit._finalize!) -Optimize `operator` starting from `peps₀` according to the parameters supplied in `alg`. -The initial environment `env₀` serves as an initial guess for the first CTMRG run. -By default, a random initial environment is used. +Find the fixed point of `operator` (i.e. the ground state) starting from `peps₀` according +to the optimization parameters supplied in `alg`. The initial environment `env₀` serves as +an initial guess for the first CTMRG run. By default, a random initial environment is used. The `finalize!` kwarg can be used to insert a function call after each optimization step by utilizing the `finalize!` kwarg of `OptimKit.optimize`. @@ -70,19 +70,19 @@ information `NamedTuple` which contains the following entries: - `times`: history of times each optimization step took """ function fixedpoint( - operator, peps₀::InfinitePEPS{T}, env₀::CTMRGEnv=CTMRGEnv(peps₀, field(T)^20); kwargs... -) where {T} + operator, peps₀::InfinitePEPS, env₀::CTMRGEnv; kwargs... +) throw(error("method not yet implemented")) alg = fixedpoint_selector(; kwargs...) # TODO: implement fixedpoint_selector return fixedpoint(operator, peps₀, env₀, alg) end function fixedpoint( operator, - peps₀::InfinitePEPS{T}, + peps₀::InfinitePEPS, env₀::CTMRGEnv, alg::PEPSOptimize; (finalize!)=OptimKit._finalize!, -) where {T} +) # setup retract and finalize! for symmetrization if isnothing(alg.symmetrization) retract = peps_retract @@ -101,10 +101,11 @@ function fixedpoint( end # initialize info collection vectors - truncation_errors = Vector{real(scalartype(T))}() - condition_numbers = Vector{real(scalartype(T))}() - gradnorms_unitcell = Vector{Matrix{real(scalartype(T))}}() - times = Float64[] + T = promote_type(real(scalartype(peps₀)), real(scalartype(env₀))) + truncation_errors = Vector{T}() + condition_numbers = Vector{T}() + gradnorms_unitcell = Vector{Matrix{T}}() + times = Vector{Float64}() # optimize operator cost function (peps_final, env_final), cost, ∂cost, numfg, convergence_history = optimize( From 3bad3da6e314d83de656d64b17656cbd8632244c Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 11:33:59 +0100 Subject: [PATCH 17/26] Rename `envs` to `env` --- examples/boundary_mps.jl | 6 +- examples/heisenberg_evol/heis_tools.jl | 10 +- examples/heisenberg_evol/simpleupdate.jl | 10 +- examples/hubbard_su.jl | 6 +- .../contractions/ctmrg_contractions.jl | 230 +++++++++--------- src/algorithms/ctmrg/ctmrg.jl | 20 +- src/algorithms/ctmrg/sequential.jl | 70 +++--- src/algorithms/ctmrg/simultaneous.jl | 38 +-- src/algorithms/ctmrg/sparse_environments.jl | 28 +-- .../fixed_point_differentiation.jl | 36 +-- .../optimization/peps_optimization.jl | 16 +- src/algorithms/toolbox.jl | 30 +-- src/environments/vumps_environments.jl | 6 +- src/operators/transfermatrix.jl | 4 +- test/boundarymps/vumps.jl | 6 +- test/ctmrg/gradients.jl | 8 +- test/ctmrg/jacobian_real_linear.jl | 12 +- test/heisenberg.jl | 8 +- 18 files changed, 269 insertions(+), 275 deletions(-) diff --git a/examples/boundary_mps.jl b/examples/boundary_mps.jl index a8d8578d..1f8ccbbb 100644 --- a/examples/boundary_mps.jl +++ b/examples/boundary_mps.jl @@ -26,7 +26,7 @@ T = InfiniteTransferPEPS(peps, 1, 1) mps = PEPSKit.initializeMPS(T, [ComplexSpace(20)]) # We then find the leading boundary MPS fixed point using the VUMPS algorithm -mps, envs, ϵ = leading_boundary(mps, T, VUMPS()) +mps, env, ϵ = leading_boundary(mps, T, VUMPS()) # The norm of the state per unit cell is then given by the expectation value N = abs(prod(expectation_value(mps, T))) @@ -52,7 +52,7 @@ peps2 = InfinitePEPS(ComplexSpace(2), ComplexSpace(2); unitcell=(2, 2)) T2 = PEPSKit.MultilineTransferPEPS(peps2, 1) mps2 = PEPSKit.initializeMPS(T2, fill(ComplexSpace(20), 2, 2)) -mps2, envs2, ϵ = leading_boundary(mps2, T2, VUMPS()) +mps2, env2, ϵ = leading_boundary(mps2, T2, VUMPS()) N2 = abs(prod(expectation_value(mps2, T2))) ctm2, = leading_boundary( @@ -77,7 +77,7 @@ pepo = ising_pepo(1) T3 = InfiniteTransferPEPO(peps, pepo, 1, 1) mps3 = PEPSKit.initializeMPS(T3, [ComplexSpace(20)]) -mps3, envs3, ϵ = leading_boundary(mps3, T3, VUMPS()) +mps3, env3, ϵ = leading_boundary(mps3, T3, VUMPS()) @show N3 = abs(prod(expectation_value(mps3, T3))) # These objects and routines can be used to optimize PEPS fixed points of 3D partition diff --git a/examples/heisenberg_evol/heis_tools.jl b/examples/heisenberg_evol/heis_tools.jl index 80aec6f9..df68cee4 100644 --- a/examples/heisenberg_evol/heis_tools.jl +++ b/examples/heisenberg_evol/heis_tools.jl @@ -16,7 +16,7 @@ using PEPSKit """ Measure magnetization on each site """ -function cal_mags(peps::InfinitePEPS, envs::CTMRGEnv) +function cal_mags(peps::InfinitePEPS, env::CTMRGEnv) N1, N2 = size(peps) lattice = collect(space(t, 1) for t in peps.A) # detect symmetry on physical axis @@ -32,7 +32,7 @@ function cal_mags(peps::InfinitePEPS, envs::CTMRGEnv) return [ collect( expectation_value( - peps, LocalOperator(lattice, (CartesianIndex(r, c),) => Sa), envs + peps, LocalOperator(lattice, (CartesianIndex(r, c),) => Sa), env ) for (r, c) in Iterators.product(1:N1, 1:N2) ) for Sa in Sas ] @@ -41,11 +41,11 @@ end """ Measure physical quantities for Heisenberg model """ -function measure_heis(peps::InfinitePEPS, H::LocalOperator, envs::CTMRGEnv) +function measure_heis(peps::InfinitePEPS, H::LocalOperator, env::CTMRGEnv) results = Dict{String,Any}() N1, N2 = size(peps) - results["e_site"] = costfun(peps, envs, H) / (N1 * N2) - results["mag"] = cal_mags(peps, envs) + results["e_site"] = costfun(peps, env, H) / (N1 * N2) + results["mag"] = cal_mags(peps, env) results["mag_norm"] = collect( norm([mags[r, c] for mags in results["mag"]]) for (r, c) in Iterators.product(1:N1, 1:N2) diff --git a/examples/heisenberg_evol/simpleupdate.jl b/examples/heisenberg_evol/simpleupdate.jl index b16e6868..ee8fedba 100644 --- a/examples/heisenberg_evol/simpleupdate.jl +++ b/examples/heisenberg_evol/simpleupdate.jl @@ -3,7 +3,7 @@ include("heis_tools.jl") # benchmark data is from Phys. Rev. B 94, 035133 (2016) Dbond, χenv, symm = 4, 16, Trivial trscheme_peps = truncerr(1e-10) & truncdim(Dbond) -trscheme_envs = truncerr(1e-10) & truncdim(χenv) +trscheme_env = truncerr(1e-10) & truncdim(χenv) N1, N2 = 2, 2 # Heisenberg model Hamiltonian # (already only includes nearest neighbor terms) @@ -41,10 +41,10 @@ for (dt, tol) in zip(dts, tols) end # measure physical quantities with CTMRG peps_ = InfinitePEPS(peps) -envs = CTMRGEnv(rand, Float64, peps_, Espace) -ctm_alg = SequentialCTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme_envs) -envs = leading_boundary(envs, peps_, ctm_alg) -meas = measure_heis(peps_, ham, envs) +env₀ = CTMRGEnv(rand, Float64, peps_, Espace) +ctm_alg = SequentialCTMRG(; tol=1e-10, verbosity=2, trscheme=trscheme_env) +env = leading_boundary(env₀, peps_, ctm_alg) +meas = measure_heis(peps_, ham, env) display(meas) @info @sprintf("Energy = %.8f\n", meas["e_site"]) @info @sprintf("Staggered magnetization = %.8f\n", mean(meas["mag_norm"])) diff --git a/examples/hubbard_su.jl b/examples/hubbard_su.jl index 3e0f6542..9c1468be 100644 --- a/examples/hubbard_su.jl +++ b/examples/hubbard_su.jl @@ -40,10 +40,10 @@ peps = InfinitePEPS(peps) # CTMRG χenv0, χenv = 6, 20 Espace = Vect[fℤ₂](0 => χenv0 / 2, 1 => χenv0 / 2) -envs = CTMRGEnv(randn, Float64, peps, Espace) +env = CTMRGEnv(randn, Float64, peps, Espace) for χ in [χenv0, χenv] ctm_alg = SequentialCTMRG(; maxiter=300, tol=1e-7) - envs, = leading_boundary(envs, peps, ctm_alg) + env, = leading_boundary(env, peps, ctm_alg) end # Benchmark values of the ground state energy from @@ -53,7 +53,7 @@ Es_exact = Dict(0 => -1.62, 2 => -0.176, 4 => 0.8603, 6 => -0.6567, 8 => -0.5243 E_exact = Es_exact[U] - U / 2 # measure energy -E = cost_function(peps, envs, ham) / (N1 * N2) +E = cost_function(peps, env, ham) / (N1 * N2) @info "Energy = $E" @info "Benchmark energy = $E_exact" @test isapprox(E, E_exact; atol=5e-2) diff --git a/src/algorithms/contractions/ctmrg_contractions.jl b/src/algorithms/contractions/ctmrg_contractions.jl index 6851babf..2ec63fcf 100644 --- a/src/algorithms/contractions/ctmrg_contractions.jl +++ b/src/algorithms/contractions/ctmrg_contractions.jl @@ -7,8 +7,8 @@ const CTMRGCornerTensor{T,S} = AbstractTensorMap{T,S,1,1} # ---------------------------- """ - enlarge_northwest_corner((row, col), envs, ket::InfinitePEPS, bra::InfinitePEPS=ket) - enlarge_northwest_corner((row, col), envs, partfunc::InfinitePartitionFunction) + enlarge_northwest_corner((row, col), env, ket::InfinitePEPS, bra::InfinitePEPS=ket) + enlarge_northwest_corner((row, col), env, partfunc::InfinitePartitionFunction) enlarge_northwest_corner(E_west, C_northwest, E_north, ket::PEPSTensor, bra::PEPSTensor=ket) enlarge_northwest_corner(E_west, C_northwest, E_north, partfunc::PartitionFunctionTensor) @@ -28,11 +28,11 @@ The networks and tensors (denoted `A` below) can correspond to either: ``` """ function enlarge_northwest_corner( - (row, col), envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket ) - E_west = envs.edges[WEST, row, _prev(col, end)] - C_northwest = envs.corners[NORTHWEST, _prev(row, end), _prev(col, end)] - E_north = envs.edges[NORTH, _prev(row, end), col] + E_west = env.edges[WEST, row, _prev(col, end)] + C_northwest = env.corners[NORTHWEST, _prev(row, end), _prev(col, end)] + E_north = env.edges[NORTH, _prev(row, end), col] return enlarge_northwest_corner( E_west, C_northwest, E_north, ket[row, col], bra[row, col] ) @@ -51,10 +51,10 @@ function enlarge_northwest_corner( ket[d; D3 D_Eabove D_Sabove D1] * conj(bra[d; D4 D_Ebelow D_Sbelow D2]) end -function enlarge_northwest_corner((row, col), envs::CTMRGEnv, partfunc::InfinitePF) - E_west = envs.edges[WEST, row, _prev(col, end)] - C_northwest = envs.corners[NORTHWEST, _prev(row, end), _prev(col, end)] - E_north = envs.edges[NORTH, _prev(row, end), col] +function enlarge_northwest_corner((row, col), env::CTMRGEnv, partfunc::InfinitePF) + E_west = env.edges[WEST, row, _prev(col, end)] + C_northwest = env.corners[NORTHWEST, _prev(row, end), _prev(col, end)] + E_north = env.edges[NORTH, _prev(row, end), col] return enlarge_northwest_corner(E_west, C_northwest, E_north, partfunc[row, col]) end function enlarge_northwest_corner( @@ -71,8 +71,8 @@ function enlarge_northwest_corner( end """ - enlarge_northeast_corner((row, col), envs, ket::InfinitePEPS, bra::InfinitePEPS=ket) - enlarge_northeast_corner((row, col), envs, partfunc::InfinitePartitionFunction) + enlarge_northeast_corner((row, col), env, ket::InfinitePEPS, bra::InfinitePEPS=ket) + enlarge_northeast_corner((row, col), env, partfunc::InfinitePartitionFunction) enlarge_northeast_corner(E_north, C_northeast, E_east, ket::PEPSTensor, bra::PEPSTensor=ket) enlarge_northeast_corner(E_north, C_northeast, E_east, partfunc::PartitionFunctionTensor) @@ -92,11 +92,11 @@ The networks and tensors (denoted `A` below) can correspond to either: ``` """ function enlarge_northeast_corner( - (row, col), envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket ) - E_north = envs.edges[NORTH, _prev(row, end), col] - C_northeast = envs.corners[NORTHEAST, _prev(row, end), _next(col, end)] - E_east = envs.edges[EAST, row, _next(col, end)] + E_north = env.edges[NORTH, _prev(row, end), col] + C_northeast = env.corners[NORTHEAST, _prev(row, end), _next(col, end)] + E_east = env.edges[EAST, row, _next(col, end)] return enlarge_northeast_corner( E_north, C_northeast, E_east, ket[row, col], bra[row, col] ) @@ -115,10 +115,10 @@ function enlarge_northeast_corner( ket[d; D1 D3 D_Sabove D_Wabove] * conj(bra[d; D2 D4 D_Sbelow D_Wbelow]) end -function enlarge_northeast_corner((row, col), envs::CTMRGEnv, partfunc::InfinitePF) - E_north = envs.edges[NORTH, _prev(row, end), col] - C_northeast = envs.corners[NORTHEAST, _prev(row, end), _next(col, end)] - E_east = envs.edges[EAST, row, _next(col, end)] +function enlarge_northeast_corner((row, col), env::CTMRGEnv, partfunc::InfinitePF) + E_north = env.edges[NORTH, _prev(row, end), col] + C_northeast = env.corners[NORTHEAST, _prev(row, end), _next(col, end)] + E_east = env.edges[EAST, row, _next(col, end)] return enlarge_northeast_corner(E_north, C_northeast, E_east, partfunc[row, col]) end function enlarge_northeast_corner( @@ -135,8 +135,8 @@ function enlarge_northeast_corner( end """ - enlarge_southeast_corner((row, col), envs, ket::InfinitePEPS, bra::InfinitePEPS=ket) - enlarge_southeast_corner((row, col), envs, partfunc::InfinitePartitionFunction) + enlarge_southeast_corner((row, col), env, ket::InfinitePEPS, bra::InfinitePEPS=ket) + enlarge_southeast_corner((row, col), env, partfunc::InfinitePartitionFunction) enlarge_southeast_corner(E_east, C_southeast, E_south, ket::PEPSTensor, bra::PEPSTensor=ket) enlarge_southeast_corner(E_east, C_southeast, E_south, partfunc::PartitionFunctionTensor) @@ -156,11 +156,11 @@ The networks and tensors (denoted `A` below) can correspond to either: ``` """ function enlarge_southeast_corner( - (row, col), envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket ) - E_east = envs.edges[EAST, row, _next(col, end)] - C_southeast = envs.corners[SOUTHEAST, _next(row, end), _next(col, end)] - E_south = envs.edges[SOUTH, _next(row, end), col] + E_east = env.edges[EAST, row, _next(col, end)] + C_southeast = env.corners[SOUTHEAST, _next(row, end), _next(col, end)] + E_south = env.edges[SOUTH, _next(row, end), col] return enlarge_southeast_corner( E_east, C_southeast, E_south, ket[row, col], bra[row, col] ) @@ -179,10 +179,10 @@ function enlarge_southeast_corner( ket[d; D_Nabove D1 D3 D_Wabove] * conj(bra[d; D_Nbelow D2 D4 D_Wbelow]) end -function enlarge_southeast_corner((row, col), envs::CTMRGEnv, partfunc::InfinitePF) - E_east = envs.edges[EAST, row, _next(col, end)] - C_southeast = envs.corners[SOUTHEAST, _next(row, end), _next(col, end)] - E_south = envs.edges[SOUTH, _next(row, end), col] +function enlarge_southeast_corner((row, col), env::CTMRGEnv, partfunc::InfinitePF) + E_east = env.edges[EAST, row, _next(col, end)] + C_southeast = env.corners[SOUTHEAST, _next(row, end), _next(col, end)] + E_south = env.edges[SOUTH, _next(row, end), col] return enlarge_southeast_corner(E_east, C_southeast, E_south, partfunc[row, col]) end function enlarge_southeast_corner( @@ -199,8 +199,8 @@ function enlarge_southeast_corner( end """ - enlarge_southwest_corner((row, col), envs, ket::InfinitePEPS, bra::InfinitePEPS=ket) - enlarge_southwest_corner((row, col), envs, partfunc::InfinitePartitionFunction) + enlarge_southwest_corner((row, col), env, ket::InfinitePEPS, bra::InfinitePEPS=ket) + enlarge_southwest_corner((row, col), env, partfunc::InfinitePartitionFunction) enlarge_southwest_corner(E_south, C_southwest, E_west, ket::PEPSTensor, bra::PEPSTensor=ket) enlarge_southwest_corner(E_south, C_southwest, E_west, partfunc::PartitionFunctionTensor) @@ -220,11 +220,11 @@ The networks and tensors (denoted `A` below) can correspond to either: ``` """ function enlarge_southwest_corner( - (row, col), envs::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, ket::InfinitePEPS, bra::InfinitePEPS=ket ) - E_south = envs.edges[SOUTH, _next(row, end), col] - C_southwest = envs.corners[SOUTHWEST, _next(row, end), _prev(col, end)] - E_west = envs.edges[WEST, row, _prev(col, end)] + E_south = env.edges[SOUTH, _next(row, end), col] + C_southwest = env.corners[SOUTHWEST, _next(row, end), _prev(col, end)] + E_west = env.edges[WEST, row, _prev(col, end)] return enlarge_southwest_corner( E_south, C_southwest, E_west, ket[row, col], bra[row, col] ) @@ -243,10 +243,10 @@ function enlarge_southwest_corner( ket[d; D_Nabove D_Eabove D1 D3] * conj(bra[d; D_Nbelow D_Ebelow D2 D4]) end -function enlarge_southwest_corner((row, col), envs::CTMRGEnv, partfunc::InfinitePF) - E_south = envs.edges[SOUTH, _next(row, end), col] - C_southwest = envs.corners[SOUTHWEST, _next(row, end), _prev(col, end)] - E_west = envs.edges[WEST, row, _prev(col, end)] +function enlarge_southwest_corner((row, col), env::CTMRGEnv, partfunc::InfinitePF) + E_south = env.edges[SOUTH, _next(row, end), col] + C_southwest = env.corners[SOUTHWEST, _next(row, end), _prev(col, end)] + E_west = env.edges[WEST, row, _prev(col, end)] return enlarge_southwest_corner(E_south, C_southwest, E_west, partfunc[row, col]) end function enlarge_southwest_corner( @@ -937,7 +937,7 @@ function renormalize_corner( end """ - renormalize_northwest_corner((row, col), enlarged_envs::CTMRGEnv, P_left, P_right) + renormalize_northwest_corner((row, col), enlarged_env::CTMRGEnv, P_left, P_right) renormalize_northwest_corner(quadrant::AbstractTensorMap{T,S,3,3}, P_left, P_right) where {T,S} renormalize_northwest_corner(E_west, C_northwest, E_north, P_left, P_right, ket::PEPSTensor, bra::PEPSTensor=ket) renormalize_northwest_corner(quadrant::AbstractTensorMap{T,S,2,2}, P_left, P_right) where {T,S} @@ -959,9 +959,9 @@ Here `A` denotes either: - a local pair of 'ket' and 'bra' `PEPSTensor`s - a `PartitionFunctionTensor`. """ -function renormalize_northwest_corner((row, col), enlarged_envs, P_left, P_right) +function renormalize_northwest_corner((row, col), enlarged_env, P_left, P_right) return renormalize_northwest_corner( - enlarged_envs[NORTHWEST, row, col], + enlarged_env[NORTHWEST, row, col], P_left[NORTH, row, col], P_right[WEST, _next(row, end), col], ) @@ -1001,7 +1001,7 @@ function renormalize_northwest_corner( end """ - renormalize_northeast_corner((row, col), enlarged_envs::CTMRGEnv, P_left, P_right) + renormalize_northeast_corner((row, col), enlarged_env::CTMRGEnv, P_left, P_right) renormalize_northwest_corner(quadrant::AbstractTensorMap{T,S,3,3}, P_left, P_right) where {T,S} renormalize_northeast_corner(E_north, C_northeast, E_east, P_left, P_right, ket::PEPSTensor, bra::PEPSTensor=ket) renormalize_northwest_corner(quadrant::AbstractTensorMap{T,S,2,2}, P_left, P_right) where {T,S} @@ -1023,9 +1023,9 @@ Here `A` denotes either: - a local pair of 'ket' and 'bra' `PEPSTensor`s - a `PartitionFunctionTensor`. """ -function renormalize_northeast_corner((row, col), enlarged_envs, P_left, P_right) +function renormalize_northeast_corner((row, col), enlarged_env, P_left, P_right) return renormalize_northeast_corner( - enlarged_envs[NORTHEAST, row, col], + enlarged_env[NORTHEAST, row, col], P_left[EAST, row, col], P_right[NORTH, row, _prev(col, end)], ) @@ -1067,7 +1067,7 @@ function renormalize_northeast_corner( end """ - renormalize_southeast_corner((row, col), enlarged_envs::CTMRGEnv, P_left, P_right) + renormalize_southeast_corner((row, col), enlarged_env::CTMRGEnv, P_left, P_right) renormalize_southeast_corner(quadrant::AbstractTensorMap{T,S,3,3}, P_left, P_right) where {T,S} renormalize_southeast_corner(E_east, C_southeast, E_south, P_left, P_right, ket::PEPSTensor, bra::PEPSTensor=ket) renormalize_southeast_corner(quadrant::AbstractTensorMap{T,S,2,2}, P_left, P_right) where {T,S} @@ -1089,9 +1089,9 @@ Here `A` denotes either: - a local pair of 'ket' and 'bra' `PEPSTensor`s - a `PartitionFunctionTensor`. """ -function renormalize_southeast_corner((row, col), enlarged_envs, P_left, P_right) +function renormalize_southeast_corner((row, col), enlarged_env, P_left, P_right) return renormalize_southeast_corner( - enlarged_envs[SOUTHEAST, row, col], + enlarged_env[SOUTHEAST, row, col], P_left[SOUTH, row, col], P_right[EAST, _prev(row, end), col], ) @@ -1132,7 +1132,7 @@ function renormalize_southeast_corner( end """ - renormalize_southwest_corner((row, col), enlarged_envs::CTMRGEnv, P_left, P_right) + renormalize_southwest_corner((row, col), enlarged_env::CTMRGEnv, P_left, P_right) renormalize_southwest_corner(quadrant::AbstractTensorMap{T,S,3,3}, P_left, P_right) where {T,S} renormalize_southwest_corner(E_south, C_southwest, E_west, P_left, P_right, ket::PEPSTensor, bra::PEPSTensor=ket) renormalize_southwest_corner(quadrant::AbstractTensorMap{T,S,2,2}, P_left, P_right) where {T,S} @@ -1154,9 +1154,9 @@ Here `A` denotes either: - a pair of 'ket' and 'bra' `PEPSTensor`s - a `PartitionFunctionTensor`. """ -function renormalize_southwest_corner((row, col), enlarged_envs, P_left, P_right) +function renormalize_southwest_corner((row, col), enlarged_env, P_left, P_right) return renormalize_corner( - enlarged_envs[SOUTHWEST, row, col], + enlarged_env[SOUTHWEST, row, col], P_left[WEST, row, col], P_right[SOUTH, row, _next(col, end)], ) @@ -1196,7 +1196,7 @@ function renormalize_southwest_corner( end """ - renormalize_bottom_corner((r, c), envs, projectors) + renormalize_bottom_corner((r, c), env, projectors) Apply bottom projector to southwest corner and south edge. ``` @@ -1207,26 +1207,26 @@ Apply bottom projector to southwest corner and south edge. ``` """ function renormalize_bottom_corner( - (row, col), envs::CTMRGEnv{C,<:CTMRG_PEPS_EdgeTensor}, projectors + (row, col), env::CTMRGEnv{C,<:CTMRG_PEPS_EdgeTensor}, projectors ) where {C} - C_southwest = envs.corners[SOUTHWEST, row, _prev(col, end)] - E_south = envs.edges[SOUTH, row, col] + C_southwest = env.corners[SOUTHWEST, row, _prev(col, end)] + E_south = env.edges[SOUTH, row, col] P_bottom = projectors[1][row] return @autoopt @tensor corner[χ_in; χ_out] := E_south[χ_in D1 D2; χ1] * C_southwest[χ1; χ2] * P_bottom[χ2 D1 D2; χ_out] end function renormalize_bottom_corner( - (row, col), envs::CTMRGEnv{C,<:CTMRG_PF_EdgeTensor}, projectors + (row, col), env::CTMRGEnv{C,<:CTMRG_PF_EdgeTensor}, projectors ) where {C} - C_southwest = envs.corners[SOUTHWEST, row, _prev(col, end)] - E_south = envs.edges[SOUTH, row, col] + C_southwest = env.corners[SOUTHWEST, row, _prev(col, end)] + E_south = env.edges[SOUTH, row, col] P_bottom = projectors[1][row] return @autoopt @tensor corner[χ_in; χ_out] := E_south[χ_in D1; χ1] * C_southwest[χ1; χ2] * P_bottom[χ2 D1; χ_out] end """ - renormalize_top_corner((row, col), envs::CTMRGEnv, projectors) + renormalize_top_corner((row, col), env::CTMRGEnv, projectors) Apply top projector to northwest corner and north edge. ``` @@ -1236,9 +1236,9 @@ Apply top projector to northwest corner and north edge. | ``` """ -function renormalize_top_corner((row, col), envs::CTMRGEnv, projectors) - C_northwest = envs.corners[NORTHWEST, row, _prev(col, end)] - E_north = envs.edges[NORTH, row, col] +function renormalize_top_corner((row, col), env::CTMRGEnv, projectors) + C_northwest = env.corners[NORTHWEST, row, _prev(col, end)] + E_north = env.edges[NORTH, row, col] P_top = projectors[2][_next(row, end)] return renormalize_top_corner(C_northwest, E_north, P_top) end @@ -1254,9 +1254,9 @@ end # edges """ - renormalize_north_edge((row, col), envs, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket) + renormalize_north_edge((row, col), env, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket) renormalize_north_edge(E_north, P_left, P_right, ket::PEPSTensor, bra::PEPSTensor=ket) - renormalize_north_edge((row, col), envs, P_left, P_right, partfunc::InfinitePartitionFunction) + renormalize_north_edge((row, col), env, P_left, P_right, partfunc::InfinitePartitionFunction) renormalize_north_edge(E_north, P_left, P_right, partfunc::PartitionFunctionTensor) Absorb a local effective tensor `A` into the north edge using the given projectors and @@ -1274,10 +1274,10 @@ Here `A` denotes either: - a `PartitionFunctionTensor`. """ function renormalize_north_edge( - (row, col), envs::CTMRGEnv, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket ) return renormalize_north_edge( - envs.edges[NORTH, _prev(row, end), col], + env.edges[NORTH, _prev(row, end), col], P_left[NORTH, row, col], P_right[NORTH, row, _prev(col, end)], ket[row, col], @@ -1295,10 +1295,10 @@ function renormalize_north_edge( P_right[χ_W; χ1 D5 D6] end function renormalize_north_edge( - (row, col), envs::CTMRGEnv, P_left, P_right, partfunc::InfinitePF + (row, col), env::CTMRGEnv, P_left, P_right, partfunc::InfinitePF ) return renormalize_north_edge( - envs.edges[NORTH, _prev(row, end), col], + env.edges[NORTH, _prev(row, end), col], P_left[NORTH, row, col], P_right[NORTH, row, _prev(col, end)], partfunc[row, col], @@ -1315,9 +1315,9 @@ function renormalize_north_edge( end """ - renormalize_east_edge((row, col), envs, P_top, P_bottom, ket::InfinitePEPS, bra::InfinitePEPS=ket) + renormalize_east_edge((row, col), env, P_top, P_bottom, ket::InfinitePEPS, bra::InfinitePEPS=ket) renormalize_east_edge(E_east, P_top, P_bottom, ket::PEPSTensor, bra::PEPSTensor=ket) - renormalize_east_edge((row, col), envs, P_top, P_bottom, partfunc::InfinitePartitionFunction) + renormalize_east_edge((row, col), env, P_top, P_bottom, partfunc::InfinitePartitionFunction) renormalize_east_edge(E_east, P_top, P_bottom, partfunc::PartitionFunctionTensor) Absorb a blocal effective tensor into the east edge using the given projectors and @@ -1338,10 +1338,10 @@ Here `A` denotes either: - a `PartitionFunctionTensor`. """ function renormalize_east_edge( - (row, col), envs::CTMRGEnv, P_bottom, P_top, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, P_bottom, P_top, ket::InfinitePEPS, bra::InfinitePEPS=ket ) return renormalize_east_edge( - envs.edges[EAST, row, _next(col, end)], + env.edges[EAST, row, _next(col, end)], P_bottom[EAST, row, col, end], P_top[EAST, _prev(row, end), col], ket[row, col], @@ -1359,10 +1359,10 @@ function renormalize_east_edge( P_top[χ_N; χ1 D5 D6] end function renormalize_east_edge( - (row, col), envs::CTMRGEnv, P_bottom, P_top, partfunc::InfinitePF + (row, col), env::CTMRGEnv, P_bottom, P_top, partfunc::InfinitePF ) return renormalize_east_edge( - envs.edges[EAST, row, _next(col, end)], + env.edges[EAST, row, _next(col, end)], P_bottom[EAST, row, col, end], P_top[EAST, _prev(row, end), col], partfunc[row, col], @@ -1379,9 +1379,9 @@ function renormalize_east_edge( end """ - renormalize_south_edge((row, col), envs, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket) + renormalize_south_edge((row, col), env, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket) renormalize_south_edge(E_south, P_left, P_right, ket::PEPSTensor, bra::PEPSTensor=ket) - renormalize_south_edge((row, col), envs, P_left, P_right, partfunc::InfinitePartitionFunction) + renormalize_south_edge((row, col), env, P_left, P_right, partfunc::InfinitePartitionFunction) renormalize_south_edge(E_south, P_left, P_right, partfunc::PartitionFunctionTensor) Absorb a local effective tensor into the south edge using the given projectors and @@ -1400,10 +1400,10 @@ Here `A` denotes either: - a `PartitionFunctionTensor`. """ function renormalize_south_edge( - (row, col), envs::CTMRGEnv, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket + (row, col), env::CTMRGEnv, P_left, P_right, ket::InfinitePEPS, bra::InfinitePEPS=ket ) return renormalize_south_edge( - envs.edges[SOUTH, _next(row, end), col], + env.edges[SOUTH, _next(row, end), col], P_left[SOUTH, row, col], P_right[SOUTH, row, _next(col, end)], ket[row, col], @@ -1421,10 +1421,10 @@ function renormalize_south_edge( P_right[χ_E; χ1 D5 D6] end function renormalize_south_edge( - (row, col), envs::CTMRGEnv, P_left, P_right, partfunc::InfinitePF + (row, col), env::CTMRGEnv, P_left, P_right, partfunc::InfinitePF ) return renormalize_south_edge( - envs.edges[SOUTH, _next(row, end), col], + env.edges[SOUTH, _next(row, end), col], P_left[SOUTH, row, col], P_right[SOUTH, row, _next(col, end)], partfunc[row, col], @@ -1441,9 +1441,9 @@ function renormalize_south_edge( end """ - renormalize_west_edge((row, col), envs, P_top, P_bottom, ket::InfinitePEPS, bra::InfinitePEPS=ket) + renormalize_west_edge((row, col), env, P_top, P_bottom, ket::InfinitePEPS, bra::InfinitePEPS=ket) renormalize_west_edge(E_west, P_top, P_bottom, ket::PEPSTensor, bra::PEPSTensor=ket) - renormalize_west_edge((row, col), envs, P_top, P_bottom, partfunc::InfinitePartitionFunction) + renormalize_west_edge((row, col), env, P_top, P_bottom, partfunc::InfinitePartitionFunction) renormalize_west_edge(E_west, P_top, P_bottom, partfunc::PartitionFunctionTensor) Absorb a local effective tensor into the west edge using the given projectors and @@ -1465,14 +1465,14 @@ Here `A` denotes either: """ function renormalize_west_edge( # For simultaneous CTMRG scheme (row, col), - envs::CTMRGEnv, + env::CTMRGEnv, P_bottom::Array{Pb,3}, P_top::Array{Pt,3}, ket::InfinitePEPS, bra::InfinitePEPS=ket, ) where {Pt,Pb} return renormalize_west_edge( - envs.edges[WEST, row, _prev(col, end)], + env.edges[WEST, row, _prev(col, end)], P_bottom[WEST, row, col], P_top[WEST, _next(row, end), col], ket[row, col], @@ -1481,13 +1481,13 @@ function renormalize_west_edge( # For simultaneous CTMRG scheme end function renormalize_west_edge( # For sequential CTMRG scheme (row, col), - envs::CTMRGEnv, + env::CTMRGEnv, projectors, ket::InfinitePEPS, bra::InfinitePEPS=ket, ) return renormalize_west_edge( - envs.edges[WEST, row, _prev(col, end)], + env.edges[WEST, row, _prev(col, end)], projectors[1][row], projectors[2][_next(row, end)], ket[row, col], @@ -1506,13 +1506,13 @@ function renormalize_west_edge( end function renormalize_west_edge( # For simultaneous CTMRG scheme (row, col), - envs::CTMRGEnv, + env::CTMRGEnv, P_bottom::Array{Pb,3}, P_top::Array{Pt,3}, partfunc::InfinitePF, ) where {Pt,Pb} return renormalize_west_edge( - envs.edges[WEST, row, _prev(col, end)], + env.edges[WEST, row, _prev(col, end)], P_bottom[WEST, row, col], P_top[WEST, _next(row, end), col], partfunc[row, col], @@ -1520,12 +1520,12 @@ function renormalize_west_edge( # For simultaneous CTMRG scheme end function renormalize_west_edge( # For sequential CTMRG scheme (row, col), - envs::CTMRGEnv, + env::CTMRGEnv, projectors, partfunc::InfinitePF, ) return renormalize_west_edge( - envs.edges[WEST, row, _prev(col, end)], + env.edges[WEST, row, _prev(col, end)], projectors[1][row], projectors[2][_next(row, end)], partfunc[row, col], @@ -1566,52 +1566,52 @@ function fix_gauge_corner( end """ - fix_gauge_northwest_corner((row, col), envs, signs) + fix_gauge_northwest_corner((row, col), env, signs) Apply `fix_gauge_corner` to the northwest corner with appropriate row and column indices. """ -function fix_gauge_northwest_corner((row, col), envs::CTMRGEnv, signs) +function fix_gauge_northwest_corner((row, col), env::CTMRGEnv, signs) return fix_gauge_corner( - envs.corners[NORTHWEST, row, col], + env.corners[NORTHWEST, row, col], signs[WEST, row, col], signs[NORTH, row, _next(col, end)], ) end """ - fix_gauge_northeast_corner((row, col), envs, signs) + fix_gauge_northeast_corner((row, col), env, signs) Apply `fix_gauge_corner` to the northeast corner with appropriate row and column indices. """ -function fix_gauge_northeast_corner((row, col), envs::CTMRGEnv, signs) +function fix_gauge_northeast_corner((row, col), env::CTMRGEnv, signs) return fix_gauge_corner( - envs.corners[NORTHEAST, row, col], + env.corners[NORTHEAST, row, col], signs[NORTH, row, col], signs[EAST, _next(row, end), col], ) end """ - fix_gauge_southeast_corner((row, col), envs, signs) + fix_gauge_southeast_corner((row, col), env, signs) Apply `fix_gauge_corner` to the southeast corner with appropriate row and column indices. """ -function fix_gauge_southeast_corner((row, col), envs::CTMRGEnv, signs) +function fix_gauge_southeast_corner((row, col), env::CTMRGEnv, signs) return fix_gauge_corner( - envs.corners[SOUTHEAST, row, col], + env.corners[SOUTHEAST, row, col], signs[EAST, row, col], signs[SOUTH, row, _prev(col, end)], ) end """ - fix_gauge_southwest_corner((row, col), envs, signs) + fix_gauge_southwest_corner((row, col), env, signs) Apply `fix_gauge_corner` to the southwest corner with appropriate row and column indices. """ -function fix_gauge_southwest_corner((row, col), envs::CTMRGEnv, signs) +function fix_gauge_southwest_corner((row, col), env::CTMRGEnv, signs) return fix_gauge_corner( - envs.corners[SOUTHWEST, row, col], + env.corners[SOUTHWEST, row, col], signs[SOUTH, row, col], signs[WEST, _prev(row, end), col], ) @@ -1642,50 +1642,50 @@ function fix_gauge_edge( end """ - fix_gauge_north_edge((row, col), envs, signs) + fix_gauge_north_edge((row, col), env, signs) Apply `fix_gauge_edge` to the north edge with appropriate row and column indices. """ -function fix_gauge_north_edge((row, col), envs::CTMRGEnv, signs) +function fix_gauge_north_edge((row, col), env::CTMRGEnv, signs) return fix_gauge_edge( - envs.edges[NORTH, row, col], + env.edges[NORTH, row, col], signs[NORTH, row, col], signs[NORTH, row, _next(col, end)], ) end """ - fix_gauge_east_edge((row, col), envs, signs) + fix_gauge_east_edge((row, col), env, signs) Apply `fix_gauge_edge` to the east edge with appropriate row and column indices. """ -function fix_gauge_east_edge((row, col), envs::CTMRGEnv, signs) +function fix_gauge_east_edge((row, col), env::CTMRGEnv, signs) return fix_gauge_edge( - envs.edges[EAST, row, col], signs[EAST, row, col], signs[EAST, _next(row, end), col] + env.edges[EAST, row, col], signs[EAST, row, col], signs[EAST, _next(row, end), col] ) end """ - fix_gauge_south_edge((row, col), envs, signs) + fix_gauge_south_edge((row, col), env, signs) Apply `fix_gauge_edge` to the south edge with appropriate row and column indices. """ -function fix_gauge_south_edge((row, col), envs::CTMRGEnv, signs) +function fix_gauge_south_edge((row, col), env::CTMRGEnv, signs) return fix_gauge_edge( - envs.edges[SOUTH, row, col], + env.edges[SOUTH, row, col], signs[SOUTH, row, col], signs[SOUTH, row, _prev(col, end)], ) end """ - fix_gauge_south_edge((row, col), envs, signs) + fix_gauge_south_edge((row, col), env, signs) Apply `fix_gauge_edge` to the west edge with appropriate row and column indices. """ -function fix_gauge_west_edge((row, col), envs::CTMRGEnv, signs) +function fix_gauge_west_edge((row, col), env::CTMRGEnv, signs) return fix_gauge_edge( - envs.edges[WEST, row, col], signs[WEST, row, col], signs[WEST, _prev(row, end), col] + env.edges[WEST, row, col], signs[WEST, row, col], signs[WEST, _prev(row, end), col] ) end diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 7b8a8d6a..0392340c 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -103,27 +103,27 @@ function _singular_value_distance((S₁, S₂)) end """ - calc_convergence(envs, CS_old, TS_old) - calc_convergence(envs_new::CTMRGEnv, envs_old::CTMRGEnv) + calc_convergence(env, CS_old, TS_old) + calc_convergence(env_new::CTMRGEnv, env_old::CTMRGEnv) -Given a new environment `envs`, compute the maximal singular value distance. +Given a new environment `env`, compute the maximal singular value distance. This determined either from the previous corner and edge singular values `CS_old` and `TS_old`, or alternatively, directly from the old environment. """ -function calc_convergence(envs, CS_old, TS_old) - CS_new = map(x -> tsvd(x)[2], envs.corners) +function calc_convergence(env, CS_old, TS_old) + CS_new = map(x -> tsvd(x)[2], env.corners) ΔCS = maximum(_singular_value_distance, zip(CS_old, CS_new)) - TS_new = map(x -> tsvd(x)[2], envs.edges) + TS_new = map(x -> tsvd(x)[2], env.edges) ΔTS = maximum(_singular_value_distance, zip(TS_old, TS_new)) @debug "maxᵢ|Cⁿ⁺¹ - Cⁿ|ᵢ = $ΔCS maxᵢ|Tⁿ⁺¹ - Tⁿ|ᵢ = $ΔTS" return max(ΔCS, ΔTS), CS_new, TS_new end -function calc_convergence(envs_new::CTMRGEnv, envs_old::CTMRGEnv) - CS_old = map(x -> tsvd(x)[2], envs_old.corners) - TS_old = map(x -> tsvd(x)[2], envs_old.edges) - return calc_convergence(envs_new, CS_old, TS_old) +function calc_convergence(env_new::CTMRGEnv, env_old::CTMRGEnv) + CS_old = map(x -> tsvd(x)[2], env_old.corners) + TS_old = map(x -> tsvd(x)[2], env_old.edges) + return calc_convergence(env_new, CS_old, TS_old) end @non_differentiable calc_convergence(args...) diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 9841480d..621163fa 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -30,44 +30,42 @@ function SequentialCTMRG(; ) end -function ctmrg_iteration(state, envs::CTMRGEnv, alg::SequentialCTMRG) +function ctmrg_iteration(state, env::CTMRGEnv, alg::SequentialCTMRG) truncation_error = zero(real(scalartype(state))) condition_number = zero(real(scalartype(state))) for _ in 1:4 # rotate for col in 1:size(state, 2) # left move column-wise - projectors, info = sequential_projectors(col, state, envs, alg.projector_alg) - envs = renormalize_sequentially(col, projectors, state, envs) + projectors, info = sequential_projectors(col, state, env, alg.projector_alg) + env = renormalize_sequentially(col, projectors, state, env) truncation_error = max(truncation_error, info.truncation_error) condition_number = max(condition_number, info.condition_number) end state = rotate_north(state, EAST) - envs = rotate_north(envs, EAST) + env = rotate_north(env, EAST) end - return envs, (; truncation_error, condition_number) + return env, (; truncation_error, condition_number) end """ - sequential_projectors(col::Int, state::InfinitePEPS, envs::CTMRGEnv, alg::ProjectorAlgorithm) - sequential_projectors(coordinate::NTuple{3,Int}, state::InfinitePEPS, envs::CTMRGEnv, alg::ProjectorAlgorithm) + sequential_projectors(col::Int, state::InfinitePEPS, env::CTMRGEnv, alg::ProjectorAlgorithm) + sequential_projectors(coordinate::NTuple{3,Int}, state::InfinitePEPS, env::CTMRGEnv, alg::ProjectorAlgorithm) Compute CTMRG projectors in the `:sequential` scheme either for an entire column `col` or for a specific `coordinate` (where `dir=WEST` is already implied in the `:sequential` scheme). """ function sequential_projectors( - col::Int, state::InfiniteSquareNetwork, envs::CTMRGEnv, alg::ProjectorAlgorithm + col::Int, state::InfiniteSquareNetwork, env::CTMRGEnv, alg::ProjectorAlgorithm ) # SVD half-infinite environment column-wise - T = promote_type(real(scalartype(state)), real(scalartype(envs))) - ϵ = Zygote.Buffer(zeros(T, size(envs, 2))) - S = Zygote.Buffer( - zeros(size(envs, 2)), tensormaptype(spacetype(eltype(state)), 1, 1, T) - ) - coordinates = eachcoordinate(envs)[:, col] + T = promote_type(real(scalartype(state)), real(scalartype(env))) + ϵ = Zygote.Buffer(zeros(T, size(env, 2))) + S = Zygote.Buffer(zeros(size(env, 2)), tensormaptype(spacetype(eltype(state)), 1, 1, T)) + coordinates = eachcoordinate(env)[:, col] projectors = dtmap(coordinates) do (r, c) - trscheme = truncation_scheme(alg, envs.edges[WEST, _prev(r, size(envs, 2)), c]) + trscheme = truncation_scheme(alg, env.edges[WEST, _prev(r, size(env, 2)), c]) proj, info = sequential_projectors( - (WEST, r, c), state, envs, @set(alg.trscheme = trscheme) + (WEST, r, c), state, env, @set(alg.trscheme = trscheme) ) S[r] = info.S ϵ[r] = info.err / norm(info.S) @@ -82,61 +80,61 @@ end function sequential_projectors( coordinate::NTuple{3,Int}, state::InfiniteSquareNetwork, - envs::CTMRGEnv, + env::CTMRGEnv, alg::HalfInfiniteProjector, ) _, r, c = coordinate - r′ = _prev(r, size(envs, 2)) - Q1 = TensorMap(EnlargedCorner(state, envs, (SOUTHWEST, r, c)), SOUTHWEST) - Q2 = TensorMap(EnlargedCorner(state, envs, (NORTHWEST, r′, c)), NORTHWEST) + r′ = _prev(r, size(env, 2)) + Q1 = TensorMap(EnlargedCorner(state, env, (SOUTHWEST, r, c)), SOUTHWEST) + Q2 = TensorMap(EnlargedCorner(state, env, (NORTHWEST, r′, c)), NORTHWEST) return compute_projector((Q1, Q2), coordinate, alg) end function sequential_projectors( coordinate::NTuple{3,Int}, state::InfiniteSquareNetwork, - envs::CTMRGEnv, + env::CTMRGEnv, alg::FullInfiniteProjector, ) - rowsize, colsize = size(envs)[2:3] + rowsize, colsize = size(env)[2:3] coordinate_nw = _next_coordinate(coordinate, rowsize, colsize) coordinate_ne = _next_coordinate(coordinate_nw, rowsize, colsize) coordinate_se = _next_coordinate(coordinate_ne, rowsize, colsize) ec = ( - TensorMap(EnlargedCorner(state, envs, coordinate_se), SOUTHEAST), - TensorMap(EnlargedCorner(state, envs, coordinate), SOUTHWEST), - TensorMap(EnlargedCorner(state, envs, coordinate_nw), NORTHWEST), - TensorMap(EnlargedCorner(state, envs, coordinate_ne), NORTHEAST), + TensorMap(EnlargedCorner(state, env, coordinate_se), SOUTHEAST), + TensorMap(EnlargedCorner(state, env, coordinate), SOUTHWEST), + TensorMap(EnlargedCorner(state, env, coordinate_nw), NORTHWEST), + TensorMap(EnlargedCorner(state, env, coordinate_ne), NORTHEAST), ) return compute_projector(ec, coordinate, alg) end """ - renormalize_sequentially(col::Int, projectors, state, envs) + renormalize_sequentially(col::Int, projectors, state, env) Renormalize one column of the CTMRG environment. """ -function renormalize_sequentially(col::Int, projectors, state, envs) - corners = Zygote.Buffer(envs.corners) - edges = Zygote.Buffer(envs.edges) +function renormalize_sequentially(col::Int, projectors, state, env) + corners = Zygote.Buffer(env.corners) + edges = Zygote.Buffer(env.edges) for (dir, r, c) in eachcoordinate(state, 1:4) (c == col && dir in [SOUTHWEST, NORTHWEST]) && continue - corners[dir, r, c] = envs.corners[dir, r, c] + corners[dir, r, c] = env.corners[dir, r, c] end for (dir, r, c) in eachcoordinate(state, 1:4) (c == col && dir == WEST) && continue - edges[dir, r, c] = envs.edges[dir, r, c] + edges[dir, r, c] = env.edges[dir, r, c] end # Apply projectors to renormalize corners and edge - for row in axes(envs.corners, 2) - C_southwest = renormalize_bottom_corner((row, col), envs, projectors) + for row in axes(env.corners, 2) + C_southwest = renormalize_bottom_corner((row, col), env, projectors) corners[SOUTHWEST, row, col] = C_southwest / norm(C_southwest) - C_northwest = renormalize_top_corner((row, col), envs, projectors) + C_northwest = renormalize_top_corner((row, col), env, projectors) corners[NORTHWEST, row, col] = C_northwest / norm(C_northwest) - E_west = renormalize_west_edge((row, col), envs, projectors, state) + E_west = renormalize_west_edge((row, col), env, projectors, state) edges[WEST, row, col] = E_west / norm(E_west) end diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index 00cd5cd6..62472783 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -30,13 +30,13 @@ function SimultaneousCTMRG(; ) end -function ctmrg_iteration(state, envs::CTMRGEnv, alg::SimultaneousCTMRG) +function ctmrg_iteration(state, env::CTMRGEnv, alg::SimultaneousCTMRG) enlarged_corners = dtmap(eachcoordinate(state, 1:4)) do idx - return TensorMap(EnlargedCorner(state, envs, idx), idx[1]) + return TensorMap(EnlargedCorner(state, env, idx), idx[1]) end # expand environment - projectors, info = simultaneous_projectors(enlarged_corners, envs, alg.projector_alg) # compute projectors on all coordinates - envs′ = renormalize_simultaneously(enlarged_corners, projectors, state, envs) # renormalize enlarged corners - return envs′, info + projectors, info = simultaneous_projectors(enlarged_corners, env, alg.projector_alg) # compute projectors on all coordinates + env′ = renormalize_simultaneously(enlarged_corners, projectors, state, env) # renormalize enlarged corners + return env′, info end # Pre-allocate U, S, and V tensor as Zygote buffers to make it differentiable @@ -59,21 +59,21 @@ end @non_differentiable _condition_number(S::AbstractTensorMap) """ - simultaneous_projectors(enlarged_corners::Array{E,3}, envs::CTMRGEnv, alg::ProjectorAlgorithm) + simultaneous_projectors(enlarged_corners::Array{E,3}, env::CTMRGEnv, alg::ProjectorAlgorithm) simultaneous_projectors(coordinate, enlarged_corners::Array{E,3}, alg::ProjectorAlgorithm) Compute CTMRG projectors in the `:simultaneous` scheme either for all provided enlarged corners or on a specific `coordinate`. """ function simultaneous_projectors( - enlarged_corners::Array{E,3}, envs::CTMRGEnv, alg::ProjectorAlgorithm + enlarged_corners::Array{E,3}, env::CTMRGEnv, alg::ProjectorAlgorithm ) where {E} - U, S, V = _prealloc_svd(envs.edges) - ϵ = Zygote.Buffer(zeros(real(scalartype(envs)), size(envs))) + U, S, V = _prealloc_svd(env.edges) + ϵ = Zygote.Buffer(zeros(real(scalartype(env)), size(env))) - projectors = dtmap(eachcoordinate(envs, 1:4)) do coordinate - coordinate′ = _next_coordinate(coordinate, size(envs)[2:3]...) - trscheme = truncation_scheme(alg, envs.edges[coordinate[1], coordinate′[2:3]...]) + projectors = dtmap(eachcoordinate(env, 1:4)) do coordinate + coordinate′ = _next_coordinate(coordinate, size(env)[2:3]...) + trscheme = truncation_scheme(alg, env.edges[coordinate[1], coordinate′[2:3]...]) proj, info = simultaneous_projectors( coordinate, enlarged_corners, @set(alg.trscheme = trscheme) ) @@ -113,26 +113,26 @@ function simultaneous_projectors( end """ - renormalize_simultaneously(enlarged_corners, projectors, state, envs) + renormalize_simultaneously(enlarged_corners, projectors, state, env) Renormalize all enlarged corners and edges simultaneously. """ -function renormalize_simultaneously(enlarged_corners, projectors, state, envs) +function renormalize_simultaneously(enlarged_corners, projectors, state, env) P_left, P_right = projectors - coordinates = eachcoordinate(envs, 1:4) + coordinates = eachcoordinate(env, 1:4) corners_edges = dtmap(coordinates) do (dir, r, c) if dir == NORTH corner = renormalize_northwest_corner((r, c), enlarged_corners, P_left, P_right) - edge = renormalize_north_edge((r, c), envs, P_left, P_right, state) + edge = renormalize_north_edge((r, c), env, P_left, P_right, state) elseif dir == EAST corner = renormalize_northeast_corner((r, c), enlarged_corners, P_left, P_right) - edge = renormalize_east_edge((r, c), envs, P_left, P_right, state) + edge = renormalize_east_edge((r, c), env, P_left, P_right, state) elseif dir == SOUTH corner = renormalize_southeast_corner((r, c), enlarged_corners, P_left, P_right) - edge = renormalize_south_edge((r, c), envs, P_left, P_right, state) + edge = renormalize_south_edge((r, c), env, P_left, P_right, state) elseif dir == WEST corner = renormalize_southwest_corner((r, c), enlarged_corners, P_left, P_right) - edge = renormalize_west_edge((r, c), envs, P_left, P_right, state) + edge = renormalize_west_edge((r, c), env, P_left, P_right, state) end return corner / norm(corner), edge / norm(edge) end diff --git a/src/algorithms/ctmrg/sparse_environments.jl b/src/algorithms/ctmrg/sparse_environments.jl index cbd1a660..616e89f1 100644 --- a/src/algorithms/ctmrg/sparse_environments.jl +++ b/src/algorithms/ctmrg/sparse_environments.jl @@ -29,39 +29,39 @@ function EnlargedCorner( end """ - EnlargedCorner(network::InfiniteSquareNetwork, envs, coordinates) + EnlargedCorner(network::InfiniteSquareNetwork, env, coordinates) Construct an enlarged corner with the correct row and column indices based on the given `coordinates` which are of the form `(dir, row, col)`. """ -function EnlargedCorner(network::InfiniteSquareNetwork, envs, coordinates) +function EnlargedCorner(network::InfiniteSquareNetwork, env, coordinates) dir, r, c = coordinates if dir == NORTHWEST return EnlargedCorner( - envs.corners[NORTHWEST, _prev(r, end), _prev(c, end)], - envs.edges[WEST, r, _prev(c, end)], - envs.edges[NORTH, _prev(r, end), c], + env.corners[NORTHWEST, _prev(r, end), _prev(c, end)], + env.edges[WEST, r, _prev(c, end)], + env.edges[NORTH, _prev(r, end), c], network[r, c], ) elseif dir == NORTHEAST return EnlargedCorner( - envs.corners[NORTHEAST, _prev(r, end), _next(c, end)], - envs.edges[NORTH, _prev(r, end), c], - envs.edges[EAST, r, _next(c, end)], + env.corners[NORTHEAST, _prev(r, end), _next(c, end)], + env.edges[NORTH, _prev(r, end), c], + env.edges[EAST, r, _next(c, end)], network[r, c], ) elseif dir == SOUTHEAST return EnlargedCorner( - envs.corners[SOUTHEAST, _next(r, end), _next(c, end)], - envs.edges[EAST, r, _next(c, end)], - envs.edges[SOUTH, _next(r, end), c], + env.corners[SOUTHEAST, _next(r, end), _next(c, end)], + env.edges[EAST, r, _next(c, end)], + env.edges[SOUTH, _next(r, end), c], network[r, c], ) elseif dir == SOUTHWEST return EnlargedCorner( - envs.corners[SOUTHWEST, _next(r, end), _prev(c, end)], - envs.edges[SOUTH, _next(r, end), c], - envs.edges[WEST, r, _prev(c, end)], + env.corners[SOUTHWEST, _next(r, end), _prev(c, end)], + env.edges[SOUTH, _next(r, end), c], + env.edges[WEST, r, _prev(c, end)], network[r, c], ) end diff --git a/src/algorithms/optimization/fixed_point_differentiation.jl b/src/algorithms/optimization/fixed_point_differentiation.jl index 03b2ec25..4194143a 100644 --- a/src/algorithms/optimization/fixed_point_differentiation.jl +++ b/src/algorithms/optimization/fixed_point_differentiation.jl @@ -91,24 +91,24 @@ function _rrule( state, alg::CTMRGAlgorithm, ) - envs, info = leading_boundary(envinit, state, alg) + env, info = leading_boundary(envinit, state, alg) - function leading_boundary_diffgauge_pullback((Δenvs′, Δinfo)) - Δenvs = unthunk(Δenvs′) + function leading_boundary_diffgauge_pullback((Δenv′, Δinfo)) + Δenv = unthunk(Δenv′) # find partial gradients of gauge_fixed single CTMRG iteration f(A, x) = gauge_fix(x, ctmrg_iteration(A, x, alg)[1])[1] - _, env_vjp = rrule_via_ad(config, f, state, envs) + _, env_vjp = rrule_via_ad(config, f, state, env) # evaluate the geometric sum ∂f∂A(x)::typeof(state) = env_vjp(x)[2] - ∂f∂x(x)::typeof(envs) = env_vjp(x)[3] - ∂F∂envs = fpgrad(Δenvs, ∂f∂x, ∂f∂A, Δenvs, gradmode) + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] + ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) - return NoTangent(), ZeroTangent(), ∂F∂envs, NoTangent() + return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() end - return (envs, info), leading_boundary_diffgauge_pullback + return (env, info), leading_boundary_diffgauge_pullback end # Here f is differentiated from an pre-computed SVD with fixed U, S and V @@ -121,9 +121,9 @@ function _rrule( alg::SimultaneousCTMRG, ) @assert !isnothing(alg.projector_alg.svd_alg.rrule_alg) - envs, = leading_boundary(envinit, state, alg) - envs_conv, info = ctmrg_iteration(state, envs, alg) - envs_fixed, signs = gauge_fix(envs, envs_conv) + env, = leading_boundary(envinit, state, alg) + env_conv, info = ctmrg_iteration(state, env, alg) + env_fixed, signs = gauge_fix(env, env_conv) # Fix SVD U_fixed, V_fixed = fix_relative_phases(info.U, info.V, signs) @@ -134,21 +134,21 @@ function _rrule( alg_fixed = @set alg.projector_alg.svd_alg = svd_alg_fixed alg_fixed = @set alg_fixed.projector_alg.trscheme = notrunc() - function leading_boundary_fixed_pullback((Δenvs′, Δinfo)) - Δenvs = unthunk(Δenvs′) + function leading_boundary_fixed_pullback((Δenv′, Δinfo)) + Δenv = unthunk(Δenv′) f(A, x) = fix_global_phases(x, ctmrg_iteration(A, x, alg_fixed)[1]) - _, env_vjp = rrule_via_ad(config, f, state, envs_fixed) + _, 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(envs) = env_vjp(x)[3] - ∂F∂envs = fpgrad(Δenvs, ∂f∂x, ∂f∂A, Δenvs, gradmode) + ∂f∂x(x)::typeof(env) = env_vjp(x)[3] + ∂F∂env = fpgrad(Δenv, ∂f∂x, ∂f∂A, Δenv, gradmode) - return NoTangent(), ZeroTangent(), ∂F∂envs, NoTangent() + return NoTangent(), ZeroTangent(), ∂F∂env, NoTangent() end - return (envs_fixed, info), leading_boundary_fixed_pullback + return (env_fixed, info), leading_boundary_fixed_pullback end @doc """ diff --git a/src/algorithms/optimization/peps_optimization.jl b/src/algorithms/optimization/peps_optimization.jl index 9295a26b..aecb2132 100644 --- a/src/algorithms/optimization/peps_optimization.jl +++ b/src/algorithms/optimization/peps_optimization.jl @@ -53,7 +53,7 @@ an initial guess for the first CTMRG run. By default, a random initial environme The `finalize!` kwarg can be used to insert a function call after each optimization step by utilizing the `finalize!` kwarg of `OptimKit.optimize`. -The function maps `(peps, envs), f, g = finalize!((peps, envs), f, g, numiter)`. +The function maps `(peps, env), f, g = finalize!((peps, env), f, g, numiter)`. The `symmetrization` kwarg accepts `nothing` or a `SymmetrizationStyle`, in which case the PEPS and PEPS gradient are symmetrized after each optimization iteration. Note that this requires a symmmetric `peps₀` and `env₀` to converge properly. @@ -69,9 +69,7 @@ information `NamedTuple` which contains the following entries: - `gradnorms_unitcell`: history of gradient norms for each respective unit cell entry - `times`: history of times each optimization step took """ -function fixedpoint( - operator, peps₀::InfinitePEPS, env₀::CTMRGEnv; kwargs... -) +function fixedpoint(operator, peps₀::InfinitePEPS, env₀::CTMRGEnv; kwargs...) throw(error("method not yet implemented")) alg = fixedpoint_selector(; kwargs...) # TODO: implement fixedpoint_selector return fixedpoint(operator, peps₀, env₀, alg) @@ -164,16 +162,16 @@ real_inner(_, η₁, η₂) = real(dot(η₁, η₂)) Return the `retract` and `finalize!` function for symmetrizing the `peps` and `grad` tensors. """ function symmetrize_retract_and_finalize!(symm::SymmetrizationStyle) - finf = function symmetrize_finalize!((peps, envs), E, grad, _) + finf = function symmetrize_finalize!((peps, env), E, grad, _) grad_symm = symmetrize!(grad, symm) - return (peps, envs), E, grad_symm + return (peps, env), E, grad_symm end - retf = function symmetrize_retract((peps, envs), η, α) + retf = function symmetrize_retract((peps, env), η, α) peps_symm = deepcopy(peps) peps_symm.A .+= η.A .* α - envs′ = deepcopy(envs) + env′ = deepcopy(env) symmetrize!(peps_symm, symm) - return (peps_symm, envs′), η + return (peps_symm, env′), η end return retf, finf end diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index cc1d6edf..7fdee623 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -1,23 +1,23 @@ """ - expectation_value(peps::InfinitePEPS, O::LocalOperator, envs::CTMRGEnv) + expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv) Compute the expectation value ⟨peps|O|peps⟩ / ⟨peps|peps⟩ of a [`LocalOperator`](@ref) `O` -for a PEPS `peps` using a given CTMRG environment `envs`. +for a PEPS `peps` using a given CTMRG environment `env`. """ -function MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, envs::CTMRGEnv) +function MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv) checklattice(peps, O) term_vals = dtmap([O.terms...]) do (inds, operator) # OhMyThreads can't iterate over O.terms directly - contract_local_operator(inds, operator, peps, peps, envs) / - contract_local_norm(inds, peps, peps, envs) + contract_local_operator(inds, operator, peps, peps, env) / + contract_local_norm(inds, peps, peps, env) end return sum(term_vals) end """ - expectation_value(pf::InfinitePartitionFunction, inds => O, envs::CTMRGEnv) + expectation_value(pf::InfinitePartitionFunction, inds => O, env::CTMRGEnv) Compute the expectation value corresponding to inserting a local tensor(s) `O` at position `inds` in the partition function `pf` and contracting the chole using a given CTMRG -environment `envs`. +environment `env`. Here `inds` can be specified as either a `Tuple{Int,Int}` or a `CartesianIndex{2}`, and `O` should be a rank-4 tensor conforming to the [`PartitionFunctionTensor`](@ref) indexing @@ -26,25 +26,25 @@ convention. function MPSKit.expectation_value( pf::InfinitePartitionFunction, op::Pair{CartesianIndex{2},<:AbstractTensorMap{T,S,2,2}}, - envs::CTMRGEnv, + env::CTMRGEnv, ) where {T,S} - return contract_local_tensor(op[1], op[2], envs) / - contract_local_tensor(op[1], pf[op[1]], envs) + return contract_local_tensor(op[1], op[2], env) / + contract_local_tensor(op[1], pf[op[1]], env) end function MPSKit.expectation_value( - pf::InfinitePartitionFunction, op::Pair{Tuple{Int,Int}}, envs::CTMRGEnv + pf::InfinitePartitionFunction, op::Pair{Tuple{Int,Int}}, env::CTMRGEnv ) - return expectation_value(pf, CartesianIndex(op[1]) => op[2], envs) + return expectation_value(pf, CartesianIndex(op[1]) => op[2], env) end """ - cost_function(peps::InfinitePEPS, envs::CTMRGEnv, O::LocalOperator) + cost_function(peps::InfinitePEPS, env::CTMRGEnv, O::LocalOperator) Real part of expectation value of `O`. Prints a warning if the expectation value yields a finite imaginary part (up to a tolerance). """ -function cost_function(peps::InfinitePEPS, envs::CTMRGEnv, O::LocalOperator) - E = MPSKit.expectation_value(peps, O, envs) +function cost_function(peps::InfinitePEPS, env::CTMRGEnv, O::LocalOperator) + E = MPSKit.expectation_value(peps, O, env) ignore_derivatives() do isapprox(imag(E), 0; atol=sqrt(eps(real(E)))) || @warn "Expectation value is not real: $E." diff --git a/src/environments/vumps_environments.jl b/src/environments/vumps_environments.jl index 1dd6ded8..b01f96f7 100644 --- a/src/environments/vumps_environments.jl +++ b/src/environments/vumps_environments.jl @@ -4,18 +4,18 @@ using MPSKit: InfiniteEnvironments # ProductSpace instances function MPSKit.issamespace( - envs::InfiniteEnvironments, + env::InfiniteEnvironments, above::InfiniteMPS, operator::InfiniteTransferMatrix, below::InfiniteMPS, ) L = MPSKit.check_length(above, operator, below) for i in 1:L - space(envs.GLs[i]) == ( + space(env.GLs[i]) == ( left_virtualspace(below, i) ⊗ _elementwise_dual(left_virtualspace(operator, i)) ← left_virtualspace(above, i) ) || return false - space(envs.GRs[i]) == ( + space(env.GRs[i]) == ( right_virtualspace(above, i) ⊗ right_virtualspace(operator, i) ← right_virtualspace(below, i) ) || return false diff --git a/src/operators/transfermatrix.jl b/src/operators/transfermatrix.jl index abbf2496..f39a19ae 100644 --- a/src/operators/transfermatrix.jl +++ b/src/operators/transfermatrix.jl @@ -231,10 +231,10 @@ the unit cell. @doc """ MPSKit.leading_boundary( - st::InfiniteMPS, op::Union{InfiniteTransferPEPS,InfiniteTransferPEPO}, alg, [envs] + st::InfiniteMPS, op::Union{InfiniteTransferPEPS,InfiniteTransferPEPO}, alg, [env] ) MPSKit.leading_boundary( - st::MPSMulitline, op::Union{MultilineTransferPEPS,MultilineTransferPEPO}, alg, [envs] + st::MPSMulitline, op::Union{MultilineTransferPEPS,MultilineTransferPEPO}, alg, [env] ) Approximate the leading boundary MPS eigenvector for the transfer operator `op` using `st` diff --git a/test/boundarymps/vumps.jl b/test/boundarymps/vumps.jl index f878a8c3..8b816ad6 100644 --- a/test/boundarymps/vumps.jl +++ b/test/boundarymps/vumps.jl @@ -14,7 +14,7 @@ const vumps_alg = VUMPS(; alg_eigsolve=MPSKit.Defaults.alg_eigsolve(; ishermitia T = PEPSKit.InfiniteTransferPEPS(psi, 1, 1) mps = PEPSKit.initializeMPS(T, [ComplexSpace(20)]) - mps, envs, ϵ = leading_boundary(mps, T, vumps_alg) + mps, env, ϵ = leading_boundary(mps, T, vumps_alg) N = abs(sum(expectation_value(mps, T))) ctm, = leading_boundary( @@ -30,7 +30,7 @@ end T = PEPSKit.MultilineTransferPEPS(psi, 1) mps = PEPSKit.initializeMPS(T, fill(ComplexSpace(20), 2, 2)) - mps, envs, ϵ = leading_boundary(mps, T, vumps_alg) + mps, env, ϵ = leading_boundary(mps, T, vumps_alg) N = abs(prod(expectation_value(mps, T))) ctm, = leading_boundary( @@ -68,6 +68,6 @@ end T = InfiniteTransferPEPO(psi, O, 1, 1) mps = PEPSKit.initializeMPS(T, [ComplexSpace(10)]) - mps, envs, ϵ = leading_boundary(mps, T, vumps_alg) + mps, env, ϵ = leading_boundary(mps, T, vumps_alg) f = abs(prod(expectation_value(mps, T))) end diff --git a/test/ctmrg/gradients.jl b/test/ctmrg/gradients.jl index 4bf4aebe..6c55b5d6 100644 --- a/test/ctmrg/gradients.jl +++ b/test/ctmrg/gradients.jl @@ -67,12 +67,10 @@ steps = -0.01:0.005:0.01 alpha=steps, retract=PEPSKit.peps_retract, inner=PEPSKit.real_inner, - ) do (peps, envs) + ) do (peps, env) E, g = Zygote.withgradient(peps) do psi - envs2, = PEPSKit.hook_pullback( - leading_boundary, envs, psi, ctmrg_alg; alg_rrule - ) - return cost_function(psi, envs2, models[i]) + env2, = PEPSKit.hook_pullback(leading_boundary, env, psi, ctmrg_alg; alg_rrule) + return cost_function(psi, env2, models[i]) end return E, only(g) diff --git a/test/ctmrg/jacobian_real_linear.jl b/test/ctmrg/jacobian_real_linear.jl index c00fa90a..24a655bf 100644 --- a/test/ctmrg/jacobian_real_linear.jl +++ b/test/ctmrg/jacobian_real_linear.jl @@ -19,12 +19,12 @@ Dbond, χenv = 2, 16 @testset "$iterscheme and $ctm_alg" for (iterscheme, ctm_alg) in algs Random.seed!(123521938519) state = InfinitePEPS(2, Dbond) - envs, = leading_boundary(CTMRGEnv(state, ComplexSpace(χenv)), state, ctm_alg) + env, = leading_boundary(CTMRGEnv(state, ComplexSpace(χenv)), state, ctm_alg) # follow code of _rrule if iterscheme == :fixed - envs_conv, info = ctmrg_iteration(state, envs, ctm_alg) - envs_fixed, signs = gauge_fix(envs, envs_conv) + env_conv, info = ctmrg_iteration(state, env, ctm_alg) + env_fixed, signs = gauge_fix(env, env_conv) U_fixed, V_fixed = fix_relative_phases(info.U, info.V, signs) svd_alg_fixed = SVDAdjoint(; fwd_alg=PEPSKit.FixedSVD(U_fixed, info.S, V_fixed), @@ -33,19 +33,19 @@ Dbond, χenv = 2, 16 alg_fixed = @set ctm_alg.projector_alg.svd_alg = svd_alg_fixed alg_fixed = @set alg_fixed.projector_alg.trscheme = notrunc() - _, env_vjp = pullback(state, envs_fixed) do A, x + _, env_vjp = pullback(state, env_fixed) do A, x e, = PEPSKit.ctmrg_iteration(A, x, alg_fixed) return PEPSKit.fix_global_phases(x, e) end elseif iterscheme == :diffgauge - _, env_vjp = pullback(state, envs) do A, x + _, env_vjp = pullback(state, env) do A, x return gauge_fix(x, ctmrg_iteration(A, x, ctm_alg)[1])[1] end end # get Jacobians of single iteration ∂f∂A(x)::typeof(state) = env_vjp(x)[1] - ∂f∂x(x)::typeof(envs) = env_vjp(x)[2] + ∂f∂x(x)::typeof(env) = env_vjp(x)[2] # compute real and complex errors env_in = CTMRGEnv(state, ComplexSpace(16)) diff --git a/test/heisenberg.jl b/test/heisenberg.jl index 87048f85..24ea65ae 100644 --- a/test/heisenberg.jl +++ b/test/heisenberg.jl @@ -80,11 +80,11 @@ end # absorb weight into site tensors and CTMRG peps = InfinitePEPS(wpeps) - envs₀ = CTMRGEnv(rand, Float64, peps, Espace) - envs, = leading_boundary(envs₀, peps, SimultaneousCTMRG()) + env₀ = CTMRGEnv(rand, Float64, peps, Espace) + env, = leading_boundary(env₀, peps, SimultaneousCTMRG()) # measure physical quantities - e_site = cost_function(peps, envs, ham) / (N1 * N2) + e_site = cost_function(peps, env, ham) / (N1 * N2) @info "Simple update energy = $e_site" # benchmark data from Phys. Rev. B 94, 035133 (2016) @test isapprox(e_site, -0.6594; atol=1e-3) @@ -92,7 +92,7 @@ end # continue with auto differentiation svd_alg_gmres = SVDAdjoint(; rrule_alg=GMRES(; tol=1e-5)) opt_alg_gmres = @set opt_alg.boundary_alg.projector_alg.svd_alg = svd_alg_gmres - peps_final, env_final, E_final, = fixedpoint(ham, peps, envs, opt_alg_gmres) # sensitivity warnings and degeneracies due to SU(2)? + peps_final, env_final, E_final, = fixedpoint(ham, peps, env, opt_alg_gmres) # sensitivity warnings and degeneracies due to SU(2)? ξ_h, ξ_v, = correlation_length(peps_final, env_final) e_site2 = E_final / (N1 * N2) @info "Auto diff energy = $e_site2" From 12670772fd31e5751d4fe32a79ea973aecc8d775 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 14:04:35 +0100 Subject: [PATCH 18/26] Backpropagate _condition_number with ZeroTangent --- src/algorithms/ctmrg/simultaneous.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index 62472783..7877d3e6 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -56,7 +56,11 @@ function _condition_number(S::AbstractTensorMap) return maximum(b_diag) / minimum(b_diag) end end -@non_differentiable _condition_number(S::AbstractTensorMap) +function ChainRulesCore.rrule(::typeof(_condition_number), S::AbstractTensorMap) # Backpropagte with ZeroTangent() as work-around + condition_number = _condition_number(S) + _condition_number_pullback(_) = (NoTangent(), ZeroTangent()) + return condition_number, _condition_number_pullback +end """ simultaneous_projectors(enlarged_corners::Array{E,3}, env::CTMRGEnv, alg::ProjectorAlgorithm) From d4c620b69e5cda427449ee5267ddc902364731bd Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 14:05:32 +0100 Subject: [PATCH 19/26] Update src/algorithms/ctmrg/simultaneous.jl Co-authored-by: Lukas Devos --- src/algorithms/ctmrg/simultaneous.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index 7877d3e6..b8c08da2 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -48,6 +48,7 @@ function _prealloc_svd(edges::Array{E,N}) where {E,N} return U, S, V end +# TODO: add `LinearAlgebra.cond` to TensorKit # Compute condition number σ_max / σ_min for diagonal singular value TensorMap function _condition_number(S::AbstractTensorMap) # Take maximal condition number over all blocks From 8c93c6e107adb54d35534a9452571ee1580e6ead Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 14:45:59 +0100 Subject: [PATCH 20/26] Relocate computation of condition to projectors --- src/algorithms/ctmrg/ctmrg.jl | 4 +--- src/algorithms/ctmrg/projectors.jl | 28 ++++++++++++++++++++++------ src/algorithms/ctmrg/sequential.jl | 8 ++++---- src/algorithms/ctmrg/simultaneous.jl | 20 ++++---------------- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index 0392340c..d390f686 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -38,11 +38,9 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRGAlgorithm) env = deepcopy(envinit) log = ignore_derivatives(() -> MPSKit.IterLog("CTMRG")) - truncation_error = zero(real(scalartype(state))) - condition_number = one(real(scalartype(state))) - info = (; truncation_error, condition_number) return LoggingExtras.withlevel(; alg.verbosity) do ctmrg_loginit!(log, η, state, envinit) + info = (;) for iter in 1:(alg.maxiter) env, info = ctmrg_iteration(state, env, alg) # Grow and renormalize in all 4 directions η, CS, TS = calc_convergence(env, CS, TS) diff --git a/src/algorithms/ctmrg/projectors.jl b/src/algorithms/ctmrg/projectors.jl index 39aa58ee..84ce446b 100644 --- a/src/algorithms/ctmrg/projectors.jl +++ b/src/algorithms/ctmrg/projectors.jl @@ -56,6 +56,16 @@ Projector algorithm implementing projectors from SVDing the full 4x4 CTMRG envir verbosity::Int = 0 end +# Compute condition number σ_max / σ_min for diagonal singular value TensorMap +function _condition_number(S::AbstractTensorMap) + # Take maximal condition number over all blocks + return maximum(blocks(S)) do (_, b) + b_diag = diag(b) + return maximum(b_diag) / minimum(b_diag) + end +end +@non_differentiable _condition_number(S::AbstractTensorMap) + """ compute_projector(enlarged_corners, coordinate, alg::ProjectorAlgorithm) @@ -66,16 +76,19 @@ function compute_projector(enlarged_corners, coordinate, alg::HalfInfiniteProjec # SVD half-infinite environment halfinf = half_infinite_environment(enlarged_corners...) svd_alg = svd_algorithm(alg, coordinate) - U, S, V, err = PEPSKit.tsvd!(halfinf, svd_alg; trunc=alg.trscheme) - # Compute SVD truncation error and check for degenerate singular values + U, S, V, truncation_error = PEPSKit.tsvd!(halfinf, svd_alg; trunc=alg.trscheme) + + # Check for degenerate singular values Zygote.isderiving() && ignore_derivatives() do if alg.verbosity > 0 && is_degenerate_spectrum(S) svals = TensorKit.SectorDict(c => diag(b) for (c, b) in blocks(S)) @warn("degenerate singular values detected: ", svals) end end + P_left, P_right = contract_projectors(U, S, V, enlarged_corners...) - return (P_left, P_right), (; err, U, S, V) + condition_number = _condition_number(S) + return (P_left, P_right), (; truncation_error, condition_number, U, S, V) end function compute_projector(enlarged_corners, coordinate, alg::FullInfiniteProjector) halfinf_left = half_infinite_environment(enlarged_corners[1], enlarged_corners[2]) @@ -84,14 +97,17 @@ function compute_projector(enlarged_corners, coordinate, alg::FullInfiniteProjec # SVD full-infinite environment fullinf = full_infinite_environment(halfinf_left, halfinf_right) svd_alg = svd_algorithm(alg, coordinate) - U, S, V, err = PEPSKit.tsvd!(fullinf, svd_alg; trunc=alg.trscheme) - # Compute SVD truncation error and check for degenerate singular values + U, S, V, truncation_error = PEPSKit.tsvd!(fullinf, svd_alg; trunc=alg.trscheme) + + # Check for degenerate singular values Zygote.isderiving() && ignore_derivatives() do if alg.verbosity > 0 && is_degenerate_spectrum(S) svals = TensorKit.SectorDict(c => diag(b) for (c, b) in blocks(S)) @warn("degenerate singular values detected: ", svals) end end + P_left, P_right = contract_projectors(U, S, V, halfinf_left, halfinf_right) - return (P_left, P_right), (; err, U, S, V) + condition_number = _condition_number(S) + return (P_left, P_right), (; truncation_error, condition_number, U, S, V) end diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 621163fa..3342e88e 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -60,6 +60,7 @@ function sequential_projectors( # SVD half-infinite environment column-wise T = promote_type(real(scalartype(state)), real(scalartype(env))) ϵ = Zygote.Buffer(zeros(T, size(env, 2))) + κ = Zygote.Buffer(zeros(T, size(env, 2))) S = Zygote.Buffer(zeros(size(env, 2)), tensormaptype(spacetype(eltype(state)), 1, 1, T)) coordinates = eachcoordinate(env)[:, col] projectors = dtmap(coordinates) do (r, c) @@ -68,13 +69,12 @@ function sequential_projectors( (WEST, r, c), state, env, @set(alg.trscheme = trscheme) ) S[r] = info.S - ϵ[r] = info.err / norm(info.S) + ϵ[r] = info.truncation_error / norm(info.S) + κ[r] = info.condition_number return proj end - truncation_error = maximum(copy(ϵ)) - condition_number = maximum(_condition_number, S) - info = (; truncation_error, condition_number) + info = (; truncation_error=maximum(copy(ϵ)), condition_number=maximum(copy(κ))) return (map(first, projectors), map(last, projectors)), info end function sequential_projectors( diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index 7877d3e6..175e52b1 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -48,20 +48,6 @@ function _prealloc_svd(edges::Array{E,N}) where {E,N} return U, S, V end -# Compute condition number σ_max / σ_min for diagonal singular value TensorMap -function _condition_number(S::AbstractTensorMap) - # Take maximal condition number over all blocks - return maximum(blocks(S)) do (_, b) - b_diag = diag(b) - return maximum(b_diag) / minimum(b_diag) - end -end -function ChainRulesCore.rrule(::typeof(_condition_number), S::AbstractTensorMap) # Backpropagte with ZeroTangent() as work-around - condition_number = _condition_number(S) - _condition_number_pullback(_) = (NoTangent(), ZeroTangent()) - return condition_number, _condition_number_pullback -end - """ simultaneous_projectors(enlarged_corners::Array{E,3}, env::CTMRGEnv, alg::ProjectorAlgorithm) simultaneous_projectors(coordinate, enlarged_corners::Array{E,3}, alg::ProjectorAlgorithm) @@ -74,6 +60,7 @@ function simultaneous_projectors( ) where {E} U, S, V = _prealloc_svd(env.edges) ϵ = Zygote.Buffer(zeros(real(scalartype(env)), size(env))) + κ = Zygote.Buffer(zeros(real(scalartype(env)), size(env))) projectors = dtmap(eachcoordinate(env, 1:4)) do coordinate coordinate′ = _next_coordinate(coordinate, size(env)[2:3]...) @@ -84,12 +71,13 @@ function simultaneous_projectors( U[coordinate...] = info.U S[coordinate...] = info.S V[coordinate...] = info.V - ϵ[coordinate...] = info.err / norm(info.S) + ϵ[coordinate...] = info.truncation_error / norm(info.S) + κ[coordinate...] = info.condition_number return proj end truncation_error = maximum(copy(ϵ)) - condition_number = maximum(_condition_number, S) + condition_number = maximum(copy(κ)) info = (; truncation_error, condition_number, U=copy(U), S=copy(S), V=copy(V)) return (map(first, projectors), map(last, projectors)), info end From 58b4570b896bd56f8a3d3e1d821798861686a39c Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 15:10:25 +0100 Subject: [PATCH 21/26] Use `@ignore_derivatives` for _condition_number --- src/algorithms/ctmrg/projectors.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/ctmrg/projectors.jl b/src/algorithms/ctmrg/projectors.jl index 32e531b6..55808eda 100644 --- a/src/algorithms/ctmrg/projectors.jl +++ b/src/algorithms/ctmrg/projectors.jl @@ -88,7 +88,7 @@ function compute_projector(enlarged_corners, coordinate, alg::HalfInfiniteProjec end P_left, P_right = contract_projectors(U, S, V, enlarged_corners...) - condition_number = _condition_number(S) + condition_number = @ignore_derivatives(_condition_number(S)) return (P_left, P_right), (; truncation_error, condition_number, U, S, V) end function compute_projector(enlarged_corners, coordinate, alg::FullInfiniteProjector) @@ -109,6 +109,6 @@ function compute_projector(enlarged_corners, coordinate, alg::FullInfiniteProjec end P_left, P_right = contract_projectors(U, S, V, halfinf_left, halfinf_right) - condition_number = _condition_number(S) + condition_number = @ignore_derivatives(_condition_number(S)) return (P_left, P_right), (; truncation_error, condition_number, U, S, V) end From bf099311974502b2048a419ceef488b1b7e36f6c Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Fri, 7 Feb 2025 15:57:57 +0100 Subject: [PATCH 22/26] Apply local info suggestion --- src/algorithms/ctmrg/ctmrg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/ctmrg/ctmrg.jl b/src/algorithms/ctmrg/ctmrg.jl index d390f686..3dc5552e 100644 --- a/src/algorithms/ctmrg/ctmrg.jl +++ b/src/algorithms/ctmrg/ctmrg.jl @@ -40,7 +40,7 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRGAlgorithm) return LoggingExtras.withlevel(; alg.verbosity) do ctmrg_loginit!(log, η, state, envinit) - info = (;) + local info for iter in 1:(alg.maxiter) env, info = ctmrg_iteration(state, env, alg) # Grow and renormalize in all 4 directions η, CS, TS = calc_convergence(env, CS, TS) From 6afe26393df0e4f89731328284db9aea3af04cd2 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Mon, 10 Feb 2025 11:17:59 +0100 Subject: [PATCH 23/26] Replace Zygote.Buffers in CTMRG --- src/algorithms/ctmrg/projectors.jl | 1 + src/algorithms/ctmrg/sequential.jl | 16 +++--------- src/algorithms/ctmrg/simultaneous.jl | 39 +++++++++++----------------- 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/algorithms/ctmrg/projectors.jl b/src/algorithms/ctmrg/projectors.jl index 55808eda..7c528e8b 100644 --- a/src/algorithms/ctmrg/projectors.jl +++ b/src/algorithms/ctmrg/projectors.jl @@ -88,6 +88,7 @@ function compute_projector(enlarged_corners, coordinate, alg::HalfInfiniteProjec end P_left, P_right = contract_projectors(U, S, V, enlarged_corners...) + truncation_error /= norm(S) condition_number = @ignore_derivatives(_condition_number(S)) return (P_left, P_right), (; truncation_error, condition_number, U, S, V) end diff --git a/src/algorithms/ctmrg/sequential.jl b/src/algorithms/ctmrg/sequential.jl index 3342e88e..4d50a960 100644 --- a/src/algorithms/ctmrg/sequential.jl +++ b/src/algorithms/ctmrg/sequential.jl @@ -57,25 +57,15 @@ for a specific `coordinate` (where `dir=WEST` is already implied in the `:sequen function sequential_projectors( col::Int, state::InfiniteSquareNetwork, env::CTMRGEnv, alg::ProjectorAlgorithm ) - # SVD half-infinite environment column-wise - T = promote_type(real(scalartype(state)), real(scalartype(env))) - ϵ = Zygote.Buffer(zeros(T, size(env, 2))) - κ = Zygote.Buffer(zeros(T, size(env, 2))) - S = Zygote.Buffer(zeros(size(env, 2)), tensormaptype(spacetype(eltype(state)), 1, 1, T)) coordinates = eachcoordinate(env)[:, col] - projectors = dtmap(coordinates) do (r, c) + proj_and_info = dtmap(coordinates) do (r, c) trscheme = truncation_scheme(alg, env.edges[WEST, _prev(r, size(env, 2)), c]) proj, info = sequential_projectors( (WEST, r, c), state, env, @set(alg.trscheme = trscheme) ) - S[r] = info.S - ϵ[r] = info.truncation_error / norm(info.S) - κ[r] = info.condition_number - return proj + return proj, info end - - info = (; truncation_error=maximum(copy(ϵ)), condition_number=maximum(copy(κ))) - return (map(first, projectors), map(last, projectors)), info + return _split_proj_and_info(proj_and_info) end function sequential_projectors( coordinate::NTuple{3,Int}, diff --git a/src/algorithms/ctmrg/simultaneous.jl b/src/algorithms/ctmrg/simultaneous.jl index 175e52b1..79b8b6e1 100644 --- a/src/algorithms/ctmrg/simultaneous.jl +++ b/src/algorithms/ctmrg/simultaneous.jl @@ -39,13 +39,18 @@ function ctmrg_iteration(state, env::CTMRGEnv, alg::SimultaneousCTMRG) return env′, info end -# Pre-allocate U, S, and V tensor as Zygote buffers to make it differentiable -function _prealloc_svd(edges::Array{E,N}) where {E,N} - Sc = scalartype(E) - U = Zygote.Buffer(map(e -> zeros(Sc, space(e)), edges)) - V = Zygote.Buffer(map(e -> zeros(Sc, domain(e), codomain(e)), edges)) - S = Zygote.Buffer(U.data, tensormaptype(spacetype(E), 1, 1, real(Sc))) # Corner type but with real numbers - return U, S, V +# Work-around to stop Zygote from choking on first execution (sometimes) +# Split up map returning projectors and info into separate arrays +function _split_proj_and_info(proj_and_info) + P_left = map(x -> x[1][1], proj_and_info) + P_right = map(x -> x[1][2], proj_and_info) + truncation_error = maximum(x -> x[2].truncation_error, proj_and_info) + condition_number = maximum(x -> x[2].condition_number, proj_and_info) + U = map(x -> x[2].U, proj_and_info) + S = map(x -> x[2].S, proj_and_info) + V = map(x -> x[2].V, proj_and_info) + info = (; truncation_error, condition_number, U, S, V) + return (P_left, P_right), info end """ @@ -58,28 +63,14 @@ enlarged corners or on a specific `coordinate`. function simultaneous_projectors( enlarged_corners::Array{E,3}, env::CTMRGEnv, alg::ProjectorAlgorithm ) where {E} - U, S, V = _prealloc_svd(env.edges) - ϵ = Zygote.Buffer(zeros(real(scalartype(env)), size(env))) - κ = Zygote.Buffer(zeros(real(scalartype(env)), size(env))) - - projectors = dtmap(eachcoordinate(env, 1:4)) do coordinate + proj_and_info = dtmap(eachcoordinate(env, 1:4)) do coordinate coordinate′ = _next_coordinate(coordinate, size(env)[2:3]...) trscheme = truncation_scheme(alg, env.edges[coordinate[1], coordinate′[2:3]...]) - proj, info = simultaneous_projectors( + return simultaneous_projectors( coordinate, enlarged_corners, @set(alg.trscheme = trscheme) ) - U[coordinate...] = info.U - S[coordinate...] = info.S - V[coordinate...] = info.V - ϵ[coordinate...] = info.truncation_error / norm(info.S) - κ[coordinate...] = info.condition_number - return proj end - - truncation_error = maximum(copy(ϵ)) - condition_number = maximum(copy(κ)) - info = (; truncation_error, condition_number, U=copy(U), S=copy(S), V=copy(V)) - return (map(first, projectors), map(last, projectors)), info + return _split_proj_and_info(proj_and_info) end function simultaneous_projectors( coordinate, enlarged_corners::Array{E,3}, alg::HalfInfiniteProjector From ea46eb2cf7f88af3cf6f4e560aef2c00719251e7 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Mon, 10 Feb 2025 16:17:44 +0100 Subject: [PATCH 24/26] Fix condition number computation --- src/algorithms/ctmrg/projectors.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/algorithms/ctmrg/projectors.jl b/src/algorithms/ctmrg/projectors.jl index 7c528e8b..e5e3d1fc 100644 --- a/src/algorithms/ctmrg/projectors.jl +++ b/src/algorithms/ctmrg/projectors.jl @@ -57,13 +57,11 @@ Projector algorithm implementing projectors from SVDing the full 4x4 CTMRG envir end # TODO: add `LinearAlgebra.cond` to TensorKit -# Compute condition number σ_max / σ_min for diagonal singular value TensorMap +# Compute condition number smax / smin for diagonal singular value TensorMap function _condition_number(S::AbstractTensorMap) - # Take maximal condition number over all blocks - return maximum(blocks(S)) do (_, b) - b_diag = diag(b) - return maximum(b_diag) / minimum(b_diag) - end + smax = maximum(first, last.(values(blocks(S)))) + smin = minimum(last, last.(values(blocks(S)))) + return smax / smin end @non_differentiable _condition_number(S::AbstractTensorMap) From 7d4f51fdf7eb95c7affab0f2b55908c98651222c Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Mon, 10 Feb 2025 16:31:27 +0100 Subject: [PATCH 25/26] Fix _condition_number --- src/algorithms/ctmrg/projectors.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/ctmrg/projectors.jl b/src/algorithms/ctmrg/projectors.jl index e5e3d1fc..7448c6bd 100644 --- a/src/algorithms/ctmrg/projectors.jl +++ b/src/algorithms/ctmrg/projectors.jl @@ -59,8 +59,8 @@ end # TODO: add `LinearAlgebra.cond` to TensorKit # Compute condition number smax / smin for diagonal singular value TensorMap function _condition_number(S::AbstractTensorMap) - smax = maximum(first, last.(values(blocks(S)))) - smin = minimum(last, last.(values(blocks(S)))) + smax = maximum(b -> first(b[2]), blocks(S)) + smin = maximum(b -> last(b[2]), blocks(S)) return smax / smin end @non_differentiable _condition_number(S::AbstractTensorMap) From e7077a3023185ce2960d0461017874b97b7cf231 Mon Sep 17 00:00:00 2001 From: Paul Brehmer Date: Mon, 10 Feb 2025 17:09:42 +0100 Subject: [PATCH 26/26] Compose first and last directly --- src/algorithms/ctmrg/projectors.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/ctmrg/projectors.jl b/src/algorithms/ctmrg/projectors.jl index 7448c6bd..61965f68 100644 --- a/src/algorithms/ctmrg/projectors.jl +++ b/src/algorithms/ctmrg/projectors.jl @@ -59,8 +59,8 @@ end # TODO: add `LinearAlgebra.cond` to TensorKit # Compute condition number smax / smin for diagonal singular value TensorMap function _condition_number(S::AbstractTensorMap) - smax = maximum(b -> first(b[2]), blocks(S)) - smin = maximum(b -> last(b[2]), blocks(S)) + smax = maximum(first ∘ last, blocks(S)) + smin = maximum(last ∘ last, blocks(S)) return smax / smin end @non_differentiable _condition_number(S::AbstractTensorMap)