Skip to content

Commit

Permalink
Start deprecation of complex parameters in Qiskit Pulse (Qiskit#9735)
Browse files Browse the repository at this point in the history
* Add warning and update tests

* Release notes

* Move warning to all Pulse objects.

* Fix releasenote typo

* Move warning to format_parameter_value
  • Loading branch information
TsafrirA committed Mar 21, 2023
1 parent 6cec912 commit eb4c5cb
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 13 deletions.
4 changes: 2 additions & 2 deletions qiskit/pulse/parameter_manager.py
Expand Up @@ -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."""
Expand Down
14 changes: 14 additions & 0 deletions qiskit/pulse/utils.py
Expand Up @@ -13,6 +13,7 @@
"""Module for common pulse programming utilities."""
import functools
from typing import List, Dict, Union
import warnings

import numpy as np

Expand Down Expand Up @@ -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:
Expand Down
@@ -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.
29 changes: 18 additions & 11 deletions test/python/pulse/test_parameter_manager.py
Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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)
Expand All @@ -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):
Expand Down

0 comments on commit eb4c5cb

Please sign in to comment.