perf: DMRG(2) with smarter adaptive tolerances#455
Conversation
8e22df4 to
8ab7f54
Compare
|
Here I've added some results on the comparisons for the sweeptimes, but also on the energy reached after a given walltime, in an attempt to not just measure what the fastest way is to get 10 sweeps done. I'd also say that it seems really hard to make generalizations about the actual walltimes it takes to get to a given energy, since the faster sweeps get compensated with the necessity for more sweeps. In general, this is not faster everywhere, but I think it at least feels faster because the sweeps take a little less time, and it makes a naive benchmark no longer favor the ITensor version that much.
|
borisdevos
left a comment
There was a problem hiding this comment.
This is amazing work, Lukas! The benchmarks also look very promising.
I was just wondering whether we should be more verbose about how AdaptiveKrylov is now the default everywhere. Also, this is not being compared explicitly in our tests by name, though I'm not sure what the criteria would be.
The refactoring indeed makes the code much easier to follow! The title of the PR could also added "performance" arguably ;)
You should definitely let others review this PR as well, since I'm no expert really, but hopefully I brought up some things with substance.
|
Thanks a lot @borisdevos for taking the time to go over this, the feedback is definitely appreciated! Some more things I would love inputs on:
|
Switch the DMRG/DMRG2 stop test from the overlap-based convergence error `ϵ_conv = 1 - |⟨old|new⟩|` to the Galerkin error (already computed each local update as `ϵ_local`/`ϵ_global`), matching VUMPS/iDMRG. Drop the now-unused `ϵ_conv` bookkeeping from both `local_update!` methods and `find_groundstate!`. Perturb the default `FiniteExcited` initial guess with a small per-site random component. Under the Galerkin criterion an unperturbed ground-state guess can already be (near) an eigenvector of the shifted operator, so convergence would trigger on the first sweep and return the ground state instead of climbing to the excited state. The perturbation preserves spaces (symmetry sector) and bond dimensions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DMRG2 reused `HAC2 = Heff * ac2` (needed for the Galerkin `ϵ_local`) as the eigensolver seed. Seeding with H*AC is power-iteration intuition (it amplifies the largest-magnitude eigenvector), which is wrong for `:SR` (smallest algebraic): it de-weights the ground state, and when the effective ground eigenvalue is 0 it annihilates the ground-state component exactly. Since H preserves the ground state's orthogonal complement, the Krylov solve then converges to the smallest nonzero eigenvalue instead — the wrong state. This bites frustration-free/parent Hamiltonians and any H - E0 shift. Seed with the plain two-site center `ac2` instead, consistent with single-site DMRG's `ac_old`. `HAC2` is still computed for the Galerkin gradient only. Verified on a finite TFIM (L=16, g=1) shifted so E0=0: old seeding gives a non-convergent 4.73 (ϵ_conv≈0.88), fixed seeding gives 5e-9≈0 (ϵ_conv≈2e-15); unshifted DMRG2 still matches single-site DMRG to 1e-14. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…port different metrics and ignore ones that aren't present
`gauge2!` unconditionally promoted the SVD center matrix to complex (`complex(c)`), which for a real-valued state forced a complexify then coerce-back-to-real roundtrip when installing the tensors. Only complexify when the state's scalartype is itself complex. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The two-site Galerkin error was projected onto BOTH the left and right null spaces (the (null,null) bond-expansion corner), which is exactly what the truncated SVD discards. It therefore collapsed to the truncation floor after a single local solve and spuriously signalled convergence after one sweep, leaving DMRG2 under-converged (e.g. the multifusion Ising ground state stopped 2.6e-6 above exact with variance 3e-5 after one sweep). Project onto the LEFT null space of `AL[pos]` only, matching single-site `calc_galerkin`, so the measure faithfully tracks convergence. The faithful measure floors at the truncation error (the gradient of a truncated state toward the untruncated optimum), so `ϵ_global <= tol` is unreachable under truncation. Converge instead at `max(tol, maxϵ_trunc)` — once the gradient reaches the level the truncation allows. With no truncation (single-site / QR gauge, `ϵ_trunc = 0`) this reduces to the plain `<= tol`. Note: penalized runs (`FiniteExcited`) can still floor above the truncation and hit maxiter; a more general stop criterion is left to a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`updatetol` kept a docstring before the `adapt_solver` refactor; restore one for the deprecated shim so the exported name stays documented. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I've updated the implementation and the PR description, benchmark results at the top. The important changes made here:
|




This PR features a somewhat large refactor of the DMRG implementations, and finally fixes #311 hopefully in a more systematic way.
The main idea is that I want to use loose bounds on the krylov solvers, since currently everyone has the wrong assumption that ITensors is a lot faster than MPSKit, which is just due to the default settings.
Nevertheless, while for ITensorMPS you just have to know and read the docs to figure out that if you want to actually converge something for a non-gapped state, I wanted to keep some guarantees on stability and convergence rates.
Therefore, this PR introduces a bunch more knobs to determine adaptive tolerance and krylov settings based on the information that is available:
A secondary part of this PR is just me refactoring the DMRG implementations into a more readable and consolidated form, which I think overall just improves the code quailty.
Benchmark summary
Backing up the claims in this PR, I've attached a number of results from investigations I did. The headline is that this new adaptive local eigensolver is an across-the-board improvement over the current main.
Doing a large amount of hyperparameter tests, it seems like the main point is that the results aren't too sensitive to the actual values of the various tolerance factors etc, but the general statement is still true that too high of a precision on the local solvers is not useful at the start of a run, but essential towards the end, specifically for gapless systems. The main thing is that the overhead of switching sides is actually quite large at higher bond dimensions (the SVD, hamiltonian building and environment transfers), such that the extra iterations in the krylov solvers are well worth it because that work has to be done anyways.
I think I'm happy with the current settings, which seems to at least balance having reasonably fast sweeps in the easy regimes and no stagnation in the harder regimes, so for me this PR is now ready.
Benchmark plots