{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "u00f6C4llBbK", "tags": [ "remove_cell" ] }, "source": [ "# Variational Thermofield Double State Generation" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "kAT8Dop2lBbM" }, "outputs": [], "source": [ "import qiskit\n", "from qiskit import IBMQ\n", "from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister\n", "from matplotlib import pyplot as plt\n", "from qiskit import Aer, execute\n", "import scipy\n", "import math\n", "import random\n", "import numpy as np\n", "from scipy.optimize import minimize\n", "import matplotlib\n", "from qiskit.ignis.verification.tomography import *\n", "from qiskit.providers.aer.noise import NoiseModel" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "yvrmm2R9lBbS" }, "source": [ "## 1. Introduction\n", "\n", "In this Notebook, we will turn our attention to a very import near-term application of quantum computers: generation of non-trivial quantum states for use in different experiments. The state that we will be looking into in this Notebook is called the Thermofield Double State, and is of great interest in areas of physics ranging from condensed matter to quantum chromodynamics, to black holes and gravitation. We will begin this Notebook by talking about what a Thermofield Doulbe State is, and some of its properties. We will then discuss a protocal for generating these states (see the Conclusion for the paper off of which this Notebook is based), and finally, we will simulate the generation of TFD states for two different Hamiltonians.\n", "\n", "## 2. Defining and Understanding the Thermofield Double State (TFD State)\n", "\n", "The goal of this Notebook is to outline a the variational process outlined [this paper]('https://arxiv.org/abs/1906.02699') in order to generate Thermofield Double States (TFD states), which are defined as follows:\n", "\n", "
\n", "$$|TFD\\rangle \\ = \\ \\frac{1}{\\sqrt{Z(\\beta)}} \\displaystyle\\sum_{n} e^{- \\beta E_n / 2} |E_n\\rangle_A \\otimes |E_n'\\rangle_B$$\n", "
\n", "\n", "Notice how we are concered with the tensor product of two states for each term in the sum (we are concerned with two different systems of qubits, register $A$ and register $B$). We also define $H_A$ to be the Hamiltonian governing the dynamics of the qubits in the first register, with $H_A |E_n\\rangle_A \\ = \\ E_n |E_n\\rangle_A$, thus making our TFD state depend on both the energy eigenstates and the energy eigenvalues. It is also important to note that $\\beta$ is defined as the inverse temperature of the system that we are describing, $\\beta \\ = \\ 1/T$. $Z(\\beta)$ denotes the partition function, which is given by:\n", "\n", "
\n", "$$Z(\\beta) \\ = \\ \\displaystyle\\sum_{n} e^{- \\beta E_n}$$\n", "
\n", "\n", "Finally, we define $E_n'\\rangle_B$ to be a vector in the state space described by the second qubit register, $B$, specifically, the time-reversed counterpart of $|E_n\\rangle_A$:\n", "\n", "
\n", "$$|E_n'\\rangle_B \\ = \\ \\Theta |E_n\\rangle_A \\ = \\ i Y K |E_n\\rangle_A$$\n", "
\n", "\n", "\n", "Where $K$ is the complex conjugation operator. Consider the density matrix corresponding to the general TFD state:\n", "\n", "
\n", "$$\\rho \\ = \\ |TFD\\rangle \\langle TFD| \\ = \\ \\frac{1}{Z(\\beta)} \\displaystyle\\sum_{m} \\displaystyle\\sum_{n} e^{-\\beta (E_n \\ + \\ E_m)/2} |E_n\\rangle \\langle E_m|_A \\ \\otimes \\ |E_n'\\rangle \\langle E_m'|_B$$\n", "
\n", "\n", "Now, what happens when we take the partial trace of the second register? Well, we get:\n", "\n", "
\n", "$$\\rho_A \\ = \\ \\frac{1}{Z(\\beta)} \\displaystyle\\sum_{m} \\displaystyle\\sum_{n} e^{-\\beta (E_n \\ + \\ E_m)/2} \\text{Tr} \\big( |E_n'\\rangle \\langle E_m'|_B \\big) |E_n\\rangle \\langle E_m|_A$$\n", "
\n", "\n", "Now, let's quickly prove a nice result that will help us simplify this. We will demonstrate that the trace of the outer product of two orthogonal vectors is $0$. Since trace is indepedent of a chosen basis, we can take the trace with respect to the matrix representation of our outer product in the time-reversed energy basis, which ends up being:\n", "\n", "
\n", "$$\\text{Tr}(|E_n'\\rangle \\langle E_m'|) \\ = \\ \\displaystyle\\sum_{j} \\langle E_j' | E_n' \\rangle \\langle E_m' | E_j' \\rangle$$\n", "
\n", "\n", "If $m \\ \\neq \\ n$, this sum clearly is $0$, as each term of the sum is $0$. If $m \\ = \\ n$, we will get:\n", "\n", "
\n", "$$\\displaystyle\\sum_{j} \\langle E_j' | E_n' \\rangle \\langle E_m' | E_j' \\rangle \\ = \\ | \\langle E_n' | E_n' \\rangle |^2 \\ = \\ 1$$\n", "
\n", "\n", "Thus:\n", "\n", "
\n", "$$\\text{Tr}(|E_n'\\rangle \\langle E_m'|) \\ = \\ \\delta_{mn}$$\n", "
\n", "\n", "With $\\delta_{mn}$ being the Kronecker Delta. Thus, we will have:\n", "\n", "
\n", "$$\\rho_A \\ = \\ \\frac{1}{Z(\\beta)} \\displaystyle\\sum_{m} \\displaystyle\\sum_{n} e^{-\\beta (E_n \\ + \\ E_m)/2} \\delta_{mn} |E_n\\rangle \\langle E_m|_A \\ = \\ \\frac{1}{Z(\\beta)} \\displaystyle\\sum_{n} e^{-\\beta E_n} |E_n\\rangle \\langle E_n|$$\n", "
\n", "\n", "Now, let's consider the matrix exponential of $H_A$, of the form $e^{-\\beta H_A}$. We have:\n", "\n", "
\n", "$$H_A \\ = \\ \\displaystyle\\sum_{n} E_n |E_n\\rangle \\langle E_n|$$\n", "
\n", "\n", "Now, we will have:\n", "\n", "
\n", "$$e^{-\\beta H_A} \\ = \\ \\displaystyle\\sum_{i \\ = \\ 0}^{\\infty} \\frac{1}{i!} \\Big( \\displaystyle\\sum_{n} - \\beta E_n |E_n\\rangle \\langle E_n| \\Big)^i$$\n", "
\n", "\n", "But since it is true that $|E_m\\rangle \\langle E_m| E_n \\rangle \\langle E_n| \\ = \\ 0$ when $m \\ \\neq \\ n$, and $|E_n\\rangle \\langle E_n| E_n \\rangle \\langle E_n| \\ = \\ 1$ we will have:\n", "\n", "
\n", "$$\\displaystyle\\sum_{i \\ = \\ 0}^{\\infty} \\frac{1}{i!} \\Big( \\displaystyle\\sum_{n} - \\beta E_n |E_n\\rangle \\langle E_n| \\Big)^i \\ = \\ \\displaystyle\\sum_{i \\ = \\ 0}^{\\infty} \\frac{1}{i!} \\Big( \\displaystyle\\sum_{n} (-\\beta E_n)^i (|E_n\\rangle \\langle E_n|)^i \\Big) \\ = \\ \\displaystyle\\sum_{i \\ = \\ 0}^{\\infty} \\frac{1}{i!} \\Big( \\displaystyle\\sum_{n} (- \\beta E_n)^i |E_n\\rangle \\langle E_n| \\Big) \\ = \\ \\displaystyle\\sum_{n} \\displaystyle\\sum_{i \\ = \\ 0}^{\\infty} \\frac{(- \\beta E_n)^i}{i!} |E_n\\rangle \\langle E_n \\rangle \\ = \\ \\displaystyle\\sum_{n} e^{- \\beta E_n} |E_n\\rangle \\langle E_n |$$\n", "
\n", "\n", "Which tells us that:\n", "\n", "
\n", "$$\\rho_A \\ = \\ \\frac{e^{-\\beta H_A}}{Z(\\beta)}$$\n", "
\n", "\n", "Which is exactly the Gibbs state for Hamiltonian $H_A$ and temperature $T \\ = \\ 1/ \\beta$. This is a pretty interesting result. The original TFD generation paper recognizes this fact, and says that because of this fact, the TFD state is equivalent to a thermal state with an effective heat bath given by the $B$ register. From a \"thermal perspective\", we have a Gibbs state that is coupled to a collection of qubits. We initialize the \n", "\n", "Let's consider what happens to the TFD state as $T \\ \\rightarrow \\ \\infty$. This knowledge will become useful in the next section. Temperature approaching infinity implies that we have $\\beta \\ \\rightarrow \\ 0$. This gives us:\n", "\n", "
\n", "$$|TFD\\rangle_{\\beta = 0} \\ = \\ \\frac{1}{\\sqrt{Z(0)}} \\displaystyle\\sum_{n} |E_n\\rangle \\ \\otimes \\ |E_n'\\rangle$$\n", "
\n", "\n", "But we also have:\n", "\n", "
\n", "$$Z(0) \\ = \\ \\displaystyle\\sum_{n} e^{0} \\ = \\ N$$\n", "
\n", "\n", "So this gives us:\n", "\n", "
\n", "$$|TFD\\rangle_{\\beta = 0} \\ = \\ \\frac{1}{\\sqrt{N}} \\displaystyle\\sum_{n} |E_n\\rangle \\ \\otimes \\ |E_n'\\rangle$$\n", "
\n", "\n", "Which is simply an even superposition over tensor products of energy eigenstates and time-reversed energy eigenstates of the Hamiltonian of the system.\n", "\n", "## 3. The Variational Ansatz\n", "\n", "Now that we better understand what exactly the TFD state is, let's disucuss how we wopuld go about preparing it. The circuit we will be using is variational, meaning that we will repeatedly prepare a statevector parametrized by classical parameters, calculate the expectation value of some cost function with respect to the prepared state, and continue this process of preparing a state and calculating the cost function until we find the minimum value of the cost function, with a classical optimizer. We will discuss the cost function later in the Notebook.\n", "\n", "The state preparation ansatz that is used in the paper is inspired by QAOA, in the sense that it involves alternating evolution between a \"cost\" unitary and a \"mixer\" unitary, mimicing Trotterized quantum adiabatic evolution, which in the limit of infinite depth will approach the ground state of the Hamiltonian of our system, $H_A \\ + \\ H_B$. We can then motivate the preparation of any TFD state from this approach, not just the case of the ground state with $T \\ \\rightarrow \\ 0$, as the zero-temperature TFD state is generally harder to prepare than other finite temperature TFD states, due to the complexity of the correlations present in each of the qubit registers, thus our circuit should surely be able to learn the more \"simple\" correlations of the non-zero temperature states.\n", "\n", "We can also motivate our ansatz from another, more \"physical\" perspective. As you will soon see, we will initialize the state of our qubits in an infinite-temperature state (that is maximally entangled). We can then think of the subsequent cost and mixer unitaries as \"cooling\" the infinite temperature state to the target temperature, with the cost unitary learning correlations within each register, while the mixer learns correlations between each register, each layer decoupling and lowering the effective temperature of the initially preparred thermal state.\n", "\n", "With this knowledge, it is not too difficult to write down the exact form of our QAOA unitary, which will look something like this:\n", "\n", "
\n", "$$U(\\boldsymbol\\gamma, \\boldsymbol\\alpha) \\ = \\ \\displaystyle\\prod_{i \\ = \\ 1}^{P} e^{- i \\alpha_i H_{C}} e^{- i \\gamma_i (H_A \\ + \\ H_B)} \\ = \\ \\displaystyle\\prod_{i \\ = \\ 1}^{P} e^{- i \\alpha_i H_{C}} e^{- i \\gamma_i H_A} e^{-i \\gamma_i H_B}$$\n", "
\n", "\n", "Where will will generally choose $H_C$ to be of the form:\n", "\n", "
\n", "$$H_C \\ = \\ \\displaystyle\\sum_{n} X^{A}_{n} X^{B}_{n} \\ + \\ Z^{A}_{n} Z^{B}_{n}$$ \n", "
\n", "\n", "From here, it also becomes clear how we should initialize our qubits:\n", "\n", "
\n", "$$|\\psi\\rangle \\ = \\ \\frac{|01\\rangle \\ - \\ |10\\rangle}{\\sqrt{2}}$$\n", "
\n", "\n", "There are actually a few different ways to motivate this initialization. Firstly, the inter-register coupling Hamiltonian $H_C$ has this state as a ground state, which gives motivation from the QAOA and adiabatic quantum evolution perspective. In addition, this state represents an infinite temperature TFD state. The idea is that layered applications of unitaries will effectively cool, and \"decouple\" the qubits until they converge on the TFD state that we are attempting to prepare. Finally, the chosen Bell state is already set up in such a way that tracing out the $A$ register would simply leave, in $B$, a time-reversed copy of the state present in register $A$, which is analogous to the structure of the TFD state.\n", "\n", "## 4. The Time-Reversed Hamiltonian\n", "\n", "Now that we know the general structure of our variational circuit, all that follows is to figure out is a small detail that we have yet to consider: what exactly is $H_B$? Well, we know that $H_B$ will be the Hamiltonian that has $|E_n'\\rangle \\ = \\ \\Theta |E_n\\rangle$ as eigenstates. It follows that $H_B$ can be written as:\n", "\n", "
\n", "$$H_B \\ = \\ -\\Theta H_A \\Theta$$\n", "
\n", "\n", "Since, given some $|E_n'\\rangle$, we will have:\n", "\n", "$$H_B |E_n'\\rangle \\ = \\ -\\Theta H_A \\Theta |E_n'\\rangle \\ = \\ \\Theta H_A |E_n\\rangle \\ = \\ \\Theta E_n |E_n\\rangle \\ = \\ E_n |E_n'\\rangle$$\n", "\n", "So the time-reversed states are eigenstates of this Hamiltonian. In addition, energy doesn't change under time-reversal, and as you can see, we retain the same energy $E_n$ for a given eigenstate under time-reversal, thus we have uniquely defined the time-reversed Hamiltonian!\n", "\n", "Now, let's consider what happens when we put this Hamiltonian into our exponential unitary:\n", "\n", "
\n", "$$e^{-i \\gamma H_B} \\ = \\ e^{i \\gamma \\Theta H_A \\Theta} \\ = \\ \\displaystyle\\sum_{n \\ = \\ 0}^{\\infty} \\frac{(i \\gamma \\Theta H_A \\Theta)^n}{n!}$$\n", "
\n", "\n", "Now, here is the key realization: $-\\Theta^2 \\ = \\ \\mathbb{I}$, as time-reversing a quantum state twice is equivalent to doing nothing. Therefore, all of the middle terms in the product expansion of the numerator of each term will cancel, and we will have:\n", "\n", "
\n", "$$\\displaystyle\\sum_{n \\ = \\ 0}^{\\infty} \\frac{(i \\gamma \\Theta H_A \\Theta)^n}{n!} \\ = \\ \\displaystyle\\sum_{n \\ = \\ 0}^{\\infty} -\\Theta \\frac{(-i \\gamma H_A )^n}{n!} \\Theta \\ = \\ - \\Theta e^{-i \\gamma H_A} \\Theta$$\n", "
" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "bX3Ufc-JlBbT" }, "source": [ "Now, you might be puzzled. $\\Theta$ in an antilinear operator, so how is it possible that what we have is even a unitary operation. Well, we start with the definition of time-reversal, which says that $\\Theta |\\Psi(t)\\rangle \\ = \\ |\\Psi(-t)\\rangle$. For time-evolution of an initial quantum state, we have:\n", "\n", "$$|\\Psi(t)\\rangle \\ = \\ e^{-iHt/\\hbar}|\\Psi(0)\\rangle$$\n", "\n", "We will also have:\n", "\n", "$$\\Theta |\\Psi(-t)\\rangle \\ = \\ |\\Psi(t)\\rangle \\ = \\ e^{-iHt/\\hbar} |\\Psi(0)\\rangle \\ = \\ e^{-iHt/\\hbar} \\Theta |\\Psi(0)\\rangle$$\n", "\n", "If we do a change of variables $t \\rightarrow -t$ in the first equation, we have:\n", "\n", "$$|\\Psi(-t)\\rangle \\ = \\ e^{iHt/\\hbar} |\\Psi(0)\\rangle$$\n", "\n", "Substituting into the second equation, we have:\n", "\n", "$$\\Theta |\\Psi(-t)\\rangle \\ = \\ \\Theta e^{iHt/\\hbar} |\\Psi(0)\\rangle \\ = \\ e^{-iHt/\\hbar} \\Theta |\\Psi(0)\\rangle$$\n", "\n", "Which leaves us with:\n", "\n", "$$\\Theta e^{iHt/\\hbar} \\ = \\ e^{-iHt/\\hbar} \\Theta \\ \\Rightarrow \\ e^{iHt/\\hbar} \\ = \\ - \\Theta e^{-iHt/\\hbar} \\Theta \\ = \\ Y K e^{-iHt/\\hbar} Y K$$\n", "\n", "Thus, the operator we end-off with is in fact unitary, given by:\n", "\n", "$$U \\ = \\ e^{i\\gamma H_A}$$\n", "\n", "Which makes sense, as this is the original time-evolution operator, but with the sign changed, signifying evolution backward in time. Thus, our time-reversed effective time-evolution under the time-reversed Hamiltonian is the same as time-evolution under the original Hamiltonian, with the sign on $t$ changed!" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "nAzlNo_5lBbT" }, "source": [ "## 5. A Simple Model\n", "\n", "We are going to start off by simulating the Thermofield Double State corresponding to a very simple uncoupled Hamiltonian of the form:\n", "\n", "
\n", "$$\\hat{H}_X \\ = \\ \\displaystyle\\sum_{k} \\ X_k$$\n", "
\n", "\n", "Where $X_k$ is a Pauli-X gate acting on the $k$-th qubit. This is a very simple model, but it provides a good place for us to start, to ensure that our simulations are functioning correctly, so we can eventually apply them to a more complex Ising model Hamiltonian.\n", "\n", "We can begin by defining all of the necessary variables to run our simulation:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "qqgA84KjlBbU" }, "outputs": [], "source": [ "# Defines the variables that define the TFD state\n", "beta = 1\n", "testing_trials = 100\n", "qubit_number = 3\n", "depth = 1" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "Qn8wK4tclBbY" }, "source": [ "Before we start building any quantum circuits, we are going to determine numerically what exactly our TFD state **should** look like, so we can ensure that our quantum circuit is outputting the correct state after we run our simulations.\n", "\n", "We can begin by writing a method that gives us the matrix representation of $\\hat{H}_X$ (in the computational basis) for any number of qubits:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "7x2yVuKSlBbZ" }, "outputs": [], "source": [ "# Calculates the cost Hamiltonian matrix for the X model\n", "def calculate_x_matrix(qubit_number):\n", "\n", " pauli_x = np.array([[0, 1], [1, 0]])\n", " pauli_z = np.array([[1, 0], [0, -1]])\n", " identity = np.array([[1, 0], [0, 1]])\n", "\n", " # Creates the transverse field component of the matrix\n", " total_matrix = np.zeros((2**qubit_number, 2**qubit_number))\n", "\n", " for i in range(0, qubit_number):\n", " matrix = 1\n", " for k in range(0, qubit_number):\n", " if (k == i):\n", " matrix = np.kron(matrix, pauli_x)\n", " else:\n", " matrix = np.kron(matrix, identity)\n", " total_matrix = np.add(total_matrix, matrix)\n", "\n", " return total_matrix" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "aLy0q8_UlBbd" }, "source": [ "And we can easily test it for a simple case:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 153 }, "colab_type": "code", "id": "MwahMVHmlBbd", "outputId": "39123109-7056-471c-92dc-962af69ecf32" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0. 1. 1. 0. 1. 0. 0. 0.]\n", " [1. 0. 0. 1. 0. 1. 0. 0.]\n", " [1. 0. 0. 1. 0. 0. 1. 0.]\n", " [0. 1. 1. 0. 0. 0. 0. 1.]\n", " [1. 0. 0. 0. 0. 1. 1. 0.]\n", " [0. 1. 0. 0. 1. 0. 0. 1.]\n", " [0. 0. 1. 0. 1. 0. 0. 1.]\n", " [0. 0. 0. 1. 0. 1. 1. 0.]]\n" ] } ], "source": [ "h_x = calculate_x_matrix(qubit_number)\n", "print(h_x)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "JZRPYnLAlBbi" }, "source": [ "One can verify that this is the correct answer, but we will not show these calculations as it simply involves summing a few matrices together.\n", "\n", "Recall that the TFD state is defined by the energy eigenvalues and eigenstates of the system in which it \"exists\". Thus, we will calculate the eignevalues and eigenvectors of the cost Hamiltonian, which we will eventually use to test our variational algorithm." ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "V7CBGGpTlBbi" }, "outputs": [], "source": [ "# Calculating the eigenvalues and eigenvectors of the cost Hamiltonian\n", "def find_eigenvec_eigenval(matrix):\n", "\n", " value, vector = np.linalg.eig(matrix)\n", "\n", " new_vector = []\n", " for v in range(0, len(vector)):\n", " holder = []\n", " for h in range(0, len(vector)):\n", " holder.append(vector[h][v])\n", " new_vector.append(holder)\n", "\n", " return [value, np.array(new_vector)]" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "xyQDIOAXlBbm" }, "source": [ "And now we can now utilize our method to find the energy eigenvalues/eigenstates (I'll only print out the eigenvalues, as the array storing the eigenvectors is fairly long):" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "id": "uxXe7TZKlBbn", "outputId": "6919f23a-487a-45ca-e257-05e97af7ef38" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 3. -3. -1. -1. -1. 1. 1. 1.]\n" ] } ], "source": [ "ev = find_eigenvec_eigenval(h_x)\n", "print(ev[0])" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "W-eEVKsZlBbs" }, "source": [ "With this newfound knowledge, we have all of the ingredients we need to create the TFD state corresponding to $\\hat{H}_X$ (remember that we already defined $\\beta$ earlier!). All we have to do is exponentiate $\\beta$ along with each $E_n$, then take the squares of each of these values and sum them together to get the value of the partition function. Finally, we append each of the exponential values to each pair of energy eigenstates, and take the sum:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "P18L94zUlBbs" }, "outputs": [], "source": [ "# Preaparing the partition function and each of the probability amplitudes of the diifferent terms in the TFD state\n", "def calculate_terms_partition(eigenvalues):\n", " list_terms = []\n", " partition_sum = 0\n", " for i in eigenvalues:\n", " list_terms.append(math.exp(-0.5*beta*i))\n", " partition_sum = partition_sum + math.exp(-1*beta*i)\n", "\n", " return [list_terms, math.sqrt(float(partition_sum))]" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "F4ANV8S6lBbx" }, "source": [ "The method above outputs each of the exponential terms, as well as the partiation function. Next, we will create a method that outputs the time evolution operator for a given $n$-qubit system:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "KH5cOPGilBbx" }, "outputs": [], "source": [ "#Preparring the TFD state for the cost function\n", "def time_reversal(n):\n", " y_gate = 1\n", " y = np.array([[0, -1], [1, 0]])\n", " for i in range(0, n):\n", " y_gate = np.kron(y_gate, y)\n", " \n", " return y_gate" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "bgZ00t82lBb1" }, "source": [ "Finally, we can create the method that builds the TFD state:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "5Dm1i0OplBb2" }, "outputs": [], "source": [ "# Constructs the TFD state\n", "def construct_tfd_state(qubit_number, matrix_function):\n", "\n", " matrix = matrix_function(qubit_number)\n", " eigen = find_eigenvec_eigenval(matrix)\n", " partition = calculate_terms_partition(eigen[0])\n", "\n", " vec = np.zeros(2**(2*qubit_number))\n", " for i in range(0, 2**qubit_number):\n", " \n", " time_rev = time_reversal(qubit_number)\n", " time_rev = complex(0,1)*np.matmul(time_rev, np.conj(eigen[1][i]))\n", " addition = (float(partition[0][i]/partition[1]))*(np.kron(eigen[1][i], time_rev))\n", " vec = np.add(vec, addition)\n", "\n", " return vec" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "W6yhSrnrlBb-" }, "source": [ "Now, we can visualize our state vector. One of the best ways to do this is by outputting a visual representation of the real component of the density matrix of the state. We can do this very easily with the help of a simple method:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 338 }, "colab_type": "code", "id": "6uyUBvyPlBcA", "outputId": "9a92bfc4-4763-45c8-bf06-f2e9a5a446ed" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "# Outputs a visual representation of a matrix\n", "def create_density_plot(data):\n", "\n", " array = np.array(data)\n", " plt.matshow(array)\n", " plt.colorbar()\n", " plt.show()\n", "\n", "tfd = construct_tfd_state(qubit_number, calculate_x_matrix)\n", "density_matrix = np.outer(tfd, np.conj(tfd))\n", "\n", "create_density_plot(np.real(density_matrix))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "0j5FGTlRlBcF" }, "source": [ "Now that we know what our TFD state should look like, let's build our variational quantum circuit and see if we can prepare a state similar to the one shown above. We can begin by defining a method that will prepare our register of maximally entangled Bell pairs. Recall that the motivation for this setup is that we begin in a state such that the two registers are maximally coupled (where the coupling is \"decreased\" with subsequent unitary evolution), with a time-reversal operator applied to the second qubit register (as when the first qubit register is traced out, we want to be left with a time-reversed copy of the first qubit register)." ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "us2jcgcxlBcF" }, "source": [ "We can begin our quantum simulation by writing the method that will initilize our $A$ and $B$ registers of qubits in maximally-entangled Bell pairs of the form:\n", "\n", "
\n", "$$|\\psi\\rangle \\ = \\ \\frac{|01\\rangle \\ - \\ |10\\rangle}{\\sqrt{2}}$$\n", "
\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "eWXGA-oAlBcG" }, "source": [ "This is not too difficult, as we are simply implementing the standard protocal for generating Bell states, followed by the application of an $X$ gate and a $Z$ gate on the second qubit register:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "4y8SFJRslBcG" }, "outputs": [], "source": [ "# Generates the Bell state\n", "def generate_bell_pairs(qubit_number, circ):\n", " q = int(qubit_number)\n", " \n", " for i in range(0, q):\n", " circ.h(i)\n", " circ.cx(i, q+i)\n", " circ.x(i)\n", " circ.z(i)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "39u2vN7ElBcL" }, "source": [ "We can also take a look at exactly what this circuit looks like:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 428 }, "colab_type": "code", "id": "gZdrAdNSlBcO", "outputId": "eaf13939-78cb-433e-a245-8dd3783599d8" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 15, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "circ = QuantumCircuit(2*qubit_number)\n", "generate_bell_pairs(qubit_number, circ)\n", "circ.draw()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "eD7qV6I-lBcT" }, "source": [ "Like we described in the first part of this Notebook, we now want to evolve the registers of quibts in the QAOA-like fashion, alternating between Trotterized time-evolution with respect to $H_A$, and its time-reversed couterpart, $H_B$, and time-evolution with respect to a general coupling Hamiltonian, which has the Bell state as its ground state. In this case, we choose the coupling Hamiltonian to be of the form:\n", "\n", "
\n", "$$H_C \\ = \\ \\displaystyle\\sum_{n} X^{A}_{n} X^{B}_{n} \\ + \\ Z^{A}_{n} Z^{B}_{n}$$\n", "
\n", "\n", "Which one can check has the Bell state we previously prepared as its ground state. We can thus create two methods that will apply the two different time-evolution unitaries:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "Wy2BYd3YlBcT" }, "outputs": [], "source": [ "# Creates the \"cost\" time-evolution unitary\n", "def x_cost_unitary(qubit_number, gamma, circ):\n", " \n", " # Applies the cost unitary to register A\n", " for i in range(0, qubit_number):\n", " circ.rx(gamma, i)\n", " \n", " # Applies the time-reversed cost unitary to register B\n", " for i in range(qubit_number, 2*qubit_number):\n", " circ.rx(-1*gamma, i)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "R4SDL3sRlBcX" }, "source": [ "We can also test out this circuit to see what exactly it looks like:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 428 }, "colab_type": "code", "id": "wJYg3xRZlBcX", "outputId": "eeea1376-98a3-427f-8a7a-73e18ec3ff74" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 17, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "circ = QuantumCircuit(2*qubit_number)\n", "x_cost_unitary(qubit_number, 0.5, circ)\n", "circ.draw()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "vllAU-cXlBcc" }, "source": [ "Now, we can create the mixer unitary, parametrized by $\\alpha$. Notice how we are only using two parameters. This is due to the fact that the Hamiltonian we are simulating is relatively simple, so we shouldn't need a high-dimensional space of parameters in order to converge on the optimal solution." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 428 }, "colab_type": "code", "id": "Lnyv8Z7LlBcc", "outputId": "608a637d-5854-48af-e32b-e5968e770282" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 18, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "# Creates the \"mixer\" unitary layer\n", "def x_mixer_unitary(qubit_number, alpha, circ):\n", " for i in range(0, qubit_number):\n", " circ.rzz(alpha, i, i+qubit_number)\n", " circ.rxx(alpha, i, i+qubit_number)\n", "\n", "circ = QuantumCircuit(2*qubit_number)\n", "x_mixer_unitary(qubit_number, 0.5, circ)\n", "circ.draw(output='mpl')" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "2Kq-F4QFlBcf" }, "source": [ "We can then put this all together into a cost function, which will have parameters inputted, and will output the cost. For this first simulation, we will be doing a state vector simulation. We will be utilizing a state-overlap cost function, meaning that we will calculate the fidelity of the preparred state with respect to the numerical form of the TFD state that we found in the previous section:\n", "\n", "
\n", "$$F(\\gamma, \\alpha) \\ = \\ |\\langle TFD | \\psi (\\gamma, \\ \\alpha)\\rangle|^2$$\n", "
\n", "\n", "You may be thinking: \"This cost function requires a priori knowledge of exactly what the TFD state is, so why even carry out this experiment in the first place?\" Well, recall that the goal of this experiment isn't to find out what the TFD state is, but rather, prepare it in a register of qubits so it can be used for other experiments (TFD states are of great interest in many areas of physics, so experimenting with them on quantum processors might be a great way to perform experiments involving them). With this knowledge, our cost function will be of the form:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 428 }, "colab_type": "code", "id": "4TzW76pdlBcf", "outputId": "160bd0d1-f4e1-4940-c216-eb999db5c54c" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 19, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "# Creates the full quantum circuit for arbitrary cost and unitary mixers\n", "def full_circuit(gamma, alpha, qubit_number, depth, cost, mixer, circ):\n", " generate_bell_pairs(qubit_number, circ)\n", " for i in range(0, depth):\n", " cost(qubit_number, gamma[i], circ)\n", " mixer(qubit_number, alpha[i], circ)\n", "\n", "circ = QuantumCircuit(2*qubit_number)\n", "full_circuit([0.3], [0.5], qubit_number, 1, x_cost_unitary, x_mixer_unitary, circ)\n", "circ.draw(output='mpl')" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "0z2cKDmelBcj" }, "source": [ "Finally, we can create the actual cost function:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "Ov0fa8mDlBck" }, "outputs": [], "source": [ "# Creates the cost function\n", "counter = 0\n", "\n", "def cost_function(params):\n", " global counter\n", " circ = QuantumCircuit(2*qubit_number)\n", " \n", " full_circuit(params[0:1], params[1:2], qubit_number, 1, x_cost_unitary, x_mixer_unitary, circ)\n", " \n", " backend = Aer.get_backend('statevector_simulator')\n", " job = execute(circ, backend)\n", " result = job.result()\n", " outputstate = result.get_statevector(circ, decimals=100)\n", " \n", " inner_prod = np.dot(np.conj(tfd), outputstate)\n", " fidelity = np.real(np.conj(inner_prod)*inner_prod)\n", " \n", " if (counter%50 == 0):\n", " print(\"Fidelity at Step \"+str(counter)+\": \"+str(fidelity))\n", " \n", " counter += 1\n", " \n", " return (1-np.real(fidelity))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "epZmH8MClBco" }, "source": [ "Now, we have everything we need to prepare our TFD state. Let's create the optimization method and run it!" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 372 }, "colab_type": "code", "id": "C9A3ygS4lBcp", "outputId": "17b39fe9-6bd5-4f94-fd07-1dec4ffef52b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fidelity at Step 0: 0.003781142656392626\n", "Fidelity at Step 50: 0.9999999599953697\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "# Creates the optimizer\n", "initial = [random.randint(-314, 314)/100 for i in range(0, 2)]\n", "out = minimize(cost_function, x0=initial, method=\"COBYLA\", options={'maxiter':200})\n", "\n", "optimal_params = out['x']\n", "\n", "# Finally, we create the visual representation of the density matrix of the preparred state\n", "circ = QuantumCircuit(2*qubit_number)\n", "full_circuit(optimal_params[0:1], optimal_params[1:2], qubit_number, 1, x_cost_unitary, x_mixer_unitary, circ)\n", "\n", "backend = Aer.get_backend('statevector_simulator')\n", "job = execute(circ, backend)\n", "result = job.result()\n", "outputstate = result.get_statevector(circ, decimals=100)\n", "\n", "final_density_matrix = np.outer(outputstate, np.conj(outputstate))\n", "create_density_plot(np.real(final_density_matrix))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "xZRs8UZ6lBct" }, "source": [ "After only $50$ steps, the algorithm converged one the optimal state. If we compare the density matrix we generated above to the one we generated earlier in the Notebook, we see that they are identical!" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "fDHN4kkklBcu" }, "source": [ "## 6. A Slightly More Complicated Hamiltonian: The Ising Model\n", "\n", "Now, with our simple example done, we can turn our attention to a system that is slightly more complicated (since it involves coupling between qubits). This is the Ising model Hamiltonian, which is given by:\n", "\n", "
\n", "$$H_I \\ = \\ \\displaystyle\\sum_{i} X_i X_{i+1} \\ + \\ \\displaystyle\\sum_{j} Z_j$$\n", "
\n", "\n", "We will follow a fairly similar process to what we did in the previous section. First, we will numerically generate the TFD state for a new value of inverse temperature, $\\beta \\ = \\ 0.3$. We start by defining a method that outputs the matrix form (in the computational basis) of the Ising model Hamiltonian:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "Hkqt1OzFlBcv" }, "outputs": [], "source": [ "# Defines beta\n", "beta = 0.3\n", "\n", "# Defines a method that generates an Ising model Hamiltonian\n", "def calculate_ising_matrix(qubit_number):\n", " pauli_x = np.array([[0, 1], [1, 0]])\n", " pauli_z = np.array([[1, 0], [0, -1]])\n", " identity = np.array([[1, 0], [0, 1]])\n", "\n", " # Creates the transverse field component of the matrix\n", " total_matrix = np.zeros((2**qubit_number, 2**qubit_number))\n", " for i in range(0, qubit_number):\n", " matrix = 1\n", " for k in range(0, qubit_number):\n", " if (k == i or k == (i+1)%qubit_number):\n", " matrix = np.kron(matrix, pauli_x)\n", " else:\n", " matrix = np.kron(matrix, identity)\n", " total_matrix = np.add(total_matrix, matrix)\n", " \n", " for i in range(0, qubit_number):\n", " matrix = 1\n", " for k in range(0, qubit_number):\n", " if (k == i):\n", " matrix = np.kron(matrix, pauli_z)\n", " else:\n", " matrix = np.kron(matrix, identity)\n", " total_matrix = np.add(total_matrix, matrix)\n", "\n", " return total_matrix" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "GdX-y0z4lBcz" }, "source": [ "For our system, this matrix will look like this:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 153 }, "colab_type": "code", "id": "BoyoklAJlBc1", "outputId": "fffb454a-8d57-4f56-e73d-4659f6a1a880" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 3. 0. 0. 1. 0. 1. 1. 0.]\n", " [ 0. 1. 1. 0. 1. 0. 0. 1.]\n", " [ 0. 1. 1. 0. 1. 0. 0. 1.]\n", " [ 1. 0. 0. -1. 0. 1. 1. 0.]\n", " [ 0. 1. 1. 0. 1. 0. 0. 1.]\n", " [ 1. 0. 0. 1. 0. -1. 1. 0.]\n", " [ 1. 0. 0. 1. 0. 1. -1. 0.]\n", " [ 0. 1. 1. 0. 1. 0. 0. -3.]]\n" ] } ], "source": [ "h_ising = calculate_ising_matrix(qubit_number)\n", "print(h_ising)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "zkSoZN0UlBc4" }, "source": [ "Now, we can simply use the function that we defined previously to generate the density matrix plot of the TFD state corresponding to this Hamiltonian:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 339 }, "colab_type": "code", "id": "KicqnB5plBc5", "outputId": "fc5d23e1-7aae-4180-fbc0-34e286f83125" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "tfd = construct_tfd_state(qubit_number, calculate_ising_matrix)\n", "density_matrix = np.outer(tfd, np.conj(tfd))\n", "create_density_plot(np.real(density_matrix))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "4kMZMr2HlBc-" }, "source": [ "Unfortunately, this density matrix plot isn't as \"exotic looking\" as the first one. Now, we simply have to do another quantum simulation. Since we have already defined a lot of the machinery we need to make our simulation, all we have left to do is construct a new cost unitary. Before we do this, let us bring up an important fact from the TFD paper: the authors found that evolving the second qubit register with respect to the time-reversed Hamiltonian did not have a great effect on the convergence of the algorithm (it is often the case with parametrized algorithms that the degrees of freedom introduced by the parameters themselves allow circuit like QAOA, with physical motivation from AQC, to be truncated). Thus, we construct our cost unitary as follows:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 246 }, "colab_type": "code", "id": "d2TrrgkDlBc_", "outputId": "cf68cc1c-2020-4019-f194-c630d97d9b1a" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 25, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "# Defines the new cost unitary for our Ising model\n", "def ising_cost_unitary(qubit_number, gamma, circ):\n", " for i in range(0, qubit_number):\n", " circ.rz(gamma, i)\n", " for i in range(0, qubit_number):\n", " circ.rxx(gamma, i, (i+1)%qubit_number)\n", "\n", "circ = QuantumCircuit(qubit_number)\n", "ising_cost_unitary(qubit_number, 0.5, circ)\n", "circ.draw(output='mpl')" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "UQ8pnwgrlBdD" }, "source": [ "Our complete circuit will look something like this:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 428 }, "colab_type": "code", "id": "CMDnoVFJlBdE", "outputId": "a0320d47-1d00-4e93-9061-ec79e48cb083" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 26, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "circ = QuantumCircuit(2*qubit_number)\n", "full_circuit([0.5], [0.5], qubit_number, 1, ising_cost_unitary, x_mixer_unitary, circ)\n", "circ.draw(output='mpl')" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "xzD-WhQnlBdL" }, "source": [ "Now, we define our new quantum simulation. First, we define the new cost function, with the new circuit. For the case of this simulation, we are going to run our variational circuit with depth equal to $2$, and different values of $\\gamma$ and $\\alpha$ for the two layers. With a depth one circuit, we observed a fidelity of around $0.94$, whereas with depth two, we get a fidelity of around $0.99$." ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "BjqnLIQQlBdM" }, "outputs": [], "source": [ "# Creates the Ising model cost function\n", "counter = 0\n", "\n", "def cost_function(params):\n", " global counter\n", " circ = QuantumCircuit(2*qubit_number)\n", " \n", " full_circuit(params[0:2], params[2:4], qubit_number, 2, ising_cost_unitary, x_mixer_unitary, circ)\n", " \n", " backend = Aer.get_backend('statevector_simulator')\n", " job = execute(circ, backend)\n", " result = job.result()\n", " outputstate = result.get_statevector(circ, decimals=100)\n", " \n", " inner_prod = np.dot(np.conj(tfd), outputstate)\n", " fidelity = np.real(np.conj(inner_prod)*inner_prod)\n", " \n", " if (counter%50 == 0):\n", " print(\"Fidelity at Step \"+str(counter)+\": \"+str(fidelity))\n", " \n", " counter += 1\n", " \n", " return (1-np.real(fidelity))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "xCaVgsTvlBdW" }, "source": [ "Then, we create the optimizer and run the circuit:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 406 }, "colab_type": "code", "id": "Hs3LEz6AlBdW", "outputId": "b4773eec-290e-407c-d76f-c66995c27a39" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fidelity at Step 0: 0.015088359370278834\n", "Fidelity at Step 50: 0.9915004967970318\n", "Fidelity at Step 100: 0.9918406839944007\n", "Fidelity at Step 150: 0.9919014432948825\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "# Creates the optimizer\n", "initial = [random.randint(-314, 314)/100 for i in range(0, 4)]\n", "out = minimize(cost_function, x0=initial, method=\"COBYLA\", options={'maxiter':200})\n", "\n", "optimal_params = out['x']\n", "\n", "# Finally, we create the visual representation of the density matrix of the preparred state\n", "circ = QuantumCircuit(2*qubit_number)\n", "full_circuit(optimal_params[0:2], optimal_params[2:4], qubit_number, 2, ising_cost_unitary, x_mixer_unitary, circ)\n", "\n", "backend = Aer.get_backend('statevector_simulator')\n", "job = execute(circ, backend)\n", "result = job.result()\n", "outputstate = result.get_statevector(circ, decimals=100)\n", "\n", "final_density_matrix = np.outer(outputstate, np.conj(outputstate))\n", "create_density_plot(np.real(final_density_matrix))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "4o03L_VwlBdi" }, "source": [ "Thus, one can see that it is in fact possible to generate the TFD state to a very high degree of accuracy, even with the removal of certain parts of the adiabatic ansatz and a relatively low circuit depth!\n", "\n", "## 7. Running the Algorithm on the IBM Device\n", "\n", "Now, to complete this Notebook, let's attempt to run the algorithm on real quantum hardware! First, we will attempt to do a sampling simulation with noise, embedded on the device that we will eventually use for the real experiment. Once we confirm that everything is working properly with this simulation, we will do our hardware run! For this implementation, we will attempt to generate the TFD state of the $X$-model Hamiltonian that we calculated in the previous section. First, we have to make a few changes. Namely, we have to do something about the fact that currently, we are using state fidelity as a cost function, which depends upon knowledge of the entire quantum state. We are still going to use fidelity as our cost function, except now, we have to reconstruct the state vector from measurements. We will do this using quantum state tomography, with Qiskit Ignis. We will write a method to do this for us:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "XXzf9--WlBdl" }, "outputs": [], "source": [ "provider = IBMQ.load_account()\n", "device = provider.get_backend('ibmq_ourense')\n", "coupling_map = device.configuration().coupling_map\n", "\n", "# We then create a noise model that mimics the noisy behaviour of the real quantum device\n", "noise_model = NoiseModel.from_backend(device)\n", "basis_gates = noise_model.basis_gates\n", "\n", "def get_state(circuit, reg, backend, layout):\n", " measurement_circuits = state_tomography_circuits(circuit, reg)\n", " \n", " # Runs the measurements\n", " job = execute(measurement_circuits, backend, initial_layout=layout, coupling_map=coupling_map, \n", " noise_model=noise_model,\n", " basis_gates=basis_gates)\n", " result = job.result()\n", " matrix = StateTomographyFitter(result, measurement_circuits)\n", " \n", " # Performs state tomography on all of the collected data\n", " return matrix.fit()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "2pwxfE8QlBd5" }, "source": [ "Basically, the `state_tomography_circuit` method returns a list of circuits we must run to perform tomography. We then create an intance of the \"fitter\" which acts as an optimization method that approximates the quantum state." ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "QvKTNOHglBd6" }, "source": [ "We can test this for a basic state. Let's consider the basic case of a Hadamard gate acting on one qubit:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 51 }, "colab_type": "code", "id": "xEGQeuoklBd7", "outputId": "bb9a169c-860e-4e7a-c017-52118f298ff5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.52148437+0.j 0.48925781+0.01171875j]\n", " [0.48925781-0.01171875j 0.47851563+0.j ]]\n" ] } ], "source": [ "reg = QuantumRegister(1)\n", "circ = QuantumCircuit(reg)\n", "circ.h(0)\n", "\n", "backend = Aer.get_backend('qasm_simulator')\n", "layout = {reg[0]: 0} # Creates a basic embedding scheme\n", "rho = get_state(circ, reg, backend, layout) # Performs tomography\n", "print(rho)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "sedmwQfulBd-" }, "source": [ "This is clearly a good approximation of the correct density matrix, as in the $Z$-basis we will have:\n", "\n", "
\n", "$$\\rho \\ = \\ \\Big(\\frac{|0\\rangle \\ + \\ |1\\rangle}{\\sqrt{2}} \\Big) \\Big(\\frac{\\langle0| \\ + \\ \\langle1|}{\\sqrt{2}} \\Big) \\ = \\ \\frac{1}{2} \\begin{pmatrix} 1 & 1 \\\\ 1 & 1 \\end{pmatrix}$$\n", "
" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "-MBIXpHilBeC" }, "source": [ "Now, before we do an actual hardware run, let's test out our state reconstruction method on a simulation of the TFD generation algorithm, with the sampling QASM simulator, as we said we would do. This doesn't require much work, all we have to do is modify the cost function, so that it uses the QASM backend. First, we have to construct the numerical value of the basic TFD state, as we have done in the past sections:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 338 }, "colab_type": "code", "id": "jTNd7hLLlBeD", "outputId": "c638c2f0-1e16-424c-a147-a8f2639c76d3" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "beta = 1\n", "\n", "tfd = construct_tfd_state(2, calculate_x_matrix)\n", "density_matrix = np.outer(tfd, np.conj(tfd))\n", "\n", "create_density_plot(np.real(density_matrix))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "iIEYwAPRlBeK" }, "source": [ "For the purposes of these new simulations, we are going to create slightly modified cost and mixer unitaries that depend on more parameters than we used in past iterations. This is due to the fact that we want to make it \"easier\" for the algorithm to converge onto the optimal solution, as in these runs, we will have to overcome precision errors due to finite sampling, as well as noise. " ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "OzuUSyW0lBeL" }, "outputs": [], "source": [ "# Creates the new \"cost\" time-evolution unitary\n", "def new_x_cost_unitary(qubit_number, gamma, circ):\n", " \n", " # Applies the cost unitary to register A\n", " for i in range(0, qubit_number):\n", " circ.rx(gamma[0], i)\n", " \n", " # Applies the time-reversed cost unitary to register B\n", " for i in range(qubit_number, 2*qubit_number):\n", " circ.rx(-1*gamma[1], i)" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "tuFkRuqglBeQ" }, "outputs": [], "source": [ "# Creates the new \"mixer\" unitary layer\n", "def new_x_mixer_unitary(qubit_number, alpha, circ):\n", " for i in range(0, qubit_number):\n", " circ.rzz(alpha[0], i, i+qubit_number)\n", " circ.rxx(alpha[1], i, i+qubit_number)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "hgzRjw3XlBeX" }, "source": [ "Finally, we create the new cost function with the new cost and mixer unitaries, as well as the dictionary storing the embedding scheme for our experiment, on the quantum device. Notice how in our new cost function, our cost function is a bit different as well. This is due to the fact that our state tomography porcess returns a density matrix. We can't calculate fidelity in the usual way, using the inner product with matrices, so we have to use another relative metric between different mixed states: **the trace distance**. This is defined as:\n", "\n", "
\n", "$$D(\\rho, \\ \\sigma) \\ = \\ \\frac{1}{2} \\text{Tr} \\sqrt{(\\rho \\ - \\ \\sigma)^{\\dagger} (\\rho \\ - \\ \\sigma)}$$\n", "
\n", "\n", "The more similar the quantum states are, the closer $D$ is to $0$. Thus, our \"effective fidelity\" between mixer states is $1 \\ - \\ D(\\rho, \\ \\sigma)$, which is what we've implmented below. Also note that the density matrix of a pure state is simply given by the outer product of that state with itself. Thus, we have:\n", "\n", "
\n", "$$\\rho_{TFD} \\ = \\ |TFD\\rangle \\langle TFD|$$\n", "
\n", "\n", "We can implement this along with the rest of the new cost function as follows:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "ocQ3M6lilBeY" }, "outputs": [], "source": [ "# Creates the cost function\n", "counter = 0\n", "\n", "def cost_function(params):\n", " global counter\n", " reg = QuantumRegister(4)\n", " circ = QuantumCircuit(reg)\n", "\n", " full_circuit([params[0:2]], [params[2:4]], 2, 1, new_x_cost_unitary, new_x_mixer_unitary, circ)\n", "\n", " layout = {reg[0]: 4, reg[1]: 3, reg[2]: 1, reg[3]: 0} # Embeds our qubits on the processor in a \"nice\" way\n", " rho = get_state(circ, reg, backend, layout)\n", "\n", " # Calculates the trace distance\n", " new_matrix = np.add(rho, -1*density_matrix)\n", " sqrt = scipy.linalg.sqrtm(np.matmul(np.conj(new_matrix), new_matrix))\n", " fidelity = 1 - 0.5*np.trace(sqrt)\n", "\n", " print(\"Fidelity at Step \"+str(counter)+\": \"+str(np.real(fidelity))+\" - \"+str(params))\n", "\n", " counter += 1\n", "\n", " return (1-np.real(fidelity))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "XU-Yuc1alBed" }, "source": [ "Now, as we said earlier, we want to attempt to simulate our algorithm on the QASM simulator, but in the exact noisy conditions that we will see on the real quantum device, in order to ensure everything is working before we do our real hardware run. We have defined coupling, a noise model, etc. so all we need. to do now is define our backend and begin the simulation. We are also going to run our simulation for a low number of shots, since this is all we will be able to do on the real device:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 714 }, "colab_type": "code", "id": "WZa26HQglBed", "outputId": "657d90e2-59cd-4dec-825e-cf73ecc1459a", "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fidelity at Step 0: 0.6246933929930509 - [-1.48 0.92 0.86 -3. ]\n", "Fidelity at Step 1: 0.27290401265248765 - [-0.48 0.92 0.86 -3. ]\n", "Fidelity at Step 2: 0.2674222203330727 - [-1.48 1.92 0.86 -3. ]\n", "Fidelity at Step 3: 0.6869593080452252 - [-1.48 0.92 1.86 -3. ]\n", "Fidelity at Step 4: 0.6928272451307216 - [-1.48 0.92 1.86 -2. ]\n", "Fidelity at Step 5: 0.3185787929998285 - [-2.1762238 0.21292722 1.98323002 -1.98838681]\n", "Fidelity at Step 6: 0.540722385263101 - [-1.01542934 0.7379066 1.89173559 -1.99700924]\n", "Fidelity at Step 7: 0.6419969503676517 - [-1.48 0.96292348 2.10628759 -2. ]\n", "Fidelity at Step 8: 0.5053938066526371 - [-1.54713442 1.35562382 1.62398275 -1.99544818]\n", "Fidelity at Step 9: 0.7780209876977459 - [-1.65078834 0.74053553 1.82652676 -1.99815902]\n", "Fidelity at Step 10: 0.6775914475560549 - [-1.86468584 0.67897124 1.71274993 -1.99461907]\n", "Fidelity at Step 11: 0.7949612717844139 - [-1.64854872 0.73965769 1.82667975 -1.87318226]\n", "Fidelity at Step 12: 0.7671208690719667 - [-1.51195734 0.53109815 1.82897156 -1.85473066]\n", "Fidelity at Step 13: 0.7588558905114005 - [-1.67829993 0.71775549 1.93874763 -1.91442483]\n", "Fidelity at Step 14: 0.7862272978466109 - [-1.66700938 0.78328694 1.8387251 -1.62800763]\n", "Fidelity at Step 15: 0.7688565585898386 - [-1.55739881 0.82148531 1.80537539 -1.88610736]\n", "Fidelity at Step 16: 0.7612848773757344 - [-1.72176406 0.72090588 1.72726862 -1.8786878 ]\n", "Fidelity at Step 17: 0.7828565471456201 - [-1.65363528 0.74655194 1.83771654 -1.81226399]\n", "Fidelity at Step 18: 0.7819324692863429 - [-1.61258769 0.68932426 1.81897632 -1.86868134]\n", "Fidelity at Step 19: 0.7799579950688262 - [-1.66873184 0.74020625 1.80384522 -1.88007335]\n", "Fidelity at Step 20: 0.7814077359868087 - [-1.63885665 0.74805956 1.81787631 -1.8717289 ]\n", "Fidelity at Step 21: 0.7692959985491155 - [-1.65639421 0.73587293 1.85470691 -1.88391376]\n", "Fidelity at Step 22: 0.7703789137310234 - [-1.65290913 0.73847833 1.82809628 -1.85829166]\n", "Fidelity at Step 23: 0.7786915668126713 - [-1.64248161 0.72593214 1.82334466 -1.87597822]\n", "Fidelity at Step 24: 0.7773245202997036 - [-1.65423586 0.74041717 1.82264366 -1.87662097]\n", "Fidelity at Step 25: 0.7747151054684588 - [-1.64617602 0.74163065 1.83336987 -1.87578134]\n", "Fidelity at Step 26: 0.7741711170062187 - [-1.6493053 0.73916848 1.82687001 -1.8693861 ]\n", "Fidelity at Step 27: 0.7783131090650253 - [-1.64812892 0.74145318 1.82613437 -1.87283987]\n", "Fidelity at Step 28: 0.7795850104894817 - [-1.64656874 0.73676254 1.82556024 -1.87448737]\n", "Fidelity at Step 29: 0.777396353016361 - [-1.65025889 0.7393714 1.82673674 -1.87407938]\n", "Fidelity at Step 30: 0.7829425961455782 - [-1.64792924 0.7396991 1.82851998 -1.87338914]\n", "Fidelity at Step 31: 0.7814718585841358 - [-1.64796763 0.73904221 1.82621941 -1.8733413 ]\n", "Fidelity at Step 32: 0.7827139742142198 - [-1.64882272 0.73943412 1.82677694 -1.87227718]\n", "Fidelity at Step 33: 0.7781029161343342 - [-1.64893266 0.73967644 1.82668347 -1.87348333]\n", "Fidelity at Step 34: 0.7834984101459561 - [-1.64851925 0.73954082 1.82688781 -1.87322455]\n", "Fidelity at Step 35: 0.7778679704619859 - [-1.64833219 0.74006633 1.82655574 -1.87308653]\n", "Fidelity at Step 36: 0.7728700502073582 - [-1.64864958 0.73958688 1.82661834 -1.87298065]\n", "Fidelity at Step 37: 0.7666230031054826 - [-1.64843318 0.73954566 1.82653433 -1.87329432]\n", "Fidelity at Step 38: 0.7819721871659717 - [-1.64852765 0.7397517 1.82670628 -1.87317848]\n", "Fidelity at Step 39: 0.7686637574117957 - [-1.64859416 0.73966636 1.82668754 -1.87319957]\n", "[-1.64854872 0.73965769 1.82667975 -1.87318226]\n" ] } ], "source": [ "# Defines the backend\n", "backend = Aer.get_backend('qasm_simulator')\n", "\n", "# Creates the optimizer\n", "initial = [random.randint(-314, 314)/200 for i in range(0, 2)]\n", "initial += [random.randint(-314, 314)/100 for i in range(0, 2)]\n", "out = minimize(cost_function, x0=initial, method=\"COBYLA\", options={'maxiter':40})\n", "\n", "optimal_params = out['x']\n", "print(optimal_params)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "5xEGWaAnlBeh" }, "source": [ "We can print our a visual representation of the optimal circuit:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 307 }, "colab_type": "code", "id": "q81i-fdYlBel", "outputId": "fe95ab68-aeb7-4945-cd04-9b6df5afaf1e" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "execution_count": 38, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "# Optimal circuit\n", "circ = QuantumCircuit(4)\n", "full_circuit([optimal_params[0:2]], [optimal_params[2:4]], 2, 1, new_x_cost_unitary, new_x_mixer_unitary, circ)\n", "circ.draw(output='mpl')" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "wgxLK5enlBep" }, "source": [ "Now, we can create a visual representation of the state that we preparred, and calculate the fidelity between it, andthe numerical value of the TFD state!" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 355 }, "colab_type": "code", "id": "VeMgf1l7lBep", "outputId": "0470db51-e12c-4a55-a58b-b78a97f17b47" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Final Fidelity: (0.9796145872499168+0j)\n" ] } ], "source": [ "# Finally, we can create a visual representation of the preparred state\n", "backend = Aer.get_backend('statevector_simulator')\n", "job = execute(circ, backend)\n", "result = job.result()\n", "outputstate = result.get_statevector(circ, decimals=100)\n", "\n", "final_density_matrix = np.outer(outputstate, np.conj(outputstate))\n", "create_density_plot(np.real(final_density_matrix))\n", "\n", "print(\"Final Fidelity: \"+str(np.dot(np.conj(tfd), outputstate)*np.dot(tfd, np.conj(outputstate))))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "jbAI7DdWlBe8" }, "source": [ "So this appears to be working very well, even when faced with noise! Now, all that is left is to run the algorithm on a real quantum computer. In order to do this, we will have to modify our \"get state\" method, so that it can be run on a real quantum backend. The IBM quantum devices are limited to a maximum number of executions of $75$ circuits per run. In a tomography experiment, we need to run $3^n$ circuits to approximate an $n$ qubit state vector. For $4$ qubits, this is $81$ circuits, which is too many for onerun. Thus, we batch our inputs into jobs of $40$ and $41$ circuit runs, then combine them to form a final density matrix:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "apU8Ke5IlBe9" }, "outputs": [], "source": [ "def get_state(circuit, reg, backend):\n", " number = len(reg)\n", " measurement_circuits = state_tomography_circuits(circuit, reg)\n", " layout = {reg[0]: 4, reg[1]: 3, reg[2]: 1, reg[3]: 0}\n", " \n", " trials = (3**number - 1) / 2\n", " \n", " # First batch\n", " job = execute(measurement_circuits[0:trials], backend, initial_layout=layout)\n", " result = job.result()\n", " matrix = StateTomographyFitter(result, measurement_circuits[0:trials])\n", " \n", " #Second batch\n", " job = execute(measurement_circuits[trials:(3**number)], backend, initial_layout=layout)\n", " result = job.result()\n", " matrix.add_data(result, measurement_circuits[trials:(3**number)])\n", " \n", " return matrix.fit()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "TbMFwMzXlBfB" }, "source": [ "Finally, we get the device and run the optimization method for $40$ iterations:" ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "2E2P76iblBfB", "outputId": "722920b8-6ae7-4988-ac5d-f52bb10ecb4f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fidelity at Step 1: 0.4527829072935565 - [ 0.585 -0.63 0.22 2.93 ]\n", "Fidelity at Step 2: 0.4887367981654336 - [ 1.585 -0.63 0.22 2.93 ]\n", "Fidelity at Step 3: 0.3394473388708993 - [1.585 0.37 0.22 2.93 ]\n", "Fidelity at Step 4: 0.19539388300207838 - [ 1.585 -0.63 1.22 2.93 ]\n", "Fidelity at Step 5: 0.4653897676413743 - [ 1.585 -0.63 0.22 3.93 ]\n", "Fidelity at Step 6: 0.6207959595838854 - [ 1.69331885 -1.07976667 -0.66375876 2.85966204]\n", "Fidelity at Step 7: 0.3989331202153723 - [ 1.90577312 -1.96192944 -1.06077166 2.72170266]\n", "Fidelity at Step 8: 0.6704389659888337 - [ 1.72547242 -0.69901544 -0.98556414 2.83878278]\n", "Fidelity at Step 9: 0.6173181140184029 - [ 1.84435676 -0.68730467 -1.46490715 2.76158401]\n", "Fidelity at Step 10: 0.5895805667687782 - [ 1.95987043 -0.66531212 -0.91933958 2.7936611 ]\n", "Fidelity at Step 11: 0.6471755383413466 - [ 1.3432026 -0.53127244 -0.93858091 2.56762849]\n", "Fidelity at Step 12: 0.6744782048820795 - [ 1.51704881 -0.57454233 -1.01372768 3.27498555]\n", "Fidelity at Step 13: 0.6509843405359511 - [ 1.461522 -0.69768428 -1.22408646 3.27356541]\n", "Fidelity at Step 14: 0.6759648008910881 - [ 1.52216986 -0.67846041 -0.95448935 3.31091085]\n", "Fidelity at Step 15: 0.6351497833839029 - [ 1.31340962 -0.66883491 -0.85231459 3.2193312 ]\n", "Fidelity at Step 16: 0.6530536760658288 - [ 1.73899732 -0.62387676 -0.90904263 3.41309241]\n", "Fidelity at Step 17: 0.6620855651554992 - [ 1.52136758 -0.73999147 -1.06250504 3.32398363]\n", "Fidelity at Step 18: 0.6632071666324763 - [ 1.49359186 -0.66275626 -0.95677585 3.36418092]\n", "Fidelity at Step 19: 0.6397903503618447 - [ 1.51593779 -0.69221203 -0.89886107 3.199994 ]\n", "Fidelity at Step 20: 0.64669873812227 - [ 1.58181743 -0.66897892 -0.95111928 3.32663221]\n", "Fidelity at Step 21: 0.6578439935933543 - [ 1.51715544 -0.6950094 -0.98023228 3.30705717]\n", "Fidelity at Step 22: 0.64517940562151 - [ 1.52264126 -0.69139744 -0.94692195 3.31530243]\n", "Fidelity at Step 23: 0.668863787854097 - [ 1.51520724 -0.6521659 -0.9578474 3.29589731]\n", "Fidelity at Step 24: 0.6648356223954741 - [ 1.5353273 -0.67348465 -0.95929381 3.31572552]\n", "Fidelity at Step 25: 0.6750383712318648 - [ 1.51839952 -0.67613079 -0.95618175 3.31711797]\n", "Fidelity at Step 26: 0.6705339041772453 - [ 1.51647098 -0.68235866 -0.94047758 3.31054021]\n", "Fidelity at Step 27: 0.6676131158981609 - [ 1.5192832 -0.67276339 -0.9551646 3.30646221]\n", "Fidelity at Step 28: 0.6659405005193388 - [ 1.52039451 -0.68500307 -0.95836747 3.31071822]\n", "Fidelity at Step 29: 0.6548769640655729 - [ 1.5222852 -0.67889003 -0.9507586 3.31197968]\n", "Fidelity at Step 30: 0.6587654135650606 - [ 1.52505342 -0.67732384 -0.95677776 3.31155522]\n", "Fidelity at Step 31: 0.6591397181708992 - [ 1.52107694 -0.67972147 -0.95539326 3.31044941]\n", "Fidelity at Step 32: 0.6479104282527792 - [ 1.52259672 -0.67867046 -0.95428923 3.31008182]\n", "Fidelity at Step 33: 0.6597662526870762 - [ 1.5210519 -0.67694949 -0.95467412 3.31140868]\n", "Fidelity at Step 34: 0.6707445526687824 - [ 1.52260406 -0.67863001 -0.95484612 3.3116913 ]\n", "Fidelity at Step 35: 0.6651471208537352 - [ 1.52187581 -0.67879766 -0.95371445 3.31130201]\n", "Fidelity at Step 36: 0.6725398830438314 - [ 1.52192703 -0.6780649 -0.95460445 3.31100973]\n", "Fidelity at Step 37: 0.6567275794764266 - [ 1.52187025 -0.67874015 -0.95475221 3.31087471]\n", "Fidelity at Step 38: 0.6793927125770123 - [ 1.52212336 -0.67847246 -0.95427491 3.31101721]\n", "Fidelity at Step 39: 0.6699441319998853 - [ 1.5222817 -0.67837307 -0.95412217 3.31098087]\n", "Fidelity at Step 40: 0.6692521492261183 - [ 1.52206466 -0.67847262 -0.95423785 3.3109168 ]\n" ] } ], "source": [ "# Gets the IBMQ account\n", "provider = IBMQ.load_account()\n", "backend = provider.backends.ibmq_ourense # Picks the Ourense device\n", "\n", "# Creates and runs the optimizer\n", "initial = [random.randint(-314, 314)/200 for i in range(0, 2)]\n", "initial += [random.randint(-314, 314)/100 for i in range(0, 2)]\n", "out = minimize(cost_function, x0=initial, method=\"COBYLA\", options={'maxiter':40})" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "O2RHQmqHlBfH" }, "source": [ "So now that we have run the optimization method, we will take the set of parameters that yielded the highest fiedlity, and prepare them on the statevector simulator, to see if we managed to find a circuit that produces a good approximationto the TFD state, without noise messing up the results. We will do this manually, as I had to terminate the optimization process early (I originally set it to 50 iterations, but itwas taking too long to complete)." ] }, { "cell_type": "code", "execution_count": 0, "metadata": { "colab": {}, "colab_type": "code", "id": "7NNkbgBElBfH" }, "outputs": [], "source": [ "# These are the optimal parameters that we found during the optimization process, corresponding to a cost\n", "# of 0.6794\n", "optimal_params = [ 1.52212336, -0.67847246, -0.95427491, 3.31101721]" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 355 }, "colab_type": "code", "id": "1OzBQ7n6lBfJ", "outputId": "4b8cbcb9-8e44-41c3-b369-864dc6a193b7", "scrolled": true }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Final Fidelity: (0.8977613510386174+0j)\n" ] } ], "source": [ "# We then simulate the algorithm for the optimal parameters:\n", "circ = QuantumCircuit(4)\n", "full_circuit([optimal_params[0:2]], [optimal_params[2:4]], 2, 1, new_x_cost_unitary, new_x_mixer_unitary, circ)\n", "\n", "backend = Aer.get_backend('statevector_simulator')\n", "job = execute(circ, backend)\n", "result = job.result()\n", "outputstate = result.get_statevector(circ, decimals=100)\n", "\n", "final_density_matrix = np.outer(outputstate, np.conj(outputstate))\n", "create_density_plot(np.real(final_density_matrix))\n", "\n", "print(\"Final Fidelity: \"+str(np.dot(np.conj(tfd), outputstate)*np.dot(tfd, np.conj(outputstate))))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "7oDdcByvlBfV" }, "source": [ "Thisis pretty good! This tells us that if we eleiminated noise/external effects from the circuit, the optimal state that we were able to prepare with the quantum device would have a fidelity of almost $0.9$ with the real TFD state. This was obviously a simple model, but as quantum devices become more robust, I'm confidentwewill beable to do actual hardware runs of more complex TFD states. In addition to this, the paper outlines a few ideas for error mitigation for this specific algorithm, so implementation of those might help as well, though we will not consider them in this Notebook." ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "dnsfPsH4lBfV" }, "source": [ "## 8. Conclusion and Further Reading\n", "\n", "This Notebook was based off of the research done by D. Zhu, S. Johri, N. M. Linke, K. A. Landsman, N. H. Nguyen, C. H. Alderete, A. Y. Matsuura, T. H. Hsieh, and C. Monroe, presented in the paper: Generation of Thermofield Double States and Critical Ground States with a Quantum Computer (linked below).\n", "\n", "If you're interested in learning more about TFD states, or their variational generation, check out the following papers:\n", "\n", "- How to Build the Thermofield Double State\n", "- Generation of Thermofield Double States and Critical Ground States with a Quantum Computer\n", "\n", "For an interesting application of TFD states, check out:\n", "\n", "- Quantum Gravity in the Lab: Teleportation by Size and Traversable Wormholes\n", "\n", "Thank you to Tim Hsieh for answering my questions relating the TFD generation paper (of which he is one of the authors)." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 68 }, "colab_type": "code", "id": "lu0OAN4IlBfV", "outputId": "568f0176-952f-4f3d-b9d5-bbfdb9410c2c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'qiskit-terra': '0.14.1', 'qiskit-aer': '0.5.1', 'qiskit-ignis': '0.3.0', 'qiskit-ibmq-provider': '0.7.1', 'qiskit-aqua': '0.7.1', 'qiskit': None}\n", "3.6.9 (default, Apr 18 2020, 01:56:04) \n", "[GCC 8.4.0]\n" ] } ], "source": [ "import qiskit\n", "print(qiskit.__qiskit_version__)\n", "\n", "import sys\n", "print(sys.version)" ] } ], "metadata": { "colab": { "collapsed_sections": [], "include_colab_link": true, "name": "tfd.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 1 }