-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Bug fix macros.measure with backendv2 #9987
Changes from 23 commits
4bab6a6
9d109d0
a40211c
25d0719
11f734b
ca3f032
2d2ff79
0d4b715
d223f4f
d29025f
68ec213
d19464b
5930369
e53f226
c1a51d5
f31ae23
3926200
4029849
381a8e6
b075cb3
df4c4c7
d4ff655
952f865
f487cfe
9fe1944
f35ed36
25f2ac9
acb0c93
9f973ea
c2bed42
f5fb919
9986e7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -11,12 +11,18 @@ | |||||
# that they have been altered from the originals. | ||||||
|
||||||
"""Module for common pulse programming macros.""" | ||||||
from __future__ import annotations | ||||||
|
||||||
from typing import Dict, List, Optional, Union | ||||||
from typing import Dict, List, Optional, Union, TYPE_CHECKING | ||||||
|
||||||
from qiskit.pulse import channels, exceptions, instructions, utils | ||||||
from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap | ||||||
from qiskit.pulse.schedule import Schedule | ||||||
from qiskit.utils.deprecation import deprecate_arg | ||||||
|
||||||
|
||||||
if TYPE_CHECKING: | ||||||
from qiskit.transpiler import Target | ||||||
|
||||||
|
||||||
def measure( | ||||||
|
@@ -30,6 +36,13 @@ def measure( | |||||
"""Return a schedule which measures the requested qubits according to the given | ||||||
instruction mapping and measure map, or by using the defaults provided by the backend. | ||||||
|
||||||
.. note:: | ||||||
This function internally dispatches schedule generation logic depending on input backend model. | ||||||
For the :class:`.BackendV1`, it considers conventional :class:`.InstructionScheduleMap` | ||||||
and utilizes the backend calibration defined for a group of qubits in the `meas_map`. | ||||||
For the :class:`.BackendV2`, it assembles calibrations of single qubit measurement | ||||||
defined in the backend target to build a composite measurement schedule for `qubits`. | ||||||
|
||||||
By default, the measurement results for each qubit are trivially mapped to the qubit | ||||||
index. This behavior is overridden by qubit_mem_slots. For instance, to measure | ||||||
qubit 0 into MemorySlot(1), qubit_mem_slots can be provided as {0: 1}. | ||||||
|
@@ -47,18 +60,67 @@ def measure( | |||||
|
||||||
Returns: | ||||||
A measurement schedule corresponding to the inputs provided. | ||||||
""" | ||||||
|
||||||
# backend is V2. | ||||||
if hasattr(backend, "target"): | ||||||
try: | ||||||
meas_map = backend.configuration().meas_map | ||||||
except AttributeError: | ||||||
# TODO add meas_map to Target in 0.25 | ||||||
meas_map = [list(range(backend.num_qubits))] | ||||||
|
||||||
return _measure_v2( | ||||||
qubits=qubits, | ||||||
target=backend.target, | ||||||
meas_map=meas_map, | ||||||
qubit_mem_slots=qubit_mem_slots or dict(zip(qubits, range(len(qubits)))), | ||||||
measure_name=measure_name, | ||||||
) | ||||||
# backend is V1 or backend is None. | ||||||
else: | ||||||
try: | ||||||
return _measure_v1( | ||||||
qubits=qubits, | ||||||
inst_map=inst_map or backend.defaults().instruction_schedule_map, | ||||||
meas_map=meas_map or backend.configuration().meas_map, | ||||||
qubit_mem_slots=qubit_mem_slots, | ||||||
measure_name=measure_name, | ||||||
) | ||||||
except AttributeError as ex: | ||||||
raise exceptions.PulseError( | ||||||
"inst_map or meas_map, and backend cannot be None simultaneously" | ||||||
) from ex | ||||||
|
||||||
|
||||||
def _measure_v1( | ||||||
qubits: List[int], | ||||||
inst_map: InstructionScheduleMap, | ||||||
meas_map: Union[List[List[int]], Dict[int, List[int]]], | ||||||
qubit_mem_slots: Optional[Dict[int, int]] = None, | ||||||
measure_name: str = "measure", | ||||||
) -> Schedule: | ||||||
"""Return a schedule which measures the requested qubits according to the given | ||||||
instruction mapping and measure map, or by using the defaults provided by the backendV1. | ||||||
|
||||||
Args: | ||||||
qubits: List of qubits to be measured. | ||||||
backend (Union[Backend, BaseBackend]): A backend instance, which contains | ||||||
hardware-specific data required for scheduling. | ||||||
inst_map: Mapping of circuit operations to pulse schedules. If None, defaults to the | ||||||
``instruction_schedule_map`` of ``backend``. | ||||||
meas_map: List of sets of qubits that must be measured together. If None, defaults to | ||||||
the ``meas_map`` of ``backend``. | ||||||
qubit_mem_slots: Mapping of measured qubit index to classical bit index. | ||||||
measure_name: Name of the measurement schedule. | ||||||
Returns: | ||||||
A measurement schedule corresponding to the inputs provided. | ||||||
Raises: | ||||||
PulseError: If both ``inst_map`` or ``meas_map``, and ``backend`` is None. | ||||||
""" | ||||||
|
||||||
schedule = Schedule(name=f"Default measurement schedule for qubits {qubits}") | ||||||
try: | ||||||
inst_map = inst_map or backend.defaults().instruction_schedule_map | ||||||
meas_map = meas_map or backend.configuration().meas_map | ||||||
except AttributeError as ex: | ||||||
raise exceptions.PulseError( | ||||||
"inst_map or meas_map, and backend cannot be None simultaneously" | ||||||
) from ex | ||||||
|
||||||
if isinstance(meas_map, list): | ||||||
meas_map = utils.format_meas_map(meas_map) | ||||||
|
||||||
|
@@ -92,6 +154,61 @@ def measure( | |||||
return schedule | ||||||
|
||||||
|
||||||
def _measure_v2( | ||||||
qubits: List[int], | ||||||
target: Target, | ||||||
meas_map: Union[List[List[int]], Dict[int, List[int]]], | ||||||
qubit_mem_slots: Dict[int, int], | ||||||
measure_name: str = "measure", | ||||||
) -> Schedule: | ||||||
"""Return a schedule which measures the requested qubits according to the given | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💯 |
||||||
target and measure map, or by using the defaults provided by the backendV2. | ||||||
|
||||||
Args: | ||||||
qubits: List of qubits to be measured. | ||||||
target: The :class:`~.Target` representing the target backend. | ||||||
meas_map: List of sets of qubits that must be measured together. | ||||||
qubit_mem_slots: Mapping of measured qubit index to classical bit index. | ||||||
measure_name: Name of the measurement schedule. | ||||||
|
||||||
Returns: | ||||||
A measurement schedule corresponding to the inputs provided. | ||||||
""" | ||||||
schedule = Schedule(name=f"Default measurement schedule for qubits {qubits}") | ||||||
|
||||||
if isinstance(meas_map, list): | ||||||
meas_map = utils.format_meas_map(meas_map) | ||||||
meas_group = set() | ||||||
for qubit in qubits: | ||||||
meas_group |= set(meas_map[qubit]) | ||||||
meas_group = sorted(list(meas_group)) | ||||||
|
||||||
for measure_qubit in meas_group: | ||||||
try: | ||||||
if measure_qubit in qubits: | ||||||
default_sched = target.get_calibration(measure_name, (measure_qubit,)).filter( | ||||||
channels=[ | ||||||
channels.MeasureChannel(measure_qubit), | ||||||
channels.AcquireChannel(measure_qubit), | ||||||
] | ||||||
) | ||||||
else: | ||||||
default_sched = target.get_calibration(measure_name, (measure_qubit,)).filter( | ||||||
channels=[ | ||||||
channels.AcquireChannel(measure_qubit), | ||||||
] | ||||||
) | ||||||
except KeyError as ex: | ||||||
raise exceptions.PulseError( | ||||||
"We could not find a default measurement schedule called '{}'. " | ||||||
"Please provide another name using the 'measure_name' keyword " | ||||||
"argument. For assistance, the instructions which are defined are: " | ||||||
"{}".format(measure_name, target.instructions) | ||||||
) from ex | ||||||
schedule += _schedule_remapping_memory_slot(default_sched, qubit_mem_slots) | ||||||
return schedule | ||||||
|
||||||
|
||||||
def measure_all(backend) -> Schedule: | ||||||
""" | ||||||
Return a Schedule which measures all qubits of the given backend. | ||||||
|
@@ -104,3 +221,35 @@ def measure_all(backend) -> Schedule: | |||||
A schedule corresponding to the inputs provided. | ||||||
""" | ||||||
return measure(qubits=list(range(backend.configuration().n_qubits)), backend=backend) | ||||||
|
||||||
|
||||||
def _schedule_remapping_memory_slot( | ||||||
schedule: Schedule, qubit_mem_slots: Dict[int, int] | ||||||
) -> Schedule: | ||||||
""" | ||||||
A helper function to overwrite MemorySlot index of :class:`.Acquire` instruction. | ||||||
|
||||||
Args: | ||||||
schedule: A measurement schedule. | ||||||
qubit_mem_slots: Mapping of measured qubit index to classical bit index. | ||||||
|
||||||
Returns: | ||||||
A measurement schedule with new memory slot index. | ||||||
""" | ||||||
new_schedule = Schedule() | ||||||
for t0, inst in schedule.instructions: | ||||||
if isinstance(inst, instructions.Acquire): | ||||||
qubit_index = inst.channel.index | ||||||
reg_index = qubit_mem_slots.get(qubit_index, 0) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The unittest you wrote made me think this should be
Suggested change
because all acquisition instruction might store results in the slot0 when you specify |
||||||
new_schedule.insert( | ||||||
t0, | ||||||
instructions.Acquire( | ||||||
inst.duration, | ||||||
channels.AcquireChannel(qubit_index), | ||||||
mem_slot=channels.MemorySlot(reg_index), | ||||||
), | ||||||
inplace=True, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💯 |
||||||
) | ||||||
else: | ||||||
new_schedule.insert(t0, inst, inplace=True) | ||||||
return new_schedule |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -280,6 +280,7 @@ def __init__( | |
matches the qubit number the properties are defined for. If some | ||
qubits don't have properties available you can set that entry to | ||
``None`` | ||
meas_map (list): List of sets of qubits that must be measured together. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please revert this file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed it at 3926200. |
||
Raises: | ||
ValueError: If both ``num_qubits`` and ``qubit_properties`` are both | ||
defined and the value of ``num_qubits`` differs from the length of | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed it at 3926200.