-
Notifications
You must be signed in to change notification settings - Fork 16
Add 3-site simple update (aka 3-site cluster update) #171
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
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
I further added a test to compare the SU and 3-site SU energy for the Hubbard model without next-nearest neighbor terms (they should be close). I think the code is now ready for fermions. |
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 some general comments throughout, keep in mind that I do not have much experience with the algorithm itself so I definitely might be missing some things or getting things wrong.
As always though, this is an impressive amount of work, and a great addition.
V = space(Pb, 1) | ||
f = isomorphism(flip(V), V) | ||
f2 = twist(f, 1) | ||
# Pa ← f†) → (f ← s ← f†) → (f ← Pb | ||
@tensor begin | ||
Pa[-1; -2] := Pa[-1; 1] * f'[1; -2] | ||
Pb[-1; -2] := f2[-1; 1] * Pb[1; -2] | ||
s[-1; -2] := f2[-1; 1] * s[1; 2] * f'[2; -2] | ||
end | ||
s = DiagonalTensorMap(permute(s, ((2,), (1,)))) |
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 think there is functionality in TensorKit to "flip" the arrow of a tensor: flip(t, i)
should do this. This might be more efficient and readable and avoid having to reconstruct the diagonal tensor.
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.
Thanks for informing me! But I find that flip
acting on s
will return a ordinary TensorMap
, so I still need to add the conversion to DiagonalTensorMap
.
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.
Jutho/TensorKit.jl#243
Will look into fixing this soon ™️
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.
So I didn't really think this through: defining flip(::DiagonalTensorMap)
is a little bit more involved... In this case, I think you can avoid the entire flip by doing the svd in the other direction, ie tsvd!(transpose(rl); trunc)
and then transposing the results. Why does this show up though, i.e. why do we need the rev
option?
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 did consider doing tsvd
in the other direction, but didn't use this approach since I'm exhausted getting the twists right... I want to keep the current implementation and wait for the upstream improvement in TensorKit.
I need rev
because the arrows in the cluster do not always point in the same direction. Under the usual choice of arrows in an InfiniteWeightPEPS
, each :sw
cluster looks like this:
M1
↓
y
↓
M2 ← x ← M3
Thus if I don't transpose rl
for tsvd
, I need to reverse the arrow direction between M1
and M2
afterwards.
In addition, After I apply the 3-site gate MPO, I fuse the internal virtual legs of the gate MPO and the cluster such that the result is M1 ← M2 ← M3
. So I need to convert the result back to M1 → M2 ← M3
. In principle I could construct the fusers that preserve the arrow direction in the cluster, so I don't need to use an additional rev
argument to specify on which bonds should I reverse the arrows. But I want to be more flexible and enable manual tweaking of the arrows on each bond.
BTW, I found that transpose(rl)
behave differently from permute(rl, ((2,), (1,)))
. Is it because transpose
will not produce fermion parity signs?
using TensorKit
V = Vect[FermionParity](0 => 2, 1 => 2)
t = rand(V ← V);
transpose(t) ≈ permute(t, ((2,), (1,))) # false
transpose(t) ≈ twist(permute(t, ((2,), (1,))), 1) # true
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.
The difference between the two is that transpose
is planar, while permute
is not. In other words, transpose
will bend the legs either clockwise or anticlockwise (does not alter the result), while permute
will cross them.
I understand the feeling about the twists, it really is not the nicest thing to work around...
Perform simple update for next-nearest neighbor Hamiltonian `ham`, | ||
where the evolution information is printed every `check_interval` steps. | ||
""" | ||
function simpleupdate3site( |
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.
Is there a way to detect the kind of hamiltonian that is supplied, and automatically map this to either the nearest-neighbour or the 3-site
version?
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.
This can surely be done: just check that for each term in the Hamiltonian, the distance between the sites of this term is 1. But this can be added in another PR. For test purposes I may deliberately to pass a Hamiltonian with only nearest neighbor terms to the 3-site SU.
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.
The reason I'm asking is mostly since I was wondering if we really want to export simpleupdate3site
as an external-facing function. I would prefer it if you could just use simpleupdate
, and this would then either tell you that it is not supported or dispatch to a lower level function to implement the specific case at hand.
This has the benefit that if we ever extend or change this, we don't necessarily have to perform breaking changes for this.
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.
Now simpleupdate
can automatically decide whether to use the 2-site or the 3-site version depending on whether the input ham
only has nearest neighbor terms. But I also added a keyword argument force_3site
to use the 3-site version for any ham
.
Co-authored-by: Lukas Devos <[email protected]>
Co-authored-by: Lukas Devos <[email protected]>
@pbrehmer I have added an example to do SU for J1-J2 model (with U(1) spin symmetry), but haven't generate the docs yet. Could you please try if the obtained SU state can be a good starting point for AD optimization? |
I'll take a look tomorrow and try out the SU J1-J2 state. I you want, I can then add a section to the example which uses the SU state to initialize an AD optimization (if it works well). |
AD optimization seems to work but it has some hickups at some point due to linesearching. I will try to improve that tomorrow. I also extended the commentary in the J1-J2 example a bit to make it similar style-wise to other examples. |
I decided to just restrict the |
@pbrehmer I'm glad to see that with SU initialization the AD energy is lower than YASTN result, so maybe a good initialization does help. How does the energy compare with the AD optimization with just 1x1 unit cell starting from random initialization? |
I was also happy to see that! Actually, on a 1x1 unit cell from random initialization, I'm having a lot of trouble to optimize the PEPS. There, I'm encountering degenerate singular values sometimes and CTMRG sometimes struggles to converge during optimization such that the linesearch fails. I also tried Lander's backtracking linesearch algorithm and also there I'm having problems. So my feeling is that the SU-evolved PEPS is actually quite a nice starting point for the AD optimization. I'll try to optimize that fully (up to higher accuracy) but I suspect that the environment dimension might be a little low for that - for small chi, PEPS optimization runs tend to get stuck at some point. |
Looking at YASTN data, the first line is
So he is already using In the calculation he also imposes the geometric |
Indeed! I'm not yet sure if |
This PR adds the 3-site simple update (aka 3-site cluster update; see discussion in Issue #164), which can handle Hamiltonians containing up to next nearest neighbor terms (e.g. the J1-J2 model).
LocalOperator
format to a bunch of 3-site MPOs acting on the clusters. Relevant functions are added insrc/algorithms/time_evolution/evoltools.jl
.src/algorithms/time_evolution/simpleupdate3site.jl
. It is tested in the new test filetest/timeevol/cluster_projectors.jl
.examples/heisenberg_evol/j1j2.jl
. We may also consider using the 3-site SU to provide initialization in the J1-J2 AD test. But note that the 3-site SU also requires a minimal unit cell size (2, 2) (in addition, bipartite structure is now not allowed), while the AD test currently uses 1-site unit cell with C4v symmetry.This is kind of a large unseparated PR. Suggestions are welcome. I'm still testing fermion models, so the code is not finalized yet.