From 3523aea7d38889ae8b508814e006ce8434b2673b Mon Sep 17 00:00:00 2001 From: Naoki Kanazawa Date: Thu, 20 Jun 2024 22:01:56 +0900 Subject: [PATCH] Fix v2 pulse drawer (#12608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix error when V2 model is set * Apply suggestions from code review * Fix black --------- Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> Co-authored-by: Elena Peña Tapia (cherry picked from commit 7d1731b60d8dd6219a292dc62f24d1a8d780e43a) --- qiskit/visualization/pulse_v2/device_info.py | 89 ++++++++++++------- .../fix-v2-pulse-drawer-d05e4e392766909f.yaml | 7 ++ 2 files changed, 65 insertions(+), 31 deletions(-) create mode 100644 releasenotes/notes/fix-v2-pulse-drawer-d05e4e392766909f.yaml diff --git a/qiskit/visualization/pulse_v2/device_info.py b/qiskit/visualization/pulse_v2/device_info.py index 1e809c43abd2..7898f978772c 100644 --- a/qiskit/visualization/pulse_v2/device_info.py +++ b/qiskit/visualization/pulse_v2/device_info.py @@ -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): @@ -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): + # 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 + 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 diff --git a/releasenotes/notes/fix-v2-pulse-drawer-d05e4e392766909f.yaml b/releasenotes/notes/fix-v2-pulse-drawer-d05e4e392766909f.yaml new file mode 100644 index 000000000000..b158703c6b89 --- /dev/null +++ b/releasenotes/notes/fix-v2-pulse-drawer-d05e4e392766909f.yaml @@ -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.