Source code for qiskit.circuit.controlledgate

# -*- coding: utf-8 -*-

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

"""Controlled unitary gate."""

from typing import Tuple, List, Optional, Union
from qiskit.circuit.exceptions import CircuitError

from .gate import Gate
from .quantumregister import QuantumRegister
from .quantumregister import Qubit
from .classicalregister import Clbit

# pylint: disable=missing-return-doc


[docs]class ControlledGate(Gate): """Controlled unitary gate.""" def __init__(self, name: str, num_qubits: int, params: List, label: Optional[str] = None, num_ctrl_qubits: Optional[int] = 1, definition: Optional[List[Tuple[Gate, List[Qubit], List[Clbit]]]] = None, ctrl_state: Optional[Union[int, str]] = None): """Create a new ControlledGate. In the new gate the first ``num_ctrl_qubits`` of the gate are the controls. Args: name: The name of the gate. num_qubits: The number of qubits the gate acts on. params: A list of parameters for the gate. label: An optional label for the gate. num_ctrl_qubits: Number of control qubits. definition: A list of gate rules for implementing this gate. The elements of the list are tuples of (:meth:`~qiskit.circuit.Gate`, [qubit_list], [clbit_list]). ctrl_state: The control state in decimal or as a bitstring (e.g. '111'). If specified as a bitstring the length must equal num_ctrl_qubits, MSB on left. If None, use 2**num_ctrl_qubits-1. Raises: CircuitError: If ``num_ctrl_qubits`` >= ``num_qubits``. CircuitError: ctrl_state < 0 or ctrl_state > 2**num_ctrl_qubits. Examples: Create a controlled standard gate and apply it to a circuit. .. jupyter-execute:: from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit.library.standard_gates import HGate qr = QuantumRegister(3) qc = QuantumCircuit(qr) c3h_gate = HGate().control(2) qc.append(c3h_gate, qr) qc.draw() Create a controlled custom gate and apply it to a circuit. .. jupyter-execute:: from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit.library.standard_gates import HGate qc1 = QuantumCircuit(2) qc1.x(0) qc1.h(1) custom = qc1.to_gate().control(2) qc2 = QuantumCircuit(4) qc2.append(custom, [0, 3, 1, 2]) qc2.draw() """ super().__init__(name, num_qubits, params, label=label) if num_ctrl_qubits < num_qubits: self.num_ctrl_qubits = num_ctrl_qubits else: raise CircuitError('number of control qubits must be less than the number of qubits') self.base_gate = None if definition: self.definition = definition if len(definition) == 1: base_gate = definition[0][0] if isinstance(base_gate, ControlledGate): self.base_gate = base_gate.base_gate else: self.base_gate = base_gate self._ctrl_state = None self.ctrl_state = ctrl_state @property def definition(self) -> List: """Return definition in terms of other basic gates. If the gate has open controls, as determined from `self.ctrl_state`, the returned definition is conjugated with X without changing the internal `_definition`. """ if self._open_ctrl: closed_gate = self.copy() closed_gate.ctrl_state = None # pylint: disable=cyclic-import from qiskit.circuit.library.standard_gates import XGate bit_ctrl_state = bin(self.ctrl_state)[2:].zfill(self.num_ctrl_qubits) qreg = QuantumRegister(self.num_qubits, 'q') definition = [(closed_gate, qreg, [])] open_rules = [] for qind, val in enumerate(bit_ctrl_state[::-1]): if val == '0': open_rules.append([XGate(), [qreg[qind]], []]) if open_rules: return open_rules + definition + open_rules else: return self._definition else: return super().definition @definition.setter def definition(self, excited_def: List): """Set controlled gate definition with closed controls.""" super(Gate, self.__class__).definition.fset(self, excited_def) @property def ctrl_state(self) -> int: """Return the control state of the gate as a decimal integer.""" return self._ctrl_state @ctrl_state.setter def ctrl_state(self, ctrl_state: Union[int, str, None]): """Set the control state of this gate. Args: ctrl_state: The control state of the gate. Raises: CircuitError: ctrl_state is invalid. """ if isinstance(ctrl_state, str): try: assert len(ctrl_state) == self.num_ctrl_qubits ctrl_state = int(ctrl_state, 2) except ValueError: raise CircuitError('invalid control bit string: ' + ctrl_state) except AssertionError: raise CircuitError('invalid control bit string: length != ' 'num_ctrl_qubits') if isinstance(ctrl_state, int): if 0 <= ctrl_state < 2**self.num_ctrl_qubits: self._ctrl_state = ctrl_state else: raise CircuitError('invalid control state specification') elif ctrl_state is None: self._ctrl_state = 2**self.num_ctrl_qubits - 1 else: raise CircuitError('invalid control state specification: {}'.format( repr(ctrl_state))) @property def _open_ctrl(self) -> bool: """Return whether gate has any open controls""" return self.ctrl_state < 2**self.num_ctrl_qubits - 1 def __eq__(self, other) -> bool: return (isinstance(other, ControlledGate) and self.num_ctrl_qubits == other.num_ctrl_qubits and self.ctrl_state == other.ctrl_state and self.base_gate == other.base_gate and self.num_qubits == other.num_qubits and self.num_clbits == other.num_clbits and self.definition == other.definition)
[docs] def inverse(self) -> 'ControlledGate': """Invert this gate by calling inverse on the base gate.""" return self.base_gate.inverse().control(self.num_ctrl_qubits)