Skip to content
9 changes: 9 additions & 0 deletions docs/src/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ When releasing a new version, move the "Unreleased" changes to a new version sec
return value regardless, so this adds no extra cost. ([#509](https://github.com/QuantumKitHub/MPSKit.jl/pull/509))
- `make_time_mpo` with `TaylorCluster` on a Hamiltonian whose virtual bond dimension varies along
the chain are now correctly handled. ([#511](https://github.com/QuantumKitHub/MPSKit.jl/pull/511))
- The converting constructor of `JordanMPO_AC_Hamiltonian` assigned the "finished" block `E` to
the "ending" field `B` whenever `B` was absent, raising a `convert` `MethodError` from deep
inside `AC_hamiltonian`. This is reached when an `MPOHamiltonian` whose scalartype differs from
the state's has an on-site term on a site where no interaction ends, such as a long-range one.
([#493](https://github.com/QuantumKitHub/MPSKit.jl/pull/493))
- Raised the `BlockTensorKit` compat lower bound to 0.3.19, which fixes a silent data-loss bug: with
`TensorKit` 0.17.2 and `BlockTensorKit` <= 0.3.18, a permuted contraction into a sparse block
tensor (e.g. an environment sweep near a `FiniteMPS` chain boundary when the operator and state
have different `scalartype`s) could silently drop data instead of erroring.

### Performance

Expand Down
280 changes: 155 additions & 125 deletions test/groundstate/groundstate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,132 +20,152 @@ verbosity_conv = 1
D = 6
L = 10

H = force_planar(transverse_field_ising(; g, L))
models = [
"nearest-neighbour" => force_planar(transverse_field_ising(; g, L)),
"long-range, real scalartype" => force_planar(long_range_ising(Float64; g, L)),
]

@testset "DMRG" begin
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^D)
v₀ = variance(ψ₀, H)
@testset "$name" for (name, H) in models
@testset "DMRG" begin
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^D)
v₀ = variance(ψ₀, H)

# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2)
)
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2)
)

ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10), envs
)
v = variance(ψ, H)
ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10), envs
)
v = variance(ψ, H)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
end

# the algorithm object carries no scratch space of its own - the sweep's allocator is
# obtained per solve - so re-using one across solves has to reproduce the answer
alg = DMRG(; verbosity = verbosity_conv, maxiter = 10)
ψ1, = find_groundstate(ψ₀, H, alg)
ψ2, = find_groundstate(ψ₀, H, alg)
@test expectation_value(ψ1, H) ≈ expectation_value(ψ2, H) atol = 1.0e-10
end
@testset "DMRG2" begin
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^D)
v₀ = variance(ψ₀, H)
trunc = truncrank(floor(Int, D * 1.5))
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG2(; verbosity = verbosity_full, maxiter = 2, trunc)
)

@testset "DMRG2" begin
ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℙ^2, ℙ^D)
v₀ = variance(ψ₀, H)
trunc = truncrank(floor(Int, D * 1.5))
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG2(; verbosity = verbosity_full, maxiter = 2, trunc)
)
ψ, envs, δ = find_groundstate(
ψ, H, DMRG2(; verbosity = verbosity_conv, maxiter = 10, trunc), envs
)
v = variance(ψ, H)

ψ, envs, δ = find_groundstate(
ψ, H, DMRG2(; verbosity = verbosity_conv, maxiter = 10, trunc), envs
)
v = variance(ψ, H)
# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
end

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
end
@testset "CBEDMRG" begin
# start from a small bond so the bond expansion is exercised
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^(D ÷ 2))
v₀ = variance(ψ₀, H)
expand = OptimalExpand(; trunc = truncrank(D ÷ 2))
trunc = truncrank(D)

@testset "CBEDMRG" begin
# start from a small bond so the bond expansion is exercised
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^(D ÷ 2))
v₀ = variance(ψ₀, H)
expand = OptimalExpand(; trunc = truncrank(D ÷ 2))
trunc = truncrank(D)
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2, alg_expand = expand, trunc)
)

# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2, alg_expand = expand, trunc)
)
ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10, alg_expand = expand, trunc), envs
)
v = variance(ψ, H)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# the bond should have grown to the truncation target
@test dim(left_virtualspace(ψ, L ÷ 2)) == D
end

ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10, alg_expand = expand, trunc), envs
)
v = variance(ψ, H)
@testset "CBEDMRG (SketchedExpand)" begin
# randomized bond expansion at single-site cost. The sketch is redrawn every sweep, so an
# aggressive expansion (a large fraction of the bond) keeps the single-site Galerkin error
# noisy; a gentle per-sweep increment lets it converge like the deterministic expanders.
Random.seed!(1234)
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^(D ÷ 2))
v₀ = variance(ψ₀, H)
expand = SketchedExpand(; trunc = truncrank(2), oversampling = 4)
trunc = truncrank(D)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# the bond should have grown to the truncation target
@test dim(left_virtualspace(ψ, L ÷ 2)) == D
end
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2, alg_expand = expand, trunc)
)

@testset "CBEDMRG (SketchedExpand)" begin
# randomized bond expansion at single-site cost. The sketch is redrawn every sweep, so an
# aggressive expansion (a large fraction of the bond) keeps the single-site Galerkin error
# noisy; a gentle per-sweep increment lets it converge like the deterministic expanders.
Random.seed!(1234)
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^(D ÷ 2))
v₀ = variance(ψ₀, H)
expand = SketchedExpand(; trunc = truncrank(2), oversampling = 4)
trunc = truncrank(D)
ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 15, alg_expand = expand, trunc), envs
)
v = variance(ψ, H)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# the bond should have grown to the truncation target
@test dim(left_virtualspace(ψ, L ÷ 2)) == D
end

# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2, alg_expand = expand, trunc)
)
@testset "DMRG3S" begin
# start from a small bond so the post-expansion is exercised, mirroring CBEDMRG above
Random.seed!(1234)
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^(D ÷ 2))
v₀ = variance(ψ₀, H)
alg_gauge = DMRG3S(0.1, ExponentialDecay(0.7)) # TODO: match final constructor API
trunc = truncrank(D)

ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 15, alg_expand = expand, trunc), envs
)
v = variance(ψ, H)
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2, alg_gauge, trunc)
)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# the bond should have grown to the truncation target
@test dim(left_virtualspace(ψ, L ÷ 2)) == D
end
ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10, alg_gauge, trunc), envs
)
v = variance(ψ, H)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# the bond should have grown to the truncation target
@test dim(left_virtualspace(ψ, L ÷ 2)) == D
end

@testset "DMRG3S" begin
# start from a small bond so the post-expansion is exercised, mirroring CBEDMRG above
Random.seed!(1234)
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^(D ÷ 2))
v₀ = variance(ψ₀, H)
alg_gauge = DMRG3S(0.1, ExponentialDecay(0.7)) # TODO: match final constructor API
trunc = truncrank(D)
@testset "GradientGrassmann" begin
ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^D)
v₀ = variance(ψ₀, H)

# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2, alg_gauge, trunc)
)
# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, GradientGrassmann(; verbosity = verbosity_full, maxiter = 2)
)

ψ, envs, δ = find_groundstate(
ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10, alg_gauge, trunc), envs
)
v = variance(ψ, H)
# an explicit `tol` keeps the optimizer from overshooting past the point where the
# gradient is floating-point noise: pushed further, the CG line search can hit a
# 0/0 in its step-size formula and feed a NaN tangent into the Grassmann retraction
ψ, envs, δ = find_groundstate(
ψ, H, GradientGrassmann(; tol, verbosity = verbosity_conv, maxiter = 50), envs
)
v = variance(ψ, H)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
# the bond should have grown to the truncation target
@test dim(left_virtualspace(ψ, L ÷ 2)) == D
# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀ && v < 1.0e-2
end
end

fast_tests || @testset "DMRG3S escapes local minimum (Hubig et al. 2015, Sec. VII A)" begin
Expand Down Expand Up @@ -173,25 +193,6 @@ verbosity_conv = 1
@test E_escape < E_stuck - 1.0
@test isapprox(E_escape, -8.6824724; atol = 1.0e-4)
end

@testset "GradientGrassmann" begin
ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℙ^2, ℙ^D)
v₀ = variance(ψ₀, H)

# test logging
ψ, envs, δ = find_groundstate(
ψ₀, H, GradientGrassmann(; verbosity = verbosity_full, maxiter = 2)
)

ψ, envs, δ = find_groundstate(
ψ, H, GradientGrassmann(; verbosity = verbosity_conv, maxiter = 50), envs
)
v = variance(ψ, H)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀ && v < 1.0e-2
end
end

@testset "InfiniteMPS ground state" verbose = true begin
Expand All @@ -206,6 +207,7 @@ end
H_ref_realT = force_planar(transverse_field_ising(Float64; g))
@test variance(ψ, H_ref_realT) ≈ v₀ atol = 1.0e-10


@testset "VUMPS (unit cell $unit_cell_size, $schedname)" for unit_cell_size in [1, 3],
(schedname, scheduler) in SCHEDULERS

Expand All @@ -227,6 +229,20 @@ end
@test v < 1.0e-2
end

@testset "VUMPS (long-range, real scalartype)" begin
H = force_planar(long_range_ising_infinite(Float64; g, L = 3))
ψ₀ = InfiniteMPS(fill(ℙ^2, 3), fill(ℙ^D, 3))
v₀ = variance(ψ₀, H)

ψ′, envs, δ = find_groundstate(ψ₀, H, VUMPS(; tol, verbosity = verbosity_conv, maxiter = 20))
v = variance(ψ′, H, envs)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
end

@testset "IDMRG" for unit_cell_size in [1, 3]
ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size)
H = repeat(H_ref, unit_cell_size)
Expand All @@ -245,6 +261,20 @@ end
@test v < 1.0e-2
end

@testset "IDMRG (long-range, real scalartype)" begin
H = force_planar(long_range_ising_infinite(Float64; g, L = 3))
ψ₀ = InfiniteMPS(fill(ℙ^2, 3), fill(ℙ^D, 3))
v₀ = variance(ψ₀, H)

ψ, envs, δ = find_groundstate(ψ₀, H, IDMRG(; tol, verbosity = verbosity_conv, maxiter = 20))
v = variance(ψ, H, envs)

# test using low variance
@test sum(δ) ≈ 0 atol = 1.0e-3
@test v < v₀
@test v < 1.0e-2
end

@testset "IDMRG2" begin
ψ = repeat(InfiniteMPS(ℙ^2, ℙ^D), 2)
H = repeat(H_ref, 2)
Expand Down
Loading
Loading