diff --git a/learning/modules/computer-science/_toc.json b/learning/modules/computer-science/_toc.json index 7e74e0bf783..048f8378825 100644 --- a/learning/modules/computer-science/_toc.json +++ b/learning/modules/computer-science/_toc.json @@ -29,6 +29,10 @@ "title": "Quantum key distribution", "url": "/learning/modules/computer-science/quantum-key-distribution" }, + { + "title": "Quantum Fourier transform", + "url": "/learning/modules/computer-science/qft" + }, { "title": "Variational quantum eigensolver", "url": "/learning/modules/computer-science/vqe" diff --git a/learning/modules/computer-science/qft.ipynb b/learning/modules/computer-science/qft.ipynb new file mode 100644 index 00000000000..3a77b3f8c38 --- /dev/null +++ b/learning/modules/computer-science/qft.ipynb @@ -0,0 +1,1024 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dd33e9e7-3e4c-48ea-81a9-70b74c34b130", + "metadata": {}, + "source": [ + "# Quantum Fourier transform" + ] + }, + { + "cell_type": "markdown", + "id": "a920f884-f6b0-46fd-a4ef-126f9c281f17", + "metadata": {}, + "source": [ + "For this Qiskit in Classrooms module, students must have a working Python environment with the following packages installed:\n", + "- `qiskit` v2.1.0 or newer\n", + "- `qiskit-ibm-runtime` v0.40.1 or newer\n", + "- `qiskit-aer` v0.17.0 or newer\n", + "- `qiskit.visualization`\n", + "- `numpy`\n", + "- `pylatexenc`\n", + "\n", + "To set up and install the packages above, see the [Install Qiskit](/docs/guides/install-qiskit) guide.\n", + "In order to run jobs on real quantum computers, students will need to set up an account with IBM Quantum® by following the steps in the [Set up your IBM Cloud account](/docs/guides/cloud-setup) guide.\n", + "\n", + "This module was tested and used XX seconds of QPU time. This is a good-faith estimate; your actual usage may vary." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46231c35-a3f5-4b04-83c5-6f15d6a5785c", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment and modify this line as needed to install dependencies\n", + "#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'" + ] + }, + { + "cell_type": "markdown", + "id": "d77edd39-2574-41bb-9338-13d139f1a6a9", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "A Fourier transform is a ubiquitous tool with applications in math, physics, signal processing, data compression, and countless other fields. A *quantum* version of the Fourier transform, aptly named the quantum Fourier transform, forms the basis for some of the most important quantum algorithms.\n", + "\n", + "Today, after a reminder of the classical Fourier transform, we'll talk about how we implement the quantum Fourier transform on a quantum computer. Then, we'll discuss one of the applications of the quantum Fourier transform to an algorithm called the phase estimation algorithm. Quantum phase estimation is a subroutine in Shor's famous factoring algorithm, which is sometimes referred to as the \"crown jewel\" of quantum computing. This module builds toward another module all about Shor's algorithm, but it's also meant to be stand-alone. The quantum Fourier transform is a fascinating and useful algorithm in its own right!" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "24e8de09-0876-46cd-9346-c0d46ffce8a9", + "metadata": {}, + "source": [ + "## The classical Fourier transform\n", + "\n", + "Before we jump into the quantum Fourier transform, let's first remind ourselves of the classical version. The Fourier transform is a method of transforming from one so-called \"basis\" to another. You can think of two bases as different perspectives of the same problem — they are both valid ways to express a function, but one or the other might be more illuminating, depending on the problem at hand. Some examples of pairs of bases that are connected by Fourier transform are position and momentum, and time and frequency.\n", + "\n", + "Let's see an example of how the Fourier transform might help us figure out what note an instrument is playing based on its audio waveform. Typically, we see the waveforms represented in the time basis — that is, the amplitude of the wave is expressed as a function of time.\n", + "\n", + "![Single sinusoidal signal plotted as a function of time.](/learning/images/modules/computer-science/qft/Cnote.avif)\n", + "\n", + "We can Fourier transform this waveform to go from the time basis to the frequency basis:\n", + "\n", + "![Frequency spectrum of the audio waveform. one clear sharp peak at 260 Hz.](/learning/images/modules/computer-science/qft/Cnotefreq.avif)\n", + "\n", + "In the frequency basis, we can easily see a clear peak at about 260 Hz. That's a middle C!\n", + "\n", + "Now, you might have been able to do determine that a middle C was being played without the use of a Fourier transform, but what if multiple notes are played at once? Then the waveform becomes more complicated when we plot it in the time basis:\n", + "\n", + "![Displacement vs. time graph of multiple sine waves at once, creating a more complicated periodic pattern.](/learning/images/modules/computer-science/qft/Cchord.avif)\n", + "\n", + "But the frequency spectrum clearly identifies three peaks:\n", + "\n", + "![Frequency spectrum of the above audio waveform. Three peaks at approximately 260 Hz, 330 Hz, and 392 Hz. The last peak is very weak, but visible.](/learning/images/modules/computer-science/qft/Cchordfreq.avif)\n", + "\n", + "\n", + "This was a C-major chord, playing the notes C, E, and G.\n", + "\n", + "This kind of Fourier analysis can help us extract the frequency components of any sort of complicated signal.\n", + "\n", + "\n", + "### Discrete Fourier transform\n", + "\n", + "The Fourier transform is useful for any number of signal-processing applications. But in most of these real-world applications (including the music example we used above), we want to transform a discrete set of $N$ data points — not a continuous function. In this case, we use the *discrete* Fourier transform. The discrete Fourier transform acts on a vector $(x_0, ..., x_{N-1})$ and maps it to the vector $(y_0, ..., y_{N-1})$ according to the formula:\n", + "\n", + "$$y_k = \\frac{1}{\\sqrt{N}}\\sum_{j=0}^{N-1}x_j\\omega_N^{jk}$$\n", + "\n", + "where we take $\\omega_N^{jk} = e^{2\\pi i \\frac{jk}{N}}$. (Note that there are other conventions that have a minus sign in the exponential, so be careful when you see the DFT in the wild.) Recall that $e^{2\\pi i \\frac{jk}{N}}$ is a periodic function, with period $\\frac{N}{k}$. So, by multiplying by this function, the Fourier transform is essentially a way to break the (discrete) function $\\{x_{j}\\}$ into a linear combination of its constituent periodic functions, each with period $\\frac{N}{k}$." + ] + }, + { + "cell_type": "markdown", + "id": "e1271322-48e3-47e1-90e1-7cf7117ccd7c", + "metadata": {}, + "source": [ + "## The quantum Fourier transform\n", + "\n", + "So now, we've seen how the Fourier transform is used to represent a function as a linear combination of a new set of so-called \"basis functions.\" Basis transformations are regularly done on qubit states, too. For example, the state of a single qubit $|\\psi\\rangle$ can be expressed in the computational basis $|\\psi\\rangle = c_0 |0\\rangle + c_1 |1\\rangle$, with basis states $|0\\rangle$ and $|1\\rangle$, or in the $X$ basis $|\\psi\\rangle = c_+ |+\\rangle + c_- |-\\rangle$ with basis states $|+\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |1\\rangle)$ and $|-\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle - |1\\rangle)$. Both are equally valid, but one might be more natural than the other, depending on the type of problem you are trying to solve.\n", + "\n", + "Qubit states can also be expressed in the Fourier basis where a state is expressed in terms of a linear combination of the Fourier basis states $|\\phi_y\\rangle$, rather than the usual, computational basis states, $|x\\rangle$. To do this, you need to apply a quantum Fourier transform (QFT):\n", + "\n", + "$$ | \\phi_y \\rangle = \\frac{1}{\\sqrt{N}}\\sum_{x=0}^{N-1}\\omega_N^{y x} \\vert x \\rangle$$\n", + "\n", + "with $\\omega_N^{yx} = e^{\\frac{2\\pi y x}{N}}$ as above, and $N$ is the number of basis states in your quantum system. Note that, since we're working with qubits now, $m$ qubits gives you $2^m$ basis states, so $N=2^m$. Here, the basis states are written as just a single number $|x\\rangle$ where $x$ ranges from $0$ to $N-1$, but you might more typically see the basis states expressed as $|00...00\\rangle$, $|00...01\\rangle$, $|00...11\\rangle$, ..., $|11...11\\rangle$, where each binary digit represents the state of qubit 0 through $m-1$, from right to left. There's an easy way to convert these binary states to a single number: just treat them like binary numbers! So, $|00...00\\rangle = |0\\rangle$, $|00...01\\rangle = |1\\rangle$, $|00...10\\rangle = |2\\rangle$, $|00...11\\rangle = |3\\rangle$, and so on, all the way up to $|11...11\\rangle = |2^m -1\\rangle = |N-1\\rangle$." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "07d09c65-9ba8-41cb-983d-b5dd33d99458", + "metadata": {}, + "source": [ + "### Building intuition for the Fourier basis states\n", + "\n", + "So, we've just gone over what the computational basis states are and how they're ordered: they're the set of states where each qubit is either in $0$ or $1$, and we order them from the state where all qubits are $0$, $|00...00\\rangle$, to the state where they are all $1$, $|11...11\\rangle$.\n", + "\n", + "But how can we make sense of the *Fourier* basis states? All of the Fourier basis states are equal superpositions of all the computational basis states, but each state differs from the other in the periodicity in the components' *phase*. To understand this more concretely, let's take a look at the four Fourier basis states of a two-qubit system. The lowest Fourier state is one whose phase does not vary at all:\n", + "\n", + "$$|\\phi_0\\rangle = \\frac{1}{2} (|00\\rangle + |01\\rangle + |10\\rangle + |11\\rangle)$$\n", + "\n", + "We can visualize this state by plotting the complex amplitude of each of the terms. The red line guides the eye to show you how the phase of this amplitude winds around the complex plane as a function of the computational basis state. For $|\\phi_0\\rangle$, the phase remains constant:\n", + "\n", + "![Bar graph of the complex amplitude (x-y plane) for each computational basis state (z-axis) for phi_0. They are all real, and so the bars all point to +1 on the x-axis](/learning/images/modules/computer-science/qft/phi0.avif)\n", + "\n", + "The next Fourier basis state is the one whose components' phases wind around from $0$ to $2\\pi$ just once:\n", + "\n", + "$$|\\phi_1\\rangle = \\frac{1}{2} (|00\\rangle + e^{i\\pi/2}|01\\rangle + e^{i\\pi}|10\\rangle + e^{3i\\pi/2}|11\\rangle) = \\frac{1}{2}(|00\\rangle + i|01\\rangle - |10\\rangle - i|11\\rangle)$$\n", + "\n", + "And we can see this winding in the plot of complex amplitude vs. computational basis state:\n", + "\n", + "![Bar graph of the complex amplitude (x-y plane) for each computational basis state (z-axis) for phi_1. The red line shows how the complex phase accumulates such that it winds around 2 pi once as you step through all of the computational basis states.](/learning/images/modules/computer-science/qft/phi1.avif)\n", + "\n", + "\n", + "So, each state has a phase that is $2\\pi/4$ radians higher than the state before it when they're ordered in the standard way, since in this example we have four basis states ($N=4$). The next basis state winds around from 0 to 2$\\pi$ twice:\n", + "\n", + "$$|\\phi_2\\rangle = \\frac{1}{2} (|00\\rangle + e^{i\\pi}|01\\rangle + e^{2i\\pi}|10\\rangle + e^{3i\\pi}|11\\rangle) = \\frac{1}{2} (|00\\rangle - |01\\rangle + |10\\rangle - |11\\rangle)$$\n", + "\n", + "\n", + "![Bar graph of the complex amplitude (x-y plane) for each computational basis state (z-axis) for phi_2. The red line shows how the complex phase accumulates such that it winds around 2 pi twice as you step through all of the computational basis states.](/learning/images/modules/computer-science/qft/phi2.avif)\n", + "\n", + "\n", + "Finally, the highest Fourier component is the one with the fastest varying phase. For our example with two qubits, it's the one whose phases wind around from 0 to $2\\pi$ three times:\n", + "\n", + "$$|\\phi_3\\rangle = \\frac{1}{2} (|00\\rangle + e^{3i\\pi/2}|01\\rangle + e^{6i\\pi/2}|10\\rangle + e^{9i\\pi/2}|11\\rangle) = \\frac{1}{2} (|00\\rangle - i|01\\rangle - |10\\rangle + i|11\\rangle)$$\n", + "\n", + "\n", + "![Bar graph of the complex amplitude (x-y plane) for each computational basis state (z-axis) for phi_3. The red line shows how the complex phase accumulates such that it winds around 2 pi three times as you step through all of the computational basis states.](/learning/images/modules/computer-science/qft/phi3.avif)\n", + "\n", + "\n", + "\n", + "In general, for an $n$-qubit state, there will be $2^n$ Fourier basis states, whose frequency in phase variation ranges from constant, for $|\\phi_0\\rangle$, to rapidly varying for $|\\phi_{2^n-1}\\rangle$, completing $2^n-1$ windings around $2\\pi$ over the superposition of states. So, when we take a QFT of a quantum state, we're essentially doing the same basic analysis that we did for the musical waveform in the Intro. We're determining the Fourier frequency components that contribute to creating the quantum state of interest.\n", + "\n", + "### Try some example QFTs:\n", + "\n", + "Let's try to continue to build our intuition for the quantum Fourier transform by making a state in the computational basis, then seeing what happens when we apply the QFT to it. For now, we'll just treat the QFT as a black box that we apply using the `QFTGate` from the Qiskit circuit library. Later, we'll take a peak under the hood to see how it's implemented.\n", + "\n", + "We start by loading the necessary packages and selecting a device to run our circuit on:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3b420ee9-fe1f-4f29-aa73-1306e7a86688", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit import QuantumCircuit\n", + "from qiskit.visualization import plot_histogram\n", + "from qiskit.circuit.library import QFTGate" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f93e8786-96e4-4adf-97b1-1d6c1222b3dc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ibm_pinguino2\n" + ] + } + ], + "source": [ + "# Load the Qiskit Runtime service\n", + "from qiskit_ibm_runtime import QiskitRuntimeService\n", + "\n", + "# Load the Runtime primitive and session\n", + "from qiskit_ibm_runtime import SamplerV2 as Sampler\n", + "\n", + "service = QiskitRuntimeService()\n", + "\n", + "# Use the least busy backend\n", + "# backend = service.least_busy(operational=True, simulator=False, min_num_qubits = 127)\n", + "backend = service.backend(\"ibm_pinguino2\")\n", + "\n", + "print(backend.name)" + ] + }, + { + "cell_type": "markdown", + "id": "ea5beb2c-d7b2-4d7b-9015-02dad90d44b6", + "metadata": {}, + "source": [ + "If you don't have time available on your account or want to use a simulator for any reason, you can run the cell below to set up a simulator that will mimic the quantum device we selected above:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7f33f044-a798-4b9c-baad-6676650cd322", + "metadata": {}, + "outputs": [], + "source": [ + "# Load the backend sampler\n", + "from qiskit.primitives import BackendSamplerV2\n", + "\n", + "# Load the Aer simulator and generate a noise model based on the currently-selected backend.\n", + "from qiskit_aer import AerSimulator\n", + "from qiskit_aer.noise import NoiseModel\n", + "\n", + "noise_model = NoiseModel.from_backend(backend)\n", + "\n", + "# Define a simulator using Aer, and use it in Sampler.\n", + "backend_sim = AerSimulator(noise_model=noise_model)\n", + "sampler_sim = BackendSamplerV2(backend=backend_sim)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d4d986a-6164-40cd-8bba-0289af2430f4", + "metadata": {}, + "outputs": [], + "source": [ + "# Alternatively, load a fake backend with generic properties and define a simulator.\n", + "from qiskit.providers.fake_provider import GenericBackendV2\n", + "\n", + "backend_gen = GenericBackendV2(num_qubits=18)\n", + "sampler_gen = BackendSamplerV2(backend=backend_gen)" + ] + }, + { + "cell_type": "markdown", + "id": "bce6f65e-1105-4abf-80ab-476385ff5b60", + "metadata": {}, + "source": [ + "#### Single computational basis state\n", + "\n", + "First, let's try just transforming a single computational basis state. We'll start with making a random computational state:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "79943d18-2d57-41f4-ba6e-9c40aca24d38", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 1: Map\n", + "\n", + "qubits = 4\n", + "N = 2**qubits\n", + "\n", + "\n", + "qc = QuantumCircuit(qubits)\n", + "\n", + "# flip state of random qubits to put in a random single computational basis state\n", + "for i in range(1, qubits):\n", + " if np.random.randint(0, 2):\n", + " qc.x(i)\n", + "\n", + "\n", + "# make a copy of the above circuit. (to be used when we apply the QFT in next part)\n", + "qc_qft = qc.copy()\n", + "\n", + "\n", + "qc.measure_all()\n", + "qc.draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c16bbbb1-99fe-4824-b9a5-b74fa79eeedf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 2: Transpile\n", + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "\n", + "target = backend.target\n", + "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n", + "\n", + "qc_isa = pm.run(qc)\n", + "\n", + "# Step 3: Run the job on a real quantum computer OR try fake backend\n", + "\n", + "sampler = Sampler(mode=backend)\n", + "pubs = [qc_isa]\n", + "\n", + "# Run the job on real quantum device\n", + "\n", + "job = sampler.run(pubs, shots=1000)\n", + "res = job.result()\n", + "counts = res[0].data.meas.get_counts()\n", + "\n", + "# OR Run the job on the Aer simulator with noise model from real backend\n", + "\n", + "# job = sampler_sim.run([qc_isa])\n", + "# res = job.result()\n", + "# counts = res[0].data.meas.get_counts()\n", + "\n", + "# Step 4: Post-Process\n", + "plot_histogram(counts)" + ] + }, + { + "cell_type": "markdown", + "id": "f3723281-56f2-42ac-afd9-44f6d0fe147b", + "metadata": {}, + "source": [ + "Now, let's Fourier transform this state with `QFTGate`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "51b45910-624c-40ee-ad56-d9af094490d2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 1: Map\n", + "\n", + "qc_qft.compose(QFTGate(qubits), inplace=True)\n", + "qc_qft.measure_all()\n", + "qc_qft.draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "198a4223-96ab-475e-a83c-75596bf569cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 2: Transpile\n", + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "\n", + "target = backend.target\n", + "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n", + "\n", + "qc_isa = pm.run(qc_qft)\n", + "\n", + "# Step 3: Run the job on a real quantum computer - try fake backend\n", + "\n", + "sampler = Sampler(mode=backend)\n", + "pubs = [qc_isa]\n", + "\n", + "# Run the job on real quantum device\n", + "\n", + "job = sampler.run(pubs, shots=1000)\n", + "res = job.result()\n", + "counts = res[0].data.meas.get_counts()\n", + "\n", + "# OR Run the job on the Aer simulator with noise model from real backend\n", + "\n", + "# job = sampler_sim.run([qc_isa])\n", + "# res = job.result()\n", + "# counts = res[0].data.meas.get_counts()\n", + "\n", + "# Step 4: Post-Process\n", + "plot_histogram(counts)" + ] + }, + { + "cell_type": "markdown", + "id": "89965746-e7d1-409f-a893-5766759a8ce3", + "metadata": {}, + "source": [ + "As you can see, we measure the populations of each state to be more or less equal, give or take some experimental and statistical noise. So, if you take the QFT of a single computational basis state, the result is an equal superposition of all states. If you're familiar with Fourier transforms, this probably doesn't surprise you. One basic principle that can help us build an intuitive connection between a function and its Fourier transform is that the width of a function is inversely proportional to the width of its Fourier transform. So, something that is very localized in time, for example, like a very short pulse, will require a broad range of frequencies to generate that pulse. So that signal will be very broad in Fourier space.\n", + "\n", + "This fact is actually related to quantum uncertainty! Heisenberg's uncertainty principle is typically stated as $\\Delta x \\Delta p \\ge \\hbar / 2 $. So if th uncertainty in $x$ ($\\Delta x$) is small, the uncertainty in momentum ($\\Delta p$) must be big, and vice-versa. It turns out that transforming from the position basis $x$ to the momentum basis $p$ is accomplished through a Fourier transform.\n", + "\n", + "Note: Keep in mind, we're measuring populations in each of the basis states, so we're losing information about the relative phases between the various parts of the superposition. So, while the QFT of any single computational basis state will yield the same even spread in population over all the basis states, the *phases* won't necessarily be the same.\n", + "\n", + "\n", + "\n", + "#### Two computational basis states\n", + "\n", + "Now, let's see what happens when we prepare a superposition of computational basis states. What do you think the Fourier transform will look like in this case?\n", + "\n", + "Let's choose the superposition:\n", + "\n", + "$$|\\psi\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |N/2\\rangle) = \\frac{1}{\\sqrt{2}} (|000...0\\rangle + |100...0\\rangle)$$" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "cd0b237b-b139-451a-8170-69babdc29e56", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 1: Map\n", + "qubits = 4\n", + "N = 2**qubits\n", + "\n", + "\n", + "qc = QuantumCircuit(qubits)\n", + "\n", + "# to make this state, we just need to apply a hadamard to the last qubit\n", + "\n", + "qc.h(qubits - 1)\n", + "\n", + "\n", + "qc_qft = qc.copy()\n", + "\n", + "\n", + "qc.measure_all()\n", + "\n", + "qc.draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0ad8006b-caaf-4e4c-b222-a9224d3f6af8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# First, let's go through steps 2-4 for the first circuit, qc\n", + "\n", + "# Step 2: Transpile\n", + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "\n", + "target = backend.target\n", + "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n", + "\n", + "qc_isa = pm.run(qc)\n", + "\n", + "# Step 3: Run the job on a real quantum computer - try fake backend\n", + "\n", + "sampler = Sampler(mode=backend)\n", + "pubs = [qc_isa]\n", + "\n", + "# Run the job on real quantum device\n", + "\n", + "job = sampler.run(pubs, shots=1000)\n", + "res = job.result()\n", + "counts = res[0].data.meas.get_counts()\n", + "\n", + "# OR Run the job on the Aer simulator with noise model from real backend\n", + "\n", + "# job = sampler_sim.run([qc_isa])\n", + "# res = job.result()\n", + "# counts = res[0].data.meas.get_counts()\n", + "\n", + "# Step 4: Post-Process\n", + "plot_histogram(counts)" + ] + }, + { + "cell_type": "markdown", + "id": "93aaa06c-7de3-4c0f-950c-e48768ca50c5", + "metadata": {}, + "source": [ + "Now, let's Fourier transform this state with `QFTGate`:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "11793bc2-53ea-4c34-aed7-06e1ce630557", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 1: Map\n", + "\n", + "qc_qft.compose(QFTGate(qubits), inplace=True)\n", + "qc_qft.measure_all()\n", + "qc_qft.draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "15b9688a-8c71-457d-87e7-8bfec6f4ee76", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Step 2: Transpile\n", + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "\n", + "target = backend.target\n", + "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n", + "\n", + "qc_isa = pm.run(qc_qft)\n", + "\n", + "# Step 3: Run the job on a real quantum computer OR try fake backend\n", + "\n", + "sampler = Sampler(mode=backend)\n", + "pubs = [qc_isa]\n", + "\n", + "# Run the job on real quantum device\n", + "\n", + "job = sampler.run(pubs, shots=1000)\n", + "res = job.result()\n", + "counts = res[0].data.meas.get_counts()\n", + "\n", + "# OR Run the job on the Aer simulator with noise model from real backend\n", + "\n", + "# job = sampler_sim.run([qc_isa])\n", + "# res = job.result()\n", + "# counts = res[0].data.meas.get_counts()\n", + "\n", + "# Step 4: Post-Process\n", + "plot_histogram(counts)" + ] + }, + { + "cell_type": "markdown", + "id": "74008f89-5634-4e52-8061-87976888c39f", + "metadata": {}, + "source": [ + "This one might be a bit more surprising. It looks like the QFT of the state $|\\psi\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |N/2\\rangle)$ is a superposition of all of the even basis states. But if we think back to our visualization of each basis state $|\\phi_y\\rangle$, and how the phase of each component winds around $2\\pi$ $y$ times, then the reason we get this result might become clear.\n", + "\n", + "#### Check your understanding\n", + "Read the question(s) below, think about your answer, then click the triangle to reveal the solution.\n", + "\n", + "
\n", + "\n", + "\n", + "Using the hint above, explain why the result we got for the QFT of $|\\psi\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + |N/2\\rangle)$ is expected.\n", + "\n", + "\n", + "\n", + "__Answer:__\n", + "\n", + "The original state has a relative phase of 0 (or an integer multiple of $2\\pi$) between the two parts of the superposition. So, we know this state has Fourier components whose phases also match up in that way: i.e. the ones that have 0 phase shift between the |0000> term and the |1000> term. Each Fourier basis state $|\\phi_y\\rangle$ is composed of terms whose phase accumulates at a rate of $2\\pi y/N$, meaning when ordered in the usual way, each term in the superposition has a phase of $2\\pi y/N$ greater than the term that came before. So, at the halfway point $N/2$, we want the phase $2\\pi y/N * N/2$ to be an integer multiple of $2\\pi$. This happens when $y$ is even.\n", + "\n", + "
\n", + "
\n", + "\n", + "\n", + "\n", + "What computational state superposition would correspond to a QFT with peaks on every odd binary number?\n", + "\n", + "\n", + "\n", + "__Answer:__\n", + "\n", + "If you took the QFT of the state $\\psi = |0\\rangle - |N/2\\rangle$, then you would see peaks on every odd binary numbered state.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "af84bf95-c211-4a58-a851-1e827a42fcdb", + "metadata": {}, + "source": [ + "## Breaking down the QFT Algorithm\n", + "\n", + "Now that we've gained more intuition into the relationship between qubit states in the computational basis and the Fourier basis, let's dig into the QFT algorithm itself. In other words, what gates do we actually implement on the quantum computer to achieve this transform?\n", + "\n", + "Let's start small, with a single qubit. So, that means we'll have 2 basis states. QFT$_2$ transforms computational basis states $|0\\rangle$ and $|1\\rangle$ into Fourier basis states $\\phi_0$ and $\\phi_1$:\n", + "\n", + "$|\\phi_0\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)$\n", + "\n", + "$|\\phi_1\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle - |1\\rangle)$\n", + "\n", + "\n", + "#### Check your understanding\n", + "Read the question(s) below, think about your answer, then click the triangle to reveal the solution.\n", + "\n", + "
\n", + "\n", + "\n", + "Use the equation for the QFT in the previous section to verify these two Fourier basis states above.\n", + "\n", + "\n", + "\n", + "__Answer:__\n", + "\n", + "The general QFT formula is:\n", + "\n", + "$$ | \\phi_y \\rangle = \\frac{1}{\\sqrt{N}}\\sum_{x=0}^{N-1}\\omega_N^{y x} \\vert x \\rangle$$\n", + "\n", + "For a single qubit ($n=1$), $N=2^n=2$, and $\\omega_N^{xy} = e^{2\\pi i \\frac {y x}{2}}$. So, we have\n", + "\n", + "$ | \\phi_0 \\rangle = \\frac{1}{\\sqrt{2}}(e^{2\\pi i \\frac {0 \\times 0}{2}}|0\\rangle + e^{2\\pi i \\frac {0 \\times 1}{2}}|1\\rangle) = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)$\n", + "\n", + "$ | \\phi_1 \\rangle = \\frac{1}{\\sqrt{2}}(e^{2\\pi i \\frac {1 \\times 0}{2}}|0\\rangle + e^{2\\pi i \\frac {1 \\times 1}{2}}|1\\rangle) = \\frac{1}{\\sqrt{2}}(|0\\rangle - |1\\rangle)$\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "Take a look at those two equations. You might already know of a quantum gate that can be used to implement this transform. That is, there is a gate that transforms the computational basis states $|0\\rangle$ and $|1\\rangle$ to the respective Fourier basis states $|\\phi_0\\rangle$ and $|\\phi_1\\rangle$. It's a Hadamard gate! This becomes even more clear if we introduce a matrix representation of the QFT$_N$ operation:\n", + "\n", + "\n", + "$$ \\text{QFT}_N = \\frac{1}{\\sqrt{N}} \\sum_{x=0}^{N-1} \\sum_{y=0}^{N-1} \\omega_N^{xy} \\vert x \\rangle \\langle y \\vert$$\n", + "\n", + "If you're not familiar with this notation for expressing a quantum operator, that's okay! It is a way to represent an $N \\times N$ matrix, where $x$ and $y$ index the columns and rows of the matrix, from $0$ to $N-1$, and $\\omega_N^{xy}$ is the value of that particular entry. So, the entry in the 0th column and 2nd row, for example, would just be $\\omega_N^{0,2} = e^{2 \\pi i \\frac{0 \\times 2}{N}} = 1$.\n", + "\n", + "In this representation, each of the computational basis states is associated with one of the basis vectors:\n", + "\n", + "$$|0\\rangle =\n", + "\\begin{pmatrix}\n", + "1 \\\\ 0 \\\\ \\vdots \\\\ 0\n", + "\\end{pmatrix},\n", + "|1\\rangle =\n", + "\\begin{pmatrix}\n", + "0 \\\\ 1 \\\\ \\vdots \\\\ 0\n", + "\\end{pmatrix},\n", + "|N-1\\rangle =\n", + "\\begin{pmatrix}\n", + "0 \\\\ 0 \\\\ \\vdots \\\\ 1\n", + "\\end{pmatrix}.\n", + "$$\n", + "\n", + "If you'd like to learn about this representation in more depth, see John Watrous's lesson on multiple systems in the [Basics of Quantum Information](/learning/courses/basics-of-quantum-information/multiple-systems/introduction) course.\n", + "\n", + "Lets try to construct the matrix for QFT$_4$. Using the above formula, we find that\n", + "\n", + "$\\text{QFT}_4 = \\frac{1}{2}\n", + "\\begin{pmatrix}\n", + " 1 & 1 & 1 & 1 \\\\\n", + " 1 & i & -1 & -i \\\\\n", + " 1 & -1 & 1 & -1 \\\\\n", + " 1 & -i & -1 & i \\\\\n", + "\\end{pmatrix}\n", + "$\n", + "\n", + "To implement this matrix on a quantum computer, we'll need to figure out which combination of gates applied to which qubits will give us a unitary transformation that matches the matrix above. We already know one of the gates that will be needed: the Hadamard. Another gate we'll need is the controlled-phase gate, which applies a relative phase $\\alpha$ to the target qubit's state, as long as the control qubit is in the state $|1\\rangle$. In matrix form this looks like:\n", + "\n", + "$\\text{CP}_\\alpha =\n", + "\\begin{pmatrix}\n", + " 1 & 0 & 0 & 0 \\\\\n", + " 0 & 1 & 0 & 0 \\\\\n", + " 0 & 0 & 1 & 0 \\\\\n", + " 0 & 0 & 0 & e^{i\\alpha} \\\\\n", + "\\end{pmatrix}\n", + "$\n", + "\n", + "Since only the state $|11\\rangle$ is changed, it actually doesn't matter which qubit is considered the \"control\" and which is the \"target.\" The result will be the same either way.\n", + "\n", + "Finally, we'll need some SWAP gates too. A SWAP gate swaps the states of two qubits. It looks like:\n", + "\n", + "$\\text{SWAP}_\\alpha =\n", + "\\begin{pmatrix}\n", + " 1 & 0 & 0 & 0 \\\\\n", + " 0 & 0 & 1 & 0 \\\\\n", + " 0 & 1 & 0 & 0 \\\\\n", + " 0 & 0 & 0 & 1 \\\\\n", + "\\end{pmatrix}\n", + "$\n", + "\n", + "The procedure to construct a QFT$_{2^m}$ circuit on $m$ qubits is iterative — you first apply QFT$_{2^{m-1}}$ to qubits $1$ through $m-1$, then add some gates between qubit $0$ and the other $m-1$ qubits. But to apply QFT$_{2^{m-1}}$, you first need to apply QFT$_{2^{m-2}}$ to qubits 2 through m-1, then add some gates between qubit 1 and the remaining qubits $2$ through $m-1$. It's like a Russian nesting doll: each doll adds a factor of two in the dimension of the QFT circuit, with the smallest doll at the very center, being QFT$_2$, or the Hadamard gate.\n", + "\n", + "To put a doll inside the next biggest size doll, hence increasing the dimension of the QFT by a factor of two, you always follow the same procedure:\n", + "\n", + "1. First, apply QFT$_{2^{m-1}}$ to the bottom-most $m-1$ qubits. This is your \"smaller doll\" of the Russian nesting doll set that you will soon put inside the next-biggest doll.\n", + "2. Use the next qubit up as a control, and apply controlled phase gates to each of the bottom $m-1$ qubits, with phases to the standard basis states of each of the remaining $m-1$ qubits.\n", + "3. Perform a Hadamard on that same top-most qubit that was used as the control in the phase gates.\n", + "4. Use SWAP gates to permute the order of the qubits so that the least significant (top) bit becomes the most significant (bottom) bit, and all others shift to the up by one.\n", + "\n", + "\n", + "We've already been using the `QFTGate` function from the Qiskit circuit library, but now let's take a look inside of some of these QFT gates to verify the above procedure. We can do this with `decompose()`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "6de41e8b-2900-4600-bd35-96df80b1b409", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc = QuantumCircuit(1)\n", + "qc.compose(QFTGate(1), inplace=True)\n", + "qc.decompose().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "066a1c6b-864e-4cf3-b9f1-2751b9b00998", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc = QuantumCircuit(2)\n", + "qc.compose(QFTGate(2), inplace=True)\n", + "qc.decompose().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "dffb70da-0107-4aeb-b433-20f4b82f6abf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc = QuantumCircuit(3)\n", + "qc.compose(QFTGate(3), inplace=True)\n", + "qc.decompose().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "b3375193-b230-4dda-a676-ae350c3a9b93", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc = QuantumCircuit(4)\n", + "qc.compose(QFTGate(4), inplace=True)\n", + "qc.decompose().draw(\"mpl\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "0f29ce85-e9a8-4445-b3ac-5625bdca9c8a", + "metadata": {}, + "source": [ + "So, hopefully from the first four QFTs you can start to see how each one is nested inside the next largest one. You may have noticed, though, that some of the phase gates are not exactly as prescribed in the procedure we outlined above, and the SWAPs don't appear after each subroutine, but are rather only at the very end of the full QFT. This is just to save us unnecessary gates, which would cause the circuit to take longer and be more error-prone. Instead of implementing the SWAP after each nested doll, instead, the circuit keeps track of where each qubit state *should* be and adjusts the qubits to which it's applying the phase gates accordingly. Then, a final set of SWAPs at the end puts everything in its proper place.\n", + "\n", + "## Application of the QFT: Phase Estimation\n", + "\n", + "Let's see how the QFT can be used to solve a useful problem in quantum computing. Calculating the inverse quantum Fourier transform is a necessary step in an algorithm known as Quantum Phase Estimation (QPE), which is itself a subroutine in many other algorithms, including the \"crown jewel\" of quantum algorithms, Shor's factoring algorithm.\n", + "\n", + "The goal of QPE is to estimate the eigenvalues of a unitary operator. Unitary operators are ubiquitous in quantum computing, and often, finding the eigenvalues of their associated eigenvectors is a necessary step in a larger algorithm. Depending on the problem, an eigenvalue can represent an energy of a Hamiltonian in a simulation-type problem, can help us find prime factors of a number in Shor's algorithm, or can contain other essential information. QPE is one of the most important and widely used subroutines in quantum computing.\n", + "\n", + "So, what does this have to do with a quantum Fourier transform? Well, as you may recall, any eigenvalue $\\lambda$ of a unitary operator has a magnitude $|\\lambda| = 1$. So we can write each eigenvalue as a complex number with magnitude one:\n", + "\n", + "$\\lambda = e^{2\\pi i \\theta}$\n", + "\n", + "where $\\theta$ is a real number between 0 and 1. If you would like more information on unitary matrices, see John Watrous' [lesson on the subject](/learning/courses/basics-of-quantum-information/multiple-systems/introduction) in Basics of Quantum Information.\n", + "\n", + "Note that $\\lambda$ is *periodic* in $\\theta$. Already, this might suggest to you that a QFT could be involved, since we saw how useful QFTs are for analyzing periodic functions. Below, we'll walk through the algorithm and see precisely how the QFT comes into play.\n", + "\n", + "\n", + "### How QPE works:\n", + "\n", + "First, we'll start with the simplest QPE algorithm, which roughly estimates the phase to a single binary digit of precision. In other words, this algorithm can distinguish between $\\theta = 0 $ and $\\theta = 1/2$, but can't do better than that. Here's the circuit diagram:\n", + "\n", + "![Circuit diagram of the QPE algorithm for a single data qubit. A Hadamard is applied to the data qubit. Next, the algorithm uses another helper qubit, on which a controlled-U gate is applied, with the data qubit as the control. After another Hadamard on qubit 0, the qubits are measured.](/learning/images/modules/computer-science/qft/QPE1qubit.avif)\n", + "\n", + "So, the qubits are prepared in the state $|\\pi_0\\rangle = |\\psi\\rangle|0\\rangle$, where qubit $0$ is in the state $|0\\rangle$ and the remaining qubits are in the state $|\\psi\\rangle$, which is an eigenstate of $U$. After the first Hadamard, the qubit state becomes:\n", + "\n", + "$|\\pi_1\\rangle = \\frac{1}{\\sqrt{2}}|\\psi\\rangle (|0\\rangle + |1\\rangle)$\n", + "\n", + "The next gate is a \"controlled-$U$\" gate. This applies the unitary operation $U$ to the bottom qubits that are in the state $|\\psi\\rangle$ if qubit 0 is in the state $|1\\rangle$, but does nothing to $|\\psi\\rangle$ if qubit 0 is in the state $|0\\rangle$. This transforms the qubits to the state:\n", + "\n", + "\n", + "$|\\pi_2\\rangle = \\frac{1}{\\sqrt{2}}( |\\psi\\rangle|0\\rangle + e^{2\\pi i \\theta}|\\psi\\rangle|1\\rangle)$\n", + " $ = \\frac{1}{\\sqrt{2}}|\\psi\\rangle (|0\\rangle + e^{2\\pi i \\theta}|1\\rangle) $\n", + "\n", + "Something weird just happened: the controlled-$U$ gate only uses qubit $0$ as a control qubit, so one might think that this gate wouldn't change qubit 0's state at all. But somehow, it does! Even though the operation was applied to the lower qubits, the overall effect of the gate is to change the phase of qubit $0$. This is known as the \"phase kickback mechanism,\" and is used in many quantum algorithms, including Deutsch-Josza and Grover's algorithms. If you want to learn more about the phase-kickback mechanism, see John Watrous' lesson on [Quantum query algorithms](https://learning.quantum.ibm.com/course/fundamentals-of-quantum-algorithms/quantum-query-algorithms/deutsch-algorithm) in Fundamentals of Quantum Algorithms.\n", + "\n", + "After the phase-kickback, we apply one more Hadamard to qubit $0$, which results in the state:\n", + "\n", + "$|\\pi_3\\rangle = |\\psi\\rangle ( \\frac{1+e^{2\\pi i \\theta}}{2} |0\\rangle + \\frac{1 - e^{2\\pi i \\theta}}{2}|1\\rangle) = |\\psi\\rangle ( \\cos(\\pi\\theta) |0\\rangle - i \\sin(\\pi\\theta)|1\\rangle)$\n", + "\n", + "So, when we measure qubit $0$ at the end, we will measure $|0\\rangle$ with 100% certainty if $\\theta = 0$ and we will measure $|1\\rangle$ with 100% certainty if $\\theta = \\frac{1}{2}$ (and if our quantum computer is perfect, with no noise). If $\\theta$ is something other than this, the final measurement is only probabilistic and only tells us so much.\n", + "\n", + "### QPE with more precision: more qubits\n", + "\n", + "We can extend this simple concept to a more complicated algorithm with arbitrary precision. If instead of just using qubit $0$ to measure the phase, we use $m$ qubits $0$ through $m-1$, then we will be able to estimate the phase with $m$ bits of precision. Let's see how this works:\n", + "\n", + "![Circuit diagram of the QPE algorithm for a multiple qubits. Hadamards are applied to the data qubits 0 through m-1. Then a series of controlled-U gates are applied to the m helper qubits. Finally, an inverse QFT is applied to the qubits and they are measured.](/learning/images/modules/computer-science/qft/QPE_withpi.avif)\n", + "\n", + "\n", + "\n", + "This more precise QPE circuit starts out the same as the single bit version: Hadamards are applied to the first $m$ qubits, and the remaining qubits are prepared in the state $|\\psi\\rangle$, creating the state:\n", + "\n", + "$$|\\pi_1\\rangle = \\frac{1}{2^{m/2}}|\\psi\\rangle(|0\\rangle+|1\\rangle)(|0\\rangle+|1\\rangle)...(|0\\rangle+|1\\rangle)$$\n", + "\n", + "Now, the controlled-unitaries are applied. Qubit $0$ is the control for the same unitary $U$ as before. But now, qubit $1$ is the control for the unitary $U^2$, which is just $U$ applied twice. So, the eigenvalue of $U^2$ is $e^{2*2\\pi i \\theta}$. In general each qubit $k$ from 0 through $m-1$ will be the control for the unitary $U^{2^k}$. That means each of these qubits will experience a phase kickback of $e^{2^k*2\\pi i \\theta}$. This results in the state:\n", + "\n", + "$$|\\pi_2\\rangle = |\\psi\\rangle \\otimes \\frac{1}{2^{m/2}} (|0\\rangle+e^{2^{m-1}2\\pi i \\theta}|1\\rangle)(|0\\rangle+e^{2^{m-2}2\\pi i \\theta}|1\\rangle)...(|0\\rangle+e^{2\\pi i \\theta}|1\\rangle)$$\n", + "\n", + "This can be rewritten as a sum over the computational basis states:\n", + "\n", + "$$|\\pi_2\\rangle = |\\psi\\rangle \\otimes \\frac{1}{2^{m/2}} \\sum_{k=0}^{2^{m}-1} e^{2\\pi i k \\theta} |k\\rangle $$\n", + "\n", + "Does the sum look familiar? It's a QFT! Recall the equation for a quantum Fourier transform:\n", + "\n", + "$$ \\text{QFT}_{2^m}| y \\rangle = \\frac{1}{\\sqrt{2^m}}\\sum_{x=0}^{2^m-1}\\omega_{2^m}^{y x} \\vert x \\rangle$$\n", + "\n", + "So, if the phase $ \\theta = y/2^m $ for some integer $y$ between $0$ and $2^m-1$, then taking the inverse QFT of this state will result in the state:\n", + "\n", + "$$|\\pi_3\\rangle = |\\psi\\rangle \\otimes |y\\rangle $$\n", + "\n", + "and from $|y\\rangle$, we can deduce $\\theta$.\n", + "\n", + "If $\\theta/2^m$ is *not* an integer multiple, however, then taking the inverse QFT will only *approximate* $\\theta$. How well it approximates $\\theta$ will be probabilistic, meaning we won't always get the best approximation, but it will be pretty close, and the more qubits $m$ you use, the better the approximation you will get. To learn about how to quantify this approximation of $\\theta$, check out John Watrous' lesson on [Phase estimation and factoring](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction) in Fundamentals of Quantum Algorithms.\n", + "\n", + "\n", + "### Conclusion\n", + "\n", + "Hopefully this module gave you a sense for what a QFT is, how it's implemented on a quantum computer, and how useful it can be in solving problems. We gave you a taste for its usefulness when we saw how it can be used in quantum phase estimation to learn about the eigenvalues of a unitary matrix." + ] + }, + { + "cell_type": "markdown", + "id": "33b2c0e7-b48a-4426-8472-ad9a52bf48ea", + "metadata": {}, + "source": [ + "### Critical Concepts\n", + "\n", + "- The Quantum Fourier Transform is the quantum analog to the Discrete Fourier Transform.\n", + "- The QFT is an example of a basis transformation.\n", + "- The Quantum Phase Estimation procedure relies on the phase kickback mechanism from the controlled-unitary operations, as well as an inverse QFT.\n", + "- QFT and QPE are both widely used subroutines in numerous quantum algorithms\n", + "\n", + "## Questions\n", + "\n", + "### True/False\n", + "\n", + "1. T/F The Quantum Fourier Transform is the quantum analogue of the classical discrete Fourier transform (DFT).\n", + "2. T/F QFT can be implemented using only Hadamard and CNOT gates.\n", + "3. T/F QFT is a key component of Shor’s algorithm.\n", + "4. T/F The output of Quantum Phase Estimation is a quantum state representing the eigenvector of the operator.\n", + "5. T/F QPE requires the use of the inverse Quantum Fourier Transform (QFT†).\n", + "6. T/F In QPE, if the phase $\\phi$ is exactly representable with $n$ bits, the algorithm gives the correct result with probability 1.\n", + "\n", + "### Short Answers\n", + "1. How many qubits are needed to perform a QFT on a system with $2^n$ data points?\n", + "2. Can the QFT be used on a state that is not a computational basis state? If so, what happens?\n", + "3. How does the number of control qubits used in QPE affect the resolution of the resulting phase estimate?\n", + "\n", + "\n", + "### Problems:\n", + "\n", + "1. Use matrix multiplication to verify that the steps in the QFT algorithm indeed result in the $\\text{QFT}_4$ matrix:\n", + "\n", + "$$\n", + "\\text{QFT}_4 = \\frac{1}{2}\n", + "\\begin{pmatrix}\n", + " 1 & 1 & 1 & 1 \\\\\n", + " 1 & i & -1 & -i \\\\\n", + " 1 & -1 & 1 & -1 \\\\\n", + " 1 & -i & -1 & i \\\\\n", + "\\end{pmatrix}\n", + "$$\n", + "\n", + "(You don't have to do this by hand!)\n", + "\n", + "\n", + "\n", + "### Challenge problems:\n", + "\n", + "1. Make a 4-qubit state that is an equal superposition of all odd computational bases: $|\\psi\\rangle = |0001\\rangle + |0011\\rangle + |0101\\rangle + |0111\\rangle +|1001\\rangle +|1011\\rangle +|1101\\rangle +|1111\\rangle$. Then perform a QFT on the state. What is the resulting state? Explain why your result makes sense, using your knowledge of Fourier transforms." + ] + } + ], + "metadata": { + "description": "Learn the theory and applications of the quantum Fourier transform.", + "in_page_toc_max_heading_level": 2, + "in_page_toc_min_heading_level": 2, + "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" + }, + "title": "Quantum Fourier transform" + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/public/learning/images/modules/computer-science/qft/Cchord.avif b/public/learning/images/modules/computer-science/qft/Cchord.avif new file mode 100644 index 00000000000..0a6dce8e472 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/Cchord.avif differ diff --git a/public/learning/images/modules/computer-science/qft/Cchordfreq.avif b/public/learning/images/modules/computer-science/qft/Cchordfreq.avif new file mode 100644 index 00000000000..aee5563646e Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/Cchordfreq.avif differ diff --git a/public/learning/images/modules/computer-science/qft/Cnote.avif b/public/learning/images/modules/computer-science/qft/Cnote.avif new file mode 100644 index 00000000000..cacb56d813f Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/Cnote.avif differ diff --git a/public/learning/images/modules/computer-science/qft/Cnotefreq.avif b/public/learning/images/modules/computer-science/qft/Cnotefreq.avif new file mode 100644 index 00000000000..ff998dcddef Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/Cnotefreq.avif differ diff --git a/public/learning/images/modules/computer-science/qft/QPE1qubit.avif b/public/learning/images/modules/computer-science/qft/QPE1qubit.avif new file mode 100644 index 00000000000..dd3a076c632 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/QPE1qubit.avif differ diff --git a/public/learning/images/modules/computer-science/qft/QPE_withpi.avif b/public/learning/images/modules/computer-science/qft/QPE_withpi.avif new file mode 100644 index 00000000000..724ab572b5b Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/QPE_withpi.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/066a1c6b-864e-4cf3-b9f1-2751b9b00998-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/066a1c6b-864e-4cf3-b9f1-2751b9b00998-0.avif new file mode 100644 index 00000000000..44ae337122a Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/066a1c6b-864e-4cf3-b9f1-2751b9b00998-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/0ad8006b-caaf-4e4c-b222-a9224d3f6af8-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/0ad8006b-caaf-4e4c-b222-a9224d3f6af8-0.avif new file mode 100644 index 00000000000..f48b24f13e4 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/0ad8006b-caaf-4e4c-b222-a9224d3f6af8-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/11793bc2-53ea-4c34-aed7-06e1ce630557-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/11793bc2-53ea-4c34-aed7-06e1ce630557-0.avif new file mode 100644 index 00000000000..df5237eb9df Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/11793bc2-53ea-4c34-aed7-06e1ce630557-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/15b9688a-8c71-457d-87e7-8bfec6f4ee76-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/15b9688a-8c71-457d-87e7-8bfec6f4ee76-0.avif new file mode 100644 index 00000000000..097ce8af652 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/15b9688a-8c71-457d-87e7-8bfec6f4ee76-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/198a4223-96ab-475e-a83c-75596bf569cb-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/198a4223-96ab-475e-a83c-75596bf569cb-0.avif new file mode 100644 index 00000000000..3f673f572e1 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/198a4223-96ab-475e-a83c-75596bf569cb-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/51b45910-624c-40ee-ad56-d9af094490d2-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/51b45910-624c-40ee-ad56-d9af094490d2-0.avif new file mode 100644 index 00000000000..b23bca92f0b Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/51b45910-624c-40ee-ad56-d9af094490d2-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/6de41e8b-2900-4600-bd35-96df80b1b409-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/6de41e8b-2900-4600-bd35-96df80b1b409-0.avif new file mode 100644 index 00000000000..1e2dff71d5f Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/6de41e8b-2900-4600-bd35-96df80b1b409-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/79943d18-2d57-41f4-ba6e-9c40aca24d38-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/79943d18-2d57-41f4-ba6e-9c40aca24d38-0.avif new file mode 100644 index 00000000000..410b84b888b Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/79943d18-2d57-41f4-ba6e-9c40aca24d38-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/b3375193-b230-4dda-a676-ae350c3a9b93-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/b3375193-b230-4dda-a676-ae350c3a9b93-0.avif new file mode 100644 index 00000000000..ae3d2e79f69 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/b3375193-b230-4dda-a676-ae350c3a9b93-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/c16bbbb1-99fe-4824-b9a5-b74fa79eeedf-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/c16bbbb1-99fe-4824-b9a5-b74fa79eeedf-0.avif new file mode 100644 index 00000000000..9bee4615e7a Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/c16bbbb1-99fe-4824-b9a5-b74fa79eeedf-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/cd0b237b-b139-451a-8170-69babdc29e56-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/cd0b237b-b139-451a-8170-69babdc29e56-0.avif new file mode 100644 index 00000000000..26c4fc0ecc3 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/cd0b237b-b139-451a-8170-69babdc29e56-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/extracted-outputs/dffb70da-0107-4aeb-b433-20f4b82f6abf-0.avif b/public/learning/images/modules/computer-science/qft/extracted-outputs/dffb70da-0107-4aeb-b433-20f4b82f6abf-0.avif new file mode 100644 index 00000000000..6196478fd7e Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/extracted-outputs/dffb70da-0107-4aeb-b433-20f4b82f6abf-0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/phi0.avif b/public/learning/images/modules/computer-science/qft/phi0.avif new file mode 100644 index 00000000000..df9d244824e Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/phi0.avif differ diff --git a/public/learning/images/modules/computer-science/qft/phi1.avif b/public/learning/images/modules/computer-science/qft/phi1.avif new file mode 100644 index 00000000000..e22da1e6b4c Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/phi1.avif differ diff --git a/public/learning/images/modules/computer-science/qft/phi2.avif b/public/learning/images/modules/computer-science/qft/phi2.avif new file mode 100644 index 00000000000..0e96951b8fe Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/phi2.avif differ diff --git a/public/learning/images/modules/computer-science/qft/phi3.avif b/public/learning/images/modules/computer-science/qft/phi3.avif new file mode 100644 index 00000000000..3a917a12e86 Binary files /dev/null and b/public/learning/images/modules/computer-science/qft/phi3.avif differ diff --git a/qiskit_bot.yaml b/qiskit_bot.yaml index 7218a9cb80e..18b1b1cc7d5 100644 --- a/qiskit_bot.yaml +++ b/qiskit_bot.yaml @@ -1070,3 +1070,5 @@ notifications: - "@christopherporter1" "docs/tutorials/transverse-field-ising-model": - "@dsierrasosa" + "learning/modules/computer-science/qft": + - "@kcmccormibm" diff --git a/scripts/config/notebook-testing.toml b/scripts/config/notebook-testing.toml index 12e4465dd70..7db66318086 100644 --- a/scripts/config/notebook-testing.toml +++ b/scripts/config/notebook-testing.toml @@ -280,6 +280,7 @@ notebooks = [ "learning/modules/computer-science/grovers.ipynb", "learning/modules/computer-science/quantum-key-distribution.ipynb", "learning/modules/computer-science/quantum-teleportation.ipynb", + "learning/modules/computer-science/qft.ipynb", "learning/modules/quantum-mechanics/superposition-with-qiskit.ipynb", "learning/modules/quantum-mechanics/stern-gerlach-measurements-with-qiskit.ipynb", "learning/modules/quantum-mechanics/exploring-uncertainty-with-qiskit.ipynb",