-
Notifications
You must be signed in to change notification settings - Fork 53
Zipper compression for FiniteMPO-FiniteMPS product #470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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(ψ) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| 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)) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| Aᶻ = _fuse_mpo_mps(O[i], Aψ, Fₗ, Fᵣ) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel that the default
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,4 +60,3 @@ end | |
| @test data ≈ predicted atol = 1.0e-8 | ||
| end | ||
| end | ||
|
|
||
| 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 |
There was a problem hiding this comment.
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_gaugestruct 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.