diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index e7a76051373b..e013934c1009 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -81,6 +81,7 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config): # instructions max_memory_slot = 0 qobj_instructions = [] + acquire_instructions = [] # Instructions are returned as tuple of shifted time and instruction for shift, instruction in schedule.instructions: @@ -111,13 +112,16 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config): elif isinstance(instruction, AcquireInstruction): max_memory_slot = max(max_memory_slot, *[slot.index for slot in instruction.mem_slots]) - if meas_map: - # verify all acquires satisfy meas_map - _validate_meas_map(instruction, meas_map) + + acquire_instructions.append(instruction) converted_instruction = instruction_converter(shift, instruction) qobj_instructions.append(converted_instruction) + if meas_map: + # verify all acquires satisfy meas_map + _validate_meas_map(acquire_instructions, meas_map) + # memory slot size is memory slot index + 1 because index starts from zero exp_memory_slot_size = max_memory_slot + 1 memory_slot_size = max(memory_slot_size, exp_memory_slot_size) @@ -199,11 +203,16 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config): header=qobj_header) -def _validate_meas_map(acquire, meas_map): +def _validate_meas_map(instructions, meas_map): """Validate all qubits tied in meas_map are to be acquired.""" meas_map_set = [set(m) for m in meas_map] # Verify that each qubit is listed once in measurement map - measured_qubits = {acq_ch.index for acq_ch in acquire.acquires} + + acquires = [] + for inst in instructions: + acquires += inst.acquires + measured_qubits = {acq_ch.index for acq_ch in acquires} + tied_qubits = set() for meas_qubit in measured_qubits: for map_inst in meas_map_set: diff --git a/qiskit/pulse/commands/acquire.py b/qiskit/pulse/commands/acquire.py index b55dac15836c..cdaae84dab20 100644 --- a/qiskit/pulse/commands/acquire.py +++ b/qiskit/pulse/commands/acquire.py @@ -15,7 +15,8 @@ """ Acquire. """ -from typing import Union, List, Optional +import warnings +from typing import Optional, Union, List from qiskit.pulse.channels import Qubit, MemorySlot, RegisterSlot, AcquireChannel from qiskit.pulse.exceptions import PulseError @@ -93,11 +94,15 @@ def __repr__(self): # pylint: disable=arguments-differ def to_instruction(self, - qubits: Union[Qubit, List[Qubit]], - mem_slots: Optional[Union[MemorySlot, List[MemorySlot]]] = None, + 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, qubits, mem_slots, reg_slots, name=name) + + 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 @@ -106,40 +111,72 @@ class AcquireInstruction(Instruction): def __init__(self, command: Acquire, - acquires: Union[AcquireChannel, List[AcquireChannel]], - mem_slots: Union[MemorySlot, List[MemorySlot]], + acquire: 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): - if not isinstance(acquires, list): - acquires = [acquires] + if isinstance(acquire, list) or isinstance(mem_slot, list) or reg_slots: + warnings.warn('The AcquireInstruction no longer supports multiple qubits, ' + 'multiple memory slots and multiple reg slots. ' + 'This behavior has been 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(acquire, list): + acquire = [acquire] - if isinstance(acquires[0], Qubit): + if isinstance(acquire[0], Qubit): raise PulseError("AcquireInstruction can not be instantiated with Qubits, " "which are deprecated.") - if not (mem_slots or reg_slots): + 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 not (mem_slot or reg_slot): raise PulseError('Neither memoryslots or registers were supplied') - if mem_slots: - if isinstance(mem_slots, MemorySlot): - mem_slots = [mem_slots] - elif len(acquires) != len(mem_slots): - raise PulseError("#mem_slots must be equals to #acquires") - - if reg_slots: - if isinstance(reg_slots, RegisterSlot): - reg_slots = [reg_slots] - if len(acquires) != len(reg_slots): - raise PulseError("#reg_slots must be equals to #acquires") + if mem_slot and len(acquire) != len(mem_slot): + raise PulseError("The number of mem_slots must be equals to the number of acquires") + + if reg_slot: + if len(acquire) != len(reg_slot): + raise PulseError("The number of reg_slots must be equals " + "to the number of acquires") else: - reg_slots = [] + reg_slot = [] - super().__init__(command, *acquires, *mem_slots, *reg_slots, name=name) + super().__init__(command, *acquire, *mem_slot, *reg_slot, name=name) - self._acquires = acquires - self._mem_slots = mem_slots - self._reg_slots = reg_slots + self._acquires = acquire + self._mem_slots = mem_slot + self._reg_slots = reg_slot + + @property + def acquire(self): + """Acquire channel to be acquired on.""" + return self._acquires[0] if self._acquires else None + + @property + def mem_slot(self): + """MemorySlot.""" + return self._mem_slots[0] if self._mem_slots else None + + @property + def reg_slot(self): + """RegisterSlot.""" + return self._reg_slots[0] if self._reg_slots else None @property def acquires(self): @@ -149,9 +186,13 @@ def acquires(self): @property def mem_slots(self): """MemorySlots.""" + warnings.warn('"mem_slots" is deprecated and being replaced by "mem_slot"', + DeprecationWarning, 3) return self._mem_slots @property def reg_slots(self): """RegisterSlots.""" + warnings.warn('"reg_slots" is deprecated and being replaced by "reg_slot"', + DeprecationWarning, 3) return self._reg_slots diff --git a/qiskit/pulse/commands/instruction.py b/qiskit/pulse/commands/instruction.py index 6d2242a7c9e1..114ccf85ef20 100644 --- a/qiskit/pulse/commands/instruction.py +++ b/qiskit/pulse/commands/instruction.py @@ -45,7 +45,7 @@ def __init__(self, command, *channels: List[Channel], duration = command.duration self._timeslots = TimeslotCollection(*(Timeslot(Interval(0, duration), channel) - for channel in channels)) + for channel in channels if channel is not None)) channels = self.channels diff --git a/qiskit/pulse/reschedule.py b/qiskit/pulse/reschedule.py index ab45a203a345..ef750cd4279d 100644 --- a/qiskit/pulse/reschedule.py +++ b/qiskit/pulse/reschedule.py @@ -140,12 +140,14 @@ def add_implicit_acquires(schedule: ScheduleComponent, meas_map: List[List[int]] Schedule """ new_schedule = Schedule(name=schedule.name) + acquire_map = dict() for time, inst in schedule.instructions: if isinstance(inst, 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} @@ -155,10 +157,14 @@ def add_implicit_acquires(schedule: ScheduleComponent, meas_map: List[List[int]] all_qubits.extend(sublist) # Replace the old acquire instruction by a new one explicitly acquiring all qubits in # the measurement group. - new_schedule |= AcquireInstruction( - cmd, - [AcquireChannel(i) for i in all_qubits], - [MemorySlot(i) for i in all_qubits]) << time + for i in all_qubits: + explicit_inst = AcquireInstruction(cmd, AcquireChannel(i), MemorySlot(i)) << time + if time not in acquire_map: + new_schedule |= explicit_inst + acquire_map = {time: {i}} + elif i not in acquire_map[time]: + new_schedule |= explicit_inst + acquire_map[time].add(i) else: new_schedule |= inst << time diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 9bfd8ab35026..4745e91271dc 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -321,7 +321,7 @@ def convert_acquire(self, instruction): t0 = instruction.t0 duration = instruction.duration qubits = instruction.qubits - qubit_channels = [channels.AcquireChannel(qubit) for qubit in qubits] + acquire_channels = [channels.AcquireChannel(qubit) for qubit in qubits] mem_slots = [channels.MemorySlot(instruction.memory_slot[i]) for i in range(len(qubits))] @@ -329,7 +329,7 @@ def convert_acquire(self, instruction): register_slots = [channels.RegisterSlot(instruction.register_slot[i]) for i in range(len(qubits))] else: - register_slots = None + register_slots = [None] * len(qubits) discriminators = (instruction.discriminators if hasattr(instruction, 'discriminators') else None) @@ -356,8 +356,9 @@ def convert_acquire(self, instruction): cmd = commands.Acquire(duration, discriminator=discriminator, kernel=kernel) schedule = Schedule() - schedule |= commands.AcquireInstruction(cmd, qubit_channels, mem_slots, - register_slots) << t0 + + 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 return schedule diff --git a/qiskit/scheduler/methods/basic.py b/qiskit/scheduler/methods/basic.py index f7810d267924..f8629a88190a 100644 --- a/qiskit/scheduler/methods/basic.py +++ b/qiskit/scheduler/methods/basic.py @@ -157,16 +157,15 @@ def get_measure_schedule() -> CircuitPulseDef: default_sched = inst_map.get('measure', qubits) for time, inst in default_sched.instructions: if isinstance(inst, AcquireInstruction): - mem_slots = [] for channel in inst.acquires: if channel.index in qubit_mem_slots.keys(): - mem_slots.append(MemorySlot(qubit_mem_slots[channel.index])) + mem_slot = MemorySlot(qubit_mem_slots[channel.index]) else: - mem_slots.append(MemorySlot(unused_mem_slots.pop())) - new_acquire = AcquireInstruction(command=inst.command, - acquires=inst.acquires, - mem_slots=mem_slots) - sched._union((time, new_acquire)) + mem_slot = MemorySlot(unused_mem_slots.pop()) + sched._union((time, AcquireInstruction(command=inst.command, + acquire=channel, + mem_slot=mem_slot))) + # Measurement pulses should only be added if its qubit was measured by the user elif inst.channels[0].index in qubit_mem_slots.keys(): sched._union((time, inst)) diff --git a/releasenotes/notes/acquire-single-channel-ea83cef8d991f945.yaml b/releasenotes/notes/acquire-single-channel-ea83cef8d991f945.yaml new file mode 100644 index 000000000000..1e06260b69fa --- /dev/null +++ b/releasenotes/notes/acquire-single-channel-ea83cef8d991f945.yaml @@ -0,0 +1,20 @@ +--- +prelude: > + Acquire and AcquireInstruction now can be applied to a single channel +features: + - | + Within the terra API allow acquires be applied to a single qubit. + This makes pulse programming more consistent and easier to reason + about as all operations in pulse would then be for single channels. + For example: + + acquire = Acquire(duration=10) + schedule = Schedule() + schedule.insert(60, acquire(AcquireChannel(0), MemorySlot(0), RegisterSlot(0))) + schedule.insert(60, acquire(AcquireChannel(1), MemorySlot(1), RegisterSlot(1))) + +deprecations: + - | + Acquire on multiple qubits has been deprecated. + ``AcquireInstruction`` parameters ``mem_slots``, ``reg_slots`` has been deprecated, + use ``reg_slot``, ``mem_slot`` instead. diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py index 0ca6d682f266..bd65a2c889e9 100644 --- a/test/python/compiler/test_assembler.py +++ b/test/python/compiler/test_assembler.py @@ -23,6 +23,7 @@ from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.compiler.assemble import assemble from qiskit.exceptions import QiskitError +from qiskit.pulse import Schedule from qiskit.pulse.channels import MemorySlot, AcquireChannel, DriveChannel, MeasureChannel from qiskit.qobj import QasmQobj, validate_qobj_against_schema from qiskit.qobj.utils import MeasLevel, MeasReturnType @@ -355,9 +356,9 @@ def setUp(self): self.schedule = pulse.Schedule(name='fake_experiment') self.schedule = self.schedule.insert(0, test_pulse(self.backend_config.drive(0))) - self.schedule = self.schedule.insert(5, acquire( - [self.backend_config.acquire(i) for i in range(self.backend_config.n_qubits)], - [MemorySlot(i) for i in range(self.backend_config.n_qubits)])) + for i in range(self.backend_config.n_qubits): + self.schedule = self.schedule.insert(5, acquire(self.backend_config.acquire(i), + MemorySlot(i))) self.user_lo_config_dict = {self.backend_config.drive(0): 4.91e9} self.user_lo_config = pulse.LoConfig(self.user_lo_config_dict) @@ -390,7 +391,7 @@ def test_assemble_single_schedule_without_lo_config(self): test_dict = qobj.to_dict() self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0]) self.assertEqual(len(test_dict['experiments']), 1) - self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2) + self.assertEqual(len(test_dict['experiments'][0]['instructions']), 3) def test_assemble_multi_schedules_without_lo_config(self): """Test assembling schedules, no lo config.""" @@ -404,7 +405,7 @@ def test_assemble_multi_schedules_without_lo_config(self): test_dict = qobj.to_dict() self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0]) self.assertEqual(len(test_dict['experiments']), 2) - self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2) + self.assertEqual(len(test_dict['experiments'][0]['instructions']), 3) def test_assemble_single_schedule_with_lo_config(self): """Test assembling a single schedule, with a single lo config.""" @@ -419,7 +420,7 @@ def test_assemble_single_schedule_with_lo_config(self): test_dict = qobj.to_dict() self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.91, 5.0]) self.assertEqual(len(test_dict['experiments']), 1) - self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2) + self.assertEqual(len(test_dict['experiments'][0]['instructions']), 3) def test_assemble_single_schedule_with_lo_config_dict(self): """Test assembling a single schedule, with a single lo config supplied as dictionary.""" @@ -434,7 +435,7 @@ def test_assemble_single_schedule_with_lo_config_dict(self): test_dict = qobj.to_dict() self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.91, 5.0]) self.assertEqual(len(test_dict['experiments']), 1) - self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2) + self.assertEqual(len(test_dict['experiments'][0]['instructions']), 3) def test_assemble_single_schedule_with_multi_lo_configs(self): """Test assembling a single schedule, with lo configs (frequency sweep).""" @@ -448,7 +449,7 @@ def test_assemble_single_schedule_with_multi_lo_configs(self): self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0]) self.assertEqual(len(test_dict['experiments']), 2) - self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2) + self.assertEqual(len(test_dict['experiments'][0]['instructions']), 3) self.assertDictEqual(test_dict['experiments'][0]['config'], {'qubit_lo_freq': [4.91, 5.0]}) @@ -465,7 +466,7 @@ def test_assemble_multi_schedules_with_multi_lo_configs(self): test_dict = qobj.to_dict() self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0]) self.assertEqual(len(test_dict['experiments']), 2) - self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2) + self.assertEqual(len(test_dict['experiments'][0]['instructions']), 3) self.assertDictEqual(test_dict['experiments'][0]['config'], {'qubit_lo_freq': [4.91, 5.0]}) @@ -482,8 +483,10 @@ def test_assemble_multi_schedules_with_wrong_number_of_multi_lo_configs(self): def test_assemble_meas_map(self): """Test assembling a single schedule, no lo config.""" acquire = pulse.Acquire(5) - schedule = acquire([AcquireChannel(0), AcquireChannel(1)], - [MemorySlot(0), MemorySlot(1)]) + schedule = Schedule(name='fake_experiment') + schedule = schedule.insert(5, acquire(AcquireChannel(0), MemorySlot(0))) + schedule = schedule.insert(5, acquire(AcquireChannel(1), MemorySlot(1))) + qobj = assemble(schedule, qubit_lo_freq=self.default_qubit_lo_freq, meas_lo_freq=self.default_meas_lo_freq, @@ -503,7 +506,7 @@ def test_assemble_memory_slots(self): # single acquisition schedule = acquire(self.backend_config.acquire(0), - mem_slots=pulse.MemorySlot(n_memoryslots-1)) + mem_slot=pulse.MemorySlot(n_memoryslots-1)) qobj = assemble(schedule, qubit_lo_freq=self.default_qubit_lo_freq, @@ -517,9 +520,9 @@ def test_assemble_memory_slots(self): # multiple acquisition schedule = acquire(self.backend_config.acquire(0), - mem_slots=pulse.MemorySlot(n_memoryslots-1)) + mem_slot=pulse.MemorySlot(n_memoryslots-1)) schedule = schedule.insert(10, acquire(self.backend_config.acquire(0), - mem_slots=pulse.MemorySlot(n_memoryslots-1))) + mem_slot=pulse.MemorySlot(n_memoryslots-1))) qobj = assemble(schedule, qubit_lo_freq=self.default_qubit_lo_freq, @@ -539,7 +542,7 @@ def test_assemble_memory_slots_for_schedules(self): schedules = [] for n_memoryslot in n_memoryslots: schedule = acquire(self.backend_config.acquire(0), - mem_slots=pulse.MemorySlot(n_memoryslot-1)) + mem_slot=pulse.MemorySlot(n_memoryslot-1)) schedules.append(schedule) qobj = assemble(schedules, diff --git a/test/python/pulse/test_reschedule.py b/test/python/pulse/test_reschedule.py index ea1f37d6a969..44e6d2a66a72 100644 --- a/test/python/pulse/test_reschedule.py +++ b/test/python/pulse/test_reschedule.py @@ -18,6 +18,7 @@ import numpy as np from qiskit import pulse +from qiskit.pulse import AcquireChannel from qiskit.pulse.commands import AcquireInstruction from qiskit.pulse.channels import MeasureChannel, MemorySlot, DriveChannel from qiskit.pulse.exceptions import PulseError @@ -142,8 +143,9 @@ def setUp(self): acquire = pulse.Acquire(5) sched = pulse.Schedule(name='fake_experiment') sched = sched.insert(0, self.short_pulse(self.config.drive(0))) - self.sched = sched.insert(5, acquire([self.config.acquire(0), self.config.acquire(1)], - [MemorySlot(0), MemorySlot(1)])) + sched = sched.insert(5, acquire(self.config.acquire(0), MemorySlot(0))) + sched = sched.insert(5, acquire(self.config.acquire(1), MemorySlot(1))) + self.sched = sched def test_add_implicit(self): """Test that implicit acquires are made explicit according to the meas map.""" @@ -151,7 +153,7 @@ def test_add_implicit(self): acquired_qubits = set() for _, inst in sched.instructions: if isinstance(inst, AcquireInstruction): - acquired_qubits.update({a.index for a in inst.acquires}) + acquired_qubits.add(inst.acquire.index) self.assertEqual(acquired_qubits, {0, 1}) def test_add_across_meas_map_sublists(self): @@ -160,7 +162,7 @@ def test_add_across_meas_map_sublists(self): acquired_qubits = set() for _, inst in sched.instructions: if isinstance(inst, AcquireInstruction): - acquired_qubits.update({a.index for a in inst.acquires}) + acquired_qubits.add(inst.acquire.index) self.assertEqual(acquired_qubits, {0, 1, 2, 3}) def test_dont_add_all(self): @@ -169,9 +171,18 @@ def test_dont_add_all(self): acquired_qubits = set() for _, inst in sched.instructions: if isinstance(inst, AcquireInstruction): - acquired_qubits.update({a.index for a in inst.acquires}) + acquired_qubits.add(inst.acquire.index) self.assertEqual(acquired_qubits, {0, 1, 2, 3}) + def test_multiple_acquires(self): + """Test for multiple acquires.""" + sched = pulse.Schedule() + acq_q0 = pulse.Acquire(1200)(AcquireChannel(0), MemorySlot(0)) + sched += acq_q0 + sched += acq_q0 << sched.duration + sched = add_implicit_acquires(sched, meas_map=[[0]]) + self.assertEqual(sched.instructions, ((0, acq_q0), (2400, acq_q0))) + class TestPad(QiskitTestCase): """Test padding of schedule with delays.""" diff --git a/test/python/pulse/test_schedule.py b/test/python/pulse/test_schedule.py index a65ce027cbd9..c660469bf70a 100644 --- a/test/python/pulse/test_schedule.py +++ b/test/python/pulse/test_schedule.py @@ -348,6 +348,48 @@ def my_test_par_sched_two(x, y, z): self.assertEqual(par_sched.parameters, ('x', 'y', 'z')) + def test_schedule_with_acquire_on_single_qubit(self): + """Test schedule with acquire on single qubit.""" + acquire = Acquire(10) + sched_single = Schedule() + for i in range(self.config.n_qubits): + sched_single = sched_single.insert(10, acquire(self.config.acquire(i), + MemorySlot(i), + RegisterSlot(i))) + + self.assertEqual(len(sched_single.instructions), 2) + self.assertEqual(len(sched_single.channels), 6) + + def test_schedule_with_acquire_on_multiple_qubits(self): + """Test schedule with acquire on multiple qubits.""" + acquire = Acquire(10) + sched_multiple = Schedule() + qubits = [self.config.acquire(i) for i in range(self.config.n_qubits)] + mem_slots = [MemorySlot(i) for i in range(self.config.n_qubits)] + reg_slots = [RegisterSlot(i) for i in range(self.config.n_qubits)] + sched_multiple = sched_multiple.insert(10, acquire(qubits, mem_slots, reg_slots)) + + self.assertEqual(len(sched_multiple.instructions), 1) + self.assertEqual(len(sched_multiple.channels), 6) + + def test_schedule_with_acquire_for_back_and_forward_compatibility(self): + """Test schedule with acquire for back and forward compatibility.""" + acquire = Acquire(10) + cmds = [ + acquire(AcquireChannel(0), MemorySlot(0)), + acquire([AcquireChannel(0)], MemorySlot(0)), + acquire(AcquireChannel(0), [MemorySlot(0)]), + acquire([AcquireChannel(0)], mem_slots=[MemorySlot(0)]), + acquire(AcquireChannel(0), MemorySlot(0), [RegisterSlot(0)]), + acquire(AcquireChannel(0), MemorySlot(0), reg_slot=RegisterSlot(0)) + ] + for cmd in cmds: + mixed_schedule = Schedule() + mixed_schedule = mixed_schedule.insert(10, cmd) + + self.assertEqual(len(mixed_schedule.instructions), 1) + self.assertTrue(MemorySlot(0) in mixed_schedule.channels) + def test_parametric_commands_in_sched(self): """Test that schedules can be built with parametric commands.""" sched = Schedule(name='test_parametric') @@ -357,8 +399,7 @@ def test_parametric_commands_in_sched(self): sched_duration = sched.duration sched += GaussianSquare(duration=1500, amp=0.2, sigma=8, width=140)(MeasureChannel(0)) << sched_duration - sched += Acquire(duration=1500)(AcquireChannel(0), - mem_slots=[MemorySlot(0)]) << sched_duration + sched += Acquire(duration=1500)(AcquireChannel(0), [MemorySlot(0)]) << sched_duration self.assertEqual(sched.duration, 1525) self.assertTrue('sigma' in sched.instructions[0][1].command.parameters) @@ -450,14 +491,14 @@ def test_filter_channels(self): sched = sched.insert(0, lp0(self.config.drive(0))) sched = sched.insert(10, lp0(self.config.drive(1))) sched = sched.insert(30, FrameChange(phase=-1.57)(self.config.drive(0))) - sched = sched.insert(60, acquire([AcquireChannel(0), AcquireChannel(1)], - [MemorySlot(0), MemorySlot(1)])) + sched = sched.insert(60, acquire(AcquireChannel(0), MemorySlot(0))) + sched = sched.insert(60, acquire(AcquireChannel(1), MemorySlot(1))) sched = sched.insert(90, lp0(self.config.drive(0))) # split instructions for those on AcquireChannel(1) and those not filtered, excluded = self._filter_and_test_consistency(sched, channels=[AcquireChannel(1)]) self.assertEqual(len(filtered.instructions), 1) - self.assertEqual(len(excluded.instructions), 4) + self.assertEqual(len(excluded.instructions), 5) # Split schedule into the part with channels on 1 and into a part without channels = [AcquireChannel(1), DriveChannel(1)] @@ -476,8 +517,8 @@ def test_filter_inst_types(self): sched = sched.insert(0, lp0(self.config.drive(0))) sched = sched.insert(10, lp0(self.config.drive(1))) sched = sched.insert(30, FrameChange(phase=-1.57)(self.config.drive(0))) - sched = sched.insert(60, acquire([self.config.acquire(i) for i in range(2)], - [MemorySlot(i) for i in range(2)])) + for i in range(2): + sched = sched.insert(60, acquire(self.config.acquire(i), MemorySlot(i))) sched = sched.insert(90, lp0(self.config.drive(0))) # test on Acquire @@ -497,13 +538,13 @@ def test_filter_inst_types(self): for _, inst in no_pulse_and_fc.instructions: self.assertFalse(isinstance(inst, (PulseInstruction, FrameChangeInstruction))) self.assertEqual(len(only_pulse_and_fc.instructions), 4) - self.assertEqual(len(no_pulse_and_fc.instructions), 1) + self.assertEqual(len(no_pulse_and_fc.instructions), 2) # test on FrameChange only_fc, no_fc = \ self._filter_and_test_consistency(sched, instruction_types={FrameChangeInstruction}) self.assertEqual(len(only_fc.instructions), 1) - self.assertEqual(len(no_fc.instructions), 4) + self.assertEqual(len(no_fc.instructions), 5) def test_filter_intervals(self): """Test filtering on intervals.""" @@ -513,8 +554,8 @@ def test_filter_intervals(self): sched = sched.insert(0, lp0(self.config.drive(0))) sched = sched.insert(10, lp0(self.config.drive(1))) sched = sched.insert(30, FrameChange(phase=-1.57)(self.config.drive(0))) - sched = sched.insert(60, acquire([self.config.acquire(i) for i in range(2)], - [MemorySlot(i) for i in range(2)])) + for i in range(2): + sched = sched.insert(60, acquire(self.config.acquire(i), MemorySlot(i))) sched = sched.insert(90, lp0(self.config.drive(0))) # split schedule into instructions occurring in (0,13), and those outside @@ -524,11 +565,11 @@ def test_filter_intervals(self): for start_time, inst in excluded.instructions: self.assertFalse((start_time >= 0) and (start_time + inst.stop_time <= 13)) self.assertEqual(len(filtered.instructions), 2) - self.assertEqual(len(excluded.instructions), 3) + self.assertEqual(len(excluded.instructions), 4) # split into schedule occurring in and outside of interval (59,65) filtered, excluded = self._filter_and_test_consistency(sched, time_ranges=[(59, 65)]) - self.assertEqual(len(filtered.instructions), 1) + self.assertEqual(len(filtered.instructions), 2) self.assertEqual(filtered.instructions[0][0], 60) self.assertIsInstance(filtered.instructions[0][1], AcquireInstruction) self.assertEqual(len(excluded.instructions), 4) @@ -540,20 +581,20 @@ def test_filter_intervals(self): filtered, excluded = \ self._filter_and_test_consistency(sched, time_ranges=[(0, 2), (8, 11), (61, 70)]) self.assertEqual(len(filtered.instructions), 0) - self.assertEqual(len(excluded.instructions), 5) + self.assertEqual(len(excluded.instructions), 6) # split instructions from multiple non-overlapping intervals, specified # as time ranges filtered, excluded = \ self._filter_and_test_consistency(sched, time_ranges=[(10, 15), (63, 93)]) self.assertEqual(len(filtered.instructions), 2) - self.assertEqual(len(excluded.instructions), 3) + self.assertEqual(len(excluded.instructions), 4) # split instructions from non-overlapping intervals, specified as Intervals filtered, excluded = \ self._filter_and_test_consistency(sched, intervals=[Interval(10, 15), Interval(63, 93)]) self.assertEqual(len(filtered.instructions), 2) - self.assertEqual(len(excluded.instructions), 3) + self.assertEqual(len(excluded.instructions), 4) def test_filter_multiple(self): """Test filter composition.""" @@ -563,8 +604,9 @@ def test_filter_multiple(self): sched = sched.insert(0, lp0(self.config.drive(0))) sched = sched.insert(10, lp0(self.config.drive(1))) sched = sched.insert(30, FrameChange(phase=-1.57)(self.config.drive(0))) - sched = sched.insert(60, acquire([self.config.acquire(i) for i in range(2)], - [MemorySlot(i) for i in range(2)])) + for i in range(2): + sched = sched.insert(60, acquire(self.config.acquire(i), MemorySlot(i))) + sched = sched.insert(90, lp0(self.config.drive(0))) # split instructions with filters on channel 0, of type PulseInstruction, @@ -577,7 +619,7 @@ def test_filter_multiple(self): self.assertIsInstance(inst, PulseInstruction) self.assertTrue(all([chan.index == 0 for chan in inst.channels])) self.assertTrue(25 <= time <= 100) - self.assertEqual(len(excluded.instructions), 4) + self.assertEqual(len(excluded.instructions), 5) self.assertTrue(excluded.instructions[0][1].channels[0] == DriveChannel(0)) self.assertTrue(excluded.instructions[2][0] == 30) @@ -592,6 +634,16 @@ 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 + filtered, excluded = self._filter_and_test_consistency(sched, + instruction_types=[ + AcquireInstruction], + time_ranges=[(25, 100)]) + self.assertTrue(len(excluded.instructions), 4) + for _, inst in filtered.instructions: + self.assertIsInstance(inst, AcquireInstruction) + self.assertTrue(len(filtered.instructions), 2) + def test_custom_filters(self): """Test custom filters.""" lp0 = self.linear(duration=3, slope=0.2, intercept=0.1) @@ -629,36 +681,36 @@ def test_empty_filters(self): sched = sched.insert(0, lp0(self.config.drive(0))) sched = sched.insert(10, lp0(self.config.drive(1))) sched = sched.insert(30, FrameChange(phase=-1.57)(self.config.drive(0))) - sched = sched.insert(60, acquire([self.config.acquire(i) for i in range(2)], - [MemorySlot(i) for i in range(2)])) + for i in range(2): + sched = sched.insert(60, acquire(self.config.acquire(i), MemorySlot(i))) sched = sched.insert(90, lp0(self.config.drive(0))) # empty channels filtered, excluded = self._filter_and_test_consistency(sched, channels=[]) self.assertTrue(len(filtered.instructions) == 0) - self.assertTrue(len(excluded.instructions) == 5) + self.assertTrue(len(excluded.instructions) == 6) # empty instruction_types filtered, excluded = self._filter_and_test_consistency(sched, instruction_types=[]) self.assertTrue(len(filtered.instructions) == 0) - self.assertTrue(len(excluded.instructions) == 5) + self.assertTrue(len(excluded.instructions) == 6) # empty time_ranges filtered, excluded = self._filter_and_test_consistency(sched, time_ranges=[]) self.assertTrue(len(filtered.instructions) == 0) - self.assertTrue(len(excluded.instructions) == 5) + self.assertTrue(len(excluded.instructions) == 6) # empty intervals filtered, excluded = self._filter_and_test_consistency(sched, intervals=[]) self.assertTrue(len(filtered.instructions) == 0) - self.assertTrue(len(excluded.instructions) == 5) + self.assertTrue(len(excluded.instructions) == 6) # empty channels with other non-empty filters filtered, excluded = self._filter_and_test_consistency(sched, channels=[], instruction_types=[PulseInstruction]) self.assertTrue(len(filtered.instructions) == 0) - self.assertTrue(len(excluded.instructions) == 5) + self.assertTrue(len(excluded.instructions) == 6) def _filter_and_test_consistency(self, schedule: Schedule, *args, **kwargs): """ diff --git a/test/python/qobj/test_pulse_converter.py b/test/python/qobj/test_pulse_converter.py index f2d2863c7bb7..b4bca999afe2 100644 --- a/test/python/qobj/test_pulse_converter.py +++ b/test/python/qobj/test_pulse_converter.py @@ -26,7 +26,7 @@ Drag) from qiskit.pulse.channels import (DriveChannel, ControlChannel, MeasureChannel, AcquireChannel, MemorySlot, RegisterSlot) -from qiskit.pulse.schedule import ParameterizedSchedule +from qiskit.pulse.schedule import ParameterizedSchedule, Schedule from qiskit.pulse import LoConfig @@ -134,9 +134,9 @@ def test_acquire(self): """Test converted qobj from AcquireInstruction.""" converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) command = Acquire(duration=10) - instruction = command([AcquireChannel(0)], - [MemorySlot(0)], - [RegisterSlot(0)]) + instruction = command(AcquireChannel(0), + MemorySlot(0), + RegisterSlot(0)) valid_qobj = PulseQobjInstruction( name='acquire', @@ -150,7 +150,7 @@ def test_acquire(self): self.assertEqual(converter(0, instruction), valid_qobj) # test without register - instruction = command([AcquireChannel(0)], [MemorySlot(0)]) + instruction = command(AcquireChannel(0), MemorySlot(0)) valid_qobj = PulseQobjInstruction( name='acquire', @@ -239,9 +239,10 @@ def test_acquire(self): cmd = Acquire(10, kernel=Kernel(name='test_kern', params={'test_params': 'test'}), discriminator=Discriminator(name='test_disc', params={'test_params': 1.0})) - instruction = cmd([AcquireChannel(i) for i in range(self.n_qubits)], - [MemorySlot(i) for i in range(self.n_qubits)], - [RegisterSlot(i) for i in range(self.n_qubits)]) + + schedule = Schedule() + for i in range(self.n_qubits): + schedule |= cmd(AcquireChannel(i), MemorySlot(i), RegisterSlot(i)) qobj = PulseQobjInstruction(name='acquire', t0=0, duration=10, qubits=[0, 1], memory_slot=[0, 1], register_slot=[0, 1], @@ -251,7 +252,7 @@ def test_acquire(self): name='test_disc', params={'test_params': 1.0})]) converted_instruction = self.converter(qobj) - self.assertEqual(converted_instruction.timeslots, instruction.timeslots) + self.assertEqual(converted_instruction.timeslots, schedule.timeslots) self.assertEqual(converted_instruction.instructions[0][-1].command, cmd) def test_snapshot(self): diff --git a/test/python/scheduler/test_basic_scheduler.py b/test/python/scheduler/test_basic_scheduler.py index 4be1ccb018d3..e9eba24b9baf 100644 --- a/test/python/scheduler/test_basic_scheduler.py +++ b/test/python/scheduler/test_basic_scheduler.py @@ -170,13 +170,14 @@ def test_measure_combined(self): qc.measure(q[1], c[1]) qc.measure(q[1], c[1]) sched = schedule(qc, self.backend, method="as_soon_as_possible") + acquire = Acquire(duration=10) expected = Schedule( self.inst_map.get('u2', [0], 3.14, 1.57), (28, self.inst_map.get('cx', [0, 1])), (50, self.inst_map.get('measure', [0, 1])), (60, self.inst_map.get('measure', [0, 1]).filter(channels=[MeasureChannel(1)])), - (60, Acquire(duration=10)([AcquireChannel(0), AcquireChannel(1)], - [MemorySlot(0), MemorySlot(1)]))) + (60, acquire(AcquireChannel(0), MemorySlot(0))), + (60, acquire(AcquireChannel(1), MemorySlot(1)))) self.assertEqual(sched.instructions, expected.instructions) def test_3q_schedule(self): @@ -439,13 +440,14 @@ def test_measure_combined(self): qc.measure(q[1], c[1]) qc.measure(q[1], c[1]) sched = schedule(qc, self.backend, method="as_soon_as_possible") + acquire = Acquire(duration=10) expected = Schedule( self.cmd_def.get('u2', [0], 3.14, 1.57), (28, self.cmd_def.get('cx', [0, 1])), (50, self.cmd_def.get('measure', [0, 1])), (60, self.cmd_def.get('measure', [0, 1]).filter(channels=[MeasureChannel(1)])), - (60, Acquire(duration=10)([AcquireChannel(0), AcquireChannel(1)], - [MemorySlot(0), MemorySlot(1)]))) + (60, acquire(AcquireChannel(0), MemorySlot(0))), + (60, acquire(AcquireChannel(1), MemorySlot(1)))) self.assertEqual(sched.instructions, expected.instructions) def test_3q_schedule(self): @@ -563,10 +565,11 @@ def test_user_mapping_for_memslots(self): qc = QuantumCircuit(q, c) qc.measure(q[0], c[1]) sched = schedule(qc, self.backend) + acquire = Acquire(duration=10) expected = Schedule( self.cmd_def.get('measure', [0, 1]).filter(channels=[MeasureChannel(0)]), - Acquire(duration=10)([AcquireChannel(0), AcquireChannel(1)], - [MemorySlot(1), MemorySlot(0)])) + acquire(AcquireChannel(0), MemorySlot(1)), + acquire(AcquireChannel(1), MemorySlot(0))) self.assertEqual(sched.instructions, expected.instructions) def test_user_mapping_for_memslots_3Q(self): @@ -579,11 +582,13 @@ def test_user_mapping_for_memslots_3Q(self): qc.measure(q[1], c[2]) qc.measure(q[2], c[0]) sched = schedule(qc, backend) + acquire = Acquire(duration=10) expected = Schedule( cmd_def.get('measure', [0, 1, 2]).filter( channels=[MeasureChannel(1), MeasureChannel(2)]), - Acquire(duration=10)([AcquireChannel(0), AcquireChannel(1), AcquireChannel(2)], - [MemorySlot(1), MemorySlot(2), MemorySlot(0)])) + acquire(AcquireChannel(0), MemorySlot(1)), + acquire(AcquireChannel(1), MemorySlot(2)), + acquire(AcquireChannel(2), MemorySlot(0))) self.assertEqual(sched.instructions, expected.instructions) def test_multiple_measure_in_3Q(self): @@ -596,11 +601,16 @@ def test_multiple_measure_in_3Q(self): qc.measure(q[0], c[2]) qc.measure(q[0], c[4]) sched = schedule(qc, backend) + + acquire = Acquire(duration=10) expected = Schedule( cmd_def.get('measure', [0, 1, 2]).filter(channels=[MeasureChannel(0)]), - Acquire(duration=10)([AcquireChannel(0), AcquireChannel(1), AcquireChannel(2)], - [MemorySlot(2), MemorySlot(0), MemorySlot(1)]), + acquire(AcquireChannel(0), MemorySlot(2)), + acquire(AcquireChannel(1), MemorySlot(0)), + acquire(AcquireChannel(2), MemorySlot(1)), (10, cmd_def.get('measure', [0, 1, 2]).filter(channels=[MeasureChannel(0)])), - (10, Acquire(duration=10)([AcquireChannel(0), AcquireChannel(1), AcquireChannel(2)], - [MemorySlot(4), MemorySlot(0), MemorySlot(1)]))) + (10, acquire(AcquireChannel(0), MemorySlot(4))), + (10, acquire(AcquireChannel(1), MemorySlot(0))), + (10, acquire(AcquireChannel(2), MemorySlot(1))) + ) self.assertEqual(sched.instructions, expected.instructions)