The Jordan-Wigner transformation in Python

Posted on 03 June 2014

Hamiltonians on one dimensional chains provide a good sanity check when trying out new numerical methods. The Jordan-Wigner transform is an easy way to obtain the eigenvalues of systems of fermions. For an annihiliation operator $a_j$, the transform is defined as $a_j = - (\otimes_{k=1}^{j-1} \sigma_z)\otimes \sigma_j$, where $\sigma_z$ is the Pauli operator along the $z$ axis, and $\sigma_j$ is the operator $|0\rangle\langle 1|$ acting on site $j$.

The series of Kronecker products would be an ideal application of a nest function, but Python does not have such a function built in. Instead, we define a recursive function to get the desired Kronecker product:

In [1]:
import numpy as np

def nested_kronecker_product(a):
    if len(a) == 2:
        return np.kron(a[0],a[1])
    else:
        return np.kron(a[0], nested_kronecker_product(a[1:]))

With this function, the transform is easy to define. We pad the transform with identity operators to match the lattice length:

In [2]:
def jordan_wigner_transform(j, lattice_length):
    sigma = np.array([[0, 1], [0, 0]])
    sigma_z = np.array([[1, 0], [0, -1]])
    I = np.eye(2)
    operators = []
    for k in range(j):
        operators.append(sigma_z)
    operators.append(sigma)
    for k in range(lattice_length-j-1):
        operators.append(I)
    return -nested_kronecker_product(operators)

Now we can get our transformed fermionic operators:

In [3]:
lattice_length = 4
a = []
for i in range(lattice_length):
    a.append(jordan_wigner_transform(i, lattice_length))

Next we define a Hamiltonian of interest, namely spinless fermions on an open chain:

$H=\sum_{}\left(c_{r}^{\dagger} c_{s}+c_{s}^{\dagger} c_{r}-\gamma(c_{r}^{\dagger} c_{s}^{\dagger}+c_{s}c_{r} )\right)-2\lambda\sum_{r}c_{r}^{\dagger}c_{r},$

where $r$ and $s$ indicate neighbors on the chain. The Python definition of this Hamiltonian is

In [4]:
def hamiltonian(gam, lam, a, lattice_length):
    H = 0
    for i in range(lattice_length - 1):
        H += a[i].T.dot(a[i+1]) - a[i].dot(a[i+1].T)
        H -= gam*(a[i].T.dot(a[i+1].T) - a[i].dot(a[i+1]))
    for i in range(lattice_length):
        H -= 2*lam*(a[i].dot(a[i].T))
    return H

Here we observed the fermionic canonical commutation relations and performed substitutions. To get the eigenvalues for a particular parameter combination, we write

In [5]:
gam, lam =1, 1
H = hamiltonian(gam, lam, a, lattice_length)
eigenvalues = np.linalg.eig(H)[0]
sorted(eigenvalues)
Out[5]:
[-8.7587704831436479,
 -8.0641777724759276,
 -6.7587704831436497,
 -6.0641777724759232,
 -5.694592710667723,
 -5.0000000000000036,
 -4.9999999999999991,
 -4.3054072893322797,
 -3.6945927106677212,
 -3.0000000000000004,
 -2.9999999999999991,
 -2.3054072893322788,
 -1.9358222275240897,
 -1.241229516856365,
 0.064177772475912706,
 0.75877048314363438]

Tags: Python, Quantum information theory

Share on: LinkedIn Facebook Google+ Twitter