Repository navigation
Iterator-based sweep solvers - #537
Conversation
|
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. |
0ed8fa7 to
f4b30c9
Compare
borisdevos
left a comment
There was a problem hiding this comment.
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 😆 )
f4b30c9 to
59ddaf2
Compare
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>
e323891 to
34aef02
Compare
|
documentation failure is unrelated, will merge once tests pass |
Description
Restructures MPSKit's sweep-based solvers and time integrators as iterators with a common shape, adds an in-place
timestep!for infiniteTDVP, 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.iteraterunssweep!(it, state, Val(:right), iter)followed bysweep!(it, state, Val(:left), iter), then any finalize or error step. Finite sweeps take their sites from_sweep_ranges. Converted:find_groundstatewithDMRG/DMRG2(DMRGState,sweep_converged)approximate!withDMRG/DMRG2propagatorwithDynamicalDMRG(NaiveInvert,Jeckelmann)approximate!andleading_boundarywithIDMRG/IDMRG2, sharing oneIDMRGStateand one bond-change measure (bondresidual)Time evolution
TimeStepStatecarries one time stept → t + dt(state, operator, environments, time, scheduler, allocator and truncation errors).TDVP,TDVP2andBUGare each just a per-sitelocal_update!. A step is a forward and a backwardsweep!, and each half-sweep advancesstate.tbydt / 2.TDVPstep is split intolocalupdate_step!(concurrentAC/Cintegration),gauge_step!(regauge and in-placegaugefix!) andrecalculate!of the environments._timestep!(alg, state)is the single entry point. One generic finitetimestep!, one copyingtimestepand the shared_timestep_copylive intime_evolve.jl, withalgleft abstract. Each algorithm only provides_timestep!and_promotes_to_complex.time_evolve/time_evolve!: one iteration is one time step plusfinalize. 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.timestep!(::InfiniteMPS, H, t, dt, ::TDVP), which also makestime_evolve!work for infiniteTDVP.Bug fixes
InfiniteMPS(C, AR)threw aMethodError(eltypeinstead ofscalartype), which broke infiniteTDVPwithleftorthflag = false.TDVPfrom a realInfiniteMPSwith a real Hamiltonian failed with anInexactError. The environments are now rebuilt for the promoted complex state, as for finite MPS.TDVP,TDVP2andBUGnow promote a real state to complex for imaginary-time evolution with a complexdt, rather than failing.Behaviour changes
finalizecallback intime_evolvethat keeps the state of an earlier step now sees it updated by later steps, as withtime_evolve!, because steps run in place.IDMRGwith several rows, the changes of the rows are now summed, asIDMRG2already did, instead of combined in a 2-norm. This makes the stopping criterion marginally stricter.state.tper half-sweep can change the time passed to a time-dependent Hamiltonian in the last bit ((t + dt/2) + dt/2vst + dt).Smaller cleanups
IterLog(::Algorithm)names logs after the algorithm type.= it.statedefaults onBase.iterateare removed, sinceiterativesolvers.jlalready provides the one-argument method.statearguments are now typed, which avoids method ambiguities with the generictime_evolveiterator.This gives the finite environment cache from
ld-envsper-site hook points in DMRG, TDVP/TDVP2, BUG and dynamical DMRG.ld-envswill be rebased onto this branch. It is also a step towards migrating these solvers to AlgorithmsInterface.jl, which will be a separate PR.Verification
main(sweep-solver refactor): scripts ran the same cases onmainand on this branch with fixed seeds, comparing withisequal.AlgorithmInfofields, energies and returned values, every iteration log record and warning, and the arguments offinalizecalls.time_evolve.maxiter = 0, CBE and DMRG3S, nearest-neighbour and long-range models, real-valued initial states, time-dependent operators, and empty or single-pointt_span.time_evolvesteps.time_evolve), compared before and after the infinite rewrite.finalizesnapshots, because steps are in place;dtcase that previously used incorrect real environments;timeevolution/*andstates/*tests pass, including new tests for in-place infinitetimestep!/time_evolve!,leftorthflag = false, real infinite TDVP and theInfiniteMPS(C, AR)constructor. The groundstate, approximate, dynamical DMRG, statmech, fidelity susceptibility and anyonic braiding tests passed on the solver refactor.Test.detect_ambiguities(MPSKit)is unchanged frommain(135).Checklist
timestep!(::InfiniteMPS, …, ::TDVP); no other public API change[Unreleased]indocs/src/changelog.md🤖 Generated with Claude Code