From eb4c5cbeb5200414a7341c02fa5836e6509c0834 Mon Sep 17 00:00:00 2001 From: TsafrirA <113579969+TsafrirA@users.noreply.github.com> Date: Tue, 21 Mar 2023 03:10:59 +0200 Subject: [PATCH] Start deprecation of complex parameters in Qiskit Pulse (#9735) * Add warning and update tests * Release notes * Move warning to all Pulse objects. * Fix releasenote typo * Move warning to format_parameter_value --- qiskit/pulse/parameter_manager.py | 4 +-- qiskit/pulse/utils.py | 14 +++++++++ ...-complex-deprecation-89ecdf968b1a2d89.yaml | 12 ++++++++ test/python/pulse/test_parameter_manager.py | 29 ++++++++++++------- 4 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 releasenotes/notes/symbolic-pulse-complex-deprecation-89ecdf968b1a2d89.yaml diff --git a/qiskit/pulse/parameter_manager.py b/qiskit/pulse/parameter_manager.py index d1db89ab1be..4fac995b22f 100644 --- a/qiskit/pulse/parameter_manager.py +++ b/qiskit/pulse/parameter_manager.py @@ -256,8 +256,8 @@ def _assign_parameter_expression(self, param_expr: ParameterExpression): updated = param_expr.parameters & self._param_map.keys() for param in updated: new_value = new_value.assign(param, self._param_map[param]) - - return format_parameter_value(new_value) + new_value = format_parameter_value(new_value) + return new_value def _update_parameter_manager(self, node: Union[Schedule, ScheduleBlock]): """A helper function to update parameter manager of pulse program.""" diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index ee3d6b6e039..03c21528484 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -13,6 +13,7 @@ """Module for common pulse programming utilities.""" import functools from typing import List, Dict, Union +import warnings import numpy as np @@ -65,6 +66,19 @@ def format_parameter_value( evaluated = float(evaluated.real) if evaluated.is_integer(): evaluated = int(evaluated) + else: + warnings.warn( + "Assignment of complex values to ParameterExpression in Qiskit Pulse objects is " + "now pending deprecation. This will align the Pulse module with other modules " + "where such assignment wasn't possible to begin with. The typical use case for complex " + "parameters in the module was the SymbolicPulse library. As of Qiskit-Terra " + "0.23.0 all library pulses were converted from complex amplitude representation" + " to real representation using two floats (amp,angle), as used in the " + "ScalableSymbolicPulse class. This eliminated the need for complex parameters. " + "Any use of complex parameters (and particularly custom-built pulses) should be " + "converted in a similar fashion to avoid the use of complex parameters.", + PendingDeprecationWarning, + ) return evaluated except TypeError: diff --git a/releasenotes/notes/symbolic-pulse-complex-deprecation-89ecdf968b1a2d89.yaml b/releasenotes/notes/symbolic-pulse-complex-deprecation-89ecdf968b1a2d89.yaml new file mode 100644 index 00000000000..9f0377cf111 --- /dev/null +++ b/releasenotes/notes/symbolic-pulse-complex-deprecation-89ecdf968b1a2d89.yaml @@ -0,0 +1,12 @@ +--- +deprecations: + - | + Assignment of complex values to ``ParameterExpression`` in any Qiskit Pulse object + now raises a ``PendingDeprecationWarning``. This will align the Pulse module with + other modules where such assignment wasn't possible to begin with. The typical use + case for complex parameters in the module was the SymbolicPulse library. As of + Qiskit-Terra 0.23.0 all library pulses were converted from complex amplitude + representation to real representation using two floats (amp,angle), as used in the + ``ScalableSymbolicPulse`` class. This eliminated the need for complex parameters. + Any use of complex parameters (and particularly custom-built pulses) should be + converted in a similar fashion to avoid the use of complex parameters. diff --git a/test/python/pulse/test_parameter_manager.py b/test/python/pulse/test_parameter_manager.py index bd0cacf6bd2..4ffa519c0a8 100644 --- a/test/python/pulse/test_parameter_manager.py +++ b/test/python/pulse/test_parameter_manager.py @@ -201,7 +201,7 @@ class TestParameterSetter(ParameterTestBase): """Test setting parameters.""" def test_set_parameter_to_channel(self): - """Test get parameters from channel.""" + """Test set parameters from channel.""" test_obj = pulse.DriveChannel(self.ch1 + self.ch2) value_dict = {self.ch1: 1, self.ch2: 2} @@ -214,7 +214,7 @@ def test_set_parameter_to_channel(self): self.assertEqual(assigned, ref_obj) def test_set_parameter_to_pulse(self): - """Test get parameters from pulse instruction.""" + """Test set parameters from pulse instruction.""" test_obj = self.parametric_waveform1 value_dict = {self.amp1_1: 0.1, self.amp1_2: 0.2, self.dur1: 160} @@ -336,42 +336,47 @@ def test_nested_assignment_partial_bind(self): self.assertEqual(assigned, ref_obj) def test_complex_valued_parameter(self): - """Test complex valued parameter can be casted to a complex value.""" + """Test complex valued parameter can be casted to a complex value, + but raises PendingDeprecationWarning..""" amp = Parameter("amp") test_obj = pulse.Constant(duration=160, amp=1j * amp) value_dict = {amp: 0.1} visitor = ParameterSetter(param_map=value_dict) - assigned = visitor.visit(test_obj) + with self.assertWarns(PendingDeprecationWarning): + assigned = visitor.visit(test_obj) ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) self.assertEqual(assigned, ref_obj) def test_complex_value_to_parameter(self): - """Test complex value can be assigned to parameter object.""" + """Test complex value can be assigned to parameter object, + but raises PendingDeprecationWarning.""" amp = Parameter("amp") test_obj = pulse.Constant(duration=160, amp=amp) value_dict = {amp: 0.1j} visitor = ParameterSetter(param_map=value_dict) - assigned = visitor.visit(test_obj) + with self.assertWarns(PendingDeprecationWarning): + assigned = visitor.visit(test_obj) ref_obj = pulse.Constant(duration=160, amp=1j * 0.1) self.assertEqual(assigned, ref_obj) def test_complex_parameter_expression(self): - """Test assignment of complex-valued parameter expression to parameter.""" + """Test assignment of complex-valued parameter expression to parameter, + but raises PendingDeprecationWarning.""" amp = Parameter("amp") mag = Parameter("A") phi = Parameter("phi") test_obj = pulse.Constant(duration=160, amp=amp) - + test_obj_copy = deepcopy(test_obj) # generate parameter expression value_dict = {amp: mag * np.exp(1j * phi)} visitor = ParameterSetter(param_map=value_dict) @@ -380,13 +385,15 @@ def test_complex_parameter_expression(self): # generate complex value value_dict = {mag: 0.1, phi: 0.5} visitor = ParameterSetter(param_map=value_dict) - assigned = visitor.visit(assigned) + with self.assertWarns(PendingDeprecationWarning): + assigned = visitor.visit(assigned) # evaluated parameter expression: 0.0877582561890373 + 0.0479425538604203*I value_dict = {amp: 0.1 * np.exp(0.5j)} - visitor = ParameterSetter(param_map=value_dict) - ref_obj = visitor.visit(test_obj) + visitor = ParameterSetter(param_map=value_dict) + with self.assertWarns(PendingDeprecationWarning): + ref_obj = visitor.visit(test_obj_copy) self.assertEqual(assigned, ref_obj) def test_invalid_pulse_amplitude(self):