diff --git a/docs/src/assets/mpskit.bib b/docs/src/assets/mpskit.bib index 296f4194b..cf9e3fd0b 100644 --- a/docs/src/assets/mpskit.bib +++ b/docs/src/assets/mpskit.bib @@ -385,6 +385,23 @@ @misc{shen2025 keywords = {Condensed Matter - Strongly Correlated Electrons,High Energy Physics - Theory} } +@article{sinha2024, + title = {Efficient Representation of Minimally Entangled Typical Thermal States in Two Dimensions via Projected Entangled Pair States}, + author = {Sinha, Aritra and Rams, Marek M. and Dziarmaga, Jacek}, + year = {2024}, + month = jan, + journal = {Physical Review B}, + volume = {109}, + number = {4}, + pages = {045136}, + publisher = {American Physical Society}, + doi = {10.1103/PhysRevB.109.045136}, + url = {https://link.aps.org/doi/10.1103/PhysRevB.109.045136}, + eprint = {2310.08533}, + archiveprefix = {arXiv}, + primaryclass = {quant-ph} +} + @article{tang2025, title = {Matrix Product State Fixed Points of Non-{{Hermitian}} Transfer Matrices}, author = {Tang, Wei and Verstraete, Frank and Haegeman, Jutho}, diff --git a/src/MPSKit.jl b/src/MPSKit.jl index 2ef8cf900..ed369c5ea 100644 --- a/src/MPSKit.jl +++ b/src/MPSKit.jl @@ -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 @@ -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") diff --git a/src/algorithms/approximate/approximate.jl b/src/algorithms/approximate/approximate.jl index 6967dcef0..b90f64770 100644 --- a/src/algorithms/approximate/approximate.jl +++ b/src/algorithms/approximate/approximate.jl @@ -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. @@ -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) | diff --git a/src/algorithms/approximate/zipper.jl b/src/algorithms/approximate/zipper.jl new file mode 100644 index 000000000..ca9d89fd9 --- /dev/null +++ b/src/algorithms/approximate/zipper.jl @@ -0,0 +1,64 @@ +""" +$(TYPEDEF) + +Algorithm that approximates an open-boundary finite MPO-MPS product using a left-to-right +zipper sweep. The MPO and MPS are contracted one site at a time, and the enlarged virtual +bond is truncated immediately using `trscheme`. + +## Fields + +$(TYPEDFIELDS) + +## Constructors + + Zipper(; trscheme, alg_svd=Defaults.alg_svd()) + Zipper(alg_gauge) + +Create a `Zipper` algorithm with the given truncated gauge algorithm, or by passing a +truncation scheme and singular value decomposition algorithm. + +## References + +- [Sinha et al. Phys. Rev. B 109 (2024)](@cite sinha2024) +""" +struct Zipper{G} <: Algorithm + "algorithm used for gauging and truncating the local tensors" + alg_gauge::G +end + +function Zipper(; trscheme::TruncationStrategy, alg_svd = Defaults.alg_svd()) + return Zipper(MatrixAlgebraKit.TruncatedAlgorithm(alg_svd, trscheme)) +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 + + T = TensorOperations.promote_contract(scalartype(O), scalartype(ψ)) + A = TensorKit.similarstoragetype(eltype(ψ), T) + + Fₗ = fuser(A, left_virtualspace(ψ, 1), left_virtualspace(O, 1)) + local carry + + As = map(1:N) do i + 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, right_virtualspace(ψ, i), right_virtualspace(O, i)) + Aᶻ = _fuse_mpo_mps(O[i], Aψ, Fₗ, Fᵣ) + i > 1 && (Aᶻ = _mul_front(carry, Aᶻ)) + + if i == N + return Aᶻ + else + AL, C, _ = left_gauge(Aᶻ, alg.alg_gauge) + carry = C + Fₗ = Fᵣ + return AL + end + end + + return FiniteMPS(As; normalize = false, overwrite = true) +end diff --git a/test/algorithms/dynamical_dmrg.jl b/test/algorithms/dynamical_dmrg.jl index 146c43af3..8e07757f4 100644 --- a/test/algorithms/dynamical_dmrg.jl +++ b/test/algorithms/dynamical_dmrg.jl @@ -60,4 +60,3 @@ end @test data ≈ predicted atol = 1.0e-8 end end - diff --git a/test/algorithms/zipper.jl b/test/algorithms/zipper.jl new file mode 100644 index 000000000..84735af3f --- /dev/null +++ b/test/algorithms/zipper.jl @@ -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