diff --git a/qiskit/extensions/standard/__init__.py b/qiskit/extensions/standard/__init__.py index 4d21ef78f338..863d3821ad04 100644 --- a/qiskit/extensions/standard/__init__.py +++ b/qiskit/extensions/standard/__init__.py @@ -18,6 +18,8 @@ from .iden import IdGate from .ms import MSGate from .r import RGate +from .rccx import RCCXGate +from .rcccx import RCCCXGate from .rx import RXGate, CrxGate from .rxx import RXXGate from .ry import RYGate, CryGate diff --git a/qiskit/extensions/standard/multi_control_toffoli_gate.py b/qiskit/extensions/standard/multi_control_toffoli_gate.py index c285a995fdc5..558835a5c18e 100644 --- a/qiskit/extensions/standard/multi_control_toffoli_gate.py +++ b/qiskit/extensions/standard/multi_control_toffoli_gate.py @@ -22,8 +22,6 @@ from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit from qiskit import QiskitError -# pylint: disable=unused-import -from .relative_phase_toffoli import rccx logger = logging.getLogger(__name__) diff --git a/qiskit/extensions/standard/rcccx.py b/qiskit/extensions/standard/rcccx.py new file mode 100644 index 000000000000..b91917a86639 --- /dev/null +++ b/qiskit/extensions/standard/rcccx.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + +"""The simplified 3-controlled Toffoli gate.""" + +import numpy + +from qiskit.circuit import QuantumCircuit, Gate, QuantumRegister +from qiskit.extensions.standard.u1 import U1Gate +from qiskit.extensions.standard.u2 import U2Gate +from qiskit.extensions.standard.x import CnotGate +from qiskit.qasm import pi + + +class RCCCXGate(Gate): + """The simplified 3-controlled Toffoli gate. + + The simplified Toffoli gate implements the Toffoli gate up to relative phases. + Note, that the simplified Toffoli is not equivalent to the Toffoli. But can be used in places + where the Toffoli gate is uncomputed again. + + This concrete implementation is from https://arxiv.org/abs/1508.03273, the complete circuit + of Fig. 4. + """ + + def __init__(self): + """Create a new RCCCX gate.""" + super().__init__('rcccx', 4, []) + + def _define(self): + """ + gate rcccx a,b,c,d + { u2(0,pi) d; + u1(pi/4) d; + cx c,d; + u1(-pi/4) d; + u2(0,pi) d; + cx a,d; + u1(pi/4) d; + cx b,d; + u1(-pi/4) d; + cx a,d; + u1(pi/4) d; + cx b,d; + u1(-pi/4) d; + u2(0,pi) d; + u1(pi/4) d; + cx c,d; + u1(-pi/4) d; + u2(0,pi) d; + } + """ + definition = [] + q = QuantumRegister(4, 'q') + + rule = [ + (U2Gate(0, pi), [q[3]], []), # H gate + (U1Gate(pi / 4), [q[3]], []), # T gate + (CnotGate(), [q[2], q[3]], []), + (U1Gate(-pi / 4), [q[3]], []), # inverse T gate + (U2Gate(0, pi), [q[3]], []), + (CnotGate(), [q[0], q[3]], []), + (U1Gate(pi / 4), [q[3]], []), + (CnotGate(), [q[1], q[3]], []), + (U1Gate(-pi / 4), [q[3]], []), + (CnotGate(), [q[0], q[3]], []), + (U1Gate(pi / 4), [q[3]], []), + (CnotGate(), [q[1], q[3]], []), + (U1Gate(-pi / 4), [q[3]], []), + (U2Gate(0, pi), [q[3]], []), + (U1Gate(pi / 4), [q[3]], []), + (CnotGate(), [q[2], q[3]], []), + (U1Gate(-pi / 4), [q[3]], []), + (U2Gate(0, pi), [q[3]], []), + ] + for inst in rule: + definition.append(inst) + self.definition = definition + + def to_matrix(self): + """Return a numpy.array for the RCCCX 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, 1j, 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, -1j, 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 rcccx(self, control_qubit1, control_qubit2, control_qubit3, target_qubit): + """Apply the simplified, relative-phase 3-control Toffoli gate.""" + return self.append(RCCCXGate(), [control_qubit1, control_qubit2, control_qubit3, target_qubit], + []) + + +QuantumCircuit.rcccx = rcccx diff --git a/qiskit/extensions/standard/rccx.py b/qiskit/extensions/standard/rccx.py new file mode 100644 index 000000000000..1ee5f58c7364 --- /dev/null +++ b/qiskit/extensions/standard/rccx.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# 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. + +"""The simplified Toffoli gate.""" + +import numpy + +from qiskit.circuit import QuantumCircuit, Gate, QuantumRegister +from qiskit.extensions.standard.u1 import U1Gate +from qiskit.extensions.standard.u2 import U2Gate +from qiskit.extensions.standard.x import CnotGate +from qiskit.qasm import pi + + +class RCCXGate(Gate): + """The simplified Toffoli gate, also referred to as Margolus gate. + + The simplified Toffoli gate implements the Toffoli gate up to relative phases. + This implementation requires three CX gates which is the minimal amount possible, + as shown in https://arxiv.org/abs/quant-ph/0312225. + Note, that the simplified Toffoli is not equivalent to the Toffoli. But can be used in places + where the Toffoli gate is uncomputed again. + + This concrete implementation is from https://arxiv.org/abs/1508.03273, the dashed box + of Fig. 3. + """ + + def __init__(self): + """Create a new simplified CCX gate.""" + super().__init__('rccx', 3, []) + + def _define(self): + """ + gate rccx a,b,c + { u2(0,pi) c; + u1(pi/4) c; + cx b, c; + u1(-pi/4) c; + cx a, c; + u1(pi/4) c; + cx b, c; + u1(-pi/4) c; + u2(0,pi) c; + } + """ + definition = [] + q = QuantumRegister(3, 'q') + rule = [ + (U2Gate(0, pi), [q[2]], []), # H gate + (U1Gate(pi / 4), [q[2]], []), # T gate + (CnotGate(), [q[1], q[2]], []), + (U1Gate(-pi / 4), [q[2]], []), # inverse T gate + (CnotGate(), [q[0], q[2]], []), + (U1Gate(pi / 4), [q[2]], []), + (CnotGate(), [q[1], q[2]], []), + (U1Gate(-pi / 4), [q[2]], []), # inverse T gate + (U2Gate(0, pi), [q[2]], []), # H gate + ] + for inst in rule: + definition.append(inst) + self.definition = definition + + def to_matrix(self): + """Return a numpy.array for the simplified CCX gate.""" + return numpy.array([[1, 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, -1j], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, -1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1j, 0, 0, 0, 0]], dtype=complex) + + +def rccx(self, control_qubit1, control_qubit2, target_qubit): + """Apply the simplified, relative-phase Toffoli gate.""" + return self.append(RCCXGate(), [control_qubit1, control_qubit2, target_qubit], []) + + +QuantumCircuit.rccx = rccx diff --git a/qiskit/extensions/standard/relative_phase_toffoli.py b/qiskit/extensions/standard/relative_phase_toffoli.py deleted file mode 100644 index 108a06eb0f39..000000000000 --- a/qiskit/extensions/standard/relative_phase_toffoli.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 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. - -""" -Relative Phase Toffoli Gates. -""" - -from qiskit.circuit import QuantumCircuit, Qubit -from qiskit.qasm import pi - -from qiskit import QiskitError - - -def _apply_rccx(circ, qba, qbb, qbc): - circ.u2(0, pi, qbc) # h - circ.u1(pi / 4, qbc) # t - circ.cx(qbb, qbc) - circ.u1(-pi / 4, qbc) # tdg - circ.cx(qba, qbc) - circ.u1(pi / 4, qbc) # t - circ.cx(qbb, qbc) - circ.u1(-pi / 4, qbc) # tdg - circ.u2(0, pi, qbc) # h - - -def _apply_rcccx(circ, qba, qbb, qbc, qbd): - circ.u2(0, pi, qbd) # h - circ.u1(pi / 4, qbd) # t - circ.cx(qbc, qbd) - circ.u1(-pi / 4, qbd) # tdg - circ.u2(0, pi, qbd) # h - circ.cx(qba, qbd) - circ.u1(pi / 4, qbd) # t - circ.cx(qbb, qbd) - circ.u1(-pi / 4, qbd) # tdg - circ.cx(qba, qbd) - circ.u1(pi / 4, qbd) # t - circ.cx(qbb, qbd) - circ.u1(-pi / 4, qbd) # tdg - circ.u2(0, pi, qbd) # h - circ.u1(pi / 4, qbd) # t - circ.cx(qbc, qbd) - circ.u1(-pi / 4, qbd) # tdg - circ.u2(0, pi, qbd) # h - - -def rccx(self, q_control_1, q_control_2, q_target): - """ - Apply 2-Control Relative-Phase Toffoli gate from q_control_1 and q_control_2 to q_target. - - The implementation is based on https://arxiv.org/pdf/1508.03273.pdf Figure 3 - - Args: - self (QuantumCircuit): The QuantumCircuit object to apply the rccx gate on. - q_control_1 (Qubit): The 1st control qubit. - q_control_2 (Qubit): The 2nd control qubit. - q_target (Qubit): The target qubit. - - Raises: - QiskitError: improper qubit specification - """ - if not isinstance(q_control_1, Qubit): - raise QiskitError('A qubit is expected for the first control.') - if not self.has_register(q_control_1.register): - raise QiskitError('The first control qubit is expected to be part of the circuit.') - - if not isinstance(q_control_2, Qubit): - raise QiskitError('A qubit is expected for the second control.') - if not self.has_register(q_control_2.register): - raise QiskitError('The second control qubit is expected to be part of the circuit.') - - if not isinstance(q_target, Qubit): - raise QiskitError('A qubit is expected for the target.') - if not self.has_register(q_target.register): - raise QiskitError('The target qubit is expected to be part of the circuit.') - self._check_dups([q_control_1, q_control_2, q_target]) - _apply_rccx(self, q_control_1, q_control_2, q_target) - - -def rcccx(self, q_control_1, q_control_2, q_control_3, q_target): - """ - Apply 3-Control Relative-Phase Toffoli gate from q_control_1, q_control_2, - and q_control_3 to q_target. - - The implementation is based on https://arxiv.org/pdf/1508.03273.pdf Figure 4 - - Args: - self (QuantumCircuit): The QuantumCircuit object to apply the rcccx gate on. - q_control_1 (Qubit): The 1st control qubit. - q_control_2 (Qubit): The 2nd control qubit. - q_control_3 (Qubit): The 3rd control qubit. - q_target (Qubit): The target qubit. - - Raises: - QiskitError: improper qubit specification - """ - if not isinstance(q_control_1, Qubit): - raise QiskitError('A qubit is expected for the first control.') - if not self.has_register(q_control_1.register): - raise QiskitError('The first control qubit is expected to be part of the circuit.') - - if not isinstance(q_control_2, Qubit): - raise QiskitError('A qubit is expected for the second control.') - if not self.has_register(q_control_2.register): - raise QiskitError('The second control qubit is expected to be part of the circuit.') - - if not isinstance(q_control_3, Qubit): - raise QiskitError('A qubit is expected for the third control.') - if not self.has_register(q_control_3.register): - raise QiskitError('The third control qubit is expected to be part of the circuit.') - - if not isinstance(q_target, Qubit): - raise QiskitError('A qubit is expected for the target.') - if not self.has_register(q_target.register): - raise QiskitError('The target qubit is expected to be part of the circuit.') - - self._check_dups([q_control_1, q_control_2, q_control_3, q_target]) - _apply_rcccx(self, q_control_1, q_control_2, q_control_3, q_target) - - -QuantumCircuit.rccx = rccx -QuantumCircuit.rcccx = rcccx diff --git a/qiskit/qasm/libs/qelib1.inc b/qiskit/qasm/libs/qelib1.inc index 4b5b99eec505..967dad4623bd 100644 --- a/qiskit/qasm/libs/qelib1.inc +++ b/qiskit/qasm/libs/qelib1.inc @@ -140,3 +140,38 @@ gate rzz(theta) a,b u1(theta) b; cx a,b; } +// relative-phase CCX +gate rccx a,b,c +{ + u2(0,pi) c; + u1(pi/4) c; + cx b, c; + u1(-pi/4) c; + cx a, c; + u1(pi/4) c; + cx b, c; + u1(-pi/4) c; + u2(0,pi) c; +} +// relative-phase CCCX +gate rcccx a,b,c,d +{ + u2(0,pi) d; + u1(pi/4) d; + cx c,d; + u1(-pi/4) d; + u2(0,pi) d; + cx a,d; + u1(pi/4) d; + cx b,d; + u1(-pi/4) d; + cx a,d; + u1(pi/4) d; + cx b,d; + u1(-pi/4) d; + u2(0,pi) d; + u1(pi/4) d; + cx c,d; + u1(-pi/4) d; + u2(0,pi) d; +} diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index c11ae37827aa..d0ad34219a07 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -36,7 +36,7 @@ ToffoliGate, HGate, RZGate, RXGate, RYGate, CryGate, CrxGate, FredkinGate, U3Gate, CHGate, CrzGate, Cu3Gate, - MSGate, Barrier) + MSGate, Barrier, RCCXGate, RCCCXGate) from qiskit.extensions.unitary import UnitaryGate import qiskit.extensions.standard as allGates @@ -350,7 +350,6 @@ def test_rotation_gates(self): dag = circuit_to_dag(qc) unroller = Unroller(['u3', 'cx']) uqc = dag_to_circuit(unroller.run(dag)) - print(uqc.size()) self.log.info('%s gate count: %d', uqc.name, uqc.size()) self.assertTrue(uqc.size() <= 93) # this limit could be changed @@ -483,6 +482,40 @@ def test_controlled_standard_gates(self, num_ctrl_qubits): target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits) self.assertTrue(matrix_equal(Operator(cgate).data, target_mat, ignore_phase=True)) + @data(2, 3) + def test_relative_phase_toffoli_gates(self, num_ctrl_qubits): + """Test the relative phase Toffoli gates. + + This test compares the matrix representation of the relative phase gate classes + (i.e. RCCXGate().to_matrix()), the matrix obtained from the unitary simulator, + and the exact version of the gate as obtained through `_compute_control_matrix`. + """ + # get target matrix (w/o relative phase) + base_mat = XGate().to_matrix() + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits) + + # build the matrix for the relative phase toffoli using the unitary simulator + circuit = QuantumCircuit(num_ctrl_qubits + 1) + if num_ctrl_qubits == 2: + circuit.rccx(0, 1, 2) + else: # num_ctrl_qubits == 3: + circuit.rcccx(0, 1, 2, 3) + simulator = BasicAer.get_backend('unitary_simulator') + simulated_mat = execute(circuit, simulator).result().get_unitary() + + # get the matrix representation from the class itself + if num_ctrl_qubits == 2: + repr_mat = RCCXGate().to_matrix() + else: # num_ctrl_qubits == 3: + repr_mat = RCCCXGate().to_matrix() + + # test up to phase + # note, that all entries may have an individual phase! (as opposed to a global phase) + self.assertTrue(matrix_equal(np.abs(simulated_mat), target_mat)) + + # compare simulated matrix with the matrix representation provided by the class + self.assertTrue(matrix_equal(simulated_mat, repr_mat)) + def _compute_control_matrix(base_mat, num_ctrl_qubits): """ diff --git a/test/python/test_qasm_parser.py b/test/python/test_qasm_parser.py index c0f3aad3889a..8be70a1b5f8b 100644 --- a/test/python/test_qasm_parser.py +++ b/test/python/test_qasm_parser.py @@ -34,6 +34,7 @@ def parse(file_path, prec=15): class TestParser(QiskitTestCase): """QasmParser""" + def setUp(self): self.qasm_file_path = self._get_resource_path('example.qasm', Path.QASMS) self.qasm_file_path_fail = self._get_resource_path( @@ -47,10 +48,10 @@ def test_parser(self): res = parse(self.qasm_file_path) self.log.info(res) # TODO: For now only some basic checks. - self.assertEqual(len(res), 1931) + self.assertEqual(len(res), 2358) self.assertEqual(res[:12], "OPENQASM 2.0") self.assertEqual(res[14:41], "gate u3(theta,phi,lambda) q") - self.assertEqual(res[1915:1930], "measure r -> d;") + self.assertEqual(res[2342:2357], "measure r -> d;") def test_parser_fail(self): """should fail a for a not valid circuit."""