Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow string keys in QuantumCircuit.assign_parameters #11432

Merged
merged 1 commit into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3075,7 +3075,7 @@ def assign_parameters( # pylint: disable=missing-raises-doc
) -> Optional["QuantumCircuit"]:
"""Assign parameters to new parameters or values.

If ``parameters`` is passed as a dictionary, the keys must be :class:`.Parameter`
If ``parameters`` is passed as a dictionary, the keys should be :class:`.Parameter`
instances in the current circuit. The values of the dictionary can either be numeric values
or new parameter objects.

Expand All @@ -3085,14 +3085,26 @@ def assign_parameters( # pylint: disable=missing-raises-doc

The values can be assigned to the current circuit object or to a copy of it.

.. note::
When ``parameters`` is given as a mapping, it is permissible to have keys that are
strings of the parameter names; these will be looked up using :meth:`get_parameter`.
You can also have keys that are :class:`.ParameterVector` instances, and in this case,
the dictionary value should be a sequence of values of the same length as the vector.

If you use either of these cases, you must leave the setting ``flat_input=False``;
changing this to ``True`` enables the fast path, where all keys must be
:class:`.Parameter` instances.

Args:
parameters: Either a dictionary or iterable specifying the new parameter values.
inplace: If False, a copy of the circuit with the bound parameters is returned.
If True the circuit instance itself is modified.
flat_input: If ``True`` and ``parameters`` is a mapping type, it is assumed to be
exactly a mapping of ``{parameter: value}``. By default (``False``), the mapping
may also contain :class:`.ParameterVector` keys that point to a corresponding
sequence of values, and these will be unrolled during the mapping.
sequence of values, and these will be unrolled during the mapping, or string keys,
which will be converted to :class:`.Parameter` instances using
:meth:`get_parameter`.
strict: If ``False``, any parameters given in the mapping that are not used in the
circuit will be ignored. If ``True`` (the default), an error will be raised
indicating a logic error.
Expand Down Expand Up @@ -3270,9 +3282,8 @@ def map_calibration(qubits, parameters, schedule):
)
return None if inplace else target

@staticmethod
def _unroll_param_dict(
parameter_binds: Mapping[Parameter, ParameterValueType]
self, parameter_binds: Mapping[Parameter, ParameterValueType]
) -> Mapping[Parameter, ParameterValueType]:
out = {}
for parameter, value in parameter_binds.items():
Expand All @@ -3283,6 +3294,8 @@ def _unroll_param_dict(
f" but was assigned to {len(value)} values."
)
out.update(zip(parameter, value))
elif isinstance(parameter, str):
out[self.get_parameter(parameter)] = value
else:
out[parameter] = value
return out
Expand Down
14 changes: 14 additions & 0 deletions releasenotes/notes/assign-by-name-305f2bbf89099174.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
features:
- |
:meth:`.QuantumCircuit.assign_parameters` now accepts string keys in the mapping form of input.
These names are used to look up the corresponding :class:`.Parameter` instance using
:meth:`~.QuantumCircuit.get_parameter`. This lets you do::

from qiskit.circuit import QuantumCircuit, Parameter

a = Parameter("a")
qc = QuantumCircuit(1)
qc.rx(a, 0)

qc.assign_parameters({"a": 1}) == qc.assign_parameters({a: 1})
14 changes: 13 additions & 1 deletion test/python/circuit/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,18 @@ def test_bind_parameters_allow_unknown(self):
c = a.bind({a: 1, b: 1}, allow_unknown_parameters=True)
self.assertEqual(c, a.bind({a: 1}))

def test_assign_parameters_by_name(self):
"""Test that parameters can be assigned by name as well as value."""
a = Parameter("a")
b = Parameter("b")
c = Parameter("c")
qc = QuantumCircuit(2, global_phase=a * 2)
qc.rx(b + 0.125 * c, 0)

self.assertEqual(
qc.assign_parameters({a: 1, b: 2, c: 3}), qc.assign_parameters({"a": 1, "b": 2, "c": 3})
)

def test_bind_parameters_custom_definition_global_phase(self):
"""Test that a custom gate with a parametrised `global_phase` is assigned correctly."""
x = Parameter("x")
Expand Down Expand Up @@ -536,7 +548,7 @@ def test_raise_if_assigning_params_not_in_circuit(self):
with self.assertRaises(CircuitError):
qc.assign_parameters({z: [3, 4, 5]})
with self.assertRaises(CircuitError):
qc.assign_parameters({"a_str": 6})
qc.assign_parameters({6: 6})
with self.assertRaises(CircuitError):
qc.assign_parameters({None: 7})

Expand Down