{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Paddle Quantum Quick Start Manual\n",
"\n",
" Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"# Overview\n",
"\n",
"Quantum computing (QC) is a new computing model laying in the intersection of quantum mechanics and theory of computation. QC follows the fundamental laws of quantum theory to manipulate quantum bits (qubits). For many specific computational tasks, it is widely believed that quantum algorithms exhibit advantages over classical algorithms at least in theory. For a systematic introduction to the subject of QC, we refer to [1-2].\n",
"\n",
"In recent years, one of the popular research topics in QC is combining the potential of quantum computing and artificial intelligence. Quantum machine learning (QML) is such an interdisciplinary subject. Researchers want to utilize the information processing advantages of quantum computing to promote the development of artificial intelligence. On the other side, it is also worth exploring the possibility of using artificial intelligence technology to break through the bottleneck of quantum computing research and development. For introductory materials about quantum machine learning, please refer to [3-5].\n",
"\n",
"Here, we provide a quick start for users to get started with Paddle Quantum. Currently, you can read all the content online or download the Jupyter Notebook from our [GitHub](https://github.com/PaddlePaddle/Quantum/tree/master/introduction). In terms of content, the quick start includes the following sections:\n",
"\n",
"- Introduction to quantum computing and quantum neural network (QNN)\n",
"- Introduction to Paddle Quantum\n",
"- PaddlePaddle optimizer tutorial\n",
"- A case study on quantum machine learning - Variational Quantum Eigensolver (VQE)\n",
"\n",
"**Latest version updated on:** Mar. 2nd, 2021 by Paddle Quantum developers.\n",
"\n",
"---\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Quantum Computing Fundamentals\n",
"\n",
"Quantum Computing (QC) uses unique phenomena in quantum physics (quantum superposition, quantum interference, and quantum entanglement) to design algorithms and help solve specific tasks in physics, chemistry, and optimization theory. There are several existing quantum computation models including the Adiabatic Quantum Computation (AQC) based on the adiabatic theorem and Measurement-Based Quantum Computation (MBQC). This introduction will focus on the most widely used Quantum Circuit model. In quantum circuits, the basic computation unit is the quantum bit (qubit), which is similar to the concept of bit in classical computers. Classical bits can only be in one of the two states, 0 or 1. By comparison, qubits can not only be in states $|0\\rangle$ and $|1\\rangle$ but also in a superposition state (we will explain this concept later). Quantum circuit model utilize quantum logic gates to manipulate the states of these qubits. The mathematics behind this process is linear algebra in the complex domain. Here we assume the readers are already familiar with linear algebra."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is a qubit?\n",
"\n",
"### Mathematical representation\n",
"\n",
"In quantum mechanics, the state of a two-level quantum system (e.g. electron spin) can be expressed as a state vector obtained through linear combinations of the following orthonormal basis,\n",
"\n",
"$$\n",
"|0\\rangle := \\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix}, \\quad |1\\rangle := \\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix}.\n",
"\\tag{1}\n",
"$$\n",
"\n",
"The vector representation here follows the Dirac notation (bra-ket) in quantum physics. This orthonormal basis $\\{|0\\rangle, |1\\rangle \\}$ is known as the **computational basis**. Physically, one could consider $|0\\rangle$ and $|1\\rangle$ as the energy ground state and excited state of an atom, respectively. All possible pure states of a qubit can be regarded as normalized vectors in the two-dimensional Hilbert space. Moreover, multi-qubit states can be represented by unit vectors in high-dimensional Hilbert space where the basis is the tensor product of $\\{|0\\rangle, |1\\rangle\\}$. For example, a 2-qubit quantum state can be represented by a unit complex vector in a 4-dimensional Hilbert space with the orthonormal basis,\n",
"\n",
"$$\n",
"\\left\\{\n",
"|00\\rangle = |0\\rangle\\otimes |0\\rangle := \\begin{bmatrix} 1 \\\\ 0 \\\\ 0 \\\\ 0 \\end{bmatrix}, \\quad \n",
"|01\\rangle = |0\\rangle\\otimes |1\\rangle := \\begin{bmatrix} 0 \\\\ 1 \\\\ 0 \\\\ 0 \\end{bmatrix}, \\quad\n",
"|10\\rangle = |1\\rangle\\otimes |0\\rangle := \\begin{bmatrix} 0 \\\\ 0 \\\\ 1 \\\\ 0 \\end{bmatrix}, \\quad\n",
"|11\\rangle = |1\\rangle\\otimes |0\\rangle := \\begin{bmatrix} 0 \\\\ 0 \\\\ 0 \\\\ 1 \\end{bmatrix}\n",
"\\right\\}.\n",
"\\tag{2}\n",
"$$\n",
"\n",
"By convention, the leftmost position in ket notation represents the first qubit $q_0$, the second position represents the second qubit $q_1$, and so on. The symbol $\\otimes$ denotes the tensor product operation. It works as follows: Given two matrices $A_{m\\times n}$ and $B_{p \\times q}$, then the tensor product of $A, B$ is\n",
"\n",
"$$\n",
"A \\otimes B =\n",
"\\begin{bmatrix}\n",
"a_{11}B & \\cdots & a_{1 n}B\\\\\n",
"\\vdots & \\ddots & \\vdots \\\\\n",
"a_{m1}B & \\cdots & a_{m n}B\n",
"\\end{bmatrix}_{(mp)\\times (nq)}.\n",
"\\tag{3}\n",
"$$\n",
"\n",
"Any single qubit quantum state $|\\psi\\rangle$ can be written as a linear combination (superposition) of the basis vectors $|0\\rangle$ and $|1\\rangle$. \n",
"\n",
"$$\n",
"|\\psi\\rangle = \\alpha |0\\rangle + \\beta |1\\rangle\n",
":= \\begin{bmatrix} \\alpha \\\\ \\beta \\end{bmatrix}.\n",
"\\tag{4}\n",
"$$\n",
"\n",
"where $\\alpha$ and $\\beta$ are **complex numbers** referred as the probability amplitudes. According to Born Rule, the probability to find the qubit in $|0\\rangle$ state is $|\\alpha|^2$; and the probability of $|1\\rangle$ is $|\\beta|^2$. Since the sum of probabilities equals to 1, one should introduce the constraint: $|\\alpha|^2 + |\\beta|^2 = 1$.\n",
"\n",
"\n",
"### Bloch sphere representation\n",
"\n",
"Geometrically, one can use a point on the unit sphere to represent the pure quantum state of a qubit. This sphere is called the **Bloch sphere**, (see Figure 1)\n",
"\n",
"$$\n",
"|\\psi\\rangle = \\alpha |0\\rangle + \\beta |1\\rangle\n",
"= \\cos\\bigg(\\frac{\\theta}{2}\\bigg) |0\\rangle + e^{i\\varphi}\\sin\\bigg(\\frac{\\theta}{2}\\bigg) |1\\rangle.\n",
"\\tag{5}\n",
"$$\n",
"\n",
"**Note**: The states of multi-qubit quantum systems cannot be directly represented by Bloch spheres. For a classical bit in state 0 or 1, it corresponds to the north or south pole of the Bloch sphere. **A qubit state can be at any point on the sphere. Such a superposition state is impossible for classical bits**. For example, the quantum state $\\frac{1}{\\sqrt{2}}\\big(|0\\rangle + i|1\\rangle\\big)$ is at the intersection of the equator and $+y$ axis.\n",
"\n",
"![intro-fig-bloch](./figures/intro-fig-bloch.png \"**Figure 1.** Bloch sphere representation of a single qubit. [[Image source]](https://en.wikipedia.org/wiki/Qubit)\")\n",
"\n",
"The following content is for readers who are more familiar with quantum computing. If you find it difficult to follow, don’t worry, you can skip it. Due to the interaction between qubits and decoherence, for a system with multiple qubits, its single-qubit subsystem will no longer stay in pure states, but a mixed state in general. The concept of mixed state can be regarded as a statistical mixture of multiple pure states. **The single-bit mixed state can be seen as a point inside the Bloch sphere**. The mixed state needs to be described in the density matrix formulation of quantum mechanics, for example the following mixed state has $1/2$ probability to be in $|0\\rangle$ or $|1\\rangle$,\n",
"\n",
"$$\n",
"\\rho_{\\text{mixed}} = \\sum_i P_i |\\psi_i\\rangle\\langle\\psi_i| = \\frac{1}{2} |0\\rangle\\langle0| + \\frac{1}{2} |1\\rangle\\langle1| := \\frac{1}{2} \\begin{bmatrix} 1 \\\\ 0\\end{bmatrix} \\begin{bmatrix} 1 & 0 \\end{bmatrix} + \\frac{1}{2} \\begin{bmatrix} 0 \\\\ 1\\end{bmatrix} \\begin{bmatrix} 0 & 1 \\end{bmatrix} = \\frac{1}{2} \\begin{bmatrix} 1 & 0\\\\ 0 & 1 \\end{bmatrix}.\n",
"\\tag{6}\n",
"$$\n",
"\n",
"The row vector (bra) $\\langle0| = |0\\rangle^\\dagger$ is the conjugate transpose of the column vector (ket) $|0\\rangle$.\n",
"\n",
"**Note:** For more information, please refer to [Wikipedia](https://en.wikipedia.org/wiki/Qubit)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is a quantum logic gate?\n",
"\n",
"In classical computers, we can apply basic logical operations (NOT gates, NAND gates, XOR gates, AND gates, and OR gates) on classical bits and combine them into more complicated operations. Quantum computing has a completely different set of logical operations, which are called quantum gates. We cannot compile existing C++ programs on a quantum computer. Because **classical computers and quantum computers have different logic gate structures, quantum algorithms need to be constructed using quantum gates**. Mathematically, a quantum gates can be expressed as a unitary matrix. Unitary operations could preserve vector length, which is a desirable property. Otherwise, if we operate on a pure state, it will be degraded into a mixed state, making it unreliable for the following running time. The unitary matrix is defined as:\n",
"\n",
"$$\n",
"U^{\\dagger}U = UU^{\\dagger} = I,\n",
"\\quad \\text{and} \\quad\n",
"\\Vert |\\psi\\rangle \\Vert = \\Vert U|\\psi\\rangle\\Vert = 1.\n",
"\\tag{7}\n",
"$$\n",
"\n",
"where $U^{\\dagger}$ is the conjugate transpose of $U$, and $I$ represents the identity matrix. But what is the physical meaning of representing quantum gates as unitary matrices? This implies that all quantum gates must be reversible. For any gate logic $U$, one can always find the corresponding reverse operation $U^\\dagger$. In addition, the unitary matrix must be a square matrix, because the input and output of the quantum operation require the same number of qubits. A quantum gate acting on $n$ qubits can be written as a $2^n \\times 2^n$ unitary matrix. The most common quantum gates act on one or two qubits, just like classical logic gates.\n",
"\n",
"### Single-qubit gate\n",
"\n",
"Next, we introduce single-qubit gates in quantum computing, including the Pauli matrices $\\{X, Y, Z\\}$, single-bit rotation gates $\\{R_x, R_y, R_z\\}$ and the Hadamard gate $H$. Firstly, **NOT gate** is important for both classical and quantum computing,\n",
"\n",
"$$\n",
"X := \\begin{bmatrix} 0 &1 \\\\ 1 &0 \\end{bmatrix}.\n",
"\\tag{8}\n",
"$$\n",
"\n",
"This quantum gate (unitary matrix) acts on the state of a single qubit (a complex vector). The operation is essentially **multiplication between a matrix and a column vector**:\n",
"\n",
"$$\n",
"X |0\\rangle := \\begin{bmatrix} 0 &1 \\\\ 1 &0 \\end{bmatrix} \\begin{bmatrix} 1 \\\\0 \\end{bmatrix}\n",
"=\\begin{bmatrix} 0 \\\\1 \\end{bmatrix} = |1\\rangle,\n",
"\\quad \\text{and} \\quad\n",
"X |1\\rangle := \\begin{bmatrix} 0 &1 \\\\ 1 &0 \\end{bmatrix} \\begin{bmatrix} 0 \\\\1 \\end{bmatrix}\n",
"=\\begin{bmatrix} 1 \\\\0 \\end{bmatrix}=|0\\rangle.\n",
"\\tag{9}\n",
"$$\n",
"\n",
"Recall the Bloch sphere representation, this operation $X$ acting on a qubit state (a point on the Bloch sphere) is equivalent to **a rotation about the $x$ axis in the Bloch sphere with angle $\\pi$** . This is why $X$ can be expressed as $R_x(\\pi)$ (differ by a global phase $e^{-i\\pi/2} = -i$ ). The other two Pauli matrices $Y$ and $Z$ are very similar in this sense (representing rotation around the $y$ and $z$ axes with angle $\\pi$):\n",
"\n",
"$$\n",
"Y := \\begin{bmatrix} 0 &-i \\\\ i &0 \\end{bmatrix},\n",
"\\quad \\text{and} \\quad \n",
"Z := \\begin{bmatrix} 1 &0 \\\\ 0 &-1 \\end{bmatrix}.\n",
"\\tag{10}\n",
"$$\n",
"\n",
"Generally speaking, any quantum gate that rotates $\\theta$ on the corresponding axis on the Bloch sphere can be expressed as\n",
"\n",
"$$\n",
"R_x(\\theta) := \n",
"\\begin{bmatrix} \n",
"\\cos \\frac{\\theta}{2} &-i\\sin \\frac{\\theta}{2} \\\\ \n",
"-i\\sin \\frac{\\theta}{2} &\\cos \\frac{\\theta}{2} \n",
"\\end{bmatrix}\n",
",\\quad \n",
"R_y(\\theta) := \n",
"\\begin{bmatrix}\n",
"\\cos \\frac{\\theta}{2} &-\\sin \\frac{\\theta}{2} \\\\ \n",
"\\sin \\frac{\\theta}{2} &\\cos \\frac{\\theta}{2} \n",
"\\end{bmatrix}\n",
",\\quad \n",
"R_z(\\theta) := \n",
"\\begin{bmatrix}\n",
"e^{-i\\frac{\\theta}{2}} & 0 \\\\ \n",
"0 & e^{i\\frac{\\theta}{2}}\n",
"\\end{bmatrix}.\n",
"\\tag{11}\n",
"$$\n",
"\n",
"In addition to the rotation gates, the most important single-qubit gate is the Hadamard gate. The corresponding Bloch spherical interpretation consists of two separate rotations, first rotating $\\pi$ around the $z$-axis, and then rotating $\\pi/2$ around the $y$-axis. Its matrix representation is\n",
"\n",
"$$\n",
"H := \\frac{1}{\\sqrt{2}}\\begin{bmatrix} 1 &1 \\\\ 1 &-1 \\end{bmatrix}.\n",
"\\tag{12}\n",
"$$\n",
"\n",
"### Two-bit quantum gate\n",
"\n",
"We can expand the idea of single-qubit gates to multi-qubit. There are two ways to realize this expansion. The first is to apply single-qubit gates on selected qubits, while the other qubits are not operated. The figure below gives a concrete example:\n",
"\n",
"![intro-fig-hadamard](./figures/intro-fig-hadamard.png \"**Figure 2.** Circuit representation and interpretation of two-qubit logic operations. [[Image source]](https://en.wikipedia.org/wiki/Quantum_logic_gate)\")\n",
"\n",
"The quantum gate acting on two-qubit system can be expressed as a $4\\times4$ unitary matrix\n",
"\n",
"$$\n",
"U = H \\otimes I \n",
"= \\frac{1}{\\sqrt{2}} \\begin{bmatrix} 1 &1 \\\\ 1 &-1 \\end{bmatrix} \n",
"\\otimes \\begin{bmatrix} 1 &0 \\\\ 0 &1 \\end{bmatrix} \n",
"= \\frac{1}{\\sqrt{2}} \\,\n",
"\\begin{bmatrix}\n",
"1 &0 &1 &0 \\\\ \n",
"0 &1 &0 &1 \\\\\n",
"1 &0 &-1 &0 \\\\\n",
"0 &1 &0 &-1 \n",
"\\end{bmatrix}.\n",
"\\tag{13}\n",
"$$\n",
"\n",
"Another way is to apply two-qubit gates directly. For example, $\\text{CNOT}$ gate will make the state of one qubit affect another qubit state.\n",
"\n",
"$$\n",
"\\text{CNOT} :=\n",
"\\begin{bmatrix}\n",
"1 &0 &0 &0 \\\\\n",
"0 &1 &0 &0 \\\\\n",
"0 &0 &0 &1 \\\\\n",
"0 &0 &1 &0\n",
"\\end{bmatrix},\n",
"\\tag{14}\n",
"$$\n",
"\n",
"When $\\text{CNOT}$ acts on the computational basis, we have\n",
"\n",
"$$\n",
"\\text{CNOT} |00\\rangle = |00\\rangle, \\quad\n",
"\\text{CNOT} |01\\rangle = |01\\rangle, \\quad\n",
"\\text{CNOT} |10\\rangle = |11\\rangle, \\quad\n",
"\\text{CNOT} |11\\rangle = |10\\rangle.\n",
"\\tag{15}\n",
"$$\n",
"\n",
"We can conclude that when the first qubit is in the $|1\\rangle$ state, $\\text{CNOT}$ will act $X$ gate on the second qubit. If the first qubit is in the $|0\\rangle$ state, then the second qubit is not affected in any way. This is why $\\text{CNOT}$ stands for the controlled-$\\text{NOT}$ gate. The following list contains frequently used quantum gates and their matrix representations. **All of these quantum gates can be called in Paddle Quantum**.\n",
"\n",
"![intro-fig-gates](./figures/intro-fig-gates.png \"**Figure 3.** List of common quantum gates. [[Image source]](https://en.wikipedia.org/wiki/Quantum_logic_gate)\")\n",
"\n",
"**Note**: For more information, please see the following Wikipedia [link](https://en.wikipedia.org/wiki/Quantum_logic_gate)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is measurement in quantum mechanics?\n",
"\n",
"For a two-level quantum system, such as the spin of an electron, it can be spin up $\\uparrow$ or spin down $\\downarrow$, corresponding to state $|0\\rangle$ and state $|1\\rangle$. As mentioned before, the electron can be in a superposition state of spin up and down, which is $|\\psi\\rangle =\\alpha |0\\rangle + \\beta |1\\rangle$. The measurement will help us further understand what is a superposition state. It is worth noting that the measurement in quantum mechanics usually refers to a statistical result rather than a single measurement. This is due to the nature of measurements in quantum physics, which collapses the observed quantum state. For example, if we measure an electron in state $|\\psi\\rangle =\\alpha |0\\rangle + \\beta |1\\rangle$, we will have a probability of $|\\alpha|^2$ to obtain the measurement results of spin up, and after measurement, the quantum state collapses to the post-measurement state $ |0\\rangle$. Similarly, we also have a probability of $|\\beta|^2$ to get the spin down post-measurement state $|1\\rangle$. So if we want to get the value of $\\alpha$ accurately, one experiment is obviously not enough. We need to prepare a lot of electrons in the superposition state $\\alpha |0\\rangle + \\beta |1\\rangle$, measure the spin of each electron, and then count the frequency. Measurement has a special place in quantum mechanics. If the reader finds it difficult to understand, we refer to [Measurement in Quantum Mechanics](https://en.wikipedia.org/wiki/Measurement_in_quantum_mechanics) for more information."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example and exercise\n",
"\n",
"### Example: Using Paddle Quantum to create a $X$ gate\n",
"\n",
"**Note:** All single-bit rotation gates are established as follows:\n",
"\n",
"$$\n",
"R_x(\\theta) :=\n",
"\\begin{bmatrix}\n",
"\\cos \\frac{\\theta}{2} &-i\\sin \\frac{\\theta}{2} \\\\\n",
"-i\\sin \\frac{\\theta}{2} &\\cos \\frac{\\theta}{2}\n",
"\\end{bmatrix}\n",
",\\quad\n",
"R_y(\\theta) :=\n",
"\\begin{bmatrix}\n",
"\\cos \\frac{\\theta}{2} &-\\sin \\frac{\\theta}{2} \\\\\n",
"\\sin \\frac{\\theta}{2} &\\cos \\frac{\\theta}{2}\n",
"\\end{bmatrix}\n",
",\\quad\n",
"R_z(\\theta) :=\n",
"\\begin{bmatrix}\n",
"e^{-i\\frac{\\theta}{2}} & 0 \\\\\n",
"0 & e^{i\\frac{\\theta}{2}}\n",
"\\end{bmatrix}.\n",
"\\tag{16}\n",
"$$\n",
"\n",
"Therefore, it is not difficult to see that the $X$ gate can be expressed as $R_x(\\pi)$. The following code will generate the $X$ gate:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2021-04-30T09:20:08.888161Z",
"start_time": "2021-04-30T09:20:05.709738Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The matrix representation of quantum gate is:\n",
"[[ 6.123234e-17+0.j -6.123234e-17-1.j]\n",
" [ 6.123234e-17-1.j 6.123234e-17+0.j]]\n"
]
}
],
"source": [
"import numpy as np\n",
"import paddle\n",
"from paddle_quantum.circuit import UAnsatz\n",
"from paddle_quantum.utils import dagger, random_pauli_str_generator, pauli_str_to_matrix\n",
"from paddle_quantum.state import vec, vec_random, density_op, density_op_random, completely_mixed_computational\n",
"\n",
"from paddle import matmul, transpose, trace \n",
"\n",
"\n",
"# Set the angle parameter theta = pi\n",
"theta = np.array([np.pi])\n",
" \n",
"# We need to convert numpy.ndarray to Tensor in PaddlePaddle\n",
"theta = paddle.to_tensor(theta)\n",
"\n",
"# Set the number of qubits required for calculation\n",
"num_qubits = 1\n",
"\n",
"# Initialize our single-bit quantum circuit\n",
"cir = UAnsatz(num_qubits)\n",
"\n",
"# Apply an Rx rotation gate to the first qubit (q0), the angle is pi\n",
"which_qubit = 0\n",
"cir.rx(theta, which_qubit)\n",
"\n",
"# Convert to numpy.ndarray\n",
"# Print out this quantum gate\n",
"print('The matrix representation of quantum gate is:')\n",
"print(cir.U.numpy())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is a global phase $-i$ in front between the output and the $X$ (NOT) gate:\n",
"\n",
"$$\n",
"\\text{output} = \\begin{bmatrix} 0 &-i \\\\ -i &0 \\end{bmatrix}\n",
"= -i\\begin{bmatrix} 0 &1 \\\\ 1 &0 \\end{bmatrix} = -i X.\n",
"\\tag{17}\n",
"$$\n",
"\n",
"Can you figure out why such a global phase is not important in quantum computing? And not important in what sense?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercise: Create a $Y$ gate\n",
"\n",
"Similar to the $X$ gate, try to create a $Y$ gate by filling the following code. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:55:00.653853Z",
"start_time": "2021-03-09T03:55:00.404797Z"
}
},
"outputs": [],
"source": [
"theta = \"your code\"\n",
" \n",
"theta = paddle.to_tensor(theta)\n",
"num_qubits = 1\n",
"cir = UAnsatz(\"your code\")\n",
"cir.ry(\"your code\")\n",
"print(cir.U.numpy())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As mentioned before, we have a global phase $-i$ in front:\n",
"\n",
"$$\n",
"\\text{output} = \\begin{bmatrix} 0 &-1 \\\\ 1 &0 \\end{bmatrix}\n",
"= -i\\begin{bmatrix} 0 &-i \\\\ i &0 \\end{bmatrix} = -i Y.\n",
"\\tag{18}\n",
"$$\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Quantum Neural Network\n",
"\n",
"After the preparations above, we now have the necessary knowledge to have a taste on quantum machine learning (QML). QML uses quantum circuits to replace classical neural networks to complete machine learning tasks. So we need to prepare a parameterized quantum circuit (PQC), which is also called Quantum neural network (QNN) or Ansatz. The quantum circuit parameters are adjustable (usually these parameters are the angles $\\theta$ of rotation gates). As we have seen in the last section, using angle $\\pi$ to create a $X$ gate is probably the simplest QNN. If we design an appropriate loss function, then certain computational tasks can be transformed into optimization problems. Keep adjusting the parameters in PQC until the loss function convergences. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example: How to create a quantum neural network?\n",
"\n",
"QNN can usually be expressed as a combination of single-qubit gates and two-qubit gates. One of the widely used circuit architectures is the hardware-efficient ansatz consists of $\\{R_x, R_y, R_z, \\text{CNOT}_{j,j+1} \\}$. They are easy to implement on near-term devices (usually superconducting qubits) because $\\text{CNOT}_{j,j+1} $ only works on adjacent qubits and hence mitigate the topological connectivity issues. The figure below gives us a concrete example:\n",
"\n",
"![intro-fig-gate1](./figures/intro-fig-gate1.png)\n",
"\n",
"Each horizontal line here represents a qubit. We define the upper qubit to be the first qubit $q_0$; the lower one is the second qubit $q_1$. From left to right, it represents the order that we apply quantum gates. The leftmost quantum gate will be applied first. Next, let’s take a look on how to build this simple two-qubit quantum neural network on Paddle Quantum."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2021-04-30T09:20:13.355621Z",
"start_time": "2021-04-30T09:20:13.331839Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The matrix representation of the quantum neural network U(theta=pi) in the figure is:\n",
"[[ 0.0000000e+00 -1.0000000e+00 6.1232340e-17 -6.1232340e-17]\n",
" [-1.0000000e+00 0.0000000e+00 -6.1232340e-17 6.1232340e-17]\n",
" [-6.1232340e-17 6.1232340e-17 1.0000000e+00 1.2246468e-16]\n",
" [ 6.1232340e-17 -6.1232340e-17 -1.2246468e-16 1.0000000e+00]]\n"
]
}
],
"source": [
"# Set the angle parameter theta\n",
"theta = np.full([4], np.pi)\n",
" \n",
"# We need to convert numpy.ndarray to Tensor in PaddlePaddle\n",
"theta = paddle.to_tensor(theta)\n",
"\n",
"# Initialize the quantum circuit\n",
"num_qubits = 2\n",
"cir = UAnsatz(num_qubits)\n",
"\n",
"# Add single-qubit rotation gates\n",
"cir.ry(theta[0], 0)\n",
"cir.ry(theta[1], 1)\n",
"\n",
"# Add two-qubit gate\n",
"cir.cnot([0, 1])\n",
"\n",
"# Add single-qubit rotation gates\n",
"cir.ry(theta[2], 0)\n",
"cir.ry(theta[3], 1)\n",
"\n",
"print('The matrix representation of the quantum neural network U(theta=pi) in the figure is:')\n",
"print(cir.U.numpy().real)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
"\\text{output} = \n",
"\\begin{bmatrix} \n",
"0 &-1 &0 &0 \\\\ \n",
"-1 &0 &0 &0 \\\\\n",
"0 &0 &1 &0 \\\\\n",
"0 &0 &0 &1 \n",
"\\end{bmatrix}.\n",
"\\tag{19}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise\n",
"\n",
"Given the following code, can you work out the corresponding circuit?"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2021-04-30T09:20:15.796777Z",
"start_time": "2021-04-30T09:20:15.786887Z"
}
},
"outputs": [],
"source": [
"theta = np.full([6], np.pi)\n",
"\n",
"theta = paddle.to_tensor(theta)\n",
"\n",
"num_qubits = 3\n",
"cir = UAnsatz(num_qubits)\n",
"\n",
"cir.ry(theta[0], 0)\n",
"cir.ry(theta[1], 1)\n",
"cir.ry(theta[2], 2)\n",
"\n",
"cir.cnot([0, 1])\n",
"cir.cnot([1, 2])\n",
"\n",
"cir.ry(theta[3], 0)\n",
"cir.ry(theta[4], 1)\n",
"cir.ry(theta[5], 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Answer:\n",
"\n",
"![intro-fig-gate2](./figures/intro-fig-gate2.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can print your circuit using Paddle Quantum as follows:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2021-04-30T09:20:18.642677Z",
"start_time": "2021-04-30T09:20:18.636895Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--Ry(3.142)----*----Ry(3.142)---------------\n",
" | \n",
"--Ry(3.142)----X--------*--------Ry(3.142)--\n",
" | \n",
"--Ry(3.142)-------------X--------Ry(3.142)--\n",
" \n"
]
}
],
"source": [
"print(cir)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Built-in circuit templates\n",
"\n",
"In the latest version of Paddle Quantum, we provide some built-in circuit templates to make users' life easier."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-09T03:55:20.402902Z",
"start_time": "2021-03-09T03:55:19.838729Z"
},
"scrolled": false
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"