Here, we provide an overview of working with Qiskit. The fundamental package of Qiskit is Terra that provides the basic building blocks necessary to program quantum computers. The fundamental unit of Qiskit is the quantum circuit. A basic workflow using Qiskit consists of two stages: Build and Execute. Build allows you to make different quantum circuits that represent the problem you are solving, and Execute that allows you to run them on different backends. After the jobs have been run, the data is collected and postprocessed depending on the desired output.
import numpy as np
from qiskit import *
%matplotlib inline
# Create a Quantum Circuit acting on a quantum register of three qubits
circ = QuantumCircuit(3)
After you create the circuit with its registers, you can add gates ("operations") to manipulate the registers. As you proceed through the tutorials you will find more gates and circuits; below is an example of a quantum circuit that makes a three-qubit GHZ state
$$|\psi\rangle = \left(|000\rangle+|111\rangle\right)/\sqrt{2}.$$To create such a state, we start with a three-qubit quantum register. By default, each qubit in the register is initialized to $|0\rangle$. To make the GHZ state, we apply the following gates:
On an ideal quantum computer, the state produced by running this circuit would be the GHZ state above.
In Qiskit, operations can be added to the circuit one by one, as shown below.
# Add a H gate on qubit 0, putting this qubit in superposition.
circ.h(0)
# Add a CX (CNOT) gate on control qubit 0 and target qubit 1, putting
# the qubits in a Bell state.
circ.cx(0, 1)
# Add a CX (CNOT) gate on control qubit 0 and target qubit 2, putting
# the qubits in a GHZ state.
circ.cx(0, 2)
<qiskit.circuit.instructionset.InstructionSet at 0x1a25ab1ac8>
You can visualize your circuit using Qiskit QuantumCircuit.draw()
, which plots the circuit in the form found in many textbooks.
circ.draw()
In this circuit, the qubits are put in order, with qubit zero at the top and qubit two at the bottom. The circuit is read left to right (meaning that gates that are applied earlier in the circuit show up further to the left).
[default]
circuit_drawer = mpl
For those that want the full LaTeX experience, you can also set the circuit_drawer = latex.
Qiskit Aer is our package for simulating quantum circuits. It provides many different backends for doing a simulation. There is also a basic, Python only, implementation called BasicAer
in Terra that can be used as a drop-in replacement for Aer
in the examples below.
The most common backend in Qiskit Aer is the statevector_simulator
. This simulator returns the quantum
state, which is a complex vector of dimensions $2^n$, where $n$ is the number of qubits
(so be careful using this as it will quickly get too large to run on your machine).
When representing the state of a multi-qubit system, the tensor order used in Qiskit is different than that used in most physics textbooks. Suppose there are $n$ qubits, and qubit $j$ is labeled as $Q_{j}$. Qiskit uses an ordering in which the $n^{\mathrm{th}}$ qubit is on the left side of the tensor product, so that the basis vectors are labeled as $Q_n\otimes \cdots \otimes Q_1\otimes Q_0$.
For example, if qubit zero is in state 0, qubit 1 is in state 0, and qubit 2 is in state 1, Qiskit would represent this state as $|100\rangle$, whereas many physics textbooks would represent it as $|001\rangle$.
This difference in labeling affects the way multi-qubit operations are represented as matrices. For example, Qiskit represents a controlled-X ($C_{X}$) operation with qubit 0 being the control and qubit 1 being the target as
$$C_X = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\\end{pmatrix}.$$To run the above circuit using the statevector simulator, first you need to import Aer and then set the backend to statevector_simulator
.
# Import Aer
from qiskit import Aer
# Run the quantum circuit on a statevector simulator backend
backend = Aer.get_backend('statevector_simulator')
Now that we have chosen the backend, it's time to compile and run the quantum circuit. In Qiskit we provide the execute
function for this. execute
returns a job
object that encapsulates information about the job submitted to the backend.
# Create a Quantum Program for execution
job = execute(circ, backend)
When you run a program, a job object is made that has the following two useful methods:
job.status()
and job.result()
, which return the status of the job and a result object, respectively.
result = job.result()
The results object contains the data and Qiskit provides the method
result.get_statevector(circ)
to return the state vector for the quantum circuit.
outputstate = result.get_statevector(circ, decimals=3)
print(outputstate)
[0.707+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.707+0.j]
Qiskit also provides a visualization toolbox to allow you to view these results.
Below, we use the visualization function to plot the real and imaginary components of the state density matrix \rho.
from qiskit.visualization import plot_state_city
plot_state_city(outputstate)
Qiskit Aer also includes a unitary_simulator
that works provided all the elements in the circuit are unitary operations. This backend calculates the $2^n \times 2^n$ matrix representing the gates in the quantum circuit.
# Run the quantum circuit on a unitary simulator backend
backend = Aer.get_backend('unitary_simulator')
job = execute(circ, backend)
result = job.result()
# Show the results
print(result.get_unitary(circ, decimals=3))
[[ 0.707+0.j 0.707+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j] [ 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.707+0.j -0.707+0.j] [ 0. +0.j 0. +0.j 0.707+0.j 0.707+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j] [ 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.707+0.j -0.707+0.j 0. +0.j 0. +0.j] [ 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.707+0.j 0.707+0.j 0. +0.j 0. +0.j] [ 0. +0.j 0. +0.j 0.707+0.j -0.707+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j] [ 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.707+0.j 0.707+0.j] [ 0.707+0.j -0.707+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j]]
The simulators above are useful because they provide information about the state output by the ideal circuit and the matrix representation of the circuit. However, a real experiment terminates by measuring each qubit (usually in the computational $|0\rangle, |1\rangle$ basis). Without measurement, we cannot gain information about the state. Measurements cause the quantum system to collapse into classical bits.
For example, suppose we make independent measurements on each qubit of the three-qubit GHZ state $$|\psi\rangle = |000\rangle +|111\rangle)/\sqrt{2},$$ and let $xyz$ denote the bitstring that results. Recall that, under the qubit labeling used by Qiskit, $x$ would correspond to the outcome on qubit 2, $y$ to the outcome on qubit 1, and $z$ to the outcome on qubit 0.
Recall the probability of obtaining outcome $xyz$ is given by $$\mathrm{Pr}(xyz) = |\langle xyz | \psi \rangle |^{2}$$ and as such for the GHZ state probability of obtaining 000 or 111 are both 1/2.
To simulate a circuit that includes measurement, we need to add measurements to the original circuit above, and use a different Aer backend.
# Create a Quantum Circuit
meas = QuantumCircuit(3, 3)
meas.barrier(range(3))
# map the quantum measurement to the classical bits
meas.measure(range(3),range(3))
# The Qiskit circuit object supports composition using
# the addition operator.
qc = circ+meas
#drawing the circuit
qc.draw()
This circuit adds a classical register, and three measurements that are used to map the outcome of qubits to the classical bits.
To simulate this circuit, we use the qasm_simulator
in Qiskit Aer. Each run of this circuit will yield either the bitstring 000 or 111. To build up statistics about the distribution of the bitstrings (to, e.g., estimate $\mathrm{Pr}(000)$), we need to repeat the circuit many times. The number of times the circuit is repeated can be specified in the execute
function, via the shots
keyword.
# Use Aer's qasm_simulator
backend_sim = Aer.get_backend('qasm_simulator')
# Execute the circuit on the qasm simulator.
# We've set the number of repeats of the circuit
# to be 1024, which is the default.
job_sim = execute(qc, backend_sim, shots=1024)
# Grab the results from the job.
result_sim = job_sim.result()
Once you have a result object, you can access the counts via the function get_counts(circuit)
. This gives you the aggregated binary outcomes of the circuit you submitted.
counts = result_sim.get_counts(qc)
print(counts)
{'000': 469, '111': 555}
Approximately 50 percent of the time, the output bitstring is 000. Qiskit also provides a function plot_histogram
, which allows you to view the outcomes.
from qiskit.visualization import plot_histogram
plot_histogram(counts)
The estimated outcome probabilities $\mathrm{Pr}(000)$ and $\mathrm{Pr}(111)$ are computed by taking the aggregate counts and dividing by the number of shots (times the circuit was repeated). Try changing the shots
keyword in the execute
function and see how the estimated probabilities change.
To faciliate access to real quantum computing hardware, we have provided a simple API interface. To access IBM Q devices, you'll need an API token. You can generate, or view, your API token here (create an account if you don't already have one).
Your IBM Q account lets you run your circuit on real devices or on our cloud simulator. Basic account usage can be seen in the examples below. For more detailed instructions on using the IBM Q account, see Part 3: The IBMQ Account.
from qiskit import IBMQ
After generating your API token, call: IBMQ.save_account('MY_TOKEN')
.
This will store your IBM Q credentials in a local file. Unless your registration information has changed, you only need to do this once. You may now load your accounts by calling,
IBMQ.load_account()
<AccountProvider for IBMQ(hub='ibm-q', group='open', project='main')>
Once your account has been loaded, you have one or more providers available to you
IBMQ.providers()
[<AccountProvider for IBMQ(hub='ibm-q', group='open', project='main')>, <AccountProvider for IBMQ(hub='ibm-q-perf', group='performance', project='default-params')>, <AccountProvider for IBMQ(hub='ibm-q-perf', group='performance', project='condor')>, <AccountProvider for IBMQ(hub='ibm-q-perf', group='qcaas', project='default')>, <AccountProvider for IBMQ(hub='perf-on-client-d', group='normal', project='default')>]
Each provider gives access to a selection of services (e.g. Backends) that is authorized by your account. To see the backends available to a given provider, first select the provider by hub, group, project, or a combination thereof:
provider = IBMQ.get_provider(group='open')
the ask the provider to list its backends:
provider.backends()
[<IBMQSimulator('ibmq_qasm_simulator') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmqx4') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmqx2') from IBMQ(hub='ibm-q', group='open', project='main')>, <IBMQBackend('ibmq_16_melbourne') from IBMQ(hub='ibm-q', group='open', project='main')>]
Today's quantum information processors are small and noisy, but are advancing at a fast pace. They provide a great opportunity to explore what noisy, intermediate-scale quantum (NISQ) computers can do.
Let us now grab a backend from the provider on which to run our quantum circuit:
backend = provider.get_backend('ibmqx2')
To run the circuit on the given device we use execute
. Sometimes the devices are busy with jobs from other users. Your job is added to the list of pending jobs called the queue, and executed in this queue order. To monitor the status of our job through the process, we can use the job_monitor
execute
functions does much more than just send your circuit(s) to a backend. This functionality can be explored in Part 5: Using the transpiler.from qiskit.tools.monitor import job_monitor
job_exp = execute(qc, backend=backend)
job_monitor(job_exp)
Job Status: job has successfully run
job_exp
has a .result()
method that lets us get the results from running our circuit.
result_exp = job_exp.result()
Like before, the counts from the execution can be obtained using get_counts(qc)
counts_exp = result_exp.get_counts(qc)
plot_histogram([counts_exp,counts], legend=['Device', 'Simulator'])
The IBM Q provider also comes with a remote optimized simulator called ibmq_qasm_simulator
. This remote simulator is capable of simulating up to 32 qubits. It can be used the
same way as the remote real backends.
simulator_backend = provider.get_backend('ibmq_qasm_simulator')
job_cloud = execute(qc, backend=simulator_backend)
result_cloud = job_cloud.result()
counts_cloud = result_cloud.get_counts(qc)
plot_histogram(counts_cloud)
If your experiment takes longer to run then you have time to wait around, or if you simply want to retrieve old jobs, the IBM Q backends allow you to do that. First you would need to note your job's ID:
job_id = job_exp.job_id()
print('JOB ID: {}'.format(job_id))
JOB ID: 5d4eac8e2c3a2d001131ddd1
Given a job ID, that job object can be later reconstructed from the backend using retrieve_job:
retrieved_job = backend.retrieve_job(job_id)
and then the results can be obtained from the new job object.
retrieved_job.result().get_counts(qc)
{'111': 415, '101': 32, '001': 17, '011': 25, '000': 483, '110': 21, '100': 12, '010': 19}
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.9.0 |
Aer | 0.3.0 |
Ignis | 0.2.0 |
Aqua | 0.5.6 |
IBM Q Provider | 0.3.1 |
System information | |
Python | 3.7.3 (default, Mar 27 2019, 16:54:48) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 4 |
Memory (Gb) | 16.0 |
Sat Aug 10 07:38:18 2019 EDT |
© Copyright IBM 2017, 2019.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.