Note
This page was generated from tutorials/circuits_advanced/08_gathering_system_information.ipynb.
Obtaining information about your backend
¶
Note: All the attributes of the backend are described in detail in theQiskit Backend Specifications. This page reviews a subset of the spec.¶
Programming a quantum computer at the microwave pulse level requires more information about the device than is required at the circuit level. A quantum circuit is built for an abstract quantum computer – it will yield the same quantum state on any quantum computer (except for varying performance levels). A pulse schedule, on the other hand, is so specific to the device, that running one program on two different backends is not expected to have the same result, even on perfectly noiseless systems.
As a basic example, imagine a drive pulse q0_X180
calibrated on qubit 0 to enact an \(X180\) pulse, which flips the state of qubit 0. If we use the samples from that pulse on qubit 1 on the same device, or qubit 0 on another device, we do not know what the resulting state will be – but we can be pretty sure it won’t be an \(X180\) operation. The qubits are each unique, with various drive coupling strengths. If we have specified a frequency for the drive pulse, it’s very probable that
pulse would have little effect on another qubit, which has its own resonant frequency.
With that, we have motivated why information from the backend may be very useful at times for building Pulse schedules. The information included in a backend
is broken into three main parts:
Configuration: static backend features
Properties: measured and reported backend characteristics
Defaults: default settings for the OpenPulse-enabled backend
which are each covered in the following sections. While all three of these contain interesting data for Pulse users, the defaults are only provided for backends enabled with OpenPulse.
The first thing you’ll need to do is grab a backend to inspect. Here we use a mocked backend that contains a snapshot of data from the real OpenPulse-enabled backend.
[1]:
from qiskit.test.mock import FakeAlmaden
backend = FakeAlmaden()
Configuration¶
The configuration is where you’ll find data about the static setup of the device, such as its name, version, the number of qubits, and the types of features it supports.
Let’s build a description of our backend using information from the backend
’s config.
[2]:
config = backend.configuration()
# Basic Features
print("This backend is called {0}, and is on version {1}. It has {2} qubit{3}. It "
"{4} OpenPulse programs. The basis gates supported on this device are {5}."
"".format(config.backend_name,
config.backend_version,
config.n_qubits,
'' if config.n_qubits == 1 else 's',
'supports' if config.open_pulse else 'does not support',
config.basis_gates))
This backend is called fake_almaden, and is on version 1.4.6. It has 20 qubits. It supports OpenPulse programs. The basis gates supported on this device are ['id', 'u1', 'u2', 'u3', 'cx'].
Neat! All of the above configuration is available for any backend, whether enabled with OpenPulse or not, although it is not an exhaustive list. There are additional attributes available on Pulse backends. Let’s go into a bit more detail with those.
The timescale, dt
, is backend dependent. Think of this as the inverse sampling rate of the control rack’s arbitrary waveform generators. Each sample point and duration in a Pulse Schedule
is given in units of this timescale.
[3]:
config.dt # units of seconds
[3]:
2.2222222222222221e-10
The configuration also provides information that is useful for building measurements. Pulse supports three measurement levels: 0: RAW
, 1: KERNELED
, and 2: DISCRIMINATED
. The meas_levels
attribute tells us which of those are supported by this backend. To learn how to execute programs with these different levels, see this page – COMING SOON.
[4]:
config.meas_levels
[4]:
[1, 2]
For backends which support measurement level 0, the sampling rate of the control rack’s analog-to-digital converters (ADCs) also becomes relevant. The configuration also has this info, where dtm
is the time per sample returned:
[5]:
config.dtm
[5]:
2.2222222222222221e-10
The measurement map, explained in detail on [this page COMING SOON], is also found here.
[6]:
config.meas_map
[6]:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]]
The configuration also supplies convenient methods for getting channels for your schedule programs. For instance:
[7]:
config.drive(0)
[7]:
DriveChannel(0)
[8]:
config.measure(0)
[8]:
MeasureChannel(0)
[9]:
config.acquire(0)
[9]:
AcquireChannel(0)
It is a matter of style and personal preference whether you use config.drive(0)
or DriveChannel(0)
.
Properties¶
The backend
properties contain data that was measured and optionally reported by the provider. Let’s see what kind of information is reported for qubit 0.
[10]:
props = backend.properties()
[11]:
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("Qubit {0} has a \n"
" - T1 time of {1} microseconds\n"
" - T2 time of {2} microseconds\n"
" - U2 gate error of {3}\n"
" - U2 gate duration of {4} nanoseconds\n"
" - resonant frequency of {5} GHz".format(
qubit,
properties.t1(qubit) * us,
properties.t2(qubit) * us,
properties.gate_error('u2', qubit),
properties.gate_length('u2', qubit) * ns,
properties.frequency(qubit) * GHz))
describe_qubit(0, props)
Qubit 0 has a
- T1 time of 96.36208105210916 microseconds
- T2 time of 43.4363963452638 microseconds
- U2 gate error of 0.0011847011560486597
- U2 gate duration of 35.55555555555556 nanoseconds
- resonant frequency of 4.85648672471907 GHz
Properties are not guaranteed to be reported, but backends without Pulse access typically also provide this data.
Defaults¶
Unlike the other two sections, PulseDefaults
are only available for Pulse-enabled backends. It contains the default program settings run on the device.
[12]:
defaults = backend.defaults()
Drive frequencies¶
Defaults contains the default frequency settings for the drive and measurement signal channels:
[13]:
q0_freq = defaults.qubit_freq_est[0] # Hz
q0_meas_freq = defaults.meas_freq_est[0] # Hz
GHz = 1e-9
print("DriveChannel(0) defaults to a modulation frequency of {} GHz.".format(q0_freq * GHz))
print("MeasureChannel(0) defaults to a modulation frequency of {} GHz.".format(q0_meas_freq * GHz))
DriveChannel(0) defaults to a modulation frequency of 4.85648672471907 GHz.
MeasureChannel(0) defaults to a modulation frequency of 7.264856891000002 GHz.
Pulse Schedule definitions for QuantumCircuit instructions¶
Finally, one of the most important aspects of the backend
for Schedule
building is the InstructionScheduleMap
. This is a basic mapping from a circuit operation’s name and qubit to the default pulse-level implementation of that instruction.
[14]:
calibrations = defaults.instruction_schedule_map
print(calibrations)
<InstructionScheduleMap(1Q instructions:
q0: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q1: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q2: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q3: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q4: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q5: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q6: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q7: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q8: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q9: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q10: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q11: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q12: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q13: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q14: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q15: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q16: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q17: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q18: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
q19: {'id', 'measure', 'u2', 'u1', 'x', 'u3'}
Multi qubit instructions:
(0, 1): {'cx'}
(1, 0): {'cx'}
(1, 2): {'cx'}
(1, 6): {'cx'}
(2, 1): {'cx'}
(2, 3): {'cx'}
(3, 2): {'cx'}
(3, 4): {'cx'}
(3, 8): {'cx'}
(4, 3): {'cx'}
(5, 6): {'cx'}
(5, 10): {'cx'}
(6, 1): {'cx'}
(6, 5): {'cx'}
(6, 7): {'cx'}
(7, 6): {'cx'}
(7, 8): {'cx'}
(7, 12): {'cx'}
(8, 3): {'cx'}
(8, 7): {'cx'}
(8, 9): {'cx'}
(9, 8): {'cx'}
(9, 14): {'cx'}
(10, 5): {'cx'}
(10, 11): {'cx'}
(11, 10): {'cx'}
(11, 12): {'cx'}
(11, 16): {'cx'}
(12, 7): {'cx'}
(12, 11): {'cx'}
(12, 13): {'cx'}
(13, 12): {'cx'}
(13, 14): {'cx'}
(13, 18): {'cx'}
(14, 9): {'cx'}
(14, 13): {'cx'}
(15, 16): {'cx'}
(16, 11): {'cx'}
(16, 15): {'cx'}
(16, 17): {'cx'}
(17, 16): {'cx'}
(17, 18): {'cx'}
(18, 13): {'cx'}
(18, 17): {'cx'}
(18, 19): {'cx'}
(19, 18): {'cx'}
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19): {'measure'}
)>
Rather than build a measurement schedule from scratch, let’s see what was calibrated by the backend to measure the qubits on this device:
[15]:
measure_schedule = calibrations.get('measure', range(config.n_qubits))
measure_schedule.draw(backend=backend)
[15]:

