Source code for qiskit_addon_aqc_tensor.ansatz_generation.parametrize_circuit

# This code is a Qiskit project.
#
# (C) Copyright IBM 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Function for constructing a parameterized version of a circuit."""

from __future__ import annotations

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit

from .from_connectivity import _allocate_parameters


[docs] def parametrize_circuit( qc: QuantumCircuit, /, *, parameter_name: str = "theta", ) -> tuple[QuantumCircuit, list[float | None]]: r"""Create a parametrized version of a circuit. Given a quantum circuit, constructs another quantum circuit which is identical except that any gates with numerical parameters are replaced by gates (of the same type) with free parameters. The new circuit is returned along with a list containing the original values of the parameters. Args: qc: The quantum circuit to parametrize. parameter_name: Name for the :class:`~qiskit.circuit.ParameterVector` representing the free parameters in the returned ansatz circuit. Returns: ``(ansatz, parameter_values)`` such that ``ansatz.assign_parameters(parameter_values)`` is identical to ``qc`` as long as ``qc`` did not already contain parameters. If ``qc`` already had parameters, then ``parameter_values`` will contain ``None`` at the entries corresponding to those parameters. Example: ======== Consider the following circuit as an example: .. plot:: :alt: Circuit diagram output by the previous code. :include-source: :context: reset from qiskit import QuantumCircuit qc = QuantumCircuit(6) qc.rx(0.4, 0) qc.ryy(0.2, 2, 3) qc.h(2) qc.rz(0.1, 2) qc.rxx(0.3, 0, 1) qc.rzz(0.3, 0, 1) qc.cx(2, 1) qc.s(1) qc.h(4) qc.draw("mpl") If the above circuit is passed to :func:`.parametrize_circuit`, it will return an ansatz obtained from this circuit by replacing numerical parameters with free parameters: .. plot:: :alt: Circuit diagram output by the previous code. :include-source: :context: close-figs from qiskit_addon_aqc_tensor import parametrize_circuit ansatz, initial_params = parametrize_circuit(qc) ansatz.draw("mpl") Further, the :func:`.parametrize_circuit` function provides parameters which, when bound to the ansatz, will result in a circuit identical to the original one: .. plot:: :alt: Circuit diagram output by the previous code. :include-source: :context: close-figs ansatz.assign_parameters(initial_params).draw("mpl") If the original circuit already contained parameters, then the returned parameter values will contain ``None`` at the entries corresponding to those parameters, and the preceding code will not work. The following example shows how to recover the original circuit in this case. .. plot:: :alt: Circuit diagram output by the previous code. :include-source: :context: close-figs from qiskit.circuit import Parameter qc = QuantumCircuit(3) alpha1 = Parameter("alpha1") alpha2 = Parameter("alpha2") qc.ry(alpha1, [0]) qc.rz(0.1, [0]) qc.ry(alpha2, [1]) qc.rz(alpha1, [1]) qc.ry(0.2, [2]) qc.rz(0.3, [2]) ansatz, initial_params = parametrize_circuit(qc) ansatz.assign_parameters( { param: val for param, val in zip(ansatz.parameters, initial_params) if val is not None }, inplace=True, ) ansatz.draw("mpl") """ ansatz = QuantumCircuit(*qc.qregs, *qc.cregs) param_vec = ParameterVector(parameter_name) initial_params: list[float | None] = [] for inst in qc.data: operation = inst.operation original_params = operation.params fixed_indices = [ i for i, val in enumerate(original_params) if not isinstance(val, Parameter) ] if fixed_indices: # Replace all non-Parameter entries with parameters operation = operation.copy() params = operation.params allocated_params, _ = _allocate_parameters(param_vec, len(fixed_indices)) for i, param in zip(fixed_indices, allocated_params): params[i] = param initial_params.append(original_params[i]) ansatz.append(operation, inst.qubits, inst.clbits) for i, param in enumerate(ansatz.parameters): if param in qc.parameters: initial_params.insert(i, None) return ansatz, initial_params