Quantum Selected Configuration Interaction
QSCI is released under a custom source available license. This prohibits commercial use. The full license can be found here.
In this tutorial we will introduce Quantum Selected Configuration Interaction (QSCI) from the quri-parts-qsci package in the context of early fault tolerant quantum computing (EFTQC)
Overview
QSCI is a quantum algorithm that produces approximate ground states of some quantum Hamiltonian. This is done by sampling a trial wave-function that is believed to be an approximate solution to the problem and then reconstructing the Hamiltonian in the space spanned by the most significant computational basis states sampled.
- QSCI is sometimes considered an EFTQC algorithm because it benefits from using deep ansatze
- The accuracy of QSCI comes from the matrix diagonalization which is performed on classical hardware
In this notebook, we will use qsci to solve a small quantum chemistry problem. First we use coupled cluster (CC) theory to obtain an approximate solution, which we encode with the unitary coupled cluster singles and doubles (UCCSD) ansatz. Finally we will estimate the fidelity of the final state obtained by real devices. The flow and major take-aways from this notebook will be
- Using
pyscfto define an small molecule and obtain its qubit Hamiltonian as well as the CC ground state - Importing the
TrotterUCCSDansatz fromquri_parts.openfermion.ansatzand populating its variational parameters using the CC solution - By using the
TrotterUCCSDansatz state, useqscifromquri_parts_qscito obtain the QSCI ground state and ground state energy - Using QURI Parts' transpiler library to transpile the resulting circuits
- Estimating the fidelity using QURI VM
To check out the source-code we use for qsci please have a look at our open source repository
Prerequisites
Although this notebook is pretty self-contained, users will benefit from first studying
Setup and methodology testing
We start with a simple problem to test our setup and methodology. This is more like a sanity check than a real test, but we want to
- Define a molecule
- Verify that CC solution works
- Verify that the trotterUCCSD solution (almost) replicates the CC energy
- Verify that QSCI improves on the CC energy
We first define the molecule below using pyscf and then using the openfermion submodule of quri_parts we convert it from a fermionic Hamiltonian to one that can operate on qubits. A lot of steps, such as applying the Jordan-Wigner transformation are performed for us automatically. If you are interested in learning about them in more detail, please see the QURI Parts documentation on the fermion-to-qubit mapping and molecular Hamiltonians.
import numpy as np
from pyscf import gto, scf, cc
from quri_parts.core.operator import get_sparse_matrix
from quri_parts.pyscf.mol import get_spin_mo_integrals_from_mole
from quri_parts.openfermion.mol import get_qubit_mapped_hamiltonian
from quri_parts.openfermion.ansatz import TrotterUCCSD
mole = gto.M(atom="H 0 0 0; H 0 0 1")
mf = scf.RHF(mole).run(verbose=0)
hamiltonian, mapping = get_qubit_mapped_hamiltonian(
*get_spin_mo_integrals_from_mole(mole, mf.mo_coeff)
)
vals, vecs = np.linalg.eigh(get_sparse_matrix(hamiltonian).toarray())
casci_gs_e = np.min(vals)
ccsd = cc.CCSD(mf).run(verbose=0)
print("The exact ground state energy is:", casci_gs_e)
print("The coupled cluster singles and doubles energy is:", ccsd.e_tot)
The exact ground state energy is: -1.1011503302326187
The coupled cluster singles and doubles energy is: -1.1011503302444787
The coupled cluster energy is almost identical to the exact ground state energy. This is not surprising for a small problem like , but in general we expect there to be some error in the CCSD result.
TrotterUCCSD
In QURI Parts the TrotterUCCSD ansatz is available. It is a quantum circuit version of the CCSD state which is generated by applying an excitation operator to a reference state (usually the Hartree-Fock state). The singles and doubles cluster operators
are used to generate the excitation operator
in CCSD. The RHS is an approximation because may include higher order terms representing triple or higher excitations, which are not accounted for in CCSD. The ground state energy in coupled cluster theory is then
The excitation operator generates a set of states from a set of excited reference states, which must be orthogonal. This condition taken together with the above equation results in a set of non-linear algebraic equations, the coupled cluster equations, which are solved in order to obtain tensors and so as to minimize the ground state energy.
The Unitary coupled cluster (UCC) ansatz constructs unitary operators by taking a modified approach using anti-Hermitian cluster operators, constructed from the cluster operators from CC theory. Generically, the UCC energy is then obtained as
and may again be truncated to include only singles and doubles if needed. The excitation operator can be generated through a Trotter decomposition, which can be realized as a quantum circuit ansatz. That is the TrotterUCCSD ansatz, which we will use in the following.
First we need a function that can convert the one-body and two-body cluster operators into parameters for the TrotterUCCSD ansatz.
from typing import Sequence
import numpy.typing as npt
def ccsd_param_to_circuit_param(
uccsd: TrotterUCCSD,
n_electrons: int,
t1: npt.NDArray[np.complex128],
t2: npt.NDArray[np.complex128],
) -> Sequence[float]:
in_param_list = uccsd.param_mapping.in_params
param_list = []
for param in in_param_list:
name_split = param.name.split("_")
if name_split[0] == "s":
_, i_str, j_str = name_split
i, j = int(i_str), int(j_str) - n_electrons // 2
param_list.append(t1[i, j])
if name_split[0] == "d":
_, i_str, j_str, a_str, b_str = name_split
i, j, b, a = (
int(i_str),
int(j_str),
int(b_str) - n_electrons // 2,
int(a_str) - n_electrons // 2,
)
param_list.append(t2[i, j, a, b])
return param_list
We specify the numer of trotter steps and other metaparameters in the trotter UCCSD ansatz below
TROTTER_STEPS = 1
USE_SINGLES = True
REDUCE_PARAMETER = True
Then the following code will generate the ansatz prepared with parameters obtained using coupled cluster theory
uccsd = TrotterUCCSD(
mole.nao * 2,
mole.nelectron,
trotter_number=TROTTER_STEPS,
use_singles=USE_SINGLES,
singlet_excitation=REDUCE_PARAMETER,
)
param = ccsd_param_to_circuit_param(uccsd, mole.nelectron, ccsd.t1, ccsd.t2)