# Qubit tapering¶

Authors: Utkarsh Azad and Soran Jahangiri. Posted: 16 May 2022. Last updated: 08 Nov 2022

The performance of variational quantum algorithms is considerably limited by the number of qubits required to represent wave functions. In the context of quantum chemistry, this limitation hinders the treatment of large molecules with algorithms such as the variational quantum eigensolver (VQE). Several approaches have been developed to reduce the qubit requirements for quantum chemistry calculations. In this tutorial, we demonstrate the symmetry-based qubit tapering approach which allows reducing the number of qubits required to perform molecular quantum simulations based on the $$\mathbb{Z}_2$$ symmetries present in molecular Hamiltonians 1 2.

A molecular Hamiltonian in the qubit basis can be expressed as a linear combination of Pauli words as

$H = \sum_{i=1}^r h_i P_i,$

where $$h_i$$ is a real coefficient and $$P_i$$ is a tensor product of Pauli and identity operators acting on $$M$$ qubits

$P_i \in \pm \left \{ I, X, Y, Z \right \} ^ {\bigotimes M}.$

The main idea in the symmetry-based qubit tapering approach is to find a unitary operator $$U$$ that transforms $$H$$ to a new Hamiltonian $$H'$$ which has the same eigenvalues as $$H$$

$H' = U^{\dagger} H U = \sum_{i=1}^r c_i \mu_i,$

such that each $$\mu_i$$ term in the new Hamiltonian always acts trivially, e.g., with an identity or a Pauli operator, on a set of qubits. This allows tapering-off those qubits from the Hamiltonian.

For instance, consider the following Hamiltonian

$H' = Z_0 X_1 - X_1 + Y_0 X_1,$

where all terms in the Hamiltonian act on the second qubit with the $$X$$ operator. It is straightforward to show that each term in the Hamiltonian commutes with $$I_0 X_1$$ and the ground-state eigenvector of $$H'$$ is also an eigenvector of $$I_0 X_1$$ with eigenvalues $$\pm 1$$. We can also rewrite the Hamiltonian as

$H' = (Z_0 I_1 - I_0 I_1 + Y_0 I_1) I_0 X_1,$

which gives us

$H'|\psi \rangle = \pm1 (Z_0 I_1 - I_0 I_1 + Y_0 I_1)|\psi \rangle,$

where $$|\psi \rangle$$ is an eigenvector of $$H'$$. This means that the Hamiltonian $$H$$ can be simplified as

$H_{tapered} = \pm1 (Z_0 - I_0 + Y_0).$

The tapered Hamiltonian $$H_{tapered}$$ has the eigenvalues

$[-2.41421, 0.41421],$

and

$[2.41421, -0.41421],$

depending on the value of the $$\pm 1$$ prefactor. The eigenvalues of the original Hamiltonian $$H$$ are

$[2.41421, -2.41421, 0.41421, -0.41421],$

which are thus reproduced by the tapered Hamiltonian.

More generally, we can construct the unitary $$U$$ such that each $$\mu_i$$ term acts with a Pauli-X operator on a set of qubits $$\left \{ j \right \}, j \in \left \{ l, ..., k \right \}$$ where $$j$$ is the qubit label. This guarantees that each term of the transformed Hamiltonian commutes with each of the Pauli-X operators applied to the $$j$$-th qubit:

