Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix v2 pulse drawer #12608

Merged
merged 3 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
89 changes: 58 additions & 31 deletions qiskit/visualization/pulse_v2/device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class :py:class:``DrawerBackendInfo`` with necessary methods to generate drawing

from qiskit import pulse
from qiskit.providers import BackendConfigurationError
from qiskit.providers.backend import Backend
from qiskit.providers.backend import Backend, BackendV2


class DrawerBackendInfo(ABC):
Expand Down Expand Up @@ -106,40 +106,67 @@ def create_from_backend(cls, backend: Backend):
Returns:
OpenPulseBackendInfo: New configured instance.
"""
configuration = backend.configuration()
defaults = backend.defaults()

# load name
name = backend.name()

# load cycle time
dt = configuration.dt

# load frequencies
chan_freqs = {}

chan_freqs.update(
{pulse.DriveChannel(qind): freq for qind, freq in enumerate(defaults.qubit_freq_est)}
)
chan_freqs.update(
{pulse.MeasureChannel(qind): freq for qind, freq in enumerate(defaults.meas_freq_est)}
)
for qind, u_lo_mappers in enumerate(configuration.u_channel_lo):
temp_val = 0.0 + 0.0j
for u_lo_mapper in u_lo_mappers:
temp_val += defaults.qubit_freq_est[u_lo_mapper.q] * u_lo_mapper.scale
chan_freqs[pulse.ControlChannel(qind)] = temp_val.real

# load qubit channel mapping
qubit_channel_map = defaultdict(list)
for qind in range(configuration.n_qubits):
qubit_channel_map[qind].append(configuration.drive(qubit=qind))
qubit_channel_map[qind].append(configuration.measure(qubit=qind))
for tind in range(configuration.n_qubits):

if hasattr(backend, "configuration") and hasattr(backend, "defaults"):
configuration = backend.configuration()
defaults = backend.defaults()

name = configuration.backend_name
dt = configuration.dt

# load frequencies
chan_freqs.update(
{
pulse.DriveChannel(qind): freq
for qind, freq in enumerate(defaults.qubit_freq_est)
}
)
chan_freqs.update(
{
pulse.MeasureChannel(qind): freq
for qind, freq in enumerate(defaults.meas_freq_est)
}
)
for qind, u_lo_mappers in enumerate(configuration.u_channel_lo):
temp_val = 0.0 + 0.0j
for u_lo_mapper in u_lo_mappers:
temp_val += defaults.qubit_freq_est[u_lo_mapper.q] * u_lo_mapper.scale
chan_freqs[pulse.ControlChannel(qind)] = temp_val.real

# load qubit channel mapping
for qind in range(configuration.n_qubits):
qubit_channel_map[qind].append(configuration.drive(qubit=qind))
qubit_channel_map[qind].append(configuration.measure(qubit=qind))
for tind in range(configuration.n_qubits):
try:
qubit_channel_map[qind].extend(configuration.control(qubits=(qind, tind)))
except BackendConfigurationError:
pass
elif isinstance(backend, BackendV2):
Copy link
Contributor

@ElePT ElePT Jun 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lint is complaining about this being an elif instead of an else. I think it is reasonable to assume that the default is BackendV2 and not do the instance check

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other thing we sometimes do is to be more defensive and do the check, in order to raise an explicit RuntimeError("backend object not yet supported") for a clearer message - we don't have a BackendV3, but it's not inconceivable that we will expand in the future and have this same bug again.

# Pure V2 model doesn't contain channel frequency information.
name = backend.name
dt = backend.dt

# load qubit channel mapping
for qind in range(backend.num_qubits):
# channels are NotImplemented by default so we must catch arbitrary error.
try:
qubit_channel_map[qind].append(backend.drive_channel(qind))
except Exception: # pylint: disable=broad-except
pass
try:
qubit_channel_map[qind].extend(configuration.control(qubits=(qind, tind)))
except BackendConfigurationError:
qubit_channel_map[qind].append(backend.measure_channel(qind))
except Exception: # pylint: disable=broad-except
pass
for tind in range(backend.num_qubits):
try:
qubit_channel_map[qind].extend(backend.control_channel(qubits=(qind, tind)))
except Exception: # pylint: disable=broad-except
pass
ElePT marked this conversation as resolved.
Show resolved Hide resolved
else:
raise RuntimeError("Backend object not yet supported")

return OpenPulseBackendInfo(
name=name, dt=dt, channel_frequency_map=chan_freqs, qubit_channel_map=qubit_channel_map
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/fix-v2-pulse-drawer-d05e4e392766909f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed a bug in :func:`qiskit.visualization.pulse_v2.interface.draw` that didn't
draw pulse schedules when the draw function was called with a :class:`.BackendV2` argument.
Because the V2 backend doesn't report hardware channel frequencies,
the generated drawing will show 'no freq.' below each channel label.
Loading