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.

from qiskit.providers.fake_provider import FakeHanoi

backend = FakeHanoi()


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.

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}."
                '' if config.n_qubits == 1 else 's',
                'supports' if config.open_pulse else 'does not support',
This backend is called fake_hanoi, and is on version 1.0.18. It has 27 qubits. It supports OpenPulse programs. The basis gates supported on this device are ['id', 'rz', 'sx', 'x', 'cx', 'reset'].

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.

config.dt  # units of seconds

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.

[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:


The measurement map, explained in detail on [this page COMING SOON], is also found here.


The configuration also supplies convenient methods for getting channels for your schedule programs. For instance:


It is a matter of style and personal preference whether you use or DriveChannel(0).


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.

props =
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(
              properties.t1(qubit) * us,
              properties.t2(qubit) * us,
              properties.gate_error('sx', qubit),
              properties.gate_length('sx', qubit) * ns,
              properties.frequency(qubit) * GHz))

describe_qubit(0, props)
Qubit 0 has a
  - T1 time of 162.29562357444243 microseconds
  - T2 time of 171.74648699183206 microseconds
  - SX gate error of 0.00013790682762652163
  - SX gate duration of 21.333333333333332 nanoseconds
  - resonant frequency of 5.035257503599211 GHz

Properties are not guaranteed to be reported, but backends without Pulse access typically also provide this data.


Unlike the other two sections, PulseDefaults are only available for Pulse-enabled backends. It contains the default program settings run on the device.

defaults = backend.defaults()

Drive frequencies

Defaults contains the default frequency settings for the drive and measurement signal channels:

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 5.035257503599211 GHz.
MeasureChannel(0) defaults to a modulation frequency of 7.1653715820000015 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.

calibrations = defaults.instruction_schedule_map
<InstructionScheduleMap(1Q instructions:
  q0: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q1: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q2: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q3: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q4: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q5: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q6: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q7: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q8: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q9: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q10: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q11: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q12: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q13: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q14: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q15: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q16: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q17: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q18: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q19: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q20: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q21: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q22: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q23: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q24: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q25: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
  q26: {'u1', 'u3', 'sx', 'u2', 'x', 'rz', 'measure', 'id'}
Multi qubit instructions:
  (0, 1): {'cx'}
  (1, 0): {'cx'}
  (1, 2): {'cx'}
  (1, 4): {'cx'}
  (2, 1): {'cx'}
  (2, 3): {'cx'}
  (3, 2): {'cx'}
  (3, 5): {'cx'}
  (4, 1): {'cx'}
  (4, 7): {'cx'}
  (5, 3): {'cx'}
  (5, 8): {'cx'}
  (6, 7): {'cx'}
  (7, 4): {'cx'}
  (7, 6): {'cx'}
  (7, 10): {'cx'}
  (8, 5): {'cx'}
  (8, 9): {'cx'}
  (8, 11): {'cx'}
  (9, 8): {'cx'}
  (10, 7): {'cx'}
  (10, 12): {'cx'}
  (11, 8): {'cx'}
  (11, 14): {'cx'}
  (12, 10): {'cx'}
  (12, 13): {'cx'}
  (12, 15): {'cx'}
  (13, 12): {'cx'}
  (13, 14): {'cx'}
  (14, 11): {'cx'}
  (14, 13): {'cx'}
  (14, 16): {'cx'}
  (15, 12): {'cx'}
  (15, 18): {'cx'}
  (16, 14): {'cx'}
  (16, 19): {'cx'}
  (17, 18): {'cx'}
  (18, 15): {'cx'}
  (18, 17): {'cx'}
  (18, 21): {'cx'}
  (19, 16): {'cx'}
  (19, 20): {'cx'}
  (19, 22): {'cx'}
  (20, 19): {'cx'}
  (21, 18): {'cx'}
  (21, 23): {'cx'}
  (22, 19): {'cx'}
  (22, 25): {'cx'}
  (23, 21): {'cx'}
  (23, 24): {'cx'}
  (24, 23): {'cx'}
  (24, 25): {'cx'}
  (25, 22): {'cx'}
  (25, 24): {'cx'}
  (25, 26): {'cx'}
  (26, 25): {'cx'}
  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26): {'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:

measure_schedule = calibrations.get('measure', range(config.n_qubits))

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.

# You can use `has` to see if an operation is defined. Ex: Does qubit 3 have an x gate defined?
calibrations.has('x', 3)
# Some circuit operations take parameters. U1 takes a rotation angle:
calibrations.get('u1', 0, P0=3.1415)
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 Schedules that you’ve made yourself.

On this page, we’ll show how to schedule QuantumCircuits into Pulse Schedules.


Version Information

Qiskit SoftwareVersion
System information
Python version3.9.9
Python compilerGCC 11.1.0
Python buildmain, Dec 29 2021 22:19:36
Memory (Gb)125.64827728271484
Wed Sep 21 09:34:41 2022 EDT

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

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.