diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index 0ea5c4b739bd..2df00258b4c8 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -17,8 +17,8 @@ from typing import Any, Dict, List, Tuple from qiskit.exceptions import QiskitError -from qiskit.pulse import Schedule, Delay -from qiskit.pulse.commands import (Command, PulseInstruction, Acquire, AcquireInstruction, +from qiskit.pulse import Schedule, Delay, Acquire +from qiskit.pulse.commands import (Command, PulseInstruction, AcquireInstruction, DelayInstruction, SamplePulse, ParametricInstruction) from qiskit.qobj import (PulseQobj, QobjHeader, QobjExperimentHeader, PulseQobjInstruction, PulseQobjExperimentConfig, @@ -189,7 +189,7 @@ def _assemble_instructions( # add samples to pulse library user_pulselib[name] = instruction.command - if isinstance(instruction, AcquireInstruction): + if isinstance(instruction, (AcquireInstruction, Acquire)): max_memory_slot = max(max_memory_slot, *[slot.index for slot in instruction.mem_slots]) # Acquires have a single AcquireChannel per inst, but we have to bundle them diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index f2b368eb6c3f..63fcb2d6c33f 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -133,15 +133,13 @@ from .channels import (DriveChannel, MeasureChannel, AcquireChannel, ControlChannel, RegisterSlot, MemorySlot) from .cmd_def import CmdDef -from .commands import (Acquire, AcquireInstruction, FrameChange, - PersistentValue, SamplePulse, Kernel, - Discriminator, ParametricPulse, +from .commands import (AcquireInstruction, FrameChange, + PersistentValue, SamplePulse, ParametricPulse, ParametricInstruction, Gaussian, GaussianSquare, Drag, ConstantPulse, functional_pulse) -from .configuration import LoConfig, LoRange +from .configuration import LoConfig, LoRange, Kernel, Discriminator from .exceptions import PulseError from .instruction_schedule_map import InstructionScheduleMap - -from .instructions import Instruction, Delay, ShiftPhase, Snapshot, SetFrequency +from .instructions import Acquire, Instruction, Delay, ShiftPhase, Snapshot, SetFrequency from .interfaces import ScheduleComponent from .schedule import Schedule diff --git a/qiskit/pulse/commands/acquire.py b/qiskit/pulse/commands/acquire.py index 3aebf1618611..82c44d457bdf 100644 --- a/qiskit/pulse/commands/acquire.py +++ b/qiskit/pulse/commands/acquire.py @@ -12,102 +12,22 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Acquire. -""" +"""Acquire. Deprecated path.""" import warnings + from typing import Optional, Union, List -from qiskit.pulse.exceptions import PulseError from ..channels import MemorySlot, RegisterSlot, AcquireChannel -from ..instructions import Instruction -from .meas_opts import Discriminator, Kernel -from .command import Command - - -class Acquire(Command): - """Acquire.""" - - ALIAS = 'acquire' - prefix = 'acq' - - def __init__(self, duration: int, kernel: Optional[Kernel] = None, - discriminator: Optional[Discriminator] = None, - name: Optional[str] = None): - """Create new acquire command. - - Args: - duration: Duration of acquisition - kernel: The data structures defining the measurement kernels - to be used (from the list of available kernels) and set of parameters - (if applicable) if the measurement level is 1 or 2. - discriminator: Discriminators to be used (from the list of available discriminator) - if the measurement level is 2 - name: Name of this command. +from ..exceptions import PulseError - Raises: - PulseError: when invalid discriminator or kernel object is input. - """ - super().__init__(duration=duration) +# pylint: disable=unused-import - self._name = Acquire.create_name(name) - - if kernel and not isinstance(kernel, Kernel): - raise PulseError('Invalid kernel object is specified.') - self._kernel = kernel - - if discriminator and not isinstance(discriminator, Discriminator): - raise PulseError('Invalid discriminator object is specified.') - self._discriminator = discriminator - - @property - def kernel(self): - """Return kernel settings.""" - return self._kernel - - @property - def discriminator(self): - """Return discrimination settings.""" - return self._discriminator - - def __eq__(self, other: 'Acquire'): - """Two Acquires are the same if they are of the same type - and have the same kernel and discriminator. - - Args: - other: Other Acquire - - Returns: - bool: are self and other equal. - """ - return (super().__eq__(other) and - self.kernel == other.kernel and - self.discriminator == other.discriminator) - - def __hash__(self): - return hash((super().__hash__(), self.kernel, self.discriminator)) - - def __repr__(self): - return '%s(duration=%d, kernel=%s, discriminator=%s, name="%s")' % \ - (self.__class__.__name__, self.duration, self.name, - self.kernel, self.discriminator) - - # pylint: disable=arguments-differ - def to_instruction(self, - qubit: Union[AcquireChannel, List[AcquireChannel]], - mem_slot: Optional[Union[MemorySlot, List[MemorySlot]]] = None, - reg_slots: Optional[Union[RegisterSlot, List[RegisterSlot]]] = None, - mem_slots: Optional[Union[List[MemorySlot]]] = None, - reg_slot: Optional[RegisterSlot] = None, - name: Optional[str] = None) -> 'AcquireInstruction': - - return AcquireInstruction(self, qubit, mem_slot=mem_slot, reg_slot=reg_slot, - mem_slots=mem_slots, reg_slots=reg_slots, name=name) - # pylint: enable=arguments-differ +from ..instructions import Acquire +from ..instructions import Instruction class AcquireInstruction(Instruction): - """Pulse to acquire measurement result.""" + """Deprecated.""" def __init__(self, command: Acquire, @@ -118,6 +38,12 @@ def __init__(self, reg_slot: Optional[RegisterSlot] = None, name: Optional[str] = None): + warnings.warn("The ``AcquireInstruction`` has been deprecated. Please use Acquire with " + "channels instead. For example, AcquireInstruction(Acquire(duration), " + "AcquireChannel(0), MemorySlot(0)) becomes Acquire(duration, " + "AcquireChannel(0), MemorySlot(0)).", + DeprecationWarning) + if isinstance(acquire, list) or isinstance(mem_slot, list) or reg_slots: warnings.warn('The AcquireInstruction on multiple qubits, multiple ' 'memory slots and multiple reg slots is deprecated. The ' diff --git a/qiskit/pulse/commands/meas_opts.py b/qiskit/pulse/commands/meas_opts.py index b80e3a12b37e..d684a38648b3 100644 --- a/qiskit/pulse/commands/meas_opts.py +++ b/qiskit/pulse/commands/meas_opts.py @@ -12,77 +12,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=missing-param-doc,useless-super-delegation +"""Measurement options. Deprecated path.""" -""" -Measurement options. -""" +# pylint: disable=unused-import -from typing import Optional - -class MeasOpts: - """Measurement options.""" - - def __init__(self, name: Optional[str] = None, **params): - """Create new measurement options. - - Parameters: - name: Name of measurement option to be used. - """ - self._name = name - self._params = params - - @property - def name(self): - """Return parameter name.""" - return self._name - - @property - def params(self): - """Return parameter dict.""" - return self._params - - def __eq__(self, other: 'MeasOpts'): - """Two measurement options are the same if they are of the same type - and have the same name and params. - - Args: - other: Other Discriminator/Kernel - - Returns: - bool: are self and other equal - """ - return (type(self) is type(other) and - self.name == other.name and - self.params == other.params) - - def __hash__(self): - return hash((super().__hash__(), self.name, self.params)) - - def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, self.name) - - -class Discriminator(MeasOpts): - """Discriminator.""" - - def __init__(self, name: Optional[str] = None, **params): - """Create new discriminator. - - Parameters: - name: Name of discriminator to be used - """ - super().__init__(name, **params) - - -class Kernel(MeasOpts): - """Kernel.""" - - def __init__(self, name: Optional[str] = None, **params): - """Create new kernel. - - Parameters: - name: Name of kernel to be used - """ - super().__init__(name, **params) +from qiskit.pulse.configuration import Kernel, Discriminator diff --git a/qiskit/pulse/configuration.py b/qiskit/pulse/configuration.py index b3e02bab59a3..9f33d814b119 100644 --- a/qiskit/pulse/configuration.py +++ b/qiskit/pulse/configuration.py @@ -21,6 +21,48 @@ from .exceptions import PulseError +class Kernel: + """Settings for this Kernel, which is responsible for integrating time series (raw) data + into IQ points. + """ + + def __init__(self, name: Optional[str] = None, **params): + """Create new kernel. + + Args: + name: Name of kernel to be used + params: Any settings for kerneling. + """ + self.name = name + self.params = params + + def __repr__(self): + return "{}({}{})".format(self.__class__.__name__, + "'" + self.name + "', " or "", + ', '.join("{}={}".format(str(k), str(v)) for k, v in self.params)) + + +class Discriminator: + """Setting for this Discriminator, which is responsible for classifying kerneled IQ points + into 0/1 state results. + """ + + def __init__(self, name: Optional[str] = None, **params): + """Create new discriminator. + + Args: + name: Name of discriminator to be used + params: Any settings for discrimination. + """ + self.name = name + self.params = params + + def __repr__(self): + return "{}({}{})".format(self.__class__.__name__, + "'" + self.name + "', " or "", + ', '.join("{}={}".format(str(k), str(v)) for k, v in self.params)) + + class LoRange: """Range of LO frequency.""" diff --git a/qiskit/pulse/instructions/__init__.py b/qiskit/pulse/instructions/__init__.py index 2aa7859cb022..f3677233ddce 100644 --- a/qiskit/pulse/instructions/__init__.py +++ b/qiskit/pulse/instructions/__init__.py @@ -29,6 +29,7 @@ An instruction can be added to a :py:class:`~qiskit.pulse.Schedule`, which is a sequence of scheduled Pulse ``Instruction`` s over many channels. """ +from .acquire import Acquire from .delay import Delay from .instruction import Instruction from .frequency import SetFrequency diff --git a/qiskit/pulse/instructions/acquire.py b/qiskit/pulse/instructions/acquire.py new file mode 100644 index 000000000000..a85ec243a851 --- /dev/null +++ b/qiskit/pulse/instructions/acquire.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The Acquire instruction is used to trigger the qubit measurement unit and provide +some metadata for the acquisition process; for example, where to store classified readout data. +""" +import warnings + +from typing import List, Optional, Union + +from ..channels import MemorySlot, RegisterSlot, AcquireChannel +from ..configuration import Kernel, Discriminator +from ..exceptions import PulseError +from .instruction import Instruction + + +class Acquire(Instruction): + """The Acquire instruction is used to trigger the ADC associated with a particular qubit; + e.g. instantiated with AcquireChannel(0), the Acquire command will trigger data collection + for the channel associated with qubit 0 readout. This instruction also provides acquisition + metadata: + + * the number of cycles during which to acquire (in terms of dt), + + * the register slot to store classified, intermediary readout results, + + * the memory slot to return classified results, + + * the kernel to integrate raw data for each shot, and + + * the discriminator to classify kerneled IQ points. + """ + + def __init__(self, + duration: int, + channel: Optional[Union[AcquireChannel, List[AcquireChannel]]] = None, + mem_slot: Optional[Union[MemorySlot, List[MemorySlot]]] = None, + reg_slots: Optional[Union[RegisterSlot, List[RegisterSlot]]] = None, + mem_slots: Optional[Union[List[MemorySlot]]] = None, + reg_slot: Optional[RegisterSlot] = None, + kernel: Optional[Kernel] = None, + discriminator: Optional[Discriminator] = None, + name: Optional[str] = None): + """Create a new Acquire instruction. + + Args: + duration: Length of time to acquire data in terms of dt. + channel: The channel that will acquire data. + mem_slot: The classical memory slot in which to store the classified readout result. + mem_slots: Deprecated list form of ``mem_slot``. + reg_slots: Deprecated list form of ``reg_slot``. + reg_slot: The fast-access register slot in which to store the classified readout + result for fast feedback. + kernel: A ``Kernel`` for integrating raw data. + discriminator: A ``Discriminator`` for discriminating kerneled IQ data into 0/1 + results. + name: Name of the instruction for display purposes. + + Raises: + PulseError: If channels are supplied, and the number of register and/or memory slots + does not equal the number of channels. + """ + if isinstance(channel, list) or isinstance(mem_slot, list) or reg_slots or mem_slots: + warnings.warn('The AcquireInstruction on multiple qubits, multiple ' + 'memory slots and multiple reg slots is deprecated. The ' + 'parameter "mem_slots" has been replaced by "mem_slot" and ' + '"reg_slots" has been replaced by "reg_slot"', DeprecationWarning, 3) + + if not isinstance(channel, list): + channels = [channel] if channel else None + else: + channels = channel + + if mem_slot and not isinstance(mem_slot, list): + mem_slot = [mem_slot] + elif mem_slots: + mem_slot = mem_slots + + if reg_slot: + reg_slot = [reg_slot] + elif reg_slots and not isinstance(reg_slots, list): + reg_slot = [reg_slots] + else: + reg_slot = reg_slots + + if channels and not (mem_slot or reg_slot): + raise PulseError('Neither MemorySlots nor RegisterSlots were supplied.') + + if channels and mem_slot and len(channels) != len(mem_slot): + raise PulseError("The number of mem_slots must be equal to the number of channels.") + + if channels and reg_slot: + if len(channels) != len(reg_slot): + raise PulseError("The number of reg_slots must be equal to the number of " + "channels.") + else: + reg_slot = [] + + if name is None and channels is None: + name = 'acq{:10x}'.format(hash((duration, kernel, discriminator))) + elif name is None: + name = 'acq{:10x}'.format(hash((duration, tuple(channels), tuple(mem_slot), + tuple(reg_slot), kernel, discriminator))) + + if channels is not None: + super().__init__(duration, *channels, *mem_slot, *reg_slot, name=name) + else: + super().__init__(duration, name=name) + + self._acquires = channels + self._channel = channels[0] if channels else None + self._mem_slots = mem_slot + self._reg_slots = reg_slot + self._kernel = kernel + self._discriminator = discriminator + + @property + def operands(self) -> List: + """Return a list of instruction operands.""" + return [self.duration, self.channel, + self.mem_slot, self.reg_slot] + + @property + def channel(self) -> AcquireChannel: + """Return the :py:class:`~qiskit.pulse.channels.Channel` that this instruction is + scheduled on. + """ + return self._channel + + @property + def kernel(self) -> Kernel: + """Return kernel settings.""" + return self._kernel + + @property + def discriminator(self) -> Discriminator: + """Return discrimination settings.""" + return self._discriminator + + @property + def acquire(self) -> AcquireChannel: + """Acquire channel to acquire data. The ``AcquireChannel`` index maps trivially to + qubit index. + """ + return self._acquires[0] if self._acquires else None + + @property + def mem_slot(self) -> MemorySlot: + """The classical memory slot which will store the classified readout result.""" + return self._mem_slots[0] if self._mem_slots else None + + @property + def reg_slot(self) -> RegisterSlot: + """The fast-access register slot which will store the classified readout result for + fast-feedback computation. + """ + return self._reg_slots[0] if self._reg_slots else None + + @property + def acquires(self) -> List[AcquireChannel]: + """Acquire channels to be acquired on.""" + return self._acquires + + @property + def mem_slots(self) -> List[MemorySlot]: + """MemorySlots.""" + return self._mem_slots + + @property + def reg_slots(self) -> List[RegisterSlot]: + """RegisterSlots.""" + return self._reg_slots + + def __repr__(self): + return "{}({}{}{}{}{}{})".format( + self.__class__.__name__, + self.duration, + ', ' + ', '.join(str(ch) for ch in self.acquires) if self.acquires else '', + ', ' + str(self.mem_slot) if self.mem_slot else '', + ', ' + str(self.reg_slot) if self.reg_slot else '', + ', ' + str(self.kernel) if self.kernel else '', + ', ' + str(self.discriminator) if self.discriminator else '') + + def __eq__(self, other): + return isinstance(other, type(self)) and self.operands == other.operands + + def __call__(self, + channel: Optional[Union[AcquireChannel, List[AcquireChannel]]] = None, + mem_slot: Optional[Union[MemorySlot, List[MemorySlot]]] = None, + reg_slots: Optional[Union[RegisterSlot, List[RegisterSlot]]] = None, + mem_slots: Optional[Union[List[MemorySlot]]] = None, + reg_slot: Optional[RegisterSlot] = None, + kernel: Optional[Kernel] = None, + discriminator: Optional[Discriminator] = None, + name: Optional[str] = None) -> 'Acquire': + """Return new ``Acquire`` that is fully instantiated with its channels. + + Args: + channel: The channel that will acquire data. + mem_slot: The classical memory slot in which to store the classified readout result. + mem_slots: Deprecated list form of ``mem_slot``. + reg_slots: Deprecated list form of ``reg_slot``. + reg_slot: The fast-access register slot in which to store the classified readout + result for fast feedback. + kernel: A ``Kernel`` for integrating raw data. + discriminator: A ``Discriminator`` for discriminating kerneled IQ data into 0/1 + results. + name: Name of the instruction for display purposes. + + Return: + Complete and ready to schedule ``Acquire``. + + Raises: + PulseError: If ``channel`` has already been set. + """ + warnings.warn("Calling Acquire with a channel is deprecated. Instantiate the acquire with " + "a channel instead.", DeprecationWarning) + if self._channel is not None: + raise PulseError("The channel has already been assigned as {}.".format(self.channel)) + return Acquire(self.duration, + channel=channel, + mem_slot=mem_slot, + reg_slots=reg_slots, + mem_slots=mem_slots, + reg_slot=reg_slot, + kernel=kernel, + discriminator=discriminator, + name=name if name is not None else self.name) diff --git a/qiskit/pulse/reschedule.py b/qiskit/pulse/reschedule.py index bb03673ee413..de8b563b3642 100644 --- a/qiskit/pulse/reschedule.py +++ b/qiskit/pulse/reschedule.py @@ -70,7 +70,7 @@ def calculate_align_time(): for schedule in schedules: last_acquire = 0 acquire_times = [time for time, inst in schedule.instructions - if isinstance(inst, AcquireInstruction)] + if isinstance(inst, (Acquire, AcquireInstruction))] if acquire_times: last_acquire = max(acquire_times) align_time = max(align_time, last_acquire) @@ -108,7 +108,7 @@ def get_max_calibration_duration(): raise PulseError("Pulse encountered on channel {0} after acquire on " "same channel.".format(chan.index)) - if isinstance(inst, AcquireInstruction): + if isinstance(inst, (Acquire, AcquireInstruction)): if time > align_time: warnings.warn("You provided an align_time which is scheduling an acquire " "sooner than it was scheduled for in the original Schedule.") @@ -143,12 +143,11 @@ def add_implicit_acquires(schedule: ScheduleComponent, meas_map: List[List[int]] acquire_map = dict() for time, inst in schedule.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, (Acquire, AcquireInstruction)): if any([acq.index != mem.index for acq, mem in zip(inst.acquires, inst.mem_slots)]): warnings.warn("One of your acquires was mapped to a memory slot which didn't match" " the qubit index. I'm relabeling them to match.") - cmd = Acquire(inst.duration, inst.command.discriminator, inst.command.kernel) # Get the label of all qubits that are measured with the qubit(s) in this instruction existing_qubits = {chan.index for chan in inst.acquires} all_qubits = [] @@ -158,7 +157,10 @@ def add_implicit_acquires(schedule: ScheduleComponent, meas_map: List[List[int]] # Replace the old acquire instruction by a new one explicitly acquiring all qubits in # the measurement group. for i in all_qubits: - explicit_inst = AcquireInstruction(cmd, AcquireChannel(i), MemorySlot(i)) << time + explicit_inst = Acquire(inst.duration, AcquireChannel(i), + mem_slot=MemorySlot(i), + kernel=inst.kernel, + discriminator=inst.discriminator) << time if time not in acquire_map: new_schedule |= explicit_inst acquire_map = {time: {i}} diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 2f53ce311add..6a051fd286cc 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -21,6 +21,7 @@ from qiskit.pulse import commands, channels, instructions from qiskit.pulse.exceptions import PulseError +from qiskit.pulse.configuration import Kernel, Discriminator from qiskit.pulse.parser import parse_string_expr from qiskit.pulse.schedule import ParameterizedSchedule, Schedule from qiskit.qobj import QobjMeasurementOption @@ -119,7 +120,7 @@ def __call__(self, shift, instruction): return method(self, shift, instruction) @bind_instruction(commands.AcquireInstruction) - def convert_acquire(self, shift, instruction): + def convert_acquire_deprecated(self, shift, instruction): """Return converted `AcquireInstruction`. Args: @@ -164,6 +165,52 @@ def convert_acquire(self, shift, instruction): }) return self._qobj_model(**command_dict) + @bind_instruction(instructions.Acquire) + def convert_acquire(self, shift, instruction): + """Return converted `Acquire`. + + Args: + shift(int): Offset time. + instruction (Acquire): acquire instruction. + Returns: + dict: Dictionary of required parameters. + """ + meas_level = self._run_config.get('meas_level', 2) + + command_dict = { + 'name': 'acquire', + 't0': shift + instruction.start_time, + 'duration': instruction.duration, + 'qubits': [q.index for q in instruction.acquires], + 'memory_slot': [m.index for m in instruction.mem_slots] + } + if meas_level == MeasLevel.CLASSIFIED: + # setup discriminators + if instruction.discriminator: + command_dict.update({ + 'discriminators': [ + QobjMeasurementOption( + name=instruction.discriminator.name, + params=instruction.discriminator.params) + ] + }) + # setup register_slots + if instruction.reg_slots: + command_dict.update({ + 'register_slot': [regs.index for regs in instruction.reg_slots] + }) + if meas_level in [MeasLevel.KERNELED, MeasLevel.CLASSIFIED]: + # setup kernels + if instruction.kernel: + command_dict.update({ + 'kernels': [ + QobjMeasurementOption( + name=instruction.kernel.name, + params=instruction.kernel.params) + ] + }) + return self._qobj_model(**command_dict) + def convert_single_acquires(self, shift, instruction, qubits=None, memory_slot=None, register_slot=None): """Return converted `AcquireInstruction`, with options to override the qubits, @@ -375,7 +422,7 @@ def get_channel(self, channel): @bind_name('acquire') def convert_acquire(self, instruction): - """Return converted `AcquireInstruction`. + """Return converted `Acquire`. Args: instruction (PulseQobjInstruction): acquire qobj @@ -404,8 +451,7 @@ def convert_acquire(self, instruction): "to first discriminator entry.") discriminator = discriminators[0] if discriminator: - discriminator = commands.Discriminator(name=discriminators[0].name, - params=discriminators[0].params) + discriminator = Discriminator(name=discriminators[0].name, **discriminators[0].params) kernels = (instruction.kernels if hasattr(instruction, 'kernels') else None) @@ -416,13 +462,14 @@ def convert_acquire(self, instruction): "kernel entry.") kernel = kernels[0] if kernel: - kernel = commands.Kernel(name=kernels[0].name, params=kernels[0].params) + kernel = Kernel(name=kernels[0].name, **kernels[0].params) - cmd = commands.Acquire(duration, discriminator=discriminator, kernel=kernel) schedule = Schedule() for acquire_channel, mem_slot, reg_slot in zip(acquire_channels, mem_slots, register_slots): - schedule |= commands.AcquireInstruction(cmd, acquire_channel, mem_slot, reg_slot) << t0 + schedule |= instructions.Acquire(duration, acquire_channel, mem_slot=mem_slot, + reg_slot=reg_slot, kernel=kernel, + discriminator=discriminator) << t0 return schedule diff --git a/qiskit/scheduler/utils.py b/qiskit/scheduler/utils.py index 900a815cf238..a0ac77c7fe40 100644 --- a/qiskit/scheduler/utils.py +++ b/qiskit/scheduler/utils.py @@ -15,11 +15,8 @@ """Scheduling utility functions.""" from typing import Dict, List, Optional, Union -from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap -from qiskit.pulse.schedule import Schedule -from qiskit.pulse.channels import MemorySlot +from qiskit.pulse import Schedule, MemorySlot, Acquire, PulseError, InstructionScheduleMap from qiskit.pulse.commands import AcquireInstruction -from qiskit.pulse.exceptions import PulseError def format_meas_map(meas_map: List[List[int]]) -> Dict[int, List[int]]: @@ -96,16 +93,16 @@ def measure(qubits: List[int], "{}".format(measure_name, inst_map.instructions)) for time, inst in default_sched.instructions: - if qubit_mem_slots and isinstance(inst, AcquireInstruction): + if qubit_mem_slots and isinstance(inst, (Acquire, AcquireInstruction)): for channel in inst.acquires: if channel.index in qubit_mem_slots: mem_slot = MemorySlot(qubit_mem_slots[channel.index]) else: mem_slot = MemorySlot(unused_mem_slots.pop()) - schedule = schedule.insert(time, AcquireInstruction(command=inst.command, - acquire=channel, - mem_slot=mem_slot)) - elif qubit_mem_slots is None and isinstance(inst, AcquireInstruction): + schedule = schedule.insert(time, Acquire(inst.duration, + channel, + mem_slot=mem_slot)) + elif qubit_mem_slots is None and isinstance(inst, (Acquire, AcquireInstruction)): schedule = schedule.insert(time, inst) # Measurement pulses should only be added if its qubit was measured by the user elif inst.channels[0].index in qubits: diff --git a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml index 692290c6634d..b7407e5f564a 100644 --- a/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml +++ b/releasenotes/notes/unify-instructions-and-commands-aaa6d8724b8a29d3.yaml @@ -11,18 +11,22 @@ features: sched += Delay(5)(DriveChannel(0)) sched += ShiftPhase(np.pi)(DriveChannel(0)) + sched += Acquire(100)(AcquireChannel(0), MemorySlot(0)) or, equivalently (though less used):: sched += DelayInstruction(Delay(5), DriveChannel(0)) sched += ShiftPhaseInstruction(ShiftPhase(np.pi), DriveChannel(0)) + sched += AcquireInstruction(Acquire(100), AcquireChannel(0), + MemorySlot(0)) Now, rather than build a command *and* an instruction, each command has been migrated into an instruction:: sched += Delay(5, DriveChannel(0)) sched += ShiftPhase(np.pi, DriveChannel(0)) - sched += SetFrequency(5.5, DriveChannel(0)) + sched += Acquire(100, AcquireChannel(0), MemorySlot(0)) + sched += SetFrequency(5.5, DriveChannel(0)) # New instruction! deprecations: - | @@ -52,3 +56,15 @@ deprecations: calling a command on a channel will be supported:: ShiftPhase()() + - | + ``AcquireInstruction`` has been deprecated and replaced by + :py:class:`~qiskit.pulse.instructions.Acquire`. The changes are:: + + Acquire()(<**channels>) -> Acquire(, <**channels>) + AcquireInstruction(Acquire(), <**channels>) + -> Acquire(, <**channels>) + + Until the deprecation period is over, the previous Acquire syntax of + calling the command on a channel will be supported:: + + Acquire()(<**channels>) diff --git a/test/python/pulse/test_commands.py b/test/python/pulse/test_commands.py index 2a377991b0f2..1cb74a589860 100644 --- a/test/python/pulse/test_commands.py +++ b/test/python/pulse/test_commands.py @@ -96,8 +96,7 @@ class TestAcquire(QiskitTestCase): """Acquisition tests.""" def test_can_construct_valid_acquire_command(self): - """Test if valid acquire command can be constructed. - """ + """Test if valid acquire command can be constructed.""" kernel_opts = { 'start_window': 0, 'stop_window': 10 @@ -111,27 +110,24 @@ def test_can_construct_valid_acquire_command(self): } discriminator = Discriminator(name='linear_discriminator', **discriminator_opts) - acq_command = Acquire(duration=10, kernel=kernel, discriminator=discriminator) + acq = Acquire(duration=10, kernel=kernel, discriminator=discriminator) - self.assertEqual(acq_command.duration, 10) - self.assertEqual(acq_command.discriminator.name, 'linear_discriminator') - self.assertEqual(acq_command.discriminator.params, discriminator_opts) - self.assertEqual(acq_command.kernel.name, 'boxcar') - self.assertEqual(acq_command.kernel.params, kernel_opts) - self.assertTrue(acq_command.name.startswith('acq')) + self.assertEqual(acq.duration, 10) + self.assertEqual(acq.discriminator.name, 'linear_discriminator') + self.assertEqual(acq.discriminator.params, discriminator_opts) + self.assertEqual(acq.kernel.name, 'boxcar') + self.assertEqual(acq.kernel.params, kernel_opts) + self.assertTrue(acq.name.startswith('acq')) def test_can_construct_acquire_command_with_default_values(self): """Test if an acquire command can be constructed with default discriminator and kernel. """ - acq_command_a = Acquire(duration=10) - acq_command_b = Acquire(duration=10) - - self.assertEqual(acq_command_a.duration, 10) - self.assertEqual(acq_command_a.discriminator, None) - self.assertEqual(acq_command_a.kernel, None) - self.assertTrue(acq_command_a.name.startswith('acq')) - self.assertNotEqual(acq_command_a.name, acq_command_b.name) - self.assertEqual(acq_command_b.name, 'acq' + str(int(acq_command_a.name[3:]) + 1)) + acq_a = Acquire(duration=10) + + self.assertEqual(acq_a.duration, 10) + self.assertEqual(acq_a.discriminator, None) + self.assertEqual(acq_a.kernel, None) + self.assertTrue(acq_a.name.startswith('acq')) class TestFrameChangeCommand(QiskitTestCase): diff --git a/test/python/pulse/test_reschedule.py b/test/python/pulse/test_reschedule.py index 44e6d2a66a72..3470bae14d97 100644 --- a/test/python/pulse/test_reschedule.py +++ b/test/python/pulse/test_reschedule.py @@ -18,8 +18,7 @@ import numpy as np from qiskit import pulse -from qiskit.pulse import AcquireChannel -from qiskit.pulse.commands import AcquireInstruction +from qiskit.pulse import AcquireChannel, Acquire from qiskit.pulse.channels import MeasureChannel, MemorySlot, DriveChannel from qiskit.pulse.exceptions import PulseError from qiskit.test import QiskitTestCase @@ -50,11 +49,11 @@ def test_align_measures(self): sched = align_measures([sched], self.cmd_def)[0] self.assertEqual(sched.name, 'fake_experiment') for time, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): self.assertEqual(time, 10) sched = align_measures([sched], self.cmd_def, align_time=20)[0] for time, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): self.assertEqual(time, 20) if isinstance(inst.channels[0], MeasureChannel): self.assertEqual(time, 20) @@ -68,11 +67,11 @@ def test_align_post_u3(self): sched = sched.insert(1, acquire(self.config.acquire(0), MemorySlot(0))) sched = align_measures([sched], self.cmd_def)[0] for time, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): self.assertEqual(time, 4) sched = align_measures([sched], self.cmd_def, max_calibration_duration=10)[0] for time, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): self.assertEqual(time, 10) def test_multi_acquire(self): @@ -124,10 +123,10 @@ def test_align_across_schedules(self): sched2 = sched2.insert(25, acquire(self.config.acquire(0), MemorySlot(0))) schedules = align_measures([sched1, sched2], self.cmd_def) for time, inst in schedules[0].instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): self.assertEqual(time, 25) for time, inst in schedules[0].instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): self.assertEqual(time, 25) @@ -152,7 +151,7 @@ def test_add_implicit(self): sched = add_implicit_acquires(self.sched, [[0, 1]]) acquired_qubits = set() for _, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): acquired_qubits.add(inst.acquire.index) self.assertEqual(acquired_qubits, {0, 1}) @@ -161,7 +160,7 @@ def test_add_across_meas_map_sublists(self): sched = add_implicit_acquires(self.sched, [[0, 2], [1, 3]]) acquired_qubits = set() for _, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): acquired_qubits.add(inst.acquire.index) self.assertEqual(acquired_qubits, {0, 1, 2, 3}) @@ -170,7 +169,7 @@ def test_dont_add_all(self): sched = add_implicit_acquires(self.sched, [[4, 5], [0, 2], [1, 3]]) acquired_qubits = set() for _, inst in sched.instructions: - if isinstance(inst, AcquireInstruction): + if isinstance(inst, Acquire): acquired_qubits.add(inst.acquire.index) self.assertEqual(acquired_qubits, {0, 1, 2, 3}) diff --git a/test/python/pulse/test_schedule.py b/test/python/pulse/test_schedule.py index 35a6595ee589..99d62cb7709a 100644 --- a/test/python/pulse/test_schedule.py +++ b/test/python/pulse/test_schedule.py @@ -21,8 +21,7 @@ from qiskit.pulse.channels import (MemorySlot, RegisterSlot, DriveChannel, AcquireChannel, SnapshotChannel, MeasureChannel) from qiskit.pulse.commands import (Acquire, PersistentValue, Snapshot, Delay, - functional_pulse, AcquireInstruction, - PulseInstruction, Gaussian, Drag, + functional_pulse, PulseInstruction, Gaussian, Drag, GaussianSquare, ConstantPulse) from qiskit.pulse import pulse_lib, SamplePulse, ShiftPhase, Instruction, SetFrequency @@ -519,11 +518,11 @@ def test_filter_inst_types(self): # test on Acquire only_acquire, no_acquire = \ - self._filter_and_test_consistency(sched, instruction_types=[AcquireInstruction]) + self._filter_and_test_consistency(sched, instruction_types=[Acquire]) for _, inst in only_acquire.instructions: - self.assertIsInstance(inst, AcquireInstruction) + self.assertIsInstance(inst, Acquire) for _, inst in no_acquire.instructions: - self.assertFalse(isinstance(inst, AcquireInstruction)) + self.assertFalse(isinstance(inst, Acquire)) # test two instruction types only_pulse_and_fc, no_pulse_and_fc = \ @@ -576,7 +575,7 @@ def test_filter_intervals(self): filtered, excluded = self._filter_and_test_consistency(sched, time_ranges=[(59, 65)]) self.assertEqual(len(filtered.instructions), 2) self.assertEqual(filtered.instructions[0][0], 60) - self.assertIsInstance(filtered.instructions[0][1], AcquireInstruction) + self.assertIsInstance(filtered.instructions[0][1], Acquire) self.assertEqual(len(excluded.instructions), 4) self.assertEqual(excluded.instructions[3][0], 90) self.assertIsInstance(excluded.instructions[3][1], PulseInstruction) @@ -639,14 +638,14 @@ def test_filter_multiple(self): # make sure the PulseInstruction not in the intervals is maintained self.assertIsInstance(excluded.instructions[0][1], PulseInstruction) - # split based on AcquireInstruction in the specified intervals + # split based on Acquire in the specified intervals filtered, excluded = self._filter_and_test_consistency(sched, instruction_types=[ - AcquireInstruction], + Acquire], time_ranges=[(25, 100)]) self.assertTrue(len(excluded.instructions), 4) for _, inst in filtered.instructions: - self.assertIsInstance(inst, AcquireInstruction) + self.assertIsInstance(inst, Acquire) self.assertTrue(len(filtered.instructions), 2) def test_custom_filters(self): diff --git a/test/python/qobj/test_pulse_converter.py b/test/python/qobj/test_pulse_converter.py index 224bd0ba171f..ebcf828d6882 100644 --- a/test/python/qobj/test_pulse_converter.py +++ b/test/python/qobj/test_pulse_converter.py @@ -22,14 +22,12 @@ from qiskit.qobj.converters import (InstructionToQobjConverter, QobjToInstructionConverter, LoConfigConverter) from qiskit.pulse.commands import (SamplePulse, FrameChange, PersistentValue, Snapshot, Acquire, - Discriminator, Kernel, Gaussian, GaussianSquare, ConstantPulse, - Drag) - + Gaussian, GaussianSquare, ConstantPulse, Drag) from qiskit.pulse.instructions import ShiftPhase, SetFrequency from qiskit.pulse.channels import (DriveChannel, ControlChannel, MeasureChannel, AcquireChannel, MemorySlot, RegisterSlot) from qiskit.pulse.schedule import ParameterizedSchedule, Schedule -from qiskit.pulse import LoConfig +from qiskit.pulse import LoConfig, Kernel, Discriminator class TestInstructionToQobjConverter(QiskitTestCase): @@ -263,13 +261,12 @@ def test_persistent_value(self): def test_acquire(self): """Test converted qobj from AcquireInstruction.""" - cmd = Acquire(10, - kernel=Kernel(name='test_kern', params={'test_params': 'test'}), - discriminator=Discriminator(name='test_disc', params={'test_params': 1.0})) - schedule = Schedule() for i in range(self.n_qubits): - schedule |= cmd(AcquireChannel(i), MemorySlot(i), RegisterSlot(i)) + schedule |= Acquire(10, AcquireChannel(i), MemorySlot(i), RegisterSlot(i), + kernel=Kernel(name='test_kern', test_params='test'), + discriminator=Discriminator(name='test_disc', + test_params=1.0)) qobj = PulseQobjInstruction(name='acquire', t0=0, duration=10, qubits=[0, 1], memory_slot=[0, 1], register_slot=[0, 1], @@ -280,7 +277,10 @@ def test_acquire(self): converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.timeslots, schedule.timeslots) - self.assertEqual(converted_instruction.instructions[0][-1].command, cmd) + self.assertEqual(converted_instruction.instructions[0][-1].duration, 10) + self.assertEqual(converted_instruction.instructions[0][-1].kernel.params, + {'test_params': 'test'}) + self.assertEqual(converted_instruction.instructions[1][-1].channel, AcquireChannel(1)) def test_snapshot(self): """Test converted qobj from SnapShot.""" diff --git a/test/python/scheduler/test_utils.py b/test/python/scheduler/test_utils.py index fd9fd3ead309..eb720e0e42a0 100644 --- a/test/python/scheduler/test_utils.py +++ b/test/python/scheduler/test_utils.py @@ -71,7 +71,7 @@ def test_measure_sched_with_meas_map(self): def test_measure_with_custom_inst_map(self): """Test measure with custom inst_map, meas_map with measure_name.""" q0_sched = GaussianSquare(1200, 1, 0.4, 1150)(MeasureChannel(0)) - q0_sched += Acquire(1200)(AcquireChannel(0), MemorySlot(0)) + q0_sched += Acquire(1200, AcquireChannel(0), MemorySlot(0)) inst_map = InstructionScheduleMap() inst_map.add('my_sched', 0, q0_sched) sched = measure(qubits=[0],