Note
Click here to download the full example code
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
where \(h_i\) is a real coefficient and \(P_i\) is a tensor product of Pauli and identity operators acting on \(M\) qubits
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\)
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
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
which gives us
where \(|\psi \rangle\) is an eigenvector of \(H'\). This means that the Hamiltonian \(H\) can be simplified as
The tapered Hamiltonian \(H_{tapered}\) has the eigenvalues
and
depending on the value of the \(\pm 1\) prefactor. The eigenvalues of the original Hamiltonian \(H\) are
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:
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
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:1921: 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:2111: 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=[2])
generator 2: (1.0) [Z1 Z3], paulix_op: PauliX(wires=[3])
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.
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)
@qml.qnode(dev, interface="autograd")
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)
@qml.qnode(dev, interface="autograd")
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 [3]
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)
@qml.qnode(dev, interface="autograd")
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.
References¶
- 1(1,2)
Sergey Bravyi, Jay M. Gambetta, Antonio Mezzacapo, Kristan Temme, “Tapering off qubits to simulate fermionic Hamiltonians”. arXiv:1701.08213
- 2
Kanav Setia, Richard Chen, Julia E. Rice, Antonio Mezzacapo, Marco Pistoia, James Whitfield, “Reducing qubit requirements for quantum simulation using molecular point group symmetries”. arXiv:1910.14644
About the author¶
Utkarsh Azad
Soran Jahangiri
Total running time of the script: ( 0 minutes 1.020 seconds)