From 6a3b145d2ec45b9d4e7a08be85ea9781ad4afddd Mon Sep 17 00:00:00 2001 From: to24toro Date: Mon, 22 May 2023 10:42:53 +0900 Subject: [PATCH 01/30] fix measure_v2 --- qiskit/pulse/macros.py | 15 +------ test/python/pulse/test_macros.py | 74 +++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index d4ba4768ef57..5491ae4ee4af 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -63,16 +63,11 @@ def measure( # 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, + meas_map=meas_map or getattr(backend, "meas_map", [list(range(backend.num_qubits))]), qubit_mem_slots=qubit_mem_slots or dict(zip(qubits, range(len(qubits)))), measure_name=measure_name, ) @@ -198,12 +193,7 @@ def _measure_v2( channels.AcquireChannel(measure_qubit), ] ) - else: - default_sched = target.get_calibration(measure_name, (measure_qubit,)).filter( - channels=[ - channels.AcquireChannel(measure_qubit), - ] - ) + schedule += _schedule_remapping_memory_slot(default_sched, qubit_mem_slots) except KeyError as ex: raise exceptions.PulseError( "We could not find a default measurement schedule called '{}'. " @@ -211,7 +201,6 @@ def _measure_v2( "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 diff --git a/test/python/pulse/test_macros.py b/test/python/pulse/test_macros.py index a3869fd11b7d..e07d5ec11e76 100644 --- a/test/python/pulse/test_macros.py +++ b/test/python/pulse/test_macros.py @@ -24,7 +24,7 @@ ) from qiskit.pulse import macros from qiskit.pulse.exceptions import PulseError -from qiskit.providers.fake_provider import FakeOpenPulse2Q, FakeHanoiV2 +from qiskit.providers.fake_provider import FakeOpenPulse2Q, FakeHanoi, FakeHanoiV2 from qiskit.test import QiskitTestCase @@ -95,13 +95,8 @@ def test_measure_v2(self): """Test macro - measure with backendV2.""" sched = macros.measure(qubits=[0], backend=self.backend_v2) expected = self.backend_v2.target.get_calibration("measure", (0,)).filter( - channels=[ - MeasureChannel(0), - ] + channels=[MeasureChannel(0), AcquireChannel(0)] ) - measure_duration = expected.filter(instruction_types=[Play]).duration - for qubit in range(self.backend_v2.num_qubits): - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(qubit)) self.assertEqual(sched.instructions, expected.instructions) def test_measure_v2_sched_with_qubit_mem_slots(self): @@ -113,15 +108,7 @@ def test_measure_v2_sched_with_qubit_mem_slots(self): ] ) measure_duration = expected.filter(instruction_types=[Play]).duration - for qubit in range(self.backend_v2.num_qubits): - if qubit == 0: - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(2)) - elif qubit == 1: - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(0)) - elif qubit == 2: - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(1)) - else: - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(qubit)) + expected += Acquire(measure_duration, AcquireChannel(0), MemorySlot(2)) self.assertEqual(sched.instructions, expected.instructions) def test_measure_v2_sched_with_meas_map(self): @@ -138,8 +125,7 @@ def test_measure_v2_sched_with_meas_map(self): ] ) measure_duration = expected.filter(instruction_types=[Play]).duration - for qubit in range(self.backend_v2.num_qubits): - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(qubit)) + expected += Acquire(measure_duration, AcquireChannel(0), MemorySlot(0)) self.assertEqual(sched_with_meas_map_list.instructions, expected.instructions) self.assertEqual(sched_with_meas_map_dict.instructions, expected.instructions) @@ -159,10 +145,58 @@ def test_multiple_measure_v2(self): measure_duration = expected.filter(instruction_types=[Play]).duration expected += Acquire(measure_duration, AcquireChannel(0), MemorySlot(0)) expected += Acquire(measure_duration, AcquireChannel(1), MemorySlot(1)) - for qubit in range(2, self.backend_v2.num_qubits): - expected += Acquire(measure_duration, AcquireChannel(qubit), MemorySlot(qubit)) self.assertEqual(sched.instructions, expected.instructions) + def test_output_with_measure_v1_and_measure_v2(self): + """Test make outputs of measure_v1 and measure_v2 consistent.""" + sched_measure_v1 = macros.measure(qubits=[0, 1], backend=FakeHanoi()) + sched_measure_v2 = macros.measure(qubits=[0, 1], backend=self.backend_v2) + self.assertEqual(sched_measure_v1.instructions, sched_measure_v2.instructions) + + def test_output_with_measure_v1_and_measure_v2_sched_with_qubit_mem_slots(self): + """Test make outputs of measure_v1 and measure_v2 with custom qubit_mem_slots consistent.""" + sched_measure_v1 = macros.measure(qubits=[0], backend=FakeHanoi(), qubit_mem_slots={0: 2}) + sched_measure_v2 = macros.measure( + qubits=[0], backend=self.backend_v2, qubit_mem_slots={0: 2} + ) + self.assertEqual(sched_measure_v1.instructions, sched_measure_v2.instructions) + + def test_output_with_measure_v1_and_measure_v2_sched_with_meas_map(self): + """Test make outputs of measure_v1 and measure_v2 + with custom meas_map as list and dict consistent.""" + num_qubits_list_measure_v1 = list(range(FakeHanoi().configuration().num_qubits)) + num_qubits_list_measure_v2 = list(range(self.backend_v2.num_qubits)) + sched_with_meas_map_list_v1 = macros.measure( + qubits=[0], backend=FakeHanoi(), meas_map=[num_qubits_list_measure_v1] + ) + sched_with_meas_map_dict_v1 = macros.measure( + qubits=[0], + backend=FakeHanoi(), + meas_map={0: num_qubits_list_measure_v1, 1: num_qubits_list_measure_v1}, + ) + sched_with_meas_map_list_v2 = macros.measure( + qubits=[0], backend=self.backend_v2, meas_map=[num_qubits_list_measure_v2] + ) + sched_with_meas_map_dict_v2 = macros.measure( + qubits=[0], + backend=self.backend_v2, + meas_map={0: num_qubits_list_measure_v2, 1: num_qubits_list_measure_v2}, + ) + self.assertEqual( + sched_with_meas_map_list_v1.instructions, + sched_with_meas_map_list_v2.instructions, + ) + self.assertEqual( + sched_with_meas_map_dict_v1.instructions, + sched_with_meas_map_dict_v2.instructions, + ) + + def test_output_with_multiple_measure_v1_and_measure_v2(self): + """Test macro - consistent output of multiple qubit measure with backendV1 and backendV2.""" + sched_measure_v1 = macros.measure(qubits=[0, 1], backend=FakeHanoi()) + sched_measure_v2 = macros.measure(qubits=[0, 1], backend=self.backend_v2) + self.assertEqual(sched_measure_v1.instructions, sched_measure_v2.instructions) + class TestMeasureAll(QiskitTestCase): """Pulse measure all macro.""" From 3cd11923bb31af3a671e6e38f261033934bdb1f0 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 23 May 2023 12:13:35 +0900 Subject: [PATCH 02/30] modify measure_all --- qiskit/pulse/macros.py | 7 ++++++- test/python/pulse/test_macros.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index 5491ae4ee4af..60c835c6591c 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -215,7 +215,12 @@ def measure_all(backend) -> Schedule: Returns: A schedule corresponding to the inputs provided. """ - return measure(qubits=list(range(backend.configuration().n_qubits)), backend=backend) + # backend is V2. + if hasattr(backend, "target"): + qubits = list(range(backend.num_qubits)) + else: + qubits = list(range(backend.configuration().n_qubits)) + return measure(qubits=qubits, backend=backend) def _schedule_remapping_memory_slot( diff --git a/test/python/pulse/test_macros.py b/test/python/pulse/test_macros.py index e07d5ec11e76..8c82e0c569be 100644 --- a/test/python/pulse/test_macros.py +++ b/test/python/pulse/test_macros.py @@ -204,6 +204,7 @@ class TestMeasureAll(QiskitTestCase): def setUp(self): super().setUp() self.backend = FakeOpenPulse2Q() + self.backend_v2 = FakeHanoiV2() self.inst_map = self.backend.defaults().instruction_schedule_map def test_measure_all(self): @@ -211,3 +212,20 @@ def test_measure_all(self): sched = macros.measure_all(self.backend) expected = Schedule(self.inst_map.get("measure", [0, 1])) self.assertEqual(sched.instructions, expected.instructions) + + def test_measure_all_v2(self): + """Test measure_all function with backendV2.""" + backend_v1 = FakeHanoi() + sched = macros.measure_all(self.backend_v2) + expected = Schedule( + backend_v1.defaults().instruction_schedule_map.get( + "measure", list(range(backend_v1.configuration().num_qubits)) + ) + ) + self.assertEqual(sched.instructions, expected.instructions) + + def test_output_of_measure_all_with_backend_v1_and_v2(self): + """Test make outputs of measure_all with backendV1 and backendV2 consistent.""" + sched_measure_v1 = macros.measure_all(backend=FakeHanoi()) + sched_measure_v2 = macros.measure_all(backend=self.backend_v2) + self.assertEqual(sched_measure_v1.instructions, sched_measure_v2.instructions) From 58023f5bef5527e046ef1a2520ce45cbee797ca2 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 23 May 2023 22:31:15 +0900 Subject: [PATCH 03/30] dispatch backend --- qiskit/providers/backend.py | 20 ++++++ qiskit/pulse/builder.py | 78 +++++++++++++++++++----- test/python/providers/test_backend_v2.py | 26 ++++++++ 3 files changed, 110 insertions(+), 14 deletions(-) diff --git a/qiskit/providers/backend.py b/qiskit/providers/backend.py index d61810f765fd..201a9d6fe375 100644 --- a/qiskit/providers/backend.py +++ b/qiskit/providers/backend.py @@ -505,6 +505,26 @@ def qubit_properties( return self.target.qubit_properties[qubit] return [self.target.qubit_properties[q] for q in qubit] + def get_qubit_channels(self, qubit: int): + r"""Return a list of channels which operate on the given ``qubit``. + Returns: + List of ``Channel``\s operated on my the given ``qubit``. + """ + channels = [] + + # add multi-qubit channels + for node_qubits in self.coupling_map: + if qubit in node_qubits: + control_channels = self.control_channel(node_qubits) + if control_channels: + channels.extend(control_channels) + + # add single qubit channels + channels.append(self.drive_channel(qubit)) + channels.append(self.measure_channel(qubit)) + channels.append(self.acquire_channel(qubit)) + return channels + def drive_channel(self, qubit: int): """Return the drive channel for the given qubit. diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index 3a34e31e817d..e87c3a838d77 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -677,6 +677,9 @@ def get_context(self) -> ScheduleBlock: @_requires_backend def num_qubits(self): """Get the number of qubits in the backend.""" + # backendV2 + if hasattr(self.backend, "target"): + return self.backend.num_qubits return self.backend.configuration().n_qubits @property @@ -1105,6 +1108,8 @@ def num_qubits() -> int: .. note:: Requires the active builder context to have a backend set. """ + if hasattr(active_backend(), "target"): + return active_backend().num_qubits return active_backend().configuration().n_qubits @@ -1120,6 +1125,12 @@ def seconds_to_samples(seconds: Union[float, np.ndarray]) -> Union[int, np.ndarr Returns: The number of samples for the time to elapse """ + # backendV2 + if hasattr(active_backend(), "target"): + if isinstance(seconds, np.ndarray): + return (seconds / active_backend().dt).astype(int) + else: + return int(seconds / active_backend().dt) if isinstance(seconds, np.ndarray): return (seconds / active_backend().configuration().dt).astype(int) return int(seconds / active_backend().configuration().dt) @@ -1135,6 +1146,9 @@ def samples_to_seconds(samples: Union[int, np.ndarray]) -> Union[float, np.ndarr Returns: The time that elapses in ``samples``. """ + # backendV2 + if hasattr(active_backend(), "target"): + return samples * active_backend().dt return samples * active_backend().configuration().dt @@ -1163,6 +1177,9 @@ def qubit_channels(qubit: int) -> Set[chans.Channel]: such as in the case where significant crosstalk exists. """ + # backendV2 + if hasattr(active_backend(), "target"): + return set(active_backend().get_qubit_channels(qubit)) return set(active_backend().configuration().get_qubit_channels(qubit)) @@ -1648,7 +1665,11 @@ def frequency_offset( finally: if compensate_phase: duration = builder.get_context().duration - t0 - dt = active_backend().configuration().dt + # backendV2 + if hasattr(active_backend(), "target"): + dt = active_backend().dt + else: + dt = active_backend().configuration().dt accumulated_phase = 2 * np.pi * ((duration * dt * frequency) % 1) for channel in channels: shift_phase(-accumulated_phase, channel) @@ -1675,6 +1696,9 @@ def drive_channel(qubit: int) -> chans.DriveChannel: .. note:: Requires the active builder context to have a backend set. """ + # backendV2 + if hasattr(active_backend(), "target"): + return active_backend().drive_channel(qubit) return active_backend().configuration().drive(qubit) @@ -1695,6 +1719,9 @@ def measure_channel(qubit: int) -> chans.MeasureChannel: .. note:: Requires the active builder context to have a backend set. """ + # backendV2 + if hasattr(active_backend(), "target"): + return active_backend().measure_channel(qubit) return active_backend().configuration().measure(qubit) @@ -1715,6 +1742,9 @@ def acquire_channel(qubit: int) -> chans.AcquireChannel: .. note:: Requires the active builder context to have a backend set. """ + # backendV2 + if hasattr(active_backend(), "target"): + return active_backend().acquire_channel(qubit) return active_backend().configuration().acquire(qubit) @@ -1745,6 +1775,9 @@ def control_channels(*qubits: Iterable[int]) -> List[chans.ControlChannel]: List of control channels associated with the supplied ordered list of qubits. """ + # backendV2 + if hasattr(active_backend(), "target"): + return active_backend().control_channel(qubits) return active_backend().configuration().control(qubits=qubits) @@ -2428,13 +2461,21 @@ def measure( registers = list(registers) except TypeError: registers = [registers] - - measure_sched = macros.measure( - qubits=qubits, - inst_map=backend.defaults().instruction_schedule_map, - meas_map=backend.configuration().meas_map, - qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, - ) + # backendV2 + if hasattr(backend, "target"): + measure_sched = macros.measure( + qubits=qubits, + backend=backend, + meas_map=backend.meas_map, + qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, + ) + else: + measure_sched = macros.measure( + qubits=qubits, + inst_map=backend.defaults().instruction_schedule_map, + meas_map=backend.configuration().meas_map, + qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, + ) # note this is not a subroutine. # just a macro to automate combination of stimulus and acquisition. @@ -2478,12 +2519,21 @@ def measure_all() -> List[chans.MemorySlot]: backend = active_backend() qubits = range(num_qubits()) registers = [chans.MemorySlot(qubit) for qubit in qubits] - measure_sched = macros.measure( - qubits=qubits, - inst_map=backend.defaults().instruction_schedule_map, - meas_map=backend.configuration().meas_map, - qubit_mem_slots={qubit: qubit for qubit in qubits}, - ) + # backendV2 + if hasattr(backend, "target"): + measure_sched = macros.measure( + qubits=qubits, + backend=backend, + meas_map=backend.meas_map, + qubit_mem_slots={qubit: qubit for qubit in qubits}, + ) + else: + measure_sched = macros.measure( + qubits=qubits, + inst_map=backend.defaults().instruction_schedule_map, + meas_map=backend.configuration().meas_map, + qubit_mem_slots={qubit: qubit for qubit in qubits}, + ) # note this is not a subroutine. # just a macro to automate combination of stimulus and acquisition. diff --git a/test/python/providers/test_backend_v2.py b/test/python/providers/test_backend_v2.py index b3a45d7e1f53..9f1c11b8b842 100644 --- a/test/python/providers/test_backend_v2.py +++ b/test/python/providers/test_backend_v2.py @@ -14,6 +14,7 @@ # pylint: disable=missing-module-docstring import math +import collections from test import combine @@ -183,6 +184,31 @@ def test_transpile_parse_inst_map(self): inst_map = _parse_inst_map(inst_map=None, backend=self.backend) self.assertIsInstance(inst_map, InstructionScheduleMap) + def test_get_qubit_channels(self): + """Test to get all channels operated on a given qubit.""" + backend = FakeBogotaV2() + qubit = 1 + bogota_cr_channels_map = { + (4, 3): 7, + (3, 4): 6, + (3, 2): 5, + (2, 3): 4, + (1, 2): 2, + (2, 1): 3, + (1, 0): 1, + (0, 1): 0, + } + ref = [] + for node_qubits in bogota_cr_channels_map: + if qubit in node_qubits: + ref.append(channels.ControlChannel(bogota_cr_channels_map[node_qubits])) + ref.append(channels.DriveChannel(qubit)) + ref.append(channels.MeasureChannel(qubit)) + ref.append(channels.AcquireChannel(qubit)) + self.assertTrue( + collections.Counter(backend.get_qubit_channels(qubit)) == collections.Counter(list(ref)) + ) + @data(0, 1, 2, 3, 4) def test_drive_channel(self, qubit): """Test getting drive channel with qubit index.""" From 93a9262b345970dd51e73f81f65712cdd46fac6b Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 24 May 2023 12:17:15 +0900 Subject: [PATCH 04/30] add test of the builder with backendV2 --- test/python/pulse/test_builder.py | 198 +++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 5 deletions(-) diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index 1d7f09d5df78..62351df8abe2 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -21,7 +21,7 @@ from qiskit.pulse.instructions import directives from qiskit.pulse.transforms import target_qobj_transform from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeOpenPulse2Q +from qiskit.providers.fake_provider import FakeOpenPulse2Q, FakeMumbaiV2 from qiskit.providers.fake_provider.utils.configurable_backend import ( ConfigurableFakeBackend as ConfigurableBackend, ) @@ -29,7 +29,7 @@ class TestBuilder(QiskitTestCase): - """Test the pulse builder context.""" + """Test the pulse builder context with backendV1.""" def setUp(self): super().setUp() @@ -46,6 +46,14 @@ def assertScheduleEqual(self, program, target): self.assertEqual(target_qobj_transform(program), target_qobj_transform(target)) +class TestBuilderV2(QiskitTestCase): + """Test the pulse builder context with backendV1.""" + + def setUp(self): + super().setUp() + self.backend = FakeMumbaiV2() + + class TestBuilderBase(TestBuilder): """Test builder base.""" @@ -258,7 +266,6 @@ def test_phase_compensated_frequency_offset(self): """Test that the phase offset context properly compensates for phase accumulation.""" d0 = pulse.DriveChannel(0) - with pulse.build(self.backend) as schedule: with pulse.frequency_offset(1e9, d0, compensate_phase=True): pulse.delay(10, d0) @@ -270,7 +277,6 @@ def test_phase_compensated_frequency_offset(self): -2 * np.pi * ((1e9 * 10 * self.configuration.dt) % 1), d0 ) reference += instructions.ShiftFrequency(-1e9, d0) - self.assertScheduleEqual(schedule, reference) @@ -535,7 +541,6 @@ def test_barrier_on_qubits(self): """Test barrier directive on qubits.""" with pulse.build(self.backend) as schedule: pulse.barrier(0, 1) - reference = pulse.ScheduleBlock() reference += directives.RelativeBarrier( pulse.DriveChannel(0), @@ -1208,3 +1213,186 @@ def test_call_subroutine_with_parametrized_duration(self): pulse.call(subroutine) self.assertEqual(len(main.blocks), 1) + + +class TestBuilderBaseV2(TestBuilderV2, TestBuilderBase): + """Test builder base with backendV2.""" + + +class TestContextsV2(TestBuilderV2, TestContexts): + """Test builder contexts with backendV2.""" + + def test_phase_compensated_frequency_offset(self): + """Test that the phase offset context properly compensates for phase + accumulation with backendV2.""" + d0 = pulse.DriveChannel(0) + with pulse.build(self.backend) as schedule: + with pulse.frequency_offset(1e9, d0, compensate_phase=True): + pulse.delay(10, d0) + + reference = pulse.Schedule() + reference += instructions.ShiftFrequency(1e9, d0) + reference += instructions.Delay(10, d0) + reference += instructions.ShiftPhase( + -2 * np.pi * ((1e9 * 10 * self.backend.target.dt) % 1), d0 + ) + reference += instructions.ShiftFrequency(-1e9, d0) + self.assertScheduleEqual(schedule, reference) + + +class TestChannelsV2(TestBuilderV2, TestChannels): + """Test builder channels with backendV2.""" + + +class TestInstructionsV2(TestBuilderV2, TestInstructions): + """Test builder instructions with backendV2.""" + + +class TestDirectivesV2(TestBuilderV2, TestDirectives): + """Test builder directives with backendV2..""" + + def test_barrier_on_qubits(self): + """Test barrier directive on qubits with backendV2. + A part of qubits map of Mumbai + 0 -- 1 -- 4 -- + | + | + 2 + """ + with pulse.build(self.backend) as schedule: + pulse.barrier(0, 1) + reference = pulse.ScheduleBlock() + reference += directives.RelativeBarrier( + pulse.DriveChannel(0), + pulse.DriveChannel(1), + pulse.MeasureChannel(0), + pulse.MeasureChannel(1), + pulse.ControlChannel(0), + pulse.ControlChannel(1), + pulse.ControlChannel(2), + pulse.ControlChannel(3), + pulse.ControlChannel(4), + pulse.ControlChannel(8), + pulse.AcquireChannel(0), + pulse.AcquireChannel(1), + ) + self.assertEqual(schedule, reference) + + +class TestUtilitiesV2(TestBuilderV2, TestUtilities): + """Test builder utilities with backendV2.""" + + def test_num_qubits(self): + """Test builder utility to get number of qubits with backendV2.""" + with pulse.build(self.backend): + self.assertEqual(pulse.num_qubits(), 27) + + def test_samples_to_seconds(self): + """Test samples to time with backendV2""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + time = pulse.samples_to_seconds(100) + self.assertTrue(isinstance(time, float)) + self.assertEqual(pulse.samples_to_seconds(100), 10) + + def test_samples_to_seconds_array(self): + """Test samples to time (array format) with backendV2.""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + samples = np.array([100, 200, 300]) + times = pulse.samples_to_seconds(samples) + self.assertTrue(np.issubdtype(times.dtype, np.floating)) + np.testing.assert_allclose(times, np.array([10, 20, 30])) + + def test_seconds_to_samples(self): + """Test time to samples with backendV2""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + samples = pulse.seconds_to_samples(10) + self.assertTrue(isinstance(samples, int)) + self.assertEqual(pulse.seconds_to_samples(10), 100) + + def test_seconds_to_samples_array(self): + """Test time to samples (array format) with backendV2.""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + times = np.array([10, 20, 30]) + samples = pulse.seconds_to_samples(times) + self.assertTrue(np.issubdtype(samples.dtype, np.integer)) + np.testing.assert_allclose(pulse.seconds_to_samples(times), np.array([100, 200, 300])) + + +class TestMacrosV2(TestBuilderV2, TestMacros): + """Test builder macros with backendV2.""" + + def test_measure(self): + """Test utility function - measure with backendV2.""" + with pulse.build(self.backend) as schedule: + reg = pulse.measure(0) + + self.assertEqual(reg, pulse.MemorySlot(0)) + + reference = macros.measure(qubits=[0], backend=self.backend, meas_map=self.backend.meas_map) + + self.assertScheduleEqual(schedule, reference) + + def test_measure_multi_qubits(self): + """Test utility function - measure with multi qubits with backendV2.""" + with pulse.build(self.backend) as schedule: + regs = pulse.measure([0, 1]) + + self.assertListEqual(regs, [pulse.MemorySlot(0), pulse.MemorySlot(1)]) + + reference = macros.measure( + qubits=[0, 1], backend=self.backend, meas_map=self.backend.meas_map + ) + + self.assertScheduleEqual(schedule, reference) + + def test_measure_all(self): + """Test utility function - measure with backendV2..""" + with pulse.build(self.backend) as schedule: + regs = pulse.measure_all() + + self.assertEqual(regs, [pulse.MemorySlot(i) for i in range(self.backend.num_qubits)]) + reference = macros.measure_all(self.backend) + + self.assertScheduleEqual(schedule, reference) + + def test_delay_qubits(self): + """Test delaying on multiple qubits with backendV2 to make sure we don't insert delays twice.""" + with pulse.build(self.backend) as schedule: + pulse.delay_qubits(10, 0, 1) + + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + m0 = pulse.MeasureChannel(0) + m1 = pulse.MeasureChannel(1) + a0 = pulse.AcquireChannel(0) + a1 = pulse.AcquireChannel(1) + u0 = pulse.ControlChannel(0) + u1 = pulse.ControlChannel(1) + u2 = pulse.ControlChannel(2) + u3 = pulse.ControlChannel(3) + u4 = pulse.ControlChannel(4) + u8 = pulse.ControlChannel(8) + + reference = pulse.Schedule() + reference += instructions.Delay(10, d0) + reference += instructions.Delay(10, d1) + reference += instructions.Delay(10, m0) + reference += instructions.Delay(10, m1) + reference += instructions.Delay(10, a0) + reference += instructions.Delay(10, a1) + reference += instructions.Delay(10, u0) + reference += instructions.Delay(10, u1) + reference += instructions.Delay(10, u2) + reference += instructions.Delay(10, u3) + reference += instructions.Delay(10, u4) + reference += instructions.Delay(10, u8) + + self.assertScheduleEqual(schedule, reference) From 2cc5451af8e442b3236d0f6e2a68451ce287ed97 Mon Sep 17 00:00:00 2001 From: to24toro Date: Fri, 26 May 2023 23:08:09 +0900 Subject: [PATCH 05/30] reconfigure test codes and some func --- qiskit/providers/backend.py | 20 - qiskit/pulse/builder.py | 44 +- qiskit/pulse/utils.py | 22 + test/python/providers/test_backend_v2.py | 26 - test/python/pulse/test_builder.py | 195 +----- test/python/pulse/test_builder_v2.py | 762 +++++++++++++++++++++++ test/python/pulse/test_utils.py | 44 ++ 7 files changed, 843 insertions(+), 270 deletions(-) create mode 100644 test/python/pulse/test_builder_v2.py create mode 100644 test/python/pulse/test_utils.py diff --git a/qiskit/providers/backend.py b/qiskit/providers/backend.py index 201a9d6fe375..d61810f765fd 100644 --- a/qiskit/providers/backend.py +++ b/qiskit/providers/backend.py @@ -505,26 +505,6 @@ def qubit_properties( return self.target.qubit_properties[qubit] return [self.target.qubit_properties[q] for q in qubit] - def get_qubit_channels(self, qubit: int): - r"""Return a list of channels which operate on the given ``qubit``. - Returns: - List of ``Channel``\s operated on my the given ``qubit``. - """ - channels = [] - - # add multi-qubit channels - for node_qubits in self.coupling_map: - if qubit in node_qubits: - control_channels = self.control_channel(node_qubits) - if control_channels: - channels.extend(control_channels) - - # add single qubit channels - channels.append(self.drive_channel(qubit)) - channels.append(self.measure_channel(qubit)) - channels.append(self.acquire_channel(qubit)) - return channels - def drive_channel(self, qubit: int): """Return the drive channel for the given qubit. diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index e87c3a838d77..27a8d42748d7 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -498,6 +498,7 @@ from qiskit.pulse.instructions import directives from qiskit.pulse.schedule import Schedule, ScheduleBlock from qiskit.pulse.transforms.alignments import AlignmentKind +from qiskit.pulse.utils import get_qubit_channels #: contextvars.ContextVar[BuilderContext]: active builder @@ -1179,7 +1180,7 @@ def qubit_channels(qubit: int) -> Set[chans.Channel]: """ # backendV2 if hasattr(active_backend(), "target"): - return set(active_backend().get_qubit_channels(qubit)) + return set(get_qubit_channels(active_backend(), qubit)) return set(active_backend().configuration().get_qubit_channels(qubit)) @@ -2461,21 +2462,11 @@ def measure( registers = list(registers) except TypeError: registers = [registers] - # backendV2 - if hasattr(backend, "target"): - measure_sched = macros.measure( - qubits=qubits, - backend=backend, - meas_map=backend.meas_map, - qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, - ) - else: - measure_sched = macros.measure( - qubits=qubits, - inst_map=backend.defaults().instruction_schedule_map, - meas_map=backend.configuration().meas_map, - qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, - ) + measure_sched = macros.measure( + qubits=qubits, + backend=backend, + qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, + ) # note this is not a subroutine. # just a macro to automate combination of stimulus and acquisition. @@ -2519,21 +2510,12 @@ def measure_all() -> List[chans.MemorySlot]: backend = active_backend() qubits = range(num_qubits()) registers = [chans.MemorySlot(qubit) for qubit in qubits] - # backendV2 - if hasattr(backend, "target"): - measure_sched = macros.measure( - qubits=qubits, - backend=backend, - meas_map=backend.meas_map, - qubit_mem_slots={qubit: qubit for qubit in qubits}, - ) - else: - measure_sched = macros.measure( - qubits=qubits, - inst_map=backend.defaults().instruction_schedule_map, - meas_map=backend.configuration().meas_map, - qubit_mem_slots={qubit: qubit for qubit in qubits}, - ) + + measure_sched = macros.measure( + qubits=qubits, + backend=backend, + qubit_mem_slots={qubit: qubit for qubit in qubits}, + ) # note this is not a subroutine. # just a macro to automate combination of stimulus and acquisition. diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index b38b6913d7a1..55a4681e871d 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -19,6 +19,7 @@ from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.pulse.exceptions import UnassignedDurationError, QiskitError from qiskit.utils.deprecation import deprecate_func, deprecate_function +from qiskit.providers.backend import BackendV2 def format_meas_map(meas_map: List[List[int]]) -> Dict[int, List[int]]: @@ -125,3 +126,24 @@ def deprecated_functionality(func): stacklevel=2, since="0.22.0", )(func) + + +def get_qubit_channels(backend: BackendV2, qubit: int): + r"""Return a list of channels which operate on the given ``qubit``. + Returns: + List of ``Channel``\s operated on my the given ``qubit``. + """ + channels = [] + + # add multi-qubit channels + for node_qubits in backend.coupling_map: + if qubit in node_qubits: + control_channels = backend.control_channel(node_qubits) + if control_channels: + channels.extend(control_channels) + + # add single qubit channels + channels.append(backend.drive_channel(qubit)) + channels.append(backend.measure_channel(qubit)) + channels.append(backend.acquire_channel(qubit)) + return channels diff --git a/test/python/providers/test_backend_v2.py b/test/python/providers/test_backend_v2.py index 9f1c11b8b842..b3a45d7e1f53 100644 --- a/test/python/providers/test_backend_v2.py +++ b/test/python/providers/test_backend_v2.py @@ -14,7 +14,6 @@ # pylint: disable=missing-module-docstring import math -import collections from test import combine @@ -184,31 +183,6 @@ def test_transpile_parse_inst_map(self): inst_map = _parse_inst_map(inst_map=None, backend=self.backend) self.assertIsInstance(inst_map, InstructionScheduleMap) - def test_get_qubit_channels(self): - """Test to get all channels operated on a given qubit.""" - backend = FakeBogotaV2() - qubit = 1 - bogota_cr_channels_map = { - (4, 3): 7, - (3, 4): 6, - (3, 2): 5, - (2, 3): 4, - (1, 2): 2, - (2, 1): 3, - (1, 0): 1, - (0, 1): 0, - } - ref = [] - for node_qubits in bogota_cr_channels_map: - if qubit in node_qubits: - ref.append(channels.ControlChannel(bogota_cr_channels_map[node_qubits])) - ref.append(channels.DriveChannel(qubit)) - ref.append(channels.MeasureChannel(qubit)) - ref.append(channels.AcquireChannel(qubit)) - self.assertTrue( - collections.Counter(backend.get_qubit_channels(qubit)) == collections.Counter(list(ref)) - ) - @data(0, 1, 2, 3, 4) def test_drive_channel(self, qubit): """Test getting drive channel with qubit index.""" diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index 62351df8abe2..61f2985545e4 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -21,7 +21,7 @@ from qiskit.pulse.instructions import directives from qiskit.pulse.transforms import target_qobj_transform from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeOpenPulse2Q, FakeMumbaiV2 +from qiskit.providers.fake_provider import FakeOpenPulse2Q from qiskit.providers.fake_provider.utils.configurable_backend import ( ConfigurableFakeBackend as ConfigurableBackend, ) @@ -29,7 +29,7 @@ class TestBuilder(QiskitTestCase): - """Test the pulse builder context with backendV1.""" + """Test the pulse builder context.""" def setUp(self): super().setUp() @@ -46,14 +46,6 @@ def assertScheduleEqual(self, program, target): self.assertEqual(target_qobj_transform(program), target_qobj_transform(target)) -class TestBuilderV2(QiskitTestCase): - """Test the pulse builder context with backendV1.""" - - def setUp(self): - super().setUp() - self.backend = FakeMumbaiV2() - - class TestBuilderBase(TestBuilder): """Test builder base.""" @@ -1213,186 +1205,3 @@ def test_call_subroutine_with_parametrized_duration(self): pulse.call(subroutine) self.assertEqual(len(main.blocks), 1) - - -class TestBuilderBaseV2(TestBuilderV2, TestBuilderBase): - """Test builder base with backendV2.""" - - -class TestContextsV2(TestBuilderV2, TestContexts): - """Test builder contexts with backendV2.""" - - def test_phase_compensated_frequency_offset(self): - """Test that the phase offset context properly compensates for phase - accumulation with backendV2.""" - d0 = pulse.DriveChannel(0) - with pulse.build(self.backend) as schedule: - with pulse.frequency_offset(1e9, d0, compensate_phase=True): - pulse.delay(10, d0) - - reference = pulse.Schedule() - reference += instructions.ShiftFrequency(1e9, d0) - reference += instructions.Delay(10, d0) - reference += instructions.ShiftPhase( - -2 * np.pi * ((1e9 * 10 * self.backend.target.dt) % 1), d0 - ) - reference += instructions.ShiftFrequency(-1e9, d0) - self.assertScheduleEqual(schedule, reference) - - -class TestChannelsV2(TestBuilderV2, TestChannels): - """Test builder channels with backendV2.""" - - -class TestInstructionsV2(TestBuilderV2, TestInstructions): - """Test builder instructions with backendV2.""" - - -class TestDirectivesV2(TestBuilderV2, TestDirectives): - """Test builder directives with backendV2..""" - - def test_barrier_on_qubits(self): - """Test barrier directive on qubits with backendV2. - A part of qubits map of Mumbai - 0 -- 1 -- 4 -- - | - | - 2 - """ - with pulse.build(self.backend) as schedule: - pulse.barrier(0, 1) - reference = pulse.ScheduleBlock() - reference += directives.RelativeBarrier( - pulse.DriveChannel(0), - pulse.DriveChannel(1), - pulse.MeasureChannel(0), - pulse.MeasureChannel(1), - pulse.ControlChannel(0), - pulse.ControlChannel(1), - pulse.ControlChannel(2), - pulse.ControlChannel(3), - pulse.ControlChannel(4), - pulse.ControlChannel(8), - pulse.AcquireChannel(0), - pulse.AcquireChannel(1), - ) - self.assertEqual(schedule, reference) - - -class TestUtilitiesV2(TestBuilderV2, TestUtilities): - """Test builder utilities with backendV2.""" - - def test_num_qubits(self): - """Test builder utility to get number of qubits with backendV2.""" - with pulse.build(self.backend): - self.assertEqual(pulse.num_qubits(), 27) - - def test_samples_to_seconds(self): - """Test samples to time with backendV2""" - target = self.backend.target - target.dt = 0.1 - with pulse.build(self.backend): - time = pulse.samples_to_seconds(100) - self.assertTrue(isinstance(time, float)) - self.assertEqual(pulse.samples_to_seconds(100), 10) - - def test_samples_to_seconds_array(self): - """Test samples to time (array format) with backendV2.""" - target = self.backend.target - target.dt = 0.1 - with pulse.build(self.backend): - samples = np.array([100, 200, 300]) - times = pulse.samples_to_seconds(samples) - self.assertTrue(np.issubdtype(times.dtype, np.floating)) - np.testing.assert_allclose(times, np.array([10, 20, 30])) - - def test_seconds_to_samples(self): - """Test time to samples with backendV2""" - target = self.backend.target - target.dt = 0.1 - with pulse.build(self.backend): - samples = pulse.seconds_to_samples(10) - self.assertTrue(isinstance(samples, int)) - self.assertEqual(pulse.seconds_to_samples(10), 100) - - def test_seconds_to_samples_array(self): - """Test time to samples (array format) with backendV2.""" - target = self.backend.target - target.dt = 0.1 - with pulse.build(self.backend): - times = np.array([10, 20, 30]) - samples = pulse.seconds_to_samples(times) - self.assertTrue(np.issubdtype(samples.dtype, np.integer)) - np.testing.assert_allclose(pulse.seconds_to_samples(times), np.array([100, 200, 300])) - - -class TestMacrosV2(TestBuilderV2, TestMacros): - """Test builder macros with backendV2.""" - - def test_measure(self): - """Test utility function - measure with backendV2.""" - with pulse.build(self.backend) as schedule: - reg = pulse.measure(0) - - self.assertEqual(reg, pulse.MemorySlot(0)) - - reference = macros.measure(qubits=[0], backend=self.backend, meas_map=self.backend.meas_map) - - self.assertScheduleEqual(schedule, reference) - - def test_measure_multi_qubits(self): - """Test utility function - measure with multi qubits with backendV2.""" - with pulse.build(self.backend) as schedule: - regs = pulse.measure([0, 1]) - - self.assertListEqual(regs, [pulse.MemorySlot(0), pulse.MemorySlot(1)]) - - reference = macros.measure( - qubits=[0, 1], backend=self.backend, meas_map=self.backend.meas_map - ) - - self.assertScheduleEqual(schedule, reference) - - def test_measure_all(self): - """Test utility function - measure with backendV2..""" - with pulse.build(self.backend) as schedule: - regs = pulse.measure_all() - - self.assertEqual(regs, [pulse.MemorySlot(i) for i in range(self.backend.num_qubits)]) - reference = macros.measure_all(self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_delay_qubits(self): - """Test delaying on multiple qubits with backendV2 to make sure we don't insert delays twice.""" - with pulse.build(self.backend) as schedule: - pulse.delay_qubits(10, 0, 1) - - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - m0 = pulse.MeasureChannel(0) - m1 = pulse.MeasureChannel(1) - a0 = pulse.AcquireChannel(0) - a1 = pulse.AcquireChannel(1) - u0 = pulse.ControlChannel(0) - u1 = pulse.ControlChannel(1) - u2 = pulse.ControlChannel(2) - u3 = pulse.ControlChannel(3) - u4 = pulse.ControlChannel(4) - u8 = pulse.ControlChannel(8) - - reference = pulse.Schedule() - reference += instructions.Delay(10, d0) - reference += instructions.Delay(10, d1) - reference += instructions.Delay(10, m0) - reference += instructions.Delay(10, m1) - reference += instructions.Delay(10, a0) - reference += instructions.Delay(10, a1) - reference += instructions.Delay(10, u0) - reference += instructions.Delay(10, u1) - reference += instructions.Delay(10, u2) - reference += instructions.Delay(10, u3) - reference += instructions.Delay(10, u4) - reference += instructions.Delay(10, u8) - - self.assertScheduleEqual(schedule, reference) diff --git a/test/python/pulse/test_builder_v2.py b/test/python/pulse/test_builder_v2.py new file mode 100644 index 000000000000..1fc62540083e --- /dev/null +++ b/test/python/pulse/test_builder_v2.py @@ -0,0 +1,762 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +"""Test pulse builder with backendV2 context utilities.""" + +import numpy as np + +from qiskit import circuit, pulse +from qiskit.pulse import builder, macros + +from qiskit.pulse.instructions import directives +from qiskit.providers.fake_provider import FakeMumbaiV2 +from qiskit.pulse import instructions +from qiskit.test import QiskitTestCase +from qiskit.pulse import library, instructions + + +class TestBuilderV2(QiskitTestCase): + """Test the pulse builder context with backendV1.""" + + def setUp(self): + super().setUp() + self.backend = FakeMumbaiV2() + + +class TestBuilderBaseV2(TestBuilderV2): + """Test builder base.""" + + def test_schedule_supplied(self): + """Test that schedule is used if it is supplied to the builder.""" + d0 = pulse.DriveChannel(0) + with pulse.build(name="reference") as reference: + with pulse.align_sequential(): + pulse.delay(10, d0) + + with pulse.build(schedule=reference) as schedule: + pass + + self.assertScheduleEqual(schedule, reference) + self.assertEqual(schedule.name, "reference") + + def test_default_alignment_left(self): + """Test default left alignment setting.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(0) + + with pulse.build(default_alignment="left") as schedule: + pulse.delay(10, d0) + pulse.delay(20, d1) + + with pulse.build(self.backend) as reference: + with pulse.align_left(): + pulse.delay(10, d0) + pulse.delay(20, d1) + + self.assertScheduleEqual(schedule, reference) + + def test_default_alignment_right(self): + """Test default right alignment setting.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(0) + + with pulse.build(default_alignment="right") as schedule: + pulse.delay(10, d0) + pulse.delay(20, d1) + + with pulse.build() as reference: + with pulse.align_right(): + pulse.delay(10, d0) + pulse.delay(20, d1) + + self.assertScheduleEqual(schedule, reference) + + def test_default_alignment_sequential(self): + """Test default sequential alignment setting.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(0) + + with pulse.build(default_alignment="sequential") as schedule: + pulse.delay(10, d0) + pulse.delay(20, d1) + + with pulse.build() as reference: + with pulse.align_sequential(): + pulse.delay(10, d0) + pulse.delay(20, d1) + + self.assertScheduleEqual(schedule, reference) + + +class TestContextsV2(TestBuilderV2): + """Test builder contexts.""" + + def test_align_sequential(self): + """Test the sequential alignment context.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + + with pulse.build() as schedule: + with pulse.align_sequential(): + pulse.delay(3, d0) + pulse.delay(5, d1) + pulse.delay(7, d0) + + reference = pulse.Schedule() + # d0 + reference.insert(0, instructions.Delay(3, d0), inplace=True) + reference.insert(8, instructions.Delay(7, d0), inplace=True) + # d1 + reference.insert(3, instructions.Delay(5, d1), inplace=True) + + self.assertScheduleEqual(schedule, reference) + + def test_align_left(self): + """Test the left alignment context.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + d2 = pulse.DriveChannel(2) + + with pulse.build() as schedule: + with pulse.align_left(): + pulse.delay(11, d2) + pulse.delay(3, d0) + with pulse.align_left(): + pulse.delay(5, d1) + pulse.delay(7, d0) + + reference = pulse.Schedule() + # d0 + reference.insert(0, instructions.Delay(3, d0), inplace=True) + reference.insert(3, instructions.Delay(7, d0), inplace=True) + # d1 + reference.insert(3, instructions.Delay(5, d1), inplace=True) + # d2 + reference.insert(0, instructions.Delay(11, d2), inplace=True) + + self.assertScheduleEqual(schedule, reference) + + def test_align_right(self): + """Test the right alignment context.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + d2 = pulse.DriveChannel(2) + + with pulse.build() as schedule: + with pulse.align_right(): + with pulse.align_right(): + pulse.delay(11, d2) + pulse.delay(3, d0) + pulse.delay(13, d0) + pulse.delay(5, d1) + + reference = pulse.Schedule() + # d0 + reference.insert(8, instructions.Delay(3, d0), inplace=True) + reference.insert(11, instructions.Delay(13, d0), inplace=True) + # d1 + reference.insert(19, instructions.Delay(5, d1), inplace=True) + # d2 + reference.insert(0, instructions.Delay(11, d2), inplace=True) + + self.assertScheduleEqual(schedule, reference) + + def test_transpiler_settings(self): + """Test the transpiler settings context. + + Tests that two cx gates are optimized away with higher optimization level. + """ + twice_cx_qc = circuit.QuantumCircuit(2) + twice_cx_qc.cx(0, 1) + twice_cx_qc.cx(0, 1) + + with pulse.build(self.backend) as schedule: + with pulse.transpiler_settings(optimization_level=0): + builder.call(twice_cx_qc) + self.assertNotEqual(len(schedule.instructions), 0) + + with pulse.build(self.backend) as schedule: + with pulse.transpiler_settings(optimization_level=3): + builder.call(twice_cx_qc) + self.assertEqual(len(schedule.instructions), 0) + + def test_scheduler_settings(self): + """Test the circuit scheduler settings context.""" + inst_map = pulse.InstructionScheduleMap() + d0 = pulse.DriveChannel(0) + test_x_sched = pulse.Schedule() + test_x_sched += instructions.Delay(10, d0) + inst_map.add("x", (0,), test_x_sched) + + ref_sched = pulse.Schedule() + ref_sched += pulse.instructions.Call(test_x_sched) + + x_qc = circuit.QuantumCircuit(2) + x_qc.x(0) + + with pulse.build(backend=self.backend) as schedule: + with pulse.transpiler_settings(basis_gates=["x"]): + with pulse.circuit_scheduler_settings(inst_map=inst_map): + builder.call(x_qc) + + self.assertScheduleEqual(schedule, ref_sched) + + def test_phase_offset(self): + """Test the phase offset context.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + with pulse.phase_offset(3.14, d0): + pulse.delay(10, d0) + + reference = pulse.Schedule() + reference += instructions.ShiftPhase(3.14, d0) + reference += instructions.Delay(10, d0) + reference += instructions.ShiftPhase(-3.14, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_frequency_offset(self): + """Test the frequency offset context.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + with pulse.frequency_offset(1e9, d0): + pulse.delay(10, d0) + + reference = pulse.Schedule() + reference += instructions.ShiftFrequency(1e9, d0) + reference += instructions.Delay(10, d0) + reference += instructions.ShiftFrequency(-1e9, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_phase_compensated_frequency_offset(self): + """Test that the phase offset context properly compensates for phase + accumulation with backendV2.""" + d0 = pulse.DriveChannel(0) + with pulse.build(self.backend) as schedule: + with pulse.frequency_offset(1e9, d0, compensate_phase=True): + pulse.delay(10, d0) + + reference = pulse.Schedule() + reference += instructions.ShiftFrequency(1e9, d0) + reference += instructions.Delay(10, d0) + reference += instructions.ShiftPhase( + -2 * np.pi * ((1e9 * 10 * self.backend.target.dt) % 1), d0 + ) + reference += instructions.ShiftFrequency(-1e9, d0) + self.assertScheduleEqual(schedule, reference) + + +class TestChannelsV2(TestBuilderV2): + """Test builder channels.""" + + def test_drive_channel(self): + """Text context builder drive channel.""" + with pulse.build(self.backend): + self.assertEqual(pulse.drive_channel(0), pulse.DriveChannel(0)) + + def test_measure_channel(self): + """Text context builder measure channel.""" + with pulse.build(self.backend): + self.assertEqual(pulse.measure_channel(0), pulse.MeasureChannel(0)) + + def test_acquire_channel(self): + """Text context builder acquire channel.""" + with pulse.build(self.backend): + self.assertEqual(pulse.acquire_channel(0), pulse.AcquireChannel(0)) + + def test_control_channel(self): + """Text context builder control channel.""" + with pulse.build(self.backend): + self.assertEqual(pulse.control_channels(0, 1)[0], pulse.ControlChannel(0)) + + +class TestInstructionsV2(TestBuilderV2): + """Test builder instructions.""" + + def test_delay(self): + """Test delay instruction.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + pulse.delay(10, d0) + + reference = pulse.Schedule() + reference += instructions.Delay(10, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_play_parametric_pulse(self): + """Test play instruction with parametric pulse.""" + d0 = pulse.DriveChannel(0) + test_pulse = library.Constant(10, 1.0) + + with pulse.build() as schedule: + pulse.play(test_pulse, d0) + + reference = pulse.Schedule() + reference += instructions.Play(test_pulse, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_play_sample_pulse(self): + """Test play instruction with sample pulse.""" + d0 = pulse.DriveChannel(0) + test_pulse = library.Waveform([0.0, 0.0]) + + with pulse.build() as schedule: + pulse.play(test_pulse, d0) + + reference = pulse.Schedule() + reference += instructions.Play(test_pulse, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_play_array_pulse(self): + """Test play instruction on an array directly.""" + d0 = pulse.DriveChannel(0) + test_array = np.array([0.0, 0.0], dtype=np.complex_) + + with pulse.build() as schedule: + pulse.play(test_array, d0) + + reference = pulse.Schedule() + test_pulse = pulse.Waveform(test_array) + reference += instructions.Play(test_pulse, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_play_name_argument(self): + """Test name argument for play instruction.""" + d0 = pulse.DriveChannel(0) + test_pulse = library.Constant(10, 1.0) + + with pulse.build() as schedule: + pulse.play(test_pulse, channel=d0, name="new_name") + + self.assertEqual(schedule.instructions[0][1].name, "new_name") + + def test_acquire_memory_slot(self): + """Test acquire instruction into memory slot.""" + acquire0 = pulse.AcquireChannel(0) + mem0 = pulse.MemorySlot(0) + + with pulse.build() as schedule: + pulse.acquire(10, acquire0, mem0) + + reference = pulse.Schedule() + reference += pulse.Acquire(10, acquire0, mem_slot=mem0) + + self.assertScheduleEqual(schedule, reference) + + def test_acquire_register_slot(self): + """Test acquire instruction into register slot.""" + acquire0 = pulse.AcquireChannel(0) + reg0 = pulse.RegisterSlot(0) + + with pulse.build() as schedule: + pulse.acquire(10, acquire0, reg0) + + reference = pulse.Schedule() + reference += pulse.Acquire(10, acquire0, reg_slot=reg0) + + self.assertScheduleEqual(schedule, reference) + + def test_acquire_qubit(self): + """Test acquire instruction on qubit.""" + acquire0 = pulse.AcquireChannel(0) + mem0 = pulse.MemorySlot(0) + + with pulse.build() as schedule: + pulse.acquire(10, 0, mem0) + + reference = pulse.Schedule() + reference += pulse.Acquire(10, acquire0, mem_slot=mem0) + + self.assertScheduleEqual(schedule, reference) + + def test_instruction_name_argument(self): + """Test setting the name of an instruction.""" + d0 = pulse.DriveChannel(0) + + for instruction_method in [ + pulse.delay, + pulse.set_frequency, + pulse.set_phase, + pulse.shift_frequency, + pulse.shift_phase, + ]: + with pulse.build() as schedule: + instruction_method(0, d0, name="instruction_name") + self.assertEqual(schedule.instructions[0][1].name, "instruction_name") + + def test_set_frequency(self): + """Test set frequency instruction.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + pulse.set_frequency(1e9, d0) + + reference = pulse.Schedule() + reference += instructions.SetFrequency(1e9, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_shift_frequency(self): + """Test shift frequency instruction.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + pulse.shift_frequency(0.1e9, d0) + + reference = pulse.Schedule() + reference += instructions.ShiftFrequency(0.1e9, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_set_phase(self): + """Test set phase instruction.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + pulse.set_phase(3.14, d0) + + reference = pulse.Schedule() + reference += instructions.SetPhase(3.14, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_shift_phase(self): + """Test shift phase instruction.""" + d0 = pulse.DriveChannel(0) + + with pulse.build() as schedule: + pulse.shift_phase(3.14, d0) + + reference = pulse.Schedule() + reference += instructions.ShiftPhase(3.14, d0) + + self.assertScheduleEqual(schedule, reference) + + def test_snapshot(self): + """Test snapshot instruction.""" + with pulse.build() as schedule: + pulse.snapshot("test", "state") + + reference = pulse.Schedule() + reference += instructions.Snapshot("test", "state") + + self.assertScheduleEqual(schedule, reference) + + +class TestDirectivesV2(TestBuilderV2): + """Test builder directives.""" + + def test_barrier_with_align_right(self): + """Test barrier directive with right alignment context.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + d2 = pulse.DriveChannel(2) + + with pulse.build() as schedule: + with pulse.align_right(): + pulse.delay(3, d0) + pulse.barrier(d0, d1, d2) + pulse.delay(11, d2) + with pulse.align_right(): + pulse.delay(5, d1) + pulse.delay(7, d0) + + reference = pulse.Schedule() + # d0 + reference.insert(0, instructions.Delay(3, d0), inplace=True) + reference.insert(7, instructions.Delay(7, d0), inplace=True) + # d1 + reference.insert(9, instructions.Delay(5, d1), inplace=True) + # d2 + reference.insert(3, instructions.Delay(11, d2), inplace=True) + + self.assertScheduleEqual(schedule, reference) + + def test_barrier_with_align_left(self): + """Test barrier directive with left alignment context.""" + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + d2 = pulse.DriveChannel(2) + + with pulse.build() as schedule: + with pulse.align_left(): + pulse.delay(3, d0) + pulse.barrier(d0, d1, d2) + pulse.delay(11, d2) + with pulse.align_left(): + pulse.delay(5, d1) + pulse.delay(7, d0) + + reference = pulse.Schedule() + # d0 + reference.insert(0, instructions.Delay(3, d0), inplace=True) + reference.insert(3, instructions.Delay(7, d0), inplace=True) + # d1 + reference.insert(3, instructions.Delay(5, d1), inplace=True) + # d2 + reference.insert(3, instructions.Delay(11, d2), inplace=True) + + self.assertScheduleEqual(schedule, reference) + + def test_barrier_on_qubits(self): + """Test barrier directive on qubits with backendV2. + A part of qubits map of Mumbai + 0 -- 1 -- 4 -- + | + | + 2 + """ + with pulse.build(self.backend) as schedule: + pulse.barrier(0, 1) + reference = pulse.ScheduleBlock() + reference += directives.RelativeBarrier( + pulse.DriveChannel(0), + pulse.DriveChannel(1), + pulse.MeasureChannel(0), + pulse.MeasureChannel(1), + pulse.ControlChannel(0), + pulse.ControlChannel(1), + pulse.ControlChannel(2), + pulse.ControlChannel(3), + pulse.ControlChannel(4), + pulse.ControlChannel(8), + pulse.AcquireChannel(0), + pulse.AcquireChannel(1), + ) + self.assertEqual(schedule, reference) + + +class TestUtilitiesV2(TestBuilderV2): + """Test builder utilities.""" + + def test_active_backend(self): + """Test getting active builder backend.""" + with pulse.build(self.backend): + self.assertEqual(pulse.active_backend(), self.backend) + + def test_append_schedule(self): + """Test appending a schedule to the active builder.""" + d0 = pulse.DriveChannel(0) + reference = pulse.Schedule() + reference += instructions.Delay(10, d0) + + with pulse.build() as schedule: + builder.call(reference) + + self.assertScheduleEqual(schedule, reference) + + def test_append_instruction(self): + """Test appending an instruction to the active builder.""" + d0 = pulse.DriveChannel(0) + instruction = instructions.Delay(10, d0) + + with pulse.build() as schedule: + builder.append_instruction(instruction) + + self.assertScheduleEqual(schedule, (0, instruction)) + + def test_qubit_channels(self): + """Test getting the qubit channels of the active builder's backend.""" + with pulse.build(self.backend): + qubit_channels = pulse.qubit_channels(0) + + self.assertEqual( + qubit_channels, + { + pulse.DriveChannel(0), + pulse.MeasureChannel(0), + pulse.AcquireChannel(0), + pulse.ControlChannel(0), + pulse.ControlChannel(1), + }, + ) + + def test_active_transpiler_settings(self): + """Test setting settings of active builder's transpiler.""" + with pulse.build(self.backend): + self.assertFalse(pulse.active_transpiler_settings()) + with pulse.transpiler_settings(test_setting=1): + self.assertEqual(pulse.active_transpiler_settings()["test_setting"], 1) + + def test_active_circuit_scheduler_settings(self): + """Test setting settings of active builder's circuit scheduler.""" + with pulse.build(self.backend): + self.assertFalse(pulse.active_circuit_scheduler_settings()) + with pulse.circuit_scheduler_settings(test_setting=1): + self.assertEqual(pulse.active_circuit_scheduler_settings()["test_setting"], 1) + + def test_num_qubits(self): + """Test builder utility to get number of qubits with backendV2.""" + with pulse.build(self.backend): + self.assertEqual(pulse.num_qubits(), 27) + + def test_samples_to_seconds(self): + """Test samples to time with backendV2""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + time = pulse.samples_to_seconds(100) + self.assertTrue(isinstance(time, float)) + self.assertEqual(pulse.samples_to_seconds(100), 10) + + def test_samples_to_seconds_array(self): + """Test samples to time (array format) with backendV2.""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + samples = np.array([100, 200, 300]) + times = pulse.samples_to_seconds(samples) + self.assertTrue(np.issubdtype(times.dtype, np.floating)) + np.testing.assert_allclose(times, np.array([10, 20, 30])) + + def test_seconds_to_samples(self): + """Test time to samples with backendV2""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + samples = pulse.seconds_to_samples(10) + self.assertTrue(isinstance(samples, int)) + self.assertEqual(pulse.seconds_to_samples(10), 100) + + def test_seconds_to_samples_array(self): + """Test time to samples (array format) with backendV2.""" + target = self.backend.target + target.dt = 0.1 + with pulse.build(self.backend): + times = np.array([10, 20, 30]) + samples = pulse.seconds_to_samples(times) + self.assertTrue(np.issubdtype(samples.dtype, np.integer)) + np.testing.assert_allclose(pulse.seconds_to_samples(times), np.array([100, 200, 300])) + + +class TestMacrosV2(TestBuilderV2): + """Test builder macros with backendV2.""" + + def test_macro(self): + """Test builder macro decorator.""" + + @pulse.macro + def nested(a): + pulse.play(pulse.Gaussian(100, a, 20), pulse.drive_channel(0)) + return a * 2 + + @pulse.macro + def test(): + pulse.play(pulse.Constant(100, 1.0), pulse.drive_channel(0)) + output = nested(0.5) + return output + + with pulse.build(self.backend) as schedule: + output = test() + self.assertEqual(output, 0.5 * 2) + + reference = pulse.Schedule() + reference += pulse.Play(pulse.Constant(100, 1.0), pulse.DriveChannel(0)) + reference += pulse.Play(pulse.Gaussian(100, 0.5, 20), pulse.DriveChannel(0)) + + self.assertScheduleEqual(schedule, reference) + + def test_measure(self): + """Test utility function - measure with backendV2.""" + with pulse.build(self.backend) as schedule: + reg = pulse.measure(0) + + self.assertEqual(reg, pulse.MemorySlot(0)) + + reference = macros.measure(qubits=[0], backend=self.backend, meas_map=self.backend.meas_map) + + self.assertScheduleEqual(schedule, reference) + + def test_measure_multi_qubits(self): + """Test utility function - measure with multi qubits with backendV2.""" + with pulse.build(self.backend) as schedule: + regs = pulse.measure([0, 1]) + + self.assertListEqual(regs, [pulse.MemorySlot(0), pulse.MemorySlot(1)]) + + reference = macros.measure( + qubits=[0, 1], backend=self.backend, meas_map=self.backend.meas_map + ) + + self.assertScheduleEqual(schedule, reference) + + def test_measure_all(self): + """Test utility function - measure with backendV2..""" + with pulse.build(self.backend) as schedule: + regs = pulse.measure_all() + + self.assertEqual(regs, [pulse.MemorySlot(i) for i in range(self.backend.num_qubits)]) + reference = macros.measure_all(self.backend) + + self.assertScheduleEqual(schedule, reference) + + def test_delay_qubit(self): + """Test delaying on a qubit macro.""" + with pulse.build(self.backend) as schedule: + pulse.delay_qubits(10, 0) + + d0 = pulse.DriveChannel(0) + m0 = pulse.MeasureChannel(0) + a0 = pulse.AcquireChannel(0) + u0 = pulse.ControlChannel(0) + u1 = pulse.ControlChannel(1) + + reference = pulse.Schedule() + reference += instructions.Delay(10, d0) + reference += instructions.Delay(10, m0) + reference += instructions.Delay(10, a0) + reference += instructions.Delay(10, u0) + reference += instructions.Delay(10, u1) + + self.assertScheduleEqual(schedule, reference) + + def test_delay_qubits(self): + """Test delaying on multiple qubits with backendV2 to make sure we don't insert delays twice.""" + with pulse.build(self.backend) as schedule: + pulse.delay_qubits(10, 0, 1) + + d0 = pulse.DriveChannel(0) + d1 = pulse.DriveChannel(1) + m0 = pulse.MeasureChannel(0) + m1 = pulse.MeasureChannel(1) + a0 = pulse.AcquireChannel(0) + a1 = pulse.AcquireChannel(1) + u0 = pulse.ControlChannel(0) + u1 = pulse.ControlChannel(1) + u2 = pulse.ControlChannel(2) + u3 = pulse.ControlChannel(3) + u4 = pulse.ControlChannel(4) + u8 = pulse.ControlChannel(8) + + reference = pulse.Schedule() + reference += instructions.Delay(10, d0) + reference += instructions.Delay(10, d1) + reference += instructions.Delay(10, m0) + reference += instructions.Delay(10, m1) + reference += instructions.Delay(10, a0) + reference += instructions.Delay(10, a1) + reference += instructions.Delay(10, u0) + reference += instructions.Delay(10, u1) + reference += instructions.Delay(10, u2) + reference += instructions.Delay(10, u3) + reference += instructions.Delay(10, u4) + reference += instructions.Delay(10, u8) + + self.assertScheduleEqual(schedule, reference) diff --git a/test/python/pulse/test_utils.py b/test/python/pulse/test_utils.py new file mode 100644 index 000000000000..e55ef2588cac --- /dev/null +++ b/test/python/pulse/test_utils.py @@ -0,0 +1,44 @@ +# # This code is part of Qiskit. +# # +# # (C) Copyright IBM 2023. +# # +# # 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. + +# """Test cases for the pulse util modules.""" + +# import collections + +# from qiskit.providers.fake_provider.backends import FakeBogotaV2 +# from qiskit.pulse import channels + + +# def test_get_qubit_channels(self): +# """Test to get all channels operated on a given qubit.""" +# backend = FakeBogotaV2() +# qubit = 1 +# bogota_cr_channels_map = { +# (4, 3): 7, +# (3, 4): 6, +# (3, 2): 5, +# (2, 3): 4, +# (1, 2): 2, +# (2, 1): 3, +# (1, 0): 1, +# (0, 1): 0, +# } +# ref = [] +# for node_qubits in bogota_cr_channels_map: +# if qubit in node_qubits: +# ref.append(channels.ControlChannel(bogota_cr_channels_map[node_qubits])) +# ref.append(channels.DriveChannel(qubit)) +# ref.append(channels.MeasureChannel(qubit)) +# ref.append(channels.AcquireChannel(qubit)) +# self.assertTrue( +# collections.Counter(backend.get_qubit_channels(qubit)) == collections.Counter(list(ref)) +# ) From a21cc84d657c3cc5b9d1d6a8b0d22c733c8bae32 Mon Sep 17 00:00:00 2001 From: to24toro Date: Fri, 26 May 2023 23:38:43 +0900 Subject: [PATCH 06/30] refactoring --- qiskit/pulse/builder.py | 21 ++++---- qiskit/pulse/macros.py | 5 +- qiskit/pulse/utils.py | 8 ++- test/python/pulse/test_builder_v2.py | 11 +++- test/python/pulse/test_utils.py | 78 ++++++++++++++-------------- 5 files changed, 68 insertions(+), 55 deletions(-) diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index 27a8d42748d7..b8fcd9abdc65 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -495,6 +495,7 @@ library, transforms, ) +from qiskit.providers.backend import BackendV2 from qiskit.pulse.instructions import directives from qiskit.pulse.schedule import Schedule, ScheduleBlock from qiskit.pulse.transforms.alignments import AlignmentKind @@ -679,7 +680,7 @@ def get_context(self) -> ScheduleBlock: def num_qubits(self): """Get the number of qubits in the backend.""" # backendV2 - if hasattr(self.backend, "target"): + if isinstance(self.backend, BackendV2): return self.backend.num_qubits return self.backend.configuration().n_qubits @@ -1109,7 +1110,7 @@ def num_qubits() -> int: .. note:: Requires the active builder context to have a backend set. """ - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return active_backend().num_qubits return active_backend().configuration().n_qubits @@ -1127,7 +1128,7 @@ def seconds_to_samples(seconds: Union[float, np.ndarray]) -> Union[int, np.ndarr The number of samples for the time to elapse """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): if isinstance(seconds, np.ndarray): return (seconds / active_backend().dt).astype(int) else: @@ -1148,7 +1149,7 @@ def samples_to_seconds(samples: Union[int, np.ndarray]) -> Union[float, np.ndarr The time that elapses in ``samples``. """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return samples * active_backend().dt return samples * active_backend().configuration().dt @@ -1179,7 +1180,7 @@ def qubit_channels(qubit: int) -> Set[chans.Channel]: """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return set(get_qubit_channels(active_backend(), qubit)) return set(active_backend().configuration().get_qubit_channels(qubit)) @@ -1667,7 +1668,7 @@ def frequency_offset( if compensate_phase: duration = builder.get_context().duration - t0 # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): dt = active_backend().dt else: dt = active_backend().configuration().dt @@ -1698,7 +1699,7 @@ def drive_channel(qubit: int) -> chans.DriveChannel: .. note:: Requires the active builder context to have a backend set. """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return active_backend().drive_channel(qubit) return active_backend().configuration().drive(qubit) @@ -1721,7 +1722,7 @@ def measure_channel(qubit: int) -> chans.MeasureChannel: .. note:: Requires the active builder context to have a backend set. """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return active_backend().measure_channel(qubit) return active_backend().configuration().measure(qubit) @@ -1744,7 +1745,7 @@ def acquire_channel(qubit: int) -> chans.AcquireChannel: .. note:: Requires the active builder context to have a backend set. """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return active_backend().acquire_channel(qubit) return active_backend().configuration().acquire(qubit) @@ -1777,7 +1778,7 @@ def control_channels(*qubits: Iterable[int]) -> List[chans.ControlChannel]: of qubits. """ # backendV2 - if hasattr(active_backend(), "target"): + if isinstance(active_backend(), BackendV2): return active_backend().control_channel(qubits) return active_backend().configuration().control(qubits=qubits) diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index 60c835c6591c..7b1229aec2c7 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -18,6 +18,7 @@ from qiskit.pulse import channels, exceptions, instructions, utils from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap from qiskit.pulse.schedule import Schedule +from qiskit.providers.backend import BackendV2 if TYPE_CHECKING: @@ -62,7 +63,7 @@ def measure( """ # backend is V2. - if hasattr(backend, "target"): + if isinstance(backend, BackendV2): return _measure_v2( qubits=qubits, @@ -216,7 +217,7 @@ def measure_all(backend) -> Schedule: A schedule corresponding to the inputs provided. """ # backend is V2. - if hasattr(backend, "target"): + if isinstance(backend, BackendV2): qubits = list(range(backend.num_qubits)) else: qubits = list(range(backend.configuration().n_qubits)) diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index 55a4681e871d..24f643c98885 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -11,7 +11,9 @@ # that they have been altered from the originals. """Module for common pulse programming utilities.""" -from typing import List, Dict, Union +from __future__ import annotations + +from typing import List, Dict, Union, TYPE_CHECKING import warnings import numpy as np @@ -19,7 +21,9 @@ from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.pulse.exceptions import UnassignedDurationError, QiskitError from qiskit.utils.deprecation import deprecate_func, deprecate_function -from qiskit.providers.backend import BackendV2 + +if TYPE_CHECKING: + from qiskit.providers.backend import BackendV2 def format_meas_map(meas_map: List[List[int]]) -> Dict[int, List[int]]: diff --git a/test/python/pulse/test_builder_v2.py b/test/python/pulse/test_builder_v2.py index 1fc62540083e..e3b89a0f82ad 100644 --- a/test/python/pulse/test_builder_v2.py +++ b/test/python/pulse/test_builder_v2.py @@ -18,10 +18,10 @@ from qiskit.pulse import builder, macros from qiskit.pulse.instructions import directives +from qiskit.pulse.transforms import target_qobj_transform from qiskit.providers.fake_provider import FakeMumbaiV2 -from qiskit.pulse import instructions -from qiskit.test import QiskitTestCase from qiskit.pulse import library, instructions +from qiskit.test import QiskitTestCase class TestBuilderV2(QiskitTestCase): @@ -31,6 +31,13 @@ def setUp(self): super().setUp() self.backend = FakeMumbaiV2() + def assertScheduleEqual(self, program, target): + """Assert an error when two pulse programs are not equal. + + .. note:: Two programs are converted into standard execution format then compared. + """ + self.assertEqual(target_qobj_transform(program), target_qobj_transform(target)) + class TestBuilderBaseV2(TestBuilderV2): """Test builder base.""" diff --git a/test/python/pulse/test_utils.py b/test/python/pulse/test_utils.py index e55ef2588cac..2331a67ec7a1 100644 --- a/test/python/pulse/test_utils.py +++ b/test/python/pulse/test_utils.py @@ -1,44 +1,44 @@ -# # This code is part of Qiskit. -# # -# # (C) Copyright IBM 2023. -# # -# # 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. +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. -# """Test cases for the pulse util modules.""" +"""Test cases for the pulse util modules.""" -# import collections +import collections -# from qiskit.providers.fake_provider.backends import FakeBogotaV2 -# from qiskit.pulse import channels +from qiskit.providers.fake_provider.backends import FakeBogotaV2 +from qiskit.pulse import channels -# def test_get_qubit_channels(self): -# """Test to get all channels operated on a given qubit.""" -# backend = FakeBogotaV2() -# qubit = 1 -# bogota_cr_channels_map = { -# (4, 3): 7, -# (3, 4): 6, -# (3, 2): 5, -# (2, 3): 4, -# (1, 2): 2, -# (2, 1): 3, -# (1, 0): 1, -# (0, 1): 0, -# } -# ref = [] -# for node_qubits in bogota_cr_channels_map: -# if qubit in node_qubits: -# ref.append(channels.ControlChannel(bogota_cr_channels_map[node_qubits])) -# ref.append(channels.DriveChannel(qubit)) -# ref.append(channels.MeasureChannel(qubit)) -# ref.append(channels.AcquireChannel(qubit)) -# self.assertTrue( -# collections.Counter(backend.get_qubit_channels(qubit)) == collections.Counter(list(ref)) -# ) +def test_get_qubit_channels(self): + """Test to get all channels operated on a given qubit.""" + backend = FakeBogotaV2() + qubit = 1 + bogota_cr_channels_map = { + (4, 3): 7, + (3, 4): 6, + (3, 2): 5, + (2, 3): 4, + (1, 2): 2, + (2, 1): 3, + (1, 0): 1, + (0, 1): 0, + } + ref = [] + for node_qubits in bogota_cr_channels_map: + if qubit in node_qubits: + ref.append(channels.ControlChannel(bogota_cr_channels_map[node_qubits])) + ref.append(channels.DriveChannel(qubit)) + ref.append(channels.MeasureChannel(qubit)) + ref.append(channels.AcquireChannel(qubit)) + self.assertTrue( + collections.Counter(backend.get_qubit_channels(qubit)) == collections.Counter(list(ref)) + ) From b3607ee1e36b9ddf97650bc368bc44f367b5ae97 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 31 May 2023 15:29:29 +0900 Subject: [PATCH 07/30] add reno --- .../dispatch-builder-with-backends-28aff96f726ca9c5.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml diff --git a/releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml b/releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml new file mode 100644 index 000000000000..087c29c46ea8 --- /dev/null +++ b/releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed an failure that attributes of :class:`.BackendV2` backends cannot be called + by introducing dispatching with backends in a builder. From bd1e169fcbef034d6bfac768c3bc43258be96f62 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 31 May 2023 15:30:43 +0900 Subject: [PATCH 08/30] fix _measure_v2 --- qiskit/pulse/macros.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index 7b1229aec2c7..a98e6bd303f3 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -68,7 +68,7 @@ def measure( return _measure_v2( qubits=qubits, target=backend.target, - meas_map=meas_map or getattr(backend, "meas_map", [list(range(backend.num_qubits))]), + meas_map=meas_map or backend.num_qubits, qubit_mem_slots=qubit_mem_slots or dict(zip(qubits, range(len(qubits)))), measure_name=measure_name, ) From d70796fed37f5a89ce707e9ee29a80a6ae1d4666 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 31 May 2023 20:00:40 +0900 Subject: [PATCH 09/30] fix backend.meas_map in measure_v2 --- qiskit/pulse/macros.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index a98e6bd303f3..f597ce1b4aea 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -68,7 +68,7 @@ def measure( return _measure_v2( qubits=qubits, target=backend.target, - meas_map=meas_map or backend.num_qubits, + meas_map=meas_map or backend.meas_map, qubit_mem_slots=qubit_mem_slots or dict(zip(qubits, range(len(qubits)))), measure_name=measure_name, ) From 2a651ddce6ebd12a5b703e3d617b7009b462f85d Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 31 May 2023 20:03:57 +0900 Subject: [PATCH 10/30] fix reno --- ...a9c5.yaml => fix-dispatching-backends-28aff96f726ca9c5.yaml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename releasenotes/notes/{dispatch-builder-with-backends-28aff96f726ca9c5.yaml => fix-dispatching-backends-28aff96f726ca9c5.yaml} (86%) diff --git a/releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml b/releasenotes/notes/fix-dispatching-backends-28aff96f726ca9c5.yaml similarity index 86% rename from releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml rename to releasenotes/notes/fix-dispatching-backends-28aff96f726ca9c5.yaml index 087c29c46ea8..c1a1bd901aa5 100644 --- a/releasenotes/notes/dispatch-builder-with-backends-28aff96f726ca9c5.yaml +++ b/releasenotes/notes/fix-dispatching-backends-28aff96f726ca9c5.yaml @@ -1,5 +1,5 @@ --- fixes: - | - Fixed an failure that attributes of :class:`.BackendV2` backends cannot be called + Fixed an failure that attributes of :class:`.BackendV2` backends cannot be used by introducing dispatching with backends in a builder. From 8d925315c648977a970d80f214e78651651aa834 Mon Sep 17 00:00:00 2001 From: to24toro Date: Mon, 5 Jun 2023 13:54:37 +0900 Subject: [PATCH 11/30] delete get_qubit_channels from utils --- qiskit/pulse/utils.py | 21 ---------------- test/python/pulse/test_utils.py | 44 --------------------------------- 2 files changed, 65 deletions(-) delete mode 100644 test/python/pulse/test_utils.py diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index 24f643c98885..4a2a3452c65e 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -130,24 +130,3 @@ def deprecated_functionality(func): stacklevel=2, since="0.22.0", )(func) - - -def get_qubit_channels(backend: BackendV2, qubit: int): - r"""Return a list of channels which operate on the given ``qubit``. - Returns: - List of ``Channel``\s operated on my the given ``qubit``. - """ - channels = [] - - # add multi-qubit channels - for node_qubits in backend.coupling_map: - if qubit in node_qubits: - control_channels = backend.control_channel(node_qubits) - if control_channels: - channels.extend(control_channels) - - # add single qubit channels - channels.append(backend.drive_channel(qubit)) - channels.append(backend.measure_channel(qubit)) - channels.append(backend.acquire_channel(qubit)) - return channels diff --git a/test/python/pulse/test_utils.py b/test/python/pulse/test_utils.py deleted file mode 100644 index 2331a67ec7a1..000000000000 --- a/test/python/pulse/test_utils.py +++ /dev/null @@ -1,44 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# 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. - -"""Test cases for the pulse util modules.""" - -import collections - -from qiskit.providers.fake_provider.backends import FakeBogotaV2 -from qiskit.pulse import channels - - -def test_get_qubit_channels(self): - """Test to get all channels operated on a given qubit.""" - backend = FakeBogotaV2() - qubit = 1 - bogota_cr_channels_map = { - (4, 3): 7, - (3, 4): 6, - (3, 2): 5, - (2, 3): 4, - (1, 2): 2, - (2, 1): 3, - (1, 0): 1, - (0, 1): 0, - } - ref = [] - for node_qubits in bogota_cr_channels_map: - if qubit in node_qubits: - ref.append(channels.ControlChannel(bogota_cr_channels_map[node_qubits])) - ref.append(channels.DriveChannel(qubit)) - ref.append(channels.MeasureChannel(qubit)) - ref.append(channels.AcquireChannel(qubit)) - self.assertTrue( - collections.Counter(backend.get_qubit_channels(qubit)) == collections.Counter(list(ref)) - ) From 9b2b80d15c1ff4f667d830bda6350d478f15c9b0 Mon Sep 17 00:00:00 2001 From: to24toro Date: Mon, 5 Jun 2023 13:55:54 +0900 Subject: [PATCH 12/30] add get_qubits_channels in qubit_channels --- qiskit/pulse/builder.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index b8fcd9abdc65..826d0bff73df 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -499,7 +499,6 @@ from qiskit.pulse.instructions import directives from qiskit.pulse.schedule import Schedule, ScheduleBlock from qiskit.pulse.transforms.alignments import AlignmentKind -from qiskit.pulse.utils import get_qubit_channels #: contextvars.ContextVar[BuilderContext]: active builder @@ -1179,9 +1178,31 @@ def qubit_channels(qubit: int) -> Set[chans.Channel]: such as in the case where significant crosstalk exists. """ + + # implement as the inner function to avoid API change in 0.24. + def get_qubit_channels_v2(backend: BackendV2, qubit: int): + r"""Return a list of channels which operate on the given ``qubit``. + Returns: + List of ``Channel``\s operated on my the given ``qubit``. + """ + channels = [] + + # add multi-qubit channels + for node_qubits in backend.coupling_map: + if qubit in node_qubits: + control_channel = backend.control_channel(node_qubits) + if control_channel: + channels.extend(control_channel) + + # add single qubit channels + channels.append(backend.drive_channel(qubit)) + channels.append(backend.measure_channel(qubit)) + channels.append(backend.acquire_channel(qubit)) + return channels + # backendV2 if isinstance(active_backend(), BackendV2): - return set(get_qubit_channels(active_backend(), qubit)) + return set(get_qubit_channels_v2(active_backend(), qubit)) return set(active_backend().configuration().get_qubit_channels(qubit)) From 891c1bdb36c36c70a7b93c2e5a4d106fcd2b01f5 Mon Sep 17 00:00:00 2001 From: to24toro Date: Mon, 5 Jun 2023 13:57:25 +0900 Subject: [PATCH 13/30] recostruct test about the builder with backendV2 --- test/python/pulse/test_builder_v2.py | 420 +-------------------------- 1 file changed, 2 insertions(+), 418 deletions(-) diff --git a/test/python/pulse/test_builder_v2.py b/test/python/pulse/test_builder_v2.py index e3b89a0f82ad..1f194a9d3c29 100644 --- a/test/python/pulse/test_builder_v2.py +++ b/test/python/pulse/test_builder_v2.py @@ -20,12 +20,12 @@ from qiskit.pulse.instructions import directives from qiskit.pulse.transforms import target_qobj_transform from qiskit.providers.fake_provider import FakeMumbaiV2 -from qiskit.pulse import library, instructions +from qiskit.pulse import instructions from qiskit.test import QiskitTestCase class TestBuilderV2(QiskitTestCase): - """Test the pulse builder context with backendV1.""" + """Test the pulse builder context with backendV2.""" def setUp(self): super().setUp() @@ -39,144 +39,9 @@ def assertScheduleEqual(self, program, target): self.assertEqual(target_qobj_transform(program), target_qobj_transform(target)) -class TestBuilderBaseV2(TestBuilderV2): - """Test builder base.""" - - def test_schedule_supplied(self): - """Test that schedule is used if it is supplied to the builder.""" - d0 = pulse.DriveChannel(0) - with pulse.build(name="reference") as reference: - with pulse.align_sequential(): - pulse.delay(10, d0) - - with pulse.build(schedule=reference) as schedule: - pass - - self.assertScheduleEqual(schedule, reference) - self.assertEqual(schedule.name, "reference") - - def test_default_alignment_left(self): - """Test default left alignment setting.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(0) - - with pulse.build(default_alignment="left") as schedule: - pulse.delay(10, d0) - pulse.delay(20, d1) - - with pulse.build(self.backend) as reference: - with pulse.align_left(): - pulse.delay(10, d0) - pulse.delay(20, d1) - - self.assertScheduleEqual(schedule, reference) - - def test_default_alignment_right(self): - """Test default right alignment setting.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(0) - - with pulse.build(default_alignment="right") as schedule: - pulse.delay(10, d0) - pulse.delay(20, d1) - - with pulse.build() as reference: - with pulse.align_right(): - pulse.delay(10, d0) - pulse.delay(20, d1) - - self.assertScheduleEqual(schedule, reference) - - def test_default_alignment_sequential(self): - """Test default sequential alignment setting.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(0) - - with pulse.build(default_alignment="sequential") as schedule: - pulse.delay(10, d0) - pulse.delay(20, d1) - - with pulse.build() as reference: - with pulse.align_sequential(): - pulse.delay(10, d0) - pulse.delay(20, d1) - - self.assertScheduleEqual(schedule, reference) - - class TestContextsV2(TestBuilderV2): """Test builder contexts.""" - def test_align_sequential(self): - """Test the sequential alignment context.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - - with pulse.build() as schedule: - with pulse.align_sequential(): - pulse.delay(3, d0) - pulse.delay(5, d1) - pulse.delay(7, d0) - - reference = pulse.Schedule() - # d0 - reference.insert(0, instructions.Delay(3, d0), inplace=True) - reference.insert(8, instructions.Delay(7, d0), inplace=True) - # d1 - reference.insert(3, instructions.Delay(5, d1), inplace=True) - - self.assertScheduleEqual(schedule, reference) - - def test_align_left(self): - """Test the left alignment context.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - d2 = pulse.DriveChannel(2) - - with pulse.build() as schedule: - with pulse.align_left(): - pulse.delay(11, d2) - pulse.delay(3, d0) - with pulse.align_left(): - pulse.delay(5, d1) - pulse.delay(7, d0) - - reference = pulse.Schedule() - # d0 - reference.insert(0, instructions.Delay(3, d0), inplace=True) - reference.insert(3, instructions.Delay(7, d0), inplace=True) - # d1 - reference.insert(3, instructions.Delay(5, d1), inplace=True) - # d2 - reference.insert(0, instructions.Delay(11, d2), inplace=True) - - self.assertScheduleEqual(schedule, reference) - - def test_align_right(self): - """Test the right alignment context.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - d2 = pulse.DriveChannel(2) - - with pulse.build() as schedule: - with pulse.align_right(): - with pulse.align_right(): - pulse.delay(11, d2) - pulse.delay(3, d0) - pulse.delay(13, d0) - pulse.delay(5, d1) - - reference = pulse.Schedule() - # d0 - reference.insert(8, instructions.Delay(3, d0), inplace=True) - reference.insert(11, instructions.Delay(13, d0), inplace=True) - # d1 - reference.insert(19, instructions.Delay(5, d1), inplace=True) - # d2 - reference.insert(0, instructions.Delay(11, d2), inplace=True) - - self.assertScheduleEqual(schedule, reference) - def test_transpiler_settings(self): """Test the transpiler settings context. @@ -217,36 +82,6 @@ def test_scheduler_settings(self): self.assertScheduleEqual(schedule, ref_sched) - def test_phase_offset(self): - """Test the phase offset context.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - with pulse.phase_offset(3.14, d0): - pulse.delay(10, d0) - - reference = pulse.Schedule() - reference += instructions.ShiftPhase(3.14, d0) - reference += instructions.Delay(10, d0) - reference += instructions.ShiftPhase(-3.14, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_frequency_offset(self): - """Test the frequency offset context.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - with pulse.frequency_offset(1e9, d0): - pulse.delay(10, d0) - - reference = pulse.Schedule() - reference += instructions.ShiftFrequency(1e9, d0) - reference += instructions.Delay(10, d0) - reference += instructions.ShiftFrequency(-1e9, d0) - - self.assertScheduleEqual(schedule, reference) - def test_phase_compensated_frequency_offset(self): """Test that the phase offset context properly compensates for phase accumulation with backendV2.""" @@ -289,239 +124,9 @@ def test_control_channel(self): self.assertEqual(pulse.control_channels(0, 1)[0], pulse.ControlChannel(0)) -class TestInstructionsV2(TestBuilderV2): - """Test builder instructions.""" - - def test_delay(self): - """Test delay instruction.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - pulse.delay(10, d0) - - reference = pulse.Schedule() - reference += instructions.Delay(10, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_play_parametric_pulse(self): - """Test play instruction with parametric pulse.""" - d0 = pulse.DriveChannel(0) - test_pulse = library.Constant(10, 1.0) - - with pulse.build() as schedule: - pulse.play(test_pulse, d0) - - reference = pulse.Schedule() - reference += instructions.Play(test_pulse, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_play_sample_pulse(self): - """Test play instruction with sample pulse.""" - d0 = pulse.DriveChannel(0) - test_pulse = library.Waveform([0.0, 0.0]) - - with pulse.build() as schedule: - pulse.play(test_pulse, d0) - - reference = pulse.Schedule() - reference += instructions.Play(test_pulse, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_play_array_pulse(self): - """Test play instruction on an array directly.""" - d0 = pulse.DriveChannel(0) - test_array = np.array([0.0, 0.0], dtype=np.complex_) - - with pulse.build() as schedule: - pulse.play(test_array, d0) - - reference = pulse.Schedule() - test_pulse = pulse.Waveform(test_array) - reference += instructions.Play(test_pulse, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_play_name_argument(self): - """Test name argument for play instruction.""" - d0 = pulse.DriveChannel(0) - test_pulse = library.Constant(10, 1.0) - - with pulse.build() as schedule: - pulse.play(test_pulse, channel=d0, name="new_name") - - self.assertEqual(schedule.instructions[0][1].name, "new_name") - - def test_acquire_memory_slot(self): - """Test acquire instruction into memory slot.""" - acquire0 = pulse.AcquireChannel(0) - mem0 = pulse.MemorySlot(0) - - with pulse.build() as schedule: - pulse.acquire(10, acquire0, mem0) - - reference = pulse.Schedule() - reference += pulse.Acquire(10, acquire0, mem_slot=mem0) - - self.assertScheduleEqual(schedule, reference) - - def test_acquire_register_slot(self): - """Test acquire instruction into register slot.""" - acquire0 = pulse.AcquireChannel(0) - reg0 = pulse.RegisterSlot(0) - - with pulse.build() as schedule: - pulse.acquire(10, acquire0, reg0) - - reference = pulse.Schedule() - reference += pulse.Acquire(10, acquire0, reg_slot=reg0) - - self.assertScheduleEqual(schedule, reference) - - def test_acquire_qubit(self): - """Test acquire instruction on qubit.""" - acquire0 = pulse.AcquireChannel(0) - mem0 = pulse.MemorySlot(0) - - with pulse.build() as schedule: - pulse.acquire(10, 0, mem0) - - reference = pulse.Schedule() - reference += pulse.Acquire(10, acquire0, mem_slot=mem0) - - self.assertScheduleEqual(schedule, reference) - - def test_instruction_name_argument(self): - """Test setting the name of an instruction.""" - d0 = pulse.DriveChannel(0) - - for instruction_method in [ - pulse.delay, - pulse.set_frequency, - pulse.set_phase, - pulse.shift_frequency, - pulse.shift_phase, - ]: - with pulse.build() as schedule: - instruction_method(0, d0, name="instruction_name") - self.assertEqual(schedule.instructions[0][1].name, "instruction_name") - - def test_set_frequency(self): - """Test set frequency instruction.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - pulse.set_frequency(1e9, d0) - - reference = pulse.Schedule() - reference += instructions.SetFrequency(1e9, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_shift_frequency(self): - """Test shift frequency instruction.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - pulse.shift_frequency(0.1e9, d0) - - reference = pulse.Schedule() - reference += instructions.ShiftFrequency(0.1e9, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_set_phase(self): - """Test set phase instruction.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - pulse.set_phase(3.14, d0) - - reference = pulse.Schedule() - reference += instructions.SetPhase(3.14, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_shift_phase(self): - """Test shift phase instruction.""" - d0 = pulse.DriveChannel(0) - - with pulse.build() as schedule: - pulse.shift_phase(3.14, d0) - - reference = pulse.Schedule() - reference += instructions.ShiftPhase(3.14, d0) - - self.assertScheduleEqual(schedule, reference) - - def test_snapshot(self): - """Test snapshot instruction.""" - with pulse.build() as schedule: - pulse.snapshot("test", "state") - - reference = pulse.Schedule() - reference += instructions.Snapshot("test", "state") - - self.assertScheduleEqual(schedule, reference) - - class TestDirectivesV2(TestBuilderV2): """Test builder directives.""" - def test_barrier_with_align_right(self): - """Test barrier directive with right alignment context.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - d2 = pulse.DriveChannel(2) - - with pulse.build() as schedule: - with pulse.align_right(): - pulse.delay(3, d0) - pulse.barrier(d0, d1, d2) - pulse.delay(11, d2) - with pulse.align_right(): - pulse.delay(5, d1) - pulse.delay(7, d0) - - reference = pulse.Schedule() - # d0 - reference.insert(0, instructions.Delay(3, d0), inplace=True) - reference.insert(7, instructions.Delay(7, d0), inplace=True) - # d1 - reference.insert(9, instructions.Delay(5, d1), inplace=True) - # d2 - reference.insert(3, instructions.Delay(11, d2), inplace=True) - - self.assertScheduleEqual(schedule, reference) - - def test_barrier_with_align_left(self): - """Test barrier directive with left alignment context.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - d2 = pulse.DriveChannel(2) - - with pulse.build() as schedule: - with pulse.align_left(): - pulse.delay(3, d0) - pulse.barrier(d0, d1, d2) - pulse.delay(11, d2) - with pulse.align_left(): - pulse.delay(5, d1) - pulse.delay(7, d0) - - reference = pulse.Schedule() - # d0 - reference.insert(0, instructions.Delay(3, d0), inplace=True) - reference.insert(3, instructions.Delay(7, d0), inplace=True) - # d1 - reference.insert(3, instructions.Delay(5, d1), inplace=True) - # d2 - reference.insert(3, instructions.Delay(11, d2), inplace=True) - - self.assertScheduleEqual(schedule, reference) - def test_barrier_on_qubits(self): """Test barrier directive on qubits with backendV2. A part of qubits map of Mumbai @@ -558,27 +163,6 @@ def test_active_backend(self): with pulse.build(self.backend): self.assertEqual(pulse.active_backend(), self.backend) - def test_append_schedule(self): - """Test appending a schedule to the active builder.""" - d0 = pulse.DriveChannel(0) - reference = pulse.Schedule() - reference += instructions.Delay(10, d0) - - with pulse.build() as schedule: - builder.call(reference) - - self.assertScheduleEqual(schedule, reference) - - def test_append_instruction(self): - """Test appending an instruction to the active builder.""" - d0 = pulse.DriveChannel(0) - instruction = instructions.Delay(10, d0) - - with pulse.build() as schedule: - builder.append_instruction(instruction) - - self.assertScheduleEqual(schedule, (0, instruction)) - def test_qubit_channels(self): """Test getting the qubit channels of the active builder's backend.""" with pulse.build(self.backend): From 83a31b3f2d5a867af3265369877a686421f14e41 Mon Sep 17 00:00:00 2001 From: to24toro Date: Mon, 5 Jun 2023 17:45:30 +0900 Subject: [PATCH 14/30] add meas_map to Target class --- qiskit/transpiler/target.py | 8 ++++++++ test/python/transpiler/test_target.py | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 61999e6e7418..b1443ea7eea7 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -44,6 +44,7 @@ from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.providers.exceptions import BackendPropertyError from qiskit.pulse.exceptions import PulseError +from qiskit.pulse.utils import format_meas_map from qiskit.utils.deprecation import deprecate_arg, deprecate_func from qiskit.exceptions import QiskitError @@ -239,6 +240,7 @@ class Target(Mapping): "_non_global_strict_basis", "qubit_properties", "_global_operations", + "meas_map", ) @deprecate_arg("aquire_alignment", new_alias="acquire_alignment", since="0.23.0") @@ -252,6 +254,7 @@ def __init__( pulse_alignment=1, acquire_alignment=1, qubit_properties=None, + meas_map=None, ): """ Create a new Target object @@ -287,6 +290,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, dict): List of sets of qubits that must be measured together. Raises: ValueError: If both ``num_qubits`` and ``qubit_properties`` are both defined and the value of ``num_qubits`` differs from the length of @@ -322,6 +326,7 @@ def __init__( "length of the input qubit_properties list" ) self.qubit_properties = qubit_properties + self.meas_map = format_meas_map(meas_map) if isinstance(meas_map, list) else meas_map def add_instruction(self, instruction, properties=None, name=None): """Add a new instruction to the :class:`~qiskit.transpiler.Target` @@ -1215,6 +1220,7 @@ def from_configuration( inst_map: Optional[InstructionScheduleMap] = None, backend_properties: Optional[BackendProperties] = None, instruction_durations: Optional[InstructionDurations] = None, + meas_map: Optional[Union[List[List[int]], Dict[int, List[int]]]] = None, dt: Optional[float] = None, timing_constraints: Optional[TimingConstraints] = None, custom_name_mapping: Optional[Dict[str, Any]] = None, @@ -1263,6 +1269,7 @@ def from_configuration( instruction_durations: Optional instruction durations for instructions. If specified it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. + meas_map: List of sets of qubits that must be measured together. dt: The system time resolution of input signals in seconds timing_constraints: Optional timing constraints to include in the :class:`~.Target` @@ -1306,6 +1313,7 @@ def from_configuration( pulse_alignment=pulse_alignment, acquire_alignment=acquire_alignment, qubit_properties=qubit_properties, + meas_map=format_meas_map(meas_map) if isinstance(meas_map, list) else meas_map, ) name_mapping = get_standard_gate_name_mapping() if custom_name_mapping is not None: diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index d634741cc1bc..e6e7a55b3071 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -36,6 +36,7 @@ from qiskit import pulse from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap from qiskit.pulse.calibration_entries import CalibrationPublisher, ScheduleDef +from qiskit.pulse.utils import format_meas_map from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.instruction_durations import InstructionDurations from qiskit.transpiler.timing_constraints import TimingConstraints @@ -1903,6 +1904,16 @@ def test_inst_map(self): self.assertEqual(target.pulse_alignment, constraints.pulse_alignment) self.assertEqual(target.acquire_alignment, constraints.acquire_alignment) + def test_meas_map(self): + fake_backend = FakeVigo() + config = fake_backend.configuration() + target = Target.from_configuration( + basis_gates=config.basis_gates, + meas_map=config.meas_map, + ) + meas_map = format_meas_map(config.meas_map) + self.assertEqual(meas_map, target.meas_map) + def test_custom_basis_gates(self): basis_gates = ["my_x", "cx"] custom_name_mapping = {"my_x": XGate()} From cf4357ba93b0710e5054c7e5bea322e5c7d6a6f2 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 13 Jun 2023 15:28:09 +0900 Subject: [PATCH 15/30] fix after mergin main branch --- qiskit/pulse/utils.py | 7 +------ qiskit/transpiler/target.py | 3 ++- test/python/pulse/test_builder.py | 3 +++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index 4a2a3452c65e..b38b6913d7a1 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -11,9 +11,7 @@ # that they have been altered from the originals. """Module for common pulse programming utilities.""" -from __future__ import annotations - -from typing import List, Dict, Union, TYPE_CHECKING +from typing import List, Dict, Union import warnings import numpy as np @@ -22,9 +20,6 @@ from qiskit.pulse.exceptions import UnassignedDurationError, QiskitError from qiskit.utils.deprecation import deprecate_func, deprecate_function -if TYPE_CHECKING: - from qiskit.providers.backend import BackendV2 - def format_meas_map(meas_map: List[List[int]]) -> Dict[int, List[int]]: """ diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index c7d6ca9bd1c6..d11c0bcdccdb 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -21,7 +21,7 @@ import itertools -from typing import Any +from typing import Union, Optional, Dict, List, Any from collections.abc import Mapping from collections import defaultdict import datetime @@ -1220,6 +1220,7 @@ def from_configuration( inst_map: InstructionScheduleMap | None = None, backend_properties: BackendProperties | None = None, instruction_durations: InstructionDurations | None = None, + meas_map: Optional[Union[List[List[int]], Dict[int, List[int]]]] = None, dt: float | None = None, timing_constraints: TimingConstraints | None = None, custom_name_mapping: dict[str, Any] | None = None, diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index 61f2985545e4..1d7f09d5df78 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -258,6 +258,7 @@ def test_phase_compensated_frequency_offset(self): """Test that the phase offset context properly compensates for phase accumulation.""" d0 = pulse.DriveChannel(0) + with pulse.build(self.backend) as schedule: with pulse.frequency_offset(1e9, d0, compensate_phase=True): pulse.delay(10, d0) @@ -269,6 +270,7 @@ def test_phase_compensated_frequency_offset(self): -2 * np.pi * ((1e9 * 10 * self.configuration.dt) % 1), d0 ) reference += instructions.ShiftFrequency(-1e9, d0) + self.assertScheduleEqual(schedule, reference) @@ -533,6 +535,7 @@ def test_barrier_on_qubits(self): """Test barrier directive on qubits.""" with pulse.build(self.backend) as schedule: pulse.barrier(0, 1) + reference = pulse.ScheduleBlock() reference += directives.RelativeBarrier( pulse.DriveChannel(0), From 407f87d192ef1b76683ddf9ff81d2b9b4034d47e Mon Sep 17 00:00:00 2001 From: Kento Ueda Date: Sun, 18 Jun 2023 18:29:49 +0900 Subject: [PATCH 16/30] fix documents about meas_map --- qiskit/transpiler/target.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index d11c0bcdccdb..ddd3fcbe947e 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -290,7 +290,9 @@ 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, dict): List of sets of qubits that must be measured together. + meas_map(list, dict): Dict or List of sets of qubits that must be + measured together. If the input of meas_map is List,the meas_map + is converted to Dict by format_meas_map. Raises: ValueError: If both ``num_qubits`` and ``qubit_properties`` are both defined and the value of ``num_qubits`` differs from the length of @@ -1269,7 +1271,9 @@ def from_configuration( instruction_durations: Optional instruction durations for instructions. If specified it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. - meas_map: List of sets of qubits that must be measured together. + meas_map(list, dict): Dict or List of sets of qubits that must be + measured together. If the input of meas_map is List,the meas_map + is converted to Dict by format_meas_map. dt: The system time resolution of input signals in seconds timing_constraints: Optional timing constraints to include in the :class:`~.Target` From 0b3404082b7de4e55096a9d7486f1c184175e035 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 27 Jun 2023 16:53:19 +0900 Subject: [PATCH 17/30] format target.py --- qiskit/transpiler/target.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index ddd3fcbe947e..9239603ac677 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -290,8 +290,8 @@ 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, dict): Dict or List of sets of qubits that must be - measured together. If the input of meas_map is List,the meas_map + meas_map(list, dict): Dict or List of sets of qubits that must be + measured together. If the input of meas_map is List,the meas_map is converted to Dict by format_meas_map. Raises: ValueError: If both ``num_qubits`` and ``qubit_properties`` are both @@ -1271,8 +1271,8 @@ def from_configuration( instruction_durations: Optional instruction durations for instructions. If specified it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. - meas_map(list, dict): Dict or List of sets of qubits that must be - measured together. If the input of meas_map is List,the meas_map + meas_map(list, dict): Dict or List of sets of qubits that must be + measured together. If the input of meas_map is List,the meas_map is converted to Dict by format_meas_map. dt: The system time resolution of input signals in seconds timing_constraints: Optional timing constraints to include in the From 985a552795e187f711ef4ca08bb0db05fd8e4b7a Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 27 Jun 2023 16:53:26 +0900 Subject: [PATCH 18/30] add reno --- .../notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml diff --git a/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml b/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml new file mode 100644 index 000000000000..ce48c912e16c --- /dev/null +++ b/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Added an argument ``meas_map`` to :class:`~.Target`. \ No newline at end of file From ca03dfffbb8d19affb568befbf4ad852e2543455 Mon Sep 17 00:00:00 2001 From: to24toro Date: Thu, 6 Jul 2023 11:18:16 +0900 Subject: [PATCH 19/30] add meas_map to target in convert_to_target --- qiskit/providers/backend_compat.py | 8 ++++++-- qiskit/providers/fake_provider/utils/backend_converter.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index b2f0cb3ce56d..3c02dbce62a2 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -70,7 +70,11 @@ def convert_to_target( if filter_faulty: faulty_qubits = set(properties.faulty_qubits()) qubit_properties = qubit_props_list_from_props(properties=properties) - target = Target(num_qubits=configuration.n_qubits, qubit_properties=qubit_properties) + target = Target( + num_qubits=configuration.n_qubits, + qubit_properties=qubit_properties, + meas_map=configuration.meas_map, + ) # Parse instructions gates: Dict[str, Any] = {} for gate in properties.gates: @@ -122,7 +126,7 @@ def convert_to_target( target.add_instruction(Measure(), measure_props) # Parse from configuration because properties doesn't exist else: - target = Target(num_qubits=configuration.n_qubits) + target = Target(num_qubits=configuration.n_qubits, meas_map=configuration.meas_map) for gate in configuration.gates: name = gate.name gate_props = ( diff --git a/qiskit/providers/fake_provider/utils/backend_converter.py b/qiskit/providers/fake_provider/utils/backend_converter.py index 25d9aebba153..649bdc8dbd90 100644 --- a/qiskit/providers/fake_provider/utils/backend_converter.py +++ b/qiskit/providers/fake_provider/utils/backend_converter.py @@ -43,7 +43,7 @@ def convert_to_target(conf_dict: dict, props_dict: dict = None, defs_dict: dict qubit_props = None if props_dict: qubit_props = qubit_props_from_props(props_dict) - target = Target(qubit_properties=qubit_props) + target = Target(qubit_properties=qubit_props, meas_map=conf_dict.get("meas_map")) # Parse from properties if it exsits if props_dict is not None: # Parse instructions From dc454054121703310ec4abbf0f969130f5d6d2e3 Mon Sep 17 00:00:00 2001 From: to24toro Date: Thu, 6 Jul 2023 14:23:53 +0900 Subject: [PATCH 20/30] add the more description about meas_map --- qiskit/transpiler/target.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 9239603ac677..d4b41bfdc41b 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -290,10 +290,9 @@ 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, dict): Dict or List of sets of qubits that must be - measured together. If the input of meas_map is List,the meas_map - is converted to Dict by format_meas_map. - Raises: + meas_map(list): A list of sets of qubits that must be + measured together. The qubits which should be measured concurrently + is provided as the nested list like [[0, 1], [2, 3, 4]]. ValueError: If both ``num_qubits`` and ``qubit_properties`` are both defined and the value of ``num_qubits`` differs from the length of ``qubit_properties``. @@ -328,7 +327,7 @@ def __init__( "length of the input qubit_properties list" ) self.qubit_properties = qubit_properties - self.meas_map = format_meas_map(meas_map) if isinstance(meas_map, list) else meas_map + self.meas_map = meas_map def add_instruction(self, instruction, properties=None, name=None): """Add a new instruction to the :class:`~qiskit.transpiler.Target` @@ -1222,7 +1221,7 @@ def from_configuration( inst_map: InstructionScheduleMap | None = None, backend_properties: BackendProperties | None = None, instruction_durations: InstructionDurations | None = None, - meas_map: Optional[Union[List[List[int]], Dict[int, List[int]]]] = None, + meas_map: Optional[List[List[int]]] = None, dt: float | None = None, timing_constraints: TimingConstraints | None = None, custom_name_mapping: dict[str, Any] | None = None, @@ -1271,9 +1270,9 @@ def from_configuration( instruction_durations: Optional instruction durations for instructions. If specified it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. - meas_map(list, dict): Dict or List of sets of qubits that must be - measured together. If the input of meas_map is List,the meas_map - is converted to Dict by format_meas_map. + meas_map(list): A list of sets of qubits that must be + measured together. The qubits which should be measured concurrently + is provided as the nested list like [[0, 1], [2, 3, 4]]. dt: The system time resolution of input signals in seconds timing_constraints: Optional timing constraints to include in the :class:`~.Target` @@ -1317,7 +1316,7 @@ def from_configuration( pulse_alignment=pulse_alignment, acquire_alignment=acquire_alignment, qubit_properties=qubit_properties, - meas_map=format_meas_map(meas_map) if isinstance(meas_map, list) else meas_map, + meas_map=meas_map, ) name_mapping = get_standard_gate_name_mapping() if custom_name_mapping is not None: From 3cc4189add52b03be74eee4854108fc574153f8f Mon Sep 17 00:00:00 2001 From: Kento Ueda <38037695+to24toro@users.noreply.github.com> Date: Thu, 6 Jul 2023 14:24:33 +0900 Subject: [PATCH 21/30] Update releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml Co-authored-by: Naoki Kanazawa --- ...ble_target_aware_meas_map-0d8542402a74e9d8.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml b/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml index ce48c912e16c..5547c3d4cfb5 100644 --- a/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml +++ b/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml @@ -1,4 +1,16 @@ --- features: - | - Added an argument ``meas_map`` to :class:`~.Target`. \ No newline at end of file + Added :attr:`~.Target.meas_map` which represents a hardware constraint of qubits + measured concurrently. This constraint is provided in the nested list form, + in which each element represents qubit group to be measured together. + In an example below, + + .. code-block:: python + + [[0, 1], [2, 3, 4]] + + qubits 0 and 1, and 2, 3 and 4 are measured together on the device. + This constraint doesn't block measuring an individual qubit, + however, Qiskit scheduler must consider the alignment of + measure operations for those qubits. \ No newline at end of file From a4926d3c330ba804f116fccc88d393abc59cdbc3 Mon Sep 17 00:00:00 2001 From: to24toro Date: Thu, 6 Jul 2023 14:26:06 +0900 Subject: [PATCH 22/30] fix test_meas_map --- test/python/transpiler/test_target.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index e6e7a55b3071..17d160d3c249 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -1911,8 +1911,7 @@ def test_meas_map(self): basis_gates=config.basis_gates, meas_map=config.meas_map, ) - meas_map = format_meas_map(config.meas_map) - self.assertEqual(meas_map, target.meas_map) + self.assertEqual(target.meas_map, config.meas_map) def test_custom_basis_gates(self): basis_gates = ["my_x", "cx"] From 6deb381bc29645e5920bf0c201fda4e74b8a379e Mon Sep 17 00:00:00 2001 From: to24toro Date: Thu, 6 Jul 2023 14:26:44 +0900 Subject: [PATCH 23/30] remove format_meas_map --- test/python/transpiler/test_target.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index 17d160d3c249..e9b28034e217 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -36,7 +36,6 @@ from qiskit import pulse from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap from qiskit.pulse.calibration_entries import CalibrationPublisher, ScheduleDef -from qiskit.pulse.utils import format_meas_map from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.instruction_durations import InstructionDurations from qiskit.transpiler.timing_constraints import TimingConstraints From e49b31951c647ac6aec909a59c0977f8a913f059 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 12 Jul 2023 16:01:10 +0900 Subject: [PATCH 24/30] rename meas_map in target to concurrent_measuments --- qiskit/providers/backend_compat.py | 4 ++-- .../fake_provider/utils/backend_converter.py | 2 +- qiskit/transpiler/target.py | 15 +++++++-------- test/python/transpiler/test_target.py | 6 +++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 3c02dbce62a2..99797281ef8d 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -73,7 +73,7 @@ def convert_to_target( target = Target( num_qubits=configuration.n_qubits, qubit_properties=qubit_properties, - meas_map=configuration.meas_map, + concurrent_measurements=configuration.meas_map, ) # Parse instructions gates: Dict[str, Any] = {} @@ -126,7 +126,7 @@ def convert_to_target( target.add_instruction(Measure(), measure_props) # Parse from configuration because properties doesn't exist else: - target = Target(num_qubits=configuration.n_qubits, meas_map=configuration.meas_map) + target = Target(num_qubits=configuration.n_qubits, concurrent_measurements=configuration.meas_map) for gate in configuration.gates: name = gate.name gate_props = ( diff --git a/qiskit/providers/fake_provider/utils/backend_converter.py b/qiskit/providers/fake_provider/utils/backend_converter.py index 649bdc8dbd90..9e038c39df38 100644 --- a/qiskit/providers/fake_provider/utils/backend_converter.py +++ b/qiskit/providers/fake_provider/utils/backend_converter.py @@ -43,7 +43,7 @@ def convert_to_target(conf_dict: dict, props_dict: dict = None, defs_dict: dict qubit_props = None if props_dict: qubit_props = qubit_props_from_props(props_dict) - target = Target(qubit_properties=qubit_props, meas_map=conf_dict.get("meas_map")) + target = Target(qubit_properties=qubit_props, concurrent_measurements=conf_dict.get("meas_map")) # Parse from properties if it exsits if props_dict is not None: # Parse instructions diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index d4b41bfdc41b..4ceb73cdc626 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -44,7 +44,6 @@ from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.providers.exceptions import BackendPropertyError from qiskit.pulse.exceptions import PulseError -from qiskit.pulse.utils import format_meas_map from qiskit.utils.deprecation import deprecate_arg, deprecate_func from qiskit.exceptions import QiskitError @@ -240,7 +239,7 @@ class Target(Mapping): "_non_global_strict_basis", "qubit_properties", "_global_operations", - "meas_map", + "concurrent_measurements", ) @deprecate_arg("aquire_alignment", new_alias="acquire_alignment", since="0.23.0") @@ -254,7 +253,7 @@ def __init__( pulse_alignment=1, acquire_alignment=1, qubit_properties=None, - meas_map=None, + concurrent_measurements=None, ): """ Create a new Target object @@ -290,7 +289,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): A list of sets of qubits that must be + concurrent_measurements(list): A list of sets of qubits that must be measured together. The qubits which should be measured concurrently is provided as the nested list like [[0, 1], [2, 3, 4]]. ValueError: If both ``num_qubits`` and ``qubit_properties`` are both @@ -327,7 +326,7 @@ def __init__( "length of the input qubit_properties list" ) self.qubit_properties = qubit_properties - self.meas_map = meas_map + self.concurrent_measurements = concurrent_measurements def add_instruction(self, instruction, properties=None, name=None): """Add a new instruction to the :class:`~qiskit.transpiler.Target` @@ -1221,7 +1220,7 @@ def from_configuration( inst_map: InstructionScheduleMap | None = None, backend_properties: BackendProperties | None = None, instruction_durations: InstructionDurations | None = None, - meas_map: Optional[List[List[int]]] = None, + concurrent_measurements: Optional[List[List[int]]] = None, dt: float | None = None, timing_constraints: TimingConstraints | None = None, custom_name_mapping: dict[str, Any] | None = None, @@ -1270,7 +1269,7 @@ def from_configuration( instruction_durations: Optional instruction durations for instructions. If specified it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. - meas_map(list): A list of sets of qubits that must be + concurrent_measurements(list): A list of sets of qubits that must be measured together. The qubits which should be measured concurrently is provided as the nested list like [[0, 1], [2, 3, 4]]. dt: The system time resolution of input signals in seconds @@ -1316,7 +1315,7 @@ def from_configuration( pulse_alignment=pulse_alignment, acquire_alignment=acquire_alignment, qubit_properties=qubit_properties, - meas_map=meas_map, + concurrent_measurements=concurrent_measurements, ) name_mapping = get_standard_gate_name_mapping() if custom_name_mapping is not None: diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index e9b28034e217..d352846c5dae 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -1903,14 +1903,14 @@ def test_inst_map(self): self.assertEqual(target.pulse_alignment, constraints.pulse_alignment) self.assertEqual(target.acquire_alignment, constraints.acquire_alignment) - def test_meas_map(self): + def test_concurrent_measurements(self): fake_backend = FakeVigo() config = fake_backend.configuration() target = Target.from_configuration( basis_gates=config.basis_gates, - meas_map=config.meas_map, + concurrent_measurements=config.meas_map, ) - self.assertEqual(target.meas_map, config.meas_map) + self.assertEqual(target.concurrent_measurements, config.meas_map) def test_custom_basis_gates(self): basis_gates = ["my_x", "cx"] From 3ce53fa79d5eb684f7a7ff05ae100624952f2a12 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 12 Jul 2023 21:14:03 +0900 Subject: [PATCH 25/30] change reno --- qiskit/providers/backend_compat.py | 4 +++- .../notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 99797281ef8d..2339322c16aa 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -126,7 +126,9 @@ def convert_to_target( target.add_instruction(Measure(), measure_props) # Parse from configuration because properties doesn't exist else: - target = Target(num_qubits=configuration.n_qubits, concurrent_measurements=configuration.meas_map) + target = Target( + num_qubits=configuration.n_qubits, concurrent_measurements=configuration.meas_map + ) for gate in configuration.gates: name = gate.name gate_props = ( diff --git a/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml b/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml index 5547c3d4cfb5..b0bb3df33f94 100644 --- a/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml +++ b/releasenotes/notes/enable_target_aware_meas_map-0d8542402a74e9d8.yaml @@ -1,7 +1,7 @@ --- features: - | - Added :attr:`~.Target.meas_map` which represents a hardware constraint of qubits + Added :attr:`~.Target.concurrent_measurements` which represents a hardware constraint of qubits measured concurrently. This constraint is provided in the nested list form, in which each element represents qubit group to be measured together. In an example below, From 3d4d0a30ff4f4685ab5b3bd894dc1b4b8d989c50 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 12 Jul 2023 21:31:54 +0900 Subject: [PATCH 26/30] remove Unused Union Dict --- qiskit/transpiler/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 4ceb73cdc626..75e659c6a1d9 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -21,7 +21,7 @@ import itertools -from typing import Union, Optional, Dict, List, Any +from typing import Optional, List, Any from collections.abc import Mapping from collections import defaultdict import datetime From dc3838da0118a0810960cb89bafaf862e2273445 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 12 Jul 2023 22:43:17 +0900 Subject: [PATCH 27/30] concurrent_measurements set as getattr(configuration, meas_map) --- qiskit/providers/backend_compat.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 2339322c16aa..fe582540a647 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -73,7 +73,7 @@ def convert_to_target( target = Target( num_qubits=configuration.n_qubits, qubit_properties=qubit_properties, - concurrent_measurements=configuration.meas_map, + concurrent_measurements=getattr(configuration, "meas_map", None), ) # Parse instructions gates: Dict[str, Any] = {} @@ -127,7 +127,8 @@ def convert_to_target( # Parse from configuration because properties doesn't exist else: target = Target( - num_qubits=configuration.n_qubits, concurrent_measurements=configuration.meas_map + num_qubits=configuration.n_qubits, + concurrent_measurements=getattr(configuration, "meas_map", None), ) for gate in configuration.gates: name = gate.name From 837ba2a625e1f8de3034e07edff94068df9897a7 Mon Sep 17 00:00:00 2001 From: Kento Ueda <38037695+to24toro@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:59:03 +0900 Subject: [PATCH 28/30] Update qiskit/transpiler/target.py Co-authored-by: Naoki Kanazawa --- qiskit/transpiler/target.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 75e659c6a1d9..959b58c0ecb6 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -290,8 +290,8 @@ def __init__( qubits don't have properties available you can set that entry to ``None`` concurrent_measurements(list): A list of sets of qubits that must be - measured together. The qubits which should be measured concurrently - is provided as the nested list like [[0, 1], [2, 3, 4]]. + measured together. This must be provided + as a nested list like [[0, 1], [2, 3, 4]]. ValueError: If both ``num_qubits`` and ``qubit_properties`` are both defined and the value of ``num_qubits`` differs from the length of ``qubit_properties``. From a054b2b3a32f63f943444a0fa51adc6b54653f65 Mon Sep 17 00:00:00 2001 From: Kento Ueda <38037695+to24toro@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:59:16 +0900 Subject: [PATCH 29/30] Update qiskit/transpiler/target.py Co-authored-by: Naoki Kanazawa --- qiskit/transpiler/target.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 959b58c0ecb6..2ada06cee0f2 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -1270,8 +1270,8 @@ def from_configuration( it will take priority for setting the ``duration`` field in the :class:`~InstructionProperties` objects for the instructions in the target. concurrent_measurements(list): A list of sets of qubits that must be - measured together. The qubits which should be measured concurrently - is provided as the nested list like [[0, 1], [2, 3, 4]]. + measured together. This must be provided + as a nested list like [[0, 1], [2, 3, 4]]. dt: The system time resolution of input signals in seconds timing_constraints: Optional timing constraints to include in the :class:`~.Target` From d949c596d1945196cccbd044b3cfb4d97524a345 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 19 Jul 2023 14:25:19 +0900 Subject: [PATCH 30/30] format --- qiskit/transpiler/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 2ada06cee0f2..05c207db59a4 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -290,7 +290,7 @@ def __init__( qubits don't have properties available you can set that entry to ``None`` concurrent_measurements(list): A list of sets of qubits that must be - measured together. This must be provided + measured together. This must be provided as a nested list like [[0, 1], [2, 3, 4]]. ValueError: If both ``num_qubits`` and ``qubit_properties`` are both defined and the value of ``num_qubits`` differs from the length of