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-ibm-runtime~=0.33.2
qiskit-aer~=0.15.1
qiskit-serverless~=0.18.0
qiskit-ibm-catalog~=0.2
qiskit-addon-sqd~=0.8.0
qiskit-addon-utils~=0.1.0
qiskit-addon-aqc-tensor~=0.1.2
qiskit-addon-obp~=0.1.0
scipy~=1.14.1
pyscf~=2.7.0
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 aTarget
by default, contains additional information -- such as theInstructionScheduleMap
, 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.
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.
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:
['reset', 'ecr', 'delay', 'if_else', 'rz', 'for_loop', 'sx', 'switch_case', 'id', 'measure', 'x']
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.
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 CXGate
s 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
- Understand Transpilation default settings and configuration options.
- Review the Commonly used parameters for transpilation topic.
- Try the Submit transpiled circuits tutorial.
- See the Transpile API documentation.