Let's implement a power law long-range ZZ interaction with open boundary conditions and some uniform field. Our Hamiltonian is
$$H = \sum_{i,j} \frac{J}{\left| i-j \right| ^ \alpha} \sigma^z_i \sigma^z_j + \vec{h} \cdot \sum_i \vec{\sigma}_i$$where $J$ is the interaction strength, $\alpha$ is the power-law decay with distance between sites, and the vector $\vec{h}$ is the static, uniform field.
First we import the things we will need:
In [1]:
from dynamite import config
from dynamite.operators import sigmax, sigmay, sigmaz, op_sum, index_sum
Let's set the spin chain length to 8 globally for the purposes of this example. However, note that you aren't required to set the spin chain length before you start building your operator!
In [2]:
config.L = 8
Now we start building up our Hamiltonian. Here is a ZZ interaction between site 0 and site 2:
In [3]:
sigmaz(0)*sigmaz(2)
Out[3]:
Let's take such an interaction and translate it along the spin chain. Note that the sum is to $i=5$ such that the operator has support on all spins of our length 8 chain (which is indexed from 0).
In [4]:
index_sum(sigmaz(0)*sigmaz(2))
Out[4]:
Sometimes it's more informative to have a term-by-term look at the operator:
In [5]:
oper = index_sum(sigmaz(0)*sigmaz(2))
print(oper.table())
Looks good! Let's create our power law. Here we are using op_sum, which takes the sum of the operators in the iterable passed to it. In our case, we will use a python generator as the argument.
In [6]:
alpha = 1.15
long_range_zz = op_sum(1/(d**alpha) * index_sum(sigmaz(0)*sigmaz(d)) for d in range(1,8))
Now what does the interaction look like?
In [7]:
long_range_zz
Out[7]:
Nice! now that we have our long-range power law interaction, we just need the static, uniform field.
In [8]:
# the x, y, z components of the field
h = [0.5, 0.2, 0.1]
sigma = [sigmax, sigmay, sigmaz]
static_field = op_sum(hi*index_sum(sigmai()) for hi,sigmai in zip(h,sigma))
static_field
Out[8]:
Then our Hamiltonian is just the sum of these two:
In [9]:
H = long_range_zz + static_field
With that, we can do whatever computations we want! For example, solving for the ground state energy:
In [10]:
energies = H.eigsolve()
print(energies[0])
or evolve a product state for some time:
In [11]:
from dynamite.states import State
# specify the initial state as a product state with one domain wall
initial_state = State(state='UUUUDDDD')
result = H.evolve(initial_state, t=5.0)
# compute overlap with initial state
overlap = abs(initial_state.dot(result))
print('overlap:', overlap)
or do imaginary time evolution from a random state to find a thermal state of some $\beta$:
$$\left| \psi_\beta \right> = e^{-\beta H} \left| \psi_r \right> = e^{-i (-i t_\beta) H} \left| \psi_r \right> $$where $\left| \psi_r \right>$ is a random state.
In [12]:
beta = 1.5
imag_time = -1j*beta
random_state = State(state='random')
thermal_state = H.evolve(random_state, t=imag_time)
thermal_state.normalize()
# print the expectation values of the energy for the two states
random_state_energy = random_state.dot(H*random_state).real
thermal_state_energy = thermal_state.dot(H*thermal_state).real
print('E random:', random_state_energy)
print('E thermal:', thermal_state_energy)
As expected, the "cold" thermal state has lower energy.