Skip to content

Instantly share code, notes, and snippets.

@buttercutter
Created February 4, 2023 15:33
Show Gist options
  • Save buttercutter/19cd09def3360f88db509670179fd54c to your computer and use it in GitHub Desktop.
Save buttercutter/19cd09def3360f88db509670179fd54c to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "e521af8c",
"metadata": {},
"source": [
"Credit: https://github.com/tencent-quantum-lab/tensorcircuit/issues/120"
]
},
{
"cell_type": "markdown",
"id": "4009fbd4",
"metadata": {},
"source": [
"# Variational Quantum Eigensolver (VQE) on Molecules"
]
},
{
"cell_type": "markdown",
"id": "d0b04a30",
"metadata": {
"tags": []
},
"source": [
"## Overview\n",
"\n",
"VQE is a variational algorithm for calculating the ground state of some given hamiltonian H which we call it $\\psi_g$ that satisfied $H \\left|\\psi_g\\right> =E_g\\left|\\psi_g\\right>$. For an arbitrary normalized wavefunction $\\psi_f$, the expectation value $\\left<\\psi_f|H|\\psi_f \\right>$ is always not lower than the ground state energy unless $\\psi_f = \\psi_g$ to some phase factor (here we assume there is no degeneracy in ground state). Based on that fact, if we use a parameterized wavefunction $\\psi_\\theta$, e.g. given by a parameterized quantum circuit (PQC) with parameters $\\theta$, we can give an approximation for the ground state enery and wavefunction by minimizing the expectation value of $H$. In practical quantum hardware, this algorithm can be realized in a quantum-neural hybrid paradigm with the gradient calculated using finite difference or paremeter shift in quantum hardware and the optimization using gradient descent method in classical computer. While in a numerical simulation, we can just calculate the gradients using automatic differentiation. \n",
"\n",
"Calculating the ground state energy for molecules is often important for quantum chemistry tasks since it can be used to find out the atom structure of the molecules. In the simulation of molecules, we do not consider the motion of nuclei which means we fix the nuclear coordinates of its constituent atoms. We only consider the electrons in the molecules since the nuclei are way heavier than the electrons and thus the energy carried by phonons is negligible or can be reconsidered using Born-Oppenheimer approximation. Strictly speaking, the eletrons lives in continuous space, thus the Hilbert space is of infinite dimensions. To conduct a practical calculation, we only preserve some important single-particle basis, e.g. the low energy atomic orbitals. In the second quantization formalism, we can represent these atomic orbitals as $c_i^\\dagger|0>$. By considering the interactions of nuclei and electrons as background and also the electron-electron interaction, a molecules hamiltonian can in generally be represented as $H = \\sum_{i, j} h_{i,j} c_i^\\dagger c_j + \\sum_{i, j, k, l} \\alpha_{i, j, k, l} c_i^\\dagger c_j^\\dagger c_k c_l$. Notice that the spin index is also absorbed into the orbital index. There are many softwares that can give these parameters in H such as `pyscf` which we will use later in this tutorial. Now we have a fermionic description for moleculars. By using a mapping from fermionic operators to spin operators such as Jordan-Wigner transformation or Bravyi-Kitaev transformation, we can map the fermionic hamiltonian to a spin hamiltonian which is more compatible with quantum computer. For a spin hamiltonian, we can easily use a PQC to construct a trail wavefunction and conduct the VQE algorithm. In the following part of this tutorial, we will demonstrate a complete example of how to use TensorCircuit to simulate VQE algorithm on Molecules."
]
},
{
"cell_type": "markdown",
"id": "12886697",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"We should first ``pip install openfermion openfermionpyscf`` to generate fermionic and qubit Hamiltonian of CO2 molecule based on quantum chemistry computation provided by openfermion and pyscf."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "68bfb967",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already up-to-date: openfermion in /home/moe/.local/lib/python3.8/site-packages (1.5.1)\n",
"Requirement already up-to-date: openfermionpyscf in /home/moe/.local/lib/python3.8/site-packages (0.5)\n",
"Requirement already up-to-date: tensorflow in /home/moe/.local/lib/python3.8/site-packages (2.11.0)\n",
"Requirement already up-to-date: tensorcircuit in /home/moe/.local/lib/python3.8/site-packages (0.7.0)\n",
"Requirement already satisfied, skipping upgrade: cirq-core>=0.15.0 in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (1.1.0)\n",
"Requirement already satisfied, skipping upgrade: cirq-google>=0.15.0 in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (1.1.0)\n",
"Requirement already satisfied, skipping upgrade: deprecation in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (2.1.0)\n",
"Requirement already satisfied, skipping upgrade: h5py>=2.8 in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (3.8.0)\n",
"Requirement already satisfied, skipping upgrade: networkx in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (2.8.8)\n",
"Requirement already satisfied, skipping upgrade: numpy>=1.11.0 in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (1.23.5)\n",
"Requirement already satisfied, skipping upgrade: pubchempy in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (1.0.4)\n",
"Requirement already satisfied, skipping upgrade: requests>=2.18 in /usr/local/lib/python3.8/dist-packages (from openfermion) (2.27.1)\n",
"Requirement already satisfied, skipping upgrade: scipy>=1.1.0 in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (1.10.0)\n",
"Requirement already satisfied, skipping upgrade: sympy in /home/moe/.local/lib/python3.8/site-packages (from openfermion) (1.11.1)\n",
"Requirement already satisfied, skipping upgrade: pyscf in /home/moe/.local/lib/python3.8/site-packages (from openfermionpyscf) (2.1.1)\n",
"Requirement already satisfied, skipping upgrade: pytest in /home/moe/.local/lib/python3.8/site-packages (from openfermionpyscf) (7.2.0)\n",
"Requirement already satisfied, skipping upgrade: absl-py>=1.0.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (1.3.0)\n",
"Requirement already satisfied, skipping upgrade: astunparse>=1.6.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (1.6.3)\n",
"Requirement already satisfied, skipping upgrade: flatbuffers>=2.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (23.1.21)\n",
"Requirement already satisfied, skipping upgrade: gast<=0.4.0,>=0.2.1 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (0.4.0)\n",
"Requirement already satisfied, skipping upgrade: google-pasta>=0.1.1 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (0.2.0)\n",
"Requirement already satisfied, skipping upgrade: grpcio<2.0,>=1.24.3 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (1.51.1)\n",
"Requirement already satisfied, skipping upgrade: keras<2.12,>=2.11.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (2.11.0)\n",
"Requirement already satisfied, skipping upgrade: libclang>=13.0.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (15.0.6.1)\n",
"Requirement already satisfied, skipping upgrade: opt-einsum>=2.3.2 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (3.3.0)\n",
"Requirement already satisfied, skipping upgrade: packaging in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (23.0)\n",
"Requirement already satisfied, skipping upgrade: protobuf<3.20,>=3.9.2 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (3.19.6)\n",
"Requirement already satisfied, skipping upgrade: setuptools in /usr/local/lib/python3.8/dist-packages (from tensorflow) (62.3.2)\n",
"Requirement already satisfied, skipping upgrade: six>=1.12.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (1.16.0)\n",
"Requirement already satisfied, skipping upgrade: tensorboard<2.12,>=2.11 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (2.11.0)\n",
"Requirement already satisfied, skipping upgrade: tensorflow-estimator<2.12,>=2.11.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (2.11.0)\n",
"Requirement already satisfied, skipping upgrade: termcolor>=1.1.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (2.2.0)\n",
"Requirement already satisfied, skipping upgrade: typing-extensions>=3.6.6 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (4.4.0)\n",
"Requirement already satisfied, skipping upgrade: wrapt>=1.11.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (1.14.1)\n",
"Requirement already satisfied, skipping upgrade: tensorflow-io-gcs-filesystem>=0.23.1; platform_machine != \"arm64\" or platform_system != \"Darwin\" in /home/moe/.local/lib/python3.8/site-packages (from tensorflow) (0.30.0)\n",
"Requirement already satisfied, skipping upgrade: tensornetwork in /home/moe/.local/lib/python3.8/site-packages (from tensorcircuit) (0.4.6)\n",
"Requirement already satisfied, skipping upgrade: duet~=0.2.7 in /home/moe/.local/lib/python3.8/site-packages (from cirq-core>=0.15.0->openfermion) (0.2.7)\n",
"Requirement already satisfied, skipping upgrade: matplotlib~=3.0 in /home/moe/.local/lib/python3.8/site-packages (from cirq-core>=0.15.0->openfermion) (3.6.2)\n",
"Requirement already satisfied, skipping upgrade: pandas in /home/moe/.local/lib/python3.8/site-packages (from cirq-core>=0.15.0->openfermion) (1.5.2)\n",
"Requirement already satisfied, skipping upgrade: sortedcontainers~=2.0 in /home/moe/.local/lib/python3.8/site-packages (from cirq-core>=0.15.0->openfermion) (2.4.0)\n",
"Requirement already satisfied, skipping upgrade: tqdm in /home/moe/.local/lib/python3.8/site-packages (from cirq-core>=0.15.0->openfermion) (4.64.1)\n",
"Requirement already satisfied, skipping upgrade: google-api-core[grpc]<2.0.0dev,>=1.14.0 in /home/moe/.local/lib/python3.8/site-packages (from cirq-google>=0.15.0->openfermion) (1.34.0)\n",
"Requirement already satisfied, skipping upgrade: proto-plus>=1.20.0 in /home/moe/.local/lib/python3.8/site-packages (from cirq-google>=0.15.0->openfermion) (1.22.2)\n",
"Requirement already satisfied, skipping upgrade: urllib3<1.27,>=1.21.1 in /home/moe/.local/lib/python3.8/site-packages (from requests>=2.18->openfermion) (1.26.13)\n",
"Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /usr/lib/python3/dist-packages (from requests>=2.18->openfermion) (2019.11.28)\n",
"Requirement already satisfied, skipping upgrade: charset-normalizer~=2.0.0; python_version >= \"3\" in /usr/local/lib/python3.8/dist-packages (from requests>=2.18->openfermion) (2.0.12)\n",
"Requirement already satisfied, skipping upgrade: idna<4,>=2.5; python_version >= \"3\" in /usr/lib/python3/dist-packages (from requests>=2.18->openfermion) (2.8)\n",
"Requirement already satisfied, skipping upgrade: mpmath>=0.19 in /home/moe/.local/lib/python3.8/site-packages (from sympy->openfermion) (1.2.1)\n",
"Requirement already satisfied, skipping upgrade: attrs>=19.2.0 in /usr/lib/python3/dist-packages (from pytest->openfermionpyscf) (19.3.0)\n",
"Requirement already satisfied, skipping upgrade: iniconfig in /home/moe/.local/lib/python3.8/site-packages (from pytest->openfermionpyscf) (2.0.0)\n",
"Requirement already satisfied, skipping upgrade: pluggy<2.0,>=0.12 in /home/moe/.local/lib/python3.8/site-packages (from pytest->openfermionpyscf) (1.0.0)\n",
"Requirement already satisfied, skipping upgrade: exceptiongroup>=1.0.0rc8; python_version < \"3.11\" in /home/moe/.local/lib/python3.8/site-packages (from pytest->openfermionpyscf) (1.1.0)\n",
"Requirement already satisfied, skipping upgrade: tomli>=1.0.0; python_version < \"3.11\" in /home/moe/.local/lib/python3.8/site-packages (from pytest->openfermionpyscf) (2.0.1)\n",
"Requirement already satisfied, skipping upgrade: wheel<1.0,>=0.23.0 in /usr/lib/python3/dist-packages (from astunparse>=1.6.0->tensorflow) (0.34.2)\n",
"Requirement already satisfied, skipping upgrade: google-auth<3,>=1.6.3 in /home/moe/.local/lib/python3.8/site-packages (from tensorboard<2.12,>=2.11->tensorflow) (2.16.0)\n",
"Requirement already satisfied, skipping upgrade: google-auth-oauthlib<0.5,>=0.4.1 in /home/moe/.local/lib/python3.8/site-packages (from tensorboard<2.12,>=2.11->tensorflow) (0.4.6)\n",
"Requirement already satisfied, skipping upgrade: markdown>=2.6.8 in /home/moe/.local/lib/python3.8/site-packages (from tensorboard<2.12,>=2.11->tensorflow) (3.4.1)\n",
"Requirement already satisfied, skipping upgrade: tensorboard-data-server<0.7.0,>=0.6.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorboard<2.12,>=2.11->tensorflow) (0.6.1)\n",
"Requirement already satisfied, skipping upgrade: tensorboard-plugin-wit>=1.6.0 in /home/moe/.local/lib/python3.8/site-packages (from tensorboard<2.12,>=2.11->tensorflow) (1.8.1)\n",
"Requirement already satisfied, skipping upgrade: werkzeug>=1.0.1 in /home/moe/.local/lib/python3.8/site-packages (from tensorboard<2.12,>=2.11->tensorflow) (2.2.2)\n",
"Requirement already satisfied, skipping upgrade: graphviz>=0.11.1 in /home/moe/.local/lib/python3.8/site-packages (from tensornetwork->tensorcircuit) (0.20.1)\n",
"Requirement already satisfied, skipping upgrade: contourpy>=1.0.1 in /home/moe/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (1.0.6)\n",
"Requirement already satisfied, skipping upgrade: cycler>=0.10 in /home/moe/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (0.11.0)\n",
"Requirement already satisfied, skipping upgrade: fonttools>=4.22.0 in /home/moe/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (4.38.0)\n",
"Requirement already satisfied, skipping upgrade: kiwisolver>=1.0.1 in /home/moe/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (1.4.4)\n",
"Requirement already satisfied, skipping upgrade: pillow>=6.2.0 in /home/moe/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (9.4.0)\n",
"Requirement already satisfied, skipping upgrade: pyparsing>=2.2.1 in /usr/lib/python3/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (2.4.6)\n",
"Requirement already satisfied, skipping upgrade: python-dateutil>=2.7 in /home/moe/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (2.8.2)\n",
"Requirement already satisfied, skipping upgrade: pytz>=2020.1 in /home/moe/.local/lib/python3.8/site-packages (from pandas->cirq-core>=0.15.0->openfermion) (2022.7)\n",
"Requirement already satisfied, skipping upgrade: googleapis-common-protos<2.0dev,>=1.56.2 in /home/moe/.local/lib/python3.8/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.14.0->cirq-google>=0.15.0->openfermion) (1.58.0)\n",
"Requirement already satisfied, skipping upgrade: grpcio-status<2.0dev,>=1.33.2; extra == \"grpc\" in /home/moe/.local/lib/python3.8/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.14.0->cirq-google>=0.15.0->openfermion) (1.51.1)\n",
"Requirement already satisfied, skipping upgrade: cachetools<6.0,>=2.0.0 in /home/moe/.local/lib/python3.8/site-packages (from google-auth<3,>=1.6.3->tensorboard<2.12,>=2.11->tensorflow) (5.2.1)\n",
"Requirement already satisfied, skipping upgrade: pyasn1-modules>=0.2.1 in /usr/lib/python3/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.12,>=2.11->tensorflow) (0.2.1)\n",
"Requirement already satisfied, skipping upgrade: rsa<5,>=3.1.4; python_version >= \"3.6\" in /home/moe/.local/lib/python3.8/site-packages (from google-auth<3,>=1.6.3->tensorboard<2.12,>=2.11->tensorflow) (4.9)\n",
"Requirement already satisfied, skipping upgrade: requests-oauthlib>=0.7.0 in /home/moe/.local/lib/python3.8/site-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.12,>=2.11->tensorflow) (1.3.1)\n",
"Requirement already satisfied, skipping upgrade: importlib-metadata>=4.4; python_version < \"3.10\" in /home/moe/.local/lib/python3.8/site-packages (from markdown>=2.6.8->tensorboard<2.12,>=2.11->tensorflow) (6.0.0)\n",
"Requirement already satisfied, skipping upgrade: MarkupSafe>=2.1.1 in /home/moe/.local/lib/python3.8/site-packages (from werkzeug>=1.0.1->tensorboard<2.12,>=2.11->tensorflow) (2.1.1)\n",
"Requirement already satisfied, skipping upgrade: pyasn1>=0.1.3 in /usr/lib/python3/dist-packages (from rsa<5,>=3.1.4; python_version >= \"3.6\"->google-auth<3,>=1.6.3->tensorboard<2.12,>=2.11->tensorflow) (0.4.2)\n",
"Requirement already satisfied, skipping upgrade: oauthlib>=3.0.0 in /usr/lib/python3/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.12,>=2.11->tensorflow) (3.1.0)\n",
"Requirement already satisfied, skipping upgrade: zipp>=0.5 in /home/moe/.local/lib/python3.8/site-packages (from importlib-metadata>=4.4; python_version < \"3.10\"->markdown>=2.6.8->tensorboard<2.12,>=2.11->tensorflow) (3.11.0)\n"
]
}
],
"source": [
"!pip3 install --upgrade openfermion openfermionpyscf tensorflow tensorcircuit"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "9e28c969",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2023-02-04 14:42:20.605315: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 AVX512F FMA\n",
"To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n",
"2023-02-04 14:42:20.743267: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/lib\n",
"2023-02-04 14:42:20.743296: I tensorflow/compiler/xla/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.\n",
"2023-02-04 14:42:21.469284: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/lib\n",
"2023-02-04 14:42:21.469409: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/lib\n",
"2023-02-04 14:42:21.469418: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n",
"/home/moe/.local/lib/python3.8/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
]
}
],
"source": [
"import numpy as np\n",
"from openfermion.chem import MolecularData\n",
"from openfermion.transforms import (\n",
" get_fermion_operator,\n",
" jordan_wigner,\n",
" binary_code_transform,\n",
" checksum_code,\n",
" reorder,\n",
")\n",
"from openfermion.chem import geometry_from_pubchem\n",
"from openfermion.utils import up_then_down\n",
"from openfermion.linalg import LinearQubitOperator\n",
"from openfermionpyscf import run_pyscf\n",
"import tensorflow as tf\n",
"\n",
"import tensorcircuit as tc\n",
"\n",
"K = tc.set_backend(\"tensorflow\")"
]
},
{
"cell_type": "markdown",
"id": "8464f4f5",
"metadata": {},
"source": [
"## Generate Hamiltonian"
]
},
{
"cell_type": "markdown",
"id": "b6677497",
"metadata": {},
"source": [
"* Get molecule energy info and molecule orbitals"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "40574813",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-185.2925660467095 -185.26927761956804 -185.068031076591\n"
]
}
],
"source": [
"multiplicity = 1\n",
"basis = \"sto-3g\"\n",
"# 15 spin orbitals for CO2\n",
"geometry = geometry_from_pubchem(\"co2\")\n",
"description = \"co2\"\n",
"molecule = MolecularData(geometry, basis, multiplicity, description=description)\n",
"molecule = run_pyscf(molecule, run_mp2=True, run_cisd=True, run_ccsd=True, run_fci=True)\n",
"print(molecule.fci_energy, molecule.ccsd_energy, molecule.hf_energy)"
]
},
{
"cell_type": "markdown",
"id": "b6677497",
"metadata": {},
"source": [
"* Find the number of spin orbitals for H2O and CO2"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "23bbfd98",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of spin orbitals for H2O: 7\n",
"Number of spin orbitals for CO2: 15\n"
]
}
],
"source": [
"molecule_h2o = MolecularData(geometry_from_pubchem(\"h2o\"), basis, multiplicity, description=\"H2O\")\n",
"molecule_h2o = run_pyscf(molecule_h2o, run_mp2=True, run_cisd=True, run_ccsd=True, run_fci=True)\n",
"print(\"Number of spin orbitals for H2O:\", molecule_h2o.n_orbitals)\n",
"\n",
"molecule_co2 = MolecularData(geometry_from_pubchem(\"co2\"), basis, multiplicity, description=\"CO2\")\n",
"molecule_co2 = run_pyscf(molecule_co2, run_mp2=True, run_cisd=True, run_ccsd=True, run_fci=True)\n",
"print(\"Number of spin orbitals for CO2:\", molecule_co2.n_orbitals)"
]
},
{
"cell_type": "markdown",
"id": "6da70c9b",
"metadata": {},
"source": [
"* Some explanation on the discrepency between theory and code output:\n",
"\n",
"The number of spin orbitals for a molecule can be calculated based on the number of electrons in the molecule. Each electron will occupy one spin orbital, and there are two spin orbitals per occupied orbital: one with a spin-up electron and one with a spin-down electron. The total number of spin orbitals is then equal to 2 times the number of electrons.\n",
"\n",
"For H2O, there are 8 electrons in the molecule, so the number of spin orbitals is 2 * 8 = 16.\n",
"\n",
"For CO2, there are 16 electrons in the molecule, so the number of spin orbitals is 2 * 16 = 32.\n",
"\n",
"\n",
"The number of spin orbitals can be found from the molecule object returned by the run_pyscf function. In the case of H2O, it returns 7 spin orbitals and in the case of CO2, it returns 15 spin orbitals.\n",
"\n",
"This is because the number of spin orbitals is proportional to the number of electrons in the molecule and the type of basis set used to describe the molecule's electronic structure. The sto-3g basis set used in these examples is a relatively small basis set, which results in a smaller number of spin orbitals."
]
},
{
"cell_type": "markdown",
"id": "bc75e810",
"metadata": {},
"source": [
"* Get Fermionic Hamiltonian"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2c526993",
"metadata": {},
"outputs": [],
"source": [
"mh = molecule.get_molecular_hamiltonian()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "df6f82e4",
"metadata": {},
"outputs": [],
"source": [
"fh = get_fermion_operator(mh)"
]
},
{
"cell_type": "markdown",
"id": "098dfbf3",
"metadata": {},
"source": [
"The one-particle operator acts on a single particle and corresponds to one-body terms in the Hamiltonian, while the two-particle operator acts on two particles and corresponds to two-body terms in the Hamiltonian. The terms in the Hamiltonian can be represented by either fermionic or qubit operators, and the coefficients of these terms give the energy contributions from each term to the total energy of the system."
]
},
{
"cell_type": "markdown",
"id": "92085dfe",
"metadata": {},
"source": [
"The notation `(C_0^\\dagger C_1^\\dagger)(C_0 C_1)` represents a two-particle operator. It's known as the Slater determinant. The terms `C_0^\\dagger C_1^\\dagger` and `C_0 C_1` represent the creation and annihilation of two fermions, respectively. The two-particle operator as a whole represents the exchange of two fermions, one created and one annihilated.\n",
"\n",
"The notation `(C_0^\\dagger C_1^\\dagger)(C_0 C_1)` represents a two-particle operator that acts on two fermions in two different states or orbitals. The two-particle operator as a whole can be thought of as a representation of the exchange of two fermions, one created in state 1 and one annihilated in state 0. It represents the interaction between two fermions, as opposed to a single fermion.\n",
"\n",
"In quantum mechanics, the Slater determinant is a way to represent the wave function of a many-body system made up of fermions. It is antisymmetric, meaning that exchanging two fermions in the wave function leads to a sign change. This is known as the Pauli exclusion principle, which states that no two fermions can occupy the same quantum state simultaneously."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "d77ca301",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-36.019328404244526\n"
]
}
],
"source": [
"print(fh.terms[((0, 1), (0, 0))]) # coefficient of (C_0^\\dagger C_1^\\dagger)(C_0 C_1)"
]
},
{
"cell_type": "markdown",
"id": "3c9f32f8",
"metadata": {},
"source": [
"The notation `C_0^\\dagger C_0` represents the creation operator `C_0^\\dagger` acting on the state with 0 electrons, which results in a state with one electron. Here, `C_0^\\dagger` creates a particle in the \"0\" orbital, and `C_0` annihilates a particle in the \"0\" orbital. The product of these two operators gives the number of particles in the \"0\" orbital."
]
},
{
"cell_type": "markdown",
"id": "439edf27",
"metadata": {},
"source": [
"The `None` output below indicates that there is no term in the FermionOperator corresponding to the one-particle operator `C_0^\\dagger C_0`. This is likely because the Hamiltonian of the CO2 molecule is more complex than a one-particle operator and requires the use of two-particle terms to describe the interactions between the particles."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "5b84bda5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"None\n"
]
}
],
"source": [
"print(fh.terms.get(((0, 1),))) # coefficient of C_0^\\dagger C_0\n",
"#print(fh.terms[((0, 1),)]) # this returns KeyError, so use .get() instead"
]
},
{
"cell_type": "markdown",
"id": "3ef327f8",
"metadata": {},
"source": [
"This following code loops over the terms in `fh.terms` and checks if the term has two elements, which represent the creation and annihilation operators. It then checks if the first character of both elements is \"c\", which means they are creation or annihilation operators, and prints the corresponding index and coefficient of the term.\n",
"\n",
"The reason for only considering terms with one creation and one annihilation operator is because such terms correspond to a single-particle operator, which can be represented by a matrix. In many quantum chemistry problems, the Hamiltonian is expressed as a sum of these single-particle operators. The single-particle operators can be expressed as the outer product of two creation/annihilation operators, hence the constraint of only considering terms with one creation and one annihilation operator."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "b98887a6",
"metadata": {},
"outputs": [],
"source": [
"one_body_terms = fh.terms\n",
"for term, coefficient in one_body_terms.items():\n",
" if len(term) == 2:\n",
" # Only consider terms with one creation and one annihilation operator\n",
" i, j = term\n",
" if i[0] == \"c\" and j[0] == \"c\":\n",
" print(\"Term: c^\\dagger_{} c_{} Coefficient: {}\".format(i[1], j[1], coefficient))\n"
]
},
{
"cell_type": "markdown",
"id": "3603a455",
"metadata": {},
"source": [
"* Transform into qubit Hamiltonian"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "0d91ce08",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"30"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The normal transformation such as JW or BK requires 14 qubits for CO2's 30 orbitals\n",
"\n",
"a = jordan_wigner(fh)\n",
"LinearQubitOperator(a).n_qubits"
]
},
{
"cell_type": "markdown",
"id": "f9c6c84e",
"metadata": {},
"source": [
"We can use binary code to save two further qubits, as the number of spin up and spin down filling is both 13 (13/odd electrons in 15 orbitals)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "12ee73f1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"28"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b = binary_code_transform(reorder(fh, up_then_down), 2 * checksum_code(molecule_co2.n_orbitals, 1))\n",
"# molecule_co2.n_orbitals = 15\n",
"# 15 is 15 spin polarized orbitals, and 1 is for odd occupation\n",
"LinearQubitOperator(b).n_qubits"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "3dd4b878",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"12.934425376117849\n"
]
}
],
"source": [
"print(b.terms[((0, \"Z\"),)]) # coefficient of Z_0 Pauli-string"
]
},
{
"cell_type": "markdown",
"id": "4ecd89c0",
"metadata": {},
"source": [
"* Transform the qubit Hamiltonian in openfermion to the format in TensorCircuit"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "66fdc39d",
"metadata": {},
"outputs": [],
"source": [
"lsb, wb = tc.templates.chems.get_ps(b, 2*(molecule_co2.n_orbitals-1))\n",
"lsa, wa = tc.templates.chems.get_ps(a, 2*molecule_co2.n_orbitals)"
]
},
{
"cell_type": "markdown",
"id": "d8263f1a",
"metadata": {},
"source": [
"* Inspect Hamiltonian in matrix form"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "88b116e9",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2023-02-04 14:43:53.321861: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/lib\n",
"2023-02-04 14:43:53.358407: W tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:265] failed call to cuInit: UNKNOWN ERROR (303)\n",
"2023-02-04 14:43:53.358463: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (t1v-n-ff0a25ed-w-0): /proc/driver/nvidia/version does not exist\n",
"2023-02-04 14:43:53.466122: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 AVX512F FMA\n",
"To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n",
"2023-02-04 14:43:55.406891: I tensorflow/compiler/xla/service/service.cc:173] XLA service 0xfc041a0 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\n",
"2023-02-04 14:43:55.406931: I tensorflow/compiler/xla/service/service.cc:181] StreamExecutor device (0): Host, Default Version\n",
"2023-02-04 14:44:55.038456: I tensorflow/compiler/jit/xla_compilation_cache.cc:477] Compiled cluster using XLA! This line is logged at most once for the lifetime of the process.\n"
]
},
{
"ename": "",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[1;31mCanceled future for execute_request message before replies were done"
]
},
{
"ename": "",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[1;31mThe Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click <a href='https://aka.ms/vscodeJupyterKernelCrash'>here</a> for more info. View Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details."
]
}
],
"source": [
"ma = tc.quantum.PauliStringSum2COO_numpy(lsa, wa)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "63b92ae4",
"metadata": {},
"outputs": [],
"source": [
"mb = tc.quantum.PauliStringSum2COO_numpy(lsb, wb)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4f76dfbc",
"metadata": {},
"outputs": [],
"source": [
"mad, mbd = ma.todense(), mb.todense()"
]
},
{
"cell_type": "markdown",
"id": "7b43d5c1",
"metadata": {},
"source": [
"The corresponding Hartree Fock product state in these two types of Hamiltonian"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a907144e",
"metadata": {},
"outputs": [],
"source": [
"bin(np.argmin(np.diag(mad)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a56f6191",
"metadata": {},
"outputs": [],
"source": [
"bin(np.argmin(np.diag(mbd)))"
]
},
{
"cell_type": "markdown",
"id": "994d944b",
"metadata": {},
"source": [
"## VQE Setup\n",
"\n",
"We can in principle evaluate each Pauli string of the Hamiltonian as an expectation measurement, but it costs lots of simulation time, instead we fuse them as a Hamiltonian matrix as shown above to run the VQE."
]
},
{
"cell_type": "markdown",
"id": "5b0f4d11",
"metadata": {},
"source": [
"* Using dense matrix expectation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6f1e065e",
"metadata": {},
"outputs": [],
"source": [
"n = 12\n",
"depth = 4\n",
"mbd_tf = tc.array_to_tensor(mbd)\n",
"\n",
"\n",
"def vqe(param):\n",
" c = tc.Circuit(n)\n",
" for i in [0, 1, 2, 3, 4, 6, 7, 8, 9, 10]:\n",
" c.X(i)\n",
" for j in range(depth):\n",
" for i in range(n - 1):\n",
" c.exp1(i, i + 1, unitary=tc.gates._xx_matrix, theta=param[j, i, 0])\n",
" for i in range(n):\n",
" c.rx(i, theta=param[j, i, 1])\n",
" for i in range(n):\n",
" c.ry(i, theta=param[j, i, 2])\n",
" for i in range(n):\n",
" c.rx(i, theta=param[j, i, 3])\n",
" return tc.templates.measurements.operator_expectation(c, mbd_tf)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "58fee777",
"metadata": {},
"outputs": [],
"source": [
"vags = tc.backend.jit(tc.backend.value_and_grad(vqe))\n",
"lr = tf.keras.optimizers.schedules.ExponentialDecay(\n",
" decay_rate=0.5, decay_steps=300, initial_learning_rate=0.5e-2\n",
")\n",
"opt = tc.backend.optimizer(tf.keras.optimizers.Adam(lr))\n",
"\n",
"param = tc.backend.implicit_randn(shape=[depth, n, 4], stddev=0.02, dtype=\"float32\")\n",
"for i in range(600):\n",
" e, g = vags(param)\n",
" param = opt.update(g, param)\n",
" if i % 100 == 0:\n",
" print(e)"
]
},
{
"cell_type": "markdown",
"id": "94918b36",
"metadata": {},
"source": [
"* Using sparse matrix expectation\n",
"\n",
"We can also use the sparse Hamiltonian matrix for circuit expectation evaluation, the only difference is to replace ``mbd_tf`` with ``mb_tf``"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "721f4494",
"metadata": {},
"outputs": [],
"source": [
"mb_tf = tc.backend.coo_sparse_matrix(\n",
" np.transpose(np.stack([mb.row, mb.col])), mb.data, shape=(2**n, 2**n)\n",
")"
]
},
{
"cell_type": "markdown",
"id": "d8847fe3",
"metadata": {},
"source": [
"A micro-benchmark between sparse matrix evaluation and dense matrix evaluation for expectation in terms of time, sparse always wins in terms of space, of course."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8a60c6c7",
"metadata": {},
"outputs": [],
"source": [
"def dense_expt(param):\n",
" c = tc.Circuit(n)\n",
" for i in range(n):\n",
" c.H(i)\n",
" c.rx(i, theta=param[i])\n",
" return tc.templates.measurements.operator_expectation(c, mbd_tf)\n",
"\n",
"\n",
"def sparse_expt(param):\n",
" c = tc.Circuit(n)\n",
" for i in range(n):\n",
" c.H(i)\n",
" c.rx(i, theta=param[i])\n",
" return tc.templates.measurements.operator_expectation(c, mb_tf)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2764bbca",
"metadata": {},
"outputs": [],
"source": [
"dense_vag = tc.backend.jit(tc.backend.value_and_grad(dense_expt))\n",
"sparse_vag = tc.backend.jit(tc.backend.value_and_grad(sparse_expt))\n",
"\n",
"v0, g0 = dense_vag(tc.backend.ones([n]))\n",
"v1, g1 = sparse_vag(tc.backend.ones([n]))\n",
"\n",
"# consistency check\n",
"\n",
"np.testing.assert_allclose(v0, v1, atol=1e-5)\n",
"np.testing.assert_allclose(g0, g1, atol=1e-5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bc0265ee",
"metadata": {},
"outputs": [],
"source": [
"%timeit dense_vag(tc.backend.ones([n]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8913d16d",
"metadata": {},
"outputs": [],
"source": [
"%timeit sparse_vag(tc.backend.ones([n]))"
]
},
{
"cell_type": "markdown",
"id": "3eafaa67",
"metadata": {},
"source": [
"Therefore, sparse matrix evaluation also saves time apart from space, which is always recommended."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.8.10 64-bit",
"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.10"
},
"vscode": {
"interpreter": {
"hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment