{ "cells": [ { "cell_type": "markdown", "id": "64968873", "metadata": {}, "source": [ "[Qiskit](https://qiskit.org/documentation/index.html) is an open source programming language\n", "developped by IBM and built on top of the Python programming language. It integrates with [OpenQASM](openqasm.html) (with some hopefully temporary friction around OpenQASM versioning). In fact, Qiskit more accurately refers to a family of integrated frameworks for quantum computation. The most important of these are:\n", "\n", "* **[Qiskit Terra](https://qiskit.org/documentation/apidoc/terra.html)**, a circuit description language with a Python-like syntax and the foundation of the Qiskit software stack. It also includes tools for the visualisation of circuits and quantum states, and for the optimisation of circuits for specific hardware devices.\n", "\n", "* **[Qiskit Aer](https://qiskit.org/documentation/apidoc/aer.html)**, which provides facilities for classically simulating those circuits, along with realistic noise models for those simultations.\n", "\n", "Along with classical simulations, Qiskit can also be used to interact with various [quantum hardware providers](https://qiskit.org/documentation/partners/) in the cloud." ] }, { "cell_type": "markdown", "id": "aa7781da", "metadata": {}, "source": [ "# Tutorial\n", "\n", "## Describing quantum circuits" ] }, { "cell_type": "markdown", "id": "fc118ee8", "metadata": {}, "source": [ "Qiskit is provided as a set of packages for Python. As a result, its syntax follows the rules of Python. For starters, we import the package as usual:" ] }, { "cell_type": "code", "execution_count": 1, "id": "3c4c99ca", "metadata": {}, "outputs": [], "source": [ "import qiskit as qs" ] }, { "cell_type": "markdown", "id": "5227a5e1", "metadata": {}, "source": [ "The core class for describing a quantum circuit in Qiskit is, unsurprisingly, the `QuantumCircuit` class. Its constructor takes up to two integer arguments, used to specificy the number of quantum and classical registers (qubits and bits):" ] }, { "cell_type": "code", "execution_count": 2, "id": "5b38448c", "metadata": {}, "outputs": [], "source": [ "qc1 = qs.QuantumCircuit(4) # Construct a QuantumCircuit with 4 qubits\n", "qc2 = qs.QuantumCircuit(4,3) # ... or with 4 qubits and 3 bits" ] }, { "cell_type": "markdown", "id": "c68ad93e", "metadata": {}, "source": [ "We can then enact gates on the circuit by calling the corresponding methods of the class:" ] }, { "cell_type": "code", "execution_count": 3, "id": "8d7c5e50", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc1.h(0)\n", "qc1.cx(0,1)\n", "qc1.cx(3,1)" ] }, { "cell_type": "markdown", "id": "26ec9ba5", "metadata": {}, "source": [ "We can then visualise this circuit with `qc1.draw()`:" ] }, { "cell_type": "code", "execution_count": 4, "id": "199db4ee", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
     ┌───┐          \n",
       "q_0: ┤ H ├──■───────\n",
       "     └───┘┌─┴─┐┌───┐\n",
       "q_1: ─────┤ X ├┤ X ├\n",
       "          └───┘└─┬─┘\n",
       "q_2: ────────────┼──\n",
       "                 │  \n",
       "q_3: ────────────■──\n",
       "                    
" ], "text/plain": [ " ┌───┐ \n", "q_0: ┤ H ├──■───────\n", " └───┘┌─┴─┐┌───┐\n", "q_1: ─────┤ X ├┤ X ├\n", " └───┘└─┬─┘\n", "q_2: ────────────┼──\n", " │ \n", "q_3: ────────────■──\n", " " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc1.draw()" ] }, { "cell_type": "markdown", "id": "ce7a9000", "metadata": {}, "source": [ "As you can see, the register of qubits is 0-indexed. Qiskit implements a large number of quantum gates in this way. Rather than listing them all here, we instead refer to the class [documentation](https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.html) (or equivalently to calling `help(qs.QuantumCircuit)` within Python)." ] }, { "cell_type": "markdown", "id": "1f5fa251", "metadata": {}, "source": [ "Measurements are a little different: they of course require access to a classical bit to store the outcome of the measurement." ] }, { "cell_type": "code", "execution_count": 5, "id": "37c1c21c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
     ┌───┐             \n",
       "q_0: ┤ H ├──■──────────\n",
       "     └───┘┌─┴─┐┌───┐   \n",
       "q_1: ─────┤ X ├┤ X ├───\n",
       "          └───┘└─┬─┘   \n",
       "q_2: ────────────┼─────\n",
       "                 │  ┌─┐\n",
       "q_3: ────────────■──┤M├\n",
       "                    └╥┘\n",
       "c: 3/════════════════╩═\n",
       "                     0 
" ], "text/plain": [ " ┌───┐ \n", "q_0: ┤ H ├──■──────────\n", " └───┘┌─┴─┐┌───┐ \n", "q_1: ─────┤ X ├┤ X ├───\n", " └───┘└─┬─┘ \n", "q_2: ────────────┼─────\n", " │ ┌─┐\n", "q_3: ────────────■──┤M├\n", " └╥┘\n", "c: 3/════════════════╩═\n", " 0 " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc2.h(0)\n", "qc2.cx(0,1)\n", "qc2.cx(3,1)\n", "\n", "qc2.measure(3,0)\n", "\n", "qc2.draw()" ] }, { "cell_type": "markdown", "id": "210037af", "metadata": {}, "source": [ "We can also initialise qubits with the following syntax:" ] }, { "cell_type": "code", "execution_count": 6, "id": "e54c5cf0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
            ┌───┐                 \n",
       "q_0: ───────┤ H ├─────────■───────\n",
       "            └───┘       ┌─┴─┐┌───┐\n",
       "q_1: ───────────────────┤ X ├┤ X ├\n",
       "     ┌─────────────────┐└───┘└─┬─┘\n",
       "q_2: ┤ Initialize(0,1) ├───────┼──\n",
       "     └─────────────────┘       │  \n",
       "q_3: ──────────────────────────■──\n",
       "                                  
" ], "text/plain": [ " ┌───┐ \n", "q_0: ───────┤ H ├─────────■───────\n", " └───┘ ┌─┴─┐┌───┐\n", "q_1: ───────────────────┤ X ├┤ X ├\n", " ┌─────────────────┐└───┘└─┬─┘\n", "q_2: ┤ Initialize(0,1) ├───────┼──\n", " └─────────────────┘ │ \n", "q_3: ──────────────────────────■──\n", " " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc3 = qc1.copy()\n", "qc3.initialize([0,1], 2)\n", "qc3.draw()" ] }, { "cell_type": "markdown", "id": "97717579", "metadata": {}, "source": [ "The first argument to `initialize` describes a qubit state as a pair of coefficients in the computational basis. The second argument describes the target qubit in the circuit for initialisation." ] }, { "cell_type": "markdown", "id": "c15c609b", "metadata": {}, "source": [ "We can then compose circuits with matching registers simply by \"summing\" them:" ] }, { "cell_type": "code", "execution_count": 7, "id": "4d541859", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
     ┌───┐                    \n",
       "q_0: ┤ H ├──■─────────────────\n",
       "     └───┘┌─┴─┐┌───┐          \n",
       "q_1: ─────┤ X ├┤ X ├───────■──\n",
       "          └───┘└─┬─┘       │  \n",
       "q_2: ────────────┼────■────┼──\n",
       "                 │  ┌─┴─┐┌─┴─┐\n",
       "q_3: ────────────■──┤ X ├┤ X ├\n",
       "                    └───┘└───┘
" ], "text/plain": [ " ┌───┐ \n", "q_0: ┤ H ├──■─────────────────\n", " └───┘┌─┴─┐┌───┐ \n", "q_1: ─────┤ X ├┤ X ├───────■──\n", " └───┘└─┬─┘ │ \n", "q_2: ────────────┼────■────┼──\n", " │ ┌─┴─┐┌─┴─┐\n", "q_3: ────────────■──┤ X ├┤ X ├\n", " └───┘└───┘" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc4 = qs.QuantumCircuit(4)\n", "\n", "qc4.cx(2,3)\n", "qc4.cx(1,3)\n", "\n", "qc1.compose(qc4).draw()" ] }, { "cell_type": "markdown", "id": "34ec277d", "metadata": {}, "source": [ "We can also `QuantumCircuit` whose quantum register is split into named subregisters. In order to understand this, we first need to introduce the `QuantumRegister` class, with an optional name:" ] }, { "cell_type": "code", "execution_count": 8, "id": "bf972a69", "metadata": {}, "outputs": [], "source": [ "qreg1 = qs.QuantumRegister(2,\"register1\")\n", "qreg2 = qs.QuantumRegister(2,\"register2\")" ] }, { "cell_type": "markdown", "id": "d209239a", "metadata": {}, "source": [ "`ClassicalRegister` has an analogous syntax for constructing classical registers. Then, we can build a `QuantumCircuit` from these registers:" ] }, { "cell_type": "code", "execution_count": 9, "id": "40f336ec", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
             ┌───┐     ┌───┐\n",
       "register1_0: ┤ H ├──■──┤ X ├\n",
       "             └───┘┌─┴─┐└─┬─┘\n",
       "register1_1: ─────┤ X ├──┼──\n",
       "                  └───┘  │  \n",
       "register2_0: ────────────┼──\n",
       "                         │  \n",
       "register2_1: ────────────■──\n",
       "                            
" ], "text/plain": [ " ┌───┐ ┌───┐\n", "register1_0: ┤ H ├──■──┤ X ├\n", " └───┘┌─┴─┐└─┬─┘\n", "register1_1: ─────┤ X ├──┼──\n", " └───┘ │ \n", "register2_0: ────────────┼──\n", " │ \n", "register2_1: ────────────■──\n", " " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc5 = qs.QuantumCircuit(qreg1, qreg2)\n", "\n", "qc5.h(0)\n", "qc5.cx(qreg1[0],qreg1[1])\n", "qc5.cx(qreg2[1],qreg1[0])\n", "\n", "qc5.draw()" ] }, { "cell_type": "markdown", "id": "409435c3", "metadata": {}, "source": [ "Finally, we can output a `QuantumCircuit` to an OpenQASM 2.0 string using the `qasm` method:" ] }, { "cell_type": "code", "execution_count": 10, "id": "e47fed18", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 3.0;\n", "include \"stdgates.inc\";\n", "qubit[2] register1;\n", "qubit[2] register2;\n", "h register1[0];\n", "cx register1[0], register1[1];\n", "cx register2[1], register1[0];\n", "\n" ] } ], "source": [ "print(qs.qasm3.dumps(qc5))" ] }, { "cell_type": "markdown", "id": "6df66e6f", "metadata": {}, "source": [ "## Simulating and running quantum circuits" ] }, { "cell_type": "markdown", "id": "bcd128d3", "metadata": {}, "source": [ "As described above, Qiskit also makes it possible to interact with various quantum hardward providers, as well as to run a simulation of a given quantum circuit on a classical backend. This allows one to extract (real or simulated) output states or outcome probabilites for measurements. The frontend for running circuits on actual quantum hardware is described in the [documentation](https://qiskit.org/documentation/partners/) and depends on the provider in question.\n", "\n", "We focus here on the classical simulation backends. These are provided via the `qiskit.providers.aer` submodule, specifically the [`AerSimulator`](https://qiskit.org/documentation/stubs/qiskit_aer.AerSimulator.html#qiskit_aer.AerSimulator) class." ] }, { "cell_type": "code", "execution_count": 11, "id": "76e1804f", "metadata": {}, "outputs": [], "source": [ "from qiskit_aer import AerSimulator" ] }, { "cell_type": "markdown", "id": "4e420037", "metadata": {}, "source": [ "In order to run the simulation, we need to set a initial state for each qubit. Here is an elementary example:" ] }, { "cell_type": "code", "execution_count": 12, "id": "4a2a580e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
   ┌─────────────────┐┌───┐\n",
       "q: ┤ Initialize(1,0) ├┤ H ├\n",
       "   └─────────────────┘└───┘
" ], "text/plain": [ " ┌─────────────────┐┌───┐\n", "q: ┤ Initialize(1,0) ├┤ H ├\n", " └─────────────────┘└───┘" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qc5 = qs.QuantumCircuit(1,0)\n", "qc5.initialize([1,0],0)\n", "qc5.h(0)\n", "qc5.draw()" ] }, { "cell_type": "markdown", "id": "029eac9e", "metadata": {}, "source": [ "And we can then run a simulation with:" ] }, { "cell_type": "code", "execution_count": 20, "id": "8ccdbdc9", "metadata": {}, "outputs": [ { "data": { "image/png": "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7NlTMTExiomJ0bZt27RmzRpNmjTJ3eXjv5jJQ6XlcDjUu3dvbdmyRZ9++qm6dOly2ddERUUpOjpaH374oapUqVIGVQJA2fvggw9011136fXXX9fYsWNlt9t1+vRp/fjjj9q+fbs2bNigr776SqmpqapXr566deum/fv3a+3atdq3b5/q1q3r7iFAzOShErNYLJo8ebJ69uypMWPGaPbs2erXr5/8/f1d+tlsNlmtVh08eFA1atRQVlYWAQ+A0WrWrKmIiAiFh4dLkjw8PBQcHKzg4GB16tRJd911l3bv3q3Nmzfryy+/1Lvvvqvz58+rZcuWBLxyhMsEUal1795df//735WRkaHRo0dr+PDhWrlypc6ePSubzabc3Fzn6dtVq1Zpz549uu2229xcNQCUru7du2vv3r3q2LGjs83hcMjhcMjDw0MRERHq16+fpk+fruXLl+vuu++WJI0dO9ZdJaMAnK4FJH388cd65ZVXFBcXJ0m67rrr1LVrV91www3KysrS8ePH9fbbbysmJkZffvkltxIAYLTL3fz90sAnSUOHDtV7772ntLS0fGdD4D6EPOC/Tpw4obVr12r16tX66quvdOLECV28eFE2m02SdPvtt+vpp59Ww4YN3VwpAJQfO3fuVP/+/dWwYUOtX7/e3eXgEoQ8oAC//PKLkpKSnM+tve6669SgQYNC7w0FAJXVd999p7lz52rcuHHcR7ScIeQB//X70w8AgKLJycmRl5eXu8vA7xDygAJceg887ocHAIXjM7L8IuQBAAAYiPNSAAAABiLkodK6dBI7bz0eAACmIOSh0jl27JgyMjKUm5ur1NRU53oS1pQAAEzCY81Qaezdu1fTp0/X7t27dejQIUVHR6tRo0a68cYb1apVKzVp0kS1atWSxEJiAJWPzWaTxWLhDgMG4cILVAoffvihJk+erEOHDql169bKycmRJB04cEApKSkKDw9X7969NXLkSLVv397N1QJA2UlOTnY+ozaPzWaTh4fHH37Zzc3Nlacnc0XlGSEPlULTpk3lcDj0wgsvqHfv3srNzdX+/ft15MgR7dy5U6tXr9a2bdvk5+enxx57TA8++KCqVavm7rIBoNRFR0dLkkaMGKFBgwapfv36zm0Oh0N2u935DG9ULIQ8GC8xMVE33nij/vrXv2rChAn5nsmYnZ2t5ORkrVu3Ts8995wO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", "text/plain": [ "
" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "simulator = AerSimulator()\n", "qc5.measure_all()\n", "qc5 = qs.transpile(qc5, simulator)\n", "result = simulator.run(qc5).result()\n", "counts = result.get_counts(qc5)\n", "from qiskit.tools.visualization import plot_histogram\n", "plot_histogram(counts)\n" ] }, { "cell_type": "markdown", "id": "fab31d8b", "metadata": {}, "source": [ "Although the topic is beyond the scope of this tutorial, one of the main selling points of Qiskit Aer is that it provides [realistic noise models](https://qiskit.org/documentation/apidoc/aer_noise.html) for simulating quantum circuits run on NISQ devices." ] }, { "cell_type": "markdown", "id": "acf32d65", "metadata": {}, "source": [ "# Deutsch-Jozsa algorithm" ] }, { "cell_type": "markdown", "id": "5757eb8f", "metadata": {}, "source": [ "For a complete explanation, see [here](https://qiskit.org/textbook/ch-algorithms/deutsch-jozsa.html).\n", "\n", "Let first define an the oracle circuit:" ] }, { "cell_type": "code", "execution_count": 17, "id": "016e5f43", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
     ┌───┐ ░                 ░ ┌───┐\n",
       "q_0: ┤ X ├─░───■─────────────░─┤ X ├\n",
       "     └───┘ ░   │             ░ └───┘\n",
       "q_1: ──────░───┼────■────────░──────\n",
       "     ┌───┐ ░   │    │        ░ ┌───┐\n",
       "q_2: ┤ X ├─░───┼────┼────■───░─┤ X ├\n",
       "     └───┘ ░ ┌─┴─┐┌─┴─┐┌─┴─┐ ░ └───┘\n",
       "q_3: ──────░─┤ X ├┤ X ├┤ X ├─░──────\n",
       "           ░ └───┘└───┘└───┘ ░      
" ], "text/plain": [ " ┌───┐ ░ ░ ┌───┐\n", "q_0: ┤ X ├─░───■─────────────░─┤ X ├\n", " └───┘ ░ │ ░ └───┘\n", "q_1: ──────░───┼────■────────░──────\n", " ┌───┐ ░ │ │ ░ ┌───┐\n", "q_2: ┤ X ├─░───┼────┼────■───░─┤ X ├\n", " └───┘ ░ ┌─┴─┐┌─┴─┐┌─┴─┐ ░ └───┘\n", "q_3: ──────░─┤ X ├┤ X ├┤ X ├─░──────\n", " ░ └───┘└───┘└───┘ ░ " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# initialization\n", "import numpy as np\n", " \n", "# importing Qiskit\n", "from qiskit import QuantumCircuit\n", "\n", "# set the length of the n-bit input string. \n", "n = 3\n", " \n", "balanced_oracle = QuantumCircuit(n+1)\n", "b_str = \"101\"\n", "\n", "# Place X-gates\n", "for qubit in range(len(b_str)):\n", " if b_str[qubit] == '1':\n", " balanced_oracle.x(qubit)\n", "\n", "# Use barrier as divider\n", "balanced_oracle.barrier()\n", "\n", "# Controlled-NOT gates\n", "for qubit in range(n):\n", " balanced_oracle.cx(qubit, n)\n", "\n", "balanced_oracle.barrier()\n", "\n", "# Place X-gates\n", "for qubit in range(len(b_str)):\n", " if b_str[qubit] == '1':\n", " balanced_oracle.x(qubit)\n", "\n", "# Show oracle\n", "balanced_oracle.draw()" ] }, { "cell_type": "markdown", "id": "1fab18e3", "metadata": {}, "source": [ "Then we can run the Deutsch-Jozsa algorithm with this oracle:" ] }, { "cell_type": "code", "execution_count": 18, "id": "b413f2c9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
     ┌───┐┌───┐ ░                 ░ ┌───┐┌───┐ ░ ┌─┐      \n",
       "q_0: ┤ H ├┤ X ├─░───■─────────────░─┤ X ├┤ H ├─░─┤M├──────\n",
       "     ├───┤└───┘ ░   │             ░ ├───┤└───┘ ░ └╥┘┌─┐   \n",
       "q_1: ┤ H ├──────░───┼────■────────░─┤ H ├──────░──╫─┤M├───\n",
       "     ├───┤┌───┐ ░   │    │        ░ ├───┤┌───┐ ░  ║ └╥┘┌─┐\n",
       "q_2: ┤ H ├┤ X ├─░───┼────┼────■───░─┤ X ├┤ H ├─░──╫──╫─┤M├\n",
       "     ├───┤├───┤ ░ ┌─┴─┐┌─┴─┐┌─┴─┐ ░ └───┘└───┘ ░  ║  ║ └╥┘\n",
       "q_3: ┤ X ├┤ H ├─░─┤ X ├┤ X ├┤ X ├─░────────────░──╫──╫──╫─\n",
       "     └───┘└───┘ ░ └───┘└───┘└───┘ ░            ░  ║  ║  ║ \n",
       "c: 3/═════════════════════════════════════════════╩══╩══╩═\n",
       "                                                  0  1  2 
" ], "text/plain": [ " ┌───┐┌───┐ ░ ░ ┌───┐┌───┐ ░ ┌─┐ \n", "q_0: ┤ H ├┤ X ├─░───■─────────────░─┤ X ├┤ H ├─░─┤M├──────\n", " ├───┤└───┘ ░ │ ░ ├───┤└───┘ ░ └╥┘┌─┐ \n", "q_1: ┤ H ├──────░───┼────■────────░─┤ H ├──────░──╫─┤M├───\n", " ├───┤┌───┐ ░ │ │ ░ ├───┤┌───┐ ░ ║ └╥┘┌─┐\n", "q_2: ┤ H ├┤ X ├─░───┼────┼────■───░─┤ X ├┤ H ├─░──╫──╫─┤M├\n", " ├───┤├───┤ ░ ┌─┴─┐┌─┴─┐┌─┴─┐ ░ └───┘└───┘ ░ ║ ║ └╥┘\n", "q_3: ┤ X ├┤ H ├─░─┤ X ├┤ X ├┤ X ├─░────────────░──╫──╫──╫─\n", " └───┘└───┘ ░ └───┘└───┘└───┘ ░ ░ ║ ║ ║ \n", "c: 3/═════════════════════════════════════════════╩══╩══╩═\n", " 0 1 2 " ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dj_circuit = QuantumCircuit(n+1, n)\n", "\n", "# Apply H-gates\n", "for qubit in range(n):\n", " dj_circuit.h(qubit)\n", " \n", "# Put qubit in state |->\n", "dj_circuit.x(n)\n", "dj_circuit.h(n)\n", "\n", "dj_circuit = QuantumCircuit(n+1, n)\n", "\n", "# Apply H-gates\n", "for qubit in range(n):\n", " dj_circuit.h(qubit)\n", " \n", "# Put qubit in state |->\n", "dj_circuit.x(n)\n", "dj_circuit.h(n)\n", " \n", "# Add oracle\n", "dj_circuit = dj_circuit.compose(balanced_oracle)\n", "\n", "# Repeat H-gates\n", "for qubit in range(n):\n", " dj_circuit.h(qubit)\n", "dj_circuit.barrier()\n", " \n", "# Measure\n", "for i in range(n):\n", " dj_circuit.measure(i, i)\n", " \n", "# Display circuit\n", "dj_circuit.draw()" ] }, { "cell_type": "markdown", "id": "3a41cf8c", "metadata": {}, "source": [ "A range of official [Jupyter notebooks](https://github.com/Qiskit/qiskit-tutorials) are also provided which give further examples of quantum comptutation in the language.'" ] }, { "cell_type": "code", "execution_count": 19, "id": "cf5914a2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 3.0;\n", "include \"stdgates.inc\";\n", "bit[3] c;\n", "qubit[4] q;\n", "h q[0];\n", "h q[1];\n", "h q[2];\n", "x q[3];\n", "h q[3];\n", "x q[0];\n", "x q[2];\n", "barrier q[0], q[1], q[2], q[3];\n", "cx q[0], q[3];\n", "cx q[1], q[3];\n", "cx q[2], q[3];\n", "barrier q[0], q[1], q[2], q[3];\n", "x q[0];\n", "x q[2];\n", "h q[0];\n", "h q[1];\n", "h q[2];\n", "barrier q[0], q[1], q[2], q[3];\n", "c[0] = measure q[0];\n", "c[1] = measure q[1];\n", "c[2] = measure q[2];\n", "\n" ] } ], "source": [ "print(qs.qasm3.dumps(dj_circuit))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.6" } }, "nbformat": 4, "nbformat_minor": 5 }