Skip to content

Commit

Permalink
Added possibility of assigning Parameter and ParameterVector by name
Browse files Browse the repository at this point in the history
It is now possible to specify in the mapping the names of the parameters instead of the parameters themselves to assign parameters to pulse `Schedule`s and `ScheduleBlock`s. It is even possible to assign all the parameters of a ParameterVector by just specifying its name and setting as a value a list of parameter values.
  • Loading branch information
arthurostrauss committed Apr 1, 2024
1 parent 4f7b54a commit b01354e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 12 deletions.
28 changes: 23 additions & 5 deletions qiskit/pulse/parameter_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
from copy import copy
from typing import Any, Mapping, Sequence

from qiskit.circuit import ParameterVector
from qiskit.circuit.parametervector import ParameterVector, ParameterVectorElement
from qiskit.circuit.parameter import Parameter
from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType
from qiskit.pulse import instructions, channels
Expand Down Expand Up @@ -362,7 +362,8 @@ def assign_parameters(
self,
pulse_program: Any,
value_dict: dict[
ParameterExpression | ParameterVector, ParameterValueType | Sequence[ParameterValueType]
ParameterExpression | ParameterVector | str,
ParameterValueType | Sequence[ParameterValueType],
],
) -> Any:
"""Modify and return program data with parameters assigned according to the input.
Expand Down Expand Up @@ -397,7 +398,7 @@ def update_parameter_table(self, new_node: Any):
def _unroll_param_dict(
self,
parameter_binds: Mapping[
Parameter | ParameterVector, ParameterValueType | Sequence[ParameterValueType]
Parameter | ParameterVector | str, ParameterValueType | Sequence[ParameterValueType]
],
) -> Mapping[Parameter, ParameterValueType]:
"""
Expand All @@ -415,7 +416,7 @@ def _unroll_param_dict(
if not isinstance(value, Sequence):
raise PulseError(
f"Parameter vector '{parameter.name}' has length {len(parameter)},"
f" but was assigned to a single value."
f" but was assigned to {value}."
)
if len(parameter) != len(value):
raise PulseError(
Expand All @@ -424,7 +425,24 @@ def _unroll_param_dict(
)
out.update(zip(parameter, value))
elif isinstance(parameter, str):
out[self.get_parameters(parameter)] = value
for param in self.parameters:
if param.name == parameter:
out[param] = value
elif (
isinstance(param, ParameterVectorElement) and param.vector.name == parameter
):
if not isinstance(value, Sequence):
raise PulseError(
f"ParameterVector '{param.vector.name}' has length {len(param.vector)},"
f" but was assigned to a single value."
)
if len(param.vector) != len(value):
raise PulseError(
f"ParameterVector '{param.vector.name}' has length {len(param.vector)},"
f" but was assigned to {len(value)} values."
)
out[param] = value[param.index]

else:
out[parameter] = value
return out
16 changes: 9 additions & 7 deletions qiskit/pulse/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,16 +714,17 @@ def is_parameterized(self) -> bool:
def assign_parameters(
self,
value_dict: dict[
ParameterExpression | ParameterVector, ParameterValueType | Sequence[ParameterValueType]
ParameterExpression | ParameterVector | str,
ParameterValueType | Sequence[ParameterValueType],
],
inplace: bool = True,
) -> "Schedule":
"""Assign the parameters in this schedule according to the input.
Args:
value_dict: A mapping from parameters (parameter vectors) to either
numeric values (list of numeric values)
or another Parameter expression (list of Parameter expressions).
value_dict: A mapping from parameters or parameter names (parameter vector
or parameter vector name) to either numeric values (list of numeric values)
or another parameter expression (list of parameter expressions).
inplace: Set ``True`` to override this instance with new parameter.
Returns:
Expand Down Expand Up @@ -1415,15 +1416,16 @@ def is_referenced(self) -> bool:
def assign_parameters(
self,
value_dict: dict[
ParameterExpression | ParameterVector, ParameterValueType | Sequence[ParameterValueType]
ParameterExpression | ParameterVector | str,
ParameterValueType | Sequence[ParameterValueType],
],
inplace: bool = True,
) -> "ScheduleBlock":
"""Assign the parameters in this schedule according to the input.
Args:
value_dict: A mapping from parameters (parameter vectors) to either numeric values
(list of numeric values)
value_dict: A mapping from parameters or parameter names (parameter vector
or parameter vector name) to either numeric values (list of numeric values)
or another parameter expression (list of parameter expressions).
inplace: Set ``True`` to override this instance with new parameter.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
prelude: >
features_pulse:
- |
It is now possible to assign parameters to pulse `Schedule`and `ScheduleBlock` objects by specifying
the parameter name as a string. The parameter name can be used to assign values to all parameters within the
`Schedule` or `ScheduleBlock` that have the same name. Moreover, the parameter name of a `ParameterVector`
can be used to assign all values of the vector simultaneously (the list of values should therefore match the
length of the vector).
36 changes: 36 additions & 0 deletions test/python/pulse/test_parameter_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,42 @@ def test_parametric_pulses_with_parameter_vector(self):
self.assertEqual(sched2.instructions[0][1].pulse.sigma, 4.0)
self.assertEqual(sched2.instructions[1][1].phase, 0.1)

def test_pulse_assignment_with_parameter_names(self):
"""Test pulse assignment with parameter names."""
sigma = Parameter("sigma")
amp = Parameter("amp")
param_vec = ParameterVector("param_vec", 2)

waveform = pulse.library.Gaussian(duration=128, sigma=sigma, amp=amp)
waveform2 = pulse.library.Gaussian(duration=128, sigma=40, amp=amp)
block = pulse.ScheduleBlock()
block += pulse.Play(waveform, pulse.DriveChannel(10))
block += pulse.Play(waveform2, pulse.DriveChannel(10))
block += pulse.ShiftPhase(param_vec[0], pulse.DriveChannel(10))
block += pulse.ShiftPhase(param_vec[1], pulse.DriveChannel(10))
block.assign_parameters({"amp": 0.2, "sigma": 4, "param_vec": [3.14, 1.57]}, inplace=True)

self.assertEqual(block.blocks[0].pulse.amp, 0.2)
self.assertEqual(block.blocks[0].pulse.sigma, 4.0)
self.assertEqual(block.blocks[1].pulse.amp, 0.2)
self.assertEqual(block.blocks[2].phase, 3.14)
self.assertEqual(block.blocks[3].phase, 1.57)

sched = pulse.Schedule()
sched += pulse.Play(waveform, pulse.DriveChannel(10))
sched += pulse.Play(waveform2, pulse.DriveChannel(10))
sched += pulse.ShiftPhase(param_vec[0], pulse.DriveChannel(10))
sched += pulse.ShiftPhase(param_vec[1], pulse.DriveChannel(10))
sched1 = sched.assign_parameters(
{"amp": 0.2, "sigma": 4, "param_vec": [3.14, 1.57]}, inplace=False
)

self.assertEqual(sched1.instructions[0][1].pulse.amp, 0.2)
self.assertEqual(sched1.instructions[0][1].pulse.sigma, 4.0)
self.assertEqual(sched1.instructions[1][1].pulse.amp, 0.2)
self.assertEqual(sched1.instructions[2][1].phase, 3.14)
self.assertEqual(sched1.instructions[3][1].phase, 1.57)


class TestScheduleTimeslots(QiskitTestCase):
"""Test for edge cases of timing overlap on parametrized channels.
Expand Down

0 comments on commit b01354e

Please sign in to comment.