Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/PEPSKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module PEPSKit

using LinearAlgebra, Statistics, Base.Threads, Base.Iterators, Printf
using Compat
using Accessors: @set, @reset
using Accessors: @set, @reset, @insert
using VectorInterface
import VectorInterface as VI

Expand Down Expand Up @@ -64,6 +64,7 @@ include("algorithms/ctmrg/projectors.jl")
include("algorithms/ctmrg/simultaneous.jl")
include("algorithms/ctmrg/sequential.jl")
include("algorithms/ctmrg/gaugefix.jl")
include("algorithms/ctmrg/initialization.jl")

include("algorithms/truncation/truncationschemes.jl")
include("algorithms/truncation/fullenv_truncation.jl")
Expand All @@ -87,6 +88,8 @@ using .Defaults: set_scheduler!
export set_scheduler!
export SVDAdjoint, FullSVDReverseRule, IterSVD
export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG
export initialize_environment,
RandomInitialization, ProductStateInitialization, ApplicationInitialization
export FixedSpaceTruncation, SiteDependentTruncation
export HalfInfiniteProjector, FullInfiniteProjector
export LocalOperator, physicalspace
Expand Down
2 changes: 2 additions & 0 deletions src/algorithms/ctmrg/ctmrg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ containing the following fields:

* `truncation_error` : Last (maximal) SVD truncation error of the CTMRG projectors.
* `condition_number` : Last (maximal) condition number of the enlarged CTMRG environment.
* `convergence_error` : Convergence error of the CTMRG algorithm at termination.

In case the `alg` is a `SimultaneousCTMRG`, the last SVD will also be returned:

