From 893b6e44190fe12ea08dd52269facf8424ae904f Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Thu, 27 Feb 2025 17:35:57 -0500 Subject: [PATCH 01/14] Fix `fin_mpo * fin_mps` with mixed scalartypes --- src/operators/mpo.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index b4344494..4a67f36a 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -225,18 +225,17 @@ end function Base.:*(mpo::FiniteMPO, mps::FiniteMPS) length(mpo) == length(mps) || throw(ArgumentError("dimension mismatch")) - - A = [mps.AC[1]; mps.AR[2:end]] - TT = storagetype(eltype(A)) + TT = storagetype(eltype(mps)) local Fᵣ # trick to make Fᵣ defined in the loop - for i in 1:length(mps) + A2 = map(1:length(mps)) do i + A1 = i == 1 ? mps.AC[1] : mps.AR[i] Fₗ = i != 1 ? Fᵣ : fuser(TT, left_virtualspace(mps, i), left_virtualspace(mpo, i)) Fᵣ = fuser(TT, right_virtualspace(mps, i), right_virtualspace(mpo, i)) - A[i] = _fuse_mpo_mps(mpo[i], A[i], Fₗ, Fᵣ) + return _fuse_mpo_mps(mpo[i], A1, Fₗ, Fᵣ) end - return changebonds!(FiniteMPS(A), + return changebonds!(FiniteMPS(A2), SvdCut(; trscheme=truncbelow(eps(real(scalartype(TT))))); normalize=false) end From 7fef91901d354d04fb252909928d85a4f69995d2 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Thu, 27 Feb 2025 17:45:56 -0500 Subject: [PATCH 02/14] Add `complex(::FiniteMPS)` --- src/states/finitemps.jl | 13 +++++++++++++ test/states.jl | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/src/states/finitemps.jl b/src/states/finitemps.jl index 92a41b31..e0d89af4 100644 --- a/src/states/finitemps.jl +++ b/src/states/finitemps.jl @@ -316,6 +316,19 @@ Base.@propagate_inbounds function Base.getindex(ψ::FiniteMPS, i::Int) end end +_complex_if_not_missing(x) = ismissing(x) ? x : complex(x) +function Base.complex(mps::FiniteMPS) + scalartype(mps) <: Complex && return mps + ALs = _complex_if_not_missing.(mps.ALs) + ARs = _complex_if_not_missing.(mps.ARs) + Cs = _complex_if_not_missing.(mps.Cs) + ACs = _complex_if_not_missing.(mps.ACs) + return FiniteMPS(collect(Union{Missing,eltype(ALs)}, ALs), + collect(Union{Missing,eltype(ARs)}, ARs), + collect(Union{Missing,eltype(ACs)}, ACs), + collect(Union{Missing,eltype(Cs)}, Cs)) +end + @inline function Base.getindex(ψ::FiniteMPS, I::AbstractUnitRange) return Base.getindex.(Ref(ψ), I) end diff --git a/test/states.jl b/test/states.jl index 1ffbed23..13e27fbf 100644 --- a/test/states.jl +++ b/test/states.jl @@ -45,6 +45,15 @@ end ψ_small = FiniteMPS(rand, elt, 4, d, D) ψ_small2 = FiniteMPS(convert(TensorMap, ψ_small)) @test dot(ψ_small, ψ_small2) ≈ dot(ψ_small, ψ_small) + + ψ′ = @constinferred complex(ψ) + @test scalartype(ψ′) <: Complex + if elt <: Complex + @test ψ === ψ′ + else + @test norm(ψ) ≈ norm(ψ′) + @test complex(convert(TensorMap, ψ)) ≈ convert(TensorMap, ψ′) + end end @testset "FiniteMPS center + (slice) indexing" begin From 2be4015a1483f638d3a8130a78da7dda9b41c692 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 06:37:48 -0500 Subject: [PATCH 03/14] Fix `fin_mpo + fin_mpo` with mixed scalartype --- Project.toml | 2 +- src/MPSKit.jl | 1 + src/operators/mpo.jl | 11 ++++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index e6d04867..2a0392f5 100644 --- a/Project.toml +++ b/Project.toml @@ -19,6 +19,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" TensorKit = "07d1fe3e-3e46-537d-9eac-e9e13d0d4cec" TensorKitManifolds = "11fa318c-39cb-4a83-b1ed-cdc7ba1e3684" +TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2" VectorInterface = "409d34a3-91d5-4945-b6ec-7529ddf182d8" [compat] @@ -50,7 +51,6 @@ julia = "1.10" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestExtras = "5ed8adda-3752-4e41-b88a-e8b09835ee3a" diff --git a/src/MPSKit.jl b/src/MPSKit.jl index 1fe569db..288a0eb8 100644 --- a/src/MPSKit.jl +++ b/src/MPSKit.jl @@ -60,6 +60,7 @@ using Compat: @compat using TensorKit using TensorKit: BraidingTensor using BlockTensorKit +using TensorOperations using KrylovKit using KrylovKit: KrylovAlgorithm using OptimKit diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index 4a67f36a..362b9a76 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -107,14 +107,13 @@ end # VectorInterface.scalartype(::Type{FiniteMPO{O}}) where {O} = scalartype(O) Base.:+(mpo::MPO) = MPO(map(+, parent(mpo))) -function Base.:+(mpo1::FiniteMPO{TO}, mpo2::FiniteMPO{TO}) where {TO<:MPOTensor} - (N = length(mpo1)) == length(mpo2) || throw(ArgumentError("dimension mismatch")) +function Base.:+(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) + N = check_length(mpo1, mpo2) @assert left_virtualspace(mpo1, 1) == left_virtualspace(mpo2, 1) && right_virtualspace(mpo1, N) == right_virtualspace(mpo2, N) - mpo = similar(parent(mpo1)) halfN = N ÷ 2 - A = storagetype(TO) + A = storagetype(eltype(mpo1)) # left half F₁ = isometry(A, (right_virtualspace(mpo1, 1) ⊕ right_virtualspace(mpo2, 1)), @@ -127,7 +126,9 @@ function Base.:+(mpo1::FiniteMPO{TO}, mpo2::FiniteMPO{TO}) where {TO<:MPOTensor} # making sure that the new operator is "full rank" O, R = leftorth!(O) - mpo[1] = transpose(O, ((2, 3), (1, 4))) + O′ = transpose(O, ((2, 3), (1, 4))) + mpo = similar(mpo1, typeof(O′)) + mpo[1] = O′ for i in 2:halfN # incorporate fusers from left side From c05a731734d9993b0ce28013ee1822c0e3c696bf Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 06:54:57 -0500 Subject: [PATCH 04/14] simplify implementation --- src/operators/mpo.jl | 53 +++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index 362b9a76..3a05d3cd 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -195,10 +195,10 @@ function VectorInterface.scale!(mpo::MPO, α::Number) return mpo end -# TODO: merge implementation with that of InfiniteMPO -function Base.:*(mpo1::FiniteMPO{TO}, mpo2::FiniteMPO{TO}) where {TO<:MPOTensor} - (N = length(mpo1)) == length(mpo2) || throw(ArgumentError("dimension mismatch")) - S = spacetype(TO) +function Base.:*(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) + check_length(mpo1, mpo2) + (S = spacetype(mpo1)) == spacetype(mpo2) || throw(SectorMismatch()) + if (left_virtualspace(mpo1, 1) != oneunit(S) || left_virtualspace(mpo2, 1) != oneunit(S)) || (right_virtualspace(mpo1, N) != oneunit(S) || @@ -208,31 +208,25 @@ function Base.:*(mpo1::FiniteMPO{TO}, mpo2::FiniteMPO{TO}) where {TO<:MPOTensor} # would work and for now I dont feel like figuring out if this is important end - O = similar(parent(mpo1)) - A = storagetype(TO) - - # note order of mpos: mpo1 * mpo2 * state -> mpo2 on top of mpo1 - local Fᵣ # trick to make Fᵣ defined in the loop - for i in 1:N - Fₗ = i != 1 ? Fᵣ : fuser(A, left_virtualspace(mpo2, i), left_virtualspace(mpo1, i)) - Fᵣ = fuser(A, right_virtualspace(mpo2, i), right_virtualspace(mpo1, i)) - @plansor O[i][-1 -2; -3 -4] := Fₗ[-1; 1 4] * mpo2[i][1 2; -3 3] * - mpo1[i][4 -2; 2 5] * - conj(Fᵣ[-4; 3 5]) - end - + O = map(fuse_mul_mpo, parent(mpo1), parent(mpo2)) return changebonds!(FiniteMPO(O), SvdCut(; trscheme=notrunc())) end +function Base.:*(mpo1::InfiniteMPO, mpo2::InfiniteMPO) + check_length(mpo1, mpo2) + Os = map(fuse_mul_mpo, parent(mpo1), parent(mpo2)) + return InfiniteMPO(Os) +end function Base.:*(mpo::FiniteMPO, mps::FiniteMPS) - length(mpo) == length(mps) || throw(ArgumentError("dimension mismatch")) - TT = storagetype(eltype(mps)) + N = check_length(mpo, mps) + T = TensorOperations.promote_contract(scalartype(mpo), scalartype(mps)) + A = TensorKit.similarstoragetype(eltype(mps), T) - local Fᵣ # trick to make Fᵣ defined in the loop - A2 = map(1:length(mps)) do i + Fᵣ = fuser(A, left_virtualspace(mps, 1), left_virtualspace(mpo, 1)) + A2 = map(1:N) do i A1 = i == 1 ? mps.AC[1] : mps.AR[i] - Fₗ = i != 1 ? Fᵣ : fuser(TT, left_virtualspace(mps, i), left_virtualspace(mpo, i)) - Fᵣ = fuser(TT, right_virtualspace(mps, i), right_virtualspace(mpo, i)) + Fₗ = Fᵣ + Fᵣ = fuser(A, right_virtualspace(mps, i), right_virtualspace(mpo, i)) return _fuse_mpo_mps(mpo[i], A1, Fₗ, Fᵣ) end @@ -240,11 +234,11 @@ function Base.:*(mpo::FiniteMPO, mps::FiniteMPS) SvdCut(; trscheme=truncbelow(eps(real(scalartype(TT))))); normalize=false) end - function Base.:*(mpo::InfiniteMPO, mps::InfiniteMPS) L = check_length(mpo, mps) T = promote_type(scalartype(mpo), scalartype(mps)) - fusers = PeriodicArray(fuser.(T, left_virtualspace.(Ref(mps), 1:L), + A = TensorKit.similarstoragetype(eltype(mps), T) + fusers = PeriodicArray(fuser.(A, left_virtualspace.(Ref(mps), 1:L), left_virtualspace.(Ref(mpo), 1:L))) As = map(1:L) do i return _fuse_mpo_mps(mpo[i], mps.AL[i], fusers[i], fusers[i + 1]) @@ -260,12 +254,6 @@ function _fuse_mpo_mps(O::MPOTensor, A::MPSTensor, Fₗ, Fᵣ) return A′ isa AbstractBlockTensorMap ? TensorMap(A′) : A′ end -function Base.:*(mpo1::InfiniteMPO, mpo2::InfiniteMPO) - check_length(mpo1, mpo2) - Os = map(fuse_mul_mpo, parent(mpo1), parent(mpo2)) - return InfiniteMPO(Os) -end - function Base.:*(mpo::FiniteMPO{<:MPOTensor}, x::AbstractTensorMap) @assert length(mpo) > 1 @assert numout(x) == length(mpo) @@ -281,8 +269,7 @@ end # in the middle function TensorKit.dot(bra::FiniteMPS{T}, mpo::FiniteMPO{<:MPOTensor}, ket::FiniteMPS{T}) where {T} - (N = length(bra)) == length(mpo) == length(ket) || - throw(ArgumentError("dimension mismatch")) + N = check_length(bra, mpo, ket) Nhalf = N ÷ 2 # left half ρ_left = isomorphism(storagetype(T), From e9b11d9a97d1ceb3ac2a99b304844e57f341f446 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 07:15:25 -0500 Subject: [PATCH 05/14] add utility `left_virtualspace` and `right_virtualspace` --- src/operators/abstractmpo.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/operators/abstractmpo.jl b/src/operators/abstractmpo.jl index 4236e2a0..57c7e0c0 100644 --- a/src/operators/abstractmpo.jl +++ b/src/operators/abstractmpo.jl @@ -24,7 +24,9 @@ end # Properties # ---------- left_virtualspace(mpo::AbstractMPO, site::Int) = left_virtualspace(mpo[site]) +left_virtualspace(mpo::AbstractMPO) = map(left_virtualspace, parent(mpo)) right_virtualspace(mpo::AbstractMPO, site::Int) = right_virtualspace(mpo[site]) +right_virtualspace(mpo::AbstractMPO) = map(right_virtualspace, parent(mpo)) physicalspace(mpo::AbstractMPO, site::Int) = physicalspace(mpo[site]) physicalspace(mpo::AbstractMPO) = map(physicalspace, mpo) From 7e95a876eeaa99963c48534fbc2e4c1a15e9f486 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 07:15:42 -0500 Subject: [PATCH 06/14] `scale(::MPO, ::Number)` with mixed scalartypes --- src/operators/abstractmpo.jl | 6 +++++- src/operators/mpo.jl | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/operators/abstractmpo.jl b/src/operators/abstractmpo.jl index 57c7e0c0..985db3dc 100644 --- a/src/operators/abstractmpo.jl +++ b/src/operators/abstractmpo.jl @@ -172,7 +172,11 @@ Base.:*(mpo::AbstractMPO, α::Number) = scale(mpo, α) Base.:/(mpo::AbstractMPO, α::Number) = scale(mpo, inv(α)) Base.:\(α::Number, mpo::AbstractMPO) = scale(mpo, inv(α)) -VectorInterface.scale(mpo::AbstractMPO, α::Number) = scale!(copy(mpo), α) +function VectorInterface.scale(mpo::AbstractMPO, α::Number) + T = VectorInterface.promote_scale(scalartype(mpo), scalartype(α)) + dst = similar(mpo, T) + return scale!(dst, mpo, α) +end LinearAlgebra.norm(mpo::AbstractMPO) = sqrt(abs(dot(mpo, mpo))) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index 3a05d3cd..c7411f86 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -194,6 +194,13 @@ function VectorInterface.scale!(mpo::MPO, α::Number) scale!(first(mpo), α) return mpo end +function VectorInterface.scale!(dst::MPO, src::MPO, α::Number) + N = check_length(dst, src) + for i in 1:N + scale!(dst[i], src[i], i == 1 ? α : One()) + end + return dst +end function Base.:*(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) check_length(mpo1, mpo2) From ccb507b570f8a69a4011934b9a493c2c885558d4 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 07:19:52 -0500 Subject: [PATCH 07/14] small fixes --- src/operators/mpo.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index c7411f86..d2400b37 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -55,9 +55,12 @@ DenseMPO(mpo::MPO) = mpo isa DenseMPO ? copy(mpo) : MPO(map(TensorMap, parent(mp Base.parent(mpo::MPO) = mpo.O Base.copy(mpo::MPO) = MPO(map(copy, mpo)) -function Base.similar(mpo::MPO, ::Type{O}, L::Int) where {O} +function Base.similar(mpo::MPO{<:MPOTensor}, ::Type{O}, L::Int) where {O<:MPOTensor} return MPO(similar(parent(mpo), O, L)) end +function Base.similar(mpo::MPO, ::Type{T}) where {T<:Number} + return MPO(similar.(parent(mpo), T)) +end Base.repeat(mpo::MPO, n::Int) = MPO(repeat(parent(mpo), n)) Base.repeat(mpo::MPO, rows::Int, cols::Int) = MultilineMPO(fill(repeat(mpo, cols), rows)) @@ -203,7 +206,7 @@ function VectorInterface.scale!(dst::MPO, src::MPO, α::Number) end function Base.:*(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) - check_length(mpo1, mpo2) + N = check_length(mpo1, mpo2) (S = spacetype(mpo1)) == spacetype(mpo2) || throw(SectorMismatch()) if (left_virtualspace(mpo1, 1) != oneunit(S) || @@ -228,7 +231,6 @@ function Base.:*(mpo::FiniteMPO, mps::FiniteMPS) N = check_length(mpo, mps) T = TensorOperations.promote_contract(scalartype(mpo), scalartype(mps)) A = TensorKit.similarstoragetype(eltype(mps), T) - Fᵣ = fuser(A, left_virtualspace(mps, 1), left_virtualspace(mpo, 1)) A2 = map(1:N) do i A1 = i == 1 ? mps.AC[1] : mps.AR[i] @@ -236,10 +238,8 @@ function Base.:*(mpo::FiniteMPO, mps::FiniteMPS) Fᵣ = fuser(A, right_virtualspace(mps, i), right_virtualspace(mpo, i)) return _fuse_mpo_mps(mpo[i], A1, Fₗ, Fᵣ) end - - return changebonds!(FiniteMPS(A2), - SvdCut(; trscheme=truncbelow(eps(real(scalartype(TT))))); - normalize=false) + trscheme = truncbelow(eps(real(T))) + return changebonds!(FiniteMPS(A2), SvdCut(; trscheme); normalize=false) end function Base.:*(mpo::InfiniteMPO, mps::InfiniteMPS) L = check_length(mpo, mps) From 713a8aaff8dde8e74af7b6a7f625611091eb44c1 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 10:56:21 -0500 Subject: [PATCH 08/14] various changes and improvements --- src/operators/mpo.jl | 2 ++ src/operators/mpohamiltonian.jl | 63 +++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index d2400b37..f617b967 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -105,6 +105,8 @@ function Base.convert(::Type{TensorMap}, mpo::FiniteMPO{<:MPOTensor}) return convert(TensorMap, _instantiate_finitempo(L, M, R)) end +Base.complex(mpo::MPO) = MPO(map(complex, parent(mpo))) + # Linear Algebra # -------------- # VectorInterface.scalartype(::Type{FiniteMPO{O}}) where {O} = scalartype(O) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index f5ba931f..5a5dbfb7 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -374,6 +374,32 @@ function add_physical_charge(H::MPOHamiltonian, charges::AbstractVector{<:Sector end end +# TODO: remove once complex(::BraidingTensor) isa BraidingTensor +# Base.complex(H::MPOHamiltonian) = MPOHamiltonian(map(complex, parent(H))) +function Base.complex(H::MPOHamiltonian) + scalartype(H) <: Complex && return H + Ws = map(parent(H)) do W + W′ = jordanmpotensortype(spacetype(W), complex(scalartype(W))) + W′[1] = W[1] + W′[end] = W[end] + for (I, v) in nonzero_pairs(W) + if v isa BraidingTensor + W′[I] = BraidingTensor{scalartype(W′)}(space(v), v.adjoint) + else + W′[I] = complex(v) + end + end + end + return MPOHamiltonian(H) +end + +function Base.similar(H::MPOHamiltonian, ::Type{O}, L::Int) where {O<:MPOTensor} + return MPOHamiltonian(similar(parent(H), O, L)) +end +function Base.similar(H::MPOHamiltonian, ::Type{T}) where {T<:Number} + return MPOHamiltonian(similar.(parent(H), T)) +end + # Linear Algebra # -------------- @@ -496,6 +522,23 @@ function VectorInterface.scale!(H::FiniteMPOHamiltonian, λ::Number) return H end +function VectorInterface.scale!(dst::MPOHamiltonian, src::MPOHamiltonian, + λ::Number) + N = check_length(dst, src) + for i in 1:N + space(dst[i]) == space(src[i]) || throw(SpaceMismatch()) + zerovector!(dst[i]) + for (I, v) in nonzero_pairs(src[i]) + # only scale "starting" terms + isstarting = I[1] == 1 && + ((isfinite(dst) && i == N && I[4] == size(src[i], 4)) || + ((!isfinite(dst) || i != N) && I[4] > 1)) + dst[i][I] = scale!(dst[i][I], v, isstarting ? λ : One()) + end + end + return dst +end + function Base.:*(H1::MPOHamiltonian, H2::MPOHamiltonian) check_length(H1, H2) Ws = fuse_mul_mpo.(parent(H1), parent(H2)) @@ -503,8 +546,8 @@ function Base.:*(H1::MPOHamiltonian, H2::MPOHamiltonian) end function Base.:*(H::FiniteMPOHamiltonian, mps::FiniteMPS) - check_length(H, mps) - @assert length(mps) > 2 "MPS should have at least three sites, to be implemented otherwise" + N = check_length(H, mps) + @assert N > 2 "MPS should have at least three sites, to be implemented otherwise" A = convert.(BlockTensorMap, [mps.AC[1]; mps.AR[2:end]]) A′ = similar(A, tensormaptype(spacetype(mps), numout(eltype(mps)), numin(eltype(mps)), @@ -515,19 +558,19 @@ function Base.:*(H::FiniteMPOHamiltonian, mps::FiniteMPS) Q, R = leftorth!(a; alg=QR()) A′[1] = convert(TensorMap, Q) - for i in 2:(length(mps) ÷ 2) + for i in 2:(N ÷ 2) @plansor a[-1 -2; -3 -4] := R[-1; 1 2] * A[i][1 3; -3] * H[i][2 -2; 3 -4] Q, R = leftorth!(a; alg=QR()) A′[i] = convert(TensorMap, Q) end # right to middle - U = ones(scalartype(H), right_virtualspace(H, length(H))) + U = ones(scalartype(H), right_virtualspace(H, N)) @plansor a[-1 -2; -3 -4] := A[end][-1 2; -3] * H[end][-2 -4; 2 1] * U[1] L, Q = rightorth!(a; alg=LQ()) A′[end] = transpose(convert(TensorMap, Q), ((1, 3), (2,))) - for i in (length(mps) - 1):-1:(length(mps) ÷ 2 + 2) + for i in (N - 1):-1:(N ÷ 2 + 2) @plansor a[-1 -2; -3 -4] := A[i][-1 3; 1] * H[i][-2 -4; 3 2] * L[1 2; -3] L, Q = rightorth!(a; alg=LQ()) A′[i] = transpose(convert(TensorMap, Q), ((1, 3), (2,))) @@ -535,10 +578,10 @@ function Base.:*(H::FiniteMPOHamiltonian, mps::FiniteMPS) # connect pieces @plansor a[-1 -2; -3] := R[-1; 1 2] * - A[length(mps) ÷ 2 + 1][1 3; 4] * - H[length(mps) ÷ 2 + 1][2 -2; 3 5] * + A[N ÷ 2 + 1][1 3; 4] * + H[N ÷ 2 + 1][2 -2; 3 5] * L[4 5; -3] - A′[length(mps) ÷ 2 + 1] = convert(TensorMap, a) + A′[N ÷ 2 + 1] = convert(TensorMap, a) return FiniteMPS(A′) end @@ -553,9 +596,7 @@ function Base.:*(H::FiniteMPOHamiltonian{<:MPOTensor}, x::AbstractTensorMap) end function TensorKit.dot(H₁::FiniteMPOHamiltonian, H₂::FiniteMPOHamiltonian) - check_length(H₁, H₂) - - N = length(H₁) + N = check_length(H₁, H₂) Nhalf = N ÷ 2 # left half @plansor ρ_left[-1; -2] := conj(H₁[1][1 2; 3 -1]) * H₂[1][1 2; 3 -2] From c58ee3ce420c0dba777934669d98a2bba9ce8a1c Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 10:56:30 -0500 Subject: [PATCH 09/14] Expand testing with mixed scalartypes --- test/operators.jl | 12 ++++++++++++ test/states.jl | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/test/operators.jl b/test/operators.jl index 0877ad3b..e3f05804 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -24,28 +24,40 @@ vspaces = (ℙ^10, Rep[U₁]((0 => 20)), Rep[SU₂](1 // 2 => 10, 3 // 2 => 5, 5 for V in (ℂ^2, U1Space(0 => 1, 1 => 1)) O₁ = rand(T, V^L, V^L) O₂ = rand(T, space(O₁)) + O₃ = rand(real(T), space(O₁)) # create MPO and convert it back to see if it is the same mpo₁ = FiniteMPO(O₁) # type-unstable for now! mpo₂ = FiniteMPO(O₂) + mpo₃ = FiniteMPO(O₃) @test convert(TensorMap, mpo₁) ≈ O₁ @test convert(TensorMap, -mpo₂) ≈ -O₂ + @test convert(TensorMap, @constinferred complex(mpo₃)) ≈ complex(O₃) # test scalar multiplication α = rand(T) @test convert(TensorMap, α * mpo₁) ≈ α * O₁ @test convert(TensorMap, mpo₁ * α) ≈ O₁ * α + @test α * mpo₃ ≈ α * complex(mpo₃) # test addition and multiplication @test convert(TensorMap, mpo₁ + mpo₂) ≈ O₁ + O₂ + @test convert(TensorMap, mpo₁ + mpo₃) ≈ O₁ + O₃ @test convert(TensorMap, mpo₁ * mpo₂) ≈ O₁ * O₂ + @test convert(TensorMap, mpo₁ * mpo₃) ≈ O₁ * O₃ # test application to a state ψ₁ = rand(T, domain(O₁)) + ψ₂ = rand(real(T), domain(O₂)) mps₁ = FiniteMPS(ψ₁) + mps₂ = FiniteMPS(ψ₂) @test convert(TensorMap, mpo₁ * mps₁) ≈ O₁ * ψ₁ @test mpo₁ * ψ₁ ≈ O₁ * ψ₁ + @test convert(TensorMap, mpo₃ * mps₁) ≈ O₃ * ψ₁ + @test mpo₃ * ψ₁ ≈ O₃ * ψ₁ + @test convert(TensorMap, mpo₁ * mps₂) ≈ O₁ * ψ₂ + @test mpo₁ * ψ₂ ≈ O₁ * ψ₂ @test dot(mps₁, mpo₁, mps₁) ≈ dot(ψ₁, O₁, ψ₁) @test dot(mps₁, mpo₁, mps₁) ≈ dot(mps₁, mpo₁ * mps₁) diff --git a/test/states.jl b/test/states.jl index 13e27fbf..4a7f1fee 100644 --- a/test/states.jl +++ b/test/states.jl @@ -46,13 +46,13 @@ end ψ_small2 = FiniteMPS(convert(TensorMap, ψ_small)) @test dot(ψ_small, ψ_small2) ≈ dot(ψ_small, ψ_small) - ψ′ = @constinferred complex(ψ) + ψ′ = @constinferred complex(ψ_small) @test scalartype(ψ′) <: Complex if elt <: Complex - @test ψ === ψ′ + @test ψ_small === ψ′ else - @test norm(ψ) ≈ norm(ψ′) - @test complex(convert(TensorMap, ψ)) ≈ convert(TensorMap, ψ′) + @test norm(ψ_small) ≈ norm(ψ′) + @test complex(convert(TensorMap, ψ_small)) ≈ convert(TensorMap, ψ′) end end From 2d11995f8ff12c54e2fa6419a0b4d15ee71892a8 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 11:02:02 -0500 Subject: [PATCH 10/14] convert to complex states in TDVP --- src/algorithms/timestep/tdvp.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/algorithms/timestep/tdvp.jl b/src/algorithms/timestep/tdvp.jl index 11550359..88fee929 100644 --- a/src/algorithms/timestep/tdvp.jl +++ b/src/algorithms/timestep/tdvp.jl @@ -25,9 +25,10 @@ $(TYPEDFIELDS) finalize::F = Defaults._finalize end -function timestep(ψ::InfiniteMPS, H, t::Number, dt::Number, alg::TDVP, - envs::AbstractMPSEnvironments=environments(ψ, H); +function timestep(ψ_::InfiniteMPS, H, t::Number, dt::Number, alg::TDVP, + envs::AbstractMPSEnvironments=environments(ψ_, H); leftorthflag=true) + ψ = complex(ψ_) temp_ACs = similar(ψ.AC) temp_Cs = similar(ψ.C) @@ -172,5 +173,5 @@ end function timestep(ψ::AbstractFiniteMPS, H, time::Number, timestep::Number, alg::Union{TDVP,TDVP2}, envs::AbstractMPSEnvironments=environments(ψ, H); kwargs...) - return timestep!(copy(ψ), H, time, timestep, alg, envs; kwargs...) + return timestep!(copy(complex(ψ)), H, time, timestep, alg, envs; kwargs...) end From cbdd30c26eb6bf1cc817153d1ab0098cadd42c31 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 16:45:59 -0500 Subject: [PATCH 11/14] Add `complex(::WindowMPS)` --- src/states/windowmps.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/states/windowmps.jl b/src/states/windowmps.jl index ad2f9a7d..147b1d29 100644 --- a/src/states/windowmps.jl +++ b/src/states/windowmps.jl @@ -118,6 +118,13 @@ function Base.copy(ψ::WindowMPS) return WindowMPS(copy(ψ.left_gs), copy(ψ.window), copy(ψ.right_gs)) end +function Base.complex(ψ::WindowMPS) + scalartype(ψ) <: Complex && return ψ + left_gs = complex(ψ.left_gs) + right_gs = ψ.left_gs === ψ.right_gs ? left_gs : complex(right_gs) + return WindowMPS(left_gs, complex(ψ.window), right_gs) +end + # not sure about the underlying methods... Base.length(ψ::WindowMPS) = length(ψ.window) Base.size(ψ::WindowMPS, i...) = size(ψ.window, i...) From 23bdc7061c8f134dc69cf05c2ec86a9871aef1bb Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 17:00:27 -0500 Subject: [PATCH 12/14] Add `complex(::InfiniteMPS)` --- src/states/infinitemps.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/states/infinitemps.jl b/src/states/infinitemps.jl index 9c756183..d4f33cb2 100644 --- a/src/states/infinitemps.jl +++ b/src/states/infinitemps.jl @@ -224,6 +224,11 @@ function Base.copy!(ψ::InfiniteMPS, ϕ::InfiniteMPS) return ψ end +function Base.complex(ψ::InfiniteMPS) + scalartype(ψ) <: Complex && return ψ + return InfiniteMPS(complex.(ψ.AL), complex.(ψ.AR), complex.(ψ.C), complex.(ψ.AC)) +end + function Base.repeat(ψ::InfiniteMPS, i::Int) return InfiniteMPS(repeat(ψ.AL, i), repeat(ψ.AR, i), repeat(ψ.C, i), repeat(ψ.AC, i)) end From 4d1af8eb14e599e82ce0187a414f825db4933092 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 28 Feb 2025 18:02:44 -0500 Subject: [PATCH 13/14] Add `complex(::LazySum)` --- src/operators/lazysum.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/operators/lazysum.jl b/src/operators/lazysum.jl index 8fe584ea..7bb2b5ce 100644 --- a/src/operators/lazysum.jl +++ b/src/operators/lazysum.jl @@ -24,6 +24,8 @@ Base.length(x::LazySum) = prod(size(x)) Base.similar(x::LazySum, ::Type{S}, dims::Dims) where {S} = LazySum(similar(x.ops, S, dims)) Base.setindex!(A::LazySum, X, i::Int) = (setindex!(A.ops, X, i); A) +Base.complex(x::LazySum) = LazySum(complex.(x.ops)) + # Holy traits TimeDependence(x::LazySum) = istimed(x) ? TimeDependent() : NotTimeDependent() istimed(x::LazySum) = any(istimed, x) From 858446eb404456f652019358bac825d2aafd6d35 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Mon, 3 Mar 2025 08:07:44 -0500 Subject: [PATCH 14/14] fix multiplication of MPOHamiltonian --- src/operators/mpohamiltonian.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 5a5dbfb7..d57c4e5e 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -533,7 +533,11 @@ function VectorInterface.scale!(dst::MPOHamiltonian, src::MPOHamiltonian, isstarting = I[1] == 1 && ((isfinite(dst) && i == N && I[4] == size(src[i], 4)) || ((!isfinite(dst) || i != N) && I[4] > 1)) - dst[i][I] = scale!(dst[i][I], v, isstarting ? λ : One()) + if v isa BraidingTensor && !isstarting + dst[i][I] = v + else + dst[i][I] = scale!(dst[i][I], v, isstarting ? λ : One()) + end end end return dst