diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index b7cb04df9503..8c90777eae01 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -24,7 +24,7 @@ import numpy as np -from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType from qiskit.pulse.exceptions import PulseError from qiskit.pulse.library.pulse import Pulse from qiskit.pulse.library.waveform import Waveform @@ -595,16 +595,16 @@ class ScalableSymbolicPulse(SymbolicPulse): additional_msg=( "Instead, use a float for ``amp`` (for the magnitude) and a float for ``angle``" ), - since="0.23.0", - pending=True, + since="0.25.0", + pending=False, predicate=lambda amp: isinstance(amp, complex), ) def __init__( self, pulse_type: str, duration: Union[ParameterExpression, int], - amp: Union[ParameterExpression, float, complex], - angle: Union[ParameterExpression, float], + amp: ParameterValueType, + angle: ParameterValueType, parameters: Optional[Dict[str, Union[ParameterExpression, complex]]] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, @@ -739,9 +739,9 @@ class Gaussian(metaclass=_PulseType): def __new__( cls, duration: Union[int, ParameterExpression], - amp: Union[complex, float, ParameterExpression], - sigma: Union[float, ParameterExpression], - angle: Optional[Union[float, ParameterExpression]] = None, + amp: ParameterValueType, + sigma: ParameterValueType, + angle: Optional[ParameterValueType] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> ScalableSymbolicPulse: @@ -750,7 +750,7 @@ def __new__( Args: duration: Pulse length in terms of the sampling period `dt`. amp: The magnitude of the amplitude of the Gaussian envelope. - Complex amp support will be deprecated. + Complex amp support is deprecated. sigma: A measure of how wide or narrow the Gaussian peak is; described mathematically in the class docstring. angle: The angle of the complex amplitude of the Gaussian envelope. Default value 0. @@ -835,11 +835,11 @@ class GaussianSquare(metaclass=_PulseType): def __new__( cls, duration: Union[int, ParameterExpression], - amp: Union[complex, float, ParameterExpression], - sigma: Union[float, ParameterExpression], - width: Optional[Union[float, ParameterExpression]] = None, - angle: Optional[Union[float, ParameterExpression]] = None, - risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, + amp: ParameterValueType, + sigma: ParameterValueType, + width: Optional[ParameterValueType] = None, + angle: Optional[ParameterValueType] = None, + risefall_sigma_ratio: Optional[ParameterValueType] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> ScalableSymbolicPulse: @@ -848,7 +848,7 @@ def __new__( Args: duration: Pulse length in terms of the sampling period `dt`. amp: The magnitude of the amplitude of the Gaussian and square pulse. - Complex amp support will be deprecated. + Complex amp support is deprecated. sigma: A measure of how wide or narrow the Gaussian risefall is; see the class docstring for more details. width: The duration of the embedded square pulse. @@ -1378,10 +1378,10 @@ class Drag(metaclass=_PulseType): def __new__( cls, duration: Union[int, ParameterExpression], - amp: Union[complex, float, ParameterExpression], - sigma: Union[float, ParameterExpression], - beta: Union[float, ParameterExpression], - angle: Optional[Union[float, ParameterExpression]] = None, + amp: ParameterValueType, + sigma: ParameterValueType, + beta: ParameterValueType, + angle: Optional[ParameterValueType] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> ScalableSymbolicPulse: @@ -1390,7 +1390,7 @@ def __new__( Args: duration: Pulse length in terms of the sampling period `dt`. amp: The magnitude of the amplitude of the DRAG envelope. - Complex amp support will be deprecated. + Complex amp support is deprecated. sigma: A measure of how wide or narrow the Gaussian peak is; described mathematically in the class docstring. beta: The correction amplitude. @@ -1449,8 +1449,8 @@ class Constant(metaclass=_PulseType): def __new__( cls, duration: Union[int, ParameterExpression], - amp: Union[complex, float, ParameterExpression], - angle: Optional[Union[float, ParameterExpression]] = None, + amp: ParameterValueType, + angle: Optional[ParameterValueType] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> ScalableSymbolicPulse: @@ -1459,7 +1459,7 @@ def __new__( Args: duration: Pulse length in terms of the sampling period `dt`. amp: The magnitude of the amplitude of the square envelope. - Complex amp support will be deprecated. + Complex amp support is deprecated. angle: The angle of the complex amplitude of the square envelope. Default value 0. name: Display name for this pulse envelope. limit_amplitude: If ``True``, then limit the amplitude of the diff --git a/releasenotes/notes/deprecate-complex-amp-41381bd9722bc878.yaml b/releasenotes/notes/deprecate-complex-amp-41381bd9722bc878.yaml new file mode 100644 index 000000000000..23eadb7ff391 --- /dev/null +++ b/releasenotes/notes/deprecate-complex-amp-41381bd9722bc878.yaml @@ -0,0 +1,16 @@ +--- +deprecations: + - | + Initializing a :class:`~qiskit.pulse.library.ScalableSymbolicPulse` with complex `amp` value is now deprecated. + This change also affects the following library pulses: + + * :class:`~qiskit.pulse.library.Gaussian` + * :class:`~qiskit.pulse.library.GaussianSquare` + * :class:`~qiskit.pulse.library.Drag` + * :class:`~qiskit.pulse.library.Constant` + + Initializing them with complex `amp` is now deprecated as well. + + Instead, one should use two floats for the `amp` and `angle` parameters, where `amp` represents the + magnitude of the complex amplitude, and `angle` represents the angle of the complex amplitude. i.e. the + complex amplitude is given by `amp` * exp(1j * `angle`). diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py index 04c8b3616a21..debd47e3a48b 100644 --- a/test/python/compiler/test_assembler.py +++ b/test/python/compiler/test_assembler.py @@ -1278,12 +1278,21 @@ def test_assemble_schedule_enum(self): def test_assemble_parametric(self): """Test that parametric pulses can be assembled properly into a PulseQobj.""" + amp = [0.5, 0.6, 1, 0.2] + angle = [np.pi / 2, 0.6, 0, 0] sched = pulse.Schedule(name="test_parametric") - sched += Play(pulse.Gaussian(duration=25, sigma=4, amp=0.5j), DriveChannel(0)) - sched += Play(pulse.Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), DriveChannel(1)) - sched += Play(pulse.Constant(duration=25, amp=1), DriveChannel(2)) + sched += Play( + pulse.Gaussian(duration=25, sigma=4, amp=amp[0], angle=angle[0]), DriveChannel(0) + ) + sched += Play( + pulse.Drag(duration=25, amp=amp[1], angle=angle[1], sigma=7.8, beta=4), DriveChannel(1) + ) + sched += Play(pulse.Constant(duration=25, amp=amp[2], angle=angle[2]), DriveChannel(2)) sched += ( - Play(pulse.GaussianSquare(duration=150, amp=0.2, sigma=8, width=140), MeasureChannel(0)) + Play( + pulse.GaussianSquare(duration=150, amp=amp[3], angle=angle[3], sigma=8, width=140), + MeasureChannel(0), + ) << sched.duration ) backend = FakeOpenPulse3Q() @@ -1302,16 +1311,24 @@ def test_assemble_parametric(self): self.assertEqual(qobj_insts[1].pulse_shape, "drag") self.assertEqual(qobj_insts[2].pulse_shape, "constant") self.assertEqual(qobj_insts[3].pulse_shape, "gaussian_square") - self.assertDictEqual(qobj_insts[0].parameters, {"duration": 25, "sigma": 4, "amp": 0.5j}) self.assertDictEqual( - qobj_insts[1].parameters, {"duration": 25, "sigma": 7.8, "amp": 0.2 + 0.3j, "beta": 4} + qobj_insts[0].parameters, + {"duration": 25, "sigma": 4, "amp": amp[0] * np.exp(1j * angle[0])}, ) - self.assertDictEqual(qobj_insts[2].parameters, {"duration": 25, "amp": 1}) self.assertDictEqual( - qobj_insts[3].parameters, {"duration": 150, "sigma": 8, "amp": 0.2, "width": 140} + qobj_insts[1].parameters, + {"duration": 25, "sigma": 7.8, "amp": amp[1] * np.exp(1j * angle[1]), "beta": 4}, + ) + self.assertDictEqual( + qobj_insts[2].parameters, {"duration": 25, "amp": amp[2] * np.exp(1j * angle[2])} + ) + self.assertDictEqual( + qobj_insts[3].parameters, + {"duration": 150, "sigma": 8, "amp": amp[3] * np.exp(1j * angle[3]), "width": 140}, ) self.assertEqual( - qobj.to_dict()["experiments"][0]["instructions"][0]["parameters"]["amp"], 0.5j + qobj.to_dict()["experiments"][0]["instructions"][0]["parameters"]["amp"], + amp[0] * np.exp(1j * angle[0]), ) def test_assemble_parametric_unsupported(self): @@ -1319,7 +1336,9 @@ def test_assemble_parametric_unsupported(self): by the backend during assemble time. """ sched = pulse.Schedule(name="test_parametric_to_sample_pulse") - sched += Play(pulse.Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), DriveChannel(1)) + sched += Play( + pulse.Drag(duration=25, amp=0.5, angle=-0.3, sigma=7.8, beta=4), DriveChannel(1) + ) sched += Play(pulse.Constant(duration=25, amp=1), DriveChannel(2)) backend = FakeOpenPulse3Q() diff --git a/test/python/pulse/test_parameter_manager.py b/test/python/pulse/test_parameter_manager.py index d395461ac3b1..e88e547e06eb 100644 --- a/test/python/pulse/test_parameter_manager.py +++ b/test/python/pulse/test_parameter_manager.py @@ -352,7 +352,8 @@ def test_complex_valued_parameter(self): with self.assertWarns(PendingDeprecationWarning): assigned = visitor.visit(test_obj) - ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) + with self.assertWarns(DeprecationWarning): + ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) self.assertEqual(assigned, ref_obj) @@ -368,7 +369,8 @@ def test_complex_value_to_parameter(self): with self.assertWarns(PendingDeprecationWarning): assigned = visitor.visit(test_obj) - ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) + with self.assertWarns(DeprecationWarning): + ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) self.assertEqual(assigned, ref_obj) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 675864c323af..575360f81758 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -143,12 +143,12 @@ class TestParametricPulses(QiskitTestCase): def test_construction(self): """Test that parametric pulses can be constructed without error.""" - Gaussian(duration=25, sigma=4, amp=0.5j) + Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2) GaussianSquare(duration=150, amp=0.2, sigma=8, width=140) GaussianSquare(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=2.5) + Constant(duration=150, amp=0.5, angle=np.pi * 0.23) + Drag(duration=25, amp=0.6, sigma=7.8, beta=4, angle=np.pi * 0.54) GaussianDeriv(duration=150, amp=0.2, sigma=8) - Constant(duration=150, amp=0.1 + 0.4j) - Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4) Sin(duration=25, amp=0.5, freq=0.1, phase=0.5, angle=0.5) Cos(duration=30, amp=0.5, freq=0.1, phase=-0.5) Sawtooth(duration=40, amp=0.5, freq=0.2, phase=3.14) @@ -164,17 +164,20 @@ def test_complex_amp_deprecation(self): and that pulses are equivalent.""" # Test deprecation warnings and errors: - with self.assertWarns(PendingDeprecationWarning): + with self.assertWarns(DeprecationWarning): Gaussian(duration=25, sigma=4, amp=0.5j) - with self.assertWarns(PendingDeprecationWarning): + with self.assertWarns(DeprecationWarning): GaussianSquare(duration=125, sigma=4, amp=0.5j, width=100) - with self.assertRaises(PulseError): - Gaussian(duration=25, sigma=4, amp=0.5j, angle=1) - with self.assertRaises(PulseError): - GaussianSquare(duration=125, sigma=4, amp=0.5j, width=100, angle=0.1) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(PulseError): + Gaussian(duration=25, sigma=4, amp=0.5j, angle=1) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(PulseError): + GaussianSquare(duration=125, sigma=4, amp=0.5j, width=100, angle=0.1) # Test that new and old API pulses are the same: - gauss_pulse_complex_amp = Gaussian(duration=25, sigma=4, amp=0.5j) + with self.assertWarns(DeprecationWarning): + gauss_pulse_complex_amp = Gaussian(duration=25, sigma=4, amp=0.5j) gauss_pulse_amp_angle = Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2) np.testing.assert_almost_equal( gauss_pulse_amp_angle.get_waveform().samples, @@ -183,7 +186,7 @@ def test_complex_amp_deprecation(self): def test_gaussian_pulse(self): """Test that Gaussian sample pulse matches the pulse library.""" - gauss = Gaussian(duration=25, sigma=4, amp=0.5j) + gauss = Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2) sample_pulse = gauss.get_waveform() self.assertIsInstance(sample_pulse, Waveform) pulse_lib_gauss = gaussian(duration=25, sigma=4, amp=0.5j, zero_ends=True).samples @@ -191,14 +194,16 @@ def test_gaussian_pulse(self): def test_gaussian_square_pulse(self): """Test that GaussianSquare sample pulse matches the pulse library.""" - gauss_sq = GaussianSquare(duration=125, sigma=4, amp=0.5j, width=100) + gauss_sq = GaussianSquare(duration=125, sigma=4, amp=0.5, width=100, angle=np.pi / 2) sample_pulse = gauss_sq.get_waveform() self.assertIsInstance(sample_pulse, Waveform) pulse_lib_gauss_sq = gaussian_square( duration=125, sigma=4, amp=0.5j, width=100, zero_ends=True ).samples np.testing.assert_almost_equal(sample_pulse.samples, pulse_lib_gauss_sq) - gauss_sq = GaussianSquare(duration=125, sigma=4, amp=0.5j, risefall_sigma_ratio=3.125) + gauss_sq = GaussianSquare( + duration=125, sigma=4, amp=0.5, risefall_sigma_ratio=3.125, angle=np.pi / 2 + ) sample_pulse = gauss_sq.get_waveform() self.assertIsInstance(sample_pulse, Waveform) pulse_lib_gauss_sq = gaussian_square( @@ -210,14 +215,17 @@ def test_gauss_square_extremes(self): """Test that the gaussian square pulse can build a gaussian.""" duration = 125 sigma = 4 - amp = 0.5j - gaus_square = GaussianSquare(duration=duration, sigma=sigma, amp=amp, width=0) - gaus = Gaussian(duration=duration, sigma=sigma, amp=amp) + amp = 0.5 + angle = np.pi / 2 + gaus_square = GaussianSquare(duration=duration, sigma=sigma, amp=amp, width=0, angle=angle) + gaus = Gaussian(duration=duration, sigma=sigma, amp=amp, angle=angle) np.testing.assert_almost_equal( gaus_square.get_waveform().samples, gaus.get_waveform().samples ) - gaus_square = GaussianSquare(duration=duration, sigma=sigma, amp=amp, width=121) - const = Constant(duration=duration, amp=amp) + gaus_square = GaussianSquare( + duration=duration, sigma=sigma, amp=amp, width=121, angle=angle + ) + const = Constant(duration=duration, amp=amp, angle=angle) np.testing.assert_almost_equal( gaus_square.get_waveform().samples[2:-2], const.get_waveform().samples[2:-2] ) @@ -226,7 +234,7 @@ def test_gauss_square_passes_validation_after_construction(self): """Test that parameter validation is consistent before and after construction. This previously used to raise an exception: see gh-7882.""" - pulse = GaussianSquare(duration=125, sigma=4, amp=0.5j, width=100) + pulse = GaussianSquare(duration=125, sigma=4, amp=0.5, width=100, angle=np.pi / 2) pulse.validate_parameters() def test_gaussian_square_drag_pulse(self): @@ -363,7 +371,7 @@ def test_gaussian_square_echo_active_amp_validation(self): def test_drag_pulse(self): """Test that the Drag sample pulse matches the pulse library.""" - drag = Drag(duration=25, sigma=4, amp=0.5j, beta=1) + drag = Drag(duration=25, sigma=4, amp=0.5, beta=1, angle=np.pi / 2) sample_pulse = drag.get_waveform() self.assertIsInstance(sample_pulse, Waveform) pulse_lib_drag = pl_drag(duration=25, sigma=4, amp=0.5j, beta=1, zero_ends=True).samples @@ -373,25 +381,26 @@ def test_drag_validation(self): """Test drag parameter validation, specifically the beta validation.""" duration = 25 sigma = 4 - amp = 0.5j + amp = 0.5 + angle = np.pi / 2 beta = 1 - wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta) + wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta, angle=angle) samples = wf.get_waveform().samples self.assertTrue(max(np.abs(samples)) <= 1) with self.assertRaises(PulseError): wf = Drag(duration=duration, sigma=sigma, amp=1.2, beta=beta) beta = sigma**2 with self.assertRaises(PulseError): - wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta) + wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta, angle=angle) # If sigma is high enough, side peaks fall out of range and norm restriction is met sigma = 100 - wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta) + wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta, angle=angle) def test_drag_beta_validation(self): """Test drag beta parameter validation.""" - def check_drag(duration, sigma, amp, beta): - wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta) + def check_drag(duration, sigma, amp, beta, angle=0): + wf = Drag(duration=duration, sigma=sigma, amp=amp, beta=beta, angle=angle) samples = wf.get_waveform().samples self.assertTrue(max(np.abs(samples)) <= 1) @@ -401,7 +410,7 @@ def check_drag(duration, sigma, amp, beta): check_drag(duration=50, sigma=16, amp=-1, beta=2) check_drag(duration=50, sigma=16, amp=1, beta=-2) check_drag(duration=50, sigma=16, amp=1, beta=6) - check_drag(duration=50, sigma=16, amp=-0.5j, beta=25) + check_drag(duration=50, sigma=16, amp=0.5, beta=25, angle=-np.pi / 2) with self.assertRaises(PulseError): check_drag(duration=50, sigma=16, amp=1, beta=20) with self.assertRaises(PulseError): @@ -541,8 +550,10 @@ def test_sech_deriv_pulse(self): def test_constant_samples(self): """Test the constant pulse and its sampled construction.""" - const = Constant(duration=150, amp=0.1 + 0.4j) - self.assertEqual(const.get_waveform().samples[0], 0.1 + 0.4j) + amp = 0.6 + angle = np.pi * 0.7 + const = Constant(duration=150, amp=amp, angle=angle) + self.assertEqual(const.get_waveform().samples[0], amp * np.exp(1j * angle)) self.assertEqual(len(const.get_waveform().samples), 150) def test_parameters(self): @@ -557,11 +568,6 @@ def test_repr(self): """Test the repr methods for parametric pulses.""" gaus = Gaussian(duration=25, amp=0.7, sigma=4, angle=0.3) self.assertEqual(repr(gaus), "Gaussian(duration=25, sigma=4, amp=0.7, angle=0.3)") - gaus = Gaussian( - duration=25, amp=0.1 + 0.7j, sigma=4 - ) # Should be removed once the deprecation of complex - # amp is completed. - self.assertEqual(repr(gaus), "Gaussian(duration=25, sigma=4, amp=(0.1+0.7j), angle=0)") gaus_square = GaussianSquare(duration=20, sigma=30, amp=1.0, width=3) self.assertEqual( repr(gaus_square), "GaussianSquare(duration=20, sigma=30, width=3, amp=1.0, angle=0)" @@ -633,7 +639,7 @@ def test_repr(self): def test_param_validation(self): """Test that parametric pulse parameters are validated when initialized.""" with self.assertRaises(PulseError): - Gaussian(duration=25, sigma=0, amp=0.5j) + Gaussian(duration=25, sigma=0, amp=0.5, angle=np.pi / 2) with self.assertRaises(PulseError): GaussianSquare(duration=150, amp=0.2, sigma=8) with self.assertRaises(PulseError): @@ -662,43 +668,45 @@ def test_param_validation(self): gaussian_square_echo(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10) with self.assertRaises(PulseError): - Constant(duration=150, amp=0.9 + 0.8j) + Constant(duration=150, amp=1.5, angle=np.pi * 0.8) with self.assertRaises(PulseError): - Drag(duration=25, amp=0.2 + 0.3j, sigma=-7.8, beta=4) + Drag(duration=25, amp=0.5, sigma=-7.8, beta=4, angle=np.pi / 3) def test_gaussian_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): - Gaussian(duration=100, sigma=1.0, amp=1.1 + 0.8j) + Gaussian(duration=100, sigma=1.0, amp=1.7, angle=np.pi * 1.1) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): - waveform = Gaussian(duration=100, sigma=1.0, amp=1.1 + 0.8j) + waveform = Gaussian(duration=100, sigma=1.0, amp=1.7, angle=np.pi * 1.1) self.assertGreater(np.abs(waveform.amp), 1.0) def test_gaussian_limit_amplitude_per_instance(self): """Test that the check for amplitude per instance.""" with self.assertRaises(PulseError): - Gaussian(duration=100, sigma=1.0, amp=1.1 + 0.8j) + Gaussian(duration=100, sigma=1.0, amp=1.6, angle=np.pi / 2.5) - waveform = Gaussian(duration=100, sigma=1.0, amp=1.1 + 0.8j, limit_amplitude=False) + waveform = Gaussian( + duration=100, sigma=1.0, amp=1.6, angle=np.pi / 2.5, limit_amplitude=False + ) self.assertGreater(np.abs(waveform.amp), 1.0) def test_gaussian_square_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): - GaussianSquare(duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10) + GaussianSquare(duration=100, sigma=1.0, amp=1.5, width=10, angle=np.pi / 5) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): - waveform = GaussianSquare(duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10) + waveform = GaussianSquare(duration=100, sigma=1.0, amp=1.5, width=10, angle=np.pi / 5) self.assertGreater(np.abs(waveform.amp), 1.0) def test_gaussian_square_limit_amplitude_per_instance(self): """Test that the check for amplitude per instance.""" with self.assertRaises(PulseError): - GaussianSquare(duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10) + GaussianSquare(duration=100, sigma=1.0, amp=1.5, width=10, angle=np.pi / 3) waveform = GaussianSquare( - duration=100, sigma=1.0, amp=1.1 + 0.8j, width=10, limit_amplitude=False + duration=100, sigma=1.0, amp=1.5, width=10, angle=np.pi / 3, limit_amplitude=False ) self.assertGreater(np.abs(waveform.amp), 1.0) @@ -743,35 +751,37 @@ def test_gaussian_square_echo_limit_amplitude_per_instance(self): def test_drag_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): - Drag(duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j) + Drag(duration=100, sigma=1.0, beta=1.0, amp=1.8, angle=np.pi * 0.3) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): - waveform = Drag(duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j) + waveform = Drag(duration=100, sigma=1.0, beta=1.0, amp=1.8, angle=np.pi * 0.3) self.assertGreater(np.abs(waveform.amp), 1.0) def test_drag_limit_amplitude_per_instance(self): """Test that the check for amplitude per instance.""" with self.assertRaises(PulseError): - Drag(duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j) + Drag(duration=100, sigma=1.0, beta=1.0, amp=1.8, angle=np.pi * 0.3) - waveform = Drag(duration=100, sigma=1.0, beta=1.0, amp=1.1 + 0.8j, limit_amplitude=False) + waveform = Drag( + duration=100, sigma=1.0, beta=1.0, amp=1.8, angle=np.pi * 0.3, limit_amplitude=False + ) self.assertGreater(np.abs(waveform.amp), 1.0) def test_constant_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): - Constant(duration=100, amp=1.1 + 0.8j) + Constant(duration=100, amp=1.3, angle=0.1) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): - waveform = Constant(duration=100, amp=1.1 + 0.8j) + waveform = Constant(duration=100, amp=1.3, angle=0.1) self.assertGreater(np.abs(waveform.amp), 1.0) def test_constant_limit_amplitude_per_instance(self): """Test that the check for amplitude per instance.""" with self.assertRaises(PulseError): - Constant(duration=100, amp=1.1 + 0.8j) + Constant(duration=100, amp=1.6, angle=0.5) - waveform = Constant(duration=100, amp=1.1 + 0.8j, limit_amplitude=False) + waveform = Constant(duration=100, amp=1.6, angle=0.5, limit_amplitude=False) self.assertGreater(np.abs(waveform.amp), 1.0) def test_sin_limit_amplitude(self): diff --git a/test/python/pulse/test_schedule.py b/test/python/pulse/test_schedule.py index e8f6a2f5a84f..654b4ffe39f9 100644 --- a/test/python/pulse/test_schedule.py +++ b/test/python/pulse/test_schedule.py @@ -337,8 +337,8 @@ def test_schedule_with_acquire_on_single_qubit(self): def test_parametric_commands_in_sched(self): """Test that schedules can be built with parametric commands.""" sched = Schedule(name="test_parametric") - sched += Play(Gaussian(duration=25, sigma=4, amp=0.5j), DriveChannel(0)) - sched += Play(Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), DriveChannel(1)) + sched += Play(Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2), DriveChannel(0)) + sched += Play(Drag(duration=25, amp=0.4, angle=0.5, sigma=7.8, beta=4), DriveChannel(1)) sched += Play(Constant(duration=25, amp=1), DriveChannel(2)) sched_duration = sched.duration sched += ( diff --git a/test/python/pulse/test_transforms.py b/test/python/pulse/test_transforms.py index d2d4b3567860..8bb66631b3e4 100644 --- a/test/python/pulse/test_transforms.py +++ b/test/python/pulse/test_transforms.py @@ -437,14 +437,14 @@ def test_parametric_pulses_with_duplicates(self): """Test with parametric pulses.""" schedule = Schedule() drive_channel = DriveChannel(0) - schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5j), drive_channel) - schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5j), drive_channel) + schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2), drive_channel) + schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2), drive_channel) schedule += Play(GaussianSquare(duration=150, amp=0.2, sigma=8, width=140), drive_channel) schedule += Play(GaussianSquare(duration=150, amp=0.2, sigma=8, width=140), drive_channel) - schedule += Play(Constant(duration=150, amp=0.1 + 0.4j), drive_channel) - schedule += Play(Constant(duration=150, amp=0.1 + 0.4j), drive_channel) - schedule += Play(Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), drive_channel) - schedule += Play(Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), drive_channel) + schedule += Play(Constant(duration=150, amp=0.5, angle=0.7), drive_channel) + schedule += Play(Constant(duration=150, amp=0.5, angle=0.7), drive_channel) + schedule += Play(Drag(duration=25, amp=0.4, angle=-0.3, sigma=7.8, beta=4), drive_channel) + schedule += Play(Drag(duration=25, amp=0.4, angle=-0.3, sigma=7.8, beta=4), drive_channel) compressed_schedule = transforms.compress_pulses([schedule]) original_pulse_ids = get_pulse_ids([schedule]) @@ -456,14 +456,14 @@ def test_parametric_pulses_with_no_duplicates(self): """Test parametric pulses with no duplicates.""" schedule = Schedule() drive_channel = DriveChannel(0) - schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5j), drive_channel) - schedule += Play(Gaussian(duration=25, sigma=4, amp=0.49j), drive_channel) + schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2), drive_channel) + schedule += Play(Gaussian(duration=25, sigma=4, amp=0.49, angle=np.pi / 2), drive_channel) schedule += Play(GaussianSquare(duration=150, amp=0.2, sigma=8, width=140), drive_channel) schedule += Play(GaussianSquare(duration=150, amp=0.19, sigma=8, width=140), drive_channel) - schedule += Play(Constant(duration=150, amp=0.1 + 0.4j), drive_channel) - schedule += Play(Constant(duration=150, amp=0.1 + 0.41j), drive_channel) - schedule += Play(Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), drive_channel) - schedule += Play(Drag(duration=25, amp=0.2 + 0.31j, sigma=7.8, beta=4), drive_channel) + schedule += Play(Constant(duration=150, amp=0.5, angle=0.3), drive_channel) + schedule += Play(Constant(duration=150, amp=0.51, angle=0.3), drive_channel) + schedule += Play(Drag(duration=25, amp=0.5, angle=0.5, sigma=7.8, beta=4), drive_channel) + schedule += Play(Drag(duration=25, amp=0.5, angle=0.51, sigma=7.8, beta=4), drive_channel) compressed_schedule = transforms.compress_pulses([schedule]) original_pulse_ids = get_pulse_ids([schedule]) diff --git a/test/python/qobj/test_pulse_converter.py b/test/python/qobj/test_pulse_converter.py index df0b683b12ec..2bdfeea8708d 100644 --- a/test/python/qobj/test_pulse_converter.py +++ b/test/python/qobj/test_pulse_converter.py @@ -63,22 +63,27 @@ def test_drive_instruction(self): def test_gaussian_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" + amp = 0.3 + angle = -0.7 converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play(Gaussian(duration=25, sigma=15, amp=-0.5 + 0.2j), DriveChannel(0)) + instruction = Play(Gaussian(duration=25, sigma=15, amp=amp, angle=angle), DriveChannel(0)) valid_qobj = PulseQobjInstruction( name="parametric_pulse", pulse_shape="gaussian", ch="d0", t0=0, - parameters={"duration": 25, "sigma": 15, "amp": -0.5 + 0.2j}, + parameters={"duration": 25, "sigma": 15, "amp": amp * np.exp(1j * angle)}, ) self.assertEqual(converter(0, instruction), valid_qobj) def test_gaussian_square_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + amp = 0.7 + angle = -0.6 instruction = Play( - GaussianSquare(duration=1500, sigma=15, amp=-0.5 + 0.2j, width=1300), MeasureChannel(1) + GaussianSquare(duration=1500, sigma=15, amp=amp, width=1300, angle=angle), + MeasureChannel(1), ) valid_qobj = PulseQobjInstruction( @@ -86,7 +91,12 @@ def test_gaussian_square_pulse_instruction(self): pulse_shape="gaussian_square", ch="m1", t0=10, - parameters={"duration": 1500, "sigma": 15, "amp": -0.5 + 0.2j, "width": 1300}, + parameters={ + "duration": 1500, + "sigma": 15, + "amp": amp * np.exp(1j * angle), + "width": 1300, + }, ) self.assertEqual(converter(10, instruction), valid_qobj) @@ -106,15 +116,19 @@ def test_constant_pulse_instruction(self): def test_drag_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" + amp = 0.7 + angle = -0.6 converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play(Drag(duration=25, sigma=15, amp=-0.5 + 0.2j, beta=0.5), DriveChannel(0)) + instruction = Play( + Drag(duration=25, sigma=15, amp=amp, angle=angle, beta=0.5), DriveChannel(0) + ) valid_qobj = PulseQobjInstruction( name="parametric_pulse", pulse_shape="drag", ch="d0", t0=30, - parameters={"duration": 25, "sigma": 15, "amp": -0.5 + 0.2j, "beta": 0.5}, + parameters={"duration": 25, "sigma": 15, "amp": amp * np.exp(1j * angle), "beta": 0.5}, ) self.assertEqual(converter(30, instruction), valid_qobj) diff --git a/test/python/scheduler/test_basic_scheduler.py b/test/python/scheduler/test_basic_scheduler.py index 8b7944eb7883..b9245da8d0c2 100644 --- a/test/python/scheduler/test_basic_scheduler.py +++ b/test/python/scheduler/test_basic_scheduler.py @@ -12,6 +12,7 @@ """Test cases for the pulse scheduler passes.""" +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 @@ -352,7 +353,9 @@ def test_parametric_input(self): qr = QuantumRegister(1) qc = QuantumCircuit(qr) qc.append(Gate("gauss", 1, []), qargs=[qr[0]]) - custom_gauss = Schedule(Play(Gaussian(duration=25, sigma=4, amp=0.5j), DriveChannel(0))) + 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) sched = schedule(qc, self.backend, inst_map=self.inst_map) self.assertEqual(sched.instructions[0], custom_gauss.instructions[0])