$[H', X^j] = 0,$

and the eigenvectors of the transformed Hamiltonian $$H'$$ are also eigenvectors of each of the $$X^{j}$$ operators. Then we can factor out all of the $$X^{j}$$ operators from the transformed Hamiltonian and replace them with their eigenvalues $$\pm 1$$. This gives us a set of tapered Hamiltonians depending on which eigenvalue $$\pm 1$$ we chose for each of the $$X^{j}$$ operators. For instance, in the case of two tapered qubits, we have four eigenvalue sectors: $$[+1, +1]$$, $$[-1, +1]$$, $$[+1, -1]$$, $$[-1, -1]$$. In these tapered Hamiltonians, the set of $$\left \{ j \right \}, j \in \left \{ l, ..., k \right \}$$ qubits are eliminated. For tapered molecular Hamiltonians, it is possible to determine the optimal sector of the eigenvalues that corresponds to the ground state. This is explained in more detail in the following sections.

The unitary operator $$U$$ can be constructed as a Clifford operator 1

$U = \Pi_j \left [\frac{1}{\sqrt{2}} \left (X^{q(j)} + \tau_j \right) \right],$

where $$\tau$$ denotes the generators of the symmetry group of $$H$$ and $$X^{q}$$ operators act on those qubits that will be ultimately tapered off from the Hamiltonian. The symmetry group of the Hamiltonian is defined as an Abelian group of Pauli words that commute with each term in the Hamiltonian (excluding $$−I$$). The generators of the symmetry group are those elements of the group that can be combined, along with their inverses, to create any other member of the group.

Let’s use the qubit tapering method and obtain the ground state energy of the Helium hydride cation $$\textrm{HeH}^+$$.

## Tapering the molecular Hamiltonian¶

In PennyLane, a molecular Hamiltonian can be created by specifying the atomic symbols and coordinates.

import pennylane as qml
from pennylane import numpy as np

symbols = ["He", "H"]
geometry = np.array([[0.00000000, 0.00000000, -0.87818361],
[0.00000000, 0.00000000,  0.87818362]])

H, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, charge=1)
print(H)


Out:

  (-1.7101641607058446) [I0]
+ (0.2145954888959013) [Z2]
+ (0.21459548889590135) [Z3]
+ (0.7306880119481013) [Z0]
+ (0.7306880119481014) [Z1]
+ (-0.014203509377549517) [Y1 Y3]
+ (-0.014203509377549517) [X1 X3]
+ (0.04383965196887748) [Y0 Y2]
+ (0.04383965196887748) [X0 X2]
+ (0.1179375533255472) [Z0 Z2]
+ (0.1179375533255472) [Z1 Z3]
+ (0.14964510367730144) [Z0 Z3]
+ (0.14964510367730144) [Z1 Z2]
+ (0.18678942625805958) [Z2 Z3]
+ (0.23635644497740252) [Z0 Z1]
+ (0.058043160912773686) [Y1 Z2 Y3]
+ (0.058043160912773686) [X1 Z2 X3]
+ (0.05804316091277369) [Y0 Z1 Y2]
+ (0.05804316091277369) [X0 Z1 X2]
+ (-0.03170755035175426) [Y0 Y1 X2 X3]
+ (-0.03170755035175426) [X0 X1 Y2 Y3]
+ (-0.014203509377549517) [Y0 Z1 Y2 Z3]
+ (-0.014203509377549517) [X0 Z1 X2 Z3]
+ (0.03170755035175426) [Y0 X1 X2 Y3]
+ (0.03170755035175426) [X0 Y1 Y2 X3]
+ (0.04383965196887748) [Z0 Y1 Z2 Y3]
+ (0.04383965196887748) [Z0 X1 Z2 X3]


This Hamiltonian contains 27 terms where each term acts on up to four qubits.

We can now obtain the symmetry generators and the $$X^{j}$$ operators that are used to construct the unitary $$U$$ operator that transforms the $$\textrm{HeH}^+$$ Hamiltonian. In PennyLane, these are constructed by using the symmetry_generators() and paulix_ops() functions.

generators = qml.symmetry_generators(H)
paulixops = qml.paulix_ops(generators, qubits)

for idx, generator in enumerate(generators):
print(f"generator {idx+1}: {generator}, paulix_op: {paulixops[idx]}")


Out:

