Adiabatic Control of Parameters in Many-Body Localised Phases

This example shows how to code up the time-dependent disordered spin Hamiltonian:

\[\begin{split}H(t) &=& \sum_{j=0}^{L-2}\frac{J_{xy}}{2}\left(S^+_{j+1}S^-_{j} + \mathrm{h.c.}\right) + J_{zz}(t)S^z_{j+1}S^z_{j} + \sum_{j=0}^{L-1}h_jS^z_{j},\nonumber\\ J_{zz}(t) &=& (1/2 + vt)J_{zz}(0).\end{split}\]

Details about the code below can be found in SciPost Phys. 2, 003 (2017).

UPDATE: The original version of this example discussed in the paper above has been deprecated as it is not compatible with python 3. Below we have implemented a new version of the example that uses more up-to-date features of QuSpin. We also leave a link down below to download the original script for comparison.

Script

download script (python 3)

download original script (python 2, depricated)

  1##################################################################
  2#                            example 1                           #
  3#    In this script we demonstrate how to use QuSpin's           #
  4#    functionality to solve the time-dependent Schroedinger      #
  5#    equation with a time-dependent operator. We also showcase   #
  6#    a function which is used to calculate the entanglement      #
  7#    entropy of a pure state.                                    #
  8##################################################################
  9from quspin.operators import quantum_operator  # Hamiltonians and operators
 10from quspin.basis import spin_basis_1d  # Hilbert space spin basis
 11from quspin.tools.measurements import ent_entropy, diag_ensemble  # entropies
 12from numpy.random import uniform, seed  # pseudo random numbers
 13from joblib import delayed, Parallel  # parallelisation
 14import numpy as np  # generic math functions
 15from time import time  # timing package
 16
 17#
 18##### define simulation parameters #####
 19n_real = 100  # number of disorder realisations
 20n_jobs = 2  # number of spawned processes used for parallelisation
 21#
 22##### define model parameters #####
 23L = 10  # system size
 24Jxy = 1.0  # xy interaction
 25Jzz_0 = 1.0  # zz interaction at time t=0
 26h_MBL = 3.9  # MBL disorder strength
 27h_ETH = 0.1  # delocalised disorder strength
 28vs = np.logspace(-3.0, 0.0, num=20, base=10)  # log_2-spaced vector of ramp speeds
 29
 30
 31#
 32##### set up Heisenberg Hamiltonian with linearly varying zz-interaction #####
 33# define linear ramp function
 34def ramp(t, v):
 35    return 0.5 + v * t
 36
 37
 38# compute basis in the 0-total magnetisation sector (requires L even)
 39basis = spin_basis_1d(L, m=0, pauli=False)
 40# define operators with OBC using site-coupling lists
 41J_zz = [[Jzz_0, i, i + 1] for i in range(L - 1)]  # OBC
 42J_xy = [[Jxy / 2.0, i, i + 1] for i in range(L - 1)]  # OBC
 43# dictionary of operators for quantum_operator.
 44# define XXZ chain parameters
 45op_dict = dict(J_xy=[["+-", J_xy], ["-+", J_xy]], J_zz=[["zz", J_zz]])
 46# define operators for local disordered field.
 47for i in range(L):
 48    op = [[1.0, i]]
 49    op_dict["hz" + str(i)] = [["z", op]]
 50# costruct the quantum_operator
 51H_XXZ = quantum_operator(op_dict, basis=basis, dtype=np.float64)
 52
 53
 54#
 55##### calculate diagonal and entanglement entropies #####
 56def realization(vs, H_XXZ, real):
 57    """
 58    This function computes the entropies for a single disorder realisation.
 59    --- arguments ---
 60    vs: vector of ramp speeds
 61    H_XXZ: time-dep. Heisenberg Hamiltonian with driven zz-interactions
 62    basis: spin_basis_1d object containing the spin basis
 63    n_real: number of disorder realisations; used only for timing
 64    """
 65    ti = time()  # get start time
 66    #
 67    seed()  # the random number needs to be seeded for each parallel process
 68    N = H_XXZ.basis.N
 69    basis = H_XXZ.basis
 70    hz = uniform(-1, 1, size=N)
 71    # define parameters to pass into quantum_operator for
 72    # hamiltonian at end of ramp
 73    pars_MBL = {"hz" + str(i): h_MBL * hz[i] for i in range(N)}
 74    pars_ETH = {"hz" + str(i): h_ETH * hz[i] for i in range(N)}
 75    #
 76    pars_MBL["J_xy"] = 1.0
 77    pars_ETH["J_xy"] = 1.0
 78    # J_zz = 1 at end of the ramp for all velocities
 79    pars_MBL["J_zz"] = 1.0
 80    pars_ETH["J_zz"] = 1.0
 81    # diagonalize
 82    E_MBL, V_MBL = H_XXZ.eigh(pars=pars_MBL)
 83    E_ETH, V_ETH = H_XXZ.eigh(pars=pars_ETH)
 84    # reset J_zz to be initial value:
 85    pars_MBL["J_zz"] = 0.5
 86    # get many-body bandwidth at t=0
 87    eigsh_args = dict(
 88        k=2, which="BE", maxiter=1e4, return_eigenvectors=False, pars=pars_MBL
 89    )
 90    Emin, Emax = H_XXZ.eigsh(**eigsh_args)
 91    # calculating middle of spectrum
 92    E_inf_temp = (Emax + Emin) / 2.0
 93    # calculate nearest eigenstate to energy at infinite temperature
 94    E, psi_0 = H_XXZ.eigsh(pars=pars_MBL, k=1, sigma=E_inf_temp, maxiter=1e4)
 95    psi_0 = psi_0.reshape((-1,))
 96
 97    run_MBL = []
 98
 99    for v in vs:
