-
Notifications
You must be signed in to change notification settings - Fork 25
Improve time evolution interface #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve time evolution interface #280
Conversation
Codecov Report❌ Patch coverage is
... and 10 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
I'm going to try making time evolution an iterator (the approach of YASTN) to allow easier user access to the intermediate states. |
lkdvos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely like the idea of having an iterator approach, I agree that this is better to allow more control over intermediate states etc.
For this particular implementation, there are some suggestions for organizational changes that I think make a slightly more robust (and type-stable) framework though.
The first thing has to do with the organization of the structs. To be more in line with the rest, and also to keep things well-separated, I think it would be nice to distinguish a bit more the iterator, the algorithm and the state.
What I mean with this is something like this:
struct SimpleUpdate <: MPSKit.Algorithm
maxiter
dt
trscheme
...
end
struct SUIterator
alg::SimpleUpdate
operator
state
endFor more inspiration, see e.g. https://github.com/QuantumKitHub/MPSKit.jl/blob/main/src/states/ortho.jl, or even the current implementation of VUMPS.
I am trying to formalize these notions into a package, which additionally has the purpose of generalizing the stopping criteria and the logging, but that is still a bit WIP. Linking it here though: https://github.com/JuliaManifolds/AlgorithmsInterface.jl
There already is some information in the discussions of that repository, which I think can be interesting to go over. In the long run, I would definitely like to switch over to that style anyways.
With this in mind, I also don't think I like not implementing timestep, which should really be equivalent to iterate(iterator, state) up to some reshuffling of algorithms.
I don't particularly mind whether we implement the first in terms of the second or the other way around, but it seems like it could be useful to have both.
Somewhat related, as the cost of a single SU step is lower than the typical CTMRG ones, it would be nice to ensure that we guarantee some form of type stability. This would mean making sure that iterate is inferrable, which probably means putting some type parameters into the different structs.
Do let me know what you think about most of these points, I'm happy to discuss further and obtain more viewpoints on this.
As a somewhat unrelated note, may I please ask for the future to try and avoid having both formatting/naming changes and algorithm changes in the same PR? I'm not against reformatting or changing names, and I understand that it is inconvenient to separate everything out, but it makes reviewing PRs a lot more involved if 80% of the lines that are showed as "changed" don't have anything to do with the actual logic changes.
|
@lkdvos Is it a good idea to first let copilot generate a draft based on your suggestion? (I haven't tried copilot yet by myself) |
|
I have no issue with you trying this out, we can always ignore the changes it proposes if it turns out to not work the way we like |
|
I may describe an additional complication in full update that In SU, apart form convergence checking, all iterations are the same. But in (fast) full update, the CTMRGEnv is only carefully re-converged (with My current design is that each |
|
For the logging: LoggingExtras.withlevel(f; it.verbosity)runs LoggingExtras.withlevel(; it.verbosity) do
# body
endis syntactically equivalent to Therefore, if you don't return anything from within the I'm happy to exclude a bunch of these functions from the docs if you like, but I'd rather do that by restricting the docs, while keeping the docstrings. I find these actually very useful, and fundamentally disagree with the idea that a private internal function cannot have a docstring. In an organization where multiple people work on different parts of the code, it seems to me almost required to be able to have this. For the function Base.iterate(it::FUIterator, state = it.state)
if state.iteration % it.environment_frequency == 0
leading_boundary
end
# actual iterator implementation
end |
|
Latest changes:
|
|
@lkdvos Ready for (hopefully) final review |
lkdvos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a couple small comments, but otherwise I think this looks more or less ready to merge for me!
lkdvos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me this looks good to go, great work!
|
Thanks for the effort, this is really nice! I can regenerate the docs examples tomorrow (unless someone does it before me :)) so that we can tag a new release. |
This PR provides an improved interface for current and upcoming (Trotter-based) time evolution algorithms.
Time evolution is now performed by an iterator, to allow easier access and finer control of the intermediate steps.
statestores the evolution outcome after each step, and will change during the evolution.dtcan be any real or complex number or any precision.gate.For simple update,
statecontains the following data:The upper-level interface of time evolution are the following functions:
t0records the initial time of the input state. The time of the output state is recorded ininfo.t.imaginary_time::Boolin theSimpleUpdatealgorithm struct. The default value istrue(different from MPSKit).tol > 0:ground_state_searchfunction. (1) In practice one set thenstepto a very large number (of order 1e+5 to 1e+6), instead of letting it evolve indefinitely, since sometimes things don't converge well. (2) Except the need to check convergence, it is not too different from other usages. (3) It's usually not actually the ground state, and only serve as initialization for more sophisticated optimizations.To do: