Translation symmetry in 1D with OperatorTS1D
Here we will show how to take advantage of translation symmetry to save time and memory in PauliStrings.jl. Consider the 1D Ising Hamiltonian with periodic boundary conditions $H=-J(\sum_{i}Z_i Z_{i+1} +g \sum_i X_i)$. There is no need to actually store all the Pauli strings in this case. H is fully specified by just $-JZ_1Z_2$ and $-Jg X_1$ and the fact that it's translation symmetric.
In general, a 1D translation symmetric operator can be written as $\sum_i T_i H_0$ where $T_i$ is the i-sites translation operator and $H_0$'s is the local operator that generates $H$. $H_0$ can be chosen so that it's only composed of Pauli strings that start on the first site.
In PauliStrings.jl, the structure OperatorTS1D
lets you manipulate operators in this format. If your problem is 1D translation symmetric, OperatorTS1D
will be much faster than Operator
.
Construction
There are two ways of constructing an OperatorTS1D
:
From the full translation symmetric $H$
First we construct the full Operator
:
function ising1D(N, J, g)
H = Operator(N)
for j in 1:(N - 1)
H += "Z",j,"Z",j+1
end
H += "Z",1,"Z",N #periodic boundary condition
for j in 1:N
H += g,"X",j
end
return -J*H
end
H = ising1D(N, J, g)
then convert it to an OperatorTS1D
Hts = OperatorTS1D(H)
If H
is not translation symmetric, then an error will be returned
From the local generator $H_0$
Construct $H_0$ using Operator
:
H = Operator(N)
H += -J, "Z",1,"Z",2
H += -J*g,"X",1
then convert it to an OperatorTS1D
Hts = OperatorTS1D(H, full=false)
Note that here, we need to set full=false
in order to specify that we are not passing the full Hamiltonian but just its local generator.
Manipulation
All the operations defined on Operator
are also defined on OperatorTS1D
.
Construct a simple translation invariant operator on 4 sites:
H = Operator(N)
H += "X", 1
H += "Z", 1,"Z", 2
Hts = OperatorTS1D(H, full=false)
println(H)
println(Operator(Hts))
(1.0 + 0.0im) X111
(1.0 + 0.0im) ZZ11
(1.0 + 0.0im) 1ZZ1
(1.0 + 0.0im) 1X11
(1.0 + 0.0im) X111
(1.0 + 0.0im) Z11Z
(1.0 + 0.0im) 11ZZ
(1.0 + 0.0im) 11X1
(1.0 + 0.0im) ZZ11
(1.0 + 0.0im) 111X
Note that only the local generator is printed when printing OperatorTS1D
, not the full operator.
Multiplication :
julia> Hts*Hts
(1.0 + 0.0im) X1X1
(2.0 + 0.0im) Z1Z1
(2.0 + 0.0im) 1111
(1.0 + 0.0im) ZZZZ
(2.0 + 0.0im) XZZ1
(2.0 + 0.0im) XX11
(2.0 + 0.0im) ZZX1
Adition
julia> Hts+Hts
(2.0 + 0.0im) X111
(2.0 + 0.0im) ZZ11
etc..
Example: computing $Tr(H^k)$
As an example of performance gains of using OperatorTS1D
instead of Operator
we compute the 8th moment ($Tr(H^8)$) of a 30 spin system.
Using the function defined above we construct a Ising Hamiltonian and convert it to an OperatorTS1D
:
N = 30
k = 8
H = ising1D(N, 1)
Hts = OperatorTS1D(H)
then we compute the kth moment (trace_product
) using both Operator
and OperatorTS1D
:
julia> @time println(trace_product(H, k))
1.1904927790006272e18 + 0.0im
80.697013 seconds (28.91 k allocations: 111.213 MiB, 0.07% gc time, 0.04% compilation time)
julia> @time println(trace_product(Hts, k))
1.190492779000627e18 + 0.0im
1.951678 seconds (37.09 k allocations: 36.165 MiB, 2.00% gc time, 2.01% compilation time)
OperatorTS1D
is 40 times faster in this case.