From c2a901455bff68e5de7240f0ad2b1f4b7e542545 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:03:53 +0100 Subject: [PATCH] Introduce new op types `GPI`, `GPI2` and `AAMS` (#1329) --- pytket/binders/circuit/Circuit/add_op.cpp | 72 +++++++ pytket/binders/circuit/main.cpp | 22 +++ pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 1 + pytket/pytket/_tket/circuit.pyi | 183 +++++++++++------- tket/conanfile.py | 2 +- tket/include/tket/Circuit/CircPool.hpp | 6 + .../Gate/GateUnitaryMatrixImplementations.hpp | 4 + tket/include/tket/OpType/OpType.hpp | 27 +++ tket/src/Circuit/CircPool.cpp | 22 +++ tket/src/Circuit/CircUtils.cpp | 4 + tket/src/Gate/Gate.cpp | 41 +++- tket/src/Gate/GateUnitaryMatrix.cpp | 3 + tket/src/Gate/GateUnitaryMatrixPrimitives.cpp | 28 +++ tket/src/OpType/OpTypeFunctions.cpp | 10 +- tket/src/OpType/OpTypeInfo.cpp | 3 + tket/test/src/test_CompilerPass.cpp | 67 +++++++ 17 files changed, 424 insertions(+), 73 deletions(-) diff --git a/pytket/binders/circuit/Circuit/add_op.cpp b/pytket/binders/circuit/Circuit/add_op.cpp index 11aaada87c..c26e0d7614 100644 --- a/pytket/binders/circuit/Circuit/add_op.cpp +++ b/pytket/binders/circuit/Circuit/add_op.cpp @@ -1317,6 +1317,42 @@ void init_circuit_add_op(py::class_> &c) { "\n\n:return: the new :py:class:`Circuit`", py::arg("angle0"), py::arg("angle1"), py::arg("angle2"), py::arg("qubit")) + .def( + "GPI", + [](Circuit *circ, const Expr &angle, unsigned qb, + const py::kwargs &kwargs) { + return add_gate_method_oneparam( + circ, OpType::GPI, angle, {qb}, kwargs); + }, + "Appends a GPI gate with a possibly symbolic angle " + "(specified in half-turns)." + "\n\n:return: the new :py:class:`Circuit`", + py::arg("angle"), py::arg("qubit")) + .def( + "GPI2", + [](Circuit *circ, const Expr &angle, unsigned qb, + const py::kwargs &kwargs) { + return add_gate_method_oneparam( + circ, OpType::GPI, angle, {qb}, kwargs); + }, + "Appends a GPI2 gate with a possibly symbolic angle " + "(specified in half-turns)." + "\n\n:return: the new :py:class:`Circuit`", + py::arg("angle"), py::arg("qubit")) + .def( + "AAMS", + [](Circuit *circ, const Expr &angle0, const Expr &angle1, + const Expr &angle2, const unsigned &qb0, const unsigned &qb1, + const py::kwargs &kwargs) { + return add_gate_method_manyparams( + circ, OpType::AAMS, {angle0, angle1, angle2}, {qb0, qb1}, + kwargs); + }, + "Appends an AAMS gate with possibly symbolic angles " + "(specified in half-turns)." + "\n\n:return: the new :py:class:`Circuit`", + py::arg("angle0"), py::arg("angle1"), py::arg("angle2"), + py::arg("qubit0"), py::arg("qubit1")) .def( "TK1", [](Circuit *circ, const Expr &angle0, const Expr &angle1, @@ -1940,6 +1976,42 @@ void init_circuit_add_op(py::class_> &c) { "\n\n:return: the new :py:class:`Circuit`", py::arg("angle0"), py::arg("angle1"), py::arg("angle2"), py::arg("qubit")) + .def( + "GPI", + [](Circuit *circ, const Expr &angle, const Qubit &qb, + const py::kwargs &kwargs) { + return add_gate_method_oneparam( + circ, OpType::GPI, angle, {qb}, kwargs); + }, + "Appends a GPI gate with a possibly symbolic angle " + "(specified in half-turns)." + "\n\n:return: the new :py:class:`Circuit`", + py::arg("angle"), py::arg("qubit")) + .def( + "GPI2", + [](Circuit *circ, const Expr &angle, const Qubit &qb, + const py::kwargs &kwargs) { + return add_gate_method_oneparam( + circ, OpType::GPI2, angle, {qb}, kwargs); + }, + "Appends a GPI2 gate with a possibly symbolic angle " + "(specified in half-turns)." + "\n\n:return: the new :py:class:`Circuit`", + py::arg("angle"), py::arg("qubit")) + .def( + "AAMS", + [](Circuit *circ, const Expr &angle0, const Expr &angle1, + const Expr &angle2, const Qubit &qb0, const Qubit &qb1, + const py::kwargs &kwargs) { + return add_gate_method_manyparams( + circ, OpType::AAMS, {angle0, angle1, angle2}, {qb0, qb1}, + kwargs); + }, + "Appends an AAMS gate with possibly symbolic angles " + "(specified in half-turns)." + "\n\n:return: the new :py:class:`Circuit`", + py::arg("angle0"), py::arg("angle1"), py::arg("angle2"), + py::arg("qubit0"), py::arg("qubit1")) .def( "TK1", [](Circuit *circ, const Expr &angle0, const Expr &angle1, diff --git a/pytket/binders/circuit/main.cpp b/pytket/binders/circuit/main.cpp index b971ae6850..2687745a63 100644 --- a/pytket/binders/circuit/main.cpp +++ b/pytket/binders/circuit/main.cpp @@ -186,6 +186,28 @@ PYBIND11_MODULE(circuit, m) { "\\end{array} \\right] = e^{\\frac12 i\\pi(\\lambda+\\phi)} " "\\mathrm{Rz}(\\phi) \\mathrm{Ry}(\\theta) " "\\mathrm{Rz}(\\lambda)`") + .value( + "GPI", OpType::GPI, + ":math:`(\\phi) \\mapsto \\left[ \\begin{array}{cc} 0 & " + "e^{-i\\pi\\phi} \\\\ e^{i\\pi\\phi} & 0 \\end{array} \\right]`") + .value( + "GPI2", OpType::GPI2, + ":math:`(\\phi) \\mapsto \\frac{1}{\\sqrt 2} \\left[ " + "\\begin{array}{cc} 1 & -ie^{-i\\pi\\phi} \\\\ -ie^{i\\pi\\phi} & " + "1 \\end{array} \\right]`") + .value( + "AAMS", OpType::AAMS, + ":math:`(\\theta, \\phi_0, \\phi_1) \\mapsto \\left[ " + "\\begin{array}{cccc} \\cos\\frac{\\pi\\theta}{2} & 0 & 0 & " + "-ie^{-i\\pi(\\phi_0+\\phi_1)}\\sin\\frac{\\pi\\theta}{2} \\\\ " + "0 & " + "\\cos\\frac{\\pi\\theta}{2} & " + "-ie^{i\\pi(\\phi_1-\\phi_0)}\\sin\\frac{\\pi\\theta}{2} & 0 \\\\ 0 " + "& " + "-ie^{i\\pi(\\phi_0-\\phi_1)}\\sin\\frac{\\pi\\theta}{2} & " + "\\cos\\frac{\\pi\\theta}{2} & 0 \\\\ " + "-ie^{i\\pi(\\phi_0+\\phi_1)}\\sin\\frac{\\pi\\theta}{2} & 0 & 0 & " + "\\cos\\frac{\\pi\\theta}{2} \\end{array} \\right]`") .value( "TK1", OpType::TK1, ":math:`(\\alpha, \\beta, \\gamma) \\mapsto " diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 0b64c80c5c..f6220f8a89 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.110@tket/stable") + self.requires("tket/1.2.111@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 1812c38873..f47aee83c6 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -15,6 +15,7 @@ Features: * New optimisation ``Transform.PushCliffordsThroughMeasures()`` and pass ``CliffordPushThroughMeasures`` that optimises Clifford subcircuits before end of circuit measurement gates. +* Add ``OpType.GPI``, ``OpType.GPI2`` and ``OpType.AAMS``. Fixes: diff --git a/pytket/pytket/_tket/circuit.pyi b/pytket/pytket/_tket/circuit.pyi index 56a36cb978..163ac46444 100644 --- a/pytket/pytket/_tket/circuit.pyi +++ b/pytket/pytket/_tket/circuit.pyi @@ -161,6 +161,20 @@ class Circuit: Construct Circuit instance from JSON serializable dictionary representation of the Circuit. """ @typing.overload + def AAMS(self, angle0: sympy.Expr | float, angle1: sympy.Expr | float, angle2: sympy.Expr | float, qubit0: int, qubit1: int, **kwargs: Any) -> Circuit: + """ + Appends an AAMS gate with possibly symbolic angles (specified in half-turns). + + :return: the new :py:class:`Circuit` + """ + @typing.overload + def AAMS(self, angle0: sympy.Expr | float, angle1: sympy.Expr | float, angle2: sympy.Expr | float, qubit0: pytket._tket.unit_id.Qubit, qubit1: pytket._tket.unit_id.Qubit, **kwargs: Any) -> Circuit: + """ + Appends an AAMS gate with possibly symbolic angles (specified in half-turns). + + :return: the new :py:class:`Circuit` + """ + @typing.overload def CCX(self, control_0: int, control_1: int, target: int, **kwargs: Any) -> Circuit: """ Appends a CCX gate on the wires for the specified control and target qubits. @@ -441,6 +455,34 @@ class Circuit: :return: the new :py:class:`Circuit` """ @typing.overload + def GPI(self, angle: sympy.Expr | float, qubit: int, **kwargs: Any) -> Circuit: + """ + Appends a GPI gate with a possibly symbolic angle (specified in half-turns). + + :return: the new :py:class:`Circuit` + """ + @typing.overload + def GPI(self, angle: sympy.Expr | float, qubit: pytket._tket.unit_id.Qubit, **kwargs: Any) -> Circuit: + """ + Appends a GPI gate with a possibly symbolic angle (specified in half-turns). + + :return: the new :py:class:`Circuit` + """ + @typing.overload + def GPI2(self, angle: sympy.Expr | float, qubit: int, **kwargs: Any) -> Circuit: + """ + Appends a GPI2 gate with a possibly symbolic angle (specified in half-turns). + + :return: the new :py:class:`Circuit` + """ + @typing.overload + def GPI2(self, angle: sympy.Expr | float, qubit: pytket._tket.unit_id.Qubit, **kwargs: Any) -> Circuit: + """ + Appends a GPI2 gate with a possibly symbolic angle (specified in half-turns). + + :return: the new :py:class:`Circuit` + """ + @typing.overload def H(self, qubit: int, **kwargs: Any) -> Circuit: """ Appends a Hadamard gate. @@ -3094,6 +3136,12 @@ class OpType: U3 : :math:`(\\theta, \\phi, \\lambda) \\mapsto \\left[ \\begin{array}{cc} \\cos\\frac{\\pi\\theta}{2} & -e^{i\\pi\\lambda} \\sin\\frac{\\pi\\theta}{2} \\\\ e^{i\\pi\\phi} \\sin\\frac{\\pi\\theta}{2} & e^{i\\pi(\\lambda+\\phi)} \\cos\\frac{\\pi\\theta}{2} \\end{array} \\right] = e^{\\frac12 i\\pi(\\lambda+\\phi)} \\mathrm{Rz}(\\phi) \\mathrm{Ry}(\\theta) \\mathrm{Rz}(\\lambda)` + GPI : :math:`(\\phi) \\mapsto \\left[ \\begin{array}{cc} 0 & e^{-i\\pi\\phi} \\\\ e^{i\\pi\\phi} & 0 \\end{array} \\right]` + + GPI2 : :math:`(\\phi) \\mapsto \\frac{1}{\\sqrt 2} \\left[ \\begin{array}{cc} 1 & -ie^{-i\\pi\\phi} \\\\ -ie^{i\\pi\\phi} & 1 \\end{array} \\right]` + + AAMS : :math:`(\\theta, \\phi_0, \\phi_1) \\mapsto \\left[ \\begin{array}{cccc} \\cos\\frac{\\pi\\theta}{2} & 0 & 0 & -ie^{-i\\pi(\\phi_0+\\phi_1)}\\sin\\frac{\\pi\\theta}{2} \\\\ 0 & \\cos\\frac{\\pi\\theta}{2} & -ie^{i\\pi(\\phi_1-\\phi_0)}\\sin\\frac{\\pi\\theta}{2} & 0 \\\\ 0 & -ie^{i\\pi(\\phi_0-\\phi_1)}\\sin\\frac{\\pi\\theta}{2} & \\cos\\frac{\\pi\\theta}{2} & 0 \\\\ -ie^{i\\pi(\\phi_0+\\phi_1)}\\sin\\frac{\\pi\\theta}{2} & 0 & 0 & \\cos\\frac{\\pi\\theta}{2} \\end{array} \\right]` + TK1 : :math:`(\\alpha, \\beta, \\gamma) \\mapsto \\mathrm{Rz}(\\alpha) \\mathrm{Rx}(\\beta) \\mathrm{Rz}(\\gamma)` TK2 : :math:`(\\alpha, \\beta, \\gamma) \\mapsto \\mathrm{XXPhase}(\\alpha) \\mathrm{YYPhase}(\\beta) \\mathrm{ZZPhase}(\\gamma)` @@ -3250,104 +3298,107 @@ class OpType: DiagonalBox : A box for synthesising a diagonal unitary matrix into a sequence of multiplexed-Rz gates """ - BRIDGE: typing.ClassVar[OpType] # value = + AAMS: typing.ClassVar[OpType] # value = + BRIDGE: typing.ClassVar[OpType] # value = Barrier: typing.ClassVar[OpType] # value = Branch: typing.ClassVar[OpType] # value = - CCX: typing.ClassVar[OpType] # value = - CH: typing.ClassVar[OpType] # value = - CRx: typing.ClassVar[OpType] # value = - CRy: typing.ClassVar[OpType] # value = - CRz: typing.ClassVar[OpType] # value = - CS: typing.ClassVar[OpType] # value = - CSWAP: typing.ClassVar[OpType] # value = - CSX: typing.ClassVar[OpType] # value = - CSXdg: typing.ClassVar[OpType] # value = - CSdg: typing.ClassVar[OpType] # value = - CU1: typing.ClassVar[OpType] # value = - CU3: typing.ClassVar[OpType] # value = - CV: typing.ClassVar[OpType] # value = - CVdg: typing.ClassVar[OpType] # value = - CX: typing.ClassVar[OpType] # value = - CY: typing.ClassVar[OpType] # value = - CZ: typing.ClassVar[OpType] # value = - CircBox: typing.ClassVar[OpType] # value = - ClassicalExpBox: typing.ClassVar[OpType] # value = + CCX: typing.ClassVar[OpType] # value = + CH: typing.ClassVar[OpType] # value = + CRx: typing.ClassVar[OpType] # value = + CRy: typing.ClassVar[OpType] # value = + CRz: typing.ClassVar[OpType] # value = + CS: typing.ClassVar[OpType] # value = + CSWAP: typing.ClassVar[OpType] # value = + CSX: typing.ClassVar[OpType] # value = + CSXdg: typing.ClassVar[OpType] # value = + CSdg: typing.ClassVar[OpType] # value = + CU1: typing.ClassVar[OpType] # value = + CU3: typing.ClassVar[OpType] # value = + CV: typing.ClassVar[OpType] # value = + CVdg: typing.ClassVar[OpType] # value = + CX: typing.ClassVar[OpType] # value = + CY: typing.ClassVar[OpType] # value = + CZ: typing.ClassVar[OpType] # value = + CircBox: typing.ClassVar[OpType] # value = + ClassicalExpBox: typing.ClassVar[OpType] # value = ClassicalTransform: typing.ClassVar[OpType] # value = - CnRy: typing.ClassVar[OpType] # value = - CnX: typing.ClassVar[OpType] # value = - CnY: typing.ClassVar[OpType] # value = - CnZ: typing.ClassVar[OpType] # value = - Conditional: typing.ClassVar[OpType] # value = - ConjugationBox: typing.ClassVar[OpType] # value = + CnRy: typing.ClassVar[OpType] # value = + CnX: typing.ClassVar[OpType] # value = + CnY: typing.ClassVar[OpType] # value = + CnZ: typing.ClassVar[OpType] # value = + Conditional: typing.ClassVar[OpType] # value = + ConjugationBox: typing.ClassVar[OpType] # value = CopyBits: typing.ClassVar[OpType] # value = - CustomGate: typing.ClassVar[OpType] # value = - DiagonalBox: typing.ClassVar[OpType] # value = - DummyBox: typing.ClassVar[OpType] # value = - ECR: typing.ClassVar[OpType] # value = - ESWAP: typing.ClassVar[OpType] # value = - ExpBox: typing.ClassVar[OpType] # value = + CustomGate: typing.ClassVar[OpType] # value = + DiagonalBox: typing.ClassVar[OpType] # value = + DummyBox: typing.ClassVar[OpType] # value = + ECR: typing.ClassVar[OpType] # value = + ESWAP: typing.ClassVar[OpType] # value = + ExpBox: typing.ClassVar[OpType] # value = ExplicitModifier: typing.ClassVar[OpType] # value = ExplicitPredicate: typing.ClassVar[OpType] # value = - FSim: typing.ClassVar[OpType] # value = + FSim: typing.ClassVar[OpType] # value = + GPI: typing.ClassVar[OpType] # value = + GPI2: typing.ClassVar[OpType] # value = Goto: typing.ClassVar[OpType] # value = H: typing.ClassVar[OpType] # value = - ISWAP: typing.ClassVar[OpType] # value = - ISWAPMax: typing.ClassVar[OpType] # value = + ISWAP: typing.ClassVar[OpType] # value = + ISWAPMax: typing.ClassVar[OpType] # value = Label: typing.ClassVar[OpType] # value = - Measure: typing.ClassVar[OpType] # value = + Measure: typing.ClassVar[OpType] # value = MultiBit: typing.ClassVar[OpType] # value = - MultiplexedRotationBox: typing.ClassVar[OpType] # value = - MultiplexedTensoredU2Box: typing.ClassVar[OpType] # value = - MultiplexedU2Box: typing.ClassVar[OpType] # value = - MultiplexorBox: typing.ClassVar[OpType] # value = - NPhasedX: typing.ClassVar[OpType] # value = - PauliExpBox: typing.ClassVar[OpType] # value = - PauliExpCommutingSetBox: typing.ClassVar[OpType] # value = - PauliExpPairBox: typing.ClassVar[OpType] # value = + MultiplexedRotationBox: typing.ClassVar[OpType] # value = + MultiplexedTensoredU2Box: typing.ClassVar[OpType] # value = + MultiplexedU2Box: typing.ClassVar[OpType] # value = + MultiplexorBox: typing.ClassVar[OpType] # value = + NPhasedX: typing.ClassVar[OpType] # value = + PauliExpBox: typing.ClassVar[OpType] # value = + PauliExpCommutingSetBox: typing.ClassVar[OpType] # value = + PauliExpPairBox: typing.ClassVar[OpType] # value = Phase: typing.ClassVar[OpType] # value = - PhasePolyBox: typing.ClassVar[OpType] # value = - PhasedISWAP: typing.ClassVar[OpType] # value = - PhasedX: typing.ClassVar[OpType] # value = - QControlBox: typing.ClassVar[OpType] # value = + PhasePolyBox: typing.ClassVar[OpType] # value = + PhasedISWAP: typing.ClassVar[OpType] # value = + PhasedX: typing.ClassVar[OpType] # value = + QControlBox: typing.ClassVar[OpType] # value = RangePredicate: typing.ClassVar[OpType] # value = - Reset: typing.ClassVar[OpType] # value = + Reset: typing.ClassVar[OpType] # value = Rx: typing.ClassVar[OpType] # value = Ry: typing.ClassVar[OpType] # value = Rz: typing.ClassVar[OpType] # value = S: typing.ClassVar[OpType] # value = - SWAP: typing.ClassVar[OpType] # value = + SWAP: typing.ClassVar[OpType] # value = SX: typing.ClassVar[OpType] # value = SXdg: typing.ClassVar[OpType] # value = Sdg: typing.ClassVar[OpType] # value = SetBits: typing.ClassVar[OpType] # value = - StatePreparationBox: typing.ClassVar[OpType] # value = + StatePreparationBox: typing.ClassVar[OpType] # value = Stop: typing.ClassVar[OpType] # value = - Sycamore: typing.ClassVar[OpType] # value = + Sycamore: typing.ClassVar[OpType] # value = T: typing.ClassVar[OpType] # value = - TK1: typing.ClassVar[OpType] # value = - TK2: typing.ClassVar[OpType] # value = + TK1: typing.ClassVar[OpType] # value = + TK2: typing.ClassVar[OpType] # value = Tdg: typing.ClassVar[OpType] # value = - TermSequenceBox: typing.ClassVar[OpType] # value = - ToffoliBox: typing.ClassVar[OpType] # value = + TermSequenceBox: typing.ClassVar[OpType] # value = + ToffoliBox: typing.ClassVar[OpType] # value = U1: typing.ClassVar[OpType] # value = U2: typing.ClassVar[OpType] # value = U3: typing.ClassVar[OpType] # value = - Unitary1qBox: typing.ClassVar[OpType] # value = - Unitary2qBox: typing.ClassVar[OpType] # value = - Unitary3qBox: typing.ClassVar[OpType] # value = + Unitary1qBox: typing.ClassVar[OpType] # value = + Unitary2qBox: typing.ClassVar[OpType] # value = + Unitary3qBox: typing.ClassVar[OpType] # value = V: typing.ClassVar[OpType] # value = Vdg: typing.ClassVar[OpType] # value = WASM: typing.ClassVar[OpType] # value = X: typing.ClassVar[OpType] # value = - XXPhase: typing.ClassVar[OpType] # value = - XXPhase3: typing.ClassVar[OpType] # value = + XXPhase: typing.ClassVar[OpType] # value = + XXPhase3: typing.ClassVar[OpType] # value = Y: typing.ClassVar[OpType] # value = - YYPhase: typing.ClassVar[OpType] # value = + YYPhase: typing.ClassVar[OpType] # value = Z: typing.ClassVar[OpType] # value = - ZZMax: typing.ClassVar[OpType] # value = - ZZPhase: typing.ClassVar[OpType] # value = - __members__: typing.ClassVar[dict[str, OpType]] # value = {'Phase': , 'Z': , 'X': , 'Y': , 'S': , 'Sdg': , 'T': , 'Tdg': , 'V': , 'Vdg': , 'SX': , 'SXdg': , 'H': , 'Rx': , 'Ry': , 'Rz': , 'U1': , 'U2': , 'U3': , 'TK1': , 'TK2': , 'CX': , 'CY': , 'CZ': , 'CH': , 'CV': , 'CVdg': , 'CSX': , 'CSXdg': , 'CS': , 'CSdg': , 'CRz': , 'CRx': , 'CRy': , 'CU1': , 'CU3': , 'CCX': , 'ECR': , 'SWAP': , 'CSWAP': , 'noop': , 'Barrier': , 'Label': , 'Branch': , 'Goto': , 'Stop': , 'BRIDGE': , 'Measure': , 'Reset': , 'CircBox': , 'PhasePolyBox': , 'Unitary1qBox': , 'Unitary2qBox': , 'Unitary3qBox': , 'ExpBox': , 'PauliExpBox': , 'PauliExpPairBox': , 'PauliExpCommutingSetBox': , 'TermSequenceBox': , 'QControlBox': , 'ToffoliBox': , 'ConjugationBox': , 'DummyBox': , 'CustomGate': , 'Conditional': , 'ISWAP': , 'PhasedISWAP': , 'XXPhase': , 'YYPhase': , 'ZZPhase': , 'XXPhase3': , 'PhasedX': , 'NPhasedX': , 'CnRy': , 'CnX': , 'CnY': , 'CnZ': , 'ZZMax': , 'ESWAP': , 'FSim': , 'Sycamore': , 'ISWAPMax': , 'ClassicalTransform': , 'WASM': , 'SetBits': , 'CopyBits': , 'RangePredicate': , 'ExplicitPredicate': , 'ExplicitModifier': , 'MultiBit': , 'ClassicalExpBox': , 'MultiplexorBox': , 'MultiplexedRotationBox': , 'MultiplexedU2Box': , 'MultiplexedTensoredU2Box': , 'StatePreparationBox': , 'DiagonalBox': } - noop: typing.ClassVar[OpType] # value = + ZZMax: typing.ClassVar[OpType] # value = + ZZPhase: typing.ClassVar[OpType] # value = + __members__: typing.ClassVar[dict[str, OpType]] # value = {'Phase': , 'Z': , 'X': , 'Y': , 'S': , 'Sdg': , 'T': , 'Tdg': , 'V': , 'Vdg': , 'SX': , 'SXdg': , 'H': , 'Rx': , 'Ry': , 'Rz': , 'U1': , 'U2': , 'U3': , 'GPI': , 'GPI2': , 'AAMS': , 'TK1': , 'TK2': , 'CX': , 'CY': , 'CZ': , 'CH': , 'CV': , 'CVdg': , 'CSX': , 'CSXdg': , 'CS': , 'CSdg': , 'CRz': , 'CRx': , 'CRy': , 'CU1': , 'CU3': , 'CCX': , 'ECR': , 'SWAP': , 'CSWAP': , 'noop': , 'Barrier': , 'Label': , 'Branch': , 'Goto': , 'Stop': , 'BRIDGE': , 'Measure': , 'Reset': , 'CircBox': , 'PhasePolyBox': , 'Unitary1qBox': , 'Unitary2qBox': , 'Unitary3qBox': , 'ExpBox': , 'PauliExpBox': , 'PauliExpPairBox': , 'PauliExpCommutingSetBox': , 'TermSequenceBox': , 'QControlBox': , 'ToffoliBox': , 'ConjugationBox': , 'DummyBox': , 'CustomGate': , 'Conditional': , 'ISWAP': , 'PhasedISWAP': , 'XXPhase': , 'YYPhase': , 'ZZPhase': , 'XXPhase3': , 'PhasedX': , 'NPhasedX': , 'CnRy': , 'CnX': , 'CnY': , 'CnZ': , 'ZZMax': , 'ESWAP': , 'FSim': , 'Sycamore': , 'ISWAPMax': , 'ClassicalTransform': , 'WASM': , 'SetBits': , 'CopyBits': , 'RangePredicate': , 'ExplicitPredicate': , 'ExplicitModifier': , 'MultiBit': , 'ClassicalExpBox': , 'MultiplexorBox': , 'MultiplexedRotationBox': , 'MultiplexedU2Box': , 'MultiplexedTensoredU2Box': , 'StatePreparationBox': , 'DiagonalBox': } + noop: typing.ClassVar[OpType] # value = @staticmethod def from_name(arg0: str) -> OpType: """ diff --git a/tket/conanfile.py b/tket/conanfile.py index 83523f5b69..a03e16a832 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.110" + version = "1.2.111" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index 5da6aafdad..461e909f0f 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -441,6 +441,12 @@ Circuit PhasedISWAP_using_TK2(const Expr &p, const Expr &t); /** Equivalent to PhasedISWAP, using CX, U3 and Rz gates */ Circuit PhasedISWAP_using_CX(const Expr &p, const Expr &t); +/** Equivalent to AAMS, using a TK2 and Rz gates */ +Circuit AAMS_using_TK2(const Expr &theta, const Expr &phi0, const Expr &phi1); + +/** Equivalent to AAMS, using CX, Rz and U3 gates */ +Circuit AAMS_using_CX(const Expr &theta, const Expr &phi0, const Expr &phi1); + /** Unwrap NPhasedX, into number_of_qubits PhasedX gates */ Circuit NPhasedX_using_PhasedX( unsigned int number_of_qubits, const Expr &alpha, const Expr &beta); diff --git a/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp b/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp index 961b247062..e04e14998d 100644 --- a/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp +++ b/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp @@ -68,6 +68,10 @@ struct GateUnitaryMatrixImplementations { static Eigen::Matrix2cd Rz(double value); static Eigen::Matrix2cd U1(double value); + static Eigen::Matrix2cd GPI(double value); + static Eigen::Matrix2cd GPI2(double value); + static Eigen::Matrix4cd AAMS(double theta, double phi0, double phi1); + static Eigen::Matrix2cd U2(double phi, double lambda); static Eigen::Matrix2cd U3(double theta, double phi, double lambda); diff --git a/tket/include/tket/OpType/OpType.hpp b/tket/include/tket/OpType/OpType.hpp index f97002f265..6be6ed96d5 100644 --- a/tket/include/tket/OpType/OpType.hpp +++ b/tket/include/tket/OpType/OpType.hpp @@ -259,6 +259,33 @@ enum class OpType { */ U1, + /** + * \f$ \mathrm{GPI}(\phi) = \left[ \begin{array}{cc} + * 0 & e^{-i\pi\phi} \\ e^{i\pi\phi} & 0 + * \end{array} \right] \f$ + */ + GPI, + + /** + * \f$ \mathrm{GPI2}(\phi) = \frac{1}{\sqrt 2} \left[ \begin{array}{cc} + * 1 & -ie^{-i\pi\phi} \\ -ie^{i\pi\phi} & 1 + * \end{array} \right] \f$ + */ + GPI2, + + /** + * \f$ \mathrm{AAMS}(\theta, \phi_0, \phi_1) = \left[ \begin{array}{cccc} + * \cos\frac{\pi\theta}{2} & 0 & 0 & + * -ie^{-i\pi(\phi_0+\phi_1)}\sin\frac{\pi\theta}{2} \\ + * 0 & \cos\frac{\pi\theta}{2} & + * -ie^{i\pi(\phi_1-\phi_0)}\sin\frac{\pi\theta}{2} & 0 \\ + * 0 & -ie^{i\pi(\phi_0-\phi_1)}\sin\frac{\pi\theta}{2} & + * \cos\frac{\pi\theta}{2} & 0 \\ + * -ie^{i\pi(\phi_0+\phi_1)}\sin\frac{\pi\theta}{2} & 0 & 0 & + * \cos\frac{\pi\theta}{2} \end{array} \right] \f$ + */ + AAMS, + /** * \f$ \mathrm{TK1}(\alpha, \beta, \gamma) = \mathrm{Rz}(\alpha) * \mathrm{Rx}(\beta) \mathrm{Rz}(\gamma) \f$ diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index c1d70a1a11..1b9d84368f 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -1195,6 +1195,28 @@ Circuit PhasedISWAP_using_CX(const Expr &p, const Expr &t) { return c; } +Circuit AAMS_using_TK2(const Expr &theta, const Expr &phi0, const Expr &phi1) { + Circuit c(2); + c.add_op(OpType::Rz, -phi0, {0}); + c.add_op(OpType::Rz, -phi1, {1}); + c.add_op(OpType::TK2, {theta, 0, 0}, {0, 1}); + c.add_op(OpType::Rz, phi0, {0}); + c.add_op(OpType::Rz, phi1, {1}); + return c; +} + +Circuit AAMS_using_CX(const Expr &theta, const Expr &phi0, const Expr &phi1) { + Circuit c(2); + c.add_op(OpType::Rz, -phi0, {0}); + c.add_op(OpType::Rz, -phi1, {1}); + c.add_op(OpType::CX, {0, 1}); + c.add_op(OpType::U3, {theta, -0.5, 0.5}, {0}); + c.add_op(OpType::CX, {0, 1}); + c.add_op(OpType::Rz, phi0, {0}); + c.add_op(OpType::Rz, phi1, {1}); + return c; +} + Circuit NPhasedX_using_PhasedX( unsigned int number_of_qubits, const Expr &alpha, const Expr &beta) { Circuit c(number_of_qubits); diff --git a/tket/src/Circuit/CircUtils.cpp b/tket/src/Circuit/CircUtils.cpp index aa29bb9ee1..fe23674bc5 100644 --- a/tket/src/Circuit/CircUtils.cpp +++ b/tket/src/Circuit/CircUtils.cpp @@ -501,6 +501,8 @@ Circuit with_TK2(Gate_ptr op) { return CircPool::CU1_using_TK2(params[0]); case OpType::XXPhase3: return CircPool::XXPhase3_using_TK2(params[0]); + case OpType::AAMS: + return CircPool::AAMS_using_TK2(params[0], params[1], params[2]); case OpType::CCX: case OpType::CSWAP: case OpType::BRIDGE: @@ -608,6 +610,8 @@ Circuit with_CX(Gate_ptr op) { return CircPool::PhasedISWAP_using_CX(params[0], params[1]); case OpType::NPhasedX: return CircPool::NPhasedX_using_PhasedX(n, params[0], params[1]); + case OpType::AAMS: + return CircPool::AAMS_using_CX(params[0], params[1], params[2]); default: throw CircuitInvalidity("Cannot decompose " + op->get_name()); } diff --git a/tket/src/Gate/Gate.cpp b/tket/src/Gate/Gate.cpp index 62486dedc4..6dc93da255 100644 --- a/tket/src/Gate/Gate.cpp +++ b/tket/src/Gate/Gate.cpp @@ -118,6 +118,15 @@ Op_ptr Gate::dagger() const { case OpType::ESWAP: { return get_op_ptr(optype, minus_times(params_[0]), n_qubits_); } + case OpType::GPI: { + return get_op_ptr(optype, params_[0]); + } + case OpType::GPI2: { + return get_op_ptr(optype, params_[0] + 1); + } + case OpType::AAMS: { + return get_op_ptr(optype, {params_[0], params_[1] + 1, params_[2]}); + } case OpType::ZZMax: { // ZZMax.dagger = ZZPhase(-0.5) return get_op_ptr(OpType::ZZPhase, -0.5); @@ -232,6 +241,13 @@ Op_ptr Gate::transpose() const { case OpType::CnZ: { return get_op_ptr(optype, std::vector(), n_qubits_); } + case OpType::GPI: + case OpType::GPI2: { + return get_op_ptr(optype, -params_[0]); + } + case OpType::AAMS: { + return get_op_ptr(optype, {params_[0], -params_[1], -params_[2]}); + } case OpType::U2: { // U2(a,b).transpose() == U2(b+1,a+1) return get_op_ptr(OpType::U2, {params_[1] + 1., params_[0] + 1.}); @@ -367,7 +383,8 @@ std::optional Gate::is_identity() const { case OpType::YYPhase: case OpType::ZZPhase: case OpType::XXPhase3: - case OpType::ESWAP: { + case OpType::ESWAP: + case OpType::AAMS: { Expr e = params[0]; if (equiv_0(e, 4)) { return 0.; @@ -462,6 +479,22 @@ bool Gate::is_clifford() const { case OpType::PhasedISWAP: case OpType::FSim: return equiv_0(4 * params_.at(0)) && equiv_0(2 * params_.at(1)); + case OpType::GPI: + return equiv_0(8 * params_.at(0)); + case OpType::GPI2: + return equiv_0(4 * params_.at(0)); + case OpType::AAMS: + if (equiv_0(params_.at(0))) { + return true; + } else if ( + !equiv_0(4 * params_.at(0)) || !equiv_0(8 * params_.at(1)) || + !equiv_0(8 * params_.at(2))) { + return false; + } else if (equiv_0(2 * params_.at(0))) { + return true; + } else { + return equiv_0(4 * params_.at(1)) && equiv_0(4 * params_.at(2)); + } default: return false; } @@ -705,6 +738,12 @@ std::vector Gate::get_tk1_angles() const { params_.at(1) + half, params_.at(0), params_.at(2) - half, (params_.at(1) + params_.at(2)) / 2}; } + case OpType::GPI: { + return {2 * params_.at(0), 1, 0, half}; + } + case OpType::GPI2: { + return {params_.at(0), half, -params_.at(0), 0}; + } case OpType::NPhasedX: { if (n_qubits_ != 1) { throw BadOpType( diff --git a/tket/src/Gate/GateUnitaryMatrix.cpp b/tket/src/Gate/GateUnitaryMatrix.cpp index 1c4fbaef8c..2202219e83 100644 --- a/tket/src/Gate/GateUnitaryMatrix.cpp +++ b/tket/src/Gate/GateUnitaryMatrix.cpp @@ -122,6 +122,8 @@ static Eigen::MatrixXcd get_unitary_or_throw( CASE_RETURN_1P(Ry) CASE_RETURN_1P(Rz) CASE_RETURN_1P(U1) + CASE_RETURN_1P(GPI) + CASE_RETURN_1P(GPI2) CASE_RETURN_1P(CRx) CASE_RETURN_1P(CRy) CASE_RETURN_1P(CRz) @@ -140,6 +142,7 @@ static Eigen::MatrixXcd get_unitary_or_throw( #undef CASE_RETURN_2P CASE_RETURN_3P(CU3) CASE_RETURN_3P(U3) + CASE_RETURN_3P(AAMS) CASE_RETURN_3P(TK1) CASE_RETURN_3P(TK2) #undef CASE_RETURN_3P diff --git a/tket/src/Gate/GateUnitaryMatrixPrimitives.cpp b/tket/src/Gate/GateUnitaryMatrixPrimitives.cpp index 19e1d4f206..af009932ff 100644 --- a/tket/src/Gate/GateUnitaryMatrixPrimitives.cpp +++ b/tket/src/Gate/GateUnitaryMatrixPrimitives.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include #include #include "tket/Gate/GateUnitaryMatrixImplementations.hpp" @@ -57,6 +59,32 @@ Eigen::Matrix2cd GateUnitaryMatrixImplementations::U1(double value) { return matr; } +Eigen::Matrix2cd GateUnitaryMatrixImplementations::GPI(double value) { + Eigen::Matrix2cd matr; + matr << 0, std::polar(1.0, -PI * value), std::polar(1.0, PI * value), 0; + return matr; +} + +Eigen::Matrix2cd GateUnitaryMatrixImplementations::GPI2(double value) { + Eigen::Matrix2cd matr; + matr << 1, -i_ * std::polar(1.0, -PI * value), + -i_ * std::polar(1.0, PI * value), 1; + return matr / std::sqrt(2.0); +} + +Eigen::Matrix4cd GateUnitaryMatrixImplementations::AAMS( + double theta, double phi0, double phi1) { + Eigen::Matrix4cd matr; + const double angle = 0.5 * PI * theta; + const double cc = cos(angle); + const double ss = sin(angle); + matr << cc, 0, 0, -i_ * std::polar(1., -PI * (phi0 + phi1)) * ss, 0, cc, + -i_ * std::polar(1., PI * (phi1 - phi0)) * ss, 0, 0, + -i_ * std::polar(1., PI * (phi0 - phi1)) * ss, cc, 0, + -i_ * std::polar(1., PI * (phi0 + phi1)) * ss, 0, 0, cc; + return matr; +} + Eigen::Matrix4cd GateUnitaryMatrixImplementations::ISWAP(double alpha) { Eigen::Matrix4cd matr = Eigen::Matrix4cd::Identity(); const double angle = 0.5 * PI * alpha; diff --git a/tket/src/OpType/OpTypeFunctions.cpp b/tket/src/OpType/OpTypeFunctions.cpp index f0796930af..adb5cae654 100644 --- a/tket/src/OpType/OpTypeFunctions.cpp +++ b/tket/src/OpType/OpTypeFunctions.cpp @@ -41,7 +41,8 @@ const OpTypeSet& all_gate_types() { OpType::CnRy, OpType::CnX, OpType::CnZ, OpType::CnY, OpType::BRIDGE, OpType::Collapse, OpType::ESWAP, OpType::FSim, OpType::Sycamore, OpType::ISWAPMax, OpType::PhasedISWAP, OpType::XXPhase3, - OpType::NPhasedX, OpType::TK2, OpType::Phase}; + OpType::NPhasedX, OpType::TK2, OpType::Phase, OpType::GPI, + OpType::GPI2, OpType::AAMS}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -61,7 +62,7 @@ const OpTypeSet& all_multi_qubit_types() { OpType::CnZ, OpType::CnY, OpType::BRIDGE, OpType::ESWAP, OpType::FSim, OpType::Sycamore, OpType::ISWAPMax, OpType::PhasedISWAP, OpType::XXPhase3, - OpType::NPhasedX, OpType::TK2}; + OpType::NPhasedX, OpType::TK2, OpType::AAMS}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -74,7 +75,7 @@ const OpTypeSet& all_single_qubit_unitary_types() { OpType::Sdg, OpType::T, OpType::Tdg, OpType::V, OpType::Vdg, OpType::SX, OpType::SXdg, OpType::H, OpType::Rx, OpType::Ry, OpType::Rz, OpType::U1, OpType::U2, OpType::U3, OpType::PhasedX, - OpType::TK1}; + OpType::TK1, OpType::GPI, OpType::GPI2}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -87,7 +88,8 @@ const OpTypeSet& all_single_qubit_types() { OpType::Vdg, OpType::SX, OpType::SXdg, OpType::H, OpType::Rx, OpType::Ry, OpType::Rz, OpType::U3, OpType::U2, OpType::U1, OpType::TK1, OpType::Measure, - OpType::Reset, OpType::Collapse, OpType::PhasedX, OpType::noop}; + OpType::Reset, OpType::Collapse, OpType::PhasedX, OpType::noop, + OpType::GPI, OpType::GPI2}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; diff --git a/tket/src/OpType/OpTypeInfo.cpp b/tket/src/OpType/OpTypeInfo.cpp index 3757f25d54..a2605edd20 100644 --- a/tket/src/OpType/OpTypeInfo.cpp +++ b/tket/src/OpType/OpTypeInfo.cpp @@ -126,6 +126,9 @@ const std::map& optypeinfo() { {OpType::CnX, {"CnX", "CnX", {}, std::nullopt}}, {OpType::CnZ, {"CnZ", "CnZ", {}, std::nullopt}}, {OpType::CnY, {"CnY", "CnY", {}, std::nullopt}}, + {OpType::GPI, {"GPI", "GPI", {2}, singleq}}, + {OpType::GPI2, {"GPI2", "GPI2", {2}, singleq}}, + {OpType::AAMS, {"AAMS", "AAMS", {4, 2, 2}, doubleq}}, {OpType::TK1, {"TK1", "TK1", {4, 4, 4}, singleq}}, {OpType::TK2, {"TK2", "TK2", {4, 4, 4}, doubleq}}, {OpType::ESWAP, {"ESWAP", "$\\mathrm{eSWAP}$", {4}, doubleq}}, diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index e1a3ebc7ba..0800e72f41 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -25,6 +25,7 @@ #include "tket/Circuit/Command.hpp" #include "tket/Circuit/PauliExpBoxes.hpp" #include "tket/Circuit/Simulation/CircuitSimulator.hpp" +#include "tket/Gate/SymTable.hpp" #include "tket/Mapping/LexiLabelling.hpp" #include "tket/OpType/OpType.hpp" #include "tket/OpType/OpTypeFunctions.hpp" @@ -2057,5 +2058,71 @@ SCENARIO("PauliExponentials") { REQUIRE(test_unitary_comparison(c, cu.get_circ_ref(), true)); } } + +SCENARIO("GPI, GPI2 and AAMS operations") { + GIVEN("A circuit") { + Circuit c(3); + c.add_op(OpType::GPI, 0.1, {0}); + c.add_op(OpType::GPI2, 0.2, {1}); + c.add_op(OpType::AAMS, {0.3, 0.4, 0.5}, {0, 1}); + c.add_op(OpType::AAMS, {0.6, 0.7, 0.8}, {1, 2}); + c.add_op(OpType::GPI, 0.1, {1}); + c.add_op(OpType::GPI2, 0.2, {2}); + Circuit c_d = c.dagger(); + Circuit c2 = c; + c2.append(c_d); + Eigen::MatrixXcd u = tket_sim::get_unitary(c2); + REQUIRE(u.isApprox(Eigen::MatrixXcd::Identity(8, 8))); + Circuit c_t = c.transpose(); + CHECK(c_t.n_gates() == c.n_gates()); + CompilationUnit cu(c); + CHECK(FullPeepholeOptimise(true, OpType::CX)->apply(cu)); + REQUIRE(test_unitary_comparison(c, cu.get_circ_ref())); + CompilationUnit cu1(c); + CHECK(gen_rebase_pass_via_tk2( + {OpType::PhasedX, OpType::Rz, OpType::CX}, + CircPool::TK2_using_CX_and_swap, CircPool::tk1_to_PhasedXRz) + ->apply(cu1)); + REQUIRE(test_unitary_comparison(c, cu1.get_circ_ref())); + } + GIVEN("Rebasing a symbolic AAAS gate") { + Circuit circ(2); + auto a = SymTable::fresh_symbol("a"); + auto b = SymTable::fresh_symbol("b"); + auto c = SymTable::fresh_symbol("c"); + auto ea = Expr(a); + auto eb = Expr(b); + auto ec = Expr(c); + SymEngine::map_basic_basic sub_map{ + std::make_pair(a, Expr(0.1)), std::make_pair(b, Expr(0.2)), + std::make_pair(c, Expr(0.3))}; + circ.add_op(OpType::AAMS, {Expr(a), Expr(b), Expr(c)}, {0, 1}); + CompilationUnit cu(circ); + CHECK(gen_rebase_pass_via_tk2( + {OpType::PhasedX, OpType::Rz, OpType::CX}, + CircPool::TK2_using_CX_and_swap, CircPool::tk1_to_PhasedXRz) + ->apply(cu)); + Circuit circ1 = cu.get_circ_ref(); + circ1.symbol_substitution(sub_map); + Circuit circ2(2); + circ2.add_op(OpType::AAMS, {0.1, 0.2, 0.3}, {0, 1}); + REQUIRE(test_unitary_comparison(circ1, circ2)); + } + GIVEN("A circuit made of Clifford gates") { + Circuit c(3); + c.add_op(OpType::GPI, 0.25, {0}); + c.add_op(OpType::GPI2, 0.5, {1}); + c.add_op(OpType::AAMS, {0, 0.1, 0.2}, {0, 1}); + c.add_op(OpType::AAMS, {1., 0.25, 0.75}, {1, 2}); + c.add_op(OpType::AAMS, {0.5, 0.5, 1.5}, {0, 1}); + REQUIRE(CliffordCircuitPredicate().verify(c)); + } + GIVEN("A circuit with a non-Clifford AAMS gate") { + Circuit c(2); + c.add_op(OpType::AAMS, {1., 0, 0.1}, {0, 1}); + REQUIRE_FALSE(CliffordCircuitPredicate().verify(c)); + } +} + } // namespace test_CompilerPass } // namespace tket