Expand Down Expand Up @@ -120,6 +121,7 @@ function leading_boundary(
for iter in 1:(alg.maxiter)
env, info = ctmrg_iteration(network, env, alg) # Grow and renormalize in all 4 directions
η, CS, TS = calc_convergence(env, CS, TS)
info = @insert info.convergence_error = η

if η ≤ alg.tol && iter ≥ alg.miniter
ctmrg_logfinish!(log, iter, η, network, env)
Expand Down
59 changes: 59 additions & 0 deletions src/algorithms/ctmrg/initialization.jl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a look at the argument order, I think something feels a little off with the virtual_spaces as final argument to me.
(Disclaimer that everything that follows is subjective and therefore please do tell me to shut up if you disagree)

In general, most of our "algorithm functions" actually follow a very similar type of interface, based on the purpose of the arguments:

algorithm_function(init, operator, algorithm, [additional_optional_structures])

With this in mind, I think I would try to make something like this work:

initialize_environment([spaces], network, alg)
initialize_environment([(T, spaces)], network, alg)

but I wonder if there is a more elegant solution for the T, possibly just putting it in the keyword arguments. I don't think there are many occasions where T != scalartype(network) is desired, and we can try to make this at least @constinferred?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mind changing T to a keyword argument, the only reason I added it like this was to stay closer to the environment and state constructor signatures.

The choice of position for the virtual_spaces argument was purely pragmatic, as this is basically the only way I could make it compatible with the way the space specification in the CTMRGEnv constructor works. Since the virtual space specification for the different directions works with optional positional arguments, putting virtual_spaces as the last positional argument was the only way to allow for more elaborate virtual space specifications as far as I could tell.

If we want to move it, we would probably need some kind of CTMRGEnvSpaces structure which contains all the information on the virtual spaces in a single object that we can then put wherever we want. This is anyway something we might want to get rid of signatures like CTMRGEnv(ComplexF64, randn, network, virtual_spaces...) in favor of something like randn(ComplexF64, spaces::CTMRGEnvSpaces). That's also part of the reason I introduced these _fill_environment_virtual_spaces methods in #266, since these would come in handy when defining CTMRGEnvSpaces(network, virtual_spaces...). We can decide to do this first, and at the same time change how we internally specify all other space specification.

Starting from the assumption that virtual_spaces needs (at least right now) to be the last positional argument, my reasoning for the argument order was to have something like

initialize_environment([T,] network, alg, virtual_space_specification)

The way you would specify a space depends on the algorithm. For product state or random initialization, the virtual space specification is just an actual space specification as it would be passed to the constructor, hence the whole reason the space specification is positioned last. For an ApplicationInitialization, a full space specification doesn't really make sense, what matters then is how the virtual spaces are truncated during the application iterations. That's why I chose to specify spaces here using a TruncationScheme, and also why the trscheme was the last positional argument in the initialize_environment signature for ApplicationInitialization.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
abstract type InitializationStyle end
struct ProductStateInitialization <: InitializationStyle end
struct RandomInitialization{F} <: InitializationStyle
f::F
RandomInitialization(f::F = randn) where {F} = new{F}(f)
end
struct ApplicationInitialization <: InitializationStyle end

function initialize_environment(
elt::Type{<:Number},
n::InfiniteSquareNetwork,
alg::RandomInitialization,
virtual_spaces... = oneunit(spacetype(n)),
)
return CTMRGEnv(alg.f, elt, n, virtual_spaces...)
end

function initialize_environment(
elt::Type{<:Number},
n::InfiniteSquareNetwork,
::ProductStateInitialization,
virtual_spaces... = oneunit(spacetype(n)),
)
i = one(sectortype(n))
env = CTMRGEnv(ones, elt, n, virtual_spaces...)
for (dir, r, c) in Iterators.product(axes(env)...)
@assert i in blocksectors(env.corners[dir, r, c])
for (c, b) in blocks(env.corners[dir, r, c])
b .= 0
c == i && (b[1, 1] = 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely need a test with fermionic iPEPS on this, in case parity-odd elements become -1 due to twists.

end
end
return env
end

function initialize_environment(
elt::Type{<:Number},
n::InfiniteSquareNetwork,
::ApplicationInitialization,
trscheme::TruncationScheme;
boundary_alg = (;
alg = :sequential, tol = 1.0e-5, maxiter = 10, verbosity = -1,
)
Comment on lines +41 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would imagine this being a part of the ApplicationInitialization struct, and the same for the trscheme?

Copy link
Member Author

@leburgel leburgel Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the trscheme, I kind of think of this as equivalent to the virtual space specification in the other schemes, which is why I kept it separate. See the comment below for some more explanation on that.

For the algorithm being part of the ApplicationInitialization struct, that was also my first thought, but it really wasn't clear to me how to do this in a simple way. Part of the reason is that I would like to reuse this for other kinds of initializations (e.g. PEPS fixed point initialization for Hamiltonians or PEPOs, using different kinds of algorithms such as SimpleUpdate or variational truncation). So it didn't really make sense to have constructors for ApplicationInitialization which put in a default algorithm since the possible algorithm really depend on the objects we're dealing with. But at the same time I really wanted a simple calling signature with a default algorithm that users wouldn't have to manually construct first. So I kind of like the black box behavior now, but I'm also not too happy with the implementation

If we make the algorithm a part of the struct,

struct ApplicationInitialization{A} <: InitializationStyle
    alg::A
end

we could get in the algorithm based on the object here through something like

function ApplicationInitialization(network::InfiniteSquareNetwork; kwargs...)
    return ApplicationIntialization(CTMRGAlgorithm(; kwargs...))
end

Then I just move the defaults there, and it cleans up the initialize_environment signature. Would that be better?

)
boundary_alg = (; boundary_alg..., trscheme) # merge trscheme with optional alg definition
env = initialize_environment(elt, n, ProductStateInitialization())
env, = leading_boundary(env, n; boundary_alg...)
return env
end

function initialize_environment(n::InfiniteSquareNetwork, args...; kwargs...)
return initialize_environment(scalartype(n), n, args...; kwargs...)
end
function initialize_environment(A::Union{InfinitePEPS, InfinitePartitionFunction}, args...; kwargs...)
return initialize_environment(scalartype(A), A, args...; kwargs...)
end
function initialize_environment(elt::Type{<:Number}, A::Union{InfinitePEPS, InfinitePartitionFunction}, args...; kwargs...)
return initialize_environment(elt, InfiniteSquareNetwork(A), args...; kwargs...)
end
1 change: 1 addition & 0 deletions src/networks/infinitesquarenetwork.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ end

## Spaces

TensorKit.spacetype(::Type{T}) where {T <: InfiniteSquareNetwork} = spacetype(eltype(T))
virtualspace(n::InfiniteSquareNetwork, r::Int, c::Int, dir) = virtualspace(n[r, c], dir)

## Vector interface
Expand Down
51 changes: 51 additions & 0 deletions test/ctmrg/initialization.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Test
using TensorKit
using PEPSKit
using Random

using MPSKitModels: classical_ising

sd = 12345

# toggle symmetry, but same issue for both
symmetries = [Z2Irrep, Trivial]

χ = 20
tol = 1.0e-4
maxiter = 1000
verbosity = 2
trscheme = FixedSpaceTruncation()
boundary_alg = (;
alg = :simultaneous,
tol,
verbosity,
trscheme,
maxiter,
)

@testset "CTMRG environment initialization for critical ising with $S symmetry (#255)" for S in symmetries
# initialize
T = classical_ising(S)
O = T[1]
n = InfinitePartitionFunction([O O; O O])
Venv = S == Z2Irrep ? Z2Space(0 => χ / 2, 1 => χ / 2) : ℂ^χ
P = space(O, 2)

# random, doesn't converge
Random.seed!(sd)
env0_rand = initialize_environment(n, RandomInitialization(), Venv)
env_rand, info = leading_boundary(env0_rand, n; boundary_alg...)
@test_broken info.convergence_error ≤ tol

# embedded product state, converges
Random.seed!(sd)
env0_prod = initialize_environment(n, ProductStateInitialization(), Venv)
env_prod, info = leading_boundary(env0_prod, n; boundary_alg...)
@test info.convergence_error ≤ tol

# grown product state, converges
Random.seed!(sd)
env0_appl = initialize_environment(InfiniteSquareNetwork(n), ApplicationInitialization(), truncdim(χ))
env_appl, info = leading_boundary(env0_appl, n; boundary_alg...)
@test info.convergence_error ≤ tol
end
3 changes: 3 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ end
@time @safetestset "correlation length" begin
include("ctmrg/correlation_length.jl")
end
@time @safetestset "initialization" begin
include("ctmrg/initialization.jl")
end
end
if GROUP == "ALL" || GROUP == "GRADIENTS"
@time @safetestset "CTMRG gradients" begin
Expand Down
Loading