diff --git a/qiskit/circuit/library/standard_gates/__init__.py b/qiskit/circuit/library/standard_gates/__init__.py index 99c09d32db31..b682b5f52886 100644 --- a/qiskit/circuit/library/standard_gates/__init__.py +++ b/qiskit/circuit/library/standard_gates/__init__.py @@ -31,6 +31,7 @@ CSwapGate CU1Gate CU3Gate + C3SqrtXGate CXGate CYGate CZGate @@ -80,7 +81,7 @@ from .u1 import U1Gate, CU1Gate, MCU1Gate from .u2 import U2Gate from .u3 import U3Gate, CU3Gate -from .x import XGate, CXGate, CCXGate, C3XGate, C4XGate, RCCXGate, RC3XGate +from .x import XGate, CXGate, CCXGate, C3XGate, C4XGate, RCCXGate, RC3XGate, C3SqrtXGate from .x import MCXGate, MCXGrayCode, MCXRecursive, MCXVChain from .y import YGate, CYGate from .z import ZGate, CZGate diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index c019fa3f3e2d..17b2046bb03e 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -52,6 +52,10 @@ XGate, CXGate, CCXGate, + C3XGate, + C4XGate, + RC3XGate, + C3SqrtXGate, YGate, CYGate, RYYGate, @@ -126,6 +130,121 @@ def_rccx.append(inst, qargs, cargs) _sel.add_equivalence(RCCXGate(), def_rccx) +# RC3XGate + +q = QuantumRegister(4, 'q') +def_rc3x = QuantumCircuit(q) +for inst, qargs, cargs in [ + (HGate(), [q[3]], []), + (TGate(), [q[3]], []), + (CXGate(), [q[2], q[3]], []), + (TdgGate(), [q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[3]], []), + (TGate(), [q[3]], []), + (CXGate(), [q[1], q[3]], []), + (TdgGate(), [q[3]], []), + (CXGate(), [q[0], q[3]], []), + (TGate(), [q[3]], []), + (CXGate(), [q[1], q[3]], []), + (TdgGate(), [q[3]], []), + (HGate(), [q[3]], []), + (TGate(), [q[3]], []), + (CXGate(), [q[2], q[3]], []), + (TdgGate(), [q[3]], []), + (HGate(), [q[3]], []), +]: + def_rc3x.append(inst, qargs, cargs) +_sel.add_equivalence(RC3XGate(), def_rc3x) + +# C3XGate + +q = QuantumRegister(4, 'q') +def_c3x = QuantumCircuit(q) +for inst, qargs, cargs in [ + (HGate(), [q[3]], []), + (CU1Gate(-pi/4), [q[0], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[3]], []), + (CU1Gate(pi/4), [q[1], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[3]], []), + (CU1Gate(-pi/4), [q[1], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[1], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(pi/4), [q[2], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(-pi/4), [q[2], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[1], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(pi/4), [q[2], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(-pi/4), [q[2], q[3]], []), + (HGate(), [q[3]], []) +]: + def_c3x.append(inst, qargs, cargs) +_sel.add_equivalence(C3XGate(), def_c3x) + +q = QuantumRegister(4, 'q') +def_c3sqrtx = QuantumCircuit(4) +for inst, qargs, cargs in [ + (HGate(), [q[3]], []), + (CU1Gate(-pi/8), [q[0], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[3]], []), + (CU1Gate(pi/8), [q[1], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[3]], []), + (CU1Gate(-pi/8), [q[1], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[1], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(pi/8), [q[2], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(-pi/8), [q[2], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[1], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(pi/8), [q[2], q[3]], []), + (HGate(), [q[3]], []), + (CXGate(), [q[0], q[2]], []), + (HGate(), [q[3]], []), + (CU1Gate(-pi/8), [q[2], q[3]], []), + (HGate(), [q[3]], []) +]: + def_c3sqrtx.append(inst, qargs, cargs) +_sel.add_equivalence(C3SqrtXGate(), def_c3sqrtx) + +# C4XGate + +q = QuantumRegister(5, 'q') +def_c4x = QuantumCircuit(q) +for inst, qargs, cargs in [ + (HGate(), [q[4]], []), + (CU1Gate(-pi / 2), [q[3], q[4]], []), + (HGate(), [q[4]], []), + (C3XGate(), [q[0], q[1], q[2], q[3]], []), + (HGate(), [q[4]], []), + (CU1Gate(pi / 2), [q[3], q[4]], []), + (HGate(), [q[4]], []), + (C3XGate(), [q[0], q[1], q[2], q[3]], []), + (C3SqrtXGate(), [q[0], q[1], q[2], q[4]], []), +]: + def_c4x.append(inst, qargs, cargs) +_sel.add_equivalence(C4XGate(), def_c4x) + # RXGate q = QuantumRegister(1, 'q') diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index e593c5409918..97d8d4418972 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -18,6 +18,7 @@ import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate +from qiskit.circuit.instruction import Instruction from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import _compute_control_matrix from qiskit.qasm import pi @@ -121,6 +122,7 @@ class CXMeta(type): Can be removed when CnotGate gets removed. """ + @classmethod def __instancecheck__(mcs, inst): return type(inst) in {CnotGate, CXGate} # pylint: disable=unidiomatic-typecheck @@ -204,7 +206,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): ControlledGate: controlled version of this gate. """ if ctrl_state is None: - ctrl_state = 2**num_ctrl_qubits - 1 + ctrl_state = 2 ** num_ctrl_qubits - 1 new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 1, label=label, ctrl_state=new_ctrl_state) gate.base_gate.label = self.label @@ -245,6 +247,7 @@ class CCXMeta(type): Can be removed when ToffoliGate gets removed. """ + @classmethod def __instancecheck__(mcs, inst): return type(inst) in {CCXGate, ToffoliGate} # pylint: disable=unidiomatic-typecheck @@ -365,7 +368,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): ControlledGate: controlled version of this gate. """ if ctrl_state is None: - ctrl_state = 2**num_ctrl_qubits - 1 + ctrl_state = 2 ** num_ctrl_qubits - 1 new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 2, label=label, ctrl_state=new_ctrl_state) gate.base_gate.label = self.label @@ -453,15 +456,9 @@ def to_matrix(self): class C3XGate(ControlledGate): - """The 3-qubit controlled X gate. - - This implementation is based on Page 17 of [1]. - - References: - [1] Barenco et al., 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf - """ + """The 3-qubit controlled X gate.""" - def __init__(self, angle=numpy.pi/4, label=None, ctrl_state=None): + def __init__(self, angle=None, label=None, ctrl_state=None): """Create a new 3-qubit controlled X gate. Args: @@ -471,7 +468,22 @@ def __init__(self, angle=numpy.pi/4, label=None, ctrl_state=None): ctrl_state (int or str or None): control state expressed as integer, string (e.g. '110'), or None. If None, use all 1s. """ - super().__init__('mcx', 4, [], num_ctrl_qubits=3, label=label, ctrl_state=ctrl_state) + import warnings + if angle == numpy.pi / 8: + warnings.warn('The parameter angle in C3XGate is going to be removed in the future. ' + 'When angle=pi/8, use C3SqrtXGate()', + DeprecationWarning, stacklevel=2) + elif angle == numpy.pi / 4: + warnings.warn('The parameter angle in C3XGate is going to be removed in the future. ' + 'angle=pi/4, while be the default behavior C3XGate.', + DeprecationWarning, stacklevel=2) + elif angle is not None: + warnings.warn('The parameter angle in C3XGate is going to be removed in the future. ', + DeprecationWarning, stacklevel=2) + else: + angle = numpy.pi / 4 + + super().__init__('c3x', 4, [], label=label, num_ctrl_qubits=3, ctrl_state=ctrl_state) self.base_gate = XGate() self._angle = angle @@ -495,37 +507,18 @@ def _define(self): } """ from .u1 import CU1Gate - q = QuantumRegister(4, name='q') - definition = [ - (HGate(), [q[3]], []), - (CU1Gate(-self._angle), [q[0], q[3]], []), - (HGate(), [q[3]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[3]], []), - (CU1Gate(self._angle), [q[1], q[3]], []), - (HGate(), [q[3]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[3]], []), - (CU1Gate(-self._angle), [q[1], q[3]], []), - (HGate(), [q[3]], []), - (CXGate(), [q[1], q[2]], []), - (HGate(), [q[3]], []), - (CU1Gate(self._angle), [q[2], q[3]], []), - (HGate(), [q[3]], []), - (CXGate(), [q[0], q[2]], []), - (HGate(), [q[3]], []), - (CU1Gate(-self._angle), [q[2], q[3]], []), - (HGate(), [q[3]], []), - (CXGate(), [q[1], q[2]], []), - (HGate(), [q[3]], []), - (CU1Gate(self._angle), [q[2], q[3]], []), - (HGate(), [q[3]], []), - (CXGate(), [q[0], q[2]], []), - (HGate(), [q[3]], []), - (CU1Gate(-self._angle), [q[2], q[3]], []), - (HGate(), [q[3]], []) + + controlled_v = Instruction('cv', 2, 0, []) + q = QuantumRegister(2, 'q') + controlled_v.definition = [ + (HGate(), [q[1]], []), + # see https://github.com/PyCQA/pylint/issues/1472 + # pylint:disable=invalid-unary-operand-type + (CU1Gate(-self._angle), [q[0], q[1]], []), + (HGate(), [q[1]], []) ] - self.definition = definition + + self.definition = c3_root_decomposition(controlled_v) def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. @@ -539,36 +532,68 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): Returns: ControlledGate: controlled version of this gate. """ - if ctrl_state is None: - ctrl_state = 2**num_ctrl_qubits - 1 - new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state - gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 3, label=label, ctrl_state=new_ctrl_state) - gate.base_gate.label = self.label - return gate + if self._angle == -numpy.pi / 4: + if ctrl_state is None: + ctrl_state = 2 ** num_ctrl_qubits - 1 + new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state + gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 3, label=label, + ctrl_state=new_ctrl_state) + gate.base_gate.label = self.label + return gate + return super().control(num_ctrl_qubits, label, ctrl_state) - def inverse(self): - """Invert this gate. The C3X is its own inverse.""" - return C3XGate(angle=self._angle) - - # This matrix is only correct if the angle is pi/4 - # def to_matrix(self): - # """Return a numpy.array for the C3X gate.""" - # return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - # [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], - # [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=complex) + def to_matrix(self): + """Return a numpy.array for the C3X gate.""" + return _compute_control_matrix(self.base_gate.to_matrix(), + self.num_ctrl_qubits, + ctrl_state=self.ctrl_state) + + +class C3SqrtXGate(ControlledGate): + """The 3-qubit controlled √X gate.""" + + def __init__(self, label=None, ctrl_state=None): + """The 3-qubit controlled √X gate. + + Args: + label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. + """ + super().__init__('c3sqrtx', 4, [], label=label, num_ctrl_qubits=3, ctrl_state=ctrl_state) + self.base_gate = Gate('sqrtx', 1, []) + self.base_gate.to_matrix = lambda: 0.5 * numpy.array([[1 + 1j, 1 - 1j], [1 - 1j, 1 + 1j]]) + + def _define(self): + """ + gate c3sqrtx a,b,c,d + { + h d; cu1(-pi/8) a,d; h d; + cx a,b; + h d; cu1(pi/8) b,d; h d; + cx a,b; + h d; cu1(-pi/8) b,d; h d; + cx b,c; + h d; cu1(pi/8) c,d; h d; + cx a,c; + h d; cu1(-pi/8) c,d; h d; + cx b,c; + h d; cu1(pi/8) c,d; h d; + cx a,c; + h d; cu1(-pi/8) c,d; h d; + } + """ + from .u1 import CU1Gate + + controlled_v = Instruction('cv', 2, 0, []) + q = QuantumRegister(2, 'q') + controlled_v.definition = [ + (HGate(), [q[1]], []), + (CU1Gate(-numpy.pi / 8), [q[0], q[1]], []), + (HGate(), [q[1]], []) + ] + + self.definition = c3_root_decomposition(controlled_v) class RC3XGate(Gate): @@ -705,7 +730,7 @@ def _define(self): (CU1Gate(numpy.pi / 2), [q[3], q[4]], []), (HGate(), [q[4]], []), (C3XGate(), [q[0], q[1], q[2], q[3]], []), - (C3XGate(numpy.pi / 8), [q[0], q[1], q[2], q[4]], []), + (C3SqrtXGate(), [q[0], q[1], q[2], q[4]], []), ] self.definition = definition @@ -722,7 +747,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): ControlledGate: controlled version of this gate. """ if ctrl_state is None: - ctrl_state = 2**num_ctrl_qubits - 1 + ctrl_state = 2 ** num_ctrl_qubits - 1 new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 4, label=label, ctrl_state=new_ctrl_state) gate.base_gate.label = self.label @@ -965,3 +990,53 @@ def _define(self): (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], [])) self.definition = definition + + +def c3_root_decomposition(v_gate): + """Implements a 3-controlled operation :math:`U`, provided with :math:`V` with :math:`V^4 = U`. + + This decomposition is described in [1], Section 7. + + Args: + v_gate (Gate or Instruction): The operation :math:`V`. Can be a single qubit gate, or a + two qubit instruction implementing the controlled version :math:`CV`. + + Returns: + list: The definition for the 3-controlled :math:`U` gate. + + Raises: + ValueError: If ``v_base_gate`` is not a 1-qubit gate or 2-qubit instruction. + + References: + [1]: Barenco et al., Elementary gates for quantum computation, 1995; + `quant-ph/9503016 `_ + """ + if not (v_gate.num_qubits == 1 and isinstance(v_gate, Gate) or + v_gate.num_qubits == 2 and isinstance(v_gate, Instruction)): + raise ValueError('v_base_gate must be a 1-qubit gate or 2-qubit instruction.') + + if v_gate.num_qubits == 1: # V acts on one qubit + controlled_v = v_gate.control() + else: # V acts on two qubits + controlled_v = v_gate + controlled_v_dg = controlled_v.inverse() + + q = QuantumRegister(4, name='q') + + definition = [ + (controlled_v, [q[0], q[3]], []), + (CXGate(), [q[0], q[1]], []), + (controlled_v_dg, [q[1], q[3]], []), + (CXGate(), [q[0], q[1]], []), + (controlled_v, [q[1], q[3]], []), + (CXGate(), [q[1], q[2]], []), + (controlled_v_dg, [q[2], q[3]], []), + (CXGate(), [q[0], q[2]], []), + (controlled_v, [q[2], q[3]], []), + (CXGate(), [q[1], q[2]], []), + (controlled_v_dg, [q[2], q[3]], []), + (CXGate(), [q[0], q[2]], []), + (controlled_v, [q[2], q[3]], []), + ] + + return definition diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index 9de8c2bc281e..914e5c61a398 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -38,7 +38,7 @@ CCXGate, HGate, RZGate, RXGate, RYGate, CRYGate, CRXGate, CSwapGate, U3Gate, CHGate, CRZGate, CU3Gate, - MSGate, Barrier, RCCXGate, RC3XGate, + MSGate, RCCXGate, RC3XGate, MCU1Gate, MCXGate, MCXGrayCode, MCXRecursive, MCXVChain, C3XGate, C4XGate) from qiskit.circuit._utils import _compute_control_matrix @@ -1009,7 +1009,7 @@ def test_controlled_standard_gates(self, num_ctrl_qubits, gate_class): numargs = len(_get_free_params(gate_class)) args = [theta] * numargs - if gate_class in [MSGate, Barrier]: + if gate_class in [MSGate]: args[0] = 2 elif gate_class in [MCU1Gate]: args[1] = 2