/home/runner/work/qml/qml/venv/lib/python3.9/site-packages/pennylane/operation.py:1886: UserWarning: Tensor object acts on overlapping wires; in some PennyLane functions this will lead to undefined behaviour
warnings.warn(
/home/runner/work/qml/qml/venv/lib/python3.9/site-packages/pennylane/operation.py:2073: UserWarning: Tensor object acts on overlapping wires; in some PennyLane functions this will lead to undefined behaviour
warnings.warn(
generator 1:   (1.0) [Z0 Z2], paulix_op: PauliX(wires=)
generator 2:   (1.0) [Z1 Z3], paulix_op: PauliX(wires=)


Once the operator $$U$$ is applied, each of the Hamiltonian terms will act on the qubits $$q_2, q_3$$ either with the identity or with a Pauli-X operator. For each of these qubits, we can simply replace the Pauli-X operator with one of its eigenvalues $$+1$$ or $$-1$$. This results in a total number of $$2^k$$ Hamiltonians, where $$k$$ is the number of tapered-off qubits and each Hamiltonian corresponds to one eigenvalue sector. The optimal sector corresponding to the ground-state energy of the molecule can be obtained by using the optimal_sector() function.

n_electrons = 2
paulix_sector = qml.qchem.optimal_sector(H, generators, n_electrons)
print(paulix_sector)


Out:

[-1, -1]


The optimal eigenvalues are $$-1, -1$$ for qubits $$q_2, q_3$$, respectively. We can now build the tapered Hamiltonian with the taper() function which constructs the operator $$U$$, applies it to the Hamiltonian and finally tapers off the qubits $$q_2, q_3$$ by replacing the Pauli-X operators acting on those qubits with the optimal eigenvalues.

H_tapered = qml.taper(H, generators, paulixops, paulix_sector)
print(H_tapered)


Out:

  ((-1.946039267356938+0j)) [I0]
+ ((-0.11608632269285395+0j)) [X0]
+ ((0.11608632269285395+0j)) [X1]
+ ((0.5160925230521999+0j)) [Z0]
+ ((0.5160925230522+0j)) [Z1]
+ ((-0.12683020140701698+0j)) [Y0 Y1]
+ ((-0.11608632182554733+0j)) [X0 Z1]
+ ((0.11608632182554732+0j)) [Z0 X1]
+ ((0.12385566388085917+0j)) [Z0 Z1]


The new Hamiltonian has only 9 non-zero terms acting on only 2 qubits! We can verify that the original and the tapered Hamiltonian both give the correct ground state energy of the $$\textrm{HeH}^+$$ cation, which is $$-2.862595242378$$ Ha computed with the full configuration interaction (FCI) method. In PennyLane, it’s possible to build a sparse matrix representation of Hamiltonians. This allows us to directly diagonalize them to obtain exact values of the ground-state energies.

H_sparse = qml.SparseHamiltonian(H.sparse_matrix(), wires=H.wires)
H_tapered_sparse = qml.SparseHamiltonian(H_tapered.sparse_matrix(), wires=H_tapered.wires)

print("Eigenvalues of H:\n", qml.eigvals(H_sparse, k=16))
print("\nEigenvalues of H_tapered:\n", qml.eigvals(H_tapered_sparse, k=4))


Out:

Eigenvalues of H:
[-3.12541987 -3.12541987 -2.86259524 -2.64241998 -2.19672513 -2.19672513
-2.19672513 -2.18547545 -2.18547545 -2.02874709 -1.35709798 -1.35709798
-0.69608961 -0.17266334 -0.17266334  1.13871403]

Eigenvalues of H_tapered:
[-2.86259524 -2.19672513 -2.02874709 -0.69608961]


## Tapering the reference state¶

The ground state Hartree-Fock energy of $$\textrm{HeH}^+$$ can be computed by directly applying the Hamiltonians to the Hartree-Fock state. For the tapered Hamiltonian, this requires transforming the Hartree-Fock state with the same symmetries obtained for the original Hamiltonian. This reduces the number of qubits in the Hartree-Fock state to match that of the tapered Hamiltonian. It can be done with the taper_hf() function.

state_tapered = qml.qchem.taper_hf(generators, paulixops, paulix_sector,
num_electrons=n_electrons, num_wires=len(H.wires))
print(state_tapered)


Out:

[1 1]


Recall that the original Hartree-Fock state for the $$\textrm{HeH}^+$$ cation is $$[1 1 0 0]$$. We can now generate the qubit representation of these states and compute the Hartree-Fock energies for each Hamiltonian.

dev = qml.device("default.qubit", wires=H.wires)
def circuit():
qml.BasisState(np.array([1, 1, 0, 0]), wires=H.wires)
return qml.state()

qubit_state = circuit()
HF_energy = qubit_state.T @ H.sparse_matrix().toarray() @ qubit_state
print(f"HF energy: {np.real(HF_energy):.8f} Ha")

dev = qml.device("default.qubit", wires=H_tapered.wires)
def circuit():
qml.BasisState(np.array([1, 1]), wires=H_tapered.wires)
return qml.state()

qubit_state = circuit()
HF_energy = qubit_state.T @ H_tapered.sparse_matrix().toarray() @ qubit_state
print(f"HF energy (tapered): {np.real(HF_energy):.8f} Ha")


Out:

HF energy: -2.85436865 Ha
HF energy (tapered): -2.85436865 Ha


These values are identical to the reference Hartree-Fock energy $$-2.8543686493$$ Ha.

## VQE simulation¶

Finally, we can use the tapered Hamiltonian and the tapered reference state to perform a VQE simulation and compute the ground-state energy of the $$\textrm{HeH}^+$$ cation. We build a tapered variational ansatz  that prepares an entangled state by evolving the tapered Hartree-Fock state using the tapered particle-conserving gates, i.e., the SingleExcitation() and DoubleExcitation() operations tapered using taper_operation().

singles, doubles = qml.qchem.excitations(n_electrons, len(H.wires))
tapered_doubles = [
qml.taper_operation(qml.DoubleExcitation, generators, paulixops, paulix_sector,
wire_order=H.wires, op_wires=double) for double in doubles
]
tapered_singles = [
qml.taper_operation(qml.SingleExcitation, generators, paulixops, paulix_sector,
wire_order=H.wires, op_wires=single) for single in singles
]

dev = qml.device("default.qubit", wires=H_tapered.wires)
def tapered_circuit(params):
qml.BasisState(state_tapered, wires=H_tapered.wires)
for idx, tapered_op in enumerate(tapered_doubles + tapered_singles):
tapered_op(params[idx])
return qml.expval(H_tapered)


We define an optimizer and the initial values of the circuit parameters and optimize the circuit parameters with respect to the ground state energy.

optimizer = qml.GradientDescentOptimizer(stepsize=0.5)
params = np.zeros(len(doubles) + len(singles), requires_grad=True)

for n in range(1, 41):
params, energy = optimizer.step_and_cost(tapered_circuit, params)
if not n % 5:
print(f"n: {n}, E: {energy:.8f} Ha, Params: {params}")


Out:

n: 5, E: -2.86236975 Ha, Params: [ 0.12087368  0.01627666 -0.01627666]
n: 10, E: -2.86256772 Ha, Params: [ 0.12687479  0.02808328 -0.02808328]
n: 15, E: -2.86259160 Ha, Params: [ 0.12820847  0.03248734 -0.03248734]
n: 20, E: -2.86259476 Ha, Params: [ 0.12867238  0.03409235 -0.03409235]
n: 25, E: -2.86259518 Ha, Params: [ 0.12884045  0.03467702 -0.03467702]
n: 30, E: -2.86259523 Ha, Params: [ 0.12890162  0.03489005 -0.03489005]
n: 35, E: -2.86259524 Ha, Params: [ 0.1289239   0.03496769 -0.03496769]
n: 40, E: -2.86259524 Ha, Params: [ 0.12893201  0.03499598 -0.03499598]


The computed energy matches the FCI energy, $$-2.862595242378$$ Ha, while the number of qubits and the number of Hamiltonian terms are significantly reduced with respect to their original values.

## Conclusions¶

Molecular Hamiltonians possess symmetries that can be leveraged to reduce the number of qubits required in quantum computing simulations. This tutorial introduces a PennyLane functionality that can be used for qubit tapering based on $$\mathbb{Z}_2$$ symmetries. The procedure includes obtaining tapered Hamiltonians and tapered reference states that can be used in variational quantum algorithms such as VQE. Utkarsh is a quantum software researcher at Xanadu, working on making quantum computing more useful and accessible, with a focus on exploring its applications in natural sciences. Whenever he's not tinkering with code, he will be walking miles searching either for fractals in nature or a cup of filter coffee. #### Soran Jahangiri

Soran Jahangiri is a quantum chemist working at Xanadu. His work is focused on developing and implementing quantum algorithms for chemistry applications.

Total running time of the script: ( 0 minutes 1.295 seconds)

Gallery generated by Sphinx-Gallery