From f52a5841e593a1bb0dc9601c85d5bebff0e874a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Fri, 7 Feb 2025 15:17:37 +0100 Subject: [PATCH 1/4] Remove schedule, sequence and related unit tests --- qiskit/__init__.py | 2 +- qiskit/compiler/__init__.py | 6 +- qiskit/compiler/scheduler.py | 109 -- qiskit/compiler/sequencer.py | 71 - test/python/compiler/test_scheduler.py | 103 -- test/python/compiler/test_sequencer.py | 109 -- test/python/scheduler/__init__.py | 15 - test/python/scheduler/test_basic_scheduler.py | 1218 ----------------- .../transpiler/test_calibrationbuilder.py | 184 +-- 9 files changed, 3 insertions(+), 1814 deletions(-) delete mode 100644 qiskit/compiler/scheduler.py delete mode 100644 qiskit/compiler/sequencer.py delete mode 100644 test/python/compiler/test_scheduler.py delete mode 100644 test/python/compiler/test_sequencer.py delete mode 100644 test/python/scheduler/__init__.py delete mode 100644 test/python/scheduler/test_basic_scheduler.py diff --git a/qiskit/__init__.py b/qiskit/__init__.py index c121c99112f8..eb5df6f9b916 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -127,7 +127,7 @@ _config = _user_config.get_config() -from qiskit.compiler import transpile, schedule, sequence +from qiskit.compiler import transpile from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from .version import __version__ diff --git a/qiskit/compiler/__init__.py b/qiskit/compiler/__init__.py index cd3ed166a4dd..3d412611cf0c 100644 --- a/qiskit/compiler/__init__.py +++ b/qiskit/compiler/__init__.py @@ -17,15 +17,11 @@ .. currentmodule:: qiskit.compiler -Circuit and Pulse Compilation Functions +Circuit Compilation Functions ======================================= -.. autofunction:: schedule .. autofunction:: transpile -.. autofunction:: sequence """ from .transpiler import transpile -from .scheduler import schedule -from .sequencer import sequence diff --git a/qiskit/compiler/scheduler.py b/qiskit/compiler/scheduler.py deleted file mode 100644 index 9004dc24fd11..000000000000 --- a/qiskit/compiler/scheduler.py +++ /dev/null @@ -1,109 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# 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. - -""" -Convenience entry point into pulse scheduling, requiring only a circuit and a backend. For more -control over pulse scheduling, look at `qiskit.scheduler.schedule_circuit`. -""" -import logging - -from time import time -from typing import List, Optional, Union - -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.exceptions import QiskitError -from qiskit.pulse import InstructionScheduleMap, Schedule -from qiskit.providers.backend import Backend -from qiskit.scheduler.config import ScheduleConfig -from qiskit.scheduler.schedule_circuit import schedule_circuit -from qiskit.utils.parallel import parallel_map -from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency - -logger = logging.getLogger(__name__) - - -def _log_schedule_time(start_time, end_time): - log_msg = f"Total Scheduling Time - {((end_time - start_time) * 1000):.5f} (ms)" - logger.info(log_msg) - - -@deprecate_pulse_dependency(moving_to_dynamics=True) -def schedule( - circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Optional[Backend] = None, - inst_map: Optional[InstructionScheduleMap] = None, - meas_map: Optional[List[List[int]]] = None, - dt: Optional[float] = None, - method: Optional[Union[str, List[str]]] = None, -) -> Union[Schedule, List[Schedule]]: - """ - Schedule a circuit to a pulse ``Schedule``, using the backend, according to any specified - methods. Supported methods are documented in :py:mod:`qiskit.scheduler.schedule_circuit`. - - Args: - circuits: The quantum circuit or circuits to translate - backend: A backend instance, which contains hardware-specific data required for scheduling - inst_map: Mapping of circuit operations to pulse schedules. If ``None``, defaults to the - ``backend``\'s ``instruction_schedule_map`` - meas_map: List of sets of qubits that must be measured together. If ``None``, defaults to - the ``backend``\'s ``meas_map`` - dt: The output sample rate of backend control electronics. For scheduled circuits - which contain time information, dt is required. If not provided, it will be - obtained from the backend configuration - method: Optionally specify a particular scheduling method - - Returns: - A pulse ``Schedule`` that implements the input circuit - - Raises: - QiskitError: If ``inst_map`` and ``meas_map`` are not passed and ``backend`` is not passed - """ - arg_circuits_list = isinstance(circuits, list) - start_time = time() - if backend and getattr(backend, "version", 0) > 1: - if inst_map is None: - inst_map = backend.instruction_schedule_map - if meas_map is None: - meas_map = backend.meas_map - if dt is None: - dt = backend.dt - else: - if inst_map is None: - if backend is None: - raise QiskitError( - "Must supply either a backend or InstructionScheduleMap for scheduling passes." - ) - defaults = backend.defaults() - if defaults is None: - raise QiskitError( - "The backend defaults are unavailable. The backend may not support pulse." - ) - inst_map = defaults.instruction_schedule_map - if meas_map is None: - if backend is None: - raise QiskitError( - "Must supply either a backend or a meas_map for scheduling passes." - ) - meas_map = backend.configuration().meas_map - if dt is None: - if backend is not None: - dt = backend.configuration().dt - - schedule_config = ScheduleConfig(inst_map=inst_map, meas_map=meas_map, dt=dt) - circuits = circuits if isinstance(circuits, list) else [circuits] - schedules = parallel_map(schedule_circuit, circuits, (schedule_config, method, backend)) - end_time = time() - _log_schedule_time(start_time, end_time) - if arg_circuits_list: - return schedules - else: - return schedules[0] diff --git a/qiskit/compiler/sequencer.py b/qiskit/compiler/sequencer.py deleted file mode 100644 index 5a381918417b..000000000000 --- a/qiskit/compiler/sequencer.py +++ /dev/null @@ -1,71 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Mapping a scheduled ``QuantumCircuit`` to a pulse ``Schedule``. -""" -from typing import List, Optional, Union - -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.exceptions import QiskitError -from qiskit.providers.backend import Backend -from qiskit.pulse import InstructionScheduleMap, Schedule -from qiskit.scheduler import ScheduleConfig -from qiskit.scheduler.sequence import sequence as _sequence -from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency - - -@deprecate_pulse_dependency(moving_to_dynamics=True) -def sequence( - scheduled_circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Optional[Backend] = None, - inst_map: Optional[InstructionScheduleMap] = None, - meas_map: Optional[List[List[int]]] = None, - dt: Optional[float] = None, -) -> Union[Schedule, List[Schedule]]: - """ - Schedule a scheduled circuit to a pulse ``Schedule``, using the backend. - - Args: - scheduled_circuits: Scheduled circuit(s) to be translated - backend: A backend instance, which contains hardware-specific data required for scheduling - inst_map: Mapping of circuit operations to pulse schedules. If ``None``, defaults to the - ``backend``\'s ``instruction_schedule_map`` - meas_map: List of sets of qubits that must be measured together. If ``None``, defaults to - the ``backend``\'s ``meas_map`` - dt: The output sample rate of backend control electronics. For scheduled circuits - which contain time information, dt is required. If not provided, it will be - obtained from the backend configuration - - Returns: - A pulse ``Schedule`` that implements the input circuit - - Raises: - QiskitError: If ``inst_map`` and ``meas_map`` are not passed and ``backend`` is not passed - """ - if inst_map is None: - if backend is None: - raise QiskitError("Must supply either a backend or inst_map for sequencing.") - inst_map = backend.defaults().instruction_schedule_map - if meas_map is None: - if backend is None: - raise QiskitError("Must supply either a backend or a meas_map for sequencing.") - meas_map = backend.configuration().meas_map - if dt is None: - if backend is None: - raise QiskitError("Must supply either a backend or a dt for sequencing.") - dt = backend.configuration().dt - - schedule_config = ScheduleConfig(inst_map=inst_map, meas_map=meas_map, dt=dt) - circuits = scheduled_circuits if isinstance(scheduled_circuits, list) else [scheduled_circuits] - schedules = [_sequence(circuit, schedule_config) for circuit in circuits] - return schedules[0] if len(schedules) == 1 else schedules diff --git a/test/python/compiler/test_scheduler.py b/test/python/compiler/test_scheduler.py deleted file mode 100644 index c349bf054c3b..000000000000 --- a/test/python/compiler/test_scheduler.py +++ /dev/null @@ -1,103 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# 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. - -"""Scheduler Test.""" - -from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit.exceptions import QiskitError -from qiskit.pulse import InstructionScheduleMap, Schedule -from qiskit.providers.fake_provider import FakeOpenPulse3Q, GenericBackendV2 -from qiskit.compiler.scheduler import schedule -from test import QiskitTestCase # pylint: disable=wrong-import-order - - -class TestCircuitScheduler(QiskitTestCase): - """Tests for scheduling.""" - - def setUp(self): - super().setUp() - qr = QuantumRegister(2, name="q") - cr = ClassicalRegister(2, name="c") - self.circ = QuantumCircuit(qr, cr, name="circ") - self.circ.cx(qr[0], qr[1]) - self.circ.measure(qr, cr) - - qr2 = QuantumRegister(2, name="q") - cr2 = ClassicalRegister(2, name="c") - self.circ2 = QuantumCircuit(qr2, cr2, name="circ2") - self.circ2.cx(qr2[0], qr2[1]) - self.circ2.measure(qr2, cr2) - - with self.assertWarns(DeprecationWarning): - self.backend = GenericBackendV2( - 3, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 - ) - - def test_instruction_map_and_backend_not_supplied(self): - """Test instruction map and backend not supplied.""" - with self.assertRaisesRegex( - QiskitError, - r"Must supply either a backend or InstructionScheduleMap for scheduling passes.", - ): - with self.assertWarns(DeprecationWarning): - schedule(self.circ) - - def test_instruction_map_and_backend_defaults_unavailable(self): - """Test backend defaults unavailable when backend is provided, but instruction map is not.""" - with self.assertWarns(DeprecationWarning): - self.backend = FakeOpenPulse3Q() - self.backend._defaults = None - with self.assertRaisesRegex( - QiskitError, r"The backend defaults are unavailable. The backend may not support pulse." - ): - with self.assertWarns(DeprecationWarning): - schedule(self.circ, self.backend) - - def test_measurement_map_and_backend_not_supplied(self): - """Test measurement map and backend not supplied.""" - with self.assertRaisesRegex( - QiskitError, - r"Must supply either a backend or a meas_map for scheduling passes.", - ): - with self.assertWarns(DeprecationWarning): - schedule(self.circ, inst_map=InstructionScheduleMap()) - - def test_schedules_single_circuit(self): - """Test scheduling of a single circuit.""" - with self.assertWarns(DeprecationWarning): - circuit_schedule = schedule(self.circ, self.backend) - - self.assertIsInstance(circuit_schedule, Schedule) - self.assertEqual(circuit_schedule.name, "circ") - - def test_schedules_multiple_circuits(self): - """Test scheduling of multiple circuits.""" - self.enable_parallel_processing() - - circuits = [self.circ, self.circ2] - with self.assertWarns(DeprecationWarning): - circuit_schedules = schedule(circuits, self.backend, method="asap") - self.assertEqual(len(circuit_schedules), len(circuits)) - - circuit_one_schedule = circuit_schedules[0] - circuit_two_schedule = circuit_schedules[1] - - with self.assertWarns(DeprecationWarning): - self.assertEqual( - circuit_one_schedule, - schedule(self.circ, self.backend, method="asap"), - ) - - self.assertEqual( - circuit_two_schedule, - schedule(self.circ2, self.backend, method="asap"), - ) diff --git a/test/python/compiler/test_sequencer.py b/test/python/compiler/test_sequencer.py deleted file mode 100644 index 3fcfc16674a6..000000000000 --- a/test/python/compiler/test_sequencer.py +++ /dev/null @@ -1,109 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2024. -# -# 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. - -# pylint: disable=missing-function-docstring - -"""Tests basic functionality of the sequence function""" -# TODO with the removal of pulses, this file can be removed too. - -import unittest - -from qiskit import QuantumCircuit, pulse -from qiskit.compiler import sequence, transpile, schedule -from qiskit.pulse.transforms import pad -from qiskit.providers.fake_provider import Fake127QPulseV1 -from test import QiskitTestCase # pylint: disable=wrong-import-order - - -class TestSequence(QiskitTestCase): - """Test sequence function.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.backend = Fake127QPulseV1() - self.backend.configuration().timing_constraints = {} - - def test_sequence_empty(self): - with self.assertWarns(DeprecationWarning): - self.assertEqual(sequence([], self.backend), []) - - def test_transpile_and_sequence_agree_with_schedule(self): - qc = QuantumCircuit(2, name="bell") - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - sc = transpile(qc, self.backend, scheduling_method="alap") - with self.assertWarns(DeprecationWarning): - actual = sequence(sc, self.backend) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - expected = schedule(transpile(qc, self.backend), self.backend) - with self.assertWarns(DeprecationWarning): - # pad adds Delay which is deprecated - self.assertEqual(actual, pad(expected)) - - def test_transpile_and_sequence_agree_with_schedule_for_circuit_with_delay(self): - qc = QuantumCircuit(1, 1, name="t2") - qc.h(0) - qc.delay(500, 0, unit="ns") - qc.h(0) - qc.measure(0, 0) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - sc = transpile(qc, self.backend, scheduling_method="alap") - with self.assertWarns(DeprecationWarning): - actual = sequence(sc, self.backend) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - expected = schedule(transpile(qc, self.backend), self.backend) - with self.assertWarns(DeprecationWarning): - self.assertEqual( - actual.exclude(instruction_types=[pulse.Delay]), - expected.exclude(instruction_types=[pulse.Delay]), - ) - - @unittest.skip("not yet determined if delays on ancilla should be removed or not") - def test_transpile_and_sequence_agree_with_schedule_for_circuits_without_measures(self): - qc = QuantumCircuit(2, name="bell_without_measurement") - qc.h(0) - qc.cx(0, 1) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - sc = transpile(qc, self.backend, scheduling_method="alap") - with self.assertWarns(DeprecationWarning): - actual = sequence(sc, self.backend) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - with self.assertWarns(DeprecationWarning): - expected = schedule(transpile(qc, self.backend), self.backend) - self.assertEqual(actual, pad(expected)) diff --git a/test/python/scheduler/__init__.py b/test/python/scheduler/__init__.py deleted file mode 100644 index b56983c88a1e..000000000000 --- a/test/python/scheduler/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# 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. - -# pylint: disable=cyclic-import - -"""Qiskit pulse scheduling tests.""" diff --git a/test/python/scheduler/test_basic_scheduler.py b/test/python/scheduler/test_basic_scheduler.py deleted file mode 100644 index 5adbb3fdc1ff..000000000000 --- a/test/python/scheduler/test_basic_scheduler.py +++ /dev/null @@ -1,1218 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2024. -# -# 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 scheduler passes.""" - -import numpy as np -from numpy import pi -from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, schedule -from qiskit.circuit import Gate, Parameter -from qiskit.circuit.library import U1Gate, U2Gate, U3Gate, SXGate -from qiskit.exceptions import QiskitError -from qiskit.pulse import ( - Schedule, - DriveChannel, - AcquireChannel, - Acquire, - MeasureChannel, - MemorySlot, - Gaussian, - GaussianSquare, - Play, - Waveform, - transforms, -) -from qiskit.pulse import build, macros, play, InstructionScheduleMap -from qiskit.providers.fake_provider import ( - FakeBackend, - FakeOpenPulse2Q, - FakeOpenPulse3Q, - GenericBackendV2, -) -from test import QiskitTestCase # pylint: disable=wrong-import-order - - -class TestBasicSchedule(QiskitTestCase): - """Scheduling tests.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.backend = FakeOpenPulse2Q() - self.inst_map = self.backend.defaults().instruction_schedule_map - - def test_unavailable_defaults(self): - """Test backend with unavailable defaults.""" - qr = QuantumRegister(1) - qc = QuantumCircuit(qr) - with self.assertWarns(DeprecationWarning): - backend = FakeBackend(None) - backend.defaults = backend.configuration - with self.assertWarns(DeprecationWarning): - self.assertRaises(QiskitError, lambda: schedule(qc, backend)) - - def test_alap_pass(self): - """Test ALAP scheduling.""" - - # ┌───────────────┐ ░ ┌─┐ - # q0_0: ┤ U2(3.14,1.57) ├────────────────────░───■──┤M├─── - # └┬──────────────┤ ░ ┌──────────────┐ ░ ┌─┴─┐└╥┘┌─┐ - # q0_1: ─┤ U2(0.5,0.25) ├─░─┤ U2(0.5,0.25) ├─░─┤ X ├─╫─┤M├ - # └──────────────┘ ░ └──────────────┘ ░ └───┘ ║ └╥┘ - # c0: 2/═════════════════════════════════════════════╩══╩═ - # 0 1 - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(U2Gate(3.14, 1.57), [q[0]]) - qc.append(U2Gate(0.5, 0.25), [q[1]]) - qc.barrier(q[1]) - qc.append(U2Gate(0.5, 0.25), [q[1]]) - qc.barrier(q[0], [q[1]]) - qc.cx(q[0], q[1]) - qc.measure(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend) - # X pulse on q0 should end at the start of the CNOT - with self.assertWarns(DeprecationWarning): - expected = Schedule( - (2, self.inst_map.get("u2", [0], 3.14, 1.57)), - self.inst_map.get("u2", [1], 0.5, 0.25), - (2, self.inst_map.get("u2", [1], 0.5, 0.25)), - (4, self.inst_map.get("cx", [0, 1])), - (26, self.inst_map.get("measure", [0, 1])), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_single_circuit_list_schedule(self): - """Test that passing a single circuit list to schedule() returns a list.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule([qc], self.backend, method="alap") - expected = Schedule() - self.assertIsInstance(sched, list) - self.assertEqual(sched[0].instructions, expected.instructions) - - def test_alap_with_barriers(self): - """Test that ALAP respects barriers on new qubits.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(U2Gate(0, 0), [q[0]]) - qc.barrier(q[0], q[1]) - qc.append(U2Gate(0, 0), [q[1]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule( - self.inst_map.get("u2", [0], 0, 0), (2, self.inst_map.get("u2", [1], 0, 0)) - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_empty_circuit_schedule(self): - """Test empty circuit being scheduled.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule() - self.assertEqual(sched.instructions, expected.instructions) - - def test_alap_aligns_end(self): - """Test that ALAP always acts as though there is a final global barrier.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(U3Gate(0, 0, 0), [q[0]]) - qc.append(U2Gate(0, 0), [q[1]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected_sched = Schedule( - (2, self.inst_map.get("u2", [1], 0, 0)), self.inst_map.get("u3", [0], 0, 0, 0) - ) - for actual, expected in zip(sched.instructions, expected_sched.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - with self.assertWarns(DeprecationWarning): - self.assertEqual( - sched.ch_duration(DriveChannel(0)), expected_sched.ch_duration(DriveChannel(1)) - ) - - def test_asap_pass(self): - """Test ASAP scheduling.""" - - # ┌───────────────┐ ░ ┌─┐ - # q0_0: ┤ U2(3.14,1.57) ├────────────────────░───■──┤M├─── - # └┬──────────────┤ ░ ┌──────────────┐ ░ ┌─┴─┐└╥┘┌─┐ - # q0_1: ─┤ U2(0.5,0.25) ├─░─┤ U2(0.5,0.25) ├─░─┤ X ├─╫─┤M├ - # └──────────────┘ ░ └──────────────┘ ░ └───┘ ║ └╥┘ - # c0: 2/═════════════════════════════════════════════╩══╩═ - # 0 1 - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(U2Gate(3.14, 1.57), [q[0]]) - qc.append(U2Gate(0.5, 0.25), [q[1]]) - qc.barrier(q[1]) - qc.append(U2Gate(0.5, 0.25), [q[1]]) - qc.barrier(q[0], q[1]) - qc.cx(q[0], q[1]) - qc.measure(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_soon_as_possible") - # X pulse on q0 should start at t=0 - expected = Schedule( - self.inst_map.get("u2", [0], 3.14, 1.57), - self.inst_map.get("u2", [1], 0.5, 0.25), - (2, self.inst_map.get("u2", [1], 0.5, 0.25)), - (4, self.inst_map.get("cx", [0, 1])), - (26, self.inst_map.get("measure", [0, 1])), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_alap_resource_respecting(self): - """Test that the ALAP pass properly respects busy resources when backwards scheduling. - For instance, a CX on 0 and 1 followed by an X on only 1 must respect both qubits' - timeline.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - qc.append(U2Gate(0.5, 0.25), [q[1]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_late_as_possible") - insts = sched.instructions - self.assertEqual(insts[0][0], 0) - self.assertEqual(insts[6][0], 22) - - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - qc.append(U2Gate(0.5, 0.25), [q[1]]) - qc.measure(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_late_as_possible") - self.assertEqual(sched.instructions[-1][0], 24) - - def test_inst_map_schedules_unaltered(self): - """Test that forward scheduling doesn't change relative timing with a command.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - with self.assertWarns(DeprecationWarning): - sched1 = schedule(qc, self.backend, method="as_soon_as_possible") - sched2 = schedule(qc, self.backend, method="as_late_as_possible") - for asap, alap in zip(sched1.instructions, sched2.instructions): - self.assertEqual(asap[0], alap[0]) - self.assertEqual(asap[1], alap[1]) - insts = sched1.instructions - self.assertEqual(insts[0][0], 0) # shift phase - self.assertEqual(insts[1][0], 0) # ym_d0 - self.assertEqual(insts[2][0], 0) # x90p_d1 - self.assertEqual(insts[3][0], 2) # cr90p_u0 - self.assertEqual(insts[4][0], 11) # xp_d0 - self.assertEqual(insts[5][0], 13) # cr90m_u0 - - def test_measure_combined(self): - """ - Test to check for measure on the same qubit which generated another measure schedule. - - The measures on different qubits are combined, but measures on the same qubit - adds another measure to the schedule. - """ - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(U2Gate(3.14, 1.57), [q[0]]) - qc.cx(q[0], q[1]) - qc.measure(q[0], c[0]) - qc.measure(q[1], c[1]) - qc.measure(q[1], c[1]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_soon_as_possible") - expected = Schedule( - self.inst_map.get("u2", [0], 3.14, 1.57), - (2, self.inst_map.get("cx", [0, 1])), - (24, self.inst_map.get("measure", [0, 1])), - (34, self.inst_map.get("measure", [0, 1]).filter(channels=[MeasureChannel(1)])), - (34, Acquire(10, AcquireChannel(1), MemorySlot(1))), - ) - self.assertEqual(sched.instructions, expected.instructions) - - # - def test_3q_schedule(self): - """Test a schedule that was recommended by David McKay :D""" - - # ┌─────────────────┐ - # q0_0: ─────────■─────────┤ U3(3.14,1.57,0) ├──────────────────────── - # ┌─┴─┐ └┬───────────────┬┘ - # q0_1: ───────┤ X ├────────┤ U2(3.14,1.57) ├───■───────────────────── - # ┌──────┴───┴──────┐ └───────────────┘ ┌─┴─┐┌─────────────────┐ - # q0_2: ┤ U2(0.778,0.122) ├───────────────────┤ X ├┤ U2(0.778,0.122) ├ - # └─────────────────┘ └───┘└─────────────────┘ - with self.assertWarns(DeprecationWarning): - backend = FakeOpenPulse3Q() - inst_map = backend.defaults().instruction_schedule_map - q = QuantumRegister(3) - c = ClassicalRegister(3) - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - qc.append(U2Gate(0.778, 0.122), [q[2]]) - qc.append(U3Gate(3.14, 1.57, 0), [q[0]]) - qc.append(U2Gate(3.14, 1.57), [q[1]]) - qc.cx(q[1], q[2]) - qc.append(U2Gate(0.778, 0.122), [q[2]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, backend) - expected = Schedule( - inst_map.get("cx", [0, 1]), - (22, inst_map.get("u2", [1], 3.14, 1.57)), - (22, inst_map.get("u2", [2], 0.778, 0.122)), - (24, inst_map.get("cx", [1, 2])), - (44, inst_map.get("u3", [0], 3.14, 1.57, 0)), - (46, inst_map.get("u2", [2], 0.778, 0.122)), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_schedule_multi(self): - """Test scheduling multiple circuits at once.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc0 = QuantumCircuit(q, c) - qc0.cx(q[0], q[1]) - qc1 = QuantumCircuit(q, c) - qc1.cx(q[0], q[1]) - with self.assertWarns(DeprecationWarning): - schedules = schedule([qc0, qc1], self.backend) - expected_insts = schedule(qc0, self.backend).instructions - for actual, expected in zip(schedules[0].instructions, expected_insts): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_circuit_name_kept(self): - """Test that the new schedule gets its name from the circuit.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c, name="CIRCNAME") - qc.cx(q[0], q[1]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="asap") - self.assertEqual(sched.name, qc.name) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - self.assertEqual(sched.name, qc.name) - - def test_can_add_gates_into_free_space(self): - """The scheduler does some time bookkeeping to know when qubits are free to be - scheduled. Make sure this works for qubits that are used in the future. This was - a bug, uncovered by this example: - - q0 = - - - - |X| - q1 = |X| |u2| |X| - - In ALAP scheduling, the next operation on qubit 0 would be added at t=0 rather - than immediately before the X gate. - """ - qr = QuantumRegister(2) - qc = QuantumCircuit(qr) - for i in range(2): - qc.append(U2Gate(0, 0), [qr[i]]) - qc.append(U1Gate(3.14), [qr[i]]) - qc.append(U2Gate(0, 0), [qr[i]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule( - self.inst_map.get("u2", [0], 0, 0), - self.inst_map.get("u2", [1], 0, 0), - (2, self.inst_map.get("u1", [0], 3.14)), - (2, self.inst_map.get("u1", [1], 3.14)), - (2, self.inst_map.get("u2", [0], 0, 0)), - (2, self.inst_map.get("u2", [1], 0, 0)), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_barriers_in_middle(self): - """As a follow on to `test_can_add_gates_into_free_space`, similar issues - arose for barriers, specifically. - """ - qr = QuantumRegister(2) - qc = QuantumCircuit(qr) - for i in range(2): - qc.append(U2Gate(0, 0), [qr[i]]) - qc.barrier(qr[i]) - qc.append(U1Gate(3.14), [qr[i]]) - qc.barrier(qr[i]) - qc.append(U2Gate(0, 0), [qr[i]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule( - self.inst_map.get("u2", [0], 0, 0), - self.inst_map.get("u2", [1], 0, 0), - (2, self.inst_map.get("u1", [0], 3.14)), - (2, self.inst_map.get("u1", [1], 3.14)), - (2, self.inst_map.get("u2", [0], 0, 0)), - (2, self.inst_map.get("u2", [1], 0, 0)), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_parametric_input(self): - """Test that scheduling works with parametric pulses as input.""" - qr = QuantumRegister(1) - qc = QuantumCircuit(qr) - qc.append(Gate("gauss", 1, []), qargs=[qr[0]]) - with self.assertWarns(DeprecationWarning): - custom_gauss = Schedule( - Play(Gaussian(duration=25, sigma=4, amp=0.5, angle=pi / 2), DriveChannel(0)) - ) - self.inst_map.add("gauss", [0], custom_gauss) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, inst_map=self.inst_map) - self.assertEqual(sched.instructions[0], custom_gauss.instructions[0]) - - def test_pulse_gates(self): - """Test scheduling calibrated pulse gates.""" - q = QuantumRegister(2) - qc = QuantumCircuit(q) - qc.append(U2Gate(0, 0), [q[0]]) - qc.barrier(q[0], q[1]) - qc.append(U2Gate(0, 0), [q[1]]) - with self.assertWarns(DeprecationWarning): - qc.add_calibration( - "u2", [0], Schedule(Play(Gaussian(28, 0.2, 4), DriveChannel(0))), [0, 0] - ) - qc.add_calibration( - "u2", [1], Schedule(Play(Gaussian(28, 0.2, 4), DriveChannel(1))), [0, 0] - ) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend) - expected = Schedule( - Play(Gaussian(28, 0.2, 4), DriveChannel(0)), - (28, Schedule(Play(Gaussian(28, 0.2, 4), DriveChannel(1)))), - ) - self.assertEqual(sched.instructions, expected.instructions) - - def test_calibrated_measurements(self): - """Test scheduling calibrated measurements.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(U2Gate(0, 0), [q[0]]) - qc.measure(q[0], c[0]) - - with self.assertWarns(DeprecationWarning): - meas_sched = Play(Gaussian(1200, 0.2, 4), MeasureChannel(0)) - meas_sched |= Acquire(1200, AcquireChannel(0), MemorySlot(0)) - qc.add_calibration("measure", [0], meas_sched) - - sched = schedule(qc, self.backend) - expected = Schedule(self.inst_map.get("u2", [0], 0, 0), (2, meas_sched)) - self.assertEqual(sched.instructions, expected.instructions) - - def test_subset_calibrated_measurements(self): - """Test that measurement calibrations can be added and used for some qubits, even - if the other qubits do not also have calibrated measurements.""" - qc = QuantumCircuit(3, 3) - qc.measure(0, 0) - qc.measure(1, 1) - qc.measure(2, 2) - meas_scheds = [] - for qubit in [0, 2]: - with self.assertWarns(DeprecationWarning): - meas = Play(Gaussian(1200, 0.2, 4), MeasureChannel(qubit)) + Acquire( - 1200, AcquireChannel(qubit), MemorySlot(qubit) - ) - meas_scheds.append(meas) - with self.assertWarns(DeprecationWarning): - qc.add_calibration("measure", [qubit], meas) - - with self.assertWarns(DeprecationWarning): - backend = FakeOpenPulse3Q() - meas = macros.measure([1], backend) - meas = meas.exclude(channels=[AcquireChannel(0), AcquireChannel(2)]) - with self.assertWarns(DeprecationWarning): - backend = FakeOpenPulse3Q() - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, backend) - expected = Schedule(meas_scheds[0], meas_scheds[1], meas) - self.assertEqual(sched.instructions, expected.instructions) - - def test_clbits_of_calibrated_measurements(self): - """Test that calibrated measurements are only used when the classical bits also match.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.measure(q[0], c[1]) - - with self.assertWarns(DeprecationWarning): - meas_sched = Play(Gaussian(1200, 0.2, 4), MeasureChannel(0)) - meas_sched |= Acquire(1200, AcquireChannel(0), MemorySlot(0)) - qc.add_calibration("measure", [0], meas_sched) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend) - # Doesn't use the calibrated schedule because the classical memory slots do not match - with self.assertWarns(DeprecationWarning): - expected = Schedule(macros.measure([0], self.backend, qubit_mem_slots={0: 1})) - self.assertEqual(sched.instructions, expected.instructions) - - def test_metadata_is_preserved_alap(self): - """Test that circuit metadata is preserved in output schedule with alap.""" - q = QuantumRegister(2) - qc = QuantumCircuit(q) - qc.append(U2Gate(0, 0), [q[0]]) - qc.barrier(q[0], q[1]) - qc.append(U2Gate(0, 0), [q[1]]) - qc.metadata = {"experiment_type": "gst", "execution_number": "1234"} - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - self.assertEqual({"experiment_type": "gst", "execution_number": "1234"}, sched.metadata) - - def test_metadata_is_preserved_asap(self): - """Test that circuit metadata is preserved in output schedule with asap.""" - q = QuantumRegister(2) - qc = QuantumCircuit(q) - qc.append(U2Gate(0, 0), [q[0]]) - qc.barrier(q[0], q[1]) - qc.append(U2Gate(0, 0), [q[1]]) - qc.metadata = {"experiment_type": "gst", "execution_number": "1234"} - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="asap") - self.assertEqual({"experiment_type": "gst", "execution_number": "1234"}, sched.metadata) - - def test_scheduler_with_params_bound(self): - """Test scheduler with parameters defined and bound""" - x = Parameter("x") - qc = QuantumCircuit(2) - qc.append(Gate("pulse_gate", 1, [x]), [0]) - with self.assertWarns(DeprecationWarning): - expected_schedule = Schedule() - qc.add_calibration( - gate="pulse_gate", qubits=[0], schedule=expected_schedule, params=[x] - ) - qc = qc.assign_parameters({x: 1}) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend) - self.assertEqual(sched, expected_schedule) - - def test_scheduler_with_params_not_bound(self): - """Test scheduler with parameters defined but not bound""" - x = Parameter("amp") - qc = QuantumCircuit(2) - qc.append(Gate("pulse_gate", 1, [x]), [0]) - with self.assertWarns(DeprecationWarning): - with build() as expected_schedule: - play(Gaussian(duration=160, amp=x, sigma=40), DriveChannel(0)) - qc.add_calibration( - gate="pulse_gate", qubits=[0], schedule=expected_schedule, params=[x] - ) - sched = schedule(qc, self.backend) - self.assertEqual(sched, transforms.target_qobj_transform(expected_schedule)) - - def test_schedule_block_in_instmap(self): - """Test schedule block in instmap can be scheduled.""" - duration = Parameter("duration") - - with self.assertWarns(DeprecationWarning): - with build() as pulse_prog: - play(Gaussian(duration, 0.1, 10), DriveChannel(0)) - - instmap = InstructionScheduleMap() - instmap.add("block_gate", (0,), pulse_prog, ["duration"]) - - qc = QuantumCircuit(1) - qc.append(Gate("block_gate", 1, [duration]), [0]) - qc.assign_parameters({duration: 100}, inplace=True) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, inst_map=instmap) - - with self.assertWarns(DeprecationWarning): - ref_sched = Schedule() - ref_sched += Play(Gaussian(100, 0.1, 10), DriveChannel(0)) - - self.assertEqual(sched, ref_sched) - - -class TestBasicScheduleV2(QiskitTestCase): - """Scheduling tests.""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.backend = GenericBackendV2(num_qubits=3, calibrate_instructions=True, seed=42) - self.inst_map = self.backend.instruction_schedule_map - # self.pulse_2_samples is the pulse sequence used to calibrate "measure" in - # GenericBackendV2. See class construction for more details. - self.pulse_2_samples = np.linspace(0, 1.0, 32, dtype=np.complex128) - - def test_alap_pass(self): - """Test ALAP scheduling.""" - - # ┌────┐ ░ ┌─┐ - # q0_0: ┤ √X ├──────────░───■──┤M├─── - # ├────┤ ░ ┌────┐ ░ ┌─┴─┐└╥┘┌─┐ - # q0_1: ┤ √X ├─░─┤ √X ├─░─┤ X ├─╫─┤M├ - # └────┘ ░ └────┘ ░ └───┘ ║ └╥┘ - # c0: 2/════════════════════════╩══╩═ - # 0 1 - - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - - qc.sx(q[0]) - qc.sx(q[1]) - qc.barrier(q[1]) - - qc.sx(q[1]) - qc.barrier(q[0], q[1]) - - qc.cx(q[0], q[1]) - qc.measure(q, c) - - with self.assertWarns(DeprecationWarning): - sched = schedule(circuits=qc, backend=self.backend, method="alap") - - # Since, the method of scheduling chosen here is 'as_late_as_possible' - # so all the π/2 pulse here should be right shifted. - # - # Calculations: - # Duration of the π/2 pulse for GenericBackendV2 backend is 16dt - # first π/2 pulse on q0 should start at 16dt because of 'as_late_as_possible'. - # first π/2 pulse on q1 should start 0dt. - # second π/2 pulse on q1 should start with a delay of 16dt. - # cx pulse( pulse on drive channel, control channel) should start with a delay - # of 16dt+16dt. - # measure pulse should start with a delay of 16dt+16dt+64dt(64dt for cx gate). - with self.assertWarns(DeprecationWarning): - expected = Schedule( - (0, self.inst_map.get("sx", [1])), - (0 + 16, self.inst_map.get("sx", [0])), # Right shifted because of alap. - (0 + 16, self.inst_map.get("sx", [1])), - (0 + 16 + 16, self.inst_map.get("cx", [0, 1])), - ( - 0 + 16 + 16 + 64, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(0), - name="pulse_2", - ), - ), - ( - 0 + 16 + 16 + 64, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(1), - name="pulse_2", - ), - ), - (0 + 16 + 16 + 64, Acquire(1792, AcquireChannel(0), MemorySlot(0))), - (0 + 16 + 16 + 64, Acquire(1792, AcquireChannel(1), MemorySlot(1))), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_single_circuit_list_schedule(self): - """Test that passing a single circuit list to schedule() returns a list.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule([qc], self.backend, method="alap") - expected = Schedule() - self.assertIsInstance(sched, list) - self.assertEqual(sched[0].instructions, expected.instructions) - - def test_alap_with_barriers(self): - """Test that ALAP respects barriers on new qubits.""" - - # ┌────┐ ░ - # q0_0: ┤ √X ├─░─────── - # └────┘ ░ ┌────┐ - # q0_1: ───────░─┤ √X ├ - # ░ └────┘ - # c0: 2/═══════════════ - # - - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.append(SXGate(), [q[0]]) - qc.barrier(q[0], q[1]) - qc.append(SXGate(), [q[1]]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - # If there wasn't a barrier the π/2 pulse on q1 would have started from 0dt, but since, - # there is a barrier so the π/2 pulse on q1 should start with a delay of 160dt. - expected = Schedule( - (0, self.inst_map.get("sx", [0])), (16, self.inst_map.get("sx", [1])) - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual, expected) - - def test_empty_circuit_schedule(self): - """Test empty circuit being scheduled.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule() - self.assertEqual(sched.instructions, expected.instructions) - - def test_alap_aligns_end(self): - """Test that ALAP always acts as though there is a final global barrier.""" - # ┌────┐ - # q1_0: ┤ √X ├ - # ├────┤ - # q1_1: ┤ √X ├ - # └────┘ - # c1: 2/══════ - - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.sx(q[0]) - qc.sx(q[1]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected_sched = Schedule( - (0, self.inst_map.get("sx", [1])), (0, self.inst_map.get("sx", [0])) - ) - for actual, expected in zip(sched.instructions, expected_sched.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - with self.assertWarns(DeprecationWarning): - self.assertEqual( - sched.ch_duration(DriveChannel(0)), expected_sched.ch_duration(DriveChannel(1)) - ) - - def test_asap_pass(self): - """Test ASAP scheduling.""" - - # ┌────┐ ░ ┌─┐ - # q0_0: ┤ √X ├──────────░───■──┤M├─── - # ├────┤ ░ ┌────┐ ░ ┌─┴─┐└╥┘┌─┐ - # q0_1: ┤ √X ├─░─┤ √X ├─░─┤ X ├─╫─┤M├ - # └────┘ ░ └────┘ ░ └───┘ ║ └╥┘ - # c0: 2/════════════════════════╩══╩═ - # 0 1 - - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - - qc.sx(q[0]) - qc.sx(q[1]) - qc.barrier(q[1]) - - qc.sx(q[1]) - qc.barrier(q[0], q[1]) - - qc.cx(q[0], q[1]) - qc.measure(q, c) - - with self.assertWarns(DeprecationWarning): - sched = schedule(circuits=qc, backend=self.backend, method="asap") - # Since, the method of scheduling chosen here is 'as_soon_as_possible' - # so all the π/2 pulse here should be left shifted. - # - # Calculations: - # Duration of the π/2 pulse for FakePerth backend is 16dt - # first π/2 pulse on q0 should start at 0dt because of 'as_soon_as_possible'. - # first π/2 pulse on q1 should start 0dt. - # second π/2 pulse on q1 should start with a delay of 16dt. - # cx pulse( pulse on drive channel, control channel) should start with a delay - # of 16dt+16dt. - # measure pulse should start with a delay of 16dt+16dt+64dt(64dt for cx gate). - with self.assertWarns(DeprecationWarning): - expected = Schedule( - (0, self.inst_map.get("sx", [1])), - (0, self.inst_map.get("sx", [0])), # Left shifted because of asap. - (0 + 16, self.inst_map.get("sx", [1])), - (0 + 16 + 16, self.inst_map.get("cx", [0, 1])), - ( - 0 + 16 + 16 + 64, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(0), - name="pulse_2", - ), - ), - ( - 0 + 16 + 16 + 64, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(1), - name="pulse_2", - ), - ), - (0 + 16 + 16 + 64, Acquire(1792, AcquireChannel(0), MemorySlot(0))), - (0 + 16 + 16 + 64, Acquire(1792, AcquireChannel(1), MemorySlot(1))), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_alap_resource_respecting(self): - """Test that the ALAP pass properly respects busy resources when backwards scheduling. - For instance, a CX on 0 and 1 followed by an X on only 1 must respect both qubits' - timeline.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - qc.sx(q[1]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_late_as_possible") - insts = sched.instructions - - # This is ShiftPhase for the cx operation. - self.assertEqual(insts[0][0], 0) - - # It takes 4 pulse operations on DriveChannel and ControlChannel to do a - # cx operation on this backend. - # 64dt is duration of cx operation on this backend. - self.assertEqual(insts[4][0], 64) - - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - qc.sx(q[1]) - qc.measure(q, c) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_late_as_possible") - - # 64dt for cx operation + 16dt for sx operation - # So, the pulses in MeasureChannel0 and 1 starts from 80dt. - self.assertEqual(sched.instructions[-1][0], 80) - - def test_inst_map_schedules_unaltered(self): - """Test that forward scheduling doesn't change relative timing with a command.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - with self.assertWarns(DeprecationWarning): - sched1 = schedule(qc, self.backend, method="as_soon_as_possible") - sched2 = schedule(qc, self.backend, method="as_late_as_possible") - for asap, alap in zip(sched1.instructions, sched2.instructions): - self.assertEqual(asap[0], alap[0]) - self.assertEqual(asap[1], alap[1]) - insts = sched1.instructions - self.assertEqual(insts[0][0], 0) # ShiftPhase at DriveChannel(0) no dt required. - self.assertEqual(insts[1][0], 0) # ShiftPhase at ControlChannel(1) no dt required. - self.assertEqual(insts[2][0], 0) # Pulse pulse_2 of duration 32dt. - self.assertEqual(insts[3][0], 0) # Pulse pulse_3 of duration 160dt. - - def test_measure_combined(self): - """ - Test to check for measure on the same qubit which generated another measure schedule. - - The measures on different qubits are combined, but measures on the same qubit - adds another measure to the schedule. - """ - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.sx(q[0]) - qc.cx(q[0], q[1]) - qc.measure(q[0], c[0]) - qc.measure(q[1], c[1]) - qc.measure(q[1], c[1]) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="as_soon_as_possible") - - expected_sched = Schedule( - # This is the schedule to implement sx gate. - (0, self.inst_map.get("sx", [0])), - # This is the schedule to implement cx gate - (0 + 16, self.inst_map.get("cx", [0, 1])), - # This is the schedule for the measurements on qubits 0 and 1 (combined) - ( - 0 + 16 + 64, - self.inst_map.get("measure", [0]).filter( - channels=[MeasureChannel(0), MeasureChannel(1)] - ), - ), - ( - 0 + 16 + 64, - self.inst_map.get("measure", [0]).filter( - channels=[AcquireChannel(0), AcquireChannel(1)] - ), - ), - # This is the schedule for the second measurement on qubit 1 - ( - 0 + 16 + 64 + 1792, - self.inst_map.get("measure", [1]).filter(channels=[MeasureChannel(1)]), - ), - ( - 0 + 16 + 64 + 1792, - self.inst_map.get("measure", [1]).filter(channels=[AcquireChannel(1)]), - ), - ) - self.assertEqual(sched.instructions, expected_sched.instructions) - - def test_3q_schedule(self): - """Test a schedule that was recommended by David McKay :D""" - - # ┌────┐ - # q0_0: ──■───┤ √X ├─────────── - # ┌─┴─┐ ├───┬┘ - # q0_1: ┤ X ├─┤ X ├───■──────── - # ├───┴┐└───┘ ┌─┴─┐┌────┐ - # q0_2: ┤ √X ├──────┤ X ├┤ √X ├ - # └────┘ └───┘└────┘ - # c0: 3/═══════════════════════ - - q = QuantumRegister(3) - c = ClassicalRegister(3) - qc = QuantumCircuit(q, c) - qc.cx(q[0], q[1]) - qc.sx(q[0]) - qc.x(q[1]) - qc.sx(q[2]) - qc.cx(q[1], q[2]) - qc.sx(q[2]) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="asap") - expected = Schedule( - (0, self.inst_map.get("cx", [0, 1])), - (0, self.inst_map.get("sx", [2])), - (0 + 64, self.inst_map.get("sx", [0])), - (0 + 64, self.inst_map.get("x", [1])), - (0 + 64 + 16, self.inst_map.get("cx", [1, 2])), - (0 + 64 + 16 + 64, self.inst_map.get("sx", [2])), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_schedule_multi(self): - """Test scheduling multiple circuits at once.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc0 = QuantumCircuit(q, c) - qc0.cx(q[0], q[1]) - qc1 = QuantumCircuit(q, c) - qc1.cx(q[0], q[1]) - with self.assertWarns(DeprecationWarning): - schedules = schedule([qc0, qc1], self.backend) - expected_insts = schedule(qc0, self.backend).instructions - for actual, expected in zip(schedules[0].instructions, expected_insts): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_circuit_name_kept(self): - """Test that the new schedule gets its name from the circuit.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c, name="CIRCNAME") - qc.cx(q[0], q[1]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="asap") - self.assertEqual(sched.name, qc.name) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - self.assertEqual(sched.name, qc.name) - - def test_can_add_gates_into_free_space(self): - """The scheduler does some time bookkeeping to know when qubits are free to be - scheduled. Make sure this works for qubits that are used in the future. This was - a bug, uncovered by this example: - - q0 = - - - - |X| - q1 = |X| |u2| |X| - - In ALAP scheduling, the next operation on qubit 0 would be added at t=0 rather - than immediately before the X gate. - """ - qr = QuantumRegister(2) - qc = QuantumCircuit(qr) - for i in range(2): - qc.sx(qr[i]) - qc.x(qr[i]) - qc.sx(qr[i]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule( - (0, self.inst_map.get("sx", [0])), - (0, self.inst_map.get("sx", [1])), - (0 + 16, self.inst_map.get("x", [0])), - (0 + 16, self.inst_map.get("x", [1])), - (0 + 16 + 16, self.inst_map.get("sx", [0])), - (0 + 16 + 16, self.inst_map.get("sx", [1])), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_barriers_in_middle(self): - """As a follow on to `test_can_add_gates_into_free_space`, similar issues - arose for barriers, specifically. - """ - qr = QuantumRegister(2) - qc = QuantumCircuit(qr) - for i in range(2): - qc.sx(qr[i]) - qc.barrier(qr[i]) - qc.x(qr[i]) - qc.barrier(qr[i]) - qc.sx(qr[i]) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - expected = Schedule( - (0, self.inst_map.get("sx", [0])), - (0, self.inst_map.get("sx", [1])), - (0 + 16, self.inst_map.get("x", [0])), - (0 + 16, self.inst_map.get("x", [1])), - (0 + 16 + 16, self.inst_map.get("sx", [0])), - (0 + 16 + 16, self.inst_map.get("sx", [1])), - ) - for actual, expected in zip(sched.instructions, expected.instructions): - self.assertEqual(actual[0], expected[0]) - self.assertEqual(actual[1], expected[1]) - - def test_parametric_input(self): - """Test that scheduling works with parametric pulses as input.""" - qr = QuantumRegister(1) - qc = QuantumCircuit(qr) - qc.append(Gate("gauss", 1, []), qargs=[qr[0]]) - with self.assertWarns(DeprecationWarning): - custom_gauss = Schedule( - Play(Gaussian(duration=25, sigma=4, amp=0.5, angle=pi / 2), DriveChannel(0)) - ) - self.inst_map.add("gauss", [0], custom_gauss) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, inst_map=self.inst_map) - self.assertEqual(sched.instructions[0], custom_gauss.instructions[0]) - - def test_pulse_gates(self): - """Test scheduling calibrated pulse gates.""" - q = QuantumRegister(2) - qc = QuantumCircuit(q) - qc.append(U2Gate(0, 0), [q[0]]) - qc.barrier(q[0], q[1]) - qc.append(U2Gate(0, 0), [q[1]]) - with self.assertWarns(DeprecationWarning): - qc.add_calibration( - "u2", [0], Schedule(Play(Gaussian(28, 0.2, 4), DriveChannel(0))), [0, 0] - ) - qc.add_calibration( - "u2", [1], Schedule(Play(Gaussian(28, 0.2, 4), DriveChannel(1))), [0, 0] - ) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend) - expected = Schedule( - Play(Gaussian(28, 0.2, 4), DriveChannel(0)), - (28, Schedule(Play(Gaussian(28, 0.2, 4), DriveChannel(1)))), - ) - self.assertEqual(sched.instructions, expected.instructions) - - def test_calibrated_measurements(self): - """Test scheduling calibrated measurements.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.sx(0) - qc.measure(q[0], c[0]) - - with self.assertWarns(DeprecationWarning): - meas_sched = Play( - GaussianSquare( - duration=1472, - sigma=64, - width=1216, - amp=0.2400000000002, - angle=-0.247301694, - name="my_custom_calibration", - ), - MeasureChannel(0), - ) - meas_sched |= Acquire(1472, AcquireChannel(0), MemorySlot(0)) - qc.add_calibration("measure", [0], meas_sched) - - sched = schedule(qc, self.backend) - expected = Schedule(self.inst_map.get("sx", [0]), (16, meas_sched)) - self.assertEqual(sched.instructions, expected.instructions) - - def test_subset_calibrated_measurements(self): - """Test that measurement calibrations can be added and used for some qubits, even - if the other qubits do not also have calibrated measurements.""" - qc = QuantumCircuit(3, 3) - qc.measure(0, 0) - qc.measure(1, 1) - qc.measure(2, 2) - meas_scheds = [] - with self.assertWarns(DeprecationWarning): - for qubit in [0, 2]: - meas = Play(Gaussian(1200, 0.2, 4), MeasureChannel(qubit)) + Acquire( - 1200, AcquireChannel(qubit), MemorySlot(qubit) - ) - meas_scheds.append(meas) - with self.assertWarns(DeprecationWarning): - qc.add_calibration("measure", [qubit], meas) - - meas = macros.measure(qubits=[1], backend=self.backend, qubit_mem_slots={0: 0, 1: 1}) - meas = meas.exclude(channels=[AcquireChannel(0), AcquireChannel(2)]) - sched = schedule(qc, self.backend) - expected = Schedule(meas_scheds[0], meas_scheds[1], meas) - self.assertEqual(sched.instructions, expected.instructions) - - def test_clbits_of_calibrated_measurements(self): - """Test that calibrated measurements are only used when the classical bits also match.""" - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.measure(q[0], c[1]) - - with self.assertWarns(DeprecationWarning): - meas_sched = Play(Gaussian(1200, 0.2, 4), MeasureChannel(0)) - meas_sched |= Acquire(1200, AcquireChannel(0), MemorySlot(0)) - qc.add_calibration("measure", [0], meas_sched) - - sched = schedule(qc, self.backend) - # Doesn't use the calibrated schedule because the classical memory slots do not match - expected = Schedule(macros.measure([0], self.backend, qubit_mem_slots={0: 1})) - self.assertEqual(sched.instructions, expected.instructions) - - def test_metadata_is_preserved_alap(self): - """Test that circuit metadata is preserved in output schedule with alap.""" - q = QuantumRegister(2) - qc = QuantumCircuit(q) - qc.sx(q[0]) - qc.barrier(q[0], q[1]) - qc.sx(q[1]) - qc.metadata = {"experiment_type": "gst", "execution_number": "1234"} - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="alap") - self.assertEqual({"experiment_type": "gst", "execution_number": "1234"}, sched.metadata) - - def test_metadata_is_preserved_asap(self): - """Test that circuit metadata is preserved in output schedule with asap.""" - q = QuantumRegister(2) - qc = QuantumCircuit(q) - qc.sx(q[0]) - qc.barrier(q[0], q[1]) - qc.sx(q[1]) - qc.metadata = {"experiment_type": "gst", "execution_number": "1234"} - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, method="asap") - self.assertEqual({"experiment_type": "gst", "execution_number": "1234"}, sched.metadata) - - def test_scheduler_with_params_bound(self): - """Test scheduler with parameters defined and bound""" - x = Parameter("x") - qc = QuantumCircuit(2) - qc.append(Gate("pulse_gate", 1, [x]), [0]) - with self.assertWarns(DeprecationWarning): - expected_schedule = Schedule() - qc.add_calibration( - gate="pulse_gate", qubits=[0], schedule=expected_schedule, params=[x] - ) - qc = qc.assign_parameters({x: 1}) - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend) - self.assertEqual(sched, expected_schedule) - - def test_scheduler_with_params_not_bound(self): - """Test scheduler with parameters defined but not bound""" - x = Parameter("amp") - qc = QuantumCircuit(2) - qc.append(Gate("pulse_gate", 1, [x]), [0]) - with self.assertWarns(DeprecationWarning): - with build() as expected_schedule: - play(Gaussian(duration=160, amp=x, sigma=40), DriveChannel(0)) - qc.add_calibration( - gate="pulse_gate", qubits=[0], schedule=expected_schedule, params=[x] - ) - sched = schedule(qc, self.backend) - with self.assertWarns(DeprecationWarning): - self.assertEqual(sched, transforms.target_qobj_transform(expected_schedule)) - - def test_schedule_block_in_instmap(self): - """Test schedule block in instmap can be scheduled.""" - duration = Parameter("duration") - - with self.assertWarns(DeprecationWarning): - with build() as pulse_prog: - play(Gaussian(duration, 0.1, 10), DriveChannel(0)) - - instmap = InstructionScheduleMap() - instmap.add("block_gate", (0,), pulse_prog, ["duration"]) - - qc = QuantumCircuit(1) - qc.append(Gate("block_gate", 1, [duration]), [0]) - qc.assign_parameters({duration: 100}, inplace=True) - - with self.assertWarns(DeprecationWarning): - sched = schedule(qc, self.backend, inst_map=instmap) - - ref_sched = Schedule() - ref_sched += Play(Gaussian(100, 0.1, 10), DriveChannel(0)) - - self.assertEqual(sched, ref_sched) - - def test_inst_sched_map_get_measure_0(self): - """Test that Schedule returned by backend.instruction_schedule_map.get('measure', [0]) - is actually Schedule for just qubit_0""" - with self.assertWarns(DeprecationWarning): - sched_from_backend = self.backend.instruction_schedule_map.get("measure", [0]) - expected_sched = Schedule( - ( - 0, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(0), - name="pulse_2", - ), - ), - ( - 0, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(1), - name="pulse_2", - ), - ), - ( - 0, - Play( - Waveform(samples=self.pulse_2_samples, name="pulse_2"), - MeasureChannel(2), - name="pulse_2", - ), - ), - (0, Acquire(1792, AcquireChannel(0), MemorySlot(0))), - (0, Acquire(1792, AcquireChannel(1), MemorySlot(1))), - (0, Acquire(1792, AcquireChannel(2), MemorySlot(2))), - name="measure", - ) - self.assertEqual(sched_from_backend, expected_sched) diff --git a/test/python/transpiler/test_calibrationbuilder.py b/test/python/transpiler/test_calibrationbuilder.py index 04bc8aef00da..314d9bd5c1f1 100644 --- a/test/python/transpiler/test_calibrationbuilder.py +++ b/test/python/transpiler/test_calibrationbuilder.py @@ -18,7 +18,7 @@ from ddt import data, ddt from qiskit.converters import circuit_to_dag -from qiskit import circuit, schedule, QiskitError, QuantumCircuit +from qiskit import circuit, QiskitError, QuantumCircuit from qiskit.circuit import Parameter from qiskit.circuit.library.standard_gates import SXGate, RXGate from qiskit.providers.fake_provider import Fake7QPulseV1, Fake27QPulseV1, GenericBackendV2 @@ -168,105 +168,6 @@ def build_forward( return ref_sched - def build_reverse( - self, - backend, - theta, - u0p_play, - d1p_play, - u0m_play, - d1m_play, - ): - """A helper function to generate reference pulse schedule for backward direction.""" - duration = self.compute_stretch_duration(u0p_play, theta) - width = self.compute_stretch_width(u0p_play, theta) - inst_sched_map = backend.defaults().instruction_schedule_map - - rz_qc_q0 = QuantumCircuit(1) - rz_qc_q0.rz(pi / 2, 0) - - rz_qc_q1 = QuantumCircuit(2) - rz_qc_q1.rz(pi / 2, 1) - - with self.assertWarns(DeprecationWarning): - rz_sched_q0 = schedule(rz_qc_q0, backend) - rz_sched_q1 = schedule(rz_qc_q1, backend) - - with builder.build( - backend, - default_alignment="sequential", - ) as ref_sched: - - # Get Schedule from the backend for Gates equivalent to Hadamard gates. - with builder.align_left(): - builder.call(rz_sched_q0) - builder.call( - inst_sched_map._get_calibration_entry(SXGate(), qubits=(0,)).get_schedule() - ) - builder.call(rz_sched_q0) - - builder.call(rz_sched_q1) - builder.call( - inst_sched_map._get_calibration_entry(SXGate(), qubits=(1,)).get_schedule() - ) - builder.call(rz_sched_q1) - - with builder.align_left(): - # Positive CRs - u0p_params = u0p_play.pulse.parameters - u0p_params["duration"] = duration - u0p_params["width"] = width - builder.play( - GaussianSquare(**u0p_params), - ControlChannel(0), - ) - d1p_params = d1p_play.pulse.parameters - d1p_params["duration"] = duration - d1p_params["width"] = width - builder.play( - GaussianSquare(**d1p_params), - DriveChannel(1), - ) - - # Get Schedule for 'x' gate from the backend. - builder.call(inst_sched_map._get_calibration_entry("x", (0,)).get_schedule()) - - with builder.align_left(): - # Negative CRs - u0m_params = u0m_play.pulse.parameters - u0m_params["duration"] = duration - u0m_params["width"] = width - builder.play( - GaussianSquare(**u0m_params), - ControlChannel(0), - ) - d1m_params = d1m_play.pulse.parameters - d1m_params["duration"] = duration - d1m_params["width"] = width - builder.play( - GaussianSquare(**d1m_params), - DriveChannel(1), - ) - - # Get Schedule for 'x' gate from the backend. - builder.call(inst_sched_map._get_calibration_entry("x", (0,)).get_schedule()) - - # Get Schedule from the backend for Gates equivalent to Hadamard gates. - with builder.align_left(): - builder.call(rz_sched_q0) - builder.call( - inst_sched_map._get_calibration_entry(SXGate(), qubits=(0,)).get_schedule() - ) - builder.call(rz_sched_q0) - - builder.call(rz_sched_q1) - builder.call( - inst_sched_map._get_calibration_entry(SXGate(), qubits=(1,)).get_schedule() - ) - builder.call(rz_sched_q1) - - return ref_sched - @data(-np.pi / 4, 0.1, np.pi / 4, np.pi / 2, np.pi) def test_rzx_calibration_cr_pulse_stretch(self, theta: float): """Test that cross resonance pulse durations are computed correctly.""" @@ -323,60 +224,6 @@ def test_raise(self): qubits = [qubit_map[q] for q in node.qargs] _pass.get_calibration(node.op, qubits) - def test_ecr_cx_forward(self): - """Test that correct pulse sequence is generated for native CR pair.""" - # Sufficiently large angle to avoid minimum duration, i.e. amplitude rescaling - theta = np.pi / 4 - - qc = circuit.QuantumCircuit(2) - qc.rzx(theta, 0, 1) - - with self.assertWarns(DeprecationWarning): - backend = Fake27QPulseV1() - inst_map = backend.defaults().instruction_schedule_map - _pass = RZXCalibrationBuilder(inst_map) - test_qc = PassManager(_pass).run(qc) - - cr_schedule = inst_map.get("cx", (0, 1)) - ref_sched = self.build_forward( - backend, - theta, - self.u0p_play(cr_schedule), - self.d1p_play(cr_schedule), - self.u0m_play(cr_schedule), - self.d1m_play(cr_schedule), - ) - - with self.assertWarns(DeprecationWarning): - self.assertEqual(schedule(test_qc, backend), target_qobj_transform(ref_sched)) - - def test_ecr_cx_reverse(self): - """Test that correct pulse sequence is generated for non-native CR pair.""" - # Sufficiently large angle to avoid minimum duration, i.e. amplitude rescaling - theta = np.pi / 4 - - qc = circuit.QuantumCircuit(2) - qc.rzx(theta, 1, 0) - - with self.assertWarns(DeprecationWarning): - backend = Fake27QPulseV1() - inst_map = backend.defaults().instruction_schedule_map - _pass = RZXCalibrationBuilder(inst_map) - test_qc = PassManager(_pass).run(qc) - - cr_schedule = inst_map.get("cx", (0, 1)) - ref_sched = self.build_reverse( - backend, - theta, - self.u0p_play(cr_schedule), - self.d1p_play(cr_schedule), - self.u0m_play(cr_schedule), - self.d1m_play(cr_schedule), - ) - - with self.assertWarns(DeprecationWarning): - self.assertEqual(schedule(test_qc, backend), target_qobj_transform(ref_sched)) - def test_pass_alive_with_dcx_ish(self): """Test if the pass is not terminated by error with direct CX input.""" cx_sched = Schedule() @@ -438,35 +285,6 @@ def build_forward( return ref_sched - def test_ecr_cx_forward(self): - """Test that correct pulse sequence is generated for native CR pair. - - .. notes:: - No echo builder only supports native direction. - """ - # Sufficiently large angle to avoid minimum duration, i.e. amplitude rescaling - theta = np.pi / 4 - - qc = circuit.QuantumCircuit(2) - qc.rzx(theta, 0, 1) - - with self.assertWarns(DeprecationWarning): - backend = Fake27QPulseV1() - inst_map = backend.defaults().instruction_schedule_map - - _pass = RZXCalibrationBuilderNoEcho(inst_map) - test_qc = PassManager(_pass).run(qc) - - cr_schedule = inst_map.get("cx", (0, 1)) - ref_sched = self.build_forward( - theta, - self.u0p_play(cr_schedule), - self.d1p_play(cr_schedule), - ) - - with self.assertWarns(DeprecationWarning): - self.assertEqual(schedule(test_qc, backend), target_qobj_transform(ref_sched)) - # # TODO - write test for forward ECR native pulse # def test_ecr_forward(self): From 2d78cd8868e7219332ff5375a1fafa0956a8d52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Fri, 7 Feb 2025 15:22:18 +0100 Subject: [PATCH 2/4] Forgot the reno --- .../notes/remove-schedule-sequence-a6249577da8d1c86.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml diff --git a/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml b/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml new file mode 100644 index 000000000000..8aa9190e8511 --- /dev/null +++ b/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + The functions ``sequence`` and ``schedule`` from the :mod:`.compiler` + module have been removed following their deprecation in Qiskit 1.3. + These functions depend on and relate to the Pulse package which is + also being removed in Qiskit 2.0. \ No newline at end of file From c6f1366c68a9d5914217f42420763146a064899e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Fri, 7 Feb 2025 16:09:25 +0100 Subject: [PATCH 3/4] Fix tests, lint, update reno --- qiskit/__init__.py | 2 - ...ve-schedule-sequence-a6249577da8d1c86.yaml | 5 +- test/python/pulse/test_builder.py | 99 +------------------ .../transpiler/test_calibrationbuilder.py | 1 - 4 files changed, 5 insertions(+), 102 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index eb5df6f9b916..376881a56799 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -138,8 +138,6 @@ "QiskitError", "QuantumCircuit", "QuantumRegister", - "schedule", - "sequence", "transpile", "generate_preset_pass_manager", ] diff --git a/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml b/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml index 8aa9190e8511..b52843040085 100644 --- a/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml +++ b/releasenotes/notes/remove-schedule-sequence-a6249577da8d1c86.yaml @@ -3,5 +3,8 @@ upgrade: - | The functions ``sequence`` and ``schedule`` from the :mod:`.compiler` module have been removed following their deprecation in Qiskit 1.3. - These functions depend on and relate to the Pulse package which is + They relied on being able to translate circuits to pulse using backend + definitions, a capability that is no longer present. For this reason + they have been removed with no proposed alternative. + Note that this removals relate to the Pulse package which is also being removed in Qiskit 2.0. \ No newline at end of file diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index 9501d176c9e0..dcc113f5742a 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -12,10 +12,9 @@ """Test pulse builder context utilities.""" -from math import pi import numpy as np -from qiskit import circuit, compiler, pulse +from qiskit import circuit, pulse from qiskit.pulse import builder, exceptions, macros from qiskit.pulse.instructions import directives from qiskit.pulse.transforms import target_qobj_transform @@ -764,102 +763,6 @@ def test_delay_qubits(self): class TestBuilderComposition(TestBuilder): """Test more sophisticated composite builder examples.""" - def test_complex_build(self): - """Test a general program build with nested contexts, - circuits and macros.""" - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - d2 = pulse.DriveChannel(2) - delay_dur = 30 - short_dur = 20 - long_dur = 49 - - def get_sched(qubit_idx: [int], backend): - qc = circuit.QuantumCircuit(2) - for idx in qubit_idx: - qc.append(circuit.library.U2Gate(0, pi / 2), [idx]) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - transpiled = compiler.transpile(qc, backend=backend, optimization_level=1) - with self.assertWarns(DeprecationWarning): - return compiler.schedule(transpiled, backend) - - with pulse.build(self.backend) as schedule: - with pulse.align_sequential(): - pulse.delay(delay_dur, d0) - pulse.call(get_sched([1], self.backend)) - - with pulse.align_right(): - pulse.play(library.Constant(short_dur, 0.1), d1) - pulse.play(library.Constant(long_dur, 0.1), d2) - pulse.call(get_sched([1], self.backend)) - - with pulse.align_left(): - pulse.call(get_sched([0, 1, 0], self.backend)) - - pulse.measure(0) - - # prepare and schedule circuits that will be used. - single_u2_qc = circuit.QuantumCircuit(2) - single_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [1]) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - single_u2_qc = compiler.transpile(single_u2_qc, self.backend, optimization_level=1) - with self.assertWarns(DeprecationWarning): - single_u2_sched = compiler.schedule(single_u2_qc, self.backend) - - # sequential context - sequential_reference = pulse.Schedule() - sequential_reference += instructions.Delay(delay_dur, d0) - sequential_reference.insert(delay_dur, single_u2_sched, inplace=True) - - # align right - align_right_reference = pulse.Schedule() - align_right_reference += pulse.Play(library.Constant(long_dur, 0.1), d2) - align_right_reference.insert( - long_dur - single_u2_sched.duration, single_u2_sched, inplace=True - ) - align_right_reference.insert( - long_dur - single_u2_sched.duration - short_dur, - pulse.Play(library.Constant(short_dur, 0.1), d1), - inplace=True, - ) - - # align left - triple_u2_qc = circuit.QuantumCircuit(2) - triple_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [0]) - triple_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [1]) - triple_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [0]) - with self.assertWarnsRegex( - DeprecationWarning, - expected_regex="The `transpile` function will " - "stop supporting inputs of type `BackendV1`", - ): - triple_u2_qc = compiler.transpile(triple_u2_qc, self.backend, optimization_level=1) - with self.assertWarns(DeprecationWarning): - align_left_reference = compiler.schedule(triple_u2_qc, self.backend, method="alap") - - # measurement - measure_reference = macros.measure( - qubits=[0], inst_map=self.inst_map, meas_map=self.configuration.meas_map - ) - reference = pulse.Schedule() - reference += sequential_reference - # Insert so that the long pulse on d2 occurs as early as possible - # without an overval on d1. - insert_time = reference.ch_stop_time(d1) - align_right_reference.ch_start_time(d1) - reference.insert(insert_time, align_right_reference, inplace=True) - reference.insert(reference.ch_stop_time(d0, d1), align_left_reference, inplace=True) - reference += measure_reference - - self.assertScheduleEqual(schedule, reference) - @decorate_test_methods(ignore_pulse_deprecation_warnings) class TestSubroutineCall(TestBuilder): diff --git a/test/python/transpiler/test_calibrationbuilder.py b/test/python/transpiler/test_calibrationbuilder.py index 314d9bd5c1f1..58b7db611368 100644 --- a/test/python/transpiler/test_calibrationbuilder.py +++ b/test/python/transpiler/test_calibrationbuilder.py @@ -36,7 +36,6 @@ ) from qiskit.pulse import builder -from qiskit.pulse.transforms import target_qobj_transform from qiskit.dagcircuit import DAGOpNode from qiskit.transpiler import PassManager, Target, InstructionProperties from qiskit.transpiler.passes.calibration.builders import ( From 2aa980e668dbd0dd1ec2adc4026ccadce39a55f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Fri, 7 Feb 2025 16:41:58 +0100 Subject: [PATCH 4/4] Attempt to fix docs (removing examples, but the functionality is deprecated and the whole docstring will go soon) --- qiskit/pulse/builder.py | 149 +-------------------- qiskit/visualization/timeline/interface.py | 6 +- 2 files changed, 4 insertions(+), 151 deletions(-) diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index bc057980cf09..70ecc9d11dfa 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -89,160 +89,13 @@ representations, while simultaneously applying a long decoupling pulse to a neighboring qubit. We terminate the experiment with a measurement to observe the state we prepared. This program which mixes circuits and pulses will be -automatically lowered to be run as a pulse program: - -.. plot:: - :alt: Output from the previous code. - :include-source: - - from math import pi - from qiskit.compiler import schedule - from qiskit.circuit import QuantumCircuit - - from qiskit import pulse - from qiskit.providers.fake_provider import GenericBackendV2 - - backend = GenericBackendV2(num_qubits=5, calibrate_instructions=True) - - d2 = pulse.DriveChannel(2) - - qc = QuantumCircuit(2) - # Hadamard - qc.rz(pi/2, 0) - qc.sx(0) - qc.rz(pi/2, 0) - - qc.cx(0, 1) - - bell_sched = schedule(qc, backend) - - with pulse.build(backend) as decoupled_bell_prep_and_measure: - # We call our bell state preparation schedule constructed above. - with pulse.align_right(): - pulse.call(bell_sched) - pulse.play(pulse.Constant(bell_sched.duration, 0.02), d2) - pulse.barrier(0, 1, 2) - registers = pulse.measure_all() - - decoupled_bell_prep_and_measure.draw() - +automatically lowered to be run as a pulse program. With the pulse builder we are able to blend programming on qubits and channels. While the pulse schedule is based on instructions that operate on channels, the pulse builder automatically handles the mapping from qubits to channels for you. -In the example below we demonstrate some more features of the pulse builder: - -.. plot:: - :include-source: - :nofigs: - - import math - from qiskit.compiler import schedule - - from qiskit import pulse, QuantumCircuit - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - - with pulse.build(backend) as pulse_prog: - # Create a pulse. - gaussian_pulse = pulse.Gaussian(10, 1.0, 2) - # Get the qubit's corresponding drive channel from the backend. - d0 = pulse.drive_channel(0) - d1 = pulse.drive_channel(1) - # Play a pulse at t=0. - pulse.play(gaussian_pulse, d0) - # Play another pulse directly after the previous pulse at t=10. - pulse.play(gaussian_pulse, d0) - # The default scheduling behavior is to schedule pulses in parallel - # across channels. For example, the statement below - # plays the same pulse on a different channel at t=0. - pulse.play(gaussian_pulse, d1) - - # We also provide pulse scheduling alignment contexts. - # The default alignment context is align_left. - - # The sequential context schedules pulse instructions sequentially in time. - # This context starts at t=10 due to earlier pulses above. - with pulse.align_sequential(): - pulse.play(gaussian_pulse, d0) - # Play another pulse after at t=20. - pulse.play(gaussian_pulse, d1) - - # We can also nest contexts as each instruction is - # contained in its local scheduling context. - # The output of a child context is a context-schedule - # with the internal instructions timing fixed relative to - # one another. This is schedule is then called in the parent context. - - # Context starts at t=30. - with pulse.align_left(): - # Start at t=30. - pulse.play(gaussian_pulse, d0) - # Start at t=30. - pulse.play(gaussian_pulse, d1) - # Context ends at t=40. - - # Alignment context where all pulse instructions are - # aligned to the right, ie., as late as possible. - with pulse.align_right(): - # Shift the phase of a pulse channel. - pulse.shift_phase(math.pi, d1) - # Starts at t=40. - pulse.delay(100, d0) - # Ends at t=140. - - # Starts at t=130. - pulse.play(gaussian_pulse, d1) - # Ends at t=140. - - # Acquire data for a qubit and store in a memory slot. - pulse.acquire(100, 0, pulse.MemorySlot(0)) - - # We also support a variety of macros for common operations. - - # Measure all qubits. - pulse.measure_all() - - # Delay on some qubits. - # This requires knowledge of which channels belong to which qubits. - # delay for 100 cycles on qubits 0 and 1. - pulse.delay_qubits(100, 0, 1) - - # Call a schedule for a quantum circuit thereby inserting into - # the pulse schedule. - qc = QuantumCircuit(2, 2) - qc.cx(0, 1) - qc_sched = schedule(qc, backend) - pulse.call(qc_sched) - - - # It is also be possible to call a preexisting schedule - tmp_sched = pulse.Schedule() - tmp_sched += pulse.Play(gaussian_pulse, d0) - pulse.call(tmp_sched) - - # We also support: - - # frequency instructions - pulse.set_frequency(5.0e9, d0) - - # phase instructions - pulse.shift_phase(0.1, d0) - - # offset contexts - with pulse.phase_offset(math.pi, d0): - pulse.play(gaussian_pulse, d0) - - -The above is just a small taste of what is possible with the builder. See the rest of the module -documentation for more information on its capabilities. - .. autofunction:: build diff --git a/qiskit/visualization/timeline/interface.py b/qiskit/visualization/timeline/interface.py index 50dd006633a7..1818f78a9073 100644 --- a/qiskit/visualization/timeline/interface.py +++ b/qiskit/visualization/timeline/interface.py @@ -301,7 +301,7 @@ def draw( :alt: Output from the previous code. :include-source: - from qiskit import QuantumCircuit, transpile, schedule + from qiskit import QuantumCircuit, transpile from qiskit.visualization.timeline import draw from qiskit.providers.fake_provider import GenericBackendV2 @@ -318,7 +318,7 @@ def draw( :alt: Output from the previous code. :include-source: - from qiskit import QuantumCircuit, transpile, schedule + from qiskit import QuantumCircuit, transpile from qiskit.visualization.timeline import draw, IQXSimple from qiskit.providers.fake_provider import GenericBackendV2 @@ -335,7 +335,7 @@ def draw( :alt: Output from the previous code. :include-source: - from qiskit import QuantumCircuit, transpile, schedule + from qiskit import QuantumCircuit, transpile from qiskit.visualization.timeline import draw, IQXDebugging from qiskit.providers.fake_provider import GenericBackendV2