100        # update J_zz to be time-dependent operator
101        pars_MBL["J_zz"] = (ramp, (v,))
102        # get hamiltonian
103        H = H_XXZ.tohamiltonian(pars=pars_MBL)
104        # evolve state and calculate oberservables.
105        run_MBL.append(_do_ramp(psi_0, H, basis, v, E_MBL, V_MBL))
106
107    run_MBL = np.vstack(run_MBL).T
108    # reset J_zz to be initial value:
109    pars_ETH["J_zz"] = 0.5
110    # get many-body bandwidth at t=0
111    eigsh_args = dict(
112        k=2, which="BE", maxiter=1e4, return_eigenvectors=False, pars=pars_ETH
113    )
114    Emin, Emax = H_XXZ.eigsh(**eigsh_args)
115    # calculating middle of spectrum
116    E_inf_temp = (Emax + Emin) / 2.0
117    # calculate nearest eigenstate to energy at infinite temperature
118    E, psi_0 = H_XXZ.eigsh(pars=pars_ETH, k=1, sigma=E_inf_temp, maxiter=1e4)
119    psi_0 = psi_0.reshape((-1,))
120
121    run_ETH = []
122
123    for v in vs:
124        # update J_zz to be time-dependent operator
125        pars_ETH["J_zz"] = (ramp, (v,))
126        # get hamiltonian
127        H = H_XXZ.tohamiltonian(pars=pars_ETH)
128        # evolve state and calculate oberservables.
129        run_ETH.append(_do_ramp(psi_0, H, basis, v, E_ETH, V_ETH))
130    run_ETH = np.vstack(run_ETH).T
131    # show time taken
132    print("realization {0}/{1} took {2:.2f} sec".format(real + 1, n_real, time() - ti))
133    #
134    return run_MBL, run_ETH
135
136
137#
138##### evolve state and evaluate entropies #####
139def _do_ramp(psi_0, H, basis, v, E_final, V_final):
140    """
141    Auxiliary function to evolve the state and calculate the entropies after the
142    ramp.
143    --- arguments ---
144    psi_0: initial state
145    H: time-dependent Hamiltonian
146    basis: spin_basis_1d object containing the spin basis (required for Sent)
147    E_final, V_final: eigensystem of H(t_f) at the end of the ramp t_f=1/(2v)
148    """
149    # determine total ramp time
150    t_f = 0.5 / v
151    # time-evolve state from time 0.0 to time t_f
152    psi = H.evolve(psi_0, 0.0, t_f)
153    # calculate entanglement entropy
154    subsys = range(basis.L // 2)  # define subsystem
155    Sent = basis.ent_entropy(psi, sub_sys_A=subsys)["Sent_A"]
156    # calculate diagonal entropy in the basis of H(t_f)
157    S_d = diag_ensemble(basis.L, psi, E_final, V_final, Sd_Renyi=True)["Sd_pure"]
158    #
159    return np.asarray([S_d, Sent])
160
161
162#
163##### produce data for n_real disorder realisations #####
164# __name__ == '__main__' required to use joblib in Windows.
165if __name__ == "__main__":
166
167    # alternative way without parallelisation
168    data = np.asarray([realization(vs, H_XXZ, i) for i in range(n_real)])
169    """
170	data = np.asarray(Parallel(n_jobs=n_jobs)(delayed(realization)(vs,H_XXZ,basis,i) for i in range(n_real)))
171	"""
172    #
173    run_MBL, run_ETH = zip(*data)  # extract MBL and data
174    # average over disorder
175    mean_MBL = np.mean(run_MBL, axis=0)
176    mean_ETH = np.mean(run_ETH, axis=0)
177    #
178    ##### plot results #####
179    import matplotlib.pyplot as plt
180
181    ### MBL plot ###
182    fig, pltarr1 = plt.subplots(2, sharex=True)  # define subplot panel
183    # subplot 1: diag enetropy vs ramp speed
184    pltarr1[0].plot(vs, mean_MBL[0], label="MBL", marker=".", color="blue")  # plot data
185    pltarr1[0].set_ylabel("$s_d(t_f)$", fontsize=22)  # label y-axis
186    pltarr1[0].set_xlabel("$v/J_{zz}(0)$", fontsize=22)  # label x-axis
187    pltarr1[0].set_xscale("log")  # set log scale on x-axis
188    pltarr1[0].grid(True, which="both")  # plot grid
189    pltarr1[0].tick_params(labelsize=16)
190    # subplot 2: entanglement entropy vs ramp speed
191    pltarr1[1].plot(vs, mean_MBL[1], marker=".", color="blue")  # plot data
192    pltarr1[1].set_ylabel("$s_\\mathrm{ent}(t_f)$", fontsize=22)  # label y-axis
193    pltarr1[1].set_xlabel("$v/J_{zz}(0)$", fontsize=22)  # label x-axis
194    pltarr1[1].set_xscale("log")  # set log scale on x-axis
195    pltarr1[1].grid(True, which="both")  # plot grid
196    pltarr1[1].tick_params(labelsize=16)
197    # save figure
198    fig.savefig("example1_MBL.pdf", bbox_inches="tight")
199    #
200    ### ETH plot ###
201    fig, pltarr2 = plt.subplots(2, sharex=True)  # define subplot panel
202    # subplot 1: diag enetropy vs ramp speed
203    pltarr2[0].plot(vs, mean_ETH[0], marker=".", color="green")  # plot data
204    pltarr2[0].set_ylabel("$s_d(t_f)$", fontsize=22)  # label y-axis
205    pltarr2[0].set_xlabel("$v/J_{zz}(0)$", fontsize=22)  # label x-axis
206    pltarr2[0].set_xscale("log")  # set log scale on x-axis
207    pltarr2[0].grid(True, which="both")  # plot grid
208    pltarr2[0].tick_params(labelsize=16)
209    # subplot 2: entanglement entropy vs ramp speed
210    pltarr2[1].plot(vs, mean_ETH[1], marker=".", color="green")  # plot data
211    pltarr2[1].set_ylabel("$s_\\mathrm{ent}(t_f)$", fontsize=22)  # label y-axis
212    pltarr2[1].set_xlabel("$v/J_{zz}(0)$", fontsize=22)  # label x-axis
213    pltarr2[1].set_xscale("log")  # set log scale on x-axis
214    pltarr2[1].grid(True, which="both")  # plot grid
215    pltarr2[1].tick_params(labelsize=16)
216    # save figure
217    fig.savefig("example1_ETH.pdf", bbox_inches="tight")
218    #
219    # plt.show() # show plots