Skip to content

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

Open
borisdevos wants to merge 19 commits into
mainfrom
bd/multiline
Open

Fix Multiline (MultilineMPS/MultilineMPO) correctness and API inconsistencies#508
borisdevos wants to merge 19 commits into
mainfrom
bd/multiline

Conversation

@borisdevos

@borisdevos borisdevos commented Aug 12, 2026

Copy link
Copy Markdown
Member

I found a cluster of bugs that all traced back to the same root cause, namely Multiline{T} was inconsistent about what it is. Sometimes it was treated as a 2D array indexed by (row, col), and sometimes as a 1D sequence of T-typed lines, this while different parts of the code picked different conventions without checking consistency. The real bugs were:

  • MultilineMPO * MultilineMPS had never worked (straight up method error), and fixing that trivially still left both a shape bug (length counting rows*cols while map/collect only ever produced rows elements) and a silently wrong row-shift bug (fidelity 0 where it should be 1).
  • expectation_value(::MultilineMPS, ::MultilineMPO, ...) had a fallback method that silently returned something meaningless (prod instead of sum, no row shift, discarded envs which was noted through a todo) for any MultilineMPO line type it wasn't explicitly guarded against. Related to this, there was half-support for Hamiltonian or finite MPO multilines even though they're meaningless. Also the environments weren't being correctly converted, so they were being recalculated every row.
  • isfinite, changebonds(::MultilineMPO, ::SvdCut), and axes(m, 2) all threw outright.

A summary of the changes I made:

"Semantics":

  • Multiline{T} now consistently treats length/eltype/iterate/m[i] as referring to the lines (matching the periodic rows), while size/axes/eachindex refer to the (nrows, ncols) lattice shape. These two views are intentionally different (length(m) != prod(size(m))). I also documented this in docstrings.
  • Fixed multiplication: both were broken. The row-shifting is implemented and documented.
  • Deleted the expectation_value fallback. Changed the infinite MPS-MPO expectation value to correctly account for environments.
  • Fixed isfinite, changebonds(::MultilineMPO/::MultilineMPS, ::SvdCut), and axes(m, i).
  • Some grassmann code needed a change related to the 1d vs 2d view thing above.
  • Added the missing instance-level spacetype/sectortype/storagetype for multiline. I needed this for the custom show (see below).

Construction restrictions:

  • MultilineMPO only accepts InfiniteMPO lines now, enforced at constructor level. This is deliberate, since Multiline is also used via converts in environments, quasiparticles etc.
  • Removed the AbstractMatrix constructor that silently built finite-line MultilineMPOs, and the dead code in orthoview.jl that existed only to support finite MPS multilines.

Display:

  • MultilineMPS/MultilineMPO custom show: added a summary which looks at the 2d shape, and a show that renders each row using that row's own existing show method. Here below an example of how it looks now:

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:

  • New test/operators/multilinempo.jl: exact-fidelity tests for the row-shift convention in both MultilineMPO * MultilineMPS and MultilineMPO * MultilineMPO, using a permutation MPO whose fixed point is known exactly.
  • test/algorithms/statmech.jl: multi-row expectation_value regression test. Also a test that the Hamiltonian loophole above still correctly throws.
  • test/states/multilinemps.jl: added instance-level isfinite(ψ) (only the type-level form was covered before).

Documentation:

  • docs/src/man/states.md: added the row-shift convention, what's currently supported (infinite lines only), and subtleties.
  • docs/src/man/operators.md: new MultilineMPO section covering the same convention, and emphasising the MPO*MPO non-composability caveat.
  • Updated docstrings on Multiline, MultilineMPO, MultilineMPS, and expectation_value.
  • Added to the changelog, as the PR template wished of me 😄 .

Some things I didn't address because I'm not sure what to do with them:

  • VectorInterface stuff is still broken for MultilineMPS, simply because InfiniteMPS itself doesn't implement zerovector/scale.
  • norm(ψ) == sqrt(nrows) for a MultilineMPS of individually-normalized rows is unchanged. This goes through dot/inner sum over rows. I don't know if this is what you want.

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 28.35821% with 48 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/utility/show.jl 0.00% 36 Missing ⚠️
src/utility/multiline.jl 50.00% 5 Missing ⚠️
src/algorithms/changebonds/svdcut.jl 60.00% 2 Missing ⚠️
src/algorithms/expval.jl 50.00% 2 Missing ⚠️
src/environments/multiline_envs.jl 50.00% 1 Missing ⚠️
src/operators/multilinempo.jl 80.00% 1 Missing ⚠️
src/states/orthoview.jl 50.00% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/algorithms/changebonds/randexpand.jl 63.63% <100.00%> (+35.06%) ⬆️
src/algorithms/grassmann.jl 79.03% <100.00%> (+79.03%) ⬆️
src/states/multilinemps.jl 81.39% <ø> (+34.72%) ⬆️
src/environments/multiline_envs.jl 80.35% <50.00%> (+80.35%) ⬆️
src/operators/multilinempo.jl 72.72% <80.00%> (+52.72%) ⬆️
src/states/orthoview.jl 89.17% <50.00%> (+25.53%) ⬆️
src/algorithms/changebonds/svdcut.jl 89.18% <60.00%> (+13.51%) ⬆️
src/algorithms/expval.jl 87.35% <50.00%> (+39.12%) ⬆️
src/utility/multiline.jl 63.49% <50.00%> (+42.18%) ⬆️
src/utility/show.jl 12.30% <0.00%> (-2.79%) ⬇️

... and 59 files with indirect coverage changes

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

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.

1 participant