Note
This page was generated from docs/tutorials/1_the_ibm_quantum_account.ipynb.
The IBM Quantum Account#
In Qiskit we have an interface for backends and jobs that is useful for running circuits and extending to third-party backends. In this tutorial, we will review the core components of Qiskit’s base backend framework, using the IBM Quantum account as an example.
The interface has three main components: the account, backends, and jobs:
account: Provides access to quantum devices and simulators, collectively called ‘backends’, and additional services tailored to a specific backend instance.
backend: A quantum device or simulator capable of running quantum circuits or pulse schedules.
job: A local reference to a collection of quantum circuits or pulse schedules submitted to a given backend.
Note: To run the sample code in this tutorial, you’ll need to have an IBM Quantum account and have qiskit-ibm-provider installed. qiskit-ibm-provider is the interface for accessing IBM Quantum services.
The Account#
The IBMProvider
class enables you to access your IBM Quantum account, all of the backends (quantum systems or simulators), and additional services that are available to you.
This class has class methods to save your account to disk, view saved account on disk, delete saved account from disk and also view the account currently active in the session. Using the constructor you can load an account from disk or enable account for current session (without saving to disk). IBMProvider
class can be initialized using the API token
, which is used to authenticate to your IBM Quantum account.
Class Methods: - IBMProvider.save_account(TOKEN, INSTANCE)
: Save your account to disk for future use and optionally set a default hub/group/project to be used when loading your account. - IBMProvider.saved_accounts()
: View the accounts saved to disk. - IBMProvider.delete_account()
: Delete the saved account from disk. - IBMProvider.active_account()
: List the account currently active in the session.
Constructor Usage: - IBMProvider()
: Load account and default hub/group/project using saved credentials. - IBMProvider(TOKEN)
: Enable your account in the current session.
You can save an account to disk as shown below:
[1]:
from qiskit_ibm_provider import IBMProvider
# IBMProvider.save_account(TOKEN)
TOKEN
here is the API token you obtain from your IBM Quantum account.
Then you can instantiate the IBMProvider
class like below:
[2]:
provider = IBMProvider()
provider
[2]:
<IBMProvider>
The IBMProvider
class inherits from qiskit.providers.BaseProvider
and implements the following instance methods:
backends()
: Returns all backends available to the account.get_backend(NAME)
: Returns the named backend.
Using the provider instance from above:
[3]:
provider = IBMProvider()
provider.backends()
[3]:
[<IBMBackend('ibmq_qasm_simulator')>,
<IBMBackend('ibmq_armonk')>,
<IBMBackend('ibmq_santiago')>,
<IBMBackend('ibmq_bogota')>,
<IBMBackend('ibmq_lima')>,
<IBMBackend('ibmq_belem')>,
<IBMBackend('ibmq_quito')>,
<IBMBackend('simulator_statevector')>,
<IBMBackend('simulator_mps')>,
<IBMBackend('simulator_extended_stabilizer')>,
<IBMBackend('simulator_stabilizer')>,
<IBMBackend('ibmq_manila')>]
Selecting a backend is done by name using the get_backend(NAME)
method:
[4]:
backend = provider.get_backend("ibmq_qasm_simulator")
backend
[4]:
<IBMBackend('ibmq_qasm_simulator')>
Filtering the Backends#
You may also optionally filter the set of returned backends, by passing arguments that query the backend’s configuration
, status
, or properties
. The filters are passed by conditions and, for more general filters, you can make advanced functions using a lambda function.
As a first example lets return only those backends that are real quantum devices, and that are currently operational:
[5]:
provider.backends(simulator=False, operational=True)
[5]:
[<IBMBackend('ibmq_armonk')>,
<IBMBackend('ibmq_santiago')>,
<IBMBackend('ibmq_bogota')>,
<IBMBackend('ibmq_lima')>,
<IBMBackend('ibmq_belem')>,
<IBMBackend('ibmq_quito')>,
<IBMBackend('ibmq_manila')>]
You can also filter by the minimum number of qubits the backends must have:
[6]:
provider.backends(min_num_qubits=5, simulator=False, operational=True)
[6]:
[<IBMBackend('ibmq_santiago')>,
<IBMBackend('ibmq_bogota')>,
<IBMBackend('ibmq_lima')>,
<IBMBackend('ibmq_belem')>,
<IBMBackend('ibmq_quito')>,
<IBMBackend('ibmq_manila')>]
Lastly, show the least busy 5 qubit device (in terms of the number of jobs pending in the queue)
[7]:
from qiskit_ibm_provider import least_busy
small_devices = provider.backends(min_num_qubits=5, simulator=False, operational=True)
backend = least_busy(small_devices)
print(backend)
ibmq_belem
The above filters can be combined as desired.
Backends#
Backends represent either a simulator or a real quantum computer, and are responsible for running quantum circuits and/or pulse schedules and returning results. They have a run
method which takes in one or more circuits as input, the Qiskit API serialization format, and returns a BaseJob
object. This object allows asynchronous running of jobs for retrieving results from a backend when the job is completed.
At a minimum, backends use the following methods, inherited from qiskit.providers.backend.BackendV1
:
provider
: Returns the instance ofIBMProvider
class used to access the backend.name
: Returns the name of the backend.status()
: Returns the current status of the backend.configuration()
: Returns the backend configuration.properties()
: Returns the backend properties.run(circuit, **kwargs)
: Runs a circuit on the backend.
IBMBackend also accepts additional parameters to the run()
method:
job_tags
: Tags to be assigned to the job.
And supports additional methods:
defaults()
: Gives a data structure of typical default parameters, if applicable.
Refer to the IBMBackend documentation for a complete list of methods.
Lets load up the least busy backend from the small_devices
filtered above:
[8]:
backend = least_busy(small_devices)
Some examples using the different methods:
[9]:
backend.provider
[9]:
<IBMProvider>
[10]:
backend.name
[10]:
'ibmq_belem'
[11]:
backend.status().to_dict()
[11]:
{'backend_name': 'ibmq_belem',
'backend_version': '1.0.14',
'operational': True,
'pending_jobs': 3,
'status_msg': 'active'}
Here we see the name of the backend, the software version it is running, along with its operational status, number of jobs pending in the backends queue, and a more detailed status message.
Next we look at the backend configuration and properties:
[12]:
backend.configuration().to_dict()
[12]:
{'backend_name': 'ibmq_belem',
'backend_version': '1.0.14',
'n_qubits': 5,
'basis_gates': ['id', 'rz', 'sx', 'x', 'cx', 'reset'],
'gates': [{'name': 'id',
'parameters': [],
'qasm_def': 'gate id q { U(0, 0, 0) q; }',
'coupling_map': [[0], [1], [2], [3], [4]]},
{'name': 'rz',
'parameters': ['theta'],
'qasm_def': 'gate rz(theta) q { U(0, 0, theta) q; }',
'coupling_map': [[0], [1], [2], [3], [4]]},
{'name': 'sx',
'parameters': [],
'qasm_def': 'gate sx q { U(pi/2, 3*pi/2, pi/2) q; }',
'coupling_map': [[0], [1], [2], [3], [4]]},
{'name': 'x',
'parameters': [],
'qasm_def': 'gate x q { U(pi, 0, pi) q; }',
'coupling_map': [[0], [1], [2], [3], [4]]},
{'name': 'cx',
'parameters': [],
'qasm_def': 'gate cx q0, q1 { CX q0, q1; }',
'coupling_map': [[0, 1],
[1, 0],
[1, 2],
[1, 3],
[2, 1],
[3, 1],
[3, 4],
[4, 3]]},
{'name': 'reset', 'parameters': None, 'qasm_def': None}],
'local': False,
'simulator': False,
'conditional': False,
'open_pulse': False,
'memory': True,
'max_shots': 8192,
'coupling_map': [[0, 1],
[1, 0],
[1, 2],
[1, 3],
[2, 1],
[3, 1],
[3, 4],
[4, 3]],
'dynamic_reprate_enabled': True,
'supported_instructions': ['id',
'sx',
'shiftf',
'reset',
'cx',
'u1',
'u2',
'u3',
'play',
'delay',
'x',
'setf',
'acquire',
'measure',
'rz'],
'rep_delay_range': [0.0, 500.0],
'default_rep_delay': 250.0,
'max_experiments': 75,
'sample_name': 'family: Falcon, revision: 4, segment: T',
'n_registers': 1,
'credits_required': True,
'online_date': datetime.datetime(2021, 1, 8, 5, 0, tzinfo=tzutc()),
'description': '5 qubit device Belem',
'dt': 0.2222222222222222,
'dtm': 0.2222222222222222,
'processor_type': {'family': 'Falcon', 'revision': 4, 'segment': 'T'},
'acquisition_latency': [],
'allow_q_object': True,
'channels': {'acquire0': {'operates': {'qubits': [0]},
'purpose': 'acquire',
'type': 'acquire'},
'acquire1': {'operates': {'qubits': [1]},
'purpose': 'acquire',
'type': 'acquire'},
'acquire2': {'operates': {'qubits': [2]},
'purpose': 'acquire',
'type': 'acquire'},
'acquire3': {'operates': {'qubits': [3]},
'purpose': 'acquire',
'type': 'acquire'},
'acquire4': {'operates': {'qubits': [4]},
'purpose': 'acquire',
'type': 'acquire'},
'd0': {'operates': {'qubits': [0]}, 'purpose': 'drive', 'type': 'drive'},
'd1': {'operates': {'qubits': [1]}, 'purpose': 'drive', 'type': 'drive'},
'd2': {'operates': {'qubits': [2]}, 'purpose': 'drive', 'type': 'drive'},
'd3': {'operates': {'qubits': [3]}, 'purpose': 'drive', 'type': 'drive'},
'd4': {'operates': {'qubits': [4]}, 'purpose': 'drive', 'type': 'drive'},
'm0': {'operates': {'qubits': [0]}, 'purpose': 'measure', 'type': 'measure'},
'm1': {'operates': {'qubits': [1]}, 'purpose': 'measure', 'type': 'measure'},
'm2': {'operates': {'qubits': [2]}, 'purpose': 'measure', 'type': 'measure'},
'm3': {'operates': {'qubits': [3]}, 'purpose': 'measure', 'type': 'measure'},
'm4': {'operates': {'qubits': [4]}, 'purpose': 'measure', 'type': 'measure'},
'u0': {'operates': {'qubits': [0, 1]},
'purpose': 'cross-resonance',
'type': 'control'},
'u1': {'operates': {'qubits': [1, 0]},
'purpose': 'cross-resonance',
'type': 'control'},
'u2': {'operates': {'qubits': [1, 2]},
'purpose': 'cross-resonance',
'type': 'control'},
'u3': {'operates': {'qubits': [1, 3]},
'purpose': 'cross-resonance',
'type': 'control'},
'u4': {'operates': {'qubits': [2, 1]},
'purpose': 'cross-resonance',
'type': 'control'},
'u5': {'operates': {'qubits': [3, 1]},
'purpose': 'cross-resonance',
'type': 'control'},
'u6': {'operates': {'qubits': [3, 4]},
'purpose': 'cross-resonance',
'type': 'control'},
'u7': {'operates': {'qubits': [4, 3]},
'purpose': 'cross-resonance',
'type': 'control'}},
'conditional_latency': [],
'discriminators': ['linear_discriminator',
'quadratic_discriminator',
'hw_centroid'],
'hamiltonian': {'description': 'Qubits are modeled as Duffing oscillators. In this case, the system includes higher energy states, i.e. not just |0> and |1>. The Pauli operators are generalized via the following set of transformations:\n\n$(\\mathbb{I}-\\sigma_{i}^z)/2 \\rightarrow O_i \\equiv b^\\dagger_{i} b_{i}$,\n\n$\\sigma_{+} \\rightarrow b^\\dagger$,\n\n$\\sigma_{-} \\rightarrow b$,\n\n$\\sigma_{i}^X \\rightarrow b^\\dagger_{i} + b_{i}$.\n\nQubits are coupled through resonator buses. The provided Hamiltonian has been projected into the zero excitation subspace of the resonator buses leading to an effective qubit-qubit flip-flop interaction. The qubit resonance frequencies in the Hamiltonian are the cavity dressed frequencies and not exactly what is returned by the backend defaults, which also includes the dressing due to the qubit-qubit interactions.\n\nQuantities are returned in angular frequencies, with units 2*pi*GHz.\n\nWARNING: Currently not all system Hamiltonian information is available to the public, missing values have been replaced with 0.\n',
'h_latex': '\\begin{align} \\mathcal{H}/\\hbar = & \\sum_{i=0}^{4}\\left(\\frac{\\omega_{q,i}}{2}(\\mathbb{I}-\\sigma_i^{z})+\\frac{\\Delta_{i}}{2}(O_i^2-O_i)+\\Omega_{d,i}D_i(t)\\sigma_i^{X}\\right) \\\\ & + J_{0,1}(\\sigma_{0}^{+}\\sigma_{1}^{-}+\\sigma_{0}^{-}\\sigma_{1}^{+}) + J_{1,2}(\\sigma_{1}^{+}\\sigma_{2}^{-}+\\sigma_{1}^{-}\\sigma_{2}^{+}) + J_{1,3}(\\sigma_{1}^{+}\\sigma_{3}^{-}+\\sigma_{1}^{-}\\sigma_{3}^{+}) + J_{3,4}(\\sigma_{3}^{+}\\sigma_{4}^{-}+\\sigma_{3}^{-}\\sigma_{4}^{+}) \\\\ & + \\Omega_{d,0}(U_{0}^{(0,1)}(t))\\sigma_{0}^{X} + \\Omega_{d,1}(U_{1}^{(1,0)}(t)+U_{3}^{(1,3)}(t)+U_{2}^{(1,2)}(t))\\sigma_{1}^{X} \\\\ & + \\Omega_{d,2}(U_{4}^{(2,1)}(t))\\sigma_{2}^{X} + \\Omega_{d,3}(U_{6}^{(3,4)}(t)+U_{5}^{(3,1)}(t))\\sigma_{3}^{X} \\\\ & + \\Omega_{d,4}(U_{7}^{(4,3)}(t))\\sigma_{4}^{X} \\\\ \\end{align}',
'h_str': ['_SUM[i,0,4,wq{i}/2*(I{i}-Z{i})]',
'_SUM[i,0,4,delta{i}/2*O{i}*O{i}]',
'_SUM[i,0,4,-delta{i}/2*O{i}]',
'_SUM[i,0,4,omegad{i}*X{i}||D{i}]',
'jq0q1*Sp0*Sm1',
'jq0q1*Sm0*Sp1',
'jq1q2*Sp1*Sm2',
'jq1q2*Sm1*Sp2',
'jq1q3*Sp1*Sm3',
'jq1q3*Sm1*Sp3',
'jq3q4*Sp3*Sm4',
'jq3q4*Sm3*Sp4',
'omegad1*X0||U0',
'omegad0*X1||U1',
'omegad3*X1||U3',
'omegad2*X1||U2',
'omegad1*X2||U4',
'omegad4*X3||U6',
'omegad1*X3||U5',
'omegad3*X4||U7'],
'osc': {},
'qub': {'0': 3, '1': 3, '2': 3, '3': 3, '4': 3},
'vars': {'delta0': -2.1119231275656283,
'delta1': -1.989081364755034,
'delta2': -2.0773937776320905,
'delta3': -2.096945401946966,
'delta4': -2.0819029355928373,
'jq0q1': 0.011772262300160973,
'jq1q2': 0.012605700949390706,
'jq1q3': 0.012591488659137172,
'jq3q4': 0.01051661912410011,
'omegad0': 0.8027253632102426,
'omegad1': 0.7994609296532756,
'omegad2': 1.9688450308371457,
'omegad3': 0.7721838249505281,
'omegad4': 0.6185844031359571,
'wq0': 31.98159911982912,
'wq1': 32.95737791154753,
'wq2': 33.68606633828003,
'wq3': 32.48696041179116,
'wq4': 33.039435267685114}},
'meas_kernels': ['hw_boxcar'],
'meas_levels': [1, 2],
'meas_lo_range': [[6.801661824e+18, 7.801661824e+18],
[6.893594461e+18, 7.893594461e+18],
[6.860214726e+18, 7.860214726e+18],
[6.803382327e+18, 7.803382327e+18],
[6.926310916e+18, 7.926310916e+18]],
'meas_map': [[0, 1, 2, 3, 4]],
'measure_esp_enabled': False,
'multi_meas_enabled': True,
'n_uchannels': 8,
'parametric_pulses': ['gaussian', 'gaussian_square', 'drag', 'constant'],
'quantum_volume': 16,
'qubit_channel_mapping': [['d0', 'm0', 'u1', 'u0'],
['u4', 'd1', 'u2', 'u1', 'u3', 'm1', 'u5', 'u0'],
['u2', 'd2', 'm2', 'u4'],
['m3', 'u3', 'u7', 'd3', 'u5', 'u6'],
['m4', 'd4', 'u6', 'u7']],
'qubit_lo_range': [[4.590029587904213e+18, 5.590029587904213e+18],
[4.745329605970436e+18, 5.745329605970436e+18],
[4.861303971058769e+18, 5.861303971058769e+18],
[4.670460335567279e+18, 5.670460335567279e+18],
[4.758389439816784e+18, 5.758389439816784e+18]],
'rep_times': [0.001],
'u_channel_lo': [[{'q': 1, 'scale': (1+0j)}],
[{'q': 0, 'scale': (1+0j)}],
[{'q': 2, 'scale': (1+0j)}],
[{'q': 3, 'scale': (1+0j)}],
[{'q': 1, 'scale': (1+0j)}],
[{'q': 1, 'scale': (1+0j)}],
[{'q': 4, 'scale': (1+0j)}],
[{'q': 3, 'scale': (1+0j)}]],
'uchannels_enabled': True,
'url': 'None',
'input_allowed': ['job'],
'allow_object_storage': True,
'pulse_num_channels': 9,
'pulse_num_qubits': 3}
The backend configuration provides some useful information via its attributes, such as basis_gates
, coupling_map
, max_experiments
, max_shots
, quantum_volume
, and simulator
.
The backend properties contain data that was measured and reported. Let’s see what kind of information is reported for qubit 0.
Note: The following example displays the properties of the SX
gate. Each backend may have different basis gates which may not contain SX
.
[13]:
props = backend.properties()
def describe_qubit(qubit, properties):
"""Print a string describing some of reported properties of the given qubit."""
# Conversion factors from standard SI units
us = 1e6
ns = 1e9
GHz = 1e-9
print(
f"Qubit {qubit} has a \n"
f" - T1 time of {properties.t1(qubit)} microseconds\n"
f" - T2 time of {properties.t2(qubit)} microseconds\n"
f" - SX gate error of {properties.gate_error('sx', qubit)}\n"
f" - SX gate duration of {properties.gate_length('sx', qubit)*ns} nanoseconds\n"
f" - resonant frequency of {properties.frequency(qubit) * GHz} GHz"
)
describe_qubit(0, props)
Qubit 0 has a
- T1 time of 0.000118798578854076 microseconds
- T2 time of 8.30176098567665e-05 microseconds
- SX gate error of 0.0003355394204181594
- SX gate duration of 35.55555555555556 nanoseconds
- resonant frequency of 4.820326782885405 GHz
Backend Service#
IBMBackendService provides generic backend related services for an account without requiring a particular backend as input. The main methods it supports are:
jobs()
: Returns a list of previously submitted jobs in the account.retrieve_job(JOB_ID)
: Returns a job by its job ID.
The backend service is defined as the backend
attribute of an IBMProvider
instance. All of the backends available to this provider are also attributes of the backend service, allowing the backend names to be autocompleted:
[14]:
provider.get_backend("ibmq_qasm_simulator")
[14]:
<IBMBackend('ibmq_qasm_simulator')>
To see the last five jobs submitted to the account, regardless of which backend they ran on:
[15]:
for ran_job in provider.backend.jobs(limit=5):
print(str(ran_job.job_id()) + " " + str(ran_job.status()))
5fdccad1c01fec001a47e018 JobStatus.QUEUED
5fdcc5fad4445500193f0072 JobStatus.DONE
5fdcc5f923da0f001acf6f67 JobStatus.DONE
5fdcc5f9d4445500193f0071 JobStatus.DONE
5fdcc41b23da0f001acf6f44 JobStatus.DONE
To retrieve a particular job:
[16]:
if ran_job is not None:
job = provider.backend.retrieve_job(ran_job.job_id())
Jobs#
Job instances can be thought of as the “ticket” for a submitted job. They find out the execution state at a given point in time (for example, if the job is queued, running, or has failed), and also allow control over the job. They have the following methods:
status()
: Returns the status of the job.backend()
: Returns the backend the job was run on.job_id()
: Gets the job_id.cancel()
: Cancels the job.result()
: Gets the results from the circuit run.
Some of the methods that are only available to “IBM Job”(IBMJob) include:
creation_date()
: Gives the date at which the job was created.queue_info()
: Returns queue information for this job, including queue position, estimated start and end time, and dynamic priorities for the hub, group, and project.error_message()
: The error message of failed jobs, if any.name()
: Returns the name assigned to this job.properties()
: Returns the backend properties for this job.time_per_step()
: Returns the time spent for each step (job creation, validation, etc).
Refer to the IBMJob documentation for a complete list of methods.
Now some examples. Let’s start with submitting a job:
[17]:
from qiskit import *
from qiskit.compiler import transpile, assemble
[18]:
qr = QuantumRegister(3)
cr = ClassicalRegister(3)
circuit = QuantumCircuit(qr, cr)
circuit.x(qr[0])
circuit.x(qr[1])
circuit.ccx(qr[0], qr[1], qr[2])
circuit.cx(qr[0], qr[1])
circuit.measure(qr, cr)
[18]:
<qiskit.circuit.instructionset.InstructionSet at 0x14338efd0>
You should first map the circuit using the transpile function and then send it to the backend:
[19]:
mapped_circuit = transpile(circuit, backend=backend)
job = backend.run(mapped_circuit, shots=1024)
The status()
method returns the job status and a message:
[19]:
job.status()
[19]:
<JobStatus.VALIDATING: 'job is being validated'>
To get a backend object from the job, use the backend()
method:
[21]:
backend_temp = job.backend()
backend_temp
[21]:
<IBMBackend('ibmq_belem')>
To get the job_id use the job_id()
method:
[22]:
job.job_id()
[22]:
'5fdccb1ad4445500193f00db'
To get the result from the job, use the result()
method:
[23]:
result = job.result()
counts = result.get_counts()
print(counts)
{'000': 24, '001': 53, '010': 28, '011': 56, '100': 26, '101': 801, '110': 13, '111': 23}
If you want to check the creation date, use creation_date()
:
[24]:
job.creation_date()
[24]:
datetime.datetime(2020, 12, 18, 10, 30, 34, 565000, tzinfo=tzlocal())
[25]:
from qiskit_ibm_provider.version import __version__
print("qiskit-ibm-provider version: {}".format(__version__))
qiskit-ibm-provider version: 0.1.0
[26]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.18.3 |
qiskit-aer | 0.9.0 |
qiskit-ignis | 0.6.0 |
qiskit-ibmq-provider (deprecated) | 0.17.0 |
qiskit-aqua (deprecated) | 0.9.5 |
qiskit | 0.31.0 |
System information | |
Python | 3.7.11 (default, Jul 27 2021, 07:03:16) [Clang 10.0.0 ] |
OS | Darwin |
CPUs | 8 |
Memory (Gb) | 32.0 |
Mon Sep 13 00:15:27 2021 EDT |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
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.
[ ]: