{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Quantinuum Systems - Quantum Subspace Expansion \n", "==================================================" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial illustrates the computation of molecular excited states through the application of the Quantum Subspace Expansion (QSE) technique on a Quantinuum emulator. The QSE method leverages a precise calculation of the ground state energy for a specific molecular configuration to approximate the energies of corresponding excited states. Consider a stable molecule, denoted by a Hamiltonian $H$, and let $\\left|\\Psi_{0}\\right\\rangle$ represent the outcome of the Variational Quantum Eigensolver ([VQE](InQ_tut_vqe_1.ipynb)) algorithm employed to estimate the ground state energy of $H$. More information on the QSE method can be found in the publication available at this [link](https://doi.org/10.1103/PhysRevA.95.042308). \n", "\n", "The QSE technique constructs a subspace of state vectors $\\left|\\Psi_j^k\\right\\rangle$ formed by one-electron excitations of the ground state wavefunction:\n", "\n", "$$\n", "\\left|\\Psi_{j}^{k}\\right\\rangle = c_k^{\\dagger}c_{j}\\left|\\Psi_0\\right\\rangle.\n", "$$\n", "\n", "where $c_k^{\\dagger}, c_{j}$ are the fermionic creation and annihilation operators over spin orbitals $k$ and $j$, respectively. That is, these vectors are formed by reducing the occupation of spin orbital $j$ by one, and increasing the occupation of spin orbital $k$ by one. The vectors are not in general orthogonal to $\\Psi_{0}$ hence we will need to calculate an overlap matrix.\n", "\n", "\n", "Within this subspace, we solve a generalized eigenvalue problem. Consider the operator $\\hat{H} $ with matrix elements given by\n", "$$(H)_{jk}^{lm} = \\langle\\Psi_j^l \\left| \\hat{H} \\right| \\Psi_k^m\\rangle,$$\n", "\n", "and define an overlap matrix $S$ whose matrix elements are given by\n", "\n", "$$S_{jk}^{lm} = \\langle \\Psi_j^l \\left|\\Psi_k^m\\right\\rangle.$$\n", "\n", "The generalized eigenvalue equation to be solved is \n", "\n", "$$\n", "HC=SCE,\n", "$$\n", "\n", "where $C$ is the matrix of eigenvectors, and $E$ is the vector of eigenvalues. Crucially, the energy eigenvalues $E$ provide an estimate of the excited state energies of $H$ as well as a refined value of the ground state energy.\n", "\n", "Notice that the solution to the generalized eigenvalue equation can be done on a classical computer, provided $H$ and $S$ have been calculated. The matrix elements of both of these matrices can be constructed using a quantum computer, in the following way. First, re-write the matrix elements in terms of $\\left | \\Psi_{0}\\right \\rangle$:\n", "\n", "$$\n", "(H)_{jk}^{lm} = \\langle\\Psi_j^l \\left| \\hat{H} \\right| \\Psi_k^m\\rangle = \\langle \\Psi_{0} | c_{j}^\\dagger c_{l} \\hat{H}c_{m}^{\\dagger}c_{k}|\\Psi_{0}\\rangle\\\\\n", "S_{jk}^{lm} = \\langle \\Psi_j^l \\left|\\Psi_k^m\\right\\rangle = \\langle \\Psi_{0} | c_{j}^\\dagger c_{l} c_{m}^{\\dagger}c_{k}|\\Psi_{0}\\rangle.\n", "$$\n", "\n", "The matrix elements can be calculated using a quantum computer or simulator. By transforming the operators \n", "$c_{j}^\\dagger c_{l} c_{k}^{\\dagger}c_{m}$ and $c_{l}^\\dagger c_{j} \\hat{H} c_{m}^{\\dagger}c_{k}$ to a set of Pauli quantum gates according to an appropriate scheme such as Jordan-Wigner or Bravyi-Kitaev, apply this gate set to the ground state wavefunction (constructed with the coefficients obtained from the VQE calculation) and perform a measurement to obtain the expected value. By measuring these expectation values, we obtain estimates of $S_{jk}^{lm}$ and $H_{jk}^{lm}$, respectively.\n", "\n", "This tutorial will guide you through the process of performing QSE and will show how to collate the resulting eigen-energies and eigen-vectors. To demonstrate these concepts, we will utilize the methane (CH$_4$) chemical system. Note that to follow this tutorial, you must have been granted access to Quantinuum systems which can be obtained by contacting Quantinuum support. Users can access Quantinuum backends through [pytket-quantinuum](https://docs.quantinuum.com/tket/extensions/pytket-quantinuum/) or via [Quantinuum Nexus](https://docs.quantinuum.com/nexus/trainings/notebooks/basics/getting_started.html).\n", "\n", "\n", "\n", "The steps below are such:\n", "\n", "- Define the system\n", "- Prepare the approximate ground state obtained by the VQE algorithm\n", "- Configure the Quantinuum backend\n", "- Establish the PMSV error mitigation \n", "- Define protocol for measuring QSE matrix elements \n", "- Build InQuanto computables \n", "- Submit and retreive InQuanto computables\n", "- The black-box approach using the AlgorithmQSE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After defining the Z-matrix for Methane, the Hamiltonian object is created using a restricted Hartree-Fock (RHF) InQuanto-PySCF driver.\n", "\n", "The `get_system` function is responsible for computing the fermionic Hamiltonian operator, Fock space, and Hartree Fock state.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "from inquanto.extensions.pyscf import ChemistryDriverPySCFMolecularRHF\n", "\n", "zmatrix = \"\"\"\n", "C\n", "H 1 1.083000\n", "H 1 1.083000 2 109.471000\n", "H 1 1.083000 2 109.471000 3 120.000000\n", "H 1 1.083000 2 109.471000 4 120.000000\n", "\"\"\"\n", "\n", "driver = ChemistryDriverPySCFMolecularRHF(\n", " zmatrix=zmatrix,\n", " charge=0,\n", " frozen=[0, 1, 2, 3, 7, 8],\n", " basis=\"STO-3G\",\n", " point_group_symmetry=True,\n", ")\n", "\n", "hamiltonian, space, state = driver.get_system()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The `qubit_encode` function carries out qubit encoding, utilizing the mapping class associated with the current integral operator. The default mapping approach is the Jordan-Wigner method. Additionally, we employ the `compress` method, which eliminates Hamiltonian terms with coefficients less than `abs_tol`, chosen as $10^{-6}$ here. This step is a means of reducing the number of measurement circuits/quantum computational resources needed (which means less time to run which is preferred for a tutorial), and the threshold we use is fairly reasonable compared to the scale of errors induced by noisy quantum simulation. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | Coefficient | \n", "Term | \n", "Coefficient Type | \n", "
---|---|---|---|
0 | \n", "-37.822198 | \n", "\n", " | <class 'numpy.float64'> | \n", "
1 | \n", "-0.496463 | \n", "Z5 | \n", "<class 'numpy.float64'> | \n", "
2 | \n", "-0.496463 | \n", "Z4 | \n", "<class 'numpy.float64'> | \n", "
3 | \n", "0.128963 | \n", "Z4 Z5 | \n", "<class 'numpy.float64'> | \n", "
4 | \n", "-0.496463 | \n", "Z3 | \n", "<class 'numpy.float64'> | \n", "
5 | \n", "0.110247 | \n", "Z3 Z5 | \n", "<class 'numpy.float64'> | \n", "
6 | \n", "0.123222 | \n", "Z3 Z4 | \n", "<class 'numpy.float64'> | \n", "
7 | \n", "-0.012975 | \n", "X2 X3 Y4 Y5 | \n", "<class 'numpy.float64'> | \n", "
8 | \n", "0.012975 | \n", "X2 Y3 Y4 X5 | \n", "<class 'numpy.float64'> | \n", "
9 | \n", "0.012975 | \n", "Y2 X3 X4 Y5 | \n", "<class 'numpy.float64'> | \n", "
10 | \n", "-0.012975 | \n", "Y2 Y3 X4 X5 | \n", "<class 'numpy.float64'> | \n", "
11 | \n", "-0.496463 | \n", "Z2 | \n", "<class 'numpy.float64'> | \n", "
12 | \n", "0.123222 | \n", "Z2 Z5 | \n", "<class 'numpy.float64'> | \n", "
13 | \n", "0.110247 | \n", "Z2 Z4 | \n", "<class 'numpy.float64'> | \n", "
14 | \n", "0.128963 | \n", "Z2 Z3 | \n", "<class 'numpy.float64'> | \n", "
15 | \n", "-0.067741 | \n", "Z1 | \n", "<class 'numpy.float64'> | \n", "
16 | \n", "0.106870 | \n", "Z1 Z5 | \n", "<class 'numpy.float64'> | \n", "
17 | \n", "0.118715 | \n", "Z1 Z4 | \n", "<class 'numpy.float64'> | \n", "
18 | \n", "0.106870 | \n", "Z1 Z3 | \n", "<class 'numpy.float64'> | \n", "
19 | \n", "0.118715 | \n", "Z1 Z2 | \n", "<class 'numpy.float64'> | \n", "
20 | \n", "-0.011845 | \n", "X0 X1 Y4 Y5 | \n", "<class 'numpy.float64'> | \n", "
21 | \n", "-0.011845 | \n", "X0 X1 Y2 Y3 | \n", "<class 'numpy.float64'> | \n", "
22 | \n", "0.011845 | \n", "X0 Y1 Y4 X5 | \n", "<class 'numpy.float64'> | \n", "
23 | \n", "0.011845 | \n", "X0 Y1 Y2 X3 | \n", "<class 'numpy.float64'> | \n", "
24 | \n", "0.011845 | \n", "Y0 X1 X4 Y5 | \n", "<class 'numpy.float64'> | \n", "
25 | \n", "0.011845 | \n", "Y0 X1 X2 Y3 | \n", "<class 'numpy.float64'> | \n", "
26 | \n", "-0.011845 | \n", "Y0 Y1 X4 X5 | \n", "<class 'numpy.float64'> | \n", "
27 | \n", "-0.011845 | \n", "Y0 Y1 X2 X3 | \n", "<class 'numpy.float64'> | \n", "
28 | \n", "-0.067741 | \n", "Z0 | \n", "<class 'numpy.float64'> | \n", "
29 | \n", "0.118715 | \n", "Z0 Z5 | \n", "<class 'numpy.float64'> | \n", "
30 | \n", "0.106870 | \n", "Z0 Z4 | \n", "<class 'numpy.float64'> | \n", "
31 | \n", "0.118715 | \n", "Z0 Z3 | \n", "<class 'numpy.float64'> | \n", "
32 | \n", "0.106870 | \n", "Z0 Z2 | \n", "<class 'numpy.float64'> | \n", "
33 | \n", "0.123178 | \n", "Z0 Z1 | \n", "<class 'numpy.float64'> | \n", "