Visualize circuit timing
Package versions
The code on this page was developed using the following requirements. We recommend using these versions or newer.
qiskit[all]~=2.1.1
In addition to visualizing instructions on a circuit you might want to use the Qiskit timeline_drawer
method, which lets you visualize a circuit's scheduling. This visualization could help you to quickly spot idling time on qubits, for example.
- This is an experimental function. It is in preview release status and is therefore subject to change.
- This function only applies to Sampler jobs.
Examples
To visualize a scheduled circuit program, you can call this function with a set of control arguments. Most of the output image's appearance can be modified by a stylesheet, but this is not required.
Draw with the default stylesheet
from qiskit import QuantumCircuit
from qiskit.visualization.timeline import draw
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.transpiler import generate_preset_pass_manager
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
backend = GenericBackendV2(5)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
draw(isa_circuit, target=backend.target)
Output:
<Figure size 1400x480 with 1 Axes>
Draw with a stylesheet suited for program debugging
from qiskit import QuantumCircuit
from qiskit.visualization.timeline import draw, IQXDebugging
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.transpiler import generate_preset_pass_manager
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
backend = GenericBackendV2(5)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
draw(isa_circuit, style=IQXDebugging(), target=backend.target)
Output:
<Figure size 1400x640 with 1 Axes>
You can create custom generator or layout functions and update an existing stylesheet with the custom functions. This way, you can control the most of the appearance of the output image without modifying the codebase of the scheduled circuit drawer. See the timeline_drawer
API reference for more examples.
Qiskit Runtime support
While the timeline drawer built in to Qiskit is useful for static circuits, it might not accurately reflect the timing of dynamic circuits because of implicit operations such as broadcasting and branch determination. As part of dynamic circuit support, Qiskit Runtime returns the accurate circuit timing information inside the job results when requested.
Enable timing data retrieval
To enable timing data retrieval, set the experimental scheduler_timing
flag to True
when running the primitive job.
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
sampler = SamplerV2(backend)
sampler.options.experimental = {
"execution": {
"scheduler_timing": True,
},
}
sampler_job = sampler.run([isa_circuit])
Output:
management.get:WARNING:2025-10-09 14:47:33,541: Loading default saved account
---------------------------------------------------------------------------
IBMInputValueError Traceback (most recent call last)
Cell In[4], line 3
1 from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
----> 3 service = QiskitRuntimeService()
4 backend = service.least_busy(operational=True, simulator=False)
6 pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
File c:\1docs\.venv\Lib\site-packages\qiskit_ibm_runtime\qiskit_runtime_service.py:234, in QiskitRuntimeService.__init__(self, channel, token, url, filename, name, instance, proxies, verify, private_endpoint, url_resolver, region, plans_preference, tags)
232 if self._account.instance:
233 if self._account.instance not in [inst["crn"] for inst in self.instances()]:
--> 234 raise IBMInputValueError(
235 "The given API token is associated with an account that does not have access to "
236 f"the instance {self._account.instance}. "
237 "To use this instance, use an API token generated from the account "
238 "with this instance available."
239 )
240 self._default_instance = True
241 self._api_clients = {self._account.instance: RuntimeClient(self._client_params)}
IBMInputValueError: 'The given API token is associated with an account that does not have access to the instance crn:v1:bluemix:public:quantum-computing:us-east:a/6c63dae5281147f1a0449b36e0aaba3a:cbf14cbc-4b63-4698-868e-8bb92dbcc8f4::. To use this instance, use an API token generated from the account with this instance available.'
Access the circuit timing data
When requested, the circuit timing data is returned in the job result metadata, under ["compilation"]["scheduler_timing"]["timing"]
. This field contains the raw timing information. To display the timing information, use the built-in visualization tool to display it, as described in the following section.
Use the following code to access the circuit timing data:
job_result = sampler_job.result()
circuit_schedule = job_result[0].metadata["compilation"]["scheduler_timing"]
circuit_schedule_timing = circuit_schedule["timing"]
Visualize the timings
To visualize the timings, you first need to convert the result metadata to fig
by using the draw_circuit_schedule_timing
(link to API reference when available) method. This method returns a plotly
figure, which you can display directly, save to a file, or both.
from qiskit_ibm_runtime.visualization import draw_circuit_schedule_timing
# Create a figure from the metadata
fig = draw_circuit_schedule_timing(
circuit_schedule=circuit_schedule_timing,
included_channels=None,
filter_readout_channels=False,
filter_barriers=False,
width=1000,
)
# Display the figure
fig.show(renderer="notebook")
# Save to a file
fig.write_html("scheduler_timing.html")
For more information about the plotly
commands, see fig.show()
and fig.write_image("<path.format>")
.
Understand the raw timing data
While visualizing the circuit timing data by using the draw_circuit_schedule_timing
method is the most common use case, it might be useful to understand the structure of the raw timing data returned. This could help you, for example, to extract information programmatically.
The timing data returned in ["compilation"]["scheduler_timing"]["timing"]
is a list of strings. Each string represents a single instruction on some channel and is comma separated into the following data types:
Branch
- Determines whether the instruction is in a control flow (then / else) or a main branchInstruction
- The gate and the qubit to operate onChannel
- The channel that is being assigned with the instruction (Qubit x / AWGRx_y / ...). Arbitrary Wave Generator Readout (AWGR) is used for readout channels communication for measuring qubits, as opposed to drive channels, which are for driving the qubits. TheX
andY
arguments correspond to the readout instrument ID and the qubit number, respectively.T0
- The instruction start time within the complete scheduleDuration
- The instruction's durationPulse
- The type of pulse operation being used
Example:
main,barrier,Qubit 0,7,0,barrier # A barrier on the main branch on qubit 0 at time 7 with 0 duration
main,reset_0,Qubit 0,7,64,play # A reset instruction on the main branch on qubit 0 at time 7 with duration 64 and a play operation
...
Understand the generated figure
The image of the circuit timing data output by draw_circuit_schedule_timing
conveys the following information:
- X axis is time in units of dt seconds, where 1 dt = 1 scheduling cycle.
- Y axis is the channel (think of channels as instruments that emit pulses).
Receive channel
is the only channel that isn't an instrument by itself. It is an instruction played on all channels that are part of a communication procedure with the hub at that time.Qubit x
is the drive channel for qubit x.AWGRx_y
(arbitrary wave generator readout) is used for readout channels communication for measuring qubits. The x and y arguments correspond to the readout instrument ID and the qubit number, respectively.Hub
controls broadcasting.
Additionally, each instruction has the format of X_Y, where X is the name of the instruction and Y is the pulse type. A play
type applies control pulses, and a capture
records the qubit's state.
End-to-end example
This example shows you how to enable the option, get it from the metadata, display it, and save it to a file.
First, set up the environment, define the circuits and convert them to ISA circuits, and define and run the jobs.
from qiskit_ibm_runtime import SamplerV2
from qiskit.circuit import QuantumCircuit
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Create a Bell circuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
qc.draw()
# Convert to an ISA circuit for the given backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
# Generate samplers for backend targets
sampler = SamplerV2(backend)
sampler.options.experimental = {
"execution": {"scheduler_timing": True}}
# Submit jobs
sampler_job = sampler.run([isa_circuit])
print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
result = sampler_job.result()
print(f">>> {' Job:':<10} {sampler_job.job_id()} finished with:\n{result}")
Next, get the circuit schedule timing:
# Get the circuit schedule timing
result[0].metadata["compilation"]["scheduler_timing"]["timing"]
Finally, you can visualize and save the timing:
from qiskit_ibm_runtime.visualization import draw_circuit_schedule_timing
circuit_schedule = (
result[0]
.metadata["compilation"]["scheduler_timing"]["timing"]
.split("\n")
)
fig = draw_circuit_schedule_timing(
circuit_schedule=circuit_schedule,
included_channels=None,
filter_readout_channels=False,
filter_barriers=False,
width=1000,
)
# Display the figure
fig.show(renderer="notebook")
Next steps
- Classical feedforward and control flow (dynamic circuits)
- [Visualize circuits](/docs/guides/Visualize circuits)