This can easily be appended to your own Pulse Schedule
(sched += calibrations.get('measure', <qubits>) << sched.duration
)!
Likewise, each qubit will have a Schedule
defined for each basis gate, and they can be appended directly to any Schedule
you build.
[16]:
# You can use `has` to see if an operation is defined. Ex: Does qubit 3 have an x gate defined?
calibrations.has('x', 3)
[16]:
True
[17]:
# Some circuit operations take parameters. U1 takes a rotation angle:
calibrations.get('u1', 0, P0=3.1415)
[17]:
Schedule((0, ShiftPhase(-3.1415, DriveChannel(0))), (0, ShiftPhase(-3.1415, ControlChannel(1))), name="u1")
While building your schedule, you can also use calibrations.add(name, qubits, schedule)
to store useful Schedule
s that you’ve made yourself.
On this page, we’ll show how to schedule QuantumCircuit
s into Pulse Schedule
s.
[18]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
/home/computertreker/git/qiskit/qiskit/.tox/docs/lib/python3.7/site-packages/qiskit/aqua/__init__.py:86: DeprecationWarning: The package qiskit.aqua is deprecated. It was moved/refactored to qiskit-terra For more information see <https://github.com/Qiskit/qiskit-aqua/blob/main/README.md#migration-guide>
warn_package('aqua', 'qiskit-terra')
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.18.2 |
qiskit-aer | 0.8.2 |
qiskit-ignis | 0.6.0 |
qiskit-ibmq-provider | 0.16.0 |
qiskit-aqua | 0.9.5 |
qiskit | 0.29.1 |
qiskit-nature | 0.2.2 |
qiskit-finance | 0.3.0 |
qiskit-optimization | 0.2.3 |
qiskit-machine-learning | 0.2.1 |
System information | |
Python | 3.7.12 (default, Nov 22 2021, 14:57:10) [GCC 11.1.0] |
OS | Linux |
CPUs | 32 |
Memory (Gb) | 125.71650314331055 |
Tue Jan 04 11:07:47 2022 EST |
This code is a part of Qiskit
© Copyright IBM 2017, 2022.
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.