-
Notifications
You must be signed in to change notification settings - Fork 33
[WIP] Boundary-MPS approximate contraction of an iPEPO window with CTMRG environment #415
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: main
Are you sure you want to change the base?
Changes from all commits
200ce7c
7aab80f
4fa7745
84f56e0
f158212
93476a3
2991f9b
86c1367
631df2c
72b0733
6f5f4d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,238 @@ | ||
| """ | ||
| Check that the physical legs of a first OBC-MPO tensor match the PEPO site's physical space. | ||
| """ | ||
| function _check_pepo_first_physicalspace(A, op) | ||
| physicalspace(A) == space(op, 1) == space(op, 2)' || | ||
| throw(SpaceMismatch("first MPO tensor physical space does not match PEPO site")) | ||
| return nothing | ||
| end | ||
|
|
||
| """ | ||
| Check that the physical legs of a last OBC-MPO tensor match the PEPO site's physical space. | ||
| """ | ||
| function _check_pepo_last_physicalspace(A, op) | ||
| physicalspace(A) == space(op, 2) == space(op, 3)' || | ||
| throw(SpaceMismatch("last MPO tensor physical space does not match PEPO site")) | ||
| return nothing | ||
| end | ||
|
|
||
| """ | ||
| Check that the physical legs of a middle MPO tensor match the PEPO site's physical space. | ||
| """ | ||
| function _check_pepo_middle_physicalspace(A, op) | ||
| physicalspace(A) == space(op, 2) == space(op, 3)' || | ||
| throw(SpaceMismatch("middle MPO tensor physical space does not match PEPO site")) | ||
| return nothing | ||
| end | ||
|
|
||
| """ | ||
| Convert a symbolic cardinal path direction to the corresponding PEPO virtual-leg index. | ||
| """ | ||
| function _mpo_path_direction(direction::Symbol) | ||
| direction === :north && return NORTH | ||
| direction === :east && return EAST | ||
| direction === :south && return SOUTH | ||
| direction === :west && return WEST | ||
| throw(ArgumentError("invalid MPO path direction: $direction")) | ||
| end | ||
|
|
||
| """ | ||
| Return the tensor-expression label for the PEPO virtual leg in a cardinal direction. | ||
| """ | ||
| function _mpo_path_virtual_label(direction::Symbol) | ||
| return (:N, :E, :S, :W)[_mpo_path_direction(direction)] | ||
| end | ||
|
|
||
| """ | ||
| Canonicalize an incoming MPO fuser so its fused PEPO leg has standard dualness. | ||
| """ | ||
| function _mpo_path_incoming_fuser(F, direction::Int) | ||
| direction in (NORTH, EAST) && return F | ||
| direction in (SOUTH, WEST) && return twist(flip(F, 1), 1) | ||
| throw(ArgumentError("invalid MPO path direction index: $direction")) | ||
| end | ||
|
|
||
| """ | ||
| Canonicalize an outgoing MPO fuser so its fused PEPO leg has standard dualness and braiding. | ||
| """ | ||
| function _mpo_path_outgoing_fuser(F, direction::Int) | ||
| direction in (NORTH, EAST) && return twist(flip(F, 1), 1) | ||
| direction in (SOUTH, WEST) && return twist(F, 3) | ||
| throw(ArgumentError("invalid MPO path direction index: $direction")) | ||
| end | ||
|
|
||
| """ | ||
| Build the `@tensor` labels from `(direction, suffix)` pairs | ||
| used to fuse MPO virtual strings. | ||
|
|
||
| - Each direction is `:north`, `:east`, `:south`, or `:west`. | ||
| - Suffix `:l` marks an incoming MPO bond and `:r` an outgoing one. | ||
|
|
||
| Examples: | ||
|
|
||
| - `((:east, :r),)` produces `[W S; N Er]`. | ||
| - `((:west, :l), (:north, :r))` produces `[Wl S; Nr E]`. | ||
| """ | ||
| function _mpo_path_result_expr(directions) | ||
| labels = [:N, :E, :S, :W] | ||
| for (direction, suffix) in directions | ||
| index = _mpo_path_direction(direction) | ||
| labels[index] = Symbol(labels[index], suffix) | ||
| end | ||
| return tensorexpr(:t, (labels[WEST], labels[SOUTH]), (labels[NORTH], labels[EAST])) | ||
| end | ||
|
|
||
| """ | ||
| Act the first tensor `op` of an OBC-MPO on PEPO tensor `A` and fuse the | ||
| outgoing MPO string with the virtual space of `A` along `direction`. | ||
| """ | ||
| @generated function mpo_path_first(A::PEPOTensor, op, ::Val{direction}) where {direction} | ||
| direction_index = _mpo_path_direction(direction) | ||
| virtual_label = _mpo_path_virtual_label(direction) | ||
| fused_label = Symbol(virtual_label, :r) | ||
|
|
||
| result_e = _mpo_path_result_expr(((direction, :r),)) | ||
| op_e = tensorexpr(:op, :dout, (:din, :r)) | ||
| A_e = tensorexpr(:A′, (:din, :dout), (:N, :E, :S, :W)) | ||
| F_e = tensorexpr(:F, fused_label, (virtual_label, :r)) | ||
| rhs = Expr(:call, :*, op_e, A_e, F_e) | ||
| contraction = macroexpand( | ||
| @__MODULE__, :(return @tensoropt $result_e := $rhs) | ||
| ) | ||
|
|
||
| return quote | ||
| _check_pepo_first_physicalspace(A, op) | ||
| A′ = twistdual(A, 2) | ||
| F = _mpo_path_outgoing_fuser( | ||
| fuser(storagetype(A), domain(A, $direction_index)', space(op, 3)), | ||
| $direction_index, | ||
| ) | ||
| $contraction | ||
| end | ||
|
Comment on lines
+103
to
+111
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is more a personal preference, but since these kinds of generated functions are already hard to follow as is, I think it would be more transparent/safer to split off the regular function part in the beginning of the quote here, and then call the generated function on the variables, really isolating the generated part without any "normal" function part like assigning variables. Same comment for |
||
| end | ||
|
|
||
| """ | ||
| Act the last tensor `op` of an OBC-MPO on PEPO tensor `A` and fuse the | ||
| incoming MPO string with the virtual space of `A` along `direction`. | ||
| """ | ||
| @generated function mpo_path_last(A::PEPOTensor, op, ::Val{direction}) where {direction} | ||
| direction_index = _mpo_path_direction(direction) | ||
| virtual_label = _mpo_path_virtual_label(direction) | ||
| fused_label = Symbol(virtual_label, :l) | ||
|
|
||
| result_e = _mpo_path_result_expr(((direction, :l),)) | ||
| F_e = Expr(:call, :conj, tensorexpr(:F, fused_label, (virtual_label, :l))) | ||
| op_e = tensorexpr(:op, (:l, :dout), :din) | ||
| A_e = tensorexpr(:A′, (:din, :dout), (:N, :E, :S, :W)) | ||
| rhs = Expr(:call, :*, F_e, op_e, A_e) | ||
| contraction = macroexpand( | ||
| @__MODULE__, :(return @tensoropt $result_e := $rhs) | ||
| ) | ||
|
|
||
| return quote | ||
| _check_pepo_last_physicalspace(A, op) | ||
| A′ = twistdual(A, 2) | ||
| F = _mpo_path_incoming_fuser( | ||
| fuser(storagetype(A), domain(A, $direction_index), space(op, 1)'), | ||
| $direction_index, | ||
| ) | ||
| $contraction | ||
| end | ||
| end | ||
|
|
||
| """ | ||
| Act the middle tensor `op` of an MPO on PEPO tensor `A` and fuse the | ||
| incoming and the outgoing MPO string with the virtual space of `A` along | ||
| `directions = (incoming, outgoing)`. | ||
| """ | ||
| @generated function mpo_path_middle( | ||
| A::PEPOTensor, op, ::Val{directions} | ||
| ) where {directions} | ||
| incoming, outgoing = directions | ||
| incoming == outgoing && | ||
| throw(ArgumentError("MPO path should enter and exit in different directions")) | ||
|
|
||
| incoming_index = _mpo_path_direction(incoming) | ||
| outgoing_index = _mpo_path_direction(outgoing) | ||
| incoming_label = _mpo_path_virtual_label(incoming) | ||
| outgoing_label = _mpo_path_virtual_label(outgoing) | ||
| fused_incoming_label = Symbol(incoming_label, :l) | ||
| fused_outgoing_label = Symbol(outgoing_label, :r) | ||
|
|
||
| result_e = _mpo_path_result_expr(((incoming, :l), (outgoing, :r))) | ||
| Fin_e = Expr( | ||
| :call, :conj, | ||
| tensorexpr(:Fin, fused_incoming_label, (incoming_label, :l)), | ||
| ) | ||
| op_e = tensorexpr(:op, (:l, :dout), (:din, :r)) | ||
| A_e = tensorexpr(:A′, (:din, :dout), (:N, :E, :S, :W)) | ||
| Fout_e = tensorexpr( | ||
| :Fout, fused_outgoing_label, (outgoing_label, :r) | ||
| ) | ||
| rhs = Expr(:call, :*, Fin_e, op_e, A_e, Fout_e) | ||
| contraction = macroexpand( | ||
| @__MODULE__, :(return @tensoropt $result_e := $rhs) | ||
| ) | ||
|
|
||
| return quote | ||
| _check_pepo_middle_physicalspace(A, op) | ||
| A′ = twistdual(A, 2) | ||
| Fin = _mpo_path_incoming_fuser( | ||
| fuser(storagetype(A), domain(A, $incoming_index), space(op, 1)'), | ||
| $incoming_index, | ||
| ) | ||
| Fout = _mpo_path_outgoing_fuser( | ||
| fuser(storagetype(A), domain(A, $outgoing_index)', space(op, 4)), | ||
| $outgoing_index, | ||
| ) | ||
| $contraction | ||
| end | ||
| end | ||
|
|
||
| """ | ||
| Route an MPO virtual string with `stringspace` through a PEPO tensor `A` | ||
| along `directions = (incoming, outgoing)`. | ||
| """ | ||
| @generated function mpo_path_string( | ||
| A::PEPOTensor, stringspace::ElementarySpace, ::Val{directions} | ||
| ) where {directions} | ||
| incoming, outgoing = directions | ||
| incoming == outgoing && | ||
| throw(ArgumentError("MPO path should enter and exit in different directions")) | ||
|
|
||
| incoming_index = _mpo_path_direction(incoming) | ||
| outgoing_index = _mpo_path_direction(outgoing) | ||
| incoming_label = _mpo_path_virtual_label(incoming) | ||
| outgoing_label = _mpo_path_virtual_label(outgoing) | ||
| fused_incoming_label = Symbol(incoming_label, :l) | ||
| fused_outgoing_label = Symbol(outgoing_label, :r) | ||
|
|
||
| result_e = _mpo_path_result_expr(((incoming, :l), (outgoing, :r))) | ||
| Fin_e = Expr( | ||
| :call, :conj, | ||
| tensorexpr(:Fin, fused_incoming_label, (incoming_label, :l)), | ||
| ) | ||
| O_e = tensorexpr(:O, (:W, :S), (:N, :E)) | ||
| I_e = tensorexpr(:I, :l, :r) | ||
| Fout_e = tensorexpr( | ||
| :Fout, fused_outgoing_label, (outgoing_label, :r) | ||
| ) | ||
| rhs = Expr(:call, :*, Fin_e, O_e, I_e, Fout_e) | ||
| contraction = macroexpand( | ||
| @__MODULE__, :(return @tensoropt $result_e := $rhs) | ||
| ) | ||
|
|
||
| return quote | ||
| O = trace_physicalspaces(A) | ||
| I = id(storagetype(A), stringspace) | ||
| Fin = _mpo_path_incoming_fuser( | ||
| fuser(storagetype(A), domain(A, $incoming_index), stringspace'), | ||
| $incoming_index, | ||
| ) | ||
| Fout = _mpo_path_outgoing_fuser( | ||
| fuser(storagetype(A), domain(A, $outgoing_index)', stringspace'), | ||
| $outgoing_index, | ||
| ) | ||
| $contraction | ||
| end | ||
| end | ||
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 it would look a bit cleaner to just use the same kind of short circuited switches as above, instead of instantiating and indexing this tuple on every call.