Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions qiskit/visualization/timeline/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,9 @@ def load_program(self, program: circuit.QuantumCircuit):
bit_position=bit_pos,
)
for gen in self.generator["gates"]:
obj_generator = partial(gen, formatter=self.formatter)
for datum in obj_generator(gate_source):
if getattr(gen, "accepts_program", False):
gen = partial(gen, program=program)
for datum in gen(gate_source, formatter=self.formatter):
self.add_data(datum)
if len(bits) > 1 and bit_pos == 0:
# Generate draw object for gate-gate link
Expand All @@ -197,23 +198,26 @@ def load_program(self, program: circuit.QuantumCircuit):
t0=line_pos, opname=instruction.operation.name, bits=bits
)
for gen in self.generator["gate_links"]:
obj_generator = partial(gen, formatter=self.formatter)
for datum in obj_generator(link_source):
if getattr(gen, "accepts_program", False):
gen = partial(gen, program=program)
for datum in gen(link_source, formatter=self.formatter):
self.add_data(datum)
if isinstance(instruction.operation, 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):
if getattr(gen, "accepts_program", False):
gen = partial(gen, program=program)
for datum in gen(barrier_source, formatter=self.formatter):
self.add_data(datum)

self.bits = list(program.qubits) + list(program.clbits)
for bit in self.bits:
for gen in self.generator["bits"]:
# Generate draw objects for bit
obj_generator = partial(gen, formatter=self.formatter)
for datum in obj_generator(bit):
if getattr(gen, "accepts_program", False):
gen = partial(gen, program=program)
for datum in gen(bit, formatter=self.formatter):
self.add_data(datum)

# update time range
Expand Down
65 changes: 49 additions & 16 deletions qiskit/visualization/timeline/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def my_object_generator(
# your code here: create and return drawings related to the gate object.
```

If a generator object has the attribute ``accepts_program`` set to ``True``, then the generator will
be called with an additional keyword argument ``program: QuantumCircuit``.

2. generator.bits

In this stylesheet entry the input data is `types.Bits` and generates timeline objects
Expand All @@ -53,6 +56,9 @@ def my_object_generator(
# your code here: create and return drawings related to the bit object.
```

If a generator object has the attribute ``accepts_program`` set to ``True``, then the generator will
be called with an additional keyword argument ``program: QuantumCircuit``.

3. generator.barriers

In this stylesheet entry the input data is `types.Barrier` and generates barrier objects
Expand All @@ -69,6 +75,9 @@ def my_object_generator(
# your code here: create and return drawings related to the barrier object.
```

If a generator object has the attribute ``accepts_program`` set to ``True``, then the generator will
be called with an additional keyword argument ``program: QuantumCircuit``.

4. generator.gate_links

In this stylesheet entry the input data is `types.GateLink` and generates barrier objects
Expand All @@ -85,15 +94,18 @@ def my_object_generator(
# your code here: create and return drawings related to the link object.
```

If a generator object has the attribute ``accepts_program`` set to ``True``, then the generator will
be called with an additional keyword argument ``program: QuantumCircuit``.

Arbitrary generator function satisfying the above format can be accepted.
Returned `ElementaryData` can be arbitrary subclasses that are implemented in
the plotter API.
"""

import warnings
from typing import List, Union, Dict, Any, Optional

from typing import List, Union, Dict, Any

from qiskit.circuit import Qubit, QuantumCircuit
from qiskit.circuit.exceptions import CircuitError
from qiskit.visualization.timeline import types, drawings

Expand Down Expand Up @@ -191,7 +203,7 @@ def gen_sched_gate(


def gen_full_gate_name(
gate: types.ScheduledGate, formatter: Dict[str, Any]
gate: types.ScheduledGate, formatter: Dict[str, Any], program: Optional[QuantumCircuit] = None
) -> List[drawings.TextData]:
"""Generate gate name.

Expand All @@ -204,6 +216,7 @@ def gen_full_gate_name(
Args:
gate: Gate information source.
formatter: Dictionary of stylesheet settings.
program: Optional program that the bits are a part of.

Returns:
List of `TextData` drawings.
Expand Down Expand Up @@ -232,12 +245,15 @@ def gen_full_gate_name(
label_latex = rf"{latex_name}"

# bit index
with warnings.catch_warnings():
warnings.simplefilter("ignore")
if len(gate.bits) > 1:
bits_str = ", ".join(map(str, [bit.index for bit in gate.bits]))
label_plain += f"[{bits_str}]"
label_latex += f"[{bits_str}]"
if len(gate.bits) > 1:
if program is None:
# This is horribly hacky and mostly meaningless, but there's no other distinguisher
# available to us if all we have is a `Bit` instance.
bits_str = ", ".join(str(id(bit))[-3:] for bit in gate.bits)
else:
bits_str = ", ".join(f"{program.find_bit(bit).index}" for bit in gate.bits)
label_plain += f"[{bits_str}]"
label_latex += f"[{bits_str}]"

# parameter list
params = []
Expand Down Expand Up @@ -276,6 +292,9 @@ def gen_full_gate_name(
return [drawing]


gen_full_gate_name.accepts_program = True


def gen_short_gate_name(
gate: types.ScheduledGate, formatter: Dict[str, Any]
) -> List[drawings.TextData]:
Expand Down Expand Up @@ -367,7 +386,11 @@ def gen_timeslot(bit: types.Bits, formatter: Dict[str, Any]) -> List[drawings.Bo
return [drawing]


def gen_bit_name(bit: types.Bits, formatter: Dict[str, Any]) -> List[drawings.TextData]:
def gen_bit_name(
bit: types.Bits,
formatter: Dict[str, Any],
program: Optional[QuantumCircuit] = None,
) -> List[drawings.TextData]:
"""Generate bit label.

Stylesheet:
Expand All @@ -376,6 +399,7 @@ def gen_bit_name(bit: types.Bits, formatter: Dict[str, Any]) -> List[drawings.Te
Args:
bit: Bit object associated to this drawing.
formatter: Dictionary of stylesheet settings.
program: Optional program that the bits are a part of.

Returns:
List of `TextData` drawings.
Expand All @@ -388,12 +412,18 @@ def gen_bit_name(bit: types.Bits, formatter: Dict[str, Any]) -> List[drawings.Te
"ha": "right",
}

with warnings.catch_warnings():
warnings.simplefilter("ignore")
label_plain = f"{bit.register.name}"
label_latex = r"{{\rm {register}}}_{{{index}}}".format(
register=bit.register.prefix, index=bit.index
)
if program is None:
warnings.warn("bits cannot be accurately named without passing a 'program'", stacklevel=2)
label_plain = "q" if isinstance(bit, Qubit) else "c"
label_latex = rf"{{\rm {label_plain}}}"
else:
loc = program.find_bit(bit)
if loc.registers:
label_plain = loc.registers[-1][0].name
label_latex = rf"{{\rm {loc.registers[-1][0].prefix}}}_{{{loc.registers[-1][1]}}}"
else:
label_plain = "q" if isinstance(bit, Qubit) else "c"
label_latex = rf"{{\rm {label_plain}}}_{{{loc.index}}}"

drawing = drawings.TextData(
data_type=types.LabelType.BIT_NAME,
Expand All @@ -408,6 +438,9 @@ def gen_bit_name(bit: types.Bits, formatter: Dict[str, Any]) -> List[drawings.Te
return [drawing]


gen_bit_name.accepts_program = True


def gen_barrier(barrier: types.Barrier, formatter: Dict[str, Any]) -> List[drawings.LineData]:
"""Generate barrier line.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
When defining a custom stylesheet for the pulse timeline drawer :func:`qiskit.visualization.timeline_drawer`,
"generator" functions that have the object attribute ``accepts_program`` set to ``True`` will
receive an extra keyword argument ``program`` containing the full scheduled
:class:`.QuantumCircuit` being drawn.
9 changes: 7 additions & 2 deletions test/python/visualization/timeline/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ def setUp(self) -> None:
"""Setup."""
super().setUp()

self.qubit = list(qiskit.QuantumRegister(1, "bar"))[0]
self.program = qiskit.QuantumCircuit(qiskit.QuantumRegister(1, "bar"))
self.program._op_start_times = []
self.qubit = self.program.qubits[0]

style = stylesheet.QiskitTimelineStyle()
self.formatter = style.formatter
Expand Down Expand Up @@ -238,7 +240,10 @@ def test_gen_timeslot(self):

def test_gen_bit_name(self):
"""Test gen_bit_name generator."""
drawing_obj = generators.gen_bit_name(self.qubit, self.formatter)[0]
with self.assertWarnsRegex(UserWarning, "bits cannot be accurately named"):
generators.gen_bit_name(self.qubit, self.formatter)

drawing_obj = generators.gen_bit_name(self.qubit, self.formatter, program=self.program)[0]

self.assertEqual(drawing_obj.data_type, str(types.LabelType.BIT_NAME.value))
self.assertListEqual(list(drawing_obj.xvals), [types.AbstractCoordinate.LEFT])
Expand Down