diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index f91f3b0908f7..6b08b3a5ad2b 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -25,6 +25,7 @@ Barrier C3XGate + C3SXGate C4XGate CCXGate DCXGate diff --git a/qiskit/circuit/library/standard_gates/__init__.py b/qiskit/circuit/library/standard_gates/__init__.py index 8da0eb854855..8a9482fd8e00 100644 --- a/qiskit/circuit/library/standard_gates/__init__.py +++ b/qiskit/circuit/library/standard_gates/__init__.py @@ -19,6 +19,7 @@ :toctree: ../stubs/ C3XGate + C3SXGate C4XGate CCXGate DCXGate @@ -89,7 +90,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, C3SXGate, C4XGate, RCCXGate, RC3XGate 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/x.py b/qiskit/circuit/library/standard_gates/x.py index 3861384704bc..9b3dad9288d9 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -12,6 +12,7 @@ """X, CX, CCX and multi-controlled X gates.""" +import warnings from math import ceil import numpy from qiskit.circuit.controlledgate import ControlledGate @@ -23,6 +24,7 @@ from .t import TGate, TdgGate from .u1 import U1Gate from .u2 import U2Gate +from .sx import SXGate class XGate(Gate): @@ -416,8 +418,8 @@ def __array__(self, dtype=None): [0, 0, 0, 1j, 0, 0, 0, 0]], dtype=dtype) -class C3XGate(ControlledGate): - """The 3-qubit controlled X gate. +class C3SXGate(ControlledGate): + """The 3-qubit controlled sqrt-X gate. This implementation is based on Page 17 of [1]. @@ -425,44 +427,53 @@ class C3XGate(ControlledGate): [1] Barenco et al., 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf """ - def __init__(self, angle=numpy.pi/4, label=None, ctrl_state=None): - """Create a new 3-qubit controlled X gate. + def __init__(self, label=None, ctrl_state=None, *, angle=None): + """Create a new 3-qubit controlled sqrt-X gate. Args: - angle (float): The angle used in the controlled-U1 gates. An angle of π/4 yields the - 3-qubit controlled X gate, an angle of π/8 the 3-qubit controlled sqrt(X) gate. 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. + angle (float): DEPRECATED. The angle used in the controlled-U1 gates. An angle of π/8 + yields the sqrt(X) gates, an angle of π/4 the 3-qubit controlled X gate. """ - super().__init__('mcx', 4, [], num_ctrl_qubits=3, label=label, - ctrl_state=ctrl_state, base_gate=XGate()) + super().__init__('c3sx', 4, [], num_ctrl_qubits=3, label=label, + ctrl_state=ctrl_state, base_gate=SXGate()) + + if angle is not None: + warnings.warn('The angle argument is deprecated as of Qiskit Terra 0.17.0 and will ' + 'be removed no earlier than 3 months after the release date.', + DeprecationWarning, stacklevel=2) + + if angle is None: + angle = numpy.pi / 8 self._angle = angle def _define(self): """ - gate c3x a,b,c,d + gate c3sqrtx a,b,c,d { - h d; cu1(-pi/4) a,d; h d; + h d; cu1(-pi/8) a,d; h d; cx a,b; - h d; cu1(pi/4) b,d; h d; + h d; cu1(pi/8) b,d; h d; cx a,b; - h d; cu1(-pi/4) b,d; h d; + h d; cu1(-pi/8) b,d; h d; cx b,c; - h d; cu1(pi/4) c,d; h d; + h d; cu1(pi/8) c,d; h d; cx a,c; - h d; cu1(-pi/4) c,d; h d; + h d; cu1(-pi/8) c,d; h d; cx b,c; - h d; cu1(pi/4) c,d; h d; + h d; cu1(pi/8) c,d; h d; cx a,c; - h d; cu1(-pi/4) c,d; h d; + h d; cu1(-pi/8) c,d; h d; } """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import CU1Gate q = QuantumRegister(4, name='q') + # pylint: disable=invalid-unary-operand-type rules = [ (HGate(), [q[3]], []), (CU1Gate(-self._angle), [q[0], q[3]], []), @@ -498,6 +509,109 @@ def _define(self): self.definition = qc + def inverse(self): + """Invert this gate. The C3X is its own inverse.""" + # pylint: disable=invalid-unary-operand-type + if self._angle is not None: + angle = -self._angle + else: + angle = None + + return C3SXGate(angle=angle, ctrl_state=self.ctrl_state) + + +class C3XGate(ControlledGate): + r"""The 4-qubit controlled X gate. + + This implementation uses :math:`\sqrt{T}` and 14 CNOT gates. + """ + + def __new__(cls, angle=None, label=None, ctrl_state=None): + if angle is not None: + return C3SXGate(label, ctrl_state, angle=angle) + + instance = super().__new__(cls) + instance.__init__(None, label, ctrl_state) + return instance + + # pylint: disable=unused-argument + def __init__(self, angle=None, label=None, ctrl_state=None): + """Create a new 3-qubit controlled X gate.""" + super().__init__('mcx', 4, [], num_ctrl_qubits=3, label=label, + ctrl_state=ctrl_state, base_gate=XGate()) + + # seems like open controls not hapening? + def _define(self): + """ + gate c3x a,b,c,d + { + h d; + p(pi/8) a; + p(pi/8) b; + p(pi/8) c; + p(pi/8) d; + cx a, b; + p(-pi/8) b; + cx a, b; + cx b, c; + p(-pi/8) c; + cx a, c; + p(pi/8) c; + cx b, c; + p(-pi/8) c; + cx a, c; + cx c, d; + p(-pi/8) d; + cx b, d; + p(pi/8) d; + cx c, d; + p(-pi/8) d; + cx a, d; + p(pi/8) d; + cx c, d; + p(-pi/8) d; + cx b, d; + p(pi/8) d; + cx c, d; + p(-pi/8) d; + cx a, d; + h d; + } + """ + from qiskit.circuit.quantumcircuit import QuantumCircuit + q = QuantumRegister(4, name='q') + qc = QuantumCircuit(q, name=self.name) + qc.h(3) + qc.p(pi / 8, [0, 1, 2, 3]) + qc.cx(0, 1) + qc.p(-pi / 8, 1) + qc.cx(0, 1) + qc.cx(1, 2) + qc.p(-pi / 8, 2) + qc.cx(0, 2) + qc.p(pi / 8, 2) + qc.cx(1, 2) + qc.p(-pi / 8, 2) + qc.cx(0, 2) + qc.cx(2, 3) + qc.p(-pi / 8, 3) + qc.cx(1, 3) + qc.p(pi / 8, 3) + qc.cx(2, 3) + qc.p(-pi / 8, 3) + qc.cx(0, 3) + qc.p(pi / 8, 3) + qc.cx(2, 3) + qc.p(-pi / 8, 3) + qc.cx(1, 3) + qc.p(pi / 8, 3) + qc.cx(2, 3) + qc.p(-pi / 8, 3) + qc.cx(0, 3) + qc.h(3) + + self.definition = qc + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. @@ -517,28 +631,17 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): return gate def inverse(self): - """Invert this gate. The C3X is its own inverse.""" - return C3XGate(angle=-self._angle, ctrl_state=self.ctrl_state) - - # This matrix is only correct if the angle is pi/4 - # def __array__(self, dtype=None): - # """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=dtype) + """Invert this gate. The C4X is its own inverse.""" + return C3XGate(ctrl_state=self.ctrl_state) + + def __array__(self, dtype=None): + """Return a numpy.array for the C4X gate.""" + mat = _compute_control_matrix(self.base_gate.to_matrix(), + self.num_ctrl_qubits, + ctrl_state=self.ctrl_state) + if dtype: + return numpy.asarray(mat, dtype=dtype) + return mat class RC3XGate(Gate): @@ -686,7 +789,7 @@ def _define(self): (CU1Gate(numpy.pi / 2), [q[3], q[4]], []), (HGate(), [q[4]], []), (RC3XGate().inverse(), [q[0], q[1], q[2], q[3]], []), - (C3XGate(numpy.pi / 8), [q[0], q[1], q[2], q[4]], []), + (C3SXGate(), [q[0], q[1], q[2], q[4]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) diff --git a/qiskit/qasm/libs/qelib1.inc b/qiskit/qasm/libs/qelib1.inc index c99fb6e2cc8b..66b6b8c6b0e9 100644 --- a/qiskit/qasm/libs/qelib1.inc +++ b/qiskit/qasm/libs/qelib1.inc @@ -206,19 +206,37 @@ gate rc3x a,b,c,d // 3-controlled X gate gate c3x a,b,c,d { - h d; cu1(-pi/4) a,d; h d; - cx a,b; - h d; cu1(pi/4) b,d; h d; - cx a,b; - h d; cu1(-pi/4) b,d; h d; - cx b,c; - h d; cu1(pi/4) c,d; h d; - cx a,c; - h d; cu1(-pi/4) c,d; h d; - cx b,c; - h d; cu1(pi/4) c,d; h d; - cx a,c; - h d; cu1(-pi/4) c,d; h d; + h d; + p(pi/8) a; + p(pi/8) b; + p(pi/8) c; + p(pi/8) d; + cx a, b; + p(-pi/8) b; + cx a, b; + cx b, c; + p(-pi/8) c; + cx a, c; + p(pi/8) c; + cx b, c; + p(-pi/8) c; + cx a, c; + cx c, d; + p(-pi/8) d; + cx b, d; + p(pi/8) d; + cx c, d; + p(-pi/8) d; + cx a, d; + p(pi/8) d; + cx c, d; + p(-pi/8) d; + cx b, d; + p(pi/8) d; + cx c, d; + p(-pi/8) d; + cx a, d; + h d; } // 3-controlled sqrt(X) gate, this equals the C3X gate where the CU1 rotations are -pi/8 not -pi/4 gate c3sqrtx a,b,c,d diff --git a/releasenotes/notes/improved-c3x-decomposition-aa812a0bc8280c7a.yaml b/releasenotes/notes/improved-c3x-decomposition-aa812a0bc8280c7a.yaml new file mode 100644 index 000000000000..557911c6946b --- /dev/null +++ b/releasenotes/notes/improved-c3x-decomposition-aa812a0bc8280c7a.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Reduce the number of CX gates in the decomposition of the 3-controlled + X gate, :class:`~qiskit.circuit.library.C3XGate`. Compiled and optimized + in the `U CX` basis, now only 14 CX and 16 U gates are used instead of + 20 and 22, respectively. diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index 11de882aadfb..8f21a62da04f 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -38,8 +38,8 @@ U3Gate, CHGate, CRZGate, CU3Gate, CUGate, SXGate, CSXGate, MSGate, Barrier, RCCXGate, RC3XGate, MCU1Gate, MCXGate, MCXGrayCode, - MCXRecursive, MCXVChain, C3XGate, C4XGate, - MCPhaseGate) + MCXRecursive, MCXVChain, C3XGate, C3SXGate, + C4XGate, MCPhaseGate) from qiskit.circuit._utils import _compute_control_matrix import qiskit.circuit.library.standard_gates as allGates from qiskit.extensions import UnitaryGate @@ -1253,6 +1253,7 @@ def test_controlled_standard_gates(self, num_ctrl_qubits, gate_class): base_mat = (np.cos(0.5 * theta) * iden - 1j * np.sin(0.5 * theta) * zgen).data else: base_mat = Operator(gate).data + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) self.assertEqual(Operator(cgate), Operator(target_mat)) @@ -1296,6 +1297,7 @@ class TestControlledGateLabel(QiskitTestCase): (CXGate, []), (CCXGate, []), (C3XGate, []), + (C3SXGate, []), (C4XGate, []), (MCXGate, [5]), (PhaseGate, [0.1]),