Skip to content
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

Refactor interfaces built around alternating update #121

Merged
merged 32 commits into from
Jan 21, 2024

Conversation

b-kloss
Copy link
Contributor

@b-kloss b-kloss commented Jan 11, 2024

This PR aims to make the solver interface more flexible without changing the existing structure substantially. Examples of target use cases:
a) A call to a local subspace expansion preceding every local solve. This would be achieved by defining a solver that calls a local expander routine before e.g. exponentiate (both of which follow the solver function and return signature).
b) A call to a global/sweep subspace expansion preceding every n sweeps of TDVP with exponentiate as the local solver. This would be accomplished by defining a custom sweeps function that assembles the above pattern, passing the solver as a step_kwarg.

  • Pass the wavefunction, psi::TTN, and the projected Hamiltonian, PH::AbstractProjTTN, as a keyword arg by reference all the way to the solver functions which is called by local_update. This allows the solver function to modify the latter out-of-place, a functionality needed e.g. for subspace expansions accessed as a solver.
  • Restructuring and renaming of kwargs for better readability.
  • Decide on design:
    a) Future goal: Pass the solver function as step_kwarg, s.t. the decision about what gets done locally is accessible by modifying the sweeps pattern (which is defined at the same level as the solver).
    b) Currently: Implement all of the logic of choosing what solver to use locally inside the solver function (which should have the necessary information about the position in the sweep etc).
  • Update dmrg solver interface and tests.
  • Update dmrg-x solver inteface and tests. Also implement linsolve?
  • Implement what used to be triggered by noise as updater (for full DMRG functionality, the alternative being subspace expansion (or both) )

Naming updates:

  • Rename region_kwargs to region_update_kwargs (or combine with updater_kwargs?).
  • Rename psi_ref! to state! and PH_ref! to projected_operator!.
  • Rename psi0 to init_state, psi to state, H to operator, PH to projected_operator.

Open questions

  • Role of extract_local_tensor when region is an edge (single-site TDVP). Subspace expansion requires truncation at this step, but could be done inside solver, at the cost of performing SVD of a single-vertex-tensor twice.
  • refactor extract/insert_local_tensor using region and neighbouring region?

…amiltonian to solver by reference, passes more information about sweep to solver.
@b-kloss b-kloss marked this pull request as draft January 11, 2024 22:33
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
@mtfishman
Copy link
Member

Thanks for getting started with this. Could you format? It makes it easier to review the PR.

@b-kloss
Copy link
Contributor Author

b-kloss commented Jan 17, 2024

format(".") throws an error, not sure what's going on there ...
I have implemented most of the changes we talked about earlier today, but I may have missed a few things. At least it's functional and a good place to work from.
One issue i was wondering about is how to name the updater_kwargs. I chose to use the underlying function's kwargs (so krylovdim instead of solver_krylovdim --- but we may decide that it's better to provide a uniform naming scheme from ITensorNetworks side and translate at the level of the solver (I did that for outputlevel vs verbosity.)

src/solvers/applyexp.jl Outdated Show resolved Hide resolved
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
which_sweep,
region_updates,
which_region_update,
region_kwargs,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think region_update_kwargs sounds better to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though the same discussion on exponentiate_updater applies here, I think we should discuss how these are being passed and maybe merge them with updater_kwargs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this will be obsolete soon, I am in favor of sticking with region_kwargs.

src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
Comment on lines +9 to +10
region_kwargs,
updater_kwargs,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan to combine these into updater_kwargs?

Copy link
Contributor Author

@b-kloss b-kloss Jan 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updater_kwargs are updater specific kwargs, while region_args are among the things that we expose to all updaters. in principle, we can nest the region_args into updater_kwargs in the call to region_update but I am not sure if that's preferable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm ok, I just have found it hard to keep track of the logic of why certain keyword arguments are bundled in certain ways, how they will be used, etc.

For example, from the perspective of this function, the only argument I can see that is being used here from region_kwargs is time_step, which I don't think is really any different conceptually from the arguments being passed in updater_kwargs (it's just another thing being used by the solver/updater). So it makes sense to me to just bundle those together in one flat NamedTuple called updater_kwargs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, these will eventually be bundled together in an upcoming PR.

function dmrg_sweep(
nsite::Int, graph::AbstractGraph; root_vertex=default_root_vertex(graph)
)
return tdvp_sweep(2, nsite, Inf, graph; root_vertex, reverse_step=false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return tdvp_sweep(2, nsite, Inf, graph; root_vertex, reverse_step=false)
order = 2
time_step = Inf
return tdvp_sweep(order, nsite, time_step, graph; root_vertex, reverse_step=false)

so we remember what 2 and Inf mean.

Copy link
Member

@mtfishman mtfishman Jan 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I'm not sure how I feel about calling this dmrg_sweep since it could be used by other solvers like linsolve. I'm also not a huge fan of the function name tdvp_sweep for the same reason.

Maybe we can rethink this API a bit, for example rename the current tdvp_sweep to default_sweep_plan and then just call that with different inputs from within the different solvers. I think a good design for that would be to choose default values that "just work" for DMRG, including defaulting to order=2, and then provide optional inputs like reverse_step=true and time_step for use with TDVP. That may mean moving some of the inputs like order and time step to keyword arguments, which seems like a better design anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and this is what the default_sweep_regions function was trying to be. That is, a sort of "typical" sweep plan that most algorithms (dmrg, linsolve, etc.) could use. We could definitely think about moving that function out of the update_step.jl file but the intent there was for it to be a pretty generic default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I forgot about default_sweep_regions, I'm not sure what the fate of that is in the current PR since a lot of things are getting changed in this PR. But it seems like default_sweep_regions, tdvp_sweep, and dmrg_sweep should all get consolidated into a single function, which tentatively we are thinking of calling something like default_sweep_plan, default_region_updates, or default_region_update_plans (since it contains both the regions that will be updated but also information about how to do the update), which I think should just get called directly by the different solvers like dmrg, tdvp, linsolve, etc. and passed to alternating_update.

Opinions on what to name that function would be welcome. We want to come up with a good name for that function and what it outputs which we can then use consistently throughout the rest of the code. The list output by tdvp_sweep/dmrg_sweep is called region_updates in the current version of this PR but we are discussing alternative names, which hopefully will be consistent with the name of the new function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I incorporated the suggestion, and the sweep_regions constructor is planend to be refactored in an upcoming PR

@mtfishman
Copy link
Member

format(".") throws an error, not sure what's going on there

That's too bad. If you think it is a bug with the formatter (i.e. the code you wrote is correct Julia syntax and runs properly, but the formatter errors on it), you can try to track which line of code is giving the formatter issues and then tell the formatter to skip those lines of code with https://domluna.github.io/JuliaFormatter.jl/stable/skipping_formatting/#Skipping-Formatting. Then ideally we could make a minimal example of what is giving the formatter issues and raise an issue.

One issue i was wondering about is how to name the updater_kwargs. I chose to use the underlying function's kwargs (so krylovdim instead of solver_krylovdim --- but we may decide that it's better to provide a uniform naming scheme from ITensorNetworks side and translate at the level of the solver (I did that for outputlevel vs verbosity.)

I would generally bias towards naming the keyword arguments after the keyword argument names of the solver, so then we can just point to the documentation of those solvers for a list of supported keyword arguments. outputlevel seems like a good example of one that we translate. Though even for that one, I would consider not passing outputlevel to the solver itself (i.e. KrylovKit.eigsolve) and if users want to print from within the solver they need to use whatever keyword arguments are defined for that solver backend, and maybe outputlevel is just used in code we write ourselves, since each solver may have it's own interface for outputting different things from the solver and it could get messy trying to mesh that with our convention for how outputlevel works.

@b-kloss
Copy link
Contributor Author

b-kloss commented Jan 20, 2024

Missed the time-dependent tdvp tests.

src/imports.jl Outdated Show resolved Hide resolved
src/solvers/contract.jl Outdated Show resolved Hide resolved
src/solvers/contract.jl Outdated Show resolved Hide resolved
src/solvers/dmrg_x.jl Outdated Show resolved Hide resolved
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
src/solvers/eigsolve.jl Outdated Show resolved Hide resolved
@b-kloss b-kloss marked this pull request as ready for review January 21, 2024 01:51
@codecov-commenter
Copy link

Codecov Report

Attention: 19 lines in your changes are missing coverage. Please review.

Comparison is base (0b72b7c) 71.37% compared to head (812de92) 72.55%.
Report is 1 commits behind head on main.

Files Patch % Lines
src/solvers/linsolve.jl 0.00% 9 Missing ⚠️
...c/treetensornetworks/solvers/alternating_update.jl 87.50% 3 Missing ⚠️
src/solvers/eigsolve.jl 80.00% 2 Missing ⚠️
src/treetensornetworks/solvers/tdvp.jl 83.33% 2 Missing ⚠️
src/treetensornetworks/solvers/update_step.jl 95.91% 2 Missing ⚠️
src/treetensornetworks/solvers/tree_sweeping.jl 66.66% 1 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #121      +/-   ##
==========================================
+ Coverage   71.37%   72.55%   +1.17%     
==========================================
  Files          67       71       +4     
  Lines        4098     4055      -43     
==========================================
+ Hits         2925     2942      +17     
+ Misses       1173     1113      -60     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mtfishman
Copy link
Member

Looks good, thanks!

@mtfishman mtfishman merged commit 5ec5228 into ITensor:main Jan 21, 2024
8 of 11 checks passed
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.

4 participants