C贸digo fuente para qiskit.transpiler.passes.basis.translate_parameterized

# This code is part of Qiskit.
# (C) Copyright IBM 2022.
# 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.

"""Translate parameterized gates only, and leave others as they are."""

from __future__ import annotations

from qiskit.circuit import Instruction, ParameterExpression, Qubit, Clbit
from qiskit.converters import circuit_to_dag
from qiskit.dagcircuit import DAGCircuit, DAGOpNode
from qiskit.circuit.equivalence_library import EquivalenceLibrary
from qiskit.exceptions import QiskitError
from qiskit.transpiler import Target

from qiskit.transpiler.basepasses import TransformationPass

from .basis_translator import BasisTranslator

[documentos]class TranslateParameterizedGates(TransformationPass): """Translate parameterized gates to a supported basis set. Once a parameterized instruction is found that is not in the ``supported_gates`` list, the instruction is decomposed one level and the parameterized sub-blocks are recursively decomposed. The recursion is stopped once all parameterized gates are in ``supported_gates``, or if a gate has no definition and a translation to the basis is attempted (this might happen e.g. for the ``UGate`` if it's not in the specified gate list). Example: The following, multiply nested circuit:: from qiskit.circuit import QuantumCircuit, ParameterVector from qiskit.transpiler.passes import TranslateParameterizedGates x = ParameterVector("x", 4) block1 = QuantumCircuit(1) block1.rx(x[0], 0) sub_block = QuantumCircuit(2) sub_block.cx(0, 1) sub_block.rz(x[2], 0) block2 = QuantumCircuit(2) block2.ry(x[1], 0) block2.append(sub_block.to_gate(), [0, 1]) block3 = QuantumCircuit(3) block3.ccx(0, 1, 2) circuit = QuantumCircuit(3) circuit.append(block1.to_gate(), [1]) circuit.append(block2.to_gate(), [0, 1]) circuit.append(block3.to_gate(), [0, 1, 2]) circuit.cry(x[3], 0, 2) supported_gates = ["rx", "ry", "rz", "cp", "crx", "cry", "crz"] unrolled = TranslateParameterizedGates(supported_gates)(circuit) is decomposed to:: 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹愨攲鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 q_0: 鈹 Ry(x[1]) 鈹溾攢鈹鈻犫攢鈹鈹 Rz(x[2]) 鈹溾敜0 鈹溾攢鈹鈹鈹鈹鈻犫攢鈹鈹鈹鈹鈹 鈹溾攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹も攲鈹鈹粹攢鈹愨敂鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹樷攤 鈹 鈹 q_1: 鈹 Rx(x[0]) 鈹溾敜 X 鈹溾攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹1 circuit-92 鈹溾攢鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹樷敂鈹鈹鈹鈹 鈹 鈹傗攲鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹 q_2: 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹2 鈹溾敜 Ry(x[3]) 鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹樷敂鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 """ def __init__( self, supported_gates: list[str] | None = None, equivalence_library: EquivalenceLibrary | None = None, target: Target | None = None, ) -> None: """ Args: supported_gates: A list of suppported basis gates specified as string. If ``None``, a ``target`` must be provided. equivalence_library: The equivalence library to translate the gates. Defaults to the equivalence library of all Qiskit standard gates. target: A :class:`.Target` containing the supported operations. If ``None``, ``supported_gates`` must be set. Note that this argument takes precedence over ``supported_gates``, if both are set. Raises: ValueError: If neither of ``supported_gates`` and ``target`` are passed. """ super().__init__() # get the default equivalence library, if none has been set if equivalence_library is None: from qiskit.circuit.library.standard_gates.equivalence_library import _sel equivalence_library = _sel # The target takes precedence over the supported_gates argument. If neither are set, # raise an error. if target is not None: supported_gates = target.operation_names elif supported_gates is None: raise ValueError("One of ``supported_gates`` or ``target`` must be specified.") self._supported_gates = supported_gates self._target = target self._translator = BasisTranslator(equivalence_library, supported_gates, target=target)
[documentos] def run(self, dag: DAGCircuit) -> DAGCircuit: """Run the transpiler pass. Args: dag: The DAG circuit in which the parameterized gates should be unrolled. Returns: A DAG where the parameterized gates have been unrolled. Raises: QiskitError: If the circuit cannot be unrolled. """ for node in dag.op_nodes(): # check whether it is parameterized and we need to decompose it if _is_parameterized(node.op) and not _is_supported( node, self._supported_gates, self._target ): definition = node.op.definition if definition is not None: # recurse to unroll further parameterized blocks unrolled = self.run(circuit_to_dag(definition)) else: # if we hit a base case, try to translate to the specified basis try: unrolled = self._translator.run(_instruction_to_dag(node.op)) except Exception as exc: raise QiskitError("Failed to translate final block.") from exc # replace with unrolled (or translated) dag dag.substitute_node_with_dag(node, unrolled) return dag
def _is_parameterized(op: Instruction) -> bool: return any( isinstance(param, ParameterExpression) and len(param.parameters) > 0 for param in op.params ) def _is_supported(node: DAGOpNode, supported_gates: list[str], target: Target | None) -> bool: """Check whether the node is supported. If the target is provided, check using the target, otherwise the supported_gates are used. """ if target is not None: return target.instruction_supported(node.op.name) return node.op.name in supported_gates def _instruction_to_dag(op: Instruction) -> DAGCircuit: dag = DAGCircuit() dag.add_qubits([Qubit() for _ in range(op.num_qubits)]) dag.add_qubits([Clbit() for _ in range(op.num_clbits)]) dag.apply_operation_back(op, dag.qubits, dag.clbits) return dag