DMRG3S: strictly single-site DMRG subspace expansion (Hubig et al. 2015)#460
DMRG3S: strictly single-site DMRG subspace expansion (Hubig et al. 2015)#460VinceNeede wants to merge 13 commits into
Conversation
|
Your PR no longer requires formatting changes. Thank you for your contribution! |
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
|
Thanks for this, I had a brief look and I think it already looks quite well. Considering the expansion algorithm itself - without having looked in a lot of detail so definitely feel free to tell me I'm wrong - I think it could be nice to avoid having both an For the TDVP implementation, I definitely agree to flush this one out first, and honestly it's probably easier to tackle and review that in a separate PR. Considering your other question, I'm definitely happy to add the |
776835c to
f2c34cc
Compare
|
Hi @lkdvos, Pushed a sketch of merging I haven't wired up Since |
lkdvos
left a comment
There was a problem hiding this comment.
Thanks for rebasing onto that other work!
Considering the DMRG constructor, I think I agree that we should exclude being able to set conflicting schemes, but unless I'm missing something that would already not be possible - the gauging algorithm is either a (truncated) orth algorithm or DMRG3S, and in the latter case there is only a single (truncated) orth algorithm in the DMRG3S.
If at all possible, I would like to design the DMRG constructor to be able to take the trscheme still, and have that routed through automatically, or error if both a trscheme and DMRG3S are provided. My experience is that while these "algorithm structs" are very convenient as a developer, it is somewhat harder as a newcomer to the package to keep track of everything, and a simple keyword-argument approach, which possibly does not expose all settings but handles the common cases, helps alleviate some of these issues.
| V = right_virtualspace(AC) | ||
| combiner, Vpert = _get_combiner(T, V, right_virtualspace(Hi)) | ||
|
|
||
| @plansor pert[-1 -2; -3] := α * El[-1 1; 2] * AC[2 3; 4] * Hi[1 -2; 3 5] * combiner[4, 5; -3] |
There was a problem hiding this comment.
I think this contraction might be the same as our AC_projection contractions, and it could be beneficial to reuse that - the main reason is that we also support density-matrix MPS, which have 2 physical legs and would break this contraction, and reusing that function automatically fixes that.
There was a problem hiding this comment.
I already took a look at AC_projection, and from my understanding, it requires to supply all the enviroments, both left and right, here instead only one is required at a time. I'm not sure it can be adapted to this use case
There was a problem hiding this comment.
yes, but the combiner can be thought of as an environment in this case, so while the code routing is a bit more complex, the contraction is the same as this one:
MPSKit.jl/src/algorithms/derivatives/mpo_derivatives.jl
Lines 69 to 70 in 5bbab08
There was a problem hiding this comment.
I have tried using MPO_AC_Hamiltonian, but the assumption of this line breaks:
For a proper environment, the resulting MPS does have a single block, but in the case of a combiner this breaks. For an hamiltonian of bond dimension 3, the right environment has blocks
y as a I also tried
JordanMPO_AC_Hamiltonian, but it requires indexing the right environmentAnd this does not work for
combiner, because getindex only works witth zero or 1 non trivial dimensions https://github.com/QuantumKitHub/BlockTensorKit.jl/blob/215f9283301cc4d964d5819754320d6c4a25a064/src/vectorspaces/sumspaceindices.jl#L63-L76
I don't think I'm in a good position to safely change either of these, so I'd rather get your take on how you'd want this handled before I try anything myself. Is there a way you'd recommend getting the raw, uncollapsed contraction out of this framework, or is this a case where the current abstraction just isn't meant to support it and I should go another route entirely?
There was a problem hiding this comment.
Thanks for checking and explaining why it fails, I definitely also missed that.
Looking into this in a little more detail, I guess we probably don't want to get a
In particular, it should be more efficient to immediately make the fuser that collapses these levels together. I would have to look at the exact implementation requirements, but I think you can call TensorKit.oplus on the fused spaces to combine them. (Unfortunately, that indeed now means we can no longer reuse the fuser function, but that's fine).
Let me know if this makes sense, I definitely might be missing something, and I'm happy to explain more of what I'm thinking of.
The idea would definitely be to try and reuse the MPO_AC_Hamiltonian, not the JordanMPO one.
There was a problem hiding this comment.
Hi @lkdvos,
I was trying to use oplus to flatten a leg, but ran into a loss of precision in doing so (my second DMRG3S test, which checks proper subspace exploration, started failing). Tracked it down to unitary/isomorphism producing a non-unitary map when the domain is a SumSpace and the codomain is its flattened (oplus(...)) counterpart — even when every sector's dimension matches exactly between the two. Minimal example:
using TensorKit, BlockTensorKit, LinearAlgebra
V1 = Rep[U₁](0 => 2, 1 => 1)
V2 = Rep[U₁](0 => 2, 1 => 1)
Vsum = V1 ⊞ V2
Vflat = oplus(Vsum)
u = unitary(ComplexF64, Vflat, Vsum)
@show norm(u' * u - id(Vsum)) # 1.73..., should be ~0
@show norm(only(u * u') - id(Vflat)) # 1.73..., should be ~0Is this a known limitation, or should I open a proper issue (and if so, here or on TensorKit)? Happy to give more detail / try a fix there.
Thanks!
There was a problem hiding this comment.
This is definitely not a known limitation and almost certainly is a bug. Feel free to open an issue on BlockTensorKit.jl, where I assume this is originating from, and I'll try and have a look asap.
Update
TODO
|
Add `FunctionalSchedule`, wrapping an arbitrary `(noise, iter, ϵ) -> noise` callable as a `NoiseSchedule`. Overload `∘` on `NoiseSchedule` to compose two schedules into a `FunctionalSchedule`, following the usual function composition convention (`s2` applied first, `s1` applied to its result). Add a `threshold` keyword to `ExponentialDecay`, snapping the decayed noise to exactly zero once it falls below it -- avoids running the (cheap but non-free) expansion step indefinitely on a vanishingly small amplitude. This also makes `ExponentialDecay ∘ Warmup` a natural way to combine a hard iteration cutoff with smooth decay.
fix typo in variable name
…mic `MPO_AC_Hamiltonian`
|
Thanks for the update! I commented on the reuse of the AC_projection, hopefully that makes sense, and I think it is safe to assume that the alg_gauge computation per site is not hurting performance, indeed after these two this looks ready to go for me. |
Summary
Implements DMRG3S — strictly single-site DMRG with subspace expansion — from Hubig, McCulloch, Schollwöck & Wolf, Phys. Rev. B 91, 155115 (2015). Unlike CBE (
alg_expand), which enriches the bond before the local eigensolve, DMRG3S enriches it after, using a perturbation built from the just-optimized tensor, the local Hamiltonian, and the environment. This lets single-site DMRG escape local minima caused by missing quantum-number sectors that CBE-style pre-expansion can't reach.This PR also targets TDVP, pending design agreement
DMRG only for now. TDVP support belongs in this same PR, but I'd like design agreement on the interface below first, since it directly shapes how it plugs into TDVP too. Docs are also on hold for the same reason — no point documenting an interface that might still change.
What's here
find_groundstate!'s left-to-right and right-to-left sweeps from two separate, near-duplicate loops into a single loop overdirection in (:right, :left), dispatching onVal. No behavioral change; this just made room for the new expansion step without doubling its code.alg_post_expandfield onDMRG, dispatching a newpost_expand!step after the eigensolve, alongside the existing pre-eigensolvealg_expand. Default (NoExpand()) reproduces current behavior exactly.DMRG3S, with a composableNoiseSchedulefor the mixing factor (ExponentialDecay,Warmup,∘for combining them).alg_expandandalg_post_expandare independent and can be used together or separately.alg_gauge, since the bond would otherwise grow unboundedly each sweep.Open design questions for maintainers
alg_post_expandsentinel: I used a dedicatedNoExpand()type rather thannothing(whichalg_expanduses). This letsNoExpandcarry its ownpost_expand!method for dispatch, but it's an inconsistency withalg_expand's convention worth flagging.post_expand!call rather than kept separate, because splitting them left the MPS briefly inconsistent between steps (caught via a real bug during development — happy to share details). This does meanpost_expand!needsalg_gaugethreaded in as a parameter, coupling it to the gauge step more tightly thanalg_expandis.TODO before merge