In [1]:
%matplotlib inline
import numpy as np
import scipy.linalg
import matplotlib.pyplot as plt, mpld3
plt.rcParams['figure.figsize'] = 12, 9
mpld3.enable_notebook()

Adiabatic Evolution of a Single Spin in External Magnetic Field

Pauli matrices:

In [2]:
sigma_x = np.array([[0, 1], [1,0]])
sigma_y = np.array([[0, 1j], [-1j, 0]])
sigma_z = np.array([[1, 0], [0, -1]])

Parameter-dependent Hamiltonian:

In [3]:
def H(s):
    return -(np.cos(np.pi*0.5*s)*sigma_z + np.sin(np.pi*0.5*s)*sigma_x)

assert np.allclose(H(0), -sigma_z)
assert np.allclose(H(1), -sigma_x)

Overlap with ground state:

In [4]:
def overlap(s, psi):
    Es, v = np.linalg.eigh(H(s))
    ground_state = v[:, 0] if Es[0] <= Es[1] else v[:, 1]
    return np.abs(np.dot(ground_state, psi))**2

assert np.isclose(overlap(0, [np.sqrt(0.3), np.sqrt(0.7)]), 0.3)

Various time evolution methods:

In [5]:
def euler(psi, s, dt):
    psi = psi - 1j*dt*np.dot(H(s), psi)
    return psi / np.linalg.norm(psi)

def unitary(psi, s, dt):
    psi = np.dot(np.eye(2) - 0.5j*dt*H(s), psi)
    return np.linalg.solve(np.eye(2) + 0.5j*dt*H(s), psi)

def expm(psi, s, dt):
      U = scipy.linalg.expm(-1j*dt*H(s))
      return np.dot(U, psi)

Adiabatic evolution from initial ground state:

In [6]:
def adiabatic(t_max, t_steps=100, method=expm):
    psi = np.array([1, 0])
    ts, dt = np.linspace(0, t_max, t_steps, retstep=True)
    overlaps = []
    for t in ts:
        s = t / float(t_max)
        psi = method(psi, s, dt)
        overlaps.append(overlap(s, psi))
    return ts, overlaps

[adiabatic(t_max)[1][-1] for t_max in [1, 10]]
Out[6]:
[0.65162718207809145, 0.9980047287715802]

Compare the different methods (with different $t_{\max}$es):

In [7]:
t_maxes = np.arange(1, 20, 0.5)

plt.title('overlap with final ground state')
plt.xlabel('t_max')
plt.ylabel('overlap')
plt.grid(color='lightgray', alpha=0.7)
for method in [euler, unitary, expm]:
    plt.plot(t_maxes, [adiabatic(t_max, method=method)[1][-1] for t_max in t_maxes], label=method.__name__)
plt.legend()
plt.show()

Time evolution of the overlaps (for different $t_{\max}$es):

In [8]:
t_maxes = [1, 2, 5, 10]

plt.title('overlap with ground state')
plt.xlabel('s = t/t_max')
plt.ylabel('overlap')
plt.grid(color='lightgray', alpha=0.7)
for t_max in t_maxes:
    ts, overlaps = adiabatic(t_max)
    plt.plot(ts/t_max, overlaps, label=str(t_max))
plt.legend()
plt.show()