{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Variational Quantum Eigensolver\n", "\n", " Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overview\n", "\n", "It is widely believed that one of the most promising applications of quantum computing in the near future is solving quantum chemistry problems [1-2]. **Variational Quantum Eigensolver** (VQE) is a strong proof to this possibility of studying quantum chemistry with **Noisy Intermediate-Scale Quantum** (NISQ) devices [1-4]. The core task is to solve the energy ground state of any molecular Hamiltonian $\\hat{H}$ by preparing a parametrized wave function ansatz $|\\Psi(\\boldsymbol\\theta)\\rangle$ on a quantum computer and adopt classical optimization methods (e.g. gradient descent) to adjust the parameters $\\boldsymbol\\theta$ to minimize the expectation value $\\langle \\Psi(\\boldsymbol\\theta)|\\hat{H}|\\Psi(\\boldsymbol\\theta)\\rangle$. This approach is based on the **Rayleigh-Ritz variational principle**. \n", "\n", "$$\n", "E_0 = \\min_{\\boldsymbol\\theta} \\langle \\Psi(\\boldsymbol\\theta)|\\hat{H}|\\Psi(\\boldsymbol\\theta)\\rangle.\n", "\\tag{1}\n", "$$\n", "\n", "where $E_0$ denotes the ground state energy. Numerically, it can be understood as finding the smallest eigenvalue $\\lambda_{\\min}$ of a **discretized** Hamiltonian $H$ (hermitian matrix) and its corresponding eigenvector $|\\Psi_0\\rangle$. How such a discretization can be done on a classical computer belongs to the art of quantum chemistry and is far beyond the scope of this tutorial. We will discuss this part with a few words in the background section. In general, such a Hamiltonian $H$ is expressed as a weighted sum of Pauli spin operators $\\{X,Y,Z\\}$ (native to quantum devices) such that this information can be processed on a quantum computer.\n", "\n", "$$\n", "H = \\sum_k c_k ~ \\bigg( \\bigotimes_{j=0}^{M-1} \\sigma_j^{(k)} \\bigg),\n", "\\tag{2}\n", "$$\n", "\n", "where $\\sigma_j^{(k)} \\in \\{I,X,Y,Z\\}$ and $M$ stands for qubit number. We refer this form of Hamiltonian as **Pauli strings**. For example, \n", "\n", "$$\n", "H= 0.12~Y_0 \\otimes I_1-0.04~X_0\\otimes Z_1.\n", "\\tag{3}\n", "$$\n", "\n", "In the next section, we will provide a brief review on the electronic structure problem which essentially tells us where does the Hamiltonian $H$ come from. For those who are already familiar with this topic or only interested in how to implement VQE on Paddle Quantum, please skip this part and jump into the illustrative example of hydrogen molecule $H_2$.\n", "\n", "## Background: the electronic structure problem\n", "\n", "In this section, we focus on one of the fundamental problems in quantum chemistry -- **the electronic structure problem**. To be more specific, we are interested in the low lying energy eigenstates of any given molecule. These knowledge could help predict reaction rates and location of stable structures [5]. Suppose a molecule consists of $N_n$ nuclei and $N_e$ electrons, the first quantized (canonical quantization) Hamiltonian operator $\\hat{H}_{mol}$ describing the total energy of this molecular system can be written as\n", "\n", "$$\n", "\\begin{align}\n", "\\hat{H}_{\\text{mol}} & = -\\sum_{i}\\frac{\\nabla_{R_i}^2}{2M_i} - \\sum_{i} \\frac{\\nabla_{r_i}^2}{2} -\\sum_{i,j}\\frac{Z_i}{\\lvert R_i - r_j\\lvert} + \\sum_{i,j>i}\\frac{Z_iZ_j}{\\lvert R_i - R_j\\lvert} + \\sum_{i, j>i}\\frac{1}{\\lvert r_i - r_j\\lvert}, \n", "\\tag{4}\n", "\\end{align}\n", "$$\n", "\n", "where $R_i, M_i,$ and $Z_i$ denote the position, mass and atomic number (the number of protons) of the $i^{th}$ nucleus, and the positions of electrons are $r_i$. The first two sums describe the kinetic energy of nuclei and electrons, respectively. The third sum describes the attractive Coulomb interaction between the positively charged nuclei and the negatively charged electrons. The last two terms represent the repulsive nuclei-nuclei and electron-electron interactions. Here, the molecular Hamiltonian $\\hat{H}_\\text{mol}$ is already in atomic units of energy, **Hartree**. 1 Hartree is $[\\hbar^2/(m_ee^2a_0^2)] = 27.2$ eV or 630 kcal/mol, where $m_e, e,$ and $a_0$ stand for the mass of electron, charge of electron, and Bohr radius. \n", "\n", "\n", "**Note:** The spin-orbit interaction and hyperfine interaction are not considered in this picture. They can be treated as perturbations if necessary. \n", "\n", "### Born-Oppenheimer approximation\n", "\n", "Since the nuclei are much heavier than electrons, the electrons will move much faster than the nuclei. It is reasonable to treat the positions of nuclei as fixed, $R_i =$ constants. This is known as the Born-Oppenheimer approximation by decoupling the behavior of nuclei and electrons in time scale. Consequently, the kinetic energy term of nuclei will disappear and the nuclei-nuclei repulsive interaction term can be viewed as an energy shift (independent of electron positions $r_i$). We could derive the electronic Hamiltonian $\\hat{H}_{\\text{electron}}$ as\n", "\n", "$$\n", "\\begin{align}\n", "\\hat{H}_{\\text{electron}} & = - \\sum_{i} \\frac{\\nabla_{r_i}^2}{2} -\\sum_{i,j}\\frac{Z_i}{\\lvert R_i - r_j\\lvert} + \\sum_{i, j>i}\\frac{1}{\\lvert r_i - r_j\\lvert} \n", "\\tag{5},\n", "\\end{align}\n", "$$\n", "\n", "The energy levels of the electrons in the molecule can be found by solving the time independent Schrödinger equation\n", "$$\n", "\\hat{H}_{\\text{electron}} |\\Psi_n \\rangle = E_n |\\Psi_n \\rangle,\n", "\\tag{6}\n", "$$\n", "\n", "where $n$ stands for the energy level. Notice the electron repulsion terms scale as $N_e(N_e-1)/2$ which means for the Oxygen molecule $O_2$ carrying 16 electrons there will be 120 electron repulsion terms in total! In general, this problem cannot be solved analytically. As Dirac concluded in [Quantum mechanics of many-electron systems](https://royalsocietypublishing.org/doi/10.1098/rspa.1929.0094) [6],\n", "\n", "> *The underlying physical laws necessary for the mathematical theory of a large part of physics and the whole of chemistry are thus completely known, and the difficulty is only that the exact application of these laws leads to equations much too complicated to be soluble.* \n", ">\n", "> ​\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t-- Paul Dirac (1929)\n", "\n", "A straightforward numerical approach is discretizing the infinite-dimensional Hilbert space into equidistant grid points where linear algebra guides the whole calculation. Suppose each axis of space is discretized into $k$ points, the $N$-electron (drop the subscript e for simplicity) wave function can be written as [2]\n", "\n", "$$\n", "|\\Psi \\rangle = \\sum_{\\mathbf{x_1}, \\ldots, \\mathbf{x_N}} \\psi(\\mathbf{x_1}, \\ldots, \\mathbf{x_N}) \\mathcal{A}(|\\mathbf{x_1}, \\ldots, \\mathbf{x_N}\\rangle).\n", "\\tag{7}\n", "$$\n", "\n", "where coordinate $|\\mathbf{x_j}\\rangle = |r_j\\rangle |\\sigma_j\\rangle$ records the spatial location and spin of the $j^{th}$ electron, $|r_j\\rangle = |x_j,y_j,z_j\\rangle$ for $j\\in \\{1,2,\\cdots,N\\}$, $x_j,y_j,z_j \\in \\{0,1,\\cdots,k-1\\}$ and $\\sigma_j \\in \\{\\downarrow,\\uparrow\\}$ for spin down or up. There will be $k^{3N}\\times 2^{N}$ complex amplitudes in total. Here, $\\mathcal{A}$ denotes anti-symmetrization and a consequence of the Pauli exclusion principle (electrons are fermion), and $\\psi(\\mathbf{x_1}, \\mathbf{x_2}, \\ldots, \\mathbf{x_N})=\\langle\\mathbf{x_1}, \\mathbf{x_2}, \\ldots, \\mathbf{x_N}|\\Psi\\rangle$. One can see that storing such a wave function already requires **exponentially growing memory** with respect to the number of electrons $N$. This would make classical simulation methods based on this naive numerical approach intractable for systems size larger than few tens of electrons. Now, the question becomes can we prepare such a wave function $|\\Psi\\rangle$ directly on a quantum computer and measure the expectation value $E_0$? In the next section, let's take the simplest molecular system -- hydrogen molecule $H_2$ as a concrete example.\n", "\n", "\n", "\n", "**Note:** A detailed review on quantum chemistry and existing classical computational methods are far beyond the scope of this tutorial, we refer the enthusiastic readers to the standard textbooks *'Molecular Electronic-Structure Theory'* [5] by Helgaker and *'Modern Quantum Chemistry: Introduction to Advanced Electronic Structure Theory'* [7] by Szabo & Ostlund. To bridge to knowledge gap between quantum chemistry and quantum computing, please check the following review papers [Quantum computational chemistry](https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.92.015003) [2] and [Quantum chemistry in the age of quantum computing](https://pubs.acs.org/doi/10.1021/acs.chemrev.8b00803) [1].\n", "\n", "**Note:** For energy calculation, it is desired to reach the **chemical accuracy** of $1.6\\times10^{-3}$ Hartree or 1 kcal/mol . " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Energy ground state of the hydrogen molecule $H_2$\n", "\n", "### Building electronic Hamiltonian\n", "\n", "First of all, let us import the necessary libraries and packages.\n", "\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:04.537224Z", "start_time": "2021-01-09T07:26:00.836470Z" } }, "outputs": [], "source": [ "import os\n", "import platform\n", "import matplotlib.pyplot as plt\n", "%config InlineBackend.figure_format = 'svg'\n", "from IPython.display import clear_output\n", "\n", "import numpy\n", "from numpy import concatenate\n", "from numpy import pi as PI\n", "from numpy import savez, zeros\n", "\n", "from paddle import fluid\n", "from paddle.complex import matmul, transpose\n", "from paddle_quantum.circuit import UAnsatz\n", "from paddle_quantum.utils import pauli_str_to_matrix\n", "from paddle_quantum.VQE.chemistrysub import H2_generator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To analyze specific molecules, we need several key information such as **geometry**, **basis set** (such as STO-3G), **multiplicity**, and **charge** to obtain the discretized Hamiltonian $H$. Specifically, through our built-in quantum chemistry toolkit, fermion-to-qubit mapping technology can be used to output the qubit Hamiltonian of hydrogen molecule $H_2$," ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:04.570840Z", "start_time": "2021-01-09T07:26:04.544640Z" } }, "outputs": [], "source": [ "Hamiltonian, N = H2_generator()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more advanced users, we provide a simple tutorial on how to generate such a Hamiltonian. Install the following two packages first (**only available for Mac/Linux users, not available to Windows users temporarily**):" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:07.190974Z", "start_time": "2021-01-09T07:26:05.431945Z" } }, "outputs": [], "source": [ "!pip install openfermion==0.11.0\n", "clear_output()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:09.219268Z", "start_time": "2021-01-09T07:26:07.195371Z" } }, "outputs": [], "source": [ "!pip install openfermionpyscf==0.4\n", "clear_output()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:10.031901Z", "start_time": "2021-01-09T07:26:09.224389Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The generated h2 Hamiltonian is \n", " (-0.04207897647782281+0j) [] +\n", "(-0.04475014401535161+0j) [X0 X1 Y2 Y3] +\n", "(0.04475014401535161+0j) [X0 Y1 Y2 X3] +\n", "(0.04475014401535161+0j) [Y0 X1 X2 Y3] +\n", "(-0.04475014401535161+0j) [Y0 Y1 X2 X3] +\n", "(0.17771287465139946+0j) [Z0] +\n", "(0.17059738328801055+0j) [Z0 Z1] +\n", "(0.12293305056183798+0j) [Z0 Z2] +\n", "(0.16768319457718958+0j) [Z0 Z3] +\n", "(0.1777128746513994+0j) [Z1] +\n", "(0.16768319457718958+0j) [Z1 Z2] +\n", "(0.12293305056183798+0j) [Z1 Z3] +\n", "(-0.24274280513140456+0j) [Z2] +\n", "(0.17627640804319586+0j) [Z2 Z3] +\n", "(-0.24274280513140456+0j) [Z3]\n" ] } ], "source": [ "# Operating system information\n", "sysStr = platform.system()\n", "\n", "# Decide which operating system the user is using\n", "if sysStr in ('Linux', 'Darwin'):\n", "\n", " import openfermion\n", " import openfermionpyscf\n", "\n", " # Please check whether the geometric configuration file of h2 is downloaded correctly\n", " geo = 'h2.xyz'\n", " charge = 0\n", " multiplicity = 1\n", "\n", " # Generate Hamiltonian\n", " mol = openfermion.hamiltonians.MolecularData(geo, 'sto-3g', multiplicity, charge)\n", " openfermionpyscf.run_pyscf(mol)\n", " terms_molecular_hamiltonian = mol.get_molecular_hamiltonian()\n", " fermionic_hamiltonian = openfermion.transforms.get_fermion_operator(terms_molecular_hamiltonian)\n", " qubit_op = openfermion.transforms.jordan_wigner(fermionic_hamiltonian)\n", "\n", " # Print result\n", " print(\"The generated h2 Hamiltonian is \\n\", qubit_op)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note:** This Hamiltonian is generated with an interatomic distance of $d = 74$ pm. \n", "\n", "In addition to hydrogen molecule $H_2$, we also provide the geometric configuration file of hydrogen fluoride (HF) molecule `hf.xyz`. If you need to test the geometric configuration of more molecules, please check out this [database](http://smart.sns.it/molecules/index.html). In addition, we also need to convert the Hamiltonian into the Pauli string format supported by Paddle Quantum. Here we provide this interface.\n", "\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:11.884870Z", "start_time": "2021-01-09T07:26:11.875694Z" } }, "outputs": [], "source": [ "def Hamiltonian_str_convert(qubit_op):\n", " '''\n", " Convert the Hamiltonian information provided above into Pauli strings supported by Paddle Quantum\n", "\n", " H = [[1.0, \"z0,x1\"], [-1.0, \"y0,z1\"], ...]\n", " '''\n", " info_dic = qubit_op.terms\n", " \n", " def process_tuple(tup):\n", " if len(tup) == 0:\n", " return 'i0'\n", " else:\n", " res = ''\n", " for ele in tup:\n", " res += ele[1].lower()\n", " res += str(ele[0])\n", " res += ','\n", " return res[:-1]\n", " H_info = []\n", " \n", " for key, value in qubit_op.terms.items():\n", " H_info.append([value.real, process_tuple(key)])\n", " \n", " return H_info\n", "\n", "if sysStr in ('Linux', 'Darwin'):\n", " Hamiltonian = Hamiltonian_str_convert(qubit_op)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Building QNN and trial wave function\n", "\n", "To implement VQE, we firstly need to design a quantum neural network QNN to prepare the wave function ansatz $|\\Psi(\\boldsymbol\\theta)\\rangle$. Here, we provide a 4-qubit quantum circuit template with a depth of $D$ blocks. The dotted frame in the figure below denotes a single block:\n", "\n", "\n", "![Utheta.jpg](https://release-data.cdn.bcebos.com/PIC%2FUtheta.jpg)\n", "\n", "Next, we use the `UAnsatz` class and the built-in `real_entangled_layer(theta, D)` circuit template in Paddle Quantum to realize this QNN.\n", "\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:13.717206Z", "start_time": "2021-01-09T07:26:13.709627Z" } }, "outputs": [], "source": [ "def U_theta(theta, Hamiltonian, N, D):\n", " \"\"\"\n", " Quantum Neural Network\n", " \"\"\"\n", " \n", " # Initialize the quantum neural network according to the number of qubits N\n", " cir = UAnsatz(N)\n", " \n", " # Built-in {R_y + CNOT} circuit template\n", " cir.real_entangled_layer(theta[:D], D)\n", " \n", " # Lay R_y gates in the last row\n", " for i in range(N):\n", " cir.ry(theta=theta[D][i][0], which_qubit=i)\n", " \n", " # The quantum neural network acts on the default initial state |0000>\n", " cir.run_state_vector()\n", " \n", " # Calculate the expected value of a given Hamiltonian\n", " expectation_val = cir.expecval(Hamiltonian)\n", "\n", " return expectation_val" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up the loss function and model\n", "\n", "Now that we have the target Hamiltonian and QNN, we will further define the training model and loss function. By applying the QNN $U(\\theta)$ on the initial state $|0..0\\rangle$, we get the output state $|\\psi(\\boldsymbol\\theta)\\rangle $. Then, the loss function to be minimized is the expectation value, \n", "\n", "\n", "$$\n", "\\min_{\\boldsymbol\\theta} \\mathcal{L}(\\boldsymbol \\theta) = \\min_{\\boldsymbol\\theta} \\langle \\Psi(\\boldsymbol\\theta)|H |\\Psi(\\boldsymbol\\theta)\\rangle\n", "= \\min_{\\boldsymbol\\theta} \\sum_k c_k~\\langle \\Psi(\\boldsymbol\\theta)| \\bigotimes_j \\sigma_j^{(k)}|\\Psi(\\boldsymbol\\theta)\\rangle.\n", "\\tag{8}\n", "$$" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:15.661433Z", "start_time": "2021-01-09T07:26:15.654903Z" } }, "outputs": [], "source": [ "class StateNet(fluid.dygraph.Layer):\n", "\n", " def __init__(self, shape, param_attr=fluid.initializer.Uniform(\n", " low=0.0, high=2 * PI), dtype=\"float64\"):\n", " super(StateNet, self).__init__()\n", " \n", " # Initialize the theta parameter list and fill the initial value with a uniform distribution of [0, 2*pi]\n", " self.theta = self.create_parameter(shape=shape, attr=param_attr, dtype=dtype, is_bias=False)\n", " \n", " # Define loss function and forward propagation mechanism\n", " def forward(self, N, D):\n", " \n", " # Calculate the loss function/expected value\n", " loss = U_theta(self.theta, Hamiltonian, N, D)\n", "\n", " return loss" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Hyper-parameters\n", "\n", "Before training the QNN, we also need to set some training hyper-parameters, mainly the learning rate (LR), the number of iterations (ITR), and the depth (D) of repeated blocks. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:17.770511Z", "start_time": "2021-01-09T07:26:17.767150Z" } }, "outputs": [], "source": [ "ITR = 100 # Set the number of optimization iterations\n", "LR = 0.2 # Set the learning rate\n", "D = 2 # Set the depth of the repetitive calculation module in QNN" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Training\n", "\n", "After all the training model parameters are set, we convert the data into variables in the Paddle dynamic computational graph, and then train the quantum neural network. The results of the training process is stored in the summary_data file.\n", "\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:33.571665Z", "start_time": "2021-01-09T07:26:19.157308Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "iter: 20 loss: -0.8523\n", "iter: 20 Ground state energy: -0.8523 Ha\n", "iter: 40 loss: -1.1264\n", "iter: 40 Ground state energy: -1.1264 Ha\n", "iter: 60 loss: -1.1323\n", "iter: 60 Ground state energy: -1.1323 Ha\n", "iter: 80 loss: -1.1360\n", "iter: 80 Ground state energy: -1.1360 Ha\n", "iter: 100 loss: -1.1361\n", "iter: 100 Ground state energy: -1.1361 Ha\n" ] } ], "source": [ "# Initialize Paddle dynamic computational graph\n", "with fluid.dygraph.guard():\n", "\n", "\n", " # Determine the parameter dimension of the network\n", " net = StateNet(shape=[D + 1, N, 1])\n", "\n", " \n", " # Generally speaking, we use Adam optimizer to obtain relatively good convergence,\n", " # You can change it to SGD or RMS prop.\n", " opt = fluid.optimizer.AdamOptimizer(\n", " learning_rate=LR, parameter_list=net.parameters())\n", "\n", " # Record optimization results\n", " summary_iter, summary_loss = [], []\n", " \n", " # Optimization loop\n", " for itr in range(1, ITR + 1):\n", " \n", " # Forward propagation to calculate loss function\n", " loss = net(N, D)\n", "\n", " # Use back propagation to minimize the loss function\n", " loss.backward()\n", " opt.minimize(loss)\n", " net.clear_gradients()\n", " \n", " # Record optimization results\n", " summary_loss.append(loss.numpy())\n", " summary_iter.append(itr)\n", " \n", " # Print result\n", " if itr % 20 == 0:\n", " print(\"iter:\", itr, \"loss:\", \"%.4f\" % loss.numpy())\n", " print(\"iter:\", itr, \"Ground state energy:\", \"%.4f Ha\" \n", " % loss.numpy())\n", "\n", " # Save the training results to the output folder\n", " os.makedirs(\"output\", exist_ok=True)\n", " savez(\"./output/summary_data\", iter = summary_iter, \n", " energy=summary_loss)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Benchmarking\n", "We have now completed the training of the quantum neural network, and the estimated value of the ground state energy obtained is $E_0 \\approx -1.1361$ Hartree, we compare it with the theoretical value $E_0 = -1.13618$ to benchmark our model. The estimation obtained with VQE agree with a full configuration-interaction (FCI) calculation within chemical accuracy $\\varepsilon = 1.6 \\times 10^{-3}$ Hartree.\n", "\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2021-01-09T07:26:42.456121Z", "start_time": "2021-01-09T07:26:41.495984Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-01-09T15:26:42.336203\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.3.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "result = numpy.load('./output/summary_data.npz')\n", "\n", "eig_val, eig_state = numpy.linalg.eig(\n", " pauli_str_to_matrix(Hamiltonian, N))\n", "min_eig_H = numpy.min(eig_val.real)\n", "min_loss = numpy.ones([len(result['iter'])]) * min_eig_H\n", "\n", "plt.figure(1)\n", "func1, = plt.plot(result['iter'], result['energy'], \n", " alpha=0.7, marker='', linestyle=\"-\", color='r')\n", "func_min, = plt.plot(result['iter'], min_loss, \n", " alpha=0.7, marker='', linestyle=\":\", color='b')\n", "plt.xlabel('Number of iteration')\n", "plt.ylabel('Energy (Ha)')\n", "\n", "plt.legend(handles=[\n", " func1,\n", " func_min\n", "],\n", " labels=[\n", " r'$\\left\\langle {\\psi \\left( {\\theta } \\right)} '\n", " r'\\right|H\\left| {\\psi \\left( {\\theta } \\right)} \\right\\rangle $',\n", " 'Ground-state energy',\n", " ], loc='best')\n", "\n", "#plt.savefig(\"vqe.png\", bbox_inches='tight', dpi=300)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Determining the interatomic distance\n", "\n", "Recall the above calculation is done with an interatomic distance $d = 74$ pm between two hydrogen atoms. Another interesting aspect we can try with VQE is determining the true interatomic distance by modifying the `h2.xyz` file. The results are summarize in figure below,\n", "\n", "![VQE-fig-dist](figures/VQE-fig-distance.png)\n", "\n", "The lowest value is found around $d = 74$ pm (1 pm = $1\\times 10^{-12}$m), which is consistent with the [experimental data](https://cccbdb.nist.gov/exp2x.asp?casno=1333740&charge=0) $d_{exp} (H_2) = 74.14$ pm.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "_______\n", "\n", "## References\n", "\n", "[1] [Cao, Yudong, et al. Quantum chemistry in the age of quantum computing. Chemical reviews 119.19 (2019): 10856-10915.](https://pubs.acs.org/doi/10.1021/acs.chemrev.8b00803)\n", "\n", "[2] [McArdle, Sam, et al. Quantum computational chemistry. Reviews of Modern Physics 92.1 (2020): 015003.](https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.92.015003)\n", "\n", "\n", "[3] [Peruzzo, A. et al. A variational eigenvalue solver on a photonic quantum processor. Nat. Commun. 5, 4213 (2014).](https://www.nature.com/articles/ncomms5213)\n", "\n", "[4] [Moll, Nikolaj, et al. Quantum optimization using variational algorithms on near-term quantum devices. Quantum Science and Technology 3.3 (2018): 030503.](https://iopscience.iop.org/article/10.1088/2058-9565/aab822)\n", "\n", "[5] Helgaker, Trygve, Poul Jorgensen, and Jeppe Olsen. Molecular electronic-structure theory. John Wiley & Sons, 2014.\n", "\n", "[6] [Dirac, Paul Adrien Maurice. Quantum mechanics of many-electron systems. Proceedings of the Royal Society of London. Series A, Containing Papers of a Mathematical and Physical Character 123.792 (1929): 714-733.](https://royalsocietypublishing.org/doi/10.1098/rspa.1929.0094)\n", "\n", "[7] Szabo, Attila, and Neil S. Ostlund. Modern quantum chemistry: introduction to advanced electronic structure theory. Courier Corporation, 2012.\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.3" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "426.667px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }