From a724420c53737a1431518825cca1c917c3966b3c Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 25 Mar 2025 17:30:06 +0100 Subject: [PATCH 01/54] add_util_leg spinoff for MPOHamiltonian --- src/utility/utility.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/utility/utility.jl b/src/utility/utility.jl index 9f1ab093f..686b5b0dc 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -65,6 +65,18 @@ function add_util_leg(tensor::AbstractTensorMap{T,S,N1,N2}) where {T,S,N1,N2} return util_front * tensor * util_back end +function add_util_mpoleg(tensor::AbstractTensorMap{T,S,N1,N2}) where {T,S,N1,N2} + # separate function for mpo because add_util_leg is also used for mps from tensors + # and the additional legs there can be different depending on the fusion tree + + ou = Vect[sectortype(_firstspace(tensor))](one(first(sectors(_firstspace(tensor)))) => 1) + + util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) + util_back = isomorphism(storagetype(tensor), domain(tensor), domain(tensor) * ou) + + return util_front * tensor * util_back +end + function union_split(a::AbstractArray) T = reduce((a, b) -> Union{a,b}, typeof.(a)) nA = similar(a, T) From eeefc5821906816fffbbdebd799a1d2bee907f26 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 25 Mar 2025 17:30:27 +0100 Subject: [PATCH 02/54] apply add_util_mpoleg to correlator --- src/algorithms/correlators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index a5dfbfdf5..ea66bd86d 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -39,6 +39,6 @@ end function correlator(state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any,S,2,2}, i::Int, j) where {S} - O₁, O₂ = decompose_localmpo(add_util_leg(O₁₂)) + O₁, O₂ = decompose_localmpo(add_util_mpoleg(O₁₂)) return correlator(state, O₁, O₂, i, j) end From 909e45326f9be9c36a01160910aa4ec0968b1ffc Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 25 Mar 2025 17:32:31 +0100 Subject: [PATCH 03/54] use add_util_mpoleg in FiniteMPO --- src/operators/mpo.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index af59c3578..e5c6ee77d 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -27,7 +27,7 @@ function FiniteMPO(Os::AbstractVector{O}) where {O} end function FiniteMPO(O::AbstractTensorMap{T,S,N,N}) where {T,S,N} - return FiniteMPO(decompose_localmpo(add_util_leg(O))) + return FiniteMPO(decompose_localmpo(add_util_mpoleg(O))) end """ From 03d40016d07ed5389ae05b3d6a79a312c58b997d Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 25 Mar 2025 17:32:53 +0100 Subject: [PATCH 04/54] use add_util_mpoleg in test on correlator --- test/algorithms.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/algorithms.jl b/test/algorithms.jl index dcf05e883..b71005220 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -674,7 +674,7 @@ end @test expectation_value(ψ, H) ≈ expectation_value(ψ, 1 => -g * S_x()) + expectation_value(ψ, (1, 2) => -S_zz()) - Z_mpo = MPSKit.add_util_leg(S_z()) + Z_mpo = MPSKit.add_util_mpoleg(S_z()) G = correlator(ψ, Z_mpo, Z_mpo, 1, 2:5) G2 = correlator(ψ, S_zz(), 1, 3:2:5) @test isapprox(G[2], G2[1], atol=1e-2) From 2abc2ffef91b924ae5177a8196b9ac122ecde4a4 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 26 Mar 2025 17:28:42 +0100 Subject: [PATCH 05/54] avoid `oneunit(S)` in one constructor of `InfiniteMPOHamiltonian` --- src/operators/mpohamiltonian.jl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index aef1c8d98..85cd1b026 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -469,9 +469,11 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, operator_size = maximum(K -> maximum(last, K; init=1) + 1, nonzero_keys) virtualspaces = PeriodicArray([Vector{MissingS}(missing, operator_size) for _ in 1:length(nonzero_keys)]) + + __oneunit = _oneunit(local_operators[1]) for V in virtualspaces - V[1] = oneunit(S) - V[end] = oneunit(S) + V[1] = __oneunit + V[end] = __oneunit end # start by filling in tensors -> space information available @@ -517,7 +519,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, end end - foreach(Base.Fix2(replace!, missing => oneunit(S)), virtualspaces) + foreach(Base.Fix2(replace!, missing => __oneunit), virtualspaces) virtualsumspaces = map(virtualspaces) do V return SumSpace(collect(S, V)) end @@ -548,6 +550,12 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, return InfiniteMPOHamiltonian(PeriodicArray(Os)) end +function _oneunit(localop::LocalOperator) # determine relevant unit without calling oneunit(S) + sp = space(localop.opp[1],1) + unit = one(collect(sectors(sp))[1]) + return Vect[sectortype(sp)](unit=>1) +end + function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_operators::Pair...) return FiniteMPOHamiltonian(lattice, local_operators) From 822d7282c70d372ab99e1a71241b8995a66c9394 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 26 Mar 2025 17:30:21 +0100 Subject: [PATCH 06/54] avoid `oneunit(S)` in one constructor of `FiniteMPOHamiltonian` --- src/operators/mpohamiltonian.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 85cd1b026..6fb395748 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -380,13 +380,15 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, E = scalartype(T) S = spacetype(T) + __oneunit = _oneunit(local_operators[1]) + virtualspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) - virtualspaces[1] = SumSpace(oneunit(S)) - virtualspaces[end] = SumSpace(oneunit(S)) + virtualspaces[1] = SumSpace(__oneunit) + virtualspaces[end] = SumSpace(__oneunit) for i in 1:(length(lattice) - 1) n_channels = maximum(last, nonzero_keys[i]; init=1) + 1 - V = SumSpace(fill(oneunit(S), n_channels)) + V = SumSpace(fill(__oneunit, n_channels)) if n_channels > 2 for ((key_L, key_R), O) in zip(nonzero_keys[i], nonzero_opps[i]) V[key_R == 0 ? end : key_R] = if O isa Number From 4c1345e6227a4eaef3a49abe6a5ec8b5da240795 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 26 Mar 2025 17:56:38 +0100 Subject: [PATCH 07/54] remove type `LocalOperator` in `_oneunit` --- src/operators/mpohamiltonian.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 6fb395748..c1f7daeb8 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -552,7 +552,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, return InfiniteMPOHamiltonian(PeriodicArray(Os)) end -function _oneunit(localop::LocalOperator) # determine relevant unit without calling oneunit(S) +function _oneunit(localop) # determine relevant unit without calling oneunit(S) sp = space(localop.opp[1],1) unit = one(collect(sectors(sp))[1]) return Vect[sectortype(sp)](unit=>1) From 9b569d4319bf8a7ce1cdcee8879a9107618f5462 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 9 Apr 2025 14:56:59 +0200 Subject: [PATCH 08/54] remove braidings in `left/right_excitation_transfer_system` of `InfiniteMPOHamiltonian` --- .../excitation/exci_transfer_system.jl | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index c489783c6..907a1aa59 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -13,6 +13,8 @@ function left_excitation_transfer_system(lBs, H, exci; mom=exci.momentum, T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isid(H, i) + # not sure what H can be here + # also, what is isid? @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * r_RL(exci.right_gs)[2; 3] * τ[3 4; 5 1] * @@ -57,11 +59,14 @@ function left_excitation_transfer_system(GBL, H::InfiniteMPOHamiltonian, exci; T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isidentitylevel(H, i) - @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * - r_RL(exci.right_gs)[2; 3] * - τ[3 4; 5 1] * - l_RL(exci.right_gs)[-1; 6] * - τ[5 6; -4 -2] + # not using braiding tensors here, leads to extra leg + util = similar(exci.left_gs.AL[1], space(parent(H)[1],1)[1]) + fill_data!(util, one) + @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * + util[1] * + r_RL(exci.right_gs)[3; 2] * + l_RL(exci.right_gs)[-1; -4] * + conj(util[-2]) end found[i] = add!(start, GBL[i]) @@ -70,7 +75,10 @@ function left_excitation_transfer_system(GBL, H::InfiniteMPOHamiltonian, exci; if isidentitylevel(H, i) T = TransferMatrix(exci.right_gs.AR, exci.left_gs.AL) if exci.trivial - T = regularize(T, l_RL(exci.right_gs), r_RL(exci.right_gs)) + # deal with extra leg + @plansor lRL_util[-1 -2; -3] := l_RL(exci.right_gs)[-1;-3] * conj(util[-2]) + @plansor rRL_util[-1 -2; -3] := r_RL(exci.right_gs)[-1;-3] * util[-2] + T = regularize(T, lRL_util, rRL_util) end else T = TransferMatrix(exci.right_gs.AR, map(h -> h[i, 1, 1, i], parent(H)), @@ -126,6 +134,7 @@ function right_excitation_transfer_system(rBs, H, exci; mom=exci.momentum, end return found end +#TODO: check that the left/right discrepancies make sense function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; mom=exci.momentum, solver=Defaults.linearsolver) @@ -142,9 +151,14 @@ function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; T = TransferMatrix(exci.left_gs.AL, H_partial, exci.right_gs.AR) start = scale!(first(T * found[i:odim]), cis(mom * len)) if exci.trivial && isidentitylevel(H, i) - @plansor start[-1 -2; -3 -4] -= τ[6 2; 3 4] * start[3 4; -3 5] * - l_LR(exci.right_gs)[5; 2] * - r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] + # not using braiding tensors here, leads to extra leg + util = similar(exci.right_gs.AL[1], space(parent(H)[1],1)[1]) + fill_data!(util, one) + @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * + conj(util[1]) * + l_LR(exci.right_gs)[3; 2] * + R_LR(exci.right_gs)[-1; -4] * + util[-2] end found[i] = add!(start, GBR[i]) @@ -153,7 +167,10 @@ function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; if isidentitylevel(H, i) tm = TransferMatrix(exci.left_gs.AL, exci.right_gs.AR) if exci.trivial - tm = regularize(tm, l_LR(exci.left_gs), r_LR(exci.right_gs)) + # deal with extra leg + @plansor lLR_util[-1 -2; -3] := l_LR(exci.left_gs)[-1;-3] * conj(util[-2]) + @plansor rLR_util[-1 -2; -3] := r_LR(exci.right_gs)[-1;-3] * util[-2] + tm = regularize(tm, lLR_util, rLR_util) end else tm = TransferMatrix(exci.left_gs.AL, map(h -> h[i, 1, 1, i], parent(H)), From 8de04433001bb33ba1606e57c632e294896fc7e7 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 9 Apr 2025 15:09:01 +0200 Subject: [PATCH 09/54] typo in `right_excitation_transfer_system` --- src/algorithms/excitation/exci_transfer_system.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 907a1aa59..d4506c0a0 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -157,7 +157,7 @@ function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * conj(util[1]) * l_LR(exci.right_gs)[3; 2] * - R_LR(exci.right_gs)[-1; -4] * + r_LR(exci.right_gs)[-1; -4] * util[-2] end From 5fbd8319b908aedf435d7d4b51e0733aa0b722d3 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 11 Apr 2025 17:43:38 +0200 Subject: [PATCH 10/54] rewrite `(In)FiniteMPOHamiltonian` and the way `oneunit` was avoided --- src/operators/mpohamiltonian.jl | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index c1f7daeb8..d8df54fee 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -380,15 +380,19 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, E = scalartype(T) S = spacetype(T) - __oneunit = _oneunit(local_operators[1]) + # avoid using one(S) + somempo = local_mpos[1].second[1] + sp_oneleg = space(somempo, 1) + + _oneunit = oneunit(sp_oneleg) virtualspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) - virtualspaces[1] = SumSpace(__oneunit) - virtualspaces[end] = SumSpace(__oneunit) + virtualspaces[1] = SumSpace(_oneunit) + virtualspaces[end] = SumSpace(_oneunit) for i in 1:(length(lattice) - 1) n_channels = maximum(last, nonzero_keys[i]; init=1) + 1 - V = SumSpace(fill(__oneunit, n_channels)) + V = SumSpace(fill(_oneunit, n_channels)) if n_channels > 2 for ((key_L, key_R), O) in zip(nonzero_keys[i], nonzero_opps[i]) V[key_R == 0 ? end : key_R] = if O isa Number @@ -472,10 +476,14 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, virtualspaces = PeriodicArray([Vector{MissingS}(missing, operator_size) for _ in 1:length(nonzero_keys)]) - __oneunit = _oneunit(local_operators[1]) + + somempo = local_mpos[1].second[1] + sp_oneleg = space(somempo, 1) + + _oneunit = oneunit(sp_oneleg) for V in virtualspaces - V[1] = __oneunit - V[end] = __oneunit + V[1] = _oneunit + V[end] = _oneunit end # start by filling in tensors -> space information available @@ -521,7 +529,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, end end - foreach(Base.Fix2(replace!, missing => __oneunit), virtualspaces) + foreach(Base.Fix2(replace!, missing => _oneunit), virtualspaces) virtualsumspaces = map(virtualspaces) do V return SumSpace(collect(S, V)) end @@ -552,12 +560,6 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, return InfiniteMPOHamiltonian(PeriodicArray(Os)) end -function _oneunit(localop) # determine relevant unit without calling oneunit(S) - sp = space(localop.opp[1],1) - unit = one(collect(sectors(sp))[1]) - return Vect[sectortype(sp)](unit=>1) -end - function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_operators::Pair...) return FiniteMPOHamiltonian(lattice, local_operators) From c28a9c1ec1b39018e825fbd09c0f9b17d2b04b1d Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 18 Apr 2025 09:33:41 +0200 Subject: [PATCH 11/54] avoid `oneunit(spacetype(H))` evaluation --- src/algorithms/ED.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index 36c112630..a9b0d7d1b 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -43,7 +43,7 @@ function exact_diagonalization(H::FiniteMPOHamiltonian; # fuse from left to right ALs = Vector{Union{Missing,TA}}(missing, L) - left = oneunit(spacetype(H)) + left = spacetype(H)(one(sector) => 1) # might need to be rightone, leave this for now for i in 1:(middle_site - 1) P = physicalspace(H, i) ALs[i] = isomorphism(T, left ⊗ P ← fuse(left ⊗ P)) From 37cecd389afad78ee9d8a71f34fc0d078f5a57c8 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 18 Apr 2025 12:31:26 +0200 Subject: [PATCH 12/54] super important typo --- src/algorithms/groundstate/gradient_grassmann.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/groundstate/gradient_grassmann.jl b/src/algorithms/groundstate/gradient_grassmann.jl index 0e44c86a1..583797532 100644 --- a/src/algorithms/groundstate/gradient_grassmann.jl +++ b/src/algorithms/groundstate/gradient_grassmann.jl @@ -56,7 +56,7 @@ function find_groundstate(ψ::S, H, alg::GradientGrassmann, envs::P=environments(ψ, H))::Tuple{S,P,Float64} where {S,P} !isa(ψ, FiniteMPS) || dim(ψ.C[end]) == 1 || - @warn "This is not fully supported - split the mps up in a sum of mps's and optimize seperately" + @warn "This is not fully supported - split the mps up in a sum of mps's and optimize separately" normalize!(ψ) fg(x) = GrassmannMPS.fg(x, H, envs) From f584d2b30988374bb87aa219b0892918a5fcbc41 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 10 Jun 2025 15:53:01 +0200 Subject: [PATCH 13/54] remove comments --- src/algorithms/excitation/exci_transfer_system.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index d4506c0a0..c6cf1fe98 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -13,8 +13,6 @@ function left_excitation_transfer_system(lBs, H, exci; mom=exci.momentum, T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isid(H, i) - # not sure what H can be here - # also, what is isid? @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * r_RL(exci.right_gs)[2; 3] * τ[3 4; 5 1] * From f8f6c2c5ca53fa9ba4017585f7c817932c4290b4 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 11 Jun 2025 10:43:23 +0200 Subject: [PATCH 14/54] get `return_type` in `excitations` to evaluate the `sector` kwarg --- .../excitation/quasiparticleexcitation.jl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index 2c0170b5f..a338a744b 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -46,7 +46,6 @@ end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; num::Int=1) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) H_eff = EffectiveExcitationHamiltonian(H, lenvs, renvs, E) - Es, ϕs, convhist = eigsolve(ϕ₀, num, :SR, alg.alg) do ϕ return H_eff(ϕ; alg.alg_environments...) end @@ -103,11 +102,19 @@ function excitations(H, alg::QuasiparticleAnsatz, momenta, lmps, renvs=lmps === rmps ? lenvs : environments(rmps, H); verbosity=Defaults.verbosity, num=1, sector=one(sectortype(lmps)), parallel=true, kwargs...) - Toutput = Core.Compiler.return_type(excitations, - Tuple{typeof(H),typeof(alg), - eltype(momenta),typeof(lmps), - typeof(lenvs), - typeof(rmps),typeof(renvs)}) + # wrapper to evaluate sector as positional argument + Toutput = let + function wrapper(H, alg, p, lmps, lenvs, rmps, renvs, sector; kwargs...) + return excitations(H, alg, p, lmps, lenvs, rmps, renvs; + sector, kwargs...) + end + Core.Compiler.return_type(wrapper, + Tuple{typeof(H),typeof(alg), + eltype(momenta),typeof(lmps), + typeof(lenvs), + typeof(rmps),typeof(renvs),typeof(sector)}) + end + results = similar(momenta, Toutput) scheduler = parallel ? :greedy : :serial tmap!(results, momenta; scheduler) do momentum From 889873add14d815327c5bdca07c727c26deb3a08 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 17 Jul 2025 12:39:55 +0200 Subject: [PATCH 15/54] typo --- src/algorithms/fixedpoint.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/algorithms/fixedpoint.jl b/src/algorithms/fixedpoint.jl index 81e7c69d9..188c65559 100644 --- a/src/algorithms/fixedpoint.jl +++ b/src/algorithms/fixedpoint.jl @@ -4,14 +4,14 @@ fixedpoint(A, x₀, which::Symbol; kwargs...) -> val, vec fixedpoint(A, x₀, which::Symbol, alg) -> val, vec -Compute the fixedpoint of a linear operator `A` using the specified eigensolver `alg`. The +Compute the fixed point of a linear operator `A` using the specified eigensolver `alg`. The fixedpoint is assumed to be unique. """ function fixedpoint(A, x₀, which::Symbol, alg::Lanczos) vals, vecs, info = eigsolve(A, x₀, 1, which, alg) if info.converged == 0 - @warnv 1 "fixedpoint not converged after $(info.numiter) iterations: normres = $(info.normres[1])" + @warnv 1 "fixed point not converged after $(info.numiter) iterations: normres = $(info.normres[1])" end return vals[1], vecs[1] @@ -21,10 +21,10 @@ function fixedpoint(A, x₀, which::Symbol, alg::Arnoldi) TT, vecs, vals, info = schursolve(A, x₀, 1, which, alg) if info.converged == 0 - @warnv 1 "fixedpoint not converged after $(info.numiter) iterations: normres = $(info.normres[1])" + @warnv 1 "fixed point not converged after $(info.numiter) iterations: normres = $(info.normres[1])" end if size(TT, 2) > 1 && TT[2, 1] != 0 - @warnv 1 "non-unique fixedpoint detected" + @warnv 1 "non-unique fixed point detected" end return vals[1], vecs[1] From 94d4d9da010a7ea00d9a923c7f07b241e76d41b8 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 24 Jul 2025 16:15:16 -0400 Subject: [PATCH 16/54] manipulate `oneunit`s in addition of `MPOHamiltonian`s --- src/operators/mpohamiltonian.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 4c13b59a6..19353132d 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -666,7 +666,7 @@ function Base.:+(H₁::FiniteMPOHamiltonian{O}, H₂::FiniteMPOHamiltonian{O}) where {O<:JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(spacetype(H₁)) + Vtriv = oneunit(space(first(H₁[1]), 1)) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims=(1, 4)) @@ -688,7 +688,7 @@ function Base.:+(H₁::InfiniteMPOHamiltonian{O}, H₂::InfiniteMPOHamiltonian{O}) where {O<:JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(spacetype(H₁)) + Vtriv = oneunit(space(first(H₁[1]), 1)) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims=(1, 4)) B = cat(H₁[i].B, H₂[i].B; dims=1) From 22d01d0efe0dcd17e88bb14f8ea0721619bba82f Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 25 Jul 2025 17:48:05 -0400 Subject: [PATCH 17/54] format --- src/algorithms/ED.jl | 2 +- src/algorithms/correlators.jl | 6 ++-- .../excitation/exci_transfer_system.jl | 28 ++++++++------- .../excitation/quasiparticleexcitation.jl | 34 ++++++++++++------- src/operators/mpohamiltonian.jl | 4 +-- src/utility/utility.jl | 2 +- 6 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index aecc8c41a..d1c86a745 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -43,7 +43,7 @@ function exact_diagonalization( TA = tensormaptype(spacetype(H), 2, 1, T) # fuse from left to right - ALs = Vector{Union{Missing,TA}}(missing, L) + ALs = Vector{Union{Missing, TA}}(missing, L) left = spacetype(H)(one(sector) => 1) # might need to be rightone, leave this for now for i in 1:(middle_site - 1) P = physicalspace(H, i) diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index 23024376f..8660d08d7 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -38,8 +38,10 @@ function correlator( return G end -function correlator(state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any,S,2,2}, i::Int, - j) where {S} +function correlator( + state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any, S, 2, 2}, i::Int, + j + ) where {S} O₁, O₂ = decompose_localmpo(add_util_mpoleg(O₁₂)) return correlator(state, O₁, O₂, i, j) end diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index d28d739df..8515a7f6a 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -60,13 +60,13 @@ function left_excitation_transfer_system( start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg - util = similar(exci.left_gs.AL[1], space(parent(H)[1],1)[1]) + util = similar(exci.left_gs.AL[1], space(parent(H)[1], 1)[1]) fill_data!(util, one) @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * - util[1] * - r_RL(exci.right_gs)[3; 2] * - l_RL(exci.right_gs)[-1; -4] * - conj(util[-2]) + util[1] * + r_RL(exci.right_gs)[3; 2] * + l_RL(exci.right_gs)[-1; -4] * + conj(util[-2]) end found[i] = add!(start, GBL[i]) @@ -139,9 +139,11 @@ function right_excitation_transfer_system( return found end #TODO: check that the left/right discrepancies make sense -function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; - mom=exci.momentum, - solver=Defaults.linearsolver) +function right_excitation_transfer_system( + GBR, H::InfiniteMPOHamiltonian, exci; + mom = exci.momentum, + solver = Defaults.linearsolver + ) len = length(H) found = zerovector(GBR) odim = length(GBR) @@ -156,13 +158,13 @@ function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; start = scale!(first(T * found[i:odim]), cis(mom * len)) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg - util = similar(exci.right_gs.AL[1], space(parent(H)[1],1)[1]) + util = similar(exci.right_gs.AL[1], space(parent(H)[1], 1)[1]) fill_data!(util, one) @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * - conj(util[1]) * - l_LR(exci.right_gs)[3; 2] * - r_LR(exci.right_gs)[-1; -4] * - util[-2] + conj(util[1]) * + l_LR(exci.right_gs)[3; 2] * + r_LR(exci.right_gs)[-1; -4] * + util[-2] end found[i] = add!(start, GBR[i]) diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index 78f5d5552..3f9ece592 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -97,22 +97,30 @@ function excitations( ϕ₀ = LeftGaugedQP(rand, lmps, rmps; sector, momentum) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) end -function excitations(H, alg::QuasiparticleAnsatz, momenta, lmps, - lenvs=environments(lmps, H), rmps=lmps, - renvs=lmps === rmps ? lenvs : environments(rmps, H); - verbosity=Defaults.verbosity, num=1, - sector=one(sectortype(lmps)), parallel=true, kwargs...) - # wrapper to evaluate sector as positional argument +function excitations( + H, alg::QuasiparticleAnsatz, momenta, lmps, + lenvs = environments(lmps, H), rmps = lmps, + renvs = lmps === rmps ? lenvs : environments(rmps, H); + verbosity = Defaults.verbosity, num = 1, + sector = one(sectortype(lmps)), parallel = true, kwargs... + ) + # wrapper to evaluate sector as positional argument Toutput = let function wrapper(H, alg, p, lmps, lenvs, rmps, renvs, sector; kwargs...) - return excitations(H, alg, p, lmps, lenvs, rmps, renvs; - sector, kwargs...) + return excitations( + H, alg, p, lmps, lenvs, rmps, renvs; + sector, kwargs... + ) end - Core.Compiler.return_type(wrapper, - Tuple{typeof(H),typeof(alg), - eltype(momenta),typeof(lmps), - typeof(lenvs), - typeof(rmps),typeof(renvs),typeof(sector)}) + Core.Compiler.return_type( + wrapper, + Tuple{ + typeof(H), typeof(alg), + eltype(momenta), typeof(lmps), + typeof(lenvs), + typeof(rmps), typeof(renvs), typeof(sector), + } + ) end results = similar(momenta, Toutput) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index b2996696e..1574cd2da 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -388,7 +388,7 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_opera somempo = local_mpos[1].second[1] sp_oneleg = space(somempo, 1) - _oneunit = oneunit(sp_oneleg) + _oneunit = oneunit(sp_oneleg) virtualsumspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) virtualsumspaces[1] = SumSpace(fill(_oneunit, 1)) @@ -478,7 +478,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ somempo = local_mpos[1].second[1] sp_oneleg = space(somempo, 1) - _oneunit = oneunit(sp_oneleg) + _oneunit = oneunit(sp_oneleg) for V in virtualspaces V[1] = _oneunit V[end] = _oneunit diff --git a/src/utility/utility.jl b/src/utility/utility.jl index f4e8efca9..d5d736da3 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -69,7 +69,7 @@ function add_util_leg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, return util_front * tensor * util_back end -function add_util_mpoleg(tensor::AbstractTensorMap{T,S,N1,N2}) where {T,S,N1,N2} +function add_util_mpoleg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, N2} # separate function for mpo because add_util_leg is also used for mps from tensors # and the additional legs there can be different depending on the fusion tree From 1983070976c70f85551ca8ac2ff14fecd9d498fe Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Mon, 25 Aug 2025 10:18:16 +0200 Subject: [PATCH 18/54] simplify expression --- src/utility/utility.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utility/utility.jl b/src/utility/utility.jl index d5d736da3..de86f7d8a 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -73,7 +73,8 @@ function add_util_mpoleg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N # separate function for mpo because add_util_leg is also used for mps from tensors # and the additional legs there can be different depending on the fusion tree - ou = Vect[sectortype(_firstspace(tensor))](one(first(sectors(_firstspace(tensor)))) => 1) + #TODO?: might need a left and right utility space if MPO has module virtual space (doesn't happen for hamiltonians) + ou = S(one(first(blocksectors(tensor))) => 1) util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) util_back = isomorphism(storagetype(tensor), domain(tensor), domain(tensor) * ou) From f6578b672dc56d849cafbdd22c76069de216f000 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Mon, 25 Aug 2025 10:18:31 +0200 Subject: [PATCH 19/54] add comments --- src/operators/mpohamiltonian.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 1574cd2da..842335862 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -388,7 +388,7 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_opera somempo = local_mpos[1].second[1] sp_oneleg = space(somempo, 1) - _oneunit = oneunit(sp_oneleg) + _oneunit = oneunit(sp_oneleg) # should be rightoneunit, but MPOHamiltonians are always diagonal for now virtualsumspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) virtualsumspaces[1] = SumSpace(fill(_oneunit, 1)) @@ -669,7 +669,7 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(space(first(H₁[1]), 1)) + Vtriv = oneunit(space(first(H₁[1]), 1)) # should also be rightoneunit, but is currently diagonal for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) From cf1154769f45dc351fecb8a97acdfd3bcb47b99a Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Mon, 25 Aug 2025 12:10:15 +0200 Subject: [PATCH 20/54] get `infinite_temperature_density_matrix` working --- src/algorithms/toolbox.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index dff979e65..80e4c5235 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -28,7 +28,7 @@ Return the density matrix of the infinite temperature state for a given Hamilton This is the identity matrix in the physical space, and the identity in the auxiliary space. """ function infinite_temperature_density_matrix(H::MPOHamiltonian) - V = oneunit(spacetype(H)) + V = oneunit(space(first(H[1]), 1)) W = map(1:length(H)) do site return BraidingTensor{scalartype(H)}(physicalspace(H, site), V) end From f86dee491eafda3b344b58e3097b5a7b12ce451f Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 26 Aug 2025 17:18:06 +0200 Subject: [PATCH 21/54] generalise identifying space in `infinite_temperature_density_matrix` --- src/algorithms/toolbox.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 80e4c5235..cd4566254 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -28,7 +28,7 @@ Return the density matrix of the infinite temperature state for a given Hamilton This is the identity matrix in the physical space, and the identity in the auxiliary space. """ function infinite_temperature_density_matrix(H::MPOHamiltonian) - V = oneunit(space(first(H[1]), 1)) + V = first(left_virtualspace(H[1])) W = map(1:length(H)) do site return BraidingTensor{scalartype(H)}(physicalspace(H, site), V) end From 967f68db3a159bb22bf88207a1c91b43fb85f777 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 09:17:11 +0200 Subject: [PATCH 22/54] make util spaces generic in `left/right_excitation_transfer_system` --- src/algorithms/excitation/exci_transfer_system.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 8515a7f6a..1e715996d 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -1,3 +1,4 @@ +# is this function ever called? b/c isid no longer exists function left_excitation_transfer_system( lBs, H, exci; mom = exci.momentum, solver = Defaults.linearsolver @@ -60,7 +61,7 @@ function left_excitation_transfer_system( start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg - util = similar(exci.left_gs.AL[1], space(parent(H)[1], 1)[1]) + util = similar(exci.left_gs.AL[i], first(left_virtualspace(H[i]))) fill_data!(util, one) @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * util[1] * @@ -95,6 +96,7 @@ function left_excitation_transfer_system( end return found end +# same with this? function right_excitation_transfer_system( rBs, H, exci; mom = exci.momentum, solver = Defaults.linearsolver ) @@ -138,7 +140,7 @@ function right_excitation_transfer_system( end return found end -#TODO: check that the left/right discrepancies make sense + function right_excitation_transfer_system( GBR, H::InfiniteMPOHamiltonian, exci; mom = exci.momentum, @@ -158,7 +160,7 @@ function right_excitation_transfer_system( start = scale!(first(T * found[i:odim]), cis(mom * len)) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg - util = similar(exci.right_gs.AL[1], space(parent(H)[1], 1)[1]) + util = similar(exci.right_gs.AL[i], first(left_virtualspace(H[i]))) fill_data!(util, one) @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * conj(util[1]) * From 2114cdc8650ac93f01ce5f81a60e33973ee1e390 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 09:30:19 +0200 Subject: [PATCH 23/54] remove braiding in `InfiniteQP` environments + rename variables for consistency --- src/environments/qp_envs.jl | 59 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/environments/qp_envs.jl b/src/environments/qp_envs.jl index acc9fa9f3..35bdb1ba7 100644 --- a/src/environments/qp_envs.jl +++ b/src/environments/qp_envs.jl @@ -63,10 +63,14 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; TransferMatrix(exci[pos], H[pos], AL[pos]) / cis(exci.momentum) if exci.trivial # regularization of trivial excitations + util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) + fill_data!(util, one) for i in ids - @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][1 4; -3 2] * - r_RL(exci.left_gs, pos)[2; 3] * τ[3 4; 5 1] * - l_RL(exci.left_gs, pos + 1)[ -1; 6 ] * τ[5 6; -4 -2] + @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][2 1; -3 3] * + util[1] * + r_RL(exci.left_gs, pos)[3; 2] * + l_RL(exci.left_gs, pos + 1)[-1; -4] * + conj(util[-2]) end end end @@ -79,11 +83,14 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; rightenv(renvs, pos, exci.right_gs) * cis(exci.momentum) if exci.trivial + util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) # can these utils be refactored? + fill_data!(util, one) for i in ids - ρ_left = l_LR(exci.left_gs, pos) - ρ_right = r_LR(exci.left_gs, pos - 1) - @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= τ[6 4; 1 3] * - rBs[pos - 1][i][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] + @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= rBs[pos - 1][i][2 1; -3 3] * + conj(util[1]) * + l_LR(exci.left_gs, pos)[3; 2] * + r_LR(exci.left_gs, pos - 1)[-1; -4] * + util[-2] end end end @@ -98,35 +105,41 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; end lB_cur = lBs[1] - for i in 1:(length(exci) - 1) - lB_cur = lB_cur * TransferMatrix(AR[i], H[i], AL[i]) / cis(exci.momentum) + for pos in 1:(length(exci) - 1) + lB_cur = lB_cur * TransferMatrix(AR[pos], H[pos], AL[pos]) / cis(exci.momentum) if exci.trivial - for k in ids - ρ_left = l_RL(exci.left_gs, i + 1) - ρ_right = r_RL(exci.left_gs, i) - @plansor lB_cur[k][-1 -2; -3 -4] -= lB_cur[k][1 4; -3 2] * - ρ_right[2; 3] * τ[3 4; 5 1] * ρ_left[-1; 6] * τ[5 6; -4 -2] + util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) + fill_data!(util, one) + for i in ids + @plansor lB_cur[i][-1 -2; -3 -4] -= lB_cur[i][2 1; -3 3] * + util[1] * + r_RL(exci.left_gs, pos)[3; 2] * + l_RL(exci.left_gs, pos + 1)[-1; -4] * + conj(util[-2]) end end - lBs[i + 1] += lB_cur + lBs[pos + 1] += lB_cur end rB_cur = rBs[end] - for i in length(exci):-1:2 - rB_cur = TransferMatrix(AL[i], H[i], AR[i]) * rB_cur * cis(exci.momentum) + for pos in length(exci):-1:2 + rB_cur = TransferMatrix(AL[pos], H[pos], AR[pos]) * rB_cur * cis(exci.momentum) if exci.trivial - for k in ids - ρ_left = l_LR(exci.left_gs, i) - ρ_right = r_LR(exci.left_gs, i - 1) - @plansor rB_cur[k][-1 -2; -3 -4] -= τ[6 4; 1 3] * - rB_cur[k][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] + util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) + fill_data!(util, one) + for i in ids + @plansor rB_cur[i][-1 -2; -3 -4] -= rB_cur[i][2 1; -3 3] * + conj(util[1]) * + l_LR(exci.left_gs, pos)[3; 2] * + r_LR(exci.left_gs, pos - 1)[-1; -4] * + util[-2] end end - rBs[i - 1] += rB_cur + rBs[pos - 1] += rB_cur end return InfiniteQPEnvironments(lBs, rBs, lenvs, renvs) From 4435fc2c7111f9f489cc13cf98d054182639f729 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 11:32:35 +0200 Subject: [PATCH 24/54] Revert "remove braiding in `InfiniteQP` environments + rename variables for consistency" This reverts commit 2114cdc8650ac93f01ce5f81a60e33973ee1e390. --- src/environments/qp_envs.jl | 59 +++++++++++++++---------------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/environments/qp_envs.jl b/src/environments/qp_envs.jl index 35bdb1ba7..acc9fa9f3 100644 --- a/src/environments/qp_envs.jl +++ b/src/environments/qp_envs.jl @@ -63,14 +63,10 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; TransferMatrix(exci[pos], H[pos], AL[pos]) / cis(exci.momentum) if exci.trivial # regularization of trivial excitations - util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) - fill_data!(util, one) for i in ids - @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][2 1; -3 3] * - util[1] * - r_RL(exci.left_gs, pos)[3; 2] * - l_RL(exci.left_gs, pos + 1)[-1; -4] * - conj(util[-2]) + @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][1 4; -3 2] * + r_RL(exci.left_gs, pos)[2; 3] * τ[3 4; 5 1] * + l_RL(exci.left_gs, pos + 1)[ -1; 6 ] * τ[5 6; -4 -2] end end end @@ -83,14 +79,11 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; rightenv(renvs, pos, exci.right_gs) * cis(exci.momentum) if exci.trivial - util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) # can these utils be refactored? - fill_data!(util, one) for i in ids - @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= rBs[pos - 1][i][2 1; -3 3] * - conj(util[1]) * - l_LR(exci.left_gs, pos)[3; 2] * - r_LR(exci.left_gs, pos - 1)[-1; -4] * - util[-2] + ρ_left = l_LR(exci.left_gs, pos) + ρ_right = r_LR(exci.left_gs, pos - 1) + @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= τ[6 4; 1 3] * + rBs[pos - 1][i][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] end end end @@ -105,41 +98,35 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; end lB_cur = lBs[1] - for pos in 1:(length(exci) - 1) - lB_cur = lB_cur * TransferMatrix(AR[pos], H[pos], AL[pos]) / cis(exci.momentum) + for i in 1:(length(exci) - 1) + lB_cur = lB_cur * TransferMatrix(AR[i], H[i], AL[i]) / cis(exci.momentum) if exci.trivial - util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) - fill_data!(util, one) - for i in ids - @plansor lB_cur[i][-1 -2; -3 -4] -= lB_cur[i][2 1; -3 3] * - util[1] * - r_RL(exci.left_gs, pos)[3; 2] * - l_RL(exci.left_gs, pos + 1)[-1; -4] * - conj(util[-2]) + for k in ids + ρ_left = l_RL(exci.left_gs, i + 1) + ρ_right = r_RL(exci.left_gs, i) + @plansor lB_cur[k][-1 -2; -3 -4] -= lB_cur[k][1 4; -3 2] * + ρ_right[2; 3] * τ[3 4; 5 1] * ρ_left[-1; 6] * τ[5 6; -4 -2] end end - lBs[pos + 1] += lB_cur + lBs[i + 1] += lB_cur end rB_cur = rBs[end] - for pos in length(exci):-1:2 - rB_cur = TransferMatrix(AL[pos], H[pos], AR[pos]) * rB_cur * cis(exci.momentum) + for i in length(exci):-1:2 + rB_cur = TransferMatrix(AL[i], H[i], AR[i]) * rB_cur * cis(exci.momentum) if exci.trivial - util = similar(exci.left_gs.AL[pos], physicalspace(H[pos])) - fill_data!(util, one) - for i in ids - @plansor rB_cur[i][-1 -2; -3 -4] -= rB_cur[i][2 1; -3 3] * - conj(util[1]) * - l_LR(exci.left_gs, pos)[3; 2] * - r_LR(exci.left_gs, pos - 1)[-1; -4] * - util[-2] + for k in ids + ρ_left = l_LR(exci.left_gs, i) + ρ_right = r_LR(exci.left_gs, i - 1) + @plansor rB_cur[k][-1 -2; -3 -4] -= τ[6 4; 1 3] * + rB_cur[k][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] end end - rBs[pos - 1] += rB_cur + rBs[i - 1] += rB_cur end return InfiniteQPEnvironments(lBs, rBs, lenvs, renvs) From 309254d6e458dd4936a4b88602c37371b2f7a9ed Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 16:55:44 +0200 Subject: [PATCH 25/54] add multifusion tests --- test/multifusion.jl | 143 ++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 3 + 2 files changed, 146 insertions(+) create mode 100644 test/multifusion.jl diff --git a/test/multifusion.jl b/test/multifusion.jl new file mode 100644 index 000000000..49eb258dd --- /dev/null +++ b/test/multifusion.jl @@ -0,0 +1,143 @@ +println(" +----------------- +| Multifusion | +----------------- +") + +module TestMultifusion + +using Test, TestExtras +using MPSKit +using TensorKit +using Random +using BlockTensorKit + +I = IsingBimodule + +M = I(1, 2, 0) # σ +Mop = I(2, 1, 0) +C0 = I(1, 1, 0) # unit of C +C1 = I(1, 1, 1) +D0 = I(2, 2, 0) # unit of D +D1 = I(2, 2, 1) +V = Vect[I](M => 1) +Vop = Vect[I](Mop => 1) +PD = Vect[I](D0 => 1, D1 => 1) +PC = Vect[I](C0 => 1, C1 => 1) + +bad_fusions = [(PC, PD), (PD, PC), (V, V), (Vop, Vop), (V, PC), (Vop, PD), (V, PD), (Vop, PC), (V, Vop), (Vop, V)] + +flippy(charge::IsingBimodule) = only(charge ⊗ D1) + +function TFIM_multifusion(::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite=false) where {T <: Number} + P = Vect[I](D0 => 1, D1 => 1) + X = zeros(T, P ← P) + for (s, f) in fusiontrees(X) + isone(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g + end + ZZ = zeros(T, P^2 ← P^2) + for (s, f) in fusiontrees(ZZ) + s.uncoupled == map(flippy, f.uncoupled) ? ZZ[s, f] .= 1 : nothing + end + + if L == Inf + lattice = twosite ? PeriodicArray([P, P]) : PeriodicArray([P]) + H₁ = InfiniteMPOHamiltonian(lattice, i => X for i in 1:length(lattice)) + H₂ = InfiniteMPOHamiltonian(lattice, (i, i+1) => ZZ for i in 1:length(lattice)) + else + lattice = fill(P, L) + H₁ = FiniteMPOHamiltonian(lattice, i => X for i in 1:L) + H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:(L - 1)) + end + return H₁ + H₂ +end + +@testset "InfiniteMPS construction" begin + for (P, V) in bad_fusions + @test_throws ArgumentError("invalid fusion channel") InfiniteMPS([P], [V]) + end +end + +@testset "FiniteMPS construction" begin + Prand, Vrand = rand(bad_fusions) + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") FiniteMPS(2, Prand, Vrand) + + for (P, V) in bad_fusions + @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V; left=V, right=V) + end +end + +@testset "Exact diagonalization" begin + H = TFIM_multifusion(; g = 0, L = 4) + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") exact_diagonalization(H) + + E, ψ = exact_diagonalization(H; sector=D0) # test that it runs with kwarg + @test isapprox(E, [-3, -1, 1, 3]; atol=1e-6) +end + +@testset "Finite systems" begin + L = 6 + g = 1.5 + H = TFIM_multifusion(; g = g, L = L) + V = Vect[I](M => 8) + init = FiniteMPS(L, PD, V; left=Vect[I](M => 1), right=Vect[I](M => 1)) + ψ, envs = find_groundstate(init, H, DMRG()) + E = expectation_value(ψ, H, envs) + + ψ2, envs2 = find_groundstate(init, H, DMRG2(;trscheme=truncbelow(1e-6))) + E2 = expectation_value(ψ2, H, envs2) + + @test isapprox(E, E2; atol=1e-6) + + ED, _ = exact_diagonalization(H; sector=D0) + @test isapprox(E, first(ED); atol=1e-6) + + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) + # excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C0, num=10) # testing sector kwarg #TODO: get this working + # @test variance(qp[1], H) < 1e-8 +end + +@testset "Infinite systems" begin + # Multifusion: effectively studying the KW dual in SSB phase + g = 1 / 1.5 + H = TFIM_multifusion(; g = g, L = Inf, twosite=true) + V = Vect[I](M => 20) + init = InfiniteMPS([PD, PD], [V, V]) + ψ, envs = find_groundstate(init, H, IDMRG()) + E = expectation_value(ψ, H, envs) + + ψ2, envs2 = find_groundstate(init, H, IDMRG2(; trscheme=truncbelow(1e-9))) + E2 = expectation_value(ψ2, H, envs2) + + ψ3, envs3 = find_groundstate(init, H, VUMPS()) + E3 = expectation_value(ψ3, H, envs3) + + @test isapprox(E, E2; atol=1e-6) + @test isapprox(E, E3; atol=1e-6) + + @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) + @test first(transfer_spectrum(ψ2; sector=C0)) ≈ 1 # testing sector kwarg + + @test only(keys(entanglement_spectrum(ψ2))) == M + + momentum = 0 + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) + excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), 0, ψ3; sector=C0); # testing sector kwarg + excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), 0, ψ3; sector=C1); + @test isapprox(first(excC1), abs(2*(g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase + # @test 0 < variance(qpC0[1], H) < 1e-8 # TODO: fix braiding thing + + # diagonal test (M = D): injective GS in symmetric phase + Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite=true) + Vdiag = Vect[I](D0 => 10, D1 => 10) + initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) + gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS()) + Ediag = expectation_value(gsdiag, Hdual, envsdiag) + # @test isapprox(E2, Ediag; atol=1e-6) + excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), 0, gsdiag; sector=D0); + excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), 0, gsdiag; sector=D1); + @test isapprox(first(excD1), abs(2*(1/g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase + # @test 0 < variance(qpD0[1], Hdual) < 1e-8 # TODO: fix braiding thing +end + +end # module TestMultifusion \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 556b26dec..2d0e6adb4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,6 +21,9 @@ include("setup.jl") if GROUP == "ALL" || GROUP == "ALGORITHMS" @time include("algorithms.jl") end + if GROUP == "ALL" || GROUP == "MULTIFUSION" + @time include("multifusion.jl") + end if GROUP == "ALL" || GROUP == "OTHER" @time include("other.jl") end From 02a944afab076634a257a0578c245b4a206a7ce5 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 16:56:21 +0200 Subject: [PATCH 26/54] actually use my variable everywhere --- test/multifusion.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index 49eb258dd..e5016d049 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -122,8 +122,8 @@ end momentum = 0 @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) - excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), 0, ψ3; sector=C0); # testing sector kwarg - excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), 0, ψ3; sector=C1); + excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector=C0) # testing sector kwarg + excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector=C1) @test isapprox(first(excC1), abs(2*(g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase # @test 0 < variance(qpC0[1], H) < 1e-8 # TODO: fix braiding thing @@ -134,8 +134,8 @@ end gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS()) Ediag = expectation_value(gsdiag, Hdual, envsdiag) # @test isapprox(E2, Ediag; atol=1e-6) - excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), 0, gsdiag; sector=D0); - excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), 0, gsdiag; sector=D1); + excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector=D0) + excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector=D1) @test isapprox(first(excD1), abs(2*(1/g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase # @test 0 < variance(qpD0[1], Hdual) < 1e-8 # TODO: fix braiding thing end From 75a6a1bf5e3af440b9474514a46da20a0d36752b Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 18:05:52 +0200 Subject: [PATCH 27/54] add Z2 Ising model --- test/setup.jl | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/setup.jl b/test/setup.jl index 09a18a83f..1292f2623 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -94,6 +94,14 @@ function S_x(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) w throw(ArgumentError("spin $spin not supported")) end end +function S_x(::Type{Z2Irrep} = Z2Irrep, ::Type{T} = ComplexF64; spin=1 // 2) where {T <: Number} + spin == 1 // 2 || throw(ArgumentError("spin $spin not supported")) + pspace = Z2Space(0 => 1, 1 => 1) + X = zeros(T, pspace, pspace) + block(X, Z2Irrep(0)) .= one(T) # no times 2 + block(X, Z2Irrep(1)) .= -one(T) + return X +end function S_y(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return if spin == 1 // 2 TensorMap(T[0 -im; im 0], ℂ^2 ← ℂ^2) @@ -121,8 +129,35 @@ end function S_zz(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return S_z(Trivial, T; spin) ⊗ S_z(Trivial, T; spin) end +function S_zz(::Type{Z2Irrep} = Z2Irrep, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} + P = Z2Space(0 => 1, 1 => 1) + ZZ = TensorMap(zeros, ComplexF64, P ⊗ P ← P ⊗ P) + flip_charge(charge::Z2Irrep) = only(charge ⊗ Z2Irrep(1)) + for (s, f) in fusiontrees(ZZ) + if s.uncoupled == map(flip_charge, f.uncoupled) + ZZ[s, f] .= 1 # no divide by 4 + end + end + return ZZ +end + +function transverse_field_ising(::Type{Z2Irrep}, ::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite=false) where {T <: Number} + X = S_x(Z2Irrep, T; spin = 1 // 2) + ZZ = S_zz(Z2Irrep, T; spin = 1 // 2) + + if L == Inf + lattice = twosite ? PeriodicArray([space(X, 1), space(X, 1)]) : PeriodicArray([space(X, 1)]) + H₁ = InfiniteMPOHamiltonian(lattice, i => -g * X for i in 1:length(lattice)) + H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:length(lattice)) + else + lattice = fill(space(X, 1), L) + H₁ = FiniteMPOHamiltonian(lattice, i => -g * X for i in 1:L) + H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:(L - 1)) + end + return H₁ + H₂ +end -function transverse_field_ising(::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} +function transverse_field_ising(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} X = S_x(Trivial, T; spin = 1 // 2) ZZ = S_zz(Trivial, T; spin = 1 // 2) From 43bbdd9b6741eb7601b0bde0ec010c41d124e97d Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 27 Aug 2025 18:06:52 +0200 Subject: [PATCH 28/54] compare to Z2 Ising model + add injectivity check --- test/multifusion.jl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index e5016d049..f4543f25a 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -117,6 +117,7 @@ end @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) @test first(transfer_spectrum(ψ2; sector=C0)) ≈ 1 # testing sector kwarg + @test !(abs(first(transfer_spectrum(ψ2; sector=C1))) ≈ 1) # testing injectivity @test only(keys(entanglement_spectrum(ψ2))) == M @@ -133,11 +134,23 @@ end initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS()) Ediag = expectation_value(gsdiag, Hdual, envsdiag) - # @test isapprox(E2, Ediag; atol=1e-6) excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector=D0) excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector=D1) @test isapprox(first(excD1), abs(2*(1/g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase # @test 0 < variance(qpD0[1], Hdual) < 1e-8 # TODO: fix braiding thing + + # comparison to Z2 Ising: injective in symmetric phase + HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite=true) + VZ2 = Z2Space(0 => 10, 1 => 10) + PZ2 = Z2Space(0 => 1, 1 => 1) + initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) + gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS()) + EZ2 = expectation_value(gsZ2, HZ2, envsZ2) + @test isapprox(EZ2, Ediag; atol=1e-6) + excZ2_0, qpZ2_0 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector=Z2Irrep(0)) + excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector=Z2Irrep(1)) + @test isapprox(first(excZ2_1), first(excD1); atol=1e-5) + @test isapprox(first(excZ2_0), first(excD0); atol=1e-5) end end # module TestMultifusion \ No newline at end of file From 3930db315519397f4662984410a13572a18bd066 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 28 Aug 2025 08:37:07 +0200 Subject: [PATCH 29/54] remove defaulting to `Z2Irrep` for new operators --- test/setup.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/setup.jl b/test/setup.jl index 1292f2623..ce0039198 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -94,7 +94,7 @@ function S_x(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) w throw(ArgumentError("spin $spin not supported")) end end -function S_x(::Type{Z2Irrep} = Z2Irrep, ::Type{T} = ComplexF64; spin=1 // 2) where {T <: Number} +function S_x(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin=1 // 2) where {T <: Number} spin == 1 // 2 || throw(ArgumentError("spin $spin not supported")) pspace = Z2Space(0 => 1, 1 => 1) X = zeros(T, pspace, pspace) @@ -129,7 +129,7 @@ end function S_zz(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return S_z(Trivial, T; spin) ⊗ S_z(Trivial, T; spin) end -function S_zz(::Type{Z2Irrep} = Z2Irrep, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} +function S_zz(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} P = Z2Space(0 => 1, 1 => 1) ZZ = TensorMap(zeros, ComplexF64, P ⊗ P ← P ⊗ P) flip_charge(charge::Z2Irrep) = only(charge ⊗ Z2Irrep(1)) From 0aa00add87d93e9fc18f07a8e724b3fb9147b6aa Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 28 Aug 2025 08:37:47 +0200 Subject: [PATCH 30/54] fix kitaev model term (even if it's not used) --- test/setup.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/setup.jl b/test/setup.jl index ce0039198..661673f21 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -302,7 +302,7 @@ function kitaev_model(; t = 1.0, mu = 1.0, Delta = 1.0, L = Inf) else lattice = fill(space(TB, 1), L) onsite_terms = ((i,) => CP for i in 1:L) - twosite_terms = ((i, i + 1) => TP + SC for i in 1:(L - 1)) + twosite_terms = ((i, i + 1) => TB + SC for i in 1:(L - 1)) terms = Iterators.flatten(twosite_terms, onsite_terms) return FiniteMPOHamiltonian(lattice, terms) end From 4b975ad261c08c8640b84554bf20bb2f00f6941a Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 28 Aug 2025 08:40:08 +0200 Subject: [PATCH 31/54] format --- test/multifusion.jl | 280 ++++++++++++++++++++++---------------------- test/setup.jl | 4 +- 2 files changed, 142 insertions(+), 142 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index f4543f25a..0a9b9a147 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -6,151 +6,151 @@ println(" module TestMultifusion -using Test, TestExtras -using MPSKit -using TensorKit -using Random -using BlockTensorKit - -I = IsingBimodule - -M = I(1, 2, 0) # σ -Mop = I(2, 1, 0) -C0 = I(1, 1, 0) # unit of C -C1 = I(1, 1, 1) -D0 = I(2, 2, 0) # unit of D -D1 = I(2, 2, 1) -V = Vect[I](M => 1) -Vop = Vect[I](Mop => 1) -PD = Vect[I](D0 => 1, D1 => 1) -PC = Vect[I](C0 => 1, C1 => 1) - -bad_fusions = [(PC, PD), (PD, PC), (V, V), (Vop, Vop), (V, PC), (Vop, PD), (V, PD), (Vop, PC), (V, Vop), (Vop, V)] - -flippy(charge::IsingBimodule) = only(charge ⊗ D1) - -function TFIM_multifusion(::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite=false) where {T <: Number} - P = Vect[I](D0 => 1, D1 => 1) - X = zeros(T, P ← P) - for (s, f) in fusiontrees(X) - isone(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g + using Test, TestExtras + using MPSKit + using TensorKit + using Random + using BlockTensorKit + + I = IsingBimodule + + M = I(1, 2, 0) # σ + Mop = I(2, 1, 0) + C0 = I(1, 1, 0) # unit of C + C1 = I(1, 1, 1) + D0 = I(2, 2, 0) # unit of D + D1 = I(2, 2, 1) + V = Vect[I](M => 1) + Vop = Vect[I](Mop => 1) + PD = Vect[I](D0 => 1, D1 => 1) + PC = Vect[I](C0 => 1, C1 => 1) + + bad_fusions = [(PC, PD), (PD, PC), (V, V), (Vop, Vop), (V, PC), (Vop, PD), (V, PD), (Vop, PC), (V, Vop), (Vop, V)] + + flippy(charge::IsingBimodule) = only(charge ⊗ D1) + + function TFIM_multifusion(::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite = false) where {T <: Number} + P = Vect[I](D0 => 1, D1 => 1) + X = zeros(T, P ← P) + for (s, f) in fusiontrees(X) + isone(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g + end + ZZ = zeros(T, P^2 ← P^2) + for (s, f) in fusiontrees(ZZ) + s.uncoupled == map(flippy, f.uncoupled) ? ZZ[s, f] .= 1 : nothing + end + + if L == Inf + lattice = twosite ? PeriodicArray([P, P]) : PeriodicArray([P]) + H₁ = InfiniteMPOHamiltonian(lattice, i => X for i in 1:length(lattice)) + H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:length(lattice)) + else + lattice = fill(P, L) + H₁ = FiniteMPOHamiltonian(lattice, i => X for i in 1:L) + H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:(L - 1)) + end + return H₁ + H₂ end - ZZ = zeros(T, P^2 ← P^2) - for (s, f) in fusiontrees(ZZ) - s.uncoupled == map(flippy, f.uncoupled) ? ZZ[s, f] .= 1 : nothing + + @testset "InfiniteMPS construction" begin + for (P, V) in bad_fusions + @test_throws ArgumentError("invalid fusion channel") InfiniteMPS([P], [V]) + end end - - if L == Inf - lattice = twosite ? PeriodicArray([P, P]) : PeriodicArray([P]) - H₁ = InfiniteMPOHamiltonian(lattice, i => X for i in 1:length(lattice)) - H₂ = InfiniteMPOHamiltonian(lattice, (i, i+1) => ZZ for i in 1:length(lattice)) - else - lattice = fill(P, L) - H₁ = FiniteMPOHamiltonian(lattice, i => X for i in 1:L) - H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:(L - 1)) + + @testset "FiniteMPS construction" begin + Prand, Vrand = rand(bad_fusions) + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") FiniteMPS(2, Prand, Vrand) + + for (P, V) in bad_fusions + @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V; left = V, right = V) + end end - return H₁ + H₂ -end -@testset "InfiniteMPS construction" begin - for (P, V) in bad_fusions - @test_throws ArgumentError("invalid fusion channel") InfiniteMPS([P], [V]) + @testset "Exact diagonalization" begin + H = TFIM_multifusion(; g = 0, L = 4) + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") exact_diagonalization(H) + + E, ψ = exact_diagonalization(H; sector = D0) # test that it runs with kwarg + @test isapprox(E, [-3, -1, 1, 3]; atol = 1.0e-6) end -end -@testset "FiniteMPS construction" begin - Prand, Vrand = rand(bad_fusions) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") FiniteMPS(2, Prand, Vrand) + @testset "Finite systems" begin + L = 6 + g = 1.5 + H = TFIM_multifusion(; g = g, L = L) + V = Vect[I](M => 8) + init = FiniteMPS(L, PD, V; left = Vect[I](M => 1), right = Vect[I](M => 1)) + ψ, envs = find_groundstate(init, H, DMRG()) + E = expectation_value(ψ, H, envs) + + ψ2, envs2 = find_groundstate(init, H, DMRG2(; trscheme = truncbelow(1.0e-6))) + E2 = expectation_value(ψ2, H, envs2) - for (P, V) in bad_fusions - @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V; left=V, right=V) + @test isapprox(E, E2; atol = 1.0e-6) + + ED, _ = exact_diagonalization(H; sector = D0) + @test isapprox(E, first(ED); atol = 1.0e-6) + + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) + # excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C0, num=10) # testing sector kwarg #TODO: get this working + # @test variance(qp[1], H) < 1e-8 end -end - -@testset "Exact diagonalization" begin - H = TFIM_multifusion(; g = 0, L = 4) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") exact_diagonalization(H) - - E, ψ = exact_diagonalization(H; sector=D0) # test that it runs with kwarg - @test isapprox(E, [-3, -1, 1, 3]; atol=1e-6) -end - -@testset "Finite systems" begin - L = 6 - g = 1.5 - H = TFIM_multifusion(; g = g, L = L) - V = Vect[I](M => 8) - init = FiniteMPS(L, PD, V; left=Vect[I](M => 1), right=Vect[I](M => 1)) - ψ, envs = find_groundstate(init, H, DMRG()) - E = expectation_value(ψ, H, envs) - - ψ2, envs2 = find_groundstate(init, H, DMRG2(;trscheme=truncbelow(1e-6))) - E2 = expectation_value(ψ2, H, envs2) - - @test isapprox(E, E2; atol=1e-6) - - ED, _ = exact_diagonalization(H; sector=D0) - @test isapprox(E, first(ED); atol=1e-6) - - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) - # excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C0, num=10) # testing sector kwarg #TODO: get this working - # @test variance(qp[1], H) < 1e-8 -end - -@testset "Infinite systems" begin - # Multifusion: effectively studying the KW dual in SSB phase - g = 1 / 1.5 - H = TFIM_multifusion(; g = g, L = Inf, twosite=true) - V = Vect[I](M => 20) - init = InfiniteMPS([PD, PD], [V, V]) - ψ, envs = find_groundstate(init, H, IDMRG()) - E = expectation_value(ψ, H, envs) - - ψ2, envs2 = find_groundstate(init, H, IDMRG2(; trscheme=truncbelow(1e-9))) - E2 = expectation_value(ψ2, H, envs2) - - ψ3, envs3 = find_groundstate(init, H, VUMPS()) - E3 = expectation_value(ψ3, H, envs3) - - @test isapprox(E, E2; atol=1e-6) - @test isapprox(E, E3; atol=1e-6) - - @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) - @test first(transfer_spectrum(ψ2; sector=C0)) ≈ 1 # testing sector kwarg - @test !(abs(first(transfer_spectrum(ψ2; sector=C1))) ≈ 1) # testing injectivity - - @test only(keys(entanglement_spectrum(ψ2))) == M - - momentum = 0 - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) - excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector=C0) # testing sector kwarg - excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector=C1) - @test isapprox(first(excC1), abs(2*(g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase - # @test 0 < variance(qpC0[1], H) < 1e-8 # TODO: fix braiding thing - - # diagonal test (M = D): injective GS in symmetric phase - Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite=true) - Vdiag = Vect[I](D0 => 10, D1 => 10) - initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) - gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS()) - Ediag = expectation_value(gsdiag, Hdual, envsdiag) - excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector=D0) - excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector=D1) - @test isapprox(first(excD1), abs(2*(1/g - 1)); atol=1e-6) # charged excitation higher in energy in symmetric phase - # @test 0 < variance(qpD0[1], Hdual) < 1e-8 # TODO: fix braiding thing - - # comparison to Z2 Ising: injective in symmetric phase - HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite=true) - VZ2 = Z2Space(0 => 10, 1 => 10) - PZ2 = Z2Space(0 => 1, 1 => 1) - initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) - gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS()) - EZ2 = expectation_value(gsZ2, HZ2, envsZ2) - @test isapprox(EZ2, Ediag; atol=1e-6) - excZ2_0, qpZ2_0 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector=Z2Irrep(0)) - excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector=Z2Irrep(1)) - @test isapprox(first(excZ2_1), first(excD1); atol=1e-5) - @test isapprox(first(excZ2_0), first(excD0); atol=1e-5) -end - -end # module TestMultifusion \ No newline at end of file + + @testset "Infinite systems" begin + # Multifusion: effectively studying the KW dual in SSB phase + g = 1 / 1.5 + H = TFIM_multifusion(; g = g, L = Inf, twosite = true) + V = Vect[I](M => 20) + init = InfiniteMPS([PD, PD], [V, V]) + ψ, envs = find_groundstate(init, H, IDMRG()) + E = expectation_value(ψ, H, envs) + + ψ2, envs2 = find_groundstate(init, H, IDMRG2(; trscheme = truncbelow(1.0e-9))) + E2 = expectation_value(ψ2, H, envs2) + + ψ3, envs3 = find_groundstate(init, H, VUMPS()) + E3 = expectation_value(ψ3, H, envs3) + + @test isapprox(E, E2; atol = 1.0e-6) + @test isapprox(E, E3; atol = 1.0e-6) + + @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) + @test first(transfer_spectrum(ψ2; sector = C0)) ≈ 1 # testing sector kwarg + @test !(abs(first(transfer_spectrum(ψ2; sector = C1))) ≈ 1) # testing injectivity + + @test only(keys(entanglement_spectrum(ψ2))) == M + + momentum = 0 + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) + excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C0) # testing sector kwarg + excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) + @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation higher in energy in symmetric phase + # @test 0 < variance(qpC0[1], H) < 1e-8 # TODO: fix braiding thing + + # diagonal test (M = D): injective GS in symmetric phase + Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite = true) + Vdiag = Vect[I](D0 => 10, D1 => 10) + initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) + gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS()) + Ediag = expectation_value(gsdiag, Hdual, envsdiag) + excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D0) + excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D1) + @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation higher in energy in symmetric phase + # @test 0 < variance(qpD0[1], Hdual) < 1e-8 # TODO: fix braiding thing + + # comparison to Z2 Ising: injective in symmetric phase + HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) + VZ2 = Z2Space(0 => 10, 1 => 10) + PZ2 = Z2Space(0 => 1, 1 => 1) + initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) + gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS()) + EZ2 = expectation_value(gsZ2, HZ2, envsZ2) + @test isapprox(EZ2, Ediag; atol = 1.0e-6) + excZ2_0, qpZ2_0 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(0)) + excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(1)) + @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-5) + @test isapprox(first(excZ2_0), first(excD0); atol = 1.0e-5) + end + +end # module TestMultifusion diff --git a/test/setup.jl b/test/setup.jl index 661673f21..fc2848944 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -94,7 +94,7 @@ function S_x(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) w throw(ArgumentError("spin $spin not supported")) end end -function S_x(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin=1 // 2) where {T <: Number} +function S_x(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} spin == 1 // 2 || throw(ArgumentError("spin $spin not supported")) pspace = Z2Space(0 => 1, 1 => 1) X = zeros(T, pspace, pspace) @@ -141,7 +141,7 @@ function S_zz(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin = 1 // 2) where {T < return ZZ end -function transverse_field_ising(::Type{Z2Irrep}, ::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite=false) where {T <: Number} +function transverse_field_ising(::Type{Z2Irrep}, ::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite = false) where {T <: Number} X = S_x(Z2Irrep, T; spin = 1 // 2) ZZ = S_zz(Z2Irrep, T; spin = 1 // 2) From 769dbde820b5b68692ed86dfab8730f02b986517 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 28 Aug 2025 08:59:29 +0200 Subject: [PATCH 32/54] actually run the multifusion test --- .github/workflows/Tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index 0b8e27800..b51cf1d75 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -29,6 +29,7 @@ jobs: - states - operators - algorithms + - multifusion - other os: - ubuntu-latest From b31ffa1f0597d6445be9285cb20c15275739365d Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 29 Aug 2025 11:40:29 +0200 Subject: [PATCH 33/54] import TestSetup module, remove unused deps + add variance tests --- test/multifusion.jl | 51 +++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index 0a9b9a147..040a570dd 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -6,11 +6,10 @@ println(" module TestMultifusion + using ..TestSetup using Test, TestExtras using MPSKit using TensorKit - using Random - using BlockTensorKit I = IsingBimodule @@ -93,27 +92,39 @@ module TestMultifusion @test isapprox(E, first(ED); atol = 1.0e-6) @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) - # excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C0, num=10) # testing sector kwarg #TODO: get this working - # @test variance(qp[1], H) < 1e-8 + excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C0, num=10) # testing sector kwarg + @test 0 < variance(qp[1], H) < 1e-8 end - @testset "Infinite systems" begin + # @testset "Infinite systems" begin # Multifusion: effectively studying the KW dual in SSB phase - g = 1 / 1.5 + g = 1 / 4 H = TFIM_multifusion(; g = g, L = Inf, twosite = true) - V = Vect[I](M => 20) + V = Vect[I](M => 48) init = InfiniteMPS([PD, PD], [V, V]) - ψ, envs = find_groundstate(init, H, IDMRG()) + v₀ = variance(init, H) + tol = 1.0e-10 + ψ, envs, δ = find_groundstate(init, H, IDMRG(;tol=tol,maxiter=400)) E = expectation_value(ψ, H, envs) + v = variance(ψ, H) - ψ2, envs2 = find_groundstate(init, H, IDMRG2(; trscheme = truncbelow(1.0e-9))) + ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol=tol, trscheme = truncbelow(1.0e-6), maxiter=400)) E2 = expectation_value(ψ2, H, envs2) + v2 = variance(ψ2, H) - ψ3, envs3 = find_groundstate(init, H, VUMPS()) + ψ3, envs3, δ3 = find_groundstate(init, H, VUMPS(;tol=tol, maxiter=400)) E3 = expectation_value(ψ3, H, envs3) + v3 = variance(ψ3, H) @test isapprox(E, E2; atol = 1.0e-6) @test isapprox(E, E3; atol = 1.0e-6) + for delta in [δ, δ2, δ3] + @test delta ≈ 0 atol=1e-3 + end + for var in [v, v2, v3] + @test var < v₀ + @test 0 < var < 1e-8 + end @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) @test first(transfer_spectrum(ψ2; sector = C0)) ≈ 1 # testing sector kwarg @@ -125,32 +136,32 @@ module TestMultifusion @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C0) # testing sector kwarg excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) - @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation higher in energy in symmetric phase - # @test 0 < variance(qpC0[1], H) < 1e-8 # TODO: fix braiding thing + @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy + @test variance(qpC1[1], H) < 1e-8 #TODO: figure out why this is negative # diagonal test (M = D): injective GS in symmetric phase Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite = true) - Vdiag = Vect[I](D0 => 10, D1 => 10) + Vdiag = Vect[I](D0 => 24, D1 => 24) initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) - gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS()) + gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS(;tol=tol, maxiter=400)) Ediag = expectation_value(gsdiag, Hdual, envsdiag) excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D0) excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D1) - @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation higher in energy in symmetric phase - # @test 0 < variance(qpD0[1], Hdual) < 1e-8 # TODO: fix braiding thing + @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation lower in energy + @test variance(qpD1[1], Hdual) < 1e-8 # TODO: is this ever negative? # comparison to Z2 Ising: injective in symmetric phase HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) - VZ2 = Z2Space(0 => 10, 1 => 10) + VZ2 = Z2Space(0 => 24, 1 => 24) PZ2 = Z2Space(0 => 1, 1 => 1) initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) - gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS()) + gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS(;tol=tol, maxiter=400)) EZ2 = expectation_value(gsZ2, HZ2, envsZ2) @test isapprox(EZ2, Ediag; atol = 1.0e-6) excZ2_0, qpZ2_0 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(0)) excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(1)) @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-5) - @test isapprox(first(excZ2_0), first(excD0); atol = 1.0e-5) - end + @test variance(qpZ2_1[1], HZ2) < 1e-6 #TODO: and this? + # end end # module TestMultifusion From 03dbee3d43414a694b85a242d7d141bc6679c56f Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 29 Aug 2025 11:41:24 +0200 Subject: [PATCH 34/54] specify allowed spin for Z2 `S_zz` + don't use deprecated call --- test/setup.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/setup.jl b/test/setup.jl index fc2848944..7a5390827 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -130,8 +130,9 @@ function S_zz(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) return S_z(Trivial, T; spin) ⊗ S_z(Trivial, T; spin) end function S_zz(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} + spin == 1 // 2 || throw(ArgumentError("spin $spin not supported")) P = Z2Space(0 => 1, 1 => 1) - ZZ = TensorMap(zeros, ComplexF64, P ⊗ P ← P ⊗ P) + ZZ = zeros(ComplexF64, P ⊗ P ← P ⊗ P) flip_charge(charge::Z2Irrep) = only(charge ⊗ Z2Irrep(1)) for (s, f) in fusiontrees(ZZ) if s.uncoupled == map(flip_charge, f.uncoupled) From a17874ab1a82e3d33f22fd6d349820462d57a4f5 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 29 Aug 2025 12:41:26 +0200 Subject: [PATCH 35/54] add variance tests for `FiniteMPS` + allow it to be negative (?) + add `FiniteExcited` test --- test/multifusion.jl | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index 040a570dd..a855d3c0f 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -76,27 +76,37 @@ module TestMultifusion @testset "Finite systems" begin L = 6 - g = 1.5 + g = 4 H = TFIM_multifusion(; g = g, L = L) V = Vect[I](M => 8) init = FiniteMPS(L, PD, V; left = Vect[I](M => 1), right = Vect[I](M => 1)) - ψ, envs = find_groundstate(init, H, DMRG()) + v₀ = variance(init, H) + ψ, envs, δ = find_groundstate(init, H, DMRG()) + v = variance(ψ, H) E = expectation_value(ψ, H, envs) - ψ2, envs2 = find_groundstate(init, H, DMRG2(; trscheme = truncbelow(1.0e-6))) + ψ2, envs2, δ2 = find_groundstate(init, H, DMRG2(; trscheme = truncbelow(1.0e-6))) + v2 = variance(ψ2, H) E2 = expectation_value(ψ2, H, envs2) + @test δ ≈ 0 atol=1e-3 + @test δ2 ≈ 0 atol=1e-3 + @test v < v₀ && v2 < v₀ + @test isapprox(E, E2; atol = 1.0e-6) ED, _ = exact_diagonalization(H; sector = D0) @test isapprox(E, first(ED); atol = 1.0e-6) @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) - excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C0, num=10) # testing sector kwarg + excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C1, num=1) # testing sector kwarg @test 0 < variance(qp[1], H) < 1e-8 + + excE_DM, qp_DM = excitations(H, FiniteExcited(;gsalg=DMRG2(; trscheme=truncbelow(1e-6))), ψ2;num=1) + @test isapprox(first(excE_DM), first(excE) + E2; atol = 1.0e-6) end - # @testset "Infinite systems" begin + @testset "Infinite systems" begin # Multifusion: effectively studying the KW dual in SSB phase g = 1 / 4 H = TFIM_multifusion(; g = g, L = Inf, twosite = true) @@ -123,7 +133,7 @@ module TestMultifusion end for var in [v, v2, v3] @test var < v₀ - @test 0 < var < 1e-8 + @test var < 1e-8 end @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) @@ -134,10 +144,9 @@ module TestMultifusion momentum = 0 @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) - excC0, qpC0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C0) # testing sector kwarg excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test variance(qpC1[1], H) < 1e-8 #TODO: figure out why this is negative + @test variance(qpC1[1], H) < 1e-8 # diagonal test (M = D): injective GS in symmetric phase Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite = true) @@ -145,10 +154,10 @@ module TestMultifusion initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS(;tol=tol, maxiter=400)) Ediag = expectation_value(gsdiag, Hdual, envsdiag) - excD0, qpD0 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D0) + excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D1) @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test variance(qpD1[1], Hdual) < 1e-8 # TODO: is this ever negative? + @test variance(qpD1[1], Hdual) < 1e-8 # comparison to Z2 Ising: injective in symmetric phase HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) @@ -158,10 +167,10 @@ module TestMultifusion gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS(;tol=tol, maxiter=400)) EZ2 = expectation_value(gsZ2, HZ2, envsZ2) @test isapprox(EZ2, Ediag; atol = 1.0e-6) - excZ2_0, qpZ2_0 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(0)) + excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(1)) - @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-5) - @test variance(qpZ2_1[1], HZ2) < 1e-6 #TODO: and this? - # end + @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-6) + @test variance(qpZ2_1[1], HZ2) < 1e-8 + end end # module TestMultifusion From fdd95c735cc9beed50dc695f331a1d2da6fe34f5 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:41:06 +0200 Subject: [PATCH 36/54] Revert "Merge branch 'master' of https://github.com/QuantumKitHub/MPSKit.jl into boris-MTK-compat" This reverts commit 2c96ab1f2707da1c01dd0d81e89ef4ff1bfcec1d, reversing changes made to a17874ab1a82e3d33f22fd6d349820462d57a4f5. --- .github/workflows/Documentation.yml | 2 +- .github/workflows/DocumentationCleanup.yml | 2 +- Project.toml | 4 +- README.md | 6 +- docs/make.jl | 2 +- src/algorithms/approximate/fvomps.jl | 2 +- .../excitation/exci_transfer_system.jl | 120 +++++++++++++++--- .../excitation/quasiparticleexcitation.jl | 8 +- src/algorithms/expval.jl | 2 +- src/algorithms/toolbox.jl | 6 +- src/environments/qp_envs.jl | 45 ++++--- src/operators/abstractmpo.jl | 7 +- src/operators/mpo.jl | 1 - src/states/abstractmps.jl | 3 - src/states/finitemps.jl | 1 + src/states/infinitemps.jl | 4 +- src/states/multilinemps.jl | 7 +- src/states/quasiparticle_state.jl | 26 +--- src/states/windowmps.jl | 6 +- src/transfermatrix/transfermatrix.jl | 4 +- src/utility/windowarray.jl | 4 +- test/algorithms.jl | 15 --- test/operators.jl | 9 -- test/setup.jl | 26 +++- test/states.jl | 38 +----- 25 files changed, 188 insertions(+), 162 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 59d82d02f..3a7606520 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -25,7 +25,7 @@ jobs: arch: - x64 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} diff --git a/.github/workflows/DocumentationCleanup.yml b/.github/workflows/DocumentationCleanup.yml index f8a1e525e..5be23b964 100644 --- a/.github/workflows/DocumentationCleanup.yml +++ b/.github/workflows/DocumentationCleanup.yml @@ -16,7 +16,7 @@ jobs: contents: write steps: - name: Checkout gh-pages branch - uses: actions/checkout@v5 + uses: actions/checkout@v4 with: ref: gh-pages - name: Delete preview and history + push changes diff --git a/Project.toml b/Project.toml index f5a00ec63..bc9949fa6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MPSKit" uuid = "bb1c41ca-d63c-52ed-829e-0820dda26502" authors = "Lukas Devos, Maarten Van Damme and contributors" -version = "0.13.4" +version = "0.13.3" [deps] Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" @@ -30,7 +30,7 @@ Combinatorics = "1" Compat = "3.47, 4.10" DocStringExtensions = "0.9.3" HalfIntegers = "1.6.0" -KrylovKit = "0.8.3, 0.9.2, 0.10" +KrylovKit = "0.8.3, 0.9.2" LinearAlgebra = "1.6" LoggingExtras = "~1.0" OhMyThreads = "0.7, 0.8" diff --git a/README.md b/README.md index 18c2cb1b6..0487f17c7 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ and matrix product operators (MPO), both finite and infinite. [doi-img]: https://zenodo.org/badge/DOI/10.5281/zenodo.10654901.svg [doi-url]: https://doi.org/10.5281/zenodo.10654901 -[codecov-img]: https://codecov.io/gh/QuantumKitHub/MPSKit.jl/branch/master/graph/badge.svg?token=rmp3bu7qn3 +[codecov-img]: https://codecov.io/gh/QuantumKitHub/MPSKit.jl/graph/badge.svg?token=rmp3bu7qn3 [codecov-url]: https://codecov.io/gh/QuantumKitHub/MPSKit.jl [ci-img]: https://github.com/QuantumKitHub/MPSKit.jl/actions/workflows/Tests.yml/badge.svg [ci-url]: https://github.com/QuantumKitHub/MPSKit.jl/actions/workflows/Tests.yml -[pkgeval-img]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/M/MPSKit.svg -[pkgeval-url]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/M/MPSKit.html +[pkgeval-img]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/T/MPSKit.svg +[pkgeval-url]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/T/MPSKit.html The framework is built upon [TensorKit.jl](https://github.com/jutho/TensorKit.jl), which provides functionality for diff --git a/docs/make.jl b/docs/make.jl index 6c2ac011d..6bbfcc67c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -28,7 +28,7 @@ bib = CitationBibliography(bibpath; style = :authoryear) # interlinks links = InterLinks( "TensorKit" => "https://jutho.github.io/TensorKit.jl/stable/", - "TensorOperations" => "https://quantumkithub.github.io/TensorOperations.jl/stable/", + "TensorOperations" => "https://jutho.github.io/TensorOperations.jl/stable/", "KrylovKit" => "https://jutho.github.io/KrylovKit.jl/stable/", "BlockTensorKit" => "https://lkdvos.github.io/BlockTensorKit.jl/dev/" ) diff --git a/src/algorithms/approximate/fvomps.jl b/src/algorithms/approximate/fvomps.jl index 7f62c817c..a10e2a112 100644 --- a/src/algorithms/approximate/fvomps.jl +++ b/src/algorithms/approximate/fvomps.jl @@ -8,7 +8,7 @@ function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, envs = environment ϵ = 0.0 for pos in [1:(length(ψ) - 1); (length(ψ) - 2):-1:1] AC2′ = AC2_projection(pos, ψ, Oϕ, envs) - al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme, alg = alg.alg_svd) + al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme) AC2 = ψ.AC[pos] * _transpose_tail(ψ.AR[pos + 1]) ϵ = max(ϵ, norm(al * c * ar - AC2) / norm(AC2)) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 8fb975f45..1e715996d 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -1,3 +1,48 @@ +# is this function ever called? b/c isid no longer exists +function left_excitation_transfer_system( + lBs, H, exci; mom = exci.momentum, + solver = Defaults.linearsolver + ) + len = length(H) + found = zero.(lBs) + odim = length(lBs) + + for i in 1:odim + # this operation can in principle be even further optimized for larger unit cells + # as we only require the terms that end at level i. + # this would require to check the finite state machine, and discard non-connected + # terms. + H_partial = map(site -> H.data[site, 1:i, 1:i], 1:len) + T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) + start = scale!(last(found[1:i] * T), cis(-mom * len)) + if exci.trivial && isid(H, i) + @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * r_RL(exci.right_gs)[2; 3] * + τ[3 4; 5 1] * l_RL(exci.right_gs)[-1; 6] * τ[5 6; -4 -2] + end + + found[i] = add!(start, lBs[i]) + + if reduce(&, contains.(H.data, i, i)) + if isid(H, i) + tm = TransferMatrix(exci.right_gs.AR, exci.left_gs.AL) + if exci.trivial + tm = regularize(tm, l_RL(exci.right_gs), r_RL(exci.right_gs)) + end + else + tm = TransferMatrix( + exci.right_gs.AR, getindex.(H.data, i, i), exci.left_gs.AL + ) + end + + found[i], convhist = linsolve( + flip(tm), found[i], found[i], solver, 1, -cis(-mom * len) + ) + convhist.converged == 0 && + @warn "GBL$i failed to converge: normres = $(convhist.normres)" + end + end + return found +end function left_excitation_transfer_system( GBL, H::InfiniteMPOHamiltonian, exci; mom = exci.momentum, solver = Defaults.linearsolver @@ -6,11 +51,6 @@ function left_excitation_transfer_system( found = zerovector(GBL) odim = length(GBL) - if istrivial(exci) - ρ_left = l_RL(exci.right_gs) - ρ_right = r_RL(exci.right_gs) - end - for i in 1:odim # this operation can in principle be even further optimized for larger unit cells # as we only require the terms that end at level i. @@ -19,8 +59,6 @@ function left_excitation_transfer_system( H_partial = map(h -> getindex(h, 1:i, 1, 1, 1:i), parent(H)) T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) - if istrivial(exci) && isidentitylevel(H, i) - regularize!(start, ρ_right, ρ_left) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg util = similar(exci.left_gs.AL[i], first(left_virtualspace(H[i]))) @@ -37,8 +75,11 @@ function left_excitation_transfer_system( if !isemptylevel(H, i) if isidentitylevel(H, i) T = TransferMatrix(exci.right_gs.AR, exci.left_gs.AL) - if istrivial(exci) - T = regularize(T, ρ_left, ρ_right) + if exci.trivial + # deal with extra leg + @plansor lRL_util[-1 -2; -3] := l_RL(exci.right_gs)[-1;-3] * conj(util[-2]) + @plansor rRL_util[-1 -2; -3] := r_RL(exci.right_gs)[-1;-3] * util[-2] + T = regularize(T, lRL_util, rRL_util) end else T = TransferMatrix( @@ -55,6 +96,50 @@ function left_excitation_transfer_system( end return found end +# same with this? +function right_excitation_transfer_system( + rBs, H, exci; mom = exci.momentum, solver = Defaults.linearsolver + ) + len = length(H) + found = zero.(rBs) + odim = length(rBs) + + for i in odim:-1:1 + # this operation can in principle be even further optimized for larger unit cells + # as we only require the terms that end at level i. + # this would require to check the finite state machine, and discard non-connected + # terms. + H_partial = map(site -> H.data[site, i:odim, i:odim], 1:len) + T = TransferMatrix(exci.left_gs.AL, H_partial, exci.right_gs.AR) + start = scale!(first(T * found[i:odim]), cis(mom * len)) + if exci.trivial && isid(H, i) + @plansor start[-1 -2; -3 -4] -= τ[6 2; 3 4] * start[3 4; -3 5] * + l_LR(exci.right_gs)[5; 2] * r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] + end + + found[i] = add!(start, rBs[i]) + + if reduce(&, contains.(H.data, i, i)) + if isid(H, i) + tm = TransferMatrix(exci.left_gs.AL, exci.right_gs.AR) + if exci.trivial + tm = regularize(tm, l_LR(exci.left_gs), r_LR(exci.right_gs)) + end + else + tm = TransferMatrix( + exci.left_gs.AL, getindex.(H.data, i, i), exci.right_gs.AR + ) + end + + found[i], convhist = linsolve( + tm, found[i], found[i], solver, 1, -cis(mom * len) + ) + convhist.converged < 1 && + @warn "GBR$i failed to converge: normres = $(convhist.normres)" + end + end + return found +end function right_excitation_transfer_system( GBR, H::InfiniteMPOHamiltonian, exci; @@ -65,11 +150,6 @@ function right_excitation_transfer_system( found = zerovector(GBR) odim = length(GBR) - if istrivial(exci) - ρ_left = l_LR(exci.right_gs) - ρ_right = r_LR(exci.right_gs) - end - for i in odim:-1:1 # this operation can in principle be even further optimized for larger unit cells # as we only require the terms that end at level i. @@ -78,8 +158,6 @@ function right_excitation_transfer_system( H_partial = map(h -> h[i:end, 1, 1, i:end], parent(H)) T = TransferMatrix(exci.left_gs.AL, H_partial, exci.right_gs.AR) start = scale!(first(T * found[i:odim]), cis(mom * len)) - if istrivial(exci) && isidentitylevel(H, i) - regularize!(start, ρ_left, ρ_right) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg util = similar(exci.right_gs.AL[i], first(left_virtualspace(H[i]))) @@ -96,8 +174,11 @@ function right_excitation_transfer_system( if !isemptylevel(H, i) if isidentitylevel(H, i) tm = TransferMatrix(exci.left_gs.AL, exci.right_gs.AR) - if istrivial(exci) - tm = regularize(tm, ρ_left, ρ_right) + if exci.trivial + # deal with extra leg + @plansor lLR_util[-1 -2; -3] := l_LR(exci.left_gs)[-1;-3] * conj(util[-2]) + @plansor rLR_util[-1 -2; -3] := r_LR(exci.right_gs)[-1;-3] * util[-2] + tm = regularize(tm, lLR_util, rLR_util) end else tm = TransferMatrix( @@ -106,7 +187,8 @@ function right_excitation_transfer_system( end found[i], convhist = linsolve( - tm, found[i], found[i], solver, 1, -cis(mom * len) + tm, found[i], found[i], solver, 1, + -cis(mom * len) ) convhist.converged < 1 && @warn "GBR$i failed to converge: normres = $(convhist.normres)" diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index fe1de0615..3f9ece592 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -56,7 +56,7 @@ function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renv end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs; num = 1, kwargs...) # Infer `renvs` in function body as it depends on `solver`. - renvs = !istopological(ϕ₀) ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) + renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs, renvs; num, kwargs...) end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP; num = 1, kwargs...) @@ -147,7 +147,7 @@ end function excitations( H, alg::QuasiparticleAnsatz, ϕ₀::FiniteQP, lenvs = environments(ϕ₀.left_gs, H), - renvs = !istopological(ϕ₀) ? lenvs : environments(ϕ₀.right_gs, H); + renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H); num = 1 ) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) @@ -234,7 +234,7 @@ function excitations( kwargs... ) # Infer `renvs` in function body as it depends on `solver`. - renvs = !istopological(ϕ₀) ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) + renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) end function excitations( @@ -383,7 +383,7 @@ function effective_excitation_renormalization_energy(H, ϕ, lenvs, renvs) E[i] = contract_mpo_expval( ψ_left.AC[i], leftenv(lenvs, i, ψ_left), H[i], rightenv(lenvs, i, ψ_left) ) - if istopological(ϕ) + if !ϕ.trivial E[i] += contract_mpo_expval( ψ_right.AC[i], leftenv(renvs, i, ψ_right), H[i], rightenv(renvs, i, ψ_right) ) diff --git a/src/algorithms/expval.jl b/src/algorithms/expval.jl index e8ac68738..2881eeb69 100644 --- a/src/algorithms/expval.jl +++ b/src/algorithms/expval.jl @@ -42,7 +42,7 @@ function expectation_value(ψ::AbstractMPS, (inds, O)::Pair) dual(_lastspace(last(local_mpo))) for (site, o) in zip(sites, local_mpo) if o isa MPOTensor - physicalspace(ψ, site) == physicalspace(o) || + physicalspace(ψ)[site] == physicalspace(o) || throw(SpaceMismatch("physical space does not match at site $site")) end end diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 8e2d0a25e..cd4566254 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -207,7 +207,7 @@ function variance( state.AC[i], envs.GLs[i], H[i][:, :, :, end], envs.GRs[i][end] ) end - lattice = physicalspace(state) + lattice = physicalspace.(Ref(state), 1:length(state)) H_renormalized = InfiniteMPOHamiltonian( lattice, i => e * id(storagetype(eltype(H)), lattice[i]) for (i, e) in enumerate(e_local) ) @@ -225,7 +225,7 @@ end function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs = environments(state, H)) # I remember there being an issue here @gertian? - istopological(state) && + state.trivial || throw(ArgumentError("variance of domain wall excitations is not implemented")) gs = state.left_gs @@ -234,7 +234,7 @@ function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs = environme GR = rightenv(envs, i, gs) return contract_mpo_expval(gs.AC[i], GL, H[i][:, :, :, end], GR[end]) end - lattice = physicalspace(gs) + lattice = physicalspace.(Ref(gs), 1:length(state)) H_regularized = H - InfiniteMPOHamiltonian( lattice, i => e * id(storagetype(eltype(H)), lattice[i]) for (i, e) in enumerate(e_local) ) diff --git a/src/environments/qp_envs.jl b/src/environments/qp_envs.jl index 4aacf0dae..acc9fa9f3 100644 --- a/src/environments/qp_envs.jl +++ b/src/environments/qp_envs.jl @@ -33,7 +33,7 @@ function environments(exci::Union{InfiniteQP, MultilineQP}, H; kwargs...) return environments(exci, H, lenvs; kwargs...) end function environments(exci::Union{InfiniteQP, MultilineQP}, H, lenvs; kwargs...) - renvs = !istopological(exci) ? lenvs : environments(exci.right_gs, H; kwargs...) + renvs = exci.trivial ? lenvs : environments(exci.right_gs, H; kwargs...) return environments(exci, H, lenvs, renvs; kwargs...) end @@ -46,7 +46,7 @@ function environments(qp::MultilineQP, operator::MultilineMPO, lenvs, renvs; kwa end function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; kwargs...) - ids = findall(Base.Fix1(isidentitylevel, H), 2:(size(H[1], 1) - 1)) .+ 1 + ids = findall(Base.Fix1(isidentitylevel, H), 2:(size(H[1], 1) - 1)) solver = environment_alg(exci, H, exci; kwargs...) AL = exci.left_gs.AL @@ -62,11 +62,11 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; lBs[pos + 1] += leftenv(lenvs, pos, exci.left_gs) * TransferMatrix(exci[pos], H[pos], AL[pos]) / cis(exci.momentum) - if istrivial(exci) && !isempty(ids) # regularization of trivial excitations - ρ_left = l_RL(exci.left_gs, pos + 1) - ρ_right = r_RL(exci.left_gs, pos) + if exci.trivial # regularization of trivial excitations for i in ids - regularize!(lBs[pos + 1][i], ρ_right, ρ_left) + @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][1 4; -3 2] * + r_RL(exci.left_gs, pos)[2; 3] * τ[3 4; 5 1] * + l_RL(exci.left_gs, pos + 1)[ -1; 6 ] * τ[5 6; -4 -2] end end end @@ -78,11 +78,12 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; rBs[pos - 1] += TransferMatrix(exci[pos], H[pos], AR[pos]) * rightenv(renvs, pos, exci.right_gs) * cis(exci.momentum) - if istrivial(exci) && !isempty(ids) - ρ_left = l_LR(exci.left_gs, pos) - ρ_right = r_LR(exci.left_gs, pos - 1) + if exci.trivial for i in ids - regularize!(rBs[pos - 1][i], ρ_left, ρ_right) + ρ_left = l_LR(exci.left_gs, pos) + ρ_right = r_LR(exci.left_gs, pos - 1) + @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= τ[6 4; 1 3] * + rBs[pos - 1][i][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] end end end @@ -100,11 +101,12 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; for i in 1:(length(exci) - 1) lB_cur = lB_cur * TransferMatrix(AR[i], H[i], AL[i]) / cis(exci.momentum) - if istrivial(exci) && !isempty(ids) - ρ_left = l_RL(exci.left_gs, i + 1) - ρ_right = r_RL(exci.left_gs, i) + if exci.trivial for k in ids - regularize!(lB_cur[k], ρ_right, ρ_left) + ρ_left = l_RL(exci.left_gs, i + 1) + ρ_right = r_RL(exci.left_gs, i) + @plansor lB_cur[k][-1 -2; -3 -4] -= lB_cur[k][1 4; -3 2] * + ρ_right[2; 3] * τ[3 4; 5 1] * ρ_left[-1; 6] * τ[5 6; -4 -2] end end @@ -115,11 +117,12 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; for i in length(exci):-1:2 rB_cur = TransferMatrix(AL[i], H[i], AR[i]) * rB_cur * cis(exci.momentum) - if istrivial(exci) && !isempty(ids) - ρ_left = l_LR(exci.left_gs, i) - ρ_right = r_LR(exci.left_gs, i - 1) + if exci.trivial for k in ids - regularize!(rB_cur[k], ρ_left, ρ_right) + ρ_left = l_LR(exci.left_gs, i) + ρ_right = r_LR(exci.left_gs, i - 1) + @plansor rB_cur[k][-1 -2; -3 -4] -= τ[6 4; 1 3] * + rB_cur[k][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] end end @@ -132,7 +135,7 @@ end function environments( exci::FiniteQP, H::FiniteMPOHamiltonian, lenvs = environments(exci.left_gs, H), - renvs = !istopological(exci) ? lenvs : environments(exci.right_gs, H); + renvs = exci.trivial ? lenvs : environments(exci.right_gs, H); kwargs... ) AL = exci.left_gs.AL @@ -161,7 +164,7 @@ function environments( end function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) - istopological(exci) && + exci.trivial || @warn "there is a phase ambiguity in topologically nontrivial statmech excitations" solver = environment_alg(exci, O, exci; kwargs...) @@ -203,7 +206,7 @@ function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) T_RL = TransferMatrix(right_gs.AR, O, left_gs.AL) T_LR = TransferMatrix(left_gs.AL, O, right_gs.AR) - if istrivial(exci) + if exci.trivial @plansor rvec[-1 -2; -3] := rightenv(lenvs, 0, left_gs)[-1 -2; 1] * conj(left_gs.C[0][-3; 1]) @plansor lvec[-1 -2; -3] := leftenv(lenvs, 1, left_gs)[-1 -2; 1] * diff --git a/src/operators/abstractmpo.jl b/src/operators/abstractmpo.jl index 29f40ac1f..4cbaa4ec9 100644 --- a/src/operators/abstractmpo.jl +++ b/src/operators/abstractmpo.jl @@ -14,7 +14,6 @@ Base.isfinite(O::AbstractMPO) = isfinite(typeof(O)) # By default, define things in terms of parent Base.size(mpo::AbstractMPO, args...) = size(parent(mpo), args...) Base.length(mpo::AbstractMPO) = length(parent(mpo)) -eachsite(mpo::AbstractMPO) = eachindex(mpo) @inline Base.getindex(mpo::AbstractMPO, i::Int) = getindex(parent(mpo), i) @inline function Base.setindex!(mpo::AbstractMPO, value, i::Int) @@ -25,11 +24,11 @@ end # Properties # ---------- left_virtualspace(mpo::AbstractMPO, site::Int) = left_virtualspace(mpo[site]) -left_virtualspace(mpo::AbstractMPO) = map(Base.Fix1(left_virtualspace, mpo), eachsite(mpo)) +left_virtualspace(mpo::AbstractMPO) = map(left_virtualspace, parent(mpo)) right_virtualspace(mpo::AbstractMPO, site::Int) = right_virtualspace(mpo[site]) -right_virtualspace(mpo::AbstractMPO) = map(Base.Fix1(right_virtualspace, mpo), eachsite(mpo)) +right_virtualspace(mpo::AbstractMPO) = map(right_virtualspace, parent(mpo)) physicalspace(mpo::AbstractMPO, site::Int) = physicalspace(mpo[site]) -physicalspace(mpo::AbstractMPO) = map(Base.Fix1(physicalspace, mpo), eachsite(mpo)) +physicalspace(mpo::AbstractMPO) = map(physicalspace, mpo) for ftype in (:spacetype, :sectortype, :storagetype) @eval TensorKit.$ftype(mpo::AbstractMPO) = $ftype(typeof(mpo)) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index cf5c50710..a4dd84cb9 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -54,7 +54,6 @@ DenseMPO(mpo::MPO) = mpo isa DenseMPO ? copy(mpo) : MPO(map(TensorMap, parent(mp # ------- Base.parent(mpo::MPO) = mpo.O Base.copy(mpo::MPO) = MPO(copy.(parent(mpo))) -eachsite(mpo::InfiniteMPO) = PeriodicArray(eachindex(mpo)) function Base.similar(mpo::MPO{<:MPOTensor}, ::Type{O}, L::Int) where {O <: MPOTensor} return MPO(similar(parent(mpo), O, L)) diff --git a/src/states/abstractmps.jl b/src/states/abstractmps.jl index fb405e19a..87500ba28 100644 --- a/src/states/abstractmps.jl +++ b/src/states/abstractmps.jl @@ -187,7 +187,6 @@ Return the virtual space of the bond to the left of sites `pos`. function left_virtualspace end left_virtualspace(A::GenericMPSTensor) = space(A, 1) left_virtualspace(O::MPOTensor) = space(O, 1) -left_virtualspace(ψ::AbstractMPS) = map(Base.Fix1(left_virtualspace, ψ), eachsite(ψ)) """ right_virtualspace(ψ::AbstractMPS, [pos=1:length(ψ)]) @@ -201,7 +200,6 @@ Return the virtual space of the bond to the right of site(s) `pos`. function right_virtualspace end right_virtualspace(A::GenericMPSTensor) = space(A, numind(A))' right_virtualspace(O::MPOTensor) = space(O, 4)' -right_virtualspace(ψ::AbstractMPS) = map(Base.Fix1(right_virtualspace, ψ), eachsite(ψ)) """ physicalspace(ψ::AbstractMPS, [pos=1:length(ψ)]) @@ -213,7 +211,6 @@ physicalspace(A::MPSTensor) = space(A, 2) physicalspace(A::GenericMPSTensor) = prod(x -> space(A, x), 2:(numind(A) - 1)) physicalspace(O::MPOTensor) = space(O, 2) physicalspace(O::AbstractBlockTensorMap{<:Any, <:Any, 2, 2}) = only(space(O, 2)) -physicalspace(ψ::AbstractMPS) = map(Base.Fix1(physicalspace, ψ), eachsite(ψ)) """ eachsite(state::AbstractMPS) diff --git a/src/states/finitemps.jl b/src/states/finitemps.jl index 1300ceaa4..aad998e06 100644 --- a/src/states/finitemps.jl +++ b/src/states/finitemps.jl @@ -394,6 +394,7 @@ function right_virtualspace(ψ::FiniteMPS, n::Integer) _firstspace(ψ.C[n]) end +physicalspace(ψ::FiniteMPS) = physicalspace.(Ref(ψ), 1:length(ψ)) function physicalspace(ψ::FiniteMPS{<:GenericMPSTensor{<:Any, N}}, n::Integer) where {N} N == 1 && return ProductSpace{spacetype(ψ)}() return physicalspace(coalesce(ψ.ALs[n], ψ.ARs[n], ψ.ACs[n])) diff --git a/src/states/infinitemps.jl b/src/states/infinitemps.jl index b92bd9c40..fe192d80e 100644 --- a/src/states/infinitemps.jl +++ b/src/states/infinitemps.jl @@ -265,15 +265,17 @@ end Base.eachindex(ψ::InfiniteMPS) = eachindex(ψ.AL) Base.eachindex(l::IndexStyle, ψ::InfiniteMPS) = eachindex(l, ψ.AL) -eachsite(ψ::InfiniteMPS) = PeriodicArray(eachindex(ψ)) Base.checkbounds(::Type{Bool}, ψ::InfiniteMPS, i::Integer) = true site_type(::Type{<:InfiniteMPS{A}}) where {A} = A bond_type(::Type{<:InfiniteMPS{<:Any, B}}) where {B} = B +left_virtualspace(ψ::InfiniteMPS) = left_virtualspace.(ψ.AL) left_virtualspace(ψ::InfiniteMPS, n::Integer) = left_virtualspace(ψ.AL[n]) +right_virtualspace(ψ::InfiniteMPS) = right_virtualspace.(ψ.AL) right_virtualspace(ψ::InfiniteMPS, n::Integer) = right_virtualspace(ψ.AL[n]) +physicalspace(ψ::InfiniteMPS) = physicalspace.(ψ.AL) physicalspace(ψ::InfiniteMPS, n::Integer) = physicalspace(ψ.AL[n]) # TensorKit.space(ψ::InfiniteMPS{<:MPSTensor}, n::Integer) = space(ψ.AC[n], 2) diff --git a/src/states/multilinemps.jl b/src/states/multilinemps.jl index 6f1199095..8ce092843 100644 --- a/src/states/multilinemps.jl +++ b/src/states/multilinemps.jl @@ -101,8 +101,5 @@ Base.convert(::Type{InfiniteMPS}, st::MultilineMPS) = only(st) Base.eltype(t::MultilineMPS) = eltype(t[1]) Base.copy!(ψ::MultilineMPS, ϕ::MultilineMPS) = (copy!.(parent(ψ), parent(ϕ)); ψ) -for f_space in (:physicalspace, :left_virtualspace, :right_virtualspace) - @eval $f_space(t::MultilineMPS, i::Int, j::Int) = $f_space(t[i], j) - @eval $f_space(t::MultilineMPS, I::CartesianIndex{2}) = $f_space(t, Tuple(I)...) - @eval $f_space(t::MultilineMPS) = map(Base.Fix1($f_space, t), eachindex(t)) -end +left_virtualspace(t::MultilineMPS, i::Int, j::Int) = left_virtualspace(t[i], j) +right_virtualspace(t::MultilineMPS, i::Int, j::Int) = right_virtualspace(t[i], j) diff --git a/src/states/quasiparticle_state.jl b/src/states/quasiparticle_state.jl index cf476f914..008ab05b8 100644 --- a/src/states/quasiparticle_state.jl +++ b/src/states/quasiparticle_state.jl @@ -133,7 +133,7 @@ function Base.convert( tm = TransferMatrix(input.left_gs.AL, input.right_gs.AR) - if istrivial(input) + if input.trivial tm = regularize(tm, l_LR(input.right_gs), r_LR(input.right_gs)) end @@ -180,7 +180,7 @@ function Base.convert( end tm = TransferMatrix(input.right_gs.AR, input.left_gs.AL) - if istrivial(input) + if input.trivial tm = regularize(tm, l_RL(input.right_gs), r_RL(input.right_gs)) end @@ -215,18 +215,9 @@ const MultilineQP{Q <: QP} = Multiline{Q} TensorKit.spacetype(::Union{QP{S}, Type{<:QP{S}}}) where {S} = spacetype(S) TensorKit.sectortype(::Union{QP{S}, Type{<:QP{S}}}) where {S} = sectortype(S) -physicalspace(state::QP, i::Int) = physicalspace(state.left_gs, i) -physicalspace(state::QP) = physicalspace(state.left_gs) left_virtualspace(state::QP, i::Int) = left_virtualspace(state.left_gs, i) -left_virtualspace(state::QP) = map(Base.Fix1(left_virtualspace, state), eachsite(state)) right_virtualspace(state::QP, i::Int) = right_virtualspace(state.right_gs, i) -right_virtualspace(state::QP) = map(Base.Fix1(right_virtualspace, state), eachsite(state)) auxiliaryspace(state::QP) = space(state.Xs[1], 2) -auxiliarysector(state::QP) = only(sectors(auxiliaryspace(state))) -eachsite(state::QP) = eachsite(state.left_gs) - -istopological(qp::QP) = qp.left_gs !== qp.right_gs -istrivial(qp::QP) = !istopological(qp) && isone(auxiliarysector(qp)) Base.copy(a::QP) = copy!(similar(a), a) Base.copyto!(a::QP, b::QP) = copy!(a, b) @@ -236,12 +227,11 @@ function Base.copy!(a::T, b::T) where {T <: QP} end return a end -Base.@constprop :aggressive function Base.getproperty(qp::QP, s::Symbol) +function Base.getproperty(v::QP, s::Symbol) if s == :trivial - Base.depwarn("`qp.trivial` is deprecated in favor of `istrivial` and `istopological`", :trivial) - return !istopological(qp) + return v.left_gs === v.right_gs else - return getfield(qp, s) + return getfield(v, s) end end @@ -406,7 +396,7 @@ function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S <: FiniteMPS} return FiniteMPS(Ls + Rs + Bs; normalize = false) end -Base.@constprop :aggressive function Base.getproperty(exci::MultilineQP, s::Symbol) +function Base.getproperty(exci::MultilineQP, s::Symbol) if s == :momentum return first(exci.data).momentum elseif s == :left_gs @@ -420,10 +410,6 @@ Base.@constprop :aggressive function Base.getproperty(exci::MultilineQP, s::Symb end end -# These should really all be the same, so it might make sense to take the first instead -istrivial(exci::MultilineQP) = all(istrivial, exci.data) -istopological(exci::MultilineQP) = all(istopological, exci.data) - # VectorInterface # --------------- diff --git a/src/states/windowmps.jl b/src/states/windowmps.jl index 5c0bab9d0..56c38452a 100644 --- a/src/states/windowmps.jl +++ b/src/states/windowmps.jl @@ -150,7 +150,11 @@ for f in (:physicalspace, :left_virtualspace, :right_virtualspace) @eval $f(ψ::WindowMPS, n::Integer) = n < 1 ? $f(ψ.left_gs, n) : n > length(ψ) ? $f(ψ.right_gs, n - length(ψ)) : $f(ψ.window, n) - @eval $f(ψ::WindowMPS) = WindowArray($f(ψ.left_gs), $f(ψ.window), $f(ψ.right_gs)) +end +function physicalspace(ψ::WindowMPS) + return WindowArray( + physicalspace(ψ.left_gs), physicalspace(ψ.window), physicalspace(ψ.right_gs) + ) end r_RR(ψ::WindowMPS) = r_RR(ψ.right_gs, length(ψ)) l_LL(ψ::WindowMPS) = l_LL(ψ.left_gs, 1) diff --git a/src/transfermatrix/transfermatrix.jl b/src/transfermatrix/transfermatrix.jl index 31a33ab06..88030e91d 100644 --- a/src/transfermatrix/transfermatrix.jl +++ b/src/transfermatrix/transfermatrix.jl @@ -87,6 +87,6 @@ function regularize!(v::MPOTensor, lvec::MPSTensor, rvec::MPSTensor) end function regularize!(v::MPOTensor, lvec::MPSBondTensor, rvec::MPSBondTensor) - λ = @plansor lvec[2; 1] * removeunit(removeunit(v, 3), 2)[1; 2] - return add!(v, insertleftunit(insertrightunit(rvec, 1; dual = isdual(space(v, 2))), 3), -λ) + return @plansor v[-1 -2; -3 -4] -= τ[6 2; 3 4] * v[3 4; -3 5] * lvec[5; 2] * rvec[-1; 1] * + τ[-2 -4; 1 6] end diff --git a/src/utility/windowarray.jl b/src/utility/windowarray.jl index afcdcedbe..3dcaa686a 100644 --- a/src/utility/windowarray.jl +++ b/src/utility/windowarray.jl @@ -15,9 +15,9 @@ struct WindowArray{T} <: AbstractVector{T} right::PeriodicVector{T} end function WindowArray( - left::AbstractVector{T}, middle::AbstractVector{T}, right::AbstractVector{T} + left::PeriodicVector{T}, middle::AbstractVector{T}, right::PeriodicVector{T} ) where {T} - return WindowArray{T}(left, middle, right) + return WindowArray{T}(left, convert(Vector{T}, middle), right) end # these definitions are a bit iffy, but will do for now diff --git a/test/algorithms.jl b/test/algorithms.jl index b2de0020d..0aac47ad2 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -475,21 +475,6 @@ module TestAlgorithms @test energies[1] ≈ 0.41047925 atol = 1.0e-4 @test variance(ϕs[1], H) < 1.0e-8 end - @testset "infinite sector convention" begin - g = 4 - H = repeat(transverse_field_ising(Z2Irrep; g = g, L = Inf), 2) - V = Z2Space(0 => 24, 1 => 24) - ψ = InfiniteMPS(physicalspace(H), [V, V]) - ψ, envs = find_groundstate(ψ, H, VUMPS(; tol = 1.0e-10, maxiter = 400)) - - # testing istrivial and istopological - momentum = 0 - exc0, qp0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ; sector = Z2Irrep(0)) - exc1, qp1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ; sector = Z2Irrep(1)) - @test isapprox(first(exc1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test first(exc0) > 2 * first(exc1) - @test variance(qp1[1], H) < 1.0e-8 - end @testset "infinite (mpo)" begin H = repeat(sixvertex(), 2) ψ = InfiniteMPS([ℂ^2, ℂ^2], [ℂ^10, ℂ^10]) diff --git a/test/operators.jl b/test/operators.jl index 12f3a9706..0f2b6011f 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -31,15 +31,6 @@ module TestOperators mpo₁ = FiniteMPO(O₁) # type-unstable for now! mpo₂ = FiniteMPO(O₂) mpo₃ = FiniteMPO(O₃) - - @test @constinferred physicalspace(mpo₁) == fill(V, L) - Vleft = @constinferred left_virtualspace(mpo₁) - Vright = @constinferred right_virtualspace(mpo₂) - for i in 1:L - @test Vleft[i] == left_virtualspace(mpo₁, i) - @test Vright[i] == right_virtualspace(mpo₁, i) - end - @test convert(TensorMap, mpo₁) ≈ O₁ @test convert(TensorMap, -mpo₂) ≈ -O₂ @test convert(TensorMap, @constinferred complex(mpo₃)) ≈ complex(O₃) diff --git a/test/setup.jl b/test/setup.jl index c769bc430..7a5390827 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -142,16 +142,32 @@ function S_zz(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin = 1 // 2) where {T < return ZZ end -function transverse_field_ising(sym::Type{<:Sector} = Trivial, ::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} - X = S_x(sym, T; spin = 1 // 2) - ZZ = S_zz(sym, T; spin = 1 // 2) +function transverse_field_ising(::Type{Z2Irrep}, ::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite = false) where {T <: Number} + X = S_x(Z2Irrep, T; spin = 1 // 2) + ZZ = S_zz(Z2Irrep, T; spin = 1 // 2) if L == Inf - lattice = PeriodicArray([space(X, 1)]) + lattice = twosite ? PeriodicArray([space(X, 1), space(X, 1)]) : PeriodicArray([space(X, 1)]) + H₁ = InfiniteMPOHamiltonian(lattice, i => -g * X for i in 1:length(lattice)) + H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:length(lattice)) + else + lattice = fill(space(X, 1), L) + H₁ = FiniteMPOHamiltonian(lattice, i => -g * X for i in 1:L) + H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:(L - 1)) + end + return H₁ + H₂ +end + +function transverse_field_ising(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} + X = S_x(Trivial, T; spin = 1 // 2) + ZZ = S_zz(Trivial, T; spin = 1 // 2) + + if L == Inf + lattice = PeriodicArray([ℂ^2]) H₁ = InfiniteMPOHamiltonian(lattice, i => -g * X for i in 1:1) H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:1) else - lattice = fill(space(X, 1), L) + lattice = fill(ℂ^2, L) H₁ = FiniteMPOHamiltonian(lattice, i => -g * X for i in 1:L) H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:(L - 1)) end diff --git a/test/states.jl b/test/states.jl index e641c0b99..f763904af 100644 --- a/test/states.jl +++ b/test/states.jl @@ -21,12 +21,7 @@ module TestStates ComplexF32, ), ] - L = rand(3:20) - ψ = FiniteMPS(rand, elt, L, d, D) - - @test @constinferred physicalspace(ψ) == fill(d, L) - @test all(x -> x ≾ D, @constinferred left_virtualspace(ψ)) - @test all(x -> x ≾ D, @constinferred right_virtualspace(ψ)) + ψ = FiniteMPS(rand, elt, rand(3:20), d, D) ovl = dot(ψ, ψ) @@ -99,10 +94,6 @@ module TestStates ψ = InfiniteMPS([rand(elt, D * d, D), rand(elt, D * d, D)]; tol) - @test physicalspace(ψ) == fill(d, 2) - @test all(x -> x ≾ D, left_virtualspace(ψ)) - @test all(x -> x ≾ D, right_virtualspace(ψ)) - for i in 1:length(ψ) @plansor difference[-1 -2; -3] := ψ.AL[i][-1 -2; 1] * ψ.C[i][1; -3] - ψ.C[i - 1][-1; 1] * ψ.AR[i][1 -2; -3] @@ -130,10 +121,6 @@ module TestStates ]; tol ) - @test physicalspace(ψ) == fill(d, 2, 2) - @test all(x -> x ≾ D, left_virtualspace(ψ)) - @test all(x -> x ≾ D, right_virtualspace(ψ)) - for i in 1:size(ψ, 1), j in 1:size(ψ, 2) @plansor difference[-1 -2; -3] := ψ.AL[i, j][-1 -2; 1] * ψ.C[i, j][1; -3] - ψ.C[i, j - 1][-1; 1] * ψ.AR[i, j][1 -2; -3] @@ -168,16 +155,6 @@ module TestStates # constructor 2 - used to take a "slice" from an infinite mps window_2 = WindowMPS(gs, 10) - P = @constinferred physicalspace(window_2) - Vleft = @constinferred left_virtualspace(window_2) - Vright = @constinferred right_virtualspace(window_2) - - for i in -3:13 - @test physicalspace(window_2, i) == P[i] - @test left_virtualspace(window_2, i) == Vleft[i] - @test right_virtualspace(window_2, i) == Vright[i] - end - # we should logically have that window_1 approximates window_2 ovl = dot(window_1, window_2) @test ovl ≈ 1 atol = 1.0e-8 @@ -229,10 +206,6 @@ module TestStates ϕ₁ = LeftGaugedQP(rand, ψ) ϕ₂ = LeftGaugedQP(rand, ψ) - @test @constinferred physicalspace(ϕ₁) == physicalspace(ψ) - @test @constinferred left_virtualspace(ϕ₁) == left_virtualspace(ψ) - @test @constinferred right_virtualspace(ϕ₁) == right_virtualspace(ψ) - @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) @@ -264,15 +237,6 @@ module TestStates ϕ₁ = LeftGaugedQP(rand, ψ) ϕ₂ = LeftGaugedQP(rand, ψ) - @test @constinferred physicalspace(ϕ₁) == physicalspace(ψ) - @test @constinferred left_virtualspace(ϕ₁) == left_virtualspace(ψ) - @test @constinferred right_virtualspace(ϕ₁) == right_virtualspace(ψ) - for i in 1:period - @test physicalspace(ψ, i) == physicalspace(ϕ₁, i) - @test left_virtualspace(ψ, i) == left_virtualspace(ϕ₁, i) - @test right_virtualspace(ψ, i) == right_virtualspace(ϕ₁, i) - end - @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) From 54a4f742d6c93ddebe36aa4a6d6a4baa14b15d0b Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:41:34 +0200 Subject: [PATCH 37/54] Revert "Revert "Merge branch 'master' of https://github.com/QuantumKitHub/MPSKit.jl into boris-MTK-compat"" This reverts commit fdd95c735cc9beed50dc695f331a1d2da6fe34f5. --- .github/workflows/Documentation.yml | 2 +- .github/workflows/DocumentationCleanup.yml | 2 +- Project.toml | 4 +- README.md | 6 +- docs/make.jl | 2 +- src/algorithms/approximate/fvomps.jl | 2 +- .../excitation/exci_transfer_system.jl | 120 +++--------------- .../excitation/quasiparticleexcitation.jl | 8 +- src/algorithms/expval.jl | 2 +- src/algorithms/toolbox.jl | 6 +- src/environments/qp_envs.jl | 45 +++---- src/operators/abstractmpo.jl | 7 +- src/operators/mpo.jl | 1 + src/states/abstractmps.jl | 3 + src/states/finitemps.jl | 1 - src/states/infinitemps.jl | 4 +- src/states/multilinemps.jl | 7 +- src/states/quasiparticle_state.jl | 26 +++- src/states/windowmps.jl | 6 +- src/transfermatrix/transfermatrix.jl | 4 +- src/utility/windowarray.jl | 4 +- test/algorithms.jl | 15 +++ test/operators.jl | 9 ++ test/setup.jl | 26 +--- test/states.jl | 38 +++++- 25 files changed, 162 insertions(+), 188 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 3a7606520..59d82d02f 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -25,7 +25,7 @@ jobs: arch: - x64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} diff --git a/.github/workflows/DocumentationCleanup.yml b/.github/workflows/DocumentationCleanup.yml index 5be23b964..f8a1e525e 100644 --- a/.github/workflows/DocumentationCleanup.yml +++ b/.github/workflows/DocumentationCleanup.yml @@ -16,7 +16,7 @@ jobs: contents: write steps: - name: Checkout gh-pages branch - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: gh-pages - name: Delete preview and history + push changes diff --git a/Project.toml b/Project.toml index bc9949fa6..f5a00ec63 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MPSKit" uuid = "bb1c41ca-d63c-52ed-829e-0820dda26502" authors = "Lukas Devos, Maarten Van Damme and contributors" -version = "0.13.3" +version = "0.13.4" [deps] Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" @@ -30,7 +30,7 @@ Combinatorics = "1" Compat = "3.47, 4.10" DocStringExtensions = "0.9.3" HalfIntegers = "1.6.0" -KrylovKit = "0.8.3, 0.9.2" +KrylovKit = "0.8.3, 0.9.2, 0.10" LinearAlgebra = "1.6" LoggingExtras = "~1.0" OhMyThreads = "0.7, 0.8" diff --git a/README.md b/README.md index 0487f17c7..18c2cb1b6 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ and matrix product operators (MPO), both finite and infinite. [doi-img]: https://zenodo.org/badge/DOI/10.5281/zenodo.10654901.svg [doi-url]: https://doi.org/10.5281/zenodo.10654901 -[codecov-img]: https://codecov.io/gh/QuantumKitHub/MPSKit.jl/graph/badge.svg?token=rmp3bu7qn3 +[codecov-img]: https://codecov.io/gh/QuantumKitHub/MPSKit.jl/branch/master/graph/badge.svg?token=rmp3bu7qn3 [codecov-url]: https://codecov.io/gh/QuantumKitHub/MPSKit.jl [ci-img]: https://github.com/QuantumKitHub/MPSKit.jl/actions/workflows/Tests.yml/badge.svg [ci-url]: https://github.com/QuantumKitHub/MPSKit.jl/actions/workflows/Tests.yml -[pkgeval-img]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/T/MPSKit.svg -[pkgeval-url]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/T/MPSKit.html +[pkgeval-img]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/M/MPSKit.svg +[pkgeval-url]: https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/M/MPSKit.html The framework is built upon [TensorKit.jl](https://github.com/jutho/TensorKit.jl), which provides functionality for diff --git a/docs/make.jl b/docs/make.jl index 6bbfcc67c..6c2ac011d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -28,7 +28,7 @@ bib = CitationBibliography(bibpath; style = :authoryear) # interlinks links = InterLinks( "TensorKit" => "https://jutho.github.io/TensorKit.jl/stable/", - "TensorOperations" => "https://jutho.github.io/TensorOperations.jl/stable/", + "TensorOperations" => "https://quantumkithub.github.io/TensorOperations.jl/stable/", "KrylovKit" => "https://jutho.github.io/KrylovKit.jl/stable/", "BlockTensorKit" => "https://lkdvos.github.io/BlockTensorKit.jl/dev/" ) diff --git a/src/algorithms/approximate/fvomps.jl b/src/algorithms/approximate/fvomps.jl index a10e2a112..7f62c817c 100644 --- a/src/algorithms/approximate/fvomps.jl +++ b/src/algorithms/approximate/fvomps.jl @@ -8,7 +8,7 @@ function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, envs = environment ϵ = 0.0 for pos in [1:(length(ψ) - 1); (length(ψ) - 2):-1:1] AC2′ = AC2_projection(pos, ψ, Oϕ, envs) - al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme) + al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme, alg = alg.alg_svd) AC2 = ψ.AC[pos] * _transpose_tail(ψ.AR[pos + 1]) ϵ = max(ϵ, norm(al * c * ar - AC2) / norm(AC2)) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 1e715996d..8fb975f45 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -1,48 +1,3 @@ -# is this function ever called? b/c isid no longer exists -function left_excitation_transfer_system( - lBs, H, exci; mom = exci.momentum, - solver = Defaults.linearsolver - ) - len = length(H) - found = zero.(lBs) - odim = length(lBs) - - for i in 1:odim - # this operation can in principle be even further optimized for larger unit cells - # as we only require the terms that end at level i. - # this would require to check the finite state machine, and discard non-connected - # terms. - H_partial = map(site -> H.data[site, 1:i, 1:i], 1:len) - T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) - start = scale!(last(found[1:i] * T), cis(-mom * len)) - if exci.trivial && isid(H, i) - @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * r_RL(exci.right_gs)[2; 3] * - τ[3 4; 5 1] * l_RL(exci.right_gs)[-1; 6] * τ[5 6; -4 -2] - end - - found[i] = add!(start, lBs[i]) - - if reduce(&, contains.(H.data, i, i)) - if isid(H, i) - tm = TransferMatrix(exci.right_gs.AR, exci.left_gs.AL) - if exci.trivial - tm = regularize(tm, l_RL(exci.right_gs), r_RL(exci.right_gs)) - end - else - tm = TransferMatrix( - exci.right_gs.AR, getindex.(H.data, i, i), exci.left_gs.AL - ) - end - - found[i], convhist = linsolve( - flip(tm), found[i], found[i], solver, 1, -cis(-mom * len) - ) - convhist.converged == 0 && - @warn "GBL$i failed to converge: normres = $(convhist.normres)" - end - end - return found -end function left_excitation_transfer_system( GBL, H::InfiniteMPOHamiltonian, exci; mom = exci.momentum, solver = Defaults.linearsolver @@ -51,6 +6,11 @@ function left_excitation_transfer_system( found = zerovector(GBL) odim = length(GBL) + if istrivial(exci) + ρ_left = l_RL(exci.right_gs) + ρ_right = r_RL(exci.right_gs) + end + for i in 1:odim # this operation can in principle be even further optimized for larger unit cells # as we only require the terms that end at level i. @@ -59,6 +19,8 @@ function left_excitation_transfer_system( H_partial = map(h -> getindex(h, 1:i, 1, 1, 1:i), parent(H)) T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) + if istrivial(exci) && isidentitylevel(H, i) + regularize!(start, ρ_right, ρ_left) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg util = similar(exci.left_gs.AL[i], first(left_virtualspace(H[i]))) @@ -75,11 +37,8 @@ function left_excitation_transfer_system( if !isemptylevel(H, i) if isidentitylevel(H, i) T = TransferMatrix(exci.right_gs.AR, exci.left_gs.AL) - if exci.trivial - # deal with extra leg - @plansor lRL_util[-1 -2; -3] := l_RL(exci.right_gs)[-1;-3] * conj(util[-2]) - @plansor rRL_util[-1 -2; -3] := r_RL(exci.right_gs)[-1;-3] * util[-2] - T = regularize(T, lRL_util, rRL_util) + if istrivial(exci) + T = regularize(T, ρ_left, ρ_right) end else T = TransferMatrix( @@ -96,50 +55,6 @@ function left_excitation_transfer_system( end return found end -# same with this? -function right_excitation_transfer_system( - rBs, H, exci; mom = exci.momentum, solver = Defaults.linearsolver - ) - len = length(H) - found = zero.(rBs) - odim = length(rBs) - - for i in odim:-1:1 - # this operation can in principle be even further optimized for larger unit cells - # as we only require the terms that end at level i. - # this would require to check the finite state machine, and discard non-connected - # terms. - H_partial = map(site -> H.data[site, i:odim, i:odim], 1:len) - T = TransferMatrix(exci.left_gs.AL, H_partial, exci.right_gs.AR) - start = scale!(first(T * found[i:odim]), cis(mom * len)) - if exci.trivial && isid(H, i) - @plansor start[-1 -2; -3 -4] -= τ[6 2; 3 4] * start[3 4; -3 5] * - l_LR(exci.right_gs)[5; 2] * r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] - end - - found[i] = add!(start, rBs[i]) - - if reduce(&, contains.(H.data, i, i)) - if isid(H, i) - tm = TransferMatrix(exci.left_gs.AL, exci.right_gs.AR) - if exci.trivial - tm = regularize(tm, l_LR(exci.left_gs), r_LR(exci.right_gs)) - end - else - tm = TransferMatrix( - exci.left_gs.AL, getindex.(H.data, i, i), exci.right_gs.AR - ) - end - - found[i], convhist = linsolve( - tm, found[i], found[i], solver, 1, -cis(mom * len) - ) - convhist.converged < 1 && - @warn "GBR$i failed to converge: normres = $(convhist.normres)" - end - end - return found -end function right_excitation_transfer_system( GBR, H::InfiniteMPOHamiltonian, exci; @@ -150,6 +65,11 @@ function right_excitation_transfer_system( found = zerovector(GBR) odim = length(GBR) + if istrivial(exci) + ρ_left = l_LR(exci.right_gs) + ρ_right = r_LR(exci.right_gs) + end + for i in odim:-1:1 # this operation can in principle be even further optimized for larger unit cells # as we only require the terms that end at level i. @@ -158,6 +78,8 @@ function right_excitation_transfer_system( H_partial = map(h -> h[i:end, 1, 1, i:end], parent(H)) T = TransferMatrix(exci.left_gs.AL, H_partial, exci.right_gs.AR) start = scale!(first(T * found[i:odim]), cis(mom * len)) + if istrivial(exci) && isidentitylevel(H, i) + regularize!(start, ρ_left, ρ_right) if exci.trivial && isidentitylevel(H, i) # not using braiding tensors here, leads to extra leg util = similar(exci.right_gs.AL[i], first(left_virtualspace(H[i]))) @@ -174,11 +96,8 @@ function right_excitation_transfer_system( if !isemptylevel(H, i) if isidentitylevel(H, i) tm = TransferMatrix(exci.left_gs.AL, exci.right_gs.AR) - if exci.trivial - # deal with extra leg - @plansor lLR_util[-1 -2; -3] := l_LR(exci.left_gs)[-1;-3] * conj(util[-2]) - @plansor rLR_util[-1 -2; -3] := r_LR(exci.right_gs)[-1;-3] * util[-2] - tm = regularize(tm, lLR_util, rLR_util) + if istrivial(exci) + tm = regularize(tm, ρ_left, ρ_right) end else tm = TransferMatrix( @@ -187,8 +106,7 @@ function right_excitation_transfer_system( end found[i], convhist = linsolve( - tm, found[i], found[i], solver, 1, - -cis(mom * len) + tm, found[i], found[i], solver, 1, -cis(mom * len) ) convhist.converged < 1 && @warn "GBR$i failed to converge: normres = $(convhist.normres)" diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index 3f9ece592..fe1de0615 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -56,7 +56,7 @@ function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renv end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs; num = 1, kwargs...) # Infer `renvs` in function body as it depends on `solver`. - renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) + renvs = !istopological(ϕ₀) ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs, renvs; num, kwargs...) end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP; num = 1, kwargs...) @@ -147,7 +147,7 @@ end function excitations( H, alg::QuasiparticleAnsatz, ϕ₀::FiniteQP, lenvs = environments(ϕ₀.left_gs, H), - renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H); + renvs = !istopological(ϕ₀) ? lenvs : environments(ϕ₀.right_gs, H); num = 1 ) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) @@ -234,7 +234,7 @@ function excitations( kwargs... ) # Infer `renvs` in function body as it depends on `solver`. - renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) + renvs = !istopological(ϕ₀) ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) end function excitations( @@ -383,7 +383,7 @@ function effective_excitation_renormalization_energy(H, ϕ, lenvs, renvs) E[i] = contract_mpo_expval( ψ_left.AC[i], leftenv(lenvs, i, ψ_left), H[i], rightenv(lenvs, i, ψ_left) ) - if !ϕ.trivial + if istopological(ϕ) E[i] += contract_mpo_expval( ψ_right.AC[i], leftenv(renvs, i, ψ_right), H[i], rightenv(renvs, i, ψ_right) ) diff --git a/src/algorithms/expval.jl b/src/algorithms/expval.jl index 2881eeb69..e8ac68738 100644 --- a/src/algorithms/expval.jl +++ b/src/algorithms/expval.jl @@ -42,7 +42,7 @@ function expectation_value(ψ::AbstractMPS, (inds, O)::Pair) dual(_lastspace(last(local_mpo))) for (site, o) in zip(sites, local_mpo) if o isa MPOTensor - physicalspace(ψ)[site] == physicalspace(o) || + physicalspace(ψ, site) == physicalspace(o) || throw(SpaceMismatch("physical space does not match at site $site")) end end diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index cd4566254..8e2d0a25e 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -207,7 +207,7 @@ function variance( state.AC[i], envs.GLs[i], H[i][:, :, :, end], envs.GRs[i][end] ) end - lattice = physicalspace.(Ref(state), 1:length(state)) + lattice = physicalspace(state) H_renormalized = InfiniteMPOHamiltonian( lattice, i => e * id(storagetype(eltype(H)), lattice[i]) for (i, e) in enumerate(e_local) ) @@ -225,7 +225,7 @@ end function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs = environments(state, H)) # I remember there being an issue here @gertian? - state.trivial || + istopological(state) && throw(ArgumentError("variance of domain wall excitations is not implemented")) gs = state.left_gs @@ -234,7 +234,7 @@ function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs = environme GR = rightenv(envs, i, gs) return contract_mpo_expval(gs.AC[i], GL, H[i][:, :, :, end], GR[end]) end - lattice = physicalspace.(Ref(gs), 1:length(state)) + lattice = physicalspace(gs) H_regularized = H - InfiniteMPOHamiltonian( lattice, i => e * id(storagetype(eltype(H)), lattice[i]) for (i, e) in enumerate(e_local) ) diff --git a/src/environments/qp_envs.jl b/src/environments/qp_envs.jl index acc9fa9f3..4aacf0dae 100644 --- a/src/environments/qp_envs.jl +++ b/src/environments/qp_envs.jl @@ -33,7 +33,7 @@ function environments(exci::Union{InfiniteQP, MultilineQP}, H; kwargs...) return environments(exci, H, lenvs; kwargs...) end function environments(exci::Union{InfiniteQP, MultilineQP}, H, lenvs; kwargs...) - renvs = exci.trivial ? lenvs : environments(exci.right_gs, H; kwargs...) + renvs = !istopological(exci) ? lenvs : environments(exci.right_gs, H; kwargs...) return environments(exci, H, lenvs, renvs; kwargs...) end @@ -46,7 +46,7 @@ function environments(qp::MultilineQP, operator::MultilineMPO, lenvs, renvs; kwa end function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; kwargs...) - ids = findall(Base.Fix1(isidentitylevel, H), 2:(size(H[1], 1) - 1)) + ids = findall(Base.Fix1(isidentitylevel, H), 2:(size(H[1], 1) - 1)) .+ 1 solver = environment_alg(exci, H, exci; kwargs...) AL = exci.left_gs.AL @@ -62,11 +62,11 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; lBs[pos + 1] += leftenv(lenvs, pos, exci.left_gs) * TransferMatrix(exci[pos], H[pos], AL[pos]) / cis(exci.momentum) - if exci.trivial # regularization of trivial excitations + if istrivial(exci) && !isempty(ids) # regularization of trivial excitations + ρ_left = l_RL(exci.left_gs, pos + 1) + ρ_right = r_RL(exci.left_gs, pos) for i in ids - @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][1 4; -3 2] * - r_RL(exci.left_gs, pos)[2; 3] * τ[3 4; 5 1] * - l_RL(exci.left_gs, pos + 1)[ -1; 6 ] * τ[5 6; -4 -2] + regularize!(lBs[pos + 1][i], ρ_right, ρ_left) end end end @@ -78,12 +78,11 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; rBs[pos - 1] += TransferMatrix(exci[pos], H[pos], AR[pos]) * rightenv(renvs, pos, exci.right_gs) * cis(exci.momentum) - if exci.trivial + if istrivial(exci) && !isempty(ids) + ρ_left = l_LR(exci.left_gs, pos) + ρ_right = r_LR(exci.left_gs, pos - 1) for i in ids - ρ_left = l_LR(exci.left_gs, pos) - ρ_right = r_LR(exci.left_gs, pos - 1) - @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= τ[6 4; 1 3] * - rBs[pos - 1][i][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] + regularize!(rBs[pos - 1][i], ρ_left, ρ_right) end end end @@ -101,12 +100,11 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; for i in 1:(length(exci) - 1) lB_cur = lB_cur * TransferMatrix(AR[i], H[i], AL[i]) / cis(exci.momentum) - if exci.trivial + if istrivial(exci) && !isempty(ids) + ρ_left = l_RL(exci.left_gs, i + 1) + ρ_right = r_RL(exci.left_gs, i) for k in ids - ρ_left = l_RL(exci.left_gs, i + 1) - ρ_right = r_RL(exci.left_gs, i) - @plansor lB_cur[k][-1 -2; -3 -4] -= lB_cur[k][1 4; -3 2] * - ρ_right[2; 3] * τ[3 4; 5 1] * ρ_left[-1; 6] * τ[5 6; -4 -2] + regularize!(lB_cur[k], ρ_right, ρ_left) end end @@ -117,12 +115,11 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; for i in length(exci):-1:2 rB_cur = TransferMatrix(AL[i], H[i], AR[i]) * rB_cur * cis(exci.momentum) - if exci.trivial + if istrivial(exci) && !isempty(ids) + ρ_left = l_LR(exci.left_gs, i) + ρ_right = r_LR(exci.left_gs, i - 1) for k in ids - ρ_left = l_LR(exci.left_gs, i) - ρ_right = r_LR(exci.left_gs, i - 1) - @plansor rB_cur[k][-1 -2; -3 -4] -= τ[6 4; 1 3] * - rB_cur[k][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] + regularize!(rB_cur[k], ρ_left, ρ_right) end end @@ -135,7 +132,7 @@ end function environments( exci::FiniteQP, H::FiniteMPOHamiltonian, lenvs = environments(exci.left_gs, H), - renvs = exci.trivial ? lenvs : environments(exci.right_gs, H); + renvs = !istopological(exci) ? lenvs : environments(exci.right_gs, H); kwargs... ) AL = exci.left_gs.AL @@ -164,7 +161,7 @@ function environments( end function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) - exci.trivial || + istopological(exci) && @warn "there is a phase ambiguity in topologically nontrivial statmech excitations" solver = environment_alg(exci, O, exci; kwargs...) @@ -206,7 +203,7 @@ function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) T_RL = TransferMatrix(right_gs.AR, O, left_gs.AL) T_LR = TransferMatrix(left_gs.AL, O, right_gs.AR) - if exci.trivial + if istrivial(exci) @plansor rvec[-1 -2; -3] := rightenv(lenvs, 0, left_gs)[-1 -2; 1] * conj(left_gs.C[0][-3; 1]) @plansor lvec[-1 -2; -3] := leftenv(lenvs, 1, left_gs)[-1 -2; 1] * diff --git a/src/operators/abstractmpo.jl b/src/operators/abstractmpo.jl index 4cbaa4ec9..29f40ac1f 100644 --- a/src/operators/abstractmpo.jl +++ b/src/operators/abstractmpo.jl @@ -14,6 +14,7 @@ Base.isfinite(O::AbstractMPO) = isfinite(typeof(O)) # By default, define things in terms of parent Base.size(mpo::AbstractMPO, args...) = size(parent(mpo), args...) Base.length(mpo::AbstractMPO) = length(parent(mpo)) +eachsite(mpo::AbstractMPO) = eachindex(mpo) @inline Base.getindex(mpo::AbstractMPO, i::Int) = getindex(parent(mpo), i) @inline function Base.setindex!(mpo::AbstractMPO, value, i::Int) @@ -24,11 +25,11 @@ end # Properties # ---------- left_virtualspace(mpo::AbstractMPO, site::Int) = left_virtualspace(mpo[site]) -left_virtualspace(mpo::AbstractMPO) = map(left_virtualspace, parent(mpo)) +left_virtualspace(mpo::AbstractMPO) = map(Base.Fix1(left_virtualspace, mpo), eachsite(mpo)) right_virtualspace(mpo::AbstractMPO, site::Int) = right_virtualspace(mpo[site]) -right_virtualspace(mpo::AbstractMPO) = map(right_virtualspace, parent(mpo)) +right_virtualspace(mpo::AbstractMPO) = map(Base.Fix1(right_virtualspace, mpo), eachsite(mpo)) physicalspace(mpo::AbstractMPO, site::Int) = physicalspace(mpo[site]) -physicalspace(mpo::AbstractMPO) = map(physicalspace, mpo) +physicalspace(mpo::AbstractMPO) = map(Base.Fix1(physicalspace, mpo), eachsite(mpo)) for ftype in (:spacetype, :sectortype, :storagetype) @eval TensorKit.$ftype(mpo::AbstractMPO) = $ftype(typeof(mpo)) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index a4dd84cb9..cf5c50710 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -54,6 +54,7 @@ DenseMPO(mpo::MPO) = mpo isa DenseMPO ? copy(mpo) : MPO(map(TensorMap, parent(mp # ------- Base.parent(mpo::MPO) = mpo.O Base.copy(mpo::MPO) = MPO(copy.(parent(mpo))) +eachsite(mpo::InfiniteMPO) = PeriodicArray(eachindex(mpo)) function Base.similar(mpo::MPO{<:MPOTensor}, ::Type{O}, L::Int) where {O <: MPOTensor} return MPO(similar(parent(mpo), O, L)) diff --git a/src/states/abstractmps.jl b/src/states/abstractmps.jl index 87500ba28..fb405e19a 100644 --- a/src/states/abstractmps.jl +++ b/src/states/abstractmps.jl @@ -187,6 +187,7 @@ Return the virtual space of the bond to the left of sites `pos`. function left_virtualspace end left_virtualspace(A::GenericMPSTensor) = space(A, 1) left_virtualspace(O::MPOTensor) = space(O, 1) +left_virtualspace(ψ::AbstractMPS) = map(Base.Fix1(left_virtualspace, ψ), eachsite(ψ)) """ right_virtualspace(ψ::AbstractMPS, [pos=1:length(ψ)]) @@ -200,6 +201,7 @@ Return the virtual space of the bond to the right of site(s) `pos`. function right_virtualspace end right_virtualspace(A::GenericMPSTensor) = space(A, numind(A))' right_virtualspace(O::MPOTensor) = space(O, 4)' +right_virtualspace(ψ::AbstractMPS) = map(Base.Fix1(right_virtualspace, ψ), eachsite(ψ)) """ physicalspace(ψ::AbstractMPS, [pos=1:length(ψ)]) @@ -211,6 +213,7 @@ physicalspace(A::MPSTensor) = space(A, 2) physicalspace(A::GenericMPSTensor) = prod(x -> space(A, x), 2:(numind(A) - 1)) physicalspace(O::MPOTensor) = space(O, 2) physicalspace(O::AbstractBlockTensorMap{<:Any, <:Any, 2, 2}) = only(space(O, 2)) +physicalspace(ψ::AbstractMPS) = map(Base.Fix1(physicalspace, ψ), eachsite(ψ)) """ eachsite(state::AbstractMPS) diff --git a/src/states/finitemps.jl b/src/states/finitemps.jl index aad998e06..1300ceaa4 100644 --- a/src/states/finitemps.jl +++ b/src/states/finitemps.jl @@ -394,7 +394,6 @@ function right_virtualspace(ψ::FiniteMPS, n::Integer) _firstspace(ψ.C[n]) end -physicalspace(ψ::FiniteMPS) = physicalspace.(Ref(ψ), 1:length(ψ)) function physicalspace(ψ::FiniteMPS{<:GenericMPSTensor{<:Any, N}}, n::Integer) where {N} N == 1 && return ProductSpace{spacetype(ψ)}() return physicalspace(coalesce(ψ.ALs[n], ψ.ARs[n], ψ.ACs[n])) diff --git a/src/states/infinitemps.jl b/src/states/infinitemps.jl index fe192d80e..b92bd9c40 100644 --- a/src/states/infinitemps.jl +++ b/src/states/infinitemps.jl @@ -265,17 +265,15 @@ end Base.eachindex(ψ::InfiniteMPS) = eachindex(ψ.AL) Base.eachindex(l::IndexStyle, ψ::InfiniteMPS) = eachindex(l, ψ.AL) +eachsite(ψ::InfiniteMPS) = PeriodicArray(eachindex(ψ)) Base.checkbounds(::Type{Bool}, ψ::InfiniteMPS, i::Integer) = true site_type(::Type{<:InfiniteMPS{A}}) where {A} = A bond_type(::Type{<:InfiniteMPS{<:Any, B}}) where {B} = B -left_virtualspace(ψ::InfiniteMPS) = left_virtualspace.(ψ.AL) left_virtualspace(ψ::InfiniteMPS, n::Integer) = left_virtualspace(ψ.AL[n]) -right_virtualspace(ψ::InfiniteMPS) = right_virtualspace.(ψ.AL) right_virtualspace(ψ::InfiniteMPS, n::Integer) = right_virtualspace(ψ.AL[n]) -physicalspace(ψ::InfiniteMPS) = physicalspace.(ψ.AL) physicalspace(ψ::InfiniteMPS, n::Integer) = physicalspace(ψ.AL[n]) # TensorKit.space(ψ::InfiniteMPS{<:MPSTensor}, n::Integer) = space(ψ.AC[n], 2) diff --git a/src/states/multilinemps.jl b/src/states/multilinemps.jl index 8ce092843..6f1199095 100644 --- a/src/states/multilinemps.jl +++ b/src/states/multilinemps.jl @@ -101,5 +101,8 @@ Base.convert(::Type{InfiniteMPS}, st::MultilineMPS) = only(st) Base.eltype(t::MultilineMPS) = eltype(t[1]) Base.copy!(ψ::MultilineMPS, ϕ::MultilineMPS) = (copy!.(parent(ψ), parent(ϕ)); ψ) -left_virtualspace(t::MultilineMPS, i::Int, j::Int) = left_virtualspace(t[i], j) -right_virtualspace(t::MultilineMPS, i::Int, j::Int) = right_virtualspace(t[i], j) +for f_space in (:physicalspace, :left_virtualspace, :right_virtualspace) + @eval $f_space(t::MultilineMPS, i::Int, j::Int) = $f_space(t[i], j) + @eval $f_space(t::MultilineMPS, I::CartesianIndex{2}) = $f_space(t, Tuple(I)...) + @eval $f_space(t::MultilineMPS) = map(Base.Fix1($f_space, t), eachindex(t)) +end diff --git a/src/states/quasiparticle_state.jl b/src/states/quasiparticle_state.jl index 008ab05b8..cf476f914 100644 --- a/src/states/quasiparticle_state.jl +++ b/src/states/quasiparticle_state.jl @@ -133,7 +133,7 @@ function Base.convert( tm = TransferMatrix(input.left_gs.AL, input.right_gs.AR) - if input.trivial + if istrivial(input) tm = regularize(tm, l_LR(input.right_gs), r_LR(input.right_gs)) end @@ -180,7 +180,7 @@ function Base.convert( end tm = TransferMatrix(input.right_gs.AR, input.left_gs.AL) - if input.trivial + if istrivial(input) tm = regularize(tm, l_RL(input.right_gs), r_RL(input.right_gs)) end @@ -215,9 +215,18 @@ const MultilineQP{Q <: QP} = Multiline{Q} TensorKit.spacetype(::Union{QP{S}, Type{<:QP{S}}}) where {S} = spacetype(S) TensorKit.sectortype(::Union{QP{S}, Type{<:QP{S}}}) where {S} = sectortype(S) +physicalspace(state::QP, i::Int) = physicalspace(state.left_gs, i) +physicalspace(state::QP) = physicalspace(state.left_gs) left_virtualspace(state::QP, i::Int) = left_virtualspace(state.left_gs, i) +left_virtualspace(state::QP) = map(Base.Fix1(left_virtualspace, state), eachsite(state)) right_virtualspace(state::QP, i::Int) = right_virtualspace(state.right_gs, i) +right_virtualspace(state::QP) = map(Base.Fix1(right_virtualspace, state), eachsite(state)) auxiliaryspace(state::QP) = space(state.Xs[1], 2) +auxiliarysector(state::QP) = only(sectors(auxiliaryspace(state))) +eachsite(state::QP) = eachsite(state.left_gs) + +istopological(qp::QP) = qp.left_gs !== qp.right_gs +istrivial(qp::QP) = !istopological(qp) && isone(auxiliarysector(qp)) Base.copy(a::QP) = copy!(similar(a), a) Base.copyto!(a::QP, b::QP) = copy!(a, b) @@ -227,11 +236,12 @@ function Base.copy!(a::T, b::T) where {T <: QP} end return a end -function Base.getproperty(v::QP, s::Symbol) +Base.@constprop :aggressive function Base.getproperty(qp::QP, s::Symbol) if s == :trivial - return v.left_gs === v.right_gs + Base.depwarn("`qp.trivial` is deprecated in favor of `istrivial` and `istopological`", :trivial) + return !istopological(qp) else - return getfield(v, s) + return getfield(qp, s) end end @@ -396,7 +406,7 @@ function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S <: FiniteMPS} return FiniteMPS(Ls + Rs + Bs; normalize = false) end -function Base.getproperty(exci::MultilineQP, s::Symbol) +Base.@constprop :aggressive function Base.getproperty(exci::MultilineQP, s::Symbol) if s == :momentum return first(exci.data).momentum elseif s == :left_gs @@ -410,6 +420,10 @@ function Base.getproperty(exci::MultilineQP, s::Symbol) end end +# These should really all be the same, so it might make sense to take the first instead +istrivial(exci::MultilineQP) = all(istrivial, exci.data) +istopological(exci::MultilineQP) = all(istopological, exci.data) + # VectorInterface # --------------- diff --git a/src/states/windowmps.jl b/src/states/windowmps.jl index 56c38452a..5c0bab9d0 100644 --- a/src/states/windowmps.jl +++ b/src/states/windowmps.jl @@ -150,11 +150,7 @@ for f in (:physicalspace, :left_virtualspace, :right_virtualspace) @eval $f(ψ::WindowMPS, n::Integer) = n < 1 ? $f(ψ.left_gs, n) : n > length(ψ) ? $f(ψ.right_gs, n - length(ψ)) : $f(ψ.window, n) -end -function physicalspace(ψ::WindowMPS) - return WindowArray( - physicalspace(ψ.left_gs), physicalspace(ψ.window), physicalspace(ψ.right_gs) - ) + @eval $f(ψ::WindowMPS) = WindowArray($f(ψ.left_gs), $f(ψ.window), $f(ψ.right_gs)) end r_RR(ψ::WindowMPS) = r_RR(ψ.right_gs, length(ψ)) l_LL(ψ::WindowMPS) = l_LL(ψ.left_gs, 1) diff --git a/src/transfermatrix/transfermatrix.jl b/src/transfermatrix/transfermatrix.jl index 88030e91d..31a33ab06 100644 --- a/src/transfermatrix/transfermatrix.jl +++ b/src/transfermatrix/transfermatrix.jl @@ -87,6 +87,6 @@ function regularize!(v::MPOTensor, lvec::MPSTensor, rvec::MPSTensor) end function regularize!(v::MPOTensor, lvec::MPSBondTensor, rvec::MPSBondTensor) - return @plansor v[-1 -2; -3 -4] -= τ[6 2; 3 4] * v[3 4; -3 5] * lvec[5; 2] * rvec[-1; 1] * - τ[-2 -4; 1 6] + λ = @plansor lvec[2; 1] * removeunit(removeunit(v, 3), 2)[1; 2] + return add!(v, insertleftunit(insertrightunit(rvec, 1; dual = isdual(space(v, 2))), 3), -λ) end diff --git a/src/utility/windowarray.jl b/src/utility/windowarray.jl index 3dcaa686a..afcdcedbe 100644 --- a/src/utility/windowarray.jl +++ b/src/utility/windowarray.jl @@ -15,9 +15,9 @@ struct WindowArray{T} <: AbstractVector{T} right::PeriodicVector{T} end function WindowArray( - left::PeriodicVector{T}, middle::AbstractVector{T}, right::PeriodicVector{T} + left::AbstractVector{T}, middle::AbstractVector{T}, right::AbstractVector{T} ) where {T} - return WindowArray{T}(left, convert(Vector{T}, middle), right) + return WindowArray{T}(left, middle, right) end # these definitions are a bit iffy, but will do for now diff --git a/test/algorithms.jl b/test/algorithms.jl index 0aac47ad2..b2de0020d 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -475,6 +475,21 @@ module TestAlgorithms @test energies[1] ≈ 0.41047925 atol = 1.0e-4 @test variance(ϕs[1], H) < 1.0e-8 end + @testset "infinite sector convention" begin + g = 4 + H = repeat(transverse_field_ising(Z2Irrep; g = g, L = Inf), 2) + V = Z2Space(0 => 24, 1 => 24) + ψ = InfiniteMPS(physicalspace(H), [V, V]) + ψ, envs = find_groundstate(ψ, H, VUMPS(; tol = 1.0e-10, maxiter = 400)) + + # testing istrivial and istopological + momentum = 0 + exc0, qp0 = excitations(H, QuasiparticleAnsatz(), momentum, ψ; sector = Z2Irrep(0)) + exc1, qp1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ; sector = Z2Irrep(1)) + @test isapprox(first(exc1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy + @test first(exc0) > 2 * first(exc1) + @test variance(qp1[1], H) < 1.0e-8 + end @testset "infinite (mpo)" begin H = repeat(sixvertex(), 2) ψ = InfiniteMPS([ℂ^2, ℂ^2], [ℂ^10, ℂ^10]) diff --git a/test/operators.jl b/test/operators.jl index 0f2b6011f..12f3a9706 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -31,6 +31,15 @@ module TestOperators mpo₁ = FiniteMPO(O₁) # type-unstable for now! mpo₂ = FiniteMPO(O₂) mpo₃ = FiniteMPO(O₃) + + @test @constinferred physicalspace(mpo₁) == fill(V, L) + Vleft = @constinferred left_virtualspace(mpo₁) + Vright = @constinferred right_virtualspace(mpo₂) + for i in 1:L + @test Vleft[i] == left_virtualspace(mpo₁, i) + @test Vright[i] == right_virtualspace(mpo₁, i) + end + @test convert(TensorMap, mpo₁) ≈ O₁ @test convert(TensorMap, -mpo₂) ≈ -O₂ @test convert(TensorMap, @constinferred complex(mpo₃)) ≈ complex(O₃) diff --git a/test/setup.jl b/test/setup.jl index 7a5390827..c769bc430 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -142,32 +142,16 @@ function S_zz(::Type{Z2Irrep}, ::Type{T} = ComplexF64; spin = 1 // 2) where {T < return ZZ end -function transverse_field_ising(::Type{Z2Irrep}, ::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite = false) where {T <: Number} - X = S_x(Z2Irrep, T; spin = 1 // 2) - ZZ = S_zz(Z2Irrep, T; spin = 1 // 2) +function transverse_field_ising(sym::Type{<:Sector} = Trivial, ::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} + X = S_x(sym, T; spin = 1 // 2) + ZZ = S_zz(sym, T; spin = 1 // 2) if L == Inf - lattice = twosite ? PeriodicArray([space(X, 1), space(X, 1)]) : PeriodicArray([space(X, 1)]) - H₁ = InfiniteMPOHamiltonian(lattice, i => -g * X for i in 1:length(lattice)) - H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:length(lattice)) - else - lattice = fill(space(X, 1), L) - H₁ = FiniteMPOHamiltonian(lattice, i => -g * X for i in 1:L) - H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:(L - 1)) - end - return H₁ + H₂ -end - -function transverse_field_ising(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} - X = S_x(Trivial, T; spin = 1 // 2) - ZZ = S_zz(Trivial, T; spin = 1 // 2) - - if L == Inf - lattice = PeriodicArray([ℂ^2]) + lattice = PeriodicArray([space(X, 1)]) H₁ = InfiniteMPOHamiltonian(lattice, i => -g * X for i in 1:1) H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:1) else - lattice = fill(ℂ^2, L) + lattice = fill(space(X, 1), L) H₁ = FiniteMPOHamiltonian(lattice, i => -g * X for i in 1:L) H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => -ZZ for i in 1:(L - 1)) end diff --git a/test/states.jl b/test/states.jl index f763904af..e641c0b99 100644 --- a/test/states.jl +++ b/test/states.jl @@ -21,7 +21,12 @@ module TestStates ComplexF32, ), ] - ψ = FiniteMPS(rand, elt, rand(3:20), d, D) + L = rand(3:20) + ψ = FiniteMPS(rand, elt, L, d, D) + + @test @constinferred physicalspace(ψ) == fill(d, L) + @test all(x -> x ≾ D, @constinferred left_virtualspace(ψ)) + @test all(x -> x ≾ D, @constinferred right_virtualspace(ψ)) ovl = dot(ψ, ψ) @@ -94,6 +99,10 @@ module TestStates ψ = InfiniteMPS([rand(elt, D * d, D), rand(elt, D * d, D)]; tol) + @test physicalspace(ψ) == fill(d, 2) + @test all(x -> x ≾ D, left_virtualspace(ψ)) + @test all(x -> x ≾ D, right_virtualspace(ψ)) + for i in 1:length(ψ) @plansor difference[-1 -2; -3] := ψ.AL[i][-1 -2; 1] * ψ.C[i][1; -3] - ψ.C[i - 1][-1; 1] * ψ.AR[i][1 -2; -3] @@ -121,6 +130,10 @@ module TestStates ]; tol ) + @test physicalspace(ψ) == fill(d, 2, 2) + @test all(x -> x ≾ D, left_virtualspace(ψ)) + @test all(x -> x ≾ D, right_virtualspace(ψ)) + for i in 1:size(ψ, 1), j in 1:size(ψ, 2) @plansor difference[-1 -2; -3] := ψ.AL[i, j][-1 -2; 1] * ψ.C[i, j][1; -3] - ψ.C[i, j - 1][-1; 1] * ψ.AR[i, j][1 -2; -3] @@ -155,6 +168,16 @@ module TestStates # constructor 2 - used to take a "slice" from an infinite mps window_2 = WindowMPS(gs, 10) + P = @constinferred physicalspace(window_2) + Vleft = @constinferred left_virtualspace(window_2) + Vright = @constinferred right_virtualspace(window_2) + + for i in -3:13 + @test physicalspace(window_2, i) == P[i] + @test left_virtualspace(window_2, i) == Vleft[i] + @test right_virtualspace(window_2, i) == Vright[i] + end + # we should logically have that window_1 approximates window_2 ovl = dot(window_1, window_2) @test ovl ≈ 1 atol = 1.0e-8 @@ -206,6 +229,10 @@ module TestStates ϕ₁ = LeftGaugedQP(rand, ψ) ϕ₂ = LeftGaugedQP(rand, ψ) + @test @constinferred physicalspace(ϕ₁) == physicalspace(ψ) + @test @constinferred left_virtualspace(ϕ₁) == left_virtualspace(ψ) + @test @constinferred right_virtualspace(ϕ₁) == right_virtualspace(ψ) + @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) @@ -237,6 +264,15 @@ module TestStates ϕ₁ = LeftGaugedQP(rand, ψ) ϕ₂ = LeftGaugedQP(rand, ψ) + @test @constinferred physicalspace(ϕ₁) == physicalspace(ψ) + @test @constinferred left_virtualspace(ϕ₁) == left_virtualspace(ψ) + @test @constinferred right_virtualspace(ϕ₁) == right_virtualspace(ψ) + for i in 1:period + @test physicalspace(ψ, i) == physicalspace(ϕ₁, i) + @test left_virtualspace(ψ, i) == left_virtualspace(ϕ₁, i) + @test right_virtualspace(ψ, i) == right_virtualspace(ϕ₁, i) + end + @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) From 2f1108ea8614344f0f082500de2f92a88a50846d Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:43:44 +0200 Subject: [PATCH 38/54] Revert "Merge branch 'master' of https://github.com/QuantumKitHub/MPSKit.jl into boris-MTK-compat" This reverts commit 2c96ab1f2707da1c01dd0d81e89ef4ff1bfcec1d, reversing changes made to 8cf041ff823492e41fd5d812fe5b03cf45ac54aa. --- .github/workflows/Tests.yml | 1 - src/algorithms/ED.jl | 2 +- src/algorithms/correlators.jl | 5 +- .../excitation/exci_transfer_system.jl | 18 -- .../excitation/quasiparticleexcitation.jl | 27 +-- src/algorithms/fixedpoint.jl | 8 +- .../groundstate/gradient_grassmann.jl | 2 +- src/algorithms/toolbox.jl | 2 +- src/operators/mpo.jl | 2 +- src/operators/mpohamiltonian.jl | 27 +-- src/utility/utility.jl | 13 -- test/algorithms.jl | 2 +- test/multifusion.jl | 176 ------------------ test/runtests.jl | 3 - test/setup.jl | 2 +- 15 files changed, 28 insertions(+), 262 deletions(-) delete mode 100644 test/multifusion.jl diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index b51cf1d75..0b8e27800 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -29,7 +29,6 @@ jobs: - states - operators - algorithms - - multifusion - other os: - ubuntu-latest diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index d1c86a745..a3a5c4036 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -44,7 +44,7 @@ function exact_diagonalization( # fuse from left to right ALs = Vector{Union{Missing, TA}}(missing, L) - left = spacetype(H)(one(sector) => 1) # might need to be rightone, leave this for now + left = oneunit(spacetype(H)) for i in 1:(middle_site - 1) P = physicalspace(H, i) ALs[i] = isomorphism(T, left ⊗ P ← fuse(left ⊗ P)) diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index 8660d08d7..acd3adebf 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -39,9 +39,8 @@ function correlator( end function correlator( - state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any, S, 2, 2}, i::Int, - j + state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any, S, 2, 2}, i::Int, j ) where {S} - O₁, O₂ = decompose_localmpo(add_util_mpoleg(O₁₂)) + O₁, O₂ = decompose_localmpo(add_util_leg(O₁₂)) return correlator(state, O₁, O₂, i, j) end diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 8fb975f45..c2b4b217d 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -21,15 +21,6 @@ function left_excitation_transfer_system( start = scale!(last(found[1:i] * T), cis(-mom * len)) if istrivial(exci) && isidentitylevel(H, i) regularize!(start, ρ_right, ρ_left) - if exci.trivial && isidentitylevel(H, i) - # not using braiding tensors here, leads to extra leg - util = similar(exci.left_gs.AL[i], first(left_virtualspace(H[i]))) - fill_data!(util, one) - @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * - util[1] * - r_RL(exci.right_gs)[3; 2] * - l_RL(exci.right_gs)[-1; -4] * - conj(util[-2]) end found[i] = add!(start, GBL[i]) @@ -80,15 +71,6 @@ function right_excitation_transfer_system( start = scale!(first(T * found[i:odim]), cis(mom * len)) if istrivial(exci) && isidentitylevel(H, i) regularize!(start, ρ_left, ρ_right) - if exci.trivial && isidentitylevel(H, i) - # not using braiding tensors here, leads to extra leg - util = similar(exci.right_gs.AL[i], first(left_virtualspace(H[i]))) - fill_data!(util, one) - @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * - conj(util[1]) * - l_LR(exci.right_gs)[3; 2] * - r_LR(exci.right_gs)[-1; -4] * - util[-2] end found[i] = add!(start, GBR[i]) diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index fe1de0615..0eb400258 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -46,6 +46,7 @@ end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; num::Int = 1) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) H_eff = EffectiveExcitationHamiltonian(H, lenvs, renvs, E) + Es, ϕs, convhist = eigsolve(ϕ₀, num, :SR, alg.alg) do ϕ return H_eff(ϕ; alg.alg_environments...) end @@ -104,25 +105,13 @@ function excitations( verbosity = Defaults.verbosity, num = 1, sector = one(sectortype(lmps)), parallel = true, kwargs... ) - # wrapper to evaluate sector as positional argument - Toutput = let - function wrapper(H, alg, p, lmps, lenvs, rmps, renvs, sector; kwargs...) - return excitations( - H, alg, p, lmps, lenvs, rmps, renvs; - sector, kwargs... - ) - end - Core.Compiler.return_type( - wrapper, - Tuple{ - typeof(H), typeof(alg), - eltype(momenta), typeof(lmps), - typeof(lenvs), - typeof(rmps), typeof(renvs), typeof(sector), - } - ) - end - + Toutput = Core.Compiler.return_type( + excitations, + Tuple{ + typeof(H), typeof(alg), eltype(momenta), typeof(lmps), + typeof(lenvs), typeof(rmps), typeof(renvs), + } + ) results = similar(momenta, Toutput) scheduler = parallel ? :greedy : :serial tmap!(results, momenta; scheduler) do momentum diff --git a/src/algorithms/fixedpoint.jl b/src/algorithms/fixedpoint.jl index 188c65559..81e7c69d9 100644 --- a/src/algorithms/fixedpoint.jl +++ b/src/algorithms/fixedpoint.jl @@ -4,14 +4,14 @@ fixedpoint(A, x₀, which::Symbol; kwargs...) -> val, vec fixedpoint(A, x₀, which::Symbol, alg) -> val, vec -Compute the fixed point of a linear operator `A` using the specified eigensolver `alg`. The +Compute the fixedpoint of a linear operator `A` using the specified eigensolver `alg`. The fixedpoint is assumed to be unique. """ function fixedpoint(A, x₀, which::Symbol, alg::Lanczos) vals, vecs, info = eigsolve(A, x₀, 1, which, alg) if info.converged == 0 - @warnv 1 "fixed point not converged after $(info.numiter) iterations: normres = $(info.normres[1])" + @warnv 1 "fixedpoint not converged after $(info.numiter) iterations: normres = $(info.normres[1])" end return vals[1], vecs[1] @@ -21,10 +21,10 @@ function fixedpoint(A, x₀, which::Symbol, alg::Arnoldi) TT, vecs, vals, info = schursolve(A, x₀, 1, which, alg) if info.converged == 0 - @warnv 1 "fixed point not converged after $(info.numiter) iterations: normres = $(info.normres[1])" + @warnv 1 "fixedpoint not converged after $(info.numiter) iterations: normres = $(info.normres[1])" end if size(TT, 2) > 1 && TT[2, 1] != 0 - @warnv 1 "non-unique fixed point detected" + @warnv 1 "non-unique fixedpoint detected" end return vals[1], vecs[1] diff --git a/src/algorithms/groundstate/gradient_grassmann.jl b/src/algorithms/groundstate/gradient_grassmann.jl index 2088a6763..478fe339c 100644 --- a/src/algorithms/groundstate/gradient_grassmann.jl +++ b/src/algorithms/groundstate/gradient_grassmann.jl @@ -59,7 +59,7 @@ function find_groundstate( ψ::S, H, alg::GradientGrassmann, envs::P = environments(ψ, H) )::Tuple{S, P, Float64} where {S, P} !isa(ψ, FiniteMPS) || dim(ψ.C[end]) == 1 || - @warn "This is not fully supported - split the mps up in a sum of mps's and optimize separately" + @warn "This is not fully supported - split the mps up in a sum of mps's and optimize seperately" normalize!(ψ) fg(x) = GrassmannMPS.fg(x, H, envs) diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 8e2d0a25e..150f276fb 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -28,7 +28,7 @@ Return the density matrix of the infinite temperature state for a given Hamilton This is the identity matrix in the physical space, and the identity in the auxiliary space. """ function infinite_temperature_density_matrix(H::MPOHamiltonian) - V = first(left_virtualspace(H[1])) + V = oneunit(spacetype(H)) W = map(1:length(H)) do site return BraidingTensor{scalartype(H)}(physicalspace(H, site), V) end diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index cf5c50710..8bc220f58 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -27,7 +27,7 @@ function FiniteMPO(Os::AbstractVector{O}) where {O} end function FiniteMPO(O::AbstractTensorMap{T, S, N, N}) where {T, S, N} - return FiniteMPO(decompose_localmpo(add_util_mpoleg(O))) + return FiniteMPO(decompose_localmpo(add_util_leg(O))) end """ diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 842335862..0727f4ca3 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -384,19 +384,13 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_opera E = scalartype(T) S = spacetype(T) - # avoid using one(S) - somempo = local_mpos[1].second[1] - sp_oneleg = space(somempo, 1) - - _oneunit = oneunit(sp_oneleg) # should be rightoneunit, but MPOHamiltonians are always diagonal for now - virtualsumspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) - virtualsumspaces[1] = SumSpace(fill(_oneunit, 1)) - virtualsumspaces[end] = SumSpace(fill(_oneunit, 1)) + virtualsumspaces[1] = SumSpace(fill(oneunit(S), 1)) + virtualsumspaces[end] = SumSpace(fill(oneunit(S), 1)) for i in 1:(length(lattice) - 1) n_channels = maximum(last, nonzero_keys[i]; init = 1) + 1 - V = SumSpace(fill(_oneunit, n_channels)) + V = SumSpace(fill(oneunit(S), n_channels)) if n_channels > 2 for ((key_L, key_R), O) in zip(nonzero_keys[i], nonzero_opps[i]) V[key_R == 0 ? end : key_R] = if O isa Number @@ -474,14 +468,9 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ virtualspaces = PeriodicArray( [Vector{MissingS}(missing, operator_size) for _ in 1:length(nonzero_keys)] ) - # avoid using one(S) - somempo = local_mpos[1].second[1] - sp_oneleg = space(somempo, 1) - - _oneunit = oneunit(sp_oneleg) for V in virtualspaces - V[1] = _oneunit - V[end] = _oneunit + V[1] = oneunit(S) + V[end] = oneunit(S) end # start by filling in tensors -> space information available @@ -527,7 +516,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ end end - foreach(Base.Fix2(replace!, missing => _oneunit), virtualspaces) + foreach(Base.Fix2(replace!, missing => oneunit(S)), virtualspaces) virtualsumspaces = map(virtualspaces) do V return SumSpace(collect(S, V)) end @@ -669,7 +658,7 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(space(first(H₁[1]), 1)) # should also be rightoneunit, but is currently diagonal + Vtriv = oneunit(spacetype(H₁)) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) @@ -693,7 +682,7 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(space(first(H₁[1]), 1)) + Vtriv = oneunit(spacetype(H₁)) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) B = cat(H₁[i].B, H₂[i].B; dims = 1) diff --git a/src/utility/utility.jl b/src/utility/utility.jl index de86f7d8a..93181d667 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -69,19 +69,6 @@ function add_util_leg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, return util_front * tensor * util_back end -function add_util_mpoleg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, N2} - # separate function for mpo because add_util_leg is also used for mps from tensors - # and the additional legs there can be different depending on the fusion tree - - #TODO?: might need a left and right utility space if MPO has module virtual space (doesn't happen for hamiltonians) - ou = S(one(first(blocksectors(tensor))) => 1) - - util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) - util_back = isomorphism(storagetype(tensor), domain(tensor), domain(tensor) * ou) - - return util_front * tensor * util_back -end - function union_split(a::AbstractArray) T = reduce((a, b) -> Union{a, b}, typeof.(a)) nA = similar(a, T) diff --git a/test/algorithms.jl b/test/algorithms.jl index b2de0020d..b009c121b 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -725,7 +725,7 @@ module TestAlgorithms @test expectation_value(ψ, H) ≈ expectation_value(ψ, 1 => -g * S_x()) + expectation_value(ψ, (1, 2) => -S_zz()) - Z_mpo = MPSKit.add_util_mpoleg(S_z()) + Z_mpo = MPSKit.add_util_leg(S_z()) G = correlator(ψ, Z_mpo, Z_mpo, 1, 2:5) G2 = correlator(ψ, S_zz(), 1, 3:2:5) @test isapprox(G[2], G2[1], atol = 1.0e-2) diff --git a/test/multifusion.jl b/test/multifusion.jl deleted file mode 100644 index a855d3c0f..000000000 --- a/test/multifusion.jl +++ /dev/null @@ -1,176 +0,0 @@ -println(" ------------------ -| Multifusion | ------------------ -") - -module TestMultifusion - - using ..TestSetup - using Test, TestExtras - using MPSKit - using TensorKit - - I = IsingBimodule - - M = I(1, 2, 0) # σ - Mop = I(2, 1, 0) - C0 = I(1, 1, 0) # unit of C - C1 = I(1, 1, 1) - D0 = I(2, 2, 0) # unit of D - D1 = I(2, 2, 1) - V = Vect[I](M => 1) - Vop = Vect[I](Mop => 1) - PD = Vect[I](D0 => 1, D1 => 1) - PC = Vect[I](C0 => 1, C1 => 1) - - bad_fusions = [(PC, PD), (PD, PC), (V, V), (Vop, Vop), (V, PC), (Vop, PD), (V, PD), (Vop, PC), (V, Vop), (Vop, V)] - - flippy(charge::IsingBimodule) = only(charge ⊗ D1) - - function TFIM_multifusion(::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite = false) where {T <: Number} - P = Vect[I](D0 => 1, D1 => 1) - X = zeros(T, P ← P) - for (s, f) in fusiontrees(X) - isone(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g - end - ZZ = zeros(T, P^2 ← P^2) - for (s, f) in fusiontrees(ZZ) - s.uncoupled == map(flippy, f.uncoupled) ? ZZ[s, f] .= 1 : nothing - end - - if L == Inf - lattice = twosite ? PeriodicArray([P, P]) : PeriodicArray([P]) - H₁ = InfiniteMPOHamiltonian(lattice, i => X for i in 1:length(lattice)) - H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:length(lattice)) - else - lattice = fill(P, L) - H₁ = FiniteMPOHamiltonian(lattice, i => X for i in 1:L) - H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:(L - 1)) - end - return H₁ + H₂ - end - - @testset "InfiniteMPS construction" begin - for (P, V) in bad_fusions - @test_throws ArgumentError("invalid fusion channel") InfiniteMPS([P], [V]) - end - end - - @testset "FiniteMPS construction" begin - Prand, Vrand = rand(bad_fusions) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") FiniteMPS(2, Prand, Vrand) - - for (P, V) in bad_fusions - @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V; left = V, right = V) - end - end - - @testset "Exact diagonalization" begin - H = TFIM_multifusion(; g = 0, L = 4) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") exact_diagonalization(H) - - E, ψ = exact_diagonalization(H; sector = D0) # test that it runs with kwarg - @test isapprox(E, [-3, -1, 1, 3]; atol = 1.0e-6) - end - - @testset "Finite systems" begin - L = 6 - g = 4 - H = TFIM_multifusion(; g = g, L = L) - V = Vect[I](M => 8) - init = FiniteMPS(L, PD, V; left = Vect[I](M => 1), right = Vect[I](M => 1)) - v₀ = variance(init, H) - ψ, envs, δ = find_groundstate(init, H, DMRG()) - v = variance(ψ, H) - E = expectation_value(ψ, H, envs) - - ψ2, envs2, δ2 = find_groundstate(init, H, DMRG2(; trscheme = truncbelow(1.0e-6))) - v2 = variance(ψ2, H) - E2 = expectation_value(ψ2, H, envs2) - - @test δ ≈ 0 atol=1e-3 - @test δ2 ≈ 0 atol=1e-3 - @test v < v₀ && v2 < v₀ - - @test isapprox(E, E2; atol = 1.0e-6) - - ED, _ = exact_diagonalization(H; sector = D0) - @test isapprox(E, first(ED); atol = 1.0e-6) - - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) - excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C1, num=1) # testing sector kwarg - @test 0 < variance(qp[1], H) < 1e-8 - - excE_DM, qp_DM = excitations(H, FiniteExcited(;gsalg=DMRG2(; trscheme=truncbelow(1e-6))), ψ2;num=1) - @test isapprox(first(excE_DM), first(excE) + E2; atol = 1.0e-6) - end - - @testset "Infinite systems" begin - # Multifusion: effectively studying the KW dual in SSB phase - g = 1 / 4 - H = TFIM_multifusion(; g = g, L = Inf, twosite = true) - V = Vect[I](M => 48) - init = InfiniteMPS([PD, PD], [V, V]) - v₀ = variance(init, H) - tol = 1.0e-10 - ψ, envs, δ = find_groundstate(init, H, IDMRG(;tol=tol,maxiter=400)) - E = expectation_value(ψ, H, envs) - v = variance(ψ, H) - - ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol=tol, trscheme = truncbelow(1.0e-6), maxiter=400)) - E2 = expectation_value(ψ2, H, envs2) - v2 = variance(ψ2, H) - - ψ3, envs3, δ3 = find_groundstate(init, H, VUMPS(;tol=tol, maxiter=400)) - E3 = expectation_value(ψ3, H, envs3) - v3 = variance(ψ3, H) - - @test isapprox(E, E2; atol = 1.0e-6) - @test isapprox(E, E3; atol = 1.0e-6) - for delta in [δ, δ2, δ3] - @test delta ≈ 0 atol=1e-3 - end - for var in [v, v2, v3] - @test var < v₀ - @test var < 1e-8 - end - - @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) - @test first(transfer_spectrum(ψ2; sector = C0)) ≈ 1 # testing sector kwarg - @test !(abs(first(transfer_spectrum(ψ2; sector = C1))) ≈ 1) # testing injectivity - - @test only(keys(entanglement_spectrum(ψ2))) == M - - momentum = 0 - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) - excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) - @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test variance(qpC1[1], H) < 1e-8 - - # diagonal test (M = D): injective GS in symmetric phase - Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite = true) - Vdiag = Vect[I](D0 => 24, D1 => 24) - initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) - gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS(;tol=tol, maxiter=400)) - Ediag = expectation_value(gsdiag, Hdual, envsdiag) - - excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D1) - @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test variance(qpD1[1], Hdual) < 1e-8 - - # comparison to Z2 Ising: injective in symmetric phase - HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) - VZ2 = Z2Space(0 => 24, 1 => 24) - PZ2 = Z2Space(0 => 1, 1 => 1) - initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) - gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS(;tol=tol, maxiter=400)) - EZ2 = expectation_value(gsZ2, HZ2, envsZ2) - @test isapprox(EZ2, Ediag; atol = 1.0e-6) - - excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(1)) - @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-6) - @test variance(qpZ2_1[1], HZ2) < 1e-8 - end - -end # module TestMultifusion diff --git a/test/runtests.jl b/test/runtests.jl index 2d0e6adb4..556b26dec 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,9 +21,6 @@ include("setup.jl") if GROUP == "ALL" || GROUP == "ALGORITHMS" @time include("algorithms.jl") end - if GROUP == "ALL" || GROUP == "MULTIFUSION" - @time include("multifusion.jl") - end if GROUP == "ALL" || GROUP == "OTHER" @time include("other.jl") end diff --git a/test/setup.jl b/test/setup.jl index c769bc430..a6b07bbc0 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -287,7 +287,7 @@ function kitaev_model(; t = 1.0, mu = 1.0, Delta = 1.0, L = Inf) else lattice = fill(space(TB, 1), L) onsite_terms = ((i,) => CP for i in 1:L) - twosite_terms = ((i, i + 1) => TB + SC for i in 1:(L - 1)) + twosite_terms = ((i, i + 1) => TP + SC for i in 1:(L - 1)) terms = Iterators.flatten(twosite_terms, onsite_terms) return FiniteMPOHamiltonian(lattice, terms) end From 8d83a82392e3a48394597f429e1f6f5e7d761ab1 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:46:06 +0200 Subject: [PATCH 39/54] Revert "Revert "Merge branch 'master' of https://github.com/QuantumKitHub/MPSKit.jl into boris-MTK-compat"" This reverts commit 2f1108ea8614344f0f082500de2f92a88a50846d. --- .github/workflows/Tests.yml | 1 + src/algorithms/ED.jl | 2 +- src/algorithms/correlators.jl | 5 +- .../excitation/exci_transfer_system.jl | 18 ++ .../excitation/quasiparticleexcitation.jl | 27 ++- src/algorithms/fixedpoint.jl | 8 +- .../groundstate/gradient_grassmann.jl | 2 +- src/algorithms/toolbox.jl | 2 +- src/operators/mpo.jl | 2 +- src/operators/mpohamiltonian.jl | 27 ++- src/utility/utility.jl | 13 ++ test/algorithms.jl | 2 +- test/multifusion.jl | 176 ++++++++++++++++++ test/runtests.jl | 3 + test/setup.jl | 2 +- 15 files changed, 262 insertions(+), 28 deletions(-) create mode 100644 test/multifusion.jl diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index 0b8e27800..b51cf1d75 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -29,6 +29,7 @@ jobs: - states - operators - algorithms + - multifusion - other os: - ubuntu-latest diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index a3a5c4036..d1c86a745 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -44,7 +44,7 @@ function exact_diagonalization( # fuse from left to right ALs = Vector{Union{Missing, TA}}(missing, L) - left = oneunit(spacetype(H)) + left = spacetype(H)(one(sector) => 1) # might need to be rightone, leave this for now for i in 1:(middle_site - 1) P = physicalspace(H, i) ALs[i] = isomorphism(T, left ⊗ P ← fuse(left ⊗ P)) diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index acd3adebf..8660d08d7 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -39,8 +39,9 @@ function correlator( end function correlator( - state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any, S, 2, 2}, i::Int, j + state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any, S, 2, 2}, i::Int, + j ) where {S} - O₁, O₂ = decompose_localmpo(add_util_leg(O₁₂)) + O₁, O₂ = decompose_localmpo(add_util_mpoleg(O₁₂)) return correlator(state, O₁, O₂, i, j) end diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index c2b4b217d..8fb975f45 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -21,6 +21,15 @@ function left_excitation_transfer_system( start = scale!(last(found[1:i] * T), cis(-mom * len)) if istrivial(exci) && isidentitylevel(H, i) regularize!(start, ρ_right, ρ_left) + if exci.trivial && isidentitylevel(H, i) + # not using braiding tensors here, leads to extra leg + util = similar(exci.left_gs.AL[i], first(left_virtualspace(H[i]))) + fill_data!(util, one) + @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * + util[1] * + r_RL(exci.right_gs)[3; 2] * + l_RL(exci.right_gs)[-1; -4] * + conj(util[-2]) end found[i] = add!(start, GBL[i]) @@ -71,6 +80,15 @@ function right_excitation_transfer_system( start = scale!(first(T * found[i:odim]), cis(mom * len)) if istrivial(exci) && isidentitylevel(H, i) regularize!(start, ρ_left, ρ_right) + if exci.trivial && isidentitylevel(H, i) + # not using braiding tensors here, leads to extra leg + util = similar(exci.right_gs.AL[i], first(left_virtualspace(H[i]))) + fill_data!(util, one) + @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * + conj(util[1]) * + l_LR(exci.right_gs)[3; 2] * + r_LR(exci.right_gs)[-1; -4] * + util[-2] end found[i] = add!(start, GBR[i]) diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index 0eb400258..fe1de0615 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -46,7 +46,6 @@ end function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; num::Int = 1) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) H_eff = EffectiveExcitationHamiltonian(H, lenvs, renvs, E) - Es, ϕs, convhist = eigsolve(ϕ₀, num, :SR, alg.alg) do ϕ return H_eff(ϕ; alg.alg_environments...) end @@ -105,13 +104,25 @@ function excitations( verbosity = Defaults.verbosity, num = 1, sector = one(sectortype(lmps)), parallel = true, kwargs... ) - Toutput = Core.Compiler.return_type( - excitations, - Tuple{ - typeof(H), typeof(alg), eltype(momenta), typeof(lmps), - typeof(lenvs), typeof(rmps), typeof(renvs), - } - ) + # wrapper to evaluate sector as positional argument + Toutput = let + function wrapper(H, alg, p, lmps, lenvs, rmps, renvs, sector; kwargs...) + return excitations( + H, alg, p, lmps, lenvs, rmps, renvs; + sector, kwargs... + ) + end + Core.Compiler.return_type( + wrapper, + Tuple{ + typeof(H), typeof(alg), + eltype(momenta), typeof(lmps), + typeof(lenvs), + typeof(rmps), typeof(renvs), typeof(sector), + } + ) + end + results = similar(momenta, Toutput) scheduler = parallel ? :greedy : :serial tmap!(results, momenta; scheduler) do momentum diff --git a/src/algorithms/fixedpoint.jl b/src/algorithms/fixedpoint.jl index 81e7c69d9..188c65559 100644 --- a/src/algorithms/fixedpoint.jl +++ b/src/algorithms/fixedpoint.jl @@ -4,14 +4,14 @@ fixedpoint(A, x₀, which::Symbol; kwargs...) -> val, vec fixedpoint(A, x₀, which::Symbol, alg) -> val, vec -Compute the fixedpoint of a linear operator `A` using the specified eigensolver `alg`. The +Compute the fixed point of a linear operator `A` using the specified eigensolver `alg`. The fixedpoint is assumed to be unique. """ function fixedpoint(A, x₀, which::Symbol, alg::Lanczos) vals, vecs, info = eigsolve(A, x₀, 1, which, alg) if info.converged == 0 - @warnv 1 "fixedpoint not converged after $(info.numiter) iterations: normres = $(info.normres[1])" + @warnv 1 "fixed point not converged after $(info.numiter) iterations: normres = $(info.normres[1])" end return vals[1], vecs[1] @@ -21,10 +21,10 @@ function fixedpoint(A, x₀, which::Symbol, alg::Arnoldi) TT, vecs, vals, info = schursolve(A, x₀, 1, which, alg) if info.converged == 0 - @warnv 1 "fixedpoint not converged after $(info.numiter) iterations: normres = $(info.normres[1])" + @warnv 1 "fixed point not converged after $(info.numiter) iterations: normres = $(info.normres[1])" end if size(TT, 2) > 1 && TT[2, 1] != 0 - @warnv 1 "non-unique fixedpoint detected" + @warnv 1 "non-unique fixed point detected" end return vals[1], vecs[1] diff --git a/src/algorithms/groundstate/gradient_grassmann.jl b/src/algorithms/groundstate/gradient_grassmann.jl index 478fe339c..2088a6763 100644 --- a/src/algorithms/groundstate/gradient_grassmann.jl +++ b/src/algorithms/groundstate/gradient_grassmann.jl @@ -59,7 +59,7 @@ function find_groundstate( ψ::S, H, alg::GradientGrassmann, envs::P = environments(ψ, H) )::Tuple{S, P, Float64} where {S, P} !isa(ψ, FiniteMPS) || dim(ψ.C[end]) == 1 || - @warn "This is not fully supported - split the mps up in a sum of mps's and optimize seperately" + @warn "This is not fully supported - split the mps up in a sum of mps's and optimize separately" normalize!(ψ) fg(x) = GrassmannMPS.fg(x, H, envs) diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 150f276fb..8e2d0a25e 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -28,7 +28,7 @@ Return the density matrix of the infinite temperature state for a given Hamilton This is the identity matrix in the physical space, and the identity in the auxiliary space. """ function infinite_temperature_density_matrix(H::MPOHamiltonian) - V = oneunit(spacetype(H)) + V = first(left_virtualspace(H[1])) W = map(1:length(H)) do site return BraidingTensor{scalartype(H)}(physicalspace(H, site), V) end diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index 8bc220f58..cf5c50710 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -27,7 +27,7 @@ function FiniteMPO(Os::AbstractVector{O}) where {O} end function FiniteMPO(O::AbstractTensorMap{T, S, N, N}) where {T, S, N} - return FiniteMPO(decompose_localmpo(add_util_leg(O))) + return FiniteMPO(decompose_localmpo(add_util_mpoleg(O))) end """ diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 0727f4ca3..842335862 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -384,13 +384,19 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_opera E = scalartype(T) S = spacetype(T) + # avoid using one(S) + somempo = local_mpos[1].second[1] + sp_oneleg = space(somempo, 1) + + _oneunit = oneunit(sp_oneleg) # should be rightoneunit, but MPOHamiltonians are always diagonal for now + virtualsumspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) - virtualsumspaces[1] = SumSpace(fill(oneunit(S), 1)) - virtualsumspaces[end] = SumSpace(fill(oneunit(S), 1)) + virtualsumspaces[1] = SumSpace(fill(_oneunit, 1)) + virtualsumspaces[end] = SumSpace(fill(_oneunit, 1)) for i in 1:(length(lattice) - 1) n_channels = maximum(last, nonzero_keys[i]; init = 1) + 1 - V = SumSpace(fill(oneunit(S), n_channels)) + V = SumSpace(fill(_oneunit, n_channels)) if n_channels > 2 for ((key_L, key_R), O) in zip(nonzero_keys[i], nonzero_opps[i]) V[key_R == 0 ? end : key_R] = if O isa Number @@ -468,9 +474,14 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ virtualspaces = PeriodicArray( [Vector{MissingS}(missing, operator_size) for _ in 1:length(nonzero_keys)] ) + # avoid using one(S) + somempo = local_mpos[1].second[1] + sp_oneleg = space(somempo, 1) + + _oneunit = oneunit(sp_oneleg) for V in virtualspaces - V[1] = oneunit(S) - V[end] = oneunit(S) + V[1] = _oneunit + V[end] = _oneunit end # start by filling in tensors -> space information available @@ -516,7 +527,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ end end - foreach(Base.Fix2(replace!, missing => oneunit(S)), virtualspaces) + foreach(Base.Fix2(replace!, missing => _oneunit), virtualspaces) virtualsumspaces = map(virtualspaces) do V return SumSpace(collect(S, V)) end @@ -658,7 +669,7 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(spacetype(H₁)) + Vtriv = oneunit(space(first(H₁[1]), 1)) # should also be rightoneunit, but is currently diagonal for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) @@ -682,7 +693,7 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(spacetype(H₁)) + Vtriv = oneunit(space(first(H₁[1]), 1)) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) B = cat(H₁[i].B, H₂[i].B; dims = 1) diff --git a/src/utility/utility.jl b/src/utility/utility.jl index 93181d667..de86f7d8a 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -69,6 +69,19 @@ function add_util_leg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, return util_front * tensor * util_back end +function add_util_mpoleg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, N2} + # separate function for mpo because add_util_leg is also used for mps from tensors + # and the additional legs there can be different depending on the fusion tree + + #TODO?: might need a left and right utility space if MPO has module virtual space (doesn't happen for hamiltonians) + ou = S(one(first(blocksectors(tensor))) => 1) + + util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) + util_back = isomorphism(storagetype(tensor), domain(tensor), domain(tensor) * ou) + + return util_front * tensor * util_back +end + function union_split(a::AbstractArray) T = reduce((a, b) -> Union{a, b}, typeof.(a)) nA = similar(a, T) diff --git a/test/algorithms.jl b/test/algorithms.jl index b009c121b..b2de0020d 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -725,7 +725,7 @@ module TestAlgorithms @test expectation_value(ψ, H) ≈ expectation_value(ψ, 1 => -g * S_x()) + expectation_value(ψ, (1, 2) => -S_zz()) - Z_mpo = MPSKit.add_util_leg(S_z()) + Z_mpo = MPSKit.add_util_mpoleg(S_z()) G = correlator(ψ, Z_mpo, Z_mpo, 1, 2:5) G2 = correlator(ψ, S_zz(), 1, 3:2:5) @test isapprox(G[2], G2[1], atol = 1.0e-2) diff --git a/test/multifusion.jl b/test/multifusion.jl new file mode 100644 index 000000000..a855d3c0f --- /dev/null +++ b/test/multifusion.jl @@ -0,0 +1,176 @@ +println(" +----------------- +| Multifusion | +----------------- +") + +module TestMultifusion + + using ..TestSetup + using Test, TestExtras + using MPSKit + using TensorKit + + I = IsingBimodule + + M = I(1, 2, 0) # σ + Mop = I(2, 1, 0) + C0 = I(1, 1, 0) # unit of C + C1 = I(1, 1, 1) + D0 = I(2, 2, 0) # unit of D + D1 = I(2, 2, 1) + V = Vect[I](M => 1) + Vop = Vect[I](Mop => 1) + PD = Vect[I](D0 => 1, D1 => 1) + PC = Vect[I](C0 => 1, C1 => 1) + + bad_fusions = [(PC, PD), (PD, PC), (V, V), (Vop, Vop), (V, PC), (Vop, PD), (V, PD), (Vop, PC), (V, Vop), (Vop, V)] + + flippy(charge::IsingBimodule) = only(charge ⊗ D1) + + function TFIM_multifusion(::Type{T} = ComplexF64; g = 1.0, L = Inf, twosite = false) where {T <: Number} + P = Vect[I](D0 => 1, D1 => 1) + X = zeros(T, P ← P) + for (s, f) in fusiontrees(X) + isone(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g + end + ZZ = zeros(T, P^2 ← P^2) + for (s, f) in fusiontrees(ZZ) + s.uncoupled == map(flippy, f.uncoupled) ? ZZ[s, f] .= 1 : nothing + end + + if L == Inf + lattice = twosite ? PeriodicArray([P, P]) : PeriodicArray([P]) + H₁ = InfiniteMPOHamiltonian(lattice, i => X for i in 1:length(lattice)) + H₂ = InfiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:length(lattice)) + else + lattice = fill(P, L) + H₁ = FiniteMPOHamiltonian(lattice, i => X for i in 1:L) + H₂ = FiniteMPOHamiltonian(lattice, (i, i + 1) => ZZ for i in 1:(L - 1)) + end + return H₁ + H₂ + end + + @testset "InfiniteMPS construction" begin + for (P, V) in bad_fusions + @test_throws ArgumentError("invalid fusion channel") InfiniteMPS([P], [V]) + end + end + + @testset "FiniteMPS construction" begin + Prand, Vrand = rand(bad_fusions) + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") FiniteMPS(2, Prand, Vrand) + + for (P, V) in bad_fusions + @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V; left = V, right = V) + end + end + + @testset "Exact diagonalization" begin + H = TFIM_multifusion(; g = 0, L = 4) + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") exact_diagonalization(H) + + E, ψ = exact_diagonalization(H; sector = D0) # test that it runs with kwarg + @test isapprox(E, [-3, -1, 1, 3]; atol = 1.0e-6) + end + + @testset "Finite systems" begin + L = 6 + g = 4 + H = TFIM_multifusion(; g = g, L = L) + V = Vect[I](M => 8) + init = FiniteMPS(L, PD, V; left = Vect[I](M => 1), right = Vect[I](M => 1)) + v₀ = variance(init, H) + ψ, envs, δ = find_groundstate(init, H, DMRG()) + v = variance(ψ, H) + E = expectation_value(ψ, H, envs) + + ψ2, envs2, δ2 = find_groundstate(init, H, DMRG2(; trscheme = truncbelow(1.0e-6))) + v2 = variance(ψ2, H) + E2 = expectation_value(ψ2, H, envs2) + + @test δ ≈ 0 atol=1e-3 + @test δ2 ≈ 0 atol=1e-3 + @test v < v₀ && v2 < v₀ + + @test isapprox(E, E2; atol = 1.0e-6) + + ED, _ = exact_diagonalization(H; sector = D0) + @test isapprox(E, first(ED); atol = 1.0e-6) + + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) + excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C1, num=1) # testing sector kwarg + @test 0 < variance(qp[1], H) < 1e-8 + + excE_DM, qp_DM = excitations(H, FiniteExcited(;gsalg=DMRG2(; trscheme=truncbelow(1e-6))), ψ2;num=1) + @test isapprox(first(excE_DM), first(excE) + E2; atol = 1.0e-6) + end + + @testset "Infinite systems" begin + # Multifusion: effectively studying the KW dual in SSB phase + g = 1 / 4 + H = TFIM_multifusion(; g = g, L = Inf, twosite = true) + V = Vect[I](M => 48) + init = InfiniteMPS([PD, PD], [V, V]) + v₀ = variance(init, H) + tol = 1.0e-10 + ψ, envs, δ = find_groundstate(init, H, IDMRG(;tol=tol,maxiter=400)) + E = expectation_value(ψ, H, envs) + v = variance(ψ, H) + + ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol=tol, trscheme = truncbelow(1.0e-6), maxiter=400)) + E2 = expectation_value(ψ2, H, envs2) + v2 = variance(ψ2, H) + + ψ3, envs3, δ3 = find_groundstate(init, H, VUMPS(;tol=tol, maxiter=400)) + E3 = expectation_value(ψ3, H, envs3) + v3 = variance(ψ3, H) + + @test isapprox(E, E2; atol = 1.0e-6) + @test isapprox(E, E3; atol = 1.0e-6) + for delta in [δ, δ2, δ3] + @test delta ≈ 0 atol=1e-3 + end + for var in [v, v2, v3] + @test var < v₀ + @test var < 1e-8 + end + + @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) + @test first(transfer_spectrum(ψ2; sector = C0)) ≈ 1 # testing sector kwarg + @test !(abs(first(transfer_spectrum(ψ2; sector = C1))) ≈ 1) # testing injectivity + + @test only(keys(entanglement_spectrum(ψ2))) == M + + momentum = 0 + @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) + excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) + @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy + @test variance(qpC1[1], H) < 1e-8 + + # diagonal test (M = D): injective GS in symmetric phase + Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite = true) + Vdiag = Vect[I](D0 => 24, D1 => 24) + initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) + gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS(;tol=tol, maxiter=400)) + Ediag = expectation_value(gsdiag, Hdual, envsdiag) + + excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D1) + @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation lower in energy + @test variance(qpD1[1], Hdual) < 1e-8 + + # comparison to Z2 Ising: injective in symmetric phase + HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) + VZ2 = Z2Space(0 => 24, 1 => 24) + PZ2 = Z2Space(0 => 1, 1 => 1) + initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) + gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS(;tol=tol, maxiter=400)) + EZ2 = expectation_value(gsZ2, HZ2, envsZ2) + @test isapprox(EZ2, Ediag; atol = 1.0e-6) + + excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(1)) + @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-6) + @test variance(qpZ2_1[1], HZ2) < 1e-8 + end + +end # module TestMultifusion diff --git a/test/runtests.jl b/test/runtests.jl index 556b26dec..2d0e6adb4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,6 +21,9 @@ include("setup.jl") if GROUP == "ALL" || GROUP == "ALGORITHMS" @time include("algorithms.jl") end + if GROUP == "ALL" || GROUP == "MULTIFUSION" + @time include("multifusion.jl") + end if GROUP == "ALL" || GROUP == "OTHER" @time include("other.jl") end diff --git a/test/setup.jl b/test/setup.jl index a6b07bbc0..c769bc430 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -287,7 +287,7 @@ function kitaev_model(; t = 1.0, mu = 1.0, Delta = 1.0, L = Inf) else lattice = fill(space(TB, 1), L) onsite_terms = ((i,) => CP for i in 1:L) - twosite_terms = ((i, i + 1) => TP + SC for i in 1:(L - 1)) + twosite_terms = ((i, i + 1) => TB + SC for i in 1:(L - 1)) terms = Iterators.flatten(twosite_terms, onsite_terms) return FiniteMPOHamiltonian(lattice, terms) end From a2027b0cc67e8daaf14ee071a7d3408ec8d097d6 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:49:18 +0200 Subject: [PATCH 40/54] fix the messed up merges hopefully --- .../excitation/exci_transfer_system.jl | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 8fb975f45..5823e0b96 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -21,15 +21,6 @@ function left_excitation_transfer_system( start = scale!(last(found[1:i] * T), cis(-mom * len)) if istrivial(exci) && isidentitylevel(H, i) regularize!(start, ρ_right, ρ_left) - if exci.trivial && isidentitylevel(H, i) - # not using braiding tensors here, leads to extra leg - util = similar(exci.left_gs.AL[i], first(left_virtualspace(H[i]))) - fill_data!(util, one) - @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * - util[1] * - r_RL(exci.right_gs)[3; 2] * - l_RL(exci.right_gs)[-1; -4] * - conj(util[-2]) end found[i] = add!(start, GBL[i]) @@ -80,15 +71,6 @@ function right_excitation_transfer_system( start = scale!(first(T * found[i:odim]), cis(mom * len)) if istrivial(exci) && isidentitylevel(H, i) regularize!(start, ρ_left, ρ_right) - if exci.trivial && isidentitylevel(H, i) - # not using braiding tensors here, leads to extra leg - util = similar(exci.right_gs.AL[i], first(left_virtualspace(H[i]))) - fill_data!(util, one) - @plansor start[-1 -2; -3 -4] -= start[2 1; -3 3] * - conj(util[1]) * - l_LR(exci.right_gs)[3; 2] * - r_LR(exci.right_gs)[-1; -4] * - util[-2] end found[i] = add!(start, GBR[i]) @@ -113,4 +95,4 @@ function right_excitation_transfer_system( end end return found -end +end \ No newline at end of file From 05e33dd5b98ae4b5322b111a96b74d5b8bfbbf71 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:52:39 +0200 Subject: [PATCH 41/54] format --- test/multifusion.jl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index a855d3c0f..cf697a4e8 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -89,8 +89,8 @@ module TestMultifusion v2 = variance(ψ2, H) E2 = expectation_value(ψ2, H, envs2) - @test δ ≈ 0 atol=1e-3 - @test δ2 ≈ 0 atol=1e-3 + @test δ ≈ 0 atol = 1.0e-3 + @test δ2 ≈ 0 atol = 1.0e-3 @test v < v₀ && v2 < v₀ @test isapprox(E, E2; atol = 1.0e-6) @@ -99,10 +99,10 @@ module TestMultifusion @test isapprox(E, first(ED); atol = 1.0e-6) @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) - excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector=C1, num=1) # testing sector kwarg - @test 0 < variance(qp[1], H) < 1e-8 + excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector = C1, num = 1) # testing sector kwarg + @test 0 < variance(qp[1], H) < 1.0e-8 - excE_DM, qp_DM = excitations(H, FiniteExcited(;gsalg=DMRG2(; trscheme=truncbelow(1e-6))), ψ2;num=1) + excE_DM, qp_DM = excitations(H, FiniteExcited(; gsalg = DMRG2(; trscheme = truncbelow(1.0e-6))), ψ2; num = 1) @test isapprox(first(excE_DM), first(excE) + E2; atol = 1.0e-6) end @@ -114,26 +114,26 @@ module TestMultifusion init = InfiniteMPS([PD, PD], [V, V]) v₀ = variance(init, H) tol = 1.0e-10 - ψ, envs, δ = find_groundstate(init, H, IDMRG(;tol=tol,maxiter=400)) + ψ, envs, δ = find_groundstate(init, H, IDMRG(; tol = tol, maxiter = 400)) E = expectation_value(ψ, H, envs) v = variance(ψ, H) - ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol=tol, trscheme = truncbelow(1.0e-6), maxiter=400)) + ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol = tol, trscheme = truncbelow(1.0e-6), maxiter = 400)) E2 = expectation_value(ψ2, H, envs2) v2 = variance(ψ2, H) - ψ3, envs3, δ3 = find_groundstate(init, H, VUMPS(;tol=tol, maxiter=400)) + ψ3, envs3, δ3 = find_groundstate(init, H, VUMPS(; tol = tol, maxiter = 400)) E3 = expectation_value(ψ3, H, envs3) v3 = variance(ψ3, H) @test isapprox(E, E2; atol = 1.0e-6) @test isapprox(E, E3; atol = 1.0e-6) for delta in [δ, δ2, δ3] - @test delta ≈ 0 atol=1e-3 + @test delta ≈ 0 atol = 1.0e-3 end for var in [v, v2, v3] @test var < v₀ - @test var < 1e-8 + @test var < 1.0e-8 end @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) @@ -146,31 +146,31 @@ module TestMultifusion @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test variance(qpC1[1], H) < 1e-8 + @test variance(qpC1[1], H) < 1.0e-8 # diagonal test (M = D): injective GS in symmetric phase Hdual = TFIM_multifusion(; g = 1 / g, L = Inf, twosite = true) Vdiag = Vect[I](D0 => 24, D1 => 24) initdiag = InfiniteMPS([PD, PD], [Vdiag, Vdiag]) - gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS(;tol=tol, maxiter=400)) + gsdiag, envsdiag = find_groundstate(initdiag, Hdual, VUMPS(; tol = tol, maxiter = 400)) Ediag = expectation_value(gsdiag, Hdual, envsdiag) excD1, qpD1 = excitations(Hdual, QuasiparticleAnsatz(), momentum, gsdiag; sector = D1) @test isapprox(first(excD1), abs(2 * (1 / g - 1)); atol = 1.0e-6) # charged excitation lower in energy - @test variance(qpD1[1], Hdual) < 1e-8 + @test variance(qpD1[1], Hdual) < 1.0e-8 # comparison to Z2 Ising: injective in symmetric phase HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) VZ2 = Z2Space(0 => 24, 1 => 24) PZ2 = Z2Space(0 => 1, 1 => 1) initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) - gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS(;tol=tol, maxiter=400)) + gsZ2, envsZ2 = find_groundstate(initZ2, HZ2, VUMPS(; tol = tol, maxiter = 400)) EZ2 = expectation_value(gsZ2, HZ2, envsZ2) @test isapprox(EZ2, Ediag; atol = 1.0e-6) excZ2_1, qpZ2_1 = excitations(HZ2, QuasiparticleAnsatz(), momentum, gsZ2; sector = Z2Irrep(1)) @test isapprox(first(excZ2_1), first(excD1); atol = 1.0e-6) - @test variance(qpZ2_1[1], HZ2) < 1e-8 + @test variance(qpZ2_1[1], HZ2) < 1.0e-8 end end # module TestMultifusion From ca03f89fa651df40a54c6b6cd446abbafeb26499 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Wed, 3 Sep 2025 09:55:27 +0200 Subject: [PATCH 42/54] actually format everything --- src/algorithms/excitation/exci_transfer_system.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index 5823e0b96..c2b4b217d 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -95,4 +95,4 @@ function right_excitation_transfer_system( end end return found -end \ No newline at end of file +end From 4783937ffd8e4c75dc210cad17fe6e698267c337 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 25 Nov 2025 10:12:36 +0100 Subject: [PATCH 43/54] typo --- src/states/abstractmps.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/states/abstractmps.jl b/src/states/abstractmps.jl index 6dd8a1341..03a4c37dd 100644 --- a/src/states/abstractmps.jl +++ b/src/states/abstractmps.jl @@ -110,7 +110,7 @@ function isfullrank(V::TensorKit.TensorMapSpace; side = :both) end """ - makefullrank!(A::PeriodicVector{<:GenericMPSTensor}; alg=Defalts.alg_qr()) + makefullrank!(A::PeriodicVector{<:GenericMPSTensor}; alg=Defaults.alg_qr()) Make the set of MPS tensors full rank by performing a series of orthogonalizations. """ From 54d5c2a0f3a39ca5d6569ab50bf94ed34808fec7 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 25 Nov 2025 10:13:16 +0100 Subject: [PATCH 44/54] deal with float dimension --- src/environments/abstract_envs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/environments/abstract_envs.jl b/src/environments/abstract_envs.jl index 1ec89a5ee..14aaacf16 100644 --- a/src/environments/abstract_envs.jl +++ b/src/environments/abstract_envs.jl @@ -81,7 +81,7 @@ function environment_alg( tol = Defaults.tol, maxiter = Defaults.maxiter, krylovdim = Defaults.krylovdim, verbosity = Defaults.VERBOSE_NONE ) - max_krylovdim = dim(left_virtualspace(above, 1)) * dim(left_virtualspace(below, 1)) + max_krylovdim = ceil(Int, dim(left_virtualspace(above, 1)) * dim(left_virtualspace(below, 1))) return GMRES(; tol, maxiter, krylovdim = min(max_krylovdim, krylovdim), verbosity) end function environment_alg( From a04358e0f63d091c743c25775e09350d9c53ae1b Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 5 Dec 2025 16:58:28 +0100 Subject: [PATCH 45/54] `left/rightunit` for MPS and MPO --- src/operators/abstractmpo.jl | 3 +++ src/states/abstractmps.jl | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/operators/abstractmpo.jl b/src/operators/abstractmpo.jl index 29f40ac1f..52e6c9f4d 100644 --- a/src/operators/abstractmpo.jl +++ b/src/operators/abstractmpo.jl @@ -31,6 +31,9 @@ right_virtualspace(mpo::AbstractMPO) = map(Base.Fix1(right_virtualspace, mpo), e physicalspace(mpo::AbstractMPO, site::Int) = physicalspace(mpo[site]) physicalspace(mpo::AbstractMPO) = map(Base.Fix1(physicalspace, mpo), eachsite(mpo)) +TensorKit.leftunit(mpo::AbstractMPO) = only(sectors(leftunitspace(physicalspace(mpo, 1)))) +TensorKit.rightunit(mpo::AbstractMPO) = only(sectors(rightunitspace(physicalspace(mpo, 1)))) + for ftype in (:spacetype, :sectortype, :storagetype) @eval TensorKit.$ftype(mpo::AbstractMPO) = $ftype(typeof(mpo)) @eval TensorKit.$ftype(::Type{MPO}) where {MPO <: AbstractMPO} = $ftype(eltype(MPO)) diff --git a/src/states/abstractmps.jl b/src/states/abstractmps.jl index 03a4c37dd..8bf98ab2e 100644 --- a/src/states/abstractmps.jl +++ b/src/states/abstractmps.jl @@ -245,3 +245,6 @@ physicalspace(ψ::AbstractMPS) = map(Base.Fix1(physicalspace, ψ), eachsite(ψ)) Return an iterator over the sites of the MPS `state`. """ eachsite(ψ::AbstractMPS) = eachindex(ψ) + +TensorKit.leftunit(ψ::AbstractMPS) = only(sectors(leftunitspace(left_virtualspace(ψ, 1)))) +TensorKit.rightunit(ψ::AbstractMPS) = only(sectors(rightunitspace(right_virtualspace(ψ, 1)))) From de9ad5a60e6d1bd706cf9e290ff34f21ea6cf219 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 5 Dec 2025 17:16:25 +0100 Subject: [PATCH 46/54] using `left/rightunit(space)` --- src/algorithms/ED.jl | 4 +-- .../excitation/quasiparticleexcitation.jl | 12 ++++----- src/algorithms/toolbox.jl | 4 +-- src/operators/mpohamiltonian.jl | 25 +++++++++---------- src/states/quasiparticle_state.jl | 2 +- src/utility/utility.jl | 2 +- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index d1c86a745..31f4f6105 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -32,7 +32,7 @@ equivalent to dense eigenvectors. """ function exact_diagonalization( H::FiniteMPOHamiltonian; - sector = one(sectortype(H)), num::Int = 1, which::Symbol = :SR, + sector = rightunit(H), num::Int = 1, which::Symbol = :SR, alg = Defaults.alg_eigsolve(; dynamic_tols = false) ) L = length(H) @@ -44,7 +44,7 @@ function exact_diagonalization( # fuse from left to right ALs = Vector{Union{Missing, TA}}(missing, L) - left = spacetype(H)(one(sector) => 1) # might need to be rightone, leave this for now + left = spacetype(H)(rightunit(sector) => 1) for i in 1:(middle_site - 1) P = physicalspace(H, i) ALs[i] = isomorphism(T, left ⊗ P ← fuse(left ⊗ P)) diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index fe1de0615..372a3383c 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -85,14 +85,14 @@ Create and optimise infinite quasiparticle states. # Keywords - `num::Int`: number of excited states to compute - `solver`: algorithm for the linear solver of the quasiparticle environments -- `sector=one(sectortype(left_ψ))`: charge of the quasiparticle state +- `sector=leftunit(left_ψ)`: charge of the quasiparticle state - `parallel=true`: enable multi-threading over different momenta """ function excitations( H, alg::QuasiparticleAnsatz, momentum::Number, lmps::InfiniteMPS, lenvs = environments(lmps, H), rmps::InfiniteMPS = lmps, renvs = lmps === rmps ? lenvs : environments(rmps, H); - sector = one(sectortype(lmps)), kwargs... + sector = leftunit(lmps), kwargs... ) ϕ₀ = LeftGaugedQP(rand, lmps, rmps; sector, momentum) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) @@ -102,7 +102,7 @@ function excitations( lenvs = environments(lmps, H), rmps = lmps, renvs = lmps === rmps ? lenvs : environments(rmps, H); verbosity = Defaults.verbosity, num = 1, - sector = one(sectortype(lmps)), parallel = true, kwargs... + sector = leftunit(lmps), parallel = true, kwargs... ) # wrapper to evaluate sector as positional argument Toutput = let @@ -178,13 +178,13 @@ Create and optimise finite quasiparticle states. # Keywords - `num::Int`: number of excited states to compute -- `sector=one(sectortype(left_ψ))`: charge of the quasiparticle state +- `sector=leftunit(lmps)`: charge of the quasiparticle state """ function excitations( H, alg::QuasiparticleAnsatz, lmps::FiniteMPS, lenvs = environments(lmps, H), rmps::FiniteMPS = lmps, renvs = lmps === rmps ? lenvs : environments(rmps, H); - sector = one(sectortype(lmps)), num = 1 + sector = leftunit(lmps), num = 1 ) ϕ₀ = LeftGaugedQP(rand, lmps, rmps; sector) return excitations(H, alg, ϕ₀, lenvs, renvs; num) @@ -273,7 +273,7 @@ function excitations( H::MultilineMPO, alg::QuasiparticleAnsatz, momentum::Real, lmps::MultilineMPS, lenvs = environments(lmps, H), rmps = lmps, renvs = lmps === rmps ? lenvs : environments(rmps, H); - sector = one(sectortype(lmps)), kwargs... + sector = leftunit(lmps), kwargs... ) ϕ₀ = LeftGaugedQP(randn, lmps, rmps; sector, momentum) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 8e2d0a25e..796752880 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -78,7 +78,7 @@ end """ transfer_spectrum(above::InfiniteMPS; below=above, tol=Defaults.tol, num_vals=20, - sector=first(sectors(oneunit(left_virtualspace(above, 1))))) + sector=leftunit(above)) Calculate the partial spectrum of the left mixed transfer matrix corresponding to the overlap of a given `above` state and a `below` state. The `sector` keyword argument can be @@ -89,7 +89,7 @@ domain of each eigenvector. The `tol` and `num_vals` keyword arguments are passe """ function transfer_spectrum( above::InfiniteMPS; below = above, tol = Defaults.tol, num_vals = 20, - sector = first(sectors(oneunit(left_virtualspace(above, 1)))) + sector = leftunit(above) ) init = randomize!( similar( diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index e04fe7626..5af1d0e36 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -419,17 +419,15 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_opera # avoid using one(S) somempo = local_mpos[1].second[1] - sp_oneleg = space(somempo, 1) - - _oneunit = oneunit(sp_oneleg) # should be rightoneunit, but MPOHamiltonians are always diagonal for now + _rightunit = space(somempo, 1) # should be rightunitspace, but MPOHamiltonians are always diagonal for now virtualsumspaces = Vector{SumSpace{S}}(undef, length(lattice) + 1) - virtualsumspaces[1] = SumSpace(fill(_oneunit, 1)) - virtualsumspaces[end] = SumSpace(fill(_oneunit, 1)) + virtualsumspaces[1] = SumSpace(fill(_rightunit, 1)) + virtualsumspaces[end] = SumSpace(fill(_rightunit, 1)) for i in 1:(length(lattice) - 1) n_channels = maximum(last, nonzero_keys[i]; init = 1) + 1 - V = SumSpace(fill(_oneunit, n_channels)) + V = SumSpace(fill(_rightunit, n_channels)) if n_channels > 2 for ((key_L, key_R), O) in zip(nonzero_keys[i], nonzero_opps[i]) V[key_R == 0 ? end : key_R] = if O isa Number @@ -509,12 +507,11 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ ) # avoid using one(S) somempo = local_mpos[1].second[1] - sp_oneleg = space(somempo, 1) + _rightunit = space(somempo, 1) # should be a rightunitspace - _oneunit = oneunit(sp_oneleg) for V in virtualspaces - V[1] = _oneunit - V[end] = _oneunit + V[1] = _rightunit + V[end] = _rightunit end # start by filling in tensors -> space information available @@ -560,7 +557,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_ end end - foreach(Base.Fix2(replace!, missing => _oneunit), virtualspaces) + foreach(Base.Fix2(replace!, missing => _rightunit), virtualspaces) virtualsumspaces = map(virtualspaces) do V return SumSpace(collect(S, V)) end @@ -691,7 +688,8 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(space(first(H₁[1]), 1)) # should also be rightoneunit, but is currently diagonal + # TODO: check if spaces match + Vtriv = rightunitspace(first(physicalspace(H₁))) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) @@ -715,7 +713,8 @@ function Base.:+( ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) - Vtriv = oneunit(space(first(H₁[1]), 1)) + # TODO: check if spaces match + Vtriv = rightunitspace(first(physicalspace(H₁))) for i in 1:N A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) B = cat(H₁[i].B, H₂[i].B; dims = 1) diff --git a/src/states/quasiparticle_state.jl b/src/states/quasiparticle_state.jl index 87dbfc6e7..156129db3 100644 --- a/src/states/quasiparticle_state.jl +++ b/src/states/quasiparticle_state.jl @@ -306,7 +306,7 @@ function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S <: FiniteMPS} elt = scalartype(v) utl = auxiliaryspace(v) - ou = oneunit(utl) + ou = leftunitspace(utl) utsp = ou ⊕ ou upper = isometry(storagetype(site_type(v.left_gs)), utsp, ou) lower = left_null(upper) diff --git a/src/utility/utility.jl b/src/utility/utility.jl index 6d697e70e..58d43a835 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -69,7 +69,7 @@ function add_util_mpoleg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N # and the additional legs there can be different depending on the fusion tree #TODO?: might need a left and right utility space if MPO has module virtual space (doesn't happen for hamiltonians) - ou = S(one(first(blocksectors(tensor))) => 1) + ou = S(rightunit(first(blocksectors(tensor))) => 1) util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) util_back = isomorphism(storagetype(tensor), domain(tensor), domain(tensor) * ou) From 0bab8133a591895eb9a477c7ba2ffbcaba93dddf Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 5 Dec 2025 17:16:41 +0100 Subject: [PATCH 47/54] test updates --- test/multifusion.jl | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index cf697a4e8..a0b5eb4ae 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -53,22 +53,20 @@ module TestMultifusion @testset "InfiniteMPS construction" begin for (P, V) in bad_fusions - @test_throws ArgumentError("invalid fusion channel") InfiniteMPS([P], [V]) + @test_throws ArgumentError InfiniteMPS([P], [V]) end end @testset "FiniteMPS construction" begin - Prand, Vrand = rand(bad_fusions) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") FiniteMPS(2, Prand, Vrand) - for (P, V) in bad_fusions + @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V) @test_warn "no fusion channels available at site 2" FiniteMPS(rand(2:100), P, V; left = V, right = V) end end @testset "Exact diagonalization" begin H = TFIM_multifusion(; g = 0, L = 4) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") exact_diagonalization(H) + @test_throws ArgumentError exact_diagonalization(H) E, ψ = exact_diagonalization(H; sector = D0) # test that it runs with kwarg @test isapprox(E, [-3, -1, 1, 3]; atol = 1.0e-6) @@ -85,7 +83,7 @@ module TestMultifusion v = variance(ψ, H) E = expectation_value(ψ, H, envs) - ψ2, envs2, δ2 = find_groundstate(init, H, DMRG2(; trscheme = truncbelow(1.0e-6))) + ψ2, envs2, δ2 = find_groundstate(init, H, DMRG2(; trscheme = trunctol(; atol = 1.0e-6))) v2 = variance(ψ2, H) E2 = expectation_value(ψ2, H, envs2) @@ -98,11 +96,10 @@ module TestMultifusion ED, _ = exact_diagonalization(H; sector = D0) @test isapprox(E, first(ED); atol = 1.0e-6) - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), ψ) - excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector = C1, num = 1) # testing sector kwarg + excE, qp = excitations(H, QuasiparticleAnsatz(), ψ2; sector = C1, num = 1) @test 0 < variance(qp[1], H) < 1.0e-8 - excE_DM, qp_DM = excitations(H, FiniteExcited(; gsalg = DMRG2(; trscheme = truncbelow(1.0e-6))), ψ2; num = 1) + excE_DM, qp_DM = excitations(H, FiniteExcited(; gsalg = DMRG2(; trscheme = trunctol(; atol = 1.0e-6))), ψ2; num = 1) @test isapprox(first(excE_DM), first(excE) + E2; atol = 1.0e-6) end @@ -118,7 +115,7 @@ module TestMultifusion E = expectation_value(ψ, H, envs) v = variance(ψ, H) - ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol = tol, trscheme = truncbelow(1.0e-6), maxiter = 400)) + ψ2, envs2, δ2 = find_groundstate(init, H, IDMRG2(; tol = tol, trscheme = trunctol(; atol = 1.0e-6), maxiter = 400)) E2 = expectation_value(ψ2, H, envs2) v2 = variance(ψ2, H) @@ -136,14 +133,12 @@ module TestMultifusion @test var < 1.0e-8 end - @test_throws ArgumentError("sectors of $V are non-diagonal") transfer_spectrum(ψ) - @test first(transfer_spectrum(ψ2; sector = C0)) ≈ 1 # testing sector kwarg + @test first(transfer_spectrum(ψ2; sector = C0)) ≈ 1 @test !(abs(first(transfer_spectrum(ψ2; sector = C1))) ≈ 1) # testing injectivity @test only(keys(entanglement_spectrum(ψ2))) == M momentum = 0 - @test_throws ArgumentError("one of Type IsingBimodule doesn't exist") excitations(H, QuasiparticleAnsatz(), momentum, ψ) excC1, qpC1 = excitations(H, QuasiparticleAnsatz(), momentum, ψ3; sector = C1) @test isapprox(first(excC1), abs(2 * (g - 1)); atol = 1.0e-6) # charged excitation lower in energy @test variance(qpC1[1], H) < 1.0e-8 @@ -160,7 +155,7 @@ module TestMultifusion @test variance(qpD1[1], Hdual) < 1.0e-8 # comparison to Z2 Ising: injective in symmetric phase - HZ2 = transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf, twosite = true) + HZ2 = repeat(transverse_field_ising(Z2Irrep; g = 1 / g, L = Inf), 2) VZ2 = Z2Space(0 => 24, 1 => 24) PZ2 = Z2Space(0 => 1, 1 => 1) initZ2 = InfiniteMPS([PZ2, PZ2], [VZ2, VZ2]) From 18a186262d29d7d436156340849869cf94205be5 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:05:45 +0100 Subject: [PATCH 48/54] some `unitspace`s --- src/algorithms/correlators.jl | 2 +- src/operators/mpo.jl | 5 +++-- src/operators/mpohamiltonian.jl | 10 +++++----- src/operators/ortho.jl | 16 ++++++++-------- src/states/finitemps.jl | 22 +++++++++++----------- src/utility/utility.jl | 2 +- 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index 8660d08d7..3f9e24e17 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -16,7 +16,7 @@ function correlator( ) first(js) > i || @error "i should be smaller than j ($i, $(first(js)))" S₁ = _firstspace(O₁) - S₁ == oneunit(S₁) || throw(ArgumentError("O₁ should start with a trivial leg.")) + isunitspace(S₁) || throw(ArgumentError("O₁ should start with a trivial leg.")) S₂ = _lastspace(O₂) S₂ == S₁' || throw(ArgumentError("O₂ should end with a trivial leg.")) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index 07ffa65ea..e9f4e07e3 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -220,8 +220,9 @@ function Base.:*(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) N = 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) || right_virtualspace(mpo2, N) != oneunit(S)) + + if (isunitspace(left_virtualspace(mpo1, 1)) || isunitspace(left_virtualspace(mpo2, 1))) || + (isunitspace(right_virtualspace(mpo1, N)) || isunitspace(right_virtualspace(mpo2, N))) @warn "left/right virtual space is not trivial, fusion may not be unique" # this is a warning because technically any isomorphism that fuses the left/right # would work and for now I dont feel like figuring out if this is important diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 5af1d0e36..ea9b6b595 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -77,11 +77,11 @@ function FiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O <: JordanMPO # left end nlvls = size(W_mats[1], 1) @assert nlvls == 1 "left boundary should have a single level" - Vspaces[1] = SumSpace(oneunit(S)) + Vspaces[1] = SumSpace(unitspace(S)) # right end nlvls = size(W_mats[end], 2) @assert nlvls == 1 "right boundary should have a single level" - Vspaces[end] = SumSpace(oneunit(S)) + Vspaces[end] = SumSpace(unitspace(S)) # start filling spaces # note that we assume that the FSA does not contain "dead ends", as this would mess with the @@ -99,7 +99,7 @@ function FiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O <: JordanMPO # start by assuming trivial spaces everywhere -- replace everything that we know # assume spacecheck errors will happen when filling the BlockTensors nlvls = size(W_mat, 2) - Vs_right = SumSpace(fill(oneunit(S), nlvls)) + Vs_right = SumSpace(fill(unitspace(S), nlvls)) end for I in eachindex(IndexCartesian(), W_mat) @@ -182,7 +182,7 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O <: MPOTens MissingS = Union{Missing, S} Vspaces = PeriodicArray([Vector{MissingS}(missing, nlvls) for _ in 1:L]) for V in Vspaces - V[1] = V[end] = oneunit(S) + V[1] = V[end] = unitspace(S) end haschanged = true @@ -232,7 +232,7 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O <: MPOTens end end - foreach(Base.Fix2(replace!, missing => oneunit(S)), Vspaces) + foreach(Base.Fix2(replace!, missing => unitspace(S)), Vspaces) Vsumspaces = map(Vspaces) do V return SumSpace(collect(S, V)) end diff --git a/src/operators/ortho.jl b/src/operators/ortho.jl index d4f0f607f..b0d46e04b 100644 --- a/src/operators/ortho.jl +++ b/src/operators/ortho.jl @@ -22,11 +22,11 @@ function left_canonicalize!( Q, R = _left_orth!(tmp; alg, trunc = trscheme) if dim(R) == 0 # fully truncated - V = oneunit(S) ⊞ oneunit(S) + V = unitspace(S) ⊞ unitspace(S) Q1 = typeof(W.A)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) Q2 = typeof(W.C)(undef, physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) else - V = ⊞(oneunit(S), space(R, 1), oneunit(S)) + V = ⊞(unitspace(S), space(R, 1), unitspace(S)) scale!(Q, d) scale!(R, inv(d)) Q1 = typeof(W.A)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W) ⊗ space(R, 1)) @@ -38,7 +38,7 @@ function left_canonicalize!( Q, R = _left_orth!(tmp; alg, trunc = trscheme) if dim(R) == 0 # fully truncated - V = oneunit(S) ⊞ oneunit(S) + V = unitspace(S) ⊞ unitspace(S) Q1 = typeof(W.A)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) Q2 = typeof(W.C)(undef, physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) else @@ -47,7 +47,7 @@ function left_canonicalize!( Q′ = transpose(Q, ((2, 3), (1, 4))) Q1 = Q′[2:end, 1, 1, 1] Q2 = removeunit(SparseBlockTensorMap(Q′[1:1, 1, 1, 1]), 1) - V = ⊞(oneunit(S), right_virtualspace(Q′), oneunit(S)) + V = ⊞(unitspace(S), right_virtualspace(Q′), unitspace(S)) end H[i] = JordanMPOTensor(codomain(W) ← physicalspace(W) ⊗ V, Q1, W.B, Q2, W.D) end @@ -108,11 +108,11 @@ function right_canonicalize!( R, Q = _right_orth!(tmp; alg, trunc = trscheme) if dim(R) == 0 - V = oneunit(S) ⊞ oneunit(S) + V = unitspace(S) ⊞ unitspace(S) Q1 = typeof(W.A)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) Q2 = typeof(W.B)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W)) else - V = ⊞(oneunit(S), space(Q, 1), oneunit(S)) + V = ⊞(unitspace(S), space(Q, 1), unitspace(S)) scale!(Q, d) scale!(R, inv(d)) Q1 = typeof(W.A)(undef, space(Q, 1) ⊗ physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) @@ -123,7 +123,7 @@ function right_canonicalize!( tmp = transpose(cat(insertleftunit(B′, 4), W.A; dims = 4), ((1,), (3, 4, 2))) R, Q = _right_orth!(tmp; alg, trunc = trscheme) if dim(R) == 0 - V = oneunit(S) ⊞ oneunit(S) + V = unitspace(S) ⊞ unitspace(S) Q1 = typeof(W.A)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W) ⊗ SumSpace{S}()) Q2 = typeof(W.B)(undef, SumSpace{S}() ⊗ physicalspace(W) ← physicalspace(W)) else @@ -132,7 +132,7 @@ function right_canonicalize!( Q′ = transpose(Q, ((1, 4), (2, 3))) Q1 = SparseBlockTensorMap(Q′[1, 1, 1, 2:end]) Q2 = removeunit(SparseBlockTensorMap(Q′[1, 1, 1, 1:1]), 4) - V = ⊞(oneunit(S), left_virtualspace(Q′), oneunit(S)) + V = ⊞(unitspace(S), left_virtualspace(Q′), unitspace(S)) end H[i] = JordanMPOTensor(V ⊗ physicalspace(W) ← domain(W), Q1, Q2, W.C, W.D) end diff --git a/src/states/finitemps.jl b/src/states/finitemps.jl index 9d0fe986a..d006a0b24 100644 --- a/src/states/finitemps.jl +++ b/src/states/finitemps.jl @@ -27,10 +27,10 @@ By convention, we have that: ## Constructors FiniteMPS([f, eltype], physicalspaces::Vector{<:Union{S,CompositeSpace{S}}}, maxvirtualspaces::Union{S,Vector{S}}; - normalize=true, left=oneunit(S), right=oneunit(S)) where {S<:ElementarySpace} + normalize=true, left=unitspace(S), right=unitspace(S)) where {S<:ElementarySpace} FiniteMPS([f, eltype], N::Int, physicalspace::Union{S,CompositeSpace{S}}, maxvirtualspaces::Union{S,Vector{S}}; - normalize=true, left=oneunit(S), right=oneunit(S)) where {S<:ElementarySpace} + normalize=true, left=unitspace(S), right=unitspace(S)) where {S<:ElementarySpace} FiniteMPS(As::Vector{<:GenericMPSTensor}; normalize=false, overwrite=false) Construct an MPS via a specification of physical and virtual spaces, or from a list of @@ -54,8 +54,8 @@ total charge can be constructed by passing a non-trivially charged vector space ### Keywords - `normalize=true`: normalize the constructed state - `overwrite=false`: overwrite the given input tensors -- `left=oneunit(S)`: left-most virtual space -- `right=oneunit(S)`: right-most virtual space +- `left=unitspace(S)`: left-most virtual space +- `right=unitspace(S)`: right-most virtual space """ struct FiniteMPS{A <: GenericMPSTensor, B <: MPSBondTensor} <: AbstractFiniteMPS ALs::Vector{Union{Missing, A}} @@ -235,7 +235,7 @@ end function FiniteMPS( f, elt, Pspaces::Vector{<:Union{S, CompositeSpace{S}}}, maxVspaces::Vector{S}; - normalize = true, left::S = oneunit(S), right::S = oneunit(S) + normalize = true, left::S = unitspace(S), right::S = unitspace(S) ) where {S <: ElementarySpace} N = length(Pspaces) length(maxVspaces) == N - 1 || @@ -292,7 +292,7 @@ end # construct from dense state # TODO: make planar? function FiniteMPS(ψ::AbstractTensor) - U = ones(scalartype(ψ), oneunit(spacetype(ψ))) + U = ones(scalartype(ψ), unitspace(spacetype(ψ))) A = _transpose_front( U * transpose(ψ * U', ((), reverse(ntuple(identity, numind(ψ) + 1)))) ) @@ -363,10 +363,10 @@ function Base.convert(::Type{TensorMap}, ψ::FiniteMPS) end # remove utility legs - space(T, 1) == oneunit(spacetype(T)) || throw(ArgumentError("utility leg not trivial")) - space(T, numind(T)) == oneunit(spacetype(T))' || + space(T, 1) == unitspace(spacetype(T)) || throw(ArgumentError("utility leg not trivial")) + space(T, numind(T)) == unitspace(spacetype(T))' || throw(ArgumentError("utility leg not trivial")) - U = ones(scalartype(ψ), oneunit(spacetype(ψ))) + U = ones(scalartype(ψ), unitspace(spacetype(ψ))) UTU = transpose( U' * _transpose_tail(T * U), (reverse(ntuple(identity, numind(T) - 2)), ()) ) @@ -407,12 +407,12 @@ end """ max_virtualspaces(ψ::FiniteMPS) - max_virtualspaces(Ps::Vector{<:Union{S,CompositeSpace{S}}}; left=oneunit(S), right=oneunit(S)) + max_virtualspaces(Ps::Vector{<:Union{S,CompositeSpace{S}}}; left=unitspace(S), right=unitspace(S)) Compute the maximal virtual spaces of a given finite MPS or its physical spaces. """ function max_virtualspaces( - Ps::Vector{<:Union{S, CompositeSpace{S}}}; left = oneunit(S), right = oneunit(S) + Ps::Vector{<:Union{S, CompositeSpace{S}}}; left = unitspace(S), right = unitspace(S) ) where {S <: ElementarySpace} Vs = similar(Ps, length(Ps) + 1) Vs[1] = left diff --git a/src/utility/utility.jl b/src/utility/utility.jl index 58d43a835..c0a83ac67 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -56,7 +56,7 @@ Add trivial one-dimensional utility spaces with trivial sector to the left and r given tensor map, i.e. as the first space of the codomain and the last space of the domain. """ function add_util_leg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, N2} - ou = oneunit(_firstspace(tensor)) + ou = unitspace(_firstspace(tensor)) util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) util_back = isomorphism(storagetype(tensor), domain(tensor), domain(tensor) * ou) From b16d01e8661b72c052f8e4a1fe01dc897b1de2ae Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:06:17 +0100 Subject: [PATCH 49/54] some `rightunitspace`s --- src/algorithms/timestep/taylorcluster.jl | 4 ++-- src/algorithms/toolbox.jl | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/algorithms/timestep/taylorcluster.jl b/src/algorithms/timestep/taylorcluster.jl index dd3c3be9c..3d93a2e0b 100644 --- a/src/algorithms/timestep/taylorcluster.jl +++ b/src/algorithms/timestep/taylorcluster.jl @@ -186,14 +186,14 @@ function make_time_mpo( H′ = copy(parent(H)) V_left = left_virtualspace(H[1]) - V_left′ = ⊞(V_left, oneunit(V_left), oneunit(V_left)) + V_left′ = ⊞(V_left, rightunitspace(V_left), rightunitspace(V_left)) H′[1] = similar(H[1], V_left′ ⊗ space(H[1], 2) ← domain(H[1])) for (I, v) in nonzero_pairs(H[1]) H′[1][I] = v end V_right = right_virtualspace(H[end]) - V_right′ = ⊞(oneunit(V_right), oneunit(V_right), V_right) + V_right′ = ⊞(rightunitspace(V_right), rightunitspace(V_right), V_right) H′[end] = similar(H[end], codomain(H[end]) ← space(H[end], 3)' ⊗ V_right′) for (I, v) in nonzero_pairs(H[end]) H′[end][I[1], 1, 1, end] = v diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 796752880..d60ebfaad 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -275,13 +275,13 @@ function periodic_boundary_conditions(mpo::InfiniteMPO{O}, L = length(mpo)) wher V_wrap = left_virtualspace(mpo, 1) ST = storagetype(O) - util = isometry(storagetype(O), oneunit(V_wrap) ← one(V_wrap)) + util = isometry(storagetype(O), rightunitspace(V_wrap) ← one(V_wrap)) @plansor cup[-1; -2 -3] := id(ST, V_wrap)[-2; -3] * util[-1] local F_right for i in 1:L - V_left = i == 1 ? oneunit(V_wrap) : fuse(V_wrap ⊗ left_virtualspace(mpo, i)) - V_right = i == L ? oneunit(V_wrap) : fuse(V_wrap ⊗ right_virtualspace(mpo, i)) + V_left = i == 1 ? rightunitspace(V_wrap) : fuse(V_wrap ⊗ left_virtualspace(mpo, i)) + V_right = i == L ? rightunitspace(V_wrap) : fuse(V_wrap ⊗ right_virtualspace(mpo, i)) output[i] = similar( mpo[i], V_left * physicalspace(mpo, i) ← physicalspace(mpo, i) * V_right ) @@ -336,7 +336,7 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L = length(H)) output = Vector{O}(undef, L) for site in 1:L V_left = if site == 1 - oneunit(V_wrap) + rightunitspace(V_wrap) else vs = Vector{S}(undef, chi_) for (k, v) in indmap @@ -345,7 +345,7 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L = length(H)) SumSpace(vs) end V_right = if site == L - oneunit(V_wrap) + rightunitspace(V_wrap) else vs = Vector{S}(undef, chi_) for (k, v) in indmap From 37b82414b8ec3eaa5c0fc662f462a91f4eb19be8 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:06:31 +0100 Subject: [PATCH 50/54] docstring to ED --- src/algorithms/ED.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index 31f4f6105..df145b064 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -1,6 +1,6 @@ """ exact_diagonalization(H::FiniteMPOHamiltonian; - sector=first(sectors(oneunit(physicalspace(H, 1)))), + sector=rightunit(H), len::Int=length(H), num::Int=1, which::Symbol=:SR, alg=Defaults.alg_eigsolve(; dynamic_tols=false)) -> vals, state_vecs, convhist @@ -13,7 +13,7 @@ equivalent to dense eigenvectors. - `H::FiniteMPOHamiltonian`: the Hamiltonian to diagonalize. ### Keyword arguments -- `sector=first(sectors(oneunit(physicalspace(H, 1))))`: the total charge of the +- `sector=rightunit(H)`: the total charge of the eigenvectors, which is chosen trivial by default. - `len::Int=length(H)`: the length of the system. - `num::Int=1`: the number of eigenvectors to find. From de8cb811e75858b3f0b540a33cffb9cb681d05c0 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:06:47 +0100 Subject: [PATCH 51/54] remove ED error test --- test/multifusion.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/multifusion.jl b/test/multifusion.jl index a0b5eb4ae..4279b7192 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -66,8 +66,6 @@ module TestMultifusion @testset "Exact diagonalization" begin H = TFIM_multifusion(; g = 0, L = 4) - @test_throws ArgumentError exact_diagonalization(H) - E, ψ = exact_diagonalization(H; sector = D0) # test that it runs with kwarg @test isapprox(E, [-3, -1, 1, 3]; atol = 1.0e-6) end From 68acebbf09e52d0d80c7e888cf51183f8d43dac4 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:08:24 +0100 Subject: [PATCH 52/54] more `rightunitspace`s in tests --- test/operators.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/operators.jl b/test/operators.jl index 03fadeea4..4a98c8122 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -213,7 +213,7 @@ module TestOperators FiniteMPOHamiltonian(grid, horizontal_operators) atol = 1.0e-4 H5 = changebonds(H4 / 3 + 2H4 / 3, SvdCut(; trscheme = trunctol(; atol = 1.0e-12))) - psi = FiniteMPS(physicalspace(H5), V ⊕ oneunit(V)) + psi = FiniteMPS(physicalspace(H5), V ⊕ rightunitspace(V)) @test expectation_value(psi, H4) ≈ expectation_value(psi, H5) end end @@ -366,7 +366,7 @@ module TestOperators @testset "DenseMPO" for ham in (transverse_field_ising(), heisenberg_XXX(; spin = 1)) pspace = physicalspace(ham, 1) - ou = oneunit(pspace) + ou = rightunitspace(pspace) ψ = InfiniteMPS([pspace], [ou ⊕ pspace]) From a20612f3705019bbdf16590215ebfdd4743c3108 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:31:42 +0100 Subject: [PATCH 53/54] even more `rightunitspace`s --- src/algorithms/timestep/wii.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/timestep/wii.jl b/src/algorithms/timestep/wii.jl index 7b2fe2169..6ba74c91c 100644 --- a/src/algorithms/timestep/wii.jl +++ b/src/algorithms/timestep/wii.jl @@ -94,14 +94,14 @@ function make_time_mpo( H′ = copy(parent(H)) V_left = left_virtualspace(H[1]) - V_left′ = ⊞(V_left, oneunit(V_left), oneunit(V_left)) + V_left′ = ⊞(V_left, rightunitspace(V_left), rightunitspace(V_left)) H′[1] = similar(H[1], V_left′ ⊗ space(H[1], 2) ← domain(H[1])) for (I, v) in nonzero_pairs(H[1]) H′[1][I] = v end V_right = right_virtualspace(H[end]) - V_right′ = ⊞(oneunit(V_right), oneunit(V_right), V_right) + V_right′ = ⊞(rightunitspace(V_right), rightunitspace(V_right), V_right) H′[end] = similar(H[end], codomain(H[end]) ← space(H[end], 3)' ⊗ V_right′) for (I, v) in nonzero_pairs(H[end]) H′[end][I[1], 1, 1, end] = v From ddbc1e316d5c64baecd89e99863f1e229602d001 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Tue, 16 Dec 2025 10:44:08 +0100 Subject: [PATCH 54/54] some `unit`s, `leftunit`s and `isunit`s --- src/algorithms/excitation/chepigaansatz.jl | 8 ++++---- src/states/quasiparticle_state.jl | 8 ++++---- src/utility/plotting.jl | 2 +- test/algorithms.jl | 4 ++-- test/multifusion.jl | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/algorithms/excitation/chepigaansatz.jl b/src/algorithms/excitation/chepigaansatz.jl index e92a618be..153b262f9 100644 --- a/src/algorithms/excitation/chepigaansatz.jl +++ b/src/algorithms/excitation/chepigaansatz.jl @@ -35,10 +35,10 @@ end function excitations( H, alg::ChepigaAnsatz, ψ::FiniteMPS, envs = environments(ψ, H); - sector = one(sectortype(ψ)), num::Int = 1, pos::Int = length(ψ) ÷ 2 + sector = leftunit(ψ), num::Int = 1, pos::Int = length(ψ) ÷ 2 ) 1 ≤ pos ≤ length(ψ) || throw(ArgumentError("invalid position $pos")) - sector == one(sector) || error("not yet implemented for charged excitations") + isunit(sector) || error("not yet implemented for charged excitations") # add random offset to kickstart Krylov process: AC = ψ.AC[pos] @@ -100,10 +100,10 @@ end function excitations( H, alg::ChepigaAnsatz2, ψ::FiniteMPS, envs = environments(ψ, H); - sector = one(sectortype(ψ)), num::Int = 1, pos::Int = length(ψ) ÷ 2 + sector = leftunit(ψ), num::Int = 1, pos::Int = length(ψ) ÷ 2 ) 1 ≤ pos ≤ length(ψ) - 1 || throw(ArgumentError("invalid position $pos")) - sector == one(sector) || error("not yet implemented for charged excitations") + isunit(sector) || error("not yet implemented for charged excitations") # add random offset to kickstart Krylov process: @plansor AC2[-1 -2; -3 -4] := ψ.AC[pos][-1 -2; 1] * ψ.AR[pos + 1][1 -4; -3] diff --git a/src/states/quasiparticle_state.jl b/src/states/quasiparticle_state.jl index 156129db3..1a923f2da 100644 --- a/src/states/quasiparticle_state.jl +++ b/src/states/quasiparticle_state.jl @@ -35,7 +35,7 @@ end #constructors function LeftGaugedQP( datfun, left_gs, right_gs = left_gs; - sector = one(sectortype(left_gs)), momentum = 0.0 + sector = leftunit(left_gs), momentum = 0.0 ) # find the left null spaces for the TNS excitation_space = Vect[typeof(sector)](sector => 1) @@ -56,7 +56,7 @@ function LeftGaugedQP( end function LeftGaugedQP( datfun, left_gs::MultilineMPS, right_gs::MultilineMPS = left_gs; - sector = one(sectortype(left_gs)), momentum = 0.0 + sector = leftunit(left_gs), momentum = 0.0 ) # not sure why this is needed for type stability Tresult = leftgaugedqptype(eltype(parent(left_gs)), typeof(momentum)) @@ -69,7 +69,7 @@ end function RightGaugedQP( datfun, left_gs, right_gs = left_gs; - sector = one(sectortype(left_gs)), momentum = 0.0 + sector = leftunit(left_gs), momentum = 0.0 ) # find the left null spaces for the TNS excitation_space = Vect[typeof(sector)](sector => 1) @@ -226,7 +226,7 @@ auxiliarysector(state::QP) = only(sectors(auxiliaryspace(state))) eachsite(state::QP) = eachsite(state.left_gs) istopological(qp::QP) = qp.left_gs !== qp.right_gs -istrivial(qp::QP) = !istopological(qp) && isone(auxiliarysector(qp)) +istrivial(qp::QP) = !istopological(qp) && isunit(auxiliarysector(qp)) Base.copy(a::QP) = copy!(similar(a), a) Base.copyto!(a::QP, b::QP) = copy!(a, b) diff --git a/src/utility/plotting.jl b/src/utility/plotting.jl index 9265311fa..ac60778f4 100644 --- a/src/utility/plotting.jl +++ b/src/utility/plotting.jl @@ -119,7 +119,7 @@ function transferplot end sector_formatter = string ) if sectors === nothing - sectors = [one(sectortype(h.args[1]))] + sectors = [unit(sectortype(h.args[1]))] end for sector in sectors diff --git a/test/algorithms.jl b/test/algorithms.jl index 8a5c6ea9d..370184977 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -1057,7 +1057,7 @@ module TestAlgorithms FiniteMPS(physicalspace(H), maxVspaces[2:(end - 1)]), H; verbosity = 0 ) E₀ = expectation_value(gs, H) - @test E₀ ≈ first(vals_dense[one(U1Irrep)]) + @test E₀ ≈ first(vals_dense[unit(U1Irrep)]) for (sector, vals) in vals_dense # ED tests @@ -1074,7 +1074,7 @@ module TestAlgorithms Es, Bs = excitations(H, QuasiparticleAnsatz(; tol), gs; sector, num = 1) Es = Es .+ E₀ # first excited state is second eigenvalue if sector is trivial - @test Es[1] ≈ vals[isone(sector) ? 2 : 1] atol = 1.0e-8 + @test Es[1] ≈ vals[isunit(sector) ? 2 : 1] atol = 1.0e-8 end # shifted charges tests diff --git a/test/multifusion.jl b/test/multifusion.jl index 4279b7192..79662e5ee 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -32,7 +32,7 @@ module TestMultifusion P = Vect[I](D0 => 1, D1 => 1) X = zeros(T, P ← P) for (s, f) in fusiontrees(X) - isone(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g + isunit(only(f.uncoupled)) ? X[s, f] .= g : X[s, f] .= -g end ZZ = zeros(T, P^2 ← P^2) for (s, f) in fusiontrees(ZZ)