|
13 | 13 | N-Qubit Sparse Pauli Operator class.
|
14 | 14 | """
|
15 | 15 |
|
| 16 | +from __future__ import annotations |
| 17 | + |
16 | 18 | from collections import defaultdict
|
| 19 | +from collections.abc import Mapping, Sequence |
17 | 20 | from numbers import Number
|
18 | 21 | from typing import Dict, Optional
|
| 22 | +from copy import deepcopy |
19 | 23 |
|
20 | 24 | import numpy as np
|
21 | 25 | import rustworkx as rx
|
22 | 26 |
|
23 | 27 | from qiskit._accelerate.sparse_pauli_op import unordered_unique
|
| 28 | +from qiskit.circuit.parameter import Parameter |
| 29 | +from qiskit.circuit.parameterexpression import ParameterExpression |
| 30 | +from qiskit.circuit.parametertable import ParameterView |
24 | 31 | from qiskit.exceptions import QiskitError
|
25 | 32 | from qiskit.quantum_info.operators.custom_iterator import CustomIterator
|
26 | 33 | from qiskit.quantum_info.operators.linear_op import LinearOp
|
@@ -112,10 +119,18 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True):
|
112 | 119 |
|
113 | 120 | pauli_list = PauliList(data.copy() if copy and hasattr(data, "copy") else data)
|
114 | 121 |
|
115 |
| - dtype = object if isinstance(coeffs, np.ndarray) and coeffs.dtype == object else complex |
| 122 | + if isinstance(coeffs, np.ndarray) and coeffs.dtype == object: |
| 123 | + dtype = object |
| 124 | + elif coeffs is not None: |
| 125 | + if not isinstance(coeffs, (np.ndarray, Sequence)): |
| 126 | + coeffs = [coeffs] |
| 127 | + if any(isinstance(coeff, ParameterExpression) for coeff in coeffs): |
| 128 | + dtype = object |
| 129 | + else: |
| 130 | + dtype = complex |
116 | 131 |
|
117 | 132 | if coeffs is None:
|
118 |
| - coeffs = np.ones(pauli_list.size, dtype=dtype) |
| 133 | + coeffs = np.ones(pauli_list.size, dtype=complex) |
119 | 134 | else:
|
120 | 135 | coeffs = np.array(coeffs, copy=copy, dtype=dtype)
|
121 | 136 |
|
@@ -997,6 +1012,58 @@ def group_commuting(self, qubit_wise=False):
|
997 | 1012 | groups[color].append(idx)
|
998 | 1013 | return [self[group] for group in groups.values()]
|
999 | 1014 |
|
| 1015 | + @property |
| 1016 | + def parameters(self) -> ParameterView: |
| 1017 | + r"""Return the free ``Parameter``\s in the coefficients.""" |
| 1018 | + ret = set() |
| 1019 | + for coeff in self.coeffs: |
| 1020 | + if isinstance(coeff, ParameterExpression): |
| 1021 | + ret |= coeff.parameters |
| 1022 | + return ParameterView(ret) |
| 1023 | + |
| 1024 | + def assign_parameters( |
| 1025 | + self, |
| 1026 | + parameters: Mapping[Parameter, complex | ParameterExpression] |
| 1027 | + | Sequence[complex | ParameterExpression], |
| 1028 | + inplace: bool = False, |
| 1029 | + ) -> SparsePauliOp | None: |
| 1030 | + r"""Bind the free ``Parameter``\s in the coefficients to provided values. |
| 1031 | +
|
| 1032 | + Args: |
| 1033 | + parameters: The values to bind the parameters to. |
| 1034 | + inplace: If ``False``, a copy of the operator with the bound parameters is returned. |
| 1035 | + If ``True`` the operator itself is modified. |
| 1036 | +
|
| 1037 | + Returns: |
| 1038 | + A copy of the operator with bound parameters, if ``inplace`` is ``False``, otherwise |
| 1039 | + ``None``. |
| 1040 | + """ |
| 1041 | + if inplace: |
| 1042 | + bound = self |
| 1043 | + else: |
| 1044 | + bound = deepcopy(self) |
| 1045 | + |
| 1046 | + # turn the parameters to a dictionary |
| 1047 | + if isinstance(parameters, Sequence): |
| 1048 | + free_parameters = bound.parameters |
| 1049 | + if len(parameters) != len(free_parameters): |
| 1050 | + raise ValueError( |
| 1051 | + f"Mismatching number of values ({len(parameters)}) and parameters " |
| 1052 | + f"({len(free_parameters)}). For partial binding please pass a dictionary of " |
| 1053 | + "{parameter: value} pairs." |
| 1054 | + ) |
| 1055 | + parameters = dict(zip(free_parameters, parameters)) |
| 1056 | + |
| 1057 | + for i, coeff in enumerate(bound.coeffs): |
| 1058 | + if isinstance(coeff, ParameterExpression): |
| 1059 | + for key in coeff.parameters & parameters.keys(): |
| 1060 | + coeff = coeff.assign(key, parameters[key]) |
| 1061 | + if len(coeff.parameters) == 0: |
| 1062 | + coeff = complex(coeff) |
| 1063 | + bound.coeffs[i] = coeff |
| 1064 | + |
| 1065 | + return None if inplace else bound |
| 1066 | + |
1000 | 1067 |
|
1001 | 1068 | # Update docstrings for API docs
|
1002 | 1069 | generate_apidocs(SparsePauliOp)
|
0 commit comments