Skip to content

Iterator-based sweep solvers - #537

Merged
lkdvos merged 15 commits into
mainfrom
ld-dmrg-iterators
Oct 14, 2026
Merged

lkdvos merged 15 commits into
mainfrom
ld-dmrg-iterators

Conversation

@lkdvos

@lkdvos lkdvos commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Description

Restructures MPSKit's sweep-based solvers and time integrators as iterators with a common shape, adds an in-place timestep! for infinite TDVP, and fixes three time-evolution bugs found along the way. Apart from the behaviour changes listed below, results, logging and returned values are unchanged.

Sweep solvers

Every converted solver has a state struct, and Base.iterate runs sweep!(it, state, Val(:right), iter) followed by sweep!(it, state, Val(:left), iter), then any finalize or error step. Finite sweeps take their sites from _sweep_ranges. Converted:

  • finite find_groundstate with DMRG/DMRG2 (DMRGState, sweep_converged)
  • finite approximate! with DMRG/DMRG2
  • propagator with DynamicalDMRG (NaiveInvert, Jeckelmann)
  • multiline approximate! and leading_boundary with IDMRG/IDMRG2, sharing one IDMRGState and one bond-change measure (bondresidual)

Time evolution

  • A single TimeStepState carries one time step t → t + dt (state, operator, environments, time, scheduler, allocator and truncation errors).
  • Finite TDVP, TDVP2 and BUG are each just a per-site local_update!. A step is a forward and a backward sweep!, and each half-sweep advances state.t by dt / 2.
  • An infinite TDVP step is split into localupdate_step! (concurrent AC/C integration), gauge_step! (regauge and in-place gaugefix!) and recalculate! of the environments.
  • _timestep!(alg, state) is the single entry point. One generic finite timestep!, one copying timestep and the shared _timestep_copy live in time_evolve.jl, with alg left abstract. Each algorithm only provides _timestep! and _promotes_to_complex.
  • time_evolve/time_evolve!: one iteration is one time step plus finalize. Only the first step copies (or promotes) the state. After that, every step runs in place and reuses the step state's scheduler and allocator.
  • New: in-place timestep!(::InfiniteMPS, H, t, dt, ::TDVP), which also makes time_evolve! work for infinite TDVP.

Bug fixes

  • InfiniteMPS(C, AR) threw a MethodError (eltype instead of scalartype), which broke infinite TDVP with leftorthflag = false.
  • Real-time infinite TDVP from a real InfiniteMPS with a real Hamiltonian failed with an InexactError. The environments are now rebuilt for the promoted complex state, as for finite MPS.
  • TDVP, TDVP2 and BUG now promote a real state to complex for imaginary-time evolution with a complex dt, rather than failing.

Behaviour changes

  • A finalize callback in time_evolve that keeps the state of an earlier step now sees it updated by later steps, as with time_evolve!, because steps run in place.
  • For multiline IDMRG with several rows, the changes of the rows are now summed, as IDMRG2 already did, instead of combined in a 2-norm. This makes the stopping criterion marginally stricter.
  • Accumulating state.t per half-sweep can change the time passed to a time-dependent Hamiltonian in the last bit ((t + dt/2) + dt/2 vs t + dt).

Smaller cleanups

  • IterLog(::Algorithm) names logs after the algorithm type.
  • The redundant = it.state defaults on Base.iterate are removed, since iterativesolvers.jl already provides the one-argument method.
  • state arguments are now typed, which avoids method ambiguities with the generic time_evolve iterator.

This gives the finite environment cache from ld-envs per-site hook points in DMRG, TDVP/TDVP2, BUG and dynamical DMRG. ld-envs will be rebased onto this branch. It is also a step towards migrating these solvers to AlgorithmsInterface.jl, which will be a separate PR.

Verification

  • Equivalence against main (sweep-solver refactor): scripts ran the same cases on main and on this branch with fixed seeds, comparing with isequal.
    • Compared: raw MPS tensors, AlgorithmInfo fields, energies and returned values, every iteration log record and warning, and the arguments of finalize calls.
    • All 168 cases were identical: 28 DMRG/approximate, 8 dynamical DMRG, 18 IDMRG and 114 TDVP/BUG/time_evolve.
    • Coverage includes converging and maxiter-limited runs, maxiter = 0, CBE and DMRG3S, nearest-neighbour and long-range models, real-valued initial states, time-dependent operators, and empty or single-point t_span.
  • Shared finite step state: the 114 finite TDVP/TDVP2/BUG cases are bit-identical before and after reusing the step state across time_evolve steps.
  • Infinite TDVP: 222 cases (serial and dynamic schedulers, both gauges, real and complex states and steps, time-dependent operators, time_evolve), compared before and after the infinite rewrite.
    • Every final state where the old code worked is identical.
    • All 59 cases that previously errored (the two bugs above) now run.
    • The remaining differences are expected:
      • finalize snapshots, because steps are in place;
      • one complex-dt case that previously used incorrect real environments;
      • a warning, consistent with finite MPS, when real environments are rebuilt for a promoted state.
  • Tests: all timeevolution/* and states/* tests pass, including new tests for in-place infinite timestep!/time_evolve!, leftorthflag = false, real infinite TDVP and the InfiniteMPS(C, AR) constructor. The groundstate, approximate, dynamical DMRG, statmech, fidelity susceptibility and anyonic braiding tests passed on the solver refactor.
  • Ambiguities: Test.detect_ambiguities(MPSKit) is unchanged from main (135).

Checklist

  • Tests pass locally (relevant subset, see above)
  • Documentation updated, if this PR changes public API: adds timestep!(::InfiniteMPS, …, ::TDVP); no other public API change
  • Runic formatter is run
  • Changelog entry added under [Unreleased] in docs/src/changelog.md

🤖 Generated with Claude Code

@lkdvos
lkdvos requested review from borisdevos and leburgel October 12, 2026 20:17
@lkdvos

lkdvos commented Oct 12, 2026

Copy link
Copy Markdown
Member Author

For context, this is the first part of some other changes where I want to uniformize the approach of the algorithms, because I actually need to rework the way the environments work. I figured out we are doing some of the expensive work in the AC_hamiltonian construction twice, and want to cache that which, depending on the model, leads to quite substantial speedups. However, it basically means that I want some way of tracking internal state per algorithm, therefore leading me to push for uniformizing this solver pattern, which we already had for VUMPS and some of the other algorithms for example.

I'm still trying to do some work on AlgorithmsInterface to actually commit switching to that, which I still think will make some of the boilerplate here less annoying, as well as getting a better way of controlling the logging and stopping criteria without requiring changes in the library itself.

@lkdvos
lkdvos force-pushed the ld-dmrg-iterators branch from 0ed8fa7 to f4b30c9 Compare October 12, 2026 20:22
@lkdvos
lkdvos marked this pull request as ready for review October 12, 2026 20:22
Comment thread src/algorithms/statmech/idmrg.jl
Comment thread src/algorithms/timestep/tdvp.jl Outdated

@borisdevos borisdevos 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.

Minor comments on my part, but it's all refactoring and I don't think many things are lost. Homogenising things like this will make maintenance easier in the long run, so I'm in favor (even though it's yet another iteration of this code, every time I look at it it's different 😆 )

@lkdvos
lkdvos force-pushed the ld-dmrg-iterators branch from f4b30c9 to 59ddaf2 Compare October 13, 2026 13:53
lkdvos and others added 15 commits October 14, 2026 10:13
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All sweep-based iterators now share DMRG's shape: `Base.iterate` runs
`sweep!(it, state, Val(:right), iter)` and `sweep!(it, state, Val(:left), iter)`,
with finite sweeps taking their sites from `_sweep_ranges`. The IDMRG
convergence measure is shared as `_center_change`. `IterLog(::Algorithm)`
names logs after the algorithm, and the redundant `= it.state` defaults
on `Base.iterate` are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`bond_change` replaces the three copies of the center-bond convergence measure
(groundstate, multiline approximation and leading boundary IDMRG/IDMRG2). It
compares bond tensors of different dimension on their common subspace through
block views instead of isometry products, and sums the changes over the rows
of a multiline MPS, as IDMRG2 already did. The multiline approximation and
leading boundary searches now use the groundstate `IDMRGState`, dispatching
on its MPS and operator types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A `TimeStepState` carries a single time step `t → t + dt` through its two
half-sweeps, and one `sweep!(alg, state, direction)` drives the per-site
`local_update!(site, direction, ψ, alg, state)` of each integrator, taking the
sites from `_sweep_ranges` and recording truncation errors per bond.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`time_evolve` with finite TDVP, TDVP2 or BUG now keeps its `TimeStepState`,
and with it the sweep allocator, from one step to the next. The copying
variant copies (or promotes to complex) the initial state once, through the
same `_timestep_copy` used by the copying `timestep` methods, and takes every
later step in place. Other algorithms still go through `timestep`/`timestep!`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split the infinite TDVP step into `localupdate_step!`, `gauge_step!` and an environment update on
`TimeStepState`, mirroring VUMPS, and carry the step's allocator over between `time_evolve` steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add `timestep!(::InfiniteMPS, ..., ::TDVP)`, which restores the gauge in place, and route infinite
TDVP through the shared copying `timestep` and the in-place `time_evolve` step, so that finite and
infinite steps take the same path. `TimeStepState` now carries the scheduler with its allocator.

Fix `InfiniteMPS(C, AR)`, which called `eltype` instead of `scalartype` and therefore failed for
infinite TDVP with `leftorthflag = false`, and rebuild the environments when a real infinite state
is promoted to complex, as for finite MPS, instead of writing complex data into real environments.
A real state is now also promoted for imaginary-time evolution with a complex time step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finite `timestep!` and the copying `timestep` are now single methods that
build the step state and call `_timestep!`, and `time_evolve` steps every
integrator in place. The step state tracks the time of the state, so the
local updates no longer recompute the half-sweep times. Also trims a few
internal state comments, folds the in-place infinite TDVP checks into the
scheduler testset, and drops the `bondresidual` changelog entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lkdvos
lkdvos force-pushed the ld-dmrg-iterators branch from e323891 to 34aef02 Compare October 14, 2026 14:48
@lkdvos

lkdvos commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

documentation failure is unrelated, will merge once tests pass

@lkdvos
lkdvos merged commit 7bf9da7 into main Oct 14, 2026
59 of 70 checks passed
@lkdvos
lkdvos deleted the ld-dmrg-iterators branch October 14, 2026 17:50
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.

2 participants