Skip to content

Commit

Permalink
Cleanup timeline and add schedule info to scheduled circuit.
Browse files Browse the repository at this point in the history
  • Loading branch information
nkanazawa1989 committed Apr 13, 2022
1 parent d9a6f0b commit a8ddcdf
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 388 deletions.
9 changes: 9 additions & 0 deletions qiskit/transpiler/basepasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,15 @@ def __call__(self, circuit, property_set=None):
result_circuit._clbit_write_latency = self.property_set["clbit_write_latency"]
if self.property_set["conditional_latency"] is not None:
result_circuit._conditional_latency = self.property_set["conditional_latency"]
if self.property_set["node_start_time"]:
# This is dictionary keyed on the DAGOpNode, which is invalidated once
# dag is converted into circuit. So this schedule information is
# also converted into list with the same ordering with circuit.data.
topological_start_times = []
start_times = self.property_set["node_start_time"]
for dag_node in result.topological_op_nodes():
topological_start_times.append(start_times[dag_node])
result_circuit._node_start_time = self.property_set["node_start_time"]

return result_circuit

Expand Down
10 changes: 10 additions & 0 deletions qiskit/transpiler/runningpassmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ def run(self, circuit, output_name=None, callback=None):
circuit._clbit_write_latency = self.property_set["clbit_write_latency"]
circuit._conditional_latency = self.property_set["conditional_latency"]

if self.property_set["node_start_time"]:
# This is dictionary keyed on the DAGOpNode, which is invalidated once
# dag is converted into circuit. So this schedule information is
# also converted into list with the same ordering with circuit.data.
topological_start_times = []
start_times = self.property_set["node_start_time"]
for dag_node in dag.topological_op_nodes():
topological_start_times.append(start_times[dag_node])
circuit._node_start_time = topological_start_times

return circuit

def _do_pass(self, pass_, dag, options):
Expand Down
72 changes: 39 additions & 33 deletions qiskit/visualization/timeline/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,14 @@

from copy import deepcopy
from functools import partial
from itertools import chain
from typing import Tuple, Iterator, Dict
from enum import Enum

import numpy as np

from qiskit import circuit
from qiskit.visualization.exceptions import VisualizationError
from qiskit.visualization.timeline import drawings, events, types
from qiskit.visualization.timeline import drawings, types
from qiskit.visualization.timeline.stylesheet import QiskitTimelineStyle


Expand Down Expand Up @@ -143,43 +142,50 @@ def load_program(self, program: circuit.QuantumCircuit):
Args:
program: Scheduled circuit object to draw.
"""
self.bits = program.qubits + program.clbits
stop_time = 0
not_gate_like = (circuit.Barrier,)

for t0, (inst, qargs, cargs) in zip(getattr(program, "_node_start_time"), program.data):
bits = qargs + cargs
for bit_pos, bit in enumerate(qargs + cargs):
if not isinstance(inst, not_gate_like):
# Generate draw object for gates
gate_source = types.ScheduledGate(
t0=t0,
operand=inst,
duration=inst.duration,
bits=bits,
bit_position=bit_pos,
)
for gen in self.generator["gates"]:
obj_generator = partial(gen, formatter=self.formatter)
for datum in obj_generator(gate_source):
self.add_data(datum)
if len(bits) > 1 and bit_pos == 0:
# Generate draw object for gate-gate link
line_pos = t0 + 0.5 * inst.duration
link_source = types.GateLink(t0=line_pos, opname=inst.name, bits=bits)
for gen in self.generator["gate_links"]:
obj_generator = partial(gen, formatter=self.formatter)
for datum in obj_generator(link_source):
self.add_data(datum)
if isinstance(inst, circuit.Barrier):
# Generate draw object for barrier
barrier_source = types.Barrier(t0=t0, bits=bits, bit_position=bit_pos)
for gen in self.generator["barriers"]:
obj_generator = partial(gen, formatter=self.formatter)
for datum in obj_generator(barrier_source):
self.add_data(datum)

self.bits = program.qubits + program.clbits
for bit in self.bits:
bit_events = events.BitEvents.load_program(scheduled_circuit=program, bit=bit)

# create objects associated with gates
for gen in self.generator["gates"]:
obj_generator = partial(gen, formatter=self.formatter)
draw_targets = [obj_generator(gate) for gate in bit_events.get_gates()]
for data in list(chain.from_iterable(draw_targets)):
self.add_data(data)

# create objects associated with gate links
for gen in self.generator["gate_links"]:
obj_generator = partial(gen, formatter=self.formatter)
draw_targets = [obj_generator(link) for link in bit_events.get_gate_links()]
for data in list(chain.from_iterable(draw_targets)):
self.add_data(data)

# create objects associated with barrier
for gen in self.generator["barriers"]:
obj_generator = partial(gen, formatter=self.formatter)
draw_targets = [obj_generator(barrier) for barrier in bit_events.get_barriers()]
for data in list(chain.from_iterable(draw_targets)):
self.add_data(data)

# create objects associated with bit
for gen in self.generator["bits"]:
# Generate draw objects for bit
obj_generator = partial(gen, formatter=self.formatter)
for data in obj_generator(bit):
self.add_data(data)

stop_time = max(stop_time, bit_events.stop_time)
for datum in obj_generator(bit):
self.add_data(datum)

# update time range
t_end = max(stop_time, self.formatter["margin.minimum_duration"])
t_end = max(program.duration, self.formatter["margin.minimum_duration"])
self.set_time_range(t_start=0, t_end=t_end)

def set_time_range(self, t_start: int, t_end: int):
Expand Down
121 changes: 0 additions & 121 deletions qiskit/visualization/timeline/events.py

This file was deleted.

5 changes: 5 additions & 0 deletions qiskit/visualization/timeline/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ def draw(
This feature enables you to control the most of appearance of the output image
without modifying the codebase of the scheduled circuit drawer.
"""
if not hasattr(program, "_node_start_time"):
raise VisualizationError(
f"Input circuit {program.name} is not scheduled. Run one of scheduling analysis passes."
)

# update stylesheet
temp_style = stylesheet.QiskitTimelineStyle()
temp_style.update(style or stylesheet.IQXStandard())
Expand Down
27 changes: 27 additions & 0 deletions releasenotes/notes/cleanup-timeline-drawer-a6287bdab4459e6e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
upgrade:
- |
A protected attribute :attr:`_node_start_time` is silently added to
:class:`~QuantumCircuit` when one of scheduling analysis passes is run on the circuit.
This information can be used to obtain circuit instruction with instruction time.
.. code-block:: python
from qiskit import QuantumCircuit, transpile
from qiskit.test.mock import FakeMontreal
backend = FakeMontreal()
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qct = transpile(
qc, backend, initial_layout=[0, 1], coupling_map=[[0, 1]], scheduling_method="alap"
)
scheduled_insts = list(zip(qct._node_start_time, qct.data))
fixes:
- |
Time misalignment bug of drawing classical register with :func:`~timeline_drawer`
has been fixed. Now classical register slots are drawn at correct position.
Loading

0 comments on commit a8ddcdf

Please sign in to comment.