{ "cells": [ { "cell_type": "code", "execution_count": 12, "outputs": [ { "data": { "text/plain": "", "text/html": "

Version Information

Qiskit SoftwareVersion
qiskit-terra0.20.0
qiskit-aer0.10.3
qiskit-ignis0.7.0
qiskit-ibmq-provider0.18.3
qiskit0.34.2
qiskit-nature0.3.1
qiskit-optimization0.3.1
qiskit-machine-learning0.3.1
System information
Python version3.8.12
Python compilerGCC 7.5.0
Python builddefault, Oct 12 2021 13:49:34
OSLinux
CPUs12
Memory (Gb)125.78773498535156
Thu Apr 07 07:39:17 2022 +03
" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import qiskit.tools.jupyter\n", "%matplotlib inline\n", "%qiskit_version_table" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "# Hands on 4\n", "\n", "\n", "This hands on delves deeper into using the IBM Quantum cloud. You'll see the following topic simulator parameters, using GPUs in simulators, and noise simulation.\n", "\n", "## Using your IBM credentials locally\n", "\n", "The IBM Quantum account has functions for handling administrative tasks. The credentials can be saved to disk, or used in a session and never saved.\n", "\n", "* `enable_account(TOKEN, HUB, GROUP, PROJECT)`: Enable your account in the current session and optionally specify a default provider to return.\n", "* `save_account(TOKEN, HUB, GROUP, PROJECT)`: Save your account to disk for future use, and optionally specify a default provider to return when loading your account.\n", "* `load_account()`: Load account using stored credentials.\n", "* `disable_account()`: Disable your account in the current session.\n", "* `stored_account()`: List the account stored to disk.\n", "* `active_account()`: List the account currently in the session.\n", "* `delete_account()`: Delete the saved account from disk.\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "from qiskit import IBMQ,visualization\n", "# Enter your token from the IBM cloud here for temporary activation (I have mine saved, so not necessary)\n", "#if IBMQ.active_account() == None :\n", "# print(\"Enabling saved account\")\n", "# IBMQ.enable_account(\"your token here\")\n", "### If you have used save_account beforehand, you can load it here\n", "if (IBMQ.active_account() is None) and (IBMQ.stored_account() is not None) :\n", " print(\"Loading saved account\")\n", " provider = IBMQ.load_account()\n", "#\n", "#print(\"Currently active account:\", IBMQ.active_account())" ] }, { "cell_type": "markdown", "source": [ "As a part of the education problem, probably you have access to at least two providers." ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": "[,\n ,\n ]" }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "IBMQ.providers() # List all available providers" ] }, { "cell_type": "markdown", "source": [ "Let's select the `hub='ibm-q-education', group='mid-east-tech-un-1'` (since it has more resources, and less wait time). Each provider provides access to a number of _backends_ i.e. quantum computers." ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 20, "outputs": [], "source": [ "# If you have access to more than one hub:\n", "provider = IBMQ.get_provider(hub='ibm-q-education', group='mid-east-tech-un-1')\n" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "Currently, there is a SDK change in Qiskit preventing us selecting a backend automatically. Let's select one using the \"IBM Q Services\" page by selecting \"Your Systems\"" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 25, "outputs": [ { "data": { "text/plain": "VBox(children=(HTML(value=\"

" }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "backend=provider.get_backend('ibm_perth')\n", "backend" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "One important aspect of the devices is what is called the \"qubit topology\"\n", "\n", "The graph below shows you how the physical qubits are connected together on the real device you will be using. For example, on the graph, qubit 0 has a physical connection to qubit 1 and qubit 3 has connections to 1 and 5 on the quantum device but qubit 0 is not connected directly to 2\n", "\n", "This graph is really important when Qiskit tries to map a circuit to the quantum device because it shows how qubits are connected and this is a huge step in transforming circuits to run in devices. For example, since 0 and 1 are connected, it will have no problem executing a C-NOT between the two. But if you wish to do the same between 0 and 2, this will be harder because of the lack of connection.\n", "\n", "The process of rewriting circuits is what we call transpilation. Without it, we won't be able to run circuits on quantum devices. This will be explained later in the class" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": 26, "outputs": [ { "data": { "text/plain": "
", "image/png": "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\n" }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "visualization.plot_gate_map(backend)\n" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "Each quantum computer has different characteristics. Remember, it is not just the number of qubit, but also how the qubits are connected matters. You can check the topologies in the IBM Q webpage" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "## Qiskit Visualization tools\n", "\n", "Within the physics community, the qubits of a multi-qubit systems are typically ordered with the first qubit on the left-most side of the tensor product and the last qubit on the right-most side. For instance, if the first qubit is in state $ \\left|0\\right\\rangle $ and second is in state $ \\left|1\\right\\rangle $, their joint state would be $ \\left|01\\right\\rangle $. Qiskit uses a slightly different ordering of the qubits, in which the qubits are represented from the most significant bit (MSB) on the left to the least significant bit (LSB) on the right (big-endian). This is similar to bitstring representation on classical computers, and enables easy conversion from bitstrings to integers after measurements are performed. For the example just given, the joint state would be represented as $ \\left|10\\right\\rangle $.\n", "\n", "Importantly, this change in the representation of multi-qubit states affects the way multi-qubit gates are represented in Qiskit, as discussed [here](htps://qiskit.org/documentation/tutorials/circuits/3_summary_of_quantum_operations.html#Two-qubit-gates_)\n", "\n", "The representation used in Qiskit enumerates the basis vectors in increasing order of the integers they represent. For instance, the basis vectors for a 2-qubit system would be ordered as $ \\left|00\\right\\rangle$, $ \\left|01\\right\\rangle $, $ \\left|10\\right\\rangle $, and $ \\left|11\\right\\rangle $. Thinking of the basis vectors as bit strings, they encode the integers 0,1,2 and 3, respectively." ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have already seen how to plot the results in a histogram. There are other visualization tools that are useful when working with quantum computers, let's explore them.\n", "\n", "### Plot a state\n", "\n", "In many situations you want to see the state of a quantum computer. This could be for debugging. Here we assume you have this state (either from simulation or state tomography) and the goal is to visualize the quantum state. This requires exponential resources, so we advise to only view the state of small quantum systems. There are several functions for generating different types of visualization of a quantum state\n", "\n", "\n", "\n", "```\n", "plot_state_city(quantum_state)\n", "plot_state_qsphere(quantum_state)\n", "plot_state_paulivec(quantum_state)\n", "plot_state_hinton(quantum_state)\n", "plot_bloch_multivector(quantum_state)\n", "```\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A quantum state is either a state matrix $ \\rho $ (Hermitian matrix) or statevector $ \\left|\\psi\\right\\rangle $ (complex vector). The state matrix is related to the statevector by\n", "$$ \\rho = \\left| \\psi \\right\\rangle\\!\\left\\langle \\psi \\right| $$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The visualizations generated by the functions are:\n", "\n", "`'plot_state_city'`: The standard view for quantum states where the real and imaginary (imag) parts of the state matrix are plotted like a city.\n", "\n", "`'plot_state_qsphere'`: The Qiskit unique view of a quantum state where the amplitude and phase of the state vector are plotted in a spherical ball. \n", "The amplitude is the thickness of the arrow and the phase is the color. For mixed states it will show different 'qsphere' for each component.\n", "\n", "`'plot_state_paulivec'`: The representation of the state matrix using Pauli operators as the basis. \n", "\n", "`'plot_state_hinton'`: Same as 'city' but where the size of the element represents the value of the matrix element.\n", "\n", "`'plot_bloch_multivector'`: The projection of the quantum state onto the single qubit space and plotting on a bloch sphere." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.visualization import plot_state_city, plot_bloch_multivector\n", "from qiskit.visualization import plot_state_paulivec, plot_state_hinton\n", "from qiskit.visualization import plot_state_qsphere" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit = QuantumCircuit(2, 2)\n", "circuit.h(0)\n", "circuit.cx(0, 1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backend = Aer.get_backend('statevector_simulator') # the device to run on\n", "result = backend.run(circuit).result()\n", "psi = result.get_statevector(circuit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_state_city(psi)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_state_hinton(psi)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_state_qsphere(psi)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_state_paulivec(psi)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_bloch_multivector(psi)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Plot Bloch Vector\n", "\n", "A standard way of plotting a quantum system is using the Bloch vector. This only works for a single qubit and takes as input the Bloch vector." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.visualization import plot_bloch_vector" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_bloch_vector([0,1,0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Circuit Visualization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When building a quantum circuit, it often helps to draw the circuit. This is supported natively by a ``QuantumCircuit`` object. You can either call ``print()`` on the circuit, or call the ``draw()`` method on the object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Build a quantum circuit\n", "circuit = QuantumCircuit(3, 3)\n", "\n", "circuit.x(1)\n", "circuit.h(range(3))\n", "circuit.cx(0, 1)\n", "circuit.measure(range(3), range(3));" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(circuit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit.draw(output='mpl')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are different drawing formats. The parameter output (str) selects the output method to use for drawing the circuit. Valid choices are ``text, mpl, latex, latex_source``. See [qiskit.circuit.QuantumCircuit.draw](https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.draw.html?highlight=draw)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit.draw(initial_state=True, output='mpl')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Depending on the output, there are also options to customize the circuit diagram rendered by the circuit." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Disable Plot Barriers and Reversing Bit Order" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first two options are shared among all three backends. They allow you to configure both the bit orders and whether or not you draw barriers. These can be set by the ``reverse_bits`` `kwarg and ``plot_barriers`` kwarg, respectively. The examples below will work with any output backend; mpl is used here for brevity." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Draw a new circuit with barriers and more registers\n", "\n", "q_a = QuantumRegister(3, name='qa')\n", "q_b = QuantumRegister(5, name='qb')\n", "c_a = ClassicalRegister(3)\n", "c_b = ClassicalRegister(5)\n", "\n", "circuit = QuantumCircuit(q_a, q_b, c_a, c_b)\n", "\n", "circuit.x(q_a[1])\n", "circuit.x(q_b[1])\n", "circuit.x(q_b[2])\n", "circuit.x(q_b[4])\n", "circuit.barrier()\n", "circuit.h(q_a)\n", "circuit.barrier(q_a)\n", "circuit.h(q_b)\n", "circuit.cswap(q_b[0], q_b[1], q_b[2])\n", "circuit.cswap(q_b[2], q_b[3], q_b[4])\n", "circuit.cswap(q_b[3], q_b[4], q_b[0])\n", "circuit.barrier(q_b)\n", "circuit.measure(q_a, c_a)\n", "circuit.measure(q_b, c_b);" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Draw the circuit\n", "circuit.draw(output='mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Draw the circuit with reversed bit order\n", "circuit.draw(output='mpl', reverse_bits=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Draw the circuit without barriers\n", "circuit.draw(output='mpl', plot_barriers=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Backend specific costumizations" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Change the background color in mpl\n", "\n", "style = {'backgroundcolor': 'lightgreen'}\n", "\n", "circuit.draw(output='mpl', style=style)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Scale the mpl output to 1/2 the normal size\n", "circuit.draw(output='mpl', scale=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use ``circuit_drawer()`` as a function" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.tools.visualization import circuit_drawer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit_drawer(circuit, output='mpl', plot_barriers=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Simulators " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we will show how to import the Qiskit Aer simulator backend and use it to run ideal (noise free) Qiskit Terra circuits." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "# Import Qiskit\n", "from qiskit import QuantumCircuit\n", "from qiskit import Aer, transpile\n", "from qiskit.tools.visualization import plot_histogram, plot_state_city\n", "import qiskit.quantum_info as qi" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Aer provider contains a variety of high performance simulator backends for a variety of simulation methods. The available backends on the current system can be viewed using ``Aer.backends``" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Aer.backends()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The main simulator backend of the Aer provider is the ``AerSimulator`` backend. A new simulator backend can be created using ``Aer.get_backend('aer_simulator')``." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "simulator = Aer.get_backend('aer_simulator')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The default behavior of the ``AerSimulator`` backend is to mimic the execution of an actual device. If a ``QuantumCircuit`` containing measurements is run it will return a count dictionary containing the final values of any classical registers in the circuit. The circuit may contain gates, measurements, resets, conditionals, and other custom simulator instructions " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create circuit\n", "circ = QuantumCircuit(2)\n", "circ.h(0)\n", "circ.cx(0, 1)\n", "circ.measure_all()\n", "\n", "# Transpile for simulator\n", "simulator = Aer.get_backend('aer_simulator')\n", "circ = transpile(circ, simulator)\n", "\n", "# Run and get counts\n", "result = simulator.run(circ).result()\n", "counts = result.get_counts(circ)\n", "plot_histogram(counts, title='Bell-State counts')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Run and get memory (measurement outcomes for each individual shot)\n", "result = simulator.run(circ, shots=10, memory=True).result()\n", "memory = result.get_memory(circ)\n", "print(memory)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Simulation methods\n", "\n", "The AerSimulator supports a variety of simulation methods, each of which supports a different set of instructions. The method can be set manually using ``simulator.set_option(method=value)`` option, or a simulator backend with a preconfigured method can be obtained directly from the Aer provider using ``Aer.get_backend``.\n", "\n", "When simulating ideal circuits, changing the method between the exact simulation methods stabilizer, ``statevector``, ``density_matrix`` and ``matrix_product_state`` should not change the simulation result (other than usual variations from sampling probabilities for measurement outcomes)\n", "\n", "Each of these methods determines the internal representation of the quantum circuit and the algorithms used to process the quantum operations. They each have advantages and disadvantages, and choosing the best method is a matter of investigation. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Increase shots to reduce sampling variance\n", "shots = 10000\n", "\n", "# Stabilizer simulation method\n", "sim_stabilizer = Aer.get_backend('aer_simulator_stabilizer')\n", "job_stabilizer = sim_stabilizer.run(circ, shots=shots)\n", "counts_stabilizer = job_stabilizer.result().get_counts(0)\n", "\n", "# Statevector simulation method\n", "sim_statevector = Aer.get_backend('aer_simulator_statevector')\n", "job_statevector = sim_statevector.run(circ, shots=shots)\n", "counts_statevector = job_statevector.result().get_counts(0)\n", "\n", "# Density Matrix simulation method\n", "sim_density = Aer.get_backend('aer_simulator_density_matrix')\n", "job_density = sim_density.run(circ, shots=shots)\n", "counts_density = job_density.result().get_counts(0)\n", "\n", "# Matrix Product State simulation method\n", "sim_mps = Aer.get_backend('aer_simulator_matrix_product_state')\n", "job_mps = sim_mps.run(circ, shots=shots)\n", "counts_mps = job_mps.result().get_counts(0)\n", "\n", "plot_histogram([counts_stabilizer, counts_statevector, counts_density, counts_mps],\n", " title='Counts for different simulation methods',\n", " legend=['stabilizer', 'statevector',\n", " 'density_matrix', 'matrix_product_state'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The default simulation method is automatic which will automatically select a one of the other simulation methods for each circuit based on the instructions in those circuits. A fixed simulation method can be specified by by adding the method name when getting the backend, or by setting the method option on the backend." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### GPU simulation\n", "\n", "The statevector, density_matrix and unitary simulators support running on a NVidia GPUs. For these methods the simulation device can also be manually set to CPU or GPU using ``simulator.set_options(device='GPU')`` backend option. If a GPU device is not available setting this option will raise an exception." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.providers.aer import AerError\n", "\n", "# Initialize a GPU backend\n", "\n", "try:\n", " simulator_gpu = Aer.get_backend('aer_simulator')\n", " simulator_gpu.set_options(device='GPU')\n", "except AerError as e:\n", " print(e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Aer provider will also contain preconfigured GPU simulator backends if Qiskit Aer was installed with GPU support on a compatible system:\n", "\n", "* ``aer_simulator_statevector_gpu``\n", "* ``aer_simulator_density_matrix_gpu``\n", "* ``aer_simulator_unitary_gpu``\n", "\n", "Note: The GPU version of Aer can be installed using ``pip install qiskit-aer-gpu``." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Simulation precision\n", "\n", "One of the available simulator options allows setting the float precision for the statevector, density_matrix unitary and superop methods. This is done using the ``set_precision=\"single\"`` or ``precision=\"double\" `` (default) option:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Configure a single-precision statevector simulator backend\n", "simulator = Aer.get_backend('aer_simulator_statevector')\n", "simulator.set_options(precision='single')\n", "\n", "# Run and get counts\n", "result = simulator.run(circ).result()\n", "counts = result.get_counts(circ)\n", "print(counts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Setting the simulation precision applies to both CPU and GPU simulation devices. Single precision will halve the required memory and may provide performance improvements on certain systems." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Can we simulate noise?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Device backend noise model simulations\n", "\n", "We will now show how to use the Qiskit Aer noise module to automatically generate a basic noise model for an IBMQ hardware device, and use this model to do noisy simulations of QuantumCircuits to study the effects of errors which occur on real devices.\n", "\n", "Note, that these automatic models are only an approximation of the real errors that occur on actual devices, due to the fact that they must be build from a limited set of input parameters related to average error rates on gates. The study of quantum errors on real devices is an active area of research and we discuss the Qiskit Aer tools for configuring more detailed noise models in another notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit import IBMQ, transpile\n", "from qiskit import QuantumCircuit\n", "from qiskit.providers.aer import AerSimulator\n", "from qiskit.tools.visualization import plot_histogram" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Qiskit Aer device noise model automatically generates a simplified noise model for a real device. This model is generated using the calibration information reported in the ``BackendProperties`` of a device and takes into account\n", "\n", "* The gate_error probability of each basis gate on each qubit.\n", "* The gate_length of each basis gate on each qubit.\n", "* The T1, T2 relaxation time constants of each qubit.\n", "* The readout error probability of each qubit." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will use real noise data for an IBM Quantum device using the data stored in Qiskit Terra. Specifically, in this tutorial, the device is ``ibmq_vigo```." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.test.mock import FakeVigo\n", "device_backend = FakeVigo()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we construct a test circuit to compare the output of the real device with the noisy output simulated on the Qiskit Aer AerSimulator. Before running with noise or on the device we show the ideal expected output with no noise." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Construct quantum circuit\n", "circ = QuantumCircuit(3, 3)\n", "circ.h(0)\n", "circ.cx(0, 1)\n", "circ.cx(1, 2)\n", "circ.measure([0, 1, 2], [0, 1, 2])\n", "\n", "sim_ideal = AerSimulator()\n", "\n", "# Execute and get counts\n", "result = sim_ideal.run(transpile(circ, sim_ideal)).result()\n", "counts = result.get_counts(0)\n", "plot_histogram(counts, title='Ideal counts for 3-qubit GHZ state')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How to generate a simulator that mimics a device?\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We call ``from_backend`` to create a simulator for ``ibmq_vigo``" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim_vigo = AerSimulator.from_backend(device_backend)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By storing the device properties in ``vigo_simulator``, we ensure that the appropriate basis gates and coupling map are used when compiling circuits for simulation, thereby most closely mimicking the gates that will be executed on a real device. In addition ``vigo_simulator`` contains an approximate noise model consisting of:\n", "\n", "* Single-qubit gate errors consisting of a single qubit depolarizing error followed by a single qubit thermal relaxation error.\n", "* Two-qubit gate errors consisting of a two-qubit depolarizing error followed by single-qubit thermal relaxation errors on both qubits in the gate.\n", "* Single-qubit readout errors on the classical bit value obtained from measurements on individual qubits.\n", "\n", "For the gate errors the error parameter of the thermal relaxation errors is derived using the ``thermal_relaxation_error`` function from ``aer.noise.errors module``, along with the individual qubit T1 and T2 parameters, and the ``gate_time`` parameter from the device backend properties. The probability of the depolarizing error is then set so that the combined average gate infidelity from the depolarizing error followed by the thermal relaxation is equal to the ``gate_error`` value from the backend properties.\n", "\n", "For the readout errors the probability that the recorded classical bit value will be flipped from the true outcome after a measurement is given by the qubit ``readout_errors``." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once we have created a noisy simulator backend based on a real device we can use it to run noisy simulations.\n", "\n", "Important: When running noisy simulations it is critical to transpile the circuit for the backend so that the circuit is transpiled to the correct noisy basis gate set for the backend." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Transpile the circuit for the noisy basis gates\n", "tcirc = transpile(circ, sim_vigo)\n", "\n", "# Execute noisy simulation and get counts\n", "result_noise = sim_vigo.run(tcirc).result()\n", "counts_noise = result_noise.get_counts(0)\n", "plot_histogram(counts_noise,\n", " title=\"Counts for 3-qubit GHZ state with device noise model\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may also be interested in:\n", "* Building Noise Models https://qiskit.org/documentation/tutorials/simulators/3_building_noise_models.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Applying noise to custom unitary gates https://qiskit.org/documentation/tutorials/simulators/4_custom_gate_noise.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Introduction to Quantum Gates and Circuits. Building Circuits with multiple components." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Quantum Circuit Properties" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When constructing quantum circuits, there are several properties that help quantify the “size” of the circuits, and their ability to be run on a noisy quantum device. Some of these, like number of qubits, are straightforward to understand, while others like depth and number of tensor components require a bit more explanation. Here we will explain all of these properties, and, in preparation for understanding how circuits change when run on actual devices, highlight the conditions under which they change." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc = QuantumCircuit(12)\n", "for idx in range(5):\n", " qc.h(idx)\n", " qc.cx(idx, idx+5)\n", "\n", "qc.cx(1, 7)\n", "qc.x(8)\n", "qc.cx(1, 9)\n", "qc.x(7)\n", "qc.cx(1, 11)\n", "qc.swap(6, 11)\n", "qc.swap(6, 9)\n", "qc.swap(6, 10)\n", "qc.x(6)\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the plot, it is easy to see that this circuit has 12 qubits, and a collection of Hadamard, CNOT, X, and SWAP gates. But how to quantify this programmatically? Because we can do single-qubit gates on all the qubits simultaneously, the number of qubits in this circuit is equal to the width of the circuit:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc.width()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also just get the number of qubits directly:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc.num_qubits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**IMPORTANT**\n", "\n", "For a quantum circuit composed from just qubits, the circuit width is equal to the number of qubits. This is the definition used in quantum computing. However, for more complicated circuits with classical registers, and classically controlled gates, this equivalence breaks down. As such, from now on we will not refer to the number of qubits in a quantum circuit as the width\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also straightforward to get the number and type of the gates in a circuit using `QuantumCircuit.count_ops()`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc.count_ops()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also get just the raw count of operations by computing the circuits `QuantumCircuit.size()`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc.size()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A particularly important circuit property is known as the circuit depth. The depth of a quantum circuit is a measure of how many “layers” of quantum gates, executed in parallel, it takes to complete the computation defined by the circuit. Because quantum gates take time to implement, the depth of a circuit roughly corresponds to the amount of time it takes the quantum computer to execute the circuit. Thus, the depth of a circuit is one important quantity used to measure if a quantum circuit can be run on a device." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc.depth()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Final Statevector and Unitary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To save the final statevector of the simulation we can append the circuit with the ``save_statevector`` instruction. Note that this instruction should be applied before any measurements if we do not want to save the collapsed post-measurement state" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Saving the final statevector\n", "# Construct quantum circuit without measure\n", "\n", "from qiskit.visualization import array_to_latex\n", "\n", "circuit = QuantumCircuit(2)\n", "circuit.h(0)\n", "circuit.cx(0, 1)\n", "circuit.save_statevector()\n", "\n", "backend = Aer.get_backend('aer_simulator')\n", "result = backend.run(circuit).result()\n", "array_to_latex(result.get_statevector())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To save the unitary matrix for a ``QuantumCircuit`` we can append the circuit with the ``save_unitary`` instruction. Note that this circuit cannot contain any measurements or resets since these instructions are not supported on for the ``\"unitary\"`` simulation method" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Saving the circuit unitary\n", "# Construct quantum circuit without measure\n", "\n", "circuit = QuantumCircuit(2)\n", "circuit.h(0)\n", "circuit.cx(0, 1)\n", "circuit.save_unitary()\n", "\n", "result = backend.run(circuit).result()\n", "array_to_latex(result.get_unitary())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also apply save instructions at multiple locations in a circuit. Note that when doing this we must provide a unique label for each instruction to retrieve them from the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Saving multiple states\n", "# Construct quantum circuit without measure\n", "\n", "steps = 5\n", "circ = QuantumCircuit(1)\n", "for i in range(steps):\n", " circ.save_statevector(label=f'psi_{i}')\n", " circ.rx(i * np.pi / steps, 0)\n", "circ.save_statevector(label=f'psi_{steps}')\n", "\n", "# Transpile for simulator\n", "simulator = Aer.get_backend('aer_simulator')\n", "circ = transpile(circ, simulator)\n", "\n", "# Run and get saved data\n", "result = simulator.run(circ).result()\n", "data = result.data(0)\n", "data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Setting a custom statevector\n", "\n", "The ``set_statevector`` instruction can be used to set a custom Statevector state. The input statevector must be valid." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Generate a random statevector\n", "num_qubits = 2\n", "psi = qi.random_statevector(2 ** num_qubits, seed=100)\n", "\n", "# Set initial state to generated statevector\n", "circ = QuantumCircuit(num_qubits)\n", "circ.set_statevector(psi)\n", "circ.save_state()\n", "\n", "# Transpile for simulator\n", "simulator = Aer.get_backend('aer_simulator')\n", "circ = transpile(circ, simulator)\n", "\n", "# Run and get saved data\n", "result = simulator.run(circ).result()\n", "result.data(0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Initialization (Using the initialize instruction)\n", "\n", "qc = QuantumCircuit(4)\n", "\n", "qc.initialize('01+-')\n", "qc.draw()\n", "qc.decompose().draw(output=\"mpl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Initialization\n", "\n", "import math\n", "\n", "desired_vector = [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0]\n", "display(array_to_latex(desired_vector))\n", "\n", "\n", "qc = QuantumCircuit(2)\n", "qc.initialize(desired_vector, [0, 1])\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit import transpile\n", "simulator = Aer.get_backend('aer_simulator')\n", "qc_transpiled = transpile(qc, simulator, basis_gates=['x', 'cx', 'ry', 'rz'], optimization_level=3)\n", "#qc_transpiled = transpile(qc, simulator, optimization_level=3)\n", "#qc_transpiled.decompose().decompose().decompose().decompose().draw()\n", "qc_transpiled.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is another option to initalize the circuit to a desired quantum state. \n", "We use ` qiskit.circuit.QuantumCircuit.isometry` In general, it is used for attaching an arbitrary isometry from m to n qubits to a circuit. In particular, this allows to attach arbitrary unitaries on n qubits (m=n) or to prepare any state on n qubits (m=0). The decomposition used here was introduced by Iten et al. in https://arxiv.org/abs/1501.06911. This is important because in many experimental architectures, the C-NOT gate is relatively 'expensive' and hence we aim to keep the number of these as low as possible. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Isometry\n", "qc = QuantumCircuit(2)\n", "qc.isometry(desired_vector, q_input=[],q_ancillas_for_output=[0,1])\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "simulator = Aer.get_backend('aer_simulator')\n", "qc_transpiled = transpile(qc, simulator, optimization_level=3)\n", "qc_transpiled.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's compare both circuit outputs. Which one has more gates?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other components" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Composite gates\n", "\n", "# Build a sub-circuit\n", "sub_q = QuantumRegister(2)\n", "sub_circ = QuantumCircuit(sub_q, name='sub_circ')\n", "sub_circ.h(sub_q[0])\n", "sub_circ.crz(1, sub_q[0], sub_q[1])\n", "sub_circ.barrier()\n", "sub_circ.id(sub_q[1])\n", "sub_circ.u(1, 2, -2, sub_q[0])\n", "\n", "# Convert to a gate and stick it into an arbitrary place in the bigger circuit\n", "sub_inst = sub_circ.to_instruction()\n", "\n", "qr = QuantumRegister(3, 'q')\n", "circ = QuantumCircuit(qr)\n", "circ.h(qr[0])\n", "circ.cx(qr[0], qr[1])\n", "circ.cx(qr[1], qr[2])\n", "circ.append(sub_inst, [qr[1], qr[2]])\n", "\n", "circ.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Circuits are not immediately decomposed upon conversion to_instruction to allow circuit design at higher levels of abstraction. When desired, or before compilation, sub-circuits will be decomposed via the decompose method." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "decomposed_circ = circ.decompose() # Does not modify original circuit\n", "decomposed_circ.draw(output=\"mpl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Circuit with Global Phase\n", "\n", "circuit = QuantumCircuit(2)\n", "circuit.h(0)\n", "#circuit.save_unitary()\n", "circuit.cx(0, 1)\n", "circuit.global_phase = np.pi / 2\n", "circuit.save_unitary()\n", "\n", "display(circuit.draw(output=\"mpl\"))\n", "#backend = Aer.get_backend('unitary_simulator')\n", "backend = Aer.get_backend('aer_simulator_unitary')\n", "result = backend.run(circuit).result()\n", "array_to_latex(result.get_unitary())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Parameterized Quantum Circuits \n", "\n", "from qiskit.circuit import Parameter\n", "theta = Parameter('theta')\n", "\n", "circuit = QuantumCircuit(1)\n", "circuit.rx(theta, 0)\n", "circuit.measure_all()\n", "circuit.draw(output=\"mpl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim = Aer.get_backend('aer_simulator')\n", "res = sim.run(circuit, parameter_binds=[{theta: [np.pi/2, np.pi, 0]}]).result() # Different bindings\n", "res.get_counts()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.circuit import Parameter\n", "\n", "theta = Parameter('θ')\n", "\n", "n = 5\n", "\n", "qc = QuantumCircuit(5, 1)\n", "\n", "qc.h(0)\n", "for i in range(n-1):\n", " qc.cx(i, i+1)\n", "\n", "qc.barrier()\n", "qc.rz(theta, range(5))\n", "qc.barrier()\n", "\n", "for i in reversed(range(n-1)):\n", " qc.cx(i, i+1)\n", "qc.h(0)\n", "qc.measure(0, 0)\n", "\n", "qc.draw('mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#We can inspect the circuit’s parameters\n", "print(qc.parameters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All circuit parameters must be bound before sending the circuit to a backend. This can be done as follows: - The``bind_parameters`` method accepts a dictionary mapping ``Parameter``s to values, and returns a new circuit with each parameter replaced by its corresponding value. Partial binding is supported, in which case the returned circuit will be parameterized by any ``Parameter``s that were not mapped to a value." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "theta_range = np.linspace(0, 2 * np.pi, 128)\n", "\n", "circuits = [qc.bind_parameters({theta: theta_val})\n", " for theta_val in theta_range]\n", "\n", "circuits[-1].draw('mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backend = Aer.get_backend('aer_simulator')\n", "job = backend.run(transpile(circuits, backend))\n", "counts = job.result().get_counts()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the example circuit, we apply a global Rz(θ) rotation on a five-qubit entangled state, and so expect to see oscillation in qubit-0 at 5θ." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "fig = plt.figure(figsize=(8,6))\n", "ax = fig.add_subplot(111)\n", "\n", "ax.plot(theta_range, list(map(lambda c: c.get('0', 0), counts)), '.-', label='0')\n", "ax.plot(theta_range, list(map(lambda c: c.get('1', 0), counts)), '.-', label='1')\n", "\n", "ax.set_xticks([i * np.pi / 2 for i in range(5)])\n", "ax.set_xticklabels(['0', r'$\\frac{\\pi}{2}$', r'$\\pi$', r'$\\frac{3\\pi}{2}$', r'$2\\pi$'], fontsize=14)\n", "ax.set_xlabel('θ', fontsize=14)\n", "ax.set_ylabel('Counts', fontsize=14)\n", "ax.legend(fontsize=14)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Random Circuit\n", "\n", "from qiskit.circuit.random import random_circuit\n", "\n", "circ = random_circuit(2, 2, measure=True)\n", "circ.draw(output='mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Pauli\n", "from qiskit.quantum_info.operators import Pauli\n", "\n", "circuit = QuantumCircuit(4)\n", "IXYZ = Pauli('IXYZ')\n", "circuit.append(IXYZ, [0, 1, 2, 3])\n", "circuit.draw('mpl')\n", "# circuit.decompose().draw()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Pauli with phase \n", "from qiskit.quantum_info.operators import Pauli\n", "\n", "circuit = QuantumCircuit(4)\n", "iIXYZ = Pauli('iIXYZ') # ['', '-i', '-', 'i']\n", "circuit.append(iIXYZ, [0, 1, 2, 3])\n", "circuit.draw('mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Any unitary!\n", "matrix = [[0, 0, 0, 1],\n", " [0, 0, 1, 0],\n", " [1, 0, 0, 0],\n", " [0, 1, 0, 0]]\n", " \n", "circuit = QuantumCircuit(2)\n", "circuit.unitary(matrix, [0, 1])\n", "circuit.draw('mpl')\n", "# circuit.decompose().draw() #synthesis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Classical logic\n", "from qiskit.circuit import classical_function, Int1\n", "\n", "@classical_function\n", "def oracle(x: Int1, y: Int1, z: Int1) -> Int1:\n", " return not x and (y or z)\n", "\n", "circuit = QuantumCircuit(4)\n", "circuit.append(oracle, [0, 1, 2, 3])\n", "circuit.draw('mpl')\n", "# circuit.decompose().draw() #synthesis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Classical logic\n", "from qiskit.circuit import classical_function, Int1\n", "@classical_function\n", "def oracle(x: Int1) -> Int1:\n", " return not x\n", "circuit = QuantumCircuit(2)\n", "circuit.append(oracle, [0, 1])\n", "circuit.draw('mpl')\n", "# circuit.decompose().draw() #synthesis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### How to create an operator?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://qiskit.org/documentation/tutorials/circuits_advanced/02_operators_overview.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Run on real hardware. Compiling Circuits\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The backends we have access to" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# If you have access to more than one hub:\n", "provider = IBMQ.get_provider(hub='ibm-q-education', group='mid-east-tech-un-1')\n", "[(b.name(), b.configuration().n_qubits) for b in provider.backends()]" ] }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "from qiskit.tools.jupyter import *\n", "backend = provider.get_backend('ibm_perth')\n", "backend" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Not working at the moment\n", "#from qiskit.providers.ibmq import least_busy\n", "#\n", "#backend = least_busy(provider.backends(\n", "# simulator=False,\n", "# filters=lambda b: b.configuration().n_qubits >= 2))\n", "#backend" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's go back to Bell state" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit = QuantumCircuit(2)\n", "circuit.h(0)\n", "circuit.cx(0, 1)\n", "circuit.measure_all()\n", "circuit.draw('mpl')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember how to run it in a simulator?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim = Aer.get_backend('aer_simulator')\n", "result = sim.run(circuit).result()\n", "counts = result.get_counts()\n", "plot_histogram(counts)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%qiskit_job_watcher\n", "job = backend.run(circuit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "job.result()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit.draw('mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit import transpile\n", "\n", "transpiled_circuit = transpile(circuit, backend)\n", "transpiled_circuit.draw('mpl')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "job = backend.run(transpiled_circuit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "job.status()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = job.result()\n", "counts = result.get_counts()\n", "plot_histogram(counts)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.visualization import plot_circuit_layout, plot_gate_map\n", "\n", "display(transpiled_circuit.draw(idle_wires=False))\n", "display(plot_gate_map(backend))\n", "plot_circuit_layout(transpiled_circuit, backend)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# a slightly more interesting example:\n", "circuit = QuantumCircuit(3)\n", "circuit.h([0,1,2])\n", "circuit.ccx(0, 1, 2)\n", "circuit.h([0,1,2])\n", "circuit.ccx(2, 0, 1)\n", "circuit.h([0,1,2])\n", "circuit.measure_all()\n", "circuit.draw()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit, backend)\n", "transpiled.draw(idle_wires=False, fold=-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Initial layout" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit, backend, initial_layout=[0, 2, 3])\n", "display(plot_circuit_layout(transpiled, backend))\n", "plot_gate_map(backend)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled.draw(idle_wires=False, fold=-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Optimization level\n", "\n", "Higher levels generate more optimized circuits, at the expense of longer transpilation time.\n", "\n", " * 0: no explicit optimization other than mapping to backend\n", " * 1: light optimization by simple adjacent gate collapsing.(default)\n", " * 2: medium optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules.\n", " * 3: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "level0 = transpile(circuit, backend, optimization_level=0)\n", "level1 = transpile(circuit, backend, optimization_level=1)\n", "level2 = transpile(circuit, backend, optimization_level=2)\n", "level3 = transpile(circuit, backend, optimization_level=3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for level in [level0, level1, level2, level3]:\n", " print(level.count_ops()['cx'], level.depth())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Transpiling is a stochastic process" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit, backend, optimization_level=2, seed_transpiler=0)\n", "transpiled.depth()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit, backend, optimization_level=2, seed_transpiler=1)\n", "transpiled.depth()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Playing with other transpiler options (without a backend)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit)\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set a basis gates" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backend.configuration().basis_gates" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit, basis_gates=['x', 'cx', 'h', 'p'])\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set a coupling map" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backend.configuration().coupling_map" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit.transpiler import CouplingMap\n", "\n", "transpiled = transpile(circuit, coupling_map=CouplingMap([(0,1),(1,2)]))\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set an initial layout in a coupling map" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit,\n", " coupling_map=CouplingMap([(0,1),(1,2)]),\n", " initial_layout=[1, 0, 2])\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set an initial_layout in the coupling map with basis gates" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit,\n", " coupling_map=CouplingMap([(0,1),(1,2)]),\n", " initial_layout=[1, 0, 2],\n", " basis_gates=['x', 'cx', 'h', 'p']\n", " )\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled.count_ops()['cx']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plus optimization level" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit,\n", " coupling_map=CouplingMap([(0,1),(1,2)]),\n", " initial_layout=[1, 0, 2],\n", " basis_gates=['x', 'cx', 'h', 'p'],\n", " optimization_level=3\n", " )\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled.count_ops()['cx']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Last parameter, approximation degree" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit,\n", " coupling_map=CouplingMap([(0,1),(1,2)]),\n", " initial_layout=[1, 0, 2],\n", " basis_gates=['x', 'cx', 'h', 'p'],\n", " approximation_degree=0.99,\n", " optimization_level=3\n", " )\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled.depth()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled = transpile(circuit,\n", " coupling_map=CouplingMap([(0,1),(1,2)]),\n", " initial_layout=[1, 0, 2],\n", " basis_gates=['x', 'cx', 'h', 'p'],\n", " approximation_degree=0.01,\n", " optimization_level=3\n", " )\n", "transpiled.draw(fold=-1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transpiled.depth()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Qiskit is hardware agnostic! For example, you can try out trapped ion machines in IONQ https://ionq.com/get-started/#cloud-access" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install qiskit-ionq" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from qiskit_ionq import IonQProvider\n", "provider = IonQProvider()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[(b.name(), b.configuration().n_qubits) for b in provider.backends()]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "circuit = QuantumCircuit(2)\n", "circuit.h(0)\n", "circuit.cx(0, 1)\n", "circuit.measure_all()\n", "circuit.draw()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backend = provider.get_backend(\"ionq_qpu\")\n", "job = backend.run(circuit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_histogram()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "job.get_counts()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "plot_histogram(job.get_counts())" ] }, { "cell_type": "markdown", "source": [ "## Send it after class\n", "\n", "* Compare real noise and simulated noise in a circuit.\n", "* Does different topologies and transpilation strategies effect the noise? Try!" ], "metadata": { "collapsed": false } } ], "metadata": { "colab": { "collapsed_sections": [ "7d_uf8JI12oJ", "Li9fj6uVzyFR", "9PBnSGwum9ZJ", "s0UwnaaJQkqa", "YqAqumfLri_P", "yWcE6S_Qp61P", "yhGcFXBa4aEM", "ZocPglAL6zRm", "58JawO_lFBGY", "iTzbCQzm67Uw", "EQ8dJq6v7NW-" ], "name": "Workshop.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Qiskit v0.34.2 (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.8.12" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "2027f275556b4ba8ab85f9c2503b1f90": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "grid_area": "right", "padding": "0px 0px 0px 0px", "width": "70px" } }, "20a1f528d14b4d47b1c0a40b28977194": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "23aba3f787d7476998e3551611f259cf": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ButtonModel", "state": { "button_style": "primary", "description": "Clear", "layout": "IPY_MODEL_2027f275556b4ba8ab85f9c2503b1f90", "style": "IPY_MODEL_aab781f877ae48129f107dcd95c77a6f" } }, "264d98ca536d4ae782eddf13663af346": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "margin": "0px 0px 0px 37px", "width": "600px" } }, "3705fd624c5545de94dd1cee6b98f4cd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "3f307fa0754f4b3c8349f8ef25024720": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "6d5ac62a9b2a4af587c9f6ab92b76f24": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_badfbfc544ea43c5973c673af2bf7d98", "IPY_MODEL_c1f9cecfbbae4ade82507cabad02f6cc", "IPY_MODEL_9430fec727134976b9a468ecfc5706fe", "IPY_MODEL_d4b4b21c84af4676af56cc8e9d4904a6", "IPY_MODEL_9bf8d71827a94a45bd3c086b0a6090da" ], "layout": "IPY_MODEL_264d98ca536d4ae782eddf13663af346" } }, "7bd3879acff446f289e548d2b45d9bb7": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "grid_template_areas": "\n \". . . . right \"\n ", "grid_template_columns": "20% 20% 20% 20% 20%", "width": "100%" } }, "8380f730b2d34c82b1000667160cd9a3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8515b7ac60ac4da4bac4e92f7773cdf1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "8589815dc2f04a58a5b8fe279e01889b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_e5fef3473d6745089818a053d0f197f2", "style": "IPY_MODEL_3705fd624c5545de94dd1cee6b98f4cd", "value": "

Circuit Properties

" } }, "9430fec727134976b9a468ecfc5706fe": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_b909b9a717fb456ea585b1f321756ece", "style": "IPY_MODEL_d3a7895f08504cf6a136e9a6b6cb9769", "value": "
Status
" } }, "960c0d96d54b4edca1376633870ee43a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "width": "70px" } }, "9ad5417c4de14746bd702cdaa9211f47": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "width": "145px" } }, "9bf8d71827a94a45bd3c086b0a6090da": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_8380f730b2d34c82b1000667160cd9a3", "style": "IPY_MODEL_3f307fa0754f4b3c8349f8ef25024720", "value": "
Message
" } }, "9e0445b4b35c4a2184b4129a1696b572": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "GridBoxModel", "state": { "children": [ "IPY_MODEL_23aba3f787d7476998e3551611f259cf" ], "layout": "IPY_MODEL_7bd3879acff446f289e548d2b45d9bb7" } }, "aab781f877ae48129f107dcd95c77a6f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ButtonStyleModel", "state": {} }, "b909b9a717fb456ea585b1f321756ece": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "width": "95px" } }, "badfbfc544ea43c5973c673af2bf7d98": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_e5fbacb579af4cfbb18aee424118b3ec", "style": "IPY_MODEL_bcc812834b504b2380ece6eebaeb1e77", "value": "
Job ID
" } }, "bcc812834b504b2380ece6eebaeb1e77": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "c1f9cecfbbae4ade82507cabad02f6cc": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_9ad5417c4de14746bd702cdaa9211f47", "style": "IPY_MODEL_8515b7ac60ac4da4bac4e92f7773cdf1", "value": "
Backend
" } }, "d3a7895f08504cf6a136e9a6b6cb9769": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "d4b4b21c84af4676af56cc8e9d4904a6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_960c0d96d54b4edca1376633870ee43a", "style": "IPY_MODEL_20a1f528d14b4d47b1c0a40b28977194", "value": "
Queue
" } }, "e5fbacb579af4cfbb18aee424118b3ec": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "width": "190px" } }, "e5fef3473d6745089818a053d0f197f2": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "margin": "0px 0px 10px 0px" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }