Skip to main contentIBM Quantum Documentation

Representing quantum computers for the transpiler

Package versions

The code on this page was developed using the following requirements. We recommend using these versions or newer.

qiskit[all]~=1.2.4
qiskit-aer~=0.15.1
qiskit-ibm-runtime~=0.31.0
qiskit-serverless~=0.17.1
qiskit-ibm-catalog~=0.1

To convert an abstract circuit to an ISA circuit that can run on a specific QPU (quantum processing unit), the transpiler needs certain information about the QPU. This information is found in two places: the BackendV2 (or legacy BackendV1) object you plan to submit jobs to, and the backend's Target attribute.

  • The Target contains all the relevant constraints of a device, such as its native basis gates, qubit connectivity, and pulse or timing information.
  • The Backend possesses a Target by default, contains additional information -- such as the InstructionScheduleMap, and provides the interface for submitting quantum circuit jobs.

You can also explicitly provide information for the transpiler to use, for example, if you have a specific use case, or if you believe this information will help the transpiler generate a more optimized circuit.

The precision with which the transpiler produces the most appropriate circuit for specific hardware depends on how much information the Target or Backend has about its constraints.

Note

Because many of the underlying transpilation algorithms are stochastic, there is no guarantee that a better circuit will be found.

This page shows several examples of passing QPU information to the transpiler. These examples use the target from the FakeSherbrooke mock backend.


Default configuration

The simplest use of the transpiler is to provide all the QPU information by providing the Backend or Target. To better understand how the transpiler works, construct a circuit and transpile it with different information, as follows.

Import the necessary libraries and instantiate the QPU: In order to convert an abstract circuit to an ISA circuit that can run on a specific processor, the transpiler needs certain information about the processor. Typically, this information is stored in the Backend or Target provided to the transpiler, and no further information is needed. However, you can also explicitly provide information for the transpiler to use, for example, if you have a specific use case, or if you believe this information will help the transpiler generate a more optimized circuit.

This topic shows several examples of passing information to the transpiler. These examples use the target from the FakeSherbrooke mock backend.

from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
 
backend = FakeSherbrooke()
target = backend.target

The example circuit uses an instance of EfficientSU2 from Qiskit's circuit library. The following cell decomposes the circuit before drawing it to show its gate structure.

from qiskit.circuit.library import EfficientSU2
 
qc = EfficientSU2(12, entanglement="circular", reps=1)
 
qc.decompose().draw("mpl")

Output:

This example uses default settings to transpile to the backend's target, which provides all the information needed to convert the circuit to one that will run on the backend.

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
 
pass_manager = generate_preset_pass_manager(
    optimization_level=1, target=target, seed_transpiler=12345
)
qc_t_target = pass_manager.run(qc)
qc_t_target.draw("mpl", idle_wires=False, fold=-1)

Output:

This example is used in later sections of this topic to illustrate that the coupling map and basis gates are the essential pieces of information to pass to the transpiler for optimal circuit construction. The QPU can usually select default settings for other information that is not passed in, such as timing and scheduling.


Coupling map

The coupling map is a graph that shows which qubits are connected and hence have two-qubit gates between them. Sometimes this graph is directional, meaning that the two-qubit gates can only go in one direction. However, the transpiler can always flip a gate's direction by adding additional single-qubit gates. An abstract quantum circuit can always be represented on this graph, even if its connectivity is limited, by introducing SWAP gates to move the quantum information around.

The qubits from our abstract circuits are called virtual qubits and those on the coupling map are physical qubits. The transpiler provides a mapping between virtual and physical qubits. One of the first steps in transpilation, the layout stage, performs this mapping.

Note

Although the routing stage is intertwined with the layout stage — which selects the actual qubits — by default, this topic treats them as separate stages for simplicity. The combination of routing and layout is called qubit mapping. Learn more about these stages in the Transpiler stages topic.

Pass the coupling_map keyword argument to see its effect on the transpiler:

coupling_map = target.build_coupling_map()
 
pass_manager = generate_preset_pass_manager(
    optimization_level=0, coupling_map=coupling_map, seed_transpiler=12345
)
qc_t_cm_lv0 = pass_manager.run(qc)
qc_t_cm_lv0.draw("mpl", idle_wires=False, fold=-1)

Output:

As shown above, several SWAP gates were inserted (each consisting of three CX gates), which will cause a lot of errors on current devices. To see which qubits are selected on the actual qubit topology, use plot_circuit_layout from Qiskit Visualizations:

from qiskit.visualization import plot_circuit_layout
 
plot_circuit_layout(qc_t_cm_lv0, backend, view="physical")

Output:

This shows that our virtual qubits 0-11 were trivially mapped to the line of physical qubits 0-11. Let's return to the default (optimization_level=1), which uses VF2Layout if any routing is required.

pass_manager = generate_preset_pass_manager(
    optimization_level=1, coupling_map=coupling_map, seed_transpiler=12345
)
qc_t_cm_lv1 = pass_manager.run(qc)
qc_t_cm_lv1.draw("mpl", idle_wires=False, fold=-1)

Output:

Now there are no SWAP gates inserted and the physical qubits selected are the same when using the target class.

from qiskit.visualization import plot_circuit_layout
 
plot_circuit_layout(qc_t_cm_lv1, backend, view="physical")

Output:

Now the layout is in a ring. Because this layout respects the circuit's connectivity, there are no SWAP gates, providing a much better circuit for execution.


Basis gates

Every quantum computer supports a limited instruction set, called its basis gates. Every gate in the circuit must be translated to the elements of this set. This set should consist of single- and two-qubit gates that provide a universal gates set, meaning that any quantum operation can be decomposed into those gates. This is done by the BasisTranslator, and the basis gates can be specified as a keyword argument to the transpiler to provide this information.

basis_gates = list(target.operation_names)
print(basis_gates)

Output:

['id', 'if_else', 'sx', 'delay', 'x', 'switch_case', 'ecr', 'rz', 'reset', 'for_loop', 'measure']

The default single-qubit gates on ibm_sherbrooke are rz, x, and sx, and the default two-qubit gate is ecr (echoed cross-resonance). CX gates are constructed from ecr gates, so on some QPUs ecr is specified as the two-qubit basis gate, while on others cx is the default. The ecr gate is the entangling part of the cx gate. To use a gate that is not in the basis gate set, follow instructions in the Qiskit SDK API documentation for custom gates using pulse gates. In addition to the control gates, there are also delay and measurement instructions.

Note

QPUs have default basis gates, but you can choose whatever gates you want, as long as you provide the instruction or add pulse gates (see Create transpiler passes.) The default basis gates are those that calibrations have been done for on the QPU, so no further instruction/pulse gates need to be provided. For example, on some QPUs cx is the default two-qubit gate and ecr on others. See the Native gates and operations topic for more details.

pass_manager = generate_preset_pass_manager(
    optimization_level=1,
    coupling_map=coupling_map,
    basis_gates=basis_gates,
    seed_transpiler=12345,
)
qc_t_cm_bg = pass_manager.run(qc)
qc_t_cm_bg.draw("mpl", idle_wires=False, fold=-1)

Output:

Note that the CXGates have been decomposed to ecr gates and single-qubit basis gates.


Device error rates

The Target class can contain information about the error rates for operations on the device. For example, the following code retrieves the properties for the echoed cross-resonance (ECR) gate between qubit 1 and 0 (note that the ECR gate is directional):

target["ecr"][(1, 0)]

Output:

InstructionProperties(duration=5.333333333333332e-07, error=0.005822316907363928, calibration=PulseQobj)

The output displays the duration of the gate (in seconds) and its error rate. To reveal error information to the transpiler, build a target model with the basis_gates and coupling_map from above and populate it with error values from the backend FakeSherbrooke.

from qiskit.transpiler import Target
from qiskit.circuit.controlflow import IfElseOp, SwitchCaseOp, ForLoopOp
 
err_targ = Target.from_configuration(
    basis_gates=basis_gates,
    coupling_map=coupling_map,
    num_qubits=target.num_qubits,
    custom_name_mapping={
        "if_else": IfElseOp,
        "switch_case": SwitchCaseOp,
        "for_loop": ForLoopOp,
    },
)
 
for i, (op, qargs) in enumerate(target.instructions):
    if op.name in basis_gates:
        err_targ[op.name][qargs] = target.instruction_properties(i)

Transpile with our new target err_targ as the target:

pass_manager = generate_preset_pass_manager(
    optimization_level=1, target=err_targ, seed_transpiler=12345
)
qc_t_cm_bg_et = pass_manager.run(qc)
qc_t_cm_bg_et.draw("mpl", idle_wires=False, fold=-1)

Output:

Because the target includes error information, the VF2PostLayout pass tries to find the optimal qubits to use, resulting in the same circuit that was originally found with the same physical qubits.


Next steps

Was this page helpful?
Report a bug or request content on GitHub.