Source code for mpqp.core.instruction.gates.custom_gate

"""In some cases, we need to manipulate unitary operations that are not defined
using native gates (by the corresponding unitary matrix for instance). For those
cases, you can use :class:`mpqp.core.instruction.gates.custom_gate.CustomGate` 
to add your custom unitary operation to the circuit, which will be decomposed 
and executed transparently."""

from typing import TYPE_CHECKING, Optional

from typeguard import typechecked

from mpqp.tools import Matrix

if TYPE_CHECKING:
    from qiskit.circuit import Parameter

from mpqp.core.instruction.gates.gate import Gate
from mpqp.core.instruction.gates.gate_definition import UnitaryMatrix
from mpqp.core.languages import Language


[docs]@typechecked class CustomGate(Gate): """Custom gates allow you to define your own unitary gates. Args: definition: The GateDefinition describing the gate. targets: The qubits on which the gate operates. label: The label of the gate. Defaults to None. Raises: ValueError: the target qubits must be contiguous and in order, and must match the size of the UnitaryMatrix Example: >>> u = UnitaryMatrix(np.array([[0,-1],[1,0]])) >>> cg = CustomGate(u, [0]) >>> print(run(QCircuit([X(0), cg]), IBMDevice.AER_SIMULATOR)) Result: IBMDevice, AER_SIMULATOR State vector: [-1, 0] Probabilities: [1, 0] Number of qubits: 1 Note: For the moment, only ordered and contiguous target qubits are allowed when instantiating a CustomGate. """ def __init__( self, definition: UnitaryMatrix, targets: list[int], label: Optional[str] = None ): self.definition = definition """See parameter description.""" if definition.nb_qubits != len(targets): raise ValueError( f"Size of the targets ({len(targets)}) must match the number of qubits of the " f"UnitaryMatrix ({definition.nb_qubits})" ) if not all([targets[i] + 1 == targets[i + 1] for i in range(len(targets) - 1)]): raise ValueError( "Target qubits must be ordered and contiguous for a CustomGate." ) # 3M-TODO: add later the possibility to give non-contiguous and/or non-ordered target qubits for CustomGate, # use the to_matrix() method inherited from Gate, maybe super().__init__(targets, label) @property def matrix(self) -> Matrix: # TODO: move this to `to_canonical_matrix` and check for the usages return self.definition.matrix
[docs] def to_matrix(self, desired_gate_size: int = 0): return self.matrix
[docs] def to_canonical_matrix(self): return self.matrix
[docs] def to_other_language( self, language: Language = Language.QISKIT, qiskit_parameters: Optional[set["Parameter"]] = None, ): if language == Language.QISKIT: from qiskit.quantum_info.operators import Operator as QiskitOperator if qiskit_parameters is None: qiskit_parameters = set() return QiskitOperator(self.matrix) elif language == Language.QASM2: from qiskit import QuantumCircuit, qasm2 from mpqp.tools.circuit import replace_custom_gate nb_qubits = max(self.targets) + 1 qiskit_circ = QuantumCircuit(nb_qubits) instr = self.to_other_language(Language.QISKIT) if TYPE_CHECKING: from qiskit.quantum_info.operators import Operator as QiskitOperator assert isinstance(instr, QiskitOperator) qiskit_circ.unitary( instr, list(reversed(self.targets)), # dang qiskit qubits order self.label, ) circuit, gphase = replace_custom_gate(qiskit_circ.data[0], nb_qubits) qasm_str = qasm2.dumps(circuit) qasm_lines = qasm_str.splitlines() instructions_only = [ line for line in qasm_lines if not ( line.startswith("qreg") or line.startswith("include") or line.startswith("creg") or line.startswith("OPENQASM") ) ] return "\n".join(instructions_only), gphase else: raise NotImplementedError(f"Error: {language} is not supported")
def __repr__(self) -> str: label = ", " + self.label if self.label else "" return f"CustomGate({UnitaryMatrix(self.matrix)}, {self.targets} {label})" def decompose(self): """Returns the circuit made of native gates equivalent to this gate. 3M-TODO refine this doc and implement """ raise NotImplementedError()