diff --git a/content/lab-4/Kai-Chun Lin_Score_Fall22_Lab4.ipynb b/content/lab-4/Kai-Chun Lin_Score_Fall22_Lab4.ipynb
new file mode 100644
index 00000000..f672dbfa
--- /dev/null
+++ b/content/lab-4/Kai-Chun Lin_Score_Fall22_Lab4.ipynb
@@ -0,0 +1,3068 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "1e0efada-b53a-4a9b-a52d-7fa35977c687",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "81f8836f-1c26-4299-a74e-2adc06bfe34d",
+ "metadata": {},
+ "source": [
+ "# Quantum Chemistry Challenge - Use Quantum Computers to Explore Interstellar Space!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b8570ffe",
+ "metadata": {},
+ "source": [
+ "# Table of Contents\n",
+ "- **[Story](#Story---Chapter-4)**\n",
+ "- **[Introduction: Special Chemistry of Interstellar Molecules](#Introduction:-Special-Chemistry-of-Interstellar-Molecules)**\n",
+ "- **[VQE Syllabi](#VQE-syllabi)**\n",
+ "- **[Part I: Exploring Interstellar $H_3^+$](#Part-I:-Exploring-Interstellar-$H_3^+$)**\n",
+ " - **[Exercise 1: Defining Molecule declaration for $H_3^+$](#exercise1)**\n",
+ " - **[Exercise 2: Building up VQE using Estimator](#exercise2)**\n",
+ " - **[Exercise 3: Creating helper functions](#exercise3)**\n",
+ " - **[Exercise 4: Computing reaction energy of $H_3^+$](#exercise4)**\n",
+ "- **[Part II: Exploring Interstellar Cyclopropenylidene($C_3H_2$)](#Part-II:-Exploring-Interstellar-Cyclopropenylidene($C_3H_2$))**\n",
+ " - **[Exercise 5: Exploring the first excited state and computing dipole moment of Cyclopropenylidene](#exercise5)**\n",
+ "- **[Part III: Final Challenge - Exploring the Interstellar Cyclopropenylidene($C_3H_2$) Reaction Chain](#Part-III:-Final-Challenge---Exploring-the-Interstellar-Cyclopropenylidene($C_3H_2$)-Reaction-Chain)**\n",
+ " - **[Final Challenge: computing the reaction energy of C + C2H2 →C3H2](##Final-Challenge---Compute-Reaction-energy-of-C-+-C2H2-→C3H2)**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9d0aaf1e-c680-4fd2-9d48-c00773e3176b",
+ "metadata": {},
+ "source": [
+ "# Story - Chapter 4\n",
+ "\n",
+ "With your path clear, you are prepared for your slingshot around the planet. But you want to increase your chances of success as much as possible, and it occurs to you there’s one last thing you can do before you attempt the maneuver. \n",
+ "\n",
+ "You see, one of the magnificent discoveries Earth scientists made while inventing interstellar travel technologies is that certain dense cosmic clouds, especially those containing Hydrogenium ($H_3^+$), Interstellar Cycloproenylidene ($C_3H_2$), or a combination of them, boost starship velocity without expending extra fuel. Like sledding down a steep hill, or sliding a block across an oiled surface. \n",
+ "\n",
+ "If you can find such a cloud within the vicinity of your slingshot path, you can increase your chances of escape. It would be as though a hand of cosmic dust pushes your starship, providing more force to your slingshot. \n",
+ "\n",
+ "But when your systems were broken, certain memory files were corrupted, and you no longer have the molecular properties your scanners require to identify such clouds. You must re-do the calculations. And the more precise your calculations, the more precise your scanners will be! \n",
+ "\n",
+ "Complete these exercises to calculate and analyze the various molecular properties required to calibrate your scanner’s sensors and potentially discover cosmic clouds that will aid in your escape. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6b53f499-906a-4489-9e8c-582daead659b",
+ "metadata": {},
+ "source": [
+ "# Introduction: Special Chemistry of Interstellar Molecules"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6f5294d9-614b-4565-b212-311d83f8b30b",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13cac829-2b5f-4cbc-b091-29670aedaa0d",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "\n",
+ "Interstellar space is the most remarkable chemical laboratory for many unstable species which are rarely found on Earth. Due to its extreme environment, a large number of chemical processes are very different from how they would behave in the Earth's environment. There are three main types of chemical processes that occur in interstellar and circumstellar regions, these are **[[1]](https://www.astronomy.ohio-state.edu/pogge.1/Ast871/Notes/Molecules.pdf)**:\n",
+ "\n",
+ "1. Gas phase processes between atoms, ions and molecules, promoted by photoionization, photodissociation, and cosmic ray ionization\n",
+ "2. Reactions on the surfaces of bare grains, with either prompt or delayed ejection of products into the gas phase\n",
+ "3. Accumulation of molecular ices on the surfaces of dust inside dark clouds, with activation of these ices by cosmic rays or photons and subsequent reactions between the radicals so generated to form species of greater complexity **[[2]](https://pubs.rsc.org/en/content/chapterhtml/2017/bk9781782627760-00001?isbn=978-1-78262-776-0)**\n",
+ "\n",
+ "This fascinating interstellar chemistry began in the 1930's with the observation of molecular absorption spectra in distant stars within the galaxy. The species CH, CH+, and CN all have electronic spectra in an accessible wavelength region where the Earth’s atmosphere is still transparent. So far, spectroscopic development including radioastronomy has contributed greatly to unveiling abundant species of molecules in the universe, and also gives us a lot of information on the chemical reaction process that happens there **[[3]](https://www.pnas.org/doi/full/10.1073/pnas.0605352103)**.\n",
+ "\n",
+ "In this lab, we will look into a few well-known interstellar chemical reactions and compute a few properties of specific molecules. We will first start off by exploring the formation of interstellar $H_3^+$ ions using $H_2$ and ionized $H_2^+$. Here we will start off by computing the reaction energy by solving for the ground state energy of the participating molecules by building a **Variational Quantum Eigensolver (VQE)** routine. This will be followed by demonstrating how we can compute molecular properties of interest using the ground state energy information obtained by VQE by other approaches dependent on the same. We will be specifically looking and investigating the excited energy absorption spectra and the dipole moment $C_3H_2$ by using a VQD(Variational Quantum Deflation) approach and the Estimator primitive. $C_3H_2$ is a highly reactive class of organic molecules known as carbenes which are only seen in the laboratory due to their reactivity on Earth but are found in significant concentrations in the interstellar medium (ISM) and on Saturn's moon Titan **[[5]](https://pubs.rsc.org/en/content/articlelanding/2003/CP/b303753n)**. Finally, we shall compute the reaction energy of creation of $C_3H_2$ which is our final competition problem to find the best way to have this calculation run on a noisy simulation **[[6]](https://en.wikipedia.org/wiki/Cyclopropenylidene)**. \n",
+ "\n",
+ "For this lab, we will be building up a VQE routine ground up as compared to using the VQE class in the previous problem. Before we begin, we shall briefly go through basics of VQE and basic problem formation using Qiskit Nature for those who are unfamiliar with these concepts."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5b586139-ea69-43e7-940b-5317db10b17c",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# VQE syllabi"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7f043602-a3c7-48fa-9949-06f84836807d",
+ "metadata": {},
+ "source": [
+ "
\n",
+ " \n",
+ " References for additional details \n",
+ "\n",
+ "For the Qiskit Nature tutorials that implement this algorithm see **[here](https://qiskit.org/documentation/nature/tutorials/01_electronic_structure.html)**.\n",
+ "For additional information, please refer to Qiskit Github **[first page of github repository](https://github.com/Qiskit/qiskit-nature)** >>> the **[test folder](https://github.com/Qiskit/qiskit-nature/tree/main/test)** with the base code for the use of each functionality.\n",
+ " \n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f77c01e2-7111-4a66-8b81-634cb28e8535",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# Part I: Exploring Interstellar $H_3^+$\n",
+ "\n",
+ "In the 21st century astrophysics, the study of molecules in interstellar medium is of great interest to uncover information about formation of stars and galaxies in the universe. When Watson, Herbst & Klemperer in 1973 proposed that interstellar molecules may be combining in an ion-molecule type of reaction, it was clear $H_3^+$ ions play an important role in the interstellar environment. **[[7]](https://www.cambridge.org/core/journals/international-journal-of-astrobiology/article/abs/h3-the-initiator-of-interstellar-chemistry/69C0753DDB337E4475416CB6FA3D802D)**\n",
+ "\n",
+ "The $H_3^+$ ion, so called \"interstellar acid\" plays a central role in interstellar chemistry acting as proton donors (acid) through the proton-hop reaction. $H_3$ is produced by the reaction $H_2 + H_2^+ → H_3^+ + H$ in which a proton in $H_2^+$ hops to molecular hydrogen. In interstellar space, cosmic rays are capable of ionizing $H_2$ and are always present. $H_3^+$ is also always ubiquitous as long as $H_2$ is present **[[7]](https://www.cambridge.org/core/journals/international-journal-of-astrobiology/article/abs/h3-the-initiator-of-interstellar-chemistry/69C0753DDB337E4475416CB6FA3D802D)**.\n",
+ "\n",
+ "As shown explained in the following paper **[[8]](https://arxiv.org/ftp/arxiv/papers/1707/1707.07926.pdf)** , $H_3^+$ is located at an important point in the ion-molecular chemistry tree and plays a pivotal role in the generation of organic molecules."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7eca19ca-2566-463e-aedb-e4e5c7e2d87f",
+ "metadata": {},
+ "source": [
+ "We will study the fundamentals of computing the ground state energy of $H_3^+$ ions using Qiskit to get you warmed up with some quantum chemistry tasks. You will be required to determine the reaction energy required to produce $H_3^+$ ions. Cosmic rays that ionize from each of the reacting molecules ground states yield $H_3^+$ ions which we will be looking into for the exercise."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c0e51df1-cd1b-47a2-9337-6af4d13c17e6",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e9e2fa76-5ab7-4b0e-a05c-654eb3b057df",
+ "metadata": {},
+ "source": [
+ "## Importing Libraries \n",
+ "\n",
+ "First, let's start by importing some libraries. We shall be using one of Qiskit's application modules: Qiskit Nature here for loading chemistry drivers and the necessary functions to help us formulate our problem and solve for the molecule we have at hand."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "36fd92cc-3c83-4084-93d3-6e425173af3c",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "# Import necessary libraries and packages\n",
+ "import math\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "\n",
+ "import warnings\n",
+ "warnings.filterwarnings('ignore')\n",
+ "\n",
+ "from qiskit import Aer, IBMQ, QuantumCircuit\n",
+ "from qiskit.primitives import Estimator\n",
+ "from qiskit.providers.aer import StatevectorSimulator\n",
+ "from qiskit.utils import QuantumInstance\n",
+ "\n",
+ "from qiskit.tools.jupyter import *\n",
+ "from qiskit.visualization import *\n",
+ "\n",
+ "# Import Qiskit libraries for VQE\n",
+ "from qiskit.algorithms import MinimumEigensolverResult, VQE\n",
+ "from qiskit.algorithms.optimizers import SLSQP, SPSA\n",
+ "\n",
+ "# Import Qiskit Nature libraries\n",
+ "from qiskit_nature.algorithms import GroundStateEigensolver, VQEUCCFactory\n",
+ "from qiskit_nature.algorithms.ground_state_solvers.minimum_eigensolver_factories import NumPyMinimumEigensolverFactory\n",
+ "from qiskit_nature.circuit.library import UCC, UCCSD\n",
+ "from qiskit_nature.drivers import Molecule\n",
+ "from qiskit_nature.drivers.second_quantization import ElectronicStructureDriverType, ElectronicStructureMoleculeDriver\n",
+ "from qiskit_nature.converters.second_quantization import QubitConverter\n",
+ "from qiskit_nature.mappers.second_quantization import BravyiKitaevMapper, JordanWignerMapper, ParityMapper\n",
+ "from qiskit_nature.problems.second_quantization.electronic import ElectronicStructureProblem\n",
+ "from qiskit_nature.transformers.second_quantization.electronic import ActiveSpaceTransformer\n",
+ "\n",
+ "# Prototype-zne\n",
+ "!pip install prototype-zne --quiet\n",
+ "\n",
+ "from qiskit_nature.settings import settings\n",
+ "\n",
+ "settings.dict_aux_operators = True"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f7a4c161-408a-4ead-a55f-fa766b37935d",
+ "metadata": {},
+ "source": [
+ "## Building up our Model ground up\n",
+ "\n",
+ "### 1. Define molecule geometry and get molecular properties\n",
+ "\n",
+ "The first step is to define our molecules by specifying their atomic coordinates, total spin (multiplicity) and charge information. For this, we will first declare molecular structure in cartesian coordinates and set a chemical driver according to its charge and multiplicity.\n",
+ "\n",
+ "Molecular geometry can be found by searching research papers **[[9]](https://pubchem.ncbi.nlm.nih.gov/) [[10]](https://webbook.nist.gov/chemistry/form-ser/)**. In this exercise, we will use the properties that originate from this source **[[11]](https://aip.scitation.org/doi/abs/10.1063/1.433585)** with an approximation of the equilateral triangle structure like below picture.\n",
+ "\n",
+ "The multiplicity of energy levels is defined as $2S + 1$, where $S$ is the total spin of the molecule. $H_3^+$ has two electrons, and by obeying the **[Aufbau principle](https://en.wikipedia.org/wiki/Aufbau_principle)** and **[Pauli principle](https://en.wikipedia.org/wiki/Pauli_exclusion_principle)**, two electron's total spin are cancelled out due to their configuration ($\\uparrow + \\downarrow $), leading to a multiplicity of 1.\n",
+ "\n",
+ "With this information, let's finish the below part to include the proper molecular structure."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "619ac424-9849-477a-b299-b22fc49cd723",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "
\n",
+ "Exercise 1: \n",
+ " \n",
+ "Complete the below Molecule declaration according to the structure of $H_3^+$. According to the reference, the atomic distance between $H$ is **0.91396** $\\text{Ă…}$ and it is equilateral triangle structure. Our target molecule contains **two electrons** with **charge +1**. To grade your answer, do not change the basis.
\n",
+ " Note from Sensei: Spin and Multiplicity of molecules \n",
+ "\n",
+ "We need to specify which electronic state to compute. In quantum chemistry calculations, $S$ corresponds to the spin of the unpaired electrons. The spin of electrons is $\\frac{1}{2}$, thus the total spin can be described as follows, \n",
+ "$S = \\frac{1}{2} \\times \\mathrm{number\\ of\\ unpaired\\ electrons}$. \n",
+ "The spin multiplicity is $2S + 1$, which is \"$\\mathrm{number\\ of\\ unpaired\\ electrons}+1$\".\n",
+ " \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "34904f45-99ca-4769-97eb-a3e983c81199",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "d53fa961-9911-43e8-b11a-02d6a9d93060",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#### Fill in the values below and complete the function to define the Molecule ####\n",
+ "\n",
+ "# Coordinates are given in Angstrom\n",
+ "hydrogen_t = [[\"H\", [0.45698, 0.0, 0.0]], # ----------- Enter your code here\n",
+ " [\"H\", [-0.45698, 0.0, 0.0]], # ----------- Enter your code here\n",
+ " [\"H\", [0.0, 0.791513, 0.0]]]\n",
+ " \n",
+ "h3p = Molecule( # Fill up the function below\n",
+ " geometry= hydrogen_t, # ----------- Enter your code here\n",
+ " multiplicity= 1, # ----------- Enter your code here\n",
+ " charge=1, # ----------- Enter your code here\n",
+ ")\n",
+ "\n",
+ "driver = ElectronicStructureMoleculeDriver(h3p, basis=\"ccpvdz\", driver_type=ElectronicStructureDriverType.PYSCF) \n",
+ "\n",
+ "properties = driver.run()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "f3f54fbe-b343-4d57-b3b9-8eaef5b871bf",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Submitting your answer. Please wait...\n",
+ "Congratulations 🎉! Your answer is correct and has been submitted.\n"
+ ]
+ }
+ ],
+ "source": [
+ "## Grade and submit your solution\n",
+ "from qc_grader.challenges.fall_2022 import grade_lab4_ex1\n",
+ "\n",
+ "grade_lab4_ex1(h3p)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c7922c09-4039-4959-9fa7-50368e1df400",
+ "metadata": {},
+ "source": [
+ "The Qiskit Nature driver gives us some properties of the molecule (see also: **[The Property Framework](https://qiskit.org/documentation/nature/tutorials/08_property_framework.html)**), let's check some out now!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "0599238c-5dbc-49aa-a391-eed022e506f7",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of alpha electrons: 1\n",
+ "number of beta electrons: 1\n",
+ "number of spin orbitals: 30\n",
+ "nuclear repulsion energy: 1.736981060004552\n"
+ ]
+ }
+ ],
+ "source": [
+ "num_alpha_electrons = properties.get_property('ParticleNumber').num_alpha\n",
+ "num_beta_electrons = properties.get_property('ParticleNumber').num_beta\n",
+ "num_spin_orbitals = int(properties.get_property('ParticleNumber').num_spin_orbitals)\n",
+ "\n",
+ "nuclear_rep_energy = properties.get_property('ElectronicEnergy').nuclear_repulsion_energy\n",
+ "print(\"number of alpha electrons: \" , num_alpha_electrons)\n",
+ "print(\"number of beta electrons: \" , num_beta_electrons)\n",
+ "print(\"number of spin orbitals: \" , num_spin_orbitals)\n",
+ "print(\"nuclear repulsion energy: \" , nuclear_rep_energy)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2bced4a1-ba4b-46f0-bc62-f86491883e1a",
+ "metadata": {},
+ "source": [
+ "### 2. Electronic Structure Problem and Active Space Transformer\n",
+ "\n",
+ "\n",
+ "The next step is to select our active space which will describe what molecular orbitals will be simulated with VQE and treat the rest with classical methods. The more orbitals we include in our active space, the more computationally expensive our quantum circuit will be. As such, the goal is to construct the most accurate active space with as fewest orbitals as possible. \n",
+ "\n",
+ "In Qiskit Nature, we use `ActiveSpaceTransformer` to specify how many electrons and molecular orbitals we would like to have in our active space, as well as which of those particular orbitals we would like to consider. \n",
+ "After that, you can then create an `ElectronicStructureProblem` that produces a list of fermionic operators before mapping them to qubits (Pauli strings)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "844b3d29-d160-4872-9a49-f5500188d634",
+ "metadata": {},
+ "source": [
+ "
\n",
+ " Note from Sensei: Active Space \n",
+ "\n",
+ " \n",
+ "As the problem size increases, the computational cost (number of qubits (which corresponds to the number of orbitals is used) and the circuit depth (which corresponds to the number of electronic excitations considered)) increases rapidly. The active space approach is a well known approach to reduce the computational cost. In many chemistry problems, the orbitals occupied with electrons high in energy and the unoccupied orbitals with low energy play an important role. So, rather than treating all orbitals and electrons with the same accuracy, treating those important electrons and orbitals with high accuracy is a good approximation in general. Therefore, we can select the important orbitals and electrons as our active space and solve the problem with low cost while maintaining the accuracy of the calculation.\n",
+ " \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "e52d6961-8a1e-4e52-98f3-a17d8676db9a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Check the occupation of the spin orbitals\n",
+ "PN_property = properties.get_property(\"ParticleNumber\")\n",
+ "\n",
+ "# Define the active space around the Fermi level \n",
+ "# (selected automatically around the HOMO and LUMO, ordered by energy)\n",
+ "transformer = ActiveSpaceTransformer(\n",
+ " num_electrons=2, # Number of electrons in our active space\n",
+ " num_molecular_orbitals=3, # Number of orbitals in our active space\n",
+ ")\n",
+ "\n",
+ "# Now you can get the reduced electronic structure problem\n",
+ "problem_reduced = ElectronicStructureProblem(driver, transformers=[transformer]) \n",
+ "\n",
+ "# The second quantized Hamiltonian of the reduce problem\n",
+ "second_q_ops_reduced = problem_reduced.second_q_ops()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "268617fc-9b7e-4a6a-aa7a-b23cdda240eb",
+ "metadata": {},
+ "source": [
+ "### 3. Fermion-Qubit Mapping \n",
+ "\n",
+ "Because electrons are fermions, the electronic systems are described by Hamiltonians consisting of fermionic operators expressed in second quantization. Since quantum computers are made up of qubits, we have to transform such fermionic Hamiltonians into the qubit operators. There are different types of mappers to transform fermionic operators to qubits operators. You can try different mapping options, but we will stick to `ParityMapper`, where we will be exploiting problem symmetries and applying two-qubit reduction to reduce the problem size. Feel free to to try out different mappers and experiment if you wish, but for the purposes of the challenge grading, we will be using `ParityMapper`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "5efe1c18-6e04-45be-904a-048ba795905b",
+ "metadata": {
+ "scrolled": true,
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "-2.3705782023199005 * IIIIII\n",
+ "+ 0.3492296180456834 * IIIIIZ\n",
+ "- 1.724684211830245e-08 * IIIIZX\n",
+ "+ 1.724684211830245e-08 * IIIIIX\n",
+ "+ 0.04107515475324274 * IIIIZZ\n",
+ "+ 0.041075085753784414 * IIIZZI\n",
+ "+ 0.3492296180456834 * IIZZII\n",
+ "- 1.724684211830245e-08 * IZXZII\n",
+ "+ 1.724684211830245e-08 * IIXIII\n",
+ "+ 0.04107515475324269 * IZZIII\n",
+ "+ 0.04107508575378436 * ZZIIII\n",
+ "+ 0.09263375051327181 * IIIIZI\n",
+ "+ 0.09263374411183314 * IIIZZZ\n",
+ "+ 0.1703424914950203 * IIZZIZ\n",
+ "+ 0.01892506184544881 * IZXZZX\n",
+ "- 0.01892506184544881 * IIXIZX\n",
+ "- 0.01892506184544881 * IZXZIX\n",
+ "+ 0.01892506184544881 * IIXIIX\n",
+ "+ 0.018925050277467714 * ZXXIXX\n",
+ "+ 0.018925050277467714 * IYYZXX\n",
+ "+ 0.018925050277467714 * ZXXZYY\n",
+ "+ 0.018925050277467714 * IYYIYY\n",
+ "+ 0.11155881235872062 * IZZIIZ\n",
+ "+ 0.0063335393221763125 * IZZIZX\n",
+ "- 0.0063335393221763125 * IZZIIX\n",
+ "- 0.006333532495285949 * ZXZZXX\n",
+ "+ 0.006333532495285949 * IXIZXX\n",
+ "- 0.006333532495285949 * ZXZIYY\n",
+ "+ 0.006333532495285949 * IXIIYY\n",
+ "+ 0.11155879438930086 * ZZIIIZ\n",
+ "- 0.006333537574596253 * ZZIIZX\n",
+ "+ 0.006333537574596253 * ZZIIIX\n",
+ "+ 0.07770205519201824 * IIIZIZ\n",
+ "+ 0.11155881235872062 * IIZZZZ\n",
+ "+ 0.0063335393221763125 * IZXZZZ\n",
+ "- 0.0063335393221763125 * IIXIZZ\n",
+ "- 0.006333532495285949 * ZXXIXZ\n",
+ "- 0.006333532495285949 * IYYZXZ\n",
+ "+ 0.006333532495285949 * ZXXZXI\n",
+ "+ 0.006333532495285949 * IYYIXI\n",
+ "+ 0.0981371173539964 * IZZIZZ\n",
+ "+ 0.006811680175428652 * ZXZZXZ\n",
+ "- 0.006811680175428652 * IXIZXZ\n",
+ "- 0.006811680175428652 * ZXZIXI\n",
+ "+ 0.006811680175428652 * IXIIXI\n",
+ "+ 0.0845137353674469 * ZZIIZZ\n",
+ "+ 0.11155879438930086 * IIZIZI\n",
+ "- 0.006333537574596252 * IZXIZI\n",
+ "+ 0.006333537574596252 * IIXZZI\n",
+ "+ 0.0845137353674469 * IZZZZI\n",
+ "+ 0.0981370925002784 * ZZIZZI\n",
+ "+ 0.09263375051327181 * IZIZII\n",
+ "+ 0.09263374411183314 * ZZZZII\n",
+ "+ 0.07770205519201824 * ZIZIII\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Setup the mapper and qubit converter\n",
+ "mapper_type = 'ParityMapper'\n",
+ "\n",
+ "if mapper_type == 'ParityMapper':\n",
+ " mapper = ParityMapper()\n",
+ "elif mapper_type == 'JordanWignerMapper':\n",
+ " mapper = JordanWignerMapper()\n",
+ "elif mapper_type == 'BravyiKitaevMapper':\n",
+ " mapper = BravyiKitaevMapper()\n",
+ "\n",
+ "\n",
+ "converter = QubitConverter(mapper)\n",
+ "\n",
+ "qubit_op = converter.convert(second_q_ops_reduced[\"ElectronicEnergy\"])\n",
+ "print(qubit_op)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ab953b19-60e6-4ecd-a825-28cdc4a6e0aa",
+ "metadata": {},
+ "source": [
+ "### Apply reduction\n",
+ "\n",
+ "We can reduce system size by applying various methods. Here, we will apply **[two qubit reduction](https://qiskit.org/documentation/stubs/qiskit.opflow.converters.TwoQubitReduction.html)**. The two qubit reduction converter eliminates the central and last qubit in a list of Paulis that have diagonal operators (Z, I) in those positions. This is a nice method that can be used in chemistry problems to reduce computational resources. In this particular example, this method can be used to taper two qubits in parity and binary-tree mapped fermionic Hamiltonians when the spin orbitals are ordered in two spin sectors (block spin order), according to the number of particles in the system."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "3ad24077-87bc-4cf2-8d18-bccd464e7223",
+ "metadata": {
+ "scrolled": true,
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "-2.3705782023198996 * IIII\n",
+ "+ 0.2715275628536651 * IIIZ\n",
+ "- 1.7246842118302443e-08 * IIZX\n",
+ "+ 1.7246842118302443e-08 * IIIX\n",
+ "- 0.051558589358590375 * IIZZ\n",
+ "+ 0.05155866475948739 * IIZI\n",
+ "- 0.2715275628536651 * IZII\n",
+ "+ 1.7246842118302443e-08 * ZXII\n",
+ "+ 1.7246842118302443e-08 * IXII\n",
+ "- 0.05155858935859042 * ZZII\n",
+ "- 0.05155866475948744 * ZIII\n",
+ "- 0.17034249149502023 * IZIZ\n",
+ "- 0.018925061845448805 * ZXZX\n",
+ "- 0.018925061845448805 * IXZX\n",
+ "+ 0.018925061845448805 * ZXIX\n",
+ "+ 0.018925061845448805 * IXIX\n",
+ "+ 0.018925050277467707 * XXXX\n",
+ "- 0.018925050277467707 * YYXX\n",
+ "- 0.018925050277467707 * XXYY\n",
+ "+ 0.018925050277467707 * YYYY\n",
+ "+ 0.11155881235872057 * ZZIZ\n",
+ "+ 0.006333539322176311 * ZZZX\n",
+ "- 0.006333539322176311 * ZZIX\n",
+ "+ 0.006333532495285946 * XZXX\n",
+ "- 0.006333532495285946 * XIXX\n",
+ "- 0.006333532495285946 * XZYY\n",
+ "+ 0.006333532495285946 * XIYY\n",
+ "+ 0.11155879438930083 * ZIIZ\n",
+ "- 0.00633353757459625 * ZIZX\n",
+ "+ 0.00633353757459625 * ZIIX\n",
+ "- 0.11155881235872057 * IZZZ\n",
+ "- 0.006333539322176311 * ZXZZ\n",
+ "- 0.006333539322176311 * IXZZ\n",
+ "- 0.006333532495285946 * XXXZ\n",
+ "+ 0.006333532495285946 * YYXZ\n",
+ "- 0.006333532495285946 * XXXI\n",
+ "+ 0.006333532495285946 * YYXI\n",
+ "+ 0.09813711735399637 * ZZZZ\n",
+ "- 0.0068116801754286496 * XZXZ\n",
+ "+ 0.0068116801754286496 * XIXZ\n",
+ "- 0.0068116801754286496 * XZXI\n",
+ "+ 0.0068116801754286496 * XIXI\n",
+ "+ 0.08451373536744687 * ZIZZ\n",
+ "+ 0.11155879438930083 * IZZI\n",
+ "- 0.006333537574596249 * ZXZI\n",
+ "- 0.006333537574596249 * IXZI\n",
+ "- 0.08451373536744687 * ZZZI\n",
+ "- 0.09813709250027836 * ZIZI\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Set the mapper to qubits\n",
+ "parity_mapper = ParityMapper() # This is the example of parity mapping\n",
+ "\n",
+ "# Set the qubit converter with two qubit reduction to reduce the computational cost \n",
+ "parity_converter = QubitConverter(parity_mapper, two_qubit_reduction=True) \n",
+ "\n",
+ "# Compute the Hamitonian in qubit form\n",
+ "qubit_op_parity = parity_converter.convert(second_q_ops_reduced.get('ElectronicEnergy'), num_particles=problem_reduced.num_particles)\n",
+ "\n",
+ "print(qubit_op_parity)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e515cade-3dc7-4826-a7c5-ef5aae7e72ca",
+ "metadata": {},
+ "source": [
+ "### 4. Compute the Real Solution for Reference Plotting\n",
+ "\n",
+ "Before we start to compute the ground state using Qiskit Runtime, let's compute a reference ground state energy by using `GroundStateEigensolver`. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "7068a7f6-9953-40ce-bc9a-495fb92736fd",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Reference energy : (-1.3014603031212078+0j)\n"
+ ]
+ }
+ ],
+ "source": [
+ "vqe_factory = VQEUCCFactory( # This is an example of UCC\"SD\" ansatz\n",
+ " quantum_instance=Aer.get_backend(\"aer_simulator_statevector\"),\n",
+ " optimizer=SLSQP(),\n",
+ " ansatz=UCC(excitations='sd')\n",
+ ") \n",
+ "\n",
+ "from qiskit.algorithms import NumPyMinimumEigensolver\n",
+ "\n",
+ "numpy_solver = NumPyMinimumEigensolver()\n",
+ "\n",
+ "solver = GroundStateEigensolver(parity_converter, vqe_factory) # Define Numpy\n",
+ "real_solution_t = solver.solve(problem_reduced).total_energies[0] \n",
+ "print('Reference energy : ', real_solution_t)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f0a1e566-660d-4ed7-ba83-3b1547f12549",
+ "metadata": {},
+ "source": [
+ "## Build VQE Routine on Estimator"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "413cd519-2c38-4d91-8681-5c32edc3fc33",
+ "metadata": {},
+ "source": [
+ "To find the ground state one of the most popular approaches around is the **Variational Quantum Eigensolver (VQE) algorithm.** The VQE algorithm works by exchanging information between a classical and a quantum computer as depicted in the following figure.\n",
+ "\n",
+ "In the previous labs we leveraged prebuilt functions in Terra to run our VQE instances. Here, we shall try to build one from the ground up using the Estimator primitive. We shall also pass in a `noise_model` to simulate a noisy simulation routine here later in the notebook."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3030a221-acd8-424d-8bc0-bddcaac97d50",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "Image: [Qiskit Nature Docs: Ground state solver tutorial](https://qiskit.org/documentation/nature/tutorials/03_ground_state_solvers.html)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d6f20447-5665-4d61-ae3a-8b2bf0f2f0c3",
+ "metadata": {},
+ "source": [
+ "We shall build up on a VQE routine using `Estimator` to measure expectation values of the prepared states and use a classical optimizer to readjust our parameters again for the next iteration."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "047decd9-301e-4eb7-a1d3-be3b06e029c8",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "### Building a Variational Quantum Eigensolver using Estimator \n",
+ "\n",
+ "To build up a routine for VQE using estimator, let's look at our previous image and leverage the same intuition. \n",
+ "\n",
+ "For our variational model or `ansatz` which will serve as our model circuit for evaluation and optimization of parameters as shown in the Qiskit Nature docs tutorial image above, we shall use a chemistry specific `UCCSD` type of ansatz for our problem here. \n",
+ "\n",
+ "As mentioned in the Qiskit nature **[tutorial](https://qiskit.org/documentation/nature/tutorials/03_ground_state_solvers.html)**: the Unitary Coupled Cluster **[`UCC`](https://qiskit.org/documentation/nature/stubs/qiskit_nature.circuit.library.UCC.html)** ansatz (see **[[12]](https://arxiv.org/abs/1805.04340)** as an example) is a chemistry standard widely utilized across. Here we shall be specifying single and double excitation states. However, the excitation type (`'s'`,`'d'`,`'sd'`) as well as other parameters can be selected.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "5875fffb-92a8-4fca-9bc2-8bf629fd5621",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define our 'ansatz' for the problem\n",
+ "ansatz = UCC(\n",
+ " qubit_converter=parity_converter,\n",
+ " num_particles=problem_reduced.num_particles, \n",
+ " num_spin_orbitals=problem_reduced.num_spin_orbitals,\n",
+ " excitations='sd'\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3ad8d117-a987-4c24-97af-53782b938e22",
+ "metadata": {},
+ "source": [
+ "Next, we will define how we shall set our routine with the `Estimator`. Remember, the `Estimator` will return the estimated expectation values of quantum circuits and observables passed to it. The **circuit** to pass here will be our `ansatz`, the **observables** will be the `qubit_op_parity` hamiltonian we just made and the **parameter_values** will be the values processed by the classical optimizer passed here as `x`. For our example here, we shall be using the Simultaneous perturbation stochastic approximation (`SPSA`) optimizer for our routine.\n",
+ "\n",
+ "This will be done iteratively and here we shall define a routine as shown in the image below: "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3c97f762-19aa-4aef-ab75-21fb8f6572ae",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "31ff8071-14a1-4e11-ac97-1e86f08f3175",
+ "metadata": {},
+ "source": [
+ "The `optimizer` we define will be used to minimize a scalar function. Here we should define a function such that we return the computed expectation value result to be minimized. We shall define `evaluate_expectation` function to calculate the same using the `Estimator`. This will be the function passed to our optimizer to solve for and generate a new set of parameters to be evaluated again.\n",
+ "\n",
+ "For the purpose of visualizing the result and getting a view on the internal states of the optimization run, we shall also define a `callback` function which shall append the convergence value per run and store it in a list to be visualized later."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "640048d9-ad56-43ab-a0c5-dd31b91bbce1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from qiskit.primitives import Estimator, BackendEstimator"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9f560bcd-7818-4669-a895-a752c5656097",
+ "metadata": {},
+ "source": [
+ "## A short note on the Estimator Primitive"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a9cea49c-8990-471f-9a29-893a4c93f42c",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "We have been using the Qiskit Runtime version of `Estimator` until now. For the purposes of this exercise, we shall use a localized version of the `Estimator` which resides in Qiskit Terra.\n",
+ "\n",
+ "### Specification\n",
+ "Formally speaking, the _Estimator_ primitive is a standardized specification for calculating and interpreting expectation values for different combinations of quantum states (i.e. circuits) and operators. This means that there is no single `Estimator` class to solve this task, but rather a family of them; each of which performing the same (internal) calculation on a slightly different way, while exposing a common _application programming interface_ (API) to the users.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "011c5198-b183-416d-a8a0-177f4c43a95e",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b20915d6-63a2-43a4-a2f9-28a53d88d2b2",
+ "metadata": {},
+ "source": [
+ "Following now, we shall go forward with Qiskit's native Terra implementation of `Estimator` and `BackendEstimator` with Qiskit's native simulators. You can substitute this with the Qiskit Runtime Estimator by following the same routine as before if you wish. Here is a tutorial that will help you **[get started with the Estimator class residing in Qiskit Terra](https://qiskit.org/documentation/apidoc/primitives.html)**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ffac1f28-20cb-4cdb-baab-ac230e615fce",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "
\n",
+ "Exercise 2: \n",
+ " \n",
+ "Complete the cell below for building up our VQE routine. Define the `Estimator` primitive object and define the `estimator.run` call for the `evaluate_expectation` function. \n",
+ "Fill the optimizer call using the `SPSA` optimizer with `maxiter=50` and pass the callback function for the intermediate values to be stored for plotting. Finally, store the result by calling the optimizer.minimize call to complete the routine.\n",
+ " \n",
+ "For successful grading, use a `UCCSD ansatz` and `maxiter=50` for your optimizer and do not modify any of the seed values.