# Symbolic Hamiltonian and Transformations

In this notebook, I will show how to systematically construct the symbolic Hamiltonian of fermionic and spin models. I will introduce the Jordan-Wigner transformation and Majorana fermions. The code implementation is based on the open source frameworks, [OpenFermion](https://quantumai.google/openfermion) and [SymPy](https://www.sympy.org/en/index.html). 

## Function Definition


```python
from openfermion import *
from sympy import *


def spin_latex(H_spin_dict):
    """Convert the spin operators into LaTeX expression via SymPy

    Parameters
    ----------
    H_spin_dict : dict
        contains spin operators and their coefficients

    Returns
    -------
    LaTeX expression
        output the symbolic expression in LaTeX form
    """
    expr = []
    for key, value in H_spin_dict.items():
        coefficient = nsimplify(simplify(value))
        if isinstance(coefficient, Integer):
            coefficient = sign(
                coefficient)*UnevaluatedExpr(factorint(abs(coefficient), visual=True))
        operator = []
        for i_key in key:
            operator.append(
                symbols(i_key[1]+'_'+str(i_key[0]), commutative=False))
        expr.append(coefficient*prod(operator))
    return sum(expr)


def fermion_latex(H_fermion_dict):
    """Convert the fermionic operators into LaTeX expression via SymPy

    Parameters
    ----------
    H_fermion_dict : dict
        contains fermionic operators and their coefficients

    Returns
    -------
    LaTeX expression
        output the symbolic expression in LaTeX form
    """
    expr = []
    for key, value in H_fermion_dict.items():
        coefficient = nsimplify(simplify(value))
        if isinstance(coefficient, Integer):
            coefficient = sign(
                coefficient)*UnevaluatedExpr(factorint(abs(coefficient), visual=True))
        if key != ():
            operator = []
            for i_key in key:
                if i_key[1] == 1:
                    operator.append(
                        symbols('c^\dagger_'+str(i_key[0]), commutative=False))
                else:
                    operator.append(
                        symbols('c_'+str(i_key[0]), commutative=False))
        else:
            operator = [1]
        expr.append(coefficient*prod(operator))
    return sum(expr)


def majorana_latex(H_majorana_dict):
    """Convert the Majorana operators into LaTeX expression via SymPy

    Parameters
    ----------
    H_majorana_dict : dict
        contains Majorana operators and their coefficients

    Returns
    -------
    LaTeX expression
        output the symbolic expression in LaTeX form
    """
    H_majorana_dict_nonzero = {key: value for key,
                               value in H_majorana_dict.items() if value != 0}
    expr = []
    for key, value in H_majorana_dict_nonzero.items():
        coefficient = nsimplify(simplify(value))
        if isinstance(coefficient/I, Integer):
            coefficient = sign(
                coefficient)*UnevaluatedExpr(factorint(abs(coefficient), visual=True))
        if key != ():
            operator = []
            for i_key in key:
                operator.append(
                    symbols('\gamma_'+str(i_key), commutative=False))
        else:
            operator = [1]
        expr.append(coefficient*prod(operator))
    return sum(expr)

```

## Spin Chain

The general anisotropic 1D spin-1/2 chain of 4 sites with nearest-neighbor exchange couplings under the open boundary condition is described by the Hamiltonian in Equation (1.12).


```python
n_qubits = 4
alpha, beta_, g = symbols('alpha, beta, g')
H_spin = QubitOperator()
for j in range(n_qubits-1):
    H_spin += QubitOperator(((j, 'Z')), -Indexed(g, j))
    H_spin += QubitOperator(((j, 'X'), (j+1, 'X')), -1)
    H_spin += QubitOperator(((j, 'Y'), (j+1, 'Y')), -Indexed(alpha, j))
    H_spin += QubitOperator(((j, 'Z'), (j+1, 'Z')), -Indexed(beta_, j))
H_spin += QubitOperator(((n_qubits-1, 'Z')), -Indexed(g, n_qubits-1))
spin_latex(H_spin.terms)

```




$\displaystyle - {\alpha}_{0} Y_{0} Y_{1} - {\alpha}_{1} Y_{1} Y_{2} - {\alpha}_{2} Y_{2} Y_{3} - {\beta}_{0} Z_{0} Z_{1} - {\beta}_{1} Z_{1} Z_{2} - {\beta}_{2} Z_{2} Z_{3} - {g}_{0} Z_{0} - {g}_{1} Z_{1} - {g}_{2} Z_{2} - {g}_{3} Z_{3} - 1 X_{0} X_{1} - 1 X_{1} X_{2} - 1 X_{2} X_{3}$



## The $p$-wave superconductor

The one-dimensional tight-binding $p$-wave superconductor, whose Hamiltonian in Equation (1.16) is quadratic.


```python
n_sites = 4
t, Delta, mu = symbols('t, Delta, mu')
H_fermion = FermionOperator()
for j in range(n_sites-1):
    H_fermion += FermionOperator(((j, 1), (j, 0)), -
                                 Indexed(mu, j)) + Rational(1/2)*Indexed(mu, j)
    H_fermion += FermionOperator(((j, 1), (j+1, 0)), -Indexed(t, j))
    H_fermion += FermionOperator(((j+1, 1), (j, 0)), -
                                 Indexed(t, j).conjugate())
    H_fermion += FermionOperator(((j, 1), (j+1, 1)), -Indexed(Delta, j))
    H_fermion += FermionOperator(((j+1, 0), (j, 0)), -
                                 Indexed(Delta, j).conjugate())
H_fermion += FermionOperator(((n_sites-1, 1), (n_sites-1, 0)), -
                             Indexed(mu, n_sites-1)) + Rational(1/2)*Indexed(mu, n_sites-1)
fermion_latex(H_fermion.terms)

```




$\displaystyle - \overline{{\Delta}_{0}} c_{1} c_{0} - \overline{{\Delta}_{1}} c_{2} c_{1} - \overline{{\Delta}_{2}} c_{3} c_{2} - \overline{{t}_{0}} c^{\dagger}_{1} c_{0} - \overline{{t}_{1}} c^{\dagger}_{2} c_{1} - \overline{{t}_{2}} c^{\dagger}_{3} c_{2} - {\Delta}_{0} c^{\dagger}_{0} c^{\dagger}_{1} - {\Delta}_{1} c^{\dagger}_{1} c^{\dagger}_{2} - {\Delta}_{2} c^{\dagger}_{2} c^{\dagger}_{3} + \frac{{\mu}_{0}}{2} - {\mu}_{0} c^{\dagger}_{0} c_{0} + \frac{{\mu}_{1}}{2} - {\mu}_{1} c^{\dagger}_{1} c_{1} + \frac{{\mu}_{2}}{2} - {\mu}_{2} c^{\dagger}_{2} c_{2} + \frac{{\mu}_{3}}{2} - {\mu}_{3} c^{\dagger}_{3} c_{3} - {t}_{0} c^{\dagger}_{0} c_{1} - {t}_{1} c^{\dagger}_{1} c_{2} - {t}_{2} c^{\dagger}_{2} c_{3}$



We assign some numerical value for the coupling parameters


```python
n_sites = 4
t, Delta, mu = 3, 5, 7
H_fermion = FermionOperator()
for j in range(n_sites-1):
    H_fermion += FermionOperator(((j, 1), (j, 0)), -mu) + 1/2*mu
    H_fermion += FermionOperator(((j, 1), (j+1, 0)), -t)
    H_fermion += FermionOperator(((j+1, 1), (j, 0)), -t)
    H_fermion += FermionOperator(((j, 1), (j+1, 1)), -Delta)
    H_fermion += FermionOperator(((j+1, 0), (j, 0)), -Delta)
H_fermion += FermionOperator(((n_sites-1, 1), (n_sites-1, 0)), -mu) + 1/2*mu
fermion_latex(H_fermion.terms).doit()

```




$\displaystyle 14 - 5 c^{\dagger}_{0} c^{\dagger}_{1} - 7 c^{\dagger}_{0} c_{0} - 3 c^{\dagger}_{0} c_{1} - 5 c^{\dagger}_{1} c^{\dagger}_{2} - 3 c^{\dagger}_{1} c_{0} - 7 c^{\dagger}_{1} c_{1} - 3 c^{\dagger}_{1} c_{2} - 5 c^{\dagger}_{2} c^{\dagger}_{3} - 3 c^{\dagger}_{2} c_{1} - 7 c^{\dagger}_{2} c_{2} - 3 c^{\dagger}_{2} c_{3} - 3 c^{\dagger}_{3} c_{2} - 7 c^{\dagger}_{3} c_{3} - 5 c_{1} c_{0} - 5 c_{2} c_{1} - 5 c_{3} c_{2}$



The Hermitian part $S = S^\dagger$ of the Bogoliubov-de Gennes Hamiltonian is


```python
get_quadratic_hamiltonian(H_fermion).hermitian_part.real

```




    array([[-7., -3.,  0.,  0.],
           [-3., -7., -3.,  0.],
           [ 0., -3., -7., -3.],
           [ 0.,  0., -3., -7.]])



The anti-symmetric part $A = - A^T$ of the Bogoliubov-de Gennes Hamiltonian is


```python
get_quadratic_hamiltonian(H_fermion).antisymmetric_part.real

```




    array([[ 0., -5.,  0.,  0.],
           [ 5.,  0., -5.,  0.],
           [ 0.,  5.,  0., -5.],
           [ 0.,  0.,  5.,  0.]])



## Majorana Representation

We can transform the Hamiltonian of the $p$-wave superconducting model from fermionic representation to Majorana representation (we set $t_i=t^*_i=\Delta_i=\Delta^*_i=3$ and $\mu_i = 2$ for demonstration):


```python
n_sites = 4
t, Delta, mu = 3, 3, 2
H_fermion = FermionOperator()
for j in range(n_sites-1):
    H_fermion += FermionOperator(((j, 1), (j, 0)), -mu) + 1/2*mu
    H_fermion += FermionOperator(((j, 1), (j+1, 0)), -t)
    H_fermion += FermionOperator(((j+1, 1), (j, 0)), -t)
    H_fermion += FermionOperator(((j, 1), (j+1, 1)), -Delta)
    H_fermion += FermionOperator(((j+1, 0), (j, 0)), -Delta)
H_fermion += FermionOperator(((n_sites-1, 1), (n_sites-1, 0)), -mu) + 1/2*mu
fermion_latex(H_fermion.terms).doit()

```




$\displaystyle 4 - 3 c^{\dagger}_{0} c^{\dagger}_{1} - 2 c^{\dagger}_{0} c_{0} - 3 c^{\dagger}_{0} c_{1} - 3 c^{\dagger}_{1} c^{\dagger}_{2} - 3 c^{\dagger}_{1} c_{0} - 2 c^{\dagger}_{1} c_{1} - 3 c^{\dagger}_{1} c_{2} - 3 c^{\dagger}_{2} c^{\dagger}_{3} - 3 c^{\dagger}_{2} c_{1} - 2 c^{\dagger}_{2} c_{2} - 3 c^{\dagger}_{2} c_{3} - 3 c^{\dagger}_{3} c_{2} - 2 c^{\dagger}_{3} c_{3} - 3 c_{1} c_{0} - 3 c_{2} c_{1} - 3 c_{3} c_{2}$



Here is an instance of Equation (1.51).


```python
H_majorana_dict = get_majorana_operator(H_fermion).terms
majorana_latex(H_majorana_dict).simplify()

```




$\displaystyle i \left(- \gamma_{0} \gamma_{1} + 3 \gamma_{1} \gamma_{2} - \gamma_{2} \gamma_{3} + 3 \gamma_{3} \gamma_{4} - \gamma_{4} \gamma_{5} + 3 \gamma_{5} \gamma_{6} - \gamma_{6} \gamma_{7}\right)$



## Jordan-Wigner Transformation

Here we explicitly how to transform the spin Hamiltonian in Equation (1.12) into fermionic Hamiltonian in Equation (1.70). We remark that OpenFermion adopts the transformation in the quantum chemistry community, which differs a minus sign of the couplings $g$ along the $z$-direction


```python
n_qubits = 4
alpha, beta_, g = 3, 5, -7
H_spin = QubitOperator()
for j in range(n_qubits-1):
    H_spin += QubitOperator(((j, 'Z')), -g)
    H_spin += QubitOperator(((j, 'X'), (j+1, 'X')), -1)
    H_spin += QubitOperator(((j, 'Y'), (j+1, 'Y')), -alpha)
    H_spin += QubitOperator(((j, 'Z'), (j+1, 'Z')), -beta_)
H_spin += QubitOperator(((n_qubits-1, 'Z')), -g)
spin_latex(H_spin.terms)

```




$\displaystyle - 1 X_{0} X_{1} - 1 X_{1} X_{2} - 1 X_{2} X_{3} - 3^{1} Y_{0} Y_{1} - 3^{1} Y_{1} Y_{2} - 3^{1} Y_{2} Y_{3} - 5^{1} Z_{0} Z_{1} - 5^{1} Z_{1} Z_{2} - 5^{1} Z_{2} Z_{3} + 7^{1} Z_{0} + 7^{1} Z_{1} + 7^{1} Z_{2} + 7^{1} Z_{3}$



Apply the Jordan-Wigner transformation, one can obtain the fermionic representation.


```python
H_fermion = normal_ordered(reverse_jordan_wigner(H_spin))
fermion_latex(H_fermion.terms)

```




$\displaystyle 13^{1} - 2^{1} c^{\dagger}_{1} c^{\dagger}_{0} - 2^{1} c^{\dagger}_{2} c^{\dagger}_{1} - 2^{1} c^{\dagger}_{3} c^{\dagger}_{2} + 2^{1} c_{1} c_{0} + 2^{1} c_{2} c_{1} + 2^{1} c_{3} c_{2} - 2^{2} c^{\dagger}_{0} c_{0} - 2^{2} c^{\dagger}_{0} c_{1} - 2^{2} c^{\dagger}_{1} c_{0} - 2^{2} c^{\dagger}_{1} c_{2} - 2^{2} c^{\dagger}_{2} c_{1} - 2^{2} c^{\dagger}_{2} c_{3} - 2^{2} c^{\dagger}_{3} c_{2} - 2^{2} c^{\dagger}_{3} c_{3} + 2^{1} \cdot 3^{1} c^{\dagger}_{1} c_{1} + 2^{1} \cdot 3^{1} c^{\dagger}_{2} c_{2} + 2^{2} \cdot 5^{1} c^{\dagger}_{1} c^{\dagger}_{0} c_{1} c_{0} + 2^{2} \cdot 5^{1} c^{\dagger}_{2} c^{\dagger}_{1} c_{2} c_{1} + 2^{2} \cdot 5^{1} c^{\dagger}_{3} c^{\dagger}_{2} c_{3} c_{2}$



We can also verify the transformation from Equation (4.1) to Equation (4.2).


```python
n_qubits = 4
J, t, gamma_, delta, g = 1, 7/2, 1/2, 5, -13
H_spin = QubitOperator()
for j in range(n_qubits-1):
    H_spin += QubitOperator(((j, 'Z')), -J*g)
    H_spin += QubitOperator(((j, 'X'), (j+1, 'X')), -J*(t + gamma_))
    H_spin += QubitOperator(((j, 'Y'), (j+1, 'Y')), -J*(t - gamma_))
    H_spin += QubitOperator(((j, 'Z'), (j+1, 'Z')), -J*delta)
H_spin += QubitOperator(((n_qubits-1, 'Z')), -J*g)
spin_latex(H_spin.terms)

```




$\displaystyle 13^{1} Z_{0} + 13^{1} Z_{1} + 13^{1} Z_{2} + 13^{1} Z_{3} - 2^{2} X_{0} X_{1} - 2^{2} X_{1} X_{2} - 2^{2} X_{2} X_{3} - 3^{1} Y_{0} Y_{1} - 3^{1} Y_{1} Y_{2} - 3^{1} Y_{2} Y_{3} - 5^{1} Z_{0} Z_{1} - 5^{1} Z_{1} Z_{2} - 5^{1} Z_{2} Z_{3}$




```python
H_fermion = normal_ordered(reverse_jordan_wigner(H_spin))
fermion_latex(H_fermion.terms)

```




$\displaystyle 1 c^{\dagger}_{1} c^{\dagger}_{0} + 1 c^{\dagger}_{2} c^{\dagger}_{1} + 1 c^{\dagger}_{3} c^{\dagger}_{2} - 1 c_{1} c_{0} - 1 c_{2} c_{1} - 1 c_{3} c_{2} - 2^{4} c^{\dagger}_{0} c_{0} - 2^{4} c^{\dagger}_{3} c_{3} + 37^{1} - 7^{1} c^{\dagger}_{0} c_{1} - 7^{1} c^{\dagger}_{1} c_{0} - 7^{1} c^{\dagger}_{1} c_{2} - 7^{1} c^{\dagger}_{2} c_{1} - 7^{1} c^{\dagger}_{2} c_{3} - 7^{1} c^{\dagger}_{3} c_{2} - 2^{1} \cdot 3^{1} c^{\dagger}_{1} c_{1} - 2^{1} \cdot 3^{1} c^{\dagger}_{2} c_{2} + 2^{2} \cdot 5^{1} c^{\dagger}_{1} c^{\dagger}_{0} c_{1} c_{0} + 2^{2} \cdot 5^{1} c^{\dagger}_{2} c^{\dagger}_{1} c_{2} c_{1} + 2^{2} \cdot 5^{1} c^{\dagger}_{3} c^{\dagger}_{2} c_{3} c_{2}$



9758ad816e66b79bca1e80cd93c7265d0c78d7d5
ipynb
Jupyter Notebook
SymbolicTransformation.ipynb
peixinshen/PhD-Thesis-Source-Code
e30eb8c94392ba5b8d6b2b375d63744e7095529d
MIT
SymbolicTransformation.ipynb
peixinshen/PhD-Thesis-Source-Code
e30eb8c94392ba5b8d6b2b375d63744e7095529d
MIT
SymbolicTransformation.ipynb
peixinshen/PhD-Thesis-Source-Code
e30eb8c94392ba5b8d6b2b375d63744e7095529d
MIT
Qwen/Qwen-72B
1. YES
2. YES