From 38f6d2c268cad1aed59bd238318795fed02025fb Mon Sep 17 00:00:00 2001 From: lkdvos Date: Thu, 30 Jul 2026 18:02:52 -0400 Subject: [PATCH 1/4] fix: `correlator` throws on unordered sites `first(js) > i || @error "..."` only logged the problem and then let the function continue into a contraction that is not the requested correlator, so an unordered (or equal) site pair returned a meaningless number instead of failing. Make it an `ArgumentError`, and document the ordering requirement in the docstring. All three `correlator` signatures funnel their arguments through the range method, so the test covers each of them. Carved out of #449 to keep that PR documentation-only. Co-Authored-By: Claude Opus 5 (1M context) --- docs/src/changelog.md | 3 +++ src/algorithms/correlators.jl | 3 ++- test/algorithms/correlators.jl | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index a920c6437..7173d9309 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -51,6 +51,9 @@ When releasing a new version, move the "Unreleased" changes to a new version sec Accordingly, `marek_gap` and `correlation_length` now return a `TensorKit.SectorDict` of per-sector results by default; pass `sector = ...` to obtain a single sector's result as before. - All `trscheme` keyword arguments are renamed to `trunc` ([#482](https://github.com/QuantumKitHub/MPSKit.jl/pull/482)). +- `correlator` now throws an `ArgumentError` when the sites are not ordered as `i < j`. + Previously such a call only logged an `@error` and then continued into a contraction that is + not the requested correlator. ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) ### Deprecated diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index 4413ef63c..ddacafb39 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -4,6 +4,7 @@ Compute the 2-point correlator <ψ|O1[i]O2[j]|ψ> for inserting `O1` at `i` and `O2` at `j`. Also accepts ranges for `j`. +The sites must be ordered as `i < j`; other orderings throw an `ArgumentError`. """ function correlator end @@ -14,7 +15,7 @@ end function correlator( state::AbstractMPS, O₁::MPOTensor, O₂::MPOTensor, i::Int, js::AbstractRange{Int} ) - first(js) > i || @error "i should be smaller than j ($i, $(first(js)))" + first(js) > i || throw(ArgumentError("i should be smaller than j ($i, $(first(js)))")) S₁ = _firstspace(O₁) isunitspace(S₁) || throw(ArgumentError("O₁ should start with a trivial leg.")) S₂ = _lastspace(O₂) diff --git a/test/algorithms/correlators.jl b/test/algorithms/correlators.jl index 2730a2ccf..d285e9c92 100644 --- a/test/algorithms/correlators.jl +++ b/test/algorithms/correlators.jl @@ -41,4 +41,12 @@ end @test isapprox(last(G), last(G2), atol = 1.0e-2) @test isapprox(G[1], expectation_value(ψ, (1, 2) => S_z_S_z()), atol = 1.0e-2) @test isapprox(G[2], expectation_value(ψ, (1, 3) => S_z_S_z()), atol = 1.0e-2) + + # the sites have to be ordered `i < j`: every `correlator` method funnels its + # arguments through the range version, so all three signatures have to throw + @test_throws ArgumentError correlator(ψ, Z_mpo, Z_mpo, 3, 2) + @test_throws ArgumentError correlator(ψ, Z_mpo, Z_mpo, 3, 1:2) + @test_throws ArgumentError correlator(ψ, S_z_S_z(), 3, 2) + # equal sites are not allowed either + @test_throws ArgumentError correlator(ψ, Z_mpo, Z_mpo, 2, 2) end From 707634841817255f8e3f2edee02e64356def9bce Mon Sep 17 00:00:00 2001 From: lkdvos Date: Thu, 30 Jul 2026 18:06:15 -0400 Subject: [PATCH 2/4] fix: `excitations` on an `InfiniteMPO` with a quasiparticle input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `excitations(H::InfiniteMPO, ::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs)` constructed its effective Hamiltonian as `EffectiveExcitationHamiltonian(H_eff, lenvs, renvs, E)`. `H_eff` is the local being assigned on that very line, so the read hit an uninitialized slot and every call through this method threw `UndefVarError: H_eff not defined in local scope` — the path was dead rather than silently wrong. The momentum entry point is unaffected, since for an `MPO` it converts to a `MultilineMPO` and dispatches to the `MultilineQP` method. The regression test uses the classical Ising transfer matrix rather than the six-vertex model of the neighbouring testset: with the latter the quasiparticle eigenproblem is only loosely converged (the eigenpair residual is ~1e-2 on both entry points), while for the former the two entry points agree to ~1e-11. Carved out of #449 to keep that PR documentation-only. Co-Authored-By: Claude Opus 5 (1M context) --- docs/src/changelog.md | 6 ++++ .../excitation/quasiparticleexcitation.jl | 2 +- test/algorithms/excitations.jl | 29 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 7173d9309..9ef38fdf0 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -61,6 +61,12 @@ When releasing a new version, move the "Unreleased" changes to a new version sec ### Fixed +- `excitations(::InfiniteMPO, ::QuasiparticleAnsatz, ::InfiniteQP, lenvs, renvs)` threw + `UndefVarError: H_eff not defined` on every call, because the effective excitation + Hamiltonian was built from the not-yet-assigned `H_eff` instead of from `H`. Only this + entry point was affected: supplying a momentum instead of a quasiparticle state converts + the operator to a `MultilineMPO` first and never reached the faulty line. + ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) - `Base.:+`/`-` on `FiniteMPS` returned a wrong state for near-parallel operands carried by different tensor networks, e.g. `norm(E₀ * gs - H * gs)` coming out as `2 * norm(gs) * E₀` instead of ~0. The lazy gauge sweep in `CView.getindex` re-derived `AL`/`C` entries that were diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index dab59fecd..424d97e7b 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -214,7 +214,7 @@ function excitations( num = 1 ) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) - H_eff = EffectiveExcitationHamiltonian(H_eff, lenvs, renvs, E) + H_eff = EffectiveExcitationHamiltonian(H, lenvs, renvs, E) Es, ϕs, convhist = eigsolve(ϕ₀, num, :LM, alg.alg) do ϕ return H_eff(ϕ, alg.alg_environments) diff --git a/test/algorithms/excitations.jl b/test/algorithms/excitations.jl index 5e9b2b4cc..7bb8fd398 100644 --- a/test/algorithms/excitations.jl +++ b/test/algorithms/excitations.jl @@ -52,6 +52,35 @@ verbosity_conv = 1 @test abs(energies[1]) > abs(energies[2]) # has a minimum at pi/2 end + @testset "infinite (mpo) - quasiparticle input" begin + # regression test: handing an `InfiniteQP` to an `InfiniteMPO`, rather than a + # momentum, used to build the effective Hamiltonian out of the (still undefined) + # `H_eff` instead of `H`, so every call through this entry point threw an + # `UndefVarError`. The momentum entry point is unaffected, as it converts to a + # `MultilineMPO` first. The classical Ising transfer matrix is used here (rather than + # the six-vertex model above) because its quasiparticle eigenproblem is well enough + # conditioned to compare the two entry points at tight tolerance. + H = classical_ising(; β = 0.4) + ψ = InfiniteMPS([ℂ^2], [ℂ^12]) + ψ, envs, = leading_boundary(ψ, H, VUMPS(; maxiter = 400, verbosity = 0, tol = 1.0e-12)) + + alg = QuasiparticleAnsatz(; tol = 1.0e-10) + for momentum in (0.0, Float64(pi / 2)) + ϕ₀ = MPSKit.LeftGaugedQP(rand, ψ, ψ; momentum) + Es, ϕs = excitations(H, alg, ϕ₀, envs, envs; num = 1) + + # same spectrum as the momentum entry point, which routes through MultilineMPO + Es_momentum, = excitations(H, alg, momentum, ψ, envs; num = 1) + @test Es[1] ≈ Es_momentum[1] atol = 1.0e-8 + + # the operator really is `H`: the result is an eigenpair of the corresponding + # effective excitation Hamiltonian + E = MPSKit.effective_excitation_renormalization_energy(H, ϕs[1], envs, envs) + H_eff = MPSKit.EffectiveExcitationHamiltonian(H, envs, envs, E) + @test norm(H_eff(ϕs[1], alg.alg_environments) - Es[1] * ϕs[1]) < 1.0e-8 + end + end + @testset "finite" begin verbosity = verbosity_conv H_inf = force_planar(transverse_field_ising()) From fb95772080d264485c8a3c0d125d3cd5d7473c6b Mon Sep 17 00:00:00 2001 From: lkdvos Date: Thu, 30 Jul 2026 18:06:30 -0400 Subject: [PATCH 3/4] fix: `isfinite` for `WindowMPOHamiltonian` `Base.isfinite(O::AbstractMPO) = isfinite(typeof(O))` requires every concrete operator type to define the trait, and `WindowMPOHamiltonian` did not, so both `isfinite(H)` and `isfinite(typeof(H))` threw a `MethodError`. A window Hamiltonian acts on a finite number of sites, so the trait is `true`, matching how it otherwise behaves as a finite Hamiltonian; it is spelled and placed like the `FiniteMPO(Hamiltonian)` definitions. Carved out of #449 to keep that PR documentation-only. Co-Authored-By: Claude Opus 5 (1M context) --- docs/src/changelog.md | 3 +++ src/operators/windowhamiltonian.jl | 1 + test/operators/windowhamiltonian.jl | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 9ef38fdf0..4a5e4d4c3 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -61,6 +61,9 @@ When releasing a new version, move the "Unreleased" changes to a new version sec ### Fixed +- `isfinite` on a `WindowMPOHamiltonian` threw a `MethodError` instead of returning `true`, + as the trait method was missing for this operator type. + ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) - `excitations(::InfiniteMPO, ::QuasiparticleAnsatz, ::InfiniteQP, lenvs, renvs)` threw `UndefVarError: H_eff not defined` on every call, because the effective excitation Hamiltonian was built from the not-yet-assigned `H_eff` instead of from `H`. Only this diff --git a/src/operators/windowhamiltonian.jl b/src/operators/windowhamiltonian.jl index a0833aa56..e95e10208 100644 --- a/src/operators/windowhamiltonian.jl +++ b/src/operators/windowhamiltonian.jl @@ -32,6 +32,7 @@ struct WindowMPOHamiltonian{O} <: AbstractMPO{O} "Hamiltonian acting on the infinite environment to the right of the window" right_ham::InfiniteMPOHamiltonian{O} end +Base.isfinite(::Type{<:WindowMPOHamiltonian}) = true function WindowMPOHamiltonian(ham::InfiniteMPOHamiltonian, interval::UnitRange) left_edge = (interval.start - 1) % length(ham) diff --git a/test/operators/windowhamiltonian.jl b/test/operators/windowhamiltonian.jl index 46d504e2a..3537d3d2b 100644 --- a/test/operators/windowhamiltonian.jl +++ b/test/operators/windowhamiltonian.jl @@ -27,6 +27,10 @@ using TensorKit: ℙ ψ = WindowMPS(gs, interval) Hw = WindowMPOHamiltonian(H, interval) @test length(Hw) == length(interval) + # the window itself is finite, even though its environments are not + @test isfinite(Hw) + @test isfinite(typeof(Hw)) + @test isfinite(Hw) == isfinite(typeof(Hw)) return expectation_value(ψ, Hw) end @test energies[1] ≈ energies[2] atol = 1.0e-8 From bba06f34bb0f1034b835f281eb82e0bcee9810e7 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Fri, 31 Jul 2026 12:02:16 -0400 Subject: [PATCH 4/4] shrink changelog --- docs/src/changelog.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 4a5e4d4c3..05a4af08d 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -61,15 +61,8 @@ When releasing a new version, move the "Unreleased" changes to a new version sec ### Fixed -- `isfinite` on a `WindowMPOHamiltonian` threw a `MethodError` instead of returning `true`, - as the trait method was missing for this operator type. - ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) -- `excitations(::InfiniteMPO, ::QuasiparticleAnsatz, ::InfiniteQP, lenvs, renvs)` threw - `UndefVarError: H_eff not defined` on every call, because the effective excitation - Hamiltonian was built from the not-yet-assigned `H_eff` instead of from `H`. Only this - entry point was affected: supplying a momentum instead of a quasiparticle state converts - the operator to a `MultilineMPO` first and never reached the faulty line. - ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) +- `isfinite(::WindowMPOHamiltonian)` was undefined. ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) +- `excitations(::InfiniteMPO, ::QuasiparticleAnsatz, ::InfiniteQP, lenvs, renvs)` referenced `H_eff` before assigning. ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) - `Base.:+`/`-` on `FiniteMPS` returned a wrong state for near-parallel operands carried by different tensor networks, e.g. `norm(E₀ * gs - H * gs)` coming out as `2 * norm(gs) * E₀` instead of ~0. The lazy gauge sweep in `CView.getindex` re-derived `AL`/`C` entries that were