Skip to content

Instantly share code, notes, and snippets.

@buttercutter
Created July 3, 2023 03:45
Show Gist options
  • Save buttercutter/1f71664cff2b16d5e881dc06e32a3fda to your computer and use it in GitHub Desktop.
Save buttercutter/1f71664cff2b16d5e881dc06e32a3fda to your computer and use it in GitHub Desktop.
Code refactoring / migration for qiskit OO-VQE
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "e521af8c",
"metadata": {
"id": "e521af8c"
},
"source": [
"Credit: https://github.com/tencent-quantum-lab/tensorcircuit/issues/120"
]
},
{
"cell_type": "markdown",
"id": "4009fbd4",
"metadata": {
"id": "4009fbd4"
},
"source": [
"# Variational Quantum Eigensolver (VQE) on Molecules"
]
},
{
"cell_type": "markdown",
"id": "d0b04a30",
"metadata": {
"tags": [],
"id": "d0b04a30"
},
"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": {
"id": "12886697"
},
"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": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"id": "68bfb967",
"outputId": "3634d663-92c6-43b3-bafb-4d14c18e1590"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Collecting openfermion\n",
" Downloading openfermion-1.5.1-py3-none-any.whl (1.1 MB)\n",
"\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/1.1 MB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m40.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting openfermionpyscf\n",
" Downloading openfermionpyscf-0.5-py3-none-any.whl (16 kB)\n",
"Requirement already satisfied: tensorflow in /usr/local/lib/python3.10/dist-packages (2.12.0)\n",
"Collecting tensorcircuit\n",
" Downloading tensorcircuit-0.10.0-py3-none-any.whl (311 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m311.2/311.2 kB\u001b[0m \u001b[31m26.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting qiskit-nature\n",
" Downloading qiskit_nature-0.6.2-py3-none-any.whl (4.1 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.1/4.1 MB\u001b[0m \u001b[31m95.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting cirq-core>=0.15.0 (from openfermion)\n",
" Downloading cirq_core-1.1.0-py3-none-any.whl (1.8 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.8/1.8 MB\u001b[0m \u001b[31m88.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting cirq-google>=0.15.0 (from openfermion)\n",
" Downloading cirq_google-1.1.0-py3-none-any.whl (577 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m577.4/577.4 kB\u001b[0m \u001b[31m45.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting deprecation (from openfermion)\n",
" Downloading deprecation-2.1.0-py2.py3-none-any.whl (11 kB)\n",
"Requirement already satisfied: h5py>=2.8 in /usr/local/lib/python3.10/dist-packages (from openfermion) (3.8.0)\n",
"Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from openfermion) (3.1)\n",
"Requirement already satisfied: numpy>=1.11.0 in /usr/local/lib/python3.10/dist-packages (from openfermion) (1.22.4)\n",
"Collecting pubchempy (from openfermion)\n",
" Downloading PubChemPy-1.0.4.tar.gz (29 kB)\n",
" Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
"Requirement already satisfied: requests>=2.18 in /usr/local/lib/python3.10/dist-packages (from openfermion) (2.27.1)\n",
"Requirement already satisfied: scipy>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from openfermion) (1.10.1)\n",
"Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from openfermion) (1.11.1)\n",
"Collecting pyscf (from openfermionpyscf)\n",
" Downloading pyscf-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (47.7 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m47.7/47.7 MB\u001b[0m \u001b[31m19.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: pytest in /usr/local/lib/python3.10/dist-packages (from openfermionpyscf) (7.2.2)\n",
"Requirement already satisfied: absl-py>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (1.4.0)\n",
"Requirement already satisfied: astunparse>=1.6.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (1.6.3)\n",
"Requirement already satisfied: flatbuffers>=2.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (23.5.26)\n",
"Requirement already satisfied: gast<=0.4.0,>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (0.4.0)\n",
"Requirement already satisfied: google-pasta>=0.1.1 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (0.2.0)\n",
"Requirement already satisfied: grpcio<2.0,>=1.24.3 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (1.56.0)\n",
"Requirement already satisfied: jax>=0.3.15 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (0.4.10)\n",
"Requirement already satisfied: keras<2.13,>=2.12.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (2.12.0)\n",
"Requirement already satisfied: libclang>=13.0.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (16.0.0)\n",
"Requirement already satisfied: opt-einsum>=2.3.2 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (3.3.0)\n",
"Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from tensorflow) (23.1)\n",
"Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.3 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (3.20.3)\n",
"Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from tensorflow) (67.7.2)\n",
"Requirement already satisfied: six>=1.12.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (1.16.0)\n",
"Requirement already satisfied: tensorboard<2.13,>=2.12 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (2.12.3)\n",
"Requirement already satisfied: tensorflow-estimator<2.13,>=2.12.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (2.12.0)\n",
"Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (2.3.0)\n",
"Requirement already satisfied: typing-extensions>=3.6.6 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (4.6.3)\n",
"Requirement already satisfied: wrapt<1.15,>=1.11.0 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (1.14.1)\n",
"Requirement already satisfied: tensorflow-io-gcs-filesystem>=0.23.1 in /usr/local/lib/python3.10/dist-packages (from tensorflow) (0.32.0)\n",
"Collecting tensornetwork (from tensorcircuit)\n",
" Downloading tensornetwork-0.4.6-py3-none-any.whl (364 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m364.3/364.3 kB\u001b[0m \u001b[31m41.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting qiskit-terra>=0.24 (from qiskit-nature)\n",
" Downloading qiskit_terra-0.24.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.9/5.9 MB\u001b[0m \u001b[31m108.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: psutil>=5 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature) (5.9.5)\n",
"Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature) (1.2.2)\n",
"Collecting rustworkx (from qiskit-nature)\n",
" Downloading rustworkx-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.9/1.9 MB\u001b[0m \u001b[31m98.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from astunparse>=1.6.0->tensorflow) (0.40.0)\n",
"Collecting duet~=0.2.7 (from cirq-core>=0.15.0->openfermion)\n",
" Downloading duet-0.2.8-py3-none-any.whl (29 kB)\n",
"Requirement already satisfied: matplotlib~=3.0 in /usr/local/lib/python3.10/dist-packages (from cirq-core>=0.15.0->openfermion) (3.7.1)\n",
"Collecting networkx (from openfermion)\n",
" Downloading networkx-2.8.8-py3-none-any.whl (2.0 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.0/2.0 MB\u001b[0m \u001b[31m110.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from cirq-core>=0.15.0->openfermion) (1.5.3)\n",
"Requirement already satisfied: sortedcontainers~=2.0 in /usr/local/lib/python3.10/dist-packages (from cirq-core>=0.15.0->openfermion) (2.4.0)\n",
"Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from cirq-core>=0.15.0->openfermion) (4.65.0)\n",
"Collecting google-api-core[grpc]<2.0.0dev,>=1.14.0 (from cirq-google>=0.15.0->openfermion)\n",
" Downloading google_api_core-1.34.0-py3-none-any.whl (120 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m120.2/120.2 kB\u001b[0m \u001b[31m16.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: proto-plus>=1.20.0 in /usr/local/lib/python3.10/dist-packages (from cirq-google>=0.15.0->openfermion) (1.22.3)\n",
"Requirement already satisfied: ml-dtypes>=0.1.0 in /usr/local/lib/python3.10/dist-packages (from jax>=0.3.15->tensorflow) (0.2.0)\n",
"Collecting ply>=3.10 (from qiskit-terra>=0.24->qiskit-nature)\n",
" Downloading ply-3.11-py2.py3-none-any.whl (49 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.6/49.6 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting dill>=0.3 (from qiskit-terra>=0.24->qiskit-nature)\n",
" Downloading dill-0.3.6-py3-none-any.whl (110 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m110.5/110.5 kB\u001b[0m \u001b[31m13.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature) (2.8.2)\n",
"Collecting stevedore>=3.0.0 (from qiskit-terra>=0.24->qiskit-nature)\n",
" Downloading stevedore-5.1.0-py3-none-any.whl (49 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.6/49.6 kB\u001b[0m \u001b[31m5.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting symengine<0.10,>=0.9 (from qiskit-terra>=0.24->qiskit-nature)\n",
" Downloading symengine-0.9.2-cp310-cp310-manylinux2010_x86_64.whl (37.5 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m37.5/37.5 MB\u001b[0m \u001b[31m17.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18->openfermion) (1.26.16)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18->openfermion) (2023.5.7)\n",
"Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18->openfermion) (2.0.12)\n",
"Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18->openfermion) (3.4)\n",
"Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->qiskit-nature) (1.2.0)\n",
"Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->qiskit-nature) (3.1.0)\n",
"Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy->openfermion) (1.3.0)\n",
"Requirement already satisfied: google-auth<3,>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from tensorboard<2.13,>=2.12->tensorflow) (2.17.3)\n",
"Requirement already satisfied: google-auth-oauthlib<1.1,>=0.5 in /usr/local/lib/python3.10/dist-packages (from tensorboard<2.13,>=2.12->tensorflow) (1.0.0)\n",
"Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.10/dist-packages (from tensorboard<2.13,>=2.12->tensorflow) (3.4.3)\n",
"Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from tensorboard<2.13,>=2.12->tensorflow) (0.7.1)\n",
"Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from tensorboard<2.13,>=2.12->tensorflow) (2.3.6)\n",
"Requirement already satisfied: attrs>=19.2.0 in /usr/local/lib/python3.10/dist-packages (from pytest->openfermionpyscf) (23.1.0)\n",
"Requirement already satisfied: iniconfig in /usr/local/lib/python3.10/dist-packages (from pytest->openfermionpyscf) (2.0.0)\n",
"Requirement already satisfied: pluggy<2.0,>=0.12 in /usr/local/lib/python3.10/dist-packages (from pytest->openfermionpyscf) (1.2.0)\n",
"Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /usr/local/lib/python3.10/dist-packages (from pytest->openfermionpyscf) (1.1.1)\n",
"Requirement already satisfied: tomli>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from pytest->openfermionpyscf) (2.0.1)\n",
"Requirement already satisfied: graphviz>=0.11.1 in /usr/local/lib/python3.10/dist-packages (from tensornetwork->tensorcircuit) (0.20.1)\n",
"Requirement already satisfied: googleapis-common-protos<2.0dev,>=1.56.2 in /usr/local/lib/python3.10/dist-packages (from google-api-core[grpc]<2.0.0dev,>=1.14.0->cirq-google>=0.15.0->openfermion) (1.59.1)\n",
"Requirement already satisfied: grpcio-status<2.0dev,>=1.33.2 in /usr/local/lib/python3.10/dist-packages (from google-api-core[grpc]<2.0.0dev,>=1.14.0->cirq-google>=0.15.0->openfermion) (1.48.2)\n",
"Requirement already satisfied: cachetools<6.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.13,>=2.12->tensorflow) (5.3.1)\n",
"Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.13,>=2.12->tensorflow) (0.3.0)\n",
"Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.10/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.13,>=2.12->tensorflow) (4.9)\n",
"Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from google-auth-oauthlib<1.1,>=0.5->tensorboard<2.13,>=2.12->tensorflow) (1.3.1)\n",
"Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (1.1.0)\n",
"Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (0.11.0)\n",
"Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (4.40.0)\n",
"Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (1.4.4)\n",
"Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (8.4.0)\n",
"Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->cirq-core>=0.15.0->openfermion) (3.1.0)\n",
"Collecting pbr!=2.1.0,>=2.0.0 (from stevedore>=3.0.0->qiskit-terra>=0.24->qiskit-nature)\n",
" Downloading pbr-5.11.1-py2.py3-none-any.whl (112 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m112.7/112.7 kB\u001b[0m \u001b[31m15.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: MarkupSafe>=2.1.1 in /usr/local/lib/python3.10/dist-packages (from werkzeug>=1.0.1->tensorboard<2.13,>=2.12->tensorflow) (2.1.3)\n",
"Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->cirq-core>=0.15.0->openfermion) (2022.7.1)\n",
"Requirement already satisfied: pyasn1<0.6.0,>=0.4.6 in /usr/local/lib/python3.10/dist-packages (from pyasn1-modules>=0.2.1->google-auth<3,>=1.6.3->tensorboard<2.13,>=2.12->tensorflow) (0.5.0)\n",
"Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<1.1,>=0.5->tensorboard<2.13,>=2.12->tensorflow) (3.2.2)\n",
"Building wheels for collected packages: pubchempy\n",
" Building wheel for pubchempy (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
" Created wheel for pubchempy: filename=PubChemPy-1.0.4-py3-none-any.whl size=13820 sha256=839de53c6833aac8d7317050d76b802c1e1e02553b238c8657bc43e6075bdaa0\n",
" Stored in directory: /root/.cache/pip/wheels/90/7c/45/18a0671e3c3316966ef7ed9ad2b3f3300a7e41d3421a44e799\n",
"Successfully built pubchempy\n",
"Installing collected packages: pubchempy, ply, symengine, rustworkx, pbr, networkx, duet, dill, deprecation, tensornetwork, stevedore, pyscf, tensorcircuit, qiskit-terra, google-api-core, cirq-core, qiskit-nature, cirq-google, openfermion, openfermionpyscf\n",
" Attempting uninstall: networkx\n",
" Found existing installation: networkx 3.1\n",
" Uninstalling networkx-3.1:\n",
" Successfully uninstalled networkx-3.1\n",
" Attempting uninstall: google-api-core\n",
" Found existing installation: google-api-core 2.11.1\n",
" Uninstalling google-api-core-2.11.1:\n",
" Successfully uninstalled google-api-core-2.11.1\n",
"Successfully installed cirq-core-1.1.0 cirq-google-1.1.0 deprecation-2.1.0 dill-0.3.6 duet-0.2.8 google-api-core-1.34.0 networkx-2.8.8 openfermion-1.5.1 openfermionpyscf-0.5 pbr-5.11.1 ply-3.11 pubchempy-1.0.4 pyscf-2.2.1 qiskit-nature-0.6.2 qiskit-terra-0.24.1 rustworkx-0.13.0 stevedore-5.1.0 symengine-0.9.2 tensorcircuit-0.10.0 tensornetwork-0.4.6\n"
]
},
{
"output_type": "display_data",
"data": {
"application/vnd.colab-display-data+json": {
"pip_warning": {
"packages": [
"google"
]
}
}
},
"metadata": {}
}
],
"source": [
"!pip3 install --upgrade openfermion openfermionpyscf tensorflow tensorcircuit qiskit-nature"
]
},
{
"cell_type": "code",
"source": [
"!git clone https://github.com/qiskit-community/qiskit-nature\n",
"!pip3 install ./qiskit-nature"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "hcfjRk4eHTmr",
"outputId": "fbf81a45-3373-4d9c-8134-1940bf4d08f9"
},
"id": "hcfjRk4eHTmr",
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Cloning into 'qiskit-nature'...\n",
"remote: Enumerating objects: 17238, done.\u001b[K\n",
"remote: Counting objects: 100% (791/791), done.\u001b[K\n",
"remote: Compressing objects: 100% (435/435), done.\u001b[K\n",
"remote: Total 17238 (delta 428), reused 665 (delta 348), pack-reused 16447\u001b[K\n",
"Receiving objects: 100% (17238/17238), 11.14 MiB | 29.26 MiB/s, done.\n",
"Resolving deltas: 100% (11306/11306), done.\n",
"Processing ./qiskit-nature\n",
" Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n",
" Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n",
" Installing backend dependencies ... \u001b[?25l\u001b[?25hdone\n",
" Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
"Requirement already satisfied: qiskit-terra>=0.24 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (0.24.1)\n",
"Requirement already satisfied: scipy>=1.4 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (1.10.1)\n",
"Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (1.22.4)\n",
"Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (5.9.5)\n",
"Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (67.7.2)\n",
"Requirement already satisfied: typing-extensions in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (4.6.3)\n",
"Requirement already satisfied: h5py in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (3.8.0)\n",
"Requirement already satisfied: rustworkx in /usr/local/lib/python3.10/dist-packages (from qiskit-nature==0.7.0) (0.13.0)\n",
"Requirement already satisfied: ply>=3.10 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature==0.7.0) (3.11)\n",
"Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature==0.7.0) (1.11.1)\n",
"Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature==0.7.0) (0.3.6)\n",
"Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature==0.7.0) (2.8.2)\n",
"Requirement already satisfied: stevedore>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature==0.7.0) (5.1.0)\n",
"Requirement already satisfied: symengine<0.10,>=0.9 in /usr/local/lib/python3.10/dist-packages (from qiskit-terra>=0.24->qiskit-nature==0.7.0) (0.9.2)\n",
"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.0->qiskit-terra>=0.24->qiskit-nature==0.7.0) (1.16.0)\n",
"Requirement already satisfied: pbr!=2.1.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from stevedore>=3.0.0->qiskit-terra>=0.24->qiskit-nature==0.7.0) (5.11.1)\n",
"Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy>=1.3->qiskit-terra>=0.24->qiskit-nature==0.7.0) (1.3.0)\n",
"Building wheels for collected packages: qiskit-nature\n",
" Building wheel for qiskit-nature (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
" Created wheel for qiskit-nature: filename=qiskit_nature-0.7.0-py3-none-any.whl size=2184694 sha256=216419edff6386dd7b036c655e8be76529ad78758cb188fec491c9b88921a473\n",
" Stored in directory: /root/.cache/pip/wheels/37/88/3c/d5c8e2cb93ca89981de0e9821c9eb886dde6f3e7c256756ef0\n",
"Successfully built qiskit-nature\n",
"Installing collected packages: qiskit-nature\n",
" Attempting uninstall: qiskit-nature\n",
" Found existing installation: qiskit-nature 0.6.2\n",
" Uninstalling qiskit-nature-0.6.2:\n",
" Successfully uninstalled qiskit-nature-0.6.2\n",
"Successfully installed qiskit-nature-0.7.0\n"
]
}
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "9e28c969",
"metadata": {
"id": "9e28c969"
},
"outputs": [],
"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": {
"id": "8464f4f5"
},
"source": [
"## Generate Hamiltonian"
]
},
{
"cell_type": "markdown",
"id": "b6677497",
"metadata": {
"id": "b6677497"
},
"source": [
"* Get molecule energy info and molecule orbitals"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "40574813",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "40574813",
"outputId": "159e47d7-3867-47ab-f94e-f38558a6a5ff"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"-185.29256604671025 -185.26927762412345 -185.06803107659124\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": "426b0550",
"metadata": {
"id": "426b0550"
},
"source": [
"* Find the number of spin orbitals for H2O and CO2"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "23bbfd98",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "23bbfd98",
"outputId": "63b502bc-3da0-4265-9f15-95f8d3371a79"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"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": {
"id": "6da70c9b"
},
"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": {
"id": "bc75e810"
},
"source": [
"* Get Fermionic Hamiltonian"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "2c526993",
"metadata": {
"id": "2c526993"
},
"outputs": [],
"source": [
"mh = molecule.get_molecular_hamiltonian()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "df6f82e4",
"metadata": {
"id": "df6f82e4"
},
"outputs": [],
"source": [
"fh = get_fermion_operator(mh)"
]
},
{
"cell_type": "markdown",
"id": "098dfbf3",
"metadata": {
"id": "098dfbf3"
},
"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": {
"id": "92085dfe"
},
"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": 8,
"id": "d77ca301",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "d77ca301",
"outputId": "f75917e5-7e83-42f1-9ebc-cc134cb8d0b2"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"-36.019328404700516\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": {
"id": "3c9f32f8"
},
"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": {
"id": "439edf27"
},
"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": 9,
"id": "5b84bda5",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "5b84bda5",
"outputId": "e4388e54-e56b-4f99-b380-b260a2e4ba1c"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"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": {
"id": "3ef327f8"
},
"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": 10,
"id": "b98887a6",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "b98887a6",
"outputId": "ad39e85a-a64a-4464-ae20-4cee082dbb78"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Term: c^\\dagger_0 c_0 Coefficient: -36.019328404700516\n",
"Term: c^\\dagger_0 c_8 Coefficient: -0.533428752513566\n",
"Term: c^\\dagger_0 c_16 Coefficient: -0.3148135502894871\n",
"Term: c^\\dagger_0 c_28 Coefficient: 0.24049574169484553\n",
"Term: c^\\dagger_1 c_1 Coefficient: -36.019328404700516\n",
"Term: c^\\dagger_1 c_9 Coefficient: -0.533428752513566\n",
"Term: c^\\dagger_1 c_17 Coefficient: -0.3148135502894871\n",
"Term: c^\\dagger_1 c_29 Coefficient: 0.24049574169484553\n",
"Term: c^\\dagger_2 c_2 Coefficient: -36.01976262280015\n",
"Term: c^\\dagger_2 c_4 Coefficient: 0.0002660340209845837\n",
"Term: c^\\dagger_2 c_6 Coefficient: 0.48949010756776407\n",
"Term: c^\\dagger_2 c_10 Coefficient: -0.3955819063845483\n",
"Term: c^\\dagger_2 c_26 Coefficient: -0.18321309390773735\n",
"Term: c^\\dagger_3 c_3 Coefficient: -36.01976262280015\n",
"Term: c^\\dagger_3 c_5 Coefficient: 0.0002660340209845837\n",
"Term: c^\\dagger_3 c_7 Coefficient: 0.48949010756776407\n",
"Term: c^\\dagger_3 c_11 Coefficient: -0.3955819063845483\n",
"Term: c^\\dagger_3 c_27 Coefficient: -0.18321309390773735\n",
"Term: c^\\dagger_4 c_2 Coefficient: 0.0002660340209845932\n",
"Term: c^\\dagger_4 c_4 Coefficient: -24.820154633175814\n",
"Term: c^\\dagger_4 c_6 Coefficient: 0.2750772382412751\n",
"Term: c^\\dagger_4 c_10 Coefficient: 0.2656275612238066\n",
"Term: c^\\dagger_4 c_26 Coefficient: 0.3097256992210586\n",
"Term: c^\\dagger_5 c_3 Coefficient: 0.0002660340209845932\n",
"Term: c^\\dagger_5 c_5 Coefficient: -24.820154633175814\n",
"Term: c^\\dagger_5 c_7 Coefficient: 0.2750772382412751\n",
"Term: c^\\dagger_5 c_11 Coefficient: 0.2656275612238066\n",
"Term: c^\\dagger_5 c_27 Coefficient: 0.3097256992210586\n",
"Term: c^\\dagger_6 c_2 Coefficient: 0.48949010756776357\n",
"Term: c^\\dagger_6 c_4 Coefficient: 0.2750772382412751\n",
"Term: c^\\dagger_6 c_6 Coefficient: -11.667794289240259\n",
"Term: c^\\dagger_6 c_10 Coefficient: -0.26750004537504346\n",
"Term: c^\\dagger_6 c_26 Coefficient: 0.9657018575993381\n",
"Term: c^\\dagger_7 c_3 Coefficient: 0.48949010756776357\n",
"Term: c^\\dagger_7 c_5 Coefficient: 0.2750772382412751\n",
"Term: c^\\dagger_7 c_7 Coefficient: -11.667794289240259\n",
"Term: c^\\dagger_7 c_11 Coefficient: -0.26750004537504346\n",
"Term: c^\\dagger_7 c_27 Coefficient: 0.9657018575993381\n",
"Term: c^\\dagger_8 c_0 Coefficient: -0.5334287525135648\n",
"Term: c^\\dagger_8 c_8 Coefficient: -11.745656147317309\n",
"Term: c^\\dagger_8 c_16 Coefficient: 0.6296419147388894\n",
"Term: c^\\dagger_8 c_28 Coefficient: 0.5837938061192638\n",
"Term: c^\\dagger_9 c_1 Coefficient: -0.5334287525135648\n",
"Term: c^\\dagger_9 c_9 Coefficient: -11.745656147317309\n",
"Term: c^\\dagger_9 c_17 Coefficient: 0.6296419147388894\n",
"Term: c^\\dagger_9 c_29 Coefficient: 0.5837938061192638\n",
"Term: c^\\dagger_10 c_2 Coefficient: -0.395581906384548\n",
"Term: c^\\dagger_10 c_4 Coefficient: 0.2656275612238064\n",
"Term: c^\\dagger_10 c_6 Coefficient: -0.2675000453750438\n",
"Term: c^\\dagger_10 c_10 Coefficient: -10.429408982309605\n",
"Term: c^\\dagger_10 c_26 Coefficient: 0.07597946676519926\n",
"Term: c^\\dagger_11 c_3 Coefficient: -0.395581906384548\n",
"Term: c^\\dagger_11 c_5 Coefficient: 0.2656275612238064\n",
"Term: c^\\dagger_11 c_7 Coefficient: -0.2675000453750438\n",
"Term: c^\\dagger_11 c_11 Coefficient: -10.429408982309605\n",
"Term: c^\\dagger_11 c_27 Coefficient: 0.07597946676519926\n",
"Term: c^\\dagger_12 c_12 Coefficient: -10.442114706289681\n",
"Term: c^\\dagger_12 c_22 Coefficient: 0.32675775092663323\n",
"Term: c^\\dagger_12 c_24 Coefficient: -0.03327309602237639\n",
"Term: c^\\dagger_13 c_13 Coefficient: -10.442114706289681\n",
"Term: c^\\dagger_13 c_23 Coefficient: 0.32675775092663323\n",
"Term: c^\\dagger_13 c_25 Coefficient: -0.03327309602237639\n",
"Term: c^\\dagger_14 c_14 Coefficient: -10.442114706289681\n",
"Term: c^\\dagger_14 c_22 Coefficient: 0.03327309602237596\n",
"Term: c^\\dagger_14 c_24 Coefficient: 0.32675775092663456\n",
"Term: c^\\dagger_15 c_15 Coefficient: -10.442114706289681\n",
"Term: c^\\dagger_15 c_23 Coefficient: 0.03327309602237596\n",
"Term: c^\\dagger_15 c_25 Coefficient: 0.32675775092663456\n",
"Term: c^\\dagger_16 c_0 Coefficient: -0.31481355028948715\n",
"Term: c^\\dagger_16 c_8 Coefficient: 0.629641914738889\n",
"Term: c^\\dagger_16 c_16 Coefficient: -10.668936174118013\n",
"Term: c^\\dagger_16 c_28 Coefficient: 0.13194954967145298\n",
"Term: c^\\dagger_17 c_1 Coefficient: -0.31481355028948715\n",
"Term: c^\\dagger_17 c_9 Coefficient: 0.629641914738889\n",
"Term: c^\\dagger_17 c_17 Coefficient: -10.668936174118013\n",
"Term: c^\\dagger_17 c_29 Coefficient: 0.13194954967145298\n",
"Term: c^\\dagger_18 c_18 Coefficient: -10.689564730643733\n",
"Term: c^\\dagger_19 c_19 Coefficient: -10.689564730643733\n",
"Term: c^\\dagger_20 c_20 Coefficient: -10.689564730643733\n",
"Term: c^\\dagger_21 c_21 Coefficient: -10.689564730643733\n",
"Term: c^\\dagger_22 c_12 Coefficient: 0.326757750926632\n",
"Term: c^\\dagger_22 c_14 Coefficient: 0.033273096022375895\n",
"Term: c^\\dagger_22 c_22 Coefficient: -10.292589815408641\n",
"Term: c^\\dagger_23 c_13 Coefficient: 0.326757750926632\n",
"Term: c^\\dagger_23 c_15 Coefficient: 0.033273096022375895\n",
"Term: c^\\dagger_23 c_23 Coefficient: -10.292589815408641\n",
"Term: c^\\dagger_24 c_12 Coefficient: -0.033273096022376374\n",
"Term: c^\\dagger_24 c_14 Coefficient: 0.3267577509266348\n",
"Term: c^\\dagger_24 c_24 Coefficient: -10.292589815408652\n",
"Term: c^\\dagger_25 c_13 Coefficient: -0.033273096022376374\n",
"Term: c^\\dagger_25 c_15 Coefficient: 0.3267577509266348\n",
"Term: c^\\dagger_25 c_25 Coefficient: -10.292589815408652\n",
"Term: c^\\dagger_26 c_2 Coefficient: -0.18321309390773752\n",
"Term: c^\\dagger_26 c_4 Coefficient: 0.30972569922105775\n",
"Term: c^\\dagger_26 c_6 Coefficient: 0.9657018575993387\n",
"Term: c^\\dagger_26 c_10 Coefficient: 0.07597946676520091\n",
"Term: c^\\dagger_26 c_26 Coefficient: -10.682274747688648\n",
"Term: c^\\dagger_27 c_3 Coefficient: -0.18321309390773752\n",
"Term: c^\\dagger_27 c_5 Coefficient: 0.30972569922105775\n",
"Term: c^\\dagger_27 c_7 Coefficient: 0.9657018575993387\n",
"Term: c^\\dagger_27 c_11 Coefficient: 0.07597946676520091\n",
"Term: c^\\dagger_27 c_27 Coefficient: -10.682274747688648\n",
"Term: c^\\dagger_28 c_0 Coefficient: 0.24049574169484692\n",
"Term: c^\\dagger_28 c_8 Coefficient: 0.5837938061192661\n",
"Term: c^\\dagger_28 c_16 Coefficient: 0.13194954967145156\n",
"Term: c^\\dagger_28 c_28 Coefficient: -10.92425561369221\n",
"Term: c^\\dagger_29 c_1 Coefficient: 0.24049574169484692\n",
"Term: c^\\dagger_29 c_9 Coefficient: 0.5837938061192661\n",
"Term: c^\\dagger_29 c_17 Coefficient: 0.13194954967145156\n",
"Term: c^\\dagger_29 c_29 Coefficient: -10.92425561369221\n"
]
}
],
"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",
" print(\"Term: c^\\dagger_{} c_{} Coefficient: {}\".format(i[0], j[0], coefficient))\n"
]
},
{
"cell_type": "markdown",
"id": "62f4ad3c-fe56-475a-b974-045ef88bed82",
"metadata": {
"id": "62f4ad3c-fe56-475a-b974-045ef88bed82"
},
"source": [
"To extract the creation and annihilation operators separately from the terms in the FermionOperator, use the following code.\n",
"\n",
"after extracting the term and coefficient from the one_body_terms dictionary, the creation and annihilation operators are constructed using the indices i[0] and j[0], respectively.\n",
"\n",
"The creation operator is represented as \"c^\\u2020\" (Unicode character for dagger) followed by the index, and the annihilation operator is represented as \"c_\" followed by the index.\n",
"\n",
"These operators are then printed along with the corresponding coefficient.\n",
"\n",
"Please note that the creation and annihilation operators are represented as strings in this code snippet."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "6c63f924-74a0-4da1-93e9-5f511fcf1fea",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "6c63f924-74a0-4da1-93e9-5f511fcf1fea",
"outputId": "dd5cd2e6-1b5b-46f8-965c-340585a63282"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Term: c^†0 c_0 Coefficient: -36.019328404700516\n",
"Term: c^†0 c_8 Coefficient: -0.533428752513566\n",
"Term: c^†0 c_16 Coefficient: -0.3148135502894871\n",
"Term: c^†0 c_28 Coefficient: 0.24049574169484553\n",
"Term: c^†1 c_1 Coefficient: -36.019328404700516\n",
"Term: c^†1 c_9 Coefficient: -0.533428752513566\n",
"Term: c^†1 c_17 Coefficient: -0.3148135502894871\n",
"Term: c^†1 c_29 Coefficient: 0.24049574169484553\n",
"Term: c^†2 c_2 Coefficient: -36.01976262280015\n",
"Term: c^†2 c_4 Coefficient: 0.0002660340209845837\n",
"Term: c^†2 c_6 Coefficient: 0.48949010756776407\n",
"Term: c^†2 c_10 Coefficient: -0.3955819063845483\n",
"Term: c^†2 c_26 Coefficient: -0.18321309390773735\n",
"Term: c^†3 c_3 Coefficient: -36.01976262280015\n",
"Term: c^†3 c_5 Coefficient: 0.0002660340209845837\n",
"Term: c^†3 c_7 Coefficient: 0.48949010756776407\n",
"Term: c^†3 c_11 Coefficient: -0.3955819063845483\n",
"Term: c^†3 c_27 Coefficient: -0.18321309390773735\n",
"Term: c^†4 c_2 Coefficient: 0.0002660340209845932\n",
"Term: c^†4 c_4 Coefficient: -24.820154633175814\n",
"Term: c^†4 c_6 Coefficient: 0.2750772382412751\n",
"Term: c^†4 c_10 Coefficient: 0.2656275612238066\n",
"Term: c^†4 c_26 Coefficient: 0.3097256992210586\n",
"Term: c^†5 c_3 Coefficient: 0.0002660340209845932\n",
"Term: c^†5 c_5 Coefficient: -24.820154633175814\n",
"Term: c^†5 c_7 Coefficient: 0.2750772382412751\n",
"Term: c^†5 c_11 Coefficient: 0.2656275612238066\n",
"Term: c^†5 c_27 Coefficient: 0.3097256992210586\n",
"Term: c^†6 c_2 Coefficient: 0.48949010756776357\n",
"Term: c^†6 c_4 Coefficient: 0.2750772382412751\n",
"Term: c^†6 c_6 Coefficient: -11.667794289240259\n",
"Term: c^†6 c_10 Coefficient: -0.26750004537504346\n",
"Term: c^†6 c_26 Coefficient: 0.9657018575993381\n",
"Term: c^†7 c_3 Coefficient: 0.48949010756776357\n",
"Term: c^†7 c_5 Coefficient: 0.2750772382412751\n",
"Term: c^†7 c_7 Coefficient: -11.667794289240259\n",
"Term: c^†7 c_11 Coefficient: -0.26750004537504346\n",
"Term: c^†7 c_27 Coefficient: 0.9657018575993381\n",
"Term: c^†8 c_0 Coefficient: -0.5334287525135648\n",
"Term: c^†8 c_8 Coefficient: -11.745656147317309\n",
"Term: c^†8 c_16 Coefficient: 0.6296419147388894\n",
"Term: c^†8 c_28 Coefficient: 0.5837938061192638\n",
"Term: c^†9 c_1 Coefficient: -0.5334287525135648\n",
"Term: c^†9 c_9 Coefficient: -11.745656147317309\n",
"Term: c^†9 c_17 Coefficient: 0.6296419147388894\n",
"Term: c^†9 c_29 Coefficient: 0.5837938061192638\n",
"Term: c^†10 c_2 Coefficient: -0.395581906384548\n",
"Term: c^†10 c_4 Coefficient: 0.2656275612238064\n",
"Term: c^†10 c_6 Coefficient: -0.2675000453750438\n",
"Term: c^†10 c_10 Coefficient: -10.429408982309605\n",
"Term: c^†10 c_26 Coefficient: 0.07597946676519926\n",
"Term: c^†11 c_3 Coefficient: -0.395581906384548\n",
"Term: c^†11 c_5 Coefficient: 0.2656275612238064\n",
"Term: c^†11 c_7 Coefficient: -0.2675000453750438\n",
"Term: c^†11 c_11 Coefficient: -10.429408982309605\n",
"Term: c^†11 c_27 Coefficient: 0.07597946676519926\n",
"Term: c^†12 c_12 Coefficient: -10.442114706289681\n",
"Term: c^†12 c_22 Coefficient: 0.32675775092663323\n",
"Term: c^†12 c_24 Coefficient: -0.03327309602237639\n",
"Term: c^†13 c_13 Coefficient: -10.442114706289681\n",
"Term: c^†13 c_23 Coefficient: 0.32675775092663323\n",
"Term: c^†13 c_25 Coefficient: -0.03327309602237639\n",
"Term: c^†14 c_14 Coefficient: -10.442114706289681\n",
"Term: c^†14 c_22 Coefficient: 0.03327309602237596\n",
"Term: c^†14 c_24 Coefficient: 0.32675775092663456\n",
"Term: c^†15 c_15 Coefficient: -10.442114706289681\n",
"Term: c^†15 c_23 Coefficient: 0.03327309602237596\n",
"Term: c^†15 c_25 Coefficient: 0.32675775092663456\n",
"Term: c^†16 c_0 Coefficient: -0.31481355028948715\n",
"Term: c^†16 c_8 Coefficient: 0.629641914738889\n",
"Term: c^†16 c_16 Coefficient: -10.668936174118013\n",
"Term: c^†16 c_28 Coefficient: 0.13194954967145298\n",
"Term: c^†17 c_1 Coefficient: -0.31481355028948715\n",
"Term: c^†17 c_9 Coefficient: 0.629641914738889\n",
"Term: c^†17 c_17 Coefficient: -10.668936174118013\n",
"Term: c^†17 c_29 Coefficient: 0.13194954967145298\n",
"Term: c^†18 c_18 Coefficient: -10.689564730643733\n",
"Term: c^†19 c_19 Coefficient: -10.689564730643733\n",
"Term: c^†20 c_20 Coefficient: -10.689564730643733\n",
"Term: c^†21 c_21 Coefficient: -10.689564730643733\n",
"Term: c^†22 c_12 Coefficient: 0.326757750926632\n",
"Term: c^†22 c_14 Coefficient: 0.033273096022375895\n",
"Term: c^†22 c_22 Coefficient: -10.292589815408641\n",
"Term: c^†23 c_13 Coefficient: 0.326757750926632\n",
"Term: c^†23 c_15 Coefficient: 0.033273096022375895\n",
"Term: c^†23 c_23 Coefficient: -10.292589815408641\n",
"Term: c^†24 c_12 Coefficient: -0.033273096022376374\n",
"Term: c^†24 c_14 Coefficient: 0.3267577509266348\n",
"Term: c^†24 c_24 Coefficient: -10.292589815408652\n",
"Term: c^†25 c_13 Coefficient: -0.033273096022376374\n",
"Term: c^†25 c_15 Coefficient: 0.3267577509266348\n",
"Term: c^†25 c_25 Coefficient: -10.292589815408652\n",
"Term: c^†26 c_2 Coefficient: -0.18321309390773752\n",
"Term: c^†26 c_4 Coefficient: 0.30972569922105775\n",
"Term: c^†26 c_6 Coefficient: 0.9657018575993387\n",
"Term: c^†26 c_10 Coefficient: 0.07597946676520091\n",
"Term: c^†26 c_26 Coefficient: -10.682274747688648\n",
"Term: c^†27 c_3 Coefficient: -0.18321309390773752\n",
"Term: c^†27 c_5 Coefficient: 0.30972569922105775\n",
"Term: c^†27 c_7 Coefficient: 0.9657018575993387\n",
"Term: c^†27 c_11 Coefficient: 0.07597946676520091\n",
"Term: c^†27 c_27 Coefficient: -10.682274747688648\n",
"Term: c^†28 c_0 Coefficient: 0.24049574169484692\n",
"Term: c^†28 c_8 Coefficient: 0.5837938061192661\n",
"Term: c^†28 c_16 Coefficient: 0.13194954967145156\n",
"Term: c^†28 c_28 Coefficient: -10.92425561369221\n",
"Term: c^†29 c_1 Coefficient: 0.24049574169484692\n",
"Term: c^†29 c_9 Coefficient: 0.5837938061192661\n",
"Term: c^†29 c_17 Coefficient: 0.13194954967145156\n",
"Term: c^†29 c_29 Coefficient: -10.92425561369221\n"
]
}
],
"source": [
"one_body_terms = fh.terms\n",
"\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",
"\n",
" # Extract creation and annihilation operators separately\n",
" creation_operator = \"c^\\u2020{}\".format(i[0]) # Creation operator\n",
" annihilation_operator = \"c_{}\".format(j[0]) # Annihilation operator\n",
"\n",
" print(\"Term: {} {} Coefficient: {}\".format(creation_operator, annihilation_operator, coefficient))"
]
},
{
"cell_type": "markdown",
"id": "3603a455",
"metadata": {
"id": "3603a455"
},
"source": [
"* Transform into qubit Hamiltonian"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "0d91ce08",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0d91ce08",
"outputId": "89f7301d-da5f-415e-8b1a-a113eff29502"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"30"
]
},
"metadata": {},
"execution_count": 12
}
],
"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": {
"id": "f9c6c84e"
},
"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": 13,
"id": "12ee73f1",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "12ee73f1",
"outputId": "39e3b5c4-6199-4c9f-f3d8-438bc695e5f5"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"28"
]
},
"metadata": {},
"execution_count": 13
}
],
"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": 14,
"id": "3dd4b878",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "3dd4b878",
"outputId": "08d0f363-469e-488b-aa47-2b40f4dba692"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"12.93442537622547\n"
]
}
],
"source": [
"print(b.terms[((0, \"Z\"),)]) # coefficient of Z_0 Pauli-string"
]
},
{
"cell_type": "markdown",
"id": "7551bd16-dd22-4dba-b72f-93fca5a572fd",
"metadata": {
"id": "7551bd16-dd22-4dba-b72f-93fca5a572fd"
},
"source": [
"Reference : https://qiskit.org/documentation/stable/0.24/stubs/qiskit.chemistry.algorithms.OrbitalOptimizationVQE.html , https://github.com/QunaSys/quantum-algorithm-grand-challenge#fermi-hubbard-model-\n",
"\n",
"Orbital rotation which adheres to fermi-hubbard model\n",
"\n",
"https://poe.com/s/HAor7aM2N1R8D954dQf5\n",
"\n",
"https://poe.com/s/O94fKe7gPwy6Q85enM2J"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "96c0b49b-4f3c-42ea-afd3-539947dfa152",
"metadata": {
"tags": [],
"id": "96c0b49b-4f3c-42ea-afd3-539947dfa152"
},
"outputs": [],
"source": [
"# This code is part of Qiskit.\n",
"#\n",
"# (C) Copyright IBM 2020.\n",
"#\n",
"# This code is licensed under the Apache License, Version 2.0. You may\n",
"# obtain a copy of this license in the LICENSE.txt file in the root directory\n",
"# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.\n",
"#\n",
"# Any modifications or derivative works of this code must retain this\n",
"# copyright notice, and modified files need to carry a notice indicating\n",
"# that they have been altered from the originals.\n",
"\n",
"\"\"\"\n",
"A ground state calculation employing the Orbital-Optimized VQE (OOVQE) algorithm.\n",
"\"\"\"\n",
"\n",
"from typing import Optional, List, Union, Tuple\n",
"import logging\n",
"import copy\n",
"import numpy as np\n",
"from scipy.linalg import expm\n",
"\n",
"#from qiskit.aqua import AquaError\n",
"#from qiskit.aqua.algorithms import VQE, MinimumEigensolver\n",
"#from qiskit.aqua.operators import LegacyBaseOperator\n",
"\n",
"from qiskit.exceptions import QiskitError\n",
"from qiskit.algorithms import MinimumEigensolver\n",
"from qiskit.opflow import OperatorBase\n",
"\n",
"'''\n",
"from .ground_state_eigensolver import GroundStateEigensolver\n",
"from .minimum_eigensolver_factories import MinimumEigensolverFactory\n",
"from ...components.variational_forms import UCCSD\n",
"from ...fermionic_operator import FermionicOperator\n",
"from ...bosonic_operator import BosonicOperator\n",
"from ...drivers.base_driver import BaseDriver\n",
"from ...drivers.fermionic_driver import FermionicDriver\n",
"from ...transformations.fermionic_transformation import FermionicTransformation\n",
"from ...results.electronic_structure_result import ElectronicStructureResult\n",
"from ...qmolecule import QMolecule\n",
"'''\n",
"\n",
"from qiskit_nature.second_q.algorithms.ground_state_solvers.ground_state_eigensolver import GroundStateEigensolver\n",
"from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver\n",
"from qiskit_nature.second_q.circuit.library import UCCSD\n",
"from qiskit_nature.second_q.operators import FermionicOp\n",
"from qiskit_nature.second_q.operators import BosonicOp\n",
"from qiskit_nature.second_q.drivers import BaseDriver\n",
"from qiskit_nature.second_q.drivers import PySCFDriver\n",
"from qiskit_nature.second_q.problems import ElectronicStructureProblem\n",
"from qiskit_nature.second_q.problems import ElectronicStructureResult\n",
"from qiskit_nature.second_q.formats.molecule_info import MoleculeInfo\n",
"\n",
"from qiskit_nature.second_q.mappers import JordanWignerMapper\n",
"from qiskit_nature.second_q.mappers import QubitMapper\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit.algorithms.optimizers import COBYLA\n",
"\n",
"from qiskit.algorithms.minimum_eigensolvers import VQE\n",
"\n",
"# Set setting to use SparsePauliOp\n",
"import qiskit_nature.settings\n",
"qiskit_nature.settings.use_pauli_sum_op = False\n",
"\n",
"logger = logging.getLogger(__name__)\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "d696b414-06f5-41bb-ad7f-c50a39225603",
"metadata": {
"tags": [],
"id": "d696b414-06f5-41bb-ad7f-c50a39225603"
},
"outputs": [],
"source": [
"class OrbitalOptimizationVQE(GroundStateEigensolver):\n",
" r\"\"\" A ground state calculation employing the OOVQE algorithm.\n",
" The Variational Quantum Eigensolver (VQE) algorithm enhanced with the Orbital Optimization (OO).\n",
" The core of the approach resides in the optimization of orbitals through the\n",
" AO-to-MO coefficients matrix C. In the usual VQE, the latter remains constant throughout\n",
" the simulation. Here, its elements are modified according to C=Ce^(-kappa) where kappa is\n",
" an anti-hermitian matrix. This transformation preserves the spectrum but modifies the\n",
" amplitudes of the ground state of given operator such that in the end a given ansatz\n",
" can be closest to that ground state, producing larger overlap and lower eigenvalue than\n",
" conventional VQE. Kappa is parametrized and optimized inside the OOVQE in the same way as\n",
" the gate angles. Therefore, at each step of OOVQE the coefficient matrix C is modified and\n",
" the operator is recomputed, unlike usual VQE where operator remains constant.\n",
" Iterative OO refers to optimization in two steps, first the wavefunction and then the\n",
" orbitals. It allows for faster optimization as the operator is not recomputed when\n",
" wavefunction is optimized. It is recommended to use the iterative method on real device/qasm\n",
" simulator with noise to facilitate the convergence of the classical optimizer.\n",
" For more details of this method refer to: https://aip.scitation.org/doi/10.1063/1.5141835\n",
" \"\"\"\n",
"\n",
" def __init__(self,\n",
" transformation: FermionicOp,\n",
" solver: MinimumEigensolver,\n",
" initial_point: Optional[np.ndarray] = None,\n",
" orbital_rotation: Optional['OrbitalRotation'] = None,\n",
" bounds: Optional[np.ndarray] = None,\n",
" iterative_oo: bool = True,\n",
" iterative_oo_iterations: int = 2,\n",
" ):\n",
" \"\"\"\n",
" Args:\n",
" transformation: a fermionic driver to operator transformation strategy.\n",
" solver: a VQE instance or a factory for the VQE solver employing any custom\n",
" variational form, such as the `VQEUCCSDFactory`. Both need to use the UCCSD\n",
" variational form.\n",
" initial_point: An optional initial point (i.e. initial parameter values)\n",
" for the optimizer. If ``None`` then VQE will look to the variational form for a\n",
" preferred point and if not will simply compute a random one.\n",
" orbital_rotation: instance of\n",
" :class:`~qiskit.chemistry.ground_state_calculation.OrbitalRotation` class\n",
" that creates the matrices that rotate the orbitals needed to produce the rotated\n",
" MO coefficients C as C = C0 * exp(-kappa).\n",
" bounds: bounds for variational form and orbital rotation\n",
" parameters given to a classical optimizer.\n",
" iterative_oo: when ``True`` optimize first the variational form and then the\n",
" orbitals, iteratively. Otherwise, the wavefunction ansatz and orbitals are\n",
" optimized simultaneously.\n",
" iterative_oo_iterations: number of iterations in the iterative procedure,\n",
" set larger to be sure to converge to the global minimum.\n",
" Raises:\n",
" QiskitError: if the number of orbital optimization iterations is less or equal to zero.\n",
" \"\"\"\n",
"\n",
" super().__init__(transformation, solver)\n",
"\n",
" self._transformation = transformation\n",
"\n",
" if not isinstance(self._transformation, FermionicOp):\n",
" raise QiskitError('OrbitalOptimizationVQE requires a FermionicOp.')\n",
" from typing import cast\n",
" self._transformation = cast(FermionicOp, self._transformation)\n",
"\n",
" self.initial_point = initial_point\n",
" self._orbital_rotation = orbital_rotation\n",
" self._bounds = bounds\n",
" self._iterative_oo = iterative_oo\n",
" self._iterative_oo_iterations = iterative_oo_iterations\n",
"\n",
" # internal parameters of the algorithm\n",
" self._driver = None # type: Optional[PySCFDriver]\n",
" self._qmolecule = None # type: Optional[MoleculeInfo]\n",
" self._qmolecule_rotated = None # type: Optional[MoleculeInfo]\n",
"\n",
" self._fixed_wavefunction_params = None\n",
" self._num_parameters_oovqe = None\n",
" self._additional_params_initialized = False\n",
" self.var_form_num_parameters = None\n",
" self.var_form_bounds = None\n",
" self._vqe = None # type: Optional[VQE]\n",
" self._bound_oo = None # type: Optional[List]\n",
"\n",
"\n",
" def returns_groundstate(self) -> bool:\n",
" return True\n",
"\n",
"\n",
" def _set_operator_and_vqe(self, driver: BaseDriver):\n",
" \"\"\" Initializes the operators using provided driver of qmolecule.\"\"\"\n",
"\n",
" if not isinstance(self._transformation, FermionicOp):\n",
" raise QiskitError('OrbitalOptimizationVQE requires a FermionicTransformation.')\n",
" if not isinstance(driver, PySCFDriver):\n",
" raise QiskitError('OrbitalOptimizationVQE only works with PySCF Drivers.')\n",
"\n",
" if self._qmolecule is None:\n",
" # in future, self._transformation.transform should return also qmolecule\n",
" # to avoid running the driver twice\n",
" self._qmolecule = driver.run()\n",
" operator, aux_operators = self._transformation._do_transform(self._qmolecule)\n",
" else:\n",
" operator, aux_operators = self._transformation._do_transform(self._qmolecule)\n",
" if operator is None: # type: ignore\n",
" raise QiskitError(\"The operator was never provided.\")\n",
"\n",
" if isinstance(self.solver, MinimumEigensolver):\n",
" # this must be called after transformation.transform\n",
" self._vqe = self.solver.get_solver(self.transformation)\n",
" else:\n",
" self._vqe = self.solver\n",
"\n",
" if not isinstance(self._vqe, VQE):\n",
" raise QiskitError(\n",
" \"The OrbitalOptimizationVQE algorithm requires the use of the VQE \" +\n",
" \"MinimumEigensolver.\")\n",
" if not isinstance(self._vqe.var_form, UCCSD):\n",
" raise QiskitError(\n",
" \"The OrbitalOptimizationVQE algorithm requires the use of the UCCSD varform.\")\n",
"\n",
" self._vqe.operator = operator\n",
" self._vqe.aux_operators = aux_operators\n",
"\n",
" def _set_bounds(self,\n",
" bounds_var_form_val: tuple = (-2 * np.pi, 2 * np.pi),\n",
" bounds_oo_val: tuple = (-2 * np.pi, 2 * np.pi)) -> None:\n",
" \"\"\" Initializes the array of bounds of wavefunction and OO parameters.\n",
" Args:\n",
" bounds_var_form_val: pair of bounds between which the optimizer confines the\n",
" values of wavefunction parameters.\n",
" bounds_oo_val: pair of bounds between which the optimizer confines the values of\n",
" OO parameters.\n",
" Raises:\n",
" QiskitError: Instantiate OrbitalRotation class and provide it to the\n",
" orbital_rotation keyword argument\n",
" \"\"\"\n",
" self._bounds = []\n",
" bounds_var_form = [bounds_var_form_val for _ in range(self._vqe.var_form.num_parameters)]\n",
" self._bound_oo = \\\n",
" [bounds_oo_val for _ in range(self._orbital_rotation.num_parameters)]\n",
" self._bounds = bounds_var_form + self._bound_oo\n",
" self._bounds = np.array(self._bounds)\n",
"\n",
" def _set_initial_point(self, initial_pt_scalar: float = 1e-1) -> None:\n",
" \"\"\" Initializes the initial point for the algorithm if the user does not provide his own.\n",
" Args:\n",
" initial_pt_scalar: value of the initial parameters for wavefunction and orbital rotation\n",
" \"\"\"\n",
" self.initial_point = [initial_pt_scalar for _ in range(self._num_parameters_oovqe)]\n",
"\n",
" def _initialize_additional_parameters(self, driver: BaseDriver):\n",
" \"\"\" Initializes additional parameters of the OOVQE algorithm. \"\"\"\n",
"\n",
" if not isinstance(self._transformation, FermionicOp):\n",
" raise QiskitError('OrbitalOptimizationVQE requires a FermionicTransformation.')\n",
"\n",
" self._set_operator_and_vqe(driver)\n",
"\n",
" if self._orbital_rotation is None:\n",
" self._orbital_rotation = OrbitalRotation(num_qubits=self._vqe.var_form.num_qubits,\n",
" transformation=self._transformation,\n",
" qmolecule=self._qmolecule)\n",
" self._num_parameters_oovqe = \\\n",
" self._vqe.var_form.num_parameters + self._orbital_rotation.num_parameters\n",
"\n",
" if self.initial_point is None:\n",
" self._set_initial_point()\n",
" else:\n",
" if len(self.initial_point) is not self._num_parameters_oovqe:\n",
" raise QiskitError(\n",
" 'Number of parameters of OOVQE ({}) does not match the length of the '\n",
" 'intitial_point ({})'.format(self._num_parameters_oovqe,\n",
" len(self.initial_point)))\n",
" if self._bounds is None:\n",
" self._set_bounds(self._orbital_rotation.parameter_bound_value)\n",
" if self._iterative_oo_iterations < 1:\n",
" raise QiskitError('Please set iterative_oo_iterations parameter to a positive number,'\n",
" ' got {} instead'.format(self._iterative_oo_iterations))\n",
"\n",
" # copies to overcome incompatibilities with error checks in VQAlgorithm class\n",
" self.var_form_num_parameters = self._vqe.var_form.num_parameters\n",
" self.var_form_bounds = copy.copy(self._vqe.var_form._bounds)\n",
" self._additional_params_initialized = True\n",
"\n",
" def _energy_evaluation_oo(self, parameters: np.ndarray) -> Union[float, List[float]]:\n",
" \"\"\" Evaluate energy at given parameters for the variational form and parameters for\n",
" given rotation of orbitals.\n",
" Args:\n",
" parameters: parameters for variational form and orbital rotations.\n",
" Returns:\n",
" energy of the hamiltonian of each parameter.\n",
" Raises:\n",
" QiskitError: Instantiate OrbitalRotation class and provide it to the\n",
" orbital_rotation keyword argument\n",
" \"\"\"\n",
"\n",
" if not isinstance(self._transformation, FermionicOp):\n",
" raise QiskitError('OrbitalOptimizationVQE requires a FermionicTransformation.')\n",
"\n",
" # slice parameter lists\n",
" if self._iterative_oo:\n",
" parameters_var_form = self._fixed_wavefunction_params\n",
" parameters_orb_rot = parameters\n",
" else:\n",
" parameters_var_form = parameters[:self.var_form_num_parameters]\n",
" parameters_orb_rot = parameters[self.var_form_num_parameters:]\n",
"\n",
" logger.info('Parameters of wavefunction are: \\n%s', repr(parameters_var_form))\n",
" logger.info('Parameters of orbital rotation are: \\n%s', repr(parameters_orb_rot))\n",
"\n",
" # rotate the orbitals\n",
" if self._orbital_rotation is None:\n",
" raise QiskitError('Instantiate OrbitalRotation class and provide it to the '\n",
" 'orbital_rotation keyword argument')\n",
"\n",
" self._orbital_rotation.orbital_rotation_matrix(parameters_orb_rot)\n",
"\n",
" # preserve original qmolecule and create a new one with rotated orbitals\n",
" self._qmolecule_rotated = copy.copy(self._qmolecule)\n",
" OrbitalOptimizationVQE._rotate_orbitals_in_qmolecule(\n",
" self._qmolecule_rotated, self._orbital_rotation)\n",
"\n",
" # construct the qubit operator\n",
" operator, aux_operators = self._transformation._do_transform(self._qmolecule_rotated)\n",
" if isinstance(operator, OperatorBase):\n",
" operator = operator.to_opflow()\n",
" self._vqe.operator = operator\n",
" self._vqe.aux_operators = aux_operators\n",
" logger.debug('Orbital rotation parameters of matrix U at evaluation %d returned'\n",
" '\\n %s', self._vqe._eval_count, repr(self._orbital_rotation.matrix_a))\n",
" self._vqe.var_form._num_parameters = self.var_form_num_parameters\n",
"\n",
" # compute the energy on given state\n",
" mean_energy = self._vqe._energy_evaluation(parameters=parameters_var_form)\n",
"\n",
" return mean_energy\n",
"\n",
" def solve(self,\n",
" driver: BaseDriver,\n",
" aux_operators: Optional[Union[List[FermionicOp],\n",
" List[BosonicOp]]] = None) \\\n",
" -> ElectronicStructureResult:\n",
"\n",
" self._initialize_additional_parameters(driver)\n",
"\n",
" if not isinstance(self._transformation, FermionicOp):\n",
" raise QiskitError('OrbitalOptimizationVQE requires a FermionicTransformation.')\n",
"\n",
" self._vqe._eval_count = 0\n",
"\n",
" # initial orbital rotation starting point is provided\n",
" if self._orbital_rotation.matrix_a is not None and self._orbital_rotation.matrix_b is not \\\n",
" None:\n",
" self._qmolecule_rotated = copy.copy(self._qmolecule)\n",
" OrbitalOptimizationVQE._rotate_orbitals_in_qmolecule(\n",
" self._qmolecule_rotated, self._orbital_rotation)\n",
" operator, aux_operators = self._transformation._do_transform(self._qmolecule_rotated)\n",
" self._vqe.operator = operator\n",
" self._vqe.aux_operators = aux_operators\n",
"\n",
" logger.info(\n",
" '\\n\\nSetting the initial value for OO matrices and rotating Hamiltonian \\n')\n",
" logger.info('Optimising Orbital Coefficient Rotation Alpha: \\n%s',\n",
" repr(self._orbital_rotation.matrix_a))\n",
" logger.info('Optimising Orbital Coefficient Rotation Beta: \\n%s',\n",
" repr(self._orbital_rotation.matrix_b))\n",
"\n",
" # save the original number of parameters as we modify their number to bypass the\n",
" # error checks that are not tailored to OOVQE\n",
"\n",
" # iterative method\n",
" if self._iterative_oo:\n",
" for _ in range(self._iterative_oo_iterations):\n",
" # optimize wavefunction ansatz\n",
" logger.info('OrbitalOptimizationVQE: Ansatz optimization, orbitals fixed.')\n",
" self._vqe.var_form._num_parameters = self.var_form_num_parameters\n",
" if isinstance(self._vqe.operator, OperatorBase): # type: ignore\n",
" self._vqe.operator = self._vqe.operator.to_opflow() # type: ignore\n",
" self._vqe.var_form._bounds = self.var_form_bounds\n",
" vqresult_wavefun = self._vqe.find_minimum(\n",
" initial_point=self.initial_point[:self.var_form_num_parameters],\n",
" var_form=self._vqe.var_form,\n",
" cost_fn=self._vqe._energy_evaluation,\n",
" optimizer=self._vqe.optimizer)\n",
" self.initial_point[:self.var_form_num_parameters] = vqresult_wavefun.optimal_point\n",
"\n",
" # optimize orbitals\n",
" logger.info('OrbitalOptimizationVQE: Orbital optimization, ansatz fixed.')\n",
" self._vqe.var_form._bounds = self._bound_oo\n",
" self._vqe.var_form._num_parameters = self._orbital_rotation.num_parameters\n",
" self._fixed_wavefunction_params = vqresult_wavefun.optimal_point\n",
" vqresult = self._vqe.find_minimum(\n",
" initial_point=self.initial_point[self.var_form_num_parameters:],\n",
" var_form=self._vqe.var_form,\n",
" cost_fn=self._energy_evaluation_oo,\n",
" optimizer=self._vqe.optimizer)\n",
" self.initial_point[self.var_form_num_parameters:] = vqresult.optimal_point\n",
" else:\n",
" # simultaneous method (ansatz and orbitals are optimized at the same time)\n",
" self._vqe.var_form._bounds = self._bounds\n",
" self._vqe.var_form._num_parameters = len(self._bounds)\n",
" vqresult = self._vqe.find_minimum(initial_point=self.initial_point,\n",
" var_form=self._vqe.var_form,\n",
" cost_fn=self._energy_evaluation_oo,\n",
" optimizer=self._vqe.optimizer)\n",
"\n",
" # write original number of parameters to avoid errors due to parameter number mismatch\n",
" self._vqe.var_form._num_parameters = self.var_form_num_parameters\n",
"\n",
" # extend VQE returned information with additional outputs\n",
" result = OOVQEResult()\n",
" result.computed_electronic_energy = vqresult.optimal_value\n",
" result.num_optimizer_evals = vqresult.optimizer_evals\n",
" result.optimal_point = vqresult.optimal_point\n",
" if self._iterative_oo:\n",
" result.optimal_point_ansatz = self.initial_point[self.var_form_num_parameters:]\n",
" result.optimal_point_orbitals = self.initial_point[:self.var_form_num_parameters]\n",
" else:\n",
" result.optimal_point_ansatz = vqresult.optimal_point[:self.var_form_num_parameters]\n",
" result.optimal_point_orbitals = vqresult.optimal_point[self.var_form_num_parameters:]\n",
" result.eigenenergies = [vqresult.optimal_value + 0j]\n",
"\n",
" # copy parameters bypass the error checks that are not tailored to OOVQE\n",
" _ret_temp_params = copy.copy(vqresult.optimal_point)\n",
" self._vqe._ret = {}\n",
" self._vqe._ret['opt_params'] = vqresult.optimal_point[:self.var_form_num_parameters]\n",
" if self._iterative_oo:\n",
" self._vqe._ret['opt_params'] = vqresult_wavefun.optimal_point\n",
" result.eigenstates = [self._vqe.get_optimal_vector()]\n",
" if not self._iterative_oo:\n",
" self._vqe._ret['opt_params'] = _ret_temp_params\n",
"\n",
" if self._vqe.aux_operators is not None:\n",
" # copy parameters bypass the error checks that are not tailored to OOVQE\n",
" self._vqe._ret['opt_params'] = vqresult.optimal_point[:self.var_form_num_parameters]\n",
" if self._iterative_oo:\n",
" self._vqe._ret['opt_params'] = vqresult_wavefun.optimal_point\n",
" self._vqe._eval_aux_ops()\n",
" result.aux_operator_eigenvalues = self._vqe._ret['aux_ops'][0]\n",
" if not self._iterative_oo:\n",
" self._vqe._ret['opt_params'] = _ret_temp_params\n",
"\n",
" result.cost_function_evals = self._vqe._eval_count\n",
" self.transformation.interpret(result)\n",
"\n",
" return result\n",
"\n",
"\n",
" @staticmethod\n",
" def _rotate_orbitals_in_qmolecule(qmolecule: MoleculeInfo,\n",
" orbital_rotation: 'OrbitalRotation') -> None:\n",
" \"\"\" Rotates the orbitals by applying a modified a anti-hermitian matrix\n",
" (orbital_rotation.matrix_a) onto the MO coefficients matrix and recomputes all the\n",
" quantities dependent on the MO coefficients. Be aware that qmolecule is modified\n",
" when this executes.\n",
" Args:\n",
" qmolecule: instance of QMolecule class\n",
" orbital_rotation: instance of OrbitalRotation class\n",
" \"\"\"\n",
"\n",
" # 1 and 2 electron integrals (required) from AO to MO basis\n",
" qmolecule.mo_coeff = np.matmul(qmolecule.mo_coeff,\n",
" orbital_rotation.matrix_a)\n",
" qmolecule.mo_onee_ints = qmolecule.oneeints2mo(qmolecule.hcore,\n",
" qmolecule.mo_coeff)\n",
" # support for unrestricted spins\n",
" if qmolecule.mo_coeff_b is not None:\n",
" qmolecule.mo_coeff_b = np.matmul(qmolecule.mo_coeff_b,\n",
" orbital_rotation.matrix_b)\n",
" qmolecule.mo_onee_ints_b = qmolecule.oneeints2mo(qmolecule.hcore,\n",
" qmolecule.mo_coeff)\n",
"\n",
" qmolecule.mo_eri_ints = qmolecule.twoeints2mo(qmolecule.eri,\n",
" qmolecule.mo_coeff)\n",
" if qmolecule.mo_coeff_b is not None:\n",
" mo_eri_b = qmolecule.twoeints2mo(qmolecule.eri,\n",
" qmolecule.mo_coeff_b)\n",
" norbs = qmolecule.mo_coeff.shape[0]\n",
" qmolecule.mo_eri_ints_bb = mo_eri_b.reshape(norbs, norbs, norbs, norbs)\n",
" qmolecule.mo_eri_ints_ba = qmolecule.twoeints2mo_general(\n",
" qmolecule.eri, qmolecule.mo_coeff_b, qmolecule.mo_coeff_b, qmolecule.mo_coeff,\n",
" qmolecule.mo_coeff)\n",
" qmolecule.mo_eri_ints_ba = qmolecule.mo_eri_ints_ba.reshape(norbs, norbs,\n",
" norbs, norbs)\n",
" # dipole integrals (if available) from AO to MO\n",
" if qmolecule.x_dip_ints is not None:\n",
" qmolecule.x_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.x_dip_ints,\n",
" qmolecule.mo_coeff)\n",
" qmolecule.y_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.y_dip_ints,\n",
" qmolecule.mo_coeff)\n",
" qmolecule.z_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.z_dip_ints,\n",
" qmolecule.mo_coeff)\n",
" # support for unrestricted spins\n",
" if qmolecule.mo_coeff_b is not None and qmolecule.x_dip_ints is not None:\n",
" qmolecule.x_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.x_dip_ints,\n",
" qmolecule.mo_coeff_b)\n",
" qmolecule.y_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.y_dip_ints,\n",
" qmolecule.mo_coeff_b)\n",
" qmolecule.z_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.z_dip_ints,\n",
" qmolecule.mo_coeff_b)\n",
"\n",
"\n",
"\n",
"class OrbitalRotation:\n",
" r\"\"\" Class that regroups methods for creation of matrices that rotate the MOs.\n",
" It allows to create the unitary matrix U = exp(-kappa) that is parameterized with kappa's\n",
" elements. The parameters are the off-diagonal elements of the anti-hermitian matrix kappa.\n",
" \"\"\"\n",
"\n",
" def __init__(self,\n",
" num_qubits: int,\n",
" transformation: FermionicOp,\n",
" qmolecule: Optional[MoleculeInfo] = None,\n",
" orbital_rotations: list = None,\n",
" orbital_rotations_beta: list = None,\n",
" parameters: list = None,\n",
" parameter_bounds: list = None,\n",
" parameter_initial_value: float = 0.1,\n",
" parameter_bound_value: Tuple[float, float] = (-2 * np.pi, 2 * np.pi)) -> None:\n",
" \"\"\"\n",
" Args:\n",
" num_qubits: number of qubits necessary to simulate a particular system.\n",
" transformation: a fermionic driver to operator transformation strategy.\n",
" qmolecule: instance of the :class:`~qiskit.chemistry.QMolecule` class which has methods\n",
" needed to recompute one-/two-electron/dipole integrals after orbital rotation\n",
" (C = C0 * exp(-kappa)). It is not required but can be used if user wished to\n",
" provide custom integrals for instance.\n",
" orbital_rotations: list of alpha orbitals that are rotated (i.e. [[0,1], ...] the\n",
" 0-th orbital is rotated with 1-st, which corresponds to non-zero entry 01 of\n",
" the matrix kappa).\n",
" orbital_rotations_beta: list of beta orbitals that are rotated.\n",
" parameters: orbital rotation parameter list of matrix elements that rotate the MOs,\n",
" each associated to a pair of orbitals that are rotated\n",
" (non-zero elements in matrix kappa), or elements in the orbital_rotation(_beta)\n",
" lists.\n",
" parameter_bounds: parameter bounds\n",
" parameter_initial_value: initial value for all the parameters.\n",
" parameter_bound_value: value for the bounds on all the parameters\n",
" \"\"\"\n",
"\n",
" self._num_qubits = num_qubits\n",
" self._transformation = transformation\n",
" self._qmolecule = qmolecule\n",
"\n",
" self._orbital_rotations = orbital_rotations\n",
" self._orbital_rotations_beta = orbital_rotations_beta\n",
" self._parameter_initial_value = parameter_initial_value\n",
" self._parameter_bound_value = parameter_bound_value\n",
" self._parameters = parameters\n",
" if self._parameters is None:\n",
" self._create_parameter_list_for_orbital_rotations()\n",
"\n",
" self._num_parameters = len(self._parameters)\n",
" self._parameter_bounds = parameter_bounds\n",
" if self._parameter_bounds is None:\n",
" self._create_parameter_bounds()\n",
"\n",
" self._freeze_core = self._transformation._freeze_core\n",
" self._core_list = self._qmolecule.core_orbitals if self._freeze_core else None\n",
"\n",
" if self._transformation._two_qubit_reduction is True:\n",
" self._dim_kappa_matrix = int((self._num_qubits + 2) / 2)\n",
" else:\n",
" self._dim_kappa_matrix = int(self._num_qubits / 2)\n",
"\n",
" self._check_for_errors()\n",
" self._matrix_a = None\n",
" self._matrix_b = None\n",
"\n",
" def _check_for_errors(self) -> None:\n",
" \"\"\" Checks for errors such as incorrect number of parameters and indices of orbitals. \"\"\"\n",
"\n",
" # number of parameters check\n",
" if self._orbital_rotations_beta is None and self._orbital_rotations is not None:\n",
" if len(self._orbital_rotations) != len(self._parameters):\n",
" raise QiskitError('Please specify same number of params ({}) as there are '\n",
" 'orbital rotations ({})'.format(len(self._parameters),\n",
" len(self._orbital_rotations)))\n",
" elif self._orbital_rotations_beta is not None and self._orbital_rotations is not None:\n",
" if len(self._orbital_rotations) + len(self._orbital_rotations_beta) != len(\n",
" self._parameters):\n",
" raise QiskitError('Please specify same number of params ({}) as there are '\n",
" 'orbital rotations ({})'.format(len(self._parameters),\n",
" len(self._orbital_rotations)))\n",
" # indices of rotated orbitals check\n",
" for exc in self._orbital_rotations:\n",
" if exc[0] > (self._dim_kappa_matrix - 1):\n",
" raise QiskitError('You specified entries that go outside '\n",
" 'the orbital rotation matrix dimensions {}, '.format(exc[0]))\n",
" if exc[1] > (self._dim_kappa_matrix - 1):\n",
" raise QiskitError('You specified entries that go outside '\n",
" 'the orbital rotation matrix dimensions {}'.format(exc[1]))\n",
" if self._orbital_rotations_beta is not None:\n",
" for exc in self._orbital_rotations_beta:\n",
" if exc[0] > (self._dim_kappa_matrix - 1):\n",
" raise QiskitError('You specified entries that go outside '\n",
" 'the orbital rotation matrix dimensions {}'.format(exc[0]))\n",
" if exc[1] > (self._dim_kappa_matrix - 1):\n",
" raise QiskitError('You specified entries that go outside '\n",
" 'the orbital rotation matrix dimensions {}'.format(exc[1]))\n",
"\n",
" def _create_orbital_rotation_list(self) -> None:\n",
" \"\"\" Creates a list of indices of matrix kappa that denote the pairs of orbitals that\n",
" will be rotated. For instance, a list of pairs of orbital such as [[0,1], [0,2]]. \"\"\"\n",
"\n",
" if self._transformation._two_qubit_reduction:\n",
" half_as = int((self._num_qubits + 2) / 2)\n",
" else:\n",
" half_as = int(self._num_qubits / 2)\n",
"\n",
" self._orbital_rotations = []\n",
"\n",
" for i in range(half_as):\n",
" for j in range(half_as):\n",
" if i < j:\n",
" self._orbital_rotations.append([i, j])\n",
"\n",
" def _create_parameter_list_for_orbital_rotations(self) -> None:\n",
" \"\"\" Initializes the initial values of orbital rotation matrix kappa. \"\"\"\n",
"\n",
" # creates the indices of matrix kappa and prevent user from trying to rotate only betas\n",
" if self._orbital_rotations is None:\n",
" self._create_orbital_rotation_list()\n",
" elif self._orbital_rotations is None and self._orbital_rotations_beta is not None:\n",
" raise QiskitError('Only beta orbitals labels (orbital_rotations_beta) have been provided.'\n",
" 'Please also specify the alpha orbitals (orbital_rotations) '\n",
" 'that are rotated as well. Do not specify anything to have by default '\n",
" 'all orbitals rotated.')\n",
"\n",
" if self._orbital_rotations_beta is not None:\n",
" num_parameters = len(self._orbital_rotations + self._orbital_rotations_beta)\n",
" else:\n",
" num_parameters = len(self._orbital_rotations)\n",
" self._parameters = [self._parameter_initial_value for _ in range(num_parameters)]\n",
"\n",
" def _create_parameter_bounds(self) -> None:\n",
" \"\"\" Create bounds for parameters. \"\"\"\n",
" self._parameter_bounds = [self._parameter_bound_value for _ in range(self._num_parameters)]\n",
"\n",
" def orbital_rotation_matrix(self, parameters: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:\n",
" \"\"\" Creates 2 matrices K_alpha, K_beta that rotate the orbitals through MO coefficient\n",
" C_alpha = C_RHF * U_alpha where U = e^(K_alpha), similarly for beta orbitals. \"\"\"\n",
"\n",
" self._parameters = parameters\n",
" k_matrix_alpha = np.zeros((self._dim_kappa_matrix, self._dim_kappa_matrix))\n",
" k_matrix_beta = np.zeros((self._dim_kappa_matrix, self._dim_kappa_matrix))\n",
"\n",
" # allows to selectively rotate pairs of orbitals\n",
" if self._orbital_rotations_beta is None:\n",
" for i, exc in enumerate(self._orbital_rotations):\n",
" k_matrix_alpha[exc[0]][exc[1]] = self._parameters[i]\n",
" k_matrix_alpha[exc[1]][exc[0]] = -self._parameters[i]\n",
" k_matrix_beta[exc[0]][exc[1]] = self._parameters[i]\n",
" k_matrix_beta[exc[1]][exc[0]] = -self._parameters[i]\n",
" else:\n",
" for i, exc in enumerate(self._orbital_rotations):\n",
" k_matrix_alpha[exc[0]][exc[1]] = self._parameters[i]\n",
" k_matrix_alpha[exc[1]][exc[0]] = -self._parameters[i]\n",
"\n",
" for j, exc in enumerate(self._orbital_rotations_beta):\n",
" k_matrix_beta[exc[0]][exc[1]] = self._parameters[j + len(self._orbital_rotations)]\n",
" k_matrix_beta[exc[1]][exc[0]] = -self._parameters[j + len(self._orbital_rotations)]\n",
"\n",
" if self._freeze_core:\n",
" half_as = int(self._dim_kappa_matrix + len(self._core_list))\n",
" k_matrix_alpha_full = np.zeros((half_as, half_as))\n",
" k_matrix_beta_full = np.zeros((half_as, half_as))\n",
" # rotating only non-frozen part of orbitals\n",
" dim_full_k = k_matrix_alpha_full.shape[0] # pylint: disable=unsubscriptable-object\n",
"\n",
" if self._core_list is None:\n",
" raise QiskitError('Give _core_list, the list of molecular spatial orbitals that are '\n",
" 'frozen (e.g. [0] for the 1s or [0,1] for respectively Li2 or N2 '\n",
" 'for example).')\n",
" lower = len(self._core_list)\n",
" upper = dim_full_k\n",
" k_matrix_alpha_full[lower:upper, lower:upper] = k_matrix_alpha\n",
" k_matrix_beta_full[lower:upper, lower:upper] = k_matrix_beta\n",
" self._matrix_a = expm(k_matrix_alpha_full)\n",
" self._matrix_b = expm(k_matrix_beta_full)\n",
" else:\n",
" self._matrix_a = expm(k_matrix_alpha)\n",
" self._matrix_b = expm(k_matrix_beta)\n",
"\n",
" return self._matrix_a, self._matrix_b\n",
"\n",
" @property\n",
" def matrix_a(self) -> np.ndarray:\n",
" \"\"\"Returns matrix A.\"\"\"\n",
" return self._matrix_a\n",
"\n",
" @property\n",
" def matrix_b(self) -> np.ndarray:\n",
" \"\"\"Returns matrix B. \"\"\"\n",
" return self._matrix_b\n",
"\n",
" @property\n",
" def num_parameters(self) -> int:\n",
" \"\"\"Returns the number of parameters.\"\"\"\n",
" return self._num_parameters\n",
"\n",
" @property\n",
" def parameter_bound_value(self) -> Tuple[float, float]:\n",
" \"\"\"Returns a value for the bounds on all the parameters.\"\"\"\n",
" return self._parameter_bound_value\n",
"\n",
"\n",
"class OOVQEResult(ElectronicStructureResult):\n",
" r\"\"\" OOVQE Result. \"\"\"\n",
"\n",
" @property\n",
" def computed_electronic_energy(self) -> float:\n",
" \"\"\" Returns the ground state energy. \"\"\"\n",
" return self.get('computed_electronic_energy')\n",
"\n",
" @computed_electronic_energy.setter\n",
" def computed_electronic_energy(self, value: float) -> None:\n",
" \"\"\" Sets the ground state energy. \"\"\"\n",
" self.data['computed_electronic_energy'] = value\n",
"\n",
" @property\n",
" def cost_function_evals(self) -> int:\n",
" \"\"\" Returns number of cost function evaluations. \"\"\"\n",
" return self.get('cost_function_evals')\n",
"\n",
" @cost_function_evals.setter\n",
" def cost_function_evals(self, value: int) -> None:\n",
" \"\"\" Sets the number of cost function evaluations. \"\"\"\n",
" self.data['cost_function_evals'] = value\n",
"\n",
" @property\n",
" def num_optimizer_evals(self) -> int:\n",
" \"\"\" Returns the number of cost function evaluations in the optimizer \"\"\"\n",
" return self.get('num_optimizer_evals')\n",
"\n",
" @num_optimizer_evals.setter\n",
" def num_optimizer_evals(self, value: float) -> None:\n",
" \"\"\" Sets the number of cost function evaluations in the optimizer \"\"\"\n",
" self.data['num_optimizer_evals'] = value\n",
"\n",
" @property\n",
" def optimal_point(self) -> list:\n",
" \"\"\" Returns the optimal parameters. \"\"\"\n",
" return self.get('optimal_point')\n",
"\n",
" @optimal_point.setter\n",
" def optimal_point(self, value: list) -> None:\n",
" \"\"\" Sets the optimal parameters. \"\"\"\n",
" self.data['optimal_point'] = value\n",
"\n",
" @property\n",
" def optimal_point_ansatz(self) -> list:\n",
" \"\"\" Returns the optimal parameters for the . \"\"\"\n",
" return self.get('optimal_point_ansatz')\n",
"\n",
" @optimal_point_ansatz.setter\n",
" def optimal_point_ansatz(self, value: list) -> None:\n",
" \"\"\" Sets the optimal parameters for the ansatz. \"\"\"\n",
" self.data['optimal_point_ansatz'] = value\n",
"\n",
" @property\n",
" def optimal_point_orbitals(self) -> list:\n",
" \"\"\" Returns the optimal parameters of the orbitals. \"\"\"\n",
" return self.get('optimal_point_orbitals')\n",
"\n",
" @optimal_point_orbitals.setter\n",
" def optimal_point_orbitals(self, value: list) -> None:\n",
" \"\"\" Sets the optimal parameters of the orbitals. \"\"\"\n",
" self.data['optimal_point_orbitals'] = value"
]
},
{
"cell_type": "markdown",
"source": [
"Refer to https://github.com/qiskit-community/qiskit-nature/pull/629#pullrequestreview-968132514 if facing issues during code refactoring"
],
"metadata": {
"id": "UjENbkE1pFHd"
},
"id": "UjENbkE1pFHd"
},
{
"cell_type": "code",
"execution_count": 17,
"id": "4849b2a1-fc99-442d-a47b-d8bb854ad7df",
"metadata": {
"id": "4849b2a1-fc99-442d-a47b-d8bb854ad7df",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 435
},
"outputId": "e1d97c31-4491-4e40-c6cf-48b8fd3335a8"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"<qiskit_nature.second_q.problems.electronic_structure_problem.ElectronicStructureProblem object at 0x7fc811d41e10>\n"
]
},
{
"output_type": "error",
"ename": "AttributeError",
"evalue": "ignored",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-17-406fbcd04c8d>\u001b[0m in \u001b[0;36m<cell line: 38>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 36\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproblem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 38\u001b[0;31m \u001b[0moovqe_result\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moovqe\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdriver\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 39\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moovqe_result\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-16-a4472a6bf21d>\u001b[0m in \u001b[0;36msolve\u001b[0;34m(self, driver, aux_operators)\u001b[0m\n\u001b[1;32m 239\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mElectronicStructureResult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 240\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 241\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_initialize_additional_parameters\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdriver\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 242\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 243\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transformation\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mFermionicOp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-16-a4472a6bf21d>\u001b[0m in \u001b[0;36m_initialize_additional_parameters\u001b[0;34m(self, driver)\u001b[0m\n\u001b[1;32m 152\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mQiskitError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'OrbitalOptimizationVQE requires a FermionicTransformation.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 153\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 154\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_set_operator_and_vqe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdriver\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 155\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_orbital_rotation\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-16-a4472a6bf21d>\u001b[0m in \u001b[0;36m_set_operator_and_vqe\u001b[0;34m(self, driver)\u001b[0m\n\u001b[1;32m 96\u001b[0m \u001b[0;31m# to avoid running the driver twice\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_qmolecule\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdriver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 98\u001b[0;31m \u001b[0moperator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maux_operators\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transformation\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_qmolecule\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 99\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0moperator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maux_operators\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transformation\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_qmolecule\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mAttributeError\u001b[0m: 'FermionicOp' object has no attribute '_do_transform'"
]
}
],
"source": [
"mapper = JordanWignerMapper()\n",
"\n",
"#transformation = QubitConverter(mapper)\n",
"transformation = FermionicOp(\n",
" {\n",
" \"+_0 -_0\": 1.0,\n",
" \"+_1 -_1\": -1.0,\n",
" },\n",
" num_spin_orbitals=2,\n",
" )\n",
"\n",
"# For an open-shell system\n",
"num_alpha = 2\n",
"num_beta = 1\n",
"\n",
"ansatz = UCCSD(\n",
" qubit_mapper=mapper,\n",
" num_particles=[num_alpha, num_beta],\n",
" num_spatial_orbitals=3,\n",
" )\n",
"\n",
"optimizer = COBYLA()\n",
"\n",
"solver = VQE(transformation, optimizer=optimizer, ansatz=ansatz)\n",
"\n",
"oovqe = OrbitalOptimizationVQE(transformation, solver, initial_point=None, orbital_rotation=None, bounds=None, iterative_oo=True, iterative_oo_iterations=2)\n",
"\n",
"# See https://github.com/qiskit-community/qiskit-nature/pull/101/files#diff-6e4bccfd47fc4ee79f4edb3f76df852e43b62ded646e6a7c177f64455242ceb9R52-R62\n",
"# on how to properly migrate deprecated FermionicTransformation library\n",
"driver = PySCFDriver(atom='Li .0 .0 .0; H .0 .0 1.5',\n",
" basis='sto-3g',\n",
" charge=0,\n",
" spin=0)\n",
"\n",
"problem = driver.run()\n",
"print(problem)\n",
"\n",
"# _do_transform() from qiskit FermionicTransformation library ( https://pastebin.com/raw/YaUwJ7xd ) is now deprecated\n",
"oovqe_result = oovqe.solve(driver)\n",
"\n",
"print(oovqe_result)"
]
},
{
"cell_type": "markdown",
"id": "4ecd89c0",
"metadata": {
"id": "4ecd89c0"
},
"source": [
"* Transform the qubit Hamiltonian in openfermion to the format in TensorCircuit (the method using \"tc\" library is too memory-consuming, needs another method such as pySCF : https://poe.com/s/6E45IFth0MbXhXmiWKK3 )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "66fdc39d",
"metadata": {
"id": "66fdc39d"
},
"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": {
"id": "d8263f1a"
},
"source": [
"* Inspect Hamiltonian in matrix form"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "88b116e9",
"metadata": {
"id": "88b116e9"
},
"outputs": [],
"source": [
"ma = tc.quantum.PauliStringSum2COO_numpy(lsa, wa)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "63b92ae4",
"metadata": {
"id": "63b92ae4"
},
"outputs": [],
"source": [
"mb = tc.quantum.PauliStringSum2COO_numpy(lsb, wb)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4f76dfbc",
"metadata": {
"id": "4f76dfbc"
},
"outputs": [],
"source": [
"mad, mbd = ma.todense(), mb.todense()"
]
},
{
"cell_type": "markdown",
"id": "7b43d5c1",
"metadata": {
"id": "7b43d5c1"
},
"source": [
"The corresponding Hartree Fock product state in these two types of Hamiltonian"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a907144e",
"metadata": {
"id": "a907144e"
},
"outputs": [],
"source": [
"bin(np.argmin(np.diag(mad)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a56f6191",
"metadata": {
"id": "a56f6191"
},
"outputs": [],
"source": [
"bin(np.argmin(np.diag(mbd)))"
]
},
{
"cell_type": "markdown",
"id": "994d944b",
"metadata": {
"id": "994d944b"
},
"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": {
"id": "5b0f4d11"
},
"source": [
"* Using dense matrix expectation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6f1e065e",
"metadata": {
"id": "6f1e065e"
},
"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": {
"id": "58fee777"
},
"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": {
"id": "94918b36"
},
"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": {
"id": "721f4494"
},
"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": {
"id": "d8847fe3"
},
"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": {
"id": "8a60c6c7"
},
"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": {
"id": "2764bbca"
},
"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": {
"id": "bc0265ee"
},
"outputs": [],
"source": [
"%timeit dense_vag(tc.backend.ones([n]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8913d16d",
"metadata": {
"id": "8913d16d"
},
"outputs": [],
"source": [
"%timeit sparse_vag(tc.backend.ones([n]))"
]
},
{
"cell_type": "markdown",
"id": "3eafaa67",
"metadata": {
"id": "3eafaa67"
},
"source": [
"Therefore, sparse matrix evaluation also saves time apart from space, which is always recommended."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.10.8"
},
"vscode": {
"interpreter": {
"hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
}
},
"colab": {
"provenance": [],
"gpuType": "T4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment