Skip to content

Fix Multiline (MultilineMPS/MultilineMPO) correctness and API inconsistencies - #508

Merged
lkdvos merged 8 commits into
mainfrom
bd/multiline
Oct 5, 2026
Merged

lkdvos merged 8 commits into
mainfrom
bd/multiline

Conversation

@borisdevos

@borisdevos borisdevos commented Aug 12, 2026 •

Copy link
Copy Markdown
Member

Multiline{T} was inconsistent about what it is: sometimes a 2D array indexed by (row, col), sometimes a 1D sequence of T-typed lines, with different parts of the code picking different conventions. The real bugs this caused:

  • expectation_value(::MultilineMPS, ::MultilineMPO, ...) had a fallback that silently returned something meaningless (prod instead of sum, no row shift, envs discarded) for any line type not explicitly guarded, including Hamiltonian lines.
  • MultilineMPO * MultilineMPS never worked, and naive fixes left both a shape bug (length counting rows*cols) and a silently wrong row shift.
  • isfinite(::MultilineMPO), changebonds(::MultilineMPO, ::SvdCut) and axes(m, i) for i > 2 all threw.

Changes

Semantics

  • Multiline is a plain vector of its lines: length/size/axes/eachindex/eltype/iterate/m[i] all refer to the lines, so size(m) == (length(m),). The lattice of lines and sites is only indexed through the orthogonality views (ψ.AL[row, col] etc.) and lattice-valued queries such as physicalspace(ψ); internal code that needs the number of columns uses linelength(m).
  • New dominant_eigenvalue(ψ, O, [envs]): for a multiline pair, the contracted quantity is the eigenvalue of the transfer operator, not an overlap, since bra and ket are different lines. expectation_value(::InfiniteMPS, ::InfiniteMPO) forwards to it, and the statmech algorithms log it.
  • expectation_value(::MultilineMPS, ::MultilineMPO) is removed, together with its unguarded fallback, in favor of dominant_eigenvalue. Note for PEPSKit: test/boundarymps/vumps.jl calls expectation_value(mps, T) on a MultilineTransferPEPS and needs to switch to dominant_eigenvalue; its VUMPS keeps working through its contract_mpo_expval overload.
  • *(::MultilineMPO, ::MultilineMPS) and *(::MultilineMPO, ::MultilineMPO) are removed; *(::InfiniteMultilineMPO, ::InfiniteMPS) pushes the boundary through every row, advancing it one full period.
  • Fixed isfinite, changebonds(::MultilineMPO/::MultilineMPS, ::SvdCut), axes(m, i), and added instance-level spacetype/sectortype/storagetype.

Line types

  • MultilineMPS lines are Union{InfiniteMPS, FiniteMPS}, MultilineMPO lines Union{InfiniteMPO, FiniteMPO}; Hamiltonian lines are excluded.
  • New aliases InfiniteMultilineMPS/FiniteMultilineMPS and InfiniteMultilineMPO/FiniteMultilineMPO dispatch on the kind of line.
  • Finite lines can be built and inspected, but no algorithm supports them: leading_boundary and changebonds(_, _, ::OptimalExpand) only accept InfiniteMultilineMPS (and InfiniteMultilineMPO where the operator is typed), so they fail at dispatch.
  • Removed the AbstractMatrix constructor that silently built finite-line MultilineMPOs. checkbounds on the multiline orthoviews dispatches on infinite vs finite lines.

Display

  • summary reports the 2D shape, and show renders each row through that line's own show, with the row mapping shown for MultilineMPO.

Here the multiline MPS:

Details
2×2 MultilineMPS(ComplexF64, Vect[IsingAnyon]) with maximal dimension 10.0:
row 1:
2-site InfiniteMPS(ComplexF64, Vect[IsingAnyon]) with maximal dimension 10.0:
| ⋮
| (:σ => 7)
├─[2]─ (:σ => 1)
│ (:I => 5, :ψ => 5)
├─[1]─ (:σ => 1)
│ (:σ => 7)
| ⋮

  ⋮
row 2:
2-site InfiniteMPS(ComplexF64, Vect[IsingAnyon]) with maximal dimension 10.0:
| ⋮
| (:I => 5, :ψ => 5)
├─[2]─ (:σ => 1)
│ (:σ => 7)
├─[1]─ (:σ => 1)
│ (:I => 5, :ψ => 5)
| ⋮

And here a multiline MPO:

Details
2×2 MultilineMPO(ComplexF64, Vect[IsingAnyon]) with maximal dimension 1.4142135623730951:
row 1:
2-site InfiniteMPO(ComplexF64, Vect[IsingAnyon]) with maximal dimension 1.4142135623730951:
| ⋮
| (:σ => 1)
┼─[2]─ (:σ => 1)
│ (:σ => 1)
┼─[1]─ (:σ => 1)
│ (:σ => 1)
| ⋮

  ↓  (row 1 maps onto row 2)
row 2:
2-site InfiniteMPO(ComplexF64, Vect[IsingAnyon]) with maximal dimension 1.4142135623730951:
| ⋮
| (:σ => 1)
┼─[2]─ (:σ => 1)
│ (:σ => 1)
┼─[1]─ (:σ => 1)
│ (:σ => 1)
| ⋮

  ↓  (row 2 maps onto row 1)

Tests

  • test/mpo/multiline.jl: exact row-shift tests for MultilineMPO * InfiniteMPS with permutation MPOs, row- and column-accumulated dominant_eigenvalue, a 2-row leading_boundary check (λ₂ ≈ λ₁²), the line aliases, and that the removed expectation_value, Hamiltonian lines and finite lines are rejected.
  • test/states/multilinemps.jl: instance-level isfinite(ψ), and the vector semantics (size, axes, eachindex, eltype) next to the 2D shape of the views.

Documentation

  • states.md: row-shift convention, why a multiline has one fixed point, and subtleties (size vs iteration, norm(ψ) == sqrt(nrows), finite lines).
  • operators.md: new MultilineMPO section. algorithms.md: dominant_eigenvalue under leading_boundary.
  • Docstrings on Multiline, MultilineMPO, MultilineMPS, expectation_value, dominant_eigenvalue; changelog entries.

Breaking

length/size/axes/eachindex/eltype of Multiline (length was nrows * ncols and size the lattice shape, now both follow the lines; eltype(::MultilineMPS) was the site tensor type, now the line type), removal of expectation_value on multiline objects, of the MultilineMPO(::AbstractMatrix) constructor and of * between multiline objects, and the narrower line types.

Not addressed

  • VectorInterface support for MultilineMPS, since InfiniteMPS itself doesn't implement zerovector/scale.
  • Multiline quasiparticle excitations: effective_excitation_hamiltonian(::MultilineMPO, ::MultilineQP, envs) uses parent(envs).leftenvs on a Vector, and the renormalization energy uses the same line as bra and ket. Both predate this PR.

Checklist

  • Tests pass locally (julia --project=test test/runtests.jl, or the relevant subset)
  • Documentation updated, if this PR changes public API (docstrings, docs/src/)
  • Runic formatter is run
  • Changelog entry added under [Unreleased] in docs/src/changelog.md, if this PR is user-facing (new feature, behavior change, bug fix, deprecation, or removal)

@codecov

codecov Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.44118% with 47 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/utility/show.jl 0.00% 30 Missing ⚠️
src/operators/multilinempo.jl 60.00% 4 Missing ⚠️
src/states/orthoview.jl 82.35% 3 Missing ⚠️
src/algorithms/changebonds/svdcut.jl 60.00% 2 Missing ⚠️
src/utility/multiline.jl 83.33% 2 Missing ⚠️
src/algorithms/grassmann.jl 88.88% 1 Missing ⚠️
src/algorithms/groundstate/vumps.jl 83.33% 1 Missing ⚠️
src/algorithms/statmech/vomps.jl 80.00% 1 Missing ⚠️
src/environments/multiline_envs.jl 50.00% 1 Missing ⚠️
src/states/multilinemps.jl 83.33% 1 Missing ⚠️
... and 1 more
Files with missing lines Coverage Δ
src/MPSKit.jl 100.00% <ø> (ø)
src/algorithms/approximate/idmrg.jl 98.41% <100.00%> (ø)
src/algorithms/changebonds/optimalexpand.jl 98.87% <100.00%> (ø)
src/algorithms/changebonds/randexpand.jl 63.63% <100.00%> (ø)
src/algorithms/expval.jl 94.38% <100.00%> (+1.27%) ⬆️
src/algorithms/statmech/gradient_grassmann.jl 100.00% <ø> (ø)
src/algorithms/statmech/idmrg.jl 100.00% <100.00%> (ø)
src/algorithms/toolbox.jl 97.89% <100.00%> (+0.03%) ⬆️
src/algorithms/grassmann.jl 80.64% <88.88%> (+0.80%) ⬆️
src/algorithms/groundstate/vumps.jl 97.43% <83.33%> (-2.57%) ⬇️
... and 9 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@leburgel leburgel self-assigned this Aug 18, 2026
@borisdevos

Copy link
Copy Markdown
Member Author

After having discussed with @leburgel, I realised I didn't fully appreciate/understand what multiline actually represents, so recent changes rectify some mistakes I made, and tried to explain these niche things more cleanly in the docs/docstrings.

  • I now allow finite lines at type and constructor level, but there's still no support for these at the algorithmic level. Hamiltonians are still correctly rejected.
  • Multiplying multiline things never made sense, so I removed these methods and added the one that does make sense.
  • Some orthoview changes I previously removed, plus fixing bounds checking on these views.
  • I took the liberty of not calling the value that you would calculate with multiline an expectation value, but rather what it is, a dominant eigenvalue. So I introduced a new function dominant_eigenvalue, explained why it's separate from expectation_value and when these actually collide. The tricky thing here was that the VUMPS code worked for both MPOs and Hamiltonians, so there's a funky check there to see whether expectation values or dominant eigenvalues are being logged.
  • Docs and docstrings clarifications.

Comment thread docs/src/changelog.md Outdated
Comment thread src/algorithms/expval.jl Outdated
Comment thread src/states/multilinemps.jl Outdated
Comment thread src/states/orthoview.jl Outdated
Comment thread src/states/orthoview.jl Outdated
Comment thread src/utility/multiline.jl Outdated
Comment thread src/utility/multiline.jl Outdated
Comment thread src/operators/multilinempo.jl Outdated
Comment thread docs/src/examples/classic2d/1.hard-hexagon/index.md Outdated
Comment thread docs/src/examples/classic2d/1.hard-hexagon/index.md Outdated
Comment thread docs/src/man/algorithms.md Outdated
Comment thread docs/src/man/algorithms.md Outdated
Comment thread docs/src/man/operators.md Outdated
Comment thread docs/src/man/operators.md Outdated
Comment thread docs/src/man/operators.md Outdated
Comment thread docs/src/man/states.md Outdated
Comment thread docs/src/man/states.md Outdated
Comment thread src/algorithms/groundstate/vumps.jl Outdated
@lkdvos lkdvos mentioned this pull request Sep 24, 2026
7 tasks done
@lkdvos
lkdvos changed the base branch from main to bd/tdvp-errors October 2, 2026 16:42
Base automatically changed from bd/tdvp-errors to main October 2, 2026 17:04
lkdvos and others added 6 commits October 2, 2026 13:07
`length`, `eltype`, iteration and `m[i]` now refer to the stored lines,
while `size`/`axes`/`eachindex` keep describing the `(nrows, ncols)`
lattice shape. Internal code uses `parent` instead of reaching into `.data`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lines are now `Union{InfiniteMPS, FiniteMPS}` and `Union{InfiniteMPO, FiniteMPO}`,
which excludes Hamiltonian lines. `InfiniteMultilineMPS`/`FiniteMultilineMPS` and
`InfiniteMultilineMPO`/`FiniteMultilineMPO` dispatch on the kind of line. Finite
lines can be built and inspected, but `leading_boundary` only accepts infinite
lines. The meaningless `*` methods between multiline objects are replaced by
`*(::InfiniteMultilineMPO, ::InfiniteMPS)`, and `checkbounds` on the multiline
orthoviews now dispatches on the line type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a `MultilineMPS`/`MultilineMPO` pair the contracted quantity is the
eigenvalue of the transfer operator rather than an overlap, since bra and
ket are different lines. `expectation_value` on multiline objects is removed,
`expectation_value(::InfiniteMPS, ::InfiniteMPO)` forwards to the new function,
and the statmech algorithms log `dominant_eigenvalue` instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each row is rendered through its own line's display, and for `MultilineMPO`
the row-to-row mapping is shown explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`size`, `axes` and `eachindex` now follow `length`, so `size(m) == (length(m),)`
instead of the `(nrows, ncols)` lattice shape. The orthogonality views keep indexing
the lattice as `[row, col]`, and code that needs the number of columns uses the
internal `linelength`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lkdvos and others added 2 commits October 2, 2026 15:17
- rename `dominant_eigenvalue` to `leading_eigenvalue` and stop exporting it
- allow any `AbstractMPS`/`AbstractMPO` lines in `MultilineMPS`/`MultilineMPO`
- rename the internal column count to `width`
- one `checkbounds` for the multiline views, delegating the column to the line's tensors
- drop instance-level `spacetype`/`sectortype`/`storagetype`, TensorKit already forwards them
- pick the VUMPS objective by dispatch instead of an `isa` switch
- free energy as `-log` of the eigenvalue in the hard-hexagon example, and docs wording

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@borisdevos

Copy link
Copy Markdown
Member Author

Looks good from my end! Maybe @leburgel wants to take a look before merging?

@leburgel leburgel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@lkdvos
lkdvos merged commit d6fcc7e into main Oct 5, 2026
71 of 72 checks passed
@lkdvos
lkdvos deleted the bd/multiline branch October 5, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants