diff --git a/qiskit/transpiler/passes/calibration/builders.py b/qiskit/transpiler/passes/calibration/builders.py index e1683d477353..bcafc1ca1471 100644 --- a/qiskit/transpiler/passes/calibration/builders.py +++ b/qiskit/transpiler/passes/calibration/builders.py @@ -170,14 +170,14 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P if target_area > gaussian_area: width = (target_area - gaussian_area) / abs(amp) - duration = math.ceil((width + n_sigmas * sigma) / sample_mult) * sample_mult + duration = round((width + n_sigmas * sigma) / sample_mult) * sample_mult return Play( GaussianSquare(amp=sign * amp, width=width, sigma=sigma, duration=duration), channel=instruction.channel, ) else: amp_scale = sign * target_area / gaussian_area - duration = math.ceil(n_sigmas * sigma / sample_mult) * sample_mult + duration = round(n_sigmas * sigma / sample_mult) * sample_mult return Play( GaussianSquare(amp=amp * amp_scale, width=0, sigma=sigma, duration=duration), channel=instruction.channel, diff --git a/releasenotes/notes/pulse-round-a014390e414c79c8.yaml b/releasenotes/notes/pulse-round-a014390e414c79c8.yaml new file mode 100644 index 000000000000..dfd3feb62abf --- /dev/null +++ b/releasenotes/notes/pulse-round-a014390e414c79c8.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixes a bug in the :class:`~.RZXCalibrationBuilder` transpiler pass that was causing pulses to sometimes be + constructed with incorrect durations. \ No newline at end of file diff --git a/test/python/pulse/test_calibrationbuilder.py b/test/python/pulse/test_calibrationbuilder.py index 79cc20fb6654..5f6e6cf2cda3 100644 --- a/test/python/pulse/test_calibrationbuilder.py +++ b/test/python/pulse/test_calibrationbuilder.py @@ -12,25 +12,20 @@ """Test the RZXCalibrationBuilderNoEcho.""" -from math import pi, erf, ceil +from math import erf, pi import numpy as np +from ddt import data, ddt from qiskit import circuit, schedule -from qiskit.transpiler import PassManager +from qiskit.pulse import ControlChannel, Delay, DriveChannel, GaussianSquare, Play, ShiftPhase from qiskit.test import QiskitTestCase -from qiskit.pulse import ( - Play, - Delay, - ShiftPhase, - ControlChannel, - DriveChannel, - GaussianSquare, -) +from qiskit.test.mock import FakeAthens +from qiskit.transpiler import PassManager from qiskit.transpiler.passes.calibration.builders import ( + RZXCalibrationBuilder, RZXCalibrationBuilderNoEcho, ) -from qiskit.test.mock import FakeAthens class TestCalibrationBuilder(QiskitTestCase): @@ -42,6 +37,30 @@ def setUp(self): self.inst_map = self.backend.defaults().instruction_schedule_map +@ddt +class TestRZXCalibrationBuilder(TestCalibrationBuilder): + """Test RZXCalibrationBuilder.""" + + @data(np.pi / 4, np.pi / 2, np.pi) + def test_rzx_calibration_builder_duration(self, theta: float): + """Test that pulse durations are computed correctly.""" + width = 512.00000001 + sigma = 64 + n_sigmas = 4 + duration = width + n_sigmas * sigma + sample_mult = 16 + amp = 1.0 + pulse = GaussianSquare(duration=duration, amp=amp, sigma=sigma, width=width) + instruction = Play(pulse, ControlChannel(1)) + scaled = RZXCalibrationBuilder.rescale_cr_inst(instruction, theta, sample_mult=sample_mult) + gaussian_area = abs(amp) * sigma * np.sqrt(2 * np.pi) * erf(n_sigmas) + area = gaussian_area + abs(amp) * width + target_area = abs(theta) / (np.pi / 2.0) * area + width = (target_area - gaussian_area) / abs(amp) + expected_duration = round((width + n_sigmas * sigma) / sample_mult) * sample_mult + self.assertEqual(scaled.duration, expected_duration) + + class TestRZXCalibrationBuilderNoEcho(TestCalibrationBuilder): """Test RZXCalibrationBuilderNoEcho.""" @@ -98,7 +117,7 @@ def test_rzx_calibration_builder(self): area = gaussian_area + abs(amp) * width target_area = abs(theta) / (np.pi / 2.0) * area width = (target_area - gaussian_area) / abs(amp) - duration = ceil((width + n_sigmas * sigma) / sample_mult) * sample_mult + duration = round((width + n_sigmas * sigma) / sample_mult) * sample_mult # Check whether the durations of the RZX pulse and # the scaled CR pulse from the CX gate match.