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
2 changes: 2 additions & 0 deletions src/MPSKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export time_evolve, timestep, timestep!, make_time_mpo
export TDVP, TDVP2, WI, WII, TaylorCluster
export changebonds, changebonds!
export VUMPSSvdCut, OptimalExpand, SvdCut, RandExpand, SketchedExpand
export Zipper
export propagator
export DynamicalDMRG, NaiveInvert, Jeckelmann
export exact_diagonalization, fidelity_susceptibility
Expand Down Expand Up @@ -188,6 +189,7 @@ include("algorithms/statmech/idmrg.jl")
include("algorithms/fidelity_susceptibility.jl")

include("algorithms/approximate/approximate.jl")
include("algorithms/approximate/zipper.jl")
include("algorithms/approximate/vomps.jl")
include("algorithms/approximate/fvomps.jl")
include("algorithms/approximate/idmrg.jl")
Expand Down
6 changes: 4 additions & 2 deletions src/algorithms/approximate/approximate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
approximate!(ψ₀, (O, ψ), algorithm, [environments]) -> (ψ, environments, ϵ)
approximate(ψ₀, ψ, algorithm, [environments]) -> (ψ, environments, ϵ)
approximate!(ψ₀, ψ, algorithm, [environments]) -> (ψ, environments, ϵ)
approximate((O, ψ), algorithm) -> ψ′

Compute an approximation to the application of an operator `O` to the state `ψ` in the form
of an MPS `ψ₀`. If only a state `ψ` is supplied instead of the `(O, ψ)` pair, `ψ₀` is
approximated directly to `ψ` (i.e. `O` is taken to be the identity).
of an MPS, using initial guess `ψ₀`. If only a state `ψ` is supplied instead of the `(O, ψ)` pair,
`ψ₀` is approximated directly to `ψ` (i.e. `O` is taken to be the identity).

**Not every algorithm supports every combination of arguments below** — see the per-algorithm
notes at the end of this docstring before picking one.
Expand Down Expand Up @@ -41,6 +42,7 @@ infinite algorithms always require an explicit `(O, ψ)` tuple, and **`VOMPS` ha
|:--------- |:----------------------------- |:---------------------------------- |:------------------:|:--------------:|
| `DMRG` | single-site, fixes bond dim | `AbstractFiniteMPS` | ✅ | ✅ |
| `DMRG2` | two-site, truncates via `trscheme` | `AbstractFiniteMPS` | ✅ | ✅ |
| `Zipper` | right-to-left streaming MPO-MPS compression | none, uses `(O, ψ)` directly | ❌ | ❌ |
| `IDMRG` | single-site, thermodynamic limit | `InfiniteMPS` / `MultilineMPS` | ❌ (tuple only) | ✅ |
| `IDMRG2` | two-site, thermodynamic limit, needs unit cell ≥ 2 | `InfiniteMPS` / `MultilineMPS` | ❌ (tuple only) | ✅ |
| `VOMPS` | tangent-space truncation | `InfiniteMPS` / `MultilineMPS` | ❌ (tuple only) | ❌ (out-of-place only) |
Expand Down
57 changes: 57 additions & 0 deletions src/algorithms/approximate/zipper.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Zipper(; alg_svd=Defaults.alg_svd(), trscheme)

Algorithm that approximates an open-boundary finite MPO-MPS product using a right-to-left
zipper sweep. The MPO and MPS are contracted one site at a time, and the enlarged virtual
bond is truncated immediately using `trscheme`.

Use as:

approximate((O, ψ), Zipper(; trscheme))

This returns an unnormalized compression of `O * ψ`, comparable to
`changebonds(O * ψ, SvdCut(; trscheme); normalize=false)`, but without storing
the fully enlarged product on every site.
"""
@kwdef struct Zipper{S} <: Algorithm
"algorithm used for the singular value decomposition"
alg_svd::S = Defaults.alg_svd()

"algorithm used for truncation of the local gauge tensors"
trscheme::TruncationStrategy
Comment on lines +17 to +21

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think I've started putting these into a single alg_gauge struct instead, that way also things like randomized SVD could be used. I agree that keeping these keyword arguments can be nice though, so it is probably also reasonable to write a custom constructor that can accept them.

end

function approximate((O, ψ)::Tuple{Any, <:FiniteMPS}, alg::Zipper)
N = check_length(O, ψ)
if !isunitspace(left_virtualspace(O, 1)) || !isunitspace(right_virtualspace(O, N))
throw(ArgumentError("Zipper is only implemented for open-boundary MPOs"))
end

ψ′ = copy(ψ)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason we need this copy here? And if so, could you restructure it to be approximate((O, psi), alg) = approximate(copy(psi), (O, psi), alg)?

T = TensorOperations.promote_contract(scalartype(O), scalartype(ψ))
A = TensorKit.similarstoragetype(eltype(ψ), T)

Fᵣ = fuser(A, right_virtualspace(ψ′, N), right_virtualspace(O, N))
local carry
alg_gauge = MatrixAlgebraKit.TruncatedAlgorithm(alg.alg_svd, alg.trscheme)

As = map(N:-1:1) do i

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason to go from back to front specifically? I think this needs extra transpositions in many places, mostly because our MPS tensors are N,1, so left_gauge is slightly more efficient than right_gauge.

Aψ = i == 1 ? ψ′.AC[1] : ψ′.AR[i]
physicalspace(Aψ) == physicalspace(O[i]) ||
throw(SpaceMismatch("MPO input physical space does not match MPS physical space at site $i"))
Fₗ = fuser(A, left_virtualspace(ψ′, i), left_virtualspace(O, i))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For double-layer iPEPS networks, I'll need to fuse 3 virtual legs. In this case fuser should be the combination of two pair-wise isomorphisms?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure if that is really necessary, in principle you can just fuse all of them together, although we might have to adapt the fuser function to also take ProductSpace arguments.

Aᶻ = _fuse_mpo_mps(O[i], Aψ, Fₗ, Fᵣ)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I feel that the default _fuse_mpo_mps, which uses @plansor, will cause troubles for iPEPO measurements. For column-by-column contraction from right to left, the default iPEPO arrow convention (top to bottom) will make the MPO virtual spaces dual.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure if that really is a problem, since you will presumably have to overload that anyways?

i < N && (Aᶻ = Aᶻ * carry)

if i == 1
return Aᶻ
else
C, AR = right_gauge(Aᶻ, alg_gauge)
carry = C
Fᵣ = Fₗ
return AR
end
end

return FiniteMPS(reverse(As); normalize = false, overwrite = true)
end
1 change: 0 additions & 1 deletion test/algorithms/dynamical_dmrg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,3 @@ end
@test data ≈ predicted atol = 1.0e-8
end
end

39 changes: 39 additions & 0 deletions test/algorithms/zipper.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
println("
-----------------------------
| Zipper tests |
-----------------------------
")

using .TestSetup
using Test
using MPSKit
using TensorKit
using TensorKit: ℙ
using Random

spacelist = [(ℙ^4, ℙ^3), (Rep[SU₂](1 => 1), Rep[SU₂](0 => 2, 1 => 2, 2 => 1))]

@testset "Finite MPO-MPS zipper $(spacetype(pspace))" for (pspace, Dspace) in spacelist
Random.seed!(1357)
L = 6
Wspace = Dspace
Vspaces = [oneunit(Wspace); fill(Wspace, L - 1); oneunit(Wspace)]
O = FiniteMPO(
[rand(ComplexF64, Vspaces[i] ⊗ pspace ← pspace ⊗ Vspaces[i + 1]) for i in 1:L]
)
ψ = FiniteMPS(rand, ComplexF64, L, pspace, Dspace)
O_copy = copy(O)
ψ_copy = copy(ψ)

trscheme = trunctol(; atol = 1.0e-10)
ref = changebonds(O * ψ, SvdCut(; trscheme); normalize = false)
got = approximate((O, ψ), Zipper(; trscheme))

@test norm(ref - got) / norm(ref) < 1.0e-10
@test norm(ψ - ψ_copy) < 1.0e-12
@test all(i -> norm(O[i] - O_copy[i]) < 1.0e-12, 1:length(O))

Dcut = 4
got_tr = approximate((O, ψ), Zipper(; trscheme = truncrank(Dcut)))
@test maximum(i -> dim(left_virtualspace(got_tr, i)), 2:length(got_tr)) <= Dcut
